lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20161110230954.20704.90445.stgit@tlendack-t1.amdoffice.net>
Date:   Thu, 10 Nov 2016 17:09:55 -0600
From:   Tom Lendacky <thomas.lendacky@....com>
To:     <netdev@...r.kernel.org>
CC:     Florian Fainelli <f.fainelli@...il.com>,
        David Miller <davem@...emloft.net>
Subject: [PATCH net-next v1 03/11] amd-xgbe: Add PCI device support

Add support for new PCI devices to the driver.

Signed-off-by: Tom Lendacky <thomas.lendacky@....com>
---
 drivers/net/ethernet/amd/Kconfig              |    4 
 drivers/net/ethernet/amd/xgbe/Makefile        |    3 
 drivers/net/ethernet/amd/xgbe/xgbe-common.h   |   99 +++
 drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c  |   76 ++
 drivers/net/ethernet/amd/xgbe/xgbe-drv.c      |   18 -
 drivers/net/ethernet/amd/xgbe/xgbe-main.c     |   24 +
 drivers/net/ethernet/amd/xgbe/xgbe-mdio.c     |   28 +
 drivers/net/ethernet/amd/xgbe/xgbe-pci.c      |  523 +++++++++++++++
 drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c   |  854 +++++++++++++++++++++++++
 drivers/net/ethernet/amd/xgbe/xgbe-platform.c |    9 
 drivers/net/ethernet/amd/xgbe/xgbe.h          |   39 +
 11 files changed, 1657 insertions(+), 20 deletions(-)
 create mode 100644 drivers/net/ethernet/amd/xgbe/xgbe-pci.c
 create mode 100644 drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c

diff --git a/drivers/net/ethernet/amd/Kconfig b/drivers/net/ethernet/amd/Kconfig
index 0038709..9255ce4 100644
--- a/drivers/net/ethernet/amd/Kconfig
+++ b/drivers/net/ethernet/amd/Kconfig
@@ -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
diff --git a/drivers/net/ethernet/amd/xgbe/Makefile b/drivers/net/ethernet/amd/xgbe/Makefile
index 217d59e..d1ce1c1 100644
--- a/drivers/net/ethernet/amd/xgbe/Makefile
+++ b/drivers/net/ethernet/amd/xgbe/Makefile
@@ -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
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-common.h b/drivers/net/ethernet/amd/xgbe/xgbe-common.h
index 8036ee5..b54862b 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-common.h
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-common.h
@@ -858,8 +858,15 @@
 
 /* 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
@@ -902,6 +909,55 @@
 #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 @@
 /* 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))
 
@@ -1329,6 +1395,39 @@
 } 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
  * operations, everything works on mask values.
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c
index 96f485a..951b5ab 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c
@@ -316,6 +316,66 @@ static ssize_t xpcs_reg_value_write(struct file *filp,
 	.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);
 }
 
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
index a43e9303..efa01a5 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c
@@ -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;
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-main.c b/drivers/net/ethernet/amd/xgbe/xgbe-main.c
index c7187fc..697483a 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-main.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-main.c
@@ -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();
 }
 
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
index 723eb90..6d99943 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-mdio.c
@@ -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
index 0000000..fe77945
--- /dev/null
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-pci.c
@@ -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
index 0000000..7ae0abc
--- /dev/null
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-phy-v2.c
@@ -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;
+}
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-platform.c b/drivers/net/ethernet/amd/xgbe/xgbe-platform.c
index 7a701de..8d438c3 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe-platform.c
+++ b/drivers/net/ethernet/amd/xgbe/xgbe-platform.c
@@ -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
diff --git a/drivers/net/ethernet/amd/xgbe/xgbe.h b/drivers/net/ethernet/amd/xgbe/xgbe.h
index 8523779..e22156e 100644
--- a/drivers/net/ethernet/amd/xgbe/xgbe.h
+++ b/drivers/net/ethernet/amd/xgbe/xgbe.h
@@ -200,6 +200,20 @@
 #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 @@ 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);

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ