| From e1515ecb75b454adfd9fafa809db95d082eed18c Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Wed, 15 Jan 2025 13:36:23 +0100 |
| Subject: i2c: rcar: fix NACK handling when being a target |
| |
| From: Wolfram Sang <wsa+renesas@sang-engineering.com> |
| |
| [ Upstream commit 093f70c134f70e4632b295240f07d2b50b74e247 ] |
| |
| 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: de20d1857dd6 ("i2c: rcar: add slave support") |
| Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com> |
| Signed-off-by: Sasha Levin <sashal@kernel.org> |
| --- |
| drivers/i2c/busses/i2c-rcar.c | 20 +++++++++++++++----- |
| 1 file changed, 15 insertions(+), 5 deletions(-) |
| |
| diff --git a/drivers/i2c/busses/i2c-rcar.c b/drivers/i2c/busses/i2c-rcar.c |
| index 316dd378fb8c4..afefe99129001 100644 |
| --- a/drivers/i2c/busses/i2c-rcar.c |
| +++ b/drivers/i2c/busses/i2c-rcar.c |
| @@ -112,6 +112,8 @@ |
| #define ID_P_PM_BLOCKED BIT(31) |
| #define ID_P_MASK GENMASK(31, 28) |
| |
| +#define ID_SLAVE_NACK BIT(0) |
| + |
| enum rcar_i2c_type { |
| I2C_RCAR_GEN1, |
| I2C_RCAR_GEN2, |
| @@ -146,6 +148,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) |
| @@ -576,6 +579,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); |
| @@ -591,7 +595,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); |
| } |
| @@ -604,18 +611,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); |
| } |
| |
| -- |
| 2.39.5 |
| |