1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * Theobroma Systems Mule I2C device multiplexer
4 *
5 * Copyright (C) 2024 Theobroma Systems Design und Consulting GmbH
6 */
7
8 #include <linux/i2c-mux.h>
9 #include <linux/i2c.h>
10 #include <linux/module.h>
11 #include <linux/of.h>
12 #include <linux/platform_device.h>
13 #include <linux/property.h>
14 #include <linux/regmap.h>
15
16 #define MULE_I2C_MUX_CONFIG_REG 0xff
17 #define MULE_I2C_MUX_DEFAULT_DEV 0x0
18
19 struct mule_i2c_reg_mux {
20 struct regmap *regmap;
21 };
22
mule_i2c_mux_select(struct i2c_mux_core * muxc,u32 dev)23 static int mule_i2c_mux_select(struct i2c_mux_core *muxc, u32 dev)
24 {
25 struct mule_i2c_reg_mux *mux = muxc->priv;
26
27 return regmap_write(mux->regmap, MULE_I2C_MUX_CONFIG_REG, dev);
28 }
29
mule_i2c_mux_deselect(struct i2c_mux_core * muxc,u32 dev)30 static int mule_i2c_mux_deselect(struct i2c_mux_core *muxc, u32 dev)
31 {
32 return mule_i2c_mux_select(muxc, MULE_I2C_MUX_DEFAULT_DEV);
33 }
34
mule_i2c_mux_remove(void * data)35 static void mule_i2c_mux_remove(void *data)
36 {
37 struct i2c_mux_core *muxc = data;
38
39 i2c_mux_del_adapters(muxc);
40
41 mule_i2c_mux_deselect(muxc, MULE_I2C_MUX_DEFAULT_DEV);
42 }
43
mule_i2c_mux_probe(struct platform_device * pdev)44 static int mule_i2c_mux_probe(struct platform_device *pdev)
45 {
46 struct device *mux_dev = &pdev->dev;
47 struct mule_i2c_reg_mux *priv;
48 struct i2c_client *client;
49 struct i2c_mux_core *muxc;
50 struct device_node *dev;
51 unsigned int readback;
52 int ndev, ret;
53 bool old_fw;
54
55 /* Count devices on the mux */
56 ndev = of_get_child_count(mux_dev->of_node);
57 dev_dbg(mux_dev, "%d devices on the mux\n", ndev);
58
59 client = to_i2c_client(mux_dev->parent);
60
61 muxc = i2c_mux_alloc(client->adapter, mux_dev, ndev, sizeof(*priv),
62 I2C_MUX_LOCKED, mule_i2c_mux_select, mule_i2c_mux_deselect);
63 if (!muxc)
64 return -ENOMEM;
65
66 priv = i2c_mux_priv(muxc);
67
68 priv->regmap = dev_get_regmap(mux_dev->parent, NULL);
69 if (!priv->regmap)
70 return dev_err_probe(mux_dev, -ENODEV,
71 "No parent i2c register map\n");
72
73 platform_set_drvdata(pdev, muxc);
74
75 /*
76 * MULE_I2C_MUX_DEFAULT_DEV is guaranteed to exist on all old and new
77 * mule fw. Mule fw without mux support will accept write ops to the
78 * config register, but readback returns 0xff (register not updated).
79 */
80 ret = mule_i2c_mux_select(muxc, MULE_I2C_MUX_DEFAULT_DEV);
81 if (ret)
82 return dev_err_probe(mux_dev, ret,
83 "Failed to write config register\n");
84
85 ret = regmap_read(priv->regmap, MULE_I2C_MUX_CONFIG_REG, &readback);
86 if (ret)
87 return dev_err_probe(mux_dev, ret,
88 "Failed to read config register\n");
89
90 old_fw = (readback != MULE_I2C_MUX_DEFAULT_DEV);
91
92 ret = devm_add_action_or_reset(mux_dev, mule_i2c_mux_remove, muxc);
93 if (ret)
94 return dev_err_probe(mux_dev, ret,
95 "Failed to register mux remove\n");
96
97 /* Create device adapters */
98 for_each_child_of_node(mux_dev->of_node, dev) {
99 u32 reg;
100
101 ret = of_property_read_u32(dev, "reg", ®);
102 if (ret)
103 return dev_err_probe(mux_dev, ret,
104 "No reg property found for %s\n",
105 of_node_full_name(dev));
106
107 if (old_fw && reg != 0) {
108 dev_warn(mux_dev,
109 "Mux is not supported, please update Mule FW\n");
110 continue;
111 }
112
113 ret = mule_i2c_mux_select(muxc, reg);
114 if (ret) {
115 dev_warn(mux_dev,
116 "Device %d not supported, please update Mule FW\n", reg);
117 continue;
118 }
119
120 ret = i2c_mux_add_adapter(muxc, 0, reg);
121 if (ret)
122 return ret;
123 }
124
125 mule_i2c_mux_deselect(muxc, MULE_I2C_MUX_DEFAULT_DEV);
126
127 return 0;
128 }
129
130 static const struct of_device_id mule_i2c_mux_of_match[] = {
131 { .compatible = "tsd,mule-i2c-mux", },
132 {},
133 };
134 MODULE_DEVICE_TABLE(of, mule_i2c_mux_of_match);
135
136 static struct platform_driver mule_i2c_mux_driver = {
137 .driver = {
138 .name = "mule-i2c-mux",
139 .of_match_table = mule_i2c_mux_of_match,
140 },
141 .probe = mule_i2c_mux_probe,
142 };
143
144 module_platform_driver(mule_i2c_mux_driver);
145
146 MODULE_AUTHOR("Farouk Bouabid <farouk.bouabid@cherry.de>");
147 MODULE_DESCRIPTION("I2C mux driver for Theobroma Systems Mule");
148 MODULE_LICENSE("GPL");
149