📄 spider_net.c
字号:
/* * Network device driver for Cell Processor-Based Blade and Celleb platform * * (C) Copyright IBM Corp. 2005 * (C) Copyright 2006 TOSHIBA CORPORATION * * Authors : Utz Bacher <utz.bacher@de.ibm.com> * Jens Osterkamp <Jens.Osterkamp@de.ibm.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */#include <linux/compiler.h>#include <linux/crc32.h>#include <linux/delay.h>#include <linux/etherdevice.h>#include <linux/ethtool.h>#include <linux/firmware.h>#include <linux/if_vlan.h>#include <linux/in.h>#include <linux/init.h>#include <linux/ioport.h>#include <linux/ip.h>#include <linux/kernel.h>#include <linux/mii.h>#include <linux/module.h>#include <linux/netdevice.h>#include <linux/device.h>#include <linux/pci.h>#include <linux/skbuff.h>#include <linux/slab.h>#include <linux/tcp.h>#include <linux/types.h>#include <linux/vmalloc.h>#include <linux/wait.h>#include <linux/workqueue.h>#include <linux/bitops.h>#include <asm/pci-bridge.h>#include <net/checksum.h>#include "spider_net.h"MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com> and Jens Osterkamp " \ "<Jens.Osterkamp@de.ibm.com>");MODULE_DESCRIPTION("Spider Southbridge Gigabit Ethernet driver");MODULE_LICENSE("GPL");MODULE_VERSION(VERSION);static int rx_descriptors = SPIDER_NET_RX_DESCRIPTORS_DEFAULT;static int tx_descriptors = SPIDER_NET_TX_DESCRIPTORS_DEFAULT;module_param(rx_descriptors, int, 0444);module_param(tx_descriptors, int, 0444);MODULE_PARM_DESC(rx_descriptors, "number of descriptors used " \ "in rx chains");MODULE_PARM_DESC(tx_descriptors, "number of descriptors used " \ "in tx chain");char spider_net_driver_name[] = "spidernet";static struct pci_device_id spider_net_pci_tbl[] = { { PCI_VENDOR_ID_TOSHIBA_2, PCI_DEVICE_ID_TOSHIBA_SPIDER_NET, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0UL }, { 0, }};MODULE_DEVICE_TABLE(pci, spider_net_pci_tbl);/** * spider_net_read_reg - reads an SMMIO register of a card * @card: device structure * @reg: register to read from * * returns the content of the specified SMMIO register. */static inline u32spider_net_read_reg(struct spider_net_card *card, u32 reg){ /* We use the powerpc specific variants instead of readl_be() because * we know spidernet is not a real PCI device and we can thus avoid the * performance hit caused by the PCI workarounds. */ return in_be32(card->regs + reg);}/** * spider_net_write_reg - writes to an SMMIO register of a card * @card: device structure * @reg: register to write to * @value: value to write into the specified SMMIO register */static inline voidspider_net_write_reg(struct spider_net_card *card, u32 reg, u32 value){ /* We use the powerpc specific variants instead of writel_be() because * we know spidernet is not a real PCI device and we can thus avoid the * performance hit caused by the PCI workarounds. */ out_be32(card->regs + reg, value);}/** spider_net_write_phy - write to phy register * @netdev: adapter to be written to * @mii_id: id of MII * @reg: PHY register * @val: value to be written to phy register * * spider_net_write_phy_register writes to an arbitrary PHY * register via the spider GPCWOPCMD register. We assume the queue does * not run full (not more than 15 commands outstanding). **/static voidspider_net_write_phy(struct net_device *netdev, int mii_id, int reg, int val){ struct spider_net_card *card = netdev_priv(netdev); u32 writevalue; writevalue = ((u32)mii_id << 21) | ((u32)reg << 16) | ((u32)val); spider_net_write_reg(card, SPIDER_NET_GPCWOPCMD, writevalue);}/** spider_net_read_phy - read from phy register * @netdev: network device to be read from * @mii_id: id of MII * @reg: PHY register * * Returns value read from PHY register * * spider_net_write_phy reads from an arbitrary PHY * register via the spider GPCROPCMD register **/static intspider_net_read_phy(struct net_device *netdev, int mii_id, int reg){ struct spider_net_card *card = netdev_priv(netdev); u32 readvalue; readvalue = ((u32)mii_id << 21) | ((u32)reg << 16); spider_net_write_reg(card, SPIDER_NET_GPCROPCMD, readvalue); /* we don't use semaphores to wait for an SPIDER_NET_GPROPCMPINT * interrupt, as we poll for the completion of the read operation * in spider_net_read_phy. Should take about 50 us */ do { readvalue = spider_net_read_reg(card, SPIDER_NET_GPCROPCMD); } while (readvalue & SPIDER_NET_GPREXEC); readvalue &= SPIDER_NET_GPRDAT_MASK; return readvalue;}/** * spider_net_setup_aneg - initial auto-negotiation setup * @card: device structure **/static voidspider_net_setup_aneg(struct spider_net_card *card){ struct mii_phy *phy = &card->phy; u32 advertise = 0; u16 bmsr, estat; bmsr = spider_net_read_phy(card->netdev, phy->mii_id, MII_BMSR); estat = spider_net_read_phy(card->netdev, phy->mii_id, MII_ESTATUS); if (bmsr & BMSR_10HALF) advertise |= ADVERTISED_10baseT_Half; if (bmsr & BMSR_10FULL) advertise |= ADVERTISED_10baseT_Full; if (bmsr & BMSR_100HALF) advertise |= ADVERTISED_100baseT_Half; if (bmsr & BMSR_100FULL) advertise |= ADVERTISED_100baseT_Full; if ((bmsr & BMSR_ESTATEN) && (estat & ESTATUS_1000_TFULL)) advertise |= SUPPORTED_1000baseT_Full; if ((bmsr & BMSR_ESTATEN) && (estat & ESTATUS_1000_THALF)) advertise |= SUPPORTED_1000baseT_Half; mii_phy_probe(phy, phy->mii_id); phy->def->ops->setup_aneg(phy, advertise);}/** * spider_net_rx_irq_off - switch off rx irq on this spider card * @card: device structure * * switches off rx irq by masking them out in the GHIINTnMSK register */static voidspider_net_rx_irq_off(struct spider_net_card *card){ u32 regvalue; regvalue = SPIDER_NET_INT0_MASK_VALUE & (~SPIDER_NET_RXINT); spider_net_write_reg(card, SPIDER_NET_GHIINT0MSK, regvalue);}/** * spider_net_rx_irq_on - switch on rx irq on this spider card * @card: device structure * * switches on rx irq by enabling them in the GHIINTnMSK register */static voidspider_net_rx_irq_on(struct spider_net_card *card){ u32 regvalue; regvalue = SPIDER_NET_INT0_MASK_VALUE | SPIDER_NET_RXINT; spider_net_write_reg(card, SPIDER_NET_GHIINT0MSK, regvalue);}/** * spider_net_set_promisc - sets the unicast address or the promiscuous mode * @card: card structure * * spider_net_set_promisc sets the unicast destination address filter and * thus either allows for non-promisc mode or promisc mode */static voidspider_net_set_promisc(struct spider_net_card *card){ u32 macu, macl; struct net_device *netdev = card->netdev; if (netdev->flags & IFF_PROMISC) { /* clear destination entry 0 */ spider_net_write_reg(card, SPIDER_NET_GMRUAFILnR, 0); spider_net_write_reg(card, SPIDER_NET_GMRUAFILnR + 0x04, 0); spider_net_write_reg(card, SPIDER_NET_GMRUA0FIL15R, SPIDER_NET_PROMISC_VALUE); } else { macu = netdev->dev_addr[0]; macu <<= 8; macu |= netdev->dev_addr[1]; memcpy(&macl, &netdev->dev_addr[2], sizeof(macl)); macu |= SPIDER_NET_UA_DESCR_VALUE; spider_net_write_reg(card, SPIDER_NET_GMRUAFILnR, macu); spider_net_write_reg(card, SPIDER_NET_GMRUAFILnR + 0x04, macl); spider_net_write_reg(card, SPIDER_NET_GMRUA0FIL15R, SPIDER_NET_NONPROMISC_VALUE); }}/** * spider_net_get_mac_address - read mac address from spider card * @card: device structure * * reads MAC address from GMACUNIMACU and GMACUNIMACL registers */static intspider_net_get_mac_address(struct net_device *netdev){ struct spider_net_card *card = netdev_priv(netdev); u32 macl, macu; macl = spider_net_read_reg(card, SPIDER_NET_GMACUNIMACL); macu = spider_net_read_reg(card, SPIDER_NET_GMACUNIMACU); netdev->dev_addr[0] = (macu >> 24) & 0xff; netdev->dev_addr[1] = (macu >> 16) & 0xff; netdev->dev_addr[2] = (macu >> 8) & 0xff; netdev->dev_addr[3] = macu & 0xff; netdev->dev_addr[4] = (macl >> 8) & 0xff; netdev->dev_addr[5] = macl & 0xff; if (!is_valid_ether_addr(&netdev->dev_addr[0])) return -EINVAL; return 0;}/** * spider_net_get_descr_status -- returns the status of a descriptor * @descr: descriptor to look at * * returns the status as in the dmac_cmd_status field of the descriptor */static inline intspider_net_get_descr_status(struct spider_net_hw_descr *hwdescr){ return hwdescr->dmac_cmd_status & SPIDER_NET_DESCR_IND_PROC_MASK;}/** * spider_net_free_chain - free descriptor chain * @card: card structure * @chain: address of chain * */static voidspider_net_free_chain(struct spider_net_card *card, struct spider_net_descr_chain *chain){ struct spider_net_descr *descr; descr = chain->ring; do { descr->bus_addr = 0; descr->hwdescr->next_descr_addr = 0; descr = descr->next; } while (descr != chain->ring); dma_free_coherent(&card->pdev->dev, chain->num_desc, chain->hwring, chain->dma_addr);}/** * spider_net_init_chain - alloc and link descriptor chain * @card: card structure * @chain: address of chain * * We manage a circular list that mirrors the hardware structure, * except that the hardware uses bus addresses. * * Returns 0 on success, <0 on failure */static intspider_net_init_chain(struct spider_net_card *card, struct spider_net_descr_chain *chain){ int i; struct spider_net_descr *descr; struct spider_net_hw_descr *hwdescr; dma_addr_t buf; size_t alloc_size; alloc_size = chain->num_desc * sizeof(struct spider_net_hw_descr); chain->hwring = dma_alloc_coherent(&card->pdev->dev, alloc_size, &chain->dma_addr, GFP_KERNEL); if (!chain->hwring) return -ENOMEM; memset(chain->ring, 0, chain->num_desc * sizeof(struct spider_net_descr)); /* Set up the hardware pointers in each descriptor */ descr = chain->ring; hwdescr = chain->hwring; buf = chain->dma_addr; for (i=0; i < chain->num_desc; i++, descr++, hwdescr++) { hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; hwdescr->next_descr_addr = 0; descr->hwdescr = hwdescr; descr->bus_addr = buf; descr->next = descr + 1; descr->prev = descr - 1; buf += sizeof(struct spider_net_hw_descr); } /* do actual circular list */ (descr-1)->next = chain->ring; chain->ring->prev = descr-1; spin_lock_init(&chain->lock); chain->head = chain->ring; chain->tail = chain->ring; return 0;}/** * spider_net_free_rx_chain_contents - frees descr contents in rx chain * @card: card structure * * returns 0 on success, <0 on failure */static voidspider_net_free_rx_chain_contents(struct spider_net_card *card){ struct spider_net_descr *descr; descr = card->rx_chain.head; do { if (descr->skb) { pci_unmap_single(card->pdev, descr->hwdescr->buf_addr, SPIDER_NET_MAX_FRAME, PCI_DMA_BIDIRECTIONAL); dev_kfree_skb(descr->skb); descr->skb = NULL; } descr = descr->next; } while (descr != card->rx_chain.head);}/** * spider_net_prepare_rx_descr - Reinitialize RX descriptor * @card: card structure * @descr: descriptor to re-init * * Return 0 on succes, <0 on failure. * * Allocates a new rx skb, iommu-maps it and attaches it to the * descriptor. Mark the descriptor as activated, ready-to-use. */static intspider_net_prepare_rx_descr(struct spider_net_card *card, struct spider_net_descr *descr){ struct spider_net_hw_descr *hwdescr = descr->hwdescr; dma_addr_t buf; int offset; int bufsize; /* we need to round up the buffer size to a multiple of 128 */ bufsize = (SPIDER_NET_MAX_FRAME + SPIDER_NET_RXBUF_ALIGN - 1) & (~(SPIDER_NET_RXBUF_ALIGN - 1)); /* and we need to have it 128 byte aligned, therefore we allocate a * bit more */ /* allocate an skb */ descr->skb = netdev_alloc_skb(card->netdev, bufsize + SPIDER_NET_RXBUF_ALIGN - 1); if (!descr->skb) { if (netif_msg_rx_err(card) && net_ratelimit()) dev_err(&card->netdev->dev, "Not enough memory to allocate rx buffer\n"); card->spider_stats.alloc_rx_skb_error++; return -ENOMEM; } hwdescr->buf_size = bufsize; hwdescr->result_size = 0; hwdescr->valid_size = 0; hwdescr->data_status = 0; hwdescr->data_error = 0; offset = ((unsigned long)descr->skb->data) & (SPIDER_NET_RXBUF_ALIGN - 1); if (offset) skb_reserve(descr->skb, SPIDER_NET_RXBUF_ALIGN - offset); /* iommu-map the skb */ buf = pci_map_single(card->pdev, descr->skb->data, SPIDER_NET_MAX_FRAME, PCI_DMA_FROMDEVICE); if (pci_dma_mapping_error(buf)) { dev_kfree_skb_any(descr->skb); descr->skb = NULL; if (netif_msg_rx_err(card) && net_ratelimit()) dev_err(&card->netdev->dev, "Could not iommu-map rx buffer\n"); card->spider_stats.rx_iommu_map_error++; hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_NOT_IN_USE; } else { hwdescr->buf_addr = buf; wmb(); hwdescr->dmac_cmd_status = SPIDER_NET_DESCR_CARDOWNED | SPIDER_NET_DMAC_NOINTR_COMPLETE; } return 0;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -