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 */ 236115d2f3SKarsten Keil #include <linux/module.h> 246115d2f3SKarsten Keil #include <linux/pci.h> 256115d2f3SKarsten Keil #include <linux/delay.h> 266115d2f3SKarsten Keil #include <linux/mISDNhw.h> 27*5a0e3ad6STejun Heo #include <linux/slab.h> 286115d2f3SKarsten Keil #include <asm/unaligned.h> 296115d2f3SKarsten Keil #include "ipac.h" 306115d2f3SKarsten Keil 316115d2f3SKarsten Keil 326115d2f3SKarsten Keil #define AVMFRITZ_REV "2.1" 336115d2f3SKarsten Keil 346115d2f3SKarsten Keil static int AVM_cnt; 356115d2f3SKarsten Keil static int debug; 366115d2f3SKarsten Keil 376115d2f3SKarsten Keil enum { 386115d2f3SKarsten Keil AVM_FRITZ_PCI, 396115d2f3SKarsten Keil AVM_FRITZ_PCIV2, 406115d2f3SKarsten Keil }; 416115d2f3SKarsten Keil 426115d2f3SKarsten Keil #define HDLC_FIFO 0x0 436115d2f3SKarsten Keil #define HDLC_STATUS 0x4 446115d2f3SKarsten Keil #define CHIP_WINDOW 0x10 456115d2f3SKarsten Keil 466115d2f3SKarsten Keil #define CHIP_INDEX 0x4 476115d2f3SKarsten Keil #define AVM_HDLC_1 0x00 486115d2f3SKarsten Keil #define AVM_HDLC_2 0x01 496115d2f3SKarsten Keil #define AVM_ISAC_FIFO 0x02 506115d2f3SKarsten Keil #define AVM_ISAC_REG_LOW 0x04 516115d2f3SKarsten Keil #define AVM_ISAC_REG_HIGH 0x06 526115d2f3SKarsten Keil 536115d2f3SKarsten Keil #define AVM_STATUS0_IRQ_ISAC 0x01 546115d2f3SKarsten Keil #define AVM_STATUS0_IRQ_HDLC 0x02 556115d2f3SKarsten Keil #define AVM_STATUS0_IRQ_TIMER 0x04 566115d2f3SKarsten Keil #define AVM_STATUS0_IRQ_MASK 0x07 576115d2f3SKarsten Keil 586115d2f3SKarsten Keil #define AVM_STATUS0_RESET 0x01 596115d2f3SKarsten Keil #define AVM_STATUS0_DIS_TIMER 0x02 606115d2f3SKarsten Keil #define AVM_STATUS0_RES_TIMER 0x04 616115d2f3SKarsten Keil #define AVM_STATUS0_ENA_IRQ 0x08 626115d2f3SKarsten Keil #define AVM_STATUS0_TESTBIT 0x10 636115d2f3SKarsten Keil 646115d2f3SKarsten Keil #define AVM_STATUS1_INT_SEL 0x0f 656115d2f3SKarsten Keil #define AVM_STATUS1_ENA_IOM 0x80 666115d2f3SKarsten Keil 676115d2f3SKarsten Keil #define HDLC_MODE_ITF_FLG 0x01 686115d2f3SKarsten Keil #define HDLC_MODE_TRANS 0x02 696115d2f3SKarsten Keil #define HDLC_MODE_CCR_7 0x04 706115d2f3SKarsten Keil #define HDLC_MODE_CCR_16 0x08 716115d2f3SKarsten Keil #define HDLC_MODE_TESTLOOP 0x80 726115d2f3SKarsten Keil 736115d2f3SKarsten Keil #define HDLC_INT_XPR 0x80 746115d2f3SKarsten Keil #define HDLC_INT_XDU 0x40 756115d2f3SKarsten Keil #define HDLC_INT_RPR 0x20 766115d2f3SKarsten Keil #define HDLC_INT_MASK 0xE0 776115d2f3SKarsten Keil 786115d2f3SKarsten Keil #define HDLC_STAT_RME 0x01 796115d2f3SKarsten Keil #define HDLC_STAT_RDO 0x10 806115d2f3SKarsten Keil #define HDLC_STAT_CRCVFRRAB 0x0E 816115d2f3SKarsten Keil #define HDLC_STAT_CRCVFR 0x06 826115d2f3SKarsten Keil #define HDLC_STAT_RML_MASK 0x3f00 836115d2f3SKarsten Keil 846115d2f3SKarsten Keil #define HDLC_CMD_XRS 0x80 856115d2f3SKarsten Keil #define HDLC_CMD_XME 0x01 866115d2f3SKarsten Keil #define HDLC_CMD_RRS 0x20 876115d2f3SKarsten Keil #define HDLC_CMD_XML_MASK 0x3f00 886115d2f3SKarsten Keil #define HDLC_FIFO_SIZE 32 896115d2f3SKarsten Keil 906115d2f3SKarsten Keil /* Fritz PCI v2.0 */ 916115d2f3SKarsten Keil 926115d2f3SKarsten Keil #define AVM_HDLC_FIFO_1 0x10 936115d2f3SKarsten Keil #define AVM_HDLC_FIFO_2 0x18 946115d2f3SKarsten Keil 956115d2f3SKarsten Keil #define AVM_HDLC_STATUS_1 0x14 966115d2f3SKarsten Keil #define AVM_HDLC_STATUS_2 0x1c 976115d2f3SKarsten Keil 986115d2f3SKarsten Keil #define AVM_ISACX_INDEX 0x04 996115d2f3SKarsten Keil #define AVM_ISACX_DATA 0x08 1006115d2f3SKarsten Keil 1016115d2f3SKarsten Keil /* data struct */ 1026115d2f3SKarsten Keil #define LOG_SIZE 63 1036115d2f3SKarsten Keil 1046115d2f3SKarsten Keil struct hdlc_stat_reg { 1056115d2f3SKarsten Keil #ifdef __BIG_ENDIAN 1066115d2f3SKarsten Keil u8 fill; 1076115d2f3SKarsten Keil u8 mode; 1086115d2f3SKarsten Keil u8 xml; 1096115d2f3SKarsten Keil u8 cmd; 1106115d2f3SKarsten Keil #else 1116115d2f3SKarsten Keil u8 cmd; 1126115d2f3SKarsten Keil u8 xml; 1136115d2f3SKarsten Keil u8 mode; 1146115d2f3SKarsten Keil u8 fill; 1156115d2f3SKarsten Keil #endif 1166115d2f3SKarsten Keil } __attribute__((packed)); 1176115d2f3SKarsten Keil 1186115d2f3SKarsten Keil struct hdlc_hw { 1196115d2f3SKarsten Keil union { 1206115d2f3SKarsten Keil u32 ctrl; 1216115d2f3SKarsten Keil struct hdlc_stat_reg sr; 1226115d2f3SKarsten Keil } ctrl; 1236115d2f3SKarsten Keil u32 stat; 1246115d2f3SKarsten Keil }; 1256115d2f3SKarsten Keil 1266115d2f3SKarsten Keil struct fritzcard { 1276115d2f3SKarsten Keil struct list_head list; 1286115d2f3SKarsten Keil struct pci_dev *pdev; 1296115d2f3SKarsten Keil char name[MISDN_MAX_IDLEN]; 1306115d2f3SKarsten Keil u8 type; 1316115d2f3SKarsten Keil u8 ctrlreg; 1326115d2f3SKarsten Keil u16 irq; 1336115d2f3SKarsten Keil u32 irqcnt; 1346115d2f3SKarsten Keil u32 addr; 1356115d2f3SKarsten Keil spinlock_t lock; /* hw lock */ 1366115d2f3SKarsten Keil struct isac_hw isac; 1376115d2f3SKarsten Keil struct hdlc_hw hdlc[2]; 1386115d2f3SKarsten Keil struct bchannel bch[2]; 1396115d2f3SKarsten Keil char log[LOG_SIZE + 1]; 1406115d2f3SKarsten Keil }; 1416115d2f3SKarsten Keil 1426115d2f3SKarsten Keil static LIST_HEAD(Cards); 1436115d2f3SKarsten Keil static DEFINE_RWLOCK(card_lock); /* protect Cards */ 1446115d2f3SKarsten Keil 1456115d2f3SKarsten Keil static void 1466115d2f3SKarsten Keil _set_debug(struct fritzcard *card) 1476115d2f3SKarsten Keil { 1486115d2f3SKarsten Keil card->isac.dch.debug = debug; 1496115d2f3SKarsten Keil card->bch[0].debug = debug; 1506115d2f3SKarsten Keil card->bch[1].debug = debug; 1516115d2f3SKarsten Keil } 1526115d2f3SKarsten Keil 1536115d2f3SKarsten Keil static int 1546115d2f3SKarsten Keil set_debug(const char *val, struct kernel_param *kp) 1556115d2f3SKarsten Keil { 1566115d2f3SKarsten Keil int ret; 1576115d2f3SKarsten Keil struct fritzcard *card; 1586115d2f3SKarsten Keil 1596115d2f3SKarsten Keil ret = param_set_uint(val, kp); 1606115d2f3SKarsten Keil if (!ret) { 1616115d2f3SKarsten Keil read_lock(&card_lock); 1626115d2f3SKarsten Keil list_for_each_entry(card, &Cards, list) 1636115d2f3SKarsten Keil _set_debug(card); 1646115d2f3SKarsten Keil read_unlock(&card_lock); 1656115d2f3SKarsten Keil } 1666115d2f3SKarsten Keil return ret; 1676115d2f3SKarsten Keil } 1686115d2f3SKarsten Keil 1696115d2f3SKarsten Keil MODULE_AUTHOR("Karsten Keil"); 1706115d2f3SKarsten Keil MODULE_LICENSE("GPL v2"); 1716115d2f3SKarsten Keil MODULE_VERSION(AVMFRITZ_REV); 1726115d2f3SKarsten Keil module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR); 1736115d2f3SKarsten Keil MODULE_PARM_DESC(debug, "avmfritz debug mask"); 1746115d2f3SKarsten Keil 1756115d2f3SKarsten Keil /* Interface functions */ 1766115d2f3SKarsten Keil 1776115d2f3SKarsten Keil static u8 1786115d2f3SKarsten Keil ReadISAC_V1(void *p, u8 offset) 1796115d2f3SKarsten Keil { 1806115d2f3SKarsten Keil struct fritzcard *fc = p; 1816115d2f3SKarsten Keil u8 idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW; 1826115d2f3SKarsten Keil 1836115d2f3SKarsten Keil outb(idx, fc->addr + CHIP_INDEX); 1846115d2f3SKarsten Keil return inb(fc->addr + CHIP_WINDOW + (offset & 0xf)); 1856115d2f3SKarsten Keil } 1866115d2f3SKarsten Keil 1876115d2f3SKarsten Keil static void 1886115d2f3SKarsten Keil WriteISAC_V1(void *p, u8 offset, u8 value) 1896115d2f3SKarsten Keil { 1906115d2f3SKarsten Keil struct fritzcard *fc = p; 1916115d2f3SKarsten Keil u8 idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW; 1926115d2f3SKarsten Keil 1936115d2f3SKarsten Keil outb(idx, fc->addr + CHIP_INDEX); 1946115d2f3SKarsten Keil outb(value, fc->addr + CHIP_WINDOW + (offset & 0xf)); 1956115d2f3SKarsten Keil } 1966115d2f3SKarsten Keil 1976115d2f3SKarsten Keil static void 1986115d2f3SKarsten Keil ReadFiFoISAC_V1(void *p, u8 off, u8 *data, int size) 1996115d2f3SKarsten Keil { 2006115d2f3SKarsten Keil struct fritzcard *fc = p; 2016115d2f3SKarsten Keil 2026115d2f3SKarsten Keil outb(AVM_ISAC_FIFO, fc->addr + CHIP_INDEX); 2036115d2f3SKarsten Keil insb(fc->addr + CHIP_WINDOW, data, size); 2046115d2f3SKarsten Keil } 2056115d2f3SKarsten Keil 2066115d2f3SKarsten Keil static void 2076115d2f3SKarsten Keil WriteFiFoISAC_V1(void *p, u8 off, u8 *data, int size) 2086115d2f3SKarsten Keil { 2096115d2f3SKarsten Keil struct fritzcard *fc = p; 2106115d2f3SKarsten Keil 2116115d2f3SKarsten Keil outb(AVM_ISAC_FIFO, fc->addr + CHIP_INDEX); 2126115d2f3SKarsten Keil outsb(fc->addr + CHIP_WINDOW, data, size); 2136115d2f3SKarsten Keil } 2146115d2f3SKarsten Keil 2156115d2f3SKarsten Keil static u8 2166115d2f3SKarsten Keil ReadISAC_V2(void *p, u8 offset) 2176115d2f3SKarsten Keil { 2186115d2f3SKarsten Keil struct fritzcard *fc = p; 2196115d2f3SKarsten Keil 2206115d2f3SKarsten Keil outl(offset, fc->addr + AVM_ISACX_INDEX); 2216115d2f3SKarsten Keil return 0xff & inl(fc->addr + AVM_ISACX_DATA); 2226115d2f3SKarsten Keil } 2236115d2f3SKarsten Keil 2246115d2f3SKarsten Keil static void 2256115d2f3SKarsten Keil WriteISAC_V2(void *p, u8 offset, u8 value) 2266115d2f3SKarsten Keil { 2276115d2f3SKarsten Keil struct fritzcard *fc = p; 2286115d2f3SKarsten Keil 2296115d2f3SKarsten Keil outl(offset, fc->addr + AVM_ISACX_INDEX); 2306115d2f3SKarsten Keil outl(value, fc->addr + AVM_ISACX_DATA); 2316115d2f3SKarsten Keil } 2326115d2f3SKarsten Keil 2336115d2f3SKarsten Keil static void 2346115d2f3SKarsten Keil ReadFiFoISAC_V2(void *p, u8 off, u8 *data, int size) 2356115d2f3SKarsten Keil { 2366115d2f3SKarsten Keil struct fritzcard *fc = p; 2376115d2f3SKarsten Keil int i; 2386115d2f3SKarsten Keil 2396115d2f3SKarsten Keil outl(off, fc->addr + AVM_ISACX_INDEX); 2406115d2f3SKarsten Keil for (i = 0; i < size; i++) 2416115d2f3SKarsten Keil data[i] = 0xff & inl(fc->addr + AVM_ISACX_DATA); 2426115d2f3SKarsten Keil } 2436115d2f3SKarsten Keil 2446115d2f3SKarsten Keil static void 2456115d2f3SKarsten Keil WriteFiFoISAC_V2(void *p, u8 off, u8 *data, int size) 2466115d2f3SKarsten Keil { 2476115d2f3SKarsten Keil struct fritzcard *fc = p; 2486115d2f3SKarsten Keil int i; 2496115d2f3SKarsten Keil 2506115d2f3SKarsten Keil outl(off, fc->addr + AVM_ISACX_INDEX); 2516115d2f3SKarsten Keil for (i = 0; i < size; i++) 2526115d2f3SKarsten Keil outl(data[i], fc->addr + AVM_ISACX_DATA); 2536115d2f3SKarsten Keil } 2546115d2f3SKarsten Keil 2556115d2f3SKarsten Keil static struct bchannel * 2566115d2f3SKarsten Keil Sel_BCS(struct fritzcard *fc, u32 channel) 2576115d2f3SKarsten Keil { 2586115d2f3SKarsten Keil if (test_bit(FLG_ACTIVE, &fc->bch[0].Flags) && 2596115d2f3SKarsten Keil (fc->bch[0].nr & channel)) 2606115d2f3SKarsten Keil return &fc->bch[0]; 2616115d2f3SKarsten Keil else if (test_bit(FLG_ACTIVE, &fc->bch[1].Flags) && 2626115d2f3SKarsten Keil (fc->bch[1].nr & channel)) 2636115d2f3SKarsten Keil return &fc->bch[1]; 2646115d2f3SKarsten Keil else 2656115d2f3SKarsten Keil return NULL; 2666115d2f3SKarsten Keil } 2676115d2f3SKarsten Keil 2686115d2f3SKarsten Keil static inline void 2696115d2f3SKarsten Keil __write_ctrl_pci(struct fritzcard *fc, struct hdlc_hw *hdlc, u32 channel) { 2706115d2f3SKarsten Keil u32 idx = channel == 2 ? AVM_HDLC_2 : AVM_HDLC_1; 2716115d2f3SKarsten Keil 2726115d2f3SKarsten Keil outl(idx, fc->addr + CHIP_INDEX); 2736115d2f3SKarsten Keil outl(hdlc->ctrl.ctrl, fc->addr + CHIP_WINDOW + HDLC_STATUS); 2746115d2f3SKarsten Keil } 2756115d2f3SKarsten Keil 2766115d2f3SKarsten Keil static inline void 2776115d2f3SKarsten Keil __write_ctrl_pciv2(struct fritzcard *fc, struct hdlc_hw *hdlc, u32 channel) { 2786115d2f3SKarsten Keil outl(hdlc->ctrl.ctrl, fc->addr + (channel == 2 ? AVM_HDLC_STATUS_2 : 2796115d2f3SKarsten Keil AVM_HDLC_STATUS_1)); 2806115d2f3SKarsten Keil } 2816115d2f3SKarsten Keil 2826115d2f3SKarsten Keil void 2836115d2f3SKarsten Keil write_ctrl(struct bchannel *bch, int which) { 2846115d2f3SKarsten Keil struct fritzcard *fc = bch->hw; 2856115d2f3SKarsten Keil struct hdlc_hw *hdlc; 2866115d2f3SKarsten Keil 2876115d2f3SKarsten Keil hdlc = &fc->hdlc[(bch->nr - 1) & 1]; 2886115d2f3SKarsten Keil pr_debug("%s: hdlc %c wr%x ctrl %x\n", fc->name, '@' + bch->nr, 2896115d2f3SKarsten Keil which, hdlc->ctrl.ctrl); 2906115d2f3SKarsten Keil switch (fc->type) { 2916115d2f3SKarsten Keil case AVM_FRITZ_PCIV2: 2926115d2f3SKarsten Keil __write_ctrl_pciv2(fc, hdlc, bch->nr); 2936115d2f3SKarsten Keil break; 2946115d2f3SKarsten Keil case AVM_FRITZ_PCI: 2956115d2f3SKarsten Keil __write_ctrl_pci(fc, hdlc, bch->nr); 2966115d2f3SKarsten Keil break; 2976115d2f3SKarsten Keil } 2986115d2f3SKarsten Keil } 2996115d2f3SKarsten Keil 3006115d2f3SKarsten Keil 3016115d2f3SKarsten Keil static inline u32 3026115d2f3SKarsten Keil __read_status_pci(u_long addr, u32 channel) 3036115d2f3SKarsten Keil { 3046115d2f3SKarsten Keil outl(channel == 2 ? AVM_HDLC_2 : AVM_HDLC_1, addr + CHIP_INDEX); 3056115d2f3SKarsten Keil return inl(addr + CHIP_WINDOW + HDLC_STATUS); 3066115d2f3SKarsten Keil } 3076115d2f3SKarsten Keil 3086115d2f3SKarsten Keil static inline u32 3096115d2f3SKarsten Keil __read_status_pciv2(u_long addr, u32 channel) 3106115d2f3SKarsten Keil { 3116115d2f3SKarsten Keil return inl(addr + (channel == 2 ? AVM_HDLC_STATUS_2 : 3126115d2f3SKarsten Keil AVM_HDLC_STATUS_1)); 3136115d2f3SKarsten Keil } 3146115d2f3SKarsten Keil 3156115d2f3SKarsten Keil 3166115d2f3SKarsten Keil static u32 3176115d2f3SKarsten Keil read_status(struct fritzcard *fc, u32 channel) 3186115d2f3SKarsten Keil { 3196115d2f3SKarsten Keil switch (fc->type) { 3206115d2f3SKarsten Keil case AVM_FRITZ_PCIV2: 3216115d2f3SKarsten Keil return __read_status_pciv2(fc->addr, channel); 3226115d2f3SKarsten Keil case AVM_FRITZ_PCI: 3236115d2f3SKarsten Keil return __read_status_pci(fc->addr, channel); 3246115d2f3SKarsten Keil } 3256115d2f3SKarsten Keil /* dummy */ 3266115d2f3SKarsten Keil return 0; 3276115d2f3SKarsten Keil } 3286115d2f3SKarsten Keil 3296115d2f3SKarsten Keil static void 3306115d2f3SKarsten Keil enable_hwirq(struct fritzcard *fc) 3316115d2f3SKarsten Keil { 3326115d2f3SKarsten Keil fc->ctrlreg |= AVM_STATUS0_ENA_IRQ; 3336115d2f3SKarsten Keil outb(fc->ctrlreg, fc->addr + 2); 3346115d2f3SKarsten Keil } 3356115d2f3SKarsten Keil 3366115d2f3SKarsten Keil static void 3376115d2f3SKarsten Keil disable_hwirq(struct fritzcard *fc) 3386115d2f3SKarsten Keil { 3396115d2f3SKarsten Keil fc->ctrlreg &= ~AVM_STATUS0_ENA_IRQ; 3406115d2f3SKarsten Keil outb(fc->ctrlreg, fc->addr + 2); 3416115d2f3SKarsten Keil } 3426115d2f3SKarsten Keil 3436115d2f3SKarsten Keil static int 3446115d2f3SKarsten Keil modehdlc(struct bchannel *bch, int protocol) 3456115d2f3SKarsten Keil { 3466115d2f3SKarsten Keil struct fritzcard *fc = bch->hw; 3476115d2f3SKarsten Keil struct hdlc_hw *hdlc; 3486115d2f3SKarsten Keil 3496115d2f3SKarsten Keil hdlc = &fc->hdlc[(bch->nr - 1) & 1]; 3506115d2f3SKarsten Keil pr_debug("%s: hdlc %c protocol %x-->%x ch %d\n", fc->name, 3516115d2f3SKarsten Keil '@' + bch->nr, bch->state, protocol, bch->nr); 3526115d2f3SKarsten Keil hdlc->ctrl.ctrl = 0; 3536115d2f3SKarsten Keil switch (protocol) { 3546115d2f3SKarsten Keil case -1: /* used for init */ 3556115d2f3SKarsten Keil bch->state = -1; 3566115d2f3SKarsten Keil case ISDN_P_NONE: 3576115d2f3SKarsten Keil if (bch->state == ISDN_P_NONE) 3586115d2f3SKarsten Keil break; 3596115d2f3SKarsten Keil hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; 3606115d2f3SKarsten Keil hdlc->ctrl.sr.mode = HDLC_MODE_TRANS; 3616115d2f3SKarsten Keil write_ctrl(bch, 5); 3626115d2f3SKarsten Keil bch->state = ISDN_P_NONE; 3636115d2f3SKarsten Keil test_and_clear_bit(FLG_HDLC, &bch->Flags); 3646115d2f3SKarsten Keil test_and_clear_bit(FLG_TRANSPARENT, &bch->Flags); 3656115d2f3SKarsten Keil break; 3666115d2f3SKarsten Keil case ISDN_P_B_RAW: 3676115d2f3SKarsten Keil bch->state = protocol; 3686115d2f3SKarsten Keil hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; 3696115d2f3SKarsten Keil hdlc->ctrl.sr.mode = HDLC_MODE_TRANS; 3706115d2f3SKarsten Keil write_ctrl(bch, 5); 3716115d2f3SKarsten Keil hdlc->ctrl.sr.cmd = HDLC_CMD_XRS; 3726115d2f3SKarsten Keil write_ctrl(bch, 1); 3736115d2f3SKarsten Keil hdlc->ctrl.sr.cmd = 0; 3746115d2f3SKarsten Keil test_and_set_bit(FLG_TRANSPARENT, &bch->Flags); 3756115d2f3SKarsten Keil break; 3766115d2f3SKarsten Keil case ISDN_P_B_HDLC: 3776115d2f3SKarsten Keil bch->state = protocol; 3786115d2f3SKarsten Keil hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; 3796115d2f3SKarsten Keil hdlc->ctrl.sr.mode = HDLC_MODE_ITF_FLG; 3806115d2f3SKarsten Keil write_ctrl(bch, 5); 3816115d2f3SKarsten Keil hdlc->ctrl.sr.cmd = HDLC_CMD_XRS; 3826115d2f3SKarsten Keil write_ctrl(bch, 1); 3836115d2f3SKarsten Keil hdlc->ctrl.sr.cmd = 0; 3846115d2f3SKarsten Keil test_and_set_bit(FLG_HDLC, &bch->Flags); 3856115d2f3SKarsten Keil break; 3866115d2f3SKarsten Keil default: 3876115d2f3SKarsten Keil pr_info("%s: protocol not known %x\n", fc->name, protocol); 3886115d2f3SKarsten Keil return -ENOPROTOOPT; 3896115d2f3SKarsten Keil } 3906115d2f3SKarsten Keil return 0; 3916115d2f3SKarsten Keil } 3926115d2f3SKarsten Keil 3936115d2f3SKarsten Keil static void 3946115d2f3SKarsten Keil hdlc_empty_fifo(struct bchannel *bch, int count) 3956115d2f3SKarsten Keil { 3966115d2f3SKarsten Keil u32 *ptr; 3976115d2f3SKarsten Keil u8 *p; 3986115d2f3SKarsten Keil u32 val, addr; 3996115d2f3SKarsten Keil int cnt = 0; 4006115d2f3SKarsten Keil struct fritzcard *fc = bch->hw; 4016115d2f3SKarsten Keil 4026115d2f3SKarsten Keil pr_debug("%s: %s %d\n", fc->name, __func__, count); 4036115d2f3SKarsten Keil if (!bch->rx_skb) { 4046115d2f3SKarsten Keil bch->rx_skb = mI_alloc_skb(bch->maxlen, GFP_ATOMIC); 4056115d2f3SKarsten Keil if (!bch->rx_skb) { 4066115d2f3SKarsten Keil pr_info("%s: B receive out of memory\n", 4076115d2f3SKarsten Keil fc->name); 4086115d2f3SKarsten Keil return; 4096115d2f3SKarsten Keil } 4106115d2f3SKarsten Keil } 4116115d2f3SKarsten Keil if ((bch->rx_skb->len + count) > bch->maxlen) { 4126115d2f3SKarsten Keil pr_debug("%s: overrun %d\n", fc->name, 4136115d2f3SKarsten Keil bch->rx_skb->len + count); 4146115d2f3SKarsten Keil return; 4156115d2f3SKarsten Keil } 4166115d2f3SKarsten Keil p = skb_put(bch->rx_skb, count); 4176115d2f3SKarsten Keil ptr = (u32 *)p; 4186115d2f3SKarsten Keil if (AVM_FRITZ_PCIV2 == fc->type) 4196115d2f3SKarsten Keil addr = fc->addr + (bch->nr == 2 ? 4206115d2f3SKarsten Keil AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1); 4216115d2f3SKarsten Keil else { 4226115d2f3SKarsten Keil addr = fc->addr + CHIP_WINDOW; 4236115d2f3SKarsten Keil outl(bch->nr == 2 ? AVM_HDLC_2 : AVM_HDLC_1, fc->addr); 4246115d2f3SKarsten Keil } 4256115d2f3SKarsten Keil while (cnt < count) { 4266115d2f3SKarsten Keil val = le32_to_cpu(inl(addr)); 4276115d2f3SKarsten Keil put_unaligned(val, ptr); 4286115d2f3SKarsten Keil ptr++; 4296115d2f3SKarsten Keil cnt += 4; 4306115d2f3SKarsten Keil } 4316115d2f3SKarsten Keil if (debug & DEBUG_HW_BFIFO) { 4326115d2f3SKarsten Keil snprintf(fc->log, LOG_SIZE, "B%1d-recv %s %d ", 4336115d2f3SKarsten Keil bch->nr, fc->name, count); 4346115d2f3SKarsten Keil print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count); 4356115d2f3SKarsten Keil } 4366115d2f3SKarsten Keil } 4376115d2f3SKarsten Keil 4386115d2f3SKarsten Keil static void 4396115d2f3SKarsten Keil hdlc_fill_fifo(struct bchannel *bch) 4406115d2f3SKarsten Keil { 4416115d2f3SKarsten Keil struct fritzcard *fc = bch->hw; 4426115d2f3SKarsten Keil struct hdlc_hw *hdlc; 4436115d2f3SKarsten Keil int count, cnt = 0; 4446115d2f3SKarsten Keil u8 *p; 4456115d2f3SKarsten Keil u32 *ptr, val, addr; 4466115d2f3SKarsten Keil 4476115d2f3SKarsten Keil hdlc = &fc->hdlc[(bch->nr - 1) & 1]; 4486115d2f3SKarsten Keil if (!bch->tx_skb) 4496115d2f3SKarsten Keil return; 4506115d2f3SKarsten Keil count = bch->tx_skb->len - bch->tx_idx; 4516115d2f3SKarsten Keil if (count <= 0) 4526115d2f3SKarsten Keil return; 4536115d2f3SKarsten Keil p = bch->tx_skb->data + bch->tx_idx; 4546115d2f3SKarsten Keil hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XME; 4556115d2f3SKarsten Keil if (count > HDLC_FIFO_SIZE) { 4566115d2f3SKarsten Keil count = HDLC_FIFO_SIZE; 4576115d2f3SKarsten Keil } else { 4586115d2f3SKarsten Keil if (test_bit(FLG_HDLC, &bch->Flags)) 4596115d2f3SKarsten Keil hdlc->ctrl.sr.cmd |= HDLC_CMD_XME; 4606115d2f3SKarsten Keil } 4616115d2f3SKarsten Keil pr_debug("%s: %s %d/%d/%d", fc->name, __func__, count, 4626115d2f3SKarsten Keil bch->tx_idx, bch->tx_skb->len); 4636115d2f3SKarsten Keil ptr = (u32 *)p; 4646115d2f3SKarsten Keil bch->tx_idx += count; 4656115d2f3SKarsten Keil hdlc->ctrl.sr.xml = ((count == HDLC_FIFO_SIZE) ? 0 : count); 4666115d2f3SKarsten Keil if (AVM_FRITZ_PCIV2 == fc->type) { 4676115d2f3SKarsten Keil __write_ctrl_pciv2(fc, hdlc, bch->nr); 4686115d2f3SKarsten Keil addr = fc->addr + (bch->nr == 2 ? 4696115d2f3SKarsten Keil AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1); 4706115d2f3SKarsten Keil } else { 4716115d2f3SKarsten Keil __write_ctrl_pci(fc, hdlc, bch->nr); 4726115d2f3SKarsten Keil addr = fc->addr + CHIP_WINDOW; 4736115d2f3SKarsten Keil } 4746115d2f3SKarsten Keil while (cnt < count) { 4756115d2f3SKarsten Keil val = get_unaligned(ptr); 4766115d2f3SKarsten Keil outl(cpu_to_le32(val), addr); 4776115d2f3SKarsten Keil ptr++; 4786115d2f3SKarsten Keil cnt += 4; 4796115d2f3SKarsten Keil } 4806115d2f3SKarsten Keil if (debug & DEBUG_HW_BFIFO) { 4816115d2f3SKarsten Keil snprintf(fc->log, LOG_SIZE, "B%1d-send %s %d ", 4826115d2f3SKarsten Keil bch->nr, fc->name, count); 4836115d2f3SKarsten Keil print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count); 4846115d2f3SKarsten Keil } 4856115d2f3SKarsten Keil } 4866115d2f3SKarsten Keil 4876115d2f3SKarsten Keil static void 4886115d2f3SKarsten Keil HDLC_irq_xpr(struct bchannel *bch) 4896115d2f3SKarsten Keil { 4906115d2f3SKarsten Keil if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len) 4916115d2f3SKarsten Keil hdlc_fill_fifo(bch); 4926115d2f3SKarsten Keil else { 4936115d2f3SKarsten Keil if (bch->tx_skb) { 4946115d2f3SKarsten Keil /* send confirm, on trans, free on hdlc. */ 4956115d2f3SKarsten Keil if (test_bit(FLG_TRANSPARENT, &bch->Flags)) 4966115d2f3SKarsten Keil confirm_Bsend(bch); 4976115d2f3SKarsten Keil dev_kfree_skb(bch->tx_skb); 4986115d2f3SKarsten Keil } 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; 5086115d2f3SKarsten Keil int len; 5096115d2f3SKarsten Keil struct hdlc_hw *hdlc; 5106115d2f3SKarsten Keil 5116115d2f3SKarsten Keil hdlc = &fc->hdlc[(bch->nr - 1) & 1]; 5126115d2f3SKarsten Keil pr_debug("%s: ch%d stat %#x\n", fc->name, bch->nr, stat); 5136115d2f3SKarsten Keil if (stat & HDLC_INT_RPR) { 5146115d2f3SKarsten Keil if (stat & HDLC_STAT_RDO) { 5156115d2f3SKarsten Keil hdlc->ctrl.sr.xml = 0; 5166115d2f3SKarsten Keil hdlc->ctrl.sr.cmd |= HDLC_CMD_RRS; 5176115d2f3SKarsten Keil write_ctrl(bch, 1); 5186115d2f3SKarsten Keil hdlc->ctrl.sr.cmd &= ~HDLC_CMD_RRS; 5196115d2f3SKarsten Keil write_ctrl(bch, 1); 5206115d2f3SKarsten Keil if (bch->rx_skb) 5216115d2f3SKarsten Keil skb_trim(bch->rx_skb, 0); 5226115d2f3SKarsten Keil } else { 5236115d2f3SKarsten Keil len = (stat & HDLC_STAT_RML_MASK) >> 8; 5246115d2f3SKarsten Keil if (!len) 5256115d2f3SKarsten Keil len = 32; 5266115d2f3SKarsten Keil hdlc_empty_fifo(bch, len); 5276115d2f3SKarsten Keil if (!bch->rx_skb) 5286115d2f3SKarsten Keil goto handle_tx; 5296115d2f3SKarsten Keil if ((stat & HDLC_STAT_RME) || test_bit(FLG_TRANSPARENT, 5306115d2f3SKarsten Keil &bch->Flags)) { 5316115d2f3SKarsten Keil if (((stat & HDLC_STAT_CRCVFRRAB) == 5326115d2f3SKarsten Keil HDLC_STAT_CRCVFR) || 5336115d2f3SKarsten Keil test_bit(FLG_TRANSPARENT, &bch->Flags)) { 5346115d2f3SKarsten Keil recv_Bchannel(bch, 0); 5356115d2f3SKarsten Keil } else { 5366115d2f3SKarsten Keil pr_debug("%s: got invalid frame\n", 5376115d2f3SKarsten Keil fc->name); 5386115d2f3SKarsten Keil skb_trim(bch->rx_skb, 0); 5396115d2f3SKarsten Keil } 5406115d2f3SKarsten Keil } 5416115d2f3SKarsten Keil } 5426115d2f3SKarsten Keil } 5436115d2f3SKarsten Keil handle_tx: 5446115d2f3SKarsten Keil if (stat & HDLC_INT_XDU) { 5456115d2f3SKarsten Keil /* Here we lost an TX interrupt, so 5466115d2f3SKarsten Keil * restart transmitting the whole frame on HDLC 5476115d2f3SKarsten Keil * in transparent mode we send the next data 5486115d2f3SKarsten Keil */ 5496115d2f3SKarsten Keil if (bch->tx_skb) 5506115d2f3SKarsten Keil pr_debug("%s: ch%d XDU len(%d) idx(%d) Flags(%lx)\n", 5516115d2f3SKarsten Keil fc->name, bch->nr, bch->tx_skb->len, 5526115d2f3SKarsten Keil bch->tx_idx, bch->Flags); 5536115d2f3SKarsten Keil else 5546115d2f3SKarsten Keil pr_debug("%s: ch%d XDU no tx_skb Flags(%lx)\n", 5556115d2f3SKarsten Keil fc->name, bch->nr, bch->Flags); 5566115d2f3SKarsten Keil if (bch->tx_skb && bch->tx_skb->len) { 5576115d2f3SKarsten Keil if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) 5586115d2f3SKarsten Keil bch->tx_idx = 0; 5596115d2f3SKarsten Keil } 5606115d2f3SKarsten Keil hdlc->ctrl.sr.xml = 0; 5616115d2f3SKarsten Keil hdlc->ctrl.sr.cmd |= HDLC_CMD_XRS; 5626115d2f3SKarsten Keil write_ctrl(bch, 1); 5636115d2f3SKarsten Keil hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XRS; 5646115d2f3SKarsten Keil HDLC_irq_xpr(bch); 5656115d2f3SKarsten Keil return; 5666115d2f3SKarsten Keil } else if (stat & HDLC_INT_XPR) 5676115d2f3SKarsten Keil HDLC_irq_xpr(bch); 5686115d2f3SKarsten Keil } 5696115d2f3SKarsten Keil 5706115d2f3SKarsten Keil static inline void 5716115d2f3SKarsten Keil HDLC_irq_main(struct fritzcard *fc) 5726115d2f3SKarsten Keil { 5736115d2f3SKarsten Keil u32 stat; 5746115d2f3SKarsten Keil struct bchannel *bch; 5756115d2f3SKarsten Keil 5766115d2f3SKarsten Keil stat = read_status(fc, 1); 5776115d2f3SKarsten Keil if (stat & HDLC_INT_MASK) { 5786115d2f3SKarsten Keil bch = Sel_BCS(fc, 1); 5796115d2f3SKarsten Keil if (bch) 5806115d2f3SKarsten Keil HDLC_irq(bch, stat); 5816115d2f3SKarsten Keil else 5826115d2f3SKarsten Keil pr_debug("%s: spurious ch1 IRQ\n", fc->name); 5836115d2f3SKarsten Keil } 5846115d2f3SKarsten Keil stat = read_status(fc, 2); 5856115d2f3SKarsten Keil if (stat & HDLC_INT_MASK) { 5866115d2f3SKarsten Keil bch = Sel_BCS(fc, 2); 5876115d2f3SKarsten Keil if (bch) 5886115d2f3SKarsten Keil HDLC_irq(bch, stat); 5896115d2f3SKarsten Keil else 5906115d2f3SKarsten Keil pr_debug("%s: spurious ch2 IRQ\n", fc->name); 5916115d2f3SKarsten Keil } 5926115d2f3SKarsten Keil } 5936115d2f3SKarsten Keil 5946115d2f3SKarsten Keil static irqreturn_t 5956115d2f3SKarsten Keil avm_fritz_interrupt(int intno, void *dev_id) 5966115d2f3SKarsten Keil { 5976115d2f3SKarsten Keil struct fritzcard *fc = dev_id; 5986115d2f3SKarsten Keil u8 val; 5996115d2f3SKarsten Keil u8 sval; 6006115d2f3SKarsten Keil 6016115d2f3SKarsten Keil spin_lock(&fc->lock); 6026115d2f3SKarsten Keil sval = inb(fc->addr + 2); 6036115d2f3SKarsten Keil pr_debug("%s: irq stat0 %x\n", fc->name, sval); 6046115d2f3SKarsten Keil if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK) { 6056115d2f3SKarsten Keil /* shared IRQ from other HW */ 6066115d2f3SKarsten Keil spin_unlock(&fc->lock); 6076115d2f3SKarsten Keil return IRQ_NONE; 6086115d2f3SKarsten Keil } 6096115d2f3SKarsten Keil fc->irqcnt++; 6106115d2f3SKarsten Keil 6116115d2f3SKarsten Keil if (!(sval & AVM_STATUS0_IRQ_ISAC)) { 6126115d2f3SKarsten Keil val = ReadISAC_V1(fc, ISAC_ISTA); 6136115d2f3SKarsten Keil mISDNisac_irq(&fc->isac, val); 6146115d2f3SKarsten Keil } 6156115d2f3SKarsten Keil if (!(sval & AVM_STATUS0_IRQ_HDLC)) 6166115d2f3SKarsten Keil HDLC_irq_main(fc); 6176115d2f3SKarsten Keil spin_unlock(&fc->lock); 6186115d2f3SKarsten Keil return IRQ_HANDLED; 6196115d2f3SKarsten Keil } 6206115d2f3SKarsten Keil 6216115d2f3SKarsten Keil static irqreturn_t 6226115d2f3SKarsten Keil avm_fritzv2_interrupt(int intno, void *dev_id) 6236115d2f3SKarsten Keil { 6246115d2f3SKarsten Keil struct fritzcard *fc = dev_id; 6256115d2f3SKarsten Keil u8 val; 6266115d2f3SKarsten Keil u8 sval; 6276115d2f3SKarsten Keil 6286115d2f3SKarsten Keil spin_lock(&fc->lock); 6296115d2f3SKarsten Keil sval = inb(fc->addr + 2); 6306115d2f3SKarsten Keil pr_debug("%s: irq stat0 %x\n", fc->name, sval); 6316115d2f3SKarsten Keil if (!(sval & AVM_STATUS0_IRQ_MASK)) { 6326115d2f3SKarsten Keil /* shared IRQ from other HW */ 6336115d2f3SKarsten Keil spin_unlock(&fc->lock); 6346115d2f3SKarsten Keil return IRQ_NONE; 6356115d2f3SKarsten Keil } 6366115d2f3SKarsten Keil fc->irqcnt++; 6376115d2f3SKarsten Keil 6386115d2f3SKarsten Keil if (sval & AVM_STATUS0_IRQ_HDLC) 6396115d2f3SKarsten Keil HDLC_irq_main(fc); 6406115d2f3SKarsten Keil if (sval & AVM_STATUS0_IRQ_ISAC) { 6416115d2f3SKarsten Keil val = ReadISAC_V2(fc, ISACX_ISTA); 6426115d2f3SKarsten Keil mISDNisac_irq(&fc->isac, val); 6436115d2f3SKarsten Keil } 6446115d2f3SKarsten Keil if (sval & AVM_STATUS0_IRQ_TIMER) { 6456115d2f3SKarsten Keil pr_debug("%s: timer irq\n", fc->name); 6466115d2f3SKarsten Keil outb(fc->ctrlreg | AVM_STATUS0_RES_TIMER, fc->addr + 2); 6476115d2f3SKarsten Keil udelay(1); 6486115d2f3SKarsten Keil outb(fc->ctrlreg, fc->addr + 2); 6496115d2f3SKarsten Keil } 6506115d2f3SKarsten Keil spin_unlock(&fc->lock); 6516115d2f3SKarsten Keil return IRQ_HANDLED; 6526115d2f3SKarsten Keil } 6536115d2f3SKarsten Keil 6546115d2f3SKarsten Keil static int 6556115d2f3SKarsten Keil avm_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) 6566115d2f3SKarsten Keil { 6576115d2f3SKarsten Keil struct bchannel *bch = container_of(ch, struct bchannel, ch); 6586115d2f3SKarsten Keil struct fritzcard *fc = bch->hw; 6596115d2f3SKarsten Keil int ret = -EINVAL; 6606115d2f3SKarsten Keil struct mISDNhead *hh = mISDN_HEAD_P(skb); 6616115d2f3SKarsten Keil u32 id; 6626115d2f3SKarsten Keil u_long flags; 6636115d2f3SKarsten Keil 6646115d2f3SKarsten Keil switch (hh->prim) { 6656115d2f3SKarsten Keil case PH_DATA_REQ: 6666115d2f3SKarsten Keil spin_lock_irqsave(&fc->lock, flags); 6676115d2f3SKarsten Keil ret = bchannel_senddata(bch, skb); 6686115d2f3SKarsten Keil if (ret > 0) { /* direct TX */ 6696115d2f3SKarsten Keil id = hh->id; /* skb can be freed */ 6706115d2f3SKarsten Keil hdlc_fill_fifo(bch); 6716115d2f3SKarsten Keil ret = 0; 6726115d2f3SKarsten Keil spin_unlock_irqrestore(&fc->lock, flags); 6736115d2f3SKarsten Keil if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) 6746115d2f3SKarsten Keil queue_ch_frame(ch, PH_DATA_CNF, id, NULL); 6756115d2f3SKarsten Keil } else 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 */ 7856115d2f3SKarsten Keil if (AVM_FRITZ_PCIV2 == fc->type) { 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 if (test_bit(FLG_ACTIVE, &bch->Flags)) { 8426115d2f3SKarsten Keil spin_lock_irqsave(&fc->lock, flags); 8436115d2f3SKarsten Keil mISDN_freebchannel(bch); 8446115d2f3SKarsten Keil test_and_clear_bit(FLG_TX_BUSY, &bch->Flags); 8456115d2f3SKarsten Keil test_and_clear_bit(FLG_ACTIVE, &bch->Flags); 8466115d2f3SKarsten Keil modehdlc(bch, ISDN_P_NONE); 8476115d2f3SKarsten Keil spin_unlock_irqrestore(&fc->lock, flags); 8486115d2f3SKarsten Keil } 8496115d2f3SKarsten Keil ch->protocol = ISDN_P_NONE; 8506115d2f3SKarsten Keil ch->peer = NULL; 8516115d2f3SKarsten Keil module_put(THIS_MODULE); 8526115d2f3SKarsten Keil ret = 0; 8536115d2f3SKarsten Keil break; 8546115d2f3SKarsten Keil case CONTROL_CHANNEL: 8556115d2f3SKarsten Keil ret = channel_bctrl(bch, arg); 8566115d2f3SKarsten Keil break; 8576115d2f3SKarsten Keil default: 8586115d2f3SKarsten Keil pr_info("%s: %s unknown prim(%x)\n", fc->name, __func__, cmd); 8596115d2f3SKarsten Keil } 8606115d2f3SKarsten Keil return ret; 8616115d2f3SKarsten Keil } 8626115d2f3SKarsten Keil 8636115d2f3SKarsten Keil static int 8646115d2f3SKarsten Keil channel_ctrl(struct fritzcard *fc, struct mISDN_ctrl_req *cq) 8656115d2f3SKarsten Keil { 8666115d2f3SKarsten Keil int ret = 0; 8676115d2f3SKarsten Keil 8686115d2f3SKarsten Keil switch (cq->op) { 8696115d2f3SKarsten Keil case MISDN_CTRL_GETOP: 8706115d2f3SKarsten Keil cq->op = MISDN_CTRL_LOOP; 8716115d2f3SKarsten Keil break; 8726115d2f3SKarsten Keil case MISDN_CTRL_LOOP: 8736115d2f3SKarsten Keil /* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */ 8746115d2f3SKarsten Keil if (cq->channel < 0 || cq->channel > 3) { 8756115d2f3SKarsten Keil ret = -EINVAL; 8766115d2f3SKarsten Keil break; 8776115d2f3SKarsten Keil } 8786115d2f3SKarsten Keil ret = fc->isac.ctrl(&fc->isac, HW_TESTLOOP, cq->channel); 8796115d2f3SKarsten Keil break; 8806115d2f3SKarsten Keil default: 8816115d2f3SKarsten Keil pr_info("%s: %s unknown Op %x\n", fc->name, __func__, cq->op); 8826115d2f3SKarsten Keil ret = -EINVAL; 8836115d2f3SKarsten Keil break; 8846115d2f3SKarsten Keil } 8856115d2f3SKarsten Keil return ret; 8866115d2f3SKarsten Keil } 8876115d2f3SKarsten Keil 8886115d2f3SKarsten Keil static int 8896115d2f3SKarsten Keil open_bchannel(struct fritzcard *fc, struct channel_req *rq) 8906115d2f3SKarsten Keil { 8916115d2f3SKarsten Keil struct bchannel *bch; 8926115d2f3SKarsten Keil 8936115d2f3SKarsten Keil if (rq->adr.channel > 2) 8946115d2f3SKarsten Keil return -EINVAL; 8956115d2f3SKarsten Keil if (rq->protocol == ISDN_P_NONE) 8966115d2f3SKarsten Keil return -EINVAL; 8976115d2f3SKarsten Keil bch = &fc->bch[rq->adr.channel - 1]; 8986115d2f3SKarsten Keil if (test_and_set_bit(FLG_OPEN, &bch->Flags)) 8996115d2f3SKarsten Keil return -EBUSY; /* b-channel can be only open once */ 9006115d2f3SKarsten Keil test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags); 9016115d2f3SKarsten Keil bch->ch.protocol = rq->protocol; 9026115d2f3SKarsten Keil rq->ch = &bch->ch; 9036115d2f3SKarsten Keil return 0; 9046115d2f3SKarsten Keil } 9056115d2f3SKarsten Keil 9066115d2f3SKarsten Keil /* 9076115d2f3SKarsten Keil * device control function 9086115d2f3SKarsten Keil */ 9096115d2f3SKarsten Keil static int 9106115d2f3SKarsten Keil avm_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) 9116115d2f3SKarsten Keil { 9126115d2f3SKarsten Keil struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); 9136115d2f3SKarsten Keil struct dchannel *dch = container_of(dev, struct dchannel, dev); 9146115d2f3SKarsten Keil struct fritzcard *fc = dch->hw; 9156115d2f3SKarsten Keil struct channel_req *rq; 9166115d2f3SKarsten Keil int err = 0; 9176115d2f3SKarsten Keil 9186115d2f3SKarsten Keil pr_debug("%s: %s cmd:%x %p\n", fc->name, __func__, cmd, arg); 9196115d2f3SKarsten Keil switch (cmd) { 9206115d2f3SKarsten Keil case OPEN_CHANNEL: 9216115d2f3SKarsten Keil rq = arg; 9226115d2f3SKarsten Keil if (rq->protocol == ISDN_P_TE_S0) 9236115d2f3SKarsten Keil err = fc->isac.open(&fc->isac, rq); 9246115d2f3SKarsten Keil else 9256115d2f3SKarsten Keil err = open_bchannel(fc, rq); 9266115d2f3SKarsten Keil if (err) 9276115d2f3SKarsten Keil break; 9286115d2f3SKarsten Keil if (!try_module_get(THIS_MODULE)) 9296115d2f3SKarsten Keil pr_info("%s: cannot get module\n", fc->name); 9306115d2f3SKarsten Keil break; 9316115d2f3SKarsten Keil case CLOSE_CHANNEL: 9326115d2f3SKarsten Keil pr_debug("%s: dev(%d) close from %p\n", fc->name, dch->dev.id, 9336115d2f3SKarsten Keil __builtin_return_address(0)); 9346115d2f3SKarsten Keil module_put(THIS_MODULE); 9356115d2f3SKarsten Keil break; 9366115d2f3SKarsten Keil case CONTROL_CHANNEL: 9376115d2f3SKarsten Keil err = channel_ctrl(fc, arg); 9386115d2f3SKarsten Keil break; 9396115d2f3SKarsten Keil default: 9406115d2f3SKarsten Keil pr_debug("%s: %s unknown command %x\n", 9416115d2f3SKarsten Keil fc->name, __func__, cmd); 9426115d2f3SKarsten Keil return -EINVAL; 9436115d2f3SKarsten Keil } 9446115d2f3SKarsten Keil return err; 9456115d2f3SKarsten Keil } 9466115d2f3SKarsten Keil 9476115d2f3SKarsten Keil int 9486115d2f3SKarsten Keil setup_fritz(struct fritzcard *fc) 9496115d2f3SKarsten Keil { 9506115d2f3SKarsten Keil u32 val, ver; 9516115d2f3SKarsten Keil 9526115d2f3SKarsten Keil if (!request_region(fc->addr, 32, fc->name)) { 9536115d2f3SKarsten Keil pr_info("%s: AVM config port %x-%x already in use\n", 9546115d2f3SKarsten Keil fc->name, fc->addr, fc->addr + 31); 9556115d2f3SKarsten Keil return -EIO; 9566115d2f3SKarsten Keil } 9576115d2f3SKarsten Keil switch (fc->type) { 9586115d2f3SKarsten Keil case AVM_FRITZ_PCI: 9596115d2f3SKarsten Keil val = inl(fc->addr); 9606115d2f3SKarsten Keil outl(AVM_HDLC_1, fc->addr + CHIP_INDEX); 9616115d2f3SKarsten Keil ver = inl(fc->addr + CHIP_WINDOW + HDLC_STATUS) >> 24; 9626115d2f3SKarsten Keil if (debug & DEBUG_HW) { 9636115d2f3SKarsten Keil pr_notice("%s: PCI stat %#x\n", fc->name, val); 9646115d2f3SKarsten Keil pr_notice("%s: PCI Class %X Rev %d\n", fc->name, 9656115d2f3SKarsten Keil val & 0xff, (val >> 8) & 0xff); 9666115d2f3SKarsten Keil pr_notice("%s: HDLC version %x\n", fc->name, ver & 0xf); 9676115d2f3SKarsten Keil } 9686115d2f3SKarsten Keil ASSIGN_FUNC(V1, ISAC, fc->isac); 9696115d2f3SKarsten Keil fc->isac.type = IPAC_TYPE_ISAC; 9706115d2f3SKarsten Keil break; 9716115d2f3SKarsten Keil case AVM_FRITZ_PCIV2: 9726115d2f3SKarsten Keil val = inl(fc->addr); 9736115d2f3SKarsten Keil ver = inl(fc->addr + AVM_HDLC_STATUS_1) >> 24; 9746115d2f3SKarsten Keil if (debug & DEBUG_HW) { 9756115d2f3SKarsten Keil pr_notice("%s: PCI V2 stat %#x\n", fc->name, val); 9766115d2f3SKarsten Keil pr_notice("%s: PCI V2 Class %X Rev %d\n", fc->name, 9776115d2f3SKarsten Keil val & 0xff, (val>>8) & 0xff); 9786115d2f3SKarsten Keil pr_notice("%s: HDLC version %x\n", fc->name, ver & 0xf); 9796115d2f3SKarsten Keil } 9806115d2f3SKarsten Keil ASSIGN_FUNC(V2, ISAC, fc->isac); 9816115d2f3SKarsten Keil fc->isac.type = IPAC_TYPE_ISACX; 9826115d2f3SKarsten Keil break; 9836115d2f3SKarsten Keil default: 9846115d2f3SKarsten Keil release_region(fc->addr, 32); 9856115d2f3SKarsten Keil pr_info("%s: AVM unknown type %d\n", fc->name, fc->type); 9866115d2f3SKarsten Keil return -ENODEV; 9876115d2f3SKarsten Keil } 9886115d2f3SKarsten Keil pr_notice("%s: %s config irq:%d base:0x%X\n", fc->name, 9896115d2f3SKarsten Keil (fc->type == AVM_FRITZ_PCI) ? "AVM Fritz!CARD PCI" : 9906115d2f3SKarsten Keil "AVM Fritz!CARD PCIv2", fc->irq, fc->addr); 9916115d2f3SKarsten Keil return 0; 9926115d2f3SKarsten Keil } 9936115d2f3SKarsten Keil 9946115d2f3SKarsten Keil static void 9956115d2f3SKarsten Keil release_card(struct fritzcard *card) 9966115d2f3SKarsten Keil { 9976115d2f3SKarsten Keil u_long flags; 9986115d2f3SKarsten Keil 9996115d2f3SKarsten Keil disable_hwirq(card); 10006115d2f3SKarsten Keil spin_lock_irqsave(&card->lock, flags); 10016115d2f3SKarsten Keil modehdlc(&card->bch[0], ISDN_P_NONE); 10026115d2f3SKarsten Keil modehdlc(&card->bch[1], ISDN_P_NONE); 10036115d2f3SKarsten Keil spin_unlock_irqrestore(&card->lock, flags); 10046115d2f3SKarsten Keil card->isac.release(&card->isac); 10056115d2f3SKarsten Keil free_irq(card->irq, card); 10066115d2f3SKarsten Keil mISDN_freebchannel(&card->bch[1]); 10076115d2f3SKarsten Keil mISDN_freebchannel(&card->bch[0]); 10086115d2f3SKarsten Keil mISDN_unregister_device(&card->isac.dch.dev); 10096115d2f3SKarsten Keil release_region(card->addr, 32); 10106115d2f3SKarsten Keil pci_disable_device(card->pdev); 10116115d2f3SKarsten Keil pci_set_drvdata(card->pdev, NULL); 10126115d2f3SKarsten Keil write_lock_irqsave(&card_lock, flags); 10136115d2f3SKarsten Keil list_del(&card->list); 10146115d2f3SKarsten Keil write_unlock_irqrestore(&card_lock, flags); 10156115d2f3SKarsten Keil kfree(card); 10166115d2f3SKarsten Keil AVM_cnt--; 10176115d2f3SKarsten Keil } 10186115d2f3SKarsten Keil 10196115d2f3SKarsten Keil static int __devinit 10206115d2f3SKarsten Keil setup_instance(struct fritzcard *card) 10216115d2f3SKarsten Keil { 10226115d2f3SKarsten Keil int i, err; 10236115d2f3SKarsten Keil u_long flags; 10246115d2f3SKarsten Keil 10256115d2f3SKarsten Keil snprintf(card->name, MISDN_MAX_IDLEN - 1, "AVM.%d", AVM_cnt + 1); 10266115d2f3SKarsten Keil write_lock_irqsave(&card_lock, flags); 10276115d2f3SKarsten Keil list_add_tail(&card->list, &Cards); 10286115d2f3SKarsten Keil write_unlock_irqrestore(&card_lock, flags); 10296115d2f3SKarsten Keil 10306115d2f3SKarsten Keil _set_debug(card); 10316115d2f3SKarsten Keil card->isac.name = card->name; 10326115d2f3SKarsten Keil spin_lock_init(&card->lock); 10336115d2f3SKarsten Keil card->isac.hwlock = &card->lock; 10346115d2f3SKarsten Keil mISDNisac_init(&card->isac, card); 10356115d2f3SKarsten Keil 10366115d2f3SKarsten Keil card->isac.dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | 10376115d2f3SKarsten Keil (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); 10386115d2f3SKarsten Keil card->isac.dch.dev.D.ctrl = avm_dctrl; 10396115d2f3SKarsten Keil for (i = 0; i < 2; i++) { 10406115d2f3SKarsten Keil card->bch[i].nr = i + 1; 10416115d2f3SKarsten Keil set_channelmap(i + 1, card->isac.dch.dev.channelmap); 10426115d2f3SKarsten Keil mISDN_initbchannel(&card->bch[i], MAX_DATA_MEM); 10436115d2f3SKarsten Keil card->bch[i].hw = card; 10446115d2f3SKarsten Keil card->bch[i].ch.send = avm_l2l1B; 10456115d2f3SKarsten Keil card->bch[i].ch.ctrl = avm_bctrl; 10466115d2f3SKarsten Keil card->bch[i].ch.nr = i + 1; 10476115d2f3SKarsten Keil list_add(&card->bch[i].ch.list, &card->isac.dch.dev.bchannels); 10486115d2f3SKarsten Keil } 10496115d2f3SKarsten Keil err = setup_fritz(card); 10506115d2f3SKarsten Keil if (err) 10516115d2f3SKarsten Keil goto error; 10526115d2f3SKarsten Keil err = mISDN_register_device(&card->isac.dch.dev, &card->pdev->dev, 10536115d2f3SKarsten Keil card->name); 10546115d2f3SKarsten Keil if (err) 10556115d2f3SKarsten Keil goto error_reg; 10566115d2f3SKarsten Keil err = init_card(card); 10576115d2f3SKarsten Keil if (!err) { 10586115d2f3SKarsten Keil AVM_cnt++; 10596115d2f3SKarsten Keil pr_notice("AVM %d cards installed DEBUG\n", AVM_cnt); 10606115d2f3SKarsten Keil return 0; 10616115d2f3SKarsten Keil } 10626115d2f3SKarsten Keil mISDN_unregister_device(&card->isac.dch.dev); 10636115d2f3SKarsten Keil error_reg: 10646115d2f3SKarsten Keil release_region(card->addr, 32); 10656115d2f3SKarsten Keil error: 10666115d2f3SKarsten Keil card->isac.release(&card->isac); 10676115d2f3SKarsten Keil mISDN_freebchannel(&card->bch[1]); 10686115d2f3SKarsten Keil mISDN_freebchannel(&card->bch[0]); 10696115d2f3SKarsten Keil write_lock_irqsave(&card_lock, flags); 10706115d2f3SKarsten Keil list_del(&card->list); 10716115d2f3SKarsten Keil write_unlock_irqrestore(&card_lock, flags); 10726115d2f3SKarsten Keil kfree(card); 10736115d2f3SKarsten Keil return err; 10746115d2f3SKarsten Keil } 10756115d2f3SKarsten Keil 10766115d2f3SKarsten Keil static int __devinit 10776115d2f3SKarsten Keil fritzpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 10786115d2f3SKarsten Keil { 10796115d2f3SKarsten Keil int err = -ENOMEM; 10806115d2f3SKarsten Keil struct fritzcard *card; 10816115d2f3SKarsten Keil 10826115d2f3SKarsten Keil card = kzalloc(sizeof(struct fritzcard), GFP_KERNEL); 10836115d2f3SKarsten Keil if (!card) { 10846115d2f3SKarsten Keil pr_info("No kmem for fritzcard\n"); 10856115d2f3SKarsten Keil return err; 10866115d2f3SKarsten Keil } 10876115d2f3SKarsten Keil if (pdev->device == PCI_DEVICE_ID_AVM_A1_V2) 10886115d2f3SKarsten Keil card->type = AVM_FRITZ_PCIV2; 10896115d2f3SKarsten Keil else 10906115d2f3SKarsten Keil card->type = AVM_FRITZ_PCI; 10916115d2f3SKarsten Keil card->pdev = pdev; 10926115d2f3SKarsten Keil err = pci_enable_device(pdev); 10936115d2f3SKarsten Keil if (err) { 10946115d2f3SKarsten Keil kfree(card); 10956115d2f3SKarsten Keil return err; 10966115d2f3SKarsten Keil } 10976115d2f3SKarsten Keil 10986115d2f3SKarsten Keil pr_notice("mISDN: found adapter %s at %s\n", 10996115d2f3SKarsten Keil (char *) ent->driver_data, pci_name(pdev)); 11006115d2f3SKarsten Keil 11016115d2f3SKarsten Keil card->addr = pci_resource_start(pdev, 1); 11026115d2f3SKarsten Keil card->irq = pdev->irq; 11036115d2f3SKarsten Keil pci_set_drvdata(pdev, card); 11046115d2f3SKarsten Keil err = setup_instance(card); 11056115d2f3SKarsten Keil if (err) 11066115d2f3SKarsten Keil pci_set_drvdata(pdev, NULL); 11076115d2f3SKarsten Keil return err; 11086115d2f3SKarsten Keil } 11096115d2f3SKarsten Keil 11106115d2f3SKarsten Keil static void __devexit 11116115d2f3SKarsten Keil fritz_remove_pci(struct pci_dev *pdev) 11126115d2f3SKarsten Keil { 11136115d2f3SKarsten Keil struct fritzcard *card = pci_get_drvdata(pdev); 11146115d2f3SKarsten Keil 11156115d2f3SKarsten Keil if (card) 11166115d2f3SKarsten Keil release_card(card); 11176115d2f3SKarsten Keil else 11186115d2f3SKarsten Keil if (debug) 11196115d2f3SKarsten Keil pr_info("%s: drvdata allready removed\n", __func__); 11206115d2f3SKarsten Keil } 11216115d2f3SKarsten Keil 11226115d2f3SKarsten Keil static struct pci_device_id fcpci_ids[] __devinitdata = { 11236115d2f3SKarsten Keil { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1, PCI_ANY_ID, PCI_ANY_ID, 11246115d2f3SKarsten Keil 0, 0, (unsigned long) "Fritz!Card PCI"}, 11256115d2f3SKarsten Keil { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1_V2, PCI_ANY_ID, PCI_ANY_ID, 11266115d2f3SKarsten Keil 0, 0, (unsigned long) "Fritz!Card PCI v2" }, 11276115d2f3SKarsten Keil { } 11286115d2f3SKarsten Keil }; 11296115d2f3SKarsten Keil MODULE_DEVICE_TABLE(pci, fcpci_ids); 11306115d2f3SKarsten Keil 11316115d2f3SKarsten Keil static struct pci_driver fcpci_driver = { 11326115d2f3SKarsten Keil .name = "fcpci", 11336115d2f3SKarsten Keil .probe = fritzpci_probe, 11346115d2f3SKarsten Keil .remove = __devexit_p(fritz_remove_pci), 11356115d2f3SKarsten Keil .id_table = fcpci_ids, 11366115d2f3SKarsten Keil }; 11376115d2f3SKarsten Keil 11386115d2f3SKarsten Keil static int __init AVM_init(void) 11396115d2f3SKarsten Keil { 11406115d2f3SKarsten Keil int err; 11416115d2f3SKarsten Keil 11426115d2f3SKarsten Keil pr_notice("AVM Fritz PCI driver Rev. %s\n", AVMFRITZ_REV); 11436115d2f3SKarsten Keil err = pci_register_driver(&fcpci_driver); 11446115d2f3SKarsten Keil return err; 11456115d2f3SKarsten Keil } 11466115d2f3SKarsten Keil 11476115d2f3SKarsten Keil static void __exit AVM_cleanup(void) 11486115d2f3SKarsten Keil { 11496115d2f3SKarsten Keil pci_unregister_driver(&fcpci_driver); 11506115d2f3SKarsten Keil } 11516115d2f3SKarsten Keil 11526115d2f3SKarsten Keil module_init(AVM_init); 11536115d2f3SKarsten Keil module_exit(AVM_cleanup); 1154