Browse Source

Merge branch 'amd-xgbe'

Tom Lendacky says:

====================
amd-xgbe: AMD XGBE driver updates 2015-01-16

The following series of patches includes functional updates to the
driver as well as some trivial changes.

- Fix checks/warnings from checkpatch in the amd-xgbe driver
- Fix checks/warnings from checkpatch in the amd-xgbe-phy driver
- Add a check to be sure that the amd-xgbe driver is using the
  amd-xgbe-phy driver
- Use a saved control register value when bringing the PCS out of
  suspend
- Clear all device state during a device restart
- Simplify the Rx descriptor ring tracking
- Remove the need for Tx path spinlocks
- Update the auto-negotiation logic to make use of the auto-negotiation
  interrupt
- Properly support/advertise the FEC capability of the device
- Use the proper page registers during auto-negotiation extended next
  page exchange
- Add ACPI support to the amd-xgbe and amd-xgbe-phy drivers
- Allow platform specific phy settings to be supplied by UEFI

This patch series is based on net-next.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
David S. Miller 10 năm trước cách đây
mục cha
commit
93d62ef97d

+ 21 - 0
Documentation/devicetree/bindings/net/amd-xgbe-phy.txt

@@ -7,17 +7,38 @@ Required properties:
    - SerDes Rx/Tx registers
    - SerDes Rx/Tx registers
    - SerDes integration registers (1/2)
    - SerDes integration registers (1/2)
    - SerDes integration registers (2/2)
    - SerDes integration registers (2/2)
+- interrupt-parent: Should be the phandle for the interrupt controller
+  that services interrupts for this device
+- interrupts: Should contain the amd-xgbe-phy interrupt.
 
 
 Optional properties:
 Optional properties:
 - amd,speed-set: Speed capabilities of the device
 - amd,speed-set: Speed capabilities of the device
     0 - 1GbE and 10GbE (default)
     0 - 1GbE and 10GbE (default)
     1 - 2.5GbE and 10GbE
     1 - 2.5GbE and 10GbE
 
 
+The following optional properties are represented by an array with each
+value corresponding to a particular speed. The first array value represents
+the setting for the 1GbE speed, the second value for the 2.5GbE speed and
+the third value for the 10GbE speed.  All three values are required if the
+property is used.
+- amd,serdes-blwc: Baseline wandering correction enablement
+    0 - Off
+    1 - On
+- amd,serdes-cdr-rate: CDR rate speed selection
+- amd,serdes-pq-skew: PQ (data sampling) skew
+- amd,serdes-tx-amp: TX amplitude boost
+
 Example:
 Example:
 	xgbe_phy@e1240800 {
 	xgbe_phy@e1240800 {
 		compatible = "amd,xgbe-phy-seattle-v1a", "ethernet-phy-ieee802.3-c45";
 		compatible = "amd,xgbe-phy-seattle-v1a", "ethernet-phy-ieee802.3-c45";
 		reg = <0 0xe1240800 0 0x00400>,
 		reg = <0 0xe1240800 0 0x00400>,
 		      <0 0xe1250000 0 0x00060>,
 		      <0 0xe1250000 0 0x00060>,
 		      <0 0xe1250080 0 0x00004>;
 		      <0 0xe1250080 0 0x00004>;
+		interrupt-parent = <&gic>;
+		interrupts = <0 323 4>;
 		amd,speed-set = <0>;
 		amd,speed-set = <0>;
+		amd,serdes-blwc = <1>, <1>, <0>;
+		amd,serdes-cdr-rate = <2>, <2>, <7>;
+		amd,serdes-pq-skew = <10>, <10>, <30>;
+		amd,serdes-tx-amp = <15>, <15>, <10>;
 	};
 	};

+ 1 - 1
drivers/net/ethernet/amd/Kconfig

@@ -179,7 +179,7 @@ config SUNLANCE
 
 
 config AMD_XGBE
 config AMD_XGBE
 	tristate "AMD 10GbE Ethernet driver"
 	tristate "AMD 10GbE Ethernet driver"
-	depends on OF_NET && HAS_IOMEM
+	depends on (OF_NET || ACPI) && HAS_IOMEM
 	select PHYLIB
 	select PHYLIB
 	select AMD_XGBE_PHY
 	select AMD_XGBE_PHY
 	select BITREVERSE
 	select BITREVERSE

+ 1 - 1
drivers/net/ethernet/amd/xgbe/xgbe-debugfs.c

@@ -328,7 +328,7 @@ void xgbe_debugfs_init(struct xgbe_prv_data *pdata)
 
 
 	buf = kasprintf(GFP_KERNEL, "amd-xgbe-%s", pdata->netdev->name);
 	buf = kasprintf(GFP_KERNEL, "amd-xgbe-%s", pdata->netdev->name);
 	pdata->xgbe_debugfs = debugfs_create_dir(buf, NULL);
 	pdata->xgbe_debugfs = debugfs_create_dir(buf, NULL);
-	if (pdata->xgbe_debugfs == NULL) {
+	if (!pdata->xgbe_debugfs) {
 		netdev_err(pdata->netdev, "debugfs_create_dir failed\n");
 		netdev_err(pdata->netdev, "debugfs_create_dir failed\n");
 		return;
 		return;
 	}
 	}

+ 1 - 31
drivers/net/ethernet/amd/xgbe/xgbe-desc.c

@@ -422,7 +422,6 @@ static void xgbe_wrapper_rx_descriptor_init(struct xgbe_prv_data *pdata)
 
 
 		ring->cur = 0;
 		ring->cur = 0;
 		ring->dirty = 0;
 		ring->dirty = 0;
-		memset(&ring->rx, 0, sizeof(ring->rx));
 
 
 		hw_if->rx_desc_init(channel);
 		hw_if->rx_desc_init(channel);
 	}
 	}
@@ -621,35 +620,6 @@ err_out:
 	return 0;
 	return 0;
 }
 }
 
 
-static void xgbe_realloc_rx_buffer(struct xgbe_channel *channel)
-{
-	struct xgbe_prv_data *pdata = channel->pdata;
-	struct xgbe_hw_if *hw_if = &pdata->hw_if;
-	struct xgbe_ring *ring = channel->rx_ring;
-	struct xgbe_ring_data *rdata;
-	int i;
-
-	DBGPR("-->xgbe_realloc_rx_buffer: rx_ring->rx.realloc_index = %u\n",
-	      ring->rx.realloc_index);
-
-	for (i = 0; i < ring->dirty; i++) {
-		rdata = XGBE_GET_DESC_DATA(ring, ring->rx.realloc_index);
-
-		/* Reset rdata values */
-		xgbe_unmap_rdata(pdata, rdata);
-
-		if (xgbe_map_rx_buffer(pdata, ring, rdata))
-			break;
-
-		hw_if->rx_desc_reset(rdata);
-
-		ring->rx.realloc_index++;
-	}
-	ring->dirty = 0;
-
-	DBGPR("<--xgbe_realloc_rx_buffer\n");
-}
-
 void xgbe_init_function_ptrs_desc(struct xgbe_desc_if *desc_if)
 void xgbe_init_function_ptrs_desc(struct xgbe_desc_if *desc_if)
 {
 {
 	DBGPR("-->xgbe_init_function_ptrs_desc\n");
 	DBGPR("-->xgbe_init_function_ptrs_desc\n");
@@ -657,7 +627,7 @@ void xgbe_init_function_ptrs_desc(struct xgbe_desc_if *desc_if)
 	desc_if->alloc_ring_resources = xgbe_alloc_ring_resources;
 	desc_if->alloc_ring_resources = xgbe_alloc_ring_resources;
 	desc_if->free_ring_resources = xgbe_free_ring_resources;
 	desc_if->free_ring_resources = xgbe_free_ring_resources;
 	desc_if->map_tx_skb = xgbe_map_tx_skb;
 	desc_if->map_tx_skb = xgbe_map_tx_skb;
-	desc_if->realloc_rx_buffer = xgbe_realloc_rx_buffer;
+	desc_if->map_rx_buffer = xgbe_map_rx_buffer;
 	desc_if->unmap_rdata = xgbe_unmap_rdata;
 	desc_if->unmap_rdata = xgbe_unmap_rdata;
 	desc_if->wrapper_tx_desc_init = xgbe_wrapper_tx_descriptor_init;
 	desc_if->wrapper_tx_desc_init = xgbe_wrapper_tx_descriptor_init;
 	desc_if->wrapper_rx_desc_init = xgbe_wrapper_rx_descriptor_init;
 	desc_if->wrapper_rx_desc_init = xgbe_wrapper_rx_descriptor_init;

+ 56 - 10
drivers/net/ethernet/amd/xgbe/xgbe-dev.c

@@ -115,6 +115,7 @@
  */
  */
 
 
 #include <linux/phy.h>
 #include <linux/phy.h>
+#include <linux/mdio.h>
 #include <linux/clk.h>
 #include <linux/clk.h>
 #include <linux/bitrev.h>
 #include <linux/bitrev.h>
 #include <linux/crc32.h>
 #include <linux/crc32.h>
@@ -130,7 +131,7 @@ static unsigned int xgbe_usec_to_riwt(struct xgbe_prv_data *pdata,
 
 
 	DBGPR("-->xgbe_usec_to_riwt\n");
 	DBGPR("-->xgbe_usec_to_riwt\n");
 
 
-	rate = clk_get_rate(pdata->sysclk);
+	rate = pdata->sysclk_rate;
 
 
 	/*
 	/*
 	 * Convert the input usec value to the watchdog timer value. Each
 	 * Convert the input usec value to the watchdog timer value. Each
@@ -153,7 +154,7 @@ static unsigned int xgbe_riwt_to_usec(struct xgbe_prv_data *pdata,
 
 
 	DBGPR("-->xgbe_riwt_to_usec\n");
 	DBGPR("-->xgbe_riwt_to_usec\n");
 
 
-	rate = clk_get_rate(pdata->sysclk);
+	rate = pdata->sysclk_rate;
 
 
 	/*
 	/*
 	 * Convert the input watchdog timer value to the usec value. Each
 	 * Convert the input watchdog timer value to the usec value. Each
@@ -673,6 +674,9 @@ static void xgbe_enable_mac_interrupts(struct xgbe_prv_data *pdata)
 
 
 static int xgbe_set_gmii_speed(struct xgbe_prv_data *pdata)
 static int xgbe_set_gmii_speed(struct xgbe_prv_data *pdata)
 {
 {
+	if (XGMAC_IOREAD_BITS(pdata, MAC_TCR, SS) == 0x3)
+		return 0;
+
 	XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, 0x3);
 	XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, 0x3);
 
 
 	return 0;
 	return 0;
@@ -680,6 +684,9 @@ static int xgbe_set_gmii_speed(struct xgbe_prv_data *pdata)
 
 
 static int xgbe_set_gmii_2500_speed(struct xgbe_prv_data *pdata)
 static int xgbe_set_gmii_2500_speed(struct xgbe_prv_data *pdata)
 {
 {
+	if (XGMAC_IOREAD_BITS(pdata, MAC_TCR, SS) == 0x2)
+		return 0;
+
 	XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, 0x2);
 	XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, 0x2);
 
 
 	return 0;
 	return 0;
@@ -687,6 +694,9 @@ static int xgbe_set_gmii_2500_speed(struct xgbe_prv_data *pdata)
 
 
 static int xgbe_set_xgmii_speed(struct xgbe_prv_data *pdata)
 static int xgbe_set_xgmii_speed(struct xgbe_prv_data *pdata)
 {
 {
+	if (XGMAC_IOREAD_BITS(pdata, MAC_TCR, SS) == 0)
+		return 0;
+
 	XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, 0);
 	XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, 0);
 
 
 	return 0;
 	return 0;
@@ -881,6 +891,23 @@ static void xgbe_write_mmd_regs(struct xgbe_prv_data *pdata, int prtad,
 	else
 	else
 		mmd_address = (pdata->mdio_mmd << 16) | (mmd_reg & 0xffff);
 		mmd_address = (pdata->mdio_mmd << 16) | (mmd_reg & 0xffff);
 
 
+	/* If the PCS is changing modes, match the MAC speed to it */
+	if (((mmd_address >> 16) == MDIO_MMD_PCS) &&
+	    ((mmd_address & 0xffff) == MDIO_CTRL2)) {
+		struct phy_device *phydev = pdata->phydev;
+
+		if (mmd_data & MDIO_PCS_CTRL2_TYPE) {
+			/* KX mode */
+			if (phydev->supported & SUPPORTED_1000baseKX_Full)
+				xgbe_set_gmii_speed(pdata);
+			else
+				xgbe_set_gmii_2500_speed(pdata);
+		} else {
+			/* KR mode */
+			xgbe_set_xgmii_speed(pdata);
+		}
+	}
+
 	/* The PCS registers are accessed using mmio. The underlying APB3
 	/* The PCS registers are accessed using mmio. The underlying APB3
 	 * management interface uses indirect addressing to access the MMD
 	 * management interface uses indirect addressing to access the MMD
 	 * register sets. This requires accessing of the PCS register in two
 	 * register sets. This requires accessing of the PCS register in two
@@ -1359,6 +1386,7 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel)
 	unsigned int tso_context, vlan_context;
 	unsigned int tso_context, vlan_context;
 	unsigned int tx_set_ic;
 	unsigned int tx_set_ic;
 	int start_index = ring->cur;
 	int start_index = ring->cur;
+	int cur_index = ring->cur;
 	int i;
 	int i;
 
 
 	DBGPR("-->xgbe_dev_xmit\n");
 	DBGPR("-->xgbe_dev_xmit\n");
@@ -1401,7 +1429,7 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel)
 	else
 	else
 		tx_set_ic = 0;
 		tx_set_ic = 0;
 
 
-	rdata = XGBE_GET_DESC_DATA(ring, ring->cur);
+	rdata = XGBE_GET_DESC_DATA(ring, cur_index);
 	rdesc = rdata->rdesc;
 	rdesc = rdata->rdesc;
 
 
 	/* Create a context descriptor if this is a TSO packet */
 	/* Create a context descriptor if this is a TSO packet */
@@ -1444,8 +1472,8 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel)
 			ring->tx.cur_vlan_ctag = packet->vlan_ctag;
 			ring->tx.cur_vlan_ctag = packet->vlan_ctag;
 		}
 		}
 
 
-		ring->cur++;
-		rdata = XGBE_GET_DESC_DATA(ring, ring->cur);
+		cur_index++;
+		rdata = XGBE_GET_DESC_DATA(ring, cur_index);
 		rdesc = rdata->rdesc;
 		rdesc = rdata->rdesc;
 	}
 	}
 
 
@@ -1473,7 +1501,7 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel)
 	XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, CTXT, 0);
 	XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, CTXT, 0);
 
 
 	/* Set OWN bit if not the first descriptor */
 	/* Set OWN bit if not the first descriptor */
-	if (ring->cur != start_index)
+	if (cur_index != start_index)
 		XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, OWN, 1);
 		XGMAC_SET_BITS_LE(rdesc->desc3, TX_NORMAL_DESC3, OWN, 1);
 
 
 	if (tso) {
 	if (tso) {
@@ -1497,9 +1525,9 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel)
 				  packet->length);
 				  packet->length);
 	}
 	}
 
 
-	for (i = ring->cur - start_index + 1; i < packet->rdesc_count; i++) {
-		ring->cur++;
-		rdata = XGBE_GET_DESC_DATA(ring, ring->cur);
+	for (i = cur_index - start_index + 1; i < packet->rdesc_count; i++) {
+		cur_index++;
+		rdata = XGBE_GET_DESC_DATA(ring, cur_index);
 		rdesc = rdata->rdesc;
 		rdesc = rdata->rdesc;
 
 
 		/* Update buffer address */
 		/* Update buffer address */
@@ -1551,7 +1579,7 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel)
 	/* Make sure ownership is written to the descriptor */
 	/* Make sure ownership is written to the descriptor */
 	wmb();
 	wmb();
 
 
-	ring->cur++;
+	ring->cur = cur_index + 1;
 	if (!packet->skb->xmit_more ||
 	if (!packet->skb->xmit_more ||
 	    netif_xmit_stopped(netdev_get_tx_queue(pdata->netdev,
 	    netif_xmit_stopped(netdev_get_tx_queue(pdata->netdev,
 						   channel->queue_index)))
 						   channel->queue_index)))
@@ -2107,6 +2135,23 @@ static void xgbe_config_jumbo_enable(struct xgbe_prv_data *pdata)
 	XGMAC_IOWRITE_BITS(pdata, MAC_RCR, JE, val);
 	XGMAC_IOWRITE_BITS(pdata, MAC_RCR, JE, val);
 }
 }
 
 
+static void xgbe_config_mac_speed(struct xgbe_prv_data *pdata)
+{
+	switch (pdata->phy_speed) {
+	case SPEED_10000:
+		xgbe_set_xgmii_speed(pdata);
+		break;
+
+	case SPEED_2500:
+		xgbe_set_gmii_2500_speed(pdata);
+		break;
+
+	case SPEED_1000:
+		xgbe_set_gmii_speed(pdata);
+		break;
+	}
+}
+
 static void xgbe_config_checksum_offload(struct xgbe_prv_data *pdata)
 static void xgbe_config_checksum_offload(struct xgbe_prv_data *pdata)
 {
 {
 	if (pdata->netdev->features & NETIF_F_RXCSUM)
 	if (pdata->netdev->features & NETIF_F_RXCSUM)
@@ -2757,6 +2802,7 @@ static int xgbe_init(struct xgbe_prv_data *pdata)
 	xgbe_config_mac_address(pdata);
 	xgbe_config_mac_address(pdata);
 	xgbe_config_jumbo_enable(pdata);
 	xgbe_config_jumbo_enable(pdata);
 	xgbe_config_flow_control(pdata);
 	xgbe_config_flow_control(pdata);
+	xgbe_config_mac_speed(pdata);
 	xgbe_config_checksum_offload(pdata);
 	xgbe_config_checksum_offload(pdata);
 	xgbe_config_vlan_support(pdata);
 	xgbe_config_vlan_support(pdata);
 	xgbe_config_mmc(pdata);
 	xgbe_config_mmc(pdata);

+ 28 - 31
drivers/net/ethernet/amd/xgbe/xgbe-drv.c

@@ -225,6 +225,11 @@ static inline unsigned int xgbe_tx_avail_desc(struct xgbe_ring *ring)
 	return (ring->rdesc_count - (ring->cur - ring->dirty));
 	return (ring->rdesc_count - (ring->cur - ring->dirty));
 }
 }
 
 
+static inline unsigned int xgbe_rx_dirty_desc(struct xgbe_ring *ring)
+{
+	return (ring->cur - ring->dirty);
+}
+
 static int xgbe_maybe_stop_tx_queue(struct xgbe_channel *channel,
 static int xgbe_maybe_stop_tx_queue(struct xgbe_channel *channel,
 				    struct xgbe_ring *ring, unsigned int count)
 				    struct xgbe_ring *ring, unsigned int count)
 {
 {
@@ -410,17 +415,13 @@ static enum hrtimer_restart xgbe_tx_timer(struct hrtimer *timer)
 	struct xgbe_channel *channel = container_of(timer,
 	struct xgbe_channel *channel = container_of(timer,
 						    struct xgbe_channel,
 						    struct xgbe_channel,
 						    tx_timer);
 						    tx_timer);
-	struct xgbe_ring *ring = channel->tx_ring;
 	struct xgbe_prv_data *pdata = channel->pdata;
 	struct xgbe_prv_data *pdata = channel->pdata;
 	struct napi_struct *napi;
 	struct napi_struct *napi;
-	unsigned long flags;
 
 
 	DBGPR("-->xgbe_tx_timer\n");
 	DBGPR("-->xgbe_tx_timer\n");
 
 
 	napi = (pdata->per_channel_irq) ? &channel->napi : &pdata->napi;
 	napi = (pdata->per_channel_irq) ? &channel->napi : &pdata->napi;
 
 
-	spin_lock_irqsave(&ring->lock, flags);
-
 	if (napi_schedule_prep(napi)) {
 	if (napi_schedule_prep(napi)) {
 		/* Disable Tx and Rx interrupts */
 		/* Disable Tx and Rx interrupts */
 		if (pdata->per_channel_irq)
 		if (pdata->per_channel_irq)
@@ -434,8 +435,6 @@ static enum hrtimer_restart xgbe_tx_timer(struct hrtimer *timer)
 
 
 	channel->tx_timer_active = 0;
 	channel->tx_timer_active = 0;
 
 
-	spin_unlock_irqrestore(&ring->lock, flags);
-
 	DBGPR("<--xgbe_tx_timer\n");
 	DBGPR("<--xgbe_tx_timer\n");
 
 
 	return HRTIMER_NORESTART;
 	return HRTIMER_NORESTART;
@@ -692,7 +691,7 @@ static void xgbe_adjust_link(struct net_device *netdev)
 	struct phy_device *phydev = pdata->phydev;
 	struct phy_device *phydev = pdata->phydev;
 	int new_state = 0;
 	int new_state = 0;
 
 
-	if (phydev == NULL)
+	if (!phydev)
 		return;
 		return;
 
 
 	if (phydev->link) {
 	if (phydev->link) {
@@ -927,7 +926,7 @@ static void xgbe_stop(struct xgbe_prv_data *pdata)
 	DBGPR("<--xgbe_stop\n");
 	DBGPR("<--xgbe_stop\n");
 }
 }
 
 
-static void xgbe_restart_dev(struct xgbe_prv_data *pdata, unsigned int reset)
+static void xgbe_restart_dev(struct xgbe_prv_data *pdata)
 {
 {
 	struct xgbe_channel *channel;
 	struct xgbe_channel *channel;
 	struct xgbe_hw_if *hw_if = &pdata->hw_if;
 	struct xgbe_hw_if *hw_if = &pdata->hw_if;
@@ -950,9 +949,8 @@ static void xgbe_restart_dev(struct xgbe_prv_data *pdata, unsigned int reset)
 	xgbe_free_tx_data(pdata);
 	xgbe_free_tx_data(pdata);
 	xgbe_free_rx_data(pdata);
 	xgbe_free_rx_data(pdata);
 
 
-	/* Issue software reset to device if requested */
-	if (reset)
-		hw_if->exit(pdata);
+	/* Issue software reset to device */
+	hw_if->exit(pdata);
 
 
 	xgbe_start(pdata);
 	xgbe_start(pdata);
 
 
@@ -967,7 +965,7 @@ static void xgbe_restart(struct work_struct *work)
 
 
 	rtnl_lock();
 	rtnl_lock();
 
 
-	xgbe_restart_dev(pdata, 1);
+	xgbe_restart_dev(pdata);
 
 
 	rtnl_unlock();
 	rtnl_unlock();
 }
 }
@@ -1446,7 +1444,6 @@ static int xgbe_xmit(struct sk_buff *skb, struct net_device *netdev)
 	struct xgbe_ring *ring;
 	struct xgbe_ring *ring;
 	struct xgbe_packet_data *packet;
 	struct xgbe_packet_data *packet;
 	struct netdev_queue *txq;
 	struct netdev_queue *txq;
-	unsigned long flags;
 	int ret;
 	int ret;
 
 
 	DBGPR("-->xgbe_xmit: skb->len = %d\n", skb->len);
 	DBGPR("-->xgbe_xmit: skb->len = %d\n", skb->len);
@@ -1458,8 +1455,6 @@ static int xgbe_xmit(struct sk_buff *skb, struct net_device *netdev)
 
 
 	ret = NETDEV_TX_OK;
 	ret = NETDEV_TX_OK;
 
 
-	spin_lock_irqsave(&ring->lock, flags);
-
 	if (skb->len == 0) {
 	if (skb->len == 0) {
 		netdev_err(netdev, "empty skb received from stack\n");
 		netdev_err(netdev, "empty skb received from stack\n");
 		dev_kfree_skb_any(skb);
 		dev_kfree_skb_any(skb);
@@ -1506,10 +1501,6 @@ static int xgbe_xmit(struct sk_buff *skb, struct net_device *netdev)
 	ret = NETDEV_TX_OK;
 	ret = NETDEV_TX_OK;
 
 
 tx_netdev_return:
 tx_netdev_return:
-	spin_unlock_irqrestore(&ring->lock, flags);
-
-	DBGPR("<--xgbe_xmit\n");
-
 	return ret;
 	return ret;
 }
 }
 
 
@@ -1587,7 +1578,7 @@ static int xgbe_change_mtu(struct net_device *netdev, int mtu)
 	pdata->rx_buf_size = ret;
 	pdata->rx_buf_size = ret;
 	netdev->mtu = mtu;
 	netdev->mtu = mtu;
 
 
-	xgbe_restart_dev(pdata, 0);
+	xgbe_restart_dev(pdata);
 
 
 	DBGPR("<--xgbe_change_mtu\n");
 	DBGPR("<--xgbe_change_mtu\n");
 
 
@@ -1776,15 +1767,28 @@ struct net_device_ops *xgbe_get_netdev_ops(void)
 static void xgbe_rx_refresh(struct xgbe_channel *channel)
 static void xgbe_rx_refresh(struct xgbe_channel *channel)
 {
 {
 	struct xgbe_prv_data *pdata = channel->pdata;
 	struct xgbe_prv_data *pdata = channel->pdata;
+	struct xgbe_hw_if *hw_if = &pdata->hw_if;
 	struct xgbe_desc_if *desc_if = &pdata->desc_if;
 	struct xgbe_desc_if *desc_if = &pdata->desc_if;
 	struct xgbe_ring *ring = channel->rx_ring;
 	struct xgbe_ring *ring = channel->rx_ring;
 	struct xgbe_ring_data *rdata;
 	struct xgbe_ring_data *rdata;
 
 
-	desc_if->realloc_rx_buffer(channel);
+	while (ring->dirty != ring->cur) {
+		rdata = XGBE_GET_DESC_DATA(ring, ring->dirty);
+
+		/* Reset rdata values */
+		desc_if->unmap_rdata(pdata, rdata);
+
+		if (desc_if->map_rx_buffer(pdata, ring, rdata))
+			break;
+
+		hw_if->rx_desc_reset(rdata);
+
+		ring->dirty++;
+	}
 
 
 	/* Update the Rx Tail Pointer Register with address of
 	/* Update the Rx Tail Pointer Register with address of
 	 * the last cleaned entry */
 	 * the last cleaned entry */
-	rdata = XGBE_GET_DESC_DATA(ring, ring->rx.realloc_index - 1);
+	rdata = XGBE_GET_DESC_DATA(ring, ring->dirty - 1);
 	XGMAC_DMA_IOWRITE(channel, DMA_CH_RDTR_LO,
 	XGMAC_DMA_IOWRITE(channel, DMA_CH_RDTR_LO,
 			  lower_32_bits(rdata->rdesc_dma));
 			  lower_32_bits(rdata->rdesc_dma));
 }
 }
@@ -1824,7 +1828,6 @@ static int xgbe_tx_poll(struct xgbe_channel *channel)
 	struct xgbe_ring_desc *rdesc;
 	struct xgbe_ring_desc *rdesc;
 	struct net_device *netdev = pdata->netdev;
 	struct net_device *netdev = pdata->netdev;
 	struct netdev_queue *txq;
 	struct netdev_queue *txq;
-	unsigned long flags;
 	int processed = 0;
 	int processed = 0;
 	unsigned int tx_packets = 0, tx_bytes = 0;
 	unsigned int tx_packets = 0, tx_bytes = 0;
 
 
@@ -1836,8 +1839,6 @@ static int xgbe_tx_poll(struct xgbe_channel *channel)
 
 
 	txq = netdev_get_tx_queue(netdev, channel->queue_index);
 	txq = netdev_get_tx_queue(netdev, channel->queue_index);
 
 
-	spin_lock_irqsave(&ring->lock, flags);
-
 	while ((processed < XGBE_TX_DESC_MAX_PROC) &&
 	while ((processed < XGBE_TX_DESC_MAX_PROC) &&
 	       (ring->dirty != ring->cur)) {
 	       (ring->dirty != ring->cur)) {
 		rdata = XGBE_GET_DESC_DATA(ring, ring->dirty);
 		rdata = XGBE_GET_DESC_DATA(ring, ring->dirty);
@@ -1868,7 +1869,7 @@ static int xgbe_tx_poll(struct xgbe_channel *channel)
 	}
 	}
 
 
 	if (!processed)
 	if (!processed)
-		goto unlock;
+		return 0;
 
 
 	netdev_tx_completed_queue(txq, tx_packets, tx_bytes);
 	netdev_tx_completed_queue(txq, tx_packets, tx_bytes);
 
 
@@ -1880,9 +1881,6 @@ static int xgbe_tx_poll(struct xgbe_channel *channel)
 
 
 	DBGPR("<--xgbe_tx_poll: processed=%d\n", processed);
 	DBGPR("<--xgbe_tx_poll: processed=%d\n", processed);
 
 
-unlock:
-	spin_unlock_irqrestore(&ring->lock, flags);
-
 	return processed;
 	return processed;
 }
 }
 
 
@@ -1934,7 +1932,7 @@ static int xgbe_rx_poll(struct xgbe_channel *channel, int budget)
 read_again:
 read_again:
 		rdata = XGBE_GET_DESC_DATA(ring, ring->cur);
 		rdata = XGBE_GET_DESC_DATA(ring, ring->cur);
 
 
-		if (ring->dirty > (XGBE_RX_DESC_CNT >> 3))
+		if (xgbe_rx_dirty_desc(ring) > (XGBE_RX_DESC_CNT >> 3))
 			xgbe_rx_refresh(channel);
 			xgbe_rx_refresh(channel);
 
 
 		if (hw_if->dev_read(channel))
 		if (hw_if->dev_read(channel))
@@ -1942,7 +1940,6 @@ read_again:
 
 
 		received++;
 		received++;
 		ring->cur++;
 		ring->cur++;
-		ring->dirty++;
 
 
 		incomplete = XGMAC_GET_BITS(packet->attributes,
 		incomplete = XGMAC_GET_BITS(packet->attributes,
 					    RX_PACKET_ATTRIBUTES,
 					    RX_PACKET_ATTRIBUTES,

+ 159 - 44
drivers/net/ethernet/amd/xgbe/xgbe-main.c

@@ -123,7 +123,10 @@
 #include <linux/io.h>
 #include <linux/io.h>
 #include <linux/of.h>
 #include <linux/of.h>
 #include <linux/of_net.h>
 #include <linux/of_net.h>
+#include <linux/of_address.h>
 #include <linux/clk.h>
 #include <linux/clk.h>
+#include <linux/property.h>
+#include <linux/acpi.h>
 
 
 #include "xgbe.h"
 #include "xgbe.h"
 #include "xgbe-common.h"
 #include "xgbe-common.h"
@@ -148,6 +151,7 @@ static void xgbe_default_config(struct xgbe_prv_data *pdata)
 	pdata->pause_autoneg = 1;
 	pdata->pause_autoneg = 1;
 	pdata->tx_pause = 1;
 	pdata->tx_pause = 1;
 	pdata->rx_pause = 1;
 	pdata->rx_pause = 1;
+	pdata->phy_speed = SPEED_UNKNOWN;
 	pdata->power_down = 0;
 	pdata->power_down = 0;
 	pdata->default_autoneg = AUTONEG_ENABLE;
 	pdata->default_autoneg = AUTONEG_ENABLE;
 	pdata->default_speed = SPEED_10000;
 	pdata->default_speed = SPEED_10000;
@@ -161,6 +165,96 @@ static void xgbe_init_all_fptrs(struct xgbe_prv_data *pdata)
 	xgbe_init_function_ptrs_desc(&pdata->desc_if);
 	xgbe_init_function_ptrs_desc(&pdata->desc_if);
 }
 }
 
 
+#ifdef CONFIG_ACPI
+static int xgbe_acpi_support(struct xgbe_prv_data *pdata)
+{
+	struct acpi_device *adev = pdata->adev;
+	struct device *dev = pdata->dev;
+	u32 property;
+	acpi_handle handle;
+	acpi_status status;
+	unsigned long long data;
+	int cca;
+	int ret;
+
+	/* Obtain the system clock setting */
+	ret = device_property_read_u32(dev, XGBE_ACPI_DMA_FREQ, &property);
+	if (ret) {
+		dev_err(dev, "unable to obtain %s property\n",
+			XGBE_ACPI_DMA_FREQ);
+		return ret;
+	}
+	pdata->sysclk_rate = property;
+
+	/* Obtain the PTP clock setting */
+	ret = device_property_read_u32(dev, XGBE_ACPI_PTP_FREQ, &property);
+	if (ret) {
+		dev_err(dev, "unable to obtain %s property\n",
+			XGBE_ACPI_PTP_FREQ);
+		return ret;
+	}
+	pdata->ptpclk_rate = property;
+
+	/* Retrieve the device cache coherency value */
+	handle = adev->handle;
+	do {
+		status = acpi_evaluate_integer(handle, "_CCA", NULL, &data);
+		if (!ACPI_FAILURE(status)) {
+			cca = data;
+			break;
+		}
+
+		status = acpi_get_parent(handle, &handle);
+	} while (!ACPI_FAILURE(status));
+
+	if (ACPI_FAILURE(status)) {
+		dev_err(dev, "error obtaining acpi coherency value\n");
+		return -EINVAL;
+	}
+	pdata->coherent = !!cca;
+
+	return 0;
+}
+#else   /* CONFIG_ACPI */
+static int xgbe_acpi_support(struct xgbe_prv_data *pdata)
+{
+	return -EINVAL;
+}
+#endif  /* CONFIG_ACPI */
+
+#ifdef CONFIG_OF
+static int xgbe_of_support(struct xgbe_prv_data *pdata)
+{
+	struct device *dev = pdata->dev;
+
+	/* Obtain the system clock setting */
+	pdata->sysclk = devm_clk_get(dev, XGBE_DMA_CLOCK);
+	if (IS_ERR(pdata->sysclk)) {
+		dev_err(dev, "dma devm_clk_get failed\n");
+		return PTR_ERR(pdata->sysclk);
+	}
+	pdata->sysclk_rate = clk_get_rate(pdata->sysclk);
+
+	/* Obtain the PTP clock setting */
+	pdata->ptpclk = devm_clk_get(dev, XGBE_PTP_CLOCK);
+	if (IS_ERR(pdata->ptpclk)) {
+		dev_err(dev, "ptp devm_clk_get failed\n");
+		return PTR_ERR(pdata->ptpclk);
+	}
+	pdata->ptpclk_rate = clk_get_rate(pdata->ptpclk);
+
+	/* Retrieve the device cache coherency value */
+	pdata->coherent = of_dma_is_coherent(dev->of_node);
+
+	return 0;
+}
+#else   /* CONFIG_OF */
+static int xgbe_of_support(struct xgbe_prv_data *pdata)
+{
+	return -EINVAL;
+}
+#endif  /*CONFIG_OF */
+
 static int xgbe_probe(struct platform_device *pdev)
 static int xgbe_probe(struct platform_device *pdev)
 {
 {
 	struct xgbe_prv_data *pdata;
 	struct xgbe_prv_data *pdata;
@@ -169,7 +263,7 @@ static int xgbe_probe(struct platform_device *pdev)
 	struct net_device *netdev;
 	struct net_device *netdev;
 	struct device *dev = &pdev->dev;
 	struct device *dev = &pdev->dev;
 	struct resource *res;
 	struct resource *res;
-	const u8 *mac_addr;
+	const char *phy_mode;
 	unsigned int i;
 	unsigned int i;
 	int ret;
 	int ret;
 
 
@@ -186,6 +280,7 @@ static int xgbe_probe(struct platform_device *pdev)
 	pdata = netdev_priv(netdev);
 	pdata = netdev_priv(netdev);
 	pdata->netdev = netdev;
 	pdata->netdev = netdev;
 	pdata->pdev = pdev;
 	pdata->pdev = pdev;
+	pdata->adev = ACPI_COMPANION(dev);
 	pdata->dev = dev;
 	pdata->dev = dev;
 	platform_set_drvdata(pdev, netdev);
 	platform_set_drvdata(pdev, netdev);
 
 
@@ -194,6 +289,9 @@ static int xgbe_probe(struct platform_device *pdev)
 	mutex_init(&pdata->rss_mutex);
 	mutex_init(&pdata->rss_mutex);
 	spin_lock_init(&pdata->tstamp_lock);
 	spin_lock_init(&pdata->tstamp_lock);
 
 
+	/* Check if we should use ACPI or DT */
+	pdata->use_acpi = (!pdata->adev || acpi_disabled) ? 0 : 1;
+
 	/* Set and validate the number of descriptors for a ring */
 	/* Set and validate the number of descriptors for a ring */
 	BUILD_BUG_ON_NOT_POWER_OF_2(XGBE_TX_DESC_CNT);
 	BUILD_BUG_ON_NOT_POWER_OF_2(XGBE_TX_DESC_CNT);
 	pdata->tx_desc_count = XGBE_TX_DESC_CNT;
 	pdata->tx_desc_count = XGBE_TX_DESC_CNT;
@@ -212,22 +310,6 @@ static int xgbe_probe(struct platform_device *pdev)
 		goto err_io;
 		goto err_io;
 	}
 	}
 
 
-	/* Obtain the system clock setting */
-	pdata->sysclk = devm_clk_get(dev, XGBE_DMA_CLOCK);
-	if (IS_ERR(pdata->sysclk)) {
-		dev_err(dev, "dma devm_clk_get failed\n");
-		ret = PTR_ERR(pdata->sysclk);
-		goto err_io;
-	}
-
-	/* Obtain the PTP clock setting */
-	pdata->ptpclk = devm_clk_get(dev, XGBE_PTP_CLOCK);
-	if (IS_ERR(pdata->ptpclk)) {
-		dev_err(dev, "ptp devm_clk_get failed\n");
-		ret = PTR_ERR(pdata->ptpclk);
-		goto err_io;
-	}
-
 	/* Obtain the mmio areas for the device */
 	/* Obtain the mmio areas for the device */
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	pdata->xgmac_regs = devm_ioremap_resource(dev, res);
 	pdata->xgmac_regs = devm_ioremap_resource(dev, res);
@@ -247,16 +329,42 @@ static int xgbe_probe(struct platform_device *pdev)
 	}
 	}
 	DBGPR("  xpcs_regs  = %p\n", pdata->xpcs_regs);
 	DBGPR("  xpcs_regs  = %p\n", pdata->xpcs_regs);
 
 
-	/* Set the DMA mask */
-	if (!dev->dma_mask)
-		dev->dma_mask = &dev->coherent_dma_mask;
-	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40));
-	if (ret) {
-		dev_err(dev, "dma_set_mask_and_coherent failed\n");
+	/* Retrieve the MAC address */
+	ret = device_property_read_u8_array(dev, XGBE_MAC_ADDR_PROPERTY,
+					    pdata->mac_addr,
+					    sizeof(pdata->mac_addr));
+	if (ret || !is_valid_ether_addr(pdata->mac_addr)) {
+		dev_err(dev, "invalid %s property\n", XGBE_MAC_ADDR_PROPERTY);
+		if (!ret)
+			ret = -EINVAL;
 		goto err_io;
 		goto err_io;
 	}
 	}
 
 
-	if (of_property_read_bool(dev->of_node, "dma-coherent")) {
+	/* Retrieve the PHY mode - it must be "xgmii" */
+	ret = device_property_read_string(dev, XGBE_PHY_MODE_PROPERTY,
+					  &phy_mode);
+	if (ret || strcmp(phy_mode, phy_modes(PHY_INTERFACE_MODE_XGMII))) {
+		dev_err(dev, "invalid %s property\n", XGBE_PHY_MODE_PROPERTY);
+		if (!ret)
+			ret = -EINVAL;
+		goto err_io;
+	}
+	pdata->phy_mode = PHY_INTERFACE_MODE_XGMII;
+
+	/* Check for per channel interrupt support */
+	if (device_property_present(dev, XGBE_DMA_IRQS_PROPERTY))
+		pdata->per_channel_irq = 1;
+
+	/* Obtain device settings unique to ACPI/OF */
+	if (pdata->use_acpi)
+		ret = xgbe_acpi_support(pdata);
+	else
+		ret = xgbe_of_support(pdata);
+	if (ret)
+		goto err_io;
+
+	/* Set the DMA coherency values */
+	if (pdata->coherent) {
 		pdata->axdomain = XGBE_DMA_OS_AXDOMAIN;
 		pdata->axdomain = XGBE_DMA_OS_AXDOMAIN;
 		pdata->arcache = XGBE_DMA_OS_ARCACHE;
 		pdata->arcache = XGBE_DMA_OS_ARCACHE;
 		pdata->awcache = XGBE_DMA_OS_AWCACHE;
 		pdata->awcache = XGBE_DMA_OS_AWCACHE;
@@ -266,10 +374,16 @@ static int xgbe_probe(struct platform_device *pdev)
 		pdata->awcache = XGBE_DMA_SYS_AWCACHE;
 		pdata->awcache = XGBE_DMA_SYS_AWCACHE;
 	}
 	}
 
 
-	/* Check for per channel interrupt support */
-	if (of_property_read_bool(dev->of_node, XGBE_DMA_IRQS))
-		pdata->per_channel_irq = 1;
+	/* Set the DMA mask */
+	if (!dev->dma_mask)
+		dev->dma_mask = &dev->coherent_dma_mask;
+	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(40));
+	if (ret) {
+		dev_err(dev, "dma_set_mask_and_coherent failed\n");
+		goto err_io;
+	}
 
 
+	/* Get the device interrupt */
 	ret = platform_get_irq(pdev, 0);
 	ret = platform_get_irq(pdev, 0);
 	if (ret < 0) {
 	if (ret < 0) {
 		dev_err(dev, "platform_get_irq 0 failed\n");
 		dev_err(dev, "platform_get_irq 0 failed\n");
@@ -279,6 +393,7 @@ static int xgbe_probe(struct platform_device *pdev)
 
 
 	netdev->irq = pdata->dev_irq;
 	netdev->irq = pdata->dev_irq;
 	netdev->base_addr = (unsigned long)pdata->xgmac_regs;
 	netdev->base_addr = (unsigned long)pdata->xgmac_regs;
+	memcpy(netdev->dev_addr, pdata->mac_addr, netdev->addr_len);
 
 
 	/* Set all the function pointers */
 	/* Set all the function pointers */
 	xgbe_init_all_fptrs(pdata);
 	xgbe_init_all_fptrs(pdata);
@@ -291,23 +406,6 @@ static int xgbe_probe(struct platform_device *pdev)
 	/* Populate the hardware features */
 	/* Populate the hardware features */
 	xgbe_get_all_hw_features(pdata);
 	xgbe_get_all_hw_features(pdata);
 
 
-	/* Retrieve the MAC address */
-	mac_addr = of_get_mac_address(dev->of_node);
-	if (!mac_addr) {
-		dev_err(dev, "invalid mac address for this device\n");
-		ret = -EINVAL;
-		goto err_io;
-	}
-	memcpy(netdev->dev_addr, mac_addr, netdev->addr_len);
-
-	/* Retrieve the PHY mode - it must be "xgmii" */
-	pdata->phy_mode = of_get_phy_mode(dev->of_node);
-	if (pdata->phy_mode != PHY_INTERFACE_MODE_XGMII) {
-		dev_err(dev, "invalid phy-mode specified for this device\n");
-		ret = -EINVAL;
-		goto err_io;
-	}
-
 	/* Set default configuration data */
 	/* Set default configuration data */
 	xgbe_default_config(pdata);
 	xgbe_default_config(pdata);
 
 
@@ -491,18 +589,35 @@ static int xgbe_resume(struct device *dev)
 }
 }
 #endif /* CONFIG_PM */
 #endif /* CONFIG_PM */
 
 
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id xgbe_acpi_match[] = {
+	{ "AMDI8001", 0 },
+	{},
+};
+
+MODULE_DEVICE_TABLE(acpi, xgbe_acpi_match);
+#endif
+
+#ifdef CONFIG_OF
 static const struct of_device_id xgbe_of_match[] = {
 static const struct of_device_id xgbe_of_match[] = {
 	{ .compatible = "amd,xgbe-seattle-v1a", },
 	{ .compatible = "amd,xgbe-seattle-v1a", },
 	{},
 	{},
 };
 };
 
 
 MODULE_DEVICE_TABLE(of, xgbe_of_match);
 MODULE_DEVICE_TABLE(of, xgbe_of_match);
+#endif
+
 static SIMPLE_DEV_PM_OPS(xgbe_pm_ops, xgbe_suspend, xgbe_resume);
 static SIMPLE_DEV_PM_OPS(xgbe_pm_ops, xgbe_suspend, xgbe_resume);
 
 
 static struct platform_driver xgbe_driver = {
 static struct platform_driver xgbe_driver = {
 	.driver = {
 	.driver = {
 		.name = "amd-xgbe",
 		.name = "amd-xgbe",
+#ifdef CONFIG_ACPI
+		.acpi_match_table = xgbe_acpi_match,
+#endif
+#ifdef CONFIG_OF
 		.of_match_table = xgbe_of_match,
 		.of_match_table = xgbe_of_match,
+#endif
 		.pm = &xgbe_pm_ops,
 		.pm = &xgbe_pm_ops,
 	},
 	},
 	.probe = xgbe_probe,
 	.probe = xgbe_probe,

+ 8 - 21
drivers/net/ethernet/amd/xgbe/xgbe-mdio.c

@@ -205,25 +205,16 @@ void xgbe_dump_phy_registers(struct xgbe_prv_data *pdata)
 
 
 int xgbe_mdio_register(struct xgbe_prv_data *pdata)
 int xgbe_mdio_register(struct xgbe_prv_data *pdata)
 {
 {
-	struct device_node *phy_node;
 	struct mii_bus *mii;
 	struct mii_bus *mii;
 	struct phy_device *phydev;
 	struct phy_device *phydev;
 	int ret = 0;
 	int ret = 0;
 
 
 	DBGPR("-->xgbe_mdio_register\n");
 	DBGPR("-->xgbe_mdio_register\n");
 
 
-	/* Retrieve the phy-handle */
-	phy_node = of_parse_phandle(pdata->dev->of_node, "phy-handle", 0);
-	if (!phy_node) {
-		dev_err(pdata->dev, "unable to parse phy-handle\n");
-		return -EINVAL;
-	}
-
 	mii = mdiobus_alloc();
 	mii = mdiobus_alloc();
-	if (mii == NULL) {
+	if (!mii) {
 		dev_err(pdata->dev, "mdiobus_alloc failed\n");
 		dev_err(pdata->dev, "mdiobus_alloc failed\n");
-		ret = -ENOMEM;
-		goto err_node_get;
+		return -ENOMEM;
 	}
 	}
 
 
 	/* Register on the MDIO bus (don't probe any PHYs) */
 	/* Register on the MDIO bus (don't probe any PHYs) */
@@ -252,18 +243,19 @@ int xgbe_mdio_register(struct xgbe_prv_data *pdata)
 	request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT,
 	request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT,
 		       MDIO_ID_ARGS(phydev->c45_ids.device_ids[MDIO_MMD_PCS]));
 		       MDIO_ID_ARGS(phydev->c45_ids.device_ids[MDIO_MMD_PCS]));
 
 
-	of_node_get(phy_node);
-	phydev->dev.of_node = phy_node;
 	ret = phy_device_register(phydev);
 	ret = phy_device_register(phydev);
 	if (ret) {
 	if (ret) {
 		dev_err(pdata->dev, "phy_device_register failed\n");
 		dev_err(pdata->dev, "phy_device_register failed\n");
-		of_node_put(phy_node);
+		goto err_phy_device;
+	}
+	if (!phydev->dev.driver) {
+		dev_err(pdata->dev, "phy driver probe failed\n");
+		ret = -EIO;
 		goto err_phy_device;
 		goto err_phy_device;
 	}
 	}
 
 
 	/* Add a reference to the PHY driver so it can't be unloaded */
 	/* Add a reference to the PHY driver so it can't be unloaded */
-	pdata->phy_module = phydev->dev.driver ?
-			    phydev->dev.driver->owner : NULL;
+	pdata->phy_module = phydev->dev.driver->owner;
 	if (!try_module_get(pdata->phy_module)) {
 	if (!try_module_get(pdata->phy_module)) {
 		dev_err(pdata->dev, "try_module_get failed\n");
 		dev_err(pdata->dev, "try_module_get failed\n");
 		ret = -EIO;
 		ret = -EIO;
@@ -283,8 +275,6 @@ int xgbe_mdio_register(struct xgbe_prv_data *pdata)
 
 
 	pdata->phydev = phydev;
 	pdata->phydev = phydev;
 
 
-	of_node_put(phy_node);
-
 	DBGPHY_REGS(pdata);
 	DBGPHY_REGS(pdata);
 
 
 	DBGPR("<--xgbe_mdio_register\n");
 	DBGPR("<--xgbe_mdio_register\n");
@@ -300,9 +290,6 @@ err_mdiobus_register:
 err_mdiobus_alloc:
 err_mdiobus_alloc:
 	mdiobus_free(mii);
 	mdiobus_free(mii);
 
 
-err_node_get:
-	of_node_put(phy_node);
-
 	return ret;
 	return ret;
 }
 }
 
 

+ 2 - 2
drivers/net/ethernet/amd/xgbe/xgbe-ptp.c

@@ -233,7 +233,7 @@ void xgbe_ptp_register(struct xgbe_prv_data *pdata)
 	snprintf(info->name, sizeof(info->name), "%s",
 	snprintf(info->name, sizeof(info->name), "%s",
 		 netdev_name(pdata->netdev));
 		 netdev_name(pdata->netdev));
 	info->owner = THIS_MODULE;
 	info->owner = THIS_MODULE;
-	info->max_adj = clk_get_rate(pdata->ptpclk);
+	info->max_adj = pdata->ptpclk_rate;
 	info->adjfreq = xgbe_adjfreq;
 	info->adjfreq = xgbe_adjfreq;
 	info->adjtime = xgbe_adjtime;
 	info->adjtime = xgbe_adjtime;
 	info->gettime = xgbe_gettime;
 	info->gettime = xgbe_gettime;
@@ -254,7 +254,7 @@ void xgbe_ptp_register(struct xgbe_prv_data *pdata)
 	 */
 	 */
 	dividend = 50000000;
 	dividend = 50000000;
 	dividend <<= 32;
 	dividend <<= 32;
-	pdata->tstamp_addend = div_u64(dividend, clk_get_rate(pdata->ptpclk));
+	pdata->tstamp_addend = div_u64(dividend, pdata->ptpclk_rate);
 
 
 	/* Setup the timecounter */
 	/* Setup the timecounter */
 	cc->read = xgbe_cc_read;
 	cc->read = xgbe_cc_read;

+ 20 - 9
drivers/net/ethernet/amd/xgbe/xgbe.h

@@ -182,10 +182,18 @@
 #define XGBE_PHY_NAME		"amd_xgbe_phy"
 #define XGBE_PHY_NAME		"amd_xgbe_phy"
 #define XGBE_PRTAD		0
 #define XGBE_PRTAD		0
 
 
+/* Common property names */
+#define XGBE_MAC_ADDR_PROPERTY	"mac-address"
+#define XGBE_PHY_MODE_PROPERTY	"phy-mode"
+#define XGBE_DMA_IRQS_PROPERTY	"amd,per-channel-interrupt"
+
 /* Device-tree clock names */
 /* Device-tree clock names */
 #define XGBE_DMA_CLOCK		"dma_clk"
 #define XGBE_DMA_CLOCK		"dma_clk"
 #define XGBE_PTP_CLOCK		"ptp_clk"
 #define XGBE_PTP_CLOCK		"ptp_clk"
-#define XGBE_DMA_IRQS		"amd,per-channel-interrupt"
+
+/* ACPI property names */
+#define XGBE_ACPI_DMA_FREQ	"amd,dma-freq"
+#define XGBE_ACPI_PTP_FREQ	"amd,ptp-freq"
 
 
 /* Timestamp support - values based on 50MHz PTP clock
 /* Timestamp support - values based on 50MHz PTP clock
  *   50MHz => 20 nsec
  *   50MHz => 20 nsec
@@ -361,8 +369,7 @@ struct xgbe_ring {
 	 *  cur   - Tx: index of descriptor to be used for current transfer
 	 *  cur   - Tx: index of descriptor to be used for current transfer
 	 *          Rx: index of descriptor to check for packet availability
 	 *          Rx: index of descriptor to check for packet availability
 	 *  dirty - Tx: index of descriptor to check for transfer complete
 	 *  dirty - Tx: index of descriptor to check for transfer complete
-	 *          Rx: count of descriptors in which a packet has been received
-	 *              (used with skb_realloc_index to refresh the ring)
+	 *          Rx: index of descriptor to check for buffer reallocation
 	 */
 	 */
 	unsigned int cur;
 	unsigned int cur;
 	unsigned int dirty;
 	unsigned int dirty;
@@ -377,11 +384,6 @@ struct xgbe_ring {
 			unsigned short cur_mss;
 			unsigned short cur_mss;
 			unsigned short cur_vlan_ctag;
 			unsigned short cur_vlan_ctag;
 		} tx;
 		} tx;
-
-		struct {
-			unsigned int realloc_index;
-			unsigned int realloc_threshold;
-		} rx;
 	};
 	};
 } ____cacheline_aligned;
 } ____cacheline_aligned;
 
 
@@ -596,7 +598,8 @@ struct xgbe_desc_if {
 	int (*alloc_ring_resources)(struct xgbe_prv_data *);
 	int (*alloc_ring_resources)(struct xgbe_prv_data *);
 	void (*free_ring_resources)(struct xgbe_prv_data *);
 	void (*free_ring_resources)(struct xgbe_prv_data *);
 	int (*map_tx_skb)(struct xgbe_channel *, struct sk_buff *);
 	int (*map_tx_skb)(struct xgbe_channel *, struct sk_buff *);
-	void (*realloc_rx_buffer)(struct xgbe_channel *);
+	int (*map_rx_buffer)(struct xgbe_prv_data *, struct xgbe_ring *,
+			     struct xgbe_ring_data *);
 	void (*unmap_rdata)(struct xgbe_prv_data *, struct xgbe_ring_data *);
 	void (*unmap_rdata)(struct xgbe_prv_data *, struct xgbe_ring_data *);
 	void (*wrapper_tx_desc_init)(struct xgbe_prv_data *);
 	void (*wrapper_tx_desc_init)(struct xgbe_prv_data *);
 	void (*wrapper_rx_desc_init)(struct xgbe_prv_data *);
 	void (*wrapper_rx_desc_init)(struct xgbe_prv_data *);
@@ -650,8 +653,12 @@ struct xgbe_hw_features {
 struct xgbe_prv_data {
 struct xgbe_prv_data {
 	struct net_device *netdev;
 	struct net_device *netdev;
 	struct platform_device *pdev;
 	struct platform_device *pdev;
+	struct acpi_device *adev;
 	struct device *dev;
 	struct device *dev;
 
 
+	/* ACPI or DT flag */
+	unsigned int use_acpi;
+
 	/* XGMAC/XPCS related mmio registers */
 	/* XGMAC/XPCS related mmio registers */
 	void __iomem *xgmac_regs;	/* XGMAC CSRs */
 	void __iomem *xgmac_regs;	/* XGMAC CSRs */
 	void __iomem *xpcs_regs;	/* XPCS MMD registers */
 	void __iomem *xpcs_regs;	/* XPCS MMD registers */
@@ -672,6 +679,7 @@ struct xgbe_prv_data {
 	struct xgbe_desc_if desc_if;
 	struct xgbe_desc_if desc_if;
 
 
 	/* AXI DMA settings */
 	/* AXI DMA settings */
+	unsigned int coherent;
 	unsigned int axdomain;
 	unsigned int axdomain;
 	unsigned int arcache;
 	unsigned int arcache;
 	unsigned int awcache;
 	unsigned int awcache;
@@ -739,6 +747,7 @@ struct xgbe_prv_data {
 	unsigned int phy_rx_pause;
 	unsigned int phy_rx_pause;
 
 
 	/* Netdev related settings */
 	/* Netdev related settings */
+	unsigned char mac_addr[ETH_ALEN];
 	netdev_features_t netdev_features;
 	netdev_features_t netdev_features;
 	struct napi_struct napi;
 	struct napi_struct napi;
 	struct xgbe_mmc_stats mmc_stats;
 	struct xgbe_mmc_stats mmc_stats;
@@ -748,7 +757,9 @@ struct xgbe_prv_data {
 
 
 	/* Device clocks */
 	/* Device clocks */
 	struct clk *sysclk;
 	struct clk *sysclk;
+	unsigned long sysclk_rate;
 	struct clk *ptpclk;
 	struct clk *ptpclk;
+	unsigned long ptpclk_rate;
 
 
 	/* Timestamp support */
 	/* Timestamp support */
 	spinlock_t tstamp_lock;
 	spinlock_t tstamp_lock;

+ 1 - 1
drivers/net/phy/Kconfig

@@ -26,7 +26,7 @@ config AMD_PHY
 
 
 config AMD_XGBE_PHY
 config AMD_XGBE_PHY
 	tristate "Driver for the AMD 10GbE (amd-xgbe) PHYs"
 	tristate "Driver for the AMD 10GbE (amd-xgbe) PHYs"
-	depends on OF && HAS_IOMEM
+	depends on (OF || ACPI) && HAS_IOMEM
 	---help---
 	---help---
 	  Currently supports the AMD 10GbE PHY
 	  Currently supports the AMD 10GbE PHY
 
 

+ 634 - 347
drivers/net/phy/amd-xgbe-phy.c

@@ -60,6 +60,7 @@
 #include <linux/interrupt.h>
 #include <linux/interrupt.h>
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/delay.h>
+#include <linux/workqueue.h>
 #include <linux/netdevice.h>
 #include <linux/netdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/etherdevice.h>
 #include <linux/skbuff.h>
 #include <linux/skbuff.h>
@@ -74,6 +75,9 @@
 #include <linux/of_platform.h>
 #include <linux/of_platform.h>
 #include <linux/of_device.h>
 #include <linux/of_device.h>
 #include <linux/uaccess.h>
 #include <linux/uaccess.h>
+#include <linux/bitops.h>
+#include <linux/property.h>
+#include <linux/acpi.h>
 
 
 MODULE_AUTHOR("Tom Lendacky <thomas.lendacky@amd.com>");
 MODULE_AUTHOR("Tom Lendacky <thomas.lendacky@amd.com>");
 MODULE_LICENSE("Dual BSD/GPL");
 MODULE_LICENSE("Dual BSD/GPL");
@@ -84,22 +88,43 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver");
 #define XGBE_PHY_MASK	0xfffffff0
 #define XGBE_PHY_MASK	0xfffffff0
 
 
 #define XGBE_PHY_SPEEDSET_PROPERTY	"amd,speed-set"
 #define XGBE_PHY_SPEEDSET_PROPERTY	"amd,speed-set"
+#define XGBE_PHY_BLWC_PROPERTY		"amd,serdes-blwc"
+#define XGBE_PHY_CDR_RATE_PROPERTY	"amd,serdes-cdr-rate"
+#define XGBE_PHY_PQ_SKEW_PROPERTY	"amd,serdes-pq-skew"
+#define XGBE_PHY_TX_AMP_PROPERTY	"amd,serdes-tx-amp"
+
+#define XGBE_PHY_SPEEDS			3
+#define XGBE_PHY_SPEED_1000		0
+#define XGBE_PHY_SPEED_2500		1
+#define XGBE_PHY_SPEED_10000		2
 
 
 #define XGBE_AN_INT_CMPLT		0x01
 #define XGBE_AN_INT_CMPLT		0x01
 #define XGBE_AN_INC_LINK		0x02
 #define XGBE_AN_INC_LINK		0x02
 #define XGBE_AN_PG_RCV			0x04
 #define XGBE_AN_PG_RCV			0x04
+#define XGBE_AN_INT_MASK		0x07
 
 
 #define XNP_MCF_NULL_MESSAGE		0x001
 #define XNP_MCF_NULL_MESSAGE		0x001
-#define XNP_ACK_PROCESSED		(1 << 12)
-#define XNP_MP_FORMATTED		(1 << 13)
-#define XNP_NP_EXCHANGE			(1 << 15)
+#define XNP_ACK_PROCESSED		BIT(12)
+#define XNP_MP_FORMATTED		BIT(13)
+#define XNP_NP_EXCHANGE			BIT(15)
 
 
 #define XGBE_PHY_RATECHANGE_COUNT	500
 #define XGBE_PHY_RATECHANGE_COUNT	500
 
 
+#define XGBE_PHY_KR_TRAINING_START	0x01
+#define XGBE_PHY_KR_TRAINING_ENABLE	0x02
+
+#define XGBE_PHY_FEC_ENABLE		0x01
+#define XGBE_PHY_FEC_FORWARD		0x02
+#define XGBE_PHY_FEC_MASK		0x03
+
 #ifndef MDIO_PMA_10GBR_PMD_CTRL
 #ifndef MDIO_PMA_10GBR_PMD_CTRL
 #define MDIO_PMA_10GBR_PMD_CTRL		0x0096
 #define MDIO_PMA_10GBR_PMD_CTRL		0x0096
 #endif
 #endif
 
 
+#ifndef MDIO_PMA_10GBR_FEC_ABILITY
+#define MDIO_PMA_10GBR_FEC_ABILITY	0x00aa
+#endif
+
 #ifndef MDIO_PMA_10GBR_FEC_CTRL
 #ifndef MDIO_PMA_10GBR_FEC_CTRL
 #define MDIO_PMA_10GBR_FEC_CTRL		0x00ab
 #define MDIO_PMA_10GBR_FEC_CTRL		0x00ab
 #endif
 #endif
@@ -108,6 +133,10 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver");
 #define MDIO_AN_XNP			0x0016
 #define MDIO_AN_XNP			0x0016
 #endif
 #endif
 
 
+#ifndef MDIO_AN_LPX
+#define MDIO_AN_LPX			0x0019
+#endif
+
 #ifndef MDIO_AN_INTMASK
 #ifndef MDIO_AN_INTMASK
 #define MDIO_AN_INTMASK			0x8001
 #define MDIO_AN_INTMASK			0x8001
 #endif
 #endif
@@ -116,18 +145,10 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver");
 #define MDIO_AN_INT			0x8002
 #define MDIO_AN_INT			0x8002
 #endif
 #endif
 
 
-#ifndef MDIO_AN_KR_CTRL
-#define MDIO_AN_KR_CTRL			0x8003
-#endif
-
 #ifndef MDIO_CTRL1_SPEED1G
 #ifndef MDIO_CTRL1_SPEED1G
 #define MDIO_CTRL1_SPEED1G		(MDIO_CTRL1_SPEED10G & ~BMCR_SPEED100)
 #define MDIO_CTRL1_SPEED1G		(MDIO_CTRL1_SPEED10G & ~BMCR_SPEED100)
 #endif
 #endif
 
 
-#ifndef MDIO_KR_CTRL_PDETECT
-#define MDIO_KR_CTRL_PDETECT		0x01
-#endif
-
 /* SerDes integration register offsets */
 /* SerDes integration register offsets */
 #define SIR0_KR_RT_1			0x002c
 #define SIR0_KR_RT_1			0x002c
 #define SIR0_STATUS			0x0040
 #define SIR0_STATUS			0x0040
@@ -140,10 +161,10 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver");
 #define SIR0_STATUS_RX_READY_WIDTH	1
 #define SIR0_STATUS_RX_READY_WIDTH	1
 #define SIR0_STATUS_TX_READY_INDEX	8
 #define SIR0_STATUS_TX_READY_INDEX	8
 #define SIR0_STATUS_TX_READY_WIDTH	1
 #define SIR0_STATUS_TX_READY_WIDTH	1
+#define SIR1_SPEED_CDR_RATE_INDEX	12
+#define SIR1_SPEED_CDR_RATE_WIDTH	4
 #define SIR1_SPEED_DATARATE_INDEX	4
 #define SIR1_SPEED_DATARATE_INDEX	4
 #define SIR1_SPEED_DATARATE_WIDTH	2
 #define SIR1_SPEED_DATARATE_WIDTH	2
-#define SIR1_SPEED_PI_SPD_SEL_INDEX	12
-#define SIR1_SPEED_PI_SPD_SEL_WIDTH	4
 #define SIR1_SPEED_PLLSEL_INDEX		3
 #define SIR1_SPEED_PLLSEL_INDEX		3
 #define SIR1_SPEED_PLLSEL_WIDTH		1
 #define SIR1_SPEED_PLLSEL_WIDTH		1
 #define SIR1_SPEED_RATECHANGE_INDEX	6
 #define SIR1_SPEED_RATECHANGE_INDEX	6
@@ -153,20 +174,26 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver");
 #define SIR1_SPEED_WORDMODE_INDEX	0
 #define SIR1_SPEED_WORDMODE_INDEX	0
 #define SIR1_SPEED_WORDMODE_WIDTH	3
 #define SIR1_SPEED_WORDMODE_WIDTH	3
 
 
+#define SPEED_10000_BLWC		0
 #define SPEED_10000_CDR			0x7
 #define SPEED_10000_CDR			0x7
 #define SPEED_10000_PLL			0x1
 #define SPEED_10000_PLL			0x1
+#define SPEED_10000_PQ			0x1e
 #define SPEED_10000_RATE		0x0
 #define SPEED_10000_RATE		0x0
 #define SPEED_10000_TXAMP		0xa
 #define SPEED_10000_TXAMP		0xa
 #define SPEED_10000_WORD		0x7
 #define SPEED_10000_WORD		0x7
 
 
+#define SPEED_2500_BLWC			1
 #define SPEED_2500_CDR			0x2
 #define SPEED_2500_CDR			0x2
 #define SPEED_2500_PLL			0x0
 #define SPEED_2500_PLL			0x0
+#define SPEED_2500_PQ			0xa
 #define SPEED_2500_RATE			0x1
 #define SPEED_2500_RATE			0x1
 #define SPEED_2500_TXAMP		0xf
 #define SPEED_2500_TXAMP		0xf
 #define SPEED_2500_WORD			0x1
 #define SPEED_2500_WORD			0x1
 
 
+#define SPEED_1000_BLWC			1
 #define SPEED_1000_CDR			0x2
 #define SPEED_1000_CDR			0x2
 #define SPEED_1000_PLL			0x0
 #define SPEED_1000_PLL			0x0
+#define SPEED_1000_PQ			0xa
 #define SPEED_1000_RATE			0x3
 #define SPEED_1000_RATE			0x3
 #define SPEED_1000_TXAMP		0xf
 #define SPEED_1000_TXAMP		0xf
 #define SPEED_1000_WORD			0x1
 #define SPEED_1000_WORD			0x1
@@ -181,15 +208,6 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver");
 #define RXTX_REG114_PQ_REG_INDEX	9
 #define RXTX_REG114_PQ_REG_INDEX	9
 #define RXTX_REG114_PQ_REG_WIDTH	7
 #define RXTX_REG114_PQ_REG_WIDTH	7
 
 
-#define RXTX_10000_BLWC			0
-#define RXTX_10000_PQ			0x1e
-
-#define RXTX_2500_BLWC			1
-#define RXTX_2500_PQ			0xa
-
-#define RXTX_1000_BLWC			1
-#define RXTX_1000_PQ			0xa
-
 /* Bit setting and getting macros
 /* Bit setting and getting macros
  *  The get macro will extract the current bit field value from within
  *  The get macro will extract the current bit field value from within
  *  the variable
  *  the variable
@@ -291,23 +309,44 @@ do {									\
 	XRXTX_IOWRITE((_priv), _reg, reg_val);				\
 	XRXTX_IOWRITE((_priv), _reg, reg_val);				\
 } while (0)
 } while (0)
 
 
+static const u32 amd_xgbe_phy_serdes_blwc[] = {
+	SPEED_1000_BLWC,
+	SPEED_2500_BLWC,
+	SPEED_10000_BLWC,
+};
+
+static const u32 amd_xgbe_phy_serdes_cdr_rate[] = {
+	SPEED_1000_CDR,
+	SPEED_2500_CDR,
+	SPEED_10000_CDR,
+};
+
+static const u32 amd_xgbe_phy_serdes_pq_skew[] = {
+	SPEED_1000_PQ,
+	SPEED_2500_PQ,
+	SPEED_10000_PQ,
+};
+
+static const u32 amd_xgbe_phy_serdes_tx_amp[] = {
+	SPEED_1000_TXAMP,
+	SPEED_2500_TXAMP,
+	SPEED_10000_TXAMP,
+};
+
 enum amd_xgbe_phy_an {
 enum amd_xgbe_phy_an {
 	AMD_XGBE_AN_READY = 0,
 	AMD_XGBE_AN_READY = 0,
-	AMD_XGBE_AN_START,
-	AMD_XGBE_AN_EVENT,
 	AMD_XGBE_AN_PAGE_RECEIVED,
 	AMD_XGBE_AN_PAGE_RECEIVED,
 	AMD_XGBE_AN_INCOMPAT_LINK,
 	AMD_XGBE_AN_INCOMPAT_LINK,
 	AMD_XGBE_AN_COMPLETE,
 	AMD_XGBE_AN_COMPLETE,
 	AMD_XGBE_AN_NO_LINK,
 	AMD_XGBE_AN_NO_LINK,
-	AMD_XGBE_AN_EXIT,
 	AMD_XGBE_AN_ERROR,
 	AMD_XGBE_AN_ERROR,
 };
 };
 
 
 enum amd_xgbe_phy_rx {
 enum amd_xgbe_phy_rx {
-	AMD_XGBE_RX_READY = 0,
-	AMD_XGBE_RX_BPA,
+	AMD_XGBE_RX_BPA = 0,
 	AMD_XGBE_RX_XNP,
 	AMD_XGBE_RX_XNP,
 	AMD_XGBE_RX_COMPLETE,
 	AMD_XGBE_RX_COMPLETE,
+	AMD_XGBE_RX_ERROR,
 };
 };
 
 
 enum amd_xgbe_phy_mode {
 enum amd_xgbe_phy_mode {
@@ -316,12 +355,13 @@ enum amd_xgbe_phy_mode {
 };
 };
 
 
 enum amd_xgbe_phy_speedset {
 enum amd_xgbe_phy_speedset {
-	AMD_XGBE_PHY_SPEEDSET_1000_10000,
+	AMD_XGBE_PHY_SPEEDSET_1000_10000 = 0,
 	AMD_XGBE_PHY_SPEEDSET_2500_10000,
 	AMD_XGBE_PHY_SPEEDSET_2500_10000,
 };
 };
 
 
 struct amd_xgbe_phy_priv {
 struct amd_xgbe_phy_priv {
 	struct platform_device *pdev;
 	struct platform_device *pdev;
+	struct acpi_device *adev;
 	struct device *dev;
 	struct device *dev;
 
 
 	struct phy_device *phydev;
 	struct phy_device *phydev;
@@ -336,10 +376,24 @@ struct amd_xgbe_phy_priv {
 	void __iomem *sir0_regs;	/* SerDes integration registers (1/2) */
 	void __iomem *sir0_regs;	/* SerDes integration registers (1/2) */
 	void __iomem *sir1_regs;	/* SerDes integration registers (2/2) */
 	void __iomem *sir1_regs;	/* SerDes integration registers (2/2) */
 
 
-	/* Maintain link status for re-starting auto-negotiation */
-	unsigned int link;
+	int an_irq;
+	char an_irq_name[IFNAMSIZ + 32];
+	struct work_struct an_irq_work;
+	unsigned int an_irq_allocated;
+
 	unsigned int speed_set;
 	unsigned int speed_set;
 
 
+	/* SerDes UEFI configurable settings.
+	 *   Switching between modes/speeds requires new values for some
+	 *   SerDes settings.  The values can be supplied as device
+	 *   properties in array format.  The first array entry is for
+	 *   1GbE, second for 2.5GbE and third for 10GbE
+	 */
+	u32 serdes_blwc[XGBE_PHY_SPEEDS];
+	u32 serdes_cdr_rate[XGBE_PHY_SPEEDS];
+	u32 serdes_pq_skew[XGBE_PHY_SPEEDS];
+	u32 serdes_tx_amp[XGBE_PHY_SPEEDS];
+
 	/* Auto-negotiation state machine support */
 	/* Auto-negotiation state machine support */
 	struct mutex an_mutex;
 	struct mutex an_mutex;
 	enum amd_xgbe_phy_an an_result;
 	enum amd_xgbe_phy_an an_result;
@@ -348,7 +402,11 @@ struct amd_xgbe_phy_priv {
 	enum amd_xgbe_phy_rx kx_state;
 	enum amd_xgbe_phy_rx kx_state;
 	struct work_struct an_work;
 	struct work_struct an_work;
 	struct workqueue_struct *an_workqueue;
 	struct workqueue_struct *an_workqueue;
+	unsigned int an_supported;
 	unsigned int parallel_detect;
 	unsigned int parallel_detect;
+	unsigned int fec_ability;
+
+	unsigned int lpm_ctrl;		/* CTRL1 for resume */
 };
 };
 
 
 static int amd_xgbe_an_enable_kr_training(struct phy_device *phydev)
 static int amd_xgbe_an_enable_kr_training(struct phy_device *phydev)
@@ -359,7 +417,7 @@ static int amd_xgbe_an_enable_kr_training(struct phy_device *phydev)
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
 
 
-	ret |= 0x02;
+	ret |= XGBE_PHY_KR_TRAINING_ENABLE;
 	phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, ret);
 	phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, ret);
 
 
 	return 0;
 	return 0;
@@ -373,7 +431,7 @@ static int amd_xgbe_an_disable_kr_training(struct phy_device *phydev)
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
 
 
-	ret &= ~0x02;
+	ret &= ~XGBE_PHY_KR_TRAINING_ENABLE;
 	phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, ret);
 	phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, ret);
 
 
 	return 0;
 	return 0;
@@ -466,12 +524,16 @@ static int amd_xgbe_phy_xgmii_mode(struct phy_device *phydev)
 
 
 	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_10000_RATE);
 	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_10000_RATE);
 	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_10000_WORD);
 	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_10000_WORD);
-	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, SPEED_10000_TXAMP);
 	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_10000_PLL);
 	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_10000_PLL);
-	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PI_SPD_SEL, SPEED_10000_CDR);
 
 
-	XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, RXTX_10000_BLWC);
-	XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_10000_PQ);
+	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, CDR_RATE,
+			   priv->serdes_cdr_rate[XGBE_PHY_SPEED_10000]);
+	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP,
+			   priv->serdes_tx_amp[XGBE_PHY_SPEED_10000]);
+	XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA,
+			   priv->serdes_blwc[XGBE_PHY_SPEED_10000]);
+	XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG,
+			   priv->serdes_pq_skew[XGBE_PHY_SPEED_10000]);
 
 
 	amd_xgbe_phy_serdes_complete_ratechange(phydev);
 	amd_xgbe_phy_serdes_complete_ratechange(phydev);
 
 
@@ -514,12 +576,16 @@ static int amd_xgbe_phy_gmii_2500_mode(struct phy_device *phydev)
 
 
 	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_2500_RATE);
 	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_2500_RATE);
 	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_2500_WORD);
 	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_2500_WORD);
-	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, SPEED_2500_TXAMP);
 	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_2500_PLL);
 	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_2500_PLL);
-	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PI_SPD_SEL, SPEED_2500_CDR);
 
 
-	XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, RXTX_2500_BLWC);
-	XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_2500_PQ);
+	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, CDR_RATE,
+			   priv->serdes_cdr_rate[XGBE_PHY_SPEED_2500]);
+	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP,
+			   priv->serdes_tx_amp[XGBE_PHY_SPEED_2500]);
+	XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA,
+			   priv->serdes_blwc[XGBE_PHY_SPEED_2500]);
+	XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG,
+			   priv->serdes_pq_skew[XGBE_PHY_SPEED_2500]);
 
 
 	amd_xgbe_phy_serdes_complete_ratechange(phydev);
 	amd_xgbe_phy_serdes_complete_ratechange(phydev);
 
 
@@ -562,12 +628,16 @@ static int amd_xgbe_phy_gmii_mode(struct phy_device *phydev)
 
 
 	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_1000_RATE);
 	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, DATARATE, SPEED_1000_RATE);
 	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_1000_WORD);
 	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, WORDMODE, SPEED_1000_WORD);
-	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP, SPEED_1000_TXAMP);
 	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_1000_PLL);
 	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PLLSEL, SPEED_1000_PLL);
-	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, PI_SPD_SEL, SPEED_1000_CDR);
 
 
-	XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA, RXTX_1000_BLWC);
-	XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG, RXTX_1000_PQ);
+	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, CDR_RATE,
+			   priv->serdes_cdr_rate[XGBE_PHY_SPEED_1000]);
+	XSIR1_IOWRITE_BITS(priv, SIR1_SPEED, TXAMP,
+			   priv->serdes_tx_amp[XGBE_PHY_SPEED_1000]);
+	XRXTX_IOWRITE_BITS(priv, RXTX_REG20, BLWC_ENA,
+			   priv->serdes_blwc[XGBE_PHY_SPEED_1000]);
+	XRXTX_IOWRITE_BITS(priv, RXTX_REG114, PQ_REG,
+			   priv->serdes_pq_skew[XGBE_PHY_SPEED_1000]);
 
 
 	amd_xgbe_phy_serdes_complete_ratechange(phydev);
 	amd_xgbe_phy_serdes_complete_ratechange(phydev);
 
 
@@ -635,6 +705,38 @@ static int amd_xgbe_phy_set_mode(struct phy_device *phydev,
 	return ret;
 	return ret;
 }
 }
 
 
+static int amd_xgbe_phy_set_an(struct phy_device *phydev, bool enable,
+			       bool restart)
+{
+	int ret;
+
+	ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1);
+	if (ret < 0)
+		return ret;
+
+	ret &= ~MDIO_AN_CTRL1_ENABLE;
+
+	if (enable)
+		ret |= MDIO_AN_CTRL1_ENABLE;
+
+	if (restart)
+		ret |= MDIO_AN_CTRL1_RESTART;
+
+	phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, ret);
+
+	return 0;
+}
+
+static int amd_xgbe_phy_restart_an(struct phy_device *phydev)
+{
+	return amd_xgbe_phy_set_an(phydev, true, true);
+}
+
+static int amd_xgbe_phy_disable_an(struct phy_device *phydev)
+{
+	return amd_xgbe_phy_set_an(phydev, false, false);
+}
+
 static enum amd_xgbe_phy_an amd_xgbe_an_tx_training(struct phy_device *phydev,
 static enum amd_xgbe_phy_an amd_xgbe_an_tx_training(struct phy_device *phydev,
 						    enum amd_xgbe_phy_rx *state)
 						    enum amd_xgbe_phy_rx *state)
 {
 {
@@ -645,7 +747,7 @@ static enum amd_xgbe_phy_an amd_xgbe_an_tx_training(struct phy_device *phydev,
 
 
 	/* If we're not in KR mode then we're done */
 	/* If we're not in KR mode then we're done */
 	if (!amd_xgbe_phy_in_kr_mode(phydev))
 	if (!amd_xgbe_phy_in_kr_mode(phydev))
-		return AMD_XGBE_AN_EVENT;
+		return AMD_XGBE_AN_PAGE_RECEIVED;
 
 
 	/* Enable/Disable FEC */
 	/* Enable/Disable FEC */
 	ad_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2);
 	ad_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2);
@@ -660,10 +762,9 @@ static enum amd_xgbe_phy_an amd_xgbe_an_tx_training(struct phy_device *phydev,
 	if (ret < 0)
 	if (ret < 0)
 		return AMD_XGBE_AN_ERROR;
 		return AMD_XGBE_AN_ERROR;
 
 
+	ret &= ~XGBE_PHY_FEC_MASK;
 	if ((ad_reg & 0xc000) && (lp_reg & 0xc000))
 	if ((ad_reg & 0xc000) && (lp_reg & 0xc000))
-		ret |= 0x01;
-	else
-		ret &= ~0x01;
+		ret |= priv->fec_ability;
 
 
 	phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FEC_CTRL, ret);
 	phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FEC_CTRL, ret);
 
 
@@ -672,14 +773,17 @@ static enum amd_xgbe_phy_an amd_xgbe_an_tx_training(struct phy_device *phydev,
 	if (ret < 0)
 	if (ret < 0)
 		return AMD_XGBE_AN_ERROR;
 		return AMD_XGBE_AN_ERROR;
 
 
-	XSIR0_IOWRITE_BITS(priv, SIR0_KR_RT_1, RESET, 1);
+	if (ret & XGBE_PHY_KR_TRAINING_ENABLE) {
+		XSIR0_IOWRITE_BITS(priv, SIR0_KR_RT_1, RESET, 1);
 
 
-	ret |= 0x01;
-	phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, ret);
+		ret |= XGBE_PHY_KR_TRAINING_START;
+		phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL,
+			      ret);
 
 
-	XSIR0_IOWRITE_BITS(priv, SIR0_KR_RT_1, RESET, 0);
+		XSIR0_IOWRITE_BITS(priv, SIR0_KR_RT_1, RESET, 0);
+	}
 
 
-	return AMD_XGBE_AN_EVENT;
+	return AMD_XGBE_AN_PAGE_RECEIVED;
 }
 }
 
 
 static enum amd_xgbe_phy_an amd_xgbe_an_tx_xnp(struct phy_device *phydev,
 static enum amd_xgbe_phy_an amd_xgbe_an_tx_xnp(struct phy_device *phydev,
@@ -696,7 +800,7 @@ static enum amd_xgbe_phy_an amd_xgbe_an_tx_xnp(struct phy_device *phydev,
 	phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_XNP + 1, 0);
 	phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_XNP + 1, 0);
 	phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_XNP, msg);
 	phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_XNP, msg);
 
 
-	return AMD_XGBE_AN_EVENT;
+	return AMD_XGBE_AN_PAGE_RECEIVED;
 }
 }
 
 
 static enum amd_xgbe_phy_an amd_xgbe_an_rx_bpa(struct phy_device *phydev,
 static enum amd_xgbe_phy_an amd_xgbe_an_rx_bpa(struct phy_device *phydev,
@@ -735,11 +839,11 @@ static enum amd_xgbe_phy_an amd_xgbe_an_rx_xnp(struct phy_device *phydev,
 	int ad_reg, lp_reg;
 	int ad_reg, lp_reg;
 
 
 	/* Check Extended Next Page support */
 	/* Check Extended Next Page support */
-	ad_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
+	ad_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_XNP);
 	if (ad_reg < 0)
 	if (ad_reg < 0)
 		return AMD_XGBE_AN_ERROR;
 		return AMD_XGBE_AN_ERROR;
 
 
-	lp_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPA);
+	lp_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_LPX);
 	if (lp_reg < 0)
 	if (lp_reg < 0)
 		return AMD_XGBE_AN_ERROR;
 		return AMD_XGBE_AN_ERROR;
 
 
@@ -748,226 +852,255 @@ static enum amd_xgbe_phy_an amd_xgbe_an_rx_xnp(struct phy_device *phydev,
 	       amd_xgbe_an_tx_training(phydev, state);
 	       amd_xgbe_an_tx_training(phydev, state);
 }
 }
 
 
-static enum amd_xgbe_phy_an amd_xgbe_an_start(struct phy_device *phydev)
+static enum amd_xgbe_phy_an amd_xgbe_an_page_received(struct phy_device *phydev)
+{
+	struct amd_xgbe_phy_priv *priv = phydev->priv;
+	enum amd_xgbe_phy_rx *state;
+	int ret;
+
+	state = amd_xgbe_phy_in_kr_mode(phydev) ? &priv->kr_state
+						: &priv->kx_state;
+
+	switch (*state) {
+	case AMD_XGBE_RX_BPA:
+		ret = amd_xgbe_an_rx_bpa(phydev, state);
+		break;
+
+	case AMD_XGBE_RX_XNP:
+		ret = amd_xgbe_an_rx_xnp(phydev, state);
+		break;
+
+	default:
+		ret = AMD_XGBE_AN_ERROR;
+	}
+
+	return ret;
+}
+
+static enum amd_xgbe_phy_an amd_xgbe_an_incompat_link(struct phy_device *phydev)
 {
 {
 	struct amd_xgbe_phy_priv *priv = phydev->priv;
 	struct amd_xgbe_phy_priv *priv = phydev->priv;
 	int ret;
 	int ret;
 
 
 	/* Be sure we aren't looping trying to negotiate */
 	/* Be sure we aren't looping trying to negotiate */
 	if (amd_xgbe_phy_in_kr_mode(phydev)) {
 	if (amd_xgbe_phy_in_kr_mode(phydev)) {
-		if (priv->kr_state != AMD_XGBE_RX_READY)
+		priv->kr_state = AMD_XGBE_RX_ERROR;
+
+		if (!(phydev->supported & SUPPORTED_1000baseKX_Full) &&
+		    !(phydev->supported & SUPPORTED_2500baseX_Full))
+			return AMD_XGBE_AN_NO_LINK;
+
+		if (priv->kx_state != AMD_XGBE_RX_BPA)
 			return AMD_XGBE_AN_NO_LINK;
 			return AMD_XGBE_AN_NO_LINK;
-		priv->kr_state = AMD_XGBE_RX_BPA;
 	} else {
 	} else {
-		if (priv->kx_state != AMD_XGBE_RX_READY)
+		priv->kx_state = AMD_XGBE_RX_ERROR;
+
+		if (!(phydev->supported & SUPPORTED_10000baseKR_Full))
+			return AMD_XGBE_AN_NO_LINK;
+
+		if (priv->kr_state != AMD_XGBE_RX_BPA)
 			return AMD_XGBE_AN_NO_LINK;
 			return AMD_XGBE_AN_NO_LINK;
-		priv->kx_state = AMD_XGBE_RX_BPA;
 	}
 	}
 
 
-	/* Set up Advertisement register 3 first */
-	ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2);
-	if (ret < 0)
+	ret = amd_xgbe_phy_disable_an(phydev);
+	if (ret)
 		return AMD_XGBE_AN_ERROR;
 		return AMD_XGBE_AN_ERROR;
 
 
-	if (phydev->supported & SUPPORTED_10000baseR_FEC)
-		ret |= 0xc000;
-	else
-		ret &= ~0xc000;
-
-	phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2, ret);
-
-	/* Set up Advertisement register 2 next */
-	ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1);
-	if (ret < 0)
+	ret = amd_xgbe_phy_switch_mode(phydev);
+	if (ret)
 		return AMD_XGBE_AN_ERROR;
 		return AMD_XGBE_AN_ERROR;
 
 
-	if (phydev->supported & SUPPORTED_10000baseKR_Full)
-		ret |= 0x80;
-	else
-		ret &= ~0x80;
-
-	if ((phydev->supported & SUPPORTED_1000baseKX_Full) ||
-	    (phydev->supported & SUPPORTED_2500baseX_Full))
-		ret |= 0x20;
-	else
-		ret &= ~0x20;
-
-	phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1, ret);
-
-	/* Set up Advertisement register 1 last */
-	ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
-	if (ret < 0)
+	ret = amd_xgbe_phy_restart_an(phydev);
+	if (ret)
 		return AMD_XGBE_AN_ERROR;
 		return AMD_XGBE_AN_ERROR;
 
 
-	if (phydev->supported & SUPPORTED_Pause)
-		ret |= 0x400;
-	else
-		ret &= ~0x400;
+	return AMD_XGBE_AN_INCOMPAT_LINK;
+}
 
 
-	if (phydev->supported & SUPPORTED_Asym_Pause)
-		ret |= 0x800;
-	else
-		ret &= ~0x800;
+static irqreturn_t amd_xgbe_an_isr(int irq, void *data)
+{
+	struct amd_xgbe_phy_priv *priv = (struct amd_xgbe_phy_priv *)data;
 
 
-	/* We don't intend to perform XNP */
-	ret &= ~XNP_NP_EXCHANGE;
+	/* Interrupt reason must be read and cleared outside of IRQ context */
+	disable_irq_nosync(priv->an_irq);
 
 
-	phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE, ret);
+	queue_work(priv->an_workqueue, &priv->an_irq_work);
 
 
-	/* Enable and start auto-negotiation */
-	phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0);
+	return IRQ_HANDLED;
+}
 
 
-	ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_KR_CTRL);
-	if (ret < 0)
-		return AMD_XGBE_AN_ERROR;
+static void amd_xgbe_an_irq_work(struct work_struct *work)
+{
+	struct amd_xgbe_phy_priv *priv = container_of(work,
+						      struct amd_xgbe_phy_priv,
+						      an_irq_work);
 
 
-	ret |= MDIO_KR_CTRL_PDETECT;
-	phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_KR_CTRL, ret);
+	/* Avoid a race between enabling the IRQ and exiting the work by
+	 * waiting for the work to finish and then queueing it
+	 */
+	flush_work(&priv->an_work);
+	queue_work(priv->an_workqueue, &priv->an_work);
+}
 
 
-	ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1);
-	if (ret < 0)
-		return AMD_XGBE_AN_ERROR;
+static void amd_xgbe_an_state_machine(struct work_struct *work)
+{
+	struct amd_xgbe_phy_priv *priv = container_of(work,
+						      struct amd_xgbe_phy_priv,
+						      an_work);
+	struct phy_device *phydev = priv->phydev;
+	enum amd_xgbe_phy_an cur_state = priv->an_state;
+	int int_reg, int_mask;
 
 
-	ret |= MDIO_AN_CTRL1_ENABLE;
-	ret |= MDIO_AN_CTRL1_RESTART;
-	phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, ret);
+	mutex_lock(&priv->an_mutex);
 
 
-	return AMD_XGBE_AN_EVENT;
-}
+	/* Read the interrupt */
+	int_reg = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT);
+	if (!int_reg)
+		goto out;
 
 
-static enum amd_xgbe_phy_an amd_xgbe_an_event(struct phy_device *phydev)
-{
-	enum amd_xgbe_phy_an new_state;
-	int ret;
+next_int:
+	if (int_reg < 0) {
+		priv->an_state = AMD_XGBE_AN_ERROR;
+		int_mask = XGBE_AN_INT_MASK;
+	} else if (int_reg & XGBE_AN_PG_RCV) {
+		priv->an_state = AMD_XGBE_AN_PAGE_RECEIVED;
+		int_mask = XGBE_AN_PG_RCV;
+	} else if (int_reg & XGBE_AN_INC_LINK) {
+		priv->an_state = AMD_XGBE_AN_INCOMPAT_LINK;
+		int_mask = XGBE_AN_INC_LINK;
+	} else if (int_reg & XGBE_AN_INT_CMPLT) {
+		priv->an_state = AMD_XGBE_AN_COMPLETE;
+		int_mask = XGBE_AN_INT_CMPLT;
+	} else {
+		priv->an_state = AMD_XGBE_AN_ERROR;
+		int_mask = 0;
+	}
 
 
-	ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT);
-	if (ret < 0)
-		return AMD_XGBE_AN_ERROR;
+	/* Clear the interrupt to be processed */
+	int_reg &= ~int_mask;
+	phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, int_reg);
 
 
-	new_state = AMD_XGBE_AN_EVENT;
-	if (ret & XGBE_AN_PG_RCV)
-		new_state = AMD_XGBE_AN_PAGE_RECEIVED;
-	else if (ret & XGBE_AN_INC_LINK)
-		new_state = AMD_XGBE_AN_INCOMPAT_LINK;
-	else if (ret & XGBE_AN_INT_CMPLT)
-		new_state = AMD_XGBE_AN_COMPLETE;
+	priv->an_result = priv->an_state;
 
 
-	if (new_state != AMD_XGBE_AN_EVENT)
-		phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0);
+again:
+	cur_state = priv->an_state;
 
 
-	return new_state;
-}
+	switch (priv->an_state) {
+	case AMD_XGBE_AN_READY:
+		priv->an_supported = 0;
+		break;
 
 
-static enum amd_xgbe_phy_an amd_xgbe_an_page_received(struct phy_device *phydev)
-{
-	struct amd_xgbe_phy_priv *priv = phydev->priv;
-	enum amd_xgbe_phy_rx *state;
-	int ret;
+	case AMD_XGBE_AN_PAGE_RECEIVED:
+		priv->an_state = amd_xgbe_an_page_received(phydev);
+		priv->an_supported++;
+		break;
 
 
-	state = amd_xgbe_phy_in_kr_mode(phydev) ? &priv->kr_state
-						: &priv->kx_state;
+	case AMD_XGBE_AN_INCOMPAT_LINK:
+		priv->an_supported = 0;
+		priv->parallel_detect = 0;
+		priv->an_state = amd_xgbe_an_incompat_link(phydev);
+		break;
 
 
-	switch (*state) {
-	case AMD_XGBE_RX_BPA:
-		ret = amd_xgbe_an_rx_bpa(phydev, state);
+	case AMD_XGBE_AN_COMPLETE:
+		priv->parallel_detect = priv->an_supported ? 0 : 1;
+		netdev_dbg(phydev->attached_dev, "%s successful\n",
+			   priv->an_supported ? "Auto negotiation"
+					      : "Parallel detection");
 		break;
 		break;
 
 
-	case AMD_XGBE_RX_XNP:
-		ret = amd_xgbe_an_rx_xnp(phydev, state);
+	case AMD_XGBE_AN_NO_LINK:
 		break;
 		break;
 
 
 	default:
 	default:
-		ret = AMD_XGBE_AN_ERROR;
+		priv->an_state = AMD_XGBE_AN_ERROR;
 	}
 	}
 
 
-	return ret;
-}
+	if (priv->an_state == AMD_XGBE_AN_NO_LINK) {
+		int_reg = 0;
+		phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0);
+	} else if (priv->an_state == AMD_XGBE_AN_ERROR) {
+		netdev_err(phydev->attached_dev,
+			   "error during auto-negotiation, state=%u\n",
+			   cur_state);
 
 
-static enum amd_xgbe_phy_an amd_xgbe_an_incompat_link(struct phy_device *phydev)
-{
-	int ret;
+		int_reg = 0;
+		phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0);
+	}
 
 
-	ret = amd_xgbe_phy_switch_mode(phydev);
-	if (ret)
-		return AMD_XGBE_AN_ERROR;
+	if (priv->an_state >= AMD_XGBE_AN_COMPLETE) {
+		priv->an_result = priv->an_state;
+		priv->an_state = AMD_XGBE_AN_READY;
+		priv->kr_state = AMD_XGBE_RX_BPA;
+		priv->kx_state = AMD_XGBE_RX_BPA;
+	}
 
 
-	return AMD_XGBE_AN_START;
-}
+	if (cur_state != priv->an_state)
+		goto again;
 
 
-static void amd_xgbe_an_state_machine(struct work_struct *work)
-{
-	struct amd_xgbe_phy_priv *priv = container_of(work,
-						      struct amd_xgbe_phy_priv,
-						      an_work);
-	struct phy_device *phydev = priv->phydev;
-	enum amd_xgbe_phy_an cur_state;
-	int sleep;
-	unsigned int an_supported = 0;
+	if (int_reg)
+		goto next_int;
 
 
-	/* Start in KX mode */
-	if (amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KX))
-		priv->an_state = AMD_XGBE_AN_ERROR;
+out:
+	enable_irq(priv->an_irq);
 
 
-	while (1) {
-		mutex_lock(&priv->an_mutex);
+	mutex_unlock(&priv->an_mutex);
+}
 
 
-		cur_state = priv->an_state;
+static int amd_xgbe_an_init(struct phy_device *phydev)
+{
+	int ret;
 
 
-		switch (priv->an_state) {
-		case AMD_XGBE_AN_START:
-			an_supported = 0;
-			priv->parallel_detect = 0;
-			priv->an_state = amd_xgbe_an_start(phydev);
-			break;
+	/* Set up Advertisement register 3 first */
+	ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2);
+	if (ret < 0)
+		return ret;
 
 
-		case AMD_XGBE_AN_EVENT:
-			priv->an_state = amd_xgbe_an_event(phydev);
-			break;
+	if (phydev->supported & SUPPORTED_10000baseR_FEC)
+		ret |= 0xc000;
+	else
+		ret &= ~0xc000;
 
 
-		case AMD_XGBE_AN_PAGE_RECEIVED:
-			priv->an_state = amd_xgbe_an_page_received(phydev);
-			an_supported++;
-			break;
+	phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 2, ret);
 
 
-		case AMD_XGBE_AN_INCOMPAT_LINK:
-			priv->an_state = amd_xgbe_an_incompat_link(phydev);
-			break;
+	/* Set up Advertisement register 2 next */
+	ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1);
+	if (ret < 0)
+		return ret;
 
 
-		case AMD_XGBE_AN_COMPLETE:
-			priv->parallel_detect = an_supported ? 0 : 1;
-			netdev_info(phydev->attached_dev, "%s successful\n",
-				    an_supported ? "Auto negotiation"
-						 : "Parallel detection");
-			/* fall through */
+	if (phydev->supported & SUPPORTED_10000baseKR_Full)
+		ret |= 0x80;
+	else
+		ret &= ~0x80;
 
 
-		case AMD_XGBE_AN_NO_LINK:
-		case AMD_XGBE_AN_EXIT:
-			goto exit_unlock;
+	if ((phydev->supported & SUPPORTED_1000baseKX_Full) ||
+	    (phydev->supported & SUPPORTED_2500baseX_Full))
+		ret |= 0x20;
+	else
+		ret &= ~0x20;
 
 
-		default:
-			priv->an_state = AMD_XGBE_AN_ERROR;
-		}
+	phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE + 1, ret);
 
 
-		if (priv->an_state == AMD_XGBE_AN_ERROR) {
-			netdev_err(phydev->attached_dev,
-				   "error during auto-negotiation, state=%u\n",
-				   cur_state);
-			goto exit_unlock;
-		}
+	/* Set up Advertisement register 1 last */
+	ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE);
+	if (ret < 0)
+		return ret;
 
 
-		sleep = (priv->an_state == AMD_XGBE_AN_EVENT) ? 1 : 0;
+	if (phydev->supported & SUPPORTED_Pause)
+		ret |= 0x400;
+	else
+		ret &= ~0x400;
 
 
-		mutex_unlock(&priv->an_mutex);
+	if (phydev->supported & SUPPORTED_Asym_Pause)
+		ret |= 0x800;
+	else
+		ret &= ~0x800;
 
 
-		if (sleep)
-			usleep_range(20, 50);
-	}
+	/* We don't intend to perform XNP */
+	ret &= ~XNP_NP_EXCHANGE;
 
 
-exit_unlock:
-	priv->an_result = priv->an_state;
-	priv->an_state = AMD_XGBE_AN_READY;
+	phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_ADVERTISE, ret);
 
 
-	mutex_unlock(&priv->an_mutex);
+	return 0;
 }
 }
 
 
 static int amd_xgbe_phy_soft_reset(struct phy_device *phydev)
 static int amd_xgbe_phy_soft_reset(struct phy_device *phydev)
@@ -992,20 +1125,57 @@ static int amd_xgbe_phy_soft_reset(struct phy_device *phydev)
 	if (ret & MDIO_CTRL1_RESET)
 	if (ret & MDIO_CTRL1_RESET)
 		return -ETIMEDOUT;
 		return -ETIMEDOUT;
 
 
-	/* Make sure the XPCS and SerDes are in compatible states */
-	return amd_xgbe_phy_xgmii_mode(phydev);
+	/* Disable auto-negotiation for now */
+	ret = amd_xgbe_phy_disable_an(phydev);
+	if (ret < 0)
+		return ret;
+
+	/* Clear auto-negotiation interrupts */
+	phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0);
+
+	return 0;
 }
 }
 
 
 static int amd_xgbe_phy_config_init(struct phy_device *phydev)
 static int amd_xgbe_phy_config_init(struct phy_device *phydev)
 {
 {
 	struct amd_xgbe_phy_priv *priv = phydev->priv;
 	struct amd_xgbe_phy_priv *priv = phydev->priv;
+	struct net_device *netdev = phydev->attached_dev;
+	int ret;
+
+	if (!priv->an_irq_allocated) {
+		/* Allocate the auto-negotiation workqueue and interrupt */
+		snprintf(priv->an_irq_name, sizeof(priv->an_irq_name) - 1,
+			 "%s-pcs", netdev_name(netdev));
+
+		priv->an_workqueue =
+			create_singlethread_workqueue(priv->an_irq_name);
+		if (!priv->an_workqueue) {
+			netdev_err(netdev, "phy workqueue creation failed\n");
+			return -ENOMEM;
+		}
+
+		ret = devm_request_irq(priv->dev, priv->an_irq,
+				       amd_xgbe_an_isr, 0, priv->an_irq_name,
+				       priv);
+		if (ret) {
+			netdev_err(netdev, "phy irq request failed\n");
+			destroy_workqueue(priv->an_workqueue);
+			return ret;
+		}
+
+		priv->an_irq_allocated = 1;
+	}
+
+	ret = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_FEC_ABILITY);
+	if (ret < 0)
+		return ret;
+	priv->fec_ability = ret & XGBE_PHY_FEC_MASK;
 
 
 	/* Initialize supported features */
 	/* Initialize supported features */
 	phydev->supported = SUPPORTED_Autoneg;
 	phydev->supported = SUPPORTED_Autoneg;
 	phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
 	phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
 	phydev->supported |= SUPPORTED_Backplane;
 	phydev->supported |= SUPPORTED_Backplane;
-	phydev->supported |= SUPPORTED_10000baseKR_Full |
-			     SUPPORTED_10000baseR_FEC;
+	phydev->supported |= SUPPORTED_10000baseKR_Full;
 	switch (priv->speed_set) {
 	switch (priv->speed_set) {
 	case AMD_XGBE_PHY_SPEEDSET_1000_10000:
 	case AMD_XGBE_PHY_SPEEDSET_1000_10000:
 		phydev->supported |= SUPPORTED_1000baseKX_Full;
 		phydev->supported |= SUPPORTED_1000baseKX_Full;
@@ -1014,11 +1184,33 @@ static int amd_xgbe_phy_config_init(struct phy_device *phydev)
 		phydev->supported |= SUPPORTED_2500baseX_Full;
 		phydev->supported |= SUPPORTED_2500baseX_Full;
 		break;
 		break;
 	}
 	}
+
+	if (priv->fec_ability & XGBE_PHY_FEC_ENABLE)
+		phydev->supported |= SUPPORTED_10000baseR_FEC;
+
 	phydev->advertising = phydev->supported;
 	phydev->advertising = phydev->supported;
 
 
-	/* Turn off and clear interrupts */
-	phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INTMASK, 0);
-	phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0);
+	/* Set initial mode - call the mode setting routines
+	 * directly to insure we are properly configured
+	 */
+	if (phydev->supported & SUPPORTED_10000baseKR_Full)
+		ret = amd_xgbe_phy_xgmii_mode(phydev);
+	else if (phydev->supported & SUPPORTED_1000baseKX_Full)
+		ret = amd_xgbe_phy_gmii_mode(phydev);
+	else if (phydev->supported & SUPPORTED_2500baseX_Full)
+		ret = amd_xgbe_phy_gmii_2500_mode(phydev);
+	else
+		ret = -EINVAL;
+	if (ret < 0)
+		return ret;
+
+	/* Set up advertisement registers based on current settings */
+	ret = amd_xgbe_an_init(phydev);
+	if (ret)
+		return ret;
+
+	/* Enable auto-negotiation interrupts */
+	phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INTMASK, 0x07);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -1028,25 +1220,19 @@ static int amd_xgbe_phy_setup_forced(struct phy_device *phydev)
 	int ret;
 	int ret;
 
 
 	/* Disable auto-negotiation */
 	/* Disable auto-negotiation */
-	ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1);
+	ret = amd_xgbe_phy_disable_an(phydev);
 	if (ret < 0)
 	if (ret < 0)
 		return ret;
 		return ret;
 
 
-	ret &= ~MDIO_AN_CTRL1_ENABLE;
-	phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, ret);
-
 	/* Validate/Set specified speed */
 	/* Validate/Set specified speed */
 	switch (phydev->speed) {
 	switch (phydev->speed) {
 	case SPEED_10000:
 	case SPEED_10000:
-		ret = amd_xgbe_phy_xgmii_mode(phydev);
+		ret = amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KR);
 		break;
 		break;
 
 
 	case SPEED_2500:
 	case SPEED_2500:
-		ret = amd_xgbe_phy_gmii_2500_mode(phydev);
-		break;
-
 	case SPEED_1000:
 	case SPEED_1000:
-		ret = amd_xgbe_phy_gmii_mode(phydev);
+		ret = amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KX);
 		break;
 		break;
 
 
 	default:
 	default:
@@ -1066,10 +1252,11 @@ static int amd_xgbe_phy_setup_forced(struct phy_device *phydev)
 	return 0;
 	return 0;
 }
 }
 
 
-static int amd_xgbe_phy_config_aneg(struct phy_device *phydev)
+static int __amd_xgbe_phy_config_aneg(struct phy_device *phydev)
 {
 {
 	struct amd_xgbe_phy_priv *priv = phydev->priv;
 	struct amd_xgbe_phy_priv *priv = phydev->priv;
 	u32 mmd_mask = phydev->c45_ids.devices_in_package;
 	u32 mmd_mask = phydev->c45_ids.devices_in_package;
+	int ret;
 
 
 	if (phydev->autoneg != AUTONEG_ENABLE)
 	if (phydev->autoneg != AUTONEG_ENABLE)
 		return amd_xgbe_phy_setup_forced(phydev);
 		return amd_xgbe_phy_setup_forced(phydev);
@@ -1078,56 +1265,79 @@ static int amd_xgbe_phy_config_aneg(struct phy_device *phydev)
 	if (!(mmd_mask & MDIO_DEVS_AN))
 	if (!(mmd_mask & MDIO_DEVS_AN))
 		return -EINVAL;
 		return -EINVAL;
 
 
-	/* Start/Restart the auto-negotiation state machine */
-	mutex_lock(&priv->an_mutex);
+	/* Disable auto-negotiation interrupt */
+	disable_irq(priv->an_irq);
+
+	/* Start auto-negotiation in a supported mode */
+	if (phydev->supported & SUPPORTED_10000baseKR_Full)
+		ret = amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KR);
+	else if ((phydev->supported & SUPPORTED_1000baseKX_Full) ||
+		 (phydev->supported & SUPPORTED_2500baseX_Full))
+		ret = amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KX);
+	else
+		ret = -EINVAL;
+	if (ret < 0) {
+		enable_irq(priv->an_irq);
+		return ret;
+	}
+
+	/* Disable and stop any in progress auto-negotiation */
+	ret = amd_xgbe_phy_disable_an(phydev);
+	if (ret < 0)
+		return ret;
+
+	/* Clear any auto-negotitation interrupts */
+	phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_INT, 0);
+
 	priv->an_result = AMD_XGBE_AN_READY;
 	priv->an_result = AMD_XGBE_AN_READY;
-	priv->an_state = AMD_XGBE_AN_START;
-	priv->kr_state = AMD_XGBE_RX_READY;
-	priv->kx_state = AMD_XGBE_RX_READY;
-	mutex_unlock(&priv->an_mutex);
+	priv->an_state = AMD_XGBE_AN_READY;
+	priv->kr_state = AMD_XGBE_RX_BPA;
+	priv->kx_state = AMD_XGBE_RX_BPA;
 
 
-	queue_work(priv->an_workqueue, &priv->an_work);
+	/* Re-enable auto-negotiation interrupt */
+	enable_irq(priv->an_irq);
 
 
-	return 0;
+	/* Set up advertisement registers based on current settings */
+	ret = amd_xgbe_an_init(phydev);
+	if (ret)
+		return ret;
+
+	/* Enable and start auto-negotiation */
+	return amd_xgbe_phy_restart_an(phydev);
 }
 }
 
 
-static int amd_xgbe_phy_aneg_done(struct phy_device *phydev)
+static int amd_xgbe_phy_config_aneg(struct phy_device *phydev)
 {
 {
 	struct amd_xgbe_phy_priv *priv = phydev->priv;
 	struct amd_xgbe_phy_priv *priv = phydev->priv;
-	enum amd_xgbe_phy_an state;
+	int ret;
 
 
 	mutex_lock(&priv->an_mutex);
 	mutex_lock(&priv->an_mutex);
-	state = priv->an_result;
+
+	ret = __amd_xgbe_phy_config_aneg(phydev);
+
 	mutex_unlock(&priv->an_mutex);
 	mutex_unlock(&priv->an_mutex);
 
 
-	return (state == AMD_XGBE_AN_COMPLETE);
+	return ret;
+}
+
+static int amd_xgbe_phy_aneg_done(struct phy_device *phydev)
+{
+	struct amd_xgbe_phy_priv *priv = phydev->priv;
+
+	return (priv->an_result == AMD_XGBE_AN_COMPLETE);
 }
 }
 
 
 static int amd_xgbe_phy_update_link(struct phy_device *phydev)
 static int amd_xgbe_phy_update_link(struct phy_device *phydev)
 {
 {
 	struct amd_xgbe_phy_priv *priv = phydev->priv;
 	struct amd_xgbe_phy_priv *priv = phydev->priv;
-	enum amd_xgbe_phy_an state;
-	unsigned int check_again, autoneg;
 	int ret;
 	int ret;
 
 
 	/* If we're doing auto-negotiation don't report link down */
 	/* If we're doing auto-negotiation don't report link down */
-	mutex_lock(&priv->an_mutex);
-	state = priv->an_state;
-	mutex_unlock(&priv->an_mutex);
-
-	if (state != AMD_XGBE_AN_READY) {
+	if (priv->an_state != AMD_XGBE_AN_READY) {
 		phydev->link = 1;
 		phydev->link = 1;
 		return 0;
 		return 0;
 	}
 	}
 
 
-	/* Since the device can be in the wrong mode when a link is
-	 * (re-)established (cable connected after the interface is
-	 * up, etc.), the link status may report no link. If there
-	 * is no link, try switching modes and checking the status
-	 * again if auto negotiation is enabled.
-	 */
-	check_again = (phydev->autoneg == AUTONEG_ENABLE) ? 1 : 0;
-again:
 	/* Link status is latched low, so read once to clear
 	/* Link status is latched low, so read once to clear
 	 * and then read again to get current state
 	 * and then read again to get current state
 	 */
 	 */
@@ -1141,25 +1351,6 @@ again:
 
 
 	phydev->link = (ret & MDIO_STAT1_LSTATUS) ? 1 : 0;
 	phydev->link = (ret & MDIO_STAT1_LSTATUS) ? 1 : 0;
 
 
-	if (!phydev->link) {
-		if (check_again) {
-			ret = amd_xgbe_phy_switch_mode(phydev);
-			if (ret < 0)
-				return ret;
-			check_again = 0;
-			goto again;
-		}
-	}
-
-	autoneg = (phydev->link && !priv->link) ? 1 : 0;
-	priv->link = phydev->link;
-	if (autoneg) {
-		/* Link is (back) up, re-start auto-negotiation */
-		ret = amd_xgbe_phy_config_aneg(phydev);
-		if (ret < 0)
-			return ret;
-	}
-
 	return 0;
 	return 0;
 }
 }
 
 
@@ -1249,6 +1440,7 @@ static int amd_xgbe_phy_read_status(struct phy_device *phydev)
 
 
 static int amd_xgbe_phy_suspend(struct phy_device *phydev)
 static int amd_xgbe_phy_suspend(struct phy_device *phydev)
 {
 {
+	struct amd_xgbe_phy_priv *priv = phydev->priv;
 	int ret;
 	int ret;
 
 
 	mutex_lock(&phydev->lock);
 	mutex_lock(&phydev->lock);
@@ -1257,6 +1449,8 @@ static int amd_xgbe_phy_suspend(struct phy_device *phydev)
 	if (ret < 0)
 	if (ret < 0)
 		goto unlock;
 		goto unlock;
 
 
+	priv->lpm_ctrl = ret;
+
 	ret |= MDIO_CTRL1_LPOWER;
 	ret |= MDIO_CTRL1_LPOWER;
 	phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
 	phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
 
 
@@ -1270,69 +1464,106 @@ unlock:
 
 
 static int amd_xgbe_phy_resume(struct phy_device *phydev)
 static int amd_xgbe_phy_resume(struct phy_device *phydev)
 {
 {
-	int ret;
+	struct amd_xgbe_phy_priv *priv = phydev->priv;
 
 
 	mutex_lock(&phydev->lock);
 	mutex_lock(&phydev->lock);
 
 
-	ret = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1);
-	if (ret < 0)
-		goto unlock;
+	priv->lpm_ctrl &= ~MDIO_CTRL1_LPOWER;
+	phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, priv->lpm_ctrl);
 
 
-	ret &= ~MDIO_CTRL1_LPOWER;
-	phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
+	mutex_unlock(&phydev->lock);
 
 
-	ret = 0;
+	return 0;
+}
 
 
-unlock:
-	mutex_unlock(&phydev->lock);
+static unsigned int amd_xgbe_phy_resource_count(struct platform_device *pdev,
+						unsigned int type)
+{
+	unsigned int count;
+	int i;
 
 
-	return ret;
+	for (i = 0, count = 0; i < pdev->num_resources; i++) {
+		struct resource *r = &pdev->resource[i];
+
+		if (type == resource_type(r))
+			count++;
+	}
+
+	return count;
 }
 }
 
 
 static int amd_xgbe_phy_probe(struct phy_device *phydev)
 static int amd_xgbe_phy_probe(struct phy_device *phydev)
 {
 {
 	struct amd_xgbe_phy_priv *priv;
 	struct amd_xgbe_phy_priv *priv;
-	struct platform_device *pdev;
-	struct device *dev;
-	char *wq_name;
-	const __be32 *property;
-	unsigned int speed_set;
+	struct platform_device *phy_pdev;
+	struct device *dev, *phy_dev;
+	unsigned int phy_resnum, phy_irqnum;
 	int ret;
 	int ret;
 
 
-	if (!phydev->dev.of_node)
+	if (!phydev->bus || !phydev->bus->parent)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	pdev = of_find_device_by_node(phydev->dev.of_node);
-	if (!pdev)
-		return -EINVAL;
-	dev = &pdev->dev;
-
-	wq_name = kasprintf(GFP_KERNEL, "%s-amd-xgbe-phy", phydev->bus->name);
-	if (!wq_name) {
-		ret = -ENOMEM;
-		goto err_pdev;
-	}
+	dev = phydev->bus->parent;
 
 
 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
-	if (!priv) {
-		ret = -ENOMEM;
-		goto err_name;
-	}
+	if (!priv)
+		return -ENOMEM;
 
 
-	priv->pdev = pdev;
+	priv->pdev = to_platform_device(dev);
+	priv->adev = ACPI_COMPANION(dev);
 	priv->dev = dev;
 	priv->dev = dev;
 	priv->phydev = phydev;
 	priv->phydev = phydev;
+	mutex_init(&priv->an_mutex);
+	INIT_WORK(&priv->an_irq_work, amd_xgbe_an_irq_work);
+	INIT_WORK(&priv->an_work, amd_xgbe_an_state_machine);
+
+	if (!priv->adev || acpi_disabled) {
+		struct device_node *bus_node;
+		struct device_node *phy_node;
+
+		bus_node = priv->dev->of_node;
+		phy_node = of_parse_phandle(bus_node, "phy-handle", 0);
+		if (!phy_node) {
+			dev_err(dev, "unable to parse phy-handle\n");
+			ret = -EINVAL;
+			goto err_priv;
+		}
+
+		phy_pdev = of_find_device_by_node(phy_node);
+		of_node_put(phy_node);
+
+		if (!phy_pdev) {
+			dev_err(dev, "unable to obtain phy device\n");
+			ret = -EINVAL;
+			goto err_priv;
+		}
+
+		phy_resnum = 0;
+		phy_irqnum = 0;
+	} else {
+		/* In ACPI, the XGBE and PHY resources are the grouped
+		 * together with the PHY resources at the end
+		 */
+		phy_pdev = priv->pdev;
+		phy_resnum = amd_xgbe_phy_resource_count(phy_pdev,
+							 IORESOURCE_MEM) - 3;
+		phy_irqnum = amd_xgbe_phy_resource_count(phy_pdev,
+							 IORESOURCE_IRQ) - 1;
+	}
+	phy_dev = &phy_pdev->dev;
 
 
 	/* Get the device mmio areas */
 	/* Get the device mmio areas */
-	priv->rxtx_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->rxtx_res = platform_get_resource(phy_pdev, IORESOURCE_MEM,
+					       phy_resnum++);
 	priv->rxtx_regs = devm_ioremap_resource(dev, priv->rxtx_res);
 	priv->rxtx_regs = devm_ioremap_resource(dev, priv->rxtx_res);
 	if (IS_ERR(priv->rxtx_regs)) {
 	if (IS_ERR(priv->rxtx_regs)) {
 		dev_err(dev, "rxtx ioremap failed\n");
 		dev_err(dev, "rxtx ioremap failed\n");
 		ret = PTR_ERR(priv->rxtx_regs);
 		ret = PTR_ERR(priv->rxtx_regs);
-		goto err_priv;
+		goto err_put;
 	}
 	}
 
 
-	priv->sir0_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	priv->sir0_res = platform_get_resource(phy_pdev, IORESOURCE_MEM,
+					       phy_resnum++);
 	priv->sir0_regs = devm_ioremap_resource(dev, priv->sir0_res);
 	priv->sir0_regs = devm_ioremap_resource(dev, priv->sir0_res);
 	if (IS_ERR(priv->sir0_regs)) {
 	if (IS_ERR(priv->sir0_regs)) {
 		dev_err(dev, "sir0 ioremap failed\n");
 		dev_err(dev, "sir0 ioremap failed\n");
@@ -1340,7 +1571,8 @@ static int amd_xgbe_phy_probe(struct phy_device *phydev)
 		goto err_rxtx;
 		goto err_rxtx;
 	}
 	}
 
 
-	priv->sir1_res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+	priv->sir1_res = platform_get_resource(phy_pdev, IORESOURCE_MEM,
+					       phy_resnum++);
 	priv->sir1_regs = devm_ioremap_resource(dev, priv->sir1_res);
 	priv->sir1_regs = devm_ioremap_resource(dev, priv->sir1_res);
 	if (IS_ERR(priv->sir1_regs)) {
 	if (IS_ERR(priv->sir1_regs)) {
 		dev_err(dev, "sir1 ioremap failed\n");
 		dev_err(dev, "sir1 ioremap failed\n");
@@ -1348,40 +1580,98 @@ static int amd_xgbe_phy_probe(struct phy_device *phydev)
 		goto err_sir0;
 		goto err_sir0;
 	}
 	}
 
 
+	/* Get the auto-negotiation interrupt */
+	ret = platform_get_irq(phy_pdev, phy_irqnum);
+	if (ret < 0) {
+		dev_err(dev, "platform_get_irq failed\n");
+		goto err_sir1;
+	}
+	priv->an_irq = ret;
+
 	/* Get the device speed set property */
 	/* Get the device speed set property */
-	speed_set = 0;
-	property = of_get_property(dev->of_node, XGBE_PHY_SPEEDSET_PROPERTY,
-				   NULL);
-	if (property)
-		speed_set = be32_to_cpu(*property);
-
-	switch (speed_set) {
-	case 0:
-		priv->speed_set = AMD_XGBE_PHY_SPEEDSET_1000_10000;
-		break;
-	case 1:
-		priv->speed_set = AMD_XGBE_PHY_SPEEDSET_2500_10000;
+	ret = device_property_read_u32(phy_dev, XGBE_PHY_SPEEDSET_PROPERTY,
+				       &priv->speed_set);
+	if (ret) {
+		dev_err(dev, "invalid %s property\n",
+			XGBE_PHY_SPEEDSET_PROPERTY);
+		goto err_sir1;
+	}
+
+	switch (priv->speed_set) {
+	case AMD_XGBE_PHY_SPEEDSET_1000_10000:
+	case AMD_XGBE_PHY_SPEEDSET_2500_10000:
 		break;
 		break;
 	default:
 	default:
-		dev_err(dev, "invalid amd,speed-set property\n");
+		dev_err(dev, "invalid %s property\n",
+			XGBE_PHY_SPEEDSET_PROPERTY);
 		ret = -EINVAL;
 		ret = -EINVAL;
 		goto err_sir1;
 		goto err_sir1;
 	}
 	}
 
 
-	priv->link = 1;
+	if (device_property_present(phy_dev, XGBE_PHY_BLWC_PROPERTY)) {
+		ret = device_property_read_u32_array(phy_dev,
+						     XGBE_PHY_BLWC_PROPERTY,
+						     priv->serdes_blwc,
+						     XGBE_PHY_SPEEDS);
+		if (ret) {
+			dev_err(dev, "invalid %s property\n",
+				XGBE_PHY_BLWC_PROPERTY);
+			goto err_sir1;
+		}
+	} else {
+		memcpy(priv->serdes_blwc, amd_xgbe_phy_serdes_blwc,
+		       sizeof(priv->serdes_blwc));
+	}
 
 
-	mutex_init(&priv->an_mutex);
-	INIT_WORK(&priv->an_work, amd_xgbe_an_state_machine);
-	priv->an_workqueue = create_singlethread_workqueue(wq_name);
-	if (!priv->an_workqueue) {
-		ret = -ENOMEM;
-		goto err_sir1;
+	if (device_property_present(phy_dev, XGBE_PHY_CDR_RATE_PROPERTY)) {
+		ret = device_property_read_u32_array(phy_dev,
+						     XGBE_PHY_CDR_RATE_PROPERTY,
+						     priv->serdes_cdr_rate,
+						     XGBE_PHY_SPEEDS);
+		if (ret) {
+			dev_err(dev, "invalid %s property\n",
+				XGBE_PHY_CDR_RATE_PROPERTY);
+			goto err_sir1;
+		}
+	} else {
+		memcpy(priv->serdes_cdr_rate, amd_xgbe_phy_serdes_cdr_rate,
+		       sizeof(priv->serdes_cdr_rate));
+	}
+
+	if (device_property_present(phy_dev, XGBE_PHY_PQ_SKEW_PROPERTY)) {
+		ret = device_property_read_u32_array(phy_dev,
+						     XGBE_PHY_PQ_SKEW_PROPERTY,
+						     priv->serdes_pq_skew,
+						     XGBE_PHY_SPEEDS);
+		if (ret) {
+			dev_err(dev, "invalid %s property\n",
+				XGBE_PHY_PQ_SKEW_PROPERTY);
+			goto err_sir1;
+		}
+	} else {
+		memcpy(priv->serdes_pq_skew, amd_xgbe_phy_serdes_pq_skew,
+		       sizeof(priv->serdes_pq_skew));
+	}
+
+	if (device_property_present(phy_dev, XGBE_PHY_TX_AMP_PROPERTY)) {
+		ret = device_property_read_u32_array(phy_dev,
+						     XGBE_PHY_TX_AMP_PROPERTY,
+						     priv->serdes_tx_amp,
+						     XGBE_PHY_SPEEDS);
+		if (ret) {
+			dev_err(dev, "invalid %s property\n",
+				XGBE_PHY_TX_AMP_PROPERTY);
+			goto err_sir1;
+		}
+	} else {
+		memcpy(priv->serdes_tx_amp, amd_xgbe_phy_serdes_tx_amp,
+		       sizeof(priv->serdes_tx_amp));
 	}
 	}
 
 
 	phydev->priv = priv;
 	phydev->priv = priv;
 
 
-	kfree(wq_name);
-	of_dev_put(pdev);
+	if (!priv->adev || acpi_disabled)
+		platform_device_put(phy_pdev);
 
 
 	return 0;
 	return 0;
 
 
@@ -1400,15 +1690,13 @@ err_rxtx:
 	devm_release_mem_region(dev, priv->rxtx_res->start,
 	devm_release_mem_region(dev, priv->rxtx_res->start,
 				resource_size(priv->rxtx_res));
 				resource_size(priv->rxtx_res));
 
 
+err_put:
+	if (!priv->adev || acpi_disabled)
+		platform_device_put(phy_pdev);
+
 err_priv:
 err_priv:
 	devm_kfree(dev, priv);
 	devm_kfree(dev, priv);
 
 
-err_name:
-	kfree(wq_name);
-
-err_pdev:
-	of_dev_put(pdev);
-
 	return ret;
 	return ret;
 }
 }
 
 
@@ -1417,13 +1705,12 @@ static void amd_xgbe_phy_remove(struct phy_device *phydev)
 	struct amd_xgbe_phy_priv *priv = phydev->priv;
 	struct amd_xgbe_phy_priv *priv = phydev->priv;
 	struct device *dev = priv->dev;
 	struct device *dev = priv->dev;
 
 
-	/* Stop any in process auto-negotiation */
-	mutex_lock(&priv->an_mutex);
-	priv->an_state = AMD_XGBE_AN_EXIT;
-	mutex_unlock(&priv->an_mutex);
+	if (priv->an_irq_allocated) {
+		devm_free_irq(dev, priv->an_irq, priv);
 
 
-	flush_workqueue(priv->an_workqueue);
-	destroy_workqueue(priv->an_workqueue);
+		flush_workqueue(priv->an_workqueue);
+		destroy_workqueue(priv->an_workqueue);
+	}
 
 
 	/* Release resources */
 	/* Release resources */
 	devm_iounmap(dev, priv->sir1_regs);
 	devm_iounmap(dev, priv->sir1_regs);