[PATCH] PHY Layer fixup
[safe/jmp/linux-2.6] / drivers / net / phy / phy_device.c
index c44d54f..33f7bdb 100644 (file)
 #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);