+// SPDX-License-Identifier: GPL-2.0+
/*
* Generic PHY Management code
*
- * SPDX-License-Identifier: GPL-2.0+
- *
* Copyright 2011 Freescale Semiconductor, Inc.
* author Andy Fleming
*
#include <config.h>
#include <common.h>
+#include <console.h>
#include <dm.h>
#include <malloc.h>
#include <net.h>
/* Generic PHY support and helper functions */
/**
- * genphy_config_advert - sanitize and advertise auto-negotation parameters
+ * genphy_config_advert - sanitize and advertise auto-negotiation parameters
* @phydev: target phy_device struct
*
* Description: Writes MII_ADVERTISE with the appropriate values,
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;
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;
}
-
/**
* genphy_setup_forced - configures/forces speed/duplex from @phydev
* @phydev: target phy_device struct
static int genphy_setup_forced(struct phy_device *phydev)
{
int err;
- int ctl = 0;
+ int ctl = BMCR_ANRESTART;
- phydev->pause = phydev->asym_pause = 0;
+ phydev->pause = 0;
+ phydev->asym_pause = 0;
- if (SPEED_1000 == phydev->speed)
+ if (phydev->speed == SPEED_1000)
ctl |= BMCR_SPEED1000;
- else if (SPEED_100 == phydev->speed)
+ else if (phydev->speed == SPEED_100)
ctl |= BMCR_SPEED100;
- if (DUPLEX_FULL == phydev->duplex)
+ if (phydev->duplex == DUPLEX_FULL)
ctl |= BMCR_FULLDPLX;
err = phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, ctl);
return err;
}
-
/**
* genphy_restart_aneg - Enable and Restart Autonegotiation
* @phydev: target phy_device struct
return ctl;
}
-
/**
* genphy_config_aneg - restart auto-negotiation or write BMCR
* @phydev: target phy_device struct
{
int result;
- if (AUTONEG_ENABLE != phydev->autoneg)
+ if (phydev->autoneg != AUTONEG_ENABLE)
return genphy_setup_forced(phydev);
result = genphy_config_advert(phydev);
return result;
if (result == 0) {
- /* Advertisment hasn't changed, but maybe aneg was never on to
- * begin with? Or maybe phy was isolated? */
+ /*
+ * Advertisment hasn't changed, but maybe aneg was never on to
+ * begin with? Or maybe phy was isolated?
+ */
int ctl = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
if (ctl < 0)
result = 1; /* do restart aneg */
}
- /* Only restart aneg if we are advertising something different
- * than we were before. */
+ /*
+ * Only restart aneg if we are advertising something different
+ * than we were before.
+ */
if (result > 0)
result = genphy_restart_aneg(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",
- phydev->dev->name);
+ phydev->dev->name);
while (!(mii_reg & BMSR_ANEGCOMPLETE)) {
/*
* Timeout reached ?
if (i > PHY_ANEG_TIMEOUT) {
printf(" TIMEOUT !\n");
phydev->link = 0;
- return 0;
+ return -ETIMEDOUT;
}
if (ctrlc()) {
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;
*/
gblpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_STAT1000);
if (gblpa < 0) {
- debug("Could not read MII_STAT1000. Ignoring gigabit capability\n");
+ debug("Could not read MII_STAT1000. ");
+ debug("Ignoring gigabit capability\n");
gblpa = 0;
}
gblpa &= phy_read(phydev,
if (lpa & LPA_100FULL)
phydev->duplex = DUPLEX_FULL;
- } else if (lpa & LPA_10FULL)
+ } else if (lpa & LPA_10FULL) {
phydev->duplex = DUPLEX_FULL;
+ }
/*
* Extended status may indicate that the PHY supports
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);
features |= SUPPORTED_1000baseX_Half;
}
- phydev->supported = features;
- phydev->advertising = features;
+ phydev->supported &= features;
+ phydev->advertising &= features;
genphy_config_aneg(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)
.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,
int phy_init(void)
{
+#ifdef CONFIG_B53_SWITCH
+ phy_b53_init();
+#endif
+#ifdef CONFIG_MV88E61XX_SWITCH
+ phy_mv88e61xx_init();
+#endif
#ifdef CONFIG_PHY_AQUANTIA
phy_aquantia_init();
#endif
#ifdef CONFIG_PHY_MARVELL
phy_marvell_init();
#endif
-#ifdef CONFIG_PHY_MICREL
- phy_micrel_init();
+#ifdef CONFIG_PHY_MICREL_KSZ8XXX
+ phy_micrel_ksz8xxx_init();
+#endif
+#ifdef CONFIG_PHY_MICREL_KSZ90X1
+ phy_micrel_ksz90x1_init();
+#endif
+#ifdef CONFIG_PHY_MESON_GXL
+ phy_meson_gxl_init();
#endif
#ifdef CONFIG_PHY_NATSEMI
phy_natsemi_init();
#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
+#ifdef CONFIG_PHY_MSCC
+ phy_mscc_init();
+#endif
+#ifdef CONFIG_PHY_FIXED
+ phy_fixed_init();
+#endif
return 0;
}
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;
+}
+
static int phy_probe(struct phy_device *phydev)
{
int err = 0;
- phydev->advertising = phydev->supported = phydev->drv->features;
+ phydev->advertising = phydev->drv->features;
+ phydev->supported = phydev->drv->features;
+
phydev->mmds = phydev->drv->mmds;
if (phydev->drv->probe)
}
static struct phy_driver *get_phy_driver(struct phy_device *phydev,
- phy_interface_t interface)
+ phy_interface_t interface)
{
struct list_head *entry;
int phy_id = phydev->phy_id;
{
struct phy_device *dev;
- /* We allocate the device, and initialize the
- * default values */
+ /*
+ * We allocate the device, and initialize the
+ * default values
+ */
dev = malloc(sizeof(*dev));
if (!dev) {
printf("Failed to allocate PHY device for %s:%d\n",
- bus->name, addr);
+ bus->name, addr);
return NULL;
}
memset(dev, 0, sizeof(*dev));
dev->duplex = -1;
- dev->link = 1;
+ dev->link = 0;
dev->interface = interface;
dev->autoneg = AUTONEG_ENABLE;
{
int phy_reg;
- /* Grab the bits from PHYIR1, and put them
- * in the upper half */
+ /*
+ * Grab the bits from PHYIR1, and put them
+ * in the upper half
+ */
phy_reg = bus->read(bus, addr, devad, MII_PHYSID1);
if (phy_reg < 0)
}
static struct phy_device *create_phy_by_mask(struct mii_dev *bus,
- unsigned phy_mask, int devad, phy_interface_t interface)
+ uint phy_mask, int devad,
+ phy_interface_t interface)
{
u32 phy_id = 0xffffffff;
+
while (phy_mask) {
int addr = ffs(phy_mask) - 1;
int r = get_phy_id(bus, addr, devad, &phy_id);
}
static struct phy_device *search_for_existing_phy(struct mii_dev *bus,
- unsigned phy_mask, phy_interface_t interface)
+ uint phy_mask,
+ phy_interface_t interface)
{
/* If we have one, return the existing device, with new interface */
while (phy_mask) {
int addr = ffs(phy_mask) - 1;
+
if (bus->phymap[addr]) {
bus->phymap[addr]->interface = interface;
return bus->phymap[addr];
}
static struct phy_device *get_phy_device_by_mask(struct mii_dev *bus,
- unsigned phy_mask, phy_interface_t interface)
+ uint phy_mask,
+ phy_interface_t interface)
{
int i;
struct phy_device *phydev;
/* Otherwise we have to try Clause 45 */
for (i = 0; i < 5; i++) {
phydev = create_phy_by_mask(bus, phy_mask,
- i ? i : MDIO_DEVAD_NONE, interface);
+ i ? i : MDIO_DEVAD_NONE, interface);
if (IS_ERR(phydev))
return NULL;
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;
}
/**
- * get_phy_device - reads the specified PHY device and returns its @phy_device struct
+ * 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
*
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)) {
}
#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;
}
* 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);
return phy_reset(phydev);
}
-struct phy_device *phy_find_by_mask(struct mii_dev *bus, unsigned phy_mask,
- phy_interface_t interface)
+struct phy_device *phy_find_by_mask(struct mii_dev *bus, uint phy_mask,
+ phy_interface_t interface)
{
/* Reset the bus */
if (bus->reset) {
bus->reset(bus);
/* Wait 15ms to make sure the PHY has come out of hard reset */
- udelay(15000);
+ mdelay(15);
}
return get_phy_device_by_mask(bus, phy_mask, interface);
{
/* 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);
+ phydev->bus->name, phydev->addr,
+ phydev->dev->name, dev->name);
}
phydev->dev = 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)
+ 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)
+ struct eth_device *dev,
+ phy_interface_t interface)
#endif
{
- struct phy_device *phydev;
+ struct phy_device *phydev = NULL;
+#ifdef CONFIG_PHY_FIXED
+ int sn;
+ const char *name;
+
+ sn = fdt_first_subnode(gd->fdt_blob, dev_of_offset(dev));
+ while (sn > 0) {
+ name = fdt_get_name(gd->fdt_blob, sn, NULL);
+ if (name && strcmp(name, "fixed-link") == 0) {
+ phydev = phy_device_create(bus,
+ sn, PHY_FIXED_ID, interface);
+ break;
+ }
+ sn = fdt_next_subnode(gd->fdt_blob, sn);
+ }
+#endif
+ if (!phydev)
+ phydev = phy_find_by_mask(bus, 1 << addr, interface);
- phydev = phy_find_by_mask(bus, 1 << addr, interface);
if (phydev)
phy_connect_dev(phydev, dev);
else
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)