patch-2.2.17 linux/drivers/net/pc300.c
Next file: linux/drivers/net/ppp.c
Previous file: linux/drivers/net/n2.c
Back to the patch index
Back to the overall index
- Lines: 2858
- Date:
Mon Sep 4 18:39:19 2000
- Orig file:
v2.2.16/drivers/net/pc300.c
- Orig date:
Thu Jan 1 01:00:00 1970
diff -u --recursive --new-file v2.2.16/drivers/net/pc300.c linux/drivers/net/pc300.c
@@ -0,0 +1,2857 @@
+#define USE_PCI_CLOCK
+static char rcsid[] =
+"$Revision: 3.1.0.2 $$Date: 2000/06/27 $";
+
+/*
+ * pc300.c Cyclades-PC300(tm) Driver.
+ *
+ * Author: Ivan Passos <ivan@cyclades.com>
+ *
+ * Copyright: (c) 1999-2000 Cyclades Corp.
+ *
+ * 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.
+ *
+ * $Log: pc300.c,v $
+ * Revision 3.1.0.2 2000/06/27 ivan
+ * Previous bugfix for the framing errors with external clock made X21
+ * boards stop working. This version fixes it.
+ *
+ * Revision 3.1.0.1 2000/06/23 ivan
+ * Revisited cpc_queue_xmit to prevent race conditions on Tx DMA buffer
+ * handling when Tx timeouts occur.
+ * Revisited Rx statistics.
+ * Added support for loopback mode in the SCA-II.
+ * Fixed a bug in the SCA-II programming that would cause framing errors
+ * when external clock was configured.
+ *
+ * Revision 3.1.0.0 2000/05/26 ivan
+ * Added Frame-Relay support.
+ * Driver now uses HDLC generic driver to provide protocol support.
+ * Added logic in the SCA interrupt handler so that no board can monopolize
+ * the driver.
+ * Request PLX I/O region, although driver doesn't use it, to avoid
+ * problems with other drivers accessing it.
+ *
+ * Revision 3.0.0.0 2000/05/15 ivan
+ * Did some changes in the DMA programming implementation to avoid the
+ * occurrence of a SCA-II bug in the second channel.
+ * Implemented workaround for PLX9050 bug that would cause a system lockup
+ * in certain systems, depending on the MMIO addresses allocated to the
+ * board.
+ * Fixed the FALC chip programming to avoid synchronization problems in the
+ * second channel (TE only).
+ * Implemented a cleaner and faster Tx DMA descriptor cleanup procedure in
+ * cpc_queue_xmit().
+ * Changed the built-in driver implementation so that the driver can use the
+ * general 'hdlcN' naming convention instead of proprietary device names.
+ * Driver load messages are now device-centric, instead of board-centric.
+ * Dynamic allocation of device structures.
+ *
+ * Revision 2.0.0.0 2000/04/15 ivan
+ * Added support for the PC300/TE boards (T1/FT1/E1/FE1).
+ *
+ * Revision 1.1.0.0 2000/02/28 ivan
+ * Major changes in the driver architecture.
+ * Driver now reports physical instead of virtual memory addresses.
+ * Added cpc_change_mtu function.
+ *
+ * Revision 1.0.0.0 1999/12/16 ivan
+ * First official release.
+ * Support for 1- and 2-channel boards (which use distinct PCI Device ID's).
+ * Support for monolythic installation (i.e., drv built into the kernel).
+ * X.25 additional checking when lapb_[dis]connect_request returns an error.
+ * SCA programming now covers X.21 as well.
+ *
+ * Revision 0.3.1.0 1999/11/18 ivan
+ * Made X.25 support configuration-dependent (as it depends on external
+ * modules to work).
+ * Changed X.25-specific function names to comply with adopted convention.
+ * Fixed typos in X.25 functions that would cause compile errors (Daniela).
+ * Fixed bug in ch_config that would disable interrupts on a previously
+ * enabled channel if the other channel on the same board was enabled later.
+ *
+ * Revision 0.3.0.0 1999/11/16 Daniela Squassoni
+ * X.25 support.
+ *
+ * Revision 0.2.3.0 1999/11/15 ivan
+ * Function cpc_ch_status now provides more detailed information.
+ * Added support for X.21 clock configuration.
+ * Changed TNR1 setting in order to prevent Tx FIFO overaccesses by the SCA.
+ * Now using PCI clock instead of internal oscillator clock for the SCA.
+ *
+ * Revision 0.2.2.0 1999/11/10 ivan
+ * Changed the *_dma_buf_check functions so that they would print only
+ * the useful info instead of the whole buffer descriptor bank.
+ * Fixed bug in cpc_queue_xmit that would eventually crash the system
+ * in case of a packet drop.
+ * Implemented TX underrun handling.
+ * Improved SCA fine tuning to boost up its performance.
+ *
+ * Revision 0.2.1.0 1999/11/03 ivan
+ * Added functions *dma_buf_pt_init to allow independent initialization
+ * of the next-descr. and DMA buffer pointers on the DMA descriptors.
+ * Kernel buffer release and tbusy clearing is now done in the interrupt
+ * handler.
+ * Fixed bug in cpc_open that would cause an interface reopen to fail.
+ * Added a protocol-specific code section in cpc_net_rx.
+ * Removed printk level defs (they might be added back after the beta phase).
+ *
+ * Revision 0.2.0.0 1999/10/28 ivan
+ * Revisited the code so that new protocols can be easily added / supported.
+ *
+ * Revision 0.1.0.1 1999/10/20 ivan
+ * Mostly "esthetic" changes.
+ *
+ * Revision 0.1.0.0 1999/10/11 ivan
+ * Initial version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/net.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+#include <linux/netdevice.h>
+#include <net/arp.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_PC300_X25
+#include <linux/lapb.h>
+#endif /* CONFIG_PC300_X25 */
+
+#include "syncppp.h"
+
+#include <linux/pc300.h>
+
+#include <asm/spinlock.h>
+
+#define CPC_LOCK(card,flags) \
+ do { \
+ spin_lock_irqsave(&card->card_lock, flags); \
+ } while (0)
+
+#define CPC_UNLOCK(card,flags) \
+ do { \
+ spin_unlock_irqrestore(&card->card_lock, flags); \
+ } while (0)
+
+#undef PC300_DEBUG_PCI
+#undef PC300_DEBUG_INTR
+#undef PC300_DEBUG_TX
+#undef PC300_DEBUG_RX
+
+/* Hardware configuration options.
+ * These are arrays of configuration options used by verification routines.
+ * The first element of each array is its size (i.e. number of options).
+ */
+static unsigned short cpc_pci_dev_id[] = {
+ PCI_DEVICE_ID_PC300_RX_1, /* PC300/RSV or PC300/X21, 1 chan */
+ PCI_DEVICE_ID_PC300_RX_2, /* PC300/RSV or PC300/X21, 2 chan */
+ PCI_DEVICE_ID_PC300_TE_1, /* PC300/TE, 1 chan */
+ PCI_DEVICE_ID_PC300_TE_2, /* PC300/TE, 2 chan */
+ 0 /* end of table */
+};
+
+/* This is the per-card data structure containing mem. addresses, irq, etc.
+ * This driver supports a maximum of PC300_MAXCARDS cards.
+ */
+static pc300_t cpc_card[PC300_MAXCARDS];
+
+static ucshort cpc_nboards = 0;
+
+#ifndef min
+#define min(a,b) (((a)<(b))?(a):(b))
+#endif
+#ifndef max
+#define max(a,b) (((a)>(b))?(a):(b))
+#endif
+
+/************************/
+/*** DMA Routines ***/
+/************************/
+static void
+tx_dma_buf_pt_init(pc300_t *card, int ch)
+{
+ int i;
+ int ch_factor = ch * N_DMA_TX_BUF;
+ volatile pcsca_bd_t *ptdescr = (pcsca_bd_t *)(card->hw.rambase
+ + DMA_TX_BD_BASE + ch_factor * sizeof(pcsca_bd_t));
+
+ for (i = 0 ; i < N_DMA_TX_BUF ; i++, ptdescr++) {
+ cpc_writel(&ptdescr->next, (uclong) (DMA_TX_BD_BASE +
+ (ch_factor + ((i + 1) & (N_DMA_TX_BUF - 1))) *
+ sizeof(pcsca_bd_t)));
+ cpc_writel(&ptdescr->ptbuf,
+ (uclong)(DMA_TX_BASE + (ch_factor + i)*BD_DEF_LEN));
+ }
+}
+
+static void
+tx_dma_buf_init(pc300_t *card, int ch)
+{
+ int i;
+ int ch_factor = ch * N_DMA_TX_BUF;
+ volatile pcsca_bd_t *ptdescr = (pcsca_bd_t *)(card->hw.rambase
+ + DMA_TX_BD_BASE + ch_factor * sizeof(pcsca_bd_t));
+
+ for (i = 0 ; i < N_DMA_TX_BUF ; i++, ptdescr++) {
+ memset_io(ptdescr, 0, sizeof (pcsca_bd_t));
+ cpc_writew(&ptdescr->len, 0);
+ cpc_writeb(&ptdescr->status, 0);
+ }
+ tx_dma_buf_pt_init(card, ch);
+}
+
+static void
+rx_dma_buf_pt_init(pc300_t *card, int ch)
+{
+ int i;
+ int ch_factor = ch * N_DMA_RX_BUF;
+ volatile pcsca_bd_t *ptdescr = (pcsca_bd_t *)(card->hw.rambase
+ + DMA_RX_BD_BASE + ch_factor * sizeof(pcsca_bd_t));
+
+ for (i = 0 ; i < N_DMA_RX_BUF ; i++, ptdescr++) {
+ cpc_writel(&ptdescr->next, (uclong) (DMA_RX_BD_BASE +
+ (ch_factor + ((i + 1) & (N_DMA_RX_BUF - 1))) *
+ sizeof(pcsca_bd_t)));
+ cpc_writel(&ptdescr->ptbuf,
+ (uclong)(DMA_RX_BASE + (ch_factor + i)*BD_DEF_LEN));
+ }
+}
+
+static void
+rx_dma_buf_init(pc300_t *card, int ch)
+{
+ int i;
+ int ch_factor = ch * N_DMA_RX_BUF;
+ volatile pcsca_bd_t *ptdescr = (pcsca_bd_t *)(card->hw.rambase
+ + DMA_RX_BD_BASE + ch_factor * sizeof(pcsca_bd_t));
+
+ for (i = 0 ; i < N_DMA_RX_BUF ; i++, ptdescr++) {
+ memset_io(ptdescr, 0, sizeof (pcsca_bd_t));
+ cpc_writew(&ptdescr->len, 0);
+ cpc_writeb(&ptdescr->status, 0);
+ }
+ rx_dma_buf_pt_init(card, ch);
+}
+
+static void
+tx_dma_buf_check(pc300_t *card, int ch)
+{
+ volatile pcsca_bd_t *ptdescr;
+ int i;
+ ucshort first_bd = card->chan[ch].tx_first_bd;
+ ucshort next_bd = card->chan[ch].tx_next_bd;
+
+ printk("#CH%d: f_bd = %d(0x%08x), n_bd = %d(0x%08x)\n", ch,
+ first_bd, TX_BD_ADDR(ch, first_bd),
+ next_bd, TX_BD_ADDR(ch, next_bd));
+ for (i = first_bd,
+ ptdescr = (pcsca_bd_t *)(card->hw.rambase + TX_BD_ADDR(ch, first_bd));
+ i != ((next_bd + 1) & (N_DMA_TX_BUF - 1)) ;
+ i = (i + 1) & (N_DMA_TX_BUF - 1),
+ ptdescr = (pcsca_bd_t *)(card->hw.rambase + TX_BD_ADDR(ch, i))) {
+ printk("\n CH%d TX%d: next=0x%lx, ptbuf=0x%lx, ST=0x%x, len=%d",
+ ch, i,
+ (uclong)cpc_readl(&ptdescr->next),
+ (uclong)cpc_readl(&ptdescr->ptbuf),
+ cpc_readb(&ptdescr->status),
+ cpc_readw(&ptdescr->len));
+ }
+ printk("\n");
+}
+
+static void
+rx_dma_buf_check(pc300_t *card, int ch)
+{
+ volatile pcsca_bd_t *ptdescr;
+ int i;
+ ucshort first_bd = card->chan[ch].rx_first_bd;
+ ucshort last_bd = card->chan[ch].rx_last_bd;
+ int ch_factor;
+
+ ch_factor = ch * N_DMA_RX_BUF;
+ printk("#CH%d: f_bd = %d, l_bd = %d\n", ch, first_bd, last_bd);
+ for (i = 0, ptdescr = (pcsca_bd_t *)(card->hw.rambase +
+ DMA_RX_BD_BASE + ch_factor*sizeof(pcsca_bd_t));
+ i < N_DMA_RX_BUF ;
+ i++, ptdescr++) {
+ if (cpc_readb(&ptdescr->status) & DST_OSB)
+ printk("\n CH%d RX%d: next=0x%lx, ptbuf=0x%lx, ST=0x%x, len=%d",
+ ch, i,
+ (uclong)cpc_readl(&ptdescr->next),
+ (uclong)cpc_readl(&ptdescr->ptbuf),
+ cpc_readb(&ptdescr->status),
+ cpc_readw(&ptdescr->len));
+ }
+ printk("\n");
+}
+
+int
+dma_get_rx_frame_size(pc300_t *card, int ch)
+{
+ volatile pcsca_bd_t *ptdescr;
+ ucshort first_bd = card->chan[ch].rx_first_bd;
+ int rcvd = 0;
+ volatile ucchar status;
+
+ ptdescr = (pcsca_bd_t *)(card->hw.rambase + RX_BD_ADDR(ch, first_bd));
+ while ((status = cpc_readb(&ptdescr->status)) & DST_OSB) {
+ rcvd += cpc_readw(&ptdescr->len);
+ first_bd = (first_bd + 1) & (N_DMA_RX_BUF - 1);
+ if (status & DST_EOM)
+ return (rcvd);
+ ptdescr = (pcsca_bd_t *)(card->hw.rambase + cpc_readl(&ptdescr->next));
+ }
+ return (-1);
+}
+
+/*
+ * dma_buf_write: writes a frame to the Tx DMA buffers
+ * NOTE: this function writes one frame at a time.
+ */
+int
+dma_buf_write(pc300_t *card, int ch, ucchar *ptdata, int len)
+{
+ int i, nchar;
+ volatile pcsca_bd_t *ptdescr;
+ int tosend = len;
+ ucchar nbuf = ((len - 1)/BD_DEF_LEN) + 1;
+
+ for (i = 0 ; i < nbuf ; i++) {
+ ptdescr = (pcsca_bd_t *)(card->hw.rambase +
+ TX_BD_ADDR(ch, card->chan[ch].tx_next_bd));
+ nchar = min(BD_DEF_LEN,tosend);
+ if (!(cpc_readb(&ptdescr->status) & DST_OSB)) {
+ memcpy_toio((void *)(card->hw.rambase +
+ cpc_readl(&ptdescr->ptbuf)),
+ &ptdata[len - tosend],
+ nchar);
+ if ((i + 1) == nbuf) {
+ /* This must be the last BD to be used */
+ cpc_writeb(&ptdescr->status, (DST_EOM | DST_EOT));
+ } else {
+ cpc_writeb(&ptdescr->status, 0);
+ }
+ cpc_writew(&ptdescr->len, nchar);
+ } else {
+ return -ENOMEM;
+ }
+ tosend -= nchar;
+ card->chan[ch].tx_next_bd =
+ (card->chan[ch].tx_next_bd + 1) & (N_DMA_TX_BUF - 1);
+ }
+ /* If it gets to here, it means we have sent the whole frame */
+ return 0;
+}
+
+/*
+ * dma_buf_read: reads a frame from the Rx DMA buffers
+ * NOTE: this function reads one frame at a time.
+ */
+int
+dma_buf_read(pc300_t *card, int ch, struct sk_buff *skb)
+{
+ int nchar;
+ pc300ch_t *chan = (pc300ch_t *)&card->chan[ch];
+ volatile pcsca_bd_t *ptdescr;
+ int rcvd = 0;
+ volatile ucchar status;
+
+ ptdescr = (pcsca_bd_t *)(card->hw.rambase +
+ RX_BD_ADDR(ch, chan->rx_first_bd));
+ while ((status = cpc_readb(&ptdescr->status)) & DST_OSB) {
+ if (status & (DST_OVR | DST_CRC | DST_RBIT | DST_SHRT | DST_ABT)) {
+ rcvd = -status;
+ /* Discard remaining descriptors used by the bad frame */
+ while(chan->rx_first_bd != chan->rx_last_bd) {
+ cpc_writeb(&ptdescr->status, 0);
+ chan->rx_first_bd =
+ (chan->rx_first_bd + 1) & (N_DMA_RX_BUF - 1);
+ if(status & DST_EOM)
+ break;
+ ptdescr = (pcsca_bd_t *)(card->hw.rambase +
+ cpc_readl(&ptdescr->next));
+ status = cpc_readb(&ptdescr->status);
+ }
+ break;
+ }
+ if ((nchar = cpc_readw(&ptdescr->len)) != 0) {
+ memcpy_fromio(skb_put(skb, nchar),
+ (void *)(card->hw.rambase +
+ cpc_readl(&ptdescr->ptbuf)),
+ nchar);
+ rcvd += nchar;
+ }
+ cpc_writeb(&ptdescr->status, 0);
+ cpc_writeb(&ptdescr->len, 0);
+ chan->rx_first_bd = (chan->rx_first_bd + 1) & (N_DMA_RX_BUF - 1);
+
+ if (status & DST_EOM) break;
+
+ ptdescr = (pcsca_bd_t *)(card->hw.rambase + cpc_readl(&ptdescr->next));
+ }
+
+ if (rcvd != 0) {
+ /* Update pointer */
+ chan->rx_last_bd = (chan->rx_first_bd - 1) & (N_DMA_RX_BUF - 1);
+ /* Update EDA */
+ cpc_writel(card->hw.scabase + DRX_REG(EDAL, ch),
+ RX_BD_ADDR(ch, chan->rx_last_bd));
+ }
+ return (rcvd);
+}
+
+void
+tx_dma_stop(pc300_t *card, int ch)
+{
+ uclong scabase = card->hw.scabase;
+ ucchar drr_ena_bit = 1<<(5 + 2*ch);
+ ucchar drr_rst_bit = 1<<(1 + 2*ch);
+
+ /* Disable DMA */
+ cpc_writeb(scabase + DRR, drr_ena_bit);
+ cpc_writeb(scabase + DRR, drr_rst_bit & ~drr_ena_bit);
+}
+
+void
+rx_dma_stop(pc300_t *card, int ch)
+{
+ uclong scabase = card->hw.scabase;
+ ucchar drr_ena_bit = 1<<(4 + 2*ch);
+ ucchar drr_rst_bit = 1<<(2*ch);
+
+ /* Disable DMA */
+ cpc_writeb(scabase + DRR, drr_ena_bit);
+ cpc_writeb(scabase + DRR, drr_rst_bit & ~drr_ena_bit);
+}
+
+/*************************/
+/*** FALC Routines ***/
+/*************************/
+void
+falc_issue_cmd(pc300_t *card, int ch, ucchar cmd)
+{
+ uclong falcbase = card->hw.falcbase;
+ unsigned long i = 0;
+
+ while (cpc_readb(falcbase + F_REG(SIS, ch)) & SIS_CEC) {
+ if (i++ >= PC300_FALC_MAXLOOP) {
+ printk("%s: FALC command locked(cmd=0x%x).\n",
+ card->chan[ch].d.name, cmd);
+ break;
+ }
+ }
+ cpc_writeb(falcbase + F_REG(CMDR, ch), cmd);
+}
+
+void
+falc_intr_enable(pc300_t *card, int ch)
+{
+ pc300ch_t *chan = (pc300ch_t *)&card->chan[ch];
+ pc300chconf_t *conf = (pc300chconf_t *)&chan->conf;
+ falc_t *pfalc = (falc_t *)&chan->falc;
+ uclong falcbase = card->hw.falcbase;
+
+ /* Interrupt pins are open-drain */
+ cpc_writeb(falcbase + F_REG(IPC, ch),
+ cpc_readb(falcbase + F_REG(IPC, ch)) & ~IPC_IC0);
+ /* Conters updated each second */
+ cpc_writeb(falcbase + F_REG(FMR1, ch),
+ cpc_readb(falcbase + F_REG(FMR1, ch)) | FMR1_ECM);
+ /* Enable SEC and ES interrupts */
+ cpc_writeb(falcbase + F_REG(IMR3, ch),
+ cpc_readb(falcbase + F_REG(IMR3, ch)) & ~(IMR3_SEC | IMR3_ES));
+ cpc_writeb(falcbase + F_REG(IMR4, ch),
+ cpc_readb(falcbase + F_REG(IMR4, ch)) &
+ ~(IMR4_LFA | IMR4_AIS | IMR4_LOS | IMR4_SLIP));
+ if (conf->media == LINE_T1) {
+ cpc_writeb(falcbase + F_REG(IMR3, ch),
+ cpc_readb(falcbase + F_REG(IMR3, ch)) & ~IMR3_LLBSC);
+ } else {
+ cpc_writeb(falcbase + F_REG(IPC, ch),
+ cpc_readb(falcbase + F_REG(IPC, ch)) | IPC_SCI);
+ cpc_writeb(falcbase + F_REG(IMR2, ch),
+ cpc_readb(falcbase + F_REG(IMR2, ch)) &
+ ~(IMR2_FAR | IMR2_LFA | IMR2_AIS | IMR2_LOS));
+ cpc_writeb(falcbase + F_REG(IMR1, ch),
+ cpc_readb(falcbase + F_REG(IMR1, ch)) & ~IMR1_LLBSC);
+ if (pfalc->multiframe_mode) {
+ cpc_writeb(falcbase + F_REG(IMR2, ch),
+ cpc_readb(falcbase + F_REG(IMR2, ch)) &
+ ~(IMR2_T400MS | IMR2_MFAR));
+ } else {
+ cpc_writeb(falcbase + F_REG(IMR2, ch),
+ cpc_readb(falcbase + F_REG(IMR2, ch)) |
+ IMR2_T400MS | IMR2_MFAR);
+ }
+ }
+}
+
+void
+falc_open_timeslot(pc300_t *card, int ch, int timeslot)
+{
+ uclong falcbase = card->hw.falcbase;
+ ucchar tshf = card->chan[ch].falc.offset;
+
+ cpc_writeb(falcbase + F_REG((ICB1 + (timeslot - tshf)/8), ch),
+ cpc_readb(falcbase + F_REG((ICB1 + (timeslot - tshf)/8), ch)) &
+ ~(0x80 >> ((timeslot - tshf) & 0x07)));
+ cpc_writeb(falcbase + F_REG((TTR1 + timeslot/8), ch),
+ cpc_readb(falcbase + F_REG((TTR1 + timeslot/8), ch)) |
+ (0x80 >> (timeslot & 0x07)));
+ cpc_writeb(falcbase + F_REG((RTR1 + timeslot/8), ch),
+ cpc_readb(falcbase + F_REG((RTR1 + timeslot/8), ch)) |
+ (0x80 >> (timeslot & 0x07)));
+}
+
+void
+falc_close_timeslot(pc300_t *card, int ch, int timeslot)
+{
+ uclong falcbase = card->hw.falcbase;
+ ucchar tshf = card->chan[ch].falc.offset;
+
+ cpc_writeb(falcbase + F_REG((ICB1 + (timeslot - tshf)/8), ch),
+ cpc_readb(falcbase + F_REG((ICB1 + (timeslot - tshf)/8), ch)) |
+ (0x80 >> ((timeslot - tshf) & 0x07)));
+ cpc_writeb(falcbase + F_REG((TTR1 + timeslot/8), ch),
+ cpc_readb(falcbase + F_REG((TTR1 + timeslot/8), ch)) &
+ ~(0x80 >> (timeslot & 0x07)));
+ cpc_writeb(falcbase + F_REG((RTR1 + timeslot/8), ch),
+ cpc_readb(falcbase + F_REG((RTR1 + timeslot/8), ch)) &
+ ~(0x80 >> (timeslot & 0x07)));
+}
+
+void
+falc_close_all_timeslots(pc300_t *card, int ch)
+{
+ pc300ch_t *chan = (pc300ch_t *)&card->chan[ch];
+ pc300chconf_t *conf = (pc300chconf_t *)&chan->conf;
+ uclong falcbase = card->hw.falcbase;
+
+ cpc_writeb(falcbase + F_REG(ICB1, ch), 0xff);
+ cpc_writeb(falcbase + F_REG(TTR1, ch), 0);
+ cpc_writeb(falcbase + F_REG(RTR1, ch), 0);
+ cpc_writeb(falcbase + F_REG(ICB2, ch), 0xff);
+ cpc_writeb(falcbase + F_REG(TTR2, ch), 0);
+ cpc_writeb(falcbase + F_REG(RTR2, ch), 0);
+ cpc_writeb(falcbase + F_REG(ICB3, ch), 0xff);
+ cpc_writeb(falcbase + F_REG(TTR3, ch), 0);
+ cpc_writeb(falcbase + F_REG(RTR3, ch), 0);
+ if (conf->media == LINE_E1) {
+ cpc_writeb(falcbase + F_REG(ICB4, ch), 0xff);
+ cpc_writeb(falcbase + F_REG(TTR4, ch), 0);
+ cpc_writeb(falcbase + F_REG(RTR4, ch), 0);
+ }
+}
+
+void
+falc_open_all_timeslots(pc300_t *card, int ch)
+{
+ pc300ch_t *chan = (pc300ch_t *)&card->chan[ch];
+ pc300chconf_t *conf = (pc300chconf_t *)&chan->conf;
+ uclong falcbase = card->hw.falcbase;
+
+ /* Timeslot 0 is never enabled */
+ cpc_writeb(falcbase + F_REG(ICB1, ch), 0);
+ cpc_writeb(falcbase + F_REG(TTR1, ch), 0x7f);
+ cpc_writeb(falcbase + F_REG(RTR1, ch), 0x7f);
+ cpc_writeb(falcbase + F_REG(ICB2, ch), 0);
+ cpc_writeb(falcbase + F_REG(TTR2, ch), 0xff);
+ cpc_writeb(falcbase + F_REG(RTR2, ch), 0xff);
+ cpc_writeb(falcbase + F_REG(ICB3, ch), 0);
+ cpc_writeb(falcbase + F_REG(TTR3, ch), 0xff);
+ cpc_writeb(falcbase + F_REG(RTR3, ch), 0xff);
+ if (conf->media == LINE_E1) {
+ cpc_writeb(falcbase + F_REG(ICB4, ch), 0);
+ cpc_writeb(falcbase + F_REG(TTR4, ch), 0xff);
+ cpc_writeb(falcbase + F_REG(RTR4, ch), 0xff);
+ } else {
+ cpc_writeb(falcbase + F_REG(ICB4, ch), 0xff);
+ cpc_writeb(falcbase + F_REG(TTR4, ch), 0x80);
+ cpc_writeb(falcbase + F_REG(RTR4, ch), 0x80);
+ }
+}
+
+void
+falc_init_timeslot(pc300_t *card, int ch)
+{
+ pc300ch_t *chan = (pc300ch_t *)&card->chan[ch];
+ pc300chconf_t *conf = (pc300chconf_t *)&chan->conf;
+ falc_t *pfalc = (falc_t *)&chan->falc;
+ int tslot;
+
+ for (tslot = 0 ; tslot < pfalc->num_channels ; tslot++) {
+ if (conf->tslot_bitmap & (1<<tslot)) {
+ // Channel enabled
+ falc_open_timeslot(card, ch, tslot + 1);
+ } else {
+ // Channel disabled
+ falc_close_timeslot(card, ch, tslot + 1);
+ }
+ }
+}
+
+void
+falc_enable_comm(pc300_t *card, int ch)
+{
+ pc300ch_t *chan = (pc300ch_t *)&card->chan[ch];
+ falc_t *pfalc = (falc_t *)&chan->falc;
+
+ if (pfalc->full_bandwidth) {
+ falc_open_all_timeslots(card, ch);
+ } else {
+ falc_init_timeslot(card, ch);
+ }
+ // CTS/DCD ON
+ cpc_writeb(card->hw.falcbase + CPLD_REG1,
+ cpc_readb(card->hw.falcbase + CPLD_REG1) |
+ ((CPLD_REG1_FALC_DCD | CPLD_REG1_FALC_CTS) << (2*ch)));
+}
+
+void
+falc_disable_comm(pc300_t *card, int ch)
+{
+ pc300ch_t *chan = (pc300ch_t *)&card->chan[ch];
+ falc_t *pfalc = (falc_t *)&chan->falc;
+
+ if (pfalc->loop_active != 2) {
+ falc_close_all_timeslots(card, ch);
+ }
+ // CTS/DCD OFF
+ cpc_writeb(card->hw.falcbase + CPLD_REG1,
+ cpc_readb(card->hw.falcbase + CPLD_REG1) &
+ ~((CPLD_REG1_FALC_DCD | CPLD_REG1_FALC_CTS) << (2*ch)));
+}
+
+void
+falc_init_t1(pc300_t *card, int ch)
+{
+ pc300ch_t *chan = (pc300ch_t *)&card->chan[ch];
+ pc300chconf_t *conf = (pc300chconf_t *)&chan->conf;
+ falc_t *pfalc = (falc_t *)&chan->falc;
+ uclong falcbase = card->hw.falcbase;
+ ucchar dja = (ch ? (LIM2_DJA2|LIM2_DJA1) : 0);
+
+ /* Switch to T1 mode (PCM 24) */
+ cpc_writeb(falcbase + F_REG(FMR1, ch), FMR1_PMOD);
+
+ /* Wait 20 us for setup */
+ udelay(20);
+
+ /* Transmit Buffer Size (1 frame) */
+ cpc_writeb(falcbase + F_REG(SIC1, ch), SIC1_XBS0);
+
+ /* Clock mode */
+ if (conf->clkrate) { /* Master mode */
+ cpc_writeb(falcbase + F_REG(LIM0, ch),
+ cpc_readb(falcbase + F_REG(LIM0, ch)) | LIM0_MAS);
+ } else { /* Slave mode */
+ cpc_writeb(falcbase + F_REG(LIM0, ch),
+ cpc_readb(falcbase + F_REG(LIM0, ch)) & ~LIM0_MAS);
+ cpc_writeb(falcbase + F_REG(LOOP, ch),
+ cpc_readb(falcbase + F_REG(LOOP, ch)) & ~LOOP_RTM);
+ }
+
+ cpc_writeb(falcbase + F_REG(IPC, ch), IPC_SCI);
+ cpc_writeb(falcbase + F_REG(FMR0, ch),
+ cpc_readb(falcbase + F_REG(FMR0, ch)) &
+ ~(FMR0_XC0 | FMR0_XC1 | FMR0_RC0 | FMR0_RC1));
+
+ switch (conf->lcode) {
+ case PC300_LC_AMI:
+ cpc_writeb(falcbase + F_REG(FMR0, ch),
+ cpc_readb(falcbase + F_REG(FMR0, ch)) |
+ FMR0_XC1 | FMR0_RC1);
+ /* Clear Channel register to ON for all channels */
+ cpc_writeb(falcbase + F_REG(CCB1, ch), 0xff);
+ cpc_writeb(falcbase + F_REG(CCB2, ch), 0xff);
+ cpc_writeb(falcbase + F_REG(CCB3, ch), 0xff);
+ break;
+
+ case PC300_LC_B8ZS:
+ cpc_writeb(falcbase + F_REG(FMR0, ch),
+ cpc_readb(falcbase + F_REG(FMR0, ch)) |
+ FMR0_XC0 | FMR0_XC1 | FMR0_RC0 | FMR0_RC1);
+ break;
+
+ case PC300_LC_NRZ:
+ cpc_writeb(falcbase + F_REG(FMR0, ch),
+ cpc_readb(falcbase + F_REG(FMR0, ch)) | 0x00);
+ break;
+ }
+
+ cpc_writeb(falcbase + F_REG(LIM0, ch),
+ cpc_readb(falcbase + F_REG(LIM0, ch)) | LIM0_ELOS);
+ cpc_writeb(falcbase + F_REG(LIM0, ch),
+ cpc_readb(falcbase + F_REG(LIM0, ch)) &
+ ~(LIM0_SCL1 | LIM0_SCL0));
+ /* Set interface mode to 2 MBPS */
+ cpc_writeb(falcbase + F_REG(FMR1, ch),
+ cpc_readb(falcbase + F_REG(FMR1, ch)) | FMR1_IMOD);
+
+ switch (conf->fr_mode) {
+ case PC300_FR_ESF:
+ pfalc->multiframe_mode = 0;
+ cpc_writeb(falcbase + F_REG(FMR4, ch),
+ cpc_readb(falcbase + F_REG(FMR4, ch)) | FMR4_FM1);
+ cpc_writeb(falcbase + F_REG(FMR1, ch),
+ cpc_readb(falcbase + F_REG(FMR1, ch)) |
+ FMR1_CRC | FMR1_EDL);
+ cpc_writeb(falcbase + F_REG(XDL1, ch), 0);
+ cpc_writeb(falcbase + F_REG(XDL2, ch), 0);
+ cpc_writeb(falcbase + F_REG(XDL3, ch), 0);
+ cpc_writeb(falcbase + F_REG(FMR0, ch),
+ cpc_readb(falcbase + F_REG(FMR0, ch)) & ~FMR0_SRAF);
+ cpc_writeb(falcbase + F_REG(FMR2, ch),
+ cpc_readb(falcbase + F_REG(FMR2, ch)) |
+ FMR2_MCSP | FMR2_SSP);
+ break;
+
+ case PC300_FR_D4:
+ pfalc->multiframe_mode = 1;
+ cpc_writeb(falcbase + F_REG(FMR4, ch),
+ cpc_readb(falcbase + F_REG(FMR4, ch)) &
+ ~(FMR4_FM1 | FMR4_FM0));
+ cpc_writeb(falcbase + F_REG(FMR0, ch),
+ cpc_readb(falcbase + F_REG(FMR0, ch)) | FMR0_SRAF);
+ cpc_writeb(falcbase + F_REG(FMR2, ch),
+ cpc_readb(falcbase + F_REG(FMR2, ch)) & ~FMR2_SSP);
+ break;
+ }
+
+ /* Enable Automatic Resynchronization */
+ cpc_writeb(falcbase + F_REG(FMR4, ch),
+ cpc_readb(falcbase + F_REG(FMR4, ch)) | FMR4_AUTO);
+
+ /* Transmit Automatic Remote Alarm */
+ cpc_writeb(falcbase + F_REG(FMR2, ch),
+ cpc_readb(falcbase + F_REG(FMR2, ch)) | FMR2_AXRA);
+
+ /* Channel translation mode 1 : one to one */
+ cpc_writeb(falcbase + F_REG(FMR1, ch),
+ cpc_readb(falcbase + F_REG(FMR1, ch)) | FMR1_CTM);
+
+ /* No signaling */
+ cpc_writeb(falcbase + F_REG(FMR1, ch),
+ cpc_readb(falcbase + F_REG(FMR1, ch)) & ~FMR1_SIGM);
+ cpc_writeb(falcbase + F_REG(FMR5, ch),
+ cpc_readb(falcbase + F_REG(FMR5, ch)) &
+ ~(FMR5_EIBR | FMR5_SRS));
+ cpc_writeb(falcbase + F_REG(CCR1, ch), 0);
+
+ cpc_writeb(falcbase + F_REG(LIM1, ch),
+ cpc_readb(falcbase + F_REG(LIM1, ch)) | LIM1_RIL0 | LIM1_RIL1);
+
+ switch (conf->lbo) {
+ /* Provides proper Line Build Out */
+ case PC300_LBO_0_DB:
+ cpc_writeb(falcbase + F_REG(LIM2, ch), (LIM2_LOS1 | dja));
+ cpc_writeb(falcbase + F_REG(XPM0, ch), 0x5a);
+ cpc_writeb(falcbase + F_REG(XPM1, ch), 0x8f);
+ cpc_writeb(falcbase + F_REG(XPM2, ch), 0x20);
+ break;
+ case PC300_LBO_7_5_DB:
+ cpc_writeb(falcbase + F_REG(LIM2, ch), (0x40 | LIM2_LOS1 | dja));
+ cpc_writeb(falcbase + F_REG(XPM0, ch), 0x11);
+ cpc_writeb(falcbase + F_REG(XPM1, ch), 0x02);
+ cpc_writeb(falcbase + F_REG(XPM2, ch), 0x20);
+ break;
+ case PC300_LBO_15_DB:
+ cpc_writeb(falcbase + F_REG(LIM2, ch), (0x80 | LIM2_LOS1 | dja));
+ cpc_writeb(falcbase + F_REG(XPM0, ch), 0x8e);
+ cpc_writeb(falcbase + F_REG(XPM1, ch), 0x01);
+ cpc_writeb(falcbase + F_REG(XPM2, ch), 0x20);
+ break;
+ case PC300_LBO_22_5_DB:
+ cpc_writeb(falcbase + F_REG(LIM2, ch), (0xc0 | LIM2_LOS1 | dja));
+ cpc_writeb(falcbase + F_REG(XPM0, ch), 0x09);
+ cpc_writeb(falcbase + F_REG(XPM1, ch), 0x01);
+ cpc_writeb(falcbase + F_REG(XPM2, ch), 0x20);
+ break;
+ }
+
+ /* Transmit Clock-Slot Offset */
+ cpc_writeb(falcbase + F_REG(XC0, ch),
+ cpc_readb(falcbase + F_REG(XC0, ch)) | 0x01);
+ /* Transmit Time-slot Offset */
+ cpc_writeb(falcbase + F_REG(XC1, ch), 0x3e);
+ /* Receive Clock-Slot offset */
+ cpc_writeb(falcbase + F_REG(RC0, ch), 0x05);
+ /* Receive Time-slot offset */
+ cpc_writeb(falcbase + F_REG(RC1, ch), 0x00);
+
+ /* LOS Detection after 176 consecutive 0s */
+ cpc_writeb(falcbase + F_REG(PCDR, ch), 0x0a);
+ /* LOS Recovery after 22 ones in the time window of PCD */
+ cpc_writeb(falcbase + F_REG(PCRR, ch), 0x15);
+
+ cpc_writeb(falcbase + F_REG(IDLE, ch), 0x7f);
+
+ if (conf->fr_mode == PC300_FR_ESF_JAPAN) {
+ cpc_writeb(falcbase + F_REG(RC1, ch),
+ cpc_readb(falcbase + F_REG(RC1, ch)) | 0x80);
+ }
+
+ falc_close_all_timeslots(card, ch);
+}
+
+void
+falc_init_e1(pc300_t *card, int ch)
+{
+ pc300ch_t *chan = (pc300ch_t *)&card->chan[ch];
+ pc300chconf_t *conf = (pc300chconf_t *)&chan->conf;
+ falc_t *pfalc = (falc_t *)&chan->falc;
+ uclong falcbase = card->hw.falcbase;
+ ucchar dja = (ch ? (LIM2_DJA2|LIM2_DJA1) : 0);
+
+ /* Switch to E1 mode (PCM 30) */
+ cpc_writeb(falcbase + F_REG(FMR1, ch),
+ cpc_readb(falcbase + F_REG(FMR1, ch)) & ~FMR1_PMOD);
+
+ /* Clock mode */
+ if (conf->clkrate) { /* Master mode */
+ cpc_writeb(falcbase + F_REG(LIM0, ch),
+ cpc_readb(falcbase + F_REG(LIM0, ch)) | LIM0_MAS);
+ } else { /* Slave mode */
+ cpc_writeb(falcbase + F_REG(LIM0, ch),
+ cpc_readb(falcbase + F_REG(LIM0, ch)) & ~LIM0_MAS);
+ }
+ cpc_writeb(falcbase + F_REG(LOOP, ch),
+ cpc_readb(falcbase + F_REG(LOOP, ch)) & ~LOOP_RTM);
+
+ cpc_writeb(falcbase + F_REG(IPC, ch), IPC_SCI);
+ cpc_writeb(falcbase + F_REG(FMR0, ch),
+ cpc_readb(falcbase + F_REG(FMR0, ch)) &
+ ~(FMR0_XC0 | FMR0_XC1 | FMR0_RC0 | FMR0_RC1));
+
+ switch (conf->lcode) {
+ case PC300_LC_AMI:
+ cpc_writeb(falcbase + F_REG(FMR0, ch),
+ cpc_readb(falcbase + F_REG(FMR0, ch)) |
+ FMR0_XC1 | FMR0_RC1);
+ break;
+
+ case PC300_LC_HDB3:
+ cpc_writeb(falcbase + F_REG(FMR0, ch),
+ cpc_readb(falcbase + F_REG(FMR0, ch)) |
+ FMR0_XC0 | FMR0_XC1 | FMR0_RC0 | FMR0_RC1);
+ break;
+
+ case PC300_LC_NRZ:
+ break;
+ }
+
+ cpc_writeb(falcbase + F_REG(LIM0, ch),
+ cpc_readb(falcbase + F_REG(LIM0, ch)) &
+ ~(LIM0_SCL1 | LIM0_SCL0));
+ /* Set interface mode to 2 MBPS */
+ cpc_writeb(falcbase + F_REG(FMR1, ch),
+ cpc_readb(falcbase + F_REG(FMR1, ch)) | FMR1_IMOD);
+
+ cpc_writeb(falcbase + F_REG(XPM0, ch), 0x18);
+ cpc_writeb(falcbase + F_REG(XPM1, ch), 0x03);
+ cpc_writeb(falcbase + F_REG(XPM2, ch), 0x00);
+
+ switch (conf->fr_mode) {
+ case PC300_FR_MF_CRC4:
+ pfalc->multiframe_mode = 1;
+ cpc_writeb(falcbase + F_REG(FMR1, ch),
+ cpc_readb(falcbase + F_REG(FMR1, ch)) | FMR1_XFS);
+ cpc_writeb(falcbase + F_REG(FMR2, ch),
+ cpc_readb(falcbase + F_REG(FMR2, ch)) | FMR2_RFS1);
+ cpc_writeb(falcbase + F_REG(FMR2, ch),
+ cpc_readb(falcbase + F_REG(FMR2, ch)) & ~FMR2_RFS0);
+ cpc_writeb(falcbase + F_REG(FMR3, ch),
+ cpc_readb(falcbase + F_REG(FMR3, ch)) & ~FMR3_EXTIW);
+
+ /* MultiFrame Resynchronization */
+ cpc_writeb(falcbase + F_REG(FMR1, ch),
+ cpc_readb(falcbase + F_REG(FMR1, ch)) | FMR1_MFCS);
+
+ /* Automatic Loss of Multiframe > 914 CRC errors */
+ cpc_writeb(falcbase + F_REG(FMR2, ch),
+ cpc_readb(falcbase + F_REG(FMR2, ch)) | FMR2_ALMF);
+
+ /* S1 and SI1/SI2 spare Bits set to 1 */
+ cpc_writeb(falcbase + F_REG(XSP, ch),
+ cpc_readb(falcbase + F_REG(XSP, ch)) & ~XSP_AXS);
+ cpc_writeb(falcbase + F_REG(XSP, ch),
+ cpc_readb(falcbase + F_REG(XSP, ch)) | XSP_EBP);
+ cpc_writeb(falcbase + F_REG(XSP, ch),
+ cpc_readb(falcbase + F_REG(XSP, ch)) |
+ XSP_XS13 | XSP_XS15);
+ break;
+
+ case PC300_FR_MF_NON_CRC4:
+ case PC300_FR_D4:
+ pfalc->multiframe_mode = 0;
+ cpc_writeb(falcbase + F_REG(FMR1, ch),
+ cpc_readb(falcbase + F_REG(FMR1, ch)) & ~FMR1_XFS);
+ cpc_writeb(falcbase + F_REG(FMR2, ch),
+ cpc_readb(falcbase + F_REG(FMR2, ch)) & ~FMR2_RFS1);
+ cpc_writeb(falcbase + F_REG(FMR2, ch),
+ cpc_readb(falcbase + F_REG(FMR2, ch)) & ~FMR2_RFS0);
+ cpc_writeb(falcbase + F_REG(XSW, ch),
+ cpc_readb(falcbase + F_REG(XSW, ch)) | XSW_XSIS);
+ cpc_writeb(falcbase + F_REG(XSP, ch),
+ cpc_readb(falcbase + F_REG(XSP, ch)) | XSP_XSIF);
+ break;
+ }
+
+ /* Automatic Force Resynchronization */
+ cpc_writeb(falcbase + F_REG(FMR1, ch),
+ cpc_readb(falcbase + F_REG(FMR1, ch)) | FMR1_AFR);
+
+ /* Transmit Automatic Remote Alarm */
+ cpc_writeb(falcbase + F_REG(FMR2, ch),
+ cpc_readb(falcbase + F_REG(FMR2, ch)) | FMR2_AXRA);
+
+ /* No signaling */
+ cpc_writeb(falcbase + F_REG(XSP, ch),
+ cpc_readb(falcbase + F_REG(XSP, ch)) & ~XSP_CASEN);
+ cpc_writeb(falcbase + F_REG(CCR1, ch), 0);
+
+ cpc_writeb(falcbase + F_REG(LIM1, ch),
+ cpc_readb(falcbase + F_REG(LIM1, ch)) | LIM1_RIL0 | LIM1_RIL1);
+ cpc_writeb(falcbase + F_REG(LIM2, ch), (LIM2_LOS1 | dja));
+
+ /* Transmit Clock-Slot Offset */
+ cpc_writeb(falcbase + F_REG(XC0, ch),
+ cpc_readb(falcbase + F_REG(XC0, ch)) | 0x01);
+ /* Transmit Time-slot Offset */
+ cpc_writeb(falcbase + F_REG(XC1, ch), 0x3e);
+ /* Receive Clock-Slot offset */
+ cpc_writeb(falcbase + F_REG(RC0, ch), 0x05);
+ /* Receive Time-slot offset */
+ cpc_writeb(falcbase + F_REG(RC1, ch), 0x00);
+
+ /* LOS Detection after 176 consecutive 0s */
+ cpc_writeb(falcbase + F_REG(PCDR, ch), 0x0a);
+ /* LOS Recovery after 22 ones in the time window of PCD */
+ cpc_writeb(falcbase + F_REG(PCRR, ch), 0x15);
+
+ /* Transmit Spare Bits for National Use (Y-Bits, Sn-Bits, Sa-Bits) */
+ cpc_writeb(falcbase + F_REG(XSW, ch),
+ cpc_readb(falcbase + F_REG(XSW, ch)) |
+ XSW_XY0 | XSW_XY1 | XSW_XY2 | XSW_XY3 | XSW_XY4);
+
+ cpc_writeb(falcbase + F_REG(IDLE, ch), 0x7f);
+
+ falc_close_all_timeslots(card, ch);
+}
+
+void
+falc_init_hdlc(pc300_t *card, int ch)
+{
+ uclong falcbase = card->hw.falcbase;
+
+ /* Enable transparent data transfer */
+ cpc_writeb(falcbase + F_REG(MODE, ch),
+ cpc_readb(falcbase + F_REG(MODE, ch)) | MODE_HRAC | MODE_MDS2);
+ cpc_writeb(falcbase + F_REG(RAH2, ch), 0xff);
+ cpc_writeb(falcbase + F_REG(RAH1, ch), 0xff);
+ cpc_writeb(falcbase + F_REG(RAL2, ch), 0xff);
+ cpc_writeb(falcbase + F_REG(RAL1, ch), 0xff);
+
+ /* Tx/Rx reset */
+ falc_issue_cmd(card, ch, CMDR_RRES | CMDR_XRES | CMDR_SRES);
+
+ /* Enable interrupt sources */
+ falc_intr_enable(card, ch);
+}
+
+void
+te_config(pc300_t *card, int ch)
+{
+ pc300ch_t *chan = (pc300ch_t *)&card->chan[ch];
+ pc300chconf_t *conf = (pc300chconf_t *)&chan->conf;
+ falc_t *pfalc = (falc_t *)&chan->falc;
+ uclong falcbase = card->hw.falcbase;
+ ucchar dummy;
+ unsigned long flags;
+
+ memset(pfalc, 0, sizeof(falc_t));
+ switch (conf->media) {
+ case LINE_T1:
+ pfalc->num_channels = NUM_OF_T1_CHANNELS;
+ pfalc->offset = 1;
+ break;
+ case LINE_E1:
+ pfalc->num_channels = NUM_OF_E1_CHANNELS;
+ pfalc->offset = 0;
+ break;
+ }
+ if (conf->tslot_bitmap == 0xffffffffUL)
+ pfalc->full_bandwidth = 1;
+ else
+ pfalc->full_bandwidth = 0;
+
+ CPC_LOCK(card, flags);
+ /* Reset the FALC chip */
+ cpc_writeb(card->hw.falcbase + CPLD_REG1,
+ cpc_readb(card->hw.falcbase + CPLD_REG1) |
+ (CPLD_REG1_FALC_RESET << (2*ch)));
+ udelay(10000);
+ cpc_writeb(card->hw.falcbase + CPLD_REG1,
+ cpc_readb(card->hw.falcbase + CPLD_REG1) &
+ ~(CPLD_REG1_FALC_RESET << (2*ch)));
+
+ if (conf->media == LINE_T1) {
+ falc_init_t1(card, ch);
+ } else {
+ falc_init_e1(card, ch);
+ }
+ falc_init_hdlc(card, ch);
+ if (conf->rx_sens == PC300_RX_SENS_SH) {
+ cpc_writeb(falcbase + F_REG(LIM0, ch),
+ cpc_readb(falcbase + F_REG(LIM0, ch)) & ~LIM0_EQON);
+ } else {
+ cpc_writeb(falcbase + F_REG(LIM0, ch),
+ cpc_readb(falcbase + F_REG(LIM0, ch)) | LIM0_EQON);
+ }
+ cpc_writeb(card->hw.falcbase + CPLD_REG2,
+ cpc_readb(card->hw.falcbase + CPLD_REG2) |
+ ((CPLD_REG2_FALC_TX_CLK | CPLD_REG2_FALC_RX_CLK) << (2*ch)));
+
+ /* Clear all interrupt registers */
+ dummy = cpc_readb(falcbase + F_REG(FISR0, ch)) +
+ cpc_readb(falcbase + F_REG(FISR1, ch)) +
+ cpc_readb(falcbase + F_REG(FISR2, ch)) +
+ cpc_readb(falcbase + F_REG(FISR3, ch));
+ CPC_UNLOCK(card, flags);
+}
+
+void
+falc_check_status (pc300_t *card, int ch, unsigned char frs0)
+{
+ pc300ch_t *chan = (pc300ch_t *)&card->chan[ch];
+ pc300chconf_t *conf = (pc300chconf_t *)&chan->conf;
+ falc_t *pfalc = (falc_t *)&chan->falc;
+ uclong falcbase = card->hw.falcbase;
+
+ /* Verify AIS alarm */
+ if (frs0 & FRS0_AIS) {
+ if (!pfalc->blue_alarm) {
+ pfalc->blue_alarm = 1;
+ pfalc->ais++;
+ // EVENT_AIS
+ if (conf->media == LINE_T1) {
+ /* Disable this interrupt as it may otherwise interfere with
+ other working boards. */
+ cpc_writeb(falcbase + F_REG(IMR0, ch),
+ cpc_readb(falcbase + F_REG(IMR0, ch)) | IMR0_PDEN);
+ }
+ falc_disable_comm(card, ch);
+ // EVENT_AIS
+ }
+ } else {
+ pfalc->blue_alarm = 0;
+ }
+
+ /* Verify LOS */
+ if (frs0 & FRS0_LOS) {
+ if (! pfalc->red_alarm) {
+ pfalc->red_alarm = 1;
+ pfalc->los++;
+ if (!pfalc->blue_alarm) {
+ // EVENT_FALC_ABNORMAL
+ if (conf->media == LINE_T1) {
+ /* Disable this interrupt as it may otherwise interfere
+ with other working boards. */
+ cpc_writeb(falcbase + F_REG(IMR0, ch),
+ cpc_readb(falcbase + F_REG(IMR0, ch)) |
+ IMR0_PDEN);
+ }
+ falc_disable_comm(card, ch);
+ // EVENT_FALC_ABNORMAL
+ }
+ }
+ } else {
+ if (pfalc->red_alarm) {
+ pfalc->red_alarm = 0;
+ pfalc->losr++;
+ }
+ }
+
+ /* Verify LFA */
+ if (frs0 & FRS0_LFA) {
+ if (!pfalc->loss_fa) {
+ pfalc->loss_fa = 1;
+ pfalc->lfa++;
+ if (!pfalc->blue_alarm && !pfalc->red_alarm) {
+ // EVENT_FALC_ABNORMAL
+ if (conf->media == LINE_T1) {
+ /* Disable this interrupt as it may otherwise interfere
+ with other working boards. */
+ cpc_writeb(falcbase + F_REG(IMR0, ch),
+ cpc_readb(falcbase + F_REG(IMR0, ch)) |
+ IMR0_PDEN);
+ }
+ falc_disable_comm(card, ch);
+ // EVENT_FALC_ABNORMAL
+ }
+ }
+ } else {
+ if (pfalc->loss_fa) {
+ pfalc->loss_fa = 0;
+ pfalc->farec++;
+ }
+ }
+
+ /* Verify LMFA */
+ if ((pfalc->multiframe_mode) && (frs0 & FRS0_LMFA)) {
+ /* D4 or CRC4 frame mode */
+ if (! pfalc->loss_mfa) {
+ pfalc->loss_mfa = 1;
+ pfalc->lmfa++;
+ if (!pfalc->blue_alarm && !pfalc->red_alarm && !pfalc->loss_fa) {
+ // EVENT_FALC_ABNORMAL
+ if (conf->media == LINE_T1) {
+ /* Disable this interrupt as it may otherwise interfere
+ with other working boards. */
+ cpc_writeb(falcbase + F_REG(IMR0, ch),
+ cpc_readb(falcbase + F_REG(IMR0, ch)) |
+ IMR0_PDEN);
+ }
+ falc_disable_comm(card, ch);
+ // EVENT_FALC_ABNORMAL
+ }
+ }
+ } else {
+ pfalc->loss_mfa = 0;
+ }
+
+ if (pfalc->red_alarm || pfalc->loss_fa ||
+ pfalc->loss_mfa || pfalc->blue_alarm) {
+ if (pfalc->sync) {
+ pfalc->sync = 0;
+ cpc_writeb(falcbase + CPLD_REG2,
+ cpc_readb(falcbase + CPLD_REG2) &
+ ~(CPLD_REG2_FALC_LED2 << (2*ch)));
+ }
+ } else {
+ if ((!pfalc->sync)) {
+ pfalc->sync = 1;
+ cpc_writeb(falcbase + CPLD_REG2,
+ cpc_readb(falcbase + CPLD_REG2) |
+ (CPLD_REG2_FALC_LED2 << (2*ch)));
+ }
+ }
+
+ /* Verify Remote Alarm */
+ if (frs0 & FRS0_RRA) {
+ if (! pfalc->yellow_alarm) {
+ pfalc->yellow_alarm = 1;
+ pfalc->rai++;
+ if (pfalc->sync) {
+ // EVENT_RAI
+ falc_disable_comm(card, ch);
+ // EVENT_RAI
+ }
+ }
+ } else {
+ pfalc->yellow_alarm = 0;
+ }
+
+ if ((pfalc->sync) && (!(pfalc->yellow_alarm))) {
+ if (! pfalc->active) {
+ // EVENT_FALC_NORMAL
+ if (pfalc->loop_active) {
+ return;
+ }
+ if (conf->media == LINE_T1) {
+ cpc_writeb(falcbase + F_REG(IMR0, ch),
+ cpc_readb(falcbase + F_REG(IMR0, ch)) & ~IMR0_PDEN);
+ }
+ falc_enable_comm(card, ch);
+ // EVENT_FALC_NORMAL
+ pfalc->active = 1;
+ }
+ } else {
+ if (pfalc->active) {
+ pfalc->active = 0;
+ }
+ }
+}
+
+void
+falc_update_stats(pc300_t *card, int ch)
+{
+ pc300ch_t *chan = (pc300ch_t *)&card->chan[ch];
+ pc300chconf_t *conf = (pc300chconf_t *)&chan->conf;
+ falc_t *pfalc = (falc_t *)&chan->falc;
+ uclong falcbase = card->hw.falcbase;
+ ucshort counter;
+
+ counter = cpc_readb(falcbase + F_REG(FECL, ch));
+ counter |= cpc_readb(falcbase + F_REG(FECH, ch)) << 8;
+ pfalc->fec += counter;
+
+ counter = cpc_readb(falcbase + F_REG(CVCL, ch));
+ counter |= cpc_readb(falcbase + F_REG(CVCH, ch)) << 8;
+ pfalc->cvc += counter;
+
+ counter = cpc_readb(falcbase + F_REG(CECL, ch));
+ counter |= cpc_readb(falcbase + F_REG(CECH, ch)) << 8;
+ pfalc->cec += counter;
+
+ counter = cpc_readb(falcbase + F_REG(EBCL, ch));
+ counter |= cpc_readb(falcbase + F_REG(EBCH, ch)) << 8;
+ pfalc->ebc += counter;
+
+ if (cpc_readb(falcbase + F_REG(LCR1, ch)) & LCR1_EPRM) {
+ counter = cpc_readb(falcbase + F_REG(BECL, ch));
+ counter |= cpc_readb(falcbase + F_REG(BECH, ch)) << 8;
+ pfalc->bec += counter;
+
+ if (((conf->media == LINE_T1) &&
+ (cpc_readb(falcbase + F_REG(FRS1, ch)) & FRS1_LLBAD) &&
+ (!(cpc_readb(falcbase + F_REG(FRS1, ch)) & FRS1_PDEN)))
+ ||
+ ((conf->media == LINE_E1) &&
+ (cpc_readb(falcbase + F_REG(RSP, ch)) & RSP_LLBAD))) {
+ pfalc->prbs = 2;
+ } else {
+ pfalc->prbs = 1;
+ }
+ }
+}
+
+void
+falc_remote_loop(pc300_t *card, int ch, int loop_on)
+{
+ pc300ch_t *chan = (pc300ch_t *)&card->chan[ch];
+ pc300chconf_t *conf = (pc300chconf_t *)&chan->conf;
+ falc_t *pfalc = (falc_t *)&chan->falc;
+ uclong falcbase = card->hw.falcbase;
+
+ if (loop_on) {
+ // EVENT_FALC_ABNORMAL
+ if (conf->media == LINE_T1) {
+ /* Disable this interrupt as it may otherwise interfere with
+ other working boards. */
+ cpc_writeb(falcbase + F_REG(IMR0, ch),
+ cpc_readb(falcbase + F_REG(IMR0, ch)) | IMR0_PDEN);
+ }
+ falc_disable_comm(card, ch);
+ // EVENT_FALC_ABNORMAL
+ cpc_writeb(falcbase + F_REG(LIM1, ch),
+ cpc_readb(falcbase + F_REG(LIM1, ch)) | LIM1_RL);
+ pfalc->loop_active = 1;
+ } else {
+ cpc_writeb(falcbase + F_REG(LIM1, ch),
+ cpc_readb(falcbase + F_REG(LIM1, ch)) & ~LIM1_RL);
+ pfalc->sync = 0;
+ cpc_writeb(falcbase + CPLD_REG2,
+ cpc_readb(falcbase + CPLD_REG2) &
+ ~(CPLD_REG2_FALC_LED2 << (2*ch)));
+ pfalc->active = 0;
+ falc_issue_cmd(card, ch, CMDR_XRES);
+ pfalc->loop_active = 0;
+ }
+}
+
+/**********************************/
+/*** Net Interface Routines ***/
+/**********************************/
+int cpc_queue_xmit(struct sk_buff *skb, struct device *dev)
+{
+ pc300dev_t *d = (pc300dev_t *)dev->priv;
+ pc300ch_t *chan = (pc300ch_t *)d->chan;
+ pc300_t *card = (pc300_t *)chan->card;
+ struct enet_statistics *stats = &d->hdlc->stats;
+ int ch = chan->channel;
+ volatile pcsca_bd_t *ptdescr;
+ uclong flags;
+#ifdef PC300_DEBUG_TX
+ int i;
+#endif
+
+ if (dev->tbusy) {
+ ucchar ilar;
+
+ if (time_before(jiffies, dev->trans_start + PC300_TX_TIMEOUT))
+ return 1;
+
+ stats->tx_errors++;
+ stats->tx_aborted_errors++;
+ printk("%s: transmit timed out, restarting channel.", dev->name);
+ CPC_LOCK(card, flags);
+ if ((ilar = cpc_readb(card->hw.scabase + ILAR)) != 0) {
+ printk("(ILAR=0x%x)", ilar);
+ cpc_writeb(card->hw.scabase + ILAR, ilar);
+ cpc_writeb(card->hw.scabase + DMER, 0x80);
+ }
+ printk("\n");
+ cpc_writeb(card->hw.scabase + M_REG(CMD, ch), CMD_TX_BUF_CLR);
+ if (d->tx_skb) {
+ dev_kfree_skb(d->tx_skb);
+ d->tx_skb = NULL;
+ }
+ if (card->hw.type == PC300_TE) {
+ cpc_writeb(card->hw.falcbase + CPLD_REG2,
+ cpc_readb(card->hw.falcbase + CPLD_REG2) &
+ ~(CPLD_REG2_FALC_LED1 << (2*ch)));
+ }
+ CPC_UNLOCK(card, flags);
+ dev->tbusy = 0;
+ }
+ if (test_and_set_bit(0, (void*)&dev->tbusy)) {
+ printk("%s: transmitter access conflict.\n", dev->name);
+ return 1;
+ }
+
+ /* Clean up descriptors from previous transmission */
+ while (chan->tx_first_bd != chan->tx_next_bd) {
+ ptdescr = (pcsca_bd_t *)
+ (card->hw.rambase + TX_BD_ADDR(ch, chan->tx_first_bd));
+ cpc_writeb(&ptdescr->status, 0);
+ chan->tx_first_bd = (chan->tx_first_bd + 1) & (N_DMA_TX_BUF - 1);
+ }
+ /* Clean up next free descriptor to avoid race problems with timeout
+ conditions */
+ ptdescr = (pcsca_bd_t *)
+ (card->hw.rambase + TX_BD_ADDR(ch, chan->tx_next_bd));
+ cpc_writeb(&ptdescr->status, 0);
+
+ /* Write buffer to DMA buffers */
+ if(dma_buf_write(card, ch, (ucchar *)skb->data, skb->len) != 0) {
+ CPC_LOCK(card, flags);
+// printk("%s: write error. Dropping TX packet.\n", dev->name);
+ dev->tbusy = 0;
+ CPC_UNLOCK(card, flags);
+ stats->tx_dropped++;
+ return 1;
+ }
+
+#ifdef PC300_DEBUG_TX
+ printk("%s T:", dev->name);
+ for(i = 0 ; i < skb->len ; i++)
+ printk(" %02x", *(skb->data + i));
+ printk("\n");
+#endif
+
+ d->tx_skb = skb;
+ dev->trans_start = jiffies;
+
+ /* Start transmission */
+ CPC_LOCK(card, flags);
+ cpc_writel(card->hw.scabase + DTX_REG(EDAL, ch),
+ TX_BD_ADDR(ch, chan->tx_next_bd));
+ cpc_writeb(card->hw.scabase + M_REG(CMD, ch), CMD_TX_ENA);
+ cpc_writeb(card->hw.scabase + DSR_TX(ch), DSR_DE);
+ if (card->hw.type == PC300_TE) {
+ cpc_writeb(card->hw.falcbase + CPLD_REG2,
+ cpc_readb(card->hw.falcbase + CPLD_REG2) |
+ (CPLD_REG2_FALC_LED1 << (2*ch)));
+ }
+ CPC_UNLOCK(card, flags);
+
+ return 0;
+}
+
+void
+cpc_net_rx(hdlc_device *hdlc)
+{
+ struct device *dev = hdlc_to_dev(hdlc);
+ pc300dev_t *d = (pc300dev_t *)dev->priv;
+ pc300ch_t *chan = (pc300ch_t *)d->chan;
+ pc300_t *card = (pc300_t *)chan->card;
+ struct enet_statistics *stats = &hdlc->stats;
+ int ch = chan->channel;
+#ifdef PC300_DEBUG_RX
+ int i;
+#endif
+ int rxb;
+ struct sk_buff *skb;
+
+ while (1) {
+ if ((rxb = dma_get_rx_frame_size(card, ch)) == -1)
+ return;
+
+ skb = dev_alloc_skb(rxb);
+ if (skb == NULL) {
+ printk("%s: Memory squeeze!!\n", dev->name);
+ return;
+ }
+ skb->dev = dev;
+
+ if((rxb = dma_buf_read(card, ch, skb)) <= 0) {
+ if (rxb < 0) { /* Invalid frame */
+ rxb = -rxb;
+ if (rxb & DST_OVR) {
+ stats->rx_errors++;
+ stats->rx_fifo_errors++;
+ }
+ if (rxb & DST_CRC) {
+ stats->rx_errors++;
+ stats->rx_crc_errors++;
+ }
+ if (rxb & (DST_RBIT | DST_SHRT | DST_ABT)) {
+ stats->rx_errors++;
+ stats->rx_frame_errors++;
+ }
+ }
+ dev_kfree_skb(skb);
+ continue;
+ }
+
+#ifdef PC300_DEBUG_RX
+ printk("%s R:", dev->name);
+ for(i = 0 ; i < skb->len ; i++)
+ printk(" %02x", *(skb->data + i));
+ printk("\n");
+#endif
+
+ switch(hdlc->mode & ~MODE_SOFT) {
+#ifdef CONFIG_PC300_X25
+ case MODE_X25:
+ {
+ int err;
+
+ skb->protocol=htons(ETH_P_X25);
+ skb->mac.raw=skb->data;
+ skb->dev=hdlc_to_dev(d->hdlc);
+
+ /* Send it to the upper layer */
+ if ((err = lapb_data_received(d, skb)) != LAPB_OK) {
+ printk("%s: lapb_data_received err - %d\n", dev->name, err);
+ dev_kfree_skb(skb);
+ }
+ stats->rx_bytes += rxb;
+ stats->rx_packets++;
+ break;
+ }
+#endif /* CONFIG_PC300_X25 */
+ default:
+ hdlc_netif_rx(hdlc, skb, 0);
+ break;
+ }
+ }
+}
+
+#ifdef CONFIG_PC300_X25
+/*********************************/
+/*** X.25 Support Routines ***/
+/*********************************/
+int
+cpc_x25_packetlayer_xmit(struct sk_buff *skb, struct device *dev)
+{
+ pc300dev_t *d = (pc300dev_t *)dev->priv;
+ int err;
+
+ switch (skb->data[0]) {
+ case 0x00:
+ break;
+
+ case 0x01:
+ if ((err = lapb_connect_request(d)) != LAPB_OK) {
+ if (err == LAPB_CONNECTED) {
+ /* Send connect confirm. msg to level 3 */
+ cpc_lapb_connected(d, 0);
+ } else {
+ printk("%s: lapb_connect_request error - %d\n",
+ dev->name, err);
+ }
+ }
+ dev_kfree_skb(skb);
+ return 0;
+
+ case 0x02:
+ if ((err = lapb_disconnect_request(d)) != LAPB_OK) {
+ if (err == LAPB_NOTCONNECTED) {
+ /* Send disconnect confirm. msg to level 3 */
+ cpc_lapb_disconnected(d, 0);
+ } else {
+ printk("%s: lapb_disconnect_request error - %d\n",
+ dev->name, err);
+ }
+ }
+ dev_kfree_skb(skb);
+ return 0;
+
+ default:
+ dev_kfree_skb(skb);
+ return 0;
+ }
+
+ skb_pull(skb, 1);
+
+ if ((err = lapb_data_request(d, skb)) != LAPB_OK) {
+ printk("%s: lapb_data_request error - %d\n", dev->name, err);
+ dev_kfree_skb(skb);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+void
+cpc_lapb_connected(void *token, int reason)
+{
+ pc300dev_t *d = (pc300dev_t *)token;
+ struct sk_buff *skb;
+ unsigned char *ptr;
+
+ if ((skb = dev_alloc_skb(1)) == NULL) {
+ printk("%s: out of memory\n", d->name);
+ return;
+ }
+
+ ptr = skb_put(skb, 1);
+ *ptr = 0x01;
+
+ skb->dev = hdlc_to_dev(d->hdlc);
+ skb->protocol = htons(ETH_P_X25);
+ skb->mac.raw = skb->data;
+ skb->pkt_type = PACKET_HOST;
+
+ netif_rx(skb);
+}
+
+void
+cpc_lapb_disconnected(void *token, int reason)
+{
+ pc300dev_t *d = (pc300dev_t *)token;
+ struct sk_buff *skb;
+ unsigned char *ptr;
+
+ if ((skb = dev_alloc_skb(1)) == NULL) {
+ printk("%s: out of memory\n", d->name);
+ return;
+ }
+
+ ptr = skb_put(skb, 1);
+ *ptr = 0x02;
+
+ skb->dev = hdlc_to_dev(d->hdlc);
+ skb->protocol = htons(ETH_P_X25);
+ skb->mac.raw = skb->data;
+ skb->pkt_type = PACKET_HOST;
+
+ netif_rx(skb);
+}
+
+void
+cpc_lapb_data_indication(void *token, struct sk_buff *skb)
+{
+ pc300dev_t *d = (pc300dev_t *)token;
+ unsigned char *ptr;
+
+ ptr = skb_push(skb, 1);
+ *ptr = 0x00;
+
+ skb->dev = hdlc_to_dev(d->hdlc);
+ skb->protocol = htons(ETH_P_X25);
+ skb->mac.raw = skb->data;
+ skb->pkt_type = PACKET_HOST;
+
+ netif_rx(skb);
+}
+
+void
+cpc_lapb_data_transmit(void *token, struct sk_buff *skb)
+{
+ pc300dev_t *d = (pc300dev_t *)token;
+ struct device *dev = hdlc_to_dev(d->hdlc);
+
+ cpc_queue_xmit (skb, dev);
+}
+#endif /* CONFIG_PC300_X25 */
+
+/************************************/
+/*** PC300 Interrupt Routines ***/
+/************************************/
+static void
+sca_intr(pc300_t *card)
+{
+ uclong scabase = card->hw.scabase;
+ volatile uclong status;
+ int ch;
+ int intr_count = 0;
+
+ while ((status = cpc_readl(scabase + ISR0)) != 0) {
+ for (ch = 0 ; ch < card->hw.nchan ; ch++) {
+ pc300ch_t *chan = &card->chan[ch];
+ pc300dev_t *d = &chan->d;
+ hdlc_device *hdlc = d->hdlc;
+ struct device *dev = hdlc_to_dev(hdlc);
+
+ dev->interrupt = 1;
+
+ /**** Reception ****/
+ if (status & IR0_DRX((IR0_DMIA|IR0_DMIB), ch)) {
+ ucchar drx_stat = cpc_readb(scabase + DSR_RX(ch));
+
+ /* Clear RX interrupts */
+ cpc_writeb(scabase + DSR_RX(ch), drx_stat | DSR_DWE);
+
+#ifdef PC300_DEBUG_INTR
+ printk("sca_intr: RX intr (st=0x%08lx, dsr=0x%02x)\n",
+ status, drx_stat);
+#endif
+ if (status & IR0_DRX(IR0_DMIA, ch)) {
+ if (drx_stat & DSR_BOF) {
+ chan->rx_first_bd = 0;
+ chan->rx_last_bd = N_DMA_RX_BUF - 1;
+ cpc_writel(scabase + DRX_REG(CDAL, ch),
+ RX_BD_ADDR(ch, chan->rx_first_bd));
+ cpc_writel(scabase + DRX_REG(EDAL, ch),
+ RX_BD_ADDR(ch, chan->rx_last_bd));
+ cpc_writew(scabase + DRX_REG(BFLL, ch), BD_DEF_LEN);
+ cpc_writeb(scabase + DSR_RX(ch), DSR_DE);
+ }
+ }
+ if (status & IR0_DRX(IR0_DMIB, ch)) {
+ if (drx_stat & DSR_EOM) {
+ if (card->hw.type == PC300_TE) {
+ cpc_writeb(card->hw.falcbase + CPLD_REG2,
+ cpc_readb(card->hw.falcbase + CPLD_REG2)
+ | (CPLD_REG2_FALC_LED1 << (2*ch)));
+ }
+ cpc_net_rx(hdlc);
+ if (card->hw.type == PC300_TE) {
+ cpc_writeb(card->hw.falcbase + CPLD_REG2,
+ cpc_readb(card->hw.falcbase + CPLD_REG2)
+ & ~(CPLD_REG2_FALC_LED1 << (2*ch)));
+ }
+ }
+ }
+ }
+
+ /**** Transmission ****/
+ if (status & IR0_DTX((IR0_EFT|IR0_DMIA|IR0_DMIB), ch)) {
+ ucchar dtx_stat = cpc_readb(scabase + DSR_TX(ch));
+
+ /* Clear TX interrupts */
+ cpc_writeb(scabase + DSR_TX(ch), dtx_stat | DSR_DWE);
+
+#ifdef PC300_DEBUG_INTR
+ printk("sca_intr: TX intr (st=0x%08lx, dsr=0x%02x)\n",
+ status, dtx_stat);
+#endif
+ if (status & IR0_DTX(IR0_EFT, ch)) {
+ if (dtx_stat & DSR_UDRF) {
+ if (cpc_readb(scabase + M_REG(TBN, ch)) != 0) {
+ cpc_writeb(scabase + M_REG(CMD, ch),
+ CMD_TX_BUF_CLR);
+ }
+ if (card->hw.type == PC300_TE) {
+ cpc_writeb(card->hw.falcbase + CPLD_REG2,
+ cpc_readb(card->hw.falcbase + CPLD_REG2)
+ & ~(CPLD_REG2_FALC_LED1 << (2*ch)));
+ }
+ if (d->tx_skb) {
+ struct sk_buff *skb = d->tx_skb;
+
+ dev_kfree_skb(skb);
+ d->tx_skb = NULL;
+ hdlc->stats.tx_errors++;
+ hdlc->stats.tx_fifo_errors++;
+ /* Tell the upper layer we are ready to transmit
+ more packets */
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+ }
+ }
+ if (status & IR0_DTX(IR0_DMIA, ch)) {
+ if (dtx_stat & DSR_BOF) {
+ }
+ }
+ if (status & IR0_DTX(IR0_DMIB, ch)) {
+ if (dtx_stat & DSR_EOM) {
+ if (card->hw.type == PC300_TE) {
+ cpc_writeb(card->hw.falcbase + CPLD_REG2,
+ cpc_readb(card->hw.falcbase + CPLD_REG2)
+ & ~(CPLD_REG2_FALC_LED1 << (2*ch)));
+ }
+ if (d->tx_skb) {
+ struct sk_buff *skb = d->tx_skb;
+
+ dev_kfree_skb(skb);
+ d->tx_skb = NULL;
+ hdlc->stats.tx_bytes += skb->len;
+ hdlc->stats.tx_packets++;
+ /* Tell the upper layer we are ready to transmit
+ more packets */
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+ }
+ }
+ }
+ dev->interrupt = 0;
+ }
+ if (++intr_count == 10)
+ /* Too much work at this board. Force exit */
+ break;
+ }
+}
+
+static void
+falc_t1_loop_detection(pc300_t *card, int ch, ucchar frs1)
+{
+ pc300ch_t *chan = (pc300ch_t *)&card->chan[ch];
+ falc_t *pfalc = (falc_t *)&chan->falc;
+ uclong falcbase = card->hw.falcbase;
+
+ if (((cpc_readb(falcbase + F_REG(LCR1, ch)) & LCR1_XPRBS) == 0) &&
+ !pfalc->loop_gen) {
+ if (frs1 & FRS1_LLBDD) {
+ // A Line Loop Back Deactuation signal detected
+ if (pfalc->loop_active) {
+ falc_remote_loop(card, ch, 0);
+ }
+ } else {
+ if ((frs1 & FRS1_LLBAD) &&
+ ((cpc_readb(falcbase + F_REG(LCR1, ch)) & LCR1_EPRM) == 0)) {
+ // A Line Loop Back Actuation signal detected
+ if (!pfalc->loop_active) {
+ falc_remote_loop(card, ch, 1);
+ }
+ }
+ }
+ }
+}
+
+static void
+falc_e1_loop_detection(pc300_t *card, int ch, ucchar rsp)
+{
+ pc300ch_t *chan = (pc300ch_t *)&card->chan[ch];
+ falc_t *pfalc = (falc_t *)&chan->falc;
+ uclong falcbase = card->hw.falcbase;
+
+ if (((cpc_readb(falcbase + F_REG(LCR1, ch)) & LCR1_XPRBS) == 0) &&
+ !pfalc->loop_gen) {
+ if (rsp & RSP_LLBDD) {
+ // A Line Loop Back Deactuation signal detected
+ if (pfalc->loop_active) {
+ falc_remote_loop(card, ch, 0);
+ }
+ } else {
+ if ((rsp & RSP_LLBAD) &&
+ ((cpc_readb(falcbase + F_REG(LCR1, ch)) & LCR1_EPRM) == 0)) {
+ // A Line Loop Back Actuation signal detected
+ if (!pfalc->loop_active) {
+ falc_remote_loop(card, ch, 1);
+ }
+ }
+ }
+ }
+}
+
+static void
+falc_t1_intr(pc300_t *card, int ch)
+{
+ pc300ch_t *chan = (pc300ch_t *)&card->chan[ch];
+ falc_t *pfalc = (falc_t *)&chan->falc;
+ uclong falcbase = card->hw.falcbase;
+ ucchar isr0, isr3, gis;
+ ucchar dummy;
+
+ while ((gis = cpc_readb(falcbase + F_REG(GIS, ch))) != 0) {
+ if (gis & GIS_ISR0) {
+ isr0 = cpc_readb(falcbase + F_REG(FISR0, ch));
+ if (isr0 & FISR0_PDEN) {
+ /* Read the bit to clear the situation */
+ if (cpc_readb(falcbase + F_REG(FRS1, ch)) & FRS1_PDEN) {
+ pfalc->pden++;
+ }
+ }
+ }
+
+ if (gis & GIS_ISR1) {
+ dummy = cpc_readb(falcbase + F_REG(FISR1, ch));
+ }
+
+ if (gis & GIS_ISR2) {
+ dummy = cpc_readb(falcbase + F_REG(FISR2, ch));
+ }
+
+ if (gis & GIS_ISR3) {
+ isr3 = cpc_readb(falcbase + F_REG(FISR3, ch));
+ if (isr3 & FISR3_SEC) {
+ pfalc->sec++;
+ falc_update_stats(card, ch);
+ falc_check_status(card, ch,
+ cpc_readb(falcbase + F_REG(FRS0, ch)));
+ }
+ if (isr3 & FISR3_ES) {
+ pfalc->es++;
+ }
+ if (isr3 & FISR3_LLBSC) {
+ falc_t1_loop_detection(card, ch,
+ cpc_readb(falcbase + F_REG(FRS1, ch)));
+ }
+ }
+ }
+}
+
+static void
+falc_e1_intr(pc300_t *card, int ch)
+{
+ pc300ch_t *chan = (pc300ch_t *)&card->chan[ch];
+ falc_t *pfalc = (falc_t *)&chan->falc;
+ uclong falcbase = card->hw.falcbase;
+ ucchar isr1, isr2, isr3, gis, rsp;
+ ucchar dummy;
+
+ while ((gis = cpc_readb(falcbase + F_REG(GIS, ch))) != 0) {
+ rsp = cpc_readb(falcbase + F_REG(RSP, ch));
+
+ if (gis & GIS_ISR0) {
+ dummy = cpc_readb(falcbase + F_REG(FISR0, ch));
+ }
+ if (gis & GIS_ISR1) {
+ isr1 = cpc_readb(falcbase + F_REG(FISR1, ch));
+ if (isr1 & FISR1_XMB) {
+ if ((pfalc->xmb_cause & 2) && pfalc->multiframe_mode) {
+ if (cpc_readb(falcbase + F_REG(FRS0, ch)) &
+ (FRS0_LOS | FRS0_AIS | FRS0_LFA)) {
+ cpc_writeb(falcbase + F_REG(XSP, ch),
+ cpc_readb(falcbase + F_REG(XSP, ch)) &
+ ~XSP_AXS);
+ } else {
+ cpc_writeb(falcbase + F_REG(XSP, ch),
+ cpc_readb(falcbase + F_REG(XSP, ch)) |
+ XSP_AXS);
+ }
+ }
+ pfalc->xmb_cause = 0;
+ cpc_writeb(falcbase + F_REG(IMR1, ch),
+ cpc_readb(falcbase + F_REG(IMR1, ch)) | IMR1_XMB);
+ }
+ if (isr1 & FISR1_LLBSC) {
+ falc_e1_loop_detection(card, ch, rsp);
+ }
+ }
+ if (gis & GIS_ISR2) {
+ isr2 = cpc_readb(falcbase + F_REG(FISR2, ch));
+ if (isr2 & FISR2_T400MS) {
+ cpc_writeb(falcbase + F_REG(XSW, ch),
+ cpc_readb(falcbase + F_REG(XSW, ch)) | XSW_XRA);
+ }
+ if (isr2 & FISR2_MFAR) {
+ cpc_writeb(falcbase + F_REG(XSW, ch),
+ cpc_readb(falcbase + F_REG(XSW, ch)) & ~XSW_XRA);
+ }
+ if (isr2 & (FISR2_FAR | FISR2_LFA | FISR2_AIS | FISR2_LOS)) {
+ pfalc->xmb_cause |= 2;
+ cpc_writeb(falcbase + F_REG(IMR1, ch),
+ cpc_readb(falcbase + F_REG(IMR1, ch)) & ~IMR1_XMB);
+ }
+ }
+ if (gis & GIS_ISR3) {
+ isr3 = cpc_readb(falcbase + F_REG(FISR3, ch));
+ if (isr3 & FISR3_SEC) {
+ pfalc->sec++;
+ falc_update_stats(card, ch);
+ falc_check_status(card, ch,
+ cpc_readb(falcbase + F_REG(FRS0, ch)));
+ }
+ if (isr3 & FISR3_ES) {
+ pfalc->es++;
+ }
+ }
+ }
+}
+
+static void
+falc_intr(pc300_t *card)
+{
+ int ch;
+
+ for (ch = 0 ; ch < card->hw.nchan ; ch++) {
+ pc300ch_t *chan = &card->chan[ch];
+ pc300chconf_t *conf = (pc300chconf_t *)&chan->conf;
+
+ if (conf->media == LINE_T1) {
+ falc_t1_intr(card, ch);
+ } else {
+ falc_e1_intr(card, ch);
+ }
+ }
+}
+
+static void
+cpc_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ pc300_t *card;
+ volatile ucchar plx_status;
+
+ if((card = (pc300_t *)dev_id) == 0){
+#ifdef PC300_DEBUG_INTR
+ printk("cpc_intr: spurious intr %d\n", irq);
+#endif
+ return; /* spurious intr */
+ }
+
+ switch (card->hw.type) {
+ case PC300_RSV:
+ case PC300_X21:
+ sca_intr(card);
+ break;
+
+ case PC300_TE:
+ while ((plx_status = (cpc_readb(card->hw.plxbase + 0x4c) &
+ (PLX_9050_LINT1_STATUS|PLX_9050_LINT2_STATUS))) != 0) {
+ if (plx_status & PLX_9050_LINT1_STATUS) { /* SCA Interrupt */
+ sca_intr(card);
+ }
+ if (plx_status & PLX_9050_LINT2_STATUS) { /* FALC Interrupt */
+ falc_intr(card);
+ }
+ }
+ break;
+ }
+}
+
+void
+cpc_sca_status(pc300_t *card, int ch)
+{
+ ucchar ilar;
+ uclong scabase = card->hw.scabase;
+ uclong flags;
+
+ tx_dma_buf_check(card, ch);
+ rx_dma_buf_check(card, ch);
+ ilar = cpc_readb(scabase + ILAR);
+ printk("ILAR=0x%02x, WCRL=0x%02x, PCR=0x%02x, BTCR=0x%02x, BOLR=0x%02x\n",
+ ilar, cpc_readb(scabase + WCRL),
+ cpc_readb(scabase + PCR),
+ cpc_readb(scabase + BTCR),
+ cpc_readb(scabase + BOLR));
+ printk("TX_CDA=0x%08lx, TX_EDA=0x%08lx\n",
+ (uclong)cpc_readl(scabase + DTX_REG(CDAL, ch)),
+ (uclong)cpc_readl(scabase + DTX_REG(EDAL, ch)));
+ printk("RX_CDA=0x%08lx, RX_EDA=0x%08lx, BFL=0x%04x\n",
+ (uclong)cpc_readl(scabase + DRX_REG(CDAL, ch)),
+ (uclong)cpc_readl(scabase + DRX_REG(EDAL, ch)),
+ cpc_readw(scabase + DRX_REG(BFLL, ch)));
+ printk("DMER=0x%02x, DSR_TX=0x%02x, DSR_RX=0x%02x\n",
+ cpc_readb(scabase + DMER),
+ cpc_readb(scabase + DSR_TX(ch)),
+ cpc_readb(scabase + DSR_RX(ch)));
+ printk("DMR_TX=0x%02x, DMR_RX=0x%02x, DIR_TX=0x%02x, DIR_RX=0x%02x\n",
+ cpc_readb(scabase + DMR_TX(ch)),
+ cpc_readb(scabase + DMR_RX(ch)),
+ cpc_readb(scabase + DIR_TX(ch)),
+ cpc_readb(scabase + DIR_RX(ch)));
+ printk("DCR_TX=0x%02x, DCR_RX=0x%02x, FCT_TX=0x%02x, FCT_RX=0x%02x\n",
+ cpc_readb(scabase + DCR_TX(ch)),
+ cpc_readb(scabase + DCR_RX(ch)),
+ cpc_readb(scabase + FCT_TX(ch)),
+ cpc_readb(scabase + FCT_RX(ch)));
+ printk("MD0=0x%02x, MD1=0x%02x, MD2=0x%02x, MD3=0x%02x, IDL=0x%02x\n",
+ cpc_readb(scabase + M_REG(MD0, ch)),
+ cpc_readb(scabase + M_REG(MD1, ch)),
+ cpc_readb(scabase + M_REG(MD2, ch)),
+ cpc_readb(scabase + M_REG(MD3, ch)),
+ cpc_readb(scabase + M_REG(IDL, ch)));
+ printk("CMD=0x%02x, SA0=0x%02x, SA1=0x%02x, TFN=0x%02x, CTL=0x%02x\n",
+ cpc_readb(scabase + M_REG(CMD, ch)),
+ cpc_readb(scabase + M_REG(SA0, ch)),
+ cpc_readb(scabase + M_REG(SA1, ch)),
+ cpc_readb(scabase + M_REG(TFN, ch)),
+ cpc_readb(scabase + M_REG(CTL, ch)));
+ printk("ST0=0x%02x, ST1=0x%02x, ST2=0x%02x, ST3=0x%02x, ST4=0x%02x\n",
+ cpc_readb(scabase + M_REG(ST0, ch)),
+ cpc_readb(scabase + M_REG(ST1, ch)),
+ cpc_readb(scabase + M_REG(ST2, ch)),
+ cpc_readb(scabase + M_REG(ST3, ch)),
+ cpc_readb(scabase + M_REG(ST4, ch)));
+ printk("CST0=0x%02x, CST1=0x%02x, CST2=0x%02x, CST3=0x%02x, FST=0x%02x\n",
+ cpc_readb(scabase + M_REG(CST0, ch)),
+ cpc_readb(scabase + M_REG(CST1, ch)),
+ cpc_readb(scabase + M_REG(CST2, ch)),
+ cpc_readb(scabase + M_REG(CST3, ch)),
+ cpc_readb(scabase + M_REG(FST, ch)));
+ printk("TRC0=0x%02x, TRC1=0x%02x, RRC=0x%02x, TBN=0x%02x, RBN=0x%02x\n",
+ cpc_readb(scabase + M_REG(TRC0, ch)),
+ cpc_readb(scabase + M_REG(TRC1, ch)),
+ cpc_readb(scabase + M_REG(RRC, ch)),
+ cpc_readb(scabase + M_REG(TBN, ch)),
+ cpc_readb(scabase + M_REG(RBN, ch)));
+ printk("TFS=0x%02x, TNR0=0x%02x, TNR1=0x%02x, RNR=0x%02x\n",
+ cpc_readb(scabase + M_REG(TFS, ch)),
+ cpc_readb(scabase + M_REG(TNR0, ch)),
+ cpc_readb(scabase + M_REG(TNR1, ch)),
+ cpc_readb(scabase + M_REG(RNR, ch)));
+ printk("TCR=0x%02x, RCR=0x%02x, TNR1=0x%02x, RNR=0x%02x\n",
+ cpc_readb(scabase + M_REG(TCR, ch)),
+ cpc_readb(scabase + M_REG(RCR, ch)),
+ cpc_readb(scabase + M_REG(TNR1, ch)),
+ cpc_readb(scabase + M_REG(RNR, ch)));
+ printk("TXS=0x%02x, RXS=0x%02x, EXS=0x%02x, TMCT=0x%02x, TMCR=0x%02x\n",
+ cpc_readb(scabase + M_REG(TXS, ch)),
+ cpc_readb(scabase + M_REG(RXS, ch)),
+ cpc_readb(scabase + M_REG(EXS, ch)),
+ cpc_readb(scabase + M_REG(TMCT, ch)),
+ cpc_readb(scabase + M_REG(TMCR, ch)));
+ printk("IE0=0x%02x, IE1=0x%02x, IE2=0x%02x, IE4=0x%02x, FIE=0x%02x\n",
+ cpc_readb(scabase + M_REG(IE0, ch)),
+ cpc_readb(scabase + M_REG(IE1, ch)),
+ cpc_readb(scabase + M_REG(IE2, ch)),
+ cpc_readb(scabase + M_REG(IE4, ch)),
+ cpc_readb(scabase + M_REG(FIE, ch)));
+ printk("IER0=0x%08lx\n", (uclong)cpc_readl(scabase + IER0));
+
+ if (ilar != 0) {
+ CPC_LOCK(card, flags);
+ cpc_writeb(scabase + ILAR, ilar);
+ cpc_writeb(scabase + DMER, 0x80);
+ CPC_UNLOCK(card, flags);
+ }
+}
+
+void
+cpc_falc_status(pc300_t *card, int ch)
+{
+ pc300ch_t *chan = &card->chan[ch];
+ falc_t *pfalc = (falc_t *)&chan->falc;
+ uclong flags;
+
+ CPC_LOCK(card, flags);
+ printk("CH%d: %s %s %d channels\n",
+ ch, (pfalc->sync ? "SYNC":""), (pfalc->active ? "ACTIVE":""),
+ pfalc->num_channels);
+
+ printk(" pden=%d, los=%d, losr=%d, lfa=%d, farec=%d\n",
+ pfalc->pden, pfalc->los, pfalc->losr, pfalc->lfa, pfalc->farec);
+ printk(" lmfa=%d, ais=%d, sec=%d, es=%d, rai=%d\n",
+ pfalc->lmfa, pfalc->ais, pfalc->sec, pfalc->es, pfalc->rai);
+ printk(" bec=%d, fec=%d, cvc=%d, cec=%d, ebc=%d\n",
+ pfalc->bec, pfalc->fec, pfalc->cvc, pfalc->cec, pfalc->ebc);
+
+ printk("\n");
+ printk(" STATUS: %s %s %s %s %s %s\n",
+ (pfalc->red_alarm ? "RED":""),
+ (pfalc->blue_alarm ? "BLU":""),
+ (pfalc->yellow_alarm ? "YEL":""),
+ (pfalc->loss_fa ? "LFA":""),
+ (pfalc->loss_mfa ? "LMF":""),
+ (pfalc->prbs ? "PRB":""));
+ CPC_UNLOCK(card, flags);
+}
+
+int
+cpc_ioctl(hdlc_device *hdlc, struct ifreq *ifr, int cmd)
+{
+ struct device *dev = hdlc_to_dev(hdlc);
+ pc300dev_t *d = (pc300dev_t *)dev->priv;
+ pc300ch_t *chan = (pc300ch_t *)d->chan;
+ pc300_t *card = (pc300_t *)chan->card;
+ pc300chconf_t *conf = (pc300chconf_t *)&chan->conf;
+ int ch = chan->channel;
+ int value;
+ void *arg = (void *) ifr->ifr_data;
+
+ if(!capable(CAP_NET_ADMIN))
+ return -EPERM;
+
+ switch(cmd) {
+ case SIOCGPC300CONF:
+ conf->proto = hdlc->mode;
+ if (!arg || copy_to_user(arg, conf, sizeof(pc300chconf_t)))
+ return -EINVAL;
+ return 0;
+ case SIOCSPC300CONF:
+ if (!suser())
+ return -EPERM;
+ if (!arg || copy_from_user(conf, arg, sizeof(pc300chconf_t)))
+ return -EINVAL;
+ return 0;
+ case SIOCGPC300STATUS:
+ cpc_sca_status(card, ch);
+ return 0;
+ case SIOCGPC300FALCSTATUS:
+ cpc_falc_status(card, ch);
+ return 0;
+ case HDLCSETLINE:
+ value = ifr->ifr_ifru.ifru_ivalue;
+ switch (value) {
+ case LINE_LOOPBACK:
+ cpc_writeb(card->hw.scabase + M_REG(MD2, ch),
+ cpc_readb(card->hw.scabase + M_REG(MD2, ch)) |
+ MD2_LOOP_MIR);
+ conf->loopback = 1;
+ return 0;
+
+ case LINE_NOLOOPBACK:
+ cpc_writeb(card->hw.scabase + M_REG(MD2, ch),
+ cpc_readb(card->hw.scabase + M_REG(MD2, ch)) &
+ ~MD2_LOOP_MIR);
+ conf->loopback = 0;
+ return 0;
+
+ case LINE_V35:
+ case LINE_X21:
+ case LINE_RS232:
+ case LINE_T1:
+ case LINE_E1:
+ /* Media */
+ conf->media = value;
+ return 0;
+
+ default:
+ /* Clock rate */
+ conf->clkrate = value;
+ return 0;
+ }
+
+ default:
+ switch(hdlc->mode & ~MODE_SOFT) {
+#ifdef CONFIG_PC300_X25
+ case MODE_X25:
+ /* There are no X.25-specific ioctls */
+ return -EINVAL;
+#endif /* CONFIG_PC300_X25 */
+ default:
+ return -EINVAL;
+ }
+ }
+}
+
+static int
+clock_rate_calc(uclong rate, uclong clock, int *br_io)
+{
+ int br, tc;
+ int br_pwr, error;
+
+ if (rate == 0)
+ return (0);
+
+ for (br = 0, br_pwr = 1 ; br <= 9 ; br++, br_pwr <<= 1) {
+ if ((tc = clock / br_pwr / rate) <= 0xff) {
+ *br_io = br;
+ break;
+ }
+ }
+
+ if (tc <= 0xff) {
+ error = ((rate - (clock / br_pwr / rate)) / rate) * 1000;
+ /* Errors bigger than +/- 1% won't be tolerated */
+ if (error < -10 || error > 10)
+ return (-1);
+ else
+ return (tc);
+ } else {
+ return (-1);
+ }
+}
+
+int
+ch_config(pc300dev_t *d)
+{
+ pc300ch_t *chan = (pc300ch_t *)d->chan;
+ pc300chconf_t *conf = (pc300chconf_t *)&chan->conf;
+ pc300_t *card = (pc300_t *)chan->card;
+ uclong scabase = card->hw.scabase;
+ uclong plxbase = card->hw.plxbase;
+ int ch = chan->channel;
+ uclong clkrate = chan->conf.clkrate;
+ ucchar loopback = (conf->loopback ? MD2_LOOP_MIR : MD2_F_DUPLEX);
+ int tmc, br;
+
+ /* Reset the channel */
+ cpc_writeb(scabase + M_REG(CMD, ch), CMD_CH_RST);
+
+ /* Configure the SCA registers */
+ cpc_writeb(scabase + M_REG(MD0, ch),
+ (MD0_CRC_CCITT|MD0_CRCC0|MD0_BIT_SYNC));
+ cpc_writeb(scabase + M_REG(MD1, ch), 0);
+ cpc_writeb(scabase + M_REG(MD2, ch), (loopback|MD2_ADPLL_X8|MD2_NRZ));
+ cpc_writeb(scabase + M_REG(IDL, ch), 0x7e);
+ cpc_writeb(scabase + M_REG(CTL, ch), CTL_URSKP|CTL_IDLC);
+
+ /* Configure HW media */
+ switch(card->hw.type) {
+ case PC300_RSV:
+ if(conf->media == LINE_V35) {
+ cpc_writel((plxbase+0x50),
+ cpc_readl(plxbase+0x50) | PC300_CHMEDIA_MASK(ch));
+ } else {
+ cpc_writel((plxbase+0x50),
+ cpc_readl(plxbase+0x50) & ~PC300_CHMEDIA_MASK(ch));
+ }
+ break;
+
+ case PC300_X21:
+ break;
+
+ case PC300_TE:
+ te_config(card, ch);
+ break;
+ }
+
+ switch(card->hw.type) {
+ case PC300_RSV:
+ case PC300_X21:
+ if (clkrate) {
+ /* Calculate the clkrate rate parameters */
+ tmc = clock_rate_calc(clkrate, card->hw.clock, &br);
+ cpc_writeb(scabase + M_REG(TMCT, ch), tmc);
+ cpc_writeb(scabase + M_REG(TXS, ch), (TXS_DTRXC|TXS_IBRG|br));
+ cpc_writeb(scabase + M_REG(TMCR, ch), tmc);
+ cpc_writeb(scabase + M_REG(RXS, ch), (RXS_IBRG|br));
+ if (card->hw.type == PC300_X21) {
+ cpc_writeb(scabase + M_REG(GPO, ch), 1);
+ cpc_writeb(scabase + M_REG(EXS, ch), EXS_TES1|EXS_RES1);
+ } else {
+ cpc_writeb(scabase + M_REG(EXS, ch), EXS_TES1);
+ }
+ } else {
+ cpc_writeb(scabase + M_REG(TMCT, ch), 1);
+ cpc_writeb(scabase + M_REG(TXS, ch), TXS_DTRXC);
+ cpc_writeb(scabase + M_REG(TMCR, ch), 1);
+ cpc_writeb(scabase + M_REG(RXS, ch), 0);
+ if (card->hw.type == PC300_X21) {
+ cpc_writeb(scabase + M_REG(GPO, ch), 0);
+ cpc_writeb(scabase + M_REG(EXS, ch), EXS_TES1|EXS_RES1);
+ } else {
+ cpc_writeb(scabase + M_REG(EXS, ch), EXS_TES1);
+ }
+ }
+ break;
+
+ case PC300_TE:
+ /* SCA always receives clock from the FALC chip */
+ cpc_writeb(scabase + M_REG(TMCT, ch), 1);
+ cpc_writeb(scabase + M_REG(TXS, ch), 0);
+ cpc_writeb(scabase + M_REG(TMCR, ch), 1);
+ cpc_writeb(scabase + M_REG(RXS, ch), 0);
+ cpc_writeb(scabase + M_REG(EXS, ch), 0);
+ break;
+ }
+
+ /* Enable Interrupts */
+ cpc_writel(scabase + IER0,
+ cpc_readl(scabase + IER0) |
+ IR0_DRX(IR0_EFT|IR0_DMIA|IR0_DMIB, ch) |
+ IR0_DTX(IR0_EFT|IR0_DMIA|IR0_DMIB, ch) );
+
+ return 0;
+}
+
+int
+rx_config(pc300dev_t *d)
+{
+ pc300ch_t *chan = (pc300ch_t *)d->chan;
+ pc300_t *card = (pc300_t *)chan->card;
+ uclong scabase = card->hw.scabase;
+ int ch = chan->channel;
+
+ cpc_writeb(scabase + DSR_RX(ch), 0);
+
+ /* General RX settings */
+ cpc_writeb(scabase + M_REG(RRC, ch), 0);
+ cpc_writeb(scabase + M_REG(RNR, ch), 16);
+
+ /* Enable reception */
+ cpc_writeb(scabase + M_REG(CMD, ch), CMD_RX_CRC_INIT);
+ cpc_writeb(scabase + M_REG(CMD, ch), CMD_RX_ENA);
+
+ /* Initialize DMA stuff */
+ chan->rx_first_bd = 0;
+ chan->rx_last_bd = N_DMA_RX_BUF - 1;
+ rx_dma_buf_init(card, ch);
+ cpc_writeb(scabase + DCR_RX(ch), DCR_FCT_CLR);
+ cpc_writeb(scabase + DMR_RX(ch), (DMR_TMOD | DMR_NF));
+ cpc_writeb(scabase + DIR_RX(ch), (DIR_EOM | DIR_BOF));
+
+ /* Start DMA */
+ cpc_writel(scabase + DRX_REG(CDAL, ch), RX_BD_ADDR(ch, chan->rx_first_bd));
+ cpc_writel(scabase + DRX_REG(EDAL, ch), RX_BD_ADDR(ch, chan->rx_last_bd));
+ cpc_writew(scabase + DRX_REG(BFLL, ch), BD_DEF_LEN);
+ cpc_writeb(scabase + DSR_RX(ch), DSR_DE);
+
+ return 0;
+}
+
+int
+tx_config(pc300dev_t *d)
+{
+ pc300ch_t *chan = (pc300ch_t *)d->chan;
+ pc300_t *card = (pc300_t *)chan->card;
+ uclong scabase = card->hw.scabase;
+ int ch = chan->channel;
+
+ cpc_writeb(scabase + DSR_TX(ch), 0);
+
+ /* General TX settings */
+ cpc_writeb(scabase + M_REG(TRC0, ch), 0);
+ cpc_writeb(scabase + M_REG(TFS, ch), 32);
+ cpc_writeb(scabase + M_REG(TNR0, ch), 20);
+ cpc_writeb(scabase + M_REG(TNR1, ch), 48);
+ cpc_writeb(scabase + M_REG(TCR, ch), 8);
+
+ /* Enable transmission */
+ cpc_writeb(scabase + M_REG(CMD, ch), CMD_TX_CRC_INIT);
+
+ /* Initialize DMA stuff */
+ chan->tx_first_bd = 0;
+ chan->tx_next_bd = 0;
+ tx_dma_buf_init(card, ch);
+ cpc_writeb(scabase + DCR_TX(ch), DCR_FCT_CLR);
+ cpc_writeb(scabase + DMR_TX(ch), (DMR_TMOD | DMR_NF));
+ cpc_writeb(scabase + DIR_TX(ch), (DIR_EOM | DIR_BOF | DIR_UDRF));
+ cpc_writel(scabase + DTX_REG(CDAL, ch), TX_BD_ADDR(ch, chan->tx_first_bd));
+ cpc_writel(scabase + DTX_REG(EDAL, ch), TX_BD_ADDR(ch, chan->tx_next_bd));
+
+ return 0;
+}
+
+int
+cpc_opench(pc300dev_t *d)
+{
+ pc300ch_t *chan = (pc300ch_t *)d->chan;
+ pc300_t *card = (pc300_t *)chan->card;
+ int ch = chan->channel;
+ uclong scabase = card->hw.scabase;
+ int err = -1;
+
+ err = ch_config(d);
+ if (err)
+ return err;
+
+ err = rx_config(d);
+ if (err)
+ return err;
+
+ err = tx_config(d);
+ if (err)
+ return err;
+
+ /* Assert RTS and DTR */
+ cpc_writeb(scabase + M_REG(CTL, ch),
+ cpc_readb(scabase + M_REG(CTL, ch)) & ~(CTL_RTS | CTL_DTR));
+
+ return 0;
+}
+
+void
+cpc_closech(pc300dev_t *d)
+{
+ pc300ch_t *chan = (pc300ch_t *)d->chan;
+ pc300_t *card = (pc300_t *)chan->card;
+ falc_t *pfalc = (falc_t *)&chan->falc;
+ int ch = chan->channel;
+
+ cpc_writeb(card->hw.scabase + M_REG(CMD, ch), CMD_CH_RST);
+ if (card->hw.type == PC300_TE) {
+ memset(pfalc, 0, sizeof(falc_t));
+ cpc_writeb(card->hw.falcbase + CPLD_REG2,
+ cpc_readb(card->hw.falcbase + CPLD_REG2) &
+ ~((CPLD_REG2_FALC_TX_CLK | CPLD_REG2_FALC_RX_CLK |
+ CPLD_REG2_FALC_LED2) << (2*ch)));
+ /* Reset the FALC chip */
+ cpc_writeb(card->hw.falcbase + CPLD_REG1,
+ cpc_readb(card->hw.falcbase + CPLD_REG1) |
+ (CPLD_REG1_FALC_RESET << (2*ch)));
+ udelay(10000);
+ cpc_writeb(card->hw.falcbase + CPLD_REG1,
+ cpc_readb(card->hw.falcbase + CPLD_REG1) &
+ ~(CPLD_REG1_FALC_RESET << (2*ch)));
+ }
+}
+
+int
+cpc_open(hdlc_device *hdlc)
+{
+ struct device *dev = hdlc_to_dev(hdlc);
+ pc300dev_t *d = (pc300dev_t *)dev->priv;
+ int err = -1;
+
+ err = cpc_opench(d);
+ if (err)
+ return err;
+
+ switch(hdlc->mode & ~MODE_SOFT) {
+#ifdef CONFIG_PC300_X25
+ case MODE_X25:
+ {
+ struct lapb_register_struct cpc_lapb_callbacks;
+
+ hdlc->ioctl = cpc_ioctl;
+ cpc_lapb_callbacks.connect_confirmation = cpc_lapb_connected;
+ cpc_lapb_callbacks.connect_indication = cpc_lapb_connected;
+ cpc_lapb_callbacks.disconnect_confirmation = cpc_lapb_disconnected;
+ cpc_lapb_callbacks.disconnect_indication = cpc_lapb_disconnected;
+ cpc_lapb_callbacks.data_indication = cpc_lapb_data_indication;
+ cpc_lapb_callbacks.data_transmit = cpc_lapb_data_transmit;
+
+ if ((err = lapb_register(d, &cpc_lapb_callbacks)) != LAPB_OK) {
+ printk("%s: lapb_register error - %d\n", dev->name, err);
+ dev->tbusy = 1;
+ dev->start = 0;
+ return -ENODEV;
+ }
+ dev->hard_start_xmit = cpc_x25_packetlayer_xmit;
+ dev->type = ARPHRD_X25;
+ dev->hard_header_len = 2;
+ dev->addr_len = 0;
+ break;
+ }
+#endif /* CONFIG_PC300_X25 */
+ default:
+ break;
+ }
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+void
+cpc_close(hdlc_device *hdlc)
+{
+ struct device *dev = hdlc_to_dev(hdlc);
+ pc300dev_t *d = (pc300dev_t *)dev->priv;
+ pc300ch_t *chan = (pc300ch_t *)d->chan;
+ pc300_t *card = (pc300_t *)chan->card;
+ uclong flags;
+
+ CPC_LOCK(card, flags);
+ switch(hdlc->mode & ~MODE_SOFT) {
+#ifdef CONFIG_PC300_X25
+ case MODE_X25:
+ {
+ int err;
+
+ if ((err = lapb_unregister(d)) != LAPB_OK) {
+ printk("%s: lapb_unregister error - %d\n", dev->name, err);
+ }
+ break;
+ }
+#endif /* CONFIG_PC300_X25 */
+ default:
+ break;
+ }
+ if (d->tx_skb) {
+ dev_kfree_skb(d->tx_skb);
+ d->tx_skb = NULL;
+ }
+ dev->tbusy = 1;
+ dev->start = 0;
+ cpc_closech(d);
+ CPC_UNLOCK(card, flags);
+
+ MOD_DEC_USE_COUNT;
+}
+
+static uclong
+detect_ram(pc300_t *card)
+{
+ uclong i;
+ ucchar data;
+ uclong rambase = card->hw.rambase;
+
+ card->hw.ramsize = PC300_RAMSIZE;
+ /* Let's find out how much RAM is present on this board */
+ for (i = 0; i < card->hw.ramsize ; i++) {
+ data = (ucchar)(i & 0xff);
+ cpc_writeb(rambase + i, data);
+ if (cpc_readb(rambase + i) != data) {
+ break;
+ }
+ }
+ return (i);
+}
+
+static void
+plx_init(pc300_t *card)
+{
+ struct RUNTIME_9050 *plx_ctl = (struct RUNTIME_9050 *)card->hw.plxbase;
+
+ /* Reset PLX */
+ cpc_writel(&plx_ctl->init_ctrl, cpc_readl(&plx_ctl->init_ctrl)|0x40000000);
+ udelay(10000L);
+ cpc_writel(&plx_ctl->init_ctrl, cpc_readl(&plx_ctl->init_ctrl)&~0x40000000);
+
+ /* Reload Config. Registers from EEPROM */
+ cpc_writel(&plx_ctl->init_ctrl, cpc_readl(&plx_ctl->init_ctrl)|0x20000000);
+ udelay(10000L);
+ cpc_writel(&plx_ctl->init_ctrl, cpc_readl(&plx_ctl->init_ctrl)&~0x20000000);
+
+}
+
+__initfunc(static int
+cpc_detect(void))
+{
+#ifdef CONFIG_PCI
+ static struct pci_dev *pdev = NULL;
+ ucchar cpc_rev_id;
+ ucchar cpc_irq = 0;
+ uclong cpc_plxphys, cpc_ramphys, cpc_scaphys, cpc_falcphys, cpc_iophys;
+ uclong cpc_plxbase, cpc_rambase, cpc_scabase, cpc_falcbase;
+ ucshort i, j, eeprom_outdated = 0;
+ ucshort device_id, dev_index = 0;
+ pc300_t *card;
+
+ if(pci_present() == 0) { /* PCI bus not present */
+ return 0;
+ }
+ for (i = 0 ; i < PC300_MAXCARDS ; i++) {
+ /* look for a Cyclades card by vendor and device id */
+ while((device_id = cpc_pci_dev_id[dev_index]) != 0) {
+ if((pdev = pci_find_device(PCI_VENDOR_ID_CYCLADES,
+ device_id, pdev)) == NULL) {
+ dev_index++; /* try next device id */
+ } else {
+ break; /* found a board */
+ }
+ }
+ if (device_id == 0)
+ break;
+
+ /* read PCI configuration area */
+ cpc_irq = pdev->irq;
+ cpc_iophys = pdev->base_address[1];
+ cpc_scaphys = pdev->base_address[2];
+ cpc_ramphys = pdev->base_address[3];
+ cpc_falcphys = pdev->base_address[4];
+ cpc_plxphys = pdev->base_address[5];
+ pci_read_config_byte(pdev, PCI_REVISION_ID, &cpc_rev_id);
+
+ if ((device_id == PCI_DEVICE_ID_PC300_RX_1) ||
+ (device_id == PCI_DEVICE_ID_PC300_RX_2) ||
+ (device_id == PCI_DEVICE_ID_PC300_TE_1) ||
+ (device_id == PCI_DEVICE_ID_PC300_TE_2)) {
+#ifdef PC300_DEBUG_PCI
+ printk("cpc (bus=0x0%x, pci_id=0x%x, ",
+ pdev->bus->number, pdev->devfn);
+ printk("rev_id=%d) IRQ%d\n", cpc_rev_id, (int)cpc_irq);
+ printk("cpc:found ramaddr=0x%lx plxaddr=0x%lx "
+ "ctladdr=0x%lx falcaddr=0x%lx\n",
+ (ulong)cpc_ramphys, (ulong)cpc_plxphys,
+ (ulong)cpc_scaphys, (ulong)cpc_falcphys);
+#endif
+ cpc_iophys &= PCI_BASE_ADDRESS_IO_MASK;
+ cpc_plxphys &= PCI_BASE_ADDRESS_MEM_MASK;
+ cpc_ramphys &= PCI_BASE_ADDRESS_MEM_MASK;
+ cpc_scaphys &= PCI_BASE_ADDRESS_MEM_MASK;
+ cpc_falcphys &= PCI_BASE_ADDRESS_MEM_MASK;
+
+ /* Although we don't use this I/O region, we should
+ request it from the kernel anyway, to avoid problems
+ with other drivers accessing it. */
+ request_region(cpc_iophys, PC300_PLX_WIN, "Cyclades-PC300");
+
+ if (cpc_plxphys) {
+ pdev->base_address[0] = cpc_plxphys;
+ pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, cpc_plxphys);
+ } else {
+ eeprom_outdated = 1;
+ cpc_plxphys = pdev->base_address[0];
+ cpc_plxphys &= PCI_BASE_ADDRESS_MEM_MASK;
+ }
+
+ cpc_plxbase = (uclong) ioremap(cpc_plxphys, PC300_PLX_WIN);
+ cpc_rambase = (uclong) ioremap(cpc_ramphys, PC300_RAMSIZE);
+ cpc_scabase = (uclong) ioremap(cpc_scaphys, PC300_SCASIZE);
+ switch(device_id) {
+ case PCI_DEVICE_ID_PC300_TE_1:
+ case PCI_DEVICE_ID_PC300_TE_2:
+ cpc_falcbase = (uclong) ioremap(cpc_falcphys,
+ PC300_FALCSIZE);
+ break;
+
+ case PCI_DEVICE_ID_PC300_RX_1:
+ case PCI_DEVICE_ID_PC300_RX_2:
+ default:
+ cpc_falcbase = 0;
+ break;
+ }
+
+#ifdef PC300_DEBUG_PCI
+ printk("cpc: relocate ramaddr=0x%lx plxaddr=0x%lx "
+ "ctladdr=0x%lx falcaddr=0x%lx\n",
+ (ulong)cpc_rambase, (ulong)cpc_plxbase,
+ (ulong)cpc_scabase, (ulong)cpc_falcbase);
+#endif
+ /* Fill the next card structure available */
+ for (j = 0 ; j < PC300_MAXCARDS ; j++) {
+ if (cpc_card[j].hw.rambase == 0) break;
+ }
+
+ if (j == PC300_MAXCARDS) { /* No more cards available */
+ printk("PC300 found at RAM 0x%lx, "
+ "but no more cards can be used.\n",
+ (ulong) cpc_ramphys);
+ printk("Change PC300_MAXCARDS in pc300drv.c and "
+ "recompile your kernel.\n");
+ return(i);
+ }
+
+ /* Allocate IRQ */
+ if(request_irq(cpc_irq, cpc_intr, SA_SHIRQ, "Cyclades-PC300",
+ &cpc_card[j]))
+ {
+ printk("PC300 found at RAM 0x%lx, "
+ "but could not allocate IRQ%d.\n",
+ (ulong) cpc_ramphys, cpc_irq);
+ return(i);
+ }
+
+ card = &cpc_card[j];
+
+ /* Set PC300 HW structure */
+ card->hw.plxphys = cpc_plxphys;
+ card->hw.plxbase = cpc_plxbase;
+ card->hw.plxsize = (uclong)PC300_PLX_WIN;
+ card->hw.scaphys = cpc_scaphys;
+ card->hw.scabase = cpc_scabase;
+ card->hw.scasize = (uclong)PC300_SCASIZE;
+ card->hw.ramphys = cpc_ramphys;
+ card->hw.rambase = cpc_rambase;
+ card->hw.ramsize = detect_ram(card);
+ card->hw.falcphys = cpc_falcphys;
+ card->hw.falcbase = cpc_falcbase;
+ card->hw.falcsize = (uclong)PC300_FALCSIZE;
+ card->hw.irq = (int)cpc_irq;
+ switch(device_id) {
+ case PCI_DEVICE_ID_PC300_RX_1:
+ case PCI_DEVICE_ID_PC300_TE_1:
+ card->hw.nchan = 1;
+ break;
+
+ case PCI_DEVICE_ID_PC300_RX_2:
+ case PCI_DEVICE_ID_PC300_TE_2:
+ default:
+ card->hw.nchan = PC300_MAXCHAN;
+ break;
+ }
+
+ /* Enable interrupts on the PCI bridge */
+ plx_init(&cpc_card[j]);
+ cpc_writew(card->hw.plxbase+0x4c,
+ cpc_readw(card->hw.plxbase+0x4c) | 0x0040);
+
+#ifdef USE_PCI_CLOCK
+ /* Set board clock to PCI clock */
+ cpc_writel(card->hw.plxbase+0x50,
+ cpc_readl(card->hw.plxbase+0x50) | 0x00000004UL);
+ card->hw.clock = PC300_PCI_CLOCK;
+#else
+ /* Set board clock to internal oscillator clock */
+ cpc_writel(card->hw.plxbase+0x50,
+ cpc_readl(card->hw.plxbase+0x50) & ~0x00000004UL);
+ card->hw.clock = PC300_OSC_CLOCK;
+#endif
+
+ /* Set Global SCA-II registers */
+ cpc_writeb(card->hw.scabase + PCR, PCR_PR2);
+ cpc_writeb(card->hw.scabase + BTCR, 0x10);
+ cpc_writeb(card->hw.scabase + WCRL, 0);
+ cpc_writeb(card->hw.scabase + DMER, 0x80);
+
+ /* Set board type */
+ switch(device_id) {
+ case PCI_DEVICE_ID_PC300_TE_1:
+ case PCI_DEVICE_ID_PC300_TE_2:
+ card->hw.type = PC300_TE;
+ /* Enable the board's global clock */
+ cpc_writeb(card->hw.falcbase + CPLD_REG1,
+ cpc_readb(card->hw.falcbase + CPLD_REG1) |
+ CPLD_REG1_GLOBAL_CLK);
+ break;
+
+ case PCI_DEVICE_ID_PC300_RX_1:
+ case PCI_DEVICE_ID_PC300_RX_2:
+ default:
+ if((cpc_readl(card->hw.plxbase+0x50) & PC300_CTYPE_MASK)) {
+ card->hw.type = PC300_X21;
+ } else {
+ card->hw.type = PC300_RSV;
+ }
+ break;
+ }
+ }
+ }
+ if (eeprom_outdated)
+ printk("WARNING: detected at least one PC300 with an outdated "
+ "EEPROM.\n");
+ return i;
+#else
+ printk("cpc: WARNING: your kernel does not have PCI support.\n");
+ return 0;
+#endif /* CONFIG_PCI */
+}
+
+/*
+ * This routine prints out the appropriate serial driver version number
+ * and identifies which options were configured into this driver.
+ */
+static inline void
+show_version(void)
+{
+ char *rcsvers, *rcsdate, *tmp;
+ rcsvers = strchr(rcsid, ' '); rcsvers++;
+ tmp = strchr(rcsvers, ' '); *tmp++ = '\0';
+ rcsdate = strchr(tmp, ' '); rcsdate++;
+ tmp = strrchr(rcsdate, ' '); *tmp = '\0';
+ printk("Cyclades-PC300 driver %s %s\n", rcsvers, rcsdate);
+ printk(" built %s %s\n", __DATE__, __TIME__);
+} /* show_version */
+
+__initfunc(int
+cpc_init(void))
+{
+ int i, j, devcount = 0;
+
+ show_version();
+ memset(&cpc_card, 0, (PC300_MAXCARDS * sizeof(pc300_t)));
+ if((cpc_nboards = cpc_detect()) == 0) {
+ printk("** No boards were found.\n");
+ return -ENODEV;
+ }
+
+ /* Fill in valid structures and invalidate the remaining */
+ for (i = 0 ; i < PC300_MAXCARDS ; i++) {
+ pc300_t *card = &cpc_card[i];
+
+ if (card->hw.rambase != 0) {
+ for(j = 0; j < card->hw.nchan; j++) {
+ pc300ch_t *chan = &card->chan[j];
+ pc300dev_t *d = &chan->d;
+ hdlc_device *hdlc;
+ struct device *dev;
+
+ memset(chan, 0, sizeof(pc300ch_t));
+
+ chan->card = card;
+ chan->channel = j;
+ chan->conf.clkrate = 64000;
+ chan->conf.loopback = 0;
+ switch(card->hw.type) {
+ case PC300_TE:
+ chan->conf.media = LINE_T1;
+ chan->conf.lcode = PC300_LC_B8ZS;
+ chan->conf.fr_mode = PC300_FR_ESF;
+ chan->conf.lbo = PC300_LBO_0_DB;
+ chan->conf.rx_sens = PC300_RX_SENS_SH;
+ chan->conf.tslot_bitmap = 0xffffffffUL;
+ break;
+
+ case PC300_X21:
+ chan->conf.media = LINE_X21;
+ break;
+
+ case PC300_RSV:
+ default:
+ chan->conf.media = LINE_RS232;
+ break;
+ }
+ chan->tx_first_bd = 0;
+ chan->tx_next_bd = 0;
+ chan->rx_first_bd = 0;
+ chan->rx_last_bd = N_DMA_RX_BUF - 1;
+
+ d->chan = chan;
+ d->tx_skb = NULL;
+
+ d->hdlc = (hdlc_device *)
+ kmalloc(sizeof(hdlc_device), GFP_KERNEL);
+ if (d->hdlc == NULL)
+ continue;
+ memset(d->hdlc, 0, sizeof(hdlc_device));
+
+ hdlc = d->hdlc;
+ hdlc->open = cpc_open;
+ hdlc->close = cpc_close;
+ hdlc->ioctl = cpc_ioctl;
+ d->if_ptr = &hdlc->pppdev;
+
+ dev = hdlc_to_dev(d->hdlc);
+ dev->mem_start = card->hw.ramphys;
+ dev->mem_end = card->hw.ramphys + card->hw.ramsize - 1;
+ dev->irq = card->hw.irq;
+ dev->interrupt = 0;
+ dev->start = 0;
+ dev->tx_queue_len = PC300_TX_QUEUE_LEN;
+ dev->hard_start_xmit = cpc_queue_xmit;
+ dev->set_multicast_list = NULL;
+ dev->set_mac_address = NULL;
+
+ if(register_hdlc_device(hdlc) == 0) {
+ dev->priv = d; /* We need 'priv', hdlc doesn't */
+ printk("%s: PC300/", hdlc->name);
+ switch(card->hw.type) {
+ case PC300_TE:
+ printk("TE ");
+ break;
+
+ case PC300_X21:
+ printk("X21");
+ break;
+
+ case PC300_RSV:
+ default:
+ printk("RSV");
+ break;
+ }
+ printk(" #%d, %ldKB of RAM at 0x%lx, IRQ%d, channel %d.\n",
+ i + 1, card->hw.ramsize/1024, card->hw.ramphys,
+ card->hw.irq, j + 1);
+ devcount++;
+ } else {
+ *(d->name) = 0;
+ kfree(d->hdlc);
+ continue;
+ }
+ }
+ spin_lock_init(&card->card_lock);
+ } else {
+ memset(&card->hw, 0, sizeof (pc300hw_t));
+ }
+ }
+
+ return 0;
+}
+
+#ifdef MODULE
+int init_module (void)
+{
+ return (cpc_init());
+} /* init_module */
+
+void cleanup_module (void)
+{
+ int i, j;
+
+ for (i = 0 ; i < PC300_MAXCARDS ; i++) {
+ pc300_t *card = &cpc_card[i];
+
+ if (card->hw.rambase != 0) {
+ for(j = 0 ; j < card->hw.nchan ; j++) {
+ unregister_hdlc_device(card->chan[j].d.hdlc);
+ }
+
+ iounmap((void *)card->hw.plxbase);
+ iounmap((void *)card->hw.scabase);
+ iounmap((void *)card->hw.rambase);
+ if (card->hw.type == PC300_TE)
+ iounmap((void *)card->hw.falcbase);
+ if (card->hw.irq)
+ free_irq(card->hw.irq, card);
+ }
+ }
+}
+#endif
+
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)