Commit 93d62ef9 authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 919d9db9 8fdb1a09
......@@ -7,17 +7,38 @@ Required properties:
- SerDes Rx/Tx registers
- SerDes integration registers (1/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:
- amd,speed-set: Speed capabilities of the device
0 - 1GbE and 10GbE (default)
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:
xgbe_phy@e1240800 {
compatible = "amd,xgbe-phy-seattle-v1a", "ethernet-phy-ieee802.3-c45";
reg = <0 0xe1240800 0 0x00400>,
<0 0xe1250000 0 0x00060>,
<0 0xe1250080 0 0x00004>;
interrupt-parent = <&gic>;
interrupts = <0 323 4>;
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>;
};
......@@ -179,7 +179,7 @@ config SUNLANCE
config AMD_XGBE
tristate "AMD 10GbE Ethernet driver"
depends on OF_NET && HAS_IOMEM
depends on (OF_NET || ACPI) && HAS_IOMEM
select PHYLIB
select AMD_XGBE_PHY
select BITREVERSE
......
......@@ -328,7 +328,7 @@ void xgbe_debugfs_init(struct xgbe_prv_data *pdata)
buf = kasprintf(GFP_KERNEL, "amd-xgbe-%s", pdata->netdev->name);
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");
return;
}
......
......@@ -422,7 +422,6 @@ static void xgbe_wrapper_rx_descriptor_init(struct xgbe_prv_data *pdata)
ring->cur = 0;
ring->dirty = 0;
memset(&ring->rx, 0, sizeof(ring->rx));
hw_if->rx_desc_init(channel);
}
......@@ -621,35 +620,6 @@ static int xgbe_map_tx_skb(struct xgbe_channel *channel, struct sk_buff *skb)
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)
{
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->free_ring_resources = xgbe_free_ring_resources;
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->wrapper_tx_desc_init = xgbe_wrapper_tx_descriptor_init;
desc_if->wrapper_rx_desc_init = xgbe_wrapper_rx_descriptor_init;
......
......@@ -115,6 +115,7 @@
*/
#include <linux/phy.h>
#include <linux/mdio.h>
#include <linux/clk.h>
#include <linux/bitrev.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");
rate = clk_get_rate(pdata->sysclk);
rate = pdata->sysclk_rate;
/*
* 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");
rate = clk_get_rate(pdata->sysclk);
rate = pdata->sysclk_rate;
/*
* 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)
{
if (XGMAC_IOREAD_BITS(pdata, MAC_TCR, SS) == 0x3)
return 0;
XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, 0x3);
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)
{
if (XGMAC_IOREAD_BITS(pdata, MAC_TCR, SS) == 0x2)
return 0;
XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, 0x2);
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)
{
if (XGMAC_IOREAD_BITS(pdata, MAC_TCR, SS) == 0)
return 0;
XGMAC_IOWRITE_BITS(pdata, MAC_TCR, SS, 0);
return 0;
......@@ -881,6 +891,23 @@ static void xgbe_write_mmd_regs(struct xgbe_prv_data *pdata, int prtad,
else
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
* management interface uses indirect addressing to access the MMD
* 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 tx_set_ic;
int start_index = ring->cur;
int cur_index = ring->cur;
int i;
DBGPR("-->xgbe_dev_xmit\n");
......@@ -1401,7 +1429,7 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel)
else
tx_set_ic = 0;
rdata = XGBE_GET_DESC_DATA(ring, ring->cur);
rdata = XGBE_GET_DESC_DATA(ring, cur_index);
rdesc = rdata->rdesc;
/* 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->cur++;
rdata = XGBE_GET_DESC_DATA(ring, ring->cur);
cur_index++;
rdata = XGBE_GET_DESC_DATA(ring, cur_index);
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);
/* 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);
if (tso) {
......@@ -1497,9 +1525,9 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel)
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;
/* Update buffer address */
......@@ -1551,7 +1579,7 @@ static void xgbe_dev_xmit(struct xgbe_channel *channel)
/* Make sure ownership is written to the descriptor */
wmb();
ring->cur++;
ring->cur = cur_index + 1;
if (!packet->skb->xmit_more ||
netif_xmit_stopped(netdev_get_tx_queue(pdata->netdev,
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);
}
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)
{
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_jumbo_enable(pdata);
xgbe_config_flow_control(pdata);
xgbe_config_mac_speed(pdata);
xgbe_config_checksum_offload(pdata);
xgbe_config_vlan_support(pdata);
xgbe_config_mmc(pdata);
......
......@@ -225,6 +225,11 @@ static inline unsigned int xgbe_tx_avail_desc(struct xgbe_ring *ring)
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,
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,
tx_timer);
struct xgbe_ring *ring = channel->tx_ring;
struct xgbe_prv_data *pdata = channel->pdata;
struct napi_struct *napi;
unsigned long flags;
DBGPR("-->xgbe_tx_timer\n");
napi = (pdata->per_channel_irq) ? &channel->napi : &pdata->napi;
spin_lock_irqsave(&ring->lock, flags);
if (napi_schedule_prep(napi)) {
/* Disable Tx and Rx interrupts */
if (pdata->per_channel_irq)
......@@ -434,8 +435,6 @@ static enum hrtimer_restart xgbe_tx_timer(struct hrtimer *timer)
channel->tx_timer_active = 0;
spin_unlock_irqrestore(&ring->lock, flags);
DBGPR("<--xgbe_tx_timer\n");
return HRTIMER_NORESTART;
......@@ -692,7 +691,7 @@ static void xgbe_adjust_link(struct net_device *netdev)
struct phy_device *phydev = pdata->phydev;
int new_state = 0;
if (phydev == NULL)
if (!phydev)
return;
if (phydev->link) {
......@@ -927,7 +926,7 @@ static void xgbe_stop(struct xgbe_prv_data *pdata)
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_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_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);
......@@ -967,7 +965,7 @@ static void xgbe_restart(struct work_struct *work)
rtnl_lock();
xgbe_restart_dev(pdata, 1);
xgbe_restart_dev(pdata);
rtnl_unlock();
}
......@@ -1446,7 +1444,6 @@ static int xgbe_xmit(struct sk_buff *skb, struct net_device *netdev)
struct xgbe_ring *ring;
struct xgbe_packet_data *packet;
struct netdev_queue *txq;
unsigned long flags;
int ret;
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;
spin_lock_irqsave(&ring->lock, flags);
if (skb->len == 0) {
netdev_err(netdev, "empty skb received from stack\n");
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;
tx_netdev_return:
spin_unlock_irqrestore(&ring->lock, flags);
DBGPR("<--xgbe_xmit\n");
return ret;
}
......@@ -1587,7 +1578,7 @@ static int xgbe_change_mtu(struct net_device *netdev, int mtu)
pdata->rx_buf_size = ret;
netdev->mtu = mtu;
xgbe_restart_dev(pdata, 0);
xgbe_restart_dev(pdata);
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)
{
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_ring *ring = channel->rx_ring;
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
* 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,
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 net_device *netdev = pdata->netdev;
struct netdev_queue *txq;
unsigned long flags;
int processed = 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);
spin_lock_irqsave(&ring->lock, flags);
while ((processed < XGBE_TX_DESC_MAX_PROC) &&
(ring->dirty != ring->cur)) {
rdata = XGBE_GET_DESC_DATA(ring, ring->dirty);
......@@ -1868,7 +1869,7 @@ static int xgbe_tx_poll(struct xgbe_channel *channel)
}
if (!processed)
goto unlock;
return 0;
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);
unlock:
spin_unlock_irqrestore(&ring->lock, flags);
return processed;
}
......@@ -1934,7 +1932,7 @@ static int xgbe_rx_poll(struct xgbe_channel *channel, int budget)
read_again:
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);
if (hw_if->dev_read(channel))
......@@ -1942,7 +1940,6 @@ static int xgbe_rx_poll(struct xgbe_channel *channel, int budget)
received++;
ring->cur++;
ring->dirty++;
incomplete = XGMAC_GET_BITS(packet->attributes,
RX_PACKET_ATTRIBUTES,
......
......@@ -123,7 +123,10 @@
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_net.h>
#include <linux/of_address.h>
#include <linux/clk.h>
#include <linux/property.h>
#include <linux/acpi.h>
#include "xgbe.h"
#include "xgbe-common.h"
......@@ -148,6 +151,7 @@ static void xgbe_default_config(struct xgbe_prv_data *pdata)
pdata->pause_autoneg = 1;
pdata->tx_pause = 1;
pdata->rx_pause = 1;
pdata->phy_speed = SPEED_UNKNOWN;
pdata->power_down = 0;
pdata->default_autoneg = AUTONEG_ENABLE;
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);
}
#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)
{
struct xgbe_prv_data *pdata;
......@@ -169,7 +263,7 @@ static int xgbe_probe(struct platform_device *pdev)
struct net_device *netdev;
struct device *dev = &pdev->dev;
struct resource *res;
const u8 *mac_addr;
const char *phy_mode;
unsigned int i;
int ret;
......@@ -186,6 +280,7 @@ static int xgbe_probe(struct platform_device *pdev)
pdata = netdev_priv(netdev);
pdata->netdev = netdev;
pdata->pdev = pdev;
pdata->adev = ACPI_COMPANION(dev);
pdata->dev = dev;
platform_set_drvdata(pdev, netdev);
......@@ -194,6 +289,9 @@ static int xgbe_probe(struct platform_device *pdev)
mutex_init(&pdata->rss_mutex);
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 */
BUILD_BUG_ON_NOT_POWER_OF_2(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;
}
/* 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 */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
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);
/* 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;
}
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->arcache = XGBE_DMA_OS_ARCACHE;
pdata->awcache = XGBE_DMA_OS_AWCACHE;
......@@ -266,10 +374,16 @@ static int xgbe_probe(struct platform_device *pdev)
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);
if (ret < 0) {
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->base_addr = (unsigned long)pdata->xgmac_regs;
memcpy(netdev->dev_addr, pdata->mac_addr, netdev->addr_len);
/* Set all the function pointers */
xgbe_init_all_fptrs(pdata);
......@@ -291,23 +406,6 @@ static int xgbe_probe(struct platform_device *pdev)
/* Populate the hardware features */
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 */
xgbe_default_config(pdata);
......@@ -491,18 +589,35 @@ static int xgbe_resume(struct device *dev)
}
#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[] = {
{ .compatible = "amd,xgbe-seattle-v1a", },
{},
};
MODULE_DEVICE_TABLE(of, xgbe_of_match);
#endif
static SIMPLE_DEV_PM_OPS(xgbe_pm_ops, xgbe_suspend, xgbe_resume);
static struct platform_driver xgbe_driver = {
.driver = {
.name = "amd-xgbe",
#ifdef CONFIG_ACPI
.acpi_match_table = xgbe_acpi_match,
#endif
#ifdef CONFIG_OF
.of_match_table = xgbe_of_match,
#endif
.pm = &xgbe_pm_ops,
},
.probe = xgbe_probe,
......
......@@ -205,25 +205,16 @@ void xgbe_dump_phy_registers(struct xgbe_prv_data *pdata)
int xgbe_mdio_register(struct xgbe_prv_data *pdata)
{
struct device_node *phy_node;
struct mii_bus *mii;
struct phy_device *phydev;
int ret = 0;
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();
if (mii == NULL) {
if (!mii) {
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) */
......@@ -252,18 +243,19 @@ int xgbe_mdio_register(struct xgbe_prv_data *pdata)
request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT,
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);
if (ret) {
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;
}
/* 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)) {
dev_err(pdata->dev, "try_module_get failed\n");
ret = -EIO;
......@@ -283,8 +275,6 @@ int xgbe_mdio_register(struct xgbe_prv_data *pdata)
pdata->phydev = phydev;
of_node_put(phy_node);
DBGPHY_REGS(pdata);
DBGPR("<--xgbe_mdio_register\n");
......@@ -300,9 +290,6 @@ int xgbe_mdio_register(struct xgbe_prv_data *pdata)
err_mdiobus_alloc:
mdiobus_free(mii);
err_node_get:
of_node_put(phy_node);
return ret;
}
......
......@@ -233,7 +233,7 @@ void xgbe_ptp_register(struct xgbe_prv_data *pdata)
snprintf(info->name, sizeof(info->name), "%s",
netdev_name(pdata->netdev));
info->owner = THIS_MODULE;
info->max_adj = clk_get_rate(pdata->ptpclk);
info->max_adj = pdata->ptpclk_rate;
info->adjfreq = xgbe_adjfreq;
info->adjtime = xgbe_adjtime;
info->gettime = xgbe_gettime;
......@@ -254,7 +254,7 @@ void xgbe_ptp_register(struct xgbe_prv_data *pdata)
*/
dividend = 50000000;
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 */
cc->read = xgbe_cc_read;
......
......@@ -182,10 +182,18 @@
#define XGBE_PHY_NAME "amd_xgbe_phy"
#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 */
#define XGBE_DMA_CLOCK "dma_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
* 50MHz => 20 nsec
......@@ -361,8 +369,7 @@ struct xgbe_ring {
* cur - Tx: index of descriptor to be used for current transfer
* Rx: index of descriptor to check for packet availability
* 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 dirty;
......@@ -377,11 +384,6 @@ struct xgbe_ring {
unsigned short cur_mss;
unsigned short cur_vlan_ctag;
} tx;
struct {
unsigned int realloc_index;
unsigned int realloc_threshold;
} rx;
};
} ____cacheline_aligned;
......@@ -596,7 +598,8 @@ struct xgbe_desc_if {
int (*alloc_ring_resources)(struct xgbe_prv_data *);
void (*free_ring_resources)(struct xgbe_prv_data *);
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 (*wrapper_tx_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 net_device *netdev;
struct platform_device *pdev;
struct acpi_device *adev;
struct device *dev;
/* ACPI or DT flag */
unsigned int use_acpi;
/* XGMAC/XPCS related mmio registers */
void __iomem *xgmac_regs; /* XGMAC CSRs */
void __iomem *xpcs_regs; /* XPCS MMD registers */
......@@ -672,6 +679,7 @@ struct xgbe_prv_data {
struct xgbe_desc_if desc_if;
/* AXI DMA settings */
unsigned int coherent;
unsigned int axdomain;
unsigned int arcache;
unsigned int awcache;
......@@ -739,6 +747,7 @@ struct xgbe_prv_data {
unsigned int phy_rx_pause;
/* Netdev related settings */
unsigned char mac_addr[ETH_ALEN];
netdev_features_t netdev_features;
struct napi_struct napi;
struct xgbe_mmc_stats mmc_stats;
......@@ -748,7 +757,9 @@ struct xgbe_prv_data {
/* Device clocks */
struct clk *sysclk;
unsigned long sysclk_rate;
struct clk *ptpclk;
unsigned long ptpclk_rate;
/* Timestamp support */
spinlock_t tstamp_lock;
......
......@@ -26,7 +26,7 @@ config AMD_PHY
config AMD_XGBE_PHY
tristate "Driver for the AMD 10GbE (amd-xgbe) PHYs"
depends on OF && HAS_IOMEM
depends on (OF || ACPI) && HAS_IOMEM
---help---
Currently supports the AMD 10GbE PHY
......
......@@ -60,6 +60,7 @@
#include <linux/interrupt.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/workqueue.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
......@@ -74,6 +75,9 @@
#include <linux/of_platform.h>
#include <linux/of_device.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_LICENSE("Dual BSD/GPL");
......@@ -84,22 +88,43 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver");
#define XGBE_PHY_MASK 0xfffffff0
#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_INC_LINK 0x02
#define XGBE_AN_PG_RCV 0x04
#define XGBE_AN_INT_MASK 0x07
#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_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
#define MDIO_PMA_10GBR_PMD_CTRL 0x0096
#endif
#ifndef MDIO_PMA_10GBR_FEC_ABILITY
#define MDIO_PMA_10GBR_FEC_ABILITY 0x00aa
#endif
#ifndef MDIO_PMA_10GBR_FEC_CTRL
#define MDIO_PMA_10GBR_FEC_CTRL 0x00ab
#endif
......@@ -108,6 +133,10 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver");
#define MDIO_AN_XNP 0x0016
#endif
#ifndef MDIO_AN_LPX
#define MDIO_AN_LPX 0x0019
#endif
#ifndef MDIO_AN_INTMASK
#define MDIO_AN_INTMASK 0x8001
#endif
......@@ -116,18 +145,10 @@ MODULE_DESCRIPTION("AMD 10GbE (amd-xgbe) PHY driver");
#define MDIO_AN_INT 0x8002
#endif
#ifndef MDIO_AN_KR_CTRL
#define MDIO_AN_KR_CTRL 0x8003
#endif
#ifndef MDIO_CTRL1_SPEED1G
#define MDIO_CTRL1_SPEED1G (MDIO_CTRL1_SPEED10G & ~BMCR_SPEED100)
#endif
#ifndef MDIO_KR_CTRL_PDETECT
#define MDIO_KR_CTRL_PDETECT 0x01
#endif
/* SerDes integration register offsets */
#define SIR0_KR_RT_1 0x002c
#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_TX_READY_INDEX 8
#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_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_WIDTH 1
#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_WIDTH 3
#define SPEED_10000_BLWC 0
#define SPEED_10000_CDR 0x7
#define SPEED_10000_PLL 0x1
#define SPEED_10000_PQ 0x1e
#define SPEED_10000_RATE 0x0
#define SPEED_10000_TXAMP 0xa
#define SPEED_10000_WORD 0x7
#define SPEED_2500_BLWC 1
#define SPEED_2500_CDR 0x2
#define SPEED_2500_PLL 0x0
#define SPEED_2500_PQ 0xa
#define SPEED_2500_RATE 0x1
#define SPEED_2500_TXAMP 0xf
#define SPEED_2500_WORD 0x1
#define SPEED_1000_BLWC 1
#define SPEED_1000_CDR 0x2
#define SPEED_1000_PLL 0x0
#define SPEED_1000_PQ 0xa
#define SPEED_1000_RATE 0x3
#define SPEED_1000_TXAMP 0xf
#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_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
* The get macro will extract the current bit field value from within
* the variable
......@@ -291,23 +309,44 @@ do { \
XRXTX_IOWRITE((_priv), _reg, reg_val); \
} 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 {
AMD_XGBE_AN_READY = 0,
AMD_XGBE_AN_START,
AMD_XGBE_AN_EVENT,
AMD_XGBE_AN_PAGE_RECEIVED,
AMD_XGBE_AN_INCOMPAT_LINK,
AMD_XGBE_AN_COMPLETE,
AMD_XGBE_AN_NO_LINK,
AMD_XGBE_AN_EXIT,
AMD_XGBE_AN_ERROR,
};
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_COMPLETE,
AMD_XGBE_RX_ERROR,
};
enum amd_xgbe_phy_mode {
......@@ -316,12 +355,13 @@ enum amd_xgbe_phy_mode {
};
enum amd_xgbe_phy_speedset {
AMD_XGBE_PHY_SPEEDSET_1000_10000,
AMD_XGBE_PHY_SPEEDSET_1000_10000 = 0,
AMD_XGBE_PHY_SPEEDSET_2500_10000,
};
struct amd_xgbe_phy_priv {
struct platform_device *pdev;
struct acpi_device *adev;
struct device *dev;
struct phy_device *phydev;
......@@ -336,10 +376,24 @@ struct amd_xgbe_phy_priv {
void __iomem *sir0_regs; /* SerDes integration registers (1/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;
/* 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 */
struct mutex an_mutex;
enum amd_xgbe_phy_an an_result;
......@@ -348,7 +402,11 @@ struct amd_xgbe_phy_priv {
enum amd_xgbe_phy_rx kx_state;
struct work_struct an_work;
struct workqueue_struct *an_workqueue;
unsigned int an_supported;
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)
......@@ -359,7 +417,7 @@ static int amd_xgbe_an_enable_kr_training(struct phy_device *phydev)
if (ret < 0)
return ret;
ret |= 0x02;
ret |= XGBE_PHY_KR_TRAINING_ENABLE;
phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, ret);
return 0;
......@@ -373,7 +431,7 @@ static int amd_xgbe_an_disable_kr_training(struct phy_device *phydev)
if (ret < 0)
return ret;
ret &= ~0x02;
ret &= ~XGBE_PHY_KR_TRAINING_ENABLE;
phy_write_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10GBR_PMD_CTRL, ret);
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, 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, 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);
......@@ -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, 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, 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);
......@@ -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, 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, 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);
......@@ -635,6 +705,38 @@ static int amd_xgbe_phy_set_mode(struct phy_device *phydev,
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,
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 (!amd_xgbe_phy_in_kr_mode(phydev))
return AMD_XGBE_AN_EVENT;
return AMD_XGBE_AN_PAGE_RECEIVED;
/* Enable/Disable FEC */
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)
return AMD_XGBE_AN_ERROR;
ret &= ~XGBE_PHY_FEC_MASK;
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);
......@@ -672,14 +773,17 @@ static enum amd_xgbe_phy_an amd_xgbe_an_tx_training(struct phy_device *phydev,
if (ret < 0)
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,
......@@ -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, 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,
......@@ -735,11 +839,11 @@ static enum amd_xgbe_phy_an amd_xgbe_an_rx_xnp(struct phy_device *phydev,
int ad_reg, lp_reg;
/* 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)
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)
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);
}
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;
int ret;
/* Be sure we aren't looping trying to negotiate */
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;
priv->kr_state = AMD_XGBE_RX_BPA;
} 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;
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;
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;
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;
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;
case AMD_XGBE_RX_XNP:
ret = amd_xgbe_an_rx_xnp(phydev, state);
case AMD_XGBE_AN_NO_LINK:
break;
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)
......@@ -992,20 +1125,57 @@ static int amd_xgbe_phy_soft_reset(struct phy_device *phydev)
if (ret & MDIO_CTRL1_RESET)
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)
{
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 */
phydev->supported = SUPPORTED_Autoneg;
phydev->supported |= SUPPORTED_Pause | SUPPORTED_Asym_Pause;
phydev->supported |= SUPPORTED_Backplane;
phydev->supported |= SUPPORTED_10000baseKR_Full |
SUPPORTED_10000baseR_FEC;
phydev->supported |= SUPPORTED_10000baseKR_Full;
switch (priv->speed_set) {
case AMD_XGBE_PHY_SPEEDSET_1000_10000:
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;
break;
}
if (priv->fec_ability & XGBE_PHY_FEC_ENABLE)
phydev->supported |= SUPPORTED_10000baseR_FEC;
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;
}
......@@ -1028,25 +1220,19 @@ static int amd_xgbe_phy_setup_forced(struct phy_device *phydev)
int ret;
/* Disable auto-negotiation */
ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1);
ret = amd_xgbe_phy_disable_an(phydev);
if (ret < 0)
return ret;
ret &= ~MDIO_AN_CTRL1_ENABLE;
phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_CTRL1, ret);
/* Validate/Set specified speed */
switch (phydev->speed) {
case SPEED_10000:
ret = amd_xgbe_phy_xgmii_mode(phydev);
ret = amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KR);
break;
case SPEED_2500:
ret = amd_xgbe_phy_gmii_2500_mode(phydev);
break;
case SPEED_1000:
ret = amd_xgbe_phy_gmii_mode(phydev);
ret = amd_xgbe_phy_set_mode(phydev, AMD_XGBE_MODE_KX);
break;
default:
......@@ -1066,10 +1252,11 @@ static int amd_xgbe_phy_setup_forced(struct phy_device *phydev)
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;
u32 mmd_mask = phydev->c45_ids.devices_in_package;
int ret;
if (phydev->autoneg != AUTONEG_ENABLE)
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))
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_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;
enum amd_xgbe_phy_an state;
int ret;
mutex_lock(&priv->an_mutex);
state = priv->an_result;
ret = __amd_xgbe_phy_config_aneg(phydev);
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)
{
struct amd_xgbe_phy_priv *priv = phydev->priv;
enum amd_xgbe_phy_an state;
unsigned int check_again, autoneg;
int ret;
/* 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;
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
* and then read again to get current state
*/
......@@ -1141,25 +1351,6 @@ static int amd_xgbe_phy_update_link(struct phy_device *phydev)
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;
}
......@@ -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)
{
struct amd_xgbe_phy_priv *priv = phydev->priv;
int ret;
mutex_lock(&phydev->lock);
......@@ -1257,6 +1449,8 @@ static int amd_xgbe_phy_suspend(struct phy_device *phydev)
if (ret < 0)
goto unlock;
priv->lpm_ctrl = ret;
ret |= MDIO_CTRL1_LPOWER;
phy_write_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1, ret);
......@@ -1270,69 +1464,106 @@ static int amd_xgbe_phy_suspend(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);
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)
{
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;
if (!phydev->dev.of_node)
if (!phydev->bus || !phydev->bus->parent)
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);
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->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 */
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);
if (IS_ERR(priv->rxtx_regs)) {
dev_err(dev, "rxtx ioremap failed\n");
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);
if (IS_ERR(priv->sir0_regs)) {
dev_err(dev, "sir0 ioremap failed\n");
......@@ -1340,7 +1571,8 @@ static int amd_xgbe_phy_probe(struct phy_device *phydev)
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);
if (IS_ERR(priv->sir1_regs)) {
dev_err(dev, "sir1 ioremap failed\n");
......@@ -1348,40 +1580,98 @@ static int amd_xgbe_phy_probe(struct phy_device *phydev)
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 */
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;
default:
dev_err(dev, "invalid amd,speed-set property\n");
dev_err(dev, "invalid %s property\n",
XGBE_PHY_SPEEDSET_PROPERTY);
ret = -EINVAL;
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;
kfree(wq_name);
of_dev_put(pdev);
if (!priv->adev || acpi_disabled)
platform_device_put(phy_pdev);
return 0;
......@@ -1400,15 +1690,13 @@ static int amd_xgbe_phy_probe(struct phy_device *phydev)
devm_release_mem_region(dev, priv->rxtx_res->start,
resource_size(priv->rxtx_res));
err_put:
if (!priv->adev || acpi_disabled)
platform_device_put(phy_pdev);
err_priv:
devm_kfree(dev, priv);
err_name:
kfree(wq_name);
err_pdev:
of_dev_put(pdev);
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 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 */
devm_iounmap(dev, priv->sir1_regs);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment