diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-imx.c')
-rw-r--r-- | drivers/i2c/busses/i2c-imx.c | 148 |
1 files changed, 107 insertions, 41 deletions
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 753d2d04cd3a..4a372b78c56d 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -34,6 +34,7 @@ #include <linux/init.h> #include <linux/interrupt.h> #include <linux/io.h> +#include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> @@ -513,7 +514,7 @@ static void i2c_imx_clr_al_bit(unsigned int status, struct imx_i2c_struct *i2c_i imx_i2c_write_reg(status, i2c_imx, IMX_I2C_I2SR); } -static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy) +static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy, bool atomic) { unsigned long orig_jiffies = jiffies; unsigned int temp; @@ -542,15 +543,38 @@ static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy) "<%s> I2C bus is busy\n", __func__); return -ETIMEDOUT; } - schedule(); + if (atomic) + udelay(100); + else + schedule(); + } return 0; } -static int i2c_imx_trx_complete(struct imx_i2c_struct *i2c_imx) +static int i2c_imx_trx_complete(struct imx_i2c_struct *i2c_imx, bool atomic) { - wait_event_timeout(i2c_imx->queue, i2c_imx->i2csr & I2SR_IIF, HZ / 10); + if (atomic) { + void __iomem *addr = i2c_imx->base + (IMX_I2C_I2SR << i2c_imx->hwdata->regshift); + unsigned int regval; + + /* + * The formula for the poll timeout is documented in the RM + * Rev.5 on page 1878: + * T_min = 10/F_scl + * Set the value hard as it is done for the non-atomic use-case. + * Use 10 kHz for the calculation since this is the minimum + * allowed SMBus frequency. Also add an offset of 100us since it + * turned out that the I2SR_IIF bit isn't set correctly within + * the minimum timeout in polling mode. + */ + readb_poll_timeout_atomic(addr, regval, regval & I2SR_IIF, 5, 1000 + 100); + i2c_imx->i2csr = regval; + imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2SR); + } else { + wait_event_timeout(i2c_imx->queue, i2c_imx->i2csr & I2SR_IIF, HZ / 10); + } if (unlikely(!(i2c_imx->i2csr & I2SR_IIF))) { dev_dbg(&i2c_imx->adapter.dev, "<%s> Timeout\n", __func__); @@ -639,7 +663,7 @@ static int i2c_imx_clk_notifier_call(struct notifier_block *nb, return notifier_from_errno(ret); } -static int i2c_imx_start(struct imx_i2c_struct *i2c_imx) +static int i2c_imx_start(struct imx_i2c_struct *i2c_imx, bool atomic) { unsigned int temp = 0; int result; @@ -656,23 +680,29 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx) imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode, i2c_imx, IMX_I2C_I2CR); /* Wait controller to be stable */ - usleep_range(50, 150); + if (atomic) + udelay(50); + else + usleep_range(50, 150); /* Start I2C transaction */ temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); temp |= I2CR_MSTA; imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); - result = i2c_imx_bus_busy(i2c_imx, 1); + result = i2c_imx_bus_busy(i2c_imx, 1, atomic); if (result) return result; temp |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK; + if (atomic) + temp &= ~I2CR_IIEN; /* Disable interrupt */ + temp &= ~I2CR_DMAEN; imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); return result; } -static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx) +static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx, bool atomic) { unsigned int temp = 0; @@ -694,7 +724,7 @@ static void i2c_imx_stop(struct imx_i2c_struct *i2c_imx) } if (!i2c_imx->stopped) - i2c_imx_bus_busy(i2c_imx, 0); + i2c_imx_bus_busy(i2c_imx, 0, atomic); /* Disable I2C controller */ temp = i2c_imx->hwdata->i2cr_ien_opcode ^ I2CR_IEN, @@ -777,7 +807,7 @@ static int i2c_imx_dma_write(struct imx_i2c_struct *i2c_imx, /* The last data byte must be transferred by the CPU. */ imx_i2c_write_reg(msgs->buf[msgs->len-1], i2c_imx, IMX_I2C_I2DR); - result = i2c_imx_trx_complete(i2c_imx); + result = i2c_imx_trx_complete(i2c_imx, false); if (result) return result; @@ -836,7 +866,7 @@ static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx, msgs->buf[msgs->len-2] = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); /* read n byte data */ - result = i2c_imx_trx_complete(i2c_imx); + result = i2c_imx_trx_complete(i2c_imx, false); if (result) return result; @@ -849,7 +879,7 @@ static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx, temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); temp &= ~(I2CR_MSTA | I2CR_MTX); imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); - i2c_imx_bus_busy(i2c_imx, 0); + i2c_imx_bus_busy(i2c_imx, 0, false); } else { /* * For i2c master receiver repeat restart operation like: @@ -867,7 +897,8 @@ static int i2c_imx_dma_read(struct imx_i2c_struct *i2c_imx, return 0; } -static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs) +static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, + bool atomic) { int i, result; @@ -876,7 +907,7 @@ static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs) /* write slave address */ imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR); - result = i2c_imx_trx_complete(i2c_imx); + result = i2c_imx_trx_complete(i2c_imx, atomic); if (result) return result; result = i2c_imx_acked(i2c_imx); @@ -890,7 +921,7 @@ static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs) "<%s> write byte: B%d=0x%X\n", __func__, i, msgs->buf[i]); imx_i2c_write_reg(msgs->buf[i], i2c_imx, IMX_I2C_I2DR); - result = i2c_imx_trx_complete(i2c_imx); + result = i2c_imx_trx_complete(i2c_imx, atomic); if (result) return result; result = i2c_imx_acked(i2c_imx); @@ -900,7 +931,8 @@ static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs) return 0; } -static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bool is_lastmsg) +static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, + bool is_lastmsg, bool atomic) { int i, result; unsigned int temp; @@ -913,7 +945,7 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bo /* write slave address */ imx_i2c_write_reg(i2c_8bit_addr_from_msg(msgs), i2c_imx, IMX_I2C_I2DR); - result = i2c_imx_trx_complete(i2c_imx); + result = i2c_imx_trx_complete(i2c_imx, atomic); if (result) return result; result = i2c_imx_acked(i2c_imx); @@ -946,7 +978,7 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bo for (i = 0; i < msgs->len; i++) { u8 len = 0; - result = i2c_imx_trx_complete(i2c_imx); + result = i2c_imx_trx_complete(i2c_imx, atomic); if (result) return result; /* @@ -974,7 +1006,7 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bo temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); temp &= ~(I2CR_MSTA | I2CR_MTX); imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); - i2c_imx_bus_busy(i2c_imx, 0); + i2c_imx_bus_busy(i2c_imx, 0, atomic); } else { /* * For i2c master receiver repeat restart operation like: @@ -1078,8 +1110,8 @@ static int i2c_imx_recovery_for_layerscape(struct imx_i2c_struct *i2c_imx) return 0; } -static int i2c_imx_xfer(struct i2c_adapter *adapter, - struct i2c_msg *msgs, int num) +static int i2c_imx_xfer_common(struct i2c_adapter *adapter, + struct i2c_msg *msgs, int num, bool atomic) { unsigned int i, temp; int result; @@ -1101,15 +1133,11 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter, enable_runtime_pm = true; } - result = pm_runtime_get_sync(i2c_imx->adapter.dev.parent); - if (result < 0) - goto out; - /* * workround for ERR010027: ensure that the I2C BUS is idle * before switching to master mode and attempting a Start cycle */ - result = i2c_imx_bus_busy(i2c_imx, 0); + result = i2c_imx_bus_busy(i2c_imx, 0, atomic); if (result) { /* timeout */ if ((result == -ETIMEDOUT) && (i2c_imx->layerscape_bus_recover == 1)) @@ -1119,11 +1147,15 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter, } /* Start I2C transfer */ - result = i2c_imx_start(i2c_imx); + result = i2c_imx_start(i2c_imx, atomic); if (result) { - if (i2c_imx->adapter.bus_recovery_info) { + /* + * Bus recovery uses gpiod_get_value_cansleep() which is not + * allowed within atomic context. + */ + if (!atomic && i2c_imx->adapter.bus_recovery_info) { i2c_recover_bus(&i2c_imx->adapter); - result = i2c_imx_start(i2c_imx); + result = i2c_imx_start(i2c_imx, atomic); } } @@ -1141,7 +1173,7 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter, temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); temp |= I2CR_RSTA; imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); - result = i2c_imx_bus_busy(i2c_imx, 1); + result = i2c_imx_bus_busy(i2c_imx, 1, atomic); if (result) goto fail0; } @@ -1165,13 +1197,14 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter, (temp & I2SR_SRW ? 1 : 0), (temp & I2SR_IIF ? 1 : 0), (temp & I2SR_RXAK ? 1 : 0)); #endif - if (msgs[i].flags & I2C_M_RD) - result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg); - else { - if (i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD) + if (msgs[i].flags & I2C_M_RD) { + result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg, atomic); + } else { + if (!atomic && + i2c_imx->dma && msgs[i].len >= DMA_THRESHOLD) result = i2c_imx_dma_write(i2c_imx, &msgs[i]); else - result = i2c_imx_write(i2c_imx, &msgs[i]); + result = i2c_imx_write(i2c_imx, &msgs[i], atomic); } if (result) goto fail0; @@ -1179,10 +1212,7 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter, fail0: /* Stop I2C transfer */ - i2c_imx_stop(i2c_imx); - - pm_runtime_mark_last_busy(i2c_imx->adapter.dev.parent); - pm_runtime_put_autosuspend(i2c_imx->adapter.dev.parent); + i2c_imx_stop(i2c_imx, atomic); out: if (enable_runtime_pm) @@ -1194,6 +1224,41 @@ out: return (result < 0) ? result : num; } +static int i2c_imx_xfer(struct i2c_adapter *adapter, + struct i2c_msg *msgs, int num) +{ + struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter); + int result; + + result = pm_runtime_get_sync(i2c_imx->adapter.dev.parent); + if (result < 0) + return result; + + result = i2c_imx_xfer_common(adapter, msgs, num, false); + + pm_runtime_mark_last_busy(i2c_imx->adapter.dev.parent); + pm_runtime_put_autosuspend(i2c_imx->adapter.dev.parent); + + return result; +} + +static int i2c_imx_xfer_atomic(struct i2c_adapter *adapter, + struct i2c_msg *msgs, int num) +{ + struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter); + int result; + + result = clk_enable(i2c_imx->clk); + if (result) + return result; + + result = i2c_imx_xfer_common(adapter, msgs, num, true); + + clk_disable(i2c_imx->clk); + + return result; +} + static void i2c_imx_prepare_recovery(struct i2c_adapter *adap) { struct imx_i2c_struct *i2c_imx; @@ -1442,8 +1507,9 @@ static int i2c_imx_unreg_slave(struct i2c_client *client) #endif static const struct i2c_algorithm i2c_imx_algo = { - .master_xfer = i2c_imx_xfer, - .functionality = i2c_imx_func, + .master_xfer = i2c_imx_xfer, + .master_xfer_atomic = i2c_imx_xfer_atomic, + .functionality = i2c_imx_func, #if IS_ENABLED(CONFIG_I2C_SLAVE) .reg_slave = i2c_imx_reg_slave, .unreg_slave = i2c_imx_unreg_slave, |