#include <config.h>
#include <common.h>
+#include <dm.h>
#include <malloc.h>
#include <net.h>
#include <command.h>
#include <phy.h>
#include <errno.h>
#include <linux/err.h>
+#include <linux/compiler.h>
+
+DECLARE_GLOBAL_DATA_PTR;
/* Generic PHY support and helper functions */
/* Setup standard advertisement */
oldadv = adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE);
-
if (adv < 0)
return adv;
if (phydev->supported & (SUPPORTED_1000baseT_Half |
SUPPORTED_1000baseT_Full)) {
oldadv = adv = phy_read(phydev, MDIO_DEVAD_NONE, MII_CTRL1000);
-
if (adv < 0)
return adv;
*/
static int genphy_setup_forced(struct phy_device *phydev)
{
- int err;
int ctl = 0;
phydev->pause = phydev->asym_pause = 0;
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);
}
int ctl;
ctl = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
-
if (ctl < 0)
return ctl;
/* 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);
}
*/
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
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;
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;
{
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) {
+ if (phydev->supported & SUPPORTED_Autoneg) {
+ int ret;
u32 lpa = 0;
u32 gblpa = 0;
u32 estatus = 0;
/* Check for gigabit capability */
- if (mii_reg & BMSR_ERCAP) {
+ if (phydev->supported & (SUPPORTED_1000baseT_Full |
+ SUPPORTED_1000baseT_Half)) {
/* 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) {
+ debug("Could not read MII_STAT1000. Ignoring gigabit capability\n");
+ ret = 0;
+ }
+ 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
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;
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)) {
}
} 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;
/* Do we support autonegotiation? */
val = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
-
if (val < 0)
return val;
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
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;
}
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;
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;
* 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;
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)
+ if (r == 0 && (phy_id & 0x1fffffff) != 0x1fffffff)
return phy_device_create(bus, addr, phy_id, interface);
phy_mask &= ~(1 << addr);
}
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);
}
int phy_reset(struct phy_device *phydev)
{
+ int err;
int reg;
int timeout = 500;
int devad = MDIO_DEVAD_NONE;
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
* 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;
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);
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;
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 */
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;
+}