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