11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Madge Horizon 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/*
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  IMPORTANT NOTE: Madge Networks no longer makes the adapters
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  supported by this driver and makes no commitment to maintain it.
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mm.h>
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/pci.h>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/atm.h>
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/atmdev.h>
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/sonet.h>
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/skbuff.h>
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/time.h>
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h>
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/uio.h>
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
41a6b7a407865aab9f849dd99a71072b7cd1175116Alexey Dobriyan#include <linux/interrupt.h>
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ioport.h>
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/wait.h>
445a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/system.h>
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>
4860063497a95e716c9a689af3be2687d261f115b4Arun Sharma#include <linux/atomic.h>
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h>
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/string.h>
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/byteorder.h>
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "horizon.h"
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define maintainer_string "Giuliano Procida at Madge Networks <gprocida@madge.com>"
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define description_string "Madge ATM Horizon [Ultra] driver"
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define version_string "1.2.1"
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void __init show_version (void) {
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  printk ("%s version %s\n", description_string, version_string);
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  CREDITS
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Driver and documentation by:
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Chris Aston        Madge Networks
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Giuliano Procida   Madge Networks
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Simon Benham       Madge Networks
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Simon Johnson      Madge Networks
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Various Others     Madge Networks
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Some inspiration taken from other drivers by:
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Alexandru Cucos    UTBv
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Kari Mettinen      University of Helsinki
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Werner Almesberger EPFL LRC
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Theory of Operation
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  I Hardware, detection, initialisation and shutdown.
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  1. Supported Hardware
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  This driver should handle all variants of the PCI Madge ATM adapters
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  with the Horizon chipset. These are all PCI cards supporting PIO, BM
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  DMA and a form of MMIO (registers only, not internal RAM).
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  The driver is only known to work with SONET and UTP Horizon Ultra
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  cards at 155Mb/s. However, code is in place to deal with both the
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  original Horizon and 25Mb/s operation.
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  There are two revisions of the Horizon ASIC: the original and the
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Ultra. Details of hardware bugs are in section III.
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  The ASIC version can be distinguished by chip markings but is NOT
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  indicated by the PCI revision (all adapters seem to have PCI rev 1).
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  I believe that:
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Horizon       => Collage  25 PCI Adapter (UTP and STP)
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Horizon Ultra => Collage 155 PCI Client (UTP or SONET)
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Ambassador x  => Collage 155 PCI Server (completely different)
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Horizon (25Mb/s) is fitted with UTP and STP connectors. It seems to
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  have a Madge B154 plus glue logic serializer. I have also found a
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  really ancient version of this with slightly different glue. It
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  comes with the revision 0 (140-025-01) ASIC.
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Horizon Ultra (155Mb/s) is fitted with either a Pulse Medialink
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  output (UTP) or an HP HFBR 5205 output (SONET). It has either
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Madge's SAMBA framer or a SUNI-lite device (early versions). It
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  comes with the revision 1 (140-027-01) ASIC.
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  2. Detection
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  All Horizon-based cards present with the same PCI Vendor and Device
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  IDs. The standard Linux 2.2 PCI API is used to locate any cards and
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  to enable bus-mastering (with appropriate latency).
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ATM_LAYER_STATUS in the control register distinguishes between the
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  two possible physical layers (25 and 155). It is not clear whether
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  the 155 cards can also operate at 25Mbps. We rely on the fact that a
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  card operates at 155 if and only if it has the newer Horizon Ultra
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ASIC.
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  For 155 cards the two possible framers are probed for and then set
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  up for loop-timing.
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  3. Initialisation
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  The card is reset and then put into a known state. The physical
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  layer is configured for normal operation at the appropriate speed;
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  in the case of the 155 cards, the framer is initialised with
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  line-based timing; the internal RAM is zeroed and the allocation of
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  buffers for RX and TX is made; the Burnt In Address is read and
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  copied to the ATM ESI; various policy settings for RX (VPI bits,
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unknown VCs, oam cells) are made. Ideally all policy items should be
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  configurable at module load (if not actually on-demand), however,
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  only the vpi vs vci bit allocation can be specified at insmod.
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  4. Shutdown
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  This is in response to module_cleaup. No VCs are in use and the card
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  should be idle; it is reset.
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  II Driver software (as it should be)
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  0. Traffic Parameters
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  The traffic classes (not an enumeration) are currently: ATM_NONE (no
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  traffic), ATM_UBR, ATM_CBR, ATM_VBR and ATM_ABR, ATM_ANYCLASS
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  (compatible with everything). Together with (perhaps only some of)
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  the following items they make up the traffic specification.
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  struct atm_trafprm {
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    unsigned char traffic_class; traffic class (ATM_UBR, ...)
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    int           max_pcr;       maximum PCR in cells per second
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    int           pcr;           desired PCR in cells per second
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    int           min_pcr;       minimum PCR in cells per second
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    int           max_cdv;       maximum CDV in microseconds
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    int           max_sdu;       maximum SDU in bytes
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  };
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Note that these denote bandwidth available not bandwidth used; the
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  possibilities according to ATMF are:
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Real Time (cdv and max CDT given)
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  CBR(pcr)             pcr bandwidth always available
17325985edcedea6396277003854657b5f3cb31a628Lucas De Marchi  rtVBR(pcr,scr,mbs)   scr bandwidth always available, up to pcr at mbs too
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Non Real Time
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17725985edcedea6396277003854657b5f3cb31a628Lucas De Marchi  nrtVBR(pcr,scr,mbs)  scr bandwidth always available, up to pcr at mbs too
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  UBR()
17925985edcedea6396277003854657b5f3cb31a628Lucas De Marchi  ABR(mcr,pcr)         mcr bandwidth always available, up to pcr (depending) too
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  mbs is max burst size (bucket)
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  pcr and scr have associated cdvt values
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  mcr is like scr but has no cdtv
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  cdtv may differ at each hop
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Some of the above items are qos items (as opposed to traffic
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  parameters). We have nothing to do with qos. All except ABR can have
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  their traffic parameters converted to GCRA parameters. The GCRA may
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  be implemented as a (real-number) leaky bucket. The GCRA can be used
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  in complicated ways by switches and in simpler ways by end-stations.
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  It can be used both to filter incoming cells and shape out-going
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  cells.
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ATM Linux actually supports:
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ATM_NONE() (no traffic in this direction)
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ATM_UBR(max_frame_size)
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ATM_CBR(max/min_pcr, max_cdv, max_frame_size)
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  0 or ATM_MAX_PCR are used to indicate maximum available PCR
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  A traffic specification consists of the AAL type and separate
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  traffic specifications for either direction. In ATM Linux it is:
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  struct atm_qos {
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  struct atm_trafprm txtp;
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  struct atm_trafprm rxtp;
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned char aal;
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  };
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  AAL types are:
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ATM_NO_AAL    AAL not specified
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ATM_AAL0      "raw" ATM cells
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ATM_AAL1      AAL1 (CBR)
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ATM_AAL2      AAL2 (VBR)
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ATM_AAL34     AAL3/4 (data)
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ATM_AAL5      AAL5 (data)
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ATM_SAAL      signaling AAL
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  The Horizon has support for AAL frame types: 0, 3/4 and 5. However,
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  it does not implement AAL 3/4 SAR and it has a different notion of
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  "raw cell" to ATM Linux's (48 bytes vs. 52 bytes) so neither are
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  supported by this driver.
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  The Horizon has limited support for ABR (including UBR), VBR and
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  CBR. Each TX channel has a bucket (containing up to 31 cell units)
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  and two timers (PCR and SCR) associated with it that can be used to
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  govern cell emissions and host notification (in the case of ABR this
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  is presumably so that RM cells may be emitted at appropriate times).
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  The timers may either be disabled or may be set to any of 240 values
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  (determined by the clock crystal, a fixed (?) per-device divider, a
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  configurable divider and a configurable timer preload value).
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  At the moment only UBR and CBR are supported by the driver. VBR will
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  be supported as soon as ATM for Linux supports it. ABR support is
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  very unlikely as RM cell handling is completely up to the driver.
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  1. TX (TX channel setup and TX transfer)
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  The TX half of the driver owns the TX Horizon registers. The TX
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  component in the IRQ handler is the BM completion handler. This can
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  only be entered when tx_busy is true (enforced by hardware). The
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  other TX component can only be entered when tx_busy is false
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  (enforced by driver). So TX is single-threaded.
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Apart from a minor optimisation to not re-select the last channel,
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  the TX send component works as follows:
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Atomic test and set tx_busy until we succeed; we should implement
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  some sort of timeout so that tx_busy will never be stuck at true.
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  If no TX channel is set up for this VC we wait for an idle one (if
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  necessary) and set it up.
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  At this point we have a TX channel ready for use. We wait for enough
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  buffers to become available then start a TX transmit (set the TX
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  descriptor, schedule transfer, exit).
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  The IRQ component handles TX completion (stats, free buffer, tx_busy
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unset, exit). We also re-schedule further transfers for the same
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  frame if needed.
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  TX setup in more detail:
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  TX open is a nop, the relevant information is held in the hrz_vcc
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  (vcc->dev_data) structure and is "cached" on the card.
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  TX close gets the TX lock and clears the channel from the "cache".
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  2. RX (Data Available and RX transfer)
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  The RX half of the driver owns the RX registers. There are two RX
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  components in the IRQ handler: the data available handler deals with
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  fresh data that has arrived on the card, the BM completion handler
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  is very similar to the TX completion handler. The data available
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  handler grabs the rx_lock and it is only released once the data has
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  been discarded or completely transferred to the host. The BM
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  completion handler only runs when the lock is held; the data
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  available handler is locked out over the same period.
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Data available on the card triggers an interrupt. If the data is not
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  suitable for our existing RX channels or we cannot allocate a buffer
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  it is flushed. Otherwise an RX receive is scheduled. Multiple RX
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  transfers may be scheduled for the same frame.
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  RX setup in more detail:
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  RX open...
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  RX close...
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  III Hardware Bugs
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  0. Byte vs Word addressing of adapter RAM.
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  A design feature; see the .h file (especially the memory map).
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  1. Bus Master Data Transfers (original Horizon only, fixed in Ultra)
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  The host must not start a transmit direction transfer at a
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  non-four-byte boundary in host memory. Instead the host should
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  perform a byte, or a two byte, or one byte followed by two byte
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  transfer in order to start the rest of the transfer on a four byte
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  boundary. RX is OK.
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Simultaneous transmit and receive direction bus master transfers are
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  not allowed.
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  The simplest solution to these two is to always do PIO (never DMA)
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  in the TX direction on the original Horizon. More complicated
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  solutions are likely to hurt my brain.
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  2. Loss of buffer on close VC
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  When a VC is being closed, the buffer associated with it is not
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  returned to the pool. The host must store the reference to this
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  buffer and when opening a new VC then give it to that new VC.
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  The host intervention currently consists of stacking such a buffer
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  pointer at VC close and checking the stack at VC open.
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  3. Failure to close a VC
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  If a VC is currently receiving a frame then closing the VC may fail
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  and the frame continues to be received.
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  The solution is to make sure any received frames are flushed when
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ready. This is currently done just before the solution to 2.
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  4. PCI bus (original Horizon only, fixed in Ultra)
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  Reading from the data port prior to initialisation will hang the PCI
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  bus. Just don't do that then! We don't.
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  IV To Do List
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Timer code may be broken.
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Allow users to specify buffer allocation split for TX and RX.
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Deal once and for all with buggy VC close.
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Handle interrupted and/or non-blocking operations.
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Change some macros to functions and move from .h to .c.
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Try to limit the number of TX frames each VC may have queued, in
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    order to reduce the chances of TX buffer exhaustion.
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Implement VBR (bucket and timers not understood) and ABR (need to
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    do RM cells manually); also no Linux support for either.
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  . Implement QoS changes on open VCs (involves extracting parts of VC open
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    and close into separate functions and using them to make changes).
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds*/
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** globals **********/
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void do_housekeeping (unsigned long arg);
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned short debug = 0;
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned short vpi_bits = 0;
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int max_tx_size = 9000;
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int max_rx_size = 9000;
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned char pci_lat = 0;
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** access functions **********/
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Read / Write Horizon registers */
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void wr_regl (const hrz_dev * dev, unsigned char reg, u32 data) {
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  outl (cpu_to_le32 (data), dev->iobase + reg);
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline u32 rd_regl (const hrz_dev * dev, unsigned char reg) {
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return le32_to_cpu (inl (dev->iobase + reg));
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void wr_regw (const hrz_dev * dev, unsigned char reg, u16 data) {
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  outw (cpu_to_le16 (data), dev->iobase + reg);
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline u16 rd_regw (const hrz_dev * dev, unsigned char reg) {
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return le16_to_cpu (inw (dev->iobase + reg));
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void wrs_regb (const hrz_dev * dev, unsigned char reg, void * addr, u32 len) {
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  outsb (dev->iobase + reg, addr, len);
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void rds_regb (const hrz_dev * dev, unsigned char reg, void * addr, u32 len) {
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  insb (dev->iobase + reg, addr, len);
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Read / Write to a given address in Horizon buffer memory.
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Interrupts must be disabled between the address register and data
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   port accesses as these must form an atomic operation. */
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void wr_mem (const hrz_dev * dev, HDW * addr, u32 data) {
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // wr_regl (dev, MEM_WR_ADDR_REG_OFF, (u32) addr);
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_regl (dev, MEM_WR_ADDR_REG_OFF, (addr - (HDW *) 0) * sizeof(HDW));
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_regl (dev, MEMORY_PORT_OFF, data);
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline u32 rd_mem (const hrz_dev * dev, HDW * addr) {
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // wr_regl (dev, MEM_RD_ADDR_REG_OFF, (u32) addr);
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_regl (dev, MEM_RD_ADDR_REG_OFF, (addr - (HDW *) 0) * sizeof(HDW));
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return rd_regl (dev, MEMORY_PORT_OFF);
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void wr_framer (const hrz_dev * dev, u32 addr, u32 data) {
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_regl (dev, MEM_WR_ADDR_REG_OFF, (u32) addr | 0x80000000);
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_regl (dev, MEMORY_PORT_OFF, data);
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline u32 rd_framer (const hrz_dev * dev, u32 addr) {
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_regl (dev, MEM_RD_ADDR_REG_OFF, (u32) addr | 0x80000000);
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return rd_regl (dev, MEMORY_PORT_OFF);
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** specialised access functions **********/
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* RX */
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void FLUSH_RX_CHANNEL (hrz_dev * dev, u16 channel) {
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_regw (dev, RX_CHANNEL_PORT_OFF, FLUSH_CHANNEL | channel);
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4292cf83afe902fd72ef4b211774e48ab39890fb328Denys Vlasenkostatic void WAIT_FLUSH_RX_COMPLETE (hrz_dev * dev) {
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  while (rd_regw (dev, RX_CHANNEL_PORT_OFF) & FLUSH_CHANNEL)
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    ;
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void SELECT_RX_CHANNEL (hrz_dev * dev, u16 channel) {
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_regw (dev, RX_CHANNEL_PORT_OFF, channel);
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4402cf83afe902fd72ef4b211774e48ab39890fb328Denys Vlasenkostatic void WAIT_UPDATE_COMPLETE (hrz_dev * dev) {
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  while (rd_regw (dev, RX_CHANNEL_PORT_OFF) & RX_CHANNEL_UPDATE_IN_PROGRESS)
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    ;
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* TX */
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void SELECT_TX_CHANNEL (hrz_dev * dev, u16 tx_channel) {
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_regl (dev, TX_CHANNEL_PORT_OFF, tx_channel);
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Update or query one configuration parameter of a particular channel. */
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void update_tx_channel_config (hrz_dev * dev, short chan, u8 mode, u16 value) {
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_regw (dev, TX_CHANNEL_CONFIG_COMMAND_OFF,
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   chan * TX_CHANNEL_CONFIG_MULT | mode);
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    wr_regw (dev, TX_CHANNEL_CONFIG_DATA_OFF, value);
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return;
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline u16 query_tx_channel_config (hrz_dev * dev, short chan, u8 mode) {
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_regw (dev, TX_CHANNEL_CONFIG_COMMAND_OFF,
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   chan * TX_CHANNEL_CONFIG_MULT | mode);
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return rd_regw (dev, TX_CHANNEL_CONFIG_DATA_OFF);
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** dump functions **********/
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void dump_skb (char * prefix, unsigned int vc, struct sk_buff * skb) {
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_HORIZON
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned int i;
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned char * data = skb->data;
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTDB (DBG_DATA, "%s(%u) ", prefix, vc);
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  for (i=0; i<skb->len && i < 256;i++)
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTDM (DBG_DATA, "%02x ", data[i]);
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTDE (DBG_DATA,"");
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  (void) prefix;
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  (void) vc;
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  (void) skb;
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void dump_regs (hrz_dev * dev) {
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_HORIZON
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_REGS, "CONTROL 0: %#x", rd_regl (dev, CONTROL_0_REG));
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_REGS, "RX CONFIG: %#x", rd_regw (dev, RX_CONFIG_OFF));
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_REGS, "TX CONFIG: %#x", rd_regw (dev, TX_CONFIG_OFF));
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_REGS, "TX STATUS: %#x", rd_regw (dev, TX_STATUS_OFF));
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_REGS, "IRQ ENBLE: %#x", rd_regl (dev, INT_ENABLE_REG_OFF));
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_REGS, "IRQ SORCE: %#x", rd_regl (dev, INT_SOURCE_REG_OFF));
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  (void) dev;
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void dump_framer (hrz_dev * dev) {
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_HORIZON
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned int i;
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTDB (DBG_REGS, "framer registers:");
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  for (i = 0; i < 0x10; ++i)
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTDM (DBG_REGS, " %02x", rd_framer (dev, i));
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTDE (DBG_REGS,"");
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  (void) dev;
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** VPI/VCI <-> (RX) channel conversions **********/
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* RX channels are 10 bit integers, these fns are quite paranoid */
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline int channel_to_vpivci (const u16 channel, short * vpi, int * vci) {
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned short vci_bits = 10 - vpi_bits;
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if ((channel & RX_CHANNEL_MASK) == channel) {
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    *vci = channel & ((~0)<<vci_bits);
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    *vpi = channel >> vci_bits;
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return channel ? 0 : -EINVAL;
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return -EINVAL;
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline int vpivci_to_channel (u16 * channel, const short vpi, const int vci) {
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned short vci_bits = 10 - vpi_bits;
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (0 <= vpi && vpi < 1<<vpi_bits && 0 <= vci && vci < 1<<vci_bits) {
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    *channel = vpi<<vci_bits | vci;
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return *channel ? 0 : -EINVAL;
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return -EINVAL;
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** decode RX queue entries **********/
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline u16 rx_q_entry_to_length (u32 x) {
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return x & RX_Q_ENTRY_LENGTH_MASK;
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline u16 rx_q_entry_to_rx_channel (u32 x) {
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return (x>>RX_Q_ENTRY_CHANNEL_SHIFT) & RX_CHANNEL_MASK;
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Cell Transmit Rate Values
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the cell transmit rate (cells per sec) can be set to a variety of
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * different values by specifying two parameters: a timer preload from
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 1 to 16 (stored as 0 to 15) and a clock divider (2 to the power of
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * an exponent from 0 to 14; the special value 15 disables the timer).
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * cellrate = baserate / (preload * 2^divider)
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The maximum cell rate that can be specified is therefore just the
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * base rate. Halving the preload is equivalent to adding 1 to the
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * divider and so values 1 to 8 of the preload are redundant except
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * in the case of a maximal divider (14).
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Given a desired cell rate, an algorithm to determine the preload
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * and divider is:
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * a) x = baserate / cellrate, want p * 2^d = x (as far as possible)
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * b) if x > 16 * 2^14 then set p = 16, d = 14 (min rate), done
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *    if x <= 16 then set p = x, d = 0 (high rates), done
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * c) now have 16 < x <= 2^18, or 1 < x/16 <= 2^14 and we want to
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *    know n such that 2^(n-1) < x/16 <= 2^n, so slide a bit until
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *    we find the range (n will be between 1 and 14), set d = n
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * d) Also have 8 < x/2^n <= 16, so set p nearest x/2^n
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The algorithm used below is a minor variant of the above.
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The base rate is derived from the oscillator frequency (Hz) using a
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * fixed divider:
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * baserate = freq / 32 in the case of some Unknown Card
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * baserate = freq / 8  in the case of the Horizon        25
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * baserate = freq / 8  in the case of the Horizon Ultra 155
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The Horizon cards have oscillators and base rates as follows:
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Card               Oscillator  Base Rate
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Unknown Card       33 MHz      1.03125 MHz (33 MHz = PCI freq)
5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Horizon        25  32 MHz      4       MHz
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Horizon Ultra 155  40 MHz      5       MHz
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The following defines give the base rates in Hz. These were
5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * previously a factor of 100 larger, no doubt someone was using
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * cps*100.
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define BR_UKN 1031250l
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define BR_HRZ 4000000l
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define BR_ULT 5000000l
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds// d is an exponent
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CR_MIND 0
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CR_MAXD 14
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds// p ranges from 1 to a power of 2
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define CR_MAXPEXP 4
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int make_rate (const hrz_dev * dev, u32 c, rounding r,
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		      u16 * bits, unsigned int * actual)
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// note: rounding the rate down means rounding 'p' up
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	const unsigned long br = test_bit(ultra, &dev->flags) ? BR_ULT : BR_HRZ;
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 div = CR_MIND;
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 pre;
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// br_exp and br_man are used to avoid overflowing (c*maxp*2^d) in
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// the tests below. We could think harder about exact possibilities
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// of failure...
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long br_man = br;
6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int br_exp = 0;
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_QOS|DBG_FLOW, "make_rate b=%lu, c=%u, %s", br, c,
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		r == round_up ? "up" : r == round_down ? "down" : "nearest");
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// avoid div by zero
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!c) {
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		PRINTD (DBG_QOS|DBG_ERR, "zero rate is not allowed!");
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (br_exp < CR_MAXPEXP + CR_MIND && (br_man % 2 == 0)) {
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		br_man = br_man >> 1;
6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		++br_exp;
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// (br >>br_exp) <<br_exp == br and
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// br_exp <= CR_MAXPEXP+CR_MIND
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (br_man <= (c << (CR_MAXPEXP+CR_MIND-br_exp))) {
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		// Equivalent to: B <= (c << (MAXPEXP+MIND))
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		// take care of rounding
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		switch (r) {
6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case round_down:
6406a19309db0a02d821494f4df754046c85a230627Julia Lawall				pre = DIV_ROUND_UP(br, c<<div);
6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				// but p must be non-zero
6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (!pre)
6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					pre = 1;
6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			case round_nearest:
6468b97c7c283c5ba8f9f4dabd418fd7dcfcc8a387eJulia Lawall				pre = DIV_ROUND_CLOSEST(br, c<<div);
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				// but p must be non-zero
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (!pre)
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					pre = 1;
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			default:	/* round_up */
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				pre = br/(c<<div);
6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				// but p must be non-zero
6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (!pre)
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					return -EINVAL;
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		PRINTD (DBG_QOS, "A: p=%u, d=%u", pre, div);
6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto got_it;
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// at this point we have
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// d == MIND and (c << (MAXPEXP+MIND)) < B
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (div < CR_MAXD) {
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		div++;
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (br_man <= (c << (CR_MAXPEXP+div-br_exp))) {
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			// Equivalent to: B <= (c << (MAXPEXP+d))
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			// c << (MAXPEXP+d-1) < B <= c << (MAXPEXP+d)
6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			// 1 << (MAXPEXP-1) < B/2^d/c <= 1 << MAXPEXP
6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			// MAXP/2 < B/c2^d <= MAXP
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			// take care of rounding
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			switch (r) {
6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				case round_down:
6736a19309db0a02d821494f4df754046c85a230627Julia Lawall					pre = DIV_ROUND_UP(br, c<<div);
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				case round_nearest:
6768b97c7c283c5ba8f9f4dabd418fd7dcfcc8a387eJulia Lawall					pre = DIV_ROUND_CLOSEST(br, c<<div);
6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					break;
6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				default: /* round_up */
6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					pre = br/(c<<div);
6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			PRINTD (DBG_QOS, "B: p=%u, d=%u", pre, div);
6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto got_it;
6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// at this point we have
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// d == MAXD and (c << (MAXPEXP+MAXD)) < B
6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// but we cannot go any higher
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// take care of rounding
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (r == round_down)
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pre = 1 << CR_MAXPEXP;
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_QOS, "C: p=%u, d=%u", pre, div);
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsgot_it:
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// paranoia
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (div > CR_MAXD || (!pre) || pre > 1<<CR_MAXPEXP) {
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		PRINTD (DBG_QOS, "set_cr internal failure: d=%u p=%u",
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			div, pre);
6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (bits)
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			*bits = (div<<CLOCK_SELECT_SHIFT) | (pre-1);
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (actual) {
7036a19309db0a02d821494f4df754046c85a230627Julia Lawall			*actual = DIV_ROUND_UP(br, pre<<div);
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			PRINTD (DBG_QOS, "actual rate: %u", *actual);
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int make_rate_with_tolerance (const hrz_dev * dev, u32 c, rounding r, unsigned int tol,
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				     u16 * bit_pattern, unsigned int * actual) {
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned int my_actual;
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_QOS|DBG_FLOW, "make_rate_with_tolerance c=%u, %s, tol=%u",
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  c, (r == round_up) ? "up" : (r == round_down) ? "down" : "nearest", tol);
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (!actual)
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // actual rate is not returned
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    actual = &my_actual;
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (make_rate (dev, c, round_nearest, bit_pattern, actual))
7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // should never happen as round_nearest always succeeds
7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -1;
7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (c - tol <= *actual && *actual <= c + tol)
7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // within tolerance
7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return 0;
7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  else
7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // intolerant, try rounding instead
7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return make_rate (dev, c, r, bit_pattern, actual);
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** Listen on a VC **********/
7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int hrz_open_rx (hrz_dev * dev, u16 channel) {
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // is there any guarantee that we don't get two simulataneous
7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // identical calls of this function from different processes? yes
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // rate_lock
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned long flags;
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u32 channel_type; // u16?
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u16 buf_ptr = RX_CHANNEL_IDLE;
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  rx_ch_desc * rx_desc = &memmap->rx_descs[channel];
7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW, "hrz_open_rx %x", channel);
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  spin_lock_irqsave (&dev->mem_lock, flags);
7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  channel_type = rd_mem (dev, &rx_desc->wr_buf_type) & BUFFER_PTR_MASK;
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  spin_unlock_irqrestore (&dev->mem_lock, flags);
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // very serious error, should never occur
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (channel_type != RX_CHANNEL_DISABLED) {
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_ERR|DBG_VCC, "RX channel for VC already open");
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -EBUSY; // clean up?
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // Give back spare buffer
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (dev->noof_spare_buffers) {
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    buf_ptr = dev->spare_buffers[--dev->noof_spare_buffers];
7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_VCC, "using a spare buffer: %u", buf_ptr);
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // should never occur
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (buf_ptr == RX_CHANNEL_DISABLED || buf_ptr == RX_CHANNEL_IDLE) {
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // but easy to recover from
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_ERR|DBG_VCC, "bad spare buffer pointer, using IDLE");
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      buf_ptr = RX_CHANNEL_IDLE;
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  } else {
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_VCC, "using IDLE buffer pointer");
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // Channel is currently disabled so change its status to idle
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // do we really need to save the flags again?
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  spin_lock_irqsave (&dev->mem_lock, flags);
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_mem (dev, &rx_desc->wr_buf_type,
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  buf_ptr | CHANNEL_TYPE_AAL5 | FIRST_CELL_OF_AAL5_FRAME);
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (buf_ptr != RX_CHANNEL_IDLE)
7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    wr_mem (dev, &rx_desc->rd_buf_type, buf_ptr);
7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  spin_unlock_irqrestore (&dev->mem_lock, flags);
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // rxer->rate = make_rate (qos->peak_cells);
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW, "hrz_open_rx ok");
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return 0;
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** change vc rate for a given vc **********/
7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void hrz_change_vc_qos (ATM_RXER * rxer, MAAL_QOS * qos) {
7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  rxer->rate = make_rate (qos->peak_cells);
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** free an skb (as per ATM device driver documentation) **********/
8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8012cf83afe902fd72ef4b211774e48ab39890fb328Denys Vlasenkostatic void hrz_kfree_skb (struct sk_buff * skb) {
8021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (ATM_SKB(skb)->vcc->pop) {
8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    ATM_SKB(skb)->vcc->pop (ATM_SKB(skb)->vcc, skb);
8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  } else {
8051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev_kfree_skb_any (skb);
8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** cancel listen on a VC **********/
8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void hrz_close_rx (hrz_dev * dev, u16 vc) {
8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned long flags;
8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u32 value;
8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u32 r1, r2;
8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  rx_ch_desc * rx_desc = &memmap->rx_descs[vc];
8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  int was_idle = 0;
8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  spin_lock_irqsave (&dev->mem_lock, flags);
8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  value = rd_mem (dev, &rx_desc->wr_buf_type) & BUFFER_PTR_MASK;
8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  spin_unlock_irqrestore (&dev->mem_lock, flags);
8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (value == RX_CHANNEL_DISABLED) {
8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // I suppose this could happen once we deal with _NONE traffic properly
8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_VCC, "closing VC: RX channel %u already disabled", vc);
8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return;
8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (value == RX_CHANNEL_IDLE)
8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    was_idle = 1;
8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  spin_lock_irqsave (&dev->mem_lock, flags);
8351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  for (;;) {
8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    wr_mem (dev, &rx_desc->wr_buf_type, RX_CHANNEL_DISABLED);
8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if ((rd_mem (dev, &rx_desc->wr_buf_type) & BUFFER_PTR_MASK) == RX_CHANNEL_DISABLED)
8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      break;
8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    was_idle = 0;
8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (was_idle) {
8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    spin_unlock_irqrestore (&dev->mem_lock, flags);
8471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return;
8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  WAIT_FLUSH_RX_COMPLETE(dev);
8511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // XXX Is this all really necessary? We can rely on the rx_data_av
8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // handler to discard frames that remain queued for delivery. If the
8541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // worry is that immediately reopening the channel (perhaps by a
8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // different process) may cause some data to be mis-delivered then
8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // there may still be a simpler solution (such as busy-waiting on
8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // rx_busy once the channel is disabled or before a new one is
8581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // opened - does this leave any holes?). Arguably setting up and
8591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // tearing down the TX and RX halves of each virtual circuit could
8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // most safely be done within ?x_busy protected regions.
8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // OK, current changes are that Simon's marker is disabled and we DO
8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // look for NULL rxer elsewhere. The code here seems flush frames
8641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // and then remember the last dead cell belonging to the channel
8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // just disabled - the cell gets relinked at the next vc_open.
8661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // However, when all VCs are closed or only a few opened there are a
8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // handful of buffers that are unusable.
8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // Does anyone feel like documenting spare_buffers properly?
8701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // Does anyone feel like fixing this in a nicer way?
8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // Flush any data which is left in the channel
8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  for (;;) {
8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // Change the rx channel port to something different to the RX
8751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // channel we are trying to close to force Horizon to flush the rx
8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // channel read and write pointers.
8771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    u16 other = vc^(RX_CHANS/2);
8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    SELECT_RX_CHANNEL (dev, other);
8811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    WAIT_UPDATE_COMPLETE (dev);
8821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    r1 = rd_mem (dev, &rx_desc->rd_buf_type);
8841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // Select this RX channel. Flush doesn't seem to work unless we
8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // select an RX channel before hand
8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    SELECT_RX_CHANNEL (dev, vc);
8891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    WAIT_UPDATE_COMPLETE (dev);
8901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // Attempt to flush a frame on this RX channel
8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    FLUSH_RX_CHANNEL (dev, vc);
8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    WAIT_FLUSH_RX_COMPLETE (dev);
8951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // Force Horizon to flush rx channel read and write pointers as before
8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    SELECT_RX_CHANNEL (dev, other);
8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    WAIT_UPDATE_COMPLETE (dev);
9001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    r2 = rd_mem (dev, &rx_desc->rd_buf_type);
9021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_VCC|DBG_RX, "r1 = %u, r2 = %u", r1, r2);
9041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (r1 == r2) {
9061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev->spare_buffers[dev->noof_spare_buffers++] = (u16)r1;
9071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      break;
9081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
9091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
9101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
9121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  {
9131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rx_q_entry * wr_ptr = &memmap->rx_q_entries[rd_regw (dev, RX_QUEUE_WR_PTR_OFF)];
9141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rx_q_entry * rd_ptr = dev->rx_q_entry;
9151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_VCC|DBG_RX, "rd_ptr = %u, wr_ptr = %u", rd_ptr, wr_ptr);
9171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    while (rd_ptr != wr_ptr) {
9191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      u32 x = rd_mem (dev, (HDW *) rd_ptr);
9201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (vc == rx_q_entry_to_rx_channel (x)) {
9221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	x |= SIMONS_DODGEY_MARKER;
9231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_RX|DBG_VCC|DBG_WARN, "marking a frame as dodgey");
9251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wr_mem (dev, (HDW *) rd_ptr, x);
9271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
9281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (rd_ptr == dev->rx_q_wrap)
9301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rd_ptr = dev->rx_q_reset;
9311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      else
9321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rd_ptr++;
9331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
9341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
9351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
9361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  spin_unlock_irqrestore (&dev->mem_lock, flags);
9381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
9401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
9411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** schedule RX transfers **********/
9431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds// Note on tail recursion: a GCC developer said that it is not likely
9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds// to be fixed soon, so do not define TAILRECUSRIONWORKS unless you
9461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds// are sure it does as you may otherwise overflow the kernel stack.
9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
94825985edcedea6396277003854657b5f3cb31a628Lucas De Marchi// giving this fn a return value would help GCC, allegedly
9491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rx_schedule (hrz_dev * dev, int irq) {
9511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned int rx_bytes;
9521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  int pio_instead = 0;
9541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef TAILRECURSIONWORKS
9551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  pio_instead = 1;
9561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  while (pio_instead) {
9571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
9581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // bytes waiting for RX transfer
9591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rx_bytes = dev->rx_bytes;
9601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
9621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    spin_count = 0;
9631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    while (rd_regl (dev, MASTER_RX_COUNT_REG_OFF)) {
9641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_RX|DBG_WARN, "RX error: other PCI Bus Master RX still in progress!");
9651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (++spin_count > 10) {
9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_RX|DBG_ERR, "spun out waiting PCI Bus Master RX completion");
9671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wr_regl (dev, MASTER_RX_COUNT_REG_OFF, 0);
9681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	clear_bit (rx_busy, &dev->flags);
9691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hrz_kfree_skb (dev->rx_skb);
9701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return;
9711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
9721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
9731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
9741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // this code follows the TX code but (at the moment) there is only
9761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // one region - the skb itself. I don't know if this will change,
9771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // but it doesn't hurt to have the code here, disabled.
9781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (rx_bytes) {
9801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // start next transfer within same region
9811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (rx_bytes <= MAX_PIO_COUNT) {
9821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_RX|DBG_BUS, "(pio)");
9831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pio_instead = 1;
9841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
9851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (rx_bytes <= MAX_TRANSFER_COUNT) {
9861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_RX|DBG_BUS, "(simple or last multi)");
9871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->rx_bytes = 0;
9881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      } else {
9891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_RX|DBG_BUS, "(continuing multi)");
9901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->rx_bytes = rx_bytes - MAX_TRANSFER_COUNT;
9911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rx_bytes = MAX_TRANSFER_COUNT;
9921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
9931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    } else {
9941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // rx_bytes == 0 -- we're between regions
9951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // regions remaining to transfer
9961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
9971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      unsigned int rx_regions = dev->rx_regions;
9981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
9991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      unsigned int rx_regions = 0;
10001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
10011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (rx_regions) {
10031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
10041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// start a new region
10051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->rx_addr = dev->rx_iovec->iov_base;
10061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rx_bytes = dev->rx_iovec->iov_len;
10071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	++dev->rx_iovec;
10081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->rx_regions = rx_regions - 1;
10091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rx_bytes <= MAX_PIO_COUNT) {
10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  PRINTD (DBG_RX|DBG_BUS, "(pio)");
10121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  pio_instead = 1;
10131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rx_bytes <= MAX_TRANSFER_COUNT) {
10151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  PRINTD (DBG_RX|DBG_BUS, "(full region)");
10161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  dev->rx_bytes = 0;
10171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
10181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  PRINTD (DBG_RX|DBG_BUS, "(start multi region)");
10191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  dev->rx_bytes = rx_bytes - MAX_TRANSFER_COUNT;
10201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  rx_bytes = MAX_TRANSFER_COUNT;
10211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
10231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      } else {
10241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// rx_regions == 0
10251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// that's all folks - end of frame
10261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sk_buff * skb = dev->rx_skb;
10271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// dev->rx_iovec = 0;
10281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	FLUSH_RX_CHANNEL (dev, dev->rx_channel);
10301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dump_skb ("<<<", dev->rx_channel, skb);
10321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_RX|DBG_SKB, "push %p %u", skb->data, skb->len);
10341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{
10361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  struct atm_vcc * vcc = ATM_SKB(skb)->vcc;
10371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  // VC layer stats
10381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  atomic_inc(&vcc->stats->rx);
1039a61bbcf28a8cb0ba56f8193d512f7222e711a294Patrick McHardy	  __net_timestamp(skb);
104025985edcedea6396277003854657b5f3cb31a628Lucas De Marchi	  // end of our responsibility
10411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  vcc->push (vcc, skb);
10421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
10441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
10451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // note: writing RX_COUNT clears any interrupt condition
10471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (rx_bytes) {
10481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (pio_instead) {
10491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (irq)
10501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  wr_regl (dev, MASTER_RX_COUNT_REG_OFF, 0);
10511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rds_regb (dev, DATA_PORT_OFF, dev->rx_addr, rx_bytes);
10521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      } else {
10531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wr_regl (dev, MASTER_RX_ADDR_REG_OFF, virt_to_bus (dev->rx_addr));
10541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wr_regl (dev, MASTER_RX_COUNT_REG_OFF, rx_bytes);
10551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
10561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev->rx_addr += rx_bytes;
10571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    } else {
10581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (irq)
10591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wr_regl (dev, MASTER_RX_COUNT_REG_OFF, 0);
10601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // allow another RX thread to start
10611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      YELLOW_LED_ON(dev);
10621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      clear_bit (rx_busy, &dev->flags);
10631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_RX, "cleared rx_busy for dev %p", dev);
10641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
10651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef TAILRECURSIONWORKS
10671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // and we all bless optimised tail calls
10681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (pio_instead)
10691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      return rx_schedule (dev, 0);
10701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return;
10711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
10721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // grrrrrrr!
10731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    irq = 0;
10741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
10751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
10761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
10771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
10781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** handle RX bus master complete events **********/
10801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10812cf83afe902fd72ef4b211774e48ab39890fb328Denys Vlasenkostatic void rx_bus_master_complete_handler (hrz_dev * dev) {
10821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (test_bit (rx_busy, &dev->flags)) {
10831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rx_schedule (dev, 1);
10841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  } else {
10851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_RX|DBG_ERR, "unexpected RX bus master completion");
10861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // clear interrupt condition on adapter
10871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    wr_regl (dev, MASTER_RX_COUNT_REG_OFF, 0);
10881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
10891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
10901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
10911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** (queue to) become the next TX thread **********/
10931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10942cf83afe902fd72ef4b211774e48ab39890fb328Denys Vlasenkostatic int tx_hold (hrz_dev * dev) {
10951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_TX, "sleeping at tx lock %p %lu", dev, dev->flags);
10961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wait_event_interruptible(dev->tx_queue, (!test_and_set_bit(tx_busy, &dev->flags)));
10971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_TX, "woken at tx lock %p %lu", dev, dev->flags);
10981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (signal_pending (current))
10991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -1;
11001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_TX, "set tx_busy for dev %p", dev);
11011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return 0;
11021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
11031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** allow another TX thread to start **********/
11051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void tx_release (hrz_dev * dev) {
11071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  clear_bit (tx_busy, &dev->flags);
11081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_TX, "cleared tx_busy for dev %p", dev);
11091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wake_up_interruptible (&dev->tx_queue);
11101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
11111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** schedule TX transfers **********/
11131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void tx_schedule (hrz_dev * const dev, int irq) {
11151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned int tx_bytes;
11161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  int append_desc = 0;
11181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  int pio_instead = 0;
11201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef TAILRECURSIONWORKS
11211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  pio_instead = 1;
11221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  while (pio_instead) {
11231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
11241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // bytes in current region waiting for TX transfer
11251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    tx_bytes = dev->tx_bytes;
11261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
11281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    spin_count = 0;
11291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    while (rd_regl (dev, MASTER_TX_COUNT_REG_OFF)) {
11301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_TX|DBG_WARN, "TX error: other PCI Bus Master TX still in progress!");
11311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (++spin_count > 10) {
11321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_TX|DBG_ERR, "spun out waiting PCI Bus Master TX completion");
11331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wr_regl (dev, MASTER_TX_COUNT_REG_OFF, 0);
11341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tx_release (dev);
11351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hrz_kfree_skb (dev->tx_skb);
11361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return;
11371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
11381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
11391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
11401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (tx_bytes) {
11421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // start next transfer within same region
11431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (!test_bit (ultra, &dev->flags) || tx_bytes <= MAX_PIO_COUNT) {
11441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_TX|DBG_BUS, "(pio)");
11451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pio_instead = 1;
11461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
11471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (tx_bytes <= MAX_TRANSFER_COUNT) {
11481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_TX|DBG_BUS, "(simple or last multi)");
11491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dev->tx_iovec) {
11501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  // end of last region
11511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  append_desc = 1;
11521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->tx_bytes = 0;
11541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      } else {
11551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_TX|DBG_BUS, "(continuing multi)");
11561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->tx_bytes = tx_bytes - MAX_TRANSFER_COUNT;
11571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tx_bytes = MAX_TRANSFER_COUNT;
11581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
11591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    } else {
11601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // tx_bytes == 0 -- we're between regions
11611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // regions remaining to transfer
11621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      unsigned int tx_regions = dev->tx_regions;
11631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (tx_regions) {
11651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// start a new region
11661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->tx_addr = dev->tx_iovec->iov_base;
11671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tx_bytes = dev->tx_iovec->iov_len;
11681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	++dev->tx_iovec;
11691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->tx_regions = tx_regions - 1;
11701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!test_bit (ultra, &dev->flags) || tx_bytes <= MAX_PIO_COUNT) {
11721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  PRINTD (DBG_TX|DBG_BUS, "(pio)");
11731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  pio_instead = 1;
11741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tx_bytes <= MAX_TRANSFER_COUNT) {
11761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  PRINTD (DBG_TX|DBG_BUS, "(full region)");
11771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  dev->tx_bytes = 0;
11781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
11791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  PRINTD (DBG_TX|DBG_BUS, "(start multi region)");
11801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  dev->tx_bytes = tx_bytes - MAX_TRANSFER_COUNT;
11811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  tx_bytes = MAX_TRANSFER_COUNT;
11821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
11831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      } else {
11841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// tx_regions == 0
11851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// that's all folks - end of frame
11861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sk_buff * skb = dev->tx_skb;
11871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->tx_iovec = NULL;
11881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// VC layer stats
11901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	atomic_inc(&ATM_SKB(skb)->vcc->stats->tx);
11911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// free the skb
11931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hrz_kfree_skb (skb);
11941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
11951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
11961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
11971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // note: writing TX_COUNT clears any interrupt condition
11981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (tx_bytes) {
11991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (pio_instead) {
12001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (irq)
12011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  wr_regl (dev, MASTER_TX_COUNT_REG_OFF, 0);
12021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wrs_regb (dev, DATA_PORT_OFF, dev->tx_addr, tx_bytes);
12031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (append_desc)
12041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  wr_regl (dev, TX_DESCRIPTOR_PORT_OFF, cpu_to_be32 (dev->tx_skb->len));
12051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      } else {
12061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wr_regl (dev, MASTER_TX_ADDR_REG_OFF, virt_to_bus (dev->tx_addr));
12071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (append_desc)
12081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  wr_regl (dev, TX_DESCRIPTOR_REG_OFF, cpu_to_be32 (dev->tx_skb->len));
12091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wr_regl (dev, MASTER_TX_COUNT_REG_OFF,
12101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 append_desc
12111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 ? tx_bytes | MASTER_TX_AUTO_APPEND_DESC
12121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 : tx_bytes);
12131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
12141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev->tx_addr += tx_bytes;
12151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    } else {
12161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (irq)
12171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wr_regl (dev, MASTER_TX_COUNT_REG_OFF, 0);
12181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      YELLOW_LED_ON(dev);
12191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      tx_release (dev);
12201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
12211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef TAILRECURSIONWORKS
12231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // and we all bless optimised tail calls
12241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (pio_instead)
12251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      return tx_schedule (dev, 0);
12261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return;
12271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
12281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // grrrrrrr!
12291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    irq = 0;
12301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
12311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
12321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
12331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
12341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** handle TX bus master complete events **********/
12361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12372cf83afe902fd72ef4b211774e48ab39890fb328Denys Vlasenkostatic void tx_bus_master_complete_handler (hrz_dev * dev) {
12381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (test_bit (tx_busy, &dev->flags)) {
12391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    tx_schedule (dev, 1);
12401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  } else {
12411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_TX|DBG_ERR, "unexpected TX bus master completion");
12421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // clear interrupt condition on adapter
12431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    wr_regl (dev, MASTER_TX_COUNT_REG_OFF, 0);
12441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
12451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
12461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
12471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** move RX Q pointer to next item in circular buffer **********/
12491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds// called only from IRQ sub-handler
12512cf83afe902fd72ef4b211774e48ab39890fb328Denys Vlasenkostatic u32 rx_queue_entry_next (hrz_dev * dev) {
12521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u32 rx_queue_entry;
12531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  spin_lock (&dev->mem_lock);
12541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  rx_queue_entry = rd_mem (dev, &dev->rx_q_entry->entry);
12551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (dev->rx_q_entry == dev->rx_q_wrap)
12561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev->rx_q_entry = dev->rx_q_reset;
12571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  else
12581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev->rx_q_entry++;
12591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_regw (dev, RX_QUEUE_RD_PTR_OFF, dev->rx_q_entry - dev->rx_q_reset);
12601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  spin_unlock (&dev->mem_lock);
12611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return rx_queue_entry;
12621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
12631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** handle RX disabled by device **********/
12651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void rx_disabled_handler (hrz_dev * dev) {
12671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_regw (dev, RX_CONFIG_OFF, rd_regw (dev, RX_CONFIG_OFF) | RX_ENABLE);
12681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // count me please
12691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTK (KERN_WARNING, "RX was disabled!");
12701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
12711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** handle RX data received by device **********/
12731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds// called from IRQ handler
12752cf83afe902fd72ef4b211774e48ab39890fb328Denys Vlasenkostatic void rx_data_av_handler (hrz_dev * dev) {
12761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u32 rx_queue_entry;
12771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u32 rx_queue_entry_flags;
12781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u16 rx_len;
12791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u16 rx_channel;
12801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW, "hrz_data_av_handler");
12821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // try to grab rx lock (not possible during RX bus mastering)
12841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (test_and_set_bit (rx_busy, &dev->flags)) {
12851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_RX, "locked out of rx lock");
12861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return;
12871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
12881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_RX, "set rx_busy for dev %p", dev);
12891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // lock is cleared if we fail now, o/w after bus master completion
12901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  YELLOW_LED_OFF(dev);
12921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  rx_queue_entry = rx_queue_entry_next (dev);
12941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  rx_len = rx_q_entry_to_length (rx_queue_entry);
12961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  rx_channel = rx_q_entry_to_rx_channel (rx_queue_entry);
12971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
12981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  WAIT_FLUSH_RX_COMPLETE (dev);
12991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  SELECT_RX_CHANNEL (dev, rx_channel);
13011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_RX, "rx_queue_entry is: %#x", rx_queue_entry);
13031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  rx_queue_entry_flags = rx_queue_entry & (RX_CRC_32_OK|RX_COMPLETE_FRAME|SIMONS_DODGEY_MARKER);
13041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (!rx_len) {
13061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // (at least) bus-mastering breaks if we try to handle a
13071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // zero-length frame, besides AAL5 does not support them
13081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTK (KERN_ERR, "zero-length frame!");
13091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rx_queue_entry_flags &= ~RX_COMPLETE_FRAME;
13101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
13111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (rx_queue_entry_flags & SIMONS_DODGEY_MARKER) {
13131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_RX|DBG_ERR, "Simon's marker detected!");
13141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
13151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (rx_queue_entry_flags == (RX_CRC_32_OK | RX_COMPLETE_FRAME)) {
13161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    struct atm_vcc * atm_vcc;
13171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_RX, "got a frame on rx_channel %x len %u", rx_channel, rx_len);
13191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    atm_vcc = dev->rxer[rx_channel];
13211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // if no vcc is assigned to this channel, we should drop the frame
13221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // (is this what SIMONS etc. was trying to achieve?)
13231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (atm_vcc) {
13251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (atm_vcc->qos.rxtp.traffic_class != ATM_NONE) {
13271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rx_len <= atm_vcc->qos.rxtp.max_sdu) {
13291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  struct sk_buff * skb = atm_alloc_charge (atm_vcc, rx_len, GFP_ATOMIC);
13311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  if (skb) {
13321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    // remember this so we can push it later
13331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    dev->rx_skb = skb;
13341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    // remember this so we can flush it later
13351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    dev->rx_channel = rx_channel;
13361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    // prepare socket buffer
13381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    skb_put (skb, rx_len);
13391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    ATM_SKB(skb)->vcc = atm_vcc;
13401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    // simple transfer
13421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    // dev->rx_regions = 0;
13431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    // dev->rx_iovec = 0;
13441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    dev->rx_bytes = rx_len;
13451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    dev->rx_addr = skb->data;
13461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    PRINTD (DBG_RX, "RX start simple transfer (addr %p, len %d)",
13471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    skb->data, rx_len);
13481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    // do the business
13501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    rx_schedule (dev, 0);
13511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    return;
13521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  } else {
13541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    PRINTD (DBG_SKB|DBG_WARN, "failed to get skb");
13551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  }
13561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
13581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  PRINTK (KERN_INFO, "frame received on TX-only VC %x", rx_channel);
13591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  // do we count this?
13601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
13611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      } else {
13631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTK (KERN_WARNING, "dropped over-size frame");
13641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// do we count this?
13651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
13661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    } else {
13681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_WARN|DBG_VCC|DBG_RX, "no VCC for this frame (VC closed)");
13691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // do we count this?
13701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
13711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  } else {
13731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // Wait update complete ? SPONG
13741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
13751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // RX was aborted
13771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  YELLOW_LED_ON(dev);
13781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  FLUSH_RX_CHANNEL (dev,rx_channel);
13801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  clear_bit (rx_busy, &dev->flags);
13811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
13831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
13841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** interrupt handler **********/
13861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
138706efcad0d43a5491602f7d7bfc1ce997cdb0d062Jeff Garzikstatic irqreturn_t interrupt_handler(int irq, void *dev_id)
138806efcad0d43a5491602f7d7bfc1ce997cdb0d062Jeff Garzik{
138906efcad0d43a5491602f7d7bfc1ce997cdb0d062Jeff Garzik  hrz_dev *dev = dev_id;
13901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u32 int_source;
13911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned int irq_ok;
13921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW, "interrupt_handler: %p", dev_id);
13941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
13951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // definitely for us
13961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  irq_ok = 0;
13971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  while ((int_source = rd_regl (dev, INT_SOURCE_REG_OFF)
13981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  & INTERESTING_INTERRUPTS)) {
13992cf83afe902fd72ef4b211774e48ab39890fb328Denys Vlasenko    // In the interests of fairness, the handlers below are
14001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // called in sequence and without immediate return to the head of
14011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // the while loop. This is only of issue for slow hosts (or when
14021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // debugging messages are on). Really slow hosts may find a fast
14031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // sender keeps them permanently in the IRQ handler. :(
14041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // (only an issue for slow hosts) RX completion goes before
14061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // rx_data_av as the former implies rx_busy and so the latter
14071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // would just abort. If it reschedules another transfer
14081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // (continuing the same frame) then it will not clear rx_busy.
14091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // (only an issue for slow hosts) TX completion goes before RX
14111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // data available as it is a much shorter routine - there is the
14121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // chance that any further transfers it schedules will be complete
14131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // by the time of the return to the head of the while loop
14141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (int_source & RX_BUS_MASTER_COMPLETE) {
14161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      ++irq_ok;
14171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_IRQ|DBG_BUS|DBG_RX, "rx_bus_master_complete asserted");
14181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      rx_bus_master_complete_handler (dev);
14191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
14201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (int_source & TX_BUS_MASTER_COMPLETE) {
14211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      ++irq_ok;
14221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_IRQ|DBG_BUS|DBG_TX, "tx_bus_master_complete asserted");
14231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      tx_bus_master_complete_handler (dev);
14241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
14251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (int_source & RX_DATA_AV) {
14261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      ++irq_ok;
14271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_IRQ|DBG_RX, "rx_data_av asserted");
14281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      rx_data_av_handler (dev);
14291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
14301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
14311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (irq_ok) {
14321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_IRQ, "work done: %u", irq_ok);
14331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  } else {
14341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_IRQ|DBG_WARN, "spurious interrupt source: %#x", int_source);
14351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
14361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_IRQ|DBG_FLOW, "interrupt_handler done: %p", dev_id);
14381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (irq_ok)
14391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return IRQ_HANDLED;
14401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return IRQ_NONE;
14411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
14421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** housekeeping **********/
14441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void do_housekeeping (unsigned long arg) {
14461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // just stats at the moment
14471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  hrz_dev * dev = (hrz_dev *) arg;
14481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // collect device-specific (not driver/atm-linux) stats here
14501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  dev->tx_cell_count += rd_regw (dev, TX_CELL_COUNT_OFF);
14511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  dev->rx_cell_count += rd_regw (dev, RX_CELL_COUNT_OFF);
14521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  dev->hec_error_count += rd_regw (dev, HEC_ERROR_COUNT_OFF);
14531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  dev->unassigned_cell_count += rd_regw (dev, UNASSIGNED_CELL_COUNT_OFF);
14541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  mod_timer (&dev->housekeeping, jiffies + HZ/10);
14561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
14581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
14591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** find an idle channel for TX and set it up **********/
14611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds// called with tx_busy set
14632cf83afe902fd72ef4b211774e48ab39890fb328Denys Vlasenkostatic short setup_idle_tx_channel (hrz_dev * dev, hrz_vcc * vcc) {
14641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned short idle_channels;
14651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  short tx_channel = -1;
14661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned int spin_count;
14671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_TX, "setup_idle_tx_channel %p", dev);
14681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // better would be to fail immediately, the caller can then decide whether
14701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // to wait or drop (depending on whether this is UBR etc.)
14711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  spin_count = 0;
14721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  while (!(idle_channels = rd_regw (dev, TX_STATUS_OFF) & IDLE_CHANNELS_MASK)) {
14731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_TX|DBG_WARN, "waiting for idle TX channel");
14741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // delay a bit here
14751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (++spin_count > 100) {
14761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_TX|DBG_ERR, "spun out waiting for idle TX channel");
14771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      return -EBUSY;
14781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
14791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
14801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // got an idle channel
14821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  {
14831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // tx_idle ensures we look for idle channels in RR order
14841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    int chan = dev->tx_idle;
14851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    int keep_going = 1;
14871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    while (keep_going) {
14881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (idle_channels & (1<<chan)) {
14891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tx_channel = chan;
14901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	keep_going = 0;
14911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
14921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      ++chan;
14931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (chan == TX_CHANS)
14941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	chan = 0;
14951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
14961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
14971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev->tx_idle = chan;
14981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
14991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // set up the channel we found
15011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  {
15021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // Initialise the cell header in the transmit channel descriptor
15031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // a.k.a. prepare the channel and remember that we have done so.
15041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    tx_ch_desc * tx_desc = &memmap->tx_descs[tx_channel];
1506b925556cc9e82b32ab68a7620b247f47193501a7Dave Jones    u32 rd_ptr;
1507b925556cc9e82b32ab68a7620b247f47193501a7Dave Jones    u32 wr_ptr;
15081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    u16 channel = vcc->channel;
15091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    unsigned long flags;
15111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    spin_lock_irqsave (&dev->mem_lock, flags);
15121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // Update the transmit channel record.
15141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev->tx_channel_record[tx_channel] = channel;
15151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // xBR channel
15171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    update_tx_channel_config (dev, tx_channel, RATE_TYPE_ACCESS,
15181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			      vcc->tx_xbr_bits);
15191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // Update the PCR counter preload value etc.
15211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    update_tx_channel_config (dev, tx_channel, PCR_TIMER_ACCESS,
15221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			      vcc->tx_pcr_bits);
15231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
15251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (vcc->tx_xbr_bits == VBR_RATE_TYPE) {
15261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // SCR timer
15271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      update_tx_channel_config (dev, tx_channel, SCR_TIMER_ACCESS,
15281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				vcc->tx_scr_bits);
15291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // Bucket size...
15311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      update_tx_channel_config (dev, tx_channel, BUCKET_CAPACITY_ACCESS,
15321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				vcc->tx_bucket_bits);
15331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // ... and fullness
15351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      update_tx_channel_config (dev, tx_channel, BUCKET_FULLNESS_ACCESS,
15361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				vcc->tx_bucket_bits);
15371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
15381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
15391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // Initialise the read and write buffer pointers
15411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rd_ptr = rd_mem (dev, &tx_desc->rd_buf_type) & BUFFER_PTR_MASK;
15421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    wr_ptr = rd_mem (dev, &tx_desc->wr_buf_type) & BUFFER_PTR_MASK;
15431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // idle TX channels should have identical pointers
15451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (rd_ptr != wr_ptr) {
15461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_TX|DBG_ERR, "TX buffer pointers are broken!");
15471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // spin_unlock... return -E...
15481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // I wonder if gcc would get rid of one of the pointer aliases
15491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
15501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_TX, "TX buffer pointers are: rd %x, wr %x.",
15511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    rd_ptr, wr_ptr);
15521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    switch (vcc->aal) {
15541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      case aal0:
15551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_QOS|DBG_TX, "tx_channel: aal0");
15561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rd_ptr |= CHANNEL_TYPE_RAW_CELLS;
15571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wr_ptr |= CHANNEL_TYPE_RAW_CELLS;
15581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
15591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      case aal34:
15601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_QOS|DBG_TX, "tx_channel: aal34");
15611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rd_ptr |= CHANNEL_TYPE_AAL3_4;
15621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wr_ptr |= CHANNEL_TYPE_AAL3_4;
15631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
15641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      case aal5:
15651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rd_ptr |= CHANNEL_TYPE_AAL5;
15661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wr_ptr |= CHANNEL_TYPE_AAL5;
15671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// Initialise the CRC
15681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wr_mem (dev, &tx_desc->partial_crc, INITIAL_CRC);
15691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
15701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
15711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    wr_mem (dev, &tx_desc->rd_buf_type, rd_ptr);
15731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    wr_mem (dev, &tx_desc->wr_buf_type, wr_ptr);
15741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // Write the Cell Header
15761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // Payload Type, CLP and GFC would go here if non-zero
15771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    wr_mem (dev, &tx_desc->cell_header, channel);
15781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    spin_unlock_irqrestore (&dev->mem_lock, flags);
15801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
15811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return tx_channel;
15831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
15841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** send a frame **********/
15861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int hrz_send (struct atm_vcc * atm_vcc, struct sk_buff * skb) {
15881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned int spin_count;
15891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  int free_buffers;
15901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  hrz_dev * dev = HRZ_DEV(atm_vcc->dev);
15911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  hrz_vcc * vcc = HRZ_VCC(atm_vcc);
15921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u16 channel = vcc->channel;
15931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u32 buffers_required;
15951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  /* signed for error return */
15971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  short tx_channel;
15981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
15991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_TX, "hrz_send vc %x data %p len %u",
16001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  channel, skb->data, skb->len);
16011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  dump_skb (">>>", channel, skb);
16031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (atm_vcc->qos.txtp.traffic_class == ATM_NONE) {
16051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTK (KERN_ERR, "attempt to send on RX-only VC %x", channel);
16061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    hrz_kfree_skb (skb);
16071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -EIO;
16081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
16091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // don't understand this
16111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ATM_SKB(skb)->vcc = atm_vcc;
16121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (skb->len > atm_vcc->qos.txtp.max_sdu) {
16141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTK (KERN_ERR, "sk_buff length greater than agreed max_sdu, dropping...");
16151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    hrz_kfree_skb (skb);
16161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -EIO;
16171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
16181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (!channel) {
16201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_ERR|DBG_TX, "attempt to transmit on zero (rx_)channel");
16211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    hrz_kfree_skb (skb);
16221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -EIO;
16231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
16241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
16261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  {
16271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // where would be a better place for this? housekeeping?
16281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    u16 status;
16291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    pci_read_config_word (dev->pci_dev, PCI_STATUS, &status);
16301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (status & PCI_STATUS_REC_MASTER_ABORT) {
16311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_BUS|DBG_ERR, "Clearing PCI Master Abort (and cleaning up)");
16321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      status &= ~PCI_STATUS_REC_MASTER_ABORT;
16331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      pci_write_config_word (dev->pci_dev, PCI_STATUS, status);
16341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (test_bit (tx_busy, &dev->flags)) {
16351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hrz_kfree_skb (dev->tx_skb);
16361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tx_release (dev);
16371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
16381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
16391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
16401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
16411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_HORIZON
16431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  /* wey-hey! */
16441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (channel == 1023) {
16451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    unsigned int i;
16461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    unsigned short d = 0;
16471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    char * s = skb->data;
16481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (*s++ == 'D') {
164966bb16de6b9a05936d1eeb20155bab008b476191Andy Shevchenko	for (i = 0; i < 4; ++i)
165066bb16de6b9a05936d1eeb20155bab008b476191Andy Shevchenko		d = (d << 4) | hex_to_bin(*s++);
16511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTK (KERN_INFO, "debug bitmap is now %hx", debug = d);
16521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
16531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
16541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
16551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // wait until TX is free and grab lock
16571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (tx_hold (dev)) {
16581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    hrz_kfree_skb (skb);
16591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -ERESTARTSYS;
16601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
16611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // Wait for enough space to be available in transmit buffer memory.
16631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // should be number of cells needed + 2 (according to hardware docs)
16651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // = ((framelen+8)+47) / 48 + 2
16661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // = (framelen+7) / 48 + 3, hmm... faster to put addition inside XXX
16671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  buffers_required = (skb->len+(ATM_AAL5_TRAILER-1)) / ATM_CELL_PAYLOAD + 3;
16681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // replace with timer and sleep, add dev->tx_buffers_queue (max 1 entry)
16701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  spin_count = 0;
16711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  while ((free_buffers = rd_regw (dev, TX_FREE_BUFFER_COUNT_OFF)) < buffers_required) {
16721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_TX, "waiting for free TX buffers, got %d of %d",
16731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    free_buffers, buffers_required);
16741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // what is the appropriate delay? implement a timeout? (depending on line speed?)
16751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // mdelay (1);
16761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // what happens if we kill (current_pid, SIGKILL) ?
16771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    schedule();
16781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (++spin_count > 1000) {
16791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_TX|DBG_ERR, "spun out waiting for tx buffers, got %d of %d",
16801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	      free_buffers, buffers_required);
16811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      tx_release (dev);
16821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      hrz_kfree_skb (skb);
16831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      return -ERESTARTSYS;
16841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
16851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
16861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
16871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // Select a channel to transmit the frame on.
16881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (channel == dev->last_vc) {
16891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_TX, "last vc hack: hit");
16901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    tx_channel = dev->tx_last;
16911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  } else {
16921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_TX, "last vc hack: miss");
16931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // Are we currently transmitting this VC on one of the channels?
16941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    for (tx_channel = 0; tx_channel < TX_CHANS; ++tx_channel)
16951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (dev->tx_channel_record[tx_channel] == channel) {
16961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_TX, "vc already on channel: hit");
16971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
16981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
16991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (tx_channel == TX_CHANS) {
17001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_TX, "vc already on channel: miss");
17011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // Find and set up an idle channel.
17021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      tx_channel = setup_idle_tx_channel (dev, vcc);
17031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (tx_channel < 0) {
17041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_TX|DBG_ERR, "failed to get channel");
17051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tx_release (dev);
17061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return tx_channel;
17071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
17081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
17091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_TX, "got channel");
17111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    SELECT_TX_CHANNEL(dev, tx_channel);
17121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev->last_vc = channel;
17141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev->tx_last = tx_channel;
17151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
17161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_TX, "using channel %u", tx_channel);
17181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  YELLOW_LED_OFF(dev);
17201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // TX start transfer
17221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  {
17241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    unsigned int tx_len = skb->len;
17251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    unsigned int tx_iovcnt = skb_shinfo(skb)->nr_frags;
17261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // remember this so we can free it later
17271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev->tx_skb = skb;
17281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (tx_iovcnt) {
17301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // scatter gather transfer
17311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev->tx_regions = tx_iovcnt;
17321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev->tx_iovec = NULL;		/* @@@ needs rewritten */
17331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev->tx_bytes = 0;
17341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_TX|DBG_BUS, "TX start scatter-gather transfer (iovec %p, len %d)",
17351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	      skb->data, tx_len);
17361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      tx_release (dev);
17371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      hrz_kfree_skb (skb);
17381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      return -EIO;
17391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    } else {
17401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // simple transfer
17411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev->tx_regions = 0;
17421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev->tx_iovec = NULL;
17431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev->tx_bytes = tx_len;
17441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev->tx_addr = skb->data;
17451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_TX|DBG_BUS, "TX start simple transfer (addr %p, len %d)",
17461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	      skb->data, tx_len);
17471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
17481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // and do the business
17501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    tx_schedule (dev, 0);
17511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
17531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return 0;
17551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
17561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** reset a card **********/
17581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void hrz_reset (const hrz_dev * dev) {
17601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u32 control_0_reg = rd_regl (dev, CONTROL_0_REG);
17611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // why not set RESET_HORIZON to one and wait for the card to
17631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // reassert that bit as zero? Like so:
17641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  control_0_reg = control_0_reg & RESET_HORIZON;
17651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_regl (dev, CONTROL_0_REG, control_0_reg);
17661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  while (control_0_reg & RESET_HORIZON)
17671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    control_0_reg = rd_regl (dev, CONTROL_0_REG);
17681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // old reset code retained:
17701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_regl (dev, CONTROL_0_REG, control_0_reg |
17711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   RESET_ATM | RESET_RX | RESET_TX | RESET_HOST);
17721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // just guessing here
17731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  udelay (1000);
17741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_regl (dev, CONTROL_0_REG, control_0_reg);
17761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
17771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** read the burnt in address **********/
17791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17802cf83afe902fd72ef4b211774e48ab39890fb328Denys Vlasenkostatic void WRITE_IT_WAIT (const hrz_dev *dev, u32 ctrl)
17811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
17821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wr_regl (dev, CONTROL_0_REG, ctrl);
17831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	udelay (5);
17841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
17851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17862cf83afe902fd72ef4b211774e48ab39890fb328Denys Vlasenkostatic void CLOCK_IT (const hrz_dev *dev, u32 ctrl)
17871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
17881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// DI must be valid around rising SK edge
17891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	WRITE_IT_WAIT(dev, ctrl & ~SEEPROM_SK);
17901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	WRITE_IT_WAIT(dev, ctrl | SEEPROM_SK);
17911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
17921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1793977a415f2b70b5693aaa23b1a16ad57ea20a1dceDavid S. Millerstatic u16 __devinit read_bia (const hrz_dev * dev, u16 addr)
17941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
17951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u32 ctrl = rd_regl (dev, CONTROL_0_REG);
17961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
17971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  const unsigned int addr_bits = 6;
17981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  const unsigned int data_bits = 16;
17991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  unsigned int i;
18011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u16 res;
18031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ctrl &= ~(SEEPROM_CS | SEEPROM_SK | SEEPROM_DI);
18051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  WRITE_IT_WAIT(dev, ctrl);
18061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // wake Serial EEPROM and send 110 (READ) command
18081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ctrl |=  (SEEPROM_CS | SEEPROM_DI);
18091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  CLOCK_IT(dev, ctrl);
18101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ctrl |= SEEPROM_DI;
18121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  CLOCK_IT(dev, ctrl);
18131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ctrl &= ~SEEPROM_DI;
18151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  CLOCK_IT(dev, ctrl);
18161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  for (i=0; i<addr_bits; i++) {
18181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (addr & (1 << (addr_bits-1)))
18191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      ctrl |= SEEPROM_DI;
18201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    else
18211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      ctrl &= ~SEEPROM_DI;
18221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    CLOCK_IT(dev, ctrl);
18241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    addr = addr << 1;
18261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
18271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // we could check that we have DO = 0 here
18291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ctrl &= ~SEEPROM_DI;
18301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  res = 0;
18321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  for (i=0;i<data_bits;i++) {
18331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    res = res >> 1;
18341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    CLOCK_IT(dev, ctrl);
18361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (rd_regl (dev, CONTROL_0_REG) & SEEPROM_DO)
18381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      res |= (1 << (data_bits-1));
18391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
18401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ctrl &= ~(SEEPROM_SK | SEEPROM_CS);
18421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  WRITE_IT_WAIT(dev, ctrl);
18431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return res;
18451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
18461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** initialise a card **********/
18481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18490a3c4bdc1b197a7d37fc75643a68daf45fe0a7ccAl Virostatic int __devinit hrz_init (hrz_dev * dev) {
18501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  int onefivefive;
18511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u16 chan;
18531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  int buff_count;
18551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  HDW * mem;
18571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  cell_buf * tx_desc;
18591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  cell_buf * rx_desc;
18601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u32 ctrl;
18621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ctrl = rd_regl (dev, CONTROL_0_REG);
18641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_INFO, "ctrl0reg is %#x", ctrl);
18651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  onefivefive = ctrl & ATM_LAYER_STATUS;
18661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (onefivefive)
18681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    printk (DEV_LABEL ": Horizon Ultra (at 155.52 MBps)");
18691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  else
18701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    printk (DEV_LABEL ": Horizon (at 25 MBps)");
18711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  printk (":");
18731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // Reset the card to get everything in a known state
18741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  printk (" reset");
18761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  hrz_reset (dev);
18771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // Clear all the buffer memory
18791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  printk (" clearing memory");
18811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  for (mem = (HDW *) memmap; mem < (HDW *) (memmap + 1); ++mem)
18831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    wr_mem (dev, mem, 0);
18841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  printk (" tx channels");
18861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // All transmit eight channels are set up as AAL5 ABR channels with
18881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // a 16us cell spacing. Why?
18891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // Channel 0 gets the free buffer at 100h, channel 1 gets the free
18911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // buffer at 110h etc.
18921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  for (chan = 0; chan < TX_CHANS; ++chan) {
18941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    tx_ch_desc * tx_desc = &memmap->tx_descs[chan];
18951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    cell_buf * buf = &memmap->inittxbufs[chan];
18961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
18971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // initialise the read and write buffer pointers
18981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    wr_mem (dev, &tx_desc->rd_buf_type, BUF_PTR(buf));
18991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    wr_mem (dev, &tx_desc->wr_buf_type, BUF_PTR(buf));
19001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // set the status of the initial buffers to empty
19021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    wr_mem (dev, &buf->next, BUFF_STATUS_EMPTY);
19031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
19041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // Use space bufn3 at the moment for tx buffers
19061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  printk (" tx buffers");
19081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  tx_desc = memmap->bufn3;
19101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_mem (dev, &memmap->txfreebufstart.next, BUF_PTR(tx_desc) | BUFF_STATUS_EMPTY);
19121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  for (buff_count = 0; buff_count < BUFN3_SIZE-1; buff_count++) {
19141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    wr_mem (dev, &tx_desc->next, BUF_PTR(tx_desc+1) | BUFF_STATUS_EMPTY);
19151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    tx_desc++;
19161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
19171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_mem (dev, &tx_desc->next, BUF_PTR(&memmap->txfreebufend) | BUFF_STATUS_EMPTY);
19191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // Initialise the transmit free buffer count
19211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_regw (dev, TX_FREE_BUFFER_COUNT_OFF, BUFN3_SIZE);
19221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  printk (" rx channels");
19241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // Initialise all of the receive channels to be AAL5 disabled with
19261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // an interrupt threshold of 0
19271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  for (chan = 0; chan < RX_CHANS; ++chan) {
19291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rx_ch_desc * rx_desc = &memmap->rx_descs[chan];
19301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    wr_mem (dev, &rx_desc->wr_buf_type, CHANNEL_TYPE_AAL5 | RX_CHANNEL_DISABLED);
19321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
19331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  printk (" rx buffers");
19351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // Use space bufn4 at the moment for rx buffers
19371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  rx_desc = memmap->bufn4;
19391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_mem (dev, &memmap->rxfreebufstart.next, BUF_PTR(rx_desc) | BUFF_STATUS_EMPTY);
19411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  for (buff_count = 0; buff_count < BUFN4_SIZE-1; buff_count++) {
19431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    wr_mem (dev, &rx_desc->next, BUF_PTR(rx_desc+1) | BUFF_STATUS_EMPTY);
19441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    rx_desc++;
19461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
19471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_mem (dev, &rx_desc->next, BUF_PTR(&memmap->rxfreebufend) | BUFF_STATUS_EMPTY);
19491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // Initialise the receive free buffer count
19511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_regw (dev, RX_FREE_BUFFER_COUNT_OFF, BUFN4_SIZE);
19521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // Initialize Horizons registers
19541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // TX config
19561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_regw (dev, TX_CONFIG_OFF,
19571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   ABR_ROUND_ROBIN | TX_NORMAL_OPERATION | DRVR_DRVRBAR_ENABLE);
19581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // RX config. Use 10-x VC bits, x VP bits, non user cells in channel 0.
19601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_regw (dev, RX_CONFIG_OFF,
19611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   DISCARD_UNUSED_VPI_VCI_BITS_SET | NON_USER_CELLS_IN_ONE_CHANNEL | vpi_bits);
19621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // RX line config
19641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_regw (dev, RX_LINE_CONFIG_OFF,
19651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   LOCK_DETECT_ENABLE | FREQUENCY_DETECT_ENABLE | GXTALOUT_SELECT_DIV4);
19661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // Set the max AAL5 cell count to be just enough to contain the
19681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // largest AAL5 frame that the user wants to receive
19691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_regw (dev, MAX_AAL5_CELL_COUNT_OFF,
19706a19309db0a02d821494f4df754046c85a230627Julia Lawall	   DIV_ROUND_UP(max_rx_size + ATM_AAL5_TRAILER, ATM_CELL_PAYLOAD));
19711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // Enable receive
19731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_regw (dev, RX_CONFIG_OFF, rd_regw (dev, RX_CONFIG_OFF) | RX_ENABLE);
19741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  printk (" control");
19761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // Drive the OE of the LEDs then turn the green LED on
19781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  ctrl |= GREEN_LED_OE | YELLOW_LED_OE | GREEN_LED | YELLOW_LED;
19791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_regl (dev, CONTROL_0_REG, ctrl);
19801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // Test for a 155-capable card
19821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (onefivefive) {
19841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // Select 155 mode... make this a choice (or: how do we detect
19851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // external line speed and switch?)
19861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    ctrl |= ATM_LAYER_SELECT;
19871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    wr_regl (dev, CONTROL_0_REG, ctrl);
19881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // test SUNI-lite vs SAMBA
19901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // Register 0x00 in the SUNI will have some of bits 3-7 set, and
19921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // they will always be zero for the SAMBA.  Ha!  Bloody hardware
19931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // engineers.  It'll never work.
19941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (rd_framer (dev, 0) & 0x00f0) {
19961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // SUNI
19971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      printk (" SUNI");
19981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
19991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // Reset, just in case
20001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      wr_framer (dev, 0x00, 0x0080);
20011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      wr_framer (dev, 0x00, 0x0000);
20021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // Configure transmit FIFO
20041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      wr_framer (dev, 0x63, rd_framer (dev, 0x63) | 0x0002);
20051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // Set line timed mode
20071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      wr_framer (dev, 0x05, rd_framer (dev, 0x05) | 0x0001);
20081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    } else {
20091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // SAMBA
20101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      printk (" SAMBA");
20111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // Reset, just in case
20131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      wr_framer (dev, 0, rd_framer (dev, 0) | 0x0001);
20141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      wr_framer (dev, 0, rd_framer (dev, 0) &~ 0x0001);
20151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // Turn off diagnostic loopback and enable line-timed mode
20171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      wr_framer (dev, 0, 0x0002);
20181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // Turn on transmit outputs
20201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      wr_framer (dev, 2, 0x0B80);
20211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
20221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  } else {
20231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // Select 25 mode
20241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    ctrl &= ~ATM_LAYER_SELECT;
20251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // Madge B154 setup
20271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // none required?
20281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
20291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  printk (" LEDs");
20311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  GREEN_LED_ON(dev);
20331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  YELLOW_LED_ON(dev);
20341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  printk (" ESI=");
20361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  {
20381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    u16 b = 0;
20391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    int i;
20401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    u8 * esi = dev->atm_dev->esi;
20411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // in the card I have, EEPROM
20431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // addresses 0, 1, 2 contain 0
20441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // addresess 5, 6 etc. contain ffff
20451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // NB: Madge prefix is 00 00 f6 (which is 00 00 6f in Ethernet bit order)
20461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // the read_bia routine gets the BIA in Ethernet bit order
20471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    for (i=0; i < ESI_LEN; ++i) {
20491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (i % 2 == 0)
20501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	b = read_bia (dev, i/2 + 2);
20511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      else
20521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	b = b >> 8;
20531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      esi[i] = b & 0xFF;
20541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      printk ("%02x", esi[i]);
20551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
20561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
20571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // Enable RX_Q and ?X_COMPLETE interrupts only
20591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  wr_regl (dev, INT_ENABLE_REG_OFF, INTERESTING_INTERRUPTS);
20601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  printk (" IRQ on");
20611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  printk (".\n");
20631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return onefivefive;
20651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
20661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** check max_sdu **********/
20681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int check_max_sdu (hrz_aal aal, struct atm_trafprm * tp, unsigned int max_frame_size) {
20701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_QOS, "check_max_sdu");
20711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  switch (aal) {
20731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    case aal0:
20741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (!(tp->max_sdu)) {
20751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_QOS, "defaulting max_sdu");
20761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tp->max_sdu = ATM_AAL0_SDU;
20771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      } else if (tp->max_sdu != ATM_AAL0_SDU) {
20781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_QOS|DBG_ERR, "rejecting max_sdu");
20791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -EINVAL;
20801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
20811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      break;
20821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    case aal34:
20831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (tp->max_sdu == 0 || tp->max_sdu > ATM_MAX_AAL34_PDU) {
20841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_QOS, "%sing max_sdu", tp->max_sdu ? "capp" : "default");
20851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tp->max_sdu = ATM_MAX_AAL34_PDU;
20861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
20871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      break;
20881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    case aal5:
20891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (tp->max_sdu == 0 || tp->max_sdu > max_frame_size) {
20901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_QOS, "%sing max_sdu", tp->max_sdu ? "capp" : "default");
20911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tp->max_sdu = max_frame_size;
20921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
20931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      break;
20941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
20951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return 0;
20961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
20971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
20981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** check pcr **********/
20991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds// something like this should be part of ATM Linux
21011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int atm_pcr_check (struct atm_trafprm * tp, unsigned int pcr) {
21021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // we are assuming non-UBR, and non-special values of pcr
21031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (tp->min_pcr == ATM_MAX_PCR)
21041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_QOS, "luser gave min_pcr = ATM_MAX_PCR");
21051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  else if (tp->min_pcr < 0)
21061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_QOS, "luser gave negative min_pcr");
21071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  else if (tp->min_pcr && tp->min_pcr > pcr)
21081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_QOS, "pcr less than min_pcr");
21091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  else
21101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // !! max_pcr = UNSPEC (0) is equivalent to max_pcr = MAX (-1)
21111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // easier to #define ATM_MAX_PCR 0 and have all rates unsigned?
21121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // [this would get rid of next two conditionals]
21131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if ((0) && tp->max_pcr == ATM_MAX_PCR)
21141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_QOS, "luser gave max_pcr = ATM_MAX_PCR");
21151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    else if ((tp->max_pcr != ATM_MAX_PCR) && tp->max_pcr < 0)
21161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_QOS, "luser gave negative max_pcr");
21171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    else if (tp->max_pcr && tp->max_pcr != ATM_MAX_PCR && tp->max_pcr < pcr)
21181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_QOS, "pcr greater than max_pcr");
21191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    else {
21201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // each limit unspecified or not violated
21211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_QOS, "xBR(pcr) OK");
21221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      return 0;
21231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
21241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_QOS, "pcr=%u, tp: min_pcr=%d, pcr=%d, max_pcr=%d",
21251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  pcr, tp->min_pcr, tp->pcr, tp->max_pcr);
21261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return -EINVAL;
21271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
21281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** open VC **********/
21301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int hrz_open (struct atm_vcc *atm_vcc)
21321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
21331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  int error;
21341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u16 channel;
21351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  struct atm_qos * qos;
21371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  struct atm_trafprm * txtp;
21381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  struct atm_trafprm * rxtp;
21391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  hrz_dev * dev = HRZ_DEV(atm_vcc->dev);
21411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  hrz_vcc vcc;
21421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  hrz_vcc * vccp; // allocated late
21431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  short vpi = atm_vcc->vpi;
21441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  int vci = atm_vcc->vci;
21451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_VCC, "hrz_open %x %x", vpi, vci);
21461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef ATM_VPI_UNSPEC
21481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // UNSPEC is deprecated, remove this code eventually
21491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (vpi == ATM_VPI_UNSPEC || vci == ATM_VCI_UNSPEC) {
21501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTK (KERN_WARNING, "rejecting open with unspecified VPI/VCI (deprecated)");
21511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -EINVAL;
21521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
21531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
21541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  error = vpivci_to_channel (&channel, vpi, vci);
21561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (error) {
21571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_WARN|DBG_VCC, "VPI/VCI out of range: %hd/%d", vpi, vci);
21581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return error;
21591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
21601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  vcc.channel = channel;
21621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // max speed for the moment
21631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  vcc.tx_rate = 0x0;
21641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  qos = &atm_vcc->qos;
21661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // check AAL and remember it
21681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  switch (qos->aal) {
21691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    case ATM_AAL0:
21701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // we would if it were 48 bytes and not 52!
21711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_QOS|DBG_VCC, "AAL0");
21721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      vcc.aal = aal0;
21731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      break;
21741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    case ATM_AAL34:
21751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      // we would if I knew how do the SAR!
21761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_QOS|DBG_VCC, "AAL3/4");
21771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      vcc.aal = aal34;
21781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      break;
21791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    case ATM_AAL5:
21801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_QOS|DBG_VCC, "AAL5");
21811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      vcc.aal = aal5;
21821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      break;
21831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    default:
21841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_QOS|DBG_VCC, "Bad AAL!");
21851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      return -EINVAL;
21861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      break;
21871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
21881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // TX traffic parameters
21901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // there are two, interrelated problems here: 1. the reservation of
21921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // PCR is not a binary choice, we are given bounds and/or a
21931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // desirable value; 2. the device is only capable of certain values,
21941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // most of which are not integers. It is almost certainly acceptable
21951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // to be off by a maximum of 1 to 10 cps.
21961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // Pragmatic choice: always store an integral PCR as that which has
21981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // been allocated, even if we allocate a little (or a lot) less,
21991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // after rounding. The actual allocation depends on what we can
22001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // manage with our rate selection algorithm. The rate selection
22011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // algorithm is given an integral PCR and a tolerance and told
22021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // whether it should round the value up or down if the tolerance is
22031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // exceeded; it returns: a) the actual rate selected (rounded up to
22041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // the nearest integer), b) a bit pattern to feed to the timer
22051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // register, and c) a failure value if no applicable rate exists.
22061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // Part of the job is done by atm_pcr_goal which gives us a PCR
22081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // specification which says: EITHER grab the maximum available PCR
22091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // (and perhaps a lower bound which we musn't pass), OR grab this
22101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // amount, rounding down if you have to (and perhaps a lower bound
22111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // which we musn't pass) OR grab this amount, rounding up if you
22121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // have to (and perhaps an upper bound which we musn't pass). If any
22131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // bounds ARE passed we fail. Note that rounding is only rounding to
22141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // match device limitations, we do not round down to satisfy
22151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // bandwidth availability even if this would not violate any given
22161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // lower bound.
22171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // Note: telephony = 64kb/s = 48 byte cell payload @ 500/3 cells/s
22191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // (say) so this is not even a binary fixpoint cell rate (but this
22201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // device can do it). To avoid this sort of hassle we use a
22211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // tolerance parameter (currently fixed at 10 cps).
22221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_QOS, "TX:");
22241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  txtp = &qos->txtp;
22261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // set up defaults for no traffic
22281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  vcc.tx_rate = 0;
22291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // who knows what would actually happen if you try and send on this?
22301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  vcc.tx_xbr_bits = IDLE_RATE_TYPE;
22311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  vcc.tx_pcr_bits = CLOCK_DISABLE;
22321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
22331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  vcc.tx_scr_bits = CLOCK_DISABLE;
22341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  vcc.tx_bucket_bits = 0;
22351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
22361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (txtp->traffic_class != ATM_NONE) {
22381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    error = check_max_sdu (vcc.aal, txtp, max_tx_size);
22391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (error) {
22401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_QOS, "TX max_sdu check failed");
22411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      return error;
22421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
22431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
22441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    switch (txtp->traffic_class) {
22451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      case ATM_UBR: {
22461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// we take "the PCR" as a rate-cap
22471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// not reserved
22481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vcc.tx_rate = 0;
22491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	make_rate (dev, 1<<30, round_nearest, &vcc.tx_pcr_bits, NULL);
22501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vcc.tx_xbr_bits = ABR_RATE_TYPE;
22511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
22521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
22531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
22541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      case ATM_ABR: {
22551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// reserve min, allow up to max
22561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vcc.tx_rate = 0; // ?
22571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	make_rate (dev, 1<<30, round_nearest, &vcc.tx_pcr_bits, 0);
22581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vcc.tx_xbr_bits = ABR_RATE_TYPE;
22591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
22601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
22611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
22621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      case ATM_CBR: {
22631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int pcr = atm_pcr_goal (txtp);
22641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rounding r;
22651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!pcr) {
22661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  // down vs. up, remaining bandwidth vs. unlimited bandwidth!!
22671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  // should really have: once someone gets unlimited bandwidth
22681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  // that no more non-UBR channels can be opened until the
22691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  // unlimited one closes?? For the moment, round_down means
22701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  // greedy people actually get something and not nothing
22711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  r = round_down;
22721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  // slight race (no locking) here so we may get -EAGAIN
22731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  // later; the greedy bastards would deserve it :)
22741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  PRINTD (DBG_QOS, "snatching all remaining TX bandwidth");
22751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  pcr = dev->tx_avail;
22761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else if (pcr < 0) {
22771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  r = round_down;
22781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  pcr = -pcr;
22791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
22801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  r = round_up;
22811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
22821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	error = make_rate_with_tolerance (dev, pcr, r, 10,
22831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					  &vcc.tx_pcr_bits, &vcc.tx_rate);
22841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (error) {
22851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  PRINTD (DBG_QOS, "could not make rate from TX PCR");
22861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  return error;
22871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
22881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// not really clear what further checking is needed
22891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	error = atm_pcr_check (txtp, vcc.tx_rate);
22901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (error) {
22911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  PRINTD (DBG_QOS, "TX PCR failed consistency check");
22921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  return error;
22931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
22941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vcc.tx_xbr_bits = CBR_RATE_TYPE;
22951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
22961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
22971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
22981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      case ATM_VBR: {
22991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int pcr = atm_pcr_goal (txtp);
23001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// int scr = atm_scr_goal (txtp);
23011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int scr = pcr/2; // just for fun
23021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int mbs = 60; // just for fun
23031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rounding pr;
23041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rounding sr;
23051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int bucket;
23061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!pcr) {
23071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  pr = round_nearest;
23081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  pcr = 1<<30;
23091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else if (pcr < 0) {
23101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  pr = round_down;
23111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  pcr = -pcr;
23121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
23131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  pr = round_up;
23141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
23151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	error = make_rate_with_tolerance (dev, pcr, pr, 10,
23161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					  &vcc.tx_pcr_bits, 0);
23171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!scr) {
23181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  // see comments for PCR with CBR above
23191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  sr = round_down;
23201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  // slight race (no locking) here so we may get -EAGAIN
23211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  // later; the greedy bastards would deserve it :)
23221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  PRINTD (DBG_QOS, "snatching all remaining TX bandwidth");
23231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  scr = dev->tx_avail;
23241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else if (scr < 0) {
23251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  sr = round_down;
23261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  scr = -scr;
23271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
23281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  sr = round_up;
23291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
23301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	error = make_rate_with_tolerance (dev, scr, sr, 10,
23311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					  &vcc.tx_scr_bits, &vcc.tx_rate);
23321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (error) {
23331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  PRINTD (DBG_QOS, "could not make rate from TX SCR");
23341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  return error;
23351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
23361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// not really clear what further checking is needed
23371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// error = atm_scr_check (txtp, vcc.tx_rate);
23381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (error) {
23391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  PRINTD (DBG_QOS, "TX SCR failed consistency check");
23401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  return error;
23411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
23421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// bucket calculations (from a piece of paper...) cell bucket
23431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// capacity must be largest integer smaller than m(p-s)/p + 1
23441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// where m = max burst size, p = pcr, s = scr
23451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	bucket = mbs*(pcr-scr)/pcr;
23461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (bucket*pcr != mbs*(pcr-scr))
23471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  bucket += 1;
23481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (bucket > BUCKET_MAX_SIZE) {
23491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  PRINTD (DBG_QOS, "shrinking bucket from %u to %u",
23501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		  bucket, BUCKET_MAX_SIZE);
23511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  bucket = BUCKET_MAX_SIZE;
23521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
23531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vcc.tx_xbr_bits = VBR_RATE_TYPE;
23541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vcc.tx_bucket_bits = bucket;
23551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
23561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
23571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
23581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      default: {
23591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_QOS, "unsupported TX traffic class");
23601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -EINVAL;
23611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
23621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
23631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
23641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
23651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // RX traffic parameters
23671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_QOS, "RX:");
23691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  rxtp = &qos->rxtp;
23711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // set up defaults for no traffic
23731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  vcc.rx_rate = 0;
23741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (rxtp->traffic_class != ATM_NONE) {
23761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    error = check_max_sdu (vcc.aal, rxtp, max_rx_size);
23771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (error) {
23781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_QOS, "RX max_sdu check failed");
23791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      return error;
23801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
23811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    switch (rxtp->traffic_class) {
23821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      case ATM_UBR: {
23831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// not reserved
23841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
23851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
23861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
23871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      case ATM_ABR: {
23881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// reserve min
23891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vcc.rx_rate = 0; // ?
23901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
23911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
23921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
23931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      case ATM_CBR: {
23941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int pcr = atm_pcr_goal (rxtp);
23951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!pcr) {
23961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  // slight race (no locking) here so we may get -EAGAIN
23971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  // later; the greedy bastards would deserve it :)
23981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  PRINTD (DBG_QOS, "snatching all remaining RX bandwidth");
23991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  pcr = dev->rx_avail;
24001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else if (pcr < 0) {
24011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  pcr = -pcr;
24021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
24031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vcc.rx_rate = pcr;
24041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// not really clear what further checking is needed
24051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	error = atm_pcr_check (rxtp, vcc.rx_rate);
24061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (error) {
24071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  PRINTD (DBG_QOS, "RX PCR failed consistency check");
24081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  return error;
24091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
24101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
24111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
24121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
24131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      case ATM_VBR: {
24141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// int scr = atm_scr_goal (rxtp);
24151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int scr = 1<<16; // just for fun
24161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!scr) {
24171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  // slight race (no locking) here so we may get -EAGAIN
24181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  // later; the greedy bastards would deserve it :)
24191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  PRINTD (DBG_QOS, "snatching all remaining RX bandwidth");
24201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  scr = dev->rx_avail;
24211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else if (scr < 0) {
24221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  scr = -scr;
24231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
24241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	vcc.rx_rate = scr;
24251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// not really clear what further checking is needed
24261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// error = atm_scr_check (rxtp, vcc.rx_rate);
24271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (error) {
24281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  PRINTD (DBG_QOS, "RX SCR failed consistency check");
24291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  return error;
24301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
24311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
24321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
24331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
24341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      default: {
24351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_QOS, "unsupported RX traffic class");
24361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -EINVAL;
24371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
24381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
24391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
24401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
24411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // late abort useful for diagnostics
24441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (vcc.aal != aal5) {
24451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_QOS, "AAL not supported");
24461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -EINVAL;
24471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
24481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // get space for our vcc stuff and copy parameters into it
24501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  vccp = kmalloc (sizeof(hrz_vcc), GFP_KERNEL);
24511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (!vccp) {
24521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTK (KERN_ERR, "out of memory!");
24531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -ENOMEM;
24541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
24551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  *vccp = vcc;
24561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // clear error and grab cell rate resource lock
24581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  error = 0;
24591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  spin_lock (&dev->rate_lock);
24601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (vcc.tx_rate > dev->tx_avail) {
24621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_QOS, "not enough TX PCR left");
24631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    error = -EAGAIN;
24641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
24651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (vcc.rx_rate > dev->rx_avail) {
24671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_QOS, "not enough RX PCR left");
24681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    error = -EAGAIN;
24691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
24701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (!error) {
24721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // really consume cell rates
24731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev->tx_avail -= vcc.tx_rate;
24741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev->rx_avail -= vcc.rx_rate;
24751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_QOS|DBG_VCC, "reserving %u TX PCR and %u RX PCR",
24761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    vcc.tx_rate, vcc.rx_rate);
24771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
24781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // release lock and exit on error
24801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  spin_unlock (&dev->rate_lock);
24811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (error) {
24821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTD (DBG_QOS|DBG_VCC, "insufficient cell rate resources");
24831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    kfree (vccp);
24841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return error;
24851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
24861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // this is "immediately before allocating the connection identifier
24881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // in hardware" - so long as the next call does not fail :)
24891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  set_bit(ATM_VF_ADDR,&atm_vcc->flags);
24901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // any errors here are very serious and should never occur
24921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (rxtp->traffic_class != ATM_NONE) {
24941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (dev->rxer[channel]) {
24951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTD (DBG_ERR|DBG_VCC, "VC already open for RX");
24961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      error = -EBUSY;
24971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
24981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (!error)
24991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      error = hrz_open_rx (dev, channel);
25001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (error) {
25011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      kfree (vccp);
25021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      return error;
25031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    }
25041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // this link allows RX frames through
25051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev->rxer[channel] = atm_vcc;
25061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
25071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
25081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // success, set elements of atm_vcc
25091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  atm_vcc->dev_data = (void *) vccp;
25101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
25111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // indicate readiness
25121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  set_bit(ATM_VF_READY,&atm_vcc->flags);
25131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
25141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return 0;
25151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
25161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
25171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** close VC **********/
25181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
25191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void hrz_close (struct atm_vcc * atm_vcc) {
25201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  hrz_dev * dev = HRZ_DEV(atm_vcc->dev);
25211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  hrz_vcc * vcc = HRZ_VCC(atm_vcc);
25221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  u16 channel = vcc->channel;
25231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_VCC|DBG_FLOW, "hrz_close");
25241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
25251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // indicate unreadiness
25261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  clear_bit(ATM_VF_READY,&atm_vcc->flags);
25271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
25281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (atm_vcc->qos.txtp.traffic_class != ATM_NONE) {
25291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    unsigned int i;
25301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
25311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // let any TX on this channel that has started complete
25321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // no restart, just keep trying
25331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    while (tx_hold (dev))
25341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      ;
25351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // remove record of any tx_channel having been setup for this channel
25361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    for (i = 0; i < TX_CHANS; ++i)
25371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      if (dev->tx_channel_record[i] == channel) {
25381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->tx_channel_record[i] = -1;
25391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	break;
25401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      }
25411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (dev->last_vc == channel)
25421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      dev->tx_last = -1;
25431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    tx_release (dev);
25441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
25451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
25461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (atm_vcc->qos.rxtp.traffic_class != ATM_NONE) {
25471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // disable RXing - it tries quite hard
25481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    hrz_close_rx (dev, channel);
25491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    // forget the vcc - no more skbs will be pushed
25501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    if (atm_vcc != dev->rxer[channel])
25511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      PRINTK (KERN_ERR, "%s atm_vcc=%p rxer[channel]=%p",
25521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	      "arghhh! we're going to die!",
25531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	      atm_vcc, dev->rxer[channel]);
25541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    dev->rxer[channel] = NULL;
25551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
25561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
25571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // atomically release our rate reservation
25581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  spin_lock (&dev->rate_lock);
25591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_QOS|DBG_VCC, "releasing %u TX PCR and %u RX PCR",
25601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  vcc->tx_rate, vcc->rx_rate);
25611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  dev->tx_avail += vcc->tx_rate;
25621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  dev->rx_avail += vcc->rx_rate;
25631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  spin_unlock (&dev->rate_lock);
25641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
25651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // free our structure
25661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  kfree (vcc);
25671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // say the VPI/VCI is free again
25681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  clear_bit(ATM_VF_ADDR,&atm_vcc->flags);
25691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
25701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
25711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
25721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int hrz_getsockopt (struct atm_vcc * atm_vcc, int level, int optname,
25731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   void *optval, int optlen) {
25741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  hrz_dev * dev = HRZ_DEV(atm_vcc->dev);
25751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_VCC, "hrz_getsockopt");
25761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  switch (level) {
25771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    case SOL_SOCKET:
25781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      switch (optname) {
25791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds//	case SO_BCTXOPT:
25801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds//	  break;
25811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds//	case SO_BCRXOPT:
25821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds//	  break;
25831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
25841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  return -ENOPROTOOPT;
25851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  break;
25861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      };
25871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      break;
25881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
25891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return -EINVAL;
25901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
25911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
25921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int hrz_setsockopt (struct atm_vcc * atm_vcc, int level, int optname,
2593b7058842c940ad2c08dd829b21e5c92ebe3b8758David S. Miller			   void *optval, unsigned int optlen) {
25941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  hrz_dev * dev = HRZ_DEV(atm_vcc->dev);
25951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW|DBG_VCC, "hrz_setsockopt");
25961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  switch (level) {
25971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    case SOL_SOCKET:
25981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      switch (optname) {
25991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds//	case SO_BCTXOPT:
26001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds//	  break;
26011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds//	case SO_BCRXOPT:
26021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds//	  break;
26031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	default:
26041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  return -ENOPROTOOPT;
26051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  break;
26061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      };
26071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      break;
26081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
26091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return -EINVAL;
26101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
26111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
26121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
26131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
26141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int hrz_ioctl (struct atm_dev * atm_dev, unsigned int cmd, void *arg) {
26151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  hrz_dev * dev = HRZ_DEV(atm_dev);
26161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW, "hrz_ioctl");
26171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return -1;
26181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
26191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
26201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsunsigned char hrz_phy_get (struct atm_dev * atm_dev, unsigned long addr) {
26211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  hrz_dev * dev = HRZ_DEV(atm_dev);
26221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW, "hrz_phy_get");
26231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return 0;
26241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
26251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
26261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void hrz_phy_put (struct atm_dev * atm_dev, unsigned char value,
26271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 unsigned long addr) {
26281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  hrz_dev * dev = HRZ_DEV(atm_dev);
26291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW, "hrz_phy_put");
26301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
26311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
26321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int hrz_change_qos (struct atm_vcc * atm_vcc, struct atm_qos *qos, int flgs) {
26331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  hrz_dev * dev = HRZ_DEV(vcc->dev);
26341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW, "hrz_change_qos");
26351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return -1;
26361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
26371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
26381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
26391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** proc file contents **********/
26401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
26411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int hrz_proc_read (struct atm_dev * atm_dev, loff_t * pos, char * page) {
26421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  hrz_dev * dev = HRZ_DEV(atm_dev);
26431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  int left = *pos;
26441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW, "hrz_proc_read");
26451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
26461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  /* more diagnostics here? */
26471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
26481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
26491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (!left--) {
26501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    unsigned int count = sprintf (page, "vbr buckets:");
26511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    unsigned int i;
26521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    for (i = 0; i < TX_CHANS; ++i)
26531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds      count += sprintf (page, " %u/%u",
26541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			query_tx_channel_config (dev, i, BUCKET_FULLNESS_ACCESS),
26551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			query_tx_channel_config (dev, i, BUCKET_CAPACITY_ACCESS));
26561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    count += sprintf (page+count, ".\n");
26571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return count;
26581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
26591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
26601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
26611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (!left--)
26621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return sprintf (page,
26631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    "cells: TX %lu, RX %lu, HEC errors %lu, unassigned %lu.\n",
26641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    dev->tx_cell_count, dev->rx_cell_count,
26651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    dev->hec_error_count, dev->unassigned_cell_count);
26661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
26671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (!left--)
26681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return sprintf (page,
26691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    "free cell buffers: TX %hu, RX %hu+%hu.\n",
26701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    rd_regw (dev, TX_FREE_BUFFER_COUNT_OFF),
26711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    rd_regw (dev, RX_FREE_BUFFER_COUNT_OFF),
26721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    dev->noof_spare_buffers);
26731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
26741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (!left--)
26751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return sprintf (page,
26761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    "cps remaining: TX %u, RX %u\n",
26771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    dev->tx_avail, dev->rx_avail);
26781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
26791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return 0;
26801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
26811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
26821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic const struct atmdev_ops hrz_ops = {
26831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  .open	= hrz_open,
26841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  .close	= hrz_close,
26851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  .send	= hrz_send,
26861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  .proc_read	= hrz_proc_read,
26871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  .owner	= THIS_MODULE,
26881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
26891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
26901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __devinit hrz_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_ent)
26911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
26921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hrz_dev * dev;
26931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err = 0;
26941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
26951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// adapter slot free, read resources from PCI configuration space
26961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 iobase = pci_resource_start (pci_dev, 0);
26971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	u32 * membase = bus_to_virt (pci_resource_start (pci_dev, 1));
26981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int irq;
26991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char lat;
27001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
27011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD (DBG_FLOW, "hrz_probe");
27021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
27031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (pci_enable_device(pci_dev))
27041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EINVAL;
27051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
27061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* XXX DEV_LABEL is a guess */
27071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!request_region(iobase, HRZ_IO_EXTENT, DEV_LABEL)) {
2708aac725cf1649d593a13be1edc99ed489f8050a99Jiri Slaby		err = -EINVAL;
27091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_disable;
27101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
27111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
27120c1cca1d8e0d58775dad43374f925e6cddf1bebcOm Narasimhan	dev = kzalloc(sizeof(hrz_dev), GFP_KERNEL);
27131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dev) {
27141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		// perhaps we should be nice: deregister all adapters and abort?
27151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		PRINTD(DBG_ERR, "out of memory");
27161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err = -ENOMEM;
27171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_release;
27181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
27191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
27201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pci_set_drvdata(pci_dev, dev);
27211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
27221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// grab IRQ and install handler - move this someplace more sensible
27231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	irq = pci_dev->irq;
27241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (request_irq(irq,
27251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			interrupt_handler,
2726dace145374b8e39aeb920304c358ab5e220341abThomas Gleixner			IRQF_SHARED, /* irqflags guess */
27271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			DEV_LABEL, /* name guess */
27281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev)) {
27291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		PRINTD(DBG_WARN, "request IRQ failed!");
27301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err = -EINVAL;
27311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_free;
27321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
27331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
27341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD(DBG_INFO, "found Madge ATM adapter (hrz) at: IO %x, IRQ %u, MEM %p",
27351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       iobase, irq, membase);
27361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2737d9ca676bcb26e1fdff9265a3e70f697cd381c889Dan Williams	dev->atm_dev = atm_dev_register(DEV_LABEL, &pci_dev->dev, &hrz_ops, -1,
2738d9ca676bcb26e1fdff9265a3e70f697cd381c889Dan Williams					NULL);
27391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!(dev->atm_dev)) {
27401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		PRINTD(DBG_ERR, "failed to register Madge ATM adapter");
27411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		err = -EINVAL;
27421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out_free_irq;
27431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
27441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
27451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD(DBG_INFO, "registered Madge ATM adapter (no. %d) (%p) at %p",
27461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       dev->atm_dev->number, dev, dev->atm_dev);
27471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->atm_dev->dev_data = (void *) dev;
27481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->pci_dev = pci_dev;
27491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
27501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// enable bus master accesses
27511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pci_set_master(pci_dev);
27521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
27531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// frobnicate latency (upwards, usually)
27541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &lat);
27551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (pci_lat) {
27561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		PRINTD(DBG_INFO, "%s PCI latency timer from %hu to %hu",
27571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       "changing", lat, pci_lat);
27581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, pci_lat);
27591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else if (lat < MIN_PCI_LATENCY) {
27601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		PRINTK(KERN_INFO, "%s PCI latency timer from %hu to %hu",
27611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       "increasing", lat, MIN_PCI_LATENCY);
27621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, MIN_PCI_LATENCY);
27631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
27641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
27651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->iobase = iobase;
27661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->irq = irq;
27671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->membase = membase;
27681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
27691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->rx_q_entry = dev->rx_q_reset = &memmap->rx_q_entries[0];
27701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->rx_q_wrap  = &memmap->rx_q_entries[RX_CHANS-1];
27711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
27721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// these next three are performance hacks
27731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->last_vc = -1;
27741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->tx_last = -1;
27751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->tx_idle = 0;
27761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
27771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->tx_regions = 0;
27781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->tx_bytes = 0;
27791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->tx_skb = NULL;
27801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->tx_iovec = NULL;
27811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
27821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->tx_cell_count = 0;
27831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->rx_cell_count = 0;
27841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->hec_error_count = 0;
27851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->unassigned_cell_count = 0;
27861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
27871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->noof_spare_buffers = 0;
27881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
27891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{
27901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned int i;
27911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0; i < TX_CHANS; ++i)
27921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev->tx_channel_record[i] = -1;
27931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
27941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
27951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->flags = 0;
27961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
27971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// Allocate cell rates and remember ASIC version
27981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// Fibre: ATM_OC3_PCR = 1555200000/8/270*260/53 - 29/53
27991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// Copper: (WRONG) we want 6 into the above, close to 25Mb/s
28001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// Copper: (plagarise!) 25600000/8/270*260/53 - n/53
28011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
28021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (hrz_init(dev)) {
28031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		// to be really pedantic, this should be ATM_OC3c_PCR
28041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->tx_avail = ATM_OC3_PCR;
28051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->rx_avail = ATM_OC3_PCR;
28061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		set_bit(ultra, &dev->flags); // NOT "|= ultra" !
28071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} else {
28081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->tx_avail = ((25600000/8)*26)/(27*53);
28091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		dev->rx_avail = ((25600000/8)*26)/(27*53);
28101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		PRINTD(DBG_WARN, "Buggy ASIC: no TX bus-mastering.");
28111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
28121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
28131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// rate changes spinlock
28141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_init(&dev->rate_lock);
28151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
28161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// on-board memory access spinlock; we want atomic reads and
28171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// writes to adapter memory (handles IRQ and SMP)
28181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_init(&dev->mem_lock);
28191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
28201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	init_waitqueue_head(&dev->tx_queue);
28211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
28221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	// vpi in 0..4, vci in 6..10
28231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->atm_dev->ci_range.vpi_bits = vpi_bits;
28241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->atm_dev->ci_range.vci_bits = 10-vpi_bits;
28251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
28261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	init_timer(&dev->housekeeping);
28271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->housekeeping.function = do_housekeeping;
28281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->housekeeping.data = (unsigned long) dev;
28291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mod_timer(&dev->housekeeping, jiffies);
28301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
28311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
28321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return err;
28331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
28341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_free_irq:
28351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_irq(dev->irq, dev);
28361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_free:
28371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(dev);
28381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_release:
28391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_region(iobase, HRZ_IO_EXTENT);
28401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout_disable:
28411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pci_disable_device(pci_dev);
28421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	goto out;
28431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
28441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
28451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __devexit hrz_remove_one(struct pci_dev *pci_dev)
28461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
28471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hrz_dev *dev;
28481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
28491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev = pci_get_drvdata(pci_dev);
28501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
28511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	PRINTD(DBG_INFO, "closing %p (atm_dev = %p)", dev, dev->atm_dev);
28521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	del_timer_sync(&dev->housekeeping);
28531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hrz_reset(dev);
28541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	atm_dev_deregister(dev->atm_dev);
28551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_irq(dev->irq, dev);
28561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_region(dev->iobase, HRZ_IO_EXTENT);
28571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(dev);
28581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
28591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	pci_disable_device(pci_dev);
28601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
28611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
28621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __init hrz_check_args (void) {
28631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef DEBUG_HORIZON
28641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTK (KERN_NOTICE, "debug bitmap is %hx", debug &= DBG_MASK);
28651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
28661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (debug)
28671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTK (KERN_NOTICE, "no debug support in this image");
28681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
28691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
28701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (vpi_bits > HRZ_MAX_VPI)
28711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTK (KERN_ERR, "vpi_bits has been limited to %hu",
28721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    vpi_bits = HRZ_MAX_VPI);
28731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
28741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (max_tx_size < 0 || max_tx_size > TX_AAL5_LIMIT)
28751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTK (KERN_NOTICE, "max_tx_size has been limited to %hu",
28761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    max_tx_size = TX_AAL5_LIMIT);
28771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
28781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (max_rx_size < 0 || max_rx_size > RX_AAL5_LIMIT)
28791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTK (KERN_NOTICE, "max_rx_size has been limited to %hu",
28801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    max_rx_size = RX_AAL5_LIMIT);
28811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
28821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return;
28831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
28841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
28851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_AUTHOR(maintainer_string);
28861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DESCRIPTION(description_string);
28871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
28881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(debug, ushort, 0644);
28891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(vpi_bits, ushort, 0);
28901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(max_tx_size, int, 0);
28911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(max_rx_size, int, 0);
28921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param(pci_lat, byte, 0);
28931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(debug, "debug bitmap, see .h file");
28941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(vpi_bits, "number of bits (0..4) to allocate to VPIs");
28951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(max_tx_size, "maximum size of TX AAL5 frames");
28961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(max_rx_size, "maximum size of RX AAL5 frames");
28971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC(pci_lat, "PCI latency in bus cycles");
28981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
28991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct pci_device_id hrz_pci_tbl[] = {
29001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ PCI_VENDOR_ID_MADGE, PCI_DEVICE_ID_MADGE_HORIZON, PCI_ANY_ID, PCI_ANY_ID,
29011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  0, 0, 0 },
29021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ 0, }
29031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
29041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
29051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_DEVICE_TABLE(pci, hrz_pci_tbl);
29061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
29071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct pci_driver hrz_driver = {
29081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name =		"horizon",
29091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.probe =	hrz_probe,
29101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.remove =	__devexit_p(hrz_remove_one),
29111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.id_table =	hrz_pci_tbl,
29121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
29131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
29141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** module entry **********/
29151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
29161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init hrz_module_init (void) {
29171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // sanity check - cast is needed since printk does not support %Zu
29181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  if (sizeof(struct MEMMAP) != 128*1024/4) {
29191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    PRINTK (KERN_ERR, "Fix struct MEMMAP (is %lu fakewords).",
29201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    (unsigned long) sizeof(struct MEMMAP));
29211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    return -ENOMEM;
29221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  }
29231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
29241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  show_version();
29251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
29261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // check arguments
29271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  hrz_check_args();
29281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
29291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  // get the juice
29301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  return pci_register_driver(&hrz_driver);
29311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
29321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
29331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/********** module exit **********/
29341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
29351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void __exit hrz_module_exit (void) {
29361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  PRINTD (DBG_FLOW, "cleanup_module");
2937b45eccdb51c102e3c5ff9eaecc36200ab2eb09c0Tobias Klauser
2938b45eccdb51c102e3c5ff9eaecc36200ab2eb09c0Tobias Klauser  pci_unregister_driver(&hrz_driver);
29391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
29401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
29411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init(hrz_module_init);
29421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit(hrz_module_exit);
2943