Commit c076ada4 authored by Roland Stigge's avatar Roland Stigge Committed by Wolfram Sang

i2c: pnx: Fix read transactions of >= 2 bytes

On transactions with n>=2 bytes, the controller actually wrongly clocks in n+1
bytes. This is caused by the (wrong) assumption that RFE in the Status Register
is 1 iff there is no byte already ordered (via a dummy TX byte). This lead to
the implementation of synchronized byte ordering, e.g.:

Dummy-TX - RX - Dummy-TX - RX - ...

But since RFE actually stays high after some Dummy-TX, it rather looks like:

Dummy-TX - Dummy-TX - RX - Dummy-TX - RX - (RX)

The last RX byte is clocked in by the bus controller, but ignored by the kernel
when filling the userspace buffer.

This patch fixes the issue by asking for RX via Dummy-TX asynchronously.
Introducing a separate counter for TX bytes.
Signed-off-by: default avatarRoland Stigge <stigge@antcom.de>
Signed-off-by: default avatarWolfram Sang <w.sang@pengutronix.de>
parent b3aafe80
...@@ -291,11 +291,16 @@ static int i2c_pnx_master_rcv(struct i2c_pnx_algo_data *alg_data) ...@@ -291,11 +291,16 @@ static int i2c_pnx_master_rcv(struct i2c_pnx_algo_data *alg_data)
* or we didn't 'ask' for it yet. * or we didn't 'ask' for it yet.
*/ */
if (ioread32(I2C_REG_STS(alg_data)) & mstatus_rfe) { if (ioread32(I2C_REG_STS(alg_data)) & mstatus_rfe) {
/* 'Asking' is done asynchronously, e.g. dummy TX of several
* bytes is done before the first actual RX arrives in FIFO.
* Therefore, ordered bytes (via TX) are counted separately.
*/
if (alg_data->mif.order) {
dev_dbg(&alg_data->adapter.dev, dev_dbg(&alg_data->adapter.dev,
"%s(): Write dummy data to fill Rx-fifo...\n", "%s(): Write dummy data to fill Rx-fifo...\n",
__func__); __func__);
if (alg_data->mif.len == 1) { if (alg_data->mif.order == 1) {
/* Last byte, do not acknowledge next rcv. */ /* Last byte, do not acknowledge next rcv. */
val |= stop_bit; val |= stop_bit;
...@@ -315,7 +320,8 @@ static int i2c_pnx_master_rcv(struct i2c_pnx_algo_data *alg_data) ...@@ -315,7 +320,8 @@ static int i2c_pnx_master_rcv(struct i2c_pnx_algo_data *alg_data)
* write a (dummy) byte to the Tx-FIFO. * write a (dummy) byte to the Tx-FIFO.
*/ */
iowrite32(val, I2C_REG_TX(alg_data)); iowrite32(val, I2C_REG_TX(alg_data));
alg_data->mif.order--;
}
return 0; return 0;
} }
...@@ -515,6 +521,7 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) ...@@ -515,6 +521,7 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
alg_data->mif.buf = pmsg->buf; alg_data->mif.buf = pmsg->buf;
alg_data->mif.len = pmsg->len; alg_data->mif.len = pmsg->len;
alg_data->mif.order = pmsg->len;
alg_data->mif.mode = (pmsg->flags & I2C_M_RD) ? alg_data->mif.mode = (pmsg->flags & I2C_M_RD) ?
I2C_SMBUS_READ : I2C_SMBUS_WRITE; I2C_SMBUS_READ : I2C_SMBUS_WRITE;
alg_data->mif.ret = 0; alg_data->mif.ret = 0;
...@@ -567,6 +574,7 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) ...@@ -567,6 +574,7 @@ i2c_pnx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
/* Cleanup to be sure... */ /* Cleanup to be sure... */
alg_data->mif.buf = NULL; alg_data->mif.buf = NULL;
alg_data->mif.len = 0; alg_data->mif.len = 0;
alg_data->mif.order = 0;
dev_dbg(&alg_data->adapter.dev, "%s(): exiting, stat = %x\n", dev_dbg(&alg_data->adapter.dev, "%s(): exiting, stat = %x\n",
__func__, ioread32(I2C_REG_STS(alg_data))); __func__, ioread32(I2C_REG_STS(alg_data)));
......
...@@ -22,6 +22,7 @@ struct i2c_pnx_mif { ...@@ -22,6 +22,7 @@ struct i2c_pnx_mif {
struct timer_list timer; /* Timeout */ struct timer_list timer; /* Timeout */
u8 * buf; /* Data buffer */ u8 * buf; /* Data buffer */
int len; /* Length of data buffer */ int len; /* Length of data buffer */
int order; /* RX Bytes to order via TX */
}; };
struct i2c_pnx_algo_data { struct i2c_pnx_algo_data {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment