xref: /linux/drivers/i2c/muxes/i2c-mux-mule.c (revision a032fe30cf09b6723ab61a05aee057311b00f9e1)
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 
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 
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 
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 
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 	unsigned int readback;
51 	int ndev, ret;
52 	bool old_fw;
53 
54 	/* Count devices on the mux */
55 	ndev = of_get_child_count(mux_dev->of_node);
56 	dev_dbg(mux_dev, "%d devices on the mux\n", ndev);
57 
58 	client = to_i2c_client(mux_dev->parent);
59 
60 	muxc = i2c_mux_alloc(client->adapter, mux_dev, ndev, sizeof(*priv),
61 			     I2C_MUX_LOCKED, mule_i2c_mux_select, mule_i2c_mux_deselect);
62 	if (!muxc)
63 		return -ENOMEM;
64 
65 	priv = i2c_mux_priv(muxc);
66 
67 	priv->regmap = dev_get_regmap(mux_dev->parent, NULL);
68 	if (!priv->regmap)
69 		return dev_err_probe(mux_dev, -ENODEV,
70 				     "No parent i2c register map\n");
71 
72 	platform_set_drvdata(pdev, muxc);
73 
74 	/*
75 	 * MULE_I2C_MUX_DEFAULT_DEV is guaranteed to exist on all old and new
76 	 * mule fw. Mule fw without mux support will accept write ops to the
77 	 * config register, but readback returns 0xff (register not updated).
78 	 */
79 	ret = mule_i2c_mux_select(muxc, MULE_I2C_MUX_DEFAULT_DEV);
80 	if (ret)
81 		return dev_err_probe(mux_dev, ret,
82 				     "Failed to write config register\n");
83 
84 	ret = regmap_read(priv->regmap, MULE_I2C_MUX_CONFIG_REG, &readback);
85 	if (ret)
86 		return dev_err_probe(mux_dev, ret,
87 				     "Failed to read config register\n");
88 
89 	old_fw = (readback != MULE_I2C_MUX_DEFAULT_DEV);
90 
91 	ret = devm_add_action_or_reset(mux_dev, mule_i2c_mux_remove, muxc);
92 	if (ret)
93 		return dev_err_probe(mux_dev, ret,
94 				     "Failed to register mux remove\n");
95 
96 	/* Create device adapters */
97 	for_each_child_of_node_scoped(mux_dev->of_node, dev) {
98 		u32 reg;
99 
100 		ret = of_property_read_u32(dev, "reg", &reg);
101 		if (ret)
102 			return dev_err_probe(mux_dev, ret,
103 					     "No reg property found for %s\n",
104 					     of_node_full_name(dev));
105 
106 		if (old_fw && reg != 0) {
107 			dev_warn(mux_dev,
108 				 "Mux is not supported, please update Mule FW\n");
109 			continue;
110 		}
111 
112 		ret = mule_i2c_mux_select(muxc, reg);
113 		if (ret) {
114 			dev_warn(mux_dev,
115 				 "Device %d not supported, please update Mule FW\n", reg);
116 			continue;
117 		}
118 
119 		ret = i2c_mux_add_adapter(muxc, 0, reg);
120 		if (ret)
121 			return ret;
122 	}
123 
124 	mule_i2c_mux_deselect(muxc, MULE_I2C_MUX_DEFAULT_DEV);
125 
126 	return 0;
127 }
128 
129 static const struct of_device_id mule_i2c_mux_of_match[] = {
130 	{ .compatible = "tsd,mule-i2c-mux", },
131 	{},
132 };
133 MODULE_DEVICE_TABLE(of, mule_i2c_mux_of_match);
134 
135 static struct platform_driver mule_i2c_mux_driver = {
136 	.driver = {
137 		.name	= "mule-i2c-mux",
138 		.of_match_table = mule_i2c_mux_of_match,
139 	},
140 	.probe		= mule_i2c_mux_probe,
141 };
142 
143 module_platform_driver(mule_i2c_mux_driver);
144 
145 MODULE_AUTHOR("Farouk Bouabid <farouk.bouabid@cherry.de>");
146 MODULE_DESCRIPTION("I2C mux driver for Theobroma Systems Mule");
147 MODULE_LICENSE("GPL");
148