mISDN: Add driver for Infineon ISDN chipset family
authorKarsten Keil <keil@b1-systems.de>
Wed, 22 Jul 2009 17:42:46 +0000 (19:42 +0200)
committerKarsten Keil <keil@b1-systems.de>
Sat, 25 Jul 2009 18:18:29 +0000 (20:18 +0200)
This driver supports cards with Infineon ISAC/HSCX, ISACX, IPAC
and IPACX chips from various manufacturers.

Signed-off-by: Karsten Keil <keil@b1-systems.de>
drivers/isdn/hardware/mISDN/Kconfig
drivers/isdn/hardware/mISDN/Makefile
drivers/isdn/hardware/mISDN/iohelper.h [new file with mode: 0644]
drivers/isdn/hardware/mISDN/ipac.h [new file with mode: 0644]
drivers/isdn/hardware/mISDN/mISDNinfineon.c [new file with mode: 0644]
drivers/isdn/hardware/mISDN/mISDNipac.c [new file with mode: 0644]

index 3024566dd099512fde781e368f02312b918820d1..a8542b81f95cdf869625218641a69dae78dc2750 100644 (file)
@@ -39,3 +39,17 @@ config MISDN_HFCUSB
          Enable support for USB ISDN TAs with Cologne Chip AG's
          HFC-S USB ISDN Controller
 
+config MISDN_INFINEON
+       tristate "Support for cards with Infineon chipset"
+       depends on MISDN
+       depends on PCI
+       select MISDN_IPAC
+       help
+         Enable support for cards with ISAC + HSCX, IPAC or IPAC-SX
+         chip from Infineon (former manufacturer Siemens).
+
+
+config MISDN_IPAC
+       tristate
+       depends on MISDN
+
index b0403526bbba01d25582c862613c045ab89cb584..2863455b4939933a709ffc556fd4c92f0047649b 100644 (file)
@@ -6,3 +6,6 @@
 obj-$(CONFIG_MISDN_HFCPCI) += hfcpci.o
 obj-$(CONFIG_MISDN_HFCMULTI) += hfcmulti.o
 obj-$(CONFIG_MISDN_HFCUSB) += hfcsusb.o
+obj-$(CONFIG_MISDN_INFINEON) += mISDNinfineon.o
+# chip modules
+obj-$(CONFIG_MISDN_IPAC) += mISDNipac.o
diff --git a/drivers/isdn/hardware/mISDN/iohelper.h b/drivers/isdn/hardware/mISDN/iohelper.h
new file mode 100644 (file)
index 0000000..c16a217
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * iohelper.h
+ *             helper for define functions to access ISDN hardware
+ *              supported are memory mapped IO
+ *             indirect port IO (one port for address, one for data)
+ *
+ * Author       Karsten Keil <keil@isdn4linux.de>
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef _IOHELPER_H
+#define _IOHELPER_H
+
+typedef        u8      (read_reg_t)(void *, u8);
+typedef        void    (write_reg_t)(void *, u8, u8);
+typedef        void    (fifo_func_t)(void *, u8, u8 *, int);
+
+struct _ioport {
+       u32     port;
+       u32     ale;
+};
+
+#define IOFUNC_IO(name, hws, ap) \
+       static u8 Read##name##_IO(void *p, u8 off) {\
+               struct hws *hw = p;\
+               return inb(hw->ap.port + off);\
+       } \
+       static void Write##name##_IO(void *p, u8 off, u8 val) {\
+               struct hws *hw = p;\
+               outb(val, hw->ap.port + off);\
+       } \
+       static void ReadFiFo##name##_IO(void *p, u8 off, u8 *dp, int size) {\
+               struct hws *hw = p;\
+               insb(hw->ap.port + off, dp, size);\
+       } \
+       static void WriteFiFo##name##_IO(void *p, u8 off, u8 *dp, int size) {\
+               struct hws *hw = p;\
+               outsb(hw->ap.port + off, dp, size);\
+       }
+
+#define IOFUNC_IND(name, hws, ap) \
+       static u8 Read##name##_IND(void *p, u8 off) {\
+               struct hws *hw = p;\
+               outb(off, hw->ap.ale);\
+               return inb(hw->ap.port);\
+       } \
+       static void Write##name##_IND(void *p, u8 off, u8 val) {\
+               struct hws *hw = p;\
+               outb(off, hw->ap.ale);\
+               outb(val, hw->ap.port);\
+       } \
+       static void ReadFiFo##name##_IND(void *p, u8 off, u8 *dp, int size) {\
+               struct hws *hw = p;\
+               outb(off, hw->ap.ale);\
+               insb(hw->ap.port, dp, size);\
+       } \
+       static void WriteFiFo##name##_IND(void *p, u8 off, u8 *dp, int size) {\
+               struct hws *hw = p;\
+               outb(off, hw->ap.ale);\
+               outsb(hw->ap.port, dp, size);\
+       }
+
+#define IOFUNC_MEMIO(name, hws, typ, adr) \
+       static u8 Read##name##_MIO(void *p, u8 off) {\
+               struct hws *hw = p;\
+               return readb(((typ *)hw->adr) + off);\
+       } \
+       static void Write##name##_MIO(void *p, u8 off, u8 val) {\
+               struct hws *hw = p;\
+               writeb(val, ((typ *)hw->adr) + off);\
+       } \
+       static void ReadFiFo##name##_MIO(void *p, u8 off, u8 *dp, int size) {\
+               struct hws *hw = p;\
+               while (size--)\
+                       *dp++ = readb(((typ *)hw->adr) + off);\
+       } \
+       static void WriteFiFo##name##_MIO(void *p, u8 off, u8 *dp, int size) {\
+               struct inf_hw *hw = p;\
+               while (size--)\
+                       writeb(*dp++, ((typ *)hw->adr) + off);\
+       }
+
+#define ASSIGN_FUNC(typ, name, dest)   do {\
+       dest.read_reg = &Read##name##_##typ;\
+       dest.write_reg = &Write##name##_##typ;\
+       dest.read_fifo = &ReadFiFo##name##_##typ;\
+       dest.write_fifo = &WriteFiFo##name##_##typ;\
+       } while (0)
+#define ASSIGN_FUNC_IPAC(typ, target)  do {\
+       ASSIGN_FUNC(typ, ISAC, target.isac);\
+       ASSIGN_FUNC(typ, IPAC, target);\
+       } while (0)
+
+#endif
\ No newline at end of file
diff --git a/drivers/isdn/hardware/mISDN/ipac.h b/drivers/isdn/hardware/mISDN/ipac.h
new file mode 100644 (file)
index 0000000..f9601d5
--- /dev/null
@@ -0,0 +1,405 @@
+/*
+ *
+ * ipac.h      Defines for the Infineon (former Siemens) ISDN
+ *             chip series
+ *
+ * Author       Karsten Keil <keil@isdn4linux.de>
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include "iohelper.h"
+
+struct isac_hw {
+       struct dchannel         dch;
+       u32                     type;
+       u32                     off;            /* offset to isac regs */
+       char                    *name;
+       spinlock_t              *hwlock;        /* lock HW acccess */
+       read_reg_t              *read_reg;
+       write_reg_t             *write_reg;
+       fifo_func_t             *read_fifo;
+       fifo_func_t             *write_fifo;
+       int                     (*monitor)(void *, u32, u8 *, int);
+       void                    (*release)(struct isac_hw *);
+       int                     (*init)(struct isac_hw *);
+       int                     (*ctrl)(struct isac_hw *, u32, u_long);
+       int                     (*open)(struct isac_hw *, struct channel_req *);
+       u8                      *mon_tx;
+       u8                      *mon_rx;
+       int                     mon_txp;
+       int                     mon_txc;
+       int                     mon_rxp;
+       struct arcofi_msg       *arcofi_list;
+       struct timer_list       arcofitimer;
+       wait_queue_head_t       arcofi_wait;
+       u8                      arcofi_bc;
+       u8                      arcofi_state;
+       u8                      mocr;
+       u8                      adf2;
+       u8                      state;
+};
+
+struct ipac_hw;
+
+struct hscx_hw {
+       struct bchannel         bch;
+       struct ipac_hw          *ip;
+       u8                      fifo_size;
+       u8                      off;    /* offset to ICA or ICB */
+       u8                      slot;
+       char                    log[64];
+};
+
+struct ipac_hw {
+       struct isac_hw          isac;
+       struct hscx_hw          hscx[2];
+       char                    *name;
+       void                    *hw;
+       spinlock_t              *hwlock;        /* lock HW acccess */
+       struct module           *owner;
+       u32                     type;
+       read_reg_t              *read_reg;
+       write_reg_t             *write_reg;
+       fifo_func_t             *read_fifo;
+       fifo_func_t             *write_fifo;
+       void                    (*release)(struct ipac_hw *);
+       int                     (*init)(struct ipac_hw *);
+       int                     (*ctrl)(struct ipac_hw *, u32, u_long);
+       u8                      conf;
+};
+
+#define IPAC_TYPE_ISAC         0x0010
+#define IPAC_TYPE_IPAC         0x0020
+#define IPAC_TYPE_ISACX                0x0040
+#define IPAC_TYPE_IPACX                0x0080
+#define IPAC_TYPE_HSCX         0x0100
+
+#define ISAC_USE_ARCOFI                0x1000
+
+/* Monitor functions */
+#define MONITOR_RX_0           0x1000
+#define MONITOR_RX_1           0x1001
+#define MONITOR_TX_0           0x2000
+#define MONITOR_TX_1           0x2001
+
+/* All registers original Siemens Spec  */
+/* IPAC/ISAC registers */
+#define ISAC_MASK              0x20
+#define ISAC_ISTA              0x20
+#define ISAC_STAR              0x21
+#define ISAC_CMDR              0x21
+#define ISAC_EXIR              0x24
+#define ISAC_ADF2              0x39
+#define ISAC_SPCR              0x30
+#define ISAC_ADF1              0x38
+#define ISAC_CIR0              0x31
+#define ISAC_CIX0              0x31
+#define ISAC_CIR1              0x33
+#define ISAC_CIX1              0x33
+#define ISAC_STCR              0x37
+#define ISAC_MODE              0x22
+#define ISAC_RSTA              0x27
+#define ISAC_RBCL              0x25
+#define ISAC_RBCH              0x2A
+#define ISAC_TIMR              0x23
+#define ISAC_SQXR              0x3b
+#define ISAC_SQRR              0x3b
+#define ISAC_MOSR              0x3a
+#define ISAC_MOCR              0x3a
+#define ISAC_MOR0              0x32
+#define ISAC_MOX0              0x32
+#define ISAC_MOR1              0x34
+#define ISAC_MOX1              0x34
+
+#define ISAC_RBCH_XAC          0x80
+
+#define IPAC_D_TIN2            0x01
+
+/* IPAC/HSCX */
+#define IPAC_ISTAB             0x20    /* RD   */
+#define IPAC_MASKB             0x20    /* WR   */
+#define IPAC_STARB             0x21    /* RD   */
+#define IPAC_CMDRB             0x21    /* WR   */
+#define IPAC_MODEB             0x22    /* R/W  */
+#define IPAC_EXIRB             0x24    /* RD   */
+#define IPAC_RBCLB             0x25    /* RD   */
+#define IPAC_RAH1              0x26    /* WR   */
+#define IPAC_RAH2              0x27    /* WR   */
+#define IPAC_RSTAB             0x27    /* RD   */
+#define IPAC_RAL1              0x28    /* R/W  */
+#define IPAC_RAL2              0x29    /* WR   */
+#define IPAC_RHCRB             0x29    /* RD   */
+#define IPAC_XBCL              0x2A    /* WR   */
+#define IPAC_CCR2              0x2C    /* R/W  */
+#define IPAC_RBCHB             0x2D    /* RD   */
+#define IPAC_XBCH              0x2D    /* WR   */
+#define HSCX_VSTR              0x2E    /* RD   */
+#define IPAC_RLCR              0x2E    /* WR   */
+#define IPAC_CCR1              0x2F    /* R/W  */
+#define IPAC_TSAX              0x30    /* WR   */
+#define IPAC_TSAR              0x31    /* WR   */
+#define IPAC_XCCR              0x32    /* WR   */
+#define IPAC_RCCR              0x33    /* WR   */
+
+/* IPAC_ISTAB/IPAC_MASKB bits */
+#define IPAC_B_XPR             0x10
+#define IPAC_B_RPF             0x40
+#define IPAC_B_RME             0x80
+#define IPAC_B_ON              0x2F
+
+/* IPAC_EXIRB bits */
+#define IPAC_B_RFS             0x04
+#define IPAC_B_RFO             0x10
+#define IPAC_B_XDU             0x40
+#define IPAC_B_XMR             0x80
+
+/* IPAC special registers */
+#define IPAC_CONF              0xC0    /* R/W  */
+#define IPAC_ISTA              0xC1    /* RD   */
+#define IPAC_MASK              0xC1    /* WR   */
+#define IPAC_ID                        0xC2    /* RD   */
+#define IPAC_ACFG              0xC3    /* R/W  */
+#define IPAC_AOE               0xC4    /* R/W  */
+#define IPAC_ARX               0xC5    /* RD   */
+#define IPAC_ATX               0xC5    /* WR   */
+#define IPAC_PITA1             0xC6    /* R/W  */
+#define IPAC_PITA2             0xC7    /* R/W  */
+#define IPAC_POTA1             0xC8    /* R/W  */
+#define IPAC_POTA2             0xC9    /* R/W  */
+#define IPAC_PCFG              0xCA    /* R/W  */
+#define IPAC_SCFG              0xCB    /* R/W  */
+#define IPAC_TIMR2             0xCC    /* R/W  */
+
+/* IPAC_ISTA/_MASK bits */
+#define IPAC__EXB              0x01
+#define IPAC__ICB              0x02
+#define IPAC__EXA              0x04
+#define IPAC__ICA              0x08
+#define IPAC__EXD              0x10
+#define IPAC__ICD              0x20
+#define IPAC__INT0             0x40
+#define IPAC__INT1             0x80
+#define IPAC__ON               0xC0
+
+/* HSCX ISTA/MASK bits */
+#define HSCX__EXB              0x01
+#define HSCX__EXA              0x02
+#define HSCX__ICA              0x04
+
+/* ISAC/ISACX/IPAC/IPACX L1 commands */
+#define ISAC_CMD_TIM           0x0
+#define ISAC_CMD_RS            0x1
+#define ISAC_CMD_SCZ           0x4
+#define ISAC_CMD_SSZ           0x2
+#define ISAC_CMD_AR8           0x8
+#define ISAC_CMD_AR10          0x9
+#define ISAC_CMD_ARL           0xA
+#define ISAC_CMD_DUI           0xF
+
+/* ISAC/ISACX/IPAC/IPACX L1 indications */
+#define ISAC_IND_RS            0x1
+#define ISAC_IND_PU            0x7
+#define ISAC_IND_DR            0x0
+#define ISAC_IND_SD            0x2
+#define ISAC_IND_DIS           0x3
+#define ISAC_IND_EI            0x6
+#define ISAC_IND_RSY           0x4
+#define ISAC_IND_ARD           0x8
+#define ISAC_IND_TI            0xA
+#define ISAC_IND_ATI           0xB
+#define ISAC_IND_AI8           0xC
+#define ISAC_IND_AI10          0xD
+#define ISAC_IND_DID           0xF
+
+/* the new ISACX / IPACX */
+/* D-channel registers   */
+#define ISACX_RFIFOD           0x00    /* RD   */
+#define ISACX_XFIFOD           0x00    /* WR   */
+#define ISACX_ISTAD            0x20    /* RD   */
+#define ISACX_MASKD            0x20    /* WR   */
+#define ISACX_STARD            0x21    /* RD   */
+#define ISACX_CMDRD            0x21    /* WR   */
+#define ISACX_MODED            0x22    /* R/W  */
+#define ISACX_EXMD1            0x23    /* R/W  */
+#define ISACX_TIMR1            0x24    /* R/W  */
+#define ISACX_SAP1             0x25    /* WR   */
+#define ISACX_SAP2             0x26    /* WR   */
+#define ISACX_RBCLD            0x26    /* RD   */
+#define ISACX_RBCHD            0x27    /* RD   */
+#define ISACX_TEI1             0x27    /* WR   */
+#define ISACX_TEI2             0x28    /* WR   */
+#define ISACX_RSTAD            0x28    /* RD   */
+#define ISACX_TMD              0x29    /* R/W  */
+#define ISACX_CIR0             0x2E    /* RD   */
+#define ISACX_CIX0             0x2E    /* WR   */
+#define ISACX_CIR1             0x2F    /* RD   */
+#define ISACX_CIX1             0x2F    /* WR   */
+
+/* Transceiver registers  */
+#define ISACX_TR_CONF0         0x30    /* R/W  */
+#define ISACX_TR_CONF1         0x31    /* R/W  */
+#define ISACX_TR_CONF2         0x32    /* R/W  */
+#define ISACX_TR_STA           0x33    /* RD   */
+#define ISACX_TR_CMD           0x34    /* R/W  */
+#define ISACX_SQRR1            0x35    /* RD   */
+#define ISACX_SQXR1            0x35    /* WR   */
+#define ISACX_SQRR2            0x36    /* RD   */
+#define ISACX_SQXR2            0x36    /* WR   */
+#define ISACX_SQRR3            0x37    /* RD   */
+#define ISACX_SQXR3            0x37    /* WR   */
+#define ISACX_ISTATR           0x38    /* RD   */
+#define ISACX_MASKTR           0x39    /* R/W  */
+#define ISACX_TR_MODE          0x3A    /* R/W  */
+#define ISACX_ACFG1            0x3C    /* R/W  */
+#define ISACX_ACFG2            0x3D    /* R/W  */
+#define ISACX_AOE              0x3E    /* R/W  */
+#define ISACX_ARX              0x3F    /* RD   */
+#define ISACX_ATX              0x3F    /* WR   */
+
+/* IOM: Timeslot, DPS, CDA  */
+#define ISACX_CDA10            0x40    /* R/W  */
+#define ISACX_CDA11            0x41    /* R/W  */
+#define ISACX_CDA20            0x42    /* R/W  */
+#define ISACX_CDA21            0x43    /* R/W  */
+#define ISACX_CDA_TSDP10       0x44    /* R/W  */
+#define ISACX_CDA_TSDP11       0x45    /* R/W  */
+#define ISACX_CDA_TSDP20       0x46    /* R/W  */
+#define ISACX_CDA_TSDP21       0x47    /* R/W  */
+#define ISACX_BCHA_TSDP_BC1    0x48    /* R/W  */
+#define ISACX_BCHA_TSDP_BC2    0x49    /* R/W  */
+#define ISACX_BCHB_TSDP_BC1    0x4A    /* R/W  */
+#define ISACX_BCHB_TSDP_BC2    0x4B    /* R/W  */
+#define ISACX_TR_TSDP_BC1      0x4C    /* R/W  */
+#define ISACX_TR_TSDP_BC2      0x4D    /* R/W  */
+#define ISACX_CDA1_CR          0x4E    /* R/W  */
+#define ISACX_CDA2_CR          0x4F    /* R/W  */
+
+/* IOM: Contol, Sync transfer, Monitor    */
+#define ISACX_TR_CR            0x50    /* R/W  */
+#define ISACX_TRC_CR           0x50    /* R/W  */
+#define ISACX_BCHA_CR          0x51    /* R/W  */
+#define ISACX_BCHB_CR          0x52    /* R/W  */
+#define ISACX_DCI_CR           0x53    /* R/W  */
+#define ISACX_DCIC_CR          0x53    /* R/W  */
+#define ISACX_MON_CR           0x54    /* R/W  */
+#define ISACX_SDS1_CR          0x55    /* R/W  */
+#define ISACX_SDS2_CR          0x56    /* R/W  */
+#define ISACX_IOM_CR           0x57    /* R/W  */
+#define ISACX_STI              0x58    /* RD   */
+#define ISACX_ASTI             0x58    /* WR   */
+#define ISACX_MSTI             0x59    /* R/W  */
+#define ISACX_SDS_CONF         0x5A    /* R/W  */
+#define ISACX_MCDA             0x5B    /* RD   */
+#define ISACX_MOR              0x5C    /* RD   */
+#define ISACX_MOX              0x5C    /* WR   */
+#define ISACX_MOSR             0x5D    /* RD   */
+#define ISACX_MOCR             0x5E    /* R/W  */
+#define ISACX_MSTA             0x5F    /* RD   */
+#define ISACX_MCONF            0x5F    /* WR   */
+
+/* Interrupt and general registers */
+#define ISACX_ISTA             0x60    /* RD   */
+#define ISACX_MASK             0x60    /* WR   */
+#define ISACX_AUXI             0x61    /* RD   */
+#define ISACX_AUXM             0x61    /* WR   */
+#define ISACX_MODE1            0x62    /* R/W  */
+#define ISACX_MODE2            0x63    /* R/W  */
+#define ISACX_ID               0x64    /* RD   */
+#define ISACX_SRES             0x64    /* WR   */
+#define ISACX_TIMR2            0x65    /* R/W  */
+
+/* Register Bits */
+/* ISACX/IPACX _ISTAD (R) and _MASKD (W) */
+#define ISACX_D_XDU            0x04
+#define ISACX_D_XMR            0x08
+#define ISACX_D_XPR            0x10
+#define ISACX_D_RFO            0x20
+#define ISACX_D_RPF            0x40
+#define ISACX_D_RME            0x80
+
+/* ISACX/IPACX _ISTA (R) and _MASK (W) */
+#define ISACX__ICD             0x01
+#define ISACX__MOS             0x02
+#define ISACX__TRAN            0x04
+#define ISACX__AUX             0x08
+#define ISACX__CIC             0x10
+#define ISACX__ST              0x20
+#define IPACX__ICB             0x40
+#define IPACX__ICA             0x80
+#define IPACX__ON              0x2C
+
+/* ISACX/IPACX _CMDRD (W) */
+#define ISACX_CMDRD_XRES       0x01
+#define ISACX_CMDRD_XME                0x02
+#define ISACX_CMDRD_XTF                0x08
+#define ISACX_CMDRD_STI                0x10
+#define ISACX_CMDRD_RRES       0x40
+#define ISACX_CMDRD_RMC                0x80
+
+/* ISACX/IPACX _RSTAD (R) */
+#define ISACX_RSTAD_TA         0x01
+#define ISACX_RSTAD_CR         0x02
+#define ISACX_RSTAD_SA0                0x04
+#define ISACX_RSTAD_SA1                0x08
+#define ISACX_RSTAD_RAB                0x10
+#define ISACX_RSTAD_CRC                0x20
+#define ISACX_RSTAD_RDO                0x40
+#define ISACX_RSTAD_VFR                0x80
+
+/* ISACX/IPACX _CIR0 (R) */
+#define ISACX_CIR0_BAS         0x01
+#define ISACX_CIR0_SG          0x08
+#define ISACX_CIR0_CIC1                0x08
+#define ISACX_CIR0_CIC0                0x08
+
+/* B-channel registers */
+#define IPACX_OFF_ICA          0x70
+#define IPACX_OFF_ICB          0x80
+
+/* ICA: IPACX_OFF_ICA + Reg ICB: IPACX_OFF_ICB + Reg */
+
+#define IPACX_ISTAB            0x00    /* RD   */
+#define IPACX_MASKB            0x00    /* WR   */
+#define IPACX_STARB            0x01    /* RD   */
+#define IPACX_CMDRB            0x01    /* WR   */
+#define IPACX_MODEB            0x02    /* R/W  */
+#define IPACX_EXMB             0x03    /* R/W  */
+#define IPACX_RAH1             0x05    /* WR   */
+#define IPACX_RAH2             0x06    /* WR   */
+#define IPACX_RBCLB            0x06    /* RD   */
+#define IPACX_RBCHB            0x07    /* RD   */
+#define IPACX_RAL1             0x07    /* WR   */
+#define IPACX_RAL2             0x08    /* WR   */
+#define IPACX_RSTAB            0x08    /* RD   */
+#define IPACX_TMB              0x09    /* R/W  */
+#define IPACX_RFIFOB           0x0A    /* RD   */
+#define IPACX_XFIFOB           0x0A    /* WR   */
+
+/* IPACX_ISTAB / IPACX_MASKB bits */
+#define IPACX_B_XDU            0x04
+#define IPACX_B_XPR            0x10
+#define IPACX_B_RFO            0x20
+#define IPACX_B_RPF            0x40
+#define IPACX_B_RME            0x80
+
+#define IPACX_B_ON             0x0B
+
+extern int mISDNisac_init(struct isac_hw *, void *);
+extern irqreturn_t mISDNisac_irq(struct isac_hw *, u8);
+extern u32 mISDNipac_init(struct ipac_hw *, void *);
+extern irqreturn_t mISDNipac_irq(struct ipac_hw *, int);
diff --git a/drivers/isdn/hardware/mISDN/mISDNinfineon.c b/drivers/isdn/hardware/mISDN/mISDNinfineon.c
new file mode 100644 (file)
index 0000000..62441ba
--- /dev/null
@@ -0,0 +1,1178 @@
+/*
+ * mISDNinfineon.c
+ *             Support for cards based on following Infineon ISDN chipsets
+ *             - ISAC + HSCX
+ *             - IPAC and IPAC-X
+ *             - ISAC-SX + HSCX
+ *
+ * Supported cards:
+ *             - Dialogic Diva 2.0
+ *             - Dialogic Diva 2.0U
+ *             - Dialogic Diva 2.01
+ *             - Dialogic Diva 2.02
+ *             - Sedlbauer Speedwin
+ *             - HST Saphir3
+ *             - Develo (former ELSA) Microlink PCI (Quickstep 1000)
+ *             - Develo (former ELSA) Quickstep 3000
+ *             - Berkom Scitel BRIX Quadro
+ *             - Dr.Neuhaus (Sagem) Niccy
+ *
+ *
+ *
+ * Author       Karsten Keil <keil@isdn4linux.de>
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/mISDNhw.h>
+#include "ipac.h"
+
+#define INFINEON_REV   "1.0"
+
+static int inf_cnt;
+static u32 debug;
+static u32 irqloops = 4;
+
+enum inf_types {
+       INF_NONE,
+       INF_DIVA20,
+       INF_DIVA20U,
+       INF_DIVA201,
+       INF_DIVA202,
+       INF_SPEEDWIN,
+       INF_SAPHIR3,
+       INF_QS1000,
+       INF_QS3000,
+       INF_NICCY,
+       INF_SCT_1,
+       INF_SCT_2,
+       INF_SCT_3,
+       INF_SCT_4,
+       INF_GAZEL_R685,
+       INF_GAZEL_R753
+};
+
+enum addr_mode {
+       AM_NONE = 0,
+       AM_IO,
+       AM_MEMIO,
+       AM_IND_IO,
+};
+
+struct inf_cinfo {
+       enum inf_types  typ;
+       const char      *full;
+       const char      *name;
+       enum addr_mode  cfg_mode;
+       enum addr_mode  addr_mode;
+       u8              cfg_bar;
+       u8              addr_bar;
+       void            *irqfunc;
+};
+
+struct _ioaddr {
+       enum addr_mode  mode;
+       union {
+               void __iomem    *p;
+               struct _ioport  io;
+       } a;
+};
+
+struct _iohandle {
+       enum addr_mode  mode;
+       resource_size_t size;
+       resource_size_t start;
+       void __iomem    *p;
+};
+
+struct inf_hw {
+       struct list_head        list;
+       struct pci_dev          *pdev;
+       const struct inf_cinfo  *ci;
+       char                    name[MISDN_MAX_IDLEN];
+       u32                     irq;
+       u32                     irqcnt;
+       struct _iohandle        cfg;
+       struct _iohandle        addr;
+       struct _ioaddr          isac;
+       struct _ioaddr          hscx;
+       spinlock_t              lock;   /* HW access lock */
+       struct ipac_hw          ipac;
+       struct inf_hw           *sc[3]; /* slave cards */
+};
+
+
+#define PCI_SUBVENDOR_HST_SAPHIR3       0x52
+#define PCI_SUBVENDOR_SEDLBAUER_PCI     0x53
+#define PCI_SUB_ID_SEDLBAUER            0x01
+
+static struct pci_device_id infineon_ids[] __devinitdata = {
+       { PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA20,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_DIVA20},
+       { PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA20_U,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_DIVA20U},
+       { PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA201,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_DIVA201},
+       { PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA202,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_DIVA202},
+       { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100,
+         PCI_SUBVENDOR_SEDLBAUER_PCI, PCI_SUB_ID_SEDLBAUER, 0, 0,
+         INF_SPEEDWIN},
+       { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100,
+         PCI_SUBVENDOR_HST_SAPHIR3, PCI_SUB_ID_SEDLBAUER, 0, 0, INF_SAPHIR3},
+       { PCI_VENDOR_ID_ELSA, PCI_DEVICE_ID_ELSA_MICROLINK,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_QS1000},
+       { PCI_VENDOR_ID_ELSA, PCI_DEVICE_ID_ELSA_QS3000,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_QS3000},
+       { PCI_VENDOR_ID_SATSAGEM, PCI_DEVICE_ID_SATSAGEM_NICCY,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_NICCY},
+       { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050,
+         PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO, 0, 0,
+         INF_SCT_1},
+       { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_R685,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_GAZEL_R685},
+       { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_R753,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_GAZEL_R753},
+       { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_DJINN_ITOO,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_GAZEL_R753},
+       { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_OLITEC,
+         PCI_ANY_ID, PCI_ANY_ID, 0, 0, INF_GAZEL_R753},
+       { }
+};
+MODULE_DEVICE_TABLE(pci, infineon_ids);
+
+/* PCI interface specific defines */
+/* Diva 2.0/2.0U */
+#define DIVA_HSCX_PORT         0x00
+#define DIVA_HSCX_ALE          0x04
+#define DIVA_ISAC_PORT         0x08
+#define DIVA_ISAC_ALE          0x0C
+#define DIVA_PCI_CTRL           0x10
+
+/* DIVA_PCI_CTRL bits */
+#define DIVA_IRQ_BIT           0x01
+#define DIVA_RESET_BIT         0x08
+#define DIVA_EEPROM_CLK                0x40
+#define DIVA_LED_A             0x10
+#define DIVA_LED_B             0x20
+#define DIVA_IRQ_CLR           0x80
+
+/* Diva 2.01/2.02 */
+/* Siemens PITA */
+#define PITA_ICR_REG           0x00
+#define PITA_INT0_STATUS       0x02
+
+#define PITA_MISC_REG          0x1c
+#define PITA_PARA_SOFTRESET    0x01000000
+#define PITA_SER_SOFTRESET     0x02000000
+#define PITA_PARA_MPX_MODE     0x04000000
+#define PITA_INT0_ENABLE       0x00020000
+
+/* TIGER 100 Registers */
+#define TIGER_RESET_ADDR       0x00
+#define TIGER_EXTERN_RESET     0x01
+#define TIGER_AUX_CTRL         0x02
+#define TIGER_AUX_DATA         0x03
+#define TIGER_AUX_IRQMASK      0x05
+#define TIGER_AUX_STATUS       0x07
+
+/* Tiger AUX BITs */
+#define TIGER_IOMASK           0xdd    /* 1 and 5 are inputs */
+#define TIGER_IRQ_BIT          0x02
+
+#define TIGER_IPAC_ALE         0xC0
+#define TIGER_IPAC_PORT                0xC8
+
+/* ELSA (now Develo) PCI cards */
+#define ELSA_IRQ_ADDR          0x4c
+#define ELSA_IRQ_MASK          0x04
+#define QS1000_IRQ_OFF         0x01
+#define QS3000_IRQ_OFF         0x03
+#define QS1000_IRQ_ON          0x41
+#define QS3000_IRQ_ON          0x43
+
+/* Dr Neuhaus/Sagem Niccy */
+#define NICCY_ISAC_PORT                0x00
+#define NICCY_HSCX_PORT                0x01
+#define NICCY_ISAC_ALE         0x02
+#define NICCY_HSCX_ALE         0x03
+
+#define NICCY_IRQ_CTRL_REG     0x38
+#define NICCY_IRQ_ENABLE       0x001f00
+#define NICCY_IRQ_DISABLE      0xff0000
+#define NICCY_IRQ_BIT          0x800000
+
+
+/* Scitel PLX */
+#define SCT_PLX_IRQ_ADDR       0x4c
+#define SCT_PLX_RESET_ADDR     0x50
+#define SCT_PLX_IRQ_ENABLE     0x41
+#define SCT_PLX_RESET_BIT      0x04
+
+/* Gazel */
+#define        GAZEL_IPAC_DATA_PORT    0x04
+/* Gazel PLX */
+#define GAZEL_CNTRL            0x50
+#define GAZEL_RESET            0x04
+#define GAZEL_RESET_9050       0x40000000
+#define GAZEL_INCSR            0x4C
+#define GAZEL_ISAC_EN          0x08
+#define GAZEL_INT_ISAC         0x20
+#define GAZEL_HSCX_EN          0x01
+#define GAZEL_INT_HSCX         0x04
+#define GAZEL_PCI_EN           0x40
+#define GAZEL_IPAC_EN          0x03
+
+
+static LIST_HEAD(Cards);
+static DEFINE_RWLOCK(card_lock); /* protect Cards */
+
+static void
+_set_debug(struct inf_hw *card)
+{
+       card->ipac.isac.dch.debug = debug;
+       card->ipac.hscx[0].bch.debug = debug;
+       card->ipac.hscx[1].bch.debug = debug;
+}
+
+static int
+set_debug(const char *val, struct kernel_param *kp)
+{
+       int ret;
+       struct inf_hw *card;
+
+       ret = param_set_uint(val, kp);
+       if (!ret) {
+               read_lock(&card_lock);
+               list_for_each_entry(card, &Cards, list)
+                       _set_debug(card);
+               read_unlock(&card_lock);
+       }
+       return ret;
+}
+
+MODULE_AUTHOR("Karsten Keil");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(INFINEON_REV);
+module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "infineon debug mask");
+module_param(irqloops, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(irqloops, "infineon maximal irqloops (default 4)");
+
+/* Interface functions */
+
+IOFUNC_IO(ISAC, inf_hw, isac.a.io)
+IOFUNC_IO(IPAC, inf_hw, hscx.a.io)
+IOFUNC_IND(ISAC, inf_hw, isac.a.io)
+IOFUNC_IND(IPAC, inf_hw, hscx.a.io)
+IOFUNC_MEMIO(ISAC, inf_hw, u32, isac.a.p)
+IOFUNC_MEMIO(IPAC, inf_hw, u32, hscx.a.p)
+
+static irqreturn_t
+diva_irq(int intno, void *dev_id)
+{
+       struct inf_hw *hw = dev_id;
+       u8 val;
+
+       spin_lock(&hw->lock);
+       val = inb((u32)hw->cfg.start + DIVA_PCI_CTRL);
+       if (!(val & DIVA_IRQ_BIT)) { /* for us or shared ? */
+               spin_unlock(&hw->lock);
+               return IRQ_NONE; /* shared */
+       }
+       hw->irqcnt++;
+       mISDNipac_irq(&hw->ipac, irqloops);
+       spin_unlock(&hw->lock);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t
+diva20x_irq(int intno, void *dev_id)
+{
+       struct inf_hw *hw = dev_id;
+       u8 val;
+
+       spin_lock(&hw->lock);
+       val = readb(hw->cfg.p);
+       if (!(val & PITA_INT0_STATUS)) { /* for us or shared ? */
+               spin_unlock(&hw->lock);
+               return IRQ_NONE; /* shared */
+       }
+       hw->irqcnt++;
+       mISDNipac_irq(&hw->ipac, irqloops);
+       writeb(PITA_INT0_STATUS, hw->cfg.p); /* ACK PITA INT0 */
+       spin_unlock(&hw->lock);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t
+tiger_irq(int intno, void *dev_id)
+{
+       struct inf_hw *hw = dev_id;
+       u8 val;
+
+       spin_lock(&hw->lock);
+       val = inb((u32)hw->cfg.start + TIGER_AUX_STATUS);
+       if (val & TIGER_IRQ_BIT) { /* for us or shared ? */
+               spin_unlock(&hw->lock);
+               return IRQ_NONE; /* shared */
+       }
+       hw->irqcnt++;
+       mISDNipac_irq(&hw->ipac, irqloops);
+       spin_unlock(&hw->lock);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t
+elsa_irq(int intno, void *dev_id)
+{
+       struct inf_hw *hw = dev_id;
+       u8 val;
+
+       spin_lock(&hw->lock);
+       val = inb((u32)hw->cfg.start + ELSA_IRQ_ADDR);
+       if (!(val & ELSA_IRQ_MASK)) {
+               spin_unlock(&hw->lock);
+               return IRQ_NONE; /* shared */
+       }
+       hw->irqcnt++;
+       mISDNipac_irq(&hw->ipac, irqloops);
+       spin_unlock(&hw->lock);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t
+niccy_irq(int intno, void *dev_id)
+{
+       struct inf_hw *hw = dev_id;
+       u32 val;
+
+       spin_lock(&hw->lock);
+       val = inl((u32)hw->cfg.start + NICCY_IRQ_CTRL_REG);
+       if (!(val & NICCY_IRQ_BIT)) { /* for us or shared ? */
+               spin_unlock(&hw->lock);
+               return IRQ_NONE; /* shared */
+       }
+       outl(val, (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG);
+       hw->irqcnt++;
+       mISDNipac_irq(&hw->ipac, irqloops);
+       spin_unlock(&hw->lock);
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t
+gazel_irq(int intno, void *dev_id)
+{
+       struct inf_hw *hw = dev_id;
+       irqreturn_t ret;
+
+       spin_lock(&hw->lock);
+       ret = mISDNipac_irq(&hw->ipac, irqloops);
+       spin_unlock(&hw->lock);
+       return ret;
+}
+
+static irqreturn_t
+ipac_irq(int intno, void *dev_id)
+{
+       struct inf_hw *hw = dev_id;
+       u8 val;
+
+       spin_lock(&hw->lock);
+       val = hw->ipac.read_reg(hw, IPAC_ISTA);
+       if (!(val & 0x3f)) {
+               spin_unlock(&hw->lock);
+               return IRQ_NONE; /* shared */
+       }
+       hw->irqcnt++;
+       mISDNipac_irq(&hw->ipac, irqloops);
+       spin_unlock(&hw->lock);
+       return IRQ_HANDLED;
+}
+
+static void
+enable_hwirq(struct inf_hw *hw)
+{
+       u16 w;
+       u32 val;
+
+       switch (hw->ci->typ) {
+       case INF_DIVA201:
+       case INF_DIVA202:
+               writel(PITA_INT0_ENABLE, hw->cfg.p);
+               break;
+       case INF_SPEEDWIN:
+       case INF_SAPHIR3:
+               outb(TIGER_IRQ_BIT, (u32)hw->cfg.start + TIGER_AUX_IRQMASK);
+               break;
+       case INF_QS1000:
+               outb(QS1000_IRQ_ON, (u32)hw->cfg.start + ELSA_IRQ_ADDR);
+               break;
+       case INF_QS3000:
+               outb(QS3000_IRQ_ON, (u32)hw->cfg.start + ELSA_IRQ_ADDR);
+               break;
+       case INF_NICCY:
+               val = inl((u32)hw->cfg.start + NICCY_IRQ_CTRL_REG);
+               val |= NICCY_IRQ_ENABLE;;
+               outl(val, (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG);
+               break;
+       case INF_SCT_1:
+               w = inw((u32)hw->cfg.start + SCT_PLX_IRQ_ADDR);
+               w |= SCT_PLX_IRQ_ENABLE;
+               outw(w, (u32)hw->cfg.start + SCT_PLX_IRQ_ADDR);
+               break;
+       case INF_GAZEL_R685:
+               outb(GAZEL_ISAC_EN + GAZEL_HSCX_EN + GAZEL_PCI_EN,
+                       (u32)hw->cfg.start + GAZEL_INCSR);
+               break;
+       case INF_GAZEL_R753:
+               outb(GAZEL_IPAC_EN + GAZEL_PCI_EN,
+                       (u32)hw->cfg.start + GAZEL_INCSR);
+               break;
+       default:
+               break;
+       }
+}
+
+static void
+disable_hwirq(struct inf_hw *hw)
+{
+       u16 w;
+       u32 val;
+
+       switch (hw->ci->typ) {
+       case INF_DIVA201:
+       case INF_DIVA202:
+               writel(0, hw->cfg.p);
+               break;
+       case INF_SPEEDWIN:
+       case INF_SAPHIR3:
+               outb(0, (u32)hw->cfg.start + TIGER_AUX_IRQMASK);
+               break;
+       case INF_QS1000:
+               outb(QS1000_IRQ_OFF, (u32)hw->cfg.start + ELSA_IRQ_ADDR);
+               break;
+       case INF_QS3000:
+               outb(QS3000_IRQ_OFF, (u32)hw->cfg.start + ELSA_IRQ_ADDR);
+               break;
+       case INF_NICCY:
+               val = inl((u32)hw->cfg.start + NICCY_IRQ_CTRL_REG);
+               val &= NICCY_IRQ_DISABLE;
+               outl(val, (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG);
+               break;
+       case INF_SCT_1:
+               w = inw((u32)hw->cfg.start + SCT_PLX_IRQ_ADDR);
+               w &= (~SCT_PLX_IRQ_ENABLE);
+               outw(w, (u32)hw->cfg.start + SCT_PLX_IRQ_ADDR);
+               break;
+       case INF_GAZEL_R685:
+       case INF_GAZEL_R753:
+               outb(0, (u32)hw->cfg.start + GAZEL_INCSR);
+               break;
+       default:
+               break;
+       }
+}
+
+static void
+ipac_chip_reset(struct inf_hw *hw)
+{
+       hw->ipac.write_reg(hw, IPAC_POTA2, 0x20);
+       mdelay(5);
+       hw->ipac.write_reg(hw, IPAC_POTA2, 0x00);
+       mdelay(5);
+       hw->ipac.write_reg(hw, IPAC_CONF, hw->ipac.conf);
+       hw->ipac.write_reg(hw, IPAC_MASK, 0xc0);
+}
+
+static void
+reset_inf(struct inf_hw *hw)
+{
+       u16 w;
+       u32 val;
+
+       if (debug & DEBUG_HW)
+               pr_notice("%s: resetting card\n", hw->name);
+       switch (hw->ci->typ) {
+       case INF_DIVA20:
+       case INF_DIVA20U:
+               outb(0, (u32)hw->cfg.start + DIVA_PCI_CTRL);
+               mdelay(10);
+               outb(DIVA_RESET_BIT, (u32)hw->cfg.start + DIVA_PCI_CTRL);
+               mdelay(10);
+               /* Workaround PCI9060 */
+               outb(9, (u32)hw->cfg.start + 0x69);
+               outb(DIVA_RESET_BIT | DIVA_LED_A,
+                       (u32)hw->cfg.start + DIVA_PCI_CTRL);
+               break;
+       case INF_DIVA201:
+               writel(PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE,
+                       hw->cfg.p + PITA_MISC_REG);
+               mdelay(1);
+               writel(PITA_PARA_MPX_MODE, hw->cfg.p + PITA_MISC_REG);
+               mdelay(10);
+               break;
+       case INF_DIVA202:
+               writel(PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE,
+                       hw->cfg.p + PITA_MISC_REG);
+               mdelay(1);
+               writel(PITA_PARA_MPX_MODE | PITA_SER_SOFTRESET,
+                       hw->cfg.p + PITA_MISC_REG);
+               mdelay(10);
+               break;
+       case INF_SPEEDWIN:
+       case INF_SAPHIR3:
+               ipac_chip_reset(hw);
+               hw->ipac.write_reg(hw, IPAC_ACFG, 0xff);
+               hw->ipac.write_reg(hw, IPAC_AOE, 0x00);
+               hw->ipac.write_reg(hw, IPAC_PCFG, 0x12);
+               break;
+       case INF_QS1000:
+       case INF_QS3000:
+               ipac_chip_reset(hw);
+               hw->ipac.write_reg(hw, IPAC_ACFG, 0x00);
+               hw->ipac.write_reg(hw, IPAC_AOE, 0x3c);
+               hw->ipac.write_reg(hw, IPAC_ATX, 0xff);
+               break;
+       case INF_NICCY:
+               break;
+       case INF_SCT_1:
+               w = inw((u32)hw->cfg.start + SCT_PLX_RESET_ADDR);
+               w &= (~SCT_PLX_RESET_BIT);
+               outw(w, (u32)hw->cfg.start + SCT_PLX_RESET_ADDR);
+               mdelay(10);
+               w = inw((u32)hw->cfg.start + SCT_PLX_RESET_ADDR);
+               w |= SCT_PLX_RESET_BIT;
+               outw(w, (u32)hw->cfg.start + SCT_PLX_RESET_ADDR);
+               mdelay(10);
+               break;
+       case INF_GAZEL_R685:
+               val = inl((u32)hw->cfg.start + GAZEL_CNTRL);
+               val |= (GAZEL_RESET_9050 + GAZEL_RESET);
+               outl(val, (u32)hw->cfg.start + GAZEL_CNTRL);
+               val &= ~(GAZEL_RESET_9050 + GAZEL_RESET);
+               mdelay(4);
+               outl(val, (u32)hw->cfg.start + GAZEL_CNTRL);
+               mdelay(10);
+               hw->ipac.isac.adf2 = 0x87;
+               hw->ipac.hscx[0].slot = 0x1f;
+               hw->ipac.hscx[0].slot = 0x23;
+               break;
+       case INF_GAZEL_R753:
+               val = inl((u32)hw->cfg.start + GAZEL_CNTRL);
+               val |= (GAZEL_RESET_9050 + GAZEL_RESET);
+               outl(val, (u32)hw->cfg.start + GAZEL_CNTRL);
+               val &= ~(GAZEL_RESET_9050 + GAZEL_RESET);
+               mdelay(4);
+               outl(val, (u32)hw->cfg.start + GAZEL_CNTRL);
+               mdelay(10);
+               ipac_chip_reset(hw);
+               hw->ipac.write_reg(hw, IPAC_ACFG, 0xff);
+               hw->ipac.write_reg(hw, IPAC_AOE, 0x00);
+               hw->ipac.conf = 0x01; /* IOM off */
+               break;
+       default:
+               return;
+       }
+       enable_hwirq(hw);
+}
+
+static int
+inf_ctrl(struct inf_hw *hw, u32 cmd, u_long arg)
+{
+       int ret = 0;
+
+       switch (cmd) {
+       case HW_RESET_REQ:
+               reset_inf(hw);
+               break;
+       default:
+               pr_info("%s: %s unknown command %x %lx\n",
+                       hw->name, __func__, cmd, arg);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int __devinit
+init_irq(struct inf_hw *hw)
+{
+       int     ret, cnt = 3;
+       u_long  flags;
+
+       if (!hw->ci->irqfunc)
+               return -EINVAL;
+       ret = request_irq(hw->irq, hw->ci->irqfunc, IRQF_SHARED, hw->name, hw);
+       if (ret) {
+               pr_info("%s: couldn't get interrupt %d\n", hw->name, hw->irq);
+               return ret;
+       }
+       while (cnt--) {
+               spin_lock_irqsave(&hw->lock, flags);
+               reset_inf(hw);
+               ret = hw->ipac.init(&hw->ipac);
+               if (ret) {
+                       spin_unlock_irqrestore(&hw->lock, flags);
+                       pr_info("%s: ISAC init failed with %d\n",
+                               hw->name, ret);
+                       break;
+               }
+               spin_unlock_irqrestore(&hw->lock, flags);
+               msleep_interruptible(10);
+               if (debug & DEBUG_HW)
+                       pr_notice("%s: IRQ %d count %d\n", hw->name,
+                               hw->irq, hw->irqcnt);
+               if (!hw->irqcnt) {
+                       pr_info("%s: IRQ(%d) got no requests during init %d\n",
+                               hw->name, hw->irq, 3 - cnt);
+               } else
+                       return 0;
+       }
+       free_irq(hw->irq, hw);
+       return -EIO;
+}
+
+static void
+release_io(struct inf_hw *hw)
+{
+       if (hw->cfg.mode) {
+               if (hw->cfg.p) {
+                       release_mem_region(hw->cfg.start, hw->cfg.size);
+                       iounmap(hw->cfg.p);
+               } else
+                       release_region(hw->cfg.start, hw->cfg.size);
+               hw->cfg.mode = AM_NONE;
+       }
+       if (hw->addr.mode) {
+               if (hw->addr.p) {
+                       release_mem_region(hw->addr.start, hw->addr.size);
+                       iounmap(hw->addr.p);
+               } else
+                       release_region(hw->addr.start, hw->addr.size);
+               hw->addr.mode = AM_NONE;
+       }
+}
+
+static int __devinit
+setup_io(struct inf_hw *hw)
+{
+       int err = 0;
+
+       if (hw->ci->cfg_mode) {
+               hw->cfg.start = pci_resource_start(hw->pdev, hw->ci->cfg_bar);
+               hw->cfg.size = pci_resource_len(hw->pdev, hw->ci->cfg_bar);
+               if (hw->ci->cfg_mode == AM_MEMIO) {
+                       if (!request_mem_region(hw->cfg.start, hw->cfg.size,
+                           hw->name))
+                               err = -EBUSY;
+               } else {
+                       if (!request_region(hw->cfg.start, hw->cfg.size,
+                           hw->name))
+                               err = -EBUSY;
+               }
+               if (err) {
+                       pr_info("mISDN: %s config port %lx (%lu bytes)"
+                               "already in use\n", hw->name,
+                               (ulong)hw->cfg.start, (ulong)hw->cfg.size);
+                       return err;
+               }
+               if (hw->ci->cfg_mode == AM_MEMIO)
+                       hw->cfg.p = ioremap(hw->cfg.start, hw->cfg.size);
+               hw->cfg.mode = hw->ci->cfg_mode;
+               if (debug & DEBUG_HW)
+                       pr_notice("%s: IO cfg %lx (%lu bytes) mode%d\n",
+                               hw->name, (ulong)hw->cfg.start,
+                               (ulong)hw->cfg.size, hw->ci->cfg_mode);
+
+       }
+       if (hw->ci->addr_mode) {
+               hw->addr.start = pci_resource_start(hw->pdev, hw->ci->addr_bar);
+               hw->addr.size = pci_resource_len(hw->pdev, hw->ci->addr_bar);
+               if (hw->ci->addr_mode == AM_MEMIO) {
+                       if (!request_mem_region(hw->addr.start, hw->addr.size,
+                           hw->name))
+                               err = -EBUSY;
+               } else {
+                       if (!request_region(hw->addr.start, hw->addr.size,
+                           hw->name))
+                               err = -EBUSY;
+               }
+               if (err) {
+                       pr_info("mISDN: %s address port %lx (%lu bytes)"
+                               "already in use\n", hw->name,
+                               (ulong)hw->addr.start, (ulong)hw->addr.size);
+                       return err;
+               }
+               if (hw->ci->addr_mode == AM_MEMIO)
+                       hw->addr.p = ioremap(hw->addr.start, hw->addr.size);
+               hw->addr.mode = hw->ci->addr_mode;
+               if (debug & DEBUG_HW)
+                       pr_notice("%s: IO addr %lx (%lu bytes) mode%d\n",
+                               hw->name, (ulong)hw->addr.start,
+                               (ulong)hw->addr.size, hw->ci->addr_mode);
+
+       }
+
+       switch (hw->ci->typ) {
+       case INF_DIVA20:
+       case INF_DIVA20U:
+               hw->ipac.type = IPAC_TYPE_ISAC | IPAC_TYPE_HSCX;
+               hw->isac.mode = hw->cfg.mode;
+               hw->isac.a.io.ale = (u32)hw->cfg.start + DIVA_ISAC_ALE;
+               hw->isac.a.io.port = (u32)hw->cfg.start + DIVA_ISAC_PORT;
+               hw->hscx.mode = hw->cfg.mode;
+               hw->hscx.a.io.ale = (u32)hw->cfg.start + DIVA_HSCX_ALE;
+               hw->hscx.a.io.port = (u32)hw->cfg.start + DIVA_HSCX_PORT;
+               break;
+       case INF_DIVA201:
+               hw->ipac.type = IPAC_TYPE_IPAC;
+               hw->ipac.isac.off = 0x80;
+               hw->isac.mode = hw->addr.mode;
+               hw->isac.a.p = hw->addr.p;
+               hw->hscx.mode = hw->addr.mode;
+               hw->hscx.a.p = hw->addr.p;
+               break;
+       case INF_DIVA202:
+               hw->ipac.type = IPAC_TYPE_IPACX;
+               hw->isac.mode = hw->addr.mode;
+               hw->isac.a.p = hw->addr.p;
+               hw->hscx.mode = hw->addr.mode;
+               hw->hscx.a.p = hw->addr.p;
+               break;
+       case INF_SPEEDWIN:
+       case INF_SAPHIR3:
+               hw->ipac.type = IPAC_TYPE_IPAC;
+               hw->ipac.isac.off = 0x80;
+               hw->isac.mode = hw->cfg.mode;
+               hw->isac.a.io.ale = (u32)hw->cfg.start + TIGER_IPAC_ALE;
+               hw->isac.a.io.port = (u32)hw->cfg.start + TIGER_IPAC_PORT;
+               hw->hscx.mode = hw->cfg.mode;
+               hw->hscx.a.io.ale = (u32)hw->cfg.start + TIGER_IPAC_ALE;
+               hw->hscx.a.io.port = (u32)hw->cfg.start + TIGER_IPAC_PORT;
+               outb(0xff, (ulong)hw->cfg.start);
+               mdelay(1);
+               outb(0x00, (ulong)hw->cfg.start);
+               mdelay(1);
+               outb(TIGER_IOMASK, (ulong)hw->cfg.start + TIGER_AUX_CTRL);
+               break;
+       case INF_QS1000:
+       case INF_QS3000:
+               hw->ipac.type = IPAC_TYPE_IPAC;
+               hw->ipac.isac.off = 0x80;
+               hw->isac.a.io.ale = (u32)hw->addr.start;
+               hw->isac.a.io.port = (u32)hw->addr.start + 1;
+               hw->isac.mode = hw->addr.mode;
+               hw->hscx.a.io.ale = (u32)hw->addr.start;
+               hw->hscx.a.io.port = (u32)hw->addr.start + 1;
+               hw->hscx.mode = hw->addr.mode;
+               break;
+       case INF_NICCY:
+               hw->ipac.type = IPAC_TYPE_ISAC | IPAC_TYPE_HSCX;
+               hw->isac.mode = hw->addr.mode;
+               hw->isac.a.io.ale = (u32)hw->addr.start + NICCY_ISAC_ALE;
+               hw->isac.a.io.port = (u32)hw->addr.start + NICCY_ISAC_PORT;
+               hw->hscx.mode = hw->addr.mode;
+               hw->hscx.a.io.ale = (u32)hw->addr.start + NICCY_HSCX_ALE;
+               hw->hscx.a.io.port = (u32)hw->addr.start + NICCY_HSCX_PORT;
+               break;
+       case INF_SCT_1:
+               hw->ipac.type = IPAC_TYPE_IPAC;
+               hw->ipac.isac.off = 0x80;
+               hw->isac.a.io.ale = (u32)hw->addr.start;
+               hw->isac.a.io.port = hw->isac.a.io.ale + 4;
+               hw->isac.mode = hw->addr.mode;
+               hw->hscx.a.io.ale = hw->isac.a.io.ale;
+               hw->hscx.a.io.port = hw->isac.a.io.port;
+               hw->hscx.mode = hw->addr.mode;
+               break;
+       case INF_SCT_2:
+               hw->ipac.type = IPAC_TYPE_IPAC;
+               hw->ipac.isac.off = 0x80;
+               hw->isac.a.io.ale = (u32)hw->addr.start + 0x08;
+               hw->isac.a.io.port = hw->isac.a.io.ale + 4;
+               hw->isac.mode = hw->addr.mode;
+               hw->hscx.a.io.ale = hw->isac.a.io.ale;
+               hw->hscx.a.io.port = hw->isac.a.io.port;
+               hw->hscx.mode = hw->addr.mode;
+               break;
+       case INF_SCT_3:
+               hw->ipac.type = IPAC_TYPE_IPAC;
+               hw->ipac.isac.off = 0x80;
+               hw->isac.a.io.ale = (u32)hw->addr.start + 0x10;
+               hw->isac.a.io.port = hw->isac.a.io.ale + 4;
+               hw->isac.mode = hw->addr.mode;
+               hw->hscx.a.io.ale = hw->isac.a.io.ale;
+               hw->hscx.a.io.port = hw->isac.a.io.port;
+               hw->hscx.mode = hw->addr.mode;
+               break;
+       case INF_SCT_4:
+               hw->ipac.type = IPAC_TYPE_IPAC;
+               hw->ipac.isac.off = 0x80;
+               hw->isac.a.io.ale = (u32)hw->addr.start + 0x20;
+               hw->isac.a.io.port = hw->isac.a.io.ale + 4;
+               hw->isac.mode = hw->addr.mode;
+               hw->hscx.a.io.ale = hw->isac.a.io.ale;
+               hw->hscx.a.io.port = hw->isac.a.io.port;
+               hw->hscx.mode = hw->addr.mode;
+               break;
+       case INF_GAZEL_R685:
+               hw->ipac.type = IPAC_TYPE_ISAC | IPAC_TYPE_HSCX;
+               hw->ipac.isac.off = 0x80;
+               hw->isac.mode = hw->addr.mode;
+               hw->isac.a.io.port = (u32)hw->addr.start;
+               hw->hscx.mode = hw->addr.mode;
+               hw->hscx.a.io.port = hw->isac.a.io.port;
+               break;
+       case INF_GAZEL_R753:
+               hw->ipac.type = IPAC_TYPE_IPAC;
+               hw->ipac.isac.off = 0x80;
+               hw->isac.mode = hw->addr.mode;
+               hw->isac.a.io.ale = (u32)hw->addr.start;
+               hw->isac.a.io.port = (u32)hw->addr.start + GAZEL_IPAC_DATA_PORT;
+               hw->hscx.mode = hw->addr.mode;
+               hw->hscx.a.io.ale = hw->isac.a.io.ale;
+               hw->hscx.a.io.port = hw->isac.a.io.port;
+               break;
+       default:
+               return -EINVAL;
+       }
+       switch (hw->isac.mode) {
+       case AM_MEMIO:
+               ASSIGN_FUNC_IPAC(MIO, hw->ipac);
+               break;
+       case AM_IND_IO:
+               ASSIGN_FUNC_IPAC(IND, hw->ipac);
+               break;
+       case AM_IO:
+               ASSIGN_FUNC_IPAC(IO, hw->ipac);
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static void
+release_card(struct inf_hw *card) {
+       ulong   flags;
+       int     i;
+
+       spin_lock_irqsave(&card->lock, flags);
+       disable_hwirq(card);
+       spin_unlock_irqrestore(&card->lock, flags);
+       card->ipac.isac.release(&card->ipac.isac);
+       free_irq(card->irq, card);
+       mISDN_unregister_device(&card->ipac.isac.dch.dev);
+       release_io(card);
+       write_lock_irqsave(&card_lock, flags);
+       list_del(&card->list);
+       write_unlock_irqrestore(&card_lock, flags);
+       switch (card->ci->typ) {
+       case INF_SCT_2:
+       case INF_SCT_3:
+       case INF_SCT_4:
+               break;
+       case INF_SCT_1:
+               for (i = 0; i < 3; i++) {
+                       if (card->sc[i])
+                               release_card(card->sc[i]);
+                       card->sc[i] = NULL;
+               }
+       default:
+               pci_disable_device(card->pdev);
+               pci_set_drvdata(card->pdev, NULL);
+               break;
+       }
+       kfree(card);
+       inf_cnt--;
+}
+
+static int __devinit
+setup_instance(struct inf_hw *card)
+{
+       int err;
+       ulong flags;
+
+       snprintf(card->name, MISDN_MAX_IDLEN - 1, "%s.%d", card->ci->name,
+               inf_cnt + 1);
+       write_lock_irqsave(&card_lock, flags);
+       list_add_tail(&card->list, &Cards);
+       write_unlock_irqrestore(&card_lock, flags);
+
+       _set_debug(card);
+       card->ipac.isac.name = card->name;
+       card->ipac.name = card->name;
+       card->ipac.owner = THIS_MODULE;
+       spin_lock_init(&card->lock);
+       card->ipac.isac.hwlock = &card->lock;
+       card->ipac.hwlock = &card->lock;
+       card->ipac.ctrl = (void *)&inf_ctrl;
+
+       err = setup_io(card);
+       if (err)
+               goto error_setup;
+
+       card->ipac.isac.dch.dev.Bprotocols =
+               mISDNipac_init(&card->ipac, card);
+
+       if (card->ipac.isac.dch.dev.Bprotocols == 0)
+               goto error_setup;;
+
+       err = mISDN_register_device(&card->ipac.isac.dch.dev,
+               &card->pdev->dev, card->name);
+       if (err)
+               goto error;
+
+       err = init_irq(card);
+       if (!err)  {
+               inf_cnt++;
+               pr_notice("Infineon %d cards installed\n", inf_cnt);
+               return 0;
+       }
+       mISDN_unregister_device(&card->ipac.isac.dch.dev);
+error:
+       card->ipac.release(&card->ipac);
+error_setup:
+       release_io(card);
+       write_lock_irqsave(&card_lock, flags);
+       list_del(&card->list);
+       write_unlock_irqrestore(&card_lock, flags);
+       return err;
+}
+
+static const struct inf_cinfo inf_card_info[] = {
+       {
+               INF_DIVA20,
+               "Dialogic Diva 2.0",
+               "diva20",
+               AM_IND_IO, AM_NONE, 2, 0,
+               &diva_irq
+       },
+       {
+               INF_DIVA20U,
+               "Dialogic Diva 2.0U",
+               "diva20U",
+               AM_IND_IO, AM_NONE, 2, 0,
+               &diva_irq
+       },
+       {
+               INF_DIVA201,
+               "Dialogic Diva 2.01",
+               "diva201",
+               AM_MEMIO, AM_MEMIO, 0, 1,
+               &diva20x_irq
+       },
+       {
+               INF_DIVA202,
+               "Dialogic Diva 2.02",
+               "diva202",
+               AM_MEMIO, AM_MEMIO, 0, 1,
+               &diva20x_irq
+       },
+       {
+               INF_SPEEDWIN,
+               "Sedlbauer SpeedWin PCI",
+               "speedwin",
+               AM_IND_IO, AM_NONE, 0, 0,
+               &tiger_irq
+       },
+       {
+               INF_SAPHIR3,
+               "HST Saphir 3",
+               "saphir",
+               AM_IND_IO, AM_NONE, 0, 0,
+               &tiger_irq
+       },
+       {
+               INF_QS1000,
+               "Develo Microlink PCI",
+               "qs1000",
+               AM_IO, AM_IND_IO, 1, 3,
+               &elsa_irq
+       },
+       {
+               INF_QS3000,
+               "Develo QuickStep 3000",
+               "qs3000",
+               AM_IO, AM_IND_IO, 1, 3,
+               &elsa_irq
+       },
+       {
+               INF_NICCY,
+               "Sagem NICCY",
+               "niccy",
+               AM_IO, AM_IND_IO, 0, 1,
+               &niccy_irq
+       },
+       {
+               INF_SCT_1,
+               "SciTel Quadro",
+               "p1_scitel",
+               AM_IO, AM_IND_IO, 1, 5,
+               &ipac_irq
+       },
+       {
+               INF_SCT_2,
+               "SciTel Quadro",
+               "p2_scitel",
+               AM_NONE, AM_IND_IO, 0, 4,
+               &ipac_irq
+       },
+       {
+               INF_SCT_3,
+               "SciTel Quadro",
+               "p3_scitel",
+               AM_NONE, AM_IND_IO, 0, 3,
+               &ipac_irq
+       },
+       {
+               INF_SCT_4,
+               "SciTel Quadro",
+               "p4_scitel",
+               AM_NONE, AM_IND_IO, 0, 2,
+               &ipac_irq
+       },
+       {
+               INF_GAZEL_R685,
+               "Gazel R685",
+               "gazel685",
+               AM_IO, AM_IO, 1, 2,
+               &gazel_irq
+       },
+       {
+               INF_GAZEL_R753,
+               "Gazel R753",
+               "gazel753",
+               AM_IO, AM_IND_IO, 1, 2,
+               &ipac_irq
+       },
+       {
+               INF_NONE,
+       }
+};
+
+static const struct inf_cinfo * __devinit
+get_card_info(enum inf_types typ)
+{
+       const struct inf_cinfo *ci = inf_card_info;
+
+       while (ci->typ != INF_NONE) {
+               if (ci->typ == typ)
+                       return ci;
+               ci++;
+       }
+       return NULL;
+}
+
+static int __devinit
+inf_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+       int err = -ENOMEM;
+       struct inf_hw *card;
+
+       card = kzalloc(sizeof(struct inf_hw), GFP_KERNEL);
+       if (!card) {
+               pr_info("No memory for Infineon ISDN card\n");
+               return err;
+       }
+       card->pdev = pdev;
+       err = pci_enable_device(pdev);
+       if (err) {
+               kfree(card);
+               return err;
+       }
+       card->ci = get_card_info(ent->driver_data);
+       if (!card->ci) {
+               pr_info("mISDN: do not have informations about adapter at %s\n",
+                       pci_name(pdev));
+               kfree(card);
+               return -EINVAL;
+       } else
+               pr_notice("mISDN: found adapter %s at %s\n",
+                       card->ci->full, pci_name(pdev));
+
+       card->irq = pdev->irq;
+       pci_set_drvdata(pdev, card);
+       err = setup_instance(card);
+       if (err) {
+               pci_disable_device(card->pdev);
+               kfree(card);
+               pci_set_drvdata(pdev, NULL);
+       } else if (ent->driver_data == INF_SCT_1) {
+               int i;
+               struct inf_hw *sc;
+
+               for (i = 1; i < 4; i++) {
+                       sc = kzalloc(sizeof(struct inf_hw), GFP_KERNEL);
+                       if (!sc) {
+                               release_card(card);
+                               return -ENOMEM;
+                       }
+                       sc->irq = card->irq;
+                       sc->pdev = card->pdev;
+                       sc->ci = card->ci + i;
+                       err = setup_instance(sc);
+                       if (err) {
+                               kfree(sc);
+                               release_card(card);
+                       } else
+                               card->sc[i - 1] = sc;
+               }
+       }
+       return err;
+}
+
+static void __devexit
+inf_remove(struct pci_dev *pdev)
+{
+       struct inf_hw   *card = pci_get_drvdata(pdev);
+
+       if (card)
+               release_card(card);
+       else
+               pr_debug("%s: drvdata allready removed\n", __func__);
+}
+
+static struct pci_driver infineon_driver = {
+       .name = "ISDN Infineon pci",
+       .probe = inf_probe,
+       .remove = __devexit_p(inf_remove),
+       .id_table = infineon_ids,
+};
+
+static int __init
+infineon_init(void)
+{
+       int err;
+
+       pr_notice("Infineon ISDN Driver Rev. %s\n", INFINEON_REV);
+       err = pci_register_driver(&infineon_driver);
+       return err;
+}
+
+static void __exit
+infineon_cleanup(void)
+{
+       pci_unregister_driver(&infineon_driver);
+}
+
+module_init(infineon_init);
+module_exit(infineon_cleanup);
diff --git a/drivers/isdn/hardware/mISDN/mISDNipac.c b/drivers/isdn/hardware/mISDN/mISDNipac.c
new file mode 100644 (file)
index 0000000..613ba04
--- /dev/null
@@ -0,0 +1,1655 @@
+/*
+ * isac.c   ISAC specific routines
+ *
+ * Author       Karsten Keil <keil@isdn4linux.de>
+ *
+ * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/mISDNhw.h>
+#include "ipac.h"
+
+
+#define DBUSY_TIMER_VALUE      80
+#define ARCOFI_USE             1
+
+#define ISAC_REV               "2.0"
+
+MODULE_AUTHOR("Karsten Keil");
+MODULE_VERSION(ISAC_REV);
+MODULE_LICENSE("GPL v2");
+
+#define ReadISAC(is, o)                (is->read_reg(is->dch.hw, o + is->off))
+#define        WriteISAC(is, o, v)     (is->write_reg(is->dch.hw, o + is->off, v))
+#define ReadHSCX(h, o)         (h->ip->read_reg(h->ip->hw, h->off + o))
+#define WriteHSCX(h, o, v)     (h->ip->write_reg(h->ip->hw, h->off + o, v))
+#define ReadIPAC(ip, o)                (ip->read_reg(ip->hw, o))
+#define WriteIPAC(ip, o, v)    (ip->write_reg(ip->hw, o, v))
+
+static inline void
+ph_command(struct isac_hw *isac, u8 command)
+{
+       pr_debug("%s: ph_command %x\n", isac->name, command);
+       if (isac->type & IPAC_TYPE_ISACX)
+               WriteISAC(isac, ISACX_CIX0, (command << 4) | 0xE);
+       else
+               WriteISAC(isac, ISAC_CIX0, (command << 2) | 3);
+}
+
+static void
+isac_ph_state_change(struct isac_hw *isac)
+{
+       switch (isac->state) {
+       case (ISAC_IND_RS):
+       case (ISAC_IND_EI):
+               ph_command(isac, ISAC_CMD_DUI);
+       }
+       schedule_event(&isac->dch, FLG_PHCHANGE);
+}
+
+static void
+isac_ph_state_bh(struct dchannel *dch)
+{
+       struct isac_hw *isac = container_of(dch, struct isac_hw, dch);
+
+       switch (isac->state) {
+       case ISAC_IND_RS:
+       case ISAC_IND_EI:
+               dch->state = 0;
+               l1_event(dch->l1, HW_RESET_IND);
+               break;
+       case ISAC_IND_DID:
+               dch->state = 3;
+               l1_event(dch->l1, HW_DEACT_CNF);
+               break;
+       case ISAC_IND_DR:
+               dch->state = 3;
+               l1_event(dch->l1, HW_DEACT_IND);
+               break;
+       case ISAC_IND_PU:
+               dch->state = 4;
+               l1_event(dch->l1, HW_POWERUP_IND);
+               break;
+       case ISAC_IND_RSY:
+               if (dch->state <= 5) {
+                       dch->state = 5;
+                       l1_event(dch->l1, ANYSIGNAL);
+               } else {
+                       dch->state = 8;
+                       l1_event(dch->l1, LOSTFRAMING);
+               }
+               break;
+       case ISAC_IND_ARD:
+               dch->state = 6;
+               l1_event(dch->l1, INFO2);
+               break;
+       case ISAC_IND_AI8:
+               dch->state = 7;
+               l1_event(dch->l1, INFO4_P8);
+               break;
+       case ISAC_IND_AI10:
+               dch->state = 7;
+               l1_event(dch->l1, INFO4_P10);
+               break;
+       }
+       pr_debug("%s: TE newstate %x\n", isac->name, dch->state);
+}
+
+void
+isac_empty_fifo(struct isac_hw *isac, int count)
+{
+       u8 *ptr;
+
+       pr_debug("%s: %s  %d\n", isac->name, __func__, count);
+
+       if (!isac->dch.rx_skb) {
+               isac->dch.rx_skb = mI_alloc_skb(isac->dch.maxlen, GFP_ATOMIC);
+               if (!isac->dch.rx_skb) {
+                       pr_info("%s: D receive out of memory\n", isac->name);
+                       WriteISAC(isac, ISAC_CMDR, 0x80);
+                       return;
+               }
+       }
+       if ((isac->dch.rx_skb->len + count) >= isac->dch.maxlen) {
+               pr_debug("%s: %s overrun %d\n", isac->name, __func__,
+                           isac->dch.rx_skb->len + count);
+               WriteISAC(isac, ISAC_CMDR, 0x80);
+               return;
+       }
+       ptr = skb_put(isac->dch.rx_skb, count);
+       isac->read_fifo(isac->dch.hw, isac->off, ptr, count);
+       WriteISAC(isac, ISAC_CMDR, 0x80);
+       if (isac->dch.debug & DEBUG_HW_DFIFO) {
+               char    pfx[MISDN_MAX_IDLEN + 16];
+
+               snprintf(pfx, MISDN_MAX_IDLEN + 15, "D-recv %s %d ",
+                       isac->name, count);
+               print_hex_dump_bytes(pfx, DUMP_PREFIX_OFFSET, ptr, count);
+       }
+}
+
+static void
+isac_fill_fifo(struct isac_hw *isac)
+{
+       int count, more;
+       u8 *ptr;
+
+       if (!isac->dch.tx_skb)
+               return;
+       count = isac->dch.tx_skb->len - isac->dch.tx_idx;
+       if (count <= 0)
+               return;
+
+       more = 0;
+       if (count > 32) {
+               more = !0;
+               count = 32;
+       }
+       pr_debug("%s: %s  %d\n", isac->name, __func__, count);
+       ptr = isac->dch.tx_skb->data + isac->dch.tx_idx;
+       isac->dch.tx_idx += count;
+       isac->write_fifo(isac->dch.hw, isac->off, ptr, count);
+       WriteISAC(isac, ISAC_CMDR, more ? 0x8 : 0xa);
+       if (test_and_set_bit(FLG_BUSY_TIMER, &isac->dch.Flags)) {
+               pr_debug("%s: %s dbusytimer running\n", isac->name, __func__);
+               del_timer(&isac->dch.timer);
+       }
+       init_timer(&isac->dch.timer);
+       isac->dch.timer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
+       add_timer(&isac->dch.timer);
+       if (isac->dch.debug & DEBUG_HW_DFIFO) {
+               char    pfx[MISDN_MAX_IDLEN + 16];
+
+               snprintf(pfx, MISDN_MAX_IDLEN + 15, "D-send %s %d ",
+                       isac->name, count);
+               print_hex_dump_bytes(pfx, DUMP_PREFIX_OFFSET, ptr, count);
+       }
+}
+
+static void
+isac_rme_irq(struct isac_hw *isac)
+{
+       u8 val, count;
+
+       val = ReadISAC(isac, ISAC_RSTA);
+       if ((val & 0x70) != 0x20) {
+               if (val & 0x40) {
+                       pr_debug("%s: ISAC RDO\n", isac->name);
+#ifdef ERROR_STATISTIC
+                       isac->dch.err_rx++;
+#endif
+               }
+               if (!(val & 0x20)) {
+                       pr_debug("%s: ISAC CRC error\n", isac->name);
+#ifdef ERROR_STATISTIC
+                       isac->dch.err_crc++;
+#endif
+               }
+               WriteISAC(isac, ISAC_CMDR, 0x80);
+               if (isac->dch.rx_skb)
+                       dev_kfree_skb(isac->dch.rx_skb);
+               isac->dch.rx_skb = NULL;
+       } else {
+               count = ReadISAC(isac, ISAC_RBCL) & 0x1f;
+               if (count == 0)
+                       count = 32;
+               isac_empty_fifo(isac, count);
+               recv_Dchannel(&isac->dch);
+       }
+}
+
+static void
+isac_xpr_irq(struct isac_hw *isac)
+{
+       if (test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags))
+               del_timer(&isac->dch.timer);
+       if (isac->dch.tx_skb && isac->dch.tx_idx < isac->dch.tx_skb->len) {
+               isac_fill_fifo(isac);
+       } else {
+               if (isac->dch.tx_skb)
+                       dev_kfree_skb(isac->dch.tx_skb);
+               if (get_next_dframe(&isac->dch))
+                       isac_fill_fifo(isac);
+       }
+}
+
+static void
+isac_retransmit(struct isac_hw *isac)
+{
+       if (test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags))
+               del_timer(&isac->dch.timer);
+       if (test_bit(FLG_TX_BUSY, &isac->dch.Flags)) {
+               /* Restart frame */
+               isac->dch.tx_idx = 0;
+               isac_fill_fifo(isac);
+       } else if (isac->dch.tx_skb) { /* should not happen */
+               pr_info("%s: tx_skb exist but not busy\n", isac->name);
+               test_and_set_bit(FLG_TX_BUSY, &isac->dch.Flags);
+               isac->dch.tx_idx = 0;
+               isac_fill_fifo(isac);
+       } else {
+               pr_info("%s: ISAC XDU no TX_BUSY\n", isac->name);
+               if (get_next_dframe(&isac->dch))
+                       isac_fill_fifo(isac);
+       }
+}
+
+static void
+isac_mos_irq(struct isac_hw *isac)
+{
+       u8 val;
+       int ret;
+
+       val = ReadISAC(isac, ISAC_MOSR);
+       pr_debug("%s: ISAC MOSR %02x\n", isac->name, val);
+#if ARCOFI_USE
+       if (val & 0x08) {
+               if (!isac->mon_rx) {
+                       isac->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC);
+                       if (!isac->mon_rx) {
+                               pr_info("%s: ISAC MON RX out of memory!\n",
+                                       isac->name);
+                               isac->mocr &= 0xf0;
+                               isac->mocr |= 0x0a;
+                               WriteISAC(isac, ISAC_MOCR, isac->mocr);
+                               goto afterMONR0;
+                       } else
+                               isac->mon_rxp = 0;
+               }
+               if (isac->mon_rxp >= MAX_MON_FRAME) {
+                       isac->mocr &= 0xf0;
+                       isac->mocr |= 0x0a;
+                       WriteISAC(isac, ISAC_MOCR, isac->mocr);
+                       isac->mon_rxp = 0;
+                       pr_debug("%s: ISAC MON RX overflow!\n", isac->name);
+                       goto afterMONR0;
+               }
+               isac->mon_rx[isac->mon_rxp++] = ReadISAC(isac, ISAC_MOR0);
+               pr_debug("%s: ISAC MOR0 %02x\n", isac->name,
+                       isac->mon_rx[isac->mon_rxp - 1]);
+               if (isac->mon_rxp == 1) {
+                       isac->mocr |= 0x04;
+                       WriteISAC(isac, ISAC_MOCR, isac->mocr);
+               }
+       }
+afterMONR0:
+       if (val & 0x80) {
+               if (!isac->mon_rx) {
+                       isac->mon_rx = kmalloc(MAX_MON_FRAME, GFP_ATOMIC);
+                       if (!isac->mon_rx) {
+                               pr_info("%s: ISAC MON RX out of memory!\n",
+                                       isac->name);
+                               isac->mocr &= 0x0f;
+                               isac->mocr |= 0xa0;
+                               WriteISAC(isac, ISAC_MOCR, isac->mocr);
+                               goto afterMONR1;
+                       } else
+                               isac->mon_rxp = 0;
+               }
+               if (isac->mon_rxp >= MAX_MON_FRAME) {
+                       isac->mocr &= 0x0f;
+                       isac->mocr |= 0xa0;
+                       WriteISAC(isac, ISAC_MOCR, isac->mocr);
+                       isac->mon_rxp = 0;
+                       pr_debug("%s: ISAC MON RX overflow!\n", isac->name);
+                       goto afterMONR1;
+               }
+               isac->mon_rx[isac->mon_rxp++] = ReadISAC(isac, ISAC_MOR1);
+               pr_debug("%s: ISAC MOR1 %02x\n", isac->name,
+                       isac->mon_rx[isac->mon_rxp - 1]);
+               isac->mocr |= 0x40;
+               WriteISAC(isac, ISAC_MOCR, isac->mocr);
+       }
+afterMONR1:
+       if (val & 0x04) {
+               isac->mocr &= 0xf0;
+               WriteISAC(isac, ISAC_MOCR, isac->mocr);
+               isac->mocr |= 0x0a;
+               WriteISAC(isac, ISAC_MOCR, isac->mocr);
+               if (isac->monitor) {
+                       ret = isac->monitor(isac->dch.hw, MONITOR_RX_0,
+                               isac->mon_rx, isac->mon_rxp);
+                       if (ret)
+                               kfree(isac->mon_rx);
+               } else {
+                       pr_info("%s: MONITOR 0 received %d but no user\n",
+                               isac->name, isac->mon_rxp);
+                       kfree(isac->mon_rx);
+               }
+               isac->mon_rx = NULL;
+               isac->mon_rxp = 0;
+       }
+       if (val & 0x40) {
+               isac->mocr &= 0x0f;
+               WriteISAC(isac, ISAC_MOCR, isac->mocr);
+               isac->mocr |= 0xa0;
+               WriteISAC(isac, ISAC_MOCR, isac->mocr);
+               if (isac->monitor) {
+                       ret = isac->monitor(isac->dch.hw, MONITOR_RX_1,
+                               isac->mon_rx, isac->mon_rxp);
+                       if (ret)
+                               kfree(isac->mon_rx);
+               } else {
+                       pr_info("%s: MONITOR 1 received %d but no user\n",
+                               isac->name, isac->mon_rxp);
+                       kfree(isac->mon_rx);
+               }
+               isac->mon_rx = NULL;
+               isac->mon_rxp = 0;
+       }
+       if (val & 0x02) {
+               if ((!isac->mon_tx) || (isac->mon_txc &&
+                       (isac->mon_txp >= isac->mon_txc) && !(val & 0x08))) {
+                       isac->mocr &= 0xf0;
+                       WriteISAC(isac, ISAC_MOCR, isac->mocr);
+                       isac->mocr |= 0x0a;
+                       WriteISAC(isac, ISAC_MOCR, isac->mocr);
+                       if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {
+                               if (isac->monitor)
+                                       ret = isac->monitor(isac->dch.hw,
+                                               MONITOR_TX_0, NULL, 0);
+                       }
+                       kfree(isac->mon_tx);
+                       isac->mon_tx = NULL;
+                       isac->mon_txc = 0;
+                       isac->mon_txp = 0;
+                       goto AfterMOX0;
+               }
+               if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {
+                       if (isac->monitor)
+                               ret = isac->monitor(isac->dch.hw,
+                                       MONITOR_TX_0, NULL, 0);
+                       kfree(isac->mon_tx);
+                       isac->mon_tx = NULL;
+                       isac->mon_txc = 0;
+                       isac->mon_txp = 0;
+                       goto AfterMOX0;
+               }
+               WriteISAC(isac, ISAC_MOX0, isac->mon_tx[isac->mon_txp++]);
+               pr_debug("%s: ISAC %02x -> MOX0\n", isac->name,
+                       isac->mon_tx[isac->mon_txp - 1]);
+       }
+AfterMOX0:
+       if (val & 0x20) {
+               if ((!isac->mon_tx) || (isac->mon_txc &&
+                       (isac->mon_txp >= isac->mon_txc) && !(val & 0x80))) {
+                       isac->mocr &= 0x0f;
+                       WriteISAC(isac, ISAC_MOCR, isac->mocr);
+                       isac->mocr |= 0xa0;
+                       WriteISAC(isac, ISAC_MOCR, isac->mocr);
+                       if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {
+                               if (isac->monitor)
+                                       ret = isac->monitor(isac->dch.hw,
+                                               MONITOR_TX_1, NULL, 0);
+                       }
+                       kfree(isac->mon_tx);
+                       isac->mon_tx = NULL;
+                       isac->mon_txc = 0;
+                       isac->mon_txp = 0;
+                       goto AfterMOX1;
+               }
+               if (isac->mon_txc && (isac->mon_txp >= isac->mon_txc)) {
+                       if (isac->monitor)
+                               ret = isac->monitor(isac->dch.hw,
+                                       MONITOR_TX_1, NULL, 0);
+                       kfree(isac->mon_tx);
+                       isac->mon_tx = NULL;
+                       isac->mon_txc = 0;
+                       isac->mon_txp = 0;
+                       goto AfterMOX1;
+               }
+               WriteISAC(isac, ISAC_MOX1, isac->mon_tx[isac->mon_txp++]);
+               pr_debug("%s: ISAC %02x -> MOX1\n", isac->name,
+                       isac->mon_tx[isac->mon_txp - 1]);
+       }
+AfterMOX1:
+       val = 0; /* dummy to avoid warning */
+#endif
+}
+
+static void
+isac_cisq_irq(struct isac_hw *isac) {
+       u8 val;
+
+       val = ReadISAC(isac, ISAC_CIR0);
+       pr_debug("%s: ISAC CIR0 %02X\n", isac->name, val);
+       if (val & 2) {
+               pr_debug("%s: ph_state change %x->%x\n", isac->name,
+                       isac->state, (val >> 2) & 0xf);
+               isac->state = (val >> 2) & 0xf;
+               isac_ph_state_change(isac);
+       }
+       if (val & 1) {
+               val = ReadISAC(isac, ISAC_CIR1);
+               pr_debug("%s: ISAC CIR1 %02X\n", isac->name, val);
+       }
+}
+
+static void
+isacsx_cic_irq(struct isac_hw *isac)
+{
+       u8 val;
+
+       val = ReadISAC(isac, ISACX_CIR0);
+       pr_debug("%s: ISACX CIR0 %02X\n", isac->name, val);
+       if (val & ISACX_CIR0_CIC0) {
+               pr_debug("%s: ph_state change %x->%x\n", isac->name,
+                       isac->state, val >> 4);
+               isac->state = val >> 4;
+               isac_ph_state_change(isac);
+       }
+}
+
+static void
+isacsx_rme_irq(struct isac_hw *isac)
+{
+       int count;
+       u8 val;
+
+       val = ReadISAC(isac, ISACX_RSTAD);
+       if ((val & (ISACX_RSTAD_VFR |
+                   ISACX_RSTAD_RDO |
+                   ISACX_RSTAD_CRC |
+                   ISACX_RSTAD_RAB))
+           != (ISACX_RSTAD_VFR | ISACX_RSTAD_CRC)) {
+               pr_debug("%s: RSTAD %#x, dropped\n", isac->name, val);
+#ifdef ERROR_STATISTIC
+               if (val & ISACX_RSTAD_CRC)
+                       isac->dch.err_rx++;
+               else
+                       isac->dch.err_crc++;
+#endif
+               WriteISAC(isac, ISACX_CMDRD, ISACX_CMDRD_RMC);
+               if (isac->dch.rx_skb)
+                       dev_kfree_skb(isac->dch.rx_skb);
+               isac->dch.rx_skb = NULL;
+       } else {
+               count = ReadISAC(isac, ISACX_RBCLD) & 0x1f;
+               if (count == 0)
+                       count = 32;
+               isac_empty_fifo(isac, count);
+               if (isac->dch.rx_skb) {
+                       skb_trim(isac->dch.rx_skb, isac->dch.rx_skb->len - 1);
+                       pr_debug("%s: dchannel received %d\n", isac->name,
+                               isac->dch.rx_skb->len);
+                       recv_Dchannel(&isac->dch);
+               }
+       }
+}
+
+irqreturn_t
+mISDNisac_irq(struct isac_hw *isac, u8 val)
+{
+       if (unlikely(!val))
+               return IRQ_NONE;
+       pr_debug("%s: ISAC interrupt %02x\n", isac->name, val);
+       if (isac->type & IPAC_TYPE_ISACX) {
+               if (val & ISACX__CIC)
+                       isacsx_cic_irq(isac);
+               if (val & ISACX__ICD) {
+                       val = ReadISAC(isac, ISACX_ISTAD);
+                       pr_debug("%s: ISTAD %02x\n", isac->name, val);
+                       if (val & ISACX_D_XDU) {
+                               pr_debug("%s: ISAC XDU\n", isac->name);
+#ifdef ERROR_STATISTIC
+                               isac->dch.err_tx++;
+#endif
+                               isac_retransmit(isac);
+                       }
+                       if (val & ISACX_D_XMR) {
+                               pr_debug("%s: ISAC XMR\n", isac->name);
+#ifdef ERROR_STATISTIC
+                               isac->dch.err_tx++;
+#endif
+                               isac_retransmit(isac);
+                       }
+                       if (val & ISACX_D_XPR)
+                               isac_xpr_irq(isac);
+                       if (val & ISACX_D_RFO) {
+                               pr_debug("%s: ISAC RFO\n", isac->name);
+                               WriteISAC(isac, ISACX_CMDRD, ISACX_CMDRD_RMC);
+                       }
+                       if (val & ISACX_D_RME)
+                               isacsx_rme_irq(isac);
+                       if (val & ISACX_D_RPF)
+                               isac_empty_fifo(isac, 0x20);
+               }
+       } else {
+               if (val & 0x80) /* RME */
+                       isac_rme_irq(isac);
+               if (val & 0x40) /* RPF */
+                       isac_empty_fifo(isac, 32);
+               if (val & 0x10) /* XPR */
+                       isac_xpr_irq(isac);
+               if (val & 0x04) /* CISQ */
+                       isac_cisq_irq(isac);
+               if (val & 0x20) /* RSC - never */
+                       pr_debug("%s: ISAC RSC interrupt\n", isac->name);
+               if (val & 0x02) /* SIN - never */
+                       pr_debug("%s: ISAC SIN interrupt\n", isac->name);
+               if (val & 0x01) {       /* EXI */
+                       val = ReadISAC(isac, ISAC_EXIR);
+                       pr_debug("%s: ISAC EXIR %02x\n", isac->name, val);
+                       if (val & 0x80) /* XMR */
+                               pr_debug("%s: ISAC XMR\n", isac->name);
+                       if (val & 0x40) { /* XDU */
+                               pr_debug("%s: ISAC XDU\n", isac->name);
+#ifdef ERROR_STATISTIC
+                               isac->dch.err_tx++;
+#endif
+                               isac_retransmit(isac);
+                       }
+                       if (val & 0x04) /* MOS */
+                               isac_mos_irq(isac);
+               }
+       }
+       return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(mISDNisac_irq);
+
+static int
+isac_l1hw(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct mISDNdevice      *dev = container_of(ch, struct mISDNdevice, D);
+       struct dchannel         *dch = container_of(dev, struct dchannel, dev);
+       struct isac_hw          *isac = container_of(dch, struct isac_hw, dch);
+       int                     ret = -EINVAL;
+       struct mISDNhead        *hh = mISDN_HEAD_P(skb);
+       u32                     id;
+       u_long                  flags;
+
+       switch (hh->prim) {
+       case PH_DATA_REQ:
+               spin_lock_irqsave(isac->hwlock, flags);
+               ret = dchannel_senddata(dch, skb);
+               if (ret > 0) { /* direct TX */
+                       id = hh->id; /* skb can be freed */
+                       isac_fill_fifo(isac);
+                       ret = 0;
+                       spin_unlock_irqrestore(isac->hwlock, flags);
+                       queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+               } else
+                       spin_unlock_irqrestore(isac->hwlock, flags);
+               return ret;
+       case PH_ACTIVATE_REQ:
+               ret = l1_event(dch->l1, hh->prim);
+               break;
+       case PH_DEACTIVATE_REQ:
+               test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags);
+               ret = l1_event(dch->l1, hh->prim);
+               break;
+       }
+
+       if (!ret)
+               dev_kfree_skb(skb);
+       return ret;
+}
+
+static int
+isac_ctrl(struct isac_hw *isac, u32 cmd, u_long para)
+{
+       u8 tl = 0;
+       u_long flags;
+
+       switch (cmd) {
+       case HW_TESTLOOP:
+               spin_lock_irqsave(isac->hwlock, flags);
+               if (!(isac->type & IPAC_TYPE_ISACX)) {
+                       /* TODO: implement for IPAC_TYPE_ISACX */
+                       if (para & 1) /* B1 */
+                               tl |= 0x0c;
+                       else if (para & 2) /* B2 */
+                               tl |= 0x3;
+                       /* we only support IOM2 mode */
+                       WriteISAC(isac, ISAC_SPCR, tl);
+                       if (tl)
+                               WriteISAC(isac, ISAC_ADF1, 0x8);
+                       else
+                               WriteISAC(isac, ISAC_ADF1, 0x0);
+               }
+               spin_unlock_irqrestore(isac->hwlock, flags);
+               break;
+       default:
+               pr_debug("%s: %s unknown command %x %lx\n", isac->name,
+                       __func__, cmd, para);
+               return -1;
+       }
+       return 0;
+}
+
+static int
+isac_l1cmd(struct dchannel *dch, u32 cmd)
+{
+       struct isac_hw *isac = container_of(dch, struct isac_hw, dch);
+       u_long flags;
+
+       pr_debug("%s: cmd(%x) state(%02x)\n", isac->name, cmd, isac->state);
+       switch (cmd) {
+       case INFO3_P8:
+               spin_lock_irqsave(isac->hwlock, flags);
+               ph_command(isac, ISAC_CMD_AR8);
+               spin_unlock_irqrestore(isac->hwlock, flags);
+               break;
+       case INFO3_P10:
+               spin_lock_irqsave(isac->hwlock, flags);
+               ph_command(isac, ISAC_CMD_AR10);
+               spin_unlock_irqrestore(isac->hwlock, flags);
+               break;
+       case HW_RESET_REQ:
+               spin_lock_irqsave(isac->hwlock, flags);
+               if ((isac->state == ISAC_IND_EI) ||
+                   (isac->state == ISAC_IND_DR) ||
+                   (isac->state == ISAC_IND_RS))
+                       ph_command(isac, ISAC_CMD_TIM);
+               else
+                       ph_command(isac, ISAC_CMD_RS);
+               spin_unlock_irqrestore(isac->hwlock, flags);
+               break;
+       case HW_DEACT_REQ:
+               skb_queue_purge(&dch->squeue);
+               if (dch->tx_skb) {
+                       dev_kfree_skb(dch->tx_skb);
+                       dch->tx_skb = NULL;
+               }
+               dch->tx_idx = 0;
+               if (dch->rx_skb) {
+                       dev_kfree_skb(dch->rx_skb);
+                       dch->rx_skb = NULL;
+               }
+               test_and_clear_bit(FLG_TX_BUSY, &dch->Flags);
+               if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
+                       del_timer(&dch->timer);
+               break;
+       case HW_POWERUP_REQ:
+               spin_lock_irqsave(isac->hwlock, flags);
+               ph_command(isac, ISAC_CMD_TIM);
+               spin_unlock_irqrestore(isac->hwlock, flags);
+               break;
+       case PH_ACTIVATE_IND:
+               test_and_set_bit(FLG_ACTIVE, &dch->Flags);
+               _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+                       GFP_ATOMIC);
+               break;
+       case PH_DEACTIVATE_IND:
+               test_and_clear_bit(FLG_ACTIVE, &dch->Flags);
+               _queue_data(&dch->dev.D, cmd, MISDN_ID_ANY, 0, NULL,
+                       GFP_ATOMIC);
+               break;
+       default:
+               pr_debug("%s: %s unknown command %x\n", isac->name,
+                       __func__, cmd);
+               return -1;
+       }
+       return 0;
+}
+
+static void
+isac_release(struct isac_hw *isac)
+{
+       if (isac->type & IPAC_TYPE_ISACX)
+               WriteISAC(isac, ISACX_MASK, 0xff);
+       else
+               WriteISAC(isac, ISAC_MASK, 0xff);
+       if (isac->dch.timer.function != NULL) {
+               del_timer(&isac->dch.timer);
+               isac->dch.timer.function = NULL;
+       }
+       kfree(isac->mon_rx);
+       isac->mon_rx = NULL;
+       kfree(isac->mon_tx);
+       isac->mon_tx = NULL;
+       if (isac->dch.l1)
+               l1_event(isac->dch.l1, CLOSE_CHANNEL);
+       mISDN_freedchannel(&isac->dch);
+}
+
+static void
+dbusy_timer_handler(struct isac_hw *isac)
+{
+       int rbch, star;
+       u_long flags;
+
+       if (test_bit(FLG_BUSY_TIMER, &isac->dch.Flags)) {
+               spin_lock_irqsave(isac->hwlock, flags);
+               rbch = ReadISAC(isac, ISAC_RBCH);
+               star = ReadISAC(isac, ISAC_STAR);
+               pr_debug("%s: D-Channel Busy RBCH %02x STAR %02x\n",
+                       isac->name, rbch, star);
+               if (rbch & ISAC_RBCH_XAC) /* D-Channel Busy */
+                       test_and_set_bit(FLG_L1_BUSY, &isac->dch.Flags);
+               else {
+                       /* discard frame; reset transceiver */
+                       test_and_clear_bit(FLG_BUSY_TIMER, &isac->dch.Flags);
+                       if (isac->dch.tx_idx)
+                               isac->dch.tx_idx = 0;
+                       else
+                               pr_info("%s: ISAC D-Channel Busy no tx_idx\n",
+                                       isac->name);
+                       /* Transmitter reset */
+                       WriteISAC(isac, ISAC_CMDR, 0x01);
+               }
+               spin_unlock_irqrestore(isac->hwlock, flags);
+       }
+}
+
+static int
+open_dchannel(struct isac_hw *isac, struct channel_req *rq)
+{
+       pr_debug("%s: %s dev(%d) open from %p\n", isac->name, __func__,
+               isac->dch.dev.id, __builtin_return_address(1));
+       if (rq->protocol != ISDN_P_TE_S0)
+               return -EINVAL;
+       if (rq->adr.channel == 1)
+               /* E-Channel not supported */
+               return -EINVAL;
+       rq->ch = &isac->dch.dev.D;
+       rq->ch->protocol = rq->protocol;
+       if (isac->dch.state == 7)
+               _queue_data(rq->ch, PH_ACTIVATE_IND, MISDN_ID_ANY,
+                   0, NULL, GFP_KERNEL);
+       return 0;
+}
+
+static const char *ISACVer[] =
+{"2086/2186 V1.1", "2085 B1", "2085 B2",
+ "2085 V2.3"};
+
+static int
+isac_init(struct isac_hw *isac)
+{
+       u8 val;
+       int err = 0;
+
+       if (!isac->dch.l1) {
+               err = create_l1(&isac->dch, isac_l1cmd);
+               if (err)
+                       return err;
+       }
+       isac->mon_tx = NULL;
+       isac->mon_rx = NULL;
+       isac->dch.timer.function = (void *) dbusy_timer_handler;
+       isac->dch.timer.data = (long)isac;
+       init_timer(&isac->dch.timer);
+       isac->mocr = 0xaa;
+       if (isac->type & IPAC_TYPE_ISACX) {
+               /* Disable all IRQ */
+               WriteISAC(isac, ISACX_MASK, 0xff);
+               val = ReadISAC(isac, ISACX_STARD);
+               pr_debug("%s: ISACX STARD %x\n", isac->name, val);
+               val = ReadISAC(isac, ISACX_ISTAD);
+               pr_debug("%s: ISACX ISTAD %x\n", isac->name, val);
+               val = ReadISAC(isac, ISACX_ISTA);
+               pr_debug("%s: ISACX ISTA %x\n", isac->name, val);
+               /* clear LDD */
+               WriteISAC(isac, ISACX_TR_CONF0, 0x00);
+               /* enable transmitter */
+               WriteISAC(isac, ISACX_TR_CONF2, 0x00);
+               /* transparent mode 0, RAC, stop/go */
+               WriteISAC(isac, ISACX_MODED, 0xc9);
+               /* all HDLC IRQ unmasked */
+               val = ReadISAC(isac, ISACX_ID);
+               if (isac->dch.debug & DEBUG_HW)
+                       pr_notice("%s: ISACX Design ID %x\n",
+                               isac->name, val & 0x3f);
+               val = ReadISAC(isac, ISACX_CIR0);
+               pr_debug("%s: ISACX CIR0 %02X\n", isac->name, val);
+               isac->state = val >> 4;
+               isac_ph_state_change(isac);
+               ph_command(isac, ISAC_CMD_RS);
+               WriteISAC(isac, ISACX_MASK, IPACX__ON);
+               WriteISAC(isac, ISACX_MASKD, 0x00);
+       } else { /* old isac */
+               WriteISAC(isac, ISAC_MASK, 0xff);
+               val = ReadISAC(isac, ISAC_STAR);
+               pr_debug("%s: ISAC STAR %x\n", isac->name, val);
+               val = ReadISAC(isac, ISAC_MODE);
+               pr_debug("%s: ISAC MODE %x\n", isac->name, val);
+               val = ReadISAC(isac, ISAC_ADF2);
+               pr_debug("%s: ISAC ADF2 %x\n", isac->name, val);
+               val = ReadISAC(isac, ISAC_ISTA);
+               pr_debug("%s: ISAC ISTA %x\n", isac->name, val);
+               if (val & 0x01) {
+                       val = ReadISAC(isac, ISAC_EXIR);
+                       pr_debug("%s: ISAC EXIR %x\n", isac->name, val);
+               }
+               val = ReadISAC(isac, ISAC_RBCH);
+               if (isac->dch.debug & DEBUG_HW)
+                       pr_notice("%s: ISAC version (%x): %s\n", isac->name,
+                               val, ISACVer[(val >> 5) & 3]);
+               isac->type |= ((val >> 5) & 3);
+               if (!isac->adf2)
+                       isac->adf2 = 0x80;
+               if (!(isac->adf2 & 0x80)) { /* only IOM 2 Mode */
+                       pr_info("%s: only support IOM2 mode but adf2=%02x\n",
+                               isac->name, isac->adf2);
+                       isac_release(isac);
+                       return -EINVAL;
+               }
+               WriteISAC(isac, ISAC_ADF2, isac->adf2);
+               WriteISAC(isac, ISAC_SQXR, 0x2f);
+               WriteISAC(isac, ISAC_SPCR, 0x00);
+               WriteISAC(isac, ISAC_STCR, 0x70);
+               WriteISAC(isac, ISAC_MODE, 0xc9);
+               WriteISAC(isac, ISAC_TIMR, 0x00);
+               WriteISAC(isac, ISAC_ADF1, 0x00);
+               val = ReadISAC(isac, ISAC_CIR0);
+               pr_debug("%s: ISAC CIR0 %x\n", isac->name, val);
+               isac->state = (val >> 2) & 0xf;
+               isac_ph_state_change(isac);
+               ph_command(isac, ISAC_CMD_RS);
+               WriteISAC(isac, ISAC_MASK, 0);
+       }
+       return err;
+}
+
+int
+mISDNisac_init(struct isac_hw *isac, void *hw)
+{
+       mISDN_initdchannel(&isac->dch, MAX_DFRAME_LEN_L1, isac_ph_state_bh);
+       isac->dch.hw = hw;
+       isac->dch.dev.D.send = isac_l1hw;
+       isac->init = isac_init;
+       isac->release = isac_release;
+       isac->ctrl = isac_ctrl;
+       isac->open = open_dchannel;
+       isac->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0);
+       isac->dch.dev.nrbchan = 2;
+       return 0;
+}
+EXPORT_SYMBOL(mISDNisac_init);
+
+static void
+waitforCEC(struct hscx_hw *hx)
+{
+       u8 starb, to = 50;
+
+       while (to) {
+               starb = ReadHSCX(hx, IPAC_STARB);
+               if (!(starb & 0x04))
+                       break;
+               udelay(1);
+               to--;
+       }
+       if (to < 50)
+               pr_debug("%s: B%1d CEC %d us\n", hx->ip->name, hx->bch.nr,
+                       50 - to);
+       if (!to)
+               pr_info("%s: B%1d CEC timeout\n", hx->ip->name, hx->bch.nr);
+}
+
+
+static void
+waitforXFW(struct hscx_hw *hx)
+{
+       u8 starb, to = 50;
+
+       while (to) {
+               starb = ReadHSCX(hx, IPAC_STARB);
+               if ((starb & 0x44) == 0x40)
+                       break;
+               udelay(1);
+               to--;
+       }
+       if (to < 50)
+               pr_debug("%s: B%1d XFW %d us\n", hx->ip->name, hx->bch.nr,
+                       50 - to);
+       if (!to)
+               pr_info("%s: B%1d XFW timeout\n", hx->ip->name, hx->bch.nr);
+}
+
+static void
+hscx_cmdr(struct hscx_hw *hx, u8 cmd)
+{
+       if (hx->ip->type & IPAC_TYPE_IPACX)
+               WriteHSCX(hx, IPACX_CMDRB, cmd);
+       else {
+               waitforCEC(hx);
+               WriteHSCX(hx, IPAC_CMDRB, cmd);
+       }
+}
+
+static void
+hscx_empty_fifo(struct hscx_hw *hscx, u8 count)
+{
+       u8 *p;
+
+       pr_debug("%s: B%1d %d\n", hscx->ip->name, hscx->bch.nr, count);
+       if (!hscx->bch.rx_skb) {
+               hscx->bch.rx_skb = mI_alloc_skb(hscx->bch.maxlen, GFP_ATOMIC);
+               if (!hscx->bch.rx_skb) {
+                       pr_info("%s: B receive out of memory\n",
+                               hscx->ip->name);
+                       hscx_cmdr(hscx, 0x80); /* RMC */
+                       return;
+               }
+       }
+       if ((hscx->bch.rx_skb->len + count) > hscx->bch.maxlen) {
+               pr_debug("%s: overrun %d\n", hscx->ip->name,
+                       hscx->bch.rx_skb->len + count);
+               skb_trim(hscx->bch.rx_skb, 0);
+               hscx_cmdr(hscx, 0x80); /* RMC */
+               return;
+       }
+       p = skb_put(hscx->bch.rx_skb, count);
+
+       if (hscx->ip->type & IPAC_TYPE_IPACX)
+               hscx->ip->read_fifo(hscx->ip->hw,
+                       hscx->off + IPACX_RFIFOB, p, count);
+       else
+               hscx->ip->read_fifo(hscx->ip->hw,
+                       hscx->off, p, count);
+
+       hscx_cmdr(hscx, 0x80); /* RMC */
+
+       if (hscx->bch.debug & DEBUG_HW_BFIFO) {
+               snprintf(hscx->log, 64, "B%1d-recv %s %d ",
+                       hscx->bch.nr, hscx->ip->name, count);
+               print_hex_dump_bytes(hscx->log, DUMP_PREFIX_OFFSET, p, count);
+       }
+}
+
+static void
+hscx_fill_fifo(struct hscx_hw *hscx)
+{
+       int count, more;
+       u8 *p;
+
+       if (!hscx->bch.tx_skb)
+               return;
+       count = hscx->bch.tx_skb->len - hscx->bch.tx_idx;
+       if (count <= 0)
+               return;
+       p = hscx->bch.tx_skb->data + hscx->bch.tx_idx;
+
+       more = test_bit(FLG_TRANSPARENT, &hscx->bch.Flags) ? 1 : 0;
+       if (count > hscx->fifo_size) {
+               count = hscx->fifo_size;
+               more = 1;
+       }
+       pr_debug("%s: B%1d %d/%d/%d\n", hscx->ip->name, hscx->bch.nr, count,
+               hscx->bch.tx_idx, hscx->bch.tx_skb->len);
+       hscx->bch.tx_idx += count;
+
+       if (hscx->ip->type & IPAC_TYPE_IPACX)
+               hscx->ip->write_fifo(hscx->ip->hw,
+                       hscx->off + IPACX_XFIFOB, p, count);
+       else {
+               waitforXFW(hscx);
+               hscx->ip->write_fifo(hscx->ip->hw,
+                       hscx->off, p, count);
+       }
+       hscx_cmdr(hscx, more ? 0x08 : 0x0a);
+
+       if (hscx->bch.debug & DEBUG_HW_BFIFO) {
+               snprintf(hscx->log, 64, "B%1d-send %s %d ",
+                       hscx->bch.nr, hscx->ip->name, count);
+               print_hex_dump_bytes(hscx->log, DUMP_PREFIX_OFFSET, p, count);
+       }
+}
+
+static void
+hscx_xpr(struct hscx_hw *hx)
+{
+       if (hx->bch.tx_skb && hx->bch.tx_idx < hx->bch.tx_skb->len)
+               hscx_fill_fifo(hx);
+       else {
+               if (hx->bch.tx_skb) {
+                       /* send confirm, on trans, free on hdlc. */
+                       if (test_bit(FLG_TRANSPARENT, &hx->bch.Flags))
+                               confirm_Bsend(&hx->bch);
+                       dev_kfree_skb(hx->bch.tx_skb);
+               }
+               if (get_next_bframe(&hx->bch))
+                       hscx_fill_fifo(hx);
+       }
+}
+
+static void
+ipac_rme(struct hscx_hw *hx)
+{
+       int count;
+       u8 rstab;
+
+       if (hx->ip->type & IPAC_TYPE_IPACX)
+               rstab = ReadHSCX(hx, IPACX_RSTAB);
+       else
+               rstab = ReadHSCX(hx, IPAC_RSTAB);
+       pr_debug("%s: B%1d RSTAB %02x\n", hx->ip->name, hx->bch.nr, rstab);
+       if ((rstab & 0xf0) != 0xa0) {
+               /* !(VFR && !RDO && CRC && !RAB) */
+               if (!(rstab & 0x80)) {
+                       if (hx->bch.debug & DEBUG_HW_BCHANNEL)
+                               pr_notice("%s: B%1d invalid frame\n",
+                                       hx->ip->name, hx->bch.nr);
+               }
+               if (rstab & 0x40) {
+                       if (hx->bch.debug & DEBUG_HW_BCHANNEL)
+                               pr_notice("%s: B%1d RDO proto=%x\n",
+                                       hx->ip->name, hx->bch.nr,
+                                       hx->bch.state);
+               }
+               if (!(rstab & 0x20)) {
+                       if (hx->bch.debug & DEBUG_HW_BCHANNEL)
+                               pr_notice("%s: B%1d CRC error\n",
+                                       hx->ip->name, hx->bch.nr);
+               }
+               hscx_cmdr(hx, 0x80); /* Do RMC */
+               return;
+       }
+       if (hx->ip->type & IPAC_TYPE_IPACX)
+               count = ReadHSCX(hx, IPACX_RBCLB);
+       else
+               count = ReadHSCX(hx, IPAC_RBCLB);
+       count &= (hx->fifo_size - 1);
+       if (count == 0)
+               count = hx->fifo_size;
+       hscx_empty_fifo(hx, count);
+       if (!hx->bch.rx_skb)
+               return;
+       if (hx->bch.rx_skb->len < 2) {
+               pr_debug("%s: B%1d frame to short %d\n",
+                       hx->ip->name, hx->bch.nr, hx->bch.rx_skb->len);
+               skb_trim(hx->bch.rx_skb, 0);
+       } else {
+               skb_trim(hx->bch.rx_skb, hx->bch.rx_skb->len - 1);
+               recv_Bchannel(&hx->bch, 0);
+       }
+}
+
+static void
+ipac_irq(struct hscx_hw *hx, u8 ista)
+{
+       u8 istab, m, exirb = 0;
+
+       if (hx->ip->type & IPAC_TYPE_IPACX)
+               istab = ReadHSCX(hx, IPACX_ISTAB);
+       else if (hx->ip->type & IPAC_TYPE_IPAC) {
+               istab = ReadHSCX(hx, IPAC_ISTAB);
+               m = (hx->bch.nr & 1) ? IPAC__EXA : IPAC__EXB;
+               if (m & ista) {
+                       exirb = ReadHSCX(hx, IPAC_EXIRB);
+                       pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name,
+                               hx->bch.nr, exirb);
+               }
+       } else if (hx->bch.nr & 2) { /* HSCX B */
+               if (ista & (HSCX__EXA | HSCX__ICA))
+                       ipac_irq(&hx->ip->hscx[0], ista);
+               if (ista & HSCX__EXB) {
+                       exirb = ReadHSCX(hx, IPAC_EXIRB);
+                       pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name,
+                               hx->bch.nr, exirb);
+               }
+               istab = ista & 0xF8;
+       } else { /* HSCX A */
+               istab = ReadHSCX(hx, IPAC_ISTAB);
+               if (ista & HSCX__EXA) {
+                       exirb = ReadHSCX(hx, IPAC_EXIRB);
+                       pr_debug("%s: B%1d EXIRB %02x\n", hx->ip->name,
+                               hx->bch.nr, exirb);
+               }
+               istab = istab & 0xF8;
+       }
+       if (exirb & IPAC_B_XDU)
+               istab |= IPACX_B_XDU;
+       if (exirb & IPAC_B_RFO)
+               istab |= IPACX_B_RFO;
+       pr_debug("%s: B%1d ISTAB %02x\n", hx->ip->name, hx->bch.nr, istab);
+
+       if (!test_bit(FLG_ACTIVE, &hx->bch.Flags))
+               return;
+
+       if (istab & IPACX_B_RME)
+               ipac_rme(hx);
+
+       if (istab & IPACX_B_RPF) {
+               hscx_empty_fifo(hx, hx->fifo_size);
+               if (test_bit(FLG_TRANSPARENT, &hx->bch.Flags)) {
+                       /* receive transparent audio data */
+                       if (hx->bch.rx_skb)
+                               recv_Bchannel(&hx->bch, 0);
+               }
+       }
+
+       if (istab & IPACX_B_RFO) {
+               pr_debug("%s: B%1d RFO error\n", hx->ip->name, hx->bch.nr);
+               hscx_cmdr(hx, 0x40);    /* RRES */
+       }
+
+       if (istab & IPACX_B_XPR)
+               hscx_xpr(hx);
+
+       if (istab & IPACX_B_XDU) {
+               if (test_bit(FLG_TRANSPARENT, &hx->bch.Flags)) {
+                       hscx_fill_fifo(hx);
+                       return;
+               }
+               pr_debug("%s: B%1d XDU error at len %d\n", hx->ip->name,
+                       hx->bch.nr, hx->bch.tx_idx);
+               hx->bch.tx_idx = 0;
+               hscx_cmdr(hx, 0x01);    /* XRES */
+       }
+}
+
+irqreturn_t
+mISDNipac_irq(struct ipac_hw *ipac, int maxloop)
+{
+       int cnt = maxloop + 1;
+       u8 ista, istad;
+       struct isac_hw  *isac = &ipac->isac;
+
+       if (ipac->type & IPAC_TYPE_IPACX) {
+               ista = ReadIPAC(ipac, ISACX_ISTA);
+               while (ista && cnt--) {
+                       pr_debug("%s: ISTA %02x\n", ipac->name, ista);
+                       if (ista & IPACX__ICA)
+                               ipac_irq(&ipac->hscx[0], ista);
+                       if (ista & IPACX__ICB)
+                               ipac_irq(&ipac->hscx[1], ista);
+                       if (ista & (ISACX__ICD | ISACX__CIC))
+                               mISDNisac_irq(&ipac->isac, ista);
+                       ista = ReadIPAC(ipac, ISACX_ISTA);
+               }
+       } else if (ipac->type & IPAC_TYPE_IPAC) {
+               ista = ReadIPAC(ipac, IPAC_ISTA);
+               while (ista && cnt--) {
+                       pr_debug("%s: ISTA %02x\n", ipac->name, ista);
+                       if (ista & (IPAC__ICD | IPAC__EXD)) {
+                               istad = ReadISAC(isac, ISAC_ISTA);
+                               pr_debug("%s: ISTAD %02x\n", ipac->name, istad);
+                               if (istad & IPAC_D_TIN2)
+                                       pr_debug("%s TIN2 irq\n", ipac->name);
+                               if (ista & IPAC__EXD)
+                                       istad |= 1; /* ISAC EXI */
+                               mISDNisac_irq(isac, istad);
+                       }
+                       if (ista & (IPAC__ICA | IPAC__EXA))
+                               ipac_irq(&ipac->hscx[0], ista);
+                       if (ista & (IPAC__ICB | IPAC__EXB))
+                               ipac_irq(&ipac->hscx[1], ista);
+                       ista = ReadIPAC(ipac, IPAC_ISTA);
+               }
+       } else if (ipac->type & IPAC_TYPE_HSCX) {
+               while (cnt) {
+                       ista = ReadIPAC(ipac, IPAC_ISTAB + ipac->hscx[1].off);
+                       pr_debug("%s: B2 ISTA %02x\n", ipac->name, ista);
+                       if (ista)
+                               ipac_irq(&ipac->hscx[1], ista);
+                       istad = ReadISAC(isac, ISAC_ISTA);
+                       pr_debug("%s: ISTAD %02x\n", ipac->name, istad);
+                       if (istad)
+                               mISDNisac_irq(isac, istad);
+                       if (0 == (ista | istad))
+                               break;
+                       cnt--;
+               }
+       }
+       if (cnt > maxloop) /* only for ISAC/HSCX without PCI IRQ test */
+               return IRQ_NONE;
+       if (cnt < maxloop)
+               pr_debug("%s: %d irqloops cpu%d\n", ipac->name,
+                       maxloop - cnt, smp_processor_id());
+       if (maxloop && !cnt)
+               pr_notice("%s: %d IRQ LOOP cpu%d\n", ipac->name,
+                       maxloop, smp_processor_id());
+       return IRQ_HANDLED;
+}
+EXPORT_SYMBOL(mISDNipac_irq);
+
+static int
+hscx_mode(struct hscx_hw *hscx, u32 bprotocol)
+{
+       pr_debug("%s: HSCX %c protocol %x-->%x ch %d\n", hscx->ip->name,
+               '@' + hscx->bch.nr, hscx->bch.state, bprotocol, hscx->bch.nr);
+       if (hscx->ip->type & IPAC_TYPE_IPACX) {
+               if (hscx->bch.nr & 1) { /* B1 and ICA */
+                       WriteIPAC(hscx->ip, ISACX_BCHA_TSDP_BC1, 0x80);
+                       WriteIPAC(hscx->ip, ISACX_BCHA_CR, 0x88);
+               } else { /* B2 and ICB */
+                       WriteIPAC(hscx->ip, ISACX_BCHB_TSDP_BC1, 0x81);
+                       WriteIPAC(hscx->ip, ISACX_BCHB_CR, 0x88);
+               }
+               switch (bprotocol) {
+               case ISDN_P_NONE: /* init */
+                       WriteHSCX(hscx, IPACX_MODEB, 0xC0);     /* rec off */
+                       WriteHSCX(hscx, IPACX_EXMB,  0x30);     /* std adj. */
+                       WriteHSCX(hscx, IPACX_MASKB, 0xFF);     /* ints off */
+                       hscx_cmdr(hscx, 0x41);
+                       test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags);
+                       test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
+                       break;
+               case ISDN_P_B_RAW:
+                       WriteHSCX(hscx, IPACX_MODEB, 0x88);     /* ex trans */
+                       WriteHSCX(hscx, IPACX_EXMB,  0x00);     /* trans */
+                       hscx_cmdr(hscx, 0x41);
+                       WriteHSCX(hscx, IPACX_MASKB, IPACX_B_ON);
+                       test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
+                       break;
+               case ISDN_P_B_HDLC:
+                       WriteHSCX(hscx, IPACX_MODEB, 0xC0);     /* trans */
+                       WriteHSCX(hscx, IPACX_EXMB,  0x00);     /* hdlc,crc */
+                       hscx_cmdr(hscx, 0x41);
+                       WriteHSCX(hscx, IPACX_MASKB, IPACX_B_ON);
+                       test_and_set_bit(FLG_HDLC, &hscx->bch.Flags);
+                       break;
+               default:
+                       pr_info("%s: protocol not known %x\n", hscx->ip->name,
+                               bprotocol);
+                       return -ENOPROTOOPT;
+               }
+       } else if (hscx->ip->type & IPAC_TYPE_IPAC) { /* IPAC */
+               WriteHSCX(hscx, IPAC_CCR1, 0x82);
+               WriteHSCX(hscx, IPAC_CCR2, 0x30);
+               WriteHSCX(hscx, IPAC_XCCR, 0x07);
+               WriteHSCX(hscx, IPAC_RCCR, 0x07);
+               WriteHSCX(hscx, IPAC_TSAX, hscx->slot);
+               WriteHSCX(hscx, IPAC_TSAR, hscx->slot);
+               switch (bprotocol) {
+               case ISDN_P_NONE:
+                       WriteHSCX(hscx, IPAC_TSAX, 0x1F);
+                       WriteHSCX(hscx, IPAC_TSAR, 0x1F);
+                       WriteHSCX(hscx, IPAC_MODEB, 0x84);
+                       WriteHSCX(hscx, IPAC_CCR1, 0x82);
+                       WriteHSCX(hscx, IPAC_MASKB, 0xFF);      /* ints off */
+                       test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags);
+                       test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
+                       break;
+               case ISDN_P_B_RAW:
+                       WriteHSCX(hscx, IPAC_MODEB, 0xe4);      /* ex trans */
+                       WriteHSCX(hscx, IPAC_CCR1, 0x82);
+                       hscx_cmdr(hscx, 0x41);
+                       WriteHSCX(hscx, IPAC_MASKB, 0);
+                       test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
+                       break;
+               case ISDN_P_B_HDLC:
+                       WriteHSCX(hscx, IPAC_MODEB, 0x8c);
+                       WriteHSCX(hscx, IPAC_CCR1, 0x8a);
+                       hscx_cmdr(hscx, 0x41);
+                       WriteHSCX(hscx, IPAC_MASKB, 0);
+                       test_and_set_bit(FLG_HDLC, &hscx->bch.Flags);
+                       break;
+               default:
+                       pr_info("%s: protocol not known %x\n", hscx->ip->name,
+                               bprotocol);
+                       return -ENOPROTOOPT;
+               }
+       } else if (hscx->ip->type & IPAC_TYPE_HSCX) { /* HSCX */
+               WriteHSCX(hscx, IPAC_CCR1, 0x85);
+               WriteHSCX(hscx, IPAC_CCR2, 0x30);
+               WriteHSCX(hscx, IPAC_XCCR, 0x07);
+               WriteHSCX(hscx, IPAC_RCCR, 0x07);
+               WriteHSCX(hscx, IPAC_TSAX, hscx->slot);
+               WriteHSCX(hscx, IPAC_TSAR, hscx->slot);
+               switch (bprotocol) {
+               case ISDN_P_NONE:
+                       WriteHSCX(hscx, IPAC_TSAX, 0x1F);
+                       WriteHSCX(hscx, IPAC_TSAR, 0x1F);
+                       WriteHSCX(hscx, IPAC_MODEB, 0x84);
+                       WriteHSCX(hscx, IPAC_CCR1, 0x85);
+                       WriteHSCX(hscx, IPAC_MASKB, 0xFF);      /* ints off */
+                       test_and_clear_bit(FLG_HDLC, &hscx->bch.Flags);
+                       test_and_clear_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
+                       break;
+               case ISDN_P_B_RAW:
+                       WriteHSCX(hscx, IPAC_MODEB, 0xe4);      /* ex trans */
+                       WriteHSCX(hscx, IPAC_CCR1, 0x85);
+                       hscx_cmdr(hscx, 0x41);
+                       WriteHSCX(hscx, IPAC_MASKB, 0);
+                       test_and_set_bit(FLG_TRANSPARENT, &hscx->bch.Flags);
+                       break;
+               case ISDN_P_B_HDLC:
+                       WriteHSCX(hscx, IPAC_MODEB, 0x8c);
+                       WriteHSCX(hscx, IPAC_CCR1, 0x8d);
+                       hscx_cmdr(hscx, 0x41);
+                       WriteHSCX(hscx, IPAC_MASKB, 0);
+                       test_and_set_bit(FLG_HDLC, &hscx->bch.Flags);
+                       break;
+               default:
+                       pr_info("%s: protocol not known %x\n", hscx->ip->name,
+                               bprotocol);
+                       return -ENOPROTOOPT;
+               }
+       } else
+               return -EINVAL;
+       hscx->bch.state = bprotocol;
+       return 0;
+}
+
+static int
+hscx_l2l1(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+       struct bchannel *bch = container_of(ch, struct bchannel, ch);
+       struct hscx_hw  *hx = container_of(bch, struct hscx_hw, bch);
+       int ret = -EINVAL;
+       struct mISDNhead *hh = mISDN_HEAD_P(skb);
+       u32 id;
+       u_long flags;
+
+       switch (hh->prim) {
+       case PH_DATA_REQ:
+               spin_lock_irqsave(hx->ip->hwlock, flags);
+               ret = bchannel_senddata(bch, skb);
+               if (ret > 0) { /* direct TX */
+                       id = hh->id; /* skb can be freed */
+                       ret = 0;
+                       hscx_fill_fifo(hx);
+                       spin_unlock_irqrestore(hx->ip->hwlock, flags);
+                       if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+                               queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+               } else
+                       spin_unlock_irqrestore(hx->ip->hwlock, flags);
+               return ret;
+       case PH_ACTIVATE_REQ:
+               spin_lock_irqsave(hx->ip->hwlock, flags);
+               if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
+                       ret = hscx_mode(hx, ch->protocol);
+               else
+                       ret = 0;
+               spin_unlock_irqrestore(hx->ip->hwlock, flags);
+               if (!ret)
+                       _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
+                               NULL, GFP_KERNEL);
+               break;
+       case PH_DEACTIVATE_REQ:
+               spin_lock_irqsave(hx->ip->hwlock, flags);
+               mISDN_clear_bchannel(bch);
+               hscx_mode(hx, ISDN_P_NONE);
+               spin_unlock_irqrestore(hx->ip->hwlock, flags);
+               _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
+                       NULL, GFP_KERNEL);
+               ret = 0;
+               break;
+       default:
+               pr_info("%s: %s unknown prim(%x,%x)\n",
+                       hx->ip->name, __func__, hh->prim, hh->id);
+               ret = -EINVAL;
+       }
+       if (!ret)
+               dev_kfree_skb(skb);
+       return ret;
+}
+
+static int
+channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
+{
+       int     ret = 0;
+
+       switch (cq->op) {
+       case MISDN_CTRL_GETOP:
+               cq->op = 0;
+               break;
+       /* Nothing implemented yet */
+       case MISDN_CTRL_FILL_EMPTY:
+       default:
+               pr_info("%s: unknown Op %x\n", __func__, cq->op);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int
+hscx_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+       struct bchannel *bch = container_of(ch, struct bchannel, ch);
+       struct hscx_hw  *hx = container_of(bch, struct hscx_hw, bch);
+       int ret = -EINVAL;
+       u_long flags;
+
+       pr_debug("%s: %s cmd:%x %p\n", hx->ip->name, __func__, cmd, arg);
+       switch (cmd) {
+       case CLOSE_CHANNEL:
+               test_and_clear_bit(FLG_OPEN, &bch->Flags);
+               if (test_bit(FLG_ACTIVE, &bch->Flags)) {
+                       spin_lock_irqsave(hx->ip->hwlock, flags);
+                       mISDN_freebchannel(bch);
+                       hscx_mode(hx, ISDN_P_NONE);
+                       spin_unlock_irqrestore(hx->ip->hwlock, flags);
+               } else {
+                       skb_queue_purge(&bch->rqueue);
+                       bch->rcount = 0;
+               }
+               ch->protocol = ISDN_P_NONE;
+               ch->peer = NULL;
+               module_put(hx->ip->owner);
+               ret = 0;
+               break;
+       case CONTROL_CHANNEL:
+               ret = channel_bctrl(bch, arg);
+               break;
+       default:
+               pr_info("%s: %s unknown prim(%x)\n",
+                       hx->ip->name, __func__, cmd);
+       }
+       return ret;
+}
+
+static void
+free_ipac(struct ipac_hw *ipac)
+{
+       isac_release(&ipac->isac);
+}
+
+static const char *HSCXVer[] =
+{"A1", "?1", "A2", "?3", "A3", "V2.1", "?6", "?7",
+ "?8", "?9", "?10", "?11", "?12", "?13", "?14", "???"};
+
+
+
+static void
+hscx_init(struct hscx_hw *hx)
+{
+       u8 val;
+
+       WriteHSCX(hx, IPAC_RAH2, 0xFF);
+       WriteHSCX(hx, IPAC_XBCH, 0x00);
+       WriteHSCX(hx, IPAC_RLCR, 0x00);
+
+       if (hx->ip->type & IPAC_TYPE_HSCX) {
+               WriteHSCX(hx, IPAC_CCR1, 0x85);
+               val = ReadHSCX(hx, HSCX_VSTR);
+               pr_debug("%s: HSCX VSTR %02x\n", hx->ip->name, val);
+               if (hx->bch.debug & DEBUG_HW)
+                       pr_notice("%s: HSCX version %s\n", hx->ip->name,
+                               HSCXVer[val & 0x0f]);
+       } else
+               WriteHSCX(hx, IPAC_CCR1, 0x82);
+       WriteHSCX(hx, IPAC_CCR2, 0x30);
+       WriteHSCX(hx, IPAC_XCCR, 0x07);
+       WriteHSCX(hx, IPAC_RCCR, 0x07);
+}
+
+static int
+ipac_init(struct ipac_hw *ipac)
+{
+       u8 val;
+
+       if (ipac->type & IPAC_TYPE_HSCX) {
+               hscx_init(&ipac->hscx[0]);
+               hscx_init(&ipac->hscx[1]);
+               val = ReadIPAC(ipac, IPAC_ID);
+       } else if (ipac->type & IPAC_TYPE_IPAC) {
+               hscx_init(&ipac->hscx[0]);
+               hscx_init(&ipac->hscx[1]);
+               WriteIPAC(ipac, IPAC_MASK, IPAC__ON);
+               val = ReadIPAC(ipac, IPAC_CONF);
+               /* conf is default 0, but can be overwritten by card setup */
+               pr_debug("%s: IPAC CONF %02x/%02x\n", ipac->name,
+                       val, ipac->conf);
+               WriteIPAC(ipac, IPAC_CONF, ipac->conf);
+               val = ReadIPAC(ipac, IPAC_ID);
+               if (ipac->hscx[0].bch.debug & DEBUG_HW)
+                       pr_notice("%s: IPAC Design ID %02x\n", ipac->name, val);
+       }
+       /* nothing special for IPACX to do here */
+       return isac_init(&ipac->isac);
+}
+
+static int
+open_bchannel(struct ipac_hw *ipac, struct channel_req *rq)
+{
+       struct bchannel         *bch;
+
+       if (rq->adr.channel > 2)
+               return -EINVAL;
+       if (rq->protocol == ISDN_P_NONE)
+               return -EINVAL;
+       bch = &ipac->hscx[rq->adr.channel - 1].bch;
+       if (test_and_set_bit(FLG_OPEN, &bch->Flags))
+               return -EBUSY; /* b-channel can be only open once */
+       test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
+       bch->ch.protocol = rq->protocol;
+       rq->ch = &bch->ch;
+       return 0;
+}
+
+static int
+channel_ctrl(struct ipac_hw *ipac, struct mISDN_ctrl_req *cq)
+{
+       int     ret = 0;
+
+       switch (cq->op) {
+       case MISDN_CTRL_GETOP:
+               cq->op = MISDN_CTRL_LOOP;
+               break;
+       case MISDN_CTRL_LOOP:
+               /* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */
+               if (cq->channel < 0 || cq->channel > 3) {
+                       ret = -EINVAL;
+                       break;
+               }
+               ret = ipac->ctrl(ipac, HW_TESTLOOP, cq->channel);
+               break;
+       default:
+               pr_info("%s: unknown CTRL OP %x\n", ipac->name, cq->op);
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int
+ipac_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+       struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D);
+       struct dchannel *dch = container_of(dev, struct dchannel, dev);
+       struct isac_hw *isac = container_of(dch, struct isac_hw, dch);
+       struct ipac_hw *ipac = container_of(isac, struct ipac_hw, isac);
+       struct channel_req *rq;
+       int err = 0;
+
+       pr_debug("%s: DCTRL: %x %p\n", ipac->name, cmd, arg);
+       switch (cmd) {
+       case OPEN_CHANNEL:
+               rq = arg;
+               if (rq->protocol == ISDN_P_TE_S0)
+                       err = open_dchannel(isac, rq);
+               else
+                       err = open_bchannel(ipac, rq);
+               if (err)
+                       break;
+               if (!try_module_get(ipac->owner))
+                       pr_info("%s: cannot get module\n", ipac->name);
+               break;
+       case CLOSE_CHANNEL:
+               pr_debug("%s: dev(%d) close from %p\n", ipac->name,
+                       dch->dev.id, __builtin_return_address(0));
+               module_put(ipac->owner);
+               break;
+       case CONTROL_CHANNEL:
+               err = channel_ctrl(ipac, arg);
+               break;
+       default:
+               pr_debug("%s: unknown DCTRL command %x\n", ipac->name, cmd);
+               return -EINVAL;
+       }
+       return err;
+}
+
+u32
+mISDNipac_init(struct ipac_hw *ipac, void *hw)
+{
+       u32 ret;
+       u8 i;
+
+       ipac->hw = hw;
+       if (ipac->isac.dch.debug & DEBUG_HW)
+               pr_notice("%s: ipac type %x\n", ipac->name, ipac->type);
+       if (ipac->type & IPAC_TYPE_HSCX) {
+               ipac->isac.type = IPAC_TYPE_ISAC;
+               ipac->hscx[0].off = 0;
+               ipac->hscx[1].off = 0x40;
+               ipac->hscx[0].fifo_size = 32;
+               ipac->hscx[1].fifo_size = 32;
+       } else if (ipac->type & IPAC_TYPE_IPAC) {
+               ipac->isac.type = IPAC_TYPE_IPAC | IPAC_TYPE_ISAC;
+               ipac->hscx[0].off = 0;
+               ipac->hscx[1].off = 0x40;
+               ipac->hscx[0].fifo_size = 64;
+               ipac->hscx[1].fifo_size = 64;
+       } else if (ipac->type & IPAC_TYPE_IPACX) {
+               ipac->isac.type = IPAC_TYPE_IPACX | IPAC_TYPE_ISACX;
+               ipac->hscx[0].off = IPACX_OFF_ICA;
+               ipac->hscx[1].off = IPACX_OFF_ICB;
+               ipac->hscx[0].fifo_size = 64;
+               ipac->hscx[1].fifo_size = 64;
+       } else
+               return 0;
+
+       mISDNisac_init(&ipac->isac, hw);
+
+       ipac->isac.dch.dev.D.ctrl = ipac_dctrl;
+
+       for (i = 0; i < 2; i++) {
+               ipac->hscx[i].bch.nr = i + 1;
+               set_channelmap(i + 1, ipac->isac.dch.dev.channelmap);
+               list_add(&ipac->hscx[i].bch.ch.list,
+                       &ipac->isac.dch.dev.bchannels);
+               mISDN_initbchannel(&ipac->hscx[i].bch, MAX_DATA_MEM);
+               ipac->hscx[i].bch.ch.nr = i + 1;
+               ipac->hscx[i].bch.ch.send = &hscx_l2l1;
+               ipac->hscx[i].bch.ch.ctrl = hscx_bctrl;
+               ipac->hscx[i].bch.hw = hw;
+               ipac->hscx[i].ip = ipac;
+               /* default values for IOM time slots
+                * can be overwriten by card */
+               ipac->hscx[i].slot = (i == 0) ? 0x2f : 0x03;
+       }
+
+       ipac->init = ipac_init;
+       ipac->release = free_ipac;
+
+       ret =   (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+               (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+       return ret;
+}
+EXPORT_SYMBOL(mISDNipac_init);
+
+static int __init
+isac_mod_init(void)
+{
+       pr_notice("mISDNipac module version %s\n", ISAC_REV);
+       return 0;
+}
+
+static void __exit
+isac_mod_cleanup(void)
+{
+       pr_notice("mISDNipac module unloaded\n");
+}
+module_init(isac_mod_init);
+module_exit(isac_mod_cleanup);