X-Git-Url: https://git.kernelconcepts.de/?p=karo-tx-uboot.git;a=blobdiff_plain;f=drivers%2Fnet%2Fphy%2Fphy.c;h=f3b1b5c1ed915df0edb613616e43878626f6740f;hp=aac85c4d0927e16a9060e6971c55fe59504f0b97;hb=b84fa855427f4b033542588c84a8667dfd1d3792;hpb=55e8250bd3cfd996d1caa04f520160a89ec04754 diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index aac85c4d09..f3b1b5c1ed 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -11,6 +11,7 @@ #include #include +#include #include #include #include @@ -20,6 +21,8 @@ #include #include +DECLARE_GLOBAL_DATA_PTR; + /* Generic PHY support and helper functions */ /** @@ -44,7 +47,6 @@ static int genphy_config_advert(struct phy_device *phydev) /* Setup standard advertisement */ oldadv = adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE); - if (adv < 0) return adv; @@ -79,7 +81,6 @@ static int genphy_config_advert(struct phy_device *phydev) if (phydev->supported & (SUPPORTED_1000baseT_Half | SUPPORTED_1000baseT_Full)) { oldadv = adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000); - if (adv < 0) return adv; @@ -112,7 +113,6 @@ static int genphy_config_advert(struct phy_device *phydev) */ static int genphy_setup_forced(struct phy_device *phydev) { - int err; int ctl = 0; phydev->pause = phydev->asym_pause = 0; @@ -125,9 +125,7 @@ static int genphy_setup_forced(struct phy_device *phydev) if (DUPLEX_FULL == phydev->duplex) ctl |= BMCR_FULLDPLX; - err = phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, ctl); - - return err; + return phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, ctl); } @@ -140,7 +138,6 @@ int genphy_restart_aneg(struct phy_device *phydev) int ctl; ctl = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); - if (ctl < 0) return ctl; @@ -149,9 +146,7 @@ int genphy_restart_aneg(struct phy_device *phydev) /* Don't isolate the PHY if we're negotiating */ ctl &= ~(BMCR_ISOLATE); - ctl = phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, ctl); - - return ctl; + return phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, ctl); } @@ -205,13 +200,16 @@ int genphy_config_aneg(struct phy_device *phydev) */ int genphy_update_link(struct phy_device *phydev) { - unsigned int mii_reg; + int mii_reg; + int bmcr; /* * Wait if the link is up, and autonegotiation is in progress * (ie - we're capable and it's not done) */ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR); + if (mii_reg < 0) + return mii_reg; /* * If we already saw the link up, and it hasn't gone down, then @@ -220,6 +218,13 @@ int genphy_update_link(struct phy_device *phydev) if (phydev->link && mii_reg & BMSR_LSTATUS) return 0; + bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); + if (bmcr < 0) + return bmcr; + + if (!(bmcr & BMCR_ANENABLE)) + return 0; + if ((mii_reg & BMSR_ANEGCAPABLE) && !(mii_reg & BMSR_ANEGCOMPLETE)) { int i = 0; @@ -246,12 +251,16 @@ int genphy_update_link(struct phy_device *phydev) udelay(1000); /* 1 ms */ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR); + if (mii_reg < 0) + return mii_reg; } printf(" done\n"); phydev->link = 1; } else { /* Read the link a second time to clear the latched state */ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR); + if (mii_reg < 0) + return mii_reg; if (mii_reg & BMSR_LSTATUS) phydev->link = 1; @@ -275,10 +284,14 @@ int genphy_parse_link(struct phy_device *phydev) { int mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR); + if (mii_reg < 0) + return mii_reg; + /* We're using autonegotiation */ if (phydev->supported & SUPPORTED_Autoneg) { + int ret; u32 lpa = 0; - int gblpa = 0; + u32 gblpa = 0; u32 estatus = 0; /* Check for gigabit capability */ @@ -287,13 +300,17 @@ int genphy_parse_link(struct phy_device *phydev) /* We want a list of states supported by * both PHYs in the link */ - gblpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_STAT1000); - if (gblpa < 0) { + ret = phy_read(phydev, MDIO_DEVAD_NONE, MII_STAT1000); + if (ret < 0) { debug("Could not read MII_STAT1000. Ignoring gigabit capability\n"); - gblpa = 0; + ret = 0; } - gblpa &= phy_read(phydev, - MDIO_DEVAD_NONE, MII_CTRL1000) << 2; + gblpa = ret; + + ret = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000); + if (ret < 0) + return ret; + gblpa &= ret << 2; } /* Set the baseline so we only have to set them @@ -313,8 +330,15 @@ int genphy_parse_link(struct phy_device *phydev) return 0; } - lpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE); - lpa &= phy_read(phydev, MDIO_DEVAD_NONE, MII_LPA); + ret = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE); + if (ret < 0) + return ret; + lpa = ret; + + ret = phy_read(phydev, MDIO_DEVAD_NONE, MII_LPA); + if (ret < 0) + return ret; + lpa &= ret; if (lpa & (LPA_100FULL | LPA_100HALF)) { phydev->speed = SPEED_100; @@ -336,6 +360,8 @@ int genphy_parse_link(struct phy_device *phydev) if ((mii_reg & BMSR_ESTATEN) && !(mii_reg & BMSR_ERCAP)) estatus = phy_read(phydev, MDIO_DEVAD_NONE, MII_ESTATUS); + if (estatus < 0) + return estatus; if (estatus & (ESTATUS_1000_XFULL | ESTATUS_1000_XHALF | ESTATUS_1000_TFULL | ESTATUS_1000_THALF)) { @@ -345,7 +371,10 @@ int genphy_parse_link(struct phy_device *phydev) } } else { - u32 bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); + int bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR); + + if (bmcr < 0) + return bmcr; phydev->speed = SPEED_10; phydev->duplex = DUPLEX_HALF; @@ -375,7 +404,6 @@ int genphy_config(struct phy_device *phydev) /* Do we support autonegotiation? */ val = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR); - if (val < 0) return val; @@ -442,21 +470,24 @@ static LIST_HEAD(phy_drivers); int phy_init(void) { +#ifdef CONFIG_PHY_AQUANTIA + phy_aquantia_init(); +#endif #ifdef CONFIG_PHY_ATHEROS phy_atheros_init(); #endif #ifdef CONFIG_PHY_BROADCOM phy_broadcom_init(); #endif +#ifdef CONFIG_PHY_CORTINA + phy_cortina_init(); +#endif #ifdef CONFIG_PHY_DAVICOM phy_davicom_init(); #endif #ifdef CONFIG_PHY_ET1011C phy_et1011c_init(); #endif -#ifdef CONFIG_PHY_ICPLUS - phy_icplus_init(); -#endif #ifdef CONFIG_PHY_LXT phy_lxt_init(); #endif @@ -490,6 +521,20 @@ int phy_register(struct phy_driver *drv) INIT_LIST_HEAD(&drv->list); list_add_tail(&drv->list, &phy_drivers); +#ifdef CONFIG_NEEDS_MANUAL_RELOC + if (drv->probe) + drv->probe += gd->reloc_off; + if (drv->config) + drv->config += gd->reloc_off; + if (drv->startup) + drv->startup += gd->reloc_off; + if (drv->shutdown) + drv->shutdown += gd->reloc_off; + if (drv->readext) + drv->readext += gd->reloc_off; + if (drv->writeext) + drv->writeext += gd->reloc_off; +#endif return 0; } @@ -533,8 +578,71 @@ static struct phy_driver *get_phy_driver(struct phy_device *phydev, return generic_for_interface(interface); } +static int aneg_enabled(struct phy_device *phydev) +{ + static const char *aneg = "_aneg"; + char varname[strlen(phydev->bus->name) + strlen(aneg) + 1]; + + snprintf(varname, sizeof(varname), "%s%s", phydev->bus->name, aneg); + return getenv_yesno(varname); +} + +static int phy_get_speed(struct phy_device *phydev) +{ + int ret; + static const char *aneg = "_speed"; + char varname[strlen(phydev->bus->name) + strlen(aneg) + 1]; + ulong val; + + snprintf(varname, sizeof(varname), "%s%s", phydev->bus->name, aneg); + + val = getenv_ulong(varname, 10, 100); + switch (val) { + case 1000: + ret = SPEED_1000; + break; + case 100: + ret = SPEED_100; + break; + case 10: + ret = SPEED_10; + break; + default: + printf("Improper setting '%s' for %s; assuming 100\n", + getenv(varname), varname); + ret = SPEED_100; + } + return ret; +} + +static int phy_get_duplex(struct phy_device *phydev) +{ + int ret = DUPLEX_FULL; + static const char *aneg = "_duplex"; + char varname[strlen(phydev->bus->name) + strlen(aneg) + 4]; + const char *val; + + snprintf(varname, sizeof(varname), "%s%d%s", + phydev->bus->name, phydev->addr, aneg); + + val = getenv(varname); + if (val != NULL) { + if (strcasecmp(val, "full") != 0) { + if (strcasecmp(val, "half") == 0) { + ret = DUPLEX_HALF; + } else { + printf("Improper setting '%s' for %s; assuming 'full'\n", + val, varname); + printf("Expected one of: 'full', 'half'\n"); + } + } + } + + return ret; +} + static struct phy_device *phy_device_create(struct mii_dev *bus, int addr, - int phy_id, + u32 phy_id, phy_interface_t interface) { struct phy_device *dev; @@ -554,14 +662,43 @@ static struct phy_device *phy_device_create(struct mii_dev *bus, int addr, dev->link = 1; dev->interface = interface; - dev->autoneg = AUTONEG_ENABLE; - dev->addr = addr; dev->phy_id = phy_id; dev->bus = bus; dev->drv = get_phy_driver(dev, interface); + if (aneg_enabled(dev)) { + dev->autoneg = AUTONEG_ENABLE; + } else { + dev->autoneg = AUTONEG_DISABLE; + dev->speed = phy_get_speed(dev); + dev->duplex = phy_get_duplex(dev); + + switch (dev->speed) { + case SPEED_1000: + if (dev->duplex == DUPLEX_FULL) + dev->supported &= SUPPORTED_1000baseT_Full; + else + dev->supported &= SUPPORTED_1000baseT_Half; + break; + case SPEED_100: + if (dev->duplex == DUPLEX_FULL) + dev->supported &= SUPPORTED_100baseT_Full; + else + dev->supported &= SUPPORTED_100baseT_Half; + break; + case SPEED_10: + if (dev->duplex == DUPLEX_FULL) + dev->supported &= SUPPORTED_10baseT_Full; + else + dev->supported &= SUPPORTED_10baseT_Half; + break; + default: + printf("Unsupported speed: %d\n", dev->speed); + } + } + phy_probe(dev); bus->phymap[addr] = dev; @@ -651,7 +788,7 @@ static struct phy_device *get_phy_device_by_mask(struct mii_dev *bus, if (phydev) return phydev; } - printf("Phy not found\n"); + printf("Phy %d not found\n", ffs(phy_mask) - 1); return phy_device_create(bus, ffs(phy_mask) - 1, 0xffffffff, interface); } @@ -671,6 +808,7 @@ static struct phy_device *get_phy_device(struct mii_dev *bus, int addr, int phy_reset(struct phy_device *phydev) { + int err; int reg; int timeout = 500; int devad = MDIO_DEVAD_NONE; @@ -693,9 +831,10 @@ int phy_reset(struct phy_device *phydev) reg |= BMCR_RESET; - if (phy_write(phydev, devad, MII_BMCR, reg) < 0) { + err = phy_write(phydev, devad, MII_BMCR, reg); + if (err < 0) { debug("PHY reset failed\n"); - return -1; + return err; } #ifdef CONFIG_PHY_RESET_DELAY @@ -706,19 +845,19 @@ int phy_reset(struct phy_device *phydev) * auto-clearing). This should happen within 0.5 seconds per the * IEEE spec. */ - while ((reg & BMCR_RESET) && timeout--) { + while ((reg & BMCR_RESET) && timeout-- >= 0) { reg = phy_read(phydev, devad, MII_BMCR); if (reg < 0) { debug("PHY status read failed\n"); - return -1; + return reg; } udelay(1000); } if (reg & BMCR_RESET) { puts("PHY reset timed out\n"); - return -1; + return -ETIMEDOUT; } return 0; @@ -743,15 +882,21 @@ struct phy_device *phy_find_by_mask(struct mii_dev *bus, unsigned phy_mask, phy_interface_t interface) { /* Reset the bus */ - if (bus->reset) + if (bus->reset) { bus->reset(bus); - /* Wait 15ms to make sure the PHY has come out of hard reset */ - udelay(15000); + /* Wait 15ms to make sure the PHY has come out of hard reset */ + udelay(15000); + } + return get_phy_device_by_mask(bus, phy_mask, interface); } +#ifdef CONFIG_DM_ETH +void phy_connect_dev(struct phy_device *phydev, struct udevice *dev) +#else void phy_connect_dev(struct phy_device *phydev, struct eth_device *dev) +#endif { /* Soft Reset the PHY */ phy_reset(phydev); @@ -764,8 +909,13 @@ void phy_connect_dev(struct phy_device *phydev, struct eth_device *dev) debug("%s connected to %s\n", dev->name, phydev->drv->name); } +#ifdef CONFIG_DM_ETH +struct phy_device *phy_connect(struct mii_dev *bus, int addr, + struct udevice *dev, phy_interface_t interface) +#else struct phy_device *phy_connect(struct mii_dev *bus, int addr, struct eth_device *dev, phy_interface_t interface) +#endif { struct phy_device *phydev; @@ -788,16 +938,13 @@ int phy_startup(struct phy_device *phydev) return 0; } -static int __board_phy_config(struct phy_device *phydev) +__weak int board_phy_config(struct phy_device *phydev) { if (phydev->drv->config) return phydev->drv->config(phydev); return 0; } -int board_phy_config(struct phy_device *phydev) - __attribute__((weak, alias("__board_phy_config"))); - int phy_config(struct phy_device *phydev) { /* Invoke an optional board-specific helper */ @@ -813,3 +960,15 @@ int phy_shutdown(struct phy_device *phydev) return 0; } + +int phy_get_interface_by_name(const char *str) +{ + int i; + + for (i = 0; i < PHY_INTERFACE_MODE_COUNT; i++) { + if (!strcmp(str, phy_interface_strings[i])) + return i; + } + + return -1; +}