i2c: imx: fix the i2c bus hang issue when do repeat restart
Test i2c device Maxim max44009, datasheet is located at: http://www.maximintegrated.com/datasheet/index.mvp/id/7175 The max44009 support repeat operation like: read -> repeat restart -> read/write The current i2c imx host controller driver don't support this operation that causes i2c bus hang due to "MTX" is cleared in .i2c_imx_read(). If "read" is the last message there have no problem, so the current driver supports all SMbus operation like: write -> repeat restart -> read/write IMX i2c controller for master receiver has some limitation: - If it is the last byte for one operation, it must generate STOP signal before read I2DR to prevent controller from generating another clock cycle. - If it is the last byte in the read, and then do repeat restart, it must set "MTX" before read I2DR to prevent controller from generating another extra clock cycle. The patch is to fix the issue. Signed-off-by: Fugang Duan <B38611@freescale.com> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
This commit is contained in:
parent
3d99beabf1
commit
054b62d9f2
@ -458,7 +458,7 @@ static int i2c_imx_write(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs)
|
static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs, bool is_lastmsg)
|
||||||
{
|
{
|
||||||
int i, result;
|
int i, result;
|
||||||
unsigned int temp;
|
unsigned int temp;
|
||||||
@ -515,15 +515,30 @@ static int i2c_imx_read(struct imx_i2c_struct *i2c_imx, struct i2c_msg *msgs)
|
|||||||
msgs->len += len;
|
msgs->len += len;
|
||||||
}
|
}
|
||||||
if (i == (msgs->len - 1)) {
|
if (i == (msgs->len - 1)) {
|
||||||
/* It must generate STOP before read I2DR to prevent
|
if (is_lastmsg) {
|
||||||
controller from generating another clock cycle */
|
/*
|
||||||
dev_dbg(&i2c_imx->adapter.dev,
|
* It must generate STOP before read I2DR to prevent
|
||||||
"<%s> clear MSTA\n", __func__);
|
* controller from generating another clock cycle
|
||||||
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
*/
|
||||||
temp &= ~(I2CR_MSTA | I2CR_MTX);
|
dev_dbg(&i2c_imx->adapter.dev,
|
||||||
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
"<%s> clear MSTA\n", __func__);
|
||||||
i2c_imx_bus_busy(i2c_imx, 0);
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||||
i2c_imx->stopped = 1;
|
temp &= ~(I2CR_MSTA | I2CR_MTX);
|
||||||
|
imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR);
|
||||||
|
i2c_imx_bus_busy(i2c_imx, 0);
|
||||||
|
i2c_imx->stopped = 1;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* For i2c master receiver repeat restart operation like:
|
||||||
|
* read -> repeat MSTA -> read/write
|
||||||
|
* The controller must set MTX before read the last byte in
|
||||||
|
* the first read operation, otherwise the first read cost
|
||||||
|
* one extra clock cycle.
|
||||||
|
*/
|
||||||
|
temp = readb(i2c_imx->base + IMX_I2C_I2CR);
|
||||||
|
temp |= I2CR_MTX;
|
||||||
|
writeb(temp, i2c_imx->base + IMX_I2C_I2CR);
|
||||||
|
}
|
||||||
} else if (i == (msgs->len - 2)) {
|
} else if (i == (msgs->len - 2)) {
|
||||||
dev_dbg(&i2c_imx->adapter.dev,
|
dev_dbg(&i2c_imx->adapter.dev,
|
||||||
"<%s> set TXAK\n", __func__);
|
"<%s> set TXAK\n", __func__);
|
||||||
@ -547,6 +562,7 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
|
|||||||
{
|
{
|
||||||
unsigned int i, temp;
|
unsigned int i, temp;
|
||||||
int result;
|
int result;
|
||||||
|
bool is_lastmsg = false;
|
||||||
struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);
|
struct imx_i2c_struct *i2c_imx = i2c_get_adapdata(adapter);
|
||||||
|
|
||||||
dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
|
dev_dbg(&i2c_imx->adapter.dev, "<%s>\n", __func__);
|
||||||
@ -558,6 +574,9 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
|
|||||||
|
|
||||||
/* read/write data */
|
/* read/write data */
|
||||||
for (i = 0; i < num; i++) {
|
for (i = 0; i < num; i++) {
|
||||||
|
if (i == num - 1)
|
||||||
|
is_lastmsg = true;
|
||||||
|
|
||||||
if (i) {
|
if (i) {
|
||||||
dev_dbg(&i2c_imx->adapter.dev,
|
dev_dbg(&i2c_imx->adapter.dev,
|
||||||
"<%s> repeated start\n", __func__);
|
"<%s> repeated start\n", __func__);
|
||||||
@ -588,7 +607,7 @@ static int i2c_imx_xfer(struct i2c_adapter *adapter,
|
|||||||
(temp & I2SR_RXAK ? 1 : 0));
|
(temp & I2SR_RXAK ? 1 : 0));
|
||||||
#endif
|
#endif
|
||||||
if (msgs[i].flags & I2C_M_RD)
|
if (msgs[i].flags & I2C_M_RD)
|
||||||
result = i2c_imx_read(i2c_imx, &msgs[i]);
|
result = i2c_imx_read(i2c_imx, &msgs[i], is_lastmsg);
|
||||||
else
|
else
|
||||||
result = i2c_imx_write(i2c_imx, &msgs[i]);
|
result = i2c_imx_write(i2c_imx, &msgs[i]);
|
||||||
if (result)
|
if (result)
|
||||||
|
Loading…
Reference in New Issue
Block a user