Message ID | 20240611-dev-mule-i2c-mux-v3-3-08d26a28e001@cherry.de (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Add Mule I2C multiplexer support | expand |
Hi all, On 11.06.24 13:43, Farouk Bouabid wrote: > Mule is an MCU that emulates a set of I2C devices which are reachable > through an I2C-mux. > > The emulated devices share a single I2C address with the mux itself > where the requested register is what determines which logic is executed > (mux logic or device logic): > > 1- The devices on the mux can be selected (mux function) by writing the > appropriate device number to an I2C config register (0xff) that is not > used by any device logic. > > 2- Any access to a register other than the config register will be > handled by the previously selected device. > > Signed-off-by: Farouk Bouabid <farouk.bouabid@cherry.de> > --- > drivers/i2c/muxes/Kconfig | 18 +++++ > drivers/i2c/muxes/Makefile | 1 + > drivers/i2c/muxes/i2c-mux-mule.c | 157 +++++++++++++++++++++++++++++++++++++++ > 3 files changed, 176 insertions(+) > > diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig > index db1b9057612a..70876259796d 100644 > --- a/drivers/i2c/muxes/Kconfig > +++ b/drivers/i2c/muxes/Kconfig > @@ -119,4 +119,22 @@ config I2C_MUX_MLXCPLD > This driver can also be built as a module. If so, the module > will be called i2c-mux-mlxcpld. > > +config I2C_MUX_MULE > + tristate "Mule I2C device multiplexer" > + depends on OF > + help > + Mule is an MCU that emulates a set of I2C devices which are reachable > + through an I2C-mux. > + > + The emulated devices share a single I2C address with the mux itself > + where the requested register is what determines which logic is executed > + (mux logic or device logic). > + > + If you say yes to this option, support will be included for a > + Mule I2C device multiplexer. This driver provides access to > + I2C devices connected on the Mule I2C mux. > + > + This driver can also be built as a module. If so, the module > + will be called i2c-mux-mule. > + > endmenu > diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile > index 6d9d865e8518..4b24f49515a7 100644 > --- a/drivers/i2c/muxes/Makefile > +++ b/drivers/i2c/muxes/Makefile > @@ -10,6 +10,7 @@ obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o > obj-$(CONFIG_I2C_MUX_GPMUX) += i2c-mux-gpmux.o > obj-$(CONFIG_I2C_MUX_LTC4306) += i2c-mux-ltc4306.o > obj-$(CONFIG_I2C_MUX_MLXCPLD) += i2c-mux-mlxcpld.o > +obj-$(CONFIG_I2C_MUX_MULE) += i2c-mux-mule.o > 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 > diff --git a/drivers/i2c/muxes/i2c-mux-mule.c b/drivers/i2c/muxes/i2c-mux-mule.c > new file mode 100644 > index 000000000000..db27fa560995 > --- /dev/null > +++ b/drivers/i2c/muxes/i2c-mux-mule.c > @@ -0,0 +1,157 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Mule I2C device multiplexer > + * > + * Copyright (C) 2024 Theobroma Systems Design und Consulting GmbH > + */ > + > +#include <linux/i2c-mux.h> > +#include <linux/i2c.h> > +#include <linux/module.h> > +#include <linux/of.h> > +#include <linux/property.h> > +#include <linux/regmap.h> > + > +#define MUX_CONFIG_REG 0xff > +#define MUX_DEFAULT_DEV 0x0 > + > +struct mule_i2c_reg_mux { > + struct regmap *regmap; > +}; > + > +static const struct regmap_config mule_regmap_config = { > + .reg_bits = 8, > + .val_bits = 8, > +}; > + > +static inline int __mux_select(struct regmap *regmap, u32 dev) > +{ > + return regmap_write(regmap, MUX_CONFIG_REG, dev); > +} > + > +static int mux_select(struct i2c_mux_core *muxc, u32 dev) > +{ > + struct mule_i2c_reg_mux *mux = muxc->priv; > + > + return __mux_select(mux->regmap, dev); > +} > + > +static int mux_deselect(struct i2c_mux_core *muxc, u32 dev) > +{ > + return mux_select(muxc, MUX_DEFAULT_DEV); > +} > + > +static void mux_remove(void *data) > +{ > + struct i2c_mux_core *muxc = data; > + > + i2c_mux_del_adapters(muxc); > + > + mux_deselect(muxc, MUX_DEFAULT_DEV); > +} > + > +static int mule_i2c_mux_probe(struct i2c_client *client) > +{ > + struct i2c_adapter *adap = client->adapter; > + struct mule_i2c_reg_mux *priv; > + struct i2c_mux_core *muxc; > + struct device_node *dev; > + unsigned int readback; > + int ndev, ret; > + bool old_fw; > + > + /* Count devices on the mux */ > + ndev = of_get_child_count(client->dev.of_node); > + dev_dbg(&client->dev, "%u devices on the mux\n", ndev); > + > + muxc = i2c_mux_alloc(adap, &client->dev, > + ndev, sizeof(*priv), > + I2C_MUX_LOCKED, > + mux_select, mux_deselect); > + if (!muxc) > + return -ENOMEM; > + > + muxc->share_addr_with_children = 1; > + priv = i2c_mux_priv(muxc); > + > + priv->regmap = devm_regmap_init_i2c(client, &mule_regmap_config); > + if (IS_ERR(priv->regmap)) > + return dev_err_probe(&client->dev, PTR_ERR(priv->regmap), > + "Failed to allocate i2c register map\n"); > + > + i2c_set_clientdata(client, muxc); > + > + /* > + * Mux 0 is guaranteed to exist on all old and new mule fw. > + * mule fw without mux support will accept write ops to the > + * config register, but readback returns 0xff (register not updated). > + */ > + ret = mux_select(muxc, 0); > + if (ret) > + return ret; > + > + ret = regmap_read(priv->regmap, MUX_CONFIG_REG, &readback); > + if (ret) > + return ret; > + > + old_fw = (readback != 0); > + > + ret = devm_add_action_or_reset(&client->dev, mux_remove, muxc); > + if (ret) > + return ret; > + > + /* Create device adapters */ > + for_each_child_of_node(client->dev.of_node, dev) { > + u32 reg; > + > + ret = of_property_read_u32(dev, "reg", ®); > + if (ret) { > + dev_err(&client->dev, "No reg property found for %s: %d\n", > + of_node_full_name(dev), ret); > + return ret; > + } > + > + if (old_fw && reg != 0) { > + dev_warn(&client->dev, > + "Mux %d not supported, please update Mule FW\n", reg); > + continue; > + } > + > + ret = mux_select(muxc, reg); > + if (ret) { > + dev_warn(&client->dev, > + "Mux %d not supported, please update Mule FW\n", reg); > + continue; > + } > + > + ret = i2c_mux_add_adapter(muxc, 0, reg, 0); The class parameter was recently removed from this function but I forgot to remove it. Sorry for the noise, I will send a v4 for this fix. Cheers, Farouk
Le 11/06/2024 à 13:43, Farouk Bouabid a écrit : > Mule is an MCU that emulates a set of I2C devices which are reachable > through an I2C-mux. > > The emulated devices share a single I2C address with the mux itself > where the requested register is what determines which logic is executed > (mux logic or device logic): > > 1- The devices on the mux can be selected (mux function) by writing the > appropriate device number to an I2C config register (0xff) that is not > used by any device logic. > > 2- Any access to a register other than the config register will be > handled by the previously selected device. > > Signed-off-by: Farouk Bouabid <farouk.bouabid@cherry.de> > --- Hi, ... > +static int mule_i2c_mux_probe(struct i2c_client *client) > +{ > + struct i2c_adapter *adap = client->adapter; > + struct mule_i2c_reg_mux *priv; > + struct i2c_mux_core *muxc; > + struct device_node *dev; > + unsigned int readback; > + int ndev, ret; > + bool old_fw; > + > + /* Count devices on the mux */ > + ndev = of_get_child_count(client->dev.of_node); > + dev_dbg(&client->dev, "%u devices on the mux\n", ndev); Should this be %d? > + > + muxc = i2c_mux_alloc(adap, &client->dev, > + ndev, sizeof(*priv), > + I2C_MUX_LOCKED, > + mux_select, mux_deselect); Nitpick: this could be written with less lines. > + if (!muxc) > + return -ENOMEM; > + > + muxc->share_addr_with_children = 1; > + priv = i2c_mux_priv(muxc); > + > + priv->regmap = devm_regmap_init_i2c(client, &mule_regmap_config); > + if (IS_ERR(priv->regmap)) > + return dev_err_probe(&client->dev, PTR_ERR(priv->regmap), > + "Failed to allocate i2c register map\n"); > + > + i2c_set_clientdata(client, muxc); > + > + /* > + * Mux 0 is guaranteed to exist on all old and new mule fw. > + * mule fw without mux support will accept write ops to the > + * config register, but readback returns 0xff (register not updated). > + */ > + ret = mux_select(muxc, 0); Should this 0 be MUX_DEFAULT_DEV (for consistency)? > + if (ret) > + return ret; > + > + ret = regmap_read(priv->regmap, MUX_CONFIG_REG, &readback); > + if (ret) > + return ret; > + > + old_fw = (readback != 0); > + > + ret = devm_add_action_or_reset(&client->dev, mux_remove, muxc); > + if (ret) > + return ret; > + > + /* Create device adapters */ > + for_each_child_of_node(client->dev.of_node, dev) { > + u32 reg; > + > + ret = of_property_read_u32(dev, "reg", ®); > + if (ret) { > + dev_err(&client->dev, "No reg property found for %s: %d\n", > + of_node_full_name(dev), ret); return dev_err_probe() as above? > + return ret; > + } > + > + if (old_fw && reg != 0) { > + dev_warn(&client->dev, > + "Mux %d not supported, please update Mule FW\n", reg); > + continue; > + } > + > + ret = mux_select(muxc, reg); > + if (ret) { > + dev_warn(&client->dev, > + "Mux %d not supported, please update Mule FW\n", reg); > + continue; > + } > + > + ret = i2c_mux_add_adapter(muxc, 0, reg, 0); > + if (ret) { > + dev_err(&client->dev, "Failed to add i2c mux adapter %d: %d\n", reg, ret); return dev_err_probe() as above? > + return ret; > + } > + } > + > + mux_deselect(muxc, MUX_DEFAULT_DEV); > + > + return 0; > +} ...
> The class parameter was recently removed from this function but I forgot to > remove it. Sorry for the noise, I will send a v4 for this fix. Please wait for my comments to the series. Hopefully later this evening.
diff --git a/drivers/i2c/muxes/Kconfig b/drivers/i2c/muxes/Kconfig index db1b9057612a..70876259796d 100644 --- a/drivers/i2c/muxes/Kconfig +++ b/drivers/i2c/muxes/Kconfig @@ -119,4 +119,22 @@ config I2C_MUX_MLXCPLD This driver can also be built as a module. If so, the module will be called i2c-mux-mlxcpld. +config I2C_MUX_MULE + tristate "Mule I2C device multiplexer" + depends on OF + help + Mule is an MCU that emulates a set of I2C devices which are reachable + through an I2C-mux. + + The emulated devices share a single I2C address with the mux itself + where the requested register is what determines which logic is executed + (mux logic or device logic). + + If you say yes to this option, support will be included for a + Mule I2C device multiplexer. This driver provides access to + I2C devices connected on the Mule I2C mux. + + This driver can also be built as a module. If so, the module + will be called i2c-mux-mule. + endmenu diff --git a/drivers/i2c/muxes/Makefile b/drivers/i2c/muxes/Makefile index 6d9d865e8518..4b24f49515a7 100644 --- a/drivers/i2c/muxes/Makefile +++ b/drivers/i2c/muxes/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_I2C_MUX_GPIO) += i2c-mux-gpio.o obj-$(CONFIG_I2C_MUX_GPMUX) += i2c-mux-gpmux.o obj-$(CONFIG_I2C_MUX_LTC4306) += i2c-mux-ltc4306.o obj-$(CONFIG_I2C_MUX_MLXCPLD) += i2c-mux-mlxcpld.o +obj-$(CONFIG_I2C_MUX_MULE) += i2c-mux-mule.o 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 diff --git a/drivers/i2c/muxes/i2c-mux-mule.c b/drivers/i2c/muxes/i2c-mux-mule.c new file mode 100644 index 000000000000..db27fa560995 --- /dev/null +++ b/drivers/i2c/muxes/i2c-mux-mule.c @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Mule I2C device multiplexer + * + * Copyright (C) 2024 Theobroma Systems Design und Consulting GmbH + */ + +#include <linux/i2c-mux.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/property.h> +#include <linux/regmap.h> + +#define MUX_CONFIG_REG 0xff +#define MUX_DEFAULT_DEV 0x0 + +struct mule_i2c_reg_mux { + struct regmap *regmap; +}; + +static const struct regmap_config mule_regmap_config = { + .reg_bits = 8, + .val_bits = 8, +}; + +static inline int __mux_select(struct regmap *regmap, u32 dev) +{ + return regmap_write(regmap, MUX_CONFIG_REG, dev); +} + +static int mux_select(struct i2c_mux_core *muxc, u32 dev) +{ + struct mule_i2c_reg_mux *mux = muxc->priv; + + return __mux_select(mux->regmap, dev); +} + +static int mux_deselect(struct i2c_mux_core *muxc, u32 dev) +{ + return mux_select(muxc, MUX_DEFAULT_DEV); +} + +static void mux_remove(void *data) +{ + struct i2c_mux_core *muxc = data; + + i2c_mux_del_adapters(muxc); + + mux_deselect(muxc, MUX_DEFAULT_DEV); +} + +static int mule_i2c_mux_probe(struct i2c_client *client) +{ + struct i2c_adapter *adap = client->adapter; + struct mule_i2c_reg_mux *priv; + struct i2c_mux_core *muxc; + struct device_node *dev; + unsigned int readback; + int ndev, ret; + bool old_fw; + + /* Count devices on the mux */ + ndev = of_get_child_count(client->dev.of_node); + dev_dbg(&client->dev, "%u devices on the mux\n", ndev); + + muxc = i2c_mux_alloc(adap, &client->dev, + ndev, sizeof(*priv), + I2C_MUX_LOCKED, + mux_select, mux_deselect); + if (!muxc) + return -ENOMEM; + + muxc->share_addr_with_children = 1; + priv = i2c_mux_priv(muxc); + + priv->regmap = devm_regmap_init_i2c(client, &mule_regmap_config); + if (IS_ERR(priv->regmap)) + return dev_err_probe(&client->dev, PTR_ERR(priv->regmap), + "Failed to allocate i2c register map\n"); + + i2c_set_clientdata(client, muxc); + + /* + * Mux 0 is guaranteed to exist on all old and new mule fw. + * mule fw without mux support will accept write ops to the + * config register, but readback returns 0xff (register not updated). + */ + ret = mux_select(muxc, 0); + if (ret) + return ret; + + ret = regmap_read(priv->regmap, MUX_CONFIG_REG, &readback); + if (ret) + return ret; + + old_fw = (readback != 0); + + ret = devm_add_action_or_reset(&client->dev, mux_remove, muxc); + if (ret) + return ret; + + /* Create device adapters */ + for_each_child_of_node(client->dev.of_node, dev) { + u32 reg; + + ret = of_property_read_u32(dev, "reg", ®); + if (ret) { + dev_err(&client->dev, "No reg property found for %s: %d\n", + of_node_full_name(dev), ret); + return ret; + } + + if (old_fw && reg != 0) { + dev_warn(&client->dev, + "Mux %d not supported, please update Mule FW\n", reg); + continue; + } + + ret = mux_select(muxc, reg); + if (ret) { + dev_warn(&client->dev, + "Mux %d not supported, please update Mule FW\n", reg); + continue; + } + + ret = i2c_mux_add_adapter(muxc, 0, reg, 0); + if (ret) { + dev_err(&client->dev, "Failed to add i2c mux adapter %d: %d\n", reg, ret); + return ret; + } + } + + mux_deselect(muxc, MUX_DEFAULT_DEV); + + return 0; +} + +static const struct of_device_id mule_i2c_mux_of_match[] = { + {.compatible = "tsd,mule-i2c-mux",}, + {}, +}; +MODULE_DEVICE_TABLE(of, mule_i2c_mux_of_match); + +static struct i2c_driver mule_i2c_mux_driver = { + .driver = { + .name = "mule-i2c-mux", + .of_match_table = mule_i2c_mux_of_match, + }, + .probe = mule_i2c_mux_probe, +}; + +module_i2c_driver(mule_i2c_mux_driver); + +MODULE_AUTHOR("Farouk Bouabid <farouk.bouabid@cherry.de>"); +MODULE_DESCRIPTION("I2C mux driver for Mule"); +MODULE_LICENSE("GPL");
Mule is an MCU that emulates a set of I2C devices which are reachable through an I2C-mux. The emulated devices share a single I2C address with the mux itself where the requested register is what determines which logic is executed (mux logic or device logic): 1- The devices on the mux can be selected (mux function) by writing the appropriate device number to an I2C config register (0xff) that is not used by any device logic. 2- Any access to a register other than the config register will be handled by the previously selected device. Signed-off-by: Farouk Bouabid <farouk.bouabid@cherry.de> --- drivers/i2c/muxes/Kconfig | 18 +++++ drivers/i2c/muxes/Makefile | 1 + drivers/i2c/muxes/i2c-mux-mule.c | 157 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 176 insertions(+)