/*****************************************************************************
 *
 * pciscc4.c	This is the device driver for the PCISCC-4 card or any other
 *		board based on the Siemens PEB-20534H (DSCC-4) communication
 *              controller. The DSCC-4 is a four-channel medium-speed (up
 *		to 10 respectively 52 Mbps/channel) synchronous serial
 *		interface controller with HDLC protocol processor and
 *		busmaster-DMA facilities, and furthermore it is undoubtly
 *              the most broken piece of silicon I ever saw in my whole
 *              life.
 *
 * Info:	http://www.afthd.tu-darmstadt.de/~dg1kjd/pciscc4
 *
 * Authors:	(c) 1999-2002  Jens David <dg1kjd@afthd.tu-darmstadt.de>
 *
 * Policy:	Please contact me before making structural changes.
 *		Before applying changes to the communication with
 *		the DSCC-4 please read:
 *		 - Data Sheet 05/00 PEB-20534 Version 2.1
 *		 - Delta Sheet Chip Rev. 2.0-2.1
 *		 - DSCC-4 Errata Book PEB-20534H Rev 2.0 DS7 05/23/2000
 *               - DSCC-4 Errata Book PED-20534H Rev 2.1 DS5 05/23/2000
 *		 - Sample driver source code as of 07/27/99
 *		All these documents are available from Infineon on
 *		request or from http://www.infineon.com/cgi/ecrm.dll/\
 *              ecrm/scripts/prod_ov.jsp?oid=13604&cat_oid=-8059 .
 *		At least the current version of this beast likes to be
 *		treated _very_ carefully. If you don't do this, it crashes
 *		itself or the system. I have made comments on these common
 *		traps where appropriate. No, there isn't such thing as a
 *		"master reset" bit. If the DMAC crashes the device needs to
 *              be power cycled or /RES asserted.
 *
 * CVS:		$Id: pciscc4.c,v 1.85 2002/02/06 17:19:31 dg1kjd Exp $
 *
 * Changelog:  	Please log any changes here.
 * 	      |	08/23/99 Initial version				Jens
 *	      | 08/25/99 Reworked buffer concept to use last-mode	Jens
 *	      |		 policy and implemented Siemens' workarounds
 *	      |	08/27/99 Reworked transmitter to use internal timers	Jens
 *	      |		 for better resolution at txdelay/txtail
 *	      | 09/01/99 Ioctl()s implemented				Jens
 *	      | 09/10/99 Descriptor chain reworked. RX hold condition	Jens
 *	      |          can't occur any more. TX descriptors are not	Jens
 *	      |          re-initialized after transmit any more.
 *	      | 09/12/99 TX reworked. TX-Timeout added.			Jens
 *	      | 09/13/99 TX timeout fixed				Jens
 *	      | 10/09/99 Cosmetic fixes and comments added		Jens
 *	      | 10/16/99 Cosmetic stuff and non-module mode fixed	Jens
 *	      | 10/21/99 TX-skbs are not freed in xmit()-statemachine	Jens
 *	      | 10/25/99 Default configuration more sensible now	Jens
 *	      | 02/13/00 Converted to new driver interface		Jens
 *	      | 08/06/00 Some more chip-bug workarounds mask rev. 2.0
 *	      |          and made locking SMP safe                      Jens
 *            | 08/16/00 Structural changes in TX-state and RTS handing
 *            |          to work around SCC-register readback problem,
 *            |          SIOCPCISCCSDCFG now even works when iface is
 *            |          up, as do proc/sysctl controls. Introduced
 *            |          dev->tbusy flag. More workarounds for 2.0 .    Jens
 *	      | 12/24/00 Cosmetic cleanups, debugging defaults to off	Jens
 *            | 07/09/01 mark_bh(NET_BH) on resetting dev->tbusy;
 *            |          enabled txd in full duplex mode on request     Jens
 *            | 02/06/02 Minor bugfix in error handling                 Jens
 *
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *----------------------------------------------------------------------------
 *
 *  | Please note that the GPL allows you to use the driver, NOT the radio. |
 *  | In order to use the radio, you need a license from the communications |
 *  | authority of your country.					    |
 *
 *----------------------------------------------------------------------------
 *
 *****************************************************************************
 *
 *				Concept of Operation
 *      	                --------------------
 *
 * I. SCCs
 * We use all SCC cores in HDLC mode. Asyncronous and BiSync operation is not
 * supported and probably never will. We do not make use of the built-in
 * LAPB/LAPD protocol processor features (Auto Mode). Moreover we can't use
 * automatic address recognition either because it is implemented in a way
 * which allows only operation with fixed header sizes and address fields on
 * static positions. Thus we use HDLC address mode 0. As far as the clock modes
 * are concerned we make use of mode 0a (for DF9IC-like modems, RX and TX clock
 * from pin header), 0b (G3RUH-"like", external RX clock, internal TX clock
 * from BRG but unfornately without oversampling), 6b (for TCM-3105-like simple
 * modems, using on-chip DPLL for RX clock recovery. Internal TX clock from BRG
 * which can optionaly be provided on TxClk and/or RTS pins. No oversampling.)
 * and 4 (external RX and TX clock like DF9IC, but with clock gapping
 * function, see Data Book). Channel coding is user-selectable
 * on a per-channel basis. DF9IC-type modems like NRZ (conversion NRZ->NRZI
 * done internally), TCM-3105-like modems want NRZI coding.
 * Moreover manchester, FM0 and FM1 can be selected (untested).
 * The internal SCC-DMAC interface seems to obey the KISS-concept. The drawback
 * of this fact is, that the chip fills our data buffers in memory completely
 * sequential. If at the end of a frame the SCC realizes, that the FCS comparison
 * failed, it does not "discard" the frame. That is, it requests an interrupt and
 * uses up a packet buffer as if the frame was valid. The frame is, however,
 * marked invalid, but of cause the host processor still needs to clean the
 * mess up, which costs time. Now consider that every six ones in series on a
 * empty channel will cause an interrupt and work to the handler. The only
 * way around this is to gate the receive data with the DCD signal. Of cause
 * the modem's DCD needs to be very fast to accomplish this. The standard-DCD
 * on DF9IC-type modems currently isn't. As far as modem handshake is concerned
 * we program the DCD input of each channel as general purpose input and read
 * its state whenever L2 requests us to do so. TX handshake can be used in two
 * modes I called "hard" and "soft". Hard mode is reserved for future
 * (smart) modems which tell the controller when they are ready to transmit
 * using the CTS (clear to send) signal. In soft mode we use each channel's
 * internal timer to generate txdelay and txtail. The advantage of this concept
 * is, that we have a resolution of one bit since the timers are clocked with
 * the effective TxClk, which also allows us to probe the TX-bitrate in external
 * clock modes (L2 likes this information). The SCC cores have some other
 * goodies, as preample transmission, one insertion after 7 consecutive zeros
 * and stuff like this which we make user selectable.
 *
 * II. DMA Controller and IRQs
 * For maximum performance and least host processor load, the design of the
 * DMA controller is descriptor orientated. For both, RX and TX channels
 * descriptor "queues" are set up on device initialization. Each descriptor
 * contains a link to its subsequent desriptor, a pointer to the buffer
 * associated with it and the buffer's size. The buffer itself is _not_ part
 * of the descriptor, but can be located anywhere else in address space.
 * Thus, in TX case all we have to do when a packet to be sent arrives from
 * L2, is painting out a descriptor (pointer to the sk_buf's data buffer,
 * length of the frame and so on) and telling the DMAC to process it. We do
 * not have to move the data around in memory. When the descriptor is finished
 * (i.e. packet sent out completely or at least data completely in FIFO), the
 * DMAC sets a flag (C) in the descriptor and issues an IRQ. We check the flag
 * and if it is set, we can skb_free up the packet. Both descriptor queues (RX
 * and TX) are organized circular with a user setable size and allocated
 * statically at device initialization. As far as the RX queue ("ring") is
 * concerned we also already allocate the sk_buffs associated with them.
 * Whenever the DMAC processed a RX descriptor (thus "filled" the buffer
 * associated with it) we release the buffer to L2 and allocate a new one.
 * No copying. The structure of the RX descriptor chain never changes either.
 * It stays the same since inititalization on device initialization and
 * descriptor memory itself is only freed when the device destructor is called.
 * The fact that both descriptor queues are kept statically has two advantages:
 * It is save, because the DMAC can not "escape" due to a race condition and
 * mess up our memory and it works around a hardware bug in the DSCC-4.
 * A few words on linux mm:
 * When a device driver allocates memory using functions like malloc() or
 * alloc_skb(), the returned address pointers are pointers to virtual memory.
 * In case of access to this memory, the MMU, as part of the CPU translates
 * the virtual addresses to physical ones, which are e.g. used to drive the
 * RAM address bus lines. If a PCI bus master accesses the same memory, it
 * needs to know the right address vom _its_ point of view, the so-called
 * "bus" address. On most architectures this is the same as the physical
 * address. We use the funktion virt_to_bus() and bus_to_virt() to translate
 * them. The descriptor structures contain both types, just to make the code
 * faster and more readable. The symbol names for "bus"-pointers end on
 * "ptr", for example rx_desc_t.next --(virt-to-bus)--> rx_desc_t.nextptr.
 * When we accessed "common" memory (i.e. descriptor or buffer memory) we
 * issue a flush_cache_all() due to the fact that some architectures (not PC)
 * don't keep memory caches consistent on DMAs. Where it isn't apropriate gcc
 * will optimize it away for us. Read and write memory barriers rmb() / wmb()
 * fit in the same category. i386 family CPUs do not perform instruction
 * reordering as far as memory access is concerned.
 * 
 * Another word on IRQ management:
 * The DMAC is not only responsible for moving around network data from/to
 * the SCC cores, but also maintains 10 so-called "interrupt queues" (IQs).
 * These are intended to help speeding up operation and minimize load on the
 * host CPU. There is the configuration queue (IQCFG) which is responsible
 * for answers to DMAC configuration commands, the peripheral queue (IQPER),
 * which cares about interrupt sources on the local bus, SSC (not SCC!) or GPP
 * if enabled, and one TX and one RX queue per channel (IQRX and IQTX
 * respectively), which are responsible for RX and TX paths and not only
 * indicate DMAC exceptions (packet finished etc.) but also SCC exceptions
 * (FIFO over/underrun, frame length exceeded etc.). Each element in the
 * queues is a dword. The queues are "naturally" organized circular as well.
 * Naturally means, that there is no such thing as a "next pointer" as in
 * the frame descriptors, but that we tell the DMAC the length of each queue
 * (user configurable in 32 dword-steps) and it does the wrap-around
 * automagically. Whenever an element is added to a queue an IRQ is issued.
 * The IRQ handler acks all interrupts by writing back the global status
 * register (GSTAR) and starts processing ALL queues, independent of who
 * caused the IRQ.
 * Update 08/16/2000: Unfortunately spuriously interrupt vectors seem to
 * get lost with some PCI bus arbiters, especially the AMD-751's (Irongate)
 * controller. I spent about one week searching for this problem but due to
 * lack of an PCI bus analyzer I did not succeed. This problem occurs with
 * 2.0 as well as 2.1 mask revision and only under *very* high PCI bus and
 * SCC bitrate load. I introduced a work-around which detects suspicious
 * interrupt vector losts and scans the whole queue. This compensates completely
 * for this symptom, but even more unfortunately if the lost of interrupt
 * vectors occurs, usually the DMAC also forgets to write the "result"
 * field of completed TX descriptors effectively locking up the transmitter
 * of the concerned channel. I could implement a similar work-around
 * here, but I rather recommend using another mainboard. I suspect that
 * what we see here is a result of non-consistent memory-cache controlling.
 * Reprogramming the PCI bus arbiter block does not seem to help either (at
 * least not in the case of AMD-751). As last resort I implemented a new
 * ioctl() (SIOCPCISCCKICKTX) to kick the transmitter manually. Use with
 * care, and only if TX is really locked up, otherwise internal synchronization
 * between descriptor cleanup process and DMAC-transmitter is lost, resulting
 * in system lockup, DMAC lockup and transmission of arbitrary data. Kicking
 * can now also be triggered by L2 via tbusy-mechanism. You can adjust
 * the time interval neccessary to consider a transmitter "hung" via
 * txkick module parameter (default is 30 seconds).
 *
 * III. General Purpose Port (GPP)
 * The general purpose port is used for status LED connection. We support
 * only 2 LEDs per port. These can be controlled with an ioctl(). We do not
 * care about it, this ought to be done by a user-space daemon. The SSC port
 * is not used. The LBI can be configured with the global settings and
 * controlled by an own ioctl().
 *
 * IV. Configuration
 * We divide configuration into global (i.e. concerning all ports, chipwide)
 * and local. We have one template for each, chipcfg_default and devcfg_default
 * which is hardcoded and never changes. On module load it is copied for each
 * chip and each device respectively (chipctl_t.cfg and devctl_t.cfg). The
 * silicon is initialized with these values only in chip_open() and
 * device_open() and the structures themselves can only be changed when the
 * corresponding interface (or all interfaces for global config) is down.
 * Changes take effect when the interface is brought up the next time. 
 *
 * V. Initialization
 * When module_init is called, the PCI driver already took care of assigning
 * two pieces of memory space and an IRQ to each board. On module load we do
 * nothing else than building up our internal structures (devctl_t and
 * chipctl_t), grabbing the interface names and registering them with the
 * network subsystem. Chip_open() and device_open() are called only upon uping
 * a device and perform IRQ grabbing, memory mapping and allocation and
 * hardware initialization.
 *
 * VI. RX Handling
 * When a frame is received completely, the C flag in the corresponding
 * descriptor is set by the DSCC-4, an interrupt vector transfered to the
 * channel's RX IQ, and the IRQ line asserted. The IRQ handler takes control
 * of the system. The first step it performs is reading the global status
 * register and writing it back, thus ack'ing the IRQ. Then it is analyzed
 * bit-by-bit to find out where it originated from. The channel's RX IQ
 * is examined and the function pciscc_isr_receiver() called for the
 * corresponding port. This functions processes the rx descriptor queue
 * starting with the element (devctl_t.dq_rx_next) following the last element
 * processed the last time the function was called. All descriptors with the
 * C-flag ("C"omplete) set are processed. And at the end the dq_rx_next pointer
 * is updated to the next to last element processed. During "processing" at
 * first two pieces of information are examined: The status field of the
 * descriptor, mainly containing the length of the received frame and a flag
 * telling us wether the frame reception was aborted or not (RA), which 
 * was written by the DMAC and the so-called Receive Data Section Status Byte
 * (RSTA) which was appended to the end of the data buffer by the channel's
 * SCC core. Both are checked and if they yield a valid frame and we success-
 * fully allocated a new skb we remove the old one from the descriptor and
 * hand it off to pciscc_rx_skb() which paints out some of the skb's members
 * and fires it up to the MAC layer. The descriptor's fields are re-initialized
 * anyway to prepare it for the next reception.
 * After all complete descriptors were processed we must tell the DMAC that the
 * last ready-to-fill descriptor (LRDA, Last Receive Descriptor Address) is the
 * one pre-previous to the last one processed. In fact experiments show that
 * normally this is not neccessary since we get an interrupt for _every_
 * received frame so we can re-prepare the descriptor then. This is just to
 * prevent the DMAC from "running around" uncontrolled in the circular
 * structure, eventually losing sync with the devctl_t.dq_rx_next pointer in
 * case of _very_ high bus/interrupt latency on heavy system load conditions.
 * 
 * VII. TX Handling
 * We assume we are in half duplex mode with software txdelay since everything
 * else is a lot easier. The current TX state is kept track of in the
 * devctl_t.txstate variable. When L2 hands us a frame, the first thing we
 * do is check whether there is a free TX descriptor ready in the device's
 * ring. The element dq_tx_last is a pointer to the last descriptor which
 * is currently to be sent or already is sent. Thus, the element next to this
 * element is the one we would like to fill. The variable dq_tx_cleanup
 * of the device control structure tells us the next element to be "cleaned
 * up" (free skb etc.) after transmission. If it points not to the element
 * we like to fill, the element we like to fill is free. If it does, we must
 * discard the new frame due to the full descriptor queue. Now that we are
 * sure to have found our descriptor candidate will can paint it out, but
 * we can't start transmission yet. We check what state the TX is in. If
 * it is idle, we load the timer with the txdelay value and start it. Of cause
 * we also need to manually assert the RTS line. If we were already
 * transmitting, or sending trailing flags we immediately schedule the
 * descriptor for process by the DMAC by pointing LTDA (Last Transmit
 * Descriptor Address) to it. In the first case, the txdelay-timer will run
 * out after the txdelay period is over, causing an IRQ. The interrupt handler
 * can now schedule the transmission. During transmission we use the timer
 * as some kind of software watchdog. When transmission finishes, we again
 * program and start the timer, this time with the txtail value. The txstate
 * variable is set to TX_TAIL and as soon as the timer runs out we can
 * deassert the RTS line and reset txstate to TX_IDLE. The frame(s) were
 * (hopefully) transmitted successfully. Anyway, each transmitted frame
 * causes a ALLS TX IRQ. The only thing we need to do then, is to call
 * tx_cleanup() which walks through the tx descriptor ring, cleaning up
 * all finished entries (freeing up the skbs and things like this).
 *
 *****************************************************************************
 */

#include <linux/config.h>
#include <linux/module.h>
#include <asm/byteorder.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <linux/string.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/in.h>
#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
#include <linux/rtnetlink.h>
#include <linux/if_arp.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/ioctl.h>
#include <linux/delay.h>
#include <net/ax25.h>
#include <net/ax25dev.h>
#include <linux/pciscc4.h>

/* --------------------------------------------------------------------------------------------- */
/* user serviceable area or module parameter */

static int xtal =	19660800;	/* oscillator frequency in HZ */
static int probebit =	9600;		/* number of cycles for tx_bitrate test */
static int txtimeout =	120;		/* TX watchdog - timeout in seconds to take iface down */
static int txkick =     30;             /* TX watchdog - timeout in seconds to kick iface */
#undef PCISCC_DEBUG			/* enable debugging */
#undef PCISCC_VDEBUG			/* enable verbose buffer debugging */

/* --------------------------------------------------------------------------------------------- */
/* global variables */

static const char PCISCC_VERSION[] = "$Id: pciscc4.c,v 1.85 2002/02/06 17:19:31 dg1kjd Exp $";

static int chipcnt;
static struct devctl_t *devctl[64];
static struct chipctl_t *chipctl[16];
static struct tq_struct txto_task;
static struct tq_struct txreset_task;
static unsigned char * volatile dummybuf;

/* --------------------------------------------------------------------------------------------- */

volatile int atom_check_counter = 0;

#define ATOMICY_CHECK {\
        if ((++atom_check_counter) > 1)\
                printk(KERN_ERR "PCISCC: Atomicy check failed in function:%s line:%u.\n", __FUNCTION__, __LINE__);\
}

#define ATOMICY_CHECK_END {\
        atom_check_counter--;\
}

/* --------------------------------------------------------------------------------------------- */

/* template for global configuration, copied on driver init */
static struct chipcfg_t chipcfg_default = {
	0,			/* LBI mode */
	1,			/* oscillator power */
	16,			/* number of RX descriptors and buffers per channel */
	8,			/* number of TX descriptors per channel */
	32,			/* interrupt queue length */
	-1,			/* priority channel */
	16,			/* RX main fifo DMA threshold */
};

/* template for device configuration, copied on driver init */
static struct devcfg_t devcfg_default = {
	CFG_CHCODE_NRZ,		/* channel coding */
	CFG_CM_DF9IC,		/* clocking mode */
	CFG_DUPLEX_HALF,	/* duplex mode */
	0,			/* DPLL */
	10,			/* BRG "M" */
	0,			/* BRG "N" (N+1)*2^M; M=10, N=0 => 9600 baud */
	0,			/* clock-out enable */
	0,			/* data inversion */
	CFG_TXDDRIVE_TP,	/* TxD driver type */
	0,			/* carrier detect inversion */
	0,			/* test loop */
	CFG_TXDEL_SOFT,		/* TX-delay mode */
	2000,			/* software TX-delay in bitclk cycles => default 250 ms @9600 baud */
	400,			/* TX-tail, see above */
	1,			/* shared flags */
	0,			/* CRC mode */
	0,			/* preamble repetitions */
	0,			/* preamble */
	0			/* HDLC extensions */
};

/* --------------------------------------------------------------------------------------------- */

#ifdef PCISCC_DEBUG
static void pciscc_pci_regdump(struct chipctl_t *cctlp)
{
	int i;
	unsigned int bus = cctlp->pcidev->bus->number;
	unsigned int devfn = cctlp->pcidev->devfn;
	unsigned int value;

	printk(KERN_INFO "PCISCC: PCI Register dump for device %u:%u follows:\nPCISCC:", bus, devfn);
	for (i=0; i<16; i++) {
		pcibios_read_config_dword(bus, devfn, i*4, &value);
		printk(KERN_INFO " 0x%08X", value);
	}
	printk(KERN_INFO "\n");
        return;
}
#endif

/* --------------------------------------------------------------------------------------------- */

#ifdef PCISCC_DEBUG
/* dump DMAC's register to syslog */
static void pciscc_dmac_regdump(struct chipctl_t *cctlp)
{
	printk(KERN_INFO "PCISCC: ---------- begin DMAC register dump ----------\n");
	printk(KERN_INFO "CH BRDA        LRDA        FRDA        BTDA        LTDA        FTDA        CFG\n");
	printk(KERN_INFO " 0 B0x%08lx B0x%08lx B0x%08lx B0x%08lx B0x%08lx B0x%08lx %08lx\n",
		(unsigned long) readl(cctlp->io_base+CH0BRDA),
		(unsigned long) readl(cctlp->io_base+CH0LRDA),
		(unsigned long) readl(cctlp->io_base+CH0FRDA),
		(unsigned long) readl(cctlp->io_base+CH0BTDA),
		(unsigned long) readl(cctlp->io_base+CH0LTDA),
		(unsigned long) readl(cctlp->io_base+CH0FTDA),
		(unsigned long) readl(cctlp->io_base+CH0CFG));
	printk(KERN_INFO " 1 B0x%08lx B0x%08lx B0x%08lx B0x%08lx B0x%08lx B0x%08lx %08lx\n",
		(unsigned long) readl(cctlp->io_base+CH1BRDA),
		(unsigned long) readl(cctlp->io_base+CH1LRDA),
		(unsigned long) readl(cctlp->io_base+CH1FRDA),
		(unsigned long) readl(cctlp->io_base+CH1BTDA),
		(unsigned long) readl(cctlp->io_base+CH1LTDA),
		(unsigned long) readl(cctlp->io_base+CH1FTDA),
		(unsigned long) readl(cctlp->io_base+CH1CFG));
	printk(KERN_INFO " 2 B0x%08lx B0x%08lx B0x%08lx B0x%08lx B0x%08lx B0x%08lx %08lx\n",
		(unsigned long) readl(cctlp->io_base+CH2BRDA),
		(unsigned long) readl(cctlp->io_base+CH2LRDA),
		(unsigned long) readl(cctlp->io_base+CH2FRDA),
		(unsigned long) readl(cctlp->io_base+CH2BTDA),
		(unsigned long) readl(cctlp->io_base+CH2LTDA),
		(unsigned long) readl(cctlp->io_base+CH2FTDA),
		(unsigned long) readl(cctlp->io_base+CH2CFG));
	printk(KERN_INFO " 3 B0x%08lx B0x%08lx B0x%08lx B0x%08lx B0x%08lx B0x%08lx %08lx\n",
		(unsigned long) readl(cctlp->io_base+CH3BRDA),
		(unsigned long) readl(cctlp->io_base+CH3LRDA),
		(unsigned long) readl(cctlp->io_base+CH3FRDA),
		(unsigned long) readl(cctlp->io_base+CH3BTDA),
		(unsigned long) readl(cctlp->io_base+CH3LTDA),
		(unsigned long) readl(cctlp->io_base+CH3FTDA),
		(unsigned long) readl(cctlp->io_base+CH3CFG));
	printk(KERN_INFO "PCISCC: ---------- end DMAC register dump ----------\n");
	return;
}
#endif /* PCISCC_DEBUG */

/* --------------------------------------------------------------------------------------------- */

#ifdef PCISCC_DEBUG
/* dump descriptor rings to syslog */
static void pciscc_queuedump(struct devctl_t *dctlp)
{
	unsigned int i;
	struct rx_desc_t *rdp;
	struct tx_desc_t *tdp;
	
	if (!dctlp) return;
	printk(KERN_INFO "PCISCC: ---------- begin queue dump RX iface %s ----------\n", dctlp->name);
	printk(KERN_INFO "%s->dq_rx=V0x%08lx %s->dq_rx_next=V0x%08lx.\n",
		dctlp->name, (unsigned long) dctlp->dq_rx,
		dctlp->name, (unsigned long) dctlp->dq_rx_next);
	printk(KERN_INFO "#   &desc       &next       &prev       &nextptr    &dataptr    &skb        &feptr      flags      result\n");
	for (rdp=dctlp->dq_rx,i=0; ((rdp!=dctlp->dq_rx) || (i==0)) && (i<256); rdp=rdp->next,i++) {
		if (!rdp) break;
		printk(KERN_INFO "%3u V0x%08lx V0x%08lx V0x%08lx B0x%08lx B0x%08lx V0x%08lx B0x%08lx 0x%08lx 0x%08lx\n",
			i,
			(unsigned long) rdp,
			(unsigned long) rdp->next,
			(unsigned long) rdp->prev,
			(unsigned long) rdp->nextptr,
			(unsigned long) rdp->dataptr,
			(unsigned long) rdp->skb,
			(unsigned long) rdp->feptr,
			(unsigned long) rdp->flags,
			(unsigned long) rdp->result);
	}
	printk(KERN_INFO "PCISCC: ---------- end queue dump RX iface %s ----------\n", dctlp->name);
	printk(KERN_INFO "PCISCC: ---------- begin queue dump TX iface %s ----------\n", dctlp->name);
	printk(KERN_INFO "%s->dq_tx=V0x%08lx %s->dq_tx_cleanup=V0x%08lx %s->dq_tx_last=V0x%08lx.\n",
		dctlp->name, (unsigned long) dctlp->dq_tx,
		dctlp->name, (unsigned long) dctlp->dq_tx_cleanup,
		dctlp->name, (unsigned long) dctlp->dq_tx_last);
	printk(KERN_INFO "#   &desc       &next       &prev       &nextptr    &dataptr    &skb        flags      result\n");
	for (tdp=dctlp->dq_tx,i=0; ((tdp!=dctlp->dq_tx) || (i==0)) && (i<256); tdp=tdp->next,i++) {
		if (!tdp) break;
		printk(KERN_INFO "%3u V0x%08lx V0x%08lx V0x%08lx B0x%08lx B0x%08lx V0x%08lx 0x%08lx 0x%08lx\n",
			i,
			(unsigned long) tdp,
			(unsigned long) tdp->next,
			(unsigned long) tdp->prev,
			(unsigned long) tdp->nextptr,
			(unsigned long) tdp->dataptr,
			(unsigned long) tdp->skb,
			(unsigned long) tdp->flags,
			(unsigned long) tdp->result);
	}
	printk(KERN_INFO "PCISCC: ---------- end queue dump TX iface %s ----------\n", dctlp->name);
	return;
}
#endif /* PCISCC_DEBUG */

/* --------------------------------------------------------------------------------------------- */

/*
 * Correct mainboard memory data path misconfigurations / design flaws
 * on certain systems which otherwise could jeopardize memory consistency.
 */
void pciscc_cpu_bridge_fixups(void)
{
	unsigned short bridge_vendor;
	unsigned short bridge_device_id;
	unsigned short s;

	pcibios_read_config_word(0, 0, PCI_VENDOR_ID, &bridge_vendor);
	pcibios_read_config_word(0, 0, PCI_DEVICE_ID, &bridge_device_id);

	if ((bridge_vendor == PCI_VENDOR_ID_AMD) && (bridge_device_id == 0x7006)) {
		printk(KERN_INFO "PCISCC: Detected AMD 751 (Irongate) Northbridge. Fixups enabled.\n");
		pcibios_read_config_word(0, 0, 0x84, &s);  /* PCI arbitration control register */
		s = (1<<12) | (1<<11) | (1<<10) | (1<<9) | (1<<7) | (1<<3);
		pcibios_write_config_word(0, 0, 0x84, s);
		pcibios_read_config_word(0, 0, 0x86, &s);
		s &= ~((1<<0) | (1<<1));
		pcibios_write_config_word(0, 0, 0x86, s);
	}
	return;
}

/* --------------------------------------------------------------------------------------------- */

/*
 * initialize chip, called when first interface of a chip is brought up
 * action sequency was carefully chosen, don't mess with it
 */
static int pciscc_chip_open(struct chipctl_t *cctlp)
{
	unsigned long start_time;
	volatile unsigned long gcc_optimizer_safe;
	unsigned int i;
	unsigned char pci_latency;
	unsigned short cmd;

	if (cctlp->initialized) return 0;
#ifdef PCISCC_DEBUG
	printk(KERN_INFO "PCISCC: chip_open().\n");
#endif
	/* map control and LBI memory */
	cctlp->io_base=ioremap(cctlp->pcidev->base_address[0], 2*1024);
	cctlp->lbi_base=ioremap(cctlp->pcidev->base_address[1], 64*1024);
	/* enable bus mastering */
	pci_set_master(cctlp->pcidev);
	/* tweak latency */
	pcibios_read_config_byte(cctlp->pcidev->bus->number, cctlp->pcidev->devfn, PCI_LATENCY_TIMER, &pci_latency);
	i = pci_latency;
#ifdef PCISCC_DEBUG
	printk(KERN_INFO "PCISCC: chip_open(): Old PCI latency timer: %u.\n", i);
#endif
	pcibios_write_config_byte(cctlp->pcidev->bus->number, cctlp->pcidev->devfn, PCI_LATENCY_TIMER, 0xf8);
	pcibios_read_config_word(cctlp->pcidev->bus->number, cctlp->pcidev->devfn, PCI_COMMAND, &cmd);
	cmd &= ~(1L<<9);  /* reset Fast Back-to-Back enable flag (Errata Sheet 03/99 1.2) */
	cmd |= (1<<6);  /* set PER bit (=normal response to parity errors) */
	cmd |= (1<<8);  /* enable /SERR driver */
	pcibios_write_config_word(cctlp->pcidev->bus->number, cctlp->pcidev->devfn, PCI_COMMAND, cmd);
	pciscc_cpu_bridge_fixups();
#ifdef PCISCC_DEBUG
	pciscc_pci_regdump(cctlp);
#endif
	/* request IRQ */
	if (request_irq(cctlp->pcidev->irq, pciscc_isr, SA_SHIRQ, "pciscc", (void *) cctlp)) {
		printk(KERN_ERR "PCISCC: chip_open(): Could not get IRQ #%u.\n", cctlp->pcidev->irq);
		pciscc_chip_close(cctlp);
		return -EAGAIN;
	}
	cctlp->irq=cctlp->pcidev->irq;
	/* allocate and initialize peripheral queue */
	if (!(cctlp->iq_per = kmalloc(cctlp->cfg.iqlen*4, GFP_DMA | GFP_KERNEL))) {
		printk(KERN_ERR "PCISCC: chip_open(): Out of memory allocating peripheral interrupt queue.\n");
		return -ENOMEM;
	}
	memset(cctlp->iq_per, 0, cctlp->cfg.iqlen*4);
	cctlp->iq_per_next = cctlp->iq_per;
	/* configuration interrupt queue */
	if (!(cctlp->iq_cfg = kmalloc(cctlp->cfg.iqlen*4, GFP_DMA | GFP_KERNEL))) {
		printk(KERN_ERR "PCISCC: chip_open(): Out of memory allocating configuration interrupt queue.\n");
		return -ENOMEM;
	}
	memset(cctlp->iq_cfg, 0, cctlp->cfg.iqlen*4);
	cctlp->iq_cfg_next = cctlp->iq_cfg;
	/* global hardware initialization */
	spin_lock_irq(&cctlp->chip_lock);	
#if defined(__LITTLE_ENDIAN)
	writel(0  /* no endian swapping */
		| (cctlp->cfg.prichan != -1 ? (SPRI | (cctlp->cfg.prichan * CHN)) : 0)
		| (4 * PERCFG)
		| (3 * LCD)
		| CMODE
		| (cctlp->cfg.oscpwr ? 0 : OSCPD), cctlp->io_base+GMODE);
#elif defined(__BIG_ENDIAN)
	writel(ENDIAN  /* endian swapping for packet data activated */
		| (cctlp->cfg.prichan != -1 ? (SPRI | (cctlp->cfg.prichan * CHN)) : 0)
		| (4 * PERCFG)
		| (3 * LCD)
		| CMODE
		| (cctlp->cfg.oscpwr ? 0 : OSCPD), cctlp->io_base+GMODE);
#else
#error endianess undefined - please fix your system
#endif
	writel(virt_to_bus(cctlp->iq_per), cctlp->io_base+IQPBAR);
	writel(virt_to_bus(cctlp->iq_cfg), cctlp->io_base+IQCFGBAR);
	writel((((cctlp->cfg.iqlen/32)-1)*IQCFGLEN) | (((cctlp->cfg.iqlen/32)-1)*IQPLEN), cctlp->io_base+IQLENR2);
	writel(((32/4)*TFSIZE0)
		| ((32/4)*TFSIZE1)
		| ((32/4)*TFSIZE2)
	        | ((32/4)*TFSIZE3), cctlp->io_base+FIFOCR1);  /* 32 dwords FIFO per channel; now fixed */
	writel(((24/4)*TFRTHRES0)
		| ((24/4)*TFRTHRES1)
		| ((24/4)*TFRTHRES2)
		| ((24/4)*TFRTHRES3)
	        | M4_0 | M4_1 | M4_2 | M4_3, cctlp->io_base+FIFOCR2);  /* 24 dwords; fixed */
	writel(cctlp->cfg.mfifo_rx_t, cctlp->io_base+FIFOCR3);
	writel((20*TFFTHRES0)
		| (20*TFFTHRES1)
		| (20*TFFTHRES2)
	        | (20*TFFTHRES3), cctlp->io_base+FIFOCR4);  /* 20 dwords; fixed */
	/* mask out all DMAC interrupts */
	writel((MRFI | MTFI | MRERR | MTERR), cctlp->io_base+CH0CFG);
	writel((MRFI | MTFI | MRERR | MTERR), cctlp->io_base+CH1CFG);
	writel((MRFI | MTFI | MRERR | MTERR), cctlp->io_base+CH2CFG);
	writel((MRFI | MTFI | MRERR | MTERR), cctlp->io_base+CH3CFG);
	/* all SCC cores in reset state */
	writel(0x00000000, cctlp->io_base+SCCBASE[0]+CCR0);
	writel(0x00000000, cctlp->io_base+SCCBASE[1]+CCR0);
	writel(0x00000000, cctlp->io_base+SCCBASE[2]+CCR0);
	writel(0x00000000, cctlp->io_base+SCCBASE[3]+CCR0);
	/* mask out all SCC interrupts */
	writel(0xffffffff, cctlp->io_base+SCCBASE[0]+IMR);
	writel(0xffffffff, cctlp->io_base+SCCBASE[1]+IMR);
	writel(0xffffffff, cctlp->io_base+SCCBASE[2]+IMR);
	writel(0xffffffff, cctlp->io_base+SCCBASE[3]+IMR);
	/* peripheral configuration */
	writel((BTYP*3), cctlp->io_base+LCONF);
	writel(0x00000000, cctlp->io_base+SSCCON);
	writel(0x00000000, cctlp->io_base+SSCIM);
	writel(0x000000ff, cctlp->io_base+GPDIR);
	writel(0x00000000, cctlp->io_base+GPDATA);
	writel(0x00000000, cctlp->io_base+GPIM);
	spin_unlock_irq(&cctlp->chip_lock);
	/* initialize configuration and peripheral IQs */
	start_time = jiffies;
	cctlp->mailbox = MAILBOX_NONE;
	writel(CFGIQCFG | CFGIQP | AR, cctlp->io_base+GCMDR);
	do {
		gcc_optimizer_safe=(jiffies-start_time)<20 && !cctlp->mailbox;
	} while (gcc_optimizer_safe);	/* timeout 20 jiffies */
	switch (cctlp->mailbox) {	/* mailbox was written by isr */
	case MAILBOX_OK:
#ifdef PCISCC_DEBUG
		printk(KERN_INFO "PCISCC: chip_open(): Success on IQ config request.\n");
#endif
		break;
	case MAILBOX_NONE: 
		printk(KERN_ERR "PCISCC: chip_open(): Timeout on IQ config request. Sync HDDs and hardware-reset NOW!\n");
		pciscc_chip_close(cctlp);
		return -EIO;
	case MAILBOX_FAILURE:
		printk(KERN_ERR "PCISCC: chip_open(): Failure on IQ config request. Sync HDDs and hardware-reset NOW!\n");
		pciscc_chip_close(cctlp);
		return -EIO;
	}
	cctlp->initialized=1;
	return 0;
}

/* --------------------------------------------------------------------------------------------- */

/*
 * close chip, called when last device (channel) of a chip was closed.
 * don't mess up.
 */
static int pciscc_chip_close(struct chipctl_t *cctlp)
{
	if (cctlp->usecnt) {
		printk(KERN_ERR "PCISCC: chip_close() called while channels active.\n");
		return -EBUSY;
	}
#ifdef PCISCC_DEBUG
	printk(KERN_INFO "PCISCC: chip_close().\n");
#endif
	/* global configuration to reset state */
	spin_lock_irq(&cctlp->chip_lock);
	writel((4 * PERCFG)
		| (3 * LCD)
		| CMODE
		| OSCPD, cctlp->io_base+GMODE);
	/* mask all DMAC interrupts */
	writel((MRFI | MTFI | MRERR | MTERR), cctlp->io_base+CH0CFG);
	writel((MRFI | MTFI | MRERR | MTERR), cctlp->io_base+CH1CFG);
	writel((MRFI | MTFI | MRERR | MTERR), cctlp->io_base+CH2CFG);
	writel((MRFI | MTFI | MRERR | MTERR), cctlp->io_base+CH3CFG);
	/* SCC cores to reset state */
	writel(0x00000000, cctlp->io_base+SCCBASE[0]+CCR0);
	writel(0x00000000, cctlp->io_base+SCCBASE[1]+CCR0);
	writel(0x00000000, cctlp->io_base+SCCBASE[2]+CCR0);
	writel(0x00000000, cctlp->io_base+SCCBASE[3]+CCR0);
	/* mask all SCC interrupts */
	writel(0xffffffff, cctlp->io_base+SCCBASE[0]+IMR);
	writel(0xffffffff, cctlp->io_base+SCCBASE[1]+IMR);
	writel(0xffffffff, cctlp->io_base+SCCBASE[2]+IMR);
	writel(0xffffffff, cctlp->io_base+SCCBASE[3]+IMR);
	spin_unlock_irq(&cctlp->chip_lock);
	/* free IQs, free IRQ, unmap control address space */
	if (cctlp->iq_per) {
		kfree(cctlp->iq_per);
		cctlp->iq_per=0;
		cctlp->iq_per_next=0;
	}
	if (cctlp->iq_cfg) {
		kfree(cctlp->iq_cfg);
		cctlp->iq_cfg=0;
		cctlp->iq_cfg_next=0;
	}
	if (cctlp->irq) {
		free_irq(cctlp->irq, (void *) cctlp);
		cctlp->irq=0;
	}
	if (cctlp->io_base) {
		iounmap(cctlp->io_base);
		cctlp->io_base=0;
	}
	if (cctlp->lbi_base) {
		iounmap(cctlp->lbi_base);
		cctlp->lbi_base=0;
	}
	cctlp->initialized=0;
	return 0;
}

/* --------------------------------------------------------------------------------------------- */

/*
 * open one channel, chip must have been initialized by chip_open() before.
 * the sequence of actions done here was carefully chosen, don't mess with
 * it unless you know exactly what you are doing...
 */
static int pciscc_channel_open(struct devctl_t *dctlp)
{
	struct chipctl_t *cctlp = dctlp->chip;
	int channel = dctlp->channel;
	struct rx_desc_t *rdp, *last_rdp;
	struct tx_desc_t *tdp, *last_tdp;
	unsigned long l;
	unsigned long start_time;
	volatile unsigned long gcc_optimizer_safe;
	int i;
	unsigned char *data;

	if (dctlp->dev.start) return 0;
#ifdef PCISCC_DEBUG
	printk(KERN_INFO "PCISCC: channel_open().\n");
#endif
	/* allocate and initialize RX and TX IQs */
	if (!(dctlp->iq_rx = kmalloc(cctlp->cfg.iqlen*4, GFP_DMA | GFP_KERNEL))) {
		printk(KERN_ERR "PCISCC: channel_open(): Out of memory allocating rx interrupt queue.\n");
		return -ENOMEM;
	}
	memset(dctlp->iq_rx, 0, cctlp->cfg.iqlen*4);
	dctlp->iq_rx_next = dctlp->iq_rx;
	if (!(dctlp->iq_tx = kmalloc(cctlp->cfg.iqlen*4, GFP_DMA | GFP_KERNEL))) {
		printk(KERN_ERR "PCISCC: channel_open(): Out of memory allocating tx interrupt queue.\n");
		return -ENOMEM;
	}
	memset(dctlp->iq_tx, 0, cctlp->cfg.iqlen*4);
	dctlp->iq_tx_next = dctlp->iq_tx;
	spin_lock_irq(&dctlp->dev_lock);
	writel(0, cctlp->io_base+SCCBASE[channel]+CCR1);  /* stop SCC */
	writel(0, cctlp->io_base+SCCBASE[channel]+CCR2);
	writel(0, cctlp->io_base+SCCBASE[channel]+CCR0);
	dctlp->ccr0 = dctlp->ccr1 = dctlp->ccr2 = 0;
	writel(0, cctlp->io_base+SCCBASE[channel]+TIMR);
	/* set IQ lengths and base addresses */
	l = readl(cctlp->io_base+IQLENR1);
	switch (channel) {
	case 0:	writel(MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH0CFG);
		writel(virt_to_bus(dctlp->iq_rx), cctlp->io_base+IQSCC0RXBAR);
		writel(virt_to_bus(dctlp->iq_tx), cctlp->io_base+IQSCC0TXBAR);
		l &= 0x0fff0fff;
		l |= (((cctlp->cfg.iqlen/32)-1)*IQSCC0RXLEN) | (((cctlp->cfg.iqlen/32)-1)*IQSCC0TXLEN);
		break;
	case 1:	writel(MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH1CFG);
		writel(virt_to_bus(dctlp->iq_rx), cctlp->io_base+IQSCC1RXBAR);
		writel(virt_to_bus(dctlp->iq_tx), cctlp->io_base+IQSCC1TXBAR);
		l &= 0xf0fff0ff;
		l |= (((cctlp->cfg.iqlen/32)-1)*IQSCC1RXLEN) | (((cctlp->cfg.iqlen/32)-1)*IQSCC1TXLEN);
		break;
	case 2:	writel(MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH2CFG);
		writel(virt_to_bus(dctlp->iq_rx), cctlp->io_base+IQSCC2RXBAR);
		writel(virt_to_bus(dctlp->iq_tx), cctlp->io_base+IQSCC2TXBAR);
		l &= 0xff0fff0f;
		l |= (((cctlp->cfg.iqlen/32)-1)*IQSCC2RXLEN) | (((cctlp->cfg.iqlen/32)-1)*IQSCC2TXLEN);
		break;
	case 3:	writel(MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH3CFG);
		writel(virt_to_bus(dctlp->iq_rx), cctlp->io_base+IQSCC3RXBAR);
		writel(virt_to_bus(dctlp->iq_tx), cctlp->io_base+IQSCC3TXBAR);
		l &= 0xfff0fff0;
		l |= (((cctlp->cfg.iqlen/32)-1)*IQSCC3RXLEN) | (((cctlp->cfg.iqlen/32)-1)*IQSCC3TXLEN);
		break;
	}
	writel(l, cctlp->io_base+IQLENR1);
	spin_unlock_irq(&dctlp->dev_lock);
	start_time = jiffies;
	cctlp->mailbox = MAILBOX_NONE;
	writel(AR	/* Action Request */
		| (channel == 0 ? (CFGIQSCC0RX | CFGIQSCC0TX) : 0)
		| (channel == 1 ? (CFGIQSCC1RX | CFGIQSCC1TX) : 0)
		| (channel == 2 ? (CFGIQSCC2RX | CFGIQSCC2TX) : 0)
		| (channel == 3 ? (CFGIQSCC3RX | CFGIQSCC3TX) : 0), cctlp->io_base+GCMDR);
	do {
		gcc_optimizer_safe=(jiffies-start_time)<20 && !cctlp->mailbox;
	} while (gcc_optimizer_safe);	/* timeout 20 jiffies */
	switch (cctlp->mailbox) {	/* mailbox was written by isr */
	case MAILBOX_OK:
#ifdef PCISCC_DEBUG
		printk(KERN_INFO "PCISCC: channel_open(): Success on IQSCC config request.\n");
#endif
		break;
	case MAILBOX_NONE: 
		printk(KERN_ERR "PCISCC: channel_open(): Timeout on IQSCC config request. Sync HDDs and hardware-reset NOW!\n");
		pciscc_channel_close(dctlp);
		return -EIO;
	case MAILBOX_FAILURE:
		printk(KERN_ERR "PCISCC: channel_open(): Failure on IQSCC config request. Sync HDDs and hardware-reset NOW!\n");
		pciscc_channel_close(dctlp);
		return -EIO;
	}
	/* initialize channel's SCC core */
	spin_lock_irq(&dctlp->dev_lock);
	dctlp->ccr0 = PU
		| ((dctlp->cfg.coding == CFG_CHCODE_NRZ) ? 0*SC : 0)
		| ((dctlp->cfg.coding == CFG_CHCODE_NRZI) ? 2*SC : 0)
		| ((dctlp->cfg.coding == CFG_CHCODE_FM0) ? 4*SC : 0)
		| ((dctlp->cfg.coding == CFG_CHCODE_FM1) ? 5*SC : 0)
		| ((dctlp->cfg.coding == CFG_CHCODE_MANCH) ? 6*SC : 0)
		| VIS
		| ((dctlp->cfg.dpll & CFG_DPLL_PS) ? 0 : PSD)
		| ((dctlp->cfg.clkout & CFG_TXTXCLK) ? TOE : 0)
		| ((dctlp->cfg.clockmode == CFG_CM_G3RUH) ? SSEL : 0)
		| ((dctlp->cfg.clockmode == CFG_CM_TCM3105) ? SSEL : 0)
		| ((dctlp->cfg.clockmode == CFG_CM_HS) ? HS : 0)
		| ((dctlp->cfg.clockmode == CFG_CM_DF9IC) ? 0*CM : 0)	/* clockmode 0a */
		| ((dctlp->cfg.clockmode == CFG_CM_G3RUH) ? 0*CM : 0)	/* clockmode 0b */
		| ((dctlp->cfg.clockmode == CFG_CM_TCM3105) ? 6*CM : 0)	/* clockmode 6b */
		| ((dctlp->cfg.clockmode == CFG_CM_HS) ? 4*CM : 0);	/* clockmode 4  */
	writel(dctlp->ccr0, cctlp->io_base+SCCBASE[channel]+CCR0);
	dctlp->ccr1 = (dctlp->cfg.datainv ? DIV : 0)
		| ((dctlp->cfg.txddrive & CFG_TXDDRIVE_TP) ? ODS : 0)
		| (dctlp->cfg.cdinv ? 0 : ICD)
		| ((dctlp->cfg.clkout & CFG_TXRTS) ? TCLKO : 0)
		| ((dctlp->cfg.txdelmode == CFG_TXDEL_HARD) ? 0 : FCTS)
		| MDS1
		| (dctlp->cfg.testloop ? TLP : 0)
		| (dctlp->cfg.sharedflg ? SFLAG : 0)
		| ((dctlp->cfg.crcmode & CFG_CRCMODE_RESET_0000) ? CRL : 0)
		| ((dctlp->cfg.crcmode & CFG_CRCMODE_CRC32) ? C32 : 0);
	writel(dctlp->ccr1, cctlp->io_base+SCCBASE[channel]+CCR1);
	dctlp->ccr2 = RAC
		| ((dctlp->cfg.crcmode & CFG_CRCMODE_RXCD) ? DRCRC : 0)
		| ((dctlp->cfg.crcmode & CFG_CRCMODE_RXCRCFWD) ? RCRC : 0)
		| ((dctlp->cfg.crcmode & CFG_CRCMODE_TXNOCRC) ? XCRC : 0)
	        | (3*RFTH)  /* 24 dwords rx  treshold, fixed */
		| (dctlp->cfg.preamble*PRE)
		| (dctlp->cfg.preamb_rpt ? EPT : 0)
		| ((dctlp->cfg.preamb_rpt == 2) ? PRE0 : 0)
		| ((dctlp->cfg.preamb_rpt == 4) ? PRE1 : 0)
		| ((dctlp->cfg.preamb_rpt == 8) ? PRE0|PRE1 : 0)
		| ((dctlp->cfg.hdlcext & CFG_HDLCEXT_ONEFILL) ? 0 : ITF)
		| ((dctlp->cfg.hdlcext & CFG_HDLCEXT_ONEINS) ? OIN : 0);
	writel(dctlp->ccr2, cctlp->io_base+SCCBASE[channel]+CCR2);
	writel((dctlp->cfg.brate_m*BRM) | (dctlp->cfg.brate_n*BRN), cctlp->io_base+SCCBASE[channel]+BRR);
	writel(RCE | (dctlp->dev.mtu*RL), cctlp->io_base+SCCBASE[channel]+RLCR);
	/*
	 * all sent | tx device underrun | timer int | tx message repeat |
	 * tx pool ready | rx device overflow | receive FIFO overflow |
	 * frame length exceeded => interrupt mask register
	 */
	writel(~(ALLS | XDU | TIN | XMR | XPR | RDO | RFO | FLEX), cctlp->io_base+SCCBASE[channel]+IMR);
	spin_unlock_irq(&dctlp->dev_lock);
	/* wait until command_executing (CEC) is clear */
	start_time=jiffies;
	do {
		l=readl(cctlp->io_base+SCCBASE[channel]+STAR);
		gcc_optimizer_safe=(jiffies-start_time)<20 && (l & CEC);
	} while (gcc_optimizer_safe);
	if (l & CEC) {
		/* not ready, but we will execute reset anyway */
		printk(KERN_ERR "PCISCC: channel_open(): Timeout waiting for SCC being ready for reset.\n");
	}
	/* execute channel's SCC core RX and TX reset */
	writel(RRES | XRES, cctlp->io_base+SCCBASE[channel]+CMDR);
	start_time = jiffies;
	dctlp->tx_mailbox = 0xffffffff;
	do {
		gcc_optimizer_safe=(jiffies-start_time)<20 && (dctlp->tx_mailbox==0xffffffff);
	} while (gcc_optimizer_safe);	/* timeout 20 jiffies */
	/* mailbox was written by isr */
	if ((dctlp->tx_mailbox & 0x03ffffff) == 0x02001000) {
		/* SCC XPR interrupt received */
#ifdef PCISCC_DEBUG
		printk(KERN_INFO "PCISCC: channel_open(): Success on SCC reset.\n");
#endif
	} else if (dctlp->tx_mailbox == 0xffffffff) { 
		printk(KERN_ERR "PCISCC: channel_open(): Timeout on SCC reset. Clocking problem?\n");
	} else {
		printk(KERN_ERR "PCISCC: channel_open(): Failure on SCC reset: mailbox=0x%0lx.\n", dctlp->tx_mailbox);
	}
	/* 
	 * Prepare circular RX and TX descriptor queues ("FIFO" rings)
	 * Attention:
	 * This beast gets _very_ angry if you try to hand it a
	 * descriptor with a data length of 0. In fact it crashes
	 * the system by asserting /SERR or something.
	 */
	spin_lock_irq(&dctlp->dev_lock);
	rdp = last_rdp = NULL;
	for (i=0; i<cctlp->cfg.rxbufcnt; i++, last_rdp=rdp) {
		if (!(rdp=kmalloc(sizeof(struct rx_desc_t), GFP_DMA | GFP_KERNEL))) {
			printk(KERN_ERR "PCISCC: channel_open(): Out of memory allocating rx descriptor chain.\n");
			spin_unlock_irq(&dctlp->dev_lock);
			pciscc_channel_close(dctlp);
			return -ENOMEM;
		}
		memset(rdp, 0, sizeof(struct rx_desc_t));
		if (i==0) {
			dctlp->dq_rx=rdp;	/* queue (ring) "head" */
		} else {
			rdp->prev=last_rdp;
			last_rdp->next=rdp;
			last_rdp->nextptr=(void *) virt_to_bus(rdp);
		}
		if (!(rdp->skb=alloc_skb(dctlp->dev.mtu+10+SKB_HEADROOM, GFP_DMA | GFP_KERNEL))) {
			printk(KERN_ERR "PCISCC: channel_open(): Out of memory allocating socket buffers.\n");
			spin_unlock_irq(&dctlp->dev_lock);
			pciscc_channel_close(dctlp);
			return -ENOMEM;
		}
		skb_reserve(rdp->skb, SKB_HEADROOM);
		rdp->dataptr=(void *) virt_to_bus(data=skb_put(rdp->skb, dctlp->dev.mtu+10));	/* we will skb_trim() it after */
		rdp->flags=(dctlp->dev.mtu*NO);							/* reception when we know frame length */
	}
	rdp->next=dctlp->dq_rx;		/* close ring structure */
	rdp->nextptr=(void *) virt_to_bus(dctlp->dq_rx);
	dctlp->dq_rx->prev=rdp;
	dctlp->dq_rx_next=dctlp->dq_rx;		/* first descriptor to be processed = "first" descriptor in chain */
	/* TX queue */
	tdp = last_tdp = NULL;
	for (i=0; i<cctlp->cfg.txbufcnt; i++, last_tdp=tdp) {
		if (!(tdp=kmalloc(sizeof(struct tx_desc_t), GFP_DMA | GFP_KERNEL))) {
			printk(KERN_ERR "PCISCC: channel_open(): Out of memory allocating tx descriptor chain.\n");
			spin_unlock_irq(&dctlp->dev_lock);
			pciscc_channel_close(dctlp);
			return -ENOMEM;
		}
		memset(tdp, 0, sizeof(struct tx_desc_t));
		if (i==0) {
			dctlp->dq_tx=tdp;
		} else {
			tdp->prev=last_tdp;
			last_tdp->next=tdp;
			last_tdp->nextptr=(void *) virt_to_bus(tdp);
		}
		tdp->skb=NULL;
		tdp->dataptr=(void *) virt_to_bus(dummybuf);
		tdp->flags=(8*NO) | FE;
	}
	tdp->next=dctlp->dq_tx;		/* close ring structure */
	tdp->nextptr=(void *) virt_to_bus(dctlp->dq_tx);
	dctlp->dq_tx->prev=tdp;
	dctlp->dq_tx_last=dctlp->dq_tx;			/* last descriptor to be transmitted */
	dctlp->dq_tx_cleanup=dctlp->dq_tx;		/* first descriptor to be cleaned up after transmission */
	flush_cache_all();
	/* initialize DMAC channel's RX */
	switch (channel) {
	case 0:	writel(IDR, cctlp->io_base+CH0CFG);
		writel(virt_to_bus(dctlp->dq_rx), cctlp->io_base+CH0BRDA);
		writel(virt_to_bus(dctlp->dq_rx), cctlp->io_base+CH0FRDA);
		writel(virt_to_bus(dctlp->dq_rx_next->prev->prev), cctlp->io_base+CH0LRDA);
		break;
	case 1:	writel(IDR, cctlp->io_base+CH1CFG);
		writel(virt_to_bus(dctlp->dq_rx), cctlp->io_base+CH1BRDA);
		writel(virt_to_bus(dctlp->dq_rx), cctlp->io_base+CH1FRDA);
		writel(virt_to_bus(dctlp->dq_rx_next->prev->prev), cctlp->io_base+CH1LRDA);
		break;
	case 2:	writel(IDR, cctlp->io_base+CH2CFG);
		writel(virt_to_bus(dctlp->dq_rx), cctlp->io_base+CH2BRDA);
		writel(virt_to_bus(dctlp->dq_rx), cctlp->io_base+CH2FRDA);
		writel(virt_to_bus(dctlp->dq_rx_next->prev->prev), cctlp->io_base+CH2LRDA);
		break;
	case 3:	writel(IDR, cctlp->io_base+CH3CFG);
		writel(virt_to_bus(dctlp->dq_rx), cctlp->io_base+CH3BRDA);
		writel(virt_to_bus(dctlp->dq_rx), cctlp->io_base+CH3FRDA);
		writel(virt_to_bus(dctlp->dq_rx_next->prev->prev), cctlp->io_base+CH3LRDA);
		break;
	}
	spin_unlock_irq(&dctlp->dev_lock);
	start_time=jiffies;
	cctlp->mailbox=MAILBOX_NONE;
	writel(AR, cctlp->io_base+GCMDR);
	do {
		gcc_optimizer_safe=(jiffies-start_time)<20 && !cctlp->mailbox;
	} while (gcc_optimizer_safe);
	switch (cctlp->mailbox) {	/* mailbox was written by isr */
	case MAILBOX_OK:
#ifdef PCISCC_DEBUG
		printk(KERN_INFO "PCISCC: channel_open(): Success on DMAC-RX config request.\n");
#endif
		dctlp->dmac_rx=DMAC_RX_INIT;
		break;
	case MAILBOX_NONE: 
		printk(KERN_ERR "PCISCC: channel_open(): Timeout on DMAC-RX config request. Sync HDDs and hardware-reset NOW!\n");
		break;
	case MAILBOX_FAILURE:
		printk(KERN_ERR "PCISCC: channel_open(): Failure on DMAC-RX config request. Sync HDDs and hardware-reset NOW!\n");
		break;
	}
	/* mask all DMAC interrupts (needed) */
	switch (channel) {
	case 0:	writel(MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH0CFG);
		break;
	case 1:	writel(MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH1CFG);
		break;
	case 2:	writel(MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH2CFG);
		break;
	case 3:	writel(MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH3CFG);
		break;
	}
	/* SCC core TX reset (again) */
	start_time=jiffies;
	do {
		l=readl(cctlp->io_base+SCCBASE[channel]+STAR);
		gcc_optimizer_safe=(jiffies-start_time)<20 && (l & CEC);
	} while (gcc_optimizer_safe);
	if (l & CEC) {
		/* not ready, but we will execute reset anyway */
		printk(KERN_ERR "PCISCC: channel_open(): Timeout waiting for SCC being ready for TX-reset.\n");
	}
	writel(XRES, cctlp->io_base+SCCBASE[channel]+CMDR);
	start_time = jiffies;
	dctlp->tx_mailbox = 0xffffffff;
	do {
		gcc_optimizer_safe=(jiffies-start_time)<20 && (dctlp->tx_mailbox==0xffffffff);
	} while (gcc_optimizer_safe);	/* timeout 20 jiffies */
	/* mailbox was written by isr */
	if ((dctlp->tx_mailbox & 0x03ffffff) == 0x02001000) {
		/* SCC XPR interrupt received */
#ifdef PCISCC_DEBUG
		printk(KERN_INFO "PCISCC: channel_open(): Success on SCC TX-reset.\n");
#endif
	} else if (dctlp->tx_mailbox == 0xffffffff) { 
		printk(KERN_ERR "PCISCC: channel_open(): Timeout on SCC TX-reset. Clocking problem?\n");
	} else {
		printk(KERN_ERR "PCISCC: channel_open(): Failure on SCC TX-reset: mailbox=0x%0lx.\n", dctlp->tx_mailbox);
	}
	/*
	 * initialize DMAC's TX channel, FI must stay masked all the time
	 * even during operation, see device errata 03/99
	 */
	switch (channel) {
	case 0:	writel(IDT | MTFI, cctlp->io_base+CH0CFG);
		writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH0BTDA);
		writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH0FTDA);
		writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH0LTDA);
		break;
	case 1:	writel(IDT | MTFI, cctlp->io_base+CH1CFG);
		writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH1BTDA);
		writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH1FTDA);
		writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH1LTDA);
		break;
	case 2:	writel(IDT | MTFI, cctlp->io_base+CH2CFG);
		writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH2BTDA);
		writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH2FTDA);
		writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH2LTDA);
		break;
	case 3:	writel(IDT | MTFI, cctlp->io_base+CH3CFG);
		writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH3BTDA);
		writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH3FTDA);
		writel(virt_to_bus(dctlp->dq_tx), cctlp->io_base+CH3LTDA);
		break;
	}
	start_time=jiffies;
	cctlp->mailbox=MAILBOX_NONE;
	writel(AR, cctlp->io_base+GCMDR);
	do {
		gcc_optimizer_safe=(jiffies-start_time)<20 && !cctlp->mailbox;
	} while (gcc_optimizer_safe);
	switch (cctlp->mailbox) {	/* mailbox was written by isr */
	case MAILBOX_OK:
#ifdef PCISCC_DEBUG
		printk(KERN_INFO "PCISCC: channel_open(): Success on DMAC-TX config request.\n");
#endif
		break;
	case MAILBOX_NONE: 
		printk(KERN_ERR "PCISCC: channel_open(): Timeout on DMAC-TX config request. Sync HDDs and hardware-reset NOW!\n");
		break;
	case MAILBOX_FAILURE:
		printk(KERN_ERR "PCISCC: channel_open(): Failure on DMAC-TX config request. Sync HDDs and hardware-reset NOW!\n");
		break;
	}
       	pciscc_set_txstate(dctlp, TX_IDLE);
	flush_cache_all();
#ifdef PCISCC_DEBUG
	pciscc_dmac_regdump(cctlp);
	pciscc_queuedump(dctlp);
#endif
	dctlp->chip->usecnt++;
	dctlp->dev.start = 1;
	dctlp->dev.tbusy = 0;
	/* clear statistics */
	mdelay(10);
	memset(&dctlp->stats, 0, sizeof(struct net_device_stats));
	/* some housekeeping */
	return 0;
}

/* --------------------------------------------------------------------------------------------- */

/* close one channel - don't mess with it either */
static void pciscc_channel_close(struct devctl_t *dctlp)
{
	struct chipctl_t *cctlp = dctlp->chip;
	int channel = dctlp->channel;
	struct rx_desc_t *rdp, *last_rdp;
	struct tx_desc_t *tdp, *last_tdp;
	unsigned long l;
	unsigned long start_time;
	volatile unsigned long gcc_optimizer_safe;

#ifdef PCISCC_DEBUG
	pciscc_dmac_regdump(cctlp);
	pciscc_queuedump(dctlp);
#endif
	/* at first stop timer */
	writel(0, cctlp->io_base+SCCBASE[channel]+TIMR);
	/* wait until command_executing (CEC) is clear */
	start_time=jiffies;
	do {
		l=readl(cctlp->io_base+SCCBASE[channel]+STAR);
		gcc_optimizer_safe=(jiffies-start_time)<20 && (l & CEC);
	} while (gcc_optimizer_safe);
	if (l & CEC) {
		/* not ready, but we will execute reset anyway */
		printk(KERN_ERR "PCISCC: channel_close(): Timeout waiting for SCC being ready for reset.\n");
	}
	/* RX and TX SCC reset */
	writel(RRES | XRES, cctlp->io_base+SCCBASE[channel]+CMDR);
	start_time = jiffies;
	dctlp->tx_mailbox = 0xffffffff;
	do {
		gcc_optimizer_safe=(jiffies-start_time)<20 && (dctlp->tx_mailbox==0xffffffff);
	} while (gcc_optimizer_safe);	/* timeout 20 jiffies */
	/* mailbox was written by isr */
	if ((dctlp->tx_mailbox & 0x03ffffff) == 0x02001000) {
	/* SCC XPR interrupt received */
#ifdef PCISCC_DEBUG
		printk(KERN_INFO "PCISCC: channel_close(): Success on SCC reset.\n");
#endif
	} else if (dctlp->tx_mailbox == 0xffffffff) {
		printk(KERN_ERR "PCISCC: channel_close(): Timeout on SCC reset.\n");
	} else {
		printk(KERN_ERR "PCISCC: channel_close(): Failure on SCC reset: mailbox=0x%0lx.\n", dctlp->tx_mailbox);
	}
	/* stop SCC core */
	writel(0, cctlp->io_base+SCCBASE[channel]+CCR1);
	writel(0, cctlp->io_base+SCCBASE[channel]+CCR2);
	writel(0, cctlp->io_base+SCCBASE[channel]+CCR0);
	dctlp->ccr0 = dctlp->ccr1 = dctlp->ccr2 = 0;
	/*
	 * Give the isr some time to "refill" the rx-dq
	 * we _MUST_ guarantee that the DMAC-RX is _NOT_ in
	 * hold state when issuing the RESET command, otherwise the DMAC
	 * will crash. (DSCC-4 Rev. <= 2.1)
	 * In addition to that we may only issue a RESET if channel is
	 * currently really initialized, otherwise something horrible will
	 * result.
	 */
	start_time=jiffies;
	do {
		gcc_optimizer_safe=(jiffies-start_time)<5;
	} while (gcc_optimizer_safe);
	/* OK, now we should be ready to put the DMAC into reset state */
	switch (channel) {
	case 0:	writel(RDR | RDT | MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH0CFG);
		break;
	case 1:	writel(RDR | RDT | MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH1CFG);
		break;
	case 2:	writel(RDR | RDT | MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH2CFG);
		break;
	case 3:	writel(RDR | RDT | MRFI | MTFI | MRERR | MTERR, cctlp->io_base+CH3CFG);
		break;
	}
	start_time = jiffies;
	cctlp->mailbox = MAILBOX_NONE;
	writel(AR, cctlp->io_base+GCMDR);
	do {
		gcc_optimizer_safe=(jiffies-start_time)<20 && !cctlp->mailbox;
	} while (gcc_optimizer_safe);	/* timeout 20 jiffies */
	switch (cctlp->mailbox) {	/* mailbox was written by isr */
	case MAILBOX_OK:
#ifdef PCISCC_DEBUG
		printk(KERN_INFO "PCISCC: channel_close(): Success on DMAC reset channel %u.\n", channel);
#endif
		dctlp->dmac_rx=DMAC_RX_RESET;
		pciscc_set_txstate(dctlp, TX_RESET);
		break;
	case MAILBOX_NONE: 
		printk(KERN_ERR "PCISCC: channel_close(): Timeout on DMAC reset channel %u. Sync HDDs and hardware-reset NOW!\n", channel);
		break;
	case MAILBOX_FAILURE:
		printk(KERN_ERR "PCISCC: channel_close(): Failure on DMAC reset channel %u. Sync HDDs and hardware-reset NOW!\n", channel);
		break;
	}
	/* clear IQs */
	l = readl(cctlp->io_base+IQLENR1);
	switch (channel) {
	case 0:	l &= 0x0fff0fff;
		writel(l, cctlp->io_base+IQLENR1);
		writel(virt_to_bus(dummybuf), cctlp->io_base+IQSCC0RXBAR);
		writel(virt_to_bus(dummybuf), cctlp->io_base+IQSCC0TXBAR);
		break;
	case 1: l &= 0xf0fff0ff;
		writel(l, cctlp->io_base+IQLENR1);
		writel(virt_to_bus(dummybuf), cctlp->io_base+IQSCC1RXBAR);
		writel(virt_to_bus(dummybuf), cctlp->io_base+IQSCC1TXBAR);
		break;
	case 2: l &= 0xff0fff0f;
		writel(l, cctlp->io_base+IQLENR1);
		writel(virt_to_bus(dummybuf), cctlp->io_base+IQSCC2RXBAR);
		writel(virt_to_bus(dummybuf), cctlp->io_base+IQSCC2TXBAR);
		break;
	case 3: l &= 0xfff0fff0;
		writel(l, cctlp->io_base+IQLENR1);
		writel(virt_to_bus(dummybuf), cctlp->io_base+IQSCC3RXBAR);
		writel(virt_to_bus(dummybuf), cctlp->io_base+IQSCC3TXBAR);
		break;
	}
	start_time = jiffies;
	cctlp->mailbox = MAILBOX_NONE;
	writel(AR
		| (channel == 0 ? (CFGIQSCC0RX | CFGIQSCC0TX) : 0)
		| (channel == 1 ? (CFGIQSCC1RX | CFGIQSCC1TX) : 0)
		| (channel == 2 ? (CFGIQSCC2RX | CFGIQSCC2TX) : 0)
		| (channel == 3 ? (CFGIQSCC3RX | CFGIQSCC3TX) : 0), cctlp->io_base+GCMDR);
	do {
		gcc_optimizer_safe=(jiffies-start_time)<20 && !cctlp->mailbox;
	} while (gcc_optimizer_safe);	/* timeout 20 jiffies */
	switch (cctlp->mailbox) {	/* mailbox was written by isr */
	case MAILBOX_OK:
#ifdef PCISCC_DEBUG
		printk(KERN_INFO "PCISCC: channel_close(): Success on IQSCC config request.\n");
#endif
		break;
	case MAILBOX_NONE: 
		printk(KERN_ERR "PCISCC: channel_close(): Timeout on IQSCC config request. Sync HDDs and hardware-reset NOW!\n");
		break;
	case MAILBOX_FAILURE:
		printk(KERN_ERR "PCISCC: channel_close(): Failure on IQSCC config request. Sync HDDs and hardware-reset NOW!\n");
		break;
	}
	if (dctlp->dq_rx) {
		rdp=dctlp->dq_rx;		/* free descriptor chains and buffers */
		do {
			if (rdp->skb) {
				kfree_skb(rdp->skb);
				rdp->skb=NULL;
			}
			last_rdp=rdp;
			rdp=rdp->next;
			kfree(last_rdp);
		} while (rdp!=dctlp->dq_rx);
		dctlp->dq_rx=NULL;
	}
	dctlp->dq_rx_next=NULL;
	if (dctlp->dq_tx) {
		tdp=dctlp->dq_tx;
		do {
			if (tdp->skb) {
				kfree_skb(tdp->skb);
				tdp->skb=NULL;
			}
			last_tdp=tdp;
			tdp=tdp->next;
			kfree(last_tdp);
		} while (tdp!=dctlp->dq_tx);
		dctlp->dq_tx=NULL;
	}
	dctlp->dq_tx_cleanup=NULL;
	dctlp->dq_tx_last=NULL;
	if (dctlp->iq_rx) {		/* free IQs */
		kfree(dctlp->iq_rx);
		dctlp->iq_rx=NULL;
	}
	if (dctlp->iq_tx) {
		kfree(dctlp->iq_tx);
		dctlp->iq_tx=NULL;
	}
	dctlp->dev.start=0;
	dctlp->chip->usecnt--;
	return;
}

/* --------------------------------------------------------------------------------------------- */

/* interrupt handler root */
static void pciscc_isr(int irq, void *dev_id, struct pt_regs *regs)
{
	struct chipctl_t *cctlp = (struct chipctl_t *) dev_id;
	struct devctl_t *dctlp;
	unsigned long status;
	unsigned long iv;
	int channel;
	unsigned long * volatile iqp;
	int processed;
	int i;

	status = readl(cctlp->io_base+GSTAR);
	writel(status, cctlp->io_base+GSTAR);	/* ack' irq */
	rmb();
	wmb();
	if (!status) return;
	/* do not disturb... */
	spin_lock(&cctlp->chip_lock);
	ATOMICY_CHECK;
	if (status & (IIPGPP | IIPLBI | IIPSSC)) {
		/* process peripheral queue */
		processed = 0;
		iqp  = cctlp->iq_per_next;
		while ((iv = *iqp) != 0) {
			*iqp = 0;
			flush_cache_all();
			rmb();
			wmb();
			printk(KERN_INFO "PCISCC: isr: IQPER vector: 0x%0lx.\n", iv);
			iqp = ((iqp==(cctlp->iq_per+cctlp->cfg.iqlen-1)) ? cctlp->iq_per : iqp+1);  /* wrap-arround */
			processed++;
		}
		cctlp->iq_per_next = iqp;
	}
	if (status & IICFG) {
		/* process configuration queue */
		cctlp->mailbox = MAILBOX_NONE;
		processed = 0;
		iqp = cctlp->iq_cfg_next;
		while ((iv = *iqp) != 0) {
			*iqp = 0;
			flush_cache_all();
			rmb();
			wmb();
#ifdef PCISCC_DEBUG
			printk(KERN_INFO "PCISCC: isr: IQCFG vector: 0x%0lx.\n", iv);
#endif
			if ((iv) & ARACK) {
				cctlp->mailbox = MAILBOX_OK;
				processed++;
			}
			if ((iv) & ARF) {
				cctlp->mailbox = MAILBOX_FAILURE;
				processed++;
			}
			iqp = ((iqp==(cctlp->iq_cfg+cctlp->cfg.iqlen-1)) ? cctlp->iq_cfg : iqp+1);  /* wrap-around */
		}
		cctlp->iq_cfg_next = iqp;
		if (processed != 1) {
			printk(KERN_ERR "PCISCC: isr: Something weird going on... IICFG:processed=%u.\n", processed);
		}
	}
	for (channel=0; channel<4; channel++) if (status & (1<<(24+channel))) {
		/* process TX queue */
		dctlp=cctlp->device[channel];
		if (!dctlp->iq_tx || !dctlp->iq_tx_next) continue;
		processed = 0;
		iqp = dctlp->iq_tx_next;
		while ((iv = *iqp) != 0) {
			*iqp = 0;
			flush_cache_all();
			rmb();
			wmb();
			if (iv & TIN) {
				/* timer interrupt */
				writel(0, cctlp->io_base+SCCBASE[channel]+TIMR);
				/* now, which state are we in? */
				switch (dctlp->txstate) {
				case TX_DELAY:
					/* data transmit */
					pciscc_set_txstate(dctlp, TX_XMIT);
					switch (channel) {
					case 0:	writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH0LTDA);
						break;
					case 1:	writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH1LTDA);
						break;
					case 2:	writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH2LTDA);
						break;
					case 3:	writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH3LTDA);
						break;
					}
					writel(txtimeout*dctlp->tx_bitrate*TVALUE, cctlp->io_base+SCCBASE[channel]+TIMR);
					writel(STI, cctlp->io_base+SCCBASE[channel]+CMDR);
					break;
				case TX_TAIL:
					/* transmitting tail */
					pciscc_set_txstate(dctlp, TX_IDLE);
					break;
				case TX_PROBE:
					/* tx bitrate test in execution */
					do_gettimeofday(&dctlp->tv);
					pciscc_set_txstate(dctlp, TX_RESET);
					dctlp->probe_mailbox=1;
					break;
				case TX_CAL:
					/* we are (i.e. were) calibrating */
					if (dctlp->dq_tx_last != dctlp->dq_tx_cleanup) {
						/* we have something in the tx queue */
						pciscc_set_txstate(dctlp, TX_XMIT);
						switch (channel) {
						case 0:	writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH0LTDA);
							break;
						case 1:	writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH1LTDA);
							break;
						case 2:	writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH2LTDA);
							break;
						case 3:	writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH3LTDA);
							break;
						}
						writel(txtimeout*dctlp->tx_bitrate*TVALUE, cctlp->io_base+SCCBASE[channel]+TIMR);
						writel(STI, cctlp->io_base+SCCBASE[channel]+CMDR);
					} else {
						pciscc_set_txstate(dctlp, TX_IDLE);
					}
					break;
				case TX_XMIT:
					/* watchdog just ran out */
					pciscc_set_txstate(dctlp, TX_IDLE);
					txto_task.routine=pciscc_bh_txto;
					txto_task.data=(void *) dctlp;
					queue_task(&txto_task, &tq_scheduler);
					break;
				default:
#ifdef PCISCC_DEBUG
					printk(KERN_ERR "PCISCC: isr: Timer interrupt while txstate=%u.\n", dctlp->txstate);
#endif
					pciscc_set_txstate(dctlp, TX_IDLE);
				}
			}
			if (iv & ALLS) {
				/* a TX frame was just completed */
				pciscc_isr_txcleanup(dctlp);
				if ((dctlp->dq_tx_cleanup == dctlp->dq_tx_last) && (dctlp->txstate != TX_PROBE)) {
					/* complete TX-queue sent out */
					if (dctlp->cfg.duplex == CFG_DUPLEX_FULLPTT) {
						/* just update txstate */
						pciscc_set_txstate(dctlp, TX_IDLE);
					} else if (dctlp->cfg.txdelmode == CFG_TXDEL_SOFT) {
						/* "normal" full duplex mode or half duplex and soft TXDELAY: start txtail */
						pciscc_set_txstate(dctlp, TX_TAIL);
						writel(dctlp->cfg.txtailval*TVALUE, cctlp->io_base+SCCBASE[channel]+TIMR);
						writel(STI, cctlp->io_base+SCCBASE[channel]+CMDR);
					} else if (dctlp->cfg.txdelmode == CFG_TXDEL_HARD) {
						/* will deassert RTS immediately */
						pciscc_set_txstate(dctlp, TX_IDLE);
					}
				}
			}
			if (iv & XDU) {
				/*
				 * TX stall - now we are _really_ in trouble.
				 * We must reset the SCC core and re-init DMAC-TX.
				 * This includes delay loops and we are in interrupt
				 * context, with all interrupts disabled... So we need
				 * to schedule a bottom half task for this.
				 */
#ifdef PCISCC_DEBUG
			        printk(KERN_ERR "PCISCC: isr: TX data underrun occured iface=%s.\n", dctlp->name);
#endif
				dctlp->stats.tx_fifo_errors++;
				txreset_task.routine=pciscc_bh_txreset;
				txto_task.data=(void *) dctlp;
				queue_task(&txreset_task, &tq_scheduler);
			}
			if (iv & XMR) {
				/*
				 * TX message repeat - not critical, since
				 * resolved automagically by abort sequence
				 * and retransmit.
				 */
#ifdef PCISCC_DEBUG
		        	printk(KERN_ERR "PCISCC: isr: TX message repeat interrupt iface=%s.\n", dctlp->name);
#endif
			}
			dctlp->tx_mailbox = iv;
		        iqp = ((iqp==(dctlp->iq_tx+cctlp->cfg.iqlen-1)) ? dctlp->iq_tx : iqp+1);  /* wrap-arround */
			processed++;
		}
		dctlp->iq_tx_next = iqp;
#ifdef PCISCC_VDEBUG
		if (processed != 1)  printk(KERN_INFO "PCISCC: isr: TX: iface=%s processed=%u\n", dctlp->name, processed);
#endif
		if (processed == 0) {
		        /*
			 * If this happens we either already processed the IV belonging to this
			 * IRQ last time, or our honored DMAC messed up again and managed
			 * to "loose" this IV and advanced to the next position in the
			 * queue. We check for the latter case. If we didnt do that, TX would "hang".
			 */
		        for (i=1; i<cctlp->cfg.iqlen; i++) {
			        iqp = ((iqp==(dctlp->iq_tx+cctlp->cfg.iqlen-1)) ? dctlp->iq_tx : iqp+1);  /* wrap-arround */
				rmb();
				wmb();
				if ((*iqp) != 0 && (*(dctlp->iq_tx_next)) == 0) {  /* note order is important */
				        dctlp->iq_tx_next = iqp;
#ifdef PCISCC_DEBUG
					printk(KERN_INFO "PCISCC: isr: TX skipped %u IVs iface=%s.\n", i, dctlp->name);
#endif
					break;
				        /* next IRQ will clean it up now */
			        }
			}
		}
	}
	for (channel=0; channel<4; channel++) if (status & (1<<(28+channel))) {
		dctlp=cctlp->device[channel];
		/* process RX queue */
		if (!dctlp->iq_rx || !dctlp->iq_rx_next) {
			printk(KERN_ERR "PCISCC: isr: IQCHAN%uRX interrupt for non-initialized queue!\n", channel);
			continue;
		}
		processed = 0;
		iqp = dctlp->iq_rx_next;
		while ((iv = *iqp) != 0) {
			*iqp = 0;
			flush_cache_all();
			rmb();
			wmb();
			/* statistics only */
			if ((iv & SCCIV_SCC) && (iv & SCCIV_RDO)) {
				dctlp->stats.rx_fifo_errors++;
			}
			if ((iv & SCCIV_SCC) && (iv & SCCIV_RFO)) {
				dctlp->stats.rx_over_errors++;
#ifdef PCISCC_DEBUG
				if (dctlp->stats.rx_over_errors == 1) {
				        printk(KERN_ERR "PCISCC: First RFO IV occurred. iface=%s.\n", dctlp->name);
       				        pciscc_dmac_regdump(cctlp);
					for (i=0; i<4; i++) {
					        /* dump all interfaces since anyone could be responsible */
					        if (cctlp->device[i]->dev.start) pciscc_queuedump(cctlp->device[i]);
					}
				}
#endif
			}
			if ((iv & SCCIV_SCC) && (iv & SCCIV_FLEX)) {
				dctlp->stats.rx_length_errors++;
			}
			if (!(iv & SCCIV_SCC) && (iv & SCCIV_ERR)) {
				dctlp->stats.rx_errors++;
			}
			if (!(iv & SCCIV_SCC) && (iv & SCCIV_HI)) {
				printk(KERN_ERR "PCISCC: isr: Weird... received HI interrupt.\n");
			}
			if (!(iv & SCCIV_SCC) && (iv & SCCIV_FI)) {
			}
			dctlp->rx_mailbox = iv;
		        iqp = ((iqp==(dctlp->iq_rx+cctlp->cfg.iqlen-1)) ? dctlp->iq_rx : iqp+1);  /* wrap-around */
			processed++;
		}
		/* in any case check RX descriptor queue for received frames */
		if (dctlp->dev.start) pciscc_isr_receiver(dctlp);
		dctlp->iq_rx_next=iqp;
	}
	ATOMICY_CHECK_END;
	spin_unlock(&cctlp->chip_lock);
	return;
}

/* --------------------------------------------------------------------------------------------- */

/* called by interrupt handler root when RX interrupt occurred */
static __inline__ void pciscc_isr_receiver(struct devctl_t *dctlp)
{
	struct chipctl_t *cctlp = dctlp->chip;
	int channel = dctlp->channel;
	struct rx_desc_t * volatile rdp;
	long status;
	volatile unsigned char rdsb;	/* receive data status byte, generated by DMAC at buffer end */
	volatile int bno;
	volatile int valid;
	struct sk_buff *new_skb;
	int processed;

#ifdef PCISCC_DEBUG
	if (!dctlp->dev.start) {
		printk(KERN_INFO "PCISCC: isr_receiver: frame received while !dev->start.\n");
	}
#endif
	for (rdp=dctlp->dq_rx_next, processed=0; (rdp->result & C); rdp=rdp->next, processed++) {
#ifdef PCISCC_DEBUG
		if ((rdp->nextptr != (void *) virt_to_bus(rdp->next)) || (rdp->dataptr != (void *) virt_to_bus(rdp->skb->data))) {
			panic("PCISCC: isr_receiver(): mm fucked with our buffers");
		}
#endif
		status = rdp->result;
		bno = (status >> 16) & 0x1fff;
		valid = 1;	/* we assume frame valid */
		if ((status & RA) || (bno <= 0) || (bno > dctlp->dev.mtu) || !(status & FE) || (rdp->feptr != (void *) virt_to_bus(rdp))) {
			/* aborted or invalid length */
			valid = 0;
		} else {
			rdsb = rdp->skb->data[bno-1];
			if (!(rdsb & SB_VFR)) {		/* incorrect bit length */
				valid = 0;
				dctlp->stats.rx_frame_errors++;
			}
			if (rdsb & SB_RDO) {		/* data overflow */
				valid = 0;		/* alreadly counted */
			}
			if (!(rdsb & SB_CRC) && !(dctlp->cfg.crcmode & CFG_CRCMODE_RXCD)) {
				/* CRC error */
				valid = 0;
				dctlp->stats.rx_crc_errors++;
			}
			if (rdsb & SB_RAB) {		/* receive message aborted */
				valid = 0;
			}
		}
		/* OK, this is a little bit tricky. We have to make sure
		 * that every descriptor has a buffer assigned. Thus we
		 * can only release a buffer to the link layer if we get
		 * a new one in turn from mm before. */
		if (valid) {
			if ((new_skb = alloc_skb(dctlp->dev.mtu+10+SKB_HEADROOM, GFP_DMA | GFP_ATOMIC))) {
				skb_reserve(new_skb, SKB_HEADROOM);
				skb_trim(rdp->skb, bno-1);
				pciscc_rx_skb(rdp->skb, dctlp);
				rdp->skb = new_skb;
				rdp->dataptr=(void *) virt_to_bus(skb_put(rdp->skb, dctlp->dev.mtu+10));
			} else {
#ifdef PCISCC_DEBUG
				printk(KERN_INFO "PCISCC: isr_receiver: Out of memory allocating new skb!\n");
#endif
			}
		}
		rdp->flags=dctlp->dev.mtu*NO;	/* prepare descriptor for next time */
		rdp->result=0;
		rdp->feptr=0;
		flush_cache_all();
	}
#ifdef PCISCC_VDEBUG
	printk(KERN_INFO "PCISCC: isr_receiver: Processed %u frames at once.\n", processed);
#endif
	dctlp->dq_rx_next = rdp;
	wmb();	/* no instruction reordering beyond this point */
	/*
	 * tell DMAC last available descriptor - keep up one
	 * descriptor space for security (paranoia) (...->prev->prev)
	 */
	switch (channel) {
	case 0:	writel(virt_to_bus(rdp->prev->prev), cctlp->io_base+CH0LRDA);
		break;
	case 1:	writel(virt_to_bus(rdp->prev->prev), cctlp->io_base+CH1LRDA);
		break;
	case 2:	writel(virt_to_bus(rdp->prev->prev), cctlp->io_base+CH2LRDA);
		break;
	case 3:	writel(virt_to_bus(rdp->prev->prev), cctlp->io_base+CH3LRDA);
		break;
	}
	return;
}

/* --------------------------------------------------------------------------------------------- */

/* called by IRQ handler root when a TX descriptor was completed */
static int pciscc_isr_txcleanup(struct devctl_t *dctlp)
{
	struct tx_desc_t * volatile tdp;
	struct device *dev = &dctlp->dev;
	int processed;
	
	processed=0;
	tdp=dctlp->dq_tx_cleanup;
	while ((tdp->result & C) && (tdp != dctlp->dq_tx_last)) {
		/* clean up all (C)omplete descriptors */
		if (tdp->skb) {
#ifdef PCISCC_DEBUG
			if ((tdp->nextptr != (void *) virt_to_bus(tdp->next)) || (tdp->dataptr != (void *) virt_to_bus(tdp->skb->data))) {
				/*
				 * paranoia check -
				 * this should _never_ever_occur_ .
				 * if it does, the memory subsystem moved around
				 * our buffers in address space, and it's the
				 * last you will see.
				 */
				printk(KERN_ERR "PCISCC: isr_txcleanup(): mm fucked with our buffers.\n");
			}
#endif
			dctlp->stats.tx_packets++;
			dctlp->stats.tx_bytes += tdp->skb->len;
			kfree_skb(tdp->skb);
			tdp->skb = NULL;
		}
		tdp->flags = (FE | (NO*8));	/* dummy */
		tdp->dataptr = (void *) virt_to_bus(dummybuf);	/* paranoia */
		tdp->result = 0;
		tdp = tdp->next;
		processed++;
		if (processed>100) {
#ifdef PCISCC_DEBUG
			printk(KERN_ERR "PCISCC: trouble in isr_txcleanup or please reduce bit rate by 20 dB.\n");
			dctlp->dev.start=0;
#endif
			break;
		}
	}
	dctlp->dq_tx_cleanup = tdp;
	wmb();
	flush_cache_all();
#ifdef PCISCC_VDEBUG
	printk(KERN_INFO "PCISCC: isr_txcleanup: Processed %u frames.\n", processed);
#endif
	if (processed > 0) {
		dev->tbusy=0;
		mark_bh(NET_BH);
	}
	return processed;
}

/* --------------------------------------------------------------------------------------------- */

/*
 * BOTTOM HALF
 * Called by TIN ISR when TX timeout has occured (watchdog)
 */
static void pciscc_bh_txto(void *arg)
{
	struct devctl_t *dctlp = (struct devctl_t *) arg;

	printk(KERN_ERR "PCISCC: Taking interface %s down due to TX hang. Clocking problem?\n", dctlp->name);
	dev_close(&dctlp->dev);
	return;
}

/* --------------------------------------------------------------------------------------------- */

/*
 * BOTTOM HALF
 * Called by ISR root when TX underrun forces us to reset
 * a given channel's transmitter.
 */
static void pciscc_bh_txreset(void *arg)
{
	struct devctl_t *dctlp = (struct devctl_t *) arg;
	int channel = dctlp->channel;
	struct chipctl_t *cctlp = dctlp->chip;
	volatile unsigned long gcc_optimizer_safe;
	unsigned long start_time;

	/* reset SCC core TX */
	writel(XRES, cctlp->io_base+SCCBASE[channel]+CMDR);
	start_time = jiffies;
	do {
		gcc_optimizer_safe=((jiffies-start_time) < 20);
	} while (gcc_optimizer_safe);	/* 20 jiffies delay */
	return;
}

/* --------------------------------------------------------------------------------------------- */

/* *REALLY* ugly work-around for timer race */
static __inline__ void pciscc_clear_timer(struct devctl_t *dctlp)
{
	struct chipctl_t *cctlp = dctlp->chip;
	unsigned long * volatile iqp;

	/* walk through TX queue eliminating TINs 	FIXME */
	if (!dctlp->iq_tx || !dctlp->iq_tx_next) return;
	for (iqp=dctlp->iq_tx_next; *iqp!=0; iqp=((iqp==(dctlp->iq_tx+cctlp->cfg.iqlen-1)) ? dctlp->iq_tx : iqp+1)) {  /* note wrap-arround */
		if (*iqp & TIN) *iqp = SCCIV_IGNORE;
	}
	return;
}

/* --------------------------------------------------------------------------------------------- */

/*
 * probe TX bitrate of a channel, called from device_open()
 * idea behind it:
 * load the channels timer with a known value and measure how long it
 * takes to reach zero, using the system timer
 */
static long pciscc_probe_txrate(struct devctl_t *dctlp)
{
	struct chipctl_t *cctlp = dctlp->chip;
	struct timeval tv_start;
	volatile unsigned long gcc_optimizer_safe;
	unsigned long start_time;
	long delta_us;
	unsigned long long tx_bitrate;

	pciscc_set_txstate(dctlp, TX_PROBE);
	dctlp->probe_mailbox = 0;
	start_time = jiffies;
	writel((probebit*TVALUE), cctlp->io_base+SCCBASE[dctlp->channel]+TIMR);
	do_gettimeofday(&tv_start);
	writel(STI, cctlp->io_base+SCCBASE[dctlp->channel]+CMDR);
	do {
		gcc_optimizer_safe = (dctlp->probe_mailbox != 1) && ((jiffies-start_time)<1000);
	} while (gcc_optimizer_safe);
	pciscc_set_txstate(dctlp, TX_IDLE);
	if (dctlp->probe_mailbox != 1)  {
		printk(KERN_ERR "PCISCC: probe_txrate(): Timeout probing %s-TxClk. Clocking problem?\n", dctlp->dev.name);
		return 9600;   /* default */
	} else {
		delta_us = (dctlp->tv.tv_sec - tv_start.tv_sec)*1000000+(dctlp->tv.tv_usec - tv_start.tv_usec);
	}
	tx_bitrate = 10000*probebit/(delta_us/100);
#ifdef PCISCC_DEBUG	
	printk(KERN_INFO "PCISCC: probe_txrate(): tx_bitrate=%ld.\n", (long) tx_bitrate);
#endif
	return tx_bitrate;
}

/* --------------------------------------------------------------------------------------------- */

/*
 * Set new transmitter state.
 * This function will be called from various places and is responsible for
 * keeping the dctlp->txstate variable and RTS line up to date.
 * Call only when holding big device lock and with IRQs off to avoid race conditions.
 * Note we may not read back SCC core register contents because of a chip bug.
 */
static __inline__ void pciscc_set_txstate(struct devctl_t *dctlp, int state)
{
	struct chipctl_t *cctlp = dctlp->chip;
	int channel = dctlp->channel;
	int ptt;

#ifdef PCISCC_DEBUG
	/* paranoia */
	if (state < TX_MIN || state > TX_MAX) {
		printk(KERN_ERR "PCISCC: pciscc_set_txstate(%s, %d) illegal state.\n", dctlp->name, state);
		state = TX_RESET;
	}
#endif
	switch (state) {
	case TX_RESET:
		ptt = 0;
	case TX_IDLE:
		if (dctlp->cfg.duplex == CFG_DUPLEX_FULLPTT) {
			ptt = 1;
		} else {
			ptt = 0;
		}
		break;
	case TX_DELAY:
	case TX_XMIT:
	case TX_TAIL:
	case TX_PROBE:
	case TX_CAL:
	default:
		ptt = 1;
	}
       	writel(dctlp->ccr1 | (ptt ? RTS : 0), cctlp->io_base+SCCBASE[channel]+CCR1);
	dctlp->txstate = state;
	return;
}

/* --------------------------------------------------------------------------------------------- */

/* DDI support: immediately assert RTS, downcall from MAC layer */
static void pciscc_setptt(struct device *dev)
{
#ifdef AX25_ARBITER_NOT_BUGGY
	struct devctl_t *dctlp = (struct devctl_t *) dev->priv;
	struct chipctl_t *cctlp = dctlp->chip;
	unsigned long flags;

	spin_lock_irqsave(dctlp->dev_lock, flags);
	pciscc_set_txstate(dctlp, TX_DELAY);
	spin_unlock_irqrestore(dctlp->dev_lock, flags);
#endif
	return;
}

/* --------------------------------------------------------------------------------------------- */

/* report DCD state to DDI layer */
static unsigned int pciscc_getdcd(struct device *dev)
{
	struct devctl_t *dctlp = (struct devctl_t *) dev->priv;
	struct chipctl_t *cctlp = dctlp->chip;
	unsigned long l;
	unsigned int dcd;

	/*
	 * Note that even though we access STAR two times value
	 * might not be consistent (errata PED-20534H Rev 2.1 DS5).
	 * If this fails the only cure left is a reduction of
         * PCI clock to 25MHz to release timing constrains. Please
	 * drop me a note if this comes neccessary for you.
	 */
	l = readl(cctlp->io_base+SCCBASE[dctlp->channel]+STAR);
	l = readl(cctlp->io_base+SCCBASE[dctlp->channel]+STAR);
	dcd = dctlp->cfg.cdinv ? !!(l & CD) : !(l & CD);
	return dcd;
}

/* --------------------------------------------------------------------------------------------- */

/* return "CTS" state to DDI layer */
static unsigned int pciscc_getcts(struct device *dev)
{
	return (!dev->tbusy);
}

/* --------------------------------------------------------------------------------------------- */

/*
 * Report PTT state to DDI layer.
 * Note: To be completely honest we'd have to read
 * CTS flag from SCC core's STAR...
 * But due to the readback-bug this is too risky and in fact
 * not required here.
 */
static unsigned int pciscc_getptt(struct device *dev)
{
	struct devctl_t *dctlp = (struct devctl_t *) dev->priv;
	int txstate = dctlp->txstate;
	unsigned int ptt;

	ptt = (txstate == TX_DELAY)
		|| (txstate == TX_XMIT)
		|| (txstate == TX_TAIL)
		|| (txstate == TX_PROBE)
		|| (txstate == TX_CAL);
	return ptt;
}

/* --------------------------------------------------------------------------------------------- */

/* 
 * this function is called by DDI layer whenever a media parameter change
 * was suggested by either proc/sysctl or MAC/LAP internal cause
 */
static void pciscc_parameter_notify(struct device *dev,	int valueno, int old, int new)
{
	struct devctl_t *dctlp = (struct devctl_t *) dev->priv;
	int m,n;
	int br, bits;
	int probe_neccessary = 0;
	
	switch (valueno) {
	case AX25_VALUES_MEDIA_DUPLEX:
		dctlp->cfg.duplex = new;
		break;
	case AX25_VALUES_MEDIA_TXBITRATE:
	case AX25_VALUES_MEDIA_RXBITRATE:
		pciscc_rate2brg(new, &m, &n);
		dctlp->cfg.brate_m = m;
		dctlp->cfg.brate_n = n;
		probe_neccessary = 1;
		break;
	case AX25_VALUES_MEDIA_TXDELAY:
	case AX25_VALUES_MEDIA_TXTAIL:
		br = ax25_dev_get_value(dev, AX25_VALUES_MEDIA_TXBITRATE);
		if (br == 0) goto reject;
		bits = (br*new)/1000;
		if (bits == 0) bits=16;  /* two flags */
		if (valueno == AX25_VALUES_MEDIA_TXDELAY) {
			dctlp->cfg.txdelval = bits;
		} else {
			dctlp->cfg.txtailval = bits;
		}
		break;
	case AX25_VALUES_MEDIA_SLOTTIME:
	case AX25_VALUES_MEDIA_PPERSISTENCE:
	case AX25_VALUES_MEDIA_AUTO_ADJUST:
	default:
		/*
		 * We do not care about changes of
		 * those - they are somebody else's problem (now).
		 */
		return;	                                                        
	}
	if (dev->start) {
		/* reinit device */
		pciscc_channel_close(dctlp);
		pciscc_channel_open(dctlp);
		if (probe_neccessary) dctlp->tx_bitrate = pciscc_probe_txrate(dctlp);
	}
reject:
	/*
	 * (Re)store values from our (changed)
	 * configuration information
	 */
	pciscc_update_values(dev);
	return;
}

/* --------------------------------------------------------------------------------------------- */

/* convert BRG N and M into bitrate in bps */
static int pciscc_brg2rate(int m, int n)
{
	int br = xtal / ((n+1) * (1<<m));
	
	if (br == 0) br=1;
	return br;
}

/* --------------------------------------------------------------------------------------------- */

/* find best BRG N and M match for given bitrate */
static void pciscc_rate2brg(int rate, int *m, int *n)
{
	int ratio	= xtal/rate;
	int brg_best_n	= 0;
	int brg_best_m	= 0;
	int brg_tmp_n	= 0;
	int brg_tmp	= 0;
	int brg_best	= 0;
	int i;
	
	*m = *n = 0;
	if (ratio > 2097152) return;
	for (i=0; i<16; i++) {
		brg_tmp_n = (ratio/(1<<i))-1;
		if (brg_tmp_n > 63 || brg_tmp_n < 0) continue;
		brg_tmp = (brg_tmp_n+1)*(1<<i);
		if (abs(brg_best-ratio) < abs(brg_tmp-ratio)) continue;
		brg_best = brg_tmp;
		brg_best_n = brg_tmp_n;
		brg_best_m = i;
	}
	*m = brg_best_m;
	*n = brg_best_n;
	return;
}

/* --------------------------------------------------------------------------------------------- */

/* update ax25_settings from our device configuration */
static void pciscc_update_values(struct device *dev)
{
	struct devctl_t *dctlp = (struct devctl_t *) dev->priv;
	int rx_br, tx_br;
	int clk_ext;

	AX25_PTR(dev)->hw.fast = 0;
	tx_br = rx_br = 0;
	clk_ext = ((dctlp->cfg.clockmode == CFG_CM_DF9IC)
		|| (dctlp->cfg.clockmode == CFG_CM_HS));
	if (dev->start) {
		tx_br = rx_br = dctlp->tx_bitrate;
	} else if (!clk_ext) {
		tx_br = rx_br = pciscc_brg2rate(dctlp->cfg.brate_m, dctlp->cfg.brate_n);
	}
	if (tx_br == 0 || rx_br == 0) {
		tx_br = rx_br = 1200;
	}
	ax25_dev_set_value(dev, AX25_VALUES_MEDIA_DUPLEX, dctlp->cfg.duplex);
	ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXBITRATE, tx_br);
	ax25_dev_set_value(dev, AX25_VALUES_MEDIA_RXBITRATE, rx_br);
	ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXDELAY, dctlp->cfg.txdelval/tx_br);
	ax25_dev_set_value(dev, AX25_VALUES_MEDIA_TXTAIL, dctlp->cfg.txtailval/tx_br);
	return;
}

/* --------------------------------------------------------------------------------------------- */

/* netdevice UP -> DOWN routine */
static int pciscc_dev_close(struct device *dev)
{
	struct devctl_t *dctlp = (struct devctl_t *) dev->priv;

	if (dctlp->dev.start) {
		pciscc_channel_close(dctlp);
	}
	pciscc_update_values(dev);
	if (dctlp->chip->initialized && !dctlp->chip->usecnt) {
		pciscc_chip_close(dctlp->chip);
	}
#ifdef MODULE
	MOD_DEC_USE_COUNT;
#endif
	return 0;
}

/* --------------------------------------------------------------------------------------------- */

/* netdevice DOWN -> UP routine */
static int pciscc_dev_open(struct device *dev)
{
	struct devctl_t *dctlp = (struct devctl_t *) dev->priv;
	int res;

	if (!dctlp->chip->initialized) {
		if ((res=pciscc_chip_open(dctlp->chip))) return res;
	}
	if (!dctlp->dev.start) {
		if ((res=pciscc_channel_open(dctlp))) return res;
		dctlp->tx_bitrate = pciscc_probe_txrate(dctlp);
		pciscc_update_values(dev);
	}
#ifdef MODULE
	MOD_INC_USE_COUNT;
#endif
	return 0;
}

/* --------------------------------------------------------------------------------------------- */

/* netdevice change MTU request */
static int pciscc_change_mtu(struct device *dev, int new_mtu)
{
	dev->mtu=new_mtu;
	return 0;
}

/* --------------------------------------------------------------------------------------------- */

/* netdevice get statistics request */
static struct net_device_stats *pciscc_get_stats(struct device *dev)
{
	struct devctl_t *dctlp;

	if (!dev || !dev->priv) return NULL;		/* paranoia */
	dctlp = (struct devctl_t *) dev->priv;
	return &dctlp->stats;
}

/* --------------------------------------------------------------------------------------------- */

/* netdevice register - finish painting netdev structure */
static int pciscc_dev_init(struct device *dev)
{
	struct devctl_t *dctlp = (struct devctl_t *) dev->priv;
	int br;

	dev->mtu		= 1500;
	dev->hard_start_xmit	= pciscc_xmit;
	dev->open		= pciscc_dev_open;
	dev->stop		= pciscc_dev_close;
	dev->get_stats	        = pciscc_get_stats;
	dev->change_mtu		= pciscc_change_mtu;
	dev->do_ioctl		= pciscc_dev_ioctl;
	dev->set_mac_address    = pciscc_dev_set_mac_address;
	dev->hard_header_len	= AX25_MAX_HEADER_LEN;
	dev->addr_len		= AX25_ADDR_LEN;
	dev->type		= ARPHRD_AX25;
	dev->tx_queue_len	= 10;
	dev->flags		= (IFF_BROADCAST | IFF_MULTICAST);
	AX25_PTR(dev)		= &((struct devctl_t *) dev->priv)->ax25dev;
	memset(AX25_PTR(dev), 0, sizeof(struct ax25_dev));
	AX25_PTR(dev)->hw.dcd	= pciscc_getdcd;
	AX25_PTR(dev)->hw.ptt	= pciscc_getptt;
	AX25_PTR(dev)->hw.rts	= pciscc_setptt;
	AX25_PTR(dev)->hw.cts	= pciscc_getcts;
	AX25_PTR(dev)->hw.parameter_change_notify = pciscc_parameter_notify;
	dev_init_buffers(dev);
	memcpy(&dctlp->cfg, &devcfg_default, sizeof(struct devcfg_t));
	br = pciscc_brg2rate(dctlp->cfg.brate_m, dctlp->cfg.brate_n);
	pciscc_update_values(dev);
	return 0;
}

/* --------------------------------------------------------------------------------------------- */

/* set device's L2 address */
static int pciscc_dev_set_mac_address(struct device *dev, void *addr)
{
	struct sockaddr *sa = addr;

	memcpy(dev->dev_addr, sa->sa_data, AX25_ADDR_LEN);
	return 0;
}

/* --------------------------------------------------------------------------------------------- */
/*
 * IOCTLs:
 *
 * SIOCPCISCCGCCFG 	PCISCC Get Chip ConFiG
 * SIOCPCISCCSCCFG	PCISCC Set Chip ConFiG
 * SIOCPCISCCGDCFG	PCISCC Get Device ConFiG
 * SIOCPCISCCSDCFG	PCISCC Set Device ConFiG
 * SIOCPCISCCSLED	PCISCC Set LEDs
 * SIOCPCISCCGDSTAT	PCISCC Get Device STATus
 * SIOCPCISCCDCAL	PCISCC Device CALibrate
 * SIOCPCISCCLBI	PCISCC DSCC-4 Local Bus Interface transaction
 * SIOCPCISCCKICKTX     PCISCC KICK TX
 */

static int pciscc_dev_ioctl(struct device *dev, struct ifreq *ifr, int cmd)
{
	struct devctl_t *dctlp = (struct devctl_t *) dev->priv;
	struct chipctl_t *cctlp = dctlp->chip;
	struct devcfg_t dcfg;
	struct chipcfg_t ccfg;
	struct lbi_xfer lbi;
	int channel = dctlp->channel;
	int i;
	int probe_neccessary;
	unsigned long l;
	unsigned long status;
	unsigned long time;

	switch (cmd) {
	case SIOCPCISCCGCCFG:
		/* return cctlp->cfg structure in user-provided buffer */
		if (copy_to_user(ifr->ifr_data, &cctlp->cfg, sizeof(struct chipcfg_t))) {
			return -EFAULT;
		}
		return 0;
	case SIOCPCISCCSCCFG:
		/* copy user-provided data buffer to cctlp->cfg */
		if (!suser()) return -EPERM;
		for (i=0; i<4; i++) {
			if (cctlp->device[i]->dev.start) return -EBUSY;
		}
		if (copy_from_user(&ccfg, ifr->ifr_data, sizeof(struct chipcfg_t))) {
			return -EFAULT;
		}
		if ((ccfg.rxbufcnt < 4) || (ccfg.rxbufcnt > 128)) return -EINVAL;
		if ((ccfg.txbufcnt < 4) || (ccfg.txbufcnt > 256)) return -EINVAL;
		if ((ccfg.iqlen < 32) || (ccfg.iqlen > 512) || (ccfg.iqlen % 32 != 0)) return -EINVAL;
		if ((ccfg.prichan > 3) || (ccfg.prichan < -1)) return -EINVAL;
		if ((ccfg.mfifo_rx_t > 124) || (ccfg.mfifo_rx_t < 4)) return -EINVAL;
		memcpy((unsigned char *) &cctlp->cfg, (unsigned char *) &ccfg, sizeof(struct chipcfg_t));
		return 0;
	case SIOCPCISCCGDCFG:
		/* return dctlp->cfg structure in user-provided buffer */
		if (copy_to_user(ifr->ifr_data, &dctlp->cfg, sizeof(struct devcfg_t))) {
			return -EFAULT;
		}
		return 0;
	case SIOCPCISCCSDCFG:
		/* copy user-provided data buffer to dctlp->cfg */
		if (!suser()) return -EPERM;
		if (copy_from_user(&dcfg, ifr->ifr_data, sizeof(struct devcfg_t))) {
			return -EFAULT;
		}
		if ((dcfg.coding < CFG_CHCODE_MIN) || (dcfg.coding > CFG_CHCODE_MAX)) return -EINVAL;
		if ((dcfg.clockmode < CFG_CM_MIN) || (dcfg.clockmode > CFG_CM_MAX)) return -EINVAL;
		if ((dcfg.duplex < CFG_DUPLEX_MIN) || (dcfg.duplex > CFG_DUPLEX_MAX)) return -EINVAL;
		if (dcfg.brate_m > CFG_BRATEM_MAX) return -EINVAL;
		if (dcfg.brate_n > CFG_BRATEN_MAX) return -EINVAL;
		if ((dcfg.txddrive < CFG_TXDDRIVE_MIN) || (dcfg.txddrive > CFG_TXDDRIVE_MAX)) return -EINVAL;
		if ((dcfg.txdelmode < CFG_TXDEL_MIN) || (dcfg.txdelmode > CFG_TXDEL_MAX)) return -EINVAL;
		if ((dcfg.preamb_rpt!=0) && (dcfg.preamb_rpt!=1) && (dcfg.preamb_rpt!=2) && (dcfg.preamb_rpt!=4) && (dcfg.preamb_rpt!=8)) return -EINVAL;
		probe_neccessary = ((dcfg.coding != dctlp->cfg.coding)
			             || (dcfg.clockmode != dctlp->cfg.clockmode)
			             || (dcfg.brate_m != dctlp->cfg.brate_m)
			             || (dcfg.brate_n != dctlp->cfg.brate_n)
			             || (dcfg.clkout != dctlp->cfg.clkout));
		memcpy((unsigned char *) &dctlp->cfg, (unsigned char *) &dcfg, sizeof(struct devcfg_t));
		if (dev->start) {
			pciscc_channel_close(dctlp);
			pciscc_channel_open(dctlp);
			if (probe_neccessary) dctlp->tx_bitrate = pciscc_probe_txrate(dctlp);
		}
		pciscc_update_values(dev);
		return 0;
	case SIOCPCISCCSLED:
		/* set channel LEDs */
		if (!suser()) return -EPERM;
		writel(0x000000ff, cctlp->io_base+GPDIR);
		writel(0x00000000, cctlp->io_base+GPIM);
		l = readl(cctlp->io_base+GPDATA);
		switch (channel) {
		case 0:	l &= ~((1<<0) | (1<<1));
			l |= (((unsigned long) ifr->ifr_data & 3) << 0);
			break;
		case 1:	l &= ~((1<<2) | (1<<3));
			l |= (((unsigned long) ifr->ifr_data & 3) << 2);
			break;
		case 2:	l &= ~((1<<4) | (1<<5));
			l |= (((unsigned long) ifr->ifr_data & 3) << 4);
			break;
		case 3:	l &= ~((1<<6) | (1<<7));
			l |= (((unsigned long) ifr->ifr_data & 3) << 6);
			break;
		}
		writel(l, cctlp->io_base+GPDATA);
		return 0;
	case SIOCPCISCCGDSTAT:
		/* get channel status */
		status = (dctlp->txstate & 0x0f);
		l = readl(cctlp->io_base+SCCBASE[channel]+STAR);
		if (l & DPLA) status |= STATUS_DPLA;
		if (l & RLI) status |= STATUS_RLI;
		if (dctlp->cfg.cdinv) {
			if (l & CD) status |= STATUS_CD;
		} else {
			if (!(l & CD)) status |= STATUS_CD;
		}
		if (l & CTS) status |= STATUS_CTS;
		if (readl(cctlp->io_base+SCCBASE[dctlp->channel]+CCR1) & RTS) status |= STATUS_RTS;
		ifr->ifr_data = (void *) status;
		return 0;
	case SIOCPCISCCDCAL:
		/* calibrate */
		if (!suser()) return -EPERM;
		if (!dev->start) return -EAGAIN;
		if ((dctlp->txstate != TX_IDLE) && (dctlp->txstate != TX_CAL)) return -EAGAIN;
		time = (unsigned long) ifr->ifr_data;
		if ((dctlp->txstate == TX_CAL) && (time != 0)) return -EAGAIN;
		if (time > 0xffffff) return -EINVAL;
		writel((time*TVALUE), cctlp->io_base+SCCBASE[channel]+TIMR);
		if (time == 0) {
			pciscc_set_txstate(dctlp, TX_IDLE);
		} else {
			pciscc_set_txstate(dctlp, TX_CAL);
			writel(STI, cctlp->io_base+SCCBASE[channel]+CMDR);
		}
		return 0;
	case SIOCPCISCCLBI:
		/* local bus transaction */
		if (!suser()) return -EPERM;
		if (copy_from_user(&lbi, ifr->ifr_data, sizeof(struct lbi_xfer))) {
			return -EFAULT;
		}
		if (lbi.mode == LBI_WRITE) {
			writew(lbi.data, cctlp->lbi_base+lbi.addr);
		} else {
			lbi.data = readl(cctlp->lbi_base+lbi.addr);
			if (copy_to_user(ifr->ifr_data, &lbi, sizeof(struct lbi_xfer)))
				return -EFAULT;
		}
		return 0;
	case SIOCPCISCCKICKTX:
		/* kick TX */
		if (!suser()) return -EPERM;
		spin_lock_irq(&dctlp->dev_lock);
		pciscc_kick_tx(dctlp);
		spin_unlock_irq(&dctlp->dev_lock);
		return 0;
	default:
		return -EINVAL;
	}
	return 0;
}

/* --------------------------------------------------------------------------------------------- */

/* kick transmitter, call only with devlock held! */
static void pciscc_kick_tx(struct devctl_t *dctlp)
{
	int i;
#ifdef PCISCC_DEBUG
	struct chipctl_t *cctlp = dctlp->chip;

	printk(KERN_INFO "----- PCISCC: KICK TX dev=%s -----\n", dctlp->name);
	printk(KERN_INFO "TX IQ base=V0x%08lx next=V%08lx - dump:\n", (unsigned long) dctlp->iq_tx, (unsigned long) dctlp->iq_tx_next);
	for (i=0; i<cctlp->cfg.iqlen; i++) printk(KERN_INFO "V%08lx: %08lx\n", (unsigned long) &dctlp->iq_tx[i], dctlp->iq_tx[i]);
#endif
	i = pciscc_isr_txcleanup(dctlp);
#ifdef PCISCC_DEBUG
	printk(KERN_INFO "PCISCC: SIOCPCISCCKICKTX %s: 1st pass cleaned %d descriptors.\n", dctlp->name, i);
#endif
	if (i == 0) {
		dctlp->dq_tx_cleanup->result |= C;
		i = pciscc_isr_txcleanup(dctlp);
#ifdef PCISCC_DEBUG
		printk(KERN_INFO "PCISCC: SIOCPCISCCKICKTX %s: 2nd pass cleaned %d descriptors.\n", dctlp->name, i);
#endif
	}
	dctlp->stats.tx_errors++;
	return;
}

/* --------------------------------------------------------------------------------------------- */

/* transmit frame, downcall from MAC layer */
static int pciscc_xmit(struct sk_buff *skb, struct device *dev)
{
	struct devctl_t *dctlp = (struct devctl_t *) dev->priv;
	struct chipctl_t *cctlp = (struct chipctl_t *) dctlp->chip;
	int channel = dctlp->channel;
	struct tx_desc_t * volatile txdp;

	if (!dev->start)  {
		printk(KERN_ERR "PCISCC: xmit(): Call when iface %s is down\n", dev->name);
		kfree_skb(skb);
		return 0;
	}
	if (dev->tbusy) {
#ifdef PCISCC_DEBUG
		printk(KERN_INFO "PCISCC: pciscc_xmit(): I'm being kicked by L2!!!\n");
#endif
		/* auto-kick if TX has been busy for 30 seconds */
		if ((jiffies-dctlp->last_tx) > txkick*HZ) pciscc_kick_tx(dctlp);
	}
	if (!skb) {
		printk(KERN_ERR "PCISCC: xmit(): L2 handed us a NULL skb!\n");
		return 0;
	}
	if (!skb->len) {
		printk(KERN_ERR "PCISCC: xmit(): L2 tried to trick us into sending a skb of len 0!\n");
		kfree_skb(skb);
		return 0;
	}
	spin_lock_irq(&dctlp->dev_lock);
	ATOMICY_CHECK;
	txdp=dctlp->dq_tx_last->next;
	if ((txdp == dctlp->dq_tx_cleanup) || (txdp->next == dctlp->dq_tx_cleanup) || (txdp->result & C) || (txdp->next->result & C)) {
		/* desriptor chain "full" */
#ifdef PCISCC_VDEBUG
		printk(KERN_INFO "PCISCC: xmit(): Dropping frame due to full TX queue interface %s.\n", dev->name);
#endif
		dctlp->stats.tx_dropped++;
		kfree_skb(skb);
#ifdef PCISCC_DEBUG
		if (!((txdp == dctlp->dq_tx_cleanup) || (txdp->next == dctlp->dq_tx_cleanup)) && ((txdp->result & C) || (txdp->next->result & C)))
			printk(KERN_INFO "PCISCC: xmit(): txdp->result & C || txdp->next->result & C!!!!\n");
#endif
		ATOMICY_CHECK_END;
		spin_unlock_irq(&dctlp->dev_lock);
		return 0;
	}
	/* prepare TX descriptor */
	txdp->result=0;
	txdp->skb=skb;
	txdp->flags=FE | (BNO*skb->len);
	txdp->dataptr=(void *) virt_to_bus(skb->data);
	dctlp->dq_tx_last=txdp;
	wmb();
	flush_cache_all();
	if (dctlp->cfg.duplex == CFG_DUPLEX_FULLPTT) {
		/* in "always on" full duplex mode we can start frame transmit at once */
		pciscc_set_txstate(dctlp, TX_XMIT);
		switch (channel) {
		case 0:	writel(virt_to_bus(txdp), cctlp->io_base+CH0LTDA);
			break;
		case 1:	writel(virt_to_bus(txdp), cctlp->io_base+CH1LTDA);
			break;
		case 2:	writel(virt_to_bus(txdp), cctlp->io_base+CH2LTDA);
			break;
		case 3:	writel(virt_to_bus(txdp), cctlp->io_base+CH3LTDA);
			break;
		}
	} else if ((dctlp->cfg.txdelmode == CFG_TXDEL_HARD) || !dctlp->cfg.txdelval) {
		/* Hardware TX-delay control using RTS/CTS or zero TX-delay */
		writel(txtimeout*dctlp->tx_bitrate*TVALUE, cctlp->io_base+SCCBASE[channel]+TIMR);
		writel(STI, cctlp->io_base+SCCBASE[channel]+CMDR);
		pciscc_set_txstate(dctlp, TX_XMIT);
		switch (channel) {
		case 0:	writel(virt_to_bus(txdp), cctlp->io_base+CH0LTDA);
			break;
		case 1:	writel(virt_to_bus(txdp), cctlp->io_base+CH1LTDA);
			break;
		case 2:	writel(virt_to_bus(txdp), cctlp->io_base+CH2LTDA);
			break;
		case 3:	writel(virt_to_bus(txdp), cctlp->io_base+CH3LTDA);
			break;
		}
	} else {
		/* half duplex or "normal" full duplex, software txdelay */
		switch (dctlp->txstate) {
		case TX_RESET:
			/* TX not initialized */
			printk(KERN_INFO "PCISCC: xmit(): %s: Cannot transmit frame since TX is not inititalized!\n", dev->name);
			break;
		case TX_IDLE:
			/* TX is idle, key up and start txdelay */
			pciscc_set_txstate(dctlp, TX_DELAY);
			writel(dctlp->cfg.txdelval*TVALUE, cctlp->io_base+SCCBASE[channel]+TIMR);
			writel(STI, cctlp->io_base+SCCBASE[channel]+CMDR);
			break;
		case TX_DELAY:
			/* tx is already keyed but not yet ready */
			break;
		case TX_TAIL:
			/* tx is currently transmitting closing txtail sequence */
			writel(txtimeout*dctlp->tx_bitrate*TVALUE, cctlp->io_base+SCCBASE[channel]+TIMR);
			pciscc_clear_timer(dctlp);
			writel(STI, cctlp->io_base+SCCBASE[channel]+CMDR);
		case TX_XMIT:	/* note fall-through */
			/* tx is already transmitting preamble or data */
			pciscc_set_txstate(dctlp, TX_XMIT);
			switch (channel) {
			case 0:	writel(virt_to_bus(txdp), cctlp->io_base+CH0LTDA);
				break;
			case 1:	writel(virt_to_bus(txdp), cctlp->io_base+CH1LTDA);
				break;
			case 2:	writel(virt_to_bus(txdp), cctlp->io_base+CH2LTDA);
				break;
			case 3:	writel(virt_to_bus(txdp), cctlp->io_base+CH3LTDA);
				break;
			}
			break;
		case TX_PROBE:
		case TX_CAL:
			/* we are busy with diagnostic stuff */
			break;
		default:
			/* should not occur */
			printk(KERN_ERR "PCISCC: Unhandled txstate in xmit() iface=%s.\n", dev->name);
		}
	}
	txdp=txdp->next;
	dev->tbusy = ((txdp == dctlp->dq_tx_cleanup) || (txdp->next == dctlp->dq_tx_cleanup));
	dctlp->last_tx = jiffies;
	ATOMICY_CHECK_END;
	spin_unlock_irq(&dctlp->dev_lock);
	/* skb will be kfree()d by isr_txcleanup after transmission */
	return 0;
}

/* --------------------------------------------------------------------------------------------- */

/* called by receiver function - prepare received skb and fire up to L2 */
static __inline__ void pciscc_rx_skb(struct sk_buff *skb, struct devctl_t *dctlp)
{
	if (!skb) {
		printk(KERN_ERR "PCISCC: rx_skb(): Received NULL skb iface=%s.\n", dctlp->name);
		return;
	}
	dctlp->stats.rx_packets++;
	dctlp->stats.rx_bytes += skb->len;
	skb->protocol = htons(ETH_P_AX25);
	skb->dev = &dctlp->dev;
	skb->mac.raw = skb->data;
	skb->pkt_type = PACKET_HOST;
	netif_rx(skb);
	return;
}

/* --------------------------------------------------------------------------------------------- */

/* Initialize pciscc control device */
#ifdef MODULE
int pciscc_init(void)
#else	/* !MODULE */
__initfunc(int pciscc_init(struct device *dummy))
#endif	/* !MODULE */
{
	int i,j;
	int devnum;
	struct pci_dev *pcidev = NULL;
	unsigned char rev_id;

	printk(KERN_INFO "PCISCC: version %s\n", PCISCC_VERSION);
	if (!(dummybuf = kmalloc(256, GFP_DMA | GFP_KERNEL))) {
		printk(KERN_ERR "PCISCC: init: Could not get memory for dummybuf.\n");
		return -ENOMEM;
	}
	chipcnt=0;
	j=0;
	while ((pcidev = pci_find_device(PCI_VENDOR_ID_SIEMENS, PCI_DEVICE_ID_SIEMENS_PEB20534H, pcidev))) {
	        pcibios_read_config_byte(pcidev->bus->number, pcidev->devfn, PCI_REVISION_ID, &rev_id);
		printk(KERN_INFO "PCISCC: Found DSCC-4 Rev. %02x at bus=%u, func=%u.\n",
		       (unsigned int) rev_id, pcidev->bus->number, pcidev->devfn);
		if (!(chipctl[chipcnt]=kmalloc(sizeof(struct chipctl_t), GFP_KERNEL))) {
			printk(KERN_ERR "PCISCC: Out of memory allocating chipctl-structure\n");
#ifdef MODULE
			cleanup_module();
#endif
			return -ENOMEM;
		}
		memset(chipctl[chipcnt], 0, sizeof(struct chipctl_t));
		chipctl[chipcnt]->pcidev=pcidev;
		chipctl[chipcnt]->chip_lock = SPIN_LOCK_UNLOCKED;
		memcpy(&chipctl[chipcnt]->cfg, &chipcfg_default, sizeof(struct chipcfg_t));
		for (i=0;i<4;i++) {
			devnum = chipcnt*4+i;
			if (!(devctl[devnum]=kmalloc(sizeof(struct devctl_t), GFP_KERNEL))) {
				printk(KERN_ERR "PCISCC: Out of memory allocating devctl-structure.\n");
#ifdef MODULE
				cleanup_module();
#endif
				return -ENOMEM;
			}
			memset(devctl[devnum], 0, sizeof(struct devctl_t));
			do {
				sprintf(devctl[devnum]->name, "dscc%u", devnum);
				j++;
			} while (dev_get(devctl[devnum]->name) && j<60); /* find free device name */
			if (j>=60) {               /* none left */
				printk(KERN_ERR "PCISCC: Could not find free netdev name.\n");
#ifdef MODULE
				cleanup_module();
#endif
				return -EEXIST;
			}
			devctl[devnum]->dev.priv = (void *) devctl[devnum];
			devctl[devnum]->dev.name = devctl[devnum]->name;
			devctl[devnum]->dev.init = pciscc_dev_init;
			devctl[devnum]->chip = chipctl[chipcnt];
			devctl[devnum]->channel = i;
			devctl[devnum]->dev_lock = SPIN_LOCK_UNLOCKED;
			chipctl[chipcnt]->device[i] = devctl[devnum];
			register_netdev(&devctl[devnum]->dev);
		}
		chipcnt++;
	}
	printk(KERN_INFO "PCISCC: %u controller(s) found.\n", chipcnt);
	return 0;
}

/* --------------------------------------------------------------------------------------------- */

#ifndef MODULE
__initfunc(void pciscc_setup(char *str, int *ints))
{
	return;
}
#endif

/* --------------------------------------------------------------------------------------------- */


/*****************************************************************************
 *                              Module stuff.                                *
 *****************************************************************************/

#ifdef MODULE
MODULE_AUTHOR("Jens David, DG1KJD <dg1kjd@afthd.tu-darmstadt.de>");
MODULE_DESCRIPTION("AX.25 Device Driver for Siemens PEB-20534H (DSCC-4) based SCC cards");
MODULE_SUPPORTED_DEVICE("pciscc");
MODULE_PARM(xtal, "i");
MODULE_PARM(probebit, "i");
MODULE_PARM(txtimeout, "i");
MODULE_PARM(txkick, "i");

int init_module(void)
{
	return pciscc_init();
}

void cleanup_module(void)
{
	int i;
	struct chipctl_t *cctlp;
	struct devctl_t *dctlp;

	for (i=0; i<4*chipcnt; i++) {
		dctlp=devctl[i];
		pciscc_dev_close(&dctlp->dev);
		if (dctlp) {
			unregister_netdev(&dctlp->dev);
			kfree(dctlp);
			devctl[i]=NULL;
		}
	}
	for (i=0; i<chipcnt; i++) {
		cctlp=chipctl[i];
		if (cctlp) {
			if (cctlp->irq) {
				free_irq(cctlp->irq, (void *) cctlp);
				cctlp->irq=0;
			}
			if (cctlp->io_base) {
				iounmap(cctlp->io_base);
				cctlp->io_base=0;
			}
			if (cctlp->lbi_base) {
				iounmap(cctlp->lbi_base);
				cctlp->lbi_base=0;
			}
			kfree(cctlp);
			chipctl[i]=NULL;
		}
	}
	if (dummybuf) {
		kfree(dummybuf);
	}
	return;
}
#endif /* MODULE */

