mirror of https://github.com/ARMmbed/mbed-os.git
876 lines
23 KiB
C
876 lines
23 KiB
C
/****************************************************************************
|
|
*
|
|
* Copyright 2020 Samsung Electronics All Rights Reserved.
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
|
|
* either express or implied. See the License for the specific
|
|
* language governing permissions and limitations under the License.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#if DEVICE_I2C
|
|
|
|
#include "i2c_api.h"
|
|
#include "i2c_def.h"
|
|
#include "cmsis.h"
|
|
#include "pinmap.h"
|
|
#include "mbed_error.h"
|
|
#include "mbed_wait_api.h"
|
|
/* States of a possibly combined I2C transfer */
|
|
typedef enum i2c_transfer_state_t {
|
|
I2C_TRANSFER_SINGLE, /* Non combined transfer */
|
|
I2C_TRANSFER_COMBINED_FIRST_MESSAGE, /*
|
|
* First message of a
|
|
* combined transfer
|
|
*/
|
|
I2C_TRANSFER_COMBINED_INTERMEDIATE_MESSAGE, /*
|
|
* Message in the middle
|
|
* of a combined
|
|
* transfer
|
|
*/
|
|
I2C_TRANSFER_COMBINED_LAST_MESSAGE, /*
|
|
* Last message of a combined
|
|
* transfer
|
|
*/
|
|
} i2c_transfer_state_t;
|
|
|
|
/*
|
|
* Driver private data structure that should not be shared by multiple
|
|
* instances of the driver
|
|
* (same driver for multiple instances of the IP)
|
|
*/
|
|
typedef struct private_i2c_t {
|
|
/* State of a possibly combined ongoing i2c transfer */
|
|
i2c_transfer_state_t transfer_state;
|
|
} private_i2c_t;
|
|
|
|
struct i2c_msg_s {
|
|
uint16_t addr; /**< Slave address */
|
|
uint16_t flags; /**< I2C flags; See I2C_M_* definitions */
|
|
#ifdef CONFIG_I2C_USERIO
|
|
uint16_t length; /**< The length of buffer */
|
|
uint8_t *buffer; /**< The Buffer for transferring message */
|
|
#else
|
|
uint8_t *buffer; /**< The Buffer for transferring message */
|
|
int length; /**< The length of buffer */
|
|
#endif
|
|
};
|
|
|
|
#define S5JS100_DEFAULT_I2CXFER_CLOCK (100 * 1000) // 100KHz
|
|
#define S5JS100_DEFAULT_I2C_TIMEOUT 10000
|
|
#define S5JS100_DEFAULT_I2CSLAVE_ADDR 0x69
|
|
#define HSI2C_INT_XFER_DONE (HSI2C_INT_XFER_DONE_NOACK_MANUAL | HSI2C_INT_XFER_DONE_MANUAL)
|
|
|
|
/*
|
|
* Retrieve the private data of the instance related to a given IP
|
|
*/
|
|
#if 0
|
|
static private_i2c_t *get_i2c_private(i2c_t *obj)
|
|
{
|
|
static private_i2c_t data0, data1;
|
|
/*
|
|
* Select which instance to give using the base
|
|
* address of registers
|
|
*/
|
|
switch ((intptr_t)obj->i2c) {
|
|
case I2C0_BASE:
|
|
return &data0;
|
|
case I2C1_BASE:
|
|
return &data1;
|
|
default:
|
|
error("i2c driver private data structure not found for this registers base address");
|
|
return (void *)0;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
static const PinMap PinMap_I2C_SDA[] = {
|
|
{I2C_SDA, I2C_0, 0},
|
|
{NC, NC, 0}
|
|
};
|
|
|
|
static const PinMap PinMap_I2C_SCL[] = {
|
|
{I2C_SCL, I2C_0, 0},
|
|
{NC, NC, 0}
|
|
};
|
|
static void hsi2c_set_hs_timing(unsigned int base, unsigned int nClkDiv,
|
|
unsigned int tSTART_SU, unsigned int tSTART_HD,
|
|
unsigned int tSTOP_SU, unsigned int tSDA_SU,
|
|
unsigned int tDATA_SU, unsigned int tDATA_HD,
|
|
unsigned int tSCL_L, unsigned int tSCL_H,
|
|
unsigned int tSR_RELEASE)
|
|
{
|
|
tSTART_SU &= 0xFF;
|
|
tSTART_HD &= 0xFF;
|
|
tSTOP_SU &= 0xFF;
|
|
putreg32(((tSTART_SU << 24) | (tSTART_HD << 16) | (tSTOP_SU << 8)), base + I2C_TIMING_HS1);
|
|
|
|
tDATA_SU &= 0xFF;
|
|
tSCL_L &= 0xFF;
|
|
tSCL_H &= 0xFF;
|
|
putreg32(((tDATA_SU << 24) | (tSCL_L << 8) | (tSCL_H << 0)), base + I2C_TIMING_HS2);
|
|
|
|
nClkDiv &= 0xFF;
|
|
tSR_RELEASE &= 0xFF;
|
|
putreg32(((nClkDiv << 16) | (tSR_RELEASE << 0)), base + I2C_TIMING_HS3);
|
|
|
|
tDATA_HD &= 0xFFFF;
|
|
putreg32(tDATA_HD, base + I2C_TIMING_SLA);
|
|
|
|
}
|
|
|
|
static void hsi2c_set_fs_timing(unsigned int base, unsigned int nClkDiv,
|
|
unsigned int tSTART_SU, unsigned int tSTART_HD,
|
|
unsigned int tSTOP_SU, unsigned int tDATA_SU,
|
|
unsigned int tDATA_HD, unsigned int tSCL_L,
|
|
unsigned int tSCL_H, unsigned int tSR_RELEASE)
|
|
{
|
|
tSTART_SU &= 0xFF;
|
|
tSTART_HD &= 0xFF;
|
|
tSTOP_SU &= 0xFF;
|
|
putreg32(((tSTART_SU << 24) | (tSTART_HD << 16) | (tSTOP_SU << 8)), base + I2C_TIMING_FS1);
|
|
|
|
tDATA_SU &= 0xFF;
|
|
tSCL_L &= 0xFF;
|
|
tSCL_H &= 0xFF;
|
|
putreg32(((tDATA_SU << 24) | (tSCL_L << 8) | (tSCL_H << 0)), base + I2C_TIMING_FS2);
|
|
|
|
nClkDiv &= 0xFF;
|
|
tSR_RELEASE &= 0xFF;
|
|
putreg32(((nClkDiv << 16) | (tSR_RELEASE << 0)), base + I2C_TIMING_FS3);
|
|
|
|
tDATA_HD &= 0xFFFF;
|
|
putreg32(tDATA_HD, base + I2C_TIMING_SLA);
|
|
}
|
|
|
|
static void hsi2c_calculate_timing(unsigned int base, unsigned int nPclk,
|
|
unsigned int nOpClk)
|
|
{
|
|
unsigned int reg;
|
|
unsigned int nClkDiv;
|
|
uint32_t tFTL_CYCLE_SCL;
|
|
int i;
|
|
int uTemp0;
|
|
int uTemp1;
|
|
int uTemp2 = 0;
|
|
|
|
reg = getreg32(base + I2C_CONF);
|
|
reg &= ~(0x7 << 13);
|
|
putreg32(reg, base + I2C_CONF);
|
|
|
|
reg = getreg32(base + I2C_CONF);
|
|
reg &= ~(0x7 << 16);
|
|
putreg32(reg, base + I2C_CONF);
|
|
|
|
tFTL_CYCLE_SCL = (getreg32(base + I2C_CONF) >> 16) & 0x7;
|
|
|
|
uTemp0 = (float)(nPclk / nOpClk) - (tFTL_CYCLE_SCL + 3) * 2;
|
|
|
|
for (i = 0; i < 256; i++) {
|
|
uTemp1 = (uTemp0 + ((tFTL_CYCLE_SCL + 3) % (i + 1)) * 2) / (i + 1);
|
|
if (uTemp1 < 512) { /* TSCL_L/H max is 512 / 2 */
|
|
uTemp2 = uTemp1 - 2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
unsigned int tSCL_H;
|
|
nClkDiv = i;
|
|
if (nOpClk > I2C_SPEED_400KHZ) {
|
|
tSCL_H = ((uTemp2 + 10) / 3) - 5;
|
|
} else {
|
|
tSCL_H = uTemp2 / 2;
|
|
}
|
|
|
|
unsigned int tSCL_L = uTemp2 - tSCL_H;
|
|
unsigned int tSTART_SU = tSCL_L;
|
|
unsigned int tSTART_HD = tSCL_L;
|
|
unsigned int tSTOP_SU = tSCL_L;
|
|
unsigned int tSDA_SU = tSCL_L;
|
|
unsigned int tDATA_SU = tSCL_L;
|
|
unsigned int tDATA_HD = tSCL_L / 2;
|
|
unsigned int tSR_RELEASE = uTemp2;
|
|
|
|
if (nOpClk > I2C_SPEED_400KHZ) {
|
|
/* 400Khz setting for Extended ID */
|
|
hsi2c_set_fs_timing(base, 1, 38, 38, 38, 19, 19, 38, 38, 76);
|
|
hsi2c_set_hs_timing(base, nClkDiv, tSTART_SU, tSTART_HD, tSTOP_SU,
|
|
tSDA_SU, tDATA_SU, tDATA_HD, tSCL_L, tSCL_H, tSR_RELEASE);
|
|
} else {
|
|
hsi2c_set_fs_timing(base, nClkDiv, tSTART_SU, tSTART_HD, tSTOP_SU,
|
|
tDATA_SU, tDATA_HD, tSCL_L, tSCL_H, tSR_RELEASE);
|
|
}
|
|
}
|
|
|
|
static void hsi2c_conf(unsigned int base, unsigned int nOpClk)
|
|
{
|
|
unsigned int val;
|
|
|
|
val = getreg32(base + I2C_CONF);
|
|
val &= ~(3 << 30);
|
|
if (nOpClk > I2C_SPEED_400KHZ) {
|
|
val |= (1 << 29);
|
|
} else {
|
|
val &= ~(1 << 29);
|
|
}
|
|
putreg32(val, base + I2C_CONF);
|
|
}
|
|
|
|
static void hsi2c_enable_int(unsigned int base, unsigned int bit)
|
|
{
|
|
unsigned int val;
|
|
val = getreg32(base + INT_EN);
|
|
val |= bit;
|
|
putreg32(val, base + INT_EN);
|
|
}
|
|
|
|
static void hsi2c_disable_int(unsigned int base, unsigned int bit)
|
|
{
|
|
unsigned int val;
|
|
|
|
val = getreg32(base + INT_EN);
|
|
val &= ~bit;
|
|
putreg32(val, base + INT_EN);
|
|
}
|
|
|
|
static int hsi2c_manual_fast_init(i2c_t *obj)
|
|
{
|
|
unsigned int val;
|
|
unsigned int base = obj->base;
|
|
|
|
hsi2c_conf(base, obj->xfer_speed);
|
|
hsi2c_calculate_timing(base, obj->clock, obj->xfer_speed);
|
|
hsi2c_enable_int(base, HSI2C_INT_XFER_DONE_MANUAL | HSI2C_INT_XFER_DONE_NOACK_MANUAL);
|
|
|
|
obj->initialized = 1;
|
|
|
|
val = getreg32(base + I2C_CONF);
|
|
val &= ~((1 << 31) | (1 << 29));
|
|
putreg32(val, base + I2C_CONF);
|
|
|
|
hsi2c_enable_int(base, HSI2C_INT_XFER_DONE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void hsi2c_set_slave_addr(unsigned int base, unsigned short addr,
|
|
unsigned int is_master)
|
|
{
|
|
unsigned int val;
|
|
|
|
addr &= 0x3FF;
|
|
|
|
val = getreg32(base + I2C_ADDR);
|
|
|
|
if (is_master == 0) {
|
|
val &= ~0x3ff;
|
|
val |= addr;
|
|
} else {
|
|
val &= ~(0x3FF << 10);
|
|
val |= (addr << 10);
|
|
}
|
|
putreg32(val, base + I2C_ADDR);
|
|
}
|
|
|
|
static int hsi2c_wait_xfer_done(i2c_t *obj)
|
|
{
|
|
int val;
|
|
int timeout = obj->timeout;
|
|
unsigned int base = obj->base;
|
|
|
|
while (timeout-- > 0) {
|
|
val = getreg32(base + INT_STAT) & HSI2C_INT_XFER_DONE_MANUAL;
|
|
if (val) {
|
|
putreg32(val, base + INT_STAT);
|
|
return (val == HSI2C_INT_XFER_DONE_MANUAL);
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
static void hsi2c_start(i2c_t *obj)
|
|
{
|
|
putreg32(0x88, I2C0_BASE + CTL);
|
|
|
|
putreg32(I2C_START, obj->base + I2C_MANUAL_CMD);
|
|
|
|
hsi2c_wait_xfer_done(obj);
|
|
}
|
|
|
|
static void hsi2c_stop(i2c_t *obj)
|
|
{
|
|
putreg32(I2C_STOP, obj->base + I2C_MANUAL_CMD);
|
|
|
|
hsi2c_wait_xfer_done(obj);
|
|
}
|
|
|
|
static void hsi2c_repstart(i2c_t *obj)
|
|
{
|
|
putreg32(I2C_RESTART, obj->base + I2C_MANUAL_CMD);
|
|
}
|
|
|
|
static int hsi2c_outb(i2c_t *obj, unsigned char data)
|
|
{
|
|
unsigned int val;
|
|
int ret;
|
|
|
|
val = ((unsigned int)data) << 24 | I2C_SEND_DATA;
|
|
putreg32(val, obj->base + I2C_MANUAL_CMD);
|
|
|
|
ret = hsi2c_wait_xfer_done(obj);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int hsi2c_inb(i2c_t *obj, unsigned char is_ack)
|
|
{
|
|
unsigned int val = I2C_READ_DATA;
|
|
unsigned char data;
|
|
int ret;
|
|
unsigned int base = obj->base;
|
|
|
|
/* Looks awkward, but if I2C_RX_ACK is set, ACK is NOT generated */
|
|
if (!is_ack) {
|
|
val |= I2C_RX_ACK;
|
|
}
|
|
putreg32(val, base + I2C_MANUAL_CMD);
|
|
|
|
ret = hsi2c_wait_xfer_done(obj);
|
|
if (ret < 0) {
|
|
return ret; /* timeout */
|
|
}
|
|
|
|
data = (getreg32(base + I2C_MANUAL_CMD) >> 16) & 0xff;
|
|
|
|
return data;
|
|
}
|
|
|
|
static int sendbytes(i2c_t *obj, struct i2c_msg_s *msg)
|
|
{
|
|
uint8_t *p = msg->buffer;
|
|
int count = msg->length;
|
|
int nak_ok = msg->flags & I2C_M_IGNORE_NAK;
|
|
int wrcount = 0, ret;
|
|
|
|
while (count > 0) {
|
|
ret = hsi2c_outb(obj, *p);
|
|
if ((ret == 1) || ((ret == 0) && nak_ok)) {
|
|
count--;
|
|
p++;
|
|
wrcount++;
|
|
} else if (ret == 0) {
|
|
/* NAK from the slave */
|
|
return -EIO;
|
|
} else {
|
|
/* Timeout */
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return wrcount;
|
|
}
|
|
|
|
static int readbytes(i2c_t *obj, struct i2c_msg_s *msg)
|
|
{
|
|
int val;
|
|
int rdcount = 0;
|
|
unsigned char *p = msg->buffer;
|
|
int count = msg->length;
|
|
|
|
while (count > 0) {
|
|
val = hsi2c_inb(obj, (count > 1));
|
|
if (val < 0) {
|
|
break;
|
|
}
|
|
|
|
*p++ = val;
|
|
rdcount++;
|
|
count--;
|
|
}
|
|
|
|
return rdcount;
|
|
}
|
|
|
|
static int try_address(i2c_t *obj, unsigned char addr, int retries)
|
|
{
|
|
int i, ret = 0;
|
|
|
|
for (i = 0; i <= retries; i++) {
|
|
ret = hsi2c_outb(obj, addr);
|
|
if (ret == 1 || i == retries) {
|
|
break;
|
|
}
|
|
hsi2c_stop(obj);
|
|
wait_us(obj->timeout / 2);
|
|
hsi2c_start(obj);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int do_address(i2c_t *obj, struct i2c_msg_s *msg)
|
|
{
|
|
unsigned short flags = msg->flags;
|
|
unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK;
|
|
unsigned char addr;
|
|
int ret;
|
|
int retries;
|
|
|
|
retries = nak_ok ? 0 : obj->retries;
|
|
|
|
if (flags & I2C_M_TEN) {
|
|
/* a 10-bit address in manual mode */
|
|
addr = 0xf0 | ((msg->addr >> 7) & 0x06);
|
|
|
|
ret = try_address(obj, addr, retries);
|
|
if ((ret != 1) && !nak_ok) {
|
|
return -ENXIO;
|
|
}
|
|
|
|
/* the remaining 8 bit address */
|
|
ret = hsi2c_outb(obj, msg->addr & 0xff);
|
|
if ((ret != 1) && !nak_ok) {
|
|
return -ENXIO;
|
|
}
|
|
|
|
if (flags & I2C_M_READ) {
|
|
hsi2c_repstart(obj);
|
|
hsi2c_wait_xfer_done(obj);
|
|
addr |= 0x1;
|
|
ret = try_address(obj, addr, retries);
|
|
if ((ret != 1) && !nak_ok) {
|
|
return -EIO;
|
|
}
|
|
}
|
|
} else {
|
|
/* 7-bit address */
|
|
addr = msg->addr << 1;
|
|
if (flags & I2C_M_READ) {
|
|
addr |= 0x1;
|
|
}
|
|
|
|
ret = try_address(obj, addr, retries);
|
|
if ((ret != 1) && !nak_ok) {
|
|
return -ENXIO;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
static void hsi2c_set_auto_config(unsigned int base, unsigned int stop,
|
|
unsigned int tx, unsigned int len)
|
|
{
|
|
unsigned int val = getreg32(base + I2C_AUTO_CONF);
|
|
|
|
if (stop) {
|
|
val |= (1 << 17);
|
|
} else {
|
|
val &= ~(1 << 17);
|
|
}
|
|
if (tx) {
|
|
val &= ~(1 << 16);
|
|
} else {
|
|
val |= (1 << 16);
|
|
}
|
|
|
|
val &= ~0xFFFF;
|
|
val |= len;
|
|
putreg32(val, base + I2C_AUTO_CONF);
|
|
}
|
|
|
|
static void hsi2c_set_trans_mode(unsigned int base, unsigned int master,
|
|
unsigned int tx)
|
|
{
|
|
unsigned int val = getreg32(base + CTL);
|
|
|
|
val |= (1 << 0); /* ctrl 0 bit write 1 */
|
|
|
|
if (master) {
|
|
val |= (1 << 3);
|
|
} else {
|
|
val &= ~(1 << 3);
|
|
}
|
|
|
|
val &= ~(3 << 6);
|
|
if (tx) {
|
|
val |= (1 << 7);
|
|
} else {
|
|
val |= (1 << 6);
|
|
}
|
|
|
|
putreg32(val, base + CTL);
|
|
}
|
|
|
|
static void hsi2c_set_hwacg_mode(unsigned int base, unsigned int slave)
|
|
{
|
|
unsigned int val = getreg32(base + CTL);
|
|
|
|
val &= ~(0x3 << 24);
|
|
|
|
if (slave) {
|
|
val |= (0x1 << 24);
|
|
} else {
|
|
val &= ~(0x1 << 24);
|
|
}
|
|
|
|
putreg32(val, base + CTL);
|
|
}
|
|
|
|
static void hsi2c_set_fifo_level(unsigned int base)
|
|
{
|
|
putreg32(0x10013, base + FIFO_CTL);
|
|
}
|
|
|
|
|
|
static void hsi2c_master_setup(i2c_t *obj, unsigned int mode,
|
|
unsigned int speed, unsigned int slave_addr)
|
|
{
|
|
if (obj->mode == I2C_POLLING) {
|
|
obj->xfer_speed = speed;
|
|
hsi2c_calculate_timing(obj->base, obj->clock, speed);
|
|
}
|
|
#ifdef CONFIG_S5JS100_I2C_INTERRUPT_MODE
|
|
else if (priv->mode == I2C_INTERRUPT) {
|
|
priv->master_test_data = (struct master_data *)malloc(sizeof(struct master_data));
|
|
/* complete_init(&priv->master_test_data->done); */
|
|
hsi2c_set_trans_mode(obj->base, 1, 1); /* set master mode */
|
|
hsi2c_conf(obj->base, speed);
|
|
hsi2c_calculate_timing(obj->base, obj->clock, speed);
|
|
hsi2c_set_slave_addr(obj->base, slave_addr, 1);
|
|
hsi2c_set_auto_mode(obj->base);
|
|
hsi2c_set_fifo_level(obj->base);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void hsi2c_slave_setup(i2c_t *obj, unsigned int mode,
|
|
unsigned int speed, unsigned int slave_addr)
|
|
{
|
|
// obj->i2c->slave_test_data = (struct slave_data *)malloc(sizeof(struct slave_data));
|
|
|
|
/* slave mode is only support slave mode */
|
|
hsi2c_set_trans_mode(obj->base, 0, 0); /* set slave mode */
|
|
|
|
/*set hwacg for slave mode */
|
|
hsi2c_set_hwacg_mode(obj->base, 1);
|
|
|
|
hsi2c_conf(obj->base, speed);
|
|
hsi2c_calculate_timing(obj->base, obj->clock, speed);
|
|
hsi2c_set_slave_addr(obj->base, slave_addr, 0);
|
|
|
|
hsi2c_disable_int(obj->base, HSI2C_INT_ALL);
|
|
hsi2c_enable_int(obj->base, HSI2C_INT_SLAVE_ADDR_MATCH | HSI2C_INT_RX_ALMOST_FULL);
|
|
|
|
hsi2c_set_fifo_level(obj->base);
|
|
hsi2c_set_auto_config(obj->base, 0, 0, 0);
|
|
|
|
hsi2c_set_trans_mode(obj->base, 0, 0);
|
|
|
|
#ifdef CONFIG_S5JS100_I2C_INTERRUPT_MODE
|
|
if ((priv->master == I2C_SLAVE_MODE) || (priv->mode == I2C_INTERRUPT)) {
|
|
irq_attach(priv->config->irq, priv->config->isr, NULL);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static int hsi2c_setup(i2c_t *obj, unsigned int master,
|
|
unsigned int mode, unsigned int speed, unsigned int slave_addr)
|
|
{
|
|
obj->master = master;
|
|
obj->mode = mode;
|
|
obj->xfer_speed = speed;
|
|
obj->slave_addr = slave_addr;
|
|
|
|
hsi2c_manual_fast_init(obj);
|
|
|
|
if (master == I2C_MASTER) {
|
|
hsi2c_master_setup(obj, mode, speed, slave_addr);
|
|
} else if (master == I2C_SLAVE_MODE) {
|
|
hsi2c_slave_setup(obj, mode, speed, slave_addr);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
unsigned int i2c_setclock(i2c_t *obj, unsigned int frequency)
|
|
{
|
|
/* Has the I2C bus frequency changed? */
|
|
if (frequency != obj->xfer_speed) {
|
|
/*
|
|
* Calculate the clock divider that results in the highest frequency
|
|
* that is than or equal to the desired speed.
|
|
*/
|
|
if (obj->mode != I2C_POLLING) {
|
|
hsi2c_conf(obj->base, frequency);
|
|
}
|
|
|
|
hsi2c_calculate_timing(obj->base, obj->clock, frequency);
|
|
}
|
|
|
|
/* Save the new I2C frequency */
|
|
obj->xfer_speed = frequency;
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
static void clear_isr(i2c_t *obj)
|
|
{
|
|
/*
|
|
* Writing to the IRQ status register clears set bits. Therefore, to
|
|
* clear indiscriminately, just read the register and write it back.
|
|
*/
|
|
uint32_t reg = obj->i2c->IRQ_STATUS;
|
|
obj->i2c->IRQ_STATUS = reg;
|
|
}
|
|
|
|
void i2c_init(i2c_t *obj, PinName sda, PinName scl)
|
|
{
|
|
/* Determine the I2C to use */
|
|
I2CName i2c_sda = (I2CName)pinmap_peripheral(sda, PinMap_I2C_SDA);
|
|
I2CName i2c_scl = (I2CName)pinmap_peripheral(scl, PinMap_I2C_SCL);
|
|
obj->i2c = (I2C_TypeDef *)pinmap_merge(i2c_sda, i2c_scl);
|
|
|
|
if ((int)obj->i2c == NC) {
|
|
error("I2C pin mapping failed");
|
|
}
|
|
|
|
pinmap_pinout(sda, PinMap_I2C_SDA);
|
|
pinmap_pinout(scl, PinMap_I2C_SCL);
|
|
|
|
/*
|
|
* Default configuration:
|
|
* - MS : Master mode
|
|
* - NEA : Normal (7-bit) addressing
|
|
* - ACKEN : Send ACKs when reading from slave
|
|
* - CLR_FIFO : Not a configuration bit => clears the FIFO
|
|
*/
|
|
uint32_t reg = I2C_CTRL_MS | \
|
|
I2C_CTRL_NEA | \
|
|
I2C_CTRL_ACKEN | \
|
|
I2C_CTRL_CLR_FIFO;
|
|
|
|
obj->i2c->CONTROL = reg;
|
|
obj->base = I2C0_BASE;
|
|
obj->retries = 0;
|
|
obj->timeout = S5JS100_DEFAULT_I2C_TIMEOUT;
|
|
obj->master = I2C_MASTER;
|
|
obj->initialized = 0;
|
|
obj->clock = cal_clk_getrate(d1_acpu_l3);
|
|
obj->slave_addr = S5JS100_DEFAULT_I2CSLAVE_ADDR;
|
|
obj->mode = I2C_POLLING;
|
|
obj->addrlen = 7;
|
|
|
|
hsi2c_setup(obj, obj->master, obj->mode, obj->xfer_speed, obj->slave_addr);
|
|
|
|
i2c_setclock(obj, S5JS100_DEFAULT_I2CXFER_CLOCK);
|
|
//get_i2c_private(obj)->transfer_state = I2C_TRANSFER_SINGLE;
|
|
|
|
//i2c_frequency(obj, 100000); /* Default to 100kHz SCL frequency */
|
|
}
|
|
|
|
int i2c_start(i2c_t *obj)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int i2c_stop(i2c_t *obj)
|
|
{
|
|
/* Clear the hardware FIFO */
|
|
obj->i2c->CONTROL |= I2C_CTRL_CLR_FIFO;
|
|
/* Clear the HOLD bit used for performing combined transfers */
|
|
obj->i2c->CONTROL &= ~I2C_CTRL_HOLD;
|
|
/* Reset the transfer size (read and write) */
|
|
obj->i2c->TRANSFER_SIZE = 0;
|
|
/* Clear interrupts */
|
|
clear_isr(obj);
|
|
return 0;
|
|
}
|
|
|
|
void i2c_frequency(i2c_t *obj, int hz)
|
|
{
|
|
/*
|
|
* Divider is split in two halfs : A and B
|
|
* A is 2 bits wide and B is 6 bits wide
|
|
* The Fscl frequency (SCL clock) is calculated with the following
|
|
* equation:
|
|
* Fscl=SystemCoreClock/(22*(A+1)*(B+1))
|
|
* Here, we only calculate the B divisor which already enables a
|
|
* wide enough range of values
|
|
*/
|
|
unsigned int frequency = hz;
|
|
|
|
if (frequency != obj->xfer_speed) {
|
|
if (obj->mode != I2C_POLLING) {
|
|
hsi2c_conf(obj->base, frequency);
|
|
}
|
|
hsi2c_calculate_timing(obj->base, obj->clock, frequency);
|
|
}
|
|
}
|
|
|
|
int i2c_transfer(i2c_t *obj, struct i2c_msg_s *msgv, int msgc)
|
|
{
|
|
struct i2c_msg_s *pmsg;
|
|
int ret = 0;
|
|
int i;
|
|
int nak_ok;
|
|
int start = 1;
|
|
int stop = 1;
|
|
//unsigned int base = obj->base;
|
|
|
|
/* Ensure that address or flags don't change meanwhile */
|
|
//s5js100_i2c_sem_wait(priv);
|
|
|
|
/* TODO: initialization */
|
|
if (start) {
|
|
hsi2c_start(obj);
|
|
}
|
|
|
|
/*
|
|
if (obj->xfer_speed > I2C_SPEED_400KHZ) {
|
|
hsi2c_conf(base, I2C_SPEED_400KHZ);
|
|
putreg32((0xF << 24 | I2C_SEND_DATA), base + I2C_MANUAL_CMD);
|
|
hsi2c_wait_xfer_noack(priv);
|
|
|
|
hsi2c_conf(base, priv->xfer_speed);
|
|
hsi2c_repstart(priv);
|
|
hsi2c_wait_xfer_noack(priv);
|
|
}
|
|
*/
|
|
|
|
for (i = 0; i < msgc; i++) {
|
|
pmsg = &msgv[i];
|
|
nak_ok = pmsg->flags & I2C_M_IGNORE_NAK;
|
|
if (!(pmsg->flags & I2C_M_NOSTART)) {
|
|
if ((i > 0) || (start == 0)) {
|
|
hsi2c_repstart(obj);
|
|
hsi2c_wait_xfer_done(obj);
|
|
}
|
|
ret = do_address(obj, pmsg);
|
|
if ((ret != 0) && !nak_ok) {
|
|
goto fail;
|
|
}
|
|
}
|
|
if (pmsg->flags & I2C_M_READ) {
|
|
/* read bytes into buffer */
|
|
ret = readbytes(obj, pmsg);
|
|
if (ret < pmsg->length) {
|
|
if (ret >= 0) {
|
|
return -EIO;
|
|
}
|
|
goto fail;
|
|
}
|
|
} else {
|
|
/* write bytes from buffer */
|
|
ret = sendbytes(obj, pmsg);
|
|
if (ret < pmsg->length) {
|
|
if (ret >= 0) {
|
|
ret = -EIO;
|
|
}
|
|
goto fail;
|
|
}
|
|
}
|
|
}
|
|
|
|
fail:
|
|
if (stop) {
|
|
hsi2c_stop(obj);
|
|
}
|
|
|
|
/* Ensure that address or flags don't change meanwhile */
|
|
//s5js100_i2c_sem_post(priv);
|
|
|
|
return ret;
|
|
}
|
|
int i2c_read(i2c_t *obj, int address, char *data, int length, int stop)
|
|
{
|
|
|
|
struct i2c_msg_s xfer[2];
|
|
|
|
xfer[0].addr = (unsigned int)address;
|
|
xfer[0].flags = 0;
|
|
xfer[0].buffer = (unsigned char *)&data[0];
|
|
xfer[0].length = 1;
|
|
|
|
xfer[1].addr = (unsigned int)address;
|
|
xfer[1].flags = I2C_M_READ;
|
|
xfer[1].buffer = (unsigned char *)&data[1];
|
|
xfer[1].length = length;
|
|
wait_us(100);
|
|
|
|
return i2c_transfer(obj, xfer, 2);
|
|
}
|
|
|
|
int i2c_write(i2c_t *obj, int address, const char *data, int length,
|
|
int stop)
|
|
{
|
|
|
|
struct i2c_msg_s msg;
|
|
|
|
msg.addr = address;
|
|
msg.flags = (obj->addrlen == 10) ? I2C_M_TEN : 0;
|
|
msg.buffer = (unsigned char *)data;
|
|
msg.length = length;
|
|
|
|
return i2c_transfer(obj, &msg, 1);
|
|
}
|
|
|
|
void i2c_reset(i2c_t *obj)
|
|
{
|
|
i2c_stop(obj);
|
|
}
|
|
|
|
int i2c_byte_read(i2c_t *obj, int last)
|
|
{
|
|
char i2c_ret = 0;
|
|
i2c_read(obj, obj->last_xfer_address, &i2c_ret, 1, last);
|
|
return i2c_ret;
|
|
}
|
|
|
|
int i2c_byte_write(i2c_t *obj, int data)
|
|
{
|
|
/* Store the number of written bytes */
|
|
uint32_t wb = i2c_write(obj, obj->last_xfer_address, (char *)&data, 1, 0);
|
|
if (wb == 1) {
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void i2c_slave_mode(i2c_t *obj, int enable_slave)
|
|
{
|
|
}
|
|
|
|
int i2c_slave_receive(i2c_t *obj)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int i2c_slave_read(i2c_t *obj, char *data, int length)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int i2c_slave_write(i2c_t *obj, const char *data, int length)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
void i2c_slave_address(i2c_t *obj, int idx, uint32_t address, uint32_t mask)
|
|
{
|
|
}
|
|
|
|
#endif // DEVICE_I2C
|