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