amd-xgbe: Prepare for working with more than one type of phy
authorLendacky, Thomas <Thomas.Lendacky@amd.com>
Thu, 3 Nov 2016 18:18:27 +0000 (13:18 -0500)
committerDavid S. Miller <davem@davemloft.net>
Fri, 4 Nov 2016 18:48:43 +0000 (14:48 -0400)
Prepare the code to be able to work with more than one type of phy by
adding additional callable functions into the phy interface and removing
phy specific settings/functions from non-phy related files.

Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/amd/xgbe/Makefile
drivers/net/ethernet/amd/xgbe/xgbe-dev.c
drivers/net/ethernet/amd/xgbe/xgbe-drv.c
drivers/net/ethernet/amd/xgbe/xgbe-ethtool.c
drivers/net/ethernet/amd/xgbe/xgbe-main.c
drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c [new file with mode: 0644]
drivers/net/ethernet/amd/xgbe/xgbe.h

index 171a7e68048d79c43867aeb6646ff4262097966d..60b4ae2f05fbb72bde98065910f438105fe17dad 100644 (file)
@@ -2,7 +2,8 @@ obj-$(CONFIG_AMD_XGBE) += amd-xgbe.o
 
 amd-xgbe-objs := xgbe-main.o xgbe-drv.o xgbe-dev.o \
                 xgbe-desc.o xgbe-ethtool.o xgbe-mdio.o \
-                xgbe-ptp.o
+                xgbe-ptp.o \
+                xgbe-phy-v1.o
 
 amd-xgbe-$(CONFIG_AMD_XGBE_DCB) += xgbe-dcb.o
 amd-xgbe-$(CONFIG_DEBUG_FS) += xgbe-debugfs.o
index f8fffea6ced51b56d84041c5469d368e842b2a3e..75c3df1251c8ec6f8673aa056ee0c4dad0bb9f79 100644 (file)
@@ -717,32 +717,26 @@ static void xgbe_enable_mac_interrupts(struct xgbe_prv_data *pdata)
        XGMAC_IOWRITE_BITS(pdata, MMC_TIER, ALL_INTERRUPTS, 0xffffffff);
 }
 
-static int xgbe_set_gmii_speed(struct xgbe_prv_data *pdata)
+static int xgbe_set_speed(struct xgbe_prv_data *pdata, int speed)
 {
-       if (XGMAC_IOREAD_BITS(pdata, MAC_TCR, SS) == 0x3)
-               return 0;
-
-       XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, 0x3);
-
-       return 0;
-}
-
-static int xgbe_set_gmii_2500_speed(struct xgbe_prv_data *pdata)
-{
-       if (XGMAC_IOREAD_BITS(pdata, MAC_TCR, SS) == 0x2)
-               return 0;
+       unsigned int ss;
 
-       XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, 0x2);
-
-       return 0;
-}
-
-static int xgbe_set_xgmii_speed(struct xgbe_prv_data *pdata)
-{
-       if (XGMAC_IOREAD_BITS(pdata, MAC_TCR, SS) == 0)
-               return 0;
+       switch (speed) {
+       case SPEED_1000:
+               ss = 0x03;
+               break;
+       case SPEED_2500:
+               ss = 0x02;
+               break;
+       case SPEED_10000:
+               ss = 0x00;
+               break;
+       default:
+               return -EINVAL;
+       }
 
-       XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, 0);
+       if (XGMAC_IOREAD_BITS(pdata, MAC_TCR, SS) != ss)
+               XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, ss);
 
        return 0;
 }
@@ -2469,19 +2463,7 @@ static void xgbe_config_jumbo_enable(struct xgbe_prv_data *pdata)
 
 static void xgbe_config_mac_speed(struct xgbe_prv_data *pdata)
 {
-       switch (pdata->phy_speed) {
-       case SPEED_10000:
-               xgbe_set_xgmii_speed(pdata);
-               break;
-
-       case SPEED_2500:
-               xgbe_set_gmii_2500_speed(pdata);
-               break;
-
-       case SPEED_1000:
-               xgbe_set_gmii_speed(pdata);
-               break;
-       }
+       xgbe_set_speed(pdata, pdata->phy_speed);
 }
 
 static void xgbe_config_checksum_offload(struct xgbe_prv_data *pdata)
@@ -3195,9 +3177,7 @@ void xgbe_init_function_ptrs_dev(struct xgbe_hw_if *hw_if)
        hw_if->read_mmd_regs = xgbe_read_mmd_regs;
        hw_if->write_mmd_regs = xgbe_write_mmd_regs;
 
-       hw_if->set_gmii_speed = xgbe_set_gmii_speed;
-       hw_if->set_gmii_2500_speed = xgbe_set_gmii_2500_speed;
-       hw_if->set_xgmii_speed = xgbe_set_xgmii_speed;
+       hw_if->set_speed = xgbe_set_speed;
 
        hw_if->enable_tx = xgbe_enable_tx;
        hw_if->disable_tx = xgbe_disable_tx;
index c4e668208e0474eed26292626fdfeafbc048314a..dd166a003572fd164d119a80be29aaa9f0a857d0 100644 (file)
@@ -778,7 +778,7 @@ static void xgbe_free_rx_data(struct xgbe_prv_data *pdata)
        DBGPR("<--xgbe_free_rx_data\n");
 }
 
-static int xgbe_phy_init(struct xgbe_prv_data *pdata)
+static int xgbe_phy_reset(struct xgbe_prv_data *pdata)
 {
        pdata->phy_link = -1;
        pdata->phy_speed = SPEED_UNKNOWN;
@@ -1292,8 +1292,8 @@ static int xgbe_open(struct net_device *netdev)
 
        DBGPR("-->xgbe_open\n");
 
-       /* Initialize the phy */
-       ret = xgbe_phy_init(pdata);
+       /* Reset the phy settings */
+       ret = xgbe_phy_reset(pdata);
        if (ret)
                return ret;
 
index 4007b429c80c4a962c628199623028f32ac59792..46c959b994a79fe31e6df62fcf69564237026c2c 100644 (file)
@@ -316,24 +316,7 @@ static int xgbe_set_settings(struct net_device *netdev,
        }
 
        if (cmd->autoneg == AUTONEG_DISABLE) {
-               switch (speed) {
-               case SPEED_10000:
-                       break;
-               case SPEED_2500:
-                       if (pdata->speed_set != XGBE_SPEEDSET_2500_10000) {
-                               netdev_err(netdev, "unsupported speed %u\n",
-                                          speed);
-                               return -EINVAL;
-                       }
-                       break;
-               case SPEED_1000:
-                       if (pdata->speed_set != XGBE_SPEEDSET_1000_10000) {
-                               netdev_err(netdev, "unsupported speed %u\n",
-                                          speed);
-                               return -EINVAL;
-                       }
-                       break;
-               default:
+               if (!pdata->phy_if.phy_valid_speed(pdata, speed)) {
                        netdev_err(netdev, "unsupported speed %u\n", speed);
                        return -EINVAL;
                }
index 6997f1110ecebaf9e5e203c82146fd918acd01ad..38c5985c52ef239c780f4b4f3af20407fc15574d 100644 (file)
 #include <linux/of_net.h>
 #include <linux/of_address.h>
 #include <linux/of_platform.h>
+#include <linux/of_device.h>
 #include <linux/clk.h>
 #include <linux/property.h>
 #include <linux/acpi.h>
@@ -145,42 +146,6 @@ MODULE_PARM_DESC(debug, " Network interface message level setting");
 static const u32 default_msg_level = (NETIF_MSG_LINK | NETIF_MSG_IFDOWN |
                                      NETIF_MSG_IFUP);
 
-static const u32 xgbe_serdes_blwc[] = {
-       XGBE_SPEED_1000_BLWC,
-       XGBE_SPEED_2500_BLWC,
-       XGBE_SPEED_10000_BLWC,
-};
-
-static const u32 xgbe_serdes_cdr_rate[] = {
-       XGBE_SPEED_1000_CDR,
-       XGBE_SPEED_2500_CDR,
-       XGBE_SPEED_10000_CDR,
-};
-
-static const u32 xgbe_serdes_pq_skew[] = {
-       XGBE_SPEED_1000_PQ,
-       XGBE_SPEED_2500_PQ,
-       XGBE_SPEED_10000_PQ,
-};
-
-static const u32 xgbe_serdes_tx_amp[] = {
-       XGBE_SPEED_1000_TXAMP,
-       XGBE_SPEED_2500_TXAMP,
-       XGBE_SPEED_10000_TXAMP,
-};
-
-static const u32 xgbe_serdes_dfe_tap_cfg[] = {
-       XGBE_SPEED_1000_DFE_TAP_CONFIG,
-       XGBE_SPEED_2500_DFE_TAP_CONFIG,
-       XGBE_SPEED_10000_DFE_TAP_CONFIG,
-};
-
-static const u32 xgbe_serdes_dfe_tap_ena[] = {
-       XGBE_SPEED_1000_DFE_TAP_ENABLE,
-       XGBE_SPEED_2500_DFE_TAP_ENABLE,
-       XGBE_SPEED_10000_DFE_TAP_ENABLE,
-};
-
 static void xgbe_default_config(struct xgbe_prv_data *pdata)
 {
        DBGPR("-->xgbe_default_config\n");
@@ -207,9 +172,22 @@ static void xgbe_init_all_fptrs(struct xgbe_prv_data *pdata)
        xgbe_init_function_ptrs_dev(&pdata->hw_if);
        xgbe_init_function_ptrs_phy(&pdata->phy_if);
        xgbe_init_function_ptrs_desc(&pdata->desc_if);
+
+       pdata->vdata->init_function_ptrs_phy_impl(&pdata->phy_if);
 }
 
 #ifdef CONFIG_ACPI
+static const struct acpi_device_id xgbe_acpi_match[];
+
+static struct xgbe_version_data *xgbe_acpi_vdata(struct xgbe_prv_data *pdata)
+{
+       const struct acpi_device_id *id;
+
+       id = acpi_match_device(xgbe_acpi_match, pdata->dev);
+
+       return id ? (struct xgbe_version_data *)id->driver_data : NULL;
+}
+
 static int xgbe_acpi_support(struct xgbe_prv_data *pdata)
 {
        struct device *dev = pdata->dev;
@@ -237,6 +215,11 @@ static int xgbe_acpi_support(struct xgbe_prv_data *pdata)
        return 0;
 }
 #else   /* CONFIG_ACPI */
+static struct xgbe_version_data *xgbe_acpi_vdata(struct xgbe_prv_data *pdata)
+{
+       return NULL;
+}
+
 static int xgbe_acpi_support(struct xgbe_prv_data *pdata)
 {
        return -EINVAL;
@@ -244,6 +227,17 @@ static int xgbe_acpi_support(struct xgbe_prv_data *pdata)
 #endif  /* CONFIG_ACPI */
 
 #ifdef CONFIG_OF
+static const struct of_device_id xgbe_of_match[];
+
+static struct xgbe_version_data *xgbe_of_vdata(struct xgbe_prv_data *pdata)
+{
+       const struct of_device_id *id;
+
+       id = of_match_device(xgbe_of_match, pdata->dev);
+
+       return id ? (struct xgbe_version_data *)id->data : NULL;
+}
+
 static int xgbe_of_support(struct xgbe_prv_data *pdata)
 {
        struct device *dev = pdata->dev;
@@ -292,6 +286,11 @@ static struct platform_device *xgbe_of_get_phy_pdev(struct xgbe_prv_data *pdata)
        return phy_pdev;
 }
 #else   /* CONFIG_OF */
+static struct xgbe_version_data *xgbe_of_vdata(struct xgbe_prv_data *pdata)
+{
+       return NULL;
+}
+
 static int xgbe_of_support(struct xgbe_prv_data *pdata)
 {
        return -EINVAL;
@@ -333,11 +332,17 @@ static struct platform_device *xgbe_get_phy_pdev(struct xgbe_prv_data *pdata)
        return phy_pdev;
 }
 
+static struct xgbe_version_data *xgbe_get_vdata(struct xgbe_prv_data *pdata)
+{
+       return pdata->use_acpi ? xgbe_acpi_vdata(pdata)
+                              : xgbe_of_vdata(pdata);
+}
+
 static int xgbe_probe(struct platform_device *pdev)
 {
        struct xgbe_prv_data *pdata;
        struct net_device *netdev;
-       struct device *dev = &pdev->dev, *phy_dev;
+       struct device *dev = &pdev->dev;
        struct platform_device *phy_pdev;
        struct resource *res;
        const char *phy_mode;
@@ -374,13 +379,17 @@ static int xgbe_probe(struct platform_device *pdev)
        /* Check if we should use ACPI or DT */
        pdata->use_acpi = dev->of_node ? 0 : 1;
 
+       /* Get the version data */
+       pdata->vdata = xgbe_get_vdata(pdata);
+
        phy_pdev = xgbe_get_phy_pdev(pdata);
        if (!phy_pdev) {
                dev_err(dev, "unable to obtain phy device\n");
                ret = -EINVAL;
                goto err_phydev;
        }
-       phy_dev = &phy_pdev->dev;
+       pdata->phy_pdev = phy_pdev;
+       pdata->phy_dev = &phy_pdev->dev;
 
        if (pdev == phy_pdev) {
                /* New style device tree or ACPI:
@@ -492,115 +501,6 @@ static int xgbe_probe(struct platform_device *pdev)
        if (device_property_present(dev, XGBE_DMA_IRQS_PROPERTY))
                pdata->per_channel_irq = 1;
 
-       /* Retrieve the PHY speedset */
-       ret = device_property_read_u32(phy_dev, XGBE_SPEEDSET_PROPERTY,
-                                      &pdata->speed_set);
-       if (ret) {
-               dev_err(dev, "invalid %s property\n", XGBE_SPEEDSET_PROPERTY);
-               goto err_io;
-       }
-
-       switch (pdata->speed_set) {
-       case XGBE_SPEEDSET_1000_10000:
-       case XGBE_SPEEDSET_2500_10000:
-               break;
-       default:
-               dev_err(dev, "invalid %s property\n", XGBE_SPEEDSET_PROPERTY);
-               ret = -EINVAL;
-               goto err_io;
-       }
-
-       /* Retrieve the PHY configuration properties */
-       if (device_property_present(phy_dev, XGBE_BLWC_PROPERTY)) {
-               ret = device_property_read_u32_array(phy_dev,
-                                                    XGBE_BLWC_PROPERTY,
-                                                    pdata->serdes_blwc,
-                                                    XGBE_SPEEDS);
-               if (ret) {
-                       dev_err(dev, "invalid %s property\n",
-                               XGBE_BLWC_PROPERTY);
-                       goto err_io;
-               }
-       } else {
-               memcpy(pdata->serdes_blwc, xgbe_serdes_blwc,
-                      sizeof(pdata->serdes_blwc));
-       }
-
-       if (device_property_present(phy_dev, XGBE_CDR_RATE_PROPERTY)) {
-               ret = device_property_read_u32_array(phy_dev,
-                                                    XGBE_CDR_RATE_PROPERTY,
-                                                    pdata->serdes_cdr_rate,
-                                                    XGBE_SPEEDS);
-               if (ret) {
-                       dev_err(dev, "invalid %s property\n",
-                               XGBE_CDR_RATE_PROPERTY);
-                       goto err_io;
-               }
-       } else {
-               memcpy(pdata->serdes_cdr_rate, xgbe_serdes_cdr_rate,
-                      sizeof(pdata->serdes_cdr_rate));
-       }
-
-       if (device_property_present(phy_dev, XGBE_PQ_SKEW_PROPERTY)) {
-               ret = device_property_read_u32_array(phy_dev,
-                                                    XGBE_PQ_SKEW_PROPERTY,
-                                                    pdata->serdes_pq_skew,
-                                                    XGBE_SPEEDS);
-               if (ret) {
-                       dev_err(dev, "invalid %s property\n",
-                               XGBE_PQ_SKEW_PROPERTY);
-                       goto err_io;
-               }
-       } else {
-               memcpy(pdata->serdes_pq_skew, xgbe_serdes_pq_skew,
-                      sizeof(pdata->serdes_pq_skew));
-       }
-
-       if (device_property_present(phy_dev, XGBE_TX_AMP_PROPERTY)) {
-               ret = device_property_read_u32_array(phy_dev,
-                                                    XGBE_TX_AMP_PROPERTY,
-                                                    pdata->serdes_tx_amp,
-                                                    XGBE_SPEEDS);
-               if (ret) {
-                       dev_err(dev, "invalid %s property\n",
-                               XGBE_TX_AMP_PROPERTY);
-                       goto err_io;
-               }
-       } else {
-               memcpy(pdata->serdes_tx_amp, xgbe_serdes_tx_amp,
-                      sizeof(pdata->serdes_tx_amp));
-       }
-
-       if (device_property_present(phy_dev, XGBE_DFE_CFG_PROPERTY)) {
-               ret = device_property_read_u32_array(phy_dev,
-                                                    XGBE_DFE_CFG_PROPERTY,
-                                                    pdata->serdes_dfe_tap_cfg,
-                                                    XGBE_SPEEDS);
-               if (ret) {
-                       dev_err(dev, "invalid %s property\n",
-                               XGBE_DFE_CFG_PROPERTY);
-                       goto err_io;
-               }
-       } else {
-               memcpy(pdata->serdes_dfe_tap_cfg, xgbe_serdes_dfe_tap_cfg,
-                      sizeof(pdata->serdes_dfe_tap_cfg));
-       }
-
-       if (device_property_present(phy_dev, XGBE_DFE_ENA_PROPERTY)) {
-               ret = device_property_read_u32_array(phy_dev,
-                                                    XGBE_DFE_ENA_PROPERTY,
-                                                    pdata->serdes_dfe_tap_ena,
-                                                    XGBE_SPEEDS);
-               if (ret) {
-                       dev_err(dev, "invalid %s property\n",
-                               XGBE_DFE_ENA_PROPERTY);
-                       goto err_io;
-               }
-       } else {
-               memcpy(pdata->serdes_dfe_tap_ena, xgbe_serdes_dfe_tap_ena,
-                      sizeof(pdata->serdes_dfe_tap_ena));
-       }
-
        /* Obtain device settings unique to ACPI/OF */
        if (pdata->use_acpi)
                ret = xgbe_acpi_support(pdata);
@@ -705,7 +605,9 @@ static int xgbe_probe(struct platform_device *pdev)
        XGMAC_SET_BITS(pdata->rss_options, MAC_RSSCR, UDP4TE, 1);
 
        /* Call MDIO/PHY initialization routine */
-       pdata->phy_if.phy_init(pdata);
+       ret = pdata->phy_if.phy_init(pdata);
+       if (ret)
+               goto err_io;
 
        /* Set device operations */
        netdev->netdev_ops = xgbe_get_netdev_ops();
@@ -780,8 +682,6 @@ static int xgbe_probe(struct platform_device *pdev)
 
        xgbe_debugfs_init(pdata);
 
-       platform_device_put(phy_pdev);
-
        netdev_notice(netdev, "net device enabled\n");
 
        DBGPR("<-- xgbe_probe\n");
@@ -817,6 +717,8 @@ static int xgbe_remove(struct platform_device *pdev)
 
        xgbe_ptp_unregister(pdata);
 
+       pdata->phy_if.phy_exit(pdata);
+
        flush_workqueue(pdata->an_workqueue);
        destroy_workqueue(pdata->an_workqueue);
 
@@ -825,6 +727,8 @@ static int xgbe_remove(struct platform_device *pdev)
 
        unregister_netdev(netdev);
 
+       platform_device_put(pdata->phy_pdev);
+
        free_netdev(netdev);
 
        DBGPR("<--xgbe_remove\n");
@@ -879,9 +783,14 @@ static int xgbe_resume(struct device *dev)
 }
 #endif /* CONFIG_PM */
 
+static const struct xgbe_version_data xgbe_v1 = {
+       .init_function_ptrs_phy_impl    = xgbe_init_function_ptrs_phy_v1,
+};
+
 #ifdef CONFIG_ACPI
 static const struct acpi_device_id xgbe_acpi_match[] = {
-       { "AMDI8001", 0 },
+       { .id = "AMDI8001",
+         .driver_data = (kernel_ulong_t)&xgbe_v1 },
        {},
 };
 
@@ -890,7 +799,8 @@ MODULE_DEVICE_TABLE(acpi, xgbe_acpi_match);
 
 #ifdef CONFIG_OF
 static const struct of_device_id xgbe_of_match[] = {
-       { .compatible = "amd,xgbe-seattle-v1a", },
+       { .compatible = "amd,xgbe-seattle-v1a",
+         .data = &xgbe_v1 },
        {},
 };
 
index e9b01fc88098618d2cff66c9eaa65e90c984764c..35c302f7889cbfe72131d7fbad996206718b966f 100644 (file)
@@ -145,280 +145,91 @@ static void xgbe_an_disable_kr_training(struct xgbe_prv_data *pdata)
        XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, reg);
 }
 
-static void xgbe_pcs_power_cycle(struct xgbe_prv_data *pdata)
+static void xgbe_kr_mode(struct xgbe_prv_data *pdata)
 {
-       unsigned int reg;
-
-       reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1);
-
-       reg |= MDIO_CTRL1_LPOWER;
-       XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg);
-
-       usleep_range(75, 100);
-
-       reg &= ~MDIO_CTRL1_LPOWER;
-       XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg);
-}
-
-static void xgbe_serdes_start_ratechange(struct xgbe_prv_data *pdata)
-{
-       /* Assert Rx and Tx ratechange */
-       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, RATECHANGE, 1);
-}
-
-static void xgbe_serdes_complete_ratechange(struct xgbe_prv_data *pdata)
-{
-       unsigned int wait;
-       u16 status;
-
-       /* Release Rx and Tx ratechange */
-       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, RATECHANGE, 0);
-
-       /* Wait for Rx and Tx ready */
-       wait = XGBE_RATECHANGE_COUNT;
-       while (wait--) {
-               usleep_range(50, 75);
-
-               status = XSIR0_IOREAD(pdata, SIR0_STATUS);
-               if (XSIR_GET_BITS(status, SIR0_STATUS, RX_READY) &&
-                   XSIR_GET_BITS(status, SIR0_STATUS, TX_READY))
-                       goto rx_reset;
-       }
-
-       netif_dbg(pdata, link, pdata->netdev, "SerDes rx/tx not ready (%#hx)\n",
-                 status);
-
-rx_reset:
-       /* Perform Rx reset for the DFE changes */
-       XRXTX_IOWRITE_BITS(pdata, RXTX_REG6, RESETB_RXD, 0);
-       XRXTX_IOWRITE_BITS(pdata, RXTX_REG6, RESETB_RXD, 1);
-}
-
-static void xgbe_xgmii_mode(struct xgbe_prv_data *pdata)
-{
-       unsigned int reg;
-
        /* Enable KR training */
        xgbe_an_enable_kr_training(pdata);
 
        /* Set MAC to 10G speed */
-       pdata->hw_if.set_xgmii_speed(pdata);
-
-       /* Set PCS to KR/10G speed */
-       reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2);
-       reg &= ~MDIO_PCS_CTRL2_TYPE;
-       reg |= MDIO_PCS_CTRL2_10GBR;
-       XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL2, reg);
-
-       reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1);
-       reg &= ~MDIO_CTRL1_SPEEDSEL;
-       reg |= MDIO_CTRL1_SPEED10G;
-       XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg);
-
-       xgbe_pcs_power_cycle(pdata);
-
-       /* Set SerDes to 10G speed */
-       xgbe_serdes_start_ratechange(pdata);
-
-       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, DATARATE, XGBE_SPEED_10000_RATE);
-       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, WORDMODE, XGBE_SPEED_10000_WORD);
-       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PLLSEL, XGBE_SPEED_10000_PLL);
-
-       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, CDR_RATE,
-                          pdata->serdes_cdr_rate[XGBE_SPEED_10000]);
-       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, TXAMP,
-                          pdata->serdes_tx_amp[XGBE_SPEED_10000]);
-       XRXTX_IOWRITE_BITS(pdata, RXTX_REG20, BLWC_ENA,
-                          pdata->serdes_blwc[XGBE_SPEED_10000]);
-       XRXTX_IOWRITE_BITS(pdata, RXTX_REG114, PQ_REG,
-                          pdata->serdes_pq_skew[XGBE_SPEED_10000]);
-       XRXTX_IOWRITE_BITS(pdata, RXTX_REG129, RXDFE_CONFIG,
-                          pdata->serdes_dfe_tap_cfg[XGBE_SPEED_10000]);
-       XRXTX_IOWRITE(pdata, RXTX_REG22,
-                     pdata->serdes_dfe_tap_ena[XGBE_SPEED_10000]);
-
-       xgbe_serdes_complete_ratechange(pdata);
-
-       netif_dbg(pdata, link, pdata->netdev, "10GbE KR mode set\n");
+       pdata->hw_if.set_speed(pdata, SPEED_10000);
+
+       /* Call PHY implementation support to complete rate change */
+       pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_KR);
 }
 
-static void xgbe_gmii_2500_mode(struct xgbe_prv_data *pdata)
+static void xgbe_kx_2500_mode(struct xgbe_prv_data *pdata)
 {
-       unsigned int reg;
-
        /* Disable KR training */
        xgbe_an_disable_kr_training(pdata);
 
        /* Set MAC to 2.5G speed */
-       pdata->hw_if.set_gmii_2500_speed(pdata);
-
-       /* Set PCS to KX/1G speed */
-       reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2);
-       reg &= ~MDIO_PCS_CTRL2_TYPE;
-       reg |= MDIO_PCS_CTRL2_10GBX;
-       XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL2, reg);
-
-       reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1);
-       reg &= ~MDIO_CTRL1_SPEEDSEL;
-       reg |= MDIO_CTRL1_SPEED1G;
-       XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg);
-
-       xgbe_pcs_power_cycle(pdata);
-
-       /* Set SerDes to 2.5G speed */
-       xgbe_serdes_start_ratechange(pdata);
-
-       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, DATARATE, XGBE_SPEED_2500_RATE);
-       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, WORDMODE, XGBE_SPEED_2500_WORD);
-       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PLLSEL, XGBE_SPEED_2500_PLL);
-
-       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, CDR_RATE,
-                          pdata->serdes_cdr_rate[XGBE_SPEED_2500]);
-       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, TXAMP,
-                          pdata->serdes_tx_amp[XGBE_SPEED_2500]);
-       XRXTX_IOWRITE_BITS(pdata, RXTX_REG20, BLWC_ENA,
-                          pdata->serdes_blwc[XGBE_SPEED_2500]);
-       XRXTX_IOWRITE_BITS(pdata, RXTX_REG114, PQ_REG,
-                          pdata->serdes_pq_skew[XGBE_SPEED_2500]);
-       XRXTX_IOWRITE_BITS(pdata, RXTX_REG129, RXDFE_CONFIG,
-                          pdata->serdes_dfe_tap_cfg[XGBE_SPEED_2500]);
-       XRXTX_IOWRITE(pdata, RXTX_REG22,
-                     pdata->serdes_dfe_tap_ena[XGBE_SPEED_2500]);
-
-       xgbe_serdes_complete_ratechange(pdata);
-
-       netif_dbg(pdata, link, pdata->netdev, "2.5GbE KX mode set\n");
+       pdata->hw_if.set_speed(pdata, SPEED_2500);
+
+       /* Call PHY implementation support to complete rate change */
+       pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_KX_2500);
 }
 
-static void xgbe_gmii_mode(struct xgbe_prv_data *pdata)
+static void xgbe_kx_1000_mode(struct xgbe_prv_data *pdata)
 {
-       unsigned int reg;
-
        /* Disable KR training */
        xgbe_an_disable_kr_training(pdata);
 
        /* Set MAC to 1G speed */
-       pdata->hw_if.set_gmii_speed(pdata);
-
-       /* Set PCS to KX/1G speed */
-       reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2);
-       reg &= ~MDIO_PCS_CTRL2_TYPE;
-       reg |= MDIO_PCS_CTRL2_10GBX;
-       XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL2, reg);
-
-       reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1);
-       reg &= ~MDIO_CTRL1_SPEEDSEL;
-       reg |= MDIO_CTRL1_SPEED1G;
-       XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg);
-
-       xgbe_pcs_power_cycle(pdata);
-
-       /* Set SerDes to 1G speed */
-       xgbe_serdes_start_ratechange(pdata);
-
-       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, DATARATE, XGBE_SPEED_1000_RATE);
-       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, WORDMODE, XGBE_SPEED_1000_WORD);
-       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PLLSEL, XGBE_SPEED_1000_PLL);
-
-       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, CDR_RATE,
-                          pdata->serdes_cdr_rate[XGBE_SPEED_1000]);
-       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, TXAMP,
-                          pdata->serdes_tx_amp[XGBE_SPEED_1000]);
-       XRXTX_IOWRITE_BITS(pdata, RXTX_REG20, BLWC_ENA,
-                          pdata->serdes_blwc[XGBE_SPEED_1000]);
-       XRXTX_IOWRITE_BITS(pdata, RXTX_REG114, PQ_REG,
-                          pdata->serdes_pq_skew[XGBE_SPEED_1000]);
-       XRXTX_IOWRITE_BITS(pdata, RXTX_REG129, RXDFE_CONFIG,
-                          pdata->serdes_dfe_tap_cfg[XGBE_SPEED_1000]);
-       XRXTX_IOWRITE(pdata, RXTX_REG22,
-                     pdata->serdes_dfe_tap_ena[XGBE_SPEED_1000]);
-
-       xgbe_serdes_complete_ratechange(pdata);
-
-       netif_dbg(pdata, link, pdata->netdev, "1GbE KX mode set\n");
+       pdata->hw_if.set_speed(pdata, SPEED_1000);
+
+       /* Call PHY implementation support to complete rate change */
+       pdata->phy_if.phy_impl.set_mode(pdata, XGBE_MODE_KX_1000);
 }
 
-static void xgbe_cur_mode(struct xgbe_prv_data *pdata,
-                         enum xgbe_mode *mode)
+static enum xgbe_mode xgbe_cur_mode(struct xgbe_prv_data *pdata)
 {
-       unsigned int reg;
-
-       reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2);
-       if ((reg & MDIO_PCS_CTRL2_TYPE) == MDIO_PCS_CTRL2_10GBR)
-               *mode = XGBE_MODE_KR;
-       else
-               *mode = XGBE_MODE_KX;
+       return pdata->phy_if.phy_impl.cur_mode(pdata);
 }
 
 static bool xgbe_in_kr_mode(struct xgbe_prv_data *pdata)
 {
-       enum xgbe_mode mode;
-
-       xgbe_cur_mode(pdata, &mode);
-
-       return (mode == XGBE_MODE_KR);
+       return (xgbe_cur_mode(pdata) == XGBE_MODE_KR);
 }
 
-static void xgbe_switch_mode(struct xgbe_prv_data *pdata)
+static void xgbe_change_mode(struct xgbe_prv_data *pdata,
+                            enum xgbe_mode mode)
 {
-       /* If we are in KR switch to KX, and vice-versa */
-       if (xgbe_in_kr_mode(pdata)) {
-               if (pdata->speed_set == XGBE_SPEEDSET_1000_10000)
-                       xgbe_gmii_mode(pdata);
-               else
-                       xgbe_gmii_2500_mode(pdata);
-       } else {
-               xgbe_xgmii_mode(pdata);
+       switch (mode) {
+       case XGBE_MODE_KX_1000:
+               xgbe_kx_1000_mode(pdata);
+               break;
+       case XGBE_MODE_KX_2500:
+               xgbe_kx_2500_mode(pdata);
+               break;
+       case XGBE_MODE_KR:
+               xgbe_kr_mode(pdata);
+               break;
+       case XGBE_MODE_UNKNOWN:
+               break;
+       default:
+               netif_dbg(pdata, link, pdata->netdev,
+                         "invalid operation mode requested (%u)\n", mode);
        }
 }
 
-static void xgbe_set_mode(struct xgbe_prv_data *pdata,
-                         enum xgbe_mode mode)
-{
-       enum xgbe_mode cur_mode;
-
-       xgbe_cur_mode(pdata, &cur_mode);
-       if (mode != cur_mode)
-               xgbe_switch_mode(pdata);
-}
-
-static bool xgbe_use_xgmii_mode(struct xgbe_prv_data *pdata)
+static void xgbe_switch_mode(struct xgbe_prv_data *pdata)
 {
-       if (pdata->phy.autoneg == AUTONEG_ENABLE) {
-               if (pdata->phy.advertising & ADVERTISED_10000baseKR_Full)
-                       return true;
-       } else {
-               if (pdata->phy.speed == SPEED_10000)
-                       return true;
-       }
-
-       return false;
+       xgbe_change_mode(pdata, pdata->phy_if.phy_impl.switch_mode(pdata));
 }
 
-static bool xgbe_use_gmii_2500_mode(struct xgbe_prv_data *pdata)
+static void xgbe_set_mode(struct xgbe_prv_data *pdata,
+                         enum xgbe_mode mode)
 {
-       if (pdata->phy.autoneg == AUTONEG_ENABLE) {
-               if (pdata->phy.advertising & ADVERTISED_2500baseX_Full)
-                       return true;
-       } else {
-               if (pdata->phy.speed == SPEED_2500)
-                       return true;
-       }
+       if (mode == xgbe_cur_mode(pdata))
+               return;
 
-       return false;
+       xgbe_change_mode(pdata, mode);
 }
 
-static bool xgbe_use_gmii_mode(struct xgbe_prv_data *pdata)
+static bool xgbe_use_mode(struct xgbe_prv_data *pdata,
+                         enum xgbe_mode mode)
 {
-       if (pdata->phy.autoneg == AUTONEG_ENABLE) {
-               if (pdata->phy.advertising & ADVERTISED_1000baseKX_Full)
-                       return true;
-       } else {
-               if (pdata->phy.speed == SPEED_1000)
-                       return true;
-       }
-
-       return false;
+       return pdata->phy_if.phy_impl.use_mode(pdata, mode);
 }
 
 static void xgbe_set_an(struct xgbe_prv_data *pdata, bool enable, bool restart)
@@ -476,13 +287,15 @@ static enum xgbe_an xgbe_an_tx_training(struct xgbe_prv_data *pdata,
        /* Start KR training */
        reg = XMDIO_READ(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL);
        if (reg & XGBE_KR_TRAINING_ENABLE) {
-               XSIR0_IOWRITE_BITS(pdata, SIR0_KR_RT_1, RESET, 1);
+               if (pdata->phy_if.phy_impl.kr_training_pre)
+                       pdata->phy_if.phy_impl.kr_training_pre(pdata);
 
                reg |= XGBE_KR_TRAINING_START;
                XMDIO_WRITE(pdata, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL,
                            reg);
 
-               XSIR0_IOWRITE_BITS(pdata, SIR0_KR_RT_1, RESET, 0);
+               if (pdata->phy_if.phy_impl.kr_training_post)
+                       pdata->phy_if.phy_impl.kr_training_post(pdata);
 
                netif_dbg(pdata, link, pdata->netdev,
                          "KR training initiated\n");
@@ -571,7 +384,7 @@ static enum xgbe_an xgbe_an_page_received(struct xgbe_prv_data *pdata)
        }
 
        state = xgbe_in_kr_mode(pdata) ? &pdata->kr_state
-                                          : &pdata->kx_state;
+                                      : &pdata->kx_state;
 
        switch (*state) {
        case XGBE_RX_BPA:
@@ -907,24 +720,28 @@ static void xgbe_phy_adjust_link(struct xgbe_prv_data *pdata)
                xgbe_phy_print_status(pdata);
 }
 
+static bool xgbe_phy_valid_speed(struct xgbe_prv_data *pdata, int speed)
+{
+       return pdata->phy_if.phy_impl.valid_speed(pdata, speed);
+}
+
 static int xgbe_phy_config_fixed(struct xgbe_prv_data *pdata)
 {
+       enum xgbe_mode mode;
+
        netif_dbg(pdata, link, pdata->netdev, "fixed PHY configuration\n");
 
        /* Disable auto-negotiation */
        xgbe_disable_an(pdata);
 
-       /* Validate/Set specified speed */
-       switch (pdata->phy.speed) {
-       case SPEED_10000:
-               xgbe_set_mode(pdata, XGBE_MODE_KR);
-               break;
-
-       case SPEED_2500:
-       case SPEED_1000:
-               xgbe_set_mode(pdata, XGBE_MODE_KX);
+       /* Set specified mode for specified speed */
+       mode = pdata->phy_if.phy_impl.get_mode(pdata, pdata->phy.speed);
+       switch (mode) {
+       case XGBE_MODE_KX_1000:
+       case XGBE_MODE_KX_2500:
+       case XGBE_MODE_KR:
                break;
-
+       case XGBE_MODE_UNKNOWN:
        default:
                return -EINVAL;
        }
@@ -933,6 +750,8 @@ static int xgbe_phy_config_fixed(struct xgbe_prv_data *pdata)
        if (pdata->phy.duplex != DUPLEX_FULL)
                return -EINVAL;
 
+       xgbe_set_mode(pdata, mode);
+
        return 0;
 }
 
@@ -950,11 +769,12 @@ static int __xgbe_phy_config_aneg(struct xgbe_prv_data *pdata)
        disable_irq(pdata->an_irq);
 
        /* Start auto-negotiation in a supported mode */
-       if (pdata->phy.advertising & ADVERTISED_10000baseKR_Full) {
+       if (xgbe_use_mode(pdata, XGBE_MODE_KR)) {
                xgbe_set_mode(pdata, XGBE_MODE_KR);
-       } else if ((pdata->phy.advertising & ADVERTISED_1000baseKX_Full) ||
-                  (pdata->phy.advertising & ADVERTISED_2500baseX_Full)) {
-               xgbe_set_mode(pdata, XGBE_MODE_KX);
+       } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_2500)) {
+               xgbe_set_mode(pdata, XGBE_MODE_KX_2500);
+       } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_1000)) {
+               xgbe_set_mode(pdata, XGBE_MODE_KX_1000);
        } else {
                enable_irq(pdata->an_irq);
                return -EINVAL;
@@ -1016,108 +836,45 @@ static void xgbe_check_link_timeout(struct xgbe_prv_data *pdata)
        }
 }
 
-static void xgbe_phy_status_force(struct xgbe_prv_data *pdata)
+static enum xgbe_mode xgbe_phy_status_aneg(struct xgbe_prv_data *pdata)
 {
-       if (xgbe_in_kr_mode(pdata)) {
-               pdata->phy.speed = SPEED_10000;
-       } else {
-               switch (pdata->speed_set) {
-               case XGBE_SPEEDSET_1000_10000:
-                       pdata->phy.speed = SPEED_1000;
-                       break;
-
-               case XGBE_SPEEDSET_2500_10000:
-                       pdata->phy.speed = SPEED_2500;
-                       break;
-               }
-       }
-       pdata->phy.duplex = DUPLEX_FULL;
+       return pdata->phy_if.phy_impl.an_outcome(pdata);
 }
 
-static void xgbe_phy_status_aneg(struct xgbe_prv_data *pdata)
+static void xgbe_phy_status_result(struct xgbe_prv_data *pdata)
 {
-       unsigned int ad_reg, lp_reg;
+       enum xgbe_mode mode;
 
        pdata->phy.lp_advertising = 0;
 
        if ((pdata->phy.autoneg != AUTONEG_ENABLE) || pdata->parallel_detect)
-               return xgbe_phy_status_force(pdata);
-
-       pdata->phy.lp_advertising |= ADVERTISED_Autoneg;
-       pdata->phy.lp_advertising |= ADVERTISED_Backplane;
-
-       /* Compare Advertisement and Link Partner register 1 */
-       ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
-       lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA);
-       if (lp_reg & 0x400)
-               pdata->phy.lp_advertising |= ADVERTISED_Pause;
-       if (lp_reg & 0x800)
-               pdata->phy.lp_advertising |= ADVERTISED_Asym_Pause;
-
-       if (pdata->phy.pause_autoneg) {
-               /* Set flow control based on auto-negotiation result */
-               pdata->phy.tx_pause = 0;
-               pdata->phy.rx_pause = 0;
-
-               if (ad_reg & lp_reg & 0x400) {
-                       pdata->phy.tx_pause = 1;
-                       pdata->phy.rx_pause = 1;
-               } else if (ad_reg & lp_reg & 0x800) {
-                       if (ad_reg & 0x400)
-                               pdata->phy.rx_pause = 1;
-                       else if (lp_reg & 0x400)
-                               pdata->phy.tx_pause = 1;
-               }
-       }
-
-       /* Compare Advertisement and Link Partner register 2 */
-       ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1);
-       lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1);
-       if (lp_reg & 0x80)
-               pdata->phy.lp_advertising |= ADVERTISED_10000baseKR_Full;
-       if (lp_reg & 0x20) {
-               switch (pdata->speed_set) {
-               case XGBE_SPEEDSET_1000_10000:
-                       pdata->phy.lp_advertising |= ADVERTISED_1000baseKX_Full;
-                       break;
-               case XGBE_SPEEDSET_2500_10000:
-                       pdata->phy.lp_advertising |= ADVERTISED_2500baseX_Full;
-                       break;
-               }
-       }
+               mode = xgbe_cur_mode(pdata);
+       else
+               mode = xgbe_phy_status_aneg(pdata);
 
-       ad_reg &= lp_reg;
-       if (ad_reg & 0x80) {
+       switch (mode) {
+       case XGBE_MODE_KX_1000:
+               pdata->phy.speed = SPEED_1000;
+               break;
+       case XGBE_MODE_KX_2500:
+               pdata->phy.speed = SPEED_2500;
+               break;
+       case XGBE_MODE_KR:
                pdata->phy.speed = SPEED_10000;
-               xgbe_set_mode(pdata, XGBE_MODE_KR);
-       } else if (ad_reg & 0x20) {
-               switch (pdata->speed_set) {
-               case XGBE_SPEEDSET_1000_10000:
-                       pdata->phy.speed = SPEED_1000;
-                       break;
-
-               case XGBE_SPEEDSET_2500_10000:
-                       pdata->phy.speed = SPEED_2500;
-                       break;
-               }
-
-               xgbe_set_mode(pdata, XGBE_MODE_KX);
-       } else {
+               break;
+       case XGBE_MODE_UNKNOWN:
+       default:
                pdata->phy.speed = SPEED_UNKNOWN;
        }
 
-       /* Compare Advertisement and Link Partner register 3 */
-       ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2);
-       lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2);
-       if (lp_reg & 0xc000)
-               pdata->phy.lp_advertising |= ADVERTISED_10000baseR_FEC;
-
        pdata->phy.duplex = DUPLEX_FULL;
+
+       xgbe_set_mode(pdata, mode);
 }
 
 static void xgbe_phy_status(struct xgbe_prv_data *pdata)
 {
-       unsigned int reg, link_aneg;
+       unsigned int link_aneg;
 
        if (test_bit(XGBE_LINK_ERR, &pdata->dev_state)) {
                netif_carrier_off(pdata->netdev);
@@ -1128,20 +885,14 @@ static void xgbe_phy_status(struct xgbe_prv_data *pdata)
 
        link_aneg = (pdata->phy.autoneg == AUTONEG_ENABLE);
 
-       /* Get the link status. Link status is latched low, so read
-        * once to clear and then read again to get current state
-        */
-       reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1);
-       reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1);
-       pdata->phy.link = (reg & MDIO_STAT1_LSTATUS) ? 1 : 0;
-
+       pdata->phy.link = pdata->phy_if.phy_impl.link_status(pdata);
        if (pdata->phy.link) {
                if (link_aneg && !xgbe_phy_aneg_done(pdata)) {
                        xgbe_check_link_timeout(pdata);
                        return;
                }
 
-               xgbe_phy_status_aneg(pdata);
+               xgbe_phy_status_result(pdata);
 
                if (test_bit(XGBE_LINK_INIT, &pdata->dev_state))
                        clear_bit(XGBE_LINK_INIT, &pdata->dev_state);
@@ -1155,7 +906,7 @@ static void xgbe_phy_status(struct xgbe_prv_data *pdata)
                                return;
                }
 
-               xgbe_phy_status_aneg(pdata);
+               xgbe_phy_status_result(pdata);
 
                netif_carrier_off(pdata->netdev);
        }
@@ -1168,6 +919,12 @@ static void xgbe_phy_stop(struct xgbe_prv_data *pdata)
 {
        netif_dbg(pdata, link, pdata->netdev, "stopping PHY\n");
 
+       if (!pdata->phy_started)
+               return;
+
+       /* Indicate the PHY is down */
+       pdata->phy_started = 0;
+
        /* Disable auto-negotiation */
        xgbe_disable_an(pdata);
 
@@ -1176,6 +933,8 @@ static void xgbe_phy_stop(struct xgbe_prv_data *pdata)
 
        devm_free_irq(pdata->dev, pdata->an_irq, pdata);
 
+       pdata->phy_if.phy_impl.stop(pdata);
+
        pdata->phy.link = 0;
        netif_carrier_off(pdata->netdev);
 
@@ -1189,28 +948,35 @@ static int xgbe_phy_start(struct xgbe_prv_data *pdata)
 
        netif_dbg(pdata, link, pdata->netdev, "starting PHY\n");
 
+       ret = pdata->phy_if.phy_impl.start(pdata);
+       if (ret)
+               return ret;
+
        ret = devm_request_irq(pdata->dev, pdata->an_irq,
                               xgbe_an_isr, 0, pdata->an_name,
                               pdata);
        if (ret) {
                netdev_err(netdev, "phy irq request failed\n");
-               return ret;
+               goto err_stop;
        }
 
        /* Set initial mode - call the mode setting routines
         * directly to insure we are properly configured
         */
-       if (xgbe_use_xgmii_mode(pdata)) {
-               xgbe_xgmii_mode(pdata);
-       } else if (xgbe_use_gmii_mode(pdata)) {
-               xgbe_gmii_mode(pdata);
-       } else if (xgbe_use_gmii_2500_mode(pdata)) {
-               xgbe_gmii_2500_mode(pdata);
+       if (xgbe_use_mode(pdata, XGBE_MODE_KR)) {
+               xgbe_kr_mode(pdata);
+       } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_2500)) {
+               xgbe_kx_2500_mode(pdata);
+       } else if (xgbe_use_mode(pdata, XGBE_MODE_KX_1000)) {
+               xgbe_kx_1000_mode(pdata);
        } else {
                ret = -EINVAL;
                goto err_irq;
        }
 
+       /* Indicate the PHY is up and running */
+       pdata->phy_started = 1;
+
        /* Set up advertisement registers based on current settings */
        xgbe_an_init(pdata);
 
@@ -1222,25 +988,19 @@ static int xgbe_phy_start(struct xgbe_prv_data *pdata)
 err_irq:
        devm_free_irq(pdata->dev, pdata->an_irq, pdata);
 
+err_stop:
+       pdata->phy_if.phy_impl.stop(pdata);
+
        return ret;
 }
 
 static int xgbe_phy_reset(struct xgbe_prv_data *pdata)
 {
-       unsigned int count, reg;
-
-       reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1);
-       reg |= MDIO_CTRL1_RESET;
-       XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg);
-
-       count = 50;
-       do {
-               msleep(20);
-               reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1);
-       } while ((reg & MDIO_CTRL1_RESET) && --count);
+       int ret;
 
-       if (reg & MDIO_CTRL1_RESET)
-               return -ETIMEDOUT;
+       ret = pdata->phy_if.phy_impl.reset(pdata);
+       if (ret)
+               return ret;
 
        /* Disable auto-negotiation for now */
        xgbe_disable_an(pdata);
@@ -1290,41 +1050,57 @@ static void xgbe_dump_phy_registers(struct xgbe_prv_data *pdata)
        dev_dbg(dev, "\n*************************************************\n");
 }
 
-static void xgbe_phy_init(struct xgbe_prv_data *pdata)
+static int xgbe_phy_best_advertised_speed(struct xgbe_prv_data *pdata)
+{
+       if (pdata->phy.advertising & ADVERTISED_10000baseKR_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;
+
+       return SPEED_UNKNOWN;
+}
+
+static void xgbe_phy_exit(struct xgbe_prv_data *pdata)
 {
+       xgbe_phy_stop(pdata);
+
+       pdata->phy_if.phy_impl.exit(pdata);
+}
+
+static int xgbe_phy_init(struct xgbe_prv_data *pdata)
+{
+       int ret;
+
        mutex_init(&pdata->an_mutex);
        INIT_WORK(&pdata->an_irq_work, xgbe_an_irq_work);
        INIT_WORK(&pdata->an_work, xgbe_an_state_machine);
        pdata->mdio_mmd = MDIO_MMD_PCS;
 
-       /* Initialize supported features */
-       pdata->phy.supported = SUPPORTED_Autoneg;
-       pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
-       pdata->phy.supported |= SUPPORTED_Backplane;
-       pdata->phy.supported |= SUPPORTED_10000baseKR_Full;
-       switch (pdata->speed_set) {
-       case XGBE_SPEEDSET_1000_10000:
-               pdata->phy.supported |= SUPPORTED_1000baseKX_Full;
-               break;
-       case XGBE_SPEEDSET_2500_10000:
-               pdata->phy.supported |= SUPPORTED_2500baseX_Full;
-               break;
-       }
-
+       /* 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);
-       if (pdata->fec_ability & MDIO_PMA_10GBR_FECABLE_ABLE)
-               pdata->phy.supported |= SUPPORTED_10000baseR_FEC;
 
+       /* 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;
 
-       pdata->phy.autoneg = AUTONEG_ENABLE;
-       pdata->phy.speed = SPEED_UNKNOWN;
-       pdata->phy.duplex = DUPLEX_UNKNOWN;
+       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 = xgbe_phy_best_advertised_speed(pdata);
+               pdata->phy.duplex = DUPLEX_FULL;
+       }
 
        pdata->phy.link = 0;
 
@@ -1346,11 +1122,14 @@ static void xgbe_phy_init(struct xgbe_prv_data *pdata)
 
        if (netif_msg_drv(pdata))
                xgbe_dump_phy_registers(pdata);
+
+       return 0;
 }
 
 void xgbe_init_function_ptrs_phy(struct xgbe_phy_if *phy_if)
 {
        phy_if->phy_init        = xgbe_phy_init;
+       phy_if->phy_exit        = xgbe_phy_exit;
 
        phy_if->phy_reset       = xgbe_phy_reset;
        phy_if->phy_start       = xgbe_phy_start;
@@ -1358,4 +1137,6 @@ void xgbe_init_function_ptrs_phy(struct xgbe_phy_if *phy_if)
 
        phy_if->phy_status      = xgbe_phy_status;
        phy_if->phy_config_aneg = xgbe_phy_config_aneg;
+
+       phy_if->phy_valid_speed = xgbe_phy_valid_speed;
 }
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v1.c
new file mode 100644 (file)
index 0000000..20ccb99
--- /dev/null
@@ -0,0 +1,821 @@
+/*
+ * AMD 10Gb Ethernet driver
+ *
+ * This file is available to you under your choice of the following two
+ * licenses:
+ *
+ * License 1: GPLv2
+ *
+ * Copyright (c) 2016 Advanced Micro Devices, Inc.
+ *
+ * This file is free software; you may copy, redistribute and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or (at
+ * your option) any later version.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and the Software, to deal in the Software
+ *     without restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ *     of the Software, and to permit persons to whom the Software is furnished
+ *     to do so, subject to the following conditions:
+ *
+ *     The above copyright notice and this permission notice shall be included
+ *     in all copies or substantial portions of the Software.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *     CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *     SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *     INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *     ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ *     THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * License 2: Modified BSD
+ *
+ * Copyright (c) 2016 Advanced Micro Devices, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *     * Redistributions of source code must retain the above copyright
+ *       notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above copyright
+ *       notice, this list of conditions and the following disclaimer in the
+ *       documentation and/or other materials provided with the distribution.
+ *     * Neither the name of Advanced Micro Devices, Inc. nor the
+ *       names of its contributors may be used to endorse or promote products
+ *       derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * This file incorporates work covered by the following copyright and
+ * permission notice:
+ *     The Synopsys DWC ETHER XGMAC Software Driver and documentation
+ *     (hereinafter "Software") is an unsupported proprietary work of Synopsys,
+ *     Inc. unless otherwise expressly agreed to in writing between Synopsys
+ *     and you.
+ *
+ *     The Software IS NOT an item of Licensed Software or Licensed Product
+ *     under any End User Software License Agreement or Agreement for Licensed
+ *     Product with Synopsys or any supplement thereto.  Permission is hereby
+ *     granted, free of charge, to any person obtaining a copy of this software
+ *     annotated with this license and the Software, to deal in the Software
+ *     without restriction, including without limitation the rights to use,
+ *     copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ *     of the Software, and to permit persons to whom the Software is furnished
+ *     to do so, subject to the following conditions:
+ *
+ *     The above copyright notice and this permission notice shall be included
+ *     in all copies or substantial portions of the Software.
+ *
+ *     THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS"
+ *     BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ *     TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ *     PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS
+ *     BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ *     CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ *     SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ *     INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ *     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ *     ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ *     THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <linux/module.h>
+#include <linux/kmod.h>
+#include <linux/device.h>
+#include <linux/property.h>
+#include <linux/mdio.h>
+#include <linux/phy.h>
+
+#include "xgbe.h"
+#include "xgbe-common.h"
+
+#define XGBE_BLWC_PROPERTY             "amd,serdes-blwc"
+#define XGBE_CDR_RATE_PROPERTY         "amd,serdes-cdr-rate"
+#define XGBE_PQ_SKEW_PROPERTY          "amd,serdes-pq-skew"
+#define XGBE_TX_AMP_PROPERTY           "amd,serdes-tx-amp"
+#define XGBE_DFE_CFG_PROPERTY          "amd,serdes-dfe-tap-config"
+#define XGBE_DFE_ENA_PROPERTY          "amd,serdes-dfe-tap-enable"
+
+/* Default SerDes settings */
+#define XGBE_SPEED_1000_BLWC           1
+#define XGBE_SPEED_1000_CDR            0x2
+#define XGBE_SPEED_1000_PLL            0x0
+#define XGBE_SPEED_1000_PQ             0xa
+#define XGBE_SPEED_1000_RATE           0x3
+#define XGBE_SPEED_1000_TXAMP          0xf
+#define XGBE_SPEED_1000_WORD           0x1
+#define XGBE_SPEED_1000_DFE_TAP_CONFIG 0x3
+#define XGBE_SPEED_1000_DFE_TAP_ENABLE 0x0
+
+#define XGBE_SPEED_2500_BLWC           1
+#define XGBE_SPEED_2500_CDR            0x2
+#define XGBE_SPEED_2500_PLL            0x0
+#define XGBE_SPEED_2500_PQ             0xa
+#define XGBE_SPEED_2500_RATE           0x1
+#define XGBE_SPEED_2500_TXAMP          0xf
+#define XGBE_SPEED_2500_WORD           0x1
+#define XGBE_SPEED_2500_DFE_TAP_CONFIG 0x3
+#define XGBE_SPEED_2500_DFE_TAP_ENABLE 0x0
+
+#define XGBE_SPEED_10000_BLWC          0
+#define XGBE_SPEED_10000_CDR           0x7
+#define XGBE_SPEED_10000_PLL           0x1
+#define XGBE_SPEED_10000_PQ            0x12
+#define XGBE_SPEED_10000_RATE          0x0
+#define XGBE_SPEED_10000_TXAMP         0xa
+#define XGBE_SPEED_10000_WORD          0x7
+#define XGBE_SPEED_10000_DFE_TAP_CONFIG        0x1
+#define XGBE_SPEED_10000_DFE_TAP_ENABLE        0x7f
+
+/* Rate-change complete wait/retry count */
+#define XGBE_RATECHANGE_COUNT          500
+
+static const u32 xgbe_phy_blwc[] = {
+       XGBE_SPEED_1000_BLWC,
+       XGBE_SPEED_2500_BLWC,
+       XGBE_SPEED_10000_BLWC,
+};
+
+static const u32 xgbe_phy_cdr_rate[] = {
+       XGBE_SPEED_1000_CDR,
+       XGBE_SPEED_2500_CDR,
+       XGBE_SPEED_10000_CDR,
+};
+
+static const u32 xgbe_phy_pq_skew[] = {
+       XGBE_SPEED_1000_PQ,
+       XGBE_SPEED_2500_PQ,
+       XGBE_SPEED_10000_PQ,
+};
+
+static const u32 xgbe_phy_tx_amp[] = {
+       XGBE_SPEED_1000_TXAMP,
+       XGBE_SPEED_2500_TXAMP,
+       XGBE_SPEED_10000_TXAMP,
+};
+
+static const u32 xgbe_phy_dfe_tap_cfg[] = {
+       XGBE_SPEED_1000_DFE_TAP_CONFIG,
+       XGBE_SPEED_2500_DFE_TAP_CONFIG,
+       XGBE_SPEED_10000_DFE_TAP_CONFIG,
+};
+
+static const u32 xgbe_phy_dfe_tap_ena[] = {
+       XGBE_SPEED_1000_DFE_TAP_ENABLE,
+       XGBE_SPEED_2500_DFE_TAP_ENABLE,
+       XGBE_SPEED_10000_DFE_TAP_ENABLE,
+};
+
+struct xgbe_phy_data {
+       /* 1000/10000 vs 2500/10000 indicator */
+       unsigned int speed_set;
+
+       /* SerDes UEFI configurable settings.
+        *   Switching between modes/speeds requires new values for some
+        *   SerDes settings.  The values can be supplied as device
+        *   properties in array format.  The first array entry is for
+        *   1GbE, second for 2.5GbE and third for 10GbE
+        */
+       u32 blwc[XGBE_SPEEDS];
+       u32 cdr_rate[XGBE_SPEEDS];
+       u32 pq_skew[XGBE_SPEEDS];
+       u32 tx_amp[XGBE_SPEEDS];
+       u32 dfe_tap_cfg[XGBE_SPEEDS];
+       u32 dfe_tap_ena[XGBE_SPEEDS];
+};
+
+static void xgbe_phy_kr_training_pre(struct xgbe_prv_data *pdata)
+{
+               XSIR0_IOWRITE_BITS(pdata, SIR0_KR_RT_1, RESET, 1);
+}
+
+static void xgbe_phy_kr_training_post(struct xgbe_prv_data *pdata)
+{
+               XSIR0_IOWRITE_BITS(pdata, SIR0_KR_RT_1, RESET, 0);
+}
+
+static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+       enum xgbe_mode mode;
+       unsigned int ad_reg, lp_reg;
+
+       pdata->phy.lp_advertising |= ADVERTISED_Autoneg;
+       pdata->phy.lp_advertising |= ADVERTISED_Backplane;
+
+       /* Compare Advertisement and Link Partner register 1 */
+       ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
+       lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA);
+       if (lp_reg & 0x400)
+               pdata->phy.lp_advertising |= ADVERTISED_Pause;
+       if (lp_reg & 0x800)
+               pdata->phy.lp_advertising |= ADVERTISED_Asym_Pause;
+
+       if (pdata->phy.pause_autoneg) {
+               /* Set flow control based on auto-negotiation result */
+               pdata->phy.tx_pause = 0;
+               pdata->phy.rx_pause = 0;
+
+               if (ad_reg & lp_reg & 0x400) {
+                       pdata->phy.tx_pause = 1;
+                       pdata->phy.rx_pause = 1;
+               } else if (ad_reg & lp_reg & 0x800) {
+                       if (ad_reg & 0x400)
+                               pdata->phy.rx_pause = 1;
+                       else if (lp_reg & 0x400)
+                               pdata->phy.tx_pause = 1;
+               }
+       }
+
+       /* Compare Advertisement and Link Partner register 2 */
+       ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1);
+       lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 1);
+       if (lp_reg & 0x80)
+               pdata->phy.lp_advertising |= ADVERTISED_10000baseKR_Full;
+       if (lp_reg & 0x20) {
+               if (phy_data->speed_set == XGBE_SPEEDSET_2500_10000)
+                       pdata->phy.lp_advertising |= ADVERTISED_2500baseX_Full;
+               else
+                       pdata->phy.lp_advertising |= ADVERTISED_1000baseKX_Full;
+       }
+
+       ad_reg &= lp_reg;
+       if (ad_reg & 0x80) {
+               mode = XGBE_MODE_KR;
+       } else if (ad_reg & 0x20) {
+               if (phy_data->speed_set == XGBE_SPEEDSET_2500_10000)
+                       mode = XGBE_MODE_KX_2500;
+               else
+                       mode = XGBE_MODE_KX_1000;
+       } else {
+               mode = XGBE_MODE_UNKNOWN;
+       }
+
+       /* Compare Advertisement and Link Partner register 3 */
+       ad_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2);
+       lp_reg = XMDIO_READ(pdata, MDIO_MMD_AN, MDIO_AN_LPA + 2);
+       if (lp_reg & 0xc000)
+               pdata->phy.lp_advertising |= ADVERTISED_10000baseR_FEC;
+
+       return mode;
+}
+
+static void xgbe_phy_pcs_power_cycle(struct xgbe_prv_data *pdata)
+{
+       unsigned int reg;
+
+       reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1);
+
+       reg |= MDIO_CTRL1_LPOWER;
+       XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg);
+
+       usleep_range(75, 100);
+
+       reg &= ~MDIO_CTRL1_LPOWER;
+       XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg);
+}
+
+static void xgbe_phy_start_ratechange(struct xgbe_prv_data *pdata)
+{
+       /* Assert Rx and Tx ratechange */
+       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, RATECHANGE, 1);
+}
+
+static void xgbe_phy_complete_ratechange(struct xgbe_prv_data *pdata)
+{
+       unsigned int wait;
+       u16 status;
+
+       /* Release Rx and Tx ratechange */
+       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, RATECHANGE, 0);
+
+       /* Wait for Rx and Tx ready */
+       wait = XGBE_RATECHANGE_COUNT;
+       while (wait--) {
+               usleep_range(50, 75);
+
+               status = XSIR0_IOREAD(pdata, SIR0_STATUS);
+               if (XSIR_GET_BITS(status, SIR0_STATUS, RX_READY) &&
+                   XSIR_GET_BITS(status, SIR0_STATUS, TX_READY))
+                       goto rx_reset;
+       }
+
+       netif_dbg(pdata, link, pdata->netdev, "SerDes rx/tx not ready (%#hx)\n",
+                 status);
+
+rx_reset:
+       /* Perform Rx reset for the DFE changes */
+       XRXTX_IOWRITE_BITS(pdata, RXTX_REG6, RESETB_RXD, 0);
+       XRXTX_IOWRITE_BITS(pdata, RXTX_REG6, RESETB_RXD, 1);
+}
+
+static void xgbe_phy_kr_mode(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+       unsigned int reg;
+
+       /* Set PCS to KR/10G speed */
+       reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2);
+       reg &= ~MDIO_PCS_CTRL2_TYPE;
+       reg |= MDIO_PCS_CTRL2_10GBR;
+       XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL2, reg);
+
+       reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1);
+       reg &= ~MDIO_CTRL1_SPEEDSEL;
+       reg |= MDIO_CTRL1_SPEED10G;
+       XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg);
+
+       xgbe_phy_pcs_power_cycle(pdata);
+
+       /* Set SerDes to 10G speed */
+       xgbe_phy_start_ratechange(pdata);
+
+       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, DATARATE, XGBE_SPEED_10000_RATE);
+       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, WORDMODE, XGBE_SPEED_10000_WORD);
+       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PLLSEL, XGBE_SPEED_10000_PLL);
+
+       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, CDR_RATE,
+                          phy_data->cdr_rate[XGBE_SPEED_10000]);
+       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, TXAMP,
+                          phy_data->tx_amp[XGBE_SPEED_10000]);
+       XRXTX_IOWRITE_BITS(pdata, RXTX_REG20, BLWC_ENA,
+                          phy_data->blwc[XGBE_SPEED_10000]);
+       XRXTX_IOWRITE_BITS(pdata, RXTX_REG114, PQ_REG,
+                          phy_data->pq_skew[XGBE_SPEED_10000]);
+       XRXTX_IOWRITE_BITS(pdata, RXTX_REG129, RXDFE_CONFIG,
+                          phy_data->dfe_tap_cfg[XGBE_SPEED_10000]);
+       XRXTX_IOWRITE(pdata, RXTX_REG22,
+                     phy_data->dfe_tap_ena[XGBE_SPEED_10000]);
+
+       xgbe_phy_complete_ratechange(pdata);
+
+       netif_dbg(pdata, link, pdata->netdev, "10GbE KR mode set\n");
+}
+
+static void xgbe_phy_kx_2500_mode(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+       unsigned int reg;
+
+       /* Set PCS to KX/1G speed */
+       reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2);
+       reg &= ~MDIO_PCS_CTRL2_TYPE;
+       reg |= MDIO_PCS_CTRL2_10GBX;
+       XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL2, reg);
+
+       reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1);
+       reg &= ~MDIO_CTRL1_SPEEDSEL;
+       reg |= MDIO_CTRL1_SPEED1G;
+       XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg);
+
+       xgbe_phy_pcs_power_cycle(pdata);
+
+       /* Set SerDes to 2.5G speed */
+       xgbe_phy_start_ratechange(pdata);
+
+       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, DATARATE, XGBE_SPEED_2500_RATE);
+       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, WORDMODE, XGBE_SPEED_2500_WORD);
+       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PLLSEL, XGBE_SPEED_2500_PLL);
+
+       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, CDR_RATE,
+                          phy_data->cdr_rate[XGBE_SPEED_2500]);
+       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, TXAMP,
+                          phy_data->tx_amp[XGBE_SPEED_2500]);
+       XRXTX_IOWRITE_BITS(pdata, RXTX_REG20, BLWC_ENA,
+                          phy_data->blwc[XGBE_SPEED_2500]);
+       XRXTX_IOWRITE_BITS(pdata, RXTX_REG114, PQ_REG,
+                          phy_data->pq_skew[XGBE_SPEED_2500]);
+       XRXTX_IOWRITE_BITS(pdata, RXTX_REG129, RXDFE_CONFIG,
+                          phy_data->dfe_tap_cfg[XGBE_SPEED_2500]);
+       XRXTX_IOWRITE(pdata, RXTX_REG22,
+                     phy_data->dfe_tap_ena[XGBE_SPEED_2500]);
+
+       xgbe_phy_complete_ratechange(pdata);
+
+       netif_dbg(pdata, link, pdata->netdev, "2.5GbE KX mode set\n");
+}
+
+static void xgbe_phy_kx_1000_mode(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+       unsigned int reg;
+
+       /* Set PCS to KX/1G speed */
+       reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2);
+       reg &= ~MDIO_PCS_CTRL2_TYPE;
+       reg |= MDIO_PCS_CTRL2_10GBX;
+       XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL2, reg);
+
+       reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1);
+       reg &= ~MDIO_CTRL1_SPEEDSEL;
+       reg |= MDIO_CTRL1_SPEED1G;
+       XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg);
+
+       xgbe_phy_pcs_power_cycle(pdata);
+
+       /* Set SerDes to 1G speed */
+       xgbe_phy_start_ratechange(pdata);
+
+       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, DATARATE, XGBE_SPEED_1000_RATE);
+       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, WORDMODE, XGBE_SPEED_1000_WORD);
+       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, PLLSEL, XGBE_SPEED_1000_PLL);
+
+       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, CDR_RATE,
+                          phy_data->cdr_rate[XGBE_SPEED_1000]);
+       XSIR1_IOWRITE_BITS(pdata, SIR1_SPEED, TXAMP,
+                          phy_data->tx_amp[XGBE_SPEED_1000]);
+       XRXTX_IOWRITE_BITS(pdata, RXTX_REG20, BLWC_ENA,
+                          phy_data->blwc[XGBE_SPEED_1000]);
+       XRXTX_IOWRITE_BITS(pdata, RXTX_REG114, PQ_REG,
+                          phy_data->pq_skew[XGBE_SPEED_1000]);
+       XRXTX_IOWRITE_BITS(pdata, RXTX_REG129, RXDFE_CONFIG,
+                          phy_data->dfe_tap_cfg[XGBE_SPEED_1000]);
+       XRXTX_IOWRITE(pdata, RXTX_REG22,
+                     phy_data->dfe_tap_ena[XGBE_SPEED_1000]);
+
+       xgbe_phy_complete_ratechange(pdata);
+
+       netif_dbg(pdata, link, pdata->netdev, "1GbE KX mode set\n");
+}
+
+static enum xgbe_mode xgbe_phy_cur_mode(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+       enum xgbe_mode mode;
+       unsigned int reg;
+
+       reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL2);
+       reg &= MDIO_PCS_CTRL2_TYPE;
+
+       if (reg == MDIO_PCS_CTRL2_10GBR) {
+               mode = XGBE_MODE_KR;
+       } else {
+               if (phy_data->speed_set == XGBE_SPEEDSET_2500_10000)
+                       mode = XGBE_MODE_KX_2500;
+               else
+                       mode = XGBE_MODE_KX_1000;
+       }
+
+       return mode;
+}
+
+static enum xgbe_mode xgbe_phy_switch_mode(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+       enum xgbe_mode mode;
+
+       /* If we are in KR switch to KX, and vice-versa */
+       if (xgbe_phy_cur_mode(pdata) == XGBE_MODE_KR) {
+               if (phy_data->speed_set == XGBE_SPEEDSET_2500_10000)
+                       mode = XGBE_MODE_KX_2500;
+               else
+                       mode = XGBE_MODE_KX_1000;
+       } else {
+               mode = XGBE_MODE_KR;
+       }
+
+       return mode;
+}
+
+static enum xgbe_mode xgbe_phy_get_mode(struct xgbe_prv_data *pdata,
+                                       int speed)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+
+       switch (speed) {
+       case SPEED_1000:
+               return (phy_data->speed_set == XGBE_SPEEDSET_1000_10000)
+                       ? XGBE_MODE_KX_1000 : XGBE_MODE_UNKNOWN;
+       case SPEED_2500:
+               return (phy_data->speed_set == XGBE_SPEEDSET_2500_10000)
+                       ? XGBE_MODE_KX_2500 : XGBE_MODE_UNKNOWN;
+       case SPEED_10000:
+               return XGBE_MODE_KR;
+       default:
+               return XGBE_MODE_UNKNOWN;
+       }
+}
+
+static void xgbe_phy_set_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode)
+{
+       switch (mode) {
+       case XGBE_MODE_KX_1000:
+               xgbe_phy_kx_1000_mode(pdata);
+               break;
+       case XGBE_MODE_KX_2500:
+               xgbe_phy_kx_2500_mode(pdata);
+               break;
+       case XGBE_MODE_KR:
+               xgbe_phy_kr_mode(pdata);
+               break;
+       default:
+               break;
+       }
+}
+
+static bool xgbe_phy_check_mode(struct xgbe_prv_data *pdata,
+                               enum xgbe_mode mode, u32 advert)
+{
+       if (pdata->phy.autoneg == AUTONEG_ENABLE) {
+               if (pdata->phy.advertising & advert)
+                       return true;
+       } else {
+               enum xgbe_mode cur_mode;
+
+               cur_mode = xgbe_phy_get_mode(pdata, pdata->phy.speed);
+               if (cur_mode == mode)
+                       return true;
+       }
+
+       return false;
+}
+
+static bool xgbe_phy_use_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode)
+{
+       switch (mode) {
+       case XGBE_MODE_KX_1000:
+               return xgbe_phy_check_mode(pdata, mode,
+                                          ADVERTISED_1000baseKX_Full);
+       case XGBE_MODE_KX_2500:
+               return xgbe_phy_check_mode(pdata, mode,
+                                          ADVERTISED_2500baseX_Full);
+       case XGBE_MODE_KR:
+               return xgbe_phy_check_mode(pdata, mode,
+                                          ADVERTISED_10000baseKR_Full);
+       default:
+               return false;
+       }
+}
+
+static bool xgbe_phy_valid_speed(struct xgbe_prv_data *pdata, int speed)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+
+       switch (speed) {
+       case SPEED_1000:
+               if (phy_data->speed_set != XGBE_SPEEDSET_1000_10000)
+                       return false;
+               return true;
+       case SPEED_2500:
+               if (phy_data->speed_set != XGBE_SPEEDSET_2500_10000)
+                       return false;
+               return true;
+       case SPEED_10000:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static int xgbe_phy_link_status(struct xgbe_prv_data *pdata)
+{
+       unsigned int reg;
+
+       /* Link status is latched low, so read once to clear
+        * and then read again to get current state
+        */
+       reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1);
+       reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_STAT1);
+
+       return (reg & MDIO_STAT1_LSTATUS) ? 1 : 0;
+}
+
+static void xgbe_phy_stop(struct xgbe_prv_data *pdata)
+{
+       /* Nothing uniquely required for stop */
+}
+
+static int xgbe_phy_start(struct xgbe_prv_data *pdata)
+{
+       /* Nothing uniquely required for start */
+       return 0;
+}
+
+static int xgbe_phy_reset(struct xgbe_prv_data *pdata)
+{
+       unsigned int reg, count;
+
+       /* Perform a software reset of the PCS */
+       reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1);
+       reg |= MDIO_CTRL1_RESET;
+       XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, reg);
+
+       count = 50;
+       do {
+               msleep(20);
+               reg = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1);
+       } while ((reg & MDIO_CTRL1_RESET) && --count);
+
+       if (reg & MDIO_CTRL1_RESET)
+               return -ETIMEDOUT;
+
+       return 0;
+}
+
+static void xgbe_phy_exit(struct xgbe_prv_data *pdata)
+{
+       /* Nothing uniquely required for exit */
+}
+
+static int xgbe_phy_init(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_phy_data *phy_data;
+       int ret;
+
+       phy_data = devm_kzalloc(pdata->dev, sizeof(*phy_data), GFP_KERNEL);
+       if (!phy_data)
+               return -ENOMEM;
+
+       /* Retrieve the PHY speedset */
+       ret = device_property_read_u32(pdata->phy_dev, XGBE_SPEEDSET_PROPERTY,
+                                      &phy_data->speed_set);
+       if (ret) {
+               dev_err(pdata->dev, "invalid %s property\n",
+                       XGBE_SPEEDSET_PROPERTY);
+               return ret;
+       }
+
+       switch (phy_data->speed_set) {
+       case XGBE_SPEEDSET_1000_10000:
+       case XGBE_SPEEDSET_2500_10000:
+               break;
+       default:
+               dev_err(pdata->dev, "invalid %s property\n",
+                       XGBE_SPEEDSET_PROPERTY);
+               return -EINVAL;
+       }
+
+       /* Retrieve the PHY configuration properties */
+       if (device_property_present(pdata->phy_dev, XGBE_BLWC_PROPERTY)) {
+               ret = device_property_read_u32_array(pdata->phy_dev,
+                                                    XGBE_BLWC_PROPERTY,
+                                                    phy_data->blwc,
+                                                    XGBE_SPEEDS);
+               if (ret) {
+                       dev_err(pdata->dev, "invalid %s property\n",
+                               XGBE_BLWC_PROPERTY);
+                       return ret;
+               }
+       } else {
+               memcpy(phy_data->blwc, xgbe_phy_blwc,
+                      sizeof(phy_data->blwc));
+       }
+
+       if (device_property_present(pdata->phy_dev, XGBE_CDR_RATE_PROPERTY)) {
+               ret = device_property_read_u32_array(pdata->phy_dev,
+                                                    XGBE_CDR_RATE_PROPERTY,
+                                                    phy_data->cdr_rate,
+                                                    XGBE_SPEEDS);
+               if (ret) {
+                       dev_err(pdata->dev, "invalid %s property\n",
+                               XGBE_CDR_RATE_PROPERTY);
+                       return ret;
+               }
+       } else {
+               memcpy(phy_data->cdr_rate, xgbe_phy_cdr_rate,
+                      sizeof(phy_data->cdr_rate));
+       }
+
+       if (device_property_present(pdata->phy_dev, XGBE_PQ_SKEW_PROPERTY)) {
+               ret = device_property_read_u32_array(pdata->phy_dev,
+                                                    XGBE_PQ_SKEW_PROPERTY,
+                                                    phy_data->pq_skew,
+                                                    XGBE_SPEEDS);
+               if (ret) {
+                       dev_err(pdata->dev, "invalid %s property\n",
+                               XGBE_PQ_SKEW_PROPERTY);
+                       return ret;
+               }
+       } else {
+               memcpy(phy_data->pq_skew, xgbe_phy_pq_skew,
+                      sizeof(phy_data->pq_skew));
+       }
+
+       if (device_property_present(pdata->phy_dev, XGBE_TX_AMP_PROPERTY)) {
+               ret = device_property_read_u32_array(pdata->phy_dev,
+                                                    XGBE_TX_AMP_PROPERTY,
+                                                    phy_data->tx_amp,
+                                                    XGBE_SPEEDS);
+               if (ret) {
+                       dev_err(pdata->dev, "invalid %s property\n",
+                               XGBE_TX_AMP_PROPERTY);
+                       return ret;
+               }
+       } else {
+               memcpy(phy_data->tx_amp, xgbe_phy_tx_amp,
+                      sizeof(phy_data->tx_amp));
+       }
+
+       if (device_property_present(pdata->phy_dev, XGBE_DFE_CFG_PROPERTY)) {
+               ret = device_property_read_u32_array(pdata->phy_dev,
+                                                    XGBE_DFE_CFG_PROPERTY,
+                                                    phy_data->dfe_tap_cfg,
+                                                    XGBE_SPEEDS);
+               if (ret) {
+                       dev_err(pdata->dev, "invalid %s property\n",
+                               XGBE_DFE_CFG_PROPERTY);
+                       return ret;
+               }
+       } else {
+               memcpy(phy_data->dfe_tap_cfg, xgbe_phy_dfe_tap_cfg,
+                      sizeof(phy_data->dfe_tap_cfg));
+       }
+
+       if (device_property_present(pdata->phy_dev, XGBE_DFE_ENA_PROPERTY)) {
+               ret = device_property_read_u32_array(pdata->phy_dev,
+                                                    XGBE_DFE_ENA_PROPERTY,
+                                                    phy_data->dfe_tap_ena,
+                                                    XGBE_SPEEDS);
+               if (ret) {
+                       dev_err(pdata->dev, "invalid %s property\n",
+                               XGBE_DFE_ENA_PROPERTY);
+                       return ret;
+               }
+       } else {
+               memcpy(phy_data->dfe_tap_ena, xgbe_phy_dfe_tap_ena,
+                      sizeof(phy_data->dfe_tap_ena));
+       }
+
+       /* Initialize supported features */
+       pdata->phy.supported = SUPPORTED_Autoneg;
+       pdata->phy.supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
+       pdata->phy.supported |= SUPPORTED_Backplane;
+       pdata->phy.supported |= SUPPORTED_10000baseKR_Full;
+       switch (phy_data->speed_set) {
+       case XGBE_SPEEDSET_1000_10000:
+               pdata->phy.supported |= SUPPORTED_1000baseKX_Full;
+               break;
+       case XGBE_SPEEDSET_2500_10000:
+               pdata->phy.supported |= SUPPORTED_2500baseX_Full;
+               break;
+       }
+
+       if (pdata->fec_ability & MDIO_PMA_10GBR_FECABLE_ABLE)
+               pdata->phy.supported |= SUPPORTED_10000baseR_FEC;
+
+       pdata->phy_data = phy_data;
+
+       return 0;
+}
+
+void xgbe_init_function_ptrs_phy_v1(struct xgbe_phy_if *phy_if)
+{
+       struct xgbe_phy_impl_if *phy_impl = &phy_if->phy_impl;
+
+       phy_impl->init                  = xgbe_phy_init;
+       phy_impl->exit                  = xgbe_phy_exit;
+
+       phy_impl->reset                 = xgbe_phy_reset;
+       phy_impl->start                 = xgbe_phy_start;
+       phy_impl->stop                  = xgbe_phy_stop;
+
+       phy_impl->link_status           = xgbe_phy_link_status;
+
+       phy_impl->valid_speed           = xgbe_phy_valid_speed;
+
+       phy_impl->use_mode              = xgbe_phy_use_mode;
+       phy_impl->set_mode              = xgbe_phy_set_mode;
+       phy_impl->get_mode              = xgbe_phy_get_mode;
+       phy_impl->switch_mode           = xgbe_phy_switch_mode;
+       phy_impl->cur_mode              = xgbe_phy_cur_mode;
+
+       phy_impl->an_outcome            = xgbe_phy_an_outcome;
+
+       phy_impl->kr_training_pre       = xgbe_phy_kr_training_pre;
+       phy_impl->kr_training_post      = xgbe_phy_kr_training_post;
+}
index a4e1b8d014910215326d5f4565e41d6bb32e672b..0d33aef36b71edfdf46460139e6c0b8b03508334 100644 (file)
 #include <net/dcbnl.h>
 
 #define XGBE_DRV_NAME          "amd-xgbe"
-#define XGBE_DRV_VERSION       "1.0.2"
+#define XGBE_DRV_VERSION       "1.0.3"
 #define XGBE_DRV_DESC          "AMD 10 Gigabit Ethernet Driver"
 
 /* Descriptor related defines */
 #define XGBE_PHY_MODE_PROPERTY "phy-mode"
 #define XGBE_DMA_IRQS_PROPERTY "amd,per-channel-interrupt"
 #define XGBE_SPEEDSET_PROPERTY "amd,speed-set"
-#define XGBE_BLWC_PROPERTY     "amd,serdes-blwc"
-#define XGBE_CDR_RATE_PROPERTY "amd,serdes-cdr-rate"
-#define XGBE_PQ_SKEW_PROPERTY  "amd,serdes-pq-skew"
-#define XGBE_TX_AMP_PROPERTY   "amd,serdes-tx-amp"
-#define XGBE_DFE_CFG_PROPERTY  "amd,serdes-dfe-tap-config"
-#define XGBE_DFE_ENA_PROPERTY  "amd,serdes-dfe-tap-enable"
 
 /* Device-tree clock names */
 #define XGBE_DMA_CLOCK         "dma_clk"
 #define XGBE_AN_PG_RCV                 0x04
 #define XGBE_AN_INT_MASK               0x07
 
-/* Rate-change complete wait/retry count */
-#define XGBE_RATECHANGE_COUNT          500
-
-/* Default SerDes settings */
-#define XGBE_SPEED_10000_BLWC          0
-#define XGBE_SPEED_10000_CDR           0x7
-#define XGBE_SPEED_10000_PLL           0x1
-#define XGBE_SPEED_10000_PQ            0x12
-#define XGBE_SPEED_10000_RATE          0x0
-#define XGBE_SPEED_10000_TXAMP         0xa
-#define XGBE_SPEED_10000_WORD          0x7
-#define XGBE_SPEED_10000_DFE_TAP_CONFIG        0x1
-#define XGBE_SPEED_10000_DFE_TAP_ENABLE        0x7f
-
-#define XGBE_SPEED_2500_BLWC           1
-#define XGBE_SPEED_2500_CDR            0x2
-#define XGBE_SPEED_2500_PLL            0x0
-#define XGBE_SPEED_2500_PQ             0xa
-#define XGBE_SPEED_2500_RATE           0x1
-#define XGBE_SPEED_2500_TXAMP          0xf
-#define XGBE_SPEED_2500_WORD           0x1
-#define XGBE_SPEED_2500_DFE_TAP_CONFIG 0x3
-#define XGBE_SPEED_2500_DFE_TAP_ENABLE 0x0
-
-#define XGBE_SPEED_1000_BLWC           1
-#define XGBE_SPEED_1000_CDR            0x2
-#define XGBE_SPEED_1000_PLL            0x0
-#define XGBE_SPEED_1000_PQ             0xa
-#define XGBE_SPEED_1000_RATE           0x3
-#define XGBE_SPEED_1000_TXAMP          0xf
-#define XGBE_SPEED_1000_WORD           0x1
-#define XGBE_SPEED_1000_DFE_TAP_CONFIG 0x3
-#define XGBE_SPEED_1000_DFE_TAP_ENABLE 0x0
-
 struct xgbe_prv_data;
 
 struct xgbe_packet_data {
@@ -527,8 +487,10 @@ enum xgbe_rx {
 };
 
 enum xgbe_mode {
-       XGBE_MODE_KR = 0,
-       XGBE_MODE_KX,
+       XGBE_MODE_KX_1000 = 0,
+       XGBE_MODE_KX_2500,
+       XGBE_MODE_KR,
+       XGBE_MODE_UNKNOWN,
 };
 
 enum xgbe_speedset {
@@ -624,9 +586,7 @@ struct xgbe_hw_if {
 
        int (*read_mmd_regs)(struct xgbe_prv_data *, int, int);
        void (*write_mmd_regs)(struct xgbe_prv_data *, int, int, int);
-       int (*set_gmii_speed)(struct xgbe_prv_data *);
-       int (*set_gmii_2500_speed)(struct xgbe_prv_data *);
-       int (*set_xgmii_speed)(struct xgbe_prv_data *);
+       int (*set_speed)(struct xgbe_prv_data *, int);
 
        void (*enable_tx)(struct xgbe_prv_data *);
        void (*disable_tx)(struct xgbe_prv_data *);
@@ -707,9 +667,50 @@ struct xgbe_hw_if {
        int (*set_rss_lookup_table)(struct xgbe_prv_data *, const u32 *);
 };
 
+/* 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 xgbe_phy_impl_if {
+       /* Perform Setup/teardown actions */
+       int (*init)(struct xgbe_prv_data *);
+       void (*exit)(struct xgbe_prv_data *);
+
+       /* Perform start/stop specific actions */
+       int (*reset)(struct xgbe_prv_data *);
+       int (*start)(struct xgbe_prv_data *);
+       void (*stop)(struct xgbe_prv_data *);
+
+       /* Return the link status */
+       int (*link_status)(struct xgbe_prv_data *);
+
+       /* Indicate if a particular speed is valid */
+       bool (*valid_speed)(struct xgbe_prv_data *, int);
+
+       /* Check if the specified mode can/should be used */
+       bool (*use_mode)(struct xgbe_prv_data *, enum xgbe_mode);
+       /* Switch the PHY into various modes */
+       void (*set_mode)(struct xgbe_prv_data *, enum xgbe_mode);
+       /* Retrieve mode needed for a specific speed */
+       enum xgbe_mode (*get_mode)(struct xgbe_prv_data *, int);
+       /* Retrieve new/next mode when trying to auto-negotiate */
+       enum xgbe_mode (*switch_mode)(struct xgbe_prv_data *);
+       /* Retrieve current mode */
+       enum xgbe_mode (*cur_mode)(struct xgbe_prv_data *);
+
+       /* Process results of auto-negotiation */
+       enum xgbe_mode (*an_outcome)(struct xgbe_prv_data *);
+
+       /* Pre/Post KR training enablement support */
+       void (*kr_training_pre)(struct xgbe_prv_data *);
+       void (*kr_training_post)(struct xgbe_prv_data *);
+};
+
 struct xgbe_phy_if {
-       /* For initial PHY setup */
-       void (*phy_init)(struct xgbe_prv_data *);
+       /* For PHY setup/teardown */
+       int (*phy_init)(struct xgbe_prv_data *);
+       void (*phy_exit)(struct xgbe_prv_data *);
 
        /* For PHY support when setting device up/down */
        int (*phy_reset)(struct xgbe_prv_data *);
@@ -719,6 +720,12 @@ struct xgbe_phy_if {
        /* For PHY support while device is up */
        void (*phy_status)(struct xgbe_prv_data *);
        int (*phy_config_aneg)(struct xgbe_prv_data *);
+
+       /* For PHY settings validation */
+       bool (*phy_valid_speed)(struct xgbe_prv_data *, int);
+
+       /* PHY implementation specific services */
+       struct xgbe_phy_impl_if phy_impl;
 };
 
 struct xgbe_desc_if {
@@ -778,11 +785,20 @@ struct xgbe_hw_features {
        unsigned int aux_snap_num;      /* Number of Aux snapshot inputs */
 };
 
+struct xgbe_version_data {
+       void (*init_function_ptrs_phy_impl)(struct xgbe_phy_if *);
+};
+
 struct xgbe_prv_data {
        struct net_device *netdev;
        struct platform_device *pdev;
        struct acpi_device *adev;
        struct device *dev;
+       struct platform_device *phy_pdev;
+       struct device *phy_dev;
+
+       /* Version related data */
+       struct xgbe_version_data *vdata;
 
        /* ACPI or DT flag */
        unsigned int use_acpi;
@@ -928,6 +944,8 @@ struct xgbe_prv_data {
        int phy_speed;
 
        /* MDIO/PHY related settings */
+       unsigned int phy_started;
+       void *phy_data;
        struct xgbe_phy phy;
        int mdio_mmd;
        unsigned long link_check;
@@ -938,21 +956,6 @@ struct xgbe_prv_data {
        int an_irq;
        struct work_struct an_irq_work;
 
-       unsigned int speed_set;
-
-       /* SerDes UEFI configurable settings.
-        *   Switching between modes/speeds requires new values for some
-        *   SerDes settings.  The values can be supplied as device
-        *   properties in array format.  The first array entry is for
-        *   1GbE, second for 2.5GbE and third for 10GbE
-        */
-       u32 serdes_blwc[XGBE_SPEEDS];
-       u32 serdes_cdr_rate[XGBE_SPEEDS];
-       u32 serdes_pq_skew[XGBE_SPEEDS];
-       u32 serdes_tx_amp[XGBE_SPEEDS];
-       u32 serdes_dfe_tap_cfg[XGBE_SPEEDS];
-       u32 serdes_dfe_tap_ena[XGBE_SPEEDS];
-
        /* Auto-negotiation state machine support */
        unsigned int an_int;
        struct mutex an_mutex;
@@ -982,6 +985,7 @@ struct xgbe_prv_data {
 
 void xgbe_init_function_ptrs_dev(struct xgbe_hw_if *);
 void xgbe_init_function_ptrs_phy(struct xgbe_phy_if *);
+void xgbe_init_function_ptrs_phy_v1(struct xgbe_phy_if *);
 void xgbe_init_function_ptrs_desc(struct xgbe_desc_if *);
 const struct net_device_ops *xgbe_get_netdev_ops(void);
 const struct ethtool_ops *xgbe_get_ethtool_ops(void);