1

i2c: muxes: add support for tsd,mule-i2c multiplexer

Theobroma Systems Mule is an MCU that emulates a set of I2C devices,
among which an amc6821 and devices that are reachable through an I2C-mux.
The devices on the mux can be selected by writing the appropriate device
number to an I2C config register (amc6821 reg 0xff).

This driver is expected to be probed as a platform device with amc6821
as its parent i2c device.

Add support for the mule-i2c-mux platform driver. The amc6821 driver
support for the mux will be added in a later commit.

Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Signed-off-by: Farouk Bouabid <farouk.bouabid@cherry.de>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
This commit is contained in:
Farouk Bouabid 2024-09-06 17:54:13 +02:00 committed by Andi Shyti
parent 4a875cf154
commit d0f8e97866
3 changed files with 165 additions and 0 deletions

View File

@ -119,4 +119,20 @@ config I2C_MUX_MLXCPLD
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called i2c-mux-mlxcpld. will be called i2c-mux-mlxcpld.
config I2C_MUX_MULE
tristate "Theobroma Systems Mule I2C device multiplexer"
depends on OF && SENSORS_AMC6821
help
Mule is an MCU that emulates a set of I2C devices, among which
devices that are reachable through an I2C-mux. The devices on the mux
can be selected by writing the appropriate device number to an I2C
configuration register.
If you say yes to this option, support will be included for a
Theobroma Systems Mule I2C multiplexer. This driver provides access to
I2C devices connected on this mux.
This driver can also be built as a module. If so, the module
will be called i2c-mux-mule.
endmenu endmenu

View File

@ -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_GPMUX) += i2c-mux-gpmux.o
obj-$(CONFIG_I2C_MUX_LTC4306) += i2c-mux-ltc4306.o obj-$(CONFIG_I2C_MUX_LTC4306) += i2c-mux-ltc4306.o
obj-$(CONFIG_I2C_MUX_MLXCPLD) += i2c-mux-mlxcpld.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_PCA9541) += i2c-mux-pca9541.o
obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o obj-$(CONFIG_I2C_MUX_PCA954x) += i2c-mux-pca954x.o
obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o obj-$(CONFIG_I2C_MUX_PINCTRL) += i2c-mux-pinctrl.o

View File

@ -0,0 +1,148 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Theobroma Systems 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/platform_device.h>
#include <linux/property.h>
#include <linux/regmap.h>
#define MULE_I2C_MUX_CONFIG_REG 0xff
#define MULE_I2C_MUX_DEFAULT_DEV 0x0
struct mule_i2c_reg_mux {
struct regmap *regmap;
};
static int mule_i2c_mux_select(struct i2c_mux_core *muxc, u32 dev)
{
struct mule_i2c_reg_mux *mux = muxc->priv;
return regmap_write(mux->regmap, MULE_I2C_MUX_CONFIG_REG, dev);
}
static int mule_i2c_mux_deselect(struct i2c_mux_core *muxc, u32 dev)
{
return mule_i2c_mux_select(muxc, MULE_I2C_MUX_DEFAULT_DEV);
}
static void mule_i2c_mux_remove(void *data)
{
struct i2c_mux_core *muxc = data;
i2c_mux_del_adapters(muxc);
mule_i2c_mux_deselect(muxc, MULE_I2C_MUX_DEFAULT_DEV);
}
static int mule_i2c_mux_probe(struct platform_device *pdev)
{
struct device *mux_dev = &pdev->dev;
struct mule_i2c_reg_mux *priv;
struct i2c_client *client;
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(mux_dev->of_node);
dev_dbg(mux_dev, "%d devices on the mux\n", ndev);
client = to_i2c_client(mux_dev->parent);
muxc = i2c_mux_alloc(client->adapter, mux_dev, ndev, sizeof(*priv),
I2C_MUX_LOCKED, mule_i2c_mux_select, mule_i2c_mux_deselect);
if (!muxc)
return -ENOMEM;
priv = i2c_mux_priv(muxc);
priv->regmap = dev_get_regmap(mux_dev->parent, NULL);
if (IS_ERR(priv->regmap))
return dev_err_probe(mux_dev, PTR_ERR(priv->regmap),
"No parent i2c register map\n");
platform_set_drvdata(pdev, muxc);
/*
* MULE_I2C_MUX_DEFAULT_DEV 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 = mule_i2c_mux_select(muxc, MULE_I2C_MUX_DEFAULT_DEV);
if (ret)
return dev_err_probe(mux_dev, ret,
"Failed to write config register\n");
ret = regmap_read(priv->regmap, MULE_I2C_MUX_CONFIG_REG, &readback);
if (ret)
return dev_err_probe(mux_dev, ret,
"Failed to read config register\n");
old_fw = (readback != MULE_I2C_MUX_DEFAULT_DEV);
ret = devm_add_action_or_reset(mux_dev, mule_i2c_mux_remove, muxc);
if (ret)
return dev_err_probe(mux_dev, ret,
"Failed to register mux remove\n");
/* Create device adapters */
for_each_child_of_node(mux_dev->of_node, dev) {
u32 reg;
ret = of_property_read_u32(dev, "reg", &reg);
if (ret)
return dev_err_probe(mux_dev, ret,
"No reg property found for %s\n",
of_node_full_name(dev));
if (old_fw && reg != 0) {
dev_warn(mux_dev,
"Mux is not supported, please update Mule FW\n");
continue;
}
ret = mule_i2c_mux_select(muxc, reg);
if (ret) {
dev_warn(mux_dev,
"Device %d not supported, please update Mule FW\n", reg);
continue;
}
ret = i2c_mux_add_adapter(muxc, 0, reg);
if (ret)
return ret;
}
mule_i2c_mux_deselect(muxc, MULE_I2C_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 platform_driver mule_i2c_mux_driver = {
.driver = {
.name = "mule-i2c-mux",
.of_match_table = mule_i2c_mux_of_match,
},
.probe = mule_i2c_mux_probe,
};
module_platform_driver(mule_i2c_mux_driver);
MODULE_AUTHOR("Farouk Bouabid <farouk.bouabid@cherry.de>");
MODULE_DESCRIPTION("I2C mux driver for Theobroma Systems Mule");
MODULE_LICENSE("GPL");