improve ethernet driver debugging
authorFlorian Fainelli <florian@openwrt.org>
Wed, 20 Jun 2012 21:57:49 +0000 (21:57 +0000)
committerFlorian Fainelli <florian@openwrt.org>
Wed, 20 Jun 2012 21:57:49 +0000 (21:57 +0000)
SVN-Revision: 32471

target/linux/mcs814x/files-3.3/drivers/net/ethernet/mcs8140/nuport_mac.c

index 030811c3c639616daf8575fbdec1b7519cc9c695..6979d41bc00d06ebbd999b50d58057f8a232b19d 100644 (file)
@@ -139,6 +139,7 @@ struct nuport_mac_priv {
        struct phy_device       *phydev;
        int                     old_link;
        int                     old_duplex;
+       u32                     msg_level;
 };
 
 void dcache_invalidate_only(unsigned long start, unsigned long end)
@@ -231,8 +232,10 @@ static int nuport_mac_start_tx_dma(struct nuport_mac_priv *priv,
 
        while (timeout--) {
                reg = nuport_mac_readl(TX_START_DMA);
-               if (!(reg & 0x01))
+               if (!(reg & 0x01)) {
+                       netdev_dbg(priv->dev, "dma ready\n");
                        break;
+               }
                cpu_relax();
        }
 
@@ -243,7 +246,7 @@ static int nuport_mac_start_tx_dma(struct nuport_mac_priv *priv,
                        skb->len, DMA_TO_DEVICE);
 
        /* enable enhanced mode */
-       nuport_mac_writel(0x03, TX_DMA_ENH);
+       nuport_mac_writel(0x01, TX_DMA_ENH);
        nuport_mac_writel(p, TX_BUFFER_ADDR);
        nuport_mac_writel((skb->len) - 1, TX_PKT_BYTES);
        wmb();
@@ -261,10 +264,24 @@ static void nuport_mac_reset_tx_dma(struct nuport_mac_priv *priv)
        nuport_mac_writel(reg, TX_START_DMA);
 }
 
-static void nuport_mac_start_rx_dma(struct nuport_mac_priv *priv,
+static int nuport_mac_start_rx_dma(struct nuport_mac_priv *priv,
                                        struct sk_buff *skb)
 {
        dma_addr_t p;
+       u32 reg;
+       unsigned int timeout = 2048;
+
+       while (timeout--) {
+               reg = nuport_mac_readl(RX_START_DMA);
+               if (!(reg & 0x01)) {
+                       netdev_dbg(priv->dev, "dma ready\n");
+                       break;
+               }
+               cpu_relax();
+       }
+
+       if (!timeout)
+               return -EBUSY;
 
        p = dma_map_single(&priv->pdev->dev, skb->data,
                                RX_ALLOC_SIZE, DMA_FROM_DEVICE);
@@ -272,6 +289,8 @@ static void nuport_mac_start_rx_dma(struct nuport_mac_priv *priv,
        nuport_mac_writel(p, RX_BUFFER_ADDR);
        wmb();
        nuport_mac_writel(0x01, RX_START_DMA);
+
+       return 0;
 }
 
 static void nuport_mac_reset_rx_dma(struct nuport_mac_priv *priv)
@@ -309,11 +328,16 @@ static int nuport_mac_start_xmit(struct sk_buff *skb, struct net_device *dev)
        struct nuport_mac_priv *priv = netdev_priv(dev);
        int ret;
 
-       dcache_clean_range((u32) skb->data, (u32)(skb->data + skb->len));
+       if (netif_queue_stopped(dev)) {
+               netdev_warn(dev, "netif queue was stopped, restarting\n");
+               netif_start_queue(dev);
+       }
+
        spin_lock_irqsave(&priv->lock, flags);
        if (priv->first_pkt) {
                ret = nuport_mac_start_tx_dma(priv, skb);
                if (ret) {
+                       netif_stop_queue(dev);
                        spin_unlock_irqrestore(&priv->lock, flags);
                        netdev_err(dev, "transmit path busy\n");
                        return NETDEV_TX_BUSY;
@@ -335,6 +359,7 @@ static int nuport_mac_start_xmit(struct sk_buff *skb, struct net_device *dev)
 
        if (priv->valid_txskb[priv->cur_tx]) {
                priv->tx_full = 1;
+               netdev_err(dev, "stopping queue\n");
                netif_stop_queue(dev);
        }
 
@@ -407,8 +432,20 @@ static irqreturn_t nuport_mac_tx_interrupt(int irq, void *dev_id)
        struct sk_buff *skb;
        unsigned long flags;
        int ret;
+       u32 reg;
 
        spin_lock_irqsave(&priv->lock, flags);
+       /* clear status word available if ready */
+       reg = nuport_mac_readl(TX_START_DMA);
+       if (reg & (1 << 18)) {
+               nuport_mac_writel(reg, TX_START_DMA);
+               reg = nuport_mac_readl(TX_DMA_STATUS);
+
+               if (reg & 1)
+                       dev->stats.tx_errors++;
+       } else
+               netdev_dbg(dev, "no status word: %08x\n", reg);
+
        skb = priv->tx_skb[priv->dma_tx];
        priv->tx_skb[priv->dma_tx] = NULL;
        priv->valid_txskb[priv->dma_tx] = 0;
@@ -427,7 +464,7 @@ static irqreturn_t nuport_mac_tx_interrupt(int irq, void *dev_id)
        }
 
        if (priv->tx_full) {
-               netdev_err(dev, "restarting transmit queue\n");
+               netdev_dbg(dev, "restarting transmit queue\n");
                netif_wake_queue(dev);
                priv->tx_full = 0;
        }
@@ -442,7 +479,7 @@ static unsigned int nuport_mac_has_work(struct nuport_mac_priv *priv)
        unsigned int i;
 
        for (i = 0; i < RX_RING_SIZE; i++)
-               if (priv->irq_rxskb[i])
+               if (priv->rx_skb[i])
                        return 1;
 
        return 0;
@@ -453,24 +490,33 @@ static irqreturn_t nuport_mac_rx_interrupt(int irq, void *dev_id)
        struct net_device *dev = (struct net_device *)dev_id;
        struct nuport_mac_priv *priv = netdev_priv(dev);
        unsigned long flags;
+       int ret;
 
        spin_lock_irqsave(&priv->lock, flags);
-       priv->pkt_len[priv->dma_rx] = nuport_mac_readl(RX_ACT_BYTES) - 4;
-       priv->irq_rxskb[priv->dma_rx] = 0;
-       priv->dma_rx++;
-
-       if (priv->dma_rx >= RX_RING_SIZE)
-               priv->dma_rx = 0;
-
-       if (priv->irq_rxskb[priv->dma_rx] == 1)
-               nuport_mac_start_rx_dma(priv, priv->rx_skb[priv->dma_rx]);
+       if (!priv->rx_full) {
+               priv->pkt_len[priv->dma_rx] = nuport_mac_readl(RX_ACT_BYTES) - 4;
+               priv->irq_rxskb[priv->dma_rx] = 0;
+               priv->dma_rx++;
+
+               if (priv->dma_rx >= RX_RING_SIZE)
+                       priv->dma_rx = 0;
+       } else
+               priv->rx_full = 0;
+
+       if (priv->irq_rxskb[priv->dma_rx] == 1) {
+               ret = nuport_mac_start_rx_dma(priv, priv->rx_skb[priv->dma_rx]);
+               if (ret)
+                       netdev_err(dev, "failed to start rx dma\n");
+       } else {
+               priv->rx_full = 1;
+               netdev_dbg(dev, "RX ring full\n");
+       }
 
        if (likely(nuport_mac_has_work(priv))) {
                /* find a way to disable DMA rx irq */
                nuport_mac_disable_rx_dma(priv);
                napi_schedule(&priv->napi);
        }
-
        spin_unlock_irqrestore(&priv->lock, flags);
 
        return IRQ_HANDLED;
@@ -742,9 +788,7 @@ static int nuport_mac_open(struct net_device *dev)
        nuport_mac_reset_rx_dma(priv);
 
        /* Start RX DMA */
-       nuport_mac_start_rx_dma(priv, priv->rx_skb[0]);
-
-       return 0;
+       return nuport_mac_start_rx_dma(priv, priv->rx_skb[0]);
 
 out_rx_skb:
        nuport_mac_free_rx_ring(priv);
@@ -782,6 +826,26 @@ static int nuport_mac_close(struct net_device *dev)
        return 0;
 }
 
+static void nuport_mac_tx_timeout(struct net_device *dev)
+{
+       struct nuport_mac_priv *priv = netdev_priv(dev);
+       unsigned int i;
+
+       netdev_warn(dev, "transmit timeout, attempting recovery\n");
+
+       netdev_info(dev, "TX DMA regs\n");
+       for (i = 0; i < DMA_CHAN_WIDTH; i += 4)
+               netdev_info(dev, "[%02x]: 0x%08x\n", i, nuport_mac_readl(TX_DMA_BASE + i));
+       netdev_info(dev, "RX DMA regs\n");
+       for (i = 0; i < DMA_CHAN_WIDTH; i += 4)
+               netdev_info(dev, "[%02x]: 0x%08x\n", i, nuport_mac_readl(RX_DMA_BASE + i));
+
+       nuport_mac_init_tx_ring(priv);
+       nuport_mac_reset_tx_dma(priv);
+
+       netif_wake_queue(dev);
+}
+
 static int nuport_mac_mii_probe(struct net_device *dev)
 {
        struct nuport_mac_priv *priv = netdev_priv(dev);
@@ -864,11 +928,27 @@ static int nuport_mac_ethtool_set_settings(struct net_device *dev,
        return -EINVAL;
 }
 
+static void nuport_mac_set_msglevel(struct net_device *dev, u32 msg_level)
+{
+       struct nuport_mac_priv *priv = netdev_priv(dev);
+
+       priv->msg_level = msg_level;
+}
+
+static u32 nuport_mac_get_msglevel(struct net_device *dev)
+{
+       struct nuport_mac_priv *priv = netdev_priv(dev);
+
+       return priv->msg_level;
+}
+
 static const struct ethtool_ops nuport_mac_ethtool_ops = {
        .get_drvinfo            = nuport_mac_ethtool_drvinfo,
        .get_link               = ethtool_op_get_link,
        .get_settings           = nuport_mac_ethtool_get_settings,
        .set_settings           = nuport_mac_ethtool_set_settings,
+       .set_msglevel           = nuport_mac_set_msglevel,
+       .get_msglevel           = nuport_mac_get_msglevel,
 };
 
 static const struct net_device_ops nuport_mac_ops = {
@@ -878,6 +958,7 @@ static const struct net_device_ops nuport_mac_ops = {
        .ndo_change_mtu         = eth_change_mtu,
        .ndo_validate_addr      = eth_validate_addr,
        .ndo_set_mac_address    = nuport_mac_change_mac_address,
+       .ndo_tx_timeout         = nuport_mac_tx_timeout,
 };
 
 static int __init nuport_mac_probe(struct platform_device *pdev)
@@ -951,10 +1032,12 @@ static int __init nuport_mac_probe(struct platform_device *pdev)
        priv->link_irq = link_irq;
        priv->rx_irq = rx_irq;
        priv->tx_irq = tx_irq;
+       priv->msg_level = NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK;
        dev->netdev_ops = &nuport_mac_ops;
        dev->ethtool_ops = &nuport_mac_ethtool_ops;
        dev->watchdog_timeo = HZ;
        dev->flags = IFF_BROADCAST;     /* Supports Broadcast */
+       dev->tx_queue_len = TX_RING_SIZE / 2;
 
        netif_napi_add(dev, &priv->napi, nuport_mac_poll, 64);