Commit 540c86f3 authored by David S. Miller's avatar David S. Miller

Merge branch 'ftgmac100-rework-batch-1-Link-and-Interrupts'

Benjamin Herrenschmidt says:

====================
ftgmac100: Rework batch 1 - Link & Interrupts

This is version 2 of the first batch of updates to the
ftgmac100 driver.

Essentially:

 - A few misc cleanups
 - Fixing link speed & duplex handling (including dealing with
   an Aspeed requirement to double reset the controller when
   the speed changes)
 - And addition of a reset task workqueue which will be used
   for delaying the re-initialization of the controller
 - Fixing a number of issues with how interrupts and NAPI
   are dealt with.

Subsequent batches will rework and improve the rx path, the
tx path, and add a bunch of features and fixes.

Version 2 addresses some review comments to patches 5 and 10
(see version history in the respective emails).
====================
Reviewed-by: default avatarFlorian Fainelli <f.fainelli@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents bb54be58 10cbd640
...@@ -46,52 +46,51 @@ ...@@ -46,52 +46,51 @@
#define MAX_PKT_SIZE 1518 #define MAX_PKT_SIZE 1518
#define RX_BUF_SIZE PAGE_SIZE /* must be smaller than 0x3fff */ #define RX_BUF_SIZE PAGE_SIZE /* must be smaller than 0x3fff */
/******************************************************************************
* private data
*****************************************************************************/
struct ftgmac100_descs { struct ftgmac100_descs {
struct ftgmac100_rxdes rxdes[RX_QUEUE_ENTRIES]; struct ftgmac100_rxdes rxdes[RX_QUEUE_ENTRIES];
struct ftgmac100_txdes txdes[TX_QUEUE_ENTRIES]; struct ftgmac100_txdes txdes[TX_QUEUE_ENTRIES];
}; };
struct ftgmac100 { struct ftgmac100 {
/* Registers */
struct resource *res; struct resource *res;
void __iomem *base; void __iomem *base;
int irq;
struct ftgmac100_descs *descs; struct ftgmac100_descs *descs;
dma_addr_t descs_dma_addr; dma_addr_t descs_dma_addr;
/* Rx ring */
struct page *rx_pages[RX_QUEUE_ENTRIES]; struct page *rx_pages[RX_QUEUE_ENTRIES];
unsigned int rx_pointer; unsigned int rx_pointer;
u32 rxdes0_edorr_mask;
/* Tx ring */
unsigned int tx_clean_pointer; unsigned int tx_clean_pointer;
unsigned int tx_pointer; unsigned int tx_pointer;
unsigned int tx_pending; unsigned int tx_pending;
u32 txdes0_edotr_mask;
spinlock_t tx_lock; spinlock_t tx_lock;
/* Component structures */
struct net_device *netdev; struct net_device *netdev;
struct device *dev; struct device *dev;
struct ncsi_dev *ndev; struct ncsi_dev *ndev;
struct napi_struct napi; struct napi_struct napi;
struct work_struct reset_task;
struct mii_bus *mii_bus; struct mii_bus *mii_bus;
int old_speed;
int int_mask_all; /* Link management */
int cur_speed;
int cur_duplex;
bool use_ncsi; bool use_ncsi;
bool enabled;
u32 rxdes0_edorr_mask; /* Misc */
u32 txdes0_edotr_mask; bool need_mac_restart;
}; };
static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv, static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv,
struct ftgmac100_rxdes *rxdes, gfp_t gfp); struct ftgmac100_rxdes *rxdes, gfp_t gfp);
/******************************************************************************
* internal functions (hardware register access)
*****************************************************************************/
static void ftgmac100_set_rx_ring_base(struct ftgmac100 *priv, dma_addr_t addr) static void ftgmac100_set_rx_ring_base(struct ftgmac100 *priv, dma_addr_t addr)
{ {
iowrite32(addr, priv->base + FTGMAC100_OFFSET_RXR_BADR); iowrite32(addr, priv->base + FTGMAC100_OFFSET_RXR_BADR);
...@@ -115,27 +114,64 @@ static void ftgmac100_txdma_normal_prio_start_polling(struct ftgmac100 *priv) ...@@ -115,27 +114,64 @@ static void ftgmac100_txdma_normal_prio_start_polling(struct ftgmac100 *priv)
iowrite32(1, priv->base + FTGMAC100_OFFSET_NPTXPD); iowrite32(1, priv->base + FTGMAC100_OFFSET_NPTXPD);
} }
static int ftgmac100_reset_hw(struct ftgmac100 *priv) static int ftgmac100_reset_mac(struct ftgmac100 *priv, u32 maccr)
{ {
struct net_device *netdev = priv->netdev; struct net_device *netdev = priv->netdev;
int i; int i;
/* NOTE: reset clears all registers */ /* NOTE: reset clears all registers */
iowrite32(FTGMAC100_MACCR_SW_RST, priv->base + FTGMAC100_OFFSET_MACCR); iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR);
for (i = 0; i < 5; i++) { iowrite32(maccr | FTGMAC100_MACCR_SW_RST,
priv->base + FTGMAC100_OFFSET_MACCR);
for (i = 0; i < 50; i++) {
unsigned int maccr; unsigned int maccr;
maccr = ioread32(priv->base + FTGMAC100_OFFSET_MACCR); maccr = ioread32(priv->base + FTGMAC100_OFFSET_MACCR);
if (!(maccr & FTGMAC100_MACCR_SW_RST)) if (!(maccr & FTGMAC100_MACCR_SW_RST))
return 0; return 0;
udelay(1000); udelay(1);
} }
netdev_err(netdev, "software reset failed\n"); netdev_err(netdev, "Hardware reset failed\n");
return -EIO; return -EIO;
} }
static int ftgmac100_reset_and_config_mac(struct ftgmac100 *priv)
{
u32 maccr = 0;
switch (priv->cur_speed) {
case SPEED_10:
case 0: /* no link */
break;
case SPEED_100:
maccr |= FTGMAC100_MACCR_FAST_MODE;
break;
case SPEED_1000:
maccr |= FTGMAC100_MACCR_GIGA_MODE;
break;
default:
netdev_err(priv->netdev, "Unknown speed %d !\n",
priv->cur_speed);
break;
}
/* (Re)initialize the queue pointers */
priv->rx_pointer = 0;
priv->tx_clean_pointer = 0;
priv->tx_pointer = 0;
priv->tx_pending = 0;
/* The doc says reset twice with 10us interval */
if (ftgmac100_reset_mac(priv, maccr))
return -EIO;
usleep_range(10, 1000);
return ftgmac100_reset_mac(priv, maccr);
}
static void ftgmac100_set_mac(struct ftgmac100 *priv, const unsigned char *mac) static void ftgmac100_set_mac(struct ftgmac100 *priv, const unsigned char *mac)
{ {
unsigned int maddr = mac[0] << 8 | mac[1]; unsigned int maddr = mac[0] << 8 | mac[1];
...@@ -211,33 +247,28 @@ static void ftgmac100_init_hw(struct ftgmac100 *priv) ...@@ -211,33 +247,28 @@ static void ftgmac100_init_hw(struct ftgmac100 *priv)
ftgmac100_set_mac(priv, priv->netdev->dev_addr); ftgmac100_set_mac(priv, priv->netdev->dev_addr);
} }
#define MACCR_ENABLE_ALL (FTGMAC100_MACCR_TXDMA_EN | \ static void ftgmac100_start_hw(struct ftgmac100 *priv)
FTGMAC100_MACCR_RXDMA_EN | \
FTGMAC100_MACCR_TXMAC_EN | \
FTGMAC100_MACCR_RXMAC_EN | \
FTGMAC100_MACCR_FULLDUP | \
FTGMAC100_MACCR_CRC_APD | \
FTGMAC100_MACCR_RX_RUNT | \
FTGMAC100_MACCR_RX_BROADPKT)
static void ftgmac100_start_hw(struct ftgmac100 *priv, int speed)
{ {
int maccr = MACCR_ENABLE_ALL; u32 maccr = ioread32(priv->base + FTGMAC100_OFFSET_MACCR);
switch (speed) { /* Keep the original GMAC and FAST bits */
default: maccr &= (FTGMAC100_MACCR_FAST_MODE | FTGMAC100_MACCR_GIGA_MODE);
case 10:
break;
case 100: /* Add all the main enable bits */
maccr |= FTGMAC100_MACCR_FAST_MODE; maccr |= FTGMAC100_MACCR_TXDMA_EN |
break; FTGMAC100_MACCR_RXDMA_EN |
FTGMAC100_MACCR_TXMAC_EN |
FTGMAC100_MACCR_RXMAC_EN |
FTGMAC100_MACCR_CRC_APD |
FTGMAC100_MACCR_PHY_LINK_LEVEL |
FTGMAC100_MACCR_RX_RUNT |
FTGMAC100_MACCR_RX_BROADPKT;
case 1000: /* Add other bits as needed */
maccr |= FTGMAC100_MACCR_GIGA_MODE; if (priv->cur_duplex == DUPLEX_FULL)
break; maccr |= FTGMAC100_MACCR_FULLDUP;
}
/* Hit the HW */
iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR); iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR);
} }
...@@ -246,9 +277,6 @@ static void ftgmac100_stop_hw(struct ftgmac100 *priv) ...@@ -246,9 +277,6 @@ static void ftgmac100_stop_hw(struct ftgmac100 *priv)
iowrite32(0, priv->base + FTGMAC100_OFFSET_MACCR); iowrite32(0, priv->base + FTGMAC100_OFFSET_MACCR);
} }
/******************************************************************************
* internal functions (receive descriptor)
*****************************************************************************/
static bool ftgmac100_rxdes_first_segment(struct ftgmac100_rxdes *rxdes) static bool ftgmac100_rxdes_first_segment(struct ftgmac100_rxdes *rxdes)
{ {
return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_FRS); return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_FRS);
...@@ -373,9 +401,6 @@ static struct page *ftgmac100_rxdes_get_page(struct ftgmac100 *priv, ...@@ -373,9 +401,6 @@ static struct page *ftgmac100_rxdes_get_page(struct ftgmac100 *priv,
return *ftgmac100_rxdes_page_slot(priv, rxdes); return *ftgmac100_rxdes_page_slot(priv, rxdes);
} }
/******************************************************************************
* internal functions (receive)
*****************************************************************************/
static int ftgmac100_next_rx_pointer(int pointer) static int ftgmac100_next_rx_pointer(int pointer)
{ {
return (pointer + 1) & (RX_QUEUE_ENTRIES - 1); return (pointer + 1) & (RX_QUEUE_ENTRIES - 1);
...@@ -560,9 +585,6 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed) ...@@ -560,9 +585,6 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed)
return true; return true;
} }
/******************************************************************************
* internal functions (transmit descriptor)
*****************************************************************************/
static void ftgmac100_txdes_reset(const struct ftgmac100 *priv, static void ftgmac100_txdes_reset(const struct ftgmac100 *priv,
struct ftgmac100_txdes *txdes) struct ftgmac100_txdes *txdes)
{ {
...@@ -656,9 +678,6 @@ static struct sk_buff *ftgmac100_txdes_get_skb(struct ftgmac100_txdes *txdes) ...@@ -656,9 +678,6 @@ static struct sk_buff *ftgmac100_txdes_get_skb(struct ftgmac100_txdes *txdes)
return (struct sk_buff *)txdes->txdes2; return (struct sk_buff *)txdes->txdes2;
} }
/******************************************************************************
* internal functions (transmit)
*****************************************************************************/
static int ftgmac100_next_tx_pointer(int pointer) static int ftgmac100_next_tx_pointer(int pointer)
{ {
return (pointer + 1) & (TX_QUEUE_ENTRIES - 1); return (pointer + 1) & (TX_QUEUE_ENTRIES - 1);
...@@ -774,9 +793,6 @@ static int ftgmac100_xmit(struct ftgmac100 *priv, struct sk_buff *skb, ...@@ -774,9 +793,6 @@ static int ftgmac100_xmit(struct ftgmac100 *priv, struct sk_buff *skb,
return NETDEV_TX_OK; return NETDEV_TX_OK;
} }
/******************************************************************************
* internal functions (buffer)
*****************************************************************************/
static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv, static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv,
struct ftgmac100_rxdes *rxdes, gfp_t gfp) struct ftgmac100_rxdes *rxdes, gfp_t gfp)
{ {
...@@ -809,6 +825,7 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv) ...@@ -809,6 +825,7 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv)
{ {
int i; int i;
/* Free all RX buffers */
for (i = 0; i < RX_QUEUE_ENTRIES; i++) { for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i]; struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i];
struct page *page = ftgmac100_rxdes_get_page(priv, rxdes); struct page *page = ftgmac100_rxdes_get_page(priv, rxdes);
...@@ -821,6 +838,7 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv) ...@@ -821,6 +838,7 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv)
__free_page(page); __free_page(page);
} }
/* Free all TX buffers */
for (i = 0; i < TX_QUEUE_ENTRIES; i++) { for (i = 0; i < TX_QUEUE_ENTRIES; i++) {
struct ftgmac100_txdes *txdes = &priv->descs->txdes[i]; struct ftgmac100_txdes *txdes = &priv->descs->txdes[i];
struct sk_buff *skb = ftgmac100_txdes_get_skb(txdes); struct sk_buff *skb = ftgmac100_txdes_get_skb(txdes);
...@@ -832,70 +850,90 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv) ...@@ -832,70 +850,90 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv)
dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE); dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE);
kfree_skb(skb); kfree_skb(skb);
} }
dma_free_coherent(priv->dev, sizeof(struct ftgmac100_descs),
priv->descs, priv->descs_dma_addr);
} }
static int ftgmac100_alloc_buffers(struct ftgmac100 *priv) static void ftgmac100_free_rings(struct ftgmac100 *priv)
{ {
int i; /* Free descriptors */
if (priv->descs)
dma_free_coherent(priv->dev, sizeof(struct ftgmac100_descs),
priv->descs, priv->descs_dma_addr);
}
static int ftgmac100_alloc_rings(struct ftgmac100 *priv)
{
/* Allocate descriptors */
priv->descs = dma_zalloc_coherent(priv->dev, priv->descs = dma_zalloc_coherent(priv->dev,
sizeof(struct ftgmac100_descs), sizeof(struct ftgmac100_descs),
&priv->descs_dma_addr, GFP_KERNEL); &priv->descs_dma_addr, GFP_KERNEL);
if (!priv->descs) if (!priv->descs)
return -ENOMEM; return -ENOMEM;
/* initialize RX ring */ return 0;
ftgmac100_rxdes_set_end_of_ring(priv, }
&priv->descs->rxdes[RX_QUEUE_ENTRIES - 1]);
static void ftgmac100_init_rings(struct ftgmac100 *priv)
{
int i;
/* Initialize RX ring */
for (i = 0; i < RX_QUEUE_ENTRIES; i++)
priv->descs->rxdes[i].rxdes0 = 0;
ftgmac100_rxdes_set_end_of_ring(priv, &priv->descs->rxdes[i - 1]);
/* Initialize TX ring */
for (i = 0; i < TX_QUEUE_ENTRIES; i++)
priv->descs->txdes[i].txdes0 = 0;
ftgmac100_txdes_set_end_of_ring(priv, &priv->descs->txdes[i -1]);
}
static int ftgmac100_alloc_rx_buffers(struct ftgmac100 *priv)
{
int i;
for (i = 0; i < RX_QUEUE_ENTRIES; i++) { for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i]; struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i];
if (ftgmac100_alloc_rx_page(priv, rxdes, GFP_KERNEL)) if (ftgmac100_alloc_rx_page(priv, rxdes, GFP_KERNEL))
goto err; return -ENOMEM;
} }
/* initialize TX ring */
ftgmac100_txdes_set_end_of_ring(priv,
&priv->descs->txdes[TX_QUEUE_ENTRIES - 1]);
return 0; return 0;
err:
ftgmac100_free_buffers(priv);
return -ENOMEM;
} }
/******************************************************************************
* internal functions (mdio)
*****************************************************************************/
static void ftgmac100_adjust_link(struct net_device *netdev) static void ftgmac100_adjust_link(struct net_device *netdev)
{ {
struct ftgmac100 *priv = netdev_priv(netdev); struct ftgmac100 *priv = netdev_priv(netdev);
struct phy_device *phydev = netdev->phydev; struct phy_device *phydev = netdev->phydev;
int ier; int new_speed;
if (phydev->speed == priv->old_speed) /* We store "no link" as speed 0 */
if (!phydev->link)
new_speed = 0;
else
new_speed = phydev->speed;
if (phydev->speed == priv->cur_speed &&
phydev->duplex == priv->cur_duplex)
return; return;
priv->old_speed = phydev->speed; /* Print status if we have a link or we had one and just lost it,
* don't print otherwise.
*/
if (new_speed || priv->cur_speed)
phy_print_status(phydev);
ier = ioread32(priv->base + FTGMAC100_OFFSET_IER); priv->cur_speed = new_speed;
priv->cur_duplex = phydev->duplex;
/* disable all interrupts */ /* Link is down, do nothing else */
iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); if (!new_speed)
return;
netif_stop_queue(netdev);
ftgmac100_stop_hw(priv);
netif_start_queue(netdev); /* Disable all interrupts */
ftgmac100_init_hw(priv); iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
ftgmac100_start_hw(priv, phydev->speed);
/* re-enable interrupts */ /* Reset the adapter asynchronously */
iowrite32(ier, priv->base + FTGMAC100_OFFSET_IER); schedule_work(&priv->reset_task);
} }
static int ftgmac100_mii_probe(struct ftgmac100 *priv) static int ftgmac100_mii_probe(struct ftgmac100 *priv)
...@@ -920,9 +958,6 @@ static int ftgmac100_mii_probe(struct ftgmac100 *priv) ...@@ -920,9 +958,6 @@ static int ftgmac100_mii_probe(struct ftgmac100 *priv)
return 0; return 0;
} }
/******************************************************************************
* struct mii_bus functions
*****************************************************************************/
static int ftgmac100_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum) static int ftgmac100_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum)
{ {
struct net_device *netdev = bus->priv; struct net_device *netdev = bus->priv;
...@@ -994,9 +1029,6 @@ static int ftgmac100_mdiobus_write(struct mii_bus *bus, int phy_addr, ...@@ -994,9 +1029,6 @@ static int ftgmac100_mdiobus_write(struct mii_bus *bus, int phy_addr,
return -EIO; return -EIO;
} }
/******************************************************************************
* struct ethtool_ops functions
*****************************************************************************/
static void ftgmac100_get_drvinfo(struct net_device *netdev, static void ftgmac100_get_drvinfo(struct net_device *netdev,
struct ethtool_drvinfo *info) struct ethtool_drvinfo *info)
{ {
...@@ -1012,168 +1044,260 @@ static const struct ethtool_ops ftgmac100_ethtool_ops = { ...@@ -1012,168 +1044,260 @@ static const struct ethtool_ops ftgmac100_ethtool_ops = {
.set_link_ksettings = phy_ethtool_set_link_ksettings, .set_link_ksettings = phy_ethtool_set_link_ksettings,
}; };
/******************************************************************************
* interrupt handler
*****************************************************************************/
static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id) static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id)
{ {
struct net_device *netdev = dev_id; struct net_device *netdev = dev_id;
struct ftgmac100 *priv = netdev_priv(netdev); struct ftgmac100 *priv = netdev_priv(netdev);
unsigned int status, new_mask = FTGMAC100_INT_BAD;
/* When running in NCSI mode, the interface should be ready for /* Fetch and clear interrupt bits, process abnormal ones */
* receiving or transmitting NCSI packets before it's opened. status = ioread32(priv->base + FTGMAC100_OFFSET_ISR);
*/ iowrite32(status, priv->base + FTGMAC100_OFFSET_ISR);
if (likely(priv->use_ncsi || netif_running(netdev))) { if (unlikely(status & FTGMAC100_INT_BAD)) {
/* Disable interrupts for polling */
iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); /* RX buffer unavailable */
napi_schedule(&priv->napi); if (status & FTGMAC100_INT_NO_RXBUF)
netdev->stats.rx_over_errors++;
/* received packet lost due to RX FIFO full */
if (status & FTGMAC100_INT_RPKT_LOST)
netdev->stats.rx_fifo_errors++;
/* sent packet lost due to excessive TX collision */
if (status & FTGMAC100_INT_XPKT_LOST)
netdev->stats.tx_fifo_errors++;
/* AHB error -> Reset the chip */
if (status & FTGMAC100_INT_AHB_ERR) {
if (net_ratelimit())
netdev_warn(netdev,
"AHB bus error ! Resetting chip.\n");
iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
schedule_work(&priv->reset_task);
return IRQ_HANDLED;
}
/* We may need to restart the MAC after such errors, delay
* this until after we have freed some Rx buffers though
*/
priv->need_mac_restart = true;
/* Disable those errors until we restart */
new_mask &= ~status;
} }
/* Only enable "bad" interrupts while NAPI is on */
iowrite32(new_mask, priv->base + FTGMAC100_OFFSET_IER);
/* Schedule NAPI bh */
napi_schedule_irqoff(&priv->napi);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
/******************************************************************************
* struct napi_struct functions
*****************************************************************************/
static int ftgmac100_poll(struct napi_struct *napi, int budget) static int ftgmac100_poll(struct napi_struct *napi, int budget)
{ {
struct ftgmac100 *priv = container_of(napi, struct ftgmac100, napi); struct ftgmac100 *priv = container_of(napi, struct ftgmac100, napi);
struct net_device *netdev = priv->netdev; bool more, completed = true;
unsigned int status;
bool completed = true;
int rx = 0; int rx = 0;
status = ioread32(priv->base + FTGMAC100_OFFSET_ISR); ftgmac100_tx_complete(priv);
iowrite32(status, priv->base + FTGMAC100_OFFSET_ISR);
if (status & (FTGMAC100_INT_RPKT_BUF | FTGMAC100_INT_NO_RXBUF)) {
/*
* FTGMAC100_INT_RPKT_BUF:
* RX DMA has received packets into RX buffer successfully
*
* FTGMAC100_INT_NO_RXBUF:
* RX buffer unavailable
*/
bool retry;
do {
retry = ftgmac100_rx_packet(priv, &rx);
} while (retry && rx < budget);
if (retry && rx == budget) do {
completed = false; more = ftgmac100_rx_packet(priv, &rx);
} } while (more && rx < budget);
if (status & (FTGMAC100_INT_XPKT_ETH | FTGMAC100_INT_XPKT_LOST)) { if (more && rx == budget)
/* completed = false;
* FTGMAC100_INT_XPKT_ETH:
* packet transmitted to ethernet successfully
*
* FTGMAC100_INT_XPKT_LOST:
* packet transmitted to ethernet lost due to late
* collision or excessive collision
*/
ftgmac100_tx_complete(priv);
}
if (status & priv->int_mask_all & (FTGMAC100_INT_NO_RXBUF |
FTGMAC100_INT_RPKT_LOST | FTGMAC100_INT_AHB_ERR)) {
if (net_ratelimit())
netdev_info(netdev, "[ISR] = 0x%x: %s%s%s\n", status,
status & FTGMAC100_INT_NO_RXBUF ? "NO_RXBUF " : "",
status & FTGMAC100_INT_RPKT_LOST ? "RPKT_LOST " : "",
status & FTGMAC100_INT_AHB_ERR ? "AHB_ERR " : "");
if (status & FTGMAC100_INT_NO_RXBUF) { /* The interrupt is telling us to kick the MAC back to life
/* RX buffer unavailable */ * after an RX overflow
netdev->stats.rx_over_errors++; */
} if (unlikely(priv->need_mac_restart)) {
ftgmac100_start_hw(priv);
if (status & FTGMAC100_INT_RPKT_LOST) { /* Re-enable "bad" interrupts */
/* received packet lost due to RX FIFO full */ iowrite32(FTGMAC100_INT_BAD,
netdev->stats.rx_fifo_errors++; priv->base + FTGMAC100_OFFSET_IER);
}
} }
/* Keep NAPI going if we have still packets to reclaim */
if (priv->tx_pending)
return budget;
if (completed) { if (completed) {
/* We are about to re-enable all interrupts. However
* the HW has been latching RX/TX packet interrupts while
* they were masked. So we clear them first, then we need
* to re-check if there's something to process
*/
iowrite32(FTGMAC100_INT_RXTX,
priv->base + FTGMAC100_OFFSET_ISR);
if (ftgmac100_rxdes_packet_ready
(ftgmac100_current_rxdes(priv)) || priv->tx_pending)
return budget;
/* deschedule NAPI */
napi_complete(napi); napi_complete(napi);
/* enable all interrupts */ /* enable all interrupts */
iowrite32(priv->int_mask_all, iowrite32(FTGMAC100_INT_ALL,
priv->base + FTGMAC100_OFFSET_IER); priv->base + FTGMAC100_OFFSET_IER);
} }
return rx; return rx;
} }
/****************************************************************************** static int ftgmac100_init_all(struct ftgmac100 *priv, bool ignore_alloc_err)
* struct net_device_ops functions
*****************************************************************************/
static int ftgmac100_open(struct net_device *netdev)
{ {
struct ftgmac100 *priv = netdev_priv(netdev); int err = 0;
unsigned int status;
/* Re-init descriptors (adjust queue sizes) */
ftgmac100_init_rings(priv);
/* Realloc rx descriptors */
err = ftgmac100_alloc_rx_buffers(priv);
if (err && !ignore_alloc_err)
return err;
/* Reinit and restart HW */
ftgmac100_init_hw(priv);
ftgmac100_start_hw(priv);
/* Re-enable the device */
napi_enable(&priv->napi);
netif_start_queue(priv->netdev);
/* Enable all interrupts */
iowrite32(FTGMAC100_INT_ALL, priv->base + FTGMAC100_OFFSET_IER);
return err;
}
static void ftgmac100_reset_task(struct work_struct *work)
{
struct ftgmac100 *priv = container_of(work, struct ftgmac100,
reset_task);
struct net_device *netdev = priv->netdev;
int err; int err;
err = ftgmac100_alloc_buffers(priv); netdev_dbg(netdev, "Resetting NIC...\n");
/* Lock the world */
rtnl_lock();
if (netdev->phydev)
mutex_lock(&netdev->phydev->lock);
if (priv->mii_bus)
mutex_lock(&priv->mii_bus->mdio_lock);
/* Check if the interface is still up */
if (!netif_running(netdev))
goto bail;
/* Stop the network stack */
netif_trans_update(netdev);
napi_disable(&priv->napi);
netif_tx_disable(netdev);
/* Stop and reset the MAC */
ftgmac100_stop_hw(priv);
err = ftgmac100_reset_and_config_mac(priv);
if (err) { if (err) {
netdev_err(netdev, "failed to allocate buffers\n"); /* Not much we can do ... it might come back... */
goto err_alloc; netdev_err(netdev, "attempting to continue...\n");
} }
err = request_irq(priv->irq, ftgmac100_interrupt, 0, netdev->name, netdev); /* Free all rx and tx buffers */
ftgmac100_free_buffers(priv);
/* Setup everything again and restart chip */
ftgmac100_init_all(priv, true);
netdev_dbg(netdev, "Reset done !\n");
bail:
if (priv->mii_bus)
mutex_unlock(&priv->mii_bus->mdio_lock);
if (netdev->phydev)
mutex_unlock(&netdev->phydev->lock);
rtnl_unlock();
}
static int ftgmac100_open(struct net_device *netdev)
{
struct ftgmac100 *priv = netdev_priv(netdev);
int err;
/* Allocate ring buffers */
err = ftgmac100_alloc_rings(priv);
if (err) { if (err) {
netdev_err(netdev, "failed to request irq %d\n", priv->irq); netdev_err(netdev, "Failed to allocate descriptors\n");
goto err_irq; return err;
} }
priv->rx_pointer = 0; /* When using NC-SI we force the speed to 100Mbit/s full duplex,
priv->tx_clean_pointer = 0; *
priv->tx_pointer = 0; * Otherwise we leave it set to 0 (no link), the link
priv->tx_pending = 0; * message from the PHY layer will handle setting it up to
* something else if needed.
*/
if (priv->use_ncsi) {
priv->cur_duplex = DUPLEX_FULL;
priv->cur_speed = SPEED_100;
} else {
priv->cur_duplex = 0;
priv->cur_speed = 0;
}
err = ftgmac100_reset_hw(priv); /* Reset the hardware */
err = ftgmac100_reset_and_config_mac(priv);
if (err) if (err)
goto err_hw; goto err_hw;
ftgmac100_init_hw(priv); /* Initialize NAPI */
ftgmac100_start_hw(priv, priv->use_ncsi ? 100 : 10); netif_napi_add(netdev, &priv->napi, ftgmac100_poll, 64);
/* Clear stale interrupts */ /* Grab our interrupt */
status = ioread32(priv->base + FTGMAC100_OFFSET_ISR); err = request_irq(netdev->irq, ftgmac100_interrupt, 0, netdev->name, netdev);
iowrite32(status, priv->base + FTGMAC100_OFFSET_ISR); if (err) {
netdev_err(netdev, "failed to request irq %d\n", netdev->irq);
goto err_irq;
}
if (netdev->phydev) /* Start things up */
err = ftgmac100_init_all(priv, false);
if (err) {
netdev_err(netdev, "Failed to allocate packet buffers\n");
goto err_alloc;
}
if (netdev->phydev) {
/* If we have a PHY, start polling */
phy_start(netdev->phydev); phy_start(netdev->phydev);
else if (priv->use_ncsi) } else if (priv->use_ncsi) {
/* If using NC-SI, set our carrier on and start the stack */
netif_carrier_on(netdev); netif_carrier_on(netdev);
napi_enable(&priv->napi); /* Start the NCSI device */
netif_start_queue(netdev);
/* enable all interrupts */
iowrite32(priv->int_mask_all, priv->base + FTGMAC100_OFFSET_IER);
/* Start the NCSI device */
if (priv->use_ncsi) {
err = ncsi_start_dev(priv->ndev); err = ncsi_start_dev(priv->ndev);
if (err) if (err)
goto err_ncsi; goto err_ncsi;
} }
priv->enabled = true;
return 0; return 0;
err_ncsi: err_ncsi:
napi_disable(&priv->napi); napi_disable(&priv->napi);
netif_stop_queue(netdev); netif_stop_queue(netdev);
iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); err_alloc:
err_hw:
free_irq(priv->irq, netdev);
err_irq:
ftgmac100_free_buffers(priv); ftgmac100_free_buffers(priv);
err_alloc: free_irq(netdev->irq, netdev);
err_irq:
netif_napi_del(&priv->napi);
err_hw:
iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
ftgmac100_free_rings(priv);
return err; return err;
} }
...@@ -1181,23 +1305,29 @@ static int ftgmac100_stop(struct net_device *netdev) ...@@ -1181,23 +1305,29 @@ static int ftgmac100_stop(struct net_device *netdev)
{ {
struct ftgmac100 *priv = netdev_priv(netdev); struct ftgmac100 *priv = netdev_priv(netdev);
if (!priv->enabled) /* Note about the reset task: We are called with the rtnl lock
return 0; * held, so we are synchronized against the core of the reset
* task. We must not try to synchronously cancel it otherwise
* we can deadlock. But since it will test for netif_running()
* which has already been cleared by the net core, we don't
* anything special to do.
*/
/* disable all interrupts */ /* disable all interrupts */
priv->enabled = false;
iowrite32(0, priv->base + FTGMAC100_OFFSET_IER); iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
netif_stop_queue(netdev); netif_stop_queue(netdev);
napi_disable(&priv->napi); napi_disable(&priv->napi);
netif_napi_del(&priv->napi);
if (netdev->phydev) if (netdev->phydev)
phy_stop(netdev->phydev); phy_stop(netdev->phydev);
else if (priv->use_ncsi) else if (priv->use_ncsi)
ncsi_stop_dev(priv->ndev); ncsi_stop_dev(priv->ndev);
ftgmac100_stop_hw(priv); ftgmac100_stop_hw(priv);
free_irq(priv->irq, netdev); free_irq(netdev->irq, netdev);
ftgmac100_free_buffers(priv); ftgmac100_free_buffers(priv);
ftgmac100_free_rings(priv);
return 0; return 0;
} }
...@@ -1321,9 +1451,6 @@ static void ftgmac100_ncsi_handler(struct ncsi_dev *nd) ...@@ -1321,9 +1451,6 @@ static void ftgmac100_ncsi_handler(struct ncsi_dev *nd)
nd->link_up ? "up" : "down"); nd->link_up ? "up" : "down");
} }
/******************************************************************************
* struct platform_driver functions
*****************************************************************************/
static int ftgmac100_probe(struct platform_device *pdev) static int ftgmac100_probe(struct platform_device *pdev)
{ {
struct resource *res; struct resource *res;
...@@ -1361,12 +1488,10 @@ static int ftgmac100_probe(struct platform_device *pdev) ...@@ -1361,12 +1488,10 @@ static int ftgmac100_probe(struct platform_device *pdev)
priv = netdev_priv(netdev); priv = netdev_priv(netdev);
priv->netdev = netdev; priv->netdev = netdev;
priv->dev = &pdev->dev; priv->dev = &pdev->dev;
INIT_WORK(&priv->reset_task, ftgmac100_reset_task);
spin_lock_init(&priv->tx_lock); spin_lock_init(&priv->tx_lock);
/* initialize NAPI */
netif_napi_add(netdev, &priv->napi, ftgmac100_poll, 64);
/* map io memory */ /* map io memory */
priv->res = request_mem_region(res->start, resource_size(res), priv->res = request_mem_region(res->start, resource_size(res),
dev_name(&pdev->dev)); dev_name(&pdev->dev));
...@@ -1383,18 +1508,11 @@ static int ftgmac100_probe(struct platform_device *pdev) ...@@ -1383,18 +1508,11 @@ static int ftgmac100_probe(struct platform_device *pdev)
goto err_ioremap; goto err_ioremap;
} }
priv->irq = irq; netdev->irq = irq;
/* MAC address from chip or random one */ /* MAC address from chip or random one */
ftgmac100_setup_mac(priv); ftgmac100_setup_mac(priv);
priv->int_mask_all = (FTGMAC100_INT_RPKT_LOST |
FTGMAC100_INT_XPKT_ETH |
FTGMAC100_INT_XPKT_LOST |
FTGMAC100_INT_AHB_ERR |
FTGMAC100_INT_RPKT_BUF |
FTGMAC100_INT_NO_RXBUF);
if (of_machine_is_compatible("aspeed,ast2400") || if (of_machine_is_compatible("aspeed,ast2400") ||
of_machine_is_compatible("aspeed,ast2500")) { of_machine_is_compatible("aspeed,ast2500")) {
priv->rxdes0_edorr_mask = BIT(30); priv->rxdes0_edorr_mask = BIT(30);
...@@ -1440,7 +1558,7 @@ static int ftgmac100_probe(struct platform_device *pdev) ...@@ -1440,7 +1558,7 @@ static int ftgmac100_probe(struct platform_device *pdev)
goto err_register_netdev; goto err_register_netdev;
} }
netdev_info(netdev, "irq %d, mapped at %p\n", priv->irq, priv->base); netdev_info(netdev, "irq %d, mapped at %p\n", netdev->irq, priv->base);
return 0; return 0;
...@@ -1467,6 +1585,12 @@ static int ftgmac100_remove(struct platform_device *pdev) ...@@ -1467,6 +1585,12 @@ static int ftgmac100_remove(struct platform_device *pdev)
priv = netdev_priv(netdev); priv = netdev_priv(netdev);
unregister_netdev(netdev); unregister_netdev(netdev);
/* There's a small chance the reset task will have been re-queued,
* during stop, make sure it's gone before we free the structure.
*/
cancel_work_sync(&priv->reset_task);
ftgmac100_destroy_mdio(netdev); ftgmac100_destroy_mdio(netdev);
iounmap(priv->base); iounmap(priv->base);
......
...@@ -86,6 +86,20 @@ ...@@ -86,6 +86,20 @@
#define FTGMAC100_INT_PHYSTS_CHG (1 << 9) #define FTGMAC100_INT_PHYSTS_CHG (1 << 9)
#define FTGMAC100_INT_NO_HPTXBUF (1 << 10) #define FTGMAC100_INT_NO_HPTXBUF (1 << 10)
/* Interrupts we care about in NAPI mode */
#define FTGMAC100_INT_BAD (FTGMAC100_INT_RPKT_LOST | \
FTGMAC100_INT_XPKT_LOST | \
FTGMAC100_INT_AHB_ERR | \
FTGMAC100_INT_NO_RXBUF)
/* Normal RX/TX interrupts, enabled when NAPI off */
#define FTGMAC100_INT_RXTX (FTGMAC100_INT_XPKT_ETH | \
FTGMAC100_INT_RPKT_BUF)
/* All the interrupts we care about */
#define FTGMAC100_INT_ALL (FTGMAC100_INT_RPKT_BUF | \
FTGMAC100_INT_BAD)
/* /*
* Interrupt timer control register * Interrupt timer control register
*/ */
......
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