📄 3c359.c
字号:
/* * 3c359.c (c) 2000 Mike Phillips (mikep@linuxtr.net) All Rights Reserved * * Linux driver for 3Com 3c359 Tokenlink Velocity XL PCI NIC * * Base Driver Olympic: * Written 1999 Peter De Schrijver & Mike Phillips * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * * 7/17/00 - Clean up, version number 0.9.0. Ready to release to the world. * * 2/16/01 - Port up to kernel 2.4.2 ready for submission into the kernel. * 3/05/01 - Last clean up stuff before submission. * 2/15/01 - Finally, update to new pci api. * * To Do: *//* * Technical Card Details * * All access to data is done with 16/8 bit transfers. The transfer * method really sucks. You can only read or write one location at a time. * * Also, the microcode for the card must be uploaded if the card does not have * the flashrom on board. This is a 28K bloat in the driver when compiled * as a module. * * Rx is very simple, status into a ring of descriptors, dma data transfer, * interrupts to tell us when a packet is received. * * Tx is a little more interesting. Similar scenario, descriptor and dma data * transfers, but we don't have to interrupt the card to tell it another packet * is ready for transmission, we are just doing simple memory writes, not io or mmio * writes. The card can be set up to simply poll on the next * descriptor pointer and when this value is non-zero will automatically download * the next packet. The card then interrupts us when the packet is done. * */#define XL_DEBUG 0#include <linux/config.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/errno.h>#include <linux/timer.h>#include <linux/in.h>#include <linux/ioport.h>#include <linux/string.h>#include <linux/proc_fs.h>#include <linux/ptrace.h>#include <linux/skbuff.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/netdevice.h>#include <linux/trdevice.h>#include <linux/stddef.h>#include <linux/init.h>#include <linux/pci.h>#include <linux/spinlock.h>#include <linux/bitops.h>#include <net/checksum.h>#include <asm/io.h>#include <asm/system.h>#include "3c359.h"static char version[] __devinitdata = "3c359.c v1.2.0 2/17/01 - Mike Phillips (mikep@linuxtr.net)" ; MODULE_AUTHOR("Mike Phillips <mikep@linuxtr.net>") ; MODULE_DESCRIPTION("3Com 3C359 Velocity XL Token Ring Adapter Driver \n") ;/* Module paramters *//* Ring Speed 0,4,16 * 0 = Autosense * 4,16 = Selected speed only, no autosense * This allows the card to be the first on the ring * and become the active monitor. * * WARNING: Some hubs will allow you to insert * at the wrong speed. * * The adapter will _not_ fail to open if there are no * active monitors on the ring, it will simply open up in * its last known ringspeed if no ringspeed is specified. */static int ringspeed[XL_MAX_ADAPTERS] = {0,} ;module_param_array(ringspeed, int, NULL, 0);MODULE_PARM_DESC(ringspeed,"3c359: Ringspeed selection - 4,16 or 0") ; /* Packet buffer size */static int pkt_buf_sz[XL_MAX_ADAPTERS] = {0,} ; module_param_array(pkt_buf_sz, int, NULL, 0) ;MODULE_PARM_DESC(pkt_buf_sz,"3c359: Initial buffer size") ; /* Message Level */static int message_level[XL_MAX_ADAPTERS] = {0,} ; module_param_array(message_level, int, NULL, 0) ;MODULE_PARM_DESC(message_level, "3c359: Level of reported messages \n") ; /* * This is a real nasty way of doing this, but otherwise you * will be stuck with 1555 lines of hex #'s in the code. */#include "3c359_microcode.h" static struct pci_device_id xl_pci_tbl[] ={ {PCI_VENDOR_ID_3COM,PCI_DEVICE_ID_3COM_3C359, PCI_ANY_ID, PCI_ANY_ID, }, { } /* terminate list */};MODULE_DEVICE_TABLE(pci,xl_pci_tbl) ; static int xl_init(struct net_device *dev);static int xl_open(struct net_device *dev);static int xl_open_hw(struct net_device *dev) ; static int xl_hw_reset(struct net_device *dev); static int xl_xmit(struct sk_buff *skb, struct net_device *dev);static void xl_dn_comp(struct net_device *dev); static int xl_close(struct net_device *dev);static void xl_set_rx_mode(struct net_device *dev);static irqreturn_t xl_interrupt(int irq, void *dev_id, struct pt_regs *regs);static struct net_device_stats * xl_get_stats(struct net_device *dev);static int xl_set_mac_address(struct net_device *dev, void *addr) ; static void xl_arb_cmd(struct net_device *dev);static void xl_asb_cmd(struct net_device *dev) ; static void xl_srb_cmd(struct net_device *dev, int srb_cmd) ; static void xl_wait_misr_flags(struct net_device *dev) ; static int xl_change_mtu(struct net_device *dev, int mtu);static void xl_srb_bh(struct net_device *dev) ; static void xl_asb_bh(struct net_device *dev) ; static void xl_reset(struct net_device *dev) ; static void xl_freemem(struct net_device *dev) ; /* EEProm Access Functions */static u16 xl_ee_read(struct net_device *dev, int ee_addr) ; static void xl_ee_write(struct net_device *dev, int ee_addr, u16 ee_value) ; /* Debugging functions */#if XL_DEBUGstatic void print_tx_state(struct net_device *dev) ; static void print_rx_state(struct net_device *dev) ; static void print_tx_state(struct net_device *dev){ struct xl_private *xl_priv = (struct xl_private *)dev->priv ; struct xl_tx_desc *txd ; u8 __iomem *xl_mmio = xl_priv->xl_mmio ; int i ; printk("tx_ring_head: %d, tx_ring_tail: %d, free_ent: %d \n",xl_priv->tx_ring_head, xl_priv->tx_ring_tail, xl_priv->free_ring_entries) ; printk("Ring , Address , FSH , DnNextPtr, Buffer, Buffer_Len \n"); for (i = 0; i < 16; i++) { txd = &(xl_priv->xl_tx_ring[i]) ; printk("%d, %08lx, %08x, %08x, %08x, %08x \n", i, virt_to_bus(txd), txd->framestartheader, txd->dnnextptr, txd->buffer, txd->buffer_length ) ; } printk("DNLISTPTR = %04x \n", readl(xl_mmio + MMIO_DNLISTPTR) ); printk("DmaCtl = %04x \n", readl(xl_mmio + MMIO_DMA_CTRL) ); printk("Queue status = %0x \n",netif_running(dev) ) ; }static void print_rx_state(struct net_device *dev){ struct xl_private *xl_priv = (struct xl_private *)dev->priv ; struct xl_rx_desc *rxd ; u8 __iomem *xl_mmio = xl_priv->xl_mmio ; int i ; printk("rx_ring_tail: %d \n", xl_priv->rx_ring_tail) ; printk("Ring , Address , FrameState , UPNextPtr, FragAddr, Frag_Len \n"); for (i = 0; i < 16; i++) { /* rxd = (struct xl_rx_desc *)xl_priv->rx_ring_dma_addr + (i * sizeof(struct xl_rx_desc)) ; */ rxd = &(xl_priv->xl_rx_ring[i]) ; printk("%d, %08lx, %08x, %08x, %08x, %08x \n", i, virt_to_bus(rxd), rxd->framestatus, rxd->upnextptr, rxd->upfragaddr, rxd->upfraglen ) ; } printk("UPLISTPTR = %04x \n", readl(xl_mmio + MMIO_UPLISTPTR) ); printk("DmaCtl = %04x \n", readl(xl_mmio + MMIO_DMA_CTRL) ); printk("Queue status = %0x \n",netif_running(dev) ) ;} #endif/* * Read values from the on-board EEProm. This looks very strange * but you have to wait for the EEProm to get/set the value before * passing/getting the next value from the nic. As with all requests * on this nic it has to be done in two stages, a) tell the nic which * memory address you want to access and b) pass/get the value from the nic. * With the EEProm, you have to wait before and inbetween access a) and b). * As this is only read at initialization time and the wait period is very * small we shouldn't have to worry about scheduling issues. */static u16 xl_ee_read(struct net_device *dev, int ee_addr){ struct xl_private *xl_priv = (struct xl_private *)dev->priv ; u8 __iomem *xl_mmio = xl_priv->xl_mmio ; /* Wait for EEProm to not be busy */ writel(IO_WORD_READ | EECONTROL, xl_mmio + MMIO_MAC_ACCESS_CMD) ; while ( readw(xl_mmio + MMIO_MACDATA) & EEBUSY ) ; /* Tell EEProm what we want to do and where */ writel(IO_WORD_WRITE | EECONTROL, xl_mmio + MMIO_MAC_ACCESS_CMD) ; writew(EEREAD + ee_addr, xl_mmio + MMIO_MACDATA) ; /* Wait for EEProm to not be busy */ writel(IO_WORD_READ | EECONTROL, xl_mmio + MMIO_MAC_ACCESS_CMD) ; while ( readw(xl_mmio + MMIO_MACDATA) & EEBUSY ) ; /* Tell EEProm what we want to do and where */ writel(IO_WORD_WRITE | EECONTROL , xl_mmio + MMIO_MAC_ACCESS_CMD) ; writew(EEREAD + ee_addr, xl_mmio + MMIO_MACDATA) ; /* Finally read the value from the EEProm */ writel(IO_WORD_READ | EEDATA , xl_mmio + MMIO_MAC_ACCESS_CMD) ; return readw(xl_mmio + MMIO_MACDATA) ; }/* * Write values to the onboard eeprom. As with eeprom read you need to * set which location to write, wait, value to write, wait, with the * added twist of having to enable eeprom writes as well. */static void xl_ee_write(struct net_device *dev, int ee_addr, u16 ee_value) { struct xl_private *xl_priv = (struct xl_private *)dev->priv ; u8 __iomem *xl_mmio = xl_priv->xl_mmio ; /* Wait for EEProm to not be busy */ writel(IO_WORD_READ | EECONTROL, xl_mmio + MMIO_MAC_ACCESS_CMD) ; while ( readw(xl_mmio + MMIO_MACDATA) & EEBUSY ) ; /* Enable write/erase */ writel(IO_WORD_WRITE | EECONTROL, xl_mmio + MMIO_MAC_ACCESS_CMD) ; writew(EE_ENABLE_WRITE, xl_mmio + MMIO_MACDATA) ; /* Wait for EEProm to not be busy */ writel(IO_WORD_READ | EECONTROL, xl_mmio + MMIO_MAC_ACCESS_CMD) ; while ( readw(xl_mmio + MMIO_MACDATA) & EEBUSY ) ; /* Put the value we want to write into EEDATA */ writel(IO_WORD_WRITE | EEDATA, xl_mmio + MMIO_MAC_ACCESS_CMD) ; writew(ee_value, xl_mmio + MMIO_MACDATA) ; /* Tell EEProm to write eevalue into ee_addr */ writel(IO_WORD_WRITE | EECONTROL, xl_mmio + MMIO_MAC_ACCESS_CMD) ; writew(EEWRITE + ee_addr, xl_mmio + MMIO_MACDATA) ; /* Wait for EEProm to not be busy, to ensure write gets done */ writel(IO_WORD_READ | EECONTROL, xl_mmio + MMIO_MAC_ACCESS_CMD) ; while ( readw(xl_mmio + MMIO_MACDATA) & EEBUSY ) ; return ; } static int __devinit xl_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct net_device *dev ; struct xl_private *xl_priv ; static int card_no = -1 ; int i ; card_no++ ; if (pci_enable_device(pdev)) { return -ENODEV ; } pci_set_master(pdev); if ((i = pci_request_regions(pdev,"3c359"))) { return i ; } ; /* * Allowing init_trdev to allocate the dev->priv structure will align xl_private * on a 32 bytes boundary which we need for the rx/tx descriptors */ dev = alloc_trdev(sizeof(struct xl_private)) ; if (!dev) { pci_release_regions(pdev) ; return -ENOMEM ; } xl_priv = dev->priv ; #if XL_DEBUG printk("pci_device: %p, dev:%p, dev->priv: %p, ba[0]: %10x, ba[1]:%10x\n", pdev, dev, dev->priv, (unsigned int)pdev->resource[0].start, (unsigned int)pdev->resource[1].start) ; #endif dev->irq=pdev->irq; dev->base_addr=pci_resource_start(pdev,0) ; xl_priv->xl_card_name = pci_name(pdev); xl_priv->xl_mmio=ioremap(pci_resource_start(pdev,1), XL_IO_SPACE); xl_priv->pdev = pdev ; if ((pkt_buf_sz[card_no] < 100) || (pkt_buf_sz[card_no] > 18000) ) xl_priv->pkt_buf_sz = PKT_BUF_SZ ; else xl_priv->pkt_buf_sz = pkt_buf_sz[card_no] ; dev->mtu = xl_priv->pkt_buf_sz - TR_HLEN ; xl_priv->xl_ring_speed = ringspeed[card_no] ; xl_priv->xl_message_level = message_level[card_no] ; xl_priv->xl_functional_addr[0] = xl_priv->xl_functional_addr[1] = xl_priv->xl_functional_addr[2] = xl_priv->xl_functional_addr[3] = 0 ; xl_priv->xl_copy_all_options = 0 ; if((i = xl_init(dev))) { iounmap(xl_priv->xl_mmio) ; free_netdev(dev) ; pci_release_regions(pdev) ; return i ; } dev->open=&xl_open; dev->hard_start_xmit=&xl_xmit; dev->change_mtu=&xl_change_mtu; dev->stop=&xl_close; dev->do_ioctl=NULL; dev->set_multicast_list=&xl_set_rx_mode; dev->get_stats=&xl_get_stats ; dev->set_mac_address=&xl_set_mac_address ; SET_MODULE_OWNER(dev); SET_NETDEV_DEV(dev, &pdev->dev); pci_set_drvdata(pdev,dev) ; if ((i = register_netdev(dev))) { printk(KERN_ERR "3C359, register netdev failed\n") ; pci_set_drvdata(pdev,NULL) ; iounmap(xl_priv->xl_mmio) ; free_netdev(dev) ; pci_release_regions(pdev) ; return i ; } printk(KERN_INFO "3C359: %s registered as: %s\n",xl_priv->xl_card_name,dev->name) ; return 0; }static int __init xl_init(struct net_device *dev) { struct xl_private *xl_priv = (struct xl_private *)dev->priv ; printk(KERN_INFO "%s \n", version); printk(KERN_INFO "%s: I/O at %hx, MMIO at %p, using irq %d\n", xl_priv->xl_card_name, (unsigned int)dev->base_addr ,xl_priv->xl_mmio, dev->irq); spin_lock_init(&xl_priv->xl_lock) ; return xl_hw_reset(dev) ; }/* * Hardware reset. This needs to be a separate entity as we need to reset the card * when we change the EEProm settings. */static int xl_hw_reset(struct net_device *dev) { struct xl_private *xl_priv = (struct xl_private *)dev->priv ; u8 __iomem *xl_mmio = xl_priv->xl_mmio ; unsigned long t ; u16 i ; u16 result_16 ; u8 result_8 ; u16 start ; int j ; /* * Reset the card. If the card has got the microcode on board, we have * missed the initialization interrupt, so we must always do this. */ writew( GLOBAL_RESET, xl_mmio + MMIO_COMMAND ) ; /* * Must wait for cmdInProgress bit (12) to clear before continuing with * card configuration. */ t=jiffies; while (readw(xl_mmio + MMIO_INTSTATUS) & INTSTAT_CMD_IN_PROGRESS) { schedule(); if(jiffies-t > 40*HZ) { printk(KERN_ERR "%s: 3COM 3C359 Velocity XL card not responding to global reset.\n", dev->name); return -ENODEV; } } /* * Enable pmbar by setting bit in CPAttention */ writel( (IO_BYTE_READ | CPATTENTION), xl_mmio + MMIO_MAC_ACCESS_CMD) ; result_8 = readb(xl_mmio + MMIO_MACDATA) ; result_8 = result_8 | CPA_PMBARVIS ; writel( (IO_BYTE_WRITE | CPATTENTION), xl_mmio + MMIO_MAC_ACCESS_CMD) ; writeb(result_8, xl_mmio + MMIO_MACDATA) ; /* * Read cpHold bit in pmbar, if cleared we have got Flashrom on board. * If not, we need to upload the microcode to the card */ writel( (IO_WORD_READ | PMBAR),xl_mmio + MMIO_MAC_ACCESS_CMD); #if XL_DEBUG printk(KERN_INFO "Read from PMBAR = %04x \n", readw(xl_mmio + MMIO_MACDATA)) ; #endif if ( readw( (xl_mmio + MMIO_MACDATA)) & PMB_CPHOLD ) { /* Set PmBar, privateMemoryBase bits (8:2) to 0 */ writel( (IO_WORD_READ | PMBAR),xl_mmio + MMIO_MAC_ACCESS_CMD); result_16 = readw(xl_mmio + MMIO_MACDATA) ; result_16 = result_16 & ~((0x7F) << 2) ; writel( (IO_WORD_WRITE | PMBAR), xl_mmio + MMIO_MAC_ACCESS_CMD) ; writew(result_16,xl_mmio + MMIO_MACDATA) ; /* Set CPAttention, memWrEn bit */ writel( (IO_BYTE_READ | CPATTENTION), xl_mmio + MMIO_MAC_ACCESS_CMD) ; result_8 = readb(xl_mmio + MMIO_MACDATA) ; result_8 = result_8 | CPA_MEMWREN ; writel( (IO_BYTE_WRITE | CPATTENTION), xl_mmio + MMIO_MAC_ACCESS_CMD) ; writeb(result_8, xl_mmio + MMIO_MACDATA) ; /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -