1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2015 Google, Inc
4  * Written by Simon Glass <sjg@chromium.org>
5  */
6 
7 #include <common.h>
8 #include <dm.h>
9 #include <errno.h>
10 #include <i2c.h>
11 #include <dm/lists.h>
12 #include <dm/root.h>
13 
14 /**
15  * struct i2c_mux: Information the uclass stores about an I2C mux
16  *
17  * @selected:	Currently selected mux, or -1 for none
18  * @i2c_bus: I2C bus to use for communcation
19  */
20 struct i2c_mux {
21 	int selected;
22 	struct udevice *i2c_bus;
23 };
24 
25 /**
26  * struct i2c_mux_bus: Information about each bus the mux controls
27  *
28  * @channel: Channel number used to select this bus
29  */
30 struct i2c_mux_bus {
31 	uint channel;
32 };
33 
34 /* Find out the mux channel number */
35 static int i2c_mux_child_post_bind(struct udevice *dev)
36 {
37 	struct i2c_mux_bus *plat = dev_get_parent_platdata(dev);
38 	int channel;
39 
40 	channel = dev_read_u32_default(dev, "reg", -1);
41 	if (channel < 0)
42 		return -EINVAL;
43 	plat->channel = channel;
44 
45 	return 0;
46 }
47 
48 /* Find the I2C buses selected by this mux */
49 static int i2c_mux_post_bind(struct udevice *mux)
50 {
51 	ofnode node;
52 	int ret;
53 
54 	debug("%s: %s\n", __func__, mux->name);
55 	/*
56 	 * There is no compatible string in the sub-nodes, so we must manually
57 	 * bind these
58 	 */
59 	dev_for_each_subnode(node, mux) {
60 		struct udevice *dev;
61 		const char *name;
62 		const char *arrow = "->";
63 		char *full_name;
64 		int parent_name_len, arrow_len, mux_name_len, name_len;
65 
66 		name = ofnode_get_name(node);
67 
68 		/* Calculate lenghts of strings */
69 		parent_name_len = strlen(mux->parent->name);
70 		arrow_len = strlen(arrow);
71 		mux_name_len = strlen(mux->name);
72 		name_len = strlen(name);
73 
74 		full_name = calloc(1, parent_name_len + arrow_len +
75 				   mux_name_len + arrow_len + name_len + 1);
76 		if (!full_name)
77 			return -ENOMEM;
78 
79 		/* Compose bus name */
80 		strcat(full_name, mux->parent->name);
81 		strcat(full_name, arrow);
82 		strcat(full_name, mux->name);
83 		strcat(full_name, arrow);
84 		strcat(full_name, name);
85 
86 		ret = device_bind_driver_to_node(mux, "i2c_mux_bus_drv",
87 						 full_name, node, &dev);
88 		debug("   - bind ret=%d, %s, req_seq %d\n", ret,
89 		      dev ? dev->name : NULL, dev->req_seq);
90 		if (ret)
91 			return ret;
92 	}
93 
94 	return 0;
95 }
96 
97 /* Set up the mux ready for use */
98 static int i2c_mux_post_probe(struct udevice *mux)
99 {
100 	struct i2c_mux *priv = dev_get_uclass_priv(mux);
101 	int ret;
102 
103 	debug("%s: %s\n", __func__, mux->name);
104 	priv->selected = -1;
105 
106 	/* if parent is of i2c uclass already, we'll take that, otherwise
107 	 * look if we find an i2c-parent phandle
108 	 */
109 	if (UCLASS_I2C == device_get_uclass_id(mux->parent)) {
110 		priv->i2c_bus = dev_get_parent(mux);
111 		debug("%s: bus=%p/%s\n", __func__, priv->i2c_bus,
112 		      priv->i2c_bus->name);
113 		return 0;
114 	}
115 
116 	ret = uclass_get_device_by_phandle(UCLASS_I2C, mux, "i2c-parent",
117 					   &priv->i2c_bus);
118 	if (ret)
119 		return ret;
120 	debug("%s: bus=%p/%s\n", __func__, priv->i2c_bus, priv->i2c_bus->name);
121 
122 	return 0;
123 }
124 
125 int i2c_mux_select(struct udevice *dev)
126 {
127 	struct i2c_mux_bus *plat = dev_get_parent_platdata(dev);
128 	struct udevice *mux = dev->parent;
129 	struct i2c_mux_ops *ops = i2c_mux_get_ops(mux);
130 
131 	if (!ops->select)
132 		return -ENOSYS;
133 
134 	return ops->select(mux, dev, plat->channel);
135 }
136 
137 int i2c_mux_deselect(struct udevice *dev)
138 {
139 	struct i2c_mux_bus *plat = dev_get_parent_platdata(dev);
140 	struct udevice *mux = dev->parent;
141 	struct i2c_mux_ops *ops = i2c_mux_get_ops(mux);
142 
143 	if (!ops->deselect)
144 		return -ENOSYS;
145 
146 	return ops->deselect(mux, dev, plat->channel);
147 }
148 
149 static int i2c_mux_bus_set_bus_speed(struct udevice *dev, unsigned int speed)
150 {
151 	struct udevice *mux = dev->parent;
152 	struct i2c_mux *priv = dev_get_uclass_priv(mux);
153 	int ret, ret2;
154 
155 	ret = i2c_mux_select(dev);
156 	if (ret)
157 		return ret;
158 	ret = dm_i2c_set_bus_speed(priv->i2c_bus, speed);
159 	ret2 = i2c_mux_deselect(dev);
160 
161 	return ret ? ret : ret2;
162 }
163 
164 static int i2c_mux_bus_probe(struct udevice *dev, uint chip_addr,
165 			     uint chip_flags)
166 {
167 	struct udevice *mux = dev->parent;
168 	struct i2c_mux *priv = dev_get_uclass_priv(mux);
169 	struct dm_i2c_ops *ops = i2c_get_ops(priv->i2c_bus);
170 	int ret, ret2;
171 
172 	debug("%s: %s, bus %s\n", __func__, dev->name, priv->i2c_bus->name);
173 	if (!ops->probe_chip)
174 		return -ENOSYS;
175 	ret = i2c_mux_select(dev);
176 	if (ret)
177 		return ret;
178 	ret = ops->probe_chip(priv->i2c_bus, chip_addr, chip_flags);
179 	ret2 = i2c_mux_deselect(dev);
180 
181 	return ret ? ret : ret2;
182 }
183 
184 static int i2c_mux_bus_xfer(struct udevice *dev, struct i2c_msg *msg,
185 			    int nmsgs)
186 {
187 	struct udevice *mux = dev->parent;
188 	struct i2c_mux *priv = dev_get_uclass_priv(mux);
189 	struct dm_i2c_ops *ops = i2c_get_ops(priv->i2c_bus);
190 	int ret, ret2;
191 
192 	debug("%s: %s, bus %s\n", __func__, dev->name, priv->i2c_bus->name);
193 	if (!ops->xfer)
194 		return -ENOSYS;
195 	ret = i2c_mux_select(dev);
196 	if (ret)
197 		return ret;
198 	ret = ops->xfer(priv->i2c_bus, msg, nmsgs);
199 	ret2 = i2c_mux_deselect(dev);
200 
201 	return ret ? ret : ret2;
202 }
203 
204 static const struct dm_i2c_ops i2c_mux_bus_ops = {
205 	.xfer		= i2c_mux_bus_xfer,
206 	.probe_chip	= i2c_mux_bus_probe,
207 	.set_bus_speed	= i2c_mux_bus_set_bus_speed,
208 };
209 
210 U_BOOT_DRIVER(i2c_mux_bus) = {
211 	.name		= "i2c_mux_bus_drv",
212 	.id		= UCLASS_I2C,
213 	.ops	= &i2c_mux_bus_ops,
214 };
215 
216 UCLASS_DRIVER(i2c_mux) = {
217 	.id		= UCLASS_I2C_MUX,
218 	.name		= "i2c_mux",
219 	.post_bind	= i2c_mux_post_bind,
220 	.post_probe	= i2c_mux_post_probe,
221 	.per_device_auto_alloc_size = sizeof(struct i2c_mux),
222 	.per_child_platdata_auto_alloc_size = sizeof(struct i2c_mux_bus),
223 	.child_post_bind = i2c_mux_child_post_bind,
224 };
225