amd-xgbe: Add PCI device support
authorLendacky, Thomas <Thomas.Lendacky@amd.com>
Thu, 10 Nov 2016 23:09:55 +0000 (17:09 -0600)
committerDavid S. Miller <davem@davemloft.net>
Sun, 13 Nov 2016 05:56:26 +0000 (00:56 -0500)
Add support for new PCI devices to the driver.

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

index 0038709..9255ce4 100644 (file)
@@ -173,8 +173,8 @@ config SUNLANCE
 
 config AMD_XGBE
        tristate "AMD 10GbE Ethernet driver"
-       depends on ((OF_NET && OF_ADDRESS) || ACPI) && HAS_IOMEM && HAS_DMA
-       depends on ARM64 || COMPILE_TEST
+       depends on ((OF_NET && OF_ADDRESS) || ACPI || PCI) && HAS_IOMEM && HAS_DMA
+       depends on X86 || ARM64 || COMPILE_TEST
        select BITREVERSE
        select CRC32
        select PTP_1588_CLOCK
index 217d59e..d1ce1c1 100644 (file)
@@ -3,8 +3,9 @@ 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-phy-v1.o \
+                xgbe-phy-v1.o xgbe-phy-v2.o \
                 xgbe-platform.o
 
+amd-xgbe-$(CONFIG_PCI) += xgbe-pci.o
 amd-xgbe-$(CONFIG_AMD_XGBE_DCB) += xgbe-dcb.o
 amd-xgbe-$(CONFIG_DEBUG_FS) += xgbe-debugfs.o
index 8036ee5..b54862b 100644 (file)
 
 /* PCS register offsets */
 #define PCS_V1_WINDOW_SELECT           0x03fc
+#define PCS_V2_WINDOW_DEF              0x9060
 #define PCS_V2_WINDOW_SELECT           0x9064
 
+/* PCS register entry bit positions and sizes */
+#define PCS_V2_WINDOW_DEF_OFFSET_INDEX 6
+#define PCS_V2_WINDOW_DEF_OFFSET_WIDTH 14
+#define PCS_V2_WINDOW_DEF_SIZE_INDEX   2
+#define PCS_V2_WINDOW_DEF_SIZE_WIDTH   4
+
 /* SerDes integration register offsets */
 #define SIR0_KR_RT_1                   0x002c
 #define SIR0_STATUS                    0x0040
 #define RXTX_REG129_RXDFE_CONFIG_INDEX 14
 #define RXTX_REG129_RXDFE_CONFIG_WIDTH 2
 
+/* MAC Control register offsets */
+#define XP_PROP_0                      0x0000
+#define XP_PROP_1                      0x0004
+#define XP_PROP_2                      0x0008
+#define XP_PROP_3                      0x000c
+#define XP_PROP_4                      0x0010
+#define XP_PROP_5                      0x0014
+#define XP_MAC_ADDR_LO                 0x0020
+#define XP_MAC_ADDR_HI                 0x0024
+#define XP_DRIVER_INT_REQ              0x0060
+#define XP_DRIVER_INT_RO               0x0064
+#define XP_DRIVER_SCRATCH_0            0x0068
+#define XP_DRIVER_SCRATCH_1            0x006c
+#define XP_INT_EN                      0x0078
+
+/* MAC Control register entry bit positions and sizes */
+#define XP_DRIVER_INT_REQ_REQUEST_INDEX                0
+#define XP_DRIVER_INT_REQ_REQUEST_WIDTH                1
+#define XP_DRIVER_INT_RO_STATUS_INDEX          0
+#define XP_DRIVER_INT_RO_STATUS_WIDTH          1
+#define XP_DRIVER_SCRATCH_0_COMMAND_INDEX      0
+#define XP_DRIVER_SCRATCH_0_COMMAND_WIDTH      8
+#define XP_DRIVER_SCRATCH_0_SUB_COMMAND_INDEX  8
+#define XP_DRIVER_SCRATCH_0_SUB_COMMAND_WIDTH  8
+#define XP_MAC_ADDR_HI_VALID_INDEX             31
+#define XP_MAC_ADDR_HI_VALID_WIDTH             1
+#define XP_PROP_0_CONN_TYPE_INDEX              28
+#define XP_PROP_0_CONN_TYPE_WIDTH              3
+#define XP_PROP_0_MDIO_ADDR_INDEX              16
+#define XP_PROP_0_MDIO_ADDR_WIDTH              5
+#define XP_PROP_0_PORT_ID_INDEX                        0
+#define XP_PROP_0_PORT_ID_WIDTH                        8
+#define XP_PROP_0_PORT_MODE_INDEX              8
+#define XP_PROP_0_PORT_MODE_WIDTH              4
+#define XP_PROP_0_PORT_SPEEDS_INDEX            23
+#define XP_PROP_0_PORT_SPEEDS_WIDTH            4
+#define XP_PROP_1_MAX_RX_DMA_INDEX             24
+#define XP_PROP_1_MAX_RX_DMA_WIDTH             5
+#define XP_PROP_1_MAX_RX_QUEUES_INDEX          8
+#define XP_PROP_1_MAX_RX_QUEUES_WIDTH          5
+#define XP_PROP_1_MAX_TX_DMA_INDEX             16
+#define XP_PROP_1_MAX_TX_DMA_WIDTH             5
+#define XP_PROP_1_MAX_TX_QUEUES_INDEX          0
+#define XP_PROP_1_MAX_TX_QUEUES_WIDTH          5
+#define XP_PROP_2_RX_FIFO_SIZE_INDEX           16
+#define XP_PROP_2_RX_FIFO_SIZE_WIDTH           16
+#define XP_PROP_2_TX_FIFO_SIZE_INDEX           0
+#define XP_PROP_2_TX_FIFO_SIZE_WIDTH           16
+
 /* Descriptor/Packet entry bit positions and sizes */
 #define RX_PACKET_ERRORS_CRC_INDEX             2
 #define RX_PACKET_ERRORS_CRC_WIDTH             1
@@ -1240,6 +1296,16 @@ do {                                                                     \
 /* Macros for building, reading or writing register values or bits
  * within the register values of XPCS registers.
  */
+#define XPCS_GET_BITS(_var, _prefix, _field)                           \
+       GET_BITS((_var),                                                \
+                _prefix##_##_field##_INDEX,                            \
+                _prefix##_##_field##_WIDTH)
+
+#define XPCS_SET_BITS(_var, _prefix, _field, _val)                      \
+       SET_BITS((_var),                                                \
+                _prefix##_##_field##_INDEX,                            \
+                _prefix##_##_field##_WIDTH, (_val))
+
 #define XPCS32_IOWRITE(_pdata, _off, _val)                             \
        iowrite32(_val, (_pdata)->xpcs_regs + (_off))
 
@@ -1328,6 +1394,39 @@ do {                                                                     \
        XRXTX_IOWRITE((_pdata), _reg, reg_val);                         \
 } while (0)
 
+/* Macros for building, reading or writing register values or bits
+ * within the register values of MAC Control registers.
+ */
+#define XP_GET_BITS(_var, _prefix, _field)                             \
+       GET_BITS((_var),                                                \
+                _prefix##_##_field##_INDEX,                            \
+                _prefix##_##_field##_WIDTH)
+
+#define XP_SET_BITS(_var, _prefix, _field, _val)                       \
+       SET_BITS((_var),                                                \
+                _prefix##_##_field##_INDEX,                            \
+                _prefix##_##_field##_WIDTH, (_val))
+
+#define XP_IOREAD(_pdata, _reg)                                                \
+       ioread32((_pdata)->xprop_regs + (_reg))
+
+#define XP_IOREAD_BITS(_pdata, _reg, _field)                           \
+       GET_BITS(XP_IOREAD((_pdata), (_reg)),                           \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH)
+
+#define XP_IOWRITE(_pdata, _reg, _val)                                 \
+       iowrite32((_val), (_pdata)->xprop_regs + (_reg))
+
+#define XP_IOWRITE_BITS(_pdata, _reg, _field, _val)                    \
+do {                                                                   \
+       u32 reg_val = XP_IOREAD((_pdata), (_reg));                      \
+       SET_BITS(reg_val,                                               \
+                _reg##_##_field##_INDEX,                               \
+                _reg##_##_field##_WIDTH, (_val));                      \
+       XP_IOWRITE((_pdata), (_reg), reg_val);                          \
+} while (0)
+
 /* Macros for building, reading or writing register values or bits
  * using MDIO.  Different from above because of the use of standardized
  * Linux include values.  No shifting is performed with the bit
index 96f485a..951b5ab 100644 (file)
@@ -316,6 +316,66 @@ static const struct file_operations xpcs_reg_value_fops = {
        .write = xpcs_reg_value_write,
 };
 
+static ssize_t xprop_reg_addr_read(struct file *filp, char __user *buffer,
+                                  size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+
+       return xgbe_common_read(buffer, count, ppos, pdata->debugfs_xprop_reg);
+}
+
+static ssize_t xprop_reg_addr_write(struct file *filp,
+                                   const char __user *buffer,
+                                   size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+
+       return xgbe_common_write(buffer, count, ppos,
+                                &pdata->debugfs_xprop_reg);
+}
+
+static ssize_t xprop_reg_value_read(struct file *filp, char __user *buffer,
+                                   size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+       unsigned int value;
+
+       value = XP_IOREAD(pdata, pdata->debugfs_xprop_reg);
+
+       return xgbe_common_read(buffer, count, ppos, value);
+}
+
+static ssize_t xprop_reg_value_write(struct file *filp,
+                                    const char __user *buffer,
+                                    size_t count, loff_t *ppos)
+{
+       struct xgbe_prv_data *pdata = filp->private_data;
+       unsigned int value;
+       ssize_t len;
+
+       len = xgbe_common_write(buffer, count, ppos, &value);
+       if (len < 0)
+               return len;
+
+       XP_IOWRITE(pdata, pdata->debugfs_xprop_reg, value);
+
+       return len;
+}
+
+static const struct file_operations xprop_reg_addr_fops = {
+       .owner = THIS_MODULE,
+       .open = simple_open,
+       .read =  xprop_reg_addr_read,
+       .write = xprop_reg_addr_write,
+};
+
+static const struct file_operations xprop_reg_value_fops = {
+       .owner = THIS_MODULE,
+       .open = simple_open,
+       .read =  xprop_reg_value_read,
+       .write = xprop_reg_value_write,
+};
+
 void xgbe_debugfs_init(struct xgbe_prv_data *pdata)
 {
        struct dentry *pfile;
@@ -367,6 +427,22 @@ void xgbe_debugfs_init(struct xgbe_prv_data *pdata)
        if (!pfile)
                netdev_err(pdata->netdev, "debugfs_create_file failed\n");
 
+       if (pdata->xprop_regs) {
+               pfile = debugfs_create_file("xprop_register", 0600,
+                                           pdata->xgbe_debugfs, pdata,
+                                           &xprop_reg_addr_fops);
+               if (!pfile)
+                       netdev_err(pdata->netdev,
+                                  "debugfs_create_file failed\n");
+
+               pfile = debugfs_create_file("xprop_register_value", 0600,
+                                           pdata->xgbe_debugfs, pdata,
+                                           &xprop_reg_value_fops);
+               if (!pfile)
+                       netdev_err(pdata->netdev,
+                                  "debugfs_create_file failed\n");
+       }
+
        kfree(buf);
 }
 
index a43e930..efa01a5 100644 (file)
@@ -374,6 +374,10 @@ static irqreturn_t xgbe_isr(int irq, void *data)
                }
        }
 
+       /* If there is not a separate AN irq, handle it here */
+       if (pdata->dev_irq == pdata->an_irq)
+               pdata->phy_if.an_isr(irq, pdata);
+
 isr_done:
        return IRQ_HANDLED;
 }
@@ -864,16 +868,16 @@ static int xgbe_start(struct xgbe_prv_data *pdata)
 
        hw_if->init(pdata);
 
-       ret = phy_if->phy_start(pdata);
-       if (ret)
-               goto err_phy;
-
        xgbe_napi_enable(pdata, 1);
 
        ret = xgbe_request_irqs(pdata);
        if (ret)
                goto err_napi;
 
+       ret = phy_if->phy_start(pdata);
+       if (ret)
+               goto err_irqs;
+
        hw_if->enable_tx(pdata);
        hw_if->enable_rx(pdata);
 
@@ -886,12 +890,12 @@ static int xgbe_start(struct xgbe_prv_data *pdata)
 
        return 0;
 
+err_irqs:
+       xgbe_free_irqs(pdata);
+
 err_napi:
        xgbe_napi_disable(pdata, 1);
 
-       phy_if->phy_stop(pdata);
-
-err_phy:
        hw_if->exit(pdata);
 
        return ret;
index c7187fc..697483a 100644 (file)
@@ -291,6 +291,19 @@ int xgbe_config_netdev(struct xgbe_prv_data *pdata)
        BUILD_BUG_ON_NOT_POWER_OF_2(XGBE_RX_DESC_CNT);
        pdata->rx_desc_count = XGBE_RX_DESC_CNT;
 
+       /* Adjust the number of queues based on interrupts assigned */
+       if (pdata->channel_irq_count) {
+               pdata->tx_ring_count = min_t(unsigned int, pdata->tx_ring_count,
+                                            pdata->channel_irq_count);
+               pdata->rx_ring_count = min_t(unsigned int, pdata->rx_ring_count,
+                                            pdata->channel_irq_count);
+
+               if (netif_msg_probe(pdata))
+                       dev_dbg(pdata->dev,
+                               "adjusted TX/RX DMA channel count = %u/%u\n",
+                               pdata->tx_ring_count, pdata->rx_ring_count);
+       }
+
        /* Set the number of queues */
        ret = netif_set_real_num_tx_queues(netdev, pdata->tx_ring_count);
        if (ret) {
@@ -393,6 +406,11 @@ int xgbe_config_netdev(struct xgbe_prv_data *pdata)
 
        xgbe_debugfs_init(pdata);
 
+       netif_dbg(pdata, drv, pdata->netdev, "%u Tx software queues\n",
+                 pdata->tx_ring_count);
+       netif_dbg(pdata, drv, pdata->netdev, "%u Rx software queues\n",
+                 pdata->rx_ring_count);
+
        return 0;
 
 err_wq:
@@ -431,11 +449,17 @@ static int __init xgbe_mod_init(void)
        if (ret)
                return ret;
 
+       ret = xgbe_pci_init();
+       if (ret)
+               return ret;
+
        return 0;
 }
 
 static void __exit xgbe_mod_exit(void)
 {
+       xgbe_pci_exit();
+
        xgbe_platform_exit();
 }
 
index 723eb90..6d99943 100644 (file)
@@ -640,6 +640,11 @@ static irqreturn_t xgbe_an_isr(int irq, void *data)
        return IRQ_HANDLED;
 }
 
+static irqreturn_t xgbe_an_combined_isr(int irq, struct xgbe_prv_data *pdata)
+{
+       return xgbe_an_isr(irq, pdata);
+}
+
 static void xgbe_an_irq_work(struct work_struct *work)
 {
        struct xgbe_prv_data *pdata = container_of(work,
@@ -1237,7 +1242,8 @@ static void xgbe_phy_stop(struct xgbe_prv_data *pdata)
        /* Disable auto-negotiation */
        xgbe_an_disable_all(pdata);
 
-       devm_free_irq(pdata->dev, pdata->an_irq, pdata);
+       if (pdata->dev_irq != pdata->an_irq)
+               devm_free_irq(pdata->dev, pdata->an_irq, pdata);
 
        pdata->phy_if.phy_impl.stop(pdata);
 
@@ -1258,12 +1264,15 @@ static int xgbe_phy_start(struct xgbe_prv_data *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");
-               goto err_stop;
+       /* If we have a separate AN irq, enable it */
+       if (pdata->dev_irq != pdata->an_irq) {
+               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");
+                       goto err_stop;
+               }
        }
 
        /* Set initial mode - call the mode setting routines
@@ -1289,7 +1298,8 @@ static int xgbe_phy_start(struct xgbe_prv_data *pdata)
        return xgbe_phy_config_aneg(pdata);
 
 err_irq:
-       devm_free_irq(pdata->dev, pdata->an_irq, pdata);
+       if (pdata->dev_irq != pdata->an_irq)
+               devm_free_irq(pdata->dev, pdata->an_irq, pdata);
 
 err_stop:
        pdata->phy_if.phy_impl.stop(pdata);
@@ -1442,4 +1452,6 @@ void xgbe_init_function_ptrs_phy(struct xgbe_phy_if *phy_if)
        phy_if->phy_config_aneg = xgbe_phy_config_aneg;
 
        phy_if->phy_valid_speed = xgbe_phy_valid_speed;
+
+       phy_if->an_isr          = xgbe_an_combined_isr;
 }
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-pci.c b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c
new file mode 100644 (file)
index 0000000..fe77945
--- /dev/null
@@ -0,0 +1,523 @@
+/*
+ * 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/device.h>
+#include <linux/pci.h>
+#include <linux/log2.h>
+
+#include "xgbe.h"
+#include "xgbe-common.h"
+
+static int xgbe_config_msi(struct xgbe_prv_data *pdata)
+{
+       unsigned int msi_count;
+       unsigned int i, j;
+       int ret;
+
+       msi_count = XGBE_MSIX_BASE_COUNT;
+       msi_count += max(pdata->rx_ring_count,
+                        pdata->tx_ring_count);
+       msi_count = roundup_pow_of_two(msi_count);
+
+       ret = pci_enable_msi_exact(pdata->pcidev, msi_count);
+       if (ret < 0) {
+               dev_info(pdata->dev, "MSI request for %u interrupts failed\n",
+                        msi_count);
+
+               ret = pci_enable_msi(pdata->pcidev);
+               if (ret < 0) {
+                       dev_info(pdata->dev, "MSI enablement failed\n");
+                       return ret;
+               }
+
+               msi_count = 1;
+       }
+
+       pdata->irq_count = msi_count;
+
+       pdata->dev_irq = pdata->pcidev->irq;
+
+       if (msi_count > 1) {
+               pdata->ecc_irq = pdata->pcidev->irq + 1;
+               pdata->i2c_irq = pdata->pcidev->irq + 2;
+               pdata->an_irq = pdata->pcidev->irq + 3;
+
+               for (i = XGBE_MSIX_BASE_COUNT, j = 0;
+                    (i < msi_count) && (j < XGBE_MAX_DMA_CHANNELS);
+                    i++, j++)
+                       pdata->channel_irq[j] = pdata->pcidev->irq + i;
+               pdata->channel_irq_count = j;
+
+               pdata->per_channel_irq = 1;
+       } else {
+               pdata->ecc_irq = pdata->pcidev->irq;
+               pdata->i2c_irq = pdata->pcidev->irq;
+               pdata->an_irq = pdata->pcidev->irq;
+       }
+
+       if (netif_msg_probe(pdata))
+               dev_dbg(pdata->dev, "MSI interrupts enabled\n");
+
+       return 0;
+}
+
+static int xgbe_config_msix(struct xgbe_prv_data *pdata)
+{
+       unsigned int msix_count;
+       unsigned int i, j;
+       int ret;
+
+       msix_count = XGBE_MSIX_BASE_COUNT;
+       msix_count += max(pdata->rx_ring_count,
+                         pdata->tx_ring_count);
+
+       pdata->msix_entries = devm_kcalloc(pdata->dev, msix_count,
+                                          sizeof(struct msix_entry),
+                                          GFP_KERNEL);
+       if (!pdata->msix_entries)
+               return -ENOMEM;
+
+       for (i = 0; i < msix_count; i++)
+               pdata->msix_entries[i].entry = i;
+
+       ret = pci_enable_msix_range(pdata->pcidev, pdata->msix_entries,
+                                   XGBE_MSIX_MIN_COUNT, msix_count);
+       if (ret < 0) {
+               dev_info(pdata->dev, "MSI-X enablement failed\n");
+               devm_kfree(pdata->dev, pdata->msix_entries);
+               pdata->msix_entries = NULL;
+               return ret;
+       }
+
+       pdata->irq_count = ret;
+
+       pdata->dev_irq = pdata->msix_entries[0].vector;
+       pdata->ecc_irq = pdata->msix_entries[1].vector;
+       pdata->i2c_irq = pdata->msix_entries[2].vector;
+       pdata->an_irq = pdata->msix_entries[3].vector;
+
+       for (i = XGBE_MSIX_BASE_COUNT, j = 0; i < ret; i++, j++)
+               pdata->channel_irq[j] = pdata->msix_entries[i].vector;
+       pdata->channel_irq_count = j;
+
+       pdata->per_channel_irq = 1;
+
+       if (netif_msg_probe(pdata))
+               dev_dbg(pdata->dev, "MSI-X interrupts enabled\n");
+
+       return 0;
+}
+
+static int xgbe_config_irqs(struct xgbe_prv_data *pdata)
+{
+       int ret;
+
+       ret = xgbe_config_msix(pdata);
+       if (!ret)
+               goto out;
+
+       ret = xgbe_config_msi(pdata);
+       if (!ret)
+               goto out;
+
+       pdata->irq_count = 1;
+       pdata->irq_shared = 1;
+
+       pdata->dev_irq = pdata->pcidev->irq;
+       pdata->ecc_irq = pdata->pcidev->irq;
+       pdata->i2c_irq = pdata->pcidev->irq;
+       pdata->an_irq = pdata->pcidev->irq;
+
+out:
+       if (netif_msg_probe(pdata)) {
+               unsigned int i;
+
+               dev_dbg(pdata->dev, " dev irq=%d\n", pdata->dev_irq);
+               dev_dbg(pdata->dev, " ecc irq=%d\n", pdata->ecc_irq);
+               dev_dbg(pdata->dev, " i2c irq=%d\n", pdata->i2c_irq);
+               dev_dbg(pdata->dev, "  an irq=%d\n", pdata->an_irq);
+               for (i = 0; i < pdata->channel_irq_count; i++)
+                       dev_dbg(pdata->dev, " dma%u irq=%d\n",
+                               i, pdata->channel_irq[i]);
+       }
+
+       return 0;
+}
+
+static int xgbe_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+       struct xgbe_prv_data *pdata;
+       struct device *dev = &pdev->dev;
+       void __iomem * const *iomap_table;
+       unsigned int ma_lo, ma_hi;
+       unsigned int reg;
+       int bar_mask;
+       int ret;
+
+       pdata = xgbe_alloc_pdata(dev);
+       if (IS_ERR(pdata)) {
+               ret = PTR_ERR(pdata);
+               goto err_alloc;
+       }
+
+       pdata->pcidev = pdev;
+       pci_set_drvdata(pdev, pdata);
+
+       /* Get the version data */
+       pdata->vdata = (struct xgbe_version_data *)id->driver_data;
+
+       ret = pcim_enable_device(pdev);
+       if (ret) {
+               dev_err(dev, "pcim_enable_device failed\n");
+               goto err_pci_enable;
+       }
+
+       /* Obtain the mmio areas for the device */
+       bar_mask = pci_select_bars(pdev, IORESOURCE_MEM);
+       ret = pcim_iomap_regions(pdev, bar_mask, XGBE_DRV_NAME);
+       if (ret) {
+               dev_err(dev, "pcim_iomap_regions failed\n");
+               goto err_pci_enable;
+       }
+
+       iomap_table = pcim_iomap_table(pdev);
+       if (!iomap_table) {
+               dev_err(dev, "pcim_iomap_table failed\n");
+               ret = -ENOMEM;
+               goto err_pci_enable;
+       }
+
+       pdata->xgmac_regs = iomap_table[XGBE_XGMAC_BAR];
+       if (!pdata->xgmac_regs) {
+               dev_err(dev, "xgmac ioremap failed\n");
+               ret = -ENOMEM;
+               goto err_pci_enable;
+       }
+       pdata->xprop_regs = pdata->xgmac_regs + XGBE_MAC_PROP_OFFSET;
+       pdata->xi2c_regs = pdata->xgmac_regs + XGBE_I2C_CTRL_OFFSET;
+       if (netif_msg_probe(pdata)) {
+               dev_dbg(dev, "xgmac_regs = %p\n", pdata->xgmac_regs);
+               dev_dbg(dev, "xprop_regs = %p\n", pdata->xprop_regs);
+               dev_dbg(dev, "xi2c_regs  = %p\n", pdata->xi2c_regs);
+       }
+
+       pdata->xpcs_regs = iomap_table[XGBE_XPCS_BAR];
+       if (!pdata->xpcs_regs) {
+               dev_err(dev, "xpcs ioremap failed\n");
+               ret = -ENOMEM;
+               goto err_pci_enable;
+       }
+       if (netif_msg_probe(pdata))
+               dev_dbg(dev, "xpcs_regs  = %p\n", pdata->xpcs_regs);
+
+       /* Configure the PCS indirect addressing support */
+       reg = XPCS32_IOREAD(pdata, PCS_V2_WINDOW_DEF);
+       pdata->xpcs_window = XPCS_GET_BITS(reg, PCS_V2_WINDOW_DEF, OFFSET);
+       pdata->xpcs_window <<= 6;
+       pdata->xpcs_window_size = XPCS_GET_BITS(reg, PCS_V2_WINDOW_DEF, SIZE);
+       pdata->xpcs_window_size = 1 << (pdata->xpcs_window_size + 7);
+       pdata->xpcs_window_mask = pdata->xpcs_window_size - 1;
+       if (netif_msg_probe(pdata)) {
+               dev_dbg(dev, "xpcs window      = %#010x\n",
+                       pdata->xpcs_window);
+               dev_dbg(dev, "xpcs window size = %#010x\n",
+                       pdata->xpcs_window_size);
+               dev_dbg(dev, "xpcs window mask = %#010x\n",
+                       pdata->xpcs_window_mask);
+       }
+
+       pci_set_master(pdev);
+
+       /* Enable all interrupts in the hardware */
+       XP_IOWRITE(pdata, XP_INT_EN, 0x1fffff);
+
+       /* Retrieve the MAC address */
+       ma_lo = XP_IOREAD(pdata, XP_MAC_ADDR_LO);
+       ma_hi = XP_IOREAD(pdata, XP_MAC_ADDR_HI);
+       pdata->mac_addr[0] = ma_lo & 0xff;
+       pdata->mac_addr[1] = (ma_lo >> 8) & 0xff;
+       pdata->mac_addr[2] = (ma_lo >> 16) & 0xff;
+       pdata->mac_addr[3] = (ma_lo >> 24) & 0xff;
+       pdata->mac_addr[4] = ma_hi & 0xff;
+       pdata->mac_addr[5] = (ma_hi >> 8) & 0xff;
+       if (!XP_GET_BITS(ma_hi, XP_MAC_ADDR_HI, VALID) ||
+           !is_valid_ether_addr(pdata->mac_addr)) {
+               dev_err(dev, "invalid mac address\n");
+               ret = -EINVAL;
+               goto err_pci_enable;
+       }
+
+       /* Clock settings */
+       pdata->sysclk_rate = XGBE_V2_DMA_CLOCK_FREQ;
+       pdata->ptpclk_rate = XGBE_V2_PTP_CLOCK_FREQ;
+
+       /* Set the DMA coherency values */
+       pdata->coherent = 1;
+       pdata->axdomain = XGBE_DMA_OS_AXDOMAIN;
+       pdata->arcache = XGBE_DMA_OS_ARCACHE;
+       pdata->awcache = XGBE_DMA_OS_AWCACHE;
+
+       /* Set the maximum channels and queues */
+       reg = XP_IOREAD(pdata, XP_PROP_1);
+       pdata->tx_max_channel_count = XP_GET_BITS(reg, XP_PROP_1, MAX_TX_DMA);
+       pdata->rx_max_channel_count = XP_GET_BITS(reg, XP_PROP_1, MAX_RX_DMA);
+       pdata->tx_max_q_count = XP_GET_BITS(reg, XP_PROP_1, MAX_TX_QUEUES);
+       pdata->rx_max_q_count = XP_GET_BITS(reg, XP_PROP_1, MAX_RX_QUEUES);
+       if (netif_msg_probe(pdata)) {
+               dev_dbg(dev, "max tx/rx channel count = %u/%u\n",
+                       pdata->tx_max_channel_count,
+                       pdata->tx_max_channel_count);
+               dev_dbg(dev, "max tx/rx hw queue count = %u/%u\n",
+                       pdata->tx_max_q_count, pdata->rx_max_q_count);
+       }
+
+       /* Set the hardware channel and queue counts */
+       xgbe_set_counts(pdata);
+
+       /* Set the maximum fifo amounts */
+       reg = XP_IOREAD(pdata, XP_PROP_2);
+       pdata->tx_max_fifo_size = XP_GET_BITS(reg, XP_PROP_2, TX_FIFO_SIZE);
+       pdata->tx_max_fifo_size *= 16384;
+       pdata->tx_max_fifo_size = min(pdata->tx_max_fifo_size,
+                                     pdata->vdata->tx_max_fifo_size);
+       pdata->rx_max_fifo_size = XP_GET_BITS(reg, XP_PROP_2, RX_FIFO_SIZE);
+       pdata->rx_max_fifo_size *= 16384;
+       pdata->rx_max_fifo_size = min(pdata->rx_max_fifo_size,
+                                     pdata->vdata->rx_max_fifo_size);
+       if (netif_msg_probe(pdata))
+               dev_dbg(dev, "max tx/rx max fifo size = %u/%u\n",
+                       pdata->tx_max_fifo_size, pdata->rx_max_fifo_size);
+
+       /* Configure interrupt support */
+       ret = xgbe_config_irqs(pdata);
+       if (ret)
+               goto err_pci_enable;
+
+       /* Configure the netdev resource */
+       ret = xgbe_config_netdev(pdata);
+       if (ret)
+               goto err_pci_enable;
+
+       netdev_notice(pdata->netdev, "net device enabled\n");
+
+       return 0;
+
+err_pci_enable:
+       xgbe_free_pdata(pdata);
+
+err_alloc:
+       dev_notice(dev, "net device not enabled\n");
+
+       return ret;
+}
+
+static void xgbe_pci_remove(struct pci_dev *pdev)
+{
+       struct xgbe_prv_data *pdata = pci_get_drvdata(pdev);
+
+       xgbe_deconfig_netdev(pdata);
+
+       xgbe_free_pdata(pdata);
+}
+
+#ifdef CONFIG_PM
+static int xgbe_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+       struct xgbe_prv_data *pdata = pci_get_drvdata(pdev);
+       struct net_device *netdev = pdata->netdev;
+       int ret = 0;
+
+       if (netif_running(netdev))
+               ret = xgbe_powerdown(netdev, XGMAC_DRIVER_CONTEXT);
+
+       pdata->lpm_ctrl = XMDIO_READ(pdata, MDIO_MMD_PCS, MDIO_CTRL1);
+       pdata->lpm_ctrl |= MDIO_CTRL1_LPOWER;
+       XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, pdata->lpm_ctrl);
+
+       return ret;
+}
+
+static int xgbe_pci_resume(struct pci_dev *pdev)
+{
+       struct xgbe_prv_data *pdata = pci_get_drvdata(pdev);
+       struct net_device *netdev = pdata->netdev;
+       int ret = 0;
+
+       pdata->lpm_ctrl &= ~MDIO_CTRL1_LPOWER;
+       XMDIO_WRITE(pdata, MDIO_MMD_PCS, MDIO_CTRL1, pdata->lpm_ctrl);
+
+       if (netif_running(netdev)) {
+               ret = xgbe_powerup(netdev, XGMAC_DRIVER_CONTEXT);
+
+               /* Schedule a restart in case the link or phy state changed
+                * while we were powered down.
+                */
+               schedule_work(&pdata->restart_work);
+       }
+
+       return ret;
+}
+#endif /* CONFIG_PM */
+
+static const struct xgbe_version_data xgbe_v2a = {
+       .init_function_ptrs_phy_impl    = xgbe_init_function_ptrs_phy_v2,
+       .xpcs_access                    = XGBE_XPCS_ACCESS_V2,
+       .mmc_64bit                      = 1,
+       .tx_max_fifo_size               = 229376,
+       .rx_max_fifo_size               = 229376,
+       .tx_tstamp_workaround           = 1,
+};
+
+static const struct xgbe_version_data xgbe_v2b = {
+       .init_function_ptrs_phy_impl    = xgbe_init_function_ptrs_phy_v2,
+       .xpcs_access                    = XGBE_XPCS_ACCESS_V2,
+       .mmc_64bit                      = 1,
+       .tx_max_fifo_size               = 65536,
+       .rx_max_fifo_size               = 65536,
+       .tx_tstamp_workaround           = 1,
+};
+
+static const struct pci_device_id xgbe_pci_table[] = {
+       { PCI_VDEVICE(AMD, 0x1458),
+         .driver_data = (kernel_ulong_t)&xgbe_v2a },
+       { PCI_VDEVICE(AMD, 0x1459),
+         .driver_data = (kernel_ulong_t)&xgbe_v2b },
+       /* Last entry must be zero */
+       { 0, }
+};
+MODULE_DEVICE_TABLE(pci, xgbe_pci_table);
+
+static struct pci_driver xgbe_driver = {
+       .name = XGBE_DRV_NAME,
+       .id_table = xgbe_pci_table,
+       .probe = xgbe_pci_probe,
+       .remove = xgbe_pci_remove,
+#ifdef CONFIG_PM
+       .suspend = xgbe_pci_suspend,
+       .resume = xgbe_pci_resume,
+#endif
+};
+
+int xgbe_pci_init(void)
+{
+       return pci_register_driver(&xgbe_driver);
+}
+
+void xgbe_pci_exit(void)
+{
+       pci_unregister_driver(&xgbe_driver);
+}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c
new file mode 100644 (file)
index 0000000..7ae0abc
--- /dev/null
@@ -0,0 +1,854 @@
+/*
+ * 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/mdio.h>
+#include <linux/phy.h>
+
+#include "xgbe.h"
+#include "xgbe-common.h"
+
+#define XGBE_PHY_PORT_SPEED_100                BIT(0)
+#define XGBE_PHY_PORT_SPEED_1000       BIT(1)
+#define XGBE_PHY_PORT_SPEED_2500       BIT(2)
+#define XGBE_PHY_PORT_SPEED_10000      BIT(3)
+
+/* Rate-change complete wait/retry count */
+#define XGBE_RATECHANGE_COUNT          500
+
+enum xgbe_port_mode {
+       XGBE_PORT_MODE_RSVD = 0,
+       XGBE_PORT_MODE_BACKPLANE,
+       XGBE_PORT_MODE_BACKPLANE_2500,
+       XGBE_PORT_MODE_1000BASE_T,
+       XGBE_PORT_MODE_1000BASE_X,
+       XGBE_PORT_MODE_NBASE_T,
+       XGBE_PORT_MODE_10GBASE_T,
+       XGBE_PORT_MODE_10GBASE_R,
+       XGBE_PORT_MODE_SFP,
+       XGBE_PORT_MODE_MAX,
+};
+
+enum xgbe_conn_type {
+       XGBE_CONN_TYPE_NONE = 0,
+       XGBE_CONN_TYPE_SFP,
+       XGBE_CONN_TYPE_MDIO,
+       XGBE_CONN_TYPE_BACKPLANE,
+       XGBE_CONN_TYPE_MAX,
+};
+
+/* PHY related configuration information */
+struct xgbe_phy_data {
+       enum xgbe_port_mode port_mode;
+
+       unsigned int port_id;
+
+       unsigned int port_speeds;
+
+       enum xgbe_conn_type conn_type;
+
+       enum xgbe_mode cur_mode;
+       enum xgbe_mode start_mode;
+
+       unsigned int rrc_count;
+};
+
+static enum xgbe_mode xgbe_phy_an_outcome(struct xgbe_prv_data *pdata)
+{
+       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)
+               pdata->phy.lp_advertising |= ADVERTISED_1000baseKX_Full;
+
+       ad_reg &= lp_reg;
+       if (ad_reg & 0x80)
+               mode = XGBE_MODE_KR;
+       else if (ad_reg & 0x20)
+               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 enum xgbe_an_mode xgbe_phy_an_mode(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+
+       switch (phy_data->port_mode) {
+       case XGBE_PORT_MODE_BACKPLANE:
+               return XGBE_AN_MODE_CL73;
+       case XGBE_PORT_MODE_BACKPLANE_2500:
+               return XGBE_AN_MODE_NONE;
+       case XGBE_PORT_MODE_1000BASE_T:
+       case XGBE_PORT_MODE_1000BASE_X:
+       case XGBE_PORT_MODE_NBASE_T:
+       case XGBE_PORT_MODE_10GBASE_T:
+       case XGBE_PORT_MODE_10GBASE_R:
+       case XGBE_PORT_MODE_SFP:
+       default:
+               return XGBE_AN_MODE_NONE;
+       }
+}
+
+static void xgbe_phy_start_ratechange(struct xgbe_prv_data *pdata)
+{
+       if (!XP_IOREAD_BITS(pdata, XP_DRIVER_INT_RO, STATUS))
+               return;
+
+       /* Log if a previous command did not complete */
+       netif_dbg(pdata, link, pdata->netdev,
+                 "firmware mailbox not ready for command\n");
+}
+
+static void xgbe_phy_complete_ratechange(struct xgbe_prv_data *pdata)
+{
+       unsigned int wait;
+
+       /* Wait for command to complete */
+       wait = XGBE_RATECHANGE_COUNT;
+       while (wait--) {
+               if (!XP_IOREAD_BITS(pdata, XP_DRIVER_INT_RO, STATUS))
+                       return;
+
+               usleep_range(1000, 2000);
+       }
+
+       netif_dbg(pdata, link, pdata->netdev,
+                 "firmware mailbox command did not complete\n");
+}
+
+static void xgbe_phy_rrc(struct xgbe_prv_data *pdata)
+{
+       unsigned int s0;
+
+       xgbe_phy_start_ratechange(pdata);
+
+       /* Receiver Reset Cycle */
+       s0 = 0;
+       XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, COMMAND, 5);
+       XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 0);
+
+       /* Call FW to make the change */
+       XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_0, s0);
+       XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_1, 0);
+       XP_IOWRITE_BITS(pdata, XP_DRIVER_INT_REQ, REQUEST, 1);
+
+       xgbe_phy_complete_ratechange(pdata);
+
+       netif_dbg(pdata, link, pdata->netdev, "receiver reset complete\n");
+}
+
+static void xgbe_phy_power_off(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+
+       xgbe_phy_start_ratechange(pdata);
+
+       /* Call FW to make the change */
+       XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_0, 0);
+       XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_1, 0);
+       XP_IOWRITE_BITS(pdata, XP_DRIVER_INT_REQ, REQUEST, 1);
+
+       xgbe_phy_complete_ratechange(pdata);
+
+       phy_data->cur_mode = XGBE_MODE_UNKNOWN;
+
+       netif_dbg(pdata, link, pdata->netdev, "phy powered off\n");
+}
+
+static void xgbe_phy_kr_mode(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+       unsigned int s0;
+
+       xgbe_phy_start_ratechange(pdata);
+
+       /* 10G/KR */
+       s0 = 0;
+       XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, COMMAND, 4);
+       XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 0);
+
+       /* Call FW to make the change */
+       XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_0, s0);
+       XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_1, 0);
+       XP_IOWRITE_BITS(pdata, XP_DRIVER_INT_REQ, REQUEST, 1);
+
+       xgbe_phy_complete_ratechange(pdata);
+
+       phy_data->cur_mode = XGBE_MODE_KR;
+
+       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 s0;
+
+       xgbe_phy_start_ratechange(pdata);
+
+       /* 2.5G/KX */
+       s0 = 0;
+       XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, COMMAND, 2);
+       XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 0);
+
+       /* Call FW to make the change */
+       XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_0, s0);
+       XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_1, 0);
+       XP_IOWRITE_BITS(pdata, XP_DRIVER_INT_REQ, REQUEST, 1);
+
+       xgbe_phy_complete_ratechange(pdata);
+
+       phy_data->cur_mode = XGBE_MODE_KX_2500;
+
+       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 s0;
+
+       xgbe_phy_start_ratechange(pdata);
+
+       /* 1G/KX */
+       s0 = 0;
+       XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, COMMAND, 1);
+       XP_SET_BITS(s0, XP_DRIVER_SCRATCH_0, SUB_COMMAND, 3);
+
+       /* Call FW to make the change */
+       XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_0, s0);
+       XP_IOWRITE(pdata, XP_DRIVER_SCRATCH_1, 0);
+       XP_IOWRITE_BITS(pdata, XP_DRIVER_INT_REQ, REQUEST, 1);
+
+       xgbe_phy_complete_ratechange(pdata);
+
+       phy_data->cur_mode = XGBE_MODE_KX_1000;
+
+       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;
+
+       return phy_data->cur_mode;
+}
+
+static enum xgbe_mode xgbe_phy_switch_bp_2500_mode(struct xgbe_prv_data *pdata)
+{
+       return XGBE_MODE_KX_2500;
+}
+
+static enum xgbe_mode xgbe_phy_switch_bp_mode(struct xgbe_prv_data *pdata)
+{
+       /* If we are in KR switch to KX, and vice-versa */
+       switch (xgbe_phy_cur_mode(pdata)) {
+       case XGBE_MODE_KX_1000:
+               return XGBE_MODE_KR;
+       case XGBE_MODE_KR:
+       default:
+               return XGBE_MODE_KX_1000;
+       }
+}
+
+static enum xgbe_mode xgbe_phy_switch_mode(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+
+       switch (phy_data->port_mode) {
+       case XGBE_PORT_MODE_BACKPLANE:
+               return xgbe_phy_switch_bp_mode(pdata);
+       case XGBE_PORT_MODE_BACKPLANE_2500:
+               return xgbe_phy_switch_bp_2500_mode(pdata);
+       case XGBE_PORT_MODE_1000BASE_T:
+       case XGBE_PORT_MODE_1000BASE_X:
+       case XGBE_PORT_MODE_NBASE_T:
+       case XGBE_PORT_MODE_10GBASE_T:
+       case XGBE_PORT_MODE_10GBASE_R:
+       case XGBE_PORT_MODE_SFP:
+       default:
+               return XGBE_MODE_UNKNOWN;
+       }
+}
+
+static enum xgbe_mode xgbe_phy_get_bp_2500_mode(int speed)
+{
+       switch (speed) {
+       case SPEED_2500:
+               return XGBE_MODE_KX_2500;
+       default:
+               return XGBE_MODE_UNKNOWN;
+       }
+}
+
+static enum xgbe_mode xgbe_phy_get_bp_mode(int speed)
+{
+       switch (speed) {
+       case SPEED_1000:
+               return XGBE_MODE_KX_1000;
+       case SPEED_10000:
+               return XGBE_MODE_KR;
+       default:
+               return XGBE_MODE_UNKNOWN;
+       }
+}
+
+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 (phy_data->port_mode) {
+       case XGBE_PORT_MODE_BACKPLANE:
+               return xgbe_phy_get_bp_mode(speed);
+       case XGBE_PORT_MODE_BACKPLANE_2500:
+               return xgbe_phy_get_bp_2500_mode(speed);
+       case XGBE_PORT_MODE_1000BASE_T:
+       case XGBE_PORT_MODE_1000BASE_X:
+       case XGBE_PORT_MODE_NBASE_T:
+       case XGBE_PORT_MODE_10GBASE_T:
+       case XGBE_PORT_MODE_10GBASE_R:
+       case XGBE_PORT_MODE_SFP:
+       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_bp_2500_mode(struct xgbe_prv_data *pdata,
+                                     enum xgbe_mode mode)
+{
+       switch (mode) {
+       case XGBE_MODE_KX_2500:
+               return xgbe_phy_check_mode(pdata, mode,
+                                          ADVERTISED_2500baseX_Full);
+       default:
+               return false;
+       }
+}
+
+static bool xgbe_phy_use_bp_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_KR:
+               return xgbe_phy_check_mode(pdata, mode,
+                                          ADVERTISED_10000baseKR_Full);
+       default:
+               return false;
+       }
+}
+
+static bool xgbe_phy_use_mode(struct xgbe_prv_data *pdata, enum xgbe_mode mode)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+
+       switch (phy_data->port_mode) {
+       case XGBE_PORT_MODE_BACKPLANE:
+               return xgbe_phy_use_bp_mode(pdata, mode);
+       case XGBE_PORT_MODE_BACKPLANE_2500:
+               return xgbe_phy_use_bp_2500_mode(pdata, mode);
+       case XGBE_PORT_MODE_1000BASE_T:
+       case XGBE_PORT_MODE_1000BASE_X:
+       case XGBE_PORT_MODE_NBASE_T:
+       case XGBE_PORT_MODE_10GBASE_T:
+       case XGBE_PORT_MODE_10GBASE_R:
+       case XGBE_PORT_MODE_SFP:
+       default:
+               return false;
+       }
+}
+
+static bool xgbe_phy_valid_speed_bp_2500_mode(int speed)
+{
+       switch (speed) {
+       case SPEED_2500:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool xgbe_phy_valid_speed_bp_mode(int speed)
+{
+       switch (speed) {
+       case SPEED_1000:
+       case SPEED_10000:
+               return true;
+       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 (phy_data->port_mode) {
+       case XGBE_PORT_MODE_BACKPLANE:
+               return xgbe_phy_valid_speed_bp_mode(speed);
+       case XGBE_PORT_MODE_BACKPLANE_2500:
+               return xgbe_phy_valid_speed_bp_2500_mode(speed);
+       case XGBE_PORT_MODE_1000BASE_T:
+       case XGBE_PORT_MODE_1000BASE_X:
+       case XGBE_PORT_MODE_NBASE_T:
+       case XGBE_PORT_MODE_10GBASE_T:
+       case XGBE_PORT_MODE_10GBASE_R:
+       case XGBE_PORT_MODE_SFP:
+       default:
+               return false;
+       }
+}
+
+static int xgbe_phy_link_status(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+       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);
+       if (reg & MDIO_STAT1_LSTATUS)
+               return 1;
+
+       /* No link, attempt a receiver reset cycle */
+       if (phy_data->rrc_count++) {
+               phy_data->rrc_count = 0;
+               xgbe_phy_rrc(pdata);
+       }
+
+       return 0;
+}
+
+static bool xgbe_phy_port_mode_mismatch(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+
+       switch (phy_data->port_mode) {
+       case XGBE_PORT_MODE_BACKPLANE:
+               if ((phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) ||
+                   (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000))
+                       return false;
+               break;
+       case XGBE_PORT_MODE_BACKPLANE_2500:
+               if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_2500)
+                       return false;
+               break;
+       case XGBE_PORT_MODE_1000BASE_T:
+               if ((phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) ||
+                   (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000))
+                       return false;
+               break;
+       case XGBE_PORT_MODE_1000BASE_X:
+               if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000)
+                       return false;
+               break;
+       case XGBE_PORT_MODE_NBASE_T:
+               if ((phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) ||
+                   (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) ||
+                   (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_2500))
+                       return false;
+               break;
+       case XGBE_PORT_MODE_10GBASE_T:
+               if ((phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) ||
+                   (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000))
+                       return false;
+               break;
+       case XGBE_PORT_MODE_10GBASE_R:
+               if (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000)
+                       return false;
+               break;
+       case XGBE_PORT_MODE_SFP:
+               if ((phy_data->port_speeds & XGBE_PHY_PORT_SPEED_100) ||
+                   (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_1000) ||
+                   (phy_data->port_speeds & XGBE_PHY_PORT_SPEED_10000))
+                       return false;
+               break;
+       default:
+               break;
+       }
+
+       return true;
+}
+
+static bool xgbe_phy_conn_type_mismatch(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+
+       switch (phy_data->port_mode) {
+       case XGBE_PORT_MODE_BACKPLANE:
+       case XGBE_PORT_MODE_BACKPLANE_2500:
+               if (phy_data->conn_type == XGBE_CONN_TYPE_BACKPLANE)
+                       return false;
+               break;
+       case XGBE_PORT_MODE_1000BASE_T:
+       case XGBE_PORT_MODE_1000BASE_X:
+       case XGBE_PORT_MODE_NBASE_T:
+       case XGBE_PORT_MODE_10GBASE_T:
+       case XGBE_PORT_MODE_10GBASE_R:
+               if (phy_data->conn_type == XGBE_CONN_TYPE_MDIO)
+                       return false;
+               break;
+       case XGBE_PORT_MODE_SFP:
+               if (phy_data->conn_type == XGBE_CONN_TYPE_SFP)
+                       return false;
+               break;
+       default:
+               break;
+       }
+
+       return true;
+}
+
+static bool xgbe_phy_port_enabled(struct xgbe_prv_data *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 void xgbe_phy_stop(struct xgbe_prv_data *pdata)
+{
+       /* Power off the PHY */
+       xgbe_phy_power_off(pdata);
+}
+
+static int xgbe_phy_start(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+
+       /* Start in highest supported mode */
+       xgbe_phy_set_mode(pdata, phy_data->start_mode);
+
+       return 0;
+}
+
+static int xgbe_phy_reset(struct xgbe_prv_data *pdata)
+{
+       struct xgbe_phy_data *phy_data = pdata->phy_data;
+       enum xgbe_mode cur_mode;
+
+       /* Reset by power cycling the PHY */
+       cur_mode = phy_data->cur_mode;
+       xgbe_phy_power_off(pdata);
+       xgbe_phy_set_mode(pdata, cur_mode);
+
+       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;
+       unsigned int reg;
+
+       /* Check if enabled */
+       if (!xgbe_phy_port_enabled(pdata)) {
+               dev_info(pdata->dev, "device is not enabled\n");
+               return -ENODEV;
+       }
+
+       phy_data = devm_kzalloc(pdata->dev, sizeof(*phy_data), GFP_KERNEL);
+       if (!phy_data)
+               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);
+       if (netif_msg_probe(pdata)) {
+               dev_dbg(pdata->dev, "port mode=%u\n", phy_data->port_mode);
+               dev_dbg(pdata->dev, "port id=%u\n", phy_data->port_id);
+               dev_dbg(pdata->dev, "port speeds=%#x\n", phy_data->port_speeds);
+               dev_dbg(pdata->dev, "conn type=%u\n", phy_data->conn_type);
+       }
+
+       /* Validate the connection requested */
+       if (xgbe_phy_conn_type_mismatch(pdata)) {
+               dev_err(pdata->dev, "phy mode/connection mismatch (%#x/%#x)\n",
+                       phy_data->port_mode, phy_data->conn_type);
+       }
+
+       /* Validate the mode requested */
+       if (xgbe_phy_port_mode_mismatch(pdata)) {
+               dev_err(pdata->dev, "phy mode/speed mismatch (%#x/%#x)\n",
+                       phy_data->port_mode, phy_data->port_speeds);
+               return -EINVAL;
+       }
+
+       /* Indicate current mode is unknown */
+       phy_data->cur_mode = XGBE_MODE_UNKNOWN;
+
+       /* Initialize supported features */
+       pdata->phy.supported = 0;
+
+       switch (phy_data->port_mode) {
+       case XGBE_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 & XGBE_PHY_PORT_SPEED_1000) {
+                       pdata->phy.supported |= SUPPORTED_1000baseKX_Full;
+                       phy_data->start_mode = XGBE_MODE_KX_1000;
+               }
+               if (phy_data->port_speeds & XGBE_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 = XGBE_MODE_KR;
+               }
+               break;
+       case XGBE_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 = XGBE_MODE_KX_2500;
+               break;
+       case XGBE_PORT_MODE_1000BASE_T:
+       case XGBE_PORT_MODE_1000BASE_X:
+       case XGBE_PORT_MODE_NBASE_T:
+       case XGBE_PORT_MODE_10GBASE_T:
+       case XGBE_PORT_MODE_10GBASE_R:
+       case XGBE_PORT_MODE_SFP:
+       default:
+               return -EINVAL;
+       }
+
+       if (netif_msg_probe(pdata))
+               dev_dbg(pdata->dev, "phy supported=%#x\n",
+                       pdata->phy.supported);
+
+       return 0;
+}
+
+void xgbe_init_function_ptrs_phy_v2(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_mode               = xgbe_phy_an_mode;
+
+       phy_impl->an_outcome            = xgbe_phy_an_outcome;
+}
index 7a701de..8d438c3 100644 (file)
@@ -462,6 +462,9 @@ static int xgbe_platform_probe(struct platform_device *pdev)
        /* Set the hardware channel and queue counts */
        xgbe_set_counts(pdata);
 
+       /* Always have XGMAC and XPCS (auto-negotiation) interrupts */
+       pdata->irq_count = 2;
+
        /* Get the device interrupt */
        ret = platform_get_irq(pdev, 0);
        if (ret < 0) {
@@ -485,6 +488,10 @@ static int xgbe_platform_probe(struct platform_device *pdev)
 
                        pdata->channel_irq[i] = ret;
                }
+
+               pdata->channel_irq_count = max;
+
+               pdata->irq_count += max;
        }
 
        /* Get the auto-negotiation interrupt */
@@ -609,7 +616,7 @@ static SIMPLE_DEV_PM_OPS(xgbe_platform_pm_ops,
 
 static struct platform_driver xgbe_driver = {
        .driver = {
-               .name = "amd-xgbe",
+               .name = XGBE_DRV_NAME,
 #ifdef CONFIG_ACPI
                .acpi_match_table = xgbe_acpi_match,
 #endif
index 8523779..e22156e 100644 (file)
 #define XGBE_ACPI_DMA_FREQ     "amd,dma-freq"
 #define XGBE_ACPI_PTP_FREQ     "amd,ptp-freq"
 
+/* PCI BAR mapping */
+#define XGBE_XGMAC_BAR         0
+#define XGBE_XPCS_BAR          1
+#define XGBE_MAC_PROP_OFFSET   0x1d000
+#define XGBE_I2C_CTRL_OFFSET   0x1e000
+
+/* PCI MSIx support */
+#define XGBE_MSIX_BASE_COUNT   4
+#define XGBE_MSIX_MIN_COUNT    (XGBE_MSIX_BASE_COUNT + 1)
+
+/* PCI clock frequencies */
+#define XGBE_V2_DMA_CLOCK_FREQ 500000000       /* 500 MHz */
+#define XGBE_V2_PTP_CLOCK_FREQ 125000000       /* 125 MHz */
+
 /* Timestamp support - values based on 50MHz PTP clock
  *   50MHz => 20 nsec
  */
@@ -738,6 +752,9 @@ struct xgbe_phy_if {
        /* For PHY settings validation */
        bool (*phy_valid_speed)(struct xgbe_prv_data *, int);
 
+       /* For single interrupt support */
+       irqreturn_t (*an_isr)(int, struct xgbe_prv_data *);
+
        /* PHY implementation specific services */
        struct xgbe_phy_impl_if phy_impl;
 };
@@ -810,6 +827,7 @@ struct xgbe_version_data {
 
 struct xgbe_prv_data {
        struct net_device *netdev;
+       struct pci_dev *pcidev;
        struct platform_device *platdev;
        struct acpi_device *adev;
        struct device *dev;
@@ -828,6 +846,8 @@ struct xgbe_prv_data {
        void __iomem *rxtx_regs;        /* SerDes Rx/Tx CSRs */
        void __iomem *sir0_regs;        /* SerDes integration registers (1/2) */
        void __iomem *sir1_regs;        /* SerDes integration registers (2/2) */
+       void __iomem *xprop_regs;       /* XGBE property registers */
+       void __iomem *xi2c_regs;        /* XGBE I2C CSRs */
 
        /* Overall device lock */
        spinlock_t lock;
@@ -844,10 +864,17 @@ struct xgbe_prv_data {
        /* Flags representing xgbe_state */
        unsigned long dev_state;
 
+       struct msix_entry *msix_entries;
        int dev_irq;
-       unsigned int per_channel_irq;
+       int ecc_irq;
+       int i2c_irq;
        int channel_irq[XGBE_MAX_DMA_CHANNELS];
 
+       unsigned int per_channel_irq;
+       unsigned int irq_shared;
+       unsigned int irq_count;
+       unsigned int channel_irq_count;
+
        struct xgbe_hw_if hw_if;
        struct xgbe_phy_if phy_if;
        struct xgbe_desc_if desc_if;
@@ -1009,6 +1036,8 @@ struct xgbe_prv_data {
 
        unsigned int debugfs_xpcs_mmd;
        unsigned int debugfs_xpcs_reg;
+
+       unsigned int debugfs_xprop_reg;
 #endif
 };
 
@@ -1021,10 +1050,18 @@ void xgbe_deconfig_netdev(struct xgbe_prv_data *);
 
 int xgbe_platform_init(void);
 void xgbe_platform_exit(void);
+#ifdef CONFIG_PCI
+int xgbe_pci_init(void);
+void xgbe_pci_exit(void);
+#else
+static inline int xgbe_pci_init(void) { return 0; }
+static inline void xgbe_pci_exit(void) { }
+#endif
 
 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_phy_v2(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);