Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Roland Stigge <stigge@antcom.de>
Signed-off-by: Wolfram Sang <w.sang@pengutronix.de>

authored by

Roland Stigge and committed by
Wolfram Sang
c076ada4 b3aafe80

+29 -20
+28 -20
drivers/i2c/busses/i2c-pnx.c
··· 291 291 * or we didn't 'ask' for it yet. 292 292 */ 293 293 if (ioread32(I2C_REG_STS(alg_data)) & mstatus_rfe) { 294 - dev_dbg(&alg_data->adapter.dev, 295 - "%s(): Write dummy data to fill Rx-fifo...\n", 296 - __func__); 294 + /* 'Asking' is done asynchronously, e.g. dummy TX of several 295 + * bytes is done before the first actual RX arrives in FIFO. 296 + * Therefore, ordered bytes (via TX) are counted separately. 297 + */ 298 + if (alg_data->mif.order) { 299 + dev_dbg(&alg_data->adapter.dev, 300 + "%s(): Write dummy data to fill Rx-fifo...\n", 301 + __func__); 297 302 298 - if (alg_data->mif.len == 1) { 299 - /* Last byte, do not acknowledge next rcv. */ 300 - val |= stop_bit; 303 + if (alg_data->mif.order == 1) { 304 + /* Last byte, do not acknowledge next rcv. */ 305 + val |= stop_bit; 306 + 307 + /* 308 + * Enable interrupt RFDAIE (data in Rx fifo), 309 + * and disable DRMIE (need data for Tx) 310 + */ 311 + ctl = ioread32(I2C_REG_CTL(alg_data)); 312 + ctl |= mcntrl_rffie | mcntrl_daie; 313 + ctl &= ~mcntrl_drmie; 314 + iowrite32(ctl, I2C_REG_CTL(alg_data)); 315 + } 301 316 302 317 /* 303 - * Enable interrupt RFDAIE (data in Rx fifo), 304 - * and disable DRMIE (need data for Tx) 318 + * Now we'll 'ask' for data: 319 + * For each byte we want to receive, we must 320 + * write a (dummy) byte to the Tx-FIFO. 305 321 */ 306 - ctl = ioread32(I2C_REG_CTL(alg_data)); 307 - ctl |= mcntrl_rffie | mcntrl_daie; 308 - ctl &= ~mcntrl_drmie; 309 - iowrite32(ctl, I2C_REG_CTL(alg_data)); 322 + iowrite32(val, I2C_REG_TX(alg_data)); 323 + alg_data->mif.order--; 310 324 } 311 - 312 - /* 313 - * Now we'll 'ask' for data: 314 - * For each byte we want to receive, we must 315 - * write a (dummy) byte to the Tx-FIFO. 316 - */ 317 - iowrite32(val, I2C_REG_TX(alg_data)); 318 - 319 325 return 0; 320 326 } 321 327 ··· 521 515 522 516 alg_data->mif.buf = pmsg->buf; 523 517 alg_data->mif.len = pmsg->len; 518 + alg_data->mif.order = pmsg->len; 524 519 alg_data->mif.mode = (pmsg->flags & I2C_M_RD) ? 525 520 I2C_SMBUS_READ : I2C_SMBUS_WRITE; 526 521 alg_data->mif.ret = 0; ··· 574 567 /* Cleanup to be sure... */ 575 568 alg_data->mif.buf = NULL; 576 569 alg_data->mif.len = 0; 570 + alg_data->mif.order = 0; 577 571 578 572 dev_dbg(&alg_data->adapter.dev, "%s(): exiting, stat = %x\n", 579 573 __func__, ioread32(I2C_REG_STS(alg_data)));
+1
include/linux/i2c-pnx.h
··· 22 22 struct timer_list timer; /* Timeout */ 23 23 u8 * buf; /* Data buffer */ 24 24 int len; /* Length of data buffer */ 25 + int order; /* RX Bytes to order via TX */ 25 26 }; 26 27 27 28 struct i2c_pnx_algo_data {