/*
* Network device driver for Cell Processor-Based Blade and Celleb platform
*
* (C) Copyright IBM Corp. 2005
* (C) Copyright 2006 TOSHIBA CORPORATION
*
* Authors : Utz Bacher <utz.bacher@de.ibm.com>
* Jens Osterkamp <Jens.Osterkamp@de.ibm.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/compiler.h>
#include <linux/crc32.h>
#include <linux/delay.h>
#include <linux/etherdevice.h>
#include <linux/ethtool.h>
#include <linux/firmware.h>
#include <linux/if_vlan.h>
#include <linux/in.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/ip.h>
#include <linux/kernel.h>
#include <linux/mii.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <linux/device.h>
#include <linux/pci.h>
#include <linux/skbuff.h>
#include <linux/slab.h>
#include <linux/tcp.h>
#include <linux/types.h>
#include <linux/vmalloc.h>
#include <linux/wait.h>
#include <linux/workqueue.h>
#include <linux/bitops.h>
#include <asm/pci-bridge.h>
#include <net/checksum.h>
#include "spider_net.h"
MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com> and Jens Osterkamp " \
"<Jens.Osterkamp@de.ibm.com>");
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, 0444);
module_param(tx_descriptors, int, 0444);
MODULE_PARM_DESC(rx_descriptors, "number of descriptors used " \
"in rx chains");
MODULE_PARM_DESC(tx_descriptors, "number of descriptors used " \
"in tx chain");
char spider_net_driver_name[] = "spidernet";
static struct pci_device_id spider_net_pci_tbl[] = {
{ PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_SPIDER_NET,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL },
{ 0, }
};
MODULE_DEVICE_TABLE(pci, spider_net_pci_tbl);
/**
* spider_net_read_reg - reads an SMMIO register of a card
* @card: device structure
* @reg: register to read from
*
* returns the content of the specified SMMIO register.
*/
static inline u32
spider_net_read_reg(struct spider_net_card *card, u32 reg)
{
/* We use the powerpc specific variants instead of readl_be() because
* we know spidernet is not a real PCI device and we can thus avoid the
* performance hit caused by the PCI workarounds.
*/
return in_be32(card->regs + reg);
}
/**
* spider_net_write_reg - 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
*/
static inline void
spider_net_write_reg(struct spider_net_card *card, u32 reg, u32 value)
{
/* We use the powerpc specific variants instead of writel_be() because
* we know spidernet is not a real PCI device and we can thus avoid the
* performance hit caused by the PCI workarounds.
*/
out_be32(card->regs + reg, value);
}
/** spider_net_write_phy - write to phy register
* @netdev: adapter to be written to
* @mii_id: id of MII
* @reg: PHY register
* @val: value to be written to phy register
*
* spider_net_write_phy_register writes to an arbitrary PHY
* register via the spider GPCWOPCMD register. We assume the queue does
* not run full (not more than 15 commands outstanding).
**/
static void
spider_net_write_phy(struct net_device *netdev, int mii_id,
int reg, int val)
{
struct spider_net_card *card = netdev_priv(netdev);
u32 writevalue;
writevalue = ((u32)mii_id << 21) |
((u32)reg << 16) | ((u32)val);
spider_net_write_reg(card, SPIDER_NET_GPCWOPCMD, writevalue);
}
/** spider_net_read_phy - read from phy register
* @netdev: network device to be read from
* @mii_id: id of MII
* @reg: PHY register
*
* Returns value read from PHY register
*
* spider_net_write_phy reads from an arbitrary PHY
* register via the spider GPCROPCMD register
**/
static int
spider_net_read_phy(struct net_device *netdev, int mii_id, int reg)
{
struct spider_net_card *card = netdev_priv(netdev);
u32 readvalue;
readvalue = ((u32)mii_id << 21) | ((u32)reg << 16);
spider_net_write_reg(card, SPIDER_NET_GPCROPCMD, readvalue);
/* we don't use semaphores to wait for an SPIDER_NET_GPROPCMPINT
* interrupt, as we poll for the completion of the read operation
* in spider_net_read_phy. Should take about 50 us */
do {
readvalue = spider_net_read_reg(card, SPIDER_NET_GPCROPCMD);
} while (readvalue & SPIDER_NET_GPREXEC);
readvalue &= SPIDER_NET_GPRDAT_MASK;
return readvalue;
}
/**
* spider_net_setup_aneg - initial auto-negotiation setup
* @card: device structure
**/
static void
spider_net_setup_aneg(struct spider_net_card *card)
{
struct mii_phy *phy = &card->phy;
u32 advertise = 0;
u16 bmsr, estat;
bmsr = spider_net_read_phy(card->netdev, phy->mii_id, MII_BMSR);
estat = spider_net_read_phy(card->netdev, phy->mii_id, MII_ESTATUS);
if (bmsr & BMSR_10HALF)
advertise |= ADVERTISED_10baseT_Half;
if (bmsr & BMSR_10FULL)
advertise |= ADVERTISED_10baseT_Full;
if (bmsr & BMSR_100HALF)
advertise |= ADVERTISED_100baseT_Half;
if (bmsr & BMSR_100FULL)
advertise |= ADVERTISED_100baseT_Full;
if ((bmsr & BMSR_ESTATEN) && (estat & ESTATUS_1000_TFULL))
advertise |= SUPPORTED_1000baseT_Full;
if ((bmsr & BMSR_ESTATEN) && (estat & ESTATUS_1000_THALF))
advertise |= SUPPORTED_1000baseT_Half;
mii_phy_probe(phy, phy->mii_id);
phy->def->ops->setup_aneg(phy, advertise);
}
/**
* 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;
regvalue = SPIDER_NET_INT0_MASK_VALUE & (~SPIDER_NET_RXINT);
spider_net_write_reg(card, SPIDER_NET_GHIINT0MSK, regvalue);
}
/**
* spider_net_rx_irq_on - switch on 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;
regvalue = SPIDER_NET_INT0_MASK_VALUE | SPIDER_NET_RXINT;
spider_net_write_reg(card, SPIDER_NET_GHIINT0MSK, regvalue);
}
/**
* spider_net_set_promisc - sets the unicast address or the promiscuous mode
* @card: card structure
*
* spider_net_set_promisc sets the unicast destination address filter and
* thus either allows for non-promisc mode or promisc mode
*/
static void
spider_net_set_promisc(struct spider_net_card *card)
{
u32 macu, macl;
struct net_device *netdev = card->netdev;
if (netdev->flags & IFF_PROMISC) {
/* clear destination entry 0 */
spider_net_write_reg(card, SPIDER_NET_GMRUAFILnR, 0);
spider_net_write_reg(card, SPIDER_NET_GMRUAFILnR + 0x04, 0);
spider_net_write_reg(card, SPIDER_NET_GMRUA0FIL15R,
SPIDER_NET_PROMISC_VALUE);
} else {
macu = netdev->dev_addr[0];
macu <<= 8;
macu |= netdev->dev_addr[1];
memcpy(&macl, &netdev->dev_addr[2], sizeof(macl));
macu |= SPIDER_NET_UA_DESCR_VALUE;
spider_net_write_reg(card, SPIDER_NET_GMRUAFILnR, macu);
spider_net_write_reg(card, SPIDER_NET_GMRUAFILnR + 0x04, macl);
spider_net_write_reg(card, SPIDER_NET_GMRUA0FIL15R,
SPIDER_NET_NONPROMISC_VALUE);
}
}
/**
* spider_net_get_mac_address - read mac address from spider card
* @card: device structure
*
* reads MAC address from GMACUNIMACU and GMACUNIMACL registers
*/
static int
spider_net_get_mac_address(struct net_device *netdev)
{
struct spider_net_card *card = netdev_priv(netdev);
u32 macl, macu;
macl = spider_net_read_reg(card, SPIDER_NET_GMACUNIMACL);
macu = spider_net_read_reg(card, SPIDER_NET_GMACUNIMACU);
netdev->dev_addr[0] = (macu >> 24) & 0xff;
netdev->dev_addr[1] = (macu >> 16) & 0xff;
netdev->dev_addr[2] = (macu >> 8) & 0xff;
netdev->dev_addr[3] = macu & 0xff;
netdev->dev_addr[4] = (macl >> 8) & 0xff;
netdev->dev_addr[5] = macl & 0xff;
if (!is_valid_ether_addr(&netdev->dev_addr[0]))
return -EINVAL;
return 0;
}
/**
* spider_net_get_descr_status -- returns the status of a descriptor
* @descr: descriptor to look at
*
* returns the status as in the dmac_cmd_status field of the descriptor
*/
static inline int
spider_net_get_descr_status(struct spider_net_hw_descr *hwdescr)
{
return hwdescr->dmac_cmd_status & SPIDER_NET_DESCR_IND_PROC_MASK;
}
/**
* 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;
descr = chain->ring;
do {
descr->bus_addr = 0;
descr->hwdescr->next_descr_addr = 0;
descr = descr->next;
} while (descr != chain->ring);
dma_free_coherent(&card->pdev->dev, chain->num_desc,
chain->hwring, chain->dma_addr);
}
/**
* spider_net_init_chain - alloc and link descriptor chain
* @card: card structure
* @chain: address of chain
*
* We manage a circular list that mirrors the hardware structure,
* except that the hardware uses bus addresses.
*
* Returns 0 on success, <0 on failure
*/
static int
spider_net_init_chain(struct spider_net_card *card,
struct spider_net_descr_chain *chain)
{
int i;
struct spider_net_descr *descr;
struct spider_net_hw_descr *hwdescr;
dma_addr_t buf;
size_t alloc_size;
alloc_size = chain->num_desc * sizeof(struct spider_net_hw_descr);
chain->hwring = dma_alloc_coherent(&card->pdev->dev, alloc_size,
&chain->dma_addr, GFP_KERNEL);
if (!chain->hwring)
return -ENOMEM;
memset(chain->ring, 0, chain->num_desc * sizeof(struct spider_net_descr));
/* Set up the hardware pointers in each descriptor */
descr = chain->ring;
hwdescr = chain->hwring;
buf = chain->dma_addr;
for (i=0; i < chain->num_desc; i++, descr++, hwdescr++) {
hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE;
hwdescr->next_descr_addr = 0;
descr->hwdescr = hwdescr;
descr->bus_addr = buf;
descr->next = descr + 1;
descr->prev = descr - 1;
buf += sizeof(struct spider_net_hw_descr);
}
/* do actual circular list */
(descr-1)->next = chain->ring;
chain->ring->prev = descr-1;
spin_lock_init(&chain->lock);
chain->head = chain->ring;
chain->tail = chain->ring;
return 0;
}
/**
* spider_net_free_rx_chain_contents - frees descr contents in rx chain
* @card: card structure
*
* returns 0 on success, <0 on failure
*/
static void
spider_net_free_rx_chain_contents(struct spider_net_card *card)
{
struct spider_net_descr *descr;
descr = card->rx_chain.head;
do {
if (descr->skb) {
pci_unmap_single(card->pdev, descr->hwdescr->buf_addr,
SPIDER_NET_MAX_FRAME,
PCI_DMA_BIDIRECTIONAL);
dev_kfree_skb(descr->skb);
descr->skb = NULL;
}
descr = descr->next;
} while (descr != card->rx_chain.head);
}
/**
* spider_net_prepare_rx_descr - Reinitialize RX descriptor
* @card: card structure
* @descr: descriptor to re-init
*
* Return 0 on succes, <0 on failure.
*
* Allocates a new rx skb, iommu-maps it and attaches it to the
* descriptor. Mark the descriptor as activated, ready-to-use.
*/
static int
spider_net_prepare_rx_descr(struct spider_net_card *card,
struct spider_net_descr *descr)
{
struct spider_net_hw_descr *hwdescr = descr->hwdescr;
dma_addr_t buf;
int offset;
int bufsize;
/* we need to round up the buffer size to a multiple of 128 */
bufsize = (SPIDER_NET_MAX_FRAME + SPIDER_NET_RXBUF_ALIGN - 1) &
(~(SPIDER_NET_RXBUF_ALIGN - 1));
/* and we need to have it 128 byte aligned, therefore we allocate a
* bit more */
/* allocate an skb */
descr->skb = netdev_alloc_skb(card->netdev,
bufsize + SPIDER_NET_RXBUF_ALIGN - 1);
if (!descr->skb) {
if (netif_msg_rx_err(card) && net_ratelimit())
dev_err(&card->netdev->dev,
"Not enough memory to allocate rx buffer\n");
card->spider_stats.alloc_rx_skb_error++;
return -ENOMEM;
}
hwdescr->buf_size = bufsize;
hwdescr->result_size = 0;
hwdescr->valid_size = 0;
hwdescr->data_status = 0;
hwdescr->data_error = 0;
offset = ((unsigned long)descr->skb->data) &
(SPIDER_NET_RXBUF_ALIGN - 1);
if (offset)
skb_reserve(descr->skb, SPIDER_NET_RXBUF_ALIGN - offset);
/* iommu-map the skb */
buf = pci_map_single(card->pdev, descr->skb->data,
SPIDER_NET_MAX_FRAME, PCI_DMA_FROMDEVICE);
if (pci_dma_mapping_error(card->pdev, buf)) {
dev_kfree_skb_any(descr->skb);
descr->skb = NULL;
if (netif_msg_rx_err(card) && net_ratelimit())
dev_err(&card->netdev->dev, "Could not iommu-map rx buffer\n");
card->spider_stats.rx_iommu_map_error++;
hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE;
} else {
hwdescr->buf_addr = buf;
wmb();
hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_CARDOWNED |
SPIDER_NET_DMAC_NOINTR_COMPLETE;
}
return 0;
}
/**
* spider_net_enable_rxchtails - sets RX dmac chain tail addresses
* @card: card structure
*
* spider_net_enable_rxchtails sets the RX DMAC chain tail adresses in the
* chip by writing to the appropriate register. DMA is enabled in
* spider_net_enable_rxdmac.
*/
static inline void
spider_net_enable_rxchtails(struct spider_net_card *card)
{
/* assume chain is aligned correctly */
spider_net_write_reg(card, SPIDER_NET_GDADCHA ,
card->rx_chain.tail->bus_addr);
}
/**
* spider_net_enable_rxdmac - enables a receive DMA controller
* @card: card structure
*
* spider_net_enable_rxdmac enables the DMA controller by setting RX_DMA_EN
* in the GDADMACCNTR register
*/
static inline void
spider_net_enable_rxdmac(struct spider_net_card *card)
{
wmb();
spider_net_write_reg(card, SPIDER_NET_GDADMACCNTR,
SPIDER_NET_DMA_RX_VALUE);
}
/**
* spider_net_disable_rxdmac - disables the receive DMA controller
* @card: card structure
*
* spider_net_disable_rxdmac terminates processing on the DMA controller
* by turing off the DMA controller, with the force-end flag set.
*/
static inline void
spider_net_disable_rxdmac(struct spider_net_card *card)
{
spider_net_write_reg(card, SPIDER_NET_GDADMACCNTR,
SPIDER_NET_DMA_RX_FEND_VALUE);
}
/**
* spider_net_refill_rx_chain - refills descriptors/skbs in the rx chains
* @card: card structure
*
* refills descriptors in the rx chain: allocates skbs and iommu-maps them.
*/
static void
spider_net_refill_rx_chain(struct spider_net_card *card)
{
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 (!spin_trylock_irqsave(&chain->lock, flags))
return;
while (spider_net_get_descr_status(chain->head->hwdescr) ==
SPIDER_NET_DESCR_NOT_IN_USE) {
if (spider_net_prepare_rx_descr(card, chain->head))
break;
chain->head = chain->head->next;
}
spin_unlock_irqrestore(&chain->lock, flags);
}
/**
* spider_net_alloc_rx_skbs - Allocates rx skbs in rx descriptor chains
* @card: card structure
*
* Returns 0 on success, <0 on failure.
*/
static int
spider_net_alloc_rx_skbs(struct spider_net_card *card)
{
struct spider_net_descr_chain *chain = &card->rx_chain;
struct spider_net_descr *start = chain->tail;
struct spider_net_descr *descr = start;
/* Link up the hardware chain pointers */
do {
descr->prev->hwdescr->next_descr_addr = descr->bus_addr;
descr = descr->next;
} while (descr != start);
/* Put at least one buffer into the chain. if this fails,
* we've got a problem. If not, spider_net_refill_rx_chain
* will do the rest at the end of this function. */
if (spider_net_prepare_rx_descr(card, chain->head))
goto error;
else
chain->head = chain->head->next;
/* This will allocate the rest of the rx buffers;
* if not, it's business as usual later on. */
spider_net_refill_rx_chain(card);
spider_net_enable_rxdmac(card);
return 0;
error:
spider_net_free_rx_chain_contents(card);
return -ENOMEM;
}
/**
* spider_net_get_multicast_hash - generates hash for multicast filter table
* @addr: multicast address
*
* returns the hash value.
*
* spider_net_get_multicast_hash calculates a hash value for a given multicast
* address, that is used to set the multicast filter tables
*/
static u8
spider_net_get_multicast_hash(struct net_device *netdev, __u8 *addr)
{
u32 crc;
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_for_crc, netdev->addr_len);
hash = (crc >> 27);
hash <<= 3;
hash |= crc & 7;
hash &= 0xff;
return hash;
}
/**
* spider_net_set_multi - sets multicast addresses and promisc flags
* @netdev: interface device structure
*
* spider_net_set_multi configures multicast addresses as needed for the
* netdev interface. It also sets up multicast, allmulti and promisc
* flags appropriately
*/
static void
spider_net_set_multi(struct net_device *netdev)
{
struct dev_mc_list *mc;
u8 hash;
int i;
u32 reg;
struct spider_net_card *card = netdev_priv(netdev);
unsigned long bitmask[SPIDER_NET_MULTICAST_HASHES / BITS_PER_LONG] =
{0, };
spider_net_set_promisc(card);
if (netdev->flags & IFF_ALLMULTI) {
for (i = 0; i < SPIDER_NET_MULTICAST_HASHES; i++) {
set_bit(i, bitmask);
}
goto write_hash;
}
/* well, we know, what the broadcast hash value is: it's xfd
hash = spider_net_get_multicast_hash(netdev, netdev->broadcast); */
set_bit(0xfd, bitmask);
for (mc = netdev->mc_list; mc; mc = mc->next) {
hash = spider_net_get_multicast_hash(netdev, mc->dmi_addr);
set_bit(hash, bitmask);
}
write_hash:
for (i = 0; i < SPIDER_NET_MULTICAST_HASHES / 4; i++) {
reg = 0;
if (test_bit(i * 4, bitmask))
reg += 0x08;
reg <<= 8;
if (test_bit(i * 4 + 1, bitmask))
reg += 0x08;
reg <<= 8;
if (test_bit(i * 4 + 2, bitmask))
reg += 0x08;
reg <<= 8;
if (test_bit(i * 4 + 3, bitmask))
reg += 0x08;
spider_net_write_reg(card, SPIDER_NET_GMRMHFILnR + i * 4, reg);
}
}
/**
* spider_net_prepare_tx_descr - fill tx descriptor with skb data
* @card: card structure
* @descr: descriptor structure to fill out
* @skb: packet to use
*
* returns 0 on success, <0 on failure.
*
* fills out the descriptor structure with skb data and len. Copies data,
* if needed (32bit DMA!)
*/
static int
spider_net_prepare_tx_descr(struct spider_net_card *card,
struct sk_buff *skb)
{
struct spider_net_descr_chain *chain = &card->tx_chain;
struct spider_net_descr *descr;
struct spider_net_hw_descr *hwdescr;
dma_addr_t buf;
unsigned long flags;
buf = pci_map_single(card->pdev, skb->data, skb->len, PCI_DMA_TODEVICE);
if (pci_dma_mapping_error(card->pdev, buf)) {
if (netif_msg_tx_err(card) && net_ratelimit())
dev_err(&card->netdev->dev, "could not iommu-map packet (%p, %i). "
"Dropping packet\n", skb->data, skb->len);
card->spider_stats.tx_iommu_map_error++;
return -ENOMEM;
}
spin_lock_irqsave(&chain->lock, flags);
descr = card->tx_chain.head;
if (descr->next == chain->tail->prev) {
spin_unlock_irqrestore(&chain->lock, flags);
pci_unmap_single(card->pdev, buf, skb->len, PCI_DMA_TODEVICE);
return -ENOMEM;
}
hwdescr = descr->hwdescr;
chain->head = descr->next;
descr->skb = skb;
hwdescr->buf_addr = buf;
hwdescr->buf_size = skb->len;
hwdescr->next_descr_addr = 0;
hwdescr->data_status = 0;
hwdescr->dmac_cmd_status =
SPIDER_NET_DESCR_CARDOWNED | SPIDER_NET_DMAC_TXFRMTL;
spin_unlock_irqrestore(&chain->lock, flags);
if (skb->ip_summed == CHECKSUM_PARTIAL)
switch (ip_hdr(skb)->protocol) {
case IPPROTO_TCP:
hwdescr->dmac_cmd_status |= SPIDER_NET_DMAC_TCP;
break;
case IPPROTO_UDP:
hwdescr->dmac_cmd_status |= SPIDER_NET_DMAC_UDP;
break;
}
/* Chain the bus address, so that the DMA engine finds this descr. */
wmb();
descr->prev->hwdescr->next_descr_addr = descr->bus_addr;
card->netdev->trans_start = jiffies; /* set netdev watchdog timer */
return 0;
}
static int
spider_net_set_low_watermark(struct spider_net_card *card)
{
struct spider_net_descr *descr = card->tx_chain.tail;
struct spider_net_hw_descr *hwdescr;
unsigned long flags;
int status;
int cnt=0;
int i;
/* 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->hwdescr->dmac_cmd_status & SPIDER_NET_DESCR_NOT_IN_USE;
if (status == SPIDER_NET_DESCR_NOT_IN_USE)
break;
descr = descr->next;
cnt++;
}
/* If TX queue is short, don't even bother with interrupts */
if (cnt < card->tx_chain.num_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;i<cnt; i++)
descr = descr->next;
/* Set the new watermark, clear the old watermark */
spin_lock_irqsave(&card->tx_chain.lock, flags);
descr->hwdescr->dmac_cmd_status |= SPIDER_NET_DESCR_TXDESFLG;
if (card->low_watermark && card->low_watermark != descr) {
hwdescr = card->low_watermark->hwdescr;
hwdescr->dmac_cmd_status =
hwdescr->dmac_cmd_status & ~SPIDER_NET_DESCR_TXDESFLG;
}
card->low_watermark = descr;
spin_unlock_irqrestore(&card->tx_chain.lock, flags);
return cnt;
}
/**
* 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 net_device *dev = card->netdev;
struct spider_net_descr_chain *chain = &card->tx_chain;
struct spider_net_descr *descr;
struct spider_net_hw_descr *hwdescr;
struct sk_buff *skb;
u32 buf_addr;
unsigned long flags;
int status;
while (1) {
spin_lock_irqsave(&chain->lock, flags);
if (chain->tail == chain->head) {
spin_unlock_irqrestore(&chain->lock, flags);
return 0;
}
descr = chain->tail;
hwdescr = descr->hwdescr;
status = spider_net_get_descr_status(hwdescr);
switch (status) {
case SPIDER_NET_DESCR_COMPLETE:
dev->stats.tx_packets++;
dev->stats.tx_bytes += descr->skb->len;
break;
case SPIDER_NET_DESCR_CARDOWNED:
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) */
case SPIDER_NET_DESCR_RESPONSE_ERROR:
case SPIDER_NET_DESCR_PROTECTION_ERROR:
case SPIDER_NET_DESCR_FORCE_END:
if (netif_msg_tx_err(card))
dev_err(&card->netdev->dev, "forcing end of tx descriptor "
"with status x%02x\n", status);
dev->stats.tx_errors++;
break;
default:
dev->stats.tx_dropped++;
if (!brutal) {
spin_unlock_irqrestore(&chain->lock, flags);
return 1;
}
}
chain->tail = descr->next;
hwdescr->dmac_cmd_status |= SPIDER_NET_DESCR_NOT_IN_USE;
skb = descr->skb;
descr->skb = NULL;
buf_addr = hwdescr->buf_addr;
spin_unlock_irqrestore(&chain->lock, flags);
/* unmap the skb */
if (skb) {
pci_unmap_single(card->pdev, buf_addr, skb->len,
PCI_DMA_TODEVICE);
dev_kfree_skb(skb);
}
}
return 0;
}
/**
* spider_net_kick_tx_dma - enables TX DMA processing
* @card: card structure
* @descr: descriptor address to enable TX processing at
*
* 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)
{
struct spider_net_descr *descr;
if (spider_net_read_reg(card, SPIDER_NET_GDTDMACCNTR) &
SPIDER_NET_TX_DMA_EN)
goto out;
descr = card->tx_chain.tail;
for (;;) {
if (spider_net_get_descr_status(descr->hwdescr) ==
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);
}
/**
* spider_net_xmit - transmits a frame over the device
* @skb: packet to send out
* @netdev: interface device structure
*
* returns 0 on success, !0 on failure
*/
static int
spider_net_xmit(struct sk_buff *skb, struct net_device *netdev)
{
int cnt;
struct spider_net_card *card = netdev_priv(netdev);
spider_net_release_tx_chain(card, 0);
if (spider_net_prepare_tx_descr(card, skb) != 0) {
netdev->stats.tx_dropped++;
netif_stop_queue(netdev);
return NETDEV_TX_BUSY;
}
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 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)
{
if ((spider_net_release_tx_chain(card, 0) != 0) &&
(card->netdev->flags & IFF_UP)) {
spider_net_kick_tx_dma(card);
netif_wake_queue(card->netdev);
}
}
/**
* spider_net_do_ioctl - called for device ioctls
* @netdev: interface device structure
* @ifr: request parameter structure for ioctl
* @cmd: command code for ioctl
*
* returns 0 on success, <0 on failure. Currently, we have no special ioctls.
* -EOPNOTSUPP is returned, if an unknown ioctl was requested
*/
static int
spider_net_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
{
switch (cmd) {
default:
return -EOPNOTSUPP;
}
}
/**
* spider_net_pass_skb_up - takes an skb from a descriptor and passes it on
* @descr: descriptor to process
* @card: card structure
*
* Fills out skb structure and passes the data to the stack.
* The descriptor state is not changed.
*/
static void
spider_net_pass_skb_up(struct spider_net_descr *descr,
struct spider_net_card *card)
{
struct spider_net_hw_descr *hwdescr = descr->hwdescr;
struct sk_buff *skb = descr->skb;
struct net_device *netdev = card->netdev;
u32 data_status = hwdescr->data_status;
u32 data_error = hwdescr->data_error;
skb_put(skb, hwdescr->valid_size);
/* the card seems to add 2 bytes of junk in front
* of the ethernet frame */
#define SPIDER_MISALIGN 2
skb_pull(skb, SPIDER_MISALIGN);
skb->protocol = eth_type_trans(skb, netdev);
/* checksum offload */
if (card->options.rx_csum) {
if ( ( (data_status & SPIDER_NET_DATA_STATUS_CKSUM_MASK) ==
SPIDER_NET_DATA_STATUS_CKSUM_MASK) &&
!(data_error & SPIDER_NET_DATA_ERR_CKSUM_MASK))
skb->ip_summed = CHECKSUM_UNNECESSARY;
else
skb->ip_summed = CHECKSUM_NONE;
} else
skb->ip_summed = CHECKSUM_NONE;
if (data_status & SPIDER_NET_VLAN_PACKET) {
/* further enhancements: HW-accel VLAN
* vlan_hwaccel_receive_skb
*/
}
/* update netdevice statistics */
netdev->stats.rx_packets++;
netdev->stats.rx_bytes += skb->len;
/* pass skb up to stack */
netif_receive_skb(skb);
}
static void show_rx_chain(struct spider_net_card *card)
{
struct spider_net_descr_chain *chain = &card->rx_chain;
struct spider_net_descr *start= chain->tail;
struct spider_net_descr *descr= start;
struct spider_net_hw_descr *hwd = start->hwdescr;
struct device *dev = &card->netdev->dev;
u32 curr_desc, next_desc;
int status;
int tot = 0;
int cnt = 0;
int off = start - chain->ring;
int cstat = hwd->dmac_cmd_status;
dev_info(dev, "Total number of descrs=%d\n",
chain->num_desc);
dev_info(dev, "Chain tail located at descr=%d, status=0x%x\n",
off, cstat);
curr_desc = spider_net_read_reg(card, SPIDER_NET_GDACTDPA);
next_desc = spider_net_read_reg(card, SPIDER_NET_GDACNEXTDA);
status = cstat;
do
{
hwd = descr->hwdescr;
off = descr - chain->ring;
status = hwd->dmac_cmd_status;
if (descr == chain->head)
dev_info(dev, "Chain head is at %d, head status=0x%x\n",
off, status);
if (curr_desc == descr->bus_addr)
dev_info(dev, "HW curr desc (GDACTDPA) is at %d, status=0x%x\n",
off, status);
if (next_desc == descr->bus_addr)
dev_info(dev, "HW next desc (GDACNEXTDA) is at %d, status=0x%x\n",
off, status);
if (hwd->next_descr_addr == 0)
dev_info(dev, "chain is cut at %d\n", off);
if (cstat != status) {
int from = (chain->num_desc + off - cnt) % chain->num_desc;
int to = (chain->num_desc + off - 1) % chain->num_desc;
dev_info(dev, "Have %d (from %d to %d) descrs "
"with stat=0x%08x\n", cnt, from, to, cstat);
cstat = status;
cnt = 0;
}
cnt ++;
tot ++;
descr = descr->next;
} while (descr != start);
dev_info(dev, "Last %d descrs with stat=0x%08x "
"for a total of %d descrs\n", cnt, cstat, tot);
#ifdef DEBUG
/* Now dump the whole ring */
descr = start;
do
{
struct spider_net_hw_descr *hwd = descr->hwdescr;
status = spider_net_get_descr_status(hwd);
cnt = descr - chain->ring;
dev_info(dev, "Descr %d stat=0x%08x skb=%p\n",
cnt, status, descr->skb);
dev_info(dev, "bus addr=%08x buf addr=%08x sz=%d\n",
descr->bus_addr, hwd->buf_addr, hwd->buf_size);
dev_info(dev, "next=%08x result sz=%d valid sz=%d\n",
hwd->next_descr_addr, hwd->result_size,
hwd->valid_size);
dev_info(dev, "dmac=%08x data stat=%08x data err=%08x\n",
hwd->dmac_cmd_status, hwd->data_status,
hwd->data_error);
dev_info(dev, "\n");
descr = descr->next;
} while (descr != start);
#endif
}
/**
* spider_net_resync_head_ptr - Advance head ptr past empty descrs
*
* If the driver fails to keep up and empty the queue, then the
* hardware wil run out of room to put incoming packets. This
* will cause the hardware to skip descrs that are full (instead
* of halting/retrying). Thus, once the driver runs, it wil need
* to "catch up" to where the hardware chain pointer is at.
*/
static void spider_net_resync_head_ptr(struct spider_net_card *card)
{
unsigned long flags;
struct spider_net_descr_chain *chain = &card->rx_chain;
struct spider_net_descr *descr;
int i, status;
/* Advance head pointer past any empty descrs */
descr = chain->head;
status = spider_net_get_descr_status(descr->hwdescr);
if (status == SPIDER_NET_DESCR_NOT_IN_USE)
return;
spin_lock_irqsave(&chain->lock, flags);
descr = chain->head;
status = spider_net_get_descr_status(descr->hwdescr);
for (i=0; i<chain->num_desc; i++) {
if (status != SPIDER_NET_DESCR_CARDOWNED) break;
descr = descr->next;
status = spider_net_get_descr_status(descr->hwdescr);
}
chain->head = descr;
spin_unlock_irqrestore(&chain->lock, flags);
}
static int spider_net_resync_tail_ptr(struct spider_net_card *card)
{
struct spider_net_descr_chain *chain = &card->rx_chain;
struct spider_net_descr *descr;
int i, status;
/* Advance tail pointer past any empty and reaped descrs */
descr = chain->tail;
status = spider_net_get_descr_status(descr->hwdescr);
for (i=0; i<chain->num_desc; i++) {
if ((status != SPIDER_NET_DESCR_CARDOWNED) &&
(status != SPIDER_NET_DESCR_NOT_IN_USE)) break;
descr = descr->next;
status = spider_net_get_descr_status(descr->hwdescr);
}
chain->tail = descr;
if ((i == chain->num_desc) || (i == 0))
return 1;
return 0;
}
/**
* spider_net_decode_one_descr - processes an RX descriptor
* @card: card structure
*
* 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 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
spider_net_decode_one_descr(struct spider_net_card *card)
{
struct net_device *dev = card->netdev;
struct spider_net_descr_chain *chain = &card->rx_chain;
struct spider_net_descr *descr = chain->tail;
struct spider_net_hw_descr *hwdescr = descr->hwdescr;
u32 hw_buf_addr;
int status;
status = spider_net_get_descr_status(hwdescr);
/* Nothing in the descriptor, or ring must be empty */
if ((status == SPIDER_NET_DESCR_CARDOWNED) ||
(status == SPIDER_NET_DESCR_NOT_IN_USE))
return 0;
/* descriptor definitively used -- move on tail */
chain->tail = descr->next;
/* unmap descriptor */
hw_buf_addr = hwdescr->buf_addr;
hwdescr->buf_addr = 0xffffffff;
pci_unmap_single(card->pdev, hw_buf_addr,
SPIDER_NET_MAX_FRAME, PCI_DMA_FROMDEVICE);
if ( (status == SPIDER_NET_DESCR_RESPONSE_ERROR) ||
(status == SPIDER_NET_DESCR_PROTECTION_ERROR) ||
(status == SPIDER_NET_DESCR_FORCE_END) ) {
if (netif_msg_rx_err(card))
dev_err(&dev->dev,
"dropping RX descriptor with state %d\n", status);
dev->stats.rx_dropped++;
goto bad_desc;
}
if ( (status != SPIDER_NET_DESCR_COMPLETE) &&
(status != SPIDER_NET_DESCR_FRAME_END) ) {
if (netif_msg_rx_err(card))
dev_err(&card->netdev->dev,
"RX descriptor with unknown state %d\n", status);
card->spider_stats.rx_desc_unk_state++;
goto bad_desc;
}
/* The cases we'll throw away the packet immediately */
if (hwdescr->data_error & SPIDER_NET_DESTROY_RX_FLAGS) {
if (netif_msg_rx_err(card))
dev_err(&card->netdev->dev,
"error in received descriptor found, "
"data_status=x%08x, data_error=x%08x\n",
hwdescr->data_status, hwdescr->data_error);
goto bad_desc;
}
if (hwdescr->dmac_cmd_status & SPIDER_NET_DESCR_BAD_STATUS) {
dev_err(&card->netdev->dev, "bad status, cmd_status=x%08x\n",
hwdescr->dmac_cmd_status);
pr_err("buf_addr=x%08x\n", hw_buf_addr);
pr_err("buf_size=x%08x\n", hwdescr->buf_size);
pr_err("next_descr_addr=x%08x\n", hwdescr->next_descr_addr);
pr_err("result_size=x%08x\n", hwdescr->result_size);
pr_err("valid_size=x%08x\n", hwdescr->valid_size);
pr_err("data_status=x%08x\n", hwdescr->data_status);
pr_err("data_error=x%08x\n", hwdescr->data_error);
pr_err("which=%ld\n", descr - card->rx_chain.ring);
card->spider_stats.rx_desc_error++;
goto bad_desc;
}
/* Ok, we've got a packet in descr */
spider_net_pass_skb_up(descr, card);
descr->skb = NULL;
hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE;
return 1;
bad_desc:
if (netif_msg_rx_err(card))
show_rx_chain(card);
dev_kfree_skb_irq(descr->skb);
descr->skb = NULL;
hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE;
return 0;
}
/**
* spider_net_poll - NAPI poll function called by the stack to return packets
* @netdev: interface device structure
* @budget: number of packets we can pass to the stack at most
*
* returns 0 if no more packets available to the driver/stack. Returns 1,
* if the quota is exceeded, but the driver has still packets.
*
* spider_net_poll returns all packets from the rx descriptors to the stack
* (using netif_receive_skb). If all/enough packets are up, the driver
* reenables interrupts and returns 0. If not, 1 is returned.
*/
static int spider_net_poll(struct napi_struct *napi, int budget)
{
struct spider_net_card *card = container_of(napi, struct spider_net_card, napi);
struct net_device *netdev = card->netdev;
int packets_done = 0;
while (packets_done < budget) {
if (!spider_net_decode_one_descr(card))
break;
packets_done++;
}
if ((packets_done == 0) && (card->num_rx_ints != 0)) {
if (!spider_net_resync_tail_ptr(card))
packets_done = budget;
spider_net_resync_head_ptr(card);
}
card->num_rx_ints = 0;
spider_net_refill_rx_chain(card);
spider_net_enable_rxdmac(card);
spider_net_cleanup_tx_ring(card);
/* if all packets are in the stack, enable interrupts and return 0 */
/* if not, return 1 */
if (packets_done < budget) {
netif_rx_complete(netdev, napi);
spider_net_rx_irq_on(card);
card->ignore_rx_ramfull = 0;
}
return packets_done;
}
/**
* spider_net_change_mtu - changes the MTU of an interface
* @netdev: interface device structure
* @new_mtu: new MTU value
*
* returns 0 on success, <0 on failure
*/
static int
spider_net_change_mtu(struct net_device *netdev, int new_mtu)
{
/* no need to re-alloc skbs or so -- the max mtu is about 2.3k
* and mtu is outbound only anyway */
if ( (new_mtu < SPIDER_NET_MIN_MTU ) ||
(new_mtu > SPIDER_NET_MAX_MTU) )
return -EINVAL;
netdev->mtu = new_mtu;
return 0;
}
/**
* spider_net_set_mac - sets the MAC of an interface
* @netdev: interface device structure
* @ptr: pointer to new MAC address
*
* Returns 0 on success, <0 on failure. Currently, we don't support this
* and will always return EOPNOTSUPP.
*/
static int
spider_net_set_mac(struct net_device *netdev, void *p)
{
struct spider_net_card *card = netdev_priv(netdev);
u32 macl, macu, regvalue;
struct sockaddr *addr = p;
if (!is_valid_ether_addr(addr->sa_data))
return -EADDRNOTAVAIL;
/* switch off GMACTPE and GMACRPE */
regvalue = spider_net_read_reg(card, SPIDER_NET_GMACOPEMD);
regvalue &= ~((1 << 5) | (1 << 6));
spider_net_write_reg(card, SPIDER_NET_GMACOPEMD, regvalue);
/* write mac */
macu = (addr->sa_data[0]<<24) + (addr->sa_data[1]<<16) +
(addr->sa_data[2]<<8) + (addr->sa_data[3]);
macl = (addr->sa_data[4]<<8) + (addr->sa_data[5]);
spider_net_write_reg(card, SPIDER_NET_GMACUNIMACU, macu);
spider_net_write_reg(card, SPIDER_NET_GMACUNIMACL, macl);
/* switch GMACTPE and GMACRPE back on */
regvalue = spider_net_read_reg(card, SPIDER_NET_GMACOPEMD);
regvalue |= ((1 << 5) | (1 << 6));
spider_net_write_reg(card, SPIDER_NET_GMACOPEMD, regvalue);
spider_net_set_promisc(card);
/* look up, whether we have been successful */
if (spider_net_get_mac_address(netdev))
return -EADDRNOTAVAIL;
if (memcmp(netdev->dev_addr,addr->sa_data,netdev->addr_len))
return -EADDRNOTAVAIL;
return 0;
}
/**
* spider_net_link_reset
* @netdev: net device structure
*
* This is called when the PHY_LINK signal is asserted. For the blade this is
* not connected so we should never get here.
*
*/
static void
spider_net_link_reset(struct net_device *netdev)
{
struct spider_net_card *card = netdev_priv(netdev);
del_timer_sync(&card->aneg_timer);
/* clear interrupt, block further interrupts */
spider_net_write_reg(card, SPIDER_NET_GMACST,
spider_net_read_reg(card, SPIDER_NET_GMACST));
spider_net_write_reg(card, SPIDER_NET_GMACINTEN, 0);
/* reset phy and setup aneg */
card->aneg_count = 0;
card->medium = BCM54XX_COPPER;
spider_net_setup_aneg(card);
mod_timer(&card->aneg_timer, jiffies + SPIDER_NET_ANEG_TIMER);
}
/**
* spider_net_handle_error_irq - handles errors raised by an interrupt
* @card: card structure
* @status_reg: interrupt status register 0 (GHIINT0STS)
*
* spider_net_handle_error_irq treats or ignores all error conditions
* found when an interrupt is presented
*/
static void
spider_net_handle_error_irq(struct spider_net_card *card, u32 status_reg,
u32 error_reg1, u32 error_reg2)
{
u32 i;
int show_error = 1;
/* check GHIINT0STS ************************************/
if (status_reg)
for (i = 0; i < 32; i++)
if (status_reg & (1<<i))
switch (i)
{
/* let error_reg1 and error_reg2 evaluation decide, what to do
case SPIDER_NET_PHYINT:
case SPIDER_NET_GMAC2INT:
case SPIDER_NET_GMAC1INT:
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;
break;
case SPIDER_NET_GPROPCMPINT:
/* PHY read operation completed */
/* we don't use semaphores, as we poll for the completion
* of the read operation in spider_net_read_phy. Should take
* about 50 us */
show_error = 0;
break;
case SPIDER_NET_GPWFFINT:
/* PHY command queue full */
if (netif_msg_intr(card))
dev_err(&card->netdev->dev, "PHY write queue full\n");
show_error = 0;
break;
/* case SPIDER_NET_GRMDADRINT: not used. print a message */
/* case SPIDER_NET_GRMARPINT: not used. print a message */
/* case SPIDER_NET_GRMMPINT: not used. print a message */
case SPIDER_NET_GDTDEN0INT:
/* someone has set TX_DMA_EN to 0 */
show_error = 0;
break;
case SPIDER_NET_GDDDEN0INT: /* fallthrough */
case SPIDER_NET_GDCDEN0INT: /* fallthrough */
case SPIDER_NET_GDBDEN0INT: /* fallthrough */
case SPIDER_NET_GDADEN0INT:
/* someone has set RX_DMA_EN to 0 */
show_error = 0;
break;
/* RX interrupts */
case SPIDER_NET_GDDFDCINT:
case SPIDER_NET_GDCFDCINT:
case SPIDER_NET_GDBFDCINT:
case SPIDER_NET_GDAFDCINT:
/* case SPIDER_NET_GDNMINT: not used. print a message */
/* case SPIDER_NET_GCNMINT: not used. print a message */
/* case SPIDER_NET_GBNMINT: not used. print a message */
/* case SPIDER_NET_GANMINT: not used. print a message */
/* case SPIDER_NET_GRFNMINT: not used. print a message */
show_error = 0;
break;
/* TX interrupts */
case SPIDER_NET_GDTFDCINT:
show_error = 0;
break;
case SPIDER_NET_GTTEDINT:
show_error = 0;
break;
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)
spider_net_kick_tx_dma(card);
*/
show_error = 0;
break;
/* case SPIDER_NET_G1TMCNTINT: not used. print a message */
/* case SPIDER_NET_GFREECNTINT: not used. print a message */
}
/* check GHIINT1STS ************************************/
if (error_reg1)
for (i = 0; i < 32; i++)
if (error_reg1 & (1<<i))
switch (i)
{
case SPIDER_NET_GTMFLLINT:
/* TX RAM full may happen on a usual case.
* Logging is not needed. */
show_error = 0;
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:
/* Could happen when rx chain is full */
if (card->ignore_rx_ramfull == 0) {
card->ignore_rx_ramfull = 1;
spider_net_resync_head_ptr(card);
spider_net_refill_rx_chain(card);
spider_net_enable_rxdmac(card);
card->num_rx_ints ++;
netif_rx_schedule(card->netdev,
&card->napi);
}
show_error = 0;
break;
/* case SPIDER_NET_GTMSHTINT: problem, print a message */
case SPIDER_NET_GDTINVDINT:
/* allrighty. tx from previous descr ok */
show_error = 0;
break;
/* chain end */
case SPIDER_NET_GDDDCEINT: /* fallthrough */
case SPIDER_NET_GDCDCEINT: /* fallthrough */
case SPIDER_NET_GDBDCEINT: /* fallthrough */
case SPIDER_NET_GDADCEINT:
spider_net_resync_head_ptr(card);
spider_net_refill_rx_chain(card);
spider_net_enable_rxdmac(card);
card->num_rx_ints ++;
netif_rx_schedule(card->netdev,
&card->napi);
show_error = 0;
break;
/* invalid descriptor */
case SPIDER_NET_GDDINVDINT: /* fallthrough */
case SPIDER_NET_GDCINVDINT: /* fallthrough */
case SPIDER_NET_GDBINVDINT: /* fallthrough */
case SPIDER_NET_GDAINVDINT:
/* Could happen when rx chain is full */
spider_net_resync_head_ptr(card);
spider_net_refill_rx_chain(card);
spider_net_enable_rxdmac(card);
card->num_rx_ints ++;
netif_rx_schedule(card->netdev,
&card->napi);
show_error = 0;
break;
/* case SPIDER_NET_GDTRSERINT: problem, print a message */
/* case SPIDER_NET_GDDRSERINT: problem, print a message */
/* case SPIDER_NET_GDCRSERINT: problem, print a message */
/* case SPIDER_NET_GDBRSERINT: problem, print a message */
/* case SPIDER_NET_GDARSERINT: problem, print a message */
/* case SPIDER_NET_GDSERINT: problem, print a message */
/* case SPIDER_NET_GDTPTERINT: problem, print a message */
/* case SPIDER_NET_GDDPTERINT: problem, print a message */
/* case SPIDER_NET_GDCPTERINT: problem, print a message */
/* case SPIDER_NET_GDBPTERINT: problem, print a message */
/* case SPIDER_NET_GDAPTERINT: problem, print a message */
default:
show_error = 1;
break;
}
/* check GHIINT2STS ************************************/
if (error_reg2)
for (i = 0; i < 32; i++)
if (error_reg2 & (1<<i))
switch (i)
{
/* there is nothing we can (want to) do at this time. Log a
* message, we can switch on and off the specific values later on
case SPIDER_NET_GPROPERINT:
case SPIDER_NET_GMCTCRSNGINT:
case SPIDER_NET_GMCTLCOLINT:
case SPIDER_NET_GMCTTMOTINT:
case SPIDER_NET_GMCRCAERINT:
case SPIDER_NET_GMCRCALERINT:
case SPIDER_NET_GMCRALNERINT:
case SPIDER_NET_GMCROVRINT:
case SPIDER_NET_GMCRRNTINT:
case SPIDER_NET_GMCRRXERINT:
case SPIDER_NET_GTITCSERINT:
case SPIDER_NET_GTIFMTERINT:
case SPIDER_NET_GTIPKTRVKINT:
case SPIDER_NET_GTISPINGINT:
case SPIDER_NET_GTISADNGINT:
case SPIDER_NET_GTISPDNGINT:
case SPIDER_NET_GRIFMTERINT:
case SPIDER_NET_GRIPKTRVKINT:
case SPIDER_NET_GRISPINGINT:
case SPIDER_NET_GRISADNGINT:
case SPIDER_NET_GRISPDNGINT:
break;
*/
default:
break;
}
if ((show_error) && (netif_msg_intr(card)) && net_ratelimit())
dev_err(&card->netdev->dev, "Error interrupt, GHIINT0STS = 0x%08x, "
"GHIINT1STS = 0x%08x, GHIINT2STS = 0x%08x\n",
status_reg, error_reg1, error_reg2);
/* clear interrupt sources */
spider_net_write_reg(card, SPIDER_NET_GHIINT1STS, error_reg1);
spider_net_write_reg(card, SPIDER_NET_GHIINT2STS, error_reg2);
}
/**
* spider_net_interrupt - interrupt handler for spider_net
* @irq: interrupt number
* @ptr: pointer to net_device
* @regs: PU registers
*
* returns IRQ_HANDLED, if interrupt was for driver, or IRQ_NONE, if no
* interrupt found raised by card.
*
* This is the interrupt handler, that turns off
* interrupts for this device and makes the stack poll the driver
*/
static irqreturn_t
spider_net_interrupt(int irq, void *ptr)
{
struct net_device *netdev = ptr;
struct spider_net_card *card = netdev_priv(netdev);
u32 status_reg, error_reg1, error_reg2;
status_reg = spider_net_read_reg(card, SPIDER_NET_GHIINT0STS);