aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorLad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>2025-01-13 12:26:42 +0000
committerWolfram Sang <wsa+renesas@sang-engineering.com>2025-01-14 13:01:30 +0100
commitb31addf2a08bc5c16dfd6eae8785546f3eea92b8 (patch)
treec4b22ee15910fcee5e495c8754bb656b947d9c11
parenti2c: riic: Use predefined macro and simplify clock tick calculation (diff)
downloadwireguard-linux-b31addf2a08bc5c16dfd6eae8785546f3eea92b8.tar.xz
wireguard-linux-b31addf2a08bc5c16dfd6eae8785546f3eea92b8.zip
i2c: riic: Add `riic_bus_barrier()` to check bus availability
Introduce a new `riic_bus_barrier()` function to verify bus availability before initiating an I2C transfer. This function enhances the bus arbitration check by ensuring that the SDA and SCL lines are not held low, in addition to checking the BBSY flag using `readb_poll_timeout()`. Previously, only the BBSY flag was checked to determine bus availability. However, it is possible for the SDA line to remain low even when BBSY = 0. This new implementation performs an additional check on the SDA and SCL lines to avoid potential bus contention issues. Signed-off-by: Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com> Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com> Tested-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> Reviewed-by: Claudiu Beznea <claudiu.beznea.uj@bp.renesas.com> Reviewed-by: Andy Shevchenko <andy@kernel.org> Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Diffstat (limited to '')
-rw-r--r--drivers/i2c/busses/i2c-riic.c30
1 files changed, 26 insertions, 4 deletions
diff --git a/drivers/i2c/busses/i2c-riic.c b/drivers/i2c/busses/i2c-riic.c
index aa3e4f430b11..d7dddd6c296a 100644
--- a/drivers/i2c/busses/i2c-riic.c
+++ b/drivers/i2c/busses/i2c-riic.c
@@ -41,6 +41,7 @@
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@@ -51,6 +52,8 @@
#define ICCR1_ICE BIT(7)
#define ICCR1_IICRST BIT(6)
#define ICCR1_SOWP BIT(4)
+#define ICCR1_SCLI BIT(1)
+#define ICCR1_SDAI BIT(0)
#define ICCR2_BBSY BIT(7)
#define ICCR2_SP BIT(3)
@@ -136,6 +139,27 @@ static inline void riic_clear_set_bit(struct riic_dev *riic, u8 clear, u8 set, u
riic_writeb(riic, (riic_readb(riic, reg) & ~clear) | set, reg);
}
+static int riic_bus_barrier(struct riic_dev *riic)
+{
+ int ret;
+ u8 val;
+
+ /*
+ * The SDA line can still be low even when BBSY = 0. Therefore, after checking
+ * the BBSY flag, also verify that the SDA and SCL lines are not being held low.
+ */
+ ret = readb_poll_timeout(riic->base + riic->info->regs[RIIC_ICCR2], val,
+ !(val & ICCR2_BBSY), 10, riic->adapter.timeout);
+ if (ret)
+ return ret;
+
+ if ((riic_readb(riic, RIIC_ICCR1) & (ICCR1_SDAI | ICCR1_SCLI)) !=
+ (ICCR1_SDAI | ICCR1_SCLI))
+ return -EBUSY;
+
+ return 0;
+}
+
static int riic_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
{
struct riic_dev *riic = i2c_get_adapdata(adap);
@@ -148,13 +172,11 @@ static int riic_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
if (ret)
return ret;
- if (riic_readb(riic, RIIC_ICCR2) & ICCR2_BBSY) {
- riic->err = -EBUSY;
+ riic->err = riic_bus_barrier(riic);
+ if (riic->err)
goto out;
- }
reinit_completion(&riic->msg_done);
- riic->err = 0;
riic_writeb(riic, 0, RIIC_ICSR2);