/*
- * Synopsys Designware I2C adapter driver (master only).
+ * Synopsys DesignWare I2C adapter driver (master only).
*
* Based on the TI DAVINCI I2C adapter driver.
*
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/io.h>
+#include <linux/slab.h>
/*
* Registers offset
#define DW_IC_INTR_STAT 0x2c
#define DW_IC_INTR_MASK 0x30
#define DW_IC_RAW_INTR_STAT 0x34
+#define DW_IC_RX_TL 0x38
+#define DW_IC_TX_TL 0x3c
#define DW_IC_CLR_INTR 0x40
#define DW_IC_CLR_RX_UNDER 0x44
#define DW_IC_CLR_RX_OVER 0x48
#define DW_IC_INTR_START_DET 0x400
#define DW_IC_INTR_GEN_CALL 0x800
+#define DW_IC_INTR_DEFAULT_MASK (DW_IC_INTR_RX_FULL | \
+ DW_IC_INTR_TX_EMPTY | \
+ DW_IC_INTR_TX_ABRT | \
+ DW_IC_INTR_STOP_DET)
+
#define DW_IC_STATUS_ACTIVITY 0x1
#define DW_IC_ERR_TX_ABRT 0x1
#define ABRT_SBYTE_ACKDET 7
#define ABRT_SBYTE_NORSTRT 9
#define ABRT_10B_RD_NORSTRT 10
-#define ARB_MASTER_DIS 11
+#define ABRT_MASTER_DIS 11
#define ARB_LOST 12
+#define DW_IC_TX_ABRT_7B_ADDR_NOACK (1UL << ABRT_7B_ADDR_NOACK)
+#define DW_IC_TX_ABRT_10ADDR1_NOACK (1UL << ABRT_10ADDR1_NOACK)
+#define DW_IC_TX_ABRT_10ADDR2_NOACK (1UL << ABRT_10ADDR2_NOACK)
+#define DW_IC_TX_ABRT_TXDATA_NOACK (1UL << ABRT_TXDATA_NOACK)
+#define DW_IC_TX_ABRT_GCALL_NOACK (1UL << ABRT_GCALL_NOACK)
+#define DW_IC_TX_ABRT_GCALL_READ (1UL << ABRT_GCALL_READ)
+#define DW_IC_TX_ABRT_SBYTE_ACKDET (1UL << ABRT_SBYTE_ACKDET)
+#define DW_IC_TX_ABRT_SBYTE_NORSTRT (1UL << ABRT_SBYTE_NORSTRT)
+#define DW_IC_TX_ABRT_10B_RD_NORSTRT (1UL << ABRT_10B_RD_NORSTRT)
+#define DW_IC_TX_ABRT_MASTER_DIS (1UL << ABRT_MASTER_DIS)
+#define DW_IC_TX_ARB_LOST (1UL << ARB_LOST)
+
+#define DW_IC_TX_ABRT_NOACK (DW_IC_TX_ABRT_7B_ADDR_NOACK | \
+ DW_IC_TX_ABRT_10ADDR1_NOACK | \
+ DW_IC_TX_ABRT_10ADDR2_NOACK | \
+ DW_IC_TX_ABRT_TXDATA_NOACK | \
+ DW_IC_TX_ABRT_GCALL_NOACK)
+
static char *abort_sources[] = {
- [ABRT_7B_ADDR_NOACK] =
+ [ABRT_7B_ADDR_NOACK] =
"slave address not acknowledged (7bit mode)",
- [ABRT_10ADDR1_NOACK] =
+ [ABRT_10ADDR1_NOACK] =
"first address byte not acknowledged (10bit mode)",
- [ABRT_10ADDR2_NOACK] =
+ [ABRT_10ADDR2_NOACK] =
"second address byte not acknowledged (10bit mode)",
- [ABRT_TXDATA_NOACK] =
+ [ABRT_TXDATA_NOACK] =
"data not acknowledged",
- [ABRT_GCALL_NOACK] =
+ [ABRT_GCALL_NOACK] =
"no acknowledgement for a general call",
- [ABRT_GCALL_READ] =
+ [ABRT_GCALL_READ] =
"read after general call",
- [ABRT_SBYTE_ACKDET] =
+ [ABRT_SBYTE_ACKDET] =
"start byte acknowledged",
- [ABRT_SBYTE_NORSTRT] =
+ [ABRT_SBYTE_NORSTRT] =
"trying to send start byte when restart is disabled",
- [ABRT_10B_RD_NORSTRT] =
+ [ABRT_10B_RD_NORSTRT] =
"trying to read when restart is disabled (10bit mode)",
- [ARB_MASTER_DIS] =
+ [ABRT_MASTER_DIS] =
"trying to use disabled adapter",
- [ARB_LOST] =
+ [ARB_LOST] =
"lost arbitration",
};
* @dev: driver model device node
* @base: IO registers pointer
* @cmd_complete: tx completion indicator
- * @pump_msg: continue in progress transfers
* @lock: protect this struct and IO registers
* @clk: input reference clock
* @cmd_err: run time hadware error code
struct device *dev;
void __iomem *base;
struct completion cmd_complete;
- struct tasklet_struct pump_msg;
struct mutex lock;
struct clk *clk;
int cmd_err;
writel(lcnt, dev->base + DW_IC_FS_SCL_LCNT);
dev_dbg(dev->dev, "Fast-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);
+ /* Configure Tx/Rx FIFO threshold levels */
+ writel(dev->tx_fifo_depth - 1, dev->base + DW_IC_TX_TL);
+ writel(0, dev->base + DW_IC_RX_TL);
+
/* configure the i2c master */
ic_con = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |
DW_IC_CON_RESTART_EN | DW_IC_CON_SPEED_FAST;
return 0;
}
+static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
+{
+ struct i2c_msg *msgs = dev->msgs;
+ u32 ic_con;
+
+ /* Disable the adapter */
+ writel(0, dev->base + DW_IC_ENABLE);
+
+ /* set the slave (target) address */
+ writel(msgs[dev->msg_write_idx].addr, dev->base + DW_IC_TAR);
+
+ /* if the slave address is ten bit address, enable 10BITADDR */
+ ic_con = readl(dev->base + DW_IC_CON);
+ if (msgs[dev->msg_write_idx].flags & I2C_M_TEN)
+ ic_con |= DW_IC_CON_10BITADDR_MASTER;
+ else
+ ic_con &= ~DW_IC_CON_10BITADDR_MASTER;
+ writel(ic_con, dev->base + DW_IC_CON);
+
+ /* Enable the adapter */
+ writel(1, dev->base + DW_IC_ENABLE);
+
+ /* Enable interrupts */
+ writel(DW_IC_INTR_DEFAULT_MASK, dev->base + DW_IC_INTR_MASK);
+}
+
/*
- * Initiate low level master read/write transaction.
- * This function is called from i2c_dw_xfer when starting a transfer.
- * This function is also called from dw_i2c_pump_msg to continue a transfer
- * that is longer than the size of the TX FIFO.
+ * Initiate (and continue) low level master read/write transaction.
+ * This function is only called from i2c_dw_isr, and pumping i2c_msg
+ * messages into the tx buffer. Even if the size of i2c_msg data is
+ * longer than the size of the tx buffer, it handles everything.
*/
static void
i2c_dw_xfer_msg(struct dw_i2c_dev *dev)
{
struct i2c_msg *msgs = dev->msgs;
- u32 ic_con, intr_mask;
- int tx_limit = dev->tx_fifo_depth - readl(dev->base + DW_IC_TXFLR);
- int rx_limit = dev->rx_fifo_depth - readl(dev->base + DW_IC_RXFLR);
+ u32 intr_mask;
+ int tx_limit, rx_limit;
u32 addr = msgs[dev->msg_write_idx].addr;
u32 buf_len = dev->tx_buf_len;
+ u8 *buf = dev->tx_buf;;
- intr_mask = DW_IC_INTR_STOP_DET | DW_IC_INTR_TX_ABRT;
-
- if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) {
- /* Disable the adapter */
- writel(0, dev->base + DW_IC_ENABLE);
-
- /* set the slave (target) address */
- writel(msgs[dev->msg_write_idx].addr, dev->base + DW_IC_TAR);
-
- /* if the slave address is ten bit address, enable 10BITADDR */
- ic_con = readl(dev->base + DW_IC_CON);
- if (msgs[dev->msg_write_idx].flags & I2C_M_TEN)
- ic_con |= DW_IC_CON_10BITADDR_MASTER;
- else
- ic_con &= ~DW_IC_CON_10BITADDR_MASTER;
- writel(ic_con, dev->base + DW_IC_CON);
-
- /* Enable the adapter */
- writel(1, dev->base + DW_IC_ENABLE);
- }
+ intr_mask = DW_IC_INTR_DEFAULT_MASK;
for (; dev->msg_write_idx < dev->msgs_num; dev->msg_write_idx++) {
- /* if target address has changed, we need to
+ /*
+ * if target address has changed, we need to
* reprogram the target address in the i2c
* adapter when we are done with this transfer
*/
- if (msgs[dev->msg_write_idx].addr != addr)
- return;
+ if (msgs[dev->msg_write_idx].addr != addr) {
+ dev_err(dev->dev,
+ "%s: invalid target address\n", __func__);
+ dev->msg_err = -EINVAL;
+ break;
+ }
if (msgs[dev->msg_write_idx].len == 0) {
dev_err(dev->dev,
"%s: invalid message length\n", __func__);
dev->msg_err = -EINVAL;
- return;
+ break;
}
if (!(dev->status & STATUS_WRITE_IN_PROGRESS)) {
/* new i2c_msg */
- dev->tx_buf = msgs[dev->msg_write_idx].buf;
+ buf = msgs[dev->msg_write_idx].buf;
buf_len = msgs[dev->msg_write_idx].len;
}
+ tx_limit = dev->tx_fifo_depth - readl(dev->base + DW_IC_TXFLR);
+ rx_limit = dev->rx_fifo_depth - readl(dev->base + DW_IC_RXFLR);
+
while (buf_len > 0 && tx_limit > 0 && rx_limit > 0) {
if (msgs[dev->msg_write_idx].flags & I2C_M_RD) {
writel(0x100, dev->base + DW_IC_DATA_CMD);
rx_limit--;
} else
- writel(*(dev->tx_buf++),
- dev->base + DW_IC_DATA_CMD);
+ writel(*buf++, dev->base + DW_IC_DATA_CMD);
tx_limit--; buf_len--;
}
+ dev->tx_buf = buf;
dev->tx_buf_len = buf_len;
if (buf_len > 0) {
/* more bytes to be written */
- intr_mask |= DW_IC_INTR_TX_EMPTY;
dev->status |= STATUS_WRITE_IN_PROGRESS;
break;
} else
dev->status &= ~STATUS_WRITE_IN_PROGRESS;
}
+ /*
+ * If i2c_msg index search is completed, we don't need TX_EMPTY
+ * interrupt any more.
+ */
+ if (dev->msg_write_idx == dev->msgs_num)
+ intr_mask &= ~DW_IC_INTR_TX_EMPTY;
+
+ if (dev->msg_err)
+ intr_mask = 0;
+
writel(intr_mask, dev->base + DW_IC_INTR_MASK);
}
i2c_dw_read(struct dw_i2c_dev *dev)
{
struct i2c_msg *msgs = dev->msgs;
- u32 addr = msgs[dev->msg_read_idx].addr;
- int rx_valid = readl(dev->base + DW_IC_RXFLR);
+ int rx_valid;
for (; dev->msg_read_idx < dev->msgs_num; dev->msg_read_idx++) {
u32 len;
if (!(msgs[dev->msg_read_idx].flags & I2C_M_RD))
continue;
- /* different i2c client, reprogram the i2c adapter */
- if (msgs[dev->msg_read_idx].addr != addr)
- return;
-
if (!(dev->status & STATUS_READ_IN_PROGRESS)) {
len = msgs[dev->msg_read_idx].len;
buf = msgs[dev->msg_read_idx].buf;
buf = dev->rx_buf;
}
+ rx_valid = readl(dev->base + DW_IC_RXFLR);
+
for (; len > 0 && rx_valid > 0; len--, rx_valid--)
*buf++ = readl(dev->base + DW_IC_DATA_CMD);
}
}
+static int i2c_dw_handle_tx_abort(struct dw_i2c_dev *dev)
+{
+ unsigned long abort_source = dev->abort_source;
+ int i;
+
+ if (abort_source & DW_IC_TX_ABRT_NOACK) {
+ for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources))
+ dev_dbg(dev->dev,
+ "%s: %s\n", __func__, abort_sources[i]);
+ return -EREMOTEIO;
+ }
+
+ for_each_set_bit(i, &abort_source, ARRAY_SIZE(abort_sources))
+ dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]);
+
+ if (abort_source & DW_IC_TX_ARB_LOST)
+ return -EAGAIN;
+ else if (abort_source & DW_IC_TX_ABRT_GCALL_READ)
+ return -EINVAL; /* wrong msgs[] data */
+ else
+ return -EIO;
+}
+
/*
* Prepare controller for a transaction and call i2c_dw_xfer_msg
*/
dev->msg_read_idx = 0;
dev->msg_err = 0;
dev->status = STATUS_IDLE;
+ dev->abort_source = 0;
ret = i2c_dw_wait_bus_not_busy(dev);
if (ret < 0)
goto done;
/* start the transfers */
- i2c_dw_xfer_msg(dev);
+ i2c_dw_xfer_init(dev);
/* wait for tx to complete */
ret = wait_for_completion_interruptible_timeout(&dev->cmd_complete, HZ);
/* no error */
if (likely(!dev->cmd_err)) {
- /* read rx fifo, and disable the adapter */
- do {
- i2c_dw_read(dev);
- } while (dev->status & STATUS_READ_IN_PROGRESS);
+ /* Disable the adapter */
writel(0, dev->base + DW_IC_ENABLE);
ret = num;
goto done;
/* We have an error */
if (dev->cmd_err == DW_IC_ERR_TX_ABRT) {
- unsigned long abort_source = dev->abort_source;
- int i;
-
- for_each_bit(i, &abort_source, ARRAY_SIZE(abort_sources)) {
- dev_err(dev->dev, "%s: %s\n", __func__, abort_sources[i]);
- }
+ ret = i2c_dw_handle_tx_abort(dev);
+ goto done;
}
ret = -EIO;
static u32 i2c_dw_func(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C | I2C_FUNC_10BIT_ADDR;
-}
-
-static void dw_i2c_pump_msg(unsigned long data)
-{
- struct dw_i2c_dev *dev = (struct dw_i2c_dev *) data;
- u32 intr_mask;
-
- i2c_dw_read(dev);
- i2c_dw_xfer_msg(dev);
-
- intr_mask = DW_IC_INTR_STOP_DET | DW_IC_INTR_TX_ABRT;
- if (dev->status & STATUS_WRITE_IN_PROGRESS)
- intr_mask |= DW_IC_INTR_TX_EMPTY;
- writel(intr_mask, dev->base + DW_IC_INTR_MASK);
+ return I2C_FUNC_I2C |
+ I2C_FUNC_10BIT_ADDR |
+ I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_I2C_BLOCK;
}
static u32 i2c_dw_read_clear_intrbits(struct dw_i2c_dev *dev)
if (stat & DW_IC_INTR_TX_ABRT) {
dev->cmd_err |= DW_IC_ERR_TX_ABRT;
dev->status = STATUS_IDLE;
- } else if (stat & DW_IC_INTR_TX_EMPTY)
- tasklet_schedule(&dev->pump_msg);
- writel(0, dev->base + DW_IC_INTR_MASK); /* disable interrupts */
- if (stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET))
+ /*
+ * Anytime TX_ABRT is set, the contents of the tx/rx
+ * buffers are flushed. Make sure to skip them.
+ */
+ writel(0, dev->base + DW_IC_INTR_MASK);
+ goto tx_aborted;
+ }
+
+ if (stat & DW_IC_INTR_RX_FULL)
+ i2c_dw_read(dev);
+
+ if (stat & DW_IC_INTR_TX_EMPTY)
+ i2c_dw_xfer_msg(dev);
+
+ /*
+ * No need to modify or disable the interrupt mask here.
+ * i2c_dw_xfer_msg() will take care of it according to
+ * the current transmit status.
+ */
+
+tx_aborted:
+ if ((stat & (DW_IC_INTR_TX_ABRT | DW_IC_INTR_STOP_DET)) || dev->msg_err)
complete(&dev->cmd_complete);
return IRQ_HANDLED;
}
init_completion(&dev->cmd_complete);
- tasklet_init(&dev->pump_msg, dw_i2c_pump_msg, (unsigned long) dev);
mutex_init(&dev->lock);
dev->dev = get_device(&pdev->dev);
dev->irq = irq;
i2c_dw_init(dev);
writel(0, dev->base + DW_IC_INTR_MASK); /* disable IRQ */
- r = request_irq(dev->irq, i2c_dw_isr, 0, pdev->name, dev);
+ r = request_irq(dev->irq, i2c_dw_isr, IRQF_DISABLED, pdev->name, dev);
if (r) {
dev_err(&pdev->dev, "failure requesting irq %i\n", dev->irq);
goto err_iounmap;