Commit | Line | Data |
---|---|---|
00db8189 AF |
1 | /* |
2 | * drivers/net/phy/mdio_bus.c | |
3 | * | |
4 | * MDIO Bus interface | |
5 | * | |
6 | * Author: Andy Fleming | |
7 | * | |
8 | * Copyright (c) 2004 Freescale Semiconductor, Inc. | |
9 | * | |
10 | * This program is free software; you can redistribute it and/or modify it | |
11 | * under the terms of the GNU General Public License as published by the | |
12 | * Free Software Foundation; either version 2 of the License, or (at your | |
13 | * option) any later version. | |
14 | * | |
15 | */ | |
00db8189 | 16 | #include <linux/kernel.h> |
00db8189 AF |
17 | #include <linux/string.h> |
18 | #include <linux/errno.h> | |
19 | #include <linux/unistd.h> | |
20 | #include <linux/slab.h> | |
21 | #include <linux/interrupt.h> | |
22 | #include <linux/init.h> | |
23 | #include <linux/delay.h> | |
3d1e4db2 | 24 | #include <linux/device.h> |
00db8189 AF |
25 | #include <linux/netdevice.h> |
26 | #include <linux/etherdevice.h> | |
27 | #include <linux/skbuff.h> | |
28 | #include <linux/spinlock.h> | |
29 | #include <linux/mm.h> | |
30 | #include <linux/module.h> | |
00db8189 AF |
31 | #include <linux/mii.h> |
32 | #include <linux/ethtool.h> | |
33 | #include <linux/phy.h> | |
34 | ||
35 | #include <asm/io.h> | |
36 | #include <asm/irq.h> | |
37 | #include <asm/uaccess.h> | |
38 | ||
298cf9be LB |
39 | /** |
40 | * mdiobus_alloc - allocate a mii_bus structure | |
41 | * | |
42 | * Description: called by a bus driver to allocate an mii_bus | |
43 | * structure to fill in. | |
44 | */ | |
45 | struct mii_bus *mdiobus_alloc(void) | |
46 | { | |
46abc021 LB |
47 | struct mii_bus *bus; |
48 | ||
49 | bus = kzalloc(sizeof(*bus), GFP_KERNEL); | |
50 | if (bus != NULL) | |
51 | bus->state = MDIOBUS_ALLOCATED; | |
52 | ||
53 | return bus; | |
298cf9be LB |
54 | } |
55 | EXPORT_SYMBOL(mdiobus_alloc); | |
56 | ||
46abc021 LB |
57 | /** |
58 | * mdiobus_release - mii_bus device release callback | |
78c36b15 | 59 | * @d: the target struct device that contains the mii_bus |
46abc021 LB |
60 | * |
61 | * Description: called when the last reference to an mii_bus is | |
62 | * dropped, to free the underlying memory. | |
63 | */ | |
64 | static void mdiobus_release(struct device *d) | |
65 | { | |
66 | struct mii_bus *bus = to_mii_bus(d); | |
161c8d2f KH |
67 | BUG_ON(bus->state != MDIOBUS_RELEASED && |
68 | /* for compatibility with error handling in drivers */ | |
69 | bus->state != MDIOBUS_ALLOCATED); | |
46abc021 LB |
70 | kfree(bus); |
71 | } | |
72 | ||
73 | static struct class mdio_bus_class = { | |
74 | .name = "mdio_bus", | |
75 | .dev_release = mdiobus_release, | |
76 | }; | |
77 | ||
b3df0da8 RD |
78 | /** |
79 | * mdiobus_register - bring up all the PHYs on a given bus and attach them to bus | |
80 | * @bus: target mii_bus | |
e1393456 | 81 | * |
b3df0da8 RD |
82 | * Description: Called by a bus driver to bring up all the PHYs |
83 | * on a given bus, and attach them to the bus. | |
84 | * | |
85 | * Returns 0 on success or < 0 on error. | |
e1393456 AF |
86 | */ |
87 | int mdiobus_register(struct mii_bus *bus) | |
88 | { | |
161c8d2f | 89 | int i, err; |
e1393456 | 90 | |
e1393456 AF |
91 | if (NULL == bus || NULL == bus->name || |
92 | NULL == bus->read || | |
93 | NULL == bus->write) | |
94 | return -EINVAL; | |
95 | ||
46abc021 LB |
96 | BUG_ON(bus->state != MDIOBUS_ALLOCATED && |
97 | bus->state != MDIOBUS_UNREGISTERED); | |
98 | ||
99 | bus->dev.parent = bus->parent; | |
100 | bus->dev.class = &mdio_bus_class; | |
101 | bus->dev.groups = NULL; | |
036b6687 | 102 | dev_set_name(&bus->dev, "%s", bus->id); |
46abc021 LB |
103 | |
104 | err = device_register(&bus->dev); | |
105 | if (err) { | |
106 | printk(KERN_ERR "mii_bus %s failed to register\n", bus->id); | |
107 | return -EINVAL; | |
108 | } | |
109 | ||
d1e7fe4d AB |
110 | mutex_init(&bus->mdio_lock); |
111 | ||
e1393456 AF |
112 | if (bus->reset) |
113 | bus->reset(bus); | |
114 | ||
115 | for (i = 0; i < PHY_MAX_ADDR; i++) { | |
4fd5f812 LB |
116 | if ((bus->phy_mask & (1 << i)) == 0) { |
117 | struct phy_device *phydev; | |
e1393456 | 118 | |
4fd5f812 | 119 | phydev = mdiobus_scan(bus, i); |
161c8d2f | 120 | if (IS_ERR(phydev)) { |
4fd5f812 | 121 | err = PTR_ERR(phydev); |
161c8d2f KH |
122 | goto error; |
123 | } | |
64b1c2b4 | 124 | } |
e1393456 AF |
125 | } |
126 | ||
161c8d2f | 127 | bus->state = MDIOBUS_REGISTERED; |
e1393456 | 128 | pr_info("%s: probed\n", bus->name); |
161c8d2f | 129 | return 0; |
e1393456 | 130 | |
161c8d2f KH |
131 | error: |
132 | while (--i >= 0) { | |
133 | if (bus->phy_map[i]) | |
134 | device_unregister(&bus->phy_map[i]->dev); | |
135 | } | |
136 | device_del(&bus->dev); | |
e1393456 AF |
137 | return err; |
138 | } | |
139 | EXPORT_SYMBOL(mdiobus_register); | |
140 | ||
141 | void mdiobus_unregister(struct mii_bus *bus) | |
142 | { | |
143 | int i; | |
144 | ||
46abc021 LB |
145 | BUG_ON(bus->state != MDIOBUS_REGISTERED); |
146 | bus->state = MDIOBUS_UNREGISTERED; | |
147 | ||
3e44017b | 148 | device_del(&bus->dev); |
e1393456 | 149 | for (i = 0; i < PHY_MAX_ADDR; i++) { |
6f4a7f41 | 150 | if (bus->phy_map[i]) |
e1393456 | 151 | device_unregister(&bus->phy_map[i]->dev); |
4dea547f | 152 | bus->phy_map[i] = NULL; |
e1393456 AF |
153 | } |
154 | } | |
155 | EXPORT_SYMBOL(mdiobus_unregister); | |
156 | ||
298cf9be LB |
157 | /** |
158 | * mdiobus_free - free a struct mii_bus | |
159 | * @bus: mii_bus to free | |
160 | * | |
46abc021 LB |
161 | * This function releases the reference to the underlying device |
162 | * object in the mii_bus. If this is the last reference, the mii_bus | |
163 | * will be freed. | |
298cf9be LB |
164 | */ |
165 | void mdiobus_free(struct mii_bus *bus) | |
166 | { | |
46abc021 LB |
167 | /* |
168 | * For compatibility with error handling in drivers. | |
169 | */ | |
170 | if (bus->state == MDIOBUS_ALLOCATED) { | |
171 | kfree(bus); | |
172 | return; | |
173 | } | |
174 | ||
175 | BUG_ON(bus->state != MDIOBUS_UNREGISTERED); | |
176 | bus->state = MDIOBUS_RELEASED; | |
177 | ||
178 | put_device(&bus->dev); | |
298cf9be LB |
179 | } |
180 | EXPORT_SYMBOL(mdiobus_free); | |
181 | ||
4fd5f812 LB |
182 | struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr) |
183 | { | |
184 | struct phy_device *phydev; | |
185 | int err; | |
186 | ||
187 | phydev = get_phy_device(bus, addr); | |
188 | if (IS_ERR(phydev) || phydev == NULL) | |
189 | return phydev; | |
190 | ||
4dea547f | 191 | err = phy_device_register(phydev); |
4fd5f812 | 192 | if (err) { |
4fd5f812 | 193 | phy_device_free(phydev); |
4dea547f | 194 | return NULL; |
4fd5f812 LB |
195 | } |
196 | ||
4fd5f812 LB |
197 | return phydev; |
198 | } | |
199 | EXPORT_SYMBOL(mdiobus_scan); | |
200 | ||
2e888103 LB |
201 | /** |
202 | * mdiobus_read - Convenience function for reading a given MII mgmt register | |
203 | * @bus: the mii_bus struct | |
204 | * @addr: the phy address | |
205 | * @regnum: register number to read | |
206 | * | |
207 | * NOTE: MUST NOT be called from interrupt context, | |
208 | * because the bus read/write functions may wait for an interrupt | |
209 | * to conclude the operation. | |
210 | */ | |
211 | int mdiobus_read(struct mii_bus *bus, int addr, u16 regnum) | |
212 | { | |
213 | int retval; | |
214 | ||
215 | BUG_ON(in_interrupt()); | |
216 | ||
217 | mutex_lock(&bus->mdio_lock); | |
218 | retval = bus->read(bus, addr, regnum); | |
219 | mutex_unlock(&bus->mdio_lock); | |
220 | ||
221 | return retval; | |
222 | } | |
223 | EXPORT_SYMBOL(mdiobus_read); | |
224 | ||
225 | /** | |
226 | * mdiobus_write - Convenience function for writing a given MII mgmt register | |
227 | * @bus: the mii_bus struct | |
228 | * @addr: the phy address | |
229 | * @regnum: register number to write | |
230 | * @val: value to write to @regnum | |
231 | * | |
232 | * NOTE: MUST NOT be called from interrupt context, | |
233 | * because the bus read/write functions may wait for an interrupt | |
234 | * to conclude the operation. | |
235 | */ | |
236 | int mdiobus_write(struct mii_bus *bus, int addr, u16 regnum, u16 val) | |
237 | { | |
238 | int err; | |
239 | ||
240 | BUG_ON(in_interrupt()); | |
241 | ||
242 | mutex_lock(&bus->mdio_lock); | |
243 | err = bus->write(bus, addr, regnum, val); | |
244 | mutex_unlock(&bus->mdio_lock); | |
245 | ||
246 | return err; | |
247 | } | |
248 | EXPORT_SYMBOL(mdiobus_write); | |
249 | ||
b3df0da8 RD |
250 | /** |
251 | * mdio_bus_match - determine if given PHY driver supports the given PHY device | |
252 | * @dev: target PHY device | |
253 | * @drv: given PHY driver | |
00db8189 | 254 | * |
b3df0da8 RD |
255 | * Description: Given a PHY device, and a PHY driver, return 1 if |
256 | * the driver supports the device. Otherwise, return 0. | |
00db8189 AF |
257 | */ |
258 | static int mdio_bus_match(struct device *dev, struct device_driver *drv) | |
259 | { | |
260 | struct phy_device *phydev = to_phy_device(dev); | |
261 | struct phy_driver *phydrv = to_phy_driver(drv); | |
262 | ||
5f708dd9 KG |
263 | return ((phydrv->phy_id & phydrv->phy_id_mask) == |
264 | (phydev->phy_id & phydrv->phy_id_mask)); | |
00db8189 AF |
265 | } |
266 | ||
3d1e4db2 AV |
267 | static bool mdio_bus_phy_may_suspend(struct phy_device *phydev) |
268 | { | |
269 | struct device_driver *drv = phydev->dev.driver; | |
270 | struct phy_driver *phydrv = to_phy_driver(drv); | |
271 | struct net_device *netdev = phydev->attached_dev; | |
272 | ||
273 | if (!drv || !phydrv->suspend) | |
274 | return false; | |
275 | ||
276 | /* PHY not attached? May suspend. */ | |
277 | if (!netdev) | |
278 | return true; | |
279 | ||
280 | /* | |
281 | * Don't suspend PHY if the attched netdev parent may wakeup. | |
282 | * The parent may point to a PCI device, as in tg3 driver. | |
283 | */ | |
284 | if (netdev->dev.parent && device_may_wakeup(netdev->dev.parent)) | |
285 | return false; | |
286 | ||
287 | /* | |
288 | * Also don't suspend PHY if the netdev itself may wakeup. This | |
289 | * is the case for devices w/o underlaying pwr. mgmt. aware bus, | |
290 | * e.g. SoC devices. | |
291 | */ | |
292 | if (device_may_wakeup(&netdev->dev)) | |
293 | return false; | |
294 | ||
295 | return true; | |
296 | } | |
297 | ||
00db8189 AF |
298 | /* Suspend and resume. Copied from platform_suspend and |
299 | * platform_resume | |
300 | */ | |
829ca9a3 | 301 | static int mdio_bus_suspend(struct device * dev, pm_message_t state) |
00db8189 | 302 | { |
3d1e4db2 | 303 | struct phy_driver *phydrv = to_phy_driver(dev->driver); |
0f0ca340 | 304 | struct phy_device *phydev = to_phy_device(dev); |
00db8189 | 305 | |
3d1e4db2 AV |
306 | if (!mdio_bus_phy_may_suspend(phydev)) |
307 | return 0; | |
308 | return phydrv->suspend(phydev); | |
00db8189 AF |
309 | } |
310 | ||
311 | static int mdio_bus_resume(struct device * dev) | |
312 | { | |
3d1e4db2 | 313 | struct phy_driver *phydrv = to_phy_driver(dev->driver); |
0f0ca340 | 314 | struct phy_device *phydev = to_phy_device(dev); |
00db8189 | 315 | |
3d1e4db2 AV |
316 | if (!mdio_bus_phy_may_suspend(phydev)) |
317 | return 0; | |
318 | return phydrv->resume(phydev); | |
00db8189 AF |
319 | } |
320 | ||
321 | struct bus_type mdio_bus_type = { | |
322 | .name = "mdio_bus", | |
323 | .match = mdio_bus_match, | |
324 | .suspend = mdio_bus_suspend, | |
325 | .resume = mdio_bus_resume, | |
326 | }; | |
11b0bacd | 327 | EXPORT_SYMBOL(mdio_bus_type); |
00db8189 | 328 | |
67c4f3fa | 329 | int __init mdio_bus_init(void) |
00db8189 | 330 | { |
46abc021 LB |
331 | int ret; |
332 | ||
333 | ret = class_register(&mdio_bus_class); | |
334 | if (!ret) { | |
335 | ret = bus_register(&mdio_bus_type); | |
336 | if (ret) | |
337 | class_unregister(&mdio_bus_class); | |
338 | } | |
339 | ||
340 | return ret; | |
00db8189 AF |
341 | } |
342 | ||
dc85dec6 | 343 | void mdio_bus_exit(void) |
e1393456 | 344 | { |
46abc021 | 345 | class_unregister(&mdio_bus_class); |
e1393456 AF |
346 | bus_unregister(&mdio_bus_type); |
347 | } |