From 5f0a71708e04ae7c5c0d4ef859d680bcce44dd86 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 11 Dec 2013 10:18:40 +0100 Subject: [PATCH 01/14] net: phy: cleanup attached device handling phy_register_device() currently requires an attached ethernet device. This is not needed, a phy device can equally well be registered as a standalone device without an ethernet device. Remove the need for an attached ethernet device in phy_register_device. Also, make the edev <-> phy connection on a registered device which simplifies the code. Signed-off-by: Sascha Hauer --- drivers/net/phy/mdio_bus.c | 4 ---- drivers/net/phy/phy.c | 14 +++----------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 87072be28..b0fbf2df3 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -153,8 +153,6 @@ static int mdio_bus_probe(struct device_d *_dev) int ret; - dev->attached_dev->phydev = dev; - if (drv->probe) { ret = drv->probe(dev); if (ret) @@ -204,8 +202,6 @@ static int mdio_bus_probe(struct device_d *_dev) return 0; err: - dev->attached_dev->phydev = NULL; - dev->attached_dev = NULL; return ret; } diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 2a3305458..d8966cd46 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -228,7 +228,7 @@ static int phy_register_device(struct phy_device* dev) { int ret; - dev->dev.parent = &dev->attached_dev->dev; + dev->dev.parent = &dev->bus->dev; ret = register_device(&dev->dev); if (ret) @@ -259,7 +259,6 @@ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr, goto fail; } - dev->attached_dev = edev; dev->interface = interface; dev->dev_flags = flags; @@ -276,7 +275,6 @@ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr, if (IS_ERR(dev) || dev->attached_dev) continue; - dev->attached_dev = edev; dev->interface = interface; dev->dev_flags = flags; @@ -287,14 +285,10 @@ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr, break; } } - - if (!edev->phydev) { - ret = -EIO; - goto fail; - } } - dev = edev->phydev; + edev->phydev = dev; + dev->attached_dev = edev; drv = to_phy_driver(dev->dev.driver); drv->config_aneg(dev); @@ -304,8 +298,6 @@ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr, return 0; fail: - if (!IS_ERR(dev)) - dev->attached_dev = NULL; puts("Unable to find a PHY (unknown ID?)\n"); return ret; } From 58f9167a04e5c780f07a0488a1cd9c71d28ffc08 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 11 Dec 2013 10:42:44 +0100 Subject: [PATCH 02/14] net: phy: bail out early in phy_device_connect If an ethernet device already has a phy in phy_device_connect all we have to do is to start autonegotiation. Do this early and bail out so that for the rest of the code it's clear that we have to search for a phy device. Signed-off-by: Sascha Hauer --- drivers/net/phy/phy.c | 66 ++++++++++++++++++++++++------------------- include/linux/phy.h | 1 - 2 files changed, 37 insertions(+), 30 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index d8966cd46..a83b35c14 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -224,6 +224,14 @@ struct phy_device *get_phy_device(struct mii_bus *bus, int addr) return dev; } +static void phy_config_aneg(struct phy_device *phydev) +{ + struct phy_driver *drv; + + drv = to_phy_driver(phydev->dev.driver); + drv->config_aneg(phydev); +} + static int phy_register_device(struct phy_device* dev) { int ret; @@ -246,18 +254,37 @@ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr, void (*adjust_link) (struct eth_device *edev), u32 flags, phy_interface_t interface) { - struct phy_driver* drv; struct phy_device* dev = NULL; unsigned int i; int ret = -EINVAL; - if (!edev->phydev) { - if (addr >= 0) { - dev = mdiobus_scan(bus, addr); - if (IS_ERR(dev)) { - ret = -EIO; - goto fail; - } + if (edev->phydev) { + phy_config_aneg(edev->phydev); + return 0; + } + + if (addr >= 0) { + dev = mdiobus_scan(bus, addr); + if (IS_ERR(dev)) { + ret = -EIO; + goto fail; + } + + dev->interface = interface; + dev->dev_flags = flags; + + ret = phy_register_device(dev); + if (ret) + goto fail; + } else { + for (i = 0; i < PHY_MAX_ADDR && !edev->phydev; i++) { + /* skip masked out PHY addresses */ + if (bus->phy_mask & (1 << i)) + continue; + + dev = mdiobus_scan(bus, i); + if (IS_ERR(dev) || dev->attached_dev) + continue; dev->interface = interface; dev->dev_flags = flags; @@ -265,33 +292,14 @@ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr, ret = phy_register_device(dev); if (ret) goto fail; - } else { - for (i = 0; i < PHY_MAX_ADDR && !edev->phydev; i++) { - /* skip masked out PHY addresses */ - if (bus->phy_mask & (1 << i)) - continue; - dev = mdiobus_scan(bus, i); - if (IS_ERR(dev) || dev->attached_dev) - continue; - - dev->interface = interface; - dev->dev_flags = flags; - - ret = phy_register_device(dev); - if (ret) - goto fail; - - break; - } + break; } } edev->phydev = dev; dev->attached_dev = edev; - drv = to_phy_driver(dev->dev.driver); - - drv->config_aneg(dev); + phy_config_aneg(edev->phydev); dev->adjust_link = adjust_link; diff --git a/include/linux/phy.h b/include/linux/phy.h index 94f631b18..a1c629e9d 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -161,7 +161,6 @@ struct phy_device { int autoneg; int force; - /* private data pointer */ /* For use by PHYs to maintain extra state */ void *priv; From 664694d9f72ae8782f9d923e8813e2f51df2a2b7 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 11 Dec 2013 10:53:22 +0100 Subject: [PATCH 03/14] net: phy: move duplicated code out of if/else Signed-off-by: Sascha Hauer --- drivers/net/phy/phy.c | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index a83b35c14..fb4cb118b 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -269,13 +269,6 @@ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr, ret = -EIO; goto fail; } - - dev->interface = interface; - dev->dev_flags = flags; - - ret = phy_register_device(dev); - if (ret) - goto fail; } else { for (i = 0; i < PHY_MAX_ADDR && !edev->phydev; i++) { /* skip masked out PHY addresses */ @@ -283,20 +276,18 @@ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr, continue; dev = mdiobus_scan(bus, i); - if (IS_ERR(dev) || dev->attached_dev) - continue; - - dev->interface = interface; - dev->dev_flags = flags; - - ret = phy_register_device(dev); - if (ret) - goto fail; - - break; + if (!IS_ERR(dev) && !dev->attached_dev) + break; } } + dev->interface = interface; + dev->dev_flags = flags; + + ret = phy_register_device(dev); + if (ret) + goto fail; + edev->phydev = dev; dev->attached_dev = edev; phy_config_aneg(edev->phydev); From d023d690fc6fd051faa77666777d9664e0e5e7e3 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 11 Dec 2013 10:56:29 +0100 Subject: [PATCH 04/14] net: phy: check if a phy already has an ethernet device If during a phy_device_connect a phy already has an ehternet device this can only mean it's already attached to another device. return -EBUSY in this case. Signed-off-by: Sascha Hauer --- drivers/net/phy/phy.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index fb4cb118b..37f664702 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -281,6 +281,9 @@ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr, } } + if (dev->attached_dev) + return -EBUSY; + dev->interface = interface; dev->dev_flags = flags; From 33790253da63facab20f5ae664debba0627344df Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 11 Dec 2013 10:58:00 +0100 Subject: [PATCH 05/14] net: phy: track registered state of a phy device With this phy_device_connect only registers a phy device if it wasn't registered already. This allows us to register phy devices outside of ethernet drivers. phy_device_connect will now pick up an already registered phy given that it's not attached to another ethernet device. Signed-off-by: Sascha Hauer --- drivers/net/phy/phy.c | 21 +++++++++++++++++---- include/linux/phy.h | 2 ++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 37f664702..74ef3d962 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -236,17 +236,28 @@ static int phy_register_device(struct phy_device* dev) { int ret; + if (dev->registered) + return -EBUSY; + dev->dev.parent = &dev->bus->dev; ret = register_device(&dev->dev); if (ret) return ret; + dev->registered = 1; + if (dev->dev.driver) return 0; dev->dev.driver = &genphy_driver.drv; - return device_probe(&dev->dev); + ret = device_probe(&dev->dev); + if (ret) { + unregister_device(&dev->dev); + dev->registered = 0; + } + + return ret; } /* Automatically gets and returns the PHY device */ @@ -287,9 +298,11 @@ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr, dev->interface = interface; dev->dev_flags = flags; - ret = phy_register_device(dev); - if (ret) - goto fail; + if (!dev->registered) { + ret = phy_register_device(dev); + if (ret) + goto fail; + } edev->phydev = dev; dev->attached_dev = edev; diff --git a/include/linux/phy.h b/include/linux/phy.h index a1c629e9d..19e4d2066 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -161,6 +161,8 @@ struct phy_device { int autoneg; int force; + int registered; + /* private data pointer */ /* For use by PHYs to maintain extra state */ void *priv; From 5200678e86f76de80abbad52eb49a6a9ea8081f1 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 11 Dec 2013 11:09:46 +0100 Subject: [PATCH 06/14] net: phy: move phy_init_hw to phy_device_connect This is needed when a phy gets registered outsize of phy_device_connect but has to be attached to an ethernet device later. Signed-off-by: Sascha Hauer --- drivers/net/phy/mdio_bus.c | 8 -------- drivers/net/phy/phy.c | 9 +++++++++ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index b0fbf2df3..84c05adba 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -181,14 +181,6 @@ static int mdio_bus_probe(struct device_d *_dev) dev->supported = drv->features; dev->advertising = drv->features; - ret = phy_init_hw(dev); - if (ret) - goto err; - - /* Sanitize settings based on PHY capabilities */ - if ((dev->supported & SUPPORTED_Autoneg) == 0) - dev->autoneg = AUTONEG_DISABLE; - dev_add_param_int_ro(&dev->dev, "phy_addr", dev->addr, "%d"); dev_add_param_int_ro(&dev->dev, "phy_id", dev->phy_id, "0x%08x"); diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 74ef3d962..b05a31390 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -306,6 +306,15 @@ int phy_device_connect(struct eth_device *edev, struct mii_bus *bus, int addr, edev->phydev = dev; dev->attached_dev = edev; + + ret = phy_init_hw(dev); + if (ret) + goto fail; + + /* Sanitize settings based on PHY capabilities */ + if ((dev->supported & SUPPORTED_Autoneg) == 0) + dev->autoneg = AUTONEG_DISABLE; + phy_config_aneg(edev->phydev); dev->adjust_link = adjust_link; From bfc9a6985c13f3159cf7a867fe7e695983e0c269 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Fri, 8 Feb 2013 10:13:49 +0100 Subject: [PATCH 07/14] net: phy: Track mii buses on a list To be able to iterate over registered mii buses Signed-off-by: Sascha Hauer --- drivers/net/phy/mdio_bus.c | 6 ++++++ include/linux/phy.h | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 84c05adba..56cb9b2e9 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -25,6 +25,8 @@ #include #include +LIST_HEAD(mii_bus_list); + /** * mdiobus_register - bring up all the PHYs on a given bus and attach them to bus * @bus: target mii_bus @@ -57,6 +59,8 @@ int mdiobus_register(struct mii_bus *bus) if (bus->reset) bus->reset(bus); + list_add_tail(&bus->list, &mii_bus_list); + pr_info("%s: probed\n", dev_name(&bus->dev)); return 0; } @@ -71,6 +75,8 @@ void mdiobus_unregister(struct mii_bus *bus) unregister_device(&bus->phy_map[i]->dev); bus->phy_map[i] = NULL; } + + list_del(&bus->list); } EXPORT_SYMBOL(mdiobus_unregister); diff --git a/include/linux/phy.h b/include/linux/phy.h index 19e4d2066..5f3b33f66 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -85,6 +85,8 @@ struct mii_bus { /* PHY addresses to be ignored when probing */ u32 phy_mask; + + struct list_head list; }; #define to_mii_bus(d) container_of(d, struct mii_bus, dev) @@ -92,6 +94,11 @@ int mdiobus_register(struct mii_bus *bus); void mdiobus_unregister(struct mii_bus *bus); struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr); +extern struct list_head mii_bus_list; + +#define for_each_mii_bus(mii) \ + list_for_each_entry(mii, &mii_bus_list, list) + /** * mdiobus_read - Convenience function for reading a given MII mgmt register * @bus: the mii_bus struct From 6cb2b2f29f362c7021a05b6f68a6e8d7a79f2972 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 11 Dec 2013 11:51:02 +0100 Subject: [PATCH 08/14] net: phy: implement detect callback for miibus devices Signed-off-by: Sascha Hauer --- drivers/net/phy/mdio_bus.c | 31 +++++++++++++++++++++++++++++++ drivers/net/phy/phy.c | 2 +- include/linux/phy.h | 4 ++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 56cb9b2e9..b29a980e6 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -27,6 +27,36 @@ LIST_HEAD(mii_bus_list); +static int mdiobus_detect(struct device_d *dev) +{ + struct mii_bus *mii = to_mii_bus(dev); + int i, ret; + + for (i = 0; i < PHY_MAX_ADDR; i++) { + struct phy_device *phydev; + + phydev = mdiobus_scan(mii, i); + if (IS_ERR(phydev)) + continue; + if (phydev->registered) + continue; + ret = phy_register_device(phydev); + if (ret) + dev_err(dev, "failed to register phy: %s\n", strerror(-ret)); + dev_info(dev, "registered phy as /dev/%s\n", phydev->cdev.name); + } + + return 0; +} + +void mdiobus_detect_all(void) +{ + struct mii_bus *mii; + + for_each_mii_bus(mii) + mdiobus_detect(&mii->dev); +} + /** * mdiobus_register - bring up all the PHYs on a given bus and attach them to bus * @bus: target mii_bus @@ -49,6 +79,7 @@ int mdiobus_register(struct mii_bus *bus) bus->dev.id = DEVICE_ID_DYNAMIC; strcpy(bus->dev.name, "miibus"); bus->dev.parent = bus->parent; + bus->dev.detect = mdiobus_detect; err = register_device(&bus->dev); if (err) { diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index b05a31390..681fb5157 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -232,7 +232,7 @@ static void phy_config_aneg(struct phy_device *phydev) drv->config_aneg(phydev); } -static int phy_register_device(struct phy_device* dev) +int phy_register_device(struct phy_device* dev) { int ret; diff --git a/include/linux/phy.h b/include/linux/phy.h index 5f3b33f66..6c9d090f4 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -94,6 +94,8 @@ int mdiobus_register(struct mii_bus *bus); void mdiobus_unregister(struct mii_bus *bus); struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr); +void mdiobus_detect_all(void); + extern struct list_head mii_bus_list; #define for_each_mii_bus(mii) \ @@ -251,6 +253,8 @@ struct phy_device *get_phy_device(struct mii_bus *bus, int addr); int phy_init(void); int phy_init_hw(struct phy_device *phydev); +int phy_register_device(struct phy_device* dev); + /** * phy_read - Convenience function for reading a given PHY register * @phydev: the phy_device struct From 8bc54c172131244abbc5adabfd7fee07d8400d8a Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Wed, 11 Dec 2013 12:27:09 +0100 Subject: [PATCH 09/14] miitool: Add option to scan mii buses With the -s option all mii buses can be scanned for devices so that they are available without doing network operations. Also, now *all* phy devices on a mii bus can be examined, not only the one attached to an ethernet device. Signed-off-by: Sascha Hauer --- commands/miitool.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/commands/miitool.c b/commands/miitool.c index 3a9ac4560..a00514d3c 100644 --- a/commands/miitool.c +++ b/commands/miitool.c @@ -37,6 +37,7 @@ #include #include #include +#include static u16 mdio_read(int fd, int offset) { @@ -218,13 +219,17 @@ static int do_miitool(int argc, char *argv[]) int argc_min; int fd; int verbose; + int scan = 0; verbose = 0; - while ((opt = getopt(argc, argv, "v")) > 0) { + while ((opt = getopt(argc, argv, "vs")) > 0) { switch (opt) { case 'v': verbose++; break; + case 's': + scan = 1; + break; default: return COMMAND_ERROR_USAGE; } @@ -232,8 +237,11 @@ static int do_miitool(int argc, char *argv[]) argc_min = optind + 1; + if (scan) + mdiobus_detect_all(); + if (argc < argc_min) - return COMMAND_ERROR_USAGE; + return scan ? 0 : COMMAND_ERROR_USAGE; filename = argv[optind]; From 8ffd4f8c741d695779a91d62d9fe7b4f1e0c7ab2 Mon Sep 17 00:00:00 2001 From: Antony Pavlov Date: Wed, 25 Dec 2013 11:50:01 +0400 Subject: [PATCH 10/14] miitool: change behaviour closer to linux' mii-tool miitool without arguments will try to show status for all phys. Signed-off-by: Antony Pavlov Signed-off-by: Sascha Hauer --- commands/miitool.c | 88 ++++++++++++++++++++------------------ drivers/net/phy/mdio_bus.c | 10 +---- include/linux/phy.h | 4 +- 3 files changed, 50 insertions(+), 52 deletions(-) diff --git a/commands/miitool.c b/commands/miitool.c index a00514d3c..96d6f8072 100644 --- a/commands/miitool.c +++ b/commands/miitool.c @@ -36,24 +36,11 @@ #include #include #include +#include #include #include +#include -static u16 mdio_read(int fd, int offset) -{ - int ret; - u16 buf; - - ret = lseek(fd, offset << 1, SEEK_SET); - if (ret < 0) - return 0; - - ret = read(fd, &buf, sizeof(u16)); - if (ret < 0) - return 0; - - return buf; -} /* Table of known MII's */ static const struct { @@ -102,7 +89,8 @@ static char *media_list(int mask, int best) return buf; } -static int show_basic_mii(int fd, int verbose) +static int show_basic_mii(struct mii_bus *mii, struct phy_device *phydev, + int verbose) { char buf[100]; int i, mii_val[32]; @@ -110,15 +98,19 @@ static int show_basic_mii(int fd, int verbose) /* Some bits in the BMSR are latched, but we can't rely on being the only reader, so only the current values are meaningful */ - mdio_read(fd, MII_BMSR); - for (i = 0; i < ((verbose > 1) ? 32 : 8); i++) - mii_val[i] = mdio_read(fd, i); + mii->read(mii, phydev->addr, MII_BMSR); + + for (i = 0; i < 32; i++) + mii_val[i] = mii->read(mii, phydev->addr, i); if (mii_val[MII_BMCR] == 0xffff) { fprintf(stderr, " No MII transceiver present!.\n"); return -1; } + printf("%s: %s%d: ", phydev->cdev.name, + mii->parent->name, mii->parent->id); + /* Descriptive rename. */ bmcr = mii_val[MII_BMCR]; bmsr = mii_val[MII_BMSR]; @@ -212,24 +204,46 @@ static int show_basic_mii(int fd, int verbose) return 0; } +static void mdiobus_show(struct device_d *dev, char *phydevname, int verbose) +{ + struct mii_bus *mii = to_mii_bus(dev); + int i; + + for (i = 0; i < PHY_MAX_ADDR; i++) { + struct phy_device *phydev; + + phydev = mdiobus_scan(mii, i); + if (IS_ERR(phydev)) + continue; + if (phydev->registered) { + + show_basic_mii(mii, phydev, verbose); + + if (phydevname && + !strcmp(phydev->cdev.name, phydevname)) { + return; + } + } + + } + + return; +} + static int do_miitool(int argc, char *argv[]) { - char *filename; + char *phydevname; + struct mii_bus *mii; int opt; int argc_min; - int fd; int verbose; - int scan = 0; verbose = 0; - while ((opt = getopt(argc, argv, "vs")) > 0) { + while ((opt = getopt(argc, argv, "v")) > 0) { switch (opt) { case 'v': verbose++; break; - case 's': - scan = 1; - break; default: return COMMAND_ERROR_USAGE; } @@ -237,23 +251,15 @@ static int do_miitool(int argc, char *argv[]) argc_min = optind + 1; - if (scan) - mdiobus_detect_all(); - - if (argc < argc_min) - return scan ? 0 : COMMAND_ERROR_USAGE; - - filename = argv[optind]; - - fd = open(filename, O_RDONLY); - if (fd < 0) { - printf("unable to read %s\n", filename); - return COMMAND_ERROR; + phydevname = NULL; + if (argc >= argc_min) { + phydevname = argv[optind]; } - show_basic_mii(fd, verbose); - - close(fd); + for_each_mii_bus(mii) { + mdiobus_detect(&mii->dev); + mdiobus_show(&mii->dev, phydevname, verbose); + } return COMMAND_SUCCESS; } diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index b29a980e6..895ead0a5 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -27,7 +27,7 @@ LIST_HEAD(mii_bus_list); -static int mdiobus_detect(struct device_d *dev) +int mdiobus_detect(struct device_d *dev) { struct mii_bus *mii = to_mii_bus(dev); int i, ret; @@ -49,14 +49,6 @@ static int mdiobus_detect(struct device_d *dev) return 0; } -void mdiobus_detect_all(void) -{ - struct mii_bus *mii; - - for_each_mii_bus(mii) - mdiobus_detect(&mii->dev); -} - /** * mdiobus_register - bring up all the PHYs on a given bus and attach them to bus * @bus: target mii_bus diff --git a/include/linux/phy.h b/include/linux/phy.h index 6c9d090f4..9994e1107 100644 --- a/include/linux/phy.h +++ b/include/linux/phy.h @@ -94,10 +94,10 @@ int mdiobus_register(struct mii_bus *bus); void mdiobus_unregister(struct mii_bus *bus); struct phy_device *mdiobus_scan(struct mii_bus *bus, int addr); -void mdiobus_detect_all(void); - extern struct list_head mii_bus_list; +int mdiobus_detect(struct device_d *dev); + #define for_each_mii_bus(mii) \ list_for_each_entry(mii, &mii_bus_list, list) From 5a3fb85094b2ff03cf6238b550e983b01dd2cedc Mon Sep 17 00:00:00 2001 From: Antony Pavlov Date: Wed, 25 Dec 2013 11:50:02 +0400 Subject: [PATCH 11/14] miitool: add initial GbE support The GbE support based on the patch for generic 'mii-tool', see http://ftp.debian.org/debian/pool/main/n/net-tools/net-tools_1.60-24.2.diff.gz We need to note Jean-Christophe PLAGNIOL-VILLARD for his initial GbE support patch: http://lists.infradead.org/pipermail/barebox/2013-February/012634.html Generic 'mii-tool' GbE support patch has some disadvantages: 1. 1000baseT-HD is prefered to 1000baseT-FD; 2. show GbE-features for 10/100 only phys (e.g. Level One LXT971). This patch fixes this disadvantages. Signed-off-by: Antony Pavlov Cc: Jean-Christophe PLAGNIOL-VILLARD Signed-off-by: Sascha Hauer --- commands/miitool.c | 72 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 56 insertions(+), 16 deletions(-) diff --git a/commands/miitool.c b/commands/miitool.c index 96d6f8072..5ccfa58bb 100644 --- a/commands/miitool.c +++ b/commands/miitool.c @@ -54,25 +54,45 @@ static const struct { const struct { char *name; - u_short value; + u_short value[2]; } media[] = { /* The order through 100baseT4 matches bits in the BMSR */ - { "10baseT-HD", ADVERTISE_10HALF }, - { "10baseT-FD", ADVERTISE_10FULL }, - { "100baseTx-HD", ADVERTISE_100HALF }, - { "100baseTx-FD", ADVERTISE_100FULL }, - { "100baseT4", LPA_100BASE4 }, - { "100baseTx", ADVERTISE_100FULL | ADVERTISE_100HALF }, - { "10baseT", ADVERTISE_10FULL | ADVERTISE_10HALF }, + { "10baseT-HD", {ADVERTISE_10HALF} }, + { "10baseT-FD", {ADVERTISE_10FULL} }, + { "100baseTx-HD", {ADVERTISE_100HALF} }, + { "100baseTx-FD", {ADVERTISE_100FULL} }, + { "100baseT4", {LPA_100BASE4} }, + { "100baseTx", {ADVERTISE_100FULL | ADVERTISE_100HALF} }, + { "10baseT", {ADVERTISE_10FULL | ADVERTISE_10HALF} }, + { "1000baseT-HD", {0, ADVERTISE_1000HALF} }, + { "1000baseT-FD", {0, ADVERTISE_1000FULL} }, + { "1000baseT", {0, ADVERTISE_1000HALF | ADVERTISE_1000FULL} }, }; #define NMEDIA (sizeof(media)/sizeof(media[0])) -static char *media_list(int mask, int best) +static const char *media_list(unsigned mask, unsigned mask2, int best) { static char buf[100]; int i; *buf = '\0'; + + if (mask & BMCR_SPEED1000) { + if (mask2 & ADVERTISE_1000FULL) { + strcat(buf, " "); + strcat(buf, "1000baseT-FD"); + if (best) + goto out; + } + + if (mask2 & ADVERTISE_1000HALF) { + strcat(buf, " "); + strcat(buf, "1000baseT-HD"); + if (best) + goto out; + } + } + mask >>= 5; for (i = 4; i >= 0; i--) { if (mask & (1 << i)) { @@ -83,6 +103,7 @@ static char *media_list(int mask, int best) } } +out: if (mask & (1 << 5)) strcat(buf, " flow-control"); @@ -94,7 +115,9 @@ static int show_basic_mii(struct mii_bus *mii, struct phy_device *phydev, { char buf[100]; int i, mii_val[32]; - int bmcr, bmsr, advert, lkpar; + unsigned bmcr, bmsr, advert, lkpar, bmcr2 = 0, lpa2 = 0; + struct phy_driver *phydrv; + int is_phy_gbit; /* Some bits in the BMSR are latched, but we can't rely on being the only reader, so only the current values are meaningful */ @@ -103,7 +126,7 @@ static int show_basic_mii(struct mii_bus *mii, struct phy_device *phydev, for (i = 0; i < 32; i++) mii_val[i] = mii->read(mii, phydev->addr, i); - if (mii_val[MII_BMCR] == 0xffff) { + if (mii_val[MII_BMCR] == 0xffff || mii_val[MII_BMSR] == 0x0000) { fprintf(stderr, " No MII transceiver present!.\n"); return -1; } @@ -117,13 +140,23 @@ static int show_basic_mii(struct mii_bus *mii, struct phy_device *phydev, advert = mii_val[MII_ADVERTISE]; lkpar = mii_val[MII_LPA]; + phydrv = to_phy_driver(phydev->dev.driver); + is_phy_gbit = phydrv->features & + (SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full); + + if (is_phy_gbit) { + bmcr2 = mii_val[MII_CTRL1000]; + lpa2 = mii_val[MII_STAT1000]; + } + *buf = '\0'; if (bmcr & BMCR_ANENABLE) { if (bmsr & BMSR_ANEGCOMPLETE) { if (advert & lkpar) { sprintf(buf, "%s%s, ", (lkpar & LPA_LPACK) ? "negotiated" : "no autonegotiation,", - media_list(advert & lkpar, 1)); + media_list(advert & lkpar, + bmcr2 & lpa2 >> 2, 1)); } else { sprintf(buf, "autonegotiation failed, "); } @@ -131,8 +164,14 @@ static int show_basic_mii(struct mii_bus *mii, struct phy_device *phydev, sprintf(buf, "autonegotiation restarted, "); } } else { + int speed1000; + + speed1000 = ((bmcr2 + & (ADVERTISE_1000HALF + | ADVERTISE_1000FULL)) & lpa2 >> 2); sprintf(buf, "%s Mbit, %s duplex, ", - (bmcr & BMCR_SPEED100) ? "100" : "10", + speed1000 ? "1000" + : (bmcr & BMCR_SPEED100) ? "100" : "10", (bmcr & BMCR_FULLDPLX) ? "full" : "half"); } @@ -189,15 +228,16 @@ static int show_basic_mii(struct mii_bus *mii, struct phy_device *phydev, if (bmsr & BMSR_RFAULT) printf("remote fault, "); printf((bmsr & BMSR_LSTATUS) ? "link ok" : "no link"); - printf("\n capabilities:%s", media_list(bmsr >> 6, 0)); - printf("\n advertising: %s", media_list(advert, 0)); + printf("\n capabilities:%s", media_list(bmsr >> 6, bmcr2, 0)); + printf("\n advertising: %s", media_list(advert, lpa2 >> 2, 0)); #define LPA_ABILITY_MASK (LPA_10HALF | LPA_10FULL \ | LPA_100HALF | LPA_100FULL \ | LPA_100BASE4 | LPA_PAUSE_CAP) if (lkpar & LPA_ABILITY_MASK) - printf("\n link partner:%s", media_list(lkpar, 0)); + printf("\n link partner:%s", + media_list(lkpar, bmcr2, 0)); printf("\n"); } From 23e0081cfd138e808ccadf7ba2fc46f487a3e965 Mon Sep 17 00:00:00 2001 From: Antony Pavlov Date: Wed, 25 Dec 2013 11:50:03 +0400 Subject: [PATCH 12/14] miitool: drop internal table of known MII, use phy drivers instead Signed-off-by: Antony Pavlov Signed-off-by: Sascha Hauer --- commands/miitool.c | 30 +++++------------------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/commands/miitool.c b/commands/miitool.c index 5ccfa58bb..1e5d4eb66 100644 --- a/commands/miitool.c +++ b/commands/miitool.c @@ -41,17 +41,6 @@ #include #include - -/* Table of known MII's */ -static const struct { - u_short id1, id2; - u_short mask1, mask2; - char *name; -} mii_id[] = { - { 0x0013, 0x78e0, 0xffff, 0xfff0, "Level One LXT971A" }, -}; -#define NMII (sizeof(mii_id)/sizeof(mii_id[0])) - const struct { char *name; u_short value[2]; @@ -189,20 +178,11 @@ static int show_basic_mii(struct mii_bus *mii, struct phy_device *phydev, } if (verbose) { - printf(" product info: "); - for (i = 0; i < NMII; i++) - if ((mii_id[i].id1 == (mii_val[2] & mii_id[i].mask1)) && - (mii_id[i].id2 == - (mii_val[3] & mii_id[i].mask2))) - break; - - if (i < NMII) - printf("%s rev %d\n", mii_id[i].name, mii_val[3]&0x0f); - else - printf("vendor %02x:%02x:%02x, model %d rev %d\n", - mii_val[2] >> 10, (mii_val[2] >> 2) & 0xff, - ((mii_val[2] << 6)|(mii_val[3] >> 10)) & 0xff, - (mii_val[3] >> 4) & 0x3f, mii_val[3] & 0x0f); + printf(" product info: %s ", phydrv->drv.name); + printf("(vendor %02x:%02x:%02x, model %d rev %d)\n", + mii_val[2] >> 10, (mii_val[2] >> 2) & 0xff, + ((mii_val[2] << 6) | (mii_val[3] >> 10)) & 0xff, + (mii_val[3] >> 4) & 0x3f, mii_val[3] & 0x0f); printf(" basic mode: "); if (bmcr & BMCR_RESET) From ce4e4eff35caf8953610131d7c9107a53769f443 Mon Sep 17 00:00:00 2001 From: Antony Pavlov Date: Wed, 25 Dec 2013 11:50:04 +0400 Subject: [PATCH 13/14] net/phy: add driver for LXT PHYs Based on Linux kernel 3.12 driver. Signed-off-by: Antony Pavlov Signed-off-by: Sascha Hauer --- drivers/net/phy/Kconfig | 5 +++++ drivers/net/phy/Makefile | 1 + drivers/net/phy/lxt.c | 31 +++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+) create mode 100644 drivers/net/phy/lxt.c diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index 83966f997..7ebdaa0c0 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -13,6 +13,11 @@ config AT803X_PHY ---help--- Currently supports the AT8030, AT8031 and AT8035 PHYs. +config LXT_PHY + bool "Driver for the Intel LXT PHYs" + ---help--- + Currently supports the lxt971 PHY. + config MICREL_PHY bool "Driver for Micrel PHYs" ---help--- diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 47e2b4233..451573ed8 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -1,4 +1,5 @@ obj-y += phy.o mdio_bus.o obj-$(CONFIG_AT803X_PHY) += at803x.o +obj-$(CONFIG_LXT_PHY) += lxt.o obj-$(CONFIG_MICREL_PHY) += micrel.o obj-$(CONFIG_SMSC_PHY) += smsc.o diff --git a/drivers/net/phy/lxt.c b/drivers/net/phy/lxt.c new file mode 100644 index 000000000..9e5ddbb42 --- /dev/null +++ b/drivers/net/phy/lxt.c @@ -0,0 +1,31 @@ +/* + * drivers/net/phy/lxt.c + * + * Driver for Intel LXT PHYs + * + * base on Andy Fleming's linux lxt.c driver + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include + +static struct phy_driver lxt97x_driver[] = { +{ + .phy_id = 0x001378e0, + .phy_id_mask = 0xfffffff0, + .drv.name = "LXT971", + .features = PHY_BASIC_FEATURES, +} }; + +static int lxt97x_phy_init(void) +{ + return phy_drivers_register(lxt97x_driver, + ARRAY_SIZE(lxt97x_driver)); +} +fs_initcall(lxt97x_phy_init); From 9bc898059000da9882f2f0561bfcff6d709e52ac Mon Sep 17 00:00:00 2001 From: Antony Pavlov Date: Wed, 25 Dec 2013 11:50:05 +0400 Subject: [PATCH 14/14] net/phy/phy.c: fix whitespace Signed-off-by: Antony Pavlov Signed-off-by: Sascha Hauer --- drivers/net/phy/phy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 681fb5157..6ca1bb257 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -773,7 +773,7 @@ int phy_driver_register(struct phy_driver *phydrv) return register_driver(&phydrv->drv); } - + int phy_drivers_register(struct phy_driver *new_driver, int n) { int i, ret = 0;