diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 18:49:45 +0000 |
commit | 2c3c1048746a4622d8c89a29670120dc8fab93c4 (patch) | |
tree | 848558de17fb3008cdf4d861b01ac7781903ce39 /drivers/isdn/hardware | |
parent | Initial commit. (diff) | |
download | linux-upstream.tar.xz linux-upstream.zip |
Adding upstream version 6.1.76.upstream/6.1.76upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
24 files changed, 22642 insertions, 0 deletions
diff --git a/drivers/isdn/hardware/Makefile b/drivers/isdn/hardware/Makefile new file mode 100644 index 000000000..96f9eb2e4 --- /dev/null +++ b/drivers/isdn/hardware/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0-only +# Makefile for the CAPI hardware drivers + +# Object files in subdirectories + +obj-$(CONFIG_MISDN) += mISDN/ diff --git a/drivers/isdn/hardware/mISDN/Kconfig b/drivers/isdn/hardware/mISDN/Kconfig new file mode 100644 index 000000000..078eeadf7 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/Kconfig @@ -0,0 +1,98 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Hardware for mISDN +# +comment "mISDN hardware drivers" + +config MISDN_HFCPCI + tristate "Support for HFC PCI cards" + depends on MISDN + depends on PCI + help + Enable support for cards with Cologne Chip AG's + HFC PCI chip. + +config MISDN_HFCMULTI + tristate "Support for HFC multiport cards (HFC-4S/8S/E1)" + depends on PCI || CPM1 + depends on MISDN + help + Enable support for cards with Cologne Chip AG's HFC multiport + chip. There are three types of chips that are quite similar, + but the interface is different: + * HFC-4S (4 S/T interfaces on one chip) + * HFC-8S (8 S/T interfaces on one chip) + * HFC-E1 (E1 interface for 2Mbit ISDN) + +config MISDN_HFCMULTI_8xx + bool "Support for XHFC embedded board in HFC multiport driver" + depends on MISDN + depends on MISDN_HFCMULTI + depends on CPM1 + default CPM1 + help + Enable support for the XHFC embedded solution from Speech Design. + +config MISDN_HFCUSB + tristate "Support for HFC-S USB based TAs" + depends on USB + help + 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 + depends on TTY + select MISDN_IPAC + select MISDN_HDLC + help + Enable support for Traverse Technologies NETJet PCI cards. + +config MISDN_HDLC + tristate + select CRC_CCITT + select BITREVERSE + +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 new file mode 100644 index 000000000..3f50f8c47 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/Makefile @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the modular ISDN hardware drivers +# +# + +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 + +obj-$(CONFIG_MISDN_HDLC) += isdnhdlc.o diff --git a/drivers/isdn/hardware/mISDN/avmfritz.c b/drivers/isdn/hardware/mISDN/avmfritz.c new file mode 100644 index 000000000..f68569bfe --- /dev/null +++ b/drivers/isdn/hardware/mISDN/avmfritz.c @@ -0,0 +1,1163 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * 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> + */ +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/mISDNhw.h> +#include <linux/slab.h> +#include <asm/unaligned.h> +#include "ipac.h" + + +#define AVMFRITZ_REV "2.3" + +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_FIFO_SIZE_128 0x20 +#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_V1 0x3f00 +#define HDLC_STAT_RML_MASK_V2 0x7f00 + +#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_V1 32 +#define HDLC_FIFO_SIZE_V2 128 + +/* 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, const 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)); +} + +static 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; + u8 mode; + + 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; + mode = (fc->type == AVM_FRITZ_PCIV2) ? HDLC_FIFO_SIZE_128 : 0; + + switch (protocol) { + case -1: /* used for init */ + bch->state = -1; + fallthrough; + 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 = 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 = 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 = 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; + struct fritzcard *fc = bch->hw; + + pr_debug("%s: %s %d\n", fc->name, __func__, count); + if (test_bit(FLG_RX_OFF, &bch->Flags)) { + p = NULL; + bch->dropcnt += count; + } else { + cnt = bchannel_get_rxbuf(bch, count); + if (cnt < 0) { + pr_warn("%s.B%d: No bufferspace for %d bytes\n", + fc->name, bch->nr, count); + return; + } + p = skb_put(bch->rx_skb, count); + } + ptr = (u32 *)p; + if (fc->type == AVM_FRITZ_PCIV2) + 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); + } + cnt = 0; + while (cnt < count) { + val = le32_to_cpu(inl(addr)); + if (p) { + put_unaligned(val, ptr); + ptr++; + } + cnt += 4; + } + if (p && (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, fs, cnt = 0, idx; + bool fillempty = false; + u8 *p; + u32 *ptr, val, addr; + + idx = (bch->nr - 1) & 1; + hdlc = &fc->hdlc[idx]; + fs = (fc->type == AVM_FRITZ_PCIV2) ? + HDLC_FIFO_SIZE_V2 : HDLC_FIFO_SIZE_V1; + if (!bch->tx_skb) { + if (!test_bit(FLG_TX_EMPTY, &bch->Flags)) + return; + count = fs; + p = bch->fill; + fillempty = true; + } else { + 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 > fs) { + count = fs; + } else { + if (test_bit(FLG_HDLC, &bch->Flags)) + hdlc->ctrl.sr.cmd |= HDLC_CMD_XME; + } + ptr = (u32 *)p; + if (!fillempty) { + pr_debug("%s.B%d: %d/%d/%d", fc->name, bch->nr, count, + bch->tx_idx, bch->tx_skb->len); + bch->tx_idx += count; + } else { + pr_debug("%s.B%d: fillempty %d\n", fc->name, bch->nr, count); + } + hdlc->ctrl.sr.xml = ((count == fs) ? 0 : count); + if (fc->type == AVM_FRITZ_PCIV2) { + __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; + } + if (fillempty) { + while (cnt < count) { + /* all bytes the same - no worry about endian */ + outl(*ptr, addr); + cnt += 4; + } + } else { + while (cnt < count) { + val = get_unaligned(ptr); + outl(cpu_to_le32(val), addr); + ptr++; + cnt += 4; + } + } + if ((debug & DEBUG_HW_BFIFO) && !fillempty) { + 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 { + dev_kfree_skb(bch->tx_skb); + if (get_next_bframe(bch)) { + hdlc_fill_fifo(bch); + test_and_clear_bit(FLG_TX_EMPTY, &bch->Flags); + } else if (test_bit(FLG_TX_EMPTY, &bch->Flags)) { + hdlc_fill_fifo(bch); + } + } +} + +static void +HDLC_irq(struct bchannel *bch, u32 stat) +{ + struct fritzcard *fc = bch->hw; + int len, fs; + u32 rmlMask; + 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 (fc->type == AVM_FRITZ_PCIV2) { + rmlMask = HDLC_STAT_RML_MASK_V2; + fs = HDLC_FIFO_SIZE_V2; + } else { + rmlMask = HDLC_STAT_RML_MASK_V1; + fs = HDLC_FIFO_SIZE_V1; + } + if (stat & HDLC_INT_RPR) { + if (stat & HDLC_STAT_RDO) { + pr_warn("%s: ch%d stat %x RDO\n", + fc->name, bch->nr, stat); + 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 & rmlMask) >> 8; + if (!len) + len = fs; + hdlc_empty_fifo(bch, len); + if (!bch->rx_skb) + goto handle_tx; + if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { + recv_Bchannel(bch, 0, false); + } else if (stat & HDLC_STAT_RME) { + if ((stat & HDLC_STAT_CRCVFRRAB) == + HDLC_STAT_CRCVFR) { + recv_Bchannel(bch, 0, false); + } else { + pr_warn("%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 + */ + pr_warn("%s: ch%d stat %x XDU %s\n", fc->name, bch->nr, + stat, bch->tx_skb ? "tx_skb" : "no tx_skb"); + if (bch->tx_skb && bch->tx_skb->len) { + if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) + bch->tx_idx = 0; + } else if (test_bit(FLG_FILLEMPTY, &bch->Flags)) { + test_and_set_bit(FLG_TX_EMPTY, &bch->Flags); + } + 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); + unsigned 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 */ + hdlc_fill_fifo(bch); + ret = 0; + } + 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); +} + +static 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 (fc->type == AVM_FRITZ_PCIV2) { + 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) +{ + return mISDN_ctrl_bchannel(bch, cq); +} + +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); + cancel_work_sync(&bch->workq); + spin_lock_irqsave(&fc->lock, flags); + mISDN_clear_bchannel(bch); + 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 | MISDN_CTRL_L1_TIMER3; + 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; + case MISDN_CTRL_L1_TIMER3: + ret = fc->isac.ctrl(&fc->isac, HW_TIMER3_VALUE, cq->p1); + 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 == 0 || 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 */ + 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; +} + +static 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 +setup_instance(struct fritzcard *card) +{ + int i, err; + unsigned short minsize; + 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); + if (AVM_FRITZ_PCIV2 == card->type) + minsize = HDLC_FIFO_SIZE_V2; + else + minsize = HDLC_FIFO_SIZE_V1; + mISDN_initbchannel(&card->bch[i], MAX_DATA_MEM, minsize); + 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 +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 +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 already removed\n", __func__); +} + +static const struct pci_device_id fcpci_ids[] = { + { 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 = 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/hfc_multi.h b/drivers/isdn/hardware/mISDN/hfc_multi.h new file mode 100644 index 000000000..5acf826d9 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfc_multi.h @@ -0,0 +1,1236 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * see notice in hfc_multi.c + */ + +#define DEBUG_HFCMULTI_FIFO 0x00010000 +#define DEBUG_HFCMULTI_CRC 0x00020000 +#define DEBUG_HFCMULTI_INIT 0x00040000 +#define DEBUG_HFCMULTI_PLXSD 0x00080000 +#define DEBUG_HFCMULTI_MODE 0x00100000 +#define DEBUG_HFCMULTI_MSG 0x00200000 +#define DEBUG_HFCMULTI_STATE 0x00400000 +#define DEBUG_HFCMULTI_FILL 0x00800000 +#define DEBUG_HFCMULTI_SYNC 0x01000000 +#define DEBUG_HFCMULTI_DTMF 0x02000000 +#define DEBUG_HFCMULTI_LOCK 0x80000000 + +#define PCI_ENA_REGIO 0x01 +#define PCI_ENA_MEMIO 0x02 + +#define XHFC_IRQ 4 /* SIU_IRQ2 */ +#define XHFC_MEMBASE 0xFE000000 +#define XHFC_MEMSIZE 0x00001000 +#define XHFC_OFFSET 0x00001000 +#define PA_XHFC_A0 0x0020 /* PA10 */ +#define PB_XHFC_IRQ1 0x00000100 /* PB23 */ +#define PB_XHFC_IRQ2 0x00000200 /* PB22 */ +#define PB_XHFC_IRQ3 0x00000400 /* PB21 */ +#define PB_XHFC_IRQ4 0x00000800 /* PB20 */ + +/* + * NOTE: some registers are assigned multiple times due to different modes + * also registers are assigned differen for HFC-4s/8s and HFC-E1 + */ + +/* + #define MAX_FRAME_SIZE 2048 +*/ + +struct hfc_chan { + struct dchannel *dch; /* link if channel is a D-channel */ + struct bchannel *bch; /* link if channel is a B-channel */ + int port; /* the interface port this */ + /* channel is associated with */ + int nt_timer; /* -1 if off, 0 if elapsed, >0 if running */ + int los, ais, slip_tx, slip_rx, rdi; /* current alarms */ + int jitter; + u_long cfg; /* port configuration */ + int sync; /* sync state (used by E1) */ + u_int protocol; /* current protocol */ + int slot_tx; /* current pcm slot */ + int bank_tx; /* current pcm bank */ + int slot_rx; + int bank_rx; + int conf; /* conference setting of TX slot */ + int txpending; /* if there is currently data in */ + /* the FIFO 0=no, 1=yes, 2=splloop */ + int Zfill; /* rx-fifo level on last hfcmulti_tx */ + int rx_off; /* set to turn fifo receive off */ + int coeff_count; /* curren coeff block */ + s32 *coeff; /* memory pointer to 8 coeff blocks */ +}; + + +struct hfcm_hw { + u_char r_ctrl; + u_char r_irq_ctrl; + u_char r_cirm; + u_char r_ram_sz; + u_char r_pcm_md0; + u_char r_irqmsk_misc; + u_char r_dtmf; + u_char r_st_sync; + u_char r_sci_msk; + u_char r_tx0, r_tx1; + u_char a_st_ctrl0[8]; + u_char r_bert_wd_md; + timer_t timer; +}; + + +/* for each stack these flags are used (cfg) */ +#define HFC_CFG_NONCAP_TX 1 /* S/T TX interface has less capacity */ +#define HFC_CFG_DIS_ECHANNEL 2 /* disable E-channel processing */ +#define HFC_CFG_REG_ECHANNEL 3 /* register E-channel */ +#define HFC_CFG_OPTICAL 4 /* the E1 interface is optical */ +#define HFC_CFG_REPORT_LOS 5 /* the card should report loss of signal */ +#define HFC_CFG_REPORT_AIS 6 /* the card should report alarm ind. sign. */ +#define HFC_CFG_REPORT_SLIP 7 /* the card should report bit slips */ +#define HFC_CFG_REPORT_RDI 8 /* the card should report remote alarm */ +#define HFC_CFG_DTMF 9 /* enable DTMF-detection */ +#define HFC_CFG_CRC4 10 /* disable CRC-4 Multiframe mode, */ +/* use double frame instead. */ + +#define HFC_TYPE_E1 1 /* controller is HFC-E1 */ +#define HFC_TYPE_4S 4 /* controller is HFC-4S */ +#define HFC_TYPE_8S 8 /* controller is HFC-8S */ +#define HFC_TYPE_XHFC 5 /* controller is XHFC */ + +#define HFC_CHIP_EXRAM_128 0 /* external ram 128k */ +#define HFC_CHIP_EXRAM_512 1 /* external ram 256k */ +#define HFC_CHIP_REVISION0 2 /* old fifo handling */ +#define HFC_CHIP_PCM_SLAVE 3 /* PCM is slave */ +#define HFC_CHIP_PCM_MASTER 4 /* PCM is master */ +#define HFC_CHIP_RX_SYNC 5 /* disable pll sync for pcm */ +#define HFC_CHIP_DTMF 6 /* DTMF decoding is enabled */ +#define HFC_CHIP_CONF 7 /* conference handling is enabled */ +#define HFC_CHIP_ULAW 8 /* ULAW mode */ +#define HFC_CHIP_CLOCK2 9 /* double clock mode */ +#define HFC_CHIP_E1CLOCK_GET 10 /* always get clock from E1 interface */ +#define HFC_CHIP_E1CLOCK_PUT 11 /* always put clock from E1 interface */ +#define HFC_CHIP_WATCHDOG 12 /* whether we should send signals */ +/* to the watchdog */ +#define HFC_CHIP_B410P 13 /* whether we have a b410p with echocan in */ +/* hw */ +#define HFC_CHIP_PLXSD 14 /* whether we have a Speech-Design PLX */ +#define HFC_CHIP_EMBSD 15 /* whether we have a SD Embedded board */ + +#define HFC_IO_MODE_PCIMEM 0x00 /* normal memory mapped IO */ +#define HFC_IO_MODE_REGIO 0x01 /* PCI io access */ +#define HFC_IO_MODE_PLXSD 0x02 /* access HFC via PLX9030 */ +#define HFC_IO_MODE_EMBSD 0x03 /* direct access */ + +/* table entry in the PCI devices list */ +struct hm_map { + char *vendor_name; + char *card_name; + int type; + int ports; + int clock2; + int leds; + int opticalsupport; + int dip_type; + int io_mode; + int irq; +}; + +struct hfc_multi { + struct list_head list; + struct hm_map *mtyp; + int id; + int pcm; /* id of pcm bus */ + int ctype; /* controller type */ + int ports; + + u_int irq; /* irq used by card */ + u_int irqcnt; + struct pci_dev *pci_dev; + int io_mode; /* selects mode */ +#ifdef HFC_REGISTER_DEBUG + void (*HFC_outb)(struct hfc_multi *hc, u_char reg, + u_char val, const char *function, int line); + void (*HFC_outb_nodebug)(struct hfc_multi *hc, u_char reg, + u_char val, const char *function, int line); + u_char (*HFC_inb)(struct hfc_multi *hc, u_char reg, + const char *function, int line); + u_char (*HFC_inb_nodebug)(struct hfc_multi *hc, u_char reg, + const char *function, int line); + u_short (*HFC_inw)(struct hfc_multi *hc, u_char reg, + const char *function, int line); + u_short (*HFC_inw_nodebug)(struct hfc_multi *hc, u_char reg, + const char *function, int line); + void (*HFC_wait)(struct hfc_multi *hc, + const char *function, int line); + void (*HFC_wait_nodebug)(struct hfc_multi *hc, + const char *function, int line); +#else + void (*HFC_outb)(struct hfc_multi *hc, u_char reg, + u_char val); + void (*HFC_outb_nodebug)(struct hfc_multi *hc, u_char reg, + u_char val); + u_char (*HFC_inb)(struct hfc_multi *hc, u_char reg); + u_char (*HFC_inb_nodebug)(struct hfc_multi *hc, u_char reg); + u_short (*HFC_inw)(struct hfc_multi *hc, u_char reg); + u_short (*HFC_inw_nodebug)(struct hfc_multi *hc, u_char reg); + void (*HFC_wait)(struct hfc_multi *hc); + void (*HFC_wait_nodebug)(struct hfc_multi *hc); +#endif + void (*read_fifo)(struct hfc_multi *hc, u_char *data, + int len); + void (*write_fifo)(struct hfc_multi *hc, u_char *data, + int len); + u_long pci_origmembase, plx_origmembase; + void __iomem *pci_membase; /* PCI memory */ + void __iomem *plx_membase; /* PLX memory */ + u_long xhfc_origmembase; + u_char *xhfc_membase; + u_long *xhfc_memaddr, *xhfc_memdata; +#ifdef CONFIG_MISDN_HFCMULTI_8xx + struct immap *immap; +#endif + u_long pb_irqmsk; /* Portbit mask to check the IRQ line */ + u_long pci_iobase; /* PCI IO */ + struct hfcm_hw hw; /* remember data of write-only-registers */ + + u_long chip; /* chip configuration */ + int masterclk; /* port that provides master clock -1=off */ + unsigned char silence;/* silence byte */ + unsigned char silence_data[128];/* silence block */ + int dtmf; /* flag that dtmf is currently in process */ + int Flen; /* F-buffer size */ + int Zlen; /* Z-buffer size (must be int for calculation)*/ + int max_trans; /* maximum transparent fifo fill */ + int Zmin; /* Z-buffer offset */ + int DTMFbase; /* base address of DTMF coefficients */ + + u_int slots; /* number of PCM slots */ + u_int leds; /* type of leds */ + u_long ledstate; /* save last state of leds */ + int opticalsupport; /* has the e1 board */ + /* an optical Interface */ + + u_int bmask[32]; /* bitmask of bchannels for port */ + u_char dnum[32]; /* array of used dchannel numbers for port */ + u_char created[32]; /* what port is created */ + u_int activity_tx; /* if there is data TX / RX */ + u_int activity_rx; /* bitmask according to port number */ + /* (will be cleared after */ + /* showing led-states) */ + u_int flash[8]; /* counter for flashing 8 leds on activity */ + + u_long wdcount; /* every 500 ms we need to */ + /* send the watchdog a signal */ + u_char wdbyte; /* watchdog toggle byte */ + int e1_state; /* keep track of last state */ + int e1_getclock; /* if sync is retrieved from interface */ + int syncronized; /* keep track of existing sync interface */ + int e1_resync; /* resync jobs */ + + spinlock_t lock; /* the lock */ + + struct mISDNclock *iclock; /* isdn clock support */ + int iclock_on; + + /* + * the channel index is counted from 0, regardless where the channel + * is located on the hfc-channel. + * the bch->channel is equvalent to the hfc-channel + */ + struct hfc_chan chan[32]; + signed char slot_owner[256]; /* owner channel of slot */ +}; + +/* PLX GPIOs */ +#define PLX_GPIO4_DIR_BIT 13 +#define PLX_GPIO4_BIT 14 +#define PLX_GPIO5_DIR_BIT 16 +#define PLX_GPIO5_BIT 17 +#define PLX_GPIO6_DIR_BIT 19 +#define PLX_GPIO6_BIT 20 +#define PLX_GPIO7_DIR_BIT 22 +#define PLX_GPIO7_BIT 23 +#define PLX_GPIO8_DIR_BIT 25 +#define PLX_GPIO8_BIT 26 + +#define PLX_GPIO4 (1 << PLX_GPIO4_BIT) +#define PLX_GPIO5 (1 << PLX_GPIO5_BIT) +#define PLX_GPIO6 (1 << PLX_GPIO6_BIT) +#define PLX_GPIO7 (1 << PLX_GPIO7_BIT) +#define PLX_GPIO8 (1 << PLX_GPIO8_BIT) + +#define PLX_GPIO4_DIR (1 << PLX_GPIO4_DIR_BIT) +#define PLX_GPIO5_DIR (1 << PLX_GPIO5_DIR_BIT) +#define PLX_GPIO6_DIR (1 << PLX_GPIO6_DIR_BIT) +#define PLX_GPIO7_DIR (1 << PLX_GPIO7_DIR_BIT) +#define PLX_GPIO8_DIR (1 << PLX_GPIO8_DIR_BIT) + +#define PLX_TERM_ON PLX_GPIO7 +#define PLX_SLAVE_EN_N PLX_GPIO5 +#define PLX_MASTER_EN PLX_GPIO6 +#define PLX_SYNC_O_EN PLX_GPIO4 +#define PLX_DSP_RES_N PLX_GPIO8 +/* GPIO4..8 Enable & Set to OUT, SLAVE_EN_N = 1 */ +#define PLX_GPIOC_INIT (PLX_GPIO4_DIR | PLX_GPIO5_DIR | PLX_GPIO6_DIR \ + | PLX_GPIO7_DIR | PLX_GPIO8_DIR | PLX_SLAVE_EN_N) + +/* PLX Interrupt Control/STATUS */ +#define PLX_INTCSR_LINTI1_ENABLE 0x01 +#define PLX_INTCSR_LINTI1_STATUS 0x04 +#define PLX_INTCSR_LINTI2_ENABLE 0x08 +#define PLX_INTCSR_LINTI2_STATUS 0x20 +#define PLX_INTCSR_PCIINT_ENABLE 0x40 + +/* PLX Registers */ +#define PLX_INTCSR 0x4c +#define PLX_CNTRL 0x50 +#define PLX_GPIOC 0x54 + + +/* + * REGISTER SETTING FOR HFC-4S/8S AND HFC-E1 + */ + +/* write only registers */ +#define R_CIRM 0x00 +#define R_CTRL 0x01 +#define R_BRG_PCM_CFG 0x02 +#define R_RAM_ADDR0 0x08 +#define R_RAM_ADDR1 0x09 +#define R_RAM_ADDR2 0x0A +#define R_FIRST_FIFO 0x0B +#define R_RAM_SZ 0x0C +#define R_FIFO_MD 0x0D +#define R_INC_RES_FIFO 0x0E +#define R_FSM_IDX 0x0F +#define R_FIFO 0x0F +#define R_SLOT 0x10 +#define R_IRQMSK_MISC 0x11 +#define R_SCI_MSK 0x12 +#define R_IRQ_CTRL 0x13 +#define R_PCM_MD0 0x14 +#define R_PCM_MD1 0x15 +#define R_PCM_MD2 0x15 +#define R_SH0H 0x15 +#define R_SH1H 0x15 +#define R_SH0L 0x15 +#define R_SH1L 0x15 +#define R_SL_SEL0 0x15 +#define R_SL_SEL1 0x15 +#define R_SL_SEL2 0x15 +#define R_SL_SEL3 0x15 +#define R_SL_SEL4 0x15 +#define R_SL_SEL5 0x15 +#define R_SL_SEL6 0x15 +#define R_SL_SEL7 0x15 +#define R_ST_SEL 0x16 +#define R_ST_SYNC 0x17 +#define R_CONF_EN 0x18 +#define R_TI_WD 0x1A +#define R_BERT_WD_MD 0x1B +#define R_DTMF 0x1C +#define R_DTMF_N 0x1D +#define R_E1_WR_STA 0x20 +#define R_E1_RD_STA 0x20 +#define R_LOS0 0x22 +#define R_LOS1 0x23 +#define R_RX0 0x24 +#define R_RX_FR0 0x25 +#define R_RX_FR1 0x26 +#define R_TX0 0x28 +#define R_TX1 0x29 +#define R_TX_FR0 0x2C + +#define R_TX_FR1 0x2D +#define R_TX_FR2 0x2E +#define R_JATT_ATT 0x2F /* undocumented */ +#define A_ST_RD_STATE 0x30 +#define A_ST_WR_STATE 0x30 +#define R_RX_OFF 0x30 +#define A_ST_CTRL0 0x31 +#define R_SYNC_OUT 0x31 +#define A_ST_CTRL1 0x32 +#define A_ST_CTRL2 0x33 +#define A_ST_SQ_WR 0x34 +#define R_TX_OFF 0x34 +#define R_SYNC_CTRL 0x35 +#define A_ST_CLK_DLY 0x37 +#define R_PWM0 0x38 +#define R_PWM1 0x39 +#define A_ST_B1_TX 0x3C +#define A_ST_B2_TX 0x3D +#define A_ST_D_TX 0x3E +#define R_GPIO_OUT0 0x40 +#define R_GPIO_OUT1 0x41 +#define R_GPIO_EN0 0x42 +#define R_GPIO_EN1 0x43 +#define R_GPIO_SEL 0x44 +#define R_BRG_CTRL 0x45 +#define R_PWM_MD 0x46 +#define R_BRG_MD 0x47 +#define R_BRG_TIM0 0x48 +#define R_BRG_TIM1 0x49 +#define R_BRG_TIM2 0x4A +#define R_BRG_TIM3 0x4B +#define R_BRG_TIM_SEL01 0x4C +#define R_BRG_TIM_SEL23 0x4D +#define R_BRG_TIM_SEL45 0x4E +#define R_BRG_TIM_SEL67 0x4F +#define A_SL_CFG 0xD0 +#define A_CONF 0xD1 +#define A_CH_MSK 0xF4 +#define A_CON_HDLC 0xFA +#define A_SUBCH_CFG 0xFB +#define A_CHANNEL 0xFC +#define A_FIFO_SEQ 0xFD +#define A_IRQ_MSK 0xFF + +/* read only registers */ +#define A_Z12 0x04 +#define A_Z1L 0x04 +#define A_Z1 0x04 +#define A_Z1H 0x05 +#define A_Z2L 0x06 +#define A_Z2 0x06 +#define A_Z2H 0x07 +#define A_F1 0x0C +#define A_F12 0x0C +#define A_F2 0x0D +#define R_IRQ_OVIEW 0x10 +#define R_IRQ_MISC 0x11 +#define R_IRQ_STATECH 0x12 +#define R_CONF_OFLOW 0x14 +#define R_RAM_USE 0x15 +#define R_CHIP_ID 0x16 +#define R_BERT_STA 0x17 +#define R_F0_CNTL 0x18 +#define R_F0_CNTH 0x19 +#define R_BERT_EC 0x1A +#define R_BERT_ECL 0x1A +#define R_BERT_ECH 0x1B +#define R_STATUS 0x1C +#define R_CHIP_RV 0x1F +#define R_STATE 0x20 +#define R_SYNC_STA 0x24 +#define R_RX_SL0_0 0x25 +#define R_RX_SL0_1 0x26 +#define R_RX_SL0_2 0x27 +#define R_JATT_DIR 0x2b /* undocumented */ +#define R_SLIP 0x2c +#define A_ST_RD_STA 0x30 +#define R_FAS_EC 0x30 +#define R_FAS_ECL 0x30 +#define R_FAS_ECH 0x31 +#define R_VIO_EC 0x32 +#define R_VIO_ECL 0x32 +#define R_VIO_ECH 0x33 +#define A_ST_SQ_RD 0x34 +#define R_CRC_EC 0x34 +#define R_CRC_ECL 0x34 +#define R_CRC_ECH 0x35 +#define R_E_EC 0x36 +#define R_E_ECL 0x36 +#define R_E_ECH 0x37 +#define R_SA6_SA13_EC 0x38 +#define R_SA6_SA13_ECL 0x38 +#define R_SA6_SA13_ECH 0x39 +#define R_SA6_SA23_EC 0x3A +#define R_SA6_SA23_ECL 0x3A +#define R_SA6_SA23_ECH 0x3B +#define A_ST_B1_RX 0x3C +#define A_ST_B2_RX 0x3D +#define A_ST_D_RX 0x3E +#define A_ST_E_RX 0x3F +#define R_GPIO_IN0 0x40 +#define R_GPIO_IN1 0x41 +#define R_GPI_IN0 0x44 +#define R_GPI_IN1 0x45 +#define R_GPI_IN2 0x46 +#define R_GPI_IN3 0x47 +#define R_INT_DATA 0x88 +#define R_IRQ_FIFO_BL0 0xC8 +#define R_IRQ_FIFO_BL1 0xC9 +#define R_IRQ_FIFO_BL2 0xCA +#define R_IRQ_FIFO_BL3 0xCB +#define R_IRQ_FIFO_BL4 0xCC +#define R_IRQ_FIFO_BL5 0xCD +#define R_IRQ_FIFO_BL6 0xCE +#define R_IRQ_FIFO_BL7 0xCF + +/* read and write registers */ +#define A_FIFO_DATA0 0x80 +#define A_FIFO_DATA1 0x80 +#define A_FIFO_DATA2 0x80 +#define A_FIFO_DATA0_NOINC 0x84 +#define A_FIFO_DATA1_NOINC 0x84 +#define A_FIFO_DATA2_NOINC 0x84 +#define R_RAM_DATA 0xC0 + + +/* + * BIT SETTING FOR HFC-4S/8S AND HFC-E1 + */ + +/* chapter 2: universal bus interface */ +/* R_CIRM */ +#define V_IRQ_SEL 0x01 +#define V_SRES 0x08 +#define V_HFCRES 0x10 +#define V_PCMRES 0x20 +#define V_STRES 0x40 +#define V_ETRES 0x40 +#define V_RLD_EPR 0x80 +/* R_CTRL */ +#define V_FIFO_LPRIO 0x02 +#define V_SLOW_RD 0x04 +#define V_EXT_RAM 0x08 +#define V_CLK_OFF 0x20 +#define V_ST_CLK 0x40 +/* R_RAM_ADDR0 */ +#define V_RAM_ADDR2 0x01 +#define V_ADDR_RES 0x40 +#define V_ADDR_INC 0x80 +/* R_RAM_SZ */ +#define V_RAM_SZ 0x01 +#define V_PWM0_16KHZ 0x10 +#define V_PWM1_16KHZ 0x20 +#define V_FZ_MD 0x80 +/* R_CHIP_ID */ +#define V_PNP_IRQ 0x01 +#define V_CHIP_ID 0x10 + +/* chapter 3: data flow */ +/* R_FIRST_FIFO */ +#define V_FIRST_FIRO_DIR 0x01 +#define V_FIRST_FIFO_NUM 0x02 +/* R_FIFO_MD */ +#define V_FIFO_MD 0x01 +#define V_CSM_MD 0x04 +#define V_FSM_MD 0x08 +#define V_FIFO_SZ 0x10 +/* R_FIFO */ +#define V_FIFO_DIR 0x01 +#define V_FIFO_NUM 0x02 +#define V_REV 0x80 +/* R_SLOT */ +#define V_SL_DIR 0x01 +#define V_SL_NUM 0x02 +/* A_SL_CFG */ +#define V_CH_DIR 0x01 +#define V_CH_SEL 0x02 +#define V_ROUTING 0x40 +/* A_CON_HDLC */ +#define V_IFF 0x01 +#define V_HDLC_TRP 0x02 +#define V_TRP_IRQ 0x04 +#define V_DATA_FLOW 0x20 +/* A_SUBCH_CFG */ +#define V_BIT_CNT 0x01 +#define V_START_BIT 0x08 +#define V_LOOP_FIFO 0x40 +#define V_INV_DATA 0x80 +/* A_CHANNEL */ +#define V_CH_DIR0 0x01 +#define V_CH_NUM0 0x02 +/* A_FIFO_SEQ */ +#define V_NEXT_FIFO_DIR 0x01 +#define V_NEXT_FIFO_NUM 0x02 +#define V_SEQ_END 0x40 + +/* chapter 4: FIFO handling and HDLC controller */ +/* R_INC_RES_FIFO */ +#define V_INC_F 0x01 +#define V_RES_F 0x02 +#define V_RES_LOST 0x04 + +/* chapter 5: S/T interface */ +/* R_SCI_MSK */ +#define V_SCI_MSK_ST0 0x01 +#define V_SCI_MSK_ST1 0x02 +#define V_SCI_MSK_ST2 0x04 +#define V_SCI_MSK_ST3 0x08 +#define V_SCI_MSK_ST4 0x10 +#define V_SCI_MSK_ST5 0x20 +#define V_SCI_MSK_ST6 0x40 +#define V_SCI_MSK_ST7 0x80 +/* R_ST_SEL */ +#define V_ST_SEL 0x01 +#define V_MULT_ST 0x08 +/* R_ST_SYNC */ +#define V_SYNC_SEL 0x01 +#define V_AUTO_SYNC 0x08 +/* A_ST_WR_STA */ +#define V_ST_SET_STA 0x01 +#define V_ST_LD_STA 0x10 +#define V_ST_ACT 0x20 +#define V_SET_G2_G3 0x80 +/* A_ST_CTRL0 */ +#define V_B1_EN 0x01 +#define V_B2_EN 0x02 +#define V_ST_MD 0x04 +#define V_D_PRIO 0x08 +#define V_SQ_EN 0x10 +#define V_96KHZ 0x20 +#define V_TX_LI 0x40 +#define V_ST_STOP 0x80 +/* A_ST_CTRL1 */ +#define V_G2_G3_EN 0x01 +#define V_D_HI 0x04 +#define V_E_IGNO 0x08 +#define V_E_LO 0x10 +#define V_B12_SWAP 0x80 +/* A_ST_CTRL2 */ +#define V_B1_RX_EN 0x01 +#define V_B2_RX_EN 0x02 +#define V_ST_TRIS 0x40 +/* A_ST_CLK_DLY */ +#define V_ST_CK_DLY 0x01 +#define V_ST_SMPL 0x10 +/* A_ST_D_TX */ +#define V_ST_D_TX 0x40 +/* R_IRQ_STATECH */ +#define V_SCI_ST0 0x01 +#define V_SCI_ST1 0x02 +#define V_SCI_ST2 0x04 +#define V_SCI_ST3 0x08 +#define V_SCI_ST4 0x10 +#define V_SCI_ST5 0x20 +#define V_SCI_ST6 0x40 +#define V_SCI_ST7 0x80 +/* A_ST_RD_STA */ +#define V_ST_STA 0x01 +#define V_FR_SYNC_ST 0x10 +#define V_TI2_EXP 0x20 +#define V_INFO0 0x40 +#define V_G2_G3 0x80 +/* A_ST_SQ_RD */ +#define V_ST_SQ 0x01 +#define V_MF_RX_RDY 0x10 +#define V_MF_TX_RDY 0x80 +/* A_ST_D_RX */ +#define V_ST_D_RX 0x40 +/* A_ST_E_RX */ +#define V_ST_E_RX 0x40 + +/* chapter 5: E1 interface */ +/* R_E1_WR_STA */ +/* R_E1_RD_STA */ +#define V_E1_SET_STA 0x01 +#define V_E1_LD_STA 0x10 +/* R_RX0 */ +#define V_RX_CODE 0x01 +#define V_RX_FBAUD 0x04 +#define V_RX_CMI 0x08 +#define V_RX_INV_CMI 0x10 +#define V_RX_INV_CLK 0x20 +#define V_RX_INV_DATA 0x40 +#define V_AIS_ITU 0x80 +/* R_RX_FR0 */ +#define V_NO_INSYNC 0x01 +#define V_AUTO_RESYNC 0x02 +#define V_AUTO_RECO 0x04 +#define V_SWORD_COND 0x08 +#define V_SYNC_LOSS 0x10 +#define V_XCRC_SYNC 0x20 +#define V_MF_RESYNC 0x40 +#define V_RESYNC 0x80 +/* R_RX_FR1 */ +#define V_RX_MF 0x01 +#define V_RX_MF_SYNC 0x02 +#define V_RX_SL0_RAM 0x04 +#define V_ERR_SIM 0x20 +#define V_RES_NMF 0x40 +/* R_TX0 */ +#define V_TX_CODE 0x01 +#define V_TX_FBAUD 0x04 +#define V_TX_CMI_CODE 0x08 +#define V_TX_INV_CMI_CODE 0x10 +#define V_TX_INV_CLK 0x20 +#define V_TX_INV_DATA 0x40 +#define V_OUT_EN 0x80 +/* R_TX1 */ +#define V_INV_CLK 0x01 +#define V_EXCHG_DATA_LI 0x02 +#define V_AIS_OUT 0x04 +#define V_ATX 0x20 +#define V_NTRI 0x40 +#define V_AUTO_ERR_RES 0x80 +/* R_TX_FR0 */ +#define V_TRP_FAS 0x01 +#define V_TRP_NFAS 0x02 +#define V_TRP_RAL 0x04 +#define V_TRP_SA 0x08 +/* R_TX_FR1 */ +#define V_TX_FAS 0x01 +#define V_TX_NFAS 0x02 +#define V_TX_RAL 0x04 +#define V_TX_SA 0x08 +/* R_TX_FR2 */ +#define V_TX_MF 0x01 +#define V_TRP_SL0 0x02 +#define V_TX_SL0_RAM 0x04 +#define V_TX_E 0x10 +#define V_NEG_E 0x20 +#define V_XS12_ON 0x40 +#define V_XS15_ON 0x80 +/* R_RX_OFF */ +#define V_RX_SZ 0x01 +#define V_RX_INIT 0x04 +/* R_SYNC_OUT */ +#define V_SYNC_E1_RX 0x01 +#define V_IPATS0 0x20 +#define V_IPATS1 0x40 +#define V_IPATS2 0x80 +/* R_TX_OFF */ +#define V_TX_SZ 0x01 +#define V_TX_INIT 0x04 +/* R_SYNC_CTRL */ +#define V_EXT_CLK_SYNC 0x01 +#define V_SYNC_OFFS 0x02 +#define V_PCM_SYNC 0x04 +#define V_NEG_CLK 0x08 +#define V_HCLK 0x10 +/* + #define V_JATT_AUTO_DEL 0x20 + #define V_JATT_AUTO 0x40 +*/ +#define V_JATT_OFF 0x80 +/* R_STATE */ +#define V_E1_STA 0x01 +#define V_ALT_FR_RX 0x40 +#define V_ALT_FR_TX 0x80 +/* R_SYNC_STA */ +#define V_RX_STA 0x01 +#define V_FR_SYNC_E1 0x04 +#define V_SIG_LOS 0x08 +#define V_MFA_STA 0x10 +#define V_AIS 0x40 +#define V_NO_MF_SYNC 0x80 +/* R_RX_SL0_0 */ +#define V_SI_FAS 0x01 +#define V_SI_NFAS 0x02 +#define V_A 0x04 +#define V_CRC_OK 0x08 +#define V_TX_E1 0x10 +#define V_TX_E2 0x20 +#define V_RX_E1 0x40 +#define V_RX_E2 0x80 +/* R_SLIP */ +#define V_SLIP_RX 0x01 +#define V_FOSLIP_RX 0x08 +#define V_SLIP_TX 0x10 +#define V_FOSLIP_TX 0x80 + +/* chapter 6: PCM interface */ +/* R_PCM_MD0 */ +#define V_PCM_MD 0x01 +#define V_C4_POL 0x02 +#define V_F0_NEG 0x04 +#define V_F0_LEN 0x08 +#define V_PCM_ADDR 0x10 +/* R_SL_SEL0 */ +#define V_SL_SEL0 0x01 +#define V_SH_SEL0 0x80 +/* R_SL_SEL1 */ +#define V_SL_SEL1 0x01 +#define V_SH_SEL1 0x80 +/* R_SL_SEL2 */ +#define V_SL_SEL2 0x01 +#define V_SH_SEL2 0x80 +/* R_SL_SEL3 */ +#define V_SL_SEL3 0x01 +#define V_SH_SEL3 0x80 +/* R_SL_SEL4 */ +#define V_SL_SEL4 0x01 +#define V_SH_SEL4 0x80 +/* R_SL_SEL5 */ +#define V_SL_SEL5 0x01 +#define V_SH_SEL5 0x80 +/* R_SL_SEL6 */ +#define V_SL_SEL6 0x01 +#define V_SH_SEL6 0x80 +/* R_SL_SEL7 */ +#define V_SL_SEL7 0x01 +#define V_SH_SEL7 0x80 +/* R_PCM_MD1 */ +#define V_ODEC_CON 0x01 +#define V_PLL_ADJ 0x04 +#define V_PCM_DR 0x10 +#define V_PCM_LOOP 0x40 +/* R_PCM_MD2 */ +#define V_SYNC_PLL 0x02 +#define V_SYNC_SRC 0x04 +#define V_SYNC_OUT 0x08 +#define V_ICR_FR_TIME 0x40 +#define V_EN_PLL 0x80 + +/* chapter 7: pulse width modulation */ +/* R_PWM_MD */ +#define V_EXT_IRQ_EN 0x08 +#define V_PWM0_MD 0x10 +#define V_PWM1_MD 0x40 + +/* chapter 8: multiparty audio conferences */ +/* R_CONF_EN */ +#define V_CONF_EN 0x01 +#define V_ULAW 0x80 +/* A_CONF */ +#define V_CONF_NUM 0x01 +#define V_NOISE_SUPPR 0x08 +#define V_ATT_LEV 0x20 +#define V_CONF_SL 0x80 +/* R_CONF_OFLOW */ +#define V_CONF_OFLOW0 0x01 +#define V_CONF_OFLOW1 0x02 +#define V_CONF_OFLOW2 0x04 +#define V_CONF_OFLOW3 0x08 +#define V_CONF_OFLOW4 0x10 +#define V_CONF_OFLOW5 0x20 +#define V_CONF_OFLOW6 0x40 +#define V_CONF_OFLOW7 0x80 + +/* chapter 9: DTMF contoller */ +/* R_DTMF0 */ +#define V_DTMF_EN 0x01 +#define V_HARM_SEL 0x02 +#define V_DTMF_RX_CH 0x04 +#define V_DTMF_STOP 0x08 +#define V_CHBL_SEL 0x10 +#define V_RST_DTMF 0x40 +#define V_ULAW_SEL 0x80 + +/* chapter 10: BERT */ +/* R_BERT_WD_MD */ +#define V_PAT_SEQ 0x01 +#define V_BERT_ERR 0x08 +#define V_AUTO_WD_RES 0x20 +#define V_WD_RES 0x80 +/* R_BERT_STA */ +#define V_BERT_SYNC_SRC 0x01 +#define V_BERT_SYNC 0x10 +#define V_BERT_INV_DATA 0x20 + +/* chapter 11: auxiliary interface */ +/* R_BRG_PCM_CFG */ +#define V_BRG_EN 0x01 +#define V_BRG_MD 0x02 +#define V_PCM_CLK 0x20 +#define V_ADDR_WRDLY 0x40 +/* R_BRG_CTRL */ +#define V_BRG_CS 0x01 +#define V_BRG_ADDR 0x08 +#define V_BRG_CS_SRC 0x80 +/* R_BRG_MD */ +#define V_BRG_MD0 0x01 +#define V_BRG_MD1 0x02 +#define V_BRG_MD2 0x04 +#define V_BRG_MD3 0x08 +#define V_BRG_MD4 0x10 +#define V_BRG_MD5 0x20 +#define V_BRG_MD6 0x40 +#define V_BRG_MD7 0x80 +/* R_BRG_TIM0 */ +#define V_BRG_TIM0_IDLE 0x01 +#define V_BRG_TIM0_CLK 0x10 +/* R_BRG_TIM1 */ +#define V_BRG_TIM1_IDLE 0x01 +#define V_BRG_TIM1_CLK 0x10 +/* R_BRG_TIM2 */ +#define V_BRG_TIM2_IDLE 0x01 +#define V_BRG_TIM2_CLK 0x10 +/* R_BRG_TIM3 */ +#define V_BRG_TIM3_IDLE 0x01 +#define V_BRG_TIM3_CLK 0x10 +/* R_BRG_TIM_SEL01 */ +#define V_BRG_WR_SEL0 0x01 +#define V_BRG_RD_SEL0 0x04 +#define V_BRG_WR_SEL1 0x10 +#define V_BRG_RD_SEL1 0x40 +/* R_BRG_TIM_SEL23 */ +#define V_BRG_WR_SEL2 0x01 +#define V_BRG_RD_SEL2 0x04 +#define V_BRG_WR_SEL3 0x10 +#define V_BRG_RD_SEL3 0x40 +/* R_BRG_TIM_SEL45 */ +#define V_BRG_WR_SEL4 0x01 +#define V_BRG_RD_SEL4 0x04 +#define V_BRG_WR_SEL5 0x10 +#define V_BRG_RD_SEL5 0x40 +/* R_BRG_TIM_SEL67 */ +#define V_BRG_WR_SEL6 0x01 +#define V_BRG_RD_SEL6 0x04 +#define V_BRG_WR_SEL7 0x10 +#define V_BRG_RD_SEL7 0x40 + +/* chapter 12: clock, reset, interrupt, timer and watchdog */ +/* R_IRQMSK_MISC */ +#define V_STA_IRQMSK 0x01 +#define V_TI_IRQMSK 0x02 +#define V_PROC_IRQMSK 0x04 +#define V_DTMF_IRQMSK 0x08 +#define V_IRQ1S_MSK 0x10 +#define V_SA6_IRQMSK 0x20 +#define V_RX_EOMF_MSK 0x40 +#define V_TX_EOMF_MSK 0x80 +/* R_IRQ_CTRL */ +#define V_FIFO_IRQ 0x01 +#define V_GLOB_IRQ_EN 0x08 +#define V_IRQ_POL 0x10 +/* R_TI_WD */ +#define V_EV_TS 0x01 +#define V_WD_TS 0x10 +/* A_IRQ_MSK */ +#define V_IRQ 0x01 +#define V_BERT_EN 0x02 +#define V_MIX_IRQ 0x04 +/* R_IRQ_OVIEW */ +#define V_IRQ_FIFO_BL0 0x01 +#define V_IRQ_FIFO_BL1 0x02 +#define V_IRQ_FIFO_BL2 0x04 +#define V_IRQ_FIFO_BL3 0x08 +#define V_IRQ_FIFO_BL4 0x10 +#define V_IRQ_FIFO_BL5 0x20 +#define V_IRQ_FIFO_BL6 0x40 +#define V_IRQ_FIFO_BL7 0x80 +/* R_IRQ_MISC */ +#define V_STA_IRQ 0x01 +#define V_TI_IRQ 0x02 +#define V_IRQ_PROC 0x04 +#define V_DTMF_IRQ 0x08 +#define V_IRQ1S 0x10 +#define V_SA6_IRQ 0x20 +#define V_RX_EOMF 0x40 +#define V_TX_EOMF 0x80 +/* R_STATUS */ +#define V_BUSY 0x01 +#define V_PROC 0x02 +#define V_DTMF_STA 0x04 +#define V_LOST_STA 0x08 +#define V_SYNC_IN 0x10 +#define V_EXT_IRQSTA 0x20 +#define V_MISC_IRQSTA 0x40 +#define V_FR_IRQSTA 0x80 +/* R_IRQ_FIFO_BL0 */ +#define V_IRQ_FIFO0_TX 0x01 +#define V_IRQ_FIFO0_RX 0x02 +#define V_IRQ_FIFO1_TX 0x04 +#define V_IRQ_FIFO1_RX 0x08 +#define V_IRQ_FIFO2_TX 0x10 +#define V_IRQ_FIFO2_RX 0x20 +#define V_IRQ_FIFO3_TX 0x40 +#define V_IRQ_FIFO3_RX 0x80 +/* R_IRQ_FIFO_BL1 */ +#define V_IRQ_FIFO4_TX 0x01 +#define V_IRQ_FIFO4_RX 0x02 +#define V_IRQ_FIFO5_TX 0x04 +#define V_IRQ_FIFO5_RX 0x08 +#define V_IRQ_FIFO6_TX 0x10 +#define V_IRQ_FIFO6_RX 0x20 +#define V_IRQ_FIFO7_TX 0x40 +#define V_IRQ_FIFO7_RX 0x80 +/* R_IRQ_FIFO_BL2 */ +#define V_IRQ_FIFO8_TX 0x01 +#define V_IRQ_FIFO8_RX 0x02 +#define V_IRQ_FIFO9_TX 0x04 +#define V_IRQ_FIFO9_RX 0x08 +#define V_IRQ_FIFO10_TX 0x10 +#define V_IRQ_FIFO10_RX 0x20 +#define V_IRQ_FIFO11_TX 0x40 +#define V_IRQ_FIFO11_RX 0x80 +/* R_IRQ_FIFO_BL3 */ +#define V_IRQ_FIFO12_TX 0x01 +#define V_IRQ_FIFO12_RX 0x02 +#define V_IRQ_FIFO13_TX 0x04 +#define V_IRQ_FIFO13_RX 0x08 +#define V_IRQ_FIFO14_TX 0x10 +#define V_IRQ_FIFO14_RX 0x20 +#define V_IRQ_FIFO15_TX 0x40 +#define V_IRQ_FIFO15_RX 0x80 +/* R_IRQ_FIFO_BL4 */ +#define V_IRQ_FIFO16_TX 0x01 +#define V_IRQ_FIFO16_RX 0x02 +#define V_IRQ_FIFO17_TX 0x04 +#define V_IRQ_FIFO17_RX 0x08 +#define V_IRQ_FIFO18_TX 0x10 +#define V_IRQ_FIFO18_RX 0x20 +#define V_IRQ_FIFO19_TX 0x40 +#define V_IRQ_FIFO19_RX 0x80 +/* R_IRQ_FIFO_BL5 */ +#define V_IRQ_FIFO20_TX 0x01 +#define V_IRQ_FIFO20_RX 0x02 +#define V_IRQ_FIFO21_TX 0x04 +#define V_IRQ_FIFO21_RX 0x08 +#define V_IRQ_FIFO22_TX 0x10 +#define V_IRQ_FIFO22_RX 0x20 +#define V_IRQ_FIFO23_TX 0x40 +#define V_IRQ_FIFO23_RX 0x80 +/* R_IRQ_FIFO_BL6 */ +#define V_IRQ_FIFO24_TX 0x01 +#define V_IRQ_FIFO24_RX 0x02 +#define V_IRQ_FIFO25_TX 0x04 +#define V_IRQ_FIFO25_RX 0x08 +#define V_IRQ_FIFO26_TX 0x10 +#define V_IRQ_FIFO26_RX 0x20 +#define V_IRQ_FIFO27_TX 0x40 +#define V_IRQ_FIFO27_RX 0x80 +/* R_IRQ_FIFO_BL7 */ +#define V_IRQ_FIFO28_TX 0x01 +#define V_IRQ_FIFO28_RX 0x02 +#define V_IRQ_FIFO29_TX 0x04 +#define V_IRQ_FIFO29_RX 0x08 +#define V_IRQ_FIFO30_TX 0x10 +#define V_IRQ_FIFO30_RX 0x20 +#define V_IRQ_FIFO31_TX 0x40 +#define V_IRQ_FIFO31_RX 0x80 + +/* chapter 13: general purpose I/O pins (GPIO) and input pins (GPI) */ +/* R_GPIO_OUT0 */ +#define V_GPIO_OUT0 0x01 +#define V_GPIO_OUT1 0x02 +#define V_GPIO_OUT2 0x04 +#define V_GPIO_OUT3 0x08 +#define V_GPIO_OUT4 0x10 +#define V_GPIO_OUT5 0x20 +#define V_GPIO_OUT6 0x40 +#define V_GPIO_OUT7 0x80 +/* R_GPIO_OUT1 */ +#define V_GPIO_OUT8 0x01 +#define V_GPIO_OUT9 0x02 +#define V_GPIO_OUT10 0x04 +#define V_GPIO_OUT11 0x08 +#define V_GPIO_OUT12 0x10 +#define V_GPIO_OUT13 0x20 +#define V_GPIO_OUT14 0x40 +#define V_GPIO_OUT15 0x80 +/* R_GPIO_EN0 */ +#define V_GPIO_EN0 0x01 +#define V_GPIO_EN1 0x02 +#define V_GPIO_EN2 0x04 +#define V_GPIO_EN3 0x08 +#define V_GPIO_EN4 0x10 +#define V_GPIO_EN5 0x20 +#define V_GPIO_EN6 0x40 +#define V_GPIO_EN7 0x80 +/* R_GPIO_EN1 */ +#define V_GPIO_EN8 0x01 +#define V_GPIO_EN9 0x02 +#define V_GPIO_EN10 0x04 +#define V_GPIO_EN11 0x08 +#define V_GPIO_EN12 0x10 +#define V_GPIO_EN13 0x20 +#define V_GPIO_EN14 0x40 +#define V_GPIO_EN15 0x80 +/* R_GPIO_SEL */ +#define V_GPIO_SEL0 0x01 +#define V_GPIO_SEL1 0x02 +#define V_GPIO_SEL2 0x04 +#define V_GPIO_SEL3 0x08 +#define V_GPIO_SEL4 0x10 +#define V_GPIO_SEL5 0x20 +#define V_GPIO_SEL6 0x40 +#define V_GPIO_SEL7 0x80 +/* R_GPIO_IN0 */ +#define V_GPIO_IN0 0x01 +#define V_GPIO_IN1 0x02 +#define V_GPIO_IN2 0x04 +#define V_GPIO_IN3 0x08 +#define V_GPIO_IN4 0x10 +#define V_GPIO_IN5 0x20 +#define V_GPIO_IN6 0x40 +#define V_GPIO_IN7 0x80 +/* R_GPIO_IN1 */ +#define V_GPIO_IN8 0x01 +#define V_GPIO_IN9 0x02 +#define V_GPIO_IN10 0x04 +#define V_GPIO_IN11 0x08 +#define V_GPIO_IN12 0x10 +#define V_GPIO_IN13 0x20 +#define V_GPIO_IN14 0x40 +#define V_GPIO_IN15 0x80 +/* R_GPI_IN0 */ +#define V_GPI_IN0 0x01 +#define V_GPI_IN1 0x02 +#define V_GPI_IN2 0x04 +#define V_GPI_IN3 0x08 +#define V_GPI_IN4 0x10 +#define V_GPI_IN5 0x20 +#define V_GPI_IN6 0x40 +#define V_GPI_IN7 0x80 +/* R_GPI_IN1 */ +#define V_GPI_IN8 0x01 +#define V_GPI_IN9 0x02 +#define V_GPI_IN10 0x04 +#define V_GPI_IN11 0x08 +#define V_GPI_IN12 0x10 +#define V_GPI_IN13 0x20 +#define V_GPI_IN14 0x40 +#define V_GPI_IN15 0x80 +/* R_GPI_IN2 */ +#define V_GPI_IN16 0x01 +#define V_GPI_IN17 0x02 +#define V_GPI_IN18 0x04 +#define V_GPI_IN19 0x08 +#define V_GPI_IN20 0x10 +#define V_GPI_IN21 0x20 +#define V_GPI_IN22 0x40 +#define V_GPI_IN23 0x80 +/* R_GPI_IN3 */ +#define V_GPI_IN24 0x01 +#define V_GPI_IN25 0x02 +#define V_GPI_IN26 0x04 +#define V_GPI_IN27 0x08 +#define V_GPI_IN28 0x10 +#define V_GPI_IN29 0x20 +#define V_GPI_IN30 0x40 +#define V_GPI_IN31 0x80 + +/* map of all registers, used for debugging */ + +#ifdef HFC_REGISTER_DEBUG +struct hfc_register_names { + char *name; + u_char reg; +} hfc_register_names[] = { + /* write registers */ + {"R_CIRM", 0x00}, + {"R_CTRL", 0x01}, + {"R_BRG_PCM_CFG ", 0x02}, + {"R_RAM_ADDR0", 0x08}, + {"R_RAM_ADDR1", 0x09}, + {"R_RAM_ADDR2", 0x0A}, + {"R_FIRST_FIFO", 0x0B}, + {"R_RAM_SZ", 0x0C}, + {"R_FIFO_MD", 0x0D}, + {"R_INC_RES_FIFO", 0x0E}, + {"R_FIFO / R_FSM_IDX", 0x0F}, + {"R_SLOT", 0x10}, + {"R_IRQMSK_MISC", 0x11}, + {"R_SCI_MSK", 0x12}, + {"R_IRQ_CTRL", 0x13}, + {"R_PCM_MD0", 0x14}, + {"R_0x15", 0x15}, + {"R_ST_SEL", 0x16}, + {"R_ST_SYNC", 0x17}, + {"R_CONF_EN", 0x18}, + {"R_TI_WD", 0x1A}, + {"R_BERT_WD_MD", 0x1B}, + {"R_DTMF", 0x1C}, + {"R_DTMF_N", 0x1D}, + {"R_E1_XX_STA", 0x20}, + {"R_LOS0", 0x22}, + {"R_LOS1", 0x23}, + {"R_RX0", 0x24}, + {"R_RX_FR0", 0x25}, + {"R_RX_FR1", 0x26}, + {"R_TX0", 0x28}, + {"R_TX1", 0x29}, + {"R_TX_FR0", 0x2C}, + {"R_TX_FR1", 0x2D}, + {"R_TX_FR2", 0x2E}, + {"R_JATT_ATT", 0x2F}, + {"A_ST_xx_STA/R_RX_OFF", 0x30}, + {"A_ST_CTRL0/R_SYNC_OUT", 0x31}, + {"A_ST_CTRL1", 0x32}, + {"A_ST_CTRL2", 0x33}, + {"A_ST_SQ_WR", 0x34}, + {"R_TX_OFF", 0x34}, + {"R_SYNC_CTRL", 0x35}, + {"A_ST_CLK_DLY", 0x37}, + {"R_PWM0", 0x38}, + {"R_PWM1", 0x39}, + {"A_ST_B1_TX", 0x3C}, + {"A_ST_B2_TX", 0x3D}, + {"A_ST_D_TX", 0x3E}, + {"R_GPIO_OUT0", 0x40}, + {"R_GPIO_OUT1", 0x41}, + {"R_GPIO_EN0", 0x42}, + {"R_GPIO_EN1", 0x43}, + {"R_GPIO_SEL", 0x44}, + {"R_BRG_CTRL", 0x45}, + {"R_PWM_MD", 0x46}, + {"R_BRG_MD", 0x47}, + {"R_BRG_TIM0", 0x48}, + {"R_BRG_TIM1", 0x49}, + {"R_BRG_TIM2", 0x4A}, + {"R_BRG_TIM3", 0x4B}, + {"R_BRG_TIM_SEL01", 0x4C}, + {"R_BRG_TIM_SEL23", 0x4D}, + {"R_BRG_TIM_SEL45", 0x4E}, + {"R_BRG_TIM_SEL67", 0x4F}, + {"A_FIFO_DATA0-2", 0x80}, + {"A_FIFO_DATA0-2_NOINC", 0x84}, + {"R_RAM_DATA", 0xC0}, + {"A_SL_CFG", 0xD0}, + {"A_CONF", 0xD1}, + {"A_CH_MSK", 0xF4}, + {"A_CON_HDLC", 0xFA}, + {"A_SUBCH_CFG", 0xFB}, + {"A_CHANNEL", 0xFC}, + {"A_FIFO_SEQ", 0xFD}, + {"A_IRQ_MSK", 0xFF}, + {NULL, 0}, + + /* read registers */ + {"A_Z1", 0x04}, + {"A_Z1H", 0x05}, + {"A_Z2", 0x06}, + {"A_Z2H", 0x07}, + {"A_F1", 0x0C}, + {"A_F2", 0x0D}, + {"R_IRQ_OVIEW", 0x10}, + {"R_IRQ_MISC", 0x11}, + {"R_IRQ_STATECH", 0x12}, + {"R_CONF_OFLOW", 0x14}, + {"R_RAM_USE", 0x15}, + {"R_CHIP_ID", 0x16}, + {"R_BERT_STA", 0x17}, + {"R_F0_CNTL", 0x18}, + {"R_F0_CNTH", 0x19}, + {"R_BERT_ECL", 0x1A}, + {"R_BERT_ECH", 0x1B}, + {"R_STATUS", 0x1C}, + {"R_CHIP_RV", 0x1F}, + {"R_STATE", 0x20}, + {"R_SYNC_STA", 0x24}, + {"R_RX_SL0_0", 0x25}, + {"R_RX_SL0_1", 0x26}, + {"R_RX_SL0_2", 0x27}, + {"R_JATT_DIR", 0x2b}, + {"R_SLIP", 0x2c}, + {"A_ST_RD_STA", 0x30}, + {"R_FAS_ECL", 0x30}, + {"R_FAS_ECH", 0x31}, + {"R_VIO_ECL", 0x32}, + {"R_VIO_ECH", 0x33}, + {"R_CRC_ECL / A_ST_SQ_RD", 0x34}, + {"R_CRC_ECH", 0x35}, + {"R_E_ECL", 0x36}, + {"R_E_ECH", 0x37}, + {"R_SA6_SA13_ECL", 0x38}, + {"R_SA6_SA13_ECH", 0x39}, + {"R_SA6_SA23_ECL", 0x3A}, + {"R_SA6_SA23_ECH", 0x3B}, + {"A_ST_B1_RX", 0x3C}, + {"A_ST_B2_RX", 0x3D}, + {"A_ST_D_RX", 0x3E}, + {"A_ST_E_RX", 0x3F}, + {"R_GPIO_IN0", 0x40}, + {"R_GPIO_IN1", 0x41}, + {"R_GPI_IN0", 0x44}, + {"R_GPI_IN1", 0x45}, + {"R_GPI_IN2", 0x46}, + {"R_GPI_IN3", 0x47}, + {"A_FIFO_DATA0-2", 0x80}, + {"A_FIFO_DATA0-2_NOINC", 0x84}, + {"R_INT_DATA", 0x88}, + {"R_RAM_DATA", 0xC0}, + {"R_IRQ_FIFO_BL0", 0xC8}, + {"R_IRQ_FIFO_BL1", 0xC9}, + {"R_IRQ_FIFO_BL2", 0xCA}, + {"R_IRQ_FIFO_BL3", 0xCB}, + {"R_IRQ_FIFO_BL4", 0xCC}, + {"R_IRQ_FIFO_BL5", 0xCD}, + {"R_IRQ_FIFO_BL6", 0xCE}, + {"R_IRQ_FIFO_BL7", 0xCF}, +}; +#endif /* HFC_REGISTER_DEBUG */ diff --git a/drivers/isdn/hardware/mISDN/hfc_multi_8xx.h b/drivers/isdn/hardware/mISDN/hfc_multi_8xx.h new file mode 100644 index 000000000..448ded8f9 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfc_multi_8xx.h @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * For License see notice in hfc_multi.c + * + * special IO and init functions for the embedded XHFC board + * from Speech Design + * + */ + +#include <asm/cpm1.h> + +/* Change this to the value used by your board */ +#ifndef IMAP_ADDR +#define IMAP_ADDR 0xFFF00000 +#endif + +static void +#ifdef HFC_REGISTER_DEBUG +HFC_outb_embsd(struct hfc_multi *hc, u_char reg, u_char val, + const char *function, int line) +#else + HFC_outb_embsd(struct hfc_multi *hc, u_char reg, u_char val) +#endif +{ + hc->immap->im_ioport.iop_padat |= PA_XHFC_A0; + writeb(reg, hc->xhfc_memaddr); + hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0); + writeb(val, hc->xhfc_memdata); +} +static u_char +#ifdef HFC_REGISTER_DEBUG +HFC_inb_embsd(struct hfc_multi *hc, u_char reg, const char *function, int line) +#else + HFC_inb_embsd(struct hfc_multi *hc, u_char reg) +#endif +{ + hc->immap->im_ioport.iop_padat |= PA_XHFC_A0; + writeb(reg, hc->xhfc_memaddr); + hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0); + return readb(hc->xhfc_memdata); +} +static u_short +#ifdef HFC_REGISTER_DEBUG +HFC_inw_embsd(struct hfc_multi *hc, u_char reg, const char *function, int line) +#else + HFC_inw_embsd(struct hfc_multi *hc, u_char reg) +#endif +{ + hc->immap->im_ioport.iop_padat |= PA_XHFC_A0; + writeb(reg, hc->xhfc_memaddr); + hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0); + return readb(hc->xhfc_memdata); +} +static void +#ifdef HFC_REGISTER_DEBUG +HFC_wait_embsd(struct hfc_multi *hc, const char *function, int line) +#else + HFC_wait_embsd(struct hfc_multi *hc) +#endif +{ + hc->immap->im_ioport.iop_padat |= PA_XHFC_A0; + writeb(R_STATUS, hc->xhfc_memaddr); + hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0); + while (readb(hc->xhfc_memdata) & V_BUSY) + cpu_relax(); +} + +/* write fifo data (EMBSD) */ +void +write_fifo_embsd(struct hfc_multi *hc, u_char *data, int len) +{ + hc->immap->im_ioport.iop_padat |= PA_XHFC_A0; + *hc->xhfc_memaddr = A_FIFO_DATA0; + hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0); + while (len) { + *hc->xhfc_memdata = *data; + data++; + len--; + } +} + +/* read fifo data (EMBSD) */ +void +read_fifo_embsd(struct hfc_multi *hc, u_char *data, int len) +{ + hc->immap->im_ioport.iop_padat |= PA_XHFC_A0; + *hc->xhfc_memaddr = A_FIFO_DATA0; + hc->immap->im_ioport.iop_padat &= ~(PA_XHFC_A0); + while (len) { + *data = (u_char)(*hc->xhfc_memdata); + data++; + len--; + } +} + +static int +setup_embedded(struct hfc_multi *hc, struct hm_map *m) +{ + printk(KERN_INFO + "HFC-multi: card manufacturer: '%s' card name: '%s' clock: %s\n", + m->vendor_name, m->card_name, m->clock2 ? "double" : "normal"); + + hc->pci_dev = NULL; + if (m->clock2) + test_and_set_bit(HFC_CHIP_CLOCK2, &hc->chip); + + hc->leds = m->leds; + hc->ledstate = 0xAFFEAFFE; + hc->opticalsupport = m->opticalsupport; + + hc->pci_iobase = 0; + hc->pci_membase = 0; + hc->xhfc_membase = NULL; + hc->xhfc_memaddr = NULL; + hc->xhfc_memdata = NULL; + + /* set memory access methods */ + if (m->io_mode) /* use mode from card config */ + hc->io_mode = m->io_mode; + switch (hc->io_mode) { + case HFC_IO_MODE_EMBSD: + test_and_set_bit(HFC_CHIP_EMBSD, &hc->chip); + hc->slots = 128; /* required */ + hc->HFC_outb = HFC_outb_embsd; + hc->HFC_inb = HFC_inb_embsd; + hc->HFC_inw = HFC_inw_embsd; + hc->HFC_wait = HFC_wait_embsd; + hc->read_fifo = read_fifo_embsd; + hc->write_fifo = write_fifo_embsd; + hc->xhfc_origmembase = XHFC_MEMBASE + XHFC_OFFSET * hc->id; + hc->xhfc_membase = (u_char *)ioremap(hc->xhfc_origmembase, + XHFC_MEMSIZE); + if (!hc->xhfc_membase) { + printk(KERN_WARNING + "HFC-multi: failed to remap xhfc address space. " + "(internal error)\n"); + return -EIO; + } + hc->xhfc_memaddr = (u_long *)(hc->xhfc_membase + 4); + hc->xhfc_memdata = (u_long *)(hc->xhfc_membase); + printk(KERN_INFO + "HFC-multi: xhfc_membase:%#lx xhfc_origmembase:%#lx " + "xhfc_memaddr:%#lx xhfc_memdata:%#lx\n", + (u_long)hc->xhfc_membase, hc->xhfc_origmembase, + (u_long)hc->xhfc_memaddr, (u_long)hc->xhfc_memdata); + break; + default: + printk(KERN_WARNING "HFC-multi: Invalid IO mode.\n"); + return -EIO; + } + + /* Prepare the MPC8XX PortA 10 as output (address/data selector) */ + hc->immap = (struct immap *)(IMAP_ADDR); + hc->immap->im_ioport.iop_papar &= ~(PA_XHFC_A0); + hc->immap->im_ioport.iop_paodr &= ~(PA_XHFC_A0); + hc->immap->im_ioport.iop_padir |= PA_XHFC_A0; + + /* Prepare the MPC8xx PortB __X__ as input (ISDN__X__IRQ) */ + hc->pb_irqmsk = (PB_XHFC_IRQ1 << hc->id); + hc->immap->im_cpm.cp_pbpar &= ~(hc->pb_irqmsk); + hc->immap->im_cpm.cp_pbodr &= ~(hc->pb_irqmsk); + hc->immap->im_cpm.cp_pbdir &= ~(hc->pb_irqmsk); + + /* At this point the needed config is done */ + /* fifos are still not enabled */ + return 0; +} diff --git a/drivers/isdn/hardware/mISDN/hfc_pci.h b/drivers/isdn/hardware/mISDN/hfc_pci.h new file mode 100644 index 000000000..a0e4806c1 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfc_pci.h @@ -0,0 +1,214 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * specific defines for CCD's HFC 2BDS0 PCI chips + * + * Author Werner Cornelius (werner@isdn4linux.de) + * + * Copyright 1999 by Werner Cornelius (werner@isdn4linux.de) + */ + +/* + * thresholds for transparent B-channel mode + * change mask and threshold simultaneously + */ +#define HFCPCI_BTRANS_THRESHOLD 128 +#define HFCPCI_FILLEMPTY 64 +#define HFCPCI_BTRANS_THRESMASK 0x00 + +/* defines for PCI config */ +#define PCI_ENA_MEMIO 0x02 +#define PCI_ENA_MASTER 0x04 + +/* GCI/IOM bus monitor registers */ +#define HCFPCI_C_I 0x08 +#define HFCPCI_TRxR 0x0C +#define HFCPCI_MON1_D 0x28 +#define HFCPCI_MON2_D 0x2C + +/* GCI/IOM bus timeslot registers */ +#define HFCPCI_B1_SSL 0x80 +#define HFCPCI_B2_SSL 0x84 +#define HFCPCI_AUX1_SSL 0x88 +#define HFCPCI_AUX2_SSL 0x8C +#define HFCPCI_B1_RSL 0x90 +#define HFCPCI_B2_RSL 0x94 +#define HFCPCI_AUX1_RSL 0x98 +#define HFCPCI_AUX2_RSL 0x9C + +/* GCI/IOM bus data registers */ +#define HFCPCI_B1_D 0xA0 +#define HFCPCI_B2_D 0xA4 +#define HFCPCI_AUX1_D 0xA8 +#define HFCPCI_AUX2_D 0xAC + +/* GCI/IOM bus configuration registers */ +#define HFCPCI_MST_EMOD 0xB4 +#define HFCPCI_MST_MODE 0xB8 +#define HFCPCI_CONNECT 0xBC + + +/* Interrupt and status registers */ +#define HFCPCI_FIFO_EN 0x44 +#define HFCPCI_TRM 0x48 +#define HFCPCI_B_MODE 0x4C +#define HFCPCI_CHIP_ID 0x58 +#define HFCPCI_CIRM 0x60 +#define HFCPCI_CTMT 0x64 +#define HFCPCI_INT_M1 0x68 +#define HFCPCI_INT_M2 0x6C +#define HFCPCI_INT_S1 0x78 +#define HFCPCI_INT_S2 0x7C +#define HFCPCI_STATUS 0x70 + +/* S/T section registers */ +#define HFCPCI_STATES 0xC0 +#define HFCPCI_SCTRL 0xC4 +#define HFCPCI_SCTRL_E 0xC8 +#define HFCPCI_SCTRL_R 0xCC +#define HFCPCI_SQ 0xD0 +#define HFCPCI_CLKDEL 0xDC +#define HFCPCI_B1_REC 0xF0 +#define HFCPCI_B1_SEND 0xF0 +#define HFCPCI_B2_REC 0xF4 +#define HFCPCI_B2_SEND 0xF4 +#define HFCPCI_D_REC 0xF8 +#define HFCPCI_D_SEND 0xF8 +#define HFCPCI_E_REC 0xFC + + +/* bits in status register (READ) */ +#define HFCPCI_PCI_PROC 0x02 +#define HFCPCI_NBUSY 0x04 +#define HFCPCI_TIMER_ELAP 0x10 +#define HFCPCI_STATINT 0x20 +#define HFCPCI_FRAMEINT 0x40 +#define HFCPCI_ANYINT 0x80 + +/* bits in CTMT (Write) */ +#define HFCPCI_CLTIMER 0x80 +#define HFCPCI_TIM3_125 0x04 +#define HFCPCI_TIM25 0x10 +#define HFCPCI_TIM50 0x14 +#define HFCPCI_TIM400 0x18 +#define HFCPCI_TIM800 0x1C +#define HFCPCI_AUTO_TIMER 0x20 +#define HFCPCI_TRANSB2 0x02 +#define HFCPCI_TRANSB1 0x01 + +/* bits in CIRM (Write) */ +#define HFCPCI_AUX_MSK 0x07 +#define HFCPCI_RESET 0x08 +#define HFCPCI_B1_REV 0x40 +#define HFCPCI_B2_REV 0x80 + +/* bits in INT_M1 and INT_S1 */ +#define HFCPCI_INTS_B1TRANS 0x01 +#define HFCPCI_INTS_B2TRANS 0x02 +#define HFCPCI_INTS_DTRANS 0x04 +#define HFCPCI_INTS_B1REC 0x08 +#define HFCPCI_INTS_B2REC 0x10 +#define HFCPCI_INTS_DREC 0x20 +#define HFCPCI_INTS_L1STATE 0x40 +#define HFCPCI_INTS_TIMER 0x80 + +/* bits in INT_M2 */ +#define HFCPCI_PROC_TRANS 0x01 +#define HFCPCI_GCI_I_CHG 0x02 +#define HFCPCI_GCI_MON_REC 0x04 +#define HFCPCI_IRQ_ENABLE 0x08 +#define HFCPCI_PMESEL 0x80 + +/* bits in STATES */ +#define HFCPCI_STATE_MSK 0x0F +#define HFCPCI_LOAD_STATE 0x10 +#define HFCPCI_ACTIVATE 0x20 +#define HFCPCI_DO_ACTION 0x40 +#define HFCPCI_NT_G2_G3 0x80 + +/* bits in HFCD_MST_MODE */ +#define HFCPCI_MASTER 0x01 +#define HFCPCI_SLAVE 0x00 +#define HFCPCI_F0IO_POSITIV 0x02 +#define HFCPCI_F0_NEGATIV 0x04 +#define HFCPCI_F0_2C4 0x08 +/* remaining bits are for codecs control */ + +/* bits in HFCD_SCTRL */ +#define SCTRL_B1_ENA 0x01 +#define SCTRL_B2_ENA 0x02 +#define SCTRL_MODE_TE 0x00 +#define SCTRL_MODE_NT 0x04 +#define SCTRL_LOW_PRIO 0x08 +#define SCTRL_SQ_ENA 0x10 +#define SCTRL_TEST 0x20 +#define SCTRL_NONE_CAP 0x40 +#define SCTRL_PWR_DOWN 0x80 + +/* bits in SCTRL_E */ +#define HFCPCI_AUTO_AWAKE 0x01 +#define HFCPCI_DBIT_1 0x04 +#define HFCPCI_IGNORE_COL 0x08 +#define HFCPCI_CHG_B1_B2 0x80 + +/* bits in FIFO_EN register */ +#define HFCPCI_FIFOEN_B1 0x03 +#define HFCPCI_FIFOEN_B2 0x0C +#define HFCPCI_FIFOEN_DTX 0x10 +#define HFCPCI_FIFOEN_B1TX 0x01 +#define HFCPCI_FIFOEN_B1RX 0x02 +#define HFCPCI_FIFOEN_B2TX 0x04 +#define HFCPCI_FIFOEN_B2RX 0x08 + + +/* definitions of fifo memory area */ +#define MAX_D_FRAMES 15 +#define MAX_B_FRAMES 31 +#define B_SUB_VAL 0x200 +#define B_FIFO_SIZE (0x2000 - B_SUB_VAL) +#define D_FIFO_SIZE 512 +#define D_FREG_MASK 0xF + +struct zt { + __le16 z1; /* Z1 pointer 16 Bit */ + __le16 z2; /* Z2 pointer 16 Bit */ +}; + +struct dfifo { + u_char data[D_FIFO_SIZE]; /* FIFO data space */ + u_char fill1[0x20A0 - D_FIFO_SIZE]; /* reserved, do not use */ + u_char f1, f2; /* f pointers */ + u_char fill2[0x20C0 - 0x20A2]; /* reserved, do not use */ + /* mask index with D_FREG_MASK for access */ + struct zt za[MAX_D_FRAMES + 1]; + u_char fill3[0x4000 - 0x2100]; /* align 16K */ +}; + +struct bzfifo { + struct zt za[MAX_B_FRAMES + 1]; /* only range 0x0..0x1F allowed */ + u_char f1, f2; /* f pointers */ + u_char fill[0x2100 - 0x2082]; /* alignment */ +}; + + +union fifo_area { + struct { + struct dfifo d_tx; /* D-send channel */ + struct dfifo d_rx; /* D-receive channel */ + } d_chan; + struct { + u_char fill1[0x200]; + u_char txdat_b1[B_FIFO_SIZE]; + struct bzfifo txbz_b1; + struct bzfifo txbz_b2; + u_char txdat_b2[B_FIFO_SIZE]; + u_char fill2[D_FIFO_SIZE]; + u_char rxdat_b1[B_FIFO_SIZE]; + struct bzfifo rxbz_b1; + struct bzfifo rxbz_b2; + u_char rxdat_b2[B_FIFO_SIZE]; + } b_chans; + u_char fill[32768]; +}; + +#define Write_hfc(a, b, c) (writeb(c, (a->hw.pci_io) + b)) +#define Read_hfc(a, b) (readb((a->hw.pci_io) + b)) diff --git a/drivers/isdn/hardware/mISDN/hfcmulti.c b/drivers/isdn/hardware/mISDN/hfcmulti.c new file mode 100644 index 000000000..e840609c5 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfcmulti.c @@ -0,0 +1,5569 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * hfcmulti.c low level driver for hfc-4s/hfc-8s/hfc-e1 based cards + * + * Author Andreas Eversberg (jolly@eversberg.eu) + * ported to mqueue mechanism: + * Peter Sprenger (sprengermoving-bytes.de) + * + * inspired by existing hfc-pci driver: + * Copyright 1999 by Werner Cornelius (werner@isdn-development.de) + * Copyright 2008 by Karsten Keil (kkeil@suse.de) + * Copyright 2008 by Andreas Eversberg (jolly@eversberg.eu) + * + * Thanks to Cologne Chip AG for this great controller! + */ + +/* + * module parameters: + * type: + * By default (0), the card is automatically detected. + * Or use the following combinations: + * Bit 0-7 = 0x00001 = HFC-E1 (1 port) + * or Bit 0-7 = 0x00004 = HFC-4S (4 ports) + * or Bit 0-7 = 0x00008 = HFC-8S (8 ports) + * Bit 8 = 0x00100 = uLaw (instead of aLaw) + * Bit 9 = 0x00200 = Disable DTMF detect on all B-channels via hardware + * Bit 10 = spare + * Bit 11 = 0x00800 = Force PCM bus into slave mode. (otherwhise auto) + * or Bit 12 = 0x01000 = Force PCM bus into master mode. (otherwhise auto) + * Bit 13 = spare + * Bit 14 = 0x04000 = Use external ram (128K) + * Bit 15 = 0x08000 = Use external ram (512K) + * Bit 16 = 0x10000 = Use 64 timeslots instead of 32 + * or Bit 17 = 0x20000 = Use 128 timeslots instead of anything else + * Bit 18 = spare + * Bit 19 = 0x80000 = Send the Watchdog a Signal (Dual E1 with Watchdog) + * (all other bits are reserved and shall be 0) + * example: 0x20204 one HFC-4S with dtmf detection and 128 timeslots on PCM + * bus (PCM master) + * + * port: (optional or required for all ports on all installed cards) + * HFC-4S/HFC-8S only bits: + * Bit 0 = 0x001 = Use master clock for this S/T interface + * (ony once per chip). + * Bit 1 = 0x002 = transmitter line setup (non capacitive mode) + * Don't use this unless you know what you are doing! + * Bit 2 = 0x004 = Disable E-channel. (No E-channel processing) + * example: 0x0001,0x0000,0x0000,0x0000 one HFC-4S with master clock + * received from port 1 + * + * HFC-E1 only bits: + * Bit 0 = 0x0001 = interface: 0=copper, 1=optical + * Bit 1 = 0x0002 = reserved (later for 32 B-channels transparent mode) + * Bit 2 = 0x0004 = Report LOS + * Bit 3 = 0x0008 = Report AIS + * Bit 4 = 0x0010 = Report SLIP + * Bit 5 = 0x0020 = Report RDI + * Bit 8 = 0x0100 = Turn off CRC-4 Multiframe Mode, use double frame + * mode instead. + * Bit 9 = 0x0200 = Force get clock from interface, even in NT mode. + * or Bit 10 = 0x0400 = Force put clock to interface, even in TE mode. + * Bit 11 = 0x0800 = Use direct RX clock for PCM sync rather than PLL. + * (E1 only) + * Bit 12-13 = 0xX000 = elastic jitter buffer (1-3), Set both bits to 0 + * for default. + * (all other bits are reserved and shall be 0) + * + * debug: + * NOTE: only one debug value must be given for all cards + * enable debugging (see hfc_multi.h for debug options) + * + * poll: + * NOTE: only one poll value must be given for all cards + * Give the number of samples for each fifo process. + * By default 128 is used. Decrease to reduce delay, increase to + * reduce cpu load. If unsure, don't mess with it! + * Valid is 8, 16, 32, 64, 128, 256. + * + * pcm: + * NOTE: only one pcm value must be given for every card. + * The PCM bus id tells the mISDNdsp module about the connected PCM bus. + * By default (0), the PCM bus id is 100 for the card that is PCM master. + * If multiple cards are PCM master (because they are not interconnected), + * each card with PCM master will have increasing PCM id. + * All PCM busses with the same ID are expected to be connected and have + * common time slots slots. + * Only one chip of the PCM bus must be master, the others slave. + * -1 means no support of PCM bus not even. + * Omit this value, if all cards are interconnected or none is connected. + * If unsure, don't give this parameter. + * + * dmask and bmask: + * NOTE: One dmask value must be given for every HFC-E1 card. + * If omitted, the E1 card has D-channel on time slot 16, which is default. + * dmask is a 32 bit mask. The bit must be set for an alternate time slot. + * If multiple bits are set, multiple virtual card fragments are created. + * For each bit set, a bmask value must be given. Each bit on the bmask + * value stands for a B-channel. The bmask may not overlap with dmask or + * with other bmask values for that card. + * Example: dmask=0x00020002 bmask=0x0000fffc,0xfffc0000 + * This will create one fragment with D-channel on slot 1 with + * B-channels on slots 2..15, and a second fragment with D-channel + * on slot 17 with B-channels on slot 18..31. Slot 16 is unused. + * If bit 0 is set (dmask=0x00000001) the D-channel is on slot 0 and will + * not function. + * Example: dmask=0x00000001 bmask=0xfffffffe + * This will create a port with all 31 usable timeslots as + * B-channels. + * If no bits are set on bmask, no B-channel is created for that fragment. + * Example: dmask=0xfffffffe bmask=0,0,0,0.... (31 0-values for bmask) + * This will create 31 ports with one D-channel only. + * If you don't know how to use it, you don't need it! + * + * iomode: + * NOTE: only one mode value must be given for every card. + * -> See hfc_multi.h for HFC_IO_MODE_* values + * By default, the IO mode is pci memory IO (MEMIO). + * Some cards require specific IO mode, so it cannot be changed. + * It may be useful to set IO mode to register io (REGIO) to solve + * PCI bridge problems. + * If unsure, don't give this parameter. + * + * clockdelay_nt: + * NOTE: only one clockdelay_nt value must be given once for all cards. + * Give the value of the clock control register (A_ST_CLK_DLY) + * of the S/T interfaces in NT mode. + * This register is needed for the TBR3 certification, so don't change it. + * + * clockdelay_te: + * NOTE: only one clockdelay_te value must be given once + * Give the value of the clock control register (A_ST_CLK_DLY) + * of the S/T interfaces in TE mode. + * This register is needed for the TBR3 certification, so don't change it. + * + * clock: + * NOTE: only one clock value must be given once + * Selects interface with clock source for mISDN and applications. + * Set to card number starting with 1. Set to -1 to disable. + * By default, the first card is used as clock source. + * + * hwid: + * NOTE: only one hwid value must be given once + * Enable special embedded devices with XHFC controllers. + */ + +/* + * debug register access (never use this, it will flood your system log) + * #define HFC_REGISTER_DEBUG + */ + +#define HFC_MULTI_VERSION "2.03" + +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/mISDNhw.h> +#include <linux/mISDNdsp.h> + +/* + #define IRQCOUNT_DEBUG + #define IRQ_DEBUG +*/ + +#include "hfc_multi.h" +#ifdef ECHOPREP +#include "gaintab.h" +#endif + +#define MAX_CARDS 8 +#define MAX_PORTS (8 * MAX_CARDS) +#define MAX_FRAGS (32 * MAX_CARDS) + +static LIST_HEAD(HFClist); +static DEFINE_SPINLOCK(HFClock); /* global hfc list lock */ + +static void ph_state_change(struct dchannel *); + +static struct hfc_multi *syncmaster; +static int plxsd_master; /* if we have a master card (yet) */ +static DEFINE_SPINLOCK(plx_lock); /* may not acquire other lock inside */ + +#define TYP_E1 1 +#define TYP_4S 4 +#define TYP_8S 8 + +static int poll_timer = 6; /* default = 128 samples = 16ms */ +/* number of POLL_TIMER interrupts for G2 timeout (ca 1s) */ +static int nt_t1_count[] = { 3840, 1920, 960, 480, 240, 120, 60, 30 }; +#define CLKDEL_TE 0x0f /* CLKDEL in TE mode */ +#define CLKDEL_NT 0x6c /* CLKDEL in NT mode + (0x60 MUST be included!) */ + +#define DIP_4S 0x1 /* DIP Switches for Beronet 1S/2S/4S cards */ +#define DIP_8S 0x2 /* DIP Switches for Beronet 8S+ cards */ +#define DIP_E1 0x3 /* DIP Switches for Beronet E1 cards */ + +/* + * module stuff + */ + +static uint type[MAX_CARDS]; +static int pcm[MAX_CARDS]; +static uint dmask[MAX_CARDS]; +static uint bmask[MAX_FRAGS]; +static uint iomode[MAX_CARDS]; +static uint port[MAX_PORTS]; +static uint debug; +static uint poll; +static int clock; +static uint timer; +static uint clockdelay_te = CLKDEL_TE; +static uint clockdelay_nt = CLKDEL_NT; +#define HWID_NONE 0 +#define HWID_MINIP4 1 +#define HWID_MINIP8 2 +#define HWID_MINIP16 3 +static uint hwid = HWID_NONE; + +static int HFC_cnt, E1_cnt, bmask_cnt, Port_cnt, PCM_cnt = 99; + +MODULE_AUTHOR("Andreas Eversberg"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(HFC_MULTI_VERSION); +module_param(debug, uint, S_IRUGO | S_IWUSR); +module_param(poll, uint, S_IRUGO | S_IWUSR); +module_param(clock, int, S_IRUGO | S_IWUSR); +module_param(timer, uint, S_IRUGO | S_IWUSR); +module_param(clockdelay_te, uint, S_IRUGO | S_IWUSR); +module_param(clockdelay_nt, uint, S_IRUGO | S_IWUSR); +module_param_array(type, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(pcm, int, NULL, S_IRUGO | S_IWUSR); +module_param_array(dmask, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(bmask, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(iomode, uint, NULL, S_IRUGO | S_IWUSR); +module_param_array(port, uint, NULL, S_IRUGO | S_IWUSR); +module_param(hwid, uint, S_IRUGO | S_IWUSR); /* The hardware ID */ + +#ifdef HFC_REGISTER_DEBUG +#define HFC_outb(hc, reg, val) \ + (hc->HFC_outb(hc, reg, val, __func__, __LINE__)) +#define HFC_outb_nodebug(hc, reg, val) \ + (hc->HFC_outb_nodebug(hc, reg, val, __func__, __LINE__)) +#define HFC_inb(hc, reg) \ + (hc->HFC_inb(hc, reg, __func__, __LINE__)) +#define HFC_inb_nodebug(hc, reg) \ + (hc->HFC_inb_nodebug(hc, reg, __func__, __LINE__)) +#define HFC_inw(hc, reg) \ + (hc->HFC_inw(hc, reg, __func__, __LINE__)) +#define HFC_inw_nodebug(hc, reg) \ + (hc->HFC_inw_nodebug(hc, reg, __func__, __LINE__)) +#define HFC_wait(hc) \ + (hc->HFC_wait(hc, __func__, __LINE__)) +#define HFC_wait_nodebug(hc) \ + (hc->HFC_wait_nodebug(hc, __func__, __LINE__)) +#else +#define HFC_outb(hc, reg, val) (hc->HFC_outb(hc, reg, val)) +#define HFC_outb_nodebug(hc, reg, val) (hc->HFC_outb_nodebug(hc, reg, val)) +#define HFC_inb(hc, reg) (hc->HFC_inb(hc, reg)) +#define HFC_inb_nodebug(hc, reg) (hc->HFC_inb_nodebug(hc, reg)) +#define HFC_inw(hc, reg) (hc->HFC_inw(hc, reg)) +#define HFC_inw_nodebug(hc, reg) (hc->HFC_inw_nodebug(hc, reg)) +#define HFC_wait(hc) (hc->HFC_wait(hc)) +#define HFC_wait_nodebug(hc) (hc->HFC_wait_nodebug(hc)) +#endif + +#ifdef CONFIG_MISDN_HFCMULTI_8xx +#include "hfc_multi_8xx.h" +#endif + +/* HFC_IO_MODE_PCIMEM */ +static void +#ifdef HFC_REGISTER_DEBUG +HFC_outb_pcimem(struct hfc_multi *hc, u_char reg, u_char val, + const char *function, int line) +#else + HFC_outb_pcimem(struct hfc_multi *hc, u_char reg, u_char val) +#endif +{ + writeb(val, hc->pci_membase + reg); +} +static u_char +#ifdef HFC_REGISTER_DEBUG +HFC_inb_pcimem(struct hfc_multi *hc, u_char reg, const char *function, int line) +#else + HFC_inb_pcimem(struct hfc_multi *hc, u_char reg) +#endif +{ + return readb(hc->pci_membase + reg); +} +static u_short +#ifdef HFC_REGISTER_DEBUG +HFC_inw_pcimem(struct hfc_multi *hc, u_char reg, const char *function, int line) +#else + HFC_inw_pcimem(struct hfc_multi *hc, u_char reg) +#endif +{ + return readw(hc->pci_membase + reg); +} +static void +#ifdef HFC_REGISTER_DEBUG +HFC_wait_pcimem(struct hfc_multi *hc, const char *function, int line) +#else + HFC_wait_pcimem(struct hfc_multi *hc) +#endif +{ + while (readb(hc->pci_membase + R_STATUS) & V_BUSY) + cpu_relax(); +} + +/* HFC_IO_MODE_REGIO */ +static void +#ifdef HFC_REGISTER_DEBUG +HFC_outb_regio(struct hfc_multi *hc, u_char reg, u_char val, + const char *function, int line) +#else + HFC_outb_regio(struct hfc_multi *hc, u_char reg, u_char val) +#endif +{ + outb(reg, hc->pci_iobase + 4); + outb(val, hc->pci_iobase); +} +static u_char +#ifdef HFC_REGISTER_DEBUG +HFC_inb_regio(struct hfc_multi *hc, u_char reg, const char *function, int line) +#else + HFC_inb_regio(struct hfc_multi *hc, u_char reg) +#endif +{ + outb(reg, hc->pci_iobase + 4); + return inb(hc->pci_iobase); +} +static u_short +#ifdef HFC_REGISTER_DEBUG +HFC_inw_regio(struct hfc_multi *hc, u_char reg, const char *function, int line) +#else + HFC_inw_regio(struct hfc_multi *hc, u_char reg) +#endif +{ + outb(reg, hc->pci_iobase + 4); + return inw(hc->pci_iobase); +} +static void +#ifdef HFC_REGISTER_DEBUG +HFC_wait_regio(struct hfc_multi *hc, const char *function, int line) +#else + HFC_wait_regio(struct hfc_multi *hc) +#endif +{ + outb(R_STATUS, hc->pci_iobase + 4); + while (inb(hc->pci_iobase) & V_BUSY) + cpu_relax(); +} + +#ifdef HFC_REGISTER_DEBUG +static void +HFC_outb_debug(struct hfc_multi *hc, u_char reg, u_char val, + const char *function, int line) +{ + char regname[256] = "", bits[9] = "xxxxxxxx"; + int i; + + i = -1; + while (hfc_register_names[++i].name) { + if (hfc_register_names[i].reg == reg) + strcat(regname, hfc_register_names[i].name); + } + if (regname[0] == '\0') + strcpy(regname, "register"); + + bits[7] = '0' + (!!(val & 1)); + bits[6] = '0' + (!!(val & 2)); + bits[5] = '0' + (!!(val & 4)); + bits[4] = '0' + (!!(val & 8)); + bits[3] = '0' + (!!(val & 16)); + bits[2] = '0' + (!!(val & 32)); + bits[1] = '0' + (!!(val & 64)); + bits[0] = '0' + (!!(val & 128)); + printk(KERN_DEBUG + "HFC_outb(chip %d, %02x=%s, 0x%02x=%s); in %s() line %d\n", + hc->id, reg, regname, val, bits, function, line); + HFC_outb_nodebug(hc, reg, val); +} +static u_char +HFC_inb_debug(struct hfc_multi *hc, u_char reg, const char *function, int line) +{ + char regname[256] = "", bits[9] = "xxxxxxxx"; + u_char val = HFC_inb_nodebug(hc, reg); + int i; + + i = 0; + while (hfc_register_names[i++].name) + ; + while (hfc_register_names[++i].name) { + if (hfc_register_names[i].reg == reg) + strcat(regname, hfc_register_names[i].name); + } + if (regname[0] == '\0') + strcpy(regname, "register"); + + bits[7] = '0' + (!!(val & 1)); + bits[6] = '0' + (!!(val & 2)); + bits[5] = '0' + (!!(val & 4)); + bits[4] = '0' + (!!(val & 8)); + bits[3] = '0' + (!!(val & 16)); + bits[2] = '0' + (!!(val & 32)); + bits[1] = '0' + (!!(val & 64)); + bits[0] = '0' + (!!(val & 128)); + printk(KERN_DEBUG + "HFC_inb(chip %d, %02x=%s) = 0x%02x=%s; in %s() line %d\n", + hc->id, reg, regname, val, bits, function, line); + return val; +} +static u_short +HFC_inw_debug(struct hfc_multi *hc, u_char reg, const char *function, int line) +{ + char regname[256] = ""; + u_short val = HFC_inw_nodebug(hc, reg); + int i; + + i = 0; + while (hfc_register_names[i++].name) + ; + while (hfc_register_names[++i].name) { + if (hfc_register_names[i].reg == reg) + strcat(regname, hfc_register_names[i].name); + } + if (regname[0] == '\0') + strcpy(regname, "register"); + + printk(KERN_DEBUG + "HFC_inw(chip %d, %02x=%s) = 0x%04x; in %s() line %d\n", + hc->id, reg, regname, val, function, line); + return val; +} +static void +HFC_wait_debug(struct hfc_multi *hc, const char *function, int line) +{ + printk(KERN_DEBUG "HFC_wait(chip %d); in %s() line %d\n", + hc->id, function, line); + HFC_wait_nodebug(hc); +} +#endif + +/* write fifo data (REGIO) */ +static void +write_fifo_regio(struct hfc_multi *hc, u_char *data, int len) +{ + outb(A_FIFO_DATA0, (hc->pci_iobase) + 4); + while (len >> 2) { + outl(cpu_to_le32(*(u32 *)data), hc->pci_iobase); + data += 4; + len -= 4; + } + while (len >> 1) { + outw(cpu_to_le16(*(u16 *)data), hc->pci_iobase); + data += 2; + len -= 2; + } + while (len) { + outb(*data, hc->pci_iobase); + data++; + len--; + } +} +/* write fifo data (PCIMEM) */ +static void +write_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len) +{ + while (len >> 2) { + writel(cpu_to_le32(*(u32 *)data), + hc->pci_membase + A_FIFO_DATA0); + data += 4; + len -= 4; + } + while (len >> 1) { + writew(cpu_to_le16(*(u16 *)data), + hc->pci_membase + A_FIFO_DATA0); + data += 2; + len -= 2; + } + while (len) { + writeb(*data, hc->pci_membase + A_FIFO_DATA0); + data++; + len--; + } +} + +/* read fifo data (REGIO) */ +static void +read_fifo_regio(struct hfc_multi *hc, u_char *data, int len) +{ + outb(A_FIFO_DATA0, (hc->pci_iobase) + 4); + while (len >> 2) { + *(u32 *)data = le32_to_cpu(inl(hc->pci_iobase)); + data += 4; + len -= 4; + } + while (len >> 1) { + *(u16 *)data = le16_to_cpu(inw(hc->pci_iobase)); + data += 2; + len -= 2; + } + while (len) { + *data = inb(hc->pci_iobase); + data++; + len--; + } +} + +/* read fifo data (PCIMEM) */ +static void +read_fifo_pcimem(struct hfc_multi *hc, u_char *data, int len) +{ + while (len >> 2) { + *(u32 *)data = + le32_to_cpu(readl(hc->pci_membase + A_FIFO_DATA0)); + data += 4; + len -= 4; + } + while (len >> 1) { + *(u16 *)data = + le16_to_cpu(readw(hc->pci_membase + A_FIFO_DATA0)); + data += 2; + len -= 2; + } + while (len) { + *data = readb(hc->pci_membase + A_FIFO_DATA0); + data++; + len--; + } +} + +static void +enable_hwirq(struct hfc_multi *hc) +{ + hc->hw.r_irq_ctrl |= V_GLOB_IRQ_EN; + HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl); +} + +static void +disable_hwirq(struct hfc_multi *hc) +{ + hc->hw.r_irq_ctrl &= ~((u_char)V_GLOB_IRQ_EN); + HFC_outb(hc, R_IRQ_CTRL, hc->hw.r_irq_ctrl); +} + +#define NUM_EC 2 +#define MAX_TDM_CHAN 32 + + +static inline void +enablepcibridge(struct hfc_multi *c) +{ + HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x3); /* was _io before */ +} + +static inline void +disablepcibridge(struct hfc_multi *c) +{ + HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x2); /* was _io before */ +} + +static inline unsigned char +readpcibridge(struct hfc_multi *hc, unsigned char address) +{ + unsigned short cipv; + unsigned char data; + + if (!hc->pci_iobase) + return 0; + + /* slow down a PCI read access by 1 PCI clock cycle */ + HFC_outb(hc, R_CTRL, 0x4); /*was _io before*/ + + if (address == 0) + cipv = 0x4000; + else + cipv = 0x5800; + + /* select local bridge port address by writing to CIP port */ + /* data = HFC_inb(c, cipv); * was _io before */ + outw(cipv, hc->pci_iobase + 4); + data = inb(hc->pci_iobase); + + /* restore R_CTRL for normal PCI read cycle speed */ + HFC_outb(hc, R_CTRL, 0x0); /* was _io before */ + + return data; +} + +static inline void +writepcibridge(struct hfc_multi *hc, unsigned char address, unsigned char data) +{ + unsigned short cipv; + unsigned int datav; + + if (!hc->pci_iobase) + return; + + if (address == 0) + cipv = 0x4000; + else + cipv = 0x5800; + + /* select local bridge port address by writing to CIP port */ + outw(cipv, hc->pci_iobase + 4); + /* define a 32 bit dword with 4 identical bytes for write sequence */ + datav = data | ((__u32) data << 8) | ((__u32) data << 16) | + ((__u32) data << 24); + + /* + * write this 32 bit dword to the bridge data port + * this will initiate a write sequence of up to 4 writes to the same + * address on the local bus interface the number of write accesses + * is undefined but >=1 and depends on the next PCI transaction + * during write sequence on the local bus + */ + outl(datav, hc->pci_iobase); +} + +static inline void +cpld_set_reg(struct hfc_multi *hc, unsigned char reg) +{ + /* Do data pin read low byte */ + HFC_outb(hc, R_GPIO_OUT1, reg); +} + +static inline void +cpld_write_reg(struct hfc_multi *hc, unsigned char reg, unsigned char val) +{ + cpld_set_reg(hc, reg); + + enablepcibridge(hc); + writepcibridge(hc, 1, val); + disablepcibridge(hc); + + return; +} + +static inline unsigned char +cpld_read_reg(struct hfc_multi *hc, unsigned char reg) +{ + unsigned char bytein; + + cpld_set_reg(hc, reg); + + /* Do data pin read low byte */ + HFC_outb(hc, R_GPIO_OUT1, reg); + + enablepcibridge(hc); + bytein = readpcibridge(hc, 1); + disablepcibridge(hc); + + return bytein; +} + +static inline void +vpm_write_address(struct hfc_multi *hc, unsigned short addr) +{ + cpld_write_reg(hc, 0, 0xff & addr); + cpld_write_reg(hc, 1, 0x01 & (addr >> 8)); +} + +static inline unsigned short +vpm_read_address(struct hfc_multi *c) +{ + unsigned short addr; + unsigned short highbit; + + addr = cpld_read_reg(c, 0); + highbit = cpld_read_reg(c, 1); + + addr = addr | (highbit << 8); + + return addr & 0x1ff; +} + +static inline unsigned char +vpm_in(struct hfc_multi *c, int which, unsigned short addr) +{ + unsigned char res; + + vpm_write_address(c, addr); + + if (!which) + cpld_set_reg(c, 2); + else + cpld_set_reg(c, 3); + + enablepcibridge(c); + res = readpcibridge(c, 1); + disablepcibridge(c); + + cpld_set_reg(c, 0); + + return res; +} + +static inline void +vpm_out(struct hfc_multi *c, int which, unsigned short addr, + unsigned char data) +{ + vpm_write_address(c, addr); + + enablepcibridge(c); + + if (!which) + cpld_set_reg(c, 2); + else + cpld_set_reg(c, 3); + + writepcibridge(c, 1, data); + + cpld_set_reg(c, 0); + + disablepcibridge(c); + + { + unsigned char regin; + regin = vpm_in(c, which, addr); + if (regin != data) + printk(KERN_DEBUG "Wrote 0x%x to register 0x%x but got back " + "0x%x\n", data, addr, regin); + } + +} + + +static void +vpm_init(struct hfc_multi *wc) +{ + unsigned char reg; + unsigned int mask; + unsigned int i, x, y; + unsigned int ver; + + for (x = 0; x < NUM_EC; x++) { + /* Setup GPIO's */ + if (!x) { + ver = vpm_in(wc, x, 0x1a0); + printk(KERN_DEBUG "VPM: Chip %d: ver %02x\n", x, ver); + } + + for (y = 0; y < 4; y++) { + vpm_out(wc, x, 0x1a8 + y, 0x00); /* GPIO out */ + vpm_out(wc, x, 0x1ac + y, 0x00); /* GPIO dir */ + vpm_out(wc, x, 0x1b0 + y, 0x00); /* GPIO sel */ + } + + /* Setup TDM path - sets fsync and tdm_clk as inputs */ + reg = vpm_in(wc, x, 0x1a3); /* misc_con */ + vpm_out(wc, x, 0x1a3, reg & ~2); + + /* Setup Echo length (256 taps) */ + vpm_out(wc, x, 0x022, 1); + vpm_out(wc, x, 0x023, 0xff); + + /* Setup timeslots */ + vpm_out(wc, x, 0x02f, 0x00); + mask = 0x02020202 << (x * 4); + + /* Setup the tdm channel masks for all chips */ + for (i = 0; i < 4; i++) + vpm_out(wc, x, 0x33 - i, (mask >> (i << 3)) & 0xff); + + /* Setup convergence rate */ + printk(KERN_DEBUG "VPM: A-law mode\n"); + reg = 0x00 | 0x10 | 0x01; + vpm_out(wc, x, 0x20, reg); + printk(KERN_DEBUG "VPM reg 0x20 is %x\n", reg); + /*vpm_out(wc, x, 0x20, (0x00 | 0x08 | 0x20 | 0x10)); */ + + vpm_out(wc, x, 0x24, 0x02); + reg = vpm_in(wc, x, 0x24); + printk(KERN_DEBUG "NLP Thresh is set to %d (0x%x)\n", reg, reg); + + /* Initialize echo cans */ + for (i = 0; i < MAX_TDM_CHAN; i++) { + if (mask & (0x00000001 << i)) + vpm_out(wc, x, i, 0x00); + } + + /* + * ARM arch at least disallows a udelay of + * more than 2ms... it gives a fake "__bad_udelay" + * reference at link-time. + * long delays in kernel code are pretty sucky anyway + * for now work around it using 5 x 2ms instead of 1 x 10ms + */ + + udelay(2000); + udelay(2000); + udelay(2000); + udelay(2000); + udelay(2000); + + /* Put in bypass mode */ + for (i = 0; i < MAX_TDM_CHAN; i++) { + if (mask & (0x00000001 << i)) + vpm_out(wc, x, i, 0x01); + } + + /* Enable bypass */ + for (i = 0; i < MAX_TDM_CHAN; i++) { + if (mask & (0x00000001 << i)) + vpm_out(wc, x, 0x78 + i, 0x01); + } + + } +} + +#ifdef UNUSED +static void +vpm_check(struct hfc_multi *hctmp) +{ + unsigned char gpi2; + + gpi2 = HFC_inb(hctmp, R_GPI_IN2); + + if ((gpi2 & 0x3) != 0x3) + printk(KERN_DEBUG "Got interrupt 0x%x from VPM!\n", gpi2); +} +#endif /* UNUSED */ + + +/* + * Interface to enable/disable the HW Echocan + * + * these functions are called within a spin_lock_irqsave on + * the channel instance lock, so we are not disturbed by irqs + * + * we can later easily change the interface to make other + * things configurable, for now we configure the taps + * + */ + +static void +vpm_echocan_on(struct hfc_multi *hc, int ch, int taps) +{ + unsigned int timeslot; + unsigned int unit; + struct bchannel *bch = hc->chan[ch].bch; +#ifdef TXADJ + int txadj = -4; + struct sk_buff *skb; +#endif + if (hc->chan[ch].protocol != ISDN_P_B_RAW) + return; + + if (!bch) + return; + +#ifdef TXADJ + skb = _alloc_mISDN_skb(PH_CONTROL_IND, HFC_VOL_CHANGE_TX, + sizeof(int), &txadj, GFP_ATOMIC); + if (skb) + recv_Bchannel_skb(bch, skb); +#endif + + timeslot = ((ch / 4) * 8) + ((ch % 4) * 4) + 1; + unit = ch % 4; + + printk(KERN_NOTICE "vpm_echocan_on called taps [%d] on timeslot %d\n", + taps, timeslot); + + vpm_out(hc, unit, timeslot, 0x7e); +} + +static void +vpm_echocan_off(struct hfc_multi *hc, int ch) +{ + unsigned int timeslot; + unsigned int unit; + struct bchannel *bch = hc->chan[ch].bch; +#ifdef TXADJ + int txadj = 0; + struct sk_buff *skb; +#endif + + if (hc->chan[ch].protocol != ISDN_P_B_RAW) + return; + + if (!bch) + return; + +#ifdef TXADJ + skb = _alloc_mISDN_skb(PH_CONTROL_IND, HFC_VOL_CHANGE_TX, + sizeof(int), &txadj, GFP_ATOMIC); + if (skb) + recv_Bchannel_skb(bch, skb); +#endif + + timeslot = ((ch / 4) * 8) + ((ch % 4) * 4) + 1; + unit = ch % 4; + + printk(KERN_NOTICE "vpm_echocan_off called on timeslot %d\n", + timeslot); + /* FILLME */ + vpm_out(hc, unit, timeslot, 0x01); +} + + +/* + * Speech Design resync feature + * NOTE: This is called sometimes outside interrupt handler. + * We must lock irqsave, so no other interrupt (other card) will occur! + * Also multiple interrupts may nest, so must lock each access (lists, card)! + */ +static inline void +hfcmulti_resync(struct hfc_multi *locked, struct hfc_multi *newmaster, int rm) +{ + struct hfc_multi *hc, *next, *pcmmaster = NULL; + void __iomem *plx_acc_32; + u_int pv; + u_long flags; + + spin_lock_irqsave(&HFClock, flags); + spin_lock(&plx_lock); /* must be locked inside other locks */ + + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "%s: RESYNC(syncmaster=0x%p)\n", + __func__, syncmaster); + + /* select new master */ + if (newmaster) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "using provided controller\n"); + } else { + list_for_each_entry_safe(hc, next, &HFClist, list) { + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + if (hc->syncronized) { + newmaster = hc; + break; + } + } + } + } + + /* Disable sync of all cards */ + list_for_each_entry_safe(hc, next, &HFClist, list) { + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + plx_acc_32 = hc->plx_membase + PLX_GPIOC; + pv = readl(plx_acc_32); + pv &= ~PLX_SYNC_O_EN; + writel(pv, plx_acc_32); + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) { + pcmmaster = hc; + if (hc->ctype == HFC_TYPE_E1) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG + "Schedule SYNC_I\n"); + hc->e1_resync |= 1; /* get SYNC_I */ + } + } + } + } + + if (newmaster) { + hc = newmaster; + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "id=%d (0x%p) = syncronized with " + "interface.\n", hc->id, hc); + /* Enable new sync master */ + plx_acc_32 = hc->plx_membase + PLX_GPIOC; + pv = readl(plx_acc_32); + pv |= PLX_SYNC_O_EN; + writel(pv, plx_acc_32); + /* switch to jatt PLL, if not disabled by RX_SYNC */ + if (hc->ctype == HFC_TYPE_E1 + && !test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "Schedule jatt PLL\n"); + hc->e1_resync |= 2; /* switch to jatt */ + } + } else { + if (pcmmaster) { + hc = pcmmaster; + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG + "id=%d (0x%p) = PCM master syncronized " + "with QUARTZ\n", hc->id, hc); + if (hc->ctype == HFC_TYPE_E1) { + /* Use the crystal clock for the PCM + master card */ + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG + "Schedule QUARTZ for HFC-E1\n"); + hc->e1_resync |= 4; /* switch quartz */ + } else { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG + "QUARTZ is automatically " + "enabled by HFC-%dS\n", hc->ctype); + } + plx_acc_32 = hc->plx_membase + PLX_GPIOC; + pv = readl(plx_acc_32); + pv |= PLX_SYNC_O_EN; + writel(pv, plx_acc_32); + } else + if (!rm) + printk(KERN_ERR "%s no pcm master, this MUST " + "not happen!\n", __func__); + } + syncmaster = newmaster; + + spin_unlock(&plx_lock); + spin_unlock_irqrestore(&HFClock, flags); +} + +/* This must be called AND hc must be locked irqsave!!! */ +static inline void +plxsd_checksync(struct hfc_multi *hc, int rm) +{ + if (hc->syncronized) { + if (syncmaster == NULL) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "%s: GOT sync on card %d" + " (id=%d)\n", __func__, hc->id + 1, + hc->id); + hfcmulti_resync(hc, hc, rm); + } + } else { + if (syncmaster == hc) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "%s: LOST sync on card %d" + " (id=%d)\n", __func__, hc->id + 1, + hc->id); + hfcmulti_resync(hc, NULL, rm); + } + } +} + + +/* + * free hardware resources used by driver + */ +static void +release_io_hfcmulti(struct hfc_multi *hc) +{ + void __iomem *plx_acc_32; + u_int pv; + u_long plx_flags; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered\n", __func__); + + /* soft reset also masks all interrupts */ + hc->hw.r_cirm |= V_SRES; + HFC_outb(hc, R_CIRM, hc->hw.r_cirm); + udelay(1000); + hc->hw.r_cirm &= ~V_SRES; + HFC_outb(hc, R_CIRM, hc->hw.r_cirm); + udelay(1000); /* instead of 'wait' that may cause locking */ + + /* release Speech Design card, if PLX was initialized */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip) && hc->plx_membase) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "%s: release PLXSD card %d\n", + __func__, hc->id + 1); + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = hc->plx_membase + PLX_GPIOC; + writel(PLX_GPIOC_INIT, plx_acc_32); + pv = readl(plx_acc_32); + /* Termination off */ + pv &= ~PLX_TERM_ON; + /* Disconnect the PCM */ + pv |= PLX_SLAVE_EN_N; + pv &= ~PLX_MASTER_EN; + pv &= ~PLX_SYNC_O_EN; + /* Put the DSP in Reset */ + pv &= ~PLX_DSP_RES_N; + writel(pv, plx_acc_32); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PCM off: PLX_GPIO=%x\n", + __func__, pv); + spin_unlock_irqrestore(&plx_lock, plx_flags); + } + + /* disable memory mapped ports / io ports */ + test_and_clear_bit(HFC_CHIP_PLXSD, &hc->chip); /* prevent resync */ + if (hc->pci_dev) + pci_write_config_word(hc->pci_dev, PCI_COMMAND, 0); + if (hc->pci_membase) + iounmap(hc->pci_membase); + if (hc->plx_membase) + iounmap(hc->plx_membase); + if (hc->pci_iobase) + release_region(hc->pci_iobase, 8); + if (hc->xhfc_membase) + iounmap((void *)hc->xhfc_membase); + + if (hc->pci_dev) { + pci_disable_device(hc->pci_dev); + pci_set_drvdata(hc->pci_dev, NULL); + } + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done\n", __func__); +} + +/* + * function called to reset the HFC chip. A complete software reset of chip + * and fifos is done. All configuration of the chip is done. + */ + +static int +init_chip(struct hfc_multi *hc) +{ + u_long flags, val, val2 = 0, rev; + int i, err = 0; + u_char r_conf_en, rval; + void __iomem *plx_acc_32; + u_int pv; + u_long plx_flags, hfc_flags; + int plx_count; + struct hfc_multi *pos, *next, *plx_last_hc; + + spin_lock_irqsave(&hc->lock, flags); + /* reset all registers */ + memset(&hc->hw, 0, sizeof(struct hfcm_hw)); + + /* revision check */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered\n", __func__); + val = HFC_inb(hc, R_CHIP_ID); + if ((val >> 4) != 0x8 && (val >> 4) != 0xc && (val >> 4) != 0xe && + (val >> 1) != 0x31) { + printk(KERN_INFO "HFC_multi: unknown CHIP_ID:%x\n", (u_int)val); + err = -EIO; + goto out; + } + rev = HFC_inb(hc, R_CHIP_RV); + printk(KERN_INFO + "HFC_multi: detected HFC with chip ID=0x%lx revision=%ld%s\n", + val, rev, (rev == 0 && (hc->ctype != HFC_TYPE_XHFC)) ? + " (old FIFO handling)" : ""); + if (hc->ctype != HFC_TYPE_XHFC && rev == 0) { + test_and_set_bit(HFC_CHIP_REVISION0, &hc->chip); + printk(KERN_WARNING + "HFC_multi: NOTE: Your chip is revision 0, " + "ask Cologne Chip for update. Newer chips " + "have a better FIFO handling. Old chips " + "still work but may have slightly lower " + "HDLC transmit performance.\n"); + } + if (rev > 1) { + printk(KERN_WARNING "HFC_multi: WARNING: This driver doesn't " + "consider chip revision = %ld. The chip / " + "bridge may not work.\n", rev); + } + + /* set s-ram size */ + hc->Flen = 0x10; + hc->Zmin = 0x80; + hc->Zlen = 384; + hc->DTMFbase = 0x1000; + if (test_bit(HFC_CHIP_EXRAM_128, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: changing to 128K external RAM\n", + __func__); + hc->hw.r_ctrl |= V_EXT_RAM; + hc->hw.r_ram_sz = 1; + hc->Flen = 0x20; + hc->Zmin = 0xc0; + hc->Zlen = 1856; + hc->DTMFbase = 0x2000; + } + if (test_bit(HFC_CHIP_EXRAM_512, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: changing to 512K external RAM\n", + __func__); + hc->hw.r_ctrl |= V_EXT_RAM; + hc->hw.r_ram_sz = 2; + hc->Flen = 0x20; + hc->Zmin = 0xc0; + hc->Zlen = 8000; + hc->DTMFbase = 0x2000; + } + if (hc->ctype == HFC_TYPE_XHFC) { + hc->Flen = 0x8; + hc->Zmin = 0x0; + hc->Zlen = 64; + hc->DTMFbase = 0x0; + } + hc->max_trans = poll << 1; + if (hc->max_trans > hc->Zlen) + hc->max_trans = hc->Zlen; + + /* Speech Design PLX bridge */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "%s: initializing PLXSD card %d\n", + __func__, hc->id + 1); + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = hc->plx_membase + PLX_GPIOC; + writel(PLX_GPIOC_INIT, plx_acc_32); + pv = readl(plx_acc_32); + /* The first and the last cards are terminating the PCM bus */ + pv |= PLX_TERM_ON; /* hc is currently the last */ + /* Disconnect the PCM */ + pv |= PLX_SLAVE_EN_N; + pv &= ~PLX_MASTER_EN; + pv &= ~PLX_SYNC_O_EN; + /* Put the DSP in Reset */ + pv &= ~PLX_DSP_RES_N; + writel(pv, plx_acc_32); + spin_unlock_irqrestore(&plx_lock, plx_flags); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: slave/term: PLX_GPIO=%x\n", + __func__, pv); + /* + * If we are the 3rd PLXSD card or higher, we must turn + * termination of last PLXSD card off. + */ + spin_lock_irqsave(&HFClock, hfc_flags); + plx_count = 0; + plx_last_hc = NULL; + list_for_each_entry_safe(pos, next, &HFClist, list) { + if (test_bit(HFC_CHIP_PLXSD, &pos->chip)) { + plx_count++; + if (pos != hc) + plx_last_hc = pos; + } + } + if (plx_count >= 3) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "%s: card %d is between, so " + "we disable termination\n", + __func__, plx_last_hc->id + 1); + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = plx_last_hc->plx_membase + PLX_GPIOC; + pv = readl(plx_acc_32); + pv &= ~PLX_TERM_ON; + writel(pv, plx_acc_32); + spin_unlock_irqrestore(&plx_lock, plx_flags); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: term off: PLX_GPIO=%x\n", + __func__, pv); + } + spin_unlock_irqrestore(&HFClock, hfc_flags); + hc->hw.r_pcm_md0 = V_F0_LEN; /* shift clock for DSP */ + } + + if (test_bit(HFC_CHIP_EMBSD, &hc->chip)) + hc->hw.r_pcm_md0 = V_F0_LEN; /* shift clock for DSP */ + + /* we only want the real Z2 read-pointer for revision > 0 */ + if (!test_bit(HFC_CHIP_REVISION0, &hc->chip)) + hc->hw.r_ram_sz |= V_FZ_MD; + + /* select pcm mode */ + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: setting PCM into slave mode\n", + __func__); + } else + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip) && !plxsd_master) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: setting PCM into master mode\n", + __func__); + hc->hw.r_pcm_md0 |= V_PCM_MD; + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: performing PCM auto detect\n", + __func__); + } + + /* soft reset */ + HFC_outb(hc, R_CTRL, hc->hw.r_ctrl); + if (hc->ctype == HFC_TYPE_XHFC) + HFC_outb(hc, 0x0C /* R_FIFO_THRES */, + 0x11 /* 16 Bytes TX/RX */); + else + HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz); + HFC_outb(hc, R_FIFO_MD, 0); + if (hc->ctype == HFC_TYPE_XHFC) + hc->hw.r_cirm = V_SRES | V_HFCRES | V_PCMRES | V_STRES; + else + hc->hw.r_cirm = V_SRES | V_HFCRES | V_PCMRES | V_STRES + | V_RLD_EPR; + HFC_outb(hc, R_CIRM, hc->hw.r_cirm); + udelay(100); + hc->hw.r_cirm = 0; + HFC_outb(hc, R_CIRM, hc->hw.r_cirm); + udelay(100); + if (hc->ctype != HFC_TYPE_XHFC) + HFC_outb(hc, R_RAM_SZ, hc->hw.r_ram_sz); + + /* Speech Design PLX bridge pcm and sync mode */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = hc->plx_membase + PLX_GPIOC; + pv = readl(plx_acc_32); + /* Connect PCM */ + if (hc->hw.r_pcm_md0 & V_PCM_MD) { + pv |= PLX_MASTER_EN | PLX_SLAVE_EN_N; + pv |= PLX_SYNC_O_EN; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: master: PLX_GPIO=%x\n", + __func__, pv); + } else { + pv &= ~(PLX_MASTER_EN | PLX_SLAVE_EN_N); + pv &= ~PLX_SYNC_O_EN; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: slave: PLX_GPIO=%x\n", + __func__, pv); + } + writel(pv, plx_acc_32); + spin_unlock_irqrestore(&plx_lock, plx_flags); + } + + /* PCM setup */ + HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x90); + if (hc->slots == 32) + HFC_outb(hc, R_PCM_MD1, 0x00); + if (hc->slots == 64) + HFC_outb(hc, R_PCM_MD1, 0x10); + if (hc->slots == 128) + HFC_outb(hc, R_PCM_MD1, 0x20); + HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0xa0); + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) + HFC_outb(hc, R_PCM_MD2, V_SYNC_SRC); /* sync via SYNC_I / O */ + else if (test_bit(HFC_CHIP_EMBSD, &hc->chip)) + HFC_outb(hc, R_PCM_MD2, 0x10); /* V_C2O_EN */ + else + HFC_outb(hc, R_PCM_MD2, 0x00); /* sync from interface */ + HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x00); + for (i = 0; i < 256; i++) { + HFC_outb_nodebug(hc, R_SLOT, i); + HFC_outb_nodebug(hc, A_SL_CFG, 0); + if (hc->ctype != HFC_TYPE_XHFC) + HFC_outb_nodebug(hc, A_CONF, 0); + hc->slot_owner[i] = -1; + } + + /* set clock speed */ + if (test_bit(HFC_CHIP_CLOCK2, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: setting double clock\n", __func__); + HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK); + } + + if (test_bit(HFC_CHIP_EMBSD, &hc->chip)) + HFC_outb(hc, 0x02 /* R_CLK_CFG */, 0x40 /* V_CLKO_OFF */); + + /* B410P GPIO */ + if (test_bit(HFC_CHIP_B410P, &hc->chip)) { + printk(KERN_NOTICE "Setting GPIOs\n"); + HFC_outb(hc, R_GPIO_SEL, 0x30); + HFC_outb(hc, R_GPIO_EN1, 0x3); + udelay(1000); + printk(KERN_NOTICE "calling vpm_init\n"); + vpm_init(hc); + } + + /* check if R_F0_CNT counts (8 kHz frame count) */ + val = HFC_inb(hc, R_F0_CNTL); + val += HFC_inb(hc, R_F0_CNTH) << 8; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "HFC_multi F0_CNT %ld after reset\n", val); + spin_unlock_irqrestore(&hc->lock, flags); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((HZ / 100) ? : 1); /* Timeout minimum 10ms */ + spin_lock_irqsave(&hc->lock, flags); + val2 = HFC_inb(hc, R_F0_CNTL); + val2 += HFC_inb(hc, R_F0_CNTH) << 8; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "HFC_multi F0_CNT %ld after 10 ms (1st try)\n", + val2); + if (val2 >= val + 8) { /* 1 ms */ + /* it counts, so we keep the pcm mode */ + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) + printk(KERN_INFO "controller is PCM bus MASTER\n"); + else + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) + printk(KERN_INFO "controller is PCM bus SLAVE\n"); + else { + test_and_set_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); + printk(KERN_INFO "controller is PCM bus SLAVE " + "(auto detected)\n"); + } + } else { + /* does not count */ + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) { + controller_fail: + printk(KERN_ERR "HFC_multi ERROR, getting no 125us " + "pulse. Seems that controller fails.\n"); + err = -EIO; + goto out; + } + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { + printk(KERN_INFO "controller is PCM bus SLAVE " + "(ignoring missing PCM clock)\n"); + } else { + /* only one pcm master */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip) + && plxsd_master) { + printk(KERN_ERR "HFC_multi ERROR, no clock " + "on another Speech Design card found. " + "Please be sure to connect PCM cable.\n"); + err = -EIO; + goto out; + } + /* retry with master clock */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = hc->plx_membase + PLX_GPIOC; + pv = readl(plx_acc_32); + pv |= PLX_MASTER_EN | PLX_SLAVE_EN_N; + pv |= PLX_SYNC_O_EN; + writel(pv, plx_acc_32); + spin_unlock_irqrestore(&plx_lock, plx_flags); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: master: " + "PLX_GPIO=%x\n", __func__, pv); + } + hc->hw.r_pcm_md0 |= V_PCM_MD; + HFC_outb(hc, R_PCM_MD0, hc->hw.r_pcm_md0 | 0x00); + spin_unlock_irqrestore(&hc->lock, flags); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((HZ / 100) ?: 1); /* Timeout min. 10ms */ + spin_lock_irqsave(&hc->lock, flags); + val2 = HFC_inb(hc, R_F0_CNTL); + val2 += HFC_inb(hc, R_F0_CNTH) << 8; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "HFC_multi F0_CNT %ld after " + "10 ms (2nd try)\n", val2); + if (val2 >= val + 8) { /* 1 ms */ + test_and_set_bit(HFC_CHIP_PCM_MASTER, + &hc->chip); + printk(KERN_INFO "controller is PCM bus MASTER " + "(auto detected)\n"); + } else + goto controller_fail; + } + } + + /* Release the DSP Reset */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip)) + plxsd_master = 1; + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc_32 = hc->plx_membase + PLX_GPIOC; + pv = readl(plx_acc_32); + pv |= PLX_DSP_RES_N; + writel(pv, plx_acc_32); + spin_unlock_irqrestore(&plx_lock, plx_flags); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: reset off: PLX_GPIO=%x\n", + __func__, pv); + } + + /* pcm id */ + if (hc->pcm) + printk(KERN_INFO "controller has given PCM BUS ID %d\n", + hc->pcm); + else { + if (test_bit(HFC_CHIP_PCM_MASTER, &hc->chip) + || test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + PCM_cnt++; /* SD has proprietary bridging */ + } + hc->pcm = PCM_cnt; + printk(KERN_INFO "controller has PCM BUS ID %d " + "(auto selected)\n", hc->pcm); + } + + /* set up timer */ + HFC_outb(hc, R_TI_WD, poll_timer); + hc->hw.r_irqmsk_misc |= V_TI_IRQMSK; + + /* set E1 state machine IRQ */ + if (hc->ctype == HFC_TYPE_E1) + hc->hw.r_irqmsk_misc |= V_STA_IRQMSK; + + /* set DTMF detection */ + if (test_bit(HFC_CHIP_DTMF, &hc->chip)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: enabling DTMF detection " + "for all B-channel\n", __func__); + hc->hw.r_dtmf = V_DTMF_EN | V_DTMF_STOP; + if (test_bit(HFC_CHIP_ULAW, &hc->chip)) + hc->hw.r_dtmf |= V_ULAW_SEL; + HFC_outb(hc, R_DTMF_N, 102 - 1); + hc->hw.r_irqmsk_misc |= V_DTMF_IRQMSK; + } + + /* conference engine */ + if (test_bit(HFC_CHIP_ULAW, &hc->chip)) + r_conf_en = V_CONF_EN | V_ULAW; + else + r_conf_en = V_CONF_EN; + if (hc->ctype != HFC_TYPE_XHFC) + HFC_outb(hc, R_CONF_EN, r_conf_en); + + /* setting leds */ + switch (hc->leds) { + case 1: /* HFC-E1 OEM */ + if (test_bit(HFC_CHIP_WATCHDOG, &hc->chip)) + HFC_outb(hc, R_GPIO_SEL, 0x32); + else + HFC_outb(hc, R_GPIO_SEL, 0x30); + + HFC_outb(hc, R_GPIO_EN1, 0x0f); + HFC_outb(hc, R_GPIO_OUT1, 0x00); + + HFC_outb(hc, R_GPIO_EN0, V_GPIO_EN2 | V_GPIO_EN3); + break; + + case 2: /* HFC-4S OEM */ + case 3: + HFC_outb(hc, R_GPIO_SEL, 0xf0); + HFC_outb(hc, R_GPIO_EN1, 0xff); + HFC_outb(hc, R_GPIO_OUT1, 0x00); + break; + } + + if (test_bit(HFC_CHIP_EMBSD, &hc->chip)) { + hc->hw.r_st_sync = 0x10; /* V_AUTO_SYNCI */ + HFC_outb(hc, R_ST_SYNC, hc->hw.r_st_sync); + } + + /* set master clock */ + if (hc->masterclk >= 0) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: setting ST master clock " + "to port %d (0..%d)\n", + __func__, hc->masterclk, hc->ports - 1); + hc->hw.r_st_sync |= (hc->masterclk | V_AUTO_SYNC); + HFC_outb(hc, R_ST_SYNC, hc->hw.r_st_sync); + } + + + + /* setting misc irq */ + HFC_outb(hc, R_IRQMSK_MISC, hc->hw.r_irqmsk_misc); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "r_irqmsk_misc.2: 0x%x\n", + hc->hw.r_irqmsk_misc); + + /* RAM access test */ + HFC_outb(hc, R_RAM_ADDR0, 0); + HFC_outb(hc, R_RAM_ADDR1, 0); + HFC_outb(hc, R_RAM_ADDR2, 0); + for (i = 0; i < 256; i++) { + HFC_outb_nodebug(hc, R_RAM_ADDR0, i); + HFC_outb_nodebug(hc, R_RAM_DATA, ((i * 3) & 0xff)); + } + for (i = 0; i < 256; i++) { + HFC_outb_nodebug(hc, R_RAM_ADDR0, i); + HFC_inb_nodebug(hc, R_RAM_DATA); + rval = HFC_inb_nodebug(hc, R_INT_DATA); + if (rval != ((i * 3) & 0xff)) { + printk(KERN_DEBUG + "addr:%x val:%x should:%x\n", i, rval, + (i * 3) & 0xff); + err++; + } + } + if (err) { + printk(KERN_DEBUG "aborting - %d RAM access errors\n", err); + err = -EIO; + goto out; + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done\n", __func__); +out: + spin_unlock_irqrestore(&hc->lock, flags); + return err; +} + + +/* + * control the watchdog + */ +static void +hfcmulti_watchdog(struct hfc_multi *hc) +{ + hc->wdcount++; + + if (hc->wdcount > 10) { + hc->wdcount = 0; + hc->wdbyte = hc->wdbyte == V_GPIO_OUT2 ? + V_GPIO_OUT3 : V_GPIO_OUT2; + + /* printk("Sending Watchdog Kill %x\n",hc->wdbyte); */ + HFC_outb(hc, R_GPIO_EN0, V_GPIO_EN2 | V_GPIO_EN3); + HFC_outb(hc, R_GPIO_OUT0, hc->wdbyte); + } +} + + + +/* + * output leds + */ +static void +hfcmulti_leds(struct hfc_multi *hc) +{ + unsigned long lled; + unsigned long leddw; + int i, state, active, leds; + struct dchannel *dch; + int led[4]; + + switch (hc->leds) { + case 1: /* HFC-E1 OEM */ + /* 2 red steady: LOS + * 1 red steady: L1 not active + * 2 green steady: L1 active + * 1st green flashing: activity on TX + * 2nd green flashing: activity on RX + */ + led[0] = 0; + led[1] = 0; + led[2] = 0; + led[3] = 0; + dch = hc->chan[hc->dnum[0]].dch; + if (dch) { + if (hc->chan[hc->dnum[0]].los) + led[1] = 1; + if (hc->e1_state != 1) { + led[0] = 1; + hc->flash[2] = 0; + hc->flash[3] = 0; + } else { + led[2] = 1; + led[3] = 1; + if (!hc->flash[2] && hc->activity_tx) + hc->flash[2] = poll; + if (!hc->flash[3] && hc->activity_rx) + hc->flash[3] = poll; + if (hc->flash[2] && hc->flash[2] < 1024) + led[2] = 0; + if (hc->flash[3] && hc->flash[3] < 1024) + led[3] = 0; + if (hc->flash[2] >= 2048) + hc->flash[2] = 0; + if (hc->flash[3] >= 2048) + hc->flash[3] = 0; + if (hc->flash[2]) + hc->flash[2] += poll; + if (hc->flash[3]) + hc->flash[3] += poll; + } + } + leds = (led[0] | (led[1]<<2) | (led[2]<<1) | (led[3]<<3))^0xF; + /* leds are inverted */ + if (leds != (int)hc->ledstate) { + HFC_outb_nodebug(hc, R_GPIO_OUT1, leds); + hc->ledstate = leds; + } + break; + + case 2: /* HFC-4S OEM */ + /* red steady: PH_DEACTIVATE + * green steady: PH_ACTIVATE + * green flashing: activity on TX + */ + for (i = 0; i < 4; i++) { + state = 0; + active = -1; + dch = hc->chan[(i << 2) | 2].dch; + if (dch) { + state = dch->state; + if (dch->dev.D.protocol == ISDN_P_NT_S0) + active = 3; + else + active = 7; + } + if (state) { + if (state == active) { + led[i] = 1; /* led green */ + hc->activity_tx |= hc->activity_rx; + if (!hc->flash[i] && + (hc->activity_tx & (1 << i))) + hc->flash[i] = poll; + if (hc->flash[i] && hc->flash[i] < 1024) + led[i] = 0; /* led off */ + if (hc->flash[i] >= 2048) + hc->flash[i] = 0; + if (hc->flash[i]) + hc->flash[i] += poll; + } else { + led[i] = 2; /* led red */ + hc->flash[i] = 0; + } + } else + led[i] = 0; /* led off */ + } + if (test_bit(HFC_CHIP_B410P, &hc->chip)) { + leds = 0; + for (i = 0; i < 4; i++) { + if (led[i] == 1) { + /*green*/ + leds |= (0x2 << (i * 2)); + } else if (led[i] == 2) { + /*red*/ + leds |= (0x1 << (i * 2)); + } + } + if (leds != (int)hc->ledstate) { + vpm_out(hc, 0, 0x1a8 + 3, leds); + hc->ledstate = leds; + } + } else { + leds = ((led[3] > 0) << 0) | ((led[1] > 0) << 1) | + ((led[0] > 0) << 2) | ((led[2] > 0) << 3) | + ((led[3] & 1) << 4) | ((led[1] & 1) << 5) | + ((led[0] & 1) << 6) | ((led[2] & 1) << 7); + if (leds != (int)hc->ledstate) { + HFC_outb_nodebug(hc, R_GPIO_EN1, leds & 0x0F); + HFC_outb_nodebug(hc, R_GPIO_OUT1, leds >> 4); + hc->ledstate = leds; + } + } + break; + + case 3: /* HFC 1S/2S Beronet */ + /* red steady: PH_DEACTIVATE + * green steady: PH_ACTIVATE + * green flashing: activity on TX + */ + for (i = 0; i < 2; i++) { + state = 0; + active = -1; + dch = hc->chan[(i << 2) | 2].dch; + if (dch) { + state = dch->state; + if (dch->dev.D.protocol == ISDN_P_NT_S0) + active = 3; + else + active = 7; + } + if (state) { + if (state == active) { + led[i] = 1; /* led green */ + hc->activity_tx |= hc->activity_rx; + if (!hc->flash[i] && + (hc->activity_tx & (1 << i))) + hc->flash[i] = poll; + if (hc->flash[i] < 1024) + led[i] = 0; /* led off */ + if (hc->flash[i] >= 2048) + hc->flash[i] = 0; + if (hc->flash[i]) + hc->flash[i] += poll; + } else { + led[i] = 2; /* led red */ + hc->flash[i] = 0; + } + } else + led[i] = 0; /* led off */ + } + leds = (led[0] > 0) | ((led[1] > 0) << 1) | ((led[0]&1) << 2) + | ((led[1]&1) << 3); + if (leds != (int)hc->ledstate) { + HFC_outb_nodebug(hc, R_GPIO_EN1, + ((led[0] > 0) << 2) | ((led[1] > 0) << 3)); + HFC_outb_nodebug(hc, R_GPIO_OUT1, + ((led[0] & 1) << 2) | ((led[1] & 1) << 3)); + hc->ledstate = leds; + } + break; + case 8: /* HFC 8S+ Beronet */ + /* off: PH_DEACTIVATE + * steady: PH_ACTIVATE + * flashing: activity on TX + */ + lled = 0xff; /* leds off */ + for (i = 0; i < 8; i++) { + state = 0; + active = -1; + dch = hc->chan[(i << 2) | 2].dch; + if (dch) { + state = dch->state; + if (dch->dev.D.protocol == ISDN_P_NT_S0) + active = 3; + else + active = 7; + } + if (state) { + if (state == active) { + lled &= ~(1 << i); /* led on */ + hc->activity_tx |= hc->activity_rx; + if (!hc->flash[i] && + (hc->activity_tx & (1 << i))) + hc->flash[i] = poll; + if (hc->flash[i] < 1024) + lled |= 1 << i; /* led off */ + if (hc->flash[i] >= 2048) + hc->flash[i] = 0; + if (hc->flash[i]) + hc->flash[i] += poll; + } else + hc->flash[i] = 0; + } + } + leddw = lled << 24 | lled << 16 | lled << 8 | lled; + if (leddw != hc->ledstate) { + /* HFC_outb(hc, R_BRG_PCM_CFG, 1); + HFC_outb(c, R_BRG_PCM_CFG, (0x0 << 6) | 0x3); */ + /* was _io before */ + HFC_outb_nodebug(hc, R_BRG_PCM_CFG, 1 | V_PCM_CLK); + outw(0x4000, hc->pci_iobase + 4); + outl(leddw, hc->pci_iobase); + HFC_outb_nodebug(hc, R_BRG_PCM_CFG, V_PCM_CLK); + hc->ledstate = leddw; + } + break; + } + hc->activity_tx = 0; + hc->activity_rx = 0; +} +/* + * read dtmf coefficients + */ + +static void +hfcmulti_dtmf(struct hfc_multi *hc) +{ + s32 *coeff; + u_int mantissa; + int co, ch; + struct bchannel *bch = NULL; + u8 exponent; + int dtmf = 0; + int addr; + u16 w_float; + struct sk_buff *skb; + struct mISDNhead *hh; + + if (debug & DEBUG_HFCMULTI_DTMF) + printk(KERN_DEBUG "%s: dtmf detection irq\n", __func__); + for (ch = 0; ch <= 31; ch++) { + /* only process enabled B-channels */ + bch = hc->chan[ch].bch; + if (!bch) + continue; + if (!hc->created[hc->chan[ch].port]) + continue; + if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) + continue; + if (debug & DEBUG_HFCMULTI_DTMF) + printk(KERN_DEBUG "%s: dtmf channel %d:", + __func__, ch); + coeff = &(hc->chan[ch].coeff[hc->chan[ch].coeff_count * 16]); + dtmf = 1; + for (co = 0; co < 8; co++) { + /* read W(n-1) coefficient */ + addr = hc->DTMFbase + ((co << 7) | (ch << 2)); + HFC_outb_nodebug(hc, R_RAM_ADDR0, addr); + HFC_outb_nodebug(hc, R_RAM_ADDR1, addr >> 8); + HFC_outb_nodebug(hc, R_RAM_ADDR2, (addr >> 16) + | V_ADDR_INC); + w_float = HFC_inb_nodebug(hc, R_RAM_DATA); + w_float |= (HFC_inb_nodebug(hc, R_RAM_DATA) << 8); + if (debug & DEBUG_HFCMULTI_DTMF) + printk(" %04x", w_float); + + /* decode float (see chip doc) */ + mantissa = w_float & 0x0fff; + if (w_float & 0x8000) + mantissa |= 0xfffff000; + exponent = (w_float >> 12) & 0x7; + if (exponent) { + mantissa ^= 0x1000; + mantissa <<= (exponent - 1); + } + + /* store coefficient */ + coeff[co << 1] = mantissa; + + /* read W(n) coefficient */ + w_float = HFC_inb_nodebug(hc, R_RAM_DATA); + w_float |= (HFC_inb_nodebug(hc, R_RAM_DATA) << 8); + if (debug & DEBUG_HFCMULTI_DTMF) + printk(" %04x", w_float); + + /* decode float (see chip doc) */ + mantissa = w_float & 0x0fff; + if (w_float & 0x8000) + mantissa |= 0xfffff000; + exponent = (w_float >> 12) & 0x7; + if (exponent) { + mantissa ^= 0x1000; + mantissa <<= (exponent - 1); + } + + /* store coefficient */ + coeff[(co << 1) | 1] = mantissa; + } + if (debug & DEBUG_HFCMULTI_DTMF) + printk(" DTMF ready %08x %08x %08x %08x " + "%08x %08x %08x %08x\n", + coeff[0], coeff[1], coeff[2], coeff[3], + coeff[4], coeff[5], coeff[6], coeff[7]); + hc->chan[ch].coeff_count++; + if (hc->chan[ch].coeff_count == 8) { + hc->chan[ch].coeff_count = 0; + skb = mI_alloc_skb(512, GFP_ATOMIC); + if (!skb) { + printk(KERN_DEBUG "%s: No memory for skb\n", + __func__); + continue; + } + hh = mISDN_HEAD_P(skb); + hh->prim = PH_CONTROL_IND; + hh->id = DTMF_HFC_COEF; + skb_put_data(skb, hc->chan[ch].coeff, 512); + recv_Bchannel_skb(bch, skb); + } + } + + /* restart DTMF processing */ + hc->dtmf = dtmf; + if (dtmf) + HFC_outb_nodebug(hc, R_DTMF, hc->hw.r_dtmf | V_RST_DTMF); +} + + +/* + * fill fifo as much as possible + */ + +static void +hfcmulti_tx(struct hfc_multi *hc, int ch) +{ + int i, ii, temp, len = 0; + int Zspace, z1, z2; /* must be int for calculation */ + int Fspace, f1, f2; + u_char *d; + int *txpending, slot_tx; + struct bchannel *bch; + struct dchannel *dch; + struct sk_buff **sp = NULL; + int *idxp; + + bch = hc->chan[ch].bch; + dch = hc->chan[ch].dch; + if ((!dch) && (!bch)) + return; + + txpending = &hc->chan[ch].txpending; + slot_tx = hc->chan[ch].slot_tx; + if (dch) { + if (!test_bit(FLG_ACTIVE, &dch->Flags)) + return; + sp = &dch->tx_skb; + idxp = &dch->tx_idx; + } else { + if (!test_bit(FLG_ACTIVE, &bch->Flags)) + return; + sp = &bch->tx_skb; + idxp = &bch->tx_idx; + } + if (*sp) + len = (*sp)->len; + + if ((!len) && *txpending != 1) + return; /* no data */ + + if (test_bit(HFC_CHIP_B410P, &hc->chip) && + (hc->chan[ch].protocol == ISDN_P_B_RAW) && + (hc->chan[ch].slot_rx < 0) && + (hc->chan[ch].slot_tx < 0)) + HFC_outb_nodebug(hc, R_FIFO, 0x20 | (ch << 1)); + else + HFC_outb_nodebug(hc, R_FIFO, ch << 1); + HFC_wait_nodebug(hc); + + if (*txpending == 2) { + /* reset fifo */ + HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait_nodebug(hc); + HFC_outb(hc, A_SUBCH_CFG, 0); + *txpending = 1; + } +next_frame: + if (dch || test_bit(FLG_HDLC, &bch->Flags)) { + f1 = HFC_inb_nodebug(hc, A_F1); + f2 = HFC_inb_nodebug(hc, A_F2); + while (f2 != (temp = HFC_inb_nodebug(hc, A_F2))) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG + "%s(card %d): reread f2 because %d!=%d\n", + __func__, hc->id + 1, temp, f2); + f2 = temp; /* repeat until F2 is equal */ + } + Fspace = f2 - f1 - 1; + if (Fspace < 0) + Fspace += hc->Flen; + /* + * Old FIFO handling doesn't give us the current Z2 read + * pointer, so we cannot send the next frame before the fifo + * is empty. It makes no difference except for a slightly + * lower performance. + */ + if (test_bit(HFC_CHIP_REVISION0, &hc->chip)) { + if (f1 != f2) + Fspace = 0; + else + Fspace = 1; + } + /* one frame only for ST D-channels, to allow resending */ + if (hc->ctype != HFC_TYPE_E1 && dch) { + if (f1 != f2) + Fspace = 0; + } + /* F-counter full condition */ + if (Fspace == 0) + return; + } + z1 = HFC_inw_nodebug(hc, A_Z1) - hc->Zmin; + z2 = HFC_inw_nodebug(hc, A_Z2) - hc->Zmin; + while (z2 != (temp = (HFC_inw_nodebug(hc, A_Z2) - hc->Zmin))) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s(card %d): reread z2 because " + "%d!=%d\n", __func__, hc->id + 1, temp, z2); + z2 = temp; /* repeat unti Z2 is equal */ + } + hc->chan[ch].Zfill = z1 - z2; + if (hc->chan[ch].Zfill < 0) + hc->chan[ch].Zfill += hc->Zlen; + Zspace = z2 - z1; + if (Zspace <= 0) + Zspace += hc->Zlen; + Zspace -= 4; /* keep not too full, so pointers will not overrun */ + /* fill transparent data only to maxinum transparent load (minus 4) */ + if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags)) + Zspace = Zspace - hc->Zlen + hc->max_trans; + if (Zspace <= 0) /* no space of 4 bytes */ + return; + + /* if no data */ + if (!len) { + if (z1 == z2) { /* empty */ + /* if done with FIFO audio data during PCM connection */ + if (bch && (!test_bit(FLG_HDLC, &bch->Flags)) && + *txpending && slot_tx >= 0) { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG + "%s: reconnecting PCM due to no " + "more FIFO data: channel %d " + "slot_tx %d\n", + __func__, ch, slot_tx); + /* connect slot */ + if (hc->ctype == HFC_TYPE_XHFC) + HFC_outb(hc, A_CON_HDLC, 0xc0 + | 0x07 << 2 | V_HDLC_TRP | V_IFF); + /* Enable FIFO, no interrupt */ + else + HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | + V_HDLC_TRP | V_IFF); + HFC_outb_nodebug(hc, R_FIFO, ch << 1 | 1); + HFC_wait_nodebug(hc); + if (hc->ctype == HFC_TYPE_XHFC) + HFC_outb(hc, A_CON_HDLC, 0xc0 + | 0x07 << 2 | V_HDLC_TRP | V_IFF); + /* Enable FIFO, no interrupt */ + else + HFC_outb(hc, A_CON_HDLC, 0xc0 | 0x00 | + V_HDLC_TRP | V_IFF); + HFC_outb_nodebug(hc, R_FIFO, ch << 1); + HFC_wait_nodebug(hc); + } + *txpending = 0; + } + return; /* no data */ + } + + /* "fill fifo if empty" feature */ + if (bch && test_bit(FLG_FILLEMPTY, &bch->Flags) + && !test_bit(FLG_HDLC, &bch->Flags) && z2 == z1) { + if (debug & DEBUG_HFCMULTI_FILL) + printk(KERN_DEBUG "%s: buffer empty, so we have " + "underrun\n", __func__); + /* fill buffer, to prevent future underrun */ + hc->write_fifo(hc, hc->silence_data, poll >> 1); + Zspace -= (poll >> 1); + } + + /* if audio data and connected slot */ + if (bch && (!test_bit(FLG_HDLC, &bch->Flags)) && (!*txpending) + && slot_tx >= 0) { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: disconnecting PCM due to " + "FIFO data: channel %d slot_tx %d\n", + __func__, ch, slot_tx); + /* disconnect slot */ + if (hc->ctype == HFC_TYPE_XHFC) + HFC_outb(hc, A_CON_HDLC, 0x80 + | 0x07 << 2 | V_HDLC_TRP | V_IFF); + /* Enable FIFO, no interrupt */ + else + HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | + V_HDLC_TRP | V_IFF); + HFC_outb_nodebug(hc, R_FIFO, ch << 1 | 1); + HFC_wait_nodebug(hc); + if (hc->ctype == HFC_TYPE_XHFC) + HFC_outb(hc, A_CON_HDLC, 0x80 + | 0x07 << 2 | V_HDLC_TRP | V_IFF); + /* Enable FIFO, no interrupt */ + else + HFC_outb(hc, A_CON_HDLC, 0x80 | 0x00 | + V_HDLC_TRP | V_IFF); + HFC_outb_nodebug(hc, R_FIFO, ch << 1); + HFC_wait_nodebug(hc); + } + *txpending = 1; + + /* show activity */ + if (dch) + hc->activity_tx |= 1 << hc->chan[ch].port; + + /* fill fifo to what we have left */ + ii = len; + if (dch || test_bit(FLG_HDLC, &bch->Flags)) + temp = 1; + else + temp = 0; + i = *idxp; + d = (*sp)->data + i; + if (ii - i > Zspace) + ii = Zspace + i; + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s(card %d): fifo(%d) has %d bytes space " + "left (z1=%04x, z2=%04x) sending %d of %d bytes %s\n", + __func__, hc->id + 1, ch, Zspace, z1, z2, ii-i, len-i, + temp ? "HDLC" : "TRANS"); + + /* Have to prep the audio data */ + hc->write_fifo(hc, d, ii - i); + hc->chan[ch].Zfill += ii - i; + *idxp = ii; + + /* if not all data has been written */ + if (ii != len) { + /* NOTE: fifo is started by the calling function */ + return; + } + + /* if all data has been written, terminate frame */ + if (dch || test_bit(FLG_HDLC, &bch->Flags)) { + /* increment f-counter */ + HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_INC_F); + HFC_wait_nodebug(hc); + } + + dev_kfree_skb(*sp); + /* check for next frame */ + if (bch && get_next_bframe(bch)) { + len = (*sp)->len; + goto next_frame; + } + if (dch && get_next_dframe(dch)) { + len = (*sp)->len; + goto next_frame; + } + + /* + * now we have no more data, so in case of transparent, + * we set the last byte in fifo to 'silence' in case we will get + * no more data at all. this prevents sending an undefined value. + */ + if (bch && test_bit(FLG_TRANSPARENT, &bch->Flags)) + HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, hc->silence); +} + + +/* NOTE: only called if E1 card is in active state */ +static void +hfcmulti_rx(struct hfc_multi *hc, int ch) +{ + int temp; + int Zsize, z1, z2 = 0; /* = 0, to make GCC happy */ + int f1 = 0, f2 = 0; /* = 0, to make GCC happy */ + int again = 0; + struct bchannel *bch; + struct dchannel *dch = NULL; + struct sk_buff *skb, **sp = NULL; + int maxlen; + + bch = hc->chan[ch].bch; + if (bch) { + if (!test_bit(FLG_ACTIVE, &bch->Flags)) + return; + } else if (hc->chan[ch].dch) { + dch = hc->chan[ch].dch; + if (!test_bit(FLG_ACTIVE, &dch->Flags)) + return; + } else { + return; + } +next_frame: + /* on first AND before getting next valid frame, R_FIFO must be written + to. */ + if (test_bit(HFC_CHIP_B410P, &hc->chip) && + (hc->chan[ch].protocol == ISDN_P_B_RAW) && + (hc->chan[ch].slot_rx < 0) && + (hc->chan[ch].slot_tx < 0)) + HFC_outb_nodebug(hc, R_FIFO, 0x20 | (ch << 1) | 1); + else + HFC_outb_nodebug(hc, R_FIFO, (ch << 1) | 1); + HFC_wait_nodebug(hc); + + /* ignore if rx is off BUT change fifo (above) to start pending TX */ + if (hc->chan[ch].rx_off) { + if (bch) + bch->dropcnt += poll; /* not exact but fair enough */ + return; + } + + if (dch || test_bit(FLG_HDLC, &bch->Flags)) { + f1 = HFC_inb_nodebug(hc, A_F1); + while (f1 != (temp = HFC_inb_nodebug(hc, A_F1))) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG + "%s(card %d): reread f1 because %d!=%d\n", + __func__, hc->id + 1, temp, f1); + f1 = temp; /* repeat until F1 is equal */ + } + f2 = HFC_inb_nodebug(hc, A_F2); + } + z1 = HFC_inw_nodebug(hc, A_Z1) - hc->Zmin; + while (z1 != (temp = (HFC_inw_nodebug(hc, A_Z1) - hc->Zmin))) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s(card %d): reread z2 because " + "%d!=%d\n", __func__, hc->id + 1, temp, z2); + z1 = temp; /* repeat until Z1 is equal */ + } + z2 = HFC_inw_nodebug(hc, A_Z2) - hc->Zmin; + Zsize = z1 - z2; + if ((dch || test_bit(FLG_HDLC, &bch->Flags)) && f1 != f2) + /* complete hdlc frame */ + Zsize++; + if (Zsize < 0) + Zsize += hc->Zlen; + /* if buffer is empty */ + if (Zsize <= 0) + return; + + if (bch) { + maxlen = bchannel_get_rxbuf(bch, Zsize); + if (maxlen < 0) { + pr_warn("card%d.B%d: No bufferspace for %d bytes\n", + hc->id + 1, bch->nr, Zsize); + return; + } + sp = &bch->rx_skb; + maxlen = bch->maxlen; + } else { /* Dchannel */ + sp = &dch->rx_skb; + maxlen = dch->maxlen + 3; + if (*sp == NULL) { + *sp = mI_alloc_skb(maxlen, GFP_ATOMIC); + if (*sp == NULL) { + pr_warn("card%d: No mem for dch rx_skb\n", + hc->id + 1); + return; + } + } + } + /* show activity */ + if (dch) + hc->activity_rx |= 1 << hc->chan[ch].port; + + /* empty fifo with what we have */ + if (dch || test_bit(FLG_HDLC, &bch->Flags)) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG "%s(card %d): fifo(%d) reading %d " + "bytes (z1=%04x, z2=%04x) HDLC %s (f1=%d, f2=%d) " + "got=%d (again %d)\n", __func__, hc->id + 1, ch, + Zsize, z1, z2, (f1 == f2) ? "fragment" : "COMPLETE", + f1, f2, Zsize + (*sp)->len, again); + /* HDLC */ + if ((Zsize + (*sp)->len) > maxlen) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG + "%s(card %d): hdlc-frame too large.\n", + __func__, hc->id + 1); + skb_trim(*sp, 0); + HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait_nodebug(hc); + return; + } + + hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize); + + if (f1 != f2) { + /* increment Z2,F2-counter */ + HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_INC_F); + HFC_wait_nodebug(hc); + /* check size */ + if ((*sp)->len < 4) { + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG + "%s(card %d): Frame below minimum " + "size\n", __func__, hc->id + 1); + skb_trim(*sp, 0); + goto next_frame; + } + /* there is at least one complete frame, check crc */ + if ((*sp)->data[(*sp)->len - 1]) { + if (debug & DEBUG_HFCMULTI_CRC) + printk(KERN_DEBUG + "%s: CRC-error\n", __func__); + skb_trim(*sp, 0); + goto next_frame; + } + skb_trim(*sp, (*sp)->len - 3); + if ((*sp)->len < MISDN_COPY_SIZE) { + skb = *sp; + *sp = mI_alloc_skb(skb->len, GFP_ATOMIC); + if (*sp) { + skb_put_data(*sp, skb->data, skb->len); + skb_trim(skb, 0); + } else { + printk(KERN_DEBUG "%s: No mem\n", + __func__); + *sp = skb; + skb = NULL; + } + } else { + skb = NULL; + } + if (debug & DEBUG_HFCMULTI_FIFO) { + printk(KERN_DEBUG "%s(card %d):", + __func__, hc->id + 1); + temp = 0; + while (temp < (*sp)->len) + printk(" %02x", (*sp)->data[temp++]); + printk("\n"); + } + if (dch) + recv_Dchannel(dch); + else + recv_Bchannel(bch, MISDN_ID_ANY, false); + *sp = skb; + again++; + goto next_frame; + } + /* there is an incomplete frame */ + } else { + /* transparent */ + hc->read_fifo(hc, skb_put(*sp, Zsize), Zsize); + if (debug & DEBUG_HFCMULTI_FIFO) + printk(KERN_DEBUG + "%s(card %d): fifo(%d) reading %d bytes " + "(z1=%04x, z2=%04x) TRANS\n", + __func__, hc->id + 1, ch, Zsize, z1, z2); + /* only bch is transparent */ + recv_Bchannel(bch, hc->chan[ch].Zfill, false); + } +} + + +/* + * Interrupt handler + */ +static void +signal_state_up(struct dchannel *dch, int info, char *msg) +{ + struct sk_buff *skb; + int id, data = info; + + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: %s\n", __func__, msg); + + id = TEI_SAPI | (GROUP_TEI << 8); /* manager address */ + + skb = _alloc_mISDN_skb(MPH_INFORMATION_IND, id, sizeof(data), &data, + GFP_ATOMIC); + if (!skb) + return; + recv_Dchannel_skb(dch, skb); +} + +static inline void +handle_timer_irq(struct hfc_multi *hc) +{ + int ch, temp; + struct dchannel *dch; + u_long flags; + + /* process queued resync jobs */ + if (hc->e1_resync) { + /* lock, so e1_resync gets not changed */ + spin_lock_irqsave(&HFClock, flags); + if (hc->e1_resync & 1) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "Enable SYNC_I\n"); + HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC); + /* disable JATT, if RX_SYNC is set */ + if (test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) + HFC_outb(hc, R_SYNC_OUT, V_SYNC_E1_RX); + } + if (hc->e1_resync & 2) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG "Enable jatt PLL\n"); + HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS); + } + if (hc->e1_resync & 4) { + if (debug & DEBUG_HFCMULTI_PLXSD) + printk(KERN_DEBUG + "Enable QUARTZ for HFC-E1\n"); + /* set jatt to quartz */ + HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC + | V_JATT_OFF); + /* switch to JATT, in case it is not already */ + HFC_outb(hc, R_SYNC_OUT, 0); + } + hc->e1_resync = 0; + spin_unlock_irqrestore(&HFClock, flags); + } + + if (hc->ctype != HFC_TYPE_E1 || hc->e1_state == 1) + for (ch = 0; ch <= 31; ch++) { + if (hc->created[hc->chan[ch].port]) { + hfcmulti_tx(hc, ch); + /* fifo is started when switching to rx-fifo */ + hfcmulti_rx(hc, ch); + if (hc->chan[ch].dch && + hc->chan[ch].nt_timer > -1) { + dch = hc->chan[ch].dch; + if (!(--hc->chan[ch].nt_timer)) { + schedule_event(dch, + FLG_PHCHANGE); + if (debug & + DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: nt_timer at " + "state %x\n", + __func__, + dch->state); + } + } + } + } + if (hc->ctype == HFC_TYPE_E1 && hc->created[0]) { + dch = hc->chan[hc->dnum[0]].dch; + /* LOS */ + temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_SIG_LOS; + hc->chan[hc->dnum[0]].los = temp; + if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dnum[0]].cfg)) { + if (!temp && hc->chan[hc->dnum[0]].los) + signal_state_up(dch, L1_SIGNAL_LOS_ON, + "LOS detected"); + if (temp && !hc->chan[hc->dnum[0]].los) + signal_state_up(dch, L1_SIGNAL_LOS_OFF, + "LOS gone"); + } + if (test_bit(HFC_CFG_REPORT_AIS, &hc->chan[hc->dnum[0]].cfg)) { + /* AIS */ + temp = HFC_inb_nodebug(hc, R_SYNC_STA) & V_AIS; + if (!temp && hc->chan[hc->dnum[0]].ais) + signal_state_up(dch, L1_SIGNAL_AIS_ON, + "AIS detected"); + if (temp && !hc->chan[hc->dnum[0]].ais) + signal_state_up(dch, L1_SIGNAL_AIS_OFF, + "AIS gone"); + hc->chan[hc->dnum[0]].ais = temp; + } + if (test_bit(HFC_CFG_REPORT_SLIP, &hc->chan[hc->dnum[0]].cfg)) { + /* SLIP */ + temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_RX; + if (!temp && hc->chan[hc->dnum[0]].slip_rx) + signal_state_up(dch, L1_SIGNAL_SLIP_RX, + " bit SLIP detected RX"); + hc->chan[hc->dnum[0]].slip_rx = temp; + temp = HFC_inb_nodebug(hc, R_SLIP) & V_FOSLIP_TX; + if (!temp && hc->chan[hc->dnum[0]].slip_tx) + signal_state_up(dch, L1_SIGNAL_SLIP_TX, + " bit SLIP detected TX"); + hc->chan[hc->dnum[0]].slip_tx = temp; + } + if (test_bit(HFC_CFG_REPORT_RDI, &hc->chan[hc->dnum[0]].cfg)) { + /* RDI */ + temp = HFC_inb_nodebug(hc, R_RX_SL0_0) & V_A; + if (!temp && hc->chan[hc->dnum[0]].rdi) + signal_state_up(dch, L1_SIGNAL_RDI_ON, + "RDI detected"); + if (temp && !hc->chan[hc->dnum[0]].rdi) + signal_state_up(dch, L1_SIGNAL_RDI_OFF, + "RDI gone"); + hc->chan[hc->dnum[0]].rdi = temp; + } + temp = HFC_inb_nodebug(hc, R_JATT_DIR); + switch (hc->chan[hc->dnum[0]].sync) { + case 0: + if ((temp & 0x60) == 0x60) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG + "%s: (id=%d) E1 now " + "in clock sync\n", + __func__, hc->id); + HFC_outb(hc, R_RX_OFF, + hc->chan[hc->dnum[0]].jitter | V_RX_INIT); + HFC_outb(hc, R_TX_OFF, + hc->chan[hc->dnum[0]].jitter | V_RX_INIT); + hc->chan[hc->dnum[0]].sync = 1; + goto check_framesync; + } + break; + case 1: + if ((temp & 0x60) != 0x60) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG + "%s: (id=%d) E1 " + "lost clock sync\n", + __func__, hc->id); + hc->chan[hc->dnum[0]].sync = 0; + break; + } + check_framesync: + temp = HFC_inb_nodebug(hc, R_SYNC_STA); + if (temp == 0x27) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG + "%s: (id=%d) E1 " + "now in frame sync\n", + __func__, hc->id); + hc->chan[hc->dnum[0]].sync = 2; + } + break; + case 2: + if ((temp & 0x60) != 0x60) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG + "%s: (id=%d) E1 lost " + "clock & frame sync\n", + __func__, hc->id); + hc->chan[hc->dnum[0]].sync = 0; + break; + } + temp = HFC_inb_nodebug(hc, R_SYNC_STA); + if (temp != 0x27) { + if (debug & DEBUG_HFCMULTI_SYNC) + printk(KERN_DEBUG + "%s: (id=%d) E1 " + "lost frame sync\n", + __func__, hc->id); + hc->chan[hc->dnum[0]].sync = 1; + } + break; + } + } + + if (test_bit(HFC_CHIP_WATCHDOG, &hc->chip)) + hfcmulti_watchdog(hc); + + if (hc->leds) + hfcmulti_leds(hc); +} + +static void +ph_state_irq(struct hfc_multi *hc, u_char r_irq_statech) +{ + struct dchannel *dch; + int ch; + int active; + u_char st_status, temp; + + /* state machine */ + for (ch = 0; ch <= 31; ch++) { + if (hc->chan[ch].dch) { + dch = hc->chan[ch].dch; + if (r_irq_statech & 1) { + HFC_outb_nodebug(hc, R_ST_SEL, + hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + /* undocumented: status changes during read */ + st_status = HFC_inb_nodebug(hc, A_ST_RD_STATE); + while (st_status != (temp = + HFC_inb_nodebug(hc, A_ST_RD_STATE))) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: reread " + "STATE because %d!=%d\n", + __func__, temp, + st_status); + st_status = temp; /* repeat */ + } + + /* Speech Design TE-sync indication */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip) && + dch->dev.D.protocol == ISDN_P_TE_S0) { + if (st_status & V_FR_SYNC_ST) + hc->syncronized |= + (1 << hc->chan[ch].port); + else + hc->syncronized &= + ~(1 << hc->chan[ch].port); + } + dch->state = st_status & 0x0f; + if (dch->dev.D.protocol == ISDN_P_NT_S0) + active = 3; + else + active = 7; + if (dch->state == active) { + HFC_outb_nodebug(hc, R_FIFO, + (ch << 1) | 1); + HFC_wait_nodebug(hc); + HFC_outb_nodebug(hc, + R_INC_RES_FIFO, V_RES_F); + HFC_wait_nodebug(hc); + dch->tx_idx = 0; + } + schedule_event(dch, FLG_PHCHANGE); + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: S/T newstate %x port %d\n", + __func__, dch->state, + hc->chan[ch].port); + } + r_irq_statech >>= 1; + } + } + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) + plxsd_checksync(hc, 0); +} + +static void +fifo_irq(struct hfc_multi *hc, int block) +{ + int ch, j; + struct dchannel *dch; + struct bchannel *bch; + u_char r_irq_fifo_bl; + + r_irq_fifo_bl = HFC_inb_nodebug(hc, R_IRQ_FIFO_BL0 + block); + j = 0; + while (j < 8) { + ch = (block << 2) + (j >> 1); + dch = hc->chan[ch].dch; + bch = hc->chan[ch].bch; + if (((!dch) && (!bch)) || (!hc->created[hc->chan[ch].port])) { + j += 2; + continue; + } + if (dch && (r_irq_fifo_bl & (1 << j)) && + test_bit(FLG_ACTIVE, &dch->Flags)) { + hfcmulti_tx(hc, ch); + /* start fifo */ + HFC_outb_nodebug(hc, R_FIFO, 0); + HFC_wait_nodebug(hc); + } + if (bch && (r_irq_fifo_bl & (1 << j)) && + test_bit(FLG_ACTIVE, &bch->Flags)) { + hfcmulti_tx(hc, ch); + /* start fifo */ + HFC_outb_nodebug(hc, R_FIFO, 0); + HFC_wait_nodebug(hc); + } + j++; + if (dch && (r_irq_fifo_bl & (1 << j)) && + test_bit(FLG_ACTIVE, &dch->Flags)) { + hfcmulti_rx(hc, ch); + } + if (bch && (r_irq_fifo_bl & (1 << j)) && + test_bit(FLG_ACTIVE, &bch->Flags)) { + hfcmulti_rx(hc, ch); + } + j++; + } +} + +#ifdef IRQ_DEBUG +int irqsem; +#endif +static irqreturn_t +hfcmulti_interrupt(int intno, void *dev_id) +{ +#ifdef IRQCOUNT_DEBUG + static int iq1 = 0, iq2 = 0, iq3 = 0, iq4 = 0, + iq5 = 0, iq6 = 0, iqcnt = 0; +#endif + struct hfc_multi *hc = dev_id; + struct dchannel *dch; + u_char r_irq_statech, status, r_irq_misc, r_irq_oview; + int i; + void __iomem *plx_acc; + u_short wval; + u_char e1_syncsta, temp, temp2; + u_long flags; + + if (!hc) { + printk(KERN_ERR "HFC-multi: Spurious interrupt!\n"); + return IRQ_NONE; + } + + spin_lock(&hc->lock); + +#ifdef IRQ_DEBUG + if (irqsem) + printk(KERN_ERR "irq for card %d during irq from " + "card %d, this is no bug.\n", hc->id + 1, irqsem); + irqsem = hc->id + 1; +#endif +#ifdef CONFIG_MISDN_HFCMULTI_8xx + if (hc->immap->im_cpm.cp_pbdat & hc->pb_irqmsk) + goto irq_notforus; +#endif + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + spin_lock_irqsave(&plx_lock, flags); + plx_acc = hc->plx_membase + PLX_INTCSR; + wval = readw(plx_acc); + spin_unlock_irqrestore(&plx_lock, flags); + if (!(wval & PLX_INTCSR_LINTI1_STATUS)) + goto irq_notforus; + } + + status = HFC_inb_nodebug(hc, R_STATUS); + r_irq_statech = HFC_inb_nodebug(hc, R_IRQ_STATECH); +#ifdef IRQCOUNT_DEBUG + if (r_irq_statech) + iq1++; + if (status & V_DTMF_STA) + iq2++; + if (status & V_LOST_STA) + iq3++; + if (status & V_EXT_IRQSTA) + iq4++; + if (status & V_MISC_IRQSTA) + iq5++; + if (status & V_FR_IRQSTA) + iq6++; + if (iqcnt++ > 5000) { + printk(KERN_ERR "iq1:%x iq2:%x iq3:%x iq4:%x iq5:%x iq6:%x\n", + iq1, iq2, iq3, iq4, iq5, iq6); + iqcnt = 0; + } +#endif + + if (!r_irq_statech && + !(status & (V_DTMF_STA | V_LOST_STA | V_EXT_IRQSTA | + V_MISC_IRQSTA | V_FR_IRQSTA))) { + /* irq is not for us */ + goto irq_notforus; + } + hc->irqcnt++; + if (r_irq_statech) { + if (hc->ctype != HFC_TYPE_E1) + ph_state_irq(hc, r_irq_statech); + } + if (status & V_LOST_STA) { + /* LOST IRQ */ + HFC_outb(hc, R_INC_RES_FIFO, V_RES_LOST); /* clear irq! */ + } + if (status & V_MISC_IRQSTA) { + /* misc IRQ */ + r_irq_misc = HFC_inb_nodebug(hc, R_IRQ_MISC); + r_irq_misc &= hc->hw.r_irqmsk_misc; /* ignore disabled irqs */ + if (r_irq_misc & V_STA_IRQ) { + if (hc->ctype == HFC_TYPE_E1) { + /* state machine */ + dch = hc->chan[hc->dnum[0]].dch; + e1_syncsta = HFC_inb_nodebug(hc, R_SYNC_STA); + if (test_bit(HFC_CHIP_PLXSD, &hc->chip) + && hc->e1_getclock) { + if (e1_syncsta & V_FR_SYNC_E1) + hc->syncronized = 1; + else + hc->syncronized = 0; + } + /* undocumented: status changes during read */ + temp = HFC_inb_nodebug(hc, R_E1_RD_STA); + while (temp != (temp2 = + HFC_inb_nodebug(hc, R_E1_RD_STA))) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: reread " + "STATE because %d!=%d\n", + __func__, temp, temp2); + temp = temp2; /* repeat */ + } + /* broadcast state change to all fragments */ + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: E1 (id=%d) newstate %x\n", + __func__, hc->id, temp & 0x7); + for (i = 0; i < hc->ports; i++) { + dch = hc->chan[hc->dnum[i]].dch; + dch->state = temp & 0x7; + schedule_event(dch, FLG_PHCHANGE); + } + + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) + plxsd_checksync(hc, 0); + } + } + if (r_irq_misc & V_TI_IRQ) { + if (hc->iclock_on) + mISDN_clock_update(hc->iclock, poll, NULL); + handle_timer_irq(hc); + } + + if (r_irq_misc & V_DTMF_IRQ) + hfcmulti_dtmf(hc); + + if (r_irq_misc & V_IRQ_PROC) { + static int irq_proc_cnt; + if (!irq_proc_cnt++) + printk(KERN_DEBUG "%s: got V_IRQ_PROC -" + " this should not happen\n", __func__); + } + + } + if (status & V_FR_IRQSTA) { + /* FIFO IRQ */ + r_irq_oview = HFC_inb_nodebug(hc, R_IRQ_OVIEW); + for (i = 0; i < 8; i++) { + if (r_irq_oview & (1 << i)) + fifo_irq(hc, i); + } + } + +#ifdef IRQ_DEBUG + irqsem = 0; +#endif + spin_unlock(&hc->lock); + return IRQ_HANDLED; + +irq_notforus: +#ifdef IRQ_DEBUG + irqsem = 0; +#endif + spin_unlock(&hc->lock); + return IRQ_NONE; +} + + +/* + * timer callback for D-chan busy resolution. Currently no function + */ + +static void +hfcmulti_dbusy_timer(struct timer_list *t) +{ +} + + +/* + * activate/deactivate hardware for selected channels and mode + * + * configure B-channel with the given protocol + * ch eqals to the HFC-channel (0-31) + * ch is the number of channel (0-4,4-7,8-11,12-15,16-19,20-23,24-27,28-31 + * for S/T, 1-31 for E1) + * the hdlc interrupts will be set/unset + */ +static int +mode_hfcmulti(struct hfc_multi *hc, int ch, int protocol, int slot_tx, + int bank_tx, int slot_rx, int bank_rx) +{ + int flow_tx = 0, flow_rx = 0, routing = 0; + int oslot_tx, oslot_rx; + int conf; + + if (ch < 0 || ch > 31) + return -EINVAL; + oslot_tx = hc->chan[ch].slot_tx; + oslot_rx = hc->chan[ch].slot_rx; + conf = hc->chan[ch].conf; + + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG + "%s: card %d channel %d protocol %x slot old=%d new=%d " + "bank new=%d (TX) slot old=%d new=%d bank new=%d (RX)\n", + __func__, hc->id, ch, protocol, oslot_tx, slot_tx, + bank_tx, oslot_rx, slot_rx, bank_rx); + + if (oslot_tx >= 0 && slot_tx != oslot_tx) { + /* remove from slot */ + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: remove from slot %d (TX)\n", + __func__, oslot_tx); + if (hc->slot_owner[oslot_tx << 1] == ch) { + HFC_outb(hc, R_SLOT, oslot_tx << 1); + HFC_outb(hc, A_SL_CFG, 0); + if (hc->ctype != HFC_TYPE_XHFC) + HFC_outb(hc, A_CONF, 0); + hc->slot_owner[oslot_tx << 1] = -1; + } else { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG + "%s: we are not owner of this tx slot " + "anymore, channel %d is.\n", + __func__, hc->slot_owner[oslot_tx << 1]); + } + } + + if (oslot_rx >= 0 && slot_rx != oslot_rx) { + /* remove from slot */ + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG + "%s: remove from slot %d (RX)\n", + __func__, oslot_rx); + if (hc->slot_owner[(oslot_rx << 1) | 1] == ch) { + HFC_outb(hc, R_SLOT, (oslot_rx << 1) | V_SL_DIR); + HFC_outb(hc, A_SL_CFG, 0); + hc->slot_owner[(oslot_rx << 1) | 1] = -1; + } else { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG + "%s: we are not owner of this rx slot " + "anymore, channel %d is.\n", + __func__, + hc->slot_owner[(oslot_rx << 1) | 1]); + } + } + + if (slot_tx < 0) { + flow_tx = 0x80; /* FIFO->ST */ + /* disable pcm slot */ + hc->chan[ch].slot_tx = -1; + hc->chan[ch].bank_tx = 0; + } else { + /* set pcm slot */ + if (hc->chan[ch].txpending) + flow_tx = 0x80; /* FIFO->ST */ + else + flow_tx = 0xc0; /* PCM->ST */ + /* put on slot */ + routing = bank_tx ? 0xc0 : 0x80; + if (conf >= 0 || bank_tx > 1) + routing = 0x40; /* loop */ + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: put channel %d to slot %d bank" + " %d flow %02x routing %02x conf %d (TX)\n", + __func__, ch, slot_tx, bank_tx, + flow_tx, routing, conf); + HFC_outb(hc, R_SLOT, slot_tx << 1); + HFC_outb(hc, A_SL_CFG, (ch << 1) | routing); + if (hc->ctype != HFC_TYPE_XHFC) + HFC_outb(hc, A_CONF, + (conf < 0) ? 0 : (conf | V_CONF_SL)); + hc->slot_owner[slot_tx << 1] = ch; + hc->chan[ch].slot_tx = slot_tx; + hc->chan[ch].bank_tx = bank_tx; + } + if (slot_rx < 0) { + /* disable pcm slot */ + flow_rx = 0x80; /* ST->FIFO */ + hc->chan[ch].slot_rx = -1; + hc->chan[ch].bank_rx = 0; + } else { + /* set pcm slot */ + if (hc->chan[ch].txpending) + flow_rx = 0x80; /* ST->FIFO */ + else + flow_rx = 0xc0; /* ST->(FIFO,PCM) */ + /* put on slot */ + routing = bank_rx ? 0x80 : 0xc0; /* reversed */ + if (conf >= 0 || bank_rx > 1) + routing = 0x40; /* loop */ + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: put channel %d to slot %d bank" + " %d flow %02x routing %02x conf %d (RX)\n", + __func__, ch, slot_rx, bank_rx, + flow_rx, routing, conf); + HFC_outb(hc, R_SLOT, (slot_rx << 1) | V_SL_DIR); + HFC_outb(hc, A_SL_CFG, (ch << 1) | V_CH_DIR | routing); + hc->slot_owner[(slot_rx << 1) | 1] = ch; + hc->chan[ch].slot_rx = slot_rx; + hc->chan[ch].bank_rx = bank_rx; + } + + switch (protocol) { + case (ISDN_P_NONE): + /* disable TX fifo */ + HFC_outb(hc, R_FIFO, ch << 1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + /* disable RX fifo */ + HFC_outb(hc, R_FIFO, (ch << 1) | 1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + if (hc->chan[ch].bch && hc->ctype != HFC_TYPE_E1) { + hc->hw.a_st_ctrl0[hc->chan[ch].port] &= + ((ch & 0x3) == 0) ? ~V_B1_EN : ~V_B2_EN; + HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_CTRL0, + hc->hw.a_st_ctrl0[hc->chan[ch].port]); + } + if (hc->chan[ch].bch) { + test_and_clear_bit(FLG_HDLC, &hc->chan[ch].bch->Flags); + test_and_clear_bit(FLG_TRANSPARENT, + &hc->chan[ch].bch->Flags); + } + break; + case (ISDN_P_B_RAW): /* B-channel */ + + if (test_bit(HFC_CHIP_B410P, &hc->chip) && + (hc->chan[ch].slot_rx < 0) && + (hc->chan[ch].slot_tx < 0)) { + + printk(KERN_DEBUG + "Setting B-channel %d to echo cancelable " + "state on PCM slot %d\n", ch, + ((ch / 4) * 8) + ((ch % 4) * 4) + 1); + printk(KERN_DEBUG + "Enabling pass through for channel\n"); + vpm_out(hc, ch, ((ch / 4) * 8) + + ((ch % 4) * 4) + 1, 0x01); + /* rx path */ + /* S/T -> PCM */ + HFC_outb(hc, R_FIFO, (ch << 1)); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF); + HFC_outb(hc, R_SLOT, (((ch / 4) * 8) + + ((ch % 4) * 4) + 1) << 1); + HFC_outb(hc, A_SL_CFG, 0x80 | (ch << 1)); + + /* PCM -> FIFO */ + HFC_outb(hc, R_FIFO, 0x20 | (ch << 1) | 1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, 0x20 | V_HDLC_TRP | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + if (hc->chan[ch].protocol != protocol) { + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + } + HFC_outb(hc, R_SLOT, ((((ch / 4) * 8) + + ((ch % 4) * 4) + 1) << 1) | 1); + HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1) | 1); + + /* tx path */ + /* PCM -> S/T */ + HFC_outb(hc, R_FIFO, (ch << 1) | 1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, 0xc0 | V_HDLC_TRP | V_IFF); + HFC_outb(hc, R_SLOT, ((((ch / 4) * 8) + + ((ch % 4) * 4)) << 1) | 1); + HFC_outb(hc, A_SL_CFG, 0x80 | 0x40 | (ch << 1) | 1); + + /* FIFO -> PCM */ + HFC_outb(hc, R_FIFO, 0x20 | (ch << 1)); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, 0x20 | V_HDLC_TRP | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + if (hc->chan[ch].protocol != protocol) { + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + } + /* tx silence */ + HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, hc->silence); + HFC_outb(hc, R_SLOT, (((ch / 4) * 8) + + ((ch % 4) * 4)) << 1); + HFC_outb(hc, A_SL_CFG, 0x80 | 0x20 | (ch << 1)); + } else { + /* enable TX fifo */ + HFC_outb(hc, R_FIFO, ch << 1); + HFC_wait(hc); + if (hc->ctype == HFC_TYPE_XHFC) + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x07 << 2 | + V_HDLC_TRP | V_IFF); + /* Enable FIFO, no interrupt */ + else + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x00 | + V_HDLC_TRP | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + if (hc->chan[ch].protocol != protocol) { + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + } + /* tx silence */ + HFC_outb_nodebug(hc, A_FIFO_DATA0_NOINC, hc->silence); + /* enable RX fifo */ + HFC_outb(hc, R_FIFO, (ch << 1) | 1); + HFC_wait(hc); + if (hc->ctype == HFC_TYPE_XHFC) + HFC_outb(hc, A_CON_HDLC, flow_rx | 0x07 << 2 | + V_HDLC_TRP); + /* Enable FIFO, no interrupt*/ + else + HFC_outb(hc, A_CON_HDLC, flow_rx | 0x00 | + V_HDLC_TRP); + HFC_outb(hc, A_SUBCH_CFG, 0); + HFC_outb(hc, A_IRQ_MSK, 0); + if (hc->chan[ch].protocol != protocol) { + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + } + } + if (hc->ctype != HFC_TYPE_E1) { + hc->hw.a_st_ctrl0[hc->chan[ch].port] |= + ((ch & 0x3) == 0) ? V_B1_EN : V_B2_EN; + HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_CTRL0, + hc->hw.a_st_ctrl0[hc->chan[ch].port]); + } + if (hc->chan[ch].bch) + test_and_set_bit(FLG_TRANSPARENT, + &hc->chan[ch].bch->Flags); + break; + case (ISDN_P_B_HDLC): /* B-channel */ + case (ISDN_P_TE_S0): /* D-channel */ + case (ISDN_P_NT_S0): + case (ISDN_P_TE_E1): + case (ISDN_P_NT_E1): + /* enable TX fifo */ + HFC_outb(hc, R_FIFO, ch << 1); + HFC_wait(hc); + if (hc->ctype == HFC_TYPE_E1 || hc->chan[ch].bch) { + /* E1 or B-channel */ + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04); + HFC_outb(hc, A_SUBCH_CFG, 0); + } else { + /* D-Channel without HDLC fill flags */ + HFC_outb(hc, A_CON_HDLC, flow_tx | 0x04 | V_IFF); + HFC_outb(hc, A_SUBCH_CFG, 2); + } + HFC_outb(hc, A_IRQ_MSK, V_IRQ); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + /* enable RX fifo */ + HFC_outb(hc, R_FIFO, (ch << 1) | 1); + HFC_wait(hc); + HFC_outb(hc, A_CON_HDLC, flow_rx | 0x04); + if (hc->ctype == HFC_TYPE_E1 || hc->chan[ch].bch) + HFC_outb(hc, A_SUBCH_CFG, 0); /* full 8 bits */ + else + HFC_outb(hc, A_SUBCH_CFG, 2); /* 2 bits dchannel */ + HFC_outb(hc, A_IRQ_MSK, V_IRQ); + HFC_outb(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait(hc); + if (hc->chan[ch].bch) { + test_and_set_bit(FLG_HDLC, &hc->chan[ch].bch->Flags); + if (hc->ctype != HFC_TYPE_E1) { + hc->hw.a_st_ctrl0[hc->chan[ch].port] |= + ((ch & 0x3) == 0) ? V_B1_EN : V_B2_EN; + HFC_outb(hc, R_ST_SEL, hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_CTRL0, + hc->hw.a_st_ctrl0[hc->chan[ch].port]); + } + } + break; + default: + printk(KERN_DEBUG "%s: protocol not known %x\n", + __func__, protocol); + hc->chan[ch].protocol = ISDN_P_NONE; + return -ENOPROTOOPT; + } + hc->chan[ch].protocol = protocol; + return 0; +} + + +/* + * connect/disconnect PCM + */ + +static void +hfcmulti_pcm(struct hfc_multi *hc, int ch, int slot_tx, int bank_tx, + int slot_rx, int bank_rx) +{ + if (slot_tx < 0 || slot_rx < 0 || bank_tx < 0 || bank_rx < 0) { + /* disable PCM */ + mode_hfcmulti(hc, ch, hc->chan[ch].protocol, -1, 0, -1, 0); + return; + } + + /* enable pcm */ + mode_hfcmulti(hc, ch, hc->chan[ch].protocol, slot_tx, bank_tx, + slot_rx, bank_rx); +} + +/* + * set/disable conference + */ + +static void +hfcmulti_conf(struct hfc_multi *hc, int ch, int num) +{ + if (num >= 0 && num <= 7) + hc->chan[ch].conf = num; + else + hc->chan[ch].conf = -1; + mode_hfcmulti(hc, ch, hc->chan[ch].protocol, hc->chan[ch].slot_tx, + hc->chan[ch].bank_tx, hc->chan[ch].slot_rx, + hc->chan[ch].bank_rx); +} + + +/* + * set/disable sample loop + */ + +/* NOTE: this function is experimental and therefore disabled */ + +/* + * Layer 1 callback function + */ +static int +hfcm_l1callback(struct dchannel *dch, u_int cmd) +{ + struct hfc_multi *hc = dch->hw; + struct sk_buff_head free_queue; + u_long flags; + + switch (cmd) { + case INFO3_P8: + case INFO3_P10: + break; + case HW_RESET_REQ: + /* start activation */ + spin_lock_irqsave(&hc->lock, flags); + if (hc->ctype == HFC_TYPE_E1) { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: HW_RESET_REQ no BRI\n", + __func__); + } else { + HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 3); /* F3 */ + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, 3); + HFC_outb(hc, A_ST_WR_STATE, 3 | (V_ST_ACT * 3)); + /* activate */ + } + spin_unlock_irqrestore(&hc->lock, flags); + l1_event(dch->l1, HW_POWERUP_IND); + break; + case HW_DEACT_REQ: + __skb_queue_head_init(&free_queue); + /* start deactivation */ + spin_lock_irqsave(&hc->lock, flags); + if (hc->ctype == HFC_TYPE_E1) { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: HW_DEACT_REQ no BRI\n", + __func__); + } else { + HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT * 2); + /* deactivate */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + hc->syncronized &= + ~(1 << hc->chan[dch->slot].port); + plxsd_checksync(hc, 0); + } + } + skb_queue_splice_init(&dch->squeue, &free_queue); + if (dch->tx_skb) { + __skb_queue_tail(&free_queue, dch->tx_skb); + dch->tx_skb = NULL; + } + dch->tx_idx = 0; + if (dch->rx_skb) { + __skb_queue_tail(&free_queue, 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); + spin_unlock_irqrestore(&hc->lock, flags); + __skb_queue_purge(&free_queue); + break; + case HW_POWERUP_REQ: + spin_lock_irqsave(&hc->lock, flags); + if (hc->ctype == HFC_TYPE_E1) { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: HW_POWERUP_REQ no BRI\n", + __func__); + } else { + HFC_outb(hc, R_ST_SEL, hc->chan[dch->slot].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, 3 | 0x10); /* activate */ + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, 3); /* activate */ + } + spin_unlock_irqrestore(&hc->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: + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: unknown command %x\n", + __func__, cmd); + return -1; + } + return 0; +} + +/* + * Layer2 -> Layer 1 Transfer + */ + +static int +handle_dmsg(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 hfc_multi *hc = dch->hw; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + int ret = -EINVAL; + unsigned int id; + u_long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + if (skb->len < 1) + break; + spin_lock_irqsave(&hc->lock, flags); + ret = dchannel_senddata(dch, skb); + if (ret > 0) { /* direct TX */ + id = hh->id; /* skb can be freed */ + hfcmulti_tx(hc, dch->slot); + ret = 0; + /* start fifo */ + HFC_outb(hc, R_FIFO, 0); + HFC_wait(hc); + spin_unlock_irqrestore(&hc->lock, flags); + queue_ch_frame(ch, PH_DATA_CNF, id, NULL); + } else + spin_unlock_irqrestore(&hc->lock, flags); + return ret; + case PH_ACTIVATE_REQ: + if (dch->dev.D.protocol != ISDN_P_TE_S0) { + spin_lock_irqsave(&hc->lock, flags); + ret = 0; + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: PH_ACTIVATE port %d (0..%d)\n", + __func__, hc->chan[dch->slot].port, + hc->ports - 1); + /* start activation */ + if (hc->ctype == HFC_TYPE_E1) { + ph_state_change(dch); + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: E1 report state %x \n", + __func__, dch->state); + } else { + HFC_outb(hc, R_ST_SEL, + hc->chan[dch->slot].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, V_ST_LD_STA | 1); + /* G1 */ + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, 1); + HFC_outb(hc, A_ST_WR_STATE, 1 | + (V_ST_ACT * 3)); /* activate */ + dch->state = 1; + } + spin_unlock_irqrestore(&hc->lock, flags); + } else + ret = l1_event(dch->l1, hh->prim); + break; + case PH_DEACTIVATE_REQ: + test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); + if (dch->dev.D.protocol != ISDN_P_TE_S0) { + struct sk_buff_head free_queue; + + __skb_queue_head_init(&free_queue); + spin_lock_irqsave(&hc->lock, flags); + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: PH_DEACTIVATE port %d (0..%d)\n", + __func__, hc->chan[dch->slot].port, + hc->ports - 1); + /* start deactivation */ + if (hc->ctype == HFC_TYPE_E1) { + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: PH_DEACTIVATE no BRI\n", + __func__); + } else { + HFC_outb(hc, R_ST_SEL, + hc->chan[dch->slot].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, V_ST_ACT * 2); + /* deactivate */ + dch->state = 1; + } + skb_queue_splice_init(&dch->squeue, &free_queue); + if (dch->tx_skb) { + __skb_queue_tail(&free_queue, dch->tx_skb); + dch->tx_skb = NULL; + } + dch->tx_idx = 0; + if (dch->rx_skb) { + __skb_queue_tail(&free_queue, 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); +#ifdef FIXME + if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) + dchannel_sched_event(&hc->dch, D_CLEARBUSY); +#endif + ret = 0; + spin_unlock_irqrestore(&hc->lock, flags); + __skb_queue_purge(&free_queue); + } else + ret = l1_event(dch->l1, hh->prim); + break; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +static void +deactivate_bchannel(struct bchannel *bch) +{ + struct hfc_multi *hc = bch->hw; + u_long flags; + + spin_lock_irqsave(&hc->lock, flags); + mISDN_clear_bchannel(bch); + hc->chan[bch->slot].coeff_count = 0; + 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); + spin_unlock_irqrestore(&hc->lock, flags); +} + +static int +handle_bmsg(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct hfc_multi *hc = bch->hw; + int ret = -EINVAL; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + unsigned long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + if (!skb->len) + break; + spin_lock_irqsave(&hc->lock, flags); + ret = bchannel_senddata(bch, skb); + if (ret > 0) { /* direct TX */ + hfcmulti_tx(hc, bch->slot); + ret = 0; + /* start fifo */ + HFC_outb_nodebug(hc, R_FIFO, 0); + HFC_wait_nodebug(hc); + } + spin_unlock_irqrestore(&hc->lock, flags); + return ret; + case PH_ACTIVATE_REQ: + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: PH_ACTIVATE ch %d (0..32)\n", + __func__, bch->slot); + spin_lock_irqsave(&hc->lock, flags); + /* activate B-channel if not already activated */ + if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) { + hc->chan[bch->slot].txpending = 0; + ret = mode_hfcmulti(hc, bch->slot, + ch->protocol, + hc->chan[bch->slot].slot_tx, + hc->chan[bch->slot].bank_tx, + hc->chan[bch->slot].slot_rx, + hc->chan[bch->slot].bank_rx); + if (!ret) { + if (ch->protocol == ISDN_P_B_RAW && !hc->dtmf + && test_bit(HFC_CHIP_DTMF, &hc->chip)) { + /* start decoder */ + hc->dtmf = 1; + if (debug & DEBUG_HFCMULTI_DTMF) + printk(KERN_DEBUG + "%s: start dtmf decoder\n", + __func__); + HFC_outb(hc, R_DTMF, hc->hw.r_dtmf | + V_RST_DTMF); + } + } + } else + ret = 0; + spin_unlock_irqrestore(&hc->lock, flags); + if (!ret) + _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, NULL, + GFP_KERNEL); + break; + case PH_CONTROL_REQ: + spin_lock_irqsave(&hc->lock, flags); + switch (hh->id) { + case HFC_SPL_LOOP_ON: /* set sample loop */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: HFC_SPL_LOOP_ON (len = %d)\n", + __func__, skb->len); + ret = 0; + break; + case HFC_SPL_LOOP_OFF: /* set silence */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_SPL_LOOP_OFF\n", + __func__); + ret = 0; + break; + default: + printk(KERN_ERR + "%s: unknown PH_CONTROL_REQ info %x\n", + __func__, hh->id); + ret = -EINVAL; + } + spin_unlock_irqrestore(&hc->lock, flags); + break; + case PH_DEACTIVATE_REQ: + deactivate_bchannel(bch); /* locked there */ + _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, NULL, + GFP_KERNEL); + ret = 0; + break; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +/* + * bchannel control function + */ +static int +channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) +{ + int ret = 0; + struct dsp_features *features = + (struct dsp_features *)(*((u_long *)&cq->p1)); + struct hfc_multi *hc = bch->hw; + int slot_tx; + int bank_tx; + int slot_rx; + int bank_rx; + int num; + + switch (cq->op) { + case MISDN_CTRL_GETOP: + ret = mISDN_ctrl_bchannel(bch, cq); + cq->op |= MISDN_CTRL_HFC_OP | MISDN_CTRL_HW_FEATURES_OP; + break; + case MISDN_CTRL_RX_OFF: /* turn off / on rx stream */ + ret = mISDN_ctrl_bchannel(bch, cq); + hc->chan[bch->slot].rx_off = !!cq->p1; + if (!hc->chan[bch->slot].rx_off) { + /* reset fifo on rx on */ + HFC_outb_nodebug(hc, R_FIFO, (bch->slot << 1) | 1); + HFC_wait_nodebug(hc); + HFC_outb_nodebug(hc, R_INC_RES_FIFO, V_RES_F); + HFC_wait_nodebug(hc); + } + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: RX_OFF request (nr=%d off=%d)\n", + __func__, bch->nr, hc->chan[bch->slot].rx_off); + break; + case MISDN_CTRL_FILL_EMPTY: + ret = mISDN_ctrl_bchannel(bch, cq); + hc->silence = bch->fill[0]; + memset(hc->silence_data, hc->silence, sizeof(hc->silence_data)); + break; + case MISDN_CTRL_HW_FEATURES: /* fill features structure */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HW_FEATURE request\n", + __func__); + /* create confirm */ + features->hfc_id = hc->id; + if (test_bit(HFC_CHIP_DTMF, &hc->chip)) + features->hfc_dtmf = 1; + if (test_bit(HFC_CHIP_CONF, &hc->chip)) + features->hfc_conf = 1; + features->hfc_loops = 0; + if (test_bit(HFC_CHIP_B410P, &hc->chip)) { + features->hfc_echocanhw = 1; + } else { + features->pcm_id = hc->pcm; + features->pcm_slots = hc->slots; + features->pcm_banks = 2; + } + break; + case MISDN_CTRL_HFC_PCM_CONN: /* connect to pcm timeslot (0..N) */ + slot_tx = cq->p1 & 0xff; + bank_tx = cq->p1 >> 8; + slot_rx = cq->p2 & 0xff; + bank_rx = cq->p2 >> 8; + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG + "%s: HFC_PCM_CONN slot %d bank %d (TX) " + "slot %d bank %d (RX)\n", + __func__, slot_tx, bank_tx, + slot_rx, bank_rx); + if (slot_tx < hc->slots && bank_tx <= 2 && + slot_rx < hc->slots && bank_rx <= 2) + hfcmulti_pcm(hc, bch->slot, + slot_tx, bank_tx, slot_rx, bank_rx); + else { + printk(KERN_WARNING + "%s: HFC_PCM_CONN slot %d bank %d (TX) " + "slot %d bank %d (RX) out of range\n", + __func__, slot_tx, bank_tx, + slot_rx, bank_rx); + ret = -EINVAL; + } + break; + case MISDN_CTRL_HFC_PCM_DISC: /* release interface from pcm timeslot */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_PCM_DISC\n", + __func__); + hfcmulti_pcm(hc, bch->slot, -1, 0, -1, 0); + break; + case MISDN_CTRL_HFC_CONF_JOIN: /* join conference (0..7) */ + num = cq->p1 & 0xff; + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_CONF_JOIN conf %d\n", + __func__, num); + if (num <= 7) + hfcmulti_conf(hc, bch->slot, num); + else { + printk(KERN_WARNING + "%s: HW_CONF_JOIN conf %d out of range\n", + __func__, num); + ret = -EINVAL; + } + break; + case MISDN_CTRL_HFC_CONF_SPLIT: /* split conference */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_CONF_SPLIT\n", __func__); + hfcmulti_conf(hc, bch->slot, -1); + break; + case MISDN_CTRL_HFC_ECHOCAN_ON: + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_ECHOCAN_ON\n", __func__); + if (test_bit(HFC_CHIP_B410P, &hc->chip)) + vpm_echocan_on(hc, bch->slot, cq->p1); + else + ret = -EINVAL; + break; + + case MISDN_CTRL_HFC_ECHOCAN_OFF: + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: HFC_ECHOCAN_OFF\n", + __func__); + if (test_bit(HFC_CHIP_B410P, &hc->chip)) + vpm_echocan_off(hc, bch->slot); + else + ret = -EINVAL; + break; + default: + ret = mISDN_ctrl_bchannel(bch, cq); + break; + } + return ret; +} + +static int +hfcm_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct hfc_multi *hc = bch->hw; + int err = -EINVAL; + u_long flags; + + if (bch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: cmd:%x %p\n", + __func__, cmd, arg); + switch (cmd) { + case CLOSE_CHANNEL: + test_and_clear_bit(FLG_OPEN, &bch->Flags); + deactivate_bchannel(bch); /* locked there */ + ch->protocol = ISDN_P_NONE; + ch->peer = NULL; + module_put(THIS_MODULE); + err = 0; + break; + case CONTROL_CHANNEL: + spin_lock_irqsave(&hc->lock, flags); + err = channel_bctrl(bch, arg); + spin_unlock_irqrestore(&hc->lock, flags); + break; + default: + printk(KERN_WARNING "%s: unknown prim(%x)\n", + __func__, cmd); + } + return err; +} + +/* + * handle D-channel events + * + * handle state change event + */ +static void +ph_state_change(struct dchannel *dch) +{ + struct hfc_multi *hc; + int ch, i; + + if (!dch) { + printk(KERN_WARNING "%s: ERROR given dch is NULL\n", __func__); + return; + } + hc = dch->hw; + ch = dch->slot; + + if (hc->ctype == HFC_TYPE_E1) { + if (dch->dev.D.protocol == ISDN_P_TE_E1) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: E1 TE (id=%d) newstate %x\n", + __func__, hc->id, dch->state); + } else { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: E1 NT (id=%d) newstate %x\n", + __func__, hc->id, dch->state); + } + switch (dch->state) { + case (1): + if (hc->e1_state != 1) { + for (i = 1; i <= 31; i++) { + /* reset fifos on e1 activation */ + HFC_outb_nodebug(hc, R_FIFO, + (i << 1) | 1); + HFC_wait_nodebug(hc); + HFC_outb_nodebug(hc, R_INC_RES_FIFO, + V_RES_F); + HFC_wait_nodebug(hc); + } + } + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + break; + + default: + if (hc->e1_state != 1) + return; + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + } + hc->e1_state = dch->state; + } else { + if (dch->dev.D.protocol == ISDN_P_TE_S0) { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG + "%s: S/T TE newstate %x\n", + __func__, dch->state); + switch (dch->state) { + case (0): + l1_event(dch->l1, HW_RESET_IND); + break; + case (3): + l1_event(dch->l1, HW_DEACT_IND); + break; + case (5): + case (8): + l1_event(dch->l1, ANYSIGNAL); + break; + case (6): + l1_event(dch->l1, INFO2); + break; + case (7): + l1_event(dch->l1, INFO4_P8); + break; + } + } else { + if (debug & DEBUG_HFCMULTI_STATE) + printk(KERN_DEBUG "%s: S/T NT newstate %x\n", + __func__, dch->state); + switch (dch->state) { + case (2): + if (hc->chan[ch].nt_timer == 0) { + hc->chan[ch].nt_timer = -1; + HFC_outb(hc, R_ST_SEL, + hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + HFC_outb(hc, A_ST_WR_STATE, 4 | + V_ST_LD_STA); /* G4 */ + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, 4); + dch->state = 4; + } else { + /* one extra count for the next event */ + hc->chan[ch].nt_timer = + nt_t1_count[poll_timer] + 1; + HFC_outb(hc, R_ST_SEL, + hc->chan[ch].port); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + /* allow G2 -> G3 transition */ + HFC_outb(hc, A_ST_WR_STATE, 2 | + V_SET_G2_G3); + } + break; + case (1): + hc->chan[ch].nt_timer = -1; + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + break; + case (4): + hc->chan[ch].nt_timer = -1; + break; + case (3): + hc->chan[ch].nt_timer = -1; + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + break; + } + } + } +} + +/* + * called for card mode init message + */ + +static void +hfcmulti_initmode(struct dchannel *dch) +{ + struct hfc_multi *hc = dch->hw; + u_char a_st_wr_state, r_e1_wr_sta; + int i, pt; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered\n", __func__); + + i = dch->slot; + pt = hc->chan[i].port; + if (hc->ctype == HFC_TYPE_E1) { + /* E1 */ + hc->chan[hc->dnum[pt]].slot_tx = -1; + hc->chan[hc->dnum[pt]].slot_rx = -1; + hc->chan[hc->dnum[pt]].conf = -1; + if (hc->dnum[pt]) { + mode_hfcmulti(hc, dch->slot, dch->dev.D.protocol, + -1, 0, -1, 0); + timer_setup(&dch->timer, hfcmulti_dbusy_timer, 0); + } + for (i = 1; i <= 31; i++) { + if (!((1 << i) & hc->bmask[pt])) /* skip unused chan */ + continue; + hc->chan[i].slot_tx = -1; + hc->chan[i].slot_rx = -1; + hc->chan[i].conf = -1; + mode_hfcmulti(hc, i, ISDN_P_NONE, -1, 0, -1, 0); + } + } + if (hc->ctype == HFC_TYPE_E1 && pt == 0) { + /* E1, port 0 */ + dch = hc->chan[hc->dnum[0]].dch; + if (test_bit(HFC_CFG_REPORT_LOS, &hc->chan[hc->dnum[0]].cfg)) { + HFC_outb(hc, R_LOS0, 255); /* 2 ms */ + HFC_outb(hc, R_LOS1, 255); /* 512 ms */ + } + if (test_bit(HFC_CFG_OPTICAL, &hc->chan[hc->dnum[0]].cfg)) { + HFC_outb(hc, R_RX0, 0); + hc->hw.r_tx0 = 0 | V_OUT_EN; + } else { + HFC_outb(hc, R_RX0, 1); + hc->hw.r_tx0 = 1 | V_OUT_EN; + } + hc->hw.r_tx1 = V_ATX | V_NTRI; + HFC_outb(hc, R_TX0, hc->hw.r_tx0); + HFC_outb(hc, R_TX1, hc->hw.r_tx1); + HFC_outb(hc, R_TX_FR0, 0x00); + HFC_outb(hc, R_TX_FR1, 0xf8); + + if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dnum[0]].cfg)) + HFC_outb(hc, R_TX_FR2, V_TX_MF | V_TX_E | V_NEG_E); + + HFC_outb(hc, R_RX_FR0, V_AUTO_RESYNC | V_AUTO_RECO | 0); + + if (test_bit(HFC_CFG_CRC4, &hc->chan[hc->dnum[0]].cfg)) + HFC_outb(hc, R_RX_FR1, V_RX_MF | V_RX_MF_SYNC); + + if (dch->dev.D.protocol == ISDN_P_NT_E1) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: E1 port is NT-mode\n", + __func__); + r_e1_wr_sta = 0; /* G0 */ + hc->e1_getclock = 0; + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: E1 port is TE-mode\n", + __func__); + r_e1_wr_sta = 0; /* F0 */ + hc->e1_getclock = 1; + } + if (test_bit(HFC_CHIP_RX_SYNC, &hc->chip)) + HFC_outb(hc, R_SYNC_OUT, V_SYNC_E1_RX); + else + HFC_outb(hc, R_SYNC_OUT, 0); + if (test_bit(HFC_CHIP_E1CLOCK_GET, &hc->chip)) + hc->e1_getclock = 1; + if (test_bit(HFC_CHIP_E1CLOCK_PUT, &hc->chip)) + hc->e1_getclock = 0; + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { + /* SLAVE (clock master) */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: E1 port is clock master " + "(clock from PCM)\n", __func__); + HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC | V_PCM_SYNC); + } else { + if (hc->e1_getclock) { + /* MASTER (clock slave) */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: E1 port is clock slave " + "(clock to PCM)\n", __func__); + HFC_outb(hc, R_SYNC_CTRL, V_SYNC_OFFS); + } else { + /* MASTER (clock master) */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: E1 port is " + "clock master " + "(clock from QUARTZ)\n", + __func__); + HFC_outb(hc, R_SYNC_CTRL, V_EXT_CLK_SYNC | + V_PCM_SYNC | V_JATT_OFF); + HFC_outb(hc, R_SYNC_OUT, 0); + } + } + HFC_outb(hc, R_JATT_ATT, 0x9c); /* undoc register */ + HFC_outb(hc, R_PWM_MD, V_PWM0_MD); + HFC_outb(hc, R_PWM0, 0x50); + HFC_outb(hc, R_PWM1, 0xff); + /* state machine setup */ + HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta | V_E1_LD_STA); + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, R_E1_WR_STA, r_e1_wr_sta); + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + hc->syncronized = 0; + plxsd_checksync(hc, 0); + } + } + if (hc->ctype != HFC_TYPE_E1) { + /* ST */ + hc->chan[i].slot_tx = -1; + hc->chan[i].slot_rx = -1; + hc->chan[i].conf = -1; + mode_hfcmulti(hc, i, dch->dev.D.protocol, -1, 0, -1, 0); + timer_setup(&dch->timer, hfcmulti_dbusy_timer, 0); + hc->chan[i - 2].slot_tx = -1; + hc->chan[i - 2].slot_rx = -1; + hc->chan[i - 2].conf = -1; + mode_hfcmulti(hc, i - 2, ISDN_P_NONE, -1, 0, -1, 0); + hc->chan[i - 1].slot_tx = -1; + hc->chan[i - 1].slot_rx = -1; + hc->chan[i - 1].conf = -1; + mode_hfcmulti(hc, i - 1, ISDN_P_NONE, -1, 0, -1, 0); + /* select interface */ + HFC_outb(hc, R_ST_SEL, pt); + /* undocumented: delay after R_ST_SEL */ + udelay(1); + if (dch->dev.D.protocol == ISDN_P_NT_S0) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: ST port %d is NT-mode\n", + __func__, pt); + /* clock delay */ + HFC_outb(hc, A_ST_CLK_DLY, clockdelay_nt); + a_st_wr_state = 1; /* G1 */ + hc->hw.a_st_ctrl0[pt] = V_ST_MD; + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: ST port %d is TE-mode\n", + __func__, pt); + /* clock delay */ + HFC_outb(hc, A_ST_CLK_DLY, clockdelay_te); + a_st_wr_state = 2; /* F2 */ + hc->hw.a_st_ctrl0[pt] = 0; + } + if (!test_bit(HFC_CFG_NONCAP_TX, &hc->chan[i].cfg)) + hc->hw.a_st_ctrl0[pt] |= V_TX_LI; + if (hc->ctype == HFC_TYPE_XHFC) { + hc->hw.a_st_ctrl0[pt] |= 0x40 /* V_ST_PU_CTRL */; + HFC_outb(hc, 0x35 /* A_ST_CTRL3 */, + 0x7c << 1 /* V_ST_PULSE */); + } + /* line setup */ + HFC_outb(hc, A_ST_CTRL0, hc->hw.a_st_ctrl0[pt]); + /* disable E-channel */ + if ((dch->dev.D.protocol == ISDN_P_NT_S0) || + test_bit(HFC_CFG_DIS_ECHANNEL, &hc->chan[i].cfg)) + HFC_outb(hc, A_ST_CTRL1, V_E_IGNO); + else + HFC_outb(hc, A_ST_CTRL1, 0); + /* enable B-channel receive */ + HFC_outb(hc, A_ST_CTRL2, V_B1_RX_EN | V_B2_RX_EN); + /* state machine setup */ + HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state | V_ST_LD_STA); + udelay(6); /* wait at least 5,21us */ + HFC_outb(hc, A_ST_WR_STATE, a_st_wr_state); + hc->hw.r_sci_msk |= 1 << pt; + /* state machine interrupts */ + HFC_outb(hc, R_SCI_MSK, hc->hw.r_sci_msk); + /* unset sync on port */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + hc->syncronized &= + ~(1 << hc->chan[dch->slot].port); + plxsd_checksync(hc, 0); + } + } + if (debug & DEBUG_HFCMULTI_INIT) + printk("%s: done\n", __func__); +} + + +static int +open_dchannel(struct hfc_multi *hc, struct dchannel *dch, + struct channel_req *rq) +{ + int err = 0; + u_long flags; + + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__, + dch->dev.id, __builtin_return_address(0)); + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + if ((dch->dev.D.protocol != ISDN_P_NONE) && + (dch->dev.D.protocol != rq->protocol)) { + if (debug & DEBUG_HFCMULTI_MODE) + printk(KERN_DEBUG "%s: change protocol %x to %x\n", + __func__, dch->dev.D.protocol, rq->protocol); + } + if ((dch->dev.D.protocol == ISDN_P_TE_S0) && + (rq->protocol != ISDN_P_TE_S0)) + l1_event(dch->l1, CLOSE_CHANNEL); + if (dch->dev.D.protocol != rq->protocol) { + if (rq->protocol == ISDN_P_TE_S0) { + err = create_l1(dch, hfcm_l1callback); + if (err) + return err; + } + dch->dev.D.protocol = rq->protocol; + spin_lock_irqsave(&hc->lock, flags); + hfcmulti_initmode(dch); + spin_unlock_irqrestore(&hc->lock, flags); + } + if (test_bit(FLG_ACTIVE, &dch->Flags)) + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, MISDN_ID_ANY, + 0, NULL, GFP_KERNEL); + rq->ch = &dch->dev.D; + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s:cannot get module\n", __func__); + return 0; +} + +static int +open_bchannel(struct hfc_multi *hc, struct dchannel *dch, + struct channel_req *rq) +{ + struct bchannel *bch; + int ch; + + if (!test_channelmap(rq->adr.channel, dch->dev.channelmap)) + return -EINVAL; + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + if (hc->ctype == HFC_TYPE_E1) + ch = rq->adr.channel; + else + ch = (rq->adr.channel - 1) + (dch->slot - 2); + bch = hc->chan[ch].bch; + if (!bch) { + printk(KERN_ERR "%s:internal error ch %d has no bch\n", + __func__, ch); + return -EINVAL; + } + if (test_and_set_bit(FLG_OPEN, &bch->Flags)) + return -EBUSY; /* b-channel can be only open once */ + bch->ch.protocol = rq->protocol; + hc->chan[ch].rx_off = 0; + rq->ch = &bch->ch; + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s:cannot get module\n", __func__); + return 0; +} + +/* + * device control function + */ +static int +channel_dctrl(struct dchannel *dch, struct mISDN_ctrl_req *cq) +{ + struct hfc_multi *hc = dch->hw; + int ret = 0; + int wd_mode, wd_cnt; + + switch (cq->op) { + case MISDN_CTRL_GETOP: + cq->op = MISDN_CTRL_HFC_OP | MISDN_CTRL_L1_TIMER3; + break; + case MISDN_CTRL_HFC_WD_INIT: /* init the watchdog */ + wd_cnt = cq->p1 & 0xf; + wd_mode = !!(cq->p1 >> 4); + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: MISDN_CTRL_HFC_WD_INIT mode %s" + ", counter 0x%x\n", __func__, + wd_mode ? "AUTO" : "MANUAL", wd_cnt); + /* set the watchdog timer */ + HFC_outb(hc, R_TI_WD, poll_timer | (wd_cnt << 4)); + hc->hw.r_bert_wd_md = (wd_mode ? V_AUTO_WD_RES : 0); + if (hc->ctype == HFC_TYPE_XHFC) + hc->hw.r_bert_wd_md |= 0x40 /* V_WD_EN */; + /* init the watchdog register and reset the counter */ + HFC_outb(hc, R_BERT_WD_MD, hc->hw.r_bert_wd_md | V_WD_RES); + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + /* enable the watchdog output for Speech-Design */ + HFC_outb(hc, R_GPIO_SEL, V_GPIO_SEL7); + HFC_outb(hc, R_GPIO_EN1, V_GPIO_EN15); + HFC_outb(hc, R_GPIO_OUT1, 0); + HFC_outb(hc, R_GPIO_OUT1, V_GPIO_OUT15); + } + break; + case MISDN_CTRL_HFC_WD_RESET: /* reset the watchdog counter */ + if (debug & DEBUG_HFCMULTI_MSG) + printk(KERN_DEBUG "%s: MISDN_CTRL_HFC_WD_RESET\n", + __func__); + HFC_outb(hc, R_BERT_WD_MD, hc->hw.r_bert_wd_md | V_WD_RES); + break; + case MISDN_CTRL_L1_TIMER3: + ret = l1_event(dch->l1, HW_TIMER3_VALUE | (cq->p1 & 0xff)); + break; + default: + printk(KERN_WARNING "%s: unknown Op %x\n", + __func__, cq->op); + ret = -EINVAL; + break; + } + return ret; +} + +static int +hfcm_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct hfc_multi *hc = dch->hw; + struct channel_req *rq; + int err = 0; + u_long flags; + + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: cmd:%x %p\n", + __func__, cmd, arg); + switch (cmd) { + case OPEN_CHANNEL: + rq = arg; + switch (rq->protocol) { + case ISDN_P_TE_S0: + case ISDN_P_NT_S0: + if (hc->ctype == HFC_TYPE_E1) { + err = -EINVAL; + break; + } + err = open_dchannel(hc, dch, rq); /* locked there */ + break; + case ISDN_P_TE_E1: + case ISDN_P_NT_E1: + if (hc->ctype != HFC_TYPE_E1) { + err = -EINVAL; + break; + } + err = open_dchannel(hc, dch, rq); /* locked there */ + break; + default: + spin_lock_irqsave(&hc->lock, flags); + err = open_bchannel(hc, dch, rq); + spin_unlock_irqrestore(&hc->lock, flags); + } + break; + case CLOSE_CHANNEL: + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: dev(%d) close from %p\n", + __func__, dch->dev.id, + __builtin_return_address(0)); + module_put(THIS_MODULE); + break; + case CONTROL_CHANNEL: + spin_lock_irqsave(&hc->lock, flags); + err = channel_dctrl(dch, arg); + spin_unlock_irqrestore(&hc->lock, flags); + break; + default: + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: unknown command %x\n", + __func__, cmd); + err = -EINVAL; + } + return err; +} + +static int +clockctl(void *priv, int enable) +{ + struct hfc_multi *hc = priv; + + hc->iclock_on = enable; + return 0; +} + +/* + * initialize the card + */ + +/* + * start timer irq, wait some time and check if we have interrupts. + * if not, reset chip and try again. + */ +static int +init_card(struct hfc_multi *hc) +{ + int err = -EIO; + u_long flags; + void __iomem *plx_acc; + u_long plx_flags; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered\n", __func__); + + spin_lock_irqsave(&hc->lock, flags); + /* set interrupts but leave global interrupt disabled */ + hc->hw.r_irq_ctrl = V_FIFO_IRQ; + disable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + + if (request_irq(hc->irq, hfcmulti_interrupt, IRQF_SHARED, + "HFC-multi", hc)) { + printk(KERN_WARNING "mISDN: Could not get interrupt %d.\n", + hc->irq); + hc->irq = 0; + return -EIO; + } + + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc = hc->plx_membase + PLX_INTCSR; + writew((PLX_INTCSR_PCIINT_ENABLE | PLX_INTCSR_LINTI1_ENABLE), + plx_acc); /* enable PCI & LINT1 irq */ + spin_unlock_irqrestore(&plx_lock, plx_flags); + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: IRQ %d count %d\n", + __func__, hc->irq, hc->irqcnt); + err = init_chip(hc); + if (err) + goto error; + /* + * Finally enable IRQ output + * this is only allowed, if an IRQ routine is already + * established for this HFC, so don't do that earlier + */ + spin_lock_irqsave(&hc->lock, flags); + enable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + /* printk(KERN_DEBUG "no master irq set!!!\n"); */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((100 * HZ) / 1000); /* Timeout 100ms */ + /* turn IRQ off until chip is completely initialized */ + spin_lock_irqsave(&hc->lock, flags); + disable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: IRQ %d count %d\n", + __func__, hc->irq, hc->irqcnt); + if (hc->irqcnt) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done\n", __func__); + + return 0; + } + if (test_bit(HFC_CHIP_PCM_SLAVE, &hc->chip)) { + printk(KERN_INFO "ignoring missing interrupts\n"); + return 0; + } + + printk(KERN_ERR "HFC PCI: IRQ(%d) getting no interrupts during init.\n", + hc->irq); + + err = -EIO; + +error: + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + spin_lock_irqsave(&plx_lock, plx_flags); + plx_acc = hc->plx_membase + PLX_INTCSR; + writew(0x00, plx_acc); /*disable IRQs*/ + spin_unlock_irqrestore(&plx_lock, plx_flags); + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: free irq %d\n", __func__, hc->irq); + if (hc->irq) { + free_irq(hc->irq, hc); + hc->irq = 0; + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done (err=%d)\n", __func__, err); + return err; +} + +/* + * find pci device and set it up + */ + +static int +setup_pci(struct hfc_multi *hc, struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct hm_map *m = (struct hm_map *)ent->driver_data; + + printk(KERN_INFO + "HFC-multi: card manufacturer: '%s' card name: '%s' clock: %s\n", + m->vendor_name, m->card_name, m->clock2 ? "double" : "normal"); + + hc->pci_dev = pdev; + if (m->clock2) + test_and_set_bit(HFC_CHIP_CLOCK2, &hc->chip); + + if (ent->vendor == PCI_VENDOR_ID_DIGIUM && + ent->device == PCI_DEVICE_ID_DIGIUM_HFC4S) { + test_and_set_bit(HFC_CHIP_B410P, &hc->chip); + test_and_set_bit(HFC_CHIP_PCM_MASTER, &hc->chip); + test_and_clear_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); + hc->slots = 32; + } + + if (hc->pci_dev->irq <= 0) { + printk(KERN_WARNING "HFC-multi: No IRQ for PCI card found.\n"); + return -EIO; + } + if (pci_enable_device(hc->pci_dev)) { + printk(KERN_WARNING "HFC-multi: Error enabling PCI card.\n"); + return -EIO; + } + hc->leds = m->leds; + hc->ledstate = 0xAFFEAFFE; + hc->opticalsupport = m->opticalsupport; + + hc->pci_iobase = 0; + hc->pci_membase = NULL; + hc->plx_membase = NULL; + + /* set memory access methods */ + if (m->io_mode) /* use mode from card config */ + hc->io_mode = m->io_mode; + switch (hc->io_mode) { + case HFC_IO_MODE_PLXSD: + test_and_set_bit(HFC_CHIP_PLXSD, &hc->chip); + hc->slots = 128; /* required */ + hc->HFC_outb = HFC_outb_pcimem; + hc->HFC_inb = HFC_inb_pcimem; + hc->HFC_inw = HFC_inw_pcimem; + hc->HFC_wait = HFC_wait_pcimem; + hc->read_fifo = read_fifo_pcimem; + hc->write_fifo = write_fifo_pcimem; + hc->plx_origmembase = hc->pci_dev->resource[0].start; + /* MEMBASE 1 is PLX PCI Bridge */ + + if (!hc->plx_origmembase) { + printk(KERN_WARNING + "HFC-multi: No IO-Memory for PCI PLX bridge found\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + hc->plx_membase = ioremap(hc->plx_origmembase, 0x80); + if (!hc->plx_membase) { + printk(KERN_WARNING + "HFC-multi: failed to remap plx address space. " + "(internal error)\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + printk(KERN_INFO + "HFC-multi: plx_membase:%#lx plx_origmembase:%#lx\n", + (u_long)hc->plx_membase, hc->plx_origmembase); + + hc->pci_origmembase = hc->pci_dev->resource[2].start; + /* MEMBASE 1 is PLX PCI Bridge */ + if (!hc->pci_origmembase) { + printk(KERN_WARNING + "HFC-multi: No IO-Memory for PCI card found\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + hc->pci_membase = ioremap(hc->pci_origmembase, 0x400); + if (!hc->pci_membase) { + printk(KERN_WARNING "HFC-multi: failed to remap io " + "address space. (internal error)\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + printk(KERN_INFO + "card %d: defined at MEMBASE %#lx (%#lx) IRQ %d HZ %d " + "leds-type %d\n", + hc->id, (u_long)hc->pci_membase, hc->pci_origmembase, + hc->pci_dev->irq, HZ, hc->leds); + pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO); + break; + case HFC_IO_MODE_PCIMEM: + hc->HFC_outb = HFC_outb_pcimem; + hc->HFC_inb = HFC_inb_pcimem; + hc->HFC_inw = HFC_inw_pcimem; + hc->HFC_wait = HFC_wait_pcimem; + hc->read_fifo = read_fifo_pcimem; + hc->write_fifo = write_fifo_pcimem; + hc->pci_origmembase = hc->pci_dev->resource[1].start; + if (!hc->pci_origmembase) { + printk(KERN_WARNING + "HFC-multi: No IO-Memory for PCI card found\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + hc->pci_membase = ioremap(hc->pci_origmembase, 256); + if (!hc->pci_membase) { + printk(KERN_WARNING + "HFC-multi: failed to remap io address space. " + "(internal error)\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + printk(KERN_INFO "card %d: defined at MEMBASE %#lx (%#lx) IRQ " + "%d HZ %d leds-type %d\n", hc->id, (u_long)hc->pci_membase, + hc->pci_origmembase, hc->pci_dev->irq, HZ, hc->leds); + pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_MEMIO); + break; + case HFC_IO_MODE_REGIO: + hc->HFC_outb = HFC_outb_regio; + hc->HFC_inb = HFC_inb_regio; + hc->HFC_inw = HFC_inw_regio; + hc->HFC_wait = HFC_wait_regio; + hc->read_fifo = read_fifo_regio; + hc->write_fifo = write_fifo_regio; + hc->pci_iobase = (u_int) hc->pci_dev->resource[0].start; + if (!hc->pci_iobase) { + printk(KERN_WARNING + "HFC-multi: No IO for PCI card found\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + if (!request_region(hc->pci_iobase, 8, "hfcmulti")) { + printk(KERN_WARNING "HFC-multi: failed to request " + "address space at 0x%08lx (internal error)\n", + hc->pci_iobase); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + printk(KERN_INFO + "%s %s: defined at IOBASE %#x IRQ %d HZ %d leds-type %d\n", + m->vendor_name, m->card_name, (u_int) hc->pci_iobase, + hc->pci_dev->irq, HZ, hc->leds); + pci_write_config_word(hc->pci_dev, PCI_COMMAND, PCI_ENA_REGIO); + break; + default: + printk(KERN_WARNING "HFC-multi: Invalid IO mode.\n"); + pci_disable_device(hc->pci_dev); + return -EIO; + } + + pci_set_drvdata(hc->pci_dev, hc); + + /* At this point the needed PCI config is done */ + /* fifos are still not enabled */ + return 0; +} + + +/* + * remove port + */ + +static void +release_port(struct hfc_multi *hc, struct dchannel *dch) +{ + int pt, ci, i = 0; + u_long flags; + struct bchannel *pb; + + ci = dch->slot; + pt = hc->chan[ci].port; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: entered for port %d\n", + __func__, pt + 1); + + if (pt >= hc->ports) { + printk(KERN_WARNING "%s: ERROR port out of range (%d).\n", + __func__, pt + 1); + return; + } + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: releasing port=%d\n", + __func__, pt + 1); + + if (dch->dev.D.protocol == ISDN_P_TE_S0) + l1_event(dch->l1, CLOSE_CHANNEL); + + hc->chan[ci].dch = NULL; + + if (hc->created[pt]) { + hc->created[pt] = 0; + mISDN_unregister_device(&dch->dev); + } + + spin_lock_irqsave(&hc->lock, flags); + + if (dch->timer.function) { + del_timer(&dch->timer); + dch->timer.function = NULL; + } + + if (hc->ctype == HFC_TYPE_E1) { /* E1 */ + /* remove sync */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + hc->syncronized = 0; + plxsd_checksync(hc, 1); + } + /* free channels */ + for (i = 0; i <= 31; i++) { + if (!((1 << i) & hc->bmask[pt])) /* skip unused chan */ + continue; + if (hc->chan[i].bch) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: free port %d channel %d\n", + __func__, hc->chan[i].port + 1, i); + pb = hc->chan[i].bch; + hc->chan[i].bch = NULL; + spin_unlock_irqrestore(&hc->lock, flags); + mISDN_freebchannel(pb); + kfree(pb); + kfree(hc->chan[i].coeff); + spin_lock_irqsave(&hc->lock, flags); + } + } + } else { + /* remove sync */ + if (test_bit(HFC_CHIP_PLXSD, &hc->chip)) { + hc->syncronized &= + ~(1 << hc->chan[ci].port); + plxsd_checksync(hc, 1); + } + /* free channels */ + if (hc->chan[ci - 2].bch) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: free port %d channel %d\n", + __func__, hc->chan[ci - 2].port + 1, + ci - 2); + pb = hc->chan[ci - 2].bch; + hc->chan[ci - 2].bch = NULL; + spin_unlock_irqrestore(&hc->lock, flags); + mISDN_freebchannel(pb); + kfree(pb); + kfree(hc->chan[ci - 2].coeff); + spin_lock_irqsave(&hc->lock, flags); + } + if (hc->chan[ci - 1].bch) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: free port %d channel %d\n", + __func__, hc->chan[ci - 1].port + 1, + ci - 1); + pb = hc->chan[ci - 1].bch; + hc->chan[ci - 1].bch = NULL; + spin_unlock_irqrestore(&hc->lock, flags); + mISDN_freebchannel(pb); + kfree(pb); + kfree(hc->chan[ci - 1].coeff); + spin_lock_irqsave(&hc->lock, flags); + } + } + + spin_unlock_irqrestore(&hc->lock, flags); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: free port %d channel D(%d)\n", __func__, + pt+1, ci); + mISDN_freedchannel(dch); + kfree(dch); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: done!\n", __func__); +} + +static void +release_card(struct hfc_multi *hc) +{ + u_long flags; + int ch; + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: release card (%d) entered\n", + __func__, hc->id); + + /* unregister clock source */ + if (hc->iclock) + mISDN_unregister_clock(hc->iclock); + + /* disable and free irq */ + spin_lock_irqsave(&hc->lock, flags); + disable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + udelay(1000); + if (hc->irq) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: free irq %d (hc=%p)\n", + __func__, hc->irq, hc); + free_irq(hc->irq, hc); + hc->irq = 0; + + } + + /* disable D-channels & B-channels */ + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: disable all channels (d and b)\n", + __func__); + for (ch = 0; ch <= 31; ch++) { + if (hc->chan[ch].dch) + release_port(hc, hc->chan[ch].dch); + } + + /* dimm leds */ + if (hc->leds) + hfcmulti_leds(hc); + + /* release hardware */ + release_io_hfcmulti(hc); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: remove instance from list\n", + __func__); + list_del(&hc->list); + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: delete instance\n", __func__); + if (hc == syncmaster) + syncmaster = NULL; + kfree(hc); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: card successfully removed\n", + __func__); +} + +static void +init_e1_port_hw(struct hfc_multi *hc, struct hm_map *m) +{ + /* set optical line type */ + if (port[Port_cnt] & 0x001) { + if (!m->opticalsupport) { + printk(KERN_INFO + "This board has no optical " + "support\n"); + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PORT set optical " + "interfacs: card(%d) " + "port(%d)\n", + __func__, + HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_OPTICAL, + &hc->chan[hc->dnum[0]].cfg); + } + } + /* set LOS report */ + if (port[Port_cnt] & 0x004) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT set " + "LOS report: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_REPORT_LOS, + &hc->chan[hc->dnum[0]].cfg); + } + /* set AIS report */ + if (port[Port_cnt] & 0x008) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT set " + "AIS report: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_REPORT_AIS, + &hc->chan[hc->dnum[0]].cfg); + } + /* set SLIP report */ + if (port[Port_cnt] & 0x010) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PORT set SLIP report: " + "card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_REPORT_SLIP, + &hc->chan[hc->dnum[0]].cfg); + } + /* set RDI report */ + if (port[Port_cnt] & 0x020) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PORT set RDI report: " + "card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_REPORT_RDI, + &hc->chan[hc->dnum[0]].cfg); + } + /* set CRC-4 Mode */ + if (!(port[Port_cnt] & 0x100)) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT turn on CRC4 report:" + " card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CFG_CRC4, + &hc->chan[hc->dnum[0]].cfg); + } else { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT turn off CRC4" + " report: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + } + /* set forced clock */ + if (port[Port_cnt] & 0x0200) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT force getting clock from " + "E1: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CHIP_E1CLOCK_GET, &hc->chip); + } else + if (port[Port_cnt] & 0x0400) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT force putting clock to " + "E1: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CHIP_E1CLOCK_PUT, &hc->chip); + } + /* set JATT PLL */ + if (port[Port_cnt] & 0x0800) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: PORT disable JATT PLL on " + "E1: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, 1); + test_and_set_bit(HFC_CHIP_RX_SYNC, &hc->chip); + } + /* set elastic jitter buffer */ + if (port[Port_cnt] & 0x3000) { + hc->chan[hc->dnum[0]].jitter = (port[Port_cnt]>>12) & 0x3; + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PORT set elastic " + "buffer to %d: card(%d) port(%d)\n", + __func__, hc->chan[hc->dnum[0]].jitter, + HFC_cnt + 1, 1); + } else + hc->chan[hc->dnum[0]].jitter = 2; /* default */ +} + +static int +init_e1_port(struct hfc_multi *hc, struct hm_map *m, int pt) +{ + struct dchannel *dch; + struct bchannel *bch; + int ch, ret = 0; + char name[MISDN_MAX_IDLEN]; + int bcount = 0; + + dch = kzalloc(sizeof(struct dchannel), GFP_KERNEL); + if (!dch) + return -ENOMEM; + dch->debug = debug; + mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, ph_state_change); + dch->hw = hc; + dch->dev.Dprotocols = (1 << ISDN_P_TE_E1) | (1 << ISDN_P_NT_E1); + dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); + dch->dev.D.send = handle_dmsg; + dch->dev.D.ctrl = hfcm_dctrl; + dch->slot = hc->dnum[pt]; + hc->chan[hc->dnum[pt]].dch = dch; + hc->chan[hc->dnum[pt]].port = pt; + hc->chan[hc->dnum[pt]].nt_timer = -1; + for (ch = 1; ch <= 31; ch++) { + if (!((1 << ch) & hc->bmask[pt])) /* skip unused channel */ + continue; + bch = kzalloc(sizeof(struct bchannel), GFP_KERNEL); + if (!bch) { + printk(KERN_ERR "%s: no memory for bchannel\n", + __func__); + ret = -ENOMEM; + goto free_chan; + } + hc->chan[ch].coeff = kzalloc(512, GFP_KERNEL); + if (!hc->chan[ch].coeff) { + printk(KERN_ERR "%s: no memory for coeffs\n", + __func__); + ret = -ENOMEM; + kfree(bch); + goto free_chan; + } + bch->nr = ch; + bch->slot = ch; + bch->debug = debug; + mISDN_initbchannel(bch, MAX_DATA_MEM, poll >> 1); + bch->hw = hc; + bch->ch.send = handle_bmsg; + bch->ch.ctrl = hfcm_bctrl; + bch->ch.nr = ch; + list_add(&bch->ch.list, &dch->dev.bchannels); + hc->chan[ch].bch = bch; + hc->chan[ch].port = pt; + set_channelmap(bch->nr, dch->dev.channelmap); + bcount++; + } + dch->dev.nrbchan = bcount; + if (pt == 0) + init_e1_port_hw(hc, m); + if (hc->ports > 1) + snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-e1.%d-%d", + HFC_cnt + 1, pt+1); + else + snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-e1.%d", HFC_cnt + 1); + ret = mISDN_register_device(&dch->dev, &hc->pci_dev->dev, name); + if (ret) + goto free_chan; + hc->created[pt] = 1; + return ret; +free_chan: + release_port(hc, dch); + return ret; +} + +static int +init_multi_port(struct hfc_multi *hc, int pt) +{ + struct dchannel *dch; + struct bchannel *bch; + int ch, i, ret = 0; + char name[MISDN_MAX_IDLEN]; + + dch = kzalloc(sizeof(struct dchannel), GFP_KERNEL); + if (!dch) + return -ENOMEM; + dch->debug = debug; + mISDN_initdchannel(dch, MAX_DFRAME_LEN_L1, ph_state_change); + dch->hw = hc; + dch->dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0); + dch->dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); + dch->dev.D.send = handle_dmsg; + dch->dev.D.ctrl = hfcm_dctrl; + dch->dev.nrbchan = 2; + i = pt << 2; + dch->slot = i + 2; + hc->chan[i + 2].dch = dch; + hc->chan[i + 2].port = pt; + hc->chan[i + 2].nt_timer = -1; + for (ch = 0; ch < dch->dev.nrbchan; ch++) { + bch = kzalloc(sizeof(struct bchannel), GFP_KERNEL); + if (!bch) { + printk(KERN_ERR "%s: no memory for bchannel\n", + __func__); + ret = -ENOMEM; + goto free_chan; + } + hc->chan[i + ch].coeff = kzalloc(512, GFP_KERNEL); + if (!hc->chan[i + ch].coeff) { + printk(KERN_ERR "%s: no memory for coeffs\n", + __func__); + ret = -ENOMEM; + kfree(bch); + goto free_chan; + } + bch->nr = ch + 1; + bch->slot = i + ch; + bch->debug = debug; + mISDN_initbchannel(bch, MAX_DATA_MEM, poll >> 1); + bch->hw = hc; + bch->ch.send = handle_bmsg; + bch->ch.ctrl = hfcm_bctrl; + bch->ch.nr = ch + 1; + list_add(&bch->ch.list, &dch->dev.bchannels); + hc->chan[i + ch].bch = bch; + hc->chan[i + ch].port = pt; + set_channelmap(bch->nr, dch->dev.channelmap); + } + /* set master clock */ + if (port[Port_cnt] & 0x001) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PROTOCOL set master clock: " + "card(%d) port(%d)\n", + __func__, HFC_cnt + 1, pt + 1); + if (dch->dev.D.protocol != ISDN_P_TE_S0) { + printk(KERN_ERR "Error: Master clock " + "for port(%d) of card(%d) is only" + " possible with TE-mode\n", + pt + 1, HFC_cnt + 1); + ret = -EINVAL; + goto free_chan; + } + if (hc->masterclk >= 0) { + printk(KERN_ERR "Error: Master clock " + "for port(%d) of card(%d) already " + "defined for port(%d)\n", + pt + 1, HFC_cnt + 1, hc->masterclk + 1); + ret = -EINVAL; + goto free_chan; + } + hc->masterclk = pt; + } + /* set transmitter line to non capacitive */ + if (port[Port_cnt] & 0x002) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PROTOCOL set non capacitive " + "transmitter: card(%d) port(%d)\n", + __func__, HFC_cnt + 1, pt + 1); + test_and_set_bit(HFC_CFG_NONCAP_TX, + &hc->chan[i + 2].cfg); + } + /* disable E-channel */ + if (port[Port_cnt] & 0x004) { + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: PROTOCOL disable E-channel: " + "card(%d) port(%d)\n", + __func__, HFC_cnt + 1, pt + 1); + test_and_set_bit(HFC_CFG_DIS_ECHANNEL, + &hc->chan[i + 2].cfg); + } + if (hc->ctype == HFC_TYPE_XHFC) { + snprintf(name, MISDN_MAX_IDLEN - 1, "xhfc.%d-%d", + HFC_cnt + 1, pt + 1); + ret = mISDN_register_device(&dch->dev, NULL, name); + } else { + snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-%ds.%d-%d", + hc->ctype, HFC_cnt + 1, pt + 1); + ret = mISDN_register_device(&dch->dev, &hc->pci_dev->dev, name); + } + if (ret) + goto free_chan; + hc->created[pt] = 1; + return ret; +free_chan: + release_port(hc, dch); + return ret; +} + +static int +hfcmulti_init(struct hm_map *m, struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + int ret_err = 0; + int pt; + struct hfc_multi *hc; + u_long flags; + u_char dips = 0, pmj = 0; /* dip settings, port mode Jumpers */ + int i, ch; + u_int maskcheck; + + if (HFC_cnt >= MAX_CARDS) { + printk(KERN_ERR "too many cards (max=%d).\n", + MAX_CARDS); + return -EINVAL; + } + if ((type[HFC_cnt] & 0xff) && (type[HFC_cnt] & 0xff) != m->type) { + printk(KERN_WARNING "HFC-MULTI: Card '%s:%s' type %d found but " + "type[%d] %d was supplied as module parameter\n", + m->vendor_name, m->card_name, m->type, HFC_cnt, + type[HFC_cnt] & 0xff); + printk(KERN_WARNING "HFC-MULTI: Load module without parameters " + "first, to see cards and their types."); + return -EINVAL; + } + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: Registering %s:%s chip type %d (0x%x)\n", + __func__, m->vendor_name, m->card_name, m->type, + type[HFC_cnt]); + + /* allocate card+fifo structure */ + hc = kzalloc(sizeof(struct hfc_multi), GFP_KERNEL); + if (!hc) { + printk(KERN_ERR "No kmem for HFC-Multi card\n"); + return -ENOMEM; + } + spin_lock_init(&hc->lock); + hc->mtyp = m; + hc->ctype = m->type; + hc->ports = m->ports; + hc->id = HFC_cnt; + hc->pcm = pcm[HFC_cnt]; + hc->io_mode = iomode[HFC_cnt]; + if (hc->ctype == HFC_TYPE_E1 && dmask[E1_cnt]) { + /* fragment card */ + pt = 0; + maskcheck = 0; + for (ch = 0; ch <= 31; ch++) { + if (!((1 << ch) & dmask[E1_cnt])) + continue; + hc->dnum[pt] = ch; + hc->bmask[pt] = bmask[bmask_cnt++]; + if ((maskcheck & hc->bmask[pt]) + || (dmask[E1_cnt] & hc->bmask[pt])) { + printk(KERN_INFO + "HFC-E1 #%d has overlapping B-channels on fragment #%d\n", + E1_cnt + 1, pt); + kfree(hc); + return -EINVAL; + } + maskcheck |= hc->bmask[pt]; + printk(KERN_INFO + "HFC-E1 #%d uses D-channel on slot %d and a B-channel map of 0x%08x\n", + E1_cnt + 1, ch, hc->bmask[pt]); + pt++; + } + hc->ports = pt; + } + if (hc->ctype == HFC_TYPE_E1 && !dmask[E1_cnt]) { + /* default card layout */ + hc->dnum[0] = 16; + hc->bmask[0] = 0xfffefffe; + hc->ports = 1; + } + + /* set chip specific features */ + hc->masterclk = -1; + if (type[HFC_cnt] & 0x100) { + test_and_set_bit(HFC_CHIP_ULAW, &hc->chip); + hc->silence = 0xff; /* ulaw silence */ + } else + hc->silence = 0x2a; /* alaw silence */ + if ((poll >> 1) > sizeof(hc->silence_data)) { + printk(KERN_ERR "HFCMULTI error: silence_data too small, " + "please fix\n"); + kfree(hc); + return -EINVAL; + } + for (i = 0; i < (poll >> 1); i++) + hc->silence_data[i] = hc->silence; + + if (hc->ctype != HFC_TYPE_XHFC) { + if (!(type[HFC_cnt] & 0x200)) + test_and_set_bit(HFC_CHIP_DTMF, &hc->chip); + test_and_set_bit(HFC_CHIP_CONF, &hc->chip); + } + + if (type[HFC_cnt] & 0x800) + test_and_set_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); + if (type[HFC_cnt] & 0x1000) { + test_and_set_bit(HFC_CHIP_PCM_MASTER, &hc->chip); + test_and_clear_bit(HFC_CHIP_PCM_SLAVE, &hc->chip); + } + if (type[HFC_cnt] & 0x4000) + test_and_set_bit(HFC_CHIP_EXRAM_128, &hc->chip); + if (type[HFC_cnt] & 0x8000) + test_and_set_bit(HFC_CHIP_EXRAM_512, &hc->chip); + hc->slots = 32; + if (type[HFC_cnt] & 0x10000) + hc->slots = 64; + if (type[HFC_cnt] & 0x20000) + hc->slots = 128; + if (type[HFC_cnt] & 0x80000) { + test_and_set_bit(HFC_CHIP_WATCHDOG, &hc->chip); + hc->wdcount = 0; + hc->wdbyte = V_GPIO_OUT2; + printk(KERN_NOTICE "Watchdog enabled\n"); + } + + if (pdev && ent) + /* setup pci, hc->slots may change due to PLXSD */ + ret_err = setup_pci(hc, pdev, ent); + else +#ifdef CONFIG_MISDN_HFCMULTI_8xx + ret_err = setup_embedded(hc, m); +#else + { + printk(KERN_WARNING "Embedded IO Mode not selected\n"); + ret_err = -EIO; + } +#endif + if (ret_err) { + if (hc == syncmaster) + syncmaster = NULL; + kfree(hc); + return ret_err; + } + + hc->HFC_outb_nodebug = hc->HFC_outb; + hc->HFC_inb_nodebug = hc->HFC_inb; + hc->HFC_inw_nodebug = hc->HFC_inw; + hc->HFC_wait_nodebug = hc->HFC_wait; +#ifdef HFC_REGISTER_DEBUG + hc->HFC_outb = HFC_outb_debug; + hc->HFC_inb = HFC_inb_debug; + hc->HFC_inw = HFC_inw_debug; + hc->HFC_wait = HFC_wait_debug; +#endif + /* create channels */ + for (pt = 0; pt < hc->ports; pt++) { + if (Port_cnt >= MAX_PORTS) { + printk(KERN_ERR "too many ports (max=%d).\n", + MAX_PORTS); + ret_err = -EINVAL; + goto free_card; + } + if (hc->ctype == HFC_TYPE_E1) + ret_err = init_e1_port(hc, m, pt); + else + ret_err = init_multi_port(hc, pt); + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG + "%s: Registering D-channel, card(%d) port(%d) " + "result %d\n", + __func__, HFC_cnt + 1, pt + 1, ret_err); + + if (ret_err) { + while (pt) { /* release already registered ports */ + pt--; + if (hc->ctype == HFC_TYPE_E1) + release_port(hc, + hc->chan[hc->dnum[pt]].dch); + else + release_port(hc, + hc->chan[(pt << 2) + 2].dch); + } + goto free_card; + } + if (hc->ctype != HFC_TYPE_E1) + Port_cnt++; /* for each S0 port */ + } + if (hc->ctype == HFC_TYPE_E1) { + Port_cnt++; /* for each E1 port */ + E1_cnt++; + } + + /* disp switches */ + switch (m->dip_type) { + case DIP_4S: + /* + * Get DIP setting for beroNet 1S/2S/4S cards + * DIP Setting: (collect GPIO 13/14/15 (R_GPIO_IN1) + + * GPI 19/23 (R_GPI_IN2)) + */ + dips = ((~HFC_inb(hc, R_GPIO_IN1) & 0xE0) >> 5) | + ((~HFC_inb(hc, R_GPI_IN2) & 0x80) >> 3) | + (~HFC_inb(hc, R_GPI_IN2) & 0x08); + + /* Port mode (TE/NT) jumpers */ + pmj = ((HFC_inb(hc, R_GPI_IN3) >> 4) & 0xf); + + if (test_bit(HFC_CHIP_B410P, &hc->chip)) + pmj = ~pmj & 0xf; + + printk(KERN_INFO "%s: %s DIPs(0x%x) jumpers(0x%x)\n", + m->vendor_name, m->card_name, dips, pmj); + break; + case DIP_8S: + /* + * Get DIP Setting for beroNet 8S0+ cards + * Enable PCI auxbridge function + */ + HFC_outb(hc, R_BRG_PCM_CFG, 1 | V_PCM_CLK); + /* prepare access to auxport */ + outw(0x4000, hc->pci_iobase + 4); + /* + * some dummy reads are required to + * read valid DIP switch data + */ + dips = inb(hc->pci_iobase); + dips = inb(hc->pci_iobase); + dips = inb(hc->pci_iobase); + dips = ~inb(hc->pci_iobase) & 0x3F; + outw(0x0, hc->pci_iobase + 4); + /* disable PCI auxbridge function */ + HFC_outb(hc, R_BRG_PCM_CFG, V_PCM_CLK); + printk(KERN_INFO "%s: %s DIPs(0x%x)\n", + m->vendor_name, m->card_name, dips); + break; + case DIP_E1: + /* + * get DIP Setting for beroNet E1 cards + * DIP Setting: collect GPI 4/5/6/7 (R_GPI_IN0) + */ + dips = (~HFC_inb(hc, R_GPI_IN0) & 0xF0) >> 4; + printk(KERN_INFO "%s: %s DIPs(0x%x)\n", + m->vendor_name, m->card_name, dips); + break; + } + + /* add to list */ + spin_lock_irqsave(&HFClock, flags); + list_add_tail(&hc->list, &HFClist); + spin_unlock_irqrestore(&HFClock, flags); + + /* use as clock source */ + if (clock == HFC_cnt + 1) + hc->iclock = mISDN_register_clock("HFCMulti", 0, clockctl, hc); + + /* initialize hardware */ + hc->irq = (m->irq) ? : hc->pci_dev->irq; + ret_err = init_card(hc); + if (ret_err) { + printk(KERN_ERR "init card returns %d\n", ret_err); + release_card(hc); + return ret_err; + } + + /* start IRQ and return */ + spin_lock_irqsave(&hc->lock, flags); + enable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + return 0; + +free_card: + release_io_hfcmulti(hc); + if (hc == syncmaster) + syncmaster = NULL; + kfree(hc); + return ret_err; +} + +static void hfc_remove_pci(struct pci_dev *pdev) +{ + struct hfc_multi *card = pci_get_drvdata(pdev); + u_long flags; + + if (debug) + printk(KERN_INFO "removing hfc_multi card vendor:%x " + "device:%x subvendor:%x subdevice:%x\n", + pdev->vendor, pdev->device, + pdev->subsystem_vendor, pdev->subsystem_device); + + if (card) { + spin_lock_irqsave(&HFClock, flags); + release_card(card); + spin_unlock_irqrestore(&HFClock, flags); + } else { + if (debug) + printk(KERN_DEBUG "%s: drvdata already removed\n", + __func__); + } +} + +#define VENDOR_CCD "Cologne Chip AG" +#define VENDOR_BN "beroNet GmbH" +#define VENDOR_DIG "Digium Inc." +#define VENDOR_JH "Junghanns.NET GmbH" +#define VENDOR_PRIM "PrimuX" + +static const struct hm_map hfcm_map[] = { + /*0*/ {VENDOR_BN, "HFC-1S Card (mini PCI)", 4, 1, 1, 3, 0, DIP_4S, 0, 0}, + /*1*/ {VENDOR_BN, "HFC-2S Card", 4, 2, 1, 3, 0, DIP_4S, 0, 0}, + /*2*/ {VENDOR_BN, "HFC-2S Card (mini PCI)", 4, 2, 1, 3, 0, DIP_4S, 0, 0}, + /*3*/ {VENDOR_BN, "HFC-4S Card", 4, 4, 1, 2, 0, DIP_4S, 0, 0}, + /*4*/ {VENDOR_BN, "HFC-4S Card (mini PCI)", 4, 4, 1, 2, 0, 0, 0, 0}, + /*5*/ {VENDOR_CCD, "HFC-4S Eval (old)", 4, 4, 0, 0, 0, 0, 0, 0}, + /*6*/ {VENDOR_CCD, "HFC-4S IOB4ST", 4, 4, 1, 2, 0, DIP_4S, 0, 0}, + /*7*/ {VENDOR_CCD, "HFC-4S", 4, 4, 1, 2, 0, 0, 0, 0}, + /*8*/ {VENDOR_DIG, "HFC-4S Card", 4, 4, 0, 2, 0, 0, HFC_IO_MODE_REGIO, 0}, + /*9*/ {VENDOR_CCD, "HFC-4S Swyx 4xS0 SX2 QuadBri", 4, 4, 1, 2, 0, 0, 0, 0}, + /*10*/ {VENDOR_JH, "HFC-4S (junghanns 2.0)", 4, 4, 1, 2, 0, 0, 0, 0}, + /*11*/ {VENDOR_PRIM, "HFC-2S Primux Card", 4, 2, 0, 0, 0, 0, 0, 0}, + + /*12*/ {VENDOR_BN, "HFC-8S Card", 8, 8, 1, 0, 0, 0, 0, 0}, + /*13*/ {VENDOR_BN, "HFC-8S Card (+)", 8, 8, 1, 8, 0, DIP_8S, + HFC_IO_MODE_REGIO, 0}, + /*14*/ {VENDOR_CCD, "HFC-8S Eval (old)", 8, 8, 0, 0, 0, 0, 0, 0}, + /*15*/ {VENDOR_CCD, "HFC-8S IOB4ST Recording", 8, 8, 1, 0, 0, 0, 0, 0}, + + /*16*/ {VENDOR_CCD, "HFC-8S IOB8ST", 8, 8, 1, 0, 0, 0, 0, 0}, + /*17*/ {VENDOR_CCD, "HFC-8S", 8, 8, 1, 0, 0, 0, 0, 0}, + /*18*/ {VENDOR_CCD, "HFC-8S", 8, 8, 1, 0, 0, 0, 0, 0}, + + /*19*/ {VENDOR_BN, "HFC-E1 Card", 1, 1, 0, 1, 0, DIP_E1, 0, 0}, + /*20*/ {VENDOR_BN, "HFC-E1 Card (mini PCI)", 1, 1, 0, 1, 0, 0, 0, 0}, + /*21*/ {VENDOR_BN, "HFC-E1+ Card (Dual)", 1, 1, 0, 1, 0, DIP_E1, 0, 0}, + /*22*/ {VENDOR_BN, "HFC-E1 Card (Dual)", 1, 1, 0, 1, 0, DIP_E1, 0, 0}, + + /*23*/ {VENDOR_CCD, "HFC-E1 Eval (old)", 1, 1, 0, 0, 0, 0, 0, 0}, + /*24*/ {VENDOR_CCD, "HFC-E1 IOB1E1", 1, 1, 0, 1, 0, 0, 0, 0}, + /*25*/ {VENDOR_CCD, "HFC-E1", 1, 1, 0, 1, 0, 0, 0, 0}, + + /*26*/ {VENDOR_CCD, "HFC-4S Speech Design", 4, 4, 0, 0, 0, 0, + HFC_IO_MODE_PLXSD, 0}, + /*27*/ {VENDOR_CCD, "HFC-E1 Speech Design", 1, 1, 0, 0, 0, 0, + HFC_IO_MODE_PLXSD, 0}, + /*28*/ {VENDOR_CCD, "HFC-4S OpenVox", 4, 4, 1, 0, 0, 0, 0, 0}, + /*29*/ {VENDOR_CCD, "HFC-2S OpenVox", 4, 2, 1, 0, 0, 0, 0, 0}, + /*30*/ {VENDOR_CCD, "HFC-8S OpenVox", 8, 8, 1, 0, 0, 0, 0, 0}, + /*31*/ {VENDOR_CCD, "XHFC-4S Speech Design", 5, 4, 0, 0, 0, 0, + HFC_IO_MODE_EMBSD, XHFC_IRQ}, + /*32*/ {VENDOR_JH, "HFC-8S (junghanns)", 8, 8, 1, 0, 0, 0, 0, 0}, + /*33*/ {VENDOR_BN, "HFC-2S Beronet Card PCIe", 4, 2, 1, 3, 0, DIP_4S, 0, 0}, + /*34*/ {VENDOR_BN, "HFC-4S Beronet Card PCIe", 4, 4, 1, 2, 0, DIP_4S, 0, 0}, +}; + +#undef H +#define H(x) ((unsigned long)&hfcm_map[x]) +static const struct pci_device_id hfmultipci_ids[] = { + + /* Cards with HFC-4S Chip */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN1SM, 0, 0, H(0)}, /* BN1S mini PCI */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN2S, 0, 0, H(1)}, /* BN2S */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN2SM, 0, 0, H(2)}, /* BN2S mini PCI */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN4S, 0, 0, H(3)}, /* BN4S */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN4SM, 0, 0, H(4)}, /* BN4S mini PCI */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_DEVICE_ID_CCD_HFC4S, 0, 0, H(5)}, /* Old Eval */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_IOB4ST, 0, 0, H(6)}, /* IOB4ST */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_HFC4S, 0, 0, H(7)}, /* 4S */ + { PCI_VENDOR_ID_DIGIUM, PCI_DEVICE_ID_DIGIUM_HFC4S, + PCI_VENDOR_ID_DIGIUM, PCI_DEVICE_ID_DIGIUM_HFC4S, 0, 0, H(8)}, + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_SWYX4S, 0, 0, H(9)}, /* 4S Swyx */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_JH4S20, 0, 0, H(10)}, + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_PMX2S, 0, 0, H(11)}, /* Primux */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_OV4S, 0, 0, H(28)}, /* OpenVox 4 */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_OV2S, 0, 0, H(29)}, /* OpenVox 2 */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + 0xb761, 0, 0, H(33)}, /* BN2S PCIe */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC4S, PCI_VENDOR_ID_CCD, + 0xb762, 0, 0, H(34)}, /* BN4S PCIe */ + + /* Cards with HFC-8S Chip */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN8S, 0, 0, H(12)}, /* BN8S */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BN8SP, 0, 0, H(13)}, /* BN8S+ */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_DEVICE_ID_CCD_HFC8S, 0, 0, H(14)}, /* old Eval */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_IOB8STR, 0, 0, H(15)}, /* IOB8ST Recording */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_IOB8ST, 0, 0, H(16)}, /* IOB8ST */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_IOB8ST_1, 0, 0, H(17)}, /* IOB8ST */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_HFC8S, 0, 0, H(18)}, /* 8S */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_OV8S, 0, 0, H(30)}, /* OpenVox 8 */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFC8S, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_JH8S, 0, 0, H(32)}, /* Junganns 8S */ + + + /* Cards with HFC-E1 Chip */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BNE1, 0, 0, H(19)}, /* BNE1 */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BNE1M, 0, 0, H(20)}, /* BNE1 mini PCI */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BNE1DP, 0, 0, H(21)}, /* BNE1 + (Dual) */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_BNE1D, 0, 0, H(22)}, /* BNE1 (Dual) */ + + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_DEVICE_ID_CCD_HFCE1, 0, 0, H(23)}, /* Old Eval */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_IOB1E1, 0, 0, H(24)}, /* IOB1E1 */ + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_HFCE1, 0, 0, H(25)}, /* E1 */ + + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_SPD4S, 0, 0, H(26)}, /* PLX PCI Bridge */ + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9030, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_SPDE1, 0, 0, H(27)}, /* PLX PCI Bridge */ + + { PCI_VENDOR_ID_CCD, PCI_DEVICE_ID_CCD_HFCE1, PCI_VENDOR_ID_CCD, + PCI_SUBDEVICE_ID_CCD_JHSE1, 0, 0, H(25)}, /* Junghanns E1 */ + + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_HFC4S), 0 }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_HFC8S), 0 }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_HFCE1), 0 }, + {0, } +}; +#undef H + +MODULE_DEVICE_TABLE(pci, hfmultipci_ids); + +static int +hfcmulti_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + struct hm_map *m = (struct hm_map *)ent->driver_data; + int ret; + + if (m == NULL && ent->vendor == PCI_VENDOR_ID_CCD && ( + ent->device == PCI_DEVICE_ID_CCD_HFC4S || + ent->device == PCI_DEVICE_ID_CCD_HFC8S || + ent->device == PCI_DEVICE_ID_CCD_HFCE1)) { + printk(KERN_ERR + "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; + } + ret = hfcmulti_init(m, pdev, ent); + if (ret) + return ret; + HFC_cnt++; + printk(KERN_INFO "%d devices registered\n", HFC_cnt); + return 0; +} + +static struct pci_driver hfcmultipci_driver = { + .name = "hfc_multi", + .probe = hfcmulti_probe, + .remove = hfc_remove_pci, + .id_table = hfmultipci_ids, +}; + +static void __exit +HFCmulti_cleanup(void) +{ + struct hfc_multi *card, *next; + + /* get rid of all devices of this driver */ + list_for_each_entry_safe(card, next, &HFClist, list) + release_card(card); + pci_unregister_driver(&hfcmultipci_driver); +} + +static int __init +HFCmulti_init(void) +{ + int err; + int i, xhfc = 0; + struct hm_map m; + + printk(KERN_INFO "mISDN: HFC-multi driver %s\n", HFC_MULTI_VERSION); + +#ifdef IRQ_DEBUG + printk(KERN_DEBUG "%s: IRQ_DEBUG IS ENABLED!\n", __func__); +#endif + + if (debug & DEBUG_HFCMULTI_INIT) + printk(KERN_DEBUG "%s: init entered\n", __func__); + + switch (poll) { + case 0: + poll_timer = 6; + poll = 128; + break; + case 8: + poll_timer = 2; + break; + case 16: + poll_timer = 3; + break; + case 32: + poll_timer = 4; + break; + case 64: + poll_timer = 5; + break; + case 128: + poll_timer = 6; + break; + case 256: + poll_timer = 7; + break; + default: + printk(KERN_ERR + "%s: Wrong poll value (%d).\n", __func__, poll); + err = -EINVAL; + return err; + + } + + if (!clock) + clock = 1; + + /* Register the embedded devices. + * This should be done before the PCI cards registration */ + switch (hwid) { + case HWID_MINIP4: + xhfc = 1; + m = hfcm_map[31]; + break; + case HWID_MINIP8: + xhfc = 2; + m = hfcm_map[31]; + break; + case HWID_MINIP16: + xhfc = 4; + m = hfcm_map[31]; + break; + default: + xhfc = 0; + } + + for (i = 0; i < xhfc; ++i) { + err = hfcmulti_init(&m, NULL, NULL); + if (err) { + printk(KERN_ERR "error registering embedded driver: " + "%x\n", err); + return err; + } + HFC_cnt++; + printk(KERN_INFO "%d devices registered\n", HFC_cnt); + } + + /* Register the PCI cards */ + err = pci_register_driver(&hfcmultipci_driver); + if (err < 0) { + printk(KERN_ERR "error registering pci driver: %x\n", err); + return err; + } + + return 0; +} + + +module_init(HFCmulti_init); +module_exit(HFCmulti_cleanup); diff --git a/drivers/isdn/hardware/mISDN/hfcpci.c b/drivers/isdn/hardware/mISDN/hfcpci.c new file mode 100644 index 000000000..fe391de1a --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfcpci.c @@ -0,0 +1,2361 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * + * hfcpci.c low level driver for CCD's hfc-pci based cards + * + * Author Werner Cornelius (werner@isdn4linux.de) + * based on existing driver for CCD hfc ISA cards + * type approval valid for HFC-S PCI A based card + * + * Copyright 1999 by Werner Cornelius (werner@isdn-development.de) + * Copyright 2008 by Karsten Keil <kkeil@novell.com> + * + * Module options: + * + * debug: + * NOTE: only one poll value must be given for all cards + * See hfc_pci.h for debug flags. + * + * poll: + * NOTE: only one poll value must be given for all cards + * Give the number of samples for each fifo process. + * By default 128 is used. Decrease to reduce delay, increase to + * reduce cpu load. If unsure, don't mess with it! + * A value of 128 will use controller's interrupt. Other values will + * use kernel timer, because the controller will not allow lower values + * than 128. + * Also note that the value depends on the kernel timer frequency. + * If kernel uses a frequency of 1000 Hz, steps of 8 samples are possible. + * If the kernel uses 100 Hz, steps of 80 samples are possible. + * If the kernel uses 300 Hz, steps of about 26 samples are possible. + */ + +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/mISDNhw.h> +#include <linux/slab.h> + +#include "hfc_pci.h" + +static const char *hfcpci_revision = "2.0"; + +static int HFC_cnt; +static uint debug; +static uint poll, tics; +static struct timer_list hfc_tl; +static unsigned long hfc_jiffies; + +MODULE_AUTHOR("Karsten Keil"); +MODULE_LICENSE("GPL"); +module_param(debug, uint, S_IRUGO | S_IWUSR); +module_param(poll, uint, S_IRUGO | S_IWUSR); + +enum { + HFC_CCD_2BD0, + HFC_CCD_B000, + HFC_CCD_B006, + HFC_CCD_B007, + HFC_CCD_B008, + HFC_CCD_B009, + HFC_CCD_B00A, + HFC_CCD_B00B, + HFC_CCD_B00C, + HFC_CCD_B100, + HFC_CCD_B700, + HFC_CCD_B701, + HFC_ASUS_0675, + HFC_BERKOM_A1T, + HFC_BERKOM_TCONCEPT, + HFC_ANIGMA_MC145575, + HFC_ZOLTRIX_2BD0, + HFC_DIGI_DF_M_IOM2_E, + HFC_DIGI_DF_M_E, + HFC_DIGI_DF_M_IOM2_A, + HFC_DIGI_DF_M_A, + HFC_ABOCOM_2BD1, + HFC_SITECOM_DC105V2, +}; + +struct hfcPCI_hw { + unsigned char cirm; + unsigned char ctmt; + unsigned char clkdel; + unsigned char states; + unsigned char conn; + unsigned char mst_m; + unsigned char int_m1; + unsigned char int_m2; + unsigned char sctrl; + unsigned char sctrl_r; + unsigned char sctrl_e; + unsigned char trm; + unsigned char fifo_en; + unsigned char bswapped; + unsigned char protocol; + int nt_timer; + unsigned char __iomem *pci_io; /* start of PCI IO memory */ + dma_addr_t dmahandle; + void *fifos; /* FIFO memory */ + int last_bfifo_cnt[2]; + /* marker saving last b-fifo frame count */ + struct timer_list timer; +}; + +#define HFC_CFG_MASTER 1 +#define HFC_CFG_SLAVE 2 +#define HFC_CFG_PCM 3 +#define HFC_CFG_2HFC 4 +#define HFC_CFG_SLAVEHFC 5 +#define HFC_CFG_NEG_F0 6 +#define HFC_CFG_SW_DD_DU 7 + +#define FLG_HFC_TIMER_T1 16 +#define FLG_HFC_TIMER_T3 17 + +#define NT_T1_COUNT 1120 /* number of 3.125ms interrupts (3.5s) */ +#define NT_T3_COUNT 31 /* number of 3.125ms interrupts (97 ms) */ +#define CLKDEL_TE 0x0e /* CLKDEL in TE mode */ +#define CLKDEL_NT 0x6c /* CLKDEL in NT mode */ + + +struct hfc_pci { + u_char subtype; + u_char chanlimit; + u_char initdone; + u_long cfg; + u_int irq; + u_int irqcnt; + struct pci_dev *pdev; + struct hfcPCI_hw hw; + spinlock_t lock; /* card lock */ + struct dchannel dch; + struct bchannel bch[2]; +}; + +/* Interface functions */ +static void +enable_hwirq(struct hfc_pci *hc) +{ + hc->hw.int_m2 |= HFCPCI_IRQ_ENABLE; + Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2); +} + +static void +disable_hwirq(struct hfc_pci *hc) +{ + hc->hw.int_m2 &= ~((u_char)HFCPCI_IRQ_ENABLE); + Write_hfc(hc, HFCPCI_INT_M2, hc->hw.int_m2); +} + +/* + * free hardware resources used by driver + */ +static void +release_io_hfcpci(struct hfc_pci *hc) +{ + /* disable memory mapped ports + busmaster */ + pci_write_config_word(hc->pdev, PCI_COMMAND, 0); + del_timer(&hc->hw.timer); + dma_free_coherent(&hc->pdev->dev, 0x8000, hc->hw.fifos, + hc->hw.dmahandle); + iounmap(hc->hw.pci_io); +} + +/* + * set mode (NT or TE) + */ +static void +hfcpci_setmode(struct hfc_pci *hc) +{ + if (hc->hw.protocol == ISDN_P_NT_S0) { + hc->hw.clkdel = CLKDEL_NT; /* ST-Bit delay for NT-Mode */ + hc->hw.sctrl |= SCTRL_MODE_NT; /* NT-MODE */ + hc->hw.states = 1; /* G1 */ + } else { + hc->hw.clkdel = CLKDEL_TE; /* ST-Bit delay for TE-Mode */ + hc->hw.sctrl &= ~SCTRL_MODE_NT; /* TE-MODE */ + hc->hw.states = 2; /* F2 */ + } + Write_hfc(hc, HFCPCI_CLKDEL, hc->hw.clkdel); + Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | hc->hw.states); + udelay(10); + Write_hfc(hc, HFCPCI_STATES, hc->hw.states | 0x40); /* Deactivate */ + Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl); +} + +/* + * function called to reset the HFC PCI chip. A complete software reset of chip + * and fifos is done. + */ +static void +reset_hfcpci(struct hfc_pci *hc) +{ + u_char val; + int cnt = 0; + + printk(KERN_DEBUG "reset_hfcpci: entered\n"); + val = Read_hfc(hc, HFCPCI_CHIP_ID); + printk(KERN_INFO "HFC_PCI: resetting HFC ChipId(%x)\n", val); + /* enable memory mapped ports, disable busmaster */ + pci_write_config_word(hc->pdev, PCI_COMMAND, PCI_ENA_MEMIO); + disable_hwirq(hc); + /* enable memory ports + busmaster */ + pci_write_config_word(hc->pdev, PCI_COMMAND, + PCI_ENA_MEMIO + PCI_ENA_MASTER); + val = Read_hfc(hc, HFCPCI_STATUS); + printk(KERN_DEBUG "HFC-PCI status(%x) before reset\n", val); + hc->hw.cirm = HFCPCI_RESET; /* Reset On */ + Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); + set_current_state(TASK_UNINTERRUPTIBLE); + mdelay(10); /* Timeout 10ms */ + hc->hw.cirm = 0; /* Reset Off */ + Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); + val = Read_hfc(hc, HFCPCI_STATUS); + printk(KERN_DEBUG "HFC-PCI status(%x) after reset\n", val); + while (cnt < 50000) { /* max 50000 us */ + udelay(5); + cnt += 5; + val = Read_hfc(hc, HFCPCI_STATUS); + if (!(val & 2)) + break; + } + printk(KERN_DEBUG "HFC-PCI status(%x) after %dus\n", val, cnt); + + hc->hw.fifo_en = 0x30; /* only D fifos enabled */ + + hc->hw.bswapped = 0; /* no exchange */ + hc->hw.ctmt = HFCPCI_TIM3_125 | HFCPCI_AUTO_TIMER; + hc->hw.trm = HFCPCI_BTRANS_THRESMASK; /* no echo connect , threshold */ + hc->hw.sctrl = 0x40; /* set tx_lo mode, error in datasheet ! */ + hc->hw.sctrl_r = 0; + hc->hw.sctrl_e = HFCPCI_AUTO_AWAKE; /* S/T Auto awake */ + hc->hw.mst_m = 0; + if (test_bit(HFC_CFG_MASTER, &hc->cfg)) + hc->hw.mst_m |= HFCPCI_MASTER; /* HFC Master Mode */ + if (test_bit(HFC_CFG_NEG_F0, &hc->cfg)) + hc->hw.mst_m |= HFCPCI_F0_NEGATIV; + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); + Write_hfc(hc, HFCPCI_TRM, hc->hw.trm); + Write_hfc(hc, HFCPCI_SCTRL_E, hc->hw.sctrl_e); + Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt); + + hc->hw.int_m1 = HFCPCI_INTS_DTRANS | HFCPCI_INTS_DREC | + HFCPCI_INTS_L1STATE | HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + + /* Clear already pending ints */ + val = Read_hfc(hc, HFCPCI_INT_S1); + + /* set NT/TE mode */ + hfcpci_setmode(hc); + + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r); + + /* + * Init GCI/IOM2 in master mode + * Slots 0 and 1 are set for B-chan 1 and 2 + * D- and monitor/CI channel are not enabled + * STIO1 is used as output for data, B1+B2 from ST->IOM+HFC + * STIO2 is used as data input, B1+B2 from IOM->ST + * ST B-channel send disabled -> continuous 1s + * The IOM slots are always enabled + */ + if (test_bit(HFC_CFG_PCM, &hc->cfg)) { + /* set data flow directions: connect B1,B2: HFC to/from PCM */ + hc->hw.conn = 0x09; + } else { + hc->hw.conn = 0x36; /* set data flow directions */ + if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) { + Write_hfc(hc, HFCPCI_B1_SSL, 0xC0); + Write_hfc(hc, HFCPCI_B2_SSL, 0xC1); + Write_hfc(hc, HFCPCI_B1_RSL, 0xC0); + Write_hfc(hc, HFCPCI_B2_RSL, 0xC1); + } else { + Write_hfc(hc, HFCPCI_B1_SSL, 0x80); + Write_hfc(hc, HFCPCI_B2_SSL, 0x81); + Write_hfc(hc, HFCPCI_B1_RSL, 0x80); + Write_hfc(hc, HFCPCI_B2_RSL, 0x81); + } + } + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); + val = Read_hfc(hc, HFCPCI_INT_S2); +} + +/* + * Timer function called when kernel timer expires + */ +static void +hfcpci_Timer(struct timer_list *t) +{ + struct hfc_pci *hc = from_timer(hc, t, hw.timer); + hc->hw.timer.expires = jiffies + 75; + /* WD RESET */ +/* + * WriteReg(hc, HFCD_DATA, HFCD_CTMT, hc->hw.ctmt | 0x80); + * add_timer(&hc->hw.timer); + */ +} + + +/* + * select a b-channel entry matching and active + */ +static struct bchannel * +Sel_BCS(struct hfc_pci *hc, int channel) +{ + if (test_bit(FLG_ACTIVE, &hc->bch[0].Flags) && + (hc->bch[0].nr & channel)) + return &hc->bch[0]; + else if (test_bit(FLG_ACTIVE, &hc->bch[1].Flags) && + (hc->bch[1].nr & channel)) + return &hc->bch[1]; + else + return NULL; +} + +/* + * clear the desired B-channel rx fifo + */ +static void +hfcpci_clear_fifo_rx(struct hfc_pci *hc, int fifo) +{ + u_char fifo_state; + struct bzfifo *bzr; + + if (fifo) { + bzr = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b2; + fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2RX; + } else { + bzr = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b1; + fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B1RX; + } + if (fifo_state) + hc->hw.fifo_en ^= fifo_state; + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); + hc->hw.last_bfifo_cnt[fifo] = 0; + bzr->f1 = MAX_B_FRAMES; + bzr->f2 = bzr->f1; /* init F pointers to remain constant */ + bzr->za[MAX_B_FRAMES].z1 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 1); + bzr->za[MAX_B_FRAMES].z2 = cpu_to_le16( + le16_to_cpu(bzr->za[MAX_B_FRAMES].z1)); + if (fifo_state) + hc->hw.fifo_en |= fifo_state; + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); +} + +/* + * clear the desired B-channel tx fifo + */ +static void hfcpci_clear_fifo_tx(struct hfc_pci *hc, int fifo) +{ + u_char fifo_state; + struct bzfifo *bzt; + + if (fifo) { + bzt = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b2; + fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B2TX; + } else { + bzt = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b1; + fifo_state = hc->hw.fifo_en & HFCPCI_FIFOEN_B1TX; + } + if (fifo_state) + hc->hw.fifo_en ^= fifo_state; + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); + if (hc->bch[fifo].debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG "hfcpci_clear_fifo_tx%d f1(%x) f2(%x) " + "z1(%x) z2(%x) state(%x)\n", + fifo, bzt->f1, bzt->f2, + le16_to_cpu(bzt->za[MAX_B_FRAMES].z1), + le16_to_cpu(bzt->za[MAX_B_FRAMES].z2), + fifo_state); + bzt->f2 = MAX_B_FRAMES; + bzt->f1 = bzt->f2; /* init F pointers to remain constant */ + bzt->za[MAX_B_FRAMES].z1 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 1); + bzt->za[MAX_B_FRAMES].z2 = cpu_to_le16(B_FIFO_SIZE + B_SUB_VAL - 2); + if (fifo_state) + hc->hw.fifo_en |= fifo_state; + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); + if (hc->bch[fifo].debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG + "hfcpci_clear_fifo_tx%d f1(%x) f2(%x) z1(%x) z2(%x)\n", + fifo, bzt->f1, bzt->f2, + le16_to_cpu(bzt->za[MAX_B_FRAMES].z1), + le16_to_cpu(bzt->za[MAX_B_FRAMES].z2)); +} + +/* + * read a complete B-frame out of the buffer + */ +static void +hfcpci_empty_bfifo(struct bchannel *bch, struct bzfifo *bz, + u_char *bdata, int count) +{ + u_char *ptr, *ptr1, new_f2; + int maxlen, new_z2; + struct zt *zp; + + if ((bch->debug & DEBUG_HW_BCHANNEL) && !(bch->debug & DEBUG_HW_BFIFO)) + printk(KERN_DEBUG "hfcpci_empty_fifo\n"); + zp = &bz->za[bz->f2]; /* point to Z-Regs */ + new_z2 = le16_to_cpu(zp->z2) + count; /* new position in fifo */ + if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z2 -= B_FIFO_SIZE; /* buffer wrap */ + new_f2 = (bz->f2 + 1) & MAX_B_FRAMES; + if ((count > MAX_DATA_SIZE + 3) || (count < 4) || + (*(bdata + (le16_to_cpu(zp->z1) - B_SUB_VAL)))) { + if (bch->debug & DEBUG_HW) + printk(KERN_DEBUG "hfcpci_empty_fifo: incoming packet " + "invalid length %d or crc\n", count); +#ifdef ERROR_STATISTIC + bch->err_inv++; +#endif + bz->za[new_f2].z2 = cpu_to_le16(new_z2); + bz->f2 = new_f2; /* next buffer */ + } else { + bch->rx_skb = mI_alloc_skb(count - 3, GFP_ATOMIC); + if (!bch->rx_skb) { + printk(KERN_WARNING "HFCPCI: receive out of memory\n"); + return; + } + count -= 3; + ptr = skb_put(bch->rx_skb, count); + + if (le16_to_cpu(zp->z2) + count <= B_FIFO_SIZE + B_SUB_VAL) + maxlen = count; /* complete transfer */ + else + maxlen = B_FIFO_SIZE + B_SUB_VAL - + le16_to_cpu(zp->z2); /* maximum */ + + ptr1 = bdata + (le16_to_cpu(zp->z2) - B_SUB_VAL); + /* start of data */ + memcpy(ptr, ptr1, maxlen); /* copy data */ + count -= maxlen; + + if (count) { /* rest remaining */ + ptr += maxlen; + ptr1 = bdata; /* start of buffer */ + memcpy(ptr, ptr1, count); /* rest */ + } + bz->za[new_f2].z2 = cpu_to_le16(new_z2); + bz->f2 = new_f2; /* next buffer */ + recv_Bchannel(bch, MISDN_ID_ANY, false); + } +} + +/* + * D-channel receive procedure + */ +static int +receive_dmsg(struct hfc_pci *hc) +{ + struct dchannel *dch = &hc->dch; + int maxlen; + int rcnt, total; + int count = 5; + u_char *ptr, *ptr1; + struct dfifo *df; + struct zt *zp; + + df = &((union fifo_area *)(hc->hw.fifos))->d_chan.d_rx; + while (((df->f1 & D_FREG_MASK) != (df->f2 & D_FREG_MASK)) && count--) { + zp = &df->za[df->f2 & D_FREG_MASK]; + rcnt = le16_to_cpu(zp->z1) - le16_to_cpu(zp->z2); + if (rcnt < 0) + rcnt += D_FIFO_SIZE; + rcnt++; + if (dch->debug & DEBUG_HW_DCHANNEL) + printk(KERN_DEBUG + "hfcpci recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)\n", + df->f1, df->f2, + le16_to_cpu(zp->z1), + le16_to_cpu(zp->z2), + rcnt); + + if ((rcnt > MAX_DFRAME_LEN + 3) || (rcnt < 4) || + (df->data[le16_to_cpu(zp->z1)])) { + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG + "empty_fifo hfcpci packet inv. len " + "%d or crc %d\n", + rcnt, + df->data[le16_to_cpu(zp->z1)]); +#ifdef ERROR_STATISTIC + cs->err_rx++; +#endif + df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | + (MAX_D_FRAMES + 1); /* next buffer */ + df->za[df->f2 & D_FREG_MASK].z2 = + cpu_to_le16((le16_to_cpu(zp->z2) + rcnt) & + (D_FIFO_SIZE - 1)); + } else { + dch->rx_skb = mI_alloc_skb(rcnt - 3, GFP_ATOMIC); + if (!dch->rx_skb) { + printk(KERN_WARNING + "HFC-PCI: D receive out of memory\n"); + break; + } + total = rcnt; + rcnt -= 3; + ptr = skb_put(dch->rx_skb, rcnt); + + if (le16_to_cpu(zp->z2) + rcnt <= D_FIFO_SIZE) + maxlen = rcnt; /* complete transfer */ + else + maxlen = D_FIFO_SIZE - le16_to_cpu(zp->z2); + /* maximum */ + + ptr1 = df->data + le16_to_cpu(zp->z2); + /* start of data */ + memcpy(ptr, ptr1, maxlen); /* copy data */ + rcnt -= maxlen; + + if (rcnt) { /* rest remaining */ + ptr += maxlen; + ptr1 = df->data; /* start of buffer */ + memcpy(ptr, ptr1, rcnt); /* rest */ + } + df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | + (MAX_D_FRAMES + 1); /* next buffer */ + df->za[df->f2 & D_FREG_MASK].z2 = cpu_to_le16(( + le16_to_cpu(zp->z2) + total) & (D_FIFO_SIZE - 1)); + recv_Dchannel(dch); + } + } + return 1; +} + +/* + * check for transparent receive data and read max one 'poll' size if avail + */ +static void +hfcpci_empty_fifo_trans(struct bchannel *bch, struct bzfifo *rxbz, + struct bzfifo *txbz, u_char *bdata) +{ + __le16 *z1r, *z2r, *z1t, *z2t; + int new_z2, fcnt_rx, fcnt_tx, maxlen; + u_char *ptr, *ptr1; + + z1r = &rxbz->za[MAX_B_FRAMES].z1; /* pointer to z reg */ + z2r = z1r + 1; + z1t = &txbz->za[MAX_B_FRAMES].z1; + z2t = z1t + 1; + + fcnt_rx = le16_to_cpu(*z1r) - le16_to_cpu(*z2r); + if (!fcnt_rx) + return; /* no data avail */ + + if (fcnt_rx <= 0) + fcnt_rx += B_FIFO_SIZE; /* bytes actually buffered */ + new_z2 = le16_to_cpu(*z2r) + fcnt_rx; /* new position in fifo */ + if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z2 -= B_FIFO_SIZE; /* buffer wrap */ + + fcnt_tx = le16_to_cpu(*z2t) - le16_to_cpu(*z1t); + if (fcnt_tx <= 0) + fcnt_tx += B_FIFO_SIZE; + /* fcnt_tx contains available bytes in tx-fifo */ + fcnt_tx = B_FIFO_SIZE - fcnt_tx; + /* remaining bytes to send (bytes in tx-fifo) */ + + if (test_bit(FLG_RX_OFF, &bch->Flags)) { + bch->dropcnt += fcnt_rx; + *z2r = cpu_to_le16(new_z2); + return; + } + maxlen = bchannel_get_rxbuf(bch, fcnt_rx); + if (maxlen < 0) { + pr_warn("B%d: No bufferspace for %d bytes\n", bch->nr, fcnt_rx); + } else { + ptr = skb_put(bch->rx_skb, fcnt_rx); + if (le16_to_cpu(*z2r) + fcnt_rx <= B_FIFO_SIZE + B_SUB_VAL) + maxlen = fcnt_rx; /* complete transfer */ + else + maxlen = B_FIFO_SIZE + B_SUB_VAL - le16_to_cpu(*z2r); + /* maximum */ + + ptr1 = bdata + (le16_to_cpu(*z2r) - B_SUB_VAL); + /* start of data */ + memcpy(ptr, ptr1, maxlen); /* copy data */ + fcnt_rx -= maxlen; + + if (fcnt_rx) { /* rest remaining */ + ptr += maxlen; + ptr1 = bdata; /* start of buffer */ + memcpy(ptr, ptr1, fcnt_rx); /* rest */ + } + recv_Bchannel(bch, fcnt_tx, false); /* bch, id, !force */ + } + *z2r = cpu_to_le16(new_z2); /* new position */ +} + +/* + * B-channel main receive routine + */ +static void +main_rec_hfcpci(struct bchannel *bch) +{ + struct hfc_pci *hc = bch->hw; + int rcnt, real_fifo; + int receive = 0, count = 5; + struct bzfifo *txbz, *rxbz; + u_char *bdata; + struct zt *zp; + + if ((bch->nr & 2) && (!hc->hw.bswapped)) { + rxbz = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b2; + txbz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b2; + bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.rxdat_b2; + real_fifo = 1; + } else { + rxbz = &((union fifo_area *)(hc->hw.fifos))->b_chans.rxbz_b1; + txbz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b1; + bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.rxdat_b1; + real_fifo = 0; + } +Begin: + count--; + if (rxbz->f1 != rxbz->f2) { + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG "hfcpci rec ch(%x) f1(%d) f2(%d)\n", + bch->nr, rxbz->f1, rxbz->f2); + zp = &rxbz->za[rxbz->f2]; + + rcnt = le16_to_cpu(zp->z1) - le16_to_cpu(zp->z2); + if (rcnt < 0) + rcnt += B_FIFO_SIZE; + rcnt++; + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG + "hfcpci rec ch(%x) z1(%x) z2(%x) cnt(%d)\n", + bch->nr, le16_to_cpu(zp->z1), + le16_to_cpu(zp->z2), rcnt); + hfcpci_empty_bfifo(bch, rxbz, bdata, rcnt); + rcnt = rxbz->f1 - rxbz->f2; + if (rcnt < 0) + rcnt += MAX_B_FRAMES + 1; + if (hc->hw.last_bfifo_cnt[real_fifo] > rcnt + 1) { + rcnt = 0; + hfcpci_clear_fifo_rx(hc, real_fifo); + } + hc->hw.last_bfifo_cnt[real_fifo] = rcnt; + if (rcnt > 1) + receive = 1; + else + receive = 0; + } else if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { + hfcpci_empty_fifo_trans(bch, rxbz, txbz, bdata); + return; + } else + receive = 0; + if (count && receive) + goto Begin; + +} + +/* + * D-channel send routine + */ +static void +hfcpci_fill_dfifo(struct hfc_pci *hc) +{ + struct dchannel *dch = &hc->dch; + int fcnt; + int count, new_z1, maxlen; + struct dfifo *df; + u_char *src, *dst, new_f1; + + if ((dch->debug & DEBUG_HW_DCHANNEL) && !(dch->debug & DEBUG_HW_DFIFO)) + printk(KERN_DEBUG "%s\n", __func__); + + if (!dch->tx_skb) + return; + count = dch->tx_skb->len - dch->tx_idx; + if (count <= 0) + return; + df = &((union fifo_area *) (hc->hw.fifos))->d_chan.d_tx; + + if (dch->debug & DEBUG_HW_DFIFO) + printk(KERN_DEBUG "%s:f1(%d) f2(%d) z1(f1)(%x)\n", __func__, + df->f1, df->f2, + le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1)); + fcnt = df->f1 - df->f2; /* frame count actually buffered */ + if (fcnt < 0) + fcnt += (MAX_D_FRAMES + 1); /* if wrap around */ + if (fcnt > (MAX_D_FRAMES - 1)) { + if (dch->debug & DEBUG_HW_DCHANNEL) + printk(KERN_DEBUG + "hfcpci_fill_Dfifo more as 14 frames\n"); +#ifdef ERROR_STATISTIC + cs->err_tx++; +#endif + return; + } + /* now determine free bytes in FIFO buffer */ + maxlen = le16_to_cpu(df->za[df->f2 & D_FREG_MASK].z2) - + le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1) - 1; + if (maxlen <= 0) + maxlen += D_FIFO_SIZE; /* count now contains available bytes */ + + if (dch->debug & DEBUG_HW_DCHANNEL) + printk(KERN_DEBUG "hfcpci_fill_Dfifo count(%d/%d)\n", + count, maxlen); + if (count > maxlen) { + if (dch->debug & DEBUG_HW_DCHANNEL) + printk(KERN_DEBUG "hfcpci_fill_Dfifo no fifo mem\n"); + return; + } + new_z1 = (le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1) + count) & + (D_FIFO_SIZE - 1); + new_f1 = ((df->f1 + 1) & D_FREG_MASK) | (D_FREG_MASK + 1); + src = dch->tx_skb->data + dch->tx_idx; /* source pointer */ + dst = df->data + le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1); + maxlen = D_FIFO_SIZE - le16_to_cpu(df->za[df->f1 & D_FREG_MASK].z1); + /* end fifo */ + if (maxlen > count) + maxlen = count; /* limit size */ + memcpy(dst, src, maxlen); /* first copy */ + + count -= maxlen; /* remaining bytes */ + if (count) { + dst = df->data; /* start of buffer */ + src += maxlen; /* new position */ + memcpy(dst, src, count); + } + df->za[new_f1 & D_FREG_MASK].z1 = cpu_to_le16(new_z1); + /* for next buffer */ + df->za[df->f1 & D_FREG_MASK].z1 = cpu_to_le16(new_z1); + /* new pos actual buffer */ + df->f1 = new_f1; /* next frame */ + dch->tx_idx = dch->tx_skb->len; +} + +/* + * B-channel send routine + */ +static void +hfcpci_fill_fifo(struct bchannel *bch) +{ + struct hfc_pci *hc = bch->hw; + int maxlen, fcnt; + int count, new_z1; + struct bzfifo *bz; + u_char *bdata; + u_char new_f1, *src, *dst; + __le16 *z1t, *z2t; + + if ((bch->debug & DEBUG_HW_BCHANNEL) && !(bch->debug & DEBUG_HW_BFIFO)) + printk(KERN_DEBUG "%s\n", __func__); + if ((!bch->tx_skb) || bch->tx_skb->len == 0) { + if (!test_bit(FLG_FILLEMPTY, &bch->Flags) && + !test_bit(FLG_TRANSPARENT, &bch->Flags)) + return; + count = HFCPCI_FILLEMPTY; + } else { + count = bch->tx_skb->len - bch->tx_idx; + } + if ((bch->nr & 2) && (!hc->hw.bswapped)) { + bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b2; + bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.txdat_b2; + } else { + bz = &((union fifo_area *)(hc->hw.fifos))->b_chans.txbz_b1; + bdata = ((union fifo_area *)(hc->hw.fifos))->b_chans.txdat_b1; + } + + if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { + z1t = &bz->za[MAX_B_FRAMES].z1; + z2t = z1t + 1; + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG "hfcpci_fill_fifo_trans ch(%x) " + "cnt(%d) z1(%x) z2(%x)\n", bch->nr, count, + le16_to_cpu(*z1t), le16_to_cpu(*z2t)); + fcnt = le16_to_cpu(*z2t) - le16_to_cpu(*z1t); + if (fcnt <= 0) + fcnt += B_FIFO_SIZE; + if (test_bit(FLG_FILLEMPTY, &bch->Flags)) { + /* fcnt contains available bytes in fifo */ + if (count > fcnt) + count = fcnt; + new_z1 = le16_to_cpu(*z1t) + count; + /* new buffer Position */ + if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z1 -= B_FIFO_SIZE; /* buffer wrap */ + dst = bdata + (le16_to_cpu(*z1t) - B_SUB_VAL); + maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(*z1t); + /* end of fifo */ + if (bch->debug & DEBUG_HW_BFIFO) + printk(KERN_DEBUG "hfcpci_FFt fillempty " + "fcnt(%d) maxl(%d) nz1(%x) dst(%p)\n", + fcnt, maxlen, new_z1, dst); + if (maxlen > count) + maxlen = count; /* limit size */ + memset(dst, bch->fill[0], maxlen); /* first copy */ + count -= maxlen; /* remaining bytes */ + if (count) { + dst = bdata; /* start of buffer */ + memset(dst, bch->fill[0], count); + } + *z1t = cpu_to_le16(new_z1); /* now send data */ + return; + } + /* fcnt contains available bytes in fifo */ + fcnt = B_FIFO_SIZE - fcnt; + /* remaining bytes to send (bytes in fifo) */ + + next_t_frame: + count = bch->tx_skb->len - bch->tx_idx; + /* maximum fill shall be poll*2 */ + if (count > (poll << 1) - fcnt) + count = (poll << 1) - fcnt; + if (count <= 0) + return; + /* data is suitable for fifo */ + new_z1 = le16_to_cpu(*z1t) + count; + /* new buffer Position */ + if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z1 -= B_FIFO_SIZE; /* buffer wrap */ + src = bch->tx_skb->data + bch->tx_idx; + /* source pointer */ + dst = bdata + (le16_to_cpu(*z1t) - B_SUB_VAL); + maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(*z1t); + /* end of fifo */ + if (bch->debug & DEBUG_HW_BFIFO) + printk(KERN_DEBUG "hfcpci_FFt fcnt(%d) " + "maxl(%d) nz1(%x) dst(%p)\n", + fcnt, maxlen, new_z1, dst); + fcnt += count; + bch->tx_idx += count; + if (maxlen > count) + maxlen = count; /* limit size */ + memcpy(dst, src, maxlen); /* first copy */ + count -= maxlen; /* remaining bytes */ + if (count) { + dst = bdata; /* start of buffer */ + src += maxlen; /* new position */ + memcpy(dst, src, count); + } + *z1t = cpu_to_le16(new_z1); /* now send data */ + if (bch->tx_idx < bch->tx_skb->len) + return; + dev_kfree_skb_any(bch->tx_skb); + if (get_next_bframe(bch)) + goto next_t_frame; + return; + } + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG + "%s: ch(%x) f1(%d) f2(%d) z1(f1)(%x)\n", + __func__, bch->nr, bz->f1, bz->f2, + bz->za[bz->f1].z1); + fcnt = bz->f1 - bz->f2; /* frame count actually buffered */ + if (fcnt < 0) + fcnt += (MAX_B_FRAMES + 1); /* if wrap around */ + if (fcnt > (MAX_B_FRAMES - 1)) { + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG + "hfcpci_fill_Bfifo more as 14 frames\n"); + return; + } + /* now determine free bytes in FIFO buffer */ + maxlen = le16_to_cpu(bz->za[bz->f2].z2) - + le16_to_cpu(bz->za[bz->f1].z1) - 1; + if (maxlen <= 0) + maxlen += B_FIFO_SIZE; /* count now contains available bytes */ + + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG "hfcpci_fill_fifo ch(%x) count(%d/%d)\n", + bch->nr, count, maxlen); + + if (maxlen < count) { + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG "hfcpci_fill_fifo no fifo mem\n"); + return; + } + new_z1 = le16_to_cpu(bz->za[bz->f1].z1) + count; + /* new buffer Position */ + if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z1 -= B_FIFO_SIZE; /* buffer wrap */ + + new_f1 = ((bz->f1 + 1) & MAX_B_FRAMES); + src = bch->tx_skb->data + bch->tx_idx; /* source pointer */ + dst = bdata + (le16_to_cpu(bz->za[bz->f1].z1) - B_SUB_VAL); + maxlen = (B_FIFO_SIZE + B_SUB_VAL) - le16_to_cpu(bz->za[bz->f1].z1); + /* end fifo */ + if (maxlen > count) + maxlen = count; /* limit size */ + memcpy(dst, src, maxlen); /* first copy */ + + count -= maxlen; /* remaining bytes */ + if (count) { + dst = bdata; /* start of buffer */ + src += maxlen; /* new position */ + memcpy(dst, src, count); + } + bz->za[new_f1].z1 = cpu_to_le16(new_z1); /* for next buffer */ + bz->f1 = new_f1; /* next frame */ + dev_kfree_skb_any(bch->tx_skb); + get_next_bframe(bch); +} + + + +/* + * handle L1 state changes TE + */ + +static void +ph_state_te(struct dchannel *dch) +{ + if (dch->debug) + printk(KERN_DEBUG "%s: TE newstate %x\n", + __func__, dch->state); + switch (dch->state) { + case 0: + l1_event(dch->l1, HW_RESET_IND); + break; + case 3: + l1_event(dch->l1, HW_DEACT_IND); + break; + case 5: + case 8: + l1_event(dch->l1, ANYSIGNAL); + break; + case 6: + l1_event(dch->l1, INFO2); + break; + case 7: + l1_event(dch->l1, INFO4_P8); + break; + } +} + +/* + * handle L1 state changes NT + */ + +static void +handle_nt_timer3(struct dchannel *dch) { + struct hfc_pci *hc = dch->hw; + + test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags); + hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + hc->hw.nt_timer = 0; + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + if (test_bit(HFC_CFG_MASTER, &hc->cfg)) + hc->hw.mst_m |= HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); +} + +static void +ph_state_nt(struct dchannel *dch) +{ + struct hfc_pci *hc = dch->hw; + + if (dch->debug) + printk(KERN_DEBUG "%s: NT newstate %x\n", + __func__, dch->state); + switch (dch->state) { + case 2: + if (hc->hw.nt_timer < 0) { + hc->hw.nt_timer = 0; + test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags); + test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags); + hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + /* Clear already pending ints */ + (void) Read_hfc(hc, HFCPCI_INT_S1); + Write_hfc(hc, HFCPCI_STATES, 4 | HFCPCI_LOAD_STATE); + udelay(10); + Write_hfc(hc, HFCPCI_STATES, 4); + dch->state = 4; + } else if (hc->hw.nt_timer == 0) { + hc->hw.int_m1 |= HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + hc->hw.nt_timer = NT_T1_COUNT; + hc->hw.ctmt &= ~HFCPCI_AUTO_TIMER; + hc->hw.ctmt |= HFCPCI_TIM3_125; + Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | + HFCPCI_CLTIMER); + test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags); + test_and_set_bit(FLG_HFC_TIMER_T1, &dch->Flags); + /* allow G2 -> G3 transition */ + Write_hfc(hc, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3); + } else { + Write_hfc(hc, HFCPCI_STATES, 2 | HFCPCI_NT_G2_G3); + } + break; + case 1: + hc->hw.nt_timer = 0; + test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags); + test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags); + hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + hc->hw.mst_m &= ~HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); + _queue_data(&dch->dev.D, PH_DEACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + break; + case 4: + hc->hw.nt_timer = 0; + test_and_clear_bit(FLG_HFC_TIMER_T3, &dch->Flags); + test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags); + hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + break; + case 3: + if (!test_and_set_bit(FLG_HFC_TIMER_T3, &dch->Flags)) { + if (!test_and_clear_bit(FLG_L2_ACTIVATED, + &dch->Flags)) { + handle_nt_timer3(dch); + break; + } + test_and_clear_bit(FLG_HFC_TIMER_T1, &dch->Flags); + hc->hw.int_m1 |= HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + hc->hw.nt_timer = NT_T3_COUNT; + hc->hw.ctmt &= ~HFCPCI_AUTO_TIMER; + hc->hw.ctmt |= HFCPCI_TIM3_125; + Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | + HFCPCI_CLTIMER); + } + break; + } +} + +static void +ph_state(struct dchannel *dch) +{ + struct hfc_pci *hc = dch->hw; + + if (hc->hw.protocol == ISDN_P_NT_S0) { + if (test_bit(FLG_HFC_TIMER_T3, &dch->Flags) && + hc->hw.nt_timer < 0) + handle_nt_timer3(dch); + else + ph_state_nt(dch); + } else + ph_state_te(dch); +} + +/* + * Layer 1 callback function + */ +static int +hfc_l1callback(struct dchannel *dch, u_int cmd) +{ + struct hfc_pci *hc = dch->hw; + + switch (cmd) { + case INFO3_P8: + case INFO3_P10: + if (test_bit(HFC_CFG_MASTER, &hc->cfg)) + hc->hw.mst_m |= HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + break; + case HW_RESET_REQ: + Write_hfc(hc, HFCPCI_STATES, HFCPCI_LOAD_STATE | 3); + /* HFC ST 3 */ + udelay(6); + Write_hfc(hc, HFCPCI_STATES, 3); /* HFC ST 2 */ + if (test_bit(HFC_CFG_MASTER, &hc->cfg)) + hc->hw.mst_m |= HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + Write_hfc(hc, HFCPCI_STATES, HFCPCI_ACTIVATE | + HFCPCI_DO_ACTION); + l1_event(dch->l1, HW_POWERUP_IND); + break; + case HW_DEACT_REQ: + hc->hw.mst_m &= ~HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + 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: + Write_hfc(hc, HFCPCI_STATES, HFCPCI_DO_ACTION); + 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: + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: unknown command %x\n", + __func__, cmd); + return -1; + } + return 0; +} + +/* + * Interrupt handler + */ +static inline void +tx_birq(struct bchannel *bch) +{ + if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len) + hfcpci_fill_fifo(bch); + else { + dev_kfree_skb_any(bch->tx_skb); + if (get_next_bframe(bch)) + hfcpci_fill_fifo(bch); + } +} + +static inline void +tx_dirq(struct dchannel *dch) +{ + if (dch->tx_skb && dch->tx_idx < dch->tx_skb->len) + hfcpci_fill_dfifo(dch->hw); + else { + dev_kfree_skb(dch->tx_skb); + if (get_next_dframe(dch)) + hfcpci_fill_dfifo(dch->hw); + } +} + +static irqreturn_t +hfcpci_int(int intno, void *dev_id) +{ + struct hfc_pci *hc = dev_id; + u_char exval; + struct bchannel *bch; + u_char val, stat; + + spin_lock(&hc->lock); + if (!(hc->hw.int_m2 & 0x08)) { + spin_unlock(&hc->lock); + return IRQ_NONE; /* not initialised */ + } + stat = Read_hfc(hc, HFCPCI_STATUS); + if (HFCPCI_ANYINT & stat) { + val = Read_hfc(hc, HFCPCI_INT_S1); + if (hc->dch.debug & DEBUG_HW_DCHANNEL) + printk(KERN_DEBUG + "HFC-PCI: stat(%02x) s1(%02x)\n", stat, val); + } else { + /* shared */ + spin_unlock(&hc->lock); + return IRQ_NONE; + } + hc->irqcnt++; + + if (hc->dch.debug & DEBUG_HW_DCHANNEL) + printk(KERN_DEBUG "HFC-PCI irq %x\n", val); + val &= hc->hw.int_m1; + if (val & 0x40) { /* state machine irq */ + exval = Read_hfc(hc, HFCPCI_STATES) & 0xf; + if (hc->dch.debug & DEBUG_HW_DCHANNEL) + printk(KERN_DEBUG "ph_state chg %d->%d\n", + hc->dch.state, exval); + hc->dch.state = exval; + schedule_event(&hc->dch, FLG_PHCHANGE); + val &= ~0x40; + } + if (val & 0x80) { /* timer irq */ + if (hc->hw.protocol == ISDN_P_NT_S0) { + if ((--hc->hw.nt_timer) < 0) + schedule_event(&hc->dch, FLG_PHCHANGE); + } + val &= ~0x80; + Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt | HFCPCI_CLTIMER); + } + if (val & 0x08) { /* B1 rx */ + bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1); + if (bch) + main_rec_hfcpci(bch); + else if (hc->dch.debug) + printk(KERN_DEBUG "hfcpci spurious 0x08 IRQ\n"); + } + if (val & 0x10) { /* B2 rx */ + bch = Sel_BCS(hc, 2); + if (bch) + main_rec_hfcpci(bch); + else if (hc->dch.debug) + printk(KERN_DEBUG "hfcpci spurious 0x10 IRQ\n"); + } + if (val & 0x01) { /* B1 tx */ + bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1); + if (bch) + tx_birq(bch); + else if (hc->dch.debug) + printk(KERN_DEBUG "hfcpci spurious 0x01 IRQ\n"); + } + if (val & 0x02) { /* B2 tx */ + bch = Sel_BCS(hc, 2); + if (bch) + tx_birq(bch); + else if (hc->dch.debug) + printk(KERN_DEBUG "hfcpci spurious 0x02 IRQ\n"); + } + if (val & 0x20) /* D rx */ + receive_dmsg(hc); + if (val & 0x04) { /* D tx */ + if (test_and_clear_bit(FLG_BUSY_TIMER, &hc->dch.Flags)) + del_timer(&hc->dch.timer); + tx_dirq(&hc->dch); + } + spin_unlock(&hc->lock); + return IRQ_HANDLED; +} + +/* + * timer callback for D-chan busy resolution. Currently no function + */ +static void +hfcpci_dbusy_timer(struct timer_list *t) +{ +} + +/* + * activate/deactivate hardware for selected channels and mode + */ +static int +mode_hfcpci(struct bchannel *bch, int bc, int protocol) +{ + struct hfc_pci *hc = bch->hw; + int fifo2; + u_char rx_slot = 0, tx_slot = 0, pcm_mode; + + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG + "HFCPCI bchannel protocol %x-->%x ch %x-->%x\n", + bch->state, protocol, bch->nr, bc); + + fifo2 = bc; + pcm_mode = (bc >> 24) & 0xff; + if (pcm_mode) { /* PCM SLOT USE */ + if (!test_bit(HFC_CFG_PCM, &hc->cfg)) + printk(KERN_WARNING + "%s: pcm channel id without HFC_CFG_PCM\n", + __func__); + rx_slot = (bc >> 8) & 0xff; + tx_slot = (bc >> 16) & 0xff; + bc = bc & 0xff; + } else if (test_bit(HFC_CFG_PCM, &hc->cfg) && (protocol > ISDN_P_NONE)) + printk(KERN_WARNING "%s: no pcm channel id but HFC_CFG_PCM\n", + __func__); + if (hc->chanlimit > 1) { + hc->hw.bswapped = 0; /* B1 and B2 normal mode */ + hc->hw.sctrl_e &= ~0x80; + } else { + if (bc & 2) { + if (protocol != ISDN_P_NONE) { + hc->hw.bswapped = 1; /* B1 and B2 exchanged */ + hc->hw.sctrl_e |= 0x80; + } else { + hc->hw.bswapped = 0; /* B1 and B2 normal mode */ + hc->hw.sctrl_e &= ~0x80; + } + fifo2 = 1; + } else { + hc->hw.bswapped = 0; /* B1 and B2 normal mode */ + hc->hw.sctrl_e &= ~0x80; + } + } + switch (protocol) { + case (-1): /* used for init */ + bch->state = -1; + bch->nr = bc; + fallthrough; + case (ISDN_P_NONE): + if (bch->state == ISDN_P_NONE) + return 0; + if (bc & 2) { + hc->hw.sctrl &= ~SCTRL_B2_ENA; + hc->hw.sctrl_r &= ~SCTRL_B2_ENA; + } else { + hc->hw.sctrl &= ~SCTRL_B1_ENA; + hc->hw.sctrl_r &= ~SCTRL_B1_ENA; + } + if (fifo2 & 2) { + hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B2; + hc->hw.int_m1 &= ~(HFCPCI_INTS_B2TRANS | + HFCPCI_INTS_B2REC); + } else { + hc->hw.fifo_en &= ~HFCPCI_FIFOEN_B1; + hc->hw.int_m1 &= ~(HFCPCI_INTS_B1TRANS | + HFCPCI_INTS_B1REC); + } +#ifdef REVERSE_BITORDER + if (bch->nr & 2) + hc->hw.cirm &= 0x7f; + else + hc->hw.cirm &= 0xbf; +#endif + bch->state = ISDN_P_NONE; + bch->nr = bc; + 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; + bch->nr = bc; + hfcpci_clear_fifo_rx(hc, (fifo2 & 2) ? 1 : 0); + hfcpci_clear_fifo_tx(hc, (fifo2 & 2) ? 1 : 0); + if (bc & 2) { + hc->hw.sctrl |= SCTRL_B2_ENA; + hc->hw.sctrl_r |= SCTRL_B2_ENA; +#ifdef REVERSE_BITORDER + hc->hw.cirm |= 0x80; +#endif + } else { + hc->hw.sctrl |= SCTRL_B1_ENA; + hc->hw.sctrl_r |= SCTRL_B1_ENA; +#ifdef REVERSE_BITORDER + hc->hw.cirm |= 0x40; +#endif + } + if (fifo2 & 2) { + hc->hw.fifo_en |= HFCPCI_FIFOEN_B2; + if (!tics) + hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS | + HFCPCI_INTS_B2REC); + hc->hw.ctmt |= 2; + hc->hw.conn &= ~0x18; + } else { + hc->hw.fifo_en |= HFCPCI_FIFOEN_B1; + if (!tics) + hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS | + HFCPCI_INTS_B1REC); + hc->hw.ctmt |= 1; + hc->hw.conn &= ~0x03; + } + test_and_set_bit(FLG_TRANSPARENT, &bch->Flags); + break; + case (ISDN_P_B_HDLC): + bch->state = protocol; + bch->nr = bc; + hfcpci_clear_fifo_rx(hc, (fifo2 & 2) ? 1 : 0); + hfcpci_clear_fifo_tx(hc, (fifo2 & 2) ? 1 : 0); + if (bc & 2) { + hc->hw.sctrl |= SCTRL_B2_ENA; + hc->hw.sctrl_r |= SCTRL_B2_ENA; + } else { + hc->hw.sctrl |= SCTRL_B1_ENA; + hc->hw.sctrl_r |= SCTRL_B1_ENA; + } + if (fifo2 & 2) { + hc->hw.last_bfifo_cnt[1] = 0; + hc->hw.fifo_en |= HFCPCI_FIFOEN_B2; + hc->hw.int_m1 |= (HFCPCI_INTS_B2TRANS | + HFCPCI_INTS_B2REC); + hc->hw.ctmt &= ~2; + hc->hw.conn &= ~0x18; + } else { + hc->hw.last_bfifo_cnt[0] = 0; + hc->hw.fifo_en |= HFCPCI_FIFOEN_B1; + hc->hw.int_m1 |= (HFCPCI_INTS_B1TRANS | + HFCPCI_INTS_B1REC); + hc->hw.ctmt &= ~1; + hc->hw.conn &= ~0x03; + } + test_and_set_bit(FLG_HDLC, &bch->Flags); + break; + default: + printk(KERN_DEBUG "prot not known %x\n", protocol); + return -ENOPROTOOPT; + } + if (test_bit(HFC_CFG_PCM, &hc->cfg)) { + if ((protocol == ISDN_P_NONE) || + (protocol == -1)) { /* init case */ + rx_slot = 0; + tx_slot = 0; + } else { + if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) { + rx_slot |= 0xC0; + tx_slot |= 0xC0; + } else { + rx_slot |= 0x80; + tx_slot |= 0x80; + } + } + if (bc & 2) { + hc->hw.conn &= 0xc7; + hc->hw.conn |= 0x08; + printk(KERN_DEBUG "%s: Write_hfc: B2_SSL 0x%x\n", + __func__, tx_slot); + printk(KERN_DEBUG "%s: Write_hfc: B2_RSL 0x%x\n", + __func__, rx_slot); + Write_hfc(hc, HFCPCI_B2_SSL, tx_slot); + Write_hfc(hc, HFCPCI_B2_RSL, rx_slot); + } else { + hc->hw.conn &= 0xf8; + hc->hw.conn |= 0x01; + printk(KERN_DEBUG "%s: Write_hfc: B1_SSL 0x%x\n", + __func__, tx_slot); + printk(KERN_DEBUG "%s: Write_hfc: B1_RSL 0x%x\n", + __func__, rx_slot); + Write_hfc(hc, HFCPCI_B1_SSL, tx_slot); + Write_hfc(hc, HFCPCI_B1_RSL, rx_slot); + } + } + Write_hfc(hc, HFCPCI_SCTRL_E, hc->hw.sctrl_e); + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); + Write_hfc(hc, HFCPCI_SCTRL, hc->hw.sctrl); + Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r); + Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt); + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); +#ifdef REVERSE_BITORDER + Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); +#endif + return 0; +} + +static int +set_hfcpci_rxtest(struct bchannel *bch, int protocol, int chan) +{ + struct hfc_pci *hc = bch->hw; + + if (bch->debug & DEBUG_HW_BCHANNEL) + printk(KERN_DEBUG + "HFCPCI bchannel test rx protocol %x-->%x ch %x-->%x\n", + bch->state, protocol, bch->nr, chan); + if (bch->nr != chan) { + printk(KERN_DEBUG + "HFCPCI rxtest wrong channel parameter %x/%x\n", + bch->nr, chan); + return -EINVAL; + } + switch (protocol) { + case (ISDN_P_B_RAW): + bch->state = protocol; + hfcpci_clear_fifo_rx(hc, (chan & 2) ? 1 : 0); + if (chan & 2) { + hc->hw.sctrl_r |= SCTRL_B2_ENA; + hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX; + if (!tics) + hc->hw.int_m1 |= HFCPCI_INTS_B2REC; + hc->hw.ctmt |= 2; + hc->hw.conn &= ~0x18; +#ifdef REVERSE_BITORDER + hc->hw.cirm |= 0x80; +#endif + } else { + hc->hw.sctrl_r |= SCTRL_B1_ENA; + hc->hw.fifo_en |= HFCPCI_FIFOEN_B1RX; + if (!tics) + hc->hw.int_m1 |= HFCPCI_INTS_B1REC; + hc->hw.ctmt |= 1; + hc->hw.conn &= ~0x03; +#ifdef REVERSE_BITORDER + hc->hw.cirm |= 0x40; +#endif + } + break; + case (ISDN_P_B_HDLC): + bch->state = protocol; + hfcpci_clear_fifo_rx(hc, (chan & 2) ? 1 : 0); + if (chan & 2) { + hc->hw.sctrl_r |= SCTRL_B2_ENA; + hc->hw.last_bfifo_cnt[1] = 0; + hc->hw.fifo_en |= HFCPCI_FIFOEN_B2RX; + hc->hw.int_m1 |= HFCPCI_INTS_B2REC; + hc->hw.ctmt &= ~2; + hc->hw.conn &= ~0x18; + } else { + hc->hw.sctrl_r |= SCTRL_B1_ENA; + hc->hw.last_bfifo_cnt[0] = 0; + hc->hw.fifo_en |= HFCPCI_FIFOEN_B1RX; + hc->hw.int_m1 |= HFCPCI_INTS_B1REC; + hc->hw.ctmt &= ~1; + hc->hw.conn &= ~0x03; + } + break; + default: + printk(KERN_DEBUG "prot not known %x\n", protocol); + return -ENOPROTOOPT; + } + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + Write_hfc(hc, HFCPCI_FIFO_EN, hc->hw.fifo_en); + Write_hfc(hc, HFCPCI_SCTRL_R, hc->hw.sctrl_r); + Write_hfc(hc, HFCPCI_CTMT, hc->hw.ctmt); + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); +#ifdef REVERSE_BITORDER + Write_hfc(hc, HFCPCI_CIRM, hc->hw.cirm); +#endif + return 0; +} + +static void +deactivate_bchannel(struct bchannel *bch) +{ + struct hfc_pci *hc = bch->hw; + u_long flags; + + spin_lock_irqsave(&hc->lock, flags); + mISDN_clear_bchannel(bch); + mode_hfcpci(bch, bch->nr, ISDN_P_NONE); + spin_unlock_irqrestore(&hc->lock, flags); +} + +/* + * Layer 1 B-channel hardware access + */ +static int +channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) +{ + return mISDN_ctrl_bchannel(bch, cq); +} +static int +hfc_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct hfc_pci *hc = bch->hw; + int ret = -EINVAL; + u_long flags; + + if (bch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: cmd:%x %p\n", __func__, cmd, arg); + switch (cmd) { + case HW_TESTRX_RAW: + spin_lock_irqsave(&hc->lock, flags); + ret = set_hfcpci_rxtest(bch, ISDN_P_B_RAW, (int)(long)arg); + spin_unlock_irqrestore(&hc->lock, flags); + break; + case HW_TESTRX_HDLC: + spin_lock_irqsave(&hc->lock, flags); + ret = set_hfcpci_rxtest(bch, ISDN_P_B_HDLC, (int)(long)arg); + spin_unlock_irqrestore(&hc->lock, flags); + break; + case HW_TESTRX_OFF: + spin_lock_irqsave(&hc->lock, flags); + mode_hfcpci(bch, bch->nr, ISDN_P_NONE); + spin_unlock_irqrestore(&hc->lock, flags); + ret = 0; + break; + case CLOSE_CHANNEL: + test_and_clear_bit(FLG_OPEN, &bch->Flags); + deactivate_bchannel(bch); + 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: + printk(KERN_WARNING "%s: unknown prim(%x)\n", + __func__, cmd); + } + return ret; +} + +/* + * Layer2 -> Layer 1 Dchannel data + */ +static int +hfcpci_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 hfc_pci *hc = dch->hw; + int ret = -EINVAL; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + unsigned int id; + u_long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + spin_lock_irqsave(&hc->lock, flags); + ret = dchannel_senddata(dch, skb); + if (ret > 0) { /* direct TX */ + id = hh->id; /* skb can be freed */ + hfcpci_fill_dfifo(dch->hw); + ret = 0; + spin_unlock_irqrestore(&hc->lock, flags); + queue_ch_frame(ch, PH_DATA_CNF, id, NULL); + } else + spin_unlock_irqrestore(&hc->lock, flags); + return ret; + case PH_ACTIVATE_REQ: + spin_lock_irqsave(&hc->lock, flags); + if (hc->hw.protocol == ISDN_P_NT_S0) { + ret = 0; + if (test_bit(HFC_CFG_MASTER, &hc->cfg)) + hc->hw.mst_m |= HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + if (test_bit(FLG_ACTIVE, &dch->Flags)) { + spin_unlock_irqrestore(&hc->lock, flags); + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + break; + } + test_and_set_bit(FLG_L2_ACTIVATED, &dch->Flags); + Write_hfc(hc, HFCPCI_STATES, HFCPCI_ACTIVATE | + HFCPCI_DO_ACTION | 1); + } else + ret = l1_event(dch->l1, hh->prim); + spin_unlock_irqrestore(&hc->lock, flags); + break; + case PH_DEACTIVATE_REQ: + test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); + spin_lock_irqsave(&hc->lock, flags); + if (hc->hw.protocol == ISDN_P_NT_S0) { + struct sk_buff_head free_queue; + + __skb_queue_head_init(&free_queue); + /* prepare deactivation */ + Write_hfc(hc, HFCPCI_STATES, 0x40); + skb_queue_splice_init(&dch->squeue, &free_queue); + if (dch->tx_skb) { + __skb_queue_tail(&free_queue, dch->tx_skb); + dch->tx_skb = NULL; + } + dch->tx_idx = 0; + if (dch->rx_skb) { + __skb_queue_tail(&free_queue, 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); +#ifdef FIXME + if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) + dchannel_sched_event(&hc->dch, D_CLEARBUSY); +#endif + hc->hw.mst_m &= ~HFCPCI_MASTER; + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + ret = 0; + spin_unlock_irqrestore(&hc->lock, flags); + __skb_queue_purge(&free_queue); + } else { + ret = l1_event(dch->l1, hh->prim); + spin_unlock_irqrestore(&hc->lock, flags); + } + break; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +/* + * Layer2 -> Layer 1 Bchannel data + */ +static int +hfcpci_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct hfc_pci *hc = bch->hw; + int ret = -EINVAL; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + unsigned long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + spin_lock_irqsave(&hc->lock, flags); + ret = bchannel_senddata(bch, skb); + if (ret > 0) { /* direct TX */ + hfcpci_fill_fifo(bch); + ret = 0; + } + spin_unlock_irqrestore(&hc->lock, flags); + return ret; + case PH_ACTIVATE_REQ: + spin_lock_irqsave(&hc->lock, flags); + if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) + ret = mode_hfcpci(bch, bch->nr, ch->protocol); + else + ret = 0; + spin_unlock_irqrestore(&hc->lock, flags); + if (!ret) + _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, + NULL, GFP_KERNEL); + break; + case PH_DEACTIVATE_REQ: + deactivate_bchannel(bch); + _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, + NULL, GFP_KERNEL); + ret = 0; + break; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +/* + * called for card init message + */ + +static void +inithfcpci(struct hfc_pci *hc) +{ + printk(KERN_DEBUG "inithfcpci: entered\n"); + timer_setup(&hc->dch.timer, hfcpci_dbusy_timer, 0); + hc->chanlimit = 2; + mode_hfcpci(&hc->bch[0], 1, -1); + mode_hfcpci(&hc->bch[1], 2, -1); +} + + +static int +init_card(struct hfc_pci *hc) +{ + int cnt = 3; + u_long flags; + + printk(KERN_DEBUG "init_card: entered\n"); + + + spin_lock_irqsave(&hc->lock, flags); + disable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + if (request_irq(hc->irq, hfcpci_int, IRQF_SHARED, "HFC PCI", hc)) { + printk(KERN_WARNING + "mISDN: couldn't get interrupt %d\n", hc->irq); + return -EIO; + } + spin_lock_irqsave(&hc->lock, flags); + reset_hfcpci(hc); + while (cnt) { + inithfcpci(hc); + /* + * Finally enable IRQ output + * this is only allowed, if an IRQ routine is already + * established for this HFC, so don't do that earlier + */ + enable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + /* Timeout 80ms */ + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((80 * HZ) / 1000); + printk(KERN_INFO "HFC PCI: IRQ %d count %d\n", + hc->irq, hc->irqcnt); + /* now switch timer interrupt off */ + spin_lock_irqsave(&hc->lock, flags); + hc->hw.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + /* reinit mode reg */ + Write_hfc(hc, HFCPCI_MST_MODE, hc->hw.mst_m); + if (!hc->irqcnt) { + printk(KERN_WARNING + "HFC PCI: IRQ(%d) getting no interrupts " + "during init %d\n", hc->irq, 4 - cnt); + if (cnt == 1) + break; + else { + reset_hfcpci(hc); + cnt--; + } + } else { + spin_unlock_irqrestore(&hc->lock, flags); + hc->initdone = 1; + return 0; + } + } + disable_hwirq(hc); + spin_unlock_irqrestore(&hc->lock, flags); + free_irq(hc->irq, hc); + return -EIO; +} + +static int +channel_ctrl(struct hfc_pci *hc, struct mISDN_ctrl_req *cq) +{ + int ret = 0; + u_char slot; + + switch (cq->op) { + case MISDN_CTRL_GETOP: + cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_CONNECT | + MISDN_CTRL_DISCONNECT | MISDN_CTRL_L1_TIMER3; + break; + case MISDN_CTRL_LOOP: + /* channel 0 disabled loop */ + if (cq->channel < 0 || cq->channel > 2) { + ret = -EINVAL; + break; + } + if (cq->channel & 1) { + if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) + slot = 0xC0; + else + slot = 0x80; + printk(KERN_DEBUG "%s: Write_hfc: B1_SSL/RSL 0x%x\n", + __func__, slot); + Write_hfc(hc, HFCPCI_B1_SSL, slot); + Write_hfc(hc, HFCPCI_B1_RSL, slot); + hc->hw.conn = (hc->hw.conn & ~7) | 6; + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); + } + if (cq->channel & 2) { + if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) + slot = 0xC1; + else + slot = 0x81; + printk(KERN_DEBUG "%s: Write_hfc: B2_SSL/RSL 0x%x\n", + __func__, slot); + Write_hfc(hc, HFCPCI_B2_SSL, slot); + Write_hfc(hc, HFCPCI_B2_RSL, slot); + hc->hw.conn = (hc->hw.conn & ~0x38) | 0x30; + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); + } + if (cq->channel & 3) + hc->hw.trm |= 0x80; /* enable IOM-loop */ + else { + hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x09; + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); + hc->hw.trm &= 0x7f; /* disable IOM-loop */ + } + Write_hfc(hc, HFCPCI_TRM, hc->hw.trm); + break; + case MISDN_CTRL_CONNECT: + if (cq->channel == cq->p1) { + ret = -EINVAL; + break; + } + if (cq->channel < 1 || cq->channel > 2 || + cq->p1 < 1 || cq->p1 > 2) { + ret = -EINVAL; + break; + } + if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) + slot = 0xC0; + else + slot = 0x80; + printk(KERN_DEBUG "%s: Write_hfc: B1_SSL/RSL 0x%x\n", + __func__, slot); + Write_hfc(hc, HFCPCI_B1_SSL, slot); + Write_hfc(hc, HFCPCI_B2_RSL, slot); + if (test_bit(HFC_CFG_SW_DD_DU, &hc->cfg)) + slot = 0xC1; + else + slot = 0x81; + printk(KERN_DEBUG "%s: Write_hfc: B2_SSL/RSL 0x%x\n", + __func__, slot); + Write_hfc(hc, HFCPCI_B2_SSL, slot); + Write_hfc(hc, HFCPCI_B1_RSL, slot); + hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x36; + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); + hc->hw.trm |= 0x80; + Write_hfc(hc, HFCPCI_TRM, hc->hw.trm); + break; + case MISDN_CTRL_DISCONNECT: + hc->hw.conn = (hc->hw.conn & ~0x3f) | 0x09; + Write_hfc(hc, HFCPCI_CONNECT, hc->hw.conn); + hc->hw.trm &= 0x7f; /* disable IOM-loop */ + break; + case MISDN_CTRL_L1_TIMER3: + ret = l1_event(hc->dch.l1, HW_TIMER3_VALUE | (cq->p1 & 0xff)); + break; + default: + printk(KERN_WARNING "%s: unknown Op %x\n", + __func__, cq->op); + ret = -EINVAL; + break; + } + return ret; +} + +static int +open_dchannel(struct hfc_pci *hc, struct mISDNchannel *ch, + struct channel_req *rq) +{ + int err = 0; + + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: dev(%d) open from %p\n", __func__, + hc->dch.dev.id, __builtin_return_address(0)); + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + if (rq->adr.channel == 1) { + /* TODO: E-Channel */ + return -EINVAL; + } + if (!hc->initdone) { + if (rq->protocol == ISDN_P_TE_S0) { + err = create_l1(&hc->dch, hfc_l1callback); + if (err) + return err; + } + hc->hw.protocol = rq->protocol; + ch->protocol = rq->protocol; + err = init_card(hc); + if (err) + return err; + } else { + if (rq->protocol != ch->protocol) { + if (hc->hw.protocol == ISDN_P_TE_S0) + l1_event(hc->dch.l1, CLOSE_CHANNEL); + if (rq->protocol == ISDN_P_TE_S0) { + err = create_l1(&hc->dch, hfc_l1callback); + if (err) + return err; + } + hc->hw.protocol = rq->protocol; + ch->protocol = rq->protocol; + hfcpci_setmode(hc); + } + } + + if (((ch->protocol == ISDN_P_NT_S0) && (hc->dch.state == 3)) || + ((ch->protocol == ISDN_P_TE_S0) && (hc->dch.state == 7))) { + _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, + 0, NULL, GFP_KERNEL); + } + rq->ch = ch; + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s:cannot get module\n", __func__); + return 0; +} + +static int +open_bchannel(struct hfc_pci *hc, struct channel_req *rq) +{ + struct bchannel *bch; + + if (rq->adr.channel == 0 || rq->adr.channel > 2) + return -EINVAL; + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + bch = &hc->bch[rq->adr.channel - 1]; + if (test_and_set_bit(FLG_OPEN, &bch->Flags)) + return -EBUSY; /* b-channel can be only open once */ + bch->ch.protocol = rq->protocol; + rq->ch = &bch->ch; /* TODO: E-channel */ + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s:cannot get module\n", __func__); + return 0; +} + +/* + * device control function + */ +static int +hfc_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct hfc_pci *hc = dch->hw; + struct channel_req *rq; + int err = 0; + + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: cmd:%x %p\n", + __func__, cmd, arg); + switch (cmd) { + case OPEN_CHANNEL: + rq = arg; + if ((rq->protocol == ISDN_P_TE_S0) || + (rq->protocol == ISDN_P_NT_S0)) + err = open_dchannel(hc, ch, rq); + else + err = open_bchannel(hc, rq); + break; + case CLOSE_CHANNEL: + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: dev(%d) close from %p\n", + __func__, hc->dch.dev.id, + __builtin_return_address(0)); + module_put(THIS_MODULE); + break; + case CONTROL_CHANNEL: + err = channel_ctrl(hc, arg); + break; + default: + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: unknown command %x\n", + __func__, cmd); + return -EINVAL; + } + return err; +} + +static int +setup_hw(struct hfc_pci *hc) +{ + void *buffer; + + printk(KERN_INFO "mISDN: HFC-PCI driver %s\n", hfcpci_revision); + hc->hw.cirm = 0; + hc->dch.state = 0; + pci_set_master(hc->pdev); + if (!hc->irq) { + printk(KERN_WARNING "HFC-PCI: No IRQ for PCI card found\n"); + return -EINVAL; + } + hc->hw.pci_io = + (char __iomem *)(unsigned long)hc->pdev->resource[1].start; + + if (!hc->hw.pci_io) { + printk(KERN_WARNING "HFC-PCI: No IO-Mem for PCI card found\n"); + return -ENOMEM; + } + /* Allocate memory for FIFOS */ + /* the memory needs to be on a 32k boundary within the first 4G */ + if (dma_set_mask(&hc->pdev->dev, 0xFFFF8000)) { + printk(KERN_WARNING + "HFC-PCI: No usable DMA configuration!\n"); + return -EIO; + } + buffer = dma_alloc_coherent(&hc->pdev->dev, 0x8000, &hc->hw.dmahandle, + GFP_KERNEL); + /* We silently assume the address is okay if nonzero */ + if (!buffer) { + printk(KERN_WARNING + "HFC-PCI: Error allocating memory for FIFO!\n"); + return -ENOMEM; + } + hc->hw.fifos = buffer; + pci_write_config_dword(hc->pdev, 0x80, hc->hw.dmahandle); + hc->hw.pci_io = ioremap((ulong) hc->hw.pci_io, 256); + if (unlikely(!hc->hw.pci_io)) { + printk(KERN_WARNING + "HFC-PCI: Error in ioremap for PCI!\n"); + dma_free_coherent(&hc->pdev->dev, 0x8000, hc->hw.fifos, + hc->hw.dmahandle); + return -ENOMEM; + } + + printk(KERN_INFO + "HFC-PCI: defined at mem %#lx fifo %p(%pad) IRQ %d HZ %d\n", + (u_long) hc->hw.pci_io, hc->hw.fifos, + &hc->hw.dmahandle, hc->irq, HZ); + + /* enable memory mapped ports, disable busmaster */ + pci_write_config_word(hc->pdev, PCI_COMMAND, PCI_ENA_MEMIO); + hc->hw.int_m2 = 0; + disable_hwirq(hc); + hc->hw.int_m1 = 0; + Write_hfc(hc, HFCPCI_INT_M1, hc->hw.int_m1); + /* At this point the needed PCI config is done */ + /* fifos are still not enabled */ + timer_setup(&hc->hw.timer, hfcpci_Timer, 0); + /* default PCM master */ + test_and_set_bit(HFC_CFG_MASTER, &hc->cfg); + return 0; +} + +static void +release_card(struct hfc_pci *hc) { + u_long flags; + + spin_lock_irqsave(&hc->lock, flags); + hc->hw.int_m2 = 0; /* interrupt output off ! */ + disable_hwirq(hc); + mode_hfcpci(&hc->bch[0], 1, ISDN_P_NONE); + mode_hfcpci(&hc->bch[1], 2, ISDN_P_NONE); + if (hc->dch.timer.function != NULL) { + del_timer(&hc->dch.timer); + hc->dch.timer.function = NULL; + } + spin_unlock_irqrestore(&hc->lock, flags); + if (hc->hw.protocol == ISDN_P_TE_S0) + l1_event(hc->dch.l1, CLOSE_CHANNEL); + if (hc->initdone) + free_irq(hc->irq, hc); + release_io_hfcpci(hc); /* must release after free_irq! */ + mISDN_unregister_device(&hc->dch.dev); + mISDN_freebchannel(&hc->bch[1]); + mISDN_freebchannel(&hc->bch[0]); + mISDN_freedchannel(&hc->dch); + pci_set_drvdata(hc->pdev, NULL); + kfree(hc); +} + +static int +setup_card(struct hfc_pci *card) +{ + int err = -EINVAL; + u_int i; + char name[MISDN_MAX_IDLEN]; + + card->dch.debug = debug; + spin_lock_init(&card->lock); + mISDN_initdchannel(&card->dch, MAX_DFRAME_LEN_L1, ph_state); + card->dch.hw = card; + card->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0); + card->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); + card->dch.dev.D.send = hfcpci_l2l1D; + card->dch.dev.D.ctrl = hfc_dctrl; + card->dch.dev.nrbchan = 2; + for (i = 0; i < 2; i++) { + card->bch[i].nr = i + 1; + set_channelmap(i + 1, card->dch.dev.channelmap); + card->bch[i].debug = debug; + mISDN_initbchannel(&card->bch[i], MAX_DATA_MEM, poll >> 1); + card->bch[i].hw = card; + card->bch[i].ch.send = hfcpci_l2l1B; + card->bch[i].ch.ctrl = hfc_bctrl; + card->bch[i].ch.nr = i + 1; + list_add(&card->bch[i].ch.list, &card->dch.dev.bchannels); + } + err = setup_hw(card); + if (err) + goto error; + snprintf(name, MISDN_MAX_IDLEN - 1, "hfc-pci.%d", HFC_cnt + 1); + err = mISDN_register_device(&card->dch.dev, &card->pdev->dev, name); + if (err) + goto error; + HFC_cnt++; + printk(KERN_INFO "HFC %d cards installed\n", HFC_cnt); + return 0; +error: + mISDN_freebchannel(&card->bch[1]); + mISDN_freebchannel(&card->bch[0]); + mISDN_freedchannel(&card->dch); + kfree(card); + return err; +} + +/* private data in the PCI devices list */ +struct _hfc_map { + u_int subtype; + u_int flag; + char *name; +}; + +static const struct _hfc_map hfc_map[] = +{ + {HFC_CCD_2BD0, 0, "CCD/Billion/Asuscom 2BD0"}, + {HFC_CCD_B000, 0, "Billion B000"}, + {HFC_CCD_B006, 0, "Billion B006"}, + {HFC_CCD_B007, 0, "Billion B007"}, + {HFC_CCD_B008, 0, "Billion B008"}, + {HFC_CCD_B009, 0, "Billion B009"}, + {HFC_CCD_B00A, 0, "Billion B00A"}, + {HFC_CCD_B00B, 0, "Billion B00B"}, + {HFC_CCD_B00C, 0, "Billion B00C"}, + {HFC_CCD_B100, 0, "Seyeon B100"}, + {HFC_CCD_B700, 0, "Primux II S0 B700"}, + {HFC_CCD_B701, 0, "Primux II S0 NT B701"}, + {HFC_ABOCOM_2BD1, 0, "Abocom/Magitek 2BD1"}, + {HFC_ASUS_0675, 0, "Asuscom/Askey 675"}, + {HFC_BERKOM_TCONCEPT, 0, "German telekom T-Concept"}, + {HFC_BERKOM_A1T, 0, "German telekom A1T"}, + {HFC_ANIGMA_MC145575, 0, "Motorola MC145575"}, + {HFC_ZOLTRIX_2BD0, 0, "Zoltrix 2BD0"}, + {HFC_DIGI_DF_M_IOM2_E, 0, + "Digi International DataFire Micro V IOM2 (Europe)"}, + {HFC_DIGI_DF_M_E, 0, + "Digi International DataFire Micro V (Europe)"}, + {HFC_DIGI_DF_M_IOM2_A, 0, + "Digi International DataFire Micro V IOM2 (North America)"}, + {HFC_DIGI_DF_M_A, 0, + "Digi International DataFire Micro V (North America)"}, + {HFC_SITECOM_DC105V2, 0, "Sitecom Connectivity DC-105 ISDN TA"}, + {}, +}; + +static const struct pci_device_id hfc_ids[] = +{ + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_2BD0), + (unsigned long) &hfc_map[0] }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B000), + (unsigned long) &hfc_map[1] }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B006), + (unsigned long) &hfc_map[2] }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B007), + (unsigned long) &hfc_map[3] }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B008), + (unsigned long) &hfc_map[4] }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B009), + (unsigned long) &hfc_map[5] }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B00A), + (unsigned long) &hfc_map[6] }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B00B), + (unsigned long) &hfc_map[7] }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B00C), + (unsigned long) &hfc_map[8] }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B100), + (unsigned long) &hfc_map[9] }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B700), + (unsigned long) &hfc_map[10] }, + { PCI_VDEVICE(CCD, PCI_DEVICE_ID_CCD_B701), + (unsigned long) &hfc_map[11] }, + { PCI_VDEVICE(ABOCOM, PCI_DEVICE_ID_ABOCOM_2BD1), + (unsigned long) &hfc_map[12] }, + { PCI_VDEVICE(ASUSTEK, PCI_DEVICE_ID_ASUSTEK_0675), + (unsigned long) &hfc_map[13] }, + { PCI_VDEVICE(BERKOM, PCI_DEVICE_ID_BERKOM_T_CONCEPT), + (unsigned long) &hfc_map[14] }, + { PCI_VDEVICE(BERKOM, PCI_DEVICE_ID_BERKOM_A1T), + (unsigned long) &hfc_map[15] }, + { PCI_VDEVICE(ANIGMA, PCI_DEVICE_ID_ANIGMA_MC145575), + (unsigned long) &hfc_map[16] }, + { PCI_VDEVICE(ZOLTRIX, PCI_DEVICE_ID_ZOLTRIX_2BD0), + (unsigned long) &hfc_map[17] }, + { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_E), + (unsigned long) &hfc_map[18] }, + { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_DIGI_DF_M_E), + (unsigned long) &hfc_map[19] }, + { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_DIGI_DF_M_IOM2_A), + (unsigned long) &hfc_map[20] }, + { PCI_VDEVICE(DIGI, PCI_DEVICE_ID_DIGI_DF_M_A), + (unsigned long) &hfc_map[21] }, + { PCI_VDEVICE(SITECOM, PCI_DEVICE_ID_SITECOM_DC105V2), + (unsigned long) &hfc_map[22] }, + {}, +}; + +static int +hfc_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int err = -ENOMEM; + struct hfc_pci *card; + struct _hfc_map *m = (struct _hfc_map *)ent->driver_data; + + card = kzalloc(sizeof(struct hfc_pci), GFP_KERNEL); + if (!card) { + printk(KERN_ERR "No kmem for HFC 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_hfcpci: found adapter %s at %s\n", + m->name, pci_name(pdev)); + + card->irq = pdev->irq; + pci_set_drvdata(pdev, card); + err = setup_card(card); + if (err) + pci_set_drvdata(pdev, NULL); + return err; +} + +static void +hfc_remove_pci(struct pci_dev *pdev) +{ + struct hfc_pci *card = pci_get_drvdata(pdev); + + if (card) + release_card(card); + else + if (debug) + printk(KERN_DEBUG "%s: drvdata already removed\n", + __func__); +} + + +static struct pci_driver hfc_driver = { + .name = "hfcpci", + .probe = hfc_probe, + .remove = hfc_remove_pci, + .id_table = hfc_ids, +}; + +static int +_hfcpci_softirq(struct device *dev, void *unused) +{ + struct hfc_pci *hc = dev_get_drvdata(dev); + struct bchannel *bch; + if (hc == NULL) + return 0; + + if (hc->hw.int_m2 & HFCPCI_IRQ_ENABLE) { + spin_lock_irq(&hc->lock); + bch = Sel_BCS(hc, hc->hw.bswapped ? 2 : 1); + if (bch && bch->state == ISDN_P_B_RAW) { /* B1 rx&tx */ + main_rec_hfcpci(bch); + tx_birq(bch); + } + bch = Sel_BCS(hc, hc->hw.bswapped ? 1 : 2); + if (bch && bch->state == ISDN_P_B_RAW) { /* B2 rx&tx */ + main_rec_hfcpci(bch); + tx_birq(bch); + } + spin_unlock_irq(&hc->lock); + } + return 0; +} + +static void +hfcpci_softirq(struct timer_list *unused) +{ + WARN_ON_ONCE(driver_for_each_device(&hfc_driver.driver, NULL, NULL, + _hfcpci_softirq) != 0); + + /* if next event would be in the past ... */ + if ((s32)(hfc_jiffies + tics - jiffies) <= 0) + hfc_jiffies = jiffies + 1; + else + hfc_jiffies += tics; + hfc_tl.expires = hfc_jiffies; + add_timer(&hfc_tl); +} + +static int __init +HFC_init(void) +{ + int err; + + if (!poll) + poll = HFCPCI_BTRANS_THRESHOLD; + + if (poll != HFCPCI_BTRANS_THRESHOLD) { + tics = (poll * HZ) / 8000; + if (tics < 1) + tics = 1; + poll = (tics * 8000) / HZ; + if (poll > 256 || poll < 8) { + printk(KERN_ERR "%s: Wrong poll value %d not in range " + "of 8..256.\n", __func__, poll); + err = -EINVAL; + return err; + } + } + if (poll != HFCPCI_BTRANS_THRESHOLD) { + printk(KERN_INFO "%s: Using alternative poll value of %d\n", + __func__, poll); + timer_setup(&hfc_tl, hfcpci_softirq, 0); + hfc_tl.expires = jiffies + tics; + hfc_jiffies = hfc_tl.expires; + add_timer(&hfc_tl); + } else + tics = 0; /* indicate the use of controller's timer */ + + err = pci_register_driver(&hfc_driver); + if (err) { + if (timer_pending(&hfc_tl)) + del_timer(&hfc_tl); + } + + return err; +} + +static void __exit +HFC_cleanup(void) +{ + del_timer_sync(&hfc_tl); + + pci_unregister_driver(&hfc_driver); +} + +module_init(HFC_init); +module_exit(HFC_cleanup); + +MODULE_DEVICE_TABLE(pci, hfc_ids); diff --git a/drivers/isdn/hardware/mISDN/hfcsusb.c b/drivers/isdn/hardware/mISDN/hfcsusb.c new file mode 100644 index 000000000..1efd17979 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfcsusb.c @@ -0,0 +1,2148 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* hfcsusb.c + * mISDN driver for Colognechip HFC-S USB chip + * + * Copyright 2001 by Peter Sprenger (sprenger@moving-bytes.de) + * Copyright 2008 by Martin Bachem (info@bachem-it.com) + * + * module params + * debug=<n>, default=0, with n=0xHHHHGGGG + * H - l1 driver flags described in hfcsusb.h + * G - common mISDN debug flags described at mISDNhw.h + * + * poll=<n>, default 128 + * n : burst size of PH_DATA_IND at transparent rx data + * + * Revision: 0.3.3 (socket), 2008-11-05 + */ + +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/usb.h> +#include <linux/mISDNhw.h> +#include <linux/slab.h> +#include "hfcsusb.h" + +static unsigned int debug; +static int poll = DEFAULT_TRANSP_BURST_SZ; + +static LIST_HEAD(HFClist); +static DEFINE_RWLOCK(HFClock); + + +MODULE_AUTHOR("Martin Bachem"); +MODULE_LICENSE("GPL"); +module_param(debug, uint, S_IRUGO | S_IWUSR); +module_param(poll, int, 0); + +static int hfcsusb_cnt; + +/* some function prototypes */ +static void hfcsusb_ph_command(struct hfcsusb *hw, u_char command); +static void release_hw(struct hfcsusb *hw); +static void reset_hfcsusb(struct hfcsusb *hw); +static void setPortMode(struct hfcsusb *hw); +static void hfcsusb_start_endpoint(struct hfcsusb *hw, int channel); +static void hfcsusb_stop_endpoint(struct hfcsusb *hw, int channel); +static int hfcsusb_setup_bch(struct bchannel *bch, int protocol); +static void deactivate_bchannel(struct bchannel *bch); +static int hfcsusb_ph_info(struct hfcsusb *hw); + +/* start next background transfer for control channel */ +static void +ctrl_start_transfer(struct hfcsusb *hw) +{ + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + if (hw->ctrl_cnt) { + hw->ctrl_urb->pipe = hw->ctrl_out_pipe; + hw->ctrl_urb->setup_packet = (u_char *)&hw->ctrl_write; + hw->ctrl_urb->transfer_buffer = NULL; + hw->ctrl_urb->transfer_buffer_length = 0; + hw->ctrl_write.wIndex = + cpu_to_le16(hw->ctrl_buff[hw->ctrl_out_idx].hfcs_reg); + hw->ctrl_write.wValue = + cpu_to_le16(hw->ctrl_buff[hw->ctrl_out_idx].reg_val); + + usb_submit_urb(hw->ctrl_urb, GFP_ATOMIC); + } +} + +/* + * queue a control transfer request to write HFC-S USB + * chip register using CTRL resuest queue + */ +static int write_reg(struct hfcsusb *hw, __u8 reg, __u8 val) +{ + struct ctrl_buf *buf; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s reg(0x%02x) val(0x%02x)\n", + hw->name, __func__, reg, val); + + spin_lock(&hw->ctrl_lock); + if (hw->ctrl_cnt >= HFC_CTRL_BUFSIZE) { + spin_unlock(&hw->ctrl_lock); + return 1; + } + buf = &hw->ctrl_buff[hw->ctrl_in_idx]; + buf->hfcs_reg = reg; + buf->reg_val = val; + if (++hw->ctrl_in_idx >= HFC_CTRL_BUFSIZE) + hw->ctrl_in_idx = 0; + if (++hw->ctrl_cnt == 1) + ctrl_start_transfer(hw); + spin_unlock(&hw->ctrl_lock); + + return 0; +} + +/* control completion routine handling background control cmds */ +static void +ctrl_complete(struct urb *urb) +{ + struct hfcsusb *hw = (struct hfcsusb *) urb->context; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + urb->dev = hw->dev; + if (hw->ctrl_cnt) { + hw->ctrl_cnt--; /* decrement actual count */ + if (++hw->ctrl_out_idx >= HFC_CTRL_BUFSIZE) + hw->ctrl_out_idx = 0; /* pointer wrap */ + + ctrl_start_transfer(hw); /* start next transfer */ + } +} + +/* handle LED bits */ +static void +set_led_bit(struct hfcsusb *hw, signed short led_bits, int set_on) +{ + if (set_on) { + if (led_bits < 0) + hw->led_state &= ~abs(led_bits); + else + hw->led_state |= led_bits; + } else { + if (led_bits < 0) + hw->led_state |= abs(led_bits); + else + hw->led_state &= ~led_bits; + } +} + +/* handle LED requests */ +static void +handle_led(struct hfcsusb *hw, int event) +{ + struct hfcsusb_vdata *driver_info = (struct hfcsusb_vdata *) + hfcsusb_idtab[hw->vend_idx].driver_info; + __u8 tmpled; + + if (driver_info->led_scheme == LED_OFF) + return; + tmpled = hw->led_state; + + switch (event) { + case LED_POWER_ON: + set_led_bit(hw, driver_info->led_bits[0], 1); + set_led_bit(hw, driver_info->led_bits[1], 0); + set_led_bit(hw, driver_info->led_bits[2], 0); + set_led_bit(hw, driver_info->led_bits[3], 0); + break; + case LED_POWER_OFF: + set_led_bit(hw, driver_info->led_bits[0], 0); + set_led_bit(hw, driver_info->led_bits[1], 0); + set_led_bit(hw, driver_info->led_bits[2], 0); + set_led_bit(hw, driver_info->led_bits[3], 0); + break; + case LED_S0_ON: + set_led_bit(hw, driver_info->led_bits[1], 1); + break; + case LED_S0_OFF: + set_led_bit(hw, driver_info->led_bits[1], 0); + break; + case LED_B1_ON: + set_led_bit(hw, driver_info->led_bits[2], 1); + break; + case LED_B1_OFF: + set_led_bit(hw, driver_info->led_bits[2], 0); + break; + case LED_B2_ON: + set_led_bit(hw, driver_info->led_bits[3], 1); + break; + case LED_B2_OFF: + set_led_bit(hw, driver_info->led_bits[3], 0); + break; + } + + if (hw->led_state != tmpled) { + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s reg(0x%02x) val(x%02x)\n", + hw->name, __func__, + HFCUSB_P_DATA, hw->led_state); + + write_reg(hw, HFCUSB_P_DATA, hw->led_state); + } +} + +/* + * Layer2 -> Layer 1 Bchannel data + */ +static int +hfcusb_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + struct hfcsusb *hw = bch->hw; + int ret = -EINVAL; + struct mISDNhead *hh = mISDN_HEAD_P(skb); + u_long flags; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + switch (hh->prim) { + case PH_DATA_REQ: + spin_lock_irqsave(&hw->lock, flags); + ret = bchannel_senddata(bch, skb); + spin_unlock_irqrestore(&hw->lock, flags); + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s PH_DATA_REQ ret(%i)\n", + hw->name, __func__, ret); + if (ret > 0) + ret = 0; + return ret; + case PH_ACTIVATE_REQ: + if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) { + hfcsusb_start_endpoint(hw, bch->nr - 1); + ret = hfcsusb_setup_bch(bch, ch->protocol); + } else + ret = 0; + if (!ret) + _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, + 0, NULL, GFP_KERNEL); + break; + case PH_DEACTIVATE_REQ: + deactivate_bchannel(bch); + _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, + 0, NULL, GFP_KERNEL); + ret = 0; + break; + } + if (!ret) + dev_kfree_skb(skb); + return ret; +} + +/* + * send full D/B channel status information + * as MPH_INFORMATION_IND + */ +static int +hfcsusb_ph_info(struct hfcsusb *hw) +{ + struct ph_info *phi; + struct dchannel *dch = &hw->dch; + int i; + + phi = kzalloc(struct_size(phi, bch, dch->dev.nrbchan), GFP_ATOMIC); + if (!phi) + return -ENOMEM; + + phi->dch.ch.protocol = hw->protocol; + phi->dch.ch.Flags = dch->Flags; + phi->dch.state = dch->state; + phi->dch.num_bch = dch->dev.nrbchan; + for (i = 0; i < dch->dev.nrbchan; i++) { + phi->bch[i].protocol = hw->bch[i].ch.protocol; + phi->bch[i].Flags = hw->bch[i].Flags; + } + _queue_data(&dch->dev.D, MPH_INFORMATION_IND, MISDN_ID_ANY, + struct_size(phi, bch, dch->dev.nrbchan), phi, GFP_ATOMIC); + kfree(phi); + + return 0; +} + +/* + * Layer2 -> Layer 1 Dchannel data + */ +static int +hfcusb_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 mISDNhead *hh = mISDN_HEAD_P(skb); + struct hfcsusb *hw = dch->hw; + int ret = -EINVAL; + u_long flags; + + switch (hh->prim) { + case PH_DATA_REQ: + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s: PH_DATA_REQ\n", + hw->name, __func__); + + spin_lock_irqsave(&hw->lock, flags); + ret = dchannel_senddata(dch, skb); + spin_unlock_irqrestore(&hw->lock, flags); + if (ret > 0) { + ret = 0; + queue_ch_frame(ch, PH_DATA_CNF, hh->id, NULL); + } + break; + + case PH_ACTIVATE_REQ: + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s: PH_ACTIVATE_REQ %s\n", + hw->name, __func__, + (hw->protocol == ISDN_P_NT_S0) ? "NT" : "TE"); + + if (hw->protocol == ISDN_P_NT_S0) { + ret = 0; + if (test_bit(FLG_ACTIVE, &dch->Flags)) { + _queue_data(&dch->dev.D, + PH_ACTIVATE_IND, MISDN_ID_ANY, 0, + NULL, GFP_ATOMIC); + } else { + hfcsusb_ph_command(hw, + HFC_L1_ACTIVATE_NT); + test_and_set_bit(FLG_L2_ACTIVATED, + &dch->Flags); + } + } else { + hfcsusb_ph_command(hw, HFC_L1_ACTIVATE_TE); + ret = l1_event(dch->l1, hh->prim); + } + break; + + case PH_DEACTIVATE_REQ: + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s: PH_DEACTIVATE_REQ\n", + hw->name, __func__); + test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); + + if (hw->protocol == ISDN_P_NT_S0) { + struct sk_buff_head free_queue; + + __skb_queue_head_init(&free_queue); + hfcsusb_ph_command(hw, HFC_L1_DEACTIVATE_NT); + spin_lock_irqsave(&hw->lock, flags); + skb_queue_splice_init(&dch->squeue, &free_queue); + if (dch->tx_skb) { + __skb_queue_tail(&free_queue, dch->tx_skb); + dch->tx_skb = NULL; + } + dch->tx_idx = 0; + if (dch->rx_skb) { + __skb_queue_tail(&free_queue, dch->rx_skb); + dch->rx_skb = NULL; + } + test_and_clear_bit(FLG_TX_BUSY, &dch->Flags); + spin_unlock_irqrestore(&hw->lock, flags); + __skb_queue_purge(&free_queue); +#ifdef FIXME + if (test_and_clear_bit(FLG_L1_BUSY, &dch->Flags)) + dchannel_sched_event(&hc->dch, D_CLEARBUSY); +#endif + ret = 0; + } else + ret = l1_event(dch->l1, hh->prim); + break; + case MPH_INFORMATION_REQ: + ret = hfcsusb_ph_info(hw); + break; + } + + return ret; +} + +/* + * Layer 1 callback function + */ +static int +hfc_l1callback(struct dchannel *dch, u_int cmd) +{ + struct hfcsusb *hw = dch->hw; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s cmd 0x%x\n", + hw->name, __func__, cmd); + + switch (cmd) { + case INFO3_P8: + case INFO3_P10: + case HW_RESET_REQ: + case HW_POWERUP_REQ: + 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); + 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: + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: unknown cmd %x\n", + hw->name, __func__, cmd); + return -1; + } + return hfcsusb_ph_info(hw); +} + +static int +open_dchannel(struct hfcsusb *hw, struct mISDNchannel *ch, + struct channel_req *rq) +{ + int err = 0; + + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG "%s: %s: dev(%d) open addr(%i) from %p\n", + hw->name, __func__, hw->dch.dev.id, rq->adr.channel, + __builtin_return_address(0)); + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + + test_and_clear_bit(FLG_ACTIVE, &hw->dch.Flags); + test_and_clear_bit(FLG_ACTIVE, &hw->ech.Flags); + hfcsusb_start_endpoint(hw, HFC_CHAN_D); + + /* E-Channel logging */ + if (rq->adr.channel == 1) { + if (hw->fifos[HFCUSB_PCM_RX].pipe) { + hfcsusb_start_endpoint(hw, HFC_CHAN_E); + set_bit(FLG_ACTIVE, &hw->ech.Flags); + _queue_data(&hw->ech.dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + } else + return -EINVAL; + } + + if (!hw->initdone) { + hw->protocol = rq->protocol; + if (rq->protocol == ISDN_P_TE_S0) { + err = create_l1(&hw->dch, hfc_l1callback); + if (err) + return err; + } + setPortMode(hw); + ch->protocol = rq->protocol; + hw->initdone = 1; + } else { + if (rq->protocol != ch->protocol) + return -EPROTONOSUPPORT; + } + + if (((ch->protocol == ISDN_P_NT_S0) && (hw->dch.state == 3)) || + ((ch->protocol == ISDN_P_TE_S0) && (hw->dch.state == 7))) + _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, + 0, NULL, GFP_KERNEL); + rq->ch = ch; + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s: %s: cannot get module\n", + hw->name, __func__); + return 0; +} + +static int +open_bchannel(struct hfcsusb *hw, struct channel_req *rq) +{ + struct bchannel *bch; + + if (rq->adr.channel == 0 || rq->adr.channel > 2) + return -EINVAL; + if (rq->protocol == ISDN_P_NONE) + return -EINVAL; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s B%i\n", + hw->name, __func__, rq->adr.channel); + + bch = &hw->bch[rq->adr.channel - 1]; + if (test_and_set_bit(FLG_OPEN, &bch->Flags)) + return -EBUSY; /* b-channel can be only open once */ + bch->ch.protocol = rq->protocol; + rq->ch = &bch->ch; + + if (!try_module_get(THIS_MODULE)) + printk(KERN_WARNING "%s: %s:cannot get module\n", + hw->name, __func__); + return 0; +} + +static int +channel_ctrl(struct hfcsusb *hw, struct mISDN_ctrl_req *cq) +{ + int ret = 0; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s op(0x%x) channel(0x%x)\n", + hw->name, __func__, (cq->op), (cq->channel)); + + switch (cq->op) { + case MISDN_CTRL_GETOP: + cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_CONNECT | + MISDN_CTRL_DISCONNECT; + break; + default: + printk(KERN_WARNING "%s: %s: unknown Op %x\n", + hw->name, __func__, cq->op); + ret = -EINVAL; + break; + } + return ret; +} + +/* + * device control function + */ +static int +hfc_dctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); + struct dchannel *dch = container_of(dev, struct dchannel, dev); + struct hfcsusb *hw = dch->hw; + struct channel_req *rq; + int err = 0; + + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: cmd:%x %p\n", + hw->name, __func__, cmd, arg); + switch (cmd) { + case OPEN_CHANNEL: + rq = arg; + if ((rq->protocol == ISDN_P_TE_S0) || + (rq->protocol == ISDN_P_NT_S0)) + err = open_dchannel(hw, ch, rq); + else + err = open_bchannel(hw, rq); + if (!err) + hw->open++; + break; + case CLOSE_CHANNEL: + hw->open--; + if (debug & DEBUG_HW_OPEN) + printk(KERN_DEBUG + "%s: %s: dev(%d) close from %p (open %d)\n", + hw->name, __func__, hw->dch.dev.id, + __builtin_return_address(0), hw->open); + if (!hw->open) { + hfcsusb_stop_endpoint(hw, HFC_CHAN_D); + if (hw->fifos[HFCUSB_PCM_RX].pipe) + hfcsusb_stop_endpoint(hw, HFC_CHAN_E); + handle_led(hw, LED_POWER_ON); + } + module_put(THIS_MODULE); + break; + case CONTROL_CHANNEL: + err = channel_ctrl(hw, arg); + break; + default: + if (dch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: unknown command %x\n", + hw->name, __func__, cmd); + return -EINVAL; + } + return err; +} + +/* + * S0 TE state change event handler + */ +static void +ph_state_te(struct dchannel *dch) +{ + struct hfcsusb *hw = dch->hw; + + if (debug & DEBUG_HW) { + if (dch->state <= HFC_MAX_TE_LAYER1_STATE) + printk(KERN_DEBUG "%s: %s: %s\n", hw->name, __func__, + HFC_TE_LAYER1_STATES[dch->state]); + else + printk(KERN_DEBUG "%s: %s: TE F%d\n", + hw->name, __func__, dch->state); + } + + switch (dch->state) { + case 0: + l1_event(dch->l1, HW_RESET_IND); + break; + case 3: + l1_event(dch->l1, HW_DEACT_IND); + break; + case 5: + case 8: + l1_event(dch->l1, ANYSIGNAL); + break; + case 6: + l1_event(dch->l1, INFO2); + break; + case 7: + l1_event(dch->l1, INFO4_P8); + break; + } + if (dch->state == 7) + handle_led(hw, LED_S0_ON); + else + handle_led(hw, LED_S0_OFF); +} + +/* + * S0 NT state change event handler + */ +static void +ph_state_nt(struct dchannel *dch) +{ + struct hfcsusb *hw = dch->hw; + + if (debug & DEBUG_HW) { + if (dch->state <= HFC_MAX_NT_LAYER1_STATE) + printk(KERN_DEBUG "%s: %s: %s\n", + hw->name, __func__, + HFC_NT_LAYER1_STATES[dch->state]); + + else + printk(KERN_INFO DRIVER_NAME "%s: %s: NT G%d\n", + hw->name, __func__, dch->state); + } + + switch (dch->state) { + case (1): + test_and_clear_bit(FLG_ACTIVE, &dch->Flags); + test_and_clear_bit(FLG_L2_ACTIVATED, &dch->Flags); + hw->nt_timer = 0; + hw->timers &= ~NT_ACTIVATION_TIMER; + handle_led(hw, LED_S0_OFF); + break; + + case (2): + if (hw->nt_timer < 0) { + hw->nt_timer = 0; + hw->timers &= ~NT_ACTIVATION_TIMER; + hfcsusb_ph_command(dch->hw, HFC_L1_DEACTIVATE_NT); + } else { + hw->timers |= NT_ACTIVATION_TIMER; + hw->nt_timer = NT_T1_COUNT; + /* allow G2 -> G3 transition */ + write_reg(hw, HFCUSB_STATES, 2 | HFCUSB_NT_G2_G3); + } + break; + case (3): + hw->nt_timer = 0; + hw->timers &= ~NT_ACTIVATION_TIMER; + test_and_set_bit(FLG_ACTIVE, &dch->Flags); + _queue_data(&dch->dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + handle_led(hw, LED_S0_ON); + break; + case (4): + hw->nt_timer = 0; + hw->timers &= ~NT_ACTIVATION_TIMER; + break; + default: + break; + } + hfcsusb_ph_info(hw); +} + +static void +ph_state(struct dchannel *dch) +{ + struct hfcsusb *hw = dch->hw; + + if (hw->protocol == ISDN_P_NT_S0) + ph_state_nt(dch); + else if (hw->protocol == ISDN_P_TE_S0) + ph_state_te(dch); +} + +/* + * disable/enable BChannel for desired protocoll + */ +static int +hfcsusb_setup_bch(struct bchannel *bch, int protocol) +{ + struct hfcsusb *hw = bch->hw; + __u8 conhdlc, sctrl, sctrl_r; + + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: protocol %x-->%x B%d\n", + hw->name, __func__, bch->state, protocol, + bch->nr); + + /* setup val for CON_HDLC */ + conhdlc = 0; + if (protocol > ISDN_P_NONE) + conhdlc = 8; /* enable FIFO */ + + switch (protocol) { + case (-1): /* used for init */ + bch->state = -1; + fallthrough; + case (ISDN_P_NONE): + if (bch->state == ISDN_P_NONE) + return 0; /* already in idle state */ + bch->state = ISDN_P_NONE; + clear_bit(FLG_HDLC, &bch->Flags); + clear_bit(FLG_TRANSPARENT, &bch->Flags); + break; + case (ISDN_P_B_RAW): + conhdlc |= 2; + bch->state = protocol; + set_bit(FLG_TRANSPARENT, &bch->Flags); + break; + case (ISDN_P_B_HDLC): + bch->state = protocol; + set_bit(FLG_HDLC, &bch->Flags); + break; + default: + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: prot not known %x\n", + hw->name, __func__, protocol); + return -ENOPROTOOPT; + } + + if (protocol >= ISDN_P_NONE) { + write_reg(hw, HFCUSB_FIFO, (bch->nr == 1) ? 0 : 2); + write_reg(hw, HFCUSB_CON_HDLC, conhdlc); + write_reg(hw, HFCUSB_INC_RES_F, 2); + write_reg(hw, HFCUSB_FIFO, (bch->nr == 1) ? 1 : 3); + write_reg(hw, HFCUSB_CON_HDLC, conhdlc); + write_reg(hw, HFCUSB_INC_RES_F, 2); + + sctrl = 0x40 + ((hw->protocol == ISDN_P_TE_S0) ? 0x00 : 0x04); + sctrl_r = 0x0; + if (test_bit(FLG_ACTIVE, &hw->bch[0].Flags)) { + sctrl |= 1; + sctrl_r |= 1; + } + if (test_bit(FLG_ACTIVE, &hw->bch[1].Flags)) { + sctrl |= 2; + sctrl_r |= 2; + } + write_reg(hw, HFCUSB_SCTRL, sctrl); + write_reg(hw, HFCUSB_SCTRL_R, sctrl_r); + + if (protocol > ISDN_P_NONE) + handle_led(hw, (bch->nr == 1) ? LED_B1_ON : LED_B2_ON); + else + handle_led(hw, (bch->nr == 1) ? LED_B1_OFF : + LED_B2_OFF); + } + return hfcsusb_ph_info(hw); +} + +static void +hfcsusb_ph_command(struct hfcsusb *hw, u_char command) +{ + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: %x\n", + hw->name, __func__, command); + + switch (command) { + case HFC_L1_ACTIVATE_TE: + /* force sending sending INFO1 */ + write_reg(hw, HFCUSB_STATES, 0x14); + /* start l1 activation */ + write_reg(hw, HFCUSB_STATES, 0x04); + break; + + case HFC_L1_FORCE_DEACTIVATE_TE: + write_reg(hw, HFCUSB_STATES, 0x10); + write_reg(hw, HFCUSB_STATES, 0x03); + break; + + case HFC_L1_ACTIVATE_NT: + if (hw->dch.state == 3) + _queue_data(&hw->dch.dev.D, PH_ACTIVATE_IND, + MISDN_ID_ANY, 0, NULL, GFP_ATOMIC); + else + write_reg(hw, HFCUSB_STATES, HFCUSB_ACTIVATE | + HFCUSB_DO_ACTION | HFCUSB_NT_G2_G3); + break; + + case HFC_L1_DEACTIVATE_NT: + write_reg(hw, HFCUSB_STATES, + HFCUSB_DO_ACTION); + break; + } +} + +/* + * Layer 1 B-channel hardware access + */ +static int +channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) +{ + return mISDN_ctrl_bchannel(bch, cq); +} + +/* collect data from incoming interrupt or isochron USB data */ +static void +hfcsusb_rx_frame(struct usb_fifo *fifo, __u8 *data, unsigned int len, + int finish) +{ + struct hfcsusb *hw = fifo->hw; + struct sk_buff *rx_skb = NULL; + int maxlen = 0; + int fifon = fifo->fifonum; + int i; + int hdlc = 0; + unsigned long flags; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s: fifo(%i) len(%i) " + "dch(%p) bch(%p) ech(%p)\n", + hw->name, __func__, fifon, len, + fifo->dch, fifo->bch, fifo->ech); + + if (!len) + return; + + if ((!!fifo->dch + !!fifo->bch + !!fifo->ech) != 1) { + printk(KERN_DEBUG "%s: %s: undefined channel\n", + hw->name, __func__); + return; + } + + spin_lock_irqsave(&hw->lock, flags); + if (fifo->dch) { + rx_skb = fifo->dch->rx_skb; + maxlen = fifo->dch->maxlen; + hdlc = 1; + } + if (fifo->bch) { + if (test_bit(FLG_RX_OFF, &fifo->bch->Flags)) { + fifo->bch->dropcnt += len; + spin_unlock_irqrestore(&hw->lock, flags); + return; + } + maxlen = bchannel_get_rxbuf(fifo->bch, len); + rx_skb = fifo->bch->rx_skb; + if (maxlen < 0) { + if (rx_skb) + skb_trim(rx_skb, 0); + pr_warn("%s.B%d: No bufferspace for %d bytes\n", + hw->name, fifo->bch->nr, len); + spin_unlock_irqrestore(&hw->lock, flags); + return; + } + maxlen = fifo->bch->maxlen; + hdlc = test_bit(FLG_HDLC, &fifo->bch->Flags); + } + if (fifo->ech) { + rx_skb = fifo->ech->rx_skb; + maxlen = fifo->ech->maxlen; + hdlc = 1; + } + + if (fifo->dch || fifo->ech) { + if (!rx_skb) { + rx_skb = mI_alloc_skb(maxlen, GFP_ATOMIC); + if (rx_skb) { + if (fifo->dch) + fifo->dch->rx_skb = rx_skb; + if (fifo->ech) + fifo->ech->rx_skb = rx_skb; + skb_trim(rx_skb, 0); + } else { + printk(KERN_DEBUG "%s: %s: No mem for rx_skb\n", + hw->name, __func__); + spin_unlock_irqrestore(&hw->lock, flags); + return; + } + } + /* D/E-Channel SKB range check */ + if ((rx_skb->len + len) >= MAX_DFRAME_LEN_L1) { + printk(KERN_DEBUG "%s: %s: sbk mem exceeded " + "for fifo(%d) HFCUSB_D_RX\n", + hw->name, __func__, fifon); + skb_trim(rx_skb, 0); + spin_unlock_irqrestore(&hw->lock, flags); + return; + } + } + + skb_put_data(rx_skb, data, len); + + if (hdlc) { + /* we have a complete hdlc packet */ + if (finish) { + if ((rx_skb->len > 3) && + (!(rx_skb->data[rx_skb->len - 1]))) { + if (debug & DBG_HFC_FIFO_VERBOSE) { + printk(KERN_DEBUG "%s: %s: fifon(%i)" + " new RX len(%i): ", + hw->name, __func__, fifon, + rx_skb->len); + i = 0; + while (i < rx_skb->len) + printk("%02x ", + rx_skb->data[i++]); + printk("\n"); + } + + /* remove CRC & status */ + skb_trim(rx_skb, rx_skb->len - 3); + + if (fifo->dch) + recv_Dchannel(fifo->dch); + if (fifo->bch) + recv_Bchannel(fifo->bch, MISDN_ID_ANY, + 0); + if (fifo->ech) + recv_Echannel(fifo->ech, + &hw->dch); + } else { + if (debug & DBG_HFC_FIFO_VERBOSE) { + printk(KERN_DEBUG + "%s: CRC or minlen ERROR fifon(%i) " + "RX len(%i): ", + hw->name, fifon, rx_skb->len); + i = 0; + while (i < rx_skb->len) + printk("%02x ", + rx_skb->data[i++]); + printk("\n"); + } + skb_trim(rx_skb, 0); + } + } + } else { + /* deliver transparent data to layer2 */ + recv_Bchannel(fifo->bch, MISDN_ID_ANY, false); + } + spin_unlock_irqrestore(&hw->lock, flags); +} + +static void +fill_isoc_urb(struct urb *urb, struct usb_device *dev, unsigned int pipe, + void *buf, int num_packets, int packet_size, int interval, + usb_complete_t complete, void *context) +{ + int k; + + usb_fill_bulk_urb(urb, dev, pipe, buf, packet_size * num_packets, + complete, context); + + urb->number_of_packets = num_packets; + urb->transfer_flags = URB_ISO_ASAP; + urb->actual_length = 0; + urb->interval = interval; + + for (k = 0; k < num_packets; k++) { + urb->iso_frame_desc[k].offset = packet_size * k; + urb->iso_frame_desc[k].length = packet_size; + urb->iso_frame_desc[k].actual_length = 0; + } +} + +/* receive completion routine for all ISO tx fifos */ +static void +rx_iso_complete(struct urb *urb) +{ + struct iso_urb *context_iso_urb = (struct iso_urb *) urb->context; + struct usb_fifo *fifo = context_iso_urb->owner_fifo; + struct hfcsusb *hw = fifo->hw; + int k, len, errcode, offset, num_isoc_packets, fifon, maxlen, + status, iso_status, i; + __u8 *buf; + static __u8 eof[8]; + __u8 s0_state; + unsigned long flags; + + fifon = fifo->fifonum; + status = urb->status; + + spin_lock_irqsave(&hw->lock, flags); + if (fifo->stop_gracefull) { + fifo->stop_gracefull = 0; + fifo->active = 0; + spin_unlock_irqrestore(&hw->lock, flags); + return; + } + spin_unlock_irqrestore(&hw->lock, flags); + + /* + * ISO transfer only partially completed, + * look at individual frame status for details + */ + if (status == -EXDEV) { + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: with -EXDEV " + "urb->status %d, fifonum %d\n", + hw->name, __func__, status, fifon); + + /* clear status, so go on with ISO transfers */ + status = 0; + } + + s0_state = 0; + if (fifo->active && !status) { + num_isoc_packets = iso_packets[fifon]; + maxlen = fifo->usb_packet_maxlen; + + for (k = 0; k < num_isoc_packets; ++k) { + len = urb->iso_frame_desc[k].actual_length; + offset = urb->iso_frame_desc[k].offset; + buf = context_iso_urb->buffer + offset; + iso_status = urb->iso_frame_desc[k].status; + + if (iso_status && (debug & DBG_HFC_FIFO_VERBOSE)) { + printk(KERN_DEBUG "%s: %s: " + "ISO packet %i, status: %i\n", + hw->name, __func__, k, iso_status); + } + + /* USB data log for every D ISO in */ + if ((fifon == HFCUSB_D_RX) && + (debug & DBG_HFC_USB_VERBOSE)) { + printk(KERN_DEBUG + "%s: %s: %d (%d/%d) len(%d) ", + hw->name, __func__, urb->start_frame, + k, num_isoc_packets - 1, + len); + for (i = 0; i < len; i++) + printk("%x ", buf[i]); + printk("\n"); + } + + if (!iso_status) { + if (fifo->last_urblen != maxlen) { + /* + * save fifo fill-level threshold bits + * to use them later in TX ISO URB + * completions + */ + hw->threshold_mask = buf[1]; + + if (fifon == HFCUSB_D_RX) + s0_state = (buf[0] >> 4); + + eof[fifon] = buf[0] & 1; + if (len > 2) + hfcsusb_rx_frame(fifo, buf + 2, + len - 2, (len < maxlen) + ? eof[fifon] : 0); + } else + hfcsusb_rx_frame(fifo, buf, len, + (len < maxlen) ? + eof[fifon] : 0); + fifo->last_urblen = len; + } + } + + /* signal S0 layer1 state change */ + if ((s0_state) && (hw->initdone) && + (s0_state != hw->dch.state)) { + hw->dch.state = s0_state; + schedule_event(&hw->dch, FLG_PHCHANGE); + } + + fill_isoc_urb(urb, fifo->hw->dev, fifo->pipe, + context_iso_urb->buffer, num_isoc_packets, + fifo->usb_packet_maxlen, fifo->intervall, + (usb_complete_t)rx_iso_complete, urb->context); + errcode = usb_submit_urb(urb, GFP_ATOMIC); + if (errcode < 0) { + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: error submitting " + "ISO URB: %d\n", + hw->name, __func__, errcode); + } + } else { + if (status && (debug & DBG_HFC_URB_INFO)) + printk(KERN_DEBUG "%s: %s: rx_iso_complete : " + "urb->status %d, fifonum %d\n", + hw->name, __func__, status, fifon); + } +} + +/* receive completion routine for all interrupt rx fifos */ +static void +rx_int_complete(struct urb *urb) +{ + int len, status, i; + __u8 *buf, maxlen, fifon; + struct usb_fifo *fifo = (struct usb_fifo *) urb->context; + struct hfcsusb *hw = fifo->hw; + static __u8 eof[8]; + unsigned long flags; + + spin_lock_irqsave(&hw->lock, flags); + if (fifo->stop_gracefull) { + fifo->stop_gracefull = 0; + fifo->active = 0; + spin_unlock_irqrestore(&hw->lock, flags); + return; + } + spin_unlock_irqrestore(&hw->lock, flags); + + fifon = fifo->fifonum; + if ((!fifo->active) || (urb->status)) { + if (debug & DBG_HFC_URB_ERROR) + printk(KERN_DEBUG + "%s: %s: RX-Fifo %i is going down (%i)\n", + hw->name, __func__, fifon, urb->status); + + fifo->urb->interval = 0; /* cancel automatic rescheduling */ + return; + } + len = urb->actual_length; + buf = fifo->buffer; + maxlen = fifo->usb_packet_maxlen; + + /* USB data log for every D INT in */ + if ((fifon == HFCUSB_D_RX) && (debug & DBG_HFC_USB_VERBOSE)) { + printk(KERN_DEBUG "%s: %s: D RX INT len(%d) ", + hw->name, __func__, len); + for (i = 0; i < len; i++) + printk("%02x ", buf[i]); + printk("\n"); + } + + if (fifo->last_urblen != fifo->usb_packet_maxlen) { + /* the threshold mask is in the 2nd status byte */ + hw->threshold_mask = buf[1]; + + /* signal S0 layer1 state change */ + if (hw->initdone && ((buf[0] >> 4) != hw->dch.state)) { + hw->dch.state = (buf[0] >> 4); + schedule_event(&hw->dch, FLG_PHCHANGE); + } + + eof[fifon] = buf[0] & 1; + /* if we have more than the 2 status bytes -> collect data */ + if (len > 2) + hfcsusb_rx_frame(fifo, buf + 2, + urb->actual_length - 2, + (len < maxlen) ? eof[fifon] : 0); + } else { + hfcsusb_rx_frame(fifo, buf, urb->actual_length, + (len < maxlen) ? eof[fifon] : 0); + } + fifo->last_urblen = urb->actual_length; + + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status) { + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: error resubmitting USB\n", + hw->name, __func__); + } +} + +/* transmit completion routine for all ISO tx fifos */ +static void +tx_iso_complete(struct urb *urb) +{ + struct iso_urb *context_iso_urb = (struct iso_urb *) urb->context; + struct usb_fifo *fifo = context_iso_urb->owner_fifo; + struct hfcsusb *hw = fifo->hw; + struct sk_buff *tx_skb; + int k, tx_offset, num_isoc_packets, sink, remain, current_len, + errcode, hdlc, i; + int *tx_idx; + int frame_complete, fifon, status, fillempty = 0; + __u8 threshbit, *p; + unsigned long flags; + + spin_lock_irqsave(&hw->lock, flags); + if (fifo->stop_gracefull) { + fifo->stop_gracefull = 0; + fifo->active = 0; + spin_unlock_irqrestore(&hw->lock, flags); + return; + } + + if (fifo->dch) { + tx_skb = fifo->dch->tx_skb; + tx_idx = &fifo->dch->tx_idx; + hdlc = 1; + } else if (fifo->bch) { + tx_skb = fifo->bch->tx_skb; + tx_idx = &fifo->bch->tx_idx; + hdlc = test_bit(FLG_HDLC, &fifo->bch->Flags); + if (!tx_skb && !hdlc && + test_bit(FLG_FILLEMPTY, &fifo->bch->Flags)) + fillempty = 1; + } else { + printk(KERN_DEBUG "%s: %s: neither BCH nor DCH\n", + hw->name, __func__); + spin_unlock_irqrestore(&hw->lock, flags); + return; + } + + fifon = fifo->fifonum; + status = urb->status; + + tx_offset = 0; + + /* + * ISO transfer only partially completed, + * look at individual frame status for details + */ + if (status == -EXDEV) { + if (debug & DBG_HFC_URB_ERROR) + printk(KERN_DEBUG "%s: %s: " + "-EXDEV (%i) fifon (%d)\n", + hw->name, __func__, status, fifon); + + /* clear status, so go on with ISO transfers */ + status = 0; + } + + if (fifo->active && !status) { + /* is FifoFull-threshold set for our channel? */ + threshbit = (hw->threshold_mask & (1 << fifon)); + num_isoc_packets = iso_packets[fifon]; + + /* predict dataflow to avoid fifo overflow */ + if (fifon >= HFCUSB_D_TX) + sink = (threshbit) ? SINK_DMIN : SINK_DMAX; + else + sink = (threshbit) ? SINK_MIN : SINK_MAX; + fill_isoc_urb(urb, fifo->hw->dev, fifo->pipe, + context_iso_urb->buffer, num_isoc_packets, + fifo->usb_packet_maxlen, fifo->intervall, + (usb_complete_t)tx_iso_complete, urb->context); + memset(context_iso_urb->buffer, 0, + sizeof(context_iso_urb->buffer)); + frame_complete = 0; + + for (k = 0; k < num_isoc_packets; ++k) { + /* analyze tx success of previous ISO packets */ + if (debug & DBG_HFC_URB_ERROR) { + errcode = urb->iso_frame_desc[k].status; + if (errcode) { + printk(KERN_DEBUG "%s: %s: " + "ISO packet %i, status: %i\n", + hw->name, __func__, k, errcode); + } + } + + /* Generate next ISO Packets */ + if (tx_skb) + remain = tx_skb->len - *tx_idx; + else if (fillempty) + remain = 15; /* > not complete */ + else + remain = 0; + + if (remain > 0) { + fifo->bit_line -= sink; + current_len = (0 - fifo->bit_line) / 8; + if (current_len > 14) + current_len = 14; + if (current_len < 0) + current_len = 0; + if (remain < current_len) + current_len = remain; + + /* how much bit do we put on the line? */ + fifo->bit_line += current_len * 8; + + context_iso_urb->buffer[tx_offset] = 0; + if (current_len == remain) { + if (hdlc) { + /* signal frame completion */ + context_iso_urb-> + buffer[tx_offset] = 1; + /* add 2 byte flags and 16bit + * CRC at end of ISDN frame */ + fifo->bit_line += 32; + } + frame_complete = 1; + } + + /* copy tx data to iso-urb buffer */ + p = context_iso_urb->buffer + tx_offset + 1; + if (fillempty) { + memset(p, fifo->bch->fill[0], + current_len); + } else { + memcpy(p, (tx_skb->data + *tx_idx), + current_len); + *tx_idx += current_len; + } + urb->iso_frame_desc[k].offset = tx_offset; + urb->iso_frame_desc[k].length = current_len + 1; + + /* USB data log for every D ISO out */ + if ((fifon == HFCUSB_D_RX) && !fillempty && + (debug & DBG_HFC_USB_VERBOSE)) { + printk(KERN_DEBUG + "%s: %s (%d/%d) offs(%d) len(%d) ", + hw->name, __func__, + k, num_isoc_packets - 1, + urb->iso_frame_desc[k].offset, + urb->iso_frame_desc[k].length); + + for (i = urb->iso_frame_desc[k].offset; + i < (urb->iso_frame_desc[k].offset + + urb->iso_frame_desc[k].length); + i++) + printk("%x ", + context_iso_urb->buffer[i]); + + printk(" skb->len(%i) tx-idx(%d)\n", + tx_skb->len, *tx_idx); + } + + tx_offset += (current_len + 1); + } else { + urb->iso_frame_desc[k].offset = tx_offset++; + urb->iso_frame_desc[k].length = 1; + /* we lower data margin every msec */ + fifo->bit_line -= sink; + if (fifo->bit_line < BITLINE_INF) + fifo->bit_line = BITLINE_INF; + } + + if (frame_complete) { + frame_complete = 0; + + if (debug & DBG_HFC_FIFO_VERBOSE) { + printk(KERN_DEBUG "%s: %s: " + "fifon(%i) new TX len(%i): ", + hw->name, __func__, + fifon, tx_skb->len); + i = 0; + while (i < tx_skb->len) + printk("%02x ", + tx_skb->data[i++]); + printk("\n"); + } + + dev_consume_skb_irq(tx_skb); + tx_skb = NULL; + if (fifo->dch && get_next_dframe(fifo->dch)) + tx_skb = fifo->dch->tx_skb; + else if (fifo->bch && + get_next_bframe(fifo->bch)) + tx_skb = fifo->bch->tx_skb; + } + } + errcode = usb_submit_urb(urb, GFP_ATOMIC); + if (errcode < 0) { + if (debug & DEBUG_HW) + printk(KERN_DEBUG + "%s: %s: error submitting ISO URB: %d \n", + hw->name, __func__, errcode); + } + + /* + * abuse DChannel tx iso completion to trigger NT mode state + * changes tx_iso_complete is assumed to be called every + * fifo->intervall (ms) + */ + if ((fifon == HFCUSB_D_TX) && (hw->protocol == ISDN_P_NT_S0) + && (hw->timers & NT_ACTIVATION_TIMER)) { + if ((--hw->nt_timer) < 0) + schedule_event(&hw->dch, FLG_PHCHANGE); + } + + } else { + if (status && (debug & DBG_HFC_URB_ERROR)) + printk(KERN_DEBUG "%s: %s: urb->status %s (%i)" + "fifonum=%d\n", + hw->name, __func__, + symbolic(urb_errlist, status), status, fifon); + } + spin_unlock_irqrestore(&hw->lock, flags); +} + +/* + * allocs urbs and start isoc transfer with two pending urbs to avoid + * gaps in the transfer chain + */ +static int +start_isoc_chain(struct usb_fifo *fifo, int num_packets_per_urb, + usb_complete_t complete, int packet_size) +{ + struct hfcsusb *hw = fifo->hw; + int i, k, errcode; + + if (debug) + printk(KERN_DEBUG "%s: %s: fifo %i\n", + hw->name, __func__, fifo->fifonum); + + /* allocate Memory for Iso out Urbs */ + for (i = 0; i < 2; i++) { + if (!(fifo->iso[i].urb)) { + fifo->iso[i].urb = + usb_alloc_urb(num_packets_per_urb, GFP_KERNEL); + if (!(fifo->iso[i].urb)) { + printk(KERN_DEBUG + "%s: %s: alloc urb for fifo %i failed", + hw->name, __func__, fifo->fifonum); + continue; + } + fifo->iso[i].owner_fifo = (struct usb_fifo *) fifo; + fifo->iso[i].indx = i; + + /* Init the first iso */ + if (ISO_BUFFER_SIZE >= + (fifo->usb_packet_maxlen * + num_packets_per_urb)) { + fill_isoc_urb(fifo->iso[i].urb, + fifo->hw->dev, fifo->pipe, + fifo->iso[i].buffer, + num_packets_per_urb, + fifo->usb_packet_maxlen, + fifo->intervall, complete, + &fifo->iso[i]); + memset(fifo->iso[i].buffer, 0, + sizeof(fifo->iso[i].buffer)); + + for (k = 0; k < num_packets_per_urb; k++) { + fifo->iso[i].urb-> + iso_frame_desc[k].offset = + k * packet_size; + fifo->iso[i].urb-> + iso_frame_desc[k].length = + packet_size; + } + } else { + printk(KERN_DEBUG + "%s: %s: ISO Buffer size to small!\n", + hw->name, __func__); + } + } + fifo->bit_line = BITLINE_INF; + + errcode = usb_submit_urb(fifo->iso[i].urb, GFP_KERNEL); + fifo->active = (errcode >= 0) ? 1 : 0; + fifo->stop_gracefull = 0; + if (errcode < 0) { + printk(KERN_DEBUG "%s: %s: %s URB nr:%d\n", + hw->name, __func__, + symbolic(urb_errlist, errcode), i); + } + } + return fifo->active; +} + +static void +stop_iso_gracefull(struct usb_fifo *fifo) +{ + struct hfcsusb *hw = fifo->hw; + int i, timeout; + u_long flags; + + for (i = 0; i < 2; i++) { + spin_lock_irqsave(&hw->lock, flags); + if (debug) + printk(KERN_DEBUG "%s: %s for fifo %i.%i\n", + hw->name, __func__, fifo->fifonum, i); + fifo->stop_gracefull = 1; + spin_unlock_irqrestore(&hw->lock, flags); + } + + for (i = 0; i < 2; i++) { + timeout = 3; + while (fifo->stop_gracefull && timeout--) + schedule_timeout_interruptible((HZ / 1000) * 16); + if (debug && fifo->stop_gracefull) + printk(KERN_DEBUG "%s: ERROR %s for fifo %i.%i\n", + hw->name, __func__, fifo->fifonum, i); + } +} + +static void +stop_int_gracefull(struct usb_fifo *fifo) +{ + struct hfcsusb *hw = fifo->hw; + int timeout; + u_long flags; + + spin_lock_irqsave(&hw->lock, flags); + if (debug) + printk(KERN_DEBUG "%s: %s for fifo %i\n", + hw->name, __func__, fifo->fifonum); + fifo->stop_gracefull = 1; + spin_unlock_irqrestore(&hw->lock, flags); + + timeout = 3; + while (fifo->stop_gracefull && timeout--) + schedule_timeout_interruptible((HZ / 1000) * 3); + if (debug && fifo->stop_gracefull) + printk(KERN_DEBUG "%s: ERROR %s for fifo %i\n", + hw->name, __func__, fifo->fifonum); +} + +/* start the interrupt transfer for the given fifo */ +static void +start_int_fifo(struct usb_fifo *fifo) +{ + struct hfcsusb *hw = fifo->hw; + int errcode; + + if (debug) + printk(KERN_DEBUG "%s: %s: INT IN fifo:%d\n", + hw->name, __func__, fifo->fifonum); + + if (!fifo->urb) { + fifo->urb = usb_alloc_urb(0, GFP_KERNEL); + if (!fifo->urb) + return; + } + usb_fill_int_urb(fifo->urb, fifo->hw->dev, fifo->pipe, + fifo->buffer, fifo->usb_packet_maxlen, + (usb_complete_t)rx_int_complete, fifo, fifo->intervall); + fifo->active = 1; + fifo->stop_gracefull = 0; + errcode = usb_submit_urb(fifo->urb, GFP_KERNEL); + if (errcode) { + printk(KERN_DEBUG "%s: %s: submit URB: status:%i\n", + hw->name, __func__, errcode); + fifo->active = 0; + } +} + +static void +setPortMode(struct hfcsusb *hw) +{ + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s %s\n", hw->name, __func__, + (hw->protocol == ISDN_P_TE_S0) ? "TE" : "NT"); + + if (hw->protocol == ISDN_P_TE_S0) { + write_reg(hw, HFCUSB_SCTRL, 0x40); + write_reg(hw, HFCUSB_SCTRL_E, 0x00); + write_reg(hw, HFCUSB_CLKDEL, CLKDEL_TE); + write_reg(hw, HFCUSB_STATES, 3 | 0x10); + write_reg(hw, HFCUSB_STATES, 3); + } else { + write_reg(hw, HFCUSB_SCTRL, 0x44); + write_reg(hw, HFCUSB_SCTRL_E, 0x09); + write_reg(hw, HFCUSB_CLKDEL, CLKDEL_NT); + write_reg(hw, HFCUSB_STATES, 1 | 0x10); + write_reg(hw, HFCUSB_STATES, 1); + } +} + +static void +reset_hfcsusb(struct hfcsusb *hw) +{ + struct usb_fifo *fifo; + int i; + + if (debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + /* do Chip reset */ + write_reg(hw, HFCUSB_CIRM, 8); + + /* aux = output, reset off */ + write_reg(hw, HFCUSB_CIRM, 0x10); + + /* set USB_SIZE to match the wMaxPacketSize for INT or BULK transfers */ + write_reg(hw, HFCUSB_USB_SIZE, (hw->packet_size / 8) | + ((hw->packet_size / 8) << 4)); + + /* set USB_SIZE_I to match the wMaxPacketSize for ISO transfers */ + write_reg(hw, HFCUSB_USB_SIZE_I, hw->iso_packet_size); + + /* enable PCM/GCI master mode */ + write_reg(hw, HFCUSB_MST_MODE1, 0); /* set default values */ + write_reg(hw, HFCUSB_MST_MODE0, 1); /* enable master mode */ + + /* init the fifos */ + write_reg(hw, HFCUSB_F_THRES, + (HFCUSB_TX_THRESHOLD / 8) | ((HFCUSB_RX_THRESHOLD / 8) << 4)); + + fifo = hw->fifos; + for (i = 0; i < HFCUSB_NUM_FIFOS; i++) { + write_reg(hw, HFCUSB_FIFO, i); /* select the desired fifo */ + fifo[i].max_size = + (i <= HFCUSB_B2_RX) ? MAX_BCH_SIZE : MAX_DFRAME_LEN; + fifo[i].last_urblen = 0; + + /* set 2 bit for D- & E-channel */ + write_reg(hw, HFCUSB_HDLC_PAR, ((i <= HFCUSB_B2_RX) ? 0 : 2)); + + /* enable all fifos */ + if (i == HFCUSB_D_TX) + write_reg(hw, HFCUSB_CON_HDLC, + (hw->protocol == ISDN_P_NT_S0) ? 0x08 : 0x09); + else + write_reg(hw, HFCUSB_CON_HDLC, 0x08); + write_reg(hw, HFCUSB_INC_RES_F, 2); /* reset the fifo */ + } + + write_reg(hw, HFCUSB_SCTRL_R, 0); /* disable both B receivers */ + handle_led(hw, LED_POWER_ON); +} + +/* start USB data pipes dependand on device's endpoint configuration */ +static void +hfcsusb_start_endpoint(struct hfcsusb *hw, int channel) +{ + /* quick check if endpoint already running */ + if ((channel == HFC_CHAN_D) && (hw->fifos[HFCUSB_D_RX].active)) + return; + if ((channel == HFC_CHAN_B1) && (hw->fifos[HFCUSB_B1_RX].active)) + return; + if ((channel == HFC_CHAN_B2) && (hw->fifos[HFCUSB_B2_RX].active)) + return; + if ((channel == HFC_CHAN_E) && (hw->fifos[HFCUSB_PCM_RX].active)) + return; + + /* start rx endpoints using USB INT IN method */ + if (hw->cfg_used == CNF_3INT3ISO || hw->cfg_used == CNF_4INT3ISO) + start_int_fifo(hw->fifos + channel * 2 + 1); + + /* start rx endpoints using USB ISO IN method */ + if (hw->cfg_used == CNF_3ISO3ISO || hw->cfg_used == CNF_4ISO3ISO) { + switch (channel) { + case HFC_CHAN_D: + start_isoc_chain(hw->fifos + HFCUSB_D_RX, + ISOC_PACKETS_D, + (usb_complete_t)rx_iso_complete, + 16); + break; + case HFC_CHAN_E: + start_isoc_chain(hw->fifos + HFCUSB_PCM_RX, + ISOC_PACKETS_D, + (usb_complete_t)rx_iso_complete, + 16); + break; + case HFC_CHAN_B1: + start_isoc_chain(hw->fifos + HFCUSB_B1_RX, + ISOC_PACKETS_B, + (usb_complete_t)rx_iso_complete, + 16); + break; + case HFC_CHAN_B2: + start_isoc_chain(hw->fifos + HFCUSB_B2_RX, + ISOC_PACKETS_B, + (usb_complete_t)rx_iso_complete, + 16); + break; + } + } + + /* start tx endpoints using USB ISO OUT method */ + switch (channel) { + case HFC_CHAN_D: + start_isoc_chain(hw->fifos + HFCUSB_D_TX, + ISOC_PACKETS_B, + (usb_complete_t)tx_iso_complete, 1); + break; + case HFC_CHAN_B1: + start_isoc_chain(hw->fifos + HFCUSB_B1_TX, + ISOC_PACKETS_D, + (usb_complete_t)tx_iso_complete, 1); + break; + case HFC_CHAN_B2: + start_isoc_chain(hw->fifos + HFCUSB_B2_TX, + ISOC_PACKETS_B, + (usb_complete_t)tx_iso_complete, 1); + break; + } +} + +/* stop USB data pipes dependand on device's endpoint configuration */ +static void +hfcsusb_stop_endpoint(struct hfcsusb *hw, int channel) +{ + /* quick check if endpoint currently running */ + if ((channel == HFC_CHAN_D) && (!hw->fifos[HFCUSB_D_RX].active)) + return; + if ((channel == HFC_CHAN_B1) && (!hw->fifos[HFCUSB_B1_RX].active)) + return; + if ((channel == HFC_CHAN_B2) && (!hw->fifos[HFCUSB_B2_RX].active)) + return; + if ((channel == HFC_CHAN_E) && (!hw->fifos[HFCUSB_PCM_RX].active)) + return; + + /* rx endpoints using USB INT IN method */ + if (hw->cfg_used == CNF_3INT3ISO || hw->cfg_used == CNF_4INT3ISO) + stop_int_gracefull(hw->fifos + channel * 2 + 1); + + /* rx endpoints using USB ISO IN method */ + if (hw->cfg_used == CNF_3ISO3ISO || hw->cfg_used == CNF_4ISO3ISO) + stop_iso_gracefull(hw->fifos + channel * 2 + 1); + + /* tx endpoints using USB ISO OUT method */ + if (channel != HFC_CHAN_E) + stop_iso_gracefull(hw->fifos + channel * 2); +} + + +/* Hardware Initialization */ +static int +setup_hfcsusb(struct hfcsusb *hw) +{ + void *dmabuf = kmalloc(sizeof(u_char), GFP_KERNEL); + u_char b; + int ret; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + if (!dmabuf) + return -ENOMEM; + + ret = read_reg_atomic(hw, HFCUSB_CHIP_ID, dmabuf); + + memcpy(&b, dmabuf, sizeof(u_char)); + kfree(dmabuf); + + /* check the chip id */ + if (ret != 1) { + printk(KERN_DEBUG "%s: %s: cannot read chip id\n", + hw->name, __func__); + return 1; + } + if (b != HFCUSB_CHIPID) { + printk(KERN_DEBUG "%s: %s: Invalid chip id 0x%02x\n", + hw->name, __func__, b); + return 1; + } + + /* first set the needed config, interface and alternate */ + (void) usb_set_interface(hw->dev, hw->if_used, hw->alt_used); + + hw->led_state = 0; + + /* init the background machinery for control requests */ + hw->ctrl_read.bRequestType = 0xc0; + hw->ctrl_read.bRequest = 1; + hw->ctrl_read.wLength = cpu_to_le16(1); + hw->ctrl_write.bRequestType = 0x40; + hw->ctrl_write.bRequest = 0; + hw->ctrl_write.wLength = 0; + usb_fill_control_urb(hw->ctrl_urb, hw->dev, hw->ctrl_out_pipe, + (u_char *)&hw->ctrl_write, NULL, 0, + (usb_complete_t)ctrl_complete, hw); + + reset_hfcsusb(hw); + return 0; +} + +static void +release_hw(struct hfcsusb *hw) +{ + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + /* + * stop all endpoints gracefully + * TODO: mISDN_core should generate CLOSE_CHANNEL + * signals after calling mISDN_unregister_device() + */ + hfcsusb_stop_endpoint(hw, HFC_CHAN_D); + hfcsusb_stop_endpoint(hw, HFC_CHAN_B1); + hfcsusb_stop_endpoint(hw, HFC_CHAN_B2); + if (hw->fifos[HFCUSB_PCM_RX].pipe) + hfcsusb_stop_endpoint(hw, HFC_CHAN_E); + if (hw->protocol == ISDN_P_TE_S0) + l1_event(hw->dch.l1, CLOSE_CHANNEL); + + mISDN_unregister_device(&hw->dch.dev); + mISDN_freebchannel(&hw->bch[1]); + mISDN_freebchannel(&hw->bch[0]); + mISDN_freedchannel(&hw->dch); + + if (hw->ctrl_urb) { + usb_kill_urb(hw->ctrl_urb); + usb_free_urb(hw->ctrl_urb); + hw->ctrl_urb = NULL; + } + + if (hw->intf) + usb_set_intfdata(hw->intf, NULL); + list_del(&hw->list); + kfree(hw); + hw = NULL; +} + +static void +deactivate_bchannel(struct bchannel *bch) +{ + struct hfcsusb *hw = bch->hw; + u_long flags; + + if (bch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: %s: bch->nr(%i)\n", + hw->name, __func__, bch->nr); + + spin_lock_irqsave(&hw->lock, flags); + mISDN_clear_bchannel(bch); + spin_unlock_irqrestore(&hw->lock, flags); + hfcsusb_setup_bch(bch, ISDN_P_NONE); + hfcsusb_stop_endpoint(hw, bch->nr - 1); +} + +/* + * Layer 1 B-channel hardware access + */ +static int +hfc_bctrl(struct mISDNchannel *ch, u_int cmd, void *arg) +{ + struct bchannel *bch = container_of(ch, struct bchannel, ch); + int ret = -EINVAL; + + if (bch->debug & DEBUG_HW) + printk(KERN_DEBUG "%s: cmd:%x %p\n", __func__, cmd, arg); + + switch (cmd) { + case HW_TESTRX_RAW: + case HW_TESTRX_HDLC: + case HW_TESTRX_OFF: + ret = -EINVAL; + break; + + case CLOSE_CHANNEL: + test_and_clear_bit(FLG_OPEN, &bch->Flags); + deactivate_bchannel(bch); + 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: + printk(KERN_WARNING "%s: unknown prim(%x)\n", + __func__, cmd); + } + return ret; +} + +static int +setup_instance(struct hfcsusb *hw, struct device *parent) +{ + u_long flags; + int err, i; + + if (debug & DBG_HFC_CALL_TRACE) + printk(KERN_DEBUG "%s: %s\n", hw->name, __func__); + + spin_lock_init(&hw->ctrl_lock); + spin_lock_init(&hw->lock); + + mISDN_initdchannel(&hw->dch, MAX_DFRAME_LEN_L1, ph_state); + hw->dch.debug = debug & 0xFFFF; + hw->dch.hw = hw; + hw->dch.dev.Dprotocols = (1 << ISDN_P_TE_S0) | (1 << ISDN_P_NT_S0); + hw->dch.dev.D.send = hfcusb_l2l1D; + hw->dch.dev.D.ctrl = hfc_dctrl; + + /* enable E-Channel logging */ + if (hw->fifos[HFCUSB_PCM_RX].pipe) + mISDN_initdchannel(&hw->ech, MAX_DFRAME_LEN_L1, NULL); + + hw->dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | + (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); + hw->dch.dev.nrbchan = 2; + for (i = 0; i < 2; i++) { + hw->bch[i].nr = i + 1; + set_channelmap(i + 1, hw->dch.dev.channelmap); + hw->bch[i].debug = debug; + mISDN_initbchannel(&hw->bch[i], MAX_DATA_MEM, poll >> 1); + hw->bch[i].hw = hw; + hw->bch[i].ch.send = hfcusb_l2l1B; + hw->bch[i].ch.ctrl = hfc_bctrl; + hw->bch[i].ch.nr = i + 1; + list_add(&hw->bch[i].ch.list, &hw->dch.dev.bchannels); + } + + hw->fifos[HFCUSB_B1_TX].bch = &hw->bch[0]; + hw->fifos[HFCUSB_B1_RX].bch = &hw->bch[0]; + hw->fifos[HFCUSB_B2_TX].bch = &hw->bch[1]; + hw->fifos[HFCUSB_B2_RX].bch = &hw->bch[1]; + hw->fifos[HFCUSB_D_TX].dch = &hw->dch; + hw->fifos[HFCUSB_D_RX].dch = &hw->dch; + hw->fifos[HFCUSB_PCM_RX].ech = &hw->ech; + hw->fifos[HFCUSB_PCM_TX].ech = &hw->ech; + + err = setup_hfcsusb(hw); + if (err) + goto out; + + snprintf(hw->name, MISDN_MAX_IDLEN - 1, "%s.%d", DRIVER_NAME, + hfcsusb_cnt + 1); + printk(KERN_INFO "%s: registered as '%s'\n", + DRIVER_NAME, hw->name); + + err = mISDN_register_device(&hw->dch.dev, parent, hw->name); + if (err) + goto out; + + hfcsusb_cnt++; + write_lock_irqsave(&HFClock, flags); + list_add_tail(&hw->list, &HFClist); + write_unlock_irqrestore(&HFClock, flags); + return 0; + +out: + mISDN_freebchannel(&hw->bch[1]); + mISDN_freebchannel(&hw->bch[0]); + mISDN_freedchannel(&hw->dch); + kfree(hw); + return err; +} + +static int +hfcsusb_probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + struct hfcsusb *hw; + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_host_interface *iface = intf->cur_altsetting; + struct usb_host_interface *iface_used = NULL; + struct usb_host_endpoint *ep; + struct hfcsusb_vdata *driver_info; + int ifnum = iface->desc.bInterfaceNumber, i, idx, alt_idx, + probe_alt_setting, vend_idx, cfg_used, *vcf, attr, cfg_found, + ep_addr, cmptbl[16], small_match, iso_packet_size, packet_size, + alt_used = 0; + + vend_idx = 0xffff; + for (i = 0; hfcsusb_idtab[i].idVendor; i++) { + if ((le16_to_cpu(dev->descriptor.idVendor) + == hfcsusb_idtab[i].idVendor) && + (le16_to_cpu(dev->descriptor.idProduct) + == hfcsusb_idtab[i].idProduct)) { + vend_idx = i; + continue; + } + } + + printk(KERN_DEBUG + "%s: interface(%d) actalt(%d) minor(%d) vend_idx(%d)\n", + __func__, ifnum, iface->desc.bAlternateSetting, + intf->minor, vend_idx); + + if (vend_idx == 0xffff) { + printk(KERN_WARNING + "%s: no valid vendor found in USB descriptor\n", + __func__); + return -EIO; + } + /* if vendor and product ID is OK, start probing alternate settings */ + alt_idx = 0; + small_match = -1; + + /* default settings */ + iso_packet_size = 16; + packet_size = 64; + + while (alt_idx < intf->num_altsetting) { + iface = intf->altsetting + alt_idx; + probe_alt_setting = iface->desc.bAlternateSetting; + cfg_used = 0; + + while (validconf[cfg_used][0]) { + cfg_found = 1; + vcf = validconf[cfg_used]; + ep = iface->endpoint; + memcpy(cmptbl, vcf, 16 * sizeof(int)); + + /* check for all endpoints in this alternate setting */ + for (i = 0; i < iface->desc.bNumEndpoints; i++) { + ep_addr = ep->desc.bEndpointAddress; + + /* get endpoint base */ + idx = ((ep_addr & 0x7f) - 1) * 2; + if (idx > 15) + return -EIO; + + if (ep_addr & 0x80) + idx++; + attr = ep->desc.bmAttributes; + + if (cmptbl[idx] != EP_NOP) { + if (cmptbl[idx] == EP_NUL) + cfg_found = 0; + if (attr == USB_ENDPOINT_XFER_INT + && cmptbl[idx] == EP_INT) + cmptbl[idx] = EP_NUL; + if (attr == USB_ENDPOINT_XFER_BULK + && cmptbl[idx] == EP_BLK) + cmptbl[idx] = EP_NUL; + if (attr == USB_ENDPOINT_XFER_ISOC + && cmptbl[idx] == EP_ISO) + cmptbl[idx] = EP_NUL; + + if (attr == USB_ENDPOINT_XFER_INT && + ep->desc.bInterval < vcf[17]) { + cfg_found = 0; + } + } + ep++; + } + + for (i = 0; i < 16; i++) + if (cmptbl[i] != EP_NOP && cmptbl[i] != EP_NUL) + cfg_found = 0; + + if (cfg_found) { + if (small_match < cfg_used) { + small_match = cfg_used; + alt_used = probe_alt_setting; + iface_used = iface; + } + } + cfg_used++; + } + alt_idx++; + } /* (alt_idx < intf->num_altsetting) */ + + /* not found a valid USB Ta Endpoint config */ + if (small_match == -1) + return -EIO; + + iface = iface_used; + hw = kzalloc(sizeof(struct hfcsusb), GFP_KERNEL); + if (!hw) + return -ENOMEM; /* got no mem */ + snprintf(hw->name, MISDN_MAX_IDLEN - 1, "%s", DRIVER_NAME); + + ep = iface->endpoint; + vcf = validconf[small_match]; + + for (i = 0; i < iface->desc.bNumEndpoints; i++) { + struct usb_fifo *f; + + ep_addr = ep->desc.bEndpointAddress; + /* get endpoint base */ + idx = ((ep_addr & 0x7f) - 1) * 2; + if (ep_addr & 0x80) + idx++; + f = &hw->fifos[idx & 7]; + + /* init Endpoints */ + if (vcf[idx] == EP_NOP || vcf[idx] == EP_NUL) { + ep++; + continue; + } + switch (ep->desc.bmAttributes) { + case USB_ENDPOINT_XFER_INT: + f->pipe = usb_rcvintpipe(dev, + ep->desc.bEndpointAddress); + f->usb_transfer_mode = USB_INT; + packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); + break; + case USB_ENDPOINT_XFER_BULK: + if (ep_addr & 0x80) + f->pipe = usb_rcvbulkpipe(dev, + ep->desc.bEndpointAddress); + else + f->pipe = usb_sndbulkpipe(dev, + ep->desc.bEndpointAddress); + f->usb_transfer_mode = USB_BULK; + packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); + break; + case USB_ENDPOINT_XFER_ISOC: + if (ep_addr & 0x80) + f->pipe = usb_rcvisocpipe(dev, + ep->desc.bEndpointAddress); + else + f->pipe = usb_sndisocpipe(dev, + ep->desc.bEndpointAddress); + f->usb_transfer_mode = USB_ISOC; + iso_packet_size = le16_to_cpu(ep->desc.wMaxPacketSize); + break; + default: + f->pipe = 0; + } + + if (f->pipe) { + f->fifonum = idx & 7; + f->hw = hw; + f->usb_packet_maxlen = + le16_to_cpu(ep->desc.wMaxPacketSize); + f->intervall = ep->desc.bInterval; + } + ep++; + } + hw->dev = dev; /* save device */ + hw->if_used = ifnum; /* save used interface */ + hw->alt_used = alt_used; /* and alternate config */ + hw->ctrl_paksize = dev->descriptor.bMaxPacketSize0; /* control size */ + hw->cfg_used = vcf[16]; /* store used config */ + hw->vend_idx = vend_idx; /* store found vendor */ + hw->packet_size = packet_size; + hw->iso_packet_size = iso_packet_size; + + /* create the control pipes needed for register access */ + hw->ctrl_in_pipe = usb_rcvctrlpipe(hw->dev, 0); + hw->ctrl_out_pipe = usb_sndctrlpipe(hw->dev, 0); + + driver_info = (struct hfcsusb_vdata *) + hfcsusb_idtab[vend_idx].driver_info; + + hw->ctrl_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!hw->ctrl_urb) { + pr_warn("%s: No memory for control urb\n", + driver_info->vend_name); + kfree(hw); + return -ENOMEM; + } + + pr_info("%s: %s: detected \"%s\" (%s, if=%d alt=%d)\n", + hw->name, __func__, driver_info->vend_name, + conf_str[small_match], ifnum, alt_used); + + if (setup_instance(hw, dev->dev.parent)) + return -EIO; + + hw->intf = intf; + usb_set_intfdata(hw->intf, hw); + return 0; +} + +/* function called when an active device is removed */ +static void +hfcsusb_disconnect(struct usb_interface *intf) +{ + struct hfcsusb *hw = usb_get_intfdata(intf); + struct hfcsusb *next; + int cnt = 0; + + printk(KERN_INFO "%s: device disconnected\n", hw->name); + + handle_led(hw, LED_POWER_OFF); + release_hw(hw); + + list_for_each_entry_safe(hw, next, &HFClist, list) + cnt++; + if (!cnt) + hfcsusb_cnt = 0; + + usb_set_intfdata(intf, NULL); +} + +static struct usb_driver hfcsusb_drv = { + .name = DRIVER_NAME, + .id_table = hfcsusb_idtab, + .probe = hfcsusb_probe, + .disconnect = hfcsusb_disconnect, + .disable_hub_initiated_lpm = 1, +}; + +module_usb_driver(hfcsusb_drv); diff --git a/drivers/isdn/hardware/mISDN/hfcsusb.h b/drivers/isdn/hardware/mISDN/hfcsusb.h new file mode 100644 index 000000000..7e2bc5068 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/hfcsusb.h @@ -0,0 +1,425 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * hfcsusb.h, HFC-S USB mISDN driver + */ + +#ifndef __HFCSUSB_H__ +#define __HFCSUSB_H__ + + +#define DRIVER_NAME "HFC-S_USB" + +#define DBG_HFC_CALL_TRACE 0x00010000 +#define DBG_HFC_FIFO_VERBOSE 0x00020000 +#define DBG_HFC_USB_VERBOSE 0x00100000 +#define DBG_HFC_URB_INFO 0x00200000 +#define DBG_HFC_URB_ERROR 0x00400000 + +#define DEFAULT_TRANSP_BURST_SZ 128 + +#define HFC_CTRL_TIMEOUT 20 /* 5ms timeout writing/reading regs */ +#define CLKDEL_TE 0x0f /* CLKDEL in TE mode */ +#define CLKDEL_NT 0x6c /* CLKDEL in NT mode */ + +/* hfcsusb Layer1 commands */ +#define HFC_L1_ACTIVATE_TE 1 +#define HFC_L1_ACTIVATE_NT 2 +#define HFC_L1_DEACTIVATE_NT 3 +#define HFC_L1_FORCE_DEACTIVATE_TE 4 + +/* cmd FLAGS in HFCUSB_STATES register */ +#define HFCUSB_LOAD_STATE 0x10 +#define HFCUSB_ACTIVATE 0x20 +#define HFCUSB_DO_ACTION 0x40 +#define HFCUSB_NT_G2_G3 0x80 + +/* timers */ +#define NT_ACTIVATION_TIMER 0x01 /* enables NT mode activation Timer */ +#define NT_T1_COUNT 10 + +#define MAX_BCH_SIZE 2048 /* allowed B-channel packet size */ + +#define HFCUSB_RX_THRESHOLD 64 /* threshold for fifo report bit rx */ +#define HFCUSB_TX_THRESHOLD 96 /* threshold for fifo report bit tx */ + +#define HFCUSB_CHIP_ID 0x16 /* Chip ID register index */ +#define HFCUSB_CIRM 0x00 /* cirm register index */ +#define HFCUSB_USB_SIZE 0x07 /* int length register */ +#define HFCUSB_USB_SIZE_I 0x06 /* iso length register */ +#define HFCUSB_F_CROSS 0x0b /* bit order register */ +#define HFCUSB_CLKDEL 0x37 /* bit delay register */ +#define HFCUSB_CON_HDLC 0xfa /* channel connect register */ +#define HFCUSB_HDLC_PAR 0xfb +#define HFCUSB_SCTRL 0x31 /* S-bus control register (tx) */ +#define HFCUSB_SCTRL_E 0x32 /* same for E and special funcs */ +#define HFCUSB_SCTRL_R 0x33 /* S-bus control register (rx) */ +#define HFCUSB_F_THRES 0x0c /* threshold register */ +#define HFCUSB_FIFO 0x0f /* fifo select register */ +#define HFCUSB_F_USAGE 0x1a /* fifo usage register */ +#define HFCUSB_MST_MODE0 0x14 +#define HFCUSB_MST_MODE1 0x15 +#define HFCUSB_P_DATA 0x1f +#define HFCUSB_INC_RES_F 0x0e +#define HFCUSB_B1_SSL 0x20 +#define HFCUSB_B2_SSL 0x21 +#define HFCUSB_B1_RSL 0x24 +#define HFCUSB_B2_RSL 0x25 +#define HFCUSB_STATES 0x30 + + +#define HFCUSB_CHIPID 0x40 /* ID value of HFC-S USB */ + +/* fifo registers */ +#define HFCUSB_NUM_FIFOS 8 /* maximum number of fifos */ +#define HFCUSB_B1_TX 0 /* index for B1 transmit bulk/int */ +#define HFCUSB_B1_RX 1 /* index for B1 receive bulk/int */ +#define HFCUSB_B2_TX 2 +#define HFCUSB_B2_RX 3 +#define HFCUSB_D_TX 4 +#define HFCUSB_D_RX 5 +#define HFCUSB_PCM_TX 6 +#define HFCUSB_PCM_RX 7 + + +#define USB_INT 0 +#define USB_BULK 1 +#define USB_ISOC 2 + +#define ISOC_PACKETS_D 8 +#define ISOC_PACKETS_B 8 +#define ISO_BUFFER_SIZE 128 + +/* defines how much ISO packets are handled in one URB */ +static int iso_packets[8] = +{ ISOC_PACKETS_B, ISOC_PACKETS_B, ISOC_PACKETS_B, ISOC_PACKETS_B, + ISOC_PACKETS_D, ISOC_PACKETS_D, ISOC_PACKETS_D, ISOC_PACKETS_D +}; + + +/* Fifo flow Control for TX ISO */ +#define SINK_MAX 68 +#define SINK_MIN 48 +#define SINK_DMIN 12 +#define SINK_DMAX 18 +#define BITLINE_INF (-96 * 8) + +/* HFC-S USB register access by Control-URSs */ +#define write_reg_atomic(a, b, c) \ + usb_control_msg((a)->dev, (a)->ctrl_out_pipe, 0, 0x40, (c), (b), \ + 0, 0, HFC_CTRL_TIMEOUT) +#define read_reg_atomic(a, b, c) \ + usb_control_msg((a)->dev, (a)->ctrl_in_pipe, 1, 0xC0, 0, (b), (c), \ + 1, HFC_CTRL_TIMEOUT) +#define HFC_CTRL_BUFSIZE 64 + +struct ctrl_buf { + __u8 hfcs_reg; /* register number */ + __u8 reg_val; /* value to be written (or read) */ +}; + +/* + * URB error codes + * Used to represent a list of values and their respective symbolic names + */ +struct hfcusb_symbolic_list { + const int num; + const char *name; +}; + +static struct hfcusb_symbolic_list urb_errlist[] = { + {-ENOMEM, "No memory for allocation of internal structures"}, + {-ENOSPC, "The host controller's bandwidth is already consumed"}, + {-ENOENT, "URB was canceled by unlink_urb"}, + {-EXDEV, "ISO transfer only partially completed"}, + {-EAGAIN, "Too match scheduled for the future"}, + {-ENXIO, "URB already queued"}, + {-EFBIG, "Too much ISO frames requested"}, + {-ENOSR, "Buffer error (overrun)"}, + {-EPIPE, "Specified endpoint is stalled (device not responding)"}, + {-EOVERFLOW, "Babble (bad cable?)"}, + {-EPROTO, "Bit-stuff error (bad cable?)"}, + {-EILSEQ, "CRC/Timeout"}, + {-ETIMEDOUT, "NAK (device does not respond)"}, + {-ESHUTDOWN, "Device unplugged"}, + {-1, NULL} +}; + +static inline const char * +symbolic(struct hfcusb_symbolic_list list[], const int num) +{ + int i; + for (i = 0; list[i].name != NULL; i++) + if (list[i].num == num) + return list[i].name; + return "<unknown USB Error>"; +} + +/* USB descriptor need to contain one of the following EndPoint combination: */ +#define CNF_4INT3ISO 1 /* 4 INT IN, 3 ISO OUT */ +#define CNF_3INT3ISO 2 /* 3 INT IN, 3 ISO OUT */ +#define CNF_4ISO3ISO 3 /* 4 ISO IN, 3 ISO OUT */ +#define CNF_3ISO3ISO 4 /* 3 ISO IN, 3 ISO OUT */ + +#define EP_NUL 1 /* Endpoint at this position not allowed */ +#define EP_NOP 2 /* all type of endpoints allowed at this position */ +#define EP_ISO 3 /* Isochron endpoint mandatory at this position */ +#define EP_BLK 4 /* Bulk endpoint mandatory at this position */ +#define EP_INT 5 /* Interrupt endpoint mandatory at this position */ + +#define HFC_CHAN_B1 0 +#define HFC_CHAN_B2 1 +#define HFC_CHAN_D 2 +#define HFC_CHAN_E 3 + + +/* + * List of all supported endpoint configuration sets, used to find the + * best matching endpoint configuration within a device's USB descriptor. + * We need at least 3 RX endpoints, and 3 TX endpoints, either + * INT-in and ISO-out, or ISO-in and ISO-out) + * with 4 RX endpoints even E-Channel logging is possible + */ +static int +validconf[][19] = { + /* INT in, ISO out config */ + {EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NOP, EP_INT, + EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL, + CNF_4INT3ISO, 2, 1}, + {EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_INT, EP_NUL, EP_NUL, + EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_ISO, EP_NUL, EP_NUL, EP_NUL, + CNF_3INT3ISO, 2, 0}, + /* ISO in, ISO out config */ + {EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, EP_NOP, + EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NOP, EP_ISO, + CNF_4ISO3ISO, 2, 1}, + {EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, EP_NUL, + EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_ISO, EP_NUL, EP_NUL, + CNF_3ISO3ISO, 2, 0}, + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} /* EOL element */ +}; + +/* string description of chosen config */ +static char *conf_str[] = { + "4 Interrupt IN + 3 Isochron OUT", + "3 Interrupt IN + 3 Isochron OUT", + "4 Isochron IN + 3 Isochron OUT", + "3 Isochron IN + 3 Isochron OUT" +}; + + +#define LED_OFF 0 /* no LED support */ +#define LED_SCHEME1 1 /* LED standard scheme */ +#define LED_SCHEME2 2 /* not used yet... */ + +#define LED_POWER_ON 1 +#define LED_POWER_OFF 2 +#define LED_S0_ON 3 +#define LED_S0_OFF 4 +#define LED_B1_ON 5 +#define LED_B1_OFF 6 +#define LED_B1_DATA 7 +#define LED_B2_ON 8 +#define LED_B2_OFF 9 +#define LED_B2_DATA 10 + +#define LED_NORMAL 0 /* LEDs are normal */ +#define LED_INVERTED 1 /* LEDs are inverted */ + +/* time in ms to perform a Flashing LED when B-Channel has traffic */ +#define LED_TIME 250 + + + +struct hfcsusb; +struct usb_fifo; + +/* structure defining input+output fifos (interrupt/bulk mode) */ +struct iso_urb { + struct urb *urb; + __u8 buffer[ISO_BUFFER_SIZE]; /* buffer rx/tx USB URB data */ + struct usb_fifo *owner_fifo; /* pointer to owner fifo */ + __u8 indx; /* Fifos's ISO double buffer 0 or 1 ? */ +#ifdef ISO_FRAME_START_DEBUG + int start_frames[ISO_FRAME_START_RING_COUNT]; + __u8 iso_frm_strt_pos; /* index in start_frame[] */ +#endif +}; + +struct usb_fifo { + int fifonum; /* fifo index attached to this structure */ + int active; /* fifo is currently active */ + struct hfcsusb *hw; /* pointer to main structure */ + int pipe; /* address of endpoint */ + __u8 usb_packet_maxlen; /* maximum length for usb transfer */ + unsigned int max_size; /* maximum size of receive/send packet */ + __u8 intervall; /* interrupt interval */ + struct urb *urb; /* transfer structure for usb routines */ + __u8 buffer[128]; /* buffer USB INT OUT URB data */ + int bit_line; /* how much bits are in the fifo? */ + + __u8 usb_transfer_mode; /* switched between ISO and INT */ + struct iso_urb iso[2]; /* two urbs to have one always + one pending */ + + struct dchannel *dch; /* link to hfcsusb_t->dch */ + struct bchannel *bch; /* link to hfcsusb_t->bch */ + struct dchannel *ech; /* link to hfcsusb_t->ech, TODO: E-CHANNEL */ + int last_urblen; /* remember length of last packet */ + __u8 stop_gracefull; /* stops URB retransmission */ +}; + +struct hfcsusb { + struct list_head list; + struct dchannel dch; + struct bchannel bch[2]; + struct dchannel ech; /* TODO : wait for struct echannel ;) */ + + struct usb_device *dev; /* our device */ + struct usb_interface *intf; /* used interface */ + int if_used; /* used interface number */ + int alt_used; /* used alternate config */ + int cfg_used; /* configuration index used */ + int vend_idx; /* index in hfcsusb_idtab */ + int packet_size; + int iso_packet_size; + struct usb_fifo fifos[HFCUSB_NUM_FIFOS]; + + /* control pipe background handling */ + struct ctrl_buf ctrl_buff[HFC_CTRL_BUFSIZE]; + int ctrl_in_idx, ctrl_out_idx, ctrl_cnt; + struct urb *ctrl_urb; + struct usb_ctrlrequest ctrl_write; + struct usb_ctrlrequest ctrl_read; + int ctrl_paksize; + int ctrl_in_pipe, ctrl_out_pipe; + spinlock_t ctrl_lock; /* lock for ctrl */ + spinlock_t lock; + + __u8 threshold_mask; + __u8 led_state; + + __u8 protocol; + int nt_timer; + int open; + __u8 timers; + __u8 initdone; + char name[MISDN_MAX_IDLEN]; +}; + +/* private vendor specific data */ +struct hfcsusb_vdata { + __u8 led_scheme; /* led display scheme */ + signed short led_bits[8]; /* array of 8 possible LED bitmask */ + char *vend_name; /* device name */ +}; + + +#define HFC_MAX_TE_LAYER1_STATE 8 +#define HFC_MAX_NT_LAYER1_STATE 4 + +static const char *HFC_TE_LAYER1_STATES[HFC_MAX_TE_LAYER1_STATE + 1] = { + "TE F0 - Reset", + "TE F1 - Reset", + "TE F2 - Sensing", + "TE F3 - Deactivated", + "TE F4 - Awaiting signal", + "TE F5 - Identifying input", + "TE F6 - Synchronized", + "TE F7 - Activated", + "TE F8 - Lost framing", +}; + +static const char *HFC_NT_LAYER1_STATES[HFC_MAX_NT_LAYER1_STATE + 1] = { + "NT G0 - Reset", + "NT G1 - Deactive", + "NT G2 - Pending activation", + "NT G3 - Active", + "NT G4 - Pending deactivation", +}; + +/* supported devices */ +static const struct usb_device_id hfcsusb_idtab[] = { + { + USB_DEVICE(0x0959, 0x2bd0), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_OFF, {4, 0, 2, 1}, + "ISDN USB TA (Cologne Chip HFC-S USB based)"}), + }, + { + USB_DEVICE(0x0675, 0x1688), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {1, 2, 0, 0}, + "DrayTek miniVigor 128 USB ISDN TA"}), + }, + { + USB_DEVICE(0x07b0, 0x0007), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x80, -64, -32, -16}, + "Billion tiny USB ISDN TA 128"}), + }, + { + USB_DEVICE(0x0742, 0x2008), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {4, 0, 2, 1}, + "Stollmann USB TA"}), + }, + { + USB_DEVICE(0x0742, 0x2009), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {4, 0, 2, 1}, + "Aceex USB ISDN TA"}), + }, + { + USB_DEVICE(0x0742, 0x200A), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {4, 0, 2, 1}, + "OEM USB ISDN TA"}), + }, + { + USB_DEVICE(0x08e3, 0x0301), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {2, 0, 1, 4}, + "Olitec USB RNIS"}), + }, + { + USB_DEVICE(0x07fa, 0x0846), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x80, -64, -32, -16}, + "Bewan Modem RNIS USB"}), + }, + { + USB_DEVICE(0x07fa, 0x0847), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x80, -64, -32, -16}, + "Djinn Numeris USB"}), + }, + { + USB_DEVICE(0x07b0, 0x0006), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x80, -64, -32, -16}, + "Twister ISDN TA"}), + }, + { + USB_DEVICE(0x071d, 0x1005), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x02, 0, 0x01, 0x04}, + "Eicon DIVA USB 4.0"}), + }, + { + USB_DEVICE(0x0586, 0x0102), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x88, -64, -32, -16}, + "ZyXEL OMNI.NET USB II"}), + }, + { + USB_DEVICE(0x1ae7, 0x0525), + .driver_info = (unsigned long) &((struct hfcsusb_vdata) + {LED_SCHEME1, {0x88, -64, -32, -16}, + "X-Tensions USB ISDN TA XC-525"}), + }, + { } +}; + +MODULE_DEVICE_TABLE(usb, hfcsusb_idtab); + +#endif /* __HFCSUSB_H__ */ diff --git a/drivers/isdn/hardware/mISDN/iohelper.h b/drivers/isdn/hardware/mISDN/iohelper.h new file mode 100644 index 000000000..c81f7aba4 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/iohelper.h @@ -0,0 +1,96 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * 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> + */ + +#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 000000000..2f0c4978a --- /dev/null +++ b/drivers/isdn/hardware/mISDN/ipac.h @@ -0,0 +1,393 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * + * 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> + */ + +#include "iohelper.h" + +struct isac_hw { + struct dchannel dch; + u32 type; + u32 off; /* offset to isac regs */ + char *name; + spinlock_t *hwlock; /* lock HW access */ + 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 access */ + 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_ISTA 0x20 +#define ISAC_MASK 0x20 +#define ISAC_CMDR 0x21 +#define ISAC_STAR 0x21 +#define ISAC_MODE 0x22 +#define ISAC_TIMR 0x23 +#define ISAC_EXIR 0x24 +#define ISAC_RBCL 0x25 +#define ISAC_RSTA 0x27 +#define ISAC_RBCH 0x2A +#define ISAC_SPCR 0x30 +#define ISAC_CIR0 0x31 +#define ISAC_CIX0 0x31 +#define ISAC_MOR0 0x32 +#define ISAC_MOX0 0x32 +#define ISAC_CIR1 0x33 +#define ISAC_CIX1 0x33 +#define ISAC_MOR1 0x34 +#define ISAC_MOX1 0x34 +#define ISAC_STCR 0x37 +#define ISAC_ADF1 0x38 +#define ISAC_ADF2 0x39 +#define ISAC_MOCR 0x3a +#define ISAC_MOSR 0x3a +#define ISAC_SQRR 0x3b +#define ISAC_SQXR 0x3b + +#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_DR 0x0 +#define ISAC_IND_RS 0x1 +#define ISAC_IND_SD 0x2 +#define ISAC_IND_DIS 0x3 +#define ISAC_IND_RSY 0x4 +#define ISAC_IND_DR6 0x5 +#define ISAC_IND_EI 0x6 +#define ISAC_IND_PU 0x7 +#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__ON 0x2C +#define IPACX__ICB 0x40 +#define IPACX__ICA 0x80 + +/* 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 000000000..36a9fa564 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/isar.h @@ -0,0 +1,256 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * + * isar.h ISAR (Siemens PSB 7110) specific defines + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * Copyright 2009 by Karsten Keil <keil@isdn4linux.de> + */ + +#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 access */ + 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/isdnhdlc.c b/drivers/isdn/hardware/mISDN/isdnhdlc.c new file mode 100644 index 000000000..985367e67 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/isdnhdlc.c @@ -0,0 +1,617 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * isdnhdlc.c -- General purpose ISDN HDLC decoder. + * + * Copyright (C) + * 2009 Karsten Keil <keil@b1-systems.de> + * 2002 Wolfgang Mües <wolfgang@iksw-muees.de> + * 2001 Frode Isaksen <fisaksen@bewan.com> + * 2001 Kai Germaschewski <kai.germaschewski@gmx.de> + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/crc-ccitt.h> +#include <linux/bitrev.h> +#include "isdnhdlc.h" + +/*-------------------------------------------------------------------*/ + +MODULE_AUTHOR("Wolfgang Mües <wolfgang@iksw-muees.de>, " + "Frode Isaksen <fisaksen@bewan.com>, " + "Kai Germaschewski <kai.germaschewski@gmx.de>"); +MODULE_DESCRIPTION("General purpose ISDN HDLC decoder"); +MODULE_LICENSE("GPL"); + +/*-------------------------------------------------------------------*/ + +enum { + HDLC_FAST_IDLE, HDLC_GET_FLAG_B0, HDLC_GETFLAG_B1A6, HDLC_GETFLAG_B7, + HDLC_GET_DATA, HDLC_FAST_FLAG +}; + +enum { + HDLC_SEND_DATA, HDLC_SEND_CRC1, HDLC_SEND_FAST_FLAG, + HDLC_SEND_FIRST_FLAG, HDLC_SEND_CRC2, HDLC_SEND_CLOSING_FLAG, + HDLC_SEND_IDLE1, HDLC_SEND_FAST_IDLE, HDLC_SENDFLAG_B0, + HDLC_SENDFLAG_B1A6, HDLC_SENDFLAG_B7, STOPPED, HDLC_SENDFLAG_ONE +}; + +void isdnhdlc_rcv_init(struct isdnhdlc_vars *hdlc, u32 features) +{ + memset(hdlc, 0, sizeof(struct isdnhdlc_vars)); + hdlc->state = HDLC_GET_DATA; + if (features & HDLC_56KBIT) + hdlc->do_adapt56 = 1; + if (features & HDLC_BITREVERSE) + hdlc->do_bitreverse = 1; +} +EXPORT_SYMBOL(isdnhdlc_out_init); + +void isdnhdlc_out_init(struct isdnhdlc_vars *hdlc, u32 features) +{ + memset(hdlc, 0, sizeof(struct isdnhdlc_vars)); + if (features & HDLC_DCHANNEL) { + hdlc->dchannel = 1; + hdlc->state = HDLC_SEND_FIRST_FLAG; + } else { + hdlc->dchannel = 0; + hdlc->state = HDLC_SEND_FAST_FLAG; + hdlc->ffvalue = 0x7e; + } + hdlc->cbin = 0x7e; + if (features & HDLC_56KBIT) { + hdlc->do_adapt56 = 1; + hdlc->state = HDLC_SENDFLAG_B0; + } else + hdlc->data_bits = 8; + if (features & HDLC_BITREVERSE) + hdlc->do_bitreverse = 1; +} +EXPORT_SYMBOL(isdnhdlc_rcv_init); + +static int +check_frame(struct isdnhdlc_vars *hdlc) +{ + int status; + + if (hdlc->dstpos < 2) /* too small - framing error */ + status = -HDLC_FRAMING_ERROR; + else if (hdlc->crc != 0xf0b8) /* crc error */ + status = -HDLC_CRC_ERROR; + else { + /* remove CRC */ + hdlc->dstpos -= 2; + /* good frame */ + status = hdlc->dstpos; + } + return status; +} + +/* + isdnhdlc_decode - decodes HDLC frames from a transparent bit stream. + + The source buffer is scanned for valid HDLC frames looking for + flags (01111110) to indicate the start of a frame. If the start of + the frame is found, the bit stuffing is removed (0 after 5 1's). + When a new flag is found, the complete frame has been received + and the CRC is checked. + If a valid frame is found, the function returns the frame length + excluding the CRC with the bit HDLC_END_OF_FRAME set. + If the beginning of a valid frame is found, the function returns + the length. + If a framing error is found (too many 1s and not a flag) the function + returns the length with the bit HDLC_FRAMING_ERROR set. + If a CRC error is found the function returns the length with the + bit HDLC_CRC_ERROR set. + If the frame length exceeds the destination buffer size, the function + returns the length with the bit HDLC_LENGTH_ERROR set. + + src - source buffer + slen - source buffer length + count - number of bytes removed (decoded) from the source buffer + dst _ destination buffer + dsize - destination buffer size + returns - number of decoded bytes in the destination buffer and status + flag. +*/ +int isdnhdlc_decode(struct isdnhdlc_vars *hdlc, const u8 *src, int slen, + int *count, u8 *dst, int dsize) +{ + int status = 0; + + static const unsigned char fast_flag[] = { + 0x00, 0x00, 0x00, 0x20, 0x30, 0x38, 0x3c, 0x3e, 0x3f + }; + + static const unsigned char fast_flag_value[] = { + 0x00, 0x7e, 0xfc, 0xf9, 0xf3, 0xe7, 0xcf, 0x9f, 0x3f + }; + + static const unsigned char fast_abort[] = { + 0x00, 0x00, 0x80, 0xc0, 0xe0, 0xf0, 0xf8, 0xfc, 0xfe, 0xff + }; + +#define handle_fast_flag(h) \ + do { \ + if (h->cbin == fast_flag[h->bit_shift]) { \ + h->ffvalue = fast_flag_value[h->bit_shift]; \ + h->state = HDLC_FAST_FLAG; \ + h->ffbit_shift = h->bit_shift; \ + h->bit_shift = 1; \ + } else { \ + h->state = HDLC_GET_DATA; \ + h->data_received = 0; \ + } \ + } while (0) + +#define handle_abort(h) \ + do { \ + h->shift_reg = fast_abort[h->ffbit_shift - 1]; \ + h->hdlc_bits1 = h->ffbit_shift - 2; \ + if (h->hdlc_bits1 < 0) \ + h->hdlc_bits1 = 0; \ + h->data_bits = h->ffbit_shift - 1; \ + h->state = HDLC_GET_DATA; \ + h->data_received = 0; \ + } while (0) + + *count = slen; + + while (slen > 0) { + if (hdlc->bit_shift == 0) { + /* the code is for bitreverse streams */ + if (hdlc->do_bitreverse == 0) + hdlc->cbin = bitrev8(*src++); + else + hdlc->cbin = *src++; + slen--; + hdlc->bit_shift = 8; + if (hdlc->do_adapt56) + hdlc->bit_shift--; + } + + switch (hdlc->state) { + case STOPPED: + return 0; + case HDLC_FAST_IDLE: + if (hdlc->cbin == 0xff) { + hdlc->bit_shift = 0; + break; + } + hdlc->state = HDLC_GET_FLAG_B0; + hdlc->hdlc_bits1 = 0; + hdlc->bit_shift = 8; + break; + case HDLC_GET_FLAG_B0: + if (!(hdlc->cbin & 0x80)) { + hdlc->state = HDLC_GETFLAG_B1A6; + hdlc->hdlc_bits1 = 0; + } else { + if ((!hdlc->do_adapt56) && + (++hdlc->hdlc_bits1 >= 8) && + (hdlc->bit_shift == 1)) + hdlc->state = HDLC_FAST_IDLE; + } + hdlc->cbin <<= 1; + hdlc->bit_shift--; + break; + case HDLC_GETFLAG_B1A6: + if (hdlc->cbin & 0x80) { + hdlc->hdlc_bits1++; + if (hdlc->hdlc_bits1 == 6) + hdlc->state = HDLC_GETFLAG_B7; + } else + hdlc->hdlc_bits1 = 0; + hdlc->cbin <<= 1; + hdlc->bit_shift--; + break; + case HDLC_GETFLAG_B7: + if (hdlc->cbin & 0x80) { + hdlc->state = HDLC_GET_FLAG_B0; + } else { + hdlc->state = HDLC_GET_DATA; + hdlc->crc = 0xffff; + hdlc->shift_reg = 0; + hdlc->hdlc_bits1 = 0; + hdlc->data_bits = 0; + hdlc->data_received = 0; + } + hdlc->cbin <<= 1; + hdlc->bit_shift--; + break; + case HDLC_GET_DATA: + if (hdlc->cbin & 0x80) { + hdlc->hdlc_bits1++; + switch (hdlc->hdlc_bits1) { + case 6: + break; + case 7: + if (hdlc->data_received) + /* bad frame */ + status = -HDLC_FRAMING_ERROR; + if (!hdlc->do_adapt56) { + if (hdlc->cbin == fast_abort + [hdlc->bit_shift + 1]) { + hdlc->state = + HDLC_FAST_IDLE; + hdlc->bit_shift = 1; + break; + } + } else + hdlc->state = HDLC_GET_FLAG_B0; + break; + default: + hdlc->shift_reg >>= 1; + hdlc->shift_reg |= 0x80; + hdlc->data_bits++; + break; + } + } else { + switch (hdlc->hdlc_bits1) { + case 5: + break; + case 6: + if (hdlc->data_received) + status = check_frame(hdlc); + hdlc->crc = 0xffff; + hdlc->shift_reg = 0; + hdlc->data_bits = 0; + if (!hdlc->do_adapt56) + handle_fast_flag(hdlc); + else { + hdlc->state = HDLC_GET_DATA; + hdlc->data_received = 0; + } + break; + default: + hdlc->shift_reg >>= 1; + hdlc->data_bits++; + break; + } + hdlc->hdlc_bits1 = 0; + } + if (status) { + hdlc->dstpos = 0; + *count -= slen; + hdlc->cbin <<= 1; + hdlc->bit_shift--; + return status; + } + if (hdlc->data_bits == 8) { + hdlc->data_bits = 0; + hdlc->data_received = 1; + hdlc->crc = crc_ccitt_byte(hdlc->crc, + hdlc->shift_reg); + + /* good byte received */ + if (hdlc->dstpos < dsize) + dst[hdlc->dstpos++] = hdlc->shift_reg; + else { + /* frame too long */ + status = -HDLC_LENGTH_ERROR; + hdlc->dstpos = 0; + } + } + hdlc->cbin <<= 1; + hdlc->bit_shift--; + break; + case HDLC_FAST_FLAG: + if (hdlc->cbin == hdlc->ffvalue) { + hdlc->bit_shift = 0; + break; + } else { + if (hdlc->cbin == 0xff) { + hdlc->state = HDLC_FAST_IDLE; + hdlc->bit_shift = 0; + } else if (hdlc->ffbit_shift == 8) { + hdlc->state = HDLC_GETFLAG_B7; + break; + } else + handle_abort(hdlc); + } + break; + default: + break; + } + } + *count -= slen; + return 0; +} +EXPORT_SYMBOL(isdnhdlc_decode); +/* + isdnhdlc_encode - encodes HDLC frames to a transparent bit stream. + + The bit stream starts with a beginning flag (01111110). After + that each byte is added to the bit stream with bit stuffing added + (0 after 5 1's). + When the last byte has been removed from the source buffer, the + CRC (2 bytes is added) and the frame terminates with the ending flag. + For the dchannel, the idle character (all 1's) is also added at the end. + If this function is called with empty source buffer (slen=0), flags or + idle character will be generated. + + src - source buffer + slen - source buffer length + count - number of bytes removed (encoded) from source buffer + dst _ destination buffer + dsize - destination buffer size + returns - number of encoded bytes in the destination buffer +*/ +int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const u8 *src, u16 slen, + int *count, u8 *dst, int dsize) +{ + static const unsigned char xfast_flag_value[] = { + 0x7e, 0x3f, 0x9f, 0xcf, 0xe7, 0xf3, 0xf9, 0xfc, 0x7e + }; + + int len = 0; + + *count = slen; + + /* special handling for one byte frames */ + if ((slen == 1) && (hdlc->state == HDLC_SEND_FAST_FLAG)) + hdlc->state = HDLC_SENDFLAG_ONE; + while (dsize > 0) { + if (hdlc->bit_shift == 0) { + if (slen && !hdlc->do_closing) { + hdlc->shift_reg = *src++; + slen--; + if (slen == 0) + /* closing sequence, CRC + flag(s) */ + hdlc->do_closing = 1; + hdlc->bit_shift = 8; + } else { + if (hdlc->state == HDLC_SEND_DATA) { + if (hdlc->data_received) { + hdlc->state = HDLC_SEND_CRC1; + hdlc->crc ^= 0xffff; + hdlc->bit_shift = 8; + hdlc->shift_reg = + hdlc->crc & 0xff; + } else if (!hdlc->do_adapt56) + hdlc->state = + HDLC_SEND_FAST_FLAG; + else + hdlc->state = + HDLC_SENDFLAG_B0; + } + + } + } + + switch (hdlc->state) { + case STOPPED: + while (dsize--) + *dst++ = 0xff; + return dsize; + case HDLC_SEND_FAST_FLAG: + hdlc->do_closing = 0; + if (slen == 0) { + /* the code is for bitreverse streams */ + if (hdlc->do_bitreverse == 0) + *dst++ = bitrev8(hdlc->ffvalue); + else + *dst++ = hdlc->ffvalue; + len++; + dsize--; + break; + } + fallthrough; + case HDLC_SENDFLAG_ONE: + if (hdlc->bit_shift == 8) { + hdlc->cbin = hdlc->ffvalue >> + (8 - hdlc->data_bits); + hdlc->state = HDLC_SEND_DATA; + hdlc->crc = 0xffff; + hdlc->hdlc_bits1 = 0; + hdlc->data_received = 1; + } + break; + case HDLC_SENDFLAG_B0: + hdlc->do_closing = 0; + hdlc->cbin <<= 1; + hdlc->data_bits++; + hdlc->hdlc_bits1 = 0; + hdlc->state = HDLC_SENDFLAG_B1A6; + break; + case HDLC_SENDFLAG_B1A6: + hdlc->cbin <<= 1; + hdlc->data_bits++; + hdlc->cbin++; + if (++hdlc->hdlc_bits1 == 6) + hdlc->state = HDLC_SENDFLAG_B7; + break; + case HDLC_SENDFLAG_B7: + hdlc->cbin <<= 1; + hdlc->data_bits++; + if (slen == 0) { + hdlc->state = HDLC_SENDFLAG_B0; + break; + } + if (hdlc->bit_shift == 8) { + hdlc->state = HDLC_SEND_DATA; + hdlc->crc = 0xffff; + hdlc->hdlc_bits1 = 0; + hdlc->data_received = 1; + } + break; + case HDLC_SEND_FIRST_FLAG: + hdlc->data_received = 1; + if (hdlc->data_bits == 8) { + hdlc->state = HDLC_SEND_DATA; + hdlc->crc = 0xffff; + hdlc->hdlc_bits1 = 0; + break; + } + hdlc->cbin <<= 1; + hdlc->data_bits++; + if (hdlc->shift_reg & 0x01) + hdlc->cbin++; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + if (hdlc->bit_shift == 0) { + hdlc->state = HDLC_SEND_DATA; + hdlc->crc = 0xffff; + hdlc->hdlc_bits1 = 0; + } + break; + case HDLC_SEND_DATA: + hdlc->cbin <<= 1; + hdlc->data_bits++; + if (hdlc->hdlc_bits1 == 5) { + hdlc->hdlc_bits1 = 0; + break; + } + if (hdlc->bit_shift == 8) + hdlc->crc = crc_ccitt_byte(hdlc->crc, + hdlc->shift_reg); + if (hdlc->shift_reg & 0x01) { + hdlc->hdlc_bits1++; + hdlc->cbin++; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + } else { + hdlc->hdlc_bits1 = 0; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + } + break; + case HDLC_SEND_CRC1: + hdlc->cbin <<= 1; + hdlc->data_bits++; + if (hdlc->hdlc_bits1 == 5) { + hdlc->hdlc_bits1 = 0; + break; + } + if (hdlc->shift_reg & 0x01) { + hdlc->hdlc_bits1++; + hdlc->cbin++; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + } else { + hdlc->hdlc_bits1 = 0; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + } + if (hdlc->bit_shift == 0) { + hdlc->shift_reg = (hdlc->crc >> 8); + hdlc->state = HDLC_SEND_CRC2; + hdlc->bit_shift = 8; + } + break; + case HDLC_SEND_CRC2: + hdlc->cbin <<= 1; + hdlc->data_bits++; + if (hdlc->hdlc_bits1 == 5) { + hdlc->hdlc_bits1 = 0; + break; + } + if (hdlc->shift_reg & 0x01) { + hdlc->hdlc_bits1++; + hdlc->cbin++; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + } else { + hdlc->hdlc_bits1 = 0; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + } + if (hdlc->bit_shift == 0) { + hdlc->shift_reg = 0x7e; + hdlc->state = HDLC_SEND_CLOSING_FLAG; + hdlc->bit_shift = 8; + } + break; + case HDLC_SEND_CLOSING_FLAG: + hdlc->cbin <<= 1; + hdlc->data_bits++; + if (hdlc->hdlc_bits1 == 5) { + hdlc->hdlc_bits1 = 0; + break; + } + if (hdlc->shift_reg & 0x01) + hdlc->cbin++; + hdlc->shift_reg >>= 1; + hdlc->bit_shift--; + if (hdlc->bit_shift == 0) { + hdlc->ffvalue = + xfast_flag_value[hdlc->data_bits]; + if (hdlc->dchannel) { + hdlc->ffvalue = 0x7e; + hdlc->state = HDLC_SEND_IDLE1; + hdlc->bit_shift = 8-hdlc->data_bits; + if (hdlc->bit_shift == 0) + hdlc->state = + HDLC_SEND_FAST_IDLE; + } else { + if (!hdlc->do_adapt56) { + hdlc->state = + HDLC_SEND_FAST_FLAG; + hdlc->data_received = 0; + } else { + hdlc->state = HDLC_SENDFLAG_B0; + hdlc->data_received = 0; + } + /* Finished this frame, send flags */ + if (dsize > 1) + dsize = 1; + } + } + break; + case HDLC_SEND_IDLE1: + hdlc->do_closing = 0; + hdlc->cbin <<= 1; + hdlc->cbin++; + hdlc->data_bits++; + hdlc->bit_shift--; + if (hdlc->bit_shift == 0) { + hdlc->state = HDLC_SEND_FAST_IDLE; + hdlc->bit_shift = 0; + } + break; + case HDLC_SEND_FAST_IDLE: + hdlc->do_closing = 0; + hdlc->cbin = 0xff; + hdlc->data_bits = 8; + if (hdlc->bit_shift == 8) { + hdlc->cbin = 0x7e; + hdlc->state = HDLC_SEND_FIRST_FLAG; + } else { + /* the code is for bitreverse streams */ + if (hdlc->do_bitreverse == 0) + *dst++ = bitrev8(hdlc->cbin); + else + *dst++ = hdlc->cbin; + hdlc->bit_shift = 0; + hdlc->data_bits = 0; + len++; + dsize = 0; + } + break; + default: + break; + } + if (hdlc->do_adapt56) { + if (hdlc->data_bits == 7) { + hdlc->cbin <<= 1; + hdlc->cbin++; + hdlc->data_bits++; + } + } + if (hdlc->data_bits == 8) { + /* the code is for bitreverse streams */ + if (hdlc->do_bitreverse == 0) + *dst++ = bitrev8(hdlc->cbin); + else + *dst++ = hdlc->cbin; + hdlc->data_bits = 0; + len++; + dsize--; + } + } + *count -= slen; + + return len; +} +EXPORT_SYMBOL(isdnhdlc_encode); diff --git a/drivers/isdn/hardware/mISDN/isdnhdlc.h b/drivers/isdn/hardware/mISDN/isdnhdlc.h new file mode 100644 index 000000000..fe2c1279c --- /dev/null +++ b/drivers/isdn/hardware/mISDN/isdnhdlc.h @@ -0,0 +1,69 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * hdlc.h -- General purpose ISDN HDLC decoder. + * + * Implementation of a HDLC decoder/encoder in software. + * Necessary because some ISDN devices don't have HDLC + * controllers. + * + * Copyright (C) + * 2009 Karsten Keil <keil@b1-systems.de> + * 2002 Wolfgang Mües <wolfgang@iksw-muees.de> + * 2001 Frode Isaksen <fisaksen@bewan.com> + * 2001 Kai Germaschewski <kai.germaschewski@gmx.de> + */ + +#ifndef __ISDNHDLC_H__ +#define __ISDNHDLC_H__ + +struct isdnhdlc_vars { + int bit_shift; + int hdlc_bits1; + int data_bits; + int ffbit_shift; /* encoding only */ + int state; + int dstpos; + + u16 crc; + + u8 cbin; + u8 shift_reg; + u8 ffvalue; + + /* set if transferring data */ + u32 data_received:1; + /* set if D channel (send idle instead of flags) */ + u32 dchannel:1; + /* set if 56K adaptation */ + u32 do_adapt56:1; + /* set if in closing phase (need to send CRC + flag) */ + u32 do_closing:1; + /* set if data is bitreverse */ + u32 do_bitreverse:1; +}; + +/* Feature Flags */ +#define HDLC_56KBIT 0x01 +#define HDLC_DCHANNEL 0x02 +#define HDLC_BITREVERSE 0x04 + +/* + The return value from isdnhdlc_decode is + the frame length, 0 if no complete frame was decoded, + or a negative error number +*/ +#define HDLC_FRAMING_ERROR 1 +#define HDLC_CRC_ERROR 2 +#define HDLC_LENGTH_ERROR 3 + +extern void isdnhdlc_rcv_init(struct isdnhdlc_vars *hdlc, u32 features); + +extern int isdnhdlc_decode(struct isdnhdlc_vars *hdlc, const u8 *src, + int slen, int *count, u8 *dst, int dsize); + +extern void isdnhdlc_out_init(struct isdnhdlc_vars *hdlc, u32 features); + +extern int isdnhdlc_encode(struct isdnhdlc_vars *hdlc, const u8 *src, + u16 slen, int *count, u8 *dst, int dsize); + +#endif /* __ISDNHDLC_H__ */ diff --git a/drivers/isdn/hardware/mISDN/mISDNinfineon.c b/drivers/isdn/hardware/mISDN/mISDNinfineon.c new file mode 100644 index 000000000..88d592baf --- /dev/null +++ b/drivers/isdn/hardware/mISDN/mISDNinfineon.c @@ -0,0 +1,1167 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * 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> + */ + +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/mISDNhw.h> +#include <linux/slab.h> +#include "ipac.h" + +#define INFINEON_REV "1.0" + +static int inf_cnt; +static u32 debug; +static u32 irqloops = 4; + +enum inf_types { + INF_NONE, + INF_DIVA20, + INF_DIVA20U, + INF_DIVA201, + INF_DIVA202, + INF_SPEEDWIN, + INF_SAPHIR3, + INF_QS1000, + INF_QS3000, + INF_NICCY, + INF_SCT_1, + INF_SCT_2, + INF_SCT_3, + INF_SCT_4, + INF_GAZEL_R685, + INF_GAZEL_R753 +}; + +enum addr_mode { + AM_NONE = 0, + AM_IO, + AM_MEMIO, + AM_IND_IO, +}; + +struct inf_cinfo { + enum inf_types typ; + const char *full; + const char *name; + enum addr_mode cfg_mode; + enum addr_mode addr_mode; + u8 cfg_bar; + u8 addr_bar; + void *irqfunc; +}; + +struct _ioaddr { + enum addr_mode mode; + union { + void __iomem *p; + struct _ioport io; + } a; +}; + +struct _iohandle { + enum addr_mode mode; + resource_size_t size; + resource_size_t start; + void __iomem *p; +}; + +struct inf_hw { + struct list_head list; + struct pci_dev *pdev; + const struct inf_cinfo *ci; + char name[MISDN_MAX_IDLEN]; + u32 irq; + u32 irqcnt; + struct _iohandle cfg; + struct _iohandle addr; + struct _ioaddr isac; + struct _ioaddr hscx; + spinlock_t lock; /* HW access lock */ + struct ipac_hw ipac; + struct inf_hw *sc[3]; /* slave cards */ +}; + + +#define PCI_SUBVENDOR_HST_SAPHIR3 0x52 +#define PCI_SUBVENDOR_SEDLBAUER_PCI 0x53 +#define PCI_SUB_ID_SEDLBAUER 0x01 + +static struct pci_device_id infineon_ids[] = { + { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_DIVA20), INF_DIVA20 }, + { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_DIVA20_U), INF_DIVA20U }, + { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_DIVA201), INF_DIVA201 }, + { PCI_VDEVICE(EICON, PCI_DEVICE_ID_EICON_DIVA202), INF_DIVA202 }, + { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, + PCI_SUBVENDOR_SEDLBAUER_PCI, PCI_SUB_ID_SEDLBAUER, 0, 0, + INF_SPEEDWIN }, + { PCI_VENDOR_ID_TIGERJET, PCI_DEVICE_ID_TIGERJET_100, + PCI_SUBVENDOR_HST_SAPHIR3, PCI_SUB_ID_SEDLBAUER, 0, 0, INF_SAPHIR3 }, + { PCI_VDEVICE(ELSA, PCI_DEVICE_ID_ELSA_MICROLINK), INF_QS1000 }, + { PCI_VDEVICE(ELSA, PCI_DEVICE_ID_ELSA_QS3000), INF_QS3000 }, + { PCI_VDEVICE(SATSAGEM, PCI_DEVICE_ID_SATSAGEM_NICCY), INF_NICCY }, + { PCI_VENDOR_ID_PLX, PCI_DEVICE_ID_PLX_9050, + PCI_VENDOR_ID_BERKOM, PCI_DEVICE_ID_BERKOM_SCITEL_QUADRO, 0, 0, + INF_SCT_1 }, + { PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_R685), INF_GAZEL_R685 }, + { PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_R753), INF_GAZEL_R753 }, + { PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_DJINN_ITOO), INF_GAZEL_R753 }, + { PCI_VDEVICE(PLX, PCI_DEVICE_ID_PLX_OLITEC), INF_GAZEL_R753 }, + { } +}; +MODULE_DEVICE_TABLE(pci, infineon_ids); + +/* PCI interface specific defines */ +/* Diva 2.0/2.0U */ +#define DIVA_HSCX_PORT 0x00 +#define DIVA_HSCX_ALE 0x04 +#define DIVA_ISAC_PORT 0x08 +#define DIVA_ISAC_ALE 0x0C +#define DIVA_PCI_CTRL 0x10 + +/* DIVA_PCI_CTRL bits */ +#define DIVA_IRQ_BIT 0x01 +#define DIVA_RESET_BIT 0x08 +#define DIVA_EEPROM_CLK 0x40 +#define DIVA_LED_A 0x10 +#define DIVA_LED_B 0x20 +#define DIVA_IRQ_CLR 0x80 + +/* Diva 2.01/2.02 */ +/* Siemens PITA */ +#define PITA_ICR_REG 0x00 +#define PITA_INT0_STATUS 0x02 + +#define PITA_MISC_REG 0x1c +#define PITA_PARA_SOFTRESET 0x01000000 +#define PITA_SER_SOFTRESET 0x02000000 +#define PITA_PARA_MPX_MODE 0x04000000 +#define PITA_INT0_ENABLE 0x00020000 + +/* TIGER 100 Registers */ +#define TIGER_RESET_ADDR 0x00 +#define TIGER_EXTERN_RESET 0x01 +#define TIGER_AUX_CTRL 0x02 +#define TIGER_AUX_DATA 0x03 +#define TIGER_AUX_IRQMASK 0x05 +#define TIGER_AUX_STATUS 0x07 + +/* Tiger AUX BITs */ +#define TIGER_IOMASK 0xdd /* 1 and 5 are inputs */ +#define TIGER_IRQ_BIT 0x02 + +#define TIGER_IPAC_ALE 0xC0 +#define TIGER_IPAC_PORT 0xC8 + +/* ELSA (now Develo) PCI cards */ +#define ELSA_IRQ_ADDR 0x4c +#define ELSA_IRQ_MASK 0x04 +#define QS1000_IRQ_OFF 0x01 +#define QS3000_IRQ_OFF 0x03 +#define QS1000_IRQ_ON 0x41 +#define QS3000_IRQ_ON 0x43 + +/* Dr Neuhaus/Sagem Niccy */ +#define NICCY_ISAC_PORT 0x00 +#define NICCY_HSCX_PORT 0x01 +#define NICCY_ISAC_ALE 0x02 +#define NICCY_HSCX_ALE 0x03 + +#define NICCY_IRQ_CTRL_REG 0x38 +#define NICCY_IRQ_ENABLE 0x001f00 +#define NICCY_IRQ_DISABLE 0xff0000 +#define NICCY_IRQ_BIT 0x800000 + + +/* Scitel PLX */ +#define SCT_PLX_IRQ_ADDR 0x4c +#define SCT_PLX_RESET_ADDR 0x50 +#define SCT_PLX_IRQ_ENABLE 0x41 +#define SCT_PLX_RESET_BIT 0x04 + +/* Gazel */ +#define GAZEL_IPAC_DATA_PORT 0x04 +/* Gazel PLX */ +#define GAZEL_CNTRL 0x50 +#define GAZEL_RESET 0x04 +#define GAZEL_RESET_9050 0x40000000 +#define GAZEL_INCSR 0x4C +#define GAZEL_ISAC_EN 0x08 +#define GAZEL_INT_ISAC 0x20 +#define GAZEL_HSCX_EN 0x01 +#define GAZEL_INT_HSCX 0x04 +#define GAZEL_PCI_EN 0x40 +#define GAZEL_IPAC_EN 0x03 + + +static LIST_HEAD(Cards); +static DEFINE_RWLOCK(card_lock); /* protect Cards */ + +static void +_set_debug(struct inf_hw *card) +{ + card->ipac.isac.dch.debug = debug; + card->ipac.hscx[0].bch.debug = debug; + card->ipac.hscx[1].bch.debug = debug; +} + +static int +set_debug(const char *val, const struct kernel_param *kp) +{ + int ret; + struct inf_hw *card; + + ret = param_set_uint(val, kp); + if (!ret) { + read_lock(&card_lock); + list_for_each_entry(card, &Cards, list) + _set_debug(card); + read_unlock(&card_lock); + } + return ret; +} + +MODULE_AUTHOR("Karsten Keil"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(INFINEON_REV); +module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "infineon debug mask"); +module_param(irqloops, uint, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(irqloops, "infineon maximal irqloops (default 4)"); + +/* Interface functions */ + +IOFUNC_IO(ISAC, inf_hw, isac.a.io) +IOFUNC_IO(IPAC, inf_hw, hscx.a.io) +IOFUNC_IND(ISAC, inf_hw, isac.a.io) +IOFUNC_IND(IPAC, inf_hw, hscx.a.io) +IOFUNC_MEMIO(ISAC, inf_hw, u32, isac.a.p) +IOFUNC_MEMIO(IPAC, inf_hw, u32, hscx.a.p) + +static irqreturn_t +diva_irq(int intno, void *dev_id) +{ + struct inf_hw *hw = dev_id; + u8 val; + + spin_lock(&hw->lock); + val = inb((u32)hw->cfg.start + DIVA_PCI_CTRL); + if (!(val & DIVA_IRQ_BIT)) { /* for us or shared ? */ + spin_unlock(&hw->lock); + return IRQ_NONE; /* shared */ + } + hw->irqcnt++; + mISDNipac_irq(&hw->ipac, irqloops); + spin_unlock(&hw->lock); + return IRQ_HANDLED; +} + +static irqreturn_t +diva20x_irq(int intno, void *dev_id) +{ + struct inf_hw *hw = dev_id; + u8 val; + + spin_lock(&hw->lock); + val = readb(hw->cfg.p); + if (!(val & PITA_INT0_STATUS)) { /* for us or shared ? */ + spin_unlock(&hw->lock); + return IRQ_NONE; /* shared */ + } + hw->irqcnt++; + mISDNipac_irq(&hw->ipac, irqloops); + writeb(PITA_INT0_STATUS, hw->cfg.p); /* ACK PITA INT0 */ + spin_unlock(&hw->lock); + return IRQ_HANDLED; +} + +static irqreturn_t +tiger_irq(int intno, void *dev_id) +{ + struct inf_hw *hw = dev_id; + u8 val; + + spin_lock(&hw->lock); + val = inb((u32)hw->cfg.start + TIGER_AUX_STATUS); + if (val & TIGER_IRQ_BIT) { /* for us or shared ? */ + spin_unlock(&hw->lock); + return IRQ_NONE; /* shared */ + } + hw->irqcnt++; + mISDNipac_irq(&hw->ipac, irqloops); + spin_unlock(&hw->lock); + return IRQ_HANDLED; +} + +static irqreturn_t +elsa_irq(int intno, void *dev_id) +{ + struct inf_hw *hw = dev_id; + u8 val; + + spin_lock(&hw->lock); + val = inb((u32)hw->cfg.start + ELSA_IRQ_ADDR); + if (!(val & ELSA_IRQ_MASK)) { + spin_unlock(&hw->lock); + return IRQ_NONE; /* shared */ + } + hw->irqcnt++; + mISDNipac_irq(&hw->ipac, irqloops); + spin_unlock(&hw->lock); + return IRQ_HANDLED; +} + +static irqreturn_t +niccy_irq(int intno, void *dev_id) +{ + struct inf_hw *hw = dev_id; + u32 val; + + spin_lock(&hw->lock); + val = inl((u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); + if (!(val & NICCY_IRQ_BIT)) { /* for us or shared ? */ + spin_unlock(&hw->lock); + return IRQ_NONE; /* shared */ + } + outl(val, (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); + hw->irqcnt++; + mISDNipac_irq(&hw->ipac, irqloops); + spin_unlock(&hw->lock); + return IRQ_HANDLED; +} + +static irqreturn_t +gazel_irq(int intno, void *dev_id) +{ + struct inf_hw *hw = dev_id; + irqreturn_t ret; + + spin_lock(&hw->lock); + ret = mISDNipac_irq(&hw->ipac, irqloops); + spin_unlock(&hw->lock); + return ret; +} + +static irqreturn_t +ipac_irq(int intno, void *dev_id) +{ + struct inf_hw *hw = dev_id; + u8 val; + + spin_lock(&hw->lock); + val = hw->ipac.read_reg(hw, IPAC_ISTA); + if (!(val & 0x3f)) { + spin_unlock(&hw->lock); + return IRQ_NONE; /* shared */ + } + hw->irqcnt++; + mISDNipac_irq(&hw->ipac, irqloops); + spin_unlock(&hw->lock); + return IRQ_HANDLED; +} + +static void +enable_hwirq(struct inf_hw *hw) +{ + u16 w; + u32 val; + + switch (hw->ci->typ) { + case INF_DIVA201: + case INF_DIVA202: + writel(PITA_INT0_ENABLE, hw->cfg.p); + break; + case INF_SPEEDWIN: + case INF_SAPHIR3: + outb(TIGER_IRQ_BIT, (u32)hw->cfg.start + TIGER_AUX_IRQMASK); + break; + case INF_QS1000: + outb(QS1000_IRQ_ON, (u32)hw->cfg.start + ELSA_IRQ_ADDR); + break; + case INF_QS3000: + outb(QS3000_IRQ_ON, (u32)hw->cfg.start + ELSA_IRQ_ADDR); + break; + case INF_NICCY: + val = inl((u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); + val |= NICCY_IRQ_ENABLE; + outl(val, (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); + break; + case INF_SCT_1: + w = inw((u32)hw->cfg.start + SCT_PLX_IRQ_ADDR); + w |= SCT_PLX_IRQ_ENABLE; + outw(w, (u32)hw->cfg.start + SCT_PLX_IRQ_ADDR); + break; + case INF_GAZEL_R685: + outb(GAZEL_ISAC_EN + GAZEL_HSCX_EN + GAZEL_PCI_EN, + (u32)hw->cfg.start + GAZEL_INCSR); + break; + case INF_GAZEL_R753: + outb(GAZEL_IPAC_EN + GAZEL_PCI_EN, + (u32)hw->cfg.start + GAZEL_INCSR); + break; + default: + break; + } +} + +static void +disable_hwirq(struct inf_hw *hw) +{ + u16 w; + u32 val; + + switch (hw->ci->typ) { + case INF_DIVA201: + case INF_DIVA202: + writel(0, hw->cfg.p); + break; + case INF_SPEEDWIN: + case INF_SAPHIR3: + outb(0, (u32)hw->cfg.start + TIGER_AUX_IRQMASK); + break; + case INF_QS1000: + outb(QS1000_IRQ_OFF, (u32)hw->cfg.start + ELSA_IRQ_ADDR); + break; + case INF_QS3000: + outb(QS3000_IRQ_OFF, (u32)hw->cfg.start + ELSA_IRQ_ADDR); + break; + case INF_NICCY: + val = inl((u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); + val &= NICCY_IRQ_DISABLE; + outl(val, (u32)hw->cfg.start + NICCY_IRQ_CTRL_REG); + break; + case INF_SCT_1: + w = inw((u32)hw->cfg.start + SCT_PLX_IRQ_ADDR); + w &= (~SCT_PLX_IRQ_ENABLE); + outw(w, (u32)hw->cfg.start + SCT_PLX_IRQ_ADDR); + break; + case INF_GAZEL_R685: + case INF_GAZEL_R753: + outb(0, (u32)hw->cfg.start + GAZEL_INCSR); + break; + default: + break; + } +} + +static void +ipac_chip_reset(struct inf_hw *hw) +{ + hw->ipac.write_reg(hw, IPAC_POTA2, 0x20); + mdelay(5); + hw->ipac.write_reg(hw, IPAC_POTA2, 0x00); + mdelay(5); + hw->ipac.write_reg(hw, IPAC_CONF, hw->ipac.conf); + hw->ipac.write_reg(hw, IPAC_MASK, 0xc0); +} + +static void +reset_inf(struct inf_hw *hw) +{ + u16 w; + u32 val; + + if (debug & DEBUG_HW) + pr_notice("%s: resetting card\n", hw->name); + switch (hw->ci->typ) { + case INF_DIVA20: + case INF_DIVA20U: + outb(0, (u32)hw->cfg.start + DIVA_PCI_CTRL); + mdelay(10); + outb(DIVA_RESET_BIT, (u32)hw->cfg.start + DIVA_PCI_CTRL); + mdelay(10); + /* Workaround PCI9060 */ + outb(9, (u32)hw->cfg.start + 0x69); + outb(DIVA_RESET_BIT | DIVA_LED_A, + (u32)hw->cfg.start + DIVA_PCI_CTRL); + break; + case INF_DIVA201: + writel(PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE, + hw->cfg.p + PITA_MISC_REG); + mdelay(1); + writel(PITA_PARA_MPX_MODE, hw->cfg.p + PITA_MISC_REG); + mdelay(10); + break; + case INF_DIVA202: + writel(PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE, + hw->cfg.p + PITA_MISC_REG); + mdelay(1); + writel(PITA_PARA_MPX_MODE | PITA_SER_SOFTRESET, + hw->cfg.p + PITA_MISC_REG); + mdelay(10); + break; + case INF_SPEEDWIN: + case INF_SAPHIR3: + ipac_chip_reset(hw); + hw->ipac.write_reg(hw, IPAC_ACFG, 0xff); + hw->ipac.write_reg(hw, IPAC_AOE, 0x00); + hw->ipac.write_reg(hw, IPAC_PCFG, 0x12); + break; + case INF_QS1000: + case INF_QS3000: + ipac_chip_reset(hw); + hw->ipac.write_reg(hw, IPAC_ACFG, 0x00); + hw->ipac.write_reg(hw, IPAC_AOE, 0x3c); + hw->ipac.write_reg(hw, IPAC_ATX, 0xff); + break; + case INF_NICCY: + break; + case INF_SCT_1: + w = inw((u32)hw->cfg.start + SCT_PLX_RESET_ADDR); + w &= (~SCT_PLX_RESET_BIT); + outw(w, (u32)hw->cfg.start + SCT_PLX_RESET_ADDR); + mdelay(10); + w = inw((u32)hw->cfg.start + SCT_PLX_RESET_ADDR); + w |= SCT_PLX_RESET_BIT; + outw(w, (u32)hw->cfg.start + SCT_PLX_RESET_ADDR); + mdelay(10); + break; + case INF_GAZEL_R685: + val = inl((u32)hw->cfg.start + GAZEL_CNTRL); + val |= (GAZEL_RESET_9050 + GAZEL_RESET); + outl(val, (u32)hw->cfg.start + GAZEL_CNTRL); + val &= ~(GAZEL_RESET_9050 + GAZEL_RESET); + mdelay(4); + outl(val, (u32)hw->cfg.start + GAZEL_CNTRL); + mdelay(10); + hw->ipac.isac.adf2 = 0x87; + hw->ipac.hscx[0].slot = 0x1f; + hw->ipac.hscx[1].slot = 0x23; + break; + case INF_GAZEL_R753: + val = inl((u32)hw->cfg.start + GAZEL_CNTRL); + val |= (GAZEL_RESET_9050 + GAZEL_RESET); + outl(val, (u32)hw->cfg.start + GAZEL_CNTRL); + val &= ~(GAZEL_RESET_9050 + GAZEL_RESET); + mdelay(4); + outl(val, (u32)hw->cfg.start + GAZEL_CNTRL); + mdelay(10); + ipac_chip_reset(hw); + hw->ipac.write_reg(hw, IPAC_ACFG, 0xff); + hw->ipac.write_reg(hw, IPAC_AOE, 0x00); + hw->ipac.conf = 0x01; /* IOM off */ + break; + default: + return; + } + enable_hwirq(hw); +} + +static int +inf_ctrl(struct inf_hw *hw, u32 cmd, u_long arg) +{ + int ret = 0; + + switch (cmd) { + case HW_RESET_REQ: + reset_inf(hw); + break; + default: + pr_info("%s: %s unknown command %x %lx\n", + hw->name, __func__, cmd, arg); + ret = -EINVAL; + break; + } + return ret; +} + +static int +init_irq(struct inf_hw *hw) +{ + int ret, cnt = 3; + u_long flags; + + if (!hw->ci->irqfunc) + return -EINVAL; + ret = request_irq(hw->irq, hw->ci->irqfunc, IRQF_SHARED, hw->name, hw); + if (ret) { + pr_info("%s: couldn't get interrupt %d\n", hw->name, hw->irq); + return ret; + } + while (cnt--) { + spin_lock_irqsave(&hw->lock, flags); + reset_inf(hw); + ret = hw->ipac.init(&hw->ipac); + if (ret) { + spin_unlock_irqrestore(&hw->lock, flags); + pr_info("%s: ISAC init failed with %d\n", + hw->name, ret); + break; + } + spin_unlock_irqrestore(&hw->lock, flags); + msleep_interruptible(10); + if (debug & DEBUG_HW) + pr_notice("%s: IRQ %d count %d\n", hw->name, + hw->irq, hw->irqcnt); + if (!hw->irqcnt) { + pr_info("%s: IRQ(%d) got no requests during init %d\n", + hw->name, hw->irq, 3 - cnt); + } else + return 0; + } + free_irq(hw->irq, hw); + return -EIO; +} + +static void +release_io(struct inf_hw *hw) +{ + if (hw->cfg.mode) { + if (hw->cfg.mode == AM_MEMIO) { + release_mem_region(hw->cfg.start, hw->cfg.size); + if (hw->cfg.p) + 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.mode == AM_MEMIO) { + release_mem_region(hw->addr.start, hw->addr.size); + if (hw->addr.p) + iounmap(hw->addr.p); + } else + release_region(hw->addr.start, hw->addr.size); + hw->addr.mode = AM_NONE; + } +} + +static int +setup_io(struct inf_hw *hw) +{ + int err = 0; + + if (hw->ci->cfg_mode) { + hw->cfg.start = pci_resource_start(hw->pdev, hw->ci->cfg_bar); + hw->cfg.size = pci_resource_len(hw->pdev, hw->ci->cfg_bar); + if (hw->ci->cfg_mode == AM_MEMIO) { + if (!request_mem_region(hw->cfg.start, hw->cfg.size, + hw->name)) + err = -EBUSY; + } else { + if (!request_region(hw->cfg.start, hw->cfg.size, + hw->name)) + err = -EBUSY; + } + if (err) { + pr_info("mISDN: %s config port %lx (%lu bytes)" + "already in use\n", hw->name, + (ulong)hw->cfg.start, (ulong)hw->cfg.size); + return err; + } + hw->cfg.mode = hw->ci->cfg_mode; + if (hw->ci->cfg_mode == AM_MEMIO) { + hw->cfg.p = ioremap(hw->cfg.start, hw->cfg.size); + if (!hw->cfg.p) + return -ENOMEM; + } + 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; + } + hw->addr.mode = hw->ci->addr_mode; + if (hw->ci->addr_mode == AM_MEMIO) { + hw->addr.p = ioremap(hw->addr.start, hw->addr.size); + if (!hw->addr.p) + return -ENOMEM; + } + 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; + } + fallthrough; + default: + pci_disable_device(card->pdev); + pci_set_drvdata(card->pdev, NULL); + break; + } + kfree(card); + inf_cnt--; +} + +static int +setup_instance(struct inf_hw *card) +{ + int err; + ulong flags; + + snprintf(card->name, MISDN_MAX_IDLEN - 1, "%s.%d", card->ci->name, + inf_cnt + 1); + write_lock_irqsave(&card_lock, flags); + list_add_tail(&card->list, &Cards); + write_unlock_irqrestore(&card_lock, flags); + + _set_debug(card); + card->ipac.isac.name = card->name; + card->ipac.name = card->name; + card->ipac.owner = THIS_MODULE; + spin_lock_init(&card->lock); + card->ipac.isac.hwlock = &card->lock; + card->ipac.hwlock = &card->lock; + card->ipac.ctrl = (void *)&inf_ctrl; + + err = setup_io(card); + if (err) + goto error_setup; + + card->ipac.isac.dch.dev.Bprotocols = + mISDNipac_init(&card->ipac, card); + + if (card->ipac.isac.dch.dev.Bprotocols == 0) + goto error_setup; + + err = mISDN_register_device(&card->ipac.isac.dch.dev, + &card->pdev->dev, card->name); + if (err) + goto error; + + err = init_irq(card); + if (!err) { + inf_cnt++; + pr_notice("Infineon %d cards installed\n", inf_cnt); + return 0; + } + mISDN_unregister_device(&card->ipac.isac.dch.dev); +error: + card->ipac.release(&card->ipac); +error_setup: + release_io(card); + write_lock_irqsave(&card_lock, flags); + list_del(&card->list); + write_unlock_irqrestore(&card_lock, flags); + return err; +} + +static const struct inf_cinfo inf_card_info[] = { + { + INF_DIVA20, + "Dialogic Diva 2.0", + "diva20", + AM_IND_IO, AM_NONE, 2, 0, + &diva_irq + }, + { + INF_DIVA20U, + "Dialogic Diva 2.0U", + "diva20U", + AM_IND_IO, AM_NONE, 2, 0, + &diva_irq + }, + { + INF_DIVA201, + "Dialogic Diva 2.01", + "diva201", + AM_MEMIO, AM_MEMIO, 0, 1, + &diva20x_irq + }, + { + INF_DIVA202, + "Dialogic Diva 2.02", + "diva202", + AM_MEMIO, AM_MEMIO, 0, 1, + &diva20x_irq + }, + { + INF_SPEEDWIN, + "Sedlbauer SpeedWin PCI", + "speedwin", + AM_IND_IO, AM_NONE, 0, 0, + &tiger_irq + }, + { + INF_SAPHIR3, + "HST Saphir 3", + "saphir", + AM_IND_IO, AM_NONE, 0, 0, + &tiger_irq + }, + { + INF_QS1000, + "Develo Microlink PCI", + "qs1000", + AM_IO, AM_IND_IO, 1, 3, + &elsa_irq + }, + { + INF_QS3000, + "Develo QuickStep 3000", + "qs3000", + AM_IO, AM_IND_IO, 1, 3, + &elsa_irq + }, + { + INF_NICCY, + "Sagem NICCY", + "niccy", + AM_IO, AM_IND_IO, 0, 1, + &niccy_irq + }, + { + INF_SCT_1, + "SciTel Quadro", + "p1_scitel", + AM_IO, AM_IND_IO, 1, 5, + &ipac_irq + }, + { + INF_SCT_2, + "SciTel Quadro", + "p2_scitel", + AM_NONE, AM_IND_IO, 0, 4, + &ipac_irq + }, + { + INF_SCT_3, + "SciTel Quadro", + "p3_scitel", + AM_NONE, AM_IND_IO, 0, 3, + &ipac_irq + }, + { + INF_SCT_4, + "SciTel Quadro", + "p4_scitel", + AM_NONE, AM_IND_IO, 0, 2, + &ipac_irq + }, + { + INF_GAZEL_R685, + "Gazel R685", + "gazel685", + AM_IO, AM_IO, 1, 2, + &gazel_irq + }, + { + INF_GAZEL_R753, + "Gazel R753", + "gazel753", + AM_IO, AM_IND_IO, 1, 2, + &ipac_irq + }, + { + INF_NONE, + } +}; + +static const struct inf_cinfo * +get_card_info(enum inf_types typ) +{ + const struct inf_cinfo *ci = inf_card_info; + + while (ci->typ != INF_NONE) { + if (ci->typ == typ) + return ci; + ci++; + } + return NULL; +} + +static int +inf_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int err = -ENOMEM; + struct inf_hw *card; + + card = kzalloc(sizeof(struct inf_hw), GFP_KERNEL); + if (!card) { + pr_info("No memory for Infineon ISDN card\n"); + return err; + } + card->pdev = pdev; + err = pci_enable_device(pdev); + if (err) { + kfree(card); + return err; + } + card->ci = get_card_info(ent->driver_data); + if (!card->ci) { + pr_info("mISDN: do not have information about adapter at %s\n", + pci_name(pdev)); + kfree(card); + pci_disable_device(pdev); + return -EINVAL; + } else + pr_notice("mISDN: found adapter %s at %s\n", + card->ci->full, pci_name(pdev)); + + card->irq = pdev->irq; + pci_set_drvdata(pdev, card); + err = setup_instance(card); + if (err) { + pci_disable_device(pdev); + kfree(card); + pci_set_drvdata(pdev, NULL); + } else if (ent->driver_data == INF_SCT_1) { + int i; + struct inf_hw *sc; + + for (i = 1; i < 4; i++) { + sc = kzalloc(sizeof(struct inf_hw), GFP_KERNEL); + if (!sc) { + release_card(card); + pci_disable_device(pdev); + return -ENOMEM; + } + sc->irq = card->irq; + sc->pdev = card->pdev; + sc->ci = card->ci + i; + err = setup_instance(sc); + if (err) { + pci_disable_device(pdev); + kfree(sc); + release_card(card); + break; + } else + card->sc[i - 1] = sc; + } + } + return err; +} + +static void +inf_remove(struct pci_dev *pdev) +{ + struct inf_hw *card = pci_get_drvdata(pdev); + + if (card) + release_card(card); + else + pr_debug("%s: drvdata already removed\n", __func__); +} + +static struct pci_driver infineon_driver = { + .name = "ISDN Infineon pci", + .probe = inf_probe, + .remove = inf_remove, + .id_table = infineon_ids, +}; + +static int __init +infineon_init(void) +{ + int err; + + pr_notice("Infineon ISDN Driver Rev. %s\n", INFINEON_REV); + err = pci_register_driver(&infineon_driver); + return err; +} + +static void __exit +infineon_cleanup(void) +{ + pci_unregister_driver(&infineon_driver); +} + +module_init(infineon_init); +module_exit(infineon_cleanup); diff --git a/drivers/isdn/hardware/mISDN/mISDNipac.c b/drivers/isdn/hardware/mISDN/mISDNipac.c new file mode 100644 index 000000000..4f8d85bb3 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/mISDNipac.c @@ -0,0 +1,1635 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * isac.c ISAC specific routines + * + * Author Karsten Keil <keil@isdn4linux.de> + * + * Copyright 2009 by Karsten Keil <keil@isdn4linux.de> + */ + +#include <linux/irqreturn.h> +#include <linux/slab.h> +#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: + case ISAC_IND_DR6: + 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); +} + +static 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); + } + 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); + 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 { + 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) + 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) + 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) + 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) + 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); + 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, unsigned long para) +{ + u8 tl = 0; + unsigned long flags; + int ret = 0; + + 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; + case HW_TIMER3_VALUE: + ret = l1_event(isac->dch.l1, HW_TIMER3_VALUE | (para & 0xff)); + break; + default: + pr_debug("%s: %s unknown command %x %lx\n", isac->name, + __func__, cmd, para); + ret = -1; + } + return ret; +} + +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_DR6) || + (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 if (isac->type != 0) + 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 timer_list *t) +{ + struct isac_hw *isac = from_timer(isac, t, dch.timer); + 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_caller(struct isac_hw *isac, struct channel_req *rq, void *caller) +{ + pr_debug("%s: %s dev(%d) open from %p\n", isac->name, __func__, + isac->dch.dev.id, caller); + 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 int +open_dchannel(struct isac_hw *isac, struct channel_req *rq) +{ + return open_dchannel_caller(isac, rq, __builtin_return_address(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; + timer_setup(&isac->dch.timer, dbusy_timer_handler, 0); + 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; + int maxlen; + + pr_debug("%s: B%1d %d\n", hscx->ip->name, hscx->bch.nr, count); + if (test_bit(FLG_RX_OFF, &hscx->bch.Flags)) { + hscx->bch.dropcnt += count; + hscx_cmdr(hscx, 0x80); /* RMC */ + return; + } + maxlen = bchannel_get_rxbuf(&hscx->bch, count); + if (maxlen < 0) { + hscx_cmdr(hscx, 0x80); /* RMC */ + if (hscx->bch.rx_skb) + skb_trim(hscx->bch.rx_skb, 0); + pr_warn("%s.B%d: No bufferspace for %d bytes\n", + hscx->ip->name, hscx->bch.nr, count); + 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) { + if (!test_bit(FLG_TX_EMPTY, &hscx->bch.Flags)) + return; + count = hscx->fifo_size; + more = 1; + p = hscx->log; + memset(p, hscx->bch.fill[0], count); + } else { + 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.tx_skb && (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 { + dev_kfree_skb(hx->bch.tx_skb); + if (get_next_bframe(&hx->bch)) { + hscx_fill_fifo(hx); + test_and_clear_bit(FLG_TX_EMPTY, &hx->bch.Flags); + } else if (test_bit(FLG_TX_EMPTY, &hx->bch.Flags)) { + 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 too 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, false); + } +} + +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)) + recv_Bchannel(&hx->bch, 0, false); + } + + 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)) { + if (test_bit(FLG_FILLEMPTY, &hx->bch.Flags)) + test_and_set_bit(FLG_TX_EMPTY, &hx->bch.Flags); + hscx_xpr(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; + } + } + 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); + unsigned 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 */ + ret = 0; + hscx_fill_fifo(hx); + } + 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) +{ + return mISDN_ctrl_bchannel(bch, cq); +} + +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); + cancel_work_sync(&bch->workq); + spin_lock_irqsave(hx->ip->hwlock, flags); + mISDN_clear_bchannel(bch); + hscx_mode(hx, ISDN_P_NONE); + spin_unlock_irqrestore(hx->ip->hwlock, flags); + 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 == 0 || 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 | MISDN_CTRL_L1_TIMER3; + 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; + case MISDN_CTRL_L1_TIMER3: + ret = ipac->isac.ctrl(&ipac->isac, HW_TIMER3_VALUE, cq->p1); + 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_caller(isac, rq, __builtin_return_address(0)); + 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].fifo_size); + 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 overwritten 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 000000000..48b3d43e2 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/mISDNisar.c @@ -0,0 +1,1693 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * mISDNisar.c ISAR (Siemens PSB 7110) specific functions + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * Copyright 2009 by Karsten Keil <keil@isdn4linux.de> + */ + +/* define this to enable static debug messages, if you kernel supports + * dynamic debugging, you should use debugfs for this + */ +/* #define DEBUG */ + +#include <linux/gfp.h> +#include <linux/delay.h> +#include <linux/vmalloc.h> +#include <linux/mISDNhw.h> +#include <linux/module.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[] = {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_warn("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; + int maxlen; + + 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; + } + if (test_bit(FLG_RX_OFF, &ch->bch.Flags)) { + ch->bch.dropcnt += ch->is->clsb; + 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: + maxlen = bchannel_get_rxbuf(&ch->bch, ch->is->clsb); + if (maxlen < 0) { + pr_warn("%s.B%d: No bufferspace for %d bytes\n", + ch->is->name, ch->bch.nr, ch->is->clsb); + 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, false); + break; + case ISDN_P_B_HDLC: + maxlen = bchannel_get_rxbuf(&ch->bch, ch->is->clsb); + if (maxlen < 0) { + pr_warn("%s.B%d: No bufferspace for %d bytes\n", + ch->is->name, ch->bch.nr, ch->is->clsb); + ch->is->write_reg(ch->is->hw, ISAR_IIA, 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 too 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, false); + } + 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, false); + 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 too 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, false); + } + 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 %d tx_idx %d\n", ch->is->name, ch->bch.nr, + ch->bch.tx_skb ? ch->bch.tx_skb->len : -1, ch->bch.tx_idx); + if (!(ch->is->bstat & + (ch->dpath == 1 ? BSTAT_RDM1 : BSTAT_RDM2))) + return; + if (!ch->bch.tx_skb) { + if (!test_bit(FLG_TX_EMPTY, &ch->bch.Flags) || + (ch->bch.state != ISDN_P_B_RAW)) + return; + count = ch->mml; + /* use the card buffer */ + memset(ch->is->buf, ch->bch.fill[0], count); + send_mbox(ch->is, SET_DPS(ch->dpath) | ISAR_HIS_SDATA, + 0, count, ch->is->buf); + return; + } + count = ch->bch.tx_skb->len - ch->bch.tx_idx; + if (count <= 0) + 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 %d tx_idx %d\n", ch->is->name, __func__, + ch->bch.nr, ch->bch.tx_skb ? ch->bch.tx_skb->len : -1, + 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); + } + } + } + dev_kfree_skb(ch->bch.tx_skb); + if (get_next_bframe(&ch->bch)) { + isar_fill_fifo(ch); + test_and_clear_bit(FLG_TX_EMPTY, &ch->bch.Flags); + } else if (test_bit(FLG_TX_EMPTY, &ch->bch.Flags)) { + 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); + } + } else if (test_bit(FLG_FILLEMPTY, &ch->bch.Flags)) { + test_and_set_bit(FLG_TX_EMPTY, &ch->bch.Flags); + } + } +} + +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); + } + } +} + +static const char *dmril[] = {"NO SPEED", "1200/75", "NODEF2", "75/1200", "NODEF4", + "300", "600", "1200", "2400", "4800", "7200", + "9600nt", "9600t", "12000", "14400", "WRONG"}; +static 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: unknown 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; + fallthrough; + 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(struct timer_list *t) +{ + struct isar_ch *ch = from_timer(ch, t, ftimer); + + 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); + } + fallthrough; + 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; + fallthrough; + 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 paths 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 */ + ret = 0; + isar_fill_fifo(ich); + } + 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; + } + fallthrough; + 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) +{ + return mISDN_ctrl_bchannel(bch, cq); +} + +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); + cancel_work_sync(&bch->workq); + spin_lock_irqsave(ich->is->hwlock, flags); + mISDN_clear_bchannel(bch); + modeisar(ich, ISDN_P_NONE); + spin_unlock_irqrestore(ich->is->hwlock, flags); + 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; + timer_setup(&isar->ch[0].ftimer, ftimer_handler, 0); + test_and_set_bit(FLG_INITIALIZED, &isar->ch[0].bch.Flags); + timer_setup(&isar->ch[1].ftimer, ftimer_handler, 0); + 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 == 0 || 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 */ + 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, 32); + 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 __init isar_mod_init(void) +{ + pr_notice("mISDN: ISAR driver Rev. %s\n", ISAR_REV); + return 0; +} + +static void __exit 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 000000000..f8447135a --- /dev/null +++ b/drivers/isdn/hardware/mISDN/netjet.c @@ -0,0 +1,1154 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * NETJet mISDN driver + * + * Author Karsten Keil <keil@isdn4linux.de> + * + * Copyright 2009 by Karsten Keil <keil@isdn4linux.de> + */ + +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/mISDNhw.h> +#include <linux/slab.h> +#include "ipac.h" +#include "iohelper.h" +#include "netjet.h" +#include "isdnhdlc.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, const 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 = dma_alloc_coherent(&card->pdev->dev, NJ_DMA_SIZE, + &card->dma, GFP_ATOMIC); + 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_ATOMIC); + 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_ATOMIC); + 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 (test_bit(FLG_RX_OFF, &bc->bch.Flags)) { + bc->bch.dropcnt += cnt; + return; + } + stat = bchannel_get_rxbuf(&bc->bch, cnt); + /* only transparent use the count here, HDLC overun is detected later */ + if (stat == -ENOMEM) { + pr_warn("%s.B%d: No memory for %d bytes\n", + card->name, bc->bch.nr, cnt); + return; + } + if (test_bit(FLG_TRANSPARENT, &bc->bch.Flags)) + 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; + } + + if (test_bit(FLG_TRANSPARENT, &bc->bch.Flags)) { + recv_Bchannel(&bc->bch, 0, false); + return; + } + + pn = bc->hrbuf; + while (cnt > 0) { + 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); + 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, false); + stat = bchannel_get_rxbuf(&bc->bch, bc->bch.maxlen); + if (stat < 0) { + pr_warn("%s.B%d: No memory for %d bytes\n", + card->name, bc->bch.nr, cnt); + return; + } + } 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); + } + pn += i; + cnt -= i; + } +} + +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, fillempty = 0; + u32 m, v, n = 0; + u8 *p; + + if (bc->free == 0) + return; + if (!bc->bch.tx_skb) { + if (!test_bit(FLG_TX_EMPTY, &bc->bch.Flags)) + return; + fillempty = 1; + count = card->send.size >> 1; + p = bc->bch.fill; + } else { + 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); + p = bc->bch.tx_skb->data + bc->bch.tx_idx; + } + if (bc->txstate & (TX_IDLE | TX_INIT | TX_UNDERRUN)) + resync(bc, card); + if (test_bit(FLG_HDLC, &bc->bch.Flags) && !fillempty) { + 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; + if (!fillempty) + bc->bch.tx_idx += count; + bc->free -= count; + } + m = (bc->bch.nr & 1) ? 0xffffff00 : 0xffff00ff; + if (fillempty) { + n = p[0]; + if (!(bc->bch.nr & 1)) + n <<= 8; + for (i = 0; i < count; i++) { + if (bc->idx >= card->send.size) + bc->idx = 0; + v = card->send.start[bc->idx]; + v &= m; + v |= n; + card->send.start[bc->idx++] = v; + } + } else { + for (i = 0; i < count; i++) { + if (bc->idx >= card->send.size) + bc->idx = 0; + v = card->send.start[bc->idx]; + v &= m; + n = p[i]; + v |= (bc->bch.nr & 1) ? n : n << 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) +{ + int ret = 1; + + if (bc->bch.tx_skb && bc->bch.tx_idx < bc->bch.tx_skb->len) { + fill_dma(bc); + } else { + dev_kfree_skb(bc->bch.tx_skb); + if (get_next_bframe(&bc->bch)) { + fill_dma(bc); + test_and_clear_bit(FLG_TX_EMPTY, &bc->bch.Flags); + } else if (test_bit(FLG_TX_EMPTY, &bc->bch.Flags)) { + fill_dma(bc); + } else if (test_bit(FLG_FILLEMPTY, &bc->bch.Flags)) { + test_and_set_bit(FLG_TX_EMPTY, &bc->bch.Flags); + ret = 0; + } else { + ret = 0; + } + } + return ret; +} + +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); + unsigned 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 */ + fill_dma(bc); + ret = 0; + } + 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) +{ + return mISDN_ctrl_bchannel(&bc->bch, cq); +} + +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); + cancel_work_sync(&bch->workq); + spin_lock_irqsave(&card->lock, flags); + mISDN_clear_bchannel(bch); + 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 | MISDN_CTRL_L1_TIMER3; + 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; + case MISDN_CTRL_L1_TIMER3: + ret = card->isac.ctrl(&card->isac, HW_TIMER3_VALUE, cq->p1); + 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 == 0 || 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); + spin_unlock_irqrestore(&card->lock, flags); + card->isac.release(&card->isac); + release_region(card->base, card->base_s); + card->base_s = 0; + } + if (card->irq > 0) + free_irq(card->irq, card); + if (device_is_registered(&card->isac.dch.dev.dev)) + 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) + dma_free_coherent(&card->pdev->dev, 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 +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, + NJ_DMA_RXSIZE >> 1); + 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 +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; + } + + if (pdev->subsystem_vendor == 0xb100 && + pdev->subsystem_device == 0x0003) { + pr_notice("Netjet: Digium TDM400P not handled yet\n"); + return -ENODEV; + } + + card = kzalloc(sizeof(struct tiger_hw), GFP_KERNEL); + 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); + pci_set_drvdata(pdev, card); + err = setup_instance(card); + if (err) + pci_set_drvdata(pdev, NULL); + + return err; +} + + +static void 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 const struct pci_device_id nj_pci_ids[] = { + { 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 = 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 000000000..b23ad9f6d --- /dev/null +++ b/drivers/isdn/hardware/mISDN/netjet.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * 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> + */ + +#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 000000000..b530c78ec --- /dev/null +++ b/drivers/isdn/hardware/mISDN/speedfax.c @@ -0,0 +1,519 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * 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> + */ + +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/slab.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, const 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_FIRMWARE("isdn/ISAR.BIN"); +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 | MISDN_CTRL_L1_TIMER3; + 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; + case MISDN_CTRL_L1_TIMER3: + ret = sf->isac.ctrl(&sf->isac, HW_TIMER3_VALUE, cq->p1); + 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 +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 +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 +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 +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 +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 already removed\n", __func__); +} + +static struct pci_device_id sfaxpci_ids[] = { + { 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 = 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 000000000..f3b8db7b4 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/w6692.c @@ -0,0 +1,1416 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * 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> + */ + +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/mISDNhw.h> +#include <linux/slab.h> +#include "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"} +}; + +#define PCI_DEVICE_ID_USR_6692 0x3409 + +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, const 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); + } + 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 + } + 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 { + 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; + int maxlen; + + 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 (test_bit(FLG_RX_OFF, &wch->bch.Flags)) { + wch->bch.dropcnt += count; + WriteW6692B(wch, W_B_CMDR, W_B_CMDR_RACK | W_B_CMDR_RACT); + return; + } + maxlen = bchannel_get_rxbuf(&wch->bch, count); + if (maxlen < 0) { + 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); + pr_warn("%s.B%d: No bufferspace for %d bytes\n", + card->name, wch->bch.nr, count); + 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, fillempty = 0; + u8 *ptr, cmd = W_B_CMDR_RACT | W_B_CMDR_XMS; + + pr_debug("%s: fill Bfifo\n", card->name); + if (!wch->bch.tx_skb) { + if (!test_bit(FLG_TX_EMPTY, &wch->bch.Flags)) + return; + ptr = wch->bch.fill; + count = W_B_FIFO_THRESH; + fillempty = 1; + } else { + 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; + if (fillempty) { + while (count > 0) { + outsb(wch->addr + W_B_XFIFO, ptr, MISDN_BCH_FILL_SIZE); + count -= MISDN_BCH_FILL_SIZE; + } + } else { + outsb(wch->addr + W_B_XFIFO, ptr, count); + } + WriteW6692B(wch, W_B_CMDR, cmd); + if ((debug & DEBUG_HW_BFIFO) && !fillempty) { + 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); + } +} + +#if 0 +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; +} +#endif + +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 { + dev_kfree_skb(wch->bch.tx_skb); + if (get_next_bframe(&wch->bch)) { + W6692_fill_Bfifo(wch); + test_and_clear_bit(FLG_TX_EMPTY, &wch->bch.Flags); + } else if (test_bit(FLG_TX_EMPTY, &wch->bch.Flags)) { + 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, false); + } + } + 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)) + recv_Bchannel(&wch->bch, 0, false); + } + } + 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_warn("%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 (star & W_B_STAR_XDOW) + return; /* handle XDOW only once */ + } + if (stat & W_B_EXI_XDUN) { + pr_warn("%s: B%d XDUN proto=%x\n", card->name, + wch->bch.nr, wch->bch.state); +#ifdef ERROR_STATISTIC + wch->bch.err_xdu++; +#endif + /* resend - no XRST needed */ + if (wch->bch.tx_skb) { + if (!test_bit(FLG_TRANSPARENT, &wch->bch.Flags)) + wch->bch.tx_idx = 0; + } else if (test_bit(FLG_FILLEMPTY, &wch->bch.Flags)) { + test_and_set_bit(FLG_TX_EMPTY, &wch->bch.Flags); + } + 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 timer_list *t) +{ + struct dchannel *dch = from_timer(dch, t, timer); + 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); + } +} + +static void initW6692(struct w6692_hw *card) +{ + u8 val; + + timer_setup(&card->dch.timer, dbusy_timer_handler, 0); + 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); + unsigned 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 */ + ret = 0; + W6692_fill_Bfifo(bc); + } + 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) +{ + return mISDN_ctrl_bchannel(bch, cq); +} + +static int +open_bchannel(struct w6692_hw *card, struct channel_req *rq) +{ + struct bchannel *bch; + + if (rq->adr.channel == 0 || 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 */ + 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 = MISDN_CTRL_L1_TIMER3; + break; + case MISDN_CTRL_L1_TIMER3: + ret = l1_event(card->dch.l1, HW_TIMER3_VALUE | (cq->p1 & 0xff)); + 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); + cancel_work_sync(&bch->workq); + spin_lock_irqsave(&card->lock, flags); + mISDN_clear_bchannel(bch); + w6692_mode(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(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, void *caller) +{ + pr_debug("%s: %s dev(%d) open from %p\n", card->name, __func__, + card->dch.dev.id, caller); + 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, __builtin_return_address(0)); + 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, + W_B_FIFO_THRESH); + 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 +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 +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 already removed\n", __func__); +} + +static const 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 = 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 000000000..45e1dc5d6 --- /dev/null +++ b/drivers/isdn/hardware/mISDN/w6692.h @@ -0,0 +1,177 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * 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> + */ + +/* 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 |