Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / isdn / hardware / mISDN / mISDNinfineon.c
diff --git a/kernel/drivers/isdn/hardware/mISDN/mISDNinfineon.c b/kernel/drivers/isdn/hardware/mISDN/mISDNinfineon.c
new file mode 100644 (file)
index 0000000..d5bdbaf
--- /dev/null
@@ -0,0 +1,1173 @@
+/*
+ * 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/interrupt.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/mISDNhw.h>
+#include <linux/slab.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[] = {
+       { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_DIVA20), INF_DIVA20 },
+       { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_DIVA20_U), INF_DIVA20U },
+       { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_DIVA201), INF_DIVA201 },
+       { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_DIVA202), 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_VDEVICE(ELSA, PCI_DEVICE_ID_ELSA_MICROLINK), INF_QS1000 },
+       { PCI_VDEVICE(ELSA, PCI_DEVICE_ID_ELSA_QS3000), INF_QS3000 },
+       { PCI_VDEVICE(SATSAGEM, PCI_DEVICE_ID_SATSAGEM_NICCY), 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_VDEVICE(PLX, PCI_DEVICE_ID_PLX_R685), INF_GAZEL_R685 },
+       { PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_R753), INF_GAZEL_R753 },
+       { PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_DJINN_ITOO), INF_GAZEL_R753 },
+       { PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_OLITEC), 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[1].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
+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
+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
+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 *
+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
+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 information about adapter at %s\n",
+                       pci_name(pdev));
+               kfree(card);
+               pci_disable_device(pdev);
+               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(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);
+                               pci_disable_device(pdev);
+                               return -ENOMEM;
+                       }
+                       sc->irq = card->irq;
+                       sc->pdev = card->pdev;
+                       sc->ci = card->ci + i;
+                       err = setup_instance(sc);
+                       if (err) {
+                               pci_disable_device(pdev);
+                               kfree(sc);
+                               release_card(card);
+                               break;
+                       } else
+                               card->sc[i - 1] = sc;
+               }
+       }
+       return err;
+}
+
+static void
+inf_remove(struct pci_dev *pdev)
+{
+       struct inf_hw   *card = pci_get_drvdata(pdev);
+
+       if (card)
+               release_card(card);
+       else
+               pr_debug("%s: drvdata already removed\n", __func__);
+}
+
+static struct pci_driver infineon_driver = {
+       .name = "ISDN Infineon pci",
+       .probe = inf_probe,
+       .remove = 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);