[PATCH v1 1/5] net: phy: marvell: add support for 88E2110 phy
Stefan Roese
sr at denx.de
Wed Mar 24 10:20:04 CET 2021
From: Igal Liberman <igall at 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 <kshi at marvell.com>
Signed-off-by: Igal Liberman <igall at marvell.com>
Signed-off-by: Stefan Roese <sr at 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
More information about the U-Boot
mailing list