[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