diff --git a/commands/miitool.c b/commands/miitool.c index 3a9ac4560..1e5d4eb66 100644 --- a/commands/miitool.c +++ b/commands/miitool.c @@ -36,55 +36,52 @@ #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 { - 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])) +#include +#include 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)) { @@ -95,42 +92,60 @@ static char *media_list(int mask, int best) } } +out: if (mask & (1 << 5)) strcat(buf, " flow-control"); 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]; - 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 */ - 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); - if (mii_val[MII_BMCR] == 0xffff) { + for (i = 0; i < 32; i++) + mii_val[i] = mii->read(mii, phydev->addr, i); + + if (mii_val[MII_BMCR] == 0xffff || mii_val[MII_BMSR] == 0x0000) { 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]; 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, "); } @@ -138,8 +153,14 @@ static int show_basic_mii(int fd, int verbose) 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"); } @@ -157,20 +178,11 @@ static int show_basic_mii(int fd, int verbose) } 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) @@ -196,27 +208,54 @@ static int show_basic_mii(int fd, int verbose) 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"); } 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; verbose = 0; @@ -232,20 +271,15 @@ static int do_miitool(int argc, char *argv[]) argc_min = optind + 1; - if (argc < argc_min) - return 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/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); diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c index 87072be28..895ead0a5 100644 --- a/drivers/net/phy/mdio_bus.c +++ b/drivers/net/phy/mdio_bus.c @@ -25,6 +25,30 @@ #include #include +LIST_HEAD(mii_bus_list); + +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; +} + /** * mdiobus_register - bring up all the PHYs on a given bus and attach them to bus * @bus: target mii_bus @@ -47,6 +71,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) { @@ -57,6 +82,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 +98,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); @@ -153,8 +182,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) @@ -183,14 +210,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"); @@ -204,8 +223,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..6ca1bb257 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -224,21 +224,40 @@ struct phy_device *get_phy_device(struct mii_bus *bus, int addr) return dev; } -static int phy_register_device(struct phy_device* 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); +} + +int phy_register_device(struct phy_device* dev) { int ret; - dev->dev.parent = &dev->attached_dev->dev; + 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 */ @@ -246,66 +265,63 @@ 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; + } - dev->attached_dev = edev; - 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->attached_dev = edev; - dev->interface = interface; - dev->dev_flags = flags; - - ret = phy_register_device(dev); - if (ret) - goto fail; - - break; - } - } - - if (!edev->phydev) { + if (addr >= 0) { + dev = mdiobus_scan(bus, addr); + if (IS_ERR(dev)) { ret = -EIO; 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) + break; + } } - dev = edev->phydev; - drv = to_phy_driver(dev->dev.driver); + if (dev->attached_dev) + return -EBUSY; - drv->config_aneg(dev); + dev->interface = interface; + dev->dev_flags = flags; + + if (!dev->registered) { + ret = phy_register_device(dev); + if (ret) + goto fail; + } + + 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; return 0; fail: - if (!IS_ERR(dev)) - dev->attached_dev = NULL; puts("Unable to find a PHY (unknown ID?)\n"); return ret; } @@ -757,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; diff --git a/include/linux/phy.h b/include/linux/phy.h index 94f631b18..9994e1107 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,13 @@ 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; + +int mdiobus_detect(struct device_d *dev); + +#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 @@ -161,6 +170,7 @@ struct phy_device { int autoneg; int force; + int registered; /* private data pointer */ /* For use by PHYs to maintain extra state */ @@ -243,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