* [PATCH v2 0/2] i2c: Add i2c-shared-gpio driver
@ 2026-05-11 16:25 Markus Stockhausen
2026-05-11 16:25 ` [PATCH v2 1/2] dt-bindings: i2c: Add i2c-shared-gpio Markus Stockhausen
` (2 more replies)
0 siblings, 3 replies; 14+ messages in thread
From: Markus Stockhausen @ 2026-05-11 16:25 UTC (permalink / raw)
To: brgl, wsa+renesas, andi.shyti, robh, krzk+dt, conor+dt, linux-i2c,
devicetree
Cc: Markus Stockhausen
This series adds support for hardware designs where multiple I2C
gpio based busses are realized with dedicated SDA lines and a
shared SCL line. This way N busses can be realized with N+1 gpios.
Currently there are several Realtek switches that make use of
this design. Samples are:
HPE 1920-48G
Linksys LGS310C
Zyxel GS1920-24
Engenius EWS2910
D-Link DGS-1250
While at first glance this might be a usecase for the new shared
gpio architecture, discussion and testing shows that this is not
trivial at all. A shared gpio is handled by a voting system. If
there at least one user that votes for the GPIO to be "high",
it stays high.
So the implementation manages several classic bitbang buses with
locking/unlocking in the pre_xfer/post_xfer handlers.
Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de>
---
v1 -> v2:
- Adapted the dts so that all SDA/SCL gpios are defined in the
parent node. This way the i2c child nodes are kept clean.
Their <reg> property is used as an index into the parent SDA
list.
- Adapted the driver to make use of the new dts structure.
- Hardened the driver by adding multiple consistency checks
during probing (number of busses, duplicate dts nodes, ...).
- Added recovery handlers that are synchronized via the central
mutex like normal bus transfers.
v1: https://lore.kernel.org/linux-i2c/20260507181711.2696783-1-markus.stockhausen@gmx.de/
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH v2 1/2] dt-bindings: i2c: Add i2c-shared-gpio 2026-05-11 16:25 [PATCH v2 0/2] i2c: Add i2c-shared-gpio driver Markus Stockhausen @ 2026-05-11 16:25 ` Markus Stockhausen 2026-05-11 17:36 ` Rob Herring (Arm) 2026-05-12 20:59 ` sashiko-bot 2026-05-11 16:25 ` [PATCH v2 2/2] i2c: Add driver for gpio based busses with shared SCL Markus Stockhausen 2026-05-12 11:00 ` [PATCH v2 0/2] i2c: Add i2c-shared-gpio driver Bartosz Golaszewski 2 siblings, 2 replies; 14+ messages in thread From: Markus Stockhausen @ 2026-05-11 16:25 UTC (permalink / raw) To: brgl, wsa+renesas, andi.shyti, robh, krzk+dt, conor+dt, linux-i2c, devicetree Cc: Markus Stockhausen Document the driver for bitbanged gpio I2C busses with shared SCL lines. Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de> --- .../bindings/i2c/i2c-gpio-shared.yaml | 105 ++++++++++++++++++ 1 file changed, 105 insertions(+) create mode 100644 Documentation/devicetree/bindings/i2c/i2c-gpio-shared.yaml diff --git a/Documentation/devicetree/bindings/i2c/i2c-gpio-shared.yaml b/Documentation/devicetree/bindings/i2c/i2c-gpio-shared.yaml new file mode 100644 index 000000000000..4a21638bd842 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/i2c-gpio-shared.yaml @@ -0,0 +1,105 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/i2c/i2c-gpio-shared.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Multiple GPIO bitbanged I2C buses with shared SCL + +maintainers: + - Markus Stockhausen <markus.stockhausen@gmx.de> + +description: + Bitbanging I2C bus driver that supports multiple independent I2C buses + sharing a single SCL line. Each child node represents one I2C bus with + its own SDA line. The index of each entry in the parent's sda-gpios + property must match the reg value of the corresponding child bus node. + +properties: + compatible: + const: i2c-gpio-shared + + scl-gpios: + maxItems: 1 + description: + GPIO used for the shared SCL signal. Must be configured as open-drain. + + sda-gpios: + minItems: 2 + maxItems: 32 + description: + GPIOs used for the SDA signals of the individual I2C buses. The index + of the GPIO in this list corresponds to the 'reg' property of the + child bus nodes (e.g., the first GPIO is for the child with reg = <0>). + Must be configured as open-drain. + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + +required: + - compatible + - scl-gpios + - sda-gpios + - "#address-cells" + - "#size-cells" + +patternProperties: + "^i2c@[0-9a-f]+$": + type: object + $ref: /schemas/i2c/i2c-controller.yaml# + unevaluatedProperties: false + + properties: + reg: + items: + - minimum: 0 + maximum: 31 + description: + Index into the sda-gpios array used by this specific bus. + + clock-frequency: + minimum: 1 + maximum: 400000 + default: 100000 + description: + Desired I2C bus clock frequency in Hz. + + required: + - reg + +additionalProperties: false + +examples: + - | + #include <dt-bindings/gpio/gpio.h> + + i2c { + compatible = "i2c-gpio-shared"; + scl-gpios = <&gpio1 31 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + sda-gpios = <&gpio1 6 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>, + <&gpio1 7 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + #address-cells = <1>; + #size-cells = <0>; + + i2c@0 { + reg = <0>; + clock-frequency = <100000>; + #address-cells = <1>; + #size-cells = <0>; + + eeprom@50 { + compatible = "atmel,24c02"; + reg = <0x50>; + }; + }; + + i2c@1 { + reg = <1>; + clock-frequency = <400000>; + #address-cells = <1>; + #size-cells = <0>; + }; + }; -- 2.54.0 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH v2 1/2] dt-bindings: i2c: Add i2c-shared-gpio 2026-05-11 16:25 ` [PATCH v2 1/2] dt-bindings: i2c: Add i2c-shared-gpio Markus Stockhausen @ 2026-05-11 17:36 ` Rob Herring (Arm) 2026-05-12 20:59 ` sashiko-bot 1 sibling, 0 replies; 14+ messages in thread From: Rob Herring (Arm) @ 2026-05-11 17:36 UTC (permalink / raw) To: Markus Stockhausen Cc: linux-i2c, brgl, devicetree, krzk+dt, wsa+renesas, andi.shyti, conor+dt On Mon, 11 May 2026 18:25:27 +0200, Markus Stockhausen wrote: > Document the driver for bitbanged gpio I2C busses > with shared SCL lines. > > Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de> > --- > .../bindings/i2c/i2c-gpio-shared.yaml | 105 ++++++++++++++++++ > 1 file changed, 105 insertions(+) > create mode 100644 Documentation/devicetree/bindings/i2c/i2c-gpio-shared.yaml > My bot found errors running 'make dt_binding_check' on your patch: yamllint warnings/errors: dtschema/dtc warnings/errors: /builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/i2c/i2c-gpio-shared.example.dtb: i2c (i2c-gpio-shared): sda-gpios: [[4294967295, 6, 6], [4294967295, 7, 6]] is too long from schema $id: http://devicetree.org/schemas/i2c/i2c-controller.yaml doc reference errors (make refcheckdocs): See https://patchwork.kernel.org/project/devicetree/patch/20260511162528.84508-2-markus.stockhausen@gmx.de The base for the series is generally the latest rc1. A different dependency should be noted in *this* patch. If you already ran 'make dt_binding_check' and didn't see the above error(s), then make sure 'yamllint' is installed and dt-schema is up to date: pip3 install dtschema --upgrade Please check and re-submit after running the above command yourself. Note that DT_SCHEMA_FILES can be set to your schema file to speed up checking your schema. However, it must be unset to test all examples with your schema. ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v2 1/2] dt-bindings: i2c: Add i2c-shared-gpio 2026-05-11 16:25 ` [PATCH v2 1/2] dt-bindings: i2c: Add i2c-shared-gpio Markus Stockhausen 2026-05-11 17:36 ` Rob Herring (Arm) @ 2026-05-12 20:59 ` sashiko-bot 1 sibling, 0 replies; 14+ messages in thread From: sashiko-bot @ 2026-05-12 20:59 UTC (permalink / raw) To: Markus Stockhausen; +Cc: krzk+dt, conor+dt, devicetree, robh Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [Medium] The `minimum` and `maximum` constraints on the child node's `reg` property are structurally invalid and will be silently ignored during schema validation. - [Medium] The compatible string in the commit message does not match the actual code implementation. -- commit 5af80521617a83464e505c8a8c5b9d9829717a38 Author: Markus Stockhausen <markus.stockhausen@gmx.de> dt-bindings: i2c: Add i2c-shared-gpio This commit documents the driver for bitbanged GPIO I2C buses with shared SCL lines. > dt-bindings: i2c: Add i2c-shared-gpio This isn't a bug, but the commit subject and description refer to the binding as i2c-shared-gpio, whereas the actual compatible string and file name introduced in the patch use i2c-gpio-shared. > diff --git a/Documentation/devicetree/bindings/i2c/i2c-gpio-shared.yaml b/Documentation/devicetree/bindings/i2c/i2c-gpio-shared.yaml > new file mode 100644 > --- /dev/null > +++ b/Documentation/devicetree/bindings/i2c/i2c-gpio-shared.yaml [ ... ] > + properties: > + reg: > + items: > + - minimum: 0 > + maximum: 31 Will these minimum and maximum constraints be silently ignored during schema validation? Because dt-schema decodes standard properties like reg internally as a matrix (an array of arrays), applying JSON Schema numeric constraints inside an items list evaluates them against an array instance instead of a numeric integer. Since JSON Schema ignores numeric constraints on non-numeric types, out-of-bounds reg values might silently pass schema validation. -- Sashiko AI review · https://sashiko.dev/#/patchset/20260511162528.84508-1-markus.stockhausen@gmx.de?part=1 ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH v2 2/2] i2c: Add driver for gpio based busses with shared SCL 2026-05-11 16:25 [PATCH v2 0/2] i2c: Add i2c-shared-gpio driver Markus Stockhausen 2026-05-11 16:25 ` [PATCH v2 1/2] dt-bindings: i2c: Add i2c-shared-gpio Markus Stockhausen @ 2026-05-11 16:25 ` Markus Stockhausen 2026-05-12 21:44 ` sashiko-bot 2026-05-12 11:00 ` [PATCH v2 0/2] i2c: Add i2c-shared-gpio driver Bartosz Golaszewski 2 siblings, 1 reply; 14+ messages in thread From: Markus Stockhausen @ 2026-05-11 16:25 UTC (permalink / raw) To: brgl, wsa+renesas, andi.shyti, robh, krzk+dt, conor+dt, linux-i2c, devicetree Cc: Markus Stockhausen Some lower end hardware (especially Realtek based switches) are designed with multiple I2C busses that share a single clock (SCL) line. E.g. the D-Link DGS-1250-28X realizes 4 I2C SFP busses with 5 gpios. Provide a i2c-gpio-shared driver that handles such hardware designs. It manages up to 32 buses that share a single SCL line. All data transfers (including recovery) are synchronized with a central mutex. Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de> --- drivers/i2c/busses/Kconfig | 10 ++ drivers/i2c/busses/Makefile | 1 + drivers/i2c/busses/i2c-gpio-shared.c | 257 +++++++++++++++++++++++++++ 3 files changed, 268 insertions(+) create mode 100644 drivers/i2c/busses/i2c-gpio-shared.c diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 8c935f867a37..fa05db759b15 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -691,6 +691,16 @@ config I2C_GPIO This is a very simple bitbanging I2C driver utilizing the arch-neutral GPIO API to control the SCL and SDA lines. +config I2C_GPIO_SHARED + tristate "multiple GPIO-based bitbanging I2C with shared SCL" + depends on GPIOLIB || COMPILE_TEST + select I2C_ALGOBIT + help + This is an alternative of the I2C GPIO driver for devices with only + few GPIO pins where multiple busses with dedicated SDA lines share + a single SCL line. It can handle up to 32 buses, where N shared I2C + buses are implemented with N+1 GPIO. + config I2C_GPIO_FAULT_INJECTOR bool "GPIO-based fault injector" depends on I2C_GPIO diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile index 547123ab351f..724b09e613cb 100644 --- a/drivers/i2c/busses/Makefile +++ b/drivers/i2c/busses/Makefile @@ -66,6 +66,7 @@ obj-$(CONFIG_I2C_EG20T) += i2c-eg20t.o obj-$(CONFIG_I2C_EMEV2) += i2c-emev2.o obj-$(CONFIG_I2C_EXYNOS5) += i2c-exynos5.o obj-$(CONFIG_I2C_GPIO) += i2c-gpio.o +obj-$(CONFIG_I2C_GPIO_SHARED) += i2c-gpio-shared.o obj-$(CONFIG_I2C_HIGHLANDER) += i2c-highlander.o obj-$(CONFIG_I2C_HISI) += i2c-hisi.o obj-$(CONFIG_I2C_HIX5HD2) += i2c-hix5hd2.o diff --git a/drivers/i2c/busses/i2c-gpio-shared.c b/drivers/i2c/busses/i2c-gpio-shared.c new file mode 100644 index 000000000000..4c2e4011fcbf --- /dev/null +++ b/drivers/i2c/busses/i2c-gpio-shared.c @@ -0,0 +1,257 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Bitbanging driver for up to 32 I2C buses that share a single SCL pin */ + +#include <linux/gpio/consumer.h> +#include <linux/i2c-algo-bit.h> +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> + +#define MAX_BUSES 32 + +struct gpio_shared_ctx; + +struct gpio_shared_bus { + struct gpio_desc *sda; + struct i2c_adapter adap; + struct i2c_algo_bit_data bit_data; + struct i2c_bus_recovery_info recovery_info; + struct gpio_shared_ctx *ctx; +}; + +struct gpio_shared_ctx { + struct gpio_desc *scl; + struct mutex lock; + struct gpio_shared_bus bus[]; +}; + +static struct gpio_shared_bus *adap_to_bus(struct i2c_adapter *adap) +{ + return container_of(adap, struct gpio_shared_bus, adap); +} + +static struct gpio_shared_ctx *adap_to_ctx(struct i2c_adapter *adap) +{ + return adap_to_bus(adap)->ctx; +} + +static void gpio_shared_setsda(void *data, int state) +{ + struct gpio_shared_bus *bus = data; + + gpiod_set_value_cansleep(bus->sda, state); +} + +static void gpio_shared_setscl(void *data, int state) +{ + struct gpio_shared_bus *bus = data; + + gpiod_set_value_cansleep(bus->ctx->scl, state); +} + +static int gpio_shared_getsda(void *data) +{ + struct gpio_shared_bus *bus = data; + + return gpiod_get_value_cansleep(bus->sda); +} + +static int gpio_shared_getscl(void *data) +{ + struct gpio_shared_bus *bus = data; + + return gpiod_get_value_cansleep(bus->ctx->scl); +} + +static int gpio_shared_pre_xfer(struct i2c_adapter *adap) +{ + return mutex_lock_interruptible(&adap_to_ctx(adap)->lock); +} + +static void gpio_shared_recovery_set_scl(struct i2c_adapter *adap, int val) +{ + gpiod_set_value_cansleep(adap_to_ctx(adap)->scl, val); +} + +static int gpio_shared_recovery_get_scl(struct i2c_adapter *adap) +{ + return gpiod_get_value_cansleep(adap_to_ctx(adap)->scl); +} + +static void gpio_shared_recovery_set_sda(struct i2c_adapter *adap, int val) +{ + gpiod_set_value_cansleep(adap_to_bus(adap)->sda, val); +} + +static int gpio_shared_recovery_get_sda(struct i2c_adapter *adap) +{ + return gpiod_get_value_cansleep(adap_to_bus(adap)->sda); +} + +static void gpio_shared_prepare_recovery(struct i2c_adapter *adap) +{ + mutex_lock(&adap_to_ctx(adap)->lock); +} + +static void gpio_shared_unlock(struct i2c_adapter *adap) +{ + mutex_unlock(&adap_to_ctx(adap)->lock); +} + +static void gpio_shared_del_adapter(void *data) +{ + i2c_del_adapter(data); +} + +static int gpio_shared_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int bus_count, sda_count, ret; + struct gpio_shared_ctx *ctx; + u32 used_buses = 0; + + bus_count = device_get_child_node_count(dev); + if (!bus_count) + return dev_err_probe(dev, -EINVAL, "no buses defined\n"); + + if (bus_count > MAX_BUSES) + return dev_err_probe(dev, -EINVAL, "maximum of %d buses allowed\n", MAX_BUSES); + + sda_count = gpiod_count(dev, "sda"); + if (sda_count < 0) + return dev_err_probe(dev, sda_count, "sda-gpios missing\n"); + + if (sda_count != bus_count) + return dev_err_probe(dev, -EINVAL, + "sda-gpios count (%d) must match child node count (%d)\n", + sda_count, bus_count); + + ctx = devm_kzalloc(dev, struct_size(ctx, bus, bus_count), GFP_KERNEL); + if (!ctx) + return dev_err_probe(dev, -ENOMEM, "memory allocation failed\n"); + + ret = devm_mutex_init(dev, &ctx->lock); + if (ret) + return dev_err_probe(dev, ret, "mutex initialization failed\n"); + + platform_set_drvdata(pdev, ctx); + + ctx->scl = devm_gpiod_get(dev, "scl", GPIOD_OUT_HIGH_OPEN_DRAIN); + if (IS_ERR(ctx->scl)) + return dev_err_probe(dev, PTR_ERR(ctx->scl), "shared SCL GPIO not found\n"); + + device_for_each_child_node_scoped(dev, child) { + struct i2c_algo_bit_data *bit_data; + struct i2c_bus_recovery_info *ri; + struct gpio_shared_bus *bus; + struct i2c_adapter *adap; + u32 bus_num, clock_freq; + + /* Use the child's reg value as the index into the sda-gpios array. */ + ret = fwnode_property_read_u32(child, "reg", &bus_num); + if (ret) + return dev_err_probe(dev, ret, + "missing reg property in child node\n"); + + if (bus_num >= sda_count) + return dev_err_probe(dev, -EINVAL, + "reg value %u out of range (max %d)\n", + bus_num, sda_count - 1); + + if (used_buses & BIT(bus_num)) + return dev_err_probe(dev, -EINVAL, + "duplicate definition of bus %d\n", bus_num); + + bus = &ctx->bus[bus_num]; + bit_data = &bus->bit_data; + ri = &bus->recovery_info; + adap = &bus->adap; + + bus->sda = devm_gpiod_get_index(dev, "sda", bus_num, + GPIOD_OUT_HIGH_OPEN_DRAIN); + if (IS_ERR(bus->sda)) + return dev_err_probe(dev, PTR_ERR(bus->sda), + "SDA GPIO for bus %u not found\n", bus_num); + /* + * Data transfer synchronization between multiple busses is realized by mutex + * locking/unlocking within pre_xfer and post_xfer helpers. + */ + bus->ctx = ctx; + bit_data->data = bus; + bit_data->setsda = gpio_shared_setsda; + bit_data->setscl = gpio_shared_setscl; + bit_data->getsda = gpio_shared_getsda; + bit_data->getscl = gpio_shared_getscl; + bit_data->pre_xfer = gpio_shared_pre_xfer; + bit_data->post_xfer = gpio_shared_unlock; + bit_data->timeout = msecs_to_jiffies(100); + /* + * clock-frequency specifies the I2C bus frequency. Convert to the half-period + * delay in microseconds that i2c-algo-bit expects. Default to 5 us (~100 kHz) + * if not specified. This is usually lower than the configured frequency, + * especially near the 400 kHz limit. + */ + if (!fwnode_property_read_u32(child, "clock-frequency", &clock_freq) && + clock_freq > 0) + bit_data->udelay = max(1u, DIV_ROUND_UP(1000000, 2 * clock_freq)); + else + bit_data->udelay = 5; + /* + * i2c_recover_bus() is called by the I2C core without going through pre_xfer + * and post_xfer. So the shared SCL mutex is NOT held at that point. Provide + * prepare_recovery/unprepare_recovery to explicitly prevent concurrent SCL + * access from another bus. + */ + ri->recover_bus = i2c_generic_scl_recovery; + ri->set_scl = gpio_shared_recovery_set_scl; + ri->get_scl = gpio_shared_recovery_get_scl; + ri->set_sda = gpio_shared_recovery_set_sda; + ri->get_sda = gpio_shared_recovery_get_sda; + ri->prepare_recovery = gpio_shared_prepare_recovery; + ri->unprepare_recovery = gpio_shared_unlock; + + adap->dev.parent = dev; + adap->owner = THIS_MODULE; + adap->algo_data = bit_data; + adap->bus_recovery_info = ri; + device_set_node(&adap->dev, child); + snprintf(adap->name, sizeof(adap->name), "i2c-gpio-shared:%u", bus_num); + + ret = devm_add_action_or_reset(dev, gpio_shared_del_adapter, adap); + if (ret) + return dev_err_probe(dev, ret, + "bus %u cleanup registration failed\n", bus_num); + + ret = i2c_bit_add_bus(adap); + if (ret) + return dev_err_probe(dev, ret, "failed to register bus %u\n", bus_num); + + dev_info(dev, "registered I2C bus %u with shared SCL (udelay %d us)\n", + bus_num, bit_data->udelay); + + used_buses |= BIT(bus_num); + } + + return 0; +} + +static const struct of_device_id gpio_shared_of_match[] = { + { .compatible = "i2c-gpio-shared" }, + {} +}; +MODULE_DEVICE_TABLE(of, gpio_shared_of_match); + +static struct platform_driver gpio_shared_driver = { + .probe = gpio_shared_probe, + .driver = { + .name = "i2c-gpio-shared", + .of_match_table = gpio_shared_of_match, + }, +}; + +module_platform_driver(gpio_shared_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Markus Stockhausen <markus.stockhausen@gmx.de>"); +MODULE_DESCRIPTION("bitbanging multi I2C driver for shared SCL"); -- 2.54.0 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH v2 2/2] i2c: Add driver for gpio based busses with shared SCL 2026-05-11 16:25 ` [PATCH v2 2/2] i2c: Add driver for gpio based busses with shared SCL Markus Stockhausen @ 2026-05-12 21:44 ` sashiko-bot 0 siblings, 0 replies; 14+ messages in thread From: sashiko-bot @ 2026-05-12 21:44 UTC (permalink / raw) To: Markus Stockhausen; +Cc: krzk+dt, conor+dt, devicetree, robh Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [High] The child fwnode reference is dropped at the end of the scoped loop, leaving a dangling pointer in the adapter device. - [High] An integer overflow in the clock-frequency calculation can cause a division by zero panic. - [Low] Usage of non-existent API `devm_mutex_init()` will cause a build failure. -- commit 26b139c7eeb6ff63f03c34df65162da9a405e505 Author: Markus Stockhausen <markus.stockhausen@gmx.de> i2c: Add driver for gpio based busses with shared SCL This commit introduces a new I2C driver for hardware designs that share a single SCL line across multiple busses. It synchronizes data transfers across up to 32 busses using a central mutex. > diff --git a/drivers/i2c/busses/i2c-gpio-shared.c b/drivers/i2c/busses/i2c-gpio-shared.c > new file mode 100644 > index 0000000000000..4c2e4011fcbf1 > --- /dev/null > +++ b/drivers/i2c/busses/i2c-gpio-shared.c [ ... ] > +static int gpio_shared_probe(struct platform_device *pdev) > +{ [ ... ] > + ctx = devm_kzalloc(dev, struct_size(ctx, bus, bus_count), GFP_KERNEL); > + if (!ctx) > + return dev_err_probe(dev, -ENOMEM, "memory allocation failed\n"); > + > + ret = devm_mutex_init(dev, &ctx->lock); Is this devm_mutex_init() intended to be a different function? It does not appear to exist in the kernel and will likely cause a build failure. > + if (ret) > + return dev_err_probe(dev, ret, "mutex initialization failed\n"); > + > + platform_set_drvdata(pdev, ctx); > + > + ctx->scl = devm_gpiod_get(dev, "scl", GPIOD_OUT_HIGH_OPEN_DRAIN); > + if (IS_ERR(ctx->scl)) > + return dev_err_probe(dev, PTR_ERR(ctx->scl), "shared SCL GPIO not found\n"); > + > + device_for_each_child_node_scoped(dev, child) { > + struct i2c_algo_bit_data *bit_data; [ ... ] > + u32 bus_num, clock_freq; [ ... ] > + /* > + * clock-frequency specifies the I2C bus frequency. Convert to the half-period > + * delay in microseconds that i2c-algo-bit expects. Default to 5 us (~100 kHz) > + * if not specified. This is usually lower than the configured frequency, > + * especially near the 400 kHz limit. > + */ > + if (!fwnode_property_read_u32(child, "clock-frequency", &clock_freq) && > + clock_freq > 0) > + bit_data->udelay = max(1u, DIV_ROUND_UP(1000000, 2 * clock_freq)); If clock-frequency is provided as 0x80000000 in the device tree, could the 32-bit unsigned multiplication 2 * clock_freq wrap around to 0? That would cause DIV_ROUND_UP() to perform a division by zero panic. > + else > + bit_data->udelay = 5; [ ... ] > + adap->dev.parent = dev; > + adap->owner = THIS_MODULE; > + adap->algo_data = bit_data; > + adap->bus_recovery_info = ri; > + device_set_node(&adap->dev, child); Since device_for_each_child_node_scoped() automatically drops the fwnode reference at the end of each iteration, and device_set_node() assigns the pointer without incrementing the reference count, will this leave adap->dev.fwnode pointing to unreferenced memory when the loop advances? > + snprintf(adap->name, sizeof(adap->name), "i2c-gpio-shared:%u", bus_num); > + > + ret = devm_add_action_or_reset(dev, gpio_shared_del_adapter, adap); -- Sashiko AI review · https://sashiko.dev/#/patchset/20260511162528.84508-1-markus.stockhausen@gmx.de?part=2 ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v2 0/2] i2c: Add i2c-shared-gpio driver 2026-05-11 16:25 [PATCH v2 0/2] i2c: Add i2c-shared-gpio driver Markus Stockhausen 2026-05-11 16:25 ` [PATCH v2 1/2] dt-bindings: i2c: Add i2c-shared-gpio Markus Stockhausen 2026-05-11 16:25 ` [PATCH v2 2/2] i2c: Add driver for gpio based busses with shared SCL Markus Stockhausen @ 2026-05-12 11:00 ` Bartosz Golaszewski 2026-05-13 5:33 ` AW: " markus.stockhausen 2026-05-13 12:11 ` Rob Herring 2 siblings, 2 replies; 14+ messages in thread From: Bartosz Golaszewski @ 2026-05-12 11:00 UTC (permalink / raw) To: Markus Stockhausen Cc: wsa+renesas, andi.shyti, robh, krzk+dt, conor+dt, linux-i2c, devicetree, Marek Vasut On Mon, May 11, 2026 at 6:25 PM Markus Stockhausen <markus.stockhausen@gmx.de> wrote: > > This series adds support for hardware designs where multiple I2C > gpio based busses are realized with dedicated SDA lines and a > shared SCL line. This way N busses can be realized with N+1 gpios. > > Currently there are several Realtek switches that make use of > this design. Samples are: > > HPE 1920-48G > Linksys LGS310C > Zyxel GS1920-24 > Engenius EWS2910 > D-Link DGS-1250 > > While at first glance this might be a usecase for the new shared > gpio architecture, discussion and testing shows that this is not > trivial at all. A shared gpio is handled by a voting system. If > there at least one user that votes for the GPIO to be "high", > it stays high. > > So the implementation manages several classic bitbang buses with > locking/unlocking in the pre_xfer/post_xfer handlers. > > Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de> > Hi! A couple high-level issues. You'll soon hear from the DT maintainers and they'll ask if the i2c-shared-gpio compatible corresponds with an actual piece of hardware on the board. It does not, so the bindings will be rejected. A virtual device doing the mediation is fine but it probably needs to be an auxiliary device instantiated dynamically from C code. It just so happens that at the same time as you submitting this, Marek Vasut wants to enable shared write-protect GPIOs for EEPROMs. This seems to be a similar situation where the default is to keep the line high and drive it low if there's at least one consumer that wants it. I will rework the gpio-shared-proxy driver with that logic in mind. Would that be enough to address the issue here? Bart ^ permalink raw reply [flat|nested] 14+ messages in thread
* AW: [PATCH v2 0/2] i2c: Add i2c-shared-gpio driver 2026-05-12 11:00 ` [PATCH v2 0/2] i2c: Add i2c-shared-gpio driver Bartosz Golaszewski @ 2026-05-13 5:33 ` markus.stockhausen 2026-05-13 7:12 ` Wolfram Sang 2026-05-13 12:11 ` Rob Herring 1 sibling, 1 reply; 14+ messages in thread From: markus.stockhausen @ 2026-05-13 5:33 UTC (permalink / raw) To: 'Bartosz Golaszewski' Cc: wsa+renesas, andi.shyti, robh, krzk+dt, conor+dt, linux-i2c, devicetree, 'Marek Vasut' > Von: Bartosz Golaszewski <brgl@kernel.org> > Gesendet: Dienstag, 12. Mai 2026 13:00 > An: Markus Stockhausen <markus.stockhausen@gmx.de> > Betreff: Re: [PATCH v2 0/2] i2c: Add i2c-shared-gpio driver > ... > Hi! > > A couple high-level issues. You'll soon hear from the DT maintainers > and they'll ask if the i2c-shared-gpio compatible corresponds with an > actual piece of hardware on the board. It does not, so the bindings > will be rejected. > > A virtual device doing the mediation is fine but it probably needs to > be an auxiliary device instantiated dynamically from C code. This whole I2C coding is totally new to me. Can you please elaborate what this means. Do I need to move over into some other folder? > It just so happens that at the same time as you submitting this, Marek > Vasut wants to enable shared write-protect GPIOs for EEPROMs. This > seems to be a similar situation where the default is to keep the line > high and drive it low if there's at least one consumer that wants it. > I will rework the gpio-shared-proxy driver with that logic in mind. > Would that be enough to address the issue here? I'm unsure if this helps. From my understanding SCL gets toggled high/low for each transferred bit during an operation. This data block may not be intercepted by other consumers Markus ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: AW: [PATCH v2 0/2] i2c: Add i2c-shared-gpio driver 2026-05-13 5:33 ` AW: " markus.stockhausen @ 2026-05-13 7:12 ` Wolfram Sang 2026-05-13 7:26 ` Bartosz Golaszewski 0 siblings, 1 reply; 14+ messages in thread From: Wolfram Sang @ 2026-05-13 7:12 UTC (permalink / raw) To: markus.stockhausen Cc: 'Bartosz Golaszewski', andi.shyti, robh, krzk+dt, conor+dt, linux-i2c, devicetree, 'Marek Vasut' [-- Attachment #1: Type: text/plain, Size: 1015 bytes --] > > It just so happens that at the same time as you submitting this, Marek > > Vasut wants to enable shared write-protect GPIOs for EEPROMs. This > > seems to be a similar situation where the default is to keep the line > > high and drive it low if there's at least one consumer that wants it. > > I will rework the gpio-shared-proxy driver with that logic in mind. > > Would that be enough to address the issue here? > > I'm unsure if this helps. From my understanding SCL gets toggled > high/low for each transferred bit during an operation. This data block > may not be intercepted by other consumers I agree. SCL is shared between the busses but if one bus uses it, the bus needs exclusive access to generate the desired clock rate. The other busses have to wait until the on-going transfer is over. This is different from the shared-GPIO examples Bart gave which need all the input ANDed together which would result in a chaos clock rate here. But thanks for the explanations, Bart! [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 833 bytes --] ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: AW: [PATCH v2 0/2] i2c: Add i2c-shared-gpio driver 2026-05-13 7:12 ` Wolfram Sang @ 2026-05-13 7:26 ` Bartosz Golaszewski 2026-05-13 7:47 ` Wolfram Sang 0 siblings, 1 reply; 14+ messages in thread From: Bartosz Golaszewski @ 2026-05-13 7:26 UTC (permalink / raw) To: Wolfram Sang Cc: markus.stockhausen, andi.shyti, robh, krzk+dt, conor+dt, linux-i2c, devicetree, Marek Vasut On Wed, May 13, 2026 at 9:12 AM Wolfram Sang <wsa+renesas@sang-engineering.com> wrote: > > > > > It just so happens that at the same time as you submitting this, Marek > > > Vasut wants to enable shared write-protect GPIOs for EEPROMs. This > > > seems to be a similar situation where the default is to keep the line > > > high and drive it low if there's at least one consumer that wants it. > > > I will rework the gpio-shared-proxy driver with that logic in mind. > > > Would that be enough to address the issue here? > > > > I'm unsure if this helps. From my understanding SCL gets toggled > > high/low for each transferred bit during an operation. This data block > > may not be intercepted by other consumers > > I agree. SCL is shared between the busses but if one bus uses it, the > bus needs exclusive access to generate the desired clock rate. The other > busses have to wait until the on-going transfer is over. This is > different from the shared-GPIO examples Bart gave which need all the > input ANDed together which would result in a chaos clock rate here. > > But thanks for the explanations, Bart! > Yes, in that case it seems to me it needs a separate virtual device driving it. In general, the idea Markus presented is fine but it must not be described in DT. Instead, you need to create an auxiliary device from subsystem code. For instance: if you see the `scl-gpios` property, you create an auxiliary device, assign it the GPIO as a software node reference and make the bus use it somehow. I don't have an exact solution because I'd need to sit down and figure it out precisely but that's the high-level idea I think may work. Bart ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: AW: [PATCH v2 0/2] i2c: Add i2c-shared-gpio driver 2026-05-13 7:26 ` Bartosz Golaszewski @ 2026-05-13 7:47 ` Wolfram Sang 2026-05-13 7:50 ` Bartosz Golaszewski 0 siblings, 1 reply; 14+ messages in thread From: Wolfram Sang @ 2026-05-13 7:47 UTC (permalink / raw) To: Bartosz Golaszewski Cc: markus.stockhausen, andi.shyti, robh, krzk+dt, conor+dt, linux-i2c, devicetree, Marek Vasut [-- Attachment #1: Type: text/plain, Size: 662 bytes --] Hi Bart, > Yes, in that case it seems to me it needs a separate virtual device > driving it. In general, the idea Markus presented is fine but it must > not be described in DT. Instead, you need to create an auxiliary > device from subsystem code. Okay, I get it that it should not be described in DT. DT should only have standard i2c-gpio busses and the OS needs to figure the details, right? I wonder about the subsystem involvement, though. The I2C core doesn't care about the scl-gpios property (except for GPIO recovery which is moot with the i2c-gpio driver). So, maybe this should be handled in the i2c-gpio driver instead? All the best, Wolfram [-- Attachment #2: signature.asc --] [-- Type: application/pgp-signature, Size: 833 bytes --] ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: AW: [PATCH v2 0/2] i2c: Add i2c-shared-gpio driver 2026-05-13 7:47 ` Wolfram Sang @ 2026-05-13 7:50 ` Bartosz Golaszewski 0 siblings, 0 replies; 14+ messages in thread From: Bartosz Golaszewski @ 2026-05-13 7:50 UTC (permalink / raw) To: Wolfram Sang Cc: markus.stockhausen, andi.shyti, robh, krzk+dt, conor+dt, linux-i2c, devicetree, Marek Vasut On Wed, May 13, 2026 at 9:47 AM Wolfram Sang <wsa+renesas@sang-engineering.com> wrote: > > Hi Bart, > > > Yes, in that case it seems to me it needs a separate virtual device > > driving it. In general, the idea Markus presented is fine but it must > > not be described in DT. Instead, you need to create an auxiliary > > device from subsystem code. > > Okay, I get it that it should not be described in DT. DT should only > have standard i2c-gpio busses and the OS needs to figure the details, > right? > > I wonder about the subsystem involvement, though. The I2C core doesn't > care about the scl-gpios property (except for GPIO recovery which is > moot with the i2c-gpio driver). So, maybe this should be handled in the > i2c-gpio driver instead? > Sure, the driver can live under drivers/gpio/ and still be instantiated in i2c core, no problem with that. Bartosz ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH v2 0/2] i2c: Add i2c-shared-gpio driver 2026-05-12 11:00 ` [PATCH v2 0/2] i2c: Add i2c-shared-gpio driver Bartosz Golaszewski 2026-05-13 5:33 ` AW: " markus.stockhausen @ 2026-05-13 12:11 ` Rob Herring 2026-05-13 12:25 ` AW: " markus.stockhausen 1 sibling, 1 reply; 14+ messages in thread From: Rob Herring @ 2026-05-13 12:11 UTC (permalink / raw) To: Bartosz Golaszewski Cc: Markus Stockhausen, wsa+renesas, andi.shyti, krzk+dt, conor+dt, linux-i2c, devicetree, Marek Vasut On Tue, May 12, 2026 at 01:00:08PM +0200, Bartosz Golaszewski wrote: > On Mon, May 11, 2026 at 6:25 PM Markus Stockhausen > <markus.stockhausen@gmx.de> wrote: > > > > This series adds support for hardware designs where multiple I2C > > gpio based busses are realized with dedicated SDA lines and a > > shared SCL line. This way N busses can be realized with N+1 gpios. > > > > Currently there are several Realtek switches that make use of > > this design. Samples are: > > > > HPE 1920-48G > > Linksys LGS310C > > Zyxel GS1920-24 > > Engenius EWS2910 > > D-Link DGS-1250 > > > > While at first glance this might be a usecase for the new shared > > gpio architecture, discussion and testing shows that this is not > > trivial at all. A shared gpio is handled by a voting system. If > > there at least one user that votes for the GPIO to be "high", > > it stays high. > > > > So the implementation manages several classic bitbang buses with > > locking/unlocking in the pre_xfer/post_xfer handlers. > > > > Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de> > > > > Hi! > > A couple high-level issues. You'll soon hear from the DT maintainers > and they'll ask if the i2c-shared-gpio compatible corresponds with an > actual piece of hardware on the board. It does not, so the bindings > will be rejected. Perhaps you missed I've already commented on this. I'm actually fine with the binding at a high level. But if we want to model it as N i2c-gpio nodes I'm fine with that as well. Rob ^ permalink raw reply [flat|nested] 14+ messages in thread
* AW: [PATCH v2 0/2] i2c: Add i2c-shared-gpio driver 2026-05-13 12:11 ` Rob Herring @ 2026-05-13 12:25 ` markus.stockhausen 0 siblings, 0 replies; 14+ messages in thread From: markus.stockhausen @ 2026-05-13 12:25 UTC (permalink / raw) To: 'Rob Herring', 'Bartosz Golaszewski' Cc: wsa+renesas, andi.shyti, krzk+dt, conor+dt, linux-i2c, devicetree, 'Marek Vasut' > Von: Rob Herring <robh@kernel.org> > Gesendet: Mittwoch, 13. Mai 2026 14:12 > Betreff: Re: [PATCH v2 0/2] i2c: Add i2c-shared-gpio driver > ... > > > So the implementation manages several classic bitbang buses with > > > locking/unlocking in the pre_xfer/post_xfer handlers. > > > > > > Signed-off-by: Markus Stockhausen <markus.stockhausen@gmx.de> > > > > > > > Hi! > > > > A couple high-level issues. You'll soon hear from the DT maintainers > > and they'll ask if the i2c-shared-gpio compatible corresponds with an > > actual piece of hardware on the board. It does not, so the bindings > > will be rejected. > > Perhaps you missed I've already commented on this. I'm actually fine > with the binding at a high level. > > But if we want to model it as N i2c-gpio nodes I'm fine with that as > well. Thanks for all your feedback on this. If it helps the dts and the end user I can offer to convert the driver into a simple drop-in replacement for i2c-gpio. Enhancing the existing i2c-gpio driver is beyond my current knowledge. Markus ^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2026-05-13 12:25 UTC | newest] Thread overview: 14+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-05-11 16:25 [PATCH v2 0/2] i2c: Add i2c-shared-gpio driver Markus Stockhausen 2026-05-11 16:25 ` [PATCH v2 1/2] dt-bindings: i2c: Add i2c-shared-gpio Markus Stockhausen 2026-05-11 17:36 ` Rob Herring (Arm) 2026-05-12 20:59 ` sashiko-bot 2026-05-11 16:25 ` [PATCH v2 2/2] i2c: Add driver for gpio based busses with shared SCL Markus Stockhausen 2026-05-12 21:44 ` sashiko-bot 2026-05-12 11:00 ` [PATCH v2 0/2] i2c: Add i2c-shared-gpio driver Bartosz Golaszewski 2026-05-13 5:33 ` AW: " markus.stockhausen 2026-05-13 7:12 ` Wolfram Sang 2026-05-13 7:26 ` Bartosz Golaszewski 2026-05-13 7:47 ` Wolfram Sang 2026-05-13 7:50 ` Bartosz Golaszewski 2026-05-13 12:11 ` Rob Herring 2026-05-13 12:25 ` AW: " markus.stockhausen
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox