1*82c29810SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 26115d2f3SKarsten Keil /* 36115d2f3SKarsten Keil * avm_fritz.c low level stuff for AVM FRITZ!CARD PCI ISDN cards 46115d2f3SKarsten Keil * Thanks to AVM, Berlin for informations 56115d2f3SKarsten Keil * 66115d2f3SKarsten Keil * Author Karsten Keil <keil@isdn4linux.de> 76115d2f3SKarsten Keil * 86115d2f3SKarsten Keil * Copyright 2009 by Karsten Keil <keil@isdn4linux.de> 96115d2f3SKarsten Keil */ 10a6b7a407SAlexey Dobriyan #include <linux/interrupt.h> 116115d2f3SKarsten Keil #include <linux/module.h> 126115d2f3SKarsten Keil #include <linux/pci.h> 136115d2f3SKarsten Keil #include <linux/delay.h> 146115d2f3SKarsten Keil #include <linux/mISDNhw.h> 155a0e3ad6STejun Heo #include <linux/slab.h> 166115d2f3SKarsten Keil #include <asm/unaligned.h> 176115d2f3SKarsten Keil #include "ipac.h" 186115d2f3SKarsten Keil 196115d2f3SKarsten Keil 206d1ee48fSKarsten Keil #define AVMFRITZ_REV "2.3" 216115d2f3SKarsten Keil 226115d2f3SKarsten Keil static int AVM_cnt; 236115d2f3SKarsten Keil static int debug; 246115d2f3SKarsten Keil 256115d2f3SKarsten Keil enum { 266115d2f3SKarsten Keil AVM_FRITZ_PCI, 276115d2f3SKarsten Keil AVM_FRITZ_PCIV2, 286115d2f3SKarsten Keil }; 296115d2f3SKarsten Keil 306115d2f3SKarsten Keil #define HDLC_FIFO 0x0 316115d2f3SKarsten Keil #define HDLC_STATUS 0x4 326115d2f3SKarsten Keil #define CHIP_WINDOW 0x10 336115d2f3SKarsten Keil 346115d2f3SKarsten Keil #define CHIP_INDEX 0x4 356115d2f3SKarsten Keil #define AVM_HDLC_1 0x00 366115d2f3SKarsten Keil #define AVM_HDLC_2 0x01 376115d2f3SKarsten Keil #define AVM_ISAC_FIFO 0x02 386115d2f3SKarsten Keil #define AVM_ISAC_REG_LOW 0x04 396115d2f3SKarsten Keil #define AVM_ISAC_REG_HIGH 0x06 406115d2f3SKarsten Keil 416115d2f3SKarsten Keil #define AVM_STATUS0_IRQ_ISAC 0x01 426115d2f3SKarsten Keil #define AVM_STATUS0_IRQ_HDLC 0x02 436115d2f3SKarsten Keil #define AVM_STATUS0_IRQ_TIMER 0x04 446115d2f3SKarsten Keil #define AVM_STATUS0_IRQ_MASK 0x07 456115d2f3SKarsten Keil 466115d2f3SKarsten Keil #define AVM_STATUS0_RESET 0x01 476115d2f3SKarsten Keil #define AVM_STATUS0_DIS_TIMER 0x02 486115d2f3SKarsten Keil #define AVM_STATUS0_RES_TIMER 0x04 496115d2f3SKarsten Keil #define AVM_STATUS0_ENA_IRQ 0x08 506115d2f3SKarsten Keil #define AVM_STATUS0_TESTBIT 0x10 516115d2f3SKarsten Keil 526115d2f3SKarsten Keil #define AVM_STATUS1_INT_SEL 0x0f 536115d2f3SKarsten Keil #define AVM_STATUS1_ENA_IOM 0x80 546115d2f3SKarsten Keil 556115d2f3SKarsten Keil #define HDLC_MODE_ITF_FLG 0x01 566115d2f3SKarsten Keil #define HDLC_MODE_TRANS 0x02 576115d2f3SKarsten Keil #define HDLC_MODE_CCR_7 0x04 586115d2f3SKarsten Keil #define HDLC_MODE_CCR_16 0x08 5909e79a77SKarsten Keil #define HDLC_FIFO_SIZE_128 0x20 606115d2f3SKarsten Keil #define HDLC_MODE_TESTLOOP 0x80 616115d2f3SKarsten Keil 626115d2f3SKarsten Keil #define HDLC_INT_XPR 0x80 636115d2f3SKarsten Keil #define HDLC_INT_XDU 0x40 646115d2f3SKarsten Keil #define HDLC_INT_RPR 0x20 656115d2f3SKarsten Keil #define HDLC_INT_MASK 0xE0 666115d2f3SKarsten Keil 676115d2f3SKarsten Keil #define HDLC_STAT_RME 0x01 686115d2f3SKarsten Keil #define HDLC_STAT_RDO 0x10 696115d2f3SKarsten Keil #define HDLC_STAT_CRCVFRRAB 0x0E 706115d2f3SKarsten Keil #define HDLC_STAT_CRCVFR 0x06 7109e79a77SKarsten Keil #define HDLC_STAT_RML_MASK_V1 0x3f00 7209e79a77SKarsten Keil #define HDLC_STAT_RML_MASK_V2 0x7f00 736115d2f3SKarsten Keil 746115d2f3SKarsten Keil #define HDLC_CMD_XRS 0x80 756115d2f3SKarsten Keil #define HDLC_CMD_XME 0x01 766115d2f3SKarsten Keil #define HDLC_CMD_RRS 0x20 776115d2f3SKarsten Keil #define HDLC_CMD_XML_MASK 0x3f00 7809e79a77SKarsten Keil 7909e79a77SKarsten Keil #define HDLC_FIFO_SIZE_V1 32 8009e79a77SKarsten Keil #define HDLC_FIFO_SIZE_V2 128 816115d2f3SKarsten Keil 826115d2f3SKarsten Keil /* Fritz PCI v2.0 */ 836115d2f3SKarsten Keil 846115d2f3SKarsten Keil #define AVM_HDLC_FIFO_1 0x10 856115d2f3SKarsten Keil #define AVM_HDLC_FIFO_2 0x18 866115d2f3SKarsten Keil 876115d2f3SKarsten Keil #define AVM_HDLC_STATUS_1 0x14 886115d2f3SKarsten Keil #define AVM_HDLC_STATUS_2 0x1c 896115d2f3SKarsten Keil 906115d2f3SKarsten Keil #define AVM_ISACX_INDEX 0x04 916115d2f3SKarsten Keil #define AVM_ISACX_DATA 0x08 926115d2f3SKarsten Keil 936115d2f3SKarsten Keil /* data struct */ 946115d2f3SKarsten Keil #define LOG_SIZE 63 956115d2f3SKarsten Keil 966115d2f3SKarsten Keil struct hdlc_stat_reg { 976115d2f3SKarsten Keil #ifdef __BIG_ENDIAN 986115d2f3SKarsten Keil u8 fill; 996115d2f3SKarsten Keil u8 mode; 1006115d2f3SKarsten Keil u8 xml; 1016115d2f3SKarsten Keil u8 cmd; 1026115d2f3SKarsten Keil #else 1036115d2f3SKarsten Keil u8 cmd; 1046115d2f3SKarsten Keil u8 xml; 1056115d2f3SKarsten Keil u8 mode; 1066115d2f3SKarsten Keil u8 fill; 1076115d2f3SKarsten Keil #endif 1086115d2f3SKarsten Keil } __attribute__((packed)); 1096115d2f3SKarsten Keil 1106115d2f3SKarsten Keil struct hdlc_hw { 1116115d2f3SKarsten Keil union { 1126115d2f3SKarsten Keil u32 ctrl; 1136115d2f3SKarsten Keil struct hdlc_stat_reg sr; 1146115d2f3SKarsten Keil } ctrl; 1156115d2f3SKarsten Keil u32 stat; 1166115d2f3SKarsten Keil }; 1176115d2f3SKarsten Keil 1186115d2f3SKarsten Keil struct fritzcard { 1196115d2f3SKarsten Keil struct list_head list; 1206115d2f3SKarsten Keil struct pci_dev *pdev; 1216115d2f3SKarsten Keil char name[MISDN_MAX_IDLEN]; 1226115d2f3SKarsten Keil u8 type; 1236115d2f3SKarsten Keil u8 ctrlreg; 1246115d2f3SKarsten Keil u16 irq; 1256115d2f3SKarsten Keil u32 irqcnt; 1266115d2f3SKarsten Keil u32 addr; 1276115d2f3SKarsten Keil spinlock_t lock; /* hw lock */ 1286115d2f3SKarsten Keil struct isac_hw isac; 1296115d2f3SKarsten Keil struct hdlc_hw hdlc[2]; 1306115d2f3SKarsten Keil struct bchannel bch[2]; 1316115d2f3SKarsten Keil char log[LOG_SIZE + 1]; 1326115d2f3SKarsten Keil }; 1336115d2f3SKarsten Keil 1346115d2f3SKarsten Keil static LIST_HEAD(Cards); 1356115d2f3SKarsten Keil static DEFINE_RWLOCK(card_lock); /* protect Cards */ 1366115d2f3SKarsten Keil 1376115d2f3SKarsten Keil static void 1386115d2f3SKarsten Keil _set_debug(struct fritzcard *card) 1396115d2f3SKarsten Keil { 1406115d2f3SKarsten Keil card->isac.dch.debug = debug; 1416115d2f3SKarsten Keil card->bch[0].debug = debug; 1426115d2f3SKarsten Keil card->bch[1].debug = debug; 1436115d2f3SKarsten Keil } 1446115d2f3SKarsten Keil 1456115d2f3SKarsten Keil static int 146e4dca7b7SKees Cook set_debug(const char *val, const struct kernel_param *kp) 1476115d2f3SKarsten Keil { 1486115d2f3SKarsten Keil int ret; 1496115d2f3SKarsten Keil struct fritzcard *card; 1506115d2f3SKarsten Keil 1516115d2f3SKarsten Keil ret = param_set_uint(val, kp); 1526115d2f3SKarsten Keil if (!ret) { 1536115d2f3SKarsten Keil read_lock(&card_lock); 1546115d2f3SKarsten Keil list_for_each_entry(card, &Cards, list) 1556115d2f3SKarsten Keil _set_debug(card); 1566115d2f3SKarsten Keil read_unlock(&card_lock); 1576115d2f3SKarsten Keil } 1586115d2f3SKarsten Keil return ret; 1596115d2f3SKarsten Keil } 1606115d2f3SKarsten Keil 1616115d2f3SKarsten Keil MODULE_AUTHOR("Karsten Keil"); 1626115d2f3SKarsten Keil MODULE_LICENSE("GPL v2"); 1636115d2f3SKarsten Keil MODULE_VERSION(AVMFRITZ_REV); 1646115d2f3SKarsten Keil module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR); 1656115d2f3SKarsten Keil MODULE_PARM_DESC(debug, "avmfritz debug mask"); 1666115d2f3SKarsten Keil 1676115d2f3SKarsten Keil /* Interface functions */ 1686115d2f3SKarsten Keil 1696115d2f3SKarsten Keil static u8 1706115d2f3SKarsten Keil ReadISAC_V1(void *p, u8 offset) 1716115d2f3SKarsten Keil { 1726115d2f3SKarsten Keil struct fritzcard *fc = p; 1736115d2f3SKarsten Keil u8 idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW; 1746115d2f3SKarsten Keil 1756115d2f3SKarsten Keil outb(idx, fc->addr + CHIP_INDEX); 1766115d2f3SKarsten Keil return inb(fc->addr + CHIP_WINDOW + (offset & 0xf)); 1776115d2f3SKarsten Keil } 1786115d2f3SKarsten Keil 1796115d2f3SKarsten Keil static void 1806115d2f3SKarsten Keil WriteISAC_V1(void *p, u8 offset, u8 value) 1816115d2f3SKarsten Keil { 1826115d2f3SKarsten Keil struct fritzcard *fc = p; 1836115d2f3SKarsten Keil u8 idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW; 1846115d2f3SKarsten Keil 1856115d2f3SKarsten Keil outb(idx, fc->addr + CHIP_INDEX); 1866115d2f3SKarsten Keil outb(value, fc->addr + CHIP_WINDOW + (offset & 0xf)); 1876115d2f3SKarsten Keil } 1886115d2f3SKarsten Keil 1896115d2f3SKarsten Keil static void 1906115d2f3SKarsten Keil ReadFiFoISAC_V1(void *p, u8 off, u8 *data, int size) 1916115d2f3SKarsten Keil { 1926115d2f3SKarsten Keil struct fritzcard *fc = p; 1936115d2f3SKarsten Keil 1946115d2f3SKarsten Keil outb(AVM_ISAC_FIFO, fc->addr + CHIP_INDEX); 1956115d2f3SKarsten Keil insb(fc->addr + CHIP_WINDOW, data, size); 1966115d2f3SKarsten Keil } 1976115d2f3SKarsten Keil 1986115d2f3SKarsten Keil static void 1996115d2f3SKarsten Keil WriteFiFoISAC_V1(void *p, u8 off, u8 *data, int size) 2006115d2f3SKarsten Keil { 2016115d2f3SKarsten Keil struct fritzcard *fc = p; 2026115d2f3SKarsten Keil 2036115d2f3SKarsten Keil outb(AVM_ISAC_FIFO, fc->addr + CHIP_INDEX); 2046115d2f3SKarsten Keil outsb(fc->addr + CHIP_WINDOW, data, size); 2056115d2f3SKarsten Keil } 2066115d2f3SKarsten Keil 2076115d2f3SKarsten Keil static u8 2086115d2f3SKarsten Keil ReadISAC_V2(void *p, u8 offset) 2096115d2f3SKarsten Keil { 2106115d2f3SKarsten Keil struct fritzcard *fc = p; 2116115d2f3SKarsten Keil 2126115d2f3SKarsten Keil outl(offset, fc->addr + AVM_ISACX_INDEX); 2136115d2f3SKarsten Keil return 0xff & inl(fc->addr + AVM_ISACX_DATA); 2146115d2f3SKarsten Keil } 2156115d2f3SKarsten Keil 2166115d2f3SKarsten Keil static void 2176115d2f3SKarsten Keil WriteISAC_V2(void *p, u8 offset, u8 value) 2186115d2f3SKarsten Keil { 2196115d2f3SKarsten Keil struct fritzcard *fc = p; 2206115d2f3SKarsten Keil 2216115d2f3SKarsten Keil outl(offset, fc->addr + AVM_ISACX_INDEX); 2226115d2f3SKarsten Keil outl(value, fc->addr + AVM_ISACX_DATA); 2236115d2f3SKarsten Keil } 2246115d2f3SKarsten Keil 2256115d2f3SKarsten Keil static void 2266115d2f3SKarsten Keil ReadFiFoISAC_V2(void *p, u8 off, u8 *data, int size) 2276115d2f3SKarsten Keil { 2286115d2f3SKarsten Keil struct fritzcard *fc = p; 2296115d2f3SKarsten Keil int i; 2306115d2f3SKarsten Keil 2316115d2f3SKarsten Keil outl(off, fc->addr + AVM_ISACX_INDEX); 2326115d2f3SKarsten Keil for (i = 0; i < size; i++) 2336115d2f3SKarsten Keil data[i] = 0xff & inl(fc->addr + AVM_ISACX_DATA); 2346115d2f3SKarsten Keil } 2356115d2f3SKarsten Keil 2366115d2f3SKarsten Keil static void 2376115d2f3SKarsten Keil WriteFiFoISAC_V2(void *p, u8 off, u8 *data, int size) 2386115d2f3SKarsten Keil { 2396115d2f3SKarsten Keil struct fritzcard *fc = p; 2406115d2f3SKarsten Keil int i; 2416115d2f3SKarsten Keil 2426115d2f3SKarsten Keil outl(off, fc->addr + AVM_ISACX_INDEX); 2436115d2f3SKarsten Keil for (i = 0; i < size; i++) 2446115d2f3SKarsten Keil outl(data[i], fc->addr + AVM_ISACX_DATA); 2456115d2f3SKarsten Keil } 2466115d2f3SKarsten Keil 2476115d2f3SKarsten Keil static struct bchannel * 2486115d2f3SKarsten Keil Sel_BCS(struct fritzcard *fc, u32 channel) 2496115d2f3SKarsten Keil { 2506115d2f3SKarsten Keil if (test_bit(FLG_ACTIVE, &fc->bch[0].Flags) && 2516115d2f3SKarsten Keil (fc->bch[0].nr & channel)) 2526115d2f3SKarsten Keil return &fc->bch[0]; 2536115d2f3SKarsten Keil else if (test_bit(FLG_ACTIVE, &fc->bch[1].Flags) && 2546115d2f3SKarsten Keil (fc->bch[1].nr & channel)) 2556115d2f3SKarsten Keil return &fc->bch[1]; 2566115d2f3SKarsten Keil else 2576115d2f3SKarsten Keil return NULL; 2586115d2f3SKarsten Keil } 2596115d2f3SKarsten Keil 2606115d2f3SKarsten Keil static inline void 2616115d2f3SKarsten Keil __write_ctrl_pci(struct fritzcard *fc, struct hdlc_hw *hdlc, u32 channel) { 2626115d2f3SKarsten Keil u32 idx = channel == 2 ? AVM_HDLC_2 : AVM_HDLC_1; 2636115d2f3SKarsten Keil 2646115d2f3SKarsten Keil outl(idx, fc->addr + CHIP_INDEX); 2656115d2f3SKarsten Keil outl(hdlc->ctrl.ctrl, fc->addr + CHIP_WINDOW + HDLC_STATUS); 2666115d2f3SKarsten Keil } 2676115d2f3SKarsten Keil 2686115d2f3SKarsten Keil static inline void 2696115d2f3SKarsten Keil __write_ctrl_pciv2(struct fritzcard *fc, struct hdlc_hw *hdlc, u32 channel) { 2706115d2f3SKarsten Keil outl(hdlc->ctrl.ctrl, fc->addr + (channel == 2 ? AVM_HDLC_STATUS_2 : 2716115d2f3SKarsten Keil AVM_HDLC_STATUS_1)); 2726115d2f3SKarsten Keil } 2736115d2f3SKarsten Keil 274569e937eSBaoyou Xie static void 2756115d2f3SKarsten Keil write_ctrl(struct bchannel *bch, int which) { 2766115d2f3SKarsten Keil struct fritzcard *fc = bch->hw; 2776115d2f3SKarsten Keil struct hdlc_hw *hdlc; 2786115d2f3SKarsten Keil 2796115d2f3SKarsten Keil hdlc = &fc->hdlc[(bch->nr - 1) & 1]; 2806115d2f3SKarsten Keil pr_debug("%s: hdlc %c wr%x ctrl %x\n", fc->name, '@' + bch->nr, 2816115d2f3SKarsten Keil which, hdlc->ctrl.ctrl); 2826115d2f3SKarsten Keil switch (fc->type) { 2836115d2f3SKarsten Keil case AVM_FRITZ_PCIV2: 2846115d2f3SKarsten Keil __write_ctrl_pciv2(fc, hdlc, bch->nr); 2856115d2f3SKarsten Keil break; 2866115d2f3SKarsten Keil case AVM_FRITZ_PCI: 2876115d2f3SKarsten Keil __write_ctrl_pci(fc, hdlc, bch->nr); 2886115d2f3SKarsten Keil break; 2896115d2f3SKarsten Keil } 2906115d2f3SKarsten Keil } 2916115d2f3SKarsten Keil 2926115d2f3SKarsten Keil 2936115d2f3SKarsten Keil static inline u32 2946115d2f3SKarsten Keil __read_status_pci(u_long addr, u32 channel) 2956115d2f3SKarsten Keil { 2966115d2f3SKarsten Keil outl(channel == 2 ? AVM_HDLC_2 : AVM_HDLC_1, addr + CHIP_INDEX); 2976115d2f3SKarsten Keil return inl(addr + CHIP_WINDOW + HDLC_STATUS); 2986115d2f3SKarsten Keil } 2996115d2f3SKarsten Keil 3006115d2f3SKarsten Keil static inline u32 3016115d2f3SKarsten Keil __read_status_pciv2(u_long addr, u32 channel) 3026115d2f3SKarsten Keil { 3036115d2f3SKarsten Keil return inl(addr + (channel == 2 ? AVM_HDLC_STATUS_2 : 3046115d2f3SKarsten Keil AVM_HDLC_STATUS_1)); 3056115d2f3SKarsten Keil } 3066115d2f3SKarsten Keil 3076115d2f3SKarsten Keil 3086115d2f3SKarsten Keil static u32 3096115d2f3SKarsten Keil read_status(struct fritzcard *fc, u32 channel) 3106115d2f3SKarsten Keil { 3116115d2f3SKarsten Keil switch (fc->type) { 3126115d2f3SKarsten Keil case AVM_FRITZ_PCIV2: 3136115d2f3SKarsten Keil return __read_status_pciv2(fc->addr, channel); 3146115d2f3SKarsten Keil case AVM_FRITZ_PCI: 3156115d2f3SKarsten Keil return __read_status_pci(fc->addr, channel); 3166115d2f3SKarsten Keil } 3176115d2f3SKarsten Keil /* dummy */ 3186115d2f3SKarsten Keil return 0; 3196115d2f3SKarsten Keil } 3206115d2f3SKarsten Keil 3216115d2f3SKarsten Keil static void 3226115d2f3SKarsten Keil enable_hwirq(struct fritzcard *fc) 3236115d2f3SKarsten Keil { 3246115d2f3SKarsten Keil fc->ctrlreg |= AVM_STATUS0_ENA_IRQ; 3256115d2f3SKarsten Keil outb(fc->ctrlreg, fc->addr + 2); 3266115d2f3SKarsten Keil } 3276115d2f3SKarsten Keil 3286115d2f3SKarsten Keil static void 3296115d2f3SKarsten Keil disable_hwirq(struct fritzcard *fc) 3306115d2f3SKarsten Keil { 3316115d2f3SKarsten Keil fc->ctrlreg &= ~AVM_STATUS0_ENA_IRQ; 3326115d2f3SKarsten Keil outb(fc->ctrlreg, fc->addr + 2); 3336115d2f3SKarsten Keil } 3346115d2f3SKarsten Keil 3356115d2f3SKarsten Keil static int 3366115d2f3SKarsten Keil modehdlc(struct bchannel *bch, int protocol) 3376115d2f3SKarsten Keil { 3386115d2f3SKarsten Keil struct fritzcard *fc = bch->hw; 3396115d2f3SKarsten Keil struct hdlc_hw *hdlc; 34009e79a77SKarsten Keil u8 mode; 3416115d2f3SKarsten Keil 3426115d2f3SKarsten Keil hdlc = &fc->hdlc[(bch->nr - 1) & 1]; 3436115d2f3SKarsten Keil pr_debug("%s: hdlc %c protocol %x-->%x ch %d\n", fc->name, 3446115d2f3SKarsten Keil '@' + bch->nr, bch->state, protocol, bch->nr); 3456115d2f3SKarsten Keil hdlc->ctrl.ctrl = 0; 34609e79a77SKarsten Keil mode = (fc->type == AVM_FRITZ_PCIV2) ? HDLC_FIFO_SIZE_128 : 0; 34709e79a77SKarsten Keil 3486115d2f3SKarsten Keil switch (protocol) { 3496115d2f3SKarsten Keil case -1: /* used for init */ 3506115d2f3SKarsten Keil bch->state = -1; 351d287c502SGustavo A. R. Silva /* fall through */ 3526115d2f3SKarsten Keil case ISDN_P_NONE: 3536115d2f3SKarsten Keil if (bch->state == ISDN_P_NONE) 3546115d2f3SKarsten Keil break; 3556115d2f3SKarsten Keil hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; 35609e79a77SKarsten Keil hdlc->ctrl.sr.mode = mode | HDLC_MODE_TRANS; 3576115d2f3SKarsten Keil write_ctrl(bch, 5); 3586115d2f3SKarsten Keil bch->state = ISDN_P_NONE; 3596115d2f3SKarsten Keil test_and_clear_bit(FLG_HDLC, &bch->Flags); 3606115d2f3SKarsten Keil test_and_clear_bit(FLG_TRANSPARENT, &bch->Flags); 3616115d2f3SKarsten Keil break; 3626115d2f3SKarsten Keil case ISDN_P_B_RAW: 3636115d2f3SKarsten Keil bch->state = protocol; 3646115d2f3SKarsten Keil hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; 36509e79a77SKarsten Keil hdlc->ctrl.sr.mode = mode | HDLC_MODE_TRANS; 3666115d2f3SKarsten Keil write_ctrl(bch, 5); 3676115d2f3SKarsten Keil hdlc->ctrl.sr.cmd = HDLC_CMD_XRS; 3686115d2f3SKarsten Keil write_ctrl(bch, 1); 3696115d2f3SKarsten Keil hdlc->ctrl.sr.cmd = 0; 3706115d2f3SKarsten Keil test_and_set_bit(FLG_TRANSPARENT, &bch->Flags); 3716115d2f3SKarsten Keil break; 3726115d2f3SKarsten Keil case ISDN_P_B_HDLC: 3736115d2f3SKarsten Keil bch->state = protocol; 3746115d2f3SKarsten Keil hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; 37509e79a77SKarsten Keil hdlc->ctrl.sr.mode = mode | HDLC_MODE_ITF_FLG; 3766115d2f3SKarsten Keil write_ctrl(bch, 5); 3776115d2f3SKarsten Keil hdlc->ctrl.sr.cmd = HDLC_CMD_XRS; 3786115d2f3SKarsten Keil write_ctrl(bch, 1); 3796115d2f3SKarsten Keil hdlc->ctrl.sr.cmd = 0; 3806115d2f3SKarsten Keil test_and_set_bit(FLG_HDLC, &bch->Flags); 3816115d2f3SKarsten Keil break; 3826115d2f3SKarsten Keil default: 3836115d2f3SKarsten Keil pr_info("%s: protocol not known %x\n", fc->name, protocol); 3846115d2f3SKarsten Keil return -ENOPROTOOPT; 3856115d2f3SKarsten Keil } 3866115d2f3SKarsten Keil return 0; 3876115d2f3SKarsten Keil } 3886115d2f3SKarsten Keil 3896115d2f3SKarsten Keil static void 3906115d2f3SKarsten Keil hdlc_empty_fifo(struct bchannel *bch, int count) 3916115d2f3SKarsten Keil { 3926115d2f3SKarsten Keil u32 *ptr; 3936115d2f3SKarsten Keil u8 *p; 3946115d2f3SKarsten Keil u32 val, addr; 3957206e659SKarsten Keil int cnt; 3966115d2f3SKarsten Keil struct fritzcard *fc = bch->hw; 3976115d2f3SKarsten Keil 3986115d2f3SKarsten Keil pr_debug("%s: %s %d\n", fc->name, __func__, count); 399c27b46e7SKarsten Keil if (test_bit(FLG_RX_OFF, &bch->Flags)) { 400c27b46e7SKarsten Keil p = NULL; 401c27b46e7SKarsten Keil bch->dropcnt += count; 402c27b46e7SKarsten Keil } else { 4037206e659SKarsten Keil cnt = bchannel_get_rxbuf(bch, count); 4047206e659SKarsten Keil if (cnt < 0) { 4057206e659SKarsten Keil pr_warning("%s.B%d: No bufferspace for %d bytes\n", 4067206e659SKarsten Keil fc->name, bch->nr, count); 4076115d2f3SKarsten Keil return; 4086115d2f3SKarsten Keil } 4096115d2f3SKarsten Keil p = skb_put(bch->rx_skb, count); 410c27b46e7SKarsten Keil } 4116115d2f3SKarsten Keil ptr = (u32 *)p; 41209e79a77SKarsten Keil if (fc->type == AVM_FRITZ_PCIV2) 4136115d2f3SKarsten Keil addr = fc->addr + (bch->nr == 2 ? 4146115d2f3SKarsten Keil AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1); 4156115d2f3SKarsten Keil else { 4166115d2f3SKarsten Keil addr = fc->addr + CHIP_WINDOW; 4176115d2f3SKarsten Keil outl(bch->nr == 2 ? AVM_HDLC_2 : AVM_HDLC_1, fc->addr); 4186115d2f3SKarsten Keil } 4197206e659SKarsten Keil cnt = 0; 4206115d2f3SKarsten Keil while (cnt < count) { 4216115d2f3SKarsten Keil val = le32_to_cpu(inl(addr)); 422c27b46e7SKarsten Keil if (p) { 4236115d2f3SKarsten Keil put_unaligned(val, ptr); 4246115d2f3SKarsten Keil ptr++; 425c27b46e7SKarsten Keil } 4266115d2f3SKarsten Keil cnt += 4; 4276115d2f3SKarsten Keil } 428c27b46e7SKarsten Keil if (p && (debug & DEBUG_HW_BFIFO)) { 4296115d2f3SKarsten Keil snprintf(fc->log, LOG_SIZE, "B%1d-recv %s %d ", 4306115d2f3SKarsten Keil bch->nr, fc->name, count); 4316115d2f3SKarsten Keil print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count); 4326115d2f3SKarsten Keil } 4336115d2f3SKarsten Keil } 4346115d2f3SKarsten Keil 4356115d2f3SKarsten Keil static void 4366115d2f3SKarsten Keil hdlc_fill_fifo(struct bchannel *bch) 4376115d2f3SKarsten Keil { 4386115d2f3SKarsten Keil struct fritzcard *fc = bch->hw; 4396115d2f3SKarsten Keil struct hdlc_hw *hdlc; 440b41a9a66SKarsten Keil int count, fs, cnt = 0, idx; 441b41a9a66SKarsten Keil bool fillempty = false; 4426115d2f3SKarsten Keil u8 *p; 4436115d2f3SKarsten Keil u32 *ptr, val, addr; 4446115d2f3SKarsten Keil 4456d1ee48fSKarsten Keil idx = (bch->nr - 1) & 1; 4466d1ee48fSKarsten Keil hdlc = &fc->hdlc[idx]; 4476d1ee48fSKarsten Keil fs = (fc->type == AVM_FRITZ_PCIV2) ? 4486d1ee48fSKarsten Keil HDLC_FIFO_SIZE_V2 : HDLC_FIFO_SIZE_V1; 4496d1ee48fSKarsten Keil if (!bch->tx_skb) { 4506d1ee48fSKarsten Keil if (!test_bit(FLG_TX_EMPTY, &bch->Flags)) 4516115d2f3SKarsten Keil return; 4526d1ee48fSKarsten Keil count = fs; 4536d1ee48fSKarsten Keil p = bch->fill; 454b41a9a66SKarsten Keil fillempty = true; 4556d1ee48fSKarsten Keil } else { 4566115d2f3SKarsten Keil count = bch->tx_skb->len - bch->tx_idx; 4576115d2f3SKarsten Keil if (count <= 0) 4586115d2f3SKarsten Keil return; 4596115d2f3SKarsten Keil p = bch->tx_skb->data + bch->tx_idx; 4606d1ee48fSKarsten Keil } 4616115d2f3SKarsten Keil hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XME; 46209e79a77SKarsten Keil if (count > fs) { 46309e79a77SKarsten Keil count = fs; 4646115d2f3SKarsten Keil } else { 4656115d2f3SKarsten Keil if (test_bit(FLG_HDLC, &bch->Flags)) 4666115d2f3SKarsten Keil hdlc->ctrl.sr.cmd |= HDLC_CMD_XME; 4676115d2f3SKarsten Keil } 4686115d2f3SKarsten Keil ptr = (u32 *)p; 469b41a9a66SKarsten Keil if (!fillempty) { 4706d1ee48fSKarsten Keil pr_debug("%s.B%d: %d/%d/%d", fc->name, bch->nr, count, 4716d1ee48fSKarsten Keil bch->tx_idx, bch->tx_skb->len); 4726115d2f3SKarsten Keil bch->tx_idx += count; 4736d1ee48fSKarsten Keil } else { 4746d1ee48fSKarsten Keil pr_debug("%s.B%d: fillempty %d\n", fc->name, bch->nr, count); 4756d1ee48fSKarsten Keil } 47609e79a77SKarsten Keil hdlc->ctrl.sr.xml = ((count == fs) ? 0 : count); 47709e79a77SKarsten Keil if (fc->type == AVM_FRITZ_PCIV2) { 4786115d2f3SKarsten Keil __write_ctrl_pciv2(fc, hdlc, bch->nr); 4796115d2f3SKarsten Keil addr = fc->addr + (bch->nr == 2 ? 4806115d2f3SKarsten Keil AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1); 4816115d2f3SKarsten Keil } else { 4826115d2f3SKarsten Keil __write_ctrl_pci(fc, hdlc, bch->nr); 4836115d2f3SKarsten Keil addr = fc->addr + CHIP_WINDOW; 4846115d2f3SKarsten Keil } 4856d1ee48fSKarsten Keil if (fillempty) { 4866d1ee48fSKarsten Keil while (cnt < count) { 4876d1ee48fSKarsten Keil /* all bytes the same - no worry about endian */ 4886d1ee48fSKarsten Keil outl(*ptr, addr); 4896d1ee48fSKarsten Keil cnt += 4; 4906d1ee48fSKarsten Keil } 4916d1ee48fSKarsten Keil } else { 4926115d2f3SKarsten Keil while (cnt < count) { 4936115d2f3SKarsten Keil val = get_unaligned(ptr); 4946115d2f3SKarsten Keil outl(cpu_to_le32(val), addr); 4956115d2f3SKarsten Keil ptr++; 4966115d2f3SKarsten Keil cnt += 4; 4976115d2f3SKarsten Keil } 4986d1ee48fSKarsten Keil } 4996d1ee48fSKarsten Keil if ((debug & DEBUG_HW_BFIFO) && !fillempty) { 5006115d2f3SKarsten Keil snprintf(fc->log, LOG_SIZE, "B%1d-send %s %d ", 5016115d2f3SKarsten Keil bch->nr, fc->name, count); 5026115d2f3SKarsten Keil print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count); 5036115d2f3SKarsten Keil } 5046115d2f3SKarsten Keil } 5056115d2f3SKarsten Keil 5066115d2f3SKarsten Keil static void 5076115d2f3SKarsten Keil HDLC_irq_xpr(struct bchannel *bch) 5086115d2f3SKarsten Keil { 5098bfddfbeSKarsten Keil if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len) { 5106115d2f3SKarsten Keil hdlc_fill_fifo(bch); 5118bfddfbeSKarsten Keil } else { 5128bfddfbeSKarsten Keil if (bch->tx_skb) 5136115d2f3SKarsten Keil dev_kfree_skb(bch->tx_skb); 5146d1ee48fSKarsten Keil if (get_next_bframe(bch)) { 5156115d2f3SKarsten Keil hdlc_fill_fifo(bch); 5166d1ee48fSKarsten Keil test_and_clear_bit(FLG_TX_EMPTY, &bch->Flags); 5176d1ee48fSKarsten Keil } else if (test_bit(FLG_TX_EMPTY, &bch->Flags)) { 5186d1ee48fSKarsten Keil hdlc_fill_fifo(bch); 5196d1ee48fSKarsten Keil } 5206115d2f3SKarsten Keil } 5216115d2f3SKarsten Keil } 5226115d2f3SKarsten Keil 5236115d2f3SKarsten Keil static void 5246115d2f3SKarsten Keil HDLC_irq(struct bchannel *bch, u32 stat) 5256115d2f3SKarsten Keil { 5266115d2f3SKarsten Keil struct fritzcard *fc = bch->hw; 52709e79a77SKarsten Keil int len, fs; 52809e79a77SKarsten Keil u32 rmlMask; 5296115d2f3SKarsten Keil struct hdlc_hw *hdlc; 5306115d2f3SKarsten Keil 5316115d2f3SKarsten Keil hdlc = &fc->hdlc[(bch->nr - 1) & 1]; 5326115d2f3SKarsten Keil pr_debug("%s: ch%d stat %#x\n", fc->name, bch->nr, stat); 53309e79a77SKarsten Keil if (fc->type == AVM_FRITZ_PCIV2) { 53409e79a77SKarsten Keil rmlMask = HDLC_STAT_RML_MASK_V2; 53509e79a77SKarsten Keil fs = HDLC_FIFO_SIZE_V2; 53609e79a77SKarsten Keil } else { 53709e79a77SKarsten Keil rmlMask = HDLC_STAT_RML_MASK_V1; 53809e79a77SKarsten Keil fs = HDLC_FIFO_SIZE_V1; 53909e79a77SKarsten Keil } 5406115d2f3SKarsten Keil if (stat & HDLC_INT_RPR) { 5416115d2f3SKarsten Keil if (stat & HDLC_STAT_RDO) { 54209e79a77SKarsten Keil pr_warning("%s: ch%d stat %x RDO\n", 54309e79a77SKarsten Keil fc->name, bch->nr, stat); 5446115d2f3SKarsten Keil hdlc->ctrl.sr.xml = 0; 5456115d2f3SKarsten Keil hdlc->ctrl.sr.cmd |= HDLC_CMD_RRS; 5466115d2f3SKarsten Keil write_ctrl(bch, 1); 5476115d2f3SKarsten Keil hdlc->ctrl.sr.cmd &= ~HDLC_CMD_RRS; 5486115d2f3SKarsten Keil write_ctrl(bch, 1); 5496115d2f3SKarsten Keil if (bch->rx_skb) 5506115d2f3SKarsten Keil skb_trim(bch->rx_skb, 0); 5516115d2f3SKarsten Keil } else { 55209e79a77SKarsten Keil len = (stat & rmlMask) >> 8; 5536115d2f3SKarsten Keil if (!len) 55409e79a77SKarsten Keil len = fs; 5556115d2f3SKarsten Keil hdlc_empty_fifo(bch, len); 5566115d2f3SKarsten Keil if (!bch->rx_skb) 5576115d2f3SKarsten Keil goto handle_tx; 558034005a0SKarsten Keil if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { 559034005a0SKarsten Keil recv_Bchannel(bch, 0, false); 560034005a0SKarsten Keil } else if (stat & HDLC_STAT_RME) { 561034005a0SKarsten Keil if ((stat & HDLC_STAT_CRCVFRRAB) == 562034005a0SKarsten Keil HDLC_STAT_CRCVFR) { 563034005a0SKarsten Keil recv_Bchannel(bch, 0, false); 5646115d2f3SKarsten Keil } else { 56509e79a77SKarsten Keil pr_warning("%s: got invalid frame\n", 5666115d2f3SKarsten Keil fc->name); 5676115d2f3SKarsten Keil skb_trim(bch->rx_skb, 0); 5686115d2f3SKarsten Keil } 5696115d2f3SKarsten Keil } 5706115d2f3SKarsten Keil } 5716115d2f3SKarsten Keil } 5726115d2f3SKarsten Keil handle_tx: 5736115d2f3SKarsten Keil if (stat & HDLC_INT_XDU) { 5746115d2f3SKarsten Keil /* Here we lost an TX interrupt, so 5756115d2f3SKarsten Keil * restart transmitting the whole frame on HDLC 5766115d2f3SKarsten Keil * in transparent mode we send the next data 5776115d2f3SKarsten Keil */ 57809e79a77SKarsten Keil pr_warning("%s: ch%d stat %x XDU %s\n", fc->name, bch->nr, 57909e79a77SKarsten Keil stat, bch->tx_skb ? "tx_skb" : "no tx_skb"); 5806115d2f3SKarsten Keil if (bch->tx_skb && bch->tx_skb->len) { 5816115d2f3SKarsten Keil if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) 5826115d2f3SKarsten Keil bch->tx_idx = 0; 5836d1ee48fSKarsten Keil } else if (test_bit(FLG_FILLEMPTY, &bch->Flags)) { 5846d1ee48fSKarsten Keil test_and_set_bit(FLG_TX_EMPTY, &bch->Flags); 5856115d2f3SKarsten Keil } 5866115d2f3SKarsten Keil hdlc->ctrl.sr.xml = 0; 5876115d2f3SKarsten Keil hdlc->ctrl.sr.cmd |= HDLC_CMD_XRS; 5886115d2f3SKarsten Keil write_ctrl(bch, 1); 5896115d2f3SKarsten Keil hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XRS; 5906115d2f3SKarsten Keil HDLC_irq_xpr(bch); 5916115d2f3SKarsten Keil return; 5926115d2f3SKarsten Keil } else if (stat & HDLC_INT_XPR) 5936115d2f3SKarsten Keil HDLC_irq_xpr(bch); 5946115d2f3SKarsten Keil } 5956115d2f3SKarsten Keil 5966115d2f3SKarsten Keil static inline void 5976115d2f3SKarsten Keil HDLC_irq_main(struct fritzcard *fc) 5986115d2f3SKarsten Keil { 5996115d2f3SKarsten Keil u32 stat; 6006115d2f3SKarsten Keil struct bchannel *bch; 6016115d2f3SKarsten Keil 6026115d2f3SKarsten Keil stat = read_status(fc, 1); 6036115d2f3SKarsten Keil if (stat & HDLC_INT_MASK) { 6046115d2f3SKarsten Keil bch = Sel_BCS(fc, 1); 6056115d2f3SKarsten Keil if (bch) 6066115d2f3SKarsten Keil HDLC_irq(bch, stat); 6076115d2f3SKarsten Keil else 6086115d2f3SKarsten Keil pr_debug("%s: spurious ch1 IRQ\n", fc->name); 6096115d2f3SKarsten Keil } 6106115d2f3SKarsten Keil stat = read_status(fc, 2); 6116115d2f3SKarsten Keil if (stat & HDLC_INT_MASK) { 6126115d2f3SKarsten Keil bch = Sel_BCS(fc, 2); 6136115d2f3SKarsten Keil if (bch) 6146115d2f3SKarsten Keil HDLC_irq(bch, stat); 6156115d2f3SKarsten Keil else 6166115d2f3SKarsten Keil pr_debug("%s: spurious ch2 IRQ\n", fc->name); 6176115d2f3SKarsten Keil } 6186115d2f3SKarsten Keil } 6196115d2f3SKarsten Keil 6206115d2f3SKarsten Keil static irqreturn_t 6216115d2f3SKarsten Keil avm_fritz_interrupt(int intno, void *dev_id) 6226115d2f3SKarsten Keil { 6236115d2f3SKarsten Keil struct fritzcard *fc = dev_id; 6246115d2f3SKarsten Keil u8 val; 6256115d2f3SKarsten Keil u8 sval; 6266115d2f3SKarsten Keil 6276115d2f3SKarsten Keil spin_lock(&fc->lock); 6286115d2f3SKarsten Keil sval = inb(fc->addr + 2); 6296115d2f3SKarsten Keil pr_debug("%s: irq stat0 %x\n", fc->name, sval); 6306115d2f3SKarsten Keil if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK) { 6316115d2f3SKarsten Keil /* shared IRQ from other HW */ 6326115d2f3SKarsten Keil spin_unlock(&fc->lock); 6336115d2f3SKarsten Keil return IRQ_NONE; 6346115d2f3SKarsten Keil } 6356115d2f3SKarsten Keil fc->irqcnt++; 6366115d2f3SKarsten Keil 6376115d2f3SKarsten Keil if (!(sval & AVM_STATUS0_IRQ_ISAC)) { 6386115d2f3SKarsten Keil val = ReadISAC_V1(fc, ISAC_ISTA); 6396115d2f3SKarsten Keil mISDNisac_irq(&fc->isac, val); 6406115d2f3SKarsten Keil } 6416115d2f3SKarsten Keil if (!(sval & AVM_STATUS0_IRQ_HDLC)) 6426115d2f3SKarsten Keil HDLC_irq_main(fc); 6436115d2f3SKarsten Keil spin_unlock(&fc->lock); 6446115d2f3SKarsten Keil return IRQ_HANDLED; 6456115d2f3SKarsten Keil } 6466115d2f3SKarsten Keil 6476115d2f3SKarsten Keil static irqreturn_t 6486115d2f3SKarsten Keil avm_fritzv2_interrupt(int intno, void *dev_id) 6496115d2f3SKarsten Keil { 6506115d2f3SKarsten Keil struct fritzcard *fc = dev_id; 6516115d2f3SKarsten Keil u8 val; 6526115d2f3SKarsten Keil u8 sval; 6536115d2f3SKarsten Keil 6546115d2f3SKarsten Keil spin_lock(&fc->lock); 6556115d2f3SKarsten Keil sval = inb(fc->addr + 2); 6566115d2f3SKarsten Keil pr_debug("%s: irq stat0 %x\n", fc->name, sval); 6576115d2f3SKarsten Keil if (!(sval & AVM_STATUS0_IRQ_MASK)) { 6586115d2f3SKarsten Keil /* shared IRQ from other HW */ 6596115d2f3SKarsten Keil spin_unlock(&fc->lock); 6606115d2f3SKarsten Keil return IRQ_NONE; 6616115d2f3SKarsten Keil } 6626115d2f3SKarsten Keil fc->irqcnt++; 6636115d2f3SKarsten Keil 6646115d2f3SKarsten Keil if (sval & AVM_STATUS0_IRQ_HDLC) 6656115d2f3SKarsten Keil HDLC_irq_main(fc); 6666115d2f3SKarsten Keil if (sval & AVM_STATUS0_IRQ_ISAC) { 6676115d2f3SKarsten Keil val = ReadISAC_V2(fc, ISACX_ISTA); 6686115d2f3SKarsten Keil mISDNisac_irq(&fc->isac, val); 6696115d2f3SKarsten Keil } 6706115d2f3SKarsten Keil if (sval & AVM_STATUS0_IRQ_TIMER) { 6716115d2f3SKarsten Keil pr_debug("%s: timer irq\n", fc->name); 6726115d2f3SKarsten Keil outb(fc->ctrlreg | AVM_STATUS0_RES_TIMER, fc->addr + 2); 6736115d2f3SKarsten Keil udelay(1); 6746115d2f3SKarsten Keil outb(fc->ctrlreg, fc->addr + 2); 6756115d2f3SKarsten Keil } 6766115d2f3SKarsten Keil spin_unlock(&fc->lock); 6776115d2f3SKarsten Keil return IRQ_HANDLED; 6786115d2f3SKarsten Keil } 6796115d2f3SKarsten Keil 6806115d2f3SKarsten Keil static int 6816115d2f3SKarsten Keil avm_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) 6826115d2f3SKarsten Keil { 6836115d2f3SKarsten Keil struct bchannel *bch = container_of(ch, struct bchannel, ch); 6846115d2f3SKarsten Keil struct fritzcard *fc = bch->hw; 6856115d2f3SKarsten Keil int ret = -EINVAL; 6866115d2f3SKarsten Keil struct mISDNhead *hh = mISDN_HEAD_P(skb); 6878bfddfbeSKarsten Keil unsigned long flags; 6886115d2f3SKarsten Keil 6896115d2f3SKarsten Keil switch (hh->prim) { 6906115d2f3SKarsten Keil case PH_DATA_REQ: 6916115d2f3SKarsten Keil spin_lock_irqsave(&fc->lock, flags); 6926115d2f3SKarsten Keil ret = bchannel_senddata(bch, skb); 6936115d2f3SKarsten Keil if (ret > 0) { /* direct TX */ 6946115d2f3SKarsten Keil hdlc_fill_fifo(bch); 6956115d2f3SKarsten Keil ret = 0; 6968bfddfbeSKarsten Keil } 6976115d2f3SKarsten Keil spin_unlock_irqrestore(&fc->lock, flags); 6986115d2f3SKarsten Keil return ret; 6996115d2f3SKarsten Keil case PH_ACTIVATE_REQ: 7006115d2f3SKarsten Keil spin_lock_irqsave(&fc->lock, flags); 7016115d2f3SKarsten Keil if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) 7026115d2f3SKarsten Keil ret = modehdlc(bch, ch->protocol); 7036115d2f3SKarsten Keil else 7046115d2f3SKarsten Keil ret = 0; 7056115d2f3SKarsten Keil spin_unlock_irqrestore(&fc->lock, flags); 7066115d2f3SKarsten Keil if (!ret) 7076115d2f3SKarsten Keil _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, 7086115d2f3SKarsten Keil NULL, GFP_KERNEL); 7096115d2f3SKarsten Keil break; 7106115d2f3SKarsten Keil case PH_DEACTIVATE_REQ: 7116115d2f3SKarsten Keil spin_lock_irqsave(&fc->lock, flags); 7126115d2f3SKarsten Keil mISDN_clear_bchannel(bch); 7136115d2f3SKarsten Keil modehdlc(bch, ISDN_P_NONE); 7146115d2f3SKarsten Keil spin_unlock_irqrestore(&fc->lock, flags); 7156115d2f3SKarsten Keil _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, 7166115d2f3SKarsten Keil NULL, GFP_KERNEL); 7176115d2f3SKarsten Keil ret = 0; 7186115d2f3SKarsten Keil break; 7196115d2f3SKarsten Keil } 7206115d2f3SKarsten Keil if (!ret) 7216115d2f3SKarsten Keil dev_kfree_skb(skb); 7226115d2f3SKarsten Keil return ret; 7236115d2f3SKarsten Keil } 7246115d2f3SKarsten Keil 7256115d2f3SKarsten Keil static void 7266115d2f3SKarsten Keil inithdlc(struct fritzcard *fc) 7276115d2f3SKarsten Keil { 7286115d2f3SKarsten Keil modehdlc(&fc->bch[0], -1); 7296115d2f3SKarsten Keil modehdlc(&fc->bch[1], -1); 7306115d2f3SKarsten Keil } 7316115d2f3SKarsten Keil 732569e937eSBaoyou Xie static void 7336115d2f3SKarsten Keil clear_pending_hdlc_ints(struct fritzcard *fc) 7346115d2f3SKarsten Keil { 7356115d2f3SKarsten Keil u32 val; 7366115d2f3SKarsten Keil 7376115d2f3SKarsten Keil val = read_status(fc, 1); 7386115d2f3SKarsten Keil pr_debug("%s: HDLC 1 STA %x\n", fc->name, val); 7396115d2f3SKarsten Keil val = read_status(fc, 2); 7406115d2f3SKarsten Keil pr_debug("%s: HDLC 2 STA %x\n", fc->name, val); 7416115d2f3SKarsten Keil } 7426115d2f3SKarsten Keil 7436115d2f3SKarsten Keil static void 7446115d2f3SKarsten Keil reset_avm(struct fritzcard *fc) 7456115d2f3SKarsten Keil { 7466115d2f3SKarsten Keil switch (fc->type) { 7476115d2f3SKarsten Keil case AVM_FRITZ_PCI: 7486115d2f3SKarsten Keil fc->ctrlreg = AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER; 7496115d2f3SKarsten Keil break; 7506115d2f3SKarsten Keil case AVM_FRITZ_PCIV2: 7516115d2f3SKarsten Keil fc->ctrlreg = AVM_STATUS0_RESET; 7526115d2f3SKarsten Keil break; 7536115d2f3SKarsten Keil } 7546115d2f3SKarsten Keil if (debug & DEBUG_HW) 7556115d2f3SKarsten Keil pr_notice("%s: reset\n", fc->name); 7566115d2f3SKarsten Keil disable_hwirq(fc); 7576115d2f3SKarsten Keil mdelay(5); 7586115d2f3SKarsten Keil switch (fc->type) { 7596115d2f3SKarsten Keil case AVM_FRITZ_PCI: 7606115d2f3SKarsten Keil fc->ctrlreg = AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER; 7616115d2f3SKarsten Keil disable_hwirq(fc); 7626115d2f3SKarsten Keil outb(AVM_STATUS1_ENA_IOM, fc->addr + 3); 7636115d2f3SKarsten Keil break; 7646115d2f3SKarsten Keil case AVM_FRITZ_PCIV2: 7656115d2f3SKarsten Keil fc->ctrlreg = 0; 7666115d2f3SKarsten Keil disable_hwirq(fc); 7676115d2f3SKarsten Keil break; 7686115d2f3SKarsten Keil } 7696115d2f3SKarsten Keil mdelay(1); 7706115d2f3SKarsten Keil if (debug & DEBUG_HW) 7716115d2f3SKarsten Keil pr_notice("%s: S0/S1 %x/%x\n", fc->name, 7726115d2f3SKarsten Keil inb(fc->addr + 2), inb(fc->addr + 3)); 7736115d2f3SKarsten Keil } 7746115d2f3SKarsten Keil 7756115d2f3SKarsten Keil static int 7766115d2f3SKarsten Keil init_card(struct fritzcard *fc) 7776115d2f3SKarsten Keil { 7786115d2f3SKarsten Keil int ret, cnt = 3; 7796115d2f3SKarsten Keil u_long flags; 7806115d2f3SKarsten Keil 7816115d2f3SKarsten Keil reset_avm(fc); /* disable IRQ */ 7826115d2f3SKarsten Keil if (fc->type == AVM_FRITZ_PCIV2) 7836115d2f3SKarsten Keil ret = request_irq(fc->irq, avm_fritzv2_interrupt, 7846115d2f3SKarsten Keil IRQF_SHARED, fc->name, fc); 7856115d2f3SKarsten Keil else 7866115d2f3SKarsten Keil ret = request_irq(fc->irq, avm_fritz_interrupt, 7876115d2f3SKarsten Keil IRQF_SHARED, fc->name, fc); 7886115d2f3SKarsten Keil if (ret) { 7896115d2f3SKarsten Keil pr_info("%s: couldn't get interrupt %d\n", 7906115d2f3SKarsten Keil fc->name, fc->irq); 7916115d2f3SKarsten Keil return ret; 7926115d2f3SKarsten Keil } 7936115d2f3SKarsten Keil while (cnt--) { 7946115d2f3SKarsten Keil spin_lock_irqsave(&fc->lock, flags); 7956115d2f3SKarsten Keil ret = fc->isac.init(&fc->isac); 7966115d2f3SKarsten Keil if (ret) { 7976115d2f3SKarsten Keil spin_unlock_irqrestore(&fc->lock, flags); 7986115d2f3SKarsten Keil pr_info("%s: ISAC init failed with %d\n", 7996115d2f3SKarsten Keil fc->name, ret); 8006115d2f3SKarsten Keil break; 8016115d2f3SKarsten Keil } 8026115d2f3SKarsten Keil clear_pending_hdlc_ints(fc); 8036115d2f3SKarsten Keil inithdlc(fc); 8046115d2f3SKarsten Keil enable_hwirq(fc); 8056115d2f3SKarsten Keil /* RESET Receiver and Transmitter */ 80609e79a77SKarsten Keil if (fc->type == AVM_FRITZ_PCIV2) { 8076115d2f3SKarsten Keil WriteISAC_V2(fc, ISACX_MASK, 0); 8086115d2f3SKarsten Keil WriteISAC_V2(fc, ISACX_CMDRD, 0x41); 8096115d2f3SKarsten Keil } else { 8106115d2f3SKarsten Keil WriteISAC_V1(fc, ISAC_MASK, 0); 8116115d2f3SKarsten Keil WriteISAC_V1(fc, ISAC_CMDR, 0x41); 8126115d2f3SKarsten Keil } 8136115d2f3SKarsten Keil spin_unlock_irqrestore(&fc->lock, flags); 8146115d2f3SKarsten Keil /* Timeout 10ms */ 8156115d2f3SKarsten Keil msleep_interruptible(10); 8166115d2f3SKarsten Keil if (debug & DEBUG_HW) 8176115d2f3SKarsten Keil pr_notice("%s: IRQ %d count %d\n", fc->name, 8186115d2f3SKarsten Keil fc->irq, fc->irqcnt); 8196115d2f3SKarsten Keil if (!fc->irqcnt) { 8206115d2f3SKarsten Keil pr_info("%s: IRQ(%d) getting no IRQs during init %d\n", 8216115d2f3SKarsten Keil fc->name, fc->irq, 3 - cnt); 8226115d2f3SKarsten Keil reset_avm(fc); 8236115d2f3SKarsten Keil } else 8246115d2f3SKarsten Keil return 0; 8256115d2f3SKarsten Keil } 8266115d2f3SKarsten Keil free_irq(fc->irq, fc); 8276115d2f3SKarsten Keil return -EIO; 8286115d2f3SKarsten Keil } 8296115d2f3SKarsten Keil 8306115d2f3SKarsten Keil static int 8316115d2f3SKarsten Keil channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) 8326115d2f3SKarsten Keil { 833034005a0SKarsten Keil return mISDN_ctrl_bchannel(bch, cq); 8346115d2f3SKarsten Keil } 8356115d2f3SKarsten Keil 8366115d2f3SKarsten Keil static int 8376115d2f3SKarsten Keil avm_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg) 8386115d2f3SKarsten Keil { 8396115d2f3SKarsten Keil struct bchannel *bch = container_of(ch, struct bchannel, ch); 8406115d2f3SKarsten Keil struct fritzcard *fc = bch->hw; 8416115d2f3SKarsten Keil int ret = -EINVAL; 8426115d2f3SKarsten Keil u_long flags; 8436115d2f3SKarsten Keil 8446115d2f3SKarsten Keil pr_debug("%s: %s cmd:%x %p\n", fc->name, __func__, cmd, arg); 8456115d2f3SKarsten Keil switch (cmd) { 8466115d2f3SKarsten Keil case CLOSE_CHANNEL: 8476115d2f3SKarsten Keil test_and_clear_bit(FLG_OPEN, &bch->Flags); 8484b921edaSKarsten Keil cancel_work_sync(&bch->workq); 8496115d2f3SKarsten Keil spin_lock_irqsave(&fc->lock, flags); 8504b921edaSKarsten Keil mISDN_clear_bchannel(bch); 8516115d2f3SKarsten Keil modehdlc(bch, ISDN_P_NONE); 8526115d2f3SKarsten Keil spin_unlock_irqrestore(&fc->lock, flags); 8536115d2f3SKarsten Keil ch->protocol = ISDN_P_NONE; 8546115d2f3SKarsten Keil ch->peer = NULL; 8556115d2f3SKarsten Keil module_put(THIS_MODULE); 8566115d2f3SKarsten Keil ret = 0; 8576115d2f3SKarsten Keil break; 8586115d2f3SKarsten Keil case CONTROL_CHANNEL: 8596115d2f3SKarsten Keil ret = channel_bctrl(bch, arg); 8606115d2f3SKarsten Keil break; 8616115d2f3SKarsten Keil default: 8626115d2f3SKarsten Keil pr_info("%s: %s unknown prim(%x)\n", fc->name, __func__, cmd); 8636115d2f3SKarsten Keil } 8646115d2f3SKarsten Keil return ret; 8656115d2f3SKarsten Keil } 8666115d2f3SKarsten Keil 8676115d2f3SKarsten Keil static int 8686115d2f3SKarsten Keil channel_ctrl(struct fritzcard *fc, struct mISDN_ctrl_req *cq) 8696115d2f3SKarsten Keil { 8706115d2f3SKarsten Keil int ret = 0; 8716115d2f3SKarsten Keil 8726115d2f3SKarsten Keil switch (cq->op) { 8736115d2f3SKarsten Keil case MISDN_CTRL_GETOP: 874c626c127SKarsten Keil cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_L1_TIMER3; 8756115d2f3SKarsten Keil break; 8766115d2f3SKarsten Keil case MISDN_CTRL_LOOP: 8776115d2f3SKarsten Keil /* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */ 8786115d2f3SKarsten Keil if (cq->channel < 0 || cq->channel > 3) { 8796115d2f3SKarsten Keil ret = -EINVAL; 8806115d2f3SKarsten Keil break; 8816115d2f3SKarsten Keil } 8826115d2f3SKarsten Keil ret = fc->isac.ctrl(&fc->isac, HW_TESTLOOP, cq->channel); 8836115d2f3SKarsten Keil break; 884c626c127SKarsten Keil case MISDN_CTRL_L1_TIMER3: 885c626c127SKarsten Keil ret = fc->isac.ctrl(&fc->isac, HW_TIMER3_VALUE, cq->p1); 886c626c127SKarsten Keil break; 8876115d2f3SKarsten Keil default: 8886115d2f3SKarsten Keil pr_info("%s: %s unknown Op %x\n", fc->name, __func__, cq->op); 8896115d2f3SKarsten Keil ret = -EINVAL; 8906115d2f3SKarsten Keil break; 8916115d2f3SKarsten Keil } 8926115d2f3SKarsten Keil return ret; 8936115d2f3SKarsten Keil } 8946115d2f3SKarsten Keil 8956115d2f3SKarsten Keil static int 8966115d2f3SKarsten Keil open_bchannel(struct fritzcard *fc, struct channel_req *rq) 8976115d2f3SKarsten Keil { 8986115d2f3SKarsten Keil struct bchannel *bch; 8996115d2f3SKarsten Keil 900819a1008SDan Carpenter if (rq->adr.channel == 0 || rq->adr.channel > 2) 9016115d2f3SKarsten Keil return -EINVAL; 9026115d2f3SKarsten Keil if (rq->protocol == ISDN_P_NONE) 9036115d2f3SKarsten Keil return -EINVAL; 9046115d2f3SKarsten Keil bch = &fc->bch[rq->adr.channel - 1]; 9056115d2f3SKarsten Keil if (test_and_set_bit(FLG_OPEN, &bch->Flags)) 9066115d2f3SKarsten Keil return -EBUSY; /* b-channel can be only open once */ 9076115d2f3SKarsten Keil bch->ch.protocol = rq->protocol; 9086115d2f3SKarsten Keil rq->ch = &bch->ch; 9096115d2f3SKarsten Keil return 0; 9106115d2f3SKarsten Keil } 9116115d2f3SKarsten Keil 9126115d2f3SKarsten Keil /* 9136115d2f3SKarsten Keil * device control function 9146115d2f3SKarsten Keil */ 9156115d2f3SKarsten Keil static int 9166115d2f3SKarsten Keil avm_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) 9176115d2f3SKarsten Keil { 9186115d2f3SKarsten Keil struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); 9196115d2f3SKarsten Keil struct dchannel *dch = container_of(dev, struct dchannel, dev); 9206115d2f3SKarsten Keil struct fritzcard *fc = dch->hw; 9216115d2f3SKarsten Keil struct channel_req *rq; 9226115d2f3SKarsten Keil int err = 0; 9236115d2f3SKarsten Keil 9246115d2f3SKarsten Keil pr_debug("%s: %s cmd:%x %p\n", fc->name, __func__, cmd, arg); 9256115d2f3SKarsten Keil switch (cmd) { 9266115d2f3SKarsten Keil case OPEN_CHANNEL: 9276115d2f3SKarsten Keil rq = arg; 9286115d2f3SKarsten Keil if (rq->protocol == ISDN_P_TE_S0) 9296115d2f3SKarsten Keil err = fc->isac.open(&fc->isac, rq); 9306115d2f3SKarsten Keil else 9316115d2f3SKarsten Keil err = open_bchannel(fc, rq); 9326115d2f3SKarsten Keil if (err) 9336115d2f3SKarsten Keil break; 9346115d2f3SKarsten Keil if (!try_module_get(THIS_MODULE)) 9356115d2f3SKarsten Keil pr_info("%s: cannot get module\n", fc->name); 9366115d2f3SKarsten Keil break; 9376115d2f3SKarsten Keil case CLOSE_CHANNEL: 9386115d2f3SKarsten Keil pr_debug("%s: dev(%d) close from %p\n", fc->name, dch->dev.id, 9396115d2f3SKarsten Keil __builtin_return_address(0)); 9406115d2f3SKarsten Keil module_put(THIS_MODULE); 9416115d2f3SKarsten Keil break; 9426115d2f3SKarsten Keil case CONTROL_CHANNEL: 9436115d2f3SKarsten Keil err = channel_ctrl(fc, arg); 9446115d2f3SKarsten Keil break; 9456115d2f3SKarsten Keil default: 9466115d2f3SKarsten Keil pr_debug("%s: %s unknown command %x\n", 9476115d2f3SKarsten Keil fc->name, __func__, cmd); 9486115d2f3SKarsten Keil return -EINVAL; 9496115d2f3SKarsten Keil } 9506115d2f3SKarsten Keil return err; 9516115d2f3SKarsten Keil } 9526115d2f3SKarsten Keil 953569e937eSBaoyou Xie static int 9546115d2f3SKarsten Keil setup_fritz(struct fritzcard *fc) 9556115d2f3SKarsten Keil { 9566115d2f3SKarsten Keil u32 val, ver; 9576115d2f3SKarsten Keil 9586115d2f3SKarsten Keil if (!request_region(fc->addr, 32, fc->name)) { 9596115d2f3SKarsten Keil pr_info("%s: AVM config port %x-%x already in use\n", 9606115d2f3SKarsten Keil fc->name, fc->addr, fc->addr + 31); 9616115d2f3SKarsten Keil return -EIO; 9626115d2f3SKarsten Keil } 9636115d2f3SKarsten Keil switch (fc->type) { 9646115d2f3SKarsten Keil case AVM_FRITZ_PCI: 9656115d2f3SKarsten Keil val = inl(fc->addr); 9666115d2f3SKarsten Keil outl(AVM_HDLC_1, fc->addr + CHIP_INDEX); 9676115d2f3SKarsten Keil ver = inl(fc->addr + CHIP_WINDOW + HDLC_STATUS) >> 24; 9686115d2f3SKarsten Keil if (debug & DEBUG_HW) { 9696115d2f3SKarsten Keil pr_notice("%s: PCI stat %#x\n", fc->name, val); 9706115d2f3SKarsten Keil pr_notice("%s: PCI Class %X Rev %d\n", fc->name, 9716115d2f3SKarsten Keil val & 0xff, (val >> 8) & 0xff); 9726115d2f3SKarsten Keil pr_notice("%s: HDLC version %x\n", fc->name, ver & 0xf); 9736115d2f3SKarsten Keil } 9746115d2f3SKarsten Keil ASSIGN_FUNC(V1, ISAC, fc->isac); 9756115d2f3SKarsten Keil fc->isac.type = IPAC_TYPE_ISAC; 9766115d2f3SKarsten Keil break; 9776115d2f3SKarsten Keil case AVM_FRITZ_PCIV2: 9786115d2f3SKarsten Keil val = inl(fc->addr); 9796115d2f3SKarsten Keil ver = inl(fc->addr + AVM_HDLC_STATUS_1) >> 24; 9806115d2f3SKarsten Keil if (debug & DEBUG_HW) { 9816115d2f3SKarsten Keil pr_notice("%s: PCI V2 stat %#x\n", fc->name, val); 9826115d2f3SKarsten Keil pr_notice("%s: PCI V2 Class %X Rev %d\n", fc->name, 9836115d2f3SKarsten Keil val & 0xff, (val >> 8) & 0xff); 9846115d2f3SKarsten Keil pr_notice("%s: HDLC version %x\n", fc->name, ver & 0xf); 9856115d2f3SKarsten Keil } 9866115d2f3SKarsten Keil ASSIGN_FUNC(V2, ISAC, fc->isac); 9876115d2f3SKarsten Keil fc->isac.type = IPAC_TYPE_ISACX; 9886115d2f3SKarsten Keil break; 9896115d2f3SKarsten Keil default: 9906115d2f3SKarsten Keil release_region(fc->addr, 32); 9916115d2f3SKarsten Keil pr_info("%s: AVM unknown type %d\n", fc->name, fc->type); 9926115d2f3SKarsten Keil return -ENODEV; 9936115d2f3SKarsten Keil } 9946115d2f3SKarsten Keil pr_notice("%s: %s config irq:%d base:0x%X\n", fc->name, 9956115d2f3SKarsten Keil (fc->type == AVM_FRITZ_PCI) ? "AVM Fritz!CARD PCI" : 9966115d2f3SKarsten Keil "AVM Fritz!CARD PCIv2", fc->irq, fc->addr); 9976115d2f3SKarsten Keil return 0; 9986115d2f3SKarsten Keil } 9996115d2f3SKarsten Keil 10006115d2f3SKarsten Keil static void 10016115d2f3SKarsten Keil release_card(struct fritzcard *card) 10026115d2f3SKarsten Keil { 10036115d2f3SKarsten Keil u_long flags; 10046115d2f3SKarsten Keil 10056115d2f3SKarsten Keil disable_hwirq(card); 10066115d2f3SKarsten Keil spin_lock_irqsave(&card->lock, flags); 10076115d2f3SKarsten Keil modehdlc(&card->bch[0], ISDN_P_NONE); 10086115d2f3SKarsten Keil modehdlc(&card->bch[1], ISDN_P_NONE); 10096115d2f3SKarsten Keil spin_unlock_irqrestore(&card->lock, flags); 10106115d2f3SKarsten Keil card->isac.release(&card->isac); 10116115d2f3SKarsten Keil free_irq(card->irq, card); 10126115d2f3SKarsten Keil mISDN_freebchannel(&card->bch[1]); 10136115d2f3SKarsten Keil mISDN_freebchannel(&card->bch[0]); 10146115d2f3SKarsten Keil mISDN_unregister_device(&card->isac.dch.dev); 10156115d2f3SKarsten Keil release_region(card->addr, 32); 10166115d2f3SKarsten Keil pci_disable_device(card->pdev); 10176115d2f3SKarsten Keil pci_set_drvdata(card->pdev, NULL); 10186115d2f3SKarsten Keil write_lock_irqsave(&card_lock, flags); 10196115d2f3SKarsten Keil list_del(&card->list); 10206115d2f3SKarsten Keil write_unlock_irqrestore(&card_lock, flags); 10216115d2f3SKarsten Keil kfree(card); 10226115d2f3SKarsten Keil AVM_cnt--; 10236115d2f3SKarsten Keil } 10246115d2f3SKarsten Keil 1025ed5a84cdSGreg Kroah-Hartman static int 10266115d2f3SKarsten Keil setup_instance(struct fritzcard *card) 10276115d2f3SKarsten Keil { 10286115d2f3SKarsten Keil int i, err; 1029034005a0SKarsten Keil unsigned short minsize; 10306115d2f3SKarsten Keil u_long flags; 10316115d2f3SKarsten Keil 10326115d2f3SKarsten Keil snprintf(card->name, MISDN_MAX_IDLEN - 1, "AVM.%d", AVM_cnt + 1); 10336115d2f3SKarsten Keil write_lock_irqsave(&card_lock, flags); 10346115d2f3SKarsten Keil list_add_tail(&card->list, &Cards); 10356115d2f3SKarsten Keil write_unlock_irqrestore(&card_lock, flags); 10366115d2f3SKarsten Keil 10376115d2f3SKarsten Keil _set_debug(card); 10386115d2f3SKarsten Keil card->isac.name = card->name; 10396115d2f3SKarsten Keil spin_lock_init(&card->lock); 10406115d2f3SKarsten Keil card->isac.hwlock = &card->lock; 10416115d2f3SKarsten Keil mISDNisac_init(&card->isac, card); 10426115d2f3SKarsten Keil 10436115d2f3SKarsten Keil card->isac.dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | 10446115d2f3SKarsten Keil (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); 10456115d2f3SKarsten Keil card->isac.dch.dev.D.ctrl = avm_dctrl; 10466115d2f3SKarsten Keil for (i = 0; i < 2; i++) { 10476115d2f3SKarsten Keil card->bch[i].nr = i + 1; 10486115d2f3SKarsten Keil set_channelmap(i + 1, card->isac.dch.dev.channelmap); 1049034005a0SKarsten Keil if (AVM_FRITZ_PCIV2 == card->type) 1050034005a0SKarsten Keil minsize = HDLC_FIFO_SIZE_V2; 1051034005a0SKarsten Keil else 1052034005a0SKarsten Keil minsize = HDLC_FIFO_SIZE_V1; 1053034005a0SKarsten Keil mISDN_initbchannel(&card->bch[i], MAX_DATA_MEM, minsize); 10546115d2f3SKarsten Keil card->bch[i].hw = card; 10556115d2f3SKarsten Keil card->bch[i].ch.send = avm_l2l1B; 10566115d2f3SKarsten Keil card->bch[i].ch.ctrl = avm_bctrl; 10576115d2f3SKarsten Keil card->bch[i].ch.nr = i + 1; 10586115d2f3SKarsten Keil list_add(&card->bch[i].ch.list, &card->isac.dch.dev.bchannels); 10596115d2f3SKarsten Keil } 10606115d2f3SKarsten Keil err = setup_fritz(card); 10616115d2f3SKarsten Keil if (err) 10626115d2f3SKarsten Keil goto error; 10636115d2f3SKarsten Keil err = mISDN_register_device(&card->isac.dch.dev, &card->pdev->dev, 10646115d2f3SKarsten Keil card->name); 10656115d2f3SKarsten Keil if (err) 10666115d2f3SKarsten Keil goto error_reg; 10676115d2f3SKarsten Keil err = init_card(card); 10686115d2f3SKarsten Keil if (!err) { 10696115d2f3SKarsten Keil AVM_cnt++; 10706115d2f3SKarsten Keil pr_notice("AVM %d cards installed DEBUG\n", AVM_cnt); 10716115d2f3SKarsten Keil return 0; 10726115d2f3SKarsten Keil } 10736115d2f3SKarsten Keil mISDN_unregister_device(&card->isac.dch.dev); 10746115d2f3SKarsten Keil error_reg: 10756115d2f3SKarsten Keil release_region(card->addr, 32); 10766115d2f3SKarsten Keil error: 10776115d2f3SKarsten Keil card->isac.release(&card->isac); 10786115d2f3SKarsten Keil mISDN_freebchannel(&card->bch[1]); 10796115d2f3SKarsten Keil mISDN_freebchannel(&card->bch[0]); 10806115d2f3SKarsten Keil write_lock_irqsave(&card_lock, flags); 10816115d2f3SKarsten Keil list_del(&card->list); 10826115d2f3SKarsten Keil write_unlock_irqrestore(&card_lock, flags); 10836115d2f3SKarsten Keil kfree(card); 10846115d2f3SKarsten Keil return err; 10856115d2f3SKarsten Keil } 10866115d2f3SKarsten Keil 1087ed5a84cdSGreg Kroah-Hartman static int 10886115d2f3SKarsten Keil fritzpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 10896115d2f3SKarsten Keil { 10906115d2f3SKarsten Keil int err = -ENOMEM; 10916115d2f3SKarsten Keil struct fritzcard *card; 10926115d2f3SKarsten Keil 10936115d2f3SKarsten Keil card = kzalloc(sizeof(struct fritzcard), GFP_KERNEL); 10946115d2f3SKarsten Keil if (!card) { 10956115d2f3SKarsten Keil pr_info("No kmem for fritzcard\n"); 10966115d2f3SKarsten Keil return err; 10976115d2f3SKarsten Keil } 10986115d2f3SKarsten Keil if (pdev->device == PCI_DEVICE_ID_AVM_A1_V2) 10996115d2f3SKarsten Keil card->type = AVM_FRITZ_PCIV2; 11006115d2f3SKarsten Keil else 11016115d2f3SKarsten Keil card->type = AVM_FRITZ_PCI; 11026115d2f3SKarsten Keil card->pdev = pdev; 11036115d2f3SKarsten Keil err = pci_enable_device(pdev); 11046115d2f3SKarsten Keil if (err) { 11056115d2f3SKarsten Keil kfree(card); 11066115d2f3SKarsten Keil return err; 11076115d2f3SKarsten Keil } 11086115d2f3SKarsten Keil 11096115d2f3SKarsten Keil pr_notice("mISDN: found adapter %s at %s\n", 11106115d2f3SKarsten Keil (char *) ent->driver_data, pci_name(pdev)); 11116115d2f3SKarsten Keil 11126115d2f3SKarsten Keil card->addr = pci_resource_start(pdev, 1); 11136115d2f3SKarsten Keil card->irq = pdev->irq; 11146115d2f3SKarsten Keil pci_set_drvdata(pdev, card); 11156115d2f3SKarsten Keil err = setup_instance(card); 11166115d2f3SKarsten Keil if (err) 11176115d2f3SKarsten Keil pci_set_drvdata(pdev, NULL); 11186115d2f3SKarsten Keil return err; 11196115d2f3SKarsten Keil } 11206115d2f3SKarsten Keil 1121ed5a84cdSGreg Kroah-Hartman static void 11226115d2f3SKarsten Keil fritz_remove_pci(struct pci_dev *pdev) 11236115d2f3SKarsten Keil { 11246115d2f3SKarsten Keil struct fritzcard *card = pci_get_drvdata(pdev); 11256115d2f3SKarsten Keil 11266115d2f3SKarsten Keil if (card) 11276115d2f3SKarsten Keil release_card(card); 11286115d2f3SKarsten Keil else 11296115d2f3SKarsten Keil if (debug) 1130698f9315SUwe Kleine-König pr_info("%s: drvdata already removed\n", __func__); 11316115d2f3SKarsten Keil } 11326115d2f3SKarsten Keil 11331d9c8fa0SArvind Yadav static const struct pci_device_id fcpci_ids[] = { 11346115d2f3SKarsten Keil { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1, PCI_ANY_ID, PCI_ANY_ID, 11356115d2f3SKarsten Keil 0, 0, (unsigned long) "Fritz!Card PCI"}, 11366115d2f3SKarsten Keil { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1_V2, PCI_ANY_ID, PCI_ANY_ID, 11376115d2f3SKarsten Keil 0, 0, (unsigned long) "Fritz!Card PCI v2" }, 11386115d2f3SKarsten Keil { } 11396115d2f3SKarsten Keil }; 11406115d2f3SKarsten Keil MODULE_DEVICE_TABLE(pci, fcpci_ids); 11416115d2f3SKarsten Keil 11426115d2f3SKarsten Keil static struct pci_driver fcpci_driver = { 11436115d2f3SKarsten Keil .name = "fcpci", 11446115d2f3SKarsten Keil .probe = fritzpci_probe, 1145ed5a84cdSGreg Kroah-Hartman .remove = fritz_remove_pci, 11466115d2f3SKarsten Keil .id_table = fcpci_ids, 11476115d2f3SKarsten Keil }; 11486115d2f3SKarsten Keil 11496115d2f3SKarsten Keil static int __init AVM_init(void) 11506115d2f3SKarsten Keil { 11516115d2f3SKarsten Keil int err; 11526115d2f3SKarsten Keil 11536115d2f3SKarsten Keil pr_notice("AVM Fritz PCI driver Rev. %s\n", AVMFRITZ_REV); 11546115d2f3SKarsten Keil err = pci_register_driver(&fcpci_driver); 11556115d2f3SKarsten Keil return err; 11566115d2f3SKarsten Keil } 11576115d2f3SKarsten Keil 11586115d2f3SKarsten Keil static void __exit AVM_cleanup(void) 11596115d2f3SKarsten Keil { 11606115d2f3SKarsten Keil pci_unregister_driver(&fcpci_driver); 11616115d2f3SKarsten Keil } 11626115d2f3SKarsten Keil 11636115d2f3SKarsten Keil module_init(AVM_init); 11646115d2f3SKarsten Keil module_exit(AVM_cleanup); 1165