Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+28 −20
Original line number Original line Diff line number Diff line
@@ -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)
			 * 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)


		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)
	/* 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)));
+1 −0
Original line number Original line Diff line number Diff line
@@ -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 {