new file mode 100644
@@ -0,0 +1,168 @@
+.. SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+============================
+Kernel driver i2c-mux-regmap
+============================
+
+Author: Vadim Pasternak <vadimp@nvidia.com>
+
+Description
+-----------
+
+i2c-mux-regmap is an i2c mux driver providing access to I2C bus segments
+from a master I2C bus and a hardware MUX controlled through FPGA device
+with indirect access to register space.
+
+For example, Lattice FPGA LFD2NX-40 device, being connected through PCIe
+bus provides SPI or LPC logic through PCIe-to-SPI or PCIe-to-LPC
+bridging.
+Thus, FPGA operates as host controller and some slave devices can be
+connected to it. For example:
+- CPU (PCIe) -> FPGA (PCIe-to-SPI bridge) -> CPLD or another FPGA
+- CPU (PCIe) -> FPGA (PCIe-to-LPC bridge) -> CPLD or another FPGA
+where 1-st FPGA connected to PCIe is located on carrier board, while 2-nd
+programming logic device is located on some switch board and cannot be
+connected to CPU PCIe root complex.
+
+E.G.::
+ ------------------------ ---------------------------------------
+| COME board | | Switch board |
+| | | |
+| ----- ------ | | ------- Bus channel 1 |
+| | | | | | | | | *--------------> |
+| | CPU | | FPGA |------->| CPLD | | |
+| | | PCIe | | LPC | --- | | Bus channel 2 |
+| | |------| | | | | |MUX|--->*--------------> Devices |
+| | | | | | | | |REG| | | |
+| | | | | | | | --- | | Bus channel n |
+| | | | | | | | | *--------------> |
+| ----- ------ | | ------- |
+| | | |
+ ------------------------ ---------------------------------------
+
+SCL/SDA of the master I2C bus is multiplexed to bus segment 1..n
+according to the settings of the MUX REG or REGS.
+
+Access to MUX selector registers is performed through the 'regmap' object,
+which provides read and write methods, implementing protocol for indirect
+access.
+
+Usage
+-----
+
+i2c-mux-regmap uses the platform bus, so it is necessary to provide a struct
+platform_device with the platform_data pointing to a struct
+i2c_mux_regmap_platform_data with:
+- The I2C adapter number of the master bus.
+- Channels array and the number of bus channels to create.
+- MUX select register offset in programming logic device space for bus
+ selection/deselection control.
+- Optional callback to notify caller when all the adapters are created and
+ handle to be passed to callback.
+See include/linux/platform_data/i2c-mux-regmap.h for details.
+
+Device Registration example
+---------------------------
+
+ /* Channels vector for regmap mux. */
+ static int regmap_mux_chan[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
+
+ /* Platform regmap mux data */
+ static struct i2c_mux_regmap_platform_data regmap_mux_data[] = {
+ {
+ .parent = 1,
+ .chan_ids = regmap_mux_chan,
+ .num_adaps = ARRAY_SIZE(regmap_mux_chan),
+ .sel_reg_addr = 0xdb,
+ },
+ {
+ .parent = 1,
+ .chan_ids = regmap_mux_chan,
+ .num_adaps = ARRAY_SIZE(regmap_mux_chan),
+ .sel_reg_addr = 0xda,
+ },
+ };
+
+ Create regmap object.
+
+ struct caller_regmap_context {
+ void __iomem *base;
+ };
+
+ /* Read callback for indirect register map access */
+ static int fpga_reg_read(void *context, unsigned int reg, unsigned int *val)
+ {
+ /* Verify there are no pending transactions */
+ /* Set address in register space */
+ /* Activate read operation */
+ /* Verify transaction completion */
+ /* Read data */
+ }
+
+ /* Write callback for indirect register map access */
+ static int reg_write(void *context, unsigned int reg, unsigned int val)
+ {
+ /* Verify there are no pending transactions */
+ /* Set address in register space */
+ /* Set data to be written */
+ /* Activate write operation */
+ /* Verify transaction completion */
+ }
+
+ static struct caller_regmap_context caller_regmap_ctx;
+
+ static const struct regmap_config fpga_regmap_config = {
+ .reg_bits = 9,
+ .val_bits = 8,
+ .max_register = 511,
+ .cache_type = REGCACHE_FLAT,
+ .writeable_reg = caller_writeable_reg,
+ .readable_reg = caller_readable_reg,
+ .volatile_reg = caller_volatile_reg,
+ .reg_defaults = caller_regmap_default,
+ .num_reg_defaults = ARRAY_SIZE(caller_regmap_default),
+ /* Methods implementing protocol to access PCI-LPC bridge. */
+ .reg_read = fpga_reg_read,
+ .reg_write = fpga_reg_write,
+ };
+
+ regmap = devm_regmap_init(&dev, NULL, &caller_regmap_ctx,
+ fpga_regmap_config);
+
+ Remap FPGA base address.
+
+ caller_regmap_ctx.base = devm_ioremap(&fpga_pci_dev->dev,
+ pci_resource_start(pci_dev, 0),
+ pci_resource_len(pci_dev, 0));
+
+ For each entry in 'regmap_mux_data' array.
+
+ mux_regmap_data[i].handle = caller_handle;
+ mux_regmap_data[i].regmap = regmap;
+ mux_regmap_data[i].completion_notify = caller_complition_notify;
+
+ pdev[i] =
+ platform_device_register_resndata(dev, "i2c-mux-regmap", i, NULL, 0,
+ ®map_mux_data[i],
+ sizeof(regmap_mux_data[i]));
+
+ In the above examples two instances of "i2c-mux-regmap" will be created.
+ For each the array of the created adapter will be passed to
+ caller_complition_notify(), if this callback was provided.
+
+SYSFS example
+=============
+ In 'sysfs' the channels will be exposed through path:
+ /sys/devices/platform/<caller-driver>/<i2c-parent-driver.parent-bus>
+ Following the above example it will contain:
+ i2c-mux-regmap.0/channel-1
+ ...
+ i2c-mux-regmap.0/channel-8
+ i2c-mux-regmap.1/channel-1
+ ...
+ i2c-mux-regmap.1/channel-8
+
+ However, MUX number of each 'i2c-mux-regmap' instance is limited by
+ the size of selector register.
+ Thus, if its size is 1 byte - up to 255 MUX channels can be created,
+ for 2 bytes respectively up to 65535.
+
@@ -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
@@ -14,5 +14,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
new file mode 100644
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
+/*
+ * Regmap i2c mux driver
+ *
+ * Copyright (C) 2023 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;
+ * @parent - parent I2C adapter;
+ * @pdata: platform data;
+ */
+struct i2c_mux_regmap {
+ int last_val;
+ struct i2c_adapter *parent;
+ 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 it's 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);
+}
+
+/* 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_mux_core *muxc;
+ int num, err;
+
+ if (!pdata)
+ return -EINVAL;
+
+ mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL);
+ if (!mux)
+ return -ENOMEM;
+
+ memcpy(&mux->pdata, pdata, sizeof(*pdata));
+ mux->parent = i2c_get_adapter(mux->pdata.parent);
+ if (!mux->parent)
+ return -EPROBE_DEFER;
+
+ muxc = i2c_mux_alloc(mux->parent, &pdev->dev, pdata->num_adaps, sizeof(*mux), 0,
+ i2c_mux_regmap_select_chan, i2c_mux_regmap_deselect);
+ if (!muxc) {
+ err = -ENOMEM;
+ goto err_i2c_mux_alloc;
+ }
+
+ platform_set_drvdata(pdev, muxc);
+ muxc->priv = mux;
+ mux->last_val = -1; /* force the first selection */
+
+ /* Create an adapter for each channel. */
+ for (num = 0; num < pdata->num_adaps; num++) {
+ err = i2c_mux_add_adapter(muxc, 0, pdata->chan_ids[num], 0);
+ if (err)
+ goto err_i2c_mux_add_adapter;
+ }
+
+ /* Notify caller when all channels' adapters are created. */
+ if (pdata->completion_notify)
+ pdata->completion_notify(pdata->handle, muxc->parent, muxc->adapter);
+
+ return 0;
+
+err_i2c_mux_add_adapter:
+ i2c_mux_del_adapters(muxc);
+err_i2c_mux_alloc:
+ i2c_put_adapter(mux->parent);
+ return err;
+}
+
+static void i2c_mux_regmap_remove(struct platform_device *pdev)
+{
+ struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
+ struct i2c_mux_regmap *mux = muxc->priv;
+
+ i2c_mux_del_adapters(muxc);
+ i2c_put_adapter(mux->parent);
+}
+
+static struct platform_driver i2c_mux_regmap_driver = {
+ .driver = {
+ .name = "i2c-mux-regmap",
+ },
+ .probe = i2c_mux_regmap_probe,
+ .remove_new = 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");
new file mode 100644
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 */
+/*
+ * Regmap i2c mux driver
+ *
+ * Copyright (C) 2023 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 space
+ * @handle: handle to be passed by callback
+ * @completion_notify: callback to notify when all the adapters are created
+ */
+struct i2c_mux_regmap_platform_data {
+ void *regmap;
+ int parent;
+ const unsigned int *chan_ids;
+ unsigned int num_adaps;
+ unsigned 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 */