]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/net/ethernet/faraday/ftgmac100.c
ftgmac100: Rework MAC reset and init
[karo-tx-linux.git] / drivers / net / ethernet / faraday / ftgmac100.c
index 928b0df2b8e033e2b784759e32a0218e0b7e16f2..2c08bacd066ca151010b6f3e369bdfb890455afd 100644 (file)
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/netdevice.h>
+#include <linux/of.h>
 #include <linux/phy.h>
 #include <linux/platform_device.h>
+#include <linux/property.h>
 #include <net/ip.h>
 #include <net/ncsi.h>
 
 #define MAX_PKT_SIZE           1518
 #define RX_BUF_SIZE            PAGE_SIZE       /* must be smaller than 0x3fff */
 
-/******************************************************************************
- * private data
- *****************************************************************************/
 struct ftgmac100_descs {
        struct ftgmac100_rxdes rxdes[RX_QUEUE_ENTRIES];
        struct ftgmac100_txdes txdes[TX_QUEUE_ENTRIES];
 };
 
 struct ftgmac100 {
+       /* Registers */
        struct resource *res;
        void __iomem *base;
-       int irq;
 
        struct ftgmac100_descs *descs;
        dma_addr_t descs_dma_addr;
 
+       /* Rx ring */
        struct page *rx_pages[RX_QUEUE_ENTRIES];
-
        unsigned int rx_pointer;
+       u32 rxdes0_edorr_mask;
+
+       /* Tx ring */
        unsigned int tx_clean_pointer;
        unsigned int tx_pointer;
        unsigned int tx_pending;
-
+       u32 txdes0_edotr_mask;
        spinlock_t tx_lock;
 
+       /* Component structures */
        struct net_device *netdev;
        struct device *dev;
        struct ncsi_dev *ndev;
        struct napi_struct napi;
-
+       struct work_struct reset_task;
        struct mii_bus *mii_bus;
-       int old_speed;
-       int int_mask_all;
+
+       /* Link management */
+       int cur_speed;
+       int cur_duplex;
        bool use_ncsi;
-       bool enabled;
 
-       u32 rxdes0_edorr_mask;
-       u32 txdes0_edotr_mask;
+       /* Misc */
+       int int_mask_all;
 };
 
 static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv,
                                   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)
 {
        iowrite32(addr, priv->base + FTGMAC100_OFFSET_RXR_BADR);
@@ -113,27 +114,64 @@ static void ftgmac100_txdma_normal_prio_start_polling(struct ftgmac100 *priv)
        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;
        int i;
 
        /* NOTE: reset clears all registers */
-       iowrite32(FTGMAC100_MACCR_SW_RST, priv->base + FTGMAC100_OFFSET_MACCR);
-       for (i = 0; i < 5; i++) {
+       iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR);
+       iowrite32(maccr | FTGMAC100_MACCR_SW_RST,
+                 priv->base + FTGMAC100_OFFSET_MACCR);
+       for (i = 0; i < 50; i++) {
                unsigned int maccr;
 
                maccr = ioread32(priv->base + FTGMAC100_OFFSET_MACCR);
                if (!(maccr & FTGMAC100_MACCR_SW_RST))
                        return 0;
 
-               udelay(1000);
+               udelay(1);
        }
 
-       netdev_err(netdev, "software reset failed\n");
+       netdev_err(netdev, "Hardware reset failed\n");
        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)
 {
        unsigned int maddr = mac[0] << 8 | mac[1];
@@ -209,33 +247,28 @@ static void ftgmac100_init_hw(struct ftgmac100 *priv)
        ftgmac100_set_mac(priv, priv->netdev->dev_addr);
 }
 
-#define MACCR_ENABLE_ALL       (FTGMAC100_MACCR_TXDMA_EN       | \
-                                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)
+static void ftgmac100_start_hw(struct ftgmac100 *priv)
 {
-       int maccr = MACCR_ENABLE_ALL;
+       u32 maccr = ioread32(priv->base + FTGMAC100_OFFSET_MACCR);
 
-       switch (speed) {
-       default:
-       case 10:
-               break;
+       /* Keep the original GMAC and FAST bits */
+       maccr &= (FTGMAC100_MACCR_FAST_MODE | FTGMAC100_MACCR_GIGA_MODE);
 
-       case 100:
-               maccr |= FTGMAC100_MACCR_FAST_MODE;
-               break;
+       /* Add all the main enable bits */
+       maccr |= FTGMAC100_MACCR_TXDMA_EN       |
+                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:
-               maccr |= FTGMAC100_MACCR_GIGA_MODE;
-               break;
-       }
+       /* Add other bits as needed */
+       if (priv->cur_duplex == DUPLEX_FULL)
+               maccr |= FTGMAC100_MACCR_FULLDUP;
 
+       /* Hit the HW */
        iowrite32(maccr, priv->base + FTGMAC100_OFFSET_MACCR);
 }
 
@@ -244,9 +277,6 @@ static void ftgmac100_stop_hw(struct ftgmac100 *priv)
        iowrite32(0, priv->base + FTGMAC100_OFFSET_MACCR);
 }
 
-/******************************************************************************
- * internal functions (receive descriptor)
- *****************************************************************************/
 static bool ftgmac100_rxdes_first_segment(struct ftgmac100_rxdes *rxdes)
 {
        return rxdes->rxdes0 & cpu_to_le32(FTGMAC100_RXDES0_FRS);
@@ -371,9 +401,6 @@ static struct page *ftgmac100_rxdes_get_page(struct ftgmac100 *priv,
        return *ftgmac100_rxdes_page_slot(priv, rxdes);
 }
 
-/******************************************************************************
- * internal functions (receive)
- *****************************************************************************/
 static int ftgmac100_next_rx_pointer(int pointer)
 {
        return (pointer + 1) & (RX_QUEUE_ENTRIES - 1);
@@ -558,9 +585,6 @@ static bool ftgmac100_rx_packet(struct ftgmac100 *priv, int *processed)
        return true;
 }
 
-/******************************************************************************
- * internal functions (transmit descriptor)
- *****************************************************************************/
 static void ftgmac100_txdes_reset(const struct ftgmac100 *priv,
                                  struct ftgmac100_txdes *txdes)
 {
@@ -654,9 +678,6 @@ static struct sk_buff *ftgmac100_txdes_get_skb(struct ftgmac100_txdes *txdes)
        return (struct sk_buff *)txdes->txdes2;
 }
 
-/******************************************************************************
- * internal functions (transmit)
- *****************************************************************************/
 static int ftgmac100_next_tx_pointer(int pointer)
 {
        return (pointer + 1) & (TX_QUEUE_ENTRIES - 1);
@@ -772,9 +793,6 @@ static int ftgmac100_xmit(struct ftgmac100 *priv, struct sk_buff *skb,
        return NETDEV_TX_OK;
 }
 
-/******************************************************************************
- * internal functions (buffer)
- *****************************************************************************/
 static int ftgmac100_alloc_rx_page(struct ftgmac100 *priv,
                                   struct ftgmac100_rxdes *rxdes, gfp_t gfp)
 {
@@ -807,6 +825,7 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv)
 {
        int i;
 
+       /* Free all RX buffers */
        for (i = 0; i < RX_QUEUE_ENTRIES; i++) {
                struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i];
                struct page *page = ftgmac100_rxdes_get_page(priv, rxdes);
@@ -819,6 +838,7 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv)
                __free_page(page);
        }
 
+       /* Free all TX buffers */
        for (i = 0; i < TX_QUEUE_ENTRIES; i++) {
                struct ftgmac100_txdes *txdes = &priv->descs->txdes[i];
                struct sk_buff *skb = ftgmac100_txdes_get_skb(txdes);
@@ -830,70 +850,90 @@ static void ftgmac100_free_buffers(struct ftgmac100 *priv)
                dma_unmap_single(priv->dev, map, skb_headlen(skb), DMA_TO_DEVICE);
                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,
                                          sizeof(struct ftgmac100_descs),
                                          &priv->descs_dma_addr, GFP_KERNEL);
        if (!priv->descs)
                return -ENOMEM;
 
-       /* initialize RX ring */
-       ftgmac100_rxdes_set_end_of_ring(priv,
-                                       &priv->descs->rxdes[RX_QUEUE_ENTRIES - 1]);
+       return 0;
+}
+
+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++) {
                struct ftgmac100_rxdes *rxdes = &priv->descs->rxdes[i];
 
                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;
-
-err:
-       ftgmac100_free_buffers(priv);
-       return -ENOMEM;
 }
 
-/******************************************************************************
- * internal functions (mdio)
- *****************************************************************************/
 static void ftgmac100_adjust_link(struct net_device *netdev)
 {
        struct ftgmac100 *priv = netdev_priv(netdev);
        struct phy_device *phydev = netdev->phydev;
-       int ier;
+       int new_speed;
 
-       if (phydev->speed == priv->old_speed)
-               return;
+       /* We store "no link" as speed 0 */
+       if (!phydev->link)
+               new_speed = 0;
+       else
+               new_speed = phydev->speed;
 
-       priv->old_speed = phydev->speed;
+       if (phydev->speed == priv->cur_speed &&
+           phydev->duplex == priv->cur_duplex)
+               return;
 
-       ier = ioread32(priv->base + FTGMAC100_OFFSET_IER);
+       /* 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);
 
-       /* disable all interrupts */
-       iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
+       priv->cur_speed = new_speed;
+       priv->cur_duplex = phydev->duplex;
 
-       netif_stop_queue(netdev);
-       ftgmac100_stop_hw(priv);
+       /* Link is down, do nothing else */
+       if (!new_speed)
+               return;
 
-       netif_start_queue(netdev);
-       ftgmac100_init_hw(priv);
-       ftgmac100_start_hw(priv, phydev->speed);
+       /* Disable all interrupts */
+       iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
 
-       /* re-enable interrupts */
-       iowrite32(ier, priv->base + FTGMAC100_OFFSET_IER);
+       /* Reset the adapter asynchronously */
+       schedule_work(&priv->reset_task);
 }
 
 static int ftgmac100_mii_probe(struct ftgmac100 *priv)
@@ -918,9 +958,6 @@ static int ftgmac100_mii_probe(struct ftgmac100 *priv)
        return 0;
 }
 
-/******************************************************************************
- * struct mii_bus functions
- *****************************************************************************/
 static int ftgmac100_mdiobus_read(struct mii_bus *bus, int phy_addr, int regnum)
 {
        struct net_device *netdev = bus->priv;
@@ -992,9 +1029,6 @@ static int ftgmac100_mdiobus_write(struct mii_bus *bus, int phy_addr,
        return -EIO;
 }
 
-/******************************************************************************
- * struct ethtool_ops functions
- *****************************************************************************/
 static void ftgmac100_get_drvinfo(struct net_device *netdev,
                                  struct ethtool_drvinfo *info)
 {
@@ -1010,9 +1044,6 @@ static const struct ethtool_ops ftgmac100_ethtool_ops = {
        .set_link_ksettings     = phy_ethtool_set_link_ksettings,
 };
 
-/******************************************************************************
- * interrupt handler
- *****************************************************************************/
 static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id)
 {
        struct net_device *netdev = dev_id;
@@ -1030,9 +1061,6 @@ static irqreturn_t ftgmac100_interrupt(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-/******************************************************************************
- * struct napi_struct functions
- *****************************************************************************/
 static int ftgmac100_poll(struct napi_struct *napi, int budget)
 {
        struct ftgmac100 *priv = container_of(napi, struct ftgmac100, napi);
@@ -1104,74 +1132,155 @@ static int ftgmac100_poll(struct napi_struct *napi, int budget)
        return rx;
 }
 
-/******************************************************************************
- * struct net_device_ops functions
- *****************************************************************************/
-static int ftgmac100_open(struct net_device *netdev)
+static int ftgmac100_init_all(struct ftgmac100 *priv, bool ignore_alloc_err)
 {
-       struct ftgmac100 *priv = netdev_priv(netdev);
-       unsigned int status;
+       int err = 0;
+
+       /* 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(priv->int_mask_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;
 
-       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) {
-               netdev_err(netdev, "failed to allocate buffers\n");
-               goto err_alloc;
+               /* Not much we can do ... it might come back... */
+               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) {
-               netdev_err(netdev, "failed to request irq %d\n", priv->irq);
-               goto err_irq;
+               netdev_err(netdev, "Failed to allocate descriptors\n");
+               return err;
        }
 
-       priv->rx_pointer = 0;
-       priv->tx_clean_pointer = 0;
-       priv->tx_pointer = 0;
-       priv->tx_pending = 0;
+       /* When using NC-SI we force the speed to 100Mbit/s full duplex,
+        *
+        * Otherwise we leave it set to 0 (no link), the link
+        * 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)
                goto err_hw;
 
-       ftgmac100_init_hw(priv);
-       ftgmac100_start_hw(priv, priv->use_ncsi ? 100 : 10);
+       /* Initialize NAPI */
+       netif_napi_add(netdev, &priv->napi, ftgmac100_poll, 64);
 
-       /* Clear stale interrupts */
-       status = ioread32(priv->base + FTGMAC100_OFFSET_ISR);
-       iowrite32(status, priv->base + FTGMAC100_OFFSET_ISR);
+       /* Grab our interrupt */
+       err = request_irq(netdev->irq, ftgmac100_interrupt, 0, netdev->name, netdev);
+       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);
-       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);
 
-       napi_enable(&priv->napi);
-       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) {
+               /* Start the NCSI device */
                err = ncsi_start_dev(priv->ndev);
                if (err)
                        goto err_ncsi;
        }
 
-       priv->enabled = true;
-
        return 0;
 
-err_ncsi:
+ err_ncsi:
        napi_disable(&priv->napi);
        netif_stop_queue(netdev);
-       iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
-err_hw:
-       free_irq(priv->irq, netdev);
-err_irq:
+ err_alloc:
        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;
 }
 
@@ -1179,23 +1288,29 @@ static int ftgmac100_stop(struct net_device *netdev)
 {
        struct ftgmac100 *priv = netdev_priv(netdev);
 
-       if (!priv->enabled)
-               return 0;
+       /* Note about the reset task: We are called with the rtnl lock
+        * 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 */
-       priv->enabled = false;
        iowrite32(0, priv->base + FTGMAC100_OFFSET_IER);
 
        netif_stop_queue(netdev);
        napi_disable(&priv->napi);
+       netif_napi_del(&priv->napi);
        if (netdev->phydev)
                phy_stop(netdev->phydev);
        else if (priv->use_ncsi)
                ncsi_stop_dev(priv->ndev);
 
        ftgmac100_stop_hw(priv);
-       free_irq(priv->irq, netdev);
+       free_irq(netdev->irq, netdev);
        ftgmac100_free_buffers(priv);
+       ftgmac100_free_rings(priv);
 
        return 0;
 }
@@ -1319,9 +1434,6 @@ static void ftgmac100_ncsi_handler(struct ncsi_dev *nd)
                    nd->link_up ? "up" : "down");
 }
 
-/******************************************************************************
- * struct platform_driver functions
- *****************************************************************************/
 static int ftgmac100_probe(struct platform_device *pdev)
 {
        struct resource *res;
@@ -1359,12 +1471,10 @@ static int ftgmac100_probe(struct platform_device *pdev)
        priv = netdev_priv(netdev);
        priv->netdev = netdev;
        priv->dev = &pdev->dev;
+       INIT_WORK(&priv->reset_task, ftgmac100_reset_task);
 
        spin_lock_init(&priv->tx_lock);
 
-       /* initialize NAPI */
-       netif_napi_add(netdev, &priv->napi, ftgmac100_poll, 64);
-
        /* map io memory */
        priv->res = request_mem_region(res->start, resource_size(res),
                                       dev_name(&pdev->dev));
@@ -1381,7 +1491,7 @@ static int ftgmac100_probe(struct platform_device *pdev)
                goto err_ioremap;
        }
 
-       priv->irq = irq;
+       netdev->irq = irq;
 
        /* MAC address from chip or random one */
        ftgmac100_setup_mac(priv);
@@ -1438,7 +1548,7 @@ static int ftgmac100_probe(struct platform_device *pdev)
                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;
 
@@ -1465,6 +1575,12 @@ static int ftgmac100_remove(struct platform_device *pdev)
        priv = netdev_priv(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);
 
        iounmap(priv->base);