844 lines
19 KiB
C
844 lines
19 KiB
C
#include <rtthread.h>
|
|
#include <rtdevice.h>
|
|
#include <rthw.h>
|
|
|
|
#include "board.h"
|
|
#include "drv_clock.h"
|
|
#include "drv_gpio.h"
|
|
|
|
|
|
#define I2C_DEBUG 0
|
|
|
|
#if I2C_DEBUG
|
|
#define I2C_DBG(...) rt_kprintf("[I2C]"),rt_kprintf(__VA_ARGS__)
|
|
#else
|
|
#define I2C_DBG(...)
|
|
#endif
|
|
|
|
#define I2C_CTRL (0x00)
|
|
#define I2C_TAR (0x04)
|
|
#define I2C_SAR (0x08)
|
|
#define I2C_DC (0x10)
|
|
#define I2C_SHCNT (0x14)
|
|
#define I2C_SLCNT (0x18)
|
|
#define I2C_FHCNT (0x1C)
|
|
#define I2C_FLCNT (0x20)
|
|
#define I2C_INTST (0x2C)
|
|
#define I2C_INTM (0x30)
|
|
#define I2C_RXTL (0x38)
|
|
#define I2C_TXTL (0x3c)
|
|
#define I2C_CINTR (0x40)
|
|
#define I2C_CRXUF (0x44)
|
|
#define I2C_CRXOF (0x48)
|
|
#define I2C_CTXOF (0x4C)
|
|
#define I2C_CRXREQ (0x50)
|
|
#define I2C_CTXABRT (0x54)
|
|
#define I2C_CRXDONE (0x58)
|
|
#define I2C_CACT (0x5C)
|
|
#define I2C_CSTP (0x60)
|
|
#define I2C_CSTT (0x64)
|
|
#define I2C_CGC (0x68)
|
|
#define I2C_ENB (0x6C)
|
|
#define I2C_STA (0x70)
|
|
#define I2C_TXFLR (0x74)
|
|
#define I2C_RXFLR (0x78)
|
|
#define I2C_SDAHD (0x7C)
|
|
#define I2C_TXABRT (0x80)
|
|
#define I2C_DMACR (0x88)
|
|
#define I2C_DMATDLR (0x8c)
|
|
#define I2C_DMARDLR (0x90)
|
|
#define I2C_SDASU (0x94)
|
|
#define I2C_ACKGC (0x98)
|
|
#define I2C_ENSTA (0x9C)
|
|
#define I2C_FLT (0xA0)
|
|
|
|
/* I2C Control Register (I2C_CTRL) */
|
|
#define I2C_CTRL_SLVDIS (1 << 6) /* after reset slave is disabled */
|
|
#define I2C_CTRL_REST (1 << 5)
|
|
#define I2C_CTRL_MATP (1 << 4) /* 1: 10bit address 0: 7bit addressing */
|
|
#define I2C_CTRL_SATP (1 << 3) /* 1: 10bit address 0: 7bit address */
|
|
#define I2C_CTRL_SPDF (2 << 1) /* fast mode 400kbps */
|
|
#define I2C_CTRL_SPDS (1 << 1) /* standard mode 100kbps */
|
|
#define I2C_CTRL_MD (1 << 0) /* master enabled */
|
|
|
|
/* I2C Status Register (I2C_STA) */
|
|
#define I2C_STA_SLVACT (1 << 6) /* Slave FSM is not in IDLE state */
|
|
#define I2C_STA_MSTACT (1 << 5) /* Master FSM is not in IDLE state */
|
|
#define I2C_STA_RFF (1 << 4) /* RFIFO if full */
|
|
#define I2C_STA_RFNE (1 << 3) /* RFIFO is not empty */
|
|
#define I2C_STA_TFE (1 << 2) /* TFIFO is empty */
|
|
#define I2C_STA_TFNF (1 << 1) /* TFIFO is not full */
|
|
#define I2C_STA_ACT (1 << 0) /* I2C Activity Status */
|
|
|
|
/* i2c interrupt status (I2C_INTST) */
|
|
#define I2C_INTST_IGC (1 << 11)
|
|
#define I2C_INTST_ISTT (1 << 10)
|
|
#define I2C_INTST_ISTP (1 << 9)
|
|
#define I2C_INTST_IACT (1 << 8)
|
|
#define I2C_INTST_RXDN (1 << 7)
|
|
#define I2C_INTST_TXABT (1 << 6)
|
|
#define I2C_INTST_RDREQ (1 << 5)
|
|
#define I2C_INTST_TXEMP (1 << 4)
|
|
#define I2C_INTST_TXOF (1 << 3)
|
|
#define I2C_INTST_RXFL (1 << 2)
|
|
#define I2C_INTST_RXOF (1 << 1)
|
|
#define I2C_INTST_RXUF (1 << 0)
|
|
|
|
/* i2c interrupt mask status (I2C_INTM) */
|
|
#define I2C_INTM_MIGC (1 << 11)
|
|
#define I2C_INTM_MISTT (1 << 10)
|
|
#define I2C_INTM_MISTP (1 << 9)
|
|
#define I2C_INTM_MIACT (1 << 8)
|
|
#define I2C_INTM_MRXDN (1 << 7)
|
|
#define I2C_INTM_MTXABT (1 << 6)
|
|
#define I2C_INTM_MRDREQ (1 << 5)
|
|
#define I2C_INTM_MTXEMP (1 << 4)
|
|
#define I2C_INTM_MTXOF (1 << 3)
|
|
#define I2C_INTM_MRXFL (1 << 2)
|
|
#define I2C_INTM_MRXOF (1 << 1)
|
|
#define I2C_INTM_MRXUF (1 << 0)
|
|
|
|
#define I2C_DC_REST (1 << 10)
|
|
#define I2C_DC_STP (1 << 9)
|
|
#define I2C_DC_READ (1 << 8)
|
|
|
|
#define I2C_ENB_I2CENB (1 << 0) /* Enable the i2c */
|
|
|
|
#define CONFIG_I2C_FIFO_LEN 64
|
|
#define I2C_FIFO_LEN (CONFIG_I2C_FIFO_LEN)
|
|
#define TX_LEVEL (I2C_FIFO_LEN / 2)
|
|
#define RX_LEVEL (I2C_FIFO_LEN / 2 - 1)
|
|
|
|
#define TIMEOUT 0xff
|
|
|
|
/*
|
|
* msg_end_type: The bus control which need to be send at end of transfer.
|
|
* @MSG_END_STOP: Send stop pulse at end of transfer.
|
|
* @MSG_END_REPEAT_START: Send repeat start at end of transfer.
|
|
*/
|
|
enum msg_end_type
|
|
{
|
|
MSG_END_STOP,
|
|
MSG_END_CONTINUE,
|
|
MSG_END_REPEAT_START,
|
|
};
|
|
|
|
/* I2C standard mode high count register(I2CSHCNT) */
|
|
#define I2CSHCNT_ADJUST(n) (((n) - 8) < 6 ? 6 : ((n) - 8))
|
|
/* I2C standard mode low count register(I2CSLCNT) */
|
|
#define I2CSLCNT_ADJUST(n) (((n) - 1) < 8 ? 8 : ((n) - 1))
|
|
/* I2C fast mode high count register(I2CFHCNT) */
|
|
#define I2CFHCNT_ADJUST(n) (((n) - 8) < 6 ? 6 : ((n) - 8))
|
|
/* I2C fast mode low count register(I2CFLCNT) */
|
|
#define I2CFLCNT_ADJUST(n) (((n) - 1) < 8 ? 8 : ((n) - 1))
|
|
|
|
/* I2C Transmit Abort Status Register (I2C_TXABRT) */
|
|
static const char *abrt_src[] =
|
|
{
|
|
"I2C_TXABRT_ABRT_7B_ADDR_NOACK",
|
|
"I2C_TXABRT_ABRT_10ADDR1_NOACK",
|
|
"I2C_TXABRT_ABRT_10ADDR2_NOACK",
|
|
"I2C_TXABRT_ABRT_XDATA_NOACK",
|
|
"I2C_TXABRT_ABRT_GCALL_NOACK",
|
|
"I2C_TXABRT_ABRT_GCALL_READ",
|
|
"I2C_TXABRT_ABRT_HS_ACKD",
|
|
"I2C_TXABRT_SBYTE_ACKDET",
|
|
"I2C_TXABRT_ABRT_HS_NORSTRT",
|
|
"I2C_TXABRT_SBYTE_NORSTRT",
|
|
"I2C_TXABRT_ABRT_10B_RD_NORSTRT",
|
|
"I2C_TXABRT_ABRT_MASTER_DIS",
|
|
"I2C_TXABRT_ARB_LOST",
|
|
"I2C_TXABRT_SLVFLUSH_TXFIFO",
|
|
"I2C_TXABRT_SLV_ARBLOST",
|
|
"I2C_TXABRT_SLVRD_INTX",
|
|
};
|
|
|
|
struct ingenic_i2c_bus
|
|
{
|
|
struct rt_i2c_bus_device parent;
|
|
|
|
rt_uint32_t hwaddr;
|
|
rt_uint32_t irqno;
|
|
|
|
struct clk *clk;
|
|
|
|
enum msg_end_type w_end_type;
|
|
enum msg_end_type r_end_type;
|
|
|
|
unsigned char *rbuf;
|
|
unsigned char *wbuf;
|
|
unsigned int rd_len;
|
|
|
|
int len;
|
|
unsigned int rate;
|
|
|
|
struct rt_completion completion;
|
|
};
|
|
|
|
#ifdef RT_USING_I2C0
|
|
static struct ingenic_i2c_bus ingenic_i2c0;
|
|
#endif
|
|
|
|
#ifdef RT_USING_I2C1
|
|
static struct ingenic_i2c_bus ingenic_i2c1;
|
|
#endif
|
|
|
|
#ifdef RT_USING_I2C2
|
|
static struct ingenic_i2c_bus ingenic_i2c2;
|
|
#endif
|
|
|
|
static inline unsigned short i2c_readl(struct ingenic_i2c_bus *i2c,
|
|
unsigned short offset)
|
|
{
|
|
return readl(i2c->hwaddr + offset);
|
|
}
|
|
|
|
static inline void i2c_writel(struct ingenic_i2c_bus *i2c, unsigned short offset,
|
|
unsigned short value)
|
|
{
|
|
writel(value, i2c->hwaddr + offset);
|
|
}
|
|
|
|
static int ingenic_i2c_disable(struct ingenic_i2c_bus *i2c)
|
|
{
|
|
int timeout = TIMEOUT;
|
|
|
|
i2c_writel(i2c, I2C_ENB, 0);
|
|
|
|
while ((i2c_readl(i2c, I2C_ENSTA) & I2C_ENB_I2CENB) && (--timeout > 0))
|
|
rt_thread_delay(1);
|
|
|
|
if (timeout) return 0;
|
|
|
|
I2C_DBG("disable i2c failed!\n");
|
|
return -RT_ETIMEOUT;
|
|
}
|
|
|
|
static int ingenic_i2c_enable(struct ingenic_i2c_bus *i2c)
|
|
{
|
|
int timeout = TIMEOUT;
|
|
|
|
i2c_writel(i2c, I2C_ENB, 1);
|
|
|
|
while (!(i2c_readl(i2c, I2C_ENSTA) & I2C_ENB_I2CENB) && (--timeout > 0))
|
|
rt_thread_delay(1);
|
|
|
|
if (timeout) return 0;
|
|
|
|
I2C_DBG("enable i2c failed\n");
|
|
return -RT_ETIMEOUT;
|
|
}
|
|
|
|
static void ingenic_i2c_reset(struct ingenic_i2c_bus *i2c)
|
|
{
|
|
i2c_readl(i2c, I2C_CTXABRT);
|
|
i2c_readl(i2c, I2C_INTST);
|
|
|
|
ingenic_i2c_disable(i2c);
|
|
rt_thread_delay(1);
|
|
ingenic_i2c_enable(i2c);
|
|
}
|
|
|
|
|
|
static int ingenic_i2c_set_speed(struct ingenic_i2c_bus *i2c, int rate)
|
|
{
|
|
/*ns */
|
|
long dev_clk = clk_get_rate(i2c->clk);
|
|
long cnt_high = 0; /* HIGH period count of the SCL clock */
|
|
long cnt_low = 0; /* LOW period count of the SCL clock */
|
|
long setup_time = 0;
|
|
long hold_time = 0;
|
|
unsigned short tmp;
|
|
|
|
i2c->rate = rate;
|
|
|
|
if (ingenic_i2c_disable(i2c))I2C_DBG("i2c not disable\n");
|
|
|
|
if (rate <= 100000)
|
|
{
|
|
tmp = 0x43 | (1 << 5); /* standard speed mode */
|
|
i2c_writel(i2c, I2C_CTRL, tmp);
|
|
}
|
|
else
|
|
{
|
|
tmp = 0x45 | (1 << 5); /* fast speed mode */
|
|
i2c_writel(i2c, I2C_CTRL, tmp);
|
|
}
|
|
|
|
/* high
|
|
* ____ ____ ____ ____
|
|
* clk __| | |___| |____| |____| |___
|
|
* | | |
|
|
* | | |
|
|
* |_|_| _________ ____
|
|
* data __/ | |\___/ \____/ \____
|
|
* setup->| |<|
|
|
* ->| |<-hold
|
|
*/
|
|
|
|
//setup_time = (10 000 000/(rate*4)) + 1;
|
|
setup_time = (dev_clk / (rate * 4));
|
|
if (setup_time > 1) setup_time -= 1;
|
|
|
|
//hold_time = (10000000/(rate*4)) - 1;
|
|
hold_time = (dev_clk / (rate * 4));
|
|
|
|
/* high
|
|
* ____ ____
|
|
* clk __| |___| |____
|
|
* low
|
|
* |<--period--->|
|
|
*
|
|
*/
|
|
cnt_high = dev_clk / (rate * 2);
|
|
cnt_low = dev_clk / (rate * 2);
|
|
|
|
if (setup_time > 255) setup_time = 255;
|
|
if (setup_time <= 0) setup_time = 1;
|
|
if (hold_time > 0xFFFF) hold_time = 0xFFFF;
|
|
|
|
if (rate <= 100000)
|
|
{
|
|
i2c_writel(i2c, I2C_SHCNT, I2CSHCNT_ADJUST(cnt_high));
|
|
i2c_writel(i2c, I2C_SLCNT, I2CSLCNT_ADJUST(cnt_low));
|
|
}
|
|
else
|
|
{
|
|
i2c_writel(i2c, I2C_FHCNT, I2CFHCNT_ADJUST(cnt_high));
|
|
i2c_writel(i2c, I2C_FLCNT, I2CFLCNT_ADJUST(cnt_low));
|
|
}
|
|
|
|
i2c_writel(i2c, I2C_SDASU, setup_time & 0xff);
|
|
i2c_writel(i2c, I2C_SDAHD, hold_time);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* function: send read command
|
|
* return: 0, successful
|
|
* 1, txfifo valid entry is more than receive fifo, before send read command,
|
|
* must be read.
|
|
* 2, txfifo count is 0 or rxfifo count is 0.
|
|
* */
|
|
static inline unsigned int i2c_send_rcmd(struct ingenic_i2c_bus *i2c)
|
|
{
|
|
unsigned int tx_count, rx_count, count, tx_valid, rx_valid;
|
|
|
|
tx_valid = i2c_readl(i2c, I2C_TXFLR);
|
|
rx_valid = i2c_readl(i2c, I2C_RXFLR);
|
|
tx_count = I2C_FIFO_LEN - tx_valid;
|
|
rx_count = I2C_FIFO_LEN - rx_valid;
|
|
|
|
if (tx_valid > rx_count)
|
|
{
|
|
I2C_DBG("###Warning: I2C transfer fifo valid entry is more receive fifo, "
|
|
"before send read cmd, please read data from "
|
|
"the read fifo.\n");
|
|
return 1;
|
|
}
|
|
|
|
if (!tx_count || !rx_count)
|
|
{
|
|
I2C_DBG("###Warning: I2C receive fifo or transfer fifo is full, "
|
|
"before send read cmd, please read data from "
|
|
"the read fifo or wait some time.\n\n");
|
|
return 2;
|
|
}
|
|
|
|
count = (i2c->rd_len < tx_count)? i2c->rd_len : tx_count;
|
|
count = (count < rx_count)? count : rx_count;
|
|
|
|
i2c->rd_len -= count;
|
|
|
|
if (!i2c->rd_len)
|
|
{
|
|
while (count > 1)
|
|
{
|
|
i2c_writel(i2c, I2C_DC, I2C_DC_READ);
|
|
count--;
|
|
}
|
|
|
|
if (i2c->r_end_type == MSG_END_STOP)
|
|
{
|
|
i2c_writel(i2c, I2C_DC, I2C_DC_READ | I2C_DC_STP);
|
|
}
|
|
else
|
|
{
|
|
i2c_writel(i2c, I2C_DC, I2C_DC_READ);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (count > 0)
|
|
{
|
|
i2c_writel(i2c, I2C_DC, I2C_DC_READ);
|
|
count--;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void txabrt(struct ingenic_i2c_bus *i2c, int src)
|
|
{
|
|
int i;
|
|
|
|
I2C_DBG("--I2C txabrt:\n");
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
if (src & (0x1 << i))
|
|
I2C_DBG("--I2C TXABRT[%d]=%s\n", i, abrt_src[i]);
|
|
}
|
|
}
|
|
|
|
static int i2c_disable_clk(struct ingenic_i2c_bus *i2c)
|
|
{
|
|
int timeout = 10;
|
|
int tmp = i2c_readl(i2c, I2C_STA);
|
|
|
|
while ((tmp & I2C_STA_MSTACT) && (--timeout > 0))
|
|
{
|
|
rt_thread_delay(2);
|
|
tmp = i2c_readl(i2c, I2C_STA);
|
|
}
|
|
|
|
if (timeout > 0)
|
|
{
|
|
clk_disable(i2c->clk);
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
I2C_DBG("--I2C disable clk timeout, I2C_STA = 0x%x\n", tmp);
|
|
ingenic_i2c_reset(i2c);
|
|
clk_disable(i2c->clk);
|
|
|
|
return -RT_ETIMEOUT;
|
|
}
|
|
}
|
|
|
|
static inline int xfer_read(struct ingenic_i2c_bus *i2c, unsigned char *buf, int len, enum msg_end_type end_type)
|
|
{
|
|
int ret = 0;
|
|
long timeout;
|
|
unsigned short tmp;
|
|
|
|
rt_memset(buf, 0, len);
|
|
|
|
i2c->rd_len = len;
|
|
i2c->len = len;
|
|
i2c->rbuf = buf;
|
|
i2c->r_end_type = end_type;
|
|
|
|
i2c_readl(i2c, I2C_CSTP); /* clear STP bit */
|
|
i2c_readl(i2c, I2C_CTXOF); /* clear TXOF bit */
|
|
i2c_readl(i2c, I2C_CTXABRT); /* clear TXABRT bit */
|
|
|
|
I2C_DBG("i2c: read %d len data\n", len);
|
|
|
|
if (len <= I2C_FIFO_LEN)
|
|
{
|
|
i2c_writel(i2c, I2C_RXTL, len - 1);
|
|
}
|
|
else
|
|
{
|
|
i2c_writel(i2c, I2C_RXTL, RX_LEVEL);
|
|
}
|
|
// I2C_DBG("RXTL: %x\n", i2c_readl(i2c, I2C_RXTL));
|
|
|
|
I2C_DBG("read len: %d\n", i2c_readl(i2c, I2C_RXFLR));
|
|
|
|
while (i2c_readl(i2c, I2C_STA) & I2C_STA_RFNE)
|
|
{
|
|
i2c_readl(i2c, I2C_DC);
|
|
}
|
|
|
|
if (i2c_send_rcmd(i2c))
|
|
{
|
|
I2C_DBG("i2c: send read command failed!\n");
|
|
}
|
|
else
|
|
{
|
|
I2C_DBG("i2c: send read command OK!\n");
|
|
}
|
|
|
|
tmp = I2C_INTM_MRXFL | I2C_INTM_MTXABT;
|
|
if (end_type == MSG_END_STOP) tmp |= I2C_INTM_MISTP;
|
|
i2c_writel(i2c, I2C_INTM, tmp);
|
|
|
|
/* wait for finish */
|
|
while(rt_completion_wait(&(i2c->completion), RT_TICK_PER_SECOND) == -RT_ETIMEOUT)
|
|
I2C_DBG("fifo len: %d\n", i2c_readl(i2c, I2C_RXFLR));
|
|
|
|
tmp = i2c_readl(i2c, I2C_TXABRT);
|
|
if (tmp)
|
|
{
|
|
txabrt(i2c, tmp);
|
|
if (tmp > 0x1 && tmp < 0x10)
|
|
ret = -RT_EIO;
|
|
else
|
|
ret = -RT_EIO;
|
|
|
|
/* abort read */
|
|
if (tmp & (1 << 5)) {
|
|
ret = -RT_ERROR;
|
|
}
|
|
i2c_readl(i2c, I2C_CTXABRT);
|
|
}
|
|
|
|
if (ret < 0) ingenic_i2c_reset(i2c);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static inline int xfer_write(struct ingenic_i2c_bus *i2c, unsigned char *buf, int len, enum msg_end_type end_type)
|
|
{
|
|
int ret = 0;
|
|
long timeout = TIMEOUT;
|
|
unsigned short reg_tmp;
|
|
|
|
i2c->wbuf = buf;
|
|
i2c->len = len;
|
|
|
|
i2c_writel(i2c, I2C_TXTL, TX_LEVEL);
|
|
|
|
i2c_readl(i2c, I2C_CSTP); /* clear STP bit */
|
|
i2c_readl(i2c, I2C_CTXOF); /* clear TXOF bit */
|
|
i2c_readl(i2c, I2C_CTXABRT); /* clear TXABRT bit */
|
|
|
|
I2C_DBG("i2c: write %d len data\n", len);
|
|
|
|
i2c->w_end_type = end_type;
|
|
|
|
while ((i2c_readl(i2c, I2C_STA) & I2C_STA_TFNF) && (i2c->len > 0))
|
|
{
|
|
reg_tmp = *i2c->wbuf++;
|
|
if (i2c->len == 1)
|
|
{
|
|
if (end_type == MSG_END_STOP)
|
|
{
|
|
reg_tmp |= I2C_DC_STP;
|
|
}
|
|
}
|
|
i2c_writel(i2c, I2C_DC, reg_tmp);
|
|
|
|
i2c->len -= 1;
|
|
}
|
|
|
|
if (i2c->len == 0)
|
|
{
|
|
i2c_writel(i2c, I2C_TXTL, 0);
|
|
}
|
|
|
|
reg_tmp = I2C_INTM_MTXEMP | I2C_INTM_MTXABT | I2C_INTM_MTXOF;
|
|
if (end_type == MSG_END_STOP) reg_tmp |= I2C_INTM_MISTP;
|
|
i2c_writel(i2c, I2C_INTM, reg_tmp);
|
|
|
|
/* wait for finish */
|
|
rt_completion_wait(&(i2c->completion), rt_tick_from_millisecond(2000));
|
|
|
|
reg_tmp = i2c_readl(i2c, I2C_TXABRT);
|
|
if (reg_tmp)
|
|
{
|
|
txabrt(i2c, reg_tmp);
|
|
if (reg_tmp > 0x1 && reg_tmp < 0x10)
|
|
ret = -RT_EIO;
|
|
else
|
|
ret = -RT_EIO;
|
|
|
|
//after I2C_TXABRT_ABRT_XDATA_NOACK error,this required core to resend
|
|
if (reg_tmp & 8)
|
|
{
|
|
ret = -RT_ERROR;
|
|
}
|
|
i2c_readl(i2c, I2C_CTXABRT);
|
|
}
|
|
|
|
if (ret < 0) ingenic_i2c_reset(i2c);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static rt_size_t ingenic_i2c_xfer(struct rt_i2c_bus_device *bus, struct rt_i2c_msg msgs[], rt_uint32_t num)
|
|
{
|
|
int i;
|
|
int ret = RT_EOK;
|
|
struct ingenic_i2c_bus *i2c;
|
|
|
|
i2c = (struct ingenic_i2c_bus*)bus;
|
|
|
|
clk_enable(i2c->clk);
|
|
i2c_writel(i2c, I2C_TAR, msgs[0].addr);
|
|
|
|
for (i = 0; i < num; i++)
|
|
{
|
|
enum msg_end_type end_type = MSG_END_STOP;
|
|
|
|
if (i < (num - 1))
|
|
{
|
|
if (msgs[i + 1].flags & RT_I2C_NO_START)
|
|
{
|
|
end_type = MSG_END_CONTINUE; /* have no STOP and START */
|
|
}
|
|
else
|
|
{
|
|
end_type = MSG_END_REPEAT_START; /* have no STOP but have RESTART */
|
|
}
|
|
}
|
|
|
|
/* initialize completion */
|
|
rt_completion_init(&(i2c->completion));
|
|
|
|
if (msgs[i].flags & RT_I2C_RD)
|
|
{
|
|
ret = xfer_read(i2c, msgs[i].buf, msgs[i].len, end_type);
|
|
}
|
|
else
|
|
{
|
|
ret = xfer_write(i2c, msgs[i].buf, msgs[i].len, end_type);
|
|
}
|
|
|
|
if (ret < 0)
|
|
{
|
|
clk_disable(i2c->clk);
|
|
goto _ERR;
|
|
}
|
|
}
|
|
|
|
ret = i2c_disable_clk(i2c);
|
|
|
|
_ERR:
|
|
return ret < 0? ret : i;
|
|
}
|
|
|
|
static const struct rt_i2c_bus_device_ops i2c_ops =
|
|
{
|
|
ingenic_i2c_xfer,
|
|
RT_NULL,
|
|
RT_NULL
|
|
};
|
|
|
|
static void i2c_irq_handler(int irqno, void *param)
|
|
{
|
|
unsigned short tmp, intst, intmsk;
|
|
struct ingenic_i2c_bus *i2c;
|
|
|
|
i2c = (struct ingenic_i2c_bus*)param;
|
|
|
|
intst = i2c_readl(i2c, I2C_INTST);
|
|
intmsk = i2c_readl(i2c, I2C_INTM);
|
|
|
|
I2C_DBG("i2c irq!!\n");
|
|
|
|
if ((intst & I2C_INTST_TXABT) && (intmsk & I2C_INTM_MTXABT))
|
|
{
|
|
I2C_DBG("%s %d, I2C transfer error, ABORT interrupt\n", __func__, __LINE__);
|
|
goto END_TRSF_IRQ_HND;
|
|
}
|
|
|
|
if ((intst & I2C_INTST_ISTP) && (intmsk & I2C_INTM_MISTP))
|
|
{
|
|
i2c_readl(i2c, I2C_CSTP); /* clear STP bit */
|
|
|
|
if (i2c->len == 0)
|
|
goto END_TRSF_IRQ_HND;
|
|
}
|
|
|
|
if ((intmsk & I2C_INTM_MTXEMP) && (intst & I2C_INTST_TXEMP))
|
|
{
|
|
if (!i2c->len)
|
|
{
|
|
if (i2c->w_end_type == MSG_END_REPEAT_START)
|
|
{
|
|
goto END_TRSF_IRQ_HND;
|
|
}
|
|
else
|
|
{
|
|
tmp = i2c_readl(i2c, I2C_INTM);
|
|
tmp &= ~I2C_INTM_MTXEMP;
|
|
i2c_writel(i2c, I2C_INTM, tmp);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while ((i2c->len > 0) && (i2c_readl(i2c, I2C_STA) & I2C_STA_TFNF))
|
|
{
|
|
tmp = *i2c->wbuf++;
|
|
if (i2c->len == 1)
|
|
{
|
|
if (i2c->w_end_type == MSG_END_STOP)
|
|
tmp |= I2C_DC_STP;
|
|
}
|
|
|
|
i2c_writel(i2c, I2C_DC, tmp);
|
|
i2c->len -= 1;
|
|
}
|
|
|
|
if (i2c->len == 0)
|
|
{
|
|
i2c_writel(i2c, I2C_TXTL, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((intst & I2C_INTST_RXFL) && (intmsk & I2C_INTM_MRXFL))
|
|
{
|
|
I2C_DBG("I2C RXFL\n");
|
|
while ((i2c_readl(i2c, I2C_STA) & I2C_STA_RFNE) && (i2c->len > 0))
|
|
{
|
|
tmp = i2c_readl(i2c, I2C_DC) & 0xff;
|
|
*i2c->rbuf++ = tmp;
|
|
i2c->len--;
|
|
}
|
|
|
|
if (i2c->len == 0)
|
|
{
|
|
goto END_RECE_IRQ_HND;
|
|
}
|
|
|
|
if (i2c->len <= I2C_FIFO_LEN)
|
|
{
|
|
i2c_writel(i2c, I2C_RXTL, i2c->len - 1);
|
|
}
|
|
|
|
if (i2c_send_rcmd(i2c))
|
|
{
|
|
I2C_DBG("%s %d, I2C controller has BUG, RXFLR or TXFLR can not clear\n", __func__, __LINE__);
|
|
}
|
|
}
|
|
|
|
if ((intst & I2C_INTST_RXOF) && (intmsk & I2C_INTM_MRXOF))
|
|
{
|
|
I2C_DBG("%s %d, I2C transfer error, RXFIFO over full\n", __func__, __LINE__);
|
|
i2c_readl(i2c, I2C_CRXOF); /* clear RXOF bit */
|
|
}
|
|
|
|
if ((intst & I2C_INTST_TXOF) && (intmsk & I2C_INTM_MTXOF))
|
|
{
|
|
I2C_DBG("%s %d, I2C transfer error, TXFIFO over full\n", __func__, __LINE__);
|
|
i2c_readl(i2c, I2C_CTXOF); /* clear TXOF bit */
|
|
goto END_TRSF_IRQ_HND;
|
|
}
|
|
|
|
return ;
|
|
|
|
END_RECE_IRQ_HND:
|
|
END_TRSF_IRQ_HND:
|
|
i2c_writel(i2c, I2C_INTM, 0);
|
|
rt_completion_done(&i2c->completion);
|
|
}
|
|
|
|
int rt_hw_i2c_init(void)
|
|
{
|
|
struct ingenic_i2c_bus *i2c;
|
|
struct rt_i2c_bus_device *i2c_bus;
|
|
|
|
#ifdef RT_USING_I2C0
|
|
{
|
|
i2c = &ingenic_i2c0;
|
|
rt_memset((void *)i2c, 0, sizeof(struct ingenic_i2c_bus));
|
|
|
|
i2c->hwaddr = I2C0_BASE;
|
|
i2c->irqno = IRQ_I2C0;
|
|
|
|
/* Set PB23 PB24 in func0 (I2C0) */
|
|
gpio_set_func(GPIO_PORT_B, GPIO_Pin_24, GPIO_FUNC_0);
|
|
gpio_set_func(GPIO_PORT_B, GPIO_Pin_23, GPIO_FUNC_0);
|
|
|
|
/* enable clock */
|
|
i2c->clk = clk_get("i2c0");
|
|
clk_enable(i2c->clk);
|
|
|
|
i2c_bus = &i2c->parent;
|
|
i2c_bus->ops = &i2c_ops;
|
|
rt_i2c_bus_device_register(i2c_bus, "i2c0");
|
|
|
|
ingenic_i2c_set_speed(i2c, 400 * 1000);
|
|
|
|
/* reset I2C */
|
|
i2c_writel(i2c, I2C_CTRL, i2c_readl(i2c, I2C_CTRL) | I2C_CTRL_REST);
|
|
i2c_writel(i2c, I2C_INTM, 0x0);
|
|
|
|
ingenic_i2c_enable(i2c);
|
|
clk_disable(i2c->clk);
|
|
|
|
/* install interrupt */
|
|
rt_hw_interrupt_install(i2c->irqno, i2c_irq_handler, i2c, "i2c0");
|
|
rt_hw_interrupt_umask(i2c->irqno);
|
|
}
|
|
#endif
|
|
|
|
#ifdef RT_USING_I2C1
|
|
{
|
|
i2c = &ingenic_i2c1;
|
|
rt_memset((void *)i2c, 0, sizeof(struct ingenic_i2c_bus));
|
|
|
|
i2c->hwaddr = I2C1_BASE;
|
|
i2c->irqno = IRQ_I2C1;
|
|
|
|
/* Set PC26 PC27 in func0 (I2C1) */
|
|
gpio_set_func(GPIO_PORT_C, GPIO_Pin_26, GPIO_FUNC_0);
|
|
gpio_set_func(GPIO_PORT_C, GPIO_Pin_27, GPIO_FUNC_0);
|
|
|
|
/* enable clock */
|
|
i2c->clk = clk_get("i2c1");
|
|
clk_enable(i2c->clk);
|
|
|
|
i2c_bus = &i2c->parent;
|
|
i2c_bus->ops = &i2c_ops;
|
|
rt_i2c_bus_device_register(i2c_bus, "i2c1");
|
|
|
|
ingenic_i2c_set_speed(i2c, 400 * 1000);
|
|
|
|
/* reset I2C */
|
|
i2c_writel(i2c, I2C_CTRL, i2c_readl(i2c, I2C_CTRL) | I2C_CTRL_REST);
|
|
i2c_writel(i2c, I2C_INTM, 0x0);
|
|
|
|
ingenic_i2c_enable(i2c);
|
|
clk_disable(i2c->clk);
|
|
|
|
/* install interrupt */
|
|
rt_hw_interrupt_install(i2c->irqno, i2c_irq_handler, i2c, "i2c1");
|
|
rt_hw_interrupt_umask(i2c->irqno);
|
|
}
|
|
#endif
|
|
|
|
#ifdef RT_USING_I2C2
|
|
{
|
|
i2c = &ingenic_i2c2;
|
|
rt_memset((void *)i2c, 0, sizeof(struct ingenic_i2c_bus));
|
|
|
|
i2c->hwaddr = I2C2_BASE;
|
|
i2c->irqno = IRQ_I2C2;
|
|
|
|
/* Set PC26 PC27 in func0 (I2C1) */
|
|
gpio_set_func(GPIO_PORT_D, GPIO_Pin_0, GPIO_FUNC_1);
|
|
gpio_set_func(GPIO_PORT_D, GPIO_Pin_1, GPIO_FUNC_1);
|
|
|
|
/* enable clock */
|
|
i2c->clk = clk_get("i2c2");
|
|
clk_enable(i2c->clk);
|
|
|
|
i2c_bus = &i2c->parent;
|
|
i2c_bus->ops = &i2c_ops;
|
|
rt_i2c_bus_device_register(i2c_bus, "i2c2");
|
|
|
|
ingenic_i2c_set_speed(i2c, 400 * 1000);
|
|
|
|
/* reset I2C */
|
|
i2c_writel(i2c, I2C_CTRL, i2c_readl(i2c, I2C_CTRL) | I2C_CTRL_REST);
|
|
i2c_writel(i2c, I2C_INTM, 0x0);
|
|
|
|
ingenic_i2c_enable(i2c);
|
|
clk_disable(i2c->clk);
|
|
|
|
/* install interrupt */
|
|
rt_hw_interrupt_install(i2c->irqno, i2c_irq_handler, i2c, "i2c2");
|
|
rt_hw_interrupt_umask(i2c->irqno);
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
INIT_BOARD_EXPORT(rt_hw_i2c_init);
|