721 lines
17 KiB
C
721 lines
17 KiB
C
#include <linux/lotus/i2c.h>
|
|
#include <common.h>
|
|
#include <asm/io.h>
|
|
|
|
|
|
/*
|
|
* I2C Registers offsets
|
|
*/
|
|
#define I2C_GLB 0x0
|
|
#define I2C_SCL_H 0x20
|
|
#define I2C_SCL_L 0x24
|
|
#define I2C_DATA1 0x28
|
|
#define I2C_TXF 0x10
|
|
#define I2C_RXF 0x14
|
|
#define I2C_CMD_BASE 0x40
|
|
#define I2C_LOOP1 0xc0
|
|
#define I2C_DST1 0xc4
|
|
#define I2C_TX_WATER 0x18
|
|
#define I2C_RX_WATER 0x1c
|
|
#define I2C_CTRL1 0x04
|
|
#define I2C_CTRL2 0x08
|
|
#define I2C_STAT 0x0c
|
|
#define I2C_INTR_RAW 0x30
|
|
#define I2C_INTR_EN 0x34
|
|
#define I2C_INTR_STAT 0x38
|
|
|
|
/*
|
|
* I2C Global Config Register -- I2C_GLB
|
|
* */
|
|
#define GLB_EN_MASK BIT(0)
|
|
#define GLB_SDA_HOLD_MASK 0xffff0
|
|
#define GLB_SDA_HOLD_SHIFT (4)
|
|
|
|
/*
|
|
* I2C Timing CMD Register -- I2C_CMD_BASE + n * 4 (n = 0, 1, 2, ... 31)
|
|
* */
|
|
#define CMD_EXIT 0x0
|
|
#define CMD_TX_S 0x1
|
|
#define CMD_TX_D1_2 0x4
|
|
#define CMD_TX_D1_1 0x5
|
|
#define CMD_TX_FIFO 0x9
|
|
#define CMD_RX_FIFO 0x12
|
|
#define CMD_RX_ACK 0x13
|
|
#define CMD_IGN_ACK 0x15
|
|
#define CMD_TX_ACK 0x16
|
|
#define CMD_TX_NACK 0x17
|
|
#define CMD_JMP1 0x18
|
|
#define CMD_UP_TXF 0x1d
|
|
#define CMD_TX_RS 0x1e
|
|
#define CMD_TX_P 0x1f
|
|
|
|
/*
|
|
* I2C Control Register 1 -- I2C_CTRL1
|
|
*/
|
|
#define CTRL1_CMD_START_MASK BIT(0)
|
|
|
|
/*
|
|
* I2C Status Register -- I2C_STAT
|
|
*/
|
|
#define STAT_RXF_NOE_MASK BIT(0) /* RX FIFO not empty flag */
|
|
#define STAT_TXF_NOF_MASK BIT(3) /* TX FIFO not full flag */
|
|
|
|
/*
|
|
* I2C Interrupt status and mask Register --
|
|
* I2C_INTR_RAW, I2C_STAT, I2C_INTR_STAT
|
|
*/
|
|
#define INTR_ABORT_MASK (BIT(0) | BIT(3))
|
|
#define INTR_RX_MASK BIT(5)
|
|
#define INTR_TX_MASK BIT(6)
|
|
#define INTR_CMD_DONE_MASK BIT(4)
|
|
#define INTR_USE_MASK (INTR_ABORT_MASK | INTR_RX_MASK | INTR_TX_MASK | INTR_CMD_DONE_MASK)
|
|
#define INTR_ALL_MASK 0xffffffff
|
|
#define I2C_TXF_DEPTH 64
|
|
#define I2C_RXF_DEPTH 64
|
|
#define I2C_TXF_WATER 32
|
|
#define I2C_RXF_WATER 32
|
|
/* for i2c rescue */
|
|
#define CHECK_SDA_IN_SHIFT (5)
|
|
#define GPIO_MODE_SHIFT (0)
|
|
#define FORCE_SCL_OEN_SHIFT (1)
|
|
#define FORCE_SDA_OEN_SHIFT (2)
|
|
|
|
|
|
struct platform_i2c {
|
|
unsigned int msg_buf_ptr;
|
|
int status;
|
|
#define I2C_WAIT_RESPOND (1 << 0)
|
|
};
|
|
|
|
struct i2c_platform_data {
|
|
unsigned int freq;
|
|
unsigned int clk;
|
|
};
|
|
|
|
struct i2c_msg {
|
|
unsigned short addr; /* slave address */
|
|
unsigned short flags;
|
|
#define I2C_M_TEN 0x0010
|
|
#define I2C_M_RD 0x0001
|
|
#define I2C_M_STOP 0x8000
|
|
#define I2C_M_NOSTART 0x4000
|
|
#define I2C_M_REV_DIR_ADDR 0x2000
|
|
#define I2C_M_IGNORE_NAK 0x1000
|
|
#define I2C_M_NO_RD_ACK 0x0800
|
|
#define I2C_M_RECV_LEN 0x0400
|
|
#define I2C_M_16BIT_DATA 0x0008
|
|
#define I2C_M_16BIT_REG 0x0002
|
|
unsigned short len; /* msg length */
|
|
unsigned char *buf; /* pointer to msg data */
|
|
};
|
|
|
|
struct i2c_driver_data {
|
|
unsigned int reg_base;
|
|
unsigned int freq;
|
|
unsigned int irq;
|
|
unsigned int clk;
|
|
struct i2c_msg *msgs;
|
|
unsigned int msg_num;
|
|
unsigned int msg_idx;
|
|
unsigned int lock;
|
|
void *private;
|
|
};
|
|
|
|
#ifdef CONFIG_LOTUS_FPGA
|
|
#define CLK_LIMIT_DEFAULT 40000
|
|
#else
|
|
#define CLK_LIMIT_DEFAULT 400000
|
|
#endif
|
|
#define write_reg_bit(value, offset, addr) ({ \
|
|
unsigned long t, mask; \
|
|
mask = 1 << (offset); \
|
|
t = readl(addr); \
|
|
t &= ~mask; \
|
|
t |= (value << (offset)) & mask; \
|
|
writel(t, addr); \
|
|
})
|
|
|
|
#define I2C_WAIT_TIMEOUT (100 * 3)
|
|
#define I2C_TIMEOUT_COUNT 0x10000
|
|
#define I2C_BUF_SIZE 8
|
|
#define I2C_INTERRUPT_NUM 0
|
|
|
|
#if defined(CONFIG_TARGET_XMFALCON)
|
|
|
|
#define PERI_CRG110 0x01b8
|
|
#define I2C_CRG_REG_BASE (CRG_REG_BASE + PERI_CRG110)
|
|
|
|
#define I2C0_REG_BASE 0x12060000
|
|
#define I2C1_REG_BASE 0x12061000
|
|
#define I2C2_REG_BASE 0x12062000
|
|
#define I2C3_REG_BASE 0x12063000
|
|
#define I2C4_REG_BASE 0x12064000
|
|
#define I2C5_REG_BASE 0x12065000
|
|
#define I2C6_REG_BASE 0x12066000
|
|
#define I2C7_REG_BASE 0x12067000
|
|
#define I2C_NUM 8
|
|
|
|
#ifdef CONFIG_LOTUS_FPGA
|
|
#define get_bus_clk() 25000000
|
|
#else
|
|
#define get_bus_clk() 50000000
|
|
#endif
|
|
#define get_host_clock(i2c_num) ({ get_bus_clk(); })
|
|
|
|
static struct platform_i2c g_i2c_platform_data[I2C_NUM] = {0};
|
|
static int g_i2c_host_cfg[I2C_NUM] = {0, 1, 2, 3, 4, 5, 6, 7}; /* i2c index */
|
|
static struct i2c_driver_data g_i2c_data[I2C_NUM] = {
|
|
{I2C0_REG_BASE, CLK_LIMIT_DEFAULT, I2C_INTERRUPT_NUM},
|
|
{I2C1_REG_BASE, CLK_LIMIT_DEFAULT, I2C_INTERRUPT_NUM},
|
|
{I2C2_REG_BASE, CLK_LIMIT_DEFAULT, I2C_INTERRUPT_NUM},
|
|
{I2C3_REG_BASE, CLK_LIMIT_DEFAULT, I2C_INTERRUPT_NUM},
|
|
{I2C4_REG_BASE, CLK_LIMIT_DEFAULT, I2C_INTERRUPT_NUM},
|
|
{I2C5_REG_BASE, CLK_LIMIT_DEFAULT, I2C_INTERRUPT_NUM},
|
|
{I2C6_REG_BASE, CLK_LIMIT_DEFAULT, I2C_INTERRUPT_NUM},
|
|
{I2C7_REG_BASE, CLK_LIMIT_DEFAULT, I2C_INTERRUPT_NUM},
|
|
};
|
|
|
|
#elif defined(CONFIG_TARGET_XMORCA)
|
|
|
|
#define PERI_CRG110 0x01b8
|
|
#define I2C_CRG_REG_BASE (CRG_REG_BASE + PERI_CRG110)
|
|
|
|
#define I2C0_REG_BASE 0x12060000
|
|
#define I2C1_REG_BASE 0x12061000
|
|
#define I2C2_REG_BASE 0x12062000
|
|
#define I2C3_REG_BASE 0x12063000
|
|
#define I2C_NUM 4
|
|
|
|
#define get_bus_clk() 50000000
|
|
#define get_host_clock(i2c_num) ({ get_bus_clk(); })
|
|
|
|
static struct platform_i2c g_i2c_platform_data[I2C_NUM] = {0};
|
|
static int g_i2c_host_cfg[I2C_NUM] = {0, 1, 2, 3}; /* 0,1,2,3: i2c index */
|
|
static struct i2c_driver_data g_i2c_data[I2C_NUM] = {
|
|
{I2C0_REG_BASE, CLK_LIMIT_DEFAULT, I2C_INTERRUPT_NUM},
|
|
{I2C1_REG_BASE, CLK_LIMIT_DEFAULT, I2C_INTERRUPT_NUM},
|
|
{I2C2_REG_BASE, CLK_LIMIT_DEFAULT, I2C_INTERRUPT_NUM},
|
|
{I2C3_REG_BASE, CLK_LIMIT_DEFAULT, I2C_INTERRUPT_NUM},
|
|
};
|
|
|
|
#endif
|
|
|
|
|
|
static void i2c_disable(const struct i2c_driver_data *i2c);
|
|
static void i2c_cfg_irq(const struct i2c_driver_data *i2c, unsigned int flag);
|
|
static unsigned int i2c_clr_irq(const struct i2c_driver_data *i2c);
|
|
|
|
static void i2c_rescue(const struct i2c_driver_data *i2c)
|
|
{
|
|
i2c_disable(i2c);
|
|
i2c_cfg_irq(i2c, 0);
|
|
i2c_clr_irq(i2c);
|
|
|
|
unsigned int val = (0x1 << GPIO_MODE_SHIFT) | (0x1 << FORCE_SCL_OEN_SHIFT) |
|
|
(0x1 << FORCE_SDA_OEN_SHIFT);
|
|
writel(val, i2c->reg_base + I2C_CTRL2);
|
|
|
|
unsigned int time_cnt = 0;
|
|
do {
|
|
for (int index = 0; index < 9; index++) { /* shift:9 */
|
|
val = (0x1 << GPIO_MODE_SHIFT) | 0x1;
|
|
writel(val, i2c->reg_base + I2C_CTRL2);
|
|
udelay(5); /* delay: 5 us */
|
|
val = (0x1 << GPIO_MODE_SHIFT) |
|
|
(0x1 << FORCE_SCL_OEN_SHIFT) |
|
|
(0x1 << FORCE_SDA_OEN_SHIFT);
|
|
writel(val, i2c->reg_base + I2C_CTRL2);
|
|
udelay(5); /* delay: 5 us */
|
|
}
|
|
|
|
time_cnt++;
|
|
if (time_cnt > I2C_WAIT_TIMEOUT) {
|
|
goto disable_rescue;
|
|
}
|
|
|
|
val = readl(i2c->reg_base + I2C_CTRL2);
|
|
} while (!(val & (0x1 << CHECK_SDA_IN_SHIFT)));
|
|
|
|
val = (0x1 << GPIO_MODE_SHIFT) | (0x1 << FORCE_SCL_OEN_SHIFT) |
|
|
(0x1 << FORCE_SDA_OEN_SHIFT);
|
|
writel(val, i2c->reg_base + I2C_CTRL2);
|
|
val = (0x1 << GPIO_MODE_SHIFT) | (0x1 << FORCE_SCL_OEN_SHIFT);
|
|
writel(val, i2c->reg_base + I2C_CTRL2);
|
|
udelay(10); /* delay: 10 us */
|
|
val = (0x1 << GPIO_MODE_SHIFT) | (0x1 << FORCE_SCL_OEN_SHIFT) |
|
|
(0x1 << FORCE_SDA_OEN_SHIFT);
|
|
writel(val, i2c->reg_base + I2C_CTRL2);
|
|
|
|
disable_rescue:
|
|
val = (0x1 << FORCE_SCL_OEN_SHIFT) | 0x1;
|
|
writel(val, i2c->reg_base + I2C_CTRL2);
|
|
}
|
|
|
|
static void i2c_disable(const struct i2c_driver_data *i2c)
|
|
{
|
|
unsigned int val = readl(i2c->reg_base + I2C_GLB);
|
|
val &= ~GLB_EN_MASK;
|
|
writel(val, i2c->reg_base + I2C_GLB);
|
|
}
|
|
|
|
static void i2c_disable_irq(const struct i2c_driver_data *i2c, unsigned int flag)
|
|
{
|
|
unsigned int val = readl(i2c->reg_base + I2C_INTR_EN);
|
|
val &= ~flag;
|
|
writel(val, i2c->reg_base + I2C_INTR_EN);
|
|
}
|
|
|
|
static unsigned int i2c_clr_irq(const struct i2c_driver_data *i2c)
|
|
{
|
|
unsigned int val = readl(i2c->reg_base + I2C_INTR_STAT);
|
|
writel(INTR_ALL_MASK, i2c->reg_base + I2C_INTR_RAW);
|
|
|
|
return val;
|
|
}
|
|
|
|
static void i2c_set_freq(struct i2c_driver_data *i2c)
|
|
{
|
|
unsigned int val;
|
|
unsigned int freq = i2c->freq;
|
|
unsigned int clk_rate = i2c->clk;
|
|
unsigned int max_freq = clk_rate >> 1;
|
|
|
|
if (freq > max_freq) {
|
|
i2c->freq = max_freq;
|
|
freq = i2c->freq;
|
|
}
|
|
|
|
if (freq <= 100000) { /* 100000:100KHz */
|
|
val = clk_rate / (freq * 2); /* 1/2:0.5 */
|
|
writel(val, i2c->reg_base + I2C_SCL_H);
|
|
writel(val, i2c->reg_base + I2C_SCL_L);
|
|
} else {
|
|
val = (clk_rate * 36) / (freq * 100); /* 36/100:0.36 */
|
|
writel(val, i2c->reg_base + I2C_SCL_H);
|
|
val = (clk_rate * 64) / (freq * 100); /* 64/100:0.64 */
|
|
writel(val, i2c->reg_base + I2C_SCL_L);
|
|
}
|
|
val = readl(i2c->reg_base + I2C_GLB);
|
|
val &= ~GLB_SDA_HOLD_MASK;
|
|
val |= ((0xa << GLB_SDA_HOLD_SHIFT) & GLB_SDA_HOLD_MASK);
|
|
writel(val, i2c->reg_base + I2C_GLB);
|
|
}
|
|
|
|
/*
|
|
* set i2c controller TX and RX FIFO water
|
|
*/
|
|
static void i2c_set_water(const struct i2c_driver_data *i2c)
|
|
{
|
|
writel(I2C_TXF_WATER, i2c->reg_base + I2C_TX_WATER);
|
|
writel(I2C_RXF_WATER, i2c->reg_base + I2C_RX_WATER);
|
|
}
|
|
|
|
static void i2c_enable_clk(unsigned char i2c_num)
|
|
{
|
|
const unsigned int clk_start_bit = 16 + i2c_num; /* 16: i2c clk start bit */
|
|
const unsigned int rst_start_bit = 24 + i2c_num; /* 24: i2c rst start bit */
|
|
const unsigned int enable_clck = 1;
|
|
const unsigned int enable_rst = 0;
|
|
write_reg_bit(enable_clck, clk_start_bit, (uintptr_t)I2C_CRG_REG_BASE);
|
|
write_reg_bit(enable_rst, rst_start_bit, (uintptr_t)I2C_CRG_REG_BASE);
|
|
}
|
|
|
|
/*
|
|
* initialise the controller, set i2c bus interface freq
|
|
*/
|
|
static void i2c_init_cfg(const struct i2c_driver_data *i2c, unsigned char i2c_num)
|
|
{
|
|
i2c_enable_clk(i2c_num);
|
|
i2c_disable(i2c);
|
|
i2c_disable_irq(i2c, INTR_ALL_MASK);
|
|
i2c_set_freq((struct i2c_driver_data *)i2c);
|
|
i2c_set_water(i2c);
|
|
}
|
|
|
|
static void i2c_cmdreg_set(const struct i2c_driver_data *i2c, unsigned int cmd,
|
|
unsigned int *offset)
|
|
{
|
|
writel(cmd, i2c->reg_base + I2C_CMD_BASE + (*offset) * 4); /* 4: bytes */
|
|
(*offset)++;
|
|
}
|
|
|
|
static void i2c_cfg_cmd(const struct i2c_driver_data *i2c)
|
|
{
|
|
struct i2c_msg *msg = i2c->msgs;
|
|
unsigned int offset = 0;
|
|
|
|
if (i2c->msg_idx == 0)
|
|
i2c_cmdreg_set(i2c, CMD_TX_S, &offset);
|
|
else
|
|
i2c_cmdreg_set(i2c, CMD_TX_RS, &offset);
|
|
|
|
if (msg->flags & I2C_M_TEN) {
|
|
if (i2c->msg_idx == 0) {
|
|
i2c_cmdreg_set(i2c, CMD_TX_D1_2, &offset);
|
|
i2c_cmdreg_set(i2c, CMD_RX_ACK, &offset);
|
|
i2c_cmdreg_set(i2c, CMD_TX_D1_1, &offset);
|
|
} else {
|
|
i2c_cmdreg_set(i2c, CMD_TX_D1_2, &offset);
|
|
}
|
|
} else {
|
|
i2c_cmdreg_set(i2c, CMD_TX_D1_1, &offset);
|
|
}
|
|
|
|
if (msg->flags & I2C_M_IGNORE_NAK)
|
|
i2c_cmdreg_set(i2c, CMD_IGN_ACK, &offset);
|
|
else
|
|
i2c_cmdreg_set(i2c, CMD_RX_ACK, &offset);
|
|
|
|
if (msg->flags & I2C_M_RD) {
|
|
if (msg->len >= 2) { /* msg len:2 */
|
|
writel(offset, i2c->reg_base + I2C_DST1);
|
|
writel(msg->len - 2, i2c->reg_base + I2C_LOOP1); /* 2: max len */
|
|
i2c_cmdreg_set(i2c, CMD_RX_FIFO, &offset);
|
|
i2c_cmdreg_set(i2c, CMD_TX_ACK, &offset);
|
|
i2c_cmdreg_set(i2c, CMD_JMP1, &offset);
|
|
}
|
|
i2c_cmdreg_set(i2c, CMD_RX_FIFO, &offset);
|
|
i2c_cmdreg_set(i2c, CMD_TX_NACK, &offset);
|
|
} else {
|
|
writel(offset, i2c->reg_base + I2C_DST1);
|
|
writel(msg->len - 1, i2c->reg_base + I2C_LOOP1);
|
|
i2c_cmdreg_set(i2c, CMD_UP_TXF, &offset);
|
|
i2c_cmdreg_set(i2c, CMD_TX_FIFO, &offset);
|
|
|
|
if (msg->flags & I2C_M_IGNORE_NAK) {
|
|
i2c_cmdreg_set(i2c, CMD_IGN_ACK, &offset);
|
|
}
|
|
else {
|
|
i2c_cmdreg_set(i2c, CMD_RX_ACK, &offset);
|
|
}
|
|
i2c_cmdreg_set(i2c, CMD_JMP1, &offset);
|
|
}
|
|
|
|
if ((i2c->msg_idx == (i2c->msg_num - 1)) || (msg->flags & I2C_M_STOP)) {
|
|
i2c_cmdreg_set(i2c, CMD_TX_P, &offset);
|
|
}
|
|
|
|
i2c_cmdreg_set(i2c, CMD_EXIT, &offset);
|
|
}
|
|
|
|
static void i2c_enable(const struct i2c_driver_data *i2c)
|
|
{
|
|
unsigned int val = readl(i2c->reg_base + I2C_GLB);
|
|
val |= GLB_EN_MASK;
|
|
writel(val, i2c->reg_base + I2C_GLB);
|
|
}
|
|
|
|
/*
|
|
* config i2c slave addr
|
|
*/
|
|
static void i2c_set_addr(const struct i2c_driver_data *i2c)
|
|
{
|
|
struct i2c_msg *msg = i2c->msgs;
|
|
unsigned int addr;
|
|
|
|
if (msg->flags & I2C_M_TEN) {
|
|
/* first byte is 11110XX0 where XX is upper 2 bits */
|
|
addr = ((msg->addr & 0x300) << 1) | 0xf000;
|
|
if (msg->flags & I2C_M_RD) {
|
|
addr |= 1 << 8; /* shift:8 */
|
|
}
|
|
|
|
/* second byte is the remaining 8 bits */
|
|
addr |= msg->addr & 0xff;
|
|
} else {
|
|
addr = (msg->addr & 0x7f) << 1;
|
|
if (msg->flags & I2C_M_RD) {
|
|
addr |= 1;
|
|
}
|
|
}
|
|
|
|
writel(addr, i2c->reg_base + I2C_DATA1);
|
|
}
|
|
|
|
/*
|
|
* start command sequence
|
|
*/
|
|
static void i2c_start_cmd(const struct i2c_driver_data *i2c)
|
|
{
|
|
unsigned int val = readl(i2c->reg_base + I2C_CTRL1);
|
|
val |= CTRL1_CMD_START_MASK;
|
|
writel(val, i2c->reg_base + I2C_CTRL1);
|
|
}
|
|
|
|
static int i2c_wait_rx_noempty(const struct i2c_driver_data *i2c)
|
|
{
|
|
unsigned int time_cnt = 0;
|
|
unsigned int val;
|
|
|
|
do {
|
|
val = readl(i2c->reg_base + I2C_STAT);
|
|
if (val & STAT_RXF_NOE_MASK) {
|
|
return 0;
|
|
}
|
|
udelay(50); /* delay:50 us */
|
|
time_cnt++;
|
|
} while (time_cnt < I2C_TIMEOUT_COUNT);
|
|
|
|
i2c_rescue(i2c);
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
static int i2c_wait_tx_nofull(const struct i2c_driver_data *i2c)
|
|
{
|
|
unsigned int time_cnt = 0;
|
|
unsigned int val;
|
|
|
|
do {
|
|
val = readl(i2c->reg_base + I2C_STAT);
|
|
if (val & STAT_TXF_NOF_MASK) {
|
|
return 0;
|
|
}
|
|
udelay(50); /* delay:50 us */
|
|
time_cnt++;
|
|
} while (time_cnt < I2C_TIMEOUT_COUNT);
|
|
|
|
i2c_rescue(i2c);
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
|
|
static int i2c_wait_idle(const struct i2c_driver_data *i2c)
|
|
{
|
|
unsigned int time_cnt = 0;
|
|
unsigned int val;
|
|
|
|
do {
|
|
val = readl(i2c->reg_base + I2C_INTR_RAW);
|
|
if (val & (INTR_ABORT_MASK)) {
|
|
printf("i2c wait idle EIO,val==0x%x\n",val);
|
|
return -EIO;
|
|
}
|
|
|
|
if (val & INTR_CMD_DONE_MASK) {
|
|
return 0;
|
|
}
|
|
udelay(50); /* delay:50 us */
|
|
time_cnt++;
|
|
} while (time_cnt < I2C_WAIT_TIMEOUT);
|
|
|
|
i2c_rescue(i2c);
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
static int i2c_polling_xfer_one_msg(const struct i2c_driver_data *i2c)
|
|
{
|
|
int status;
|
|
unsigned int val;
|
|
struct i2c_msg *msg = i2c->msgs;
|
|
unsigned int msg_buf_ptr = 0;
|
|
|
|
i2c_enable(i2c);
|
|
i2c_clr_irq(i2c);
|
|
i2c_set_addr(i2c);
|
|
i2c_cfg_cmd(i2c);
|
|
i2c_start_cmd(i2c);
|
|
|
|
if (msg->flags & I2C_M_RD) {
|
|
while (msg_buf_ptr < msg->len) {
|
|
status = i2c_wait_rx_noempty(i2c);
|
|
if (status) {
|
|
goto end;
|
|
}
|
|
val = readl(i2c->reg_base + I2C_RXF);
|
|
msg->buf[msg_buf_ptr] = val;
|
|
msg_buf_ptr++;
|
|
}
|
|
} else {
|
|
while (msg_buf_ptr < msg->len) {
|
|
status = i2c_wait_tx_nofull(i2c);
|
|
if (status) {
|
|
goto end;
|
|
}
|
|
val = msg->buf[msg_buf_ptr];
|
|
writel(val, i2c->reg_base + I2C_TXF);
|
|
msg_buf_ptr++;
|
|
}
|
|
}
|
|
|
|
status = i2c_wait_idle(i2c);
|
|
end:
|
|
i2c_disable(i2c);
|
|
|
|
return status;
|
|
}
|
|
|
|
static void i2c_cfg_irq(const struct i2c_driver_data *i2c, unsigned int flag)
|
|
{
|
|
writel(flag, i2c->reg_base + I2C_INTR_EN);
|
|
}
|
|
|
|
static int i2c_xfer(unsigned char i2c_num, const struct i2c_msg *msgs, int num)
|
|
{
|
|
int status = 0;
|
|
|
|
if (msgs == NULL) {
|
|
printf("[error] msg pointer is null.\n");
|
|
return -EIO;
|
|
}
|
|
|
|
struct i2c_driver_data *i2c = &g_i2c_data[i2c_num];
|
|
struct platform_i2c *hpi = &g_i2c_platform_data[i2c_num];
|
|
|
|
i2c->clk = get_host_clock(0);
|
|
i2c->private = (void *)(hpi);
|
|
|
|
i2c->msgs = (struct i2c_msg *)msgs;
|
|
i2c->msg_num = (unsigned int)num;
|
|
i2c->msg_idx = 0;
|
|
|
|
while (i2c->msg_idx < i2c->msg_num) {
|
|
status = i2c_polling_xfer_one_msg(i2c);
|
|
if (status) {
|
|
break;
|
|
}
|
|
|
|
i2c->msgs++;
|
|
i2c->msg_idx++;
|
|
}
|
|
|
|
if (!status || i2c->msg_idx > 0) {
|
|
status = i2c->msg_idx;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
static int i2c_check_enable(unsigned char i2c_num)
|
|
{
|
|
if (i2c_num >= I2C_NUM) {
|
|
return -1;
|
|
}
|
|
|
|
return g_i2c_host_cfg[i2c_num];
|
|
}
|
|
|
|
static int hal_i2c_recv_inner(const struct i2c_client *client, unsigned int *buf,
|
|
unsigned int count)
|
|
{
|
|
struct i2c_msg msg[I2C_BUF_SIZE] = {0};
|
|
unsigned char recv_buf[I2C_BUF_SIZE] = {0};
|
|
|
|
if (client->reg_width == 2) { /* reg_width:2 */
|
|
recv_buf[0] = (client->reg_addr >> 8) & 0xff; /* shift:8 */
|
|
recv_buf[1] = client->reg_addr & 0xff;
|
|
} else {
|
|
recv_buf[0] = client->reg_addr & 0xff;
|
|
}
|
|
|
|
msg[0].addr = client->dev_addr;
|
|
msg[0].flags = 0;
|
|
msg[0].len = client->reg_width;
|
|
msg[0].buf = recv_buf;
|
|
|
|
msg[1].addr = client->dev_addr;
|
|
msg[1].flags = 0;
|
|
msg[1].flags |= I2C_M_RD;
|
|
msg[1].len = count;
|
|
msg[1].buf = recv_buf;
|
|
|
|
int ret = i2c_xfer(client->i2c_num, msg, 2); /* msg num:2 */
|
|
if (ret < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (count == 2) { /* data_width:2 */
|
|
*buf = recv_buf[0] | (recv_buf[1] << 8); /* shift:8 */
|
|
} else {
|
|
*buf = recv_buf[0];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hal_i2c_send_inner(unsigned char i2c_num, unsigned short dev_addr, const char *buf,
|
|
unsigned int count)
|
|
{
|
|
struct i2c_msg msg = {0};
|
|
msg.addr = dev_addr;
|
|
msg.flags = 0;
|
|
msg.len = count;
|
|
msg.buf = (unsigned char *)buf;
|
|
int ret = i2c_xfer(i2c_num, &msg, 1);
|
|
return (ret == 1) ? count : ret;
|
|
}
|
|
|
|
static int hal_i2c_init_inner(unsigned char i2c_num)
|
|
{
|
|
struct i2c_driver_data *i2c = NULL;
|
|
struct platform_i2c *hpi = NULL;
|
|
|
|
int ret = i2c_check_enable(i2c_num);
|
|
if (ret < 0) {
|
|
return -1;
|
|
}
|
|
|
|
i2c = &g_i2c_data[i2c_num];
|
|
hpi = &g_i2c_platform_data[i2c_num];
|
|
i2c->clk = get_host_clock(i2c_num);
|
|
i2c->private = (void *)(hpi);
|
|
i2c->irq = 0;
|
|
i2c_init_cfg(i2c, i2c_num);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hal_i2c_recv(const struct i2c_client *client, unsigned int *buf,
|
|
unsigned int count)
|
|
{
|
|
if ((client == NULL) || (buf == NULL)) {
|
|
printf("[error] i2c recv param is null.\n");
|
|
return -1;
|
|
}
|
|
|
|
if (client->i2c_num >= I2C_NUM) {
|
|
printf("[error] i2c num(%u) is invalid.\n", client->i2c_num);
|
|
return -1;
|
|
}
|
|
|
|
if (count > I2C_BUF_SIZE) {
|
|
printf("[error] buf count(%u) should lees than(%d).\n", count, I2C_BUF_SIZE);
|
|
return -1;
|
|
}
|
|
|
|
return hal_i2c_recv_inner(client, buf, count);
|
|
}
|
|
|
|
int hal_i2c_send(unsigned char i2c_num, unsigned short dev_addr, const char *buf,
|
|
unsigned int count)
|
|
{
|
|
if (i2c_num >= I2C_NUM) {
|
|
printf("[error] i2c num(%u) is invalid.\n", i2c_num);
|
|
return -1;
|
|
}
|
|
|
|
if (buf == NULL) {
|
|
printf("[error] i2c send param is null.\n");
|
|
return -1;
|
|
}
|
|
|
|
if (count > I2C_BUF_SIZE) {
|
|
printf("[error] buf count(%u) should lees than(%d).\n", count, I2C_BUF_SIZE);
|
|
return -1;
|
|
}
|
|
|
|
return hal_i2c_send_inner(i2c_num, dev_addr, buf, count);
|
|
}
|
|
|
|
int hal_i2c_init(unsigned char i2c_num)
|
|
{
|
|
if (i2c_num >= I2C_NUM) {
|
|
printf("[error] i2c num(%u) is invalid.\n", i2c_num);
|
|
return -1;
|
|
}
|
|
|
|
return hal_i2c_init_inner(i2c_num);
|
|
}
|