1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* |
3 | * Fixed MDIO bus (MDIO bus emulation with fixed PHYs) |
4 | * |
5 | * Author: Vitaly Bordug <vbordug@ru.mvista.com> |
6 | * Anton Vorontsov <avorontsov@ru.mvista.com> |
7 | * |
8 | * Copyright (c) 2006-2007 MontaVista Software, Inc. |
9 | */ |
10 | |
11 | #include <linux/kernel.h> |
12 | #include <linux/module.h> |
13 | #include <linux/platform_device.h> |
14 | #include <linux/list.h> |
15 | #include <linux/mii.h> |
16 | #include <linux/phy.h> |
17 | #include <linux/phy_fixed.h> |
18 | #include <linux/err.h> |
19 | #include <linux/slab.h> |
20 | #include <linux/of.h> |
21 | #include <linux/gpio/consumer.h> |
22 | #include <linux/idr.h> |
23 | #include <linux/netdevice.h> |
24 | #include <linux/linkmode.h> |
25 | |
26 | #include "swphy.h" |
27 | |
28 | struct fixed_mdio_bus { |
29 | struct mii_bus *mii_bus; |
30 | struct list_head phys; |
31 | }; |
32 | |
33 | struct fixed_phy { |
34 | int addr; |
35 | struct phy_device *phydev; |
36 | struct fixed_phy_status status; |
37 | bool no_carrier; |
38 | int (*link_update)(struct net_device *, struct fixed_phy_status *); |
39 | struct list_head node; |
40 | struct gpio_desc *link_gpiod; |
41 | }; |
42 | |
43 | static struct platform_device *pdev; |
44 | static struct fixed_mdio_bus platform_fmb = { |
45 | .phys = LIST_HEAD_INIT(platform_fmb.phys), |
46 | }; |
47 | |
48 | int fixed_phy_change_carrier(struct net_device *dev, bool new_carrier) |
49 | { |
50 | struct fixed_mdio_bus *fmb = &platform_fmb; |
51 | struct phy_device *phydev = dev->phydev; |
52 | struct fixed_phy *fp; |
53 | |
54 | if (!phydev || !phydev->mdio.bus) |
55 | return -EINVAL; |
56 | |
57 | list_for_each_entry(fp, &fmb->phys, node) { |
58 | if (fp->addr == phydev->mdio.addr) { |
59 | fp->no_carrier = !new_carrier; |
60 | return 0; |
61 | } |
62 | } |
63 | return -EINVAL; |
64 | } |
65 | EXPORT_SYMBOL_GPL(fixed_phy_change_carrier); |
66 | |
67 | static void fixed_phy_update(struct fixed_phy *fp) |
68 | { |
69 | if (!fp->no_carrier && fp->link_gpiod) |
70 | fp->status.link = !!gpiod_get_value_cansleep(desc: fp->link_gpiod); |
71 | } |
72 | |
73 | static int fixed_mdio_read(struct mii_bus *bus, int phy_addr, int reg_num) |
74 | { |
75 | struct fixed_mdio_bus *fmb = bus->priv; |
76 | struct fixed_phy *fp; |
77 | |
78 | list_for_each_entry(fp, &fmb->phys, node) { |
79 | if (fp->addr == phy_addr) { |
80 | struct fixed_phy_status state; |
81 | |
82 | fp->status.link = !fp->no_carrier; |
83 | |
84 | /* Issue callback if user registered it. */ |
85 | if (fp->link_update) |
86 | fp->link_update(fp->phydev->attached_dev, |
87 | &fp->status); |
88 | |
89 | /* Check the GPIO for change in status */ |
90 | fixed_phy_update(fp); |
91 | state = fp->status; |
92 | |
93 | return swphy_read_reg(reg: reg_num, state: &state); |
94 | } |
95 | } |
96 | |
97 | return 0xFFFF; |
98 | } |
99 | |
100 | static int fixed_mdio_write(struct mii_bus *bus, int phy_addr, int reg_num, |
101 | u16 val) |
102 | { |
103 | return 0; |
104 | } |
105 | |
106 | /* |
107 | * If something weird is required to be done with link/speed, |
108 | * network driver is able to assign a function to implement this. |
109 | * May be useful for PHY's that need to be software-driven. |
110 | */ |
111 | int fixed_phy_set_link_update(struct phy_device *phydev, |
112 | int (*link_update)(struct net_device *, |
113 | struct fixed_phy_status *)) |
114 | { |
115 | struct fixed_mdio_bus *fmb = &platform_fmb; |
116 | struct fixed_phy *fp; |
117 | |
118 | if (!phydev || !phydev->mdio.bus) |
119 | return -EINVAL; |
120 | |
121 | list_for_each_entry(fp, &fmb->phys, node) { |
122 | if (fp->addr == phydev->mdio.addr) { |
123 | fp->link_update = link_update; |
124 | fp->phydev = phydev; |
125 | return 0; |
126 | } |
127 | } |
128 | |
129 | return -ENOENT; |
130 | } |
131 | EXPORT_SYMBOL_GPL(fixed_phy_set_link_update); |
132 | |
133 | static int fixed_phy_add_gpiod(unsigned int irq, int phy_addr, |
134 | struct fixed_phy_status *status, |
135 | struct gpio_desc *gpiod) |
136 | { |
137 | int ret; |
138 | struct fixed_mdio_bus *fmb = &platform_fmb; |
139 | struct fixed_phy *fp; |
140 | |
141 | ret = swphy_validate_state(state: status); |
142 | if (ret < 0) |
143 | return ret; |
144 | |
145 | fp = kzalloc(size: sizeof(*fp), GFP_KERNEL); |
146 | if (!fp) |
147 | return -ENOMEM; |
148 | |
149 | if (irq != PHY_POLL) |
150 | fmb->mii_bus->irq[phy_addr] = irq; |
151 | |
152 | fp->addr = phy_addr; |
153 | fp->status = *status; |
154 | fp->link_gpiod = gpiod; |
155 | |
156 | fixed_phy_update(fp); |
157 | |
158 | list_add_tail(new: &fp->node, head: &fmb->phys); |
159 | |
160 | return 0; |
161 | } |
162 | |
163 | int fixed_phy_add(unsigned int irq, int phy_addr, |
164 | struct fixed_phy_status *status) |
165 | { |
166 | return fixed_phy_add_gpiod(irq, phy_addr, status, NULL); |
167 | } |
168 | EXPORT_SYMBOL_GPL(fixed_phy_add); |
169 | |
170 | static DEFINE_IDA(phy_fixed_ida); |
171 | |
172 | static void fixed_phy_del(int phy_addr) |
173 | { |
174 | struct fixed_mdio_bus *fmb = &platform_fmb; |
175 | struct fixed_phy *fp, *tmp; |
176 | |
177 | list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { |
178 | if (fp->addr == phy_addr) { |
179 | list_del(entry: &fp->node); |
180 | if (fp->link_gpiod) |
181 | gpiod_put(desc: fp->link_gpiod); |
182 | kfree(objp: fp); |
183 | ida_free(&phy_fixed_ida, id: phy_addr); |
184 | return; |
185 | } |
186 | } |
187 | } |
188 | |
189 | #ifdef CONFIG_OF_GPIO |
190 | static struct gpio_desc *fixed_phy_get_gpiod(struct device_node *np) |
191 | { |
192 | struct device_node *fixed_link_node; |
193 | struct gpio_desc *gpiod; |
194 | |
195 | if (!np) |
196 | return NULL; |
197 | |
198 | fixed_link_node = of_get_child_by_name(node: np, name: "fixed-link" ); |
199 | if (!fixed_link_node) |
200 | return NULL; |
201 | |
202 | /* |
203 | * As the fixed link is just a device tree node without any |
204 | * Linux device associated with it, we simply have obtain |
205 | * the GPIO descriptor from the device tree like this. |
206 | */ |
207 | gpiod = fwnode_gpiod_get_index(of_fwnode_handle(fixed_link_node), |
208 | con_id: "link" , index: 0, flags: GPIOD_IN, label: "mdio" ); |
209 | if (IS_ERR(ptr: gpiod) && PTR_ERR(ptr: gpiod) != -EPROBE_DEFER) { |
210 | if (PTR_ERR(ptr: gpiod) != -ENOENT) |
211 | pr_err("error getting GPIO for fixed link %pOF, proceed without\n" , |
212 | fixed_link_node); |
213 | gpiod = NULL; |
214 | } |
215 | of_node_put(node: fixed_link_node); |
216 | |
217 | return gpiod; |
218 | } |
219 | #else |
220 | static struct gpio_desc *fixed_phy_get_gpiod(struct device_node *np) |
221 | { |
222 | return NULL; |
223 | } |
224 | #endif |
225 | |
226 | static struct phy_device *__fixed_phy_register(unsigned int irq, |
227 | struct fixed_phy_status *status, |
228 | struct device_node *np, |
229 | struct gpio_desc *gpiod) |
230 | { |
231 | struct fixed_mdio_bus *fmb = &platform_fmb; |
232 | struct phy_device *phy; |
233 | int phy_addr; |
234 | int ret; |
235 | |
236 | if (!fmb->mii_bus || fmb->mii_bus->state != MDIOBUS_REGISTERED) |
237 | return ERR_PTR(error: -EPROBE_DEFER); |
238 | |
239 | /* Check if we have a GPIO associated with this fixed phy */ |
240 | if (!gpiod) { |
241 | gpiod = fixed_phy_get_gpiod(np); |
242 | if (IS_ERR(ptr: gpiod)) |
243 | return ERR_CAST(ptr: gpiod); |
244 | } |
245 | |
246 | /* Get the next available PHY address, up to PHY_MAX_ADDR */ |
247 | phy_addr = ida_alloc_max(ida: &phy_fixed_ida, PHY_MAX_ADDR - 1, GFP_KERNEL); |
248 | if (phy_addr < 0) |
249 | return ERR_PTR(error: phy_addr); |
250 | |
251 | ret = fixed_phy_add_gpiod(irq, phy_addr, status, gpiod); |
252 | if (ret < 0) { |
253 | ida_free(&phy_fixed_ida, id: phy_addr); |
254 | return ERR_PTR(error: ret); |
255 | } |
256 | |
257 | phy = get_phy_device(bus: fmb->mii_bus, addr: phy_addr, is_c45: false); |
258 | if (IS_ERR(ptr: phy)) { |
259 | fixed_phy_del(phy_addr); |
260 | return ERR_PTR(error: -EINVAL); |
261 | } |
262 | |
263 | /* propagate the fixed link values to struct phy_device */ |
264 | phy->link = status->link; |
265 | if (status->link) { |
266 | phy->speed = status->speed; |
267 | phy->duplex = status->duplex; |
268 | phy->pause = status->pause; |
269 | phy->asym_pause = status->asym_pause; |
270 | } |
271 | |
272 | of_node_get(node: np); |
273 | phy->mdio.dev.of_node = np; |
274 | phy->is_pseudo_fixed_link = true; |
275 | |
276 | switch (status->speed) { |
277 | case SPEED_1000: |
278 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_1000baseT_Half_BIT, |
279 | addr: phy->supported); |
280 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_1000baseT_Full_BIT, |
281 | addr: phy->supported); |
282 | fallthrough; |
283 | case SPEED_100: |
284 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_100baseT_Half_BIT, |
285 | addr: phy->supported); |
286 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_100baseT_Full_BIT, |
287 | addr: phy->supported); |
288 | fallthrough; |
289 | case SPEED_10: |
290 | default: |
291 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_10baseT_Half_BIT, |
292 | addr: phy->supported); |
293 | linkmode_set_bit(nr: ETHTOOL_LINK_MODE_10baseT_Full_BIT, |
294 | addr: phy->supported); |
295 | } |
296 | |
297 | phy_advertise_supported(phydev: phy); |
298 | |
299 | ret = phy_device_register(phy); |
300 | if (ret) { |
301 | phy_device_free(phydev: phy); |
302 | of_node_put(node: np); |
303 | fixed_phy_del(phy_addr); |
304 | return ERR_PTR(error: ret); |
305 | } |
306 | |
307 | return phy; |
308 | } |
309 | |
310 | struct phy_device *fixed_phy_register(unsigned int irq, |
311 | struct fixed_phy_status *status, |
312 | struct device_node *np) |
313 | { |
314 | return __fixed_phy_register(irq, status, np, NULL); |
315 | } |
316 | EXPORT_SYMBOL_GPL(fixed_phy_register); |
317 | |
318 | struct phy_device * |
319 | fixed_phy_register_with_gpiod(unsigned int irq, |
320 | struct fixed_phy_status *status, |
321 | struct gpio_desc *gpiod) |
322 | { |
323 | return __fixed_phy_register(irq, status, NULL, gpiod); |
324 | } |
325 | EXPORT_SYMBOL_GPL(fixed_phy_register_with_gpiod); |
326 | |
327 | void fixed_phy_unregister(struct phy_device *phy) |
328 | { |
329 | phy_device_remove(phydev: phy); |
330 | of_node_put(node: phy->mdio.dev.of_node); |
331 | fixed_phy_del(phy_addr: phy->mdio.addr); |
332 | } |
333 | EXPORT_SYMBOL_GPL(fixed_phy_unregister); |
334 | |
335 | static int __init fixed_mdio_bus_init(void) |
336 | { |
337 | struct fixed_mdio_bus *fmb = &platform_fmb; |
338 | int ret; |
339 | |
340 | pdev = platform_device_register_simple(name: "Fixed MDIO bus" , id: 0, NULL, num: 0); |
341 | if (IS_ERR(ptr: pdev)) |
342 | return PTR_ERR(ptr: pdev); |
343 | |
344 | fmb->mii_bus = mdiobus_alloc(); |
345 | if (fmb->mii_bus == NULL) { |
346 | ret = -ENOMEM; |
347 | goto err_mdiobus_reg; |
348 | } |
349 | |
350 | snprintf(buf: fmb->mii_bus->id, MII_BUS_ID_SIZE, fmt: "fixed-0" ); |
351 | fmb->mii_bus->name = "Fixed MDIO Bus" ; |
352 | fmb->mii_bus->priv = fmb; |
353 | fmb->mii_bus->parent = &pdev->dev; |
354 | fmb->mii_bus->read = &fixed_mdio_read; |
355 | fmb->mii_bus->write = &fixed_mdio_write; |
356 | fmb->mii_bus->phy_mask = ~0; |
357 | |
358 | ret = mdiobus_register(fmb->mii_bus); |
359 | if (ret) |
360 | goto err_mdiobus_alloc; |
361 | |
362 | return 0; |
363 | |
364 | err_mdiobus_alloc: |
365 | mdiobus_free(bus: fmb->mii_bus); |
366 | err_mdiobus_reg: |
367 | platform_device_unregister(pdev); |
368 | return ret; |
369 | } |
370 | module_init(fixed_mdio_bus_init); |
371 | |
372 | static void __exit fixed_mdio_bus_exit(void) |
373 | { |
374 | struct fixed_mdio_bus *fmb = &platform_fmb; |
375 | struct fixed_phy *fp, *tmp; |
376 | |
377 | mdiobus_unregister(bus: fmb->mii_bus); |
378 | mdiobus_free(bus: fmb->mii_bus); |
379 | platform_device_unregister(pdev); |
380 | |
381 | list_for_each_entry_safe(fp, tmp, &fmb->phys, node) { |
382 | list_del(entry: &fp->node); |
383 | kfree(objp: fp); |
384 | } |
385 | ida_destroy(ida: &phy_fixed_ida); |
386 | } |
387 | module_exit(fixed_mdio_bus_exit); |
388 | |
389 | MODULE_DESCRIPTION("Fixed MDIO bus (MDIO bus emulation with fixed PHYs)" ); |
390 | MODULE_AUTHOR("Vitaly Bordug" ); |
391 | MODULE_LICENSE("GPL" ); |
392 | |