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

