|
@@ -84,7 +84,7 @@ MODULE_DEVICE_TABLE(pci, spider_net_pci_tbl);
|
|
|
*
|
|
|
* returns the content of the specified SMMIO register.
|
|
|
*/
|
|
|
-static u32
|
|
|
+static inline u32
|
|
|
spider_net_read_reg(struct spider_net_card *card, u32 reg)
|
|
|
{
|
|
|
u32 value;
|
|
@@ -101,7 +101,7 @@ spider_net_read_reg(struct spider_net_card *card, u32 reg)
|
|
|
* @reg: register to write to
|
|
|
* @value: value to write into the specified SMMIO register
|
|
|
*/
|
|
|
-static void
|
|
|
+static inline void
|
|
|
spider_net_write_reg(struct spider_net_card *card, u32 reg, u32 value)
|
|
|
{
|
|
|
value = cpu_to_le32(value);
|
|
@@ -259,39 +259,10 @@ spider_net_get_mac_address(struct net_device *netdev)
|
|
|
*
|
|
|
* returns the status as in the dmac_cmd_status field of the descriptor
|
|
|
*/
|
|
|
-static enum spider_net_descr_status
|
|
|
+static inline int
|
|
|
spider_net_get_descr_status(struct spider_net_descr *descr)
|
|
|
{
|
|
|
- u32 cmd_status;
|
|
|
-
|
|
|
- cmd_status = descr->dmac_cmd_status;
|
|
|
- cmd_status >>= SPIDER_NET_DESCR_IND_PROC_SHIFT;
|
|
|
- /* no need to mask out any bits, as cmd_status is 32 bits wide only
|
|
|
- * (and unsigned) */
|
|
|
- return cmd_status;
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * spider_net_set_descr_status -- sets the status of a descriptor
|
|
|
- * @descr: descriptor to change
|
|
|
- * @status: status to set in the descriptor
|
|
|
- *
|
|
|
- * changes the status to the specified value. Doesn't change other bits
|
|
|
- * in the status
|
|
|
- */
|
|
|
-static void
|
|
|
-spider_net_set_descr_status(struct spider_net_descr *descr,
|
|
|
- enum spider_net_descr_status status)
|
|
|
-{
|
|
|
- u32 cmd_status;
|
|
|
- /* read the status */
|
|
|
- cmd_status = descr->dmac_cmd_status;
|
|
|
- /* clean the upper 4 bits */
|
|
|
- cmd_status &= SPIDER_NET_DESCR_IND_PROC_MASKO;
|
|
|
- /* add the status to it */
|
|
|
- cmd_status |= ((u32)status)<<SPIDER_NET_DESCR_IND_PROC_SHIFT;
|
|
|
- /* and write it back */
|
|
|
- descr->dmac_cmd_status = cmd_status;
|
|
|
+ return descr->dmac_cmd_status & SPIDER_NET_DESCR_IND_PROC_MASK;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -328,24 +299,23 @@ spider_net_free_chain(struct spider_net_card *card,
|
|
|
static int
|
|
|
spider_net_init_chain(struct spider_net_card *card,
|
|
|
struct spider_net_descr_chain *chain,
|
|
|
- struct spider_net_descr *start_descr, int no)
|
|
|
+ struct spider_net_descr *start_descr,
|
|
|
+ int direction, int no)
|
|
|
{
|
|
|
int i;
|
|
|
struct spider_net_descr *descr;
|
|
|
dma_addr_t buf;
|
|
|
|
|
|
- atomic_set(&card->rx_chain_refill,0);
|
|
|
-
|
|
|
descr = start_descr;
|
|
|
memset(descr, 0, sizeof(*descr) * no);
|
|
|
|
|
|
/* set up the hardware pointers in each descriptor */
|
|
|
for (i=0; i<no; i++, descr++) {
|
|
|
- spider_net_set_descr_status(descr, SPIDER_NET_DESCR_NOT_IN_USE);
|
|
|
+ descr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE;
|
|
|
|
|
|
buf = pci_map_single(card->pdev, descr,
|
|
|
SPIDER_NET_DESCR_SIZE,
|
|
|
- PCI_DMA_BIDIRECTIONAL);
|
|
|
+ direction);
|
|
|
|
|
|
if (buf == DMA_ERROR_CODE)
|
|
|
goto iommu_error;
|
|
@@ -360,10 +330,11 @@ spider_net_init_chain(struct spider_net_card *card,
|
|
|
start_descr->prev = descr-1;
|
|
|
|
|
|
descr = start_descr;
|
|
|
- for (i=0; i < no; i++, descr++) {
|
|
|
- descr->next_descr_addr = descr->next->bus_addr;
|
|
|
- }
|
|
|
+ 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;
|
|
|
|
|
@@ -375,7 +346,7 @@ iommu_error:
|
|
|
if (descr->bus_addr)
|
|
|
pci_unmap_single(card->pdev, descr->bus_addr,
|
|
|
SPIDER_NET_DESCR_SIZE,
|
|
|
- PCI_DMA_BIDIRECTIONAL);
|
|
|
+ direction);
|
|
|
return -ENOMEM;
|
|
|
}
|
|
|
|
|
@@ -396,7 +367,7 @@ spider_net_free_rx_chain_contents(struct spider_net_card *card)
|
|
|
dev_kfree_skb(descr->skb);
|
|
|
pci_unmap_single(card->pdev, descr->buf_addr,
|
|
|
SPIDER_NET_MAX_FRAME,
|
|
|
- PCI_DMA_BIDIRECTIONAL);
|
|
|
+ PCI_DMA_FROMDEVICE);
|
|
|
}
|
|
|
descr = descr->next;
|
|
|
}
|
|
@@ -446,15 +417,16 @@ spider_net_prepare_rx_descr(struct spider_net_card *card,
|
|
|
skb_reserve(descr->skb, SPIDER_NET_RXBUF_ALIGN - offset);
|
|
|
/* io-mmu-map the skb */
|
|
|
buf = pci_map_single(card->pdev, descr->skb->data,
|
|
|
- SPIDER_NET_MAX_FRAME, PCI_DMA_BIDIRECTIONAL);
|
|
|
+ SPIDER_NET_MAX_FRAME, PCI_DMA_FROMDEVICE);
|
|
|
descr->buf_addr = buf;
|
|
|
if (buf == DMA_ERROR_CODE) {
|
|
|
dev_kfree_skb_any(descr->skb);
|
|
|
if (netif_msg_rx_err(card) && net_ratelimit())
|
|
|
pr_err("Could not iommu-map rx buffer\n");
|
|
|
- spider_net_set_descr_status(descr, SPIDER_NET_DESCR_NOT_IN_USE);
|
|
|
+ descr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE;
|
|
|
} else {
|
|
|
- descr->dmac_cmd_status = SPIDER_NET_DMAC_RX_CARDOWNED;
|
|
|
+ descr->dmac_cmd_status = SPIDER_NET_DESCR_CARDOWNED |
|
|
|
+ SPIDER_NET_DMAC_NOINTR_COMPLETE;
|
|
|
}
|
|
|
|
|
|
return error;
|
|
@@ -468,7 +440,7 @@ spider_net_prepare_rx_descr(struct spider_net_card *card,
|
|
|
* chip by writing to the appropriate register. DMA is enabled in
|
|
|
* spider_net_enable_rxdmac.
|
|
|
*/
|
|
|
-static void
|
|
|
+static inline void
|
|
|
spider_net_enable_rxchtails(struct spider_net_card *card)
|
|
|
{
|
|
|
/* assume chain is aligned correctly */
|
|
@@ -483,7 +455,7 @@ spider_net_enable_rxchtails(struct spider_net_card *card)
|
|
|
* spider_net_enable_rxdmac enables the DMA controller by setting RX_DMA_EN
|
|
|
* in the GDADMACCNTR register
|
|
|
*/
|
|
|
-static void
|
|
|
+static inline void
|
|
|
spider_net_enable_rxdmac(struct spider_net_card *card)
|
|
|
{
|
|
|
wmb();
|
|
@@ -500,23 +472,24 @@ spider_net_enable_rxdmac(struct spider_net_card *card)
|
|
|
static void
|
|
|
spider_net_refill_rx_chain(struct spider_net_card *card)
|
|
|
{
|
|
|
- struct spider_net_descr_chain *chain;
|
|
|
-
|
|
|
- chain = &card->rx_chain;
|
|
|
+ struct spider_net_descr_chain *chain = &card->rx_chain;
|
|
|
+ unsigned long flags;
|
|
|
|
|
|
/* one context doing the refill (and a second context seeing that
|
|
|
* and omitting it) is ok. If called by NAPI, we'll be called again
|
|
|
* as spider_net_decode_one_descr is called several times. If some
|
|
|
* interrupt calls us, the NAPI is about to clean up anyway. */
|
|
|
- if (atomic_inc_return(&card->rx_chain_refill) == 1)
|
|
|
- while (spider_net_get_descr_status(chain->head) ==
|
|
|
- SPIDER_NET_DESCR_NOT_IN_USE) {
|
|
|
- if (spider_net_prepare_rx_descr(card, chain->head))
|
|
|
- break;
|
|
|
- chain->head = chain->head->next;
|
|
|
- }
|
|
|
+ if (!spin_trylock_irqsave(&chain->lock, flags))
|
|
|
+ return;
|
|
|
+
|
|
|
+ while (spider_net_get_descr_status(chain->head) ==
|
|
|
+ SPIDER_NET_DESCR_NOT_IN_USE) {
|
|
|
+ if (spider_net_prepare_rx_descr(card, chain->head))
|
|
|
+ break;
|
|
|
+ chain->head = chain->head->next;
|
|
|
+ }
|
|
|
|
|
|
- atomic_dec(&card->rx_chain_refill);
|
|
|
+ spin_unlock_irqrestore(&chain->lock, flags);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -553,111 +526,6 @@ error:
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * 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 void
|
|
|
-spider_net_release_tx_descr(struct spider_net_card *card,
|
|
|
- struct spider_net_descr *descr)
|
|
|
-{
|
|
|
- struct sk_buff *skb;
|
|
|
-
|
|
|
- /* unmap the skb */
|
|
|
- skb = descr->skb;
|
|
|
- pci_unmap_single(card->pdev, descr->buf_addr, skb->len,
|
|
|
- PCI_DMA_BIDIRECTIONAL);
|
|
|
-
|
|
|
- dev_kfree_skb_any(skb);
|
|
|
-
|
|
|
- /* set status to not used */
|
|
|
- spider_net_set_descr_status(descr, SPIDER_NET_DESCR_NOT_IN_USE);
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * spider_net_release_tx_chain - processes sent tx descriptors
|
|
|
- * @card: adapter structure
|
|
|
- * @brutal: if set, don't care about whether descriptor seems to be in use
|
|
|
- *
|
|
|
- * returns 0 if the tx ring is empty, otherwise 1.
|
|
|
- *
|
|
|
- * spider_net_release_tx_chain releases the tx descriptors that spider has
|
|
|
- * finished with (if non-brutal) or simply release tx descriptors (if brutal).
|
|
|
- * If some other context is calling this function, we return 1 so that we're
|
|
|
- * scheduled again (if we were scheduled) and will not loose initiative.
|
|
|
- */
|
|
|
-static int
|
|
|
-spider_net_release_tx_chain(struct spider_net_card *card, int brutal)
|
|
|
-{
|
|
|
- struct spider_net_descr_chain *tx_chain = &card->tx_chain;
|
|
|
- enum spider_net_descr_status status;
|
|
|
-
|
|
|
- if (atomic_inc_return(&card->tx_chain_release) != 1) {
|
|
|
- atomic_dec(&card->tx_chain_release);
|
|
|
- return 1;
|
|
|
- }
|
|
|
-
|
|
|
- for (;;) {
|
|
|
- status = spider_net_get_descr_status(tx_chain->tail);
|
|
|
- switch (status) {
|
|
|
- case SPIDER_NET_DESCR_CARDOWNED:
|
|
|
- if (!brutal)
|
|
|
- goto out;
|
|
|
- /* fallthrough, if we release the descriptors
|
|
|
- * brutally (then we don't care about
|
|
|
- * SPIDER_NET_DESCR_CARDOWNED) */
|
|
|
- case SPIDER_NET_DESCR_RESPONSE_ERROR:
|
|
|
- case SPIDER_NET_DESCR_PROTECTION_ERROR:
|
|
|
- case SPIDER_NET_DESCR_FORCE_END:
|
|
|
- if (netif_msg_tx_err(card))
|
|
|
- pr_err("%s: forcing end of tx descriptor "
|
|
|
- "with status x%02x\n",
|
|
|
- card->netdev->name, status);
|
|
|
- card->netdev_stats.tx_dropped++;
|
|
|
- break;
|
|
|
-
|
|
|
- case SPIDER_NET_DESCR_COMPLETE:
|
|
|
- card->netdev_stats.tx_packets++;
|
|
|
- card->netdev_stats.tx_bytes +=
|
|
|
- tx_chain->tail->skb->len;
|
|
|
- break;
|
|
|
-
|
|
|
- default: /* any other value (== SPIDER_NET_DESCR_NOT_IN_USE) */
|
|
|
- goto out;
|
|
|
- }
|
|
|
- spider_net_release_tx_descr(card, tx_chain->tail);
|
|
|
- tx_chain->tail = tx_chain->tail->next;
|
|
|
- }
|
|
|
-out:
|
|
|
- atomic_dec(&card->tx_chain_release);
|
|
|
-
|
|
|
- netif_wake_queue(card->netdev);
|
|
|
-
|
|
|
- if (status == SPIDER_NET_DESCR_CARDOWNED)
|
|
|
- return 1;
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * 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
|
|
|
- */
|
|
|
-static void
|
|
|
-spider_net_cleanup_tx_ring(struct spider_net_card *card)
|
|
|
-{
|
|
|
- if ( (spider_net_release_tx_chain(card, 0)) &&
|
|
|
- (card->netdev->flags & IFF_UP) ) {
|
|
|
- mod_timer(&card->tx_timer, jiffies + SPIDER_NET_TX_TIMER);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
/**
|
|
|
* spider_net_get_multicast_hash - generates hash for multicast filter table
|
|
|
* @addr: multicast address
|
|
@@ -760,97 +628,6 @@ spider_net_disable_rxdmac(struct spider_net_card *card)
|
|
|
SPIDER_NET_DMA_RX_FEND_VALUE);
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * spider_net_stop - called upon ifconfig down
|
|
|
- * @netdev: interface device structure
|
|
|
- *
|
|
|
- * always returns 0
|
|
|
- */
|
|
|
-int
|
|
|
-spider_net_stop(struct net_device *netdev)
|
|
|
-{
|
|
|
- struct spider_net_card *card = netdev_priv(netdev);
|
|
|
-
|
|
|
- tasklet_kill(&card->rxram_full_tl);
|
|
|
- netif_poll_disable(netdev);
|
|
|
- netif_carrier_off(netdev);
|
|
|
- netif_stop_queue(netdev);
|
|
|
- del_timer_sync(&card->tx_timer);
|
|
|
-
|
|
|
- /* disable/mask all interrupts */
|
|
|
- spider_net_write_reg(card, SPIDER_NET_GHIINT0MSK, 0);
|
|
|
- spider_net_write_reg(card, SPIDER_NET_GHIINT1MSK, 0);
|
|
|
- spider_net_write_reg(card, SPIDER_NET_GHIINT2MSK, 0);
|
|
|
-
|
|
|
- /* free_irq(netdev->irq, netdev);*/
|
|
|
- free_irq(to_pci_dev(netdev->class_dev.dev)->irq, netdev);
|
|
|
-
|
|
|
- spider_net_write_reg(card, SPIDER_NET_GDTDMACCNTR,
|
|
|
- SPIDER_NET_DMA_TX_FEND_VALUE);
|
|
|
-
|
|
|
- /* turn off DMA, force end */
|
|
|
- spider_net_disable_rxdmac(card);
|
|
|
-
|
|
|
- /* release chains */
|
|
|
- spider_net_release_tx_chain(card, 1);
|
|
|
-
|
|
|
- spider_net_free_chain(card, &card->tx_chain);
|
|
|
- spider_net_free_chain(card, &card->rx_chain);
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * spider_net_get_next_tx_descr - returns the next available tx descriptor
|
|
|
- * @card: device structure to get descriptor from
|
|
|
- *
|
|
|
- * returns the address of the next descriptor, or NULL if not available.
|
|
|
- */
|
|
|
-static struct spider_net_descr *
|
|
|
-spider_net_get_next_tx_descr(struct spider_net_card *card)
|
|
|
-{
|
|
|
- /* check, if head points to not-in-use descr */
|
|
|
- if ( spider_net_get_descr_status(card->tx_chain.head) ==
|
|
|
- SPIDER_NET_DESCR_NOT_IN_USE ) {
|
|
|
- return card->tx_chain.head;
|
|
|
- } else {
|
|
|
- return NULL;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-/**
|
|
|
- * spider_net_set_txdescr_cmdstat - sets the tx descriptor command field
|
|
|
- * @descr: descriptor structure to fill out
|
|
|
- * @skb: packet to consider
|
|
|
- *
|
|
|
- * fills out the command and status field of the descriptor structure,
|
|
|
- * depending on hardware checksum settings.
|
|
|
- */
|
|
|
-static void
|
|
|
-spider_net_set_txdescr_cmdstat(struct spider_net_descr *descr,
|
|
|
- struct sk_buff *skb)
|
|
|
-{
|
|
|
- /* make sure the other fields in the descriptor are written */
|
|
|
- wmb();
|
|
|
-
|
|
|
- if (skb->ip_summed != CHECKSUM_HW) {
|
|
|
- descr->dmac_cmd_status = SPIDER_NET_DMAC_CMDSTAT_NOCS;
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- /* is packet ip?
|
|
|
- * if yes: tcp? udp? */
|
|
|
- if (skb->protocol == htons(ETH_P_IP)) {
|
|
|
- if (skb->nh.iph->protocol == IPPROTO_TCP)
|
|
|
- descr->dmac_cmd_status = SPIDER_NET_DMAC_CMDSTAT_TCPCS;
|
|
|
- else if (skb->nh.iph->protocol == IPPROTO_UDP)
|
|
|
- descr->dmac_cmd_status = SPIDER_NET_DMAC_CMDSTAT_UDPCS;
|
|
|
- else /* the stack should checksum non-tcp and non-udp
|
|
|
- packets on his own: NETIF_F_IP_CSUM */
|
|
|
- descr->dmac_cmd_status = SPIDER_NET_DMAC_CMDSTAT_NOCS;
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
/**
|
|
|
* spider_net_prepare_tx_descr - fill tx descriptor with skb data
|
|
|
* @card: card structure
|
|
@@ -864,13 +641,12 @@ spider_net_set_txdescr_cmdstat(struct spider_net_descr *descr,
|
|
|
*/
|
|
|
static int
|
|
|
spider_net_prepare_tx_descr(struct spider_net_card *card,
|
|
|
- struct spider_net_descr *descr,
|
|
|
struct sk_buff *skb)
|
|
|
{
|
|
|
+ struct spider_net_descr *descr = card->tx_chain.head;
|
|
|
dma_addr_t buf;
|
|
|
|
|
|
- buf = pci_map_single(card->pdev, skb->data,
|
|
|
- skb->len, PCI_DMA_BIDIRECTIONAL);
|
|
|
+ buf = pci_map_single(card->pdev, skb->data, skb->len, PCI_DMA_TODEVICE);
|
|
|
if (buf == DMA_ERROR_CODE) {
|
|
|
if (netif_msg_tx_err(card) && net_ratelimit())
|
|
|
pr_err("could not iommu-map packet (%p, %i). "
|
|
@@ -880,10 +656,101 @@ spider_net_prepare_tx_descr(struct spider_net_card *card,
|
|
|
|
|
|
descr->buf_addr = buf;
|
|
|
descr->buf_size = skb->len;
|
|
|
+ descr->next_descr_addr = 0;
|
|
|
descr->skb = skb;
|
|
|
descr->data_status = 0;
|
|
|
|
|
|
- spider_net_set_txdescr_cmdstat(descr,skb);
|
|
|
+ descr->dmac_cmd_status =
|
|
|
+ SPIDER_NET_DESCR_CARDOWNED | SPIDER_NET_DMAC_NOCS;
|
|
|
+ if (skb->protocol == htons(ETH_P_IP))
|
|
|
+ switch (skb->nh.iph->protocol) {
|
|
|
+ case IPPROTO_TCP:
|
|
|
+ descr->dmac_cmd_status |= SPIDER_NET_DMAC_TCP;
|
|
|
+ break;
|
|
|
+ case IPPROTO_UDP:
|
|
|
+ descr->dmac_cmd_status |= SPIDER_NET_DMAC_UDP;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ descr->prev->next_descr_addr = descr->bus_addr;
|
|
|
+
|
|
|
+ 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)
|
|
|
+{
|
|
|
+ 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;
|
|
|
+
|
|
|
+ /* unmap the skb */
|
|
|
+ skb = descr->skb;
|
|
|
+ pci_unmap_single(card->pdev, descr->buf_addr, skb->len,
|
|
|
+ PCI_DMA_TODEVICE);
|
|
|
+ dev_kfree_skb_any(skb);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * spider_net_release_tx_chain - processes sent tx descriptors
|
|
|
+ * @card: adapter structure
|
|
|
+ * @brutal: if set, don't care about whether descriptor seems to be in use
|
|
|
+ *
|
|
|
+ * returns 0 if the tx ring is empty, otherwise 1.
|
|
|
+ *
|
|
|
+ * spider_net_release_tx_chain releases the tx descriptors that spider has
|
|
|
+ * finished with (if non-brutal) or simply release tx descriptors (if brutal).
|
|
|
+ * If some other context is calling this function, we return 1 so that we're
|
|
|
+ * scheduled again (if we were scheduled) and will not loose initiative.
|
|
|
+ */
|
|
|
+static int
|
|
|
+spider_net_release_tx_chain(struct spider_net_card *card, int brutal)
|
|
|
+{
|
|
|
+ struct spider_net_descr_chain *chain = &card->tx_chain;
|
|
|
+ int status;
|
|
|
+
|
|
|
+ spider_net_read_reg(card, SPIDER_NET_GDTDMACCNTR);
|
|
|
+
|
|
|
+ while (chain->tail != chain->head) {
|
|
|
+ status = spider_net_get_descr_status(chain->tail);
|
|
|
+ switch (status) {
|
|
|
+ case SPIDER_NET_DESCR_COMPLETE:
|
|
|
+ card->netdev_stats.tx_packets++;
|
|
|
+ card->netdev_stats.tx_bytes += chain->tail->skb->len;
|
|
|
+ break;
|
|
|
+
|
|
|
+ case SPIDER_NET_DESCR_CARDOWNED:
|
|
|
+ if (!brutal)
|
|
|
+ return 1;
|
|
|
+ /* fallthrough, if we release the descriptors
|
|
|
+ * brutally (then we don't care about
|
|
|
+ * SPIDER_NET_DESCR_CARDOWNED) */
|
|
|
+
|
|
|
+ case SPIDER_NET_DESCR_RESPONSE_ERROR:
|
|
|
+ case SPIDER_NET_DESCR_PROTECTION_ERROR:
|
|
|
+ case SPIDER_NET_DESCR_FORCE_END:
|
|
|
+ if (netif_msg_tx_err(card))
|
|
|
+ pr_err("%s: forcing end of tx descriptor "
|
|
|
+ "with status x%02x\n",
|
|
|
+ card->netdev->name, status);
|
|
|
+ card->netdev_stats.tx_errors++;
|
|
|
+ break;
|
|
|
+
|
|
|
+ default:
|
|
|
+ card->netdev_stats.tx_dropped++;
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ spider_net_release_tx_descr(card);
|
|
|
+ }
|
|
|
|
|
|
return 0;
|
|
|
}
|
|
@@ -896,18 +763,32 @@ spider_net_prepare_tx_descr(struct spider_net_card *card,
|
|
|
* 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
|
|
|
*/
|
|
|
-static void
|
|
|
-spider_net_kick_tx_dma(struct spider_net_card *card,
|
|
|
- struct spider_net_descr *descr)
|
|
|
+static inline void
|
|
|
+spider_net_kick_tx_dma(struct spider_net_card *card)
|
|
|
{
|
|
|
- /* this is the only descriptor in the output chain.
|
|
|
- * Enable TX DMA */
|
|
|
+ struct spider_net_descr *descr;
|
|
|
|
|
|
- spider_net_write_reg(card, SPIDER_NET_GDTDCHA,
|
|
|
- descr->bus_addr);
|
|
|
+ if (spider_net_read_reg(card, SPIDER_NET_GDTDMACCNTR) &
|
|
|
+ SPIDER_NET_TX_DMA_EN)
|
|
|
+ goto out;
|
|
|
|
|
|
- spider_net_write_reg(card, SPIDER_NET_GDTDMACCNTR,
|
|
|
- SPIDER_NET_DMA_TX_VALUE);
|
|
|
+ descr = card->tx_chain.tail;
|
|
|
+ for (;;) {
|
|
|
+ if (spider_net_get_descr_status(descr) ==
|
|
|
+ SPIDER_NET_DESCR_CARDOWNED) {
|
|
|
+ spider_net_write_reg(card, SPIDER_NET_GDTDCHA,
|
|
|
+ descr->bus_addr);
|
|
|
+ spider_net_write_reg(card, SPIDER_NET_GDTDMACCNTR,
|
|
|
+ SPIDER_NET_DMA_TX_VALUE);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ if (descr == card->tx_chain.head)
|
|
|
+ break;
|
|
|
+ descr = descr->next;
|
|
|
+ }
|
|
|
+
|
|
|
+out:
|
|
|
+ mod_timer(&card->tx_timer, jiffies + SPIDER_NET_TX_TIMER);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -915,47 +796,69 @@ spider_net_kick_tx_dma(struct spider_net_card *card,
|
|
|
* @skb: packet to send out
|
|
|
* @netdev: interface device structure
|
|
|
*
|
|
|
- * returns 0 on success, <0 on failure
|
|
|
+ * returns 0 on success, !0 on failure
|
|
|
*/
|
|
|
static int
|
|
|
spider_net_xmit(struct sk_buff *skb, struct net_device *netdev)
|
|
|
{
|
|
|
struct spider_net_card *card = netdev_priv(netdev);
|
|
|
- struct spider_net_descr *descr;
|
|
|
+ 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);
|
|
|
|
|
|
- descr = spider_net_get_next_tx_descr(card);
|
|
|
+ if (chain->head->next == chain->tail->prev) {
|
|
|
+ card->netdev_stats.tx_dropped++;
|
|
|
+ result = NETDEV_TX_LOCKED;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
|
|
|
- if (!descr)
|
|
|
- goto error;
|
|
|
+ if (spider_net_get_descr_status(descr) != SPIDER_NET_DESCR_NOT_IN_USE) {
|
|
|
+ result = NETDEV_TX_LOCKED;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
|
|
|
- result = spider_net_prepare_tx_descr(card, descr, skb);
|
|
|
- if (result)
|
|
|
- goto error;
|
|
|
+ if (spider_net_prepare_tx_descr(card, skb) != 0) {
|
|
|
+ card->netdev_stats.tx_dropped++;
|
|
|
+ result = NETDEV_TX_BUSY;
|
|
|
+ goto out;
|
|
|
+ }
|
|
|
+
|
|
|
+ result = NETDEV_TX_OK;
|
|
|
|
|
|
+ spider_net_kick_tx_dma(card);
|
|
|
card->tx_chain.head = card->tx_chain.head->next;
|
|
|
|
|
|
- if (spider_net_get_descr_status(descr->prev) !=
|
|
|
- SPIDER_NET_DESCR_CARDOWNED) {
|
|
|
- /* make sure the current descriptor is in memory. Then
|
|
|
- * kicking it on again makes sense, if the previous is not
|
|
|
- * card-owned anymore. Check the previous descriptor twice
|
|
|
- * to omit an mb() in heavy traffic cases */
|
|
|
- mb();
|
|
|
- if (spider_net_get_descr_status(descr->prev) !=
|
|
|
- SPIDER_NET_DESCR_CARDOWNED)
|
|
|
- spider_net_kick_tx_dma(card, descr);
|
|
|
- }
|
|
|
+out:
|
|
|
+ spin_unlock_irqrestore(&chain->lock, flags);
|
|
|
+ netif_wake_queue(netdev);
|
|
|
+ return result;
|
|
|
+}
|
|
|
|
|
|
- mod_timer(&card->tx_timer, jiffies + SPIDER_NET_TX_TIMER);
|
|
|
+/**
|
|
|
+ * 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
|
|
|
+ */
|
|
|
+static void
|
|
|
+spider_net_cleanup_tx_ring(struct spider_net_card *card)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
|
|
|
- return NETDEV_TX_OK;
|
|
|
+ spin_lock_irqsave(&card->tx_chain.lock, flags);
|
|
|
|
|
|
-error:
|
|
|
- card->netdev_stats.tx_dropped++;
|
|
|
- return NETDEV_TX_BUSY;
|
|
|
+ if ((spider_net_release_tx_chain(card, 0) != 0) &&
|
|
|
+ (card->netdev->flags & IFF_UP))
|
|
|
+ spider_net_kick_tx_dma(card);
|
|
|
+
|
|
|
+ spin_unlock_irqrestore(&card->tx_chain.lock, flags);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -1002,7 +905,7 @@ spider_net_pass_skb_up(struct spider_net_descr *descr,
|
|
|
|
|
|
/* unmap descriptor */
|
|
|
pci_unmap_single(card->pdev, descr->buf_addr, SPIDER_NET_MAX_FRAME,
|
|
|
- PCI_DMA_BIDIRECTIONAL);
|
|
|
+ PCI_DMA_FROMDEVICE);
|
|
|
|
|
|
/* the cases we'll throw away the packet immediately */
|
|
|
if (data_error & SPIDER_NET_DESTROY_RX_FLAGS) {
|
|
@@ -1067,14 +970,11 @@ spider_net_pass_skb_up(struct spider_net_descr *descr,
|
|
|
static int
|
|
|
spider_net_decode_one_descr(struct spider_net_card *card, int napi)
|
|
|
{
|
|
|
- enum spider_net_descr_status status;
|
|
|
- struct spider_net_descr *descr;
|
|
|
- struct spider_net_descr_chain *chain;
|
|
|
+ struct spider_net_descr_chain *chain = &card->rx_chain;
|
|
|
+ struct spider_net_descr *descr = chain->tail;
|
|
|
+ int status;
|
|
|
int result;
|
|
|
|
|
|
- chain = &card->rx_chain;
|
|
|
- descr = chain->tail;
|
|
|
-
|
|
|
status = spider_net_get_descr_status(descr);
|
|
|
|
|
|
if (status == SPIDER_NET_DESCR_CARDOWNED) {
|
|
@@ -1103,7 +1003,7 @@ spider_net_decode_one_descr(struct spider_net_card *card, int napi)
|
|
|
card->netdev->name, status);
|
|
|
card->netdev_stats.rx_dropped++;
|
|
|
pci_unmap_single(card->pdev, descr->buf_addr,
|
|
|
- SPIDER_NET_MAX_FRAME, PCI_DMA_BIDIRECTIONAL);
|
|
|
+ SPIDER_NET_MAX_FRAME, PCI_DMA_FROMDEVICE);
|
|
|
dev_kfree_skb_irq(descr->skb);
|
|
|
goto refill;
|
|
|
}
|
|
@@ -1119,7 +1019,7 @@ spider_net_decode_one_descr(struct spider_net_card *card, int napi)
|
|
|
/* ok, we've got a packet in descr */
|
|
|
result = spider_net_pass_skb_up(descr, card, napi);
|
|
|
refill:
|
|
|
- spider_net_set_descr_status(descr, SPIDER_NET_DESCR_NOT_IN_USE);
|
|
|
+ descr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE;
|
|
|
/* change the descriptor state: */
|
|
|
if (!napi)
|
|
|
spider_net_refill_rx_chain(card);
|
|
@@ -1290,21 +1190,6 @@ spider_net_set_mac(struct net_device *netdev, void *p)
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * spider_net_enable_txdmac - enables a TX DMA controller
|
|
|
- * @card: card structure
|
|
|
- *
|
|
|
- * spider_net_enable_txdmac enables the TX DMA controller by setting the
|
|
|
- * descriptor chain tail address
|
|
|
- */
|
|
|
-static void
|
|
|
-spider_net_enable_txdmac(struct spider_net_card *card)
|
|
|
-{
|
|
|
- /* assume chain is aligned correctly */
|
|
|
- spider_net_write_reg(card, SPIDER_NET_GDTDCHA,
|
|
|
- card->tx_chain.tail->bus_addr);
|
|
|
-}
|
|
|
-
|
|
|
/**
|
|
|
* spider_net_handle_rxram_full - cleans up RX ring upon RX RAM full interrupt
|
|
|
* @card: card structure
|
|
@@ -1653,7 +1538,6 @@ spider_net_enable_card(struct spider_net_card *card)
|
|
|
{ SPIDER_NET_GMRWOLCTRL, 0 },
|
|
|
{ SPIDER_NET_GTESTMD, 0x10000000 },
|
|
|
{ SPIDER_NET_GTTQMSK, 0x00400040 },
|
|
|
- { SPIDER_NET_GTESTMD, 0 },
|
|
|
|
|
|
{ SPIDER_NET_GMACINTEN, 0 },
|
|
|
|
|
@@ -1692,9 +1576,6 @@ spider_net_enable_card(struct spider_net_card *card)
|
|
|
|
|
|
spider_net_write_reg(card, SPIDER_NET_GRXDMAEN, SPIDER_NET_WOL_VALUE);
|
|
|
|
|
|
- /* set chain tail adress for TX chain */
|
|
|
- spider_net_enable_txdmac(card);
|
|
|
-
|
|
|
spider_net_write_reg(card, SPIDER_NET_GMACLENLMT,
|
|
|
SPIDER_NET_LENLMT_VALUE);
|
|
|
spider_net_write_reg(card, SPIDER_NET_GMACMODE,
|
|
@@ -1709,6 +1590,9 @@ spider_net_enable_card(struct spider_net_card *card)
|
|
|
SPIDER_NET_INT1_MASK_VALUE);
|
|
|
spider_net_write_reg(card, SPIDER_NET_GHIINT2MSK,
|
|
|
SPIDER_NET_INT2_MASK_VALUE);
|
|
|
+
|
|
|
+ spider_net_write_reg(card, SPIDER_NET_GDTDMACCNTR,
|
|
|
+ SPIDER_NET_GDTDCEIDIS);
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -1728,10 +1612,12 @@ spider_net_open(struct net_device *netdev)
|
|
|
|
|
|
result = -ENOMEM;
|
|
|
if (spider_net_init_chain(card, &card->tx_chain,
|
|
|
- card->descr, tx_descriptors))
|
|
|
+ card->descr,
|
|
|
+ PCI_DMA_TODEVICE, tx_descriptors))
|
|
|
goto alloc_tx_failed;
|
|
|
if (spider_net_init_chain(card, &card->rx_chain,
|
|
|
- card->descr + tx_descriptors, rx_descriptors))
|
|
|
+ card->descr + tx_descriptors,
|
|
|
+ PCI_DMA_FROMDEVICE, rx_descriptors))
|
|
|
goto alloc_rx_failed;
|
|
|
|
|
|
/* allocate rx skbs */
|
|
@@ -1954,6 +1840,49 @@ spider_net_workaround_rxramfull(struct spider_net_card *card)
|
|
|
SPIDER_NET_CKRCTRL_STOP_VALUE);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * spider_net_stop - called upon ifconfig down
|
|
|
+ * @netdev: interface device structure
|
|
|
+ *
|
|
|
+ * always returns 0
|
|
|
+ */
|
|
|
+int
|
|
|
+spider_net_stop(struct net_device *netdev)
|
|
|
+{
|
|
|
+ struct spider_net_card *card = netdev_priv(netdev);
|
|
|
+
|
|
|
+ tasklet_kill(&card->rxram_full_tl);
|
|
|
+ netif_poll_disable(netdev);
|
|
|
+ netif_carrier_off(netdev);
|
|
|
+ netif_stop_queue(netdev);
|
|
|
+ del_timer_sync(&card->tx_timer);
|
|
|
+
|
|
|
+ /* disable/mask all interrupts */
|
|
|
+ spider_net_write_reg(card, SPIDER_NET_GHIINT0MSK, 0);
|
|
|
+ spider_net_write_reg(card, SPIDER_NET_GHIINT1MSK, 0);
|
|
|
+ spider_net_write_reg(card, SPIDER_NET_GHIINT2MSK, 0);
|
|
|
+
|
|
|
+ /* free_irq(netdev->irq, netdev);*/
|
|
|
+ free_irq(to_pci_dev(netdev->class_dev.dev)->irq, netdev);
|
|
|
+
|
|
|
+ spider_net_write_reg(card, SPIDER_NET_GDTDMACCNTR,
|
|
|
+ SPIDER_NET_DMA_TX_FEND_VALUE);
|
|
|
+
|
|
|
+ /* turn off DMA, force end */
|
|
|
+ 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_free_chain(card, &card->tx_chain);
|
|
|
+ spider_net_free_chain(card, &card->rx_chain);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* spider_net_tx_timeout_task - task scheduled by the watchdog timeout
|
|
|
* function (to be called not under interrupt status)
|
|
@@ -1982,7 +1911,7 @@ spider_net_tx_timeout_task(void *data)
|
|
|
goto out;
|
|
|
|
|
|
spider_net_open(netdev);
|
|
|
- spider_net_kick_tx_dma(card, card->tx_chain.head);
|
|
|
+ spider_net_kick_tx_dma(card);
|
|
|
netif_device_attach(netdev);
|
|
|
|
|
|
out:
|
|
@@ -2065,7 +1994,6 @@ spider_net_setup_netdev(struct spider_net_card *card)
|
|
|
|
|
|
pci_set_drvdata(card->pdev, netdev);
|
|
|
|
|
|
- atomic_set(&card->tx_chain_release,0);
|
|
|
card->rxram_full_tl.data = (unsigned long) card;
|
|
|
card->rxram_full_tl.func =
|
|
|
(void (*)(unsigned long)) spider_net_handle_rxram_full;
|
|
@@ -2079,7 +2007,7 @@ spider_net_setup_netdev(struct spider_net_card *card)
|
|
|
|
|
|
spider_net_setup_netdev_ops(netdev);
|
|
|
|
|
|
- netdev->features = NETIF_F_HW_CSUM;
|
|
|
+ netdev->features = NETIF_F_HW_CSUM | NETIF_F_LLTX;
|
|
|
/* some time: NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX |
|
|
|
* NETIF_F_HW_VLAN_FILTER */
|
|
|
|