From: Igal Liberman <ig...@marvell.com> E2110 support 10M/100M/1G/2.5G/5G speed and use C45 register definition. Need to use C45 or C22 r13/r14 indirect method to access
Signed-off-by: Kevin Shi <k...@marvell.com> Signed-off-by: Igal Liberman <ig...@marvell.com> Signed-off-by: Stefan Roese <s...@denx.de> --- drivers/net/phy/marvell.c | 205 ++++++++++++++++++++++++++++++++++++++ include/linux/ethtool.h | 3 +- 2 files changed, 207 insertions(+), 1 deletion(-) diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c index a62c695c5c84..c3f86d98f9e3 100644 --- a/drivers/net/phy/marvell.c +++ b/drivers/net/phy/marvell.c @@ -6,6 +6,7 @@ * author Andy Fleming */ #include <common.h> +#include <console.h> #include <errno.h> #include <phy.h> #include <linux/bitops.h> @@ -104,6 +105,19 @@ #define MIIM_88E151x_MODE_SGMII 1 #define MIIM_88E151x_RESET_OFFS 15 +/* 88E2110 PHY defines */ +#define MIIM_88E2110_PHY_STATUS 0x8008 +#define MIIM_88E2110_PHYSTAT_SPEED 0xc000 +#define MIIM_88E2110_PHYSTAT_10GBIT 0xc000 +#define MIIM_88E2110_PHYSTAT_GBIT 0x8000 +#define MIIM_88E2110_PHYSTAT_100 0x4000 +#define MIIM_88E2110_PHYSTAT_DUPLEX 0x2000 +#define MIIM_88E2110_PHYSTAT_SPDDONE 0x0800 +#define MIIM_88E2110_PHYSTAT_LINK 0x0400 +#define MIIM_88E2110_PHYSTAT_SPEED_5G 0x000c +#define MIIM_88E2110_PHYSTAT_5GBIT 0x0008 +#define MIIM_88E2110_PHYSTAT_2_5GBIT 0x0004 + static int m88e1xxx_phy_extread(struct phy_device *phydev, int addr, int devaddr, int regnum) { @@ -590,6 +604,185 @@ static int m88e1680_config(struct phy_device *phydev) return 0; } +/* Marvell 88E2110 */ +static int m88e2110_probe(struct phy_device *phydev) +{ + /* + * skip reset since phy has its own initial value. + * resettting leads to weird behavior + */ + phydev->flags |= PHY_FLAG_BROKEN_RESET; + + return 0; +} + +static int m88e2110_config(struct phy_device *phydev) +{ + u16 reg; + + /* Perform lane swap */ + reg = phy_read(phydev, 1, 0xc000); + reg |= 0x1; + phy_write(phydev, 1, 0xc000, reg); + + /* Configure auto-negotiation advertisement */ + if (phydev->interface == PHY_INTERFACE_MODE_SFI) { + /* Disabled 10G advertisement */ + phy_write(phydev, 7, 0x20, 0x1e1); + } else { + if (phydev->interface == PHY_INTERFACE_MODE_SGMII_2500) { + /* Disabled 10G/5G advertisements */ + phy_write(phydev, 7, 0x20, 0xa1); + } else { + /* Disable 10G/5G/2.5G auto-negotiation advertisement */ + phy_write(phydev, 7, 0x20, 0x1); + } + } + + /* Restart auto-negotiation */ + phy_write(phydev, 7, 0, 0x3200); + + return 0; +} + +/* Parse the 88E2110's status register for speed and duplex + * information + */ +static uint m88e2110_parse_status(struct phy_device *phydev) +{ + unsigned int speed; + unsigned int mii_reg; + + mii_reg = phy_read(phydev, 3, MIIM_88E2110_PHY_STATUS); + + if ((mii_reg & MIIM_88E2110_PHYSTAT_LINK) && + !(mii_reg & MIIM_88E2110_PHYSTAT_SPDDONE)) { + int i = 0; + + puts("Waiting for PHY realtime link"); + while (!(mii_reg & MIIM_88E2110_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, 3, MIIM_88E2110_PHY_STATUS); + } + puts(" done\n"); + mdelay(500); /* another 500 ms (results in faster booting) */ + } else { + if (mii_reg & MIIM_88E2110_PHYSTAT_LINK) + phydev->link = 1; + else + phydev->link = 0; + } + + if (mii_reg & MIIM_88E2110_PHYSTAT_DUPLEX) + phydev->duplex = DUPLEX_FULL; + else + phydev->duplex = DUPLEX_HALF; + + speed = mii_reg & MIIM_88E2110_PHYSTAT_SPEED; + + switch (speed) { + case MIIM_88E2110_PHYSTAT_10GBIT: + switch (mii_reg & MIIM_88E2110_PHYSTAT_SPEED_5G) { + case MIIM_88E2110_PHYSTAT_5GBIT: + phydev->speed = SPEED_5000; + break; + case MIIM_88E2110_PHYSTAT_2_5GBIT: + phydev->speed = SPEED_2500; + break; + default: + puts(" Unknown speed detected\n"); + break; + } + case MIIM_88E2110_PHYSTAT_GBIT: + phydev->speed = SPEED_1000; + break; + case MIIM_88E2110_PHYSTAT_100: + phydev->speed = SPEED_100; + break; + default: + phydev->speed = SPEED_10; + break; + } + + return 0; +} + +static int m88e2110_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, 7, 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; + + debug("%s Waiting for PHY auto negotiation to complete", + phydev->drv->name); + while (!(mii_reg & BMSR_ANEGCOMPLETE)) { + /* + * Timeout reached ? + */ + if (i > PHY_ANEG_TIMEOUT) { + debug(" TIMEOUT !\n"); + phydev->link = 0; + return 0; + } + + if (ctrlc()) { + puts("user interrupt!\n"); + phydev->link = 0; + return -EINTR; + } + + if ((i++ % 500) == 0) + debug("."); + + udelay(1000); /* 1 ms */ + mii_reg = phy_read(phydev, 7, MII_BMSR); + } + debug(" done\n"); + phydev->link = 1; + } else { + /* Read the link a second time to clear the latched state */ + mii_reg = phy_read(phydev, 7, MII_BMSR); + + if (mii_reg & BMSR_LSTATUS) + phydev->link = 1; + else + phydev->link = 0; + } + + return 0; +} + +static int m88e2110_startup(struct phy_device *phydev) +{ + m88e2110_update_link(phydev); + m88e2110_parse_status(phydev); + + return 0; +} + static struct phy_driver M88E1011S_driver = { .name = "Marvell 88E1011S", .uid = 0x1410c60, @@ -692,6 +885,17 @@ static struct phy_driver M88E1680_driver = { .shutdown = &genphy_shutdown, }; +static struct phy_driver M88E2110_driver = { + .name = "Marvell 88E2110", + .uid = 0x2b09b8, + .mask = 0xffffff0, + .features = PHY_GBIT_FEATURES, + .probe = &m88e2110_probe, + .config = &m88e2110_config, + .startup = &m88e2110_startup, + .shutdown = &genphy_shutdown, +}; + int phy_marvell_init(void) { phy_register(&M88E1310_driver); @@ -704,6 +908,7 @@ int phy_marvell_init(void) phy_register(&M88E1011S_driver); phy_register(&M88E151x_driver); phy_register(&M88E1680_driver); + phy_register(&M88E2110_driver); return 0; } diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index f6dbdb096d34..a97bde48df99 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -614,11 +614,12 @@ enum ethtool_sfeatures_retval_bits { * it was foced up into this mode or autonegotiated. */ -/* The forced speed, 10Mb, 100Mb, gigabit, 2.5Gb, 10GbE. */ +/* The forced speed, 10Mb, 100Mb, gigabit, 2.5Gb, 5Gb, 10GbE. */ #define SPEED_10 10 #define SPEED_100 100 #define SPEED_1000 1000 #define SPEED_2500 2500 +#define SPEED_5000 5000 #define SPEED_10000 10000 /* Duplex, half or full. */ -- 2.31.0