/***************************************************************************** * * 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 PCISCC-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. * * Info: http://www.afthd.tu-darmstadt.de/~dg1kjd/pciscc4 * * Authors: (c) 1999 Jens David * * Policy: Please contact me before making structural changes. * Before applying changes to the communication with * the DSCC-4 please read: * - Data Sheet 09/98 PEB-20534 Version 2.0 * - Delta Sheet Chip Rev. 2.0-2.1 * - Addendum/Corrections to Data Sheet 09/98 as of 01/99 * - DSCC-4 Errata Sheet (Book?) 03/99 Chip Rev. 2.1 * - Sample driver source code as of 07/27/99 * All these documents are available from Infineon on * request or from http://www.infineon.de/... . * 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". * * CVS: $Id: pciscc4.c,v 1.60 2000/02/13 19:18:41 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 * * * 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 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 09/98 pp. 141 ff). Channel coding is user-selectable * on a per-channel basis. DF9IC-type modems like NRZ (conversion NRZ->NRZI * done internally), TCM-3105-like modems are usually used with NRZI coding. * Moreover manchester, FM0 and FM1 can be selected (untested). * The internal SCC-DMAC interface seem 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 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 * "addr", 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. * 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. * * 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(). We don't care any more. * * 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 wether 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* ------------------------------------------------------------------------- */ /* user serviceable area or module parameter */ static int xtal = 29491200; /* oscillator frequency in HZ */ static int probebit = 9600; /* number of cycles for tx_bitrate test */ static int txtimeout= 30; /* TX watchdog - timeout in seconds */ #define PCISCC_DEBUG /* enable debugging */ #undef PCISCC_VDEBUG /* enable verbose buffer debugging */ #define FULLDUP_PTT /* pull PTT on full duplex */ /* ------------------------------------------------------------------------- */ /* global variables */ static const char PCISCC_VERSION[] = "$Id: pciscc4.c,v 1.60 2000/02/13 19:18:41 dg1kjd Exp $"; static int chipcnt; static struct devctl_t *devctl[64]; static struct chipctl_t *chipctl[16]; static struct tq_struct txto_task; static unsigned char *dummybuf; /* 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 */ 128, /* number of TX descriptors per channel */ 32, /* interrupt queue length */ -1, /* priority channel */ 16, /* RX main fifo DMA threshold */ 0 /* endian swap for data buffers */ }; /* 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 */ 32, /* TX main fifo share */ 24, /* TX main fifo refill threshold in dw */ 20, /* TX main fifo forward */ 24, /* RX channel fifo forward threshold */ 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 repeatitions */ 0, /* preamble */ 0 /* HDLC extensions */ }; /* ------------------------------------------------------------------------- */ #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 "# &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 */ /* ------------------------------------------------------------------------- */ /* * 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; int i; int fifo_sum; char pci_latency; if (cctlp->initialized) return 0; #ifdef PCISCC_DEBUG printk(KERN_INFO "PCISCC: chip_open().\n"); #endif /* * make sure FIFO space requested by all devices is not larger than * what's available on the silicon. */ cli(); for (i=0, fifo_sum=0; i<4; i++) { fifo_sum += cctlp->device[i]->cfg.mfifo_tx_p; } sti(); if (fifo_sum > 128) { printk(KERN_ERR "PCISCC: chip_open(): TX main_fifo misconfiguration.\n"); return -EAGAIN; } /* map control and LBI memory */ cctlp->io_base=ioremap_nocache(cctlp->pcidev->base_address[0], 2*1024); cctlp->lbi_base=ioremap_nocache(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); #ifdef PCISCC_DEBUG printk(KERN_INFO "PCISCC: chip_open(): Old PCI latency timer: %u.\n", (unsigned int) pci_latency); #endif pcibios_write_config_byte(cctlp->pcidev->bus->number, cctlp->pcidev->devfn, PCI_LATENCY_TIMER, 255); /* 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 */ cli(); writel((cctlp->cfg.endianswap ? ENDIAN : 0) | (cctlp->cfg.prichan != -1 ? (SPRI | (cctlp->cfg.prichan * CHN)) : 0) | (4 * PERCFG) | (3 * LCD) | CMODE | (cctlp->cfg.oscpwr ? 0 : OSCPD), cctlp->io_base+GMODE); 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(((cctlp->device[0]->cfg.mfifo_tx_p/4)*TFSIZE0) | ((cctlp->device[1]->cfg.mfifo_tx_p/4)*TFSIZE1) | ((cctlp->device[2]->cfg.mfifo_tx_p/4)*TFSIZE2) | ((cctlp->device[3]->cfg.mfifo_tx_p/4)*TFSIZE3), cctlp->io_base+FIFOCR1); writel(((cctlp->device[0]->cfg.mfifo_tx_r/4)*TFRTHRES0) | ((cctlp->device[1]->cfg.mfifo_tx_r/4)*TFRTHRES1) | ((cctlp->device[2]->cfg.mfifo_tx_r/4)*TFRTHRES2) | ((cctlp->device[3]->cfg.mfifo_tx_r/4)*TFRTHRES3) | M4_0 | M4_1 | M4_2 | M4_3, cctlp->io_base+FIFOCR2); writel(cctlp->cfg.mfifo_rx_t, cctlp->io_base+FIFOCR3); writel(((cctlp->device[0]->cfg.mfifo_tx_f)*TFFTHRES0) | ((cctlp->device[1]->cfg.mfifo_tx_f)*TFFTHRES1) | ((cctlp->device[2]->cfg.mfifo_tx_f)*TFFTHRES2) | ((cctlp->device[3]->cfg.mfifo_tx_f)*TFFTHRES3), cctlp->io_base+FIFOCR4); /* 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); sti(); /* 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 */ cli(); writel((cctlp->cfg.endianswap ? ENDIAN : 0) | (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); sti(); /* 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; struct net_device *dev = &dctlp->dev; 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; cli(); 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); 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); sti(); 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 */ cli(); l = 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(l, cctlp->io_base+SCCBASE[channel]+CCR0); l = (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(l, cctlp->io_base+SCCBASE[channel]+CCR1); l = 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) /* 1 and 2 dwords somehow don't work */ | ((dctlp->cfg.cfifo_rx_t == 1) ? 1*RFTH : 0) | ((dctlp->cfg.cfifo_rx_t == 2) ? 1*RFTH : 0) | ((dctlp->cfg.cfifo_rx_t == 4) ? 1*RFTH : 0) | ((dctlp->cfg.cfifo_rx_t == 16) ? 2*RFTH : 0) | ((dctlp->cfg.cfifo_rx_t == 24) ? 3*RFTH : 0) | ((dctlp->cfg.cfifo_rx_t == 32) ? 4*RFTH : 0) | ((dctlp->cfg.cfifo_rx_t == 60) ? 5*RFTH : 0) | (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(l, 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); sti(); /* 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 */ switch (dctlp->tx_mailbox & 0x03ffffff) { /* mailbox was written by isr */ case 0x02001000: /* SCC XPR interrupt received */ #ifdef PCISCC_DEBUG printk(KERN_INFO "PCISCC: channel_open(): Success on SCC reset.\n"); #endif break; case 0xffffffff: printk(KERN_ERR "PCISCC: channel_open(): Timeout on SCC reset. Clocking problem?\n"); break; default: printk(KERN_ERR "PCISCC: channel_open(): Failure on SCC reset: mailbox=0x%0lx.\n", dctlp->tx_mailbox); break; } if (!(dctlp->tx_bitrate=pciscc_probe_txrate(dctlp))) dctlp->tx_bitrate=9600; /* * 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. */ cli(); rdp = last_rdp = NULL; for (i=0; icfg.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"); sti(); 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"); sti(); 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; icfg.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"); sti(); 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; } sti(); 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 */ switch (dctlp->tx_mailbox & 0x03ffffff) { /* mailbox was written by isr */ case 0x02001000: /* SCC XPR interrupt received */ #ifdef PCISCC_DEBUG printk(KERN_INFO "PCISCC: channel_open(): Success on SCC TX-reset.\n"); #endif break; case 0xffffffff: printk(KERN_ERR "PCISCC: channel_open(): Timeout on SCC TX-reset. Clocking problem?\n"); break; default: printk(KERN_ERR "PCISCC: channel_open(): Failure on SCC TX-reset: mailbox=0x%0lx.\n", dctlp->tx_mailbox); break; } /* * 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 dctlp->txstate=TX_IDLE; 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; } #ifdef FULLDUP_PTT if (dctlp->cfg.duplex == CFG_DUPLEX_FULL) { l = readl(cctlp->io_base+SCCBASE[channel]+CCR1); l |= RTS; writel(l, cctlp->io_base+SCCBASE[channel]+CCR1); } #endif flush_cache_all(); #ifdef PCISCC_DEBUG pciscc_dmac_regdump(cctlp); pciscc_queuedump(dctlp); #endif dctlp->chip->usecnt++; dctlp->dev.start=1; /* clear statistics */ mdelay(10); memset(&dctlp->stats, 0, sizeof(struct net_device_stats)); /* some housekeeping */ pciscc_update_values(dev); 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 */ switch (dctlp->tx_mailbox & 0x03ffffff) { /* mailbox was written by isr */ case 0x02001000: /* SCC XPR interrupt received */ #ifdef PCISCC_DEBUG printk(KERN_INFO "PCISCC: channel_close(): Success on SCC reset.\n"); #endif break; case 0xffffffff: printk(KERN_ERR "PCISCC: channel_close(): Timeout on SCC reset.\n"); break; default: printk(KERN_ERR "PCISCC: channel_close(): Failure on SCC reset: mailbox=0x%0lx.\n", dctlp->tx_mailbox); break; } /* 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); /* * 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; dctlp->txstate=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 flags; unsigned long status; unsigned long l; int channel; unsigned long *iqp; int processed; status = readl(cctlp->io_base+GSTAR); writel(status, cctlp->io_base+GSTAR); /* ack' irq */ if (!status) return; /* do not disturb... */ save_flags(flags); cli(); if (status & (IIPGPP | IIPLBI | IIPSSC)) { /* process peripheral queue */ processed = 0; for (iqp=cctlp->iq_per_next; *iqp!=0; iqp=((iqp==(cctlp->iq_per+cctlp->cfg.iqlen-1)) ? cctlp->iq_per : iqp+1)) { /* note wrap-arround */ /* I just love raping for-statements */ printk(KERN_INFO "PCISCC: isr: IQPER vector: 0x%0lx.\n", *iqp); processed++; *iqp=0; } cctlp->iq_per_next=iqp; } if (status & IICFG) { /* process configuration queue */ cctlp->mailbox = MAILBOX_NONE; processed = 0; /* I love raping for-statements... */ for (iqp=cctlp->iq_cfg_next; *iqp!=0; iqp=((iqp==(cctlp->iq_cfg+cctlp->cfg.iqlen-1)) ? cctlp->iq_cfg : iqp+1)) { /* note wrap-arround */ #ifdef PCISCC_DEBUG printk(KERN_INFO "PCISCC: isr: IQCFG vector: 0x%0lx.\n", *iqp); #endif if ((*iqp) & ARACK) { cctlp->mailbox = MAILBOX_OK; processed++; } if ((*iqp) & ARF) { cctlp->mailbox = MAILBOX_FAILURE; processed++; } *iqp=0; } 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; 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) { /* timer interrupt */ writel(0, cctlp->io_base+SCCBASE[channel]+TIMR); /* now, which state are we in? */ switch (dctlp->txstate) { case TX_DELAY: /* data transmit */ dctlp->txstate=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 */ dctlp->txstate=TX_IDLE; l=readl(cctlp->io_base+SCCBASE[channel]+CCR1); l=l & (~RTS); writel(l, cctlp->io_base+SCCBASE[channel]+CCR1); break; case TX_PROBE: /* tx bitrate test going on */ do_gettimeofday(&dctlp->tv); dctlp->txstate=TX_RESET; dctlp->tx_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 */ dctlp->txstate = 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 { dctlp->txstate=TX_IDLE; #ifdef FULLDUP_PTT if (dctlp->cfg.duplex == CFG_DUPLEX_HALF) { l=readl(cctlp->io_base+SCCBASE[channel]+CCR1); l=l & (~RTS); writel(l, cctlp->io_base+SCCBASE[channel]+CCR1); } #else l=readl(cctlp->io_base+SCCBASE[channel]+CCR1); l=l & (~RTS); writel(l, cctlp->io_base+SCCBASE[channel]+CCR1); #endif } break; case TX_XMIT: /* watchdog just ran out */ l=readl(cctlp->io_base+SCCBASE[channel]+CCR1); l=l & (~RTS); writel(l, cctlp->io_base+SCCBASE[channel]+CCR1); dctlp->txstate=TX_IDLE; txto_task.routine=pciscc_bh_txto; txto_task.data=(void *) dctlp; queue_task(&txto_task, &tq_scheduler); break; default: printk(KERN_ERR "PCISCC: isr: Timer interrupt while txstate=%u.\n", dctlp->txstate); dctlp->txstate=TX_IDLE; } } if (*iqp & ALLS) { /* a TX frame was just completed */ pciscc_isr_txcleanup(dctlp); if (dctlp->dq_tx_cleanup == dctlp->dq_tx_last) { /* complete TX-queue sent out */ if (dctlp->cfg.duplex == CFG_DUPLEX_FULL) { /* just update txstate */ dctlp->txstate=TX_IDLE; #ifndef FULLDUP_PTT /* release PTT */ l=readl(cctlp->io_base+SCCBASE[channel]+CCR1); l=l & (~RTS); writel(l, cctlp->io_base+SCCBASE[channel]+CCR1); #endif } else if (dctlp->cfg.txdelmode == CFG_TXDEL_SOFT) { /* start txtail */ dctlp->txstate=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) { /* deassert RTS immediately */ dctlp->txstate=TX_IDLE; l=readl(cctlp->io_base+SCCBASE[channel]+CCR1); l=l & (~RTS); writel(l, cctlp->io_base+SCCBASE[channel]+CCR1); } } } if (*iqp & 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... */ #ifdef PCISCC_DEBUG printk(KERN_INFO "PCISCC: isr: TX data underrun occured iface=%s.\n", dctlp->name); #endif dctlp->stats.tx_fifo_errors++; /* reset TX-DMAC */ switch (channel) { case 0: writel(MRFI | MTFI | MRERR | MTERR | RDT, cctlp->io_base+CH0CFG); writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH0BTDA); writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH0LTDA); case 1: writel(MRFI | MTFI | MRERR | MTERR | RDT, cctlp->io_base+CH1CFG); writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH1BTDA); writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH1LTDA); case 2: writel(MRFI | MTFI | MRERR | MTERR | RDT, cctlp->io_base+CH2CFG); writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH2BTDA); writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH2LTDA); case 3: writel(MRFI | MTFI | MRERR | MTERR | RDT, cctlp->io_base+CH3CFG); writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH3BTDA); writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH3LTDA); } writel(AR, cctlp->io_base+GCMDR); mdelay(10); /* reset SCC core TX */ writel(XRES, cctlp->io_base+SCCBASE[channel]+CMDR); mdelay(30); /* re-init TX-DMAC */ switch (channel) { case 0: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH0BTDA); writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH0LTDA); writel(MTFI | IDT, cctlp->io_base+CH0CFG); case 1: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH1BTDA); writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH1LTDA); writel(MTFI | IDT, cctlp->io_base+CH1CFG); case 2: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH2BTDA); writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH2LTDA); writel(MTFI | IDT, cctlp->io_base+CH2CFG); case 3: writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH3BTDA); writel(virt_to_bus(dctlp->dq_tx_last), cctlp->io_base+CH3LTDA); writel(MTFI | IDT, cctlp->io_base+CH3CFG); } writel(AR, cctlp->io_base+GCMDR); mdelay(10); /* We can't do anything more since we are still * in the interrupt handler. We can only hope that * the reset succeeded. */ } if (*iqp & XMR) { /* * TX message repeat - not critical, since * resolved automagically by abort sequence * and retransmit. */ #ifdef PCISCC_VDEBUG printk(KERN_INFO "PCISCC: isr: TX message repeat interrupt iface=%s.\n", dctlp->name); #endif } dctlp->tx_mailbox=*iqp; *iqp=0; processed++; } dctlp->iq_tx_next=iqp; } 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; for (iqp=dctlp->iq_rx_next; *iqp!=0; iqp=((iqp==(dctlp->iq_rx+cctlp->cfg.iqlen-1)) ? dctlp->iq_rx : iqp+1)) { /* note wrap-arround */ /* statistics only */ if ((*iqp & SCCIV_SCC) && (*iqp & SCCIV_RDO)) { dctlp->stats.rx_fifo_errors++; } if ((*iqp & SCCIV_SCC) && (*iqp & SCCIV_RFO)) { dctlp->stats.rx_over_errors++; } if ((*iqp & SCCIV_SCC) && (*iqp & SCCIV_FLEX)) { dctlp->stats.rx_length_errors++; } if (!(*iqp & SCCIV_SCC) && (*iqp & SCCIV_ERR)) { dctlp->stats.rx_errors++; } if (!(*iqp & SCCIV_SCC) && (*iqp & SCCIV_HI)) { printk(KERN_ERR "PCISCC: isr: Weird... received HI interrupt.\n"); } if (!(*iqp & SCCIV_SCC) && (*iqp & SCCIV_FI)) { } dctlp->rx_mailbox=*iqp; *iqp=0; processed++; } /* in any case check RX descriptor queue for received frames */ if (dctlp->dev.start) pciscc_isr_receiver(dctlp); dctlp->iq_rx_next=iqp; } restore_flags(flags); 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 *rdp; long status; unsigned char rdsb; /* receive data status byte, generated by DMAC at buffer end */ int bno; 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; /* areadly 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; /* * 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 __inline__ void pciscc_isr_txcleanup(struct devctl_t *dctlp) { struct tx_desc_t *tdp; int processed; processed=0; tdp=dctlp->dq_tx_cleanup; while ((tdp != dctlp->dq_tx_last) && (tdp->result & C)) { /* 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"); } #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++; #ifdef PCISCC_DEBUG if (processed>100) { printk(KERN_ERR "PCISCC: trouble in isr_txcleanup or please reduce bit rate by 20 dB.\n"); dctlp->dev.start=0; break; } #endif } dctlp->dq_tx_cleanup = tdp; flush_cache_all(); #ifdef PCISCC_VDEBUG printk(KERN_INFO "PCISCC: isr_txcleanup: Processed %u frames.\n", processed); #endif return; } /* ------------------------------------------------------------------------- */ /* called by TIN ISR as bh 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; } /* ------------------------------------------------------------------------- */ /* *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 *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; int channel = dctlp->channel; struct timeval tv_start; volatile unsigned long gcc_optimizer_safe; unsigned long start_time; long delta_us; unsigned long long tx_bitrate; unsigned long l; dctlp->txstate = TX_PROBE; dctlp->tx_mailbox = 0; start_time = jiffies; /* assert RTS line */ l=readl(cctlp->io_base+SCCBASE[dctlp->channel]+CCR1); l |= RTS; writel(l, cctlp->io_base+SCCBASE[dctlp->channel]+CCR1); 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->tx_mailbox && ((jiffies-start_time)<1000); } while (gcc_optimizer_safe); dctlp->txstate = TX_RESET; if (!dctlp->tx_mailbox) { printk(KERN_ERR "PCISCC: probe_txrate(): Timeout probing %s-TxClk. Clocking problem?\n", dctlp->dev.name); return 0; } 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); printk(KERN_INFO "PCISCC: probe_txrate(): tx_bitrate=%ld.\n", (long) tx_bitrate); if (dctlp->cfg.duplex == CFG_DUPLEX_HALF) { l=readl(cctlp->io_base+SCCBASE[channel]+CCR1); l=l & (~RTS); writel(l, cctlp->io_base+SCCBASE[channel]+CCR1); } else { #ifndef FULLDUP_PTT l=readl(cctlp->io_base+SCCBASE[channel]+CCR1); l=l & (~RTS); writel(l, cctlp->io_base+SCCBASE[channel]+CCR1); #endif } return tx_bitrate; } /* ------------------------------------------------------------------------- */ /* DDI support: immediately assert RTS, downcall from MAC layer */ static void pciscc_setptt(struct net_device *dev) { #ifdef AX25_ARBITER_NOT_BUGGY struct devctl_t *dctlp = (struct devctl_t *) dev->priv; struct chipctl_t *cctlp = dctlp->chip; unsigned long l; l = readl(cctlp->io_base+SCCBASE[dctlp->channel]+CCR1); l |= RTS; writel(l, cctlp->io_base+SCCBASE[dctlp->channel]+CCR1); #endif return; } /* ------------------------------------------------------------------------- */ /* report DCD state to DDI layer */ static unsigned int pciscc_getdcd(struct net_device *dev) { struct devctl_t *dctlp = (struct devctl_t *) dev->priv; struct chipctl_t *cctlp = dctlp->chip; unsigned long l; unsigned int dcd; 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 net_device *dev) { struct devctl_t *dctlp = (struct devctl_t *) dev->priv; struct chipctl_t *cctlp = dctlp->chip; unsigned int cts; cts = !!(readl(cctlp->io_base+SCCBASE[dctlp->channel]+STAR) & CTS); return cts; } /* ------------------------------------------------------------------------- */ /* report PTT state to DDI layer */ static unsigned int pciscc_getptt(struct net_device *dev) { struct devctl_t *dctlp = (struct devctl_t *) dev->priv; struct chipctl_t *cctlp = dctlp->chip; unsigned int ptt; ptt = !(readl(cctlp->io_base+SCCBASE[dctlp->channel]+STAR) & CTS); ptt = (dctlp->cfg.cdinv ? ptt : !ptt); 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 net_device *dev, int valueno, int old, int new) { struct devctl_t *dctlp = (struct devctl_t *) dev->priv; int m,n; int br, bits; printk(KERN_ERR "PCISCC: pciscc_parameter_notify(%s, %u).\n", dev->name, valueno); switch (valueno) { case AX25_VALUES_MEDIA_DUPLEX: if (dev->start) goto reject; dctlp->cfg.duplex = new; break; case AX25_VALUES_MEDIA_TXBITRATE: case AX25_VALUES_MEDIA_RXBITRATE: if (dev->start) goto reject; pciscc_rate2brg(new, &m, &n); dctlp->cfg.brate_m = m; dctlp->cfg.brate_n = n; 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; } 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< 2097152) return; for (i=0; i<16; i++) { brg_tmp_n = (ratio/(1< 63 || brg_tmp_n < 0) continue; brg_tmp = (brg_tmp_n+1)*(1<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_G3RUH || dctlp->cfg.clockmode == CFG_CM_TCM3105); if (clk_ext) { tx_br = rx_br = pciscc_brg2rate(dctlp->cfg.brate_m, dctlp->cfg.brate_n); } if (dev->start) { tx_br = dctlp->tx_bitrate; if (clk_ext) rx_br = tx_br; /* assumption; we have no way to find out */ } if (tx_br > 0) { 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 net_device *dev) { struct devctl_t *dctlp = (struct devctl_t *) dev->priv; if (dctlp->dev.start) { pciscc_channel_close(dctlp); } dctlp->dev.start=0; 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 net_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; pciscc_update_values(dev); } #ifdef MODULE MOD_INC_USE_COUNT; #endif return 0; } /* ------------------------------------------------------------------------- */ /* netdevice change MTU request */ static int pciscc_change_mtu(struct net_device *dev, int new_mtu) { dev->mtu=new_mtu; return 0; } /* ------------------------------------------------------------------------- */ /* netdevice get statistics request */ static struct net_device_stats *pciscc_get_stats(struct net_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 net_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 net_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 */ static int pciscc_dev_ioctl(struct net_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; 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; for (i=0; i<4; i++) { if (cctlp->device[i]->dev.start) return -EBUSY; } 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.mfifo_tx_p < 4) || (dcfg.mfifo_tx_p > 124) || (dcfg.mfifo_tx_p % 4 != 0)) return -EINVAL; if ((dcfg.mfifo_tx_r < 1) || (dcfg.mfifo_tx_r > dcfg.mfifo_tx_p) || (dcfg.mfifo_tx_r % 4 != 0)) return -EINVAL; if (dcfg.mfifo_tx_f > (dcfg.mfifo_tx_p-1)) return -EINVAL; if ((dcfg.cfifo_rx_t != 1) && (dcfg.cfifo_rx_t != 2) && (dcfg.cfifo_rx_t != 4) && (dcfg.cfifo_rx_t != 16) && (dcfg.cfifo_rx_t != 24) && (dcfg.cfifo_rx_t != 32) && (dcfg.cfifo_rx_t != 60)) 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; memcpy((unsigned char *) &dctlp->cfg, (unsigned char *) &dcfg, sizeof(struct devcfg_t)); pciscc_update_values(dev); return 0; case SIOCPCISCCSLED: /* set channel LEDs */ if (!suser()) return -EPERM; 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_IDLE)) return -EAGAIN; time = (unsigned long) ifr->ifr_data; if (time > 0xffffff) return -EINVAL; writel((time*TVALUE), cctlp->io_base+SCCBASE[channel]+TIMR); if (time == 0) { dctlp->txstate = TX_IDLE; if (dctlp->cfg.duplex == CFG_DUPLEX_HALF) { l = readl(cctlp->io_base+SCCBASE[channel]+CCR1); l &= ~RTS; writel(l, cctlp->io_base+SCCBASE[channel]+CCR1); } else { /* duplex */ #ifndef FULLDUP_PTT l = readl(cctlp->io_base+SCCBASE[channel]+CCR1); l &= ~RTS; writel(l, cctlp->io_base+SCCBASE[channel]+CCR1); #endif } } else { dctlp->txstate = TX_CAL; l=readl(cctlp->io_base+SCCBASE[channel]+CCR1); writel(l | RTS, cctlp->io_base+SCCBASE[channel]+CCR1); 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; default: return -EINVAL; } return 0; } /* ------------------------------------------------------------------------- */ /* transmit frame, downcall from MAC layer */ static int pciscc_xmit(struct sk_buff *skb, struct net_device *dev) { struct devctl_t *dctlp = (struct devctl_t *) dev->priv; struct chipctl_t *cctlp = (struct chipctl_t *) dctlp->chip; int channel = dctlp->channel; unsigned long flags; struct tx_desc_t *txdp; unsigned long l; if (!dev->start) { printk(KERN_ERR "PCISCC: xmit(): Call when iface %s is down\n", dev->name); kfree_skb(skb); return 0; } 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; } save_flags(flags); cli(); 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); restore_flags(flags); 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; flush_cache_all(); if (dctlp->cfg.duplex == CFG_DUPLEX_FULL) { /* in full duplex mode we can start frame transmit at once */ dctlp->txstate=TX_XMIT; l=readl(cctlp->io_base+SCCBASE[channel]+CCR1); writel(l | RTS, cctlp->io_base+SCCBASE[channel]+CCR1); 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 */ if (dctlp->txstate == TX_IDLE) { writel(txtimeout*dctlp->tx_bitrate*TVALUE, cctlp->io_base+SCCBASE[channel]+TIMR); writel(STI, cctlp->io_base+SCCBASE[channel]+CMDR); } dctlp->txstate=TX_XMIT; if (dctlp->cfg.txdelmode == CFG_TXDEL_SOFT) { l=readl(cctlp->io_base+SCCBASE[channel]+CCR1); writel(l | RTS, cctlp->io_base+SCCBASE[channel]+CCR1); } 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, 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); case TX_IDLE: /* TX is idle, key up and start txdelay */ dctlp->txstate=TX_DELAY; l=readl(cctlp->io_base+SCCBASE[channel]+CCR1); writel(l | RTS, cctlp->io_base+SCCBASE[channel]+CCR1); 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 */ dctlp->txstate=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); } } /* skb will be kfree()d by isr_txcleanup after transmission */ restore_flags(flags); 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 net_device *dummy)) #endif /* !MODULE */ { int i,j; int devnum; struct pci_dev *pcidev = NULL; printk(KERN_ERR "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))) { 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; 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; chipctl[chipcnt]->device[i] = devctl[devnum]; register_netdev(&devctl[devnum]->dev); } chipcnt++; } printk(KERN_ERR "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 "); 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"); 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; iirq) { 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 */