mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 03:44:45 +01:00
i2c: rcar: fix NACK handling when being a target
When this controller is a target, the NACK handling had two issues.
First, the return value from the backend was not checked on the initial
WRITE_REQUESTED. So, the driver missed to send a NACK in this case.
Also, the NACK always arrives one byte late on the bus, even in the
WRITE_RECEIVED case. This seems to be a HW issue. We should then not
rely on the backend to correctly NACK the superfluous byte as well. Fix
both issues by introducing a flag which gets set whenever the backend
requests a NACK and keep sending it until we get a STOP condition.
Fixes: de20d1857d ("i2c: rcar: add slave support")
Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
This commit is contained in:
parent
385f2dbbc9
commit
093f70c134
1 changed files with 15 additions and 5 deletions
|
|
@ -130,6 +130,8 @@
|
|||
#define ID_P_PM_BLOCKED BIT(31)
|
||||
#define ID_P_MASK GENMASK(31, 27)
|
||||
|
||||
#define ID_SLAVE_NACK BIT(0)
|
||||
|
||||
enum rcar_i2c_type {
|
||||
I2C_RCAR_GEN1,
|
||||
I2C_RCAR_GEN2,
|
||||
|
|
@ -166,6 +168,7 @@ struct rcar_i2c_priv {
|
|||
int irq;
|
||||
|
||||
struct i2c_client *host_notify_client;
|
||||
u8 slave_flags;
|
||||
};
|
||||
|
||||
#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
|
||||
|
|
@ -655,6 +658,7 @@ static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv)
|
|||
{
|
||||
u32 ssr_raw, ssr_filtered;
|
||||
u8 value;
|
||||
int ret;
|
||||
|
||||
ssr_raw = rcar_i2c_read(priv, ICSSR) & 0xff;
|
||||
ssr_filtered = ssr_raw & rcar_i2c_read(priv, ICSIER);
|
||||
|
|
@ -670,7 +674,10 @@ static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv)
|
|||
rcar_i2c_write(priv, ICRXTX, value);
|
||||
rcar_i2c_write(priv, ICSIER, SDE | SSR | SAR);
|
||||
} else {
|
||||
i2c_slave_event(priv->slave, I2C_SLAVE_WRITE_REQUESTED, &value);
|
||||
ret = i2c_slave_event(priv->slave, I2C_SLAVE_WRITE_REQUESTED, &value);
|
||||
if (ret)
|
||||
priv->slave_flags |= ID_SLAVE_NACK;
|
||||
|
||||
rcar_i2c_read(priv, ICRXTX); /* dummy read */
|
||||
rcar_i2c_write(priv, ICSIER, SDR | SSR | SAR);
|
||||
}
|
||||
|
|
@ -683,18 +690,21 @@ static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv)
|
|||
if (ssr_filtered & SSR) {
|
||||
i2c_slave_event(priv->slave, I2C_SLAVE_STOP, &value);
|
||||
rcar_i2c_write(priv, ICSCR, SIE | SDBS); /* clear our NACK */
|
||||
priv->slave_flags &= ~ID_SLAVE_NACK;
|
||||
rcar_i2c_write(priv, ICSIER, SAR);
|
||||
rcar_i2c_write(priv, ICSSR, ~SSR & 0xff);
|
||||
}
|
||||
|
||||
/* master wants to write to us */
|
||||
if (ssr_filtered & SDR) {
|
||||
int ret;
|
||||
|
||||
value = rcar_i2c_read(priv, ICRXTX);
|
||||
ret = i2c_slave_event(priv->slave, I2C_SLAVE_WRITE_RECEIVED, &value);
|
||||
/* Send NACK in case of error */
|
||||
rcar_i2c_write(priv, ICSCR, SIE | SDBS | (ret < 0 ? FNA : 0));
|
||||
if (ret)
|
||||
priv->slave_flags |= ID_SLAVE_NACK;
|
||||
|
||||
/* Send NACK in case of error, but it will come 1 byte late :( */
|
||||
rcar_i2c_write(priv, ICSCR, SIE | SDBS |
|
||||
(priv->slave_flags & ID_SLAVE_NACK ? FNA : 0));
|
||||
rcar_i2c_write(priv, ICSSR, ~SDR & 0xff);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue