summaryrefslogtreecommitdiffstats
path: root/drivers/isdn/hardware
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2009-09-16 13:48:32 +0900
committerPaul Mundt <lethal@linux-sh.org>2009-09-16 13:48:32 +0900
commitea88023b3491a384575ebcd5e8a449e841a28a24 (patch)
treef46e3d8302e44dc55ce31823501e100472d29683 /drivers/isdn/hardware
parenta6f15ade97989d414e9bf33874c9d5d1f39808ec (diff)
parent0cb583fd2862f19ea88b02eb307d11c09e51e2f8 (diff)
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Conflicts: arch/sh/kernel/vmlinux.lds.S
Diffstat (limited to 'drivers/isdn/hardware')
-rw-r--r--drivers/isdn/hardware/eicon/message.c4
-rw-r--r--drivers/isdn/hardware/eicon/os_4bri.c3
-rw-r--r--drivers/isdn/hardware/mISDN/Kconfig51
-rw-r--r--drivers/isdn/hardware/mISDN/Makefile8
-rw-r--r--drivers/isdn/hardware/mISDN/avmfritz.c1152
-rw-r--r--drivers/isdn/hardware/mISDN/hfcmulti.c23
-rw-r--r--drivers/isdn/hardware/mISDN/hfcpci.c16
-rw-r--r--drivers/isdn/hardware/mISDN/hfcsusb.c16
-rw-r--r--drivers/isdn/hardware/mISDN/iohelper.h109
-rw-r--r--drivers/isdn/hardware/mISDN/ipac.h405
-rw-r--r--drivers/isdn/hardware/mISDN/isar.h269
-rw-r--r--drivers/isdn/hardware/mISDN/mISDNinfineon.c1178
-rw-r--r--drivers/isdn/hardware/mISDN/mISDNipac.c1655
-rw-r--r--drivers/isdn/hardware/mISDN/mISDNisar.c1726
-rw-r--r--drivers/isdn/hardware/mISDN/netjet.c1156
-rw-r--r--drivers/isdn/hardware/mISDN/netjet.h58
-rw-r--r--drivers/isdn/hardware/mISDN/speedfax.c526
-rw-r--r--drivers/isdn/hardware/mISDN/w6692.c1440
-rw-r--r--drivers/isdn/hardware/mISDN/w6692.h190
19 files changed, 9932 insertions, 53 deletions
diff --git a/drivers/isdn/hardware/eicon/message.c b/drivers/isdn/hardware/eicon/message.c
index 31f91c18c69..27d5dd68f4f 100644
--- a/drivers/isdn/hardware/eicon/message.c
+++ b/drivers/isdn/hardware/eicon/message.c
@@ -551,9 +551,7 @@ word api_put(APPL * appl, CAPI_MSG * msg)
dbug(1,dprintf("com=%x",msg->header.command));
for(j=0;j<MAX_MSG_PARMS+1;j++) msg_parms[j].length = 0;
- for(i=0, ret = _BAD_MSG;
- i<(sizeof(ftable)/sizeof(struct _ftable));
- i++) {
+ for(i=0, ret = _BAD_MSG; i < ARRAY_SIZE(ftable); i++) {
if(ftable[i].command==msg->header.command) {
/* break loop if the message is correct, otherwise continue scan */
diff --git a/drivers/isdn/hardware/eicon/os_4bri.c b/drivers/isdn/hardware/eicon/os_4bri.c
index c964b8d91ad..cb7616c5b60 100644
--- a/drivers/isdn/hardware/eicon/os_4bri.c
+++ b/drivers/isdn/hardware/eicon/os_4bri.c
@@ -149,8 +149,7 @@ int diva_4bri_init_card(diva_os_xdi_adapter_t * a)
diva_os_xdi_adapter_t *diva_current;
diva_os_xdi_adapter_t *adapter_list[4];
PISDN_ADAPTER Slave;
- unsigned long bar_length[sizeof(_4bri_bar_length) /
- sizeof(_4bri_bar_length[0])];
+ unsigned long bar_length[ARRAY_SIZE(_4bri_bar_length)];
int v2 = _4bri_is_rev_2_card(a->CardOrdinal);
int tasks = _4bri_is_rev_2_bri_card(a->CardOrdinal) ? 1 : MQ_INSTANCE_COUNT;
int factor = (tasks == 1) ? 1 : 2;
diff --git a/drivers/isdn/hardware/mISDN/Kconfig b/drivers/isdn/hardware/mISDN/Kconfig
index 3024566dd09..bde55d7287f 100644
--- a/drivers/isdn/hardware/mISDN/Kconfig
+++ b/drivers/isdn/hardware/mISDN/Kconfig
@@ -39,3 +39,54 @@ config MISDN_HFCUSB
Enable support for USB ISDN TAs with Cologne Chip AG's
HFC-S USB ISDN Controller
+config MISDN_AVMFRITZ
+ tristate "Support for AVM FRITZ!CARD PCI"
+ depends on MISDN
+ depends on PCI
+ select MISDN_IPAC
+ help
+ Enable support for AVMs FRITZ!CARD PCI cards
+
+config MISDN_SPEEDFAX
+ tristate "Support for Sedlbauer Speedfax+"
+ depends on MISDN
+ depends on PCI
+ select MISDN_IPAC
+ select MISDN_ISAR
+ help
+ Enable support for Sedlbauer Speedfax+.
+
+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_W6692
+ tristate "Support for cards with Winbond 6692"
+ depends on MISDN
+ depends on PCI
+ help
+ Enable support for Winbond 6692 PCI chip based cards.
+
+config MISDN_NETJET
+ tristate "Support for NETJet cards"
+ depends on MISDN
+ depends on PCI
+ select MISDN_IPAC
+ select ISDN_HDLC
+ help
+ Enable support for Traverse Technologies NETJet PCI cards.
+
+
+config MISDN_IPAC
+ tristate
+ depends on MISDN
+
+config MISDN_ISAR
+ tristate
+ depends on MISDN
+
diff --git a/drivers/isdn/hardware/mISDN/Makefile b/drivers/isdn/hardware/mISDN/Makefile
index b0403526bbb..2987d990993 100644
--- a/drivers/isdn/hardware/mISDN/Makefile
+++ b/drivers/isdn/hardware/mISDN/Makefile
@@ -6,3 +6,11 @@
obj-$(CONFIG_MISDN_HFCPCI) += hfcpci.o
obj-$(CONFIG_MISDN_HFCMULTI) += hfcmulti.o
obj-$(CONFIG_MISDN_HFCUSB) += hfcsusb.o
+obj-$(CONFIG_MISDN_AVMFRITZ) += avmfritz.o
+obj-$(CONFIG_MISDN_SPEEDFAX) += speedfax.o
+obj-$(CONFIG_MISDN_INFINEON) += mISDNinfineon.o
+obj-$(CONFIG_MISDN_W6692) += w6692.o
+obj-$(CONFIG_MISDN_NETJET) += netjet.o
+# chip modules
+obj-$(CONFIG_MISDN_IPAC) += mISDNipac.o
+obj-$(CONFIG_MISDN_ISAR) += mISDNisar.o
diff --git a/drivers/isdn/hardware/mISDN/avmfritz.c b/drivers/isdn/hardware/mISDN/avmfritz.c
new file mode 100644
index 00000000000..81ac541d40d
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/avmfritz.c
@@ -0,0 +1,1152 @@
+/*
+ * avm_fritz.c low level stuff for AVM FRITZ!CARD PCI ISDN cards
+ * Thanks to AVM, Berlin for informations
+ *
+ * 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 <asm/unaligned.h>
+#include "ipac.h"
+
+
+#define AVMFRITZ_REV "2.1"
+
+static int AVM_cnt;
+static int debug;
+
+enum {
+ AVM_FRITZ_PCI,
+ AVM_FRITZ_PCIV2,
+};
+
+#define HDLC_FIFO 0x0
+#define HDLC_STATUS 0x4
+#define CHIP_WINDOW 0x10
+
+#define CHIP_INDEX 0x4
+#define AVM_HDLC_1 0x00
+#define AVM_HDLC_2 0x01
+#define AVM_ISAC_FIFO 0x02
+#define AVM_ISAC_REG_LOW 0x04
+#define AVM_ISAC_REG_HIGH 0x06
+
+#define AVM_STATUS0_IRQ_ISAC 0x01
+#define AVM_STATUS0_IRQ_HDLC 0x02
+#define AVM_STATUS0_IRQ_TIMER 0x04
+#define AVM_STATUS0_IRQ_MASK 0x07
+
+#define AVM_STATUS0_RESET 0x01
+#define AVM_STATUS0_DIS_TIMER 0x02
+#define AVM_STATUS0_RES_TIMER 0x04
+#define AVM_STATUS0_ENA_IRQ 0x08
+#define AVM_STATUS0_TESTBIT 0x10
+
+#define AVM_STATUS1_INT_SEL 0x0f
+#define AVM_STATUS1_ENA_IOM 0x80
+
+#define HDLC_MODE_ITF_FLG 0x01
+#define HDLC_MODE_TRANS 0x02
+#define HDLC_MODE_CCR_7 0x04
+#define HDLC_MODE_CCR_16 0x08
+#define HDLC_MODE_TESTLOOP 0x80
+
+#define HDLC_INT_XPR 0x80
+#define HDLC_INT_XDU 0x40
+#define HDLC_INT_RPR 0x20
+#define HDLC_INT_MASK 0xE0
+
+#define HDLC_STAT_RME 0x01
+#define HDLC_STAT_RDO 0x10
+#define HDLC_STAT_CRCVFRRAB 0x0E
+#define HDLC_STAT_CRCVFR 0x06
+#define HDLC_STAT_RML_MASK 0x3f00
+
+#define HDLC_CMD_XRS 0x80
+#define HDLC_CMD_XME 0x01
+#define HDLC_CMD_RRS 0x20
+#define HDLC_CMD_XML_MASK 0x3f00
+#define HDLC_FIFO_SIZE 32
+
+/* Fritz PCI v2.0 */
+
+#define AVM_HDLC_FIFO_1 0x10
+#define AVM_HDLC_FIFO_2 0x18
+
+#define AVM_HDLC_STATUS_1 0x14
+#define AVM_HDLC_STATUS_2 0x1c
+
+#define AVM_ISACX_INDEX 0x04
+#define AVM_ISACX_DATA 0x08
+
+/* data struct */
+#define LOG_SIZE 63
+
+struct hdlc_stat_reg {
+#ifdef __BIG_ENDIAN
+ u8 fill;
+ u8 mode;
+ u8 xml;
+ u8 cmd;
+#else
+ u8 cmd;
+ u8 xml;
+ u8 mode;
+ u8 fill;
+#endif
+} __attribute__((packed));
+
+struct hdlc_hw {
+ union {
+ u32 ctrl;
+ struct hdlc_stat_reg sr;
+ } ctrl;
+ u32 stat;
+};
+
+struct fritzcard {
+ struct list_head list;
+ struct pci_dev *pdev;
+ char name[MISDN_MAX_IDLEN];
+ u8 type;
+ u8 ctrlreg;
+ u16 irq;
+ u32 irqcnt;
+ u32 addr;
+ spinlock_t lock; /* hw lock */
+ struct isac_hw isac;
+ struct hdlc_hw hdlc[2];
+ struct bchannel bch[2];
+ char log[LOG_SIZE + 1];
+};
+
+static LIST_HEAD(Cards);
+static DEFINE_RWLOCK(card_lock); /* protect Cards */
+
+static void
+_set_debug(struct fritzcard *card)
+{
+ card->isac.dch.debug = debug;
+ card->bch[0].debug = debug;
+ card->bch[1].debug = debug;
+}
+
+static int
+set_debug(const char *val, struct kernel_param *kp)
+{
+ int ret;
+ struct fritzcard *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(AVMFRITZ_REV);
+module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "avmfritz debug mask");
+
+/* Interface functions */
+
+static u8
+ReadISAC_V1(void *p, u8 offset)
+{
+ struct fritzcard *fc = p;
+ u8 idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
+
+ outb(idx, fc->addr + CHIP_INDEX);
+ return inb(fc->addr + CHIP_WINDOW + (offset & 0xf));
+}
+
+static void
+WriteISAC_V1(void *p, u8 offset, u8 value)
+{
+ struct fritzcard *fc = p;
+ u8 idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
+
+ outb(idx, fc->addr + CHIP_INDEX);
+ outb(value, fc->addr + CHIP_WINDOW + (offset & 0xf));
+}
+
+static void
+ReadFiFoISAC_V1(void *p, u8 off, u8 *data, int size)
+{
+ struct fritzcard *fc = p;
+
+ outb(AVM_ISAC_FIFO, fc->addr + CHIP_INDEX);
+ insb(fc->addr + CHIP_WINDOW, data, size);
+}
+
+static void
+WriteFiFoISAC_V1(void *p, u8 off, u8 *data, int size)
+{
+ struct fritzcard *fc = p;
+
+ outb(AVM_ISAC_FIFO, fc->addr + CHIP_INDEX);
+ outsb(fc->addr + CHIP_WINDOW, data, size);
+}
+
+static u8
+ReadISAC_V2(void *p, u8 offset)
+{
+ struct fritzcard *fc = p;
+
+ outl(offset, fc->addr + AVM_ISACX_INDEX);
+ return 0xff & inl(fc->addr + AVM_ISACX_DATA);
+}
+
+static void
+WriteISAC_V2(void *p, u8 offset, u8 value)
+{
+ struct fritzcard *fc = p;
+
+ outl(offset, fc->addr + AVM_ISACX_INDEX);
+ outl(value, fc->addr + AVM_ISACX_DATA);
+}
+
+static void
+ReadFiFoISAC_V2(void *p, u8 off, u8 *data, int size)
+{
+ struct fritzcard *fc = p;
+ int i;
+
+ outl(off, fc->addr + AVM_ISACX_INDEX);
+ for (i = 0; i < size; i++)
+ data[i] = 0xff & inl(fc->addr + AVM_ISACX_DATA);
+}
+
+static void
+WriteFiFoISAC_V2(void *p, u8 off, u8 *data, int size)
+{
+ struct fritzcard *fc = p;
+ int i;
+
+ outl(off, fc->addr + AVM_ISACX_INDEX);
+ for (i = 0; i < size; i++)
+ outl(data[i], fc->addr + AVM_ISACX_DATA);
+}
+
+static struct bchannel *
+Sel_BCS(struct fritzcard *fc, u32 channel)
+{
+ if (test_bit(FLG_ACTIVE, &fc->bch[0].Flags) &&
+ (fc->bch[0].nr & channel))
+ return &fc->bch[0];
+ else if (test_bit(FLG_ACTIVE, &fc->bch[1].Flags) &&
+ (fc->bch[1].nr & channel))
+ return &fc->bch[1];
+ else
+ return NULL;
+}
+
+static inline void
+__write_ctrl_pci(struct fritzcard *fc, struct hdlc_hw *hdlc, u32 channel) {
+ u32 idx = channel == 2 ? AVM_HDLC_2 : AVM_HDLC_1;
+
+ outl(idx, fc->addr + CHIP_INDEX);
+ outl(hdlc->ctrl.ctrl, fc->addr + CHIP_WINDOW + HDLC_STATUS);
+}
+
+static inline void
+__write_ctrl_pciv2(struct fritzcard *fc, struct hdlc_hw *hdlc, u32 channel) {
+ outl(hdlc->ctrl.ctrl, fc->addr + (channel == 2 ? AVM_HDLC_STATUS_2 :
+ AVM_HDLC_STATUS_1));
+}
+
+void
+write_ctrl(struct bchannel *bch, int which) {
+ struct fritzcard *fc = bch->hw;
+ struct hdlc_hw *hdlc;
+
+ hdlc = &fc->hdlc[(bch->nr - 1) & 1];
+ pr_debug("%s: hdlc %c wr%x ctrl %x\n", fc->name, '@' + bch->nr,
+ which, hdlc->ctrl.ctrl);
+ switch (fc->type) {
+ case AVM_FRITZ_PCIV2:
+ __write_ctrl_pciv2(fc, hdlc, bch->nr);
+ break;
+ case AVM_FRITZ_PCI:
+ __write_ctrl_pci(fc, hdlc, bch->nr);
+ break;
+ }
+}
+
+
+static inline u32
+__read_status_pci(u_long addr, u32 channel)
+{
+ outl(channel == 2 ? AVM_HDLC_2 : AVM_HDLC_1, addr + CHIP_INDEX);
+ return inl(addr + CHIP_WINDOW + HDLC_STATUS);
+}
+
+static inline u32
+__read_status_pciv2(u_long addr, u32 channel)
+{
+ return inl(addr + (channel == 2 ? AVM_HDLC_STATUS_2 :
+ AVM_HDLC_STATUS_1));
+}
+
+
+static u32
+read_status(struct fritzcard *fc, u32 channel)
+{
+ switch (fc->type) {
+ case AVM_FRITZ_PCIV2:
+ return __read_status_pciv2(fc->addr, channel);
+ case AVM_FRITZ_PCI:
+ return __read_status_pci(fc->addr, channel);
+ }
+ /* dummy */
+ return 0;
+}
+
+static void
+enable_hwirq(struct fritzcard *fc)
+{
+ fc->ctrlreg |= AVM_STATUS0_ENA_IRQ;
+ outb(fc->ctrlreg, fc->addr + 2);
+}
+
+static void
+disable_hwirq(struct fritzcard *fc)
+{
+ fc->ctrlreg &= ~AVM_STATUS0_ENA_IRQ;
+ outb(fc->ctrlreg, fc->addr + 2);
+}
+
+static int
+modehdlc(struct bchannel *bch, int protocol)
+{
+ struct fritzcard *fc = bch->hw;
+ struct hdlc_hw *hdlc;
+
+ hdlc = &fc->hdlc[(bch->nr - 1) & 1];
+ pr_debug("%s: hdlc %c protocol %x-->%x ch %d\n", fc->name,
+ '@' + bch->nr, bch->state, protocol, bch->nr);
+ hdlc->ctrl.ctrl = 0;
+ switch (protocol) {
+ case -1: /* used for init */
+ bch->state = -1;
+ case ISDN_P_NONE:
+ if (bch->state == ISDN_P_NONE)
+ break;
+ hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS;
+ hdlc->ctrl.sr.mode = HDLC_MODE_TRANS;
+ write_ctrl(bch, 5);
+ bch->state = ISDN_P_NONE;
+ test_and_clear_bit(FLG_HDLC, &bch->Flags);
+ test_and_clear_bit(FLG_TRANSPARENT, &bch->Flags);
+ break;
+ case ISDN_P_B_RAW:
+ bch->state = protocol;
+ hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS;
+ hdlc->ctrl.sr.mode = HDLC_MODE_TRANS;
+ write_ctrl(bch, 5);
+ hdlc->ctrl.sr.cmd = HDLC_CMD_XRS;
+ write_ctrl(bch, 1);
+ hdlc->ctrl.sr.cmd = 0;
+ test_and_set_bit(FLG_TRANSPARENT, &bch->Flags);
+ break;
+ case ISDN_P_B_HDLC:
+ bch->state = protocol;
+ hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS;
+ hdlc->ctrl.sr.mode = HDLC_MODE_ITF_FLG;
+ write_ctrl(bch, 5);
+ hdlc->ctrl.sr.cmd = HDLC_CMD_XRS;
+ write_ctrl(bch, 1);
+ hdlc->ctrl.sr.cmd = 0;
+ test_and_set_bit(FLG_HDLC, &bch->Flags);
+ break;
+ default:
+ pr_info("%s: protocol not known %x\n", fc->name, protocol);
+ return -ENOPROTOOPT;
+ }
+ return 0;
+}
+
+static void
+hdlc_empty_fifo(struct bchannel *bch, int count)
+{
+ u32 *ptr;
+ u8 *p;
+ u32 val, addr;
+ int cnt = 0;
+ struct fritzcard *fc = bch->hw;
+
+ pr_debug("%s: %s %d\n", fc->name, __func__, count);
+ if (!bch->rx_skb) {
+ bch->rx_skb = mI_alloc_skb(bch->maxlen, GFP_ATOMIC);
+ if (!bch->rx_skb) {
+ pr_info("%s: B receive out of memory\n",
+ fc->name);
+ return;
+ }
+ }
+ if ((bch->rx_skb->len + count) > bch->maxlen) {
+ pr_debug("%s: overrun %d\n", fc->name,
+ bch->rx_skb->len + count);
+ return;
+ }
+ p = skb_put(bch->rx_skb, count);
+ ptr = (u32 *)p;
+ if (AVM_FRITZ_PCIV2 == fc->type)
+ addr = fc->addr + (bch->nr == 2 ?
+ AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1);
+ else {
+ addr = fc->addr + CHIP_WINDOW;
+ outl(bch->nr == 2 ? AVM_HDLC_2 : AVM_HDLC_1, fc->addr);
+ }
+ while (cnt < count) {
+ val = le32_to_cpu(inl(addr));
+ put_unaligned(val, ptr);
+ ptr++;
+ cnt += 4;
+ }
+ if (debug & DEBUG_HW_BFIFO) {
+ snprintf(fc->log, LOG_SIZE, "B%1d-recv %s %d ",
+ bch->nr, fc->name, count);
+ print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count);
+ }
+}
+
+static void
+hdlc_fill_fifo(struct bchannel *bch)
+{
+ struct fritzcard *fc = bch->hw;
+ struct hdlc_hw *hdlc;
+ int count, cnt = 0;
+ u8 *p;
+ u32 *ptr, val, addr;
+
+ hdlc = &fc->hdlc[(bch->nr - 1) & 1];
+ if (!bch->tx_skb)
+ return;
+ count = bch->tx_skb->len - bch->tx_idx;
+ if (count <= 0)
+ return;
+ p = bch->tx_skb->data + bch->tx_idx;
+ hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XME;
+ if (count > HDLC_FIFO_SIZE) {
+ count = HDLC_FIFO_SIZE;
+ } else {
+ if (test_bit(FLG_HDLC, &bch->Flags))
+ hdlc->ctrl.sr.cmd |= HDLC_CMD_XME;
+ }
+ pr_debug("%s: %s %d/%d/%d", fc->name, __func__, count,
+ bch->tx_idx, bch->tx_skb->len);
+ ptr = (u32 *)p;
+ bch->tx_idx += count;
+ hdlc->ctrl.sr.xml = ((count == HDLC_FIFO_SIZE) ? 0 : count);
+ if (AVM_FRITZ_PCIV2 == fc->type) {
+ __write_ctrl_pciv2(fc, hdlc, bch->nr);
+ addr = fc->addr + (bch->nr == 2 ?
+ AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1);
+ } else {
+ __write_ctrl_pci(fc, hdlc, bch->nr);
+ addr = fc->addr + CHIP_WINDOW;
+ }
+ while (cnt < count) {
+ val = get_unaligned(ptr);
+ outl(cpu_to_le32(val), addr);
+ ptr++;
+ cnt += 4;
+ }
+ if (debug & DEBUG_HW_BFIFO) {
+ snprintf(fc->log, LOG_SIZE, "B%1d-send %s %d ",
+ bch->nr, fc->name, count);
+ print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count);
+ }
+}
+
+static void
+HDLC_irq_xpr(struct bchannel *bch)
+{
+ if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len)
+ hdlc_fill_fifo(bch);
+ else {
+ if (bch->tx_skb) {
+ /* send confirm, on trans, free on hdlc. */
+ if (test_bit(FLG_TRANSPARENT, &bch->Flags))
+ confirm_Bsend(bch);
+ dev_kfree_skb(bch->tx_skb);
+ }
+ if (get_next_bframe(bch))
+ hdlc_fill_fifo(bch);
+ }
+}
+
+static void
+HDLC_irq(struct bchannel *bch, u32 stat)
+{
+ struct fritzcard *fc = bch->hw;
+ int len;
+ struct hdlc_hw *hdlc;
+
+ hdlc = &fc->hdlc[(bch->nr - 1) & 1];
+ pr_debug("%s: ch%d stat %#x\n", fc->name, bch->nr, stat);
+ if (stat & HDLC_INT_RPR) {
+ if (stat & HDLC_STAT_RDO) {
+ hdlc->ctrl.sr.xml = 0;
+ hdlc->ctrl.sr.cmd |= HDLC_CMD_RRS;
+ write_ctrl(bch, 1);
+ hdlc->ctrl.sr.cmd &= ~HDLC_CMD_RRS;
+ write_ctrl(bch, 1);
+ if (bch->rx_skb)
+ skb_trim(bch->rx_skb, 0);
+ } else {
+ len = (stat & HDLC_STAT_RML_MASK) >> 8;
+ if (!len)
+ len = 32;
+ hdlc_empty_fifo(bch, len);
+ if (!bch->rx_skb)
+ goto handle_tx;
+ if ((stat & HDLC_STAT_RME) || test_bit(FLG_TRANSPARENT,
+ &bch->Flags)) {
+ if (((stat & HDLC_STAT_CRCVFRRAB) ==
+ HDLC_STAT_CRCVFR) ||
+ test_bit(FLG_TRANSPARENT, &bch->Flags)) {
+ recv_Bchannel(bch, 0);
+ } else {
+ pr_debug("%s: got invalid frame\n",
+ fc->name);
+ skb_trim(bch->rx_skb, 0);
+ }
+ }
+ }
+ }
+handle_tx:
+ if (stat & HDLC_INT_XDU) {
+ /* Here we lost an TX interrupt, so
+ * restart transmitting the whole frame on HDLC
+ * in transparent mode we send the next data
+ */
+ if (bch->tx_skb)
+ pr_debug("%s: ch%d XDU len(%d) idx(%d) Flags(%lx)\n",
+ fc->name, bch->nr, bch->tx_skb->len,
+ bch->tx_idx, bch->Flags);
+ else
+ pr_debug("%s: ch%d XDU no tx_skb Flags(%lx)\n",
+ fc->name, bch->nr, bch->Flags);
+ if (bch->tx_skb && bch->tx_skb->len) {
+ if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+ bch->tx_idx = 0;
+ }
+ hdlc->ctrl.sr.xml = 0;
+ hdlc->ctrl.sr.cmd |= HDLC_CMD_XRS;
+ write_ctrl(bch, 1);
+ hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XRS;
+ HDLC_irq_xpr(bch);
+ return;
+ } else if (stat & HDLC_INT_XPR)
+ HDLC_irq_xpr(bch);
+}
+
+static inline void
+HDLC_irq_main(struct fritzcard *fc)
+{
+ u32 stat;
+ struct bchannel *bch;
+
+ stat = read_status(fc, 1);
+ if (stat & HDLC_INT_MASK) {
+ bch = Sel_BCS(fc, 1);
+ if (bch)
+ HDLC_irq(bch, stat);
+ else
+ pr_debug("%s: spurious ch1 IRQ\n", fc->name);
+ }
+ stat = read_status(fc, 2);
+ if (stat & HDLC_INT_MASK) {
+ bch = Sel_BCS(fc, 2);
+ if (bch)
+ HDLC_irq(bch, stat);
+ else
+ pr_debug("%s: spurious ch2 IRQ\n", fc->name);
+ }
+}
+
+static irqreturn_t
+avm_fritz_interrupt(int intno, void *dev_id)
+{
+ struct fritzcard *fc = dev_id;
+ u8 val;
+ u8 sval;
+
+ spin_lock(&fc->lock);
+ sval = inb(fc->addr + 2);
+ pr_debug("%s: irq stat0 %x\n", fc->name, sval);
+ if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK) {
+ /* shared IRQ from other HW */
+ spin_unlock(&fc->lock);
+ return IRQ_NONE;
+ }
+ fc->irqcnt++;
+
+ if (!(sval & AVM_STATUS0_IRQ_ISAC)) {
+ val = ReadISAC_V1(fc, ISAC_ISTA);
+ mISDNisac_irq(&fc->isac, val);
+ }
+ if (!(sval & AVM_STATUS0_IRQ_HDLC))
+ HDLC_irq_main(fc);
+ spin_unlock(&fc->lock);
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+avm_fritzv2_interrupt(int intno, void *dev_id)
+{
+ struct fritzcard *fc = dev_id;
+ u8 val;
+ u8 sval;
+
+ spin_lock(&fc->lock);
+ sval = inb(fc->addr + 2);
+ pr_debug("%s: irq stat0 %x\n", fc->name, sval);
+ if (!(sval & AVM_STATUS0_IRQ_MASK)) {
+ /* shared IRQ from other HW */
+ spin_unlock(&fc->lock);
+ return IRQ_NONE;
+ }
+ fc->irqcnt++;
+
+ if (sval & AVM_STATUS0_IRQ_HDLC)
+ HDLC_irq_main(fc);
+ if (sval & AVM_STATUS0_IRQ_ISAC) {
+ val = ReadISAC_V2(fc, ISACX_ISTA);
+ mISDNisac_irq(&fc->isac, val);
+ }
+ if (sval & AVM_STATUS0_IRQ_TIMER) {
+ pr_debug("%s: timer irq\n", fc->name);
+ outb(fc->ctrlreg | AVM_STATUS0_RES_TIMER, fc->addr + 2);
+ udelay(1);
+ outb(fc->ctrlreg, fc->addr + 2);
+ }
+ spin_unlock(&fc->lock);
+ return IRQ_HANDLED;
+}
+
+static int
+avm_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+ struct bchannel *bch = container_of(ch, struct bchannel, ch);
+ struct fritzcard *fc = bch->hw;
+ 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(&fc->lock, flags);
+ ret = bchannel_senddata(bch, skb);
+ if (ret > 0) { /* direct TX */
+ id = hh->id; /* skb can be freed */
+ hdlc_fill_fifo(bch);
+ ret = 0;
+ spin_unlock_irqrestore(&fc->lock, flags);
+ if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+ queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+ } else
+ spin_unlock_irqrestore(&fc->lock, flags);
+ return ret;
+ case PH_ACTIVATE_REQ:
+ spin_lock_irqsave(&fc->lock, flags);
+ if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
+ ret = modehdlc(bch, ch->protocol);
+ else
+ ret = 0;
+ spin_unlock_irqrestore(&fc->lock, flags);
+ if (!ret)
+ _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
+ NULL, GFP_KERNEL);
+ break;
+ case PH_DEACTIVATE_REQ:
+ spin_lock_irqsave(&fc->lock, flags);
+ mISDN_clear_bchannel(bch);
+ modehdlc(bch, ISDN_P_NONE);
+ spin_unlock_irqrestore(&fc->lock, flags);
+ _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
+ NULL, GFP_KERNEL);
+ ret = 0;
+ break;
+ }
+ if (!ret)
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+static void
+inithdlc(struct fritzcard *fc)
+{
+ modehdlc(&fc->bch[0], -1);
+ modehdlc(&fc->bch[1], -1);
+}
+
+void
+clear_pending_hdlc_ints(struct fritzcard *fc)
+{
+ u32 val;
+
+ val = read_status(fc, 1);
+ pr_debug("%s: HDLC 1 STA %x\n", fc->name, val);
+ val = read_status(fc, 2);
+ pr_debug("%s: HDLC 2 STA %x\n", fc->name, val);
+}
+
+static void
+reset_avm(struct fritzcard *fc)
+{
+ switch (fc->type) {
+ case AVM_FRITZ_PCI:
+ fc->ctrlreg = AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER;
+ break;
+ case AVM_FRITZ_PCIV2:
+ fc->ctrlreg = AVM_STATUS0_RESET;
+ break;
+ }
+ if (debug & DEBUG_HW)
+ pr_notice("%s: reset\n", fc->name);
+ disable_hwirq(fc);
+ mdelay(5);
+ switch (fc->type) {
+ case AVM_FRITZ_PCI:
+ fc->ctrlreg = AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER;
+ disable_hwirq(fc);
+ outb(AVM_STATUS1_ENA_IOM, fc->addr + 3);
+ break;
+ case AVM_FRITZ_PCIV2:
+ fc->ctrlreg = 0;
+ disable_hwirq(fc);
+ break;
+ }
+ mdelay(1);
+ if (debug & DEBUG_HW)
+ pr_notice("%s: S0/S1 %x/%x\n", fc->name,
+ inb(fc->addr + 2), inb(fc->addr + 3));
+}
+
+static int
+init_card(struct fritzcard *fc)
+{
+ int ret, cnt = 3;
+ u_long flags;
+
+ reset_avm(fc); /* disable IRQ */
+ if (fc->type == AVM_FRITZ_PCIV2)
+ ret = request_irq(fc->irq, avm_fritzv2_interrupt,
+ IRQF_SHARED, fc->name, fc);
+ else
+ ret = request_irq(fc->irq, avm_fritz_interrupt,
+ IRQF_SHARED, fc->name, fc);
+ if (ret) {
+ pr_info("%s: couldn't get interrupt %d\n",
+ fc->name, fc->irq);
+ return ret;
+ }
+ while (cnt--) {
+ spin_lock_irqsave(&fc->lock, flags);
+ ret = fc->isac.init(&fc->isac);
+ if (ret) {
+ spin_unlock_irqrestore(&fc->lock, flags);
+ pr_info("%s: ISAC init failed with %d\n",
+ fc->name, ret);
+ break;
+ }
+ clear_pending_hdlc_ints(fc);
+ inithdlc(fc);
+ enable_hwirq(fc);
+ /* RESET Receiver and Transmitter */
+ if (AVM_FRITZ_PCIV2 == fc->type) {
+ WriteISAC_V2(fc, ISACX_MASK, 0);
+ WriteISAC_V2(fc, ISACX_CMDRD, 0x41);
+ } else {
+ WriteISAC_V1(fc, ISAC_MASK, 0);
+ WriteISAC_V1(fc, ISAC_CMDR, 0x41);
+ }
+ spin_unlock_irqrestore(&fc->lock, flags);
+ /* Timeout 10ms */
+ msleep_interruptible(10);
+ if (debug & DEBUG_HW)
+ pr_notice("%s: IRQ %d count %d\n", fc->name,
+ fc->irq, fc->irqcnt);
+ if (!fc->irqcnt) {
+ pr_info("%s: IRQ(%d) getting no IRQs during init %d\n",
+ fc->name, fc->irq, 3 - cnt);
+ reset_avm(fc);
+ } else
+ return 0;
+ }
+ free_irq(fc->irq, fc);
+ return -EIO;
+}
+
+static int
+channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
+{
+ int ret = 0;
+ struct fritzcard *fc = bch->hw;
+
+ switch (cq->op) {
+ case MISDN_CTRL_GETOP:
+ cq->op = 0;
+ break;
+ /* Nothing implemented yet */
+ case MISDN_CTRL_FILL_EMPTY:
+ default:
+ pr_info("%s: %s unknown Op %x\n", fc->name, __func__, cq->op);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int
+avm_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+ struct bchannel *bch = container_of(ch, struct bchannel, ch);
+ struct fritzcard *fc = bch->hw;
+ int ret = -EINVAL;
+ u_long flags;
+
+ pr_debug("%s: %s cmd:%x %p\n", fc->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(&fc->lock, flags);
+ mISDN_freebchannel(bch);
+ test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
+ test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
+ modehdlc(bch, ISDN_P_NONE);
+ spin_unlock_irqrestore(&fc->lock, flags);
+ }
+ ch->protocol = ISDN_P_NONE;
+ ch->peer = NULL;
+ module_put(THIS_MODULE);
+ ret = 0;
+ break;
+ case CONTROL_CHANNEL:
+ ret = channel_bctrl(bch, arg);
+ break;
+ default:
+ pr_info("%s: %s unknown prim(%x)\n", fc->name, __func__, cmd);
+ }
+ return ret;
+}
+
+static int
+channel_ctrl(struct fritzcard *fc, 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 = fc->isac.ctrl(&fc->isac, HW_TESTLOOP, cq->channel);
+ break;
+ default:
+ pr_info("%s: %s unknown Op %x\n", fc->name, __func__, cq->op);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int
+open_bchannel(struct fritzcard *fc, struct channel_req *rq)
+{
+ struct bchannel *bch;
+
+ if (rq->adr.channel > 2)
+ return -EINVAL;
+ if (rq->protocol == ISDN_P_NONE)
+ return -EINVAL;
+ bch = &fc->bch[rq->adr.channel - 1];
+ 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;
+}
+
+/*
+ * device control function
+ */
+static int
+avm_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 fritzcard *fc = dch->hw;
+ struct channel_req *rq;
+ int err = 0;
+
+ pr_debug("%s: %s cmd:%x %p\n", fc->name, __func__, cmd, arg);
+ switch (cmd) {
+ case OPEN_CHANNEL:
+ rq = arg;
+ if (rq->protocol == ISDN_P_TE_S0)
+ err = fc->isac.open(&fc->isac, rq);
+ else
+ err = open_bchannel(fc, rq);
+ if (err)
+ break;
+ if (!try_module_get(THIS_MODULE))
+ pr_info("%s: cannot get module\n", fc->name);
+ break;
+ case CLOSE_CHANNEL:
+ pr_debug("%s: dev(%d) close from %p\n", fc->name, dch->dev.id,
+ __builtin_return_address(0));
+ module_put(THIS_MODULE);
+ break;
+ case CONTROL_CHANNEL:
+ err = channel_ctrl(fc, arg);
+ break;
+ default:
+ pr_debug("%s: %s unknown command %x\n",
+ fc->name, __func__, cmd);
+ return -EINVAL;
+ }
+ return err;
+}
+
+int
+setup_fritz(struct fritzcard *fc)
+{
+ u32 val, ver;
+
+ if (!request_region(fc->addr, 32, fc->name)) {
+ pr_info("%s: AVM config port %x-%x already in use\n",
+ fc->name, fc->addr, fc->addr + 31);
+ return -EIO;
+ }
+ switch (fc->type) {
+ case AVM_FRITZ_PCI:
+ val = inl(fc->addr);
+ outl(AVM_HDLC_1, fc->addr + CHIP_INDEX);
+ ver = inl(fc->addr + CHIP_WINDOW + HDLC_STATUS) >> 24;
+ if (debug & DEBUG_HW) {
+ pr_notice("%s: PCI stat %#x\n", fc->name, val);
+ pr_notice("%s: PCI Class %X Rev %d\n", fc->name,
+ val & 0xff, (val >> 8) & 0xff);
+ pr_notice("%s: HDLC version %x\n", fc->name, ver & 0xf);
+ }
+ ASSIGN_FUNC(V1, ISAC, fc->isac);
+ fc->isac.type = IPAC_TYPE_ISAC;
+ break;
+ case AVM_FRITZ_PCIV2:
+ val = inl(fc->addr);
+ ver = inl(fc->addr + AVM_HDLC_STATUS_1) >> 24;
+ if (debug & DEBUG_HW) {
+ pr_notice("%s: PCI V2 stat %#x\n", fc->name, val);
+ pr_notice("%s: PCI V2 Class %X Rev %d\n", fc->name,
+ val & 0xff, (val>>8) & 0xff);
+ pr_notice("%s: HDLC version %x\n", fc->name, ver & 0xf);
+ }
+ ASSIGN_FUNC(V2, ISAC, fc->isac);
+ fc->isac.type = IPAC_TYPE_ISACX;
+ break;
+ default:
+ release_region(fc->addr, 32);
+ pr_info("%s: AVM unknown type %d\n", fc->name, fc->type);
+ return -ENODEV;
+ }
+ pr_notice("%s: %s config irq:%d base:0x%X\n", fc->name,
+ (fc->type == AVM_FRITZ_PCI) ? "AVM Fritz!CARD PCI" :
+ "AVM Fritz!CARD PCIv2", fc->irq, fc->addr);
+ return 0;
+}
+
+static void
+release_card(struct fritzcard *card)
+{
+ u_long flags;
+
+ disable_hwirq(card);
+ spin_lock_irqsave(&card->lock, flags);
+ modehdlc(&card->bch[0], ISDN_P_NONE);
+ modehdlc(&card->bch[1], ISDN_P_NONE);
+ spin_unlock_irqrestore(&card->lock, flags);
+ card->isac.release(&card->isac);
+ free_irq(card->irq, card);
+ mISDN_freebchannel(&card->bch[1]);
+ mISDN_freebchannel(&card->bch[0]);
+ mISDN_unregister_device(&card->isac.dch.dev);
+ release_region(card->addr, 32);
+ pci_disable_device(card->pdev);
+ pci_set_drvdata(card->pdev, NULL);
+ write_lock_irqsave(&card_lock, flags);
+ list_del(&card->list);
+ write_unlock_irqrestore(&card_lock, flags);
+ kfree(card);
+ AVM_cnt--;
+}
+
+static int __devinit
+setup_instance(struct fritzcard *card)
+{
+ int i, err;
+ u_long flags;
+
+ snprintf(card->name, MISDN_MAX_IDLEN - 1, "AVM.%d", AVM_cnt + 1);
+ write_lock_irqsave(&card_lock, flags);
+ list_add_tail(&card->list, &Cards);
+ write_unlock_irqrestore(&card_lock, flags);
+
+ _set_debug(card);
+ card->isac.name = card->name;
+ spin_lock_init(&card->lock);
+ card->isac.hwlock = &card->lock;
+ mISDNisac_init(&card->isac, card);
+
+ card->isac.dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+ (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+ card->isac.dch.dev.D.ctrl = avm_dctrl;
+ for (i = 0; i < 2; i++) {
+ card->bch[i].nr = i + 1;
+ set_channelmap(i + 1, card->isac.dch.dev.channelmap);
+ mISDN_initbchannel(&card->bch[i], MAX_DATA_MEM);
+ card->bch[i].hw = card;
+ card->bch[i].ch.send = avm_l2l1B;
+ card->bch[i].ch.ctrl = avm_bctrl;
+ card->bch[i].ch.nr = i + 1;
+ list_add(&card->bch[i].ch.list, &card->isac.dch.dev.bchannels);
+ }
+ err = setup_fritz(card);
+ if (err)
+ goto error;
+ err = mISDN_register_device(&card->isac.dch.dev, &card->pdev->dev,
+ card->name);
+ if (err)
+ goto error_reg;
+ err = init_card(card);
+ if (!err) {
+ AVM_cnt++;
+ pr_notice("AVM %d cards installed DEBUG\n", AVM_cnt);
+ return 0;
+ }
+ mISDN_unregister_device(&card->isac.dch.dev);
+error_reg:
+ release_region(card->addr, 32);
+error:
+ card->isac.release(&card->isac);
+ mISDN_freebchannel(&card->bch[1]);
+ mISDN_freebchannel(&card->bch[0]);
+ write_lock_irqsave(&card_lock, flags);
+ list_del(&card->list);
+ write_unlock_irqrestore(&card_lock, flags);
+ kfree(card);
+ return err;
+}
+
+static int __devinit
+fritzpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ int err = -ENOMEM;
+ struct fritzcard *card;
+
+ card = kzalloc(sizeof(struct fritzcard), GFP_KERNEL);
+ if (!card) {
+ pr_info("No kmem for fritzcard\n");
+ return err;
+ }
+ if (pdev->device == PCI_DEVICE_ID_AVM_A1_V2)
+ card->type = AVM_FRITZ_PCIV2;
+ else
+ card->type = AVM_FRITZ_PCI;
+ card->pdev = pdev;
+ err = pci_enable_device(pdev);
+ if (err) {
+ kfree(card);
+ return err;
+ }
+
+ pr_notice("mISDN: found adapter %s at %s\n",
+ (char *) ent->driver_data, pci_name(pdev));
+
+ card->addr = pci_resource_start(pdev, 1);
+ card->irq = pdev->irq;
+ pci_set_drvdata(pdev, card);
+ err = setup_instance(card);
+ if (err)
+ pci_set_drvdata(pdev, NULL);
+ return err;
+}
+
+static void __devexit
+fritz_remove_pci(struct pci_dev *pdev)
+{
+ struct fritzcard *card = pci_get_drvdata(pdev);
+
+ if (card)
+ release_card(card);
+ else
+ if (debug)
+ pr_info("%s: drvdata allready removed\n", __func__);
+}
+
+static struct pci_device_id fcpci_ids[] __devinitdata = {
+ { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1, PCI_ANY_ID, PCI_ANY_ID,
+ 0, 0, (unsigned long) "Fritz!Card PCI"},
+ { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1_V2, PCI_ANY_ID, PCI_ANY_ID,
+ 0, 0, (unsigned long) "Fritz!Card PCI v2" },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, fcpci_ids);
+
+static struct pci_driver fcpci_driver = {
+ .name = "fcpci",
+ .probe = fritzpci_probe,
+ .remove = __devexit_p(fritz_remove_pci),
+ .id_table = fcpci_ids,
+};
+
+static int __init AVM_init(void)
+{
+ int err;
+
+ pr_notice("AVM Fritz PCI driver Rev. %s\n", AVMFRITZ_REV);
+ err = pci_register_driver(&fcpci_driver);
+ return err;
+}
+
+static void __exit AVM_cleanup(void)
+{
+ pci_unregister_driver(&fcpci_driver);
+}
+
+module_init(AVM_init);
+module_exit(AVM_cleanup);
diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c
index e1dab30aed3..faed794cf75 100644
--- a/drivers/isdn/hardware/mISDN/hfcmulti.c
+++ b/drivers/isdn/hardware/mISDN/hfcmulti.c
@@ -3416,22 +3416,8 @@ deactivate_bchannel(struct bchannel *bch)
u_long flags;
spin_lock_irqsave(&hc->lock, flags);
- if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) {
- dev_kfree_skb(bch->next_skb);
- bch->next_skb = NULL;
- }
- if (bch->tx_skb) {
- dev_kfree_skb(bch->tx_skb);
- bch->tx_skb = NULL;
- }
- bch->tx_idx = 0;
- if (bch->rx_skb) {
- dev_kfree_skb(bch->rx_skb);
- bch->rx_skb = NULL;
- }
+ mISDN_clear_bchannel(bch);
hc->chan[bch->slot].coeff_count = 0;
- test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
- test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
hc->chan[bch->slot].rx_off = 0;
hc->chan[bch->slot].conf = -1;
mode_hfcmulti(hc, bch->slot, ISDN_P_NONE, -1, 0, -1, 0);
@@ -5384,9 +5370,10 @@ hfcmulti_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
ent->device == PCI_DEVICE_ID_CCD_HFC8S ||
ent->device == PCI_DEVICE_ID_CCD_HFCE1)) {
printk(KERN_ERR
- "Unknown HFC multiport controller (vendor:%x device:%x "
- "subvendor:%x subdevice:%x)\n", ent->vendor, ent->device,
- ent->subvendor, ent->subdevice);
+ "Unknown HFC multiport controller (vendor:%04x device:%04x "
+ "subvendor:%04x subdevice:%04x)\n", pdev->vendor,
+ pdev->device, pdev->subsystem_vendor,
+ pdev->subsystem_device);
printk(KERN_ERR
"Please contact the driver maintainer for support.\n");
return -ENODEV;
diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c
index 228ffbed128..70e6b0e0112 100644
--- a/drivers/isdn/hardware/mISDN/hfcpci.c
+++ b/drivers/isdn/hardware/mISDN/hfcpci.c
@@ -1522,22 +1522,8 @@ deactivate_bchannel(struct bchannel *bch)
u_long flags;
spin_lock_irqsave(&hc->lock, flags);
- if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) {
- dev_kfree_skb(bch->next_skb);
- bch->next_skb = NULL;
- }
- if (bch->tx_skb) {
- dev_kfree_skb(bch->tx_skb);
- bch->tx_skb = NULL;
- }
- bch->tx_idx = 0;
- if (bch->rx_skb) {
- dev_kfree_skb(bch->rx_skb);
- bch->rx_skb = NULL;
- }
+ mISDN_clear_bchannel(bch);
mode_hfcpci(bch, bch->nr, ISDN_P_NONE);
- test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
- test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
spin_unlock_irqrestore(&hc->lock, flags);
}
diff --git a/drivers/isdn/hardware/mISDN/hfcsusb.c b/drivers/isdn/hardware/mISDN/hfcsusb.c
index 6b7704c41b9..fc46a26cb14 100644
--- a/drivers/isdn/hardware/mISDN/hfcsusb.c
+++ b/drivers/isdn/hardware/mISDN/hfcsusb.c
@@ -1809,21 +1809,7 @@ deactivate_bchannel(struct bchannel *bch)
hw->name, __func__, bch->nr);
spin_lock_irqsave(&hw->lock, flags);
- if (test_and_clear_bit(FLG_TX_NEXT, &bch->Flags)) {
- dev_kfree_skb(bch->next_skb);
- bch->next_skb = NULL;
- }
- if (bch->tx_skb) {
- dev_kfree_skb(bch->tx_skb);
- bch->tx_skb = NULL;
- }
- bch->tx_idx = 0;
- if (bch->rx_skb) {
- dev_kfree_skb(bch->rx_skb);
- bch->rx_skb = NULL;
- }
- clear_bit(FLG_ACTIVE, &bch->Flags);
- clear_bit(FLG_TX_BUSY, &bch->Flags);
+ mISDN_clear_bchannel(bch);
spin_unlock_irqrestore(&hw->lock, flags);
hfcsusb_setup_bch(bch, ISDN_P_NONE);
hfcsusb_stop_endpoint(hw, bch->nr);
diff --git a/drivers/isdn/hardware/mISDN/iohelper.h b/drivers/isdn/hardware/mISDN/iohelper.h
new file mode 100644
index 00000000000..b438981107a
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/iohelper.h
@@ -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_func)(void *hwp, u8 offset);
+typedef void (write_reg_func)(void *hwp, u8 offset, u8 value);
+typedef void (fifo_func)(void *hwp, u8 offset, u8 *datap, int size);
+
+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 hws *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
diff --git a/drivers/isdn/hardware/mISDN/ipac.h b/drivers/isdn/hardware/mISDN/ipac.h
new file mode 100644
index 00000000000..74a6ccf9065
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/ipac.h
@@ -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_func *read_reg;
+ write_reg_func *write_reg;
+ fifo_func *read_fifo;
+ fifo_func *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_func *read_reg;
+ write_reg_func *write_reg;
+ fifo_func *read_fifo;
+ fifo_func *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/isar.h b/drivers/isdn/hardware/mISDN/isar.h
new file mode 100644
index 00000000000..4a134acd44d
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/isar.h
@@ -0,0 +1,269 @@
+/*
+ *
+ * isar.h ISAR (Siemens PSB 7110) specific defines
+ *
+ * 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 isar_hw;
+
+struct isar_ch {
+ struct bchannel bch;
+ struct isar_hw *is;
+ struct timer_list ftimer;
+ u8 nr;
+ u8 dpath;
+ u8 mml;
+ u8 state;
+ u8 cmd;
+ u8 mod;
+ u8 newcmd;
+ u8 newmod;
+ u8 try_mod;
+ u8 conmsg[16];
+};
+
+struct isar_hw {
+ struct isar_ch ch[2];
+ void *hw;
+ spinlock_t *hwlock; /* lock HW acccess */
+ char *name;
+ struct module *owner;
+ read_reg_func *read_reg;
+ write_reg_func *write_reg;
+ fifo_func *read_fifo;
+ fifo_func *write_fifo;
+ int (*ctrl)(void *, u32, u_long);
+ void (*release)(struct isar_hw *);
+ int (*init)(struct isar_hw *);
+ int (*open)(struct isar_hw *, struct channel_req *);
+ int (*firmware)(struct isar_hw *, const u8 *, int);
+ unsigned long Flags;
+ int version;
+ u8 bstat;
+ u8 iis;
+ u8 cmsb;
+ u8 clsb;
+ u8 buf[256];
+ u8 log[256];
+};
+
+#define ISAR_IRQMSK 0x04
+#define ISAR_IRQSTA 0x04
+#define ISAR_IRQBIT 0x75
+#define ISAR_CTRL_H 0x61
+#define ISAR_CTRL_L 0x60
+#define ISAR_IIS 0x58
+#define ISAR_IIA 0x58
+#define ISAR_HIS 0x50
+#define ISAR_HIA 0x50
+#define ISAR_MBOX 0x4c
+#define ISAR_WADR 0x4a
+#define ISAR_RADR 0x48
+
+#define ISAR_HIS_VNR 0x14
+#define ISAR_HIS_DKEY 0x02
+#define ISAR_HIS_FIRM 0x1e
+#define ISAR_HIS_STDSP 0x08
+#define ISAR_HIS_DIAG 0x05
+#define ISAR_HIS_P0CFG 0x3c
+#define ISAR_HIS_P12CFG 0x24
+#define ISAR_HIS_SARTCFG 0x25
+#define ISAR_HIS_PUMPCFG 0x26
+#define ISAR_HIS_PUMPCTRL 0x2a
+#define ISAR_HIS_IOM2CFG 0x27
+#define ISAR_HIS_IOM2REQ 0x07
+#define ISAR_HIS_IOM2CTRL 0x2b
+#define ISAR_HIS_BSTREQ 0x0c
+#define ISAR_HIS_PSTREQ 0x0e
+#define ISAR_HIS_SDATA 0x20
+#define ISAR_HIS_DPS1 0x40
+#define ISAR_HIS_DPS2 0x80
+#define SET_DPS(x) ((x<<6) & 0xc0)
+
+#define ISAR_IIS_MSCMSD 0x3f
+#define ISAR_IIS_VNR 0x15
+#define ISAR_IIS_DKEY 0x03
+#define ISAR_IIS_FIRM 0x1f
+#define ISAR_IIS_STDSP 0x09
+#define ISAR_IIS_DIAG 0x25
+#define ISAR_IIS_GSTEV 0x00
+#define ISAR_IIS_BSTEV 0x28
+#define ISAR_IIS_BSTRSP 0x2c
+#define ISAR_IIS_PSTRSP 0x2e
+#define ISAR_IIS_PSTEV 0x2a
+#define ISAR_IIS_IOM2RSP 0x27
+#define ISAR_IIS_RDATA 0x20
+#define ISAR_IIS_INVMSG 0x3f
+
+#define ISAR_CTRL_SWVER 0x10
+#define ISAR_CTRL_STST 0x40
+
+#define ISAR_MSG_HWVER 0x20
+
+#define ISAR_DP1_USE 1
+#define ISAR_DP2_USE 2
+#define ISAR_RATE_REQ 3
+
+#define PMOD_DISABLE 0
+#define PMOD_FAX 1
+#define PMOD_DATAMODEM 2
+#define PMOD_HALFDUPLEX 3
+#define PMOD_V110 4
+#define PMOD_DTMF 5
+#define PMOD_DTMF_TRANS 6
+#define PMOD_BYPASS 7
+
+#define PCTRL_ORIG 0x80
+#define PV32P2_V23R 0x40
+#define PV32P2_V22A 0x20
+#define PV32P2_V22B 0x10
+#define PV32P2_V22C 0x08
+#define PV32P2_V21 0x02
+#define PV32P2_BEL 0x01
+
+/* LSB MSB in ISAR doc wrong !!! Arghhh */
+#define PV32P3_AMOD 0x80
+#define PV32P3_V32B 0x02
+#define PV32P3_V23B 0x01
+#define PV32P4_48 0x11
+#define PV32P5_48 0x05
+#define PV32P4_UT48 0x11
+#define PV32P5_UT48 0x0d
+#define PV32P4_96 0x11
+#define PV32P5_96 0x03
+#define PV32P4_UT96 0x11
+#define PV32P5_UT96 0x0f
+#define PV32P4_B96 0x91
+#define PV32P5_B96 0x0b
+#define PV32P4_UTB96 0xd1
+#define PV32P5_UTB96 0x0f
+#define PV32P4_120 0xb1
+#define PV32P5_120 0x09
+#define PV32P4_UT120 0xf1
+#define PV32P5_UT120 0x0f
+#define PV32P4_144 0x99
+#define PV32P5_144 0x09
+#define PV32P4_UT144 0xf9
+#define PV32P5_UT144 0x0f
+#define PV32P6_CTN 0x01
+#define PV32P6_ATN 0x02
+
+#define PFAXP2_CTN 0x01
+#define PFAXP2_ATN 0x04
+
+#define PSEV_10MS_TIMER 0x02
+#define PSEV_CON_ON 0x18
+#define PSEV_CON_OFF 0x19
+#define PSEV_V24_OFF 0x20
+#define PSEV_CTS_ON 0x21
+#define PSEV_CTS_OFF 0x22
+#define PSEV_DCD_ON 0x23
+#define PSEV_DCD_OFF 0x24
+#define PSEV_DSR_ON 0x25
+#define PSEV_DSR_OFF 0x26
+#define PSEV_REM_RET 0xcc
+#define PSEV_REM_REN 0xcd
+#define PSEV_GSTN_CLR 0xd4
+
+#define PSEV_RSP_READY 0xbc
+#define PSEV_LINE_TX_H 0xb3
+#define PSEV_LINE_TX_B 0xb2
+#define PSEV_LINE_RX_H 0xb1
+#define PSEV_LINE_RX_B 0xb0
+#define PSEV_RSP_CONN 0xb5
+#define PSEV_RSP_DISC 0xb7
+#define PSEV_RSP_FCERR 0xb9
+#define PSEV_RSP_SILDET 0xbe
+#define PSEV_RSP_SILOFF 0xab
+#define PSEV_FLAGS_DET 0xba
+
+#define PCTRL_CMD_TDTMF 0x5a
+
+#define PCTRL_CMD_FTH 0xa7
+#define PCTRL_CMD_FRH 0xa5
+#define PCTRL_CMD_FTM 0xa8
+#define PCTRL_CMD_FRM 0xa6
+#define PCTRL_CMD_SILON 0xac
+#define PCTRL_CMD_CONT 0xa2
+#define PCTRL_CMD_ESC 0xa4
+#define PCTRL_CMD_SILOFF 0xab
+#define PCTRL_CMD_HALT 0xa9
+
+#define PCTRL_LOC_RET 0xcf
+#define PCTRL_LOC_REN 0xce
+
+#define SMODE_DISABLE 0
+#define SMODE_V14 2
+#define SMODE_HDLC 3
+#define SMODE_BINARY 4
+#define SMODE_FSK_V14 5
+
+#define SCTRL_HDMC_BOTH 0x00
+#define SCTRL_HDMC_DTX 0x80
+#define SCTRL_HDMC_DRX 0x40
+#define S_P1_OVSP 0x40
+#define S_P1_SNP 0x20
+#define S_P1_EOP 0x10
+#define S_P1_EDP 0x08
+#define S_P1_NSB 0x04
+#define S_P1_CHS_8 0x03
+#define S_P1_CHS_7 0x02
+#define S_P1_CHS_6 0x01
+#define S_P1_CHS_5 0x00
+
+#define S_P2_BFT_DEF 0x10
+
+#define IOM_CTRL_ENA 0x80
+#define IOM_CTRL_NOPCM 0x00
+#define IOM_CTRL_ALAW 0x02
+#define IOM_CTRL_ULAW 0x04
+#define IOM_CTRL_RCV 0x01
+
+#define IOM_P1_TXD 0x10
+
+#define HDLC_FED 0x40
+#define HDLC_FSD 0x20
+#define HDLC_FST 0x20
+#define HDLC_ERROR 0x1c
+#define HDLC_ERR_FAD 0x10
+#define HDLC_ERR_RER 0x08
+#define HDLC_ERR_CER 0x04
+#define SART_NMD 0x01
+
+#define BSTAT_RDM0 0x1
+#define BSTAT_RDM1 0x2
+#define BSTAT_RDM2 0x4
+#define BSTAT_RDM3 0x8
+#define BSTEV_TBO 0x1f
+#define BSTEV_RBO 0x2f
+
+/* FAX State Machine */
+#define STFAX_NULL 0
+#define STFAX_READY 1
+#define STFAX_LINE 2
+#define STFAX_CONT 3
+#define STFAX_ACTIV 4
+#define STFAX_ESCAPE 5
+#define STFAX_SILDET 6
+
+extern u32 mISDNisar_init(struct isar_hw *, void *);
+extern void mISDNisar_irq(struct isar_hw *);
diff --git a/drivers/isdn/hardware/mISDN/mISDNinfineon.c b/drivers/isdn/hardware/mISDN/mISDNinfineon.c
new file mode 100644
index 00000000000..62441ba53b9
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/mISDNinfineon.c
@@ -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
index 00000000000..613ba043537
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/mISDNipac.c
@@ -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);
diff --git a/drivers/isdn/hardware/mISDN/mISDNisar.c b/drivers/isdn/hardware/mISDN/mISDNisar.c
new file mode 100644
index 00000000000..de352a17673
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/mISDNisar.c
@@ -0,0 +1,1726 @@
+/*
+ * mISDNisar.c ISAR (Siemens PSB 7110) specific functions
+ *
+ * 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.
+ *
+ */
+
+/* define this to enable static debug messages, if you kernel supports
+ * dynamic debugging, you should use debugfs for this
+ */
+/* #define DEBUG */
+
+#include <linux/delay.h>
+#include <linux/vmalloc.h>
+#include <linux/mISDNhw.h>
+#include "isar.h"
+
+#define ISAR_REV "2.1"
+
+MODULE_AUTHOR("Karsten Keil");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(ISAR_REV);
+
+#define DEBUG_HW_FIRMWARE_FIFO 0x10000
+
+static const u8 faxmodulation_s[] = "3,24,48,72,73,74,96,97,98,121,122,145,146";
+static const u8 faxmodulation[] = {3, 24, 48, 72, 73, 74, 96, 97, 98, 121,
+ 122, 145, 146};
+#define FAXMODCNT 13
+
+static void isar_setup(struct isar_hw *);
+
+static inline int
+waitforHIA(struct isar_hw *isar, int timeout)
+{
+ int t = timeout;
+ u8 val = isar->read_reg(isar->hw, ISAR_HIA);
+
+ while ((val & 1) && t) {
+ udelay(1);
+ t--;
+ val = isar->read_reg(isar->hw, ISAR_HIA);
+ }
+ pr_debug("%s: HIA after %dus\n", isar->name, timeout - t);
+ return timeout;
+}
+
+/*
+ * send msg to ISAR mailbox
+ * if msg is NULL use isar->buf
+ */
+static int
+send_mbox(struct isar_hw *isar, u8 his, u8 creg, u8 len, u8 *msg)
+{
+ if (!waitforHIA(isar, 1000))
+ return 0;
+ pr_debug("send_mbox(%02x,%02x,%d)\n", his, creg, len);
+ isar->write_reg(isar->hw, ISAR_CTRL_H, creg);
+ isar->write_reg(isar->hw, ISAR_CTRL_L, len);
+ isar->write_reg(isar->hw, ISAR_WADR, 0);
+ if (!msg)
+ msg = isar->buf;
+ if (msg && len) {
+ isar->write_fifo(isar->hw, ISAR_MBOX, msg, len);
+ if (isar->ch[0].bch.debug & DEBUG_HW_BFIFO) {
+ int l = 0;
+
+ while (l < (int)len) {
+ hex_dump_to_buffer(msg + l, len - l, 32, 1,
+ isar->log, 256, 1);
+ pr_debug("%s: %s %02x: %s\n", isar->name,
+ __func__, l, isar->log);
+ l += 32;
+ }
+ }
+ }
+ isar->write_reg(isar->hw, ISAR_HIS, his);
+ waitforHIA(isar, 1000);
+ return 1;
+}
+
+/*
+ * receive message from ISAR mailbox
+ * if msg is NULL use isar->buf
+ */
+static void
+rcv_mbox(struct isar_hw *isar, u8 *msg)
+{
+ if (!msg)
+ msg = isar->buf;
+ isar->write_reg(isar->hw, ISAR_RADR, 0);
+ if (msg && isar->clsb) {
+ isar->read_fifo(isar->hw, ISAR_MBOX, msg, isar->clsb);
+ if (isar->ch[0].bch.debug & DEBUG_HW_BFIFO) {
+ int l = 0;
+
+ while (l < (int)isar->clsb) {
+ hex_dump_to_buffer(msg + l, isar->clsb - l, 32,
+ 1, isar->log, 256, 1);
+ pr_debug("%s: %s %02x: %s\n", isar->name,
+ __func__, l, isar->log);
+ l += 32;
+ }
+ }
+ }
+ isar->write_reg(isar->hw, ISAR_IIA, 0);
+}
+
+static inline void
+get_irq_infos(struct isar_hw *isar)
+{
+ isar->iis = isar->read_reg(isar->hw, ISAR_IIS);
+ isar->cmsb = isar->read_reg(isar->hw, ISAR_CTRL_H);
+ isar->clsb = isar->read_reg(isar->hw, ISAR_CTRL_L);
+ pr_debug("%s: rcv_mbox(%02x,%02x,%d)\n", isar->name,
+ isar->iis, isar->cmsb, isar->clsb);
+}
+
+/*
+ * poll answer message from ISAR mailbox
+ * should be used only with ISAR IRQs disabled before DSP was started
+ *
+ */
+static int
+poll_mbox(struct isar_hw *isar, int maxdelay)
+{
+ int t = maxdelay;
+ u8 irq;
+
+ irq = isar->read_reg(isar->hw, ISAR_IRQBIT);
+ while (t && !(irq & ISAR_IRQSTA)) {
+ udelay(1);
+ t--;
+ }
+ if (t) {
+ get_irq_infos(isar);
+ rcv_mbox(isar, NULL);
+ }
+ pr_debug("%s: pulled %d bytes after %d us\n",
+ isar->name, isar->clsb, maxdelay - t);
+ return t;
+}
+
+static int
+ISARVersion(struct isar_hw *isar)
+{
+ int ver;
+
+ /* disable ISAR IRQ */
+ isar->write_reg(isar->hw, ISAR_IRQBIT, 0);
+ isar->buf[0] = ISAR_MSG_HWVER;
+ isar->buf[1] = 0;
+ isar->buf[2] = 1;
+ if (!send_mbox(isar, ISAR_HIS_VNR, 0, 3, NULL))
+ return -1;
+ if (!poll_mbox(isar, 1000))
+ return -2;
+ if (isar->iis == ISAR_IIS_VNR) {
+ if (isar->clsb == 1) {
+ ver = isar->buf[0] & 0xf;
+ return ver;
+ }
+ return -3;
+ }
+ return -4;
+}
+
+static int
+load_firmware(struct isar_hw *isar, const u8 *buf, int size)
+{
+ u32 saved_debug = isar->ch[0].bch.debug;
+ int ret, cnt;
+ u8 nom, noc;
+ u16 left, val, *sp = (u16 *)buf;
+ u8 *mp;
+ u_long flags;
+
+ struct {
+ u16 sadr;
+ u16 len;
+ u16 d_key;
+ } blk_head;
+
+ if (1 != isar->version) {
+ pr_err("%s: ISAR wrong version %d firmware download aborted\n",
+ isar->name, isar->version);
+ return -EINVAL;
+ }
+ if (!(saved_debug & DEBUG_HW_FIRMWARE_FIFO))
+ isar->ch[0].bch.debug &= ~DEBUG_HW_BFIFO;
+ pr_debug("%s: load firmware %d words (%d bytes)\n",
+ isar->name, size/2, size);
+ cnt = 0;
+ size /= 2;
+ /* disable ISAR IRQ */
+ spin_lock_irqsave(isar->hwlock, flags);
+ isar->write_reg(isar->hw, ISAR_IRQBIT, 0);
+ spin_unlock_irqrestore(isar->hwlock, flags);
+ while (cnt < size) {
+ blk_head.sadr = le16_to_cpu(*sp++);
+ blk_head.len = le16_to_cpu(*sp++);
+ blk_head.d_key = le16_to_cpu(*sp++);
+ cnt += 3;
+ pr_debug("ISAR firmware block (%#x,%d,%#x)\n",
+ blk_head.sadr, blk_head.len, blk_head.d_key & 0xff);
+ left = blk_head.len;
+ if (cnt + left > size) {
+ pr_info("%s: firmware error have %d need %d words\n",
+ isar->name, size, cnt + left);
+ ret = -EINVAL;
+ goto reterrflg;
+ }
+ spin_lock_irqsave(isar->hwlock, flags);
+ if (!send_mbox(isar, ISAR_HIS_DKEY, blk_head.d_key & 0xff,
+ 0, NULL)) {
+ pr_info("ISAR send_mbox dkey failed\n");
+ ret = -ETIME;
+ goto reterror;
+ }
+ if (!poll_mbox(isar, 1000)) {
+ pr_warning("ISAR poll_mbox dkey failed\n");
+ ret = -ETIME;
+ goto reterror;
+ }
+ spin_unlock_irqrestore(isar->hwlock, flags);
+ if ((isar->iis != ISAR_IIS_DKEY) || isar->cmsb || isar->clsb) {
+ pr_info("ISAR wrong dkey response (%x,%x,%x)\n",
+ isar->iis, isar->cmsb, isar->clsb);
+ ret = 1;
+ goto reterrflg;
+ }
+ while (left > 0) {
+ if (left > 126)
+ noc = 126;
+ else
+ noc = left;
+ nom = (2 * noc) + 3;
+ mp = isar->buf;
+ /* the ISAR is big endian */
+ *mp++ = blk_head.sadr >> 8;
+ *mp++ = blk_head.sadr & 0xFF;
+ left -= noc;
+ cnt += noc;
+ *mp++ = noc;
+ pr_debug("%s: load %3d words at %04x\n", isar->name,
+ noc, blk_head.sadr);
+ blk_head.sadr += noc;
+ while (noc) {
+ val = le16_to_cpu(*sp++);
+ *mp++ = val >> 8;
+ *mp++ = val & 0xFF;;
+ noc--;
+ }
+ spin_lock_irqsave(isar->hwlock, flags);
+ if (!send_mbox(isar, ISAR_HIS_FIRM, 0, nom, NULL)) {
+ pr_info("ISAR send_mbox prog failed\n");
+ ret = -ETIME;
+ goto reterror;
+ }
+ if (!poll_mbox(isar, 1000)) {
+ pr_info("ISAR poll_mbox prog failed\n");
+ ret = -ETIME;
+ goto reterror;
+ }
+ spin_unlock_irqrestore(isar->hwlock, flags);
+ if ((isar->iis != ISAR_IIS_FIRM) ||
+ isar->cmsb || isar->clsb) {
+ pr_info("ISAR wrong prog response (%x,%x,%x)\n",
+ isar->iis, isar->cmsb, isar->clsb);
+ ret = -EIO;
+ goto reterrflg;
+ }
+ }
+ pr_debug("%s: ISAR firmware block %d words loaded\n",
+ isar->name, blk_head.len);
+ }
+ isar->ch[0].bch.debug = saved_debug;
+ /* 10ms delay */
+ cnt = 10;
+ while (cnt--)
+ mdelay(1);
+ isar->buf[0] = 0xff;
+ isar->buf[1] = 0xfe;
+ isar->bstat = 0;
+ spin_lock_irqsave(isar->hwlock, flags);
+ if (!send_mbox(isar, ISAR_HIS_STDSP, 0, 2, NULL)) {
+ pr_info("ISAR send_mbox start dsp failed\n");
+ ret = -ETIME;
+ goto reterror;
+ }
+ if (!poll_mbox(isar, 1000)) {
+ pr_info("ISAR poll_mbox start dsp failed\n");
+ ret = -ETIME;
+ goto reterror;
+ }
+ if ((isar->iis != ISAR_IIS_STDSP) || isar->cmsb || isar->clsb) {
+ pr_info("ISAR wrong start dsp response (%x,%x,%x)\n",
+ isar->iis, isar->cmsb, isar->clsb);
+ ret = -EIO;
+ goto reterror;
+ } else
+ pr_debug("%s: ISAR start dsp success\n", isar->name);
+
+ /* NORMAL mode entered */
+ /* Enable IRQs of ISAR */
+ isar->write_reg(isar->hw, ISAR_IRQBIT, ISAR_IRQSTA);
+ spin_unlock_irqrestore(isar->hwlock, flags);
+ cnt = 1000; /* max 1s */
+ while ((!isar->bstat) && cnt) {
+ mdelay(1);
+ cnt--;
+ }
+ if (!cnt) {
+ pr_info("ISAR no general status event received\n");
+ ret = -ETIME;
+ goto reterrflg;
+ } else
+ pr_debug("%s: ISAR general status event %x\n",
+ isar->name, isar->bstat);
+ /* 10ms delay */
+ cnt = 10;
+ while (cnt--)
+ mdelay(1);
+ isar->iis = 0;
+ spin_lock_irqsave(isar->hwlock, flags);
+ if (!send_mbox(isar, ISAR_HIS_DIAG, ISAR_CTRL_STST, 0, NULL)) {
+ pr_info("ISAR send_mbox self tst failed\n");
+ ret = -ETIME;
+ goto reterror;
+ }
+ spin_unlock_irqrestore(isar->hwlock, flags);
+ cnt = 10000; /* max 100 ms */
+ while ((isar->iis != ISAR_IIS_DIAG) && cnt) {
+ udelay(10);
+ cnt--;
+ }
+ mdelay(1);
+ if (!cnt) {
+ pr_info("ISAR no self tst response\n");
+ ret = -ETIME;
+ goto reterrflg;
+ }
+ if ((isar->cmsb == ISAR_CTRL_STST) && (isar->clsb == 1)
+ && (isar->buf[0] == 0))
+ pr_debug("%s: ISAR selftest OK\n", isar->name);
+ else {
+ pr_info("ISAR selftest not OK %x/%x/%x\n",
+ isar->cmsb, isar->clsb, isar->buf[0]);
+ ret = -EIO;
+ goto reterrflg;
+ }
+ spin_lock_irqsave(isar->hwlock, flags);
+ isar->iis = 0;
+ if (!send_mbox(isar, ISAR_HIS_DIAG, ISAR_CTRL_SWVER, 0, NULL)) {
+ pr_info("ISAR RQST SVN failed\n");
+ ret = -ETIME;
+ goto reterror;
+ }
+ spin_unlock_irqrestore(isar->hwlock, flags);
+ cnt = 30000; /* max 300 ms */
+ while ((isar->iis != ISAR_IIS_DIAG) && cnt) {
+ udelay(10);
+ cnt--;
+ }
+ mdelay(1);
+ if (!cnt) {
+ pr_info("ISAR no SVN response\n");
+ ret = -ETIME;
+ goto reterrflg;
+ } else {
+ if ((isar->cmsb == ISAR_CTRL_SWVER) && (isar->clsb == 1)) {
+ pr_notice("%s: ISAR software version %#x\n",
+ isar->name, isar->buf[0]);
+ } else {
+ pr_info("%s: ISAR wrong swver response (%x,%x)"
+ " cnt(%d)\n", isar->name, isar->cmsb,
+ isar->clsb, cnt);
+ ret = -EIO;
+ goto reterrflg;
+ }
+ }
+ spin_lock_irqsave(isar->hwlock, flags);
+ isar_setup(isar);
+ spin_unlock_irqrestore(isar->hwlock, flags);
+ ret = 0;
+reterrflg:
+ spin_lock_irqsave(isar->hwlock, flags);
+reterror:
+ isar->ch[0].bch.debug = saved_debug;
+ if (ret)
+ /* disable ISAR IRQ */
+ isar->write_reg(isar->hw, ISAR_IRQBIT, 0);
+ spin_unlock_irqrestore(isar->hwlock, flags);
+ return ret;
+}
+
+static inline void
+deliver_status(struct isar_ch *ch, int status)
+{
+ pr_debug("%s: HL->LL FAXIND %x\n", ch->is->name, status);
+ _queue_data(&ch->bch.ch, PH_CONTROL_IND, status, 0, NULL, GFP_ATOMIC);
+}
+
+static inline void
+isar_rcv_frame(struct isar_ch *ch)
+{
+ u8 *ptr;
+
+ if (!ch->is->clsb) {
+ pr_debug("%s; ISAR zero len frame\n", ch->is->name);
+ ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+ return;
+ }
+ switch (ch->bch.state) {
+ case ISDN_P_NONE:
+ pr_debug("%s: ISAR protocol 0 spurious IIS_RDATA %x/%x/%x\n",
+ ch->is->name, ch->is->iis, ch->is->cmsb, ch->is->clsb);
+ ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+ break;
+ case ISDN_P_B_RAW:
+ case ISDN_P_B_L2DTMF:
+ case ISDN_P_B_MODEM_ASYNC:
+ if (!ch->bch.rx_skb) {
+ ch->bch.rx_skb = mI_alloc_skb(ch->bch.maxlen,
+ GFP_ATOMIC);
+ if (unlikely(!ch->bch.rx_skb)) {
+ pr_info("%s: B receive out of memory\n",
+ ch->is->name);
+ ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+ break;
+ }
+ }
+ rcv_mbox(ch->is, skb_put(ch->bch.rx_skb, ch->is->clsb));
+ recv_Bchannel(&ch->bch, 0);
+ break;
+ case ISDN_P_B_HDLC:
+ if (!ch->bch.rx_skb) {
+ ch->bch.rx_skb = mI_alloc_skb(ch->bch.maxlen,
+ GFP_ATOMIC);
+ if (unlikely(!ch->bch.rx_skb)) {
+ pr_info("%s: B receive out of memory\n",
+ ch->is->name);
+ ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+ break;
+ }
+ }
+ if ((ch->bch.rx_skb->len + ch->is->clsb) >
+ (ch->bch.maxlen + 2)) {
+ pr_debug("%s: incoming packet too large\n",
+ ch->is->name);
+ ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+ skb_trim(ch->bch.rx_skb, 0);
+ break;
+ }
+ if (ch->is->cmsb & HDLC_ERROR) {
+ pr_debug("%s: ISAR frame error %x len %d\n",
+ ch->is->name, ch->is->cmsb, ch->is->clsb);
+#ifdef ERROR_STATISTIC
+ if (ch->is->cmsb & HDLC_ERR_RER)
+ ch->bch.err_inv++;
+ if (ch->is->cmsb & HDLC_ERR_CER)
+ ch->bch.err_crc++;
+#endif
+ skb_trim(ch->bch.rx_skb, 0);
+ ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+ break;
+ }
+ if (ch->is->cmsb & HDLC_FSD)
+ skb_trim(ch->bch.rx_skb, 0);
+ ptr = skb_put(ch->bch.rx_skb, ch->is->clsb);
+ rcv_mbox(ch->is, ptr);
+ if (ch->is->cmsb & HDLC_FED) {
+ if (ch->bch.rx_skb->len < 3) { /* last 2 are the FCS */
+ pr_debug("%s: ISAR frame to short %d\n",
+ ch->is->name, ch->bch.rx_skb->len);
+ skb_trim(ch->bch.rx_skb, 0);
+ break;
+ }
+ skb_trim(ch->bch.rx_skb, ch->bch.rx_skb->len - 2);
+ recv_Bchannel(&ch->bch, 0);
+ }
+ break;
+ case ISDN_P_B_T30_FAX:
+ if (ch->state != STFAX_ACTIV) {
+ pr_debug("%s: isar_rcv_frame: not ACTIV\n",
+ ch->is->name);
+ ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+ if (ch->bch.rx_skb)
+ skb_trim(ch->bch.rx_skb, 0);
+ break;
+ }
+ if (!ch->bch.rx_skb) {
+ ch->bch.rx_skb = mI_alloc_skb(ch->bch.maxlen,
+ GFP_ATOMIC);
+ if (unlikely(!ch->bch.rx_skb)) {
+ pr_info("%s: B receive out of memory\n",
+ __func__);
+ ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+ break;
+ }
+ }
+ if (ch->cmd == PCTRL_CMD_FRM) {
+ rcv_mbox(ch->is, skb_put(ch->bch.rx_skb, ch->is->clsb));
+ pr_debug("%s: isar_rcv_frame: %d\n",
+ ch->is->name, ch->bch.rx_skb->len);
+ if (ch->is->cmsb & SART_NMD) { /* ABORT */
+ pr_debug("%s: isar_rcv_frame: no more data\n",
+ ch->is->name);
+ ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+ send_mbox(ch->is, SET_DPS(ch->dpath) |
+ ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC,
+ 0, NULL);
+ ch->state = STFAX_ESCAPE;
+ /* set_skb_flag(skb, DF_NOMOREDATA); */
+ }
+ recv_Bchannel(&ch->bch, 0);
+ if (ch->is->cmsb & SART_NMD)
+ deliver_status(ch, HW_MOD_NOCARR);
+ break;
+ }
+ if (ch->cmd != PCTRL_CMD_FRH) {
+ pr_debug("%s: isar_rcv_frame: unknown fax mode %x\n",
+ ch->is->name, ch->cmd);
+ ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+ if (ch->bch.rx_skb)
+ skb_trim(ch->bch.rx_skb, 0);
+ break;
+ }
+ /* PCTRL_CMD_FRH */
+ if ((ch->bch.rx_skb->len + ch->is->clsb) >
+ (ch->bch.maxlen + 2)) {
+ pr_info("%s: %s incoming packet too large\n",
+ ch->is->name, __func__);
+ ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+ skb_trim(ch->bch.rx_skb, 0);
+ break;
+ } else if (ch->is->cmsb & HDLC_ERROR) {
+ pr_info("%s: ISAR frame error %x len %d\n",
+ ch->is->name, ch->is->cmsb, ch->is->clsb);
+ skb_trim(ch->bch.rx_skb, 0);
+ ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+ break;
+ }
+ if (ch->is->cmsb & HDLC_FSD)
+ skb_trim(ch->bch.rx_skb, 0);
+ ptr = skb_put(ch->bch.rx_skb, ch->is->clsb);
+ rcv_mbox(ch->is, ptr);
+ if (ch->is->cmsb & HDLC_FED) {
+ if (ch->bch.rx_skb->len < 3) { /* last 2 are the FCS */
+ pr_info("%s: ISAR frame to short %d\n",
+ ch->is->name, ch->bch.rx_skb->len);
+ skb_trim(ch->bch.rx_skb, 0);
+ break;
+ }
+ skb_trim(ch->bch.rx_skb, ch->bch.rx_skb->len - 2);
+ recv_Bchannel(&ch->bch, 0);
+ }
+ if (ch->is->cmsb & SART_NMD) { /* ABORT */
+ pr_debug("%s: isar_rcv_frame: no more data\n",
+ ch->is->name);
+ ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+ if (ch->bch.rx_skb)
+ skb_trim(ch->bch.rx_skb, 0);
+ send_mbox(ch->is, SET_DPS(ch->dpath) |
+ ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC, 0, NULL);
+ ch->state = STFAX_ESCAPE;
+ deliver_status(ch, HW_MOD_NOCARR);
+ }
+ break;
+ default:
+ pr_info("isar_rcv_frame protocol (%x)error\n", ch->bch.state);
+ ch->is->write_reg(ch->is->hw, ISAR_IIA, 0);
+ break;
+ }
+}
+
+static void
+isar_fill_fifo(struct isar_ch *ch)
+{
+ int count;
+ u8 msb;
+ u8 *ptr;
+
+ pr_debug("%s: ch%d tx_skb %p tx_idx %d\n",
+ ch->is->name, ch->bch.nr, ch->bch.tx_skb, ch->bch.tx_idx);
+ if (!ch->bch.tx_skb)
+ return;
+ count = ch->bch.tx_skb->len - ch->bch.tx_idx;
+ if (count <= 0)
+ return;
+ if (!(ch->is->bstat &
+ (ch->dpath == 1 ? BSTAT_RDM1 : BSTAT_RDM2)))
+ return;
+ if (count > ch->mml) {
+ msb = 0;
+ count = ch->mml;
+ } else {
+ msb = HDLC_FED;
+ }
+ ptr = ch->bch.tx_skb->data + ch->bch.tx_idx;
+ if (!ch->bch.tx_idx) {
+ pr_debug("%s: frame start\n", ch->is->name);
+ if ((ch->bch.state == ISDN_P_B_T30_FAX) &&
+ (ch->cmd == PCTRL_CMD_FTH)) {
+ if (count > 1) {
+ if ((ptr[0] == 0xff) && (ptr[1] == 0x13)) {
+ /* last frame */
+ test_and_set_bit(FLG_LASTDATA,
+ &ch->bch.Flags);
+ pr_debug("%s: set LASTDATA\n",
+ ch->is->name);
+ if (msb == HDLC_FED)
+ test_and_set_bit(FLG_DLEETX,
+ &ch->bch.Flags);
+ }
+ }
+ }
+ msb |= HDLC_FST;
+ }
+ ch->bch.tx_idx += count;
+ switch (ch->bch.state) {
+ case ISDN_P_NONE:
+ pr_info("%s: wrong protocol 0\n", __func__);
+ break;
+ case ISDN_P_B_RAW:
+ case ISDN_P_B_L2DTMF:
+ case ISDN_P_B_MODEM_ASYNC:
+ send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA,
+ 0, count, ptr);
+ break;
+ case ISDN_P_B_HDLC:
+ send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA,
+ msb, count, ptr);
+ break;
+ case ISDN_P_B_T30_FAX:
+ if (ch->state != STFAX_ACTIV)
+ pr_debug("%s: not ACTIV\n", ch->is->name);
+ else if (ch->cmd == PCTRL_CMD_FTH)
+ send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA,
+ msb, count, ptr);
+ else if (ch->cmd == PCTRL_CMD_FTM)
+ send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA,
+ 0, count, ptr);
+ else
+ pr_debug("%s: not FTH/FTM\n", ch->is->name);
+ break;
+ default:
+ pr_info("%s: protocol(%x) error\n",
+ __func__, ch->bch.state);
+ break;
+ }
+}
+
+static inline struct isar_ch *
+sel_bch_isar(struct isar_hw *isar, u8 dpath)
+{
+ struct isar_ch *base = &isar->ch[0];
+
+ if ((!dpath) || (dpath > 2))
+ return NULL;
+ if (base->dpath == dpath)
+ return base;
+ base++;
+ if (base->dpath == dpath)
+ return base;
+ return NULL;
+}
+
+static void
+send_next(struct isar_ch *ch)
+{
+ pr_debug("%s: %s ch%d tx_skb %p tx_idx %d\n",
+ ch->is->name, __func__, ch->bch.nr,
+ ch->bch.tx_skb, ch->bch.tx_idx);
+ if (ch->bch.state == ISDN_P_B_T30_FAX) {
+ if (ch->cmd == PCTRL_CMD_FTH) {
+ if (test_bit(FLG_LASTDATA, &ch->bch.Flags)) {
+ pr_debug("set NMD_DATA\n");
+ test_and_set_bit(FLG_NMD_DATA, &ch->bch.Flags);
+ }
+ } else if (ch->cmd == PCTRL_CMD_FTM) {
+ if (test_bit(FLG_DLEETX, &ch->bch.Flags)) {
+ test_and_set_bit(FLG_LASTDATA, &ch->bch.Flags);
+ test_and_set_bit(FLG_NMD_DATA, &ch->bch.Flags);
+ }
+ }
+ }
+ if (ch->bch.tx_skb) {
+ /* send confirm, on trans, free on hdlc. */
+ if (test_bit(FLG_TRANSPARENT, &ch->bch.Flags))
+ confirm_Bsend(&ch->bch);
+ dev_kfree_skb(ch->bch.tx_skb);
+ }
+ if (get_next_bframe(&ch->bch))
+ isar_fill_fifo(ch);
+ else {
+ if (test_and_clear_bit(FLG_DLEETX, &ch->bch.Flags)) {
+ if (test_and_clear_bit(FLG_LASTDATA,
+ &ch->bch.Flags)) {
+ if (test_and_clear_bit(FLG_NMD_DATA,
+ &ch->bch.Flags)) {
+ u8 zd = 0;
+ send_mbox(ch->is, SET_DPS(ch->dpath) |
+ ISAR_HIS_SDATA, 0x01, 1, &zd);
+ }
+ test_and_set_bit(FLG_LL_OK, &ch->bch.Flags);
+ } else {
+ deliver_status(ch, HW_MOD_CONNECT);
+ }
+ }
+ }
+}
+
+static void
+check_send(struct isar_hw *isar, u8 rdm)
+{
+ struct isar_ch *ch;
+
+ pr_debug("%s: rdm %x\n", isar->name, rdm);
+ if (rdm & BSTAT_RDM1) {
+ ch = sel_bch_isar(isar, 1);
+ if (ch && test_bit(FLG_ACTIVE, &ch->bch.Flags)) {
+ if (ch->bch.tx_skb && (ch->bch.tx_skb->len >
+ ch->bch.tx_idx))
+ isar_fill_fifo(ch);
+ else
+ send_next(ch);
+ }
+ }
+ if (rdm & BSTAT_RDM2) {
+ ch = sel_bch_isar(isar, 2);
+ if (ch && test_bit(FLG_ACTIVE, &ch->bch.Flags)) {
+ if (ch->bch.tx_skb && (ch->bch.tx_skb->len >
+ ch->bch.tx_idx))
+ isar_fill_fifo(ch);
+ else
+ send_next(ch);
+ }
+ }
+}
+
+const char *dmril[] = {"NO SPEED", "1200/75", "NODEF2", "75/1200", "NODEF4",
+ "300", "600", "1200", "2400", "4800", "7200",
+ "9600nt", "9600t", "12000", "14400", "WRONG"};
+const char *dmrim[] = {"NO MOD", "NO DEF", "V32/V32b", "V22", "V21",
+ "Bell103", "V23", "Bell202", "V17", "V29", "V27ter"};
+
+static void
+isar_pump_status_rsp(struct isar_ch *ch) {
+ u8 ril = ch->is->buf[0];
+ u8 rim;
+
+ if (!test_and_clear_bit(ISAR_RATE_REQ, &ch->is->Flags))
+ return;
+ if (ril > 14) {
+ pr_info("%s: wrong pstrsp ril=%d\n", ch->is->name, ril);
+ ril = 15;
+ }
+ switch (ch->is->buf[1]) {
+ case 0:
+ rim = 0;
+ break;
+ case 0x20:
+ rim = 2;
+ break;
+ case 0x40:
+ rim = 3;
+ break;
+ case 0x41:
+ rim = 4;
+ break;
+ case 0x51:
+ rim = 5;
+ break;
+ case 0x61:
+ rim = 6;
+ break;
+ case 0x71:
+ rim = 7;
+ break;
+ case 0x82:
+ rim = 8;
+ break;
+ case 0x92:
+ rim = 9;
+ break;
+ case 0xa2:
+ rim = 10;
+ break;
+ default:
+ rim = 1;
+ break;
+ }
+ sprintf(ch->conmsg, "%s %s", dmril[ril], dmrim[rim]);
+ pr_debug("%s: pump strsp %s\n", ch->is->name, ch->conmsg);
+}
+
+static void
+isar_pump_statev_modem(struct isar_ch *ch, u8 devt) {
+ u8 dps = SET_DPS(ch->dpath);
+
+ switch (devt) {
+ case PSEV_10MS_TIMER:
+ pr_debug("%s: pump stev TIMER\n", ch->is->name);
+ break;
+ case PSEV_CON_ON:
+ pr_debug("%s: pump stev CONNECT\n", ch->is->name);
+ deliver_status(ch, HW_MOD_CONNECT);
+ break;
+ case PSEV_CON_OFF:
+ pr_debug("%s: pump stev NO CONNECT\n", ch->is->name);
+ send_mbox(ch->is, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
+ deliver_status(ch, HW_MOD_NOCARR);
+ break;
+ case PSEV_V24_OFF:
+ pr_debug("%s: pump stev V24 OFF\n", ch->is->name);
+ break;
+ case PSEV_CTS_ON:
+ pr_debug("%s: pump stev CTS ON\n", ch->is->name);
+ break;
+ case PSEV_CTS_OFF:
+ pr_debug("%s pump stev CTS OFF\n", ch->is->name);
+ break;
+ case PSEV_DCD_ON:
+ pr_debug("%s: pump stev CARRIER ON\n", ch->is->name);
+ test_and_set_bit(ISAR_RATE_REQ, &ch->is->Flags);
+ send_mbox(ch->is, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
+ break;
+ case PSEV_DCD_OFF:
+ pr_debug("%s: pump stev CARRIER OFF\n", ch->is->name);
+ break;
+ case PSEV_DSR_ON:
+ pr_debug("%s: pump stev DSR ON\n", ch->is->name);
+ break;
+ case PSEV_DSR_OFF:
+ pr_debug("%s: pump stev DSR_OFF\n", ch->is->name);
+ break;
+ case PSEV_REM_RET:
+ pr_debug("%s: pump stev REMOTE RETRAIN\n", ch->is->name);
+ break;
+ case PSEV_REM_REN:
+ pr_debug("%s: pump stev REMOTE RENEGOTIATE\n", ch->is->name);
+ break;
+ case PSEV_GSTN_CLR:
+ pr_debug("%s: pump stev GSTN CLEAR\n", ch->is->name);
+ break;
+ default:
+ pr_info("u%s: nknown pump stev %x\n", ch->is->name, devt);
+ break;
+ }
+}
+
+static void
+isar_pump_statev_fax(struct isar_ch *ch, u8 devt) {
+ u8 dps = SET_DPS(ch->dpath);
+ u8 p1;
+
+ switch (devt) {
+ case PSEV_10MS_TIMER:
+ pr_debug("%s: pump stev TIMER\n", ch->is->name);
+ break;
+ case PSEV_RSP_READY:
+ pr_debug("%s: pump stev RSP_READY\n", ch->is->name);
+ ch->state = STFAX_READY;
+ deliver_status(ch, HW_MOD_READY);
+#ifdef AUTOCON
+ if (test_bit(BC_FLG_ORIG, &ch->bch.Flags))
+ isar_pump_cmd(bch, HW_MOD_FRH, 3);
+ else
+ isar_pump_cmd(bch, HW_MOD_FTH, 3);
+#endif
+ break;
+ case PSEV_LINE_TX_H:
+ if (ch->state == STFAX_LINE) {
+ pr_debug("%s: pump stev LINE_TX_H\n", ch->is->name);
+ ch->state = STFAX_CONT;
+ send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL,
+ PCTRL_CMD_CONT, 0, NULL);
+ } else {
+ pr_debug("%s: pump stev LINE_TX_H wrong st %x\n",
+ ch->is->name, ch->state);
+ }
+ break;
+ case PSEV_LINE_RX_H:
+ if (ch->state == STFAX_LINE) {
+ pr_debug("%s: pump stev LINE_RX_H\n", ch->is->name);
+ ch->state = STFAX_CONT;
+ send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL,
+ PCTRL_CMD_CONT, 0, NULL);
+ } else {
+ pr_debug("%s: pump stev LINE_RX_H wrong st %x\n",
+ ch->is->name, ch->state);
+ }
+ break;
+ case PSEV_LINE_TX_B:
+ if (ch->state == STFAX_LINE) {
+ pr_debug("%s: pump stev LINE_TX_B\n", ch->is->name);
+ ch->state = STFAX_CONT;
+ send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL,
+ PCTRL_CMD_CONT, 0, NULL);
+ } else {
+ pr_debug("%s: pump stev LINE_TX_B wrong st %x\n",
+ ch->is->name, ch->state);
+ }
+ break;
+ case PSEV_LINE_RX_B:
+ if (ch->state == STFAX_LINE) {
+ pr_debug("%s: pump stev LINE_RX_B\n", ch->is->name);
+ ch->state = STFAX_CONT;
+ send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL,
+ PCTRL_CMD_CONT, 0, NULL);
+ } else {
+ pr_debug("%s: pump stev LINE_RX_B wrong st %x\n",
+ ch->is->name, ch->state);
+ }
+ break;
+ case PSEV_RSP_CONN:
+ if (ch->state == STFAX_CONT) {
+ pr_debug("%s: pump stev RSP_CONN\n", ch->is->name);
+ ch->state = STFAX_ACTIV;
+ test_and_set_bit(ISAR_RATE_REQ, &ch->is->Flags);
+ send_mbox(ch->is, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
+ if (ch->cmd == PCTRL_CMD_FTH) {
+ int delay = (ch->mod == 3) ? 1000 : 200;
+ /* 1s (200 ms) Flags before data */
+ if (test_and_set_bit(FLG_FTI_RUN,
+ &ch->bch.Flags))
+ del_timer(&ch->ftimer);
+ ch->ftimer.expires =
+ jiffies + ((delay * HZ)/1000);
+ test_and_set_bit(FLG_LL_CONN,
+ &ch->bch.Flags);
+ add_timer(&ch->ftimer);
+ } else {
+ deliver_status(ch, HW_MOD_CONNECT);
+ }
+ } else {
+ pr_debug("%s: pump stev RSP_CONN wrong st %x\n",
+ ch->is->name, ch->state);
+ }
+ break;
+ case PSEV_FLAGS_DET:
+ pr_debug("%s: pump stev FLAGS_DET\n", ch->is->name);
+ break;
+ case PSEV_RSP_DISC:
+ pr_debug("%s: pump stev RSP_DISC state(%d)\n",
+ ch->is->name, ch->state);
+ if (ch->state == STFAX_ESCAPE) {
+ p1 = 5;
+ switch (ch->newcmd) {
+ case 0:
+ ch->state = STFAX_READY;
+ break;
+ case PCTRL_CMD_FTM:
+ p1 = 2;
+ case PCTRL_CMD_FTH:
+ send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL,
+ PCTRL_CMD_SILON, 1, &p1);
+ ch->state = STFAX_SILDET;
+ break;
+ case PCTRL_CMD_FRH:
+ case PCTRL_CMD_FRM:
+ ch->mod = ch->newmod;
+ p1 = ch->newmod;
+ ch->newmod = 0;
+ ch->cmd = ch->newcmd;
+ ch->newcmd = 0;
+ send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL,
+ ch->cmd, 1, &p1);
+ ch->state = STFAX_LINE;
+ ch->try_mod = 3;
+ break;
+ default:
+ pr_debug("%s: RSP_DISC unknown newcmd %x\n",
+ ch->is->name, ch->newcmd);
+ break;
+ }
+ } else if (ch->state == STFAX_ACTIV) {
+ if (test_and_clear_bit(FLG_LL_OK, &ch->bch.Flags))
+ deliver_status(ch, HW_MOD_OK);
+ else if (ch->cmd == PCTRL_CMD_FRM)
+ deliver_status(ch, HW_MOD_NOCARR);
+ else
+ deliver_status(ch, HW_MOD_FCERROR);
+ ch->state = STFAX_READY;
+ } else if (ch->state != STFAX_SILDET) {
+ /* ignore in STFAX_SILDET */
+ ch->state = STFAX_READY;
+ deliver_status(ch, HW_MOD_FCERROR);
+ }
+ break;
+ case PSEV_RSP_SILDET:
+ pr_debug("%s: pump stev RSP_SILDET\n", ch->is->name);
+ if (ch->state == STFAX_SILDET) {
+ ch->mod = ch->newmod;
+ p1 = ch->newmod;
+ ch->newmod = 0;
+ ch->cmd = ch->newcmd;
+ ch->newcmd = 0;
+ send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL,
+ ch->cmd, 1, &p1);
+ ch->state = STFAX_LINE;
+ ch->try_mod = 3;
+ }
+ break;
+ case PSEV_RSP_SILOFF:
+ pr_debug("%s: pump stev RSP_SILOFF\n", ch->is->name);
+ break;
+ case PSEV_RSP_FCERR:
+ if (ch->state == STFAX_LINE) {
+ pr_debug("%s: pump stev RSP_FCERR try %d\n",
+ ch->is->name, ch->try_mod);
+ if (ch->try_mod--) {
+ send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL,
+ ch->cmd, 1, &ch->mod);
+ break;
+ }
+ }
+ pr_debug("%s: pump stev RSP_FCERR\n", ch->is->name);
+ ch->state = STFAX_ESCAPE;
+ send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, PCTRL_CMD_ESC,
+ 0, NULL);
+ deliver_status(ch, HW_MOD_FCERROR);
+ break;
+ default:
+ break;
+ }
+}
+
+void
+mISDNisar_irq(struct isar_hw *isar)
+{
+ struct isar_ch *ch;
+
+ get_irq_infos(isar);
+ switch (isar->iis & ISAR_IIS_MSCMSD) {
+ case ISAR_IIS_RDATA:
+ ch = sel_bch_isar(isar, isar->iis >> 6);
+ if (ch)
+ isar_rcv_frame(ch);
+ else {
+ pr_debug("%s: ISAR spurious IIS_RDATA %x/%x/%x\n",
+ isar->name, isar->iis, isar->cmsb,
+ isar->clsb);
+ isar->write_reg(isar->hw, ISAR_IIA, 0);
+ }
+ break;
+ case ISAR_IIS_GSTEV:
+ isar->write_reg(isar->hw, ISAR_IIA, 0);
+ isar->bstat |= isar->cmsb;
+ check_send(isar, isar->cmsb);
+ break;
+ case ISAR_IIS_BSTEV:
+#ifdef ERROR_STATISTIC
+ ch = sel_bch_isar(isar, isar->iis >> 6);
+ if (ch) {
+ if (isar->cmsb == BSTEV_TBO)
+ ch->bch.err_tx++;
+ if (isar->cmsb == BSTEV_RBO)
+ ch->bch.err_rdo++;
+ }
+#endif
+ pr_debug("%s: Buffer STEV dpath%d msb(%x)\n",
+ isar->name, isar->iis>>6, isar->cmsb);
+ isar->write_reg(isar->hw, ISAR_IIA, 0);
+ break;
+ case ISAR_IIS_PSTEV:
+ ch = sel_bch_isar(isar, isar->iis >> 6);
+ if (ch) {
+ rcv_mbox(isar, NULL);
+ if (ch->bch.state == ISDN_P_B_MODEM_ASYNC)
+ isar_pump_statev_modem(ch, isar->cmsb);
+ else if (ch->bch.state == ISDN_P_B_T30_FAX)
+ isar_pump_statev_fax(ch, isar->cmsb);
+ else if (ch->bch.state == ISDN_P_B_RAW) {
+ int tt;
+ tt = isar->cmsb | 0x30;
+ if (tt == 0x3e)
+ tt = '*';
+ else if (tt == 0x3f)
+ tt = '#';
+ else if (tt > '9')
+ tt += 7;
+ tt |= DTMF_TONE_VAL;
+ _queue_data(&ch->bch.ch, PH_CONTROL_IND,
+ MISDN_ID_ANY, sizeof(tt), &tt,
+ GFP_ATOMIC);
+ } else
+ pr_debug("%s: ISAR IIS_PSTEV pm %d sta %x\n",
+ isar->name, ch->bch.state,
+ isar->cmsb);
+ } else {
+ pr_debug("%s: ISAR spurious IIS_PSTEV %x/%x/%x\n",
+ isar->name, isar->iis, isar->cmsb,
+ isar->clsb);
+ isar->write_reg(isar->hw, ISAR_IIA, 0);
+ }
+ break;
+ case ISAR_IIS_PSTRSP:
+ ch = sel_bch_isar(isar, isar->iis >> 6);
+ if (ch) {
+ rcv_mbox(isar, NULL);
+ isar_pump_status_rsp(ch);
+ } else {
+ pr_debug("%s: ISAR spurious IIS_PSTRSP %x/%x/%x\n",
+ isar->name, isar->iis, isar->cmsb,
+ isar->clsb);
+ isar->write_reg(isar->hw, ISAR_IIA, 0);
+ }
+ break;
+ case ISAR_IIS_DIAG:
+ case ISAR_IIS_BSTRSP:
+ case ISAR_IIS_IOM2RSP:
+ rcv_mbox(isar, NULL);
+ break;
+ case ISAR_IIS_INVMSG:
+ rcv_mbox(isar, NULL);
+ pr_debug("%s: invalid msg his:%x\n", isar->name, isar->cmsb);
+ break;
+ default:
+ rcv_mbox(isar, NULL);
+ pr_debug("%s: unhandled msg iis(%x) ctrl(%x/%x)\n",
+ isar->name, isar->iis, isar->cmsb, isar->clsb);
+ break;
+ }
+}
+EXPORT_SYMBOL(mISDNisar_irq);
+
+static void
+ftimer_handler(unsigned long data)
+{
+ struct isar_ch *ch = (struct isar_ch *)data;
+
+ pr_debug("%s: ftimer flags %lx\n", ch->is->name, ch->bch.Flags);
+ test_and_clear_bit(FLG_FTI_RUN, &ch->bch.Flags);
+ if (test_and_clear_bit(FLG_LL_CONN, &ch->bch.Flags))
+ deliver_status(ch, HW_MOD_CONNECT);
+}
+
+static void
+setup_pump(struct isar_ch *ch) {
+ u8 dps = SET_DPS(ch->dpath);
+ u8 ctrl, param[6];
+
+ switch (ch->bch.state) {
+ case ISDN_P_NONE:
+ case ISDN_P_B_RAW:
+ case ISDN_P_B_HDLC:
+ send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG, PMOD_BYPASS, 0, NULL);
+ break;
+ case ISDN_P_B_L2DTMF:
+ if (test_bit(FLG_DTMFSEND, &ch->bch.Flags)) {
+ param[0] = 5; /* TOA 5 db */
+ send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG,
+ PMOD_DTMF_TRANS, 1, param);
+ } else {
+ param[0] = 40; /* REL -46 dbm */
+ send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG,
+ PMOD_DTMF, 1, param);
+ }
+ case ISDN_P_B_MODEM_ASYNC:
+ ctrl = PMOD_DATAMODEM;
+ if (test_bit(FLG_ORIGIN, &ch->bch.Flags)) {
+ ctrl |= PCTRL_ORIG;
+ param[5] = PV32P6_CTN;
+ } else {
+ param[5] = PV32P6_ATN;
+ }
+ param[0] = 6; /* 6 db */
+ param[1] = PV32P2_V23R | PV32P2_V22A | PV32P2_V22B |
+ PV32P2_V22C | PV32P2_V21 | PV32P2_BEL;
+ param[2] = PV32P3_AMOD | PV32P3_V32B | PV32P3_V23B;
+ param[3] = PV32P4_UT144;
+ param[4] = PV32P5_UT144;
+ send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG, ctrl, 6, param);
+ break;
+ case ISDN_P_B_T30_FAX:
+ ctrl = PMOD_FAX;
+ if (test_bit(FLG_ORIGIN, &ch->bch.Flags)) {
+ ctrl |= PCTRL_ORIG;
+ param[1] = PFAXP2_CTN;
+ } else {
+ param[1] = PFAXP2_ATN;
+ }
+ param[0] = 6; /* 6 db */
+ send_mbox(ch->is, dps | ISAR_HIS_PUMPCFG, ctrl, 2, param);
+ ch->state = STFAX_NULL;
+ ch->newcmd = 0;
+ ch->newmod = 0;
+ test_and_set_bit(FLG_FTI_RUN, &ch->bch.Flags);
+ break;
+ }
+ udelay(1000);
+ send_mbox(ch->is, dps | ISAR_HIS_PSTREQ, 0, 0, NULL);
+ udelay(1000);
+}
+
+static void
+setup_sart(struct isar_ch *ch) {
+ u8 dps = SET_DPS(ch->dpath);
+ u8 ctrl, param[2] = {0, 0};
+
+ switch (ch->bch.state) {
+ case ISDN_P_NONE:
+ send_mbox(ch->is, dps | ISAR_HIS_SARTCFG, SMODE_DISABLE,
+ 0, NULL);
+ break;
+ case ISDN_P_B_RAW:
+ case ISDN_P_B_L2DTMF:
+ send_mbox(ch->is, dps | ISAR_HIS_SARTCFG, SMODE_BINARY,
+ 2, param);
+ break;
+ case ISDN_P_B_HDLC:
+ case ISDN_P_B_T30_FAX:
+ send_mbox(ch->is, dps | ISAR_HIS_SARTCFG, SMODE_HDLC,
+ 1, param);
+ break;
+ case ISDN_P_B_MODEM_ASYNC:
+ ctrl = SMODE_V14 | SCTRL_HDMC_BOTH;
+ param[0] = S_P1_CHS_8;
+ param[1] = S_P2_BFT_DEF;
+ send_mbox(ch->is, dps | ISAR_HIS_SARTCFG, ctrl, 2, param);
+ break;
+ }
+ udelay(1000);
+ send_mbox(ch->is, dps | ISAR_HIS_BSTREQ, 0, 0, NULL);
+ udelay(1000);
+}
+
+static void
+setup_iom2(struct isar_ch *ch) {
+ u8 dps = SET_DPS(ch->dpath);
+ u8 cmsb = IOM_CTRL_ENA, msg[5] = {IOM_P1_TXD, 0, 0, 0, 0};
+
+ if (ch->bch.nr == 2) {
+ msg[1] = 1;
+ msg[3] = 1;
+ }
+ switch (ch->bch.state) {
+ case ISDN_P_NONE:
+ cmsb = 0;
+ /* dummy slot */
+ msg[1] = ch->dpath + 2;
+ msg[3] = ch->dpath + 2;
+ break;
+ case ISDN_P_B_RAW:
+ case ISDN_P_B_HDLC:
+ break;
+ case ISDN_P_B_MODEM_ASYNC:
+ case ISDN_P_B_T30_FAX:
+ cmsb |= IOM_CTRL_RCV;
+ case ISDN_P_B_L2DTMF:
+ if (test_bit(FLG_DTMFSEND, &ch->bch.Flags))
+ cmsb |= IOM_CTRL_RCV;
+ cmsb |= IOM_CTRL_ALAW;
+ break;
+ }
+ send_mbox(ch->is, dps | ISAR_HIS_IOM2CFG, cmsb, 5, msg);
+ udelay(1000);
+ send_mbox(ch->is, dps | ISAR_HIS_IOM2REQ, 0, 0, NULL);
+ udelay(1000);
+}
+
+static int
+modeisar(struct isar_ch *ch, u32 bprotocol)
+{
+ /* Here we are selecting the best datapath for requested protocol */
+ if (ch->bch.state == ISDN_P_NONE) { /* New Setup */
+ switch (bprotocol) {
+ case ISDN_P_NONE: /* init */
+ if (!ch->dpath)
+ /* no init for dpath 0 */
+ return 0;
+ test_and_clear_bit(FLG_HDLC, &ch->bch.Flags);
+ test_and_clear_bit(FLG_TRANSPARENT, &ch->bch.Flags);
+ break;
+ case ISDN_P_B_RAW:
+ case ISDN_P_B_HDLC:
+ /* best is datapath 2 */
+ if (!test_and_set_bit(ISAR_DP2_USE, &ch->is->Flags))
+ ch->dpath = 2;
+ else if (!test_and_set_bit(ISAR_DP1_USE,
+ &ch->is->Flags))
+ ch->dpath = 1;
+ else {
+ pr_info("modeisar both pathes in use\n");
+ return -EBUSY;
+ }
+ if (bprotocol == ISDN_P_B_HDLC)
+ test_and_set_bit(FLG_HDLC, &ch->bch.Flags);
+ else
+ test_and_set_bit(FLG_TRANSPARENT,
+ &ch->bch.Flags);
+ break;
+ case ISDN_P_B_MODEM_ASYNC:
+ case ISDN_P_B_T30_FAX:
+ case ISDN_P_B_L2DTMF:
+ /* only datapath 1 */
+ if (!test_and_set_bit(ISAR_DP1_USE, &ch->is->Flags))
+ ch->dpath = 1;
+ else {
+ pr_info("%s: ISAR modeisar analog functions"
+ "only with DP1\n", ch->is->name);
+ return -EBUSY;
+ }
+ break;
+ default:
+ pr_info("%s: protocol not known %x\n", ch->is->name,
+ bprotocol);
+ return -ENOPROTOOPT;
+ }
+ }
+ pr_debug("%s: ISAR ch%d dp%d protocol %x->%x\n", ch->is->name,
+ ch->bch.nr, ch->dpath, ch->bch.state, bprotocol);
+ ch->bch.state = bprotocol;
+ setup_pump(ch);
+ setup_iom2(ch);
+ setup_sart(ch);
+ if (ch->bch.state == ISDN_P_NONE) {
+ /* Clear resources */
+ if (ch->dpath == 1)
+ test_and_clear_bit(ISAR_DP1_USE, &ch->is->Flags);
+ else if (ch->dpath == 2)
+ test_and_clear_bit(ISAR_DP2_USE, &ch->is->Flags);
+ ch->dpath = 0;
+ ch->is->ctrl(ch->is->hw, HW_DEACT_IND, ch->bch.nr);
+ } else
+ ch->is->ctrl(ch->is->hw, HW_ACTIVATE_IND, ch->bch.nr);
+ return 0;
+}
+
+static void
+isar_pump_cmd(struct isar_ch *ch, u32 cmd, u8 para)
+{
+ u8 dps = SET_DPS(ch->dpath);
+ u8 ctrl = 0, nom = 0, p1 = 0;
+
+ pr_debug("%s: isar_pump_cmd %x/%x state(%x)\n",
+ ch->is->name, cmd, para, ch->bch.state);
+ switch (cmd) {
+ case HW_MOD_FTM:
+ if (ch->state == STFAX_READY) {
+ p1 = para;
+ ctrl = PCTRL_CMD_FTM;
+ nom = 1;
+ ch->state = STFAX_LINE;
+ ch->cmd = ctrl;
+ ch->mod = para;
+ ch->newmod = 0;
+ ch->newcmd = 0;
+ ch->try_mod = 3;
+ } else if ((ch->state == STFAX_ACTIV) &&
+ (ch->cmd == PCTRL_CMD_FTM) && (ch->mod == para))
+ deliver_status(ch, HW_MOD_CONNECT);
+ else {
+ ch->newmod = para;
+ ch->newcmd = PCTRL_CMD_FTM;
+ nom = 0;
+ ctrl = PCTRL_CMD_ESC;
+ ch->state = STFAX_ESCAPE;
+ }
+ break;
+ case HW_MOD_FTH:
+ if (ch->state == STFAX_READY) {
+ p1 = para;
+ ctrl = PCTRL_CMD_FTH;
+ nom = 1;
+ ch->state = STFAX_LINE;
+ ch->cmd = ctrl;
+ ch->mod = para;
+ ch->newmod = 0;
+ ch->newcmd = 0;
+ ch->try_mod = 3;
+ } else if ((ch->state == STFAX_ACTIV) &&
+ (ch->cmd == PCTRL_CMD_FTH) && (ch->mod == para))
+ deliver_status(ch, HW_MOD_CONNECT);
+ else {
+ ch->newmod = para;
+ ch->newcmd = PCTRL_CMD_FTH;
+ nom = 0;
+ ctrl = PCTRL_CMD_ESC;
+ ch->state = STFAX_ESCAPE;
+ }
+ break;
+ case HW_MOD_FRM:
+ if (ch->state == STFAX_READY) {
+ p1 = para;
+ ctrl = PCTRL_CMD_FRM;
+ nom = 1;
+ ch->state = STFAX_LINE;
+ ch->cmd = ctrl;
+ ch->mod = para;
+ ch->newmod = 0;
+ ch->newcmd = 0;
+ ch->try_mod = 3;
+ } else if ((ch->state == STFAX_ACTIV) &&
+ (ch->cmd == PCTRL_CMD_FRM) && (ch->mod == para))
+ deliver_status(ch, HW_MOD_CONNECT);
+ else {
+ ch->newmod = para;
+ ch->newcmd = PCTRL_CMD_FRM;
+ nom = 0;
+ ctrl = PCTRL_CMD_ESC;
+ ch->state = STFAX_ESCAPE;
+ }
+ break;
+ case HW_MOD_FRH:
+ if (ch->state == STFAX_READY) {
+ p1 = para;
+ ctrl = PCTRL_CMD_FRH;
+ nom = 1;
+ ch->state = STFAX_LINE;
+ ch->cmd = ctrl;
+ ch->mod = para;
+ ch->newmod = 0;
+ ch->newcmd = 0;
+ ch->try_mod = 3;
+ } else if ((ch->state == STFAX_ACTIV) &&
+ (ch->cmd == PCTRL_CMD_FRH) && (ch->mod == para))
+ deliver_status(ch, HW_MOD_CONNECT);
+ else {
+ ch->newmod = para;
+ ch->newcmd = PCTRL_CMD_FRH;
+ nom = 0;
+ ctrl = PCTRL_CMD_ESC;
+ ch->state = STFAX_ESCAPE;
+ }
+ break;
+ case PCTRL_CMD_TDTMF:
+ p1 = para;
+ nom = 1;
+ ctrl = PCTRL_CMD_TDTMF;
+ break;
+ }
+ if (ctrl)
+ send_mbox(ch->is, dps | ISAR_HIS_PUMPCTRL, ctrl, nom, &p1);
+}
+
+static void
+isar_setup(struct isar_hw *isar)
+{
+ u8 msg;
+ int i;
+
+ /* Dpath 1, 2 */
+ msg = 61;
+ for (i = 0; i < 2; i++) {
+ /* Buffer Config */
+ send_mbox(isar, (i ? ISAR_HIS_DPS2 : ISAR_HIS_DPS1) |
+ ISAR_HIS_P12CFG, 4, 1, &msg);
+ isar->ch[i].mml = msg;
+ isar->ch[i].bch.state = 0;
+ isar->ch[i].dpath = i + 1;
+ modeisar(&isar->ch[i], ISDN_P_NONE);
+ }
+}
+
+static int
+isar_l2l1(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+ struct bchannel *bch = container_of(ch, struct bchannel, ch);
+ struct isar_ch *ich = container_of(bch, struct isar_ch, bch);
+ int ret = -EINVAL;
+ struct mISDNhead *hh = mISDN_HEAD_P(skb);
+ u32 id, *val;
+ u_long flags;
+
+ switch (hh->prim) {
+ case PH_DATA_REQ:
+ spin_lock_irqsave(ich->is->hwlock, flags);
+ ret = bchannel_senddata(bch, skb);
+ if (ret > 0) { /* direct TX */
+ id = hh->id; /* skb can be freed */
+ ret = 0;
+ isar_fill_fifo(ich);
+ spin_unlock_irqrestore(ich->is->hwlock, flags);
+ if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+ queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+ } else
+ spin_unlock_irqrestore(ich->is->hwlock, flags);
+ return ret;
+ case PH_ACTIVATE_REQ:
+ spin_lock_irqsave(ich->is->hwlock, flags);
+ if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
+ ret = modeisar(ich, ch->protocol);
+ else
+ ret = 0;
+ spin_unlock_irqrestore(ich->is->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(ich->is->hwlock, flags);
+ mISDN_clear_bchannel(bch);
+ modeisar(ich, ISDN_P_NONE);
+ spin_unlock_irqrestore(ich->is->hwlock, flags);
+ _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
+ NULL, GFP_KERNEL);
+ ret = 0;
+ break;
+ case PH_CONTROL_REQ:
+ val = (u32 *)skb->data;
+ pr_debug("%s: PH_CONTROL | REQUEST %x/%x\n", ich->is->name,
+ hh->id, *val);
+ if ((hh->id == 0) && ((*val & ~DTMF_TONE_MASK) ==
+ DTMF_TONE_VAL)) {
+ if (bch->state == ISDN_P_B_L2DTMF) {
+ char tt = *val & DTMF_TONE_MASK;
+
+ if (tt == '*')
+ tt = 0x1e;
+ else if (tt == '#')
+ tt = 0x1f;
+ else if (tt > '9')
+ tt -= 7;
+ tt &= 0x1f;
+ spin_lock_irqsave(ich->is->hwlock, flags);
+ isar_pump_cmd(ich, PCTRL_CMD_TDTMF, tt);
+ spin_unlock_irqrestore(ich->is->hwlock, flags);
+ } else {
+ pr_info("%s: DTMF send wrong protocol %x\n",
+ __func__, bch->state);
+ return -EINVAL;
+ }
+ } else if ((hh->id == HW_MOD_FRM) || (hh->id == HW_MOD_FRH) ||
+ (hh->id == HW_MOD_FTM) || (hh->id == HW_MOD_FTH)) {
+ for (id = 0; id < FAXMODCNT; id++)
+ if (faxmodulation[id] == *val)
+ break;
+ if ((FAXMODCNT > id) &&
+ test_bit(FLG_INITIALIZED, &bch->Flags)) {
+ pr_debug("%s: isar: new mod\n", ich->is->name);
+ isar_pump_cmd(ich, hh->id, *val);
+ ret = 0;
+ } else {
+ pr_info("%s: wrong modulation\n",
+ ich->is->name);
+ ret = -EINVAL;
+ }
+ } else if (hh->id == HW_MOD_LASTDATA)
+ test_and_set_bit(FLG_DLEETX, &bch->Flags);
+ else {
+ pr_info("%s: unknown PH_CONTROL_REQ %x\n",
+ ich->is->name, hh->id);
+ ret = -EINVAL;
+ }
+ default:
+ pr_info("%s: %s unknown prim(%x,%x)\n",
+ ich->is->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
+isar_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+ struct bchannel *bch = container_of(ch, struct bchannel, ch);
+ struct isar_ch *ich = container_of(bch, struct isar_ch, bch);
+ int ret = -EINVAL;
+ u_long flags;
+
+ pr_debug("%s: %s cmd:%x %p\n", ich->is->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(ich->is->hwlock, flags);
+ mISDN_freebchannel(bch);
+ modeisar(ich, ISDN_P_NONE);
+ spin_unlock_irqrestore(ich->is->hwlock, flags);
+ } else {
+ skb_queue_purge(&bch->rqueue);
+ bch->rcount = 0;
+ }
+ ch->protocol = ISDN_P_NONE;
+ ch->peer = NULL;
+ module_put(ich->is->owner);
+ ret = 0;
+ break;
+ case CONTROL_CHANNEL:
+ ret = channel_bctrl(bch, arg);
+ break;
+ default:
+ pr_info("%s: %s unknown prim(%x)\n",
+ ich->is->name, __func__, cmd);
+ }
+ return ret;
+}
+
+static void
+free_isar(struct isar_hw *isar)
+{
+ modeisar(&isar->ch[0], ISDN_P_NONE);
+ modeisar(&isar->ch[1], ISDN_P_NONE);
+ del_timer(&isar->ch[0].ftimer);
+ del_timer(&isar->ch[1].ftimer);
+ test_and_clear_bit(FLG_INITIALIZED, &isar->ch[0].bch.Flags);
+ test_and_clear_bit(FLG_INITIALIZED, &isar->ch[1].bch.Flags);
+}
+
+static int
+init_isar(struct isar_hw *isar)
+{
+ int cnt = 3;
+
+ while (cnt--) {
+ isar->version = ISARVersion(isar);
+ if (isar->ch[0].bch.debug & DEBUG_HW)
+ pr_notice("%s: Testing version %d (%d time)\n",
+ isar->name, isar->version, 3 - cnt);
+ if (isar->version == 1)
+ break;
+ isar->ctrl(isar->hw, HW_RESET_REQ, 0);
+ }
+ if (isar->version != 1)
+ return -EINVAL;
+ isar->ch[0].ftimer.function = &ftimer_handler;
+ isar->ch[0].ftimer.data = (long)&isar->ch[0];
+ init_timer(&isar->ch[0].ftimer);
+ test_and_set_bit(FLG_INITIALIZED, &isar->ch[0].bch.Flags);
+ isar->ch[1].ftimer.function = &ftimer_handler;
+ isar->ch[1].ftimer.data = (long)&isar->ch[1];
+ init_timer(&isar->ch[1].ftimer);
+ test_and_set_bit(FLG_INITIALIZED, &isar->ch[1].bch.Flags);
+ return 0;
+}
+
+static int
+isar_open(struct isar_hw *isar, struct channel_req *rq)
+{
+ struct bchannel *bch;
+
+ if (rq->adr.channel > 2)
+ return -EINVAL;
+ if (rq->protocol == ISDN_P_NONE)
+ return -EINVAL;
+ bch = &isar->ch[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;
+}
+
+u32
+mISDNisar_init(struct isar_hw *isar, void *hw)
+{
+ u32 ret, i;
+
+ isar->hw = hw;
+ for (i = 0; i < 2; i++) {
+ isar->ch[i].bch.nr = i + 1;
+ mISDN_initbchannel(&isar->ch[i].bch, MAX_DATA_MEM);
+ isar->ch[i].bch.ch.nr = i + 1;
+ isar->ch[i].bch.ch.send = &isar_l2l1;
+ isar->ch[i].bch.ch.ctrl = isar_bctrl;
+ isar->ch[i].bch.hw = hw;
+ isar->ch[i].is = isar;
+ }
+
+ isar->init = &init_isar;
+ isar->release = &free_isar;
+ isar->firmware = &load_firmware;
+ isar->open = &isar_open;
+
+ ret = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+ (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)) |
+ (1 << (ISDN_P_B_L2DTMF & ISDN_P_B_MASK)) |
+ (1 << (ISDN_P_B_MODEM_ASYNC & ISDN_P_B_MASK)) |
+ (1 << (ISDN_P_B_T30_FAX & ISDN_P_B_MASK));
+
+ return ret;
+}
+EXPORT_SYMBOL(mISDNisar_init);
+
+static int isar_mod_init(void)
+{
+ pr_notice("mISDN: ISAR driver Rev. %s\n", ISAR_REV);
+ return 0;
+}
+
+static void isar_mod_cleanup(void)
+{
+ pr_notice("mISDN: ISAR module unloaded\n");
+}
+module_init(isar_mod_init);
+module_exit(isar_mod_cleanup);
diff --git a/drivers/isdn/hardware/mISDN/netjet.c b/drivers/isdn/hardware/mISDN/netjet.c
new file mode 100644
index 00000000000..6c1b164937a
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/netjet.c
@@ -0,0 +1,1156 @@
+/*
+ * NETJet mISDN driver
+ *
+ * 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"
+#include "iohelper.h"
+#include "netjet.h"
+#include <linux/isdn/hdlc.h>
+
+#define NETJET_REV "2.0"
+
+enum nj_types {
+ NETJET_S_TJ300,
+ NETJET_S_TJ320,
+ ENTERNOW__TJ320,
+};
+
+struct tiger_dma {
+ size_t size;
+ u32 *start;
+ int idx;
+ u32 dmastart;
+ u32 dmairq;
+ u32 dmaend;
+ u32 dmacur;
+};
+
+struct tiger_hw;
+
+struct tiger_ch {
+ struct bchannel bch;
+ struct tiger_hw *nj;
+ int idx;
+ int free;
+ int lastrx;
+ u16 rxstate;
+ u16 txstate;
+ struct isdnhdlc_vars hsend;
+ struct isdnhdlc_vars hrecv;
+ u8 *hsbuf;
+ u8 *hrbuf;
+};
+
+#define TX_INIT 0x0001
+#define TX_IDLE 0x0002
+#define TX_RUN 0x0004
+#define TX_UNDERRUN 0x0100
+#define RX_OVERRUN 0x0100
+
+#define LOG_SIZE 64
+
+struct tiger_hw {
+ struct list_head list;
+ struct pci_dev *pdev;
+ char name[MISDN_MAX_IDLEN];
+ enum nj_types typ;
+ int irq;
+ u32 irqcnt;
+ u32 base;
+ size_t base_s;
+ dma_addr_t dma;
+ void *dma_p;
+ spinlock_t lock; /* lock HW */
+ struct isac_hw isac;
+ struct tiger_dma send;
+ struct tiger_dma recv;
+ struct tiger_ch bc[2];
+ u8 ctrlreg;
+ u8 dmactrl;
+ u8 auxd;
+ u8 last_is0;
+ u8 irqmask0;
+ char log[LOG_SIZE];
+};
+
+static LIST_HEAD(Cards);
+static DEFINE_RWLOCK(card_lock); /* protect Cards */
+static u32 debug;
+static int nj_cnt;
+
+static void
+_set_debug(struct tiger_hw *card)
+{
+ card->isac.dch.debug = debug;
+ card->bc[0].bch.debug = debug;
+ card->bc[1].bch.debug = debug;
+}
+
+static int
+set_debug(const char *val, struct kernel_param *kp)
+{
+ int ret;
+ struct tiger_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(NETJET_REV);
+module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Netjet debug mask");
+
+static void
+nj_disable_hwirq(struct tiger_hw *card)
+{
+ outb(0, card->base + NJ_IRQMASK0);
+ outb(0, card->base + NJ_IRQMASK1);
+}
+
+
+static u8
+ReadISAC_nj(void *p, u8 offset)
+{
+ struct tiger_hw *card = p;
+ u8 ret;
+
+ card->auxd &= 0xfc;
+ card->auxd |= (offset >> 4) & 3;
+ outb(card->auxd, card->base + NJ_AUXDATA);
+ ret = inb(card->base + NJ_ISAC_OFF + ((offset & 0x0f) << 2));
+ return ret;
+}
+
+static void
+WriteISAC_nj(void *p, u8 offset, u8 value)
+{
+ struct tiger_hw *card = p;
+
+ card->auxd &= 0xfc;
+ card->auxd |= (offset >> 4) & 3;
+ outb(card->auxd, card->base + NJ_AUXDATA);
+ outb(value, card->base + NJ_ISAC_OFF + ((offset & 0x0f) << 2));
+}
+
+static void
+ReadFiFoISAC_nj(void *p, u8 offset, u8 *data, int size)
+{
+ struct tiger_hw *card = p;
+
+ card->auxd &= 0xfc;
+ outb(card->auxd, card->base + NJ_AUXDATA);
+ insb(card->base + NJ_ISAC_OFF, data, size);
+}
+
+static void
+WriteFiFoISAC_nj(void *p, u8 offset, u8 *data, int size)
+{
+ struct tiger_hw *card = p;
+
+ card->auxd &= 0xfc;
+ outb(card->auxd, card->base + NJ_AUXDATA);
+ outsb(card->base + NJ_ISAC_OFF, data, size);
+}
+
+static void
+fill_mem(struct tiger_ch *bc, u32 idx, u32 cnt, u32 fill)
+{
+ struct tiger_hw *card = bc->bch.hw;
+ u32 mask = 0xff, val;
+
+ pr_debug("%s: B%1d fill %02x len %d idx %d/%d\n", card->name,
+ bc->bch.nr, fill, cnt, idx, card->send.idx);
+ if (bc->bch.nr & 2) {
+ fill <<= 8;
+ mask <<= 8;
+ }
+ mask ^= 0xffffffff;
+ while (cnt--) {
+ val = card->send.start[idx];
+ val &= mask;
+ val |= fill;
+ card->send.start[idx++] = val;
+ if (idx >= card->send.size)
+ idx = 0;
+ }
+}
+
+static int
+mode_tiger(struct tiger_ch *bc, u32 protocol)
+{
+ struct tiger_hw *card = bc->bch.hw;
+
+ pr_debug("%s: B%1d protocol %x-->%x\n", card->name,
+ bc->bch.nr, bc->bch.state, protocol);
+ switch (protocol) {
+ case ISDN_P_NONE:
+ if (bc->bch.state == ISDN_P_NONE)
+ break;
+ fill_mem(bc, 0, card->send.size, 0xff);
+ bc->bch.state = protocol;
+ /* only stop dma and interrupts if both channels NULL */
+ if ((card->bc[0].bch.state == ISDN_P_NONE) &&
+ (card->bc[1].bch.state == ISDN_P_NONE)) {
+ card->dmactrl = 0;
+ outb(card->dmactrl, card->base + NJ_DMACTRL);
+ outb(0, card->base + NJ_IRQMASK0);
+ }
+ test_and_clear_bit(FLG_HDLC, &bc->bch.Flags);
+ test_and_clear_bit(FLG_TRANSPARENT, &bc->bch.Flags);
+ bc->txstate = 0;
+ bc->rxstate = 0;
+ bc->lastrx = -1;
+ break;
+ case ISDN_P_B_RAW:
+ test_and_set_bit(FLG_TRANSPARENT, &bc->bch.Flags);
+ bc->bch.state = protocol;
+ bc->idx = 0;
+ bc->free = card->send.size/2;
+ bc->rxstate = 0;
+ bc->txstate = TX_INIT | TX_IDLE;
+ bc->lastrx = -1;
+ if (!card->dmactrl) {
+ card->dmactrl = 1;
+ outb(card->dmactrl, card->base + NJ_DMACTRL);
+ outb(0x0f, card->base + NJ_IRQMASK0);
+ }
+ break;
+ case ISDN_P_B_HDLC:
+ test_and_set_bit(FLG_HDLC, &bc->bch.Flags);
+ bc->bch.state = protocol;
+ bc->idx = 0;
+ bc->free = card->send.size/2;
+ bc->rxstate = 0;
+ bc->txstate = TX_INIT | TX_IDLE;
+ isdnhdlc_rcv_init(&bc->hrecv, 0);
+ isdnhdlc_out_init(&bc->hsend, 0);
+ bc->lastrx = -1;
+ if (!card->dmactrl) {
+ card->dmactrl = 1;
+ outb(card->dmactrl, card->base + NJ_DMACTRL);
+ outb(0x0f, card->base + NJ_IRQMASK0);
+ }
+ break;
+ default:
+ pr_info("%s: %s protocol %x not handled\n", card->name,
+ __func__, protocol);
+ return -ENOPROTOOPT;
+ }
+ card->send.dmacur = inl(card->base + NJ_DMA_READ_ADR);
+ card->recv.dmacur = inl(card->base + NJ_DMA_WRITE_ADR);
+ card->send.idx = (card->send.dmacur - card->send.dmastart) >> 2;
+ card->recv.idx = (card->recv.dmacur - card->recv.dmastart) >> 2;
+ pr_debug("%s: %s ctrl %x irq %02x/%02x idx %d/%d\n",
+ card->name, __func__,
+ inb(card->base + NJ_DMACTRL),
+ inb(card->base + NJ_IRQMASK0),
+ inb(card->base + NJ_IRQSTAT0),
+ card->send.idx,
+ card->recv.idx);
+ return 0;
+}
+
+static void
+nj_reset(struct tiger_hw *card)
+{
+ outb(0xff, card->base + NJ_CTRL); /* Reset On */
+ mdelay(1);
+
+ /* now edge triggered for TJ320 GE 13/07/00 */
+ /* see comment in IRQ function */
+ if (card->typ == NETJET_S_TJ320) /* TJ320 */
+ card->ctrlreg = 0x40; /* Reset Off and status read clear */
+ else
+ card->ctrlreg = 0x00; /* Reset Off and status read clear */
+ outb(card->ctrlreg, card->base + NJ_CTRL);
+ mdelay(10);
+
+ /* configure AUX pins (all output except ISAC IRQ pin) */
+ card->auxd = 0;
+ card->dmactrl = 0;
+ outb(~NJ_ISACIRQ, card->base + NJ_AUXCTRL);
+ outb(NJ_ISACIRQ, card->base + NJ_IRQMASK1);
+ outb(card->auxd, card->base + NJ_AUXDATA);
+}
+
+static int
+inittiger(struct tiger_hw *card)
+{
+ int i;
+
+ card->dma_p = pci_alloc_consistent(card->pdev, NJ_DMA_SIZE,
+ &card->dma);
+ if (!card->dma_p) {
+ pr_info("%s: No DMA memory\n", card->name);
+ return -ENOMEM;
+ }
+ if ((u64)card->dma > 0xffffffff) {
+ pr_info("%s: DMA outside 32 bit\n", card->name);
+ return -ENOMEM;
+ }
+ for (i = 0; i < 2; i++) {
+ card->bc[i].hsbuf = kmalloc(NJ_DMA_TXSIZE, GFP_KERNEL);
+ if (!card->bc[i].hsbuf) {
+ pr_info("%s: no B%d send buffer\n", card->name, i + 1);
+ return -ENOMEM;
+ }
+ card->bc[i].hrbuf = kmalloc(NJ_DMA_RXSIZE, GFP_KERNEL);
+ if (!card->bc[i].hrbuf) {
+ pr_info("%s: no B%d recv buffer\n", card->name, i + 1);
+ return -ENOMEM;
+ }
+ }
+ memset(card->dma_p, 0xff, NJ_DMA_SIZE);
+
+ card->send.start = card->dma_p;
+ card->send.dmastart = (u32)card->dma;
+ card->send.dmaend = card->send.dmastart +
+ (4 * (NJ_DMA_TXSIZE - 1));
+ card->send.dmairq = card->send.dmastart +
+ (4 * ((NJ_DMA_TXSIZE / 2) - 1));
+ card->send.size = NJ_DMA_TXSIZE;
+
+ if (debug & DEBUG_HW)
+ pr_notice("%s: send buffer phy %#x - %#x - %#x virt %p"
+ " size %zu u32\n", card->name,
+ card->send.dmastart, card->send.dmairq,
+ card->send.dmaend, card->send.start, card->send.size);
+
+ outl(card->send.dmastart, card->base + NJ_DMA_READ_START);
+ outl(card->send.dmairq, card->base + NJ_DMA_READ_IRQ);
+ outl(card->send.dmaend, card->base + NJ_DMA_READ_END);
+
+ card->recv.start = card->dma_p + (NJ_DMA_SIZE / 2);
+ card->recv.dmastart = (u32)card->dma + (NJ_DMA_SIZE / 2);
+ card->recv.dmaend = card->recv.dmastart +
+ (4 * (NJ_DMA_RXSIZE - 1));
+ card->recv.dmairq = card->recv.dmastart +
+ (4 * ((NJ_DMA_RXSIZE / 2) - 1));
+ card->recv.size = NJ_DMA_RXSIZE;
+
+ if (debug & DEBUG_HW)
+ pr_notice("%s: recv buffer phy %#x - %#x - %#x virt %p"
+ " size %zu u32\n", card->name,
+ card->recv.dmastart, card->recv.dmairq,
+ card->recv.dmaend, card->recv.start, card->recv.size);
+
+ outl(card->recv.dmastart, card->base + NJ_DMA_WRITE_START);
+ outl(card->recv.dmairq, card->base + NJ_DMA_WRITE_IRQ);
+ outl(card->recv.dmaend, card->base + NJ_DMA_WRITE_END);
+ return 0;
+}
+
+static void
+read_dma(struct tiger_ch *bc, u32 idx, int cnt)
+{
+ struct tiger_hw *card = bc->bch.hw;
+ int i, stat;
+ u32 val;
+ u8 *p, *pn;
+
+ if (bc->lastrx == idx) {
+ bc->rxstate |= RX_OVERRUN;
+ pr_info("%s: B%1d overrun at idx %d\n", card->name,
+ bc->bch.nr, idx);
+ }
+ bc->lastrx = idx;
+ if (!bc->bch.rx_skb) {
+ bc->bch.rx_skb = mI_alloc_skb(bc->bch.maxlen, GFP_ATOMIC);
+ if (!bc->bch.rx_skb) {
+ pr_info("%s: B%1d receive out of memory\n",
+ card->name, bc->bch.nr);
+ return;
+ }
+ }
+
+ if (test_bit(FLG_TRANSPARENT, &bc->bch.Flags)) {
+ if ((bc->bch.rx_skb->len + cnt) > bc->bch.maxlen) {
+ pr_debug("%s: B%1d overrun %d\n", card->name,
+ bc->bch.nr, bc->bch.rx_skb->len + cnt);
+ skb_trim(bc->bch.rx_skb, 0);
+ return;
+ }
+ p = skb_put(bc->bch.rx_skb, cnt);
+ } else
+ p = bc->hrbuf;
+
+ for (i = 0; i < cnt; i++) {
+ val = card->recv.start[idx++];
+ if (bc->bch.nr & 2)
+ val >>= 8;
+ if (idx >= card->recv.size)
+ idx = 0;
+ p[i] = val & 0xff;
+ }
+ pn = bc->hrbuf;
+next_frame:
+ if (test_bit(FLG_HDLC, &bc->bch.Flags)) {
+ stat = isdnhdlc_decode(&bc->hrecv, pn, cnt, &i,
+ bc->bch.rx_skb->data, bc->bch.maxlen);
+ if (stat > 0) /* valid frame received */
+ p = skb_put(bc->bch.rx_skb, stat);
+ else if (stat == -HDLC_CRC_ERROR)
+ pr_info("%s: B%1d receive frame CRC error\n",
+ card->name, bc->bch.nr);
+ else if (stat == -HDLC_FRAMING_ERROR)
+ pr_info("%s: B%1d receive framing error\n",
+ card->name, bc->bch.nr);
+ else if (stat == -HDLC_LENGTH_ERROR)
+ pr_info("%s: B%1d receive frame too long (> %d)\n",
+ card->name, bc->bch.nr, bc->bch.maxlen);
+ } else
+ stat = cnt;
+
+ if (stat > 0) {
+ if (debug & DEBUG_HW_BFIFO) {
+ snprintf(card->log, LOG_SIZE, "B%1d-recv %s %d ",
+ bc->bch.nr, card->name, stat);
+ print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET,
+ p, stat);
+ }
+ recv_Bchannel(&bc->bch, 0);
+ }
+ if (test_bit(FLG_HDLC, &bc->bch.Flags)) {
+ pn += i;
+ cnt -= i;
+ if (!bc->bch.rx_skb) {
+ bc->bch.rx_skb = mI_alloc_skb(bc->bch.maxlen,
+ GFP_ATOMIC);
+ if (!bc->bch.rx_skb) {
+ pr_info("%s: B%1d receive out of memory\n",
+ card->name, bc->bch.nr);
+ return;
+ }
+ }
+ if (cnt > 0)
+ goto next_frame;
+ }
+}
+
+static void
+recv_tiger(struct tiger_hw *card, u8 irq_stat)
+{
+ u32 idx;
+ int cnt = card->recv.size / 2;
+
+ /* Note receive is via the WRITE DMA channel */
+ card->last_is0 &= ~NJ_IRQM0_WR_MASK;
+ card->last_is0 |= (irq_stat & NJ_IRQM0_WR_MASK);
+
+ if (irq_stat & NJ_IRQM0_WR_END)
+ idx = cnt - 1;
+ else
+ idx = card->recv.size - 1;
+
+ if (test_bit(FLG_ACTIVE, &card->bc[0].bch.Flags))
+ read_dma(&card->bc[0], idx, cnt);
+ if (test_bit(FLG_ACTIVE, &card->bc[1].bch.Flags))
+ read_dma(&card->bc[1], idx, cnt);
+}
+
+/* sync with current DMA address at start or after exception */
+static void
+resync(struct tiger_ch *bc, struct tiger_hw *card)
+{
+ card->send.dmacur = inl(card->base | NJ_DMA_READ_ADR);
+ card->send.idx = (card->send.dmacur - card->send.dmastart) >> 2;
+ if (bc->free > card->send.size / 2)
+ bc->free = card->send.size / 2;
+ /* currently we simple sync to the next complete free area
+ * this hast the advantage that we have always maximum time to
+ * handle TX irq
+ */
+ if (card->send.idx < ((card->send.size / 2) - 1))
+ bc->idx = (card->recv.size / 2) - 1;
+ else
+ bc->idx = card->recv.size - 1;
+ bc->txstate = TX_RUN;
+ pr_debug("%s: %s B%1d free %d idx %d/%d\n", card->name,
+ __func__, bc->bch.nr, bc->free, bc->idx, card->send.idx);
+}
+
+static int bc_next_frame(struct tiger_ch *);
+
+static void
+fill_hdlc_flag(struct tiger_ch *bc)
+{
+ struct tiger_hw *card = bc->bch.hw;
+ int count, i;
+ u32 m, v;
+ u8 *p;
+
+ if (bc->free == 0)
+ return;
+ pr_debug("%s: %s B%1d %d state %x idx %d/%d\n", card->name,
+ __func__, bc->bch.nr, bc->free, bc->txstate,
+ bc->idx, card->send.idx);
+ if (bc->txstate & (TX_IDLE | TX_INIT | TX_UNDERRUN))
+ resync(bc, card);
+ count = isdnhdlc_encode(&bc->hsend, NULL, 0, &i,
+ bc->hsbuf, bc->free);
+ pr_debug("%s: B%1d hdlc encoded %d flags\n", card->name,
+ bc->bch.nr, count);
+ bc->free -= count;
+ p = bc->hsbuf;
+ m = (bc->bch.nr & 1) ? 0xffffff00 : 0xffff00ff;
+ for (i = 0; i < count; i++) {
+ if (bc->idx >= card->send.size)
+ bc->idx = 0;
+ v = card->send.start[bc->idx];
+ v &= m;
+ v |= (bc->bch.nr & 1) ? (u32)(p[i]) : ((u32)(p[i])) << 8;
+ card->send.start[bc->idx++] = v;
+ }
+ if (debug & DEBUG_HW_BFIFO) {
+ snprintf(card->log, LOG_SIZE, "B%1d-send %s %d ",
+ bc->bch.nr, card->name, count);
+ print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, p, count);
+ }
+}
+
+static void
+fill_dma(struct tiger_ch *bc)
+{
+ struct tiger_hw *card = bc->bch.hw;
+ int count, i;
+ u32 m, v;
+ u8 *p;
+
+ if (bc->free == 0)
+ return;
+ count = bc->bch.tx_skb->len - bc->bch.tx_idx;
+ if (count <= 0)
+ return;
+ pr_debug("%s: %s B%1d %d/%d/%d/%d state %x idx %d/%d\n", card->name,
+ __func__, bc->bch.nr, count, bc->free, bc->bch.tx_idx,
+ bc->bch.tx_skb->len, bc->txstate, bc->idx, card->send.idx);
+ if (bc->txstate & (TX_IDLE | TX_INIT | TX_UNDERRUN))
+ resync(bc, card);
+ p = bc->bch.tx_skb->data + bc->bch.tx_idx;
+ if (test_bit(FLG_HDLC, &bc->bch.Flags)) {
+ count = isdnhdlc_encode(&bc->hsend, p, count, &i,
+ bc->hsbuf, bc->free);
+ pr_debug("%s: B%1d hdlc encoded %d in %d\n", card->name,
+ bc->bch.nr, i, count);
+ bc->bch.tx_idx += i;
+ bc->free -= count;
+ p = bc->hsbuf;
+ } else {
+ if (count > bc->free)
+ count = bc->free;
+ bc->bch.tx_idx += count;
+ bc->free -= count;
+ }
+ m = (bc->bch.nr & 1) ? 0xffffff00 : 0xffff00ff;
+ for (i = 0; i < count; i++) {
+ if (bc->idx >= card->send.size)
+ bc->idx = 0;
+ v = card->send.start[bc->idx];
+ v &= m;
+ v |= (bc->bch.nr & 1) ? (u32)(p[i]) : ((u32)(p[i])) << 8;
+ card->send.start[bc->idx++] = v;
+ }
+ if (debug & DEBUG_HW_BFIFO) {
+ snprintf(card->log, LOG_SIZE, "B%1d-send %s %d ",
+ bc->bch.nr, card->name, count);
+ print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, p, count);
+ }
+ if (bc->free)
+ bc_next_frame(bc);
+}
+
+
+static int
+bc_next_frame(struct tiger_ch *bc)
+{
+ if (bc->bch.tx_skb && bc->bch.tx_idx < bc->bch.tx_skb->len)
+ fill_dma(bc);
+ else {
+ if (bc->bch.tx_skb) {
+ /* send confirm, on trans, free on hdlc. */
+ if (test_bit(FLG_TRANSPARENT, &bc->bch.Flags))
+ confirm_Bsend(&bc->bch);
+ dev_kfree_skb(bc->bch.tx_skb);
+ }
+ if (get_next_bframe(&bc->bch))
+ fill_dma(bc);
+ else
+ return 0;
+ }
+ return 1;
+}
+
+static void
+send_tiger_bc(struct tiger_hw *card, struct tiger_ch *bc)
+{
+ int ret;
+
+ bc->free += card->send.size / 2;
+ if (bc->free >= card->send.size) {
+ if (!(bc->txstate & (TX_UNDERRUN | TX_INIT))) {
+ pr_info("%s: B%1d TX underrun state %x\n", card->name,
+ bc->bch.nr, bc->txstate);
+ bc->txstate |= TX_UNDERRUN;
+ }
+ bc->free = card->send.size;
+ }
+ ret = bc_next_frame(bc);
+ if (!ret) {
+ if (test_bit(FLG_HDLC, &bc->bch.Flags)) {
+ fill_hdlc_flag(bc);
+ return;
+ }
+ pr_debug("%s: B%1d TX no data free %d idx %d/%d\n", card->name,
+ bc->bch.nr, bc->free, bc->idx, card->send.idx);
+ if (!(bc->txstate & (TX_IDLE | TX_INIT))) {
+ fill_mem(bc, bc->idx, bc->free, 0xff);
+ if (bc->free == card->send.size)
+ bc->txstate |= TX_IDLE;
+ }
+ }
+}
+
+static void
+send_tiger(struct tiger_hw *card, u8 irq_stat)
+{
+ int i;
+
+ /* Note send is via the READ DMA channel */
+ if ((irq_stat & card->last_is0) & NJ_IRQM0_RD_MASK) {
+ pr_info("%s: tiger warn write double dma %x/%x\n",
+ card->name, irq_stat, card->last_is0);
+ return;
+ } else {
+ card->last_is0 &= ~NJ_IRQM0_RD_MASK;
+ card->last_is0 |= (irq_stat & NJ_IRQM0_RD_MASK);
+ }
+ for (i = 0; i < 2; i++) {
+ if (test_bit(FLG_ACTIVE, &card->bc[i].bch.Flags))
+ send_tiger_bc(card, &card->bc[i]);
+ }
+}
+
+static irqreturn_t
+nj_irq(int intno, void *dev_id)
+{
+ struct tiger_hw *card = dev_id;
+ u8 val, s1val, s0val;
+
+ spin_lock(&card->lock);
+ s0val = inb(card->base | NJ_IRQSTAT0);
+ s1val = inb(card->base | NJ_IRQSTAT1);
+ if ((s1val & NJ_ISACIRQ) && (s0val == 0)) {
+ /* shared IRQ */
+ spin_unlock(&card->lock);
+ return IRQ_NONE;
+ }
+ pr_debug("%s: IRQSTAT0 %02x IRQSTAT1 %02x\n", card->name, s0val, s1val);
+ card->irqcnt++;
+ if (!(s1val & NJ_ISACIRQ)) {
+ val = ReadISAC_nj(card, ISAC_ISTA);
+ if (val)
+ mISDNisac_irq(&card->isac, val);
+ }
+
+ if (s0val)
+ /* write to clear */
+ outb(s0val, card->base | NJ_IRQSTAT0);
+ else
+ goto end;
+ s1val = s0val;
+ /* set bits in sval to indicate which page is free */
+ card->recv.dmacur = inl(card->base | NJ_DMA_WRITE_ADR);
+ card->recv.idx = (card->recv.dmacur - card->recv.dmastart) >> 2;
+ if (card->recv.dmacur < card->recv.dmairq)
+ s0val = 0x08; /* the 2nd write area is free */
+ else
+ s0val = 0x04; /* the 1st write area is free */
+
+ card->send.dmacur = inl(card->base | NJ_DMA_READ_ADR);
+ card->send.idx = (card->send.dmacur - card->send.dmastart) >> 2;
+ if (card->send.dmacur < card->send.dmairq)
+ s0val |= 0x02; /* the 2nd read area is free */
+ else
+ s0val |= 0x01; /* the 1st read area is free */
+
+ pr_debug("%s: DMA Status %02x/%02x/%02x %d/%d\n", card->name,
+ s1val, s0val, card->last_is0,
+ card->recv.idx, card->send.idx);
+ /* test if we have a DMA interrupt */
+ if (s0val != card->last_is0) {
+ if ((s0val & NJ_IRQM0_RD_MASK) !=
+ (card->last_is0 & NJ_IRQM0_RD_MASK))
+ /* got a write dma int */
+ send_tiger(card, s0val);
+ if ((s0val & NJ_IRQM0_WR_MASK) !=
+ (card->last_is0 & NJ_IRQM0_WR_MASK))
+ /* got a read dma int */
+ recv_tiger(card, s0val);
+ }
+end:
+ spin_unlock(&card->lock);
+ return IRQ_HANDLED;
+}
+
+static int
+nj_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+ int ret = -EINVAL;
+ struct bchannel *bch = container_of(ch, struct bchannel, ch);
+ struct tiger_ch *bc = container_of(bch, struct tiger_ch, bch);
+ struct tiger_hw *card = bch->hw;
+ struct mISDNhead *hh = mISDN_HEAD_P(skb);
+ u32 id;
+ u_long flags;
+
+ switch (hh->prim) {
+ case PH_DATA_REQ:
+ spin_lock_irqsave(&card->lock, flags);
+ ret = bchannel_senddata(bch, skb);
+ if (ret > 0) { /* direct TX */
+ id = hh->id; /* skb can be freed */
+ fill_dma(bc);
+ ret = 0;
+ spin_unlock_irqrestore(&card->lock, flags);
+ if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+ queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+ } else
+ spin_unlock_irqrestore(&card->lock, flags);
+ return ret;
+ case PH_ACTIVATE_REQ:
+ spin_lock_irqsave(&card->lock, flags);
+ if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
+ ret = mode_tiger(bc, ch->protocol);
+ else
+ ret = 0;
+ spin_unlock_irqrestore(&card->lock, flags);
+ if (!ret)
+ _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
+ NULL, GFP_KERNEL);
+ break;
+ case PH_DEACTIVATE_REQ:
+ spin_lock_irqsave(&card->lock, flags);
+ mISDN_clear_bchannel(bch);
+ mode_tiger(bc, ISDN_P_NONE);
+ spin_unlock_irqrestore(&card->lock, flags);
+ _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
+ NULL, GFP_KERNEL);
+ ret = 0;
+ break;
+ }
+ if (!ret)
+ dev_kfree_skb(skb);
+ return ret;
+}
+
+static int
+channel_bctrl(struct tiger_ch *bc, struct mISDN_ctrl_req *cq)
+{
+ int ret = 0;
+ struct tiger_hw *card = bc->bch.hw;
+
+ switch (cq->op) {
+ case MISDN_CTRL_GETOP:
+ cq->op = 0;
+ break;
+ /* Nothing implemented yet */
+ case MISDN_CTRL_FILL_EMPTY:
+ default:
+ pr_info("%s: %s unknown Op %x\n", card->name, __func__, cq->op);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int
+nj_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+ struct bchannel *bch = container_of(ch, struct bchannel, ch);
+ struct tiger_ch *bc = container_of(bch, struct tiger_ch, bch);
+ struct tiger_hw *card = bch->hw;
+ int ret = -EINVAL;
+ u_long flags;
+
+ pr_debug("%s: %s cmd:%x %p\n", card->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(&card->lock, flags);
+ mISDN_freebchannel(bch);
+ test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
+ test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
+ mode_tiger(bc, ISDN_P_NONE);
+ spin_unlock_irqrestore(&card->lock, flags);
+ }
+ ch->protocol = ISDN_P_NONE;
+ ch->peer = NULL;
+ module_put(THIS_MODULE);
+ ret = 0;
+ break;
+ case CONTROL_CHANNEL:
+ ret = channel_bctrl(bc, arg);
+ break;
+ default:
+ pr_info("%s: %s unknown prim(%x)\n", card->name, __func__, cmd);
+ }
+ return ret;
+}
+
+static int
+channel_ctrl(struct tiger_hw *card, 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 = card->isac.ctrl(&card->isac, HW_TESTLOOP, cq->channel);
+ break;
+ default:
+ pr_info("%s: %s unknown Op %x\n", card->name, __func__, cq->op);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int
+open_bchannel(struct tiger_hw *card, struct channel_req *rq)
+{
+ struct bchannel *bch;
+
+ if (rq->adr.channel > 2)
+ return -EINVAL;
+ if (rq->protocol == ISDN_P_NONE)
+ return -EINVAL;
+ bch = &card->bc[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;
+}
+
+/*
+ * device control function
+ */
+static int
+nj_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 tiger_hw *card = dch->hw;
+ struct channel_req *rq;
+ int err = 0;
+
+ pr_debug("%s: %s cmd:%x %p\n", card->name, __func__, cmd, arg);
+ switch (cmd) {
+ case OPEN_CHANNEL:
+ rq = arg;
+ if (rq->protocol == ISDN_P_TE_S0)
+ err = card->isac.open(&card->isac, rq);
+ else
+ err = open_bchannel(card, rq);
+ if (err)
+ break;
+ if (!try_module_get(THIS_MODULE))
+ pr_info("%s: cannot get module\n", card->name);
+ break;
+ case CLOSE_CHANNEL:
+ pr_debug("%s: dev(%d) close from %p\n", card->name, dch->dev.id,
+ __builtin_return_address(0));
+ module_put(THIS_MODULE);
+ break;
+ case CONTROL_CHANNEL:
+ err = channel_ctrl(card, arg);
+ break;
+ default:
+ pr_debug("%s: %s unknown command %x\n",
+ card->name, __func__, cmd);
+ return -EINVAL;
+ }
+ return err;
+}
+
+static int
+nj_init_card(struct tiger_hw *card)
+{
+ u_long flags;
+ int ret;
+
+ spin_lock_irqsave(&card->lock, flags);
+ nj_disable_hwirq(card);
+ spin_unlock_irqrestore(&card->lock, flags);
+
+ card->irq = card->pdev->irq;
+ if (request_irq(card->irq, nj_irq, IRQF_SHARED, card->name, card)) {
+ pr_info("%s: couldn't get interrupt %d\n",
+ card->name, card->irq);
+ card->irq = -1;
+ return -EIO;
+ }
+
+ spin_lock_irqsave(&card->lock, flags);
+ nj_reset(card);
+ ret = card->isac.init(&card->isac);
+ if (ret)
+ goto error;
+ ret = inittiger(card);
+ if (ret)
+ goto error;
+ mode_tiger(&card->bc[0], ISDN_P_NONE);
+ mode_tiger(&card->bc[1], ISDN_P_NONE);
+error:
+ spin_unlock_irqrestore(&card->lock, flags);
+ return ret;
+}
+
+
+static void
+nj_release(struct tiger_hw *card)
+{
+ u_long flags;
+ int i;
+
+ if (card->base_s) {
+ spin_lock_irqsave(&card->lock, flags);
+ nj_disable_hwirq(card);
+ mode_tiger(&card->bc[0], ISDN_P_NONE);
+ mode_tiger(&card->bc[1], ISDN_P_NONE);
+ card->isac.release(&card->isac);
+ spin_unlock_irqrestore(&card->lock, flags);
+ release_region(card->base, card->base_s);
+ card->base_s = 0;
+ }
+ if (card->irq > 0)
+ free_irq(card->irq, card);
+ if (card->isac.dch.dev.dev.class)
+ mISDN_unregister_device(&card->isac.dch.dev);
+
+ for (i = 0; i < 2; i++) {
+ mISDN_freebchannel(&card->bc[i].bch);
+ kfree(card->bc[i].hsbuf);
+ kfree(card->bc[i].hrbuf);
+ }
+ if (card->dma_p)
+ pci_free_consistent(card->pdev, NJ_DMA_SIZE,
+ card->dma_p, card->dma);
+ write_lock_irqsave(&card_lock, flags);
+ list_del(&card->list);
+ write_unlock_irqrestore(&card_lock, flags);
+ pci_clear_master(card->pdev);
+ pci_disable_device(card->pdev);
+ pci_set_drvdata(card->pdev, NULL);
+ kfree(card);
+}
+
+
+static int
+nj_setup(struct tiger_hw *card)
+{
+ card->base = pci_resource_start(card->pdev, 0);
+ card->base_s = pci_resource_len(card->pdev, 0);
+ if (!request_region(card->base, card->base_s, card->name)) {
+ pr_info("%s: NETjet config port %#x-%#x already in use\n",
+ card->name, card->base,
+ (u32)(card->base + card->base_s - 1));
+ card->base_s = 0;
+ return -EIO;
+ }
+ ASSIGN_FUNC(nj, ISAC, card->isac);
+ return 0;
+}
+
+
+static int __devinit
+setup_instance(struct tiger_hw *card)
+{
+ int i, err;
+ u_long flags;
+
+ snprintf(card->name, MISDN_MAX_IDLEN - 1, "netjet.%d", nj_cnt + 1);
+ write_lock_irqsave(&card_lock, flags);
+ list_add_tail(&card->list, &Cards);
+ write_unlock_irqrestore(&card_lock, flags);
+
+ _set_debug(card);
+ card->isac.name = card->name;
+ spin_lock_init(&card->lock);
+ card->isac.hwlock = &card->lock;
+ mISDNisac_init(&card->isac, card);
+
+ card->isac.dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+ (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+ card->isac.dch.dev.D.ctrl = nj_dctrl;
+ for (i = 0; i < 2; i++) {
+ card->bc[i].bch.nr = i + 1;
+ set_channelmap(i + 1, card->isac.dch.dev.channelmap);
+ mISDN_initbchannel(&card->bc[i].bch, MAX_DATA_MEM);
+ card->bc[i].bch.hw = card;
+ card->bc[i].bch.ch.send = nj_l2l1B;
+ card->bc[i].bch.ch.ctrl = nj_bctrl;
+ card->bc[i].bch.ch.nr = i + 1;
+ list_add(&card->bc[i].bch.ch.list,
+ &card->isac.dch.dev.bchannels);
+ card->bc[i].bch.hw = card;
+ }
+ err = nj_setup(card);
+ if (err)
+ goto error;
+ err = mISDN_register_device(&card->isac.dch.dev, &card->pdev->dev,
+ card->name);
+ if (err)
+ goto error;
+ err = nj_init_card(card);
+ if (!err) {
+ nj_cnt++;
+ pr_notice("Netjet %d cards installed\n", nj_cnt);
+ return 0;
+ }
+error:
+ nj_release(card);
+ return err;
+}
+
+static int __devinit
+nj_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ int err = -ENOMEM;
+ int cfg;
+ struct tiger_hw *card;
+
+ if (pdev->subsystem_vendor == 0x8086 &&
+ pdev->subsystem_device == 0x0003) {
+ pr_notice("Netjet: Digium X100P/X101P not handled\n");
+ return -ENODEV;
+ }
+
+ if (pdev->subsystem_vendor == 0x55 &&
+ pdev->subsystem_device == 0x02) {
+ pr_notice("Netjet: Enter!Now not handled yet\n");
+ return -ENODEV;
+ }
+
+ card = kzalloc(sizeof(struct tiger_hw), GFP_ATOMIC);
+ if (!card) {
+ pr_info("No kmem for Netjet\n");
+ return err;
+ }
+
+ card->pdev = pdev;
+
+ err = pci_enable_device(pdev);
+ if (err) {
+ kfree(card);
+ return err;
+ }
+
+ printk(KERN_INFO "nj_probe(mISDN): found adapter at %s\n",
+ pci_name(pdev));
+
+ pci_set_master(pdev);
+
+ /* the TJ300 and TJ320 must be detected, the IRQ handling is different
+ * unfortunately the chips use the same device ID, but the TJ320 has
+ * the bit20 in status PCI cfg register set
+ */
+ pci_read_config_dword(pdev, 0x04, &cfg);
+ if (cfg & 0x00100000)
+ card->typ = NETJET_S_TJ320;
+ else
+ card->typ = NETJET_S_TJ300;
+
+ card->base = pci_resource_start(pdev, 0);
+ card->irq = pdev->irq;
+ pci_set_drvdata(pdev, card);
+ err = setup_instance(card);
+ if (err)
+ pci_set_drvdata(pdev, NULL);
+
+ return err;
+}
+
+
+static void __devexit nj_remove(struct pci_dev *pdev)
+{
+ struct tiger_hw *card = pci_get_drvdata(pdev);
+
+ if (card)
+ nj_release(card);
+ else
+ pr_info("%s drvdata already removed\n", __func__);
+}
+
+/* We cannot select cards with PCI_SUB... IDs, since here are cards with
+ * SUB IDs set to PCI_ANY_ID, so we need to match all and reject
+ * known other cards which not work with this driver - see probe function */
+static struct pci_device_id nj_pci_ids[] __devinitdata = {
+ { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_300,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ { }
+};
+MODULE_DEVICE_TABLE(pci, nj_pci_ids);
+
+static struct pci_driver nj_driver = {
+ .name = "netjet",
+ .probe = nj_probe,
+ .remove = __devexit_p(nj_remove),
+ .id_table = nj_pci_ids,
+};
+
+static int __init nj_init(void)
+{
+ int err;
+
+ pr_notice("Netjet PCI driver Rev. %s\n", NETJET_REV);
+ err = pci_register_driver(&nj_driver);
+ return err;
+}
+
+static void __exit nj_cleanup(void)
+{
+ pci_unregister_driver(&nj_driver);
+}
+
+module_init(nj_init);
+module_exit(nj_cleanup);
diff --git a/drivers/isdn/hardware/mISDN/netjet.h b/drivers/isdn/hardware/mISDN/netjet.h
new file mode 100644
index 00000000000..d061ff99560
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/netjet.h
@@ -0,0 +1,58 @@
+/*
+ * NETjet common header file
+ *
+ * Author Karsten Keil
+ * based on work of Matt Henderson and Daniel Potts,
+ * Traverse Technologies P/L www.traverse.com.au
+ *
+ * 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.
+ *
+ */
+
+#define NJ_CTRL 0x00
+#define NJ_DMACTRL 0x01
+#define NJ_AUXCTRL 0x02
+#define NJ_AUXDATA 0x03
+#define NJ_IRQMASK0 0x04
+#define NJ_IRQMASK1 0x05
+#define NJ_IRQSTAT0 0x06
+#define NJ_IRQSTAT1 0x07
+#define NJ_DMA_READ_START 0x08
+#define NJ_DMA_READ_IRQ 0x0c
+#define NJ_DMA_READ_END 0x10
+#define NJ_DMA_READ_ADR 0x14
+#define NJ_DMA_WRITE_START 0x18
+#define NJ_DMA_WRITE_IRQ 0x1c
+#define NJ_DMA_WRITE_END 0x20
+#define NJ_DMA_WRITE_ADR 0x24
+#define NJ_PULSE_CNT 0x28
+
+#define NJ_ISAC_OFF 0xc0
+#define NJ_ISACIRQ 0x10
+
+#define NJ_IRQM0_RD_MASK 0x03
+#define NJ_IRQM0_RD_IRQ 0x01
+#define NJ_IRQM0_RD_END 0x02
+#define NJ_IRQM0_WR_MASK 0x0c
+#define NJ_IRQM0_WR_IRQ 0x04
+#define NJ_IRQM0_WR_END 0x08
+
+/* one page here is no need to be smaller */
+#define NJ_DMA_SIZE 4096
+/* 2 * 64 byte is a compromise between IRQ count and latency */
+#define NJ_DMA_RXSIZE 128 /* 2 * 64 */
+#define NJ_DMA_TXSIZE 128 /* 2 * 64 */
+
diff --git a/drivers/isdn/hardware/mISDN/speedfax.c b/drivers/isdn/hardware/mISDN/speedfax.c
new file mode 100644
index 00000000000..ff3a4e290da
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/speedfax.c
@@ -0,0 +1,526 @@
+/*
+ * speedfax.c low level stuff for Sedlbauer Speedfax+ cards
+ * based on the ISAR DSP
+ * Thanks to Sedlbauer AG for informations and HW
+ *
+ * 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 <linux/firmware.h>
+#include "ipac.h"
+#include "isar.h"
+
+#define SPEEDFAX_REV "2.0"
+
+#define PCI_SUBVENDOR_SPEEDFAX_PYRAMID 0x51
+#define PCI_SUBVENDOR_SPEEDFAX_PCI 0x54
+#define PCI_SUB_ID_SEDLBAUER 0x01
+
+#define SFAX_PCI_ADDR 0xc8
+#define SFAX_PCI_ISAC 0xd0
+#define SFAX_PCI_ISAR 0xe0
+
+/* TIGER 100 Registers */
+
+#define TIGER_RESET_ADDR 0x00
+#define TIGER_EXTERN_RESET_ON 0x01
+#define TIGER_EXTERN_RESET_OFF 0x00
+#define TIGER_AUX_CTRL 0x02
+#define TIGER_AUX_DATA 0x03
+#define TIGER_AUX_IRQMASK 0x05
+#define TIGER_AUX_STATUS 0x07
+
+/* Tiger AUX BITs */
+#define SFAX_AUX_IOMASK 0xdd /* 1 and 5 are inputs */
+#define SFAX_ISAR_RESET_BIT_OFF 0x00
+#define SFAX_ISAR_RESET_BIT_ON 0x01
+#define SFAX_TIGER_IRQ_BIT 0x02
+#define SFAX_LED1_BIT 0x08
+#define SFAX_LED2_BIT 0x10
+
+#define SFAX_PCI_RESET_ON (SFAX_ISAR_RESET_BIT_ON)
+#define SFAX_PCI_RESET_OFF (SFAX_LED1_BIT | SFAX_LED2_BIT)
+
+static int sfax_cnt;
+static u32 debug;
+static u32 irqloops = 4;
+
+struct sfax_hw {
+ struct list_head list;
+ struct pci_dev *pdev;
+ char name[MISDN_MAX_IDLEN];
+ u32 irq;
+ u32 irqcnt;
+ u32 cfg;
+ struct _ioport p_isac;
+ struct _ioport p_isar;
+ u8 aux_data;
+ spinlock_t lock; /* HW access lock */
+ struct isac_hw isac;
+ struct isar_hw isar;
+};
+
+static LIST_HEAD(Cards);
+static DEFINE_RWLOCK(card_lock); /* protect Cards */
+
+static void
+_set_debug(struct sfax_hw *card)
+{
+ card->isac.dch.debug = debug;
+ card->isar.ch[0].bch.debug = debug;
+ card->isar.ch[1].bch.debug = debug;
+}
+
+static int
+set_debug(const char *val, struct kernel_param *kp)
+{
+ int ret;
+ struct sfax_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(SPEEDFAX_REV);
+module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Speedfax debug mask");
+module_param(irqloops, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(irqloops, "Speedfax maximal irqloops (default 4)");
+
+IOFUNC_IND(ISAC, sfax_hw, p_isac)
+IOFUNC_IND(ISAR, sfax_hw, p_isar)
+
+static irqreturn_t
+speedfax_irq(int intno, void *dev_id)
+{
+ struct sfax_hw *sf = dev_id;
+ u8 val;
+ int cnt = irqloops;
+
+ spin_lock(&sf->lock);
+ val = inb(sf->cfg + TIGER_AUX_STATUS);
+ if (val & SFAX_TIGER_IRQ_BIT) { /* for us or shared ? */
+ spin_unlock(&sf->lock);
+ return IRQ_NONE; /* shared */
+ }
+ sf->irqcnt++;
+ val = ReadISAR_IND(sf, ISAR_IRQBIT);
+Start_ISAR:
+ if (val & ISAR_IRQSTA)
+ mISDNisar_irq(&sf->isar);
+ val = ReadISAC_IND(sf, ISAC_ISTA);
+ if (val)
+ mISDNisac_irq(&sf->isac, val);
+ val = ReadISAR_IND(sf, ISAR_IRQBIT);
+ if ((val & ISAR_IRQSTA) && cnt--)
+ goto Start_ISAR;
+ if (cnt < irqloops)
+ pr_debug("%s: %d irqloops cpu%d\n", sf->name,
+ irqloops - cnt, smp_processor_id());
+ if (irqloops && !cnt)
+ pr_notice("%s: %d IRQ LOOP cpu%d\n", sf->name,
+ irqloops, smp_processor_id());
+ spin_unlock(&sf->lock);
+ return IRQ_HANDLED;
+}
+
+static void
+enable_hwirq(struct sfax_hw *sf)
+{
+ WriteISAC_IND(sf, ISAC_MASK, 0);
+ WriteISAR_IND(sf, ISAR_IRQBIT, ISAR_IRQMSK);
+ outb(SFAX_TIGER_IRQ_BIT, sf->cfg + TIGER_AUX_IRQMASK);
+}
+
+static void
+disable_hwirq(struct sfax_hw *sf)
+{
+ WriteISAC_IND(sf, ISAC_MASK, 0xFF);
+ WriteISAR_IND(sf, ISAR_IRQBIT, 0);
+ outb(0, sf->cfg + TIGER_AUX_IRQMASK);
+}
+
+static void
+reset_speedfax(struct sfax_hw *sf)
+{
+
+ pr_debug("%s: resetting card\n", sf->name);
+ outb(TIGER_EXTERN_RESET_ON, sf->cfg + TIGER_RESET_ADDR);
+ outb(SFAX_PCI_RESET_ON, sf->cfg + TIGER_AUX_DATA);
+ mdelay(1);
+ outb(TIGER_EXTERN_RESET_OFF, sf->cfg + TIGER_RESET_ADDR);
+ sf->aux_data = SFAX_PCI_RESET_OFF;
+ outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA);
+ mdelay(1);
+}
+
+static int
+sfax_ctrl(struct sfax_hw *sf, u32 cmd, u_long arg)
+{
+ int ret = 0;
+
+ switch (cmd) {
+ case HW_RESET_REQ:
+ reset_speedfax(sf);
+ break;
+ case HW_ACTIVATE_IND:
+ if (arg & 1)
+ sf->aux_data &= ~SFAX_LED1_BIT;
+ if (arg & 2)
+ sf->aux_data &= ~SFAX_LED2_BIT;
+ outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA);
+ break;
+ case HW_DEACT_IND:
+ if (arg & 1)
+ sf->aux_data |= SFAX_LED1_BIT;
+ if (arg & 2)
+ sf->aux_data |= SFAX_LED2_BIT;
+ outb(sf->aux_data, sf->cfg + TIGER_AUX_DATA);
+ break;
+ default:
+ pr_info("%s: %s unknown command %x %lx\n",
+ sf->name, __func__, cmd, arg);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int
+channel_ctrl(struct sfax_hw *sf, 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 = sf->isac.ctrl(&sf->isac, HW_TESTLOOP, cq->channel);
+ break;
+ default:
+ pr_info("%s: unknown Op %x\n", sf->name, cq->op);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int
+sfax_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 sfax_hw *sf = dch->hw;
+ struct channel_req *rq;
+ int err = 0;
+
+ pr_debug("%s: cmd:%x %p\n", sf->name, cmd, arg);
+ switch (cmd) {
+ case OPEN_CHANNEL:
+ rq = arg;
+ if (rq->protocol == ISDN_P_TE_S0)
+ err = sf->isac.open(&sf->isac, rq);
+ else
+ err = sf->isar.open(&sf->isar, rq);
+ if (err)
+ break;
+ if (!try_module_get(THIS_MODULE))
+ pr_info("%s: cannot get module\n", sf->name);
+ break;
+ case CLOSE_CHANNEL:
+ pr_debug("%s: dev(%d) close from %p\n", sf->name,
+ dch->dev.id, __builtin_return_address(0));
+ module_put(THIS_MODULE);
+ break;
+ case CONTROL_CHANNEL:
+ err = channel_ctrl(sf, arg);
+ break;
+ default:
+ pr_debug("%s: unknown command %x\n", sf->name, cmd);
+ return -EINVAL;
+ }
+ return err;
+}
+
+static int __devinit
+init_card(struct sfax_hw *sf)
+{
+ int ret, cnt = 3;
+ u_long flags;
+
+ ret = request_irq(sf->irq, speedfax_irq, IRQF_SHARED, sf->name, sf);
+ if (ret) {
+ pr_info("%s: couldn't get interrupt %d\n", sf->name, sf->irq);
+ return ret;
+ }
+ while (cnt--) {
+ spin_lock_irqsave(&sf->lock, flags);
+ ret = sf->isac.init(&sf->isac);
+ if (ret) {
+ spin_unlock_irqrestore(&sf->lock, flags);
+ pr_info("%s: ISAC init failed with %d\n",
+ sf->name, ret);
+ break;
+ }
+ enable_hwirq(sf);
+ /* RESET Receiver and Transmitter */
+ WriteISAC_IND(sf, ISAC_CMDR, 0x41);
+ spin_unlock_irqrestore(&sf->lock, flags);
+ msleep_interruptible(10);
+ if (debug & DEBUG_HW)
+ pr_notice("%s: IRQ %d count %d\n", sf->name,
+ sf->irq, sf->irqcnt);
+ if (!sf->irqcnt) {
+ pr_info("%s: IRQ(%d) got no requests during init %d\n",
+ sf->name, sf->irq, 3 - cnt);
+ } else
+ return 0;
+ }
+ free_irq(sf->irq, sf);
+ return -EIO;
+}
+
+
+static int __devinit
+setup_speedfax(struct sfax_hw *sf)
+{
+ u_long flags;
+
+ if (!request_region(sf->cfg, 256, sf->name)) {
+ pr_info("mISDN: %s config port %x-%x already in use\n",
+ sf->name, sf->cfg, sf->cfg + 255);
+ return -EIO;
+ }
+ outb(0xff, sf->cfg);
+ outb(0, sf->cfg);
+ outb(0xdd, sf->cfg + TIGER_AUX_CTRL);
+ outb(0, sf->cfg + TIGER_AUX_IRQMASK);
+
+ sf->isac.type = IPAC_TYPE_ISAC;
+ sf->p_isac.ale = sf->cfg + SFAX_PCI_ADDR;
+ sf->p_isac.port = sf->cfg + SFAX_PCI_ISAC;
+ sf->p_isar.ale = sf->cfg + SFAX_PCI_ADDR;
+ sf->p_isar.port = sf->cfg + SFAX_PCI_ISAR;
+ ASSIGN_FUNC(IND, ISAC, sf->isac);
+ ASSIGN_FUNC(IND, ISAR, sf->isar);
+ spin_lock_irqsave(&sf->lock, flags);
+ reset_speedfax(sf);
+ disable_hwirq(sf);
+ spin_unlock_irqrestore(&sf->lock, flags);
+ return 0;
+}
+
+static void
+release_card(struct sfax_hw *card) {
+ u_long flags;
+
+ spin_lock_irqsave(&card->lock, flags);
+ disable_hwirq(card);
+ spin_unlock_irqrestore(&card->lock, flags);
+ card->isac.release(&card->isac);
+ free_irq(card->irq, card);
+ card->isar.release(&card->isar);
+ mISDN_unregister_device(&card->isac.dch.dev);
+ release_region(card->cfg, 256);
+ pci_disable_device(card->pdev);
+ pci_set_drvdata(card->pdev, NULL);
+ write_lock_irqsave(&card_lock, flags);
+ list_del(&card->list);
+ write_unlock_irqrestore(&card_lock, flags);
+ kfree(card);
+ sfax_cnt--;
+}
+
+static int __devinit
+setup_instance(struct sfax_hw *card)
+{
+ const struct firmware *firmware;
+ int i, err;
+ u_long flags;
+
+ snprintf(card->name, MISDN_MAX_IDLEN - 1, "Speedfax.%d", sfax_cnt + 1);
+ write_lock_irqsave(&card_lock, flags);
+ list_add_tail(&card->list, &Cards);
+ write_unlock_irqrestore(&card_lock, flags);
+ _set_debug(card);
+ spin_lock_init(&card->lock);
+ card->isac.hwlock = &card->lock;
+ card->isar.hwlock = &card->lock;
+ card->isar.ctrl = (void *)&sfax_ctrl;
+ card->isac.name = card->name;
+ card->isar.name = card->name;
+ card->isar.owner = THIS_MODULE;
+
+ err = request_firmware(&firmware, "isdn/ISAR.BIN", &card->pdev->dev);
+ if (err < 0) {
+ pr_info("%s: firmware request failed %d\n",
+ card->name, err);
+ goto error_fw;
+ }
+ if (debug & DEBUG_HW)
+ pr_notice("%s: got firmware %zu bytes\n",
+ card->name, firmware->size);
+
+ mISDNisac_init(&card->isac, card);
+
+ card->isac.dch.dev.D.ctrl = sfax_dctrl;
+ card->isac.dch.dev.Bprotocols =
+ mISDNisar_init(&card->isar, card);
+ for (i = 0; i < 2; i++) {
+ set_channelmap(i + 1, card->isac.dch.dev.channelmap);
+ list_add(&card->isar.ch[i].bch.ch.list,
+ &card->isac.dch.dev.bchannels);
+ }
+
+ err = setup_speedfax(card);
+ if (err)
+ goto error_setup;
+ err = card->isar.init(&card->isar);
+ if (err)
+ goto error;
+ err = mISDN_register_device(&card->isac.dch.dev,
+ &card->pdev->dev, card->name);
+ if (err)
+ goto error;
+ err = init_card(card);
+ if (err)
+ goto error_init;
+ err = card->isar.firmware(&card->isar, firmware->data, firmware->size);
+ if (!err) {
+ release_firmware(firmware);
+ sfax_cnt++;
+ pr_notice("SpeedFax %d cards installed\n", sfax_cnt);
+ return 0;
+ }
+ disable_hwirq(card);
+ free_irq(card->irq, card);
+error_init:
+ mISDN_unregister_device(&card->isac.dch.dev);
+error:
+ release_region(card->cfg, 256);
+error_setup:
+ card->isac.release(&card->isac);
+ card->isar.release(&card->isar);
+ release_firmware(firmware);
+error_fw:
+ pci_disable_device(card->pdev);
+ write_lock_irqsave(&card_lock, flags);
+ list_del(&card->list);
+ write_unlock_irqrestore(&card_lock, flags);
+ kfree(card);
+ return err;
+}
+
+static int __devinit
+sfaxpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ int err = -ENOMEM;
+ struct sfax_hw *card = kzalloc(sizeof(struct sfax_hw), GFP_KERNEL);
+
+ if (!card) {
+ pr_info("No memory for Speedfax+ PCI\n");
+ return err;
+ }
+ card->pdev = pdev;
+ err = pci_enable_device(pdev);
+ if (err) {
+ kfree(card);
+ return err;
+ }
+
+ pr_notice("mISDN: Speedfax found adapter %s at %s\n",
+ (char *)ent->driver_data, pci_name(pdev));
+
+ card->cfg = pci_resource_start(pdev, 0);
+ card->irq = pdev->irq;
+ pci_set_drvdata(pdev, card);
+ err = setup_instance(card);
+ if (err)
+ pci_set_drvdata(pdev, NULL);
+ return err;
+}
+
+static void __devexit
+sfax_remove_pci(struct pci_dev *pdev)
+{
+ struct sfax_hw *card = pci_get_drvdata(pdev);
+
+ if (card)
+ release_card(card);
+ else
+ pr_debug("%s: drvdata allready removed\n", __func__);
+}
+
+static struct pci_device_id sfaxpci_ids[] __devinitdata = {
+ { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100,
+ PCI_SUBVENDOR_SPEEDFAX_PYRAMID, PCI_SUB_ID_SEDLBAUER,
+ 0, 0, (unsigned long) "Pyramid Speedfax + PCI"
+ },
+ { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100,
+ PCI_SUBVENDOR_SPEEDFAX_PCI, PCI_SUB_ID_SEDLBAUER,
+ 0, 0, (unsigned long) "Sedlbauer Speedfax + PCI"
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(pci, sfaxpci_ids);
+
+static struct pci_driver sfaxpci_driver = {
+ .name = "speedfax+ pci",
+ .probe = sfaxpci_probe,
+ .remove = __devexit_p(sfax_remove_pci),
+ .id_table = sfaxpci_ids,
+};
+
+static int __init
+Speedfax_init(void)
+{
+ int err;
+
+ pr_notice("Sedlbauer Speedfax+ Driver Rev. %s\n",
+ SPEEDFAX_REV);
+ err = pci_register_driver(&sfaxpci_driver);
+ return err;
+}
+
+static void __exit
+Speedfax_cleanup(void)
+{
+ pci_unregister_driver(&sfaxpci_driver);
+}
+
+module_init(Speedfax_init);
+module_exit(Speedfax_cleanup);
diff --git a/drivers/isdn/hardware/mISDN/w6692.c b/drivers/isdn/hardware/mISDN/w6692.c
new file mode 100644
index 00000000000..d3f1077b709
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/w6692.c
@@ -0,0 +1,1440 @@
+/*
+ * w6692.c mISDN driver for Winbond w6692 based cards
+ *
+ * Author Karsten Keil <kkeil@suse.de>
+ * based on the w6692 I4L driver from Petr Novak <petr.novak@i.cz>
+ *
+ * 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 "w6692.h"
+
+#define W6692_REV "2.0"
+
+#define DBUSY_TIMER_VALUE 80
+
+enum {
+ W6692_ASUS,
+ W6692_WINBOND,
+ W6692_USR
+};
+
+/* private data in the PCI devices list */
+struct w6692map {
+ u_int subtype;
+ char *name;
+};
+
+static const struct w6692map w6692_map[] =
+{
+ {W6692_ASUS, "Dynalink/AsusCom IS64PH"},
+ {W6692_WINBOND, "Winbond W6692"},
+ {W6692_USR, "USR W6692"}
+};
+
+#ifndef PCI_VENDOR_ID_USR
+#define PCI_VENDOR_ID_USR 0x16ec
+#define PCI_DEVICE_ID_USR_6692 0x3409
+#endif
+
+struct w6692_ch {
+ struct bchannel bch;
+ u32 addr;
+ struct timer_list timer;
+ u8 b_mode;
+};
+
+struct w6692_hw {
+ struct list_head list;
+ struct pci_dev *pdev;
+ char name[MISDN_MAX_IDLEN];
+ u32 irq;
+ u32 irqcnt;
+ u32 addr;
+ u32 fmask; /* feature mask - bit set per card nr */
+ int subtype;
+ spinlock_t lock; /* hw lock */
+ u8 imask;
+ u8 pctl;
+ u8 xaddr;
+ u8 xdata;
+ u8 state;
+ struct w6692_ch bc[2];
+ struct dchannel dch;
+ char log[64];
+};
+
+static LIST_HEAD(Cards);
+static DEFINE_RWLOCK(card_lock); /* protect Cards */
+
+static int w6692_cnt;
+static int debug;
+static u32 led;
+static u32 pots;
+
+static void
+_set_debug(struct w6692_hw *card)
+{
+ card->dch.debug = debug;
+ card->bc[0].bch.debug = debug;
+ card->bc[1].bch.debug = debug;
+}
+
+static int
+set_debug(const char *val, struct kernel_param *kp)
+{
+ int ret;
+ struct w6692_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(W6692_REV);
+module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "W6692 debug mask");
+module_param(led, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(led, "W6692 LED support bitmask (one bit per card)");
+module_param(pots, uint, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(pots, "W6692 POTS support bitmask (one bit per card)");
+
+static inline u8
+ReadW6692(struct w6692_hw *card, u8 offset)
+{
+ return inb(card->addr + offset);
+}
+
+static inline void
+WriteW6692(struct w6692_hw *card, u8 offset, u8 value)
+{
+ outb(value, card->addr + offset);
+}
+
+static inline u8
+ReadW6692B(struct w6692_ch *bc, u8 offset)
+{
+ return inb(bc->addr + offset);
+}
+
+static inline void
+WriteW6692B(struct w6692_ch *bc, u8 offset, u8 value)
+{
+ outb(value, bc->addr + offset);
+}
+
+static void
+enable_hwirq(struct w6692_hw *card)
+{
+ WriteW6692(card, W_IMASK, card->imask);
+}
+
+static void
+disable_hwirq(struct w6692_hw *card)
+{
+ WriteW6692(card, W_IMASK, 0xff);
+}
+
+static const char *W6692Ver[] = {"V00", "V01", "V10", "V11"};
+
+static void
+W6692Version(struct w6692_hw *card)
+{
+ int val;
+
+ val = ReadW6692(card, W_D_RBCH);
+ pr_notice("%s: Winbond W6692 version: %s\n", card->name,
+ W6692Ver[(val >> 6) & 3]);
+}
+
+static void
+w6692_led_handler(struct w6692_hw *card, int on)
+{
+ if ((!(card->fmask & led)) || card->subtype == W6692_USR)
+ return;
+ if (on) {
+ card->xdata &= 0xfb; /* LED ON */
+ WriteW6692(card, W_XDATA, card->xdata);
+ } else {
+ card->xdata |= 0x04; /* LED OFF */
+ WriteW6692(card, W_XDATA, card->xdata);
+ }
+}
+
+static void
+ph_command(struct w6692_hw *card, u8 cmd)
+{
+ pr_debug("%s: ph_command %x\n", card->name, cmd);
+ WriteW6692(card, W_CIX, cmd);
+}
+
+static void
+W6692_new_ph(struct w6692_hw *card)
+{
+ if (card->state == W_L1CMD_RST)
+ ph_command(card, W_L1CMD_DRC);
+ schedule_event(&card->dch, FLG_PHCHANGE);
+}
+
+static void
+W6692_ph_bh(struct dchannel *dch)
+{
+ struct w6692_hw *card = dch->hw;
+
+ switch (card->state) {
+ case W_L1CMD_RST:
+ dch->state = 0;
+ l1_event(dch->l1, HW_RESET_IND);
+ break;
+ case W_L1IND_CD:
+ dch->state = 3;
+ l1_event(dch->l1, HW_DEACT_CNF);
+ break;
+ case W_L1IND_DRD:
+ dch->state = 3;
+ l1_event(dch->l1, HW_DEACT_IND);
+ break;
+ case W_L1IND_CE:
+ dch->state = 4;
+ l1_event(dch->l1, HW_POWERUP_IND);
+ break;
+ case W_L1IND_LD:
+ if (dch->state <= 5) {
+ dch->state = 5;
+ l1_event(dch->l1, ANYSIGNAL);
+ } else {
+ dch->state = 8;
+ l1_event(dch->l1, LOSTFRAMING);
+ }
+ break;
+ case W_L1IND_ARD:
+ dch->state = 6;
+ l1_event(dch->l1, INFO2);
+ break;
+ case W_L1IND_AI8:
+ dch->state = 7;
+ l1_event(dch->l1, INFO4_P8);
+ break;
+ case W_L1IND_AI10:
+ dch->state = 7;
+ l1_event(dch->l1, INFO4_P10);
+ break;
+ default:
+ pr_debug("%s: TE unknown state %02x dch state %02x\n",
+ card->name, card->state, dch->state);
+ break;
+ }
+ pr_debug("%s: TE newstate %02x\n", card->name, dch->state);
+}
+
+static void
+W6692_empty_Dfifo(struct w6692_hw *card, int count)
+{
+ struct dchannel *dch = &card->dch;
+ u8 *ptr;
+
+ pr_debug("%s: empty_Dfifo %d\n", card->name, count);
+ if (!dch->rx_skb) {
+ dch->rx_skb = mI_alloc_skb(card->dch.maxlen, GFP_ATOMIC);
+ if (!dch->rx_skb) {
+ pr_info("%s: D receive out of memory\n", card->name);
+ WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK);
+ return;
+ }
+ }
+ if ((dch->rx_skb->len + count) >= dch->maxlen) {
+ pr_debug("%s: empty_Dfifo overrun %d\n", card->name,
+ dch->rx_skb->len + count);
+ WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK);
+ return;
+ }
+ ptr = skb_put(dch->rx_skb, count);
+ insb(card->addr + W_D_RFIFO, ptr, count);
+ WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK);
+ if (debug & DEBUG_HW_DFIFO) {
+ snprintf(card->log, 63, "D-recv %s %d ",
+ card->name, count);
+ print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count);
+ }
+}
+
+static void
+W6692_fill_Dfifo(struct w6692_hw *card)
+{
+ struct dchannel *dch = &card->dch;
+ int count;
+ u8 *ptr;
+ u8 cmd = W_D_CMDR_XMS;
+
+ pr_debug("%s: fill_Dfifo\n", card->name);
+ if (!dch->tx_skb)
+ return;
+ count = dch->tx_skb->len - dch->tx_idx;
+ if (count <= 0)
+ return;
+ if (count > W_D_FIFO_THRESH)
+ count = W_D_FIFO_THRESH;
+ else
+ cmd |= W_D_CMDR_XME;
+ ptr = dch->tx_skb->data + dch->tx_idx;
+ dch->tx_idx += count;
+ outsb(card->addr + W_D_XFIFO, ptr, count);
+ WriteW6692(card, W_D_CMDR, cmd);
+ if (test_and_set_bit(FLG_BUSY_TIMER, &dch->Flags)) {
+ pr_debug("%s: fill_Dfifo dbusytimer running\n", card->name);
+ del_timer(&dch->timer);
+ }
+ init_timer(&dch->timer);
+ dch->timer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
+ add_timer(&dch->timer);
+ if (debug & DEBUG_HW_DFIFO) {
+ snprintf(card->log, 63, "D-send %s %d ",
+ card->name, count);
+ print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count);
+ }
+}
+
+static void
+d_retransmit(struct w6692_hw *card)
+{
+ struct dchannel *dch = &card->dch;
+
+ if (test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags))
+ del_timer(&dch->timer);
+#ifdef FIXME
+ if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags))
+ dchannel_sched_event(dch, D_CLEARBUSY);
+#endif
+ if (test_bit(FLG_TX_BUSY, &dch->Flags)) {
+ /* Restart frame */
+ dch->tx_idx = 0;
+ W6692_fill_Dfifo(card);
+ } else if (dch->tx_skb) { /* should not happen */
+ pr_info("%s: %s without TX_BUSY\n", card->name, __func__);
+ test_and_set_bit(FLG_TX_BUSY, &dch->Flags);
+ dch->tx_idx = 0;
+ W6692_fill_Dfifo(card);
+ } else {
+ pr_info("%s: XDU no TX_BUSY\n", card->name);
+ if (get_next_dframe(dch))
+ W6692_fill_Dfifo(card);
+ }
+}
+
+static void
+handle_rxD(struct w6692_hw *card) {
+ u8 stat;
+ int count;
+
+ stat = ReadW6692(card, W_D_RSTA);
+ if (stat & (W_D_RSTA_RDOV | W_D_RSTA_CRCE | W_D_RSTA_RMB)) {
+ if (stat & W_D_RSTA_RDOV) {
+ pr_debug("%s: D-channel RDOV\n", card->name);
+#ifdef ERROR_STATISTIC
+ card->dch.err_rx++;
+#endif
+ }
+ if (stat & W_D_RSTA_CRCE) {
+ pr_debug("%s: D-channel CRC error\n", card->name);
+#ifdef ERROR_STATISTIC
+ card->dch.err_crc++;
+#endif
+ }
+ if (stat & W_D_RSTA_RMB) {
+ pr_debug("%s: D-channel ABORT\n", card->name);
+#ifdef ERROR_STATISTIC
+ card->dch.err_rx++;
+#endif
+ }
+ if (card->dch.rx_skb)
+ dev_kfree_skb(card->dch.rx_skb);
+ card->dch.rx_skb = NULL;
+ WriteW6692(card, W_D_CMDR, W_D_CMDR_RACK | W_D_CMDR_RRST);
+ } else {
+ count = ReadW6692(card, W_D_RBCL) & (W_D_FIFO_THRESH - 1);
+ if (count == 0)
+ count = W_D_FIFO_THRESH;
+ W6692_empty_Dfifo(card, count);
+ recv_Dchannel(&card->dch);
+ }
+}
+
+static void
+handle_txD(struct w6692_hw *card) {
+ if (test_and_clear_bit(FLG_BUSY_TIMER, &card->dch.Flags))
+ del_timer(&card->dch.timer);
+ if (card->dch.tx_skb && card->dch.tx_idx < card->dch.tx_skb->len) {
+ W6692_fill_Dfifo(card);
+ } else {
+ if (card->dch.tx_skb)
+ dev_kfree_skb(card->dch.tx_skb);
+ if (get_next_dframe(&card->dch))
+ W6692_fill_Dfifo(card);
+ }
+}
+
+static void
+handle_statusD(struct w6692_hw *card)
+{
+ struct dchannel *dch = &card->dch;
+ u8 exval, v1, cir;
+
+ exval = ReadW6692(card, W_D_EXIR);
+
+ pr_debug("%s: D_EXIR %02x\n", card->name, exval);
+ if (exval & (W_D_EXI_XDUN | W_D_EXI_XCOL)) {
+ /* Transmit underrun/collision */
+ pr_debug("%s: D-channel underrun/collision\n", card->name);
+#ifdef ERROR_STATISTIC
+ dch->err_tx++;
+#endif
+ d_retransmit(card);
+ }
+ if (exval & W_D_EXI_RDOV) { /* RDOV */
+ pr_debug("%s: D-channel RDOV\n", card->name);
+ WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST);
+ }
+ if (exval & W_D_EXI_TIN2) /* TIN2 - never */
+ pr_debug("%s: spurious TIN2 interrupt\n", card->name);
+ if (exval & W_D_EXI_MOC) { /* MOC - not supported */
+ v1 = ReadW6692(card, W_MOSR);
+ pr_debug("%s: spurious MOC interrupt MOSR %02x\n",
+ card->name, v1);
+ }
+ if (exval & W_D_EXI_ISC) { /* ISC - Level1 change */
+ cir = ReadW6692(card, W_CIR);
+ pr_debug("%s: ISC CIR %02X\n", card->name, cir);
+ if (cir & W_CIR_ICC) {
+ v1 = cir & W_CIR_COD_MASK;
+ pr_debug("%s: ph_state_change %x -> %x\n", card->name,
+ dch->state, v1);
+ card->state = v1;
+ if (card->fmask & led) {
+ switch (v1) {
+ case W_L1IND_AI8:
+ case W_L1IND_AI10:
+ w6692_led_handler(card, 1);
+ break;
+ default:
+ w6692_led_handler(card, 0);
+ break;
+ }
+ }
+ W6692_new_ph(card);
+ }
+ if (cir & W_CIR_SCC) {
+ v1 = ReadW6692(card, W_SQR);
+ pr_debug("%s: SCC SQR %02X\n", card->name, v1);
+ }
+ }
+ if (exval & W_D_EXI_WEXP)
+ pr_debug("%s: spurious WEXP interrupt!\n", card->name);
+ if (exval & W_D_EXI_TEXP)
+ pr_debug("%s: spurious TEXP interrupt!\n", card->name);
+}
+
+static void
+W6692_empty_Bfifo(struct w6692_ch *wch, int count)
+{
+ struct w6692_hw *card = wch->bch.hw;
+ u8 *ptr;
+
+ pr_debug("%s: empty_Bfifo %d\n", card->name, count);
+ if (unlikely(wch->bch.state == ISDN_P_NONE)) {
+ pr_debug("%s: empty_Bfifo ISDN_P_NONE\n", card->name);
+ WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
+ if (wch->bch.rx_skb)
+ skb_trim(wch->bch.rx_skb, 0);
+ return;
+ }
+ if (!wch->bch.rx_skb) {
+ wch->bch.rx_skb = mI_alloc_skb(wch->bch.maxlen, GFP_ATOMIC);
+ if (unlikely(!wch->bch.rx_skb)) {
+ pr_info("%s: B receive out of memory\n", card->name);
+ WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK |
+ W_B_CMDR_RACT);
+ return;
+ }
+ }
+ if (wch->bch.rx_skb->len + count > wch->bch.maxlen) {
+ pr_debug("%s: empty_Bfifo incoming packet too large\n",
+ card->name);
+ WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
+ skb_trim(wch->bch.rx_skb, 0);
+ return;
+ }
+ ptr = skb_put(wch->bch.rx_skb, count);
+ insb(wch->addr + W_B_RFIFO, ptr, count);
+ WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT);
+ if (debug & DEBUG_HW_DFIFO) {
+ snprintf(card->log, 63, "B%1d-recv %s %d ",
+ wch->bch.nr, card->name, count);
+ print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count);
+ }
+}
+
+static void
+W6692_fill_Bfifo(struct w6692_ch *wch)
+{
+ struct w6692_hw *card = wch->bch.hw;
+ int count;
+ u8 *ptr, cmd = W_B_CMDR_RACT | W_B_CMDR_XMS;
+
+ pr_debug("%s: fill Bfifo\n", card->name);
+ if (!wch->bch.tx_skb)
+ return;
+ count = wch->bch.tx_skb->len - wch->bch.tx_idx;
+ if (count <= 0)
+ return;
+ ptr = wch->bch.tx_skb->data + wch->bch.tx_idx;
+ if (count > W_B_FIFO_THRESH)
+ count = W_B_FIFO_THRESH;
+ else if (test_bit(FLG_HDLC, &wch->bch.Flags))
+ cmd |= W_B_CMDR_XME;
+
+ pr_debug("%s: fill Bfifo%d/%d\n", card->name,
+ count, wch->bch.tx_idx);
+ wch->bch.tx_idx += count;
+ outsb(wch->addr + W_B_XFIFO, ptr, count);
+ WriteW6692B(wch, W_B_CMDR, cmd);
+ if (debug & DEBUG_HW_DFIFO) {
+ snprintf(card->log, 63, "B%1d-send %s %d ",
+ wch->bch.nr, card->name, count);
+ print_hex_dump_bytes(card->log, DUMP_PREFIX_OFFSET, ptr, count);
+ }
+}
+
+static int
+setvolume(struct w6692_ch *wch, int mic, struct sk_buff *skb)
+{
+ struct w6692_hw *card = wch->bch.hw;
+ u16 *vol = (u16 *)skb->data;
+ u8 val;
+
+ if ((!(card->fmask & pots)) ||
+ !test_bit(FLG_TRANSPARENT, &wch->bch.Flags))
+ return -ENODEV;
+ if (skb->len < 2)
+ return -EINVAL;
+ if (*vol > 7)
+ return -EINVAL;
+ val = *vol & 7;
+ val = 7 - val;
+ if (mic) {
+ val <<= 3;
+ card->xaddr &= 0xc7;
+ } else {
+ card->xaddr &= 0xf8;
+ }
+ card->xaddr |= val;
+ WriteW6692(card, W_XADDR, card->xaddr);
+ return 0;
+}
+
+static int
+enable_pots(struct w6692_ch *wch)
+{
+ struct w6692_hw *card = wch->bch.hw;
+
+ if ((!(card->fmask & pots)) ||
+ !test_bit(FLG_TRANSPARENT, &wch->bch.Flags))
+ return -ENODEV;
+ wch->b_mode |= W_B_MODE_EPCM | W_B_MODE_BSW0;
+ WriteW6692B(wch, W_B_MODE, wch->b_mode);
+ WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
+ card->pctl |= ((wch->bch.nr & 2) ? W_PCTL_PCX : 0);
+ WriteW6692(card, W_PCTL, card->pctl);
+ return 0;
+}
+
+static int
+disable_pots(struct w6692_ch *wch)
+{
+ struct w6692_hw *card = wch->bch.hw;
+
+ if (!(card->fmask & pots))
+ return -ENODEV;
+ wch->b_mode &= ~(W_B_MODE_EPCM | W_B_MODE_BSW0);
+ WriteW6692B(wch, W_B_MODE, wch->b_mode);
+ WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT |
+ W_B_CMDR_XRST);
+ return 0;
+}
+
+static int
+w6692_mode(struct w6692_ch *wch, u32 pr)
+{
+ struct w6692_hw *card;
+
+ card = wch->bch.hw;
+ pr_debug("%s: B%d protocol %x-->%x\n", card->name,
+ wch->bch.nr, wch->bch.state, pr);
+ switch (pr) {
+ case ISDN_P_NONE:
+ if ((card->fmask & pots) && (wch->b_mode & W_B_MODE_EPCM))
+ disable_pots(wch);
+ wch->b_mode = 0;
+ mISDN_clear_bchannel(&wch->bch);
+ WriteW6692B(wch, W_B_MODE, wch->b_mode);
+ WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
+ test_and_clear_bit(FLG_HDLC, &wch->bch.Flags);
+ test_and_clear_bit(FLG_TRANSPARENT, &wch->bch.Flags);
+ break;
+ case ISDN_P_B_RAW:
+ wch->b_mode = W_B_MODE_MMS;
+ WriteW6692B(wch, W_B_MODE, wch->b_mode);
+ WriteW6692B(wch, W_B_EXIM, 0);
+ WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT |
+ W_B_CMDR_XRST);
+ test_and_set_bit(FLG_TRANSPARENT, &wch->bch.Flags);
+ break;
+ case ISDN_P_B_HDLC:
+ wch->b_mode = W_B_MODE_ITF;
+ WriteW6692B(wch, W_B_MODE, wch->b_mode);
+ WriteW6692B(wch, W_B_ADM1, 0xff);
+ WriteW6692B(wch, W_B_ADM2, 0xff);
+ WriteW6692B(wch, W_B_EXIM, 0);
+ WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_RACT |
+ W_B_CMDR_XRST);
+ test_and_set_bit(FLG_HDLC, &wch->bch.Flags);
+ break;
+ default:
+ pr_info("%s: protocol %x not known\n", card->name, pr);
+ return -ENOPROTOOPT;
+ }
+ wch->bch.state = pr;
+ return 0;
+}
+
+static void
+send_next(struct w6692_ch *wch)
+{
+ if (wch->bch.tx_skb && wch->bch.tx_idx < wch->bch.tx_skb->len)
+ W6692_fill_Bfifo(wch);
+ else {
+ if (wch->bch.tx_skb) {
+ /* send confirm, on trans, free on hdlc. */
+ if (test_bit(FLG_TRANSPARENT, &wch->bch.Flags))
+ confirm_Bsend(&wch->bch);
+ dev_kfree_skb(wch->bch.tx_skb);
+ }
+ if (get_next_bframe(&wch->bch))
+ W6692_fill_Bfifo(wch);
+ }
+}
+
+static void
+W6692B_interrupt(struct w6692_hw *card, int ch)
+{
+ struct w6692_ch *wch = &card->bc[ch];
+ int count;
+ u8 stat, star = 0;
+
+ stat = ReadW6692B(wch, W_B_EXIR);
+ pr_debug("%s: B%d EXIR %02x\n", card->name, wch->bch.nr, stat);
+ if (stat & W_B_EXI_RME) {
+ star = ReadW6692B(wch, W_B_STAR);
+ if (star & (W_B_STAR_RDOV | W_B_STAR_CRCE | W_B_STAR_RMB)) {
+ if ((star & W_B_STAR_RDOV) &&
+ test_bit(FLG_ACTIVE, &wch->bch.Flags)) {
+ pr_debug("%s: B%d RDOV proto=%x\n", card->name,
+ wch->bch.nr, wch->bch.state);
+#ifdef ERROR_STATISTIC
+ wch->bch.err_rdo++;
+#endif
+ }
+ if (test_bit(FLG_HDLC, &wch->bch.Flags)) {
+ if (star & W_B_STAR_CRCE) {
+ pr_debug("%s: B%d CRC error\n",
+ card->name, wch->bch.nr);
+#ifdef ERROR_STATISTIC
+ wch->bch.err_crc++;
+#endif
+ }
+ if (star & W_B_STAR_RMB) {
+ pr_debug("%s: B%d message abort\n",
+ card->name, wch->bch.nr);
+#ifdef ERROR_STATISTIC
+ wch->bch.err_inv++;
+#endif
+ }
+ }
+ WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK |
+ W_B_CMDR_RRST | W_B_CMDR_RACT);
+ if (wch->bch.rx_skb)
+ skb_trim(wch->bch.rx_skb, 0);
+ } else {
+ count = ReadW6692B(wch, W_B_RBCL) &
+ (W_B_FIFO_THRESH - 1);
+ if (count == 0)
+ count = W_B_FIFO_THRESH;
+ W6692_empty_Bfifo(wch, count);
+ recv_Bchannel(&wch->bch, 0);
+ }
+ }
+ if (stat & W_B_EXI_RMR) {
+ if (!(stat & W_B_EXI_RME))
+ star = ReadW6692B(wch, W_B_STAR);
+ if (star & W_B_STAR_RDOV) {
+ pr_debug("%s: B%d RDOV proto=%x\n", card->name,
+ wch->bch.nr, wch->bch.state);
+#ifdef ERROR_STATISTIC
+ wch->bch.err_rdo++;
+#endif
+ WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK |
+ W_B_CMDR_RRST | W_B_CMDR_RACT);
+ } else {
+ W6692_empty_Bfifo(wch, W_B_FIFO_THRESH);
+ if (test_bit(FLG_TRANSPARENT, &wch->bch.Flags) &&
+ wch->bch.rx_skb && (wch->bch.rx_skb->len > 0))
+ recv_Bchannel(&wch->bch, 0);
+ }
+ }
+ if (stat & W_B_EXI_RDOV) {
+ /* only if it is not handled yet */
+ if (!(star & W_B_STAR_RDOV)) {
+ pr_debug("%s: B%d RDOV IRQ proto=%x\n", card->name,
+ wch->bch.nr, wch->bch.state);
+#ifdef ERROR_STATISTIC
+ wch->bch.err_rdo++;
+#endif
+ WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK |
+ W_B_CMDR_RRST | W_B_CMDR_RACT);
+ }
+ }
+ if (stat & W_B_EXI_XFR) {
+ if (!(stat & (W_B_EXI_RME | W_B_EXI_RMR))) {
+ star = ReadW6692B(wch, W_B_STAR);
+ pr_debug("%s: B%d star %02x\n", card->name,
+ wch->bch.nr, star);
+ }
+ if (star & W_B_STAR_XDOW) {
+ pr_debug("%s: B%d XDOW proto=%x\n", card->name,
+ wch->bch.nr, wch->bch.state);
+#ifdef ERROR_STATISTIC
+ wch->bch.err_xdu++;
+#endif
+ WriteW6692B(wch, W_B_CMDR, W_B_CMDR_XRST |
+ W_B_CMDR_RACT);
+ /* resend */
+ if (wch->bch.tx_skb) {
+ if (!test_bit(FLG_TRANSPARENT, &wch->bch.Flags))
+ wch->bch.tx_idx = 0;
+ }
+ }
+ send_next(wch);
+ if (stat & W_B_EXI_XDUN)
+ return; /* handle XDOW only once */
+ }
+ if (stat & W_B_EXI_XDUN) {
+ pr_debug("%s: B%d XDUN proto=%x\n", card->name,
+ wch->bch.nr, wch->bch.state);
+#ifdef ERROR_STATISTIC
+ wch->bch.err_xdu++;
+#endif
+ WriteW6692B(wch, W_B_CMDR, W_B_CMDR_XRST | W_B_CMDR_RACT);
+ /* resend */
+ if (wch->bch.tx_skb) {
+ if (!test_bit(FLG_TRANSPARENT, &wch->bch.Flags))
+ wch->bch.tx_idx = 0;
+ }
+ send_next(wch);
+ }
+}
+
+static irqreturn_t
+w6692_irq(int intno, void *dev_id)
+{
+ struct w6692_hw *card = dev_id;
+ u8 ista;
+
+ spin_lock(&card->lock);
+ ista = ReadW6692(card, W_ISTA);
+ if ((ista | card->imask) == card->imask) {
+ /* possible a shared IRQ reqest */
+ spin_unlock(&card->lock);
+ return IRQ_NONE;
+ }
+ card->irqcnt++;
+ pr_debug("%s: ista %02x\n", card->name, ista);
+ ista &= ~card->imask;
+ if (ista & W_INT_B1_EXI)
+ W6692B_interrupt(card, 0);
+ if (ista & W_INT_B2_EXI)
+ W6692B_interrupt(card, 1);
+ if (ista & W_INT_D_RME)
+ handle_rxD(card);
+ if (ista & W_INT_D_RMR)
+ W6692_empty_Dfifo(card, W_D_FIFO_THRESH);
+ if (ista & W_INT_D_XFR)
+ handle_txD(card);
+ if (ista & W_INT_D_EXI)
+ handle_statusD(card);
+ if (ista & (W_INT_XINT0 | W_INT_XINT1)) /* XINT0/1 - never */
+ pr_debug("%s: W6692 spurious XINT!\n", card->name);
+/* End IRQ Handler */
+ spin_unlock(&card->lock);
+ return IRQ_HANDLED;
+}
+
+static void
+dbusy_timer_handler(struct dchannel *dch)
+{
+ struct w6692_hw *card = dch->hw;
+ int rbch, star;
+ u_long flags;
+
+ if (test_bit(FLG_BUSY_TIMER, &dch->Flags)) {
+ spin_lock_irqsave(&card->lock, flags);
+ rbch = ReadW6692(card, W_D_RBCH);
+ star = ReadW6692(card, W_D_STAR);
+ pr_debug("%s: D-Channel Busy RBCH %02x STAR %02x\n",
+ card->name, rbch, star);
+ if (star & W_D_STAR_XBZ) /* D-Channel Busy */
+ test_and_set_bit(FLG_L1_BUSY, &dch->Flags);
+ else {
+ /* discard frame; reset transceiver */
+ test_and_clear_bit(FLG_BUSY_TIMER, &dch->Flags);
+ if (dch->tx_idx)
+ dch->tx_idx = 0;
+ else
+ pr_info("%s: W6692 D-Channel Busy no tx_idx\n",
+ card->name);
+ /* Transmitter reset */
+ WriteW6692(card, W_D_CMDR, W_D_CMDR_XRST);
+ }
+ spin_unlock_irqrestore(&card->lock, flags);
+ }
+}
+
+void initW6692(struct w6692_hw *card)
+{
+ u8 val;
+
+ card->dch.timer.function = (void *)dbusy_timer_handler;
+ card->dch.timer.data = (u_long)&card->dch;
+ init_timer(&card->dch.timer);
+ w6692_mode(&card->bc[0], ISDN_P_NONE);
+ w6692_mode(&card->bc[1], ISDN_P_NONE);
+ WriteW6692(card, W_D_CTL, 0x00);
+ disable_hwirq(card);
+ WriteW6692(card, W_D_SAM, 0xff);
+ WriteW6692(card, W_D_TAM, 0xff);
+ WriteW6692(card, W_D_MODE, W_D_MODE_RACT);
+ card->state = W_L1CMD_RST;
+ ph_command(card, W_L1CMD_RST);
+ ph_command(card, W_L1CMD_ECK);
+ /* enable all IRQ but extern */
+ card->imask = 0x18;
+ WriteW6692(card, W_D_EXIM, 0x00);
+ WriteW6692B(&card->bc[0], W_B_EXIM, 0);
+ WriteW6692B(&card->bc[1], W_B_EXIM, 0);
+ /* Reset D-chan receiver and transmitter */
+ WriteW6692(card, W_D_CMDR, W_D_CMDR_RRST | W_D_CMDR_XRST);
+ /* Reset B-chan receiver and transmitter */
+ WriteW6692B(&card->bc[0], W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
+ WriteW6692B(&card->bc[1], W_B_CMDR, W_B_CMDR_RRST | W_B_CMDR_XRST);
+ /* enable peripheral */
+ if (card->subtype == W6692_USR) {
+ /* seems that USR implemented some power control features
+ * Pin 79 is connected to the oscilator circuit so we
+ * have to handle it here
+ */
+ card->pctl = 0x80;
+ card->xdata = 0;
+ WriteW6692(card, W_PCTL, card->pctl);
+ WriteW6692(card, W_XDATA, card->xdata);
+ } else {
+ card->pctl = W_PCTL_OE5 | W_PCTL_OE4 | W_PCTL_OE2 |
+ W_PCTL_OE1 | W_PCTL_OE0;
+ card->xaddr = 0x00;/* all sw off */
+ if (card->fmask & pots)
+ card->xdata |= 0x06; /* POWER UP/ LED OFF / ALAW */
+ if (card->fmask & led)
+ card->xdata |= 0x04; /* LED OFF */
+ if ((card->fmask & pots) || (card->fmask & led)) {
+ WriteW6692(card, W_PCTL, card->pctl);
+ WriteW6692(card, W_XADDR, card->xaddr);
+ WriteW6692(card, W_XDATA, card->xdata);
+ val = ReadW6692(card, W_XADDR);
+ if (debug & DEBUG_HW)
+ pr_notice("%s: W_XADDR=%02x\n",
+ card->name, val);
+ }
+ }
+}
+
+static void
+reset_w6692(struct w6692_hw *card)
+{
+ WriteW6692(card, W_D_CTL, W_D_CTL_SRST);
+ mdelay(10);
+ WriteW6692(card, W_D_CTL, 0);
+}
+
+static int
+init_card(struct w6692_hw *card)
+{
+ int cnt = 3;
+ u_long flags;
+
+ spin_lock_irqsave(&card->lock, flags);
+ disable_hwirq(card);
+ spin_unlock_irqrestore(&card->lock, flags);
+ if (request_irq(card->irq, w6692_irq, IRQF_SHARED, card->name, card)) {
+ pr_info("%s: couldn't get interrupt %d\n", card->name,
+ card->irq);
+ return -EIO;
+ }
+ while (cnt--) {
+ spin_lock_irqsave(&card->lock, flags);
+ initW6692(card);
+ enable_hwirq(card);
+ spin_unlock_irqrestore(&card->lock, flags);
+ /* Timeout 10ms */
+ msleep_interruptible(10);
+ if (debug & DEBUG_HW)
+ pr_notice("%s: IRQ %d count %d\n", card->name,
+ card->irq, card->irqcnt);
+ if (!card->irqcnt) {
+ pr_info("%s: IRQ(%d) getting no IRQs during init %d\n",
+ card->name, card->irq, 3 - cnt);
+ reset_w6692(card);
+ } else
+ return 0;
+ }
+ free_irq(card->irq, card);
+ return -EIO;
+}
+
+static int
+w6692_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb)
+{
+ struct bchannel *bch = container_of(ch, struct bchannel, ch);
+ struct w6692_ch *bc = container_of(bch, struct w6692_ch, bch);
+ struct w6692_hw *card = bch->hw;
+ 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(&card->lock, flags);
+ ret = bchannel_senddata(bch, skb);
+ if (ret > 0) { /* direct TX */
+ id = hh->id; /* skb can be freed */
+ ret = 0;
+ W6692_fill_Bfifo(bc);
+ spin_unlock_irqrestore(&card->lock, flags);
+ if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
+ queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+ } else
+ spin_unlock_irqrestore(&card->lock, flags);
+ return ret;
+ case PH_ACTIVATE_REQ:
+ spin_lock_irqsave(&card->lock, flags);
+ if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
+ ret = w6692_mode(bc, ch->protocol);
+ else
+ ret = 0;
+ spin_unlock_irqrestore(&card->lock, flags);
+ if (!ret)
+ _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
+ NULL, GFP_KERNEL);
+ break;
+ case PH_DEACTIVATE_REQ:
+ spin_lock_irqsave(&card->lock, flags);
+ mISDN_clear_bchannel(bch);
+ w6692_mode(bc, ISDN_P_NONE);
+ spin_unlock_irqrestore(&card->lock, 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",
+ card->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
+open_bchannel(struct w6692_hw *card, struct channel_req *rq)
+{
+ struct bchannel *bch;
+
+ if (rq->adr.channel > 2)
+ return -EINVAL;
+ if (rq->protocol == ISDN_P_NONE)
+ return -EINVAL;
+ bch = &card->bc[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 w6692_hw *card, struct mISDN_ctrl_req *cq)
+{
+ int ret = 0;
+
+ switch (cq->op) {
+ case MISDN_CTRL_GETOP:
+ cq->op = 0;
+ break;
+ default:
+ pr_info("%s: unknown CTRL OP %x\n", card->name, cq->op);
+ ret = -EINVAL;
+ break;
+ }
+ return ret;
+}
+
+static int
+w6692_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
+{
+ struct bchannel *bch = container_of(ch, struct bchannel, ch);
+ struct w6692_ch *bc = container_of(bch, struct w6692_ch, bch);
+ struct w6692_hw *card = bch->hw;
+ int ret = -EINVAL;
+ u_long flags;
+
+ pr_debug("%s: %s cmd:%x %p\n", card->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(&card->lock, flags);
+ mISDN_freebchannel(bch);
+ w6692_mode(bc, ISDN_P_NONE);
+ spin_unlock_irqrestore(&card->lock, flags);
+ } else {
+ skb_queue_purge(&bch->rqueue);
+ bch->rcount = 0;
+ }
+ ch->protocol = ISDN_P_NONE;
+ ch->peer = NULL;
+ module_put(THIS_MODULE);
+ ret = 0;
+ break;
+ case CONTROL_CHANNEL:
+ ret = channel_bctrl(bch, arg);
+ break;
+ default:
+ pr_info("%s: %s unknown prim(%x)\n",
+ card->name, __func__, cmd);
+ }
+ return ret;
+}
+
+static int
+w6692_l2l1D(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 w6692_hw *card = container_of(dch, struct w6692_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(&card->lock, flags);
+ ret = dchannel_senddata(dch, skb);
+ if (ret > 0) { /* direct TX */
+ id = hh->id; /* skb can be freed */
+ W6692_fill_Dfifo(card);
+ ret = 0;
+ spin_unlock_irqrestore(&card->lock, flags);
+ queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
+ } else
+ spin_unlock_irqrestore(&card->lock, 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
+w6692_l1callback(struct dchannel *dch, u32 cmd)
+{
+ struct w6692_hw *card = container_of(dch, struct w6692_hw, dch);
+ u_long flags;
+
+ pr_debug("%s: cmd(%x) state(%02x)\n", card->name, cmd, card->state);
+ switch (cmd) {
+ case INFO3_P8:
+ spin_lock_irqsave(&card->lock, flags);
+ ph_command(card, W_L1CMD_AR8);
+ spin_unlock_irqrestore(&card->lock, flags);
+ break;
+ case INFO3_P10:
+ spin_lock_irqsave(&card->lock, flags);
+ ph_command(card, W_L1CMD_AR10);
+ spin_unlock_irqrestore(&card->lock, flags);
+ break;
+ case HW_RESET_REQ:
+ spin_lock_irqsave(&card->lock, flags);
+ if (card->state != W_L1IND_DRD)
+ ph_command(card, W_L1CMD_RST);
+ ph_command(card, W_L1CMD_ECK);
+ spin_unlock_irqrestore(&card->lock, 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(&card->lock, flags);
+ ph_command(card, W_L1CMD_ECK);
+ spin_unlock_irqrestore(&card->lock, 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", card->name,
+ __func__, cmd);
+ return -1;
+ }
+ return 0;
+}
+
+static int
+open_dchannel(struct w6692_hw *card, struct channel_req *rq)
+{
+ pr_debug("%s: %s dev(%d) open from %p\n", card->name, __func__,
+ card->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 = &card->dch.dev.D;
+ rq->ch->protocol = rq->protocol;
+ if (card->dch.state == 7)
+ _queue_data(rq->ch, PH_ACTIVATE_IND, MISDN_ID_ANY,
+ 0, NULL, GFP_KERNEL);
+ return 0;
+}
+
+static int
+w6692_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 w6692_hw *card = container_of(dch, struct w6692_hw, dch);
+ struct channel_req *rq;
+ int err = 0;
+
+ pr_debug("%s: DCTRL: %x %p\n", card->name, cmd, arg);
+ switch (cmd) {
+ case OPEN_CHANNEL:
+ rq = arg;
+ if (rq->protocol == ISDN_P_TE_S0)
+ err = open_dchannel(card, rq);
+ else
+ err = open_bchannel(card, rq);
+ if (err)
+ break;
+ if (!try_module_get(THIS_MODULE))
+ pr_info("%s: cannot get module\n", card->name);
+ break;
+ case CLOSE_CHANNEL:
+ pr_debug("%s: dev(%d) close from %p\n", card->name,
+ dch->dev.id, __builtin_return_address(0));
+ module_put(THIS_MODULE);
+ break;
+ case CONTROL_CHANNEL:
+ err = channel_ctrl(card, arg);
+ break;
+ default:
+ pr_debug("%s: unknown DCTRL command %x\n", card->name, cmd);
+ return -EINVAL;
+ }
+ return err;
+}
+
+static int
+setup_w6692(struct w6692_hw *card)
+{
+ u32 val;
+
+ if (!request_region(card->addr, 256, card->name)) {
+ pr_info("%s: config port %x-%x already in use\n", card->name,
+ card->addr, card->addr + 255);
+ return -EIO;
+ }
+ W6692Version(card);
+ card->bc[0].addr = card->addr;
+ card->bc[1].addr = card->addr + 0x40;
+ val = ReadW6692(card, W_ISTA);
+ if (debug & DEBUG_HW)
+ pr_notice("%s ISTA=%02x\n", card->name, val);
+ val = ReadW6692(card, W_IMASK);
+ if (debug & DEBUG_HW)
+ pr_notice("%s IMASK=%02x\n", card->name, val);
+ val = ReadW6692(card, W_D_EXIR);
+ if (debug & DEBUG_HW)
+ pr_notice("%s D_EXIR=%02x\n", card->name, val);
+ val = ReadW6692(card, W_D_EXIM);
+ if (debug & DEBUG_HW)
+ pr_notice("%s D_EXIM=%02x\n", card->name, val);
+ val = ReadW6692(card, W_D_RSTA);
+ if (debug & DEBUG_HW)
+ pr_notice("%s D_RSTA=%02x\n", card->name, val);
+ return 0;
+}
+
+static void
+release_card(struct w6692_hw *card)
+{
+ u_long flags;
+
+ spin_lock_irqsave(&card->lock, flags);
+ disable_hwirq(card);
+ w6692_mode(&card->bc[0], ISDN_P_NONE);
+ w6692_mode(&card->bc[1], ISDN_P_NONE);
+ if ((card->fmask & led) || card->subtype == W6692_USR) {
+ card->xdata |= 0x04; /* LED OFF */
+ WriteW6692(card, W_XDATA, card->xdata);
+ }
+ spin_unlock_irqrestore(&card->lock, flags);
+ free_irq(card->irq, card);
+ l1_event(card->dch.l1, CLOSE_CHANNEL);
+ mISDN_unregister_device(&card->dch.dev);
+ release_region(card->addr, 256);
+ mISDN_freebchannel(&card->bc[1].bch);
+ mISDN_freebchannel(&card->bc[0].bch);
+ mISDN_freedchannel(&card->dch);
+ write_lock_irqsave(&card_lock, flags);
+ list_del(&card->list);
+ write_unlock_irqrestore(&card_lock, flags);
+ pci_disable_device(card->pdev);
+ pci_set_drvdata(card->pdev, NULL);
+ kfree(card);
+}
+
+static int
+setup_instance(struct w6692_hw *card)
+{
+ int i, err;
+ u_long flags;
+
+ snprintf(card->name, MISDN_MAX_IDLEN - 1, "w6692.%d", w6692_cnt + 1);
+ write_lock_irqsave(&card_lock, flags);
+ list_add_tail(&card->list, &Cards);
+ write_unlock_irqrestore(&card_lock, flags);
+ card->fmask = (1 << w6692_cnt);
+ _set_debug(card);
+ spin_lock_init(&card->lock);
+ mISDN_initdchannel(&card->dch, MAX_DFRAME_LEN_L1, W6692_ph_bh);
+ card->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0);
+ card->dch.dev.D.send = w6692_l2l1D;
+ card->dch.dev.D.ctrl = w6692_dctrl;
+ card->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
+ (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
+ card->dch.hw = card;
+ card->dch.dev.nrbchan = 2;
+ for (i = 0; i < 2; i++) {
+ mISDN_initbchannel(&card->bc[i].bch, MAX_DATA_MEM);
+ card->bc[i].bch.hw = card;
+ card->bc[i].bch.nr = i + 1;
+ card->bc[i].bch.ch.nr = i + 1;
+ card->bc[i].bch.ch.send = w6692_l2l1B;
+ card->bc[i].bch.ch.ctrl = w6692_bctrl;
+ set_channelmap(i + 1, card->dch.dev.channelmap);
+ list_add(&card->bc[i].bch.ch.list, &card->dch.dev.bchannels);
+ }
+ err = setup_w6692(card);
+ if (err)
+ goto error_setup;
+ err = mISDN_register_device(&card->dch.dev, &card->pdev->dev,
+ card->name);
+ if (err)
+ goto error_reg;
+ err = init_card(card);
+ if (err)
+ goto error_init;
+ err = create_l1(&card->dch, w6692_l1callback);
+ if (!err) {
+ w6692_cnt++;
+ pr_notice("W6692 %d cards installed\n", w6692_cnt);
+ return 0;
+ }
+
+ free_irq(card->irq, card);
+error_init:
+ mISDN_unregister_device(&card->dch.dev);
+error_reg:
+ release_region(card->addr, 256);
+error_setup:
+ mISDN_freebchannel(&card->bc[1].bch);
+ mISDN_freebchannel(&card->bc[0].bch);
+ mISDN_freedchannel(&card->dch);
+ write_lock_irqsave(&card_lock, flags);
+ list_del(&card->list);
+ write_unlock_irqrestore(&card_lock, flags);
+ kfree(card);
+ return err;
+}
+
+static int __devinit
+w6692_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ int err = -ENOMEM;
+ struct w6692_hw *card;
+ struct w6692map *m = (struct w6692map *)ent->driver_data;
+
+ card = kzalloc(sizeof(struct w6692_hw), GFP_KERNEL);
+ if (!card) {
+ pr_info("No kmem for w6692 card\n");
+ return err;
+ }
+ card->pdev = pdev;
+ card->subtype = m->subtype;
+ err = pci_enable_device(pdev);
+ if (err) {
+ kfree(card);
+ return err;
+ }
+
+ printk(KERN_INFO "mISDN_w6692: found adapter %s at %s\n",
+ m->name, pci_name(pdev));
+
+ card->addr = pci_resource_start(pdev, 1);
+ card->irq = pdev->irq;
+ pci_set_drvdata(pdev, card);
+ err = setup_instance(card);
+ if (err)
+ pci_set_drvdata(pdev, NULL);
+ return err;
+}
+
+static void __devexit
+w6692_remove_pci(struct pci_dev *pdev)
+{
+ struct w6692_hw *card = pci_get_drvdata(pdev);
+
+ if (card)
+ release_card(card);
+ else
+ if (debug)
+ pr_notice("%s: drvdata allready removed\n", __func__);
+}
+
+static struct pci_device_id w6692_ids[] = {
+ { PCI_VENDOR_ID_DYNALINK, PCI_DEVICE_ID_DYNALINK_IS64PH,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (ulong)&w6692_map[0]},
+ { PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692,
+ PCI_VENDOR_ID_USR, PCI_DEVICE_ID_USR_6692, 0, 0,
+ (ulong)&w6692_map[2]},
+ { PCI_VENDOR_ID_WINBOND2, PCI_DEVICE_ID_WINBOND2_6692,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, (ulong)&w6692_map[1]},
+ { }
+};
+MODULE_DEVICE_TABLE(pci, w6692_ids);
+
+static struct pci_driver w6692_driver = {
+ .name = "w6692",
+ .probe = w6692_probe,
+ .remove = __devexit_p(w6692_remove_pci),
+ .id_table = w6692_ids,
+};
+
+static int __init w6692_init(void)
+{
+ int err;
+
+ pr_notice("Winbond W6692 PCI driver Rev. %s\n", W6692_REV);
+
+ err = pci_register_driver(&w6692_driver);
+ return err;
+}
+
+static void __exit w6692_cleanup(void)
+{
+ pci_unregister_driver(&w6692_driver);
+}
+
+module_init(w6692_init);
+module_exit(w6692_cleanup);
diff --git a/drivers/isdn/hardware/mISDN/w6692.h b/drivers/isdn/hardware/mISDN/w6692.h
new file mode 100644
index 00000000000..f95697757fd
--- /dev/null
+++ b/drivers/isdn/hardware/mISDN/w6692.h
@@ -0,0 +1,190 @@
+/*
+ * Winbond W6692 specific defines
+ *
+ * Author Karsten Keil <keil@isdn4linux.de>
+ * based on the w6692 I4L driver from Petr Novak <petr.novak@i.cz>
+ *
+ * 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.
+ *
+ */
+
+/* Specifications of W6692 registers */
+
+#define W_D_RFIFO 0x00 /* R */
+#define W_D_XFIFO 0x04 /* W */
+#define W_D_CMDR 0x08 /* W */
+#define W_D_MODE 0x0c /* R/W */
+#define W_D_TIMR 0x10 /* R/W */
+#define W_ISTA 0x14 /* R_clr */
+#define W_IMASK 0x18 /* R/W */
+#define W_D_EXIR 0x1c /* R_clr */
+#define W_D_EXIM 0x20 /* R/W */
+#define W_D_STAR 0x24 /* R */
+#define W_D_RSTA 0x28 /* R */
+#define W_D_SAM 0x2c /* R/W */
+#define W_D_SAP1 0x30 /* R/W */
+#define W_D_SAP2 0x34 /* R/W */
+#define W_D_TAM 0x38 /* R/W */
+#define W_D_TEI1 0x3c /* R/W */
+#define W_D_TEI2 0x40 /* R/W */
+#define W_D_RBCH 0x44 /* R */
+#define W_D_RBCL 0x48 /* R */
+#define W_TIMR2 0x4c /* W */
+#define W_L1_RC 0x50 /* R/W */
+#define W_D_CTL 0x54 /* R/W */
+#define W_CIR 0x58 /* R */
+#define W_CIX 0x5c /* W */
+#define W_SQR 0x60 /* R */
+#define W_SQX 0x64 /* W */
+#define W_PCTL 0x68 /* R/W */
+#define W_MOR 0x6c /* R */
+#define W_MOX 0x70 /* R/W */
+#define W_MOSR 0x74 /* R_clr */
+#define W_MOCR 0x78 /* R/W */
+#define W_GCR 0x7c /* R/W */
+
+#define W_B_RFIFO 0x80 /* R */
+#define W_B_XFIFO 0x84 /* W */
+#define W_B_CMDR 0x88 /* W */
+#define W_B_MODE 0x8c /* R/W */
+#define W_B_EXIR 0x90 /* R_clr */
+#define W_B_EXIM 0x94 /* R/W */
+#define W_B_STAR 0x98 /* R */
+#define W_B_ADM1 0x9c /* R/W */
+#define W_B_ADM2 0xa0 /* R/W */
+#define W_B_ADR1 0xa4 /* R/W */
+#define W_B_ADR2 0xa8 /* R/W */
+#define W_B_RBCL 0xac /* R */
+#define W_B_RBCH 0xb0 /* R */
+
+#define W_XADDR 0xf4 /* R/W */
+#define W_XDATA 0xf8 /* R/W */
+#define W_EPCTL 0xfc /* W */
+
+/* W6692 register bits */
+
+#define W_D_CMDR_XRST 0x01
+#define W_D_CMDR_XME 0x02
+#define W_D_CMDR_XMS 0x08
+#define W_D_CMDR_STT 0x10
+#define W_D_CMDR_RRST 0x40
+#define W_D_CMDR_RACK 0x80
+
+#define W_D_MODE_RLP 0x01
+#define W_D_MODE_DLP 0x02
+#define W_D_MODE_MFD 0x04
+#define W_D_MODE_TEE 0x08
+#define W_D_MODE_TMS 0x10
+#define W_D_MODE_RACT 0x40
+#define W_D_MODE_MMS 0x80
+
+#define W_INT_B2_EXI 0x01
+#define W_INT_B1_EXI 0x02
+#define W_INT_D_EXI 0x04
+#define W_INT_XINT0 0x08
+#define W_INT_XINT1 0x10
+#define W_INT_D_XFR 0x20
+#define W_INT_D_RME 0x40
+#define W_INT_D_RMR 0x80
+
+#define W_D_EXI_WEXP 0x01
+#define W_D_EXI_TEXP 0x02
+#define W_D_EXI_ISC 0x04
+#define W_D_EXI_MOC 0x08
+#define W_D_EXI_TIN2 0x10
+#define W_D_EXI_XCOL 0x20
+#define W_D_EXI_XDUN 0x40
+#define W_D_EXI_RDOV 0x80
+
+#define W_D_STAR_DRDY 0x10
+#define W_D_STAR_XBZ 0x20
+#define W_D_STAR_XDOW 0x80
+
+#define W_D_RSTA_RMB 0x10
+#define W_D_RSTA_CRCE 0x20
+#define W_D_RSTA_RDOV 0x40
+
+#define W_D_CTL_SRST 0x20
+
+#define W_CIR_SCC 0x80
+#define W_CIR_ICC 0x40
+#define W_CIR_COD_MASK 0x0f
+
+#define W_PCTL_PCX 0x01
+#define W_PCTL_XMODE 0x02
+#define W_PCTL_OE0 0x04
+#define W_PCTL_OE1 0x08
+#define W_PCTL_OE2 0x10
+#define W_PCTL_OE3 0x20
+#define W_PCTL_OE4 0x40
+#define W_PCTL_OE5 0x80
+
+#define W_B_CMDR_XRST 0x01
+#define W_B_CMDR_XME 0x02
+#define W_B_CMDR_XMS 0x04
+#define W_B_CMDR_RACT 0x20
+#define W_B_CMDR_RRST 0x40
+#define W_B_CMDR_RACK 0x80
+
+#define W_B_MODE_FTS0 0x01
+#define W_B_MODE_FTS1 0x02
+#define W_B_MODE_SW56 0x04
+#define W_B_MODE_BSW0 0x08
+#define W_B_MODE_BSW1 0x10
+#define W_B_MODE_EPCM 0x20
+#define W_B_MODE_ITF 0x40
+#define W_B_MODE_MMS 0x80
+
+#define W_B_EXI_XDUN 0x01
+#define W_B_EXI_XFR 0x02
+#define W_B_EXI_RDOV 0x10
+#define W_B_EXI_RME 0x20
+#define W_B_EXI_RMR 0x40
+
+#define W_B_STAR_XBZ 0x01
+#define W_B_STAR_XDOW 0x04
+#define W_B_STAR_RMB 0x10
+#define W_B_STAR_CRCE 0x20
+#define W_B_STAR_RDOV 0x40
+
+#define W_B_RBCH_LOV 0x20
+
+/* W6692 Layer1 commands */
+
+#define W_L1CMD_ECK 0x00
+#define W_L1CMD_RST 0x01
+#define W_L1CMD_SCP 0x04
+#define W_L1CMD_SSP 0x02
+#define W_L1CMD_AR8 0x08
+#define W_L1CMD_AR10 0x09
+#define W_L1CMD_EAL 0x0a
+#define W_L1CMD_DRC 0x0f
+
+/* W6692 Layer1 indications */
+
+#define W_L1IND_CE 0x07
+#define W_L1IND_DRD 0x00
+#define W_L1IND_LD 0x04
+#define W_L1IND_ARD 0x08
+#define W_L1IND_TI 0x0a
+#define W_L1IND_ATI 0x0b
+#define W_L1IND_AI8 0x0c
+#define W_L1IND_AI10 0x0d
+#define W_L1IND_CD 0x0f
+
+/* FIFO thresholds */
+#define W_D_FIFO_THRESH 64
+#define W_B_FIFO_THRESH 64