Skip to content

Commit 31d16f3

Browse files
mausyscfriedt
authored andcommitted
drivers: i2c: cdns: add broken hold bit workaround for Zynq-7000
The Xilinx Zynq 7000 I2C controller has the following bugs: - completion indication is not given to the driver at the end of a read/receive transfer with HOLD bit set. - Invalid read transaction are generated on the bus when HW timeout condition occurs with HOLD bit set. - If the delay between address register write and control register write in cdns_i2c_mrecv function is more, the xfer size register rolls over and controller is stuck. As a result of the above, this patch disallows message transfers with a repeated start condition following a read operation. Also disables interrupts between the address register write and control register write during message reception, to prevent transfer size register rollover. Signed-off-by: Simon Maurer <[email protected]>
1 parent 23aef46 commit 31d16f3

File tree

2 files changed

+55
-0
lines changed

2 files changed

+55
-0
lines changed

drivers/i2c/Kconfig.cdns

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,15 @@ config I2C_CADENCE
99
select EVENTS
1010
help
1111
Enable Cadence I2C driver.
12+
13+
if I2C_CADENCE
14+
15+
config I2C_CADENCE_BROKEN_HOLD_BIT
16+
bool "Workaround for errata condition found on the Zynq-7000"
17+
default n
18+
help
19+
Disallows message transfers with a repeated start condition following a read operation. Also
20+
disables interrupts between the address register write and control register write during
21+
message reception, to prevent transfer size register rollover.
22+
23+
endif # I2C_CADENCE

drivers/i2c/i2c_cdns.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -899,6 +899,10 @@ static void cdns_i2c_mrecv(struct cdns_i2c_data *i2c_bus, uint16_t msg_addr)
899899
uint32_t isr_status;
900900
bool hold_clear = false;
901901
uint32_t addr;
902+
#if defined(CONFIG_I2C_CADENCE_BROKEN_HOLD_BIT)
903+
unsigned int key;
904+
bool irq_save = false;
905+
#endif /* CONFIG_I2C_CADENCE_BROKEN_HOLD_BIT */
902906

903907
/* Initialize the receive buffer and count */
904908
i2c_bus->p_recv_buf = i2c_bus->p_msg->buf;
@@ -933,6 +937,9 @@ static void cdns_i2c_mrecv(struct cdns_i2c_data *i2c_bus, uint16_t msg_addr)
933937
if ((i2c_bus->bus_hold_flag == 0U) && (i2c_bus->recv_count <= i2c_bus->fifo_depth)) {
934938
if ((ctrl_reg & CDNS_I2C_CR_HOLD) != 0U) {
935939
hold_clear = true;
940+
#if defined(CONFIG_I2C_CADENCE_BROKEN_HOLD_BIT)
941+
irq_save = true;
942+
#endif /* CONFIG_I2C_CADENCE_BROKEN_HOLD_BIT */
936943
}
937944
}
938945

@@ -945,11 +952,30 @@ static void cdns_i2c_mrecv(struct cdns_i2c_data *i2c_bus, uint16_t msg_addr)
945952
ctrl_reg &= ~CDNS_I2C_CR_HOLD;
946953
ctrl_reg &= ~CDNS_I2C_CR_CLR_FIFO;
947954

955+
#if defined(CONFIG_I2C_CADENCE_BROKEN_HOLD_BIT)
956+
/*
957+
* In case of Xilinx Zynq SOC, clear the HOLD bit before transfer size
958+
* register reaches '0'. This is an IP bug which causes transfer size
959+
* register overflow to 0xFF. To satisfy this timing requirement,
960+
* disable the interrupts on current processor core between register
961+
* writes to slave address register and control register.
962+
*/
963+
if (irq_save) {
964+
key = irq_lock();
965+
}
966+
#endif /* CONFIG_I2C_CADENCE_BROKEN_HOLD_BIT */
967+
948968
/* Write the address and control register values */
949969
cdns_i2c_writereg(i2c_bus, addr, CDNS_I2C_ADDR_OFFSET);
950970
cdns_i2c_writereg(i2c_bus, ctrl_reg, CDNS_I2C_CR_OFFSET);
951971
/* Read back to ensure write completion */
952972
(void)cdns_i2c_readreg(i2c_bus, CDNS_I2C_CR_OFFSET);
973+
974+
#if defined(CONFIG_I2C_CADENCE_BROKEN_HOLD_BIT)
975+
if (irq_save) {
976+
irq_unlock(key);
977+
}
978+
#endif /* CONFIG_I2C_CADENCE_BROKEN_HOLD_BIT */
953979
} else {
954980
/* Directly write the address if no need to clear the hold bit */
955981
cdns_i2c_writereg(i2c_bus, addr, CDNS_I2C_ADDR_OFFSET);
@@ -1173,8 +1199,25 @@ static int32_t cdns_i2c_master_handle_repeated_start(struct cdns_i2c_data *i2c_b
11731199
struct i2c_msg *msgs, uint8_t num_msgs)
11741200
{
11751201
uint32_t reg;
1202+
1203+
#if defined(CONFIG_I2C_CADENCE_BROKEN_HOLD_BIT)
1204+
/*
1205+
* This controller does not give completion interrupt after a
1206+
* master receive message if HOLD bit is set (repeated start),
1207+
* resulting in SW timeout. Hence, if a receive message is
1208+
* followed by any other message, an error is returned
1209+
* indicating that this sequence is not supported.
1210+
*/
1211+
for (uint8_t count = 0; count < num_msgs - 1; count++) {
1212+
if (msgs[count].flags & I2C_MSG_READ) {
1213+
LOG_ERR("Can't do repeated start after a receive message");
1214+
return -EOPNOTSUPP;
1215+
}
1216+
}
1217+
#else
11761218
(void)msgs;
11771219
(void)num_msgs;
1220+
#endif
11781221

11791222
/* Set the hold flag and register */
11801223
i2c_bus->bus_hold_flag = 1;

0 commit comments

Comments
 (0)