ambassador.c

来自「linux 内核源代码」· C语言 代码 · 共 2,334 行 · 第 1/5 页

C
2,334
字号
/*  Madge Ambassador ATM Adapter driver.  Copyright (C) 1995-1999  Madge Networks Ltd.  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 of the License, 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  The GNU GPL is contained in /usr/doc/copyright/GPL on a Debian  system and in the file COPYING in the Linux kernel source.*//* * dedicated to the memory of Graham Gordon 1971-1998 * */#include <linux/module.h>#include <linux/types.h>#include <linux/pci.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/ioport.h>#include <linux/atmdev.h>#include <linux/delay.h>#include <linux/interrupt.h>#include <linux/poison.h>#include <linux/bitrev.h>#include <asm/atomic.h>#include <asm/io.h>#include <asm/byteorder.h>#include "ambassador.h"#define maintainer_string "Giuliano Procida at Madge Networks <gprocida@madge.com>"#define description_string "Madge ATM Ambassador driver"#define version_string "1.2.4"static inline void __init show_version (void) {  printk ("%s version %s\n", description_string, version_string);}/*    Theory of Operation    I Hardware, detection, initialisation and shutdown.    1. Supported Hardware    This driver is for the PCI ATMizer-based Ambassador card (except  very early versions). It is not suitable for the similar EISA "TR7"  card. Commercially, both cards are known as Collage Server ATM  adapters.    The loader supports image transfer to the card, image start and few  other miscellaneous commands.    Only AAL5 is supported with vpi = 0 and vci in the range 0 to 1023.    The cards are big-endian.    2. Detection    Standard PCI stuff, the early cards are detected and rejected.    3. Initialisation    The cards are reset and the self-test results are checked. The  microcode image is then transferred and started. This waits for a  pointer to a descriptor containing details of the host-based queues  and buffers and various parameters etc. Once they are processed  normal operations may begin. The BIA is read using a microcode  command.    4. Shutdown    This may be accomplished either by a card reset or via the microcode  shutdown command. Further investigation required.    5. Persistent state    The card reset does not affect PCI configuration (good) or the  contents of several other "shared run-time registers" (bad) which  include doorbell and interrupt control as well as EEPROM and PCI  control. The driver must be careful when modifying these registers  not to touch bits it does not use and to undo any changes at exit.    II Driver software    0. Generalities    The adapter is quite intelligent (fast) and has a simple interface  (few features). VPI is always zero, 1024 VCIs are supported. There  is limited cell rate support. UBR channels can be capped and ABR  (explicit rate, but not EFCI) is supported. There is no CBR or VBR  support.    1. Driver <-> Adapter Communication    Apart from the basic loader commands, the driver communicates  through three entities: the command queue (CQ), the transmit queue  pair (TXQ) and the receive queue pairs (RXQ). These three entities  are set up by the host and passed to the microcode just after it has  been started.    All queues are host-based circular queues. They are contiguous and  (due to hardware limitations) have some restrictions as to their  locations in (bus) memory. They are of the "full means the same as  empty so don't do that" variety since the adapter uses pointers  internally.    The queue pairs work as follows: one queue is for supply to the  adapter, items in it are pending and are owned by the adapter; the  other is the queue for return from the adapter, items in it have  been dealt with by the adapter. The host adds items to the supply  (TX descriptors and free RX buffer descriptors) and removes items  from the return (TX and RX completions). The adapter deals with out  of order completions.    Interrupts (card to host) and the doorbell (host to card) are used  for signalling.    1. CQ    This is to communicate "open VC", "close VC", "get stats" etc. to  the adapter. At most one command is retired every millisecond by the  card. There is no out of order completion or notification. The  driver needs to check the return code of the command, waiting as  appropriate.    2. TXQ    TX supply items are of variable length (scatter gather support) and  so the queue items are (more or less) pointers to the real thing.  Each TX supply item contains a unique, host-supplied handle (the skb  bus address seems most sensible as this works for Alphas as well,  there is no need to do any endian conversions on the handles).    TX return items consist of just the handles above.    3. RXQ (up to 4 of these with different lengths and buffer sizes)    RX supply items consist of a unique, host-supplied handle (the skb  bus address again) and a pointer to the buffer data area.    RX return items consist of the handle above, the VC, length and a  status word. This just screams "oh so easy" doesn't it?  Note on RX pool sizes:     Each pool should have enough buffers to handle a back-to-back stream  of minimum sized frames on a single VC. For example:      frame spacing = 3us (about right)        delay = IRQ lat + RX handling + RX buffer replenish = 20 (us)  (a guess)        min number of buffers for one VC = 1 + delay/spacing (buffers)    delay/spacing = latency = (20+2)/3 = 7 (buffers)  (rounding up)      The 20us delay assumes that there is no need to sleep; if we need to  sleep to get buffers we are going to drop frames anyway.    In fact, each pool should have enough buffers to support the  simultaneous reassembly of a separate frame on each VC and cope with  the case in which frames complete in round robin cell fashion on  each VC.    Only one frame can complete at each cell arrival, so if "n" VCs are  open, the worst case is to have them all complete frames together  followed by all starting new frames together.      desired number of buffers = n + delay/spacing      These are the extreme requirements, however, they are "n+k" for some  "k" so we have only the constant to choose. This is the argument  rx_lats which current defaults to 7.    Actually, "n ? n+k : 0" is better and this is what is implemented,  subject to the limit given by the pool size.    4. Driver locking    Simple spinlocks are used around the TX and RX queue mechanisms.  Anyone with a faster, working method is welcome to implement it.    The adapter command queue is protected with a spinlock. We always  wait for commands to complete.    A more complex form of locking is used around parts of the VC open  and close functions. There are three reasons for a lock: 1. we need  to do atomic rate reservation and release (not used yet), 2. Opening  sometimes involves two adapter commands which must not be separated  by another command on the same VC, 3. the changes to RX pool size  must be atomic. The lock needs to work over context switches, so we  use a semaphore.    III Hardware Features and Microcode Bugs    1. Byte Ordering    *%^"$&%^$*&^"$(%^$#&^%$(&#%$*(&^#%!"!"!*!    2. Memory access    All structures that are not accessed using DMA must be 4-byte  aligned (not a problem) and must not cross 4MB boundaries.    There is a DMA memory hole at E0000000-E00000FF (groan).    TX fragments (DMA read) must not cross 4MB boundaries (would be 16MB  but for a hardware bug).    RX buffers (DMA write) must not cross 16MB boundaries and must  include spare trailing bytes up to the next 4-byte boundary; they  will be written with rubbish.    The PLX likes to prefetch; if reading up to 4 u32 past the end of  each TX fragment is not a problem, then TX can be made to go a  little faster by passing a flag at init that disables a prefetch  workaround. We do not pass this flag. (new microcode only)    Now we:  . Note that alloc_skb rounds up size to a 16byte boundary.    . Ensure all areas do not traverse 4MB boundaries.  . Ensure all areas do not start at a E00000xx bus address.  (I cannot be certain, but this may always hold with Linux)  . Make all failures cause a loud message.  . Discard non-conforming SKBs (causes TX failure or RX fill delay).  . Discard non-conforming TX fragment descriptors (the TX fails).  In the future we could:  . Allow RX areas that traverse 4MB (but not 16MB) boundaries.  . Segment TX areas into some/more fragments, when necessary.  . Relax checks for non-DMA items (ignore hole).  . Give scatter-gather (iovec) requirements using ???. (?)    3. VC close is broken (only for new microcode)    The VC close adapter microcode command fails to do anything if any  frames have been received on the VC but none have been transmitted.  Frames continue to be reassembled and passed (with IRQ) to the  driver.    IV To Do List    . Fix bugs!    . Timer code may be broken.    . Deal with buggy VC close (somehow) in microcode 12.    . Handle interrupted and/or non-blocking writes - is this a job for    the protocol layer?    . Add code to break up TX fragments when they span 4MB boundaries.    . Add SUNI phy layer (need to know where SUNI lives on card).    . Implement a tx_alloc fn to (a) satisfy TX alignment etc. and (b)    leave extra headroom space for Ambassador TX descriptors.    . Understand these elements of struct atm_vcc: recvq (proto?),    sleep, callback, listenq, backlog_quota, reply and user_back.    . Adjust TX/RX skb allocation to favour IP with LANE/CLIP (configurable).    . Impose a TX-pending limit (2?) on each VC, help avoid TX q overflow.    . Decide whether RX buffer recycling is or can be made completely safe;    turn it back on. It looks like Werner is going to axe this.    . Implement QoS changes on open VCs (involves extracting parts of VC open    and close into separate functions and using them to make changes).    . Hack on command queue so that someone can issue multiple commands and wait    on the last one (OR only "no-op" or "wait" commands are waited for).    . Eliminate need for while-schedule around do_command.  *//********** microcode **********/#ifdef AMB_NEW_MICROCODE#define UCODE(x) UCODE2(atmsar12.x)#else#define UCODE(x) UCODE2(atmsar11.x)#endif#define UCODE2(x) #xstatic u32 __devinitdata ucode_start =#include UCODE(start);static region __devinitdata ucode_regions[] = {#include UCODE(regions)  { 0, 0 }};static u32 __devinitdata ucode_data[] = {#include UCODE(data)  0xdeadbeef};static void do_housekeeping (unsigned long arg);/********** globals **********/static unsigned short debug = 0;static unsigned int cmds = 8;static unsigned int txs = 32;static unsigned int rxs[NUM_RX_POOLS] = { 64, 64, 64, 64 };static unsigned int rxs_bs[NUM_RX_POOLS] = { 4080, 12240, 36720, 65535 };static unsigned int rx_lats = 7;static unsigned char pci_lat = 0;static const unsigned long onegigmask = -1 << 30;/********** access to adapter **********/static inline void wr_plain (const amb_dev * dev, size_t addr, u32 data) {  PRINTD (DBG_FLOW|DBG_REGS, "wr: %08zx <- %08x", addr, data);#ifdef AMB_MMIO  dev->membase[addr / sizeof(u32)] = data;#else  outl (data, dev->iobase + addr);#endif}static inline u32 rd_plain (const amb_dev * dev, size_t addr) {#ifdef AMB_MMIO  u32 data = dev->membase[addr / sizeof(u32)];#else  u32 data = inl (dev->iobase + addr);#endif  PRINTD (DBG_FLOW|DBG_REGS, "rd: %08zx -> %08x", addr, data);  return data;}static inline void wr_mem (const amb_dev * dev, size_t addr, u32 data) {  __be32 be = cpu_to_be32 (data);  PRINTD (DBG_FLOW|DBG_REGS, "wr: %08zx <- %08x b[%08x]", addr, data, be);#ifdef AMB_MMIO  dev->membase[addr / sizeof(u32)] = be;#else  outl (be, dev->iobase + addr);#endif}static inline u32 rd_mem (const amb_dev * dev, size_t addr) {#ifdef AMB_MMIO  __be32 be = dev->membase[addr / sizeof(u32)];#else  __be32 be = inl (dev->iobase + addr);#endif  u32 data = be32_to_cpu (be);  PRINTD (DBG_FLOW|DBG_REGS, "rd: %08zx -> %08x b[%08x]", addr, data, be);  return data;}/********** dump routines **********/static inline void dump_registers (const amb_dev * dev) {#ifdef DEBUG_AMBASSADOR  if (debug & DBG_REGS) {    size_t i;    PRINTD (DBG_REGS, "reading PLX control: ");    for (i = 0x00; i < 0x30; i += sizeof(u32))      rd_mem (dev, i);    PRINTD (DBG_REGS, "reading mailboxes: ");    for (i = 0x40; i < 0x60; i += sizeof(u32))      rd_mem (dev, i);    PRINTD (DBG_REGS, "reading doorb irqev irqen reset:");    for (i = 0x60; i < 0x70; i += sizeof(u32))      rd_mem (dev, i);  }#else  (void) dev;#endif  return;}static inline void dump_loader_block (volatile loader_block * lb) {#ifdef DEBUG_AMBASSADOR  unsigned int i;  PRINTDB (DBG_LOAD, "lb @ %p; res: %d, cmd: %d, pay:",	   lb, be32_to_cpu (lb->result), be32_to_cpu (lb->command));  for (i = 0; i < MAX_COMMAND_DATA; ++i)    PRINTDM (DBG_LOAD, " %08x", be32_to_cpu (lb->payload.data[i]));  PRINTDE (DBG_LOAD, ", vld: %08x", be32_to_cpu (lb->valid));#else  (void) lb;#endif  return;}static inline void dump_command (command * cmd) {#ifdef DEBUG_AMBASSADOR  unsigned int i;  PRINTDB (DBG_CMD, "cmd @ %p, req: %08x, pars:",	   cmd, /*be32_to_cpu*/ (cmd->request));  for (i = 0; i < 3; ++i)    PRINTDM (DBG_CMD, " %08x", /*be32_to_cpu*/ (cmd->args.par[i]));  PRINTDE (DBG_CMD, "");#else  (void) cmd;#endif  return;}static inline void dump_skb (char * prefix, unsigned int vc, struct sk_buff * skb) {#ifdef DEBUG_AMBASSADOR  unsigned int i;  unsigned char * data = skb->data;  PRINTDB (DBG_DATA, "%s(%u) ", prefix, vc);  for (i=0; i<skb->len && i < 256;i++)    PRINTDM (DBG_DATA, "%02x ", data[i]);  PRINTDE (DBG_DATA,"");#else  (void) prefix;  (void) vc;  (void) skb;#endif  return;}/********** check memory areas for use by Ambassador **********//* see limitations under Hardware Features */static inline int check_area (void * start, size_t length) {  // assumes length > 0  const u32 fourmegmask = -1 << 22;  const u32 twofivesixmask = -1 << 8;  const u32 starthole = 0xE0000000;  u32 startaddress = virt_to_bus (start);  u32 lastaddress = startaddress+length-1;  if ((startaddress ^ lastaddress) & fourmegmask ||      (startaddress & twofivesixmask) == starthole) {    PRINTK (KERN_ERR, "check_area failure: [%x,%x] - mail maintainer!",	    startaddress, lastaddress);    return -1;  } else {    return 0;  }}/********** free an skb (as per ATM device driver documentation) **********/static inline void amb_kfree_skb (struct sk_buff * skb) {  if (ATM_SKB(skb)->vcc->pop) {    ATM_SKB(skb)->vcc->pop (ATM_SKB(skb)->vcc, skb);  } else {    dev_kfree_skb_any (skb);  }}/********** TX completion **********/

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?