X-Git-Url: https://git.kernelconcepts.de/?p=karo-tx-uboot.git;a=blobdiff_plain;f=drivers%2Fnet%2Fphy%2Fphy.c;h=7ad1343ab14692fe7219454407c97587f6fd300c;hp=ce69c195368c045f5b2f02acc93be47710ee627a;hb=0c9e2dc612e8b970ea2d13712b0e0f3f1282be4f;hpb=b91a9d9d4dc81d8b62690b03546d92d27744e9b5 diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index ce69c19536..7ad1343ab1 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -1,21 +1,7 @@ /* * Generic PHY Management code * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA - * + * SPDX-License-Identifier: GPL-2.0+ * * Copyright 2011 Freescale Semiconductor, Inc. * author Andy Fleming @@ -31,6 +17,7 @@ #include #include #include +#include /* Generic PHY support and helper functions */ @@ -43,7 +30,7 @@ * what is supported. Returns < 0 on error, 0 if the PHY's advertisement * hasn't changed, and > 0 if it has changed. */ -int genphy_config_advert(struct phy_device *phydev) +static int genphy_config_advert(struct phy_device *phydev) { u32 advertise; int oldadv, adv; @@ -74,6 +61,10 @@ int genphy_config_advert(struct phy_device *phydev) adv |= ADVERTISE_PAUSE_CAP; if (advertise & ADVERTISED_Asym_Pause) adv |= ADVERTISE_PAUSE_ASYM; + if (advertise & ADVERTISED_1000baseX_Half) + adv |= ADVERTISE_1000XHALF; + if (advertise & ADVERTISED_1000baseX_Full) + adv |= ADVERTISE_1000XFULL; if (adv != oldadv) { err = phy_write(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE, adv); @@ -118,9 +109,8 @@ int genphy_config_advert(struct phy_device *phydev) * Description: Configures MII_BMCR to force speed/duplex * to the values in phydev. Assumes that the values are valid. */ -int genphy_setup_forced(struct phy_device *phydev) +static int genphy_setup_forced(struct phy_device *phydev) { - int err; int ctl = 0; phydev->pause = phydev->asym_pause = 0; @@ -133,9 +123,7 @@ 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); } @@ -157,9 +145,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); } @@ -213,13 +199,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 @@ -228,6 +217,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; @@ -254,12 +250,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; @@ -279,23 +279,34 @@ int genphy_update_link(struct phy_device *phydev) * * Stolen from Linux's mii.c and phy_device.c */ -static int genphy_parse_link(struct phy_device *phydev) +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 (mii_reg & BMSR_ANEGCAPABLE) { - u32 lpa = 0; - u32 gblpa = 0; + int ret; + u16 lpa; + u16 gblpa = 0; + int estatus = 0; /* Check for gigabit capability */ if (mii_reg & BMSR_ERCAP) { /* We want a list of states supported by * both PHYs in the link */ - gblpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_STAT1000); - gblpa &= phy_read(phydev, - MDIO_DEVAD_NONE, MII_CTRL1000) << 2; + ret = phy_read(phydev, MDIO_DEVAD_NONE, MII_STAT1000); + if (ret < 0) + return ret; + 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 @@ -315,8 +326,15 @@ static 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; @@ -326,8 +344,33 @@ static int genphy_parse_link(struct phy_device *phydev) } else if (lpa & LPA_10FULL) phydev->duplex = DUPLEX_FULL; + + /* + * Extended status may indicate that the PHY supports + * 1000BASE-T/X even though the 1000BASE-T registers + * are missing. In this case we can't tell whether the + * peer also supports it, so we only check extended + * status if the 1000BASE-T registers are actually + * missing. + */ + 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)) { + phydev->speed = SPEED_1000; + if (estatus & (ESTATUS_1000_XFULL | ESTATUS_1000_TFULL)) + phydev->duplex = DUPLEX_FULL; + } + } 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; @@ -383,6 +426,10 @@ int genphy_config(struct phy_device *phydev) features |= SUPPORTED_1000baseT_Full; if (val & ESTATUS_1000_THALF) features |= SUPPORTED_1000baseT_Half; + if (val & ESTATUS_1000_XFULL) + features |= SUPPORTED_1000baseX_Full; + if (val & ESTATUS_1000_XHALF) + features |= SUPPORTED_1000baseX_Half; } phydev->supported = features; @@ -429,6 +476,12 @@ int phy_init(void) #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 @@ -444,6 +497,9 @@ int phy_init(void) #ifdef CONFIG_PHY_REALTEK phy_realtek_init(); #endif +#ifdef CONFIG_PHY_SMSC + phy_smsc_init(); +#endif #ifdef CONFIG_PHY_TERANETICS phy_teranetics_init(); #endif @@ -462,7 +518,7 @@ int phy_register(struct phy_driver *drv) return 0; } -int phy_probe(struct phy_device *phydev) +static int phy_probe(struct phy_device *phydev) { int err = 0; @@ -485,7 +541,7 @@ static struct phy_driver *generic_for_interface(phy_interface_t interface) return &genphy_driver; } -struct phy_driver *get_phy_driver(struct phy_device *phydev, +static struct phy_driver *get_phy_driver(struct phy_device *phydev, phy_interface_t interface) { struct list_head *entry; @@ -502,8 +558,9 @@ struct phy_driver *get_phy_driver(struct phy_device *phydev, return generic_for_interface(interface); } -struct phy_device *phy_device_create(struct mii_dev *bus, int addr, int phy_id, - phy_interface_t interface) +static struct phy_device *phy_device_create(struct mii_dev *bus, int addr, + int phy_id, + phy_interface_t interface) { struct phy_device *dev; @@ -546,7 +603,7 @@ struct phy_device *phy_device_create(struct mii_dev *bus, int addr, int phy_id, * Description: Reads the ID registers of the PHY at @addr on the * @bus, stores it in @phy_id and returns zero on success. */ -int get_phy_id(struct mii_dev *bus, int addr, int devad, u32 *phy_id) +static int get_phy_id(struct mii_dev *bus, int addr, int devad, u32 *phy_id) { int phy_reg; @@ -570,53 +627,78 @@ int get_phy_id(struct mii_dev *bus, int addr, int devad, u32 *phy_id) return 0; } -/** - * get_phy_device - reads the specified PHY device and returns its @phy_device struct - * @bus: the target MII bus - * @addr: PHY address on the MII bus - * - * Description: Reads the ID registers of the PHY at @addr on the - * @bus, then allocates and returns the phy_device to represent it. - */ -struct phy_device *get_phy_device(struct mii_dev *bus, int addr, - phy_interface_t interface) +static struct phy_device *create_phy_by_mask(struct mii_dev *bus, + unsigned phy_mask, int devad, phy_interface_t interface) { - u32 phy_id = 0x1fffffff; - int i; - int r; + u32 phy_id = 0xffffffff; + while (phy_mask) { + int addr = ffs(phy_mask) - 1; + int r = get_phy_id(bus, addr, devad, &phy_id); + if (r < 0) + return ERR_PTR(r); + /* If the PHY ID is mostly f's, we didn't find anything */ + if ((phy_id & 0x1fffffff) != 0x1fffffff) + return phy_device_create(bus, addr, phy_id, interface); + phy_mask &= ~(1 << addr); + } + return NULL; +} +static struct phy_device *search_for_existing_phy(struct mii_dev *bus, + unsigned phy_mask, phy_interface_t interface) +{ /* If we have one, return the existing device, with new interface */ - if (bus->phymap[addr]) { - bus->phymap[addr]->interface = interface; - - return bus->phymap[addr]; + while (phy_mask) { + int addr = ffs(phy_mask) - 1; + if (bus->phymap[addr]) { + bus->phymap[addr]->interface = interface; + return bus->phymap[addr]; + } + phy_mask &= ~(1 << addr); } + return NULL; +} - /* Try Standard (ie Clause 22) access */ - r = get_phy_id(bus, addr, MDIO_DEVAD_NONE, &phy_id); - if (r) - return NULL; - - /* If the PHY ID is mostly f's, we didn't find anything */ - if ((phy_id & 0x1fffffff) != 0x1fffffff) - return phy_device_create(bus, addr, phy_id, interface); +static struct phy_device *get_phy_device_by_mask(struct mii_dev *bus, + unsigned phy_mask, phy_interface_t interface) +{ + int i; + struct phy_device *phydev; + phydev = search_for_existing_phy(bus, phy_mask, interface); + if (phydev) + return phydev; + /* Try Standard (ie Clause 22) access */ /* Otherwise we have to try Clause 45 */ - for (i = 1; i < 5; i++) { - r = get_phy_id(bus, addr, i, &phy_id); - if (r) + for (i = 0; i < 5; i++) { + phydev = create_phy_by_mask(bus, phy_mask, + i ? i : MDIO_DEVAD_NONE, interface); + if (IS_ERR(phydev)) return NULL; - - /* If the phy_id is mostly Fs, there is no device there */ - if ((phy_id & 0x1fffffff) != 0x1fffffff) - break; + if (phydev) + return phydev; } + printf("Phy not found\n"); + return phy_device_create(bus, ffs(phy_mask) - 1, 0xffffffff, interface); +} - return phy_device_create(bus, addr, phy_id, interface); +/** + * get_phy_device - reads the specified PHY device and returns its @phy_device struct + * @bus: the target MII bus + * @addr: PHY address on the MII bus + * + * Description: Reads the ID registers of the PHY at @addr on the + * @bus, then allocates and returns the phy_device to represent it. + */ +static struct phy_device *get_phy_device(struct mii_dev *bus, int addr, + phy_interface_t interface) +{ + return get_phy_device_by_mask(bus, 1 << addr, interface); } int phy_reset(struct phy_device *phydev) { + int err; int reg; int timeout = 500; int devad = MDIO_DEVAD_NONE; @@ -639,9 +721,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 @@ -652,19 +735,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; @@ -685,50 +768,59 @@ int miiphy_reset(const char *devname, unsigned char addr) return phy_reset(phydev); } -struct phy_device *phy_connect(struct mii_dev *bus, int addr, - struct eth_device *dev, - phy_interface_t interface) +struct phy_device *phy_find_by_mask(struct mii_dev *bus, unsigned phy_mask, + phy_interface_t interface) { - struct phy_device *phydev; - /* Reset the bus */ - bus->reset(bus); + if (bus->reset) + bus->reset(bus); /* 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); +} - phydev = get_phy_device(bus, addr, interface); - - if (!phydev) { - printf("Could not get PHY for %s:%d\n", bus->name, addr); - - return NULL; - } - +void phy_connect_dev(struct phy_device *phydev, struct eth_device *dev) +{ /* Soft Reset the PHY */ phy_reset(phydev); - - if (phydev->dev) + if (phydev->dev) { printf("%s:%d is connected to %s. Reconnecting to %s\n", - bus->name, addr, phydev->dev->name, dev->name); - + phydev->bus->name, phydev->addr, + phydev->dev->name, dev->name); + } phydev->dev = dev; - debug("%s connected to %s\n", dev->name, phydev->drv->name); +} + +struct phy_device *phy_connect(struct mii_dev *bus, int addr, + struct eth_device *dev, phy_interface_t interface) +{ + struct phy_device *phydev; + phydev = phy_find_by_mask(bus, 1 << addr, interface); + if (phydev) + phy_connect_dev(phydev, dev); + else + printf("Could not get PHY for %s: addr %d\n", bus->name, addr); return phydev; } +/* + * Start the PHY. Returns 0 on success, or a negative error code. + */ int phy_startup(struct phy_device *phydev) { if (phydev->drv->startup) - phydev->drv->startup(phydev); + return phydev->drv->startup(phydev); return 0; } static int __board_phy_config(struct phy_device *phydev) { + if (phydev->drv->config) + return phydev->drv->config(phydev); return 0; } @@ -737,9 +829,6 @@ int board_phy_config(struct phy_device *phydev) int phy_config(struct phy_device *phydev) { - if (phydev->drv->config) - phydev->drv->config(phydev); - /* Invoke an optional board-specific helper */ board_phy_config(phydev);