aboutsummaryrefslogtreecommitdiff
path: root/drivers/net/phy/phy_device.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/phy/phy_device.c')
-rw-r--r--drivers/net/phy/phy_device.c172
1 files changed, 148 insertions, 24 deletions
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index c44d54f6310..33f7bdb5857 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -39,18 +39,9 @@
#include <asm/irq.h>
#include <asm/uaccess.h>
-static int genphy_config_init(struct phy_device *phydev);
-
-static struct phy_driver genphy_driver = {
- .phy_id = 0xffffffff,
- .phy_id_mask = 0xffffffff,
- .name = "Generic PHY",
- .config_init = genphy_config_init,
- .features = 0,
- .config_aneg = genphy_config_aneg,
- .read_status = genphy_read_status,
- .driver = {.owner = THIS_MODULE, },
-};
+static struct phy_driver genphy_driver;
+extern int mdio_bus_init(void);
+extern void mdio_bus_exit(void);
/* get_phy_device
*
@@ -110,6 +101,7 @@ struct phy_device * get_phy_device(struct mii_bus *bus, int addr)
return dev;
}
+#ifdef CONFIG_PHYCONTROL
/* phy_prepare_link:
*
* description: Tells the PHY infrastructure to handle the
@@ -124,6 +116,132 @@ void phy_prepare_link(struct phy_device *phydev,
phydev->adjust_link = handler;
}
+/* phy_connect:
+ *
+ * description: Convenience function for connecting ethernet
+ * devices to PHY devices. The default behavior is for
+ * the PHY infrastructure to handle everything, and only notify
+ * the connected driver when the link status changes. If you
+ * don't want, or can't use the provided functionality, you may
+ * choose to call only the subset of functions which provide
+ * the desired functionality.
+ */
+struct phy_device * phy_connect(struct net_device *dev, const char *phy_id,
+ void (*handler)(struct net_device *), u32 flags)
+{
+ struct phy_device *phydev;
+
+ phydev = phy_attach(dev, phy_id, flags);
+
+ if (IS_ERR(phydev))
+ return phydev;
+
+ phy_prepare_link(phydev, handler);
+
+ phy_start_machine(phydev, NULL);
+
+ if (phydev->irq > 0)
+ phy_start_interrupts(phydev);
+
+ return phydev;
+}
+EXPORT_SYMBOL(phy_connect);
+
+void phy_disconnect(struct phy_device *phydev)
+{
+ if (phydev->irq > 0)
+ phy_stop_interrupts(phydev);
+
+ phy_stop_machine(phydev);
+
+ phydev->adjust_link = NULL;
+
+ phy_detach(phydev);
+}
+EXPORT_SYMBOL(phy_disconnect);
+
+#endif /* CONFIG_PHYCONTROL */
+
+/* phy_attach:
+ *
+ * description: Called by drivers to attach to a particular PHY
+ * device. The phy_device is found, and properly hooked up
+ * to the phy_driver. If no driver is attached, then the
+ * genphy_driver is used. The phy_device is given a ptr to
+ * the attaching device, and given a callback for link status
+ * change. The phy_device is returned to the attaching
+ * driver.
+ */
+static int phy_compare_id(struct device *dev, void *data)
+{
+ return strcmp((char *)data, dev->bus_id) ? 0 : 1;
+}
+
+struct phy_device *phy_attach(struct net_device *dev,
+ const char *phy_id, u32 flags)
+{
+ struct bus_type *bus = &mdio_bus_type;
+ struct phy_device *phydev;
+ struct device *d;
+
+ /* Search the list of PHY devices on the mdio bus for the
+ * PHY with the requested name */
+ d = bus_find_device(bus, NULL, (void *)phy_id, phy_compare_id);
+
+ if (d) {
+ phydev = to_phy_device(d);
+ } else {
+ printk(KERN_ERR "%s not found\n", phy_id);
+ return ERR_PTR(-ENODEV);
+ }
+
+ /* Assume that if there is no driver, that it doesn't
+ * exist, and we should use the genphy driver. */
+ if (NULL == d->driver) {
+ int err;
+ down_write(&d->bus->subsys.rwsem);
+ d->driver = &genphy_driver.driver;
+
+ err = d->driver->probe(d);
+
+ if (err < 0)
+ return ERR_PTR(err);
+
+ device_bind_driver(d);
+ up_write(&d->bus->subsys.rwsem);
+ }
+
+ if (phydev->attached_dev) {
+ printk(KERN_ERR "%s: %s already attached\n",
+ dev->name, phy_id);
+ return ERR_PTR(-EBUSY);
+ }
+
+ phydev->attached_dev = dev;
+
+ phydev->dev_flags = flags;
+
+ return phydev;
+}
+EXPORT_SYMBOL(phy_attach);
+
+void phy_detach(struct phy_device *phydev)
+{
+ phydev->attached_dev = NULL;
+
+ /* If the device had no specific driver before (i.e. - it
+ * was using the generic driver), we unbind the device
+ * from the generic driver so that there's a chance a
+ * real driver could be loaded */
+ if (phydev->dev.driver == &genphy_driver.driver) {
+ down_write(&phydev->dev.bus->subsys.rwsem);
+ device_release_driver(&phydev->dev);
+ up_write(&phydev->dev.bus->subsys.rwsem);
+ }
+}
+EXPORT_SYMBOL(phy_detach);
+
+
/* Generic PHY support and helper functions */
/* genphy_config_advert
@@ -132,7 +250,7 @@ void phy_prepare_link(struct phy_device *phydev,
* after sanitizing the values to make sure we only advertise
* what is supported
*/
-static int genphy_config_advert(struct phy_device *phydev)
+int genphy_config_advert(struct phy_device *phydev)
{
u32 advertise;
int adv;
@@ -190,6 +308,7 @@ static int genphy_config_advert(struct phy_device *phydev)
return adv;
}
+EXPORT_SYMBOL(genphy_config_advert);
/* genphy_setup_forced
*
@@ -541,32 +660,37 @@ void phy_driver_unregister(struct phy_driver *drv)
}
EXPORT_SYMBOL(phy_driver_unregister);
+static struct phy_driver genphy_driver = {
+ .phy_id = 0xffffffff,
+ .phy_id_mask = 0xffffffff,
+ .name = "Generic PHY",
+ .config_init = genphy_config_init,
+ .features = 0,
+ .config_aneg = genphy_config_aneg,
+ .read_status = genphy_read_status,
+ .driver = {.owner= THIS_MODULE, },
+};
static int __init phy_init(void)
{
int rc;
- extern int mdio_bus_init(void);
-
- rc = phy_driver_register(&genphy_driver);
- if (rc)
- goto out;
rc = mdio_bus_init();
if (rc)
- goto out_unreg;
+ return rc;
- return 0;
+ rc = phy_driver_register(&genphy_driver);
+ if (rc)
+ mdio_bus_exit();
-out_unreg:
- phy_driver_unregister(&genphy_driver);
-out:
return rc;
}
static void __exit phy_exit(void)
{
phy_driver_unregister(&genphy_driver);
+ mdio_bus_exit();
}
-module_init(phy_init);
+subsys_initcall(phy_init);
module_exit(phy_exit);