* [PATCH 1/3] net/axgbe: Add external PHY read/write functions
@ 2026-02-16 12:52 Ashok Kumar Natarajan
2026-02-16 12:52 ` [PATCH 2/3] net/axgbe: Add support for Marvell M88E1512 PHY Ashok Kumar Natarajan
` (3 more replies)
0 siblings, 4 replies; 23+ messages in thread
From: Ashok Kumar Natarajan @ 2026-02-16 12:52 UTC (permalink / raw)
To: dev; +Cc: Selwin.Sebastian, Ashok Kumar Natarajan
Introduce helper functions to perform external PHY register read and
write operations. These helpers currently support only IEEE Clause 22
PHY access, providing a simple and consistent API for accessing
standard 16‑bit MII registers on external PHY devices.
This patch does not implement Clause 45 transactions; support for C45
access can be added in future updates as needed.
No functional changes are introduced for existing drivers. The new
helpers will be used by subsequent patches that require external PHY
management.
Signed-off-by: Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com>
---
.mailmap | 1 +
drivers/net/axgbe/axgbe_ethdev.h | 3 +++
drivers/net/axgbe/axgbe_phy_impl.c | 29 +++++++++++++++++++++++++++++
3 files changed, 33 insertions(+)
diff --git a/.mailmap b/.mailmap
index 8bbfcc1703..6f951bcd92 100644
--- a/.mailmap
+++ b/.mailmap
@@ -165,6 +165,7 @@ Ashish Paul <apaul@juniper.net>
Ashish Sadanandan <ashish.sadanandan@gmail.com>
Ashish Shah <ashish.n.shah@intel.com>
Ashok Kaladi <ashok.k.kaladi@intel.com>
+Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com>
Ashwin Sekhar T K <asekhar@marvell.com> <ashwin.sekhar@caviumnetworks.com>
Asim Jamshed <asim.jamshed@gmail.com>
Atul Patel <atul.patel@intel.com>
diff --git a/drivers/net/axgbe/axgbe_ethdev.h b/drivers/net/axgbe/axgbe_ethdev.h
index b94a7f3562..12d77cd520 100644
--- a/drivers/net/axgbe/axgbe_ethdev.h
+++ b/drivers/net/axgbe/axgbe_ethdev.h
@@ -398,6 +398,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_phy_impl.c b/drivers/net/axgbe/axgbe_phy_impl.c
index 9249e11335..4f3cc63836 100644
--- a/drivers/net/axgbe/axgbe_phy_impl.c
+++ b/drivers/net/axgbe/axgbe_phy_impl.c
@@ -251,6 +251,8 @@ 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_phy_get_comm_ownership(struct axgbe_port *pdata);
+static void axgbe_phy_put_comm_ownership(struct axgbe_port *pdata);
static int axgbe_phy_i2c_xfer(struct axgbe_port *pdata,
struct axgbe_i2c_op *i2c_op)
@@ -258,6 +260,30 @@ 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)
{
@@ -2542,4 +2568,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
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 2/3] net/axgbe: Add support for Marvell M88E1512 PHY
2026-02-16 12:52 [PATCH 1/3] net/axgbe: Add external PHY read/write functions Ashok Kumar Natarajan
@ 2026-02-16 12:52 ` Ashok Kumar Natarajan
2026-02-16 16:29 ` Stephen Hemminger
2026-02-25 12:47 ` [PATCH v2 2/3] net/axgbe: add support for marvell m88e1512 PHY Ashok Kumar Natarajan
2026-02-16 12:52 ` [PATCH 3/3] net/axgbe: Add support for 100Mbps link speed Ashok Kumar Natarajan
` (2 subsequent siblings)
3 siblings, 2 replies; 23+ messages in thread
From: Ashok Kumar Natarajan @ 2026-02-16 12:52 UTC (permalink / raw)
To: dev; +Cc: Selwin.Sebastian, Ashok Kumar Natarajan
Add support for the Marvell M88E1512 PHY in the axgbe driver. This patch
implements device identification and basic initialization required to
operate with this PHY. Only the M88E1512 model is supported in this
update.
Signed-off-by: Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com>
---
drivers/net/axgbe/axgbe_ethdev.h | 1 +
drivers/net/axgbe/axgbe_phy.h | 32 ++++
drivers/net/axgbe/axgbe_phy_impl.c | 261 ++++++++++++++++++++++++++++-
3 files changed, 290 insertions(+), 4 deletions(-)
diff --git a/drivers/net/axgbe/axgbe_ethdev.h b/drivers/net/axgbe/axgbe_ethdev.h
index 12d77cd520..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 {
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 4f3cc63836..20cfdcc796 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,9 +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)
@@ -1116,12 +1120,19 @@ 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;
+
+ /* Auto-negotiation config is implemented only for 1000BASE-T */
+ if (phy_data->port_mode != AXGBE_PORT_MODE_1000BASE_T)
+ return 0;
+
+ /* Supports only Marvell M88E1512 */
+ 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)
@@ -1889,6 +1900,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
*/
@@ -1932,6 +1954,8 @@ static int axgbe_phy_link_status(struct axgbe_port *pdata, int *an_restart)
axgbe_phy_rrc(pdata);
}
+out:
+ pdata->rx_adapt_done = false;
return 0;
}
@@ -2289,6 +2313,225 @@ static int axgbe_phy_reset(struct axgbe_port *pdata)
return 0;
}
+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_phy_soft_reset(struct axgbe_port *pdata)
+{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ unsigned int mdio_addr = phy_data->mdio_addr;
+ int ret, bmcr;
+
+ bmcr = pdata->phy_if.phy_impl.read(pdata, mdio_addr, MII_BMCR);
+ if (bmcr < 0)
+ return bmcr;
+
+ 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);
+ return 0;
+}
+
+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 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 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_phy_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_phy_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;
@@ -2542,6 +2785,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) {
+ ret = axgbe_m88e1512_init(pdata);
+ if(ret)
+ return ret;
+ }
+
return 0;
}
void axgbe_init_function_ptrs_phy_v2(struct axgbe_phy_if *phy_if)
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH 3/3] net/axgbe: Add support for 100Mbps link speed
2026-02-16 12:52 [PATCH 1/3] net/axgbe: Add external PHY read/write functions Ashok Kumar Natarajan
2026-02-16 12:52 ` [PATCH 2/3] net/axgbe: Add support for Marvell M88E1512 PHY Ashok Kumar Natarajan
@ 2026-02-16 12:52 ` Ashok Kumar Natarajan
2026-02-25 12:49 ` [PATCH v2 3/3] net/axgbe: add " Ashok Kumar Natarajan
2026-02-17 0:27 ` [PATCH 1/3] net/axgbe: Add external PHY read/write functions Stephen Hemminger
2026-02-25 12:44 ` [PATCH v2 1/3] net/axgbe: add " Ashok Kumar Natarajan
3 siblings, 1 reply; 23+ messages in thread
From: Ashok Kumar Natarajan @ 2026-02-16 12:52 UTC (permalink / raw)
To: dev; +Cc: Selwin.Sebastian, Ashok Kumar Natarajan
Add 100Mbps link speed support to the axgbe driver. This update enables
the MAC and PHY configuration required to operate at 100M, including
proper mode selection and link handling.
Signed-off-by: Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com>
---
drivers/net/axgbe/axgbe_mdio.c | 4 ++--
drivers/net/axgbe/axgbe_phy_impl.c | 15 +++++++++++++++
2 files changed, 17 insertions(+), 2 deletions(-)
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_impl.c b/drivers/net/axgbe/axgbe_phy_impl.c
index 20cfdcc796..3dbbee15b0 100644
--- a/drivers/net/axgbe/axgbe_phy_impl.c
+++ b/drivers/net/axgbe/axgbe_phy_impl.c
@@ -1534,6 +1534,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;
@@ -1731,6 +1743,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;
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH 2/3] net/axgbe: Add support for Marvell M88E1512 PHY
2026-02-16 12:52 ` [PATCH 2/3] net/axgbe: Add support for Marvell M88E1512 PHY Ashok Kumar Natarajan
@ 2026-02-16 16:29 ` Stephen Hemminger
2026-02-25 12:47 ` [PATCH v2 2/3] net/axgbe: add support for marvell m88e1512 PHY Ashok Kumar Natarajan
1 sibling, 0 replies; 23+ messages in thread
From: Stephen Hemminger @ 2026-02-16 16:29 UTC (permalink / raw)
To: Ashok Kumar Natarajan; +Cc: dev, Selwin.Sebastian
On Mon, 16 Feb 2026 18:22:04 +0530
Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com> wrote:
> diff --git a/drivers/net/axgbe/axgbe_ethdev.h b/drivers/net/axgbe/axgbe_ethdev.h
> index 12d77cd520..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;
> };
>
These really shouldn't be "int".
This looks like legacy era K&R code style.
The driver has this in lots of places.
The flags should be bool, but that is another issue.
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH 1/3] net/axgbe: Add external PHY read/write functions
2026-02-16 12:52 [PATCH 1/3] net/axgbe: Add external PHY read/write functions Ashok Kumar Natarajan
2026-02-16 12:52 ` [PATCH 2/3] net/axgbe: Add support for Marvell M88E1512 PHY Ashok Kumar Natarajan
2026-02-16 12:52 ` [PATCH 3/3] net/axgbe: Add support for 100Mbps link speed Ashok Kumar Natarajan
@ 2026-02-17 0:27 ` Stephen Hemminger
2026-02-25 12:44 ` [PATCH v2 1/3] net/axgbe: add " Ashok Kumar Natarajan
3 siblings, 0 replies; 23+ messages in thread
From: Stephen Hemminger @ 2026-02-17 0:27 UTC (permalink / raw)
To: Ashok Kumar Natarajan; +Cc: dev, Selwin.Sebastian
On Mon, 16 Feb 2026 18:22:03 +0530
Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com> wrote:
> Introduce helper functions to perform external PHY register read and
> write operations. These helpers currently support only IEEE Clause 22
> PHY access, providing a simple and consistent API for accessing
> standard 16‑bit MII registers on external PHY devices.
>
> This patch does not implement Clause 45 transactions; support for C45
> access can be added in future updates as needed.
>
> No functional changes are introduced for existing drivers. The new
> helpers will be used by subsequent patches that require external PHY
> management.
>
> Signed-off-by: Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com>
> ---
The result of AI review feedback reports some issues that need resolution.
All AI feedback can be wrong, so use this is inspiration.
Correctness issues (must fix):
1. axgbe_get_phy_id() uses the return value of read() directly in
bit-shift arithmetic without checking for errors. If either MDIO read
fails (returns negative), the resulting PHY ID is garbage and could
accidentally match M88E1512_E_PHY_ID, triggering the wrong init
sequence. Please add error checks after each read.
2. axgbe_phy_an_config() silently drops the return value of
axgbe_m88e1512_config_aneg(). That function can fail at multiple points
but the caller always returns 0. Should be: return
axgbe_m88e1512_config_aneg(pdata);
3. axgbe_m88e1512_init() switches PHY pages multiple times but never
restores page 0 on error paths. If a write fails while on page 0xFF or
0x12, all subsequent MDIO accesses from other code paths will hit the
wrong registers. Add an error cleanup label that restores page 0.
4. The read/write helpers (patch 1) don't check the return value of the
underlying read_ext_mii_regs_c22/write_ext_mii_regs_c22 calls. Errors
from the hardware layer pass through silently.
Other items:
5. Function name typo: axgbe_m88e5112_set_page should be axgbe_m88e1512_set_page to match the actual PHY model.
6. All three subject lines use uppercase "Add" after the colon — should be lowercase per DPDK convention.
7. Missing space: if(ret) should be if (ret) in the init caller in patch 2.
8. The init function blocks for 2 seconds total (two rte_delay_ms(1000) calls). Is polling for reset completion feasible instead?
9. Missing release notes for external PHY support and 100M speed — these are user-visible features.
10. Various magic numbers (0x8001, 0x1177, 0xC00D) written to registers without defines or comments referencing the datasheet.
Please fix at least items 1-4 and resubmit.
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v2 1/3] net/axgbe: add external PHY read/write functions
2026-02-16 12:52 [PATCH 1/3] net/axgbe: Add external PHY read/write functions Ashok Kumar Natarajan
` (2 preceding siblings ...)
2026-02-17 0:27 ` [PATCH 1/3] net/axgbe: Add external PHY read/write functions Stephen Hemminger
@ 2026-02-25 12:44 ` Ashok Kumar Natarajan
2026-02-25 23:09 ` Stephen Hemminger
2026-02-26 16:46 ` [PATCH v3 " Ashok Kumar Natarajan
3 siblings, 2 replies; 23+ messages in thread
From: Ashok Kumar Natarajan @ 2026-02-25 12:44 UTC (permalink / raw)
To: dev; +Cc: Selwin.Sebastian, Ashok Kumar Natarajan
Introduce helper functions to perform external PHY register read and
write operations. These helpers currently support only IEEE Clause 22
PHY access, providing a simple and consistent API for accessing
standard 16‑bit MII registers on external PHY devices.
Signed-off-by: Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com>
---
.mailmap | 1 +
drivers/net/axgbe/axgbe_dev.c | 56 ++++++++++++++++--------------
drivers/net/axgbe/axgbe_ethdev.h | 16 ++++++---
drivers/net/axgbe/axgbe_phy_impl.c | 45 +++++++++++++++++++++++-
4 files changed, 85 insertions(+), 33 deletions(-)
diff --git a/.mailmap b/.mailmap
index 6c4c977dde..5c8b47f1a9 100644
--- a/.mailmap
+++ b/.mailmap
@@ -165,6 +165,7 @@ Ashish Paul <apaul@juniper.net>
Ashish Sadanandan <ashish.sadanandan@gmail.com>
Ashish Shah <ashish.n.shah@intel.com>
Ashok Kaladi <ashok.k.kaladi@intel.com>
+Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com>
Ashwin Sekhar T K <asekhar@marvell.com> <ashwin.sekhar@caviumnetworks.com>
Asim Jamshed <asim.jamshed@gmail.com>
Atul Patel <atul.patel@intel.com>
diff --git a/drivers/net/axgbe/axgbe_dev.c b/drivers/net/axgbe/axgbe_dev.c
index 634d4ee4a5..10a99aeac2 100644
--- a/drivers/net/axgbe/axgbe_dev.c
+++ b/drivers/net/axgbe/axgbe_dev.c
@@ -63,9 +63,9 @@ static int mdio_complete(struct axgbe_port *pdata)
return 0;
}
-static unsigned int axgbe_create_mdio_sca_c22(int port, int reg)
+static unsigned int axgbe_create_mdio_sca_c22(u8 port, u16 reg)
{
- unsigned int mdio_sca;
+ u32 mdio_sca;
mdio_sca = 0;
AXGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, RA, reg);
@@ -74,20 +74,20 @@ static unsigned int axgbe_create_mdio_sca_c22(int port, int reg)
return mdio_sca;
}
-static unsigned int axgbe_create_mdio_sca_c45(int port, unsigned int da, int reg)
+static unsigned int axgbe_create_mdio_sca_c45(u8 port, u8 dev_addr, u16 reg)
{
- unsigned int mdio_sca;
+ u32 mdio_sca;
mdio_sca = 0;
AXGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, RA, reg);
AXGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, PA, port);
- AXGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, DA, da);
+ AXGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, DA, dev_addr);
return mdio_sca;
}
static int axgbe_write_ext_mii_regs(struct axgbe_port *pdata,
- unsigned int mdio_sca, u16 val)
+ u32 mdio_sca, u16 value)
{
unsigned int mdio_sccd;
uint64_t timeout;
@@ -95,7 +95,7 @@ static int axgbe_write_ext_mii_regs(struct axgbe_port *pdata,
AXGMAC_IOWRITE(pdata, MAC_MDIOSCAR, mdio_sca);
mdio_sccd = 0;
- AXGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, DATA, val);
+ AXGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, DATA, value);
AXGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, CMD, 1);
AXGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, BUSY, 1);
AXGMAC_IOWRITE(pdata, MAC_MDIOSCCDR, mdio_sccd);
@@ -113,28 +113,28 @@ static int axgbe_write_ext_mii_regs(struct axgbe_port *pdata,
static int axgbe_write_ext_mii_regs_c22(struct axgbe_port *pdata,
- int addr, int reg, u16 val)
+ u8 phy_addr, u16 reg, u16 value)
{
- unsigned int mdio_sca;
+ u32 mdio_sca;
- mdio_sca = axgbe_create_mdio_sca_c22(addr, reg);
+ mdio_sca = axgbe_create_mdio_sca_c22(phy_addr, reg);
- return axgbe_write_ext_mii_regs(pdata, mdio_sca, val);
+ return axgbe_write_ext_mii_regs(pdata, mdio_sca, value);
}
static int axgbe_write_ext_mii_regs_c45(struct axgbe_port *pdata,
- int addr, int devad, int reg, u16 val)
+ u8 phy_addr, u8 dev_addr, u16 reg, u16 value)
{
- unsigned int mdio_sca;
+ u32 mdio_sca;
- mdio_sca = axgbe_create_mdio_sca_c45(addr, devad, reg);
+ mdio_sca = axgbe_create_mdio_sca_c45(phy_addr, dev_addr, reg);
- return axgbe_write_ext_mii_regs(pdata, mdio_sca, val);
+ return axgbe_write_ext_mii_regs(pdata, mdio_sca, value);
}
-static int axgbe_read_ext_mii_regs(struct axgbe_port *pdata,
- unsigned int mdio_sca)
+static int axgbe_read_ext_mii_regs(struct axgbe_port *pdata, u32 mdio_sca,
+ u16 *value)
{
unsigned int mdio_sccd;
uint64_t timeout;
@@ -158,26 +158,28 @@ static int axgbe_read_ext_mii_regs(struct axgbe_port *pdata,
return -ETIMEDOUT;
success:
- return AXGMAC_IOREAD_BITS(pdata, MAC_MDIOSCCDR, DATA);
+ *value = AXGMAC_IOREAD_BITS(pdata, MAC_MDIOSCCDR, DATA);
+ return 0;
}
-static int axgbe_read_ext_mii_regs_c22(struct axgbe_port *pdata, int addr, int reg)
+static int axgbe_read_ext_mii_regs_c22(struct axgbe_port *pdata, u8 phy_addr,
+ u16 reg, u16 *value)
{
- unsigned int mdio_sca;
+ u32 mdio_sca;
- mdio_sca = axgbe_create_mdio_sca_c22(addr, reg);
+ mdio_sca = axgbe_create_mdio_sca_c22(phy_addr, reg);
- return axgbe_read_ext_mii_regs(pdata, mdio_sca);
+ return axgbe_read_ext_mii_regs(pdata, mdio_sca, value);
}
-static int axgbe_read_ext_mii_regs_c45(struct axgbe_port *pdata, int addr,
- int devad, int reg)
+static int axgbe_read_ext_mii_regs_c45(struct axgbe_port *pdata, u8 phy_addr,
+ u8 dev_addr, u16 reg, u16 *value)
{
- unsigned int mdio_sca;
+ u32 mdio_sca;
- mdio_sca = axgbe_create_mdio_sca_c45(addr, devad, reg);
+ mdio_sca = axgbe_create_mdio_sca_c45(phy_addr, dev_addr, reg);
- return axgbe_read_ext_mii_regs(pdata, mdio_sca);
+ return axgbe_read_ext_mii_regs(pdata, mdio_sca, value);
}
static int axgbe_set_ext_mii_mode(struct axgbe_port *pdata, unsigned int port,
diff --git a/drivers/net/axgbe/axgbe_ethdev.h b/drivers/net/axgbe/axgbe_ethdev.h
index b94a7f3562..f8f58460c4 100644
--- a/drivers/net/axgbe/axgbe_ethdev.h
+++ b/drivers/net/axgbe/axgbe_ethdev.h
@@ -327,11 +327,14 @@ struct axgbe_hw_if {
int (*set_ext_mii_mode)(struct axgbe_port *, unsigned int,
enum axgbe_mdio_mode);
- int (*read_ext_mii_regs_c22)(struct axgbe_port *pdata, int addr, int reg);
- int (*write_ext_mii_regs_c22)(struct axgbe_port *pdata, int addr, int reg, uint16_t val);
- int (*read_ext_mii_regs_c45)(struct axgbe_port *pdata, int addr, int devad, int reg);
- int (*write_ext_mii_regs_c45)(struct axgbe_port *pdata, int addr, int devad,
- int reg, uint16_t val);
+ int (*read_ext_mii_regs_c22)(struct axgbe_port *pdata, u8 phy_addr,
+ u16 reg, u16 *value);
+ int (*write_ext_mii_regs_c22)(struct axgbe_port *pdata, u8 phy_addr,
+ u16 reg, u16 value);
+ int (*read_ext_mii_regs_c45)(struct axgbe_port *pdata, u8 phy_addr,
+ u8 dev_addr, u16 reg, u16 *value);
+ int (*write_ext_mii_regs_c45)(struct axgbe_port *pdata, u8 phy_addr,
+ u8 dev_addr, u16 reg, u16 value);
/* For FLOW ctrl */
int (*config_tx_flow_control)(struct axgbe_port *);
@@ -398,6 +401,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, u16 reg, u16 *value);
+ int (*write)(struct axgbe_port *port, u16 reg, u16 value);
};
struct axgbe_phy_if {
diff --git a/drivers/net/axgbe/axgbe_phy_impl.c b/drivers/net/axgbe/axgbe_phy_impl.c
index 9249e11335..5bee7fec7a 100644
--- a/drivers/net/axgbe/axgbe_phy_impl.c
+++ b/drivers/net/axgbe/axgbe_phy_impl.c
@@ -203,7 +203,7 @@ struct axgbe_phy_data {
unsigned int rrc_count;
- unsigned int mdio_addr;
+ uint8_t mdio_addr;
/* SFP Support */
enum axgbe_sfp_comm sfp_comm;
@@ -251,6 +251,8 @@ 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_phy_get_comm_ownership(struct axgbe_port *pdata);
+static void axgbe_phy_put_comm_ownership(struct axgbe_port *pdata);
static int axgbe_phy_i2c_xfer(struct axgbe_port *pdata,
struct axgbe_i2c_op *i2c_op)
@@ -258,6 +260,44 @@ static int axgbe_phy_i2c_xfer(struct axgbe_port *pdata,
return pdata->i2c_if.i2c_xfer(pdata, i2c_op);
}
+static int axgbe_phy_read(struct axgbe_port *pdata, u16 reg, u16 *value)
+{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ int ret;
+
+ ret = axgbe_phy_get_comm_ownership(pdata);
+ if (ret)
+ return ret;
+
+ ret = pdata->hw_if.read_ext_mii_regs_c22(pdata,
+ phy_data->mdio_addr, reg, value);
+ if (ret)
+ PMD_DRV_LOG_LINE(ERR, "mdio read failed %s",
+ strerror(ret));
+
+ axgbe_phy_put_comm_ownership(pdata);
+ return ret;
+}
+
+static int axgbe_phy_write(struct axgbe_port *pdata, u16 reg, u16 value)
+{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ int ret;
+ ret = axgbe_phy_get_comm_ownership(pdata);
+ if (ret)
+ return ret;
+
+ ret = pdata->hw_if.write_ext_mii_regs_c22(pdata,
+ phy_data->mdio_addr, reg, value);
+ if (ret)
+ PMD_DRV_LOG_LINE(ERR, "mdio write failed %s",
+ strerror(ret));
+
+ axgbe_phy_put_comm_ownership(pdata);
+ return ret;
+}
+
+
static int axgbe_phy_redrv_write(struct axgbe_port *pdata, unsigned int reg,
unsigned int val)
{
@@ -2542,4 +2582,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_read;
+ phy_impl->write = axgbe_phy_write;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 2/3] net/axgbe: add support for marvell m88e1512 PHY
2026-02-16 12:52 ` [PATCH 2/3] net/axgbe: Add support for Marvell M88E1512 PHY Ashok Kumar Natarajan
2026-02-16 16:29 ` Stephen Hemminger
@ 2026-02-25 12:47 ` Ashok Kumar Natarajan
2026-02-26 16:47 ` [PATCH v3 " Ashok Kumar Natarajan
1 sibling, 1 reply; 23+ messages in thread
From: Ashok Kumar Natarajan @ 2026-02-25 12:47 UTC (permalink / raw)
To: dev; +Cc: Selwin.Sebastian, Ashok Kumar Natarajan
Add support for the Marvell M88E1512 PHY in the axgbe driver. This patch
implements device identification and basic initialization required to
operate with this PHY. Only the M88E1512 model is supported in this
update.
Signed-off-by: Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com>
---
drivers/net/axgbe/axgbe_phy.h | 44 ++++
drivers/net/axgbe/axgbe_phy_impl.c | 387 ++++++++++++++++++++++++++++-
2 files changed, 427 insertions(+), 4 deletions(-)
diff --git a/drivers/net/axgbe/axgbe_phy.h b/drivers/net/axgbe/axgbe_phy.h
index eee3afc370..1bedd8e132 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,26 @@
#define MDIO_AN_10GBT_CTRL 32 /* 10GBASE-T auto-negotiation control */
#define MDIO_AN_10GBT_STAT 33 /* 10GBASE-T auto-negotiation status */
+#define M88E1512_E_PHY_ID 0x01410DD0
+#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_PHY_PAGE 22
+#define AXGBE_M88E1512_COPPER_PAGE 0x00
+#define AXGBE_M88E1512_LED_PAGE 0x03
+#define AXGBE_M88E1512_MODE_PAGE 0x12
+
+#define AXGBE_M88E1512_MODE_RGMII_COPPER 0
+#define AXGBE_M88E1512_MODE_SGMII_COPPER 1
+#define AXGBE_M88E1512_MODE_RGMII_1000BX 2
+#define AXGBE_M88E1512_MODE_RGMII_100BFX 3
+#define AXGBE_M88E1512_MODE_RGMII_SGMII 4
+#define AXGBE_M88E1512_MODE_SW_RESET 0x8000
+
+
/* Control register 1. */
/* Enable extended speed selection */
#define MDIO_CTRL1_SPEEDSELEXT (BMCR_SPEED1000 | BMCR_SPEED100)
@@ -190,5 +233,6 @@
#define DUPLEX_FULL 0x01
#define DUPLEX_UNKNOWN 0xff
+#define AXGBE_PHY_REVISION_MASK 0xfffffff0
#endif
/* PHY */
diff --git a/drivers/net/axgbe/axgbe_phy_impl.c b/drivers/net/axgbe/axgbe_phy_impl.c
index 5bee7fec7a..0c23feaea3 100644
--- a/drivers/net/axgbe/axgbe_phy_impl.c
+++ b/drivers/net/axgbe/axgbe_phy_impl.c
@@ -3,6 +3,7 @@
* Copyright(c) 2018 Synopsys, Inc. All rights reserved.
*/
+#include <rte_cycles.h>
#include "axgbe_ethdev.h"
#include "axgbe_common.h"
#include "axgbe_phy.h"
@@ -204,6 +205,7 @@ struct axgbe_phy_data {
unsigned int rrc_count;
uint8_t mdio_addr;
+ uint32_t phy_id;
/* SFP Support */
enum axgbe_sfp_comm sfp_comm;
@@ -253,6 +255,8 @@ static void axgbe_phy_rrc(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_get_ext_phy_link_status(struct axgbe_port *pdata,
+ bool *linkup);
static int axgbe_phy_i2c_xfer(struct axgbe_port *pdata,
struct axgbe_i2c_op *i2c_op)
@@ -297,6 +301,83 @@ static int axgbe_phy_write(struct axgbe_port *pdata, u16 reg, u16 value)
return ret;
}
+static int axgbe_phy_config_advert(struct axgbe_port *pdata)
+{
+ u16 advert;
+ int ret;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_ADVERTISE, &advert);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to read ADVERTISE register");
+ return ret;
+ }
+
+ advert |= ADVERTISE_FULL;
+ advert |= ADVERTISE_PAUSE_CAP;
+
+ ret = pdata->phy_if.phy_impl.write(pdata, MII_ADVERTISE, advert);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to write ADVERTISE register");
+ return ret;
+ }
+ return 0;
+}
+
+static int axgbe_phy_set_speed(struct axgbe_port *pdata)
+{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ u16 bmcr;
+ int ret;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMCR, &bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to read BMCR register");
+ return ret;
+ }
+
+ 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;
+ }
+
+ ret = pdata->phy_if.phy_impl.write(pdata, MII_BMCR, bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to write BMCR register");
+ return ret;
+ }
+ return 0;
+}
+
+static int axgbe_phy_config_aneg(struct axgbe_port *pdata)
+{
+ u16 bmcr;
+ int ret;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMCR, &bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to read BMCR register");
+ return ret;
+ }
+
+ bmcr &= ~(BMCR_ANENABLE);
+ if (pdata->phy.autoneg == AUTONEG_ENABLE)
+ bmcr |= BMCR_ANENABLE | BMCR_ANRESTART;
+
+ ret = pdata->phy_if.phy_impl.write(pdata, MII_BMCR, bmcr);
+
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to write BMCR register");
+ return ret;
+ }
+ return 0;
+}
static int axgbe_phy_redrv_write(struct axgbe_port *pdata, unsigned int reg,
unsigned int val)
@@ -1132,10 +1213,35 @@ static unsigned int axgbe_phy_an_advertising(struct axgbe_port *pdata)
static int axgbe_phy_an_config(struct axgbe_port *pdata __rte_unused)
{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ int ret;
+
+ /* Auto-negotiation config is implemented only for 1000BASE-T */
+ if (phy_data->port_mode != AXGBE_PORT_MODE_1000BASE_T)
+ return 0;
+
+ /* Supports only Marvell M88E1512 PHY for now*/
+ if (phy_data->phy_id == M88E1512_E_PHY_ID) {
+ ret = axgbe_phy_config_advert(pdata);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY ADVERTISE config failed");
+ return ret;
+ }
+
+ ret = axgbe_phy_set_speed(pdata);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY set speed failed");
+ return ret;
+ }
+
+ ret = axgbe_phy_config_aneg(pdata);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY AN config failed");
+ return ret;
+ }
+ PMD_DRV_LOG_LINE(DEBUG, "M88E1512 PHY AN config done");
+ }
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)
@@ -1883,7 +1989,8 @@ static bool axgbe_phy_use_mode(struct axgbe_port *pdata, enum axgbe_mode mode)
static int axgbe_phy_link_status(struct axgbe_port *pdata, int *an_restart)
{
struct axgbe_phy_data *phy_data = pdata->phy_data;
- unsigned int reg;
+ uint32_t reg;
+ bool ext_phy_link_up = false;
*an_restart = 0;
@@ -1903,6 +2010,26 @@ static int axgbe_phy_link_status(struct axgbe_port *pdata, int *an_restart)
}
}
+ if (phy_data->port_mode == AXGBE_PORT_MODE_1000BASE_T) {
+ if (phy_data->phy_id == M88E1512_E_PHY_ID) {
+ if (axgbe_get_ext_phy_link_status(pdata,
+ &ext_phy_link_up)) {
+ PMD_DRV_LOG_LINE(ERR,
+ "Get link status failed");
+ goto out;
+ }
+
+ if (ext_phy_link_up) {
+ 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
*/
@@ -1946,6 +2073,8 @@ static int axgbe_phy_link_status(struct axgbe_port *pdata, int *an_restart)
axgbe_phy_rrc(pdata);
}
+out:
+ pdata->rx_adapt_done = false;
return 0;
}
@@ -2303,6 +2432,241 @@ static int axgbe_phy_reset(struct axgbe_port *pdata)
return 0;
}
+static int axgbe_get_phy_id(struct axgbe_port *pdata)
+{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ u16 phy_id_1;
+ u16 phy_id_2;
+ int ret;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_PHYSID1,
+ &phy_id_1);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to read PHYSID1 register");
+ return ret;
+ }
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_PHYSID2,
+ &phy_id_2);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to read PHYSID2 register");
+ return ret;
+ }
+
+ phy_data->phy_id = (u32)(phy_id_1 << 16);
+ phy_data->phy_id |= phy_id_2;
+ phy_data->phy_id &= AXGBE_PHY_REVISION_MASK;
+
+ if (phy_data->phy_id == AXGBE_PHY_REVISION_MASK) {
+ PMD_DRV_LOG_LINE(ERR, "Invalid PHY id (0x%x)",
+ phy_data->phy_id);
+ return -EINVAL;
+ }
+ PMD_DRV_LOG_LINE(DEBUG, "PHY id (0x%x)", phy_data->phy_id);
+ return 0;
+}
+
+static int axgbe_phy_poll_reset(struct axgbe_port *pdata)
+{
+ const uint64_t timeout_ms = 2000;
+ const uint64_t interval_ms = 1;
+ const uint64_t hz = rte_get_timer_hz();
+ const uint64_t start = rte_get_timer_cycles();
+
+ u16 bmcr = 0;
+ int ret;
+
+ for (;;) {
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMCR, &bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY read BMCR failed");
+ return ret;
+ }
+ if (!(bmcr & BMCR_RESET))
+ return 0;
+
+ uint64_t elapsed_ms =
+ ((rte_get_timer_cycles() - start) * 1000) / hz;
+ if (elapsed_ms >= timeout_ms)
+ break;
+
+ rte_delay_us_block(interval_ms * 1000);
+ }
+ return -ETIMEDOUT;
+}
+
+
+static int axgbe_phy_soft_reset(struct axgbe_port *pdata)
+{
+ int ret;
+ u16 bmcr;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMCR, &bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to read BMCR register");
+ return ret;
+ }
+
+ bmcr |= BMCR_RESET;
+
+ ret = pdata->phy_if.phy_impl.write(pdata, MII_BMCR, bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to write BMCR register");
+ return ret;
+ }
+
+ ret = axgbe_phy_poll_reset(pdata);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Poll for PHY reset done failed");
+ return ret;
+ }
+ return 0;
+}
+
+static int axgbe_m88e1512_set_page(struct axgbe_port *pdata, u16 page)
+{
+ int ret;
+
+ ret = pdata->phy_if.phy_impl.write(pdata,
+ AXGBE_M88E1512_PAGE_ADDR, page);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR,
+ "PHY set page failed for page 0x%x", page);
+ }
+ return ret;
+}
+
+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)
+{
+ int ret;
+ u8 i;
+
+ PMD_DRV_LOG_LINE(DEBUG, "Initialize M88E1512 phy");
+
+ /* Switch to PHY page 0xFF */
+ ret = axgbe_m88e1512_set_page(pdata, 0xff);
+ if (ret)
+ goto err_out;
+
+ /* Configure M88E1512 errata registers */
+ for (i = 0; i < ARRAY_SIZE(errata_vals); i++) {
+ ret = pdata->phy_if.phy_impl.write(pdata,
+ AXGBE_M88E1512_CFG_REG_2,
+ errata_vals[i].reg17);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Config M88E1512 errata failed");
+ goto err_set_copper_page;
+ }
+
+ ret = pdata->phy_if.phy_impl.write(pdata,
+ AXGBE_M88E1512_CFG_REG_1,
+ errata_vals[i].reg16);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Config M88E1512 errata failed");
+ goto err_set_copper_page;
+ }
+ }
+
+ /* Switch to PHY page 0xFB */
+ ret = axgbe_m88e1512_set_page(pdata, 0xfb);
+ if (ret)
+ goto err_set_copper_page;
+
+ ret = pdata->phy_if.phy_impl.write(pdata,
+ AXGBE_M88E1512_CFG_REG_3, 0xC00D);
+ if (ret)
+ goto err_set_copper_page;
+
+ /* Switch to PHY copper page */
+ ret = axgbe_m88e1512_set_page(pdata, AXGBE_M88E1512_COPPER_PAGE);
+ if (ret)
+ goto err_out;
+
+ /* SGMII-to-Copper mode initialization */
+
+ /* Switch to PHY mode page */
+ ret = axgbe_m88e1512_set_page(pdata, AXGBE_M88E1512_MODE_PAGE);
+ if (ret)
+ goto err_set_copper_page;
+
+ ret = pdata->phy_if.phy_impl.write(pdata,
+ AXGBE_M88E1512_MODE,
+ (AXGBE_M88E1512_MODE_SW_RESET |
+ AXGBE_M88E1512_MODE_SGMII_COPPER));
+ if (ret)
+ goto err_set_copper_page;
+
+ /* Switch to PHY copper page */
+ ret = axgbe_m88e1512_set_page(pdata, AXGBE_M88E1512_COPPER_PAGE);
+ if (ret)
+ goto err_out;
+
+ ret = axgbe_phy_soft_reset(pdata);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "M88E1512 PHY soft reset failed");
+ goto err_out;
+ }
+
+ PMD_DRV_LOG_LINE(DEBUG, "M88E1512 phy init done");
+ return 0;
+
+err_set_copper_page:
+ /* Switch to PHY page 0 */
+ axgbe_m88e1512_set_page(pdata, AXGBE_M88E1512_COPPER_PAGE);
+err_out:
+ PMD_DRV_LOG_LINE(ERR, "M88E1512 PHY initialization failed");
+ return ret;
+}
+
+
+static int axgbe_get_ext_phy_link_status(struct axgbe_port *pdata, bool *linkup)
+{
+ int ret;
+ u16 bmcr;
+ u16 bmsr;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMCR, &bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY read failed for BMCR register");
+ return ret;
+ }
+
+ /* Autoneg is being started, therefore disregard BMSR value and
+ * report link as down.
+ */
+ if (bmcr & BMCR_ANRESTART)
+ return 0;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMSR, &bmsr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY read failed for BMSR register");
+ return ret;
+ }
+
+ if (bmsr & BMSR_LSTATUS)
+ goto done;
+
+ /* Read link status */
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMSR, &bmsr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY read failed for BMSR register");
+ return ret;
+ }
+done:
+ *linkup = (bmsr & BMSR_LSTATUS) ? true : false;
+ return 0;
+}
+
+
static int axgbe_phy_init(struct axgbe_port *pdata)
{
struct axgbe_phy_data *phy_data;
@@ -2556,6 +2920,21 @@ static int axgbe_phy_init(struct axgbe_port *pdata)
}
phy_data->phy_cdr_delay = AXGBE_CDR_DELAY_INIT;
+
+ ret = axgbe_get_phy_id(pdata);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "failed to get PHY id");
+ return ret;
+ }
+
+ PMD_DRV_LOG_LINE(DEBUG, "PHY ID = 0x%x", phy_data->phy_id);
+
+ if (phy_data->phy_id == M88E1512_E_PHY_ID) {
+ ret = axgbe_m88e1512_init(pdata);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
void axgbe_init_function_ptrs_phy_v2(struct axgbe_phy_if *phy_if)
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v2 3/3] net/axgbe: add support for 100Mbps link speed
2026-02-16 12:52 ` [PATCH 3/3] net/axgbe: Add support for 100Mbps link speed Ashok Kumar Natarajan
@ 2026-02-25 12:49 ` Ashok Kumar Natarajan
2026-02-26 16:49 ` [PATCH v3 3/3] net/axgbe: fix 100M SGMII mode Ashok Kumar Natarajan
0 siblings, 1 reply; 23+ messages in thread
From: Ashok Kumar Natarajan @ 2026-02-25 12:49 UTC (permalink / raw)
To: dev; +Cc: Selwin.Sebastian, Ashok Kumar Natarajan
Add 100Mbps link speed support to the axgbe driver. This update enables
the MAC and PHY configuration required to operate at 100M, including
proper mode selection and link handling.
Signed-off-by: Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com>
---
drivers/net/axgbe/axgbe_mdio.c | 4 ++--
drivers/net/axgbe/axgbe_phy_impl.c | 15 +++++++++++++++
2 files changed, 17 insertions(+), 2 deletions(-)
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_impl.c b/drivers/net/axgbe/axgbe_phy_impl.c
index 0c23feaea3..e6a9dd39a9 100644
--- a/drivers/net/axgbe/axgbe_phy_impl.c
+++ b/drivers/net/axgbe/axgbe_phy_impl.c
@@ -1643,6 +1643,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;
@@ -1840,6 +1852,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;
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH v2 1/3] net/axgbe: add external PHY read/write functions
2026-02-25 12:44 ` [PATCH v2 1/3] net/axgbe: add " Ashok Kumar Natarajan
@ 2026-02-25 23:09 ` Stephen Hemminger
2026-02-26 17:09 ` Natarajan, Ashok Kumar
2026-02-26 16:46 ` [PATCH v3 " Ashok Kumar Natarajan
1 sibling, 1 reply; 23+ messages in thread
From: Stephen Hemminger @ 2026-02-25 23:09 UTC (permalink / raw)
To: Ashok Kumar Natarajan; +Cc: dev, Selwin.Sebastian
On Wed, 25 Feb 2026 18:14:54 +0530
Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com> wrote:
> Introduce helper functions to perform external PHY register read and
> write operations. These helpers currently support only IEEE Clause 22
> PHY access, providing a simple and consistent API for accessing
> standard 16‑bit MII registers on external PHY devices.
>
> Signed-off-by: Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com>
> ---
I didn't see anything here, but AI spotted some things.
Please revise and resubmit.
Thanks for the series. A few findings, mostly in patch 2:
Patch 2 (M88E1512 support):
1. axgbe_phy_link_status() always returns 0 (link up) even when the
M88E1512 reports link down or the read fails. The goto out path leads
to return 0, which means "link up" in this function's contract. The
link-down and error paths need to return a non-zero value.
2. Integer shift UB in axgbe_get_phy_id(): (u32)(phy_id_1 << 16) evaluates the
shift at int width before widening. If bit 15 of phy_id_1 is set,
shifting into the sign bit is undefined behavior. Move the cast before
the shift: (u32)phy_id_1 << 16.
3. axgbe_get_ext_phy_link_status() doesn't
set *linkup = false on the BMCR_ANRESTART early-return path. Works
today because the caller pre-initializes the variable, but the function
should be self-contained.
4. The new out label unconditionally clears
rx_adapt_done on the M88E1512 link-down path — please verify this is
intentional and doesn't interfere with the internal SerDes receiver
adaptation state machine.
Patch 3 (100Mbps):
5. The SPEED_1000 → SPEED_100 change in axgbe_sgmii_100_mode() is a bug fix in existing code — the 100M mode function was incorrectly configuring the MAC for 1G. This should have its own Fixes: tag and Cc: stable@dpdk.org so it gets backported to stable branches.
Item 1 is the most critical — it inverts link status reporting for the M88E1512.
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v3 1/3] net/axgbe: add external PHY read/write functions
2026-02-25 12:44 ` [PATCH v2 1/3] net/axgbe: add " Ashok Kumar Natarajan
2026-02-25 23:09 ` Stephen Hemminger
@ 2026-02-26 16:46 ` Ashok Kumar Natarajan
2026-02-26 19:09 ` Stephen Hemminger
2026-02-27 8:43 ` [PATCH v4 " Ashok Kumar Natarajan
1 sibling, 2 replies; 23+ messages in thread
From: Ashok Kumar Natarajan @ 2026-02-26 16:46 UTC (permalink / raw)
To: dev; +Cc: Selwin.Sebastian, Ashok Kumar Natarajan
Introduce helper functions to perform external PHY register read and
write operations. These helpers currently support only IEEE Clause 22
PHY access, providing a simple and consistent API for accessing
standard 16‑bit MII registers on external PHY devices.
Signed-off-by: Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com>
---
.mailmap | 1 +
drivers/net/axgbe/axgbe_dev.c | 56 ++++++++++++++++--------------
drivers/net/axgbe/axgbe_ethdev.h | 16 ++++++---
drivers/net/axgbe/axgbe_phy_impl.c | 45 +++++++++++++++++++++++-
4 files changed, 85 insertions(+), 33 deletions(-)
diff --git a/.mailmap b/.mailmap
index 6c4c977dde..5c8b47f1a9 100644
--- a/.mailmap
+++ b/.mailmap
@@ -165,6 +165,7 @@ Ashish Paul <apaul@juniper.net>
Ashish Sadanandan <ashish.sadanandan@gmail.com>
Ashish Shah <ashish.n.shah@intel.com>
Ashok Kaladi <ashok.k.kaladi@intel.com>
+Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com>
Ashwin Sekhar T K <asekhar@marvell.com> <ashwin.sekhar@caviumnetworks.com>
Asim Jamshed <asim.jamshed@gmail.com>
Atul Patel <atul.patel@intel.com>
diff --git a/drivers/net/axgbe/axgbe_dev.c b/drivers/net/axgbe/axgbe_dev.c
index 634d4ee4a5..10a99aeac2 100644
--- a/drivers/net/axgbe/axgbe_dev.c
+++ b/drivers/net/axgbe/axgbe_dev.c
@@ -63,9 +63,9 @@ static int mdio_complete(struct axgbe_port *pdata)
return 0;
}
-static unsigned int axgbe_create_mdio_sca_c22(int port, int reg)
+static unsigned int axgbe_create_mdio_sca_c22(u8 port, u16 reg)
{
- unsigned int mdio_sca;
+ u32 mdio_sca;
mdio_sca = 0;
AXGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, RA, reg);
@@ -74,20 +74,20 @@ static unsigned int axgbe_create_mdio_sca_c22(int port, int reg)
return mdio_sca;
}
-static unsigned int axgbe_create_mdio_sca_c45(int port, unsigned int da, int reg)
+static unsigned int axgbe_create_mdio_sca_c45(u8 port, u8 dev_addr, u16 reg)
{
- unsigned int mdio_sca;
+ u32 mdio_sca;
mdio_sca = 0;
AXGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, RA, reg);
AXGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, PA, port);
- AXGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, DA, da);
+ AXGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, DA, dev_addr);
return mdio_sca;
}
static int axgbe_write_ext_mii_regs(struct axgbe_port *pdata,
- unsigned int mdio_sca, u16 val)
+ u32 mdio_sca, u16 value)
{
unsigned int mdio_sccd;
uint64_t timeout;
@@ -95,7 +95,7 @@ static int axgbe_write_ext_mii_regs(struct axgbe_port *pdata,
AXGMAC_IOWRITE(pdata, MAC_MDIOSCAR, mdio_sca);
mdio_sccd = 0;
- AXGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, DATA, val);
+ AXGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, DATA, value);
AXGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, CMD, 1);
AXGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, BUSY, 1);
AXGMAC_IOWRITE(pdata, MAC_MDIOSCCDR, mdio_sccd);
@@ -113,28 +113,28 @@ static int axgbe_write_ext_mii_regs(struct axgbe_port *pdata,
static int axgbe_write_ext_mii_regs_c22(struct axgbe_port *pdata,
- int addr, int reg, u16 val)
+ u8 phy_addr, u16 reg, u16 value)
{
- unsigned int mdio_sca;
+ u32 mdio_sca;
- mdio_sca = axgbe_create_mdio_sca_c22(addr, reg);
+ mdio_sca = axgbe_create_mdio_sca_c22(phy_addr, reg);
- return axgbe_write_ext_mii_regs(pdata, mdio_sca, val);
+ return axgbe_write_ext_mii_regs(pdata, mdio_sca, value);
}
static int axgbe_write_ext_mii_regs_c45(struct axgbe_port *pdata,
- int addr, int devad, int reg, u16 val)
+ u8 phy_addr, u8 dev_addr, u16 reg, u16 value)
{
- unsigned int mdio_sca;
+ u32 mdio_sca;
- mdio_sca = axgbe_create_mdio_sca_c45(addr, devad, reg);
+ mdio_sca = axgbe_create_mdio_sca_c45(phy_addr, dev_addr, reg);
- return axgbe_write_ext_mii_regs(pdata, mdio_sca, val);
+ return axgbe_write_ext_mii_regs(pdata, mdio_sca, value);
}
-static int axgbe_read_ext_mii_regs(struct axgbe_port *pdata,
- unsigned int mdio_sca)
+static int axgbe_read_ext_mii_regs(struct axgbe_port *pdata, u32 mdio_sca,
+ u16 *value)
{
unsigned int mdio_sccd;
uint64_t timeout;
@@ -158,26 +158,28 @@ static int axgbe_read_ext_mii_regs(struct axgbe_port *pdata,
return -ETIMEDOUT;
success:
- return AXGMAC_IOREAD_BITS(pdata, MAC_MDIOSCCDR, DATA);
+ *value = AXGMAC_IOREAD_BITS(pdata, MAC_MDIOSCCDR, DATA);
+ return 0;
}
-static int axgbe_read_ext_mii_regs_c22(struct axgbe_port *pdata, int addr, int reg)
+static int axgbe_read_ext_mii_regs_c22(struct axgbe_port *pdata, u8 phy_addr,
+ u16 reg, u16 *value)
{
- unsigned int mdio_sca;
+ u32 mdio_sca;
- mdio_sca = axgbe_create_mdio_sca_c22(addr, reg);
+ mdio_sca = axgbe_create_mdio_sca_c22(phy_addr, reg);
- return axgbe_read_ext_mii_regs(pdata, mdio_sca);
+ return axgbe_read_ext_mii_regs(pdata, mdio_sca, value);
}
-static int axgbe_read_ext_mii_regs_c45(struct axgbe_port *pdata, int addr,
- int devad, int reg)
+static int axgbe_read_ext_mii_regs_c45(struct axgbe_port *pdata, u8 phy_addr,
+ u8 dev_addr, u16 reg, u16 *value)
{
- unsigned int mdio_sca;
+ u32 mdio_sca;
- mdio_sca = axgbe_create_mdio_sca_c45(addr, devad, reg);
+ mdio_sca = axgbe_create_mdio_sca_c45(phy_addr, dev_addr, reg);
- return axgbe_read_ext_mii_regs(pdata, mdio_sca);
+ return axgbe_read_ext_mii_regs(pdata, mdio_sca, value);
}
static int axgbe_set_ext_mii_mode(struct axgbe_port *pdata, unsigned int port,
diff --git a/drivers/net/axgbe/axgbe_ethdev.h b/drivers/net/axgbe/axgbe_ethdev.h
index b94a7f3562..24336f31f6 100644
--- a/drivers/net/axgbe/axgbe_ethdev.h
+++ b/drivers/net/axgbe/axgbe_ethdev.h
@@ -327,11 +327,14 @@ struct axgbe_hw_if {
int (*set_ext_mii_mode)(struct axgbe_port *, unsigned int,
enum axgbe_mdio_mode);
- int (*read_ext_mii_regs_c22)(struct axgbe_port *pdata, int addr, int reg);
- int (*write_ext_mii_regs_c22)(struct axgbe_port *pdata, int addr, int reg, uint16_t val);
- int (*read_ext_mii_regs_c45)(struct axgbe_port *pdata, int addr, int devad, int reg);
- int (*write_ext_mii_regs_c45)(struct axgbe_port *pdata, int addr, int devad,
- int reg, uint16_t val);
+ int (*read_ext_mii_regs_c22)(struct axgbe_port *pdata, u8 phy_addr,
+ u16 reg, u16 *value);
+ int (*write_ext_mii_regs_c22)(struct axgbe_port *pdata, u8 phy_addr,
+ u16 reg, u16 value);
+ int (*read_ext_mii_regs_c45)(struct axgbe_port *pdata, u8 phy_addr,
+ u8 dev_addr, u16 reg, u16 *value);
+ int (*write_ext_mii_regs_c45)(struct axgbe_port *pdata, u8 phy_addr,
+ u8 dev_addr, u16 reg, u16 value);
/* For FLOW ctrl */
int (*config_tx_flow_control)(struct axgbe_port *);
@@ -398,6 +401,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 *pdata, u16 reg, u16 *value);
+ int (*write)(struct axgbe_port *pdata, u16 reg, u16 value);
};
struct axgbe_phy_if {
diff --git a/drivers/net/axgbe/axgbe_phy_impl.c b/drivers/net/axgbe/axgbe_phy_impl.c
index 9249e11335..5bee7fec7a 100644
--- a/drivers/net/axgbe/axgbe_phy_impl.c
+++ b/drivers/net/axgbe/axgbe_phy_impl.c
@@ -203,7 +203,7 @@ struct axgbe_phy_data {
unsigned int rrc_count;
- unsigned int mdio_addr;
+ uint8_t mdio_addr;
/* SFP Support */
enum axgbe_sfp_comm sfp_comm;
@@ -251,6 +251,8 @@ 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_phy_get_comm_ownership(struct axgbe_port *pdata);
+static void axgbe_phy_put_comm_ownership(struct axgbe_port *pdata);
static int axgbe_phy_i2c_xfer(struct axgbe_port *pdata,
struct axgbe_i2c_op *i2c_op)
@@ -258,6 +260,44 @@ static int axgbe_phy_i2c_xfer(struct axgbe_port *pdata,
return pdata->i2c_if.i2c_xfer(pdata, i2c_op);
}
+static int axgbe_phy_read(struct axgbe_port *pdata, u16 reg, u16 *value)
+{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ int ret;
+
+ ret = axgbe_phy_get_comm_ownership(pdata);
+ if (ret)
+ return ret;
+
+ ret = pdata->hw_if.read_ext_mii_regs_c22(pdata,
+ phy_data->mdio_addr, reg, value);
+ if (ret)
+ PMD_DRV_LOG_LINE(ERR, "mdio read failed %s",
+ strerror(ret));
+
+ axgbe_phy_put_comm_ownership(pdata);
+ return ret;
+}
+
+static int axgbe_phy_write(struct axgbe_port *pdata, u16 reg, u16 value)
+{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ int ret;
+ ret = axgbe_phy_get_comm_ownership(pdata);
+ if (ret)
+ return ret;
+
+ ret = pdata->hw_if.write_ext_mii_regs_c22(pdata,
+ phy_data->mdio_addr, reg, value);
+ if (ret)
+ PMD_DRV_LOG_LINE(ERR, "mdio write failed %s",
+ strerror(ret));
+
+ axgbe_phy_put_comm_ownership(pdata);
+ return ret;
+}
+
+
static int axgbe_phy_redrv_write(struct axgbe_port *pdata, unsigned int reg,
unsigned int val)
{
@@ -2542,4 +2582,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_read;
+ phy_impl->write = axgbe_phy_write;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v3 2/3] net/axgbe: add support for marvell m88e1512 PHY
2026-02-25 12:47 ` [PATCH v2 2/3] net/axgbe: add support for marvell m88e1512 PHY Ashok Kumar Natarajan
@ 2026-02-26 16:47 ` Ashok Kumar Natarajan
2026-02-27 8:44 ` [PATCH v4 " Ashok Kumar Natarajan
0 siblings, 1 reply; 23+ messages in thread
From: Ashok Kumar Natarajan @ 2026-02-26 16:47 UTC (permalink / raw)
To: dev; +Cc: Selwin.Sebastian, Ashok Kumar Natarajan
Add support for the Marvell M88E1512 PHY in the axgbe driver. This patch
implements device identification and basic initialization required to
operate with this PHY. Only the M88E1512 model is supported in this
update.
Signed-off-by: Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com>
---
drivers/net/axgbe/axgbe_phy.h | 44 ++++
drivers/net/axgbe/axgbe_phy_impl.c | 389 ++++++++++++++++++++++++++++-
2 files changed, 429 insertions(+), 4 deletions(-)
diff --git a/drivers/net/axgbe/axgbe_phy.h b/drivers/net/axgbe/axgbe_phy.h
index eee3afc370..1bedd8e132 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,26 @@
#define MDIO_AN_10GBT_CTRL 32 /* 10GBASE-T auto-negotiation control */
#define MDIO_AN_10GBT_STAT 33 /* 10GBASE-T auto-negotiation status */
+#define M88E1512_E_PHY_ID 0x01410DD0
+#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_PHY_PAGE 22
+#define AXGBE_M88E1512_COPPER_PAGE 0x00
+#define AXGBE_M88E1512_LED_PAGE 0x03
+#define AXGBE_M88E1512_MODE_PAGE 0x12
+
+#define AXGBE_M88E1512_MODE_RGMII_COPPER 0
+#define AXGBE_M88E1512_MODE_SGMII_COPPER 1
+#define AXGBE_M88E1512_MODE_RGMII_1000BX 2
+#define AXGBE_M88E1512_MODE_RGMII_100BFX 3
+#define AXGBE_M88E1512_MODE_RGMII_SGMII 4
+#define AXGBE_M88E1512_MODE_SW_RESET 0x8000
+
+
/* Control register 1. */
/* Enable extended speed selection */
#define MDIO_CTRL1_SPEEDSELEXT (BMCR_SPEED1000 | BMCR_SPEED100)
@@ -190,5 +233,6 @@
#define DUPLEX_FULL 0x01
#define DUPLEX_UNKNOWN 0xff
+#define AXGBE_PHY_REVISION_MASK 0xfffffff0
#endif
/* PHY */
diff --git a/drivers/net/axgbe/axgbe_phy_impl.c b/drivers/net/axgbe/axgbe_phy_impl.c
index 5bee7fec7a..8635a53a6d 100644
--- a/drivers/net/axgbe/axgbe_phy_impl.c
+++ b/drivers/net/axgbe/axgbe_phy_impl.c
@@ -3,6 +3,7 @@
* Copyright(c) 2018 Synopsys, Inc. All rights reserved.
*/
+#include <rte_cycles.h>
#include "axgbe_ethdev.h"
#include "axgbe_common.h"
#include "axgbe_phy.h"
@@ -204,6 +205,7 @@ struct axgbe_phy_data {
unsigned int rrc_count;
uint8_t mdio_addr;
+ uint32_t phy_id;
/* SFP Support */
enum axgbe_sfp_comm sfp_comm;
@@ -253,6 +255,8 @@ static void axgbe_phy_rrc(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_get_ext_phy_link_status(struct axgbe_port *pdata,
+ bool *linkup);
static int axgbe_phy_i2c_xfer(struct axgbe_port *pdata,
struct axgbe_i2c_op *i2c_op)
@@ -297,6 +301,83 @@ static int axgbe_phy_write(struct axgbe_port *pdata, u16 reg, u16 value)
return ret;
}
+static int axgbe_phy_config_advert(struct axgbe_port *pdata)
+{
+ u16 advert;
+ int ret;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_ADVERTISE, &advert);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to read ADVERTISE register");
+ return ret;
+ }
+
+ advert |= ADVERTISE_FULL;
+ advert |= ADVERTISE_PAUSE_CAP;
+
+ ret = pdata->phy_if.phy_impl.write(pdata, MII_ADVERTISE, advert);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to write ADVERTISE register");
+ return ret;
+ }
+ return 0;
+}
+
+static int axgbe_phy_set_speed(struct axgbe_port *pdata)
+{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ u16 bmcr;
+ int ret;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMCR, &bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to read BMCR register");
+ return ret;
+ }
+
+ 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;
+ }
+
+ ret = pdata->phy_if.phy_impl.write(pdata, MII_BMCR, bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to write BMCR register");
+ return ret;
+ }
+ return 0;
+}
+
+static int axgbe_phy_config_aneg(struct axgbe_port *pdata)
+{
+ u16 bmcr;
+ int ret;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMCR, &bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to read BMCR register");
+ return ret;
+ }
+
+ bmcr &= ~(BMCR_ANENABLE);
+ if (pdata->phy.autoneg == AUTONEG_ENABLE)
+ bmcr |= BMCR_ANENABLE | BMCR_ANRESTART;
+
+ ret = pdata->phy_if.phy_impl.write(pdata, MII_BMCR, bmcr);
+
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to write BMCR register");
+ return ret;
+ }
+ return 0;
+}
static int axgbe_phy_redrv_write(struct axgbe_port *pdata, unsigned int reg,
unsigned int val)
@@ -1132,10 +1213,35 @@ static unsigned int axgbe_phy_an_advertising(struct axgbe_port *pdata)
static int axgbe_phy_an_config(struct axgbe_port *pdata __rte_unused)
{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ int ret;
+
+ /* Auto-negotiation config is implemented only for 1000BASE-T */
+ if (phy_data->port_mode != AXGBE_PORT_MODE_1000BASE_T)
+ return 0;
+
+ /* Supports only Marvell M88E1512 PHY for now*/
+ if (phy_data->phy_id == M88E1512_E_PHY_ID) {
+ ret = axgbe_phy_config_advert(pdata);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY ADVERTISE config failed");
+ return ret;
+ }
+
+ ret = axgbe_phy_set_speed(pdata);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY set speed failed");
+ return ret;
+ }
+
+ ret = axgbe_phy_config_aneg(pdata);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY AN config failed");
+ return ret;
+ }
+ PMD_DRV_LOG_LINE(DEBUG, "M88E1512 PHY AN config done");
+ }
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)
@@ -1880,10 +1986,12 @@ static bool axgbe_phy_use_mode(struct axgbe_port *pdata, enum axgbe_mode mode)
}
}
+/* return 0 means link down, 1 means link up */
static int axgbe_phy_link_status(struct axgbe_port *pdata, int *an_restart)
{
struct axgbe_phy_data *phy_data = pdata->phy_data;
- unsigned int reg;
+ uint32_t reg;
+ bool ext_phy_link_up = false;
*an_restart = 0;
@@ -1903,6 +2011,26 @@ static int axgbe_phy_link_status(struct axgbe_port *pdata, int *an_restart)
}
}
+ if (phy_data->port_mode == AXGBE_PORT_MODE_1000BASE_T) {
+ if (phy_data->phy_id == M88E1512_E_PHY_ID) {
+ if (axgbe_get_ext_phy_link_status(pdata,
+ &ext_phy_link_up)) {
+ PMD_DRV_LOG_LINE(ERR,
+ "Get link status failed");
+ goto out;
+ }
+
+ if (ext_phy_link_up) {
+ 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
*/
@@ -1946,6 +2074,7 @@ static int axgbe_phy_link_status(struct axgbe_port *pdata, int *an_restart)
axgbe_phy_rrc(pdata);
}
+out:
return 0;
}
@@ -2303,6 +2432,243 @@ static int axgbe_phy_reset(struct axgbe_port *pdata)
return 0;
}
+static int axgbe_get_phy_id(struct axgbe_port *pdata)
+{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ u16 phy_id_1;
+ u16 phy_id_2;
+ int ret;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_PHYSID1,
+ &phy_id_1);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to read PHYSID1 register");
+ return ret;
+ }
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_PHYSID2,
+ &phy_id_2);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to read PHYSID2 register");
+ return ret;
+ }
+
+ phy_data->phy_id = (u32)phy_id_1 << 16;
+ phy_data->phy_id |= phy_id_2;
+ phy_data->phy_id &= AXGBE_PHY_REVISION_MASK;
+
+ if (phy_data->phy_id == AXGBE_PHY_REVISION_MASK) {
+ PMD_DRV_LOG_LINE(ERR, "Invalid PHY id (0x%x)",
+ phy_data->phy_id);
+ return -EINVAL;
+ }
+ PMD_DRV_LOG_LINE(DEBUG, "PHY id (0x%x)", phy_data->phy_id);
+ return 0;
+}
+
+static int axgbe_phy_poll_reset(struct axgbe_port *pdata)
+{
+ const uint64_t timeout_ms = 2000;
+ const uint64_t interval_ms = 1;
+ const uint64_t hz = rte_get_timer_hz();
+ const uint64_t start = rte_get_timer_cycles();
+
+ u16 bmcr = 0;
+ int ret;
+
+ for (;;) {
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMCR, &bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY read BMCR failed");
+ return ret;
+ }
+ if (!(bmcr & BMCR_RESET))
+ return 0;
+
+ uint64_t elapsed_ms =
+ ((rte_get_timer_cycles() - start) * 1000) / hz;
+ if (elapsed_ms >= timeout_ms)
+ break;
+
+ rte_delay_us_block(interval_ms * 1000);
+ }
+ return -ETIMEDOUT;
+}
+
+
+static int axgbe_phy_soft_reset(struct axgbe_port *pdata)
+{
+ int ret;
+ u16 bmcr;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMCR, &bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to read BMCR register");
+ return ret;
+ }
+
+ bmcr |= BMCR_RESET;
+
+ ret = pdata->phy_if.phy_impl.write(pdata, MII_BMCR, bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to write BMCR register");
+ return ret;
+ }
+
+ ret = axgbe_phy_poll_reset(pdata);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Poll for PHY reset done failed");
+ return ret;
+ }
+ return 0;
+}
+
+static int axgbe_m88e1512_set_page(struct axgbe_port *pdata, u16 page)
+{
+ int ret;
+
+ ret = pdata->phy_if.phy_impl.write(pdata,
+ AXGBE_M88E1512_PAGE_ADDR, page);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR,
+ "PHY set page failed for page 0x%x", page);
+ }
+ return ret;
+}
+
+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)
+{
+ int ret;
+ u8 i;
+
+ PMD_DRV_LOG_LINE(DEBUG, "Initialize M88E1512 phy");
+
+ /* Switch to PHY page 0xFF */
+ ret = axgbe_m88e1512_set_page(pdata, 0xff);
+ if (ret)
+ goto err_out;
+
+ /* Configure M88E1512 errata registers */
+ for (i = 0; i < ARRAY_SIZE(errata_vals); i++) {
+ ret = pdata->phy_if.phy_impl.write(pdata,
+ AXGBE_M88E1512_CFG_REG_2,
+ errata_vals[i].reg17);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Config M88E1512 errata failed");
+ goto err_set_copper_page;
+ }
+
+ ret = pdata->phy_if.phy_impl.write(pdata,
+ AXGBE_M88E1512_CFG_REG_1,
+ errata_vals[i].reg16);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Config M88E1512 errata failed");
+ goto err_set_copper_page;
+ }
+ }
+
+ /* Switch to PHY page 0xFB */
+ ret = axgbe_m88e1512_set_page(pdata, 0xfb);
+ if (ret)
+ goto err_set_copper_page;
+
+ ret = pdata->phy_if.phy_impl.write(pdata,
+ AXGBE_M88E1512_CFG_REG_3, 0xC00D);
+ if (ret)
+ goto err_set_copper_page;
+
+ /* Switch to PHY copper page */
+ ret = axgbe_m88e1512_set_page(pdata, AXGBE_M88E1512_COPPER_PAGE);
+ if (ret)
+ goto err_out;
+
+ /* SGMII-to-Copper mode initialization */
+
+ /* Switch to PHY mode page */
+ ret = axgbe_m88e1512_set_page(pdata, AXGBE_M88E1512_MODE_PAGE);
+ if (ret)
+ goto err_set_copper_page;
+
+ ret = pdata->phy_if.phy_impl.write(pdata,
+ AXGBE_M88E1512_MODE,
+ (AXGBE_M88E1512_MODE_SW_RESET |
+ AXGBE_M88E1512_MODE_SGMII_COPPER));
+ if (ret)
+ goto err_set_copper_page;
+
+ /* Switch to PHY copper page */
+ ret = axgbe_m88e1512_set_page(pdata, AXGBE_M88E1512_COPPER_PAGE);
+ if (ret)
+ goto err_out;
+
+ ret = axgbe_phy_soft_reset(pdata);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "M88E1512 PHY soft reset failed");
+ goto err_out;
+ }
+
+ PMD_DRV_LOG_LINE(DEBUG, "M88E1512 phy init done");
+ return 0;
+
+err_set_copper_page:
+ /* Switch to PHY page 0 */
+ axgbe_m88e1512_set_page(pdata, AXGBE_M88E1512_COPPER_PAGE);
+err_out:
+ PMD_DRV_LOG_LINE(ERR, "M88E1512 PHY initialization failed");
+ return ret;
+}
+
+
+static int axgbe_get_ext_phy_link_status(struct axgbe_port *pdata, bool *linkup)
+{
+ int ret;
+ u16 bmcr;
+ u16 bmsr;
+
+ *linkup = false;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMCR, &bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY read failed for BMCR register");
+ return ret;
+ }
+
+ /* Autoneg is being started, therefore disregard BMSR value and
+ * report link as down.
+ */
+ if (bmcr & BMCR_ANRESTART)
+ return 0;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMSR, &bmsr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY read failed for BMSR register");
+ return ret;
+ }
+
+ if (bmsr & BMSR_LSTATUS)
+ goto done;
+
+ /* Read link status */
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMSR, &bmsr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY read failed for BMSR register");
+ return ret;
+ }
+done:
+ *linkup = (bmsr & BMSR_LSTATUS) ? true : false;
+ return 0;
+}
+
+
static int axgbe_phy_init(struct axgbe_port *pdata)
{
struct axgbe_phy_data *phy_data;
@@ -2556,6 +2922,21 @@ static int axgbe_phy_init(struct axgbe_port *pdata)
}
phy_data->phy_cdr_delay = AXGBE_CDR_DELAY_INIT;
+
+ ret = axgbe_get_phy_id(pdata);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "failed to get PHY id");
+ return ret;
+ }
+
+ PMD_DRV_LOG_LINE(DEBUG, "PHY ID = 0x%x", phy_data->phy_id);
+
+ if (phy_data->phy_id == M88E1512_E_PHY_ID) {
+ ret = axgbe_m88e1512_init(pdata);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
void axgbe_init_function_ptrs_phy_v2(struct axgbe_phy_if *phy_if)
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v3 3/3] net/axgbe: fix 100M SGMII mode
2026-02-25 12:49 ` [PATCH v2 3/3] net/axgbe: add " Ashok Kumar Natarajan
@ 2026-02-26 16:49 ` Ashok Kumar Natarajan
2026-02-27 8:45 ` [PATCH v4 " Ashok Kumar Natarajan
0 siblings, 1 reply; 23+ messages in thread
From: Ashok Kumar Natarajan @ 2026-02-26 16:49 UTC (permalink / raw)
To: dev; +Cc: Selwin.Sebastian, Ashok Kumar Natarajan, stable
In SGMII 100M mode, set MAC speed to 100M instead of 1G,
and add the PHY implementation for SGMII 100 mode.
Fixes: a5c7273771e8 ("net/axgbe: add phy programming APIs")
Cc: stable@dpdk.org
Signed-off-by: Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com>
---
drivers/net/axgbe/axgbe_mdio.c | 4 ++--
drivers/net/axgbe/axgbe_phy_impl.c | 15 +++++++++++++++
2 files changed, 17 insertions(+), 2 deletions(-)
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_impl.c b/drivers/net/axgbe/axgbe_phy_impl.c
index 8635a53a6d..8e94be399d 100644
--- a/drivers/net/axgbe/axgbe_phy_impl.c
+++ b/drivers/net/axgbe/axgbe_phy_impl.c
@@ -1643,6 +1643,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;
@@ -1840,6 +1852,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;
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* RE: [PATCH v2 1/3] net/axgbe: add external PHY read/write functions
2026-02-25 23:09 ` Stephen Hemminger
@ 2026-02-26 17:09 ` Natarajan, Ashok Kumar
0 siblings, 0 replies; 23+ messages in thread
From: Natarajan, Ashok Kumar @ 2026-02-26 17:09 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev@dpdk.org, Sebastian, Selwin
[AMD Official Use Only - AMD Internal Distribution Only]
Thanks for the review.
axgbe_phy_link_status() follows the existing driver convention where it returns 0 when the link is down and 1 when the link is up. The logic in the patch is aligned with this behavior. Added a brief comment in the code(v3) to make this return semantics clearer.
Taken care of the other comments in v3 patches
Could you share which AI tool was used for this review?
Thanks,
Ashok
-----Original Message-----
From: Stephen Hemminger <stephen@networkplumber.org>
Sent: Thursday, February 26, 2026 4:39 AM
To: Natarajan, Ashok Kumar <AshokKumar.Natarajan@amd.com>
Cc: dev@dpdk.org; Sebastian, Selwin <Selwin.Sebastian@amd.com>
Subject: Re: [PATCH v2 1/3] net/axgbe: add external PHY read/write functions
[You don't often get email from stephen@networkplumber.org. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
Caution: This message originated from an External Source. Use proper caution when opening attachments, clicking links, or responding.
On Wed, 25 Feb 2026 18:14:54 +0530
Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com> wrote:
> Introduce helper functions to perform external PHY register read and
> write operations. These helpers currently support only IEEE Clause 22
> PHY access, providing a simple and consistent API for accessing
> standard 16‑bit MII registers on external PHY devices.
>
> Signed-off-by: Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com>
> ---
I didn't see anything here, but AI spotted some things.
Please revise and resubmit.
Thanks for the series. A few findings, mostly in patch 2:
Patch 2 (M88E1512 support):
1. axgbe_phy_link_status() always returns 0 (link up) even when the
M88E1512 reports link down or the read fails. The goto out path leads to return 0, which means "link up" in this function's contract. The link-down and error paths need to return a non-zero value.
2. Integer shift UB in axgbe_get_phy_id(): (u32)(phy_id_1 << 16) evaluates the shift at int width before widening. If bit 15 of phy_id_1 is set, shifting into the sign bit is undefined behavior. Move the cast before the shift: (u32)phy_id_1 << 16.
3. axgbe_get_ext_phy_link_status() doesn't set *linkup = false on the BMCR_ANRESTART early-return path. Works today because the caller pre-initializes the variable, but the function should be self-contained.
4. The new out label unconditionally clears rx_adapt_done on the M88E1512 link-down path — please verify this is intentional and doesn't interfere with the internal SerDes receiver adaptation state machine.
Patch 3 (100Mbps):
5. The SPEED_1000 → SPEED_100 change in axgbe_sgmii_100_mode() is a bug fix in existing code — the 100M mode function was incorrectly configuring the MAC for 1G. This should have its own Fixes: tag and Cc: stable@dpdk.org so it gets backported to stable branches.
Item 1 is the most critical — it inverts link status reporting for the M88E1512.
^ permalink raw reply [flat|nested] 23+ messages in thread
* Re: [PATCH v3 1/3] net/axgbe: add external PHY read/write functions
2026-02-26 16:46 ` [PATCH v3 " Ashok Kumar Natarajan
@ 2026-02-26 19:09 ` Stephen Hemminger
2026-02-27 8:43 ` [PATCH v4 " Ashok Kumar Natarajan
1 sibling, 0 replies; 23+ messages in thread
From: Stephen Hemminger @ 2026-02-26 19:09 UTC (permalink / raw)
To: Ashok Kumar Natarajan; +Cc: dev, Selwin.Sebastian
On Thu, 26 Feb 2026 22:16:36 +0530
Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com> wrote:
> + ret = pdata->hw_if.read_ext_mii_regs_c22(pdata,
> + phy_data->mdio_addr, reg, value);
> + if (ret)
> + PMD_DRV_LOG_LINE(ERR, "mdio read failed %s",
> + strerror(ret));
Shouldn't this be strerror(-ret) here?
It looks like the driver uses the negative errno values to indicate errors.
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v4 1/3] net/axgbe: add external PHY read/write functions
2026-02-26 16:46 ` [PATCH v3 " Ashok Kumar Natarajan
2026-02-26 19:09 ` Stephen Hemminger
@ 2026-02-27 8:43 ` Ashok Kumar Natarajan
2026-02-28 14:37 ` [PATCH v5 " Ashok Kumar Natarajan
1 sibling, 1 reply; 23+ messages in thread
From: Ashok Kumar Natarajan @ 2026-02-27 8:43 UTC (permalink / raw)
To: dev; +Cc: Selwin.Sebastian, Ashok Kumar Natarajan
Introduce helper functions to perform external PHY register read and
write operations. These helpers currently support only IEEE Clause 22
PHY access, providing a simple and consistent API for accessing
standard 16‑bit MII registers on external PHY devices.
Signed-off-by: Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com>
---
.mailmap | 1 +
drivers/net/axgbe/axgbe_dev.c | 56 ++++++++++++++++--------------
drivers/net/axgbe/axgbe_ethdev.h | 16 ++++++---
drivers/net/axgbe/axgbe_phy_impl.c | 45 +++++++++++++++++++++++-
4 files changed, 85 insertions(+), 33 deletions(-)
diff --git a/.mailmap b/.mailmap
index 6c4c977dde..5c8b47f1a9 100644
--- a/.mailmap
+++ b/.mailmap
@@ -165,6 +165,7 @@ Ashish Paul <apaul@juniper.net>
Ashish Sadanandan <ashish.sadanandan@gmail.com>
Ashish Shah <ashish.n.shah@intel.com>
Ashok Kaladi <ashok.k.kaladi@intel.com>
+Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com>
Ashwin Sekhar T K <asekhar@marvell.com> <ashwin.sekhar@caviumnetworks.com>
Asim Jamshed <asim.jamshed@gmail.com>
Atul Patel <atul.patel@intel.com>
diff --git a/drivers/net/axgbe/axgbe_dev.c b/drivers/net/axgbe/axgbe_dev.c
index 634d4ee4a5..10a99aeac2 100644
--- a/drivers/net/axgbe/axgbe_dev.c
+++ b/drivers/net/axgbe/axgbe_dev.c
@@ -63,9 +63,9 @@ static int mdio_complete(struct axgbe_port *pdata)
return 0;
}
-static unsigned int axgbe_create_mdio_sca_c22(int port, int reg)
+static unsigned int axgbe_create_mdio_sca_c22(u8 port, u16 reg)
{
- unsigned int mdio_sca;
+ u32 mdio_sca;
mdio_sca = 0;
AXGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, RA, reg);
@@ -74,20 +74,20 @@ static unsigned int axgbe_create_mdio_sca_c22(int port, int reg)
return mdio_sca;
}
-static unsigned int axgbe_create_mdio_sca_c45(int port, unsigned int da, int reg)
+static unsigned int axgbe_create_mdio_sca_c45(u8 port, u8 dev_addr, u16 reg)
{
- unsigned int mdio_sca;
+ u32 mdio_sca;
mdio_sca = 0;
AXGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, RA, reg);
AXGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, PA, port);
- AXGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, DA, da);
+ AXGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, DA, dev_addr);
return mdio_sca;
}
static int axgbe_write_ext_mii_regs(struct axgbe_port *pdata,
- unsigned int mdio_sca, u16 val)
+ u32 mdio_sca, u16 value)
{
unsigned int mdio_sccd;
uint64_t timeout;
@@ -95,7 +95,7 @@ static int axgbe_write_ext_mii_regs(struct axgbe_port *pdata,
AXGMAC_IOWRITE(pdata, MAC_MDIOSCAR, mdio_sca);
mdio_sccd = 0;
- AXGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, DATA, val);
+ AXGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, DATA, value);
AXGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, CMD, 1);
AXGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, BUSY, 1);
AXGMAC_IOWRITE(pdata, MAC_MDIOSCCDR, mdio_sccd);
@@ -113,28 +113,28 @@ static int axgbe_write_ext_mii_regs(struct axgbe_port *pdata,
static int axgbe_write_ext_mii_regs_c22(struct axgbe_port *pdata,
- int addr, int reg, u16 val)
+ u8 phy_addr, u16 reg, u16 value)
{
- unsigned int mdio_sca;
+ u32 mdio_sca;
- mdio_sca = axgbe_create_mdio_sca_c22(addr, reg);
+ mdio_sca = axgbe_create_mdio_sca_c22(phy_addr, reg);
- return axgbe_write_ext_mii_regs(pdata, mdio_sca, val);
+ return axgbe_write_ext_mii_regs(pdata, mdio_sca, value);
}
static int axgbe_write_ext_mii_regs_c45(struct axgbe_port *pdata,
- int addr, int devad, int reg, u16 val)
+ u8 phy_addr, u8 dev_addr, u16 reg, u16 value)
{
- unsigned int mdio_sca;
+ u32 mdio_sca;
- mdio_sca = axgbe_create_mdio_sca_c45(addr, devad, reg);
+ mdio_sca = axgbe_create_mdio_sca_c45(phy_addr, dev_addr, reg);
- return axgbe_write_ext_mii_regs(pdata, mdio_sca, val);
+ return axgbe_write_ext_mii_regs(pdata, mdio_sca, value);
}
-static int axgbe_read_ext_mii_regs(struct axgbe_port *pdata,
- unsigned int mdio_sca)
+static int axgbe_read_ext_mii_regs(struct axgbe_port *pdata, u32 mdio_sca,
+ u16 *value)
{
unsigned int mdio_sccd;
uint64_t timeout;
@@ -158,26 +158,28 @@ static int axgbe_read_ext_mii_regs(struct axgbe_port *pdata,
return -ETIMEDOUT;
success:
- return AXGMAC_IOREAD_BITS(pdata, MAC_MDIOSCCDR, DATA);
+ *value = AXGMAC_IOREAD_BITS(pdata, MAC_MDIOSCCDR, DATA);
+ return 0;
}
-static int axgbe_read_ext_mii_regs_c22(struct axgbe_port *pdata, int addr, int reg)
+static int axgbe_read_ext_mii_regs_c22(struct axgbe_port *pdata, u8 phy_addr,
+ u16 reg, u16 *value)
{
- unsigned int mdio_sca;
+ u32 mdio_sca;
- mdio_sca = axgbe_create_mdio_sca_c22(addr, reg);
+ mdio_sca = axgbe_create_mdio_sca_c22(phy_addr, reg);
- return axgbe_read_ext_mii_regs(pdata, mdio_sca);
+ return axgbe_read_ext_mii_regs(pdata, mdio_sca, value);
}
-static int axgbe_read_ext_mii_regs_c45(struct axgbe_port *pdata, int addr,
- int devad, int reg)
+static int axgbe_read_ext_mii_regs_c45(struct axgbe_port *pdata, u8 phy_addr,
+ u8 dev_addr, u16 reg, u16 *value)
{
- unsigned int mdio_sca;
+ u32 mdio_sca;
- mdio_sca = axgbe_create_mdio_sca_c45(addr, devad, reg);
+ mdio_sca = axgbe_create_mdio_sca_c45(phy_addr, dev_addr, reg);
- return axgbe_read_ext_mii_regs(pdata, mdio_sca);
+ return axgbe_read_ext_mii_regs(pdata, mdio_sca, value);
}
static int axgbe_set_ext_mii_mode(struct axgbe_port *pdata, unsigned int port,
diff --git a/drivers/net/axgbe/axgbe_ethdev.h b/drivers/net/axgbe/axgbe_ethdev.h
index b94a7f3562..24336f31f6 100644
--- a/drivers/net/axgbe/axgbe_ethdev.h
+++ b/drivers/net/axgbe/axgbe_ethdev.h
@@ -327,11 +327,14 @@ struct axgbe_hw_if {
int (*set_ext_mii_mode)(struct axgbe_port *, unsigned int,
enum axgbe_mdio_mode);
- int (*read_ext_mii_regs_c22)(struct axgbe_port *pdata, int addr, int reg);
- int (*write_ext_mii_regs_c22)(struct axgbe_port *pdata, int addr, int reg, uint16_t val);
- int (*read_ext_mii_regs_c45)(struct axgbe_port *pdata, int addr, int devad, int reg);
- int (*write_ext_mii_regs_c45)(struct axgbe_port *pdata, int addr, int devad,
- int reg, uint16_t val);
+ int (*read_ext_mii_regs_c22)(struct axgbe_port *pdata, u8 phy_addr,
+ u16 reg, u16 *value);
+ int (*write_ext_mii_regs_c22)(struct axgbe_port *pdata, u8 phy_addr,
+ u16 reg, u16 value);
+ int (*read_ext_mii_regs_c45)(struct axgbe_port *pdata, u8 phy_addr,
+ u8 dev_addr, u16 reg, u16 *value);
+ int (*write_ext_mii_regs_c45)(struct axgbe_port *pdata, u8 phy_addr,
+ u8 dev_addr, u16 reg, u16 value);
/* For FLOW ctrl */
int (*config_tx_flow_control)(struct axgbe_port *);
@@ -398,6 +401,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 *pdata, u16 reg, u16 *value);
+ int (*write)(struct axgbe_port *pdata, u16 reg, u16 value);
};
struct axgbe_phy_if {
diff --git a/drivers/net/axgbe/axgbe_phy_impl.c b/drivers/net/axgbe/axgbe_phy_impl.c
index 9249e11335..cbab2908b4 100644
--- a/drivers/net/axgbe/axgbe_phy_impl.c
+++ b/drivers/net/axgbe/axgbe_phy_impl.c
@@ -203,7 +203,7 @@ struct axgbe_phy_data {
unsigned int rrc_count;
- unsigned int mdio_addr;
+ uint8_t mdio_addr;
/* SFP Support */
enum axgbe_sfp_comm sfp_comm;
@@ -251,6 +251,8 @@ 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_phy_get_comm_ownership(struct axgbe_port *pdata);
+static void axgbe_phy_put_comm_ownership(struct axgbe_port *pdata);
static int axgbe_phy_i2c_xfer(struct axgbe_port *pdata,
struct axgbe_i2c_op *i2c_op)
@@ -258,6 +260,44 @@ static int axgbe_phy_i2c_xfer(struct axgbe_port *pdata,
return pdata->i2c_if.i2c_xfer(pdata, i2c_op);
}
+static int axgbe_phy_read(struct axgbe_port *pdata, u16 reg, u16 *value)
+{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ int ret;
+
+ ret = axgbe_phy_get_comm_ownership(pdata);
+ if (ret)
+ return ret;
+
+ ret = pdata->hw_if.read_ext_mii_regs_c22(pdata,
+ phy_data->mdio_addr, reg, value);
+ if (ret)
+ PMD_DRV_LOG_LINE(ERR, "mdio read failed %s",
+ strerror(-ret));
+
+ axgbe_phy_put_comm_ownership(pdata);
+ return ret;
+}
+
+static int axgbe_phy_write(struct axgbe_port *pdata, u16 reg, u16 value)
+{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ int ret;
+ ret = axgbe_phy_get_comm_ownership(pdata);
+ if (ret)
+ return ret;
+
+ ret = pdata->hw_if.write_ext_mii_regs_c22(pdata,
+ phy_data->mdio_addr, reg, value);
+ if (ret)
+ PMD_DRV_LOG_LINE(ERR, "mdio write failed %s",
+ strerror(-ret));
+
+ axgbe_phy_put_comm_ownership(pdata);
+ return ret;
+}
+
+
static int axgbe_phy_redrv_write(struct axgbe_port *pdata, unsigned int reg,
unsigned int val)
{
@@ -2542,4 +2582,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_read;
+ phy_impl->write = axgbe_phy_write;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 2/3] net/axgbe: add support for marvell m88e1512 PHY
2026-02-26 16:47 ` [PATCH v3 " Ashok Kumar Natarajan
@ 2026-02-27 8:44 ` Ashok Kumar Natarajan
2026-02-27 23:25 ` Stephen Hemminger
2026-02-28 14:39 ` [PATCH v5 " Ashok Kumar Natarajan
0 siblings, 2 replies; 23+ messages in thread
From: Ashok Kumar Natarajan @ 2026-02-27 8:44 UTC (permalink / raw)
To: dev; +Cc: Selwin.Sebastian, Ashok Kumar Natarajan
Add support for the Marvell M88E1512 PHY in the axgbe driver. This patch
implements device identification and basic initialization required to
operate with this PHY. Only the M88E1512 model is supported in this
update.
Signed-off-by: Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com>
---
drivers/net/axgbe/axgbe_phy.h | 44 ++++
drivers/net/axgbe/axgbe_phy_impl.c | 389 ++++++++++++++++++++++++++++-
2 files changed, 429 insertions(+), 4 deletions(-)
diff --git a/drivers/net/axgbe/axgbe_phy.h b/drivers/net/axgbe/axgbe_phy.h
index eee3afc370..1bedd8e132 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,26 @@
#define MDIO_AN_10GBT_CTRL 32 /* 10GBASE-T auto-negotiation control */
#define MDIO_AN_10GBT_STAT 33 /* 10GBASE-T auto-negotiation status */
+#define M88E1512_E_PHY_ID 0x01410DD0
+#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_PHY_PAGE 22
+#define AXGBE_M88E1512_COPPER_PAGE 0x00
+#define AXGBE_M88E1512_LED_PAGE 0x03
+#define AXGBE_M88E1512_MODE_PAGE 0x12
+
+#define AXGBE_M88E1512_MODE_RGMII_COPPER 0
+#define AXGBE_M88E1512_MODE_SGMII_COPPER 1
+#define AXGBE_M88E1512_MODE_RGMII_1000BX 2
+#define AXGBE_M88E1512_MODE_RGMII_100BFX 3
+#define AXGBE_M88E1512_MODE_RGMII_SGMII 4
+#define AXGBE_M88E1512_MODE_SW_RESET 0x8000
+
+
/* Control register 1. */
/* Enable extended speed selection */
#define MDIO_CTRL1_SPEEDSELEXT (BMCR_SPEED1000 | BMCR_SPEED100)
@@ -190,5 +233,6 @@
#define DUPLEX_FULL 0x01
#define DUPLEX_UNKNOWN 0xff
+#define AXGBE_PHY_REVISION_MASK 0xfffffff0
#endif
/* PHY */
diff --git a/drivers/net/axgbe/axgbe_phy_impl.c b/drivers/net/axgbe/axgbe_phy_impl.c
index cbab2908b4..8dd3090c03 100644
--- a/drivers/net/axgbe/axgbe_phy_impl.c
+++ b/drivers/net/axgbe/axgbe_phy_impl.c
@@ -3,6 +3,7 @@
* Copyright(c) 2018 Synopsys, Inc. All rights reserved.
*/
+#include <rte_cycles.h>
#include "axgbe_ethdev.h"
#include "axgbe_common.h"
#include "axgbe_phy.h"
@@ -204,6 +205,7 @@ struct axgbe_phy_data {
unsigned int rrc_count;
uint8_t mdio_addr;
+ uint32_t phy_id;
/* SFP Support */
enum axgbe_sfp_comm sfp_comm;
@@ -253,6 +255,8 @@ static void axgbe_phy_rrc(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_get_ext_phy_link_status(struct axgbe_port *pdata,
+ bool *linkup);
static int axgbe_phy_i2c_xfer(struct axgbe_port *pdata,
struct axgbe_i2c_op *i2c_op)
@@ -297,6 +301,83 @@ static int axgbe_phy_write(struct axgbe_port *pdata, u16 reg, u16 value)
return ret;
}
+static int axgbe_phy_config_advert(struct axgbe_port *pdata)
+{
+ u16 advert;
+ int ret;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_ADVERTISE, &advert);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to read ADVERTISE register");
+ return ret;
+ }
+
+ advert |= ADVERTISE_FULL;
+ advert |= ADVERTISE_PAUSE_CAP;
+
+ ret = pdata->phy_if.phy_impl.write(pdata, MII_ADVERTISE, advert);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to write ADVERTISE register");
+ return ret;
+ }
+ return 0;
+}
+
+static int axgbe_phy_set_speed(struct axgbe_port *pdata)
+{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ u16 bmcr;
+ int ret;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMCR, &bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to read BMCR register");
+ return ret;
+ }
+
+ 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;
+ }
+
+ ret = pdata->phy_if.phy_impl.write(pdata, MII_BMCR, bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to write BMCR register");
+ return ret;
+ }
+ return 0;
+}
+
+static int axgbe_phy_config_aneg(struct axgbe_port *pdata)
+{
+ u16 bmcr;
+ int ret;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMCR, &bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to read BMCR register");
+ return ret;
+ }
+
+ bmcr &= ~(BMCR_ANENABLE);
+ if (pdata->phy.autoneg == AUTONEG_ENABLE)
+ bmcr |= BMCR_ANENABLE | BMCR_ANRESTART;
+
+ ret = pdata->phy_if.phy_impl.write(pdata, MII_BMCR, bmcr);
+
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to write BMCR register");
+ return ret;
+ }
+ return 0;
+}
static int axgbe_phy_redrv_write(struct axgbe_port *pdata, unsigned int reg,
unsigned int val)
@@ -1132,10 +1213,35 @@ static unsigned int axgbe_phy_an_advertising(struct axgbe_port *pdata)
static int axgbe_phy_an_config(struct axgbe_port *pdata __rte_unused)
{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ int ret;
+
+ /* Auto-negotiation config is implemented only for 1000BASE-T */
+ if (phy_data->port_mode != AXGBE_PORT_MODE_1000BASE_T)
+ return 0;
+
+ /* Supports only Marvell M88E1512 PHY for now*/
+ if (phy_data->phy_id == M88E1512_E_PHY_ID) {
+ ret = axgbe_phy_config_advert(pdata);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY ADVERTISE config failed");
+ return ret;
+ }
+
+ ret = axgbe_phy_set_speed(pdata);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY set speed failed");
+ return ret;
+ }
+
+ ret = axgbe_phy_config_aneg(pdata);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY AN config failed");
+ return ret;
+ }
+ PMD_DRV_LOG_LINE(DEBUG, "M88E1512 PHY AN config done");
+ }
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)
@@ -1880,10 +1986,12 @@ static bool axgbe_phy_use_mode(struct axgbe_port *pdata, enum axgbe_mode mode)
}
}
+/* return 0 means link down, 1 means link up */
static int axgbe_phy_link_status(struct axgbe_port *pdata, int *an_restart)
{
struct axgbe_phy_data *phy_data = pdata->phy_data;
- unsigned int reg;
+ uint32_t reg;
+ bool ext_phy_link_up = false;
*an_restart = 0;
@@ -1903,6 +2011,26 @@ static int axgbe_phy_link_status(struct axgbe_port *pdata, int *an_restart)
}
}
+ if (phy_data->port_mode == AXGBE_PORT_MODE_1000BASE_T) {
+ if (phy_data->phy_id == M88E1512_E_PHY_ID) {
+ if (axgbe_get_ext_phy_link_status(pdata,
+ &ext_phy_link_up)) {
+ PMD_DRV_LOG_LINE(ERR,
+ "Get link status failed");
+ goto out;
+ }
+
+ if (ext_phy_link_up) {
+ 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
*/
@@ -1946,6 +2074,7 @@ static int axgbe_phy_link_status(struct axgbe_port *pdata, int *an_restart)
axgbe_phy_rrc(pdata);
}
+out:
return 0;
}
@@ -2303,6 +2432,243 @@ static int axgbe_phy_reset(struct axgbe_port *pdata)
return 0;
}
+static int axgbe_get_phy_id(struct axgbe_port *pdata)
+{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ u16 phy_id_1;
+ u16 phy_id_2;
+ int ret;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_PHYSID1,
+ &phy_id_1);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to read PHYSID1 register");
+ return ret;
+ }
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_PHYSID2,
+ &phy_id_2);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to read PHYSID2 register");
+ return ret;
+ }
+
+ phy_data->phy_id = (u32)phy_id_1 << 16;
+ phy_data->phy_id |= phy_id_2;
+ phy_data->phy_id &= AXGBE_PHY_REVISION_MASK;
+
+ if (phy_data->phy_id == AXGBE_PHY_REVISION_MASK) {
+ PMD_DRV_LOG_LINE(ERR, "Invalid PHY id (0x%x)",
+ phy_data->phy_id);
+ return -EINVAL;
+ }
+ PMD_DRV_LOG_LINE(DEBUG, "PHY id (0x%x)", phy_data->phy_id);
+ return 0;
+}
+
+static int axgbe_phy_poll_reset(struct axgbe_port *pdata)
+{
+ const uint64_t timeout_ms = 2000;
+ const uint64_t interval_ms = 1;
+ const uint64_t hz = rte_get_timer_hz();
+ const uint64_t start = rte_get_timer_cycles();
+
+ u16 bmcr = 0;
+ int ret;
+
+ for (;;) {
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMCR, &bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY read BMCR failed");
+ return ret;
+ }
+ if (!(bmcr & BMCR_RESET))
+ return 0;
+
+ uint64_t elapsed_ms =
+ ((rte_get_timer_cycles() - start) * 1000) / hz;
+ if (elapsed_ms >= timeout_ms)
+ break;
+
+ rte_delay_us_block(interval_ms * 1000);
+ }
+ return -ETIMEDOUT;
+}
+
+
+static int axgbe_phy_soft_reset(struct axgbe_port *pdata)
+{
+ int ret;
+ u16 bmcr;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMCR, &bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to read BMCR register");
+ return ret;
+ }
+
+ bmcr |= BMCR_RESET;
+
+ ret = pdata->phy_if.phy_impl.write(pdata, MII_BMCR, bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to write BMCR register");
+ return ret;
+ }
+
+ ret = axgbe_phy_poll_reset(pdata);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Poll for PHY reset done failed");
+ return ret;
+ }
+ return 0;
+}
+
+static int axgbe_m88e1512_set_page(struct axgbe_port *pdata, u16 page)
+{
+ int ret;
+
+ ret = pdata->phy_if.phy_impl.write(pdata,
+ AXGBE_M88E1512_PAGE_ADDR, page);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR,
+ "PHY set page failed for page 0x%x", page);
+ }
+ return ret;
+}
+
+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)
+{
+ int ret;
+ u8 i;
+
+ PMD_DRV_LOG_LINE(DEBUG, "Initialize M88E1512 phy");
+
+ /* Switch to PHY page 0xFF */
+ ret = axgbe_m88e1512_set_page(pdata, 0xff);
+ if (ret)
+ goto err_out;
+
+ /* Configure M88E1512 errata registers */
+ for (i = 0; i < ARRAY_SIZE(errata_vals); i++) {
+ ret = pdata->phy_if.phy_impl.write(pdata,
+ AXGBE_M88E1512_CFG_REG_2,
+ errata_vals[i].reg17);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Config M88E1512 errata failed");
+ goto err_set_copper_page;
+ }
+
+ ret = pdata->phy_if.phy_impl.write(pdata,
+ AXGBE_M88E1512_CFG_REG_1,
+ errata_vals[i].reg16);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Config M88E1512 errata failed");
+ goto err_set_copper_page;
+ }
+ }
+
+ /* Switch to PHY page 0xFB */
+ ret = axgbe_m88e1512_set_page(pdata, 0xfb);
+ if (ret)
+ goto err_set_copper_page;
+
+ ret = pdata->phy_if.phy_impl.write(pdata,
+ AXGBE_M88E1512_CFG_REG_3, 0xC00D);
+ if (ret)
+ goto err_set_copper_page;
+
+ /* Switch to PHY copper page */
+ ret = axgbe_m88e1512_set_page(pdata, AXGBE_M88E1512_COPPER_PAGE);
+ if (ret)
+ goto err_out;
+
+ /* SGMII-to-Copper mode initialization */
+
+ /* Switch to PHY mode page */
+ ret = axgbe_m88e1512_set_page(pdata, AXGBE_M88E1512_MODE_PAGE);
+ if (ret)
+ goto err_set_copper_page;
+
+ ret = pdata->phy_if.phy_impl.write(pdata,
+ AXGBE_M88E1512_MODE,
+ (AXGBE_M88E1512_MODE_SW_RESET |
+ AXGBE_M88E1512_MODE_SGMII_COPPER));
+ if (ret)
+ goto err_set_copper_page;
+
+ /* Switch to PHY copper page */
+ ret = axgbe_m88e1512_set_page(pdata, AXGBE_M88E1512_COPPER_PAGE);
+ if (ret)
+ goto err_out;
+
+ ret = axgbe_phy_soft_reset(pdata);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "M88E1512 PHY soft reset failed");
+ goto err_out;
+ }
+
+ PMD_DRV_LOG_LINE(DEBUG, "M88E1512 phy init done");
+ return 0;
+
+err_set_copper_page:
+ /* Switch to PHY page 0 */
+ axgbe_m88e1512_set_page(pdata, AXGBE_M88E1512_COPPER_PAGE);
+err_out:
+ PMD_DRV_LOG_LINE(ERR, "M88E1512 PHY initialization failed");
+ return ret;
+}
+
+
+static int axgbe_get_ext_phy_link_status(struct axgbe_port *pdata, bool *linkup)
+{
+ int ret;
+ u16 bmcr;
+ u16 bmsr;
+
+ *linkup = false;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMCR, &bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY read failed for BMCR register");
+ return ret;
+ }
+
+ /* Autoneg is being started, therefore disregard BMSR value and
+ * report link as down.
+ */
+ if (bmcr & BMCR_ANRESTART)
+ return 0;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMSR, &bmsr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY read failed for BMSR register");
+ return ret;
+ }
+
+ if (bmsr & BMSR_LSTATUS)
+ goto done;
+
+ /* Read link status */
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMSR, &bmsr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY read failed for BMSR register");
+ return ret;
+ }
+done:
+ *linkup = (bmsr & BMSR_LSTATUS) ? true : false;
+ return 0;
+}
+
+
static int axgbe_phy_init(struct axgbe_port *pdata)
{
struct axgbe_phy_data *phy_data;
@@ -2556,6 +2922,21 @@ static int axgbe_phy_init(struct axgbe_port *pdata)
}
phy_data->phy_cdr_delay = AXGBE_CDR_DELAY_INIT;
+
+ ret = axgbe_get_phy_id(pdata);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "failed to get PHY id");
+ return ret;
+ }
+
+ PMD_DRV_LOG_LINE(DEBUG, "PHY ID = 0x%x", phy_data->phy_id);
+
+ if (phy_data->phy_id == M88E1512_E_PHY_ID) {
+ ret = axgbe_m88e1512_init(pdata);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
void axgbe_init_function_ptrs_phy_v2(struct axgbe_phy_if *phy_if)
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v4 3/3] net/axgbe: fix 100M SGMII mode
2026-02-26 16:49 ` [PATCH v3 3/3] net/axgbe: fix 100M SGMII mode Ashok Kumar Natarajan
@ 2026-02-27 8:45 ` Ashok Kumar Natarajan
2026-02-28 14:40 ` [PATCH v5 " Ashok Kumar Natarajan
0 siblings, 1 reply; 23+ messages in thread
From: Ashok Kumar Natarajan @ 2026-02-27 8:45 UTC (permalink / raw)
To: dev; +Cc: Selwin.Sebastian, Ashok Kumar Natarajan, stable
In SGMII 100M mode, set MAC speed to 100M instead of 1G,
and add the PHY implementation for SGMII 100 mode.
Fixes: a5c7273771e8 ("net/axgbe: add phy programming APIs")
Cc: stable@dpdk.org
Signed-off-by: Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com>
---
drivers/net/axgbe/axgbe_mdio.c | 4 ++--
drivers/net/axgbe/axgbe_phy_impl.c | 15 +++++++++++++++
2 files changed, 17 insertions(+), 2 deletions(-)
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_impl.c b/drivers/net/axgbe/axgbe_phy_impl.c
index 8dd3090c03..88274962f0 100644
--- a/drivers/net/axgbe/axgbe_phy_impl.c
+++ b/drivers/net/axgbe/axgbe_phy_impl.c
@@ -1643,6 +1643,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;
@@ -1840,6 +1852,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;
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH v4 2/3] net/axgbe: add support for marvell m88e1512 PHY
2026-02-27 8:44 ` [PATCH v4 " Ashok Kumar Natarajan
@ 2026-02-27 23:25 ` Stephen Hemminger
2026-02-28 14:39 ` [PATCH v5 " Ashok Kumar Natarajan
1 sibling, 0 replies; 23+ messages in thread
From: Stephen Hemminger @ 2026-02-27 23:25 UTC (permalink / raw)
To: Ashok Kumar Natarajan; +Cc: dev, Selwin.Sebastian
On Fri, 27 Feb 2026 14:14:29 +0530
Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com> wrote:
> @@ -2556,6 +2922,21 @@ static int axgbe_phy_init(struct axgbe_port *pdata)
> }
>
> phy_data->phy_cdr_delay = AXGBE_CDR_DELAY_INIT;
> +
> + ret = axgbe_get_phy_id(pdata);
> + if (ret) {
> + PMD_DRV_LOG_LINE(ERR, "failed to get PHY id");
> + return ret;
> + }
> +
> + PMD_DRV_LOG_LINE(DEBUG, "PHY ID = 0x%x", phy_data->phy_id);
> +
> + if (phy_data->phy_id == M88E1512_E_PHY_ID) {
> + ret = axgbe_m88e1512_init(pdata);
> + if (ret)
> + return ret;
> + }
> +
> return 0;
> }
AI was confused and thinks that there may be an issue here:
Patch 2/3: net/axgbe: add support for marvell m88e1512 PHY
Error — axgbe_get_phy_id() called unconditionally in axgbe_phy_init() (potential regression)
This is the most significant finding in the series. axgbe_get_phy_id() is inserted at the end of axgbe_phy_init() without any guard on port mode:

c
phy_data->phy_cdr_delay = AXGBE_CDR_DELAY_INIT;
+
+ ret = axgbe_get_phy_id(pdata);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "failed to get PHY id");
+ return ret;
+ }
axgbe_phy_init() is called for all port modes — SFP, backplane, 10GBASE-T, etc. For port modes that have no external PHY, axgbe_phy_read() will attempt a Clause 22 MDIO read on phy_data->mdio_addr and likely timeout (-ETIMEDOUT), causing the entire PHY init to fail. This is a regression for devices that previously worked.
The M88E1512-specific init block below already checks phy_data->phy_id, but the axgbe_get_phy_id() call itself must not fail for non-1000BASE-T ports.
Fix: Gate the PHY ID read (and subsequent M88E1512 init) on the port mode:

c
if (phy_data->port_mode == AXGBE_PORT_MODE_1000BASE_T) {
ret = axgbe_get_phy_id(pdata);
if (ret) {
PMD_DRV_LOG_LINE(ERR, "failed to get PHY id");
return ret;
}
if (phy_data->phy_id == M88E1512_E_PHY_ID) {
ret = axgbe_m88e1512_init(pdata);
if (ret)
return ret;
}
}
This is consistent with how axgbe_phy_link_status() in the same patch correctly guards the external PHY check with if (phy_data->port_mode == AXGBE_PORT_MODE_1000BASE_T).
Confidence: High (~90%). The MDIO read will go through hardware I/O regardless of whether a PHY exists at the address.
^ permalink raw reply [flat|nested] 23+ messages in thread
* [PATCH v5 1/3] net/axgbe: add external PHY read/write functions
2026-02-27 8:43 ` [PATCH v4 " Ashok Kumar Natarajan
@ 2026-02-28 14:37 ` Ashok Kumar Natarajan
2026-02-28 16:41 ` Stephen Hemminger
0 siblings, 1 reply; 23+ messages in thread
From: Ashok Kumar Natarajan @ 2026-02-28 14:37 UTC (permalink / raw)
To: dev; +Cc: Selwin.Sebastian, Ashok Kumar Natarajan
Introduce helper functions to perform external PHY register read and
write operations. These helpers currently support only IEEE Clause 22
PHY access, providing a simple and consistent API for accessing
standard 16‑bit MII registers on external PHY devices.
Signed-off-by: Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com>
---
.mailmap | 1 +
drivers/net/axgbe/axgbe_dev.c | 56 ++++++++++++++++--------------
drivers/net/axgbe/axgbe_ethdev.h | 16 ++++++---
drivers/net/axgbe/axgbe_phy_impl.c | 45 +++++++++++++++++++++++-
4 files changed, 85 insertions(+), 33 deletions(-)
diff --git a/.mailmap b/.mailmap
index 6c4c977dde..5c8b47f1a9 100644
--- a/.mailmap
+++ b/.mailmap
@@ -165,6 +165,7 @@ Ashish Paul <apaul@juniper.net>
Ashish Sadanandan <ashish.sadanandan@gmail.com>
Ashish Shah <ashish.n.shah@intel.com>
Ashok Kaladi <ashok.k.kaladi@intel.com>
+Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com>
Ashwin Sekhar T K <asekhar@marvell.com> <ashwin.sekhar@caviumnetworks.com>
Asim Jamshed <asim.jamshed@gmail.com>
Atul Patel <atul.patel@intel.com>
diff --git a/drivers/net/axgbe/axgbe_dev.c b/drivers/net/axgbe/axgbe_dev.c
index 634d4ee4a5..10a99aeac2 100644
--- a/drivers/net/axgbe/axgbe_dev.c
+++ b/drivers/net/axgbe/axgbe_dev.c
@@ -63,9 +63,9 @@ static int mdio_complete(struct axgbe_port *pdata)
return 0;
}
-static unsigned int axgbe_create_mdio_sca_c22(int port, int reg)
+static unsigned int axgbe_create_mdio_sca_c22(u8 port, u16 reg)
{
- unsigned int mdio_sca;
+ u32 mdio_sca;
mdio_sca = 0;
AXGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, RA, reg);
@@ -74,20 +74,20 @@ static unsigned int axgbe_create_mdio_sca_c22(int port, int reg)
return mdio_sca;
}
-static unsigned int axgbe_create_mdio_sca_c45(int port, unsigned int da, int reg)
+static unsigned int axgbe_create_mdio_sca_c45(u8 port, u8 dev_addr, u16 reg)
{
- unsigned int mdio_sca;
+ u32 mdio_sca;
mdio_sca = 0;
AXGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, RA, reg);
AXGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, PA, port);
- AXGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, DA, da);
+ AXGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, DA, dev_addr);
return mdio_sca;
}
static int axgbe_write_ext_mii_regs(struct axgbe_port *pdata,
- unsigned int mdio_sca, u16 val)
+ u32 mdio_sca, u16 value)
{
unsigned int mdio_sccd;
uint64_t timeout;
@@ -95,7 +95,7 @@ static int axgbe_write_ext_mii_regs(struct axgbe_port *pdata,
AXGMAC_IOWRITE(pdata, MAC_MDIOSCAR, mdio_sca);
mdio_sccd = 0;
- AXGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, DATA, val);
+ AXGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, DATA, value);
AXGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, CMD, 1);
AXGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, BUSY, 1);
AXGMAC_IOWRITE(pdata, MAC_MDIOSCCDR, mdio_sccd);
@@ -113,28 +113,28 @@ static int axgbe_write_ext_mii_regs(struct axgbe_port *pdata,
static int axgbe_write_ext_mii_regs_c22(struct axgbe_port *pdata,
- int addr, int reg, u16 val)
+ u8 phy_addr, u16 reg, u16 value)
{
- unsigned int mdio_sca;
+ u32 mdio_sca;
- mdio_sca = axgbe_create_mdio_sca_c22(addr, reg);
+ mdio_sca = axgbe_create_mdio_sca_c22(phy_addr, reg);
- return axgbe_write_ext_mii_regs(pdata, mdio_sca, val);
+ return axgbe_write_ext_mii_regs(pdata, mdio_sca, value);
}
static int axgbe_write_ext_mii_regs_c45(struct axgbe_port *pdata,
- int addr, int devad, int reg, u16 val)
+ u8 phy_addr, u8 dev_addr, u16 reg, u16 value)
{
- unsigned int mdio_sca;
+ u32 mdio_sca;
- mdio_sca = axgbe_create_mdio_sca_c45(addr, devad, reg);
+ mdio_sca = axgbe_create_mdio_sca_c45(phy_addr, dev_addr, reg);
- return axgbe_write_ext_mii_regs(pdata, mdio_sca, val);
+ return axgbe_write_ext_mii_regs(pdata, mdio_sca, value);
}
-static int axgbe_read_ext_mii_regs(struct axgbe_port *pdata,
- unsigned int mdio_sca)
+static int axgbe_read_ext_mii_regs(struct axgbe_port *pdata, u32 mdio_sca,
+ u16 *value)
{
unsigned int mdio_sccd;
uint64_t timeout;
@@ -158,26 +158,28 @@ static int axgbe_read_ext_mii_regs(struct axgbe_port *pdata,
return -ETIMEDOUT;
success:
- return AXGMAC_IOREAD_BITS(pdata, MAC_MDIOSCCDR, DATA);
+ *value = AXGMAC_IOREAD_BITS(pdata, MAC_MDIOSCCDR, DATA);
+ return 0;
}
-static int axgbe_read_ext_mii_regs_c22(struct axgbe_port *pdata, int addr, int reg)
+static int axgbe_read_ext_mii_regs_c22(struct axgbe_port *pdata, u8 phy_addr,
+ u16 reg, u16 *value)
{
- unsigned int mdio_sca;
+ u32 mdio_sca;
- mdio_sca = axgbe_create_mdio_sca_c22(addr, reg);
+ mdio_sca = axgbe_create_mdio_sca_c22(phy_addr, reg);
- return axgbe_read_ext_mii_regs(pdata, mdio_sca);
+ return axgbe_read_ext_mii_regs(pdata, mdio_sca, value);
}
-static int axgbe_read_ext_mii_regs_c45(struct axgbe_port *pdata, int addr,
- int devad, int reg)
+static int axgbe_read_ext_mii_regs_c45(struct axgbe_port *pdata, u8 phy_addr,
+ u8 dev_addr, u16 reg, u16 *value)
{
- unsigned int mdio_sca;
+ u32 mdio_sca;
- mdio_sca = axgbe_create_mdio_sca_c45(addr, devad, reg);
+ mdio_sca = axgbe_create_mdio_sca_c45(phy_addr, dev_addr, reg);
- return axgbe_read_ext_mii_regs(pdata, mdio_sca);
+ return axgbe_read_ext_mii_regs(pdata, mdio_sca, value);
}
static int axgbe_set_ext_mii_mode(struct axgbe_port *pdata, unsigned int port,
diff --git a/drivers/net/axgbe/axgbe_ethdev.h b/drivers/net/axgbe/axgbe_ethdev.h
index b94a7f3562..24336f31f6 100644
--- a/drivers/net/axgbe/axgbe_ethdev.h
+++ b/drivers/net/axgbe/axgbe_ethdev.h
@@ -327,11 +327,14 @@ struct axgbe_hw_if {
int (*set_ext_mii_mode)(struct axgbe_port *, unsigned int,
enum axgbe_mdio_mode);
- int (*read_ext_mii_regs_c22)(struct axgbe_port *pdata, int addr, int reg);
- int (*write_ext_mii_regs_c22)(struct axgbe_port *pdata, int addr, int reg, uint16_t val);
- int (*read_ext_mii_regs_c45)(struct axgbe_port *pdata, int addr, int devad, int reg);
- int (*write_ext_mii_regs_c45)(struct axgbe_port *pdata, int addr, int devad,
- int reg, uint16_t val);
+ int (*read_ext_mii_regs_c22)(struct axgbe_port *pdata, u8 phy_addr,
+ u16 reg, u16 *value);
+ int (*write_ext_mii_regs_c22)(struct axgbe_port *pdata, u8 phy_addr,
+ u16 reg, u16 value);
+ int (*read_ext_mii_regs_c45)(struct axgbe_port *pdata, u8 phy_addr,
+ u8 dev_addr, u16 reg, u16 *value);
+ int (*write_ext_mii_regs_c45)(struct axgbe_port *pdata, u8 phy_addr,
+ u8 dev_addr, u16 reg, u16 value);
/* For FLOW ctrl */
int (*config_tx_flow_control)(struct axgbe_port *);
@@ -398,6 +401,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 *pdata, u16 reg, u16 *value);
+ int (*write)(struct axgbe_port *pdata, u16 reg, u16 value);
};
struct axgbe_phy_if {
diff --git a/drivers/net/axgbe/axgbe_phy_impl.c b/drivers/net/axgbe/axgbe_phy_impl.c
index 9249e11335..cbab2908b4 100644
--- a/drivers/net/axgbe/axgbe_phy_impl.c
+++ b/drivers/net/axgbe/axgbe_phy_impl.c
@@ -203,7 +203,7 @@ struct axgbe_phy_data {
unsigned int rrc_count;
- unsigned int mdio_addr;
+ uint8_t mdio_addr;
/* SFP Support */
enum axgbe_sfp_comm sfp_comm;
@@ -251,6 +251,8 @@ 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_phy_get_comm_ownership(struct axgbe_port *pdata);
+static void axgbe_phy_put_comm_ownership(struct axgbe_port *pdata);
static int axgbe_phy_i2c_xfer(struct axgbe_port *pdata,
struct axgbe_i2c_op *i2c_op)
@@ -258,6 +260,44 @@ static int axgbe_phy_i2c_xfer(struct axgbe_port *pdata,
return pdata->i2c_if.i2c_xfer(pdata, i2c_op);
}
+static int axgbe_phy_read(struct axgbe_port *pdata, u16 reg, u16 *value)
+{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ int ret;
+
+ ret = axgbe_phy_get_comm_ownership(pdata);
+ if (ret)
+ return ret;
+
+ ret = pdata->hw_if.read_ext_mii_regs_c22(pdata,
+ phy_data->mdio_addr, reg, value);
+ if (ret)
+ PMD_DRV_LOG_LINE(ERR, "mdio read failed %s",
+ strerror(-ret));
+
+ axgbe_phy_put_comm_ownership(pdata);
+ return ret;
+}
+
+static int axgbe_phy_write(struct axgbe_port *pdata, u16 reg, u16 value)
+{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ int ret;
+ ret = axgbe_phy_get_comm_ownership(pdata);
+ if (ret)
+ return ret;
+
+ ret = pdata->hw_if.write_ext_mii_regs_c22(pdata,
+ phy_data->mdio_addr, reg, value);
+ if (ret)
+ PMD_DRV_LOG_LINE(ERR, "mdio write failed %s",
+ strerror(-ret));
+
+ axgbe_phy_put_comm_ownership(pdata);
+ return ret;
+}
+
+
static int axgbe_phy_redrv_write(struct axgbe_port *pdata, unsigned int reg,
unsigned int val)
{
@@ -2542,4 +2582,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_read;
+ phy_impl->write = axgbe_phy_write;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v5 2/3] net/axgbe: add support for marvell m88e1512 PHY
2026-02-27 8:44 ` [PATCH v4 " Ashok Kumar Natarajan
2026-02-27 23:25 ` Stephen Hemminger
@ 2026-02-28 14:39 ` Ashok Kumar Natarajan
1 sibling, 0 replies; 23+ messages in thread
From: Ashok Kumar Natarajan @ 2026-02-28 14:39 UTC (permalink / raw)
To: dev; +Cc: Selwin.Sebastian, Ashok Kumar Natarajan
Add support for the Marvell M88E1512 PHY in the axgbe driver. This patch
implements device identification and basic initialization required to
operate with this PHY. Only the M88E1512 model is supported in this
update.
Signed-off-by: Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com>
---
drivers/net/axgbe/axgbe_phy.h | 44 ++++
drivers/net/axgbe/axgbe_phy_impl.c | 391 ++++++++++++++++++++++++++++-
2 files changed, 431 insertions(+), 4 deletions(-)
diff --git a/drivers/net/axgbe/axgbe_phy.h b/drivers/net/axgbe/axgbe_phy.h
index eee3afc370..1bedd8e132 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,26 @@
#define MDIO_AN_10GBT_CTRL 32 /* 10GBASE-T auto-negotiation control */
#define MDIO_AN_10GBT_STAT 33 /* 10GBASE-T auto-negotiation status */
+#define M88E1512_E_PHY_ID 0x01410DD0
+#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_PHY_PAGE 22
+#define AXGBE_M88E1512_COPPER_PAGE 0x00
+#define AXGBE_M88E1512_LED_PAGE 0x03
+#define AXGBE_M88E1512_MODE_PAGE 0x12
+
+#define AXGBE_M88E1512_MODE_RGMII_COPPER 0
+#define AXGBE_M88E1512_MODE_SGMII_COPPER 1
+#define AXGBE_M88E1512_MODE_RGMII_1000BX 2
+#define AXGBE_M88E1512_MODE_RGMII_100BFX 3
+#define AXGBE_M88E1512_MODE_RGMII_SGMII 4
+#define AXGBE_M88E1512_MODE_SW_RESET 0x8000
+
+
/* Control register 1. */
/* Enable extended speed selection */
#define MDIO_CTRL1_SPEEDSELEXT (BMCR_SPEED1000 | BMCR_SPEED100)
@@ -190,5 +233,6 @@
#define DUPLEX_FULL 0x01
#define DUPLEX_UNKNOWN 0xff
+#define AXGBE_PHY_REVISION_MASK 0xfffffff0
#endif
/* PHY */
diff --git a/drivers/net/axgbe/axgbe_phy_impl.c b/drivers/net/axgbe/axgbe_phy_impl.c
index cbab2908b4..e180fd28e1 100644
--- a/drivers/net/axgbe/axgbe_phy_impl.c
+++ b/drivers/net/axgbe/axgbe_phy_impl.c
@@ -3,6 +3,7 @@
* Copyright(c) 2018 Synopsys, Inc. All rights reserved.
*/
+#include <rte_cycles.h>
#include "axgbe_ethdev.h"
#include "axgbe_common.h"
#include "axgbe_phy.h"
@@ -204,6 +205,7 @@ struct axgbe_phy_data {
unsigned int rrc_count;
uint8_t mdio_addr;
+ uint32_t phy_id;
/* SFP Support */
enum axgbe_sfp_comm sfp_comm;
@@ -253,6 +255,8 @@ static void axgbe_phy_rrc(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_get_ext_phy_link_status(struct axgbe_port *pdata,
+ bool *linkup);
static int axgbe_phy_i2c_xfer(struct axgbe_port *pdata,
struct axgbe_i2c_op *i2c_op)
@@ -297,6 +301,83 @@ static int axgbe_phy_write(struct axgbe_port *pdata, u16 reg, u16 value)
return ret;
}
+static int axgbe_phy_config_advert(struct axgbe_port *pdata)
+{
+ u16 advert;
+ int ret;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_ADVERTISE, &advert);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to read ADVERTISE register");
+ return ret;
+ }
+
+ advert |= ADVERTISE_FULL;
+ advert |= ADVERTISE_PAUSE_CAP;
+
+ ret = pdata->phy_if.phy_impl.write(pdata, MII_ADVERTISE, advert);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to write ADVERTISE register");
+ return ret;
+ }
+ return 0;
+}
+
+static int axgbe_phy_set_speed(struct axgbe_port *pdata)
+{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ u16 bmcr;
+ int ret;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMCR, &bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to read BMCR register");
+ return ret;
+ }
+
+ 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;
+ }
+
+ ret = pdata->phy_if.phy_impl.write(pdata, MII_BMCR, bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to write BMCR register");
+ return ret;
+ }
+ return 0;
+}
+
+static int axgbe_phy_config_aneg(struct axgbe_port *pdata)
+{
+ u16 bmcr;
+ int ret;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMCR, &bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to read BMCR register");
+ return ret;
+ }
+
+ bmcr &= ~(BMCR_ANENABLE);
+ if (pdata->phy.autoneg == AUTONEG_ENABLE)
+ bmcr |= BMCR_ANENABLE | BMCR_ANRESTART;
+
+ ret = pdata->phy_if.phy_impl.write(pdata, MII_BMCR, bmcr);
+
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to write BMCR register");
+ return ret;
+ }
+ return 0;
+}
static int axgbe_phy_redrv_write(struct axgbe_port *pdata, unsigned int reg,
unsigned int val)
@@ -1132,10 +1213,35 @@ static unsigned int axgbe_phy_an_advertising(struct axgbe_port *pdata)
static int axgbe_phy_an_config(struct axgbe_port *pdata __rte_unused)
{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ int ret;
+
+ /* Auto-negotiation config is implemented only for 1000BASE-T */
+ if (phy_data->port_mode != AXGBE_PORT_MODE_1000BASE_T)
+ return 0;
+
+ /* Supports only Marvell M88E1512 PHY for now*/
+ if (phy_data->phy_id == M88E1512_E_PHY_ID) {
+ ret = axgbe_phy_config_advert(pdata);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY ADVERTISE config failed");
+ return ret;
+ }
+
+ ret = axgbe_phy_set_speed(pdata);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY set speed failed");
+ return ret;
+ }
+
+ ret = axgbe_phy_config_aneg(pdata);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY AN config failed");
+ return ret;
+ }
+ PMD_DRV_LOG_LINE(DEBUG, "M88E1512 PHY AN config done");
+ }
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)
@@ -1880,10 +1986,12 @@ static bool axgbe_phy_use_mode(struct axgbe_port *pdata, enum axgbe_mode mode)
}
}
+/* return 0 means link down, 1 means link up */
static int axgbe_phy_link_status(struct axgbe_port *pdata, int *an_restart)
{
struct axgbe_phy_data *phy_data = pdata->phy_data;
- unsigned int reg;
+ uint32_t reg;
+ bool ext_phy_link_up = false;
*an_restart = 0;
@@ -1903,6 +2011,26 @@ static int axgbe_phy_link_status(struct axgbe_port *pdata, int *an_restart)
}
}
+ if (phy_data->port_mode == AXGBE_PORT_MODE_1000BASE_T) {
+ if (phy_data->phy_id == M88E1512_E_PHY_ID) {
+ if (axgbe_get_ext_phy_link_status(pdata,
+ &ext_phy_link_up)) {
+ PMD_DRV_LOG_LINE(ERR,
+ "Get link status failed");
+ goto out;
+ }
+
+ if (ext_phy_link_up) {
+ 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
*/
@@ -1946,6 +2074,7 @@ static int axgbe_phy_link_status(struct axgbe_port *pdata, int *an_restart)
axgbe_phy_rrc(pdata);
}
+out:
return 0;
}
@@ -2303,6 +2432,243 @@ static int axgbe_phy_reset(struct axgbe_port *pdata)
return 0;
}
+static int axgbe_get_phy_id(struct axgbe_port *pdata)
+{
+ struct axgbe_phy_data *phy_data = pdata->phy_data;
+ u16 phy_id_1;
+ u16 phy_id_2;
+ int ret;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_PHYSID1,
+ &phy_id_1);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to read PHYSID1 register");
+ return ret;
+ }
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_PHYSID2,
+ &phy_id_2);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to read PHYSID2 register");
+ return ret;
+ }
+
+ phy_data->phy_id = (u32)phy_id_1 << 16;
+ phy_data->phy_id |= phy_id_2;
+ phy_data->phy_id &= AXGBE_PHY_REVISION_MASK;
+
+ if (phy_data->phy_id == AXGBE_PHY_REVISION_MASK) {
+ PMD_DRV_LOG_LINE(ERR, "Invalid PHY id (0x%x)",
+ phy_data->phy_id);
+ return -EINVAL;
+ }
+ PMD_DRV_LOG_LINE(DEBUG, "PHY id (0x%x)", phy_data->phy_id);
+ return 0;
+}
+
+static int axgbe_phy_poll_reset(struct axgbe_port *pdata)
+{
+ const uint64_t timeout_ms = 2000;
+ const uint64_t interval_ms = 1;
+ const uint64_t hz = rte_get_timer_hz();
+ const uint64_t start = rte_get_timer_cycles();
+
+ u16 bmcr = 0;
+ int ret;
+
+ for (;;) {
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMCR, &bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY read BMCR failed");
+ return ret;
+ }
+ if (!(bmcr & BMCR_RESET))
+ return 0;
+
+ uint64_t elapsed_ms =
+ ((rte_get_timer_cycles() - start) * 1000) / hz;
+ if (elapsed_ms >= timeout_ms)
+ break;
+
+ rte_delay_us_block(interval_ms * 1000);
+ }
+ return -ETIMEDOUT;
+}
+
+
+static int axgbe_phy_soft_reset(struct axgbe_port *pdata)
+{
+ int ret;
+ u16 bmcr;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMCR, &bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to read BMCR register");
+ return ret;
+ }
+
+ bmcr |= BMCR_RESET;
+
+ ret = pdata->phy_if.phy_impl.write(pdata, MII_BMCR, bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Failed to write BMCR register");
+ return ret;
+ }
+
+ ret = axgbe_phy_poll_reset(pdata);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Poll for PHY reset done failed");
+ return ret;
+ }
+ return 0;
+}
+
+static int axgbe_m88e1512_set_page(struct axgbe_port *pdata, u16 page)
+{
+ int ret;
+
+ ret = pdata->phy_if.phy_impl.write(pdata,
+ AXGBE_M88E1512_PAGE_ADDR, page);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR,
+ "PHY set page failed for page 0x%x", page);
+ }
+ return ret;
+}
+
+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)
+{
+ int ret;
+ u8 i;
+
+ PMD_DRV_LOG_LINE(DEBUG, "Initialize M88E1512 phy");
+
+ /* Switch to PHY page 0xFF */
+ ret = axgbe_m88e1512_set_page(pdata, 0xff);
+ if (ret)
+ goto err_out;
+
+ /* Configure M88E1512 errata registers */
+ for (i = 0; i < ARRAY_SIZE(errata_vals); i++) {
+ ret = pdata->phy_if.phy_impl.write(pdata,
+ AXGBE_M88E1512_CFG_REG_2,
+ errata_vals[i].reg17);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Config M88E1512 errata failed");
+ goto err_set_copper_page;
+ }
+
+ ret = pdata->phy_if.phy_impl.write(pdata,
+ AXGBE_M88E1512_CFG_REG_1,
+ errata_vals[i].reg16);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "Config M88E1512 errata failed");
+ goto err_set_copper_page;
+ }
+ }
+
+ /* Switch to PHY page 0xFB */
+ ret = axgbe_m88e1512_set_page(pdata, 0xfb);
+ if (ret)
+ goto err_set_copper_page;
+
+ ret = pdata->phy_if.phy_impl.write(pdata,
+ AXGBE_M88E1512_CFG_REG_3, 0xC00D);
+ if (ret)
+ goto err_set_copper_page;
+
+ /* Switch to PHY copper page */
+ ret = axgbe_m88e1512_set_page(pdata, AXGBE_M88E1512_COPPER_PAGE);
+ if (ret)
+ goto err_out;
+
+ /* SGMII-to-Copper mode initialization */
+
+ /* Switch to PHY mode page */
+ ret = axgbe_m88e1512_set_page(pdata, AXGBE_M88E1512_MODE_PAGE);
+ if (ret)
+ goto err_set_copper_page;
+
+ ret = pdata->phy_if.phy_impl.write(pdata,
+ AXGBE_M88E1512_MODE,
+ (AXGBE_M88E1512_MODE_SW_RESET |
+ AXGBE_M88E1512_MODE_SGMII_COPPER));
+ if (ret)
+ goto err_set_copper_page;
+
+ /* Switch to PHY copper page */
+ ret = axgbe_m88e1512_set_page(pdata, AXGBE_M88E1512_COPPER_PAGE);
+ if (ret)
+ goto err_out;
+
+ ret = axgbe_phy_soft_reset(pdata);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "M88E1512 PHY soft reset failed");
+ goto err_out;
+ }
+
+ PMD_DRV_LOG_LINE(DEBUG, "M88E1512 phy init done");
+ return 0;
+
+err_set_copper_page:
+ /* Switch to PHY page 0 */
+ axgbe_m88e1512_set_page(pdata, AXGBE_M88E1512_COPPER_PAGE);
+err_out:
+ PMD_DRV_LOG_LINE(ERR, "M88E1512 PHY initialization failed");
+ return ret;
+}
+
+
+static int axgbe_get_ext_phy_link_status(struct axgbe_port *pdata, bool *linkup)
+{
+ int ret;
+ u16 bmcr;
+ u16 bmsr;
+
+ *linkup = false;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMCR, &bmcr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY read failed for BMCR register");
+ return ret;
+ }
+
+ /* Autoneg is being started, therefore disregard BMSR value and
+ * report link as down.
+ */
+ if (bmcr & BMCR_ANRESTART)
+ return 0;
+
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMSR, &bmsr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY read failed for BMSR register");
+ return ret;
+ }
+
+ if (bmsr & BMSR_LSTATUS)
+ goto done;
+
+ /* Read link status */
+ ret = pdata->phy_if.phy_impl.read(pdata, MII_BMSR, &bmsr);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "PHY read failed for BMSR register");
+ return ret;
+ }
+done:
+ *linkup = (bmsr & BMSR_LSTATUS) ? true : false;
+ return 0;
+}
+
+
static int axgbe_phy_init(struct axgbe_port *pdata)
{
struct axgbe_phy_data *phy_data;
@@ -2556,6 +2922,23 @@ static int axgbe_phy_init(struct axgbe_port *pdata)
}
phy_data->phy_cdr_delay = AXGBE_CDR_DELAY_INIT;
+
+ if (phy_data->port_mode == AXGBE_PORT_MODE_1000BASE_T) {
+ ret = axgbe_get_phy_id(pdata);
+ if (ret) {
+ PMD_DRV_LOG_LINE(ERR, "failed to get PHY id");
+ return ret;
+ }
+
+ PMD_DRV_LOG_LINE(DEBUG, "PHY ID = 0x%x", phy_data->phy_id);
+
+ if (phy_data->phy_id == M88E1512_E_PHY_ID) {
+ ret = axgbe_m88e1512_init(pdata);
+ if (ret)
+ return ret;
+ }
+ }
+
return 0;
}
void axgbe_init_function_ptrs_phy_v2(struct axgbe_phy_if *phy_if)
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* [PATCH v5 3/3] net/axgbe: fix 100M SGMII mode
2026-02-27 8:45 ` [PATCH v4 " Ashok Kumar Natarajan
@ 2026-02-28 14:40 ` Ashok Kumar Natarajan
0 siblings, 0 replies; 23+ messages in thread
From: Ashok Kumar Natarajan @ 2026-02-28 14:40 UTC (permalink / raw)
To: dev; +Cc: Selwin.Sebastian, Ashok Kumar Natarajan, stable
In SGMII 100M mode, set MAC speed to 100M instead of 1G,
and add the PHY implementation for SGMII 100 mode.
Fixes: a5c7273771e8 ("net/axgbe: add phy programming APIs")
Cc: stable@dpdk.org
Signed-off-by: Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com>
---
drivers/net/axgbe/axgbe_mdio.c | 4 ++--
drivers/net/axgbe/axgbe_phy_impl.c | 15 +++++++++++++++
2 files changed, 17 insertions(+), 2 deletions(-)
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_impl.c b/drivers/net/axgbe/axgbe_phy_impl.c
index e180fd28e1..369d766884 100644
--- a/drivers/net/axgbe/axgbe_phy_impl.c
+++ b/drivers/net/axgbe/axgbe_phy_impl.c
@@ -1643,6 +1643,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;
@@ -1840,6 +1852,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;
--
2.34.1
^ permalink raw reply related [flat|nested] 23+ messages in thread
* Re: [PATCH v5 1/3] net/axgbe: add external PHY read/write functions
2026-02-28 14:37 ` [PATCH v5 " Ashok Kumar Natarajan
@ 2026-02-28 16:41 ` Stephen Hemminger
2026-03-01 4:35 ` Natarajan, Ashok Kumar
0 siblings, 1 reply; 23+ messages in thread
From: Stephen Hemminger @ 2026-02-28 16:41 UTC (permalink / raw)
To: Ashok Kumar Natarajan; +Cc: dev, Selwin.Sebastian
On Sat, 28 Feb 2026 20:07:48 +0530
Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com> wrote:
> Introduce helper functions to perform external PHY register read and
> write operations. These helpers currently support only IEEE Clause 22
> PHY access, providing a simple and consistent API for accessing
> standard 16‑bit MII registers on external PHY devices.
>
> Signed-off-by: Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com>
> ---
> .mailmap | 1 +
> drivers/net/axgbe/axgbe_dev.c | 56 ++++++++++++++++--------------
> drivers/net/axgbe/axgbe_ethdev.h | 16 ++++++---
> drivers/net/axgbe/axgbe_phy_impl.c | 45 +++++++++++++++++++++++-
> 4 files changed, 85 insertions(+), 33 deletions(-)
Applied all three patches to next-net
^ permalink raw reply [flat|nested] 23+ messages in thread
* RE: [PATCH v5 1/3] net/axgbe: add external PHY read/write functions
2026-02-28 16:41 ` Stephen Hemminger
@ 2026-03-01 4:35 ` Natarajan, Ashok Kumar
0 siblings, 0 replies; 23+ messages in thread
From: Natarajan, Ashok Kumar @ 2026-03-01 4:35 UTC (permalink / raw)
To: Stephen Hemminger; +Cc: dev@dpdk.org, Sebastian, Selwin
[AMD Official Use Only - AMD Internal Distribution Only]
Thanks for applying the patches.
-----Original Message-----
From: Stephen Hemminger <stephen@networkplumber.org>
Sent: Saturday, February 28, 2026 10:11 PM
To: Natarajan, Ashok Kumar <AshokKumar.Natarajan@amd.com>
Cc: dev@dpdk.org; Sebastian, Selwin <Selwin.Sebastian@amd.com>
Subject: Re: [PATCH v5 1/3] net/axgbe: add external PHY read/write functions
Caution: This message originated from an External Source. Use proper caution when opening attachments, clicking links, or responding.
On Sat, 28 Feb 2026 20:07:48 +0530
Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com> wrote:
> Introduce helper functions to perform external PHY register read and
> write operations. These helpers currently support only IEEE Clause 22
> PHY access, providing a simple and consistent API for accessing
> standard 16‑bit MII registers on external PHY devices.
>
> Signed-off-by: Ashok Kumar Natarajan <ashokkumar.natarajan@amd.com>
> ---
> .mailmap | 1 +
> drivers/net/axgbe/axgbe_dev.c | 56 ++++++++++++++++--------------
> drivers/net/axgbe/axgbe_ethdev.h | 16 ++++++---
> drivers/net/axgbe/axgbe_phy_impl.c | 45 +++++++++++++++++++++++-
> 4 files changed, 85 insertions(+), 33 deletions(-)
Applied all three patches to next-net
^ permalink raw reply [flat|nested] 23+ messages in thread
end of thread, other threads:[~2026-03-01 4:36 UTC | newest]
Thread overview: 23+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-16 12:52 [PATCH 1/3] net/axgbe: Add external PHY read/write functions Ashok Kumar Natarajan
2026-02-16 12:52 ` [PATCH 2/3] net/axgbe: Add support for Marvell M88E1512 PHY Ashok Kumar Natarajan
2026-02-16 16:29 ` Stephen Hemminger
2026-02-25 12:47 ` [PATCH v2 2/3] net/axgbe: add support for marvell m88e1512 PHY Ashok Kumar Natarajan
2026-02-26 16:47 ` [PATCH v3 " Ashok Kumar Natarajan
2026-02-27 8:44 ` [PATCH v4 " Ashok Kumar Natarajan
2026-02-27 23:25 ` Stephen Hemminger
2026-02-28 14:39 ` [PATCH v5 " Ashok Kumar Natarajan
2026-02-16 12:52 ` [PATCH 3/3] net/axgbe: Add support for 100Mbps link speed Ashok Kumar Natarajan
2026-02-25 12:49 ` [PATCH v2 3/3] net/axgbe: add " Ashok Kumar Natarajan
2026-02-26 16:49 ` [PATCH v3 3/3] net/axgbe: fix 100M SGMII mode Ashok Kumar Natarajan
2026-02-27 8:45 ` [PATCH v4 " Ashok Kumar Natarajan
2026-02-28 14:40 ` [PATCH v5 " Ashok Kumar Natarajan
2026-02-17 0:27 ` [PATCH 1/3] net/axgbe: Add external PHY read/write functions Stephen Hemminger
2026-02-25 12:44 ` [PATCH v2 1/3] net/axgbe: add " Ashok Kumar Natarajan
2026-02-25 23:09 ` Stephen Hemminger
2026-02-26 17:09 ` Natarajan, Ashok Kumar
2026-02-26 16:46 ` [PATCH v3 " Ashok Kumar Natarajan
2026-02-26 19:09 ` Stephen Hemminger
2026-02-27 8:43 ` [PATCH v4 " Ashok Kumar Natarajan
2026-02-28 14:37 ` [PATCH v5 " Ashok Kumar Natarajan
2026-02-28 16:41 ` Stephen Hemminger
2026-03-01 4:35 ` Natarajan, Ashok Kumar
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox