|
@@ -39,6 +39,7 @@
|
|
|
#define MXS_I2C_CTRL0_SET (0x04)
|
|
|
|
|
|
#define MXS_I2C_CTRL0_SFTRST 0x80000000
|
|
|
+#define MXS_I2C_CTRL0_RUN 0x20000000
|
|
|
#define MXS_I2C_CTRL0_SEND_NAK_ON_LAST 0x02000000
|
|
|
#define MXS_I2C_CTRL0_RETAIN_CLOCK 0x00200000
|
|
|
#define MXS_I2C_CTRL0_POST_SEND_STOP 0x00100000
|
|
@@ -64,6 +65,13 @@
|
|
|
#define MXS_I2C_CTRL1_SLAVE_STOP_IRQ 0x02
|
|
|
#define MXS_I2C_CTRL1_SLAVE_IRQ 0x01
|
|
|
|
|
|
+#define MXS_I2C_DATA (0xa0)
|
|
|
+
|
|
|
+#define MXS_I2C_DEBUG0 (0xb0)
|
|
|
+#define MXS_I2C_DEBUG0_CLR (0xb8)
|
|
|
+
|
|
|
+#define MXS_I2C_DEBUG0_DMAREQ 0x80000000
|
|
|
+
|
|
|
#define MXS_I2C_IRQ_MASK (MXS_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ | \
|
|
|
MXS_I2C_CTRL1_NO_SLAVE_ACK_IRQ | \
|
|
|
MXS_I2C_CTRL1_EARLY_TERM_IRQ | \
|
|
@@ -298,6 +306,135 @@ write_init_pio_fail:
|
|
|
return -EINVAL;
|
|
|
}
|
|
|
|
|
|
+static int mxs_i2c_pio_wait_dmareq(struct mxs_i2c_dev *i2c)
|
|
|
+{
|
|
|
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
|
|
|
+
|
|
|
+ while (!(readl(i2c->regs + MXS_I2C_DEBUG0) &
|
|
|
+ MXS_I2C_DEBUG0_DMAREQ)) {
|
|
|
+ if (time_after(jiffies, timeout))
|
|
|
+ return -ETIMEDOUT;
|
|
|
+ cond_resched();
|
|
|
+ }
|
|
|
+
|
|
|
+ writel(MXS_I2C_DEBUG0_DMAREQ, i2c->regs + MXS_I2C_DEBUG0_CLR);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int mxs_i2c_pio_wait_cplt(struct mxs_i2c_dev *i2c)
|
|
|
+{
|
|
|
+ unsigned long timeout = jiffies + msecs_to_jiffies(1000);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * We do not use interrupts in the PIO mode. Due to the
|
|
|
+ * maximum transfer length being 8 bytes in PIO mode, the
|
|
|
+ * overhead of interrupt would be too large and this would
|
|
|
+ * neglect the gain from using the PIO mode.
|
|
|
+ */
|
|
|
+
|
|
|
+ while (!(readl(i2c->regs + MXS_I2C_CTRL1) &
|
|
|
+ MXS_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ)) {
|
|
|
+ if (time_after(jiffies, timeout))
|
|
|
+ return -ETIMEDOUT;
|
|
|
+ cond_resched();
|
|
|
+ }
|
|
|
+
|
|
|
+ writel(MXS_I2C_CTRL1_DATA_ENGINE_CMPLT_IRQ,
|
|
|
+ i2c->regs + MXS_I2C_CTRL1_CLR);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int mxs_i2c_pio_setup_xfer(struct i2c_adapter *adap,
|
|
|
+ struct i2c_msg *msg, uint32_t flags)
|
|
|
+{
|
|
|
+ struct mxs_i2c_dev *i2c = i2c_get_adapdata(adap);
|
|
|
+ uint32_t addr_data = msg->addr << 1;
|
|
|
+ uint32_t data = 0;
|
|
|
+ int i, shifts_left, ret;
|
|
|
+
|
|
|
+ /* Mute IRQs coming from this block. */
|
|
|
+ writel(MXS_I2C_IRQ_MASK << 8, i2c->regs + MXS_I2C_CTRL1_CLR);
|
|
|
+
|
|
|
+ if (msg->flags & I2C_M_RD) {
|
|
|
+ addr_data |= I2C_SMBUS_READ;
|
|
|
+
|
|
|
+ /* SELECT command. */
|
|
|
+ writel(MXS_I2C_CTRL0_RUN | MXS_CMD_I2C_SELECT,
|
|
|
+ i2c->regs + MXS_I2C_CTRL0);
|
|
|
+
|
|
|
+ ret = mxs_i2c_pio_wait_dmareq(i2c);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ writel(addr_data, i2c->regs + MXS_I2C_DATA);
|
|
|
+
|
|
|
+ ret = mxs_i2c_pio_wait_cplt(i2c);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ /* READ command. */
|
|
|
+ writel(MXS_I2C_CTRL0_RUN | MXS_CMD_I2C_READ | flags |
|
|
|
+ MXS_I2C_CTRL0_XFER_COUNT(msg->len),
|
|
|
+ i2c->regs + MXS_I2C_CTRL0);
|
|
|
+
|
|
|
+ for (i = 0; i < msg->len; i++) {
|
|
|
+ if ((i & 3) == 0) {
|
|
|
+ ret = mxs_i2c_pio_wait_dmareq(i2c);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+ data = readl(i2c->regs + MXS_I2C_DATA);
|
|
|
+ }
|
|
|
+ msg->buf[i] = data & 0xff;
|
|
|
+ data >>= 8;
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ addr_data |= I2C_SMBUS_WRITE;
|
|
|
+
|
|
|
+ /* WRITE command. */
|
|
|
+ writel(MXS_I2C_CTRL0_RUN | MXS_CMD_I2C_WRITE | flags |
|
|
|
+ MXS_I2C_CTRL0_XFER_COUNT(msg->len + 1),
|
|
|
+ i2c->regs + MXS_I2C_CTRL0);
|
|
|
+
|
|
|
+ /*
|
|
|
+ * The LSB of data buffer is the first byte blasted across
|
|
|
+ * the bus. Higher order bytes follow. Thus the following
|
|
|
+ * filling schematic.
|
|
|
+ */
|
|
|
+ data = addr_data << 24;
|
|
|
+ for (i = 0; i < msg->len; i++) {
|
|
|
+ data >>= 8;
|
|
|
+ data |= (msg->buf[i] << 24);
|
|
|
+ if ((i & 3) == 2) {
|
|
|
+ ret = mxs_i2c_pio_wait_dmareq(i2c);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+ writel(data, i2c->regs + MXS_I2C_DATA);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ shifts_left = 24 - (i & 3) * 8;
|
|
|
+ if (shifts_left) {
|
|
|
+ data >>= shifts_left;
|
|
|
+ ret = mxs_i2c_pio_wait_dmareq(i2c);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+ writel(data, i2c->regs + MXS_I2C_DATA);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ret = mxs_i2c_pio_wait_cplt(i2c);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
+
|
|
|
+ /* Clear any dangling IRQs and re-enable interrupts. */
|
|
|
+ writel(MXS_I2C_IRQ_MASK, i2c->regs + MXS_I2C_CTRL1_CLR);
|
|
|
+ writel(MXS_I2C_IRQ_MASK << 8, i2c->regs + MXS_I2C_CTRL1_SET);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/*
|
|
|
* Low level master read/write transaction.
|
|
|
*/
|
|
@@ -316,24 +453,37 @@ static int mxs_i2c_xfer_msg(struct i2c_adapter *adap, struct i2c_msg *msg,
|
|
|
if (msg->len == 0)
|
|
|
return -EINVAL;
|
|
|
|
|
|
- INIT_COMPLETION(i2c->cmd_complete);
|
|
|
- i2c->cmd_err = 0;
|
|
|
-
|
|
|
- ret = mxs_i2c_dma_setup_xfer(adap, msg, flags);
|
|
|
- if (ret)
|
|
|
- return ret;
|
|
|
+ /*
|
|
|
+ * The current boundary to select between PIO/DMA transfer method
|
|
|
+ * is set to 8 bytes, transfers shorter than 8 bytes are transfered
|
|
|
+ * using PIO mode while longer transfers use DMA. The 8 byte border is
|
|
|
+ * based on this empirical measurement and a lot of previous frobbing.
|
|
|
+ */
|
|
|
+ if (msg->len < 8) {
|
|
|
+ ret = mxs_i2c_pio_setup_xfer(adap, msg, flags);
|
|
|
+ if (ret)
|
|
|
+ mxs_i2c_reset(i2c);
|
|
|
+ } else {
|
|
|
+ i2c->cmd_err = 0;
|
|
|
+ INIT_COMPLETION(i2c->cmd_complete);
|
|
|
+ ret = mxs_i2c_dma_setup_xfer(adap, msg, flags);
|
|
|
+ if (ret)
|
|
|
+ return ret;
|
|
|
|
|
|
- ret = wait_for_completion_timeout(&i2c->cmd_complete,
|
|
|
+ ret = wait_for_completion_timeout(&i2c->cmd_complete,
|
|
|
msecs_to_jiffies(1000));
|
|
|
- if (ret == 0)
|
|
|
- goto timeout;
|
|
|
+ if (ret == 0)
|
|
|
+ goto timeout;
|
|
|
+
|
|
|
+ if (i2c->cmd_err == -ENXIO)
|
|
|
+ mxs_i2c_reset(i2c);
|
|
|
|
|
|
- if (i2c->cmd_err == -ENXIO)
|
|
|
- mxs_i2c_reset(i2c);
|
|
|
+ ret = i2c->cmd_err;
|
|
|
+ }
|
|
|
|
|
|
- dev_dbg(i2c->dev, "Done with err=%d\n", i2c->cmd_err);
|
|
|
+ dev_dbg(i2c->dev, "Done with err=%d\n", ret);
|
|
|
|
|
|
- return i2c->cmd_err;
|
|
|
+ return ret;
|
|
|
|
|
|
timeout:
|
|
|
dev_dbg(i2c->dev, "Timeout!\n");
|