]> git.hungrycats.org Git - linux/commitdiff
Add new PC300 WAN driver (courtesy Cyclades), and my own changes:
authorJeff Garzik <jgarzik@mandrakesoft.com>
Tue, 30 Apr 2002 17:38:46 +0000 (13:38 -0400)
committerJeff Garzik <jgarzik@mandrakesoft.com>
Tue, 30 Apr 2002 17:38:46 +0000 (13:38 -0400)
* patch added stuff to include/linux.  move these three new headers
  to drivers/net/wan.
* change the code to support these changes
* slightly better Config.in entry. needs more work, though.

drivers/net/wan/Config.help
drivers/net/wan/Config.in
drivers/net/wan/Makefile
drivers/net/wan/hd64572.h [new file with mode: 0644]
drivers/net/wan/pc300-falc-lh.h [new file with mode: 0644]
drivers/net/wan/pc300.h [new file with mode: 0644]
drivers/net/wan/pc300_drv.c [new file with mode: 0644]
drivers/net/wan/pc300_tty.c [new file with mode: 0644]
drivers/pci/pci.ids
include/linux/pci_ids.h

index eebc3287fa86319d490e1388f9633070137e08fe..530f11937044278cd7620846cd4d665da2b6fb03 100644 (file)
@@ -426,3 +426,25 @@ CONFIG_C101
 
   If unsure, say N here.
 
+Cyclades-PC300 support
+CONFIG_PC300
+  This is a driver for the Cyclades-PC300 synchronous communication
+  boards. These boards provide synchronous serial interfaces to your
+  Linux box (interfaces currently available are RS-232/V.35, X.21 and
+  T1/E1). If you wish to support Multilink PPP, please select the
+  option below this one and read the file README.mlppp provided by PC300
+  package.
+
+  If you want to compile this as a module ( = code which can be
+  inserted in and removed from the running kernel whenever you want),
+  say M here and read Documentation/modules.txt. The module will be
+  called pc300.o.
+
+  If you haven't heard about it, it's safe to say N.
+
+Cyclades-PC300 Sync TTY (to MLPPP) support
+CONFIG_PC300_MLPPP
+  Say 'Y' to this option if you are planning to use Multilink PPP over the
+  PC300 synchronous communication boards.
+
+
index 34c1eb53487b4919da7787e31dba4afe594d497c..08b70907b4ea00d90b22259ed95fac5d802ff01e 100644 (file)
@@ -66,6 +66,17 @@ if [ "$CONFIG_WAN" = "y" ]; then
       else
         comment '    X.25/LAPB support is disabled'
       fi
+
+      dep_tristate '    Cyclades-PC300 support (RS-232/V.35, X.21, T1/E1 boards)' CONFIG_PC300 $CONFIG_HDLC $CONFIG_PCI
+      if [ "$CONFIG_PC300" != "n" ]; then
+            if [ "$CONFIG_PPP" != "n" -a "$CONFIG_PPP_MULTLINK" != "n" -a "$CONFIG_PPP_SYNCTTY" != "n" -a "$CONFIG_HDLC_PPP" = "y" ]; then
+                    bool '      Cyclades-PC300 MLPPP support' CONFIG_PC300_MLPPP
+            else
+                    comment '     Cyclades-PC300 MLPPP support is disabled.'+                       comment '     Refer to the file README.mlppp, provided by PC300 package.'
+            fi
+      fi
+
+
       dep_tristate '    SDL RISCom/N2 support' CONFIG_N2 $CONFIG_HDLC
       dep_tristate '    Moxa C101 support' CONFIG_C101 $CONFIG_HDLC
       dep_tristate '    FarSync T-Series support' CONFIG_FARSYNC $CONFIG_HDLC
index 7452bd2669c3f931e78a157b6f818b25f31d229d..ebf712b3b8bb440ac21d0eff9c1145b097a455ab 100644 (file)
@@ -8,7 +8,7 @@
 O_TARGET := wan.o
 
 export-objs := z85230.o syncppp.o comx.o sdladrv.o cycx_drv.o hdlc_generic.o \
-               dlci.o
+               dlci.o pc300_drv.o
 
 wanpipe-y                      := sdlamain.o sdla_ft1.o
 wanpipe-$(CONFIG_WANPIPE_X25)  += sdla_x25.o
@@ -30,6 +30,10 @@ hdlc-$(CONFIG_HDLC_PPP)              += hdlc_ppp.o
 hdlc-$(CONFIG_HDLC_X25)                += hdlc_x25.o
 hdlc-objs                      := $(hdlc-y)
 
+pc300-y                                := pc300_drv.o
+pc300-$(CONFIG_PC300_MLPPP)    += pc300_tty.o
+pc300-objs                     := $(pc300-y)
+
 obj-$(CONFIG_HOSTESS_SV11)     += z85230.o     syncppp.o       hostess_sv11.o
 obj-$(CONFIG_SEALEVEL_4021)    += z85230.o     syncppp.o       sealevel.o
 obj-$(CONFIG_COMX)             +=                              comx.o
@@ -63,6 +67,7 @@ endif
 obj-$(CONFIG_CYCLADES_SYNC)    += cycx_drv.o cyclomx.o
 obj-$(CONFIG_LAPBETHER)                += lapbether.o
 obj-$(CONFIG_SBNI)             += sbni.o
+obj-$(CONFIG_PC300)            += pc300.o
 obj-$(CONFIG_HDLC)             += hdlc.o
 ifeq ($(CONFIG_HDLC_PPP),y)
   obj-$(CONFIG_HDLC)           += syncppp.o
diff --git a/drivers/net/wan/hd64572.h b/drivers/net/wan/hd64572.h
new file mode 100644 (file)
index 0000000..65995bb
--- /dev/null
@@ -0,0 +1,443 @@
+/*
+ * hd64572.h   Description of the Hitachi HD64572 (SCA-II), valid for 
+ *             CPU modes 0 & 2.
+ *
+ * Author:     Ivan Passos <ivan@cyclades.com>
+ *
+ * Copyright:   (c) 2000-2001 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: hd64572.h,v $
+ * Revision 3.1  2001/06/15 12:41:10  regina
+ * upping major version number
+ *
+ * Revision 1.1.1.1  2001/06/13 20:24:49  daniela
+ * PC300 initial CVS version (3.4.0-pre1)
+ *
+ * Revision 1.0 2000/01/25 ivan
+ * Initial version.
+ *
+ */
+
+#ifndef _HD64572_H
+#define _HD64572_H
+
+/* Illegal Access Register */
+#define        ILAR    0x00
+
+/* Wait Controller Registers */
+#define PABR0L 0x20    /* Physical Addr Boundary Register 0 L */
+#define PABR0H 0x21    /* Physical Addr Boundary Register 0 H */
+#define PABR1L 0x22    /* Physical Addr Boundary Register 1 L */
+#define PABR1H 0x23    /* Physical Addr Boundary Register 1 H */
+#define WCRL   0x24    /* Wait Control Register L */
+#define WCRM   0x25    /* Wait Control Register M */
+#define WCRH   0x26    /* Wait Control Register H */
+
+/* Interrupt Registers */
+#define IVR    0x60    /* Interrupt Vector Register */
+#define IMVR   0x64    /* Interrupt Modified Vector Register */
+#define ITCR   0x68    /* Interrupt Control Register */
+#define ISR0   0x6c    /* Interrupt Status Register 0 */
+#define ISR1   0x70    /* Interrupt Status Register 1 */
+#define IER0   0x74    /* Interrupt Enable Register 0 */
+#define IER1   0x78    /* Interrupt Enable Register 1 */
+
+/* Register Access Macros (chan is 0 or 1 in _any_ case) */
+#define        M_REG(reg, chan)        (reg + 0x80*chan)               /* MSCI */
+#define        DRX_REG(reg, chan)      (reg + 0x40*chan)               /* DMA Rx */
+#define        DTX_REG(reg, chan)      (reg + 0x20*(2*chan + 1))       /* DMA Tx */
+#define        TRX_REG(reg, chan)      (reg + 0x20*chan)               /* Timer Rx */
+#define        TTX_REG(reg, chan)      (reg + 0x10*(2*chan + 1))       /* Timer Tx */
+#define        ST_REG(reg, chan)       (reg + 0x80*chan)               /* Status Cnt */
+#define IR0_DRX(val, chan)     ((val)<<(8*(chan)))             /* Int DMA Rx */
+#define IR0_DTX(val, chan)     ((val)<<(4*(2*chan + 1)))       /* Int DMA Tx */
+#define IR0_M(val, chan)       ((val)<<(8*(chan)))             /* Int MSCI */
+
+/* MSCI Channel Registers */
+#define MD0    0x138   /* Mode reg 0 */
+#define MD1    0x139   /* Mode reg 1 */
+#define MD2    0x13a   /* Mode reg 2 */
+#define MD3    0x13b   /* Mode reg 3 */
+#define CTL    0x130   /* Control reg */
+#define RXS    0x13c   /* RX clock source */
+#define TXS    0x13d   /* TX clock source */
+#define EXS    0x13e   /* External clock input selection */
+#define TMCT   0x144   /* Time constant (Tx) */
+#define TMCR   0x145   /* Time constant (Rx) */
+#define CMD    0x128   /* Command reg */
+#define ST0    0x118   /* Status reg 0 */
+#define ST1    0x119   /* Status reg 1 */
+#define ST2    0x11a   /* Status reg 2 */
+#define ST3    0x11b   /* Status reg 3 */
+#define ST4    0x11c   /* Status reg 4 */
+#define FST    0x11d   /* frame Status reg  */
+#define IE0    0x120   /* Interrupt enable reg 0 */
+#define IE1    0x121   /* Interrupt enable reg 1 */
+#define IE2    0x122   /* Interrupt enable reg 2 */
+#define IE4    0x124   /* Interrupt enable reg 4 */
+#define FIE    0x125   /* Frame Interrupt enable reg  */
+#define SA0    0x140   /* Syn Address reg 0 */
+#define SA1    0x141   /* Syn Address reg 1 */
+#define IDL    0x142   /* Idle register */
+#define TRBL   0x100   /* TX/RX buffer reg L */ 
+#define TRBK   0x101   /* TX/RX buffer reg K */ 
+#define TRBJ   0x102   /* TX/RX buffer reg J */ 
+#define TRBH   0x103   /* TX/RX buffer reg H */ 
+#define TRC0   0x148   /* TX Ready control reg 0 */ 
+#define TRC1   0x149   /* TX Ready control reg 1 */ 
+#define RRC    0x14a   /* RX Ready control reg */ 
+#define CST0   0x108   /* Current Status Register 0 */ 
+#define CST1   0x109   /* Current Status Register 1 */ 
+#define CST2   0x10a   /* Current Status Register 2 */ 
+#define CST3   0x10b   /* Current Status Register 3 */ 
+#define GPO    0x131   /* General Purpose Output Pin Ctl Reg */
+#define TFS    0x14b   /* Tx Start Threshold Ctl Reg */
+#define TFN    0x143   /* Inter-transmit-frame Time Fill Ctl Reg */
+#define TBN    0x110   /* Tx Buffer Number Reg */
+#define RBN    0x111   /* Rx Buffer Number Reg */
+#define TNR0   0x150   /* Tx DMA Request Ctl Reg 0 */
+#define TNR1   0x151   /* Tx DMA Request Ctl Reg 1 */
+#define TCR    0x152   /* Tx DMA Critical Request Reg */
+#define RNR    0x154   /* Rx DMA Request Ctl Reg */
+#define RCR    0x156   /* Rx DMA Critical Request Reg */
+
+/* Timer Registers */
+#define TCNTL  0x200   /* Timer Upcounter L */
+#define TCNTH  0x201   /* Timer Upcounter H */
+#define TCONRL 0x204   /* Timer Constant Register L */
+#define TCONRH 0x205   /* Timer Constant Register H */
+#define TCSR   0x206   /* Timer Control/Status Register */
+#define TEPR   0x207   /* Timer Expand Prescale Register */
+
+/* DMA registers */
+#define PCR            0x40            /* DMA priority control reg */
+#define DRR            0x44            /* DMA reset reg */
+#define DMER           0x07            /* DMA Master Enable reg */
+#define BTCR           0x08            /* Burst Tx Ctl Reg */
+#define BOLR           0x0c            /* Back-off Length Reg */
+#define DSR_RX(chan)   (0x48 + 2*chan) /* DMA Status Reg (Rx) */
+#define DSR_TX(chan)   (0x49 + 2*chan) /* DMA Status Reg (Tx) */
+#define DIR_RX(chan)   (0x4c + 2*chan) /* DMA Interrupt Enable Reg (Rx) */
+#define DIR_TX(chan)   (0x4d + 2*chan) /* DMA Interrupt Enable Reg (Tx) */
+#define FCT_RX(chan)   (0x50 + 2*chan) /* Frame End Interrupt Counter (Rx) */
+#define FCT_TX(chan)   (0x51 + 2*chan) /* Frame End Interrupt Counter (Tx) */
+#define DMR_RX(chan)   (0x54 + 2*chan) /* DMA Mode Reg (Rx) */
+#define DMR_TX(chan)   (0x55 + 2*chan) /* DMA Mode Reg (Tx) */
+#define DCR_RX(chan)   (0x58 + 2*chan) /* DMA Command Reg (Rx) */
+#define DCR_TX(chan)   (0x59 + 2*chan) /* DMA Command Reg (Tx) */
+
+/* DMA Channel Registers */
+#define DARL   0x80    /* Dest Addr Register L (single-block, RX only) */
+#define DARH   0x81    /* Dest Addr Register H (single-block, RX only) */
+#define DARB   0x82    /* Dest Addr Register B (single-block, RX only) */
+#define DARBH  0x83    /* Dest Addr Register BH (single-block, RX only) */
+#define SARL   0x80    /* Source Addr Register L (single-block, TX only) */
+#define SARH   0x81    /* Source Addr Register H (single-block, TX only) */
+#define SARB   0x82    /* Source Addr Register B (single-block, TX only) */
+#define DARBH  0x83    /* Source Addr Register BH (single-block, TX only) */
+#define BARL   0x80    /* Buffer Addr Register L (chained-block) */
+#define BARH   0x81    /* Buffer Addr Register H (chained-block) */
+#define BARB   0x82    /* Buffer Addr Register B (chained-block) */
+#define BARBH  0x83    /* Buffer Addr Register BH (chained-block) */
+#define CDAL   0x84    /* Current Descriptor Addr Register L */
+#define CDAH   0x85    /* Current Descriptor Addr Register H */
+#define CDAB   0x86    /* Current Descriptor Addr Register B */
+#define CDABH  0x87    /* Current Descriptor Addr Register BH */
+#define EDAL   0x88    /* Error Descriptor Addr Register L */
+#define EDAH   0x89    /* Error Descriptor Addr Register H */
+#define EDAB   0x8a    /* Error Descriptor Addr Register B */
+#define EDABH  0x8b    /* Error Descriptor Addr Register BH */
+#define BFLL   0x90    /* RX Buffer Length L (only RX) */
+#define BFLH   0x91    /* RX Buffer Length H (only RX) */
+#define BCRL   0x8c    /* Byte Count Register L */
+#define BCRH   0x8d    /* Byte Count Register H */
+
+/* Block Descriptor Structure */
+typedef struct {
+       unsigned long   next;           /* pointer to next block descriptor */
+       unsigned long   ptbuf;          /* buffer pointer */
+       unsigned short  len;            /* data length */
+       unsigned char   status;         /* status */
+       unsigned char   filler[5];      /* alignment filler (16 bytes) */ 
+} pcsca_bd_t;
+
+/* 
+       Descriptor Status definitions:
+
+       Bit     Transmission    Reception
+
+       7       EOM             EOM
+       6       -               Short Frame
+       5       -               Abort
+       4       -               Residual bit
+       3       Underrun        Overrun 
+       2       -               CRC
+       1       Ownership       Ownership
+       0       EOT             -
+*/
+#define DST_EOT                0x01    /* End of transmit command */
+#define DST_OSB                0x02    /* Ownership bit */
+#define DST_CRC                0x04    /* CRC Error */
+#define DST_OVR                0x08    /* Overrun */
+#define DST_UDR                0x08    /* Underrun */
+#define DST_RBIT       0x10    /* Residual bit */
+#define DST_ABT                0x20    /* Abort */
+#define DST_SHRT       0x40    /* Short Frame  */
+#define DST_EOM                0x80    /* End of Message  */
+
+/* Status Counter Registers */
+#define CMCR   0x158   /* Counter Master Ctl Reg */
+#define TECNTL 0x160   /* Tx EOM Counter L */
+#define TECNTM 0x161   /* Tx EOM Counter M */
+#define TECNTH 0x162   /* Tx EOM Counter H */
+#define TECCR  0x163   /* Tx EOM Counter Ctl Reg */
+#define URCNTL 0x164   /* Underrun Counter L */
+#define URCNTH 0x165   /* Underrun Counter H */
+#define URCCR  0x167   /* Underrun Counter Ctl Reg */
+#define RECNTL 0x168   /* Rx EOM Counter L */
+#define RECNTM 0x169   /* Rx EOM Counter M */
+#define RECNTH 0x16a   /* Rx EOM Counter H */
+#define RECCR  0x16b   /* Rx EOM Counter Ctl Reg */
+#define ORCNTL 0x16c   /* Overrun Counter L */
+#define ORCNTH 0x16d   /* Overrun Counter H */
+#define ORCCR  0x16f   /* Overrun Counter Ctl Reg */
+#define CECNTL 0x170   /* CRC Counter L */
+#define CECNTH 0x171   /* CRC Counter H */
+#define CECCR  0x173   /* CRC Counter Ctl Reg */
+#define ABCNTL 0x174   /* Abort frame Counter L */
+#define ABCNTH 0x175   /* Abort frame Counter H */
+#define ABCCR  0x177   /* Abort frame Counter Ctl Reg */
+#define SHCNTL 0x178   /* Short frame Counter L */
+#define SHCNTH 0x179   /* Short frame Counter H */
+#define SHCCR  0x17b   /* Short frame Counter Ctl Reg */
+#define RSCNTL 0x17c   /* Residual bit Counter L */
+#define RSCNTH 0x17d   /* Residual bit Counter H */
+#define RSCCR  0x17f   /* Residual bit Counter Ctl Reg */
+
+/* Register Programming Constants */
+
+#define IR0_DMIC       0x00000001
+#define IR0_DMIB       0x00000002
+#define IR0_DMIA       0x00000004
+#define IR0_EFT                0x00000008
+#define IR0_DMAREQ     0x00010000
+#define IR0_TXINT      0x00020000
+#define IR0_RXINTB     0x00040000
+#define IR0_RXINTA     0x00080000
+#define IR0_TXRDY      0x00100000
+#define IR0_RXRDY      0x00200000
+
+#define MD0_CRC16_0    0x00
+#define MD0_CRC16_1    0x01
+#define MD0_CRC32      0x02
+#define MD0_CRC_CCITT  0x03
+#define MD0_CRCC0      0x04
+#define MD0_CRCC1      0x08
+#define MD0_AUTO_ENA   0x10
+#define MD0_ASYNC      0x00
+#define MD0_BY_MSYNC   0x20
+#define MD0_BY_BISYNC  0x40
+#define MD0_BY_EXT     0x60
+#define MD0_BIT_SYNC   0x80
+#define MD0_TRANSP     0xc0
+
+#define MD1_NOADDR     0x00
+#define MD1_SADDR1     0x40
+#define MD1_SADDR2     0x80
+#define MD1_DADDR      0xc0
+
+#define MD2_F_DUPLEX   0x00
+#define MD2_AUTO_ECHO  0x01
+#define MD2_LOOP_HI_Z  0x02
+#define MD2_LOOP_MIR   0x03
+#define MD2_ADPLL_X8   0x00
+#define MD2_ADPLL_X16  0x08
+#define MD2_ADPLL_X32  0x10
+#define MD2_NRZ                0x00
+#define MD2_NRZI       0x20
+#define MD2_NRZ_IEEE   0x40
+#define MD2_MANCH      0x00
+#define MD2_FM1                0x20
+#define MD2_FM0                0x40
+#define MD2_FM         0x80
+
+#define CTL_RTS                0x01
+#define CTL_DTR                0x02
+#define CTL_SYN                0x04
+#define CTL_IDLC       0x10
+#define CTL_UDRNC      0x20
+#define CTL_URSKP      0x40
+#define CTL_URCT       0x80
+
+#define        RXS_BR0         0x01
+#define        RXS_BR1         0x02
+#define        RXS_BR2         0x04
+#define        RXS_BR3         0x08
+#define        RXS_ECLK        0x00
+#define        RXS_ECLK_NS     0x20
+#define        RXS_IBRG        0x40
+#define        RXS_PLL1        0x50
+#define        RXS_PLL2        0x60
+#define        RXS_PLL3        0x70
+#define        RXS_DRTXC       0x80
+
+#define        TXS_BR0         0x01
+#define        TXS_BR1         0x02
+#define        TXS_BR2         0x04
+#define        TXS_BR3         0x08
+#define        TXS_ECLK        0x00
+#define        TXS_IBRG        0x40
+#define        TXS_RCLK        0x60
+#define        TXS_DTRXC       0x80
+
+#define        EXS_RES0        0x01
+#define        EXS_RES1        0x02
+#define        EXS_RES2        0x04
+#define        EXS_TES0        0x10
+#define        EXS_TES1        0x20
+#define        EXS_TES2        0x40
+
+#define CMD_RX_RST     0x11
+#define CMD_RX_ENA     0x12
+#define CMD_RX_DIS     0x13
+#define CMD_RX_CRC_INIT        0x14
+#define CMD_RX_MSG_REJ 0x15
+#define CMD_RX_MP_SRCH 0x16
+#define CMD_RX_CRC_EXC 0x17
+#define CMD_RX_CRC_FRC 0x18
+#define CMD_TX_RST     0x01
+#define CMD_TX_ENA     0x02
+#define CMD_TX_DISA    0x03
+#define CMD_TX_CRC_INIT        0x04
+#define CMD_TX_CRC_EXC 0x05
+#define CMD_TX_EOM     0x06
+#define CMD_TX_ABORT   0x07
+#define CMD_TX_MP_ON   0x08
+#define CMD_TX_BUF_CLR 0x09
+#define CMD_TX_DISB    0x0b
+#define CMD_CH_RST     0x21
+#define CMD_SRCH_MODE  0x31
+#define CMD_NOP                0x00
+
+#define ST0_RXRDY      0x01
+#define ST0_TXRDY      0x02
+#define ST0_RXINTB     0x20
+#define ST0_RXINTA     0x40
+#define ST0_TXINT      0x80
+
+#define ST1_IDLE       0x01
+#define ST1_ABORT      0x02
+#define ST1_CDCD       0x04
+#define ST1_CCTS       0x08
+#define ST1_SYN_FLAG   0x10
+#define ST1_CLMD       0x20
+#define ST1_TXIDLE     0x40
+#define ST1_UDRN       0x80
+
+#define ST2_CRCE       0x04
+#define ST2_ONRN       0x08
+#define ST2_RBIT       0x10
+#define ST2_ABORT      0x20
+#define ST2_SHORT      0x40
+#define ST2_EOM                0x80
+
+#define ST3_RX_ENA     0x01
+#define ST3_TX_ENA     0x02
+#define ST3_DCD                0x04
+#define ST3_CTS                0x08
+#define ST3_SRCH_MODE  0x10
+#define ST3_SLOOP      0x20
+#define ST3_GPI                0x80
+
+#define ST4_RDNR       0x01
+#define ST4_RDCR       0x02
+#define ST4_TDNR       0x04
+#define ST4_TDCR       0x08
+#define ST4_OCLM       0x20
+#define ST4_CFT                0x40
+#define ST4_CGPI       0x80
+
+#define FST_CRCEF      0x04
+#define FST_OVRNF      0x08
+#define FST_RBIF       0x10
+#define FST_ABTF       0x20
+#define FST_SHRTF      0x40
+#define FST_EOMF       0x80
+
+#define IE0_RXRDY      0x01
+#define IE0_TXRDY      0x02
+#define IE0_RXINTB     0x20
+#define IE0_RXINTA     0x40
+#define IE0_TXINT      0x80
+
+#define IE1_IDLD       0x01
+#define IE1_ABTD       0x02
+#define IE1_CDCD       0x04
+#define IE1_CCTS       0x08
+#define IE1_SYNCD      0x10
+#define IE1_CLMD       0x20
+#define IE1_IDL                0x40
+#define IE1_UDRN       0x80
+
+#define IE2_CRCE       0x04
+#define IE2_OVRN       0x08
+#define IE2_RBIT       0x10
+#define IE2_ABT                0x20
+#define IE2_SHRT       0x40
+#define IE2_EOM                0x80
+
+#define IE4_RDNR       0x01
+#define IE4_RDCR       0x02
+#define IE4_TDNR       0x04
+#define IE4_TDCR       0x08
+#define IE4_OCLM       0x20
+#define IE4_CFT                0x40
+#define IE4_CGPI       0x80
+
+#define FIE_CRCEF      0x04
+#define FIE_OVRNF      0x08
+#define FIE_RBIF       0x10
+#define FIE_ABTF       0x20
+#define FIE_SHRTF      0x40
+#define FIE_EOMF       0x80
+
+#define DSR_DWE                0x01
+#define DSR_DE         0x02
+#define DSR_REF                0x04
+#define DSR_UDRF       0x04
+#define DSR_COA                0x08
+#define DSR_COF                0x10
+#define DSR_BOF                0x20
+#define DSR_EOM                0x40
+#define DSR_EOT                0x80
+
+#define DIR_REF                0x04
+#define DIR_UDRF       0x04
+#define DIR_COA                0x08
+#define DIR_COF                0x10
+#define DIR_BOF                0x20
+#define DIR_EOM                0x40
+#define DIR_EOT                0x80
+
+#define DMR_CNTE       0x02
+#define DMR_NF         0x04
+#define DMR_SEOME      0x08
+#define DMR_TMOD       0x10
+
+#define DCR_SW_ABT     0x01
+#define DCR_FCT_CLR    0x02
+
+#define PCR_PR0                0x01
+#define PCR_PR1                0x02
+#define PCR_PR2                0x04
+#define PCR_CCC                0x08
+#define PCR_BRC                0x10
+#define PCR_OSB                0x40
+#define PCR_BURST      0x80
+
+#endif /* (_HD64572_H) */
diff --git a/drivers/net/wan/pc300-falc-lh.h b/drivers/net/wan/pc300-falc-lh.h
new file mode 100644 (file)
index 0000000..01ed23c
--- /dev/null
@@ -0,0 +1,1238 @@
+/*
+ * falc.h      Description of the Siemens FALC T1/E1 framer.
+ *
+ * Author:     Ivan Passos <ivan@cyclades.com>
+ *
+ * Copyright:  (c) 2000-2001 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: falc-lh.h,v $
+ * Revision 3.1  2001/06/15 12:41:10  regina
+ * upping major version number
+ *
+ * Revision 1.1.1.1  2001/06/13 20:24:47  daniela
+ * PC300 initial CVS version (3.4.0-pre1)
+ *
+ * Revision 1.1 2000/05/15 ivan
+ * Included DJA bits for the LIM2 register.
+ *
+ * Revision 1.0 2000/02/22 ivan
+ * Initial version.
+ *
+ */
+
+#ifndef _FALC_LH_H
+#define _FALC_LH_H
+
+#define NUM_OF_T1_CHANNELS     24
+#define NUM_OF_E1_CHANNELS     32
+
+/*>>>>>>>>>>>>>>>>>  FALC Register Bits (Transmit Mode)  <<<<<<<<<<<<<<<<<<< */
+
+/* CMDR (Command Register)
+   ---------------- E1 & T1 ------------------------------ */
+#define CMDR_RMC       0x80
+#define CMDR_RRES      0x40
+#define CMDR_XREP      0x20
+#define CMDR_XRES      0x10
+#define CMDR_XHF       0x08
+#define CMDR_XTF       0x04
+#define CMDR_XME       0x02
+#define CMDR_SRES      0x01
+
+/* MODE (Mode Register)
+   ----------------- E1 & T1 ----------------------------- */
+#define MODE_MDS2      0x80
+#define MODE_MDS1      0x40
+#define MODE_MDS0      0x20
+#define MODE_BRAC      0x10
+#define MODE_HRAC      0x08
+
+/* IPC (Interrupt Port Configuration)
+   ----------------- E1 & T1 ----------------------------- */
+#define IPC_VIS                0x80
+#define IPC_SCI                0x04
+#define IPC_IC1                0x02
+#define IPC_IC0                0x01
+
+/* CCR1 (Common Configuration Register 1)
+   ----------------- E1 & T1 ----------------------------- */
+#define CCR1_SFLG       0x80
+#define CCR1_XTS16RA    0x40
+#define CCR1_BRM        0x40
+#define CCR1_CASSYM     0x20
+#define CCR1_EDLX       0x20
+#define CCR1_EITS       0x10
+#define CCR1_ITF        0x08
+#define CCR1_RFT1       0x02
+#define CCR1_RFT0       0x01
+
+/* CCR3 (Common Configuration Register 3)
+   ---------------- E1 & T1 ------------------------------ */
+
+#define CCR3_PRE1       0x80
+#define CCR3_PRE0       0x40
+#define CCR3_EPT        0x20
+#define CCR3_RADD       0x10
+#define CCR3_RCRC       0x04
+#define CCR3_XCRC       0x02
+
+
+/* RTR1-4 (Receive Timeslot Register 1-4)
+   ---------------- E1 & T1 ------------------------------ */
+
+#define RTR1_TS0        0x80
+#define RTR1_TS1        0x40
+#define RTR1_TS2        0x20
+#define RTR1_TS3        0x10
+#define RTR1_TS4        0x08
+#define RTR1_TS5        0x04
+#define RTR1_TS6        0x02
+#define RTR1_TS7        0x01
+
+#define RTR2_TS8        0x80
+#define RTR2_TS9        0x40
+#define RTR2_TS10       0x20
+#define RTR2_TS11       0x10
+#define RTR2_TS12       0x08
+#define RTR2_TS13       0x04
+#define RTR2_TS14       0x02
+#define RTR2_TS15       0x01
+
+#define RTR3_TS16       0x80
+#define RTR3_TS17       0x40
+#define RTR3_TS18       0x20
+#define RTR3_TS19       0x10
+#define RTR3_TS20       0x08
+#define RTR3_TS21       0x04
+#define RTR3_TS22       0x02
+#define RTR3_TS23       0x01
+
+#define RTR4_TS24       0x80
+#define RTR4_TS25       0x40
+#define RTR4_TS26       0x20
+#define RTR4_TS27       0x10
+#define RTR4_TS28       0x08
+#define RTR4_TS29       0x04
+#define RTR4_TS30       0x02
+#define RTR4_TS31       0x01
+
+
+/* TTR1-4 (Transmit Timeslot Register 1-4)
+   ---------------- E1 & T1 ------------------------------ */
+
+#define TTR1_TS0        0x80
+#define TTR1_TS1        0x40
+#define TTR1_TS2        0x20
+#define TTR1_TS3        0x10
+#define TTR1_TS4        0x08
+#define TTR1_TS5        0x04
+#define TTR1_TS6        0x02
+#define TTR1_TS7        0x01
+
+#define TTR2_TS8        0x80
+#define TTR2_TS9        0x40
+#define TTR2_TS10       0x20
+#define TTR2_TS11       0x10
+#define TTR2_TS12       0x08
+#define TTR2_TS13       0x04
+#define TTR2_TS14       0x02
+#define TTR2_TS15       0x01
+
+#define TTR3_TS16       0x80
+#define TTR3_TS17       0x40
+#define TTR3_TS18       0x20
+#define TTR3_TS19       0x10
+#define TTR3_TS20       0x08
+#define TTR3_TS21       0x04
+#define TTR3_TS22       0x02
+#define TTR3_TS23       0x01
+
+#define TTR4_TS24       0x80
+#define TTR4_TS25       0x40
+#define TTR4_TS26       0x20
+#define TTR4_TS27       0x10
+#define TTR4_TS28       0x08
+#define TTR4_TS29       0x04
+#define TTR4_TS30       0x02
+#define TTR4_TS31       0x01
+
+
+
+/* IMR0-4 (Interrupt Mask Register 0-4)
+
+   ----------------- E1 & T1 ----------------------------- */
+
+#define IMR0_RME        0x80
+#define IMR0_RFS        0x40
+#define IMR0_T8MS       0x20
+#define IMR0_ISF        0x20
+#define IMR0_RMB        0x10
+#define IMR0_CASC       0x08
+#define IMR0_RSC        0x08
+#define IMR0_CRC6       0x04
+#define IMR0_CRC4       0x04
+#define IMR0_PDEN      0x02
+#define IMR0_RPF        0x01
+
+#define IMR1_CASE       0x80
+#define IMR1_RDO        0x40
+#define IMR1_ALLS       0x20
+#define IMR1_XDU        0x10
+#define IMR1_XMB        0x08
+#define IMR1_XLSC       0x02
+#define IMR1_XPR        0x01
+#define IMR1_LLBSC     0x80
+
+#define IMR2_FAR        0x80
+#define IMR2_LFA        0x40
+#define IMR2_MFAR       0x20
+#define IMR2_T400MS     0x10
+#define IMR2_LMFA       0x10
+#define IMR2_AIS        0x08
+#define IMR2_LOS        0x04
+#define IMR2_RAR        0x02
+#define IMR2_RA         0x01
+
+#define IMR3_ES         0x80
+#define IMR3_SEC        0x40
+#define IMR3_LMFA16     0x20
+#define IMR3_AIS16      0x10
+#define IMR3_RA16       0x08
+#define IMR3_API        0x04
+#define IMR3_XSLP       0x20
+#define IMR3_XSLN       0x10
+#define IMR3_LLBSC      0x08
+#define IMR3_XRS        0x04
+#define IMR3_SLN        0x02
+#define IMR3_SLP        0x01
+
+#define IMR4_LFA        0x80
+#define IMR4_FER        0x40
+#define IMR4_CER        0x20
+#define IMR4_AIS        0x10
+#define IMR4_LOS        0x08
+#define IMR4_CVE        0x04
+#define IMR4_SLIP       0x02
+#define IMR4_EBE        0x01
+
+/* FMR0-5 for E1 and T1  (Framer Mode Register ) */
+
+#define FMR0_XC1        0x80
+#define FMR0_XC0        0x40
+#define FMR0_RC1        0x20
+#define FMR0_RC0        0x10
+#define FMR0_EXTD       0x08
+#define FMR0_ALM        0x04
+#define E1_FMR0_FRS     0x02
+#define T1_FMR0_FRS     0x08
+#define FMR0_SRAF       0x04
+#define FMR0_EXLS       0x02
+#define FMR0_SIM        0x01
+
+#define FMR1_MFCS       0x80
+#define FMR1_AFR        0x40
+#define FMR1_ENSA       0x20
+#define FMR1_CTM        0x80
+#define FMR1_SIGM       0x40
+#define FMR1_EDL        0x20
+#define FMR1_PMOD       0x10
+#define FMR1_XFS        0x08
+#define FMR1_CRC        0x08
+#define FMR1_ECM        0x04
+#define FMR1_IMOD       0x02
+#define FMR1_XAIS       0x01
+
+#define FMR2_RFS1       0x80
+#define FMR2_RFS0       0x40
+#define FMR2_MCSP      0x40
+#define FMR2_RTM        0x20
+#define FMR2_SSP        0x20
+#define FMR2_DAIS       0x10
+#define FMR2_SAIS       0x08
+#define FMR2_PLB        0x04
+#define FMR2_AXRA       0x02
+#define FMR2_ALMF       0x01
+#define FMR2_EXZE       0x01
+
+#define LOOP_RTM       0x40
+#define LOOP_SFM       0x40
+#define LOOP_ECLB      0x20
+#define LOOP_CLA       0x1f
+
+/*--------------------- E1 ----------------------------*/
+#define FMR3_XLD       0x20
+#define FMR3_XLU       0x10
+
+/*--------------------- T1 ----------------------------*/
+#define FMR4_AIS3       0x80
+#define FMR4_TM         0x40
+#define FMR4_XRA        0x20
+#define FMR4_SSC1       0x10
+#define FMR4_SSC0       0x08
+#define FMR4_AUTO       0x04
+#define FMR4_FM1        0x02
+#define FMR4_FM0        0x01
+
+#define FMR5_SRS        0x80
+#define FMR5_EIBR       0x40
+#define FMR5_XLD        0x20
+#define FMR5_XLU        0x10
+
+
+/* LOOP (Channel Loop Back)
+
+   ------------------ E1 & T1 ---------------------------- */
+
+#define LOOP_SFM        0x40
+#define LOOP_ECLB       0x20
+#define LOOP_CLA4       0x10
+#define LOOP_CLA3       0x08
+#define LOOP_CLA2       0x04
+#define LOOP_CLA1       0x02
+#define LOOP_CLA0       0x01
+
+
+
+/* XSW (Transmit Service Word Pulseframe)
+
+   ------------------- E1 --------------------------- */
+
+#define XSW_XSIS        0x80
+#define XSW_XTM         0x40
+#define XSW_XRA         0x20
+#define XSW_XY0         0x10
+#define XSW_XY1         0x08
+#define XSW_XY2         0x04
+#define XSW_XY3         0x02
+#define XSW_XY4         0x01
+
+
+/* XSP (Transmit Spare Bits)
+
+   ------------------- E1 --------------------------- */
+
+#define XSP_XAP         0x80
+#define XSP_CASEN       0x40
+#define XSP_TT0         0x20
+#define XSP_EBP         0x10
+#define XSP_AXS         0x08
+#define XSP_XSIF        0x04
+#define XSP_XS13        0x02
+#define XSP_XS15        0x01
+
+
+/* XC0/1 (Transmit Control 0/1)
+   ------------------ E1 & T1 ---------------------------- */
+
+#define XC0_SA8E        0x80
+#define XC0_SA7E        0x40
+#define XC0_SA6E        0x20
+#define XC0_SA5E        0x10
+#define XC0_SA4E        0x08
+#define XC0_BRM         0x80
+#define XC0_MFBS        0x40
+#define XC0_SFRZ        0x10
+#define XC0_XCO2        0x04
+#define XC0_XCO1        0x02
+#define XC0_XCO0        0x01
+
+#define XC1_XTO5        0x20
+#define XC1_XTO4        0x10
+#define XC1_XTO3        0x08
+#define XC1_XTO2        0x04
+#define XC1_XTO1        0x02
+#define XC1_XTO0        0x01
+
+
+/* RC0/1 (Receive Control 0/1)
+   ------------------ E1 & T1 ---------------------------- */
+
+#define RC0_SICS        0x40
+#define RC0_CRCI        0x20
+#define RC0_XCRCI       0x10
+#define RC0_RDIS        0x08
+#define RC0_RCO2        0x04
+#define RC0_RCO1        0x02
+#define RC0_RCO0        0x01
+
+#define RC1_SWD         0x80
+#define RC1_ASY4        0x40
+#define RC1_RRAM        0x40
+#define RC1_RTO5        0x20
+#define RC1_RTO4        0x10
+#define RC1_RTO3        0x08
+#define RC1_RTO2        0x04
+#define RC1_RTO1        0x02
+#define RC1_RTO0        0x01
+
+
+
+/* XPM0-2 (Transmit Pulse Mask 0-2)
+   --------------------- E1 & T1 ------------------------- */
+
+#define XPM0_XP12       0x80
+#define XPM0_XP11       0x40
+#define XPM0_XP10       0x20
+#define XPM0_XP04       0x10
+#define XPM0_XP03       0x08
+#define XPM0_XP02       0x04
+#define XPM0_XP01       0x02
+#define XPM0_XP00       0x01
+
+#define XPM1_XP30       0x80
+#define XPM1_XP24       0x40
+#define XPM1_XP23       0x20
+#define XPM1_XP22       0x10
+#define XPM1_XP21       0x08
+#define XPM1_XP20       0x04
+#define XPM1_XP14       0x02
+#define XPM1_XP13       0x01
+
+#define XPM2_XLHP       0x80
+#define XPM2_XLT        0x40
+#define XPM2_DAXLT      0x20
+#define XPM2_XP34       0x08
+#define XPM2_XP33       0x04
+#define XPM2_XP32       0x02
+#define XPM2_XP31       0x01
+
+
+/* TSWM (Transparent Service Word Mask)
+   ------------------ E1 ---------------------------- */
+
+#define TSWM_TSIS       0x80
+#define TSWM_TSIF       0x40
+#define TSWM_TRA        0x20
+#define TSWM_TSA4       0x10
+#define TSWM_TSA5       0x08
+#define TSWM_TSA6       0x04
+#define TSWM_TSA7       0x02
+#define TSWM_TSA8       0x01
+
+/* IDLE <Idle Channel Code Register>
+
+   ------------------ E1 & T1 ----------------------- */
+
+#define IDLE_IDL7       0x80
+#define IDLE_IDL6       0x40
+#define IDLE_IDL5       0x20
+#define IDLE_IDL4       0x10
+#define IDLE_IDL3       0x08
+#define IDLE_IDL2       0x04
+#define IDLE_IDL1       0x02
+#define IDLE_IDL0       0x01
+
+
+/* XSA4-8 <Transmit SA4-8 Register(Read/Write) >
+   -------------------E1 ----------------------------- */
+
+#define XSA4_XS47       0x80
+#define XSA4_XS46       0x40
+#define XSA4_XS45       0x20
+#define XSA4_XS44       0x10
+#define XSA4_XS43       0x08
+#define XSA4_XS42       0x04
+#define XSA4_XS41       0x02
+#define XSA4_XS40       0x01
+
+#define XSA5_XS57       0x80
+#define XSA5_XS56       0x40
+#define XSA5_XS55       0x20
+#define XSA5_XS54       0x10
+#define XSA5_XS53       0x08
+#define XSA5_XS52       0x04
+#define XSA5_XS51       0x02
+#define XSA5_XS50       0x01
+
+#define XSA6_XS67       0x80
+#define XSA6_XS66       0x40
+#define XSA6_XS65       0x20
+#define XSA6_XS64       0x10
+#define XSA6_XS63       0x08
+#define XSA6_XS62       0x04
+#define XSA6_XS61       0x02
+#define XSA6_XS60       0x01
+
+#define XSA7_XS77       0x80
+#define XSA7_XS76       0x40
+#define XSA7_XS75       0x20
+#define XSA7_XS74       0x10
+#define XSA7_XS73       0x08
+#define XSA7_XS72       0x04
+#define XSA7_XS71       0x02
+#define XSA7_XS70       0x01
+
+#define XSA8_XS87       0x80
+#define XSA8_XS86       0x40
+#define XSA8_XS85       0x20
+#define XSA8_XS84       0x10
+#define XSA8_XS83       0x08
+#define XSA8_XS82       0x04
+#define XSA8_XS81       0x02
+#define XSA8_XS80       0x01
+
+
+/* XDL1-3 (Transmit DL-Bit Register1-3 (read/write))
+   ----------------------- T1 --------------------- */
+
+#define XDL1_XDL17      0x80
+#define XDL1_XDL16      0x40
+#define XDL1_XDL15      0x20
+#define XDL1_XDL14      0x10
+#define XDL1_XDL13      0x08
+#define XDL1_XDL12      0x04
+#define XDL1_XDL11      0x02
+#define XDL1_XDL10      0x01
+
+#define XDL2_XDL27      0x80
+#define XDL2_XDL26      0x40
+#define XDL2_XDL25      0x20
+#define XDL2_XDL24      0x10
+#define XDL2_XDL23      0x08
+#define XDL2_XDL22      0x04
+#define XDL2_XDL21      0x02
+#define XDL2_XDL20      0x01
+
+#define XDL3_XDL37      0x80
+#define XDL3_XDL36      0x40
+#define XDL3_XDL35      0x20
+#define XDL3_XDL34      0x10
+#define XDL3_XDL33      0x08
+#define XDL3_XDL32      0x04
+#define XDL3_XDL31      0x02
+#define XDL3_XDL30      0x01
+
+
+/* ICB1-4 (Idle Channel Register 1-4)
+   ------------------ E1 ---------------------------- */
+
+#define E1_ICB1_IC0    0x80
+#define E1_ICB1_IC1    0x40
+#define E1_ICB1_IC2    0x20
+#define E1_ICB1_IC3    0x10
+#define E1_ICB1_IC4    0x08
+#define E1_ICB1_IC5    0x04
+#define E1_ICB1_IC6    0x02
+#define E1_ICB1_IC7    0x01
+
+#define E1_ICB2_IC8    0x80
+#define E1_ICB2_IC9    0x40
+#define E1_ICB2_IC10   0x20
+#define E1_ICB2_IC11   0x10
+#define E1_ICB2_IC12   0x08
+#define E1_ICB2_IC13   0x04
+#define E1_ICB2_IC14   0x02
+#define E1_ICB2_IC15   0x01
+
+#define E1_ICB3_IC16   0x80
+#define E1_ICB3_IC17   0x40
+#define E1_ICB3_IC18   0x20
+#define E1_ICB3_IC19   0x10
+#define E1_ICB3_IC20   0x08
+#define E1_ICB3_IC21   0x04
+#define E1_ICB3_IC22   0x02
+#define E1_ICB3_IC23   0x01
+
+#define E1_ICB4_IC24   0x80
+#define E1_ICB4_IC25   0x40
+#define E1_ICB4_IC26   0x20
+#define E1_ICB4_IC27   0x10
+#define E1_ICB4_IC28   0x08
+#define E1_ICB4_IC29   0x04
+#define E1_ICB4_IC30   0x02
+#define E1_ICB4_IC31   0x01
+
+/* ICB1-4 (Idle Channel Register 1-4)
+   ------------------ T1 ---------------------------- */
+
+#define T1_ICB1_IC1    0x80
+#define T1_ICB1_IC2    0x40
+#define T1_ICB1_IC3    0x20
+#define T1_ICB1_IC4    0x10
+#define T1_ICB1_IC5    0x08
+#define T1_ICB1_IC6    0x04
+#define T1_ICB1_IC7    0x02
+#define T1_ICB1_IC8    0x01
+
+#define T1_ICB2_IC9    0x80
+#define T1_ICB2_IC10   0x40
+#define T1_ICB2_IC11   0x20
+#define T1_ICB2_IC12   0x10
+#define T1_ICB2_IC13   0x08
+#define T1_ICB2_IC14   0x04
+#define T1_ICB2_IC15   0x02
+#define T1_ICB2_IC16   0x01
+
+#define T1_ICB3_IC17   0x80
+#define T1_ICB3_IC18   0x40
+#define T1_ICB3_IC19   0x20
+#define T1_ICB3_IC20   0x10
+#define T1_ICB3_IC21   0x08
+#define T1_ICB3_IC22   0x04
+#define T1_ICB3_IC23   0x02
+#define T1_ICB3_IC24   0x01
+
+/* FMR3 (Framer Mode Register 3)
+   --------------------E1------------------------ */
+
+#define FMR3_CMI        0x08
+#define FMR3_SYNSA      0x04
+#define FMR3_CFRZ       0x02
+#define FMR3_EXTIW      0x01
+
+
+
+/* CCB1-3 (Clear Channel Register)
+   ------------------- T1 ----------------------- */
+
+#define CCB1_CH1        0x80
+#define CCB1_CH2        0x40
+#define CCB1_CH3        0x20
+#define CCB1_CH4        0x10
+#define CCB1_CH5        0x08
+#define CCB1_CH6        0x04
+#define CCB1_CH7        0x02
+#define CCB1_CH8        0x01
+
+#define CCB2_CH9        0x80
+#define CCB2_CH10       0x40
+#define CCB2_CH11       0x20
+#define CCB2_CH12       0x10
+#define CCB2_CH13       0x08
+#define CCB2_CH14       0x04
+#define CCB2_CH15       0x02
+#define CCB2_CH16       0x01
+
+#define CCB3_CH17       0x80
+#define CCB3_CH18       0x40
+#define CCB3_CH19       0x20
+#define CCB3_CH20       0x10
+#define CCB3_CH21       0x08
+#define CCB3_CH22       0x04
+#define CCB3_CH23       0x02
+#define CCB3_CH24       0x01
+
+
+/* LIM0/1 (Line Interface Mode 0/1)
+   ------------------- E1 & T1 --------------------------- */
+
+#define LIM0_XFB        0x80
+#define LIM0_XDOS       0x40
+#define LIM0_SCL1       0x20
+#define LIM0_SCL0       0x10
+#define LIM0_EQON       0x08
+#define LIM0_ELOS       0x04
+#define LIM0_LL         0x02
+#define LIM0_MAS        0x01
+
+#define LIM1_EFSC       0x80
+#define LIM1_RIL2       0x40
+#define LIM1_RIL1       0x20
+#define LIM1_RIL0       0x10
+#define LIM1_DCOC       0x08
+#define LIM1_JATT       0x04
+#define LIM1_RL         0x02
+#define LIM1_DRS        0x01
+
+
+/* PCDR (Pulse Count Detection Register(Read/Write))
+   ------------------ E1 & T1 ------------------------- */
+
+#define PCDR_PCD7      0x80
+#define PCDR_PCD6      0x40
+#define PCDR_PCD5      0x20
+#define PCDR_PCD4      0x10
+#define PCDR_PCD3      0x08
+#define PCDR_PCD2      0x04
+#define PCDR_PCD1      0x02
+#define PCDR_PCD0      0x01
+
+#define PCRR_PCR7      0x80
+#define PCRR_PCR6      0x40
+#define PCRR_PCR5      0x20
+#define PCRR_PCR4      0x10
+#define PCRR_PCR3      0x08
+#define PCRR_PCR2      0x04
+#define PCRR_PCR1      0x02
+#define PCRR_PCR0      0x01
+
+
+/* LIM2 (Line Interface Mode 2)
+
+   ------------------ E1 & T1 ---------------------------- */
+
+#define LIM2_DJA2      0x20
+#define LIM2_DJA1      0x10
+#define LIM2_LOS2      0x02
+#define LIM2_LOS1      0x01
+
+/* LCR1 (Loop Code Register 1) */
+
+#define LCR1_EPRM      0x80
+#define        LCR1_XPRBS      0x40
+
+/* SIC1 (System Interface Control 1) */
+#define SIC1_SRSC      0x80
+#define SIC1_RBS1      0x20
+#define SIC1_RBS0      0x10
+#define SIC1_SXSC      0x08
+#define SIC1_XBS1      0x02
+#define SIC1_XBS0      0x01
+
+/* DEC (Disable Error Counter)
+   ------------------ E1 & T1 ---------------------------- */
+
+#define DEC_DCEC3       0x20
+#define DEC_DBEC        0x10
+#define DEC_DCEC1       0x08
+#define DEC_DCEC        0x08
+#define DEC_DEBC        0x04
+#define DEC_DCVC        0x02
+#define DEC_DFEC        0x01
+
+
+/* FALC Register Bits (Receive Mode)
+   ---------------------------------------------------------------------------- */
+
+
+/* FRS0/1 (Framer Receive Status Register 0/1)
+   ----------------- E1 & T1 ---------------------------------- */
+
+#define FRS0_LOS        0x80
+#define FRS0_AIS        0x40
+#define FRS0_LFA        0x20
+#define FRS0_RRA        0x10
+#define FRS0_API        0x08
+#define FRS0_NMF        0x04
+#define FRS0_LMFA       0x02
+#define FRS0_FSRF       0x01
+
+#define FRS1_TS16RA     0x40
+#define FRS1_TS16LOS    0x20
+#define FRS1_TS16AIS    0x10
+#define FRS1_TS16LFA    0x08
+#define FRS1_EXZD       0x80
+#define FRS1_LLBDD      0x10
+#define FRS1_LLBAD      0x08
+#define FRS1_XLS        0x02
+#define FRS1_XLO        0x01
+#define FRS1_PDEN      0x40
+
+/* FRS2/3 (Framer Receive Status Register 2/3)
+   ----------------- T1 ---------------------------------- */
+
+#define FRS2_ESC2       0x80
+#define FRS2_ESC1       0x40
+#define FRS2_ESC0       0x20
+
+#define FRS3_FEH5       0x20
+#define FRS3_FEH4       0x10
+#define FRS3_FEH3       0x08
+#define FRS3_FEH2       0x04
+#define FRS3_FEH1       0x02
+#define FRS3_FEH0       0x01
+
+
+/* RSW (Receive Service Word Pulseframe)
+   ----------------- E1 ------------------------------ */
+
+#define RSW_RSI         0x80
+#define RSW_RRA         0x20
+#define RSW_RYO         0x10
+#define RSW_RY1         0x08
+#define RSW_RY2         0x04
+#define RSW_RY3         0x02
+#define RSW_RY4         0x01
+
+
+/* RSP (Receive Spare Bits / Additional Status)
+   ---------------- E1 ------------------------------- */
+
+#define RSP_SI1         0x80
+#define RSP_SI2         0x40
+#define RSP_LLBDD      0x10
+#define RSP_LLBAD      0x08
+#define RSP_RSIF        0x04
+#define RSP_RS13        0x02
+#define RSP_RS15        0x01
+
+
+/* FECL (Framing Error Counter)
+   ---------------- E1 & T1 -------------------------- */
+
+#define FECL_FE7        0x80
+#define FECL_FE6        0x40
+#define FECL_FE5        0x20
+#define FECL_FE4        0x10
+#define FECL_FE3        0x08
+#define FECL_FE2        0x04
+#define FECL_FE1        0x02
+#define FECL_FE0        0x01
+
+#define FECH_FE15       0x80
+#define FECH_FE14       0x40
+#define FECH_FE13       0x20
+#define FECH_FE12       0x10
+#define FECH_FE11       0x08
+#define FECH_FE10       0x04
+#define FECH_FE9        0x02
+#define FECH_FE8        0x01
+
+
+/* CVCl (Code Violation Counter)
+   ----------------- E1 ------------------------- */
+
+#define CVCL_CV7        0x80
+#define CVCL_CV6        0x40
+#define CVCL_CV5        0x20
+#define CVCL_CV4        0x10
+#define CVCL_CV3        0x08
+#define CVCL_CV2        0x04
+#define CVCL_CV1        0x02
+#define CVCL_CV0        0x01
+
+#define CVCH_CV15       0x80
+#define CVCH_CV14       0x40
+#define CVCH_CV13       0x20
+#define CVCH_CV12       0x10
+#define CVCH_CV11       0x08
+#define CVCH_CV10       0x04
+#define CVCH_CV9        0x02
+#define CVCH_CV8        0x01
+
+
+/* CEC1-3L (CRC Error Counter)
+   ------------------ E1 ----------------------------- */
+
+#define CEC1L_CR7       0x80
+#define CEC1L_CR6       0x40
+#define CEC1L_CR5       0x20
+#define CEC1L_CR4       0x10
+#define CEC1L_CR3       0x08
+#define CEC1L_CR2       0x04
+#define CEC1L_CR1       0x02
+#define CEC1L_CR0       0x01
+
+#define CEC1H_CR15      0x80
+#define CEC1H_CR14      0x40
+#define CEC1H_CR13      0x20
+#define CEC1H_CR12      0x10
+#define CEC1H_CR11      0x08
+#define CEC1H_CR10      0x04
+#define CEC1H_CR9       0x02
+#define CEC1H_CR8       0x01
+
+#define CEC2L_CR7       0x80
+#define CEC2L_CR6       0x40
+#define CEC2L_CR5       0x20
+#define CEC2L_CR4       0x10
+#define CEC2L_CR3       0x08
+#define CEC2L_CR2       0x04
+#define CEC2L_CR1       0x02
+#define CEC2L_CR0       0x01
+
+#define CEC2H_CR15      0x80
+#define CEC2H_CR14      0x40
+#define CEC2H_CR13      0x20
+#define CEC2H_CR12      0x10
+#define CEC2H_CR11      0x08
+#define CEC2H_CR10      0x04
+#define CEC2H_CR9       0x02
+#define CEC2H_CR8       0x01
+
+#define CEC3L_CR7       0x80
+#define CEC3L_CR6       0x40
+#define CEC3L_CR5       0x20
+#define CEC3L_CR4       0x10
+#define CEC3L_CR3       0x08
+#define CEC3L_CR2       0x04
+#define CEC3L_CR1       0x02
+#define CEC3L_CR0       0x01
+
+#define CEC3H_CR15      0x80
+#define CEC3H_CR14      0x40
+#define CEC3H_CR13      0x20
+#define CEC3H_CR12      0x10
+#define CEC3H_CR11      0x08
+#define CEC3H_CR10      0x04
+#define CEC3H_CR9       0x02
+#define CEC3H_CR8       0x01
+
+
+/* CECL (CRC Error Counter)
+
+   ------------------ T1 ----------------------------- */
+
+#define CECL_CR7        0x80
+#define CECL_CR6        0x40
+#define CECL_CR5        0x20
+#define CECL_CR4        0x10
+#define CECL_CR3        0x08
+#define CECL_CR2        0x04
+#define CECL_CR1        0x02
+#define CECL_CR0        0x01
+
+#define CECH_CR15       0x80
+#define CECH_CR14       0x40
+#define CECH_CR13       0x20
+#define CECH_CR12       0x10
+#define CECH_CR11       0x08
+#define CECH_CR10       0x04
+#define CECH_CR9        0x02
+#define CECH_CR8        0x01
+
+/* EBCL (E Bit Error Counter)
+   ------------------- E1 & T1 ------------------------- */
+
+#define EBCL_EB7        0x80
+#define EBCL_EB6        0x40
+#define EBCL_EB5        0x20
+#define EBCL_EB4        0x10
+#define EBCL_EB3        0x08
+#define EBCL_EB2        0x04
+#define EBCL_EB1        0x02
+#define EBCL_EB0        0x01
+
+#define EBCH_EB15       0x80
+#define EBCH_EB14       0x40
+#define EBCH_EB13       0x20
+#define EBCH_EB12       0x10
+#define EBCH_EB11       0x08
+#define EBCH_EB10       0x04
+#define EBCH_EB9        0x02
+#define EBCH_EB8        0x01
+
+
+/* RSA4-8 (Receive Sa4-8-Bit Register)
+   -------------------- E1 --------------------------- */
+
+#define RSA4_RS47       0x80
+#define RSA4_RS46       0x40
+#define RSA4_RS45       0x20
+#define RSA4_RS44       0x10
+#define RSA4_RS43       0x08
+#define RSA4_RS42       0x04
+#define RSA4_RS41       0x02
+#define RSA4_RS40       0x01
+
+#define RSA5_RS57       0x80
+#define RSA5_RS56       0x40
+#define RSA5_RS55       0x20
+#define RSA5_RS54       0x10
+#define RSA5_RS53       0x08
+#define RSA5_RS52       0x04
+#define RSA5_RS51       0x02
+#define RSA5_RS50       0x01
+
+#define RSA6_RS67       0x80
+#define RSA6_RS66       0x40
+#define RSA6_RS65       0x20
+#define RSA6_RS64       0x10
+#define RSA6_RS63       0x08
+#define RSA6_RS62       0x04
+#define RSA6_RS61       0x02
+#define RSA6_RS60       0x01
+
+#define RSA7_RS77       0x80
+#define RSA7_RS76       0x40
+#define RSA7_RS75       0x20
+#define RSA7_RS74       0x10
+#define RSA7_RS73       0x08
+#define RSA7_RS72       0x04
+#define RSA7_RS71       0x02
+#define RSA7_RS70       0x01
+
+#define RSA8_RS87       0x80
+#define RSA8_RS86       0x40
+#define RSA8_RS85       0x20
+#define RSA8_RS84       0x10
+#define RSA8_RS83       0x08
+#define RSA8_RS82       0x04
+#define RSA8_RS81       0x02
+#define RSA8_RS80       0x01
+
+/* RSA6S (Receive Sa6 Bit Status Register)
+   ------------------------ T1 ------------------------- */
+
+#define RSA6S_SX        0x20
+#define RSA6S_SF        0x10
+#define RSA6S_SE        0x08
+#define RSA6S_SC        0x04
+#define RSA6S_SA        0x02
+#define RSA6S_S8        0x01
+
+
+/* RDL1-3 Receive DL-Bit Register1-3)
+   ------------------------ T1 ------------------------- */
+
+#define RDL1_RDL17      0x80
+#define RDL1_RDL16      0x40
+#define RDL1_RDL15      0x20
+#define RDL1_RDL14      0x10
+#define RDL1_RDL13      0x08
+#define RDL1_RDL12      0x04
+#define RDL1_RDL11      0x02
+#define RDL1_RDL10      0x01
+
+#define RDL2_RDL27      0x80
+#define RDL2_RDL26      0x40
+#define RDL2_RDL25      0x20
+#define RDL2_RDL24      0x10
+#define RDL2_RDL23      0x08
+#define RDL2_RDL22      0x04
+#define RDL2_RDL21      0x02
+#define RDL2_RDL20      0x01
+
+#define RDL3_RDL37      0x80
+#define RDL3_RDL36      0x40
+#define RDL3_RDL35      0x20
+#define RDL3_RDL34      0x10
+#define RDL3_RDL33      0x08
+#define RDL3_RDL32      0x04
+#define RDL3_RDL31      0x02
+#define RDL3_RDL30      0x01
+
+
+/* SIS (Signaling Status Register)
+
+   -------------------- E1 & T1 -------------------------- */
+
+#define SIS_XDOV        0x80
+#define SIS_XFW         0x40
+#define SIS_XREP        0x20
+#define SIS_RLI         0x08
+#define SIS_CEC         0x04
+#define SIS_BOM         0x01
+
+
+/* RSIS (Receive Signaling Status Register)
+
+   -------------------- E1 & T1 --------------------------- */
+
+#define RSIS_VFR        0x80
+#define RSIS_RDO        0x40
+#define RSIS_CRC16      0x20
+#define RSIS_RAB        0x10
+#define RSIS_HA1        0x08
+#define RSIS_HA0        0x04
+#define RSIS_HFR        0x02
+#define RSIS_LA         0x01
+
+
+/* RBCL/H (Receive Byte Count Low/High)
+
+   ------------------- E1 & T1 ----------------------- */
+
+#define RBCL_RBC7       0x80
+#define RBCL_RBC6       0x40
+#define RBCL_RBC5       0x20
+#define RBCL_RBC4       0x10
+#define RBCL_RBC3       0x08
+#define RBCL_RBC2       0x04
+#define RBCL_RBC1       0x02
+#define RBCL_RBC0       0x01
+
+#define RBCH_OV         0x10
+#define RBCH_RBC11      0x08
+#define RBCH_RBC10      0x04
+#define RBCH_RBC9       0x02
+#define RBCH_RBC8       0x01
+
+
+/* ISR1-3  (Interrupt Status Register 1-3)
+
+   ------------------ E1 & T1 ------------------------------ */
+
+#define  FISR0_RME     0x80
+#define  FISR0_RFS     0x40
+#define  FISR0_T8MS    0x20
+#define  FISR0_ISF     0x20
+#define  FISR0_RMB     0x10
+#define  FISR0_CASC    0x08
+#define  FISR0_RSC     0x08
+#define  FISR0_CRC6    0x04
+#define  FISR0_CRC4    0x04
+#define  FISR0_PDEN    0x02
+#define  FISR0_RPF     0x01
+
+#define  FISR1_CASE    0x80
+#define  FISR1_LLBSC   0x80
+#define  FISR1_RDO     0x40
+#define  FISR1_ALLS    0x20
+#define  FISR1_XDU     0x10
+#define  FISR1_XMB     0x08
+#define  FISR1_XLSC    0x02
+#define  FISR1_XPR     0x01
+
+#define  FISR2_FAR     0x80
+#define  FISR2_LFA     0x40
+#define  FISR2_MFAR    0x20
+#define  FISR2_T400MS  0x10
+#define  FISR2_LMFA    0x10
+#define  FISR2_AIS     0x08
+#define  FISR2_LOS     0x04
+#define  FISR2_RAR     0x02
+#define  FISR2_RA      0x01
+
+#define  FISR3_ES      0x80
+#define  FISR3_SEC     0x40
+#define  FISR3_LMFA16  0x20
+#define  FISR3_AIS16   0x10
+#define  FISR3_RA16    0x08
+#define  FISR3_API     0x04
+#define  FISR3_XSLP    0x20
+#define  FISR3_XSLN    0x10
+#define  FISR3_LLBSC   0x08
+#define  FISR3_XRS     0x04
+#define  FISR3_SLN     0x02
+#define  FISR3_SLP     0x01
+
+
+/* GIS  (Global Interrupt Status Register)
+
+   --------------------- E1 & T1 --------------------- */
+
+#define  GIS_ISR3      0x08
+#define  GIS_ISR2      0x04
+#define  GIS_ISR1      0x02
+#define  GIS_ISR0      0x01
+
+
+/* VSTR  (Version Status Register)
+
+   --------------------- E1 & T1 --------------------- */
+
+#define  VSTR_VN3      0x08
+#define  VSTR_VN2      0x04
+#define  VSTR_VN1      0x02
+#define  VSTR_VN0      0x01
+
+
+/*>>>>>>>>>>>>>>>>>>>>>  Local Control Structures  <<<<<<<<<<<<<<<<<<<<<<<<< */
+
+/* Write-only Registers (E1/T1 control mode write registers) */
+#define XFIFOH 0x00            /* Tx FIFO High Byte */
+#define XFIFOL 0x01            /* Tx FIFO Low Byte */
+#define CMDR   0x02            /* Command Reg */
+#define DEC    0x60            /* Disable Error Counter */
+#define TEST2  0x62            /* Manuf. Test Reg 2 */
+#define XS(nbr)        (0x70 + (nbr))  /* Tx CAS Reg (0 to 15) */
+
+/* Read-write Registers (E1/T1 status mode read registers) */
+#define MODE   0x03    /* Mode Reg */
+#define RAH1   0x04    /* Receive Address High 1 */
+#define RAH2   0x05    /* Receive Address High 2 */
+#define RAL1   0x06    /* Receive Address Low 1 */
+#define RAL2   0x07    /* Receive Address Low 2 */
+#define IPC    0x08    /* Interrupt Port Configuration */
+#define CCR1   0x09    /* Common Configuration Reg 1 */
+#define CCR3   0x0A    /* Common Configuration Reg 3 */
+#define PRE    0x0B    /* Preamble Reg */
+#define RTR1   0x0C    /* Receive Timeslot Reg 1 */
+#define RTR2   0x0D    /* Receive Timeslot Reg 2 */
+#define RTR3   0x0E    /* Receive Timeslot Reg 3 */
+#define RTR4   0x0F    /* Receive Timeslot Reg 4 */
+#define TTR1   0x10    /* Transmit Timeslot Reg 1 */
+#define TTR2   0x11    /* Transmit Timeslot Reg 2 */
+#define TTR3   0x12    /* Transmit Timeslot Reg 3 */
+#define TTR4   0x13    /* Transmit Timeslot Reg 4 */
+#define IMR0   0x14    /* Interrupt Mask Reg 0 */
+#define IMR1   0x15    /* Interrupt Mask Reg 1 */
+#define IMR2   0x16    /* Interrupt Mask Reg 2 */
+#define IMR3   0x17    /* Interrupt Mask Reg 3 */
+#define IMR4   0x18    /* Interrupt Mask Reg 4 */
+#define IMR5   0x19    /* Interrupt Mask Reg 5 */
+#define FMR0   0x1A    /* Framer Mode Reigster 0 */
+#define FMR1   0x1B    /* Framer Mode Reigster 1 */
+#define FMR2   0x1C    /* Framer Mode Reigster 2 */
+#define LOOP   0x1D    /* Channel Loop Back */
+#define XSW    0x1E    /* Transmit Service Word */
+#define FMR4   0x1E    /* Framer Mode Reg 4 */
+#define XSP    0x1F    /* Transmit Spare Bits */
+#define FMR5   0x1F    /* Framer Mode Reg 5 */
+#define XC0    0x20    /* Transmit Control 0 */
+#define XC1    0x21    /* Transmit Control 1 */
+#define RC0    0x22    /* Receive Control 0 */
+#define RC1    0x23    /* Receive Control 1 */
+#define XPM0   0x24    /* Transmit Pulse Mask 0 */
+#define XPM1   0x25    /* Transmit Pulse Mask 1 */
+#define XPM2   0x26    /* Transmit Pulse Mask 2 */
+#define TSWM   0x27    /* Transparent Service Word Mask */
+#define TEST1  0x28    /* Manuf. Test Reg 1 */
+#define IDLE   0x29    /* Idle Channel Code */
+#define XSA4    0x2A   /* Transmit SA4 Bit Reg */
+#define XDL1   0x2A    /* Transmit DL-Bit Reg 2 */
+#define XSA5    0x2B   /* Transmit SA4 Bit Reg */
+#define XDL2   0x2B    /* Transmit DL-Bit Reg 2 */
+#define XSA6    0x2C   /* Transmit SA4 Bit Reg */
+#define XDL3   0x2C    /* Transmit DL-Bit Reg 2 */
+#define XSA7    0x2D   /* Transmit SA4 Bit Reg */
+#define CCB1   0x2D    /* Clear Channel Reg 1 */
+#define XSA8    0x2E   /* Transmit SA4 Bit Reg */
+#define CCB2   0x2E    /* Clear Channel Reg 2 */
+#define FMR3   0x2F    /* Framer Mode Reg. 3 */
+#define CCB3   0x2F    /* Clear Channel Reg 3 */
+#define ICB1   0x30    /* Idle Channel Reg 1 */
+#define ICB2   0x31    /* Idle Channel Reg 2 */
+#define ICB3   0x32    /* Idle Channel Reg 3 */
+#define ICB4   0x33    /* Idle Channel Reg 4 */
+#define LIM0   0x34    /* Line Interface Mode 0 */
+#define LIM1   0x35    /* Line Interface Mode 1 */
+#define PCDR   0x36    /* Pulse Count Detection */
+#define PCRR   0x37    /* Pulse Count Recovery */
+#define LIM2   0x38    /* Line Interface Mode Reg 2 */
+#define LCR1   0x39    /* Loop Code Reg 1 */
+#define LCR2   0x3A    /* Loop Code Reg 2 */
+#define LCR3   0x3B    /* Loop Code Reg 3 */
+#define SIC1   0x3C    /* System Interface Control 1 */
+
+/* Read-only Registers (E1/T1 control mode read registers) */
+#define RFIFOH 0x00            /* Receive FIFO */
+#define RFIFOL 0x01            /* Receive FIFO */
+#define FRS0   0x4C            /* Framer Receive Status 0 */
+#define FRS1   0x4D            /* Framer Receive Status 1 */
+#define RSW    0x4E            /* Receive Service Word */
+#define FRS2   0x4E            /* Framer Receive Status 2 */
+#define RSP    0x4F            /* Receive Spare Bits */
+#define FRS3   0x4F            /* Framer Receive Status 3 */
+#define FECL   0x50            /* Framing Error Counter */
+#define FECH   0x51            /* Framing Error Counter */
+#define CVCL   0x52            /* Code Violation Counter */
+#define CVCH   0x53            /* Code Violation Counter */
+#define CECL   0x54            /* CRC Error Counter 1 */
+#define CECH   0x55            /* CRC Error Counter 1 */
+#define EBCL   0x56            /* E-Bit Error Counter */
+#define EBCH   0x57            /* E-Bit Error Counter */
+#define BECL   0x58            /* Bit Error Counter Low */
+#define BECH   0x59            /* Bit Error Counter Low */
+#define CEC3   0x5A            /* CRC Error Counter 3 (16-bit) */
+#define RSA4   0x5C            /* Receive SA4 Bit Reg */
+#define RDL1   0x5C            /* Receive DL-Bit Reg 1 */
+#define RSA5   0x5D            /* Receive SA5 Bit Reg */
+#define RDL2   0x5D            /* Receive DL-Bit Reg 2 */
+#define RSA6   0x5E            /* Receive SA6 Bit Reg */
+#define RDL3   0x5E            /* Receive DL-Bit Reg 3 */
+#define RSA7   0x5F            /* Receive SA7 Bit Reg */
+#define RSA8   0x60            /* Receive SA8 Bit Reg */
+#define RSA6S  0x61            /* Receive SA6 Bit Status Reg */
+#define TSR0   0x62            /* Manuf. Test Reg 0 */
+#define TSR1   0x63            /* Manuf. Test Reg 1 */
+#define SIS    0x64            /* Signaling Status Reg */
+#define RSIS   0x65            /* Receive Signaling Status Reg */
+#define RBCL   0x66            /* Receive Byte Control */
+#define RBCH   0x67            /* Receive Byte Control */
+#define FISR0  0x68            /* Interrupt Status Reg 0 */
+#define FISR1  0x69            /* Interrupt Status Reg 1 */
+#define FISR2  0x6A            /* Interrupt Status Reg 2 */
+#define FISR3  0x6B            /* Interrupt Status Reg 3 */
+#define GIS    0x6E            /* Global Interrupt Status */
+#define VSTR   0x6F            /* Version Status */
+#define RS(nbr)        (0x70 + (nbr))  /* Rx CAS Reg (0 to 15) */
+
+#endif /* _FALC_LH_H */
+
diff --git a/drivers/net/wan/pc300.h b/drivers/net/wan/pc300.h
new file mode 100644 (file)
index 0000000..6730577
--- /dev/null
@@ -0,0 +1,501 @@
+/*
+ * pc300.h     Cyclades-PC300(tm) Kernel API Definitions.
+ *
+ * Author:     Ivan Passos <ivan@cyclades.com>
+ *
+ * Copyright:  (c) 1999-2002 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.h,v $
+ * Revision 3.12  2002/03/07 14:17:09  henrique
+ * License data fixed
+ *
+ * Revision 3.11  2002/01/28 21:09:39  daniela
+ * Included ';' after pc300hw.bus.
+ *
+ * Revision 3.10  2002/01/17 17:58:52  ivan
+ * Support for PC300-TE/M (PMC).
+ *
+ * Revision 3.9  2001/09/28 13:30:53  daniela
+ * Renamed dma_start routine to rx_dma_start.
+ *
+ * Revision 3.8  2001/09/24 13:03:45  daniela
+ * Fixed BOF interrupt treatment. Created dma_start routine.
+ *
+ * Revision 3.7  2001/08/10 17:19:58  daniela
+ * Fixed IOCTLs defines.
+ *
+ * Revision 3.6  2001/07/18 19:24:42  daniela
+ * Included kernel version.
+ *
+ * Revision 3.5  2001/07/05 18:38:08  daniela
+ * DMA transmission bug fix.
+ *
+ * Revision 3.4  2001/06/26 17:10:40  daniela
+ * New configuration parameters (line code, CRC calculation and clock).
+ *
+ * Revision 3.3  2001/06/22 13:13:02  regina
+ * MLPPP implementation
+ *
+ * Revision 3.2  2001/06/18 17:56:09  daniela
+ * Increased DEF_MTU and TX_QUEUE_LEN.
+ *
+ * Revision 3.1  2001/06/15 12:41:10  regina
+ * upping major version number
+ *
+ * Revision 1.1.1.1  2001/06/13 20:25:06  daniela
+ * PC300 initial CVS version (3.4.0-pre1)
+ *
+ * Revision 2.3 2001/03/05 daniela
+ * Created struct pc300conf, to provide the hardware information to pc300util.
+ * Inclusion of 'alloc_ramsize' field on structure 'pc300hw'.
+ * 
+ * Revision 2.2 2000/12/22 daniela
+ * Structures and defines to support pc300util: statistics, status, 
+ * loopback tests, trace.
+ * 
+ * Revision 2.1 2000/09/28 ivan
+ * Inclusion of 'iophys' and 'iosize' fields on structure 'pc300hw', to 
+ * allow release of I/O region at module unload.
+ * Changed location of include files.
+ *
+ * Revision 2.0 2000/03/27 ivan
+ * Added support for the PC300/TE cards.
+ *
+ * Revision 1.1 2000/01/31 ivan
+ * Replaced 'pc300[drv|sca].h' former PC300 driver include files.
+ *
+ * Revision 1.0 1999/12/16 ivan
+ * First official release.
+ * Inclusion of 'nchan' field on structure 'pc300hw', to allow variable 
+ * number of ports per card.
+ * Inclusion of 'if_ptr' field on structure 'pc300dev'.
+ *
+ * Revision 0.6 1999/11/17 ivan
+ * Changed X.25-specific function names to comply with adopted convention.
+ *
+ * Revision 0.5 1999/11/16 Daniela Squassoni
+ * X.25 support.
+ *
+ * Revision 0.4 1999/11/15 ivan
+ * Inclusion of 'clock' field on structure 'pc300hw'.
+ *
+ * Revision 0.3 1999/11/10 ivan
+ * IOCTL name changing.
+ * Inclusion of driver function prototypes.
+ *
+ * Revision 0.2 1999/11/03 ivan
+ * Inclusion of 'tx_skb' and union 'ifu' on structure 'pc300dev'.
+ *
+ * Revision 0.1 1999/01/15 ivan
+ * Initial version.
+ *
+ */
+
+#ifndef        _PC300_H
+#define        _PC300_H
+
+#include <linux/hdlc.h>
+#include "hd64572.h"
+#include "pc300-falc-lh.h"
+
+#ifndef CY_TYPES
+#define CY_TYPES
+#if defined(__alpha__)
+typedef        unsigned long   ucdouble;       /* 64 bits, unsigned */
+typedef        unsigned int    uclong;         /* 32 bits, unsigned */
+#else
+typedef        unsigned long   uclong;         /* 32 bits, unsigned */
+#endif
+typedef        unsigned short  ucshort;        /* 16 bits, unsigned */
+typedef        unsigned char   ucchar;         /* 8 bits, unsigned */
+#endif /* CY_TYPES */
+
+#define PC300_PROTO_MLPPP 1            
+
+#define PC300_KERNEL   "2.4.x" /* Kernel supported by this driver */
+
+#define        PC300_DEVNAME   "hdlc"  /* Dev. name base (for hdlc0, hdlc1, etc.) */
+#define PC300_MAXINDEX 100     /* Max dev. name index (the '0' in hdlc0) */
+
+#define        PC300_MAXCARDS  4       /* Max number of cards per system */
+#define        PC300_MAXCHAN   2       /* Number of channels per card */
+
+#define        PC300_PLX_WIN   0x80    /* PLX control window size (128b) */
+#define        PC300_RAMSIZE   0x40000 /* RAM window size (256Kb) */
+#define        PC300_SCASIZE   0x400   /* SCA window size (1Kb) */
+#define        PC300_FALCSIZE  0x400   /* FALC window size (1Kb) */
+
+#define PC300_OSC_CLOCK        24576000
+#define PC300_PCI_CLOCK        33000000
+
+#define BD_DEF_LEN     0x0800  /* DMA buffer length (2KB) */
+#define DMA_TX_MEMSZ   0x8000  /* Total DMA Tx memory size (32KB/ch) */
+#define DMA_RX_MEMSZ   0x10000 /* Total DMA Rx memory size (64KB/ch) */
+
+#define N_DMA_TX_BUF   (DMA_TX_MEMSZ / BD_DEF_LEN)     /* DMA Tx buffers */
+#define N_DMA_RX_BUF   (DMA_RX_MEMSZ / BD_DEF_LEN)     /* DMA Rx buffers */
+
+/* DMA Buffer Offsets */
+#define DMA_TX_BASE    ((N_DMA_TX_BUF + N_DMA_RX_BUF) *        \
+                        PC300_MAXCHAN * sizeof(pcsca_bd_t))
+#define DMA_RX_BASE    (DMA_TX_BASE + PC300_MAXCHAN*DMA_TX_MEMSZ)
+
+/* DMA Descriptor Offsets */
+#define DMA_TX_BD_BASE 0x0000
+#define DMA_RX_BD_BASE (DMA_TX_BD_BASE + ((PC300_MAXCHAN*DMA_TX_MEMSZ / \
+                               BD_DEF_LEN) * sizeof(pcsca_bd_t)))
+
+/* DMA Descriptor Macros */
+#define TX_BD_ADDR(chan, n)    (DMA_TX_BD_BASE + \
+                                ((N_DMA_TX_BUF*chan) + n) * sizeof(pcsca_bd_t))
+#define RX_BD_ADDR(chan, n)    (DMA_RX_BD_BASE + \
+                                ((N_DMA_RX_BUF*chan) + n) * sizeof(pcsca_bd_t))
+
+/* Macro to access the FALC registers (TE only) */
+#define F_REG(reg, chan)       (0x200*(chan) + ((reg)<<2))
+
+/***************************************
+ * Memory access functions/macros      *
+ * (required to support Alpha systems) *
+ ***************************************/
+#ifdef __KERNEL__
+#define cpc_writeb(port,val)   {writeb((ucchar)(val),(ulong)(port)); mb();}
+#define cpc_writew(port,val)   {writew((ushort)(val),(ulong)(port)); mb();}
+#define cpc_writel(port,val)   {writel((uclong)(val),(ulong)(port)); mb();}
+
+#define cpc_readb(port)                readb(port)
+#define cpc_readw(port)                readw(port)
+#define cpc_readl(port)                readl(port)
+
+#else /* __KERNEL__ */
+#define cpc_writeb(port,val)   (*(volatile ucchar *)(port) = (ucchar)(val))
+#define cpc_writew(port,val)   (*(volatile ucshort *)(port) = (ucshort)(val))
+#define cpc_writel(port,val)   (*(volatile uclong *)(port) = (uclong)(val))
+
+#define cpc_readb(port)                (*(volatile ucchar *)(port))
+#define cpc_readw(port)                (*(volatile ucshort *)(port))
+#define cpc_readl(port)                (*(volatile uclong *)(port))
+
+#endif /* __KERNEL__ */
+
+/****** Data Structures *****************************************************/
+
+/*
+ *      RUNTIME_9050 - PLX PCI9050-1 local configuration and shared runtime
+ *      registers. This structure can be used to access the 9050 registers
+ *      (memory mapped).
+ */
+struct RUNTIME_9050 {
+       uclong  loc_addr_range[4];      /* 00-0Ch : Local Address Ranges */
+       uclong  loc_rom_range;          /* 10h : Local ROM Range */
+       uclong  loc_addr_base[4];       /* 14-20h : Local Address Base Addrs */
+       uclong  loc_rom_base;           /* 24h : Local ROM Base */
+       uclong  loc_bus_descr[4];       /* 28-34h : Local Bus Descriptors */
+       uclong  rom_bus_descr;          /* 38h : ROM Bus Descriptor */
+       uclong  cs_base[4];             /* 3C-48h : Chip Select Base Addrs */
+       uclong  intr_ctrl_stat;         /* 4Ch : Interrupt Control/Status */
+       uclong  init_ctrl;              /* 50h : EEPROM ctrl, Init Ctrl, etc */
+};
+
+#define PLX_9050_LINT1_ENABLE  0x01
+#define PLX_9050_LINT1_POL     0x02
+#define PLX_9050_LINT1_STATUS  0x04
+#define PLX_9050_LINT2_ENABLE  0x08
+#define PLX_9050_LINT2_POL     0x10
+#define PLX_9050_LINT2_STATUS  0x20
+#define PLX_9050_INTR_ENABLE   0x40
+#define PLX_9050_SW_INTR       0x80
+
+/* Masks to access the init_ctrl PLX register */
+#define        PC300_CLKSEL_MASK               (0x00000004UL)
+#define        PC300_CHMEDIA_MASK(chan)        (0x00000020UL<<(chan*3))
+#define        PC300_CTYPE_MASK                (0x00000800UL)
+
+/* CPLD Registers (base addr = falcbase, TE only) */
+/* CPLD v. 0 */
+#define CPLD_REG1      0x140   /* Chip resets, DCD/CTS status */
+#define CPLD_REG2      0x144   /* Clock enable , LED control */
+/* CPLD v. 2 or higher */
+#define CPLD_V2_REG1   0x100   /* Chip resets, DCD/CTS status */
+#define CPLD_V2_REG2   0x104   /* Clock enable , LED control */
+#define CPLD_ID_REG    0x108   /* CPLD version */
+
+/* CPLD Register bit description: for the FALC bits, they should always be 
+   set based on the channel (use (bit<<(2*ch)) to access the correct bit for 
+   that channel) */
+#define CPLD_REG1_FALC_RESET   0x01
+#define CPLD_REG1_SCA_RESET    0x02
+#define CPLD_REG1_GLOBAL_CLK   0x08
+#define CPLD_REG1_FALC_DCD     0x10
+#define CPLD_REG1_FALC_CTS     0x20
+
+#define CPLD_REG2_FALC_TX_CLK  0x01
+#define CPLD_REG2_FALC_RX_CLK  0x02
+#define CPLD_REG2_FALC_LED1    0x10
+#define CPLD_REG2_FALC_LED2    0x20
+
+/* Structure with FALC-related fields (TE only) */
+#define PC300_FALC_MAXLOOP     0x0000ffff      /* for falc_issue_cmd() */
+
+typedef struct falc {
+       ucchar sync;            /* If true FALC is synchronized */
+       ucchar active;          /* if TRUE then already active */
+       ucchar loop_active;     /* if TRUE a line loopback UP was received */
+       ucchar loop_gen;        /* if TRUE a line loopback UP was issued */
+
+       ucchar num_channels;
+       ucchar offset;          /* 1 for T1, 0 for E1 */
+       ucchar full_bandwidth;
+
+       ucchar xmb_cause;
+       ucchar multiframe_mode;
+
+       /* Statistics */
+       ucshort pden;   /* Pulse Density violation count */
+       ucshort los;    /* Loss of Signal count */
+       ucshort losr;   /* Loss of Signal recovery count */
+       ucshort lfa;    /* Loss of frame alignment count */
+       ucshort farec;  /* Frame Alignment Recovery count */
+       ucshort lmfa;   /* Loss of multiframe alignment count */
+       ucshort ais;    /* Remote Alarm indication Signal count */
+       ucshort sec;    /* One-second timer */
+       ucshort es;     /* Errored second */
+       ucshort rai;    /* remote alarm received */
+       ucshort bec;
+       ucshort fec;
+       ucshort cvc;
+       ucshort cec;
+       ucshort ebc;
+
+       /* Status */
+       ucchar red_alarm;
+       ucchar blue_alarm;
+       ucchar loss_fa;
+       ucchar yellow_alarm;
+       ucchar loss_mfa;
+       ucchar prbs;
+} falc_t;
+
+typedef struct falc_status {
+       ucchar sync;  /* If true FALC is synchronized */
+       ucchar red_alarm;
+       ucchar blue_alarm;
+       ucchar loss_fa;
+       ucchar yellow_alarm;
+       ucchar loss_mfa;
+       ucchar prbs;
+} falc_status_t;
+
+typedef struct rsv_x21_status {
+       ucchar dcd;
+       ucchar dsr;
+       ucchar cts;
+       ucchar rts;
+       ucchar dtr;
+} rsv_x21_status_t;
+
+typedef struct pc300stats {
+       int hw_type;
+       uclong line_on;
+       uclong line_off;
+       struct net_device_stats gen_stats;
+       falc_t te_stats;
+} pc300stats_t;
+
+typedef struct pc300status {
+       int hw_type;
+       rsv_x21_status_t gen_status;
+       falc_status_t te_status;
+} pc300status_t;
+
+typedef struct pc300loopback {
+       char loop_type;
+       char loop_on;
+} pc300loopback_t;
+
+typedef struct pc300patterntst {
+       char patrntst_on;       /* 0 - off; 1 - on; 2 - read num_errors */
+       ucshort num_errors;
+} pc300patterntst_t;
+
+typedef struct pc300dev {
+       void *if_ptr;           /* General purpose pointer */
+       struct pc300ch *chan;
+       ucchar trace_on;
+       uclong line_on;         /* DCD(X.21, RSV) / sync(TE) change counters */
+       uclong line_off;
+#ifdef __KERNEL__
+       char name[16];
+       hdlc_device *hdlc;
+
+       void *private;
+       struct sk_buff *tx_skb;
+       union { /* This union has all the protocol-specific structures */
+               struct ppp_device pppdev;
+       }ifu;
+#ifdef CONFIG_PC300_MLPPP
+       void *cpc_tty;  /* information to PC300 TTY driver */
+#endif
+#endif /* __KERNEL__ */
+}pc300dev_t;
+
+typedef struct pc300hw {
+       int type;               /* RSV, X21, etc. */
+       int bus;                /* Bus (PCI, PMC, etc.) */
+       int nchan;              /* number of channels */
+       int irq;                /* interrupt request level */
+       uclong clock;           /* Board clock */
+       ucchar cpld_id;         /* CPLD ID (TE only) */
+       ucshort cpld_reg1;      /* CPLD reg 1 (TE only) */
+       ucshort cpld_reg2;      /* CPLD reg 2 (TE only) */
+       ucshort gpioc_reg;      /* PLX GPIOC reg */
+       ucshort intctl_reg;     /* PLX Int Ctrl/Status reg */
+       uclong iophys;          /* PLX registers I/O base */
+       uclong iosize;          /* PLX registers I/O size */
+       uclong plxphys;         /* PLX registers MMIO base (physical) */
+       uclong plxbase;         /* PLX registers MMIO base (virtual) */
+       uclong plxsize;         /* PLX registers MMIO size */
+       uclong scaphys;         /* SCA registers MMIO base (physical) */
+       uclong scabase;         /* SCA registers MMIO base (virtual) */
+       uclong scasize;         /* SCA registers MMIO size */
+       uclong ramphys;         /* On-board RAM MMIO base (physical) */
+       uclong rambase;         /* On-board RAM MMIO base (virtual) */
+       uclong alloc_ramsize;   /* RAM MMIO size allocated by the PCI bridge */
+       uclong ramsize;         /* On-board RAM MMIO size */
+       uclong falcphys;        /* FALC registers MMIO base (physical) */
+       uclong falcbase;        /* FALC registers MMIO base (virtual) */
+       uclong falcsize;        /* FALC registers MMIO size */
+} pc300hw_t;
+
+typedef struct pc300chconf {
+       sync_serial_settings    phys_settings;  /* Clock type/rate (in bps), 
+                                                  loopback mode */
+       raw_hdlc_proto          proto_settings; /* Encoding, parity (CRC) */
+       uclong media;           /* HW media (RS232, V.35, etc.) */
+       uclong proto;           /* Protocol (PPP, X.25, etc.) */
+       ucchar monitor;         /* Monitor mode (0 = off, !0 = on) */
+
+       /* TE-specific parameters */
+       ucchar lcode;           /* Line Code (AMI, B8ZS, etc.) */
+       ucchar fr_mode;         /* Frame Mode (ESF, D4, etc.) */
+       ucchar lbo;             /* Line Build Out */
+       ucchar rx_sens;         /* Rx Sensitivity (long- or short-haul) */
+       uclong tslot_bitmap;    /* bit[i]=1  =>  timeslot _i_ is active */
+} pc300chconf_t;
+
+typedef struct pc300ch {
+       struct pc300 *card;
+       int channel;
+       pc300dev_t d;
+       pc300chconf_t conf;
+       ucchar tx_first_bd;     /* First TX DMA block descr. w/ data */
+       ucchar tx_next_bd;      /* Next free TX DMA block descriptor */
+       ucchar rx_first_bd;     /* First free RX DMA block descriptor */
+       ucchar rx_last_bd;      /* Last free RX DMA block descriptor */
+       ucchar nfree_tx_bd;     /* Number of free TX DMA block descriptors */
+       falc_t falc;            /* FALC structure (TE only) */
+} pc300ch_t;
+
+typedef struct pc300 {
+       pc300hw_t hw;                   /* hardware config. */
+       pc300ch_t chan[PC300_MAXCHAN];
+#ifdef __KERNEL__
+       spinlock_t card_lock;
+#endif /* __KERNEL__ */
+} pc300_t;
+
+typedef struct pc300conf {
+       pc300hw_t hw;
+       pc300chconf_t conf;
+} pc300conf_t;
+
+/* DEV ioctl() commands */
+#define        N_SPPP_IOCTLS   2
+
+enum pc300_ioctl_cmds {
+       SIOCCPCRESERVED = (SIOCDEVPRIVATE + N_SPPP_IOCTLS),
+       SIOCGPC300CONF,
+       SIOCSPC300CONF,
+       SIOCGPC300STATUS,
+       SIOCGPC300FALCSTATUS,
+       SIOCGPC300UTILSTATS,
+       SIOCGPC300UTILSTATUS,
+       SIOCSPC300TRACE,
+       SIOCSPC300LOOPBACK,
+       SIOCSPC300PATTERNTEST,
+};
+
+/* Loopback types - PC300/TE boards */
+enum pc300_loopback_cmds {
+       PC300LOCLOOP = 1,
+       PC300REMLOOP,
+       PC300PAYLOADLOOP,
+       PC300GENLOOPUP,
+       PC300GENLOOPDOWN,
+};
+
+/* Control Constant Definitions */
+#define        PC300_RSV       0x01
+#define        PC300_X21       0x02
+#define        PC300_TE        0x03
+
+#define        PC300_PCI       0x00
+#define        PC300_PMC       0x01
+
+#define PC300_LC_AMI   0x01
+#define PC300_LC_B8ZS  0x02
+#define PC300_LC_NRZ   0x03
+#define PC300_LC_HDB3  0x04
+
+/* Framing (T1) */
+#define PC300_FR_ESF           0x01
+#define PC300_FR_D4            0x02
+#define PC300_FR_ESF_JAPAN     0x03
+
+/* Framing (E1) */
+#define PC300_FR_MF_CRC4       0x04
+#define PC300_FR_MF_NON_CRC4   0x05
+#define PC300_FR_UNFRAMED      0x06
+
+#define PC300_LBO_0_DB         0x00
+#define PC300_LBO_7_5_DB       0x01
+#define PC300_LBO_15_DB                0x02
+#define PC300_LBO_22_5_DB      0x03
+
+#define PC300_RX_SENS_SH       0x01
+#define PC300_RX_SENS_LH       0x02
+
+#define PC300_TX_TIMEOUT       (2*HZ)
+#define PC300_TX_QUEUE_LEN     100
+#define        PC300_DEF_MTU           1600
+
+#ifdef __KERNEL__
+/* Function Prototypes */
+int dma_buf_write(pc300_t *, int, ucchar *, int);
+int dma_buf_read(pc300_t *, int, struct sk_buff *);
+void tx_dma_start(pc300_t *, int);
+void rx_dma_start(pc300_t *, int);
+void tx_dma_stop(pc300_t *, int);
+void rx_dma_stop(pc300_t *, int);
+int cpc_queue_xmit(struct sk_buff *, struct net_device *);
+void cpc_net_rx(hdlc_device *);
+void cpc_sca_status(pc300_t *, int);
+int cpc_change_mtu(struct net_device *, int);
+int cpc_ioctl(struct net_device *, struct ifreq *, int);
+int ch_config(pc300dev_t *);
+int rx_config(pc300dev_t *);
+int tx_config(pc300dev_t *);
+void cpc_opench(pc300dev_t *);
+void cpc_closech(pc300dev_t *);
+int cpc_open(struct net_device *dev);
+int cpc_close(struct net_device *dev);
+int cpc_set_media(hdlc_device *, int);
+#endif /* __KERNEL__ */
+
+#endif /* _PC300_H */
+
diff --git a/drivers/net/wan/pc300_drv.c b/drivers/net/wan/pc300_drv.c
new file mode 100644 (file)
index 0000000..75aa2f6
--- /dev/null
@@ -0,0 +1,3683 @@
+#define        USE_PCI_CLOCK
+static char rcsid[] = 
+"Revision: 3.4.5 Date: 2002/03/07 ";
+
+/*
+ * pc300.c     Cyclades-PC300(tm) Driver.
+ *
+ * Author:     Ivan Passos <ivan@cyclades.com>
+ * Maintainer: Daniela Squassoni <daniela@cyclades.com>
+ *
+ * Copyright:  (c) 1999-2002 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.
+ *     
+ *     Using tabstop = 4.
+ * 
+ * $Log: pc300_drv.c,v $
+ * Revision 3.23  2002/03/20 13:58:40  henrique
+ * Fixed ortographic mistakes
+ *
+ * Revision 3.22  2002/03/13 16:56:56  henrique
+ * Take out the debug messages
+ *
+ * Revision 3.21  2002/03/07 14:17:09  henrique
+ * License data fixed
+ *
+ * Revision 3.20  2002/01/17 17:58:52  ivan
+ * Support for PC300-TE/M (PMC).
+ *
+ * Revision 3.19  2002/01/03 17:08:47  daniela
+ * Enables DMA reception when the SCA-II disables it improperly.
+ *
+ * Revision 3.18  2001/12/03 18:47:50  daniela
+ * Esthetic changes.
+ *
+ * Revision 3.17  2001/10/19 16:50:13  henrique
+ * Patch to kernel 2.4.12 and new generic hdlc.
+ *
+ * Revision 3.16  2001/10/16 15:12:31  regina
+ * clear statistics
+ *
+ * Revision 3.11 to 3.15  2001/10/11 20:26:04  daniela
+ * More DMA fixes for noisy lines.
+ * Return the size of bad frames in dma_get_rx_frame_size, so that the Rx buffer
+ * descriptors can be cleaned by dma_buf_read (called in cpc_net_rx).
+ * Renamed dma_start routine to rx_dma_start. Improved Rx statistics.
+ * Fixed BOF interrupt treatment. Created dma_start routine.
+ * Changed min and max to cpc_min and cpc_max.
+ *
+ * Revision 3.10  2001/08/06 12:01:51  regina
+ * Fixed problem in DSR_DE bit.
+ *
+ * Revision 3.9  2001/07/18 19:27:26  daniela
+ * Added some history comments.
+ *
+ * Revision 3.8  2001/07/12 13:11:19  regina
+ * bug fix - DCD-OFF in pc300 tty driver
+ *
+ * Revision 3.3 to 3.7  2001/07/06 15:00:20  daniela
+ * Removing kernel 2.4.3 and previous support.
+ * DMA transmission bug fix.
+ * MTU check in cpc_net_rx fixed.
+ * Boot messages reviewed.
+ * New configuration parameters (line code, CRC calculation and clock).
+ *
+ * Revision 3.2 2001/06/22 13:13:02  regina
+ * MLPPP implementation. Changed the header of message trace to include
+ * the device name. New format : "hdlcX[R/T]: ".
+ * Default configuration changed.
+ *
+ * Revision 3.1 2001/06/15 regina
+ * in cpc_queue_xmit, netif_stop_queue is called if don't have free descriptor
+ * upping major version number
+ *
+ * Revision 1.1.1.1  2001/06/13 20:25:04  daniela
+ * PC300 initial CVS version (3.4.0-pre1)
+ *
+ * Revision 3.0.1.2 2001/06/08 daniela
+ * Did some changes in the DMA programming implementation to avoid the 
+ * occurrence of a SCA-II bug when CDA is accessed during a DMA transfer.
+ *
+ * Revision 3.0.1.1 2001/05/02 daniela
+ * Added kernel 2.4.3 support.
+ * 
+ * Revision 3.0.1.0 2001/03/13 daniela, henrique
+ * Added Frame Relay Support.
+ * Driver now uses HDLC generic driver to provide protocol support.
+ * 
+ * Revision 3.0.0.8 2001/03/02 daniela
+ * Fixed ram size detection. 
+ * Changed SIOCGPC300CONF ioctl, to give hw information to pc300util.
+ * 
+ * Revision 3.0.0.7 2001/02/23 daniela
+ * netif_stop_queue called before the SCA-II transmition commands in 
+ * cpc_queue_xmit, and with interrupts disabled to avoid race conditions with 
+ * transmition interrupts.
+ * Fixed falc_check_status for Unframed E1.
+ * 
+ * Revision 3.0.0.6 2000/12/13 daniela
+ * Implemented pc300util support: trace, statistics, status and loopback
+ * tests for the PC300 TE boards.
+ * 
+ * Revision 3.0.0.5 2000/12/12 ivan
+ * Added support for Unframed E1.
+ * Implemented monitor mode.
+ * Fixed DCD sensitivity on the second channel.
+ * Driver now complies with new PCI kernel architecture.
+ *
+ * Revision 3.0.0.4 2000/09/28 ivan
+ * Implemented DCD sensitivity.
+ * Moved hardware-specific open to the end of cpc_open, to avoid race
+ * conditions with early reception interrupts.
+ * Included code for [request|release]_mem_region().
+ * Changed location of pc300.h .
+ * Minor code revision (contrib. of Jeff Garzik).
+ *
+ * Revision 3.0.0.3 2000/07/03 ivan
+ * Previous bugfix for the framing errors with external clock made X21
+ * boards stop working. This version fixes it.
+ *
+ * Revision 3.0.0.2 2000/06/23 ivan
+ * Revisited cpc_queue_xmit to prevent race conditions on Tx DMA buffer
+ * handling when Tx timeouts occur.
+ * Revisited Rx statistics.
+ * Fixed a bug in the SCA-II programming that would cause framing errors
+ * when external clock was configured.
+ *
+ * Revision 3.0.0.1 2000/05/26 ivan
+ * 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 net_device structures.
+ * Code is now compliant with the new module interface (module_[init|exit]).
+ * Make use of the PCI helper functions to access PCI resources.
+ *
+ * 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.
+ * Softnet compliancy implemented.
+ * 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
+ * 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 <linux/spinlock.h>
+#include <linux/if.h>
+#include <linux/version.h>
+#include <net/arp.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <net/syncppp.h>
+
+#include "pc300.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
+#undef PC300_DEBUG_OTHER
+
+static struct pci_device_id cpc_pci_dev_id[] __devinitdata = {
+       /* PC300/RSV or PC300/X21, 2 chan */
+       {0x120e, 0x300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x300},
+       /* PC300/RSV or PC300/X21, 1 chan */
+       {0x120e, 0x301, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x301},
+       /* PC300/TE, 2 chan */
+       {0x120e, 0x310, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x310},
+       /* PC300/TE, 1 chan */
+       {0x120e, 0x311, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x311},
+       /* PC300/TE-M, 2 chan */
+       {0x120e, 0x320, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x320},
+       /* PC300/TE-M, 1 chan */
+       {0x120e, 0x321, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0x321},
+       /* End of table */
+       {0,},
+};
+MODULE_DEVICE_TABLE(pci, cpc_pci_dev_id);
+
+#ifndef cpc_min
+#define        cpc_min(a,b)    (((a)<(b))?(a):(b))
+#endif
+#ifndef cpc_max
+#define        cpc_max(a,b)    (((a)>(b))?(a):(b))
+#endif
+
+/* prototypes */
+static void tx_dma_buf_pt_init(pc300_t *, int);
+static void tx_dma_buf_init(pc300_t *, int);
+static void rx_dma_buf_pt_init(pc300_t *, int);
+static void rx_dma_buf_init(pc300_t *, int);
+static void tx_dma_buf_check(pc300_t *, int);
+static void rx_dma_buf_check(pc300_t *, int);
+static void cpc_intr(int, void *, struct pt_regs *);
+static struct net_device_stats *cpc_get_stats(struct net_device *);
+static int clock_rate_calc(uclong, uclong, int *);
+static uclong detect_ram(pc300_t *);
+static void plx_init(pc300_t *);
+static void cpc_trace(struct net_device *, struct sk_buff *, char);
+static int cpc_attach(hdlc_device *, unsigned short, unsigned short);
+
+#ifdef CONFIG_PC300_MLPPP
+void cpc_tty_init(pc300dev_t * dev);
+void cpc_tty_unregister_service(pc300dev_t * pc300dev);
+void cpc_tty_receive(pc300dev_t * pc300dev);
+void cpc_tty_trigger_poll(pc300dev_t * pc300dev);
+void cpc_tty_reset_var(void);
+#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, DST_OSB);
+       }
+       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");
+}
+
+#ifdef PC300_DEBUG_OTHER
+/* Show all TX buffer descriptors */
+static void tx1_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;
+       uclong scabase = card->hw.scabase;
+
+       printk ("\nnfree_tx_bd = %d \n", card->chan[ch].nfree_tx_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));
+       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)));
+       for (i = 0; i < N_DMA_TX_BUF; i++) {
+               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");
+}
+#endif
+                        
+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) || (first_bd == card->chan[ch].rx_last_bd)) {
+                       /* Return the size of a good frame or incomplete bad frame 
+                       * (dma_buf_read will clean the buffer descriptors in this case). */
+                       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;
+
+       if (nbuf >= card->chan[ch].nfree_tx_bd) {
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < nbuf; i++) {
+               ptdescr = (pcsca_bd_t *) (card->hw.rambase +
+                                         TX_BD_ADDR(ch, card->chan[ch].tx_next_bd));
+               nchar = cpc_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);
+                       cpc_writew(&ptdescr->len, nchar);
+                       card->chan[ch].nfree_tx_bd--;
+                       if ((i + 1) == nbuf) {
+                               /* This must be the last BD to be used */
+                               cpc_writeb(&ptdescr->status, DST_EOM);
+                       } else {
+                               cpc_writeb(&ptdescr->status, 0);
+                       }
+               } 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) {
+               nchar = cpc_readw(&ptdescr->len);
+               if ((status & (DST_OVR | DST_CRC | DST_RBIT | DST_SHRT | DST_ABT))
+                   || (nchar > BD_DEF_LEN)) {
+
+                       if (nchar > BD_DEF_LEN)
+                               status |= DST_RBIT;
+                       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 != 0) {
+                       if (skb) {
+                               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);
+}
+
+void rx_dma_start(pc300_t * card, int ch)
+{
+       uclong scabase = card->hw.scabase;
+       pc300ch_t *chan = (pc300ch_t *) & card->chan[ch];
+       
+       /* Start DMA */
+       cpc_writel(scabase + DRX_REG(CDAL, ch),
+                  RX_BD_ADDR(ch, chan->rx_first_bd));
+       if (cpc_readl(scabase + DRX_REG(CDAL,ch)) !=
+                                 RX_BD_ADDR(ch, chan->rx_first_bd)) {
+               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 (!(cpc_readb(scabase + DSR_RX(ch)) & DSR_DE)) {
+       cpc_writeb(scabase + DSR_RX(ch), DSR_DE);
+       }
+}
+
+/*************************/
+/***   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));
+       if (conf->fr_mode == PC300_FR_UNFRAMED) {
+               cpc_writeb(falcbase + F_REG(IMR4, ch),
+                          cpc_readb(falcbase + F_REG(IMR4, ch)) & ~(IMR4_LOS));
+       } else {
+               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 == IF_IFACE_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);
+               if (conf->fr_mode == PC300_FR_UNFRAMED) {
+                       cpc_writeb(falcbase + F_REG(IMR2, ch),
+                                  cpc_readb(falcbase + F_REG(IMR2, ch)) & ~(IMR2_LOS));
+               } else {
+                       cpc_writeb(falcbase + F_REG(IMR2, ch),
+                                  cpc_readb(falcbase + F_REG(IMR2, ch)) &
+                                  ~(IMR2_FAR | IMR2_LFA | IMR2_AIS | IMR2_LOS));
+                       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 == IF_IFACE_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;
+
+       cpc_writeb(falcbase + F_REG(ICB1, ch), 0);
+       if (conf->fr_mode == PC300_FR_UNFRAMED) {
+               cpc_writeb(falcbase + F_REG(TTR1, ch), 0xff);
+               cpc_writeb(falcbase + F_REG(RTR1, ch), 0xff);
+       } else {
+               /* Timeslot 0 is never enabled */
+               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 == IF_IFACE_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 + card->hw.cpld_reg1,
+                  cpc_readb(card->hw.falcbase + card->hw.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 + card->hw.cpld_reg1,
+                  cpc_readb(card->hw.falcbase + card->hw.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->phys_settings.clock_type == CLOCK_INT) { /* 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->phys_settings.clock_type == CLOCK_INT) { /* 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_SFM);
+
+       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);
+
+                       /* 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);
+
+                       /* Transmit Spare Bits for National Use (Y, Sn, Sa) */
+                       cpc_writeb(falcbase + F_REG(XSW, ch),
+                                  cpc_readb(falcbase + F_REG(XSW, ch)) |
+                                  XSW_XY0 | XSW_XY1 | XSW_XY2 | XSW_XY3 | XSW_XY4);
+                       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 | 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);
+
+                       /* 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);
+
+                       /* Transmit Spare Bits for National Use (Y, Sn, Sa) */
+                       cpc_writeb(falcbase + F_REG(XSW, ch),
+                                  cpc_readb(falcbase + F_REG(XSW, ch)) |
+                                  XSW_XY0 | XSW_XY1 | XSW_XY2 | XSW_XY3 | XSW_XY4);
+                       break;
+
+               case PC300_FR_UNFRAMED:
+                       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 | FMR2_RFS0));
+                       cpc_writeb(falcbase + F_REG(XSP, ch),
+                                  cpc_readb(falcbase + F_REG(XSP, ch)) | XSP_TT0);
+                       cpc_writeb(falcbase + F_REG(XSW, ch),
+                                  cpc_readb(falcbase + F_REG(XSW, ch)) & 
+                                  ~(XSW_XTM|XSW_XY0|XSW_XY1|XSW_XY2|XSW_XY3|XSW_XY4));
+                       cpc_writeb(falcbase + F_REG(TSWM, ch), 0xff);
+                       cpc_writeb(falcbase + F_REG(FMR2, ch),
+                                  cpc_readb(falcbase + F_REG(FMR2, ch)) |
+                                  (FMR2_RTM | FMR2_DAIS));
+                       cpc_writeb(falcbase + F_REG(FMR2, ch),
+                                  cpc_readb(falcbase + F_REG(FMR2, ch)) & ~FMR2_AXRA);
+                       cpc_writeb(falcbase + F_REG(FMR1, ch),
+                                  cpc_readb(falcbase + F_REG(FMR1, ch)) & ~FMR1_AFR);
+                       pfalc->sync = 1;
+                       cpc_writeb(falcbase + card->hw.cpld_reg2,
+                                  cpc_readb(falcbase + card->hw.cpld_reg2) |
+                                  (CPLD_REG2_FALC_LED2 << (2 * ch)));
+                       break;
+       }
+
+       /* 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);
+
+       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;
+       pc300ch_t *chan = (pc300ch_t *) & card->chan[ch];
+       pc300chconf_t *conf = (pc300chconf_t *) & chan->conf;
+
+       /* Enable transparent data transfer */
+       if (conf->fr_mode == PC300_FR_UNFRAMED) {
+               cpc_writeb(falcbase + F_REG(MODE, ch), 0);
+       } else {
+               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 IF_IFACE_T1:
+                       pfalc->num_channels = NUM_OF_T1_CHANNELS;
+                       pfalc->offset = 1;
+                       break;
+               case IF_IFACE_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 + card->hw.cpld_reg1,
+                  cpc_readb(card->hw.falcbase + card->hw.cpld_reg1) |
+                  (CPLD_REG1_FALC_RESET << (2 * ch)));
+       udelay(10000);
+       cpc_writeb(card->hw.falcbase + card->hw.cpld_reg1,
+                  cpc_readb(card->hw.falcbase + card->hw.cpld_reg1) &
+                  ~(CPLD_REG1_FALC_RESET << (2 * ch)));
+
+       if (conf->media == IF_IFACE_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 + card->hw.cpld_reg2,
+                  cpc_readb(card->hw.falcbase + card->hw.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 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 == IF_IFACE_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++;
+               }
+       }
+
+       if (conf->fr_mode != PC300_FR_UNFRAMED) {
+               /* Verify AIS alarm */
+               if (frs0 & FRS0_AIS) {
+                       if (!pfalc->blue_alarm) {
+                               pfalc->blue_alarm = 1;
+                               pfalc->ais++;
+                               // EVENT_AIS
+                               if (conf->media == IF_IFACE_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 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 == IF_IFACE_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 == IF_IFACE_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;
+               }
+
+               /* 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 !PC300_UNFRAMED */
+
+       if (pfalc->red_alarm || pfalc->loss_fa ||
+           pfalc->loss_mfa || pfalc->blue_alarm) {
+               if (pfalc->sync) {
+                       pfalc->sync = 0;
+                       chan->d.line_off++;
+                       cpc_writeb(falcbase + card->hw.cpld_reg2,
+                                  cpc_readb(falcbase + card->hw.cpld_reg2) &
+                                  ~(CPLD_REG2_FALC_LED2 << (2 * ch)));
+               }
+       } else {
+               if (!pfalc->sync) {
+                       pfalc->sync = 1;
+                       chan->d.line_on++;
+                       cpc_writeb(falcbase + card->hw.cpld_reg2,
+                                  cpc_readb(falcbase + card->hw.cpld_reg2) |
+                                  (CPLD_REG2_FALC_LED2 << (2 * ch)));
+               }
+       }
+
+       if (pfalc->sync && !pfalc->yellow_alarm) {
+               if (!pfalc->active) {
+                       // EVENT_FALC_NORMAL
+                       if (pfalc->loop_active) {
+                               return;
+                       }
+                       if (conf->media == IF_IFACE_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) {
+               mdelay(10);
+               counter = cpc_readb(falcbase + F_REG(BECL, ch));
+               counter |= cpc_readb(falcbase + F_REG(BECH, ch)) << 8;
+               pfalc->bec += counter;
+
+               if (((conf->media == IF_IFACE_T1) &&
+                    (cpc_readb(falcbase + F_REG(FRS1, ch)) & FRS1_LLBAD) &&
+                    (!(cpc_readb(falcbase + F_REG(FRS1, ch)) & FRS1_PDEN)))
+                   ||
+                   ((conf->media == IF_IFACE_E1) &&
+                    (cpc_readb(falcbase + F_REG(RSP, ch)) & RSP_LLBAD))) {
+                       pfalc->prbs = 2;
+               } else {
+                       pfalc->prbs = 1;
+               }
+       }
+}
+
+/*----------------------------------------------------------------------------
+ * falc_remote_loop
+ *----------------------------------------------------------------------------
+ * Description:        In the remote loopback mode the clock and data recovered
+ *             from the line inputs RL1/2 or RDIP/RDIN are routed back
+ *             to the line outputs XL1/2 or XDOP/XDON via the analog
+ *             transmitter. As in normal mode they are processsed by
+ *             the synchronizer and then sent to the system interface.
+ *----------------------------------------------------------------------------
+ */
+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 == IF_IFACE_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 + card->hw.cpld_reg2,
+                          cpc_readb(falcbase + card->hw.cpld_reg2) &
+                          ~(CPLD_REG2_FALC_LED2 << (2 * ch)));
+               pfalc->active = 0;
+               falc_issue_cmd(card, ch, CMDR_XRES);
+               pfalc->loop_active = 0;
+       }
+}
+
+/*----------------------------------------------------------------------------
+ * falc_local_loop
+ *----------------------------------------------------------------------------
+ * Description: The local loopback mode disconnects the receive lines 
+ *             RL1/RL2 resp. RDIP/RDIN from the receiver. Instead of the
+ *             signals coming from the line the data provided by system
+ *             interface are routed through the analog receiver back to
+ *             the system interface. The unipolar bit stream will be
+ *             undisturbed transmitted on the line. Receiver and transmitter
+ *             coding must be identical.
+ *----------------------------------------------------------------------------
+ */
+void falc_local_loop(pc300_t * card, int ch, int loop_on)
+{
+       pc300ch_t *chan = (pc300ch_t *) & card->chan[ch];
+       falc_t *pfalc = (falc_t *) & chan->falc;
+       uclong falcbase = card->hw.falcbase;
+
+       if (loop_on) {
+               cpc_writeb(falcbase + F_REG(LIM0, ch),
+                          cpc_readb(falcbase + F_REG(LIM0, ch)) | LIM0_LL);
+               pfalc->loop_active = 1;
+       } else {
+               cpc_writeb(falcbase + F_REG(LIM0, ch),
+                          cpc_readb(falcbase + F_REG(LIM0, ch)) & ~LIM0_LL);
+               pfalc->loop_active = 0;
+       }
+}
+
+/*----------------------------------------------------------------------------
+ * falc_payload_loop
+ *----------------------------------------------------------------------------
+ * Description: This routine allows to enable/disable payload loopback.
+ *             When the payload loop is activated, the received 192 bits
+ *             of payload data will be looped back to the transmit
+ *             direction. The framing bits, CRC6 and DL bits are not 
+ *             looped. They are originated by the FALC-LH transmitter.
+ *----------------------------------------------------------------------------
+ */
+void falc_payload_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 == IF_IFACE_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(FMR2, ch),
+                          cpc_readb(falcbase + F_REG(FMR2, ch)) | FMR2_PLB);
+               if (conf->media == IF_IFACE_T1) {
+                       cpc_writeb(falcbase + F_REG(FMR4, ch),
+                                  cpc_readb(falcbase + F_REG(FMR4, ch)) | FMR4_TM);
+               } else {
+                       cpc_writeb(falcbase + F_REG(FMR5, ch),
+                                  cpc_readb(falcbase + F_REG(FMR5, ch)) | XSP_TT0);
+               }
+               falc_open_all_timeslots(card, ch);
+               pfalc->loop_active = 2;
+       } else {
+               cpc_writeb(falcbase + F_REG(FMR2, ch),
+                          cpc_readb(falcbase + F_REG(FMR2, ch)) & ~FMR2_PLB);
+               if (conf->media == IF_IFACE_T1) {
+                       cpc_writeb(falcbase + F_REG(FMR4, ch),
+                                  cpc_readb(falcbase + F_REG(FMR4, ch)) & ~FMR4_TM);
+               } else {
+                       cpc_writeb(falcbase + F_REG(FMR5, ch),
+                                  cpc_readb(falcbase + F_REG(FMR5, ch)) & ~XSP_TT0);
+               }
+               pfalc->sync = 0;
+               cpc_writeb(falcbase + card->hw.cpld_reg2,
+                          cpc_readb(falcbase + card->hw.cpld_reg2) &
+                          ~(CPLD_REG2_FALC_LED2 << (2 * ch)));
+               pfalc->active = 0;
+               falc_issue_cmd(card, ch, CMDR_XRES);
+               pfalc->loop_active = 0;
+       }
+}
+
+/*----------------------------------------------------------------------------
+ * turn_off_xlu
+ *----------------------------------------------------------------------------
+ * Description:        Turns XLU bit off in the proper register
+ *----------------------------------------------------------------------------
+ */
+void turn_off_xlu(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;
+
+       if (conf->media == IF_IFACE_T1) {
+               cpc_writeb(falcbase + F_REG(FMR5, ch),
+                          cpc_readb(falcbase + F_REG(FMR5, ch)) & ~FMR5_XLU);
+       } else {
+               cpc_writeb(falcbase + F_REG(FMR3, ch),
+                          cpc_readb(falcbase + F_REG(FMR3, ch)) & ~FMR3_XLU);
+       }
+}
+
+/*----------------------------------------------------------------------------
+ * turn_off_xld
+ *----------------------------------------------------------------------------
+ * Description: Turns XLD bit off in the proper register
+ *----------------------------------------------------------------------------
+ */
+void turn_off_xld(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;
+
+       if (conf->media == IF_IFACE_T1) {
+               cpc_writeb(falcbase + F_REG(FMR5, ch),
+                          cpc_readb(falcbase + F_REG(FMR5, ch)) & ~FMR5_XLD);
+       } else {
+               cpc_writeb(falcbase + F_REG(FMR3, ch),
+                          cpc_readb(falcbase + F_REG(FMR3, ch)) & ~FMR3_XLD);
+       }
+}
+
+/*----------------------------------------------------------------------------
+ * falc_generate_loop_up_code
+ *----------------------------------------------------------------------------
+ * Description:        This routine writes the proper FALC chip register in order
+ *             to generate a LOOP activation code over a T1/E1 line.
+ *----------------------------------------------------------------------------
+ */
+void falc_generate_loop_up_code(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;
+
+       if (conf->media == IF_IFACE_T1) {
+               cpc_writeb(falcbase + F_REG(FMR5, ch),
+                          cpc_readb(falcbase + F_REG(FMR5, ch)) | FMR5_XLU);
+       } else {
+               cpc_writeb(falcbase + F_REG(FMR3, ch),
+                          cpc_readb(falcbase + F_REG(FMR3, ch)) | FMR3_XLU);
+       }
+       // EVENT_FALC_ABNORMAL
+       if (conf->media == IF_IFACE_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
+       pfalc->loop_gen = 1;
+}
+
+/*----------------------------------------------------------------------------
+ * falc_generate_loop_down_code
+ *----------------------------------------------------------------------------
+ * Description:        This routine writes the proper FALC chip register in order
+ *             to generate a LOOP deactivation code over a T1/E1 line.
+ *----------------------------------------------------------------------------
+ */
+void falc_generate_loop_down_code(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;
+
+       if (conf->media == IF_IFACE_T1) {
+               cpc_writeb(falcbase + F_REG(FMR5, ch),
+                          cpc_readb(falcbase + F_REG(FMR5, ch)) | FMR5_XLD);
+       } else {
+               cpc_writeb(falcbase + F_REG(FMR3, ch),
+                          cpc_readb(falcbase + F_REG(FMR3, ch)) | FMR3_XLD);
+       }
+       pfalc->sync = 0;
+       cpc_writeb(falcbase + card->hw.cpld_reg2,
+                  cpc_readb(falcbase + card->hw.cpld_reg2) &
+                  ~(CPLD_REG2_FALC_LED2 << (2 * ch)));
+       pfalc->active = 0;
+//?    falc_issue_cmd(card, ch, CMDR_XRES);
+       pfalc->loop_gen = 0;
+}
+
+/*----------------------------------------------------------------------------
+ * falc_pattern_test
+ *----------------------------------------------------------------------------
+ * Description:        This routine generates a pattern code and checks
+ *             it on the reception side.
+ *----------------------------------------------------------------------------
+ */
+void falc_pattern_test(pc300_t * card, int ch, unsigned int activate)
+{
+       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 (activate) {
+               pfalc->prbs = 1;
+               pfalc->bec = 0;
+               if (conf->media == IF_IFACE_T1) {
+                       /* Disable local loop activation/deactivation detect */
+                       cpc_writeb(falcbase + F_REG(IMR3, ch),
+                                  cpc_readb(falcbase + F_REG(IMR3, ch)) | IMR3_LLBSC);
+               } else {
+                       /* Disable local loop activation/deactivation detect */
+                       cpc_writeb(falcbase + F_REG(IMR1, ch),
+                                  cpc_readb(falcbase + F_REG(IMR1, ch)) | IMR1_LLBSC);
+               }
+               /* Activates generation and monitoring of PRBS 
+                * (Pseudo Random Bit Sequence) */
+               cpc_writeb(falcbase + F_REG(LCR1, ch),
+                          cpc_readb(falcbase + F_REG(LCR1, ch)) | LCR1_EPRM | LCR1_XPRBS);
+       } else {
+               pfalc->prbs = 0;
+               /* Deactivates generation and monitoring of PRBS 
+                * (Pseudo Random Bit Sequence) */
+               cpc_writeb(falcbase + F_REG(LCR1, ch),
+                          cpc_readb(falcbase+F_REG(LCR1,ch)) & ~(LCR1_EPRM | LCR1_XPRBS));
+               if (conf->media == IF_IFACE_T1) {
+                       /* Enable local loop activation/deactivation detect */
+                       cpc_writeb(falcbase + F_REG(IMR3, ch),
+                                  cpc_readb(falcbase + F_REG(IMR3, ch)) & ~IMR3_LLBSC);
+               } else {
+                       /* Enable local loop activation/deactivation detect */
+                       cpc_writeb(falcbase + F_REG(IMR1, ch),
+                                  cpc_readb(falcbase + F_REG(IMR1, ch)) & ~IMR1_LLBSC);
+               }
+       }
+}
+
+/*----------------------------------------------------------------------------
+ * falc_pattern_test_error
+ *----------------------------------------------------------------------------
+ * Description:        This routine returns the bit error counter value
+ *----------------------------------------------------------------------------
+ */
+ucshort falc_pattern_test_error(pc300_t * card, int ch)
+{
+       pc300ch_t *chan = (pc300ch_t *) & card->chan[ch];
+       falc_t *pfalc = (falc_t *) & chan->falc;
+
+       return (pfalc->bec);
+}
+
+/**********************************/
+/***   Net Interface Routines   ***/
+/**********************************/
+
+static void
+cpc_trace(struct net_device *dev, struct sk_buff *skb_main, char rx_tx)
+{
+       struct sk_buff *skb;
+
+       if ((skb = dev_alloc_skb(10 + skb_main->len)) == NULL) {
+               printk("%s: out of memory\n", dev->name);
+               return;
+       }
+       skb_put(skb, 10 + skb_main->len);
+
+       skb->dev = dev;
+       skb->protocol = htons(ETH_P_CUST);
+       skb->mac.raw = skb->data;
+       skb->pkt_type = PACKET_HOST;
+       skb->len = 10 + skb_main->len;
+
+       memcpy(skb->data, dev->name, 5);
+       skb->data[5] = '[';
+       skb->data[6] = rx_tx;
+       skb->data[7] = ']';
+       skb->data[8] = ':';
+       skb->data[9] = ' ';
+       memcpy(&skb->data[10], skb_main->data, skb_main->len);
+
+       netif_rx(skb);
+}
+
+void cpc_tx_timeout(struct net_device *dev)
+{
+       pc300dev_t *d = (pc300dev_t *) dev->priv;
+       pc300ch_t *chan = (pc300ch_t *) d->chan;
+       pc300_t *card = (pc300_t *) chan->card;
+       struct net_device_stats *stats = &d->hdlc->stats;
+       int ch = chan->channel;
+       uclong flags;
+       ucchar ilar;
+
+       stats->tx_errors++;
+       stats->tx_aborted_errors++;
+       CPC_LOCK(card, flags);
+       if ((ilar = cpc_readb(card->hw.scabase + ILAR)) != 0) {
+               printk("%s: ILAR=0x%x\n", dev->name, ilar);
+               cpc_writeb(card->hw.scabase + ILAR, ilar);
+               cpc_writeb(card->hw.scabase + DMER, 0x80);
+       }
+       if (card->hw.type == PC300_TE) {
+               cpc_writeb(card->hw.falcbase + card->hw.cpld_reg2,
+                          cpc_readb(card->hw.falcbase + card->hw.cpld_reg2) &
+                          ~(CPLD_REG2_FALC_LED1 << (2 * ch)));
+       }
+       dev->trans_start = jiffies;
+       CPC_UNLOCK(card, flags);
+       netif_wake_queue(dev);
+}
+
+int cpc_queue_xmit(struct sk_buff *skb, struct net_device *dev)
+{
+       pc300dev_t *d = (pc300dev_t *) dev->priv;
+       pc300ch_t *chan = (pc300ch_t *) d->chan;
+       pc300_t *card = (pc300_t *) chan->card;
+       struct net_device_stats *stats = &d->hdlc->stats;
+       int ch = chan->channel;
+       uclong flags;
+#ifdef PC300_DEBUG_TX
+       int i;
+#endif
+
+       if (chan->conf.monitor) {
+               /* In monitor mode no Tx is done: ignore packet */
+               dev_kfree_skb(skb);
+               return 0;
+       } else if (!netif_carrier_ok(dev)) {
+               /* DCD must be OFF: drop packet */
+               dev_kfree_skb(skb);
+               stats->tx_errors++;
+               stats->tx_carrier_errors++;
+               return 0;
+       } else if (cpc_readb(card->hw.scabase + M_REG(ST3, ch)) & ST3_DCD) {
+               printk("%s: DCD is OFF. Going administrative down.\n", dev->name);
+               stats->tx_errors++;
+               stats->tx_carrier_errors++;
+               dev_kfree_skb(skb);
+               netif_carrier_off(dev);
+               CPC_LOCK(card, flags);
+               cpc_writeb(card->hw.scabase + M_REG(CMD, ch), CMD_TX_BUF_CLR);
+               if (card->hw.type == PC300_TE) {
+                       cpc_writeb(card->hw.falcbase + card->hw.cpld_reg2,
+                                  cpc_readb(card->hw.falcbase + card->hw.cpld_reg2) & 
+                                                       ~(CPLD_REG2_FALC_LED1 << (2 * ch)));
+               }
+               CPC_UNLOCK(card, flags);
+               netif_wake_queue(dev);
+               return 0;
+       }
+
+       /* Write buffer to DMA buffers */
+       if (dma_buf_write(card, ch, (ucchar *) skb->data, skb->len) != 0) {
+//             printk("%s: write error. Dropping TX packet.\n", dev->name);
+               netif_stop_queue(dev);
+               dev_kfree_skb(skb);
+               stats->tx_errors++;
+               stats->tx_dropped++;
+               return 0;
+       }
+#ifdef PC300_DEBUG_TX
+       printk("%s T:", dev->name);
+       for (i = 0; i < skb->len; i++)
+               printk(" %02x", *(skb->data + i));
+       printk("\n");
+#endif
+
+       if (d->trace_on) {
+               cpc_trace(dev, skb, 'T');
+       }
+       dev->trans_start = jiffies;
+
+       /* Start transmission */
+       CPC_LOCK(card, flags);
+       /* verify if it has more than one free descriptor */
+       if (card->chan[ch].nfree_tx_bd <= 1) {
+               /* don't have so stop the queue */
+               netif_stop_queue(dev);
+       }
+       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 + card->hw.cpld_reg2,
+                          cpc_readb(card->hw.falcbase + card->hw.cpld_reg2) |
+                          (CPLD_REG2_FALC_LED1 << (2 * ch)));
+       }
+       CPC_UNLOCK(card, flags);
+       dev_kfree_skb(skb);
+
+       return 0;
+}
+
+void cpc_net_rx(hdlc_device * hdlc)
+{
+       struct net_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 net_device_stats *stats = &d->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;
+
+               if (!netif_carrier_ok(dev)) {
+                       /* DCD must be OFF: drop packet */
+                   printk("%s : DCD is OFF - drop %d rx bytes\n", dev->name, rxb); 
+                       skb = NULL;
+               } else {
+                       if (rxb > (dev->mtu + 40)) { /* add headers */
+                               printk("%s : MTU exceeded %d\n", dev->name, rxb); 
+                               skb = NULL;
+                       } else {
+                               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) || (skb == NULL)) {
+#ifdef PC300_DEBUG_RX
+                       printk("%s: rxb = %x\n", dev->name, rxb);
+#endif
+                       if ((skb == NULL) && (rxb > 0)) {
+                               /* rxb > dev->mtu */
+                               stats->rx_errors++;
+                               stats->rx_length_errors++;
+                               continue;
+                       }
+
+                       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++;
+                               }
+                       }
+                       if (skb) {
+                               dev_kfree_skb_irq(skb);
+                       }
+                       continue;
+               }
+
+               stats->rx_bytes += rxb;
+
+#ifdef PC300_DEBUG_RX
+               printk("%s R:", dev->name);
+               for (i = 0; i < skb->len; i++)
+                       printk(" %02x", *(skb->data + i));
+               printk("\n");
+#endif
+               if (d->trace_on) {
+                       cpc_trace(dev, skb, 'R');
+               }
+               stats->rx_packets++;
+               skb->mac.raw = skb->data;
+               skb->protocol = htons(ETH_P_HDLC);
+               netif_rx(skb);
+       }
+}
+
+/************************************/
+/***   PC300 Interrupt Routines   ***/
+/************************************/
+static void sca_tx_intr(pc300dev_t *dev)
+{
+       pc300ch_t *chan = (pc300ch_t *)dev->chan; 
+       pc300_t *card = (pc300_t *)chan->card; 
+       int ch = chan->channel; 
+       volatile pcsca_bd_t * ptdescr; 
+       struct net_device_stats *stats = &dev->hdlc->stats; 
+
+    /* Clean up descriptors from previous transmission */
+       ptdescr = (pcsca_bd_t *)(card->hw.rambase +
+                                               TX_BD_ADDR(ch,chan->tx_first_bd));
+       while ((cpc_readl(card->hw.scabase + DTX_REG(CDAL,ch)) != 
+                                                       TX_BD_ADDR(ch,chan->tx_first_bd)) && 
+                       (cpc_readb(&ptdescr->status) & DST_OSB)) {
+               stats->tx_packets++;
+               stats->tx_bytes += cpc_readw(&ptdescr->len);
+               cpc_writeb(&ptdescr->status, DST_OSB);
+               cpc_writew(&ptdescr->len, 0);
+               chan->nfree_tx_bd++;
+               chan->tx_first_bd = (chan->tx_first_bd + 1) & (N_DMA_TX_BUF - 1);
+               ptdescr = (pcsca_bd_t *)(card->hw.rambase +
+                                               TX_BD_ADDR(ch,chan->tx_first_bd));
+    }
+
+#ifdef CONFIG_PC300_MLPPP
+       if (chan->conf.proto == PC300_PROTO_MLPPP) {
+                       cpc_tty_trigger_poll(dev);
+       } else {
+#endif
+       /* Tell the upper layer we are ready to transmit more packets */
+               netif_wake_queue((struct net_device*)dev->hdlc);
+#ifdef CONFIG_PC300_MLPPP
+       }
+#endif
+}
+
+static void sca_intr(pc300_t * card)
+{
+       uclong scabase = card->hw.scabase;
+       volatile uclong status;
+       int ch;
+       int intr_count = 0;
+       unsigned char dsr_rx;
+
+       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 net_device *dev = hdlc_to_dev(hdlc);
+
+                       spin_lock(&card->card_lock);
+
+           /**** 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 chan[%d] (st=0x%08lx, dsr=0x%02x)\n",
+                                        ch, status, drx_stat);
+#endif
+                               if (status & IR0_DRX(IR0_DMIA, ch)) {
+                                       if (drx_stat & DSR_BOF) {
+#ifdef CONFIG_PC300_MLPPP
+                                               if (chan->conf.proto == PC300_PROTO_MLPPP) {
+                                                       /* verify if driver is TTY */
+                                                       if ((cpc_readb(scabase + DSR_RX(ch)) & DSR_DE)) {
+                                                               rx_dma_stop(card, ch);
+                                                       }
+                                                       cpc_tty_receive(d);
+                                                       rx_dma_start(card, ch);
+                                               } else 
+#endif
+                                               {
+                                                       if ((cpc_readb(scabase + DSR_RX(ch)) & DSR_DE)) {
+                                                               rx_dma_stop(card, ch);
+                                                       }
+                                                       cpc_net_rx(hdlc);
+                                                       /* Discard invalid frames */
+                                                       hdlc->stats.rx_errors++;
+                                                       hdlc->stats.rx_over_errors++;
+                                                       chan->rx_first_bd = 0;
+                                                       chan->rx_last_bd = N_DMA_RX_BUF - 1;
+                                                       rx_dma_start(card, ch);
+                                               }
+                                       }
+                               }
+                               if (status & IR0_DRX(IR0_DMIB, ch)) {
+                                       if (drx_stat & DSR_EOM) {
+                                               if (card->hw.type == PC300_TE) {
+                                                       cpc_writeb(card->hw.falcbase +
+                                                                  card->hw.cpld_reg2,
+                                                                  cpc_readb (card->hw.falcbase +
+                                                                       card->hw.cpld_reg2) |
+                                                                  (CPLD_REG2_FALC_LED1 << (2 * ch)));
+                                               }
+#ifdef CONFIG_PC300_MLPPP
+                                               if (chan->conf.proto == PC300_PROTO_MLPPP) {
+                                                       /* verify if driver is TTY */
+                                                       cpc_tty_receive(d);
+                                               } else {
+                                                       cpc_net_rx(hdlc);
+                                               }
+#else
+                                               cpc_net_rx(hdlc);
+#endif
+                                               if (card->hw.type == PC300_TE) {
+                                                       cpc_writeb(card->hw.falcbase +
+                                                                  card->hw.cpld_reg2,
+                                                                  cpc_readb (card->hw.falcbase +
+                                                                               card->hw.cpld_reg2) &
+                                                                  ~ (CPLD_REG2_FALC_LED1 << (2 * ch)));
+                                               }
+                                       }
+                               }
+                               if (!(dsr_rx = cpc_readb(scabase + DSR_RX(ch)) & DSR_DE)) {
+
+printk("%s: RX intr chan[%d] (st=0x%08lx, dsr=0x%02x, dsr2=0x%02x)\n", 
+       dev->name, ch, status, drx_stat, dsr_rx);
+                                       cpc_writeb(scabase + DSR_RX(ch), (dsr_rx | DSR_DE) & 0xfe);
+                               }
+                       }
+
+           /**** 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 chan[%d] (st=0x%08lx, dsr=0x%02x)\n",
+                                        ch, 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 + card->hw.cpld_reg2,
+                                                                  cpc_readb (card->hw.falcbase + 
+                                                                                  card->hw.cpld_reg2) &
+                                                                  ~ (CPLD_REG2_FALC_LED1 << (2 * ch)));
+                                               }
+                                               hdlc->stats.tx_errors++;
+                                               hdlc->stats.tx_fifo_errors++;
+                                               sca_tx_intr(d);
+                                       }
+                               }
+                               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 + card->hw.cpld_reg2,
+                                                                  cpc_readb (card->hw.falcbase +
+                                                                                       card->hw.cpld_reg2) &
+                                                                  ~ (CPLD_REG2_FALC_LED1 << (2 * ch)));
+                                               }
+                                               sca_tx_intr(d);
+                                       }
+                               }
+                       }
+
+           /**** MSCI ****/
+                       if (status & IR0_M(IR0_RXINTA, ch)) {
+                               ucchar st1 = cpc_readb(scabase + M_REG(ST1, ch));
+
+                               /* Clear MSCI interrupts */
+                               cpc_writeb(scabase + M_REG(ST1, ch), st1);
+
+#ifdef PC300_DEBUG_INTR
+                               printk("sca_intr: MSCI intr chan[%d] (st=0x%08lx, st1=0x%02x)\n",
+                                        ch, status, st1);
+#endif
+                               if (st1 & ST1_CDCD) {   /* DCD changed */
+                                       if (cpc_readb(scabase + M_REG(ST3, ch)) & ST3_DCD) {
+                                               printk ("%s: DCD is OFF. Going administrative down.\n",
+                                                        dev->name);
+#ifdef CONFIG_PC300_MLPPP
+                                               if (chan->conf.proto != PC300_PROTO_MLPPP) {
+                                                       netif_carrier_off(dev);
+                                               }
+#else
+                                               netif_carrier_off(dev);
+
+#endif
+                                               card->chan[ch].d.line_off++;
+                                       } else {        /* DCD = 1 */
+                                               printk ("%s: DCD is ON. Going administrative up.\n",
+                                                        dev->name);
+#ifdef CONFIG_PC300_MLPPP
+                                               if (chan->conf.proto != PC300_PROTO_MLPPP)
+                                                       /* verify if driver is not TTY */
+#endif
+                                                       netif_carrier_on(dev);
+                                               card->chan[ch].d.line_on++;
+                                       }
+                               }
+                       }
+                       spin_unlock(&card->card_lock);
+               }
+               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 Deactivation 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 Activation 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 Deactivation 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 Activation 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 == IF_IFACE_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 */
+       }
+
+       if (card->hw.rambase == 0) {
+#ifdef PC300_DEBUG_INTR
+               printk("cpc_intr: spurious intr2 %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 + card->hw.intctl_reg) &
+                                (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_change_mtu(struct net_device *dev, int new_mtu)
+{
+       if ((new_mtu < 128) || (new_mtu > PC300_DEF_MTU))
+               return -EINVAL;
+       dev->mtu = new_mtu;
+       return 0;
+}
+
+int cpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
+{
+       hdlc_device *hdlc = dev_to_hdlc(dev);
+       pc300dev_t *d = (pc300dev_t *) dev->priv;
+       pc300ch_t *chan = (pc300ch_t *) d->chan;
+       pc300_t *card = (pc300_t *) chan->card;
+       pc300conf_t conf_aux;
+       pc300chconf_t *conf = (pc300chconf_t *) & chan->conf;
+       int ch = chan->channel;
+       void *arg = (void *) ifr->ifr_data;
+       union line_settings * line = &ifr->ifr_settings->ifs_line;
+       uclong scabase = card->hw.scabase;
+
+       if (!capable(CAP_NET_ADMIN))
+               return -EPERM;
+
+       switch (cmd) {
+               case SIOCGPC300CONF:
+#ifdef CONFIG_PC300_MLPPP
+                       if (conf->proto != PC300_PROTO_MLPPP) {
+                               conf->proto = hdlc->proto;
+                       }
+#else
+                       conf->proto = hdlc->proto;
+#endif
+                       memcpy(&conf_aux.conf, conf, sizeof(pc300chconf_t));
+                       memcpy(&conf_aux.hw, &card->hw, sizeof(pc300hw_t));
+                       if (!arg || 
+                               copy_to_user(arg, &conf_aux, sizeof(pc300conf_t))) 
+                               return -EINVAL;
+                       return 0;
+               case SIOCSPC300CONF:
+                       if (!suser())
+                               return -EPERM;
+                       if (!arg || 
+                               copy_from_user(&conf_aux.conf, arg, sizeof(pc300chconf_t)))
+                               return -EINVAL;
+                       if (card->hw.cpld_id < 0x02 &&
+                           conf_aux.conf.fr_mode == PC300_FR_UNFRAMED) {
+                               /* CPLD_ID < 0x02 doesn't support Unframed E1 */
+                               return -EINVAL;
+                       }
+#ifdef CONFIG_PC300_MLPPP
+                       if (conf_aux.conf.proto == PC300_PROTO_MLPPP) {
+                               if (conf->proto != PC300_PROTO_MLPPP) {
+                                       memcpy(conf, &conf_aux.conf, sizeof(pc300chconf_t));
+                                       cpc_tty_init(d);        /* init TTY driver */
+                               }
+                       } else {
+                               if (conf_aux.conf.proto == 0xffff) {
+                                       if (conf->proto == PC300_PROTO_MLPPP){ 
+                                               /* ifdown interface */
+                                               cpc_close(dev);
+                                       }
+                               } else {
+                                       memcpy(conf, &conf_aux.conf, sizeof(pc300chconf_t));
+                                       hdlc->proto = conf->proto;
+                               }
+                       }
+#else
+                       memcpy(conf, &conf_aux.conf, sizeof(pc300chconf_t));
+                       hdlc->proto = conf->proto;
+#endif
+                       return 0;
+               case SIOCGPC300STATUS:
+                       cpc_sca_status(card, ch);
+                       return 0;
+               case SIOCGPC300FALCSTATUS:
+                       cpc_falc_status(card, ch);
+                       return 0;
+
+               case SIOCGPC300UTILSTATS:
+                       {
+                               if (!arg) {     /* clear statistics */
+                                       memset(&hdlc->stats, 0, sizeof(struct net_device_stats));
+                                       if (card->hw.type == PC300_TE) {
+                                               memset(&chan->falc, 0, sizeof(falc_t));
+                                       }
+                               } else {
+                                       pc300stats_t pc300stats;
+
+                                       memset(&pc300stats, 0, sizeof(pc300stats_t));
+                                       pc300stats.hw_type = card->hw.type;
+                                       pc300stats.line_on = card->chan[ch].d.line_on;
+                                       pc300stats.line_off = card->chan[ch].d.line_off;
+                                       memcpy(&pc300stats.gen_stats, &hdlc->stats,
+                                              sizeof(struct net_device_stats));
+                                       if (card->hw.type == PC300_TE)
+                                               memcpy(&pc300stats.te_stats,&chan->falc,sizeof(falc_t));
+                                   copy_to_user(arg, &pc300stats, sizeof(pc300stats_t));
+                               }
+                               return 0;
+                       }
+
+               case SIOCGPC300UTILSTATUS:
+                       {
+                               struct pc300status pc300status;
+
+                               pc300status.hw_type = card->hw.type;
+                               if (card->hw.type == PC300_TE) {
+                                       pc300status.te_status.sync = chan->falc.sync;
+                                       pc300status.te_status.red_alarm = chan->falc.red_alarm;
+                                       pc300status.te_status.blue_alarm = chan->falc.blue_alarm;
+                                       pc300status.te_status.loss_fa = chan->falc.loss_fa;
+                                       pc300status.te_status.yellow_alarm =chan->falc.yellow_alarm;
+                                       pc300status.te_status.loss_mfa = chan->falc.loss_mfa;
+                                       pc300status.te_status.prbs = chan->falc.prbs;
+                               } else {
+                                       pc300status.gen_status.dcd =
+                                               !(cpc_readb (scabase + M_REG(ST3, ch)) & ST3_DCD);
+                                       pc300status.gen_status.cts =
+                                               !(cpc_readb (scabase + M_REG(ST3, ch)) & ST3_CTS);
+                                       pc300status.gen_status.rts =
+                                               !(cpc_readb (scabase + M_REG(CTL, ch)) & CTL_RTS);
+                                       pc300status.gen_status.dtr =
+                                               !(cpc_readb (scabase + M_REG(CTL, ch)) & CTL_DTR);
+                                       /* There is no DSR in HD64572 */
+                               }
+                               if (!arg
+                                   || copy_to_user(arg, &pc300status, sizeof(pc300status_t)))
+                                               return -EINVAL;
+                               return 0;
+                       }
+
+               case SIOCSPC300TRACE:
+                       /* Sets/resets a trace_flag for the respective device */
+                       if (!arg || copy_from_user(&d->trace_on, arg,sizeof(unsigned char)))
+                                       return -EINVAL;
+                       return 0;
+
+               case SIOCSPC300LOOPBACK:
+                       {
+                               struct pc300loopback pc300loop;
+
+                               /* TE boards only */
+                               if (card->hw.type != PC300_TE)
+                                       return -EINVAL;
+
+                               if (!arg || 
+                                       copy_from_user(&pc300loop, arg, sizeof(pc300loopback_t)))
+                                               return -EINVAL;
+                               switch (pc300loop.loop_type) {
+                                       case PC300LOCLOOP:      /* Turn the local loop on/off */
+                                               falc_local_loop(card, ch, pc300loop.loop_on);
+                                               return 0;
+
+                                       case PC300REMLOOP:      /* Turn the remote loop on/off */
+                                               falc_remote_loop(card, ch, pc300loop.loop_on);
+                                               return 0;
+
+                                       case PC300PAYLOADLOOP:  /* Turn the payload loop on/off */
+                                               falc_payload_loop(card, ch, pc300loop.loop_on);
+                                               return 0;
+
+                                       case PC300GENLOOPUP:    /* Generate loop UP */
+                                               if (pc300loop.loop_on) {
+                                                       falc_generate_loop_up_code (card, ch);
+                                               } else {
+                                                       turn_off_xlu(card, ch);
+                                               }
+                                               return 0;
+
+                                       case PC300GENLOOPDOWN:  /* Generate loop DOWN */
+                                               if (pc300loop.loop_on) {
+                                                       falc_generate_loop_down_code (card, ch);
+                                               } else {
+                                                       turn_off_xld(card, ch);
+                                               }
+                                               return 0;
+
+                                       default:
+                                               return -EINVAL;
+                               }
+                       }
+
+               case SIOCSPC300PATTERNTEST:
+                       /* Turn the pattern test on/off and show the errors counter */
+                       {
+                               struct pc300patterntst pc300patrntst;
+
+                               /* TE boards only */
+                               if (card->hw.type != PC300_TE)
+                                       return -EINVAL;
+
+                               if (card->hw.cpld_id < 0x02) {
+                                       /* CPLD_ID < 0x02 doesn't support pattern test */
+                                       return -EINVAL;
+                               }
+
+                               if (!arg || 
+                                       copy_from_user(&pc300patrntst,arg,sizeof(pc300patterntst_t)))
+                                               return -EINVAL;
+                               if (pc300patrntst.patrntst_on == 2) {
+                                       if (chan->falc.prbs == 0) {
+                                               falc_pattern_test(card, ch, 1);
+                                       }
+                                       pc300patrntst.num_errors =
+                                               falc_pattern_test_error(card, ch);
+                                       if (!arg
+                                           || copy_to_user(arg, &pc300patrntst,
+                                                           sizeof (pc300patterntst_t)))
+                                                       return -EINVAL;
+                               } else {
+                                       falc_pattern_test(card, ch, pc300patrntst.patrntst_on);
+                               }
+                               return 0;
+                       }
+
+               case SIOCWANDEV:
+                       switch (ifr->ifr_settings->type) {
+                               case IF_GET_IFACE:
+                               {
+                                       const size_t size = sizeof(sync_serial_settings);
+                                       ifr->ifr_settings->type = conf->media;
+       
+                                       if (copy_to_user(&line->sync, &conf->phys_settings, size)) {
+                                               return -EFAULT;
+                                       }
+                                       return 0;
+                               }
+
+                               case IF_IFACE_V35:
+                               case IF_IFACE_V24:
+                               case IF_IFACE_X21:
+                               {
+                                       const size_t size = sizeof(sync_serial_settings);
+
+                                       if (!capable(CAP_NET_ADMIN)) {
+                                               return -EPERM;
+                                       }
+
+                                       if (copy_from_user(&conf->phys_settings, 
+                                                                          &line->sync, size)) {
+                                               return -EFAULT;
+                                       }
+
+                                       if (conf->phys_settings.loopback) {
+                                               cpc_writeb(card->hw.scabase + M_REG(MD2, ch),
+                                                       cpc_readb(card->hw.scabase + M_REG(MD2, ch)) | 
+                                                       MD2_LOOP_MIR);
+                                       }
+                                       conf->media = ifr->ifr_settings->type;
+                                       return 0;
+                               }
+
+                               case IF_IFACE_T1:
+                               case IF_IFACE_E1:
+                               {
+                                       const size_t size = sizeof(sync_serial_settings);
+
+                                       if (!capable(CAP_NET_ADMIN)) {
+                                               return -EPERM;
+                                       }
+                                       
+                                       if (copy_from_user(&conf->phys_settings, 
+                                               &line->te1, size)) {
+                                               return -EFAULT;
+                                       }/* Ignoring HDLC slot_map for a while */
+                                       
+                                       if (conf->phys_settings.loopback) {
+                                               cpc_writeb(card->hw.scabase + M_REG(MD2, ch),
+                                                       cpc_readb(card->hw.scabase + M_REG(MD2, ch)) | 
+                                                       MD2_LOOP_MIR);
+                                       }
+                                       conf->media = ifr->ifr_settings->type;
+                                       return 0;
+                               }
+                               default:
+                                       return hdlc_ioctl(dev, ifr, cmd);
+                       }
+
+               default:
+                       return hdlc_ioctl(dev, ifr, cmd);
+       }
+}
+
+static struct net_device_stats *cpc_get_stats(struct net_device *dev)
+{
+       pc300dev_t *d = (pc300dev_t *) dev->priv;
+
+       if (d)
+               return &d->hdlc->stats;
+       else
+               return NULL;
+}
+
+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.phys_settings.clock_rate;
+       uclong clktype = chan->conf.phys_settings.clock_type;
+       ucshort encoding = chan->conf.proto_settings.encoding;
+       ucshort parity = chan->conf.proto_settings.parity;   
+       int tmc, br;
+       ucchar md0, md2;
+    
+       /* Reset the channel */
+       cpc_writeb(scabase + M_REG(CMD, ch), CMD_CH_RST);
+
+       /* Configure the SCA registers */
+       switch (parity) {
+               case PARITY_NONE:
+                       md0 = MD0_BIT_SYNC;
+                       break;
+               case PARITY_CRC16_PR0:
+                       md0 = MD0_CRC16_0|MD0_CRCC0|MD0_BIT_SYNC;
+                       break;
+               case PARITY_CRC16_PR1:
+                       md0 = MD0_CRC16_1|MD0_CRCC0|MD0_BIT_SYNC;
+                       break;
+               case PARITY_CRC32_PR1_CCITT:
+                       md0 = MD0_CRC32|MD0_CRCC0|MD0_BIT_SYNC;
+                       break;
+               case PARITY_CRC16_PR1_CCITT:
+               default:
+                       md0 = MD0_CRC_CCITT|MD0_CRCC0|MD0_BIT_SYNC;
+                       break;
+       }
+       switch (encoding) {
+               case ENCODING_NRZI:
+                       md2 = MD2_F_DUPLEX|MD2_ADPLL_X8|MD2_NRZI;
+                       break;
+               case ENCODING_FM_MARK:  /* FM1 */
+                       md2 = MD2_F_DUPLEX|MD2_ADPLL_X8|MD2_FM|MD2_FM1;
+                       break;
+               case ENCODING_FM_SPACE: /* FM0 */
+                       md2 = MD2_F_DUPLEX|MD2_ADPLL_X8|MD2_FM|MD2_FM0;
+                       break;
+               case ENCODING_MANCHESTER: /* It's not working... */
+                       md2 = MD2_F_DUPLEX|MD2_ADPLL_X8|MD2_FM|MD2_MANCH;
+                       break;
+               case ENCODING_NRZ:
+               default:
+                       md2 = MD2_F_DUPLEX|MD2_ADPLL_X8|MD2_NRZ;
+                       break;
+       }
+       cpc_writeb(scabase + M_REG(MD0, ch), md0);
+       cpc_writeb(scabase + M_REG(MD1, ch), 0);
+       cpc_writeb(scabase + M_REG(MD2, ch), md2);
+       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 == IF_IFACE_V35) {
+                               cpc_writel((plxbase + card->hw.gpioc_reg),
+                                          cpc_readl(plxbase + card->hw.gpioc_reg) | PC300_CHMEDIA_MASK(ch));
+                       } else {
+                               cpc_writel((plxbase + card->hw.gpioc_reg),
+                                          cpc_readl(plxbase + card->hw.gpioc_reg) & ~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 (clktype == CLOCK_INT || clktype == CLOCK_TXINT) {
+                               /* Calculate the clkrate 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));
+                               if (clktype == CLOCK_INT) {
+                                       cpc_writeb(scabase + M_REG(TMCR, ch), tmc);
+                                       cpc_writeb(scabase + M_REG(RXS, ch), 
+                                                  (RXS_IBRG | br));
+                               } else {
+                                       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), 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);
+                               if (clktype == CLOCK_EXT) {
+                                       cpc_writeb(scabase + M_REG(TXS, ch), 
+                                                  TXS_DTRXC);
+                               } else {
+                                       cpc_writeb(scabase + M_REG(TXS, ch), 
+                                                  TXS_DTRXC|TXS_RCLK);
+                               }
+                               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_M(IR0_RXINTA, ch) |
+                  IR0_DRX(IR0_EFT | IR0_DMIA | IR0_DMIB, ch) |
+                  IR0_DTX(IR0_EFT | IR0_DMIA | IR0_DMIB, ch));
+       cpc_writeb(scabase + M_REG(IE0, ch),
+                  cpc_readl(scabase + M_REG(IE0, ch)) | IE0_RXINTA);
+       cpc_writeb(scabase + M_REG(IE1, ch),
+                  cpc_readl(scabase + M_REG(IE1, ch)) | IE1_CDCD);
+
+       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 */
+       rx_dma_start(card, ch);
+
+       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;
+}
+
+static int cpc_attach(hdlc_device * hdlc, unsigned short encoding,
+                     unsigned short parity)
+{
+       struct net_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;
+
+       if (card->hw.type == PC300_TE) {
+               if (encoding != ENCODING_NRZ && encoding != ENCODING_NRZI) {
+                       return -EINVAL;
+               }
+       } else {
+               if (encoding != ENCODING_NRZ && encoding != ENCODING_NRZI &&
+                   encoding != ENCODING_FM_MARK && encoding != ENCODING_FM_SPACE) {
+                       /* Driver doesn't support ENCODING_MANCHESTER yet */
+                       return -EINVAL;
+               }
+       }
+
+       if (parity != PARITY_NONE && parity != PARITY_CRC16_PR0 &&
+           parity != PARITY_CRC16_PR1 && parity != PARITY_CRC32_PR1_CCITT &&
+           parity != PARITY_CRC16_PR1_CCITT) {
+               return -EINVAL;
+       }
+
+       conf->proto_settings.encoding = encoding;
+       conf->proto_settings.parity = parity;
+       return 0;
+}
+
+void 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;
+
+       ch_config(d);
+
+       rx_config(d);
+
+       tx_config(d);
+
+       /* Assert RTS and DTR */
+       cpc_writeb(scabase + M_REG(CTL, ch),
+                  cpc_readb(scabase + M_REG(CTL, ch)) & ~(CTL_RTS | CTL_DTR));
+}
+
+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);
+       rx_dma_stop(card, ch);
+       tx_dma_stop(card, ch);
+
+       if (card->hw.type == PC300_TE) {
+               memset(pfalc, 0, sizeof(falc_t));
+               cpc_writeb(card->hw.falcbase + card->hw.cpld_reg2,
+                          cpc_readb(card->hw.falcbase + card->hw.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 + card->hw.cpld_reg1,
+                          cpc_readb(card->hw.falcbase + card->hw.cpld_reg1) |
+                          (CPLD_REG1_FALC_RESET << (2 * ch)));
+               udelay(10000);
+               cpc_writeb(card->hw.falcbase + card->hw.cpld_reg1,
+                          cpc_readb(card->hw.falcbase + card->hw.cpld_reg1) &
+                          ~(CPLD_REG1_FALC_RESET << (2 * ch)));
+       }
+}
+
+int cpc_open(struct net_device *dev)
+{
+       hdlc_device *hdlc = dev_to_hdlc(dev);
+       pc300dev_t *d = (pc300dev_t *) dev->priv;
+       struct ifreq ifr;
+       int result;
+
+#ifdef PC300_DEBUG_OTHER
+       printk("pc300: cpc_open");
+#endif
+
+       if (hdlc->proto == IF_PROTO_PPP) {
+               d->if_ptr = &hdlc->state.ppp.pppdev;
+       }
+
+       result = hdlc_open(hdlc);
+       if (hdlc->proto == IF_PROTO_PPP) {
+               dev->priv = d;
+       }
+       if (result) {
+               return result;
+       }
+
+       MOD_INC_USE_COUNT;
+       sprintf(ifr.ifr_name, "%s", dev->name);
+       cpc_opench(d);
+       netif_start_queue(dev);
+       return 0;
+}
+
+int cpc_close(struct net_device *dev)
+{
+       hdlc_device *hdlc = dev_to_hdlc(dev);
+       pc300dev_t *d = (pc300dev_t *) dev->priv;
+       pc300ch_t *chan = (pc300ch_t *) d->chan;
+       pc300_t *card = (pc300_t *) chan->card;
+       uclong flags;
+
+#ifdef PC300_DEBUG_OTHER
+       printk("pc300: cpc_close");
+#endif
+
+       netif_stop_queue(dev);
+
+       CPC_LOCK(card, flags);
+       cpc_closech(d);
+       CPC_UNLOCK(card, flags);
+
+       hdlc_close(hdlc);
+       if (hdlc->proto == IF_PROTO_PPP) {
+               d->if_ptr = NULL;
+       }
+#ifdef CONFIG_PC300_MLPPP
+       if (chan->conf.proto == PC300_PROTO_MLPPP) {
+               cpc_tty_unregister_service(d);
+               chan->conf.proto = 0xffff;
+       }
+#endif
+
+       MOD_DEC_USE_COUNT;
+       return 0;
+}
+
+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);
+
+}
+
+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(KERN_INFO "Cyclades-PC300 driver %s %s (built %s %s)\n", 
+               rcsvers, rcsdate, __DATE__, __TIME__);
+}                              /* show_version */
+
+static void cpc_init_card(pc300_t * card)
+{
+       int i, devcount = 0;
+       static int board_nbr = 1;
+
+       /* Enable interrupts on the PCI bridge */
+       plx_init(card);
+       cpc_writew(card->hw.plxbase + card->hw.intctl_reg,
+                  cpc_readw(card->hw.plxbase + card->hw.intctl_reg) | 0x0040);
+
+#ifdef USE_PCI_CLOCK
+       /* Set board clock to PCI clock */
+       cpc_writel(card->hw.plxbase + card->hw.gpioc_reg,
+                  cpc_readl(card->hw.plxbase + card->hw.gpioc_reg) | 0x00000004UL);
+       card->hw.clock = PC300_PCI_CLOCK;
+#else
+       /* Set board clock to internal oscillator clock */
+       cpc_writel(card->hw.plxbase + card->hw.gpioc_reg,
+                  cpc_readl(card->hw.plxbase + card->hw.gpioc_reg) & ~0x00000004UL);
+       card->hw.clock = PC300_OSC_CLOCK;
+#endif
+
+       /* Detect actual on-board RAM size */
+       card->hw.ramsize = detect_ram(card);
+
+       /* 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);
+
+       if (card->hw.type == PC300_TE) {
+               ucchar reg1;
+
+               /* Check CPLD version */
+               reg1 = cpc_readb(card->hw.falcbase + CPLD_REG1);
+               cpc_writeb(card->hw.falcbase + CPLD_REG1, (reg1 + 0x5a));
+               if (cpc_readb(card->hw.falcbase + CPLD_REG1) == reg1) {
+                       /* New CPLD */
+                       card->hw.cpld_id = cpc_readb(card->hw.falcbase + CPLD_ID_REG);
+                       card->hw.cpld_reg1 = CPLD_V2_REG1;
+                       card->hw.cpld_reg2 = CPLD_V2_REG2;
+               } else {
+                       /* old CPLD */
+                       card->hw.cpld_id = 0;
+                       card->hw.cpld_reg1 = CPLD_REG1;
+                       card->hw.cpld_reg2 = CPLD_REG2;
+                       cpc_writeb(card->hw.falcbase + CPLD_REG1, reg1);
+               }
+
+               /* Enable the board's global clock */
+               cpc_writeb(card->hw.falcbase + card->hw.cpld_reg1,
+                          cpc_readb(card->hw.falcbase + card->hw.cpld_reg1) |
+                          CPLD_REG1_GLOBAL_CLK);
+
+       }
+
+       for (i = 0; i < card->hw.nchan; i++) {
+               pc300ch_t *chan = &card->chan[i];
+               pc300dev_t *d = &chan->d;
+               hdlc_device *hdlc;
+               struct net_device *dev;
+
+               chan->card = card;
+               chan->channel = i;
+               chan->conf.phys_settings.clock_rate = 0;
+               chan->conf.phys_settings.clock_type = CLOCK_EXT;
+               chan->conf.proto_settings.encoding = ENCODING_NRZ;
+               chan->conf.proto_settings.parity = PARITY_CRC16_PR1_CCITT;
+               switch (card->hw.type) {
+                       case PC300_TE:
+                               chan->conf.media = IF_IFACE_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 = IF_IFACE_X21;
+                               break;
+
+                       case PC300_RSV:
+                       default:
+                               chan->conf.media = IF_IFACE_V35;
+                               break;
+               }
+               chan->conf.proto = IF_PROTO_PPP;
+               chan->tx_first_bd = 0;
+               chan->tx_next_bd = 0;
+               chan->rx_first_bd = 0;
+               chan->rx_last_bd = N_DMA_RX_BUF - 1;
+               chan->nfree_tx_bd = N_DMA_TX_BUF;
+
+               d->chan = chan;
+               d->tx_skb = NULL;
+               d->trace_on = 0;
+               d->line_on = 0;
+               d->line_off = 0;
+
+               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->xmit = cpc_queue_xmit;
+               hdlc->attach = cpc_attach;
+
+               dev = hdlc_to_dev(hdlc);
+
+               dev->mem_start = card->hw.ramphys;
+               dev->mem_end = card->hw.ramphys + card->hw.ramsize - 1;
+               dev->irq = card->hw.irq;
+               dev->init = NULL;
+               dev->tx_queue_len = PC300_TX_QUEUE_LEN;
+               dev->mtu = PC300_DEF_MTU;
+
+               dev->open = cpc_open;
+               dev->stop = cpc_close;
+               dev->tx_timeout = cpc_tx_timeout;
+               dev->watchdog_timeo = PC300_TX_TIMEOUT;
+               dev->get_stats = cpc_get_stats;
+               dev->set_multicast_list = NULL;
+               dev->set_mac_address = NULL;
+               dev->change_mtu = cpc_change_mtu;
+               dev->do_ioctl = cpc_ioctl;
+
+               if (register_hdlc_device(hdlc) == 0) {
+                       dev->priv = d;  /* We need 'priv', hdlc doesn't */
+                       printk("%s: Cyclades-PC300/", dev->name);
+                       switch (card->hw.type) {
+                               case PC300_TE:
+                                       if (card->hw.bus == PC300_PMC) {
+                                               printk("TE-M");
+                                       } else {
+                                               printk("TE  ");
+                                       }
+                                       break;
+
+                               case PC300_X21:
+                                       printk("X21 ");
+                                       break;
+
+                               case PC300_RSV:
+                               default:
+                                       printk("RSV ");
+                                       break;
+                       }
+                       printk (" #%d, %ldKB of RAM at 0x%08lx, IRQ%d, channel %d.\n",
+                                board_nbr, card->hw.ramsize / 1024,
+                                card->hw.ramphys, card->hw.irq, i + 1);
+                       devcount++;
+               } else {
+                       printk ("Dev%d on card(0x%08lx): unable to allocate i/f name.\n",
+                                i + 1, card->hw.ramphys);
+                       *(dev->name) = 0;
+                       kfree(d->hdlc);
+                       continue;
+               }
+       }
+       spin_lock_init(&card->card_lock);
+
+       board_nbr++;
+}
+
+static int __devinit
+cpc_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       static int first_time = 1;
+       ucchar cpc_rev_id;
+       int err = 0, eeprom_outdated = 0;
+       ucshort device_id;
+       pc300_t *card;
+
+       if (first_time) {
+               first_time = 0;
+               show_version();
+#ifdef CONFIG_PC300_MLPPP
+               cpc_tty_reset_var();
+#endif
+       }
+
+       card = (pc300_t *) kmalloc(sizeof(pc300_t), GFP_KERNEL);
+       if (card == NULL) {
+               printk("PC300 found at RAM 0x%08lx, "
+                      "but could not allocate card structure.\n",
+                      pci_resource_start(pdev, 3));
+               return -ENOMEM;
+       }
+       memset(card, 0, sizeof(pc300_t));
+
+       /* read PCI configuration area */
+       device_id = ent->device;
+       card->hw.irq = pdev->irq;
+       card->hw.iophys = pci_resource_start(pdev, 1);
+       card->hw.iosize = pci_resource_len(pdev, 1);
+       card->hw.scaphys = pci_resource_start(pdev, 2);
+       card->hw.scasize = pci_resource_len(pdev, 2);
+       card->hw.ramphys = pci_resource_start(pdev, 3);
+       card->hw.alloc_ramsize = pci_resource_len(pdev, 3);
+       card->hw.falcphys = pci_resource_start(pdev, 4);
+       card->hw.falcsize = pci_resource_len(pdev, 4);
+       card->hw.plxphys = pci_resource_start(pdev, 5);
+       card->hw.plxsize = pci_resource_len(pdev, 5);
+       pci_read_config_byte(pdev, PCI_REVISION_ID, &cpc_rev_id);
+
+       switch (device_id) {
+               case PCI_DEVICE_ID_PC300_RX_1:
+               case PCI_DEVICE_ID_PC300_TE_1:
+               case PCI_DEVICE_ID_PC300_TE_M_1:
+                       card->hw.nchan = 1;
+                       break;
+
+               case PCI_DEVICE_ID_PC300_RX_2:
+               case PCI_DEVICE_ID_PC300_TE_2:
+               case PCI_DEVICE_ID_PC300_TE_M_2:
+               default:
+                       card->hw.nchan = PC300_MAXCHAN;
+                       break;
+       }
+#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, card->hw.irq);
+       printk("cpc:found  ramaddr=0x%08lx plxaddr=0x%08lx "
+              "ctladdr=0x%08lx falcaddr=0x%08lx\n",
+              card->hw.ramphys, card->hw.plxphys, card->hw.scaphys,
+              card->hw.falcphys);
+#endif
+       /* 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. */
+       if (!request_region(card->hw.iophys, card->hw.iosize, "PLX Registers")) {
+               /* In case we can't allocate it, warn user */
+               printk("WARNING: couldn't allocate I/O region for PC300 board "
+                      "at 0x%08lx!\n", card->hw.ramphys);
+       }
+
+       if (card->hw.plxphys) {
+               pci_write_config_dword(pdev, PCI_BASE_ADDRESS_0, card->hw.plxphys);
+       } else {
+               eeprom_outdated = 1;
+               card->hw.plxphys = pci_resource_start(pdev, 0);
+               card->hw.plxsize = pci_resource_len(pdev, 0);
+       }
+
+       if (!request_mem_region(card->hw.plxphys, card->hw.plxsize,
+                               "PLX Registers")) {
+               printk("PC300 found at RAM 0x%08lx, "
+                      "but could not allocate PLX mem region.\n",
+                      card->hw.ramphys);
+               err = -ENODEV;
+               goto err_release_io;
+       }
+       if (!request_mem_region(card->hw.ramphys, card->hw.alloc_ramsize,
+                               "On-board RAM")) {
+               printk("PC300 found at RAM 0x%08lx, "
+                      "but could not allocate RAM mem region.\n",
+                      card->hw.ramphys);
+               err = -ENODEV;
+               goto err_release_plx;
+       }
+       if (!request_mem_region(card->hw.scaphys, card->hw.scasize,
+                               "SCA-II Registers")) {
+               printk("PC300 found at RAM 0x%08lx, "
+                      "but could not allocate SCA mem region.\n",
+                      card->hw.ramphys);
+               err = -ENODEV;
+               goto err_release_ram;
+       }
+
+       if ((err = pci_enable_device(pdev)) != 0)
+               goto err_release_sca;
+
+       card->hw.plxbase = (uclong) ioremap(card->hw.plxphys, card->hw.plxsize);
+       card->hw.rambase = (uclong) ioremap(card->hw.ramphys,
+                                           card->hw.alloc_ramsize);
+       card->hw.scabase = (uclong) ioremap(card->hw.scaphys, card->hw.scasize);
+       switch (device_id) {
+               case PCI_DEVICE_ID_PC300_TE_1:
+               case PCI_DEVICE_ID_PC300_TE_2:
+               case PCI_DEVICE_ID_PC300_TE_M_1:
+               case PCI_DEVICE_ID_PC300_TE_M_2:
+                       request_mem_region(card->hw.falcphys, card->hw.falcsize,
+                                          "FALC Registers");
+                       card->hw.falcbase = (uclong) ioremap(card->hw.falcphys,
+                                                            card->hw.falcsize);
+                       break;
+
+               case PCI_DEVICE_ID_PC300_RX_1:
+               case PCI_DEVICE_ID_PC300_RX_2:
+               default:
+                       card->hw.falcbase = 0;
+                       break;
+       }
+
+#ifdef PC300_DEBUG_PCI
+       printk("cpc: relocate ramaddr=0x%08lx plxaddr=0x%08lx "
+              "ctladdr=0x%08lx falcaddr=0x%08lx\n",
+              card->hw.rambase, card->hw.plxbase, card->hw.scabase,
+              card->hw.falcbase);
+#endif
+
+       /* Set PCI drv pointer to the card structure */
+       pdev->driver_data = card;
+
+       /* Set board type */
+       switch (device_id) {
+               case PCI_DEVICE_ID_PC300_TE_1:
+               case PCI_DEVICE_ID_PC300_TE_2:
+               case PCI_DEVICE_ID_PC300_TE_M_1:
+               case PCI_DEVICE_ID_PC300_TE_M_2:
+                       card->hw.type = PC300_TE;
+
+                       if ((device_id == PCI_DEVICE_ID_PC300_TE_M_1) ||
+                           (device_id == PCI_DEVICE_ID_PC300_TE_M_2)) {
+                               card->hw.bus = PC300_PMC;
+                               /* Set PLX register offsets */
+                               card->hw.gpioc_reg = 0x54;
+                               card->hw.intctl_reg = 0x4c;
+                       } else {
+                               card->hw.bus = PC300_PCI;
+                               /* Set PLX register offsets */
+                               card->hw.gpioc_reg = 0x50;
+                               card->hw.intctl_reg = 0x4c;
+                       }
+                       break;
+
+               case PCI_DEVICE_ID_PC300_RX_1:
+               case PCI_DEVICE_ID_PC300_RX_2:
+               default:
+                       card->hw.bus = PC300_PCI;
+                       /* Set PLX register offsets */
+                       card->hw.gpioc_reg = 0x50;
+                       card->hw.intctl_reg = 0x4c;
+
+                       if ((cpc_readl(card->hw.plxbase + card->hw.gpioc_reg) & PC300_CTYPE_MASK)) {
+                               card->hw.type = PC300_X21;
+                       } else {
+                               card->hw.type = PC300_RSV;
+                       }
+                       break;
+       }
+
+       /* Allocate IRQ */
+       if (request_irq(card->hw.irq, cpc_intr, SA_SHIRQ, "Cyclades-PC300", card)) {
+               printk ("PC300 found at RAM 0x%08lx, but could not allocate IRQ%d.\n",
+                        card->hw.ramphys, card->hw.irq);
+               goto err_io_unmap;
+       }
+
+       cpc_init_card(card);
+
+       if (eeprom_outdated)
+               printk("WARNING: PC300 with outdated EEPROM.\n");
+       return 0;
+
+err_io_unmap:
+       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);
+               release_mem_region(card->hw.falcphys, card->hw.falcsize);
+       }
+err_release_sca:
+       release_mem_region(card->hw.scaphys, card->hw.scasize);
+err_release_ram:
+       release_mem_region(card->hw.ramphys, card->hw.alloc_ramsize);
+err_release_plx:
+       release_mem_region(card->hw.plxphys, card->hw.plxsize);
+err_release_io:
+       release_region(card->hw.iophys, card->hw.iosize);
+       kfree(card);
+       return -ENODEV;
+}
+
+static void __devexit cpc_remove_one(struct pci_dev *pdev)
+{
+       pc300_t *card = (pc300_t *) pdev->driver_data;
+
+       if (card->hw.rambase != 0) {
+               int i;
+
+               /* Disable interrupts on the PCI bridge */
+               cpc_writew(card->hw.plxbase + card->hw.intctl_reg,
+                          cpc_readw(card->hw.plxbase + card->hw.intctl_reg) & ~(0x0040));
+
+               for (i = 0; i < card->hw.nchan; i++) {
+                       unregister_hdlc_device(card->chan[i].d.hdlc);
+               }
+               iounmap((void *) card->hw.plxbase);
+               iounmap((void *) card->hw.scabase);
+               iounmap((void *) card->hw.rambase);
+               release_mem_region(card->hw.plxphys, card->hw.plxsize);
+               release_mem_region(card->hw.ramphys, card->hw.alloc_ramsize);
+               release_mem_region(card->hw.scaphys, card->hw.scasize);
+               release_region(card->hw.iophys, card->hw.iosize);
+               if (card->hw.type == PC300_TE) {
+                       iounmap((void *) card->hw.falcbase);
+                       release_mem_region(card->hw.falcphys, card->hw.falcsize);
+               }
+               if (card->hw.irq)
+                       free_irq(card->hw.irq, card);
+               kfree(card);
+       }
+}
+
+static struct pci_driver cpc_driver = {
+       name:"pc300",
+       id_table:cpc_pci_dev_id,
+       probe:cpc_init_one,
+       remove:cpc_remove_one,
+       suspend:NULL,
+       resume:NULL,
+};
+
+static int __init cpc_init(void)
+{
+       return pci_module_init(&cpc_driver);
+}
+
+static void __exit cpc_cleanup_module(void)
+{
+       pci_unregister_driver(&cpc_driver);
+}
+
+module_init(cpc_init);
+module_exit(cpc_cleanup_module);
diff --git a/drivers/net/wan/pc300_tty.c b/drivers/net/wan/pc300_tty.c
new file mode 100644 (file)
index 0000000..d179244
--- /dev/null
@@ -0,0 +1,1146 @@
+/*
+ * pc300_tty.c Cyclades-PC300(tm) TTY Driver.
+ *
+ * Author:     Regina Kodato <reginak@cyclades.com>
+ *
+ * Copyright:  (c) 1999-2002 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_tty.c,v $
+ *  Revision 3.7  2002/03/07 14:17:09  henrique
+ *  License data fixed
+ *
+ *  Revision 3.6  2001/12/10 12:29:42  regina
+ *  Fix the MLPPP bug
+ *
+ *  Revision 3.5  2001/10/31 11:20:05  regina
+ *  automatic pppd starts
+ *
+ *  Revision 3.4  2001/08/06 12:01:51  regina
+ *  problem in DSR_DE bit
+ *
+ *  Revision 3.3  2001/07/26 22:58:41  regina
+ *  update EDA value
+ *
+ *  Revision 3.2  2001/07/12 13:11:20  regina
+ *  bug fix - DCD-OFF in pc300 tty driver
+ *
+ *     DMA transmission bug fix
+ *  
+ *  Revision 3.1  2001/06/22 13:13:02  regina
+ *  MLPPP implementation
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/netdevice.h>
+#include <linux/spinlock.h>
+#include <linux/malloc.h>
+#include <linux/if.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/skbuff.h>
+
+/* TTY includes */
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+
+#include "pc300.h"
+
+/* defines and macros */
+/* TTY Global definitions */
+#define        CPC_TTY_NPORTS  8       /* maximum number of the sync tty connections */
+#define        CPC_TTY_MAJOR   CYCLADES_MAJOR  
+#define CPC_TTY_MINOR_START    240     /* minor of the first PC300 interface */
+
+#define CPC_TTY_MAX_MTU        2000    
+
+/* tty interface state */
+#define        CPC_TTY_ST_IDLE 0
+#define CPC_TTY_ST_INIT        1       /* configured with MLPPP and up */
+#define CPC_TTY_ST_OPEN        2       /* opened by application */
+
+#define        CPC_TTY_LOCK(card,flags)\
+       do {\
+               spin_lock_irqsave(&card->card_lock, flags);     \
+       } while (0)
+
+#define CPC_TTY_UNLOCK(card,flags)     \
+       do {\
+               spin_unlock_irqrestore(&card->card_lock, flags);        \
+       } while (0)
+
+//#define      CPC_TTY_DBG(format,a...)        printk(format,##a)
+#define        CPC_TTY_DBG(format,a...)
+
+/* data structures */
+typedef struct _st_cpc_rx_buf {
+       struct _st_cpc_rx_buf   *next;
+       int             size;
+       unsigned char   data[1];
+} st_cpc_rx_buf;
+
+struct st_cpc_rx_list {
+       st_cpc_rx_buf   *first;
+       st_cpc_rx_buf   *last;
+};
+
+typedef        struct _st_cpc_tty_area {
+       int             state;          /* state of the TTY interface */
+       int             num_open;       
+       unsigned int    tty_minor;      /* minor this interface */
+       volatile struct st_cpc_rx_list buf_rx;  /* ptr. to reception buffer */
+       unsigned char*  buf_tx;         /* ptr. to transmission buffer */
+       pc300dev_t*     pc300dev;       /* ptr. to info struct in PC300 driver */
+       unsigned char   name[20];       /* interf. name + "-tty" */
+       struct tty_struct *tty;         
+       struct tq_struct tty_tx_task_queue; /* tx task - tx interrupt */
+       struct tq_struct tty_rx_task_queue; /* rx task - rx interrupt */
+       } st_cpc_tty_area;
+
+/* TTY data structures */
+static struct tty_struct *cpc_tty_serial_table[CPC_TTY_NPORTS];
+static struct termios *cpc_tty_serial_termios[CPC_TTY_NPORTS];
+static struct termios *cpc_tty_serial_termios_locked[CPC_TTY_NPORTS];
+static struct tty_driver serial_drv, callout_drv;
+
+/* local variables */
+st_cpc_tty_area        cpc_tty_area[CPC_TTY_NPORTS];
+
+int cpc_tty_cnt=0;     /* number of intrfaces configured with MLPPP */
+int cpc_tty_refcount;
+int cpc_tty_unreg_flag = 0;
+
+/* TTY functions prototype */
+static int cpc_tty_open(struct tty_struct *tty, struct file *flip);
+static void cpc_tty_close(struct tty_struct *tty, struct file *flip);
+static int cpc_tty_write(struct tty_struct *tty, int from_user,
+                               const unsigned char *buf, int count);
+static int cpc_tty_write_room(struct tty_struct *tty);
+static int cpc_tty_chars_in_buffer(struct tty_struct *tty);
+static int cpc_tty_ioctl(struct tty_struct *tty, struct file *file,
+                               unsigned int cmd, unsigned long arg);
+static void cpc_tty_flush_buffer(struct tty_struct *tty);
+static void cpc_tty_hangup(struct tty_struct *tty);
+static void cpc_tty_rx_task(void *data);
+static void cpc_tty_tx_task(void *data);
+static int cpc_tty_send_to_card(pc300dev_t *dev,void *buf, int len);
+static void cpc_tty_trace(pc300dev_t *dev, char* buf, int len, char rxtx);
+static void cpc_tty_dtr_off(pc300dev_t *pc300dev);
+static void cpc_tty_dtr_on(pc300dev_t *pc300dev);
+
+/* functions called by PC300 driver */
+void cpc_tty_init(pc300dev_t *dev);
+void cpc_tty_unregister_service(pc300dev_t *pc300dev);
+void cpc_tty_receive(pc300dev_t *pc300dev);
+void cpc_tty_trigger_poll(pc300dev_t *pc300dev);
+void cpc_tty_reset_var(void);
+
+/*
+ * PC300 TTY clear DTR signal
+ */
+static void cpc_tty_dtr_off(pc300dev_t *pc300dev)
+{
+       pc300ch_t *pc300chan = (pc300ch_t *)pc300dev->chan; 
+       pc300_t *card = (pc300_t *) pc300chan->card; 
+       int ch = pc300chan->channel; 
+       unsigned long flags; 
+
+       CPC_TTY_DBG("%s-tty: Clear signal DTR\n",
+               ((struct net_device*)(pc300dev->hdlc))->name);
+       CPC_TTY_LOCK(card, flags); 
+       cpc_writeb(card->hw.scabase + M_REG(CTL,ch), 
+               cpc_readb(card->hw.scabase+M_REG(CTL,ch))& CTL_DTR); 
+       CPC_TTY_UNLOCK(card,flags); 
+}
+
+/*
+ * PC300 TTY set DTR signal to ON
+ */
+static void cpc_tty_dtr_on(pc300dev_t *pc300dev)
+{
+       pc300ch_t *pc300chan = (pc300ch_t *)pc300dev->chan; 
+       pc300_t *card = (pc300_t *) pc300chan->card; 
+       int ch = pc300chan->channel; 
+       unsigned long flags; 
+
+       CPC_TTY_DBG("%s-tty: Set signal DTR\n",
+               ((struct net_device*)(pc300dev->hdlc))->name);
+       CPC_TTY_LOCK(card, flags); 
+       cpc_writeb(card->hw.scabase + M_REG(CTL,ch), 
+               cpc_readb(card->hw.scabase+M_REG(CTL,ch))& ~CTL_DTR); 
+       CPC_TTY_UNLOCK(card,flags); 
+}
+
+/*
+ * PC300 TTY initialization routine
+ *
+ * This routine is called by the PC300 driver during board configuration 
+ * (ioctl=SIOCSP300CONF). At this point the adapter is completely
+ * initialized.
+ * o verify kernel version (only 2.4.x)
+ * o register TTY driver
+ * o init cpc_tty_area struct
+ */
+void cpc_tty_init(pc300dev_t *pc300dev)
+{
+       int port, aux;
+       st_cpc_tty_area * cpc_tty;
+
+       if (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)) {
+               printk("%s-tty: Error: TTY driver is supported on 2.4.X kernel!\n",
+                       ((struct net_device*)(pc300dev->hdlc))->name);
+               return;
+       }
+       
+       /* hdlcX - X=interface number */
+       port = ((struct net_device*)(pc300dev->hdlc))->name[4] - '0';
+       if (port >= CPC_TTY_NPORTS) {
+               printk("%s-tty: invalid interface selected (0-%i): %i", 
+                       ((struct net_device*)(pc300dev->hdlc))->name,
+                       CPC_TTY_NPORTS-1,port);
+               return;
+       }
+
+       if (cpc_tty_cnt == 0) { /* first TTY connection -> register driver */
+               CPC_TTY_DBG("%s-tty: driver init, major:%i, minor range:%i=%i\n",
+                       ((struct net_device*)(pc300dev->hdlc))->name,
+                       CPC_TTY_MAJOR, CPC_TTY_MINOR_START,
+                       CPC_TTY_MINOR_START+CPC_TTY_NPORTS);
+               /* initialize tty driver struct */
+               memset(&serial_drv,0,sizeof(struct tty_driver));
+               serial_drv.magic = TTY_DRIVER_MAGIC;
+               serial_drv.driver_name = "pc300_tty";
+               serial_drv.name = "ttyCP";
+               serial_drv.major = CPC_TTY_MAJOR;
+               serial_drv.minor_start = CPC_TTY_MINOR_START;
+               serial_drv.num = CPC_TTY_NPORTS;
+               serial_drv.type = TTY_DRIVER_TYPE_SERIAL;
+               serial_drv.subtype = SERIAL_TYPE_NORMAL;
+
+               serial_drv.init_termios = tty_std_termios;
+               serial_drv.init_termios.c_cflag = B9600|CS8|CREAD|HUPCL|CLOCAL;
+               serial_drv.flags = TTY_DRIVER_REAL_RAW;
+               serial_drv.refcount = &cpc_tty_refcount;
+
+               /* tty data structures */
+               serial_drv.table = cpc_tty_serial_table;
+               serial_drv.termios = cpc_tty_serial_termios;
+               serial_drv.termios_locked = cpc_tty_serial_termios_locked;
+
+               /* interface routines from the upper tty layer to the tty driver */
+               serial_drv.open = cpc_tty_open;
+               serial_drv.close = cpc_tty_close;
+               serial_drv.write = cpc_tty_write; 
+               serial_drv.write_room = cpc_tty_write_room; 
+               serial_drv.chars_in_buffer = cpc_tty_chars_in_buffer; 
+               serial_drv.ioctl = cpc_tty_ioctl; 
+               serial_drv.flush_buffer = cpc_tty_flush_buffer; 
+               serial_drv.hangup = cpc_tty_hangup;
+
+               /* the callout device is just like normal device except for major */
+               /* number and the subtype code */ 
+               callout_drv = serial_drv; 
+               callout_drv.name = "cucp"; 
+               callout_drv.major = CPC_TTY_MAJOR + 1;
+               callout_drv.subtype = SERIAL_TYPE_CALLOUT;
+               callout_drv.read_proc = 0;
+               callout_drv.proc_entry = 0; 
+               
+               /* register the TTY driver */
+               if (tty_register_driver(&serial_drv)) { 
+                       printk("%s-tty: Failed to register serial driver! ",
+                               ((struct net_device*)(pc300dev->hdlc))->name);
+                       return;
+               } 
+
+               if (tty_register_driver(&callout_drv)) {
+                       CPC_TTY_DBG("%s-tty: Failed to register callout driver! ",
+                               ((struct net_device*)(pc300dev->hdlc))->name);
+                       return;
+               }
+               memset((void *)cpc_tty_area, 0,
+                                                               sizeof(st_cpc_tty_area) * CPC_TTY_NPORTS);
+       }
+
+       cpc_tty = &cpc_tty_area[port];
+       
+       if (cpc_tty->state != CPC_TTY_ST_IDLE) {
+               CPC_TTY_DBG("%s-tty: TTY port %i, already in use.\n",
+                                       ((struct net_device*)(pc300dev->hdlc))->name,port);
+               return;
+       }
+
+       cpc_tty_cnt++;
+       cpc_tty->state = CPC_TTY_ST_INIT; 
+       cpc_tty->num_open= 0;
+       cpc_tty->tty_minor = port + CPC_TTY_MINOR_START;
+       cpc_tty->pc300dev = pc300dev; 
+       
+       cpc_tty->tty_tx_task_queue.routine = cpc_tty_tx_task; 
+       cpc_tty->tty_tx_task_queue.data = (void *)cpc_tty; 
+       
+       cpc_tty->tty_rx_task_queue.routine = cpc_tty_rx_task; 
+       cpc_tty->tty_rx_task_queue.data = (void *) port; 
+       
+       cpc_tty->buf_rx.first = cpc_tty->buf_rx.last = 0;
+
+       pc300dev->cpc_tty = (void *)cpc_tty; 
+       
+       aux = strlen(((struct net_device*)(pc300dev->hdlc))->name);
+       memcpy(cpc_tty->name,((struct net_device*)(pc300dev->hdlc))->name,aux);
+       memcpy(&cpc_tty->name[aux], "-tty", 5);
+       
+       cpc_open((struct net_device *)pc300dev->hdlc);
+       cpc_tty_dtr_off(pc300dev);
+
+       CPC_TTY_DBG("%s: Initializing TTY Sync Driver, tty major#%d minor#%i\n",
+                       cpc_tty->name,CPC_TTY_MAJOR,cpc_tty->tty_minor); 
+       return; 
+} 
+
+/*
+ * PC300 TTY OPEN routine
+ *
+ * This routine is called by the tty driver to open the interface 
+ * o verify minor
+ * o allocate buffer to Rx and Tx
+ */
+static int cpc_tty_open(struct tty_struct *tty, struct file *flip)
+{
+       int port ;
+       st_cpc_tty_area *cpc_tty;
+
+       if (!tty) { 
+               return -ENODEV;
+       } 
+
+       port = minor(tty->device) - tty->driver.minor_start; 
+
+       if ((port < 0) || (port >= CPC_TTY_NPORTS)){ 
+               CPC_TTY_DBG("pc300_tty: open invalid minor %i\n",MINOR(tty->device));
+               return -ENODEV;
+       } 
+
+       cpc_tty = &cpc_tty_area[port];
+       
+       if (cpc_tty->state == CPC_TTY_ST_IDLE){
+               CPC_TTY_DBG("%s: open - invalid interface, minor=%i\n",
+                                       cpc_tty->name, MINOR(tty->device));
+               return -ENODEV;
+       }
+
+       if (cpc_tty->num_open == 0) { /* first open of this tty */
+               if (!cpc_tty_area[port].buf_tx){
+                       cpc_tty_area[port].buf_tx = kmalloc(CPC_TTY_MAX_MTU,GFP_KERNEL);
+                       if (cpc_tty_area[port].buf_tx == 0){
+                               CPC_TTY_DBG("%s: error in memory allocation\n",cpc_tty->name);
+                               return -ENOMEM;
+                       }
+               } 
+
+               if (cpc_tty_area[port].buf_rx.first) {
+                       unsigned char * aux;
+                       while (cpc_tty_area[port].buf_rx.first) {
+                               aux = (unsigned char *)cpc_tty_area[port].buf_rx.first;
+                               cpc_tty_area[port].buf_rx.first = cpc_tty_area[port].buf_rx.first->next;
+                               kfree(aux);
+                       }
+                       cpc_tty_area[port].buf_rx.first = NULL;
+                       cpc_tty_area[port].buf_rx.last = NULL;
+               }
+
+               cpc_tty_area[port].state = CPC_TTY_ST_OPEN;
+               cpc_tty_area[port].tty = tty;
+               tty->driver_data = &cpc_tty_area[port];
+
+               cpc_tty_dtr_on(cpc_tty->pc300dev);
+       } 
+
+       cpc_tty->num_open++;
+
+       CPC_TTY_DBG("%s: opening TTY driver\n", cpc_tty->name);
+       
+       /* avisar driver PC300 */ 
+       return 0; 
+}
+
+/*
+ * PC300 TTY CLOSE routine
+ *
+ * This routine is called by the tty driver to close the interface 
+ * o call close channel in PC300 driver (cpc_closech)
+ * o free Rx and Tx buffers
+ */
+
+static void cpc_tty_close(struct tty_struct *tty, struct file *flip)
+{
+       st_cpc_tty_area    *cpc_tty;
+       unsigned long flags;
+       int res;
+
+       if (!tty || !tty->driver_data ) {
+               CPC_TTY_DBG("hdlx-tty: no TTY in close \n");
+               return;
+       }
+
+       cpc_tty = (st_cpc_tty_area *) tty->driver_data;
+
+       if ((cpc_tty->tty != tty)|| (cpc_tty->state != CPC_TTY_ST_OPEN)) {
+               CPC_TTY_DBG("%s: TTY is not opened\n",cpc_tty->name);
+               return;
+       }
+       
+       if (!cpc_tty->num_open) {
+               CPC_TTY_DBG("%s: TTY is closed\n",cpc_tty->name);
+               return;
+       }
+
+       if (--cpc_tty->num_open > 0) {
+               CPC_TTY_DBG("%s: TTY closed\n",cpc_tty->name);
+               return;
+       }
+
+       cpc_tty_dtr_off(cpc_tty->pc300dev);
+
+       CPC_TTY_LOCK(cpc_tty->pc300dev->chan->card, flags);  /* lock irq */ 
+       cpc_tty->tty = NULL;
+       cpc_tty->state = CPC_TTY_ST_INIT;
+       CPC_TTY_UNLOCK(cpc_tty->pc300dev->chan->card, flags); /* unlock irq */ 
+       
+       if (cpc_tty->buf_rx.first) {
+               unsigned char * aux;
+               while (cpc_tty->buf_rx.first) {
+                       aux = (unsigned char *)cpc_tty->buf_rx.first;
+                       cpc_tty->buf_rx.first = cpc_tty->buf_rx.first->next;
+                       kfree(aux);
+               }
+               cpc_tty->buf_rx.first = NULL;
+               cpc_tty->buf_rx.last = NULL;
+       }
+       
+       if (cpc_tty->buf_tx) {
+               kfree(cpc_tty->buf_tx);
+               cpc_tty->buf_tx = NULL;
+       }
+
+       CPC_TTY_DBG("%s: TTY closed\n",cpc_tty->name);
+       
+       if (!cpc_tty_refcount && cpc_tty_unreg_flag) {
+               cpc_tty_unreg_flag = 0;
+               CPC_TTY_DBG("%s: unregister the tty driver\n", cpc_tty->name);
+               if ((res=tty_unregister_driver(&serial_drv))) { 
+                       CPC_TTY_DBG("%s: ERROR ->unregister the tty driver error=%d\n",
+                                                       cpc_tty->name,res);
+               }
+               if ((res=tty_unregister_driver(&callout_drv))) { 
+                       CPC_TTY_DBG("%s: ERROR ->unregister the tty driver error=%d\n",
+                                                       cpc_tty->name,res);
+               }
+       }
+       return; 
+} 
+
+/*
+ * PC300 TTY WRITE routine
+ *
+ * This routine is called by the tty driver to write a series of characters
+ * to the tty device. The characters may come from user or kernel space.
+ * o verify the DCD signal
+ * o send characters to board and start the transmission
+ */
+static int cpc_tty_write(struct tty_struct *tty, int from_user,
+                       const unsigned char *buf, int count)
+{
+       st_cpc_tty_area    *cpc_tty; 
+       pc300ch_t *pc300chan; 
+       pc300_t *card; 
+       int ch; 
+       unsigned long flags; 
+       struct net_device_stats *stats; 
+
+       if (!tty || !tty->driver_data ) { 
+               CPC_TTY_DBG("hdlcX-tty: no TTY in write\n");
+               return -ENODEV;
+       } 
+
+       cpc_tty = (st_cpc_tty_area *) tty->driver_data; 
+
+       if ((cpc_tty->tty != tty) ||  (cpc_tty->state != CPC_TTY_ST_OPEN)) { 
+               CPC_TTY_DBG("%s: TTY is not opened\n", cpc_tty->name);
+               return -ENODEV; 
+       }
+
+       if (count > CPC_TTY_MAX_MTU) { 
+               CPC_TTY_DBG("%s: count is invalid\n",cpc_tty->name);
+               return -EINVAL;        /* frame too big */ 
+       }
+
+       CPC_TTY_DBG("%s: cpc_tty_write %s data len=%i\n",cpc_tty->name,
+               (from_user)?"from user" : "from kernel",count);
+       
+       pc300chan = (pc300ch_t *)((pc300dev_t*)cpc_tty->pc300dev)->chan; 
+       stats = &((pc300dev_t*)cpc_tty->pc300dev)->hdlc->stats;
+       card = (pc300_t *) pc300chan->card;
+       ch = pc300chan->channel; 
+
+       /* verify DCD signal*/ 
+       if (cpc_readb(card->hw.scabase + M_REG(ST3,ch)) & ST3_DCD) { 
+               /* DCD is OFF */ 
+               CPC_TTY_DBG("%s : DCD is OFF\n", cpc_tty->name);
+               stats->tx_errors++;
+               stats->tx_carrier_errors++;
+               CPC_TTY_LOCK(card, flags); 
+               cpc_writeb(card->hw.scabase + M_REG(CMD, ch), CMD_TX_BUF_CLR); 
+               
+               if (card->hw.type == PC300_TE) { 
+                       cpc_writeb(card->hw.falcbase + card->hw.cpld_reg2, 
+                               cpc_readb(card->hw.falcbase + card->hw.cpld_reg2) & 
+                               ~(CPLD_REG2_FALC_LED1 << (2 *ch))); 
+               }
+
+               CPC_TTY_UNLOCK(card, flags); 
+
+               return -EINVAL; 
+       }
+
+       if (from_user) { 
+               unsigned char *buf_tmp; 
+
+               buf_tmp = cpc_tty->buf_tx;
+               if (copy_from_user(buf_tmp, buf, count)) { 
+                       /* failed to copy from user */
+                       CPC_TTY_DBG("%s: error in copy from user\n",cpc_tty->name);
+                       return -EINVAL; 
+               }
+
+               if (cpc_tty_send_to_card(cpc_tty->pc300dev, (void*) buf_tmp,count)) { 
+                       /* failed to send */ 
+                       CPC_TTY_DBG("%s: transmission error\n",cpc_tty->name);
+                       return 0; 
+               }
+       } else {
+               if (cpc_tty_send_to_card(cpc_tty->pc300dev, (void*)buf, count)) { 
+                  /* failed to send */
+                  CPC_TTY_DBG("%s: trasmition error\n", cpc_tty->name);
+                  return 0;
+               }
+       }
+       return count; 
+} 
+
+/*
+ * PC300 TTY Write Room routine
+ * 
+ * This routine returns the numbers of characteres the tty driver will accept
+ * for queuing to be written. 
+ * o return MTU
+ */
+static int cpc_tty_write_room(struct tty_struct *tty)
+{
+       st_cpc_tty_area    *cpc_tty; 
+
+       if (!tty || !tty->driver_data ) { 
+               CPC_TTY_DBG("hdlcX-tty: no TTY to write room\n");
+               return -ENODEV;
+       }
+
+       cpc_tty = (st_cpc_tty_area *) tty->driver_data; 
+
+       if ((cpc_tty->tty != tty) ||  (cpc_tty->state != CPC_TTY_ST_OPEN)) { 
+               CPC_TTY_DBG("%s: TTY is not opened\n",cpc_tty->name);
+               return -ENODEV; 
+       }
+       
+       CPC_TTY_DBG("%s: write room\n",cpc_tty->name);
+       
+       return CPC_TTY_MAX_MTU;
+} 
+
+/*
+ * PC300 TTY chars in buffer routine
+ * 
+ * This routine returns the chars number in the transmission buffer 
+ * o returns 0
+ */
+static int cpc_tty_chars_in_buffer(struct tty_struct *tty)
+{
+       st_cpc_tty_area    *cpc_tty; 
+
+       if (!tty || !tty->driver_data ) {
+               CPC_TTY_DBG("hdlcX-tty: no TTY to chars in buffer\n");
+               return -ENODEV; 
+       }
+
+       cpc_tty = (st_cpc_tty_area *) tty->driver_data; 
+
+       if ((cpc_tty->tty != tty) || (cpc_tty->state != CPC_TTY_ST_OPEN)) { 
+               CPC_TTY_DBG("%s: TTY is not opened\n",cpc_tty->name);
+               return -ENODEV; 
+       }
+   
+       return(0); 
+} 
+
+/*
+ * PC300 TTY IOCTL routine
+ *
+ * This routine treats TIOCMBIS (set DTR signal) and TIOCMBIC (clear DTR
+ * signal)IOCTL commands. 
+ */
+static int cpc_tty_ioctl(struct tty_struct *tty, struct file *file,
+               unsigned int cmd, unsigned long arg)
+
+{
+       st_cpc_tty_area    *cpc_tty; 
+
+       if (!tty || !tty->driver_data ) {
+               CPC_TTY_DBG("hdlcX-tty: no TTY to chars in buffer\n");  
+               return -ENODEV; 
+       }
+
+       cpc_tty = (st_cpc_tty_area *) tty->driver_data; 
+
+       if ((cpc_tty->tty != tty) ||  (cpc_tty->state != CPC_TTY_ST_OPEN)) { 
+               CPC_TTY_DBG("%s: TTY is not opened\n",cpc_tty->name);
+               return -ENODEV; 
+       }
+
+       CPC_TTY_DBG("%s: IOCTL cmd %x\n",cpc_tty->name,cmd);
+       
+       switch (cmd) { 
+               case TIOCMBIS :    /* set DTR */
+                       cpc_tty_dtr_on(cpc_tty->pc300dev);
+                       break; 
+
+               case TIOCMBIC:     /* clear DTR */ 
+                       cpc_tty_dtr_off(cpc_tty->pc300dev);
+                       break; 
+               default :
+                       return -ENOIOCTLCMD;
+       }
+       return 0;
+} 
+
+/*
+ * PC300 TTY Flush Buffer routine
+ *
+ * This routine resets the transmission buffer 
+ */
+static void cpc_tty_flush_buffer(struct tty_struct *tty)
+{ 
+       st_cpc_tty_area    *cpc_tty; 
+       
+       if (!tty || !tty->driver_data ) {
+               CPC_TTY_DBG("hdlcX-tty: no TTY to flush buffer\n");     
+               return; 
+       }
+
+       cpc_tty = (st_cpc_tty_area *) tty->driver_data; 
+
+       if ((cpc_tty->tty != tty) ||  (cpc_tty->state != CPC_TTY_ST_OPEN)) { 
+               CPC_TTY_DBG("%s: TTY is not opened\n",cpc_tty->name);
+               return; 
+       }
+
+       CPC_TTY_DBG("%s: call wake_up_interruptible\n",cpc_tty->name);
+       
+       wake_up_interruptible(&tty->write_wait); 
+
+       if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup){
+               CPC_TTY_DBG("%s: call line disc. wake up\n",cpc_tty->name);
+               tty->ldisc.write_wakeup(tty); 
+       } 
+
+       return; 
+} 
+
+/*
+ * PC300 TTY Hangup routine
+ *
+ * This routine is called by the tty driver to hangup the interface 
+ * o clear DTR signal
+ */
+
+static void cpc_tty_hangup(struct tty_struct *tty)
+{ 
+       st_cpc_tty_area    *cpc_tty; 
+       int res;
+
+       if (!tty || !tty->driver_data ) {
+               CPC_TTY_DBG("hdlcX-tty: no TTY to hangup\n");   
+               return ; 
+       }
+
+       cpc_tty = (st_cpc_tty_area *) tty->driver_data; 
+
+       if ((cpc_tty->tty != tty) || (cpc_tty->state != CPC_TTY_ST_OPEN)) {
+               CPC_TTY_DBG("%s: TTY is not opened\n",cpc_tty->name);
+               return ;
+       }
+       if (!cpc_tty_refcount && cpc_tty_unreg_flag) {
+               cpc_tty_unreg_flag = 0;
+               CPC_TTY_DBG("%s: unregister the tty driver\n", cpc_tty->name);
+               if ((res=tty_unregister_driver(&serial_drv))) { 
+                       CPC_TTY_DBG("%s: ERROR ->unregister the tty driver error=%d\n",
+                                                       cpc_tty->name,res);
+               }
+               if ((res=tty_unregister_driver(&callout_drv))) { 
+                       CPC_TTY_DBG("%s: ERROR ->unregister the tty driver error=%d\n",
+                                                       cpc_tty->name,res);
+               }
+       }
+       cpc_tty_dtr_off(cpc_tty->pc300dev);
+}
+
+/*
+ * PC300 TTY RX task routine
+ * This routine treats RX task
+ * o verify read buffer
+ * o call the line disc. read
+ * o free memory
+ */
+static void cpc_tty_rx_task(void * data)
+{
+       int port, i, j;
+       st_cpc_tty_area *cpc_tty; 
+       volatile st_cpc_rx_buf * buf;
+       char flags=0,flg_rx=1; 
+
+       if (cpc_tty_cnt == 0) return;
+
+       for (i=0; (i < 4) && flg_rx ; i++) {
+               flg_rx = 0;
+               port = (int) data;
+               for (j=0; j < CPC_TTY_NPORTS; j++) {
+                       cpc_tty = &cpc_tty_area[port];
+               
+                       if ((buf=cpc_tty->buf_rx.first) != 0) {
+                                                                                                                       
+                               if (cpc_tty->tty && (cpc_tty->tty->ldisc.receive_buf)) { 
+                                       CPC_TTY_DBG("%s: call line disc. receive_buf\n",cpc_tty->name);
+                                       cpc_tty->tty->ldisc.receive_buf(cpc_tty->tty, buf->data, 
+                                                                                                       &flags, buf->size);
+                               }       
+                               cpc_tty->buf_rx.first = cpc_tty->buf_rx.first->next;
+                               kfree((unsigned char *)buf);
+                               buf = cpc_tty->buf_rx.first;
+                               flg_rx = 1;
+                       }
+                       if (++port == CPC_TTY_NPORTS) port = 0;
+               }
+       }
+} 
+
+/*
+ * PC300 TTY RX task routine
+ *
+ * This routine treats RX interrupt. 
+ * o read all frames in card
+ * o verify the frame size
+ * o read the frame in rx buffer
+ */
+static void cpc_tty_rx_disc_frame(pc300ch_t *pc300chan)
+{
+       volatile pcsca_bd_t * ptdescr; 
+       volatile unsigned char status; 
+       pc300_t *card = (pc300_t *)pc300chan->card; 
+       int ch = pc300chan->channel; 
+
+       /* dma buf read */ 
+       ptdescr = (pcsca_bd_t *)(card->hw.rambase + 
+                               RX_BD_ADDR(ch, pc300chan->rx_first_bd)); 
+       while (pc300chan->rx_first_bd != pc300chan->rx_last_bd) { 
+               status = cpc_readb(&ptdescr->status); 
+               cpc_writeb(&ptdescr->status, 0); 
+               cpc_writeb(&ptdescr->len, 0); 
+               pc300chan->rx_first_bd = (pc300chan->rx_first_bd + 1) & 
+                                       (N_DMA_RX_BUF - 1); 
+               if (status & DST_EOM) { 
+                       break; /* end of message */
+               }
+               ptdescr = (pcsca_bd_t *)(card->hw.rambase + cpc_readl(&ptdescr->next)); 
+       }
+}
+
+void cpc_tty_receive(pc300dev_t *pc300dev)
+{
+       st_cpc_tty_area    *cpc_tty; 
+       pc300ch_t *pc300chan = (pc300ch_t *)pc300dev->chan; 
+       pc300_t *card = (pc300_t *)pc300chan->card; 
+       int ch = pc300chan->channel; 
+       volatile pcsca_bd_t * ptdescr; 
+       struct net_device_stats *stats = &pc300dev->hdlc->stats; 
+       int rx_len, rx_aux; 
+       volatile unsigned char status; 
+       unsigned short first_bd = pc300chan->rx_first_bd;
+       st_cpc_rx_buf   *new;
+       unsigned char dsr_rx;
+
+       if (pc300dev->cpc_tty == NULL) { 
+               return; 
+       }
+
+       dsr_rx = cpc_readb(card->hw.scabase + DSR_RX(ch));
+
+       cpc_tty = (st_cpc_tty_area *)pc300dev->cpc_tty; 
+
+       while (1) { 
+               rx_len = 0;
+               ptdescr = (pcsca_bd_t *)(card->hw.rambase + RX_BD_ADDR(ch, first_bd));
+               while ((status = cpc_readb(&ptdescr->status)) & DST_OSB) {
+                       rx_len += cpc_readw(&ptdescr->len);
+                       first_bd = (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 (!rx_len) { 
+                       if (dsr_rx & DSR_BOF) {
+                               /* update EDA */ 
+                               cpc_writel(card->hw.scabase + DRX_REG(EDAL, ch), 
+                                               RX_BD_ADDR(ch, pc300chan->rx_last_bd)); 
+                       }
+                       return; 
+               }
+               
+               if (rx_len > CPC_TTY_MAX_MTU) { 
+                       /* Free RX descriptors */ 
+                       CPC_TTY_DBG("%s: frame size is invalid.\n",cpc_tty->name);
+                       stats->rx_errors++; 
+                       stats->rx_frame_errors++; 
+                       cpc_tty_rx_disc_frame(pc300chan);
+                       continue;
+               } 
+               
+               new = (st_cpc_rx_buf *) kmalloc(rx_len + sizeof(st_cpc_rx_buf), GFP_ATOMIC);
+               if (new == 0) {
+                       cpc_tty_rx_disc_frame(pc300chan);
+                       continue;
+               }
+               
+               /* dma buf read */ 
+               ptdescr = (pcsca_bd_t *)(card->hw.rambase + 
+                               RX_BD_ADDR(ch, pc300chan->rx_first_bd)); 
+
+               rx_len = 0;     /* counter frame size */
+               
+               while ((status = cpc_readb(&ptdescr->status)) & DST_OSB) {
+                       rx_aux = cpc_readw(&ptdescr->len);
+                       if ((status & (DST_OVR | DST_CRC | DST_RBIT |  DST_SHRT | DST_ABT))
+                               || (rx_aux > BD_DEF_LEN)) {
+                               CPC_TTY_DBG("%s: reception error\n", cpc_tty->name);
+                               stats->rx_errors++; 
+                               if (status & DST_OVR) { 
+                                       stats->rx_fifo_errors++; 
+                               }
+                               if (status & DST_CRC) { 
+                                       stats->rx_crc_errors++; 
+                               }
+                               if ((status & (DST_RBIT | DST_SHRT | DST_ABT)) ||
+                                       (rx_aux > BD_DEF_LEN))  { 
+                                       stats->rx_frame_errors++; 
+                               } 
+                               /* discard remainig descriptors used by the bad frame */ 
+                               CPC_TTY_DBG("%s: reception error - discard descriptors",
+                                               cpc_tty->name);
+                               cpc_tty_rx_disc_frame(pc300chan);
+                               rx_len = 0;
+                               kfree((unsigned char *)new);
+                               break; /* read next frame - while(1) */
+                       }
+
+                       if (cpc_tty->state != CPC_TTY_ST_OPEN) {
+                               /* Free RX descriptors */ 
+                               cpc_tty_rx_disc_frame(pc300chan);
+                               stats->rx_dropped++; 
+                               rx_len = 0; 
+                               kfree((unsigned char *)new);
+                               break; /* read next frame - while(1) */
+                       }
+
+                       /* read the segment of the frame */
+                       if (rx_aux != 0) {
+                               memcpy_fromio((new->data + rx_len), 
+                                       (void *)(card->hw.rambase + 
+                                        cpc_readl(&ptdescr->ptbuf)), rx_aux);
+                               rx_len += rx_aux; 
+                       }
+                       cpc_writeb(&ptdescr->status,0); 
+                       cpc_writeb(&ptdescr->len, 0); 
+                       pc300chan->rx_first_bd = (pc300chan->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)); 
+               }
+               /* update pointer */ 
+               pc300chan->rx_last_bd = (pc300chan->rx_first_bd - 1) & 
+                                       (N_DMA_RX_BUF - 1) ; 
+               if (!(dsr_rx & DSR_BOF)) {
+                       /* update EDA */ 
+                       cpc_writel(card->hw.scabase + DRX_REG(EDAL, ch), 
+                                       RX_BD_ADDR(ch, pc300chan->rx_last_bd)); 
+               }
+               if (rx_len != 0) { 
+                       stats->rx_bytes += rx_len; 
+               
+                       if (pc300dev->trace_on) { 
+                               cpc_tty_trace(pc300dev, new->data,rx_len, 'R'); 
+                       } 
+                       new->size = rx_len;
+                       new->next = 0;
+                       if (cpc_tty->buf_rx.first == 0) {
+                               cpc_tty->buf_rx.first = new;
+                               cpc_tty->buf_rx.last = new;
+                       } else {
+                               cpc_tty->buf_rx.last->next = new;
+                               cpc_tty->buf_rx.last = new;
+                       }
+                       schedule_task(&(cpc_tty->tty_rx_task_queue));
+                       stats->rx_packets++;
+               }
+       } 
+} 
+
+/*
+ * PC300 TTY TX task routine
+ * 
+ * This routine treats TX interrupt. 
+ * o if need call line discipline wakeup
+ * o call wake_up_interruptible
+ */
+static void cpc_tty_tx_task(void *data)
+{
+       st_cpc_tty_area *cpc_tty = (st_cpc_tty_area *) data; 
+       struct tty_struct *tty; 
+
+       CPC_TTY_DBG("%s: cpc_tty_tx_task init\n",cpc_tty->name);
+       
+       if ((tty = cpc_tty->tty) == 0) { 
+               CPC_TTY_DBG("%s: the interface is not opened\n",cpc_tty->name);
+               return; 
+       }
+
+       if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup){
+               CPC_TTY_DBG("%s:call line disc. wakeup\n",cpc_tty->name);
+               tty->ldisc.write_wakeup (tty); 
+       }
+
+       wake_up_interruptible(&tty->write_wait); 
+}
+
+/*
+ * PC300 TTY send to card routine
+ * 
+ * This routine send data to card. 
+ * o clear descriptors
+ * o write data to DMA buffers
+ * o start the transmission
+ */
+static int cpc_tty_send_to_card(pc300dev_t *dev,void* buf, int len)
+{
+       pc300ch_t *chan = (pc300ch_t *)dev->chan; 
+       pc300_t *card = (pc300_t *)chan->card; 
+       int ch = chan->channel; 
+       struct net_device_stats *stats = &dev->hdlc->stats; 
+       unsigned long flags; 
+       volatile pcsca_bd_t * ptdescr; 
+       int i, nchar;
+       int tosend = len;
+       int nbuf = ((len - 1)/BD_DEF_LEN) + 1;
+       unsigned char *pdata=buf;
+
+       CPC_TTY_DBG("%s:cpc_tty_send_to_cars len=%i", 
+                       (st_cpc_tty_area *)dev->cpc_tty->name,len);     
+
+       if (nbuf >= card->chan[ch].nfree_tx_bd) {
+               return 1;
+       }
+       
+       /* write buffer to DMA buffers */ 
+       CPC_TTY_DBG("%s: call dma_buf_write\n",
+                       (st_cpc_tty_area *)dev->cpc_tty->name); 
+       for (i = 0 ; i < nbuf ; i++) {
+               ptdescr = (pcsca_bd_t *)(card->hw.rambase + 
+                       TX_BD_ADDR(ch, card->chan[ch].tx_next_bd));
+               nchar = (BD_DEF_LEN > tosend) ? tosend : BD_DEF_LEN;
+               if (cpc_readb(&ptdescr->status) & DST_OSB) {
+                       memcpy_toio((void *)(card->hw.rambase + 
+                               cpc_readl(&ptdescr->ptbuf)), 
+                               &pdata[len - tosend], 
+                               nchar);
+                       card->chan[ch].nfree_tx_bd--;
+                       if ((i + 1) == nbuf) {
+                               /* This must be the last BD to be used */
+                               cpc_writeb(&ptdescr->status, DST_EOM);
+                       } else {
+                               cpc_writeb(&ptdescr->status, 0);
+                       }
+                       cpc_writew(&ptdescr->len, nchar);
+               } else {
+                       CPC_TTY_DBG("%s: error in dma_buf_write\n",
+                                       (st_cpc_tty_area *)dev->cpc_tty->name); 
+                       stats->tx_dropped++;
+                       return 1; 
+               }
+               tosend -= nchar;
+               card->chan[ch].tx_next_bd = 
+                       (card->chan[ch].tx_next_bd + 1) & (N_DMA_TX_BUF - 1);
+       }
+
+       if (dev->trace_on) { 
+               cpc_tty_trace(dev, buf, len,'T'); 
+       }
+
+       /* start transmission */ 
+       CPC_TTY_DBG("%s: start transmission\n",
+               (st_cpc_tty_area *)dev->cpc_tty->name); 
+       
+       CPC_TTY_LOCK(card, flags); 
+       cpc_writeb(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 + card->hw.cpld_reg2, 
+                       cpc_readb(card->hw.falcbase + card->hw.cpld_reg2) |
+                       (CPLD_REG2_FALC_LED1 << (2 * ch))); 
+       }
+       CPC_TTY_UNLOCK(card, flags); 
+       return 0; 
+} 
+
+/*
+ *     PC300 TTY trace routine
+ *
+ *  This routine send trace of connection to application. 
+ *  o clear descriptors
+ *  o write data to DMA buffers
+ *  o start the transmission
+ */
+
+static void cpc_tty_trace(pc300dev_t *dev, char* buf, int len, char rxtx)
+{
+       struct sk_buff *skb; 
+
+       if ((skb = dev_alloc_skb(10 + len)) == NULL) { 
+               /* out of memory */ 
+               CPC_TTY_DBG("%s: tty_trace - out of memory\n",
+                       ((struct net_device *)(dev->hdlc))->name);
+               return; 
+       }
+
+       skb_put (skb, 10 + len); 
+       skb->dev = (struct net_device *) dev->hdlc; 
+       skb->protocol = htons(ETH_P_CUST); 
+       skb->mac.raw = skb->data; 
+       skb->pkt_type = PACKET_HOST; 
+       skb->len = 10 + len; 
+
+       memcpy(skb->data,((struct net_device *)(dev->hdlc))->name,5);
+       skb->data[5] = '['; 
+       skb->data[6] = rxtx; 
+       skb->data[7] = ']'; 
+       skb->data[8] = ':'; 
+       skb->data[9] = ' '; 
+       memcpy(&skb->data[10], buf, len); 
+       netif_rx(skb); 
+}      
+
+/*
+ *     PC300 TTY unregister service routine
+ *
+ *     This routine unregister one interface. 
+ */
+void cpc_tty_unregister_service(pc300dev_t *pc300dev)
+{
+       st_cpc_tty_area *cpc_tty; 
+       ulong flags;
+       int res;
+
+       if ((cpc_tty= (st_cpc_tty_area *) pc300dev->cpc_tty) == 0) { 
+               CPC_TTY_DBG("%s: interface is not TTY\n",
+                       ((struct net_device *)(pc300dev->hdlc))->name);
+               return; 
+       }
+       CPC_TTY_DBG("%s: cpc_tty_unregister_service", cpc_tty->name);
+
+       if (cpc_tty->pc300dev != pc300dev) { 
+               CPC_TTY_DBG("%s: invalid tty ptr=%s\n", 
+               ((struct net_device *)(pc300dev->hdlc))->name, cpc_tty->name);
+               return; 
+       }
+
+       if (--cpc_tty_cnt == 0) { 
+               if (cpc_tty_refcount) {
+                       CPC_TTY_DBG("%s: unregister is not possible, refcount=%d",
+                                                       cpc_tty->name, cpc_tty_refcount);
+                       cpc_tty_cnt++;
+                       cpc_tty_unreg_flag = 1;
+                       return;
+               } else { 
+                       CPC_TTY_DBG("%s: unregister the tty driver\n", cpc_tty->name);
+                       if ((res=tty_unregister_driver(&serial_drv))) { 
+                               CPC_TTY_DBG("%s: ERROR ->unregister the tty driver error=%d\n",
+                                                               cpc_tty->name,res);
+                       }
+                       if ((res=tty_unregister_driver(&callout_drv))) { 
+                               CPC_TTY_DBG("%s: ERROR ->unregister the tty driver error=%d\n",
+                                                               cpc_tty->name,res);
+                       }
+               }
+       }
+       CPC_TTY_LOCK(pc300dev->chan->card,flags);
+       cpc_tty->tty = NULL; 
+       CPC_TTY_UNLOCK(pc300dev->chan->card, flags);
+       cpc_tty->tty_minor = 0; 
+       cpc_tty->state = CPC_TTY_ST_IDLE; 
+} 
+
+/*
+ * PC300 TTY trigger poll routine
+ * This routine is called by pc300driver to treats Tx interrupt. 
+ */
+void cpc_tty_trigger_poll(pc300dev_t *pc300dev)
+{
+       st_cpc_tty_area *cpc_tty = (st_cpc_tty_area *)pc300dev->cpc_tty; 
+       if (!cpc_tty) {
+               return;
+       }
+       schedule_task(&(cpc_tty->tty_tx_task_queue)); 
+} 
+
+/*
+ * PC300 TTY reset var routine
+ * This routine is called by pc300driver to init the TTY area. 
+ */
+
+void cpc_tty_reset_var(void)
+{
+       int i ; 
+
+       CPC_TTY_DBG("hdlcX-tty: reset variables\n");
+       /* reset  the tty_driver structure - serial_drv */ 
+       memset(&serial_drv, 0, sizeof(struct tty_driver));
+       memset(&callout_drv, 0, sizeof(struct tty_driver));
+       for (i=0; i < CPC_TTY_NPORTS; i++){
+               memset(&cpc_tty_area[i],0, sizeof(st_cpc_tty_area)); 
+       }
+}
index cc5c6eac2a6d731743857b9644f0872c2cc7a3a0..7fb4db9ea7ca414b1bd16426f04442dae18eac9b 100644 (file)
        0105  Cyclom_8Y above first megabyte
        0200  Cyclom_Z below first megabyte
        0201  Cyclom_Z above first megabyte
-       0300  PC300 RX 2
-       0301  PC300 RX 1
-       0310  PC300 TE 2
-       0311  PC300 TE 1
+       0300  PC300/RSV or /X21 (2 ports)
+       0301  PC300/RSV or /X21 (1 ports)
+       0310  PC300/TE (2 ports)
+       0311  PC300/TE (1 port)
+       0320  PC300/TE-M (2 ports)
+       0321  PC300/TE-M (1 port)
 120f  Essential Communications
        0001  Roadrunner serial HIPPI
 1210  Hyperparallel Technologies
index 70b0f2fb1d4870ba7f7cba9f38666d3812a571b6..b9017295eb1eb27dc64ad0bf22f5c17b9f7bf214 100644 (file)
 #define PCI_DEVICE_ID_PC300_RX_1       0x0301
 #define PCI_DEVICE_ID_PC300_TE_2       0x0310
 #define PCI_DEVICE_ID_PC300_TE_1       0x0311
+#define PCI_DEVICE_ID_PC300_TE_M_2     0x0320
+#define PCI_DEVICE_ID_PC300_TE_M_1     0x0321
 
 #define PCI_VENDOR_ID_ESSENTIAL                0x120f
 #define PCI_DEVICE_ID_ESSENTIAL_ROADRUNNER     0x0001