commit f23f79743e1c17a0c2f3fbb28fae67d47ac6812f Author: Linas Vepstas Date: Tue Oct 10 16:23:24 2006 -0500 [PATCH] powerpc/cell spidernet DMA coalescing The current driver code performs 512 DMA mappns of a bunch of 32-byte structures. This is silly, as they are all in contiguous memory. Ths patch changes the code to DMA map the entie area with just one call. Signed-off-by: Linas Vepstas Cc: James K Lewis Cc: Arnd Bergmann commit 16f8395a8d7f648a5d0ffbf31d7bb59e81394b3b Author: Linas Vepstas Date: Tue Oct 10 16:22:29 2006 -0500 [PATCH] powerpc/cell spidernet release all descrs Bugfix: rx descriptor release function fails to visit the last entry while walking receive descriptor ring. Signed-off-by: Linas Vepstas Cc: James K Lewis Cc: Arnd Bergmann commit e7a3db262e0ab92c5275bead26999df49bb71ee3 Author: Linas Vepstas Date: Tue Oct 10 16:21:10 2006 -0500 [PATCH] powerpc/cell spidernet DMA direction fix The ring buffer descriptors are DMA-accessed bidirectionally, but are not declared in this way. Fix this. Signed-off-by: Linas Vepstas Cc: James K Lewis Cc: Arnd Bergmann commit d7e20cb5f150a7e7ec0b94d813ffbec4e9370be6 Author: Linas Vepstas Date: Tue Oct 10 16:19:34 2006 -0500 [PATCH] powerpc/cell spidernet variable name change Cosmetic patch: give the variable holding the numer of descriptors a more descriptive name, so to avoid confusion. Signed-off-by: Linas Vepstas Cc: James K Lewis Cc: Arnd Bergmann commit ec4f536d1bc0e8b0d64c476e253e1d617635cb0e Author: Linas Vepstas Date: Tue Oct 10 16:18:18 2006 -0500 [PATCH] powerpc/cell spidernet reduce DMA kicking The current code attempts to start the TX dma every time a packet is queued. This is too conservative, and wastes CPU time. This patch changes behaviour to call the kick-dma function less often, only when the tx queue is at risk of emptying. This reduces cpu usage, improves performance. Signed-off-by: Linas Vepstas Cc: James K Lewis Cc: Arnd Bergmann commit f90c0e7350dd400bbe3b66cb53f39a881bfcf566 Author: Linas Vepstas Date: Tue Oct 10 16:15:29 2006 -0500 [PATCH] powerpc/cell spidernet Remove a dummy register read that is not needed. This reduces CPU usage notably during transmit. Signed-off-by: Linas Vepstas Cc: Arnd Bergmann Cc: James K Lewis commit 92d08871db0490fc46608ecd292597e3d59fb1ec Author: Linas Vepstas Date: Tue Oct 10 16:14:29 2006 -0500 [PATCH] powerpc/cell spidernet refine locking The transmit side of the spider ethernet driver currently places locks around some very large chunks of code. This results in a fair amount of lock contention is some cases. This patch makes the locks much more fine-grained, protecting only the cirtical sections. One lock is used to protect three locations: the queue head and tail pointers, and the queue low-watermark location. Signed-off-by: Linas Vepstas Cc: Arnd Bergmann Cc: James K Lewis commit cad0b544d64af4190ff23bbc54cee14b1d2e2c6b Author: Linas Vepstas Date: Tue Oct 10 16:13:05 2006 -0500 [PATCH] powerpc/cell spidernet NAPI polling info. This patch moves transmit queue cleanup code out of the interrupt context, and into the NAPI polling routine. Signed-off-by: Linas Vepstas Acked-by: Arnd Bergmann Cc: James K Lewis commit ae74e1a9cc8d156e6c64e9bcee977652e756bb51 Author: Linas Vepstas Date: Tue Oct 10 16:11:33 2006 -0500 [PATCH] powerpc/cell spidernet low watermark patch. Implement basic low-watermark support for the transmit queue. Hardware low-watermarks allow a properly configured kernel to continously stream data to a device and not have to handle any interrupts at all in doing so. Correct zero-interrupt operation can be actually observed for this driver, when the socket buffer is made large enough. The basic idea of a low-watermark interrupt is as follows. The device driver queues up a bunch of packets for the hardware to transmit, and then kicks the hardware to get it started. As the hardware drains the queue of pending, untransmitted packets, the device driver will want to know when the queue is almost empty, so that it can queue some more packets. If the queue drains down to the low waterark, then an interrupt will be generated. However, if the kernel/driver continues to add enough packets to keep the queue partially filled, no interrupt will actually be generated, and the hardware can continue streaming packets indefinitely in this mode. The impelmentation is done by setting the DESCR_TXDESFLG flag in one of the packets. When the hardware sees this flag, it will interrupt the device driver. Because this flag is on a fixed packet, rather than at fixed location in the queue, the code below needs to move the flag as more packets are queued up. This implementation attempts to keep the flag at about 1/4 from "empty". Signed-off-by: Linas Vepstas Signed-off-by: James K Lewis Acked-by: Arnd Bergmann commit b3ed7a4267558ae6dbc5d28ccf2c739a13cf5aa9 Author: Linas Vepstas Date: Tue Oct 10 16:09:40 2006 -0500 [PATCH] powerpc/cell spidernet incorrect offset Bugfix -- the rx chain is in memory after the tx chain -- the offset being used was wrong, resulting in memory corruption when the size of the rx and tx rings weren't exactly the same. Signed-off-by: Linas Vepstas Cc: James K Lewis Cc: Arnd Bergmann commit b5b4229b4f43d9e728e89bee6ffc4b96e35605fa Author: Linas Vepstas Date: Tue Oct 10 16:08:42 2006 -0500 [PATCH] powerpc/cell spidernet stop error printing patch. Turn off mis-interpretation of the queue-empty interrupt status bit as an error. Signed-off-by: Linas Vepstas Signed-off-by: James K Lewis Acked-by: Arnd Bergmann commit 5a8a66516f49a3a242cb93ccb2b09485f2968a46 Author: Linas Vepstas Date: Tue Oct 10 16:06:53 2006 -0500 [PATCH] powerpc/cell spidernet fix error interrupt print The print message associated with the descriptor chain end interrupt prints a bogs value. Fix that. Signed-off-by: Linas Vepstas Cc: James K Lewis Cc: Arnd Bergmann commit 2304fb9616bab2476be4409cfc6e3142e96e3f27 Author: Linas Vepstas Date: Tue Oct 10 16:05:00 2006 -0500 [PATCH] powerpc/cell spidernet bogus rx interrupt bit The current receive interrupt mask sets a bogus bit that doesn't even belong to the definition of this register. Remove it. Signed-off-by: Linas Vepstas Cc: James K Lewis Cc: Arnd Bergmann commit 6bd5a191596137706466ad7d06acfbf3ca8223ab Author: Linas Vepstas Date: Tue Oct 10 16:04:00 2006 -0500 [PATCH] Spidernet stop queue when queue is full. This patch adds a call to netif_stop_queue() when there is no more room for more packets on the transmit queue. Signed-off-by: Linas Vepstas Cc: James K Lewis Cc: Arnd Bergmann commit 02afe79b99b591f66cc88e9f864ff685b5841e0b Author: Linas Vepstas Date: Tue Oct 10 16:02:54 2006 -0500 [PATCH] Spidernet fix register field definitions This patch fixes the names of a few fields in the DMA control register. There is no functional change. Signed-off-by: Linas Vepstas Cc: James K Lewis Cc: Arnd Bergmann commit 45927c686a6fc6b1be8cf99eff990f654b292445 Author: Linas Vepstas Date: Tue Oct 10 16:01:51 2006 -0500 [PATCH] powerpc/cell spidernet add missing netdev watchdog Set the netdev watchdog timer. Signed-off-by: Linas Vepstas Cc: James K Lewis Cc: Arnd Bergmann commit ac0ce6becb7325e8e870460efdec781f0ca95482 Author: Linas Vepstas Date: Tue Oct 10 16:01:00 2006 -0500 [PATCH] powerpc/cell spidernet zlen min packet length Polite device drivers pad short packets to 60 bytes, so that mean-spirited users don't accidentally DOS some other OS that can't handle short packets. Signed-off-by: Linas Vepstas Cc: James K Lewis Cc: Arnd Bergmann commit 2863834f264750d09231b1d7876a3d223b1f99df Author: Linas Vepstas Date: Tue Oct 10 16:00:04 2006 -0500 [PATCH] powerpc/cell spidernet force-end fix Bugfix: when cleaning up the transmit queue upon device close, be sure to walk the entire queue. Signed-off-by: Linas Vepstas Cc: James K Lewis Cc: Arnd Bergmann commit 089b448d6fbabf3ed773b9be0499bcb63dd5fd5f Author: Linas Vepstas Date: Tue Oct 10 15:59:02 2006 -0500 [PATCH] Spidernet module parm permissions The module param permsissions should bw read-only, not writable. From: James K Lewis Signed-off-by: James K Lewis Signed-off-by: Linas Vepstas Cc: Arnd Bergmann commit 525e127457e4601fc4a86be9045338b64433c286 Author: Linas Vepstas Date: Tue Oct 10 15:57:26 2006 -0500 [PATCH] powerpc/cell spidernet burst alignment patch. This patch increases the Burst Address alignment from 64 to 1024 in the Spidernet driver. This improves transmit performance for large packets. From: James K Lewis Signed-off-by: James K Lewis Signed-off-by: Linas Vepstas Acked-by: Arnd Bergmann commit c6ac95967906c22fa3eac4e5e46b9013fbdccb86 Author: Linas Vepstas Date: Tue Oct 10 15:56:04 2006 -0500 [PATCH] powerpc/cell spidernet ethtool -i version number info. This patch adds version information as reported by ethtool -i to the Spidernet driver. From: James K Lewis Signed-off-by: James K Lewis Signed-off-by: Linas Vepstas Acked-by: Arnd Bergmann Index: linux-2.6/drivers/net/spider_net.c =================================================================== --- linux-2.6.orig/drivers/net/spider_net.c +++ linux-2.6/drivers/net/spider_net.c @@ -55,12 +55,13 @@ MODULE_AUTHOR("Utz Bacher "); MODULE_DESCRIPTION("Spider Southbridge Gigabit Ethernet driver"); MODULE_LICENSE("GPL"); +MODULE_VERSION(VERSION); static int rx_descriptors = SPIDER_NET_RX_DESCRIPTORS_DEFAULT; static int tx_descriptors = SPIDER_NET_TX_DESCRIPTORS_DEFAULT; -module_param(rx_descriptors, int, 0644); -module_param(tx_descriptors, int, 0644); +module_param(rx_descriptors, int, 0444); +module_param(tx_descriptors, int, 0444); MODULE_PARM_DESC(rx_descriptors, "number of descriptors used " \ "in rx chains"); @@ -266,25 +267,6 @@ spider_net_get_descr_status(struct spide } /** - * spider_net_free_chain - free descriptor chain - * @card: card structure - * @chain: address of chain - * - */ -static void -spider_net_free_chain(struct spider_net_card *card, - struct spider_net_descr_chain *chain) -{ - struct spider_net_descr *descr; - - for (descr = chain->tail; !descr->bus_addr; descr = descr->next) { - pci_unmap_single(card->pdev, descr->bus_addr, - SPIDER_NET_DESCR_SIZE, PCI_DMA_BIDIRECTIONAL); - descr->bus_addr = 0; - } -} - -/** * spider_net_init_chain - links descriptor chain * @card: card structure * @chain: address of chain @@ -296,15 +278,15 @@ spider_net_free_chain(struct spider_net_ * * returns 0 on success, <0 on failure */ -static int +static void spider_net_init_chain(struct spider_net_card *card, struct spider_net_descr_chain *chain, struct spider_net_descr *start_descr, - int direction, int no) + dma_addr_t buf, + int no) { int i; struct spider_net_descr *descr; - dma_addr_t buf; descr = start_descr; memset(descr, 0, sizeof(*descr) * no); @@ -313,41 +295,20 @@ spider_net_init_chain(struct spider_net_ for (i=0; idmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; - buf = pci_map_single(card->pdev, descr, - SPIDER_NET_DESCR_SIZE, - direction); - - if (pci_dma_mapping_error(buf)) - goto iommu_error; - descr->bus_addr = buf; + descr->next_descr_addr = 0; descr->next = descr + 1; descr->prev = descr - 1; + buf += sizeof(struct spider_net_descr); } /* do actual circular list */ (descr-1)->next = start_descr; start_descr->prev = descr-1; - descr = start_descr; - if (direction == PCI_DMA_FROMDEVICE) - for (i=0; i < no; i++, descr++) - descr->next_descr_addr = descr->next->bus_addr; - spin_lock_init(&chain->lock); chain->head = start_descr; chain->tail = start_descr; - - return 0; - -iommu_error: - descr = start_descr; - for (i=0; i < no; i++, descr++) - if (descr->bus_addr) - pci_unmap_single(card->pdev, descr->bus_addr, - SPIDER_NET_DESCR_SIZE, - direction); - return -ENOMEM; } /** @@ -362,15 +323,15 @@ spider_net_free_rx_chain_contents(struct struct spider_net_descr *descr; descr = card->rx_chain.head; - while (descr->next != card->rx_chain.head) { + do { if (descr->skb) { dev_kfree_skb(descr->skb); pci_unmap_single(card->pdev, descr->buf_addr, SPIDER_NET_MAX_FRAME, - PCI_DMA_FROMDEVICE); + PCI_DMA_BIDIRECTIONAL); } descr = descr->next; - } + } while (descr != card->rx_chain.head); } /** @@ -645,26 +606,41 @@ static int spider_net_prepare_tx_descr(struct spider_net_card *card, struct sk_buff *skb) { - struct spider_net_descr *descr = card->tx_chain.head; + struct spider_net_descr *descr; dma_addr_t buf; + unsigned long flags; + int length; - buf = pci_map_single(card->pdev, skb->data, skb->len, PCI_DMA_TODEVICE); + length = skb->len; + if (length < ETH_ZLEN) { + if (skb_pad(skb, ETH_ZLEN-length)) + return 0; + length = ETH_ZLEN; + } + + buf = pci_map_single(card->pdev, skb->data, length, PCI_DMA_TODEVICE); if (pci_dma_mapping_error(buf)) { if (netif_msg_tx_err(card) && net_ratelimit()) pr_err("could not iommu-map packet (%p, %i). " - "Dropping packet\n", skb->data, skb->len); + "Dropping packet\n", skb->data, length); card->spider_stats.tx_iommu_map_error++; return -ENOMEM; } + spin_lock_irqsave(&card->tx_chain.lock, flags); + descr = card->tx_chain.head; + card->tx_chain.head = descr->next; + descr->buf_addr = buf; - descr->buf_size = skb->len; + descr->buf_size = length; descr->next_descr_addr = 0; descr->skb = skb; descr->data_status = 0; descr->dmac_cmd_status = SPIDER_NET_DESCR_CARDOWNED | SPIDER_NET_DMAC_NOCS; + spin_unlock_irqrestore(&card->tx_chain.lock, flags); + if (skb->protocol == htons(ETH_P_IP)) switch (skb->nh.iph->protocol) { case IPPROTO_TCP: @@ -675,32 +651,51 @@ spider_net_prepare_tx_descr(struct spide break; } + /* Chain the bus address, so that the DMA engine finds this descr. */ descr->prev->next_descr_addr = descr->bus_addr; + card->netdev->trans_start = jiffies; /* set netdev watchdog timer */ return 0; } -/** - * spider_net_release_tx_descr - processes a used tx descriptor - * @card: card structure - * @descr: descriptor to release - * - * releases a used tx descriptor (unmapping, freeing of skb) - */ -static inline void -spider_net_release_tx_descr(struct spider_net_card *card) +static int +spider_net_set_low_watermark(struct spider_net_card *card) { + unsigned long flags; + int status; + int cnt=0; + int i; struct spider_net_descr *descr = card->tx_chain.tail; - struct sk_buff *skb; - card->tx_chain.tail = card->tx_chain.tail->next; - descr->dmac_cmd_status |= SPIDER_NET_DESCR_NOT_IN_USE; + /* Measure the length of the queue. Measurement does not + * need to be precise -- does not need a lock. */ + while (descr != card->tx_chain.head) { + status = descr->dmac_cmd_status & SPIDER_NET_DESCR_NOT_IN_USE; + if (status == SPIDER_NET_DESCR_NOT_IN_USE) + break; + descr = descr->next; + cnt++; + } - /* unmap the skb */ - skb = descr->skb; - pci_unmap_single(card->pdev, descr->buf_addr, skb->len, - PCI_DMA_TODEVICE); - dev_kfree_skb_any(skb); + /* If TX queue is short, don't even bother with interrupts */ + if (cnt < card->num_tx_desc/4) + return cnt; + + /* Set low-watermark 3/4th's of the way into the queue. */ + descr = card->tx_chain.tail; + cnt = (cnt*3)/4; + for (i=0;inext; + + /* Set the new watermark, clear the old watermark */ + spin_lock_irqsave(&card->tx_chain.lock, flags); + descr->dmac_cmd_status |= SPIDER_NET_DESCR_TXDESFLG; + if (card->low_watermark && card->low_watermark != descr) + card->low_watermark->dmac_cmd_status = + card->low_watermark->dmac_cmd_status & ~SPIDER_NET_DESCR_TXDESFLG; + card->low_watermark = descr; + spin_unlock_irqrestore(&card->tx_chain.lock, flags); + return cnt; } /** @@ -719,21 +714,29 @@ static int spider_net_release_tx_chain(struct spider_net_card *card, int brutal) { struct spider_net_descr_chain *chain = &card->tx_chain; + struct spider_net_descr *descr; + struct sk_buff *skb; + u32 buf_addr; + unsigned long flags; int status; - spider_net_read_reg(card, SPIDER_NET_GDTDMACCNTR); - while (chain->tail != chain->head) { - status = spider_net_get_descr_status(chain->tail); + spin_lock_irqsave(&chain->lock, flags); + descr = chain->tail; + + status = spider_net_get_descr_status(descr); switch (status) { case SPIDER_NET_DESCR_COMPLETE: card->netdev_stats.tx_packets++; - card->netdev_stats.tx_bytes += chain->tail->skb->len; + card->netdev_stats.tx_bytes += descr->skb->len; break; case SPIDER_NET_DESCR_CARDOWNED: - if (!brutal) + if (!brutal) { + spin_unlock_irqrestore(&chain->lock, flags); return 1; + } + /* fallthrough, if we release the descriptors * brutally (then we don't care about * SPIDER_NET_DESCR_CARDOWNED) */ @@ -750,11 +753,25 @@ spider_net_release_tx_chain(struct spide default: card->netdev_stats.tx_dropped++; - return 1; + if (!brutal) { + spin_unlock_irqrestore(&chain->lock, flags); + return 1; + } } - spider_net_release_tx_descr(card); - } + chain->tail = descr->next; + descr->dmac_cmd_status |= SPIDER_NET_DESCR_NOT_IN_USE; + skb = descr->skb; + buf_addr = descr->buf_addr; + spin_unlock_irqrestore(&chain->lock, flags); + + /* unmap the skb */ + if (skb) { + int len = skb->len < ETH_ZLEN ? ETH_ZLEN : skb->len; + pci_unmap_single(card->pdev, buf_addr, len, PCI_DMA_TODEVICE); + dev_kfree_skb(skb); + } + } return 0; } @@ -763,8 +780,12 @@ spider_net_release_tx_chain(struct spide * @card: card structure * @descr: descriptor address to enable TX processing at * - * spider_net_kick_tx_dma writes the current tx chain head as start address - * of the tx descriptor chain and enables the transmission DMA engine + * This routine will start the transmit DMA running if + * it is not already running. This routine ned only be + * called when queueing a new packet to an empty tx queue. + * Writes the current tx chain head as start address + * of the tx descriptor chain and enables the transmission + * DMA engine. */ static inline void spider_net_kick_tx_dma(struct spider_net_card *card) @@ -804,65 +825,43 @@ out: static int spider_net_xmit(struct sk_buff *skb, struct net_device *netdev) { + int cnt; struct spider_net_card *card = netdev_priv(netdev); struct spider_net_descr_chain *chain = &card->tx_chain; - struct spider_net_descr *descr = chain->head; - unsigned long flags; - int result; - - spin_lock_irqsave(&chain->lock, flags); spider_net_release_tx_chain(card, 0); - if (chain->head->next == chain->tail->prev) { - card->netdev_stats.tx_dropped++; - result = NETDEV_TX_LOCKED; - goto out; - } - - if (spider_net_get_descr_status(descr) != SPIDER_NET_DESCR_NOT_IN_USE) { - card->netdev_stats.tx_dropped++; - result = NETDEV_TX_LOCKED; - goto out; - } + if ((chain->head->next == chain->tail->prev) || + (spider_net_prepare_tx_descr(card, skb) != 0)) { - if (spider_net_prepare_tx_descr(card, skb) != 0) { card->netdev_stats.tx_dropped++; - result = NETDEV_TX_BUSY; - goto out; + netif_stop_queue(netdev); + return NETDEV_TX_BUSY; } - result = NETDEV_TX_OK; - - spider_net_kick_tx_dma(card); - card->tx_chain.head = card->tx_chain.head->next; - -out: - spin_unlock_irqrestore(&chain->lock, flags); - netif_wake_queue(netdev); - return result; + cnt = spider_net_set_low_watermark(card); + if (cnt < 5) + spider_net_kick_tx_dma(card); + return NETDEV_TX_OK; } /** * spider_net_cleanup_tx_ring - cleans up the TX ring * @card: card structure * - * spider_net_cleanup_tx_ring is called by the tx_timer (as we don't use - * interrupts to cleanup our TX ring) and returns sent packets to the stack - * by freeing them + * spider_net_cleanup_tx_ring is called by either the tx_timer + * or from the NAPI polling routine. + * This routine releases resources associted with transmitted + * packets, including updating the queue tail pointer. */ static void spider_net_cleanup_tx_ring(struct spider_net_card *card) { - unsigned long flags; - - spin_lock_irqsave(&card->tx_chain.lock, flags); - if ((spider_net_release_tx_chain(card, 0) != 0) && - (card->netdev->flags & IFF_UP)) + (card->netdev->flags & IFF_UP)) { spider_net_kick_tx_dma(card); - - spin_unlock_irqrestore(&card->tx_chain.lock, flags); + netif_wake_queue(card->netdev); + } } /** @@ -1053,6 +1052,7 @@ spider_net_poll(struct net_device *netde int packets_to_do, packets_done = 0; int no_more_packets = 0; + spider_net_cleanup_tx_ring(card); packets_to_do = min(*budget, netdev->quota); while (packets_to_do) { @@ -1243,12 +1243,15 @@ spider_net_handle_error_irq(struct spide case SPIDER_NET_PHYINT: case SPIDER_NET_GMAC2INT: case SPIDER_NET_GMAC1INT: - case SPIDER_NET_GIPSINT: case SPIDER_NET_GFIFOINT: case SPIDER_NET_DMACINT: case SPIDER_NET_GSYSINT: break; */ + case SPIDER_NET_GIPSINT: + show_error = 0; + break; + case SPIDER_NET_GPWOPCMPINT: /* PHY write operation completed */ show_error = 0; @@ -1307,9 +1310,10 @@ spider_net_handle_error_irq(struct spide case SPIDER_NET_GDTDCEINT: /* chain end. If a descriptor should be sent, kick off * tx dma - if (card->tx_chain.tail == card->tx_chain.head) + if (card->tx_chain.tail != card->tx_chain.head) spider_net_kick_tx_dma(card); - show_error = 0; */ + */ + show_error = 0; break; /* case SPIDER_NET_G1TMCNTINT: not used. print a message */ @@ -1354,7 +1358,7 @@ spider_net_handle_error_irq(struct spide if (netif_msg_intr(card)) pr_err("got descriptor chain end interrupt, " "restarting DMAC %c.\n", - 'D'+i-SPIDER_NET_GDDDCEINT); + 'D'-(i-SPIDER_NET_GDDDCEINT)/3); spider_net_refill_rx_chain(card); spider_net_enable_rxdmac(card); show_error = 0; @@ -1423,8 +1427,9 @@ spider_net_handle_error_irq(struct spide } if ((show_error) && (netif_msg_intr(card))) - pr_err("Got error interrupt, GHIINT0STS = 0x%08x, " + pr_err("Got error interrupt on %s, GHIINT0STS = 0x%08x, " "GHIINT1STS = 0x%08x, GHIINT2STS = 0x%08x\n", + card->netdev->name, status_reg, error_reg1, error_reg2); /* clear interrupt sources */ @@ -1460,6 +1465,8 @@ spider_net_interrupt(int irq, void *ptr, spider_net_rx_irq_off(card); netif_rx_schedule(netdev); } + if (status_reg & SPIDER_NET_TXINT) + netif_rx_schedule(netdev); if (status_reg & SPIDER_NET_ERRINT ) spider_net_handle_error_irq(card, status_reg); @@ -1599,7 +1606,7 @@ spider_net_enable_card(struct spider_net SPIDER_NET_INT2_MASK_VALUE); spider_net_write_reg(card, SPIDER_NET_GDTDMACCNTR, - SPIDER_NET_GDTDCEIDIS); + SPIDER_NET_GDTBSTA | SPIDER_NET_GDTDCEIDIS); } /** @@ -1615,16 +1622,33 @@ int spider_net_open(struct net_device *netdev) { struct spider_net_card *card = netdev_priv(netdev); - int result; + struct spider_net_descr *descr; + int result = -ENOMEM; - result = -ENOMEM; - if (spider_net_init_chain(card, &card->tx_chain, card->descr, - PCI_DMA_TODEVICE, card->tx_desc)) - goto alloc_tx_failed; - if (spider_net_init_chain(card, &card->rx_chain, - card->descr + card->rx_desc, - PCI_DMA_FROMDEVICE, card->rx_desc)) - goto alloc_rx_failed; + card->descr_dma_addr = pci_map_single(card->pdev, card->descr, + (card->num_tx_desc+card->num_rx_desc)*sizeof(struct spider_net_descr), + PCI_DMA_BIDIRECTIONAL); + if (pci_dma_mapping_error(card->descr_dma_addr)) + return -ENOMEM; + + spider_net_init_chain(card, &card->tx_chain, card->descr, + card->descr_dma_addr, + card->num_tx_desc); + + card->low_watermark = NULL; + + /* rx_chain is after tx_chain, so offset is descr + tx_count */ + spider_net_init_chain(card, &card->rx_chain, + card->descr + card->num_tx_desc, + card->descr_dma_addr + + card->num_tx_desc * sizeof(struct spider_net_descr), + card->num_rx_desc); + + descr = card->rx_chain.head; + do { + descr->next_descr_addr = descr->next->bus_addr; + descr = descr->next; + } while (descr != card->rx_chain.head); /* allocate rx skbs */ if (spider_net_alloc_rx_skbs(card)) @@ -1650,10 +1674,21 @@ spider_net_open(struct net_device *netde register_int_failed: spider_net_free_rx_chain_contents(card); alloc_skbs_failed: - spider_net_free_chain(card, &card->rx_chain); -alloc_rx_failed: - spider_net_free_chain(card, &card->tx_chain); -alloc_tx_failed: + descr = card->rx_chain.head; + do { + descr->bus_addr = 0; + descr = descr->next; + } while (descr != card->rx_chain.head); + + descr = card->tx_chain.head; + do { + descr->bus_addr = 0; + descr = descr->next; + } while (descr != card->tx_chain.head); + + pci_unmap_single(card->pdev, card->descr_dma_addr, + (card->num_tx_desc+card->num_rx_desc)*sizeof(struct spider_net_descr), + PCI_DMA_BIDIRECTIONAL); return result; } @@ -1856,6 +1891,7 @@ int spider_net_stop(struct net_device *netdev) { struct spider_net_card *card = netdev_priv(netdev); + struct spider_net_descr *descr; tasklet_kill(&card->rxram_full_tl); netif_poll_disable(netdev); @@ -1878,13 +1914,24 @@ spider_net_stop(struct net_device *netde spider_net_disable_rxdmac(card); /* release chains */ - if (spin_trylock(&card->tx_chain.lock)) { - spider_net_release_tx_chain(card, 1); - spin_unlock(&card->tx_chain.lock); - } + spider_net_release_tx_chain(card, 1); + spider_net_free_rx_chain_contents(card); - spider_net_free_chain(card, &card->tx_chain); - spider_net_free_chain(card, &card->rx_chain); + descr = card->rx_chain.head; + do { + descr->bus_addr = 0; + descr = descr->next; + } while (descr != card->rx_chain.head); + + descr = card->tx_chain.head; + do { + descr->bus_addr = 0; + descr = descr->next; + } while (descr != card->tx_chain.head); + + pci_unmap_single(card->pdev, card->descr_dma_addr, + (card->num_tx_desc+card->num_rx_desc)*sizeof(struct spider_net_descr), + PCI_DMA_BIDIRECTIONAL); return 0; } @@ -2012,8 +2059,8 @@ spider_net_setup_netdev(struct spider_ne card->options.rx_csum = SPIDER_NET_RX_CSUM_DEFAULT; - card->tx_desc = tx_descriptors; - card->rx_desc = rx_descriptors; + card->num_tx_desc = tx_descriptors; + card->num_rx_desc = rx_descriptors; spider_net_setup_netdev_ops(netdev); @@ -2252,6 +2299,8 @@ static struct pci_driver spider_net_driv */ static int __init spider_net_init(void) { + printk(KERN_INFO "Spidernet version %s.\n", VERSION); + if (rx_descriptors < SPIDER_NET_RX_DESCRIPTORS_MIN) { rx_descriptors = SPIDER_NET_RX_DESCRIPTORS_MIN; pr_info("adjusting rx descriptors to %i.\n", rx_descriptors); Index: linux-2.6/drivers/net/spider_net.h =================================================================== --- linux-2.6.orig/drivers/net/spider_net.h +++ linux-2.6/drivers/net/spider_net.h @@ -24,6 +24,8 @@ #ifndef _SPIDER_NET_H #define _SPIDER_NET_H +#define VERSION "1.1 A" + #include "sungem_phy.h" extern int spider_net_stop(struct net_device *netdev); @@ -47,7 +49,7 @@ extern char spider_net_driver_name[]; #define SPIDER_NET_TX_DESCRIPTORS_MIN 16 #define SPIDER_NET_TX_DESCRIPTORS_MAX 512 -#define SPIDER_NET_TX_TIMER 20 +#define SPIDER_NET_TX_TIMER (HZ/5) #define SPIDER_NET_RX_CSUM_DEFAULT 1 @@ -189,7 +191,9 @@ extern char spider_net_driver_name[]; #define SPIDER_NET_MACMODE_VALUE 0x00000001 #define SPIDER_NET_BURSTLMT_VALUE 0x00000200 /* about 16 us */ -/* 1(0) enable r/tx dma +/* DMAC control register GDMACCNTR + * + * 1(0) enable r/tx dma * 0000000 fixed to 0 * * 000000 fixed to 0 @@ -198,6 +202,7 @@ extern char spider_net_driver_name[]; * * 000000 fixed to 0 * 00 burst alignment: 128 bytes + * 11 burst alignment: 1024 bytes * * 00000 fixed to 0 * 0 descr writeback size 32 bytes @@ -208,10 +213,13 @@ extern char spider_net_driver_name[]; #define SPIDER_NET_DMA_RX_VALUE 0x80000000 #define SPIDER_NET_DMA_RX_FEND_VALUE 0x00030003 /* to set TX_DMA_EN */ -#define SPIDER_NET_TX_DMA_EN 0x80000000 -#define SPIDER_NET_GDTDCEIDIS 0x00000002 -#define SPIDER_NET_DMA_TX_VALUE SPIDER_NET_TX_DMA_EN | \ - SPIDER_NET_GDTDCEIDIS +#define SPIDER_NET_TX_DMA_EN 0x80000000 +#define SPIDER_NET_GDTBSTA 0x00000300 +#define SPIDER_NET_GDTDCEIDIS 0x00000002 +#define SPIDER_NET_DMA_TX_VALUE SPIDER_NET_TX_DMA_EN | \ + SPIDER_NET_GDTBSTA | \ + SPIDER_NET_GDTDCEIDIS + #define SPIDER_NET_DMA_TX_FEND_VALUE 0x00030003 /* SPIDER_NET_UA_DESCR_VALUE is OR'ed with the unicast address */ @@ -320,13 +328,10 @@ enum spider_net_int2_status { SPIDER_NET_GRISPDNGINT }; -#define SPIDER_NET_TXINT ( (1 << SPIDER_NET_GTTEDINT) | \ - (1 << SPIDER_NET_GDTDCEINT) | \ - (1 << SPIDER_NET_GDTFDCINT) ) - -/* we rely on flagged descriptor interrupts*/ -#define SPIDER_NET_RXINT ( (1 << SPIDER_NET_GDAFDCINT) | \ - (1 << SPIDER_NET_GRMFLLINT) ) +#define SPIDER_NET_TXINT ( (1 << SPIDER_NET_GDTFDCINT) ) + +/* We rely on flagged descriptor interrupts */ +#define SPIDER_NET_RXINT ( (1 << SPIDER_NET_GDAFDCINT) ) #define SPIDER_NET_ERRINT ( 0xffffffff & \ (~SPIDER_NET_TXINT) & \ @@ -349,6 +354,7 @@ enum spider_net_int2_status { #define SPIDER_NET_DESCR_FORCE_END 0x50000000 /* used in rx and tx */ #define SPIDER_NET_DESCR_CARDOWNED 0xA0000000 /* used in rx and tx */ #define SPIDER_NET_DESCR_NOT_IN_USE 0xF0000000 +#define SPIDER_NET_DESCR_TXDESFLG 0x00800000 struct spider_net_descr { /* as defined by the hardware */ @@ -391,8 +397,6 @@ struct spider_net_descr_chain { * 701b8000 would be correct, but every packets gets that flag */ #define SPIDER_NET_DESTROY_RX_FLAGS 0x700b8000 -#define SPIDER_NET_DESCR_SIZE 32 - /* this will be bigger some time */ struct spider_net_options { int rx_csum; /* for rx: if 0 ip_summed=NONE, @@ -431,27 +435,26 @@ struct spider_net_card { void __iomem *regs; + int num_rx_desc; + int num_tx_desc; struct spider_net_descr_chain tx_chain; struct spider_net_descr_chain rx_chain; + struct spider_net_descr *low_watermark; + dma_addr_t descr_dma_addr; - struct net_device_stats netdev_stats; - - struct spider_net_options options; - - spinlock_t intmask_lock; struct tasklet_struct rxram_full_tl; struct timer_list tx_timer; - struct work_struct tx_timeout_task; atomic_t tx_timeout_task_counter; wait_queue_head_t waitq; /* for ethtool */ int msg_enable; - int rx_desc; - int tx_desc; + struct net_device_stats netdev_stats; struct spider_net_extra_stats spider_stats; + struct spider_net_options options; + /* Must be last element in the structure */ struct spider_net_descr descr[0]; }; Index: linux-2.6/drivers/net/spider_net_ethtool.c =================================================================== --- linux-2.6.orig/drivers/net/spider_net_ethtool.c +++ linux-2.6/drivers/net/spider_net_ethtool.c @@ -76,7 +76,7 @@ spider_net_ethtool_get_drvinfo(struct ne /* clear and fill out info */ memset(drvinfo, 0, sizeof(struct ethtool_drvinfo)); strncpy(drvinfo->driver, spider_net_driver_name, 32); - strncpy(drvinfo->version, "0.1", 32); + strncpy(drvinfo->version, VERSION, 32); strcpy(drvinfo->fw_version, "no information"); strncpy(drvinfo->bus_info, pci_name(card->pdev), 32); } @@ -158,9 +158,9 @@ spider_net_ethtool_get_ringparam(struct struct spider_net_card *card = netdev->priv; ering->tx_max_pending = SPIDER_NET_TX_DESCRIPTORS_MAX; - ering->tx_pending = card->tx_desc; + ering->tx_pending = card->num_tx_desc; ering->rx_max_pending = SPIDER_NET_RX_DESCRIPTORS_MAX; - ering->rx_pending = card->rx_desc; + ering->rx_pending = card->num_rx_desc; } static int spider_net_get_stats_count(struct net_device *netdev)