18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci  Madge Ambassador ATM Adapter driver.
48c2ecf20Sopenharmony_ci  Copyright (C) 1995-1999  Madge Networks Ltd.
58c2ecf20Sopenharmony_ci
68c2ecf20Sopenharmony_ci*/
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/* * dedicated to the memory of Graham Gordon 1971-1998 * */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/module.h>
118c2ecf20Sopenharmony_ci#include <linux/types.h>
128c2ecf20Sopenharmony_ci#include <linux/pci.h>
138c2ecf20Sopenharmony_ci#include <linux/kernel.h>
148c2ecf20Sopenharmony_ci#include <linux/init.h>
158c2ecf20Sopenharmony_ci#include <linux/ioport.h>
168c2ecf20Sopenharmony_ci#include <linux/atmdev.h>
178c2ecf20Sopenharmony_ci#include <linux/delay.h>
188c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
198c2ecf20Sopenharmony_ci#include <linux/poison.h>
208c2ecf20Sopenharmony_ci#include <linux/bitrev.h>
218c2ecf20Sopenharmony_ci#include <linux/mutex.h>
228c2ecf20Sopenharmony_ci#include <linux/firmware.h>
238c2ecf20Sopenharmony_ci#include <linux/ihex.h>
248c2ecf20Sopenharmony_ci#include <linux/slab.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#include <linux/atomic.h>
278c2ecf20Sopenharmony_ci#include <asm/io.h>
288c2ecf20Sopenharmony_ci#include <asm/byteorder.h>
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#include "ambassador.h"
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci#define maintainer_string "Giuliano Procida at Madge Networks <gprocida@madge.com>"
338c2ecf20Sopenharmony_ci#define description_string "Madge ATM Ambassador driver"
348c2ecf20Sopenharmony_ci#define version_string "1.2.4"
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic inline void __init show_version (void) {
378c2ecf20Sopenharmony_ci  printk ("%s version %s\n", description_string, version_string);
388c2ecf20Sopenharmony_ci}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/*
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci  Theory of Operation
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci  I Hardware, detection, initialisation and shutdown.
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci  1. Supported Hardware
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci  This driver is for the PCI ATMizer-based Ambassador card (except
498c2ecf20Sopenharmony_ci  very early versions). It is not suitable for the similar EISA "TR7"
508c2ecf20Sopenharmony_ci  card. Commercially, both cards are known as Collage Server ATM
518c2ecf20Sopenharmony_ci  adapters.
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci  The loader supports image transfer to the card, image start and few
548c2ecf20Sopenharmony_ci  other miscellaneous commands.
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci  Only AAL5 is supported with vpi = 0 and vci in the range 0 to 1023.
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci  The cards are big-endian.
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci  2. Detection
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci  Standard PCI stuff, the early cards are detected and rejected.
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci  3. Initialisation
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci  The cards are reset and the self-test results are checked. The
678c2ecf20Sopenharmony_ci  microcode image is then transferred and started. This waits for a
688c2ecf20Sopenharmony_ci  pointer to a descriptor containing details of the host-based queues
698c2ecf20Sopenharmony_ci  and buffers and various parameters etc. Once they are processed
708c2ecf20Sopenharmony_ci  normal operations may begin. The BIA is read using a microcode
718c2ecf20Sopenharmony_ci  command.
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci  4. Shutdown
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci  This may be accomplished either by a card reset or via the microcode
768c2ecf20Sopenharmony_ci  shutdown command. Further investigation required.
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci  5. Persistent state
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci  The card reset does not affect PCI configuration (good) or the
818c2ecf20Sopenharmony_ci  contents of several other "shared run-time registers" (bad) which
828c2ecf20Sopenharmony_ci  include doorbell and interrupt control as well as EEPROM and PCI
838c2ecf20Sopenharmony_ci  control. The driver must be careful when modifying these registers
848c2ecf20Sopenharmony_ci  not to touch bits it does not use and to undo any changes at exit.
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci  II Driver software
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci  0. Generalities
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci  The adapter is quite intelligent (fast) and has a simple interface
918c2ecf20Sopenharmony_ci  (few features). VPI is always zero, 1024 VCIs are supported. There
928c2ecf20Sopenharmony_ci  is limited cell rate support. UBR channels can be capped and ABR
938c2ecf20Sopenharmony_ci  (explicit rate, but not EFCI) is supported. There is no CBR or VBR
948c2ecf20Sopenharmony_ci  support.
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci  1. Driver <-> Adapter Communication
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci  Apart from the basic loader commands, the driver communicates
998c2ecf20Sopenharmony_ci  through three entities: the command queue (CQ), the transmit queue
1008c2ecf20Sopenharmony_ci  pair (TXQ) and the receive queue pairs (RXQ). These three entities
1018c2ecf20Sopenharmony_ci  are set up by the host and passed to the microcode just after it has
1028c2ecf20Sopenharmony_ci  been started.
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci  All queues are host-based circular queues. They are contiguous and
1058c2ecf20Sopenharmony_ci  (due to hardware limitations) have some restrictions as to their
1068c2ecf20Sopenharmony_ci  locations in (bus) memory. They are of the "full means the same as
1078c2ecf20Sopenharmony_ci  empty so don't do that" variety since the adapter uses pointers
1088c2ecf20Sopenharmony_ci  internally.
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci  The queue pairs work as follows: one queue is for supply to the
1118c2ecf20Sopenharmony_ci  adapter, items in it are pending and are owned by the adapter; the
1128c2ecf20Sopenharmony_ci  other is the queue for return from the adapter, items in it have
1138c2ecf20Sopenharmony_ci  been dealt with by the adapter. The host adds items to the supply
1148c2ecf20Sopenharmony_ci  (TX descriptors and free RX buffer descriptors) and removes items
1158c2ecf20Sopenharmony_ci  from the return (TX and RX completions). The adapter deals with out
1168c2ecf20Sopenharmony_ci  of order completions.
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci  Interrupts (card to host) and the doorbell (host to card) are used
1198c2ecf20Sopenharmony_ci  for signalling.
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci  1. CQ
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci  This is to communicate "open VC", "close VC", "get stats" etc. to
1248c2ecf20Sopenharmony_ci  the adapter. At most one command is retired every millisecond by the
1258c2ecf20Sopenharmony_ci  card. There is no out of order completion or notification. The
1268c2ecf20Sopenharmony_ci  driver needs to check the return code of the command, waiting as
1278c2ecf20Sopenharmony_ci  appropriate.
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci  2. TXQ
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci  TX supply items are of variable length (scatter gather support) and
1328c2ecf20Sopenharmony_ci  so the queue items are (more or less) pointers to the real thing.
1338c2ecf20Sopenharmony_ci  Each TX supply item contains a unique, host-supplied handle (the skb
1348c2ecf20Sopenharmony_ci  bus address seems most sensible as this works for Alphas as well,
1358c2ecf20Sopenharmony_ci  there is no need to do any endian conversions on the handles).
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci  TX return items consist of just the handles above.
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci  3. RXQ (up to 4 of these with different lengths and buffer sizes)
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci  RX supply items consist of a unique, host-supplied handle (the skb
1428c2ecf20Sopenharmony_ci  bus address again) and a pointer to the buffer data area.
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci  RX return items consist of the handle above, the VC, length and a
1458c2ecf20Sopenharmony_ci  status word. This just screams "oh so easy" doesn't it?
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci  Note on RX pool sizes:
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci  Each pool should have enough buffers to handle a back-to-back stream
1508c2ecf20Sopenharmony_ci  of minimum sized frames on a single VC. For example:
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci    frame spacing = 3us (about right)
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci    delay = IRQ lat + RX handling + RX buffer replenish = 20 (us)  (a guess)
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci    min number of buffers for one VC = 1 + delay/spacing (buffers)
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci    delay/spacing = latency = (20+2)/3 = 7 (buffers)  (rounding up)
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci  The 20us delay assumes that there is no need to sleep; if we need to
1618c2ecf20Sopenharmony_ci  sleep to get buffers we are going to drop frames anyway.
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci  In fact, each pool should have enough buffers to support the
1648c2ecf20Sopenharmony_ci  simultaneous reassembly of a separate frame on each VC and cope with
1658c2ecf20Sopenharmony_ci  the case in which frames complete in round robin cell fashion on
1668c2ecf20Sopenharmony_ci  each VC.
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci  Only one frame can complete at each cell arrival, so if "n" VCs are
1698c2ecf20Sopenharmony_ci  open, the worst case is to have them all complete frames together
1708c2ecf20Sopenharmony_ci  followed by all starting new frames together.
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci    desired number of buffers = n + delay/spacing
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci  These are the extreme requirements, however, they are "n+k" for some
1758c2ecf20Sopenharmony_ci  "k" so we have only the constant to choose. This is the argument
1768c2ecf20Sopenharmony_ci  rx_lats which current defaults to 7.
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci  Actually, "n ? n+k : 0" is better and this is what is implemented,
1798c2ecf20Sopenharmony_ci  subject to the limit given by the pool size.
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci  4. Driver locking
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ci  Simple spinlocks are used around the TX and RX queue mechanisms.
1848c2ecf20Sopenharmony_ci  Anyone with a faster, working method is welcome to implement it.
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci  The adapter command queue is protected with a spinlock. We always
1878c2ecf20Sopenharmony_ci  wait for commands to complete.
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci  A more complex form of locking is used around parts of the VC open
1908c2ecf20Sopenharmony_ci  and close functions. There are three reasons for a lock: 1. we need
1918c2ecf20Sopenharmony_ci  to do atomic rate reservation and release (not used yet), 2. Opening
1928c2ecf20Sopenharmony_ci  sometimes involves two adapter commands which must not be separated
1938c2ecf20Sopenharmony_ci  by another command on the same VC, 3. the changes to RX pool size
1948c2ecf20Sopenharmony_ci  must be atomic. The lock needs to work over context switches, so we
1958c2ecf20Sopenharmony_ci  use a semaphore.
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci  III Hardware Features and Microcode Bugs
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_ci  1. Byte Ordering
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_ci  *%^"$&%^$*&^"$(%^$#&^%$(&#%$*(&^#%!"!"!*!
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci  2. Memory access
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci  All structures that are not accessed using DMA must be 4-byte
2068c2ecf20Sopenharmony_ci  aligned (not a problem) and must not cross 4MB boundaries.
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci  There is a DMA memory hole at E0000000-E00000FF (groan).
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci  TX fragments (DMA read) must not cross 4MB boundaries (would be 16MB
2118c2ecf20Sopenharmony_ci  but for a hardware bug).
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci  RX buffers (DMA write) must not cross 16MB boundaries and must
2148c2ecf20Sopenharmony_ci  include spare trailing bytes up to the next 4-byte boundary; they
2158c2ecf20Sopenharmony_ci  will be written with rubbish.
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci  The PLX likes to prefetch; if reading up to 4 u32 past the end of
2188c2ecf20Sopenharmony_ci  each TX fragment is not a problem, then TX can be made to go a
2198c2ecf20Sopenharmony_ci  little faster by passing a flag at init that disables a prefetch
2208c2ecf20Sopenharmony_ci  workaround. We do not pass this flag. (new microcode only)
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci  Now we:
2238c2ecf20Sopenharmony_ci  . Note that alloc_skb rounds up size to a 16byte boundary.
2248c2ecf20Sopenharmony_ci  . Ensure all areas do not traverse 4MB boundaries.
2258c2ecf20Sopenharmony_ci  . Ensure all areas do not start at a E00000xx bus address.
2268c2ecf20Sopenharmony_ci  (I cannot be certain, but this may always hold with Linux)
2278c2ecf20Sopenharmony_ci  . Make all failures cause a loud message.
2288c2ecf20Sopenharmony_ci  . Discard non-conforming SKBs (causes TX failure or RX fill delay).
2298c2ecf20Sopenharmony_ci  . Discard non-conforming TX fragment descriptors (the TX fails).
2308c2ecf20Sopenharmony_ci  In the future we could:
2318c2ecf20Sopenharmony_ci  . Allow RX areas that traverse 4MB (but not 16MB) boundaries.
2328c2ecf20Sopenharmony_ci  . Segment TX areas into some/more fragments, when necessary.
2338c2ecf20Sopenharmony_ci  . Relax checks for non-DMA items (ignore hole).
2348c2ecf20Sopenharmony_ci  . Give scatter-gather (iovec) requirements using ???. (?)
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci  3. VC close is broken (only for new microcode)
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci  The VC close adapter microcode command fails to do anything if any
2398c2ecf20Sopenharmony_ci  frames have been received on the VC but none have been transmitted.
2408c2ecf20Sopenharmony_ci  Frames continue to be reassembled and passed (with IRQ) to the
2418c2ecf20Sopenharmony_ci  driver.
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci  IV To Do List
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci  . Fix bugs!
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci  . Timer code may be broken.
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci  . Deal with buggy VC close (somehow) in microcode 12.
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci  . Handle interrupted and/or non-blocking writes - is this a job for
2528c2ecf20Sopenharmony_ci    the protocol layer?
2538c2ecf20Sopenharmony_ci
2548c2ecf20Sopenharmony_ci  . Add code to break up TX fragments when they span 4MB boundaries.
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci  . Add SUNI phy layer (need to know where SUNI lives on card).
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci  . Implement a tx_alloc fn to (a) satisfy TX alignment etc. and (b)
2598c2ecf20Sopenharmony_ci    leave extra headroom space for Ambassador TX descriptors.
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci  . Understand these elements of struct atm_vcc: recvq (proto?),
2628c2ecf20Sopenharmony_ci    sleep, callback, listenq, backlog_quota, reply and user_back.
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci  . Adjust TX/RX skb allocation to favour IP with LANE/CLIP (configurable).
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci  . Impose a TX-pending limit (2?) on each VC, help avoid TX q overflow.
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_ci  . Decide whether RX buffer recycling is or can be made completely safe;
2698c2ecf20Sopenharmony_ci    turn it back on. It looks like Werner is going to axe this.
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci  . Implement QoS changes on open VCs (involves extracting parts of VC open
2728c2ecf20Sopenharmony_ci    and close into separate functions and using them to make changes).
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci  . Hack on command queue so that someone can issue multiple commands and wait
2758c2ecf20Sopenharmony_ci    on the last one (OR only "no-op" or "wait" commands are waited for).
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci  . Eliminate need for while-schedule around do_command.
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci*/
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_cistatic void do_housekeeping (struct timer_list *t);
2828c2ecf20Sopenharmony_ci/********** globals **********/
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_cistatic unsigned short debug = 0;
2858c2ecf20Sopenharmony_cistatic unsigned int cmds = 8;
2868c2ecf20Sopenharmony_cistatic unsigned int txs = 32;
2878c2ecf20Sopenharmony_cistatic unsigned int rxs[NUM_RX_POOLS] = { 64, 64, 64, 64 };
2888c2ecf20Sopenharmony_cistatic unsigned int rxs_bs[NUM_RX_POOLS] = { 4080, 12240, 36720, 65535 };
2898c2ecf20Sopenharmony_cistatic unsigned int rx_lats = 7;
2908c2ecf20Sopenharmony_cistatic unsigned char pci_lat = 0;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_cistatic const unsigned long onegigmask = -1 << 30;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci/********** access to adapter **********/
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic inline void wr_plain (const amb_dev * dev, size_t addr, u32 data) {
2978c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_REGS, "wr: %08zx <- %08x", addr, data);
2988c2ecf20Sopenharmony_ci#ifdef AMB_MMIO
2998c2ecf20Sopenharmony_ci  dev->membase[addr / sizeof(u32)] = data;
3008c2ecf20Sopenharmony_ci#else
3018c2ecf20Sopenharmony_ci  outl (data, dev->iobase + addr);
3028c2ecf20Sopenharmony_ci#endif
3038c2ecf20Sopenharmony_ci}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cistatic inline u32 rd_plain (const amb_dev * dev, size_t addr) {
3068c2ecf20Sopenharmony_ci#ifdef AMB_MMIO
3078c2ecf20Sopenharmony_ci  u32 data = dev->membase[addr / sizeof(u32)];
3088c2ecf20Sopenharmony_ci#else
3098c2ecf20Sopenharmony_ci  u32 data = inl (dev->iobase + addr);
3108c2ecf20Sopenharmony_ci#endif
3118c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_REGS, "rd: %08zx -> %08x", addr, data);
3128c2ecf20Sopenharmony_ci  return data;
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ci
3158c2ecf20Sopenharmony_cistatic inline void wr_mem (const amb_dev * dev, size_t addr, u32 data) {
3168c2ecf20Sopenharmony_ci  __be32 be = cpu_to_be32 (data);
3178c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_REGS, "wr: %08zx <- %08x b[%08x]", addr, data, be);
3188c2ecf20Sopenharmony_ci#ifdef AMB_MMIO
3198c2ecf20Sopenharmony_ci  dev->membase[addr / sizeof(u32)] = be;
3208c2ecf20Sopenharmony_ci#else
3218c2ecf20Sopenharmony_ci  outl (be, dev->iobase + addr);
3228c2ecf20Sopenharmony_ci#endif
3238c2ecf20Sopenharmony_ci}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_cistatic inline u32 rd_mem (const amb_dev * dev, size_t addr) {
3268c2ecf20Sopenharmony_ci#ifdef AMB_MMIO
3278c2ecf20Sopenharmony_ci  __be32 be = dev->membase[addr / sizeof(u32)];
3288c2ecf20Sopenharmony_ci#else
3298c2ecf20Sopenharmony_ci  __be32 be = inl (dev->iobase + addr);
3308c2ecf20Sopenharmony_ci#endif
3318c2ecf20Sopenharmony_ci  u32 data = be32_to_cpu (be);
3328c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_REGS, "rd: %08zx -> %08x b[%08x]", addr, data, be);
3338c2ecf20Sopenharmony_ci  return data;
3348c2ecf20Sopenharmony_ci}
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci/********** dump routines **********/
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cistatic inline void dump_registers (const amb_dev * dev) {
3398c2ecf20Sopenharmony_ci#ifdef DEBUG_AMBASSADOR
3408c2ecf20Sopenharmony_ci  if (debug & DBG_REGS) {
3418c2ecf20Sopenharmony_ci    size_t i;
3428c2ecf20Sopenharmony_ci    PRINTD (DBG_REGS, "reading PLX control: ");
3438c2ecf20Sopenharmony_ci    for (i = 0x00; i < 0x30; i += sizeof(u32))
3448c2ecf20Sopenharmony_ci      rd_mem (dev, i);
3458c2ecf20Sopenharmony_ci    PRINTD (DBG_REGS, "reading mailboxes: ");
3468c2ecf20Sopenharmony_ci    for (i = 0x40; i < 0x60; i += sizeof(u32))
3478c2ecf20Sopenharmony_ci      rd_mem (dev, i);
3488c2ecf20Sopenharmony_ci    PRINTD (DBG_REGS, "reading doorb irqev irqen reset:");
3498c2ecf20Sopenharmony_ci    for (i = 0x60; i < 0x70; i += sizeof(u32))
3508c2ecf20Sopenharmony_ci      rd_mem (dev, i);
3518c2ecf20Sopenharmony_ci  }
3528c2ecf20Sopenharmony_ci#else
3538c2ecf20Sopenharmony_ci  (void) dev;
3548c2ecf20Sopenharmony_ci#endif
3558c2ecf20Sopenharmony_ci  return;
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_cistatic inline void dump_loader_block (volatile loader_block * lb) {
3598c2ecf20Sopenharmony_ci#ifdef DEBUG_AMBASSADOR
3608c2ecf20Sopenharmony_ci  unsigned int i;
3618c2ecf20Sopenharmony_ci  PRINTDB (DBG_LOAD, "lb @ %p; res: %d, cmd: %d, pay:",
3628c2ecf20Sopenharmony_ci	   lb, be32_to_cpu (lb->result), be32_to_cpu (lb->command));
3638c2ecf20Sopenharmony_ci  for (i = 0; i < MAX_COMMAND_DATA; ++i)
3648c2ecf20Sopenharmony_ci    PRINTDM (DBG_LOAD, " %08x", be32_to_cpu (lb->payload.data[i]));
3658c2ecf20Sopenharmony_ci  PRINTDE (DBG_LOAD, ", vld: %08x", be32_to_cpu (lb->valid));
3668c2ecf20Sopenharmony_ci#else
3678c2ecf20Sopenharmony_ci  (void) lb;
3688c2ecf20Sopenharmony_ci#endif
3698c2ecf20Sopenharmony_ci  return;
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic inline void dump_command (command * cmd) {
3738c2ecf20Sopenharmony_ci#ifdef DEBUG_AMBASSADOR
3748c2ecf20Sopenharmony_ci  unsigned int i;
3758c2ecf20Sopenharmony_ci  PRINTDB (DBG_CMD, "cmd @ %p, req: %08x, pars:",
3768c2ecf20Sopenharmony_ci	   cmd, /*be32_to_cpu*/ (cmd->request));
3778c2ecf20Sopenharmony_ci  for (i = 0; i < 3; ++i)
3788c2ecf20Sopenharmony_ci    PRINTDM (DBG_CMD, " %08x", /*be32_to_cpu*/ (cmd->args.par[i]));
3798c2ecf20Sopenharmony_ci  PRINTDE (DBG_CMD, "");
3808c2ecf20Sopenharmony_ci#else
3818c2ecf20Sopenharmony_ci  (void) cmd;
3828c2ecf20Sopenharmony_ci#endif
3838c2ecf20Sopenharmony_ci  return;
3848c2ecf20Sopenharmony_ci}
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic inline void dump_skb (char * prefix, unsigned int vc, struct sk_buff * skb) {
3878c2ecf20Sopenharmony_ci#ifdef DEBUG_AMBASSADOR
3888c2ecf20Sopenharmony_ci  unsigned int i;
3898c2ecf20Sopenharmony_ci  unsigned char * data = skb->data;
3908c2ecf20Sopenharmony_ci  PRINTDB (DBG_DATA, "%s(%u) ", prefix, vc);
3918c2ecf20Sopenharmony_ci  for (i=0; i<skb->len && i < 256;i++)
3928c2ecf20Sopenharmony_ci    PRINTDM (DBG_DATA, "%02x ", data[i]);
3938c2ecf20Sopenharmony_ci  PRINTDE (DBG_DATA,"");
3948c2ecf20Sopenharmony_ci#else
3958c2ecf20Sopenharmony_ci  (void) prefix;
3968c2ecf20Sopenharmony_ci  (void) vc;
3978c2ecf20Sopenharmony_ci  (void) skb;
3988c2ecf20Sopenharmony_ci#endif
3998c2ecf20Sopenharmony_ci  return;
4008c2ecf20Sopenharmony_ci}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci/********** check memory areas for use by Ambassador **********/
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci/* see limitations under Hardware Features */
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_cistatic int check_area (void * start, size_t length) {
4078c2ecf20Sopenharmony_ci  // assumes length > 0
4088c2ecf20Sopenharmony_ci  const u32 fourmegmask = -1 << 22;
4098c2ecf20Sopenharmony_ci  const u32 twofivesixmask = -1 << 8;
4108c2ecf20Sopenharmony_ci  const u32 starthole = 0xE0000000;
4118c2ecf20Sopenharmony_ci  u32 startaddress = virt_to_bus (start);
4128c2ecf20Sopenharmony_ci  u32 lastaddress = startaddress+length-1;
4138c2ecf20Sopenharmony_ci  if ((startaddress ^ lastaddress) & fourmegmask ||
4148c2ecf20Sopenharmony_ci      (startaddress & twofivesixmask) == starthole) {
4158c2ecf20Sopenharmony_ci    PRINTK (KERN_ERR, "check_area failure: [%x,%x] - mail maintainer!",
4168c2ecf20Sopenharmony_ci	    startaddress, lastaddress);
4178c2ecf20Sopenharmony_ci    return -1;
4188c2ecf20Sopenharmony_ci  } else {
4198c2ecf20Sopenharmony_ci    return 0;
4208c2ecf20Sopenharmony_ci  }
4218c2ecf20Sopenharmony_ci}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci/********** free an skb (as per ATM device driver documentation) **********/
4248c2ecf20Sopenharmony_ci
4258c2ecf20Sopenharmony_cistatic void amb_kfree_skb (struct sk_buff * skb) {
4268c2ecf20Sopenharmony_ci  if (ATM_SKB(skb)->vcc->pop) {
4278c2ecf20Sopenharmony_ci    ATM_SKB(skb)->vcc->pop (ATM_SKB(skb)->vcc, skb);
4288c2ecf20Sopenharmony_ci  } else {
4298c2ecf20Sopenharmony_ci    dev_kfree_skb_any (skb);
4308c2ecf20Sopenharmony_ci  }
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci/********** TX completion **********/
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic void tx_complete (amb_dev * dev, tx_out * tx) {
4368c2ecf20Sopenharmony_ci  tx_simple * tx_descr = bus_to_virt (tx->handle);
4378c2ecf20Sopenharmony_ci  struct sk_buff * skb = tx_descr->skb;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_TX, "tx_complete %p %p", dev, tx);
4408c2ecf20Sopenharmony_ci
4418c2ecf20Sopenharmony_ci  // VC layer stats
4428c2ecf20Sopenharmony_ci  atomic_inc(&ATM_SKB(skb)->vcc->stats->tx);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ci  // free the descriptor
4458c2ecf20Sopenharmony_ci  kfree (tx_descr);
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci  // free the skb
4488c2ecf20Sopenharmony_ci  amb_kfree_skb (skb);
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci  dev->stats.tx_ok++;
4518c2ecf20Sopenharmony_ci  return;
4528c2ecf20Sopenharmony_ci}
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci/********** RX completion **********/
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_cistatic void rx_complete (amb_dev * dev, rx_out * rx) {
4578c2ecf20Sopenharmony_ci  struct sk_buff * skb = bus_to_virt (rx->handle);
4588c2ecf20Sopenharmony_ci  u16 vc = be16_to_cpu (rx->vc);
4598c2ecf20Sopenharmony_ci  // unused: u16 lec_id = be16_to_cpu (rx->lec_id);
4608c2ecf20Sopenharmony_ci  u16 status = be16_to_cpu (rx->status);
4618c2ecf20Sopenharmony_ci  u16 rx_len = be16_to_cpu (rx->length);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_RX, "rx_complete %p %p (len=%hu)", dev, rx, rx_len);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci  // XXX move this in and add to VC stats ???
4668c2ecf20Sopenharmony_ci  if (!status) {
4678c2ecf20Sopenharmony_ci    struct atm_vcc * atm_vcc = dev->rxer[vc];
4688c2ecf20Sopenharmony_ci    dev->stats.rx.ok++;
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci    if (atm_vcc) {
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci      if (rx_len <= atm_vcc->qos.rxtp.max_sdu) {
4738c2ecf20Sopenharmony_ci
4748c2ecf20Sopenharmony_ci	if (atm_charge (atm_vcc, skb->truesize)) {
4758c2ecf20Sopenharmony_ci
4768c2ecf20Sopenharmony_ci	  // prepare socket buffer
4778c2ecf20Sopenharmony_ci	  ATM_SKB(skb)->vcc = atm_vcc;
4788c2ecf20Sopenharmony_ci	  skb_put (skb, rx_len);
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	  dump_skb ("<<<", vc, skb);
4818c2ecf20Sopenharmony_ci
4828c2ecf20Sopenharmony_ci	  // VC layer stats
4838c2ecf20Sopenharmony_ci	  atomic_inc(&atm_vcc->stats->rx);
4848c2ecf20Sopenharmony_ci	  __net_timestamp(skb);
4858c2ecf20Sopenharmony_ci	  // end of our responsibility
4868c2ecf20Sopenharmony_ci	  atm_vcc->push (atm_vcc, skb);
4878c2ecf20Sopenharmony_ci	  return;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	} else {
4908c2ecf20Sopenharmony_ci	  // someone fix this (message), please!
4918c2ecf20Sopenharmony_ci	  PRINTD (DBG_INFO|DBG_RX, "dropped thanks to atm_charge (vc %hu, truesize %u)", vc, skb->truesize);
4928c2ecf20Sopenharmony_ci	  // drop stats incremented in atm_charge
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci
4958c2ecf20Sopenharmony_ci      } else {
4968c2ecf20Sopenharmony_ci      	PRINTK (KERN_INFO, "dropped over-size frame");
4978c2ecf20Sopenharmony_ci	// should we count this?
4988c2ecf20Sopenharmony_ci	atomic_inc(&atm_vcc->stats->rx_drop);
4998c2ecf20Sopenharmony_ci      }
5008c2ecf20Sopenharmony_ci
5018c2ecf20Sopenharmony_ci    } else {
5028c2ecf20Sopenharmony_ci      PRINTD (DBG_WARN|DBG_RX, "got frame but RX closed for channel %hu", vc);
5038c2ecf20Sopenharmony_ci      // this is an adapter bug, only in new version of microcode
5048c2ecf20Sopenharmony_ci    }
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci  } else {
5078c2ecf20Sopenharmony_ci    dev->stats.rx.error++;
5088c2ecf20Sopenharmony_ci    if (status & CRC_ERR)
5098c2ecf20Sopenharmony_ci      dev->stats.rx.badcrc++;
5108c2ecf20Sopenharmony_ci    if (status & LEN_ERR)
5118c2ecf20Sopenharmony_ci      dev->stats.rx.toolong++;
5128c2ecf20Sopenharmony_ci    if (status & ABORT_ERR)
5138c2ecf20Sopenharmony_ci      dev->stats.rx.aborted++;
5148c2ecf20Sopenharmony_ci    if (status & UNUSED_ERR)
5158c2ecf20Sopenharmony_ci      dev->stats.rx.unused++;
5168c2ecf20Sopenharmony_ci  }
5178c2ecf20Sopenharmony_ci
5188c2ecf20Sopenharmony_ci  dev_kfree_skb_any (skb);
5198c2ecf20Sopenharmony_ci  return;
5208c2ecf20Sopenharmony_ci}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_ci/*
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci  Note on queue handling.
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci  Here "give" and "take" refer to queue entries and a queue (pair)
5278c2ecf20Sopenharmony_ci  rather than frames to or from the host or adapter. Empty frame
5288c2ecf20Sopenharmony_ci  buffers are given to the RX queue pair and returned unused or
5298c2ecf20Sopenharmony_ci  containing RX frames. TX frames (well, pointers to TX fragment
5308c2ecf20Sopenharmony_ci  lists) are given to the TX queue pair, completions are returned.
5318c2ecf20Sopenharmony_ci
5328c2ecf20Sopenharmony_ci*/
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci/********** command queue **********/
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci// I really don't like this, but it's the best I can do at the moment
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci// also, the callers are responsible for byte order as the microcode
5398c2ecf20Sopenharmony_ci// sometimes does 16-bit accesses (yuk yuk yuk)
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_cistatic int command_do (amb_dev * dev, command * cmd) {
5428c2ecf20Sopenharmony_ci  amb_cq * cq = &dev->cq;
5438c2ecf20Sopenharmony_ci  volatile amb_cq_ptrs * ptrs = &cq->ptrs;
5448c2ecf20Sopenharmony_ci  command * my_slot;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_CMD, "command_do %p", dev);
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_ci  if (test_bit (dead, &dev->flags))
5498c2ecf20Sopenharmony_ci    return 0;
5508c2ecf20Sopenharmony_ci
5518c2ecf20Sopenharmony_ci  spin_lock (&cq->lock);
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci  // if not full...
5548c2ecf20Sopenharmony_ci  if (cq->pending < cq->maximum) {
5558c2ecf20Sopenharmony_ci    // remember my slot for later
5568c2ecf20Sopenharmony_ci    my_slot = ptrs->in;
5578c2ecf20Sopenharmony_ci    PRINTD (DBG_CMD, "command in slot %p", my_slot);
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci    dump_command (cmd);
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci    // copy command in
5628c2ecf20Sopenharmony_ci    *ptrs->in = *cmd;
5638c2ecf20Sopenharmony_ci    cq->pending++;
5648c2ecf20Sopenharmony_ci    ptrs->in = NEXTQ (ptrs->in, ptrs->start, ptrs->limit);
5658c2ecf20Sopenharmony_ci
5668c2ecf20Sopenharmony_ci    // mail the command
5678c2ecf20Sopenharmony_ci    wr_mem (dev, offsetof(amb_mem, mb.adapter.cmd_address), virt_to_bus (ptrs->in));
5688c2ecf20Sopenharmony_ci
5698c2ecf20Sopenharmony_ci    if (cq->pending > cq->high)
5708c2ecf20Sopenharmony_ci      cq->high = cq->pending;
5718c2ecf20Sopenharmony_ci    spin_unlock (&cq->lock);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci    // these comments were in a while-loop before, msleep removes the loop
5748c2ecf20Sopenharmony_ci    // go to sleep
5758c2ecf20Sopenharmony_ci    // PRINTD (DBG_CMD, "wait: sleeping %lu for command", timeout);
5768c2ecf20Sopenharmony_ci    msleep(cq->pending);
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci    // wait for my slot to be reached (all waiters are here or above, until...)
5798c2ecf20Sopenharmony_ci    while (ptrs->out != my_slot) {
5808c2ecf20Sopenharmony_ci      PRINTD (DBG_CMD, "wait: command slot (now at %p)", ptrs->out);
5818c2ecf20Sopenharmony_ci      set_current_state(TASK_UNINTERRUPTIBLE);
5828c2ecf20Sopenharmony_ci      schedule();
5838c2ecf20Sopenharmony_ci    }
5848c2ecf20Sopenharmony_ci
5858c2ecf20Sopenharmony_ci    // wait on my slot (... one gets to its slot, and... )
5868c2ecf20Sopenharmony_ci    while (ptrs->out->request != cpu_to_be32 (SRB_COMPLETE)) {
5878c2ecf20Sopenharmony_ci      PRINTD (DBG_CMD, "wait: command slot completion");
5888c2ecf20Sopenharmony_ci      set_current_state(TASK_UNINTERRUPTIBLE);
5898c2ecf20Sopenharmony_ci      schedule();
5908c2ecf20Sopenharmony_ci    }
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci    PRINTD (DBG_CMD, "command complete");
5938c2ecf20Sopenharmony_ci    // update queue (... moves the queue along to the next slot)
5948c2ecf20Sopenharmony_ci    spin_lock (&cq->lock);
5958c2ecf20Sopenharmony_ci    cq->pending--;
5968c2ecf20Sopenharmony_ci    // copy command out
5978c2ecf20Sopenharmony_ci    *cmd = *ptrs->out;
5988c2ecf20Sopenharmony_ci    ptrs->out = NEXTQ (ptrs->out, ptrs->start, ptrs->limit);
5998c2ecf20Sopenharmony_ci    spin_unlock (&cq->lock);
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci    return 0;
6028c2ecf20Sopenharmony_ci  } else {
6038c2ecf20Sopenharmony_ci    cq->filled++;
6048c2ecf20Sopenharmony_ci    spin_unlock (&cq->lock);
6058c2ecf20Sopenharmony_ci    return -EAGAIN;
6068c2ecf20Sopenharmony_ci  }
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci}
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci/********** TX queue pair **********/
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_cistatic int tx_give (amb_dev * dev, tx_in * tx) {
6138c2ecf20Sopenharmony_ci  amb_txq * txq = &dev->txq;
6148c2ecf20Sopenharmony_ci  unsigned long flags;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_TX, "tx_give %p", dev);
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci  if (test_bit (dead, &dev->flags))
6198c2ecf20Sopenharmony_ci    return 0;
6208c2ecf20Sopenharmony_ci
6218c2ecf20Sopenharmony_ci  spin_lock_irqsave (&txq->lock, flags);
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci  if (txq->pending < txq->maximum) {
6248c2ecf20Sopenharmony_ci    PRINTD (DBG_TX, "TX in slot %p", txq->in.ptr);
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci    *txq->in.ptr = *tx;
6278c2ecf20Sopenharmony_ci    txq->pending++;
6288c2ecf20Sopenharmony_ci    txq->in.ptr = NEXTQ (txq->in.ptr, txq->in.start, txq->in.limit);
6298c2ecf20Sopenharmony_ci    // hand over the TX and ring the bell
6308c2ecf20Sopenharmony_ci    wr_mem (dev, offsetof(amb_mem, mb.adapter.tx_address), virt_to_bus (txq->in.ptr));
6318c2ecf20Sopenharmony_ci    wr_mem (dev, offsetof(amb_mem, doorbell), TX_FRAME);
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci    if (txq->pending > txq->high)
6348c2ecf20Sopenharmony_ci      txq->high = txq->pending;
6358c2ecf20Sopenharmony_ci    spin_unlock_irqrestore (&txq->lock, flags);
6368c2ecf20Sopenharmony_ci    return 0;
6378c2ecf20Sopenharmony_ci  } else {
6388c2ecf20Sopenharmony_ci    txq->filled++;
6398c2ecf20Sopenharmony_ci    spin_unlock_irqrestore (&txq->lock, flags);
6408c2ecf20Sopenharmony_ci    return -EAGAIN;
6418c2ecf20Sopenharmony_ci  }
6428c2ecf20Sopenharmony_ci}
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_cistatic int tx_take (amb_dev * dev) {
6458c2ecf20Sopenharmony_ci  amb_txq * txq = &dev->txq;
6468c2ecf20Sopenharmony_ci  unsigned long flags;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_TX, "tx_take %p", dev);
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci  spin_lock_irqsave (&txq->lock, flags);
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci  if (txq->pending && txq->out.ptr->handle) {
6538c2ecf20Sopenharmony_ci    // deal with TX completion
6548c2ecf20Sopenharmony_ci    tx_complete (dev, txq->out.ptr);
6558c2ecf20Sopenharmony_ci    // mark unused again
6568c2ecf20Sopenharmony_ci    txq->out.ptr->handle = 0;
6578c2ecf20Sopenharmony_ci    // remove item
6588c2ecf20Sopenharmony_ci    txq->pending--;
6598c2ecf20Sopenharmony_ci    txq->out.ptr = NEXTQ (txq->out.ptr, txq->out.start, txq->out.limit);
6608c2ecf20Sopenharmony_ci
6618c2ecf20Sopenharmony_ci    spin_unlock_irqrestore (&txq->lock, flags);
6628c2ecf20Sopenharmony_ci    return 0;
6638c2ecf20Sopenharmony_ci  } else {
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci    spin_unlock_irqrestore (&txq->lock, flags);
6668c2ecf20Sopenharmony_ci    return -1;
6678c2ecf20Sopenharmony_ci  }
6688c2ecf20Sopenharmony_ci}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci/********** RX queue pairs **********/
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_cistatic int rx_give (amb_dev * dev, rx_in * rx, unsigned char pool) {
6738c2ecf20Sopenharmony_ci  amb_rxq * rxq = &dev->rxq[pool];
6748c2ecf20Sopenharmony_ci  unsigned long flags;
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_RX, "rx_give %p[%hu]", dev, pool);
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci  spin_lock_irqsave (&rxq->lock, flags);
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci  if (rxq->pending < rxq->maximum) {
6818c2ecf20Sopenharmony_ci    PRINTD (DBG_RX, "RX in slot %p", rxq->in.ptr);
6828c2ecf20Sopenharmony_ci
6838c2ecf20Sopenharmony_ci    *rxq->in.ptr = *rx;
6848c2ecf20Sopenharmony_ci    rxq->pending++;
6858c2ecf20Sopenharmony_ci    rxq->in.ptr = NEXTQ (rxq->in.ptr, rxq->in.start, rxq->in.limit);
6868c2ecf20Sopenharmony_ci    // hand over the RX buffer
6878c2ecf20Sopenharmony_ci    wr_mem (dev, offsetof(amb_mem, mb.adapter.rx_address[pool]), virt_to_bus (rxq->in.ptr));
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci    spin_unlock_irqrestore (&rxq->lock, flags);
6908c2ecf20Sopenharmony_ci    return 0;
6918c2ecf20Sopenharmony_ci  } else {
6928c2ecf20Sopenharmony_ci    spin_unlock_irqrestore (&rxq->lock, flags);
6938c2ecf20Sopenharmony_ci    return -1;
6948c2ecf20Sopenharmony_ci  }
6958c2ecf20Sopenharmony_ci}
6968c2ecf20Sopenharmony_ci
6978c2ecf20Sopenharmony_cistatic int rx_take (amb_dev * dev, unsigned char pool) {
6988c2ecf20Sopenharmony_ci  amb_rxq * rxq = &dev->rxq[pool];
6998c2ecf20Sopenharmony_ci  unsigned long flags;
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_RX, "rx_take %p[%hu]", dev, pool);
7028c2ecf20Sopenharmony_ci
7038c2ecf20Sopenharmony_ci  spin_lock_irqsave (&rxq->lock, flags);
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci  if (rxq->pending && (rxq->out.ptr->status || rxq->out.ptr->length)) {
7068c2ecf20Sopenharmony_ci    // deal with RX completion
7078c2ecf20Sopenharmony_ci    rx_complete (dev, rxq->out.ptr);
7088c2ecf20Sopenharmony_ci    // mark unused again
7098c2ecf20Sopenharmony_ci    rxq->out.ptr->status = 0;
7108c2ecf20Sopenharmony_ci    rxq->out.ptr->length = 0;
7118c2ecf20Sopenharmony_ci    // remove item
7128c2ecf20Sopenharmony_ci    rxq->pending--;
7138c2ecf20Sopenharmony_ci    rxq->out.ptr = NEXTQ (rxq->out.ptr, rxq->out.start, rxq->out.limit);
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci    if (rxq->pending < rxq->low)
7168c2ecf20Sopenharmony_ci      rxq->low = rxq->pending;
7178c2ecf20Sopenharmony_ci    spin_unlock_irqrestore (&rxq->lock, flags);
7188c2ecf20Sopenharmony_ci    return 0;
7198c2ecf20Sopenharmony_ci  } else {
7208c2ecf20Sopenharmony_ci    if (!rxq->pending && rxq->buffers_wanted)
7218c2ecf20Sopenharmony_ci      rxq->emptied++;
7228c2ecf20Sopenharmony_ci    spin_unlock_irqrestore (&rxq->lock, flags);
7238c2ecf20Sopenharmony_ci    return -1;
7248c2ecf20Sopenharmony_ci  }
7258c2ecf20Sopenharmony_ci}
7268c2ecf20Sopenharmony_ci
7278c2ecf20Sopenharmony_ci/********** RX Pool handling **********/
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci/* pre: buffers_wanted = 0, post: pending = 0 */
7308c2ecf20Sopenharmony_cistatic void drain_rx_pool (amb_dev * dev, unsigned char pool) {
7318c2ecf20Sopenharmony_ci  amb_rxq * rxq = &dev->rxq[pool];
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_POOL, "drain_rx_pool %p %hu", dev, pool);
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci  if (test_bit (dead, &dev->flags))
7368c2ecf20Sopenharmony_ci    return;
7378c2ecf20Sopenharmony_ci
7388c2ecf20Sopenharmony_ci  /* we are not quite like the fill pool routines as we cannot just
7398c2ecf20Sopenharmony_ci     remove one buffer, we have to remove all of them, but we might as
7408c2ecf20Sopenharmony_ci     well pretend... */
7418c2ecf20Sopenharmony_ci  if (rxq->pending > rxq->buffers_wanted) {
7428c2ecf20Sopenharmony_ci    command cmd;
7438c2ecf20Sopenharmony_ci    cmd.request = cpu_to_be32 (SRB_FLUSH_BUFFER_Q);
7448c2ecf20Sopenharmony_ci    cmd.args.flush.flags = cpu_to_be32 (pool << SRB_POOL_SHIFT);
7458c2ecf20Sopenharmony_ci    while (command_do (dev, &cmd))
7468c2ecf20Sopenharmony_ci      schedule();
7478c2ecf20Sopenharmony_ci    /* the pool may also be emptied via the interrupt handler */
7488c2ecf20Sopenharmony_ci    while (rxq->pending > rxq->buffers_wanted)
7498c2ecf20Sopenharmony_ci      if (rx_take (dev, pool))
7508c2ecf20Sopenharmony_ci	schedule();
7518c2ecf20Sopenharmony_ci  }
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci  return;
7548c2ecf20Sopenharmony_ci}
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_cistatic void drain_rx_pools (amb_dev * dev) {
7578c2ecf20Sopenharmony_ci  unsigned char pool;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_POOL, "drain_rx_pools %p", dev);
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci  for (pool = 0; pool < NUM_RX_POOLS; ++pool)
7628c2ecf20Sopenharmony_ci    drain_rx_pool (dev, pool);
7638c2ecf20Sopenharmony_ci}
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_cistatic void fill_rx_pool (amb_dev * dev, unsigned char pool,
7668c2ecf20Sopenharmony_ci                                 gfp_t priority)
7678c2ecf20Sopenharmony_ci{
7688c2ecf20Sopenharmony_ci  rx_in rx;
7698c2ecf20Sopenharmony_ci  amb_rxq * rxq;
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_POOL, "fill_rx_pool %p %hu %x", dev, pool, priority);
7728c2ecf20Sopenharmony_ci
7738c2ecf20Sopenharmony_ci  if (test_bit (dead, &dev->flags))
7748c2ecf20Sopenharmony_ci    return;
7758c2ecf20Sopenharmony_ci
7768c2ecf20Sopenharmony_ci  rxq = &dev->rxq[pool];
7778c2ecf20Sopenharmony_ci  while (rxq->pending < rxq->maximum && rxq->pending < rxq->buffers_wanted) {
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci    struct sk_buff * skb = alloc_skb (rxq->buffer_size, priority);
7808c2ecf20Sopenharmony_ci    if (!skb) {
7818c2ecf20Sopenharmony_ci      PRINTD (DBG_SKB|DBG_POOL, "failed to allocate skb for RX pool %hu", pool);
7828c2ecf20Sopenharmony_ci      return;
7838c2ecf20Sopenharmony_ci    }
7848c2ecf20Sopenharmony_ci    if (check_area (skb->data, skb->truesize)) {
7858c2ecf20Sopenharmony_ci      dev_kfree_skb_any (skb);
7868c2ecf20Sopenharmony_ci      return;
7878c2ecf20Sopenharmony_ci    }
7888c2ecf20Sopenharmony_ci    // cast needed as there is no %? for pointer differences
7898c2ecf20Sopenharmony_ci    PRINTD (DBG_SKB, "allocated skb at %p, head %p, area %li",
7908c2ecf20Sopenharmony_ci	    skb, skb->head, (long) skb_end_offset(skb));
7918c2ecf20Sopenharmony_ci    rx.handle = virt_to_bus (skb);
7928c2ecf20Sopenharmony_ci    rx.host_address = cpu_to_be32 (virt_to_bus (skb->data));
7938c2ecf20Sopenharmony_ci    if (rx_give (dev, &rx, pool))
7948c2ecf20Sopenharmony_ci      dev_kfree_skb_any (skb);
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci  }
7978c2ecf20Sopenharmony_ci
7988c2ecf20Sopenharmony_ci  return;
7998c2ecf20Sopenharmony_ci}
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci// top up all RX pools
8028c2ecf20Sopenharmony_cistatic void fill_rx_pools (amb_dev * dev) {
8038c2ecf20Sopenharmony_ci  unsigned char pool;
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_POOL, "fill_rx_pools %p", dev);
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci  for (pool = 0; pool < NUM_RX_POOLS; ++pool)
8088c2ecf20Sopenharmony_ci    fill_rx_pool (dev, pool, GFP_ATOMIC);
8098c2ecf20Sopenharmony_ci
8108c2ecf20Sopenharmony_ci  return;
8118c2ecf20Sopenharmony_ci}
8128c2ecf20Sopenharmony_ci
8138c2ecf20Sopenharmony_ci/********** enable host interrupts **********/
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_cistatic void interrupts_on (amb_dev * dev) {
8168c2ecf20Sopenharmony_ci  wr_plain (dev, offsetof(amb_mem, interrupt_control),
8178c2ecf20Sopenharmony_ci	    rd_plain (dev, offsetof(amb_mem, interrupt_control))
8188c2ecf20Sopenharmony_ci	    | AMB_INTERRUPT_BITS);
8198c2ecf20Sopenharmony_ci}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci/********** disable host interrupts **********/
8228c2ecf20Sopenharmony_ci
8238c2ecf20Sopenharmony_cistatic void interrupts_off (amb_dev * dev) {
8248c2ecf20Sopenharmony_ci  wr_plain (dev, offsetof(amb_mem, interrupt_control),
8258c2ecf20Sopenharmony_ci	    rd_plain (dev, offsetof(amb_mem, interrupt_control))
8268c2ecf20Sopenharmony_ci	    &~ AMB_INTERRUPT_BITS);
8278c2ecf20Sopenharmony_ci}
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci/********** interrupt handling **********/
8308c2ecf20Sopenharmony_ci
8318c2ecf20Sopenharmony_cistatic irqreturn_t interrupt_handler(int irq, void *dev_id) {
8328c2ecf20Sopenharmony_ci  amb_dev * dev = dev_id;
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci  PRINTD (DBG_IRQ|DBG_FLOW, "interrupt_handler: %p", dev_id);
8358c2ecf20Sopenharmony_ci
8368c2ecf20Sopenharmony_ci  {
8378c2ecf20Sopenharmony_ci    u32 interrupt = rd_plain (dev, offsetof(amb_mem, interrupt));
8388c2ecf20Sopenharmony_ci
8398c2ecf20Sopenharmony_ci    // for us or someone else sharing the same interrupt
8408c2ecf20Sopenharmony_ci    if (!interrupt) {
8418c2ecf20Sopenharmony_ci      PRINTD (DBG_IRQ, "irq not for me: %d", irq);
8428c2ecf20Sopenharmony_ci      return IRQ_NONE;
8438c2ecf20Sopenharmony_ci    }
8448c2ecf20Sopenharmony_ci
8458c2ecf20Sopenharmony_ci    // definitely for us
8468c2ecf20Sopenharmony_ci    PRINTD (DBG_IRQ, "FYI: interrupt was %08x", interrupt);
8478c2ecf20Sopenharmony_ci    wr_plain (dev, offsetof(amb_mem, interrupt), -1);
8488c2ecf20Sopenharmony_ci  }
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci  {
8518c2ecf20Sopenharmony_ci    unsigned int irq_work = 0;
8528c2ecf20Sopenharmony_ci    unsigned char pool;
8538c2ecf20Sopenharmony_ci    for (pool = 0; pool < NUM_RX_POOLS; ++pool)
8548c2ecf20Sopenharmony_ci      while (!rx_take (dev, pool))
8558c2ecf20Sopenharmony_ci	++irq_work;
8568c2ecf20Sopenharmony_ci    while (!tx_take (dev))
8578c2ecf20Sopenharmony_ci      ++irq_work;
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci    if (irq_work) {
8608c2ecf20Sopenharmony_ci      fill_rx_pools (dev);
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci      PRINTD (DBG_IRQ, "work done: %u", irq_work);
8638c2ecf20Sopenharmony_ci    } else {
8648c2ecf20Sopenharmony_ci      PRINTD (DBG_IRQ|DBG_WARN, "no work done");
8658c2ecf20Sopenharmony_ci    }
8668c2ecf20Sopenharmony_ci  }
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci  PRINTD (DBG_IRQ|DBG_FLOW, "interrupt_handler done: %p", dev_id);
8698c2ecf20Sopenharmony_ci  return IRQ_HANDLED;
8708c2ecf20Sopenharmony_ci}
8718c2ecf20Sopenharmony_ci
8728c2ecf20Sopenharmony_ci/********** make rate (not quite as much fun as Horizon) **********/
8738c2ecf20Sopenharmony_ci
8748c2ecf20Sopenharmony_cistatic int make_rate (unsigned int rate, rounding r,
8758c2ecf20Sopenharmony_ci		      u16 * bits, unsigned int * actual) {
8768c2ecf20Sopenharmony_ci  unsigned char exp = -1; // hush gcc
8778c2ecf20Sopenharmony_ci  unsigned int man = -1;  // hush gcc
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_QOS, "make_rate %u", rate);
8808c2ecf20Sopenharmony_ci
8818c2ecf20Sopenharmony_ci  // rates in cells per second, ITU format (nasty 16-bit floating-point)
8828c2ecf20Sopenharmony_ci  // given 5-bit e and 9-bit m:
8838c2ecf20Sopenharmony_ci  // rate = EITHER (1+m/2^9)*2^e    OR 0
8848c2ecf20Sopenharmony_ci  // bits = EITHER 1<<14 | e<<9 | m OR 0
8858c2ecf20Sopenharmony_ci  // (bit 15 is "reserved", bit 14 "non-zero")
8868c2ecf20Sopenharmony_ci  // smallest rate is 0 (special representation)
8878c2ecf20Sopenharmony_ci  // largest rate is (1+511/512)*2^31 = 4290772992 (< 2^32-1)
8888c2ecf20Sopenharmony_ci  // smallest non-zero rate is (1+0/512)*2^0 = 1 (> 0)
8898c2ecf20Sopenharmony_ci  // simple algorithm:
8908c2ecf20Sopenharmony_ci  // find position of top bit, this gives e
8918c2ecf20Sopenharmony_ci  // remove top bit and shift (rounding if feeling clever) by 9-e
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci  // ucode bug: please don't set bit 14! so 0 rate not representable
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci  if (rate > 0xffc00000U) {
8968c2ecf20Sopenharmony_ci    // larger than largest representable rate
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci    if (r == round_up) {
8998c2ecf20Sopenharmony_ci	return -EINVAL;
9008c2ecf20Sopenharmony_ci    } else {
9018c2ecf20Sopenharmony_ci      exp = 31;
9028c2ecf20Sopenharmony_ci      man = 511;
9038c2ecf20Sopenharmony_ci    }
9048c2ecf20Sopenharmony_ci
9058c2ecf20Sopenharmony_ci  } else if (rate) {
9068c2ecf20Sopenharmony_ci    // representable rate
9078c2ecf20Sopenharmony_ci
9088c2ecf20Sopenharmony_ci    exp = 31;
9098c2ecf20Sopenharmony_ci    man = rate;
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci    // invariant: rate = man*2^(exp-31)
9128c2ecf20Sopenharmony_ci    while (!(man & (1<<31))) {
9138c2ecf20Sopenharmony_ci      exp = exp - 1;
9148c2ecf20Sopenharmony_ci      man = man<<1;
9158c2ecf20Sopenharmony_ci    }
9168c2ecf20Sopenharmony_ci
9178c2ecf20Sopenharmony_ci    // man has top bit set
9188c2ecf20Sopenharmony_ci    // rate = (2^31+(man-2^31))*2^(exp-31)
9198c2ecf20Sopenharmony_ci    // rate = (1+(man-2^31)/2^31)*2^exp
9208c2ecf20Sopenharmony_ci    man = man<<1;
9218c2ecf20Sopenharmony_ci    man &= 0xffffffffU; // a nop on 32-bit systems
9228c2ecf20Sopenharmony_ci    // rate = (1+man/2^32)*2^exp
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci    // exp is in the range 0 to 31, man is in the range 0 to 2^32-1
9258c2ecf20Sopenharmony_ci    // time to lose significance... we want m in the range 0 to 2^9-1
9268c2ecf20Sopenharmony_ci    // rounding presents a minor problem... we first decide which way
9278c2ecf20Sopenharmony_ci    // we are rounding (based on given rounding direction and possibly
9288c2ecf20Sopenharmony_ci    // the bits of the mantissa that are to be discarded).
9298c2ecf20Sopenharmony_ci
9308c2ecf20Sopenharmony_ci    switch (r) {
9318c2ecf20Sopenharmony_ci      case round_down: {
9328c2ecf20Sopenharmony_ci	// just truncate
9338c2ecf20Sopenharmony_ci	man = man>>(32-9);
9348c2ecf20Sopenharmony_ci	break;
9358c2ecf20Sopenharmony_ci      }
9368c2ecf20Sopenharmony_ci      case round_up: {
9378c2ecf20Sopenharmony_ci	// check all bits that we are discarding
9388c2ecf20Sopenharmony_ci	if (man & (~0U>>9)) {
9398c2ecf20Sopenharmony_ci	  man = (man>>(32-9)) + 1;
9408c2ecf20Sopenharmony_ci	  if (man == (1<<9)) {
9418c2ecf20Sopenharmony_ci	    // no need to check for round up outside of range
9428c2ecf20Sopenharmony_ci	    man = 0;
9438c2ecf20Sopenharmony_ci	    exp += 1;
9448c2ecf20Sopenharmony_ci	  }
9458c2ecf20Sopenharmony_ci	} else {
9468c2ecf20Sopenharmony_ci	  man = (man>>(32-9));
9478c2ecf20Sopenharmony_ci	}
9488c2ecf20Sopenharmony_ci	break;
9498c2ecf20Sopenharmony_ci      }
9508c2ecf20Sopenharmony_ci      case round_nearest: {
9518c2ecf20Sopenharmony_ci	// check msb that we are discarding
9528c2ecf20Sopenharmony_ci	if (man & (1<<(32-9-1))) {
9538c2ecf20Sopenharmony_ci	  man = (man>>(32-9)) + 1;
9548c2ecf20Sopenharmony_ci	  if (man == (1<<9)) {
9558c2ecf20Sopenharmony_ci	    // no need to check for round up outside of range
9568c2ecf20Sopenharmony_ci	    man = 0;
9578c2ecf20Sopenharmony_ci	    exp += 1;
9588c2ecf20Sopenharmony_ci	  }
9598c2ecf20Sopenharmony_ci	} else {
9608c2ecf20Sopenharmony_ci	  man = (man>>(32-9));
9618c2ecf20Sopenharmony_ci	}
9628c2ecf20Sopenharmony_ci	break;
9638c2ecf20Sopenharmony_ci      }
9648c2ecf20Sopenharmony_ci    }
9658c2ecf20Sopenharmony_ci
9668c2ecf20Sopenharmony_ci  } else {
9678c2ecf20Sopenharmony_ci    // zero rate - not representable
9688c2ecf20Sopenharmony_ci
9698c2ecf20Sopenharmony_ci    if (r == round_down) {
9708c2ecf20Sopenharmony_ci      return -EINVAL;
9718c2ecf20Sopenharmony_ci    } else {
9728c2ecf20Sopenharmony_ci      exp = 0;
9738c2ecf20Sopenharmony_ci      man = 0;
9748c2ecf20Sopenharmony_ci    }
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci  }
9778c2ecf20Sopenharmony_ci
9788c2ecf20Sopenharmony_ci  PRINTD (DBG_QOS, "rate: man=%u, exp=%hu", man, exp);
9798c2ecf20Sopenharmony_ci
9808c2ecf20Sopenharmony_ci  if (bits)
9818c2ecf20Sopenharmony_ci    *bits = /* (1<<14) | */ (exp<<9) | man;
9828c2ecf20Sopenharmony_ci
9838c2ecf20Sopenharmony_ci  if (actual)
9848c2ecf20Sopenharmony_ci    *actual = (exp >= 9)
9858c2ecf20Sopenharmony_ci      ? (1 << exp) + (man << (exp-9))
9868c2ecf20Sopenharmony_ci      : (1 << exp) + ((man + (1<<(9-exp-1))) >> (9-exp));
9878c2ecf20Sopenharmony_ci
9888c2ecf20Sopenharmony_ci  return 0;
9898c2ecf20Sopenharmony_ci}
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci/********** Linux ATM Operations **********/
9928c2ecf20Sopenharmony_ci
9938c2ecf20Sopenharmony_ci// some are not yet implemented while others do not make sense for
9948c2ecf20Sopenharmony_ci// this device
9958c2ecf20Sopenharmony_ci
9968c2ecf20Sopenharmony_ci/********** Open a VC **********/
9978c2ecf20Sopenharmony_ci
9988c2ecf20Sopenharmony_cistatic int amb_open (struct atm_vcc * atm_vcc)
9998c2ecf20Sopenharmony_ci{
10008c2ecf20Sopenharmony_ci  int error;
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci  struct atm_qos * qos;
10038c2ecf20Sopenharmony_ci  struct atm_trafprm * txtp;
10048c2ecf20Sopenharmony_ci  struct atm_trafprm * rxtp;
10058c2ecf20Sopenharmony_ci  u16 tx_rate_bits = -1; // hush gcc
10068c2ecf20Sopenharmony_ci  u16 tx_vc_bits = -1; // hush gcc
10078c2ecf20Sopenharmony_ci  u16 tx_frame_bits = -1; // hush gcc
10088c2ecf20Sopenharmony_ci
10098c2ecf20Sopenharmony_ci  amb_dev * dev = AMB_DEV(atm_vcc->dev);
10108c2ecf20Sopenharmony_ci  amb_vcc * vcc;
10118c2ecf20Sopenharmony_ci  unsigned char pool = -1; // hush gcc
10128c2ecf20Sopenharmony_ci  short vpi = atm_vcc->vpi;
10138c2ecf20Sopenharmony_ci  int vci = atm_vcc->vci;
10148c2ecf20Sopenharmony_ci
10158c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_VCC, "amb_open %x %x", vpi, vci);
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci#ifdef ATM_VPI_UNSPEC
10188c2ecf20Sopenharmony_ci  // UNSPEC is deprecated, remove this code eventually
10198c2ecf20Sopenharmony_ci  if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC) {
10208c2ecf20Sopenharmony_ci    PRINTK (KERN_WARNING, "rejecting open with unspecified VPI/VCI (deprecated)");
10218c2ecf20Sopenharmony_ci    return -EINVAL;
10228c2ecf20Sopenharmony_ci  }
10238c2ecf20Sopenharmony_ci#endif
10248c2ecf20Sopenharmony_ci
10258c2ecf20Sopenharmony_ci  if (!(0 <= vpi && vpi < (1<<NUM_VPI_BITS) &&
10268c2ecf20Sopenharmony_ci	0 <= vci && vci < (1<<NUM_VCI_BITS))) {
10278c2ecf20Sopenharmony_ci    PRINTD (DBG_WARN|DBG_VCC, "VPI/VCI out of range: %hd/%d", vpi, vci);
10288c2ecf20Sopenharmony_ci    return -EINVAL;
10298c2ecf20Sopenharmony_ci  }
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci  qos = &atm_vcc->qos;
10328c2ecf20Sopenharmony_ci
10338c2ecf20Sopenharmony_ci  if (qos->aal != ATM_AAL5) {
10348c2ecf20Sopenharmony_ci    PRINTD (DBG_QOS, "AAL not supported");
10358c2ecf20Sopenharmony_ci    return -EINVAL;
10368c2ecf20Sopenharmony_ci  }
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci  // traffic parameters
10398c2ecf20Sopenharmony_ci
10408c2ecf20Sopenharmony_ci  PRINTD (DBG_QOS, "TX:");
10418c2ecf20Sopenharmony_ci  txtp = &qos->txtp;
10428c2ecf20Sopenharmony_ci  if (txtp->traffic_class != ATM_NONE) {
10438c2ecf20Sopenharmony_ci    switch (txtp->traffic_class) {
10448c2ecf20Sopenharmony_ci      case ATM_UBR: {
10458c2ecf20Sopenharmony_ci	// we take "the PCR" as a rate-cap
10468c2ecf20Sopenharmony_ci	int pcr = atm_pcr_goal (txtp);
10478c2ecf20Sopenharmony_ci	if (!pcr) {
10488c2ecf20Sopenharmony_ci	  // no rate cap
10498c2ecf20Sopenharmony_ci	  tx_rate_bits = 0;
10508c2ecf20Sopenharmony_ci	  tx_vc_bits = TX_UBR;
10518c2ecf20Sopenharmony_ci	  tx_frame_bits = TX_FRAME_NOTCAP;
10528c2ecf20Sopenharmony_ci	} else {
10538c2ecf20Sopenharmony_ci	  rounding r;
10548c2ecf20Sopenharmony_ci	  if (pcr < 0) {
10558c2ecf20Sopenharmony_ci	    r = round_down;
10568c2ecf20Sopenharmony_ci	    pcr = -pcr;
10578c2ecf20Sopenharmony_ci	  } else {
10588c2ecf20Sopenharmony_ci	    r = round_up;
10598c2ecf20Sopenharmony_ci	  }
10608c2ecf20Sopenharmony_ci	  error = make_rate (pcr, r, &tx_rate_bits, NULL);
10618c2ecf20Sopenharmony_ci	  if (error)
10628c2ecf20Sopenharmony_ci	    return error;
10638c2ecf20Sopenharmony_ci	  tx_vc_bits = TX_UBR_CAPPED;
10648c2ecf20Sopenharmony_ci	  tx_frame_bits = TX_FRAME_CAPPED;
10658c2ecf20Sopenharmony_ci	}
10668c2ecf20Sopenharmony_ci	break;
10678c2ecf20Sopenharmony_ci      }
10688c2ecf20Sopenharmony_ci#if 0
10698c2ecf20Sopenharmony_ci      case ATM_ABR: {
10708c2ecf20Sopenharmony_ci	pcr = atm_pcr_goal (txtp);
10718c2ecf20Sopenharmony_ci	PRINTD (DBG_QOS, "pcr goal = %d", pcr);
10728c2ecf20Sopenharmony_ci	break;
10738c2ecf20Sopenharmony_ci      }
10748c2ecf20Sopenharmony_ci#endif
10758c2ecf20Sopenharmony_ci      default: {
10768c2ecf20Sopenharmony_ci	// PRINTD (DBG_QOS, "request for non-UBR/ABR denied");
10778c2ecf20Sopenharmony_ci	PRINTD (DBG_QOS, "request for non-UBR denied");
10788c2ecf20Sopenharmony_ci	return -EINVAL;
10798c2ecf20Sopenharmony_ci      }
10808c2ecf20Sopenharmony_ci    }
10818c2ecf20Sopenharmony_ci    PRINTD (DBG_QOS, "tx_rate_bits=%hx, tx_vc_bits=%hx",
10828c2ecf20Sopenharmony_ci	    tx_rate_bits, tx_vc_bits);
10838c2ecf20Sopenharmony_ci  }
10848c2ecf20Sopenharmony_ci
10858c2ecf20Sopenharmony_ci  PRINTD (DBG_QOS, "RX:");
10868c2ecf20Sopenharmony_ci  rxtp = &qos->rxtp;
10878c2ecf20Sopenharmony_ci  if (rxtp->traffic_class == ATM_NONE) {
10888c2ecf20Sopenharmony_ci    // do nothing
10898c2ecf20Sopenharmony_ci  } else {
10908c2ecf20Sopenharmony_ci    // choose an RX pool (arranged in increasing size)
10918c2ecf20Sopenharmony_ci    for (pool = 0; pool < NUM_RX_POOLS; ++pool)
10928c2ecf20Sopenharmony_ci      if ((unsigned int) rxtp->max_sdu <= dev->rxq[pool].buffer_size) {
10938c2ecf20Sopenharmony_ci	PRINTD (DBG_VCC|DBG_QOS|DBG_POOL, "chose pool %hu (max_sdu %u <= %u)",
10948c2ecf20Sopenharmony_ci		pool, rxtp->max_sdu, dev->rxq[pool].buffer_size);
10958c2ecf20Sopenharmony_ci	break;
10968c2ecf20Sopenharmony_ci      }
10978c2ecf20Sopenharmony_ci    if (pool == NUM_RX_POOLS) {
10988c2ecf20Sopenharmony_ci      PRINTD (DBG_WARN|DBG_VCC|DBG_QOS|DBG_POOL,
10998c2ecf20Sopenharmony_ci	      "no pool suitable for VC (RX max_sdu %d is too large)",
11008c2ecf20Sopenharmony_ci	      rxtp->max_sdu);
11018c2ecf20Sopenharmony_ci      return -EINVAL;
11028c2ecf20Sopenharmony_ci    }
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci    switch (rxtp->traffic_class) {
11058c2ecf20Sopenharmony_ci      case ATM_UBR: {
11068c2ecf20Sopenharmony_ci	break;
11078c2ecf20Sopenharmony_ci      }
11088c2ecf20Sopenharmony_ci#if 0
11098c2ecf20Sopenharmony_ci      case ATM_ABR: {
11108c2ecf20Sopenharmony_ci	pcr = atm_pcr_goal (rxtp);
11118c2ecf20Sopenharmony_ci	PRINTD (DBG_QOS, "pcr goal = %d", pcr);
11128c2ecf20Sopenharmony_ci	break;
11138c2ecf20Sopenharmony_ci      }
11148c2ecf20Sopenharmony_ci#endif
11158c2ecf20Sopenharmony_ci      default: {
11168c2ecf20Sopenharmony_ci	// PRINTD (DBG_QOS, "request for non-UBR/ABR denied");
11178c2ecf20Sopenharmony_ci	PRINTD (DBG_QOS, "request for non-UBR denied");
11188c2ecf20Sopenharmony_ci	return -EINVAL;
11198c2ecf20Sopenharmony_ci      }
11208c2ecf20Sopenharmony_ci    }
11218c2ecf20Sopenharmony_ci  }
11228c2ecf20Sopenharmony_ci
11238c2ecf20Sopenharmony_ci  // get space for our vcc stuff
11248c2ecf20Sopenharmony_ci  vcc = kmalloc (sizeof(amb_vcc), GFP_KERNEL);
11258c2ecf20Sopenharmony_ci  if (!vcc) {
11268c2ecf20Sopenharmony_ci    PRINTK (KERN_ERR, "out of memory!");
11278c2ecf20Sopenharmony_ci    return -ENOMEM;
11288c2ecf20Sopenharmony_ci  }
11298c2ecf20Sopenharmony_ci  atm_vcc->dev_data = (void *) vcc;
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci  // no failures beyond this point
11328c2ecf20Sopenharmony_ci
11338c2ecf20Sopenharmony_ci  // we are not really "immediately before allocating the connection
11348c2ecf20Sopenharmony_ci  // identifier in hardware", but it will just have to do!
11358c2ecf20Sopenharmony_ci  set_bit(ATM_VF_ADDR,&atm_vcc->flags);
11368c2ecf20Sopenharmony_ci
11378c2ecf20Sopenharmony_ci  if (txtp->traffic_class != ATM_NONE) {
11388c2ecf20Sopenharmony_ci    command cmd;
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci    vcc->tx_frame_bits = tx_frame_bits;
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci    mutex_lock(&dev->vcc_sf);
11438c2ecf20Sopenharmony_ci    if (dev->rxer[vci]) {
11448c2ecf20Sopenharmony_ci      // RXer on the channel already, just modify rate...
11458c2ecf20Sopenharmony_ci      cmd.request = cpu_to_be32 (SRB_MODIFY_VC_RATE);
11468c2ecf20Sopenharmony_ci      cmd.args.modify_rate.vc = cpu_to_be32 (vci);  // vpi 0
11478c2ecf20Sopenharmony_ci      cmd.args.modify_rate.rate = cpu_to_be32 (tx_rate_bits << SRB_RATE_SHIFT);
11488c2ecf20Sopenharmony_ci      while (command_do (dev, &cmd))
11498c2ecf20Sopenharmony_ci	schedule();
11508c2ecf20Sopenharmony_ci      // ... and TX flags, preserving the RX pool
11518c2ecf20Sopenharmony_ci      cmd.request = cpu_to_be32 (SRB_MODIFY_VC_FLAGS);
11528c2ecf20Sopenharmony_ci      cmd.args.modify_flags.vc = cpu_to_be32 (vci);  // vpi 0
11538c2ecf20Sopenharmony_ci      cmd.args.modify_flags.flags = cpu_to_be32
11548c2ecf20Sopenharmony_ci	( (AMB_VCC(dev->rxer[vci])->rx_info.pool << SRB_POOL_SHIFT)
11558c2ecf20Sopenharmony_ci	  | (tx_vc_bits << SRB_FLAGS_SHIFT) );
11568c2ecf20Sopenharmony_ci      while (command_do (dev, &cmd))
11578c2ecf20Sopenharmony_ci	schedule();
11588c2ecf20Sopenharmony_ci    } else {
11598c2ecf20Sopenharmony_ci      // no RXer on the channel, just open (with pool zero)
11608c2ecf20Sopenharmony_ci      cmd.request = cpu_to_be32 (SRB_OPEN_VC);
11618c2ecf20Sopenharmony_ci      cmd.args.open.vc = cpu_to_be32 (vci);  // vpi 0
11628c2ecf20Sopenharmony_ci      cmd.args.open.flags = cpu_to_be32 (tx_vc_bits << SRB_FLAGS_SHIFT);
11638c2ecf20Sopenharmony_ci      cmd.args.open.rate = cpu_to_be32 (tx_rate_bits << SRB_RATE_SHIFT);
11648c2ecf20Sopenharmony_ci      while (command_do (dev, &cmd))
11658c2ecf20Sopenharmony_ci	schedule();
11668c2ecf20Sopenharmony_ci    }
11678c2ecf20Sopenharmony_ci    dev->txer[vci].tx_present = 1;
11688c2ecf20Sopenharmony_ci    mutex_unlock(&dev->vcc_sf);
11698c2ecf20Sopenharmony_ci  }
11708c2ecf20Sopenharmony_ci
11718c2ecf20Sopenharmony_ci  if (rxtp->traffic_class != ATM_NONE) {
11728c2ecf20Sopenharmony_ci    command cmd;
11738c2ecf20Sopenharmony_ci
11748c2ecf20Sopenharmony_ci    vcc->rx_info.pool = pool;
11758c2ecf20Sopenharmony_ci
11768c2ecf20Sopenharmony_ci    mutex_lock(&dev->vcc_sf);
11778c2ecf20Sopenharmony_ci    /* grow RX buffer pool */
11788c2ecf20Sopenharmony_ci    if (!dev->rxq[pool].buffers_wanted)
11798c2ecf20Sopenharmony_ci      dev->rxq[pool].buffers_wanted = rx_lats;
11808c2ecf20Sopenharmony_ci    dev->rxq[pool].buffers_wanted += 1;
11818c2ecf20Sopenharmony_ci    fill_rx_pool (dev, pool, GFP_KERNEL);
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci    if (dev->txer[vci].tx_present) {
11848c2ecf20Sopenharmony_ci      // TXer on the channel already
11858c2ecf20Sopenharmony_ci      // switch (from pool zero) to this pool, preserving the TX bits
11868c2ecf20Sopenharmony_ci      cmd.request = cpu_to_be32 (SRB_MODIFY_VC_FLAGS);
11878c2ecf20Sopenharmony_ci      cmd.args.modify_flags.vc = cpu_to_be32 (vci);  // vpi 0
11888c2ecf20Sopenharmony_ci      cmd.args.modify_flags.flags = cpu_to_be32
11898c2ecf20Sopenharmony_ci	( (pool << SRB_POOL_SHIFT)
11908c2ecf20Sopenharmony_ci	  | (dev->txer[vci].tx_vc_bits << SRB_FLAGS_SHIFT) );
11918c2ecf20Sopenharmony_ci    } else {
11928c2ecf20Sopenharmony_ci      // no TXer on the channel, open the VC (with no rate info)
11938c2ecf20Sopenharmony_ci      cmd.request = cpu_to_be32 (SRB_OPEN_VC);
11948c2ecf20Sopenharmony_ci      cmd.args.open.vc = cpu_to_be32 (vci);  // vpi 0
11958c2ecf20Sopenharmony_ci      cmd.args.open.flags = cpu_to_be32 (pool << SRB_POOL_SHIFT);
11968c2ecf20Sopenharmony_ci      cmd.args.open.rate = cpu_to_be32 (0);
11978c2ecf20Sopenharmony_ci    }
11988c2ecf20Sopenharmony_ci    while (command_do (dev, &cmd))
11998c2ecf20Sopenharmony_ci      schedule();
12008c2ecf20Sopenharmony_ci    // this link allows RX frames through
12018c2ecf20Sopenharmony_ci    dev->rxer[vci] = atm_vcc;
12028c2ecf20Sopenharmony_ci    mutex_unlock(&dev->vcc_sf);
12038c2ecf20Sopenharmony_ci  }
12048c2ecf20Sopenharmony_ci
12058c2ecf20Sopenharmony_ci  // indicate readiness
12068c2ecf20Sopenharmony_ci  set_bit(ATM_VF_READY,&atm_vcc->flags);
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci  return 0;
12098c2ecf20Sopenharmony_ci}
12108c2ecf20Sopenharmony_ci
12118c2ecf20Sopenharmony_ci/********** Close a VC **********/
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_cistatic void amb_close (struct atm_vcc * atm_vcc) {
12148c2ecf20Sopenharmony_ci  amb_dev * dev = AMB_DEV (atm_vcc->dev);
12158c2ecf20Sopenharmony_ci  amb_vcc * vcc = AMB_VCC (atm_vcc);
12168c2ecf20Sopenharmony_ci  u16 vci = atm_vcc->vci;
12178c2ecf20Sopenharmony_ci
12188c2ecf20Sopenharmony_ci  PRINTD (DBG_VCC|DBG_FLOW, "amb_close");
12198c2ecf20Sopenharmony_ci
12208c2ecf20Sopenharmony_ci  // indicate unreadiness
12218c2ecf20Sopenharmony_ci  clear_bit(ATM_VF_READY,&atm_vcc->flags);
12228c2ecf20Sopenharmony_ci
12238c2ecf20Sopenharmony_ci  // disable TXing
12248c2ecf20Sopenharmony_ci  if (atm_vcc->qos.txtp.traffic_class != ATM_NONE) {
12258c2ecf20Sopenharmony_ci    command cmd;
12268c2ecf20Sopenharmony_ci
12278c2ecf20Sopenharmony_ci    mutex_lock(&dev->vcc_sf);
12288c2ecf20Sopenharmony_ci    if (dev->rxer[vci]) {
12298c2ecf20Sopenharmony_ci      // RXer still on the channel, just modify rate... XXX not really needed
12308c2ecf20Sopenharmony_ci      cmd.request = cpu_to_be32 (SRB_MODIFY_VC_RATE);
12318c2ecf20Sopenharmony_ci      cmd.args.modify_rate.vc = cpu_to_be32 (vci);  // vpi 0
12328c2ecf20Sopenharmony_ci      cmd.args.modify_rate.rate = cpu_to_be32 (0);
12338c2ecf20Sopenharmony_ci      // ... and clear TX rate flags (XXX to stop RM cell output?), preserving RX pool
12348c2ecf20Sopenharmony_ci    } else {
12358c2ecf20Sopenharmony_ci      // no RXer on the channel, close channel
12368c2ecf20Sopenharmony_ci      cmd.request = cpu_to_be32 (SRB_CLOSE_VC);
12378c2ecf20Sopenharmony_ci      cmd.args.close.vc = cpu_to_be32 (vci); // vpi 0
12388c2ecf20Sopenharmony_ci    }
12398c2ecf20Sopenharmony_ci    dev->txer[vci].tx_present = 0;
12408c2ecf20Sopenharmony_ci    while (command_do (dev, &cmd))
12418c2ecf20Sopenharmony_ci      schedule();
12428c2ecf20Sopenharmony_ci    mutex_unlock(&dev->vcc_sf);
12438c2ecf20Sopenharmony_ci  }
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci  // disable RXing
12468c2ecf20Sopenharmony_ci  if (atm_vcc->qos.rxtp.traffic_class != ATM_NONE) {
12478c2ecf20Sopenharmony_ci    command cmd;
12488c2ecf20Sopenharmony_ci
12498c2ecf20Sopenharmony_ci    // this is (the?) one reason why we need the amb_vcc struct
12508c2ecf20Sopenharmony_ci    unsigned char pool = vcc->rx_info.pool;
12518c2ecf20Sopenharmony_ci
12528c2ecf20Sopenharmony_ci    mutex_lock(&dev->vcc_sf);
12538c2ecf20Sopenharmony_ci    if (dev->txer[vci].tx_present) {
12548c2ecf20Sopenharmony_ci      // TXer still on the channel, just go to pool zero XXX not really needed
12558c2ecf20Sopenharmony_ci      cmd.request = cpu_to_be32 (SRB_MODIFY_VC_FLAGS);
12568c2ecf20Sopenharmony_ci      cmd.args.modify_flags.vc = cpu_to_be32 (vci);  // vpi 0
12578c2ecf20Sopenharmony_ci      cmd.args.modify_flags.flags = cpu_to_be32
12588c2ecf20Sopenharmony_ci	(dev->txer[vci].tx_vc_bits << SRB_FLAGS_SHIFT);
12598c2ecf20Sopenharmony_ci    } else {
12608c2ecf20Sopenharmony_ci      // no TXer on the channel, close the VC
12618c2ecf20Sopenharmony_ci      cmd.request = cpu_to_be32 (SRB_CLOSE_VC);
12628c2ecf20Sopenharmony_ci      cmd.args.close.vc = cpu_to_be32 (vci); // vpi 0
12638c2ecf20Sopenharmony_ci    }
12648c2ecf20Sopenharmony_ci    // forget the rxer - no more skbs will be pushed
12658c2ecf20Sopenharmony_ci    if (atm_vcc != dev->rxer[vci])
12668c2ecf20Sopenharmony_ci      PRINTK (KERN_ERR, "%s vcc=%p rxer[vci]=%p",
12678c2ecf20Sopenharmony_ci	      "arghhh! we're going to die!",
12688c2ecf20Sopenharmony_ci	      vcc, dev->rxer[vci]);
12698c2ecf20Sopenharmony_ci    dev->rxer[vci] = NULL;
12708c2ecf20Sopenharmony_ci    while (command_do (dev, &cmd))
12718c2ecf20Sopenharmony_ci      schedule();
12728c2ecf20Sopenharmony_ci
12738c2ecf20Sopenharmony_ci    /* shrink RX buffer pool */
12748c2ecf20Sopenharmony_ci    dev->rxq[pool].buffers_wanted -= 1;
12758c2ecf20Sopenharmony_ci    if (dev->rxq[pool].buffers_wanted == rx_lats) {
12768c2ecf20Sopenharmony_ci      dev->rxq[pool].buffers_wanted = 0;
12778c2ecf20Sopenharmony_ci      drain_rx_pool (dev, pool);
12788c2ecf20Sopenharmony_ci    }
12798c2ecf20Sopenharmony_ci    mutex_unlock(&dev->vcc_sf);
12808c2ecf20Sopenharmony_ci  }
12818c2ecf20Sopenharmony_ci
12828c2ecf20Sopenharmony_ci  // free our structure
12838c2ecf20Sopenharmony_ci  kfree (vcc);
12848c2ecf20Sopenharmony_ci
12858c2ecf20Sopenharmony_ci  // say the VPI/VCI is free again
12868c2ecf20Sopenharmony_ci  clear_bit(ATM_VF_ADDR,&atm_vcc->flags);
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_ci  return;
12898c2ecf20Sopenharmony_ci}
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci/********** Send **********/
12928c2ecf20Sopenharmony_ci
12938c2ecf20Sopenharmony_cistatic int amb_send (struct atm_vcc * atm_vcc, struct sk_buff * skb) {
12948c2ecf20Sopenharmony_ci  amb_dev * dev = AMB_DEV(atm_vcc->dev);
12958c2ecf20Sopenharmony_ci  amb_vcc * vcc = AMB_VCC(atm_vcc);
12968c2ecf20Sopenharmony_ci  u16 vc = atm_vcc->vci;
12978c2ecf20Sopenharmony_ci  unsigned int tx_len = skb->len;
12988c2ecf20Sopenharmony_ci  unsigned char * tx_data = skb->data;
12998c2ecf20Sopenharmony_ci  tx_simple * tx_descr;
13008c2ecf20Sopenharmony_ci  tx_in tx;
13018c2ecf20Sopenharmony_ci
13028c2ecf20Sopenharmony_ci  if (test_bit (dead, &dev->flags))
13038c2ecf20Sopenharmony_ci    return -EIO;
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_TX, "amb_send vc %x data %p len %u",
13068c2ecf20Sopenharmony_ci	  vc, tx_data, tx_len);
13078c2ecf20Sopenharmony_ci
13088c2ecf20Sopenharmony_ci  dump_skb (">>>", vc, skb);
13098c2ecf20Sopenharmony_ci
13108c2ecf20Sopenharmony_ci  if (!dev->txer[vc].tx_present) {
13118c2ecf20Sopenharmony_ci    PRINTK (KERN_ERR, "attempt to send on RX-only VC %x", vc);
13128c2ecf20Sopenharmony_ci    return -EBADFD;
13138c2ecf20Sopenharmony_ci  }
13148c2ecf20Sopenharmony_ci
13158c2ecf20Sopenharmony_ci  // this is a driver private field so we have to set it ourselves,
13168c2ecf20Sopenharmony_ci  // despite the fact that we are _required_ to use it to check for a
13178c2ecf20Sopenharmony_ci  // pop function
13188c2ecf20Sopenharmony_ci  ATM_SKB(skb)->vcc = atm_vcc;
13198c2ecf20Sopenharmony_ci
13208c2ecf20Sopenharmony_ci  if (skb->len > (size_t) atm_vcc->qos.txtp.max_sdu) {
13218c2ecf20Sopenharmony_ci    PRINTK (KERN_ERR, "sk_buff length greater than agreed max_sdu, dropping...");
13228c2ecf20Sopenharmony_ci    return -EIO;
13238c2ecf20Sopenharmony_ci  }
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci  if (check_area (skb->data, skb->len)) {
13268c2ecf20Sopenharmony_ci    atomic_inc(&atm_vcc->stats->tx_err);
13278c2ecf20Sopenharmony_ci    return -ENOMEM; // ?
13288c2ecf20Sopenharmony_ci  }
13298c2ecf20Sopenharmony_ci
13308c2ecf20Sopenharmony_ci  // allocate memory for fragments
13318c2ecf20Sopenharmony_ci  tx_descr = kmalloc (sizeof(tx_simple), GFP_KERNEL);
13328c2ecf20Sopenharmony_ci  if (!tx_descr) {
13338c2ecf20Sopenharmony_ci    PRINTK (KERN_ERR, "could not allocate TX descriptor");
13348c2ecf20Sopenharmony_ci    return -ENOMEM;
13358c2ecf20Sopenharmony_ci  }
13368c2ecf20Sopenharmony_ci  if (check_area (tx_descr, sizeof(tx_simple))) {
13378c2ecf20Sopenharmony_ci    kfree (tx_descr);
13388c2ecf20Sopenharmony_ci    return -ENOMEM;
13398c2ecf20Sopenharmony_ci  }
13408c2ecf20Sopenharmony_ci  PRINTD (DBG_TX, "fragment list allocated at %p", tx_descr);
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci  tx_descr->skb = skb;
13438c2ecf20Sopenharmony_ci
13448c2ecf20Sopenharmony_ci  tx_descr->tx_frag.bytes = cpu_to_be32 (tx_len);
13458c2ecf20Sopenharmony_ci  tx_descr->tx_frag.address = cpu_to_be32 (virt_to_bus (tx_data));
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci  tx_descr->tx_frag_end.handle = virt_to_bus (tx_descr);
13488c2ecf20Sopenharmony_ci  tx_descr->tx_frag_end.vc = 0;
13498c2ecf20Sopenharmony_ci  tx_descr->tx_frag_end.next_descriptor_length = 0;
13508c2ecf20Sopenharmony_ci  tx_descr->tx_frag_end.next_descriptor = 0;
13518c2ecf20Sopenharmony_ci#ifdef AMB_NEW_MICROCODE
13528c2ecf20Sopenharmony_ci  tx_descr->tx_frag_end.cpcs_uu = 0;
13538c2ecf20Sopenharmony_ci  tx_descr->tx_frag_end.cpi = 0;
13548c2ecf20Sopenharmony_ci  tx_descr->tx_frag_end.pad = 0;
13558c2ecf20Sopenharmony_ci#endif
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci  tx.vc = cpu_to_be16 (vcc->tx_frame_bits | vc);
13588c2ecf20Sopenharmony_ci  tx.tx_descr_length = cpu_to_be16 (sizeof(tx_frag)+sizeof(tx_frag_end));
13598c2ecf20Sopenharmony_ci  tx.tx_descr_addr = cpu_to_be32 (virt_to_bus (&tx_descr->tx_frag));
13608c2ecf20Sopenharmony_ci
13618c2ecf20Sopenharmony_ci  while (tx_give (dev, &tx))
13628c2ecf20Sopenharmony_ci    schedule();
13638c2ecf20Sopenharmony_ci  return 0;
13648c2ecf20Sopenharmony_ci}
13658c2ecf20Sopenharmony_ci
13668c2ecf20Sopenharmony_ci/********** Change QoS on a VC **********/
13678c2ecf20Sopenharmony_ci
13688c2ecf20Sopenharmony_ci// int amb_change_qos (struct atm_vcc * atm_vcc, struct atm_qos * qos, int flags);
13698c2ecf20Sopenharmony_ci
13708c2ecf20Sopenharmony_ci/********** Free RX Socket Buffer **********/
13718c2ecf20Sopenharmony_ci
13728c2ecf20Sopenharmony_ci#if 0
13738c2ecf20Sopenharmony_cistatic void amb_free_rx_skb (struct atm_vcc * atm_vcc, struct sk_buff * skb) {
13748c2ecf20Sopenharmony_ci  amb_dev * dev = AMB_DEV (atm_vcc->dev);
13758c2ecf20Sopenharmony_ci  amb_vcc * vcc = AMB_VCC (atm_vcc);
13768c2ecf20Sopenharmony_ci  unsigned char pool = vcc->rx_info.pool;
13778c2ecf20Sopenharmony_ci  rx_in rx;
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_ci  // This may be unsafe for various reasons that I cannot really guess
13808c2ecf20Sopenharmony_ci  // at. However, I note that the ATM layer calls kfree_skb rather
13818c2ecf20Sopenharmony_ci  // than dev_kfree_skb at this point so we are least covered as far
13828c2ecf20Sopenharmony_ci  // as buffer locking goes. There may be bugs if pcap clones RX skbs.
13838c2ecf20Sopenharmony_ci
13848c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_SKB, "amb_rx_free skb %p (atm_vcc %p, vcc %p)",
13858c2ecf20Sopenharmony_ci	  skb, atm_vcc, vcc);
13868c2ecf20Sopenharmony_ci
13878c2ecf20Sopenharmony_ci  rx.handle = virt_to_bus (skb);
13888c2ecf20Sopenharmony_ci  rx.host_address = cpu_to_be32 (virt_to_bus (skb->data));
13898c2ecf20Sopenharmony_ci
13908c2ecf20Sopenharmony_ci  skb->data = skb->head;
13918c2ecf20Sopenharmony_ci  skb_reset_tail_pointer(skb);
13928c2ecf20Sopenharmony_ci  skb->len = 0;
13938c2ecf20Sopenharmony_ci
13948c2ecf20Sopenharmony_ci  if (!rx_give (dev, &rx, pool)) {
13958c2ecf20Sopenharmony_ci    // success
13968c2ecf20Sopenharmony_ci    PRINTD (DBG_SKB|DBG_POOL, "recycled skb for pool %hu", pool);
13978c2ecf20Sopenharmony_ci    return;
13988c2ecf20Sopenharmony_ci  }
13998c2ecf20Sopenharmony_ci
14008c2ecf20Sopenharmony_ci  // just do what the ATM layer would have done
14018c2ecf20Sopenharmony_ci  dev_kfree_skb_any (skb);
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci  return;
14048c2ecf20Sopenharmony_ci}
14058c2ecf20Sopenharmony_ci#endif
14068c2ecf20Sopenharmony_ci
14078c2ecf20Sopenharmony_ci/********** Proc File Output **********/
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_cistatic int amb_proc_read (struct atm_dev * atm_dev, loff_t * pos, char * page) {
14108c2ecf20Sopenharmony_ci  amb_dev * dev = AMB_DEV (atm_dev);
14118c2ecf20Sopenharmony_ci  int left = *pos;
14128c2ecf20Sopenharmony_ci  unsigned char pool;
14138c2ecf20Sopenharmony_ci
14148c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW, "amb_proc_read");
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci  /* more diagnostics here? */
14178c2ecf20Sopenharmony_ci
14188c2ecf20Sopenharmony_ci  if (!left--) {
14198c2ecf20Sopenharmony_ci    amb_stats * s = &dev->stats;
14208c2ecf20Sopenharmony_ci    return sprintf (page,
14218c2ecf20Sopenharmony_ci		    "frames: TX OK %lu, RX OK %lu, RX bad %lu "
14228c2ecf20Sopenharmony_ci		    "(CRC %lu, long %lu, aborted %lu, unused %lu).\n",
14238c2ecf20Sopenharmony_ci		    s->tx_ok, s->rx.ok, s->rx.error,
14248c2ecf20Sopenharmony_ci		    s->rx.badcrc, s->rx.toolong,
14258c2ecf20Sopenharmony_ci		    s->rx.aborted, s->rx.unused);
14268c2ecf20Sopenharmony_ci  }
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci  if (!left--) {
14298c2ecf20Sopenharmony_ci    amb_cq * c = &dev->cq;
14308c2ecf20Sopenharmony_ci    return sprintf (page, "cmd queue [cur/hi/max]: %u/%u/%u. ",
14318c2ecf20Sopenharmony_ci		    c->pending, c->high, c->maximum);
14328c2ecf20Sopenharmony_ci  }
14338c2ecf20Sopenharmony_ci
14348c2ecf20Sopenharmony_ci  if (!left--) {
14358c2ecf20Sopenharmony_ci    amb_txq * t = &dev->txq;
14368c2ecf20Sopenharmony_ci    return sprintf (page, "TX queue [cur/max high full]: %u/%u %u %u.\n",
14378c2ecf20Sopenharmony_ci		    t->pending, t->maximum, t->high, t->filled);
14388c2ecf20Sopenharmony_ci  }
14398c2ecf20Sopenharmony_ci
14408c2ecf20Sopenharmony_ci  if (!left--) {
14418c2ecf20Sopenharmony_ci    unsigned int count = sprintf (page, "RX queues [cur/max/req low empty]:");
14428c2ecf20Sopenharmony_ci    for (pool = 0; pool < NUM_RX_POOLS; ++pool) {
14438c2ecf20Sopenharmony_ci      amb_rxq * r = &dev->rxq[pool];
14448c2ecf20Sopenharmony_ci      count += sprintf (page+count, " %u/%u/%u %u %u",
14458c2ecf20Sopenharmony_ci			r->pending, r->maximum, r->buffers_wanted, r->low, r->emptied);
14468c2ecf20Sopenharmony_ci    }
14478c2ecf20Sopenharmony_ci    count += sprintf (page+count, ".\n");
14488c2ecf20Sopenharmony_ci    return count;
14498c2ecf20Sopenharmony_ci  }
14508c2ecf20Sopenharmony_ci
14518c2ecf20Sopenharmony_ci  if (!left--) {
14528c2ecf20Sopenharmony_ci    unsigned int count = sprintf (page, "RX buffer sizes:");
14538c2ecf20Sopenharmony_ci    for (pool = 0; pool < NUM_RX_POOLS; ++pool) {
14548c2ecf20Sopenharmony_ci      amb_rxq * r = &dev->rxq[pool];
14558c2ecf20Sopenharmony_ci      count += sprintf (page+count, " %u", r->buffer_size);
14568c2ecf20Sopenharmony_ci    }
14578c2ecf20Sopenharmony_ci    count += sprintf (page+count, ".\n");
14588c2ecf20Sopenharmony_ci    return count;
14598c2ecf20Sopenharmony_ci  }
14608c2ecf20Sopenharmony_ci
14618c2ecf20Sopenharmony_ci#if 0
14628c2ecf20Sopenharmony_ci  if (!left--) {
14638c2ecf20Sopenharmony_ci    // suni block etc?
14648c2ecf20Sopenharmony_ci  }
14658c2ecf20Sopenharmony_ci#endif
14668c2ecf20Sopenharmony_ci
14678c2ecf20Sopenharmony_ci  return 0;
14688c2ecf20Sopenharmony_ci}
14698c2ecf20Sopenharmony_ci
14708c2ecf20Sopenharmony_ci/********** Operation Structure **********/
14718c2ecf20Sopenharmony_ci
14728c2ecf20Sopenharmony_cistatic const struct atmdev_ops amb_ops = {
14738c2ecf20Sopenharmony_ci  .open         = amb_open,
14748c2ecf20Sopenharmony_ci  .close	= amb_close,
14758c2ecf20Sopenharmony_ci  .send         = amb_send,
14768c2ecf20Sopenharmony_ci  .proc_read	= amb_proc_read,
14778c2ecf20Sopenharmony_ci  .owner	= THIS_MODULE,
14788c2ecf20Sopenharmony_ci};
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci/********** housekeeping **********/
14818c2ecf20Sopenharmony_cistatic void do_housekeeping (struct timer_list *t) {
14828c2ecf20Sopenharmony_ci  amb_dev * dev = from_timer(dev, t, housekeeping);
14838c2ecf20Sopenharmony_ci
14848c2ecf20Sopenharmony_ci  // could collect device-specific (not driver/atm-linux) stats here
14858c2ecf20Sopenharmony_ci
14868c2ecf20Sopenharmony_ci  // last resort refill once every ten seconds
14878c2ecf20Sopenharmony_ci  fill_rx_pools (dev);
14888c2ecf20Sopenharmony_ci  mod_timer(&dev->housekeeping, jiffies + 10*HZ);
14898c2ecf20Sopenharmony_ci
14908c2ecf20Sopenharmony_ci  return;
14918c2ecf20Sopenharmony_ci}
14928c2ecf20Sopenharmony_ci
14938c2ecf20Sopenharmony_ci/********** creation of communication queues **********/
14948c2ecf20Sopenharmony_ci
14958c2ecf20Sopenharmony_cistatic int create_queues(amb_dev *dev, unsigned int cmds, unsigned int txs,
14968c2ecf20Sopenharmony_ci			 unsigned int *rxs, unsigned int *rx_buffer_sizes)
14978c2ecf20Sopenharmony_ci{
14988c2ecf20Sopenharmony_ci  unsigned char pool;
14998c2ecf20Sopenharmony_ci  size_t total = 0;
15008c2ecf20Sopenharmony_ci  void * memory;
15018c2ecf20Sopenharmony_ci  void * limit;
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW, "create_queues %p", dev);
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_ci  total += cmds * sizeof(command);
15068c2ecf20Sopenharmony_ci
15078c2ecf20Sopenharmony_ci  total += txs * (sizeof(tx_in) + sizeof(tx_out));
15088c2ecf20Sopenharmony_ci
15098c2ecf20Sopenharmony_ci  for (pool = 0; pool < NUM_RX_POOLS; ++pool)
15108c2ecf20Sopenharmony_ci    total += rxs[pool] * (sizeof(rx_in) + sizeof(rx_out));
15118c2ecf20Sopenharmony_ci
15128c2ecf20Sopenharmony_ci  memory = kmalloc (total, GFP_KERNEL);
15138c2ecf20Sopenharmony_ci  if (!memory) {
15148c2ecf20Sopenharmony_ci    PRINTK (KERN_ERR, "could not allocate queues");
15158c2ecf20Sopenharmony_ci    return -ENOMEM;
15168c2ecf20Sopenharmony_ci  }
15178c2ecf20Sopenharmony_ci  if (check_area (memory, total)) {
15188c2ecf20Sopenharmony_ci    PRINTK (KERN_ERR, "queues allocated in nasty area");
15198c2ecf20Sopenharmony_ci    kfree (memory);
15208c2ecf20Sopenharmony_ci    return -ENOMEM;
15218c2ecf20Sopenharmony_ci  }
15228c2ecf20Sopenharmony_ci
15238c2ecf20Sopenharmony_ci  limit = memory + total;
15248c2ecf20Sopenharmony_ci  PRINTD (DBG_INIT, "queues from %p to %p", memory, limit);
15258c2ecf20Sopenharmony_ci
15268c2ecf20Sopenharmony_ci  PRINTD (DBG_CMD, "command queue at %p", memory);
15278c2ecf20Sopenharmony_ci
15288c2ecf20Sopenharmony_ci  {
15298c2ecf20Sopenharmony_ci    command * cmd = memory;
15308c2ecf20Sopenharmony_ci    amb_cq * cq = &dev->cq;
15318c2ecf20Sopenharmony_ci
15328c2ecf20Sopenharmony_ci    cq->pending = 0;
15338c2ecf20Sopenharmony_ci    cq->high = 0;
15348c2ecf20Sopenharmony_ci    cq->maximum = cmds - 1;
15358c2ecf20Sopenharmony_ci
15368c2ecf20Sopenharmony_ci    cq->ptrs.start = cmd;
15378c2ecf20Sopenharmony_ci    cq->ptrs.in = cmd;
15388c2ecf20Sopenharmony_ci    cq->ptrs.out = cmd;
15398c2ecf20Sopenharmony_ci    cq->ptrs.limit = cmd + cmds;
15408c2ecf20Sopenharmony_ci
15418c2ecf20Sopenharmony_ci    memory = cq->ptrs.limit;
15428c2ecf20Sopenharmony_ci  }
15438c2ecf20Sopenharmony_ci
15448c2ecf20Sopenharmony_ci  PRINTD (DBG_TX, "TX queue pair at %p", memory);
15458c2ecf20Sopenharmony_ci
15468c2ecf20Sopenharmony_ci  {
15478c2ecf20Sopenharmony_ci    tx_in * in = memory;
15488c2ecf20Sopenharmony_ci    tx_out * out;
15498c2ecf20Sopenharmony_ci    amb_txq * txq = &dev->txq;
15508c2ecf20Sopenharmony_ci
15518c2ecf20Sopenharmony_ci    txq->pending = 0;
15528c2ecf20Sopenharmony_ci    txq->high = 0;
15538c2ecf20Sopenharmony_ci    txq->filled = 0;
15548c2ecf20Sopenharmony_ci    txq->maximum = txs - 1;
15558c2ecf20Sopenharmony_ci
15568c2ecf20Sopenharmony_ci    txq->in.start = in;
15578c2ecf20Sopenharmony_ci    txq->in.ptr = in;
15588c2ecf20Sopenharmony_ci    txq->in.limit = in + txs;
15598c2ecf20Sopenharmony_ci
15608c2ecf20Sopenharmony_ci    memory = txq->in.limit;
15618c2ecf20Sopenharmony_ci    out = memory;
15628c2ecf20Sopenharmony_ci
15638c2ecf20Sopenharmony_ci    txq->out.start = out;
15648c2ecf20Sopenharmony_ci    txq->out.ptr = out;
15658c2ecf20Sopenharmony_ci    txq->out.limit = out + txs;
15668c2ecf20Sopenharmony_ci
15678c2ecf20Sopenharmony_ci    memory = txq->out.limit;
15688c2ecf20Sopenharmony_ci  }
15698c2ecf20Sopenharmony_ci
15708c2ecf20Sopenharmony_ci  PRINTD (DBG_RX, "RX queue pairs at %p", memory);
15718c2ecf20Sopenharmony_ci
15728c2ecf20Sopenharmony_ci  for (pool = 0; pool < NUM_RX_POOLS; ++pool) {
15738c2ecf20Sopenharmony_ci    rx_in * in = memory;
15748c2ecf20Sopenharmony_ci    rx_out * out;
15758c2ecf20Sopenharmony_ci    amb_rxq * rxq = &dev->rxq[pool];
15768c2ecf20Sopenharmony_ci
15778c2ecf20Sopenharmony_ci    rxq->buffer_size = rx_buffer_sizes[pool];
15788c2ecf20Sopenharmony_ci    rxq->buffers_wanted = 0;
15798c2ecf20Sopenharmony_ci
15808c2ecf20Sopenharmony_ci    rxq->pending = 0;
15818c2ecf20Sopenharmony_ci    rxq->low = rxs[pool] - 1;
15828c2ecf20Sopenharmony_ci    rxq->emptied = 0;
15838c2ecf20Sopenharmony_ci    rxq->maximum = rxs[pool] - 1;
15848c2ecf20Sopenharmony_ci
15858c2ecf20Sopenharmony_ci    rxq->in.start = in;
15868c2ecf20Sopenharmony_ci    rxq->in.ptr = in;
15878c2ecf20Sopenharmony_ci    rxq->in.limit = in + rxs[pool];
15888c2ecf20Sopenharmony_ci
15898c2ecf20Sopenharmony_ci    memory = rxq->in.limit;
15908c2ecf20Sopenharmony_ci    out = memory;
15918c2ecf20Sopenharmony_ci
15928c2ecf20Sopenharmony_ci    rxq->out.start = out;
15938c2ecf20Sopenharmony_ci    rxq->out.ptr = out;
15948c2ecf20Sopenharmony_ci    rxq->out.limit = out + rxs[pool];
15958c2ecf20Sopenharmony_ci
15968c2ecf20Sopenharmony_ci    memory = rxq->out.limit;
15978c2ecf20Sopenharmony_ci  }
15988c2ecf20Sopenharmony_ci
15998c2ecf20Sopenharmony_ci  if (memory == limit) {
16008c2ecf20Sopenharmony_ci    return 0;
16018c2ecf20Sopenharmony_ci  } else {
16028c2ecf20Sopenharmony_ci    PRINTK (KERN_ERR, "bad queue alloc %p != %p (tell maintainer)", memory, limit);
16038c2ecf20Sopenharmony_ci    kfree (limit - total);
16048c2ecf20Sopenharmony_ci    return -ENOMEM;
16058c2ecf20Sopenharmony_ci  }
16068c2ecf20Sopenharmony_ci
16078c2ecf20Sopenharmony_ci}
16088c2ecf20Sopenharmony_ci
16098c2ecf20Sopenharmony_ci/********** destruction of communication queues **********/
16108c2ecf20Sopenharmony_ci
16118c2ecf20Sopenharmony_cistatic void destroy_queues (amb_dev * dev) {
16128c2ecf20Sopenharmony_ci  // all queues assumed empty
16138c2ecf20Sopenharmony_ci  void * memory = dev->cq.ptrs.start;
16148c2ecf20Sopenharmony_ci  // includes txq.in, txq.out, rxq[].in and rxq[].out
16158c2ecf20Sopenharmony_ci
16168c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW, "destroy_queues %p", dev);
16178c2ecf20Sopenharmony_ci
16188c2ecf20Sopenharmony_ci  PRINTD (DBG_INIT, "freeing queues at %p", memory);
16198c2ecf20Sopenharmony_ci  kfree (memory);
16208c2ecf20Sopenharmony_ci
16218c2ecf20Sopenharmony_ci  return;
16228c2ecf20Sopenharmony_ci}
16238c2ecf20Sopenharmony_ci
16248c2ecf20Sopenharmony_ci/********** basic loader commands and error handling **********/
16258c2ecf20Sopenharmony_ci// centisecond timeouts - guessing away here
16268c2ecf20Sopenharmony_cistatic unsigned int command_timeouts [] = {
16278c2ecf20Sopenharmony_ci	[host_memory_test]     = 15,
16288c2ecf20Sopenharmony_ci	[read_adapter_memory]  = 2,
16298c2ecf20Sopenharmony_ci	[write_adapter_memory] = 2,
16308c2ecf20Sopenharmony_ci	[adapter_start]        = 50,
16318c2ecf20Sopenharmony_ci	[get_version_number]   = 10,
16328c2ecf20Sopenharmony_ci	[interrupt_host]       = 1,
16338c2ecf20Sopenharmony_ci	[flash_erase_sector]   = 1,
16348c2ecf20Sopenharmony_ci	[adap_download_block]  = 1,
16358c2ecf20Sopenharmony_ci	[adap_erase_flash]     = 1,
16368c2ecf20Sopenharmony_ci	[adap_run_in_iram]     = 1,
16378c2ecf20Sopenharmony_ci	[adap_end_download]    = 1
16388c2ecf20Sopenharmony_ci};
16398c2ecf20Sopenharmony_ci
16408c2ecf20Sopenharmony_ci
16418c2ecf20Sopenharmony_cistatic unsigned int command_successes [] = {
16428c2ecf20Sopenharmony_ci	[host_memory_test]     = COMMAND_PASSED_TEST,
16438c2ecf20Sopenharmony_ci	[read_adapter_memory]  = COMMAND_READ_DATA_OK,
16448c2ecf20Sopenharmony_ci	[write_adapter_memory] = COMMAND_WRITE_DATA_OK,
16458c2ecf20Sopenharmony_ci	[adapter_start]        = COMMAND_COMPLETE,
16468c2ecf20Sopenharmony_ci	[get_version_number]   = COMMAND_COMPLETE,
16478c2ecf20Sopenharmony_ci	[interrupt_host]       = COMMAND_COMPLETE,
16488c2ecf20Sopenharmony_ci	[flash_erase_sector]   = COMMAND_COMPLETE,
16498c2ecf20Sopenharmony_ci	[adap_download_block]  = COMMAND_COMPLETE,
16508c2ecf20Sopenharmony_ci	[adap_erase_flash]     = COMMAND_COMPLETE,
16518c2ecf20Sopenharmony_ci	[adap_run_in_iram]     = COMMAND_COMPLETE,
16528c2ecf20Sopenharmony_ci	[adap_end_download]    = COMMAND_COMPLETE
16538c2ecf20Sopenharmony_ci};
16548c2ecf20Sopenharmony_ci
16558c2ecf20Sopenharmony_cistatic  int decode_loader_result (loader_command cmd, u32 result)
16568c2ecf20Sopenharmony_ci{
16578c2ecf20Sopenharmony_ci	int res;
16588c2ecf20Sopenharmony_ci	const char *msg;
16598c2ecf20Sopenharmony_ci
16608c2ecf20Sopenharmony_ci	if (result == command_successes[cmd])
16618c2ecf20Sopenharmony_ci		return 0;
16628c2ecf20Sopenharmony_ci
16638c2ecf20Sopenharmony_ci	switch (result) {
16648c2ecf20Sopenharmony_ci		case BAD_COMMAND:
16658c2ecf20Sopenharmony_ci			res = -EINVAL;
16668c2ecf20Sopenharmony_ci			msg = "bad command";
16678c2ecf20Sopenharmony_ci			break;
16688c2ecf20Sopenharmony_ci		case COMMAND_IN_PROGRESS:
16698c2ecf20Sopenharmony_ci			res = -ETIMEDOUT;
16708c2ecf20Sopenharmony_ci			msg = "command in progress";
16718c2ecf20Sopenharmony_ci			break;
16728c2ecf20Sopenharmony_ci		case COMMAND_PASSED_TEST:
16738c2ecf20Sopenharmony_ci			res = 0;
16748c2ecf20Sopenharmony_ci			msg = "command passed test";
16758c2ecf20Sopenharmony_ci			break;
16768c2ecf20Sopenharmony_ci		case COMMAND_FAILED_TEST:
16778c2ecf20Sopenharmony_ci			res = -EIO;
16788c2ecf20Sopenharmony_ci			msg = "command failed test";
16798c2ecf20Sopenharmony_ci			break;
16808c2ecf20Sopenharmony_ci		case COMMAND_READ_DATA_OK:
16818c2ecf20Sopenharmony_ci			res = 0;
16828c2ecf20Sopenharmony_ci			msg = "command read data ok";
16838c2ecf20Sopenharmony_ci			break;
16848c2ecf20Sopenharmony_ci		case COMMAND_READ_BAD_ADDRESS:
16858c2ecf20Sopenharmony_ci			res = -EINVAL;
16868c2ecf20Sopenharmony_ci			msg = "command read bad address";
16878c2ecf20Sopenharmony_ci			break;
16888c2ecf20Sopenharmony_ci		case COMMAND_WRITE_DATA_OK:
16898c2ecf20Sopenharmony_ci			res = 0;
16908c2ecf20Sopenharmony_ci			msg = "command write data ok";
16918c2ecf20Sopenharmony_ci			break;
16928c2ecf20Sopenharmony_ci		case COMMAND_WRITE_BAD_ADDRESS:
16938c2ecf20Sopenharmony_ci			res = -EINVAL;
16948c2ecf20Sopenharmony_ci			msg = "command write bad address";
16958c2ecf20Sopenharmony_ci			break;
16968c2ecf20Sopenharmony_ci		case COMMAND_WRITE_FLASH_FAILURE:
16978c2ecf20Sopenharmony_ci			res = -EIO;
16988c2ecf20Sopenharmony_ci			msg = "command write flash failure";
16998c2ecf20Sopenharmony_ci			break;
17008c2ecf20Sopenharmony_ci		case COMMAND_COMPLETE:
17018c2ecf20Sopenharmony_ci			res = 0;
17028c2ecf20Sopenharmony_ci			msg = "command complete";
17038c2ecf20Sopenharmony_ci			break;
17048c2ecf20Sopenharmony_ci		case COMMAND_FLASH_ERASE_FAILURE:
17058c2ecf20Sopenharmony_ci			res = -EIO;
17068c2ecf20Sopenharmony_ci			msg = "command flash erase failure";
17078c2ecf20Sopenharmony_ci			break;
17088c2ecf20Sopenharmony_ci		case COMMAND_WRITE_BAD_DATA:
17098c2ecf20Sopenharmony_ci			res = -EINVAL;
17108c2ecf20Sopenharmony_ci			msg = "command write bad data";
17118c2ecf20Sopenharmony_ci			break;
17128c2ecf20Sopenharmony_ci		default:
17138c2ecf20Sopenharmony_ci			res = -EINVAL;
17148c2ecf20Sopenharmony_ci			msg = "unknown error";
17158c2ecf20Sopenharmony_ci			PRINTD (DBG_LOAD|DBG_ERR,
17168c2ecf20Sopenharmony_ci				"decode_loader_result got %d=%x !",
17178c2ecf20Sopenharmony_ci				result, result);
17188c2ecf20Sopenharmony_ci			break;
17198c2ecf20Sopenharmony_ci	}
17208c2ecf20Sopenharmony_ci
17218c2ecf20Sopenharmony_ci	PRINTK (KERN_ERR, "%s", msg);
17228c2ecf20Sopenharmony_ci	return res;
17238c2ecf20Sopenharmony_ci}
17248c2ecf20Sopenharmony_ci
17258c2ecf20Sopenharmony_cistatic int do_loader_command(volatile loader_block *lb, const amb_dev *dev,
17268c2ecf20Sopenharmony_ci			     loader_command cmd)
17278c2ecf20Sopenharmony_ci{
17288c2ecf20Sopenharmony_ci
17298c2ecf20Sopenharmony_ci  unsigned long timeout;
17308c2ecf20Sopenharmony_ci
17318c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_LOAD, "do_loader_command");
17328c2ecf20Sopenharmony_ci
17338c2ecf20Sopenharmony_ci  /* do a command
17348c2ecf20Sopenharmony_ci
17358c2ecf20Sopenharmony_ci     Set the return value to zero, set the command type and set the
17368c2ecf20Sopenharmony_ci     valid entry to the right magic value. The payload is already
17378c2ecf20Sopenharmony_ci     correctly byte-ordered so we leave it alone. Hit the doorbell
17388c2ecf20Sopenharmony_ci     with the bus address of this structure.
17398c2ecf20Sopenharmony_ci
17408c2ecf20Sopenharmony_ci  */
17418c2ecf20Sopenharmony_ci
17428c2ecf20Sopenharmony_ci  lb->result = 0;
17438c2ecf20Sopenharmony_ci  lb->command = cpu_to_be32 (cmd);
17448c2ecf20Sopenharmony_ci  lb->valid = cpu_to_be32 (DMA_VALID);
17458c2ecf20Sopenharmony_ci  // dump_registers (dev);
17468c2ecf20Sopenharmony_ci  // dump_loader_block (lb);
17478c2ecf20Sopenharmony_ci  wr_mem (dev, offsetof(amb_mem, doorbell), virt_to_bus (lb) & ~onegigmask);
17488c2ecf20Sopenharmony_ci
17498c2ecf20Sopenharmony_ci  timeout = command_timeouts[cmd] * 10;
17508c2ecf20Sopenharmony_ci
17518c2ecf20Sopenharmony_ci  while (!lb->result || lb->result == cpu_to_be32 (COMMAND_IN_PROGRESS))
17528c2ecf20Sopenharmony_ci    if (timeout) {
17538c2ecf20Sopenharmony_ci      timeout = msleep_interruptible(timeout);
17548c2ecf20Sopenharmony_ci    } else {
17558c2ecf20Sopenharmony_ci      PRINTD (DBG_LOAD|DBG_ERR, "command %d timed out", cmd);
17568c2ecf20Sopenharmony_ci      dump_registers (dev);
17578c2ecf20Sopenharmony_ci      dump_loader_block (lb);
17588c2ecf20Sopenharmony_ci      return -ETIMEDOUT;
17598c2ecf20Sopenharmony_ci    }
17608c2ecf20Sopenharmony_ci
17618c2ecf20Sopenharmony_ci  if (cmd == adapter_start) {
17628c2ecf20Sopenharmony_ci    // wait for start command to acknowledge...
17638c2ecf20Sopenharmony_ci    timeout = 100;
17648c2ecf20Sopenharmony_ci    while (rd_plain (dev, offsetof(amb_mem, doorbell)))
17658c2ecf20Sopenharmony_ci      if (timeout) {
17668c2ecf20Sopenharmony_ci	timeout = msleep_interruptible(timeout);
17678c2ecf20Sopenharmony_ci      } else {
17688c2ecf20Sopenharmony_ci	PRINTD (DBG_LOAD|DBG_ERR, "start command did not clear doorbell, res=%08x",
17698c2ecf20Sopenharmony_ci		be32_to_cpu (lb->result));
17708c2ecf20Sopenharmony_ci	dump_registers (dev);
17718c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
17728c2ecf20Sopenharmony_ci      }
17738c2ecf20Sopenharmony_ci    return 0;
17748c2ecf20Sopenharmony_ci  } else {
17758c2ecf20Sopenharmony_ci    return decode_loader_result (cmd, be32_to_cpu (lb->result));
17768c2ecf20Sopenharmony_ci  }
17778c2ecf20Sopenharmony_ci
17788c2ecf20Sopenharmony_ci}
17798c2ecf20Sopenharmony_ci
17808c2ecf20Sopenharmony_ci/* loader: determine loader version */
17818c2ecf20Sopenharmony_ci
17828c2ecf20Sopenharmony_cistatic int get_loader_version(loader_block *lb, const amb_dev *dev,
17838c2ecf20Sopenharmony_ci			      u32 *version)
17848c2ecf20Sopenharmony_ci{
17858c2ecf20Sopenharmony_ci  int res;
17868c2ecf20Sopenharmony_ci
17878c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_LOAD, "get_loader_version");
17888c2ecf20Sopenharmony_ci
17898c2ecf20Sopenharmony_ci  res = do_loader_command (lb, dev, get_version_number);
17908c2ecf20Sopenharmony_ci  if (res)
17918c2ecf20Sopenharmony_ci    return res;
17928c2ecf20Sopenharmony_ci  if (version)
17938c2ecf20Sopenharmony_ci    *version = be32_to_cpu (lb->payload.version);
17948c2ecf20Sopenharmony_ci  return 0;
17958c2ecf20Sopenharmony_ci}
17968c2ecf20Sopenharmony_ci
17978c2ecf20Sopenharmony_ci/* loader: write memory data blocks */
17988c2ecf20Sopenharmony_ci
17998c2ecf20Sopenharmony_cistatic int loader_write(loader_block *lb, const amb_dev *dev,
18008c2ecf20Sopenharmony_ci			const struct ihex_binrec *rec)
18018c2ecf20Sopenharmony_ci{
18028c2ecf20Sopenharmony_ci  transfer_block * tb = &lb->payload.transfer;
18038c2ecf20Sopenharmony_ci
18048c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_LOAD, "loader_write");
18058c2ecf20Sopenharmony_ci
18068c2ecf20Sopenharmony_ci  tb->address = rec->addr;
18078c2ecf20Sopenharmony_ci  tb->count = cpu_to_be32(be16_to_cpu(rec->len) / 4);
18088c2ecf20Sopenharmony_ci  memcpy(tb->data, rec->data, be16_to_cpu(rec->len));
18098c2ecf20Sopenharmony_ci  return do_loader_command (lb, dev, write_adapter_memory);
18108c2ecf20Sopenharmony_ci}
18118c2ecf20Sopenharmony_ci
18128c2ecf20Sopenharmony_ci/* loader: verify memory data blocks */
18138c2ecf20Sopenharmony_ci
18148c2ecf20Sopenharmony_cistatic int loader_verify(loader_block *lb, const amb_dev *dev,
18158c2ecf20Sopenharmony_ci			 const struct ihex_binrec *rec)
18168c2ecf20Sopenharmony_ci{
18178c2ecf20Sopenharmony_ci  transfer_block * tb = &lb->payload.transfer;
18188c2ecf20Sopenharmony_ci  int res;
18198c2ecf20Sopenharmony_ci
18208c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_LOAD, "loader_verify");
18218c2ecf20Sopenharmony_ci
18228c2ecf20Sopenharmony_ci  tb->address = rec->addr;
18238c2ecf20Sopenharmony_ci  tb->count = cpu_to_be32(be16_to_cpu(rec->len) / 4);
18248c2ecf20Sopenharmony_ci  res = do_loader_command (lb, dev, read_adapter_memory);
18258c2ecf20Sopenharmony_ci  if (!res && memcmp(tb->data, rec->data, be16_to_cpu(rec->len)))
18268c2ecf20Sopenharmony_ci    res = -EINVAL;
18278c2ecf20Sopenharmony_ci  return res;
18288c2ecf20Sopenharmony_ci}
18298c2ecf20Sopenharmony_ci
18308c2ecf20Sopenharmony_ci/* loader: start microcode */
18318c2ecf20Sopenharmony_ci
18328c2ecf20Sopenharmony_cistatic int loader_start(loader_block *lb, const amb_dev *dev, u32 address)
18338c2ecf20Sopenharmony_ci{
18348c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_LOAD, "loader_start");
18358c2ecf20Sopenharmony_ci
18368c2ecf20Sopenharmony_ci  lb->payload.start = cpu_to_be32 (address);
18378c2ecf20Sopenharmony_ci  return do_loader_command (lb, dev, adapter_start);
18388c2ecf20Sopenharmony_ci}
18398c2ecf20Sopenharmony_ci
18408c2ecf20Sopenharmony_ci/********** reset card **********/
18418c2ecf20Sopenharmony_ci
18428c2ecf20Sopenharmony_cistatic inline void sf (const char * msg)
18438c2ecf20Sopenharmony_ci{
18448c2ecf20Sopenharmony_ci	PRINTK (KERN_ERR, "self-test failed: %s", msg);
18458c2ecf20Sopenharmony_ci}
18468c2ecf20Sopenharmony_ci
18478c2ecf20Sopenharmony_cistatic int amb_reset (amb_dev * dev, int diags) {
18488c2ecf20Sopenharmony_ci  u32 word;
18498c2ecf20Sopenharmony_ci
18508c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_LOAD, "amb_reset");
18518c2ecf20Sopenharmony_ci
18528c2ecf20Sopenharmony_ci  word = rd_plain (dev, offsetof(amb_mem, reset_control));
18538c2ecf20Sopenharmony_ci  // put card into reset state
18548c2ecf20Sopenharmony_ci  wr_plain (dev, offsetof(amb_mem, reset_control), word | AMB_RESET_BITS);
18558c2ecf20Sopenharmony_ci  // wait a short while
18568c2ecf20Sopenharmony_ci  udelay (10);
18578c2ecf20Sopenharmony_ci#if 1
18588c2ecf20Sopenharmony_ci  // put card into known good state
18598c2ecf20Sopenharmony_ci  wr_plain (dev, offsetof(amb_mem, interrupt_control), AMB_DOORBELL_BITS);
18608c2ecf20Sopenharmony_ci  // clear all interrupts just in case
18618c2ecf20Sopenharmony_ci  wr_plain (dev, offsetof(amb_mem, interrupt), -1);
18628c2ecf20Sopenharmony_ci#endif
18638c2ecf20Sopenharmony_ci  // clear self-test done flag
18648c2ecf20Sopenharmony_ci  wr_plain (dev, offsetof(amb_mem, mb.loader.ready), 0);
18658c2ecf20Sopenharmony_ci  // take card out of reset state
18668c2ecf20Sopenharmony_ci  wr_plain (dev, offsetof(amb_mem, reset_control), word &~ AMB_RESET_BITS);
18678c2ecf20Sopenharmony_ci
18688c2ecf20Sopenharmony_ci  if (diags) {
18698c2ecf20Sopenharmony_ci    unsigned long timeout;
18708c2ecf20Sopenharmony_ci    // 4.2 second wait
18718c2ecf20Sopenharmony_ci    msleep(4200);
18728c2ecf20Sopenharmony_ci    // half second time-out
18738c2ecf20Sopenharmony_ci    timeout = 500;
18748c2ecf20Sopenharmony_ci    while (!rd_plain (dev, offsetof(amb_mem, mb.loader.ready)))
18758c2ecf20Sopenharmony_ci      if (timeout) {
18768c2ecf20Sopenharmony_ci	timeout = msleep_interruptible(timeout);
18778c2ecf20Sopenharmony_ci      } else {
18788c2ecf20Sopenharmony_ci	PRINTD (DBG_LOAD|DBG_ERR, "reset timed out");
18798c2ecf20Sopenharmony_ci	return -ETIMEDOUT;
18808c2ecf20Sopenharmony_ci      }
18818c2ecf20Sopenharmony_ci
18828c2ecf20Sopenharmony_ci    // get results of self-test
18838c2ecf20Sopenharmony_ci    // XXX double check byte-order
18848c2ecf20Sopenharmony_ci    word = rd_mem (dev, offsetof(amb_mem, mb.loader.result));
18858c2ecf20Sopenharmony_ci    if (word & SELF_TEST_FAILURE) {
18868c2ecf20Sopenharmony_ci      if (word & GPINT_TST_FAILURE)
18878c2ecf20Sopenharmony_ci	sf ("interrupt");
18888c2ecf20Sopenharmony_ci      if (word & SUNI_DATA_PATTERN_FAILURE)
18898c2ecf20Sopenharmony_ci	sf ("SUNI data pattern");
18908c2ecf20Sopenharmony_ci      if (word & SUNI_DATA_BITS_FAILURE)
18918c2ecf20Sopenharmony_ci	sf ("SUNI data bits");
18928c2ecf20Sopenharmony_ci      if (word & SUNI_UTOPIA_FAILURE)
18938c2ecf20Sopenharmony_ci	sf ("SUNI UTOPIA interface");
18948c2ecf20Sopenharmony_ci      if (word & SUNI_FIFO_FAILURE)
18958c2ecf20Sopenharmony_ci	sf ("SUNI cell buffer FIFO");
18968c2ecf20Sopenharmony_ci      if (word & SRAM_FAILURE)
18978c2ecf20Sopenharmony_ci	sf ("bad SRAM");
18988c2ecf20Sopenharmony_ci      // better return value?
18998c2ecf20Sopenharmony_ci      return -EIO;
19008c2ecf20Sopenharmony_ci    }
19018c2ecf20Sopenharmony_ci
19028c2ecf20Sopenharmony_ci  }
19038c2ecf20Sopenharmony_ci  return 0;
19048c2ecf20Sopenharmony_ci}
19058c2ecf20Sopenharmony_ci
19068c2ecf20Sopenharmony_ci/********** transfer and start the microcode **********/
19078c2ecf20Sopenharmony_ci
19088c2ecf20Sopenharmony_cistatic int ucode_init(loader_block *lb, amb_dev *dev)
19098c2ecf20Sopenharmony_ci{
19108c2ecf20Sopenharmony_ci  const struct firmware *fw;
19118c2ecf20Sopenharmony_ci  unsigned long start_address;
19128c2ecf20Sopenharmony_ci  const struct ihex_binrec *rec;
19138c2ecf20Sopenharmony_ci  const char *errmsg = NULL;
19148c2ecf20Sopenharmony_ci  int res;
19158c2ecf20Sopenharmony_ci
19168c2ecf20Sopenharmony_ci  res = request_ihex_firmware(&fw, "atmsar11.fw", &dev->pci_dev->dev);
19178c2ecf20Sopenharmony_ci  if (res) {
19188c2ecf20Sopenharmony_ci    PRINTK (KERN_ERR, "Cannot load microcode data");
19198c2ecf20Sopenharmony_ci    return res;
19208c2ecf20Sopenharmony_ci  }
19218c2ecf20Sopenharmony_ci
19228c2ecf20Sopenharmony_ci  /* First record contains just the start address */
19238c2ecf20Sopenharmony_ci  rec = (const struct ihex_binrec *)fw->data;
19248c2ecf20Sopenharmony_ci  if (be16_to_cpu(rec->len) != sizeof(__be32) || be32_to_cpu(rec->addr)) {
19258c2ecf20Sopenharmony_ci    errmsg = "no start record";
19268c2ecf20Sopenharmony_ci    goto fail;
19278c2ecf20Sopenharmony_ci  }
19288c2ecf20Sopenharmony_ci  start_address = be32_to_cpup((__be32 *)rec->data);
19298c2ecf20Sopenharmony_ci
19308c2ecf20Sopenharmony_ci  rec = ihex_next_binrec(rec);
19318c2ecf20Sopenharmony_ci
19328c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_LOAD, "ucode_init");
19338c2ecf20Sopenharmony_ci
19348c2ecf20Sopenharmony_ci  while (rec) {
19358c2ecf20Sopenharmony_ci    PRINTD (DBG_LOAD, "starting region (%x, %u)", be32_to_cpu(rec->addr),
19368c2ecf20Sopenharmony_ci	    be16_to_cpu(rec->len));
19378c2ecf20Sopenharmony_ci    if (be16_to_cpu(rec->len) > 4 * MAX_TRANSFER_DATA) {
19388c2ecf20Sopenharmony_ci	    errmsg = "record too long";
19398c2ecf20Sopenharmony_ci	    goto fail;
19408c2ecf20Sopenharmony_ci    }
19418c2ecf20Sopenharmony_ci    if (be16_to_cpu(rec->len) & 3) {
19428c2ecf20Sopenharmony_ci	    errmsg = "odd number of bytes";
19438c2ecf20Sopenharmony_ci	    goto fail;
19448c2ecf20Sopenharmony_ci    }
19458c2ecf20Sopenharmony_ci    res = loader_write(lb, dev, rec);
19468c2ecf20Sopenharmony_ci    if (res)
19478c2ecf20Sopenharmony_ci      break;
19488c2ecf20Sopenharmony_ci
19498c2ecf20Sopenharmony_ci    res = loader_verify(lb, dev, rec);
19508c2ecf20Sopenharmony_ci    if (res)
19518c2ecf20Sopenharmony_ci      break;
19528c2ecf20Sopenharmony_ci    rec = ihex_next_binrec(rec);
19538c2ecf20Sopenharmony_ci  }
19548c2ecf20Sopenharmony_ci  release_firmware(fw);
19558c2ecf20Sopenharmony_ci  if (!res)
19568c2ecf20Sopenharmony_ci    res = loader_start(lb, dev, start_address);
19578c2ecf20Sopenharmony_ci
19588c2ecf20Sopenharmony_ci  return res;
19598c2ecf20Sopenharmony_cifail:
19608c2ecf20Sopenharmony_ci  release_firmware(fw);
19618c2ecf20Sopenharmony_ci  PRINTK(KERN_ERR, "Bad microcode data (%s)", errmsg);
19628c2ecf20Sopenharmony_ci  return -EINVAL;
19638c2ecf20Sopenharmony_ci}
19648c2ecf20Sopenharmony_ci
19658c2ecf20Sopenharmony_ci/********** give adapter parameters **********/
19668c2ecf20Sopenharmony_ci
19678c2ecf20Sopenharmony_cistatic inline __be32 bus_addr(void * addr) {
19688c2ecf20Sopenharmony_ci    return cpu_to_be32 (virt_to_bus (addr));
19698c2ecf20Sopenharmony_ci}
19708c2ecf20Sopenharmony_ci
19718c2ecf20Sopenharmony_cistatic int amb_talk(amb_dev *dev)
19728c2ecf20Sopenharmony_ci{
19738c2ecf20Sopenharmony_ci  adap_talk_block a;
19748c2ecf20Sopenharmony_ci  unsigned char pool;
19758c2ecf20Sopenharmony_ci  unsigned long timeout;
19768c2ecf20Sopenharmony_ci
19778c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW, "amb_talk %p", dev);
19788c2ecf20Sopenharmony_ci
19798c2ecf20Sopenharmony_ci  a.command_start = bus_addr (dev->cq.ptrs.start);
19808c2ecf20Sopenharmony_ci  a.command_end   = bus_addr (dev->cq.ptrs.limit);
19818c2ecf20Sopenharmony_ci  a.tx_start      = bus_addr (dev->txq.in.start);
19828c2ecf20Sopenharmony_ci  a.tx_end        = bus_addr (dev->txq.in.limit);
19838c2ecf20Sopenharmony_ci  a.txcom_start   = bus_addr (dev->txq.out.start);
19848c2ecf20Sopenharmony_ci  a.txcom_end     = bus_addr (dev->txq.out.limit);
19858c2ecf20Sopenharmony_ci
19868c2ecf20Sopenharmony_ci  for (pool = 0; pool < NUM_RX_POOLS; ++pool) {
19878c2ecf20Sopenharmony_ci    // the other "a" items are set up by the adapter
19888c2ecf20Sopenharmony_ci    a.rec_struct[pool].buffer_start = bus_addr (dev->rxq[pool].in.start);
19898c2ecf20Sopenharmony_ci    a.rec_struct[pool].buffer_end   = bus_addr (dev->rxq[pool].in.limit);
19908c2ecf20Sopenharmony_ci    a.rec_struct[pool].rx_start     = bus_addr (dev->rxq[pool].out.start);
19918c2ecf20Sopenharmony_ci    a.rec_struct[pool].rx_end       = bus_addr (dev->rxq[pool].out.limit);
19928c2ecf20Sopenharmony_ci    a.rec_struct[pool].buffer_size = cpu_to_be32 (dev->rxq[pool].buffer_size);
19938c2ecf20Sopenharmony_ci  }
19948c2ecf20Sopenharmony_ci
19958c2ecf20Sopenharmony_ci#ifdef AMB_NEW_MICROCODE
19968c2ecf20Sopenharmony_ci  // disable fast PLX prefetching
19978c2ecf20Sopenharmony_ci  a.init_flags = 0;
19988c2ecf20Sopenharmony_ci#endif
19998c2ecf20Sopenharmony_ci
20008c2ecf20Sopenharmony_ci  // pass the structure
20018c2ecf20Sopenharmony_ci  wr_mem (dev, offsetof(amb_mem, doorbell), virt_to_bus (&a));
20028c2ecf20Sopenharmony_ci
20038c2ecf20Sopenharmony_ci  // 2.2 second wait (must not touch doorbell during 2 second DMA test)
20048c2ecf20Sopenharmony_ci  msleep(2200);
20058c2ecf20Sopenharmony_ci  // give the adapter another half second?
20068c2ecf20Sopenharmony_ci  timeout = 500;
20078c2ecf20Sopenharmony_ci  while (rd_plain (dev, offsetof(amb_mem, doorbell)))
20088c2ecf20Sopenharmony_ci    if (timeout) {
20098c2ecf20Sopenharmony_ci      timeout = msleep_interruptible(timeout);
20108c2ecf20Sopenharmony_ci    } else {
20118c2ecf20Sopenharmony_ci      PRINTD (DBG_INIT|DBG_ERR, "adapter init timed out");
20128c2ecf20Sopenharmony_ci      return -ETIMEDOUT;
20138c2ecf20Sopenharmony_ci    }
20148c2ecf20Sopenharmony_ci
20158c2ecf20Sopenharmony_ci  return 0;
20168c2ecf20Sopenharmony_ci}
20178c2ecf20Sopenharmony_ci
20188c2ecf20Sopenharmony_ci// get microcode version
20198c2ecf20Sopenharmony_cistatic void amb_ucode_version(amb_dev *dev)
20208c2ecf20Sopenharmony_ci{
20218c2ecf20Sopenharmony_ci  u32 major;
20228c2ecf20Sopenharmony_ci  u32 minor;
20238c2ecf20Sopenharmony_ci  command cmd;
20248c2ecf20Sopenharmony_ci  cmd.request = cpu_to_be32 (SRB_GET_VERSION);
20258c2ecf20Sopenharmony_ci  while (command_do (dev, &cmd)) {
20268c2ecf20Sopenharmony_ci    set_current_state(TASK_UNINTERRUPTIBLE);
20278c2ecf20Sopenharmony_ci    schedule();
20288c2ecf20Sopenharmony_ci  }
20298c2ecf20Sopenharmony_ci  major = be32_to_cpu (cmd.args.version.major);
20308c2ecf20Sopenharmony_ci  minor = be32_to_cpu (cmd.args.version.minor);
20318c2ecf20Sopenharmony_ci  PRINTK (KERN_INFO, "microcode version is %u.%u", major, minor);
20328c2ecf20Sopenharmony_ci}
20338c2ecf20Sopenharmony_ci
20348c2ecf20Sopenharmony_ci// get end station address
20358c2ecf20Sopenharmony_cistatic void amb_esi(amb_dev *dev, u8 *esi)
20368c2ecf20Sopenharmony_ci{
20378c2ecf20Sopenharmony_ci  u32 lower4;
20388c2ecf20Sopenharmony_ci  u16 upper2;
20398c2ecf20Sopenharmony_ci  command cmd;
20408c2ecf20Sopenharmony_ci
20418c2ecf20Sopenharmony_ci  cmd.request = cpu_to_be32 (SRB_GET_BIA);
20428c2ecf20Sopenharmony_ci  while (command_do (dev, &cmd)) {
20438c2ecf20Sopenharmony_ci    set_current_state(TASK_UNINTERRUPTIBLE);
20448c2ecf20Sopenharmony_ci    schedule();
20458c2ecf20Sopenharmony_ci  }
20468c2ecf20Sopenharmony_ci  lower4 = be32_to_cpu (cmd.args.bia.lower4);
20478c2ecf20Sopenharmony_ci  upper2 = be32_to_cpu (cmd.args.bia.upper2);
20488c2ecf20Sopenharmony_ci  PRINTD (DBG_LOAD, "BIA: lower4: %08x, upper2 %04x", lower4, upper2);
20498c2ecf20Sopenharmony_ci
20508c2ecf20Sopenharmony_ci  if (esi) {
20518c2ecf20Sopenharmony_ci    unsigned int i;
20528c2ecf20Sopenharmony_ci
20538c2ecf20Sopenharmony_ci    PRINTDB (DBG_INIT, "ESI:");
20548c2ecf20Sopenharmony_ci    for (i = 0; i < ESI_LEN; ++i) {
20558c2ecf20Sopenharmony_ci      if (i < 4)
20568c2ecf20Sopenharmony_ci	  esi[i] = bitrev8(lower4>>(8*i));
20578c2ecf20Sopenharmony_ci      else
20588c2ecf20Sopenharmony_ci	  esi[i] = bitrev8(upper2>>(8*(i-4)));
20598c2ecf20Sopenharmony_ci      PRINTDM (DBG_INIT, " %02x", esi[i]);
20608c2ecf20Sopenharmony_ci    }
20618c2ecf20Sopenharmony_ci
20628c2ecf20Sopenharmony_ci    PRINTDE (DBG_INIT, "");
20638c2ecf20Sopenharmony_ci  }
20648c2ecf20Sopenharmony_ci
20658c2ecf20Sopenharmony_ci  return;
20668c2ecf20Sopenharmony_ci}
20678c2ecf20Sopenharmony_ci
20688c2ecf20Sopenharmony_cistatic void fixup_plx_window (amb_dev *dev, loader_block *lb)
20698c2ecf20Sopenharmony_ci{
20708c2ecf20Sopenharmony_ci	// fix up the PLX-mapped window base address to match the block
20718c2ecf20Sopenharmony_ci	unsigned long blb;
20728c2ecf20Sopenharmony_ci	u32 mapreg;
20738c2ecf20Sopenharmony_ci	blb = virt_to_bus(lb);
20748c2ecf20Sopenharmony_ci	// the kernel stack had better not ever cross a 1Gb boundary!
20758c2ecf20Sopenharmony_ci	mapreg = rd_plain (dev, offsetof(amb_mem, stuff[10]));
20768c2ecf20Sopenharmony_ci	mapreg &= ~onegigmask;
20778c2ecf20Sopenharmony_ci	mapreg |= blb & onegigmask;
20788c2ecf20Sopenharmony_ci	wr_plain (dev, offsetof(amb_mem, stuff[10]), mapreg);
20798c2ecf20Sopenharmony_ci	return;
20808c2ecf20Sopenharmony_ci}
20818c2ecf20Sopenharmony_ci
20828c2ecf20Sopenharmony_cistatic int amb_init(amb_dev *dev)
20838c2ecf20Sopenharmony_ci{
20848c2ecf20Sopenharmony_ci  loader_block lb;
20858c2ecf20Sopenharmony_ci
20868c2ecf20Sopenharmony_ci  u32 version;
20878c2ecf20Sopenharmony_ci
20888c2ecf20Sopenharmony_ci  if (amb_reset (dev, 1)) {
20898c2ecf20Sopenharmony_ci    PRINTK (KERN_ERR, "card reset failed!");
20908c2ecf20Sopenharmony_ci  } else {
20918c2ecf20Sopenharmony_ci    fixup_plx_window (dev, &lb);
20928c2ecf20Sopenharmony_ci
20938c2ecf20Sopenharmony_ci    if (get_loader_version (&lb, dev, &version)) {
20948c2ecf20Sopenharmony_ci      PRINTK (KERN_INFO, "failed to get loader version");
20958c2ecf20Sopenharmony_ci    } else {
20968c2ecf20Sopenharmony_ci      PRINTK (KERN_INFO, "loader version is %08x", version);
20978c2ecf20Sopenharmony_ci
20988c2ecf20Sopenharmony_ci      if (ucode_init (&lb, dev)) {
20998c2ecf20Sopenharmony_ci	PRINTK (KERN_ERR, "microcode failure");
21008c2ecf20Sopenharmony_ci      } else if (create_queues (dev, cmds, txs, rxs, rxs_bs)) {
21018c2ecf20Sopenharmony_ci	PRINTK (KERN_ERR, "failed to get memory for queues");
21028c2ecf20Sopenharmony_ci      } else {
21038c2ecf20Sopenharmony_ci
21048c2ecf20Sopenharmony_ci	if (amb_talk (dev)) {
21058c2ecf20Sopenharmony_ci	  PRINTK (KERN_ERR, "adapter did not accept queues");
21068c2ecf20Sopenharmony_ci	} else {
21078c2ecf20Sopenharmony_ci
21088c2ecf20Sopenharmony_ci	  amb_ucode_version (dev);
21098c2ecf20Sopenharmony_ci	  return 0;
21108c2ecf20Sopenharmony_ci
21118c2ecf20Sopenharmony_ci	} /* amb_talk */
21128c2ecf20Sopenharmony_ci
21138c2ecf20Sopenharmony_ci	destroy_queues (dev);
21148c2ecf20Sopenharmony_ci      } /* create_queues, ucode_init */
21158c2ecf20Sopenharmony_ci
21168c2ecf20Sopenharmony_ci      amb_reset (dev, 0);
21178c2ecf20Sopenharmony_ci    } /* get_loader_version */
21188c2ecf20Sopenharmony_ci
21198c2ecf20Sopenharmony_ci  } /* amb_reset */
21208c2ecf20Sopenharmony_ci
21218c2ecf20Sopenharmony_ci  return -EINVAL;
21228c2ecf20Sopenharmony_ci}
21238c2ecf20Sopenharmony_ci
21248c2ecf20Sopenharmony_cistatic void setup_dev(amb_dev *dev, struct pci_dev *pci_dev)
21258c2ecf20Sopenharmony_ci{
21268c2ecf20Sopenharmony_ci      unsigned char pool;
21278c2ecf20Sopenharmony_ci
21288c2ecf20Sopenharmony_ci      // set up known dev items straight away
21298c2ecf20Sopenharmony_ci      dev->pci_dev = pci_dev;
21308c2ecf20Sopenharmony_ci      pci_set_drvdata(pci_dev, dev);
21318c2ecf20Sopenharmony_ci
21328c2ecf20Sopenharmony_ci      dev->iobase = pci_resource_start (pci_dev, 1);
21338c2ecf20Sopenharmony_ci      dev->irq = pci_dev->irq;
21348c2ecf20Sopenharmony_ci      dev->membase = bus_to_virt(pci_resource_start(pci_dev, 0));
21358c2ecf20Sopenharmony_ci
21368c2ecf20Sopenharmony_ci      // flags (currently only dead)
21378c2ecf20Sopenharmony_ci      dev->flags = 0;
21388c2ecf20Sopenharmony_ci
21398c2ecf20Sopenharmony_ci      // Allocate cell rates (fibre)
21408c2ecf20Sopenharmony_ci      // ATM_OC3_PCR = 1555200000/8/270*260/53 - 29/53
21418c2ecf20Sopenharmony_ci      // to be really pedantic, this should be ATM_OC3c_PCR
21428c2ecf20Sopenharmony_ci      dev->tx_avail = ATM_OC3_PCR;
21438c2ecf20Sopenharmony_ci      dev->rx_avail = ATM_OC3_PCR;
21448c2ecf20Sopenharmony_ci
21458c2ecf20Sopenharmony_ci      // semaphore for txer/rxer modifications - we cannot use a
21468c2ecf20Sopenharmony_ci      // spinlock as the critical region needs to switch processes
21478c2ecf20Sopenharmony_ci      mutex_init(&dev->vcc_sf);
21488c2ecf20Sopenharmony_ci      // queue manipulation spinlocks; we want atomic reads and
21498c2ecf20Sopenharmony_ci      // writes to the queue descriptors (handles IRQ and SMP)
21508c2ecf20Sopenharmony_ci      // consider replacing "int pending" -> "atomic_t available"
21518c2ecf20Sopenharmony_ci      // => problem related to who gets to move queue pointers
21528c2ecf20Sopenharmony_ci      spin_lock_init (&dev->cq.lock);
21538c2ecf20Sopenharmony_ci      spin_lock_init (&dev->txq.lock);
21548c2ecf20Sopenharmony_ci      for (pool = 0; pool < NUM_RX_POOLS; ++pool)
21558c2ecf20Sopenharmony_ci	spin_lock_init (&dev->rxq[pool].lock);
21568c2ecf20Sopenharmony_ci}
21578c2ecf20Sopenharmony_ci
21588c2ecf20Sopenharmony_cistatic void setup_pci_dev(struct pci_dev *pci_dev)
21598c2ecf20Sopenharmony_ci{
21608c2ecf20Sopenharmony_ci	unsigned char lat;
21618c2ecf20Sopenharmony_ci
21628c2ecf20Sopenharmony_ci	// enable bus master accesses
21638c2ecf20Sopenharmony_ci	pci_set_master(pci_dev);
21648c2ecf20Sopenharmony_ci
21658c2ecf20Sopenharmony_ci	// frobnicate latency (upwards, usually)
21668c2ecf20Sopenharmony_ci	pci_read_config_byte (pci_dev, PCI_LATENCY_TIMER, &lat);
21678c2ecf20Sopenharmony_ci
21688c2ecf20Sopenharmony_ci	if (!pci_lat)
21698c2ecf20Sopenharmony_ci		pci_lat = (lat < MIN_PCI_LATENCY) ? MIN_PCI_LATENCY : lat;
21708c2ecf20Sopenharmony_ci
21718c2ecf20Sopenharmony_ci	if (lat != pci_lat) {
21728c2ecf20Sopenharmony_ci		PRINTK (KERN_INFO, "Changing PCI latency timer from %hu to %hu",
21738c2ecf20Sopenharmony_ci			lat, pci_lat);
21748c2ecf20Sopenharmony_ci		pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, pci_lat);
21758c2ecf20Sopenharmony_ci	}
21768c2ecf20Sopenharmony_ci}
21778c2ecf20Sopenharmony_ci
21788c2ecf20Sopenharmony_cistatic int amb_probe(struct pci_dev *pci_dev,
21798c2ecf20Sopenharmony_ci		     const struct pci_device_id *pci_ent)
21808c2ecf20Sopenharmony_ci{
21818c2ecf20Sopenharmony_ci	amb_dev * dev;
21828c2ecf20Sopenharmony_ci	int err;
21838c2ecf20Sopenharmony_ci	unsigned int irq;
21848c2ecf20Sopenharmony_ci
21858c2ecf20Sopenharmony_ci	err = pci_enable_device(pci_dev);
21868c2ecf20Sopenharmony_ci	if (err < 0) {
21878c2ecf20Sopenharmony_ci		PRINTK (KERN_ERR, "skipped broken (PLX rev 2) card");
21888c2ecf20Sopenharmony_ci		goto out;
21898c2ecf20Sopenharmony_ci	}
21908c2ecf20Sopenharmony_ci
21918c2ecf20Sopenharmony_ci	// read resources from PCI configuration space
21928c2ecf20Sopenharmony_ci	irq = pci_dev->irq;
21938c2ecf20Sopenharmony_ci
21948c2ecf20Sopenharmony_ci	if (pci_dev->device == PCI_DEVICE_ID_MADGE_AMBASSADOR_BAD) {
21958c2ecf20Sopenharmony_ci		PRINTK (KERN_ERR, "skipped broken (PLX rev 2) card");
21968c2ecf20Sopenharmony_ci		err = -EINVAL;
21978c2ecf20Sopenharmony_ci		goto out_disable;
21988c2ecf20Sopenharmony_ci	}
21998c2ecf20Sopenharmony_ci
22008c2ecf20Sopenharmony_ci	PRINTD (DBG_INFO, "found Madge ATM adapter (amb) at"
22018c2ecf20Sopenharmony_ci		" IO %llx, IRQ %u, MEM %p",
22028c2ecf20Sopenharmony_ci		(unsigned long long)pci_resource_start(pci_dev, 1),
22038c2ecf20Sopenharmony_ci		irq, bus_to_virt(pci_resource_start(pci_dev, 0)));
22048c2ecf20Sopenharmony_ci
22058c2ecf20Sopenharmony_ci	// check IO region
22068c2ecf20Sopenharmony_ci	err = pci_request_region(pci_dev, 1, DEV_LABEL);
22078c2ecf20Sopenharmony_ci	if (err < 0) {
22088c2ecf20Sopenharmony_ci		PRINTK (KERN_ERR, "IO range already in use!");
22098c2ecf20Sopenharmony_ci		goto out_disable;
22108c2ecf20Sopenharmony_ci	}
22118c2ecf20Sopenharmony_ci
22128c2ecf20Sopenharmony_ci	dev = kzalloc(sizeof(amb_dev), GFP_KERNEL);
22138c2ecf20Sopenharmony_ci	if (!dev) {
22148c2ecf20Sopenharmony_ci		PRINTK (KERN_ERR, "out of memory!");
22158c2ecf20Sopenharmony_ci		err = -ENOMEM;
22168c2ecf20Sopenharmony_ci		goto out_release;
22178c2ecf20Sopenharmony_ci	}
22188c2ecf20Sopenharmony_ci
22198c2ecf20Sopenharmony_ci	setup_dev(dev, pci_dev);
22208c2ecf20Sopenharmony_ci
22218c2ecf20Sopenharmony_ci	err = amb_init(dev);
22228c2ecf20Sopenharmony_ci	if (err < 0) {
22238c2ecf20Sopenharmony_ci		PRINTK (KERN_ERR, "adapter initialisation failure");
22248c2ecf20Sopenharmony_ci		goto out_free;
22258c2ecf20Sopenharmony_ci	}
22268c2ecf20Sopenharmony_ci
22278c2ecf20Sopenharmony_ci	setup_pci_dev(pci_dev);
22288c2ecf20Sopenharmony_ci
22298c2ecf20Sopenharmony_ci	// grab (but share) IRQ and install handler
22308c2ecf20Sopenharmony_ci	err = request_irq(irq, interrupt_handler, IRQF_SHARED, DEV_LABEL, dev);
22318c2ecf20Sopenharmony_ci	if (err < 0) {
22328c2ecf20Sopenharmony_ci		PRINTK (KERN_ERR, "request IRQ failed!");
22338c2ecf20Sopenharmony_ci		goto out_reset;
22348c2ecf20Sopenharmony_ci	}
22358c2ecf20Sopenharmony_ci
22368c2ecf20Sopenharmony_ci	dev->atm_dev = atm_dev_register (DEV_LABEL, &pci_dev->dev, &amb_ops, -1,
22378c2ecf20Sopenharmony_ci					 NULL);
22388c2ecf20Sopenharmony_ci	if (!dev->atm_dev) {
22398c2ecf20Sopenharmony_ci		PRINTD (DBG_ERR, "failed to register Madge ATM adapter");
22408c2ecf20Sopenharmony_ci		err = -EINVAL;
22418c2ecf20Sopenharmony_ci		goto out_free_irq;
22428c2ecf20Sopenharmony_ci	}
22438c2ecf20Sopenharmony_ci
22448c2ecf20Sopenharmony_ci	PRINTD (DBG_INFO, "registered Madge ATM adapter (no. %d) (%p) at %p",
22458c2ecf20Sopenharmony_ci		dev->atm_dev->number, dev, dev->atm_dev);
22468c2ecf20Sopenharmony_ci	dev->atm_dev->dev_data = (void *) dev;
22478c2ecf20Sopenharmony_ci
22488c2ecf20Sopenharmony_ci	// register our address
22498c2ecf20Sopenharmony_ci	amb_esi (dev, dev->atm_dev->esi);
22508c2ecf20Sopenharmony_ci
22518c2ecf20Sopenharmony_ci	// 0 bits for vpi, 10 bits for vci
22528c2ecf20Sopenharmony_ci	dev->atm_dev->ci_range.vpi_bits = NUM_VPI_BITS;
22538c2ecf20Sopenharmony_ci	dev->atm_dev->ci_range.vci_bits = NUM_VCI_BITS;
22548c2ecf20Sopenharmony_ci
22558c2ecf20Sopenharmony_ci	timer_setup(&dev->housekeeping, do_housekeeping, 0);
22568c2ecf20Sopenharmony_ci	mod_timer(&dev->housekeeping, jiffies);
22578c2ecf20Sopenharmony_ci
22588c2ecf20Sopenharmony_ci	// enable host interrupts
22598c2ecf20Sopenharmony_ci	interrupts_on (dev);
22608c2ecf20Sopenharmony_ci
22618c2ecf20Sopenharmony_ciout:
22628c2ecf20Sopenharmony_ci	return err;
22638c2ecf20Sopenharmony_ci
22648c2ecf20Sopenharmony_ciout_free_irq:
22658c2ecf20Sopenharmony_ci	free_irq(irq, dev);
22668c2ecf20Sopenharmony_ciout_reset:
22678c2ecf20Sopenharmony_ci	amb_reset(dev, 0);
22688c2ecf20Sopenharmony_ciout_free:
22698c2ecf20Sopenharmony_ci	kfree(dev);
22708c2ecf20Sopenharmony_ciout_release:
22718c2ecf20Sopenharmony_ci	pci_release_region(pci_dev, 1);
22728c2ecf20Sopenharmony_ciout_disable:
22738c2ecf20Sopenharmony_ci	pci_disable_device(pci_dev);
22748c2ecf20Sopenharmony_ci	goto out;
22758c2ecf20Sopenharmony_ci}
22768c2ecf20Sopenharmony_ci
22778c2ecf20Sopenharmony_ci
22788c2ecf20Sopenharmony_cistatic void amb_remove_one(struct pci_dev *pci_dev)
22798c2ecf20Sopenharmony_ci{
22808c2ecf20Sopenharmony_ci	struct amb_dev *dev;
22818c2ecf20Sopenharmony_ci
22828c2ecf20Sopenharmony_ci	dev = pci_get_drvdata(pci_dev);
22838c2ecf20Sopenharmony_ci
22848c2ecf20Sopenharmony_ci	PRINTD(DBG_INFO|DBG_INIT, "closing %p (atm_dev = %p)", dev, dev->atm_dev);
22858c2ecf20Sopenharmony_ci	del_timer_sync(&dev->housekeeping);
22868c2ecf20Sopenharmony_ci	// the drain should not be necessary
22878c2ecf20Sopenharmony_ci	drain_rx_pools(dev);
22888c2ecf20Sopenharmony_ci	interrupts_off(dev);
22898c2ecf20Sopenharmony_ci	amb_reset(dev, 0);
22908c2ecf20Sopenharmony_ci	free_irq(dev->irq, dev);
22918c2ecf20Sopenharmony_ci	pci_disable_device(pci_dev);
22928c2ecf20Sopenharmony_ci	destroy_queues(dev);
22938c2ecf20Sopenharmony_ci	atm_dev_deregister(dev->atm_dev);
22948c2ecf20Sopenharmony_ci	kfree(dev);
22958c2ecf20Sopenharmony_ci	pci_release_region(pci_dev, 1);
22968c2ecf20Sopenharmony_ci}
22978c2ecf20Sopenharmony_ci
22988c2ecf20Sopenharmony_cistatic void __init amb_check_args (void) {
22998c2ecf20Sopenharmony_ci  unsigned char pool;
23008c2ecf20Sopenharmony_ci  unsigned int max_rx_size;
23018c2ecf20Sopenharmony_ci
23028c2ecf20Sopenharmony_ci#ifdef DEBUG_AMBASSADOR
23038c2ecf20Sopenharmony_ci  PRINTK (KERN_NOTICE, "debug bitmap is %hx", debug &= DBG_MASK);
23048c2ecf20Sopenharmony_ci#else
23058c2ecf20Sopenharmony_ci  if (debug)
23068c2ecf20Sopenharmony_ci    PRINTK (KERN_NOTICE, "no debugging support");
23078c2ecf20Sopenharmony_ci#endif
23088c2ecf20Sopenharmony_ci
23098c2ecf20Sopenharmony_ci  if (cmds < MIN_QUEUE_SIZE)
23108c2ecf20Sopenharmony_ci    PRINTK (KERN_NOTICE, "cmds has been raised to %u",
23118c2ecf20Sopenharmony_ci	    cmds = MIN_QUEUE_SIZE);
23128c2ecf20Sopenharmony_ci
23138c2ecf20Sopenharmony_ci  if (txs < MIN_QUEUE_SIZE)
23148c2ecf20Sopenharmony_ci    PRINTK (KERN_NOTICE, "txs has been raised to %u",
23158c2ecf20Sopenharmony_ci	    txs = MIN_QUEUE_SIZE);
23168c2ecf20Sopenharmony_ci
23178c2ecf20Sopenharmony_ci  for (pool = 0; pool < NUM_RX_POOLS; ++pool)
23188c2ecf20Sopenharmony_ci    if (rxs[pool] < MIN_QUEUE_SIZE)
23198c2ecf20Sopenharmony_ci      PRINTK (KERN_NOTICE, "rxs[%hu] has been raised to %u",
23208c2ecf20Sopenharmony_ci	      pool, rxs[pool] = MIN_QUEUE_SIZE);
23218c2ecf20Sopenharmony_ci
23228c2ecf20Sopenharmony_ci  // buffers sizes should be greater than zero and strictly increasing
23238c2ecf20Sopenharmony_ci  max_rx_size = 0;
23248c2ecf20Sopenharmony_ci  for (pool = 0; pool < NUM_RX_POOLS; ++pool)
23258c2ecf20Sopenharmony_ci    if (rxs_bs[pool] <= max_rx_size)
23268c2ecf20Sopenharmony_ci      PRINTK (KERN_NOTICE, "useless pool (rxs_bs[%hu] = %u)",
23278c2ecf20Sopenharmony_ci	      pool, rxs_bs[pool]);
23288c2ecf20Sopenharmony_ci    else
23298c2ecf20Sopenharmony_ci      max_rx_size = rxs_bs[pool];
23308c2ecf20Sopenharmony_ci
23318c2ecf20Sopenharmony_ci  if (rx_lats < MIN_RX_BUFFERS)
23328c2ecf20Sopenharmony_ci    PRINTK (KERN_NOTICE, "rx_lats has been raised to %u",
23338c2ecf20Sopenharmony_ci	    rx_lats = MIN_RX_BUFFERS);
23348c2ecf20Sopenharmony_ci
23358c2ecf20Sopenharmony_ci  return;
23368c2ecf20Sopenharmony_ci}
23378c2ecf20Sopenharmony_ci
23388c2ecf20Sopenharmony_ci/********** module stuff **********/
23398c2ecf20Sopenharmony_ci
23408c2ecf20Sopenharmony_ciMODULE_AUTHOR(maintainer_string);
23418c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(description_string);
23428c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
23438c2ecf20Sopenharmony_ciMODULE_FIRMWARE("atmsar11.fw");
23448c2ecf20Sopenharmony_cimodule_param(debug,   ushort, 0644);
23458c2ecf20Sopenharmony_cimodule_param(cmds,    uint, 0);
23468c2ecf20Sopenharmony_cimodule_param(txs,     uint, 0);
23478c2ecf20Sopenharmony_cimodule_param_array(rxs,     uint, NULL, 0);
23488c2ecf20Sopenharmony_cimodule_param_array(rxs_bs,  uint, NULL, 0);
23498c2ecf20Sopenharmony_cimodule_param(rx_lats, uint, 0);
23508c2ecf20Sopenharmony_cimodule_param(pci_lat, byte, 0);
23518c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug,   "debug bitmap, see .h file");
23528c2ecf20Sopenharmony_ciMODULE_PARM_DESC(cmds,    "number of command queue entries");
23538c2ecf20Sopenharmony_ciMODULE_PARM_DESC(txs,     "number of TX queue entries");
23548c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rxs,     "number of RX queue entries [" __MODULE_STRING(NUM_RX_POOLS) "]");
23558c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rxs_bs,  "size of RX buffers [" __MODULE_STRING(NUM_RX_POOLS) "]");
23568c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rx_lats, "number of extra buffers to cope with RX latencies");
23578c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pci_lat, "PCI latency in bus cycles");
23588c2ecf20Sopenharmony_ci
23598c2ecf20Sopenharmony_ci/********** module entry **********/
23608c2ecf20Sopenharmony_ci
23618c2ecf20Sopenharmony_cistatic const struct pci_device_id amb_pci_tbl[] = {
23628c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(MADGE, PCI_DEVICE_ID_MADGE_AMBASSADOR), 0 },
23638c2ecf20Sopenharmony_ci	{ PCI_VDEVICE(MADGE, PCI_DEVICE_ID_MADGE_AMBASSADOR_BAD), 0 },
23648c2ecf20Sopenharmony_ci	{ 0, }
23658c2ecf20Sopenharmony_ci};
23668c2ecf20Sopenharmony_ci
23678c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, amb_pci_tbl);
23688c2ecf20Sopenharmony_ci
23698c2ecf20Sopenharmony_cistatic struct pci_driver amb_driver = {
23708c2ecf20Sopenharmony_ci	.name =		"amb",
23718c2ecf20Sopenharmony_ci	.probe =	amb_probe,
23728c2ecf20Sopenharmony_ci	.remove =	amb_remove_one,
23738c2ecf20Sopenharmony_ci	.id_table =	amb_pci_tbl,
23748c2ecf20Sopenharmony_ci};
23758c2ecf20Sopenharmony_ci
23768c2ecf20Sopenharmony_cistatic int __init amb_module_init (void)
23778c2ecf20Sopenharmony_ci{
23788c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_INIT, "init_module");
23798c2ecf20Sopenharmony_ci
23808c2ecf20Sopenharmony_ci  BUILD_BUG_ON(sizeof(amb_mem) != 4*16 + 4*12);
23818c2ecf20Sopenharmony_ci
23828c2ecf20Sopenharmony_ci  show_version();
23838c2ecf20Sopenharmony_ci
23848c2ecf20Sopenharmony_ci  amb_check_args();
23858c2ecf20Sopenharmony_ci
23868c2ecf20Sopenharmony_ci  // get the juice
23878c2ecf20Sopenharmony_ci  return pci_register_driver(&amb_driver);
23888c2ecf20Sopenharmony_ci}
23898c2ecf20Sopenharmony_ci
23908c2ecf20Sopenharmony_ci/********** module exit **********/
23918c2ecf20Sopenharmony_ci
23928c2ecf20Sopenharmony_cistatic void __exit amb_module_exit (void)
23938c2ecf20Sopenharmony_ci{
23948c2ecf20Sopenharmony_ci  PRINTD (DBG_FLOW|DBG_INIT, "cleanup_module");
23958c2ecf20Sopenharmony_ci
23968c2ecf20Sopenharmony_ci  pci_unregister_driver(&amb_driver);
23978c2ecf20Sopenharmony_ci}
23988c2ecf20Sopenharmony_ci
23998c2ecf20Sopenharmony_cimodule_init(amb_module_init);
24008c2ecf20Sopenharmony_cimodule_exit(amb_module_exit);
2401