X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=drivers%2Fnet%2Fphy%2Fphy.c;h=80bdfb6d9d5dc535ca6e89f7aa7ecaf597eb7189;hb=21342d4aed6c77a4aa7a5b2579b3c23e21aea31a;hp=5b04c85939040c27b832194a32f171686d6b88f6;hpb=9b18e5199ec42f9dc62c0da7f4fe7554e199980d;p=oweals%2Fu-boot.git diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index 5b04c85939..80bdfb6d9d 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -11,6 +11,8 @@ #include #include +#include +#include #include #include #include @@ -20,6 +22,8 @@ #include #include +DECLARE_GLOBAL_DATA_PTR; + /* Generic PHY support and helper functions */ /** @@ -34,16 +38,16 @@ static int genphy_config_advert(struct phy_device *phydev) { u32 advertise; - int oldadv, adv; + int oldadv, adv, bmsr; int err, changed = 0; - /* Only allow advertising what - * this PHY supports */ + /* Only allow advertising what this PHY supports */ phydev->advertising &= phydev->supported; advertise = phydev->advertising; /* Setup standard advertisement */ - oldadv = adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE); + adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE); + oldadv = adv; if (adv < 0) return adv; @@ -75,29 +79,40 @@ static int genphy_config_advert(struct phy_device *phydev) changed = 1; } + bmsr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR); + if (bmsr < 0) + return bmsr; + + /* Per 802.3-2008, Section 22.2.4.2.16 Extended status all + * 1000Mbits/sec capable PHYs shall have the BMSR_ESTATEN bit set to a + * logical 1. + */ + if (!(bmsr & BMSR_ESTATEN)) + return changed; + /* Configure gigabit if it's supported */ - if (phydev->supported & (SUPPORTED_1000baseT_Half | - SUPPORTED_1000baseT_Full)) { - oldadv = adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000); + adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000); + oldadv = adv; - if (adv < 0) - return adv; + if (adv < 0) + return adv; - adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); + adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF); + + if (phydev->supported & (SUPPORTED_1000baseT_Half | + SUPPORTED_1000baseT_Full)) { if (advertise & SUPPORTED_1000baseT_Half) adv |= ADVERTISE_1000HALF; if (advertise & SUPPORTED_1000baseT_Full) adv |= ADVERTISE_1000FULL; + } - if (adv != oldadv) { - err = phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, - adv); + if (adv != oldadv) + changed = 1; - if (err < 0) - return err; - changed = 1; - } - } + err = phy_write(phydev, MDIO_DEVAD_NONE, MII_CTRL1000, adv); + if (err < 0) + return err; return changed; } @@ -113,7 +128,7 @@ static int genphy_config_advert(struct phy_device *phydev) static int genphy_setup_forced(struct phy_device *phydev) { int err; - int ctl = 0; + int ctl = BMCR_ANRESTART; phydev->pause = phydev->asym_pause = 0; @@ -220,7 +235,8 @@ int genphy_update_link(struct phy_device *phydev) if (phydev->link && mii_reg & BMSR_LSTATUS) return 0; - if ((mii_reg & BMSR_ANEGCAPABLE) && !(mii_reg & BMSR_ANEGCOMPLETE)) { + if ((phydev->autoneg == AUTONEG_ENABLE) && + !(mii_reg & BMSR_ANEGCOMPLETE)) { int i = 0; printf("%s Waiting for PHY auto negotiation to complete", @@ -232,7 +248,7 @@ int genphy_update_link(struct phy_device *phydev) if (i > PHY_ANEG_TIMEOUT) { printf(" TIMEOUT !\n"); phydev->link = 0; - return 0; + return -ETIMEDOUT; } if (ctrlc()) { @@ -276,7 +292,7 @@ int genphy_parse_link(struct phy_device *phydev) int mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR); /* We're using autonegotiation */ - if (phydev->supported & SUPPORTED_Autoneg) { + if (phydev->autoneg == AUTONEG_ENABLE) { u32 lpa = 0; int gblpa = 0; u32 estatus = 0; @@ -367,8 +383,6 @@ int genphy_config(struct phy_device *phydev) int val; u32 features; - /* For now, I'll claim that the generic driver supports - * all possible port types */ features = (SUPPORTED_TP | SUPPORTED_MII | SUPPORTED_AUI | SUPPORTED_FIBRE | SUPPORTED_BNC); @@ -407,8 +421,8 @@ int genphy_config(struct phy_device *phydev) features |= SUPPORTED_1000baseX_Half; } - phydev->supported = features; - phydev->advertising = features; + phydev->supported &= features; + phydev->advertising &= features; genphy_config_aneg(phydev); @@ -417,10 +431,13 @@ int genphy_config(struct phy_device *phydev) int genphy_startup(struct phy_device *phydev) { - genphy_update_link(phydev); - genphy_parse_link(phydev); + int ret; - return 0; + ret = genphy_update_link(phydev); + if (ret) + return ret; + + return genphy_parse_link(phydev); } int genphy_shutdown(struct phy_device *phydev) @@ -432,7 +449,9 @@ static struct phy_driver genphy_driver = { .uid = 0xffffffff, .mask = 0xffffffff, .name = "Generic PHY", - .features = 0, + .features = PHY_GBIT_FEATURES | SUPPORTED_MII | + SUPPORTED_AUI | SUPPORTED_FIBRE | + SUPPORTED_BNC, .config = genphy_config, .startup = genphy_startup, .shutdown = genphy_shutdown, @@ -442,6 +461,12 @@ static LIST_HEAD(phy_drivers); int phy_init(void) { +#ifdef CONFIG_MV88E61XX_SWITCH + phy_mv88e61xx_init(); +#endif +#ifdef CONFIG_PHY_AQUANTIA + phy_aquantia_init(); +#endif #ifdef CONFIG_PHY_ATHEROS phy_atheros_init(); #endif @@ -478,9 +503,15 @@ int phy_init(void) #ifdef CONFIG_PHY_TERANETICS phy_teranetics_init(); #endif +#ifdef CONFIG_PHY_TI + phy_ti_init(); +#endif #ifdef CONFIG_PHY_VITESSE phy_vitesse_init(); #endif +#ifdef CONFIG_PHY_XILINX + phy_xilinx_init(); +#endif return 0; } @@ -490,6 +521,44 @@ 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; +} + +int phy_set_supported(struct phy_device *phydev, u32 max_speed) +{ + /* The default values for phydev->supported are provided by the PHY + * driver "features" member, we want to reset to sane defaults first + * before supporting higher speeds. + */ + phydev->supported &= PHY_DEFAULT_FEATURES; + + switch (max_speed) { + default: + return -ENOTSUPP; + case SPEED_1000: + phydev->supported |= PHY_1000BT_FEATURES; + /* fall through */ + case SPEED_100: + phydev->supported |= PHY_100BT_FEATURES; + /* fall through */ + case SPEED_10: + phydev->supported |= PHY_10BT_FEATURES; + } + return 0; } @@ -534,7 +603,7 @@ static struct phy_driver *get_phy_driver(struct phy_device *phydev, } 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; @@ -551,7 +620,7 @@ static struct phy_device *phy_device_create(struct mii_dev *bus, int addr, memset(dev, 0, sizeof(*dev)); dev->duplex = -1; - dev->link = 1; + dev->link = 0; dev->interface = interface; dev->autoneg = AUTONEG_ENABLE; @@ -578,7 +647,7 @@ static struct phy_device *phy_device_create(struct mii_dev *bus, int addr, * Description: Reads the ID registers of the PHY at @addr on the * @bus, stores it in @phy_id and returns zero on success. */ -static int get_phy_id(struct mii_dev *bus, int addr, int devad, u32 *phy_id) +int __weak get_phy_id(struct mii_dev *bus, int addr, int devad, u32 *phy_id) { int phy_reg; @@ -651,8 +720,16 @@ static struct phy_device *get_phy_device_by_mask(struct mii_dev *bus, if (phydev) return phydev; } - printf("Phy %d not found\n", ffs(phy_mask) - 1); - return phy_device_create(bus, ffs(phy_mask) - 1, 0xffffffff, interface); + + debug("\n%s PHY: ", bus->name); + while (phy_mask) { + int addr = ffs(phy_mask) - 1; + debug("%d ", addr); + phy_mask &= ~(1 << addr); + } + debug("not found\n"); + + return NULL; } /** @@ -675,6 +752,9 @@ int phy_reset(struct phy_device *phydev) int timeout = 500; int devad = MDIO_DEVAD_NONE; + if (phydev->flags & PHY_FLAG_BROKEN_RESET) + return 0; + #ifdef CONFIG_PHYLIB_10G /* If it's 10G, we need to issue reset through one of the MMDs */ if (is_10g_interface(phydev->interface)) { @@ -685,15 +765,7 @@ int phy_reset(struct phy_device *phydev) } #endif - reg = phy_read(phydev, devad, MII_BMCR); - if (reg < 0) { - debug("PHY status read failed\n"); - return -1; - } - - reg |= BMCR_RESET; - - if (phy_write(phydev, devad, MII_BMCR, reg) < 0) { + if (phy_write(phydev, devad, MII_BMCR, BMCR_RESET) < 0) { debug("PHY reset failed\n"); return -1; } @@ -706,6 +778,7 @@ int phy_reset(struct phy_device *phydev) * auto-clearing). This should happen within 0.5 seconds per the * IEEE spec. */ + reg = phy_read(phydev, devad, MII_BMCR); while ((reg & BMCR_RESET) && timeout--) { reg = phy_read(phydev, devad, MII_BMCR); @@ -743,19 +816,25 @@ 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); - if (phydev->dev) { + if (phydev->dev && phydev->dev != dev) { printf("%s:%d is connected to %s. Reconnecting to %s\n", phydev->bus->name, phydev->addr, phydev->dev->name, dev->name); @@ -764,8 +843,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; @@ -798,9 +882,7 @@ __weak int board_phy_config(struct phy_device *phydev) int phy_config(struct phy_device *phydev) { /* Invoke an optional board-specific helper */ - board_phy_config(phydev); - - return 0; + return board_phy_config(phydev); } int phy_shutdown(struct phy_device *phydev) @@ -810,3 +892,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; +}