11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Madge Ambassador ATM Adapter driver.
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Copyright (C) 1995-1999  Madge Networks Ltd.
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  This program is free software; you can redistribute it and/or modify
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  it under the terms of the GNU General Public License as published by
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  the Free Software Foundation; either version 2 of the License, or
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  (at your option) any later version.
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  This program is distributed in the hope that it will be useful,
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  but WITHOUT ANY WARRANTY; without even the implied warranty of
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  GNU General Public License for more details.
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  You should have received a copy of the GNU General Public License
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  along with this program; if not, write to the Free Software
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  The GNU GPL is contained in /usr/doc/copyright/GPL on a Debian
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  system and in the file COPYING in the Linux kernel source.
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* * dedicated to the memory of Graham Gordon 1971-1998 * */
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/types.h>
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/pci.h>
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ioport.h>
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/atmdev.h>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h>
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h>
343c6b377321678c649f9b3c66da0149975c614102Randy Dunlap#include <linux/poison.h>
350a8588578508a6c58a30ad1a7e23b285cfab49f3Akinobu Mita#include <linux/bitrev.h>
36eff0dee54674a449e7f160aad9f3e0d38e6983ebDaniel Walker#include <linux/mutex.h>
3727d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse#include <linux/firmware.h>
3827d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse#include <linux/ihex.h>
395a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4160063497a95e716c9a689af3be2687d261f115b4Arun Sharma#include <linux/atomic.h>
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/byteorder.h>
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "ambassador.h"
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define maintainer_string "Giuliano Procida at Madge Networks <gprocida@madge.com>"
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define description_string "Madge ATM Ambassador driver"
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define version_string "1.2.4"
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void __init show_version (void) {
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  printk ("%s version %s\n", description_string, version_string);
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Theory of Operation
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  I Hardware, detection, initialisation and shutdown.
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  1. Supported Hardware
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  This driver is for the PCI ATMizer-based Ambassador card (except
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  very early versions). It is not suitable for the similar EISA "TR7"
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  card. Commercially, both cards are known as Collage Server ATM
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  adapters.
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  The loader supports image transfer to the card, image start and few
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  other miscellaneous commands.
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Only AAL5 is supported with vpi = 0 and vci in the range 0 to 1023.
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  The cards are big-endian.
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  2. Detection
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Standard PCI stuff, the early cards are detected and rejected.
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  3. Initialisation
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  The cards are reset and the self-test results are checked. The
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  microcode image is then transferred and started. This waits for a
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  pointer to a descriptor containing details of the host-based queues
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  and buffers and various parameters etc. Once they are processed
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  normal operations may begin. The BIA is read using a microcode
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  command.
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  4. Shutdown
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  This may be accomplished either by a card reset or via the microcode
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  shutdown command. Further investigation required.
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  5. Persistent state
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  The card reset does not affect PCI configuration (good) or the
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  contents of several other "shared run-time registers" (bad) which
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  include doorbell and interrupt control as well as EEPROM and PCI
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  control. The driver must be careful when modifying these registers
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  not to touch bits it does not use and to undo any changes at exit.
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  II Driver software
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  0. Generalities
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  The adapter is quite intelligent (fast) and has a simple interface
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  (few features). VPI is always zero, 1024 VCIs are supported. There
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  is limited cell rate support. UBR channels can be capped and ABR
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  (explicit rate, but not EFCI) is supported. There is no CBR or VBR
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  support.
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  1. Driver <-> Adapter Communication
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Apart from the basic loader commands, the driver communicates
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  through three entities: the command queue (CQ), the transmit queue
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  pair (TXQ) and the receive queue pairs (RXQ). These three entities
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  are set up by the host and passed to the microcode just after it has
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  been started.
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  All queues are host-based circular queues. They are contiguous and
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  (due to hardware limitations) have some restrictions as to their
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  locations in (bus) memory. They are of the "full means the same as
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  empty so don't do that" variety since the adapter uses pointers
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  internally.
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  The queue pairs work as follows: one queue is for supply to the
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  adapter, items in it are pending and are owned by the adapter; the
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  other is the queue for return from the adapter, items in it have
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  been dealt with by the adapter. The host adds items to the supply
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  (TX descriptors and free RX buffer descriptors) and removes items
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  from the return (TX and RX completions). The adapter deals with out
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  of order completions.
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Interrupts (card to host) and the doorbell (host to card) are used
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  for signalling.
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  1. CQ
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  This is to communicate "open VC", "close VC", "get stats" etc. to
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  the adapter. At most one command is retired every millisecond by the
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  card. There is no out of order completion or notification. The
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  driver needs to check the return code of the command, waiting as
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  appropriate.
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  2. TXQ
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  TX supply items are of variable length (scatter gather support) and
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  so the queue items are (more or less) pointers to the real thing.
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Each TX supply item contains a unique, host-supplied handle (the skb
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  bus address seems most sensible as this works for Alphas as well,
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  there is no need to do any endian conversions on the handles).
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  TX return items consist of just the handles above.
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  3. RXQ (up to 4 of these with different lengths and buffer sizes)
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  RX supply items consist of a unique, host-supplied handle (the skb
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  bus address again) and a pointer to the buffer data area.
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  RX return items consist of the handle above, the VC, length and a
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  status word. This just screams "oh so easy" doesn't it?
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Note on RX pool sizes:
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Each pool should have enough buffers to handle a back-to-back stream
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  of minimum sized frames on a single VC. For example:
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    frame spacing = 3us (about right)
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    delay = IRQ lat + RX handling + RX buffer replenish = 20 (us)  (a guess)
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    min number of buffers for one VC = 1 + delay/spacing (buffers)
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    delay/spacing = latency = (20+2)/3 = 7 (buffers)  (rounding up)
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  The 20us delay assumes that there is no need to sleep; if we need to
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  sleep to get buffers we are going to drop frames anyway.
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  In fact, each pool should have enough buffers to support the
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  simultaneous reassembly of a separate frame on each VC and cope with
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  the case in which frames complete in round robin cell fashion on
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  each VC.
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Only one frame can complete at each cell arrival, so if "n" VCs are
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  open, the worst case is to have them all complete frames together
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  followed by all starting new frames together.
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    desired number of buffers = n + delay/spacing
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  These are the extreme requirements, however, they are "n+k" for some
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  "k" so we have only the constant to choose. This is the argument
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  rx_lats which current defaults to 7.
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Actually, "n ? n+k : 0" is better and this is what is implemented,
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  subject to the limit given by the pool size.
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  4. Driver locking
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Simple spinlocks are used around the TX and RX queue mechanisms.
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Anyone with a faster, working method is welcome to implement it.
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  The adapter command queue is protected with a spinlock. We always
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wait for commands to complete.
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  A more complex form of locking is used around parts of the VC open
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  and close functions. There are three reasons for a lock: 1. we need
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  to do atomic rate reservation and release (not used yet), 2. Opening
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  sometimes involves two adapter commands which must not be separated
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  by another command on the same VC, 3. the changes to RX pool size
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  must be atomic. The lock needs to work over context switches, so we
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  use a semaphore.
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  III Hardware Features and Microcode Bugs
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  1. Byte Ordering
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  *%^"$&%^$*&^"$(%^$#&^%$(&#%$*(&^#%!"!"!*!
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  2. Memory access
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  All structures that are not accessed using DMA must be 4-byte
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  aligned (not a problem) and must not cross 4MB boundaries.
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  There is a DMA memory hole at E0000000-E00000FF (groan).
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  TX fragments (DMA read) must not cross 4MB boundaries (would be 16MB
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  but for a hardware bug).
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  RX buffers (DMA write) must not cross 16MB boundaries and must
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  include spare trailing bytes up to the next 4-byte boundary; they
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  will be written with rubbish.
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  The PLX likes to prefetch; if reading up to 4 u32 past the end of
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  each TX fragment is not a problem, then TX can be made to go a
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  little faster by passing a flag at init that disables a prefetch
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  workaround. We do not pass this flag. (new microcode only)
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Now we:
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Note that alloc_skb rounds up size to a 16byte boundary.
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Ensure all areas do not traverse 4MB boundaries.
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Ensure all areas do not start at a E00000xx bus address.
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  (I cannot be certain, but this may always hold with Linux)
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Make all failures cause a loud message.
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Discard non-conforming SKBs (causes TX failure or RX fill delay).
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Discard non-conforming TX fragment descriptors (the TX fails).
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  In the future we could:
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Allow RX areas that traverse 4MB (but not 16MB) boundaries.
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Segment TX areas into some/more fragments, when necessary.
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Relax checks for non-DMA items (ignore hole).
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Give scatter-gather (iovec) requirements using ???. (?)
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  3. VC close is broken (only for new microcode)
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  The VC close adapter microcode command fails to do anything if any
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  frames have been received on the VC but none have been transmitted.
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Frames continue to be reassembled and passed (with IRQ) to the
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  driver.
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  IV To Do List
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Fix bugs!
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Timer code may be broken.
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Deal with buggy VC close (somehow) in microcode 12.
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Handle interrupted and/or non-blocking writes - is this a job for
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    the protocol layer?
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Add code to break up TX fragments when they span 4MB boundaries.
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Add SUNI phy layer (need to know where SUNI lives on card).
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Implement a tx_alloc fn to (a) satisfy TX alignment etc. and (b)
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    leave extra headroom space for Ambassador TX descriptors.
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Understand these elements of struct atm_vcc: recvq (proto?),
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    sleep, callback, listenq, backlog_quota, reply and user_back.
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Adjust TX/RX skb allocation to favour IP with LANE/CLIP (configurable).
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Impose a TX-pending limit (2?) on each VC, help avoid TX q overflow.
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Decide whether RX buffer recycling is or can be made completely safe;
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    turn it back on. It looks like Werner is going to axe this.
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Implement QoS changes on open VCs (involves extracting parts of VC open
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    and close into separate functions and using them to make changes).
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Hack on command queue so that someone can issue multiple commands and wait
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    on the last one (OR only "no-op" or "wait" commands are waited for).
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Eliminate need for while-schedule around do_command.
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void do_housekeeping (unsigned long arg);
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** globals **********/
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned short debug = 0;
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int cmds = 8;
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int txs = 32;
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int rxs[NUM_RX_POOLS] = { 64, 64, 64, 64 };
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int rxs_bs[NUM_RX_POOLS] = { 4080, 12240, 36720, 65535 };
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int rx_lats = 7;
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned char pci_lat = 0;
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const unsigned long onegigmask = -1 << 30;
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** access to adapter **********/
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void wr_plain (const amb_dev * dev, size_t addr, u32 data) {
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_REGS, "wr: %08zx <- %08x", addr, data);
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef AMB_MMIO
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  dev->membase[addr / sizeof(u32)] = data;
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  outl (data, dev->iobase + addr);
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline u32 rd_plain (const amb_dev * dev, size_t addr) {
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef AMB_MMIO
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u32 data = dev->membase[addr / sizeof(u32)];
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u32 data = inl (dev->iobase + addr);
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_REGS, "rd: %08zx -> %08x", addr, data);
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return data;
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void wr_mem (const amb_dev * dev, size_t addr, u32 data) {
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  __be32 be = cpu_to_be32 (data);
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_REGS, "wr: %08zx <- %08x b[%08x]", addr, data, be);
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef AMB_MMIO
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  dev->membase[addr / sizeof(u32)] = be;
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  outl (be, dev->iobase + addr);
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline u32 rd_mem (const amb_dev * dev, size_t addr) {
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef AMB_MMIO
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  __be32 be = dev->membase[addr / sizeof(u32)];
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  __be32 be = inl (dev->iobase + addr);
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u32 data = be32_to_cpu (be);
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_REGS, "rd: %08zx -> %08x b[%08x]", addr, data, be);
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return data;
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** dump routines **********/
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void dump_registers (const amb_dev * dev) {
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_AMBASSADOR
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (debug & DBG_REGS) {
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    size_t i;
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_REGS, "reading PLX control: ");
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    for (i = 0x00; i < 0x30; i += sizeof(u32))
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      rd_mem (dev, i);
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_REGS, "reading mailboxes: ");
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    for (i = 0x40; i < 0x60; i += sizeof(u32))
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      rd_mem (dev, i);
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_REGS, "reading doorb irqev irqen reset:");
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    for (i = 0x60; i < 0x70; i += sizeof(u32))
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      rd_mem (dev, i);
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  (void) dev;
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void dump_loader_block (volatile loader_block * lb) {
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_AMBASSADOR
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned int i;
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTDB (DBG_LOAD, "lb @ %p; res: %d, cmd: %d, pay:",
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   lb, be32_to_cpu (lb->result), be32_to_cpu (lb->command));
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  for (i = 0; i < MAX_COMMAND_DATA; ++i)
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTDM (DBG_LOAD, " %08x", be32_to_cpu (lb->payload.data[i]));
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTDE (DBG_LOAD, ", vld: %08x", be32_to_cpu (lb->valid));
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  (void) lb;
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void dump_command (command * cmd) {
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_AMBASSADOR
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned int i;
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTDB (DBG_CMD, "cmd @ %p, req: %08x, pars:",
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   cmd, /*be32_to_cpu*/ (cmd->request));
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  for (i = 0; i < 3; ++i)
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTDM (DBG_CMD, " %08x", /*be32_to_cpu*/ (cmd->args.par[i]));
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTDE (DBG_CMD, "");
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  (void) cmd;
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void dump_skb (char * prefix, unsigned int vc, struct sk_buff * skb) {
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_AMBASSADOR
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned int i;
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned char * data = skb->data;
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTDB (DBG_DATA, "%s(%u) ", prefix, vc);
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  for (i=0; i<skb->len && i < 256;i++)
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTDM (DBG_DATA, "%02x ", data[i]);
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTDE (DBG_DATA,"");
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  (void) prefix;
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  (void) vc;
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  (void) skb;
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** check memory areas for use by Ambassador **********/
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* see limitations under Hardware Features */
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
421a5b2db67139e991d9e9e19260989d0e66a03a2b2Denys Vlasenkostatic int check_area (void * start, size_t length) {
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // assumes length > 0
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  const u32 fourmegmask = -1 << 22;
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  const u32 twofivesixmask = -1 << 8;
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  const u32 starthole = 0xE0000000;
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u32 startaddress = virt_to_bus (start);
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u32 lastaddress = startaddress+length-1;
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if ((startaddress ^ lastaddress) & fourmegmask ||
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      (startaddress & twofivesixmask) == starthole) {
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTK (KERN_ERR, "check_area failure: [%x,%x] - mail maintainer!",
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    startaddress, lastaddress);
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -1;
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  } else {
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return 0;
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** free an skb (as per ATM device driver documentation) **********/
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
440a5b2db67139e991d9e9e19260989d0e66a03a2b2Denys Vlasenkostatic void amb_kfree_skb (struct sk_buff * skb) {
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (ATM_SKB(skb)->vcc->pop) {
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    ATM_SKB(skb)->vcc->pop (ATM_SKB(skb)->vcc, skb);
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  } else {
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev_kfree_skb_any (skb);
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** TX completion **********/
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
450a5b2db67139e991d9e9e19260989d0e66a03a2b2Denys Vlasenkostatic void tx_complete (amb_dev * dev, tx_out * tx) {
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  tx_simple * tx_descr = bus_to_virt (tx->handle);
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  struct sk_buff * skb = tx_descr->skb;
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_TX, "tx_complete %p %p", dev, tx);
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // VC layer stats
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  atomic_inc(&ATM_SKB(skb)->vcc->stats->tx);
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // free the descriptor
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  kfree (tx_descr);
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // free the skb
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  amb_kfree_skb (skb);
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  dev->stats.tx_ok++;
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** RX completion **********/
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rx_complete (amb_dev * dev, rx_out * rx) {
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  struct sk_buff * skb = bus_to_virt (rx->handle);
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u16 vc = be16_to_cpu (rx->vc);
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // unused: u16 lec_id = be16_to_cpu (rx->lec_id);
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u16 status = be16_to_cpu (rx->status);
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u16 rx_len = be16_to_cpu (rx->length);
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_RX, "rx_complete %p %p (len=%hu)", dev, rx, rx_len);
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // XXX move this in and add to VC stats ???
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (!status) {
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    struct atm_vcc * atm_vcc = dev->rxer[vc];
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev->stats.rx.ok++;
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (atm_vcc) {
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (rx_len <= atm_vcc->qos.rxtp.max_sdu) {
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (atm_charge (atm_vcc, skb->truesize)) {
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  // prepare socket buffer
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  ATM_SKB(skb)->vcc = atm_vcc;
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  skb_put (skb, rx_len);
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  dump_skb ("<<<", vc, skb);
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  // VC layer stats
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  atomic_inc(&atm_vcc->stats->rx);
499a61bbcf28a8cb0ba56f8193d512f7222e711a294Patrick McHardy	  __net_timestamp(skb);
50025985edcedea6396277003854657b5f3cb31a628Lucas De Marchi	  // end of our responsibility
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  atm_vcc->push (atm_vcc, skb);
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  return;
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  // someone fix this (message), please!
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  PRINTD (DBG_INFO|DBG_RX, "dropped thanks to atm_charge (vc %hu, truesize %u)", vc, skb->truesize);
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  // drop stats incremented in atm_charge
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      } else {
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      	PRINTK (KERN_INFO, "dropped over-size frame");
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// should we count this?
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	atomic_inc(&atm_vcc->stats->rx_drop);
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    } else {
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_WARN|DBG_RX, "got frame but RX closed for channel %hu", vc);
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // this is an adapter bug, only in new version of microcode
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  } else {
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev->stats.rx.error++;
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (status & CRC_ERR)
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev->stats.rx.badcrc++;
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (status & LEN_ERR)
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev->stats.rx.toolong++;
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (status & ABORT_ERR)
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev->stats.rx.aborted++;
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (status & UNUSED_ERR)
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev->stats.rx.unused++;
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  dev_kfree_skb_any (skb);
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Note on queue handling.
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Here "give" and "take" refer to queue entries and a queue (pair)
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  rather than frames to or from the host or adapter. Empty frame
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  buffers are given to the RX queue pair and returned unused or
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  containing RX frames. TX frames (well, pointers to TX fragment
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  lists) are given to the TX queue pair, completions are returned.
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** command queue **********/
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds// I really don't like this, but it's the best I can do at the moment
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds// also, the callers are responsible for byte order as the microcode
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds// sometimes does 16-bit accesses (yuk yuk yuk)
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int command_do (amb_dev * dev, command * cmd) {
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  amb_cq * cq = &dev->cq;
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  volatile amb_cq_ptrs * ptrs = &cq->ptrs;
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  command * my_slot;
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_CMD, "command_do %p", dev);
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (test_bit (dead, &dev->flags))
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return 0;
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  spin_lock (&cq->lock);
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // if not full...
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (cq->pending < cq->maximum) {
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // remember my slot for later
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    my_slot = ptrs->in;
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_CMD, "command in slot %p", my_slot);
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dump_command (cmd);
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // copy command in
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    *ptrs->in = *cmd;
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    cq->pending++;
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    ptrs->in = NEXTQ (ptrs->in, ptrs->start, ptrs->limit);
5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // mail the command
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    wr_mem (dev, offsetof(amb_mem, mb.adapter.cmd_address), virt_to_bus (ptrs->in));
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (cq->pending > cq->high)
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cq->high = cq->pending;
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    spin_unlock (&cq->lock);
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // these comments were in a while-loop before, msleep removes the loop
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // go to sleep
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // PRINTD (DBG_CMD, "wait: sleeping %lu for command", timeout);
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    msleep(cq->pending);
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // wait for my slot to be reached (all waiters are here or above, until...)
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    while (ptrs->out != my_slot) {
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_CMD, "wait: command slot (now at %p)", ptrs->out);
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      set_current_state(TASK_UNINTERRUPTIBLE);
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      schedule();
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // wait on my slot (... one gets to its slot, and... )
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    while (ptrs->out->request != cpu_to_be32 (SRB_COMPLETE)) {
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_CMD, "wait: command slot completion");
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      set_current_state(TASK_UNINTERRUPTIBLE);
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      schedule();
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_CMD, "command complete");
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // update queue (... moves the queue along to the next slot)
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    spin_lock (&cq->lock);
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    cq->pending--;
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // copy command out
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    *cmd = *ptrs->out;
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    ptrs->out = NEXTQ (ptrs->out, ptrs->start, ptrs->limit);
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    spin_unlock (&cq->lock);
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return 0;
6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  } else {
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    cq->filled++;
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    spin_unlock (&cq->lock);
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -EAGAIN;
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** TX queue pair **********/
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
627a5b2db67139e991d9e9e19260989d0e66a03a2b2Denys Vlasenkostatic int tx_give (amb_dev * dev, tx_in * tx) {
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  amb_txq * txq = &dev->txq;
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned long flags;
6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_TX, "tx_give %p", dev);
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (test_bit (dead, &dev->flags))
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return 0;
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  spin_lock_irqsave (&txq->lock, flags);
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (txq->pending < txq->maximum) {
6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_TX, "TX in slot %p", txq->in.ptr);
6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    *txq->in.ptr = *tx;
6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    txq->pending++;
6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    txq->in.ptr = NEXTQ (txq->in.ptr, txq->in.start, txq->in.limit);
6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // hand over the TX and ring the bell
6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    wr_mem (dev, offsetof(amb_mem, mb.adapter.tx_address), virt_to_bus (txq->in.ptr));
6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    wr_mem (dev, offsetof(amb_mem, doorbell), TX_FRAME);
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (txq->pending > txq->high)
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      txq->high = txq->pending;
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    spin_unlock_irqrestore (&txq->lock, flags);
6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return 0;
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  } else {
6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    txq->filled++;
6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    spin_unlock_irqrestore (&txq->lock, flags);
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -EAGAIN;
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
659a5b2db67139e991d9e9e19260989d0e66a03a2b2Denys Vlasenkostatic int tx_take (amb_dev * dev) {
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  amb_txq * txq = &dev->txq;
6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned long flags;
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_TX, "tx_take %p", dev);
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  spin_lock_irqsave (&txq->lock, flags);
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (txq->pending && txq->out.ptr->handle) {
6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // deal with TX completion
6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    tx_complete (dev, txq->out.ptr);
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // mark unused again
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    txq->out.ptr->handle = 0;
6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // remove item
6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    txq->pending--;
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    txq->out.ptr = NEXTQ (txq->out.ptr, txq->out.start, txq->out.limit);
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    spin_unlock_irqrestore (&txq->lock, flags);
6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return 0;
6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  } else {
6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    spin_unlock_irqrestore (&txq->lock, flags);
6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -1;
6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** RX queue pairs **********/
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
687a5b2db67139e991d9e9e19260989d0e66a03a2b2Denys Vlasenkostatic int rx_give (amb_dev * dev, rx_in * rx, unsigned char pool) {
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  amb_rxq * rxq = &dev->rxq[pool];
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned long flags;
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_RX, "rx_give %p[%hu]", dev, pool);
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  spin_lock_irqsave (&rxq->lock, flags);
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (rxq->pending < rxq->maximum) {
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_RX, "RX in slot %p", rxq->in.ptr);
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    *rxq->in.ptr = *rx;
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rxq->pending++;
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rxq->in.ptr = NEXTQ (rxq->in.ptr, rxq->in.start, rxq->in.limit);
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // hand over the RX buffer
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    wr_mem (dev, offsetof(amb_mem, mb.adapter.rx_address[pool]), virt_to_bus (rxq->in.ptr));
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    spin_unlock_irqrestore (&rxq->lock, flags);
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return 0;
7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  } else {
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    spin_unlock_irqrestore (&rxq->lock, flags);
7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -1;
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
712a5b2db67139e991d9e9e19260989d0e66a03a2b2Denys Vlasenkostatic int rx_take (amb_dev * dev, unsigned char pool) {
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  amb_rxq * rxq = &dev->rxq[pool];
7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned long flags;
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_RX, "rx_take %p[%hu]", dev, pool);
7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  spin_lock_irqsave (&rxq->lock, flags);
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (rxq->pending && (rxq->out.ptr->status || rxq->out.ptr->length)) {
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // deal with RX completion
7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rx_complete (dev, rxq->out.ptr);
7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // mark unused again
7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rxq->out.ptr->status = 0;
7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rxq->out.ptr->length = 0;
7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // remove item
7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rxq->pending--;
7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rxq->out.ptr = NEXTQ (rxq->out.ptr, rxq->out.start, rxq->out.limit);
7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (rxq->pending < rxq->low)
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      rxq->low = rxq->pending;
7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    spin_unlock_irqrestore (&rxq->lock, flags);
7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return 0;
7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  } else {
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (!rxq->pending && rxq->buffers_wanted)
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      rxq->emptied++;
7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    spin_unlock_irqrestore (&rxq->lock, flags);
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -1;
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** RX Pool handling **********/
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* pre: buffers_wanted = 0, post: pending = 0 */
745a5b2db67139e991d9e9e19260989d0e66a03a2b2Denys Vlasenkostatic void drain_rx_pool (amb_dev * dev, unsigned char pool) {
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  amb_rxq * rxq = &dev->rxq[pool];
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_POOL, "drain_rx_pool %p %hu", dev, pool);
7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (test_bit (dead, &dev->flags))
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return;
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  /* we are not quite like the fill pool routines as we cannot just
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds     remove one buffer, we have to remove all of them, but we might as
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds     well pretend... */
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (rxq->pending > rxq->buffers_wanted) {
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    command cmd;
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    cmd.request = cpu_to_be32 (SRB_FLUSH_BUFFER_Q);
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    cmd.args.flush.flags = cpu_to_be32 (pool << SRB_POOL_SHIFT);
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    while (command_do (dev, &cmd))
7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      schedule();
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* the pool may also be emptied via the interrupt handler */
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    while (rxq->pending > rxq->buffers_wanted)
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (rx_take (dev, pool))
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	schedule();
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void drain_rx_pools (amb_dev * dev) {
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned char pool;
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_POOL, "drain_rx_pools %p", dev);
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  for (pool = 0; pool < NUM_RX_POOLS; ++pool)
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    drain_rx_pool (dev, pool);
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
780a5b2db67139e991d9e9e19260989d0e66a03a2b2Denys Vlasenkostatic void fill_rx_pool (amb_dev * dev, unsigned char pool,
781dd0fc66fb33cd610bc1a5db8a5e232d34879b4d7Al Viro                                 gfp_t priority)
7825938a7b580bb7a738ee349048565ac76e6f3f5d7Victor Fusco{
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  rx_in rx;
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  amb_rxq * rxq;
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_POOL, "fill_rx_pool %p %hu %x", dev, pool, priority);
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (test_bit (dead, &dev->flags))
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return;
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  rxq = &dev->rxq[pool];
7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  while (rxq->pending < rxq->maximum && rxq->pending < rxq->buffers_wanted) {
7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    struct sk_buff * skb = alloc_skb (rxq->buffer_size, priority);
7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (!skb) {
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_SKB|DBG_POOL, "failed to allocate skb for RX pool %hu", pool);
7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      return;
7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (check_area (skb->data, skb->truesize)) {
8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev_kfree_skb_any (skb);
8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      return;
8021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // cast needed as there is no %? for pointer differences
8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_SKB, "allocated skb at %p, head %p, area %li",
8054305b541357ddbd205aa145dc378926b7cb12283Arnaldo Carvalho de Melo	    skb, skb->head, (long) (skb_end_pointer(skb) - skb->head));
8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rx.handle = virt_to_bus (skb);
8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rx.host_address = cpu_to_be32 (virt_to_bus (skb->data));
8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (rx_give (dev, &rx, pool))
8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev_kfree_skb_any (skb);
8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
816135d23d66c53ade614c288d422f4c4b3205eb201Wang Shaoyan// top up all RX pools
8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void fill_rx_pools (amb_dev * dev) {
8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned char pool;
8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_POOL, "fill_rx_pools %p", dev);
8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  for (pool = 0; pool < NUM_RX_POOLS; ++pool)
8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    fill_rx_pool (dev, pool, GFP_ATOMIC);
8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** enable host interrupts **********/
8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
830a5b2db67139e991d9e9e19260989d0e66a03a2b2Denys Vlasenkostatic void interrupts_on (amb_dev * dev) {
8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_plain (dev, offsetof(amb_mem, interrupt_control),
8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    rd_plain (dev, offsetof(amb_mem, interrupt_control))
8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    | AMB_INTERRUPT_BITS);
8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** disable host interrupts **********/
8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
838a5b2db67139e991d9e9e19260989d0e66a03a2b2Denys Vlasenkostatic void interrupts_off (amb_dev * dev) {
8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_plain (dev, offsetof(amb_mem, interrupt_control),
8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    rd_plain (dev, offsetof(amb_mem, interrupt_control))
8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    &~ AMB_INTERRUPT_BITS);
8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** interrupt handling **********/
8451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8467d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic irqreturn_t interrupt_handler(int irq, void *dev_id) {
847c7bec5aba52392aa8d675b8722735caf4a8b7265Jeff Garzik  amb_dev * dev = dev_id;
8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_IRQ|DBG_FLOW, "interrupt_handler: %p", dev_id);
8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  {
8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    u32 interrupt = rd_plain (dev, offsetof(amb_mem, interrupt));
8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // for us or someone else sharing the same interrupt
8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (!interrupt) {
8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_IRQ, "irq not for me: %d", irq);
8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      return IRQ_NONE;
8581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
8591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // definitely for us
8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_IRQ, "FYI: interrupt was %08x", interrupt);
8621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    wr_plain (dev, offsetof(amb_mem, interrupt), -1);
8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
8641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  {
8661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    unsigned int irq_work = 0;
8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    unsigned char pool;
8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    for (pool = 0; pool < NUM_RX_POOLS; ++pool)
8691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      while (!rx_take (dev, pool))
8701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	++irq_work;
8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    while (!tx_take (dev))
8721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      ++irq_work;
8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (irq_work) {
8751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      fill_rx_pools (dev);
8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_IRQ, "work done: %u", irq_work);
8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    } else {
8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_IRQ|DBG_WARN, "no work done");
8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
8811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
8821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_IRQ|DBG_FLOW, "interrupt_handler done: %p", dev_id);
8841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return IRQ_HANDLED;
8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** make rate (not quite as much fun as Horizon) **********/
8881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8893a4e5e2033eb7558ebe307e46f5fb6e63b92cab8Jeff Garzikstatic int make_rate (unsigned int rate, rounding r,
8903a4e5e2033eb7558ebe307e46f5fb6e63b92cab8Jeff Garzik		      u16 * bits, unsigned int * actual) {
8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned char exp = -1; // hush gcc
8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned int man = -1;  // hush gcc
8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_QOS, "make_rate %u", rate);
8951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // rates in cells per second, ITU format (nasty 16-bit floating-point)
8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // given 5-bit e and 9-bit m:
8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // rate = EITHER (1+m/2^9)*2^e    OR 0
8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // bits = EITHER 1<<14 | e<<9 | m OR 0
9001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // (bit 15 is "reserved", bit 14 "non-zero")
9011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // smallest rate is 0 (special representation)
9021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // largest rate is (1+511/512)*2^31 = 4290772992 (< 2^32-1)
9031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // smallest non-zero rate is (1+0/512)*2^0 = 1 (> 0)
9041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // simple algorithm:
9051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // find position of top bit, this gives e
9061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // remove top bit and shift (rounding if feeling clever) by 9-e
9071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // ucode bug: please don't set bit 14! so 0 rate not representable
9091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (rate > 0xffc00000U) {
9111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // larger than largest representable rate
9121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (r == round_up) {
9141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -EINVAL;
9151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    } else {
9161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      exp = 31;
9171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      man = 511;
9181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
9191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  } else if (rate) {
9211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // representable rate
9221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    exp = 31;
9241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    man = rate;
9251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // invariant: rate = man*2^(exp-31)
9271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    while (!(man & (1<<31))) {
9281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      exp = exp - 1;
9291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      man = man<<1;
9301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
9311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // man has top bit set
9331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // rate = (2^31+(man-2^31))*2^(exp-31)
9341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // rate = (1+(man-2^31)/2^31)*2^exp
9351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    man = man<<1;
9361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    man &= 0xffffffffU; // a nop on 32-bit systems
9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // rate = (1+man/2^32)*2^exp
9381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // exp is in the range 0 to 31, man is in the range 0 to 2^32-1
9401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // time to lose significance... we want m in the range 0 to 2^9-1
9411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // rounding presents a minor problem... we first decide which way
9421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // we are rounding (based on given rounding direction and possibly
9431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // the bits of the mantissa that are to be discarded).
9441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    switch (r) {
9461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      case round_down: {
9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// just truncate
9481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	man = man>>(32-9);
9491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
9501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
9511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      case round_up: {
9521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// check all bits that we are discarding
9535f3f24faf322e54752d1df00cdb768a013de5d1fAlexey Dobriyan	if (man & (~0U>>9)) {
9541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  man = (man>>(32-9)) + 1;
9551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  if (man == (1<<9)) {
9561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    // no need to check for round up outside of range
9571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    man = 0;
9581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    exp += 1;
9591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  }
9601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
9611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  man = (man>>(32-9));
9621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
9641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
9651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      case round_nearest: {
9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// check msb that we are discarding
9671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (man & (1<<(32-9-1))) {
9681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  man = (man>>(32-9)) + 1;
9691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  if (man == (1<<9)) {
9701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    // no need to check for round up outside of range
9711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    man = 0;
9721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    exp += 1;
9731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  }
9741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
9751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  man = (man>>(32-9));
9761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
9781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
9791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
9801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  } else {
9821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // zero rate - not representable
9831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (r == round_down) {
9851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      return -EINVAL;
9861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    } else {
9871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      exp = 0;
9881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      man = 0;
9891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
9901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
9921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_QOS, "rate: man=%u, exp=%hu", man, exp);
9941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (bits)
9961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    *bits = /* (1<<14) | */ (exp<<9) | man;
9971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (actual)
9991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    *actual = (exp >= 9)
10001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      ? (1 << exp) + (man << (exp-9))
10011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      : (1 << exp) + ((man + (1<<(9-exp-1))) >> (9-exp));
10021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return 0;
10041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
10051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** Linux ATM Operations **********/
10071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds// some are not yet implemented while others do not make sense for
10091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds// this device
10101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** Open a VC **********/
10121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int amb_open (struct atm_vcc * atm_vcc)
10141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
10151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  int error;
10161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  struct atm_qos * qos;
10181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  struct atm_trafprm * txtp;
10191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  struct atm_trafprm * rxtp;
1020b1734d2388cc45ecdec58615e35955d0d402f938Jeff Garzik  u16 tx_rate_bits = -1; // hush gcc
10211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u16 tx_vc_bits = -1; // hush gcc
10221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u16 tx_frame_bits = -1; // hush gcc
10231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  amb_dev * dev = AMB_DEV(atm_vcc->dev);
10251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  amb_vcc * vcc;
10261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned char pool = -1; // hush gcc
10271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  short vpi = atm_vcc->vpi;
10281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  int vci = atm_vcc->vci;
10291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_VCC, "amb_open %x %x", vpi, vci);
10311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef ATM_VPI_UNSPEC
10331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // UNSPEC is deprecated, remove this code eventually
10341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC) {
10351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTK (KERN_WARNING, "rejecting open with unspecified VPI/VCI (deprecated)");
10361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -EINVAL;
10371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
10381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
10391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (!(0 <= vpi && vpi < (1<<NUM_VPI_BITS) &&
10411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	0 <= vci && vci < (1<<NUM_VCI_BITS))) {
10421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_WARN|DBG_VCC, "VPI/VCI out of range: %hd/%d", vpi, vci);
10431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -EINVAL;
10441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
10451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  qos = &atm_vcc->qos;
10471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (qos->aal != ATM_AAL5) {
10491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_QOS, "AAL not supported");
10501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -EINVAL;
10511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
10521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // traffic parameters
10541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_QOS, "TX:");
10561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  txtp = &qos->txtp;
10571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (txtp->traffic_class != ATM_NONE) {
10581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    switch (txtp->traffic_class) {
10591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      case ATM_UBR: {
10601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// we take "the PCR" as a rate-cap
10611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int pcr = atm_pcr_goal (txtp);
10621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!pcr) {
10631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  // no rate cap
10641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  tx_rate_bits = 0;
10651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  tx_vc_bits = TX_UBR;
10661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  tx_frame_bits = TX_FRAME_NOTCAP;
10671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
10681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  rounding r;
10691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  if (pcr < 0) {
10701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    r = round_down;
10711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    pcr = -pcr;
10721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  } else {
10731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    r = round_up;
10741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  }
10751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  error = make_rate (pcr, r, &tx_rate_bits, NULL);
1076b1734d2388cc45ecdec58615e35955d0d402f938Jeff Garzik	  if (error)
1077b1734d2388cc45ecdec58615e35955d0d402f938Jeff Garzik	    return error;
10781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  tx_vc_bits = TX_UBR_CAPPED;
10791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  tx_frame_bits = TX_FRAME_CAPPED;
10801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
10821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
10831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
10841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      case ATM_ABR: {
10851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pcr = atm_pcr_goal (txtp);
10861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_QOS, "pcr goal = %d", pcr);
10871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
10881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
10891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
10901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      default: {
10911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// PRINTD (DBG_QOS, "request for non-UBR/ABR denied");
10921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_QOS, "request for non-UBR denied");
10931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -EINVAL;
10941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
10951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
10961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_QOS, "tx_rate_bits=%hx, tx_vc_bits=%hx",
10971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    tx_rate_bits, tx_vc_bits);
10981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
10991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_QOS, "RX:");
11011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  rxtp = &qos->rxtp;
11021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (rxtp->traffic_class == ATM_NONE) {
11031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // do nothing
11041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  } else {
11051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // choose an RX pool (arranged in increasing size)
11061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    for (pool = 0; pool < NUM_RX_POOLS; ++pool)
11071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if ((unsigned int) rxtp->max_sdu <= dev->rxq[pool].buffer_size) {
11081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_VCC|DBG_QOS|DBG_POOL, "chose pool %hu (max_sdu %u <= %u)",
11091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pool, rxtp->max_sdu, dev->rxq[pool].buffer_size);
11101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
11111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
11121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (pool == NUM_RX_POOLS) {
11131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_WARN|DBG_VCC|DBG_QOS|DBG_POOL,
11141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	      "no pool suitable for VC (RX max_sdu %d is too large)",
11151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	      rxtp->max_sdu);
11161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      return -EINVAL;
11171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
11181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    switch (rxtp->traffic_class) {
11201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      case ATM_UBR: {
11211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
11221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
11231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
11241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      case ATM_ABR: {
11251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pcr = atm_pcr_goal (rxtp);
11261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_QOS, "pcr goal = %d", pcr);
11271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
11281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
11291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
11301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      default: {
11311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// PRINTD (DBG_QOS, "request for non-UBR/ABR denied");
11321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_QOS, "request for non-UBR denied");
11331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -EINVAL;
11341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
11351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
11361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
11371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // get space for our vcc stuff
11391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  vcc = kmalloc (sizeof(amb_vcc), GFP_KERNEL);
11401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (!vcc) {
11411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTK (KERN_ERR, "out of memory!");
11421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -ENOMEM;
11431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
11441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  atm_vcc->dev_data = (void *) vcc;
11451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // no failures beyond this point
11471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // we are not really "immediately before allocating the connection
11491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // identifier in hardware", but it will just have to do!
11501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  set_bit(ATM_VF_ADDR,&atm_vcc->flags);
11511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (txtp->traffic_class != ATM_NONE) {
11531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    command cmd;
11541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    vcc->tx_frame_bits = tx_frame_bits;
11561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1157eff0dee54674a449e7f160aad9f3e0d38e6983ebDaniel Walker    mutex_lock(&dev->vcc_sf);
11581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (dev->rxer[vci]) {
11591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // RXer on the channel already, just modify rate...
11601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.request = cpu_to_be32 (SRB_MODIFY_VC_RATE);
11611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.args.modify_rate.vc = cpu_to_be32 (vci);  // vpi 0
11621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.args.modify_rate.rate = cpu_to_be32 (tx_rate_bits << SRB_RATE_SHIFT);
11631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      while (command_do (dev, &cmd))
11641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	schedule();
11651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // ... and TX flags, preserving the RX pool
11661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.request = cpu_to_be32 (SRB_MODIFY_VC_FLAGS);
11671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.args.modify_flags.vc = cpu_to_be32 (vci);  // vpi 0
11681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.args.modify_flags.flags = cpu_to_be32
11691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	( (AMB_VCC(dev->rxer[vci])->rx_info.pool << SRB_POOL_SHIFT)
11701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  | (tx_vc_bits << SRB_FLAGS_SHIFT) );
11711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      while (command_do (dev, &cmd))
11721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	schedule();
11731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    } else {
11741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // no RXer on the channel, just open (with pool zero)
11751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.request = cpu_to_be32 (SRB_OPEN_VC);
11761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.args.open.vc = cpu_to_be32 (vci);  // vpi 0
11771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.args.open.flags = cpu_to_be32 (tx_vc_bits << SRB_FLAGS_SHIFT);
11781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.args.open.rate = cpu_to_be32 (tx_rate_bits << SRB_RATE_SHIFT);
11791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      while (command_do (dev, &cmd))
11801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	schedule();
11811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
11821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev->txer[vci].tx_present = 1;
1183eff0dee54674a449e7f160aad9f3e0d38e6983ebDaniel Walker    mutex_unlock(&dev->vcc_sf);
11841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
11851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (rxtp->traffic_class != ATM_NONE) {
11871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    command cmd;
11881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    vcc->rx_info.pool = pool;
11901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1191eff0dee54674a449e7f160aad9f3e0d38e6983ebDaniel Walker    mutex_lock(&dev->vcc_sf);
11921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* grow RX buffer pool */
11931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (!dev->rxq[pool].buffers_wanted)
11941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev->rxq[pool].buffers_wanted = rx_lats;
11951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev->rxq[pool].buffers_wanted += 1;
11961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    fill_rx_pool (dev, pool, GFP_KERNEL);
11971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (dev->txer[vci].tx_present) {
11991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // TXer on the channel already
12001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // switch (from pool zero) to this pool, preserving the TX bits
12011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.request = cpu_to_be32 (SRB_MODIFY_VC_FLAGS);
12021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.args.modify_flags.vc = cpu_to_be32 (vci);  // vpi 0
12031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.args.modify_flags.flags = cpu_to_be32
12041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	( (pool << SRB_POOL_SHIFT)
12051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  | (dev->txer[vci].tx_vc_bits << SRB_FLAGS_SHIFT) );
12061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    } else {
12071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // no TXer on the channel, open the VC (with no rate info)
12081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.request = cpu_to_be32 (SRB_OPEN_VC);
12091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.args.open.vc = cpu_to_be32 (vci);  // vpi 0
12101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.args.open.flags = cpu_to_be32 (pool << SRB_POOL_SHIFT);
12111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.args.open.rate = cpu_to_be32 (0);
12121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
12131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    while (command_do (dev, &cmd))
12141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      schedule();
12151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // this link allows RX frames through
12161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev->rxer[vci] = atm_vcc;
1217eff0dee54674a449e7f160aad9f3e0d38e6983ebDaniel Walker    mutex_unlock(&dev->vcc_sf);
12181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
12191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // indicate readiness
12211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  set_bit(ATM_VF_READY,&atm_vcc->flags);
12221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return 0;
12241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
12251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** Close a VC **********/
12271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void amb_close (struct atm_vcc * atm_vcc) {
12291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  amb_dev * dev = AMB_DEV (atm_vcc->dev);
12301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  amb_vcc * vcc = AMB_VCC (atm_vcc);
12311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u16 vci = atm_vcc->vci;
12321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_VCC|DBG_FLOW, "amb_close");
12341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // indicate unreadiness
12361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  clear_bit(ATM_VF_READY,&atm_vcc->flags);
12371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // disable TXing
12391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (atm_vcc->qos.txtp.traffic_class != ATM_NONE) {
12401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    command cmd;
12411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1242eff0dee54674a449e7f160aad9f3e0d38e6983ebDaniel Walker    mutex_lock(&dev->vcc_sf);
12431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (dev->rxer[vci]) {
12441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // RXer still on the channel, just modify rate... XXX not really needed
12451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.request = cpu_to_be32 (SRB_MODIFY_VC_RATE);
12461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.args.modify_rate.vc = cpu_to_be32 (vci);  // vpi 0
12471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.args.modify_rate.rate = cpu_to_be32 (0);
12481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // ... and clear TX rate flags (XXX to stop RM cell output?), preserving RX pool
12491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    } else {
12501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // no RXer on the channel, close channel
12511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.request = cpu_to_be32 (SRB_CLOSE_VC);
12521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.args.close.vc = cpu_to_be32 (vci); // vpi 0
12531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
12541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev->txer[vci].tx_present = 0;
12551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    while (command_do (dev, &cmd))
12561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      schedule();
1257eff0dee54674a449e7f160aad9f3e0d38e6983ebDaniel Walker    mutex_unlock(&dev->vcc_sf);
12581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
12591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // disable RXing
12611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (atm_vcc->qos.rxtp.traffic_class != ATM_NONE) {
12621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    command cmd;
12631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // this is (the?) one reason why we need the amb_vcc struct
12651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    unsigned char pool = vcc->rx_info.pool;
12661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1267eff0dee54674a449e7f160aad9f3e0d38e6983ebDaniel Walker    mutex_lock(&dev->vcc_sf);
12681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (dev->txer[vci].tx_present) {
12691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // TXer still on the channel, just go to pool zero XXX not really needed
12701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.request = cpu_to_be32 (SRB_MODIFY_VC_FLAGS);
12711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.args.modify_flags.vc = cpu_to_be32 (vci);  // vpi 0
12721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.args.modify_flags.flags = cpu_to_be32
12731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	(dev->txer[vci].tx_vc_bits << SRB_FLAGS_SHIFT);
12741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    } else {
12751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // no TXer on the channel, close the VC
12761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.request = cpu_to_be32 (SRB_CLOSE_VC);
12771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      cmd.args.close.vc = cpu_to_be32 (vci); // vpi 0
12781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
12791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // forget the rxer - no more skbs will be pushed
12801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (atm_vcc != dev->rxer[vci])
12811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTK (KERN_ERR, "%s vcc=%p rxer[vci]=%p",
12821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	      "arghhh! we're going to die!",
12831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	      vcc, dev->rxer[vci]);
12841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev->rxer[vci] = NULL;
12851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    while (command_do (dev, &cmd))
12861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      schedule();
12871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    /* shrink RX buffer pool */
12891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev->rxq[pool].buffers_wanted -= 1;
12901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (dev->rxq[pool].buffers_wanted == rx_lats) {
12911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev->rxq[pool].buffers_wanted = 0;
12921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      drain_rx_pool (dev, pool);
12931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
1294eff0dee54674a449e7f160aad9f3e0d38e6983ebDaniel Walker    mutex_unlock(&dev->vcc_sf);
12951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
12961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // free our structure
12981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  kfree (vcc);
12991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // say the VPI/VCI is free again
13011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  clear_bit(ATM_VF_ADDR,&atm_vcc->flags);
13021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
13041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
13051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** Send **********/
13071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int amb_send (struct atm_vcc * atm_vcc, struct sk_buff * skb) {
13091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  amb_dev * dev = AMB_DEV(atm_vcc->dev);
13101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  amb_vcc * vcc = AMB_VCC(atm_vcc);
13111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u16 vc = atm_vcc->vci;
13121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned int tx_len = skb->len;
13131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned char * tx_data = skb->data;
13141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  tx_simple * tx_descr;
13151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  tx_in tx;
13161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (test_bit (dead, &dev->flags))
13181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -EIO;
13191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_TX, "amb_send vc %x data %p len %u",
13211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  vc, tx_data, tx_len);
13221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  dump_skb (">>>", vc, skb);
13241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (!dev->txer[vc].tx_present) {
13261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTK (KERN_ERR, "attempt to send on RX-only VC %x", vc);
13271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -EBADFD;
13281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
13291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // this is a driver private field so we have to set it ourselves,
13311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // despite the fact that we are _required_ to use it to check for a
13321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // pop function
13331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ATM_SKB(skb)->vcc = atm_vcc;
13341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (skb->len > (size_t) atm_vcc->qos.txtp.max_sdu) {
13361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTK (KERN_ERR, "sk_buff length greater than agreed max_sdu, dropping...");
13371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -EIO;
13381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
13391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (check_area (skb->data, skb->len)) {
13411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    atomic_inc(&atm_vcc->stats->tx_err);
13421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -ENOMEM; // ?
13431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
13441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // allocate memory for fragments
13461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  tx_descr = kmalloc (sizeof(tx_simple), GFP_KERNEL);
13471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (!tx_descr) {
13481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTK (KERN_ERR, "could not allocate TX descriptor");
13491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -ENOMEM;
13501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
13511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (check_area (tx_descr, sizeof(tx_simple))) {
13521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    kfree (tx_descr);
13531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -ENOMEM;
13541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
13551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_TX, "fragment list allocated at %p", tx_descr);
13561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  tx_descr->skb = skb;
13581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  tx_descr->tx_frag.bytes = cpu_to_be32 (tx_len);
13601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  tx_descr->tx_frag.address = cpu_to_be32 (virt_to_bus (tx_data));
13611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  tx_descr->tx_frag_end.handle = virt_to_bus (tx_descr);
13631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  tx_descr->tx_frag_end.vc = 0;
13641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  tx_descr->tx_frag_end.next_descriptor_length = 0;
13651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  tx_descr->tx_frag_end.next_descriptor = 0;
13661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef AMB_NEW_MICROCODE
13671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  tx_descr->tx_frag_end.cpcs_uu = 0;
13681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  tx_descr->tx_frag_end.cpi = 0;
13691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  tx_descr->tx_frag_end.pad = 0;
13701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
13711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  tx.vc = cpu_to_be16 (vcc->tx_frame_bits | vc);
13731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  tx.tx_descr_length = cpu_to_be16 (sizeof(tx_frag)+sizeof(tx_frag_end));
13741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  tx.tx_descr_addr = cpu_to_be32 (virt_to_bus (&tx_descr->tx_frag));
13751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  while (tx_give (dev, &tx))
13771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    schedule();
13781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return 0;
13791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
13801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** Change QoS on a VC **********/
13821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds// int amb_change_qos (struct atm_vcc * atm_vcc, struct atm_qos * qos, int flags);
13841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** Free RX Socket Buffer **********/
13861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
13881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void amb_free_rx_skb (struct atm_vcc * atm_vcc, struct sk_buff * skb) {
13891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  amb_dev * dev = AMB_DEV (atm_vcc->dev);
13901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  amb_vcc * vcc = AMB_VCC (atm_vcc);
13911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned char pool = vcc->rx_info.pool;
13921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  rx_in rx;
13931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // This may be unsafe for various reasons that I cannot really guess
13951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // at. However, I note that the ATM layer calls kfree_skb rather
13961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // than dev_kfree_skb at this point so we are least covered as far
13971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // as buffer locking goes. There may be bugs if pcap clones RX skbs.
13981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_SKB, "amb_rx_free skb %p (atm_vcc %p, vcc %p)",
14001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  skb, atm_vcc, vcc);
14011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  rx.handle = virt_to_bus (skb);
14031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  rx.host_address = cpu_to_be32 (virt_to_bus (skb->data));
14041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  skb->data = skb->head;
14061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  skb->tail = skb->head;
14071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  skb->len = 0;
14081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (!rx_give (dev, &rx, pool)) {
14101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // success
14111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_SKB|DBG_POOL, "recycled skb for pool %hu", pool);
14121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return;
14131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
14141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // just do what the ATM layer would have done
14161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  dev_kfree_skb_any (skb);
14171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
14191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
14201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
14211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** Proc File Output **********/
14231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int amb_proc_read (struct atm_dev * atm_dev, loff_t * pos, char * page) {
14251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  amb_dev * dev = AMB_DEV (atm_dev);
14261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  int left = *pos;
14271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned char pool;
14281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW, "amb_proc_read");
14301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  /* more diagnostics here? */
14321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (!left--) {
14341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    amb_stats * s = &dev->stats;
14351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return sprintf (page,
14361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    "frames: TX OK %lu, RX OK %lu, RX bad %lu "
14371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    "(CRC %lu, long %lu, aborted %lu, unused %lu).\n",
14381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    s->tx_ok, s->rx.ok, s->rx.error,
14391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    s->rx.badcrc, s->rx.toolong,
14401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    s->rx.aborted, s->rx.unused);
14411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
14421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (!left--) {
14441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    amb_cq * c = &dev->cq;
14451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return sprintf (page, "cmd queue [cur/hi/max]: %u/%u/%u. ",
14461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    c->pending, c->high, c->maximum);
14471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
14481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (!left--) {
14501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    amb_txq * t = &dev->txq;
14511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return sprintf (page, "TX queue [cur/max high full]: %u/%u %u %u.\n",
14521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    t->pending, t->maximum, t->high, t->filled);
14531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
14541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (!left--) {
14561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    unsigned int count = sprintf (page, "RX queues [cur/max/req low empty]:");
14571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    for (pool = 0; pool < NUM_RX_POOLS; ++pool) {
14581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      amb_rxq * r = &dev->rxq[pool];
14591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      count += sprintf (page+count, " %u/%u/%u %u %u",
14601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			r->pending, r->maximum, r->buffers_wanted, r->low, r->emptied);
14611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
14621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    count += sprintf (page+count, ".\n");
14631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return count;
14641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
14651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (!left--) {
14671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    unsigned int count = sprintf (page, "RX buffer sizes:");
14681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    for (pool = 0; pool < NUM_RX_POOLS; ++pool) {
14691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      amb_rxq * r = &dev->rxq[pool];
14701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      count += sprintf (page+count, " %u", r->buffer_size);
14711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
14721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    count += sprintf (page+count, ".\n");
14731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return count;
14741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
14751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
14771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (!left--) {
14781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // suni block etc?
14791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
14801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
14811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return 0;
14831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
14841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** Operation Structure **********/
14861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const struct atmdev_ops amb_ops = {
14881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  .open         = amb_open,
14891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  .close	= amb_close,
14901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  .send         = amb_send,
14911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  .proc_read	= amb_proc_read,
14921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  .owner	= THIS_MODULE,
14931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
14941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** housekeeping **********/
14961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void do_housekeeping (unsigned long arg) {
14971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  amb_dev * dev = (amb_dev *) arg;
14981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // could collect device-specific (not driver/atm-linux) stats here
15001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // last resort refill once every ten seconds
15021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  fill_rx_pools (dev);
15031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  mod_timer(&dev->housekeeping, jiffies + 10*HZ);
15041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
15061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
15071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** creation of communication queues **********/
15091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __devinit create_queues (amb_dev * dev, unsigned int cmds,
15111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 unsigned int txs, unsigned int * rxs,
15121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 unsigned int * rx_buffer_sizes) {
15131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned char pool;
15141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  size_t total = 0;
15151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  void * memory;
15161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  void * limit;
15171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW, "create_queues %p", dev);
15191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  total += cmds * sizeof(command);
15211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  total += txs * (sizeof(tx_in) + sizeof(tx_out));
15231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  for (pool = 0; pool < NUM_RX_POOLS; ++pool)
15251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    total += rxs[pool] * (sizeof(rx_in) + sizeof(rx_out));
15261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  memory = kmalloc (total, GFP_KERNEL);
15281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (!memory) {
15291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTK (KERN_ERR, "could not allocate queues");
15301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -ENOMEM;
15311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
15321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (check_area (memory, total)) {
15331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTK (KERN_ERR, "queues allocated in nasty area");
15341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    kfree (memory);
15351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -ENOMEM;
15361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
15371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  limit = memory + total;
15391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_INIT, "queues from %p to %p", memory, limit);
15401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_CMD, "command queue at %p", memory);
15421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  {
15441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    command * cmd = memory;
15451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    amb_cq * cq = &dev->cq;
15461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    cq->pending = 0;
15481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    cq->high = 0;
15491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    cq->maximum = cmds - 1;
15501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    cq->ptrs.start = cmd;
15521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    cq->ptrs.in = cmd;
15531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    cq->ptrs.out = cmd;
15541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    cq->ptrs.limit = cmd + cmds;
15551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    memory = cq->ptrs.limit;
15571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
15581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_TX, "TX queue pair at %p", memory);
15601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  {
15621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    tx_in * in = memory;
15631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    tx_out * out;
15641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    amb_txq * txq = &dev->txq;
15651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    txq->pending = 0;
15671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    txq->high = 0;
15681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    txq->filled = 0;
15691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    txq->maximum = txs - 1;
15701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    txq->in.start = in;
15721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    txq->in.ptr = in;
15731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    txq->in.limit = in + txs;
15741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    memory = txq->in.limit;
15761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    out = memory;
15771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    txq->out.start = out;
15791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    txq->out.ptr = out;
15801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    txq->out.limit = out + txs;
15811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    memory = txq->out.limit;
15831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
15841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_RX, "RX queue pairs at %p", memory);
15861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  for (pool = 0; pool < NUM_RX_POOLS; ++pool) {
15881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rx_in * in = memory;
15891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rx_out * out;
15901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    amb_rxq * rxq = &dev->rxq[pool];
15911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rxq->buffer_size = rx_buffer_sizes[pool];
15931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rxq->buffers_wanted = 0;
15941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rxq->pending = 0;
15961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rxq->low = rxs[pool] - 1;
15971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rxq->emptied = 0;
15981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rxq->maximum = rxs[pool] - 1;
15991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rxq->in.start = in;
16011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rxq->in.ptr = in;
16021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rxq->in.limit = in + rxs[pool];
16031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    memory = rxq->in.limit;
16051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    out = memory;
16061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rxq->out.start = out;
16081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rxq->out.ptr = out;
16091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rxq->out.limit = out + rxs[pool];
16101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    memory = rxq->out.limit;
16121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
16131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (memory == limit) {
16151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return 0;
16161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  } else {
16171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTK (KERN_ERR, "bad queue alloc %p != %p (tell maintainer)", memory, limit);
16181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    kfree (limit - total);
16191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -ENOMEM;
16201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
16211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
16231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** destruction of communication queues **********/
16251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void destroy_queues (amb_dev * dev) {
16271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // all queues assumed empty
16281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  void * memory = dev->cq.ptrs.start;
16291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // includes txq.in, txq.out, rxq[].in and rxq[].out
16301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW, "destroy_queues %p", dev);
16321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_INIT, "freeing queues at %p", memory);
16341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  kfree (memory);
16351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
16371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
16381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** basic loader commands and error handling **********/
16401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds// centisecond timeouts - guessing away here
16411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int command_timeouts [] = {
16421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	[host_memory_test]     = 15,
16431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	[read_adapter_memory]  = 2,
16441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	[write_adapter_memory] = 2,
16451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	[adapter_start]        = 50,
16461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	[get_version_number]   = 10,
16471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	[interrupt_host]       = 1,
16481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	[flash_erase_sector]   = 1,
16491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	[adap_download_block]  = 1,
16501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	[adap_erase_flash]     = 1,
16511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	[adap_run_in_iram]     = 1,
16521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	[adap_end_download]    = 1
16531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
16541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int command_successes [] = {
16571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	[host_memory_test]     = COMMAND_PASSED_TEST,
16581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	[read_adapter_memory]  = COMMAND_READ_DATA_OK,
16591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	[write_adapter_memory] = COMMAND_WRITE_DATA_OK,
16601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	[adapter_start]        = COMMAND_COMPLETE,
16611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	[get_version_number]   = COMMAND_COMPLETE,
16621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	[interrupt_host]       = COMMAND_COMPLETE,
16631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	[flash_erase_sector]   = COMMAND_COMPLETE,
16641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	[adap_download_block]  = COMMAND_COMPLETE,
16651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	[adap_erase_flash]     = COMMAND_COMPLETE,
16661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	[adap_run_in_iram]     = COMMAND_COMPLETE,
16671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	[adap_end_download]    = COMMAND_COMPLETE
16681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
16691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic  int decode_loader_result (loader_command cmd, u32 result)
16711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
16721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int res;
16731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	const char *msg;
16741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (result == command_successes[cmd])
16761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
16771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (result) {
16791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case BAD_COMMAND:
16801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			res = -EINVAL;
16811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			msg = "bad command";
16821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
16831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case COMMAND_IN_PROGRESS:
16841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			res = -ETIMEDOUT;
16851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			msg = "command in progress";
16861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
16871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case COMMAND_PASSED_TEST:
16881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			res = 0;
16891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			msg = "command passed test";
16901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
16911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case COMMAND_FAILED_TEST:
16921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			res = -EIO;
16931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			msg = "command failed test";
16941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
16951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case COMMAND_READ_DATA_OK:
16961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			res = 0;
16971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			msg = "command read data ok";
16981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
16991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case COMMAND_READ_BAD_ADDRESS:
17001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			res = -EINVAL;
17011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			msg = "command read bad address";
17021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
17031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case COMMAND_WRITE_DATA_OK:
17041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			res = 0;
17051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			msg = "command write data ok";
17061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
17071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case COMMAND_WRITE_BAD_ADDRESS:
17081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			res = -EINVAL;
17091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			msg = "command write bad address";
17101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
17111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case COMMAND_WRITE_FLASH_FAILURE:
17121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			res = -EIO;
17131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			msg = "command write flash failure";
17141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
17151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case COMMAND_COMPLETE:
17161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			res = 0;
17171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			msg = "command complete";
17181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
17191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case COMMAND_FLASH_ERASE_FAILURE:
17201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			res = -EIO;
17211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			msg = "command flash erase failure";
17221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
17231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case COMMAND_WRITE_BAD_DATA:
17241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			res = -EINVAL;
17251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			msg = "command write bad data";
17261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
17271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		default:
17281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			res = -EINVAL;
17291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			msg = "unknown error";
17301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			PRINTD (DBG_LOAD|DBG_ERR,
17311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				"decode_loader_result got %d=%x !",
17321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				result, result);
17331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
17341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
17351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTK (KERN_ERR, "%s", msg);
17371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return res;
17381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
17391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __devinit do_loader_command (volatile loader_block * lb,
17411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				     const amb_dev * dev, loader_command cmd) {
17421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned long timeout;
17441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_LOAD, "do_loader_command");
17461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  /* do a command
17481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds     Set the return value to zero, set the command type and set the
17501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds     valid entry to the right magic value. The payload is already
17511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds     correctly byte-ordered so we leave it alone. Hit the doorbell
17521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds     with the bus address of this structure.
17531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  */
17551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  lb->result = 0;
17571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  lb->command = cpu_to_be32 (cmd);
17581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  lb->valid = cpu_to_be32 (DMA_VALID);
17591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // dump_registers (dev);
17601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // dump_loader_block (lb);
17611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_mem (dev, offsetof(amb_mem, doorbell), virt_to_bus (lb) & ~onegigmask);
17621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  timeout = command_timeouts[cmd] * 10;
17641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  while (!lb->result || lb->result == cpu_to_be32 (COMMAND_IN_PROGRESS))
17661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (timeout) {
17671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      timeout = msleep_interruptible(timeout);
17681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    } else {
17691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_LOAD|DBG_ERR, "command %d timed out", cmd);
17701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dump_registers (dev);
17711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dump_loader_block (lb);
17721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      return -ETIMEDOUT;
17731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
17741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (cmd == adapter_start) {
17761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // wait for start command to acknowledge...
17771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    timeout = 100;
17781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    while (rd_plain (dev, offsetof(amb_mem, doorbell)))
17791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (timeout) {
17801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	timeout = msleep_interruptible(timeout);
17811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      } else {
17821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_LOAD|DBG_ERR, "start command did not clear doorbell, res=%08x",
17831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		be32_to_cpu (lb->result));
17841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dump_registers (dev);
17851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ETIMEDOUT;
17861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
17871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return 0;
17881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  } else {
17891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return decode_loader_result (cmd, be32_to_cpu (lb->result));
17901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
17911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
17931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* loader: determine loader version */
17951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __devinit get_loader_version (loader_block * lb,
17971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				      const amb_dev * dev, u32 * version) {
17981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  int res;
17991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_LOAD, "get_loader_version");
18011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  res = do_loader_command (lb, dev, get_version_number);
18031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (res)
18041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return res;
18051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (version)
18061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    *version = be32_to_cpu (lb->payload.version);
18071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return 0;
18081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
18091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* loader: write memory data blocks */
18111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
181227d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhousestatic int __devinit loader_write (loader_block* lb,
181327d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse				   const amb_dev *dev,
181427d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse				   const struct ihex_binrec *rec) {
18151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  transfer_block * tb = &lb->payload.transfer;
18161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_LOAD, "loader_write");
181827d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse
181927d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse  tb->address = rec->addr;
182027d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse  tb->count = cpu_to_be32(be16_to_cpu(rec->len) / 4);
182127d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse  memcpy(tb->data, rec->data, be16_to_cpu(rec->len));
18221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return do_loader_command (lb, dev, write_adapter_memory);
18231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
18241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* loader: verify memory data blocks */
18261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __devinit loader_verify (loader_block * lb,
182827d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse				    const amb_dev *dev,
182927d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse				    const struct ihex_binrec *rec) {
18301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  transfer_block * tb = &lb->payload.transfer;
18311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  int res;
18321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_LOAD, "loader_verify");
18341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
183527d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse  tb->address = rec->addr;
183627d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse  tb->count = cpu_to_be32(be16_to_cpu(rec->len) / 4);
18371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  res = do_loader_command (lb, dev, read_adapter_memory);
183827d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse  if (!res && memcmp(tb->data, rec->data, be16_to_cpu(rec->len)))
183927d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse    res = -EINVAL;
18401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return res;
18411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
18421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* loader: start microcode */
18441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __devinit loader_start (loader_block * lb,
18461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				const amb_dev * dev, u32 address) {
18471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_LOAD, "loader_start");
18481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  lb->payload.start = cpu_to_be32 (address);
18501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return do_loader_command (lb, dev, adapter_start);
18511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
18521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** reset card **********/
18541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void sf (const char * msg)
18561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
18571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTK (KERN_ERR, "self-test failed: %s", msg);
18581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
18591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int amb_reset (amb_dev * dev, int diags) {
18611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u32 word;
18621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_LOAD, "amb_reset");
18641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  word = rd_plain (dev, offsetof(amb_mem, reset_control));
18661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // put card into reset state
18671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_plain (dev, offsetof(amb_mem, reset_control), word | AMB_RESET_BITS);
18681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // wait a short while
18691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  udelay (10);
18701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 1
18711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // put card into known good state
18721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_plain (dev, offsetof(amb_mem, interrupt_control), AMB_DOORBELL_BITS);
18731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // clear all interrupts just in case
18741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_plain (dev, offsetof(amb_mem, interrupt), -1);
18751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
18761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // clear self-test done flag
18771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_plain (dev, offsetof(amb_mem, mb.loader.ready), 0);
18781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // take card out of reset state
18791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_plain (dev, offsetof(amb_mem, reset_control), word &~ AMB_RESET_BITS);
18801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (diags) {
18821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    unsigned long timeout;
18831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // 4.2 second wait
18841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    msleep(4200);
18851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // half second time-out
18861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    timeout = 500;
18871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    while (!rd_plain (dev, offsetof(amb_mem, mb.loader.ready)))
18881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (timeout) {
18891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	timeout = msleep_interruptible(timeout);
18901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      } else {
18911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_LOAD|DBG_ERR, "reset timed out");
18921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -ETIMEDOUT;
18931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
18941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // get results of self-test
18961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // XXX double check byte-order
18971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    word = rd_mem (dev, offsetof(amb_mem, mb.loader.result));
18981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (word & SELF_TEST_FAILURE) {
18991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (word & GPINT_TST_FAILURE)
19001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sf ("interrupt");
19011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (word & SUNI_DATA_PATTERN_FAILURE)
19021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sf ("SUNI data pattern");
19031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (word & SUNI_DATA_BITS_FAILURE)
19041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sf ("SUNI data bits");
19051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (word & SUNI_UTOPIA_FAILURE)
19061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sf ("SUNI UTOPIA interface");
19071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (word & SUNI_FIFO_FAILURE)
19081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sf ("SUNI cell buffer FIFO");
19091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (word & SRAM_FAILURE)
19101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sf ("bad SRAM");
19111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // better return value?
19121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      return -EIO;
19131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
19141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
19161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return 0;
19171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
19181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** transfer and start the microcode **********/
19201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __devinit ucode_init (loader_block * lb, amb_dev * dev) {
192227d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse  const struct firmware *fw;
192327d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse  unsigned long start_address;
192427d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse  const struct ihex_binrec *rec;
19256a75da4a1a1d220fc3e32b816e83c258617505c5Jesper Juhl  const char *errmsg = 0;
19261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  int res;
19276a75da4a1a1d220fc3e32b816e83c258617505c5Jesper Juhl
192827d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse  res = request_ihex_firmware(&fw, "atmsar11.fw", &dev->pci_dev->dev);
192927d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse  if (res) {
193027d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse    PRINTK (KERN_ERR, "Cannot load microcode data");
193127d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse    return res;
193227d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse  }
193327d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse
193427d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse  /* First record contains just the start address */
193527d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse  rec = (const struct ihex_binrec *)fw->data;
193627d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse  if (be16_to_cpu(rec->len) != sizeof(__be32) || be32_to_cpu(rec->addr)) {
19376a75da4a1a1d220fc3e32b816e83c258617505c5Jesper Juhl    errmsg = "no start record";
19386a75da4a1a1d220fc3e32b816e83c258617505c5Jesper Juhl    goto fail;
193927d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse  }
194027d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse  start_address = be32_to_cpup((__be32 *)rec->data);
194127d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse
194227d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse  rec = ihex_next_binrec(rec);
194327d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse
19441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_LOAD, "ucode_init");
194527d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse
194627d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse  while (rec) {
194727d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse    PRINTD (DBG_LOAD, "starting region (%x, %u)", be32_to_cpu(rec->addr),
194827d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse	    be16_to_cpu(rec->len));
194927d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse    if (be16_to_cpu(rec->len) > 4 * MAX_TRANSFER_DATA) {
19506a75da4a1a1d220fc3e32b816e83c258617505c5Jesper Juhl	    errmsg = "record too long";
19516a75da4a1a1d220fc3e32b816e83c258617505c5Jesper Juhl	    goto fail;
19521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
195327d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse    if (be16_to_cpu(rec->len) & 3) {
19546a75da4a1a1d220fc3e32b816e83c258617505c5Jesper Juhl	    errmsg = "odd number of bytes";
19556a75da4a1a1d220fc3e32b816e83c258617505c5Jesper Juhl	    goto fail;
195627d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse    }
195727d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse    res = loader_write(lb, dev, rec);
195827d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse    if (res)
195927d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse      break;
196027d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse
196127d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse    res = loader_verify(lb, dev, rec);
196227d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse    if (res)
196327d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse      break;
19641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
196527d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse  release_firmware(fw);
196627d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse  if (!res)
196727d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse    res = loader_start(lb, dev, start_address);
196827d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse
196927d202fff1555f5b0eb16a5aedc452566f9ab8bbDavid Woodhouse  return res;
19706a75da4a1a1d220fc3e32b816e83c258617505c5Jesper Juhlfail:
19716a75da4a1a1d220fc3e32b816e83c258617505c5Jesper Juhl  release_firmware(fw);
19726a75da4a1a1d220fc3e32b816e83c258617505c5Jesper Juhl  PRINTK(KERN_ERR, "Bad microcode data (%s)", errmsg);
19736a75da4a1a1d220fc3e32b816e83c258617505c5Jesper Juhl  return -EINVAL;
19741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
19751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** give adapter parameters **********/
19771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline __be32 bus_addr(void * addr) {
19791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return cpu_to_be32 (virt_to_bus (addr));
19801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
19811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __devinit amb_talk (amb_dev * dev) {
19831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  adap_talk_block a;
19841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned char pool;
19851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned long timeout;
19861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW, "amb_talk %p", dev);
19881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  a.command_start = bus_addr (dev->cq.ptrs.start);
19901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  a.command_end   = bus_addr (dev->cq.ptrs.limit);
19911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  a.tx_start      = bus_addr (dev->txq.in.start);
19921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  a.tx_end        = bus_addr (dev->txq.in.limit);
19931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  a.txcom_start   = bus_addr (dev->txq.out.start);
19941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  a.txcom_end     = bus_addr (dev->txq.out.limit);
19951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  for (pool = 0; pool < NUM_RX_POOLS; ++pool) {
19971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // the other "a" items are set up by the adapter
19981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    a.rec_struct[pool].buffer_start = bus_addr (dev->rxq[pool].in.start);
19991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    a.rec_struct[pool].buffer_end   = bus_addr (dev->rxq[pool].in.limit);
20001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    a.rec_struct[pool].rx_start     = bus_addr (dev->rxq[pool].out.start);
20011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    a.rec_struct[pool].rx_end       = bus_addr (dev->rxq[pool].out.limit);
20021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    a.rec_struct[pool].buffer_size = cpu_to_be32 (dev->rxq[pool].buffer_size);
20031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
20041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef AMB_NEW_MICROCODE
20061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // disable fast PLX prefetching
20071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  a.init_flags = 0;
20081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
20091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // pass the structure
20111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_mem (dev, offsetof(amb_mem, doorbell), virt_to_bus (&a));
20121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // 2.2 second wait (must not touch doorbell during 2 second DMA test)
20141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  msleep(2200);
20151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // give the adapter another half second?
20161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  timeout = 500;
20171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  while (rd_plain (dev, offsetof(amb_mem, doorbell)))
20181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (timeout) {
20191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      timeout = msleep_interruptible(timeout);
20201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    } else {
20211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_INIT|DBG_ERR, "adapter init timed out");
20221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      return -ETIMEDOUT;
20231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
20241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return 0;
20261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
20271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds// get microcode version
20291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __devinit amb_ucode_version (amb_dev * dev) {
20301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u32 major;
20311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u32 minor;
20321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  command cmd;
20331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  cmd.request = cpu_to_be32 (SRB_GET_VERSION);
20341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  while (command_do (dev, &cmd)) {
20351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    set_current_state(TASK_UNINTERRUPTIBLE);
20361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    schedule();
20371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
20381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  major = be32_to_cpu (cmd.args.version.major);
20391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  minor = be32_to_cpu (cmd.args.version.minor);
20401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTK (KERN_INFO, "microcode version is %u.%u", major, minor);
20411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
20421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds// get end station address
20441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __devinit amb_esi (amb_dev * dev, u8 * esi) {
20451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u32 lower4;
20461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u16 upper2;
20471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  command cmd;
20481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  cmd.request = cpu_to_be32 (SRB_GET_BIA);
20501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  while (command_do (dev, &cmd)) {
20511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    set_current_state(TASK_UNINTERRUPTIBLE);
20521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    schedule();
20531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
20541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  lower4 = be32_to_cpu (cmd.args.bia.lower4);
20551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  upper2 = be32_to_cpu (cmd.args.bia.upper2);
20561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_LOAD, "BIA: lower4: %08x, upper2 %04x", lower4, upper2);
20571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (esi) {
20591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    unsigned int i;
20601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTDB (DBG_INIT, "ESI:");
20621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    for (i = 0; i < ESI_LEN; ++i) {
20631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (i < 4)
20640a8588578508a6c58a30ad1a7e23b285cfab49f3Akinobu Mita	  esi[i] = bitrev8(lower4>>(8*i));
20651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      else
20660a8588578508a6c58a30ad1a7e23b285cfab49f3Akinobu Mita	  esi[i] = bitrev8(upper2>>(8*(i-4)));
20671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTDM (DBG_INIT, " %02x", esi[i]);
20681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
20691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTDE (DBG_INIT, "");
20711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
20721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
20741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
20751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void fixup_plx_window (amb_dev *dev, loader_block *lb)
20771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
20781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// fix up the PLX-mapped window base address to match the block
20791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long blb;
20801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 mapreg;
20811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	blb = virt_to_bus(lb);
20821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// the kernel stack had better not ever cross a 1Gb boundary!
20831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mapreg = rd_plain (dev, offsetof(amb_mem, stuff[10]));
20841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mapreg &= ~onegigmask;
20851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mapreg |= blb & onegigmask;
20861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wr_plain (dev, offsetof(amb_mem, stuff[10]), mapreg);
20871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return;
20881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
20891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __devinit amb_init (amb_dev * dev)
20911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
20921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  loader_block lb;
20931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u32 version;
20951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (amb_reset (dev, 1)) {
20971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTK (KERN_ERR, "card reset failed!");
20981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  } else {
20991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    fixup_plx_window (dev, &lb);
21001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (get_loader_version (&lb, dev, &version)) {
21021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTK (KERN_INFO, "failed to get loader version");
21031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    } else {
21041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTK (KERN_INFO, "loader version is %08x", version);
21051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (ucode_init (&lb, dev)) {
21071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTK (KERN_ERR, "microcode failure");
21081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      } else if (create_queues (dev, cmds, txs, rxs, rxs_bs)) {
21091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTK (KERN_ERR, "failed to get memory for queues");
21101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      } else {
21111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (amb_talk (dev)) {
21131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  PRINTK (KERN_ERR, "adapter did not accept queues");
21141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
21151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  amb_ucode_version (dev);
21171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  return 0;
21181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} /* amb_talk */
21201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	destroy_queues (dev);
21221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      } /* create_queues, ucode_init */
21231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      amb_reset (dev, 0);
21251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    } /* get_loader_version */
21261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  } /* amb_reset */
21281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return -EINVAL;
21301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
21311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void setup_dev(amb_dev *dev, struct pci_dev *pci_dev)
21331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
21341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      unsigned char pool;
21351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // set up known dev items straight away
21371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev->pci_dev = pci_dev;
21381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      pci_set_drvdata(pci_dev, dev);
21391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev->iobase = pci_resource_start (pci_dev, 1);
21411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev->irq = pci_dev->irq;
21421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev->membase = bus_to_virt(pci_resource_start(pci_dev, 0));
21431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // flags (currently only dead)
21451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev->flags = 0;
21461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // Allocate cell rates (fibre)
21481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // ATM_OC3_PCR = 1555200000/8/270*260/53 - 29/53
21491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // to be really pedantic, this should be ATM_OC3c_PCR
21501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev->tx_avail = ATM_OC3_PCR;
21511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev->rx_avail = ATM_OC3_PCR;
21521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // semaphore for txer/rxer modifications - we cannot use a
21541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // spinlock as the critical region needs to switch processes
2155eff0dee54674a449e7f160aad9f3e0d38e6983ebDaniel Walker      mutex_init(&dev->vcc_sf);
21561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // queue manipulation spinlocks; we want atomic reads and
21571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // writes to the queue descriptors (handles IRQ and SMP)
21581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // consider replacing "int pending" -> "atomic_t available"
21591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // => problem related to who gets to move queue pointers
21601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      spin_lock_init (&dev->cq.lock);
21611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      spin_lock_init (&dev->txq.lock);
21621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      for (pool = 0; pool < NUM_RX_POOLS; ++pool)
21631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_init (&dev->rxq[pool].lock);
21641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
21651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void setup_pci_dev(struct pci_dev *pci_dev)
21671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
21681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char lat;
21691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// enable bus master accesses
21711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pci_set_master(pci_dev);
21721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// frobnicate latency (upwards, usually)
21741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pci_read_config_byte (pci_dev, PCI_LATENCY_TIMER, &lat);
21751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!pci_lat)
21771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pci_lat = (lat < MIN_PCI_LATENCY) ? MIN_PCI_LATENCY : lat;
21781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (lat != pci_lat) {
21801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		PRINTK (KERN_INFO, "Changing PCI latency timer from %hu to %hu",
21811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			lat, pci_lat);
21821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, pci_lat);
21831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
21841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
21851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __devinit amb_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_ent)
21871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
21881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	amb_dev * dev;
21891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err;
21901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int irq;
21911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = pci_enable_device(pci_dev);
21931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (err < 0) {
21941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		PRINTK (KERN_ERR, "skipped broken (PLX rev 2) card");
21951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
21961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
21971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// read resources from PCI configuration space
21991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	irq = pci_dev->irq;
22001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (pci_dev->device == PCI_DEVICE_ID_MADGE_AMBASSADOR_BAD) {
22021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		PRINTK (KERN_ERR, "skipped broken (PLX rev 2) card");
22031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err = -EINVAL;
22041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_disable;
22051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
22061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_INFO, "found Madge ATM adapter (amb) at"
2208e29419fffceb8ec36def3c922040e1ca7bcd3de5Greg Kroah-Hartman		" IO %llx, IRQ %u, MEM %p",
2209e29419fffceb8ec36def3c922040e1ca7bcd3de5Greg Kroah-Hartman		(unsigned long long)pci_resource_start(pci_dev, 1),
22101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		irq, bus_to_virt(pci_resource_start(pci_dev, 0)));
22111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// check IO region
22131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = pci_request_region(pci_dev, 1, DEV_LABEL);
22141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (err < 0) {
22151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		PRINTK (KERN_ERR, "IO range already in use!");
22161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_disable;
22171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
22181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2219a5929af4075c2ccd08aef7ca95a41f2a2abb3e47Joonwoo Park	dev = kzalloc(sizeof(amb_dev), GFP_KERNEL);
22201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dev) {
22211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		PRINTK (KERN_ERR, "out of memory!");
22221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err = -ENOMEM;
22231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_release;
22241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
22251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	setup_dev(dev, pci_dev);
22271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = amb_init(dev);
22291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (err < 0) {
22301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		PRINTK (KERN_ERR, "adapter initialisation failure");
22311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_free;
22321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
22331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	setup_pci_dev(pci_dev);
22351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// grab (but share) IRQ and install handler
2237dace145374b8e39aeb920304c358ab5e220341abThomas Gleixner	err = request_irq(irq, interrupt_handler, IRQF_SHARED, DEV_LABEL, dev);
22381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (err < 0) {
22391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		PRINTK (KERN_ERR, "request IRQ failed!");
22401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_reset;
22411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
22421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2243d9ca676bcb26e1fdff9265a3e70f697cd381c889Dan Williams	dev->atm_dev = atm_dev_register (DEV_LABEL, &pci_dev->dev, &amb_ops, -1,
2244d9ca676bcb26e1fdff9265a3e70f697cd381c889Dan Williams					 NULL);
22451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dev->atm_dev) {
22461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		PRINTD (DBG_ERR, "failed to register Madge ATM adapter");
22471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err = -EINVAL;
22481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_free_irq;
22491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
22501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_INFO, "registered Madge ATM adapter (no. %d) (%p) at %p",
22521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->atm_dev->number, dev, dev->atm_dev);
22531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->atm_dev->dev_data = (void *) dev;
22541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// register our address
22561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	amb_esi (dev, dev->atm_dev->esi);
22571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// 0 bits for vpi, 10 bits for vci
22591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->atm_dev->ci_range.vpi_bits = NUM_VPI_BITS;
22601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->atm_dev->ci_range.vci_bits = NUM_VCI_BITS;
22611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	init_timer(&dev->housekeeping);
22631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->housekeeping.function = do_housekeeping;
22641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->housekeeping.data = (unsigned long) dev;
22651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mod_timer(&dev->housekeeping, jiffies);
22661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// enable host interrupts
22681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	interrupts_on (dev);
22691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
22711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return err;
22721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_free_irq:
22741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_irq(irq, dev);
22751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_reset:
22761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	amb_reset(dev, 0);
22771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_free:
22781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(dev);
22791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_release:
22801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pci_release_region(pci_dev, 1);
22811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_disable:
22821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pci_disable_device(pci_dev);
22831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	goto out;
22841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
22851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __devexit amb_remove_one(struct pci_dev *pci_dev)
22881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
22891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct amb_dev *dev;
22901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev = pci_get_drvdata(pci_dev);
22921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD(DBG_INFO|DBG_INIT, "closing %p (atm_dev = %p)", dev, dev->atm_dev);
22941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	del_timer_sync(&dev->housekeeping);
22951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// the drain should not be necessary
22961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	drain_rx_pools(dev);
22971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	interrupts_off(dev);
22981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	amb_reset(dev, 0);
22991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_irq(dev->irq, dev);
23001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pci_disable_device(pci_dev);
23011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	destroy_queues(dev);
23021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	atm_dev_deregister(dev->atm_dev);
23031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(dev);
23041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pci_release_region(pci_dev, 1);
23051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
23061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __init amb_check_args (void) {
23081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned char pool;
23091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned int max_rx_size;
23101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_AMBASSADOR
23121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTK (KERN_NOTICE, "debug bitmap is %hx", debug &= DBG_MASK);
23131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
23141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (debug)
23151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTK (KERN_NOTICE, "no debugging support");
23161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
23171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (cmds < MIN_QUEUE_SIZE)
23191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTK (KERN_NOTICE, "cmds has been raised to %u",
23201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    cmds = MIN_QUEUE_SIZE);
23211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (txs < MIN_QUEUE_SIZE)
23231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTK (KERN_NOTICE, "txs has been raised to %u",
23241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    txs = MIN_QUEUE_SIZE);
23251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  for (pool = 0; pool < NUM_RX_POOLS; ++pool)
23271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (rxs[pool] < MIN_QUEUE_SIZE)
23281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTK (KERN_NOTICE, "rxs[%hu] has been raised to %u",
23291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	      pool, rxs[pool] = MIN_QUEUE_SIZE);
23301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // buffers sizes should be greater than zero and strictly increasing
23321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  max_rx_size = 0;
23331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  for (pool = 0; pool < NUM_RX_POOLS; ++pool)
23341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (rxs_bs[pool] <= max_rx_size)
23351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTK (KERN_NOTICE, "useless pool (rxs_bs[%hu] = %u)",
23361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	      pool, rxs_bs[pool]);
23371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    else
23381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      max_rx_size = rxs_bs[pool];
23391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (rx_lats < MIN_RX_BUFFERS)
23411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTK (KERN_NOTICE, "rx_lats has been raised to %u",
23421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    rx_lats = MIN_RX_BUFFERS);
23431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
23451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
23461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** module stuff **********/
23481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR(maintainer_string);
23501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION(description_string);
23511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
2352e8c0ae2c04372248f2f6940a5984f5748aae9664Ben HutchingsMODULE_FIRMWARE("atmsar11.fw");
23531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(debug,   ushort, 0644);
23541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(cmds,    uint, 0);
23551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(txs,     uint, 0);
23561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array(rxs,     uint, NULL, 0);
23571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param_array(rxs_bs,  uint, NULL, 0);
23581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(rx_lats, uint, 0);
23591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(pci_lat, byte, 0);
23601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(debug,   "debug bitmap, see .h file");
23611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(cmds,    "number of command queue entries");
23621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(txs,     "number of TX queue entries");
23631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(rxs,     "number of RX queue entries [" __MODULE_STRING(NUM_RX_POOLS) "]");
23641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(rxs_bs,  "size of RX buffers [" __MODULE_STRING(NUM_RX_POOLS) "]");
23651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(rx_lats, "number of extra buffers to cope with RX latencies");
23661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(pci_lat, "PCI latency in bus cycles");
23671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** module entry **********/
23691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct pci_device_id amb_pci_tbl[] = {
237112c8471a1e8af98b7e9a6917c393e0f85b582550Peter Huewe	{ PCI_VDEVICE(MADGE, PCI_DEVICE_ID_MADGE_AMBASSADOR), 0 },
237212c8471a1e8af98b7e9a6917c393e0f85b582550Peter Huewe	{ PCI_VDEVICE(MADGE, PCI_DEVICE_ID_MADGE_AMBASSADOR_BAD), 0 },
23731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ 0, }
23741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
23751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DEVICE_TABLE(pci, amb_pci_tbl);
23771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct pci_driver amb_driver = {
23791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name =		"amb",
23801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.probe =	amb_probe,
23811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.remove =	__devexit_p(amb_remove_one),
23821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table =	amb_pci_tbl,
23831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
23841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init amb_module_init (void)
23861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
23871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_INIT, "init_module");
23881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // sanity check - cast needed as printk does not support %Zu
23901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (sizeof(amb_mem) != 4*16 + 4*12) {
23911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTK (KERN_ERR, "Fix amb_mem (is %lu words).",
23921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    (unsigned long) sizeof(amb_mem));
23931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -ENOMEM;
23941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
23951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  show_version();
23971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  amb_check_args();
23991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // get the juice
24011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return pci_register_driver(&amb_driver);
24021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
24031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** module exit **********/
24051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit amb_module_exit (void)
24071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
24081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_INIT, "cleanup_module");
2409b45eccdb51c102e3c5ff9eaecc36200ab2eb09c0Tobias Klauser
2410b45eccdb51c102e3c5ff9eaecc36200ab2eb09c0Tobias Klauser  pci_unregister_driver(&amb_driver);
24111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
24121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(amb_module_init);
24141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(amb_module_exit);
2415