From: Vadim Pasternak <vadimp@nvidia.com>
To: <peda@lysator.liu.se>
Cc: <wsa@the-dreams.de>, <linux-i2c@vger.kernel.org>,
Vadim Pasternak <vadimp@nvidia.com>
Subject: [PATCH i2c-mux next 1/1] i2c: mux: Add register map based mux driver
Date: Tue, 19 May 2026 20:43:10 +0300 [thread overview]
Message-ID: <20260519174310.1128423-1-vadimp@nvidia.com> (raw)
Add 'regmap' mux driver to allow virtual bus switching by setting a
single selector register.
The 'regmap' is supposed to be passed to driver within a platform data
by parent platform driver.
Unlike i2c-mux-reg, this driver relies on regmap abstraction
instead of direct MMIO access, allowing the mux selector register
to be accessed through arbitrary transport mechanisms supported
by regmap callbacks, including indirect PCIe-to-SPI and PCIe-to-LPC
transactions.
The register may reside on any bus type or mapped memory, supported by
'regmap' infrastructure.
Motivation is to support mux selector registers accessed indirectly
through transport-specific protocols.
For example, an FPGA connected through PCIe may expose SPI or LPC
bridges used to access downstream CPLDs or secondary FPGAs located on
another board. In such configurations the mux selector register is not
directly memory-mapped and access is performed through regmap
reg_read()/reg_write() callbacks implementing the required indirect
transaction protocol.
Signed-off-by: Vadim Pasternak <vadimp@nvidia.com>
---
Note:
Originally proposed in 2023:
https://patchwork.ozlabs.org/project/linux-i2c/patch/20230823155332.48648-2-vadimp@nvidia.com/
Reworked against current upstream.
---
drivers/i2c/muxes/Kconfig | 12 ++
drivers/i2c/muxes/Makefile | 1 +
drivers/i2c/muxes/i2c-mux-regmap.c | 150 +++++++++++++++++++
include/linux/platform_data/i2c-mux-regmap.h | 37 +++++
4 files changed, 200 insertions(+)
create mode 100644 drivers/i2c/muxes/i2c-mux-regmap.c
create mode 100644 include/linux/platform_data/i2c-mux-regmap.h
diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig
index 6d2f66810cdc..9b5e5258917a 100644
--- a/drivers/i2c/muxes/Kconfig
+++ b/drivers/i2c/muxes/Kconfig
@@ -99,6 +99,18 @@ config I2C_MUX_REG
This driver can also be built as a module. If so, the module
will be called i2c-mux-reg.
+config I2C_MUX_REGMAP
+ tristate "Register map based I2C multiplexer"
+ depends on REGMAP
+ help
+ If you say yes to this option, support will be included for a
+ register map based I2C multiplexer. This driver provides access to
+ I2C busses connected through a MUX, which is controlled
+ by a single register through the regmap.
+
+ This driver can also be built as a module. If so, the module
+ will be called i2c-mux-regmap.
+
config I2C_DEMUX_PINCTRL
tristate "pinctrl-based I2C demultiplexer"
depends on PINCTRL && OF
diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile
index 4b24f49515a7..edecfc593df7 100644
--- a/drivers/i2c/muxes/Makefile
+++ b/drivers/i2c/muxes/Makefile
@@ -15,5 +15,6 @@ obj-$(CONFIG_I2C_MUX_PCA9541) += i2c-mux-pca9541.o
obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o
obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o
obj-$(CONFIG_I2C_MUX_REG) += i2c-mux-reg.o
+obj-$(CONFIG_I2C_MUX_REGMAP) += i2c-mux-regmap.o
ccflags-$(CONFIG_I2C_DEBUG_BUS) := -DDEBUG
diff --git a/drivers/i2c/muxes/i2c-mux-regmap.c b/drivers/i2c/muxes/i2c-mux-regmap.c
new file mode 100644
index 000000000000..485f275baeb5
--- /dev/null
+++ b/drivers/i2c/muxes/i2c-mux-regmap.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*
+ * Regmap i2c mux driver
+ *
+ * Copyright (C) 2026 Nvidia Technologies Ltd.
+ */
+
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/i2c-mux.h>
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_data/i2c-mux-regmap.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+/* i2c_mux_regmap - mux control structure:
+ * @last_val - last selected register value or -1 if mux deselected;
+ * @pdata: platform data;
+ */
+struct i2c_mux_regmap {
+ int last_val;
+ struct i2c_mux_regmap_platform_data pdata;
+};
+
+static int i2c_mux_regmap_select_chan(struct i2c_mux_core *muxc, u32 chan)
+{
+ struct i2c_mux_regmap *mux = i2c_mux_priv(muxc);
+ int err = 0;
+
+ /* Only select the channel if its different from the last channel */
+ if (mux->last_val != chan) {
+ err = regmap_write(mux->pdata.regmap, mux->pdata.sel_reg_addr, chan);
+ mux->last_val = err < 0 ? -1 : chan;
+ }
+
+ return err;
+}
+
+static int i2c_mux_regmap_deselect(struct i2c_mux_core *muxc, u32 chan)
+{
+ struct i2c_mux_regmap *mux = i2c_mux_priv(muxc);
+
+ /* Deselect active channel */
+ mux->last_val = -1;
+
+ return regmap_write(mux->pdata.regmap, mux->pdata.sel_reg_addr, 0);
+}
+
+static void i2c_mux_regmap_notify_probe_abandon(struct platform_device *pdev,
+ struct i2c_mux_regmap *mux,
+ struct i2c_mux_core *muxc,
+ struct i2c_adapter *parent,
+ int err, bool user_notify_called)
+{
+ const struct i2c_mux_regmap_platform_data *pd;
+
+ if (user_notify_called || err == -EPROBE_DEFER)
+ return;
+ pd = mux ? &mux->pdata : dev_get_platdata(&pdev->dev);
+ if (!pd || !pd->completion_notify || !pd->handle)
+ return;
+ pd->completion_notify(pd->handle, muxc ? muxc->parent : parent, NULL);
+}
+
+/* Probe/remove functions */
+static int i2c_mux_regmap_probe(struct platform_device *pdev)
+{
+ struct i2c_mux_regmap_platform_data *pdata = dev_get_platdata(&pdev->dev);
+ struct i2c_mux_regmap *mux;
+ struct i2c_adapter *parent;
+ struct i2c_mux_core *muxc = NULL;
+ bool user_notify_called = false;
+ int num, ret;
+
+ if (!pdata)
+ return -EINVAL;
+
+ parent = i2c_get_adapter(pdata->parent);
+ if (!parent)
+ return -EPROBE_DEFER;
+
+ muxc = i2c_mux_alloc(parent, &pdev->dev, pdata->num_adaps, sizeof(*mux), 0,
+ i2c_mux_regmap_select_chan, i2c_mux_regmap_deselect);
+ if (!muxc) {
+ ret = -ENOMEM;
+ goto err_put_parent;
+ }
+
+ mux = i2c_mux_priv(muxc);
+ memcpy(&mux->pdata, pdata, sizeof(*pdata));
+ platform_set_drvdata(pdev, muxc);
+ mux->last_val = -1; /* force the first selection */
+
+ /* Create an adapter for each channel. */
+ for (num = 0; num < pdata->num_adaps; num++) {
+ ret = i2c_mux_add_adapter(muxc, 0, pdata->chan_ids[num]);
+ if (ret)
+ goto err_i2c_mux_add_adapter;
+ }
+
+ /* Notify caller when all channels' adapters are created. */
+ if (pdata->completion_notify) {
+ ret = pdata->completion_notify(pdata->handle, muxc->parent,
+ muxc->adapter);
+ user_notify_called = true;
+ if (ret)
+ goto err_i2c_mux_add_adapter;
+ }
+
+ return 0;
+
+err_i2c_mux_add_adapter:
+ i2c_mux_regmap_notify_probe_abandon(pdev, mux, muxc, parent, ret,
+ user_notify_called);
+ i2c_mux_del_adapters(muxc);
+ goto put_parent;
+err_put_parent:
+ i2c_mux_regmap_notify_probe_abandon(pdev, mux, muxc, parent, ret,
+ user_notify_called);
+put_parent:
+ i2c_put_adapter(parent);
+
+ return ret;
+}
+
+static void i2c_mux_regmap_remove(struct platform_device *pdev)
+{
+ struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
+
+ i2c_mux_del_adapters(muxc);
+ i2c_put_adapter(muxc->parent);
+}
+
+static struct platform_driver i2c_mux_regmap_driver = {
+ .driver = {
+ .name = "i2c-mux-regmap",
+ },
+ .probe = i2c_mux_regmap_probe,
+ .remove = i2c_mux_regmap_remove,
+};
+
+module_platform_driver(i2c_mux_regmap_driver);
+
+MODULE_AUTHOR("Vadim Pasternak (vadimp@nvidia.com)");
+MODULE_DESCRIPTION("Regmap I2C multiplexer driver");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_ALIAS("platform:i2c-mux-regmap");
diff --git a/include/linux/platform_data/i2c-mux-regmap.h b/include/linux/platform_data/i2c-mux-regmap.h
new file mode 100644
index 000000000000..0015d5a905d9
--- /dev/null
+++ b/include/linux/platform_data/i2c-mux-regmap.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/*
+ * Regmap i2c mux driver
+ *
+ * Copyright (C) 2026 Nvidia Technologies Ltd.
+ */
+
+#ifndef __LINUX_PLATFORM_DATA_I2C_MUX_REGMAP_H
+#define __LINUX_PLATFORM_DATA_I2C_MUX_REGMAP_H
+
+/**
+ * struct i2c_mux_regmap_platform_data - Platform-dependent data for i2c-mux-regmap
+ * @regmap: register map of parent device;
+ * @parent: Parent I2C bus adapter number
+ * @chan_ids: channels array
+ * @num_adaps: number of adapters
+ * @sel_reg_addr: mux select register offset in CPLD/FPGA space
+ * @handle: handle to be passed by callback
+ * @completion_notify: callback invoked after all child adapters are
+ * created. May also be invoked once with @adapters set to NULL if
+ * probe fails after parent adapter acquisition, so waiters can
+ * unblock.
+ * The adapters array is owned by mux core, remains valid until mux
+ * removal, and contains @num_adaps entries.
+ */
+struct i2c_mux_regmap_platform_data {
+ struct regmap *regmap;
+ int parent;
+ const unsigned int *chan_ids;
+ int num_adaps;
+ int sel_reg_addr;
+ void *handle;
+ int (*completion_notify)(void *handle, struct i2c_adapter *parent,
+ struct i2c_adapter *adapters[]);
+};
+
+#endif /* __LINUX_PLATFORM_DATA_I2C_MUX_REGMAP_H */
--
2.34.1
next reply other threads:[~2026-05-19 17:44 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-19 17:43 Vadim Pasternak [this message]
2026-05-20 8:32 ` [PATCH i2c-mux next 1/1] i2c: mux: Add register map based mux driver kernel test robot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260519174310.1128423-1-vadimp@nvidia.com \
--to=vadimp@nvidia.com \
--cc=linux-i2c@vger.kernel.org \
--cc=peda@lysator.liu.se \
--cc=wsa@the-dreams.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox