[mcs814x] nuport-mac: disable MAC RX and TX during interface close
[openwrt.git] / target / linux / mcs814x / files-3.3 / drivers / net / ethernet / mcs8140 / nuport_mac.c
index 8254026..4a42086 100644 (file)
@@ -145,6 +145,7 @@ struct nuport_mac_priv {
 
        /* Transmit buffers */
        struct sk_buff *tx_skb[TX_RING_SIZE];
+       dma_addr_t tx_addr;
        unsigned int valid_txskb[TX_RING_SIZE];
        unsigned int cur_tx;
        unsigned int dma_tx;
@@ -152,6 +153,7 @@ struct nuport_mac_priv {
 
        /* Receive buffers */
        struct sk_buff *rx_skb[RX_RING_SIZE];
+       dma_addr_t rx_addr;
        unsigned int irq_rxskb[RX_RING_SIZE];
        int pkt_len[RX_RING_SIZE];
        unsigned int cur_rx;
@@ -237,7 +239,6 @@ static int nuport_mac_mii_reset(struct mii_bus *bus)
 static int nuport_mac_start_tx_dma(struct nuport_mac_priv *priv,
                                        struct sk_buff *skb)
 {
-       dma_addr_t p;
        u32 reg;
        unsigned int timeout = 2048;
 
@@ -253,12 +254,14 @@ static int nuport_mac_start_tx_dma(struct nuport_mac_priv *priv,
        if (!timeout)
                return -EBUSY;
 
-       p = dma_map_single(&priv->pdev->dev, skb->data,
+       priv->tx_addr = dma_map_single(&priv->pdev->dev, skb->data,
                        skb->len, DMA_TO_DEVICE);
+       if (dma_mapping_error(&priv->pdev->dev, priv->tx_addr))
+               return -ENOMEM;
 
        /* enable enhanced mode */
        nuport_mac_writel(TX_DMA_ENH_ENABLE, TX_DMA_ENH);
-       nuport_mac_writel(p, TX_BUFFER_ADDR);
+       nuport_mac_writel(priv->tx_addr, TX_BUFFER_ADDR);
        nuport_mac_writel((skb->len) - 1, TX_PKT_BYTES);
        wmb();
        reg = TX_DMA_ENABLE | TX_DMA_START_FRAME | TX_DMA_END_FRAME;
@@ -279,7 +282,6 @@ static void nuport_mac_reset_tx_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;
 
@@ -295,10 +297,12 @@ static int nuport_mac_start_rx_dma(struct nuport_mac_priv *priv,
        if (!timeout)
                return -EBUSY;
 
-       p = dma_map_single(&priv->pdev->dev, skb->data,
+       priv->rx_addr = dma_map_single(&priv->pdev->dev, skb->data,
                                RX_ALLOC_SIZE, DMA_FROM_DEVICE);
+       if (dma_mapping_error(&priv->pdev->dev, priv->rx_addr))
+               return -ENOMEM;
 
-       nuport_mac_writel(p, RX_BUFFER_ADDR);
+       nuport_mac_writel(priv->rx_addr, RX_BUFFER_ADDR);
        wmb();
        nuport_mac_writel(RX_DMA_ENABLE, RX_START_DMA);
 
@@ -422,19 +426,25 @@ static irqreturn_t nuport_mac_link_interrupt(int irq, void *dev_id)
        struct nuport_mac_priv *priv = netdev_priv(dev);
        u32 reg;
        u8 phy_addr;
+       unsigned long flags;
+       irqreturn_t ret = IRQ_HANDLED;
 
+       spin_lock_irqsave(&priv->lock, flags);
        reg = nuport_mac_readl(LINK_INT_CSR);
        phy_addr = (reg >> LINK_PHY_ADDR_SHIFT) & (PHY_MAX_ADDR - 1);
 
        if (phy_addr != priv->phydev->addr) {
                netdev_err(dev, "spurious PHY irq (phy: %d)\n", phy_addr);
-               return IRQ_NONE;
+               ret = IRQ_NONE;
+               goto out;
        }
 
        priv->phydev->link = (reg & LINK_UP);
        nuport_mac_adjust_link(dev);
 
-       return IRQ_HANDLED;
+out:
+       spin_unlock_irqrestore(&priv->lock, flags);
+       return ret;
 }
 
 static irqreturn_t nuport_mac_tx_interrupt(int irq, void *dev_id)
@@ -461,6 +471,8 @@ static irqreturn_t nuport_mac_tx_interrupt(int irq, void *dev_id)
        skb = priv->tx_skb[priv->dma_tx];
        priv->tx_skb[priv->dma_tx] = NULL;
        priv->valid_txskb[priv->dma_tx] = 0;
+       dma_unmap_single(&priv->pdev->dev, priv->rx_addr, skb->len,
+                               DMA_TO_DEVICE);
        dev_kfree_skb_irq(skb);
 
        priv->dma_tx++;
@@ -554,6 +566,9 @@ static int nuport_mac_rx(struct net_device *dev, int limit)
                status = get_unaligned((u32 *) (skb->data + len));
                skb->dev = dev;
 
+               dma_unmap_single(&priv->pdev->dev, priv->rx_addr, skb->len,
+                               DMA_FROM_DEVICE);
+
                /* packet filter failed */
                if (!(status & (1 << 30))) {
                        dev_kfree_skb_irq(skb);
@@ -673,6 +688,10 @@ static void nuport_mac_free_rx_ring(struct nuport_mac_priv *priv)
                dev_kfree_skb(priv->rx_skb[i]);
                priv->rx_skb[i] = NULL;
        }
+
+       if (priv->rx_addr)
+               dma_unmap_single(&priv->pdev->dev, priv->rx_addr, RX_ALLOC_SIZE,
+                               DMA_TO_DEVICE);
 }
 
 static void nuport_mac_read_mac_address(struct net_device *dev)
@@ -746,7 +765,12 @@ static int nuport_mac_open(struct net_device *dev)
                goto out_emac_clk;
        }
 
-       phy_start(priv->phydev);
+       ret = request_irq(priv->tx_irq, &nuport_mac_tx_interrupt,
+                               0, dev->name, dev);
+       if (ret) {
+               netdev_err(dev, "unable to request rx interrupt\n");
+               goto out_link_irq;
+       }
 
        /* Enable link interrupt monitoring for our PHY address */
        reg = LINK_INT_EN | (priv->phydev->addr << LINK_PHY_ADDR_SHIFT);
@@ -760,14 +784,7 @@ static int nuport_mac_open(struct net_device *dev)
        nuport_mac_writel(LINK_POLL_MASK, LINK_INT_POLL_TIME);
        spin_unlock_irqrestore(&priv->lock, flags);
 
-       ret = request_irq(priv->tx_irq, &nuport_mac_tx_interrupt,
-                               0, dev->name, dev);
-       if (ret) {
-               netdev_err(dev, "unable to request rx interrupt\n");
-               goto out_link_irq;
-       }
-
-       napi_enable(&priv->napi);
+       phy_start(priv->phydev);
 
        ret = request_irq(priv->rx_irq, &nuport_mac_rx_interrupt,
                                0, dev->name, dev);
@@ -790,7 +807,13 @@ static int nuport_mac_open(struct net_device *dev)
        nuport_mac_reset_rx_dma(priv);
 
        /* Start RX DMA */
-       return nuport_mac_start_rx_dma(priv, priv->rx_skb[0]);
+       spin_lock_irqsave(&priv->lock, flags);
+       ret = nuport_mac_start_rx_dma(priv, priv->rx_skb[0]);
+       spin_unlock_irqrestore(&priv->lock, flags);
+
+       napi_enable(&priv->napi);
+
+       return ret;
 
 out_rx_skb:
        nuport_mac_free_rx_ring(priv);
@@ -806,9 +829,14 @@ out_emac_clk:
 
 static int nuport_mac_close(struct net_device *dev)
 {
+       u32 reg;
        struct nuport_mac_priv *priv = netdev_priv(dev);
 
        spin_lock_irq(&priv->lock);
+       reg = nuport_mac_readl(CTRL_REG);
+       reg &= ~(RX_ENABLE | TX_ENABLE);
+       nuport_mac_writel(reg, CTRL_REG);
+
        napi_disable(&priv->napi);
        netif_stop_queue(dev);
 
@@ -880,8 +908,8 @@ static int nuport_mac_mii_probe(struct net_device *dev)
        phydev->supported &= PHY_BASIC_FEATURES;
        phydev->advertising = phydev->supported;
        priv->phydev = phydev;
-       priv->old_link = 0;
-       priv->old_duplex = -1;
+       priv->old_link = 1;
+       priv->old_duplex = DUPLEX_FULL;
 
        dev_info(&priv->pdev->dev, "attached PHY driver [%s] "
                "(mii_bus:phy_addr=%d)\n",