summaryrefslogtreecommitdiff
path: root/drivers/net/phy
diff options
context:
space:
mode:
authorWolfgang Denk <wd@denx.de>2011-04-20 23:01:52 +0200
committerWolfgang Denk <wd@denx.de>2011-04-20 23:01:52 +0200
commitaef293bc85dad4f8d645e46d723606a747236a60 (patch)
treeb18d3f3bb2dfafc6116a7f079fc0e7411936f78a /drivers/net/phy
parent9dd5dad88777a7c6a63f8c103706b57364a135f4 (diff)
parent995daa0b81f35c93a1d14e5c6a932bc304d06718 (diff)
downloadu-boot-imx-aef293bc85dad4f8d645e46d723606a747236a60.zip
u-boot-imx-aef293bc85dad4f8d645e46d723606a747236a60.tar.gz
u-boot-imx-aef293bc85dad4f8d645e46d723606a747236a60.tar.bz2
Merge branch 'phylib' of git://git.denx.de/u-boot-mmc
Diffstat (limited to 'drivers/net/phy')
-rw-r--r--drivers/net/phy/Makefile13
-rw-r--r--drivers/net/phy/atheros.c48
-rw-r--r--drivers/net/phy/broadcom.c288
-rw-r--r--drivers/net/phy/davicom.c98
-rw-r--r--drivers/net/phy/generic_10g.c105
-rw-r--r--drivers/net/phy/lxt.c87
-rw-r--r--drivers/net/phy/marvell.c367
-rw-r--r--drivers/net/phy/micrel.c40
-rw-r--r--drivers/net/phy/natsemi.c96
-rw-r--r--drivers/net/phy/phy.c755
-rw-r--r--drivers/net/phy/realtek.c130
-rw-r--r--drivers/net/phy/teranetics.c62
-rw-r--r--drivers/net/phy/vitesse.c242
13 files changed, 2331 insertions, 0 deletions
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index bba8901..a59834b 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -28,6 +28,19 @@ LIB := $(obj)libphy.o
COBJS-$(CONFIG_BITBANGMII) += miiphybb.o
COBJS-$(CONFIG_MV88E61XX_SWITCH) += mv88e61xx.o
+COBJS-$(CONFIG_PHYLIB) += phy.o
+COBJS-$(CONFIG_PHYLIB_10G) += generic_10g.o
+COBJS-$(CONFIG_PHY_ATHEROS) += atheros.o
+COBJS-$(CONFIG_PHY_BROADCOM) += broadcom.o
+COBJS-$(CONFIG_PHY_DAVICOM) += davicom.o
+COBJS-$(CONFIG_PHY_LXT) += lxt.o
+COBJS-$(CONFIG_PHY_MARVELL) += marvell.o
+COBJS-$(CONFIG_PHY_MICREL) += micrel.o
+COBJS-$(CONFIG_PHY_NATSEMI) += natsemi.o
+COBJS-$(CONFIG_PHY_REALTEK) += realtek.o
+COBJS-$(CONFIG_PHY_TERANETICS) += teranetics.o
+COBJS-$(CONFIG_PHY_VITESSE) += vitesse.o
+
COBJS := $(COBJS-y)
SRCS := $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(COBJS))
diff --git a/drivers/net/phy/atheros.c b/drivers/net/phy/atheros.c
new file mode 100644
index 0000000..798473d
--- /dev/null
+++ b/drivers/net/phy/atheros.c
@@ -0,0 +1,48 @@
+/*
+ * Atheros PHY drivers
+ *
+ * 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
+ *
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ *
+ */
+#include <phy.h>
+
+static int ar8021_config(struct phy_device *phydev)
+{
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x1d, 0x05);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x3D47);
+
+ return 0;
+}
+
+struct phy_driver AR8021_driver = {
+ .name = "AR8021",
+ .uid = 0x4dd040,
+ .mask = 0xfffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = ar8021_config,
+ .startup = genphy_startup,
+ .shutdown = genphy_shutdown,
+};
+
+int phy_atheros_init(void)
+{
+ phy_register(&AR8021_driver);
+
+ return 0;
+}
diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c
new file mode 100644
index 0000000..427ac60
--- /dev/null
+++ b/drivers/net/phy/broadcom.c
@@ -0,0 +1,288 @@
+/*
+ * Broadcom PHY drivers
+ *
+ * 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
+ *
+ * Copyright 2010-2011 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ *
+ */
+#include <config.h>
+#include <common.h>
+#include <phy.h>
+
+/* Broadcom BCM54xx -- taken from linux sungem_phy */
+#define MIIM_BCM54xx_AUXCNTL 0x18
+#define MIIM_BCM54xx_AUXCNTL_ENCODE(val) (((val & 0x7) << 12)|(val & 0x7))
+#define MIIM_BCM54xx_AUXSTATUS 0x19
+#define MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK 0x0700
+#define MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT 8
+
+#define MIIM_BCM54XX_SHD 0x1c
+#define MIIM_BCM54XX_SHD_WRITE 0x8000
+#define MIIM_BCM54XX_SHD_VAL(x) ((x & 0x1f) << 10)
+#define MIIM_BCM54XX_SHD_DATA(x) ((x & 0x3ff) << 0)
+#define MIIM_BCM54XX_SHD_WR_ENCODE(val, data) \
+ (MIIM_BCM54XX_SHD_WRITE | MIIM_BCM54XX_SHD_VAL(val) | \
+ MIIM_BCM54XX_SHD_DATA(data))
+
+#define MIIM_BCM54XX_EXP_DATA 0x15 /* Expansion register data */
+#define MIIM_BCM54XX_EXP_SEL 0x17 /* Expansion register select */
+#define MIIM_BCM54XX_EXP_SEL_SSD 0x0e00 /* Secondary SerDes select */
+#define MIIM_BCM54XX_EXP_SEL_ER 0x0f00 /* Expansion register select */
+
+/* Broadcom BCM5461S */
+static int bcm5461_config(struct phy_device *phydev)
+{
+ genphy_config_aneg(phydev);
+
+ phy_reset(phydev);
+
+ return 0;
+}
+
+static int bcm54xx_parse_status(struct phy_device *phydev)
+{
+ unsigned int mii_reg;
+
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXSTATUS);
+
+ switch ((mii_reg & MIIM_BCM54xx_AUXSTATUS_LINKMODE_MASK) >>
+ MIIM_BCM54xx_AUXSTATUS_LINKMODE_SHIFT) {
+ case 1:
+ phydev->duplex = DUPLEX_HALF;
+ phydev->speed = SPEED_10;
+ break;
+ case 2:
+ phydev->duplex = DUPLEX_FULL;
+ phydev->speed = SPEED_10;
+ break;
+ case 3:
+ phydev->duplex = DUPLEX_HALF;
+ phydev->speed = SPEED_100;
+ break;
+ case 5:
+ phydev->duplex = DUPLEX_FULL;
+ phydev->speed = SPEED_100;
+ break;
+ case 6:
+ phydev->duplex = DUPLEX_HALF;
+ phydev->speed = SPEED_1000;
+ break;
+ case 7:
+ phydev->duplex = DUPLEX_FULL;
+ phydev->speed = SPEED_1000;
+ break;
+ default:
+ printf("Auto-neg error, defaulting to 10BT/HD\n");
+ phydev->duplex = DUPLEX_HALF;
+ phydev->speed = SPEED_10;
+ break;
+ }
+
+ return 0;
+}
+
+static int bcm54xx_startup(struct phy_device *phydev)
+{
+ /* Read the Status (2x to make sure link is right) */
+ genphy_update_link(phydev);
+ bcm54xx_parse_status(phydev);
+
+ return 0;
+}
+
+/* Broadcom BCM5482S */
+/*
+ * "Ethernet@Wirespeed" needs to be enabled to achieve link in certain
+ * circumstances. eg a gigabit TSEC connected to a gigabit switch with
+ * a 4-wire ethernet cable. Both ends advertise gigabit, but can't
+ * link. "Ethernet@Wirespeed" reduces advertised speed until link
+ * can be achieved.
+ */
+static u32 bcm5482_read_wirespeed(struct phy_device *phydev, u32 reg)
+{
+ return (phy_read(phydev, MDIO_DEVAD_NONE, reg) & 0x8FFF) | 0x8010;
+}
+
+static int bcm5482_config(struct phy_device *phydev)
+{
+ unsigned int reg;
+
+ /* reset the PHY */
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
+ reg |= BMCR_RESET;
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, reg);
+
+ /* Setup read from auxilary control shadow register 7 */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL,
+ MIIM_BCM54xx_AUXCNTL_ENCODE(7));
+ /* Read Misc Control register and or in Ethernet@Wirespeed */
+ reg = bcm5482_read_wirespeed(phydev, MIIM_BCM54xx_AUXCNTL);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54xx_AUXCNTL, reg);
+
+ /* Initial config/enable of secondary SerDes interface */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_SHD,
+ MIIM_BCM54XX_SHD_WR_ENCODE(0x14, 0xf));
+ /* Write intial value to secondary SerDes Contol */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL,
+ MIIM_BCM54XX_EXP_SEL_SSD | 0);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA,
+ BMCR_ANRESTART);
+ /* Enable copper/fiber auto-detect */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_SHD,
+ MIIM_BCM54XX_SHD_WR_ENCODE(0x1e, 0x201));
+
+ genphy_config_aneg(phydev);
+
+ return 0;
+}
+
+/*
+ * Find out if PHY is in copper or serdes mode by looking at Expansion Reg
+ * 0x42 - "Operating Mode Status Register"
+ */
+static int bcm5482_is_serdes(struct phy_device *phydev)
+{
+ u16 val;
+ int serdes = 0;
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL,
+ MIIM_BCM54XX_EXP_SEL_ER | 0x42);
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA);
+
+ switch (val & 0x1f) {
+ case 0x0d: /* RGMII-to-100Base-FX */
+ case 0x0e: /* RGMII-to-SGMII */
+ case 0x0f: /* RGMII-to-SerDes */
+ case 0x12: /* SGMII-to-SerDes */
+ case 0x13: /* SGMII-to-100Base-FX */
+ case 0x16: /* SerDes-to-Serdes */
+ serdes = 1;
+ break;
+ case 0x6: /* RGMII-to-Copper */
+ case 0x14: /* SGMII-to-Copper */
+ case 0x17: /* SerDes-to-Copper */
+ break;
+ default:
+ printf("ERROR, invalid PHY mode (0x%x\n)", val);
+ break;
+ }
+
+ return serdes;
+}
+
+/*
+ * Determine SerDes link speed and duplex from Expansion reg 0x42 "Operating
+ * Mode Status Register"
+ */
+static u32 bcm5482_parse_serdes_sr(struct phy_device *phydev)
+{
+ u16 val;
+ int i = 0;
+
+ /* Wait 1s for link - Clause 37 autonegotiation happens very fast */
+ while (1) {
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_SEL,
+ MIIM_BCM54XX_EXP_SEL_ER | 0x42);
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_BCM54XX_EXP_DATA);
+
+ if (val & 0x8000)
+ break;
+
+ if (i++ > 1000) {
+ phydev->link = 0;
+ return 1;
+ }
+
+ udelay(1000); /* 1 ms */
+ }
+
+ phydev->link = 1;
+ switch ((val >> 13) & 0x3) {
+ case (0x00):
+ phydev->speed = 10;
+ break;
+ case (0x01):
+ phydev->speed = 100;
+ break;
+ case (0x02):
+ phydev->speed = 1000;
+ break;
+ }
+
+ phydev->duplex = (val & 0x1000) == 0x1000;
+
+ return 0;
+}
+
+/*
+ * Figure out if BCM5482 is in serdes or copper mode and determine link
+ * configuration accordingly
+ */
+static int bcm5482_startup(struct phy_device *phydev)
+{
+ if (bcm5482_is_serdes(phydev)) {
+ bcm5482_parse_serdes_sr(phydev);
+ phydev->port = PORT_FIBRE;
+ } else {
+ /* Wait for auto-negotiation to complete or fail */
+ genphy_update_link(phydev);
+ /* Parse BCM54xx copper aux status register */
+ bcm54xx_parse_status(phydev);
+ }
+
+ return 0;
+}
+
+static struct phy_driver BCM5461S_driver = {
+ .name = "Broadcom BCM5461S",
+ .uid = 0x2060c0,
+ .mask = 0xfffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &bcm5461_config,
+ .startup = &bcm54xx_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver BCM5464S_driver = {
+ .name = "Broadcom BCM5464S",
+ .uid = 0x2060b0,
+ .mask = 0xfffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &bcm5461_config,
+ .startup = &bcm54xx_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver BCM5482S_driver = {
+ .name = "Broadcom BCM5482S",
+ .uid = 0x143bcb0,
+ .mask = 0xffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &bcm5482_config,
+ .startup = &bcm5482_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int phy_broadcom_init(void)
+{
+ phy_register(&BCM5482S_driver);
+ phy_register(&BCM5464S_driver);
+ phy_register(&BCM5461S_driver);
+
+ return 0;
+}
diff --git a/drivers/net/phy/davicom.c b/drivers/net/phy/davicom.c
new file mode 100644
index 0000000..e96a4af
--- /dev/null
+++ b/drivers/net/phy/davicom.c
@@ -0,0 +1,98 @@
+/*
+ * Davicom PHY drivers
+ *
+ * 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
+ *
+ * Copyright 2010-2011 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ *
+ */
+#include <phy.h>
+
+#define MIIM_DM9161_SCR 0x10
+#define MIIM_DM9161_SCR_INIT 0x0610
+
+/* DM9161 Specified Configuration and Status Register */
+#define MIIM_DM9161_SCSR 0x11
+#define MIIM_DM9161_SCSR_100F 0x8000
+#define MIIM_DM9161_SCSR_100H 0x4000
+#define MIIM_DM9161_SCSR_10F 0x2000
+#define MIIM_DM9161_SCSR_10H 0x1000
+
+/* DM9161 10BT Configuration/Status */
+#define MIIM_DM9161_10BTCSR 0x12
+#define MIIM_DM9161_10BTCSR_INIT 0x7800
+
+
+/* Davicom DM9161E */
+static int dm9161_config(struct phy_device *phydev)
+{
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_ISOLATE);
+ /* Do not bypass the scrambler/descrambler */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_DM9161_SCR,
+ MIIM_DM9161_SCR_INIT);
+ /* Clear 10BTCSR to default */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_DM9161_10BTCSR,
+ MIIM_DM9161_10BTCSR_INIT);
+
+ genphy_config_aneg(phydev);
+
+ return 0;
+}
+
+static int dm9161_parse_status(struct phy_device *phydev)
+{
+ int mii_reg;
+
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_DM9161_SCSR);
+
+ if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_100H))
+ phydev->speed = SPEED_100;
+ else
+ phydev->speed = SPEED_10;
+
+ if (mii_reg & (MIIM_DM9161_SCSR_100F | MIIM_DM9161_SCSR_10F))
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ return 0;
+}
+
+static int dm9161_startup(struct phy_device *phydev)
+{
+ genphy_update_link(phydev);
+ dm9161_parse_status(phydev);
+
+ return 0;
+}
+
+static struct phy_driver DM9161_driver = {
+ .name = "Davicom DM9161E",
+ .uid = 0x181b880,
+ .mask = 0xffffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &dm9161_config,
+ .startup = &dm9161_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int phy_davicom_init(void)
+{
+ phy_register(&DM9161_driver);
+
+ return 0;
+}
diff --git a/drivers/net/phy/generic_10g.c b/drivers/net/phy/generic_10g.c
new file mode 100644
index 0000000..315c508
--- /dev/null
+++ b/drivers/net/phy/generic_10g.c
@@ -0,0 +1,105 @@
+/*
+ * 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
+ *
+ *
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ *
+ * Based loosely off of Linux's PHY Lib
+ */
+
+#include <config.h>
+#include <common.h>
+#include <miiphy.h>
+#include <phy.h>
+
+int gen10g_shutdown(struct phy_device *phydev)
+{
+ return 0;
+}
+
+int gen10g_startup(struct phy_device *phydev)
+{
+ int devad, reg;
+ u32 mmd_mask = phydev->mmds;
+
+ phydev->link = 1;
+
+ /* For now just lie and say it's 10G all the time */
+ phydev->speed = SPEED_10000;
+ phydev->duplex = DUPLEX_FULL;
+
+ for (devad = 0; mmd_mask; devad++, mmd_mask = mmd_mask >> 1) {
+ if (!mmd_mask & 1)
+ continue;
+
+ /* Read twice because link state is latched and a
+ * read moves the current state into the register */
+ phy_read(phydev, devad, MDIO_STAT1);
+ reg = phy_read(phydev, devad, MDIO_STAT1);
+ if (reg < 0 || !(reg & MDIO_STAT1_LSTATUS))
+ phydev->link = 0;
+ }
+
+ return 0;
+}
+
+int gen10g_discover_mmds(struct phy_device *phydev)
+{
+ int mmd, stat2, devs1, devs2;
+
+ /* Assume PHY must have at least one of PMA/PMD, WIS, PCS, PHY
+ * XS or DTE XS; give up if none is present. */
+ for (mmd = 1; mmd <= 5; mmd++) {
+ /* Is this MMD present? */
+ stat2 = phy_read(phydev, mmd, MDIO_STAT2);
+ if (stat2 < 0 ||
+ (stat2 & MDIO_STAT2_DEVPRST) != MDIO_STAT2_DEVPRST_VAL)
+ continue;
+
+ /* It should tell us about all the other MMDs */
+ devs1 = phy_read(phydev, mmd, MDIO_DEVS1);
+ devs2 = phy_read(phydev, mmd, MDIO_DEVS2);
+ if (devs1 < 0 || devs2 < 0)
+ continue;
+
+ phydev->mmds = devs1 | (devs2 << 16);
+ return 0;
+ }
+
+ return 0;
+}
+
+int gen10g_config(struct phy_device *phydev)
+{
+ /* For now, assume 10000baseT. Fill in later */
+ phydev->supported = phydev->advertising = SUPPORTED_10000baseT_Full;
+
+ return gen10g_discover_mmds(phydev);
+}
+
+struct phy_driver gen10g_driver = {
+ .uid = 0xffffffff,
+ .mask = 0xffffffff,
+ .name = "Generic 10G PHY",
+ .features = 0,
+ .config = gen10g_config,
+ .startup = gen10g_startup,
+ .shutdown = gen10g_shutdown,
+};
+
diff --git a/drivers/net/phy/lxt.c b/drivers/net/phy/lxt.c
new file mode 100644
index 0000000..d67bbdd
--- /dev/null
+++ b/drivers/net/phy/lxt.c
@@ -0,0 +1,87 @@
+/*
+ * LXT PHY drivers
+ *
+ * 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
+ *
+ * Copyright 2010-2011 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ *
+ */
+#include <phy.h>
+
+/* LXT971 Status 2 registers */
+#define MIIM_LXT971_SR2 0x11 /* Status Register 2 */
+#define MIIM_LXT971_SR2_SPEED_MASK 0x4200
+#define MIIM_LXT971_SR2_10HDX 0x0000 /* 10 Mbit half duplex selected */
+#define MIIM_LXT971_SR2_10FDX 0x0200 /* 10 Mbit full duplex selected */
+#define MIIM_LXT971_SR2_100HDX 0x4000 /* 100 Mbit half duplex selected */
+#define MIIM_LXT971_SR2_100FDX 0x4200 /* 100 Mbit full duplex selected */
+
+
+/* LXT971 */
+static int lxt971_parse_status(struct phy_device *phydev)
+{
+ int mii_reg;
+ int speed;
+
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_LXT971_SR2);
+ speed = mii_reg & MIIM_LXT971_SR2_SPEED_MASK;
+
+ switch (speed) {
+ case MIIM_LXT971_SR2_10HDX:
+ phydev->speed = SPEED_10;
+ phydev->duplex = DUPLEX_HALF;
+ break;
+ case MIIM_LXT971_SR2_10FDX:
+ phydev->speed = SPEED_10;
+ phydev->duplex = DUPLEX_FULL;
+ break;
+ case MIIM_LXT971_SR2_100HDX:
+ phydev->speed = SPEED_100;
+ phydev->duplex = DUPLEX_HALF;
+ break;
+ default:
+ phydev->speed = SPEED_100;
+ phydev->duplex = DUPLEX_FULL;
+ }
+
+ return 0;
+}
+
+static int lxt971_startup(struct phy_device *phydev)
+{
+ genphy_update_link(phydev);
+ lxt971_parse_status(phydev);
+
+ return 0;
+}
+
+static struct phy_driver LXT971_driver = {
+ .name = "LXT971",
+ .uid = 0x1378e0,
+ .mask = 0xfffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &lxt971_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int phy_lxt_init(void)
+{
+ phy_register(&LXT971_driver);
+
+ return 0;
+}
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
new file mode 100644
index 0000000..bd1cdc4
--- /dev/null
+++ b/drivers/net/phy/marvell.c
@@ -0,0 +1,367 @@
+/*
+ * Marvell PHY drivers
+ *
+ * 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
+ *
+ * Copyright 2010-2011 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ *
+ */
+#include <config.h>
+#include <common.h>
+#include <phy.h>
+
+#define PHY_AUTONEGOTIATE_TIMEOUT 5000
+
+/* 88E1011 PHY Status Register */
+#define MIIM_88E1xxx_PHY_STATUS 0x11
+#define MIIM_88E1xxx_PHYSTAT_SPEED 0xc000
+#define MIIM_88E1xxx_PHYSTAT_GBIT 0x8000
+#define MIIM_88E1xxx_PHYSTAT_100 0x4000
+#define MIIM_88E1xxx_PHYSTAT_DUPLEX 0x2000
+#define MIIM_88E1xxx_PHYSTAT_SPDDONE 0x0800
+#define MIIM_88E1xxx_PHYSTAT_LINK 0x0400
+
+#define MIIM_88E1xxx_PHY_SCR 0x10
+#define MIIM_88E1xxx_PHY_MDI_X_AUTO 0x0060
+
+/* 88E1111 PHY LED Control Register */
+#define MIIM_88E1111_PHY_LED_CONTROL 24
+#define MIIM_88E1111_PHY_LED_DIRECT 0x4100
+#define MIIM_88E1111_PHY_LED_COMBINE 0x411C
+
+/* 88E1118 PHY defines */
+#define MIIM_88E1118_PHY_PAGE 22
+#define MIIM_88E1118_PHY_LED_PAGE 3
+
+/* 88E1121 PHY LED Control Register */
+#define MIIM_88E1121_PHY_LED_CTRL 16
+#define MIIM_88E1121_PHY_LED_PAGE 3
+#define MIIM_88E1121_PHY_LED_DEF 0x0030
+
+/* 88E1121 PHY IRQ Enable/Status Register */
+#define MIIM_88E1121_PHY_IRQ_EN 18
+#define MIIM_88E1121_PHY_IRQ_STATUS 19
+
+#define MIIM_88E1121_PHY_PAGE 22
+
+/* 88E1145 Extended PHY Specific Control Register */
+#define MIIM_88E1145_PHY_EXT_CR 20
+#define MIIM_M88E1145_RGMII_RX_DELAY 0x0080
+#define MIIM_M88E1145_RGMII_TX_DELAY 0x0002
+
+#define MIIM_88E1145_PHY_LED_CONTROL 24
+#define MIIM_88E1145_PHY_LED_DIRECT 0x4100
+
+#define MIIM_88E1145_PHY_PAGE 29
+#define MIIM_88E1145_PHY_CAL_OV 30
+
+#define MIIM_88E1149_PHY_PAGE 29
+
+/* Marvell 88E1011S */
+static int m88e1011s_config(struct phy_device *phydev)
+{
+ /* Reset and configure the PHY */
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
+
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x1d, 0x1f);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x200c);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x1d, 0x5);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x100);
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
+
+ genphy_config_aneg(phydev);
+
+ return 0;
+}
+
+/* Parse the 88E1011's status register for speed and duplex
+ * information
+ */
+static uint m88e1xxx_parse_status(struct phy_device *phydev)
+{
+ unsigned int speed;
+ unsigned int mii_reg;
+
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_88E1xxx_PHY_STATUS);
+
+ if ((mii_reg & MIIM_88E1xxx_PHYSTAT_LINK) &&
+ !(mii_reg & MIIM_88E1xxx_PHYSTAT_SPDDONE)) {
+ int i = 0;
+
+ puts("Waiting for PHY realtime link");
+ while (!(mii_reg & MIIM_88E1xxx_PHYSTAT_SPDDONE)) {
+ /* Timeout reached ? */
+ if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
+ puts(" TIMEOUT !\n");
+ phydev->link = 0;
+ break;
+ }
+
+ if ((i++ % 1000) == 0)
+ putc('.');
+ udelay(1000);
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE,
+ MIIM_88E1xxx_PHY_STATUS);
+ }
+ puts(" done\n");
+ udelay(500000); /* another 500 ms (results in faster booting) */
+ } else {
+ if (mii_reg & MIIM_88E1xxx_PHYSTAT_LINK)
+ phydev->link = 1;
+ else
+ phydev->link = 0;
+ }
+
+ if (mii_reg & MIIM_88E1xxx_PHYSTAT_DUPLEX)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ speed = mii_reg & MIIM_88E1xxx_PHYSTAT_SPEED;
+
+ switch (speed) {
+ case MIIM_88E1xxx_PHYSTAT_GBIT:
+ phydev->speed = SPEED_1000;
+ break;
+ case MIIM_88E1xxx_PHYSTAT_100:
+ phydev->speed = SPEED_100;
+ break;
+ default:
+ phydev->speed = SPEED_10;
+ break;
+ }
+
+ return 0;
+}
+
+static int m88e1011s_startup(struct phy_device *phydev)
+{
+ genphy_update_link(phydev);
+ m88e1xxx_parse_status(phydev);
+
+ return 0;
+}
+
+/* Marvell 88E1111S */
+static int m88e1111s_config(struct phy_device *phydev)
+{
+ int reg;
+
+ if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) ||
+ (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) ||
+ (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID) ||
+ (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)) {
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, 0x1b);
+ reg = (reg & 0xfff0) | 0xb;
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x1b, reg);
+ } else {
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x1b, 0x1f);
+ }
+
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x14, 0x0cd2);
+
+ genphy_config_aneg(phydev);
+
+ phy_reset(phydev);
+
+ return 0;
+}
+
+/* Marvell 88E1118 */
+static int m88e1118_config(struct phy_device *phydev)
+{
+ /* Change Page Number */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x0002);
+ /* Delay RGMII TX and RX */
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x15, 0x1070);
+ /* Change Page Number */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x0003);
+ /* Adjust LED control */
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x10, 0x021e);
+ /* Change Page Number */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x0000);
+
+ genphy_config_aneg(phydev);
+
+ phy_reset(phydev);
+
+ return 0;
+}
+
+static int m88e1118_startup(struct phy_device *phydev)
+{
+ /* Change Page Number */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1118_PHY_PAGE, 0x0000);
+
+ genphy_update_link(phydev);
+ m88e1xxx_parse_status(phydev);
+
+ return 0;
+}
+
+/* Marvell 88E1121R */
+static int m88e1121_config(struct phy_device *phydev)
+{
+ int pg;
+
+ /* Configure the PHY */
+ genphy_config_aneg(phydev);
+
+ /* Switch the page to access the led register */
+ pg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_88E1121_PHY_PAGE);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1121_PHY_PAGE,
+ MIIM_88E1121_PHY_LED_PAGE);
+ /* Configure leds */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1121_PHY_LED_CTRL,
+ MIIM_88E1121_PHY_LED_DEF);
+ /* Restore the page pointer */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1121_PHY_PAGE, pg);
+
+ /* Disable IRQs and de-assert interrupt */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1121_PHY_IRQ_EN, 0);
+ phy_read(phydev, MDIO_DEVAD_NONE, MIIM_88E1121_PHY_IRQ_STATUS);
+
+ return 0;
+}
+
+/* Marvell 88E1145 */
+static int m88e1145_config(struct phy_device *phydev)
+{
+ int reg;
+
+ /* Errata E0, E1 */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1145_PHY_PAGE, 0x001b);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1145_PHY_CAL_OV, 0x418f);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1145_PHY_PAGE, 0x0016);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1145_PHY_CAL_OV, 0xa2da);
+
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1xxx_PHY_SCR,
+ MIIM_88E1xxx_PHY_MDI_X_AUTO);
+
+ reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_88E1145_PHY_EXT_CR);
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)
+ reg |= MIIM_M88E1145_RGMII_RX_DELAY |
+ MIIM_M88E1145_RGMII_TX_DELAY;
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1145_PHY_EXT_CR, reg);
+
+ genphy_config_aneg(phydev);
+
+ phy_reset(phydev);
+
+ return 0;
+}
+
+static int m88e1145_startup(struct phy_device *phydev)
+{
+ genphy_update_link(phydev);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1145_PHY_LED_CONTROL,
+ MIIM_88E1145_PHY_LED_DIRECT);
+ m88e1xxx_parse_status(phydev);
+
+ return 0;
+}
+
+/* Marvell 88E1149S */
+static int m88e1149_config(struct phy_device *phydev)
+{
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1149_PHY_PAGE, 0x1f);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x200c);
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_88E1149_PHY_PAGE, 0x5);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x0);
+ phy_write(phydev, MDIO_DEVAD_NONE, 0x1e, 0x100);
+
+ genphy_config_aneg(phydev);
+
+ phy_reset(phydev);
+
+ return 0;
+}
+
+
+static struct phy_driver M88E1011S_driver = {
+ .name = "Marvell 88E1011S",
+ .uid = 0x1410c60,
+ .mask = 0xffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &m88e1011s_config,
+ .startup = &m88e1011s_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver M88E1111S_driver = {
+ .name = "Marvell 88E1111S",
+ .uid = 0x1410cc0,
+ .mask = 0xffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &m88e1111s_config,
+ .startup = &m88e1011s_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver M88E1118_driver = {
+ .name = "Marvell 88E1118",
+ .uid = 0x1410e10,
+ .mask = 0xffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &m88e1118_config,
+ .startup = &m88e1118_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver M88E1121R_driver = {
+ .name = "Marvell 88E1121R",
+ .uid = 0x1410cb0,
+ .mask = 0xffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &m88e1121_config,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver M88E1145_driver = {
+ .name = "Marvell 88E1145",
+ .uid = 0x1410cd0,
+ .mask = 0xffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &m88e1145_config,
+ .startup = &m88e1145_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver M88E1149S_driver = {
+ .name = "Marvell 88E1149S",
+ .uid = 0x1410ca0,
+ .mask = 0xffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &m88e1149_config,
+ .startup = &m88e1011s_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int phy_marvell_init(void)
+{
+ phy_register(&M88E1149S_driver);
+ phy_register(&M88E1145_driver);
+ phy_register(&M88E1121R_driver);
+ phy_register(&M88E1118_driver);
+ phy_register(&M88E1111S_driver);
+ phy_register(&M88E1011S_driver);
+
+ return 0;
+}
diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c
new file mode 100644
index 0000000..47064a1
--- /dev/null
+++ b/drivers/net/phy/micrel.c
@@ -0,0 +1,40 @@
+/*
+ * Micrel PHY drivers
+ *
+ * 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
+ *
+ * Copyright 2010-2011 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ *
+ */
+#include <phy.h>
+
+static struct phy_driver KSZ804_driver = {
+ .name = "Micrel KSZ804",
+ .uid = 0x221510,
+ .mask = 0xfffff0,
+ .features = PHY_BASIC_FEATURES,
+ .config = &genphy_config,
+ .startup = &genphy_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int phy_micrel_init(void)
+{
+ phy_register(&KSZ804_driver);
+
+ return 0;
+}
diff --git a/drivers/net/phy/natsemi.c b/drivers/net/phy/natsemi.c
new file mode 100644
index 0000000..ea60ac1
--- /dev/null
+++ b/drivers/net/phy/natsemi.c
@@ -0,0 +1,96 @@
+/*
+ * National Semiconductor PHY drivers
+ *
+ * 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
+ *
+ * Copyright 2010-2011 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ *
+ */
+#include <phy.h>
+
+/* DP83865 Link and Auto-Neg Status Register */
+#define MIIM_DP83865_LANR 0x11
+#define MIIM_DP83865_SPD_MASK 0x0018
+#define MIIM_DP83865_SPD_1000 0x0010
+#define MIIM_DP83865_SPD_100 0x0008
+#define MIIM_DP83865_DPX_FULL 0x0002
+
+
+/* NatSemi DP83865 */
+static int dp83865_config(struct phy_device *phydev)
+{
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
+ genphy_config_aneg(phydev);
+
+ return 0;
+}
+
+static int dp83865_parse_status(struct phy_device *phydev)
+{
+ int mii_reg;
+
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_DP83865_LANR);
+
+ switch (mii_reg & MIIM_DP83865_SPD_MASK) {
+
+ case MIIM_DP83865_SPD_1000:
+ phydev->speed = SPEED_1000;
+ break;
+
+ case MIIM_DP83865_SPD_100:
+ phydev->speed = SPEED_100;
+ break;
+
+ default:
+ phydev->speed = SPEED_10;
+ break;
+
+ }
+
+ if (mii_reg & MIIM_DP83865_DPX_FULL)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ return 0;
+}
+
+static int dp83865_startup(struct phy_device *phydev)
+{
+ genphy_update_link(phydev);
+ dp83865_parse_status(phydev);
+
+ return 0;
+}
+
+
+static struct phy_driver DP83865_driver = {
+ .name = "NatSemi DP83865",
+ .uid = 0x20005c70,
+ .mask = 0xfffffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &dp83865_config,
+ .startup = &dp83865_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int phy_natsemi_init(void)
+{
+ phy_register(&DP83865_driver);
+
+ return 0;
+}
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
new file mode 100644
index 0000000..c7edcc0
--- /dev/null
+++ b/drivers/net/phy/phy.c
@@ -0,0 +1,755 @@
+/*
+ * 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
+ *
+ *
+ * Copyright 2011 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ *
+ * Based loosely off of Linux's PHY Lib
+ */
+
+#include <config.h>
+#include <common.h>
+#include <malloc.h>
+#include <net.h>
+#include <command.h>
+#include <miiphy.h>
+#include <phy.h>
+#include <errno.h>
+
+/* Generic PHY support and helper functions */
+
+/**
+ * genphy_config_advert - sanitize and advertise auto-negotation parameters
+ * @phydev: target phy_device struct
+ *
+ * Description: Writes MII_ADVERTISE with the appropriate values,
+ * after sanitizing the values to make sure we only advertise
+ * 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)
+{
+ u32 advertise;
+ int oldadv, adv;
+ int err, changed = 0;
+
+ /* 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);
+
+ if (adv < 0)
+ return adv;
+
+ adv &= ~(ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_PAUSE_CAP |
+ ADVERTISE_PAUSE_ASYM);
+ if (advertise & ADVERTISED_10baseT_Half)
+ adv |= ADVERTISE_10HALF;
+ if (advertise & ADVERTISED_10baseT_Full)
+ adv |= ADVERTISE_10FULL;
+ if (advertise & ADVERTISED_100baseT_Half)
+ adv |= ADVERTISE_100HALF;
+ if (advertise & ADVERTISED_100baseT_Full)
+ adv |= ADVERTISE_100FULL;
+ if (advertise & ADVERTISED_Pause)
+ adv |= ADVERTISE_PAUSE_CAP;
+ if (advertise & ADVERTISED_Asym_Pause)
+ adv |= ADVERTISE_PAUSE_ASYM;
+
+ if (adv != oldadv) {
+ err = phy_write(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE, adv);
+
+ if (err < 0)
+ return err;
+ changed = 1;
+ }
+
+ /* 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);
+
+ if (adv < 0)
+ return adv;
+
+ adv &= ~(ADVERTISE_1000FULL | ADVERTISE_1000HALF);
+ 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 (err < 0)
+ return err;
+ changed = 1;
+ }
+ }
+
+ return changed;
+}
+
+
+/**
+ * genphy_setup_forced - configures/forces speed/duplex from @phydev
+ * @phydev: target phy_device struct
+ *
+ * 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)
+{
+ int err;
+ int ctl = 0;
+
+ phydev->pause = phydev->asym_pause = 0;
+
+ if (SPEED_1000 == phydev->speed)
+ ctl |= BMCR_SPEED1000;
+ else if (SPEED_100 == phydev->speed)
+ ctl |= BMCR_SPEED100;
+
+ if (DUPLEX_FULL == phydev->duplex)
+ 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
+ */
+int genphy_restart_aneg(struct phy_device *phydev)
+{
+ int ctl;
+
+ ctl = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
+
+ if (ctl < 0)
+ return ctl;
+
+ ctl |= (BMCR_ANENABLE | BMCR_ANRESTART);
+
+ /* Don't isolate the PHY if we're negotiating */
+ ctl &= ~(BMCR_ISOLATE);
+
+ ctl = phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, ctl);
+
+ return ctl;
+}
+
+
+/**
+ * genphy_config_aneg - restart auto-negotiation or write BMCR
+ * @phydev: target phy_device struct
+ *
+ * Description: If auto-negotiation is enabled, we configure the
+ * advertising, and then restart auto-negotiation. If it is not
+ * enabled, then we write the BMCR.
+ */
+int genphy_config_aneg(struct phy_device *phydev)
+{
+ int result;
+
+ if (AUTONEG_ENABLE != phydev->autoneg)
+ return genphy_setup_forced(phydev);
+
+ result = genphy_config_advert(phydev);
+
+ if (result < 0) /* error */
+ return result;
+
+ if (result == 0) {
+ /* 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)
+ return ctl;
+
+ if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE))
+ result = 1; /* do restart aneg */
+ }
+
+ /* Only restart aneg if we are advertising something different
+ * than we were before. */
+ if (result > 0)
+ result = genphy_restart_aneg(phydev);
+
+ return result;
+}
+
+/**
+ * genphy_update_link - update link status in @phydev
+ * @phydev: target phy_device struct
+ *
+ * Description: Update the value in phydev->link to reflect the
+ * current link value. In order to do this, we need to read
+ * the status register twice, keeping the second value.
+ */
+int genphy_update_link(struct phy_device *phydev)
+{
+ unsigned int mii_reg;
+
+ /*
+ * 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 we already saw the link up, and it hasn't gone down, then
+ * we don't need to wait for autoneg again
+ */
+ if (phydev->link && mii_reg & BMSR_LSTATUS)
+ return 0;
+
+ if ((mii_reg & BMSR_ANEGCAPABLE) && !(mii_reg & BMSR_ANEGCOMPLETE)) {
+ int i = 0;
+
+ printf("%s Waiting for PHY auto negotiation to complete",
+ phydev->dev->name);
+ while (!(mii_reg & BMSR_ANEGCOMPLETE)) {
+ /*
+ * Timeout reached ?
+ */
+ if (i > PHY_ANEG_TIMEOUT) {
+ printf(" TIMEOUT !\n");
+ phydev->link = 0;
+ return 0;
+ }
+
+ if (ctrlc()) {
+ puts("user interrupt!\n");
+ phydev->link = 0;
+ return -EINTR;
+ }
+
+ if ((i++ % 500) == 0)
+ printf(".");
+
+ udelay(1000); /* 1 ms */
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
+ }
+ 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 & BMSR_LSTATUS)
+ phydev->link = 1;
+ else
+ phydev->link = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Generic function which updates the speed and duplex. If
+ * autonegotiation is enabled, it uses the AND of the link
+ * partner's advertised capabilities and our advertised
+ * capabilities. If autonegotiation is disabled, we use the
+ * appropriate bits in the control register.
+ *
+ * Stolen from Linux's mii.c and phy_device.c
+ */
+static 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;
+
+ /* 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;
+ }
+
+ /* Set the baseline so we only have to set them
+ * if they're different
+ */
+ phydev->speed = SPEED_10;
+ phydev->duplex = DUPLEX_HALF;
+
+ /* Check the gigabit fields */
+ if (gblpa & (PHY_1000BTSR_1000FD | PHY_1000BTSR_1000HD)) {
+ phydev->speed = SPEED_1000;
+
+ if (gblpa & PHY_1000BTSR_1000FD)
+ phydev->duplex = DUPLEX_FULL;
+
+ /* We're done! */
+ return 0;
+ }
+
+ lpa = phy_read(phydev, MDIO_DEVAD_NONE, MII_ADVERTISE);
+ lpa &= phy_read(phydev, MDIO_DEVAD_NONE, MII_LPA);
+
+ if (lpa & (LPA_100FULL | LPA_100HALF)) {
+ phydev->speed = SPEED_100;
+
+ if (lpa & LPA_100FULL)
+ phydev->duplex = DUPLEX_FULL;
+
+ } else if (lpa & LPA_10FULL)
+ phydev->duplex = DUPLEX_FULL;
+ } else {
+ u32 bmcr = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMCR);
+
+ phydev->speed = SPEED_10;
+ phydev->duplex = DUPLEX_HALF;
+
+ if (bmcr & BMCR_FULLDPLX)
+ phydev->duplex = DUPLEX_FULL;
+
+ if (bmcr & BMCR_SPEED1000)
+ phydev->speed = SPEED_1000;
+ else if (bmcr & BMCR_SPEED100)
+ phydev->speed = SPEED_100;
+ }
+
+ return 0;
+}
+
+int genphy_config(struct phy_device *phydev)
+{
+ 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);
+
+ /* Do we support autonegotiation? */
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MII_BMSR);
+
+ if (val < 0)
+ return val;
+
+ if (val & BMSR_ANEGCAPABLE)
+ features |= SUPPORTED_Autoneg;
+
+ if (val & BMSR_100FULL)
+ features |= SUPPORTED_100baseT_Full;
+ if (val & BMSR_100HALF)
+ features |= SUPPORTED_100baseT_Half;
+ if (val & BMSR_10FULL)
+ features |= SUPPORTED_10baseT_Full;
+ if (val & BMSR_10HALF)
+ features |= SUPPORTED_10baseT_Half;
+
+ if (val & BMSR_ESTATEN) {
+ val = phy_read(phydev, MDIO_DEVAD_NONE, MII_ESTATUS);
+
+ if (val < 0)
+ return val;
+
+ if (val & ESTATUS_1000_TFULL)
+ features |= SUPPORTED_1000baseT_Full;
+ if (val & ESTATUS_1000_THALF)
+ features |= SUPPORTED_1000baseT_Half;
+ }
+
+ phydev->supported = features;
+ phydev->advertising = features;
+
+ genphy_config_aneg(phydev);
+
+ return 0;
+}
+
+int genphy_startup(struct phy_device *phydev)
+{
+ genphy_update_link(phydev);
+ genphy_parse_link(phydev);
+
+ return 0;
+}
+
+int genphy_shutdown(struct phy_device *phydev)
+{
+ return 0;
+}
+
+static struct phy_driver genphy_driver = {
+ .uid = 0xffffffff,
+ .mask = 0xffffffff,
+ .name = "Generic PHY",
+ .features = 0,
+ .config = genphy_config,
+ .startup = genphy_startup,
+ .shutdown = genphy_shutdown,
+};
+
+static LIST_HEAD(phy_drivers);
+
+int phy_init(void)
+{
+#ifdef CONFIG_PHY_ATHEROS
+ phy_atheros_init();
+#endif
+#ifdef CONFIG_PHY_BROADCOM
+ phy_broadcom_init();
+#endif
+#ifdef CONFIG_PHY_DAVICOM
+ phy_davicom_init();
+#endif
+#ifdef CONFIG_PHY_LXT
+ phy_lxt_init();
+#endif
+#ifdef CONFIG_PHY_MARVELL
+ phy_marvell_init();
+#endif
+#ifdef CONFIG_PHY_MICREL
+ phy_micrel_init();
+#endif
+#ifdef CONFIG_PHY_NATSEMI
+ phy_natsemi_init();
+#endif
+#ifdef CONFIG_PHY_REALTEK
+ phy_realtek_init();
+#endif
+#ifdef CONFIG_PHY_TERANETICS
+ phy_teranetics_init();
+#endif
+#ifdef CONFIG_PHY_VITESSE
+ phy_vitesse_init();
+#endif
+
+ return 0;
+}
+
+int phy_register(struct phy_driver *drv)
+{
+ INIT_LIST_HEAD(&drv->list);
+ list_add_tail(&drv->list, &phy_drivers);
+
+ return 0;
+}
+
+int phy_probe(struct phy_device *phydev)
+{
+ int err = 0;
+
+ phydev->advertising = phydev->supported = phydev->drv->features;
+ phydev->mmds = phydev->drv->mmds;
+
+ if (phydev->drv->probe)
+ err = phydev->drv->probe(phydev);
+
+ return err;
+}
+
+static struct phy_driver *generic_for_interface(phy_interface_t interface)
+{
+#ifdef CONFIG_PHYLIB_10G
+ if (is_10g_interface(interface))
+ return &gen10g_driver;
+#endif
+
+ return &genphy_driver;
+}
+
+struct phy_driver *get_phy_driver(struct phy_device *phydev,
+ phy_interface_t interface)
+{
+ struct list_head *entry;
+ int phy_id = phydev->phy_id;
+ struct phy_driver *drv = NULL;
+
+ list_for_each(entry, &phy_drivers) {
+ drv = list_entry(entry, struct phy_driver, list);
+ if ((drv->uid & drv->mask) == (phy_id & drv->mask))
+ return drv;
+ }
+
+ /* If we made it here, there's no driver for this PHY */
+ return generic_for_interface(interface);
+}
+
+struct phy_device *phy_device_create(struct mii_dev *bus, int addr, int phy_id,
+ phy_interface_t interface)
+{
+ struct phy_device *dev;
+
+ /* 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);
+ return NULL;
+ }
+
+ memset(dev, 0, sizeof(*dev));
+
+ dev->duplex = -1;
+ 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);
+
+ phy_probe(dev);
+
+ bus->phymap[addr] = dev;
+
+ return dev;
+}
+
+/**
+ * get_phy_id - reads the specified addr for its ID.
+ * @bus: the target MII bus
+ * @addr: PHY address on the MII bus
+ * @phy_id: where to store the ID retrieved.
+ *
+ * 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)
+{
+ int phy_reg;
+
+ /* 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)
+ return -EIO;
+
+ *phy_id = (phy_reg & 0xffff) << 16;
+
+ /* Grab the bits from PHYIR2, and put them in the lower half */
+ phy_reg = bus->read(bus, addr, devad, MII_PHYSID2);
+
+ if (phy_reg < 0)
+ return -EIO;
+
+ *phy_id |= (phy_reg & 0xffff);
+
+ 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)
+{
+ u32 phy_id = 0x1fffffff;
+ int i;
+ int r;
+
+ /* If we have one, return the existing device, with new interface */
+ if (bus->phymap[addr]) {
+ bus->phymap[addr]->interface = interface;
+
+ return bus->phymap[addr];
+ }
+
+ /* 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);
+
+ /* Otherwise we have to try Clause 45 */
+ for (i = 1; i < 5; i++) {
+ r = get_phy_id(bus, addr, i, &phy_id);
+ if (r)
+ return NULL;
+
+ /* If the phy_id is mostly Fs, there is no device there */
+ if ((phy_id & 0x1fffffff) != 0x1fffffff)
+ break;
+ }
+
+ return phy_device_create(bus, addr, phy_id, interface);
+}
+
+int phy_reset(struct phy_device *phydev)
+{
+ int reg;
+ int timeout = 500;
+ int devad = MDIO_DEVAD_NONE;
+
+#ifdef CONFIG_PHYLIB_10G
+ /* If it's 10G, we need to issue reset through one of the MMDs */
+ if (is_10g_interface(phydev->interface)) {
+ if (!phydev->mmds)
+ gen10g_discover_mmds(phydev);
+
+ devad = ffs(phydev->mmds) - 1;
+ }
+#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) {
+ debug("PHY reset failed\n");
+ return -1;
+ }
+
+#ifdef CONFIG_PHY_RESET_DELAY
+ udelay(CONFIG_PHY_RESET_DELAY); /* Intel LXT971A needs this */
+#endif
+ /*
+ * Poll the control register for the reset bit to go to 0 (it is
+ * auto-clearing). This should happen within 0.5 seconds per the
+ * IEEE spec.
+ */
+ while ((reg & BMCR_RESET) && timeout--) {
+ reg = phy_read(phydev, devad, MII_BMCR);
+
+ if (reg < 0) {
+ debug("PHY status read failed\n");
+ return -1;
+ }
+ udelay(1000);
+ }
+
+ if (reg & BMCR_RESET) {
+ puts("PHY reset timed out\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+int miiphy_reset(const char *devname, unsigned char addr)
+{
+ struct mii_dev *bus = miiphy_get_dev_by_name(devname);
+ struct phy_device *phydev;
+
+ /*
+ * miiphy_reset was only used on standard PHYs, so we'll fake it here.
+ * If later code tries to connect with the right interface, this will
+ * be corrected by get_phy_device in phy_connect()
+ */
+ phydev = get_phy_device(bus, addr, PHY_INTERFACE_MODE_MII);
+
+ 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 *phydev;
+
+ /* Reset the bus */
+ bus->reset(bus);
+
+ /* Wait 15ms to make sure the PHY has come out of hard reset */
+ udelay(15000);
+
+ phydev = get_phy_device(bus, addr, interface);
+
+ if (!phydev) {
+ printf("Could not get PHY for %s:%d\n", bus->name, addr);
+
+ return NULL;
+ }
+
+ /* Soft Reset the PHY */
+ phy_reset(phydev);
+
+ if (phydev->dev)
+ printf("%s:%d is connected to %s. Reconnecting to %s\n",
+ bus->name, addr, phydev->dev->name, dev->name);
+
+ phydev->dev = dev;
+
+ printf("%s connected to %s\n", dev->name, phydev->drv->name);
+
+ return phydev;
+}
+
+int phy_startup(struct phy_device *phydev)
+{
+ if (phydev->drv->startup)
+ phydev->drv->startup(phydev);
+
+ return 0;
+}
+
+static int __board_phy_config(struct phy_device *phydev)
+{
+ return 0;
+}
+
+int board_phy_config(struct phy_device *phydev)
+ __attribute__((weak, alias("__board_phy_config")));
+
+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);
+
+ return 0;
+}
+
+int phy_shutdown(struct phy_device *phydev)
+{
+ if (phydev->drv->shutdown)
+ phydev->drv->shutdown(phydev);
+
+ return 0;
+}
diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c
new file mode 100644
index 0000000..b7e2753
--- /dev/null
+++ b/drivers/net/phy/realtek.c
@@ -0,0 +1,130 @@
+/*
+ * RealTek PHY drivers
+ *
+ * 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
+ *
+ * Copyright 2010-2011 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ *
+ */
+#include <config.h>
+#include <common.h>
+#include <phy.h>
+
+#define PHY_AUTONEGOTIATE_TIMEOUT 5000
+
+/* RTL8211B PHY Status Register */
+#define MIIM_RTL8211B_PHY_STATUS 0x11
+#define MIIM_RTL8211B_PHYSTAT_SPEED 0xc000
+#define MIIM_RTL8211B_PHYSTAT_GBIT 0x8000
+#define MIIM_RTL8211B_PHYSTAT_100 0x4000
+#define MIIM_RTL8211B_PHYSTAT_DUPLEX 0x2000
+#define MIIM_RTL8211B_PHYSTAT_SPDDONE 0x0800
+#define MIIM_RTL8211B_PHYSTAT_LINK 0x0400
+
+
+/* RealTek RTL8211B */
+static int rtl8211b_config(struct phy_device *phydev)
+{
+ phy_write(phydev, MDIO_DEVAD_NONE, MII_BMCR, BMCR_RESET);
+
+ genphy_config_aneg(phydev);
+
+ return 0;
+}
+
+static int rtl8211b_parse_status(struct phy_device *phydev)
+{
+ unsigned int speed;
+ unsigned int mii_reg;
+
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_RTL8211B_PHY_STATUS);
+
+ if (!(mii_reg & MIIM_RTL8211B_PHYSTAT_SPDDONE)) {
+ int i = 0;
+
+ /* in case of timeout ->link is cleared */
+ phydev->link = 1;
+ puts("Waiting for PHY realtime link");
+ while (!(mii_reg & MIIM_RTL8211B_PHYSTAT_SPDDONE)) {
+ /* Timeout reached ? */
+ if (i > PHY_AUTONEGOTIATE_TIMEOUT) {
+ puts(" TIMEOUT !\n");
+ phydev->link = 0;
+ break;
+ }
+
+ if ((i++ % 1000) == 0)
+ putc('.');
+ udelay(1000); /* 1 ms */
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE,
+ MIIM_RTL8211B_PHY_STATUS);
+ }
+ puts(" done\n");
+ udelay(500000); /* another 500 ms (results in faster booting) */
+ } else {
+ if (mii_reg & MIIM_RTL8211B_PHYSTAT_LINK)
+ phydev->link = 1;
+ else
+ phydev->link = 0;
+ }
+
+ if (mii_reg & MIIM_RTL8211B_PHYSTAT_DUPLEX)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ speed = (mii_reg & MIIM_RTL8211B_PHYSTAT_SPEED);
+
+ switch (speed) {
+ case MIIM_RTL8211B_PHYSTAT_GBIT:
+ phydev->speed = SPEED_1000;
+ break;
+ case MIIM_RTL8211B_PHYSTAT_100:
+ phydev->speed = SPEED_100;
+ break;
+ default:
+ phydev->speed = SPEED_10;
+ }
+
+ return 0;
+}
+
+static int rtl8211b_startup(struct phy_device *phydev)
+{
+ /* Read the Status (2x to make sure link is right) */
+ genphy_update_link(phydev);
+ rtl8211b_parse_status(phydev);
+
+ return 0;
+}
+
+static struct phy_driver RTL8211B_driver = {
+ .name = "RealTek RTL8211B",
+ .uid = 0x1cc910,
+ .mask = 0xfffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &rtl8211b_config,
+ .startup = &rtl8211b_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int phy_realtek_init(void)
+{
+ phy_register(&RTL8211B_driver);
+
+ return 0;
+}
diff --git a/drivers/net/phy/teranetics.c b/drivers/net/phy/teranetics.c
new file mode 100644
index 0000000..a771791
--- /dev/null
+++ b/drivers/net/phy/teranetics.c
@@ -0,0 +1,62 @@
+/*
+ * Teranetics PHY drivers
+ *
+ * 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
+ *
+ * Copyright 2010-2011 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ *
+ */
+#include <config.h>
+#include <phy.h>
+
+#ifndef CONFIG_PHYLIB_10G
+#error The Teranetics PHY needs 10G support
+#endif
+
+int tn2020_config(struct phy_device *phydev)
+{
+ if (phydev->port == PORT_FIBRE) {
+ unsigned short restart_an = (MDIO_AN_CTRL1_RESTART |
+ MDIO_AN_CTRL1_ENABLE |
+ MDIO_AN_CTRL1_XNP);
+
+ phy_write(phydev, 30, 93, 2);
+ phy_write(phydev, MDIO_MMD_AN, MDIO_CTRL1, restart_an);
+ }
+
+ return 0;
+}
+
+struct phy_driver tn2020_driver = {
+ .name = "Teranetics TN2020",
+ .uid = 0x00a19410,
+ .mask = 0xfffffff0,
+ .features = PHY_10G_FEATURES,
+ .mmds = (MDIO_DEVS_PMAPMD | MDIO_DEVS_PCS |
+ MDIO_DEVS_PHYXS | MDIO_DEVS_AN |
+ MDIO_DEVS_VEND1 | MDIO_DEVS_VEND2),
+ .config = &tn2020_config,
+ .startup = &gen10g_startup,
+ .shutdown = &gen10g_shutdown,
+};
+
+int phy_teranetics_init(void)
+{
+ phy_register(&tn2020_driver);
+
+ return 0;
+}
diff --git a/drivers/net/phy/vitesse.c b/drivers/net/phy/vitesse.c
new file mode 100644
index 0000000..d48d4fe
--- /dev/null
+++ b/drivers/net/phy/vitesse.c
@@ -0,0 +1,242 @@
+/*
+ * Vitesse PHY drivers
+ *
+ * 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
+ *
+ * Copyright 2010-2011 Freescale Semiconductor, Inc.
+ * author Andy Fleming
+ *
+ */
+#include <miiphy.h>
+
+/* Cicada Auxiliary Control/Status Register */
+#define MIIM_CIS82xx_AUX_CONSTAT 0x1c
+#define MIIM_CIS82xx_AUXCONSTAT_INIT 0x0004
+#define MIIM_CIS82xx_AUXCONSTAT_DUPLEX 0x0020
+#define MIIM_CIS82xx_AUXCONSTAT_SPEED 0x0018
+#define MIIM_CIS82xx_AUXCONSTAT_GBIT 0x0010
+#define MIIM_CIS82xx_AUXCONSTAT_100 0x0008
+
+/* Cicada Extended Control Register 1 */
+#define MIIM_CIS82xx_EXT_CON1 0x17
+#define MIIM_CIS8201_EXTCON1_INIT 0x0000
+
+/* Cicada 8204 Extended PHY Control Register 1 */
+#define MIIM_CIS8204_EPHY_CON 0x17
+#define MIIM_CIS8204_EPHYCON_INIT 0x0006
+#define MIIM_CIS8204_EPHYCON_RGMII 0x1100
+
+/* Cicada 8204 Serial LED Control Register */
+#define MIIM_CIS8204_SLED_CON 0x1b
+#define MIIM_CIS8204_SLEDCON_INIT 0x1115
+
+/* Vitesse VSC8601 Extended PHY Control Register 1 */
+#define MIIM_VSC8601_EPHY_CON 0x17
+#define MIIM_VSC8601_EPHY_CON_INIT_SKEW 0x1120
+#define MIIM_VSC8601_SKEW_CTRL 0x1c
+
+#define PHY_EXT_PAGE_ACCESS 0x1f
+
+/* CIS8201 */
+static int vitesse_config(struct phy_device *phydev)
+{
+ /* Override PHY config settings */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_CIS82xx_AUX_CONSTAT,
+ MIIM_CIS82xx_AUXCONSTAT_INIT);
+ /* Set up the interface mode */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_CIS82xx_EXT_CON1,
+ MIIM_CIS8201_EXTCON1_INIT);
+
+ genphy_config_aneg(phydev);
+
+ return 0;
+}
+
+static int vitesse_parse_status(struct phy_device *phydev)
+{
+ int speed;
+ int mii_reg;
+
+ mii_reg = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_CIS82xx_AUX_CONSTAT);
+
+ if (mii_reg & MIIM_CIS82xx_AUXCONSTAT_DUPLEX)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ speed = mii_reg & MIIM_CIS82xx_AUXCONSTAT_SPEED;
+ switch (speed) {
+ case MIIM_CIS82xx_AUXCONSTAT_GBIT:
+ phydev->speed = SPEED_1000;
+ break;
+ case MIIM_CIS82xx_AUXCONSTAT_100:
+ phydev->speed = SPEED_100;
+ break;
+ default:
+ phydev->speed = SPEED_10;
+ break;
+ }
+
+ return 0;
+}
+
+static int vitesse_startup(struct phy_device *phydev)
+{
+ genphy_update_link(phydev);
+ vitesse_parse_status(phydev);
+
+ return 0;
+}
+
+static int cis8204_config(struct phy_device *phydev)
+{
+ /* Override PHY config settings */
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_CIS82xx_AUX_CONSTAT,
+ MIIM_CIS82xx_AUXCONSTAT_INIT);
+
+ genphy_config_aneg(phydev);
+
+ if ((phydev->interface == PHY_INTERFACE_MODE_RGMII) ||
+ (phydev->interface == PHY_INTERFACE_MODE_RGMII) ||
+ (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID) ||
+ (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID))
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_CIS8204_EPHY_CON,
+ MIIM_CIS8204_EPHYCON_INIT |
+ MIIM_CIS8204_EPHYCON_RGMII);
+ else
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_CIS8204_EPHY_CON,
+ MIIM_CIS8204_EPHYCON_INIT);
+
+ return 0;
+}
+
+/* Vitesse VSC8601 */
+int vsc8601_config(struct phy_device *phydev)
+{
+ /* Configure some basic stuff */
+#ifdef CONFIG_SYS_VSC8601_SKEWFIX
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8601_EPHY_CON,
+ MIIM_VSC8601_EPHY_CON_INIT_SKEW);
+#if defined(CONFIG_SYS_VSC8601_SKEW_TX) && defined(CONFIG_SYS_VSC8601_SKEW_RX)
+ phy_write(phydev, MDIO_DEVAD_NONE, PHY_EXT_PAGE_ACCESS, 1);
+#define VSC8101_SKEW \
+ ((CONFIG_SYS_VSC8601_SKEW_TX << 14) \
+ | (CONFIG_SYS_VSC8601_SKEW_RX << 12))
+ phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8601_SKEW_CTRL,
+ VSC8101_SKEW);
+ phy_write(phydev, MDIO_DEVAD_NONE, PHY_EXT_PAGE_ACCESS, 0);
+#endif
+#endif
+
+ genphy_config_aneg(phydev);
+
+ return 0;
+}
+
+static struct phy_driver VSC8211_driver = {
+ .name = "Vitesse VSC8211",
+ .uid = 0xfc4b0,
+ .mask = 0xffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &vitesse_config,
+ .startup = &vitesse_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver VSC8221_driver = {
+ .name = "Vitesse VSC8221",
+ .uid = 0xfc550,
+ .mask = 0xffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &vitesse_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver VSC8244_driver = {
+ .name = "Vitesse VSC8244",
+ .uid = 0xfc6c0,
+ .mask = 0xffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &vitesse_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver VSC8234_driver = {
+ .name = "Vitesse VSC8234",
+ .uid = 0xfc620,
+ .mask = 0xffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &vitesse_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver VSC8601_driver = {
+ .name = "Vitesse VSC8601",
+ .uid = 0x70420,
+ .mask = 0xffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &vsc8601_config,
+ .startup = &vitesse_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver VSC8641_driver = {
+ .name = "Vitesse VSC8641",
+ .uid = 0x70430,
+ .mask = 0xffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &genphy_config_aneg,
+ .startup = &vitesse_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+/* Vitesse bought Cicada, so we'll put these here */
+static struct phy_driver cis8201_driver = {
+ .name = "CIS8201",
+ .uid = 0xfc410,
+ .mask = 0xffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &vitesse_config,
+ .startup = &vitesse_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+static struct phy_driver cis8204_driver = {
+ .name = "Cicada Cis8204",
+ .uid = 0xfc440,
+ .mask = 0xffff0,
+ .features = PHY_GBIT_FEATURES,
+ .config = &cis8204_config,
+ .startup = &vitesse_startup,
+ .shutdown = &genphy_shutdown,
+};
+
+int phy_vitesse_init(void)
+{
+ phy_register(&VSC8641_driver);
+ phy_register(&VSC8601_driver);
+ phy_register(&VSC8234_driver);
+ phy_register(&VSC8244_driver);
+ phy_register(&VSC8211_driver);
+ phy_register(&VSC8221_driver);
+ phy_register(&cis8201_driver);
+ phy_register(&cis8204_driver);
+
+ return 0;
+}