@@ -412,7 +412,7 @@ static void i2c_imx_dma_free(struct imx_i2c_struct *i2c_imx)
/** Functions for IMX I2C adapter driver ***************************************
*******************************************************************************/
-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 blocking)
{
unsigned long orig_jiffies = jiffies;
unsigned int temp;
@@ -438,15 +438,41 @@ 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 (!blocking) {
+ schedule();
+ } else {
+ printk("%s: delay\n", __func__);
+ udelay(100);
+ }
}
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 blocking)
{
- wait_event_timeout(i2c_imx->queue, i2c_imx->i2csr & I2SR_IIF, HZ / 10);
+ if (!blocking) {
+ wait_event_timeout(i2c_imx->queue, i2c_imx->i2csr & I2SR_IIF, HZ / 10);
+ } else {
+ int counter = 0;
+ unsigned int reg;
+
+ while (1) {
+ printk("%s: try %d\n", __func__, counter);
+ reg = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2SR);
+ i2c_imx->i2csr = reg;
+ if (reg & I2SR_IIF)
+ break;
+
+ if (counter > 1000) {
+ dev_err(&i2c_imx->adapter.dev, "<%s> TXR timeout\n", __func__);
+ return -EIO;
+ }
+ udelay(100);
+ counter++;
+ }
+ imx_i2c_write_reg(0, i2c_imx, IMX_I2C_I2SR);
+ }
if (unlikely(!(i2c_imx->i2csr & I2SR_IIF))) {
dev_dbg(&i2c_imx->adapter.dev, "<%s> Timeout\n", __func__);
@@ -511,7 +537,7 @@ static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx)
#endif
}
-static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)
+static int i2c_imx_start(struct imx_i2c_struct *i2c_imx, bool blocking)
{
unsigned int temp = 0;
int result;
@@ -535,18 +561,24 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)
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, blocking);
if (result)
return result;
i2c_imx->stopped = 0;
- temp |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK;
+ if (!blocking) {
+ temp |= I2CR_IIEN | I2CR_MTX | I2CR_TXAK;
+ } else {
+ temp |= I2CR_MTX | I2CR_TXAK;
+ 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 blocking)
{
unsigned int temp = 0;
@@ -568,7 +600,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, blocking);
i2c_imx->stopped = 1;
}
@@ -653,7 +685,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;
@@ -716,7 +748,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;
@@ -729,7 +761,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);
i2c_imx->stopped = 1;
} else {
/*
@@ -748,7 +780,7 @@ 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 blocking)
{
int i, result;
@@ -757,7 +789,7 @@ static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs)
/* write slave address */
imx_i2c_write_reg(msgs->addr << 1, i2c_imx, IMX_I2C_I2DR);
- result = i2c_imx_trx_complete(i2c_imx);
+ result = i2c_imx_trx_complete(i2c_imx, blocking);
if (result)
return result;
result = i2c_imx_acked(i2c_imx);
@@ -771,7 +803,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, blocking);
if (result)
return result;
result = i2c_imx_acked(i2c_imx);
@@ -793,7 +825,7 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bo
/* write slave address */
imx_i2c_write_reg((msgs->addr << 1) | 0x01, i2c_imx, IMX_I2C_I2DR);
- result = i2c_imx_trx_complete(i2c_imx);
+ result = i2c_imx_trx_complete(i2c_imx, false);
if (result)
return result;
result = i2c_imx_acked(i2c_imx);
@@ -824,7 +856,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, false);
if (result)
return result;
/*
@@ -852,7 +884,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, false);
i2c_imx->stopped = 1;
} else {
/*
@@ -884,8 +916,8 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bo
return 0;
}
-static int i2c_imx_xfer(struct i2c_adapter *adapter,
- struct i2c_msg *msgs, int num)
+static int i2c_imx_xfer_s(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num, bool blocking)
{
unsigned int i, temp;
int result;
@@ -895,7 +927,7 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
/* Start I2C transfer */
- result = i2c_imx_start(i2c_imx);
+ result = i2c_imx_start(i2c_imx, blocking);
if (result)
goto fail0;
@@ -910,7 +942,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, blocking);
if (result)
goto fail0;
}
@@ -935,12 +967,19 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
(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)
- result = i2c_imx_dma_write(i2c_imx, &msgs[i]);
+ if (!blocking)
+ result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg);
else
- result = i2c_imx_write(i2c_imx, &msgs[i]);
+ return -EPERM; /* not implemented */
+ else {
+ if (!blocking) {
+ if (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], blocking);
+ } else {
+ result = i2c_imx_write(i2c_imx, &msgs[i], blocking);
+ }
}
if (result)
goto fail0;
@@ -948,7 +987,7 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
fail0:
/* Stop I2C transfer */
- i2c_imx_stop(i2c_imx);
+ i2c_imx_stop(i2c_imx, blocking);
dev_dbg(&i2c_imx->adapter.dev, "<%s> exit with: %s: %d\n", __func__,
(result < 0) ? "error" : "success msg",
@@ -956,6 +995,18 @@ fail0:
return (result < 0) ? result : num;
}
+static int i2c_imx_xfer(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num)
+{
+ return i2c_imx_xfer_s(adapter, msgs, num, false);
+}
+
+static int i2c_imx_xfer_blocking(struct i2c_adapter *adapter,
+ struct i2c_msg *msgs, int num)
+{
+ return i2c_imx_xfer_s(adapter, msgs, num, true);
+}
+
static u32 i2c_imx_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL
@@ -964,6 +1015,7 @@ static u32 i2c_imx_func(struct i2c_adapter *adapter)
static struct i2c_algorithm i2c_imx_algo = {
.master_xfer = i2c_imx_xfer,
+ .master_xfer_blocking = i2c_imx_xfer_blocking,
.functionality = i2c_imx_func,
};
@@ -397,6 +397,8 @@ struct i2c_algorithm {
processed, or a negative value on error */
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,
int num);
+ int (*master_xfer_blocking)(struct i2c_adapter *adap, struct i2c_msg *msgs,
+ int num);
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
unsigned short flags, char read_write,
u8 command, int size, union i2c_smbus_data *data);
Implement a blocking i2c xfer function that does not call schedule() and does not rely on acquiring locks. The caller must ensure that there is no concurrent user. Signed-off-by: Stefan Christ <s.christ@phytec.de> --- drivers/i2c/busses/i2c-imx.c | 108 ++++++++++++++++++++++++++++++++----------- include/linux/i2c.h | 2 + 2 files changed, 82 insertions(+), 28 deletions(-)