[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20260114141352.103425-2-jie.i.li@nokia.com>
Date: Wed, 14 Jan 2026 15:13:51 +0100
From: Jie Li <lj29312931@...il.com>
To: wsa@...nel.org
Cc: linux-i2c@...r.kernel.org,
robh@...nel.org,
krzk+dt@...nel.org,
conor+dt@...nel.org,
devicetree@...r.kernel.org,
linus.walleij@...aro.org,
linux-kernel@...r.kernel.org,
Jie Li <jie.i.li@...ia.com>
Subject: [PATCH v1 1/2] i2c: core: add "force-set-sda" flag for open-drain SDA without "FLAG_IS_OUT" bit
On certain specialized SoC platforms, the I2C SDA pin is physically
open-drain but lacks the "FLAG_IS_OUT" bit in the GPIO subsystem.
In such cases, the set_sda function isn't assigned, causing bus
recovery to fail.
This patch introduces a new optional pinctrl flag "force-set-sda".
When this flag is present in the device tree, the I2C recovery
mechanism will explicitly attempt to toggle the SDA line through
the pinctrl state, ensuring the bus can be freed even when the
default recovery logic is insufficient for this specific hardware
implementation.
This change is necessary to improve the robustness of I2C
communication on hardware where the SDA line can remain stuck
low and standard recovery fails.
Signed-off-by: Jie Li <jie.i.li@...ia.com>
---
drivers/i2c/i2c-core-base.c | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index ae7e9c8b65a6..ffbab3e4528d 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -42,6 +42,9 @@
#include <linux/rwsem.h>
#include <linux/slab.h>
#include <linux/string_choices.h>
+#include <linux/device.h>
+#include <linux/gpio/driver.h>
+#include <linux/fwnode.h>
#include "i2c-core.h"
@@ -422,9 +425,25 @@ static int i2c_gpio_init_recovery(struct i2c_adapter *adap)
return i2c_gpio_init_generic_recovery(adap);
}
+/* Check if SDA can be driven for recovery when
+ * GPIO direction reporting is unavailable.
+ * Usage: add new flag "force-set-sda" in dts pinctrl.
+ */
+static bool force_set_sda(struct device *dev)
+{
+ if (!dev || !dev->of_node)
+ return false;
+
+ if (of_property_read_bool(dev->of_node, "force-set-sda"))
+ return true;
+ else
+ return false;
+}
+
static int i2c_init_recovery(struct i2c_adapter *adap)
{
struct i2c_bus_recovery_info *bri = adap->bus_recovery_info;
+ struct device *dev = &adap->dev;
bool is_error_level = true;
char *err_str;
@@ -446,7 +465,7 @@ static int i2c_init_recovery(struct i2c_adapter *adap)
if (bri->sda_gpiod) {
bri->get_sda = get_sda_gpio_value;
/* FIXME: add proper flag instead of '0' once available */
- if (gpiod_get_direction(bri->sda_gpiod) == 0)
+ if (gpiod_get_direction(bri->sda_gpiod) == 0 || force_set_sda(dev))
bri->set_sda = set_sda_gpio_value;
}
} else if (bri->recover_bus == i2c_generic_scl_recovery) {
--
2.43.0
Powered by blists - more mailing lists