📄 tms380tr.c
字号:
/* * tms380tr.c: A network driver library for Texas Instruments TMS380-based * Token Ring Adapters. * * Originally sktr.c: Written 1997 by Christoph Goos * * A fine result of the Linux Systems Network Architecture Project. * http://www.linux-sna.org * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * * The following modules are currently available for card support: * - tmspci (Generic PCI card support) * - abyss (Madge PCI support) * - tmsisa (SysKonnect TR4/16 ISA) * * Sources: * - The hardware related parts of this driver are take from * the SysKonnect Token Ring driver for Windows NT. * - I used the IBM Token Ring driver 'ibmtr.c' as a base for this * driver, as well as the 'skeleton.c' driver by Donald Becker. * - Also various other drivers in the linux source tree were taken * as samples for some tasks. * - TI TMS380 Second-Generation Token Ring User's Guide * - TI datasheets for respective chips * - David Hein at Texas Instruments * - Various Madge employees * * Maintainer(s): * JS Jay Schulist jschlst@samba.org * CG Christoph Goos cgoos@syskonnect.de * AF Adam Fritzler mid@auk.cx * MLP Mike Phillips phillim@amtrak.com * JF Jochen Friedrich jochen@scram.de * * Modification History: * 29-Aug-97 CG Created * 04-Apr-98 CG Fixed problems caused by tok_timer_check * 10-Apr-98 CG Fixed lockups at cable disconnection * 27-May-98 JS Formated to Linux Kernel Format * 31-May-98 JS Hacked in PCI support * 16-Jun-98 JS Modulized for multiple cards with one driver * Sep-99 AF Renamed to tms380tr (supports more than SK's) * 23-Sep-99 AF Added Compaq and Thomas-Conrad PCI support * Fixed a bug causing double copies on PCI * Fixed for new multicast stuff (2.2/2.3) * 25-Sep-99 AF Uped TPL_NUM from 3 to 9 * Removed extraneous 'No free TPL' * 22-Dec-99 AF Added Madge PCI Mk2 support and generalized * parts of the initilization procedure. * 30-Dec-99 AF Turned tms380tr into a library ala 8390. * Madge support is provided in the abyss module * Generic PCI support is in the tmspci module. * 30-Nov-00 JF Updated PCI code to support IO MMU via * pci_map_static(). Alpha uses this MMU for ISA * as well. * 14-Jan-01 JF Fix DMA on ifdown/ifup sequences. Some * cleanup. * 13-Jan-02 JF Add spinlock to fix race condition. * 09-Nov-02 JF Fixed printks to not SPAM the console during * normal operation. * 30-Dec-02 JF Removed incorrect __init from * tms380tr_init_card. * 22-Jul-05 JF Converted to dma-mapping. * * To do: * 1. Multi/Broadcast packet handling (this may have fixed itself) * 2. Write a sktrisa module that includes the old ISA support (done) * 3. Allow modules to load their own microcode * 4. Speed up the BUD process -- freezing the kernel for 3+sec is * quite unacceptable. * 5. Still a few remaining stalls when the cable is unplugged. */#ifdef MODULEstatic const char version[] = "tms380tr.c: v1.10 30/12/2002 by Christoph Goos, Adam Fritzler\n";#endif#include <linux/module.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/fcntl.h>#include <linux/interrupt.h>#include <linux/ptrace.h>#include <linux/ioport.h>#include <linux/in.h>#include <linux/slab.h>#include <linux/string.h>#include <linux/time.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/dma-mapping.h>#include <linux/delay.h>#include <linux/netdevice.h>#include <linux/etherdevice.h>#include <linux/skbuff.h>#include <linux/trdevice.h>#include <linux/firmware.h>#include <linux/bitops.h>#include <asm/system.h>#include <asm/io.h>#include <asm/dma.h>#include <asm/irq.h>#include <asm/uaccess.h>#include "tms380tr.h" /* Our Stuff *//* Use 0 for production, 1 for verification, 2 for debug, and * 3 for very verbose debug. */#ifndef TMS380TR_DEBUG#define TMS380TR_DEBUG 0#endifstatic unsigned int tms380tr_debug = TMS380TR_DEBUG;/* Index to functions, as function prototypes. * Alphabetical by function name. *//* "A" *//* "B" */static int tms380tr_bringup_diags(struct net_device *dev);/* "C" */static void tms380tr_cancel_tx_queue(struct net_local* tp);static int tms380tr_chipset_init(struct net_device *dev);static void tms380tr_chk_irq(struct net_device *dev);static void tms380tr_chk_outstanding_cmds(struct net_device *dev);static void tms380tr_chk_src_addr(unsigned char *frame, unsigned char *hw_addr);static unsigned char tms380tr_chk_ssb(struct net_local *tp, unsigned short IrqType);int tms380tr_close(struct net_device *dev);static void tms380tr_cmd_status_irq(struct net_device *dev);/* "D" */static void tms380tr_disable_interrupts(struct net_device *dev);#if TMS380TR_DEBUG > 0static void tms380tr_dump(unsigned char *Data, int length);#endif/* "E" */static void tms380tr_enable_interrupts(struct net_device *dev);static void tms380tr_exec_cmd(struct net_device *dev, unsigned short Command);static void tms380tr_exec_sifcmd(struct net_device *dev, unsigned int WriteValue);/* "F" *//* "G" */static struct net_device_stats *tms380tr_get_stats(struct net_device *dev);/* "H" */static int tms380tr_hardware_send_packet(struct sk_buff *skb, struct net_device *dev);/* "I" */static int tms380tr_init_adapter(struct net_device *dev);static void tms380tr_init_ipb(struct net_local *tp);static void tms380tr_init_net_local(struct net_device *dev);static void tms380tr_init_opb(struct net_device *dev);/* "M" *//* "O" */int tms380tr_open(struct net_device *dev);static void tms380tr_open_adapter(struct net_device *dev);/* "P" *//* "R" */static void tms380tr_rcv_status_irq(struct net_device *dev);static int tms380tr_read_ptr(struct net_device *dev);static void tms380tr_read_ram(struct net_device *dev, unsigned char *Data, unsigned short Address, int Length);static int tms380tr_reset_adapter(struct net_device *dev);static void tms380tr_reset_interrupt(struct net_device *dev);static void tms380tr_ring_status_irq(struct net_device *dev);/* "S" */static int tms380tr_send_packet(struct sk_buff *skb, struct net_device *dev);static void tms380tr_set_multicast_list(struct net_device *dev);static int tms380tr_set_mac_address(struct net_device *dev, void *addr);/* "T" */static void tms380tr_timer_chk(unsigned long data);static void tms380tr_timer_end_wait(unsigned long data);static void tms380tr_tx_status_irq(struct net_device *dev);/* "U" */static void tms380tr_update_rcv_stats(struct net_local *tp, unsigned char DataPtr[], unsigned int Length);/* "W" */void tms380tr_wait(unsigned long time);static void tms380tr_write_rpl_status(RPL *rpl, unsigned int Status);static void tms380tr_write_tpl_status(TPL *tpl, unsigned int Status);#define SIFREADB(reg) (((struct net_local *)dev->priv)->sifreadb(dev, reg))#define SIFWRITEB(val, reg) (((struct net_local *)dev->priv)->sifwriteb(dev, val, reg))#define SIFREADW(reg) (((struct net_local *)dev->priv)->sifreadw(dev, reg))#define SIFWRITEW(val, reg) (((struct net_local *)dev->priv)->sifwritew(dev, val, reg))#if 0 /* TMS380TR_DEBUG > 0 */static int madgemc_sifprobe(struct net_device *dev){ unsigned char old, chk1, chk2; old = SIFREADB(SIFADR); /* Get the old SIFADR value */ chk1 = 0; /* Begin with check value 0 */ do { madgemc_setregpage(dev, 0); /* Write new SIFADR value */ SIFWRITEB(chk1, SIFADR); chk2 = SIFREADB(SIFADR); if (chk2 != chk1) return -1; madgemc_setregpage(dev, 1); /* Read, invert and write */ chk2 = SIFREADB(SIFADD); if (chk2 != chk1) return -1; madgemc_setregpage(dev, 0); chk2 ^= 0x0FE; SIFWRITEB(chk2, SIFADR); /* Read, invert and compare */ madgemc_setregpage(dev, 1); chk2 = SIFREADB(SIFADD); madgemc_setregpage(dev, 0); chk2 ^= 0x0FE; if(chk1 != chk2) return (-1); /* No adapter */ chk1 -= 2; } while(chk1 != 0); /* Repeat 128 times (all byte values) */ madgemc_setregpage(dev, 0); /* sanity */ /* Restore the SIFADR value */ SIFWRITEB(old, SIFADR); return (0);}#endif/* * Open/initialize the board. This is called sometime after * booting when the 'ifconfig' program is run. * * This routine should set everything up anew at each open, even * registers that "should" only need to be set once at boot, so that * there is non-reboot way to recover if something goes wrong. */int tms380tr_open(struct net_device *dev){ struct net_local *tp = netdev_priv(dev); int err; /* init the spinlock */ spin_lock_init(&tp->lock); init_timer(&tp->timer); /* Reset the hardware here. Don't forget to set the station address. */#ifdef CONFIG_ISA if(dev->dma > 0) { unsigned long flags=claim_dma_lock(); disable_dma(dev->dma); set_dma_mode(dev->dma, DMA_MODE_CASCADE); enable_dma(dev->dma); release_dma_lock(flags); }#endif err = tms380tr_chipset_init(dev); if(err) { printk(KERN_INFO "%s: Chipset initialization error\n", dev->name); return (-1); } tp->timer.expires = jiffies + 30*HZ; tp->timer.function = tms380tr_timer_end_wait; tp->timer.data = (unsigned long)dev; add_timer(&tp->timer); printk(KERN_DEBUG "%s: Adapter RAM size: %dK\n", dev->name, tms380tr_read_ptr(dev)); tms380tr_enable_interrupts(dev); tms380tr_open_adapter(dev); netif_start_queue(dev); /* Wait for interrupt from hardware. If interrupt does not come, * there will be a timeout from the timer. */ tp->Sleeping = 1; interruptible_sleep_on(&tp->wait_for_tok_int); del_timer(&tp->timer); /* If AdapterVirtOpenFlag is 1, the adapter is now open for use */ if(tp->AdapterVirtOpenFlag == 0) { tms380tr_disable_interrupts(dev); return (-1); } tp->StartTime = jiffies; /* Start function control timer */ tp->timer.expires = jiffies + 2*HZ; tp->timer.function = tms380tr_timer_chk; tp->timer.data = (unsigned long)dev; add_timer(&tp->timer); return (0);}/* * Timeout function while waiting for event */static void tms380tr_timer_end_wait(unsigned long data){ struct net_device *dev = (struct net_device*)data; struct net_local *tp = netdev_priv(dev); if(tp->Sleeping) { tp->Sleeping = 0; wake_up_interruptible(&tp->wait_for_tok_int); } return;}/* * Initialize the chipset */static int tms380tr_chipset_init(struct net_device *dev){ struct net_local *tp = netdev_priv(dev); int err; tms380tr_init_ipb(tp); tms380tr_init_opb(dev); tms380tr_init_net_local(dev); if(tms380tr_debug > 3) printk(KERN_DEBUG "%s: Resetting adapter...\n", dev->name); err = tms380tr_reset_adapter(dev); if(err < 0) return (-1); if(tms380tr_debug > 3) printk(KERN_DEBUG "%s: Bringup diags...\n", dev->name); err = tms380tr_bringup_diags(dev); if(err < 0) return (-1); if(tms380tr_debug > 3) printk(KERN_DEBUG "%s: Init adapter...\n", dev->name); err = tms380tr_init_adapter(dev); if(err < 0) return (-1); if(tms380tr_debug > 3) printk(KERN_DEBUG "%s: Done!\n", dev->name); return (0);}/* * Initializes the net_local structure. */static void tms380tr_init_net_local(struct net_device *dev){ struct net_local *tp = netdev_priv(dev); int i; dma_addr_t dmabuf; tp->scb.CMD = 0; tp->scb.Parm[0] = 0; tp->scb.Parm[1] = 0; tp->ssb.STS = 0; tp->ssb.Parm[0] = 0; tp->ssb.Parm[1] = 0; tp->ssb.Parm[2] = 0; tp->CMDqueue = 0; tp->AdapterOpenFlag = 0; tp->AdapterVirtOpenFlag = 0; tp->ScbInUse = 0; tp->OpenCommandIssued = 0; tp->ReOpenInProgress = 0; tp->HaltInProgress = 0; tp->TransmitHaltScheduled = 0; tp->LobeWireFaultLogged = 0; tp->LastOpenStatus = 0; tp->MaxPacketSize = DEFAULT_PACKET_SIZE; /* Create circular chain of transmit lists */ for (i = 0; i < TPL_NUM; i++) { tp->Tpl[i].NextTPLAddr = htonl(((char *)(&tp->Tpl[(i+1) % TPL_NUM]) - (char *)tp) + tp->dmabuffer); /* DMA buffer may be MMU driven */ tp->Tpl[i].Status = 0; tp->Tpl[i].FrameSize = 0; tp->Tpl[i].FragList[0].DataCount = 0; tp->Tpl[i].FragList[0].DataAddr = 0; tp->Tpl[i].NextTPLPtr = &tp->Tpl[(i+1) % TPL_NUM]; tp->Tpl[i].MData = NULL; tp->Tpl[i].TPLIndex = i; tp->Tpl[i].DMABuff = 0; tp->Tpl[i].BusyFlag = 0; } tp->TplFree = tp->TplBusy = &tp->Tpl[0]; /* Create circular chain of receive lists */ for (i = 0; i < RPL_NUM; i++) { tp->Rpl[i].NextRPLAddr = htonl(((char *)(&tp->Rpl[(i+1) % RPL_NUM]) - (char *)tp) + tp->dmabuffer); /* DMA buffer may be MMU driven */ tp->Rpl[i].Status = (RX_VALID | RX_START_FRAME | RX_END_FRAME | RX_FRAME_IRQ); tp->Rpl[i].FrameSize = 0; tp->Rpl[i].FragList[0].DataCount = cpu_to_be16((unsigned short)tp->MaxPacketSize); /* Alloc skb and point adapter to data area */ tp->Rpl[i].Skb = dev_alloc_skb(tp->MaxPacketSize); tp->Rpl[i].DMABuff = 0; /* skb == NULL ? then use local buffer */ if(tp->Rpl[i].Skb == NULL) { tp->Rpl[i].SkbStat = SKB_UNAVAILABLE; tp->Rpl[i].FragList[0].DataAddr = htonl(((char *)tp->LocalRxBuffers[i] - (char *)tp) + tp->dmabuffer); tp->Rpl[i].MData = tp->LocalRxBuffers[i]; } else /* SKB != NULL */ { tp->Rpl[i].Skb->dev = dev; skb_put(tp->Rpl[i].Skb, tp->MaxPacketSize); /* data unreachable for DMA ? then use local buffer */ dmabuf = dma_map_single(tp->pdev, tp->Rpl[i].Skb->data, tp->MaxPacketSize, DMA_FROM_DEVICE); if(tp->dmalimit && (dmabuf + tp->MaxPacketSize > tp->dmalimit)) { tp->Rpl[i].SkbStat = SKB_DATA_COPY; tp->Rpl[i].FragList[0].DataAddr = htonl(((char *)tp->LocalRxBuffers[i] - (char *)tp) + tp->dmabuffer); tp->Rpl[i].MData = tp->LocalRxBuffers[i]; } else /* DMA directly in skb->data */ { tp->Rpl[i].SkbStat = SKB_DMA_DIRECT; tp->Rpl[i].FragList[0].DataAddr = htonl(dmabuf); tp->Rpl[i].MData = tp->Rpl[i].Skb->data; tp->Rpl[i].DMABuff = dmabuf; } } tp->Rpl[i].NextRPLPtr = &tp->Rpl[(i+1) % RPL_NUM]; tp->Rpl[i].RPLIndex = i; } tp->RplHead = &tp->Rpl[0]; tp->RplTail = &tp->Rpl[RPL_NUM-1]; tp->RplTail->Status = (RX_START_FRAME | RX_END_FRAME | RX_FRAME_IRQ); return;}/* * Initializes the initialisation parameter block. */static void tms380tr_init_ipb(struct net_local *tp){ tp->ipb.Init_Options = BURST_MODE; tp->ipb.CMD_Status_IV = 0; tp->ipb.TX_IV = 0; tp->ipb.RX_IV = 0; tp->ipb.Ring_Status_IV = 0; tp->ipb.SCB_Clear_IV = 0; tp->ipb.Adapter_CHK_IV = 0; tp->ipb.RX_Burst_Size = BURST_SIZE; tp->ipb.TX_Burst_Size = BURST_SIZE; tp->ipb.DMA_Abort_Thrhld = DMA_RETRIES; tp->ipb.SCB_Addr = 0; tp->ipb.SSB_Addr = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -