summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSebastian Hesselbarth <sebastian.hesselbarth@gmail.com>2014-06-24 12:18:11 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2014-06-25 08:31:27 +0200
commitf38c708c2d6284f96260aacd0c5b38e6c5529010 (patch)
treedea46f36c6d4d207faaba409fe2506003ca591c7
parent47fc75bc77b0f7e467f53d627075daac7b2a9df2 (diff)
downloadbarebox-f38c708c2d6284f96260aacd0c5b38e6c5529010.tar.gz
barebox-f38c708c2d6284f96260aacd0c5b38e6c5529010.tar.xz
net: phy: add support for Marvell PHY drivers
This adds initial support for Marvell PHY specific drivers. As a first PHY, a driver for MV88E1121R is ported over from Linux. Signed-off-by: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
-rw-r--r--drivers/net/phy/Kconfig5
-rw-r--r--drivers/net/phy/Makefile1
-rw-r--r--drivers/net/phy/marvell.c199
3 files changed, 205 insertions, 0 deletions
diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig
index 02e1e83c0b..d0a02c1e40 100644
--- a/drivers/net/phy/Kconfig
+++ b/drivers/net/phy/Kconfig
@@ -18,6 +18,11 @@ config LXT_PHY
---help---
Currently supports the lxt971 PHY.
+config MARVELL_PHY
+ tristate "Drivers for Marvell PHYs"
+ ---help---
+ Add support for various Marvell PHYs (e.g. 88E1121R).
+
config MICREL_PHY
bool "Driver for Micrel PHYs"
---help---
diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile
index 7f8277f1d5..94b9be83ea 100644
--- a/drivers/net/phy/Makefile
+++ b/drivers/net/phy/Makefile
@@ -1,6 +1,7 @@
obj-y += phy.o mdio_bus.o
obj-$(CONFIG_AT803X_PHY) += at803x.o
obj-$(CONFIG_LXT_PHY) += lxt.o
+obj-$(CONFIG_MARVELL_PHY) += marvell.o
obj-$(CONFIG_MICREL_PHY) += micrel.o
obj-$(CONFIG_NATIONAL_PHY) += national.o
obj-$(CONFIG_SMSC_PHY) += smsc.o
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
new file mode 100644
index 0000000000..34f852e065
--- /dev/null
+++ b/drivers/net/phy/marvell.c
@@ -0,0 +1,199 @@
+/*
+ * drivers/net/phy/marvell.c
+ *
+ * Driver for Marvell PHYs based on Linux driver
+ */
+
+#include <common.h>
+#include <init.h>
+#include <linux/mii.h>
+#include <linux/ethtool.h>
+#include <linux/phy.h>
+#include <linux/smscphy.h>
+
+/* Known PHY IDs */
+#define MARVELL_PHY_ID_88E1101 0x01410c60
+#define MARVELL_PHY_ID_88E1112 0x01410c90
+#define MARVELL_PHY_ID_88E1111 0x01410cc0
+#define MARVELL_PHY_ID_88E1118 0x01410e10
+#define MARVELL_PHY_ID_88E1121R 0x01410cb0
+#define MARVELL_PHY_ID_88E1145 0x01410cd0
+#define MARVELL_PHY_ID_88E1149R 0x01410e50
+#define MARVELL_PHY_ID_88E1240 0x01410e30
+#define MARVELL_PHY_ID_88E1318S 0x01410e90
+#define MARVELL_PHY_ID_88E1116R 0x01410e40
+#define MARVELL_PHY_ID_88E1510 0x01410dd0
+
+/* Mask used for ID comparisons */
+#define MARVELL_PHY_ID_MASK 0xfffffff0
+
+/* Marvell Register Page register */
+#define MII_MARVELL_PHY_PAGE 22
+#define MII_MARVELL_PHY_DEFAULT_PAGE 0
+
+#define MII_M1011_PHY_SCR 0x10
+#define MII_M1011_PHY_SCR_AUTO_CROSS 0x0060
+
+#define MII_M1011_PHY_STATUS 0x11
+#define MII_M1011_PHY_STATUS_1000 BIT(15)
+#define MII_M1011_PHY_STATUS_100 BIT(14)
+#define MII_M1011_PHY_STATUS_SPD_MASK \
+ (MII_M1011_PHY_STATUS_1000 | MII_M1011_PHY_STATUS_100)
+#define MII_M1011_PHY_STATUS_FULLDUPLEX BIT(13)
+#define MII_M1011_PHY_STATUS_RESOLVED BIT(11)
+#define MII_M1011_PHY_STATUS_LINK BIT(10)
+
+#define MII_88E1121_PHY_MSCR_PAGE 2
+#define MII_88E1121_PHY_MSCR 0x15
+#define MII_88E1121_PHY_MSCR_TX_DELAY BIT(4)
+#define MII_88E1121_PHY_MSCR_RX_DELAY BIT(5)
+#define MII_88E1121_PHY_MSCR_DELAY_MASK \
+ (MII_88E1121_PHY_MSCR_RX_DELAY | MII_88E1121_PHY_MSCR_TX_DELAY)
+
+/*
+ * marvell_read_status
+ *
+ * Generic status code does not detect Fiber correctly!
+ * Description:
+ * Check the link, then figure out the current state
+ * by comparing what we advertise with what the link partner
+ * advertises. Start by checking the gigabit possibilities,
+ * then move on to 10/100.
+ */
+static int marvell_read_status(struct phy_device *phydev)
+{
+ int adv;
+ int err;
+ int lpa;
+ int status = 0;
+
+ /* Update the link, but return if there
+ * was an error */
+ err = genphy_update_link(phydev);
+ if (err)
+ return err;
+
+ if (AUTONEG_ENABLE == phydev->autoneg) {
+ status = phy_read(phydev, MII_M1011_PHY_STATUS);
+ if (status < 0)
+ return status;
+
+ lpa = phy_read(phydev, MII_LPA);
+ if (lpa < 0)
+ return lpa;
+
+ adv = phy_read(phydev, MII_ADVERTISE);
+ if (adv < 0)
+ return adv;
+
+ lpa &= adv;
+
+ if (status & MII_M1011_PHY_STATUS_FULLDUPLEX)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ status = status & MII_M1011_PHY_STATUS_SPD_MASK;
+ phydev->pause = phydev->asym_pause = 0;
+
+ switch (status) {
+ case MII_M1011_PHY_STATUS_1000:
+ phydev->speed = SPEED_1000;
+ break;
+
+ case MII_M1011_PHY_STATUS_100:
+ phydev->speed = SPEED_100;
+ break;
+
+ default:
+ phydev->speed = SPEED_10;
+ break;
+ }
+
+ if (phydev->duplex == DUPLEX_FULL) {
+ phydev->pause = lpa & LPA_PAUSE_CAP ? 1 : 0;
+ phydev->asym_pause = lpa & LPA_PAUSE_ASYM ? 1 : 0;
+ }
+ } else {
+ int bmcr = phy_read(phydev, MII_BMCR);
+
+ if (bmcr < 0)
+ return bmcr;
+
+ if (bmcr & BMCR_FULLDPLX)
+ phydev->duplex = DUPLEX_FULL;
+ else
+ phydev->duplex = DUPLEX_HALF;
+
+ if (bmcr & BMCR_SPEED1000)
+ phydev->speed = SPEED_1000;
+ else if (bmcr & BMCR_SPEED100)
+ phydev->speed = SPEED_100;
+ else
+ phydev->speed = SPEED_10;
+
+ phydev->pause = phydev->asym_pause = 0;
+ }
+
+ return 0;
+}
+
+static int m88e1121_config_init(struct phy_device *phydev)
+{
+ u16 reg;
+ int ret;
+
+ ret = phy_write(phydev, MII_MARVELL_PHY_PAGE,
+ MII_88E1121_PHY_MSCR_PAGE);
+ if (ret < 0)
+ return ret;
+
+ /* Setup RGMII TX/RX delay */
+ reg = phy_read(phydev, MII_88E1121_PHY_MSCR) &
+ ~MII_88E1121_PHY_MSCR_DELAY_MASK;
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+ phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID)
+ reg |= MII_88E1121_PHY_MSCR_RX_DELAY;
+ if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID ||
+ phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID)
+ reg |= MII_88E1121_PHY_MSCR_TX_DELAY;
+ ret = phy_write(phydev, MII_88E1121_PHY_MSCR, reg);
+ if (ret < 0)
+ return ret;
+
+ phy_write(phydev, MII_MARVELL_PHY_PAGE, MII_MARVELL_PHY_DEFAULT_PAGE);
+ if (ret < 0)
+ return ret;
+
+ /* Enable auto-crossover */
+ ret = phy_write(phydev, MII_M1011_PHY_SCR,
+ MII_M1011_PHY_SCR_AUTO_CROSS);
+ if (ret < 0)
+ return ret;
+
+ /* Reset PHY */
+ ret = phy_write(phydev, MII_BMCR,
+ phy_read(phydev, MII_BMCR) | BMCR_RESET);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static struct phy_driver marvell_phys[] = {
+{
+ .phy_id = MARVELL_PHY_ID_88E1121R,
+ .phy_id_mask = MARVELL_PHY_ID_MASK,
+ .drv.name = "Marvell 88E1121R",
+ .features = PHY_GBIT_FEATURES,
+ .config_init = m88e1121_config_init,
+ .config_aneg = genphy_config_aneg,
+ .read_status = marvell_read_status,
+},
+};
+
+static int __init marvell_phy_init(void)
+{
+ return phy_drivers_register(marvell_phys, ARRAY_SIZE(marvell_phys));
+}
+fs_initcall(marvell_phy_init);