#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;
int ctl;
ctl = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
-
if (ctl < 0)
return ctl;
int genphy_update_link(struct phy_device *phydev)
{
int mii_reg;
+ int bmcr;
/*
* Wait if the link is up, and autonegotiation is in progress
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;
return mii_reg;
/* We're using autonegotiation */
- if (mii_reg & BMSR_ANEGCAPABLE) {
+ if (phydev->supported & SUPPORTED_Autoneg) {
int ret;
- u16 lpa;
- u16 gblpa = 0;
- int estatus = 0;
+ 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
*/
ret = phy_read(phydev, MDIO_DEVAD_NONE, MII_STAT1000);
- if (ret < 0)
- return ret;
+ 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);
/* 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);
}
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;
+}