This issue arises when the I2C Controller is operating as a master and has issued a read transaction (i.e. it is a master receiver).
The HOLD bit when set (i2c.Control_reg0[HOLD]), prevents the controller from issuing a STOP condition at the end of the transfer.
Instead, when transfer_size (i2c.Transfer_size_reg0) is 0, SCL will be held low until software decides what to do next.
If the controller timeout expires (see i2c.Time_out_reg0[TO]) while the controller is holding SCL low, it incorrectly transfers 16 bytes of additional read data from the slave device.
The transfer_size register gets corrupted and rolls over to 0xFF as well.
If there is room in the FIFO, these additional bytes will be saved in the FIFO.
The issue happens even if the timeout interrupt is disabled.
Impact: The HOLD bit is used primarily in two circumstances:
- When doing a repeated START.
To do a second transfer after the read without relinquishing the I2C interface, the HOLD bit is used and a second transfer is started without first issuing a STOP condition.
- When doing a large read transfer.
A read that is larger than 255B requires the transfer_size register to be reprogrammed.
The method of doing this is to allow transfer_size to decrement to 0 and then to reprogram it to a higher value.
It should be noted that the issue will only occur when software is unable to service the I2C controller in a timely fashion. (i.e. before timeout).
However, if the issue does occur, unintended data transfer happens.
Depending on the system and the specific transfer, this could be innocuous or serious.
Setting i2c.Time_out_reg0 to a larger value will make the issue less likely by giving software more time to respond to the I2C events.
This will not avoid the issue completely, however.
To avoid the problem, software cannot allow transfer_size to decrement to 0 while the HOLD bit is set.
This rule prevents the use of the repeated START feature described in (a) in the Impact section above.
Software can avoid scenario (b) above by splitting large read transfers into multiple separate transfers.
This may need upper-level protocol support to reassemble the transactions into their full and original size, however.
It is possible to support large read transfers if special precautions are taken to avoid having the HOLD bit set when transfer_size is 0.
As described above, the I2C interface must be quiesced in order to reprogram transfer_size.
The workaround uses the fact that the bus will quiesce when the FIFO fills up.
Therefore, the method to be used is:
- The driver must keep track of how much data it has read from the FIFO in the current transfer chunk.
- It should stop reading from the FIFO when there are 17B of data remaining.
- Then, it must wait until transfer_size=1. Since there are 17B of data remaining, this indicates that the FIFO is full with 16B of data.
- At this point, the I2C interface will be quiesced (with one last byte of data still to be transferred) and the driver can reprogram transfer_size.
- The driver can then read the data from the FIFO as usual and continue from step (2).
Configurations Affected: Systems that utilize I2C as a master.
Resolution: No Fix Planned.
Refer to (Xilinx Answer 47916) - Zynq-7000 SoC Silicon Revision Differences