xref: /linux/drivers/net/dsa/motorcomm/mdio_bus.c (revision cece0b573deb5d102547835da720f60d7b7888cc)
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2026 David Yang
4  */
5 
6 #include <linux/of_mdio.h>
7 
8 #include "chip.h"
9 #include "mdio_bus.h"
10 #include "smi.h"
11 
12 static int yt921x_intif_wait(struct yt921x_priv *priv)
13 {
14 	u32 val = 0;
15 
16 	return yt921x_reg_wait(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START,
17 			       &val);
18 }
19 
20 int yt921x_intif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp)
21 {
22 	struct device *dev = yt921x_priv_to_device(priv);
23 	u32 mask;
24 	u32 ctrl;
25 	u32 val;
26 	int res;
27 
28 	res = yt921x_intif_wait(priv);
29 	if (res)
30 		return res;
31 
32 	mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
33 	       YT921X_MBUS_CTRL_OP_M;
34 	ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
35 	       YT921X_MBUS_CTRL_READ;
36 	res = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl);
37 	if (res)
38 		return res;
39 	res = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START);
40 	if (res)
41 		return res;
42 
43 	res = yt921x_intif_wait(priv);
44 	if (res)
45 		return res;
46 	res = yt921x_reg_read(priv, YT921X_INT_MBUS_DIN, &val);
47 	if (res)
48 		return res;
49 
50 	if ((u16)val != val)
51 		dev_info(dev,
52 			 "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n",
53 			 __func__, port, reg, val);
54 	*valp = (u16)val;
55 	return 0;
56 }
57 
58 int yt921x_intif_write(struct yt921x_priv *priv, int port, int reg, u16 val)
59 {
60 	u32 mask;
61 	u32 ctrl;
62 	int res;
63 
64 	res = yt921x_intif_wait(priv);
65 	if (res)
66 		return res;
67 
68 	mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
69 	       YT921X_MBUS_CTRL_OP_M;
70 	ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
71 	       YT921X_MBUS_CTRL_WRITE;
72 	res = yt921x_reg_update_bits(priv, YT921X_INT_MBUS_CTRL, mask, ctrl);
73 	if (res)
74 		return res;
75 	res = yt921x_reg_write(priv, YT921X_INT_MBUS_DOUT, val);
76 	if (res)
77 		return res;
78 	res = yt921x_reg_write(priv, YT921X_INT_MBUS_OP, YT921X_MBUS_OP_START);
79 	if (res)
80 		return res;
81 
82 	return yt921x_intif_wait(priv);
83 }
84 
85 int
86 yt921x_intif_modify_changed(struct yt921x_priv *priv, int port, int reg,
87 			    u16 mask, u16 val)
88 {
89 	int res;
90 	u16 v;
91 	u16 u;
92 
93 	res = yt921x_intif_read(priv, port, reg, &v);
94 	if (res)
95 		return res;
96 
97 	u = v;
98 	u &= ~mask;
99 	u |= val;
100 	if (u == v)
101 		return 0;
102 
103 	res = yt921x_intif_write(priv, port, reg, u);
104 	if (res)
105 		return res;
106 
107 	return 1;
108 }
109 
110 static int yt921x_mbus_int_read(struct mii_bus *mbus, int port, int reg)
111 {
112 	struct yt921x_priv *priv = mbus->priv;
113 	u16 val;
114 	int res;
115 
116 	if (port >= YT921X_PORT_NUM)
117 		return U16_MAX;
118 
119 	mutex_lock(&priv->reg_lock);
120 	res = yt921x_intif_read(priv, port, reg, &val);
121 	mutex_unlock(&priv->reg_lock);
122 
123 	if (res)
124 		return res;
125 	return val;
126 }
127 
128 static int
129 yt921x_mbus_int_write(struct mii_bus *mbus, int port, int reg, u16 data)
130 {
131 	struct yt921x_priv *priv = mbus->priv;
132 	int res;
133 
134 	if (port >= YT921X_PORT_NUM)
135 		return -ENODEV;
136 
137 	mutex_lock(&priv->reg_lock);
138 	res = yt921x_intif_write(priv, port, reg, data);
139 	mutex_unlock(&priv->reg_lock);
140 
141 	return res;
142 }
143 
144 int yt921x_mbus_int_init(struct yt921x_priv *priv, struct device_node *mnp)
145 {
146 	struct device *dev = yt921x_priv_to_device(priv);
147 	struct mii_bus *mbus;
148 	int res;
149 
150 	mbus = devm_mdiobus_alloc(dev);
151 	if (!mbus)
152 		return -ENOMEM;
153 
154 	mbus->name = "YT921x internal MDIO bus";
155 	snprintf(mbus->id, MII_BUS_ID_SIZE, "%s", dev_name(dev));
156 	mbus->priv = priv;
157 	mbus->read = yt921x_mbus_int_read;
158 	mbus->write = yt921x_mbus_int_write;
159 	mbus->parent = dev;
160 	mbus->phy_mask = (u32)~GENMASK(YT921X_PORT_NUM - 1, 0);
161 
162 	res = devm_of_mdiobus_register(dev, mbus, mnp);
163 	if (res)
164 		return res;
165 
166 	priv->mbus_int = mbus;
167 
168 	return 0;
169 }
170 
171 static int yt921x_extif_wait(struct yt921x_priv *priv)
172 {
173 	u32 val = 0;
174 
175 	return yt921x_reg_wait(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START,
176 			       &val);
177 }
178 
179 static int
180 yt921x_extif_read(struct yt921x_priv *priv, int port, int reg, u16 *valp)
181 {
182 	struct device *dev = yt921x_priv_to_device(priv);
183 	u32 mask;
184 	u32 ctrl;
185 	u32 val;
186 	int res;
187 
188 	res = yt921x_extif_wait(priv);
189 	if (res)
190 		return res;
191 
192 	mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
193 	       YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M;
194 	ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
195 	       YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_READ;
196 	res = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl);
197 	if (res)
198 		return res;
199 	res = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START);
200 	if (res)
201 		return res;
202 
203 	res = yt921x_extif_wait(priv);
204 	if (res)
205 		return res;
206 	res = yt921x_reg_read(priv, YT921X_EXT_MBUS_DIN, &val);
207 	if (res)
208 		return res;
209 
210 	if ((u16)val != val)
211 		dev_info(dev,
212 			 "%s: port %d, reg 0x%x: Expected u16, got 0x%08x\n",
213 			 __func__, port, reg, val);
214 	*valp = (u16)val;
215 	return 0;
216 }
217 
218 static int
219 yt921x_extif_write(struct yt921x_priv *priv, int port, int reg, u16 val)
220 {
221 	u32 mask;
222 	u32 ctrl;
223 	int res;
224 
225 	res = yt921x_extif_wait(priv);
226 	if (res)
227 		return res;
228 
229 	mask = YT921X_MBUS_CTRL_PORT_M | YT921X_MBUS_CTRL_REG_M |
230 	       YT921X_MBUS_CTRL_TYPE_M | YT921X_MBUS_CTRL_OP_M;
231 	ctrl = YT921X_MBUS_CTRL_PORT(port) | YT921X_MBUS_CTRL_REG(reg) |
232 	       YT921X_MBUS_CTRL_TYPE_C22 | YT921X_MBUS_CTRL_WRITE;
233 	res = yt921x_reg_update_bits(priv, YT921X_EXT_MBUS_CTRL, mask, ctrl);
234 	if (res)
235 		return res;
236 	res = yt921x_reg_write(priv, YT921X_EXT_MBUS_DOUT, val);
237 	if (res)
238 		return res;
239 	res = yt921x_reg_write(priv, YT921X_EXT_MBUS_OP, YT921X_MBUS_OP_START);
240 	if (res)
241 		return res;
242 
243 	return yt921x_extif_wait(priv);
244 }
245 
246 static int yt921x_mbus_ext_read(struct mii_bus *mbus, int port, int reg)
247 {
248 	struct yt921x_priv *priv = mbus->priv;
249 	u16 val;
250 	int res;
251 
252 	mutex_lock(&priv->reg_lock);
253 	res = yt921x_extif_read(priv, port, reg, &val);
254 	mutex_unlock(&priv->reg_lock);
255 
256 	if (res)
257 		return res;
258 	return val;
259 }
260 
261 static int
262 yt921x_mbus_ext_write(struct mii_bus *mbus, int port, int reg, u16 data)
263 {
264 	struct yt921x_priv *priv = mbus->priv;
265 	int res;
266 
267 	mutex_lock(&priv->reg_lock);
268 	res = yt921x_extif_write(priv, port, reg, data);
269 	mutex_unlock(&priv->reg_lock);
270 
271 	return res;
272 }
273 
274 int yt921x_mbus_ext_init(struct yt921x_priv *priv, struct device_node *mnp)
275 {
276 	struct device *dev = yt921x_priv_to_device(priv);
277 	struct mii_bus *mbus;
278 	int res;
279 
280 	mbus = devm_mdiobus_alloc(dev);
281 	if (!mbus)
282 		return -ENOMEM;
283 
284 	mbus->name = "YT921x external MDIO bus";
285 	snprintf(mbus->id, MII_BUS_ID_SIZE, "%s@ext", dev_name(dev));
286 	mbus->priv = priv;
287 	/* TODO: c45? */
288 	mbus->read = yt921x_mbus_ext_read;
289 	mbus->write = yt921x_mbus_ext_write;
290 	mbus->parent = dev;
291 
292 	res = devm_of_mdiobus_register(dev, mbus, mnp);
293 	if (res)
294 		return res;
295 
296 	priv->mbus_ext = mbus;
297 
298 	return 0;
299 }
300