diff options
Diffstat (limited to 'drivers/net/phy/phy_device.c')
-rw-r--r-- | drivers/net/phy/phy_device.c | 172 |
1 files changed, 148 insertions, 24 deletions
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index c44d54f6310a..33f7bdb5857c 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c | |||
@@ -39,18 +39,9 @@ | |||
39 | #include <asm/irq.h> | 39 | #include <asm/irq.h> |
40 | #include <asm/uaccess.h> | 40 | #include <asm/uaccess.h> |
41 | 41 | ||
42 | static int genphy_config_init(struct phy_device *phydev); | 42 | static struct phy_driver genphy_driver; |
43 | 43 | extern int mdio_bus_init(void); | |
44 | static struct phy_driver genphy_driver = { | 44 | extern void mdio_bus_exit(void); |
45 | .phy_id = 0xffffffff, | ||
46 | .phy_id_mask = 0xffffffff, | ||
47 | .name = "Generic PHY", | ||
48 | .config_init = genphy_config_init, | ||
49 | .features = 0, | ||
50 | .config_aneg = genphy_config_aneg, | ||
51 | .read_status = genphy_read_status, | ||
52 | .driver = {.owner = THIS_MODULE, }, | ||
53 | }; | ||
54 | 45 | ||
55 | /* get_phy_device | 46 | /* get_phy_device |
56 | * | 47 | * |
@@ -110,6 +101,7 @@ struct phy_device * get_phy_device(struct mii_bus *bus, int addr) | |||
110 | return dev; | 101 | return dev; |
111 | } | 102 | } |
112 | 103 | ||
104 | #ifdef CONFIG_PHYCONTROL | ||
113 | /* phy_prepare_link: | 105 | /* phy_prepare_link: |
114 | * | 106 | * |
115 | * description: Tells the PHY infrastructure to handle the | 107 | * description: Tells the PHY infrastructure to handle the |
@@ -124,6 +116,132 @@ void phy_prepare_link(struct phy_device *phydev, | |||
124 | phydev->adjust_link = handler; | 116 | phydev->adjust_link = handler; |
125 | } | 117 | } |
126 | 118 | ||
119 | /* phy_connect: | ||
120 | * | ||
121 | * description: Convenience function for connecting ethernet | ||
122 | * devices to PHY devices. The default behavior is for | ||
123 | * the PHY infrastructure to handle everything, and only notify | ||
124 | * the connected driver when the link status changes. If you | ||
125 | * don't want, or can't use the provided functionality, you may | ||
126 | * choose to call only the subset of functions which provide | ||
127 | * the desired functionality. | ||
128 | */ | ||
129 | struct phy_device * phy_connect(struct net_device *dev, const char *phy_id, | ||
130 | void (*handler)(struct net_device *), u32 flags) | ||
131 | { | ||
132 | struct phy_device *phydev; | ||
133 | |||
134 | phydev = phy_attach(dev, phy_id, flags); | ||
135 | |||
136 | if (IS_ERR(phydev)) | ||
137 | return phydev; | ||
138 | |||
139 | phy_prepare_link(phydev, handler); | ||
140 | |||
141 | phy_start_machine(phydev, NULL); | ||
142 | |||
143 | if (phydev->irq > 0) | ||
144 | phy_start_interrupts(phydev); | ||
145 | |||
146 | return phydev; | ||
147 | } | ||
148 | EXPORT_SYMBOL(phy_connect); | ||
149 | |||
150 | void phy_disconnect(struct phy_device *phydev) | ||
151 | { | ||
152 | if (phydev->irq > 0) | ||
153 | phy_stop_interrupts(phydev); | ||
154 | |||
155 | phy_stop_machine(phydev); | ||
156 | |||
157 | phydev->adjust_link = NULL; | ||
158 | |||
159 | phy_detach(phydev); | ||
160 | } | ||
161 | EXPORT_SYMBOL(phy_disconnect); | ||
162 | |||
163 | #endif /* CONFIG_PHYCONTROL */ | ||
164 | |||
165 | /* phy_attach: | ||
166 | * | ||
167 | * description: Called by drivers to attach to a particular PHY | ||
168 | * device. The phy_device is found, and properly hooked up | ||
169 | * to the phy_driver. If no driver is attached, then the | ||
170 | * genphy_driver is used. The phy_device is given a ptr to | ||
171 | * the attaching device, and given a callback for link status | ||
172 | * change. The phy_device is returned to the attaching | ||
173 | * driver. | ||
174 | */ | ||
175 | static int phy_compare_id(struct device *dev, void *data) | ||
176 | { | ||
177 | return strcmp((char *)data, dev->bus_id) ? 0 : 1; | ||
178 | } | ||
179 | |||
180 | struct phy_device *phy_attach(struct net_device *dev, | ||
181 | const char *phy_id, u32 flags) | ||
182 | { | ||
183 | struct bus_type *bus = &mdio_bus_type; | ||
184 | struct phy_device *phydev; | ||
185 | struct device *d; | ||
186 | |||
187 | /* Search the list of PHY devices on the mdio bus for the | ||
188 | * PHY with the requested name */ | ||
189 | d = bus_find_device(bus, NULL, (void *)phy_id, phy_compare_id); | ||
190 | |||
191 | if (d) { | ||
192 | phydev = to_phy_device(d); | ||
193 | } else { | ||
194 | printk(KERN_ERR "%s not found\n", phy_id); | ||
195 | return ERR_PTR(-ENODEV); | ||
196 | } | ||
197 | |||
198 | /* Assume that if there is no driver, that it doesn't | ||
199 | * exist, and we should use the genphy driver. */ | ||
200 | if (NULL == d->driver) { | ||
201 | int err; | ||
202 | down_write(&d->bus->subsys.rwsem); | ||
203 | d->driver = &genphy_driver.driver; | ||
204 | |||
205 | err = d->driver->probe(d); | ||
206 | |||
207 | if (err < 0) | ||
208 | return ERR_PTR(err); | ||
209 | |||
210 | device_bind_driver(d); | ||
211 | up_write(&d->bus->subsys.rwsem); | ||
212 | } | ||
213 | |||
214 | if (phydev->attached_dev) { | ||
215 | printk(KERN_ERR "%s: %s already attached\n", | ||
216 | dev->name, phy_id); | ||
217 | return ERR_PTR(-EBUSY); | ||
218 | } | ||
219 | |||
220 | phydev->attached_dev = dev; | ||
221 | |||
222 | phydev->dev_flags = flags; | ||
223 | |||
224 | return phydev; | ||
225 | } | ||
226 | EXPORT_SYMBOL(phy_attach); | ||
227 | |||
228 | void phy_detach(struct phy_device *phydev) | ||
229 | { | ||
230 | phydev->attached_dev = NULL; | ||
231 | |||
232 | /* If the device had no specific driver before (i.e. - it | ||
233 | * was using the generic driver), we unbind the device | ||
234 | * from the generic driver so that there's a chance a | ||
235 | * real driver could be loaded */ | ||
236 | if (phydev->dev.driver == &genphy_driver.driver) { | ||
237 | down_write(&phydev->dev.bus->subsys.rwsem); | ||
238 | device_release_driver(&phydev->dev); | ||
239 | up_write(&phydev->dev.bus->subsys.rwsem); | ||
240 | } | ||
241 | } | ||
242 | EXPORT_SYMBOL(phy_detach); | ||
243 | |||
244 | |||
127 | /* Generic PHY support and helper functions */ | 245 | /* Generic PHY support and helper functions */ |
128 | 246 | ||
129 | /* genphy_config_advert | 247 | /* genphy_config_advert |
@@ -132,7 +250,7 @@ void phy_prepare_link(struct phy_device *phydev, | |||
132 | * after sanitizing the values to make sure we only advertise | 250 | * after sanitizing the values to make sure we only advertise |
133 | * what is supported | 251 | * what is supported |
134 | */ | 252 | */ |
135 | static int genphy_config_advert(struct phy_device *phydev) | 253 | int genphy_config_advert(struct phy_device *phydev) |
136 | { | 254 | { |
137 | u32 advertise; | 255 | u32 advertise; |
138 | int adv; | 256 | int adv; |
@@ -190,6 +308,7 @@ static int genphy_config_advert(struct phy_device *phydev) | |||
190 | 308 | ||
191 | return adv; | 309 | return adv; |
192 | } | 310 | } |
311 | EXPORT_SYMBOL(genphy_config_advert); | ||
193 | 312 | ||
194 | /* genphy_setup_forced | 313 | /* genphy_setup_forced |
195 | * | 314 | * |
@@ -541,32 +660,37 @@ void phy_driver_unregister(struct phy_driver *drv) | |||
541 | } | 660 | } |
542 | EXPORT_SYMBOL(phy_driver_unregister); | 661 | EXPORT_SYMBOL(phy_driver_unregister); |
543 | 662 | ||
663 | static struct phy_driver genphy_driver = { | ||
664 | .phy_id = 0xffffffff, | ||
665 | .phy_id_mask = 0xffffffff, | ||
666 | .name = "Generic PHY", | ||
667 | .config_init = genphy_config_init, | ||
668 | .features = 0, | ||
669 | .config_aneg = genphy_config_aneg, | ||
670 | .read_status = genphy_read_status, | ||
671 | .driver = {.owner= THIS_MODULE, }, | ||
672 | }; | ||
544 | 673 | ||
545 | static int __init phy_init(void) | 674 | static int __init phy_init(void) |
546 | { | 675 | { |
547 | int rc; | 676 | int rc; |
548 | extern int mdio_bus_init(void); | ||
549 | |||
550 | rc = phy_driver_register(&genphy_driver); | ||
551 | if (rc) | ||
552 | goto out; | ||
553 | 677 | ||
554 | rc = mdio_bus_init(); | 678 | rc = mdio_bus_init(); |
555 | if (rc) | 679 | if (rc) |
556 | goto out_unreg; | 680 | return rc; |
557 | 681 | ||
558 | return 0; | 682 | rc = phy_driver_register(&genphy_driver); |
683 | if (rc) | ||
684 | mdio_bus_exit(); | ||
559 | 685 | ||
560 | out_unreg: | ||
561 | phy_driver_unregister(&genphy_driver); | ||
562 | out: | ||
563 | return rc; | 686 | return rc; |
564 | } | 687 | } |
565 | 688 | ||
566 | static void __exit phy_exit(void) | 689 | static void __exit phy_exit(void) |
567 | { | 690 | { |
568 | phy_driver_unregister(&genphy_driver); | 691 | phy_driver_unregister(&genphy_driver); |
692 | mdio_bus_exit(); | ||
569 | } | 693 | } |
570 | 694 | ||
571 | module_init(phy_init); | 695 | subsys_initcall(phy_init); |
572 | module_exit(phy_exit); | 696 | module_exit(phy_exit); |