[PATCH] PHY Layer fixup

This patch adds back the code that was taken out, thus re-enabling:

* The PHY Layer to initialize without crashing
* Drivers to actually connect to PHYs
* The entire PHY Control Layer

This patch is used by the gianfar driver, and other drivers which are in
development.

Signed-off-by: Andy Fleming <afleming@freescale.com>
Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index c44d54f..33f7bdb 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 @@
 	return dev;
 }
 
+#ifdef CONFIG_PHYCONTROL
 /* phy_prepare_link:
  *
  * description: Tells the PHY infrastructure to handle the
@@ -124,6 +116,132 @@
 	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 @@
  *   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 @@
 
 	return adv;
 }
+EXPORT_SYMBOL(genphy_config_advert);
 
 /* genphy_setup_forced
  *
@@ -541,32 +660,37 @@
 }
 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);