[PATCH] net/axgbe: added marvel m88e1512 PHY support
Ashok Kumar Natarajan
ashokkumar.natarajan at amd.com
Mon Feb 16 09:54:31 CET 2026
Added marvel m88e1512 1G PHY support in amd axgbe driver
Signed-off-by: Ashok Kumar Natarajan <ashokkumar.natarajan at amd.com>
---
.mailmap | 1 +
drivers/net/axgbe/axgbe_ethdev.h | 4 +
drivers/net/axgbe/axgbe_mdio.c | 4 +-
drivers/net/axgbe/axgbe_phy.h | 32 +++
drivers/net/axgbe/axgbe_phy_impl.c | 309 ++++++++++++++++++++++++++++-
5 files changed, 343 insertions(+), 7 deletions(-)
diff --git a/.mailmap b/.mailmap
index 8bbfcc1703..6f951bcd92 100644
--- a/.mailmap
+++ b/.mailmap
@@ -165,6 +165,7 @@ Ashish Paul <apaul at juniper.net>
Ashish Sadanandan <ashish.sadanandan at gmail.com>
Ashish Shah <ashish.n.shah at intel.com>
Ashok Kaladi <ashok.k.kaladi at intel.com>
+Ashok Kumar Natarajan <ashokkumar.natarajan at amd.com>
Ashwin Sekhar T K <asekhar at marvell.com> <ashwin.sekhar at caviumnetworks.com>
Asim Jamshed <asim.jamshed at gmail.com>
Atul Patel <atul.patel at intel.com>
diff --git a/drivers/net/axgbe/axgbe_ethdev.h b/drivers/net/axgbe/axgbe_ethdev.h
index b94a7f3562..3a9dc81691 100644
--- a/drivers/net/axgbe/axgbe_ethdev.h
+++ b/drivers/net/axgbe/axgbe_ethdev.h
@@ -276,6 +276,7 @@ struct axgbe_phy {
int pause_autoneg;
int tx_pause;
int rx_pause;
+ int id;
};
enum axgbe_i2c_cmd {
@@ -398,6 +399,9 @@ struct axgbe_phy_impl_if {
/* Pre/Post KR training enablement support */
void (*kr_training_pre)(struct axgbe_port *);
void (*kr_training_post)(struct axgbe_port *);
+
+ int (*read)(struct axgbe_port *port, int addr, int reg);
+ int (*write)(struct axgbe_port *port, int addr, int reg, u16 val);
};
struct axgbe_phy_if {
diff --git a/drivers/net/axgbe/axgbe_mdio.c b/drivers/net/axgbe/axgbe_mdio.c
index 952a0add62..a37cd8b80d 100644
--- a/drivers/net/axgbe/axgbe_mdio.c
+++ b/drivers/net/axgbe/axgbe_mdio.c
@@ -155,8 +155,8 @@ static void axgbe_sgmii_10_mode(struct axgbe_port *pdata)
static void axgbe_sgmii_100_mode(struct axgbe_port *pdata)
{
- /* Set MAC to 1G speed */
- pdata->hw_if.set_speed(pdata, SPEED_1000);
+ /* Set MAC to 100M speed */
+ pdata->hw_if.set_speed(pdata, SPEED_100);
/* Call PHY implementation support to complete rate change */
pdata->phy_if.phy_impl.set_mode(pdata, AXGBE_MODE_SGMII_100);
diff --git a/drivers/net/axgbe/axgbe_phy.h b/drivers/net/axgbe/axgbe_phy.h
index eee3afc370..ef02488adf 100644
--- a/drivers/net/axgbe/axgbe_phy.h
+++ b/drivers/net/axgbe/axgbe_phy.h
@@ -63,6 +63,29 @@
#define BMCR_RESET 0x8000 /* Reset to default state */
#define BMCR_SPEED10 0x0000 /* Select 10Mbps */
+/* Advertisement control register. */
+#define ADVERTISE_SLCT 0x001f /* Selector bits */
+#define ADVERTISE_CSMA 0x0001 /* Only selector supported */
+#define ADVERTISE_10HALF 0x0020 /* Try for 10mbps half-duplex */
+#define ADVERTISE_1000XFULL 0x0020 /* Try for 1000BASE-X full-duplex */
+#define ADVERTISE_10FULL 0x0040 /* Try for 10mbps full-duplex */
+#define ADVERTISE_1000XHALF 0x0040 /* Try for 1000BASE-X half-duplex */
+#define ADVERTISE_100HALF 0x0080 /* Try for 100mbps half-duplex */
+#define ADVERTISE_1000XPAUSE 0x0080 /* Try for 1000BASE-X pause */
+#define ADVERTISE_100FULL 0x0100 /* Try for 100mbps full-duplex */
+#define ADVERTISE_1000XPSE_ASYM 0x0100 /* Try for 1000BASE-X asym pause */
+#define ADVERTISE_100BASE4 0x0200 /* Try for 100mbps 4k packets */
+#define ADVERTISE_PAUSE_CAP 0x0400 /* Try for pause */
+#define ADVERTISE_PAUSE_ASYM 0x0800 /* Try for asymmetric pause */
+#define ADVERTISE_RESV 0x1000 /* Unused... */
+#define ADVERTISE_RFAULT 0x2000 /* Say we can detect faults */
+#define ADVERTISE_LPACK 0x4000 /* Ack link partners response */
+#define ADVERTISE_NPAGE 0x8000 /* Next page bit */
+
+#define ADVERTISE_FULL (ADVERTISE_100FULL | ADVERTISE_10FULL | \
+ ADVERTISE_CSMA)
+#define ADVERTISE_ALL (ADVERTISE_10HALF | ADVERTISE_10FULL | \
+ ADVERTISE_100HALF | ADVERTISE_100FULL)
/* MDIO Manageable Devices (MMDs). */
#define MDIO_MMD_PMAPMD 1 /* Physical Medium Attachment
@@ -114,6 +137,15 @@
#define MDIO_AN_10GBT_CTRL 32 /* 10GBASE-T auto-negotiation control */
#define MDIO_AN_10GBT_STAT 33 /* 10GBASE-T auto-negotiation status */
+#define AXGBE_M88E1512_PAGE_ADDR 0x0016
+#define AXGBE_M88E1512_CFG_REG_1 0x0010
+#define AXGBE_M88E1512_CFG_REG_2 0x0011
+#define AXGBE_M88E1512_CFG_REG_3 0x0007
+#define AXGBE_M88E1512_MODE 0x0014
+
+#define AXGBE_M88E1512_SCR 0x10
+
+
/* Control register 1. */
/* Enable extended speed selection */
#define MDIO_CTRL1_SPEEDSELEXT (BMCR_SPEED1000 | BMCR_SPEED100)
diff --git a/drivers/net/axgbe/axgbe_phy_impl.c b/drivers/net/axgbe/axgbe_phy_impl.c
index 9249e11335..96773dd722 100644
--- a/drivers/net/axgbe/axgbe_phy_impl.c
+++ b/drivers/net/axgbe/axgbe_phy_impl.c
@@ -37,6 +37,8 @@
#define AXGBE_CDR_DELAY_INC 10000
#define AXGBE_CDR_DELAY_MAX 100000
+#define M88E1512_E_PHY_ID 0x01410DD0
+
enum axgbe_port_mode {
AXGBE_PORT_MODE_RSVD = 0,
AXGBE_PORT_MODE_BACKPLANE,
@@ -250,7 +252,11 @@ static enum axgbe_an_mode axgbe_phy_an_mode(struct axgbe_port *pdata);
static void axgbe_phy_perform_ratechange(struct axgbe_port *pdata,
enum axgbe_mb_cmd cmd, enum axgbe_mb_subcmd sub_cmd);
static void axgbe_phy_rrc(struct axgbe_port *pdata);
+static int axgbe_get_ext_phy_link_status(struct axgbe_port *pdata);
+static int axgbe_phy_get_comm_ownership(struct axgbe_port *pdata);
+static void axgbe_phy_put_comm_ownership(struct axgbe_port *pdata);
+static int axgbe_m88e1512_config_aneg(struct axgbe_port *pdata);
static int axgbe_phy_i2c_xfer(struct axgbe_port *pdata,
struct axgbe_i2c_op *i2c_op)
@@ -258,6 +264,29 @@ static int axgbe_phy_i2c_xfer(struct axgbe_port *pdata,
return pdata->i2c_if.i2c_xfer(pdata, i2c_op);
}
+static int axgbe_phy_mii_read_c22(struct axgbe_port *pdata, int addr, int reg)
+{
+ int ret, regval;
+ ret = axgbe_phy_get_comm_ownership(pdata);
+ if (ret)
+ return -1;
+ regval = pdata->hw_if.read_ext_mii_regs_c22(pdata, addr, reg);
+ axgbe_phy_put_comm_ownership(pdata);
+ return regval;
+}
+
+static int axgbe_phy_mii_write_c22(struct axgbe_port *pdata, int addr,
+ int reg, u16 val)
+{
+ int ret, regval;
+ ret = axgbe_phy_get_comm_ownership(pdata);
+ if (ret)
+ return -1;
+ regval = pdata->hw_if.write_ext_mii_regs_c22(pdata, addr, reg, val);
+ axgbe_phy_put_comm_ownership(pdata);
+ return regval;
+}
+
static int axgbe_phy_redrv_write(struct axgbe_port *pdata, unsigned int reg,
unsigned int val)
{
@@ -1090,12 +1119,17 @@ static unsigned int axgbe_phy_an_advertising(struct axgbe_port *pdata)
return advertising;
}
-static int axgbe_phy_an_config(struct axgbe_port *pdata __rte_unused)
+static int axgbe_phy_an_config(struct axgbe_port *pdata)
{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+
+ if (phy_data->port_mode != AXGBE_PORT_MODE_1000BASE_T)
+ return 0;
+
+ if (pdata->phy.id == M88E1512_E_PHY_ID)
+ axgbe_m88e1512_config_aneg(pdata);
+
return 0;
- /* Dummy API since there is no case to support
- * external phy devices registered through kernel APIs
- */
}
static enum axgbe_an_mode axgbe_phy_an_sfp_mode(struct axgbe_phy_data *phy_data)
@@ -1207,6 +1241,7 @@ static void axgbe_set_rx_adap_mode(struct axgbe_port *pdata,
{
if (pdata->rx_adapt_retries++ >= MAX_RX_ADAPT_RETRIES) {
pdata->rx_adapt_retries = 0;
+ pdata->mode_set = false;
return;
}
@@ -1497,6 +1532,18 @@ static void axgbe_phy_sgmii_1000_mode(struct axgbe_port *pdata)
phy_data->cur_mode = AXGBE_MODE_SGMII_1000;
}
+static void axgbe_phy_sgmii_100_mode(struct axgbe_port *pdata)
+{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+
+ axgbe_phy_set_redrv_mode(pdata);
+
+ /* 100M/SGMII */
+ axgbe_phy_perform_ratechange(pdata, AXGBE_MB_CMD_SET_1G, AXGBE_MB_SUBCMD_100MBITS);
+
+ phy_data->cur_mode = AXGBE_MODE_SGMII_100;
+}
+
static void axgbe_phy_sgmii_10_mode(struct axgbe_port *pdata)
{
struct axgbe_phy_data *phy_data = pdata->phy_data;
@@ -1694,6 +1741,9 @@ static void axgbe_phy_set_mode(struct axgbe_port *pdata, enum axgbe_mode mode)
case AXGBE_MODE_SGMII_1000:
axgbe_phy_sgmii_1000_mode(pdata);
break;
+ case AXGBE_MODE_SGMII_100:
+ axgbe_phy_sgmii_100_mode(pdata);
+ break;
case AXGBE_MODE_SGMII_10:
axgbe_phy_sgmii_10_mode(pdata);
break;
@@ -1863,6 +1913,17 @@ static int axgbe_phy_link_status(struct axgbe_port *pdata, int *an_restart)
}
}
+ if (phy_data->port_mode == AXGBE_PORT_MODE_1000BASE_T) {
+ if (pdata->phy.id == M88E1512_E_PHY_ID) {
+ if (axgbe_get_ext_phy_link_status(pdata) == 1) {
+ PMD_DRV_LOG_LINE(DEBUG, "M88E1512 PHY link is up");
+ } else {
+ PMD_DRV_LOG_LINE(DEBUG, "M88E1512 PHY link is not up");
+ goto out;
+ }
+ }
+ }
+
/* Link status is latched low, so read once to clear
* and then read again to get current state
*/
@@ -1905,7 +1966,8 @@ static int axgbe_phy_link_status(struct axgbe_port *pdata, int *an_restart)
phy_data->rrc_count = 0;
axgbe_phy_rrc(pdata);
}
-
+out:
+ pdata->rx_adapt_done = false;
return 0;
}
@@ -2263,6 +2325,226 @@ static int axgbe_phy_reset(struct axgbe_port *pdata)
return 0;
}
+static int axgbe_m88e5112_set_page(struct axgbe_port *pdata, int page)
+{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ unsigned int mdio_addr = phy_data->mdio_addr;
+ return pdata->phy_if.phy_impl.write(pdata, mdio_addr,
+ AXGBE_M88E1512_PAGE_ADDR, page);
+}
+
+static int axgbe_get_phy_id(struct axgbe_port *pdata)
+{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ unsigned int mdio_addr = phy_data->mdio_addr;
+ int phy_id, ret_val;
+ ret_val = pdata->phy_if.phy_impl.read(pdata, mdio_addr, MII_PHYSID1);
+
+ phy_id = ret_val << 16;
+
+ ret_val = pdata->phy_if.phy_impl.read(pdata, mdio_addr, MII_PHYSID2);
+ phy_id |= ret_val & 0xfff0;
+
+ return phy_id;
+}
+
+static int axgbe_m88e1512_soft_reset(struct axgbe_port *pdata)
+{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ unsigned int mdio_addr = phy_data->mdio_addr;
+ int bmcr;
+ int ret;
+
+ bmcr = pdata->phy_if.phy_impl.read(pdata, mdio_addr, MII_BMCR);
+ if (bmcr == -1)
+ goto out;
+ bmcr |= BMCR_RESET;
+ ret = pdata->phy_if.phy_impl.write(pdata, mdio_addr, MII_BMCR, bmcr);
+ if (ret)
+ return ret;
+
+ rte_delay_us_sleep(1);
+out:
+ return ret;
+}
+
+static int axgbe_m88e1512_config_aneg(struct axgbe_port *pdata)
+{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ unsigned int mdio_addr = phy_data->mdio_addr;
+ int an_advert, bmcr;
+ int ret;
+
+ an_advert = ADVERTISE_10FULL |
+ ADVERTISE_100FULL |
+ ADVERTISE_PAUSE_CAP |
+ ADVERTISE_PAUSE_ASYM;
+
+ ret = pdata->phy_if.phy_impl.write(pdata, mdio_addr,
+ MII_ADVERTISE, an_advert);
+ if (ret)
+ return ret;
+
+ bmcr = pdata->phy_if.phy_impl.read(pdata, mdio_addr, MII_BMCR);
+ if (bmcr < 0)
+ return bmcr;
+
+ bmcr &= ~(MDIO_CTRL1_SPEEDSELEXT);
+ switch (phy_data->cur_mode) {
+ case AXGBE_MODE_SGMII_1000:
+ bmcr |= BMCR_SPEED1000;
+ break;
+ case AXGBE_MODE_SGMII_100:
+ bmcr |= BMCR_SPEED100;
+ break;
+ default:
+ break;
+ }
+ if (pdata->phy.autoneg == AUTONEG_ENABLE)
+ bmcr |= BMCR_ANENABLE | BMCR_ANRESTART;
+
+ ret = pdata->phy_if.phy_impl.write(pdata, mdio_addr,
+ MII_BMCR, bmcr);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static const struct {
+ u16 reg17, reg16;
+} errata_vals[] = {
+ { 0x214b, 0x2144 },
+ { 0x0c28, 0x2146 },
+ { 0xb233, 0x214d },
+ { 0xcc0c, 0x2159 },
+};
+
+static int axgbe_m88e1512_init(struct axgbe_port *pdata)
+{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ unsigned int mdio_addr = phy_data->mdio_addr;
+ int ret;
+ unsigned int i;
+
+ PMD_DRV_LOG_LINE(DEBUG, "Initialize M88E1512 phy");
+
+ /* Switch to PHY page 0xFF */
+ ret = axgbe_m88e5112_set_page(pdata, 0xff);
+ if (ret)
+ return ret;
+ /* Configure M88E1512 errata registers */
+ for (i = 0; i < ARRAY_SIZE(errata_vals); i++) {
+ ret = pdata->phy_if.phy_impl.write(pdata, mdio_addr,
+ AXGBE_M88E1512_CFG_REG_2,
+ errata_vals[i].reg17);
+ if (ret)
+ return ret;
+
+ ret = pdata->phy_if.phy_impl.write(pdata, mdio_addr,
+ AXGBE_M88E1512_CFG_REG_1,
+ errata_vals[i].reg16);
+ if (ret)
+ return ret;
+ }
+
+ /* Switch to PHY page 0xFB */
+ ret = axgbe_m88e5112_set_page(pdata, 0xfb);
+ if (ret)
+ return ret;
+
+ ret = pdata->phy_if.phy_impl.write(pdata, mdio_addr,
+ AXGBE_M88E1512_CFG_REG_3, 0xC00D);
+ if (ret)
+ return ret;
+
+ /* Switch to PHY page 0 */
+ ret = axgbe_m88e5112_set_page(pdata, 0);
+ if (ret)
+ return ret;
+
+ /* SGMII-to-Copper mode initialization */
+
+ /* Switch to PHY page 0x12 */
+ ret = axgbe_m88e5112_set_page(pdata, 0x12);
+ if (ret)
+ return ret;
+
+ ret = pdata->phy_if.phy_impl.write(pdata, mdio_addr,
+ AXGBE_M88E1512_MODE, 0x8001);
+ if (ret)
+ return ret;
+
+ /* Switch to PHY page 0 */
+ ret = axgbe_m88e5112_set_page(pdata, 0);
+ if (ret)
+ return ret;
+
+ ret = axgbe_m88e1512_soft_reset(pdata);
+ if (ret)
+ return ret;
+
+ rte_delay_ms(1000);
+
+
+ /* Switch to PHY page 3 */
+ ret = axgbe_m88e5112_set_page(pdata, 3);
+ if (ret)
+ return ret;
+
+ /* enable downshift */
+ ret = pdata->phy_if.phy_impl.write(pdata, mdio_addr,
+ AXGBE_M88E1512_SCR, 0x1177);
+ if (ret)
+ return ret;
+
+
+ /* Switch to PHY page 0 */
+ ret = axgbe_m88e5112_set_page(pdata, 0);
+ if (ret)
+ return ret;
+
+ ret = axgbe_m88e1512_soft_reset(pdata);
+ if (ret)
+ return ret;
+
+ rte_delay_ms(1000);
+
+ PMD_DRV_LOG_LINE(DEBUG, "M88E1512 phy init done");
+ return 0;
+}
+
+
+static int axgbe_get_ext_phy_link_status(struct axgbe_port *pdata)
+{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ unsigned int mdio_addr = phy_data->mdio_addr;
+ int status = 0, bmcr;
+
+ bmcr = pdata->phy_if.phy_impl.read(pdata, mdio_addr, MII_BMCR);
+ if (bmcr < 0)
+ return bmcr;
+
+ /* Autoneg is being started, therefore disregard BMSR value and
+ * report link as down.
+ */
+ if (bmcr & BMCR_ANRESTART)
+ return 0;
+
+ status = pdata->phy_if.phy_impl.read(pdata, mdio_addr, MII_BMSR);
+ if (status < 0)
+ return status;
+ else if (status & BMSR_LSTATUS)
+ goto done;
+
+ /* Read link and autonegotiation status */
+ status = pdata->phy_if.phy_impl.read(pdata, mdio_addr, MII_BMSR);
+ if (status < 0)
+ return status;
+done:
+ return (status & BMSR_LSTATUS) ? 1 : 0;
+}
+
static int axgbe_phy_init(struct axgbe_port *pdata)
{
struct axgbe_phy_data *phy_data;
@@ -2292,6 +2574,12 @@ static int axgbe_phy_init(struct axgbe_port *pdata)
phy_data->conn_type = XP_GET_BITS(pdata->pp0, XP_PROP_0, CONN_TYPE);
phy_data->mdio_addr = XP_GET_BITS(pdata->pp0, XP_PROP_0, MDIO_ADDR);
+ PMD_DRV_LOG_LINE(DEBUG, "port mode : %d", phy_data->port_mode);
+ PMD_DRV_LOG_LINE(DEBUG, "port id : %d", phy_data->port_id);
+ PMD_DRV_LOG_LINE(DEBUG, "port speed : %d", phy_data->port_speeds);
+ PMD_DRV_LOG_LINE(DEBUG, "conn type : %d", phy_data->conn_type);
+ PMD_DRV_LOG_LINE(DEBUG, "mdio addr : %d", phy_data->mdio_addr);
+
phy_data->redrv = XP_GET_BITS(pdata->pp4, XP_PROP_4, REDRV_PRESENT);
phy_data->redrv_if = XP_GET_BITS(pdata->pp4, XP_PROP_4, REDRV_IF);
phy_data->redrv_addr = XP_GET_BITS(pdata->pp4, XP_PROP_4, REDRV_ADDR);
@@ -2516,8 +2804,16 @@ static int axgbe_phy_init(struct axgbe_port *pdata)
}
phy_data->phy_cdr_delay = AXGBE_CDR_DELAY_INIT;
+
+ pdata->phy.id = axgbe_get_phy_id(pdata);
+ PMD_DRV_LOG_LINE(DEBUG, "PHY ID = 0x%x", pdata->phy.id);
+
+ if (pdata->phy.id == M88E1512_E_PHY_ID)
+ axgbe_m88e1512_init(pdata);
+
return 0;
}
+
void axgbe_init_function_ptrs_phy_v2(struct axgbe_phy_if *phy_if)
{
struct axgbe_phy_impl_if *phy_impl = &phy_if->phy_impl;
@@ -2542,4 +2838,7 @@ void axgbe_init_function_ptrs_phy_v2(struct axgbe_phy_if *phy_if)
phy_impl->kr_training_pre = axgbe_phy_kr_training_pre;
phy_impl->kr_training_post = axgbe_phy_kr_training_post;
+
+ phy_impl->read = axgbe_phy_mii_read_c22;
+ phy_impl->write = axgbe_phy_mii_write_c22;
}
--
2.34.1
More information about the dev
mailing list