patch-2.3.99-pre3 linux/drivers/atm/ambassador.c

Next file: linux/drivers/atm/ambassador.h
Previous file: linux/drivers/atm/Makefile
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.99-pre2/linux/drivers/atm/ambassador.c linux/drivers/atm/ambassador.c
@@ -32,6 +32,7 @@
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 
+#include <asm/atomic.h>
 #include <asm/io.h>
 #include <asm/byteorder.h>
 
@@ -39,7 +40,7 @@
 
 #define maintainer_string "Giuliano Procida at Madge Networks <gprocida@madge.com>"
 #define description_string "Madge ATM Ambassador driver"
-#define version_string "1.2"
+#define version_string "1.2.4"
 
 static inline void __init show_version (void) {
   printk ("%s version %s\n", description_string, version_string);
@@ -97,8 +98,8 @@
   
   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 kept and ABR
-  (explicit rate, bu not EFCI) is supported. There is no CBR or VBR
+  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
@@ -321,8 +322,29 @@
 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, const u32 * addr, u32 data) {
+  PRINTD (DBG_FLOW|DBG_REGS, "wr: %p <- %08x", addr, data);
+#ifdef AMB_MMIO
+  dev->membase[addr - (u32 *) 0] = data;
+#else
+  outl (data, dev->iobase + (addr - (u32 *) 0) * sizeof(u32));
+#endif
+}
+
+static inline u32 rd_plain (const amb_dev * dev, const u32 * addr) {
+#ifdef AMB_MMIO
+  u32 data = dev->membase[addr - (u32 *) 0];
+#else
+  u32 data = inl (dev->iobase + (addr - (u32 *) 0) * sizeof(u32));
+#endif
+  PRINTD (DBG_FLOW|DBG_REGS, "rd: %p -> %08x", addr, data);
+  return data;
+}
+
 static const amb_mem * const mem = 0;
 
 static inline void wr_mem (const amb_dev * dev, const u32 * addr, u32 data) {
@@ -342,7 +364,7 @@
   u32 be = inl (dev->iobase + (addr - (u32 *) 0) * sizeof(u32));
 #endif
   u32 data = be32_to_cpu (be);
-  PRINTD (DBG_FLOW|DBG_REGS, "rd: %p -> %08x b[%08x]", addr, data, be);  
+  PRINTD (DBG_FLOW|DBG_REGS, "rd: %p -> %08x b[%08x]", addr, data, be);
   return data;
 }
 
@@ -350,14 +372,18 @@
 
 static inline void dump_registers (const amb_dev * dev) {
 #ifdef DEBUG_AMBASSADOR
-  // u32 * i;
-  // PRINTD (DBG_REGS, "mailboxes: ");
-  // for (i = (u32 *) 0x40; i < (u32 *) 0x60; ++i)
-  // PRINTD (DBG_REGS, "%08x ", rd_mem (dev, i));
-  PRINTD (DBG_REGS, "doorb %08x", rd_mem (dev, (u32 *) 0x60));
-  PRINTD (DBG_REGS, "irqev %08x", rd_mem (dev, (u32 *) 0x64));
-  PRINTD (DBG_REGS, "irqen %08x", rd_mem (dev, (u32 *) 0x68));
-  PRINTD (DBG_REGS, "reset %08x", rd_mem (dev, (u32 *) 0x6c));
+  if (debug & DBG_REGS) {
+    u32 * i;
+    PRINTD (DBG_REGS, "reading PLX control: ");
+    for (i = (u32 *) 0x00; i < (u32 *) 0x30; ++i)
+      rd_mem (dev, i);
+    PRINTD (DBG_REGS, "reading mailboxes: ");
+    for (i = (u32 *) 0x40; i < (u32 *) 0x60; ++i)
+      rd_mem (dev, i);
+    PRINTD (DBG_REGS, "reading doorb irqev irqen reset:");
+    for (i = (u32 *) 0x60; i < (u32 *) 0x70; ++i)
+      rd_mem (dev, i);
+  }
 #else
   (void) dev;
 #endif
@@ -414,8 +440,8 @@
 
 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 fourmegmask = -1 << 22;
+  const u32 twofivesixmask = -1 << 8;
   const u32 starthole = 0xE0000000;
   u32 startaddress = virt_to_bus (start);
   u32 lastaddress = startaddress+length-1;
@@ -435,7 +461,7 @@
   if (ATM_SKB(skb)->vcc->pop) {
     ATM_SKB(skb)->vcc->pop (ATM_SKB(skb)->vcc, skb);
   } else {
-    dev_kfree_skb (skb);
+    dev_kfree_skb_any (skb);
   }
 }
 
@@ -448,7 +474,7 @@
   PRINTD (DBG_FLOW|DBG_TX, "tx_complete %p %p", dev, tx);
   
   // VC layer stats
-  ATM_SKB(skb)->vcc->stats->tx++;
+  atomic_inc(&ATM_SKB(skb)->vcc->stats->tx);
   
   // free the descriptor
   kfree (tx_descr);
@@ -489,7 +515,7 @@
 	  dump_skb ("<<<", vc, skb);
 	  
 	  // VC layer stats
-	  atm_vcc->stats->rx++;
+	  atomic_inc(&atm_vcc->stats->rx);
 	  skb->stamp = xtime;
 	  // end of our responsability
 	  atm_vcc->push (atm_vcc, skb);
@@ -504,7 +530,7 @@
       } else {
       	PRINTK (KERN_INFO, "dropped over-size frame");
 	// should we count this?
-	atm_vcc->stats->rx_drop++;
+	atomic_inc(&atm_vcc->stats->rx_drop);
       }
       
     } else {
@@ -524,7 +550,7 @@
       dev->stats.rx.unused++;
   }
   
-  dev_kfree_skb (skb);
+  dev_kfree_skb_any (skb);
   return;
 }
 
@@ -548,7 +574,8 @@
 // sometimes does 16-bit accesses (yuk yuk yuk)
 
 static int command_do (amb_dev * dev, command * cmd) {
-  volatile amb_cq * cq = &dev->cq;
+  amb_cq * cq = &dev->cq;
+  volatile amb_cq_ptrs * ptrs = &cq->ptrs;
   command * my_slot;
   unsigned long timeout;
   
@@ -562,18 +589,18 @@
   // if not full...
   if (cq->pending < cq->maximum) {
     // remember my slot for later
-    my_slot = cq->in;
+    my_slot = ptrs->in;
     PRINTD (DBG_CMD, "command in slot %p", my_slot);
     
     dump_command (cmd);
     
     // copy command in
-    *cq->in = *cmd;
+    *ptrs->in = *cmd;
     cq->pending++;
-    cq->in = NEXTQ (cq->in, cq->start, cq->limit);
+    ptrs->in = NEXTQ (ptrs->in, ptrs->start, ptrs->limit);
     
     // mail the command
-    wr_mem (dev, &mem->mb.adapter.cmd_address, virt_to_bus (cq->in));
+    wr_mem (dev, &mem->mb.adapter.cmd_address, virt_to_bus (ptrs->in));
     
     // prepare to wait for cq->pending milliseconds
     // effectively one centisecond on i386
@@ -591,13 +618,13 @@
     }
     
     // wait for my slot to be reached (all waiters are here or above, until...)
-    while (cq->out != my_slot) {
-      PRINTD (DBG_CMD, "wait: command slot (now at %p)", cq->out);
+    while (ptrs->out != my_slot) {
+      PRINTD (DBG_CMD, "wait: command slot (now at %p)", ptrs->out);
       schedule();
     }
     
     // wait on my slot (... one gets to its slot, and... )
-    while (cq->out->request != cpu_to_be32 (SRB_COMPLETE)) {
+    while (ptrs->out->request != cpu_to_be32 (SRB_COMPLETE)) {
       PRINTD (DBG_CMD, "wait: command slot completion");
       schedule();
     }
@@ -607,12 +634,13 @@
     spin_lock (&cq->lock);
     cq->pending--;
     // copy command out
-    *cmd = *cq->out;
-    cq->out = NEXTQ (cq->out, cq->start, cq->limit);
+    *cmd = *ptrs->out;
+    ptrs->out = NEXTQ (ptrs->out, ptrs->start, ptrs->limit);
     spin_unlock (&cq->lock);
     
     return 0;
   } else {
+    cq->filled++;
     spin_unlock (&cq->lock);
     return -EAGAIN;
   }
@@ -796,7 +824,7 @@
       return;
     }
     if (check_area (skb->data, skb->truesize)) {
-      dev_kfree_skb (skb);
+      dev_kfree_skb_any (skb);
       return;
     }
     // cast needed as there is no %? for pointer differences
@@ -805,14 +833,14 @@
     rx.handle = virt_to_bus (skb);
     rx.host_address = cpu_to_be32 (virt_to_bus (skb->data));
     if (rx_give (dev, &rx, pool))
-      dev_kfree_skb (skb);
+      dev_kfree_skb_any (skb);
     
   }
   
   return;
 }
 
-// top up all RX pools (also called as a bottom half)
+// top up all RX pools (can also be called as a bottom half)
 static void fill_rx_pools (amb_dev * dev) {
   unsigned char pool;
   
@@ -827,25 +855,23 @@
 /********** enable host interrupts **********/
 
 static inline void interrupts_on (amb_dev * dev) {
-  wr_mem (dev, &mem->interrupt_control,
-	  rd_mem (dev, &mem->interrupt_control)
-	  | AMB_INTERRUPT_BITS);
+  wr_plain (dev, &mem->interrupt_control,
+	    rd_plain (dev, &mem->interrupt_control)
+	    | AMB_INTERRUPT_BITS);
 }
 
 /********** disable host interrupts **********/
 
 static inline void interrupts_off (amb_dev * dev) {
-  wr_mem (dev, &mem->interrupt_control,
-	  rd_mem (dev, &mem->interrupt_control)
-	  &~ AMB_INTERRUPT_BITS);
+  wr_plain (dev, &mem->interrupt_control,
+	    rd_plain (dev, &mem->interrupt_control)
+	    &~ AMB_INTERRUPT_BITS);
 }
 
 /********** interrupt handling **********/
 
 static void interrupt_handler (int irq, void * dev_id, struct pt_regs * pt_regs) {
   amb_dev * dev = amb_devs;
-  unsigned int irq_ok;
-  unsigned int irq_ok_old;
   (void) pt_regs;
   
   PRINTD (DBG_IRQ|DBG_FLOW, "interrupt_handler: %p", dev_id);
@@ -860,50 +886,56 @@
       break;
     dev = dev->prev;
   }
+  // impossible - unless we add the device to our list after both
+  // registering the IRQ handler for it and enabling interrupts, AND
+  // the card generates an IRQ at startup - should not happen again
   if (!dev) {
-    PRINTD (DBG_IRQ, "irq not for me: %d", irq);
+    PRINTD (DBG_IRQ, "irq for unknown device: %d", irq);
     return;
   }
+  // impossible - unless we have memory corruption of dev or kernel
   if (irq != dev->irq) {
     PRINTD (DBG_IRQ|DBG_ERR, "irq mismatch: %d", irq);
     return;
   }
   
-  // definitely for us
-  irq_ok = 0;
-  irq_ok_old = -1;
+  {
+    u32 interrupt = rd_plain (dev, &mem->interrupt);
   
-  // perhaps disable interrupts? (disabled at PIC by Linux)
-  // interrupts_off (dev);
+    // for us or someone else sharing the same interrupt
+    if (!interrupt) {
+      PRINTD (DBG_IRQ, "irq not for me: %d", irq);
+      return;
+    }
+    
+    // definitely for us
+    PRINTD (DBG_IRQ, "FYI: interrupt was %08x", interrupt);
+    wr_plain (dev, &mem->interrupt, -1);
+  }
   
-  while (irq_ok_old != irq_ok && irq_ok < 100) {
+  {
+    unsigned int irq_work = 0;
     unsigned char pool;
-    PRINTD (DBG_IRQ, "FYI: interrupt was %08x, work %u",
-	    rd_mem (dev, &mem->interrupt), irq_ok);
-    wr_mem (dev, &mem->interrupt, -1);
-    irq_ok_old = irq_ok;
     for (pool = 0; pool < NUM_RX_POOLS; ++pool)
       while (!rx_take (dev, pool))
-	++irq_ok;
+	++irq_work;
     while (!tx_take (dev))
-      ++irq_ok;
-  }
+      ++irq_work;
   
-  if (irq_ok) {
-#if 0
-    queue_task (&dev->bh, &tq_immediate);
-    mark_bh (IMMEDIATE_BH);
+    if (irq_work) {
+#ifdef FILL_RX_POOLS_IN_BH
+      queue_task (&dev->bh, &tq_immediate);
+      mark_bh (IMMEDIATE_BH);
 #else
-    fill_rx_pools (dev);
+      fill_rx_pools (dev);
 #endif
 
-    PRINTD (DBG_IRQ, "work done: %u", irq_ok);
-  } else {
-    PRINTD (DBG_IRQ|DBG_WARN, "no work done");
+      PRINTD (DBG_IRQ, "work done: %u", irq_work);
+    } else {
+      PRINTD (DBG_IRQ|DBG_WARN, "no work done");
+    }
   }
   
-  // perhaps re-enable interrupts? (re-enabled at PIC by Linux)
-  // interrupts_on (dev);
   PRINTD (DBG_IRQ|DBG_FLOW, "interrupt_handler done: %p", dev_id);
   return;
 }
@@ -913,6 +945,7 @@
 #ifdef DEBUG_AMBASSADOR
 static void dont_panic (amb_dev * dev) {
   amb_cq * cq = &dev->cq;
+  volatile amb_cq_ptrs * ptrs = &cq->ptrs;
   amb_txq * txq;
   amb_rxq * rxq;
   command * cmd;
@@ -926,10 +959,11 @@
   cli();
   
   PRINTK (KERN_INFO, "don't panic - putting adapter into reset");
-  wr_mem (dev, &mem->reset_control, rd_mem (dev, &mem->reset_control) | AMB_RESET);
+  wr_plain (dev, &mem->reset_control,
+	    rd_plain (dev, &mem->reset_control) | AMB_RESET_BITS);
   
   PRINTK (KERN_INFO, "marking all commands complete");
-  for (cmd = cq->start; cmd < cq->limit; ++cmd)
+  for (cmd = ptrs->start; cmd < ptrs->limit; ++cmd)
     cmd->request = cpu_to_be32 (SRB_COMPLETE);
 
   PRINTK (KERN_INFO, "completing all TXs");
@@ -952,7 +986,7 @@
       if (rx == rxq->in.start)
 	rx = rxq->in.limit;
       --rx;
-      dev_kfree_skb (bus_to_virt (rx->handle));
+      dev_kfree_skb_any (bus_to_virt (rx->handle));
     }
   }
   
@@ -972,18 +1006,19 @@
   
   PRINTD (DBG_FLOW|DBG_QOS, "make_rate %u", rate);
   
-  // rates in cells per second, ITU format (nasty 16bit fp)
+  // rates in cells per second, ITU format (nasty 16-bit floating-point)
   // given 5-bit e and 9-bit m:
   // rate = EITHER (1+m/2^9)*2^e    OR 0
   // bits = EITHER 1<<14 | e<<9 | m OR 0
   // (bit 15 is "reserved", bit 14 "non-zero")
   // smallest rate is 0 (special representation)
   // largest rate is (1+511/512)*2^31 = 4290772992 (< 2^32-1)
+  // smallest non-zero rate is (1+0/512)*2^0 = 1 (> 0)
   // simple algorithm:
   // find position of top bit, this gives e
   // remove top bit and shift (rounding if feeling clever) by 9-e
   
-  // ucode bug: please don't set bit 14! 0 not representable
+  // ucode bug: please don't set bit 14! so 0 rate not representable
   
   if (rate > 0xffc00000U) {
     // larger than largest representable rate
@@ -1228,7 +1263,7 @@
   
   // we are not really "immediately before allocating the connection
   // identifier in hardware", but it will just have to do!
-  atm_vcc->flags |= ATM_VF_ADDR;
+  set_bit(ATM_VF_ADDR,&atm_vcc->flags);
   
   if (txtp->traffic_class != ATM_NONE) {
     command cmd;
@@ -1303,7 +1338,7 @@
   atm_vcc->vci = vci;
   
   // indicate readiness
-  atm_vcc->flags |= ATM_VF_READY;
+  set_bit(ATM_VF_READY,&atm_vcc->flags);
   
   MOD_INC_USE_COUNT;
   return 0;
@@ -1319,7 +1354,7 @@
   PRINTD (DBG_VCC|DBG_FLOW, "amb_close");
   
   // indicate unreadiness
-  atm_vcc->flags &= ~ATM_VF_READY;
+  clear_bit(ATM_VF_READY,&atm_vcc->flags);
   
   // disable TXing
   if (atm_vcc->qos.txtp.traffic_class != ATM_NONE) {
@@ -1384,7 +1419,7 @@
   kfree (vcc);
   
   // say the VPI/VCI is free again
-  atm_vcc->flags &= ~ATM_VF_ADDR;
+  clear_bit(ATM_VF_ADDR,&atm_vcc->flags);
   MOD_DEC_USE_COUNT;
 }
 
@@ -1394,6 +1429,8 @@
 static int amb_ioctl (struct atm_dev * dev, unsigned int cmd, void * arg) {
   unsigned short newdebug;
   if (cmd == AMB_SETDEBUG) {
+    if (!capable(CAP_NET_ADMIN))
+      return -EPERM;
     if (copy_from_user (&newdebug, arg, sizeof(newdebug))) {
       // moan
       return -EFAULT;
@@ -1402,6 +1439,8 @@
       return 0;
     }
   } else if (cmd == AMB_DONTPANIC) {
+    if (!capable(CAP_NET_ADMIN))
+      return -EPERM;
     dont_panic (dev);
   } else {
     // moan
@@ -1453,7 +1492,7 @@
   }
   
   if (check_area (skb->data, skb->len)) {
-    atm_vcc->stats->tx_err++;
+    atomic_inc(&atm_vcc->stats->tx_err);
     return -ENOMEM; // ?
   }
   
@@ -1564,7 +1603,7 @@
   // at. However, I note that the ATM layer calls kfree_skb rather
   // than dev_kfree_skb at this point so we are least covered as far
   // as buffer locking goes. There may be bugs if pcap clones RX skbs.
-  
+
   PRINTD (DBG_FLOW|DBG_SKB, "amb_rx_free skb %p (atm_vcc %p, vcc %p)",
 	  skb, atm_vcc, vcc);
   
@@ -1582,7 +1621,7 @@
   }
   
   // just do what the ATM layer would have done
-  kfree_skb (skb);
+  dev_kfree_skb_any (skb);
   
   return;
 }
@@ -1733,12 +1772,12 @@
     cq->high = 0;
     cq->maximum = cmds - 1;
     
-    cq->start = cmd;
-    cq->in = cmd;
-    cq->out = cmd;
-    cq->limit = cmd + cmds;
+    cq->ptrs.start = cmd;
+    cq->ptrs.in = cmd;
+    cq->ptrs.out = cmd;
+    cq->ptrs.limit = cmd + cmds;
     
-    memory = cq->limit;
+    memory = cq->ptrs.limit;
   }
   
   PRINTD (DBG_TX, "TX queue pair at %p", memory);
@@ -1810,7 +1849,7 @@
 
 static void destroy_queues (amb_dev * dev) {
   // all queues assumed empty
-  void * memory = dev->cq.start;
+  void * memory = dev->cq.ptrs.start;
   // includes txq.in, txq.out, rxq[].in and rxq[].out
   
   PRINTD (DBG_FLOW, "destroy_queues %p", dev);
@@ -1823,9 +1862,8 @@
 
 /********** basic loader commands and error handling **********/
 
-static int __init do_loader_command (const amb_dev * dev, loader_command cmd,
-				     volatile loader_block * lb) {
-  
+static int __init do_loader_command (volatile loader_block * lb,
+				     const amb_dev * dev, loader_command cmd) {
   // centisecond timeouts - guessing away here
   unsigned int command_timeouts [] = {
     [host_memory_test]     = 15,
@@ -1939,8 +1977,9 @@
   lb->result = 0;
   lb->command = cpu_to_be32 (cmd);
   lb->valid = cpu_to_be32 (DMA_VALID);
+  // dump_registers (dev);
   // dump_loader_block (lb);
-  wr_mem (dev, &mem->doorbell, virt_to_bus (lb));
+  wr_mem (dev, &mem->doorbell, virt_to_bus (lb) & ~onegigmask);
   
   timeout = command_timeouts[cmd] * HZ/100;
   
@@ -1957,7 +1996,7 @@
   if (cmd == adapter_start) {
     // wait for start command to acknowledge...
     timeout = HZ/10;
-    while (rd_mem (dev, &mem->doorbell))
+    while (rd_plain (dev, &mem->doorbell))
       if (timeout) {
 	timeout = schedule_timeout (timeout);
       } else {
@@ -1975,27 +2014,27 @@
 
 /* loader: determine loader version */
 
-static int __init get_loader_version (const amb_dev * dev, u32 * version) {
-  loader_block lb;
+static int __init get_loader_version (loader_block * lb,
+				      const amb_dev * dev, u32 * version) {
   int res;
   
   PRINTD (DBG_FLOW|DBG_LOAD, "get_loader_version");
   
-  res = do_loader_command (dev, get_version_number, &lb);
+  res = do_loader_command (lb, dev, get_version_number);
   if (res)
     return res;
   if (version)
-    *version = be32_to_cpu (lb.payload.version);
+    *version = be32_to_cpu (lb->payload.version);
   return 0;
 }
 
-/* loader: read or verify memory data blocks */
+/* loader: write memory data blocks */
 
-static int __init loader_write (const amb_dev * dev, const u32 * data,
+static int __init loader_write (loader_block * lb,
+				const amb_dev * dev, const u32 * data,
 				u32 address, unsigned int count) {
   unsigned int i;
-  loader_block lb;
-  transfer_block * tb = &lb.payload.transfer;
+  transfer_block * tb = &lb->payload.transfer;
   
   PRINTD (DBG_FLOW|DBG_LOAD, "loader_write");
   
@@ -2005,14 +2044,16 @@
   tb->count = cpu_to_be32 (count);
   for (i = 0; i < count; ++i)
     tb->data[i] = cpu_to_be32 (data[i]);
-  return do_loader_command (dev, write_adapter_memory, &lb);
+  return do_loader_command (lb, dev, write_adapter_memory);
 }
 
-static int __init loader_verify (const amb_dev * dev, const u32 * data,
+/* loader: verify memory data blocks */
+
+static int __init loader_verify (loader_block * lb,
+				 const amb_dev * dev, const u32 * data,
 				 u32 address, unsigned int count) {
   unsigned int i;
-  loader_block lb;
-  transfer_block * tb = &lb.payload.transfer;
+  transfer_block * tb = &lb->payload.transfer;
   int res;
   
   PRINTD (DBG_FLOW|DBG_LOAD, "loader_verify");
@@ -2021,7 +2062,7 @@
     return -EINVAL;
   tb->address = cpu_to_be32 (address);
   tb->count = cpu_to_be32 (count);
-  res = do_loader_command (dev, read_adapter_memory, &lb);
+  res = do_loader_command (lb, dev, read_adapter_memory);
   if (!res)
     for (i = 0; i < count; ++i)
       if (tb->data[i] != cpu_to_be32 (data[i])) {
@@ -2031,13 +2072,14 @@
   return res;
 }
 
-static int __init loader_start (const amb_dev * dev, u32 address) {
-  loader_block lb;
-  
+/* loader: start microcode */
+
+static int __init loader_start (loader_block * lb,
+				const amb_dev * dev, u32 address) {
   PRINTD (DBG_FLOW|DBG_LOAD, "loader_start");
   
-  lb.payload.start = cpu_to_be32 (address);
-  return do_loader_command (dev, adapter_start, &lb);
+  lb->payload.start = cpu_to_be32 (address);
+  return do_loader_command (lb, dev, adapter_start);
 }
 
 /********** reset card **********/
@@ -2047,19 +2089,21 @@
   
   PRINTD (DBG_FLOW|DBG_LOAD, "amb_reset");
   
-  word = rd_mem (dev, &mem->reset_control);
-#if 0
-  // clear all interrupts just in case
-  wr_mem (dev, &mem->interrupt, -1);
-#endif
+  word = rd_plain (dev, &mem->reset_control);
   // put card into reset state
-  wr_mem (dev, &mem->reset_control, word | AMB_RESET);
+  wr_plain (dev, &mem->reset_control, word | AMB_RESET_BITS);
   // wait a short while
   udelay (10);
+#if 1
+  // put card into known good state
+  wr_plain (dev, &mem->interrupt_control, AMB_DOORBELL_BITS);
+  // clear all interrupts just in case
+  wr_plain (dev, &mem->interrupt, -1);
+#endif
   // clear self-test done flag
-  wr_mem (dev, &mem->mb.loader.ready, 0);
+  wr_plain (dev, &mem->mb.loader.ready, 0);
   // take card out of reset state
-  wr_mem (dev, &mem->reset_control, word &~ AMB_RESET);
+  wr_plain (dev, &mem->reset_control, word &~ AMB_RESET_BITS);
   
   if (diags) { 
     unsigned long timeout;
@@ -2069,7 +2113,7 @@
       timeout = schedule_timeout (timeout);
     // half second time-out
     timeout = HZ/2;
-    while (!rd_mem (dev, &mem->mb.loader.ready))
+    while (!rd_plain (dev, &mem->mb.loader.ready))
       if (timeout) {
 	timeout = schedule_timeout (timeout);
       } else {
@@ -2078,6 +2122,7 @@
       }
     
     // get results of self-test
+    // XXX double check byte-order
     word = rd_mem (dev, &mem->mb.loader.result);
     if (word & SELF_TEST_FAILURE) {
       void sf (const char * msg) {
@@ -2105,7 +2150,7 @@
 
 /********** transfer and start the microcode **********/
 
-static int __init ucode_init (amb_dev * dev) {
+static int __init ucode_init (loader_block * lb, amb_dev * dev) {
   unsigned int i = 0;
   unsigned int total = 0;
   const u32 * pointer = ucode_data;
@@ -2125,10 +2170,10 @@
       else
 	words = MAX_TRANSFER_DATA;
       total += words;
-      res = loader_write (dev, pointer, address, words);
+      res = loader_write (lb, dev, pointer, address, words);
       if (res)
 	return res;
-      res = loader_verify (dev, pointer, address, words);
+      res = loader_verify (lb, dev, pointer, address, words);
       if (res)
 	return res;
       count -= words;
@@ -2138,7 +2183,7 @@
     i += 1;
   }
   if (*pointer == 0xdeadbeef) {
-    return loader_start (dev, ucode_start);
+    return loader_start (lb, dev, ucode_start);
   } else {
     // cast needed as there is no %? for pointer differnces
     PRINTD (DBG_LOAD|DBG_ERR,
@@ -2156,14 +2201,14 @@
   unsigned char pool;
   unsigned long timeout;
   
-  static inline u32 x (void * addr) {
+  u32 x (void * addr) {
     return cpu_to_be32 (virt_to_bus (addr));
   }
   
   PRINTD (DBG_FLOW, "amb_talk %p", dev);
   
-  a.command_start = x (dev->cq.start);
-  a.command_end   = x (dev->cq.limit);
+  a.command_start = x (dev->cq.ptrs.start);
+  a.command_end   = x (dev->cq.ptrs.limit);
   a.tx_start      = x (dev->txq.in.start);
   a.tx_end        = x (dev->txq.in.limit);
   a.txcom_start   = x (dev->txq.out.start);
@@ -2192,7 +2237,7 @@
     timeout = schedule_timeout (timeout);
   // give the adapter another half second?
   timeout = HZ/2;
-  while (rd_mem (dev, &mem->doorbell))
+  while (rd_plain (dev, &mem->doorbell))
     if (timeout) {
       timeout = schedule_timeout (timeout);
     } else {
@@ -2259,38 +2304,55 @@
 }
 
 static int __init amb_init (amb_dev * dev) {
-  u32 version;
+  loader_block lb;
   
-  /* enable adapter doorbell */
-  wr_mem (dev, &mem->interrupt_control,
-	  rd_mem (dev, &mem->interrupt_control)
-	  | AMB_DOORBELL_BITS);
+  void fixup_plx_window (void) {
+    // fix up the PLX-mapped window base address to match the block
+    unsigned long blb;
+    u32 mapreg;
+    blb = virt_to_bus (&lb);
+    // the kernel stack had better not ever cross a 1Gb boundary!
+    mapreg = rd_plain (dev, &mem->stuff[10]);
+    mapreg &= ~onegigmask;
+    mapreg |= blb & onegigmask;
+    wr_plain (dev, &mem->stuff[10], mapreg);
+    return;
+  }
+  
+  u32 version;
   
   if (amb_reset (dev, 1)) {
     PRINTK (KERN_ERR, "card reset failed!");
-  } else if (get_loader_version (dev, &version)) {
-    PRINTK (KERN_INFO, "failed to get loader version");
   } else {
-    PRINTK (KERN_INFO, "loader version is %08x", version);
+    fixup_plx_window ();
     
-    if (ucode_init (dev)) {
-      PRINTK (KERN_ERR, "microcode failure");
-    } else if (create_queues (dev, cmds, txs, rxs, rxs_bs)) {
-      PRINTK (KERN_ERR, "failed to get memory for queues");
+    if (get_loader_version (&lb, dev, &version)) {
+      PRINTK (KERN_INFO, "failed to get loader version");
     } else {
+      PRINTK (KERN_INFO, "loader version is %08x", version);
       
-      if (amb_talk (dev)) {
-	PRINTK (KERN_ERR, "adapter did not accept queues");
+      if (ucode_init (&lb, dev)) {
+	PRINTK (KERN_ERR, "microcode failure");
+      } else if (create_queues (dev, cmds, txs, rxs, rxs_bs)) {
+	PRINTK (KERN_ERR, "failed to get memory for queues");
       } else {
 	
-	amb_ucode_version (dev);
-	return 0;
-      } /* amb_talk */
+	if (amb_talk (dev)) {
+	  PRINTK (KERN_ERR, "adapter did not accept queues");
+	} else {
+	  
+	  amb_ucode_version (dev);
+	  return 0;
+	  
+	} /* amb_talk */
+	
+	destroy_queues (dev);
+      } /* create_queues, ucode_init */
       
-      destroy_queues (dev);
-    } /* create_queues, ucode_init */
+      amb_reset (dev, 0);
+    } /* get_loader_version */
     
-  } /* get_loader_version, amb_reset */
+  } /* amb_reset */
   
   return -1;
 }
@@ -2303,81 +2365,57 @@
     amb_dev * dev;
     
     // read resources from PCI configuration space
-    u32 * membase = bus_to_virt
-      (pci_dev->resource[0].start);
-    u32 iobase = pci_dev->resource[1].start;
     u8 irq = pci_dev->irq;
+    u32 * membase = bus_to_virt (pci_dev->resource[0].start);
+    u32 iobase = pci_dev->resource[1].start;
     
-    // check IO region
-    if (check_region (iobase, AMB_EXTENT)) {
-      PRINTK (KERN_ERR, "IO range already in use!");
-      return;
-    }
-    
-    dev = kmalloc (sizeof(amb_dev), GFP_KERNEL);
-    if (!dev) {
-      // perhaps we should be nice: deregister all adapters and abort?
-      PRINTK (KERN_ERR, "out of memory!");
-      return;
-    }
-    memset (dev, 0, sizeof(amb_dev));    
-    
-    // set up known dev items straight away
-    dev->pci_dev = pci_dev; 
-    
-    dev->iobase = iobase;
-    dev->irq = irq; 
-    dev->membase = membase;
-    
-    // flags (currently only dead)
-    dev->flags = 0;
-    
-    // Allocate cell rates (fibre)
-    // ATM_OC3_PCR = 1555200000/8/270*260/53 - 29/53
-    // to be really pedantic, this should be ATM_OC3c_PCR
-    dev->tx_avail = ATM_OC3_PCR;
-    dev->rx_avail = ATM_OC3_PCR;
-    
-#if 0
-    // initialise bottom half
-    dev->bh.next = 0;
-    dev->bh.sync = 0;
-    dev->bh.routine = (void (*)(void *)) fill_rx_pools;
-    dev->bh.data = dev;
-#endif
-    
-    // semaphore for txer/rxer modifications - we cannot use a
-    // spinlock as the critical region needs to switch processes
-    init_MUTEX (&dev->vcc_sf);
-    // queue manipulation spinlocks; we want atomic reads and
-    // writes to the queue descriptors (handles IRQ and SMP)
-    // consider replacing "int pending" -> "atomic_t available"
-    // => problem related to who gets to move queue pointers
-    spin_lock_init (&dev->cq.lock);
-    spin_lock_init (&dev->txq.lock);
-    {
+    void setup_dev (void) {
       unsigned char pool;
+      memset (dev, 0, sizeof(amb_dev));
+      
+      // set up known dev items straight away
+      dev->pci_dev = pci_dev; 
+      
+      dev->iobase = iobase;
+      dev->irq = irq; 
+      dev->membase = membase;
+      
+      // flags (currently only dead)
+      dev->flags = 0;
+      
+      // Allocate cell rates (fibre)
+      // ATM_OC3_PCR = 1555200000/8/270*260/53 - 29/53
+      // to be really pedantic, this should be ATM_OC3c_PCR
+      dev->tx_avail = ATM_OC3_PCR;
+      dev->rx_avail = ATM_OC3_PCR;
+      
+#ifdef FILL_RX_POOLS_IN_BH
+      // initialise bottom half
+      dev->bh.next = 0;
+      dev->bh.sync = 0;
+      dev->bh.routine = (void (*)(void *)) fill_rx_pools;
+      dev->bh.data = dev;
+#endif
+      
+      // semaphore for txer/rxer modifications - we cannot use a
+      // spinlock as the critical region needs to switch processes
+      init_MUTEX (&dev->vcc_sf);
+      // queue manipulation spinlocks; we want atomic reads and
+      // writes to the queue descriptors (handles IRQ and SMP)
+      // consider replacing "int pending" -> "atomic_t available"
+      // => problem related to who gets to move queue pointers
+      spin_lock_init (&dev->cq.lock);
+      spin_lock_init (&dev->txq.lock);
       for (pool = 0; pool < NUM_RX_POOLS; ++pool)
 	spin_lock_init (&dev->rxq[pool].lock);
     }
     
-    // grab (but share) IRQ and install handler
-    if (request_irq (irq, interrupt_handler, SA_SHIRQ, DEV_LABEL, dev)) {
-      PRINTK (KERN_ERR, "request IRQ failed!");
-      // free_irq is at "endif"
-    } else {
-      
+    void setup_pci_dev (void) {
       unsigned char lat;
       
-      // reserve IO region
-      request_region (iobase, AMB_EXTENT, DEV_LABEL);
-      
-      PRINTD (DBG_INFO, "found Madge ATM adapter (amb) at IO %x, IRQ %u, MEM %p",
-	      iobase, irq, membase);
-      
       // enable bus master accesses
       pci_set_master (pci_dev);
-
+      
       // frobnicate latency (upwards, usually)
       pci_read_config_byte (pci_dev, PCI_LATENCY_TIMER, &lat);
       if (pci_lat) {
@@ -2389,43 +2427,78 @@
 		"increasing", lat, MIN_PCI_LATENCY);
 	pci_write_config_byte (pci_dev, PCI_LATENCY_TIMER, MIN_PCI_LATENCY);
       }
+    }
+    
+    PRINTD (DBG_INFO, "found Madge ATM adapter (amb) at"
+	    " IO %x, IRQ %u, MEM %p", iobase, irq, membase);
+    
+    // check IO region
+    if (check_region (iobase, AMB_EXTENT)) {
+      PRINTK (KERN_ERR, "IO range already in use!");
+      return;
+    }
+    
+    dev = kmalloc (sizeof(amb_dev), GFP_KERNEL);
+    if (!dev) {
+      // perhaps we should be nice: deregister all adapters and abort?
+      PRINTK (KERN_ERR, "out of memory!");
+      return;
+    }
+    
+    setup_dev();
+    
+    if (amb_init (dev)) {
+      PRINTK (KERN_ERR, "adapter initialisation failure");
+    } else {
+      
+      setup_pci_dev();
       
-      if (amb_init (dev)) {
-	PRINTK (KERN_ERR, "adapter initialisation failure");
-      } else if (!(dev->atm_dev = atm_dev_register (DEV_LABEL, &amb_ops, -1, 0))) {
-	PRINTD (DBG_ERR, "failed to register Madge ATM adapter");
+      // grab (but share) IRQ and install handler
+      if (request_irq (irq, interrupt_handler, SA_SHIRQ, DEV_LABEL, dev)) {
+	PRINTK (KERN_ERR, "request IRQ failed!");
+	// free_irq is at "endif"
       } else {
 	
-	PRINTD (DBG_INFO, "registered Madge ATM adapter (no. %d) (%p) at %p",
-		dev->atm_dev->number, dev, dev->atm_dev);
-	dev->atm_dev->dev_data = (void *) dev;
-	
-	// register our address
-	amb_esi (dev, dev->atm_dev->esi);
+	// reserve IO region
+	request_region (iobase, AMB_EXTENT, DEV_LABEL);
 	
-	// 0 bits for vpi, 10 bits for vci
-	dev->atm_dev->ci_range.vpi_bits = NUM_VPI_BITS;
-	dev->atm_dev->ci_range.vci_bits = NUM_VCI_BITS;
-	
-	fill_rx_pools (dev);
-	
-	/* enable host interrupts */
-	interrupts_on (dev);
-	
-        // update count and linked list
-	++devs;
-	dev->prev = amb_devs;
-	amb_devs = dev;
-	// success
-	return;
+	dev->atm_dev = atm_dev_register (DEV_LABEL, &amb_ops, -1, NULL);
+	if (!dev->atm_dev) {
+	  PRINTD (DBG_ERR, "failed to register Madge ATM adapter");
+	} else {
+	  
+	  PRINTD (DBG_INFO, "registered Madge ATM adapter (no. %d) (%p) at %p",
+		  dev->atm_dev->number, dev, dev->atm_dev);
+	  dev->atm_dev->dev_data = (void *) dev;
+	  
+	  // register our address
+	  amb_esi (dev, dev->atm_dev->esi);
+	  
+	  // 0 bits for vpi, 10 bits for vci
+	  dev->atm_dev->ci_range.vpi_bits = NUM_VPI_BITS;
+	  dev->atm_dev->ci_range.vci_bits = NUM_VCI_BITS;
+	  
+	  // update count and linked list
+	  ++devs;
+	  dev->prev = amb_devs;
+	  amb_devs = dev;
+	  
+	  // enable host interrupts
+	  interrupts_on (dev);
+	  
+	  // success
+	  return;
+	  
+	  // not currently reached
+	  atm_dev_deregister (dev->atm_dev);
+	} /* atm_dev_register */
 	
-	// not currently reached
-	atm_dev_deregister (dev->atm_dev);
-      } /* atm_dev_register, amb_init */
+	release_region (iobase, AMB_EXTENT);
+	free_irq (irq, dev);
+      } /* request_region, request_irq */
       
-      release_region (iobase, AMB_EXTENT);
-      free_irq (irq, dev);
-    } /* request_region, request_irq */
+      amb_reset (dev, 0);
+    } /* amb_init */
     
     kfree (dev);
   } /* kmalloc, end-of-fn */
@@ -2529,7 +2602,6 @@
   
   show_version();
   
-  // check arguments
   amb_check_args();
   
   // get the juice
@@ -2566,8 +2638,8 @@
     PRINTD (DBG_INFO|DBG_INIT, "closing %p (atm_dev = %p)", dev, dev->atm_dev);
     // the drain should not be necessary
     drain_rx_pools (dev);
-    amb_reset (dev, 0);
     interrupts_off (dev);
+    amb_reset (dev, 0);
     destroy_queues (dev);
     atm_dev_deregister (dev->atm_dev);
     free_irq (dev->irq, dev);
@@ -2594,7 +2666,6 @@
   
   show_version();
   
-  // check arguments
   amb_check_args();
   
   // get the juice

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)