Commit 11f1a52b authored by Arnd Bergmann's avatar Arnd Bergmann Committed by Jeff Garzik

[PATCH] spidernet: performance optimizations

Performance optimizations, changes in these areas:
  - RX and TX checksum offload
  - correct maximum MTU
  - don't use TX interrupts anymore, use a timer instead
  - remove some superfluous barriers
  - improve RX RAM full handling

From: Utz Bacher <utz.bacher@de.ibm.com>
Signed-off-by: default avatarJens Osterkamp <jens.osterkamp@de.ibm.com>
Signed-off-by: default avatarArnd Bergmann <arndb@de.ibm.com>
Signed-off-by: default avatarJeff Garzik <jgarzik@pobox.com>
parent 8e0a613b
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
*/ */
#include <linux/config.h> #include <linux/config.h>
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/crc32.h> #include <linux/crc32.h>
#include <linux/delay.h> #include <linux/delay.h>
...@@ -43,6 +42,7 @@ ...@@ -43,6 +42,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/tcp.h> #include <linux/tcp.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/vmalloc.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/workqueue.h> #include <linux/workqueue.h>
#include <asm/bitops.h> #include <asm/bitops.h>
...@@ -108,42 +108,6 @@ spider_net_write_reg(struct spider_net_card *card, u32 reg, u32 value) ...@@ -108,42 +108,6 @@ spider_net_write_reg(struct spider_net_card *card, u32 reg, u32 value)
writel(value, card->regs + reg); writel(value, card->regs + reg);
} }
/**
* spider_net_write_reg_sync - writes to an SMMIO register of a card
* @card: device structure
* @reg: register to write to
* @value: value to write into the specified SMMIO register
*
* Unlike spider_net_write_reg, this will also make sure the
* data arrives on the card by reading the reg again.
*/
static void
spider_net_write_reg_sync(struct spider_net_card *card, u32 reg, u32 value)
{
value = cpu_to_le32(value);
writel(value, card->regs + reg);
(void)readl(card->regs + reg);
}
/**
* spider_net_rx_irq_off - switch off rx irq on this spider card
* @card: device structure
*
* switches off rx irq by masking them out in the GHIINTnMSK register
*/
static void
spider_net_rx_irq_off(struct spider_net_card *card)
{
u32 regvalue;
unsigned long flags;
spin_lock_irqsave(&card->intmask_lock, flags);
regvalue = spider_net_read_reg(card, SPIDER_NET_GHIINT0MSK);
regvalue &= ~SPIDER_NET_RXINT;
spider_net_write_reg_sync(card, SPIDER_NET_GHIINT0MSK, regvalue);
spin_unlock_irqrestore(&card->intmask_lock, flags);
}
/** spider_net_write_phy - write to phy register /** spider_net_write_phy - write to phy register
* @netdev: adapter to be written to * @netdev: adapter to be written to
* @mii_id: id of MII * @mii_id: id of MII
...@@ -199,60 +163,33 @@ spider_net_read_phy(struct net_device *netdev, int mii_id, int reg) ...@@ -199,60 +163,33 @@ spider_net_read_phy(struct net_device *netdev, int mii_id, int reg)
} }
/** /**
* spider_net_rx_irq_on - switch on rx irq on this spider card * spider_net_rx_irq_off - switch off rx irq on this spider card
* @card: device structure
*
* switches on rx irq by enabling them in the GHIINTnMSK register
*/
static void
spider_net_rx_irq_on(struct spider_net_card *card)
{
u32 regvalue;
unsigned long flags;
spin_lock_irqsave(&card->intmask_lock, flags);
regvalue = spider_net_read_reg(card, SPIDER_NET_GHIINT0MSK);
regvalue |= SPIDER_NET_RXINT;
spider_net_write_reg_sync(card, SPIDER_NET_GHIINT0MSK, regvalue);
spin_unlock_irqrestore(&card->intmask_lock, flags);
}
/**
* spider_net_tx_irq_off - switch off tx irq on this spider card
* @card: device structure * @card: device structure
* *
* switches off tx irq by masking them out in the GHIINTnMSK register * switches off rx irq by masking them out in the GHIINTnMSK register
*/ */
static void static void
spider_net_tx_irq_off(struct spider_net_card *card) spider_net_rx_irq_off(struct spider_net_card *card)
{ {
u32 regvalue; u32 regvalue;
unsigned long flags;
spin_lock_irqsave(&card->intmask_lock, flags); regvalue = SPIDER_NET_INT0_MASK_VALUE & (~SPIDER_NET_RXINT);
regvalue = spider_net_read_reg(card, SPIDER_NET_GHIINT0MSK); spider_net_write_reg(card, SPIDER_NET_GHIINT0MSK, regvalue);
regvalue &= ~SPIDER_NET_TXINT;
spider_net_write_reg_sync(card, SPIDER_NET_GHIINT0MSK, regvalue);
spin_unlock_irqrestore(&card->intmask_lock, flags);
} }
/** /**
* spider_net_tx_irq_on - switch on tx irq on this spider card * spider_net_rx_irq_on - switch on rx irq on this spider card
* @card: device structure * @card: device structure
* *
* switches on tx irq by enabling them in the GHIINTnMSK register * switches on rx irq by enabling them in the GHIINTnMSK register
*/ */
static void static void
spider_net_tx_irq_on(struct spider_net_card *card) spider_net_rx_irq_on(struct spider_net_card *card)
{ {
u32 regvalue; u32 regvalue;
unsigned long flags;
spin_lock_irqsave(&card->intmask_lock, flags); regvalue = SPIDER_NET_INT0_MASK_VALUE | SPIDER_NET_RXINT;
regvalue = spider_net_read_reg(card, SPIDER_NET_GHIINT0MSK); spider_net_write_reg(card, SPIDER_NET_GHIINT0MSK, regvalue);
regvalue |= SPIDER_NET_TXINT;
spider_net_write_reg_sync(card, SPIDER_NET_GHIINT0MSK, regvalue);
spin_unlock_irqrestore(&card->intmask_lock, flags);
} }
/** /**
...@@ -326,9 +263,8 @@ static enum spider_net_descr_status ...@@ -326,9 +263,8 @@ static enum spider_net_descr_status
spider_net_get_descr_status(struct spider_net_descr *descr) spider_net_get_descr_status(struct spider_net_descr *descr)
{ {
u32 cmd_status; u32 cmd_status;
rmb();
cmd_status = descr->dmac_cmd_status; cmd_status = descr->dmac_cmd_status;
rmb();
cmd_status >>= SPIDER_NET_DESCR_IND_PROC_SHIFT; cmd_status >>= SPIDER_NET_DESCR_IND_PROC_SHIFT;
/* no need to mask out any bits, as cmd_status is 32 bits wide only /* no need to mask out any bits, as cmd_status is 32 bits wide only
* (and unsigned) */ * (and unsigned) */
...@@ -349,7 +285,6 @@ spider_net_set_descr_status(struct spider_net_descr *descr, ...@@ -349,7 +285,6 @@ spider_net_set_descr_status(struct spider_net_descr *descr,
{ {
u32 cmd_status; u32 cmd_status;
/* read the status */ /* read the status */
mb();
cmd_status = descr->dmac_cmd_status; cmd_status = descr->dmac_cmd_status;
/* clean the upper 4 bits */ /* clean the upper 4 bits */
cmd_status &= SPIDER_NET_DESCR_IND_PROC_MASKO; cmd_status &= SPIDER_NET_DESCR_IND_PROC_MASKO;
...@@ -357,7 +292,6 @@ spider_net_set_descr_status(struct spider_net_descr *descr, ...@@ -357,7 +292,6 @@ spider_net_set_descr_status(struct spider_net_descr *descr,
cmd_status |= ((u32)status)<<SPIDER_NET_DESCR_IND_PROC_SHIFT; cmd_status |= ((u32)status)<<SPIDER_NET_DESCR_IND_PROC_SHIFT;
/* and write it back */ /* and write it back */
descr->dmac_cmd_status = cmd_status; descr->dmac_cmd_status = cmd_status;
wmb();
} }
/** /**
...@@ -398,8 +332,9 @@ spider_net_init_chain(struct spider_net_card *card, ...@@ -398,8 +332,9 @@ spider_net_init_chain(struct spider_net_card *card,
{ {
int i; int i;
struct spider_net_descr *descr; struct spider_net_descr *descr;
dma_addr_t buf;
spin_lock_init(&card->chain_lock); atomic_set(&card->rx_chain_refill,0);
descr = start_descr; descr = start_descr;
memset(descr, 0, sizeof(*descr) * no); memset(descr, 0, sizeof(*descr) * no);
...@@ -408,14 +343,14 @@ spider_net_init_chain(struct spider_net_card *card, ...@@ -408,14 +343,14 @@ spider_net_init_chain(struct spider_net_card *card,
for (i=0; i<no; i++, descr++) { for (i=0; i<no; i++, descr++) {
spider_net_set_descr_status(descr, SPIDER_NET_DESCR_NOT_IN_USE); spider_net_set_descr_status(descr, SPIDER_NET_DESCR_NOT_IN_USE);
descr->bus_addr = buf = pci_map_single(card->pdev, descr,
pci_map_single(card->pdev, descr, SPIDER_NET_DESCR_SIZE,
SPIDER_NET_DESCR_SIZE, PCI_DMA_BIDIRECTIONAL);
PCI_DMA_BIDIRECTIONAL);
if (descr->bus_addr == DMA_ERROR_CODE) if (buf == DMA_ERROR_CODE)
goto iommu_error; goto iommu_error;
descr->bus_addr = buf;
descr->next = descr + 1; descr->next = descr + 1;
descr->prev = descr - 1; descr->prev = descr - 1;
...@@ -439,7 +374,8 @@ spider_net_init_chain(struct spider_net_card *card, ...@@ -439,7 +374,8 @@ spider_net_init_chain(struct spider_net_card *card,
for (i=0; i < no; i++, descr++) for (i=0; i < no; i++, descr++)
if (descr->bus_addr) if (descr->bus_addr)
pci_unmap_single(card->pdev, descr->bus_addr, pci_unmap_single(card->pdev, descr->bus_addr,
SPIDER_NET_DESCR_SIZE, PCI_DMA_BIDIRECTIONAL); SPIDER_NET_DESCR_SIZE,
PCI_DMA_BIDIRECTIONAL);
return -ENOMEM; return -ENOMEM;
} }
...@@ -459,7 +395,7 @@ spider_net_free_rx_chain_contents(struct spider_net_card *card) ...@@ -459,7 +395,7 @@ spider_net_free_rx_chain_contents(struct spider_net_card *card)
if (descr->skb) { if (descr->skb) {
dev_kfree_skb(descr->skb); dev_kfree_skb(descr->skb);
pci_unmap_single(card->pdev, descr->buf_addr, pci_unmap_single(card->pdev, descr->buf_addr,
SPIDER_NET_MAX_MTU, SPIDER_NET_MAX_FRAME,
PCI_DMA_BIDIRECTIONAL); PCI_DMA_BIDIRECTIONAL);
} }
descr = descr->next; descr = descr->next;
...@@ -486,7 +422,7 @@ spider_net_prepare_rx_descr(struct spider_net_card *card, ...@@ -486,7 +422,7 @@ spider_net_prepare_rx_descr(struct spider_net_card *card,
int bufsize; int bufsize;
/* we need to round up the buffer size to a multiple of 128 */ /* we need to round up the buffer size to a multiple of 128 */
bufsize = (SPIDER_NET_MAX_MTU + SPIDER_NET_RXBUF_ALIGN - 1) & bufsize = (SPIDER_NET_MAX_FRAME + SPIDER_NET_RXBUF_ALIGN - 1) &
(~(SPIDER_NET_RXBUF_ALIGN - 1)); (~(SPIDER_NET_RXBUF_ALIGN - 1));
/* and we need to have it 128 byte aligned, therefore we allocate a /* and we need to have it 128 byte aligned, therefore we allocate a
...@@ -494,10 +430,8 @@ spider_net_prepare_rx_descr(struct spider_net_card *card, ...@@ -494,10 +430,8 @@ spider_net_prepare_rx_descr(struct spider_net_card *card,
/* allocate an skb */ /* allocate an skb */
descr->skb = dev_alloc_skb(bufsize + SPIDER_NET_RXBUF_ALIGN - 1); descr->skb = dev_alloc_skb(bufsize + SPIDER_NET_RXBUF_ALIGN - 1);
if (!descr->skb) { if (!descr->skb) {
if (net_ratelimit()) if (netif_msg_rx_err(card) && net_ratelimit())
if (netif_msg_rx_err(card)) pr_err("Not enough memory to allocate rx buffer\n");
pr_err("Not enough memory to allocate "
"rx buffer\n");
return -ENOMEM; return -ENOMEM;
} }
descr->buf_size = bufsize; descr->buf_size = bufsize;
...@@ -512,12 +446,11 @@ spider_net_prepare_rx_descr(struct spider_net_card *card, ...@@ -512,12 +446,11 @@ spider_net_prepare_rx_descr(struct spider_net_card *card,
skb_reserve(descr->skb, SPIDER_NET_RXBUF_ALIGN - offset); skb_reserve(descr->skb, SPIDER_NET_RXBUF_ALIGN - offset);
/* io-mmu-map the skb */ /* io-mmu-map the skb */
buf = pci_map_single(card->pdev, descr->skb->data, buf = pci_map_single(card->pdev, descr->skb->data,
SPIDER_NET_MAX_MTU, SPIDER_NET_MAX_FRAME, PCI_DMA_BIDIRECTIONAL);
PCI_DMA_BIDIRECTIONAL);
descr->buf_addr = buf; descr->buf_addr = buf;
if (buf == DMA_ERROR_CODE) { if (buf == DMA_ERROR_CODE) {
dev_kfree_skb_any(descr->skb); dev_kfree_skb_any(descr->skb);
if (netif_msg_rx_err(card)) if (netif_msg_rx_err(card) && net_ratelimit())
pr_err("Could not iommu-map rx buffer\n"); pr_err("Could not iommu-map rx buffer\n");
spider_net_set_descr_status(descr, SPIDER_NET_DESCR_NOT_IN_USE); spider_net_set_descr_status(descr, SPIDER_NET_DESCR_NOT_IN_USE);
} else { } else {
...@@ -528,10 +461,10 @@ spider_net_prepare_rx_descr(struct spider_net_card *card, ...@@ -528,10 +461,10 @@ spider_net_prepare_rx_descr(struct spider_net_card *card,
} }
/** /**
* spider_net_enable_rxctails - sets RX dmac chain tail addresses * spider_net_enable_rxchtails - sets RX dmac chain tail addresses
* @card: card structure * @card: card structure
* *
* spider_net_enable_rxctails sets the RX DMAC chain tail adresses in the * spider_net_enable_rxchtails sets the RX DMAC chain tail adresses in the
* chip by writing to the appropriate register. DMA is enabled in * chip by writing to the appropriate register. DMA is enabled in
* spider_net_enable_rxdmac. * spider_net_enable_rxdmac.
*/ */
...@@ -553,6 +486,7 @@ spider_net_enable_rxchtails(struct spider_net_card *card) ...@@ -553,6 +486,7 @@ spider_net_enable_rxchtails(struct spider_net_card *card)
static void static void
spider_net_enable_rxdmac(struct spider_net_card *card) spider_net_enable_rxdmac(struct spider_net_card *card)
{ {
wmb();
spider_net_write_reg(card, SPIDER_NET_GDADMACCNTR, spider_net_write_reg(card, SPIDER_NET_GDADMACCNTR,
SPIDER_NET_DMA_RX_VALUE); SPIDER_NET_DMA_RX_VALUE);
} }
...@@ -561,32 +495,28 @@ spider_net_enable_rxdmac(struct spider_net_card *card) ...@@ -561,32 +495,28 @@ spider_net_enable_rxdmac(struct spider_net_card *card)
* spider_net_refill_rx_chain - refills descriptors/skbs in the rx chains * spider_net_refill_rx_chain - refills descriptors/skbs in the rx chains
* @card: card structure * @card: card structure
* *
* refills descriptors in all chains (last used chain first): allocates skbs * refills descriptors in the rx chain: allocates skbs and iommu-maps them.
* and iommu-maps them.
*/ */
static void static void
spider_net_refill_rx_chain(struct spider_net_card *card) spider_net_refill_rx_chain(struct spider_net_card *card)
{ {
struct spider_net_descr_chain *chain; struct spider_net_descr_chain *chain;
int count = 0;
unsigned long flags;
chain = &card->rx_chain; chain = &card->rx_chain;
spin_lock_irqsave(&card->chain_lock, flags); /* one context doing the refill (and a second context seeing that
while (spider_net_get_descr_status(chain->head) == * and omitting it) is ok. If called by NAPI, we'll be called again
SPIDER_NET_DESCR_NOT_IN_USE) { * as spider_net_decode_one_descr is called several times. If some
if (spider_net_prepare_rx_descr(card, chain->head)) * interrupt calls us, the NAPI is about to clean up anyway. */
break; if (atomic_inc_return(&card->rx_chain_refill) == 1)
count++; while (spider_net_get_descr_status(chain->head) ==
chain->head = chain->head->next; SPIDER_NET_DESCR_NOT_IN_USE) {
} if (spider_net_prepare_rx_descr(card, chain->head))
spin_unlock_irqrestore(&card->chain_lock, flags); break;
chain->head = chain->head->next;
}
/* could be optimized, only do that, if we know the DMA processing atomic_dec(&card->rx_chain_refill);
* has terminated */
if (count)
spider_net_enable_rxdmac(card);
} }
/** /**
...@@ -615,6 +545,7 @@ spider_net_alloc_rx_skbs(struct spider_net_card *card) ...@@ -615,6 +545,7 @@ spider_net_alloc_rx_skbs(struct spider_net_card *card)
/* this will allocate the rest of the rx buffers; if not, it's /* this will allocate the rest of the rx buffers; if not, it's
* business as usual later on */ * business as usual later on */
spider_net_refill_rx_chain(card); spider_net_refill_rx_chain(card);
spider_net_enable_rxdmac(card);
return 0; return 0;
error: error:
...@@ -651,24 +582,30 @@ spider_net_release_tx_descr(struct spider_net_card *card, ...@@ -651,24 +582,30 @@ spider_net_release_tx_descr(struct spider_net_card *card,
* @card: adapter structure * @card: adapter structure
* @brutal: if set, don't care about whether descriptor seems to be in use * @brutal: if set, don't care about whether descriptor seems to be in use
* *
* releases the tx descriptors that spider has finished with (if non-brutal) * returns 0 if the tx ring is empty, otherwise 1.
* or simply release tx descriptors (if brutal) *
* 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 void static int
spider_net_release_tx_chain(struct spider_net_card *card, int brutal) spider_net_release_tx_chain(struct spider_net_card *card, int brutal)
{ {
struct spider_net_descr_chain *tx_chain = &card->tx_chain; struct spider_net_descr_chain *tx_chain = &card->tx_chain;
enum spider_net_descr_status status; enum spider_net_descr_status status;
spider_net_tx_irq_off(card); if (atomic_inc_return(&card->tx_chain_release) != 1) {
atomic_dec(&card->tx_chain_release);
return 1;
}
/* no lock for chain needed, if this is only executed once at a time */
again:
for (;;) { for (;;) {
status = spider_net_get_descr_status(tx_chain->tail); status = spider_net_get_descr_status(tx_chain->tail);
switch (status) { switch (status) {
case SPIDER_NET_DESCR_CARDOWNED: case SPIDER_NET_DESCR_CARDOWNED:
if (!brutal) goto out; if (!brutal)
goto out;
/* fallthrough, if we release the descriptors /* fallthrough, if we release the descriptors
* brutally (then we don't care about * brutally (then we don't care about
* SPIDER_NET_DESCR_CARDOWNED) */ * SPIDER_NET_DESCR_CARDOWNED) */
...@@ -695,25 +632,30 @@ spider_net_release_tx_chain(struct spider_net_card *card, int brutal) ...@@ -695,25 +632,30 @@ spider_net_release_tx_chain(struct spider_net_card *card, int brutal)
tx_chain->tail = tx_chain->tail->next; tx_chain->tail = tx_chain->tail->next;
} }
out: out:
atomic_dec(&card->tx_chain_release);
netif_wake_queue(card->netdev); netif_wake_queue(card->netdev);
if (!brutal) { if (status == SPIDER_NET_DESCR_CARDOWNED)
/* switch on tx irqs (while we are still in the interrupt return 1;
* handler, so we don't get an interrupt), check again return 0;
* for done descriptors. This results in fewer interrupts */ }
spider_net_tx_irq_on(card);
status = spider_net_get_descr_status(tx_chain->tail);
switch (status) {
case SPIDER_NET_DESCR_RESPONSE_ERROR:
case SPIDER_NET_DESCR_PROTECTION_ERROR:
case SPIDER_NET_DESCR_FORCE_END:
case SPIDER_NET_DESCR_COMPLETE:
goto again;
default:
break;
}
}
/**
* 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);
}
} }
/** /**
...@@ -728,16 +670,22 @@ spider_net_release_tx_chain(struct spider_net_card *card, int brutal) ...@@ -728,16 +670,22 @@ spider_net_release_tx_chain(struct spider_net_card *card, int brutal)
static u8 static u8
spider_net_get_multicast_hash(struct net_device *netdev, __u8 *addr) spider_net_get_multicast_hash(struct net_device *netdev, __u8 *addr)
{ {
/* FIXME: an addr of 01:00:5e:00:00:01 must result in 0xa9,
* ff:ff:ff:ff:ff:ff must result in 0xfd */
u32 crc; u32 crc;
u8 hash; u8 hash;
char addr_for_crc[ETH_ALEN] = { 0, };
int i, bit;
for (i = 0; i < ETH_ALEN * 8; i++) {
bit = (addr[i / 8] >> (i % 8)) & 1;
addr_for_crc[ETH_ALEN - 1 - i / 8] += bit << (7 - (i % 8));
}
crc = crc32_be(~0, addr, netdev->addr_len); crc = crc32_be(~0, addr_for_crc, netdev->addr_len);
hash = (crc >> 27); hash = (crc >> 27);
hash <<= 3; hash <<= 3;
hash |= crc & 7; hash |= crc & 7;
hash &= 0xff;
return hash; return hash;
} }
...@@ -823,9 +771,11 @@ spider_net_stop(struct net_device *netdev) ...@@ -823,9 +771,11 @@ spider_net_stop(struct net_device *netdev)
{ {
struct spider_net_card *card = netdev_priv(netdev); struct spider_net_card *card = netdev_priv(netdev);
tasklet_kill(&card->rxram_full_tl);
netif_poll_disable(netdev); netif_poll_disable(netdev);
netif_carrier_off(netdev); netif_carrier_off(netdev);
netif_stop_queue(netdev); netif_stop_queue(netdev);
del_timer_sync(&card->tx_timer);
/* disable/mask all interrupts */ /* disable/mask all interrupts */
spider_net_write_reg(card, SPIDER_NET_GHIINT0MSK, 0); spider_net_write_reg(card, SPIDER_NET_GHIINT0MSK, 0);
...@@ -874,13 +824,15 @@ spider_net_get_next_tx_descr(struct spider_net_card *card) ...@@ -874,13 +824,15 @@ spider_net_get_next_tx_descr(struct spider_net_card *card)
* @skb: packet to consider * @skb: packet to consider
* *
* fills out the command and status field of the descriptor structure, * fills out the command and status field of the descriptor structure,
* depending on hardware checksum settings. This function assumes a wmb() * depending on hardware checksum settings.
* has executed before.
*/ */
static void static void
spider_net_set_txdescr_cmdstat(struct spider_net_descr *descr, spider_net_set_txdescr_cmdstat(struct spider_net_descr *descr,
struct sk_buff *skb) struct sk_buff *skb)
{ {
/* make sure the other fields in the descriptor are written */
wmb();
if (skb->ip_summed != CHECKSUM_HW) { if (skb->ip_summed != CHECKSUM_HW) {
descr->dmac_cmd_status = SPIDER_NET_DMAC_CMDSTAT_NOCS; descr->dmac_cmd_status = SPIDER_NET_DMAC_CMDSTAT_NOCS;
return; return;
...@@ -889,14 +841,13 @@ spider_net_set_txdescr_cmdstat(struct spider_net_descr *descr, ...@@ -889,14 +841,13 @@ spider_net_set_txdescr_cmdstat(struct spider_net_descr *descr,
/* is packet ip? /* is packet ip?
* if yes: tcp? udp? */ * if yes: tcp? udp? */
if (skb->protocol == htons(ETH_P_IP)) { if (skb->protocol == htons(ETH_P_IP)) {
if (skb->nh.iph->protocol == IPPROTO_TCP) { if (skb->nh.iph->protocol == IPPROTO_TCP)
descr->dmac_cmd_status = SPIDER_NET_DMAC_CMDSTAT_TCPCS; descr->dmac_cmd_status = SPIDER_NET_DMAC_CMDSTAT_TCPCS;
} else if (skb->nh.iph->protocol == IPPROTO_UDP) { else if (skb->nh.iph->protocol == IPPROTO_UDP)
descr->dmac_cmd_status = SPIDER_NET_DMAC_CMDSTAT_UDPCS; descr->dmac_cmd_status = SPIDER_NET_DMAC_CMDSTAT_UDPCS;
} else { /* the stack should checksum non-tcp and non-udp else /* the stack should checksum non-tcp and non-udp
packets on his own: NETIF_F_IP_CSUM */ packets on his own: NETIF_F_IP_CSUM */
descr->dmac_cmd_status = SPIDER_NET_DMAC_CMDSTAT_NOCS; descr->dmac_cmd_status = SPIDER_NET_DMAC_CMDSTAT_NOCS;
}
} }
} }
...@@ -916,10 +867,12 @@ spider_net_prepare_tx_descr(struct spider_net_card *card, ...@@ -916,10 +867,12 @@ spider_net_prepare_tx_descr(struct spider_net_card *card,
struct spider_net_descr *descr, struct spider_net_descr *descr,
struct sk_buff *skb) struct sk_buff *skb)
{ {
dma_addr_t buf = pci_map_single(card->pdev, skb->data, dma_addr_t buf;
skb->len, PCI_DMA_BIDIRECTIONAL);
buf = pci_map_single(card->pdev, skb->data,
skb->len, PCI_DMA_BIDIRECTIONAL);
if (buf == DMA_ERROR_CODE) { if (buf == DMA_ERROR_CODE) {
if (netif_msg_tx_err(card)) if (netif_msg_tx_err(card) && net_ratelimit())
pr_err("could not iommu-map packet (%p, %i). " pr_err("could not iommu-map packet (%p, %i). "
"Dropping packet\n", skb->data, skb->len); "Dropping packet\n", skb->data, skb->len);
return -ENOMEM; return -ENOMEM;
...@@ -930,10 +883,6 @@ spider_net_prepare_tx_descr(struct spider_net_card *card, ...@@ -930,10 +883,6 @@ spider_net_prepare_tx_descr(struct spider_net_card *card,
descr->skb = skb; descr->skb = skb;
descr->data_status = 0; descr->data_status = 0;
/* make sure the above values are in memory before we change the
* status */
wmb();
spider_net_set_txdescr_cmdstat(descr,skb); spider_net_set_txdescr_cmdstat(descr,skb);
return 0; return 0;
...@@ -975,17 +924,12 @@ spider_net_xmit(struct sk_buff *skb, struct net_device *netdev) ...@@ -975,17 +924,12 @@ spider_net_xmit(struct sk_buff *skb, struct net_device *netdev)
struct spider_net_descr *descr; struct spider_net_descr *descr;
int result; int result;
descr = spider_net_get_next_tx_descr(card); spider_net_release_tx_chain(card, 0);
if (!descr) { descr = spider_net_get_next_tx_descr(card);
netif_stop_queue(netdev);
descr = spider_net_get_next_tx_descr(card); if (!descr)
if (!descr) goto error;
goto error;
else
netif_start_queue(netdev);
}
result = spider_net_prepare_tx_descr(card, descr, skb); result = spider_net_prepare_tx_descr(card, descr, skb);
if (result) if (result)
...@@ -993,19 +937,25 @@ spider_net_xmit(struct sk_buff *skb, struct net_device *netdev) ...@@ -993,19 +937,25 @@ spider_net_xmit(struct sk_buff *skb, struct net_device *netdev)
card->tx_chain.head = card->tx_chain.head->next; card->tx_chain.head = card->tx_chain.head->next;
/* make sure the status from spider_net_prepare_tx_descr is in
* memory before we check out the previous descriptor */
wmb();
if (spider_net_get_descr_status(descr->prev) != if (spider_net_get_descr_status(descr->prev) !=
SPIDER_NET_DESCR_CARDOWNED) SPIDER_NET_DESCR_CARDOWNED) {
spider_net_kick_tx_dma(card, descr); /* 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);
}
mod_timer(&card->tx_timer, jiffies + SPIDER_NET_TX_TIMER);
return NETDEV_TX_OK; return NETDEV_TX_OK;
error: error:
card->netdev_stats.tx_dropped++; card->netdev_stats.tx_dropped++;
return NETDEV_TX_LOCKED; return NETDEV_TX_BUSY;
} }
/** /**
...@@ -1030,6 +980,7 @@ spider_net_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) ...@@ -1030,6 +980,7 @@ spider_net_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
* spider_net_pass_skb_up - takes an skb from a descriptor and passes it on * spider_net_pass_skb_up - takes an skb from a descriptor and passes it on
* @descr: descriptor to process * @descr: descriptor to process
* @card: card structure * @card: card structure
* @napi: whether caller is in NAPI context
* *
* returns 1 on success, 0 if no packet was passed to the stack * returns 1 on success, 0 if no packet was passed to the stack
* *
...@@ -1038,7 +989,7 @@ spider_net_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) ...@@ -1038,7 +989,7 @@ spider_net_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
*/ */
static int static int
spider_net_pass_skb_up(struct spider_net_descr *descr, spider_net_pass_skb_up(struct spider_net_descr *descr,
struct spider_net_card *card) struct spider_net_card *card, int napi)
{ {
struct sk_buff *skb; struct sk_buff *skb;
struct net_device *netdev; struct net_device *netdev;
...@@ -1049,22 +1000,20 @@ spider_net_pass_skb_up(struct spider_net_descr *descr, ...@@ -1049,22 +1000,20 @@ spider_net_pass_skb_up(struct spider_net_descr *descr,
netdev = card->netdev; netdev = card->netdev;
/* check for errors in the data_error flag */ /* unmap descriptor */
if ((data_error & SPIDER_NET_DATA_ERROR_MASK) && pci_unmap_single(card->pdev, descr->buf_addr, SPIDER_NET_MAX_FRAME,
netif_msg_rx_err(card))
pr_err("error in received descriptor found, "
"data_status=x%08x, data_error=x%08x\n",
data_status, data_error);
/* prepare skb, unmap descriptor */
skb = descr->skb;
pci_unmap_single(card->pdev, descr->buf_addr, SPIDER_NET_MAX_MTU,
PCI_DMA_BIDIRECTIONAL); PCI_DMA_BIDIRECTIONAL);
/* the cases we'll throw away the packet immediately */ /* the cases we'll throw away the packet immediately */
if (data_error & SPIDER_NET_DESTROY_RX_FLAGS) if (data_error & SPIDER_NET_DESTROY_RX_FLAGS) {
if (netif_msg_rx_err(card))
pr_err("error in received descriptor found, "
"data_status=x%08x, data_error=x%08x\n",
data_status, data_error);
return 0; return 0;
}
skb = descr->skb;
skb->dev = netdev; skb->dev = netdev;
skb_put(skb, descr->valid_size); skb_put(skb, descr->valid_size);
...@@ -1076,14 +1025,14 @@ spider_net_pass_skb_up(struct spider_net_descr *descr, ...@@ -1076,14 +1025,14 @@ spider_net_pass_skb_up(struct spider_net_descr *descr,
/* checksum offload */ /* checksum offload */
if (card->options.rx_csum) { if (card->options.rx_csum) {
if ( (data_status & SPIDER_NET_DATA_STATUS_CHK_MASK) && if ( ( (data_status & SPIDER_NET_DATA_STATUS_CKSUM_MASK) ==
(!(data_error & SPIDER_NET_DATA_ERROR_CHK_MASK)) ) SPIDER_NET_DATA_STATUS_CKSUM_MASK) &&
!(data_error & SPIDER_NET_DATA_ERR_CKSUM_MASK))
skb->ip_summed = CHECKSUM_UNNECESSARY; skb->ip_summed = CHECKSUM_UNNECESSARY;
else else
skb->ip_summed = CHECKSUM_NONE; skb->ip_summed = CHECKSUM_NONE;
} else { } else
skb->ip_summed = CHECKSUM_NONE; skb->ip_summed = CHECKSUM_NONE;
}
if (data_status & SPIDER_NET_VLAN_PACKET) { if (data_status & SPIDER_NET_VLAN_PACKET) {
/* further enhancements: HW-accel VLAN /* further enhancements: HW-accel VLAN
...@@ -1092,7 +1041,10 @@ spider_net_pass_skb_up(struct spider_net_descr *descr, ...@@ -1092,7 +1041,10 @@ spider_net_pass_skb_up(struct spider_net_descr *descr,
} }
/* pass skb up to stack */ /* pass skb up to stack */
netif_receive_skb(skb); if (napi)
netif_receive_skb(skb);
else
netif_rx_ni(skb);
/* update netdevice statistics */ /* update netdevice statistics */
card->netdev_stats.rx_packets++; card->netdev_stats.rx_packets++;
...@@ -1102,16 +1054,18 @@ spider_net_pass_skb_up(struct spider_net_descr *descr, ...@@ -1102,16 +1054,18 @@ spider_net_pass_skb_up(struct spider_net_descr *descr,
} }
/** /**
* spider_net_decode_descr - processes an rx descriptor * spider_net_decode_one_descr - processes an rx descriptor
* @card: card structure * @card: card structure
* @napi: whether caller is in NAPI context
* *
* returns 1 if a packet has been sent to the stack, otherwise 0 * returns 1 if a packet has been sent to the stack, otherwise 0
* *
* processes an rx descriptor by iommu-unmapping the data buffer and passing * processes an rx descriptor by iommu-unmapping the data buffer and passing
* the packet up to the stack * the packet up to the stack. This function is called in softirq
* context, e.g. either bottom half from interrupt or NAPI polling context
*/ */
static int static int
spider_net_decode_one_descr(struct spider_net_card *card) spider_net_decode_one_descr(struct spider_net_card *card, int napi)
{ {
enum spider_net_descr_status status; enum spider_net_descr_status status;
struct spider_net_descr *descr; struct spider_net_descr *descr;
...@@ -1125,17 +1079,19 @@ spider_net_decode_one_descr(struct spider_net_card *card) ...@@ -1125,17 +1079,19 @@ spider_net_decode_one_descr(struct spider_net_card *card)
if (status == SPIDER_NET_DESCR_CARDOWNED) { if (status == SPIDER_NET_DESCR_CARDOWNED) {
/* nothing in the descriptor yet */ /* nothing in the descriptor yet */
return 0; result=0;
goto out;
} }
if (status == SPIDER_NET_DESCR_NOT_IN_USE) { if (status == SPIDER_NET_DESCR_NOT_IN_USE) {
/* not initialized yet, I bet chain->tail == chain->head /* not initialized yet, the ring must be empty */
* and the ring is empty */
spider_net_refill_rx_chain(card); spider_net_refill_rx_chain(card);
return 0; spider_net_enable_rxdmac(card);
result=0;
goto out;
} }
/* descriptor definitively used -- move on head */ /* descriptor definitively used -- move on tail */
chain->tail = descr->next; chain->tail = descr->next;
result = 0; result = 0;
...@@ -1146,6 +1102,9 @@ spider_net_decode_one_descr(struct spider_net_card *card) ...@@ -1146,6 +1102,9 @@ spider_net_decode_one_descr(struct spider_net_card *card)
pr_err("%s: dropping RX descriptor with state %d\n", pr_err("%s: dropping RX descriptor with state %d\n",
card->netdev->name, status); card->netdev->name, status);
card->netdev_stats.rx_dropped++; card->netdev_stats.rx_dropped++;
pci_unmap_single(card->pdev, descr->buf_addr,
SPIDER_NET_MAX_FRAME, PCI_DMA_BIDIRECTIONAL);
dev_kfree_skb_irq(descr->skb);
goto refill; goto refill;
} }
...@@ -1158,12 +1117,13 @@ spider_net_decode_one_descr(struct spider_net_card *card) ...@@ -1158,12 +1117,13 @@ spider_net_decode_one_descr(struct spider_net_card *card)
} }
/* ok, we've got a packet in descr */ /* ok, we've got a packet in descr */
result = spider_net_pass_skb_up(descr, card); result = spider_net_pass_skb_up(descr, card, napi);
refill: refill:
spider_net_set_descr_status(descr, SPIDER_NET_DESCR_NOT_IN_USE); spider_net_set_descr_status(descr, SPIDER_NET_DESCR_NOT_IN_USE);
/* change the descriptor state: */ /* change the descriptor state: */
spider_net_refill_rx_chain(card); if (!napi)
spider_net_refill_rx_chain(card);
out:
return result; return result;
} }
...@@ -1189,7 +1149,7 @@ spider_net_poll(struct net_device *netdev, int *budget) ...@@ -1189,7 +1149,7 @@ spider_net_poll(struct net_device *netdev, int *budget)
packets_to_do = min(*budget, netdev->quota); packets_to_do = min(*budget, netdev->quota);
while (packets_to_do) { while (packets_to_do) {
if (spider_net_decode_one_descr(card)) { if (spider_net_decode_one_descr(card, 1)) {
packets_done++; packets_done++;
packets_to_do--; packets_to_do--;
} else { } else {
...@@ -1201,6 +1161,7 @@ spider_net_poll(struct net_device *netdev, int *budget) ...@@ -1201,6 +1161,7 @@ spider_net_poll(struct net_device *netdev, int *budget)
netdev->quota -= packets_done; netdev->quota -= packets_done;
*budget -= packets_done; *budget -= packets_done;
spider_net_refill_rx_chain(card);
/* if all packets are in the stack, enable interrupts and return 0 */ /* if all packets are in the stack, enable interrupts and return 0 */
/* if not, return 1 */ /* if not, return 1 */
...@@ -1344,6 +1305,24 @@ spider_net_enable_txdmac(struct spider_net_card *card) ...@@ -1344,6 +1305,24 @@ spider_net_enable_txdmac(struct spider_net_card *card)
card->tx_chain.tail->bus_addr); card->tx_chain.tail->bus_addr);
} }
/**
* spider_net_handle_rxram_full - cleans up RX ring upon RX RAM full interrupt
* @card: card structure
*
* spider_net_handle_rxram_full empties the RX ring so that spider can put
* more packets in it and empty its RX RAM. This is called in bottom half
* context
*/
static void
spider_net_handle_rxram_full(struct spider_net_card *card)
{
while (spider_net_decode_one_descr(card, 0))
;
spider_net_enable_rxchtails(card);
spider_net_enable_rxdmac(card);
netif_rx_schedule(card->netdev);
}
/** /**
* spider_net_handle_error_irq - handles errors raised by an interrupt * spider_net_handle_error_irq - handles errors raised by an interrupt
* @card: card structure * @card: card structure
...@@ -1452,17 +1431,21 @@ spider_net_handle_error_irq(struct spider_net_card *card, u32 status_reg) ...@@ -1452,17 +1431,21 @@ spider_net_handle_error_irq(struct spider_net_card *card, u32 status_reg)
switch (i) switch (i)
{ {
case SPIDER_NET_GTMFLLINT: case SPIDER_NET_GTMFLLINT:
if (netif_msg_intr(card)) if (netif_msg_intr(card) && net_ratelimit())
pr_err("Spider TX RAM full\n"); pr_err("Spider TX RAM full\n");
show_error = 0; show_error = 0;
break; break;
case SPIDER_NET_GRFDFLLINT: /* fallthrough */
case SPIDER_NET_GRFCFLLINT: /* fallthrough */
case SPIDER_NET_GRFBFLLINT: /* fallthrough */
case SPIDER_NET_GRFAFLLINT: /* fallthrough */
case SPIDER_NET_GRMFLLINT: case SPIDER_NET_GRMFLLINT:
if (netif_msg_intr(card)) if (netif_msg_intr(card) && net_ratelimit())
pr_err("Spider RX RAM full, incoming packets " pr_err("Spider RX RAM full, incoming packets "
"might be discarded !\n"); "might be discarded!\n");
netif_rx_schedule(card->netdev); spider_net_rx_irq_off(card);
spider_net_enable_rxchtails(card); tasklet_schedule(&card->rxram_full_tl);
spider_net_enable_rxdmac(card); show_error = 0;
break; break;
/* case SPIDER_NET_GTMSHTINT: problem, print a message */ /* case SPIDER_NET_GTMSHTINT: problem, print a message */
...@@ -1470,10 +1453,6 @@ spider_net_handle_error_irq(struct spider_net_card *card, u32 status_reg) ...@@ -1470,10 +1453,6 @@ spider_net_handle_error_irq(struct spider_net_card *card, u32 status_reg)
/* allrighty. tx from previous descr ok */ /* allrighty. tx from previous descr ok */
show_error = 0; show_error = 0;
break; break;
/* case SPIDER_NET_GRFDFLLINT: print a message down there */
/* case SPIDER_NET_GRFCFLLINT: print a message down there */
/* case SPIDER_NET_GRFBFLLINT: print a message down there */
/* case SPIDER_NET_GRFAFLLINT: print a message down there */
/* chain end */ /* chain end */
case SPIDER_NET_GDDDCEINT: /* fallthrough */ case SPIDER_NET_GDDDCEINT: /* fallthrough */
...@@ -1485,6 +1464,7 @@ spider_net_handle_error_irq(struct spider_net_card *card, u32 status_reg) ...@@ -1485,6 +1464,7 @@ spider_net_handle_error_irq(struct spider_net_card *card, u32 status_reg)
"restarting DMAC %c.\n", "restarting DMAC %c.\n",
'D'+i-SPIDER_NET_GDDDCEINT); 'D'+i-SPIDER_NET_GDDDCEINT);
spider_net_refill_rx_chain(card); spider_net_refill_rx_chain(card);
spider_net_enable_rxdmac(card);
show_error = 0; show_error = 0;
break; break;
...@@ -1495,6 +1475,7 @@ spider_net_handle_error_irq(struct spider_net_card *card, u32 status_reg) ...@@ -1495,6 +1475,7 @@ spider_net_handle_error_irq(struct spider_net_card *card, u32 status_reg)
case SPIDER_NET_GDAINVDINT: case SPIDER_NET_GDAINVDINT:
/* could happen when rx chain is full */ /* could happen when rx chain is full */
spider_net_refill_rx_chain(card); spider_net_refill_rx_chain(card);
spider_net_enable_rxdmac(card);
show_error = 0; show_error = 0;
break; break;
...@@ -1583,17 +1564,13 @@ spider_net_interrupt(int irq, void *ptr, struct pt_regs *regs) ...@@ -1583,17 +1564,13 @@ spider_net_interrupt(int irq, void *ptr, struct pt_regs *regs)
if (!status_reg) if (!status_reg)
return IRQ_NONE; return IRQ_NONE;
if (status_reg & SPIDER_NET_TXINT)
spider_net_release_tx_chain(card, 0);
if (status_reg & SPIDER_NET_RXINT ) { if (status_reg & SPIDER_NET_RXINT ) {
spider_net_rx_irq_off(card); spider_net_rx_irq_off(card);
netif_rx_schedule(netdev); netif_rx_schedule(netdev);
} }
/* we do this after rx and tx processing, as we want the tx chain if (status_reg & SPIDER_NET_ERRINT )
* processed to see, whether we should restart tx dma processing */ spider_net_handle_error_irq(card, status_reg);
spider_net_handle_error_irq(card, status_reg);
/* clear interrupt sources */ /* clear interrupt sources */
spider_net_write_reg(card, SPIDER_NET_GHIINT0STS, status_reg); spider_net_write_reg(card, SPIDER_NET_GHIINT0STS, status_reg);
...@@ -1834,26 +1811,27 @@ spider_net_setup_phy(struct spider_net_card *card) ...@@ -1834,26 +1811,27 @@ spider_net_setup_phy(struct spider_net_card *card)
/** /**
* spider_net_download_firmware - loads firmware into the adapter * spider_net_download_firmware - loads firmware into the adapter
* @card: card structure * @card: card structure
* @firmware: firmware pointer * @firmware_ptr: pointer to firmware data
* *
* spider_net_download_firmware loads the firmware opened by * spider_net_download_firmware loads the firmware data into the
* spider_net_init_firmware into the adapter. * adapter. It assumes the length etc. to be allright.
*/ */
static int static int
spider_net_download_firmware(struct spider_net_card *card, spider_net_download_firmware(struct spider_net_card *card,
const struct firmware *firmware) u8 *firmware_ptr)
{ {
int sequencer, i; int sequencer, i;
u32 *fw_ptr = (u32 *)firmware->data; u32 *fw_ptr = (u32 *)firmware_ptr;
/* stop sequencers */ /* stop sequencers */
spider_net_write_reg(card, SPIDER_NET_GSINIT, spider_net_write_reg(card, SPIDER_NET_GSINIT,
SPIDER_NET_STOP_SEQ_VALUE); SPIDER_NET_STOP_SEQ_VALUE);
for (sequencer = 0; sequencer < 6; sequencer++) { for (sequencer = 0; sequencer < SPIDER_NET_FIRMWARE_SEQS;
sequencer++) {
spider_net_write_reg(card, spider_net_write_reg(card,
SPIDER_NET_GSnPRGADR + sequencer * 8, 0); SPIDER_NET_GSnPRGADR + sequencer * 8, 0);
for (i = 0; i < SPIDER_NET_FIRMWARE_LEN; i++) { for (i = 0; i < SPIDER_NET_FIRMWARE_SEQWORDS; i++) {
spider_net_write_reg(card, SPIDER_NET_GSnPRGDAT + spider_net_write_reg(card, SPIDER_NET_GSnPRGDAT +
sequencer * 8, *fw_ptr); sequencer * 8, *fw_ptr);
fw_ptr++; fw_ptr++;
...@@ -1898,41 +1876,53 @@ spider_net_download_firmware(struct spider_net_card *card, ...@@ -1898,41 +1876,53 @@ spider_net_download_firmware(struct spider_net_card *card,
static int static int
spider_net_init_firmware(struct spider_net_card *card) spider_net_init_firmware(struct spider_net_card *card)
{ {
struct firmware *firmware; struct firmware *firmware = NULL;
struct device_node *dn; struct device_node *dn;
u8 *fw_prop; u8 *fw_prop = NULL;
int err = -EIO; int err = -ENOENT;
int fw_size;
if (request_firmware((const struct firmware **)&firmware, if (request_firmware((const struct firmware **)&firmware,
SPIDER_NET_FIRMWARE_NAME, &card->pdev->dev) < 0) { SPIDER_NET_FIRMWARE_NAME, &card->pdev->dev) == 0) {
if (netif_msg_probe(card)) if ( (firmware->size != SPIDER_NET_FIRMWARE_LEN) &&
pr_err("Couldn't read in sequencer data file %s.\n", netif_msg_probe(card) ) {
SPIDER_NET_FIRMWARE_NAME); pr_err("Incorrect size of spidernet firmware in " \
"filesystem. Looking in host firmware...\n");
dn = pci_device_to_OF_node(card->pdev); goto try_host_fw;
if (!dn) }
goto out; err = spider_net_download_firmware(card, firmware->data);
fw_prop = (u8 *)get_property(dn, "firmware", NULL); release_firmware(firmware);
if (!fw_prop) if (err)
goto out; goto try_host_fw;
memcpy(firmware->data, fw_prop, 6 * SPIDER_NET_FIRMWARE_LEN * sizeof(u32)); goto done;
firmware->size = 6 * SPIDER_NET_FIRMWARE_LEN * sizeof(u32);
} }
if (firmware->size != 6 * SPIDER_NET_FIRMWARE_LEN * sizeof(u32)) { try_host_fw:
if (netif_msg_probe(card)) dn = pci_device_to_OF_node(card->pdev);
pr_err("Invalid size of sequencer data file %s.\n", if (!dn)
SPIDER_NET_FIRMWARE_NAME); goto out_err;
goto out;
fw_prop = (u8 *)get_property(dn, "firmware", &fw_size);
if (!fw_prop)
goto out_err;
if ( (fw_size != SPIDER_NET_FIRMWARE_LEN) &&
netif_msg_probe(card) ) {
pr_err("Incorrect size of spidernet firmware in " \
"host firmware\n");
goto done;
} }
if (!spider_net_download_firmware(card, firmware)) err = spider_net_download_firmware(card, fw_prop);
err = 0;
out:
release_firmware(firmware);
done:
return err;
out_err:
if (netif_msg_probe(card))
pr_err("Couldn't find spidernet firmware in filesystem " \
"or host firmware\n");
return err; return err;
} }
...@@ -1952,10 +1942,11 @@ spider_net_workaround_rxramfull(struct spider_net_card *card) ...@@ -1952,10 +1942,11 @@ spider_net_workaround_rxramfull(struct spider_net_card *card)
SPIDER_NET_CKRCTRL_RUN_VALUE); SPIDER_NET_CKRCTRL_RUN_VALUE);
/* empty sequencer data */ /* empty sequencer data */
for (sequencer = 0; sequencer < 6; sequencer++) { for (sequencer = 0; sequencer < SPIDER_NET_FIRMWARE_SEQS;
sequencer++) {
spider_net_write_reg(card, SPIDER_NET_GSnPRGDAT + spider_net_write_reg(card, SPIDER_NET_GSnPRGDAT +
sequencer * 8, 0x0); sequencer * 8, 0x0);
for (i = 0; i < SPIDER_NET_FIRMWARE_LEN; i++) { for (i = 0; i < SPIDER_NET_FIRMWARE_SEQWORDS; i++) {
spider_net_write_reg(card, SPIDER_NET_GSnPRGDAT + spider_net_write_reg(card, SPIDER_NET_GSnPRGDAT +
sequencer * 8, 0x0); sequencer * 8, 0x0);
} }
...@@ -2079,7 +2070,15 @@ spider_net_setup_netdev(struct spider_net_card *card) ...@@ -2079,7 +2070,15 @@ spider_net_setup_netdev(struct spider_net_card *card)
SET_NETDEV_DEV(netdev, &card->pdev->dev); SET_NETDEV_DEV(netdev, &card->pdev->dev);
pci_set_drvdata(card->pdev, netdev); pci_set_drvdata(card->pdev, netdev);
spin_lock_init(&card->intmask_lock);
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;
init_timer(&card->tx_timer);
card->tx_timer.function =
(void (*)(unsigned long)) spider_net_cleanup_tx_ring;
card->tx_timer.data = (unsigned long) card;
netdev->irq = card->pdev->irq; netdev->irq = card->pdev->irq;
card->options.rx_csum = SPIDER_NET_RX_CSUM_DEFAULT; card->options.rx_csum = SPIDER_NET_RX_CSUM_DEFAULT;
......
...@@ -33,25 +33,32 @@ extern struct ethtool_ops spider_net_ethtool_ops; ...@@ -33,25 +33,32 @@ extern struct ethtool_ops spider_net_ethtool_ops;
extern char spider_net_driver_name[]; extern char spider_net_driver_name[];
#define SPIDER_NET_MAX_MTU 2308 #define SPIDER_NET_MAX_FRAME 2312
#define SPIDER_NET_MAX_MTU 2294
#define SPIDER_NET_MIN_MTU 64 #define SPIDER_NET_MIN_MTU 64
#define SPIDER_NET_RXBUF_ALIGN 128 #define SPIDER_NET_RXBUF_ALIGN 128
#define SPIDER_NET_RX_DESCRIPTORS_DEFAULT 64 #define SPIDER_NET_RX_DESCRIPTORS_DEFAULT 256
#define SPIDER_NET_RX_DESCRIPTORS_MIN 16 #define SPIDER_NET_RX_DESCRIPTORS_MIN 16
#define SPIDER_NET_RX_DESCRIPTORS_MAX 256 #define SPIDER_NET_RX_DESCRIPTORS_MAX 512
#define SPIDER_NET_TX_DESCRIPTORS_DEFAULT 64 #define SPIDER_NET_TX_DESCRIPTORS_DEFAULT 256
#define SPIDER_NET_TX_DESCRIPTORS_MIN 16 #define SPIDER_NET_TX_DESCRIPTORS_MIN 16
#define SPIDER_NET_TX_DESCRIPTORS_MAX 256 #define SPIDER_NET_TX_DESCRIPTORS_MAX 512
#define SPIDER_NET_TX_TIMER 20
#define SPIDER_NET_RX_CSUM_DEFAULT 1 #define SPIDER_NET_RX_CSUM_DEFAULT 1
#define SPIDER_NET_WATCHDOG_TIMEOUT 5*HZ #define SPIDER_NET_WATCHDOG_TIMEOUT 50*HZ
#define SPIDER_NET_NAPI_WEIGHT 64 #define SPIDER_NET_NAPI_WEIGHT 64
#define SPIDER_NET_FIRMWARE_LEN 1024 #define SPIDER_NET_FIRMWARE_SEQS 6
#define SPIDER_NET_FIRMWARE_SEQWORDS 1024
#define SPIDER_NET_FIRMWARE_LEN (SPIDER_NET_FIRMWARE_SEQS * \
SPIDER_NET_FIRMWARE_SEQWORDS * \
sizeof(u32))
#define SPIDER_NET_FIRMWARE_NAME "spider_fw.bin" #define SPIDER_NET_FIRMWARE_NAME "spider_fw.bin"
/** spider_net SMMIO registers */ /** spider_net SMMIO registers */
...@@ -142,14 +149,12 @@ extern char spider_net_driver_name[]; ...@@ -142,14 +149,12 @@ extern char spider_net_driver_name[];
/** SCONFIG registers */ /** SCONFIG registers */
#define SPIDER_NET_SCONFIG_IOACTE 0x00002810 #define SPIDER_NET_SCONFIG_IOACTE 0x00002810
/** hardcoded register values */ /** interrupt mask registers */
#define SPIDER_NET_INT0_MASK_VALUE 0x3f7fe3ff #define SPIDER_NET_INT0_MASK_VALUE 0x3f7fe2c7
#define SPIDER_NET_INT1_MASK_VALUE 0xffffffff #define SPIDER_NET_INT1_MASK_VALUE 0xffff7ff7
/* no MAC aborts -> auto retransmission */ /* no MAC aborts -> auto retransmission */
#define SPIDER_NET_INT2_MASK_VALUE 0xfffffff1 #define SPIDER_NET_INT2_MASK_VALUE 0xffef7ff1
/* clear counter when interrupt sources are cleared
#define SPIDER_NET_FRAMENUM_VALUE 0x0001f001 */
/* we rely on flagged descriptor interrupts */ /* we rely on flagged descriptor interrupts */
#define SPIDER_NET_FRAMENUM_VALUE 0x00000000 #define SPIDER_NET_FRAMENUM_VALUE 0x00000000
/* set this first, then the FRAMENUM_VALUE */ /* set this first, then the FRAMENUM_VALUE */
...@@ -168,7 +173,7 @@ extern char spider_net_driver_name[]; ...@@ -168,7 +173,7 @@ extern char spider_net_driver_name[];
#if 0 #if 0
#define SPIDER_NET_WOL_VALUE 0x00000000 #define SPIDER_NET_WOL_VALUE 0x00000000
#endif #endif
#define SPIDER_NET_IPSECINIT_VALUE 0x00f000f8 #define SPIDER_NET_IPSECINIT_VALUE 0x6f716f71
/* pause frames: automatic, no upper retransmission count */ /* pause frames: automatic, no upper retransmission count */
/* outside loopback mode: ETOMOD signal dont matter, not connected */ /* outside loopback mode: ETOMOD signal dont matter, not connected */
...@@ -318,6 +323,10 @@ enum spider_net_int2_status { ...@@ -318,6 +323,10 @@ enum spider_net_int2_status {
#define SPIDER_NET_RXINT ( (1 << SPIDER_NET_GDAFDCINT) | \ #define SPIDER_NET_RXINT ( (1 << SPIDER_NET_GDAFDCINT) | \
(1 << SPIDER_NET_GRMFLLINT) ) (1 << SPIDER_NET_GRMFLLINT) )
#define SPIDER_NET_ERRINT ( 0xffffffff & \
(~SPIDER_NET_TXINT) & \
(~SPIDER_NET_RXINT) )
#define SPIDER_NET_GPREXEC 0x80000000 #define SPIDER_NET_GPREXEC 0x80000000
#define SPIDER_NET_GPRDAT_MASK 0x0000ffff #define SPIDER_NET_GPRDAT_MASK 0x0000ffff
...@@ -358,9 +367,6 @@ enum spider_net_int2_status { ...@@ -358,9 +367,6 @@ enum spider_net_int2_status {
/* descr ready, descr is in middle of chain, get interrupt on completion */ /* descr ready, descr is in middle of chain, get interrupt on completion */
#define SPIDER_NET_DMAC_RX_CARDOWNED 0xa0800000 #define SPIDER_NET_DMAC_RX_CARDOWNED 0xa0800000
/* multicast is no problem */
#define SPIDER_NET_DATA_ERROR_MASK 0xffffbfff
enum spider_net_descr_status { enum spider_net_descr_status {
SPIDER_NET_DESCR_COMPLETE = 0x00, /* used in rx and tx */ SPIDER_NET_DESCR_COMPLETE = 0x00, /* used in rx and tx */
SPIDER_NET_DESCR_RESPONSE_ERROR = 0x01, /* used in rx and tx */ SPIDER_NET_DESCR_RESPONSE_ERROR = 0x01, /* used in rx and tx */
...@@ -384,7 +390,7 @@ struct spider_net_descr { ...@@ -384,7 +390,7 @@ struct spider_net_descr {
/* used in the driver */ /* used in the driver */
struct sk_buff *skb; struct sk_buff *skb;
dma_addr_t bus_addr; u32 bus_addr;
struct spider_net_descr *next; struct spider_net_descr *next;
struct spider_net_descr *prev; struct spider_net_descr *prev;
} __attribute__((aligned(32))); } __attribute__((aligned(32)));
...@@ -396,21 +402,21 @@ struct spider_net_descr_chain { ...@@ -396,21 +402,21 @@ struct spider_net_descr_chain {
}; };
/* descriptor data_status bits */ /* descriptor data_status bits */
#define SPIDER_NET_RXIPCHK 29 #define SPIDER_NET_RX_IPCHK 29
#define SPIDER_NET_TCPUDPIPCHK 28 #define SPIDER_NET_RX_TCPCHK 28
#define SPIDER_NET_DATA_STATUS_CHK_MASK (1 << SPIDER_NET_RXIPCHK | \
1 << SPIDER_NET_TCPUDPIPCHK)
#define SPIDER_NET_VLAN_PACKET 21 #define SPIDER_NET_VLAN_PACKET 21
#define SPIDER_NET_DATA_STATUS_CKSUM_MASK ( (1 << SPIDER_NET_RX_IPCHK) | \
(1 << SPIDER_NET_RX_TCPCHK) )
/* descriptor data_error bits */ /* descriptor data_error bits */
#define SPIDER_NET_RXIPCHKERR 27 #define SPIDER_NET_RX_IPCHKERR 27
#define SPIDER_NET_RXTCPCHKERR 26 #define SPIDER_NET_RX_RXTCPCHKERR 28
#define SPIDER_NET_DATA_ERROR_CHK_MASK (1 << SPIDER_NET_RXIPCHKERR | \
1 << SPIDER_NET_RXTCPCHKERR) #define SPIDER_NET_DATA_ERR_CKSUM_MASK (1 << SPIDER_NET_RX_IPCHKERR)
/* the cases we don't pass the packet to the stack */ /* the cases we don't pass the packet to the stack.
#define SPIDER_NET_DESTROY_RX_FLAGS 0x70138000 * 701b8000 would be correct, but every packets gets that flag */
#define SPIDER_NET_DESTROY_RX_FLAGS 0x700b8000
#define SPIDER_NET_DESCR_SIZE 32 #define SPIDER_NET_DESCR_SIZE 32
...@@ -445,13 +451,16 @@ struct spider_net_card { ...@@ -445,13 +451,16 @@ struct spider_net_card {
struct spider_net_descr_chain tx_chain; struct spider_net_descr_chain tx_chain;
struct spider_net_descr_chain rx_chain; struct spider_net_descr_chain rx_chain;
spinlock_t chain_lock; atomic_t rx_chain_refill;
atomic_t tx_chain_release;
struct net_device_stats netdev_stats; struct net_device_stats netdev_stats;
struct spider_net_options options; struct spider_net_options options;
spinlock_t intmask_lock; spinlock_t intmask_lock;
struct tasklet_struct rxram_full_tl;
struct timer_list tx_timer;
struct work_struct tx_timeout_task; struct work_struct tx_timeout_task;
atomic_t tx_timeout_task_counter; atomic_t tx_timeout_task_counter;
......
...@@ -113,6 +113,23 @@ spider_net_ethtool_set_rx_csum(struct net_device *netdev, u32 n) ...@@ -113,6 +113,23 @@ spider_net_ethtool_set_rx_csum(struct net_device *netdev, u32 n)
return 0; return 0;
} }
static uint32_t
spider_net_ethtool_get_tx_csum(struct net_device *netdev)
{
return (netdev->features & NETIF_F_HW_CSUM) != 0;
}
static int
spider_net_ethtool_set_tx_csum(struct net_device *netdev, uint32_t data)
{
if (data)
netdev->features |= NETIF_F_HW_CSUM;
else
netdev->features &= ~NETIF_F_HW_CSUM;
return 0;
}
struct ethtool_ops spider_net_ethtool_ops = { struct ethtool_ops spider_net_ethtool_ops = {
.get_settings = spider_net_ethtool_get_settings, .get_settings = spider_net_ethtool_get_settings,
.get_drvinfo = spider_net_ethtool_get_drvinfo, .get_drvinfo = spider_net_ethtool_get_drvinfo,
...@@ -122,5 +139,7 @@ struct ethtool_ops spider_net_ethtool_ops = { ...@@ -122,5 +139,7 @@ struct ethtool_ops spider_net_ethtool_ops = {
.nway_reset = spider_net_ethtool_nway_reset, .nway_reset = spider_net_ethtool_nway_reset,
.get_rx_csum = spider_net_ethtool_get_rx_csum, .get_rx_csum = spider_net_ethtool_get_rx_csum,
.set_rx_csum = spider_net_ethtool_set_rx_csum, .set_rx_csum = spider_net_ethtool_set_rx_csum,
.get_tx_csum = spider_net_ethtool_get_tx_csum,
.set_tx_csum = spider_net_ethtool_set_tx_csum,
}; };
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment