]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/net/ipg.c
ipg: plug Tx completion leak
[linux-2.6-omap-h63xx.git] / drivers / net / ipg.c
index 59898ce54dcf3170a7579d28aa12c0d4bce35bee..9752902f2d9a51bde550411b9ecac88ff32d1057 100644 (file)
@@ -55,6 +55,26 @@ MODULE_DESCRIPTION("IC Plus IP1000 Gigabit Ethernet Adapter Linux Driver "
                   DrvVer);
 MODULE_LICENSE("GPL");
 
+//variable record -- index by leading revision/length
+//Revision/Length(=N*4), Address1, Data1, Address2, Data2,...,AddressN,DataN
+static unsigned short DefaultPhyParam[] = {
+       // 11/12/03 IP1000A v1-3 rev=0x40
+       /*--------------------------------------------------------------------------
+       (0x4000|(15*4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 22, 0x85bd, 24, 0xfff2,
+                                27, 0x0c10, 28, 0x0c10, 29, 0x2c10, 31, 0x0003, 23, 0x92f6,
+                                31, 0x0000, 23, 0x003d, 30, 0x00de, 20, 0x20e7,  9, 0x0700,
+         --------------------------------------------------------------------------*/
+       // 12/17/03 IP1000A v1-4 rev=0x40
+       (0x4000 | (07 * 4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 27, 0xeb8e, 31,
+           0x0000,
+       30, 0x005e, 9, 0x0700,
+       // 01/09/04 IP1000A v1-5 rev=0x41
+       (0x4100 | (07 * 4)), 31, 0x0001, 27, 0x01e0, 31, 0x0002, 27, 0xeb8e, 31,
+           0x0000,
+       30, 0x005e, 9, 0x0700,
+       0x0000
+};
+
 static const char *ipg_brand_name[] = {
        "IC PLUS IP1000 1000/100/10 based NIC",
        "Sundance Technology ST2021 based NIC",
@@ -754,7 +774,7 @@ static int init_rfdlist(struct net_device *dev)
 
                if (sp->RxBuff[i]) {
                        pci_unmap_single(sp->pdev,
-                               le64_to_cpu(rxfd->frag_info & ~IPG_RFI_FRAGLEN),
+                               le64_to_cpu(rxfd->frag_info) & ~IPG_RFI_FRAGLEN,
                                sp->rx_buf_sz, PCI_DMA_FROMDEVICE);
                        IPG_DEV_KFREE_SKB(sp->RxBuff[i]);
                        sp->RxBuff[i] = NULL;
@@ -837,21 +857,14 @@ static void init_tfdlist(struct net_device *dev)
 static void ipg_nic_txfree(struct net_device *dev)
 {
        struct ipg_nic_private *sp = netdev_priv(dev);
-       void __iomem *ioaddr = sp->ioaddr;
-       unsigned int curr;
-       u64 txd_map;
-       unsigned int released, pending;
-
-       txd_map = (u64)sp->txd_map;
-       curr = ipg_r32(TFD_LIST_PTR_0) -
-               do_div(txd_map, sizeof(struct ipg_tx)) - 1;
+       unsigned int released, pending, dirty;
 
        IPG_DEBUG_MSG("_nic_txfree\n");
 
        pending = sp->tx_current - sp->tx_dirty;
+       dirty = sp->tx_dirty % IPG_TFDLIST_LENGTH;
 
        for (released = 0; released < pending; released++) {
-               unsigned int dirty = sp->tx_dirty % IPG_TFDLIST_LENGTH;
                struct sk_buff *skb = sp->TxBuff[dirty];
                struct ipg_tx *txfd = sp->txd + dirty;
 
@@ -862,22 +875,20 @@ static void ipg_nic_txfree(struct net_device *dev)
                 * If the TFDDone bit is set, free the associated
                 * buffer.
                 */
-               if (dirty == curr)
-                       break;
-
-               /* Setup TFDDONE for compatible issue. */
-               txfd->tfc |= cpu_to_le64(IPG_TFC_TFDDONE);
+               if (!(txfd->tfc & cpu_to_le64(IPG_TFC_TFDDONE)))
+                        break;
 
                /* Free the transmit buffer. */
                if (skb) {
                        pci_unmap_single(sp->pdev,
-                               le64_to_cpu(txfd->frag_info & ~IPG_TFI_FRAGLEN),
+                               le64_to_cpu(txfd->frag_info) & ~IPG_TFI_FRAGLEN,
                                skb->len, PCI_DMA_TODEVICE);
 
                        IPG_DEV_KFREE_SKB(skb);
 
                        sp->TxBuff[dirty] = NULL;
                }
+               dirty = (dirty + 1) % IPG_TFDLIST_LENGTH;
        }
 
        sp->tx_dirty += released;
@@ -990,7 +1001,7 @@ static void ipg_nic_txcleanup(struct net_device *dev)
 }
 
 /* Provides statistical information about the IPG NIC. */
-struct net_device_stats *ipg_nic_get_stats(struct net_device *dev)
+static struct net_device_stats *ipg_nic_get_stats(struct net_device *dev)
 {
        struct ipg_nic_private *sp = netdev_priv(dev);
        void __iomem *ioaddr = sp->ioaddr;
@@ -1413,10 +1424,10 @@ static int ipg_nic_rx(struct net_device *dev)
                        framelen = IPG_RXFRAG_SIZE;
                }
 
-               if ((IPG_DROP_ON_RX_ETH_ERRORS && (le64_to_cpu(rxfd->rfs &
+               if ((IPG_DROP_ON_RX_ETH_ERRORS && (le64_to_cpu(rxfd->rfs) &
                       (IPG_RFS_RXFIFOOVERRUN | IPG_RFS_RXRUNTFRAME |
                        IPG_RFS_RXALIGNMENTERROR | IPG_RFS_RXFCSERROR |
-                       IPG_RFS_RXOVERSIZEDFRAME | IPG_RFS_RXLENGTHERROR))))) {
+                       IPG_RFS_RXOVERSIZEDFRAME | IPG_RFS_RXLENGTHERROR)))) {
 
                        IPG_DEBUG_MSG("Rx error, RFS = %16.16lx\n",
                                      (unsigned long int) rxfd->rfs);
@@ -1425,27 +1436,27 @@ static int ipg_nic_rx(struct net_device *dev)
                        sp->stats.rx_errors++;
 
                        /* Increment detailed receive error statistics. */
-                       if (le64_to_cpu(rxfd->rfs & IPG_RFS_RXFIFOOVERRUN)) {
+                       if (le64_to_cpu(rxfd->rfs) & IPG_RFS_RXFIFOOVERRUN) {
                                IPG_DEBUG_MSG("RX FIFO overrun occured.\n");
                                sp->stats.rx_fifo_errors++;
                        }
 
-                       if (le64_to_cpu(rxfd->rfs & IPG_RFS_RXRUNTFRAME)) {
+                       if (le64_to_cpu(rxfd->rfs) & IPG_RFS_RXRUNTFRAME) {
                                IPG_DEBUG_MSG("RX runt occured.\n");
                                sp->stats.rx_length_errors++;
                        }
 
-                       if (le64_to_cpu(rxfd->rfs & IPG_RFS_RXOVERSIZEDFRAME)) ;
+                       if (le64_to_cpu(rxfd->rfs) & IPG_RFS_RXOVERSIZEDFRAME) ;
                        /* Do nothing, error count handled by a IPG
                         * statistic register.
                         */
 
-                       if (le64_to_cpu(rxfd->rfs & IPG_RFS_RXALIGNMENTERROR)) {
+                       if (le64_to_cpu(rxfd->rfs) & IPG_RFS_RXALIGNMENTERROR) {
                                IPG_DEBUG_MSG("RX alignment error occured.\n");
                                sp->stats.rx_frame_errors++;
                        }
 
-                       if (le64_to_cpu(rxfd->rfs & IPG_RFS_RXFCSERROR)) ;
+                       if (le64_to_cpu(rxfd->rfs) & IPG_RFS_RXFCSERROR) ;
                        /* Do nothing, error count handled by a IPG
                         * statistic register.
                         */
@@ -1455,10 +1466,10 @@ static int ipg_nic_rx(struct net_device *dev)
                         * not pass it to higher layer processes.
                         */
                        if (skb) {
-                               u64 info = rxfd->frag_info;
+                               __le64 info = rxfd->frag_info;
 
                                pci_unmap_single(sp->pdev,
-                                       le64_to_cpu(info & ~IPG_RFI_FRAGLEN),
+                                       le64_to_cpu(info) & ~IPG_RFI_FRAGLEN,
                                        sp->rx_buf_sz, PCI_DMA_FROMDEVICE);
 
                                IPG_DEV_KFREE_SKB(skb);
@@ -1532,9 +1543,9 @@ static int ipg_nic_rx(struct net_device *dev)
        if (!i)
                sp->EmptyRFDListCount++;
 #endif
-       while ((le64_to_cpu(rxfd->rfs & IPG_RFS_RFDDONE)) &&
-              !((le64_to_cpu(rxfd->rfs & IPG_RFS_FRAMESTART)) &&
-                (le64_to_cpu(rxfd->rfs & IPG_RFS_FRAMEEND)))) {
+       while ((le64_to_cpu(rxfd->rfs) & IPG_RFS_RFDDONE) &&
+              !((le64_to_cpu(rxfd->rfs) & IPG_RFS_FRAMESTART) &&
+                (le64_to_cpu(rxfd->rfs) & IPG_RFS_FRAMEEND))) {
                unsigned int entry = curr++ % IPG_RFDLIST_LENGTH;
 
                rxfd = sp->rxd + entry;
@@ -1552,7 +1563,7 @@ static int ipg_nic_rx(struct net_device *dev)
                 */
                if (sp->RxBuff[entry]) {
                        pci_unmap_single(sp->pdev,
-                               le64_to_cpu(rxfd->frag_info & ~IPG_RFI_FRAGLEN),
+                               le64_to_cpu(rxfd->frag_info) & ~IPG_RFI_FRAGLEN,
                                sp->rx_buf_sz, PCI_DMA_FROMDEVICE);
                        IPG_DEV_KFREE_SKB(sp->RxBuff[entry]);
                }
@@ -1610,6 +1621,8 @@ static irqreturn_t ipg_interrupt_handler(int irq, void *dev_inst)
 #ifdef JUMBO_FRAME
        ipg_nic_rxrestore(dev);
 #endif
+       spin_lock(&sp->lock);
+
        /* Get interrupt source information, and acknowledge
         * some (i.e. TxDMAComplete, RxDMAComplete, RxEarly,
         * IntRequested, MacControlFrame, LinkEvent) interrupts
@@ -1627,9 +1640,7 @@ static irqreturn_t ipg_interrupt_handler(int irq, void *dev_inst)
        handled = 1;
 
        if (unlikely(!netif_running(dev)))
-               goto out;
-
-       spin_lock(&sp->lock);
+               goto out_unlock;
 
        /* If RFDListEnd interrupt, restore all used RFDs. */
        if (status & IPG_IS_RFD_LIST_END) {
@@ -1713,9 +1724,9 @@ out_enable:
        ipg_w16(IPG_IE_TX_DMA_COMPLETE | IPG_IE_RX_DMA_COMPLETE |
                IPG_IE_HOST_ERROR | IPG_IE_INT_REQUESTED | IPG_IE_TX_COMPLETE |
                IPG_IE_LINK_EVENT | IPG_IE_UPDATE_STATS, INT_ENABLE);
-
+out_unlock:
        spin_unlock(&sp->lock);
-out:
+
        return IRQ_RETVAL(handled);
 }
 
@@ -1730,7 +1741,7 @@ static void ipg_rx_clear(struct ipg_nic_private *sp)
                        IPG_DEV_KFREE_SKB(sp->RxBuff[i]);
                        sp->RxBuff[i] = NULL;
                        pci_unmap_single(sp->pdev,
-                               le64_to_cpu(rxfd->frag_info & ~IPG_RFI_FRAGLEN),
+                               le64_to_cpu(rxfd->frag_info) & ~IPG_RFI_FRAGLEN,
                                sp->rx_buf_sz, PCI_DMA_FROMDEVICE);
                }
        }
@@ -1745,7 +1756,7 @@ static void ipg_tx_clear(struct ipg_nic_private *sp)
                        struct ipg_tx *txfd = sp->txd + i;
 
                        pci_unmap_single(sp->pdev,
-                               le64_to_cpu(txfd->frag_info & ~IPG_TFI_FRAGLEN),
+                               le64_to_cpu(txfd->frag_info) & ~IPG_TFI_FRAGLEN,
                                sp->TxBuff[i]->len, PCI_DMA_TODEVICE);
 
                        IPG_DEV_KFREE_SKB(sp->TxBuff[i]);