16115d2f3SKarsten Keil /* 26115d2f3SKarsten Keil * avm_fritz.c low level stuff for AVM FRITZ!CARD PCI ISDN cards 36115d2f3SKarsten Keil * Thanks to AVM, Berlin for informations 46115d2f3SKarsten Keil * 56115d2f3SKarsten Keil * Author Karsten Keil <keil@isdn4linux.de> 66115d2f3SKarsten Keil * 76115d2f3SKarsten Keil * Copyright 2009 by Karsten Keil <keil@isdn4linux.de> 86115d2f3SKarsten Keil * 96115d2f3SKarsten Keil * This program is free software; you can redistribute it and/or modify 106115d2f3SKarsten Keil * it under the terms of the GNU General Public License version 2 as 116115d2f3SKarsten Keil * published by the Free Software Foundation. 126115d2f3SKarsten Keil * 136115d2f3SKarsten Keil * This program is distributed in the hope that it will be useful, 146115d2f3SKarsten Keil * but WITHOUT ANY WARRANTY; without even the implied warranty of 156115d2f3SKarsten Keil * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 166115d2f3SKarsten Keil * GNU General Public License for more details. 176115d2f3SKarsten Keil * 186115d2f3SKarsten Keil * You should have received a copy of the GNU General Public License 196115d2f3SKarsten Keil * along with this program; if not, write to the Free Software 206115d2f3SKarsten Keil * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 216115d2f3SKarsten Keil * 226115d2f3SKarsten Keil */ 23a6b7a407SAlexey Dobriyan #include <linux/interrupt.h> 246115d2f3SKarsten Keil #include <linux/module.h> 256115d2f3SKarsten Keil #include <linux/pci.h> 266115d2f3SKarsten Keil #include <linux/delay.h> 276115d2f3SKarsten Keil #include <linux/mISDNhw.h> 285a0e3ad6STejun Heo #include <linux/slab.h> 296115d2f3SKarsten Keil #include <asm/unaligned.h> 306115d2f3SKarsten Keil #include "ipac.h" 316115d2f3SKarsten Keil 326115d2f3SKarsten Keil 3309e79a77SKarsten Keil #define AVMFRITZ_REV "2.2" 346115d2f3SKarsten Keil 356115d2f3SKarsten Keil static int AVM_cnt; 366115d2f3SKarsten Keil static int debug; 376115d2f3SKarsten Keil 386115d2f3SKarsten Keil enum { 396115d2f3SKarsten Keil AVM_FRITZ_PCI, 406115d2f3SKarsten Keil AVM_FRITZ_PCIV2, 416115d2f3SKarsten Keil }; 426115d2f3SKarsten Keil 436115d2f3SKarsten Keil #define HDLC_FIFO 0x0 446115d2f3SKarsten Keil #define HDLC_STATUS 0x4 456115d2f3SKarsten Keil #define CHIP_WINDOW 0x10 466115d2f3SKarsten Keil 476115d2f3SKarsten Keil #define CHIP_INDEX 0x4 486115d2f3SKarsten Keil #define AVM_HDLC_1 0x00 496115d2f3SKarsten Keil #define AVM_HDLC_2 0x01 506115d2f3SKarsten Keil #define AVM_ISAC_FIFO 0x02 516115d2f3SKarsten Keil #define AVM_ISAC_REG_LOW 0x04 526115d2f3SKarsten Keil #define AVM_ISAC_REG_HIGH 0x06 536115d2f3SKarsten Keil 546115d2f3SKarsten Keil #define AVM_STATUS0_IRQ_ISAC 0x01 556115d2f3SKarsten Keil #define AVM_STATUS0_IRQ_HDLC 0x02 566115d2f3SKarsten Keil #define AVM_STATUS0_IRQ_TIMER 0x04 576115d2f3SKarsten Keil #define AVM_STATUS0_IRQ_MASK 0x07 586115d2f3SKarsten Keil 596115d2f3SKarsten Keil #define AVM_STATUS0_RESET 0x01 606115d2f3SKarsten Keil #define AVM_STATUS0_DIS_TIMER 0x02 616115d2f3SKarsten Keil #define AVM_STATUS0_RES_TIMER 0x04 626115d2f3SKarsten Keil #define AVM_STATUS0_ENA_IRQ 0x08 636115d2f3SKarsten Keil #define AVM_STATUS0_TESTBIT 0x10 646115d2f3SKarsten Keil 656115d2f3SKarsten Keil #define AVM_STATUS1_INT_SEL 0x0f 666115d2f3SKarsten Keil #define AVM_STATUS1_ENA_IOM 0x80 676115d2f3SKarsten Keil 686115d2f3SKarsten Keil #define HDLC_MODE_ITF_FLG 0x01 696115d2f3SKarsten Keil #define HDLC_MODE_TRANS 0x02 706115d2f3SKarsten Keil #define HDLC_MODE_CCR_7 0x04 716115d2f3SKarsten Keil #define HDLC_MODE_CCR_16 0x08 7209e79a77SKarsten Keil #define HDLC_FIFO_SIZE_128 0x20 736115d2f3SKarsten Keil #define HDLC_MODE_TESTLOOP 0x80 746115d2f3SKarsten Keil 756115d2f3SKarsten Keil #define HDLC_INT_XPR 0x80 766115d2f3SKarsten Keil #define HDLC_INT_XDU 0x40 776115d2f3SKarsten Keil #define HDLC_INT_RPR 0x20 786115d2f3SKarsten Keil #define HDLC_INT_MASK 0xE0 796115d2f3SKarsten Keil 806115d2f3SKarsten Keil #define HDLC_STAT_RME 0x01 816115d2f3SKarsten Keil #define HDLC_STAT_RDO 0x10 826115d2f3SKarsten Keil #define HDLC_STAT_CRCVFRRAB 0x0E 836115d2f3SKarsten Keil #define HDLC_STAT_CRCVFR 0x06 8409e79a77SKarsten Keil #define HDLC_STAT_RML_MASK_V1 0x3f00 8509e79a77SKarsten Keil #define HDLC_STAT_RML_MASK_V2 0x7f00 866115d2f3SKarsten Keil 876115d2f3SKarsten Keil #define HDLC_CMD_XRS 0x80 886115d2f3SKarsten Keil #define HDLC_CMD_XME 0x01 896115d2f3SKarsten Keil #define HDLC_CMD_RRS 0x20 906115d2f3SKarsten Keil #define HDLC_CMD_XML_MASK 0x3f00 9109e79a77SKarsten Keil 9209e79a77SKarsten Keil #define HDLC_FIFO_SIZE_V1 32 9309e79a77SKarsten Keil #define HDLC_FIFO_SIZE_V2 128 946115d2f3SKarsten Keil 956115d2f3SKarsten Keil /* Fritz PCI v2.0 */ 966115d2f3SKarsten Keil 976115d2f3SKarsten Keil #define AVM_HDLC_FIFO_1 0x10 986115d2f3SKarsten Keil #define AVM_HDLC_FIFO_2 0x18 996115d2f3SKarsten Keil 1006115d2f3SKarsten Keil #define AVM_HDLC_STATUS_1 0x14 1016115d2f3SKarsten Keil #define AVM_HDLC_STATUS_2 0x1c 1026115d2f3SKarsten Keil 1036115d2f3SKarsten Keil #define AVM_ISACX_INDEX 0x04 1046115d2f3SKarsten Keil #define AVM_ISACX_DATA 0x08 1056115d2f3SKarsten Keil 1066115d2f3SKarsten Keil /* data struct */ 1076115d2f3SKarsten Keil #define LOG_SIZE 63 1086115d2f3SKarsten Keil 1096115d2f3SKarsten Keil struct hdlc_stat_reg { 1106115d2f3SKarsten Keil #ifdef __BIG_ENDIAN 1116115d2f3SKarsten Keil u8 fill; 1126115d2f3SKarsten Keil u8 mode; 1136115d2f3SKarsten Keil u8 xml; 1146115d2f3SKarsten Keil u8 cmd; 1156115d2f3SKarsten Keil #else 1166115d2f3SKarsten Keil u8 cmd; 1176115d2f3SKarsten Keil u8 xml; 1186115d2f3SKarsten Keil u8 mode; 1196115d2f3SKarsten Keil u8 fill; 1206115d2f3SKarsten Keil #endif 1216115d2f3SKarsten Keil } __attribute__((packed)); 1226115d2f3SKarsten Keil 1236115d2f3SKarsten Keil struct hdlc_hw { 1246115d2f3SKarsten Keil union { 1256115d2f3SKarsten Keil u32 ctrl; 1266115d2f3SKarsten Keil struct hdlc_stat_reg sr; 1276115d2f3SKarsten Keil } ctrl; 1286115d2f3SKarsten Keil u32 stat; 1296115d2f3SKarsten Keil }; 1306115d2f3SKarsten Keil 1316115d2f3SKarsten Keil struct fritzcard { 1326115d2f3SKarsten Keil struct list_head list; 1336115d2f3SKarsten Keil struct pci_dev *pdev; 1346115d2f3SKarsten Keil char name[MISDN_MAX_IDLEN]; 1356115d2f3SKarsten Keil u8 type; 1366115d2f3SKarsten Keil u8 ctrlreg; 1376115d2f3SKarsten Keil u16 irq; 1386115d2f3SKarsten Keil u32 irqcnt; 1396115d2f3SKarsten Keil u32 addr; 1406115d2f3SKarsten Keil spinlock_t lock; /* hw lock */ 1416115d2f3SKarsten Keil struct isac_hw isac; 1426115d2f3SKarsten Keil struct hdlc_hw hdlc[2]; 1436115d2f3SKarsten Keil struct bchannel bch[2]; 1446115d2f3SKarsten Keil char log[LOG_SIZE + 1]; 1456115d2f3SKarsten Keil }; 1466115d2f3SKarsten Keil 1476115d2f3SKarsten Keil static LIST_HEAD(Cards); 1486115d2f3SKarsten Keil static DEFINE_RWLOCK(card_lock); /* protect Cards */ 1496115d2f3SKarsten Keil 1506115d2f3SKarsten Keil static void 1516115d2f3SKarsten Keil _set_debug(struct fritzcard *card) 1526115d2f3SKarsten Keil { 1536115d2f3SKarsten Keil card->isac.dch.debug = debug; 1546115d2f3SKarsten Keil card->bch[0].debug = debug; 1556115d2f3SKarsten Keil card->bch[1].debug = debug; 1566115d2f3SKarsten Keil } 1576115d2f3SKarsten Keil 1586115d2f3SKarsten Keil static int 1596115d2f3SKarsten Keil set_debug(const char *val, struct kernel_param *kp) 1606115d2f3SKarsten Keil { 1616115d2f3SKarsten Keil int ret; 1626115d2f3SKarsten Keil struct fritzcard *card; 1636115d2f3SKarsten Keil 1646115d2f3SKarsten Keil ret = param_set_uint(val, kp); 1656115d2f3SKarsten Keil if (!ret) { 1666115d2f3SKarsten Keil read_lock(&card_lock); 1676115d2f3SKarsten Keil list_for_each_entry(card, &Cards, list) 1686115d2f3SKarsten Keil _set_debug(card); 1696115d2f3SKarsten Keil read_unlock(&card_lock); 1706115d2f3SKarsten Keil } 1716115d2f3SKarsten Keil return ret; 1726115d2f3SKarsten Keil } 1736115d2f3SKarsten Keil 1746115d2f3SKarsten Keil MODULE_AUTHOR("Karsten Keil"); 1756115d2f3SKarsten Keil MODULE_LICENSE("GPL v2"); 1766115d2f3SKarsten Keil MODULE_VERSION(AVMFRITZ_REV); 1776115d2f3SKarsten Keil module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR); 1786115d2f3SKarsten Keil MODULE_PARM_DESC(debug, "avmfritz debug mask"); 1796115d2f3SKarsten Keil 1806115d2f3SKarsten Keil /* Interface functions */ 1816115d2f3SKarsten Keil 1826115d2f3SKarsten Keil static u8 1836115d2f3SKarsten Keil ReadISAC_V1(void *p, u8 offset) 1846115d2f3SKarsten Keil { 1856115d2f3SKarsten Keil struct fritzcard *fc = p; 1866115d2f3SKarsten Keil u8 idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW; 1876115d2f3SKarsten Keil 1886115d2f3SKarsten Keil outb(idx, fc->addr + CHIP_INDEX); 1896115d2f3SKarsten Keil return inb(fc->addr + CHIP_WINDOW + (offset & 0xf)); 1906115d2f3SKarsten Keil } 1916115d2f3SKarsten Keil 1926115d2f3SKarsten Keil static void 1936115d2f3SKarsten Keil WriteISAC_V1(void *p, u8 offset, u8 value) 1946115d2f3SKarsten Keil { 1956115d2f3SKarsten Keil struct fritzcard *fc = p; 1966115d2f3SKarsten Keil u8 idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW; 1976115d2f3SKarsten Keil 1986115d2f3SKarsten Keil outb(idx, fc->addr + CHIP_INDEX); 1996115d2f3SKarsten Keil outb(value, fc->addr + CHIP_WINDOW + (offset & 0xf)); 2006115d2f3SKarsten Keil } 2016115d2f3SKarsten Keil 2026115d2f3SKarsten Keil static void 2036115d2f3SKarsten Keil ReadFiFoISAC_V1(void *p, u8 off, u8 *data, int size) 2046115d2f3SKarsten Keil { 2056115d2f3SKarsten Keil struct fritzcard *fc = p; 2066115d2f3SKarsten Keil 2076115d2f3SKarsten Keil outb(AVM_ISAC_FIFO, fc->addr + CHIP_INDEX); 2086115d2f3SKarsten Keil insb(fc->addr + CHIP_WINDOW, data, size); 2096115d2f3SKarsten Keil } 2106115d2f3SKarsten Keil 2116115d2f3SKarsten Keil static void 2126115d2f3SKarsten Keil WriteFiFoISAC_V1(void *p, u8 off, u8 *data, int size) 2136115d2f3SKarsten Keil { 2146115d2f3SKarsten Keil struct fritzcard *fc = p; 2156115d2f3SKarsten Keil 2166115d2f3SKarsten Keil outb(AVM_ISAC_FIFO, fc->addr + CHIP_INDEX); 2176115d2f3SKarsten Keil outsb(fc->addr + CHIP_WINDOW, data, size); 2186115d2f3SKarsten Keil } 2196115d2f3SKarsten Keil 2206115d2f3SKarsten Keil static u8 2216115d2f3SKarsten Keil ReadISAC_V2(void *p, u8 offset) 2226115d2f3SKarsten Keil { 2236115d2f3SKarsten Keil struct fritzcard *fc = p; 2246115d2f3SKarsten Keil 2256115d2f3SKarsten Keil outl(offset, fc->addr + AVM_ISACX_INDEX); 2266115d2f3SKarsten Keil return 0xff & inl(fc->addr + AVM_ISACX_DATA); 2276115d2f3SKarsten Keil } 2286115d2f3SKarsten Keil 2296115d2f3SKarsten Keil static void 2306115d2f3SKarsten Keil WriteISAC_V2(void *p, u8 offset, u8 value) 2316115d2f3SKarsten Keil { 2326115d2f3SKarsten Keil struct fritzcard *fc = p; 2336115d2f3SKarsten Keil 2346115d2f3SKarsten Keil outl(offset, fc->addr + AVM_ISACX_INDEX); 2356115d2f3SKarsten Keil outl(value, fc->addr + AVM_ISACX_DATA); 2366115d2f3SKarsten Keil } 2376115d2f3SKarsten Keil 2386115d2f3SKarsten Keil static void 2396115d2f3SKarsten Keil ReadFiFoISAC_V2(void *p, u8 off, u8 *data, int size) 2406115d2f3SKarsten Keil { 2416115d2f3SKarsten Keil struct fritzcard *fc = p; 2426115d2f3SKarsten Keil int i; 2436115d2f3SKarsten Keil 2446115d2f3SKarsten Keil outl(off, fc->addr + AVM_ISACX_INDEX); 2456115d2f3SKarsten Keil for (i = 0; i < size; i++) 2466115d2f3SKarsten Keil data[i] = 0xff & inl(fc->addr + AVM_ISACX_DATA); 2476115d2f3SKarsten Keil } 2486115d2f3SKarsten Keil 2496115d2f3SKarsten Keil static void 2506115d2f3SKarsten Keil WriteFiFoISAC_V2(void *p, u8 off, u8 *data, int size) 2516115d2f3SKarsten Keil { 2526115d2f3SKarsten Keil struct fritzcard *fc = p; 2536115d2f3SKarsten Keil int i; 2546115d2f3SKarsten Keil 2556115d2f3SKarsten Keil outl(off, fc->addr + AVM_ISACX_INDEX); 2566115d2f3SKarsten Keil for (i = 0; i < size; i++) 2576115d2f3SKarsten Keil outl(data[i], fc->addr + AVM_ISACX_DATA); 2586115d2f3SKarsten Keil } 2596115d2f3SKarsten Keil 2606115d2f3SKarsten Keil static struct bchannel * 2616115d2f3SKarsten Keil Sel_BCS(struct fritzcard *fc, u32 channel) 2626115d2f3SKarsten Keil { 2636115d2f3SKarsten Keil if (test_bit(FLG_ACTIVE, &fc->bch[0].Flags) && 2646115d2f3SKarsten Keil (fc->bch[0].nr & channel)) 2656115d2f3SKarsten Keil return &fc->bch[0]; 2666115d2f3SKarsten Keil else if (test_bit(FLG_ACTIVE, &fc->bch[1].Flags) && 2676115d2f3SKarsten Keil (fc->bch[1].nr & channel)) 2686115d2f3SKarsten Keil return &fc->bch[1]; 2696115d2f3SKarsten Keil else 2706115d2f3SKarsten Keil return NULL; 2716115d2f3SKarsten Keil } 2726115d2f3SKarsten Keil 2736115d2f3SKarsten Keil static inline void 2746115d2f3SKarsten Keil __write_ctrl_pci(struct fritzcard *fc, struct hdlc_hw *hdlc, u32 channel) { 2756115d2f3SKarsten Keil u32 idx = channel == 2 ? AVM_HDLC_2 : AVM_HDLC_1; 2766115d2f3SKarsten Keil 2776115d2f3SKarsten Keil outl(idx, fc->addr + CHIP_INDEX); 2786115d2f3SKarsten Keil outl(hdlc->ctrl.ctrl, fc->addr + CHIP_WINDOW + HDLC_STATUS); 2796115d2f3SKarsten Keil } 2806115d2f3SKarsten Keil 2816115d2f3SKarsten Keil static inline void 2826115d2f3SKarsten Keil __write_ctrl_pciv2(struct fritzcard *fc, struct hdlc_hw *hdlc, u32 channel) { 2836115d2f3SKarsten Keil outl(hdlc->ctrl.ctrl, fc->addr + (channel == 2 ? AVM_HDLC_STATUS_2 : 2846115d2f3SKarsten Keil AVM_HDLC_STATUS_1)); 2856115d2f3SKarsten Keil } 2866115d2f3SKarsten Keil 2876115d2f3SKarsten Keil void 2886115d2f3SKarsten Keil write_ctrl(struct bchannel *bch, int which) { 2896115d2f3SKarsten Keil struct fritzcard *fc = bch->hw; 2906115d2f3SKarsten Keil struct hdlc_hw *hdlc; 2916115d2f3SKarsten Keil 2926115d2f3SKarsten Keil hdlc = &fc->hdlc[(bch->nr - 1) & 1]; 2936115d2f3SKarsten Keil pr_debug("%s: hdlc %c wr%x ctrl %x\n", fc->name, '@' + bch->nr, 2946115d2f3SKarsten Keil which, hdlc->ctrl.ctrl); 2956115d2f3SKarsten Keil switch (fc->type) { 2966115d2f3SKarsten Keil case AVM_FRITZ_PCIV2: 2976115d2f3SKarsten Keil __write_ctrl_pciv2(fc, hdlc, bch->nr); 2986115d2f3SKarsten Keil break; 2996115d2f3SKarsten Keil case AVM_FRITZ_PCI: 3006115d2f3SKarsten Keil __write_ctrl_pci(fc, hdlc, bch->nr); 3016115d2f3SKarsten Keil break; 3026115d2f3SKarsten Keil } 3036115d2f3SKarsten Keil } 3046115d2f3SKarsten Keil 3056115d2f3SKarsten Keil 3066115d2f3SKarsten Keil static inline u32 3076115d2f3SKarsten Keil __read_status_pci(u_long addr, u32 channel) 3086115d2f3SKarsten Keil { 3096115d2f3SKarsten Keil outl(channel == 2 ? AVM_HDLC_2 : AVM_HDLC_1, addr + CHIP_INDEX); 3106115d2f3SKarsten Keil return inl(addr + CHIP_WINDOW + HDLC_STATUS); 3116115d2f3SKarsten Keil } 3126115d2f3SKarsten Keil 3136115d2f3SKarsten Keil static inline u32 3146115d2f3SKarsten Keil __read_status_pciv2(u_long addr, u32 channel) 3156115d2f3SKarsten Keil { 3166115d2f3SKarsten Keil return inl(addr + (channel == 2 ? AVM_HDLC_STATUS_2 : 3176115d2f3SKarsten Keil AVM_HDLC_STATUS_1)); 3186115d2f3SKarsten Keil } 3196115d2f3SKarsten Keil 3206115d2f3SKarsten Keil 3216115d2f3SKarsten Keil static u32 3226115d2f3SKarsten Keil read_status(struct fritzcard *fc, u32 channel) 3236115d2f3SKarsten Keil { 3246115d2f3SKarsten Keil switch (fc->type) { 3256115d2f3SKarsten Keil case AVM_FRITZ_PCIV2: 3266115d2f3SKarsten Keil return __read_status_pciv2(fc->addr, channel); 3276115d2f3SKarsten Keil case AVM_FRITZ_PCI: 3286115d2f3SKarsten Keil return __read_status_pci(fc->addr, channel); 3296115d2f3SKarsten Keil } 3306115d2f3SKarsten Keil /* dummy */ 3316115d2f3SKarsten Keil return 0; 3326115d2f3SKarsten Keil } 3336115d2f3SKarsten Keil 3346115d2f3SKarsten Keil static void 3356115d2f3SKarsten Keil enable_hwirq(struct fritzcard *fc) 3366115d2f3SKarsten Keil { 3376115d2f3SKarsten Keil fc->ctrlreg |= AVM_STATUS0_ENA_IRQ; 3386115d2f3SKarsten Keil outb(fc->ctrlreg, fc->addr + 2); 3396115d2f3SKarsten Keil } 3406115d2f3SKarsten Keil 3416115d2f3SKarsten Keil static void 3426115d2f3SKarsten Keil disable_hwirq(struct fritzcard *fc) 3436115d2f3SKarsten Keil { 3446115d2f3SKarsten Keil fc->ctrlreg &= ~AVM_STATUS0_ENA_IRQ; 3456115d2f3SKarsten Keil outb(fc->ctrlreg, fc->addr + 2); 3466115d2f3SKarsten Keil } 3476115d2f3SKarsten Keil 3486115d2f3SKarsten Keil static int 3496115d2f3SKarsten Keil modehdlc(struct bchannel *bch, int protocol) 3506115d2f3SKarsten Keil { 3516115d2f3SKarsten Keil struct fritzcard *fc = bch->hw; 3526115d2f3SKarsten Keil struct hdlc_hw *hdlc; 35309e79a77SKarsten Keil u8 mode; 3546115d2f3SKarsten Keil 3556115d2f3SKarsten Keil hdlc = &fc->hdlc[(bch->nr - 1) & 1]; 3566115d2f3SKarsten Keil pr_debug("%s: hdlc %c protocol %x-->%x ch %d\n", fc->name, 3576115d2f3SKarsten Keil '@' + bch->nr, bch->state, protocol, bch->nr); 3586115d2f3SKarsten Keil hdlc->ctrl.ctrl = 0; 35909e79a77SKarsten Keil mode = (fc->type == AVM_FRITZ_PCIV2) ? HDLC_FIFO_SIZE_128 : 0; 36009e79a77SKarsten Keil 3616115d2f3SKarsten Keil switch (protocol) { 3626115d2f3SKarsten Keil case -1: /* used for init */ 3636115d2f3SKarsten Keil bch->state = -1; 3646115d2f3SKarsten Keil case ISDN_P_NONE: 3656115d2f3SKarsten Keil if (bch->state == ISDN_P_NONE) 3666115d2f3SKarsten Keil break; 3676115d2f3SKarsten Keil hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; 36809e79a77SKarsten Keil hdlc->ctrl.sr.mode = mode | HDLC_MODE_TRANS; 3696115d2f3SKarsten Keil write_ctrl(bch, 5); 3706115d2f3SKarsten Keil bch->state = ISDN_P_NONE; 3716115d2f3SKarsten Keil test_and_clear_bit(FLG_HDLC, &bch->Flags); 3726115d2f3SKarsten Keil test_and_clear_bit(FLG_TRANSPARENT, &bch->Flags); 3736115d2f3SKarsten Keil break; 3746115d2f3SKarsten Keil case ISDN_P_B_RAW: 3756115d2f3SKarsten Keil bch->state = protocol; 3766115d2f3SKarsten Keil hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; 37709e79a77SKarsten Keil hdlc->ctrl.sr.mode = mode | HDLC_MODE_TRANS; 3786115d2f3SKarsten Keil write_ctrl(bch, 5); 3796115d2f3SKarsten Keil hdlc->ctrl.sr.cmd = HDLC_CMD_XRS; 3806115d2f3SKarsten Keil write_ctrl(bch, 1); 3816115d2f3SKarsten Keil hdlc->ctrl.sr.cmd = 0; 3826115d2f3SKarsten Keil test_and_set_bit(FLG_TRANSPARENT, &bch->Flags); 3836115d2f3SKarsten Keil break; 3846115d2f3SKarsten Keil case ISDN_P_B_HDLC: 3856115d2f3SKarsten Keil bch->state = protocol; 3866115d2f3SKarsten Keil hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; 38709e79a77SKarsten Keil hdlc->ctrl.sr.mode = mode | HDLC_MODE_ITF_FLG; 3886115d2f3SKarsten Keil write_ctrl(bch, 5); 3896115d2f3SKarsten Keil hdlc->ctrl.sr.cmd = HDLC_CMD_XRS; 3906115d2f3SKarsten Keil write_ctrl(bch, 1); 3916115d2f3SKarsten Keil hdlc->ctrl.sr.cmd = 0; 3926115d2f3SKarsten Keil test_and_set_bit(FLG_HDLC, &bch->Flags); 3936115d2f3SKarsten Keil break; 3946115d2f3SKarsten Keil default: 3956115d2f3SKarsten Keil pr_info("%s: protocol not known %x\n", fc->name, protocol); 3966115d2f3SKarsten Keil return -ENOPROTOOPT; 3976115d2f3SKarsten Keil } 3986115d2f3SKarsten Keil return 0; 3996115d2f3SKarsten Keil } 4006115d2f3SKarsten Keil 4016115d2f3SKarsten Keil static void 4026115d2f3SKarsten Keil hdlc_empty_fifo(struct bchannel *bch, int count) 4036115d2f3SKarsten Keil { 4046115d2f3SKarsten Keil u32 *ptr; 4056115d2f3SKarsten Keil u8 *p; 4066115d2f3SKarsten Keil u32 val, addr; 407*7206e659SKarsten Keil int cnt; 4086115d2f3SKarsten Keil struct fritzcard *fc = bch->hw; 4096115d2f3SKarsten Keil 4106115d2f3SKarsten Keil pr_debug("%s: %s %d\n", fc->name, __func__, count); 411*7206e659SKarsten Keil cnt = bchannel_get_rxbuf(bch, count); 412*7206e659SKarsten Keil if (cnt < 0) { 413*7206e659SKarsten Keil pr_warning("%s.B%d: No bufferspace for %d bytes\n", 414*7206e659SKarsten Keil fc->name, bch->nr, count); 4156115d2f3SKarsten Keil return; 4166115d2f3SKarsten Keil } 4176115d2f3SKarsten Keil p = skb_put(bch->rx_skb, count); 4186115d2f3SKarsten Keil ptr = (u32 *)p; 41909e79a77SKarsten Keil if (fc->type == AVM_FRITZ_PCIV2) 4206115d2f3SKarsten Keil addr = fc->addr + (bch->nr == 2 ? 4216115d2f3SKarsten Keil AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1); 4226115d2f3SKarsten Keil else { 4236115d2f3SKarsten Keil addr = fc->addr + CHIP_WINDOW; 4246115d2f3SKarsten Keil outl(bch->nr == 2 ? AVM_HDLC_2 : AVM_HDLC_1, fc->addr); 4256115d2f3SKarsten Keil } 426*7206e659SKarsten Keil cnt = 0; 4276115d2f3SKarsten Keil while (cnt < count) { 4286115d2f3SKarsten Keil val = le32_to_cpu(inl(addr)); 4296115d2f3SKarsten Keil put_unaligned(val, ptr); 4306115d2f3SKarsten Keil ptr++; 4316115d2f3SKarsten Keil cnt += 4; 4326115d2f3SKarsten Keil } 4336115d2f3SKarsten Keil if (debug & DEBUG_HW_BFIFO) { 4346115d2f3SKarsten Keil snprintf(fc->log, LOG_SIZE, "B%1d-recv %s %d ", 4356115d2f3SKarsten Keil bch->nr, fc->name, count); 4366115d2f3SKarsten Keil print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count); 4376115d2f3SKarsten Keil } 4386115d2f3SKarsten Keil } 4396115d2f3SKarsten Keil 4406115d2f3SKarsten Keil static void 4416115d2f3SKarsten Keil hdlc_fill_fifo(struct bchannel *bch) 4426115d2f3SKarsten Keil { 4436115d2f3SKarsten Keil struct fritzcard *fc = bch->hw; 4446115d2f3SKarsten Keil struct hdlc_hw *hdlc; 44509e79a77SKarsten Keil int count, fs, cnt = 0; 4466115d2f3SKarsten Keil u8 *p; 4476115d2f3SKarsten Keil u32 *ptr, val, addr; 4486115d2f3SKarsten Keil 4496115d2f3SKarsten Keil hdlc = &fc->hdlc[(bch->nr - 1) & 1]; 4506115d2f3SKarsten Keil if (!bch->tx_skb) 4516115d2f3SKarsten Keil return; 4526115d2f3SKarsten Keil count = bch->tx_skb->len - bch->tx_idx; 4536115d2f3SKarsten Keil if (count <= 0) 4546115d2f3SKarsten Keil return; 45509e79a77SKarsten Keil fs = (fc->type == AVM_FRITZ_PCIV2) ? 45609e79a77SKarsten Keil HDLC_FIFO_SIZE_V2 : HDLC_FIFO_SIZE_V1; 4576115d2f3SKarsten Keil p = bch->tx_skb->data + bch->tx_idx; 4586115d2f3SKarsten Keil hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XME; 45909e79a77SKarsten Keil if (count > fs) { 46009e79a77SKarsten Keil count = fs; 4616115d2f3SKarsten Keil } else { 4626115d2f3SKarsten Keil if (test_bit(FLG_HDLC, &bch->Flags)) 4636115d2f3SKarsten Keil hdlc->ctrl.sr.cmd |= HDLC_CMD_XME; 4646115d2f3SKarsten Keil } 4656115d2f3SKarsten Keil pr_debug("%s: %s %d/%d/%d", fc->name, __func__, count, 4666115d2f3SKarsten Keil bch->tx_idx, bch->tx_skb->len); 4676115d2f3SKarsten Keil ptr = (u32 *)p; 4686115d2f3SKarsten Keil bch->tx_idx += count; 46909e79a77SKarsten Keil hdlc->ctrl.sr.xml = ((count == fs) ? 0 : count); 47009e79a77SKarsten Keil if (fc->type == AVM_FRITZ_PCIV2) { 4716115d2f3SKarsten Keil __write_ctrl_pciv2(fc, hdlc, bch->nr); 4726115d2f3SKarsten Keil addr = fc->addr + (bch->nr == 2 ? 4736115d2f3SKarsten Keil AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1); 4746115d2f3SKarsten Keil } else { 4756115d2f3SKarsten Keil __write_ctrl_pci(fc, hdlc, bch->nr); 4766115d2f3SKarsten Keil addr = fc->addr + CHIP_WINDOW; 4776115d2f3SKarsten Keil } 4786115d2f3SKarsten Keil while (cnt < count) { 4796115d2f3SKarsten Keil val = get_unaligned(ptr); 4806115d2f3SKarsten Keil outl(cpu_to_le32(val), addr); 4816115d2f3SKarsten Keil ptr++; 4826115d2f3SKarsten Keil cnt += 4; 4836115d2f3SKarsten Keil } 4846115d2f3SKarsten Keil if (debug & DEBUG_HW_BFIFO) { 4856115d2f3SKarsten Keil snprintf(fc->log, LOG_SIZE, "B%1d-send %s %d ", 4866115d2f3SKarsten Keil bch->nr, fc->name, count); 4876115d2f3SKarsten Keil print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count); 4886115d2f3SKarsten Keil } 4896115d2f3SKarsten Keil } 4906115d2f3SKarsten Keil 4916115d2f3SKarsten Keil static void 4926115d2f3SKarsten Keil HDLC_irq_xpr(struct bchannel *bch) 4936115d2f3SKarsten Keil { 4948bfddfbeSKarsten Keil if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len) { 4956115d2f3SKarsten Keil hdlc_fill_fifo(bch); 4968bfddfbeSKarsten Keil } else { 4978bfddfbeSKarsten Keil if (bch->tx_skb) 4986115d2f3SKarsten Keil dev_kfree_skb(bch->tx_skb); 4996115d2f3SKarsten Keil if (get_next_bframe(bch)) 5006115d2f3SKarsten Keil hdlc_fill_fifo(bch); 5016115d2f3SKarsten Keil } 5026115d2f3SKarsten Keil } 5036115d2f3SKarsten Keil 5046115d2f3SKarsten Keil static void 5056115d2f3SKarsten Keil HDLC_irq(struct bchannel *bch, u32 stat) 5066115d2f3SKarsten Keil { 5076115d2f3SKarsten Keil struct fritzcard *fc = bch->hw; 50809e79a77SKarsten Keil int len, fs; 50909e79a77SKarsten Keil u32 rmlMask; 5106115d2f3SKarsten Keil struct hdlc_hw *hdlc; 5116115d2f3SKarsten Keil 5126115d2f3SKarsten Keil hdlc = &fc->hdlc[(bch->nr - 1) & 1]; 5136115d2f3SKarsten Keil pr_debug("%s: ch%d stat %#x\n", fc->name, bch->nr, stat); 51409e79a77SKarsten Keil if (fc->type == AVM_FRITZ_PCIV2) { 51509e79a77SKarsten Keil rmlMask = HDLC_STAT_RML_MASK_V2; 51609e79a77SKarsten Keil fs = HDLC_FIFO_SIZE_V2; 51709e79a77SKarsten Keil } else { 51809e79a77SKarsten Keil rmlMask = HDLC_STAT_RML_MASK_V1; 51909e79a77SKarsten Keil fs = HDLC_FIFO_SIZE_V1; 52009e79a77SKarsten Keil } 5216115d2f3SKarsten Keil if (stat & HDLC_INT_RPR) { 5226115d2f3SKarsten Keil if (stat & HDLC_STAT_RDO) { 52309e79a77SKarsten Keil pr_warning("%s: ch%d stat %x RDO\n", 52409e79a77SKarsten Keil fc->name, bch->nr, stat); 5256115d2f3SKarsten Keil hdlc->ctrl.sr.xml = 0; 5266115d2f3SKarsten Keil hdlc->ctrl.sr.cmd |= HDLC_CMD_RRS; 5276115d2f3SKarsten Keil write_ctrl(bch, 1); 5286115d2f3SKarsten Keil hdlc->ctrl.sr.cmd &= ~HDLC_CMD_RRS; 5296115d2f3SKarsten Keil write_ctrl(bch, 1); 5306115d2f3SKarsten Keil if (bch->rx_skb) 5316115d2f3SKarsten Keil skb_trim(bch->rx_skb, 0); 5326115d2f3SKarsten Keil } else { 53309e79a77SKarsten Keil len = (stat & rmlMask) >> 8; 5346115d2f3SKarsten Keil if (!len) 53509e79a77SKarsten Keil len = fs; 5366115d2f3SKarsten Keil hdlc_empty_fifo(bch, len); 5376115d2f3SKarsten Keil if (!bch->rx_skb) 5386115d2f3SKarsten Keil goto handle_tx; 53909e79a77SKarsten Keil if (test_bit(FLG_TRANSPARENT, &bch->Flags) || 54009e79a77SKarsten Keil (stat & HDLC_STAT_RME)) { 5416115d2f3SKarsten Keil if (((stat & HDLC_STAT_CRCVFRRAB) == 5426115d2f3SKarsten Keil HDLC_STAT_CRCVFR) || 5436115d2f3SKarsten Keil test_bit(FLG_TRANSPARENT, &bch->Flags)) { 5446115d2f3SKarsten Keil recv_Bchannel(bch, 0); 5456115d2f3SKarsten Keil } else { 54609e79a77SKarsten Keil pr_warning("%s: got invalid frame\n", 5476115d2f3SKarsten Keil fc->name); 5486115d2f3SKarsten Keil skb_trim(bch->rx_skb, 0); 5496115d2f3SKarsten Keil } 5506115d2f3SKarsten Keil } 5516115d2f3SKarsten Keil } 5526115d2f3SKarsten Keil } 5536115d2f3SKarsten Keil handle_tx: 5546115d2f3SKarsten Keil if (stat & HDLC_INT_XDU) { 5556115d2f3SKarsten Keil /* Here we lost an TX interrupt, so 5566115d2f3SKarsten Keil * restart transmitting the whole frame on HDLC 5576115d2f3SKarsten Keil * in transparent mode we send the next data 5586115d2f3SKarsten Keil */ 55909e79a77SKarsten Keil pr_warning("%s: ch%d stat %x XDU %s\n", fc->name, bch->nr, 56009e79a77SKarsten Keil stat, bch->tx_skb ? "tx_skb" : "no tx_skb"); 5616115d2f3SKarsten Keil if (bch->tx_skb && bch->tx_skb->len) { 5626115d2f3SKarsten Keil if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) 5636115d2f3SKarsten Keil bch->tx_idx = 0; 5646115d2f3SKarsten Keil } 5656115d2f3SKarsten Keil hdlc->ctrl.sr.xml = 0; 5666115d2f3SKarsten Keil hdlc->ctrl.sr.cmd |= HDLC_CMD_XRS; 5676115d2f3SKarsten Keil write_ctrl(bch, 1); 5686115d2f3SKarsten Keil hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XRS; 5696115d2f3SKarsten Keil HDLC_irq_xpr(bch); 5706115d2f3SKarsten Keil return; 5716115d2f3SKarsten Keil } else if (stat & HDLC_INT_XPR) 5726115d2f3SKarsten Keil HDLC_irq_xpr(bch); 5736115d2f3SKarsten Keil } 5746115d2f3SKarsten Keil 5756115d2f3SKarsten Keil static inline void 5766115d2f3SKarsten Keil HDLC_irq_main(struct fritzcard *fc) 5776115d2f3SKarsten Keil { 5786115d2f3SKarsten Keil u32 stat; 5796115d2f3SKarsten Keil struct bchannel *bch; 5806115d2f3SKarsten Keil 5816115d2f3SKarsten Keil stat = read_status(fc, 1); 5826115d2f3SKarsten Keil if (stat & HDLC_INT_MASK) { 5836115d2f3SKarsten Keil bch = Sel_BCS(fc, 1); 5846115d2f3SKarsten Keil if (bch) 5856115d2f3SKarsten Keil HDLC_irq(bch, stat); 5866115d2f3SKarsten Keil else 5876115d2f3SKarsten Keil pr_debug("%s: spurious ch1 IRQ\n", fc->name); 5886115d2f3SKarsten Keil } 5896115d2f3SKarsten Keil stat = read_status(fc, 2); 5906115d2f3SKarsten Keil if (stat & HDLC_INT_MASK) { 5916115d2f3SKarsten Keil bch = Sel_BCS(fc, 2); 5926115d2f3SKarsten Keil if (bch) 5936115d2f3SKarsten Keil HDLC_irq(bch, stat); 5946115d2f3SKarsten Keil else 5956115d2f3SKarsten Keil pr_debug("%s: spurious ch2 IRQ\n", fc->name); 5966115d2f3SKarsten Keil } 5976115d2f3SKarsten Keil } 5986115d2f3SKarsten Keil 5996115d2f3SKarsten Keil static irqreturn_t 6006115d2f3SKarsten Keil avm_fritz_interrupt(int intno, void *dev_id) 6016115d2f3SKarsten Keil { 6026115d2f3SKarsten Keil struct fritzcard *fc = dev_id; 6036115d2f3SKarsten Keil u8 val; 6046115d2f3SKarsten Keil u8 sval; 6056115d2f3SKarsten Keil 6066115d2f3SKarsten Keil spin_lock(&fc->lock); 6076115d2f3SKarsten Keil sval = inb(fc->addr + 2); 6086115d2f3SKarsten Keil pr_debug("%s: irq stat0 %x\n", fc->name, sval); 6096115d2f3SKarsten Keil if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK) { 6106115d2f3SKarsten Keil /* shared IRQ from other HW */ 6116115d2f3SKarsten Keil spin_unlock(&fc->lock); 6126115d2f3SKarsten Keil return IRQ_NONE; 6136115d2f3SKarsten Keil } 6146115d2f3SKarsten Keil fc->irqcnt++; 6156115d2f3SKarsten Keil 6166115d2f3SKarsten Keil if (!(sval & AVM_STATUS0_IRQ_ISAC)) { 6176115d2f3SKarsten Keil val = ReadISAC_V1(fc, ISAC_ISTA); 6186115d2f3SKarsten Keil mISDNisac_irq(&fc->isac, val); 6196115d2f3SKarsten Keil } 6206115d2f3SKarsten Keil if (!(sval & AVM_STATUS0_IRQ_HDLC)) 6216115d2f3SKarsten Keil HDLC_irq_main(fc); 6226115d2f3SKarsten Keil spin_unlock(&fc->lock); 6236115d2f3SKarsten Keil return IRQ_HANDLED; 6246115d2f3SKarsten Keil } 6256115d2f3SKarsten Keil 6266115d2f3SKarsten Keil static irqreturn_t 6276115d2f3SKarsten Keil avm_fritzv2_interrupt(int intno, void *dev_id) 6286115d2f3SKarsten Keil { 6296115d2f3SKarsten Keil struct fritzcard *fc = dev_id; 6306115d2f3SKarsten Keil u8 val; 6316115d2f3SKarsten Keil u8 sval; 6326115d2f3SKarsten Keil 6336115d2f3SKarsten Keil spin_lock(&fc->lock); 6346115d2f3SKarsten Keil sval = inb(fc->addr + 2); 6356115d2f3SKarsten Keil pr_debug("%s: irq stat0 %x\n", fc->name, sval); 6366115d2f3SKarsten Keil if (!(sval & AVM_STATUS0_IRQ_MASK)) { 6376115d2f3SKarsten Keil /* shared IRQ from other HW */ 6386115d2f3SKarsten Keil spin_unlock(&fc->lock); 6396115d2f3SKarsten Keil return IRQ_NONE; 6406115d2f3SKarsten Keil } 6416115d2f3SKarsten Keil fc->irqcnt++; 6426115d2f3SKarsten Keil 6436115d2f3SKarsten Keil if (sval & AVM_STATUS0_IRQ_HDLC) 6446115d2f3SKarsten Keil HDLC_irq_main(fc); 6456115d2f3SKarsten Keil if (sval & AVM_STATUS0_IRQ_ISAC) { 6466115d2f3SKarsten Keil val = ReadISAC_V2(fc, ISACX_ISTA); 6476115d2f3SKarsten Keil mISDNisac_irq(&fc->isac, val); 6486115d2f3SKarsten Keil } 6496115d2f3SKarsten Keil if (sval & AVM_STATUS0_IRQ_TIMER) { 6506115d2f3SKarsten Keil pr_debug("%s: timer irq\n", fc->name); 6516115d2f3SKarsten Keil outb(fc->ctrlreg | AVM_STATUS0_RES_TIMER, fc->addr + 2); 6526115d2f3SKarsten Keil udelay(1); 6536115d2f3SKarsten Keil outb(fc->ctrlreg, fc->addr + 2); 6546115d2f3SKarsten Keil } 6556115d2f3SKarsten Keil spin_unlock(&fc->lock); 6566115d2f3SKarsten Keil return IRQ_HANDLED; 6576115d2f3SKarsten Keil } 6586115d2f3SKarsten Keil 6596115d2f3SKarsten Keil static int 6606115d2f3SKarsten Keil avm_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) 6616115d2f3SKarsten Keil { 6626115d2f3SKarsten Keil struct bchannel *bch = container_of(ch, struct bchannel, ch); 6636115d2f3SKarsten Keil struct fritzcard *fc = bch->hw; 6646115d2f3SKarsten Keil int ret = -EINVAL; 6656115d2f3SKarsten Keil struct mISDNhead *hh = mISDN_HEAD_P(skb); 6668bfddfbeSKarsten Keil unsigned long flags; 6676115d2f3SKarsten Keil 6686115d2f3SKarsten Keil switch (hh->prim) { 6696115d2f3SKarsten Keil case PH_DATA_REQ: 6706115d2f3SKarsten Keil spin_lock_irqsave(&fc->lock, flags); 6716115d2f3SKarsten Keil ret = bchannel_senddata(bch, skb); 6726115d2f3SKarsten Keil if (ret > 0) { /* direct TX */ 6736115d2f3SKarsten Keil hdlc_fill_fifo(bch); 6746115d2f3SKarsten Keil ret = 0; 6758bfddfbeSKarsten Keil } 6766115d2f3SKarsten Keil spin_unlock_irqrestore(&fc->lock, flags); 6776115d2f3SKarsten Keil return ret; 6786115d2f3SKarsten Keil case PH_ACTIVATE_REQ: 6796115d2f3SKarsten Keil spin_lock_irqsave(&fc->lock, flags); 6806115d2f3SKarsten Keil if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) 6816115d2f3SKarsten Keil ret = modehdlc(bch, ch->protocol); 6826115d2f3SKarsten Keil else 6836115d2f3SKarsten Keil ret = 0; 6846115d2f3SKarsten Keil spin_unlock_irqrestore(&fc->lock, flags); 6856115d2f3SKarsten Keil if (!ret) 6866115d2f3SKarsten Keil _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, 6876115d2f3SKarsten Keil NULL, GFP_KERNEL); 6886115d2f3SKarsten Keil break; 6896115d2f3SKarsten Keil case PH_DEACTIVATE_REQ: 6906115d2f3SKarsten Keil spin_lock_irqsave(&fc->lock, flags); 6916115d2f3SKarsten Keil mISDN_clear_bchannel(bch); 6926115d2f3SKarsten Keil modehdlc(bch, ISDN_P_NONE); 6936115d2f3SKarsten Keil spin_unlock_irqrestore(&fc->lock, flags); 6946115d2f3SKarsten Keil _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, 6956115d2f3SKarsten Keil NULL, GFP_KERNEL); 6966115d2f3SKarsten Keil ret = 0; 6976115d2f3SKarsten Keil break; 6986115d2f3SKarsten Keil } 6996115d2f3SKarsten Keil if (!ret) 7006115d2f3SKarsten Keil dev_kfree_skb(skb); 7016115d2f3SKarsten Keil return ret; 7026115d2f3SKarsten Keil } 7036115d2f3SKarsten Keil 7046115d2f3SKarsten Keil static void 7056115d2f3SKarsten Keil inithdlc(struct fritzcard *fc) 7066115d2f3SKarsten Keil { 7076115d2f3SKarsten Keil modehdlc(&fc->bch[0], -1); 7086115d2f3SKarsten Keil modehdlc(&fc->bch[1], -1); 7096115d2f3SKarsten Keil } 7106115d2f3SKarsten Keil 7116115d2f3SKarsten Keil void 7126115d2f3SKarsten Keil clear_pending_hdlc_ints(struct fritzcard *fc) 7136115d2f3SKarsten Keil { 7146115d2f3SKarsten Keil u32 val; 7156115d2f3SKarsten Keil 7166115d2f3SKarsten Keil val = read_status(fc, 1); 7176115d2f3SKarsten Keil pr_debug("%s: HDLC 1 STA %x\n", fc->name, val); 7186115d2f3SKarsten Keil val = read_status(fc, 2); 7196115d2f3SKarsten Keil pr_debug("%s: HDLC 2 STA %x\n", fc->name, val); 7206115d2f3SKarsten Keil } 7216115d2f3SKarsten Keil 7226115d2f3SKarsten Keil static void 7236115d2f3SKarsten Keil reset_avm(struct fritzcard *fc) 7246115d2f3SKarsten Keil { 7256115d2f3SKarsten Keil switch (fc->type) { 7266115d2f3SKarsten Keil case AVM_FRITZ_PCI: 7276115d2f3SKarsten Keil fc->ctrlreg = AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER; 7286115d2f3SKarsten Keil break; 7296115d2f3SKarsten Keil case AVM_FRITZ_PCIV2: 7306115d2f3SKarsten Keil fc->ctrlreg = AVM_STATUS0_RESET; 7316115d2f3SKarsten Keil break; 7326115d2f3SKarsten Keil } 7336115d2f3SKarsten Keil if (debug & DEBUG_HW) 7346115d2f3SKarsten Keil pr_notice("%s: reset\n", fc->name); 7356115d2f3SKarsten Keil disable_hwirq(fc); 7366115d2f3SKarsten Keil mdelay(5); 7376115d2f3SKarsten Keil switch (fc->type) { 7386115d2f3SKarsten Keil case AVM_FRITZ_PCI: 7396115d2f3SKarsten Keil fc->ctrlreg = AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER; 7406115d2f3SKarsten Keil disable_hwirq(fc); 7416115d2f3SKarsten Keil outb(AVM_STATUS1_ENA_IOM, fc->addr + 3); 7426115d2f3SKarsten Keil break; 7436115d2f3SKarsten Keil case AVM_FRITZ_PCIV2: 7446115d2f3SKarsten Keil fc->ctrlreg = 0; 7456115d2f3SKarsten Keil disable_hwirq(fc); 7466115d2f3SKarsten Keil break; 7476115d2f3SKarsten Keil } 7486115d2f3SKarsten Keil mdelay(1); 7496115d2f3SKarsten Keil if (debug & DEBUG_HW) 7506115d2f3SKarsten Keil pr_notice("%s: S0/S1 %x/%x\n", fc->name, 7516115d2f3SKarsten Keil inb(fc->addr + 2), inb(fc->addr + 3)); 7526115d2f3SKarsten Keil } 7536115d2f3SKarsten Keil 7546115d2f3SKarsten Keil static int 7556115d2f3SKarsten Keil init_card(struct fritzcard *fc) 7566115d2f3SKarsten Keil { 7576115d2f3SKarsten Keil int ret, cnt = 3; 7586115d2f3SKarsten Keil u_long flags; 7596115d2f3SKarsten Keil 7606115d2f3SKarsten Keil reset_avm(fc); /* disable IRQ */ 7616115d2f3SKarsten Keil if (fc->type == AVM_FRITZ_PCIV2) 7626115d2f3SKarsten Keil ret = request_irq(fc->irq, avm_fritzv2_interrupt, 7636115d2f3SKarsten Keil IRQF_SHARED, fc->name, fc); 7646115d2f3SKarsten Keil else 7656115d2f3SKarsten Keil ret = request_irq(fc->irq, avm_fritz_interrupt, 7666115d2f3SKarsten Keil IRQF_SHARED, fc->name, fc); 7676115d2f3SKarsten Keil if (ret) { 7686115d2f3SKarsten Keil pr_info("%s: couldn't get interrupt %d\n", 7696115d2f3SKarsten Keil fc->name, fc->irq); 7706115d2f3SKarsten Keil return ret; 7716115d2f3SKarsten Keil } 7726115d2f3SKarsten Keil while (cnt--) { 7736115d2f3SKarsten Keil spin_lock_irqsave(&fc->lock, flags); 7746115d2f3SKarsten Keil ret = fc->isac.init(&fc->isac); 7756115d2f3SKarsten Keil if (ret) { 7766115d2f3SKarsten Keil spin_unlock_irqrestore(&fc->lock, flags); 7776115d2f3SKarsten Keil pr_info("%s: ISAC init failed with %d\n", 7786115d2f3SKarsten Keil fc->name, ret); 7796115d2f3SKarsten Keil break; 7806115d2f3SKarsten Keil } 7816115d2f3SKarsten Keil clear_pending_hdlc_ints(fc); 7826115d2f3SKarsten Keil inithdlc(fc); 7836115d2f3SKarsten Keil enable_hwirq(fc); 7846115d2f3SKarsten Keil /* RESET Receiver and Transmitter */ 78509e79a77SKarsten Keil if (fc->type == AVM_FRITZ_PCIV2) { 7866115d2f3SKarsten Keil WriteISAC_V2(fc, ISACX_MASK, 0); 7876115d2f3SKarsten Keil WriteISAC_V2(fc, ISACX_CMDRD, 0x41); 7886115d2f3SKarsten Keil } else { 7896115d2f3SKarsten Keil WriteISAC_V1(fc, ISAC_MASK, 0); 7906115d2f3SKarsten Keil WriteISAC_V1(fc, ISAC_CMDR, 0x41); 7916115d2f3SKarsten Keil } 7926115d2f3SKarsten Keil spin_unlock_irqrestore(&fc->lock, flags); 7936115d2f3SKarsten Keil /* Timeout 10ms */ 7946115d2f3SKarsten Keil msleep_interruptible(10); 7956115d2f3SKarsten Keil if (debug & DEBUG_HW) 7966115d2f3SKarsten Keil pr_notice("%s: IRQ %d count %d\n", fc->name, 7976115d2f3SKarsten Keil fc->irq, fc->irqcnt); 7986115d2f3SKarsten Keil if (!fc->irqcnt) { 7996115d2f3SKarsten Keil pr_info("%s: IRQ(%d) getting no IRQs during init %d\n", 8006115d2f3SKarsten Keil fc->name, fc->irq, 3 - cnt); 8016115d2f3SKarsten Keil reset_avm(fc); 8026115d2f3SKarsten Keil } else 8036115d2f3SKarsten Keil return 0; 8046115d2f3SKarsten Keil } 8056115d2f3SKarsten Keil free_irq(fc->irq, fc); 8066115d2f3SKarsten Keil return -EIO; 8076115d2f3SKarsten Keil } 8086115d2f3SKarsten Keil 8096115d2f3SKarsten Keil static int 8106115d2f3SKarsten Keil channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) 8116115d2f3SKarsten Keil { 8126115d2f3SKarsten Keil int ret = 0; 8136115d2f3SKarsten Keil struct fritzcard *fc = bch->hw; 8146115d2f3SKarsten Keil 8156115d2f3SKarsten Keil switch (cq->op) { 8166115d2f3SKarsten Keil case MISDN_CTRL_GETOP: 8176115d2f3SKarsten Keil cq->op = 0; 8186115d2f3SKarsten Keil break; 8196115d2f3SKarsten Keil /* Nothing implemented yet */ 8206115d2f3SKarsten Keil case MISDN_CTRL_FILL_EMPTY: 8216115d2f3SKarsten Keil default: 8226115d2f3SKarsten Keil pr_info("%s: %s unknown Op %x\n", fc->name, __func__, cq->op); 8236115d2f3SKarsten Keil ret = -EINVAL; 8246115d2f3SKarsten Keil break; 8256115d2f3SKarsten Keil } 8266115d2f3SKarsten Keil return ret; 8276115d2f3SKarsten Keil } 8286115d2f3SKarsten Keil 8296115d2f3SKarsten Keil static int 8306115d2f3SKarsten Keil avm_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg) 8316115d2f3SKarsten Keil { 8326115d2f3SKarsten Keil struct bchannel *bch = container_of(ch, struct bchannel, ch); 8336115d2f3SKarsten Keil struct fritzcard *fc = bch->hw; 8346115d2f3SKarsten Keil int ret = -EINVAL; 8356115d2f3SKarsten Keil u_long flags; 8366115d2f3SKarsten Keil 8376115d2f3SKarsten Keil pr_debug("%s: %s cmd:%x %p\n", fc->name, __func__, cmd, arg); 8386115d2f3SKarsten Keil switch (cmd) { 8396115d2f3SKarsten Keil case CLOSE_CHANNEL: 8406115d2f3SKarsten Keil test_and_clear_bit(FLG_OPEN, &bch->Flags); 8416115d2f3SKarsten Keil spin_lock_irqsave(&fc->lock, flags); 8426115d2f3SKarsten Keil mISDN_freebchannel(bch); 8436115d2f3SKarsten Keil modehdlc(bch, ISDN_P_NONE); 8446115d2f3SKarsten Keil spin_unlock_irqrestore(&fc->lock, flags); 8456115d2f3SKarsten Keil ch->protocol = ISDN_P_NONE; 8466115d2f3SKarsten Keil ch->peer = NULL; 8476115d2f3SKarsten Keil module_put(THIS_MODULE); 8486115d2f3SKarsten Keil ret = 0; 8496115d2f3SKarsten Keil break; 8506115d2f3SKarsten Keil case CONTROL_CHANNEL: 8516115d2f3SKarsten Keil ret = channel_bctrl(bch, arg); 8526115d2f3SKarsten Keil break; 8536115d2f3SKarsten Keil default: 8546115d2f3SKarsten Keil pr_info("%s: %s unknown prim(%x)\n", fc->name, __func__, cmd); 8556115d2f3SKarsten Keil } 8566115d2f3SKarsten Keil return ret; 8576115d2f3SKarsten Keil } 8586115d2f3SKarsten Keil 8596115d2f3SKarsten Keil static int 8606115d2f3SKarsten Keil channel_ctrl(struct fritzcard *fc, struct mISDN_ctrl_req *cq) 8616115d2f3SKarsten Keil { 8626115d2f3SKarsten Keil int ret = 0; 8636115d2f3SKarsten Keil 8646115d2f3SKarsten Keil switch (cq->op) { 8656115d2f3SKarsten Keil case MISDN_CTRL_GETOP: 866c626c127SKarsten Keil cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_L1_TIMER3; 8676115d2f3SKarsten Keil break; 8686115d2f3SKarsten Keil case MISDN_CTRL_LOOP: 8696115d2f3SKarsten Keil /* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */ 8706115d2f3SKarsten Keil if (cq->channel < 0 || cq->channel > 3) { 8716115d2f3SKarsten Keil ret = -EINVAL; 8726115d2f3SKarsten Keil break; 8736115d2f3SKarsten Keil } 8746115d2f3SKarsten Keil ret = fc->isac.ctrl(&fc->isac, HW_TESTLOOP, cq->channel); 8756115d2f3SKarsten Keil break; 876c626c127SKarsten Keil case MISDN_CTRL_L1_TIMER3: 877c626c127SKarsten Keil ret = fc->isac.ctrl(&fc->isac, HW_TIMER3_VALUE, cq->p1); 878c626c127SKarsten Keil break; 8796115d2f3SKarsten Keil default: 8806115d2f3SKarsten Keil pr_info("%s: %s unknown Op %x\n", fc->name, __func__, cq->op); 8816115d2f3SKarsten Keil ret = -EINVAL; 8826115d2f3SKarsten Keil break; 8836115d2f3SKarsten Keil } 8846115d2f3SKarsten Keil return ret; 8856115d2f3SKarsten Keil } 8866115d2f3SKarsten Keil 8876115d2f3SKarsten Keil static int 8886115d2f3SKarsten Keil open_bchannel(struct fritzcard *fc, struct channel_req *rq) 8896115d2f3SKarsten Keil { 8906115d2f3SKarsten Keil struct bchannel *bch; 8916115d2f3SKarsten Keil 892819a1008SDan Carpenter if (rq->adr.channel == 0 || rq->adr.channel > 2) 8936115d2f3SKarsten Keil return -EINVAL; 8946115d2f3SKarsten Keil if (rq->protocol == ISDN_P_NONE) 8956115d2f3SKarsten Keil return -EINVAL; 8966115d2f3SKarsten Keil bch = &fc->bch[rq->adr.channel - 1]; 8976115d2f3SKarsten Keil if (test_and_set_bit(FLG_OPEN, &bch->Flags)) 8986115d2f3SKarsten Keil return -EBUSY; /* b-channel can be only open once */ 8996115d2f3SKarsten Keil test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags); 9006115d2f3SKarsten Keil bch->ch.protocol = rq->protocol; 9016115d2f3SKarsten Keil rq->ch = &bch->ch; 9026115d2f3SKarsten Keil return 0; 9036115d2f3SKarsten Keil } 9046115d2f3SKarsten Keil 9056115d2f3SKarsten Keil /* 9066115d2f3SKarsten Keil * device control function 9076115d2f3SKarsten Keil */ 9086115d2f3SKarsten Keil static int 9096115d2f3SKarsten Keil avm_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) 9106115d2f3SKarsten Keil { 9116115d2f3SKarsten Keil struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); 9126115d2f3SKarsten Keil struct dchannel *dch = container_of(dev, struct dchannel, dev); 9136115d2f3SKarsten Keil struct fritzcard *fc = dch->hw; 9146115d2f3SKarsten Keil struct channel_req *rq; 9156115d2f3SKarsten Keil int err = 0; 9166115d2f3SKarsten Keil 9176115d2f3SKarsten Keil pr_debug("%s: %s cmd:%x %p\n", fc->name, __func__, cmd, arg); 9186115d2f3SKarsten Keil switch (cmd) { 9196115d2f3SKarsten Keil case OPEN_CHANNEL: 9206115d2f3SKarsten Keil rq = arg; 9216115d2f3SKarsten Keil if (rq->protocol == ISDN_P_TE_S0) 9226115d2f3SKarsten Keil err = fc->isac.open(&fc->isac, rq); 9236115d2f3SKarsten Keil else 9246115d2f3SKarsten Keil err = open_bchannel(fc, rq); 9256115d2f3SKarsten Keil if (err) 9266115d2f3SKarsten Keil break; 9276115d2f3SKarsten Keil if (!try_module_get(THIS_MODULE)) 9286115d2f3SKarsten Keil pr_info("%s: cannot get module\n", fc->name); 9296115d2f3SKarsten Keil break; 9306115d2f3SKarsten Keil case CLOSE_CHANNEL: 9316115d2f3SKarsten Keil pr_debug("%s: dev(%d) close from %p\n", fc->name, dch->dev.id, 9326115d2f3SKarsten Keil __builtin_return_address(0)); 9336115d2f3SKarsten Keil module_put(THIS_MODULE); 9346115d2f3SKarsten Keil break; 9356115d2f3SKarsten Keil case CONTROL_CHANNEL: 9366115d2f3SKarsten Keil err = channel_ctrl(fc, arg); 9376115d2f3SKarsten Keil break; 9386115d2f3SKarsten Keil default: 9396115d2f3SKarsten Keil pr_debug("%s: %s unknown command %x\n", 9406115d2f3SKarsten Keil fc->name, __func__, cmd); 9416115d2f3SKarsten Keil return -EINVAL; 9426115d2f3SKarsten Keil } 9436115d2f3SKarsten Keil return err; 9446115d2f3SKarsten Keil } 9456115d2f3SKarsten Keil 9466115d2f3SKarsten Keil int 9476115d2f3SKarsten Keil setup_fritz(struct fritzcard *fc) 9486115d2f3SKarsten Keil { 9496115d2f3SKarsten Keil u32 val, ver; 9506115d2f3SKarsten Keil 9516115d2f3SKarsten Keil if (!request_region(fc->addr, 32, fc->name)) { 9526115d2f3SKarsten Keil pr_info("%s: AVM config port %x-%x already in use\n", 9536115d2f3SKarsten Keil fc->name, fc->addr, fc->addr + 31); 9546115d2f3SKarsten Keil return -EIO; 9556115d2f3SKarsten Keil } 9566115d2f3SKarsten Keil switch (fc->type) { 9576115d2f3SKarsten Keil case AVM_FRITZ_PCI: 9586115d2f3SKarsten Keil val = inl(fc->addr); 9596115d2f3SKarsten Keil outl(AVM_HDLC_1, fc->addr + CHIP_INDEX); 9606115d2f3SKarsten Keil ver = inl(fc->addr + CHIP_WINDOW + HDLC_STATUS) >> 24; 9616115d2f3SKarsten Keil if (debug & DEBUG_HW) { 9626115d2f3SKarsten Keil pr_notice("%s: PCI stat %#x\n", fc->name, val); 9636115d2f3SKarsten Keil pr_notice("%s: PCI Class %X Rev %d\n", fc->name, 9646115d2f3SKarsten Keil val & 0xff, (val >> 8) & 0xff); 9656115d2f3SKarsten Keil pr_notice("%s: HDLC version %x\n", fc->name, ver & 0xf); 9666115d2f3SKarsten Keil } 9676115d2f3SKarsten Keil ASSIGN_FUNC(V1, ISAC, fc->isac); 9686115d2f3SKarsten Keil fc->isac.type = IPAC_TYPE_ISAC; 9696115d2f3SKarsten Keil break; 9706115d2f3SKarsten Keil case AVM_FRITZ_PCIV2: 9716115d2f3SKarsten Keil val = inl(fc->addr); 9726115d2f3SKarsten Keil ver = inl(fc->addr + AVM_HDLC_STATUS_1) >> 24; 9736115d2f3SKarsten Keil if (debug & DEBUG_HW) { 9746115d2f3SKarsten Keil pr_notice("%s: PCI V2 stat %#x\n", fc->name, val); 9756115d2f3SKarsten Keil pr_notice("%s: PCI V2 Class %X Rev %d\n", fc->name, 9766115d2f3SKarsten Keil val & 0xff, (val >> 8) & 0xff); 9776115d2f3SKarsten Keil pr_notice("%s: HDLC version %x\n", fc->name, ver & 0xf); 9786115d2f3SKarsten Keil } 9796115d2f3SKarsten Keil ASSIGN_FUNC(V2, ISAC, fc->isac); 9806115d2f3SKarsten Keil fc->isac.type = IPAC_TYPE_ISACX; 9816115d2f3SKarsten Keil break; 9826115d2f3SKarsten Keil default: 9836115d2f3SKarsten Keil release_region(fc->addr, 32); 9846115d2f3SKarsten Keil pr_info("%s: AVM unknown type %d\n", fc->name, fc->type); 9856115d2f3SKarsten Keil return -ENODEV; 9866115d2f3SKarsten Keil } 9876115d2f3SKarsten Keil pr_notice("%s: %s config irq:%d base:0x%X\n", fc->name, 9886115d2f3SKarsten Keil (fc->type == AVM_FRITZ_PCI) ? "AVM Fritz!CARD PCI" : 9896115d2f3SKarsten Keil "AVM Fritz!CARD PCIv2", fc->irq, fc->addr); 9906115d2f3SKarsten Keil return 0; 9916115d2f3SKarsten Keil } 9926115d2f3SKarsten Keil 9936115d2f3SKarsten Keil static void 9946115d2f3SKarsten Keil release_card(struct fritzcard *card) 9956115d2f3SKarsten Keil { 9966115d2f3SKarsten Keil u_long flags; 9976115d2f3SKarsten Keil 9986115d2f3SKarsten Keil disable_hwirq(card); 9996115d2f3SKarsten Keil spin_lock_irqsave(&card->lock, flags); 10006115d2f3SKarsten Keil modehdlc(&card->bch[0], ISDN_P_NONE); 10016115d2f3SKarsten Keil modehdlc(&card->bch[1], ISDN_P_NONE); 10026115d2f3SKarsten Keil spin_unlock_irqrestore(&card->lock, flags); 10036115d2f3SKarsten Keil card->isac.release(&card->isac); 10046115d2f3SKarsten Keil free_irq(card->irq, card); 10056115d2f3SKarsten Keil mISDN_freebchannel(&card->bch[1]); 10066115d2f3SKarsten Keil mISDN_freebchannel(&card->bch[0]); 10076115d2f3SKarsten Keil mISDN_unregister_device(&card->isac.dch.dev); 10086115d2f3SKarsten Keil release_region(card->addr, 32); 10096115d2f3SKarsten Keil pci_disable_device(card->pdev); 10106115d2f3SKarsten Keil pci_set_drvdata(card->pdev, NULL); 10116115d2f3SKarsten Keil write_lock_irqsave(&card_lock, flags); 10126115d2f3SKarsten Keil list_del(&card->list); 10136115d2f3SKarsten Keil write_unlock_irqrestore(&card_lock, flags); 10146115d2f3SKarsten Keil kfree(card); 10156115d2f3SKarsten Keil AVM_cnt--; 10166115d2f3SKarsten Keil } 10176115d2f3SKarsten Keil 10186115d2f3SKarsten Keil static int __devinit 10196115d2f3SKarsten Keil setup_instance(struct fritzcard *card) 10206115d2f3SKarsten Keil { 10216115d2f3SKarsten Keil int i, err; 10226115d2f3SKarsten Keil u_long flags; 10236115d2f3SKarsten Keil 10246115d2f3SKarsten Keil snprintf(card->name, MISDN_MAX_IDLEN - 1, "AVM.%d", AVM_cnt + 1); 10256115d2f3SKarsten Keil write_lock_irqsave(&card_lock, flags); 10266115d2f3SKarsten Keil list_add_tail(&card->list, &Cards); 10276115d2f3SKarsten Keil write_unlock_irqrestore(&card_lock, flags); 10286115d2f3SKarsten Keil 10296115d2f3SKarsten Keil _set_debug(card); 10306115d2f3SKarsten Keil card->isac.name = card->name; 10316115d2f3SKarsten Keil spin_lock_init(&card->lock); 10326115d2f3SKarsten Keil card->isac.hwlock = &card->lock; 10336115d2f3SKarsten Keil mISDNisac_init(&card->isac, card); 10346115d2f3SKarsten Keil 10356115d2f3SKarsten Keil card->isac.dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | 10366115d2f3SKarsten Keil (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); 10376115d2f3SKarsten Keil card->isac.dch.dev.D.ctrl = avm_dctrl; 10386115d2f3SKarsten Keil for (i = 0; i < 2; i++) { 10396115d2f3SKarsten Keil card->bch[i].nr = i + 1; 10406115d2f3SKarsten Keil set_channelmap(i + 1, card->isac.dch.dev.channelmap); 10416115d2f3SKarsten Keil mISDN_initbchannel(&card->bch[i], MAX_DATA_MEM); 10426115d2f3SKarsten Keil card->bch[i].hw = card; 10436115d2f3SKarsten Keil card->bch[i].ch.send = avm_l2l1B; 10446115d2f3SKarsten Keil card->bch[i].ch.ctrl = avm_bctrl; 10456115d2f3SKarsten Keil card->bch[i].ch.nr = i + 1; 10466115d2f3SKarsten Keil list_add(&card->bch[i].ch.list, &card->isac.dch.dev.bchannels); 10476115d2f3SKarsten Keil } 10486115d2f3SKarsten Keil err = setup_fritz(card); 10496115d2f3SKarsten Keil if (err) 10506115d2f3SKarsten Keil goto error; 10516115d2f3SKarsten Keil err = mISDN_register_device(&card->isac.dch.dev, &card->pdev->dev, 10526115d2f3SKarsten Keil card->name); 10536115d2f3SKarsten Keil if (err) 10546115d2f3SKarsten Keil goto error_reg; 10556115d2f3SKarsten Keil err = init_card(card); 10566115d2f3SKarsten Keil if (!err) { 10576115d2f3SKarsten Keil AVM_cnt++; 10586115d2f3SKarsten Keil pr_notice("AVM %d cards installed DEBUG\n", AVM_cnt); 10596115d2f3SKarsten Keil return 0; 10606115d2f3SKarsten Keil } 10616115d2f3SKarsten Keil mISDN_unregister_device(&card->isac.dch.dev); 10626115d2f3SKarsten Keil error_reg: 10636115d2f3SKarsten Keil release_region(card->addr, 32); 10646115d2f3SKarsten Keil error: 10656115d2f3SKarsten Keil card->isac.release(&card->isac); 10666115d2f3SKarsten Keil mISDN_freebchannel(&card->bch[1]); 10676115d2f3SKarsten Keil mISDN_freebchannel(&card->bch[0]); 10686115d2f3SKarsten Keil write_lock_irqsave(&card_lock, flags); 10696115d2f3SKarsten Keil list_del(&card->list); 10706115d2f3SKarsten Keil write_unlock_irqrestore(&card_lock, flags); 10716115d2f3SKarsten Keil kfree(card); 10726115d2f3SKarsten Keil return err; 10736115d2f3SKarsten Keil } 10746115d2f3SKarsten Keil 10756115d2f3SKarsten Keil static int __devinit 10766115d2f3SKarsten Keil fritzpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 10776115d2f3SKarsten Keil { 10786115d2f3SKarsten Keil int err = -ENOMEM; 10796115d2f3SKarsten Keil struct fritzcard *card; 10806115d2f3SKarsten Keil 10816115d2f3SKarsten Keil card = kzalloc(sizeof(struct fritzcard), GFP_KERNEL); 10826115d2f3SKarsten Keil if (!card) { 10836115d2f3SKarsten Keil pr_info("No kmem for fritzcard\n"); 10846115d2f3SKarsten Keil return err; 10856115d2f3SKarsten Keil } 10866115d2f3SKarsten Keil if (pdev->device == PCI_DEVICE_ID_AVM_A1_V2) 10876115d2f3SKarsten Keil card->type = AVM_FRITZ_PCIV2; 10886115d2f3SKarsten Keil else 10896115d2f3SKarsten Keil card->type = AVM_FRITZ_PCI; 10906115d2f3SKarsten Keil card->pdev = pdev; 10916115d2f3SKarsten Keil err = pci_enable_device(pdev); 10926115d2f3SKarsten Keil if (err) { 10936115d2f3SKarsten Keil kfree(card); 10946115d2f3SKarsten Keil return err; 10956115d2f3SKarsten Keil } 10966115d2f3SKarsten Keil 10976115d2f3SKarsten Keil pr_notice("mISDN: found adapter %s at %s\n", 10986115d2f3SKarsten Keil (char *) ent->driver_data, pci_name(pdev)); 10996115d2f3SKarsten Keil 11006115d2f3SKarsten Keil card->addr = pci_resource_start(pdev, 1); 11016115d2f3SKarsten Keil card->irq = pdev->irq; 11026115d2f3SKarsten Keil pci_set_drvdata(pdev, card); 11036115d2f3SKarsten Keil err = setup_instance(card); 11046115d2f3SKarsten Keil if (err) 11056115d2f3SKarsten Keil pci_set_drvdata(pdev, NULL); 11066115d2f3SKarsten Keil return err; 11076115d2f3SKarsten Keil } 11086115d2f3SKarsten Keil 11096115d2f3SKarsten Keil static void __devexit 11106115d2f3SKarsten Keil fritz_remove_pci(struct pci_dev *pdev) 11116115d2f3SKarsten Keil { 11126115d2f3SKarsten Keil struct fritzcard *card = pci_get_drvdata(pdev); 11136115d2f3SKarsten Keil 11146115d2f3SKarsten Keil if (card) 11156115d2f3SKarsten Keil release_card(card); 11166115d2f3SKarsten Keil else 11176115d2f3SKarsten Keil if (debug) 1118698f9315SUwe Kleine-König pr_info("%s: drvdata already removed\n", __func__); 11196115d2f3SKarsten Keil } 11206115d2f3SKarsten Keil 11216115d2f3SKarsten Keil static struct pci_device_id fcpci_ids[] __devinitdata = { 11226115d2f3SKarsten Keil { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1, PCI_ANY_ID, PCI_ANY_ID, 11236115d2f3SKarsten Keil 0, 0, (unsigned long) "Fritz!Card PCI"}, 11246115d2f3SKarsten Keil { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1_V2, PCI_ANY_ID, PCI_ANY_ID, 11256115d2f3SKarsten Keil 0, 0, (unsigned long) "Fritz!Card PCI v2" }, 11266115d2f3SKarsten Keil { } 11276115d2f3SKarsten Keil }; 11286115d2f3SKarsten Keil MODULE_DEVICE_TABLE(pci, fcpci_ids); 11296115d2f3SKarsten Keil 11306115d2f3SKarsten Keil static struct pci_driver fcpci_driver = { 11316115d2f3SKarsten Keil .name = "fcpci", 11326115d2f3SKarsten Keil .probe = fritzpci_probe, 11336115d2f3SKarsten Keil .remove = __devexit_p(fritz_remove_pci), 11346115d2f3SKarsten Keil .id_table = fcpci_ids, 11356115d2f3SKarsten Keil }; 11366115d2f3SKarsten Keil 11376115d2f3SKarsten Keil static int __init AVM_init(void) 11386115d2f3SKarsten Keil { 11396115d2f3SKarsten Keil int err; 11406115d2f3SKarsten Keil 11416115d2f3SKarsten Keil pr_notice("AVM Fritz PCI driver Rev. %s\n", AVMFRITZ_REV); 11426115d2f3SKarsten Keil err = pci_register_driver(&fcpci_driver); 11436115d2f3SKarsten Keil return err; 11446115d2f3SKarsten Keil } 11456115d2f3SKarsten Keil 11466115d2f3SKarsten Keil static void __exit AVM_cleanup(void) 11476115d2f3SKarsten Keil { 11486115d2f3SKarsten Keil pci_unregister_driver(&fcpci_driver); 11496115d2f3SKarsten Keil } 11506115d2f3SKarsten Keil 11516115d2f3SKarsten Keil module_init(AVM_init); 11526115d2f3SKarsten Keil module_exit(AVM_cleanup); 1153