Commit dd66b39f authored by Raviteja Narayanam's avatar Raviteja Narayanam Committed by Wolfram Sang

i2c: cadence: Clear HOLD bit before xfer_size register rolls over

On Xilinx zynq SOC if the delay between address register write and
control register write in cdns_mrecv function is more, the xfer size
register rolls over and controller is stuck. This is an IP bug and
is resolved in later versions of IP.

To avoid this scenario, disable the interrupts on the current processor
core between the two register writes and enable them later. This can
help achieve the timing constraint.
Signed-off-by: default avatarRaviteja Narayanam <raviteja.narayanam@xilinx.com>
Acked-by: default avatarMichal Simek <michal.simek@xilinx.com>
Signed-off-by: default avatarWolfram Sang <wsa@kernel.org>
parent 4aa908fe
...@@ -578,6 +578,11 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id) ...@@ -578,6 +578,11 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
{ {
unsigned int ctrl_reg; unsigned int ctrl_reg;
unsigned int isr_status; unsigned int isr_status;
unsigned long flags;
bool hold_clear = false;
bool irq_save = false;
u32 addr;
id->p_recv_buf = id->p_msg->buf; id->p_recv_buf = id->p_msg->buf;
id->recv_count = id->p_msg->len; id->recv_count = id->p_msg->len;
...@@ -618,14 +623,43 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id) ...@@ -618,14 +623,43 @@ static void cdns_i2c_mrecv(struct cdns_i2c *id)
cdns_i2c_writereg(id->recv_count, CDNS_I2C_XFER_SIZE_OFFSET); cdns_i2c_writereg(id->recv_count, CDNS_I2C_XFER_SIZE_OFFSET);
} }
/* Set the slave address in address register - triggers operation */ /* Determine hold_clear based on number of bytes to receive and hold flag */
cdns_i2c_writereg(id->p_msg->addr & CDNS_I2C_ADDR_MASK,
CDNS_I2C_ADDR_OFFSET);
/* Clear the bus hold flag if bytes to receive is less than FIFO size */
if (!id->bus_hold_flag && if (!id->bus_hold_flag &&
((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) && ((id->p_msg->flags & I2C_M_RECV_LEN) != I2C_M_RECV_LEN) &&
(id->recv_count <= CDNS_I2C_FIFO_DEPTH)) (id->recv_count <= CDNS_I2C_FIFO_DEPTH)) {
cdns_i2c_clear_bus_hold(id); if (cdns_i2c_readreg(CDNS_I2C_CR_OFFSET) & CDNS_I2C_CR_HOLD) {
hold_clear = true;
if (id->quirks & CDNS_I2C_BROKEN_HOLD_BIT)
irq_save = true;
}
}
addr = id->p_msg->addr;
addr &= CDNS_I2C_ADDR_MASK;
if (hold_clear) {
ctrl_reg = cdns_i2c_readreg(CDNS_I2C_CR_OFFSET) & ~CDNS_I2C_CR_HOLD;
/*
* In case of Xilinx Zynq SOC, clear the HOLD bit before transfer size
* register reaches '0'. This is an IP bug which causes transfer size
* register overflow to 0xFF. To satisfy this timing requirement,
* disable the interrupts on current processor core between register
* writes to slave address register and control register.
*/
if (irq_save)
local_irq_save(flags);
cdns_i2c_writereg(addr, CDNS_I2C_ADDR_OFFSET);
cdns_i2c_writereg(ctrl_reg, CDNS_I2C_CR_OFFSET);
/* Read it back to avoid bufferring and make sure write happens */
cdns_i2c_readreg(CDNS_I2C_CR_OFFSET);
if (irq_save)
local_irq_restore(flags);
} else {
cdns_i2c_writereg(addr, CDNS_I2C_ADDR_OFFSET);
}
cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET); cdns_i2c_writereg(CDNS_I2C_ENABLED_INTR_MASK, CDNS_I2C_IER_OFFSET);
} }
......
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