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