net/axgbe: add phy init and related APIs
authorRavi Kumar <ravi1.kumar@amd.com>
Fri, 6 Apr 2018 12:36:38 +0000 (08:36 -0400)
committerFerruh Yigit <ferruh.yigit@intel.com>
Fri, 13 Apr 2018 22:41:44 +0000 (00:41 +0200)
Added device phy initialization, read/write and other
maintenance apis to be used within PMD.

Signed-off-by: Ravi Kumar <ravi1.kumar@amd.com>
drivers/net/axgbe/Makefile
drivers/net/axgbe/axgbe_dev.c
drivers/net/axgbe/axgbe_ethdev.c
drivers/net/axgbe/axgbe_ethdev.h
drivers/net/axgbe/axgbe_i2c.c [new file with mode: 0644]
drivers/net/axgbe/axgbe_mdio.c [new file with mode: 0644]
drivers/net/axgbe/axgbe_phy_impl.c [new file with mode: 0644]

index 6b8da12..efd9f0f 100644 (file)
@@ -24,5 +24,8 @@ LDLIBS += -lrte_ethdev
 #
 SRCS-$(CONFIG_RTE_LIBRTE_AXGBE_PMD) += axgbe_ethdev.c
 SRCS-$(CONFIG_RTE_LIBRTE_AXGBE_PMD) += axgbe_dev.c
+SRCS-$(CONFIG_RTE_LIBRTE_AXGBE_PMD) += axgbe_mdio.c
+SRCS-$(CONFIG_RTE_LIBRTE_AXGBE_PMD) += axgbe_phy_impl.c
+SRCS-$(CONFIG_RTE_LIBRTE_AXGBE_PMD) += axgbe_i2c.c
 
 include $(RTE_SDK)/mk/rte.lib.mk
index 70a796b..4a45ee6 100644 (file)
@@ -7,6 +7,187 @@
 #include "axgbe_common.h"
 #include "axgbe_phy.h"
 
+/* query busy bit */
+static int mdio_complete(struct axgbe_port *pdata)
+{
+       if (!AXGMAC_IOREAD_BITS(pdata, MAC_MDIOSCCDR, BUSY))
+               return 1;
+
+       return 0;
+}
+
+static int axgbe_write_ext_mii_regs(struct axgbe_port *pdata, int addr,
+                                   int reg, u16 val)
+{
+       unsigned int mdio_sca, mdio_sccd;
+       uint64_t timeout;
+
+       mdio_sca = 0;
+       AXGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, REG, reg);
+       AXGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, DA, addr);
+       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, CMD, 1);
+       AXGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, BUSY, 1);
+       AXGMAC_IOWRITE(pdata, MAC_MDIOSCCDR, mdio_sccd);
+
+       timeout = rte_get_timer_cycles() + rte_get_timer_hz();
+       while (time_before(rte_get_timer_cycles(), timeout)) {
+               rte_delay_us(100);
+               if (mdio_complete(pdata))
+                       return 0;
+       }
+
+       PMD_DRV_LOG(ERR, "Mdio write operation timed out\n");
+       return -ETIMEDOUT;
+}
+
+static int axgbe_read_ext_mii_regs(struct axgbe_port *pdata, int addr,
+                                  int reg)
+{
+       unsigned int mdio_sca, mdio_sccd;
+       uint64_t timeout;
+
+       mdio_sca = 0;
+       AXGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, REG, reg);
+       AXGMAC_SET_BITS(mdio_sca, MAC_MDIOSCAR, DA, addr);
+       AXGMAC_IOWRITE(pdata, MAC_MDIOSCAR, mdio_sca);
+
+       mdio_sccd = 0;
+       AXGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, CMD, 3);
+       AXGMAC_SET_BITS(mdio_sccd, MAC_MDIOSCCDR, BUSY, 1);
+       AXGMAC_IOWRITE(pdata, MAC_MDIOSCCDR, mdio_sccd);
+
+       timeout = rte_get_timer_cycles() + rte_get_timer_hz();
+
+       while (time_before(rte_get_timer_cycles(), timeout)) {
+               rte_delay_us(100);
+               if (mdio_complete(pdata))
+                       goto success;
+       }
+
+       PMD_DRV_LOG(ERR, "Mdio read operation timed out\n");
+       return -ETIMEDOUT;
+
+success:
+       return AXGMAC_IOREAD_BITS(pdata, MAC_MDIOSCCDR, DATA);
+}
+
+static int axgbe_set_ext_mii_mode(struct axgbe_port *pdata, unsigned int port,
+                                 enum axgbe_mdio_mode mode)
+{
+       unsigned int reg_val = 0;
+
+       switch (mode) {
+       case AXGBE_MDIO_MODE_CL22:
+               if (port > AXGMAC_MAX_C22_PORT)
+                       return -EINVAL;
+               reg_val |= (1 << port);
+               break;
+       case AXGBE_MDIO_MODE_CL45:
+               break;
+       default:
+               return -EINVAL;
+       }
+       AXGMAC_IOWRITE(pdata, MAC_MDIOCL22R, reg_val);
+
+       return 0;
+}
+
+static int axgbe_read_mmd_regs_v2(struct axgbe_port *pdata,
+                                 int prtad __rte_unused, int mmd_reg)
+{
+       unsigned int mmd_address, index, offset;
+       int mmd_data;
+
+       if (mmd_reg & MII_ADDR_C45)
+               mmd_address = mmd_reg & ~MII_ADDR_C45;
+       else
+               mmd_address = (pdata->mdio_mmd << 16) | (mmd_reg & 0xffff);
+
+       /* The PCS registers are accessed using mmio. The underlying
+        * management interface uses indirect addressing to access the MMD
+        * register sets. This requires accessing of the PCS register in two
+        * phases, an address phase and a data phase.
+        *
+        * The mmio interface is based on 16-bit offsets and values. All
+        * register offsets must therefore be adjusted by left shifting the
+        * offset 1 bit and reading 16 bits of data.
+        */
+       mmd_address <<= 1;
+       index = mmd_address & ~pdata->xpcs_window_mask;
+       offset = pdata->xpcs_window + (mmd_address & pdata->xpcs_window_mask);
+
+       pthread_mutex_lock(&pdata->xpcs_mutex);
+
+       XPCS32_IOWRITE(pdata, pdata->xpcs_window_sel_reg, index);
+       mmd_data = XPCS16_IOREAD(pdata, offset);
+
+       pthread_mutex_unlock(&pdata->xpcs_mutex);
+
+       return mmd_data;
+}
+
+static void axgbe_write_mmd_regs_v2(struct axgbe_port *pdata,
+                                   int prtad __rte_unused,
+                                   int mmd_reg, int mmd_data)
+{
+       unsigned int mmd_address, index, offset;
+
+       if (mmd_reg & MII_ADDR_C45)
+               mmd_address = mmd_reg & ~MII_ADDR_C45;
+       else
+               mmd_address = (pdata->mdio_mmd << 16) | (mmd_reg & 0xffff);
+
+       /* The PCS registers are accessed using mmio. The underlying
+        * management interface uses indirect addressing to access the MMD
+        * register sets. This requires accessing of the PCS register in two
+        * phases, an address phase and a data phase.
+        *
+        * The mmio interface is based on 16-bit offsets and values. All
+        * register offsets must therefore be adjusted by left shifting the
+        * offset 1 bit and writing 16 bits of data.
+        */
+       mmd_address <<= 1;
+       index = mmd_address & ~pdata->xpcs_window_mask;
+       offset = pdata->xpcs_window + (mmd_address & pdata->xpcs_window_mask);
+
+       pthread_mutex_lock(&pdata->xpcs_mutex);
+
+       XPCS32_IOWRITE(pdata, pdata->xpcs_window_sel_reg, index);
+       XPCS16_IOWRITE(pdata, offset, mmd_data);
+
+       pthread_mutex_unlock(&pdata->xpcs_mutex);
+}
+
+static int axgbe_read_mmd_regs(struct axgbe_port *pdata, int prtad,
+                              int mmd_reg)
+{
+       switch (pdata->vdata->xpcs_access) {
+       case AXGBE_XPCS_ACCESS_V1:
+               PMD_DRV_LOG(ERR, "PHY_Version 1 is not supported\n");
+               return -1;
+       case AXGBE_XPCS_ACCESS_V2:
+       default:
+               return axgbe_read_mmd_regs_v2(pdata, prtad, mmd_reg);
+       }
+}
+
+static void axgbe_write_mmd_regs(struct axgbe_port *pdata, int prtad,
+                                int mmd_reg, int mmd_data)
+{
+       switch (pdata->vdata->xpcs_access) {
+       case AXGBE_XPCS_ACCESS_V1:
+               PMD_DRV_LOG(ERR, "PHY_Version 1 is not supported\n");
+               return;
+       case AXGBE_XPCS_ACCESS_V2:
+       default:
+               return axgbe_write_mmd_regs_v2(pdata, prtad, mmd_reg, mmd_data);
+       }
+}
+
 static int __axgbe_exit(struct axgbe_port *pdata)
 {
        unsigned int count = 2000;
@@ -42,4 +223,11 @@ static int axgbe_exit(struct axgbe_port *pdata)
 void axgbe_init_function_ptrs_dev(struct axgbe_hw_if *hw_if)
 {
        hw_if->exit = axgbe_exit;
+
+       hw_if->read_mmd_regs = axgbe_read_mmd_regs;
+       hw_if->write_mmd_regs = axgbe_write_mmd_regs;
+
+       hw_if->set_ext_mii_mode = axgbe_set_ext_mii_mode;
+       hw_if->read_ext_mii_regs = axgbe_read_ext_mii_regs;
+       hw_if->write_ext_mii_regs = axgbe_write_ext_mii_regs;
 }
index 22228f4..d4cf279 100644 (file)
@@ -25,6 +25,7 @@ static const struct rte_pci_id pci_id_axgbe_map[] = {
 };
 
 static struct axgbe_version_data axgbe_v2a = {
+       .init_function_ptrs_phy_impl    = axgbe_init_function_ptrs_phy_v2,
        .xpcs_access                    = AXGBE_XPCS_ACCESS_V2,
        .mmc_64bit                      = 1,
        .tx_max_fifo_size               = 229376,
@@ -35,6 +36,7 @@ static struct axgbe_version_data axgbe_v2a = {
 };
 
 static struct axgbe_version_data axgbe_v2b = {
+       .init_function_ptrs_phy_impl    = axgbe_init_function_ptrs_phy_v2,
        .xpcs_access                    = AXGBE_XPCS_ACCESS_V2,
        .mmc_64bit                      = 1,
        .tx_max_fifo_size               = 65536,
@@ -149,6 +151,9 @@ static void axgbe_get_all_hw_features(struct axgbe_port *pdata)
 static void axgbe_init_all_fptrs(struct axgbe_port *pdata)
 {
        axgbe_init_function_ptrs_dev(&pdata->hw_if);
+       axgbe_init_function_ptrs_phy(&pdata->phy_if);
+       axgbe_init_function_ptrs_i2c(&pdata->i2c_if);
+       pdata->vdata->init_function_ptrs_phy_impl(&pdata->phy_if);
 }
 
 static void axgbe_set_counts(struct axgbe_port *pdata)
@@ -336,6 +341,12 @@ eth_axgbe_dev_init(struct rte_eth_dev *eth_dev)
        pthread_mutex_init(&pdata->an_mutex, NULL);
        pthread_mutex_init(&pdata->phy_mutex, NULL);
 
+       ret = pdata->phy_if.phy_init(pdata);
+       if (ret) {
+               rte_free(eth_dev->data->mac_addrs);
+               return ret;
+       }
+
        PMD_INIT_LOG(DEBUG, "port %d vendorID=0x%x deviceID=0x%x",
                     eth_dev->data->port_id, pci_dev->id.vendor_id,
                     pci_dev->id.device_id);
index effb132..17e9e41 100644 (file)
@@ -189,6 +189,61 @@ enum axgbe_mdio_mode {
        AXGBE_MDIO_MODE_CL45,
 };
 
+struct axgbe_phy {
+       uint32_t supported;
+       uint32_t advertising;
+       uint32_t lp_advertising;
+
+       int address;
+
+       int autoneg;
+       int speed;
+       int duplex;
+
+       int link;
+
+       int pause_autoneg;
+       int tx_pause;
+       int rx_pause;
+};
+
+enum axgbe_i2c_cmd {
+       AXGBE_I2C_CMD_READ = 0,
+       AXGBE_I2C_CMD_WRITE,
+};
+
+struct axgbe_i2c_op {
+       enum axgbe_i2c_cmd cmd;
+
+       unsigned int target;
+
+       uint8_t *buf;
+       unsigned int len;
+};
+
+struct axgbe_i2c_op_state {
+       struct axgbe_i2c_op *op;
+
+       unsigned int tx_len;
+       unsigned char *tx_buf;
+
+       unsigned int rx_len;
+       unsigned char *rx_buf;
+
+       unsigned int tx_abort_source;
+
+       int ret;
+};
+
+struct axgbe_i2c {
+       unsigned int started;
+       unsigned int max_speed_mode;
+       unsigned int rx_fifo_size;
+       unsigned int tx_fifo_size;
+
+       struct axgbe_i2c_op_state op_state;
+};
+
 struct axgbe_hw_if {
        void (*config_flow_control)(struct axgbe_port *);
        int (*config_rx_mode)(struct axgbe_port *);
@@ -211,6 +266,89 @@ struct axgbe_hw_if {
        int (*exit)(struct axgbe_port *);
 };
 
+/* This structure represents implementation specific routines for an
+ * implementation of a PHY. All routines are required unless noted below.
+ *   Optional routines:
+ *     kr_training_pre, kr_training_post
+ */
+struct axgbe_phy_impl_if {
+       /* Perform Setup/teardown actions */
+       int (*init)(struct axgbe_port *);
+       void (*exit)(struct axgbe_port *);
+
+       /* Perform start/stop specific actions */
+       int (*reset)(struct axgbe_port *);
+       int (*start)(struct axgbe_port *);
+       void (*stop)(struct axgbe_port *);
+
+       /* Return the link status */
+       int (*link_status)(struct axgbe_port *, int *);
+
+       /* Indicate if a particular speed is valid */
+       int (*valid_speed)(struct axgbe_port *, int);
+
+       /* Check if the specified mode can/should be used */
+       bool (*use_mode)(struct axgbe_port *, enum axgbe_mode);
+       /* Switch the PHY into various modes */
+       void (*set_mode)(struct axgbe_port *, enum axgbe_mode);
+       /* Retrieve mode needed for a specific speed */
+       enum axgbe_mode (*get_mode)(struct axgbe_port *, int);
+       /* Retrieve new/next mode when trying to auto-negotiate */
+       enum axgbe_mode (*switch_mode)(struct axgbe_port *);
+       /* Retrieve current mode */
+       enum axgbe_mode (*cur_mode)(struct axgbe_port *);
+
+       /* Retrieve current auto-negotiation mode */
+       enum axgbe_an_mode (*an_mode)(struct axgbe_port *);
+
+       /* Configure auto-negotiation settings */
+       int (*an_config)(struct axgbe_port *);
+
+       /* Set/override auto-negotiation advertisement settings */
+       unsigned int (*an_advertising)(struct axgbe_port *port);
+
+       /* Process results of auto-negotiation */
+       enum axgbe_mode (*an_outcome)(struct axgbe_port *);
+
+       /* Pre/Post KR training enablement support */
+       void (*kr_training_pre)(struct axgbe_port *);
+       void (*kr_training_post)(struct axgbe_port *);
+};
+
+struct axgbe_phy_if {
+       /* For PHY setup/teardown */
+       int (*phy_init)(struct axgbe_port *);
+       void (*phy_exit)(struct axgbe_port *);
+
+       /* For PHY support when setting device up/down */
+       int (*phy_reset)(struct axgbe_port *);
+       int (*phy_start)(struct axgbe_port *);
+       void (*phy_stop)(struct axgbe_port *);
+
+       /* For PHY support while device is up */
+       void (*phy_status)(struct axgbe_port *);
+       int (*phy_config_aneg)(struct axgbe_port *);
+
+       /* For PHY settings validation */
+       int (*phy_valid_speed)(struct axgbe_port *, int);
+       /* For single interrupt support */
+       void (*an_isr)(struct axgbe_port *);
+       /* PHY implementation specific services */
+       struct axgbe_phy_impl_if phy_impl;
+};
+
+struct axgbe_i2c_if {
+       /* For initial I2C setup */
+       int (*i2c_init)(struct axgbe_port *);
+
+       /* For I2C support when setting device up/down */
+       int (*i2c_start)(struct axgbe_port *);
+       void (*i2c_stop)(struct axgbe_port *);
+
+       /* For performing I2C operations */
+       int (*i2c_xfer)(struct axgbe_port *, struct axgbe_i2c_op *);
+};
+
 /* This structure contains flags that indicate what hardware features
  * or configurations are present in the device.
  */
@@ -258,6 +396,7 @@ struct axgbe_hw_features {
 };
 
 struct axgbe_version_data {
+       void (*init_function_ptrs_phy_impl)(struct axgbe_phy_if *);
        enum axgbe_xpcs_access xpcs_access;
        unsigned int mmc_64bit;
        unsigned int tx_max_fifo_size;
@@ -295,6 +434,8 @@ struct axgbe_port {
        unsigned long dev_state;
 
        struct axgbe_hw_if hw_if;
+       struct axgbe_phy_if phy_if;
+       struct axgbe_i2c_if i2c_if;
 
        /* AXI DMA settings */
        unsigned int coherent;
@@ -366,7 +507,38 @@ struct axgbe_port {
        struct axgbe_hw_features hw_feat;
 
        struct ether_addr mac_addr;
+
+       /* MDIO/PHY related settings */
+       unsigned int phy_started;
+       void *phy_data;
+       struct axgbe_phy phy;
+       int mdio_mmd;
+       unsigned long link_check;
+       volatile int mdio_completion;
+
+       unsigned int kr_redrv;
+
+       /* Auto-negotiation atate machine support */
+       unsigned int an_int;
+       unsigned int an_status;
+       enum axgbe_an an_result;
+       enum axgbe_an an_state;
+       enum axgbe_rx kr_state;
+       enum axgbe_rx kx_state;
+       unsigned int an_supported;
+       unsigned int parallel_detect;
+       unsigned int fec_ability;
+       unsigned long an_start;
+       enum axgbe_an_mode an_mode;
+
+       /* I2C support */
+       struct axgbe_i2c i2c;
+       volatile int i2c_complete;
 };
 
 void axgbe_init_function_ptrs_dev(struct axgbe_hw_if *hw_if);
+void axgbe_init_function_ptrs_phy(struct axgbe_phy_if *phy_if);
+void axgbe_init_function_ptrs_phy_v2(struct axgbe_phy_if *phy_if);
+void axgbe_init_function_ptrs_i2c(struct axgbe_i2c_if *i2c_if);
+
 #endif /* RTE_ETH_AXGBE_H_ */
diff --git a/drivers/net/axgbe/axgbe_i2c.c b/drivers/net/axgbe/axgbe_i2c.c
new file mode 100644 (file)
index 0000000..204ec36
--- /dev/null
@@ -0,0 +1,331 @@
+/*   SPDX-License-Identifier: BSD-3-Clause
+ *   Copyright(c) 2018 Advanced Micro Devices, Inc. All rights reserved.
+ *   Copyright(c) 2018 Synopsys, Inc. All rights reserved.
+ */
+
+#include "axgbe_ethdev.h"
+#include "axgbe_common.h"
+
+#define AXGBE_ABORT_COUNT      500
+#define AXGBE_DISABLE_COUNT    1000
+
+#define AXGBE_STD_SPEED                1
+
+#define AXGBE_INTR_RX_FULL     BIT(IC_RAW_INTR_STAT_RX_FULL_INDEX)
+#define AXGBE_INTR_TX_EMPTY    BIT(IC_RAW_INTR_STAT_TX_EMPTY_INDEX)
+#define AXGBE_INTR_TX_ABRT     BIT(IC_RAW_INTR_STAT_TX_ABRT_INDEX)
+#define AXGBE_INTR_STOP_DET    BIT(IC_RAW_INTR_STAT_STOP_DET_INDEX)
+#define AXGBE_DEFAULT_INT_MASK (AXGBE_INTR_RX_FULL  |  \
+                                AXGBE_INTR_TX_EMPTY |  \
+                                AXGBE_INTR_TX_ABRT  |  \
+                                AXGBE_INTR_STOP_DET)
+
+#define AXGBE_I2C_READ         BIT(8)
+#define AXGBE_I2C_STOP         BIT(9)
+
+static int axgbe_i2c_abort(struct axgbe_port *pdata)
+{
+       unsigned int wait = AXGBE_ABORT_COUNT;
+
+       /* Must be enabled to recognize the abort request */
+       XI2C_IOWRITE_BITS(pdata, IC_ENABLE, EN, 1);
+
+       /* Issue the abort */
+       XI2C_IOWRITE_BITS(pdata, IC_ENABLE, ABORT, 1);
+
+       while (wait--) {
+               if (!XI2C_IOREAD_BITS(pdata, IC_ENABLE, ABORT))
+                       return 0;
+               rte_delay_us(500);
+       }
+
+       return -EBUSY;
+}
+
+static int axgbe_i2c_set_enable(struct axgbe_port *pdata, bool enable)
+{
+       unsigned int wait = AXGBE_DISABLE_COUNT;
+       unsigned int mode = enable ? 1 : 0;
+
+       while (wait--) {
+               XI2C_IOWRITE_BITS(pdata, IC_ENABLE, EN, mode);
+               if (XI2C_IOREAD_BITS(pdata, IC_ENABLE_STATUS, EN) == mode)
+                       return 0;
+
+               rte_delay_us(100);
+       }
+
+       return -EBUSY;
+}
+
+static int axgbe_i2c_disable(struct axgbe_port *pdata)
+{
+       unsigned int ret;
+
+       ret = axgbe_i2c_set_enable(pdata, false);
+       if (ret) {
+               /* Disable failed, try an abort */
+               ret = axgbe_i2c_abort(pdata);
+               if (ret)
+                       return ret;
+
+               /* Abort succeeded, try to disable again */
+               ret = axgbe_i2c_set_enable(pdata, false);
+       }
+
+       return ret;
+}
+
+static int axgbe_i2c_enable(struct axgbe_port *pdata)
+{
+       return axgbe_i2c_set_enable(pdata, true);
+}
+
+static void axgbe_i2c_clear_all_interrupts(struct axgbe_port *pdata)
+{
+       XI2C_IOREAD(pdata, IC_CLR_INTR);
+}
+
+static void axgbe_i2c_disable_interrupts(struct axgbe_port *pdata)
+{
+       XI2C_IOWRITE(pdata, IC_INTR_MASK, 0);
+}
+
+static void axgbe_i2c_enable_interrupts(struct axgbe_port *pdata)
+{
+       XI2C_IOWRITE(pdata, IC_INTR_MASK, AXGBE_DEFAULT_INT_MASK);
+}
+
+static void axgbe_i2c_write(struct axgbe_port *pdata)
+{
+       struct axgbe_i2c_op_state *state = &pdata->i2c.op_state;
+       unsigned int tx_slots;
+       unsigned int cmd;
+
+       /* Configured to never receive Rx overflows, so fill up Tx fifo */
+       tx_slots = pdata->i2c.tx_fifo_size - XI2C_IOREAD(pdata, IC_TXFLR);
+       while (tx_slots && state->tx_len) {
+               if (state->op->cmd == AXGBE_I2C_CMD_READ)
+                       cmd = AXGBE_I2C_READ;
+               else
+                       cmd = *state->tx_buf++;
+
+               if (state->tx_len == 1)
+                       XI2C_SET_BITS(cmd, IC_DATA_CMD, STOP, 1);
+
+               XI2C_IOWRITE(pdata, IC_DATA_CMD, cmd);
+
+               tx_slots--;
+               state->tx_len--;
+       }
+
+       /* No more Tx operations, so ignore TX_EMPTY and return */
+       if (!state->tx_len)
+               XI2C_IOWRITE_BITS(pdata, IC_INTR_MASK, TX_EMPTY, 0);
+}
+
+static void axgbe_i2c_read(struct axgbe_port *pdata)
+{
+       struct axgbe_i2c_op_state *state = &pdata->i2c.op_state;
+       unsigned int rx_slots;
+
+       /* Anything to be read? */
+       if (state->op->cmd != AXGBE_I2C_CMD_READ)
+               return;
+
+       rx_slots = XI2C_IOREAD(pdata, IC_RXFLR);
+       while (rx_slots && state->rx_len) {
+               *state->rx_buf++ = XI2C_IOREAD(pdata, IC_DATA_CMD);
+               state->rx_len--;
+               rx_slots--;
+       }
+}
+
+static void axgbe_i2c_clear_isr_interrupts(struct axgbe_port *pdata,
+                                         unsigned int isr)
+{
+       struct axgbe_i2c_op_state *state = &pdata->i2c.op_state;
+
+       if (isr & AXGBE_INTR_TX_ABRT) {
+               state->tx_abort_source = XI2C_IOREAD(pdata, IC_TX_ABRT_SOURCE);
+               XI2C_IOREAD(pdata, IC_CLR_TX_ABRT);
+       }
+
+       if (isr & AXGBE_INTR_STOP_DET)
+               XI2C_IOREAD(pdata, IC_CLR_STOP_DET);
+}
+
+static int axgbe_i2c_isr(struct axgbe_port *pdata)
+{
+       struct axgbe_i2c_op_state *state = &pdata->i2c.op_state;
+       unsigned int isr;
+
+       isr = XI2C_IOREAD(pdata, IC_RAW_INTR_STAT);
+
+       axgbe_i2c_clear_isr_interrupts(pdata, isr);
+
+       if (isr & AXGBE_INTR_TX_ABRT) {
+               axgbe_i2c_disable_interrupts(pdata);
+
+               state->ret = -EIO;
+               goto out;
+       }
+
+       /* Check for data in the Rx fifo */
+       axgbe_i2c_read(pdata);
+
+       /* Fill up the Tx fifo next */
+       axgbe_i2c_write(pdata);
+
+out:
+       /* Complete on an error or STOP condition */
+       if (state->ret || XI2C_GET_BITS(isr, IC_RAW_INTR_STAT, STOP_DET))
+               return 1;
+
+       return 0;
+}
+
+static void axgbe_i2c_set_mode(struct axgbe_port *pdata)
+{
+       unsigned int reg;
+
+       reg = XI2C_IOREAD(pdata, IC_CON);
+       XI2C_SET_BITS(reg, IC_CON, MASTER_MODE, 1);
+       XI2C_SET_BITS(reg, IC_CON, SLAVE_DISABLE, 1);
+       XI2C_SET_BITS(reg, IC_CON, RESTART_EN, 1);
+       XI2C_SET_BITS(reg, IC_CON, SPEED, AXGBE_STD_SPEED);
+       XI2C_SET_BITS(reg, IC_CON, RX_FIFO_FULL_HOLD, 1);
+       XI2C_IOWRITE(pdata, IC_CON, reg);
+}
+
+static void axgbe_i2c_get_features(struct axgbe_port *pdata)
+{
+       struct axgbe_i2c *i2c = &pdata->i2c;
+       unsigned int reg;
+
+       reg = XI2C_IOREAD(pdata, IC_COMP_PARAM_1);
+       i2c->max_speed_mode = XI2C_GET_BITS(reg, IC_COMP_PARAM_1,
+                                           MAX_SPEED_MODE);
+       i2c->rx_fifo_size = XI2C_GET_BITS(reg, IC_COMP_PARAM_1,
+                                         RX_BUFFER_DEPTH);
+       i2c->tx_fifo_size = XI2C_GET_BITS(reg, IC_COMP_PARAM_1,
+                                         TX_BUFFER_DEPTH);
+}
+
+static void axgbe_i2c_set_target(struct axgbe_port *pdata, unsigned int addr)
+{
+       XI2C_IOWRITE(pdata, IC_TAR, addr);
+}
+
+static int axgbe_i2c_xfer(struct axgbe_port *pdata, struct axgbe_i2c_op *op)
+{
+       struct axgbe_i2c_op_state *state = &pdata->i2c.op_state;
+       int ret;
+       uint64_t timeout;
+
+       pthread_mutex_lock(&pdata->i2c_mutex);
+       ret = axgbe_i2c_disable(pdata);
+       if (ret) {
+               PMD_DRV_LOG(ERR, "failed to disable i2c master\n");
+               return ret;
+       }
+
+       axgbe_i2c_set_target(pdata, op->target);
+
+       memset(state, 0, sizeof(*state));
+       state->op = op;
+       state->tx_len = op->len;
+       state->tx_buf = (unsigned char *)op->buf;
+       state->rx_len = op->len;
+       state->rx_buf = (unsigned char *)op->buf;
+
+       axgbe_i2c_clear_all_interrupts(pdata);
+       ret = axgbe_i2c_enable(pdata);
+       if (ret) {
+               PMD_DRV_LOG(ERR, "failed to enable i2c master\n");
+               return ret;
+       }
+
+       /* Enabling the interrupts will cause the TX FIFO empty interrupt to
+        * fire and begin to process the command via the ISR.
+        */
+       axgbe_i2c_enable_interrupts(pdata);
+       timeout = rte_get_timer_cycles() + rte_get_timer_hz();
+
+       while (time_before(rte_get_timer_cycles(), timeout)) {
+               rte_delay_us(100);
+               if (XI2C_IOREAD(pdata, IC_RAW_INTR_STAT)) {
+                       if (axgbe_i2c_isr(pdata))
+                               goto success;
+               }
+       }
+
+       PMD_DRV_LOG(ERR, "i2c operation timed out\n");
+       axgbe_i2c_disable_interrupts(pdata);
+       axgbe_i2c_disable(pdata);
+       ret = -ETIMEDOUT;
+       goto unlock;
+
+success:
+       ret = state->ret;
+       if (ret) {
+               if (state->tx_abort_source & IC_TX_ABRT_7B_ADDR_NOACK)
+                       ret = -ENOTCONN;
+               else if (state->tx_abort_source & IC_TX_ABRT_ARB_LOST)
+                       ret = -EAGAIN;
+       }
+
+unlock:
+       pthread_mutex_unlock(&pdata->i2c_mutex);
+       return ret;
+}
+
+static void axgbe_i2c_stop(struct axgbe_port *pdata)
+{
+       if (!pdata->i2c.started)
+               return;
+
+       pdata->i2c.started = 0;
+       axgbe_i2c_disable_interrupts(pdata);
+       axgbe_i2c_disable(pdata);
+       axgbe_i2c_clear_all_interrupts(pdata);
+}
+
+static int axgbe_i2c_start(struct axgbe_port *pdata)
+{
+       if (pdata->i2c.started)
+               return 0;
+
+       pdata->i2c.started = 1;
+
+       return 0;
+}
+
+static int axgbe_i2c_init(struct axgbe_port *pdata)
+{
+       int ret;
+
+       axgbe_i2c_disable_interrupts(pdata);
+
+       ret = axgbe_i2c_disable(pdata);
+       if (ret) {
+               PMD_DRV_LOG(ERR, "failed to disable i2c master\n");
+               return ret;
+       }
+
+       axgbe_i2c_get_features(pdata);
+
+       axgbe_i2c_set_mode(pdata);
+
+       axgbe_i2c_clear_all_interrupts(pdata);
+
+       return 0;
+}
+
+void axgbe_init_function_ptrs_i2c(struct axgbe_i2c_if *i2c_if)
+{
+       i2c_if->i2c_init                = axgbe_i2c_init;
+       i2c_if->i2c_start               = axgbe_i2c_start;
+       i2c_if->i2c_stop                = axgbe_i2c_stop;
+       i2c_if->i2c_xfer                = axgbe_i2c_xfer;
+}
diff --git a/drivers/net/axgbe/axgbe_mdio.c b/drivers/net/axgbe/axgbe_mdio.c
new file mode 100644 (file)
index 0000000..691ff79
--- /dev/null
@@ -0,0 +1,81 @@
+/*   SPDX-License-Identifier: BSD-3-Clause
+ *   Copyright(c) 2018 Advanced Micro Devices, Inc. All rights reserved.
+ *   Copyright(c) 2018 Synopsys, Inc. All rights reserved.
+ */
+
+#include "axgbe_ethdev.h"
+#include "axgbe_common.h"
+#include "axgbe_phy.h"
+
+static int axgbe_phy_best_advertised_speed(struct axgbe_port *pdata)
+{
+       if (pdata->phy.advertising & ADVERTISED_10000baseKR_Full)
+               return SPEED_10000;
+       else if (pdata->phy.advertising & ADVERTISED_10000baseT_Full)
+               return SPEED_10000;
+       else if (pdata->phy.advertising & ADVERTISED_2500baseX_Full)
+               return SPEED_2500;
+       else if (pdata->phy.advertising & ADVERTISED_1000baseKX_Full)
+               return SPEED_1000;
+       else if (pdata->phy.advertising & ADVERTISED_1000baseT_Full)
+               return SPEED_1000;
+       else if (pdata->phy.advertising & ADVERTISED_100baseT_Full)
+               return SPEED_100;
+
+       return SPEED_UNKNOWN;
+}
+
+static int axgbe_phy_init(struct axgbe_port *pdata)
+{
+       int ret;
+
+       pdata->mdio_mmd = MDIO_MMD_PCS;
+
+       /* Check for FEC support */
+       pdata->fec_ability = XMDIO_READ(pdata, MDIO_MMD_PMAPMD,
+                                       MDIO_PMA_10GBR_FECABLE);
+       pdata->fec_ability &= (MDIO_PMA_10GBR_FECABLE_ABLE |
+                              MDIO_PMA_10GBR_FECABLE_ERRABLE);
+
+       /* Setup the phy (including supported features) */
+       ret = pdata->phy_if.phy_impl.init(pdata);
+       if (ret)
+               return ret;
+       pdata->phy.advertising = pdata->phy.supported;
+
+       pdata->phy.address = 0;
+
+       if (pdata->phy.advertising & ADVERTISED_Autoneg) {
+               pdata->phy.autoneg = AUTONEG_ENABLE;
+               pdata->phy.speed = SPEED_UNKNOWN;
+               pdata->phy.duplex = DUPLEX_UNKNOWN;
+       } else {
+               pdata->phy.autoneg = AUTONEG_DISABLE;
+               pdata->phy.speed = axgbe_phy_best_advertised_speed(pdata);
+               pdata->phy.duplex = DUPLEX_FULL;
+       }
+
+       pdata->phy.link = 0;
+
+       pdata->phy.pause_autoneg = pdata->pause_autoneg;
+       pdata->phy.tx_pause = pdata->tx_pause;
+       pdata->phy.rx_pause = pdata->rx_pause;
+
+       /* Fix up Flow Control advertising */
+       pdata->phy.advertising &= ~ADVERTISED_Pause;
+       pdata->phy.advertising &= ~ADVERTISED_Asym_Pause;
+
+       if (pdata->rx_pause) {
+               pdata->phy.advertising |= ADVERTISED_Pause;
+               pdata->phy.advertising |= ADVERTISED_Asym_Pause;
+       }
+
+       if (pdata->tx_pause)
+               pdata->phy.advertising ^= ADVERTISED_Asym_Pause;
+       return 0;
+}
+
+void axgbe_init_function_ptrs_phy(struct axgbe_phy_if *phy_if)
+{
+       phy_if->phy_init        = axgbe_phy_init;
+}
diff --git a/drivers/net/axgbe/axgbe_phy_impl.c b/drivers/net/axgbe/axgbe_phy_impl.c
new file mode 100644 (file)
index 0000000..047b619
--- /dev/null
@@ -0,0 +1,677 @@
+/*   SPDX-License-Identifier: BSD-3-Clause
+ *   Copyright(c) 2018 Advanced Micro Devices, Inc. All rights reserved.
+ *   Copyright(c) 2018 Synopsys, Inc. All rights reserved.
+ */
+
+#include "axgbe_ethdev.h"
+#include "axgbe_common.h"
+#include "axgbe_phy.h"
+
+#define AXGBE_PHY_PORT_SPEED_100       BIT(0)
+#define AXGBE_PHY_PORT_SPEED_1000      BIT(1)
+#define AXGBE_PHY_PORT_SPEED_2500      BIT(2)
+#define AXGBE_PHY_PORT_SPEED_10000     BIT(3)
+
+#define AXGBE_MUTEX_RELEASE            0x80000000
+
+#define AXGBE_SFP_DIRECT               7
+
+/* I2C target addresses */
+#define AXGBE_SFP_SERIAL_ID_ADDRESS    0x50
+#define AXGBE_SFP_DIAG_INFO_ADDRESS    0x51
+#define AXGBE_SFP_PHY_ADDRESS          0x56
+#define AXGBE_GPIO_ADDRESS_PCA9555     0x20
+
+/* SFP sideband signal indicators */
+#define AXGBE_GPIO_NO_TX_FAULT         BIT(0)
+#define AXGBE_GPIO_NO_RATE_SELECT      BIT(1)
+#define AXGBE_GPIO_NO_MOD_ABSENT       BIT(2)
+#define AXGBE_GPIO_NO_RX_LOS           BIT(3)
+
+/* Rate-change complete wait/retry count */
+#define AXGBE_RATECHANGE_COUNT         500
+
+enum axgbe_port_mode {
+       AXGBE_PORT_MODE_RSVD = 0,
+       AXGBE_PORT_MODE_BACKPLANE,
+       AXGBE_PORT_MODE_BACKPLANE_2500,
+       AXGBE_PORT_MODE_1000BASE_T,
+       AXGBE_PORT_MODE_1000BASE_X,
+       AXGBE_PORT_MODE_NBASE_T,
+       AXGBE_PORT_MODE_10GBASE_T,
+       AXGBE_PORT_MODE_10GBASE_R,
+       AXGBE_PORT_MODE_SFP,
+       AXGBE_PORT_MODE_MAX,
+};
+
+enum axgbe_conn_type {
+       AXGBE_CONN_TYPE_NONE = 0,
+       AXGBE_CONN_TYPE_SFP,
+       AXGBE_CONN_TYPE_MDIO,
+       AXGBE_CONN_TYPE_RSVD1,
+       AXGBE_CONN_TYPE_BACKPLANE,
+       AXGBE_CONN_TYPE_MAX,
+};
+
+/* SFP/SFP+ related definitions */
+enum axgbe_sfp_comm {
+       AXGBE_SFP_COMM_DIRECT = 0,
+       AXGBE_SFP_COMM_PCA9545,
+};
+
+enum axgbe_sfp_cable {
+       AXGBE_SFP_CABLE_UNKNOWN = 0,
+       AXGBE_SFP_CABLE_ACTIVE,
+       AXGBE_SFP_CABLE_PASSIVE,
+};
+
+enum axgbe_sfp_base {
+       AXGBE_SFP_BASE_UNKNOWN = 0,
+       AXGBE_SFP_BASE_1000_T,
+       AXGBE_SFP_BASE_1000_SX,
+       AXGBE_SFP_BASE_1000_LX,
+       AXGBE_SFP_BASE_1000_CX,
+       AXGBE_SFP_BASE_10000_SR,
+       AXGBE_SFP_BASE_10000_LR,
+       AXGBE_SFP_BASE_10000_LRM,
+       AXGBE_SFP_BASE_10000_ER,
+       AXGBE_SFP_BASE_10000_CR,
+};
+
+enum axgbe_sfp_speed {
+       AXGBE_SFP_SPEED_UNKNOWN = 0,
+       AXGBE_SFP_SPEED_100_1000,
+       AXGBE_SFP_SPEED_1000,
+       AXGBE_SFP_SPEED_10000,
+};
+
+/* SFP Serial ID Base ID values relative to an offset of 0 */
+#define AXGBE_SFP_BASE_ID                      0
+#define AXGBE_SFP_ID_SFP                       0x03
+
+#define AXGBE_SFP_BASE_EXT_ID                  1
+#define AXGBE_SFP_EXT_ID_SFP                   0x04
+
+#define AXGBE_SFP_BASE_10GBE_CC                        3
+#define AXGBE_SFP_BASE_10GBE_CC_SR             BIT(4)
+#define AXGBE_SFP_BASE_10GBE_CC_LR             BIT(5)
+#define AXGBE_SFP_BASE_10GBE_CC_LRM            BIT(6)
+#define AXGBE_SFP_BASE_10GBE_CC_ER             BIT(7)
+
+#define AXGBE_SFP_BASE_1GBE_CC                 6
+#define AXGBE_SFP_BASE_1GBE_CC_SX              BIT(0)
+#define AXGBE_SFP_BASE_1GBE_CC_LX              BIT(1)
+#define AXGBE_SFP_BASE_1GBE_CC_CX              BIT(2)
+#define AXGBE_SFP_BASE_1GBE_CC_T               BIT(3)
+
+#define AXGBE_SFP_BASE_CABLE                   8
+#define AXGBE_SFP_BASE_CABLE_PASSIVE           BIT(2)
+#define AXGBE_SFP_BASE_CABLE_ACTIVE            BIT(3)
+
+#define AXGBE_SFP_BASE_BR                      12
+#define AXGBE_SFP_BASE_BR_1GBE_MIN             0x0a
+#define AXGBE_SFP_BASE_BR_1GBE_MAX             0x0d
+#define AXGBE_SFP_BASE_BR_10GBE_MIN            0x64
+#define AXGBE_SFP_BASE_BR_10GBE_MAX            0x68
+
+#define AXGBE_SFP_BASE_CU_CABLE_LEN            18
+
+#define AXGBE_SFP_BASE_VENDOR_NAME             20
+#define AXGBE_SFP_BASE_VENDOR_NAME_LEN         16
+#define AXGBE_SFP_BASE_VENDOR_PN               40
+#define AXGBE_SFP_BASE_VENDOR_PN_LEN           16
+#define AXGBE_SFP_BASE_VENDOR_REV              56
+#define AXGBE_SFP_BASE_VENDOR_REV_LEN          4
+
+#define AXGBE_SFP_BASE_CC                      63
+
+/* SFP Serial ID Extended ID values relative to an offset of 64 */
+#define AXGBE_SFP_BASE_VENDOR_SN               4
+#define AXGBE_SFP_BASE_VENDOR_SN_LEN           16
+
+#define AXGBE_SFP_EXTD_DIAG                    28
+#define AXGBE_SFP_EXTD_DIAG_ADDR_CHANGE                BIT(2)
+
+#define AXGBE_SFP_EXTD_SFF_8472                        30
+
+#define AXGBE_SFP_EXTD_CC                      31
+
+struct axgbe_sfp_eeprom {
+       u8 base[64];
+       u8 extd[32];
+       u8 vendor[32];
+};
+
+#define AXGBE_BEL_FUSE_VENDOR  "BEL-FUSE"
+#define AXGBE_BEL_FUSE_PARTNO  "1GBT-SFP06"
+
+struct axgbe_sfp_ascii {
+       union {
+               char vendor[AXGBE_SFP_BASE_VENDOR_NAME_LEN + 1];
+               char partno[AXGBE_SFP_BASE_VENDOR_PN_LEN + 1];
+               char rev[AXGBE_SFP_BASE_VENDOR_REV_LEN + 1];
+               char serno[AXGBE_SFP_BASE_VENDOR_SN_LEN + 1];
+       } u;
+};
+
+/* MDIO PHY reset types */
+enum axgbe_mdio_reset {
+       AXGBE_MDIO_RESET_NONE = 0,
+       AXGBE_MDIO_RESET_I2C_GPIO,
+       AXGBE_MDIO_RESET_INT_GPIO,
+       AXGBE_MDIO_RESET_MAX,
+};
+
+/* Re-driver related definitions */
+enum axgbe_phy_redrv_if {
+       AXGBE_PHY_REDRV_IF_MDIO = 0,
+       AXGBE_PHY_REDRV_IF_I2C,
+       AXGBE_PHY_REDRV_IF_MAX,
+};
+
+enum axgbe_phy_redrv_model {
+       AXGBE_PHY_REDRV_MODEL_4223 = 0,
+       AXGBE_PHY_REDRV_MODEL_4227,
+       AXGBE_PHY_REDRV_MODEL_MAX,
+};
+
+enum axgbe_phy_redrv_mode {
+       AXGBE_PHY_REDRV_MODE_CX = 5,
+       AXGBE_PHY_REDRV_MODE_SR = 9,
+};
+
+#define AXGBE_PHY_REDRV_MODE_REG       0x12b0
+
+/* PHY related configuration information */
+struct axgbe_phy_data {
+       enum axgbe_port_mode port_mode;
+
+       unsigned int port_id;
+
+       unsigned int port_speeds;
+
+       enum axgbe_conn_type conn_type;
+
+       enum axgbe_mode cur_mode;
+       enum axgbe_mode start_mode;
+
+       unsigned int rrc_count;
+
+       unsigned int mdio_addr;
+
+       unsigned int comm_owned;
+
+       /* SFP Support */
+       enum axgbe_sfp_comm sfp_comm;
+       unsigned int sfp_mux_address;
+       unsigned int sfp_mux_channel;
+
+       unsigned int sfp_gpio_address;
+       unsigned int sfp_gpio_mask;
+       unsigned int sfp_gpio_rx_los;
+       unsigned int sfp_gpio_tx_fault;
+       unsigned int sfp_gpio_mod_absent;
+       unsigned int sfp_gpio_rate_select;
+
+       unsigned int sfp_rx_los;
+       unsigned int sfp_tx_fault;
+       unsigned int sfp_mod_absent;
+       unsigned int sfp_diags;
+       unsigned int sfp_changed;
+       unsigned int sfp_phy_avail;
+       unsigned int sfp_cable_len;
+       enum axgbe_sfp_base sfp_base;
+       enum axgbe_sfp_cable sfp_cable;
+       enum axgbe_sfp_speed sfp_speed;
+       struct axgbe_sfp_eeprom sfp_eeprom;
+
+       /* External PHY support */
+       enum axgbe_mdio_mode phydev_mode;
+       enum axgbe_mdio_reset mdio_reset;
+       unsigned int mdio_reset_addr;
+       unsigned int mdio_reset_gpio;
+
+       /* Re-driver support */
+       unsigned int redrv;
+       unsigned int redrv_if;
+       unsigned int redrv_addr;
+       unsigned int redrv_lane;
+       unsigned int redrv_model;
+};
+
+static void axgbe_phy_sfp_gpio_setup(struct axgbe_port *pdata)
+{
+       struct axgbe_phy_data *phy_data = pdata->phy_data;
+       unsigned int reg;
+
+       reg = XP_IOREAD(pdata, XP_PROP_3);
+
+       phy_data->sfp_gpio_address = AXGBE_GPIO_ADDRESS_PCA9555 +
+               XP_GET_BITS(reg, XP_PROP_3, GPIO_ADDR);
+
+       phy_data->sfp_gpio_mask = XP_GET_BITS(reg, XP_PROP_3, GPIO_MASK);
+
+       phy_data->sfp_gpio_rx_los = XP_GET_BITS(reg, XP_PROP_3,
+                                               GPIO_RX_LOS);
+       phy_data->sfp_gpio_tx_fault = XP_GET_BITS(reg, XP_PROP_3,
+                                                 GPIO_TX_FAULT);
+       phy_data->sfp_gpio_mod_absent = XP_GET_BITS(reg, XP_PROP_3,
+                                                   GPIO_MOD_ABS);
+       phy_data->sfp_gpio_rate_select = XP_GET_BITS(reg, XP_PROP_3,
+                                                    GPIO_RATE_SELECT);
+}
+
+static void axgbe_phy_sfp_comm_setup(struct axgbe_port *pdata)
+{
+       struct axgbe_phy_data *phy_data = pdata->phy_data;
+       unsigned int reg, mux_addr_hi, mux_addr_lo;
+
+       reg = XP_IOREAD(pdata, XP_PROP_4);
+
+       mux_addr_hi = XP_GET_BITS(reg, XP_PROP_4, MUX_ADDR_HI);
+       mux_addr_lo = XP_GET_BITS(reg, XP_PROP_4, MUX_ADDR_LO);
+       if (mux_addr_lo == AXGBE_SFP_DIRECT)
+               return;
+
+       phy_data->sfp_comm = AXGBE_SFP_COMM_PCA9545;
+       phy_data->sfp_mux_address = (mux_addr_hi << 2) + mux_addr_lo;
+       phy_data->sfp_mux_channel = XP_GET_BITS(reg, XP_PROP_4, MUX_CHAN);
+}
+
+static void axgbe_phy_sfp_setup(struct axgbe_port *pdata)
+{
+       axgbe_phy_sfp_comm_setup(pdata);
+       axgbe_phy_sfp_gpio_setup(pdata);
+}
+
+static bool axgbe_phy_redrv_error(struct axgbe_phy_data *phy_data)
+{
+       if (!phy_data->redrv)
+               return false;
+
+       if (phy_data->redrv_if >= AXGBE_PHY_REDRV_IF_MAX)
+               return true;
+
+       switch (phy_data->redrv_model) {
+       case AXGBE_PHY_REDRV_MODEL_4223:
+               if (phy_data->redrv_lane > 3)
+                       return true;
+               break;
+       case AXGBE_PHY_REDRV_MODEL_4227:
+               if (phy_data->redrv_lane > 1)
+                       return true;
+               break;
+       default:
+               return true;
+       }
+
+       return false;
+}
+
+static int axgbe_phy_mdio_reset_setup(struct axgbe_port *pdata)
+{
+       struct axgbe_phy_data *phy_data = pdata->phy_data;
+       unsigned int reg;
+
+       if (phy_data->conn_type != AXGBE_CONN_TYPE_MDIO)
+               return 0;
+       reg = XP_IOREAD(pdata, XP_PROP_3);
+       phy_data->mdio_reset = XP_GET_BITS(reg, XP_PROP_3, MDIO_RESET);
+       switch (phy_data->mdio_reset) {
+       case AXGBE_MDIO_RESET_NONE:
+       case AXGBE_MDIO_RESET_I2C_GPIO:
+       case AXGBE_MDIO_RESET_INT_GPIO:
+               break;
+       default:
+               PMD_DRV_LOG(ERR, "unsupported MDIO reset (%#x)\n",
+                           phy_data->mdio_reset);
+               return -EINVAL;
+       }
+       if (phy_data->mdio_reset == AXGBE_MDIO_RESET_I2C_GPIO) {
+               phy_data->mdio_reset_addr = AXGBE_GPIO_ADDRESS_PCA9555 +
+                       XP_GET_BITS(reg, XP_PROP_3,
+                                   MDIO_RESET_I2C_ADDR);
+               phy_data->mdio_reset_gpio = XP_GET_BITS(reg, XP_PROP_3,
+                                                       MDIO_RESET_I2C_GPIO);
+       } else if (phy_data->mdio_reset == AXGBE_MDIO_RESET_INT_GPIO) {
+               phy_data->mdio_reset_gpio = XP_GET_BITS(reg, XP_PROP_3,
+                                                       MDIO_RESET_INT_GPIO);
+       }
+
+       return 0;
+}
+
+static bool axgbe_phy_port_mode_mismatch(struct axgbe_port *pdata)
+{
+       struct axgbe_phy_data *phy_data = pdata->phy_data;
+
+       switch (phy_data->port_mode) {
+       case AXGBE_PORT_MODE_BACKPLANE:
+               if ((phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_1000) ||
+                   (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_10000))
+                       return false;
+               break;
+       case AXGBE_PORT_MODE_BACKPLANE_2500:
+               if (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_2500)
+                       return false;
+               break;
+       case AXGBE_PORT_MODE_1000BASE_T:
+               if ((phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_100) ||
+                   (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_1000))
+                       return false;
+               break;
+       case AXGBE_PORT_MODE_1000BASE_X:
+               if (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_1000)
+                       return false;
+               break;
+       case AXGBE_PORT_MODE_NBASE_T:
+               if ((phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_100) ||
+                   (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_1000) ||
+                   (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_2500))
+                       return false;
+               break;
+       case AXGBE_PORT_MODE_10GBASE_T:
+               if ((phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_100) ||
+                   (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_1000) ||
+                   (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_10000))
+                       return false;
+               break;
+       case AXGBE_PORT_MODE_10GBASE_R:
+               if (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_10000)
+                       return false;
+               break;
+       case AXGBE_PORT_MODE_SFP:
+               if ((phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_100) ||
+                   (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_1000) ||
+                   (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_10000))
+                       return false;
+               break;
+       default:
+               break;
+       }
+
+       return true;
+}
+
+static bool axgbe_phy_conn_type_mismatch(struct axgbe_port *pdata)
+{
+       struct axgbe_phy_data *phy_data = pdata->phy_data;
+
+       switch (phy_data->port_mode) {
+       case AXGBE_PORT_MODE_BACKPLANE:
+       case AXGBE_PORT_MODE_BACKPLANE_2500:
+               if (phy_data->conn_type == AXGBE_CONN_TYPE_BACKPLANE)
+                       return false;
+               break;
+       case AXGBE_PORT_MODE_1000BASE_T:
+       case AXGBE_PORT_MODE_1000BASE_X:
+       case AXGBE_PORT_MODE_NBASE_T:
+       case AXGBE_PORT_MODE_10GBASE_T:
+       case AXGBE_PORT_MODE_10GBASE_R:
+               if (phy_data->conn_type == AXGBE_CONN_TYPE_MDIO)
+                       return false;
+               break;
+       case AXGBE_PORT_MODE_SFP:
+               if (phy_data->conn_type == AXGBE_CONN_TYPE_SFP)
+                       return false;
+               break;
+       default:
+               break;
+       }
+
+       return true;
+}
+
+static bool axgbe_phy_port_enabled(struct axgbe_port *pdata)
+{
+       unsigned int reg;
+
+       reg = XP_IOREAD(pdata, XP_PROP_0);
+       if (!XP_GET_BITS(reg, XP_PROP_0, PORT_SPEEDS))
+               return false;
+       if (!XP_GET_BITS(reg, XP_PROP_0, CONN_TYPE))
+               return false;
+
+       return true;
+}
+
+static int axgbe_phy_init(struct axgbe_port *pdata)
+{
+       struct axgbe_phy_data *phy_data;
+       unsigned int reg;
+       int ret;
+
+       /* Check if enabled */
+       if (!axgbe_phy_port_enabled(pdata)) {
+               PMD_DRV_LOG(ERR, "device is not enabled\n");
+               return -ENODEV;
+       }
+
+       /* Initialize the I2C controller */
+       ret = pdata->i2c_if.i2c_init(pdata);
+       if (ret)
+               return ret;
+
+       phy_data = rte_zmalloc("phy_data memory", sizeof(*phy_data), 0);
+       if (!phy_data) {
+               PMD_DRV_LOG(ERR, "phy_data allocation failed\n");
+               return -ENOMEM;
+       }
+       pdata->phy_data = phy_data;
+
+       reg = XP_IOREAD(pdata, XP_PROP_0);
+       phy_data->port_mode = XP_GET_BITS(reg, XP_PROP_0, PORT_MODE);
+       phy_data->port_id = XP_GET_BITS(reg, XP_PROP_0, PORT_ID);
+       phy_data->port_speeds = XP_GET_BITS(reg, XP_PROP_0, PORT_SPEEDS);
+       phy_data->conn_type = XP_GET_BITS(reg, XP_PROP_0, CONN_TYPE);
+       phy_data->mdio_addr = XP_GET_BITS(reg, XP_PROP_0, MDIO_ADDR);
+
+       reg = XP_IOREAD(pdata, XP_PROP_4);
+       phy_data->redrv = XP_GET_BITS(reg, XP_PROP_4, REDRV_PRESENT);
+       phy_data->redrv_if = XP_GET_BITS(reg, XP_PROP_4, REDRV_IF);
+       phy_data->redrv_addr = XP_GET_BITS(reg, XP_PROP_4, REDRV_ADDR);
+       phy_data->redrv_lane = XP_GET_BITS(reg, XP_PROP_4, REDRV_LANE);
+       phy_data->redrv_model = XP_GET_BITS(reg, XP_PROP_4, REDRV_MODEL);
+
+       /* Validate the connection requested */
+       if (axgbe_phy_conn_type_mismatch(pdata)) {
+               PMD_DRV_LOG(ERR, "phy mode/connection mismatch (%#x/%#x)\n",
+                           phy_data->port_mode, phy_data->conn_type);
+               return -EINVAL;
+       }
+
+       /* Validate the mode requested */
+       if (axgbe_phy_port_mode_mismatch(pdata)) {
+               PMD_DRV_LOG(ERR, "phy mode/speed mismatch (%#x/%#x)\n",
+                           phy_data->port_mode, phy_data->port_speeds);
+               return -EINVAL;
+       }
+
+       /* Check for and validate MDIO reset support */
+       ret = axgbe_phy_mdio_reset_setup(pdata);
+       if (ret)
+               return ret;
+
+       /* Validate the re-driver information */
+       if (axgbe_phy_redrv_error(phy_data)) {
+               PMD_DRV_LOG(ERR, "phy re-driver settings error\n");
+               return -EINVAL;
+       }
+       pdata->kr_redrv = phy_data->redrv;
+
+       /* Indicate current mode is unknown */
+       phy_data->cur_mode = AXGBE_MODE_UNKNOWN;
+
+       /* Initialize supported features */
+       pdata->phy.supported = 0;
+
+       switch (phy_data->port_mode) {
+               /* Backplane support */
+       case AXGBE_PORT_MODE_BACKPLANE:
+               pdata->phy.supported |= SUPPORTED_Autoneg;
+               pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+               pdata->phy.supported |= SUPPORTED_Backplane;
+               if (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_1000) {
+                       pdata->phy.supported |= SUPPORTED_1000baseKX_Full;
+                       phy_data->start_mode = AXGBE_MODE_KX_1000;
+               }
+               if (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_10000) {
+                       pdata->phy.supported |= SUPPORTED_10000baseKR_Full;
+                       if (pdata->fec_ability & MDIO_PMA_10GBR_FECABLE_ABLE)
+                               pdata->phy.supported |=
+                                       SUPPORTED_10000baseR_FEC;
+                       phy_data->start_mode = AXGBE_MODE_KR;
+               }
+
+               phy_data->phydev_mode = AXGBE_MDIO_MODE_NONE;
+               break;
+       case AXGBE_PORT_MODE_BACKPLANE_2500:
+               pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+               pdata->phy.supported |= SUPPORTED_Backplane;
+               pdata->phy.supported |= SUPPORTED_2500baseX_Full;
+               phy_data->start_mode = AXGBE_MODE_KX_2500;
+
+               phy_data->phydev_mode = AXGBE_MDIO_MODE_NONE;
+               break;
+
+               /* MDIO 1GBase-T support */
+       case AXGBE_PORT_MODE_1000BASE_T:
+               pdata->phy.supported |= SUPPORTED_Autoneg;
+               pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+               pdata->phy.supported |= SUPPORTED_TP;
+               if (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_100) {
+                       pdata->phy.supported |= SUPPORTED_100baseT_Full;
+                       phy_data->start_mode = AXGBE_MODE_SGMII_100;
+               }
+               if (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_1000) {
+                       pdata->phy.supported |= SUPPORTED_1000baseT_Full;
+                       phy_data->start_mode = AXGBE_MODE_SGMII_1000;
+               }
+
+               phy_data->phydev_mode = AXGBE_MDIO_MODE_CL22;
+               break;
+
+               /* MDIO Base-X support */
+       case AXGBE_PORT_MODE_1000BASE_X:
+               pdata->phy.supported |= SUPPORTED_Autoneg;
+               pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+               pdata->phy.supported |= SUPPORTED_FIBRE;
+               pdata->phy.supported |= SUPPORTED_1000baseT_Full;
+               phy_data->start_mode = AXGBE_MODE_X;
+
+               phy_data->phydev_mode = AXGBE_MDIO_MODE_CL22;
+               break;
+
+               /* MDIO NBase-T support */
+       case AXGBE_PORT_MODE_NBASE_T:
+               pdata->phy.supported |= SUPPORTED_Autoneg;
+               pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+               pdata->phy.supported |= SUPPORTED_TP;
+               if (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_100) {
+                       pdata->phy.supported |= SUPPORTED_100baseT_Full;
+                       phy_data->start_mode = AXGBE_MODE_SGMII_100;
+               }
+               if (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_1000) {
+                       pdata->phy.supported |= SUPPORTED_1000baseT_Full;
+                       phy_data->start_mode = AXGBE_MODE_SGMII_1000;
+               }
+               if (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_2500) {
+                       pdata->phy.supported |= SUPPORTED_2500baseX_Full;
+                       phy_data->start_mode = AXGBE_MODE_KX_2500;
+               }
+
+               phy_data->phydev_mode = AXGBE_MDIO_MODE_CL45;
+               break;
+
+               /* 10GBase-T support */
+       case AXGBE_PORT_MODE_10GBASE_T:
+               pdata->phy.supported |= SUPPORTED_Autoneg;
+               pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+               pdata->phy.supported |= SUPPORTED_TP;
+               if (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_100) {
+                       pdata->phy.supported |= SUPPORTED_100baseT_Full;
+                       phy_data->start_mode = AXGBE_MODE_SGMII_100;
+               }
+               if (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_1000) {
+                       pdata->phy.supported |= SUPPORTED_1000baseT_Full;
+                       phy_data->start_mode = AXGBE_MODE_SGMII_1000;
+               }
+               if (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_10000) {
+                       pdata->phy.supported |= SUPPORTED_10000baseT_Full;
+                       phy_data->start_mode = AXGBE_MODE_KR;
+               }
+
+               phy_data->phydev_mode = AXGBE_MDIO_MODE_NONE;
+               break;
+
+               /* 10GBase-R support */
+       case AXGBE_PORT_MODE_10GBASE_R:
+               pdata->phy.supported |= SUPPORTED_Autoneg;
+               pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+               pdata->phy.supported |= SUPPORTED_TP;
+               pdata->phy.supported |= SUPPORTED_10000baseT_Full;
+               if (pdata->fec_ability & MDIO_PMA_10GBR_FECABLE_ABLE)
+                       pdata->phy.supported |= SUPPORTED_10000baseR_FEC;
+               phy_data->start_mode = AXGBE_MODE_SFI;
+
+               phy_data->phydev_mode = AXGBE_MDIO_MODE_NONE;
+               break;
+
+               /* SFP support */
+       case AXGBE_PORT_MODE_SFP:
+               pdata->phy.supported |= SUPPORTED_Autoneg;
+               pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+               pdata->phy.supported |= SUPPORTED_TP;
+               pdata->phy.supported |= SUPPORTED_FIBRE;
+               if (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_100) {
+                       pdata->phy.supported |= SUPPORTED_100baseT_Full;
+                       phy_data->start_mode = AXGBE_MODE_SGMII_100;
+               }
+               if (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_1000) {
+                       pdata->phy.supported |= SUPPORTED_1000baseT_Full;
+                       phy_data->start_mode = AXGBE_MODE_SGMII_1000;
+               }
+               if (phy_data->port_speeds & AXGBE_PHY_PORT_SPEED_10000) {
+                       pdata->phy.supported |= SUPPORTED_10000baseT_Full;
+                       phy_data->start_mode = AXGBE_MODE_SFI;
+                       if (pdata->fec_ability & MDIO_PMA_10GBR_FECABLE_ABLE)
+                               pdata->phy.supported |=
+                                       SUPPORTED_10000baseR_FEC;
+               }
+
+               phy_data->phydev_mode = AXGBE_MDIO_MODE_CL22;
+
+               axgbe_phy_sfp_setup(pdata);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if ((phy_data->conn_type & AXGBE_CONN_TYPE_MDIO) &&
+           (phy_data->phydev_mode != AXGBE_MDIO_MODE_NONE)) {
+               ret = pdata->hw_if.set_ext_mii_mode(pdata, phy_data->mdio_addr,
+                                                   phy_data->phydev_mode);
+               if (ret) {
+                       PMD_DRV_LOG(ERR, "mdio port/clause not compatible (%d/%u)\n",
+                                   phy_data->mdio_addr, phy_data->phydev_mode);
+                       return -EINVAL;
+               }
+       }
+
+       if (phy_data->redrv && !phy_data->redrv_if) {
+               ret = pdata->hw_if.set_ext_mii_mode(pdata, phy_data->redrv_addr,
+                                                   AXGBE_MDIO_MODE_CL22);
+               if (ret) {
+                       PMD_DRV_LOG(ERR, "redriver mdio port not compatible (%u)\n",
+                                   phy_data->redrv_addr);
+                       return -EINVAL;
+               }
+       }
+       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;
+
+       phy_impl->init                  = axgbe_phy_init;
+}