Message ID | 20201006105135.28985-2-ceggers@arri.de |
---|---|
State | New |
Headers | show |
Series | [v3,1/3] i2c: imx: Fix reset of I2SR_IAL flag | expand |
From: Christian Eggers > Sent: 06 October 2020 11:52 > > According to the "VFxxx Controller Reference Manual" (and the comment > block starting at line 97), Vybrid requires writing a one for clearing > an interrupt flag. Syncing the method for clearing I2SR_IIF in > i2c_imx_isr(). > > Signed-off-by: Christian Eggers <ceggers@arri.de> > Cc: stable@vger.kernel.org > --- > drivers/i2c/busses/i2c-imx.c | 20 +++++++++++++++----- > 1 file changed, 15 insertions(+), 5 deletions(-) > > diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c > index 0ab5381aa012..745f4071155a 100644 > --- a/drivers/i2c/busses/i2c-imx.c > +++ b/drivers/i2c/busses/i2c-imx.c > @@ -412,6 +412,19 @@ static void i2c_imx_dma_free(struct imx_i2c_struct *i2c_imx) > dma->chan_using = NULL; > } > > +static void i2c_imx_clear_irq(struct imx_i2c_struct *i2c_imx, unsigned int bits) > +{ > + unsigned int temp; > + > + /* > + * i2sr_clr_opcode is the value to clear all interrupts. > + * Here we want to clear only <bits>, so we write > + * ~i2sr_clr_opcode with just <bits> toggled. > + */ > + temp = ~i2c_imx->hwdata->i2sr_clr_opcode ^ bits; > + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR); > +} That looks either wrong or maybe just overcomplicated. Why isn't: imx_i2c_write_reg(bits, i2c_imx, IMX_I2C_I2SR); enough? More usually you just write back the read value of such 'write 1 to clear' status registers and then act on all the set bits. That ensures you clear all interrupts that were pending. If you need to avoid writes of bits that aren't in the 'clear all interrupts' value then you just need: bits &= i2c_imx->hwdata->i2sr_clr_opcode; prior to the write. David - Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)
Hi David, On Tuesday, 6 October 2020, 14:06:36 CEST, David Laight wrote: > From: Christian Eggers > > +static void i2c_imx_clear_irq(struct imx_i2c_struct *i2c_imx, unsigned > > int bits) +{ > > + unsigned int temp; > > + > > + /* > > + * i2sr_clr_opcode is the value to clear all interrupts. > > + * Here we want to clear only <bits>, so we write > > + * ~i2sr_clr_opcode with just <bits> toggled. > > + */ > > + temp = ~i2c_imx->hwdata->i2sr_clr_opcode ^ bits; > > + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR); > > +} > > That looks either wrong or maybe just overcomplicated. Yes, it looks so. > Why isn't: > imx_i2c_write_reg(bits, i2c_imx, IMX_I2C_I2SR); > enough? i.MX requires W1C and Vybrid requires W0C in order to clear status bits. > More usually you just write back the read value of such > 'write 1 to clear' status registers and then act on all > the set bits. This pattern has been suggested by Uwe Klein-Koenig. It works because write access to read-only register bits is ignored, independent whether 0 or 1 is written. W0C is quite unusual, but I didn't design the hardware... The pattern ensures that not accidentally more status bits are cleared than desired. > That ensures you clear all interrupts that were pending. I think that Uwe's intention was not clearing bits which are not handled at this place. Otherwise events may get lost. > If you need to avoid writes of bits that aren't in the > 'clear all interrupts' value then you just need: > bits &= i2c_imx->hwdata->i2sr_clr_opcode; > prior to the write. I think this wouldn't fit the W0C case for Vybrid. Best regards Christian
On Tue, Oct 06, 2020 at 12:06:36PM +0000, David Laight wrote: > From: Christian Eggers > > Sent: 06 October 2020 11:52 > > > > According to the "VFxxx Controller Reference Manual" (and the comment > > block starting at line 97), Vybrid requires writing a one for clearing > > an interrupt flag. Syncing the method for clearing I2SR_IIF in > > i2c_imx_isr(). > > > > Signed-off-by: Christian Eggers <ceggers@arri.de> > > Cc: stable@vger.kernel.org > > --- > > drivers/i2c/busses/i2c-imx.c | 20 +++++++++++++++----- > > 1 file changed, 15 insertions(+), 5 deletions(-) > > > > diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c > > index 0ab5381aa012..745f4071155a 100644 > > --- a/drivers/i2c/busses/i2c-imx.c > > +++ b/drivers/i2c/busses/i2c-imx.c > > @@ -412,6 +412,19 @@ static void i2c_imx_dma_free(struct imx_i2c_struct *i2c_imx) > > dma->chan_using = NULL; > > } > > > > +static void i2c_imx_clear_irq(struct imx_i2c_struct *i2c_imx, unsigned int bits) > > +{ > > + unsigned int temp; > > + > > + /* > > + * i2sr_clr_opcode is the value to clear all interrupts. > > + * Here we want to clear only <bits>, so we write > > + * ~i2sr_clr_opcode with just <bits> toggled. > > + */ > > + temp = ~i2c_imx->hwdata->i2sr_clr_opcode ^ bits; > > + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR); > > +} > > That looks either wrong or maybe just overcomplicated. > Why isn't: > imx_i2c_write_reg(bits, i2c_imx, IMX_I2C_I2SR); > enough? Your question suggests you either didn't read the comment or the comment is not good enough. Maybe once you understood the complication (see Christian's mail) you could suggest a better wording? Maybe we have to mention that this handles both W1C and W0C. Best regards Uwe
From: Uwe Kleine-König > Sent: 06 October 2020 13:46 ... > > > +static void i2c_imx_clear_irq(struct imx_i2c_struct *i2c_imx, unsigned int bits) > > > +{ > > > + unsigned int temp; > > > + > > > + /* > > > + * i2sr_clr_opcode is the value to clear all interrupts. > > > + * Here we want to clear only <bits>, so we write > > > + * ~i2sr_clr_opcode with just <bits> toggled. > > > + */ > > > + temp = ~i2c_imx->hwdata->i2sr_clr_opcode ^ bits; > > > + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR); > > > +} > > > > That looks either wrong or maybe just overcomplicated. > > Why isn't: > > imx_i2c_write_reg(bits, i2c_imx, IMX_I2C_I2SR); > > enough? > > Your question suggests you either didn't read the comment or the comment > is not good enough. Maybe once you understood the complication (see > Christian's mail) you could suggest a better wording? Maybe we have to > mention that this handles both W1C and W0C. Yes, the comment should just say that some devices are W1C and other W0C. It isn't obvious from that code fragment at all. Now for the 3rd variant which zeros the bits when they are read :-) David - Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales)
diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index 0ab5381aa012..745f4071155a 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -412,6 +412,19 @@ static void i2c_imx_dma_free(struct imx_i2c_struct *i2c_imx) dma->chan_using = NULL; } +static void i2c_imx_clear_irq(struct imx_i2c_struct *i2c_imx, unsigned int bits) +{ + unsigned int temp; + + /* + * i2sr_clr_opcode is the value to clear all interrupts. + * Here we want to clear only <bits>, so we write + * ~i2sr_clr_opcode with just <bits> toggled. + */ + temp = ~i2c_imx->hwdata->i2sr_clr_opcode ^ bits; + imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR); +} + static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy, bool atomic) { unsigned long orig_jiffies = jiffies; @@ -424,8 +437,7 @@ static int i2c_imx_bus_busy(struct imx_i2c_struct *i2c_imx, int for_busy, bool a /* check for arbitration lost */ if (temp & I2SR_IAL) { - temp &= ~I2SR_IAL; - imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR); + i2c_imx_clear_irq(i2c_imx, I2SR_IAL); return -EAGAIN; } @@ -623,9 +635,7 @@ static irqreturn_t i2c_imx_isr(int irq, void *dev_id) if (temp & I2SR_IIF) { /* save status register */ i2c_imx->i2csr = temp; - temp &= ~I2SR_IIF; - temp |= (i2c_imx->hwdata->i2sr_clr_opcode & I2SR_IIF); - imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2SR); + i2c_imx_clear_irq(i2c_imx, I2SR_IIF); wake_up(&i2c_imx->queue); return IRQ_HANDLED; }
According to the "VFxxx Controller Reference Manual" (and the comment block starting at line 97), Vybrid requires writing a one for clearing an interrupt flag. Syncing the method for clearing I2SR_IIF in i2c_imx_isr(). Signed-off-by: Christian Eggers <ceggers@arri.de> Cc: stable@vger.kernel.org --- drivers/i2c/busses/i2c-imx.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-)