Commit | Line | Data |
---|---|---|
55716d26 | 1 | // SPDX-License-Identifier: GPL-2.0-only |
8bc487d1 GL |
2 | /* |
3 | * OF helpers for the MDIO (Ethernet PHY) API | |
4 | * | |
5 | * Copyright (c) 2009 Secret Lab Technologies, Ltd. | |
6 | * | |
8bc487d1 GL |
7 | * This file provides helper functions for extracting PHY device information |
8 | * out of the OpenFirmware device tree and using it to populate an mii_bus. | |
9 | */ | |
10 | ||
24c30dbb | 11 | #include <linux/device.h> |
24c30dbb | 12 | #include <linux/err.h> |
bc1bee3b | 13 | #include <linux/fwnode_mdio.h> |
1bf34366 CJ |
14 | #include <linux/kernel.h> |
15 | #include <linux/module.h> | |
16 | #include <linux/netdevice.h> | |
8bc487d1 | 17 | #include <linux/of.h> |
e3873444 | 18 | #include <linux/of_irq.h> |
8bc487d1 | 19 | #include <linux/of_mdio.h> |
b7862412 | 20 | #include <linux/of_net.h> |
1bf34366 CJ |
21 | #include <linux/phy.h> |
22 | #include <linux/phy_fixed.h> | |
8bc487d1 | 23 | |
69226896 RQ |
24 | #define DEFAULT_GPIO_RESET_DELAY 10 /* in microseconds */ |
25 | ||
8bc487d1 GL |
26 | MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>"); |
27 | MODULE_LICENSE("GPL"); | |
28 | ||
3c6f5592 JG |
29 | /* Extract the clause 22 phy ID from the compatible string of the form |
30 | * ethernet-phy-idAAAA.BBBB */ | |
31 | static int of_get_phy_id(struct device_node *device, u32 *phy_id) | |
32 | { | |
cf996860 | 33 | return fwnode_get_phy_id(of_fwnode_handle(device), phy_id); |
3c6f5592 JG |
34 | } |
35 | ||
5972157c | 36 | int of_mdiobus_phy_device_register(struct mii_bus *mdio, struct phy_device *phy, |
bc1bee3b | 37 | struct device_node *child, u32 addr) |
2e79cb30 | 38 | { |
bc1bee3b CJ |
39 | return fwnode_mdiobus_phy_device_register(mdio, phy, |
40 | of_fwnode_handle(child), | |
41 | addr); | |
5972157c OR |
42 | } |
43 | EXPORT_SYMBOL(of_mdiobus_phy_device_register); | |
44 | ||
45 | static int of_mdiobus_register_phy(struct mii_bus *mdio, | |
46 | struct device_node *child, u32 addr) | |
47 | { | |
8d2cb3ad | 48 | return fwnode_mdiobus_register_phy(mdio, of_fwnode_handle(child), addr); |
2e79cb30 FF |
49 | } |
50 | ||
66bdede4 GU |
51 | static int of_mdiobus_register_device(struct mii_bus *mdio, |
52 | struct device_node *child, u32 addr) | |
a9049e0c | 53 | { |
7e33d84d | 54 | struct fwnode_handle *fwnode = of_fwnode_handle(child); |
a9049e0c AL |
55 | struct mdio_device *mdiodev; |
56 | int rc; | |
57 | ||
58 | mdiodev = mdio_device_create(mdio, addr); | |
a3478bae | 59 | if (IS_ERR(mdiodev)) |
66bdede4 | 60 | return PTR_ERR(mdiodev); |
a9049e0c AL |
61 | |
62 | /* Associate the OF node with the device structure so it | |
63 | * can be looked up later. | |
64 | */ | |
7e33d84d IC |
65 | fwnode_handle_get(fwnode); |
66 | device_set_node(&mdiodev->dev, fwnode); | |
a9049e0c AL |
67 | |
68 | /* All data is now stored in the mdiodev struct; register it. */ | |
69 | rc = mdio_device_register(mdiodev); | |
70 | if (rc) { | |
cb376176 ZH |
71 | device_set_node(&mdiodev->dev, NULL); |
72 | fwnode_handle_put(fwnode); | |
a9049e0c | 73 | mdio_device_free(mdiodev); |
66bdede4 | 74 | return rc; |
a9049e0c AL |
75 | } |
76 | ||
a613b26a RH |
77 | dev_dbg(&mdio->dev, "registered mdio device %pOFn at address %i\n", |
78 | child, addr); | |
66bdede4 | 79 | return 0; |
a9049e0c AL |
80 | } |
81 | ||
ae461131 AL |
82 | /* The following is a list of PHY compatible strings which appear in |
83 | * some DTBs. The compatible string is never matched against a PHY | |
84 | * driver, so is pointless. We only expect devices which are not PHYs | |
85 | * to have a compatible string, so they can be matched to an MDIO | |
86 | * driver. Encourage users to upgrade their DT blobs to remove these. | |
87 | */ | |
88 | static const struct of_device_id whitelist_phys[] = { | |
89 | { .compatible = "brcm,40nm-ephy" }, | |
7e1392fb | 90 | { .compatible = "broadcom,bcm5241" }, |
ae461131 AL |
91 | { .compatible = "marvell,88E1111", }, |
92 | { .compatible = "marvell,88e1116", }, | |
93 | { .compatible = "marvell,88e1118", }, | |
f5a952c0 | 94 | { .compatible = "marvell,88e1145", }, |
ae461131 AL |
95 | { .compatible = "marvell,88e1149r", }, |
96 | { .compatible = "marvell,88e1310", }, | |
97 | { .compatible = "marvell,88E1510", }, | |
98 | { .compatible = "marvell,88E1514", }, | |
99 | { .compatible = "moxa,moxart-rtl8201cp", }, | |
100 | {} | |
101 | }; | |
102 | ||
801a8ef5 AL |
103 | /* |
104 | * Return true if the child node is for a phy. It must either: | |
105 | * o Compatible string of "ethernet-phy-idX.X" | |
106 | * o Compatible string of "ethernet-phy-ieee802.3-c45" | |
107 | * o Compatible string of "ethernet-phy-ieee802.3-c22" | |
ae461131 | 108 | * o In the white list above (and issue a warning) |
801a8ef5 AL |
109 | * o No compatibility string |
110 | * | |
111 | * A device which is not a phy is expected to have a compatible string | |
112 | * indicating what sort of device it is. | |
113 | */ | |
0aa4d016 | 114 | bool of_mdiobus_child_is_phy(struct device_node *child) |
801a8ef5 AL |
115 | { |
116 | u32 phy_id; | |
117 | ||
118 | if (of_get_phy_id(child, &phy_id) != -EINVAL) | |
119 | return true; | |
120 | ||
121 | if (of_device_is_compatible(child, "ethernet-phy-ieee802.3-c45")) | |
122 | return true; | |
123 | ||
124 | if (of_device_is_compatible(child, "ethernet-phy-ieee802.3-c22")) | |
125 | return true; | |
126 | ||
ae461131 AL |
127 | if (of_match_node(whitelist_phys, child)) { |
128 | pr_warn(FW_WARN | |
0d638a07 RH |
129 | "%pOF: Whitelisted compatible string. Please remove\n", |
130 | child); | |
ae461131 AL |
131 | return true; |
132 | } | |
133 | ||
801a8ef5 AL |
134 | if (!of_find_property(child, "compatible", NULL)) |
135 | return true; | |
136 | ||
137 | return false; | |
138 | } | |
0aa4d016 | 139 | EXPORT_SYMBOL(of_mdiobus_child_is_phy); |
801a8ef5 | 140 | |
8bc487d1 GL |
141 | /** |
142 | * of_mdiobus_register - Register mii_bus and create PHYs from the device tree | |
143 | * @mdio: pointer to mii_bus structure | |
144 | * @np: pointer to device_node of MDIO bus. | |
145 | * | |
146 | * This function registers the mii_bus structure and registers a phy_device | |
147 | * for each child node of @np. | |
148 | */ | |
149 | int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np) | |
150 | { | |
8bc487d1 | 151 | struct device_node *child; |
2e79cb30 | 152 | bool scanphys = false; |
e7f4dc35 | 153 | int addr, rc; |
8bc487d1 | 154 | |
6d07a68a FF |
155 | if (!np) |
156 | return mdiobus_register(mdio); | |
157 | ||
a77f4f70 FF |
158 | /* Do not continue if the node is disabled */ |
159 | if (!of_device_is_available(np)) | |
160 | return -ENODEV; | |
161 | ||
8bc487d1 GL |
162 | /* Mask out all PHYs from auto probing. Instead the PHYs listed in |
163 | * the device tree are populated after the bus has been registered */ | |
164 | mdio->phy_mask = ~0; | |
165 | ||
7e33d84d | 166 | device_set_node(&mdio->dev, of_fwnode_handle(np)); |
25106022 | 167 | |
69226896 RQ |
168 | /* Get bus level PHY reset GPIO details */ |
169 | mdio->reset_delay_us = DEFAULT_GPIO_RESET_DELAY; | |
170 | of_property_read_u32(np, "reset-delay-us", &mdio->reset_delay_us); | |
bb383129 BT |
171 | mdio->reset_post_delay_us = 0; |
172 | of_property_read_u32(np, "reset-post-delay-us", &mdio->reset_post_delay_us); | |
69226896 | 173 | |
8bc487d1 GL |
174 | /* Register the MDIO bus */ |
175 | rc = mdiobus_register(mdio); | |
176 | if (rc) | |
177 | return rc; | |
178 | ||
801a8ef5 | 179 | /* Loop over the child nodes and register a phy_device for each phy */ |
e207e761 | 180 | for_each_available_child_of_node(np, child) { |
8f838288 DM |
181 | addr = of_mdio_parse_addr(&mdio->dev, child); |
182 | if (addr < 0) { | |
779d835e | 183 | scanphys = true; |
19458860 DD |
184 | continue; |
185 | } | |
186 | ||
801a8ef5 | 187 | if (of_mdiobus_child_is_phy(child)) |
66bdede4 | 188 | rc = of_mdiobus_register_phy(mdio, child, addr); |
a9049e0c | 189 | else |
66bdede4 | 190 | rc = of_mdiobus_register_device(mdio, child, addr); |
95f566de MB |
191 | |
192 | if (rc == -ENODEV) | |
193 | dev_err(&mdio->dev, | |
194 | "MDIO device at address %d is missing.\n", | |
195 | addr); | |
196 | else if (rc) | |
66bdede4 | 197 | goto unregister; |
8bc487d1 GL |
198 | } |
199 | ||
779d835e SH |
200 | if (!scanphys) |
201 | return 0; | |
202 | ||
203 | /* auto scan for PHYs with empty reg property */ | |
204 | for_each_available_child_of_node(np, child) { | |
205 | /* Skip PHYs with reg property set */ | |
2fca6d28 | 206 | if (of_find_property(child, "reg", NULL)) |
779d835e SH |
207 | continue; |
208 | ||
779d835e SH |
209 | for (addr = 0; addr < PHY_MAX_ADDR; addr++) { |
210 | /* skip already registered PHYs */ | |
7f854420 | 211 | if (mdiobus_is_registered_device(mdio, addr)) |
779d835e SH |
212 | continue; |
213 | ||
214 | /* be noisy to encourage people to set reg property */ | |
a613b26a RH |
215 | dev_info(&mdio->dev, "scan phy %pOFn at address %i\n", |
216 | child, addr); | |
779d835e | 217 | |
66bdede4 | 218 | if (of_mdiobus_child_is_phy(child)) { |
5a8d7f12 FF |
219 | /* -ENODEV is the return code that PHYLIB has |
220 | * standardized on to indicate that bus | |
221 | * scanning should continue. | |
222 | */ | |
66bdede4 | 223 | rc = of_mdiobus_register_phy(mdio, child, addr); |
5a8d7f12 FF |
224 | if (!rc) |
225 | break; | |
226 | if (rc != -ENODEV) | |
66bdede4 GU |
227 | goto unregister; |
228 | } | |
779d835e SH |
229 | } |
230 | } | |
231 | ||
8bc487d1 | 232 | return 0; |
66bdede4 GU |
233 | |
234 | unregister: | |
1c48709e | 235 | of_node_put(child); |
66bdede4 GU |
236 | mdiobus_unregister(mdio); |
237 | return rc; | |
8bc487d1 GL |
238 | } |
239 | EXPORT_SYMBOL(of_mdiobus_register); | |
240 | ||
b5b6775d RK |
241 | /** |
242 | * of_mdio_find_device - Given a device tree node, find the mdio_device | |
243 | * @np: pointer to the mdio_device's device tree node | |
244 | * | |
245 | * If successful, returns a pointer to the mdio_device with the embedded | |
246 | * struct device refcount incremented by one, or NULL on failure. | |
247 | * The caller should call put_device() on the mdio_device after its use | |
248 | */ | |
249 | struct mdio_device *of_mdio_find_device(struct device_node *np) | |
250 | { | |
0fb16976 | 251 | return fwnode_mdio_find_device(of_fwnode_handle(np)); |
b5b6775d RK |
252 | } |
253 | EXPORT_SYMBOL(of_mdio_find_device); | |
254 | ||
8bc487d1 GL |
255 | /** |
256 | * of_phy_find_device - Give a PHY node, find the phy_device | |
257 | * @phy_np: Pointer to the phy's device tree node | |
258 | * | |
f018ae7a RK |
259 | * If successful, returns a pointer to the phy_device with the embedded |
260 | * struct device refcount incremented by one, or NULL on failure. | |
8bc487d1 GL |
261 | */ |
262 | struct phy_device *of_phy_find_device(struct device_node *phy_np) | |
263 | { | |
2d7b8bf1 | 264 | return fwnode_phy_find_device(of_fwnode_handle(phy_np)); |
8bc487d1 GL |
265 | } |
266 | EXPORT_SYMBOL(of_phy_find_device); | |
267 | ||
268 | /** | |
269 | * of_phy_connect - Connect to the phy described in the device tree | |
270 | * @dev: pointer to net_device claiming the phy | |
271 | * @phy_np: Pointer to device tree node for the PHY | |
272 | * @hndlr: Link state callback for the network device | |
735c1e25 | 273 | * @flags: flags to pass to the PHY |
8bc487d1 GL |
274 | * @iface: PHY data interface type |
275 | * | |
f018ae7a RK |
276 | * If successful, returns a pointer to the phy_device with the embedded |
277 | * struct device refcount incremented by one, or NULL on failure. The | |
278 | * refcount must be dropped by calling phy_disconnect() or phy_detach(). | |
8bc487d1 GL |
279 | */ |
280 | struct phy_device *of_phy_connect(struct net_device *dev, | |
281 | struct device_node *phy_np, | |
282 | void (*hndlr)(struct net_device *), u32 flags, | |
283 | phy_interface_t iface) | |
284 | { | |
285 | struct phy_device *phy = of_phy_find_device(phy_np); | |
f018ae7a | 286 | int ret; |
8bc487d1 GL |
287 | |
288 | if (!phy) | |
289 | return NULL; | |
290 | ||
902053f1 | 291 | phy->dev_flags |= flags; |
2f637151 | 292 | |
f018ae7a RK |
293 | ret = phy_connect_direct(dev, phy, hndlr, iface); |
294 | ||
295 | /* refcount is held by phy_connect_direct() on success */ | |
e5a03bfd | 296 | put_device(&phy->mdio.dev); |
f018ae7a RK |
297 | |
298 | return ret ? NULL : phy; | |
8bc487d1 GL |
299 | } |
300 | EXPORT_SYMBOL(of_phy_connect); | |
24c30dbb | 301 | |
b7862412 DL |
302 | /** |
303 | * of_phy_get_and_connect | |
304 | * - Get phy node and connect to the phy described in the device tree | |
305 | * @dev: pointer to net_device claiming the phy | |
306 | * @np: Pointer to device tree node for the net_device claiming the phy | |
307 | * @hndlr: Link state callback for the network device | |
308 | * | |
309 | * If successful, returns a pointer to the phy_device with the embedded | |
310 | * struct device refcount incremented by one, or NULL on failure. The | |
311 | * refcount must be dropped by calling phy_disconnect() or phy_detach(). | |
312 | */ | |
313 | struct phy_device *of_phy_get_and_connect(struct net_device *dev, | |
314 | struct device_node *np, | |
315 | void (*hndlr)(struct net_device *)) | |
316 | { | |
317 | phy_interface_t iface; | |
318 | struct device_node *phy_np; | |
319 | struct phy_device *phy; | |
6eb9c9da | 320 | int ret; |
b7862412 | 321 | |
0c65b2b9 AL |
322 | ret = of_get_phy_mode(np, &iface); |
323 | if (ret) | |
b7862412 | 324 | return NULL; |
6eb9c9da LW |
325 | if (of_phy_is_fixed_link(np)) { |
326 | ret = of_phy_register_fixed_link(np); | |
327 | if (ret < 0) { | |
328 | netdev_err(dev, "broken fixed-link specification\n"); | |
329 | return NULL; | |
330 | } | |
331 | phy_np = of_node_get(np); | |
332 | } else { | |
333 | phy_np = of_parse_phandle(np, "phy-handle", 0); | |
334 | if (!phy_np) | |
335 | return NULL; | |
336 | } | |
b7862412 DL |
337 | |
338 | phy = of_phy_connect(dev, phy_np, hndlr, 0, iface); | |
339 | ||
340 | of_node_put(phy_np); | |
341 | ||
342 | return phy; | |
343 | } | |
344 | EXPORT_SYMBOL(of_phy_get_and_connect); | |
345 | ||
3be2a49e TP |
346 | /* |
347 | * of_phy_is_fixed_link() and of_phy_register_fixed_link() must | |
348 | * support two DT bindings: | |
349 | * - the old DT binding, where 'fixed-link' was a property with 5 | |
e65c2793 | 350 | * cells encoding various information about the fixed PHY |
3be2a49e TP |
351 | * - the new DT binding, where 'fixed-link' is a sub-node of the |
352 | * Ethernet device. | |
353 | */ | |
354 | bool of_phy_is_fixed_link(struct device_node *np) | |
355 | { | |
356 | struct device_node *dn; | |
4cba5c21 SS |
357 | int len, err; |
358 | const char *managed; | |
3be2a49e TP |
359 | |
360 | /* New binding */ | |
361 | dn = of_get_child_by_name(np, "fixed-link"); | |
362 | if (dn) { | |
363 | of_node_put(dn); | |
364 | return true; | |
365 | } | |
366 | ||
4cba5c21 SS |
367 | err = of_property_read_string(np, "managed", &managed); |
368 | if (err == 0 && strcmp(managed, "auto") != 0) | |
369 | return true; | |
370 | ||
3be2a49e TP |
371 | /* Old binding */ |
372 | if (of_get_property(np, "fixed-link", &len) && | |
373 | len == (5 * sizeof(__be32))) | |
374 | return true; | |
375 | ||
376 | return false; | |
377 | } | |
378 | EXPORT_SYMBOL(of_phy_is_fixed_link); | |
379 | ||
380 | int of_phy_register_fixed_link(struct device_node *np) | |
381 | { | |
382 | struct fixed_phy_status status = {}; | |
383 | struct device_node *fixed_link_node; | |
d226a2b8 | 384 | u32 fixed_link_prop[5]; |
4cba5c21 SS |
385 | const char *managed; |
386 | ||
5a7a8346 SS |
387 | if (of_property_read_string(np, "managed", &managed) == 0 && |
388 | strcmp(managed, "in-band-status") == 0) { | |
389 | /* status is zeroed, namely its .link member */ | |
390 | goto register_phy; | |
4cba5c21 | 391 | } |
3be2a49e TP |
392 | |
393 | /* New binding */ | |
394 | fixed_link_node = of_get_child_by_name(np, "fixed-link"); | |
395 | if (fixed_link_node) { | |
396 | status.link = 1; | |
a72e1541 RR |
397 | status.duplex = of_property_read_bool(fixed_link_node, |
398 | "full-duplex"); | |
48c1699d JH |
399 | if (of_property_read_u32(fixed_link_node, "speed", |
400 | &status.speed)) { | |
401 | of_node_put(fixed_link_node); | |
3be2a49e | 402 | return -EINVAL; |
48c1699d | 403 | } |
a72e1541 RR |
404 | status.pause = of_property_read_bool(fixed_link_node, "pause"); |
405 | status.asym_pause = of_property_read_bool(fixed_link_node, | |
406 | "asym-pause"); | |
3be2a49e | 407 | of_node_put(fixed_link_node); |
a5597008 | 408 | |
5a7a8346 | 409 | goto register_phy; |
3be2a49e TP |
410 | } |
411 | ||
412 | /* Old binding */ | |
d226a2b8 SS |
413 | if (of_property_read_u32_array(np, "fixed-link", fixed_link_prop, |
414 | ARRAY_SIZE(fixed_link_prop)) == 0) { | |
3be2a49e | 415 | status.link = 1; |
d226a2b8 SS |
416 | status.duplex = fixed_link_prop[1]; |
417 | status.speed = fixed_link_prop[2]; | |
418 | status.pause = fixed_link_prop[3]; | |
419 | status.asym_pause = fixed_link_prop[4]; | |
5a7a8346 | 420 | goto register_phy; |
3be2a49e TP |
421 | } |
422 | ||
423 | return -ENODEV; | |
5a7a8346 SS |
424 | |
425 | register_phy: | |
5468e82f | 426 | return PTR_ERR_OR_ZERO(fixed_phy_register(PHY_POLL, &status, np)); |
3be2a49e TP |
427 | } |
428 | EXPORT_SYMBOL(of_phy_register_fixed_link); | |
3f65047c JH |
429 | |
430 | void of_phy_deregister_fixed_link(struct device_node *np) | |
431 | { | |
432 | struct phy_device *phydev; | |
433 | ||
434 | phydev = of_phy_find_device(np); | |
435 | if (!phydev) | |
436 | return; | |
437 | ||
438 | fixed_phy_unregister(phydev); | |
439 | ||
440 | put_device(&phydev->mdio.dev); /* of_phy_find_device() */ | |
441 | phy_device_free(phydev); /* fixed_phy_register() */ | |
442 | } | |
443 | EXPORT_SYMBOL(of_phy_deregister_fixed_link); |