X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fnet%2Fphy%2Fphy.c;h=62925bb2863225056d414c3283fc94a534d8f1b0;hb=31043e20ae748635f142483e8b7b645948687055;hp=c7edcc09097b00e32c709e3328a2cccb93c31e26;hpb=aef293bc85dad4f8d645e46d723606a747236a60;p=u-boot diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index c7edcc0909..62925bb286 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,7 +109,7 @@ 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; @@ -279,14 +270,15 @@ 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); /* We're using autonegotiation */ if (mii_reg & BMSR_ANEGCAPABLE) { u32 lpa = 0; - u32 gblpa = 0; + int gblpa = 0; + u32 estatus = 0; /* Check for gigabit capability */ if (mii_reg & BMSR_ERCAP) { @@ -294,6 +286,10 @@ static int genphy_parse_link(struct phy_device *phydev) * both PHYs in the link */ gblpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_STAT1000); + if (gblpa < 0) { + debug("Could not read MII_STAT1000. Ignoring gigabit capability\n"); + gblpa = 0; + } gblpa &= phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000) << 2; } @@ -326,6 +322,26 @@ 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 & (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); @@ -383,6 +399,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 +449,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 +470,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 +491,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 +514,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 +531,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 +576,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,49 +600,73 @@ 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) @@ -685,50 +739,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); +} - printf("%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 +800,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);