162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Network device driver for Cell Processor-Based Blade and Celleb platform 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (C) Copyright IBM Corp. 2005 662306a36Sopenharmony_ci * (C) Copyright 2006 TOSHIBA CORPORATION 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Authors : Utz Bacher <utz.bacher@de.ibm.com> 962306a36Sopenharmony_ci * Jens Osterkamp <Jens.Osterkamp@de.ibm.com> 1062306a36Sopenharmony_ci */ 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/compiler.h> 1362306a36Sopenharmony_ci#include <linux/crc32.h> 1462306a36Sopenharmony_ci#include <linux/delay.h> 1562306a36Sopenharmony_ci#include <linux/etherdevice.h> 1662306a36Sopenharmony_ci#include <linux/ethtool.h> 1762306a36Sopenharmony_ci#include <linux/firmware.h> 1862306a36Sopenharmony_ci#include <linux/if_vlan.h> 1962306a36Sopenharmony_ci#include <linux/in.h> 2062306a36Sopenharmony_ci#include <linux/init.h> 2162306a36Sopenharmony_ci#include <linux/interrupt.h> 2262306a36Sopenharmony_ci#include <linux/gfp.h> 2362306a36Sopenharmony_ci#include <linux/ioport.h> 2462306a36Sopenharmony_ci#include <linux/ip.h> 2562306a36Sopenharmony_ci#include <linux/kernel.h> 2662306a36Sopenharmony_ci#include <linux/mii.h> 2762306a36Sopenharmony_ci#include <linux/module.h> 2862306a36Sopenharmony_ci#include <linux/netdevice.h> 2962306a36Sopenharmony_ci#include <linux/device.h> 3062306a36Sopenharmony_ci#include <linux/pci.h> 3162306a36Sopenharmony_ci#include <linux/skbuff.h> 3262306a36Sopenharmony_ci#include <linux/tcp.h> 3362306a36Sopenharmony_ci#include <linux/types.h> 3462306a36Sopenharmony_ci#include <linux/vmalloc.h> 3562306a36Sopenharmony_ci#include <linux/wait.h> 3662306a36Sopenharmony_ci#include <linux/workqueue.h> 3762306a36Sopenharmony_ci#include <linux/bitops.h> 3862306a36Sopenharmony_ci#include <linux/of.h> 3962306a36Sopenharmony_ci#include <net/checksum.h> 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#include "spider_net.h" 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ciMODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com> and Jens Osterkamp " \ 4462306a36Sopenharmony_ci "<Jens.Osterkamp@de.ibm.com>"); 4562306a36Sopenharmony_ciMODULE_DESCRIPTION("Spider Southbridge Gigabit Ethernet driver"); 4662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 4762306a36Sopenharmony_ciMODULE_VERSION(VERSION); 4862306a36Sopenharmony_ciMODULE_FIRMWARE(SPIDER_NET_FIRMWARE_NAME); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic int rx_descriptors = SPIDER_NET_RX_DESCRIPTORS_DEFAULT; 5162306a36Sopenharmony_cistatic int tx_descriptors = SPIDER_NET_TX_DESCRIPTORS_DEFAULT; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_cimodule_param(rx_descriptors, int, 0444); 5462306a36Sopenharmony_cimodule_param(tx_descriptors, int, 0444); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ciMODULE_PARM_DESC(rx_descriptors, "number of descriptors used " \ 5762306a36Sopenharmony_ci "in rx chains"); 5862306a36Sopenharmony_ciMODULE_PARM_DESC(tx_descriptors, "number of descriptors used " \ 5962306a36Sopenharmony_ci "in tx chain"); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_cichar spider_net_driver_name[] = "spidernet"; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic const struct pci_device_id spider_net_pci_tbl[] = { 6462306a36Sopenharmony_ci { PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_SPIDER_NET, 6562306a36Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, 6662306a36Sopenharmony_ci { 0, } 6762306a36Sopenharmony_ci}; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, spider_net_pci_tbl); 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci/** 7262306a36Sopenharmony_ci * spider_net_read_reg - reads an SMMIO register of a card 7362306a36Sopenharmony_ci * @card: device structure 7462306a36Sopenharmony_ci * @reg: register to read from 7562306a36Sopenharmony_ci * 7662306a36Sopenharmony_ci * returns the content of the specified SMMIO register. 7762306a36Sopenharmony_ci */ 7862306a36Sopenharmony_cistatic inline u32 7962306a36Sopenharmony_cispider_net_read_reg(struct spider_net_card *card, u32 reg) 8062306a36Sopenharmony_ci{ 8162306a36Sopenharmony_ci /* We use the powerpc specific variants instead of readl_be() because 8262306a36Sopenharmony_ci * we know spidernet is not a real PCI device and we can thus avoid the 8362306a36Sopenharmony_ci * performance hit caused by the PCI workarounds. 8462306a36Sopenharmony_ci */ 8562306a36Sopenharmony_ci return in_be32(card->regs + reg); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci/** 8962306a36Sopenharmony_ci * spider_net_write_reg - writes to an SMMIO register of a card 9062306a36Sopenharmony_ci * @card: device structure 9162306a36Sopenharmony_ci * @reg: register to write to 9262306a36Sopenharmony_ci * @value: value to write into the specified SMMIO register 9362306a36Sopenharmony_ci */ 9462306a36Sopenharmony_cistatic inline void 9562306a36Sopenharmony_cispider_net_write_reg(struct spider_net_card *card, u32 reg, u32 value) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci /* We use the powerpc specific variants instead of writel_be() because 9862306a36Sopenharmony_ci * we know spidernet is not a real PCI device and we can thus avoid the 9962306a36Sopenharmony_ci * performance hit caused by the PCI workarounds. 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_ci out_be32(card->regs + reg, value); 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci/** 10562306a36Sopenharmony_ci * spider_net_write_phy - write to phy register 10662306a36Sopenharmony_ci * @netdev: adapter to be written to 10762306a36Sopenharmony_ci * @mii_id: id of MII 10862306a36Sopenharmony_ci * @reg: PHY register 10962306a36Sopenharmony_ci * @val: value to be written to phy register 11062306a36Sopenharmony_ci * 11162306a36Sopenharmony_ci * spider_net_write_phy_register writes to an arbitrary PHY 11262306a36Sopenharmony_ci * register via the spider GPCWOPCMD register. We assume the queue does 11362306a36Sopenharmony_ci * not run full (not more than 15 commands outstanding). 11462306a36Sopenharmony_ci **/ 11562306a36Sopenharmony_cistatic void 11662306a36Sopenharmony_cispider_net_write_phy(struct net_device *netdev, int mii_id, 11762306a36Sopenharmony_ci int reg, int val) 11862306a36Sopenharmony_ci{ 11962306a36Sopenharmony_ci struct spider_net_card *card = netdev_priv(netdev); 12062306a36Sopenharmony_ci u32 writevalue; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci writevalue = ((u32)mii_id << 21) | 12362306a36Sopenharmony_ci ((u32)reg << 16) | ((u32)val); 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GPCWOPCMD, writevalue); 12662306a36Sopenharmony_ci} 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci/** 12962306a36Sopenharmony_ci * spider_net_read_phy - read from phy register 13062306a36Sopenharmony_ci * @netdev: network device to be read from 13162306a36Sopenharmony_ci * @mii_id: id of MII 13262306a36Sopenharmony_ci * @reg: PHY register 13362306a36Sopenharmony_ci * 13462306a36Sopenharmony_ci * Returns value read from PHY register 13562306a36Sopenharmony_ci * 13662306a36Sopenharmony_ci * spider_net_write_phy reads from an arbitrary PHY 13762306a36Sopenharmony_ci * register via the spider GPCROPCMD register 13862306a36Sopenharmony_ci **/ 13962306a36Sopenharmony_cistatic int 14062306a36Sopenharmony_cispider_net_read_phy(struct net_device *netdev, int mii_id, int reg) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct spider_net_card *card = netdev_priv(netdev); 14362306a36Sopenharmony_ci u32 readvalue; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci readvalue = ((u32)mii_id << 21) | ((u32)reg << 16); 14662306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GPCROPCMD, readvalue); 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* we don't use semaphores to wait for an SPIDER_NET_GPROPCMPINT 14962306a36Sopenharmony_ci * interrupt, as we poll for the completion of the read operation 15062306a36Sopenharmony_ci * in spider_net_read_phy. Should take about 50 us 15162306a36Sopenharmony_ci */ 15262306a36Sopenharmony_ci do { 15362306a36Sopenharmony_ci readvalue = spider_net_read_reg(card, SPIDER_NET_GPCROPCMD); 15462306a36Sopenharmony_ci } while (readvalue & SPIDER_NET_GPREXEC); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci readvalue &= SPIDER_NET_GPRDAT_MASK; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci return readvalue; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci/** 16262306a36Sopenharmony_ci * spider_net_setup_aneg - initial auto-negotiation setup 16362306a36Sopenharmony_ci * @card: device structure 16462306a36Sopenharmony_ci **/ 16562306a36Sopenharmony_cistatic void 16662306a36Sopenharmony_cispider_net_setup_aneg(struct spider_net_card *card) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci struct mii_phy *phy = &card->phy; 16962306a36Sopenharmony_ci u32 advertise = 0; 17062306a36Sopenharmony_ci u16 bmsr, estat; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci bmsr = spider_net_read_phy(card->netdev, phy->mii_id, MII_BMSR); 17362306a36Sopenharmony_ci estat = spider_net_read_phy(card->netdev, phy->mii_id, MII_ESTATUS); 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (bmsr & BMSR_10HALF) 17662306a36Sopenharmony_ci advertise |= ADVERTISED_10baseT_Half; 17762306a36Sopenharmony_ci if (bmsr & BMSR_10FULL) 17862306a36Sopenharmony_ci advertise |= ADVERTISED_10baseT_Full; 17962306a36Sopenharmony_ci if (bmsr & BMSR_100HALF) 18062306a36Sopenharmony_ci advertise |= ADVERTISED_100baseT_Half; 18162306a36Sopenharmony_ci if (bmsr & BMSR_100FULL) 18262306a36Sopenharmony_ci advertise |= ADVERTISED_100baseT_Full; 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci if ((bmsr & BMSR_ESTATEN) && (estat & ESTATUS_1000_TFULL)) 18562306a36Sopenharmony_ci advertise |= SUPPORTED_1000baseT_Full; 18662306a36Sopenharmony_ci if ((bmsr & BMSR_ESTATEN) && (estat & ESTATUS_1000_THALF)) 18762306a36Sopenharmony_ci advertise |= SUPPORTED_1000baseT_Half; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci sungem_phy_probe(phy, phy->mii_id); 19062306a36Sopenharmony_ci phy->def->ops->setup_aneg(phy, advertise); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci/** 19562306a36Sopenharmony_ci * spider_net_rx_irq_off - switch off rx irq on this spider card 19662306a36Sopenharmony_ci * @card: device structure 19762306a36Sopenharmony_ci * 19862306a36Sopenharmony_ci * switches off rx irq by masking them out in the GHIINTnMSK register 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_cistatic void 20162306a36Sopenharmony_cispider_net_rx_irq_off(struct spider_net_card *card) 20262306a36Sopenharmony_ci{ 20362306a36Sopenharmony_ci u32 regvalue; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci regvalue = SPIDER_NET_INT0_MASK_VALUE & (~SPIDER_NET_RXINT); 20662306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GHIINT0MSK, regvalue); 20762306a36Sopenharmony_ci} 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci/** 21062306a36Sopenharmony_ci * spider_net_rx_irq_on - switch on rx irq on this spider card 21162306a36Sopenharmony_ci * @card: device structure 21262306a36Sopenharmony_ci * 21362306a36Sopenharmony_ci * switches on rx irq by enabling them in the GHIINTnMSK register 21462306a36Sopenharmony_ci */ 21562306a36Sopenharmony_cistatic void 21662306a36Sopenharmony_cispider_net_rx_irq_on(struct spider_net_card *card) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci u32 regvalue; 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_ci regvalue = SPIDER_NET_INT0_MASK_VALUE | SPIDER_NET_RXINT; 22162306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GHIINT0MSK, regvalue); 22262306a36Sopenharmony_ci} 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci/** 22562306a36Sopenharmony_ci * spider_net_set_promisc - sets the unicast address or the promiscuous mode 22662306a36Sopenharmony_ci * @card: card structure 22762306a36Sopenharmony_ci * 22862306a36Sopenharmony_ci * spider_net_set_promisc sets the unicast destination address filter and 22962306a36Sopenharmony_ci * thus either allows for non-promisc mode or promisc mode 23062306a36Sopenharmony_ci */ 23162306a36Sopenharmony_cistatic void 23262306a36Sopenharmony_cispider_net_set_promisc(struct spider_net_card *card) 23362306a36Sopenharmony_ci{ 23462306a36Sopenharmony_ci u32 macu, macl; 23562306a36Sopenharmony_ci struct net_device *netdev = card->netdev; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci if (netdev->flags & IFF_PROMISC) { 23862306a36Sopenharmony_ci /* clear destination entry 0 */ 23962306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GMRUAFILnR, 0); 24062306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GMRUAFILnR + 0x04, 0); 24162306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GMRUA0FIL15R, 24262306a36Sopenharmony_ci SPIDER_NET_PROMISC_VALUE); 24362306a36Sopenharmony_ci } else { 24462306a36Sopenharmony_ci macu = netdev->dev_addr[0]; 24562306a36Sopenharmony_ci macu <<= 8; 24662306a36Sopenharmony_ci macu |= netdev->dev_addr[1]; 24762306a36Sopenharmony_ci memcpy(&macl, &netdev->dev_addr[2], sizeof(macl)); 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci macu |= SPIDER_NET_UA_DESCR_VALUE; 25062306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GMRUAFILnR, macu); 25162306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GMRUAFILnR + 0x04, macl); 25262306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GMRUA0FIL15R, 25362306a36Sopenharmony_ci SPIDER_NET_NONPROMISC_VALUE); 25462306a36Sopenharmony_ci } 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci/** 25862306a36Sopenharmony_ci * spider_net_get_descr_status -- returns the status of a descriptor 25962306a36Sopenharmony_ci * @hwdescr: descriptor to look at 26062306a36Sopenharmony_ci * 26162306a36Sopenharmony_ci * returns the status as in the dmac_cmd_status field of the descriptor 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_cistatic inline int 26462306a36Sopenharmony_cispider_net_get_descr_status(struct spider_net_hw_descr *hwdescr) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci return hwdescr->dmac_cmd_status & SPIDER_NET_DESCR_IND_PROC_MASK; 26762306a36Sopenharmony_ci} 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci/** 27062306a36Sopenharmony_ci * spider_net_free_chain - free descriptor chain 27162306a36Sopenharmony_ci * @card: card structure 27262306a36Sopenharmony_ci * @chain: address of chain 27362306a36Sopenharmony_ci * 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_cistatic void 27662306a36Sopenharmony_cispider_net_free_chain(struct spider_net_card *card, 27762306a36Sopenharmony_ci struct spider_net_descr_chain *chain) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci struct spider_net_descr *descr; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci descr = chain->ring; 28262306a36Sopenharmony_ci do { 28362306a36Sopenharmony_ci descr->bus_addr = 0; 28462306a36Sopenharmony_ci descr->hwdescr->next_descr_addr = 0; 28562306a36Sopenharmony_ci descr = descr->next; 28662306a36Sopenharmony_ci } while (descr != chain->ring); 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci dma_free_coherent(&card->pdev->dev, chain->num_desc * sizeof(struct spider_net_hw_descr), 28962306a36Sopenharmony_ci chain->hwring, chain->dma_addr); 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci/** 29362306a36Sopenharmony_ci * spider_net_init_chain - alloc and link descriptor chain 29462306a36Sopenharmony_ci * @card: card structure 29562306a36Sopenharmony_ci * @chain: address of chain 29662306a36Sopenharmony_ci * 29762306a36Sopenharmony_ci * We manage a circular list that mirrors the hardware structure, 29862306a36Sopenharmony_ci * except that the hardware uses bus addresses. 29962306a36Sopenharmony_ci * 30062306a36Sopenharmony_ci * Returns 0 on success, <0 on failure 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_cistatic int 30362306a36Sopenharmony_cispider_net_init_chain(struct spider_net_card *card, 30462306a36Sopenharmony_ci struct spider_net_descr_chain *chain) 30562306a36Sopenharmony_ci{ 30662306a36Sopenharmony_ci int i; 30762306a36Sopenharmony_ci struct spider_net_descr *descr; 30862306a36Sopenharmony_ci struct spider_net_hw_descr *hwdescr; 30962306a36Sopenharmony_ci dma_addr_t buf; 31062306a36Sopenharmony_ci size_t alloc_size; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci alloc_size = chain->num_desc * sizeof(struct spider_net_hw_descr); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci chain->hwring = dma_alloc_coherent(&card->pdev->dev, alloc_size, 31562306a36Sopenharmony_ci &chain->dma_addr, GFP_KERNEL); 31662306a36Sopenharmony_ci if (!chain->hwring) 31762306a36Sopenharmony_ci return -ENOMEM; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* Set up the hardware pointers in each descriptor */ 32062306a36Sopenharmony_ci descr = chain->ring; 32162306a36Sopenharmony_ci hwdescr = chain->hwring; 32262306a36Sopenharmony_ci buf = chain->dma_addr; 32362306a36Sopenharmony_ci for (i=0; i < chain->num_desc; i++, descr++, hwdescr++) { 32462306a36Sopenharmony_ci hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; 32562306a36Sopenharmony_ci hwdescr->next_descr_addr = 0; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci descr->hwdescr = hwdescr; 32862306a36Sopenharmony_ci descr->bus_addr = buf; 32962306a36Sopenharmony_ci descr->next = descr + 1; 33062306a36Sopenharmony_ci descr->prev = descr - 1; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci buf += sizeof(struct spider_net_hw_descr); 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci /* do actual circular list */ 33562306a36Sopenharmony_ci (descr-1)->next = chain->ring; 33662306a36Sopenharmony_ci chain->ring->prev = descr-1; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci spin_lock_init(&chain->lock); 33962306a36Sopenharmony_ci chain->head = chain->ring; 34062306a36Sopenharmony_ci chain->tail = chain->ring; 34162306a36Sopenharmony_ci return 0; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci/** 34562306a36Sopenharmony_ci * spider_net_free_rx_chain_contents - frees descr contents in rx chain 34662306a36Sopenharmony_ci * @card: card structure 34762306a36Sopenharmony_ci * 34862306a36Sopenharmony_ci * returns 0 on success, <0 on failure 34962306a36Sopenharmony_ci */ 35062306a36Sopenharmony_cistatic void 35162306a36Sopenharmony_cispider_net_free_rx_chain_contents(struct spider_net_card *card) 35262306a36Sopenharmony_ci{ 35362306a36Sopenharmony_ci struct spider_net_descr *descr; 35462306a36Sopenharmony_ci 35562306a36Sopenharmony_ci descr = card->rx_chain.head; 35662306a36Sopenharmony_ci do { 35762306a36Sopenharmony_ci if (descr->skb) { 35862306a36Sopenharmony_ci dma_unmap_single(&card->pdev->dev, 35962306a36Sopenharmony_ci descr->hwdescr->buf_addr, 36062306a36Sopenharmony_ci SPIDER_NET_MAX_FRAME, 36162306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 36262306a36Sopenharmony_ci dev_kfree_skb(descr->skb); 36362306a36Sopenharmony_ci descr->skb = NULL; 36462306a36Sopenharmony_ci } 36562306a36Sopenharmony_ci descr = descr->next; 36662306a36Sopenharmony_ci } while (descr != card->rx_chain.head); 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci/** 37062306a36Sopenharmony_ci * spider_net_prepare_rx_descr - Reinitialize RX descriptor 37162306a36Sopenharmony_ci * @card: card structure 37262306a36Sopenharmony_ci * @descr: descriptor to re-init 37362306a36Sopenharmony_ci * 37462306a36Sopenharmony_ci * Return 0 on success, <0 on failure. 37562306a36Sopenharmony_ci * 37662306a36Sopenharmony_ci * Allocates a new rx skb, iommu-maps it and attaches it to the 37762306a36Sopenharmony_ci * descriptor. Mark the descriptor as activated, ready-to-use. 37862306a36Sopenharmony_ci */ 37962306a36Sopenharmony_cistatic int 38062306a36Sopenharmony_cispider_net_prepare_rx_descr(struct spider_net_card *card, 38162306a36Sopenharmony_ci struct spider_net_descr *descr) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci struct spider_net_hw_descr *hwdescr = descr->hwdescr; 38462306a36Sopenharmony_ci dma_addr_t buf; 38562306a36Sopenharmony_ci int offset; 38662306a36Sopenharmony_ci int bufsize; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci /* we need to round up the buffer size to a multiple of 128 */ 38962306a36Sopenharmony_ci bufsize = (SPIDER_NET_MAX_FRAME + SPIDER_NET_RXBUF_ALIGN - 1) & 39062306a36Sopenharmony_ci (~(SPIDER_NET_RXBUF_ALIGN - 1)); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci /* and we need to have it 128 byte aligned, therefore we allocate a 39362306a36Sopenharmony_ci * bit more 39462306a36Sopenharmony_ci */ 39562306a36Sopenharmony_ci /* allocate an skb */ 39662306a36Sopenharmony_ci descr->skb = netdev_alloc_skb(card->netdev, 39762306a36Sopenharmony_ci bufsize + SPIDER_NET_RXBUF_ALIGN - 1); 39862306a36Sopenharmony_ci if (!descr->skb) { 39962306a36Sopenharmony_ci if (netif_msg_rx_err(card) && net_ratelimit()) 40062306a36Sopenharmony_ci dev_err(&card->netdev->dev, 40162306a36Sopenharmony_ci "Not enough memory to allocate rx buffer\n"); 40262306a36Sopenharmony_ci card->spider_stats.alloc_rx_skb_error++; 40362306a36Sopenharmony_ci return -ENOMEM; 40462306a36Sopenharmony_ci } 40562306a36Sopenharmony_ci hwdescr->buf_size = bufsize; 40662306a36Sopenharmony_ci hwdescr->result_size = 0; 40762306a36Sopenharmony_ci hwdescr->valid_size = 0; 40862306a36Sopenharmony_ci hwdescr->data_status = 0; 40962306a36Sopenharmony_ci hwdescr->data_error = 0; 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci offset = ((unsigned long)descr->skb->data) & 41262306a36Sopenharmony_ci (SPIDER_NET_RXBUF_ALIGN - 1); 41362306a36Sopenharmony_ci if (offset) 41462306a36Sopenharmony_ci skb_reserve(descr->skb, SPIDER_NET_RXBUF_ALIGN - offset); 41562306a36Sopenharmony_ci /* iommu-map the skb */ 41662306a36Sopenharmony_ci buf = dma_map_single(&card->pdev->dev, descr->skb->data, 41762306a36Sopenharmony_ci SPIDER_NET_MAX_FRAME, DMA_FROM_DEVICE); 41862306a36Sopenharmony_ci if (dma_mapping_error(&card->pdev->dev, buf)) { 41962306a36Sopenharmony_ci dev_kfree_skb_any(descr->skb); 42062306a36Sopenharmony_ci descr->skb = NULL; 42162306a36Sopenharmony_ci if (netif_msg_rx_err(card) && net_ratelimit()) 42262306a36Sopenharmony_ci dev_err(&card->netdev->dev, "Could not iommu-map rx buffer\n"); 42362306a36Sopenharmony_ci card->spider_stats.rx_iommu_map_error++; 42462306a36Sopenharmony_ci hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; 42562306a36Sopenharmony_ci } else { 42662306a36Sopenharmony_ci hwdescr->buf_addr = buf; 42762306a36Sopenharmony_ci wmb(); 42862306a36Sopenharmony_ci hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_CARDOWNED | 42962306a36Sopenharmony_ci SPIDER_NET_DMAC_NOINTR_COMPLETE; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci return 0; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci/** 43662306a36Sopenharmony_ci * spider_net_enable_rxchtails - sets RX dmac chain tail addresses 43762306a36Sopenharmony_ci * @card: card structure 43862306a36Sopenharmony_ci * 43962306a36Sopenharmony_ci * spider_net_enable_rxchtails sets the RX DMAC chain tail addresses in the 44062306a36Sopenharmony_ci * chip by writing to the appropriate register. DMA is enabled in 44162306a36Sopenharmony_ci * spider_net_enable_rxdmac. 44262306a36Sopenharmony_ci */ 44362306a36Sopenharmony_cistatic inline void 44462306a36Sopenharmony_cispider_net_enable_rxchtails(struct spider_net_card *card) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci /* assume chain is aligned correctly */ 44762306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GDADCHA , 44862306a36Sopenharmony_ci card->rx_chain.tail->bus_addr); 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci/** 45262306a36Sopenharmony_ci * spider_net_enable_rxdmac - enables a receive DMA controller 45362306a36Sopenharmony_ci * @card: card structure 45462306a36Sopenharmony_ci * 45562306a36Sopenharmony_ci * spider_net_enable_rxdmac enables the DMA controller by setting RX_DMA_EN 45662306a36Sopenharmony_ci * in the GDADMACCNTR register 45762306a36Sopenharmony_ci */ 45862306a36Sopenharmony_cistatic inline void 45962306a36Sopenharmony_cispider_net_enable_rxdmac(struct spider_net_card *card) 46062306a36Sopenharmony_ci{ 46162306a36Sopenharmony_ci wmb(); 46262306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GDADMACCNTR, 46362306a36Sopenharmony_ci SPIDER_NET_DMA_RX_VALUE); 46462306a36Sopenharmony_ci} 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci/** 46762306a36Sopenharmony_ci * spider_net_disable_rxdmac - disables the receive DMA controller 46862306a36Sopenharmony_ci * @card: card structure 46962306a36Sopenharmony_ci * 47062306a36Sopenharmony_ci * spider_net_disable_rxdmac terminates processing on the DMA controller 47162306a36Sopenharmony_ci * by turing off the DMA controller, with the force-end flag set. 47262306a36Sopenharmony_ci */ 47362306a36Sopenharmony_cistatic inline void 47462306a36Sopenharmony_cispider_net_disable_rxdmac(struct spider_net_card *card) 47562306a36Sopenharmony_ci{ 47662306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GDADMACCNTR, 47762306a36Sopenharmony_ci SPIDER_NET_DMA_RX_FEND_VALUE); 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci/** 48162306a36Sopenharmony_ci * spider_net_refill_rx_chain - refills descriptors/skbs in the rx chains 48262306a36Sopenharmony_ci * @card: card structure 48362306a36Sopenharmony_ci * 48462306a36Sopenharmony_ci * refills descriptors in the rx chain: allocates skbs and iommu-maps them. 48562306a36Sopenharmony_ci */ 48662306a36Sopenharmony_cistatic void 48762306a36Sopenharmony_cispider_net_refill_rx_chain(struct spider_net_card *card) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci struct spider_net_descr_chain *chain = &card->rx_chain; 49062306a36Sopenharmony_ci unsigned long flags; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* one context doing the refill (and a second context seeing that 49362306a36Sopenharmony_ci * and omitting it) is ok. If called by NAPI, we'll be called again 49462306a36Sopenharmony_ci * as spider_net_decode_one_descr is called several times. If some 49562306a36Sopenharmony_ci * interrupt calls us, the NAPI is about to clean up anyway. 49662306a36Sopenharmony_ci */ 49762306a36Sopenharmony_ci if (!spin_trylock_irqsave(&chain->lock, flags)) 49862306a36Sopenharmony_ci return; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci while (spider_net_get_descr_status(chain->head->hwdescr) == 50162306a36Sopenharmony_ci SPIDER_NET_DESCR_NOT_IN_USE) { 50262306a36Sopenharmony_ci if (spider_net_prepare_rx_descr(card, chain->head)) 50362306a36Sopenharmony_ci break; 50462306a36Sopenharmony_ci chain->head = chain->head->next; 50562306a36Sopenharmony_ci } 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci spin_unlock_irqrestore(&chain->lock, flags); 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci/** 51162306a36Sopenharmony_ci * spider_net_alloc_rx_skbs - Allocates rx skbs in rx descriptor chains 51262306a36Sopenharmony_ci * @card: card structure 51362306a36Sopenharmony_ci * 51462306a36Sopenharmony_ci * Returns 0 on success, <0 on failure. 51562306a36Sopenharmony_ci */ 51662306a36Sopenharmony_cistatic int 51762306a36Sopenharmony_cispider_net_alloc_rx_skbs(struct spider_net_card *card) 51862306a36Sopenharmony_ci{ 51962306a36Sopenharmony_ci struct spider_net_descr_chain *chain = &card->rx_chain; 52062306a36Sopenharmony_ci struct spider_net_descr *start = chain->tail; 52162306a36Sopenharmony_ci struct spider_net_descr *descr = start; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci /* Link up the hardware chain pointers */ 52462306a36Sopenharmony_ci do { 52562306a36Sopenharmony_ci descr->prev->hwdescr->next_descr_addr = descr->bus_addr; 52662306a36Sopenharmony_ci descr = descr->next; 52762306a36Sopenharmony_ci } while (descr != start); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* Put at least one buffer into the chain. if this fails, 53062306a36Sopenharmony_ci * we've got a problem. If not, spider_net_refill_rx_chain 53162306a36Sopenharmony_ci * will do the rest at the end of this function. 53262306a36Sopenharmony_ci */ 53362306a36Sopenharmony_ci if (spider_net_prepare_rx_descr(card, chain->head)) 53462306a36Sopenharmony_ci goto error; 53562306a36Sopenharmony_ci else 53662306a36Sopenharmony_ci chain->head = chain->head->next; 53762306a36Sopenharmony_ci 53862306a36Sopenharmony_ci /* This will allocate the rest of the rx buffers; 53962306a36Sopenharmony_ci * if not, it's business as usual later on. 54062306a36Sopenharmony_ci */ 54162306a36Sopenharmony_ci spider_net_refill_rx_chain(card); 54262306a36Sopenharmony_ci spider_net_enable_rxdmac(card); 54362306a36Sopenharmony_ci return 0; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cierror: 54662306a36Sopenharmony_ci spider_net_free_rx_chain_contents(card); 54762306a36Sopenharmony_ci return -ENOMEM; 54862306a36Sopenharmony_ci} 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci/** 55162306a36Sopenharmony_ci * spider_net_get_multicast_hash - generates hash for multicast filter table 55262306a36Sopenharmony_ci * @netdev: interface device structure 55362306a36Sopenharmony_ci * @addr: multicast address 55462306a36Sopenharmony_ci * 55562306a36Sopenharmony_ci * returns the hash value. 55662306a36Sopenharmony_ci * 55762306a36Sopenharmony_ci * spider_net_get_multicast_hash calculates a hash value for a given multicast 55862306a36Sopenharmony_ci * address, that is used to set the multicast filter tables 55962306a36Sopenharmony_ci */ 56062306a36Sopenharmony_cistatic u8 56162306a36Sopenharmony_cispider_net_get_multicast_hash(struct net_device *netdev, __u8 *addr) 56262306a36Sopenharmony_ci{ 56362306a36Sopenharmony_ci u32 crc; 56462306a36Sopenharmony_ci u8 hash; 56562306a36Sopenharmony_ci char addr_for_crc[ETH_ALEN] = { 0, }; 56662306a36Sopenharmony_ci int i, bit; 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci for (i = 0; i < ETH_ALEN * 8; i++) { 56962306a36Sopenharmony_ci bit = (addr[i / 8] >> (i % 8)) & 1; 57062306a36Sopenharmony_ci addr_for_crc[ETH_ALEN - 1 - i / 8] += bit << (7 - (i % 8)); 57162306a36Sopenharmony_ci } 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci crc = crc32_be(~0, addr_for_crc, netdev->addr_len); 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci hash = (crc >> 27); 57662306a36Sopenharmony_ci hash <<= 3; 57762306a36Sopenharmony_ci hash |= crc & 7; 57862306a36Sopenharmony_ci hash &= 0xff; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci return hash; 58162306a36Sopenharmony_ci} 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci/** 58462306a36Sopenharmony_ci * spider_net_set_multi - sets multicast addresses and promisc flags 58562306a36Sopenharmony_ci * @netdev: interface device structure 58662306a36Sopenharmony_ci * 58762306a36Sopenharmony_ci * spider_net_set_multi configures multicast addresses as needed for the 58862306a36Sopenharmony_ci * netdev interface. It also sets up multicast, allmulti and promisc 58962306a36Sopenharmony_ci * flags appropriately 59062306a36Sopenharmony_ci */ 59162306a36Sopenharmony_cistatic void 59262306a36Sopenharmony_cispider_net_set_multi(struct net_device *netdev) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci struct netdev_hw_addr *ha; 59562306a36Sopenharmony_ci u8 hash; 59662306a36Sopenharmony_ci int i; 59762306a36Sopenharmony_ci u32 reg; 59862306a36Sopenharmony_ci struct spider_net_card *card = netdev_priv(netdev); 59962306a36Sopenharmony_ci DECLARE_BITMAP(bitmask, SPIDER_NET_MULTICAST_HASHES); 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci spider_net_set_promisc(card); 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci if (netdev->flags & IFF_ALLMULTI) { 60462306a36Sopenharmony_ci bitmap_fill(bitmask, SPIDER_NET_MULTICAST_HASHES); 60562306a36Sopenharmony_ci goto write_hash; 60662306a36Sopenharmony_ci } 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci bitmap_zero(bitmask, SPIDER_NET_MULTICAST_HASHES); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci /* well, we know, what the broadcast hash value is: it's xfd 61162306a36Sopenharmony_ci hash = spider_net_get_multicast_hash(netdev, netdev->broadcast); */ 61262306a36Sopenharmony_ci __set_bit(0xfd, bitmask); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, netdev) { 61562306a36Sopenharmony_ci hash = spider_net_get_multicast_hash(netdev, ha->addr); 61662306a36Sopenharmony_ci __set_bit(hash, bitmask); 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ciwrite_hash: 62062306a36Sopenharmony_ci for (i = 0; i < SPIDER_NET_MULTICAST_HASHES / 4; i++) { 62162306a36Sopenharmony_ci reg = 0; 62262306a36Sopenharmony_ci if (test_bit(i * 4, bitmask)) 62362306a36Sopenharmony_ci reg += 0x08; 62462306a36Sopenharmony_ci reg <<= 8; 62562306a36Sopenharmony_ci if (test_bit(i * 4 + 1, bitmask)) 62662306a36Sopenharmony_ci reg += 0x08; 62762306a36Sopenharmony_ci reg <<= 8; 62862306a36Sopenharmony_ci if (test_bit(i * 4 + 2, bitmask)) 62962306a36Sopenharmony_ci reg += 0x08; 63062306a36Sopenharmony_ci reg <<= 8; 63162306a36Sopenharmony_ci if (test_bit(i * 4 + 3, bitmask)) 63262306a36Sopenharmony_ci reg += 0x08; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GMRMHFILnR + i * 4, reg); 63562306a36Sopenharmony_ci } 63662306a36Sopenharmony_ci} 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci/** 63962306a36Sopenharmony_ci * spider_net_prepare_tx_descr - fill tx descriptor with skb data 64062306a36Sopenharmony_ci * @card: card structure 64162306a36Sopenharmony_ci * @skb: packet to use 64262306a36Sopenharmony_ci * 64362306a36Sopenharmony_ci * returns 0 on success, <0 on failure. 64462306a36Sopenharmony_ci * 64562306a36Sopenharmony_ci * fills out the descriptor structure with skb data and len. Copies data, 64662306a36Sopenharmony_ci * if needed (32bit DMA!) 64762306a36Sopenharmony_ci */ 64862306a36Sopenharmony_cistatic int 64962306a36Sopenharmony_cispider_net_prepare_tx_descr(struct spider_net_card *card, 65062306a36Sopenharmony_ci struct sk_buff *skb) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci struct spider_net_descr_chain *chain = &card->tx_chain; 65362306a36Sopenharmony_ci struct spider_net_descr *descr; 65462306a36Sopenharmony_ci struct spider_net_hw_descr *hwdescr; 65562306a36Sopenharmony_ci dma_addr_t buf; 65662306a36Sopenharmony_ci unsigned long flags; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci buf = dma_map_single(&card->pdev->dev, skb->data, skb->len, 65962306a36Sopenharmony_ci DMA_TO_DEVICE); 66062306a36Sopenharmony_ci if (dma_mapping_error(&card->pdev->dev, buf)) { 66162306a36Sopenharmony_ci if (netif_msg_tx_err(card) && net_ratelimit()) 66262306a36Sopenharmony_ci dev_err(&card->netdev->dev, "could not iommu-map packet (%p, %i). " 66362306a36Sopenharmony_ci "Dropping packet\n", skb->data, skb->len); 66462306a36Sopenharmony_ci card->spider_stats.tx_iommu_map_error++; 66562306a36Sopenharmony_ci return -ENOMEM; 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci spin_lock_irqsave(&chain->lock, flags); 66962306a36Sopenharmony_ci descr = card->tx_chain.head; 67062306a36Sopenharmony_ci if (descr->next == chain->tail->prev) { 67162306a36Sopenharmony_ci spin_unlock_irqrestore(&chain->lock, flags); 67262306a36Sopenharmony_ci dma_unmap_single(&card->pdev->dev, buf, skb->len, 67362306a36Sopenharmony_ci DMA_TO_DEVICE); 67462306a36Sopenharmony_ci return -ENOMEM; 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci hwdescr = descr->hwdescr; 67762306a36Sopenharmony_ci chain->head = descr->next; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci descr->skb = skb; 68062306a36Sopenharmony_ci hwdescr->buf_addr = buf; 68162306a36Sopenharmony_ci hwdescr->buf_size = skb->len; 68262306a36Sopenharmony_ci hwdescr->next_descr_addr = 0; 68362306a36Sopenharmony_ci hwdescr->data_status = 0; 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci hwdescr->dmac_cmd_status = 68662306a36Sopenharmony_ci SPIDER_NET_DESCR_CARDOWNED | SPIDER_NET_DMAC_TXFRMTL; 68762306a36Sopenharmony_ci spin_unlock_irqrestore(&chain->lock, flags); 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci if (skb->ip_summed == CHECKSUM_PARTIAL) 69062306a36Sopenharmony_ci switch (ip_hdr(skb)->protocol) { 69162306a36Sopenharmony_ci case IPPROTO_TCP: 69262306a36Sopenharmony_ci hwdescr->dmac_cmd_status |= SPIDER_NET_DMAC_TCP; 69362306a36Sopenharmony_ci break; 69462306a36Sopenharmony_ci case IPPROTO_UDP: 69562306a36Sopenharmony_ci hwdescr->dmac_cmd_status |= SPIDER_NET_DMAC_UDP; 69662306a36Sopenharmony_ci break; 69762306a36Sopenharmony_ci } 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci /* Chain the bus address, so that the DMA engine finds this descr. */ 70062306a36Sopenharmony_ci wmb(); 70162306a36Sopenharmony_ci descr->prev->hwdescr->next_descr_addr = descr->bus_addr; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci netif_trans_update(card->netdev); /* set netdev watchdog timer */ 70462306a36Sopenharmony_ci return 0; 70562306a36Sopenharmony_ci} 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_cistatic int 70862306a36Sopenharmony_cispider_net_set_low_watermark(struct spider_net_card *card) 70962306a36Sopenharmony_ci{ 71062306a36Sopenharmony_ci struct spider_net_descr *descr = card->tx_chain.tail; 71162306a36Sopenharmony_ci struct spider_net_hw_descr *hwdescr; 71262306a36Sopenharmony_ci unsigned long flags; 71362306a36Sopenharmony_ci int status; 71462306a36Sopenharmony_ci int cnt=0; 71562306a36Sopenharmony_ci int i; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci /* Measure the length of the queue. Measurement does not 71862306a36Sopenharmony_ci * need to be precise -- does not need a lock. 71962306a36Sopenharmony_ci */ 72062306a36Sopenharmony_ci while (descr != card->tx_chain.head) { 72162306a36Sopenharmony_ci status = descr->hwdescr->dmac_cmd_status & SPIDER_NET_DESCR_NOT_IN_USE; 72262306a36Sopenharmony_ci if (status == SPIDER_NET_DESCR_NOT_IN_USE) 72362306a36Sopenharmony_ci break; 72462306a36Sopenharmony_ci descr = descr->next; 72562306a36Sopenharmony_ci cnt++; 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci /* If TX queue is short, don't even bother with interrupts */ 72962306a36Sopenharmony_ci if (cnt < card->tx_chain.num_desc/4) 73062306a36Sopenharmony_ci return cnt; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci /* Set low-watermark 3/4th's of the way into the queue. */ 73362306a36Sopenharmony_ci descr = card->tx_chain.tail; 73462306a36Sopenharmony_ci cnt = (cnt*3)/4; 73562306a36Sopenharmony_ci for (i=0;i<cnt; i++) 73662306a36Sopenharmony_ci descr = descr->next; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci /* Set the new watermark, clear the old watermark */ 73962306a36Sopenharmony_ci spin_lock_irqsave(&card->tx_chain.lock, flags); 74062306a36Sopenharmony_ci descr->hwdescr->dmac_cmd_status |= SPIDER_NET_DESCR_TXDESFLG; 74162306a36Sopenharmony_ci if (card->low_watermark && card->low_watermark != descr) { 74262306a36Sopenharmony_ci hwdescr = card->low_watermark->hwdescr; 74362306a36Sopenharmony_ci hwdescr->dmac_cmd_status = 74462306a36Sopenharmony_ci hwdescr->dmac_cmd_status & ~SPIDER_NET_DESCR_TXDESFLG; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci card->low_watermark = descr; 74762306a36Sopenharmony_ci spin_unlock_irqrestore(&card->tx_chain.lock, flags); 74862306a36Sopenharmony_ci return cnt; 74962306a36Sopenharmony_ci} 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci/** 75262306a36Sopenharmony_ci * spider_net_release_tx_chain - processes sent tx descriptors 75362306a36Sopenharmony_ci * @card: adapter structure 75462306a36Sopenharmony_ci * @brutal: if set, don't care about whether descriptor seems to be in use 75562306a36Sopenharmony_ci * 75662306a36Sopenharmony_ci * returns 0 if the tx ring is empty, otherwise 1. 75762306a36Sopenharmony_ci * 75862306a36Sopenharmony_ci * spider_net_release_tx_chain releases the tx descriptors that spider has 75962306a36Sopenharmony_ci * finished with (if non-brutal) or simply release tx descriptors (if brutal). 76062306a36Sopenharmony_ci * If some other context is calling this function, we return 1 so that we're 76162306a36Sopenharmony_ci * scheduled again (if we were scheduled) and will not lose initiative. 76262306a36Sopenharmony_ci */ 76362306a36Sopenharmony_cistatic int 76462306a36Sopenharmony_cispider_net_release_tx_chain(struct spider_net_card *card, int brutal) 76562306a36Sopenharmony_ci{ 76662306a36Sopenharmony_ci struct net_device *dev = card->netdev; 76762306a36Sopenharmony_ci struct spider_net_descr_chain *chain = &card->tx_chain; 76862306a36Sopenharmony_ci struct spider_net_descr *descr; 76962306a36Sopenharmony_ci struct spider_net_hw_descr *hwdescr; 77062306a36Sopenharmony_ci struct sk_buff *skb; 77162306a36Sopenharmony_ci u32 buf_addr; 77262306a36Sopenharmony_ci unsigned long flags; 77362306a36Sopenharmony_ci int status; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci while (1) { 77662306a36Sopenharmony_ci spin_lock_irqsave(&chain->lock, flags); 77762306a36Sopenharmony_ci if (chain->tail == chain->head) { 77862306a36Sopenharmony_ci spin_unlock_irqrestore(&chain->lock, flags); 77962306a36Sopenharmony_ci return 0; 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci descr = chain->tail; 78262306a36Sopenharmony_ci hwdescr = descr->hwdescr; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci status = spider_net_get_descr_status(hwdescr); 78562306a36Sopenharmony_ci switch (status) { 78662306a36Sopenharmony_ci case SPIDER_NET_DESCR_COMPLETE: 78762306a36Sopenharmony_ci dev->stats.tx_packets++; 78862306a36Sopenharmony_ci dev->stats.tx_bytes += descr->skb->len; 78962306a36Sopenharmony_ci break; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci case SPIDER_NET_DESCR_CARDOWNED: 79262306a36Sopenharmony_ci if (!brutal) { 79362306a36Sopenharmony_ci spin_unlock_irqrestore(&chain->lock, flags); 79462306a36Sopenharmony_ci return 1; 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci /* fallthrough, if we release the descriptors 79862306a36Sopenharmony_ci * brutally (then we don't care about 79962306a36Sopenharmony_ci * SPIDER_NET_DESCR_CARDOWNED) 80062306a36Sopenharmony_ci */ 80162306a36Sopenharmony_ci fallthrough; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci case SPIDER_NET_DESCR_RESPONSE_ERROR: 80462306a36Sopenharmony_ci case SPIDER_NET_DESCR_PROTECTION_ERROR: 80562306a36Sopenharmony_ci case SPIDER_NET_DESCR_FORCE_END: 80662306a36Sopenharmony_ci if (netif_msg_tx_err(card)) 80762306a36Sopenharmony_ci dev_err(&card->netdev->dev, "forcing end of tx descriptor " 80862306a36Sopenharmony_ci "with status x%02x\n", status); 80962306a36Sopenharmony_ci dev->stats.tx_errors++; 81062306a36Sopenharmony_ci break; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci default: 81362306a36Sopenharmony_ci dev->stats.tx_dropped++; 81462306a36Sopenharmony_ci if (!brutal) { 81562306a36Sopenharmony_ci spin_unlock_irqrestore(&chain->lock, flags); 81662306a36Sopenharmony_ci return 1; 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci } 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_ci chain->tail = descr->next; 82162306a36Sopenharmony_ci hwdescr->dmac_cmd_status |= SPIDER_NET_DESCR_NOT_IN_USE; 82262306a36Sopenharmony_ci skb = descr->skb; 82362306a36Sopenharmony_ci descr->skb = NULL; 82462306a36Sopenharmony_ci buf_addr = hwdescr->buf_addr; 82562306a36Sopenharmony_ci spin_unlock_irqrestore(&chain->lock, flags); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci /* unmap the skb */ 82862306a36Sopenharmony_ci if (skb) { 82962306a36Sopenharmony_ci dma_unmap_single(&card->pdev->dev, buf_addr, skb->len, 83062306a36Sopenharmony_ci DMA_TO_DEVICE); 83162306a36Sopenharmony_ci dev_consume_skb_any(skb); 83262306a36Sopenharmony_ci } 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci return 0; 83562306a36Sopenharmony_ci} 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_ci/** 83862306a36Sopenharmony_ci * spider_net_kick_tx_dma - enables TX DMA processing 83962306a36Sopenharmony_ci * @card: card structure 84062306a36Sopenharmony_ci * 84162306a36Sopenharmony_ci * This routine will start the transmit DMA running if 84262306a36Sopenharmony_ci * it is not already running. This routine ned only be 84362306a36Sopenharmony_ci * called when queueing a new packet to an empty tx queue. 84462306a36Sopenharmony_ci * Writes the current tx chain head as start address 84562306a36Sopenharmony_ci * of the tx descriptor chain and enables the transmission 84662306a36Sopenharmony_ci * DMA engine. 84762306a36Sopenharmony_ci */ 84862306a36Sopenharmony_cistatic inline void 84962306a36Sopenharmony_cispider_net_kick_tx_dma(struct spider_net_card *card) 85062306a36Sopenharmony_ci{ 85162306a36Sopenharmony_ci struct spider_net_descr *descr; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci if (spider_net_read_reg(card, SPIDER_NET_GDTDMACCNTR) & 85462306a36Sopenharmony_ci SPIDER_NET_TX_DMA_EN) 85562306a36Sopenharmony_ci goto out; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci descr = card->tx_chain.tail; 85862306a36Sopenharmony_ci for (;;) { 85962306a36Sopenharmony_ci if (spider_net_get_descr_status(descr->hwdescr) == 86062306a36Sopenharmony_ci SPIDER_NET_DESCR_CARDOWNED) { 86162306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GDTDCHA, 86262306a36Sopenharmony_ci descr->bus_addr); 86362306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GDTDMACCNTR, 86462306a36Sopenharmony_ci SPIDER_NET_DMA_TX_VALUE); 86562306a36Sopenharmony_ci break; 86662306a36Sopenharmony_ci } 86762306a36Sopenharmony_ci if (descr == card->tx_chain.head) 86862306a36Sopenharmony_ci break; 86962306a36Sopenharmony_ci descr = descr->next; 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ciout: 87362306a36Sopenharmony_ci mod_timer(&card->tx_timer, jiffies + SPIDER_NET_TX_TIMER); 87462306a36Sopenharmony_ci} 87562306a36Sopenharmony_ci 87662306a36Sopenharmony_ci/** 87762306a36Sopenharmony_ci * spider_net_xmit - transmits a frame over the device 87862306a36Sopenharmony_ci * @skb: packet to send out 87962306a36Sopenharmony_ci * @netdev: interface device structure 88062306a36Sopenharmony_ci * 88162306a36Sopenharmony_ci * returns NETDEV_TX_OK on success, NETDEV_TX_BUSY on failure 88262306a36Sopenharmony_ci */ 88362306a36Sopenharmony_cistatic netdev_tx_t 88462306a36Sopenharmony_cispider_net_xmit(struct sk_buff *skb, struct net_device *netdev) 88562306a36Sopenharmony_ci{ 88662306a36Sopenharmony_ci int cnt; 88762306a36Sopenharmony_ci struct spider_net_card *card = netdev_priv(netdev); 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci spider_net_release_tx_chain(card, 0); 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_ci if (spider_net_prepare_tx_descr(card, skb) != 0) { 89262306a36Sopenharmony_ci netdev->stats.tx_dropped++; 89362306a36Sopenharmony_ci netif_stop_queue(netdev); 89462306a36Sopenharmony_ci return NETDEV_TX_BUSY; 89562306a36Sopenharmony_ci } 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci cnt = spider_net_set_low_watermark(card); 89862306a36Sopenharmony_ci if (cnt < 5) 89962306a36Sopenharmony_ci spider_net_kick_tx_dma(card); 90062306a36Sopenharmony_ci return NETDEV_TX_OK; 90162306a36Sopenharmony_ci} 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci/** 90462306a36Sopenharmony_ci * spider_net_cleanup_tx_ring - cleans up the TX ring 90562306a36Sopenharmony_ci * @t: timer context used to obtain the pointer to net card data structure 90662306a36Sopenharmony_ci * 90762306a36Sopenharmony_ci * spider_net_cleanup_tx_ring is called by either the tx_timer 90862306a36Sopenharmony_ci * or from the NAPI polling routine. 90962306a36Sopenharmony_ci * This routine releases resources associted with transmitted 91062306a36Sopenharmony_ci * packets, including updating the queue tail pointer. 91162306a36Sopenharmony_ci */ 91262306a36Sopenharmony_cistatic void 91362306a36Sopenharmony_cispider_net_cleanup_tx_ring(struct timer_list *t) 91462306a36Sopenharmony_ci{ 91562306a36Sopenharmony_ci struct spider_net_card *card = from_timer(card, t, tx_timer); 91662306a36Sopenharmony_ci if ((spider_net_release_tx_chain(card, 0) != 0) && 91762306a36Sopenharmony_ci (card->netdev->flags & IFF_UP)) { 91862306a36Sopenharmony_ci spider_net_kick_tx_dma(card); 91962306a36Sopenharmony_ci netif_wake_queue(card->netdev); 92062306a36Sopenharmony_ci } 92162306a36Sopenharmony_ci} 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci/** 92462306a36Sopenharmony_ci * spider_net_do_ioctl - called for device ioctls 92562306a36Sopenharmony_ci * @netdev: interface device structure 92662306a36Sopenharmony_ci * @ifr: request parameter structure for ioctl 92762306a36Sopenharmony_ci * @cmd: command code for ioctl 92862306a36Sopenharmony_ci * 92962306a36Sopenharmony_ci * returns 0 on success, <0 on failure. Currently, we have no special ioctls. 93062306a36Sopenharmony_ci * -EOPNOTSUPP is returned, if an unknown ioctl was requested 93162306a36Sopenharmony_ci */ 93262306a36Sopenharmony_cistatic int 93362306a36Sopenharmony_cispider_net_do_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd) 93462306a36Sopenharmony_ci{ 93562306a36Sopenharmony_ci switch (cmd) { 93662306a36Sopenharmony_ci default: 93762306a36Sopenharmony_ci return -EOPNOTSUPP; 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci} 94062306a36Sopenharmony_ci 94162306a36Sopenharmony_ci/** 94262306a36Sopenharmony_ci * spider_net_pass_skb_up - takes an skb from a descriptor and passes it on 94362306a36Sopenharmony_ci * @descr: descriptor to process 94462306a36Sopenharmony_ci * @card: card structure 94562306a36Sopenharmony_ci * 94662306a36Sopenharmony_ci * Fills out skb structure and passes the data to the stack. 94762306a36Sopenharmony_ci * The descriptor state is not changed. 94862306a36Sopenharmony_ci */ 94962306a36Sopenharmony_cistatic void 95062306a36Sopenharmony_cispider_net_pass_skb_up(struct spider_net_descr *descr, 95162306a36Sopenharmony_ci struct spider_net_card *card) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci struct spider_net_hw_descr *hwdescr = descr->hwdescr; 95462306a36Sopenharmony_ci struct sk_buff *skb = descr->skb; 95562306a36Sopenharmony_ci struct net_device *netdev = card->netdev; 95662306a36Sopenharmony_ci u32 data_status = hwdescr->data_status; 95762306a36Sopenharmony_ci u32 data_error = hwdescr->data_error; 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_ci skb_put(skb, hwdescr->valid_size); 96062306a36Sopenharmony_ci 96162306a36Sopenharmony_ci /* the card seems to add 2 bytes of junk in front 96262306a36Sopenharmony_ci * of the ethernet frame 96362306a36Sopenharmony_ci */ 96462306a36Sopenharmony_ci#define SPIDER_MISALIGN 2 96562306a36Sopenharmony_ci skb_pull(skb, SPIDER_MISALIGN); 96662306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, netdev); 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci /* checksum offload */ 96962306a36Sopenharmony_ci skb_checksum_none_assert(skb); 97062306a36Sopenharmony_ci if (netdev->features & NETIF_F_RXCSUM) { 97162306a36Sopenharmony_ci if ( ( (data_status & SPIDER_NET_DATA_STATUS_CKSUM_MASK) == 97262306a36Sopenharmony_ci SPIDER_NET_DATA_STATUS_CKSUM_MASK) && 97362306a36Sopenharmony_ci !(data_error & SPIDER_NET_DATA_ERR_CKSUM_MASK)) 97462306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 97562306a36Sopenharmony_ci } 97662306a36Sopenharmony_ci 97762306a36Sopenharmony_ci if (data_status & SPIDER_NET_VLAN_PACKET) { 97862306a36Sopenharmony_ci /* further enhancements: HW-accel VLAN */ 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci 98162306a36Sopenharmony_ci /* update netdevice statistics */ 98262306a36Sopenharmony_ci netdev->stats.rx_packets++; 98362306a36Sopenharmony_ci netdev->stats.rx_bytes += skb->len; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci /* pass skb up to stack */ 98662306a36Sopenharmony_ci netif_receive_skb(skb); 98762306a36Sopenharmony_ci} 98862306a36Sopenharmony_ci 98962306a36Sopenharmony_cistatic void show_rx_chain(struct spider_net_card *card) 99062306a36Sopenharmony_ci{ 99162306a36Sopenharmony_ci struct spider_net_descr_chain *chain = &card->rx_chain; 99262306a36Sopenharmony_ci struct spider_net_descr *start= chain->tail; 99362306a36Sopenharmony_ci struct spider_net_descr *descr= start; 99462306a36Sopenharmony_ci struct spider_net_hw_descr *hwd = start->hwdescr; 99562306a36Sopenharmony_ci struct device *dev = &card->netdev->dev; 99662306a36Sopenharmony_ci u32 curr_desc, next_desc; 99762306a36Sopenharmony_ci int status; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci int tot = 0; 100062306a36Sopenharmony_ci int cnt = 0; 100162306a36Sopenharmony_ci int off = start - chain->ring; 100262306a36Sopenharmony_ci int cstat = hwd->dmac_cmd_status; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci dev_info(dev, "Total number of descrs=%d\n", 100562306a36Sopenharmony_ci chain->num_desc); 100662306a36Sopenharmony_ci dev_info(dev, "Chain tail located at descr=%d, status=0x%x\n", 100762306a36Sopenharmony_ci off, cstat); 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci curr_desc = spider_net_read_reg(card, SPIDER_NET_GDACTDPA); 101062306a36Sopenharmony_ci next_desc = spider_net_read_reg(card, SPIDER_NET_GDACNEXTDA); 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci status = cstat; 101362306a36Sopenharmony_ci do 101462306a36Sopenharmony_ci { 101562306a36Sopenharmony_ci hwd = descr->hwdescr; 101662306a36Sopenharmony_ci off = descr - chain->ring; 101762306a36Sopenharmony_ci status = hwd->dmac_cmd_status; 101862306a36Sopenharmony_ci 101962306a36Sopenharmony_ci if (descr == chain->head) 102062306a36Sopenharmony_ci dev_info(dev, "Chain head is at %d, head status=0x%x\n", 102162306a36Sopenharmony_ci off, status); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci if (curr_desc == descr->bus_addr) 102462306a36Sopenharmony_ci dev_info(dev, "HW curr desc (GDACTDPA) is at %d, status=0x%x\n", 102562306a36Sopenharmony_ci off, status); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci if (next_desc == descr->bus_addr) 102862306a36Sopenharmony_ci dev_info(dev, "HW next desc (GDACNEXTDA) is at %d, status=0x%x\n", 102962306a36Sopenharmony_ci off, status); 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci if (hwd->next_descr_addr == 0) 103262306a36Sopenharmony_ci dev_info(dev, "chain is cut at %d\n", off); 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci if (cstat != status) { 103562306a36Sopenharmony_ci int from = (chain->num_desc + off - cnt) % chain->num_desc; 103662306a36Sopenharmony_ci int to = (chain->num_desc + off - 1) % chain->num_desc; 103762306a36Sopenharmony_ci dev_info(dev, "Have %d (from %d to %d) descrs " 103862306a36Sopenharmony_ci "with stat=0x%08x\n", cnt, from, to, cstat); 103962306a36Sopenharmony_ci cstat = status; 104062306a36Sopenharmony_ci cnt = 0; 104162306a36Sopenharmony_ci } 104262306a36Sopenharmony_ci 104362306a36Sopenharmony_ci cnt ++; 104462306a36Sopenharmony_ci tot ++; 104562306a36Sopenharmony_ci descr = descr->next; 104662306a36Sopenharmony_ci } while (descr != start); 104762306a36Sopenharmony_ci 104862306a36Sopenharmony_ci dev_info(dev, "Last %d descrs with stat=0x%08x " 104962306a36Sopenharmony_ci "for a total of %d descrs\n", cnt, cstat, tot); 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci#ifdef DEBUG 105262306a36Sopenharmony_ci /* Now dump the whole ring */ 105362306a36Sopenharmony_ci descr = start; 105462306a36Sopenharmony_ci do 105562306a36Sopenharmony_ci { 105662306a36Sopenharmony_ci struct spider_net_hw_descr *hwd = descr->hwdescr; 105762306a36Sopenharmony_ci status = spider_net_get_descr_status(hwd); 105862306a36Sopenharmony_ci cnt = descr - chain->ring; 105962306a36Sopenharmony_ci dev_info(dev, "Descr %d stat=0x%08x skb=%p\n", 106062306a36Sopenharmony_ci cnt, status, descr->skb); 106162306a36Sopenharmony_ci dev_info(dev, "bus addr=%08x buf addr=%08x sz=%d\n", 106262306a36Sopenharmony_ci descr->bus_addr, hwd->buf_addr, hwd->buf_size); 106362306a36Sopenharmony_ci dev_info(dev, "next=%08x result sz=%d valid sz=%d\n", 106462306a36Sopenharmony_ci hwd->next_descr_addr, hwd->result_size, 106562306a36Sopenharmony_ci hwd->valid_size); 106662306a36Sopenharmony_ci dev_info(dev, "dmac=%08x data stat=%08x data err=%08x\n", 106762306a36Sopenharmony_ci hwd->dmac_cmd_status, hwd->data_status, 106862306a36Sopenharmony_ci hwd->data_error); 106962306a36Sopenharmony_ci dev_info(dev, "\n"); 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_ci descr = descr->next; 107262306a36Sopenharmony_ci } while (descr != start); 107362306a36Sopenharmony_ci#endif 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci} 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci/** 107862306a36Sopenharmony_ci * spider_net_resync_head_ptr - Advance head ptr past empty descrs 107962306a36Sopenharmony_ci * @card: card structure 108062306a36Sopenharmony_ci * 108162306a36Sopenharmony_ci * If the driver fails to keep up and empty the queue, then the 108262306a36Sopenharmony_ci * hardware wil run out of room to put incoming packets. This 108362306a36Sopenharmony_ci * will cause the hardware to skip descrs that are full (instead 108462306a36Sopenharmony_ci * of halting/retrying). Thus, once the driver runs, it wil need 108562306a36Sopenharmony_ci * to "catch up" to where the hardware chain pointer is at. 108662306a36Sopenharmony_ci */ 108762306a36Sopenharmony_cistatic void spider_net_resync_head_ptr(struct spider_net_card *card) 108862306a36Sopenharmony_ci{ 108962306a36Sopenharmony_ci unsigned long flags; 109062306a36Sopenharmony_ci struct spider_net_descr_chain *chain = &card->rx_chain; 109162306a36Sopenharmony_ci struct spider_net_descr *descr; 109262306a36Sopenharmony_ci int i, status; 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_ci /* Advance head pointer past any empty descrs */ 109562306a36Sopenharmony_ci descr = chain->head; 109662306a36Sopenharmony_ci status = spider_net_get_descr_status(descr->hwdescr); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci if (status == SPIDER_NET_DESCR_NOT_IN_USE) 109962306a36Sopenharmony_ci return; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci spin_lock_irqsave(&chain->lock, flags); 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci descr = chain->head; 110462306a36Sopenharmony_ci status = spider_net_get_descr_status(descr->hwdescr); 110562306a36Sopenharmony_ci for (i=0; i<chain->num_desc; i++) { 110662306a36Sopenharmony_ci if (status != SPIDER_NET_DESCR_CARDOWNED) break; 110762306a36Sopenharmony_ci descr = descr->next; 110862306a36Sopenharmony_ci status = spider_net_get_descr_status(descr->hwdescr); 110962306a36Sopenharmony_ci } 111062306a36Sopenharmony_ci chain->head = descr; 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci spin_unlock_irqrestore(&chain->lock, flags); 111362306a36Sopenharmony_ci} 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_cistatic int spider_net_resync_tail_ptr(struct spider_net_card *card) 111662306a36Sopenharmony_ci{ 111762306a36Sopenharmony_ci struct spider_net_descr_chain *chain = &card->rx_chain; 111862306a36Sopenharmony_ci struct spider_net_descr *descr; 111962306a36Sopenharmony_ci int i, status; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci /* Advance tail pointer past any empty and reaped descrs */ 112262306a36Sopenharmony_ci descr = chain->tail; 112362306a36Sopenharmony_ci status = spider_net_get_descr_status(descr->hwdescr); 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci for (i=0; i<chain->num_desc; i++) { 112662306a36Sopenharmony_ci if ((status != SPIDER_NET_DESCR_CARDOWNED) && 112762306a36Sopenharmony_ci (status != SPIDER_NET_DESCR_NOT_IN_USE)) break; 112862306a36Sopenharmony_ci descr = descr->next; 112962306a36Sopenharmony_ci status = spider_net_get_descr_status(descr->hwdescr); 113062306a36Sopenharmony_ci } 113162306a36Sopenharmony_ci chain->tail = descr; 113262306a36Sopenharmony_ci 113362306a36Sopenharmony_ci if ((i == chain->num_desc) || (i == 0)) 113462306a36Sopenharmony_ci return 1; 113562306a36Sopenharmony_ci return 0; 113662306a36Sopenharmony_ci} 113762306a36Sopenharmony_ci 113862306a36Sopenharmony_ci/** 113962306a36Sopenharmony_ci * spider_net_decode_one_descr - processes an RX descriptor 114062306a36Sopenharmony_ci * @card: card structure 114162306a36Sopenharmony_ci * 114262306a36Sopenharmony_ci * Returns 1 if a packet has been sent to the stack, otherwise 0. 114362306a36Sopenharmony_ci * 114462306a36Sopenharmony_ci * Processes an RX descriptor by iommu-unmapping the data buffer 114562306a36Sopenharmony_ci * and passing the packet up to the stack. This function is called 114662306a36Sopenharmony_ci * in softirq context, e.g. either bottom half from interrupt or 114762306a36Sopenharmony_ci * NAPI polling context. 114862306a36Sopenharmony_ci */ 114962306a36Sopenharmony_cistatic int 115062306a36Sopenharmony_cispider_net_decode_one_descr(struct spider_net_card *card) 115162306a36Sopenharmony_ci{ 115262306a36Sopenharmony_ci struct net_device *dev = card->netdev; 115362306a36Sopenharmony_ci struct spider_net_descr_chain *chain = &card->rx_chain; 115462306a36Sopenharmony_ci struct spider_net_descr *descr = chain->tail; 115562306a36Sopenharmony_ci struct spider_net_hw_descr *hwdescr = descr->hwdescr; 115662306a36Sopenharmony_ci u32 hw_buf_addr; 115762306a36Sopenharmony_ci int status; 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci status = spider_net_get_descr_status(hwdescr); 116062306a36Sopenharmony_ci 116162306a36Sopenharmony_ci /* Nothing in the descriptor, or ring must be empty */ 116262306a36Sopenharmony_ci if ((status == SPIDER_NET_DESCR_CARDOWNED) || 116362306a36Sopenharmony_ci (status == SPIDER_NET_DESCR_NOT_IN_USE)) 116462306a36Sopenharmony_ci return 0; 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci /* descriptor definitively used -- move on tail */ 116762306a36Sopenharmony_ci chain->tail = descr->next; 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci /* unmap descriptor */ 117062306a36Sopenharmony_ci hw_buf_addr = hwdescr->buf_addr; 117162306a36Sopenharmony_ci hwdescr->buf_addr = 0xffffffff; 117262306a36Sopenharmony_ci dma_unmap_single(&card->pdev->dev, hw_buf_addr, SPIDER_NET_MAX_FRAME, 117362306a36Sopenharmony_ci DMA_FROM_DEVICE); 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci if ( (status == SPIDER_NET_DESCR_RESPONSE_ERROR) || 117662306a36Sopenharmony_ci (status == SPIDER_NET_DESCR_PROTECTION_ERROR) || 117762306a36Sopenharmony_ci (status == SPIDER_NET_DESCR_FORCE_END) ) { 117862306a36Sopenharmony_ci if (netif_msg_rx_err(card)) 117962306a36Sopenharmony_ci dev_err(&dev->dev, 118062306a36Sopenharmony_ci "dropping RX descriptor with state %d\n", status); 118162306a36Sopenharmony_ci dev->stats.rx_dropped++; 118262306a36Sopenharmony_ci goto bad_desc; 118362306a36Sopenharmony_ci } 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci if ( (status != SPIDER_NET_DESCR_COMPLETE) && 118662306a36Sopenharmony_ci (status != SPIDER_NET_DESCR_FRAME_END) ) { 118762306a36Sopenharmony_ci if (netif_msg_rx_err(card)) 118862306a36Sopenharmony_ci dev_err(&card->netdev->dev, 118962306a36Sopenharmony_ci "RX descriptor with unknown state %d\n", status); 119062306a36Sopenharmony_ci card->spider_stats.rx_desc_unk_state++; 119162306a36Sopenharmony_ci goto bad_desc; 119262306a36Sopenharmony_ci } 119362306a36Sopenharmony_ci 119462306a36Sopenharmony_ci /* The cases we'll throw away the packet immediately */ 119562306a36Sopenharmony_ci if (hwdescr->data_error & SPIDER_NET_DESTROY_RX_FLAGS) { 119662306a36Sopenharmony_ci if (netif_msg_rx_err(card)) 119762306a36Sopenharmony_ci dev_err(&card->netdev->dev, 119862306a36Sopenharmony_ci "error in received descriptor found, " 119962306a36Sopenharmony_ci "data_status=x%08x, data_error=x%08x\n", 120062306a36Sopenharmony_ci hwdescr->data_status, hwdescr->data_error); 120162306a36Sopenharmony_ci goto bad_desc; 120262306a36Sopenharmony_ci } 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci if (hwdescr->dmac_cmd_status & SPIDER_NET_DESCR_BAD_STATUS) { 120562306a36Sopenharmony_ci dev_err(&card->netdev->dev, "bad status, cmd_status=x%08x\n", 120662306a36Sopenharmony_ci hwdescr->dmac_cmd_status); 120762306a36Sopenharmony_ci pr_err("buf_addr=x%08x\n", hw_buf_addr); 120862306a36Sopenharmony_ci pr_err("buf_size=x%08x\n", hwdescr->buf_size); 120962306a36Sopenharmony_ci pr_err("next_descr_addr=x%08x\n", hwdescr->next_descr_addr); 121062306a36Sopenharmony_ci pr_err("result_size=x%08x\n", hwdescr->result_size); 121162306a36Sopenharmony_ci pr_err("valid_size=x%08x\n", hwdescr->valid_size); 121262306a36Sopenharmony_ci pr_err("data_status=x%08x\n", hwdescr->data_status); 121362306a36Sopenharmony_ci pr_err("data_error=x%08x\n", hwdescr->data_error); 121462306a36Sopenharmony_ci pr_err("which=%ld\n", descr - card->rx_chain.ring); 121562306a36Sopenharmony_ci 121662306a36Sopenharmony_ci card->spider_stats.rx_desc_error++; 121762306a36Sopenharmony_ci goto bad_desc; 121862306a36Sopenharmony_ci } 121962306a36Sopenharmony_ci 122062306a36Sopenharmony_ci /* Ok, we've got a packet in descr */ 122162306a36Sopenharmony_ci spider_net_pass_skb_up(descr, card); 122262306a36Sopenharmony_ci descr->skb = NULL; 122362306a36Sopenharmony_ci hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; 122462306a36Sopenharmony_ci return 1; 122562306a36Sopenharmony_ci 122662306a36Sopenharmony_cibad_desc: 122762306a36Sopenharmony_ci if (netif_msg_rx_err(card)) 122862306a36Sopenharmony_ci show_rx_chain(card); 122962306a36Sopenharmony_ci dev_kfree_skb_irq(descr->skb); 123062306a36Sopenharmony_ci descr->skb = NULL; 123162306a36Sopenharmony_ci hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; 123262306a36Sopenharmony_ci return 0; 123362306a36Sopenharmony_ci} 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci/** 123662306a36Sopenharmony_ci * spider_net_poll - NAPI poll function called by the stack to return packets 123762306a36Sopenharmony_ci * @napi: napi device structure 123862306a36Sopenharmony_ci * @budget: number of packets we can pass to the stack at most 123962306a36Sopenharmony_ci * 124062306a36Sopenharmony_ci * returns 0 if no more packets available to the driver/stack. Returns 1, 124162306a36Sopenharmony_ci * if the quota is exceeded, but the driver has still packets. 124262306a36Sopenharmony_ci * 124362306a36Sopenharmony_ci * spider_net_poll returns all packets from the rx descriptors to the stack 124462306a36Sopenharmony_ci * (using netif_receive_skb). If all/enough packets are up, the driver 124562306a36Sopenharmony_ci * reenables interrupts and returns 0. If not, 1 is returned. 124662306a36Sopenharmony_ci */ 124762306a36Sopenharmony_cistatic int spider_net_poll(struct napi_struct *napi, int budget) 124862306a36Sopenharmony_ci{ 124962306a36Sopenharmony_ci struct spider_net_card *card = container_of(napi, struct spider_net_card, napi); 125062306a36Sopenharmony_ci int packets_done = 0; 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci while (packets_done < budget) { 125362306a36Sopenharmony_ci if (!spider_net_decode_one_descr(card)) 125462306a36Sopenharmony_ci break; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci packets_done++; 125762306a36Sopenharmony_ci } 125862306a36Sopenharmony_ci 125962306a36Sopenharmony_ci if ((packets_done == 0) && (card->num_rx_ints != 0)) { 126062306a36Sopenharmony_ci if (!spider_net_resync_tail_ptr(card)) 126162306a36Sopenharmony_ci packets_done = budget; 126262306a36Sopenharmony_ci spider_net_resync_head_ptr(card); 126362306a36Sopenharmony_ci } 126462306a36Sopenharmony_ci card->num_rx_ints = 0; 126562306a36Sopenharmony_ci 126662306a36Sopenharmony_ci spider_net_refill_rx_chain(card); 126762306a36Sopenharmony_ci spider_net_enable_rxdmac(card); 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci spider_net_cleanup_tx_ring(&card->tx_timer); 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci /* if all packets are in the stack, enable interrupts and return 0 */ 127262306a36Sopenharmony_ci /* if not, return 1 */ 127362306a36Sopenharmony_ci if (packets_done < budget) { 127462306a36Sopenharmony_ci napi_complete_done(napi, packets_done); 127562306a36Sopenharmony_ci spider_net_rx_irq_on(card); 127662306a36Sopenharmony_ci card->ignore_rx_ramfull = 0; 127762306a36Sopenharmony_ci } 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci return packets_done; 128062306a36Sopenharmony_ci} 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci/** 128362306a36Sopenharmony_ci * spider_net_set_mac - sets the MAC of an interface 128462306a36Sopenharmony_ci * @netdev: interface device structure 128562306a36Sopenharmony_ci * @p: pointer to new MAC address 128662306a36Sopenharmony_ci * 128762306a36Sopenharmony_ci * Returns 0 on success, <0 on failure. Currently, we don't support this 128862306a36Sopenharmony_ci * and will always return EOPNOTSUPP. 128962306a36Sopenharmony_ci */ 129062306a36Sopenharmony_cistatic int 129162306a36Sopenharmony_cispider_net_set_mac(struct net_device *netdev, void *p) 129262306a36Sopenharmony_ci{ 129362306a36Sopenharmony_ci struct spider_net_card *card = netdev_priv(netdev); 129462306a36Sopenharmony_ci u32 macl, macu, regvalue; 129562306a36Sopenharmony_ci struct sockaddr *addr = p; 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 129862306a36Sopenharmony_ci return -EADDRNOTAVAIL; 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci eth_hw_addr_set(netdev, addr->sa_data); 130162306a36Sopenharmony_ci 130262306a36Sopenharmony_ci /* switch off GMACTPE and GMACRPE */ 130362306a36Sopenharmony_ci regvalue = spider_net_read_reg(card, SPIDER_NET_GMACOPEMD); 130462306a36Sopenharmony_ci regvalue &= ~((1 << 5) | (1 << 6)); 130562306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GMACOPEMD, regvalue); 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci /* write mac */ 130862306a36Sopenharmony_ci macu = (netdev->dev_addr[0]<<24) + (netdev->dev_addr[1]<<16) + 130962306a36Sopenharmony_ci (netdev->dev_addr[2]<<8) + (netdev->dev_addr[3]); 131062306a36Sopenharmony_ci macl = (netdev->dev_addr[4]<<8) + (netdev->dev_addr[5]); 131162306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GMACUNIMACU, macu); 131262306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GMACUNIMACL, macl); 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci /* switch GMACTPE and GMACRPE back on */ 131562306a36Sopenharmony_ci regvalue = spider_net_read_reg(card, SPIDER_NET_GMACOPEMD); 131662306a36Sopenharmony_ci regvalue |= ((1 << 5) | (1 << 6)); 131762306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GMACOPEMD, regvalue); 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci spider_net_set_promisc(card); 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci return 0; 132262306a36Sopenharmony_ci} 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci/** 132562306a36Sopenharmony_ci * spider_net_link_reset 132662306a36Sopenharmony_ci * @netdev: net device structure 132762306a36Sopenharmony_ci * 132862306a36Sopenharmony_ci * This is called when the PHY_LINK signal is asserted. For the blade this is 132962306a36Sopenharmony_ci * not connected so we should never get here. 133062306a36Sopenharmony_ci * 133162306a36Sopenharmony_ci */ 133262306a36Sopenharmony_cistatic void 133362306a36Sopenharmony_cispider_net_link_reset(struct net_device *netdev) 133462306a36Sopenharmony_ci{ 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_ci struct spider_net_card *card = netdev_priv(netdev); 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci del_timer_sync(&card->aneg_timer); 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci /* clear interrupt, block further interrupts */ 134162306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GMACST, 134262306a36Sopenharmony_ci spider_net_read_reg(card, SPIDER_NET_GMACST)); 134362306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GMACINTEN, 0); 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci /* reset phy and setup aneg */ 134662306a36Sopenharmony_ci card->aneg_count = 0; 134762306a36Sopenharmony_ci card->medium = BCM54XX_COPPER; 134862306a36Sopenharmony_ci spider_net_setup_aneg(card); 134962306a36Sopenharmony_ci mod_timer(&card->aneg_timer, jiffies + SPIDER_NET_ANEG_TIMER); 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci} 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci/** 135462306a36Sopenharmony_ci * spider_net_handle_error_irq - handles errors raised by an interrupt 135562306a36Sopenharmony_ci * @card: card structure 135662306a36Sopenharmony_ci * @status_reg: interrupt status register 0 (GHIINT0STS) 135762306a36Sopenharmony_ci * @error_reg1: interrupt status register 1 (GHIINT1STS) 135862306a36Sopenharmony_ci * @error_reg2: interrupt status register 2 (GHIINT2STS) 135962306a36Sopenharmony_ci * 136062306a36Sopenharmony_ci * spider_net_handle_error_irq treats or ignores all error conditions 136162306a36Sopenharmony_ci * found when an interrupt is presented 136262306a36Sopenharmony_ci */ 136362306a36Sopenharmony_cistatic void 136462306a36Sopenharmony_cispider_net_handle_error_irq(struct spider_net_card *card, u32 status_reg, 136562306a36Sopenharmony_ci u32 error_reg1, u32 error_reg2) 136662306a36Sopenharmony_ci{ 136762306a36Sopenharmony_ci u32 i; 136862306a36Sopenharmony_ci int show_error = 1; 136962306a36Sopenharmony_ci 137062306a36Sopenharmony_ci /* check GHIINT0STS ************************************/ 137162306a36Sopenharmony_ci if (status_reg) 137262306a36Sopenharmony_ci for (i = 0; i < 32; i++) 137362306a36Sopenharmony_ci if (status_reg & (1<<i)) 137462306a36Sopenharmony_ci switch (i) 137562306a36Sopenharmony_ci { 137662306a36Sopenharmony_ci /* let error_reg1 and error_reg2 evaluation decide, what to do 137762306a36Sopenharmony_ci case SPIDER_NET_PHYINT: 137862306a36Sopenharmony_ci case SPIDER_NET_GMAC2INT: 137962306a36Sopenharmony_ci case SPIDER_NET_GMAC1INT: 138062306a36Sopenharmony_ci case SPIDER_NET_GFIFOINT: 138162306a36Sopenharmony_ci case SPIDER_NET_DMACINT: 138262306a36Sopenharmony_ci case SPIDER_NET_GSYSINT: 138362306a36Sopenharmony_ci break; */ 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci case SPIDER_NET_GIPSINT: 138662306a36Sopenharmony_ci show_error = 0; 138762306a36Sopenharmony_ci break; 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci case SPIDER_NET_GPWOPCMPINT: 139062306a36Sopenharmony_ci /* PHY write operation completed */ 139162306a36Sopenharmony_ci show_error = 0; 139262306a36Sopenharmony_ci break; 139362306a36Sopenharmony_ci case SPIDER_NET_GPROPCMPINT: 139462306a36Sopenharmony_ci /* PHY read operation completed */ 139562306a36Sopenharmony_ci /* we don't use semaphores, as we poll for the completion 139662306a36Sopenharmony_ci * of the read operation in spider_net_read_phy. Should take 139762306a36Sopenharmony_ci * about 50 us 139862306a36Sopenharmony_ci */ 139962306a36Sopenharmony_ci show_error = 0; 140062306a36Sopenharmony_ci break; 140162306a36Sopenharmony_ci case SPIDER_NET_GPWFFINT: 140262306a36Sopenharmony_ci /* PHY command queue full */ 140362306a36Sopenharmony_ci if (netif_msg_intr(card)) 140462306a36Sopenharmony_ci dev_err(&card->netdev->dev, "PHY write queue full\n"); 140562306a36Sopenharmony_ci show_error = 0; 140662306a36Sopenharmony_ci break; 140762306a36Sopenharmony_ci 140862306a36Sopenharmony_ci /* case SPIDER_NET_GRMDADRINT: not used. print a message */ 140962306a36Sopenharmony_ci /* case SPIDER_NET_GRMARPINT: not used. print a message */ 141062306a36Sopenharmony_ci /* case SPIDER_NET_GRMMPINT: not used. print a message */ 141162306a36Sopenharmony_ci 141262306a36Sopenharmony_ci case SPIDER_NET_GDTDEN0INT: 141362306a36Sopenharmony_ci /* someone has set TX_DMA_EN to 0 */ 141462306a36Sopenharmony_ci show_error = 0; 141562306a36Sopenharmony_ci break; 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci case SPIDER_NET_GDDDEN0INT: 141862306a36Sopenharmony_ci case SPIDER_NET_GDCDEN0INT: 141962306a36Sopenharmony_ci case SPIDER_NET_GDBDEN0INT: 142062306a36Sopenharmony_ci case SPIDER_NET_GDADEN0INT: 142162306a36Sopenharmony_ci /* someone has set RX_DMA_EN to 0 */ 142262306a36Sopenharmony_ci show_error = 0; 142362306a36Sopenharmony_ci break; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci /* RX interrupts */ 142662306a36Sopenharmony_ci case SPIDER_NET_GDDFDCINT: 142762306a36Sopenharmony_ci case SPIDER_NET_GDCFDCINT: 142862306a36Sopenharmony_ci case SPIDER_NET_GDBFDCINT: 142962306a36Sopenharmony_ci case SPIDER_NET_GDAFDCINT: 143062306a36Sopenharmony_ci /* case SPIDER_NET_GDNMINT: not used. print a message */ 143162306a36Sopenharmony_ci /* case SPIDER_NET_GCNMINT: not used. print a message */ 143262306a36Sopenharmony_ci /* case SPIDER_NET_GBNMINT: not used. print a message */ 143362306a36Sopenharmony_ci /* case SPIDER_NET_GANMINT: not used. print a message */ 143462306a36Sopenharmony_ci /* case SPIDER_NET_GRFNMINT: not used. print a message */ 143562306a36Sopenharmony_ci show_error = 0; 143662306a36Sopenharmony_ci break; 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci /* TX interrupts */ 143962306a36Sopenharmony_ci case SPIDER_NET_GDTFDCINT: 144062306a36Sopenharmony_ci show_error = 0; 144162306a36Sopenharmony_ci break; 144262306a36Sopenharmony_ci case SPIDER_NET_GTTEDINT: 144362306a36Sopenharmony_ci show_error = 0; 144462306a36Sopenharmony_ci break; 144562306a36Sopenharmony_ci case SPIDER_NET_GDTDCEINT: 144662306a36Sopenharmony_ci /* chain end. If a descriptor should be sent, kick off 144762306a36Sopenharmony_ci * tx dma 144862306a36Sopenharmony_ci if (card->tx_chain.tail != card->tx_chain.head) 144962306a36Sopenharmony_ci spider_net_kick_tx_dma(card); 145062306a36Sopenharmony_ci */ 145162306a36Sopenharmony_ci show_error = 0; 145262306a36Sopenharmony_ci break; 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci /* case SPIDER_NET_G1TMCNTINT: not used. print a message */ 145562306a36Sopenharmony_ci /* case SPIDER_NET_GFREECNTINT: not used. print a message */ 145662306a36Sopenharmony_ci } 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci /* check GHIINT1STS ************************************/ 145962306a36Sopenharmony_ci if (error_reg1) 146062306a36Sopenharmony_ci for (i = 0; i < 32; i++) 146162306a36Sopenharmony_ci if (error_reg1 & (1<<i)) 146262306a36Sopenharmony_ci switch (i) 146362306a36Sopenharmony_ci { 146462306a36Sopenharmony_ci case SPIDER_NET_GTMFLLINT: 146562306a36Sopenharmony_ci /* TX RAM full may happen on a usual case. 146662306a36Sopenharmony_ci * Logging is not needed. 146762306a36Sopenharmony_ci */ 146862306a36Sopenharmony_ci show_error = 0; 146962306a36Sopenharmony_ci break; 147062306a36Sopenharmony_ci case SPIDER_NET_GRFDFLLINT: 147162306a36Sopenharmony_ci case SPIDER_NET_GRFCFLLINT: 147262306a36Sopenharmony_ci case SPIDER_NET_GRFBFLLINT: 147362306a36Sopenharmony_ci case SPIDER_NET_GRFAFLLINT: 147462306a36Sopenharmony_ci case SPIDER_NET_GRMFLLINT: 147562306a36Sopenharmony_ci /* Could happen when rx chain is full */ 147662306a36Sopenharmony_ci if (card->ignore_rx_ramfull == 0) { 147762306a36Sopenharmony_ci card->ignore_rx_ramfull = 1; 147862306a36Sopenharmony_ci spider_net_resync_head_ptr(card); 147962306a36Sopenharmony_ci spider_net_refill_rx_chain(card); 148062306a36Sopenharmony_ci spider_net_enable_rxdmac(card); 148162306a36Sopenharmony_ci card->num_rx_ints ++; 148262306a36Sopenharmony_ci napi_schedule(&card->napi); 148362306a36Sopenharmony_ci } 148462306a36Sopenharmony_ci show_error = 0; 148562306a36Sopenharmony_ci break; 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_ci /* case SPIDER_NET_GTMSHTINT: problem, print a message */ 148862306a36Sopenharmony_ci case SPIDER_NET_GDTINVDINT: 148962306a36Sopenharmony_ci /* allrighty. tx from previous descr ok */ 149062306a36Sopenharmony_ci show_error = 0; 149162306a36Sopenharmony_ci break; 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci /* chain end */ 149462306a36Sopenharmony_ci case SPIDER_NET_GDDDCEINT: 149562306a36Sopenharmony_ci case SPIDER_NET_GDCDCEINT: 149662306a36Sopenharmony_ci case SPIDER_NET_GDBDCEINT: 149762306a36Sopenharmony_ci case SPIDER_NET_GDADCEINT: 149862306a36Sopenharmony_ci spider_net_resync_head_ptr(card); 149962306a36Sopenharmony_ci spider_net_refill_rx_chain(card); 150062306a36Sopenharmony_ci spider_net_enable_rxdmac(card); 150162306a36Sopenharmony_ci card->num_rx_ints ++; 150262306a36Sopenharmony_ci napi_schedule(&card->napi); 150362306a36Sopenharmony_ci show_error = 0; 150462306a36Sopenharmony_ci break; 150562306a36Sopenharmony_ci 150662306a36Sopenharmony_ci /* invalid descriptor */ 150762306a36Sopenharmony_ci case SPIDER_NET_GDDINVDINT: 150862306a36Sopenharmony_ci case SPIDER_NET_GDCINVDINT: 150962306a36Sopenharmony_ci case SPIDER_NET_GDBINVDINT: 151062306a36Sopenharmony_ci case SPIDER_NET_GDAINVDINT: 151162306a36Sopenharmony_ci /* Could happen when rx chain is full */ 151262306a36Sopenharmony_ci spider_net_resync_head_ptr(card); 151362306a36Sopenharmony_ci spider_net_refill_rx_chain(card); 151462306a36Sopenharmony_ci spider_net_enable_rxdmac(card); 151562306a36Sopenharmony_ci card->num_rx_ints ++; 151662306a36Sopenharmony_ci napi_schedule(&card->napi); 151762306a36Sopenharmony_ci show_error = 0; 151862306a36Sopenharmony_ci break; 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_ci /* case SPIDER_NET_GDTRSERINT: problem, print a message */ 152162306a36Sopenharmony_ci /* case SPIDER_NET_GDDRSERINT: problem, print a message */ 152262306a36Sopenharmony_ci /* case SPIDER_NET_GDCRSERINT: problem, print a message */ 152362306a36Sopenharmony_ci /* case SPIDER_NET_GDBRSERINT: problem, print a message */ 152462306a36Sopenharmony_ci /* case SPIDER_NET_GDARSERINT: problem, print a message */ 152562306a36Sopenharmony_ci /* case SPIDER_NET_GDSERINT: problem, print a message */ 152662306a36Sopenharmony_ci /* case SPIDER_NET_GDTPTERINT: problem, print a message */ 152762306a36Sopenharmony_ci /* case SPIDER_NET_GDDPTERINT: problem, print a message */ 152862306a36Sopenharmony_ci /* case SPIDER_NET_GDCPTERINT: problem, print a message */ 152962306a36Sopenharmony_ci /* case SPIDER_NET_GDBPTERINT: problem, print a message */ 153062306a36Sopenharmony_ci /* case SPIDER_NET_GDAPTERINT: problem, print a message */ 153162306a36Sopenharmony_ci default: 153262306a36Sopenharmony_ci show_error = 1; 153362306a36Sopenharmony_ci break; 153462306a36Sopenharmony_ci } 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci /* check GHIINT2STS ************************************/ 153762306a36Sopenharmony_ci if (error_reg2) 153862306a36Sopenharmony_ci for (i = 0; i < 32; i++) 153962306a36Sopenharmony_ci if (error_reg2 & (1<<i)) 154062306a36Sopenharmony_ci switch (i) 154162306a36Sopenharmony_ci { 154262306a36Sopenharmony_ci /* there is nothing we can (want to) do at this time. Log a 154362306a36Sopenharmony_ci * message, we can switch on and off the specific values later on 154462306a36Sopenharmony_ci case SPIDER_NET_GPROPERINT: 154562306a36Sopenharmony_ci case SPIDER_NET_GMCTCRSNGINT: 154662306a36Sopenharmony_ci case SPIDER_NET_GMCTLCOLINT: 154762306a36Sopenharmony_ci case SPIDER_NET_GMCTTMOTINT: 154862306a36Sopenharmony_ci case SPIDER_NET_GMCRCAERINT: 154962306a36Sopenharmony_ci case SPIDER_NET_GMCRCALERINT: 155062306a36Sopenharmony_ci case SPIDER_NET_GMCRALNERINT: 155162306a36Sopenharmony_ci case SPIDER_NET_GMCROVRINT: 155262306a36Sopenharmony_ci case SPIDER_NET_GMCRRNTINT: 155362306a36Sopenharmony_ci case SPIDER_NET_GMCRRXERINT: 155462306a36Sopenharmony_ci case SPIDER_NET_GTITCSERINT: 155562306a36Sopenharmony_ci case SPIDER_NET_GTIFMTERINT: 155662306a36Sopenharmony_ci case SPIDER_NET_GTIPKTRVKINT: 155762306a36Sopenharmony_ci case SPIDER_NET_GTISPINGINT: 155862306a36Sopenharmony_ci case SPIDER_NET_GTISADNGINT: 155962306a36Sopenharmony_ci case SPIDER_NET_GTISPDNGINT: 156062306a36Sopenharmony_ci case SPIDER_NET_GRIFMTERINT: 156162306a36Sopenharmony_ci case SPIDER_NET_GRIPKTRVKINT: 156262306a36Sopenharmony_ci case SPIDER_NET_GRISPINGINT: 156362306a36Sopenharmony_ci case SPIDER_NET_GRISADNGINT: 156462306a36Sopenharmony_ci case SPIDER_NET_GRISPDNGINT: 156562306a36Sopenharmony_ci break; 156662306a36Sopenharmony_ci */ 156762306a36Sopenharmony_ci default: 156862306a36Sopenharmony_ci break; 156962306a36Sopenharmony_ci } 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci if ((show_error) && (netif_msg_intr(card)) && net_ratelimit()) 157262306a36Sopenharmony_ci dev_err(&card->netdev->dev, "Error interrupt, GHIINT0STS = 0x%08x, " 157362306a36Sopenharmony_ci "GHIINT1STS = 0x%08x, GHIINT2STS = 0x%08x\n", 157462306a36Sopenharmony_ci status_reg, error_reg1, error_reg2); 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci /* clear interrupt sources */ 157762306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GHIINT1STS, error_reg1); 157862306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GHIINT2STS, error_reg2); 157962306a36Sopenharmony_ci} 158062306a36Sopenharmony_ci 158162306a36Sopenharmony_ci/** 158262306a36Sopenharmony_ci * spider_net_interrupt - interrupt handler for spider_net 158362306a36Sopenharmony_ci * @irq: interrupt number 158462306a36Sopenharmony_ci * @ptr: pointer to net_device 158562306a36Sopenharmony_ci * 158662306a36Sopenharmony_ci * returns IRQ_HANDLED, if interrupt was for driver, or IRQ_NONE, if no 158762306a36Sopenharmony_ci * interrupt found raised by card. 158862306a36Sopenharmony_ci * 158962306a36Sopenharmony_ci * This is the interrupt handler, that turns off 159062306a36Sopenharmony_ci * interrupts for this device and makes the stack poll the driver 159162306a36Sopenharmony_ci */ 159262306a36Sopenharmony_cistatic irqreturn_t 159362306a36Sopenharmony_cispider_net_interrupt(int irq, void *ptr) 159462306a36Sopenharmony_ci{ 159562306a36Sopenharmony_ci struct net_device *netdev = ptr; 159662306a36Sopenharmony_ci struct spider_net_card *card = netdev_priv(netdev); 159762306a36Sopenharmony_ci u32 status_reg, error_reg1, error_reg2; 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci status_reg = spider_net_read_reg(card, SPIDER_NET_GHIINT0STS); 160062306a36Sopenharmony_ci error_reg1 = spider_net_read_reg(card, SPIDER_NET_GHIINT1STS); 160162306a36Sopenharmony_ci error_reg2 = spider_net_read_reg(card, SPIDER_NET_GHIINT2STS); 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci if (!(status_reg & SPIDER_NET_INT0_MASK_VALUE) && 160462306a36Sopenharmony_ci !(error_reg1 & SPIDER_NET_INT1_MASK_VALUE) && 160562306a36Sopenharmony_ci !(error_reg2 & SPIDER_NET_INT2_MASK_VALUE)) 160662306a36Sopenharmony_ci return IRQ_NONE; 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_ci if (status_reg & SPIDER_NET_RXINT ) { 160962306a36Sopenharmony_ci spider_net_rx_irq_off(card); 161062306a36Sopenharmony_ci napi_schedule(&card->napi); 161162306a36Sopenharmony_ci card->num_rx_ints ++; 161262306a36Sopenharmony_ci } 161362306a36Sopenharmony_ci if (status_reg & SPIDER_NET_TXINT) 161462306a36Sopenharmony_ci napi_schedule(&card->napi); 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ci if (status_reg & SPIDER_NET_LINKINT) 161762306a36Sopenharmony_ci spider_net_link_reset(netdev); 161862306a36Sopenharmony_ci 161962306a36Sopenharmony_ci if (status_reg & SPIDER_NET_ERRINT ) 162062306a36Sopenharmony_ci spider_net_handle_error_irq(card, status_reg, 162162306a36Sopenharmony_ci error_reg1, error_reg2); 162262306a36Sopenharmony_ci 162362306a36Sopenharmony_ci /* clear interrupt sources */ 162462306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GHIINT0STS, status_reg); 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_ci return IRQ_HANDLED; 162762306a36Sopenharmony_ci} 162862306a36Sopenharmony_ci 162962306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 163062306a36Sopenharmony_ci/** 163162306a36Sopenharmony_ci * spider_net_poll_controller - artificial interrupt for netconsole etc. 163262306a36Sopenharmony_ci * @netdev: interface device structure 163362306a36Sopenharmony_ci * 163462306a36Sopenharmony_ci * see Documentation/networking/netconsole.rst 163562306a36Sopenharmony_ci */ 163662306a36Sopenharmony_cistatic void 163762306a36Sopenharmony_cispider_net_poll_controller(struct net_device *netdev) 163862306a36Sopenharmony_ci{ 163962306a36Sopenharmony_ci disable_irq(netdev->irq); 164062306a36Sopenharmony_ci spider_net_interrupt(netdev->irq, netdev); 164162306a36Sopenharmony_ci enable_irq(netdev->irq); 164262306a36Sopenharmony_ci} 164362306a36Sopenharmony_ci#endif /* CONFIG_NET_POLL_CONTROLLER */ 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_ci/** 164662306a36Sopenharmony_ci * spider_net_enable_interrupts - enable interrupts 164762306a36Sopenharmony_ci * @card: card structure 164862306a36Sopenharmony_ci * 164962306a36Sopenharmony_ci * spider_net_enable_interrupt enables several interrupts 165062306a36Sopenharmony_ci */ 165162306a36Sopenharmony_cistatic void 165262306a36Sopenharmony_cispider_net_enable_interrupts(struct spider_net_card *card) 165362306a36Sopenharmony_ci{ 165462306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GHIINT0MSK, 165562306a36Sopenharmony_ci SPIDER_NET_INT0_MASK_VALUE); 165662306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GHIINT1MSK, 165762306a36Sopenharmony_ci SPIDER_NET_INT1_MASK_VALUE); 165862306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GHIINT2MSK, 165962306a36Sopenharmony_ci SPIDER_NET_INT2_MASK_VALUE); 166062306a36Sopenharmony_ci} 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_ci/** 166362306a36Sopenharmony_ci * spider_net_disable_interrupts - disable interrupts 166462306a36Sopenharmony_ci * @card: card structure 166562306a36Sopenharmony_ci * 166662306a36Sopenharmony_ci * spider_net_disable_interrupts disables all the interrupts 166762306a36Sopenharmony_ci */ 166862306a36Sopenharmony_cistatic void 166962306a36Sopenharmony_cispider_net_disable_interrupts(struct spider_net_card *card) 167062306a36Sopenharmony_ci{ 167162306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GHIINT0MSK, 0); 167262306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GHIINT1MSK, 0); 167362306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GHIINT2MSK, 0); 167462306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GMACINTEN, 0); 167562306a36Sopenharmony_ci} 167662306a36Sopenharmony_ci 167762306a36Sopenharmony_ci/** 167862306a36Sopenharmony_ci * spider_net_init_card - initializes the card 167962306a36Sopenharmony_ci * @card: card structure 168062306a36Sopenharmony_ci * 168162306a36Sopenharmony_ci * spider_net_init_card initializes the card so that other registers can 168262306a36Sopenharmony_ci * be used 168362306a36Sopenharmony_ci */ 168462306a36Sopenharmony_cistatic void 168562306a36Sopenharmony_cispider_net_init_card(struct spider_net_card *card) 168662306a36Sopenharmony_ci{ 168762306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_CKRCTRL, 168862306a36Sopenharmony_ci SPIDER_NET_CKRCTRL_STOP_VALUE); 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_CKRCTRL, 169162306a36Sopenharmony_ci SPIDER_NET_CKRCTRL_RUN_VALUE); 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci /* trigger ETOMOD signal */ 169462306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GMACOPEMD, 169562306a36Sopenharmony_ci spider_net_read_reg(card, SPIDER_NET_GMACOPEMD) | 0x4); 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci spider_net_disable_interrupts(card); 169862306a36Sopenharmony_ci} 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci/** 170162306a36Sopenharmony_ci * spider_net_enable_card - enables the card by setting all kinds of regs 170262306a36Sopenharmony_ci * @card: card structure 170362306a36Sopenharmony_ci * 170462306a36Sopenharmony_ci * spider_net_enable_card sets a lot of SMMIO registers to enable the device 170562306a36Sopenharmony_ci */ 170662306a36Sopenharmony_cistatic void 170762306a36Sopenharmony_cispider_net_enable_card(struct spider_net_card *card) 170862306a36Sopenharmony_ci{ 170962306a36Sopenharmony_ci int i; 171062306a36Sopenharmony_ci /* the following array consists of (register),(value) pairs 171162306a36Sopenharmony_ci * that are set in this function. A register of 0 ends the list 171262306a36Sopenharmony_ci */ 171362306a36Sopenharmony_ci u32 regs[][2] = { 171462306a36Sopenharmony_ci { SPIDER_NET_GRESUMINTNUM, 0 }, 171562306a36Sopenharmony_ci { SPIDER_NET_GREINTNUM, 0 }, 171662306a36Sopenharmony_ci 171762306a36Sopenharmony_ci /* set interrupt frame number registers */ 171862306a36Sopenharmony_ci /* clear the single DMA engine registers first */ 171962306a36Sopenharmony_ci { SPIDER_NET_GFAFRMNUM, SPIDER_NET_GFXFRAMES_VALUE }, 172062306a36Sopenharmony_ci { SPIDER_NET_GFBFRMNUM, SPIDER_NET_GFXFRAMES_VALUE }, 172162306a36Sopenharmony_ci { SPIDER_NET_GFCFRMNUM, SPIDER_NET_GFXFRAMES_VALUE }, 172262306a36Sopenharmony_ci { SPIDER_NET_GFDFRMNUM, SPIDER_NET_GFXFRAMES_VALUE }, 172362306a36Sopenharmony_ci /* then set, what we really need */ 172462306a36Sopenharmony_ci { SPIDER_NET_GFFRMNUM, SPIDER_NET_FRAMENUM_VALUE }, 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci /* timer counter registers and stuff */ 172762306a36Sopenharmony_ci { SPIDER_NET_GFREECNNUM, 0 }, 172862306a36Sopenharmony_ci { SPIDER_NET_GONETIMENUM, 0 }, 172962306a36Sopenharmony_ci { SPIDER_NET_GTOUTFRMNUM, 0 }, 173062306a36Sopenharmony_ci 173162306a36Sopenharmony_ci /* RX mode setting */ 173262306a36Sopenharmony_ci { SPIDER_NET_GRXMDSET, SPIDER_NET_RXMODE_VALUE }, 173362306a36Sopenharmony_ci /* TX mode setting */ 173462306a36Sopenharmony_ci { SPIDER_NET_GTXMDSET, SPIDER_NET_TXMODE_VALUE }, 173562306a36Sopenharmony_ci /* IPSEC mode setting */ 173662306a36Sopenharmony_ci { SPIDER_NET_GIPSECINIT, SPIDER_NET_IPSECINIT_VALUE }, 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci { SPIDER_NET_GFTRESTRT, SPIDER_NET_RESTART_VALUE }, 173962306a36Sopenharmony_ci 174062306a36Sopenharmony_ci { SPIDER_NET_GMRWOLCTRL, 0 }, 174162306a36Sopenharmony_ci { SPIDER_NET_GTESTMD, 0x10000000 }, 174262306a36Sopenharmony_ci { SPIDER_NET_GTTQMSK, 0x00400040 }, 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci { SPIDER_NET_GMACINTEN, 0 }, 174562306a36Sopenharmony_ci 174662306a36Sopenharmony_ci /* flow control stuff */ 174762306a36Sopenharmony_ci { SPIDER_NET_GMACAPAUSE, SPIDER_NET_MACAPAUSE_VALUE }, 174862306a36Sopenharmony_ci { SPIDER_NET_GMACTXPAUSE, SPIDER_NET_TXPAUSE_VALUE }, 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci { SPIDER_NET_GMACBSTLMT, SPIDER_NET_BURSTLMT_VALUE }, 175162306a36Sopenharmony_ci { 0, 0} 175262306a36Sopenharmony_ci }; 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci i = 0; 175562306a36Sopenharmony_ci while (regs[i][0]) { 175662306a36Sopenharmony_ci spider_net_write_reg(card, regs[i][0], regs[i][1]); 175762306a36Sopenharmony_ci i++; 175862306a36Sopenharmony_ci } 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci /* clear unicast filter table entries 1 to 14 */ 176162306a36Sopenharmony_ci for (i = 1; i <= 14; i++) { 176262306a36Sopenharmony_ci spider_net_write_reg(card, 176362306a36Sopenharmony_ci SPIDER_NET_GMRUAFILnR + i * 8, 176462306a36Sopenharmony_ci 0x00080000); 176562306a36Sopenharmony_ci spider_net_write_reg(card, 176662306a36Sopenharmony_ci SPIDER_NET_GMRUAFILnR + i * 8 + 4, 176762306a36Sopenharmony_ci 0x00000000); 176862306a36Sopenharmony_ci } 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GMRUA0FIL15R, 0x08080000); 177162306a36Sopenharmony_ci 177262306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_ECMODE, SPIDER_NET_ECMODE_VALUE); 177362306a36Sopenharmony_ci 177462306a36Sopenharmony_ci /* set chain tail address for RX chains and 177562306a36Sopenharmony_ci * enable DMA 177662306a36Sopenharmony_ci */ 177762306a36Sopenharmony_ci spider_net_enable_rxchtails(card); 177862306a36Sopenharmony_ci spider_net_enable_rxdmac(card); 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GRXDMAEN, SPIDER_NET_WOL_VALUE); 178162306a36Sopenharmony_ci 178262306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GMACLENLMT, 178362306a36Sopenharmony_ci SPIDER_NET_LENLMT_VALUE); 178462306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GMACOPEMD, 178562306a36Sopenharmony_ci SPIDER_NET_OPMODE_VALUE); 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GDTDMACCNTR, 178862306a36Sopenharmony_ci SPIDER_NET_GDTBSTA); 178962306a36Sopenharmony_ci} 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci/** 179262306a36Sopenharmony_ci * spider_net_download_firmware - loads firmware into the adapter 179362306a36Sopenharmony_ci * @card: card structure 179462306a36Sopenharmony_ci * @firmware_ptr: pointer to firmware data 179562306a36Sopenharmony_ci * 179662306a36Sopenharmony_ci * spider_net_download_firmware loads the firmware data into the 179762306a36Sopenharmony_ci * adapter. It assumes the length etc. to be allright. 179862306a36Sopenharmony_ci */ 179962306a36Sopenharmony_cistatic int 180062306a36Sopenharmony_cispider_net_download_firmware(struct spider_net_card *card, 180162306a36Sopenharmony_ci const void *firmware_ptr) 180262306a36Sopenharmony_ci{ 180362306a36Sopenharmony_ci int sequencer, i; 180462306a36Sopenharmony_ci const u32 *fw_ptr = firmware_ptr; 180562306a36Sopenharmony_ci 180662306a36Sopenharmony_ci /* stop sequencers */ 180762306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GSINIT, 180862306a36Sopenharmony_ci SPIDER_NET_STOP_SEQ_VALUE); 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci for (sequencer = 0; sequencer < SPIDER_NET_FIRMWARE_SEQS; 181162306a36Sopenharmony_ci sequencer++) { 181262306a36Sopenharmony_ci spider_net_write_reg(card, 181362306a36Sopenharmony_ci SPIDER_NET_GSnPRGADR + sequencer * 8, 0); 181462306a36Sopenharmony_ci for (i = 0; i < SPIDER_NET_FIRMWARE_SEQWORDS; i++) { 181562306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GSnPRGDAT + 181662306a36Sopenharmony_ci sequencer * 8, *fw_ptr); 181762306a36Sopenharmony_ci fw_ptr++; 181862306a36Sopenharmony_ci } 181962306a36Sopenharmony_ci } 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci if (spider_net_read_reg(card, SPIDER_NET_GSINIT)) 182262306a36Sopenharmony_ci return -EIO; 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GSINIT, 182562306a36Sopenharmony_ci SPIDER_NET_RUN_SEQ_VALUE); 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_ci return 0; 182862306a36Sopenharmony_ci} 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ci/** 183162306a36Sopenharmony_ci * spider_net_init_firmware - reads in firmware parts 183262306a36Sopenharmony_ci * @card: card structure 183362306a36Sopenharmony_ci * 183462306a36Sopenharmony_ci * Returns 0 on success, <0 on failure 183562306a36Sopenharmony_ci * 183662306a36Sopenharmony_ci * spider_net_init_firmware opens the sequencer firmware and does some basic 183762306a36Sopenharmony_ci * checks. This function opens and releases the firmware structure. A call 183862306a36Sopenharmony_ci * to download the firmware is performed before the release. 183962306a36Sopenharmony_ci * 184062306a36Sopenharmony_ci * Firmware format 184162306a36Sopenharmony_ci * =============== 184262306a36Sopenharmony_ci * spider_fw.bin is expected to be a file containing 6*1024*4 bytes, 4k being 184362306a36Sopenharmony_ci * the program for each sequencer. Use the command 184462306a36Sopenharmony_ci * tail -q -n +2 Seq_code1_0x088.txt Seq_code2_0x090.txt \ 184562306a36Sopenharmony_ci * Seq_code3_0x098.txt Seq_code4_0x0A0.txt Seq_code5_0x0A8.txt \ 184662306a36Sopenharmony_ci * Seq_code6_0x0B0.txt | xxd -r -p -c4 > spider_fw.bin 184762306a36Sopenharmony_ci * 184862306a36Sopenharmony_ci * to generate spider_fw.bin, if you have sequencer programs with something 184962306a36Sopenharmony_ci * like the following contents for each sequencer: 185062306a36Sopenharmony_ci * <ONE LINE COMMENT> 185162306a36Sopenharmony_ci * <FIRST 4-BYTES-WORD FOR SEQUENCER> 185262306a36Sopenharmony_ci * <SECOND 4-BYTES-WORD FOR SEQUENCER> 185362306a36Sopenharmony_ci * ... 185462306a36Sopenharmony_ci * <1024th 4-BYTES-WORD FOR SEQUENCER> 185562306a36Sopenharmony_ci */ 185662306a36Sopenharmony_cistatic int 185762306a36Sopenharmony_cispider_net_init_firmware(struct spider_net_card *card) 185862306a36Sopenharmony_ci{ 185962306a36Sopenharmony_ci struct firmware *firmware = NULL; 186062306a36Sopenharmony_ci struct device_node *dn; 186162306a36Sopenharmony_ci const u8 *fw_prop = NULL; 186262306a36Sopenharmony_ci int err = -ENOENT; 186362306a36Sopenharmony_ci int fw_size; 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_ci if (request_firmware((const struct firmware **)&firmware, 186662306a36Sopenharmony_ci SPIDER_NET_FIRMWARE_NAME, &card->pdev->dev) == 0) { 186762306a36Sopenharmony_ci if ( (firmware->size != SPIDER_NET_FIRMWARE_LEN) && 186862306a36Sopenharmony_ci netif_msg_probe(card) ) { 186962306a36Sopenharmony_ci dev_err(&card->netdev->dev, 187062306a36Sopenharmony_ci "Incorrect size of spidernet firmware in " \ 187162306a36Sopenharmony_ci "filesystem. Looking in host firmware...\n"); 187262306a36Sopenharmony_ci goto try_host_fw; 187362306a36Sopenharmony_ci } 187462306a36Sopenharmony_ci err = spider_net_download_firmware(card, firmware->data); 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci release_firmware(firmware); 187762306a36Sopenharmony_ci if (err) 187862306a36Sopenharmony_ci goto try_host_fw; 187962306a36Sopenharmony_ci 188062306a36Sopenharmony_ci goto done; 188162306a36Sopenharmony_ci } 188262306a36Sopenharmony_ci 188362306a36Sopenharmony_citry_host_fw: 188462306a36Sopenharmony_ci dn = pci_device_to_OF_node(card->pdev); 188562306a36Sopenharmony_ci if (!dn) 188662306a36Sopenharmony_ci goto out_err; 188762306a36Sopenharmony_ci 188862306a36Sopenharmony_ci fw_prop = of_get_property(dn, "firmware", &fw_size); 188962306a36Sopenharmony_ci if (!fw_prop) 189062306a36Sopenharmony_ci goto out_err; 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci if ( (fw_size != SPIDER_NET_FIRMWARE_LEN) && 189362306a36Sopenharmony_ci netif_msg_probe(card) ) { 189462306a36Sopenharmony_ci dev_err(&card->netdev->dev, 189562306a36Sopenharmony_ci "Incorrect size of spidernet firmware in host firmware\n"); 189662306a36Sopenharmony_ci goto done; 189762306a36Sopenharmony_ci } 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci err = spider_net_download_firmware(card, fw_prop); 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_cidone: 190262306a36Sopenharmony_ci return err; 190362306a36Sopenharmony_ciout_err: 190462306a36Sopenharmony_ci if (netif_msg_probe(card)) 190562306a36Sopenharmony_ci dev_err(&card->netdev->dev, 190662306a36Sopenharmony_ci "Couldn't find spidernet firmware in filesystem " \ 190762306a36Sopenharmony_ci "or host firmware\n"); 190862306a36Sopenharmony_ci return err; 190962306a36Sopenharmony_ci} 191062306a36Sopenharmony_ci 191162306a36Sopenharmony_ci/** 191262306a36Sopenharmony_ci * spider_net_open - called upon ifonfig up 191362306a36Sopenharmony_ci * @netdev: interface device structure 191462306a36Sopenharmony_ci * 191562306a36Sopenharmony_ci * returns 0 on success, <0 on failure 191662306a36Sopenharmony_ci * 191762306a36Sopenharmony_ci * spider_net_open allocates all the descriptors and memory needed for 191862306a36Sopenharmony_ci * operation, sets up multicast list and enables interrupts 191962306a36Sopenharmony_ci */ 192062306a36Sopenharmony_ciint 192162306a36Sopenharmony_cispider_net_open(struct net_device *netdev) 192262306a36Sopenharmony_ci{ 192362306a36Sopenharmony_ci struct spider_net_card *card = netdev_priv(netdev); 192462306a36Sopenharmony_ci int result; 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci result = spider_net_init_firmware(card); 192762306a36Sopenharmony_ci if (result) 192862306a36Sopenharmony_ci goto init_firmware_failed; 192962306a36Sopenharmony_ci 193062306a36Sopenharmony_ci /* start probing with copper */ 193162306a36Sopenharmony_ci card->aneg_count = 0; 193262306a36Sopenharmony_ci card->medium = BCM54XX_COPPER; 193362306a36Sopenharmony_ci spider_net_setup_aneg(card); 193462306a36Sopenharmony_ci if (card->phy.def->phy_id) 193562306a36Sopenharmony_ci mod_timer(&card->aneg_timer, jiffies + SPIDER_NET_ANEG_TIMER); 193662306a36Sopenharmony_ci 193762306a36Sopenharmony_ci result = spider_net_init_chain(card, &card->tx_chain); 193862306a36Sopenharmony_ci if (result) 193962306a36Sopenharmony_ci goto alloc_tx_failed; 194062306a36Sopenharmony_ci card->low_watermark = NULL; 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ci result = spider_net_init_chain(card, &card->rx_chain); 194362306a36Sopenharmony_ci if (result) 194462306a36Sopenharmony_ci goto alloc_rx_failed; 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_ci /* Allocate rx skbs */ 194762306a36Sopenharmony_ci result = spider_net_alloc_rx_skbs(card); 194862306a36Sopenharmony_ci if (result) 194962306a36Sopenharmony_ci goto alloc_skbs_failed; 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_ci spider_net_set_multi(netdev); 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_ci /* further enhancement: setup hw vlan, if needed */ 195462306a36Sopenharmony_ci 195562306a36Sopenharmony_ci result = -EBUSY; 195662306a36Sopenharmony_ci if (request_irq(netdev->irq, spider_net_interrupt, 195762306a36Sopenharmony_ci IRQF_SHARED, netdev->name, netdev)) 195862306a36Sopenharmony_ci goto register_int_failed; 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_ci spider_net_enable_card(card); 196162306a36Sopenharmony_ci 196262306a36Sopenharmony_ci netif_start_queue(netdev); 196362306a36Sopenharmony_ci netif_carrier_on(netdev); 196462306a36Sopenharmony_ci napi_enable(&card->napi); 196562306a36Sopenharmony_ci 196662306a36Sopenharmony_ci spider_net_enable_interrupts(card); 196762306a36Sopenharmony_ci 196862306a36Sopenharmony_ci return 0; 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_ciregister_int_failed: 197162306a36Sopenharmony_ci spider_net_free_rx_chain_contents(card); 197262306a36Sopenharmony_cialloc_skbs_failed: 197362306a36Sopenharmony_ci spider_net_free_chain(card, &card->rx_chain); 197462306a36Sopenharmony_cialloc_rx_failed: 197562306a36Sopenharmony_ci spider_net_free_chain(card, &card->tx_chain); 197662306a36Sopenharmony_cialloc_tx_failed: 197762306a36Sopenharmony_ci del_timer_sync(&card->aneg_timer); 197862306a36Sopenharmony_ciinit_firmware_failed: 197962306a36Sopenharmony_ci return result; 198062306a36Sopenharmony_ci} 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_ci/** 198362306a36Sopenharmony_ci * spider_net_link_phy 198462306a36Sopenharmony_ci * @t: timer context used to obtain the pointer to net card data structure 198562306a36Sopenharmony_ci */ 198662306a36Sopenharmony_cistatic void spider_net_link_phy(struct timer_list *t) 198762306a36Sopenharmony_ci{ 198862306a36Sopenharmony_ci struct spider_net_card *card = from_timer(card, t, aneg_timer); 198962306a36Sopenharmony_ci struct mii_phy *phy = &card->phy; 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci /* if link didn't come up after SPIDER_NET_ANEG_TIMEOUT tries, setup phy again */ 199262306a36Sopenharmony_ci if (card->aneg_count > SPIDER_NET_ANEG_TIMEOUT) { 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci pr_debug("%s: link is down trying to bring it up\n", 199562306a36Sopenharmony_ci card->netdev->name); 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_ci switch (card->medium) { 199862306a36Sopenharmony_ci case BCM54XX_COPPER: 199962306a36Sopenharmony_ci /* enable fiber with autonegotiation first */ 200062306a36Sopenharmony_ci if (phy->def->ops->enable_fiber) 200162306a36Sopenharmony_ci phy->def->ops->enable_fiber(phy, 1); 200262306a36Sopenharmony_ci card->medium = BCM54XX_FIBER; 200362306a36Sopenharmony_ci break; 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ci case BCM54XX_FIBER: 200662306a36Sopenharmony_ci /* fiber didn't come up, try to disable fiber autoneg */ 200762306a36Sopenharmony_ci if (phy->def->ops->enable_fiber) 200862306a36Sopenharmony_ci phy->def->ops->enable_fiber(phy, 0); 200962306a36Sopenharmony_ci card->medium = BCM54XX_UNKNOWN; 201062306a36Sopenharmony_ci break; 201162306a36Sopenharmony_ci 201262306a36Sopenharmony_ci case BCM54XX_UNKNOWN: 201362306a36Sopenharmony_ci /* copper, fiber with and without failed, 201462306a36Sopenharmony_ci * retry from beginning 201562306a36Sopenharmony_ci */ 201662306a36Sopenharmony_ci spider_net_setup_aneg(card); 201762306a36Sopenharmony_ci card->medium = BCM54XX_COPPER; 201862306a36Sopenharmony_ci break; 201962306a36Sopenharmony_ci } 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci card->aneg_count = 0; 202262306a36Sopenharmony_ci mod_timer(&card->aneg_timer, jiffies + SPIDER_NET_ANEG_TIMER); 202362306a36Sopenharmony_ci return; 202462306a36Sopenharmony_ci } 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_ci /* link still not up, try again later */ 202762306a36Sopenharmony_ci if (!(phy->def->ops->poll_link(phy))) { 202862306a36Sopenharmony_ci card->aneg_count++; 202962306a36Sopenharmony_ci mod_timer(&card->aneg_timer, jiffies + SPIDER_NET_ANEG_TIMER); 203062306a36Sopenharmony_ci return; 203162306a36Sopenharmony_ci } 203262306a36Sopenharmony_ci 203362306a36Sopenharmony_ci /* link came up, get abilities */ 203462306a36Sopenharmony_ci phy->def->ops->read_link(phy); 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GMACST, 203762306a36Sopenharmony_ci spider_net_read_reg(card, SPIDER_NET_GMACST)); 203862306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GMACINTEN, 0x4); 203962306a36Sopenharmony_ci 204062306a36Sopenharmony_ci if (phy->speed == 1000) 204162306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GMACMODE, 0x00000001); 204262306a36Sopenharmony_ci else 204362306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GMACMODE, 0); 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci card->aneg_count = 0; 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_ci pr_info("%s: link up, %i Mbps, %s-duplex %sautoneg.\n", 204862306a36Sopenharmony_ci card->netdev->name, phy->speed, 204962306a36Sopenharmony_ci phy->duplex == 1 ? "Full" : "Half", 205062306a36Sopenharmony_ci phy->autoneg == 1 ? "" : "no "); 205162306a36Sopenharmony_ci} 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ci/** 205462306a36Sopenharmony_ci * spider_net_setup_phy - setup PHY 205562306a36Sopenharmony_ci * @card: card structure 205662306a36Sopenharmony_ci * 205762306a36Sopenharmony_ci * returns 0 on success, <0 on failure 205862306a36Sopenharmony_ci * 205962306a36Sopenharmony_ci * spider_net_setup_phy is used as part of spider_net_probe. 206062306a36Sopenharmony_ci **/ 206162306a36Sopenharmony_cistatic int 206262306a36Sopenharmony_cispider_net_setup_phy(struct spider_net_card *card) 206362306a36Sopenharmony_ci{ 206462306a36Sopenharmony_ci struct mii_phy *phy = &card->phy; 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GDTDMASEL, 206762306a36Sopenharmony_ci SPIDER_NET_DMASEL_VALUE); 206862306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GPCCTRL, 206962306a36Sopenharmony_ci SPIDER_NET_PHY_CTRL_VALUE); 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_ci phy->dev = card->netdev; 207262306a36Sopenharmony_ci phy->mdio_read = spider_net_read_phy; 207362306a36Sopenharmony_ci phy->mdio_write = spider_net_write_phy; 207462306a36Sopenharmony_ci 207562306a36Sopenharmony_ci for (phy->mii_id = 1; phy->mii_id <= 31; phy->mii_id++) { 207662306a36Sopenharmony_ci unsigned short id; 207762306a36Sopenharmony_ci id = spider_net_read_phy(card->netdev, phy->mii_id, MII_BMSR); 207862306a36Sopenharmony_ci if (id != 0x0000 && id != 0xffff) { 207962306a36Sopenharmony_ci if (!sungem_phy_probe(phy, phy->mii_id)) { 208062306a36Sopenharmony_ci pr_info("Found %s.\n", phy->def->name); 208162306a36Sopenharmony_ci break; 208262306a36Sopenharmony_ci } 208362306a36Sopenharmony_ci } 208462306a36Sopenharmony_ci } 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci return 0; 208762306a36Sopenharmony_ci} 208862306a36Sopenharmony_ci 208962306a36Sopenharmony_ci/** 209062306a36Sopenharmony_ci * spider_net_workaround_rxramfull - work around firmware bug 209162306a36Sopenharmony_ci * @card: card structure 209262306a36Sopenharmony_ci * 209362306a36Sopenharmony_ci * no return value 209462306a36Sopenharmony_ci **/ 209562306a36Sopenharmony_cistatic void 209662306a36Sopenharmony_cispider_net_workaround_rxramfull(struct spider_net_card *card) 209762306a36Sopenharmony_ci{ 209862306a36Sopenharmony_ci int i, sequencer = 0; 209962306a36Sopenharmony_ci 210062306a36Sopenharmony_ci /* cancel reset */ 210162306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_CKRCTRL, 210262306a36Sopenharmony_ci SPIDER_NET_CKRCTRL_RUN_VALUE); 210362306a36Sopenharmony_ci 210462306a36Sopenharmony_ci /* empty sequencer data */ 210562306a36Sopenharmony_ci for (sequencer = 0; sequencer < SPIDER_NET_FIRMWARE_SEQS; 210662306a36Sopenharmony_ci sequencer++) { 210762306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GSnPRGADR + 210862306a36Sopenharmony_ci sequencer * 8, 0x0); 210962306a36Sopenharmony_ci for (i = 0; i < SPIDER_NET_FIRMWARE_SEQWORDS; i++) { 211062306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GSnPRGDAT + 211162306a36Sopenharmony_ci sequencer * 8, 0x0); 211262306a36Sopenharmony_ci } 211362306a36Sopenharmony_ci } 211462306a36Sopenharmony_ci 211562306a36Sopenharmony_ci /* set sequencer operation */ 211662306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GSINIT, 0x000000fe); 211762306a36Sopenharmony_ci 211862306a36Sopenharmony_ci /* reset */ 211962306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_CKRCTRL, 212062306a36Sopenharmony_ci SPIDER_NET_CKRCTRL_STOP_VALUE); 212162306a36Sopenharmony_ci} 212262306a36Sopenharmony_ci 212362306a36Sopenharmony_ci/** 212462306a36Sopenharmony_ci * spider_net_stop - called upon ifconfig down 212562306a36Sopenharmony_ci * @netdev: interface device structure 212662306a36Sopenharmony_ci * 212762306a36Sopenharmony_ci * always returns 0 212862306a36Sopenharmony_ci */ 212962306a36Sopenharmony_ciint 213062306a36Sopenharmony_cispider_net_stop(struct net_device *netdev) 213162306a36Sopenharmony_ci{ 213262306a36Sopenharmony_ci struct spider_net_card *card = netdev_priv(netdev); 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_ci napi_disable(&card->napi); 213562306a36Sopenharmony_ci netif_carrier_off(netdev); 213662306a36Sopenharmony_ci netif_stop_queue(netdev); 213762306a36Sopenharmony_ci del_timer_sync(&card->tx_timer); 213862306a36Sopenharmony_ci del_timer_sync(&card->aneg_timer); 213962306a36Sopenharmony_ci 214062306a36Sopenharmony_ci spider_net_disable_interrupts(card); 214162306a36Sopenharmony_ci 214262306a36Sopenharmony_ci free_irq(netdev->irq, netdev); 214362306a36Sopenharmony_ci 214462306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_GDTDMACCNTR, 214562306a36Sopenharmony_ci SPIDER_NET_DMA_TX_FEND_VALUE); 214662306a36Sopenharmony_ci 214762306a36Sopenharmony_ci /* turn off DMA, force end */ 214862306a36Sopenharmony_ci spider_net_disable_rxdmac(card); 214962306a36Sopenharmony_ci 215062306a36Sopenharmony_ci /* release chains */ 215162306a36Sopenharmony_ci spider_net_release_tx_chain(card, 1); 215262306a36Sopenharmony_ci spider_net_free_rx_chain_contents(card); 215362306a36Sopenharmony_ci 215462306a36Sopenharmony_ci spider_net_free_chain(card, &card->tx_chain); 215562306a36Sopenharmony_ci spider_net_free_chain(card, &card->rx_chain); 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci return 0; 215862306a36Sopenharmony_ci} 215962306a36Sopenharmony_ci 216062306a36Sopenharmony_ci/** 216162306a36Sopenharmony_ci * spider_net_tx_timeout_task - task scheduled by the watchdog timeout 216262306a36Sopenharmony_ci * function (to be called not under interrupt status) 216362306a36Sopenharmony_ci * @work: work context used to obtain the pointer to net card data structure 216462306a36Sopenharmony_ci * 216562306a36Sopenharmony_ci * called as task when tx hangs, resets interface (if interface is up) 216662306a36Sopenharmony_ci */ 216762306a36Sopenharmony_cistatic void 216862306a36Sopenharmony_cispider_net_tx_timeout_task(struct work_struct *work) 216962306a36Sopenharmony_ci{ 217062306a36Sopenharmony_ci struct spider_net_card *card = 217162306a36Sopenharmony_ci container_of(work, struct spider_net_card, tx_timeout_task); 217262306a36Sopenharmony_ci struct net_device *netdev = card->netdev; 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_ci if (!(netdev->flags & IFF_UP)) 217562306a36Sopenharmony_ci goto out; 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_ci netif_device_detach(netdev); 217862306a36Sopenharmony_ci spider_net_stop(netdev); 217962306a36Sopenharmony_ci 218062306a36Sopenharmony_ci spider_net_workaround_rxramfull(card); 218162306a36Sopenharmony_ci spider_net_init_card(card); 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ci if (spider_net_setup_phy(card)) 218462306a36Sopenharmony_ci goto out; 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_ci spider_net_open(netdev); 218762306a36Sopenharmony_ci spider_net_kick_tx_dma(card); 218862306a36Sopenharmony_ci netif_device_attach(netdev); 218962306a36Sopenharmony_ci 219062306a36Sopenharmony_ciout: 219162306a36Sopenharmony_ci atomic_dec(&card->tx_timeout_task_counter); 219262306a36Sopenharmony_ci} 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci/** 219562306a36Sopenharmony_ci * spider_net_tx_timeout - called when the tx timeout watchdog kicks in. 219662306a36Sopenharmony_ci * @netdev: interface device structure 219762306a36Sopenharmony_ci * @txqueue: unused 219862306a36Sopenharmony_ci * 219962306a36Sopenharmony_ci * called, if tx hangs. Schedules a task that resets the interface 220062306a36Sopenharmony_ci */ 220162306a36Sopenharmony_cistatic void 220262306a36Sopenharmony_cispider_net_tx_timeout(struct net_device *netdev, unsigned int txqueue) 220362306a36Sopenharmony_ci{ 220462306a36Sopenharmony_ci struct spider_net_card *card; 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_ci card = netdev_priv(netdev); 220762306a36Sopenharmony_ci atomic_inc(&card->tx_timeout_task_counter); 220862306a36Sopenharmony_ci if (netdev->flags & IFF_UP) 220962306a36Sopenharmony_ci schedule_work(&card->tx_timeout_task); 221062306a36Sopenharmony_ci else 221162306a36Sopenharmony_ci atomic_dec(&card->tx_timeout_task_counter); 221262306a36Sopenharmony_ci card->spider_stats.tx_timeouts++; 221362306a36Sopenharmony_ci} 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_cistatic const struct net_device_ops spider_net_ops = { 221662306a36Sopenharmony_ci .ndo_open = spider_net_open, 221762306a36Sopenharmony_ci .ndo_stop = spider_net_stop, 221862306a36Sopenharmony_ci .ndo_start_xmit = spider_net_xmit, 221962306a36Sopenharmony_ci .ndo_set_rx_mode = spider_net_set_multi, 222062306a36Sopenharmony_ci .ndo_set_mac_address = spider_net_set_mac, 222162306a36Sopenharmony_ci .ndo_eth_ioctl = spider_net_do_ioctl, 222262306a36Sopenharmony_ci .ndo_tx_timeout = spider_net_tx_timeout, 222362306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 222462306a36Sopenharmony_ci /* HW VLAN */ 222562306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 222662306a36Sopenharmony_ci /* poll controller */ 222762306a36Sopenharmony_ci .ndo_poll_controller = spider_net_poll_controller, 222862306a36Sopenharmony_ci#endif /* CONFIG_NET_POLL_CONTROLLER */ 222962306a36Sopenharmony_ci}; 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci/** 223262306a36Sopenharmony_ci * spider_net_setup_netdev_ops - initialization of net_device operations 223362306a36Sopenharmony_ci * @netdev: net_device structure 223462306a36Sopenharmony_ci * 223562306a36Sopenharmony_ci * fills out function pointers in the net_device structure 223662306a36Sopenharmony_ci */ 223762306a36Sopenharmony_cistatic void 223862306a36Sopenharmony_cispider_net_setup_netdev_ops(struct net_device *netdev) 223962306a36Sopenharmony_ci{ 224062306a36Sopenharmony_ci netdev->netdev_ops = &spider_net_ops; 224162306a36Sopenharmony_ci netdev->watchdog_timeo = SPIDER_NET_WATCHDOG_TIMEOUT; 224262306a36Sopenharmony_ci /* ethtool ops */ 224362306a36Sopenharmony_ci netdev->ethtool_ops = &spider_net_ethtool_ops; 224462306a36Sopenharmony_ci} 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_ci/** 224762306a36Sopenharmony_ci * spider_net_setup_netdev - initialization of net_device 224862306a36Sopenharmony_ci * @card: card structure 224962306a36Sopenharmony_ci * 225062306a36Sopenharmony_ci * Returns 0 on success or <0 on failure 225162306a36Sopenharmony_ci * 225262306a36Sopenharmony_ci * spider_net_setup_netdev initializes the net_device structure 225362306a36Sopenharmony_ci **/ 225462306a36Sopenharmony_cistatic int 225562306a36Sopenharmony_cispider_net_setup_netdev(struct spider_net_card *card) 225662306a36Sopenharmony_ci{ 225762306a36Sopenharmony_ci int result; 225862306a36Sopenharmony_ci struct net_device *netdev = card->netdev; 225962306a36Sopenharmony_ci struct device_node *dn; 226062306a36Sopenharmony_ci struct sockaddr addr; 226162306a36Sopenharmony_ci const u8 *mac; 226262306a36Sopenharmony_ci 226362306a36Sopenharmony_ci SET_NETDEV_DEV(netdev, &card->pdev->dev); 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_ci pci_set_drvdata(card->pdev, netdev); 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_ci timer_setup(&card->tx_timer, spider_net_cleanup_tx_ring, 0); 226862306a36Sopenharmony_ci netdev->irq = card->pdev->irq; 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_ci card->aneg_count = 0; 227162306a36Sopenharmony_ci timer_setup(&card->aneg_timer, spider_net_link_phy, 0); 227262306a36Sopenharmony_ci 227362306a36Sopenharmony_ci netif_napi_add(netdev, &card->napi, spider_net_poll); 227462306a36Sopenharmony_ci 227562306a36Sopenharmony_ci spider_net_setup_netdev_ops(netdev); 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci netdev->hw_features = NETIF_F_RXCSUM | NETIF_F_IP_CSUM; 227862306a36Sopenharmony_ci if (SPIDER_NET_RX_CSUM_DEFAULT) 227962306a36Sopenharmony_ci netdev->features |= NETIF_F_RXCSUM; 228062306a36Sopenharmony_ci netdev->features |= NETIF_F_IP_CSUM | NETIF_F_LLTX; 228162306a36Sopenharmony_ci /* some time: NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_HW_VLAN_CTAG_RX | 228262306a36Sopenharmony_ci * NETIF_F_HW_VLAN_CTAG_FILTER 228362306a36Sopenharmony_ci */ 228462306a36Sopenharmony_ci 228562306a36Sopenharmony_ci /* MTU range: 64 - 2294 */ 228662306a36Sopenharmony_ci netdev->min_mtu = SPIDER_NET_MIN_MTU; 228762306a36Sopenharmony_ci netdev->max_mtu = SPIDER_NET_MAX_MTU; 228862306a36Sopenharmony_ci 228962306a36Sopenharmony_ci netdev->irq = card->pdev->irq; 229062306a36Sopenharmony_ci card->num_rx_ints = 0; 229162306a36Sopenharmony_ci card->ignore_rx_ramfull = 0; 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_ci dn = pci_device_to_OF_node(card->pdev); 229462306a36Sopenharmony_ci if (!dn) 229562306a36Sopenharmony_ci return -EIO; 229662306a36Sopenharmony_ci 229762306a36Sopenharmony_ci mac = of_get_property(dn, "local-mac-address", NULL); 229862306a36Sopenharmony_ci if (!mac) 229962306a36Sopenharmony_ci return -EIO; 230062306a36Sopenharmony_ci memcpy(addr.sa_data, mac, ETH_ALEN); 230162306a36Sopenharmony_ci 230262306a36Sopenharmony_ci result = spider_net_set_mac(netdev, &addr); 230362306a36Sopenharmony_ci if ((result) && (netif_msg_probe(card))) 230462306a36Sopenharmony_ci dev_err(&card->netdev->dev, 230562306a36Sopenharmony_ci "Failed to set MAC address: %i\n", result); 230662306a36Sopenharmony_ci 230762306a36Sopenharmony_ci result = register_netdev(netdev); 230862306a36Sopenharmony_ci if (result) { 230962306a36Sopenharmony_ci if (netif_msg_probe(card)) 231062306a36Sopenharmony_ci dev_err(&card->netdev->dev, 231162306a36Sopenharmony_ci "Couldn't register net_device: %i\n", result); 231262306a36Sopenharmony_ci return result; 231362306a36Sopenharmony_ci } 231462306a36Sopenharmony_ci 231562306a36Sopenharmony_ci if (netif_msg_probe(card)) 231662306a36Sopenharmony_ci pr_info("Initialized device %s.\n", netdev->name); 231762306a36Sopenharmony_ci 231862306a36Sopenharmony_ci return 0; 231962306a36Sopenharmony_ci} 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_ci/** 232262306a36Sopenharmony_ci * spider_net_alloc_card - allocates net_device and card structure 232362306a36Sopenharmony_ci * 232462306a36Sopenharmony_ci * returns the card structure or NULL in case of errors 232562306a36Sopenharmony_ci * 232662306a36Sopenharmony_ci * the card and net_device structures are linked to each other 232762306a36Sopenharmony_ci */ 232862306a36Sopenharmony_cistatic struct spider_net_card * 232962306a36Sopenharmony_cispider_net_alloc_card(void) 233062306a36Sopenharmony_ci{ 233162306a36Sopenharmony_ci struct net_device *netdev; 233262306a36Sopenharmony_ci struct spider_net_card *card; 233362306a36Sopenharmony_ci 233462306a36Sopenharmony_ci netdev = alloc_etherdev(struct_size(card, darray, 233562306a36Sopenharmony_ci size_add(tx_descriptors, rx_descriptors))); 233662306a36Sopenharmony_ci if (!netdev) 233762306a36Sopenharmony_ci return NULL; 233862306a36Sopenharmony_ci 233962306a36Sopenharmony_ci card = netdev_priv(netdev); 234062306a36Sopenharmony_ci card->netdev = netdev; 234162306a36Sopenharmony_ci card->msg_enable = SPIDER_NET_DEFAULT_MSG; 234262306a36Sopenharmony_ci INIT_WORK(&card->tx_timeout_task, spider_net_tx_timeout_task); 234362306a36Sopenharmony_ci init_waitqueue_head(&card->waitq); 234462306a36Sopenharmony_ci atomic_set(&card->tx_timeout_task_counter, 0); 234562306a36Sopenharmony_ci 234662306a36Sopenharmony_ci card->rx_chain.num_desc = rx_descriptors; 234762306a36Sopenharmony_ci card->rx_chain.ring = card->darray; 234862306a36Sopenharmony_ci card->tx_chain.num_desc = tx_descriptors; 234962306a36Sopenharmony_ci card->tx_chain.ring = card->darray + rx_descriptors; 235062306a36Sopenharmony_ci 235162306a36Sopenharmony_ci return card; 235262306a36Sopenharmony_ci} 235362306a36Sopenharmony_ci 235462306a36Sopenharmony_ci/** 235562306a36Sopenharmony_ci * spider_net_undo_pci_setup - releases PCI ressources 235662306a36Sopenharmony_ci * @card: card structure 235762306a36Sopenharmony_ci * 235862306a36Sopenharmony_ci * spider_net_undo_pci_setup releases the mapped regions 235962306a36Sopenharmony_ci */ 236062306a36Sopenharmony_cistatic void 236162306a36Sopenharmony_cispider_net_undo_pci_setup(struct spider_net_card *card) 236262306a36Sopenharmony_ci{ 236362306a36Sopenharmony_ci iounmap(card->regs); 236462306a36Sopenharmony_ci pci_release_regions(card->pdev); 236562306a36Sopenharmony_ci} 236662306a36Sopenharmony_ci 236762306a36Sopenharmony_ci/** 236862306a36Sopenharmony_ci * spider_net_setup_pci_dev - sets up the device in terms of PCI operations 236962306a36Sopenharmony_ci * @pdev: PCI device 237062306a36Sopenharmony_ci * 237162306a36Sopenharmony_ci * Returns the card structure or NULL if any errors occur 237262306a36Sopenharmony_ci * 237362306a36Sopenharmony_ci * spider_net_setup_pci_dev initializes pdev and together with the 237462306a36Sopenharmony_ci * functions called in spider_net_open configures the device so that 237562306a36Sopenharmony_ci * data can be transferred over it 237662306a36Sopenharmony_ci * The net_device structure is attached to the card structure, if the 237762306a36Sopenharmony_ci * function returns without error. 237862306a36Sopenharmony_ci **/ 237962306a36Sopenharmony_cistatic struct spider_net_card * 238062306a36Sopenharmony_cispider_net_setup_pci_dev(struct pci_dev *pdev) 238162306a36Sopenharmony_ci{ 238262306a36Sopenharmony_ci struct spider_net_card *card; 238362306a36Sopenharmony_ci unsigned long mmio_start, mmio_len; 238462306a36Sopenharmony_ci 238562306a36Sopenharmony_ci if (pci_enable_device(pdev)) { 238662306a36Sopenharmony_ci dev_err(&pdev->dev, "Couldn't enable PCI device\n"); 238762306a36Sopenharmony_ci return NULL; 238862306a36Sopenharmony_ci } 238962306a36Sopenharmony_ci 239062306a36Sopenharmony_ci if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { 239162306a36Sopenharmony_ci dev_err(&pdev->dev, 239262306a36Sopenharmony_ci "Couldn't find proper PCI device base address.\n"); 239362306a36Sopenharmony_ci goto out_disable_dev; 239462306a36Sopenharmony_ci } 239562306a36Sopenharmony_ci 239662306a36Sopenharmony_ci if (pci_request_regions(pdev, spider_net_driver_name)) { 239762306a36Sopenharmony_ci dev_err(&pdev->dev, 239862306a36Sopenharmony_ci "Couldn't obtain PCI resources, aborting.\n"); 239962306a36Sopenharmony_ci goto out_disable_dev; 240062306a36Sopenharmony_ci } 240162306a36Sopenharmony_ci 240262306a36Sopenharmony_ci pci_set_master(pdev); 240362306a36Sopenharmony_ci 240462306a36Sopenharmony_ci card = spider_net_alloc_card(); 240562306a36Sopenharmony_ci if (!card) { 240662306a36Sopenharmony_ci dev_err(&pdev->dev, 240762306a36Sopenharmony_ci "Couldn't allocate net_device structure, aborting.\n"); 240862306a36Sopenharmony_ci goto out_release_regions; 240962306a36Sopenharmony_ci } 241062306a36Sopenharmony_ci card->pdev = pdev; 241162306a36Sopenharmony_ci 241262306a36Sopenharmony_ci /* fetch base address and length of first resource */ 241362306a36Sopenharmony_ci mmio_start = pci_resource_start(pdev, 0); 241462306a36Sopenharmony_ci mmio_len = pci_resource_len(pdev, 0); 241562306a36Sopenharmony_ci 241662306a36Sopenharmony_ci card->netdev->mem_start = mmio_start; 241762306a36Sopenharmony_ci card->netdev->mem_end = mmio_start + mmio_len; 241862306a36Sopenharmony_ci card->regs = ioremap(mmio_start, mmio_len); 241962306a36Sopenharmony_ci 242062306a36Sopenharmony_ci if (!card->regs) { 242162306a36Sopenharmony_ci dev_err(&pdev->dev, 242262306a36Sopenharmony_ci "Couldn't obtain PCI resources, aborting.\n"); 242362306a36Sopenharmony_ci goto out_release_regions; 242462306a36Sopenharmony_ci } 242562306a36Sopenharmony_ci 242662306a36Sopenharmony_ci return card; 242762306a36Sopenharmony_ci 242862306a36Sopenharmony_ciout_release_regions: 242962306a36Sopenharmony_ci pci_release_regions(pdev); 243062306a36Sopenharmony_ciout_disable_dev: 243162306a36Sopenharmony_ci pci_disable_device(pdev); 243262306a36Sopenharmony_ci return NULL; 243362306a36Sopenharmony_ci} 243462306a36Sopenharmony_ci 243562306a36Sopenharmony_ci/** 243662306a36Sopenharmony_ci * spider_net_probe - initialization of a device 243762306a36Sopenharmony_ci * @pdev: PCI device 243862306a36Sopenharmony_ci * @ent: entry in the device id list 243962306a36Sopenharmony_ci * 244062306a36Sopenharmony_ci * Returns 0 on success, <0 on failure 244162306a36Sopenharmony_ci * 244262306a36Sopenharmony_ci * spider_net_probe initializes pdev and registers a net_device 244362306a36Sopenharmony_ci * structure for it. After that, the device can be ifconfig'ed up 244462306a36Sopenharmony_ci **/ 244562306a36Sopenharmony_cistatic int 244662306a36Sopenharmony_cispider_net_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 244762306a36Sopenharmony_ci{ 244862306a36Sopenharmony_ci int err = -EIO; 244962306a36Sopenharmony_ci struct spider_net_card *card; 245062306a36Sopenharmony_ci 245162306a36Sopenharmony_ci card = spider_net_setup_pci_dev(pdev); 245262306a36Sopenharmony_ci if (!card) 245362306a36Sopenharmony_ci goto out; 245462306a36Sopenharmony_ci 245562306a36Sopenharmony_ci spider_net_workaround_rxramfull(card); 245662306a36Sopenharmony_ci spider_net_init_card(card); 245762306a36Sopenharmony_ci 245862306a36Sopenharmony_ci err = spider_net_setup_phy(card); 245962306a36Sopenharmony_ci if (err) 246062306a36Sopenharmony_ci goto out_undo_pci; 246162306a36Sopenharmony_ci 246262306a36Sopenharmony_ci err = spider_net_setup_netdev(card); 246362306a36Sopenharmony_ci if (err) 246462306a36Sopenharmony_ci goto out_undo_pci; 246562306a36Sopenharmony_ci 246662306a36Sopenharmony_ci return 0; 246762306a36Sopenharmony_ci 246862306a36Sopenharmony_ciout_undo_pci: 246962306a36Sopenharmony_ci spider_net_undo_pci_setup(card); 247062306a36Sopenharmony_ci free_netdev(card->netdev); 247162306a36Sopenharmony_ciout: 247262306a36Sopenharmony_ci return err; 247362306a36Sopenharmony_ci} 247462306a36Sopenharmony_ci 247562306a36Sopenharmony_ci/** 247662306a36Sopenharmony_ci * spider_net_remove - removal of a device 247762306a36Sopenharmony_ci * @pdev: PCI device 247862306a36Sopenharmony_ci * 247962306a36Sopenharmony_ci * Returns 0 on success, <0 on failure 248062306a36Sopenharmony_ci * 248162306a36Sopenharmony_ci * spider_net_remove is called to remove the device and unregisters the 248262306a36Sopenharmony_ci * net_device 248362306a36Sopenharmony_ci **/ 248462306a36Sopenharmony_cistatic void 248562306a36Sopenharmony_cispider_net_remove(struct pci_dev *pdev) 248662306a36Sopenharmony_ci{ 248762306a36Sopenharmony_ci struct net_device *netdev; 248862306a36Sopenharmony_ci struct spider_net_card *card; 248962306a36Sopenharmony_ci 249062306a36Sopenharmony_ci netdev = pci_get_drvdata(pdev); 249162306a36Sopenharmony_ci card = netdev_priv(netdev); 249262306a36Sopenharmony_ci 249362306a36Sopenharmony_ci wait_event(card->waitq, 249462306a36Sopenharmony_ci atomic_read(&card->tx_timeout_task_counter) == 0); 249562306a36Sopenharmony_ci 249662306a36Sopenharmony_ci unregister_netdev(netdev); 249762306a36Sopenharmony_ci 249862306a36Sopenharmony_ci /* switch off card */ 249962306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_CKRCTRL, 250062306a36Sopenharmony_ci SPIDER_NET_CKRCTRL_STOP_VALUE); 250162306a36Sopenharmony_ci spider_net_write_reg(card, SPIDER_NET_CKRCTRL, 250262306a36Sopenharmony_ci SPIDER_NET_CKRCTRL_RUN_VALUE); 250362306a36Sopenharmony_ci 250462306a36Sopenharmony_ci spider_net_undo_pci_setup(card); 250562306a36Sopenharmony_ci free_netdev(netdev); 250662306a36Sopenharmony_ci} 250762306a36Sopenharmony_ci 250862306a36Sopenharmony_cistatic struct pci_driver spider_net_driver = { 250962306a36Sopenharmony_ci .name = spider_net_driver_name, 251062306a36Sopenharmony_ci .id_table = spider_net_pci_tbl, 251162306a36Sopenharmony_ci .probe = spider_net_probe, 251262306a36Sopenharmony_ci .remove = spider_net_remove 251362306a36Sopenharmony_ci}; 251462306a36Sopenharmony_ci 251562306a36Sopenharmony_ci/** 251662306a36Sopenharmony_ci * spider_net_init - init function when the driver is loaded 251762306a36Sopenharmony_ci * 251862306a36Sopenharmony_ci * spider_net_init registers the device driver 251962306a36Sopenharmony_ci */ 252062306a36Sopenharmony_cistatic int __init spider_net_init(void) 252162306a36Sopenharmony_ci{ 252262306a36Sopenharmony_ci printk(KERN_INFO "Spidernet version %s.\n", VERSION); 252362306a36Sopenharmony_ci 252462306a36Sopenharmony_ci if (rx_descriptors < SPIDER_NET_RX_DESCRIPTORS_MIN) { 252562306a36Sopenharmony_ci rx_descriptors = SPIDER_NET_RX_DESCRIPTORS_MIN; 252662306a36Sopenharmony_ci pr_info("adjusting rx descriptors to %i.\n", rx_descriptors); 252762306a36Sopenharmony_ci } 252862306a36Sopenharmony_ci if (rx_descriptors > SPIDER_NET_RX_DESCRIPTORS_MAX) { 252962306a36Sopenharmony_ci rx_descriptors = SPIDER_NET_RX_DESCRIPTORS_MAX; 253062306a36Sopenharmony_ci pr_info("adjusting rx descriptors to %i.\n", rx_descriptors); 253162306a36Sopenharmony_ci } 253262306a36Sopenharmony_ci if (tx_descriptors < SPIDER_NET_TX_DESCRIPTORS_MIN) { 253362306a36Sopenharmony_ci tx_descriptors = SPIDER_NET_TX_DESCRIPTORS_MIN; 253462306a36Sopenharmony_ci pr_info("adjusting tx descriptors to %i.\n", tx_descriptors); 253562306a36Sopenharmony_ci } 253662306a36Sopenharmony_ci if (tx_descriptors > SPIDER_NET_TX_DESCRIPTORS_MAX) { 253762306a36Sopenharmony_ci tx_descriptors = SPIDER_NET_TX_DESCRIPTORS_MAX; 253862306a36Sopenharmony_ci pr_info("adjusting tx descriptors to %i.\n", tx_descriptors); 253962306a36Sopenharmony_ci } 254062306a36Sopenharmony_ci 254162306a36Sopenharmony_ci return pci_register_driver(&spider_net_driver); 254262306a36Sopenharmony_ci} 254362306a36Sopenharmony_ci 254462306a36Sopenharmony_ci/** 254562306a36Sopenharmony_ci * spider_net_cleanup - exit function when driver is unloaded 254662306a36Sopenharmony_ci * 254762306a36Sopenharmony_ci * spider_net_cleanup unregisters the device driver 254862306a36Sopenharmony_ci */ 254962306a36Sopenharmony_cistatic void __exit spider_net_cleanup(void) 255062306a36Sopenharmony_ci{ 255162306a36Sopenharmony_ci pci_unregister_driver(&spider_net_driver); 255262306a36Sopenharmony_ci} 255362306a36Sopenharmony_ci 255462306a36Sopenharmony_cimodule_init(spider_net_init); 255562306a36Sopenharmony_cimodule_exit(spider_net_cleanup); 2556