]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/net/phy/phy_device.c
net: phy: Add detection of 1000BaseX link mode support
[linux.git] / drivers / net / phy / phy_device.c
index dcc93a8731744e2e4b3e54e71291893e525e0c1f..03c885ec1f9818965b0a186d8eebceb6024fd70b 100644 (file)
@@ -89,7 +89,7 @@ EXPORT_SYMBOL_GPL(phy_10_100_features_array);
 
 const int phy_basic_t1_features_array[2] = {
        ETHTOOL_LINK_MODE_TP_BIT,
-       ETHTOOL_LINK_MODE_100baseT_Full_BIT,
+       ETHTOOL_LINK_MODE_100baseT1_Full_BIT,
 };
 EXPORT_SYMBOL_GPL(phy_basic_t1_features_array);
 
@@ -948,6 +948,9 @@ int phy_connect_direct(struct net_device *dev, struct phy_device *phydev,
 {
        int rc;
 
+       if (!dev)
+               return -EINVAL;
+
        rc = phy_attach_direct(dev, phydev, phydev->dev_flags, interface);
        if (rc)
                return rc;
@@ -1013,7 +1016,7 @@ void phy_disconnect(struct phy_device *phydev)
                phy_stop(phydev);
 
        if (phy_interrupt_is_valid(phydev))
-               free_irq(phydev->irq, phydev);
+               phy_free_interrupt(phydev);
 
        phydev->adjust_link = NULL;
 
@@ -1133,6 +1136,44 @@ void phy_attached_print(struct phy_device *phydev, const char *fmt, ...)
 }
 EXPORT_SYMBOL(phy_attached_print);
 
+static void phy_sysfs_create_links(struct phy_device *phydev)
+{
+       struct net_device *dev = phydev->attached_dev;
+       int err;
+
+       if (!dev)
+               return;
+
+       err = sysfs_create_link(&phydev->mdio.dev.kobj, &dev->dev.kobj,
+                               "attached_dev");
+       if (err)
+               return;
+
+       err = sysfs_create_link_nowarn(&dev->dev.kobj,
+                                      &phydev->mdio.dev.kobj,
+                                      "phydev");
+       if (err) {
+               dev_err(&dev->dev, "could not add device link to %s err %d\n",
+                       kobject_name(&phydev->mdio.dev.kobj),
+                       err);
+               /* non-fatal - some net drivers can use one netdevice
+                * with more then one phy
+                */
+       }
+
+       phydev->sysfs_links = true;
+}
+
+static ssize_t
+phy_standalone_show(struct device *dev, struct device_attribute *attr,
+                   char *buf)
+{
+       struct phy_device *phydev = to_phy_device(dev);
+
+       return sprintf(buf, "%d\n", !phydev->attached_dev);
+}
+static DEVICE_ATTR_RO(phy_standalone);
+
 /**
  * phy_attach_direct - attach a network device to a given PHY device pointer
  * @dev: network device to attach
@@ -1151,9 +1192,9 @@ EXPORT_SYMBOL(phy_attached_print);
 int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
                      u32 flags, phy_interface_t interface)
 {
-       struct module *ndev_owner = dev->dev.parent->driver->owner;
        struct mii_bus *bus = phydev->mdio.bus;
        struct device *d = &phydev->mdio.dev;
+       struct module *ndev_owner = NULL;
        bool using_genphy = false;
        int err;
 
@@ -1162,8 +1203,10 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
         * our own module->refcnt here, otherwise we would not be able to
         * unload later on.
         */
+       if (dev)
+               ndev_owner = dev->dev.parent->driver->owner;
        if (ndev_owner != bus->owner && !try_module_get(bus->owner)) {
-               dev_err(&dev->dev, "failed to get the bus module\n");
+               phydev_err(phydev, "failed to get the bus module\n");
                return -EIO;
        }
 
@@ -1182,7 +1225,7 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
        }
 
        if (!try_module_get(d->driver->owner)) {
-               dev_err(&dev->dev, "failed to get the device driver module\n");
+               phydev_err(phydev, "failed to get the device driver module\n");
                err = -EIO;
                goto error_put_device;
        }
@@ -1203,8 +1246,10 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
        }
 
        phydev->phy_link_change = phy_link_change;
-       phydev->attached_dev = dev;
-       dev->phydev = phydev;
+       if (dev) {
+               phydev->attached_dev = dev;
+               dev->phydev = phydev;
+       }
 
        /* Some Ethernet drivers try to connect to a PHY device before
         * calling register_netdevice() -> netdev_register_kobject() and
@@ -1216,22 +1261,13 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
         */
        phydev->sysfs_links = false;
 
-       err = sysfs_create_link(&phydev->mdio.dev.kobj, &dev->dev.kobj,
-                               "attached_dev");
-       if (!err) {
-               err = sysfs_create_link_nowarn(&dev->dev.kobj,
-                                              &phydev->mdio.dev.kobj,
-                                              "phydev");
-               if (err) {
-                       dev_err(&dev->dev, "could not add device link to %s err %d\n",
-                               kobject_name(&phydev->mdio.dev.kobj),
-                               err);
-                       /* non-fatal - some net drivers can use one netdevice
-                        * with more then one phy
-                        */
-               }
+       phy_sysfs_create_links(phydev);
 
-               phydev->sysfs_links = true;
+       if (!phydev->attached_dev) {
+               err = sysfs_create_file(&phydev->mdio.dev.kobj,
+                                       &dev_attr_phy_standalone.attr);
+               if (err)
+                       phydev_err(phydev, "error creating 'phy_standalone' sysfs entry\n");
        }
 
        phydev->dev_flags = flags;
@@ -1243,7 +1279,8 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
        /* Initial carrier state is off as the phy is about to be
         * (re)initialized.
         */
-       netif_carrier_off(phydev->attached_dev);
+       if (dev)
+               netif_carrier_off(phydev->attached_dev);
 
        /* Do initial configuration here, now that
         * we have certain key parameters
@@ -1290,6 +1327,9 @@ struct phy_device *phy_attach(struct net_device *dev, const char *bus_id,
        struct device *d;
        int rc;
 
+       if (!dev)
+               return ERR_PTR(-EINVAL);
+
        /* Search the list of PHY devices on the mdio bus for the
         * PHY with the requested name
         */
@@ -1349,16 +1389,24 @@ EXPORT_SYMBOL_GPL(phy_driver_is_genphy_10g);
 void phy_detach(struct phy_device *phydev)
 {
        struct net_device *dev = phydev->attached_dev;
-       struct module *ndev_owner = dev->dev.parent->driver->owner;
+       struct module *ndev_owner = NULL;
        struct mii_bus *bus;
 
        if (phydev->sysfs_links) {
-               sysfs_remove_link(&dev->dev.kobj, "phydev");
+               if (dev)
+                       sysfs_remove_link(&dev->dev.kobj, "phydev");
                sysfs_remove_link(&phydev->mdio.dev.kobj, "attached_dev");
        }
+
+       if (!phydev->attached_dev)
+               sysfs_remove_file(&phydev->mdio.dev.kobj,
+                                 &dev_attr_phy_standalone.attr);
+
        phy_suspend(phydev);
-       phydev->attached_dev->phydev = NULL;
-       phydev->attached_dev = NULL;
+       if (dev) {
+               phydev->attached_dev->phydev = NULL;
+               phydev->attached_dev = NULL;
+       }
        phydev->phylink = NULL;
 
        phy_led_triggers_unregister(phydev);
@@ -1381,6 +1429,8 @@ void phy_detach(struct phy_device *phydev)
        bus = phydev->mdio.bus;
 
        put_device(&phydev->mdio.dev);
+       if (dev)
+               ndev_owner = dev->dev.parent->driver->owner;
        if (ndev_owner != bus->owner)
                module_put(bus->owner);
 
@@ -1880,6 +1930,9 @@ int genphy_config_init(struct phy_device *phydev)
                if (val & ESTATUS_1000_THALF)
                        linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
                                         features);
+               if (val & ESTATUS_1000_XFULL)
+                       linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
+                                        features);
        }
 
        linkmode_and(phydev->supported, phydev->supported, features);