162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * avm_fritz.c low level stuff for AVM FRITZ!CARD PCI ISDN cards 462306a36Sopenharmony_ci * Thanks to AVM, Berlin for informations 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Author Karsten Keil <keil@isdn4linux.de> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright 2009 by Karsten Keil <keil@isdn4linux.de> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <linux/module.h> 1262306a36Sopenharmony_ci#include <linux/pci.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci#include <linux/mISDNhw.h> 1562306a36Sopenharmony_ci#include <linux/slab.h> 1662306a36Sopenharmony_ci#include <asm/unaligned.h> 1762306a36Sopenharmony_ci#include "ipac.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define AVMFRITZ_REV "2.3" 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistatic int AVM_cnt; 2362306a36Sopenharmony_cistatic int debug; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_cienum { 2662306a36Sopenharmony_ci AVM_FRITZ_PCI, 2762306a36Sopenharmony_ci AVM_FRITZ_PCIV2, 2862306a36Sopenharmony_ci}; 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define HDLC_FIFO 0x0 3162306a36Sopenharmony_ci#define HDLC_STATUS 0x4 3262306a36Sopenharmony_ci#define CHIP_WINDOW 0x10 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#define CHIP_INDEX 0x4 3562306a36Sopenharmony_ci#define AVM_HDLC_1 0x00 3662306a36Sopenharmony_ci#define AVM_HDLC_2 0x01 3762306a36Sopenharmony_ci#define AVM_ISAC_FIFO 0x02 3862306a36Sopenharmony_ci#define AVM_ISAC_REG_LOW 0x04 3962306a36Sopenharmony_ci#define AVM_ISAC_REG_HIGH 0x06 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci#define AVM_STATUS0_IRQ_ISAC 0x01 4262306a36Sopenharmony_ci#define AVM_STATUS0_IRQ_HDLC 0x02 4362306a36Sopenharmony_ci#define AVM_STATUS0_IRQ_TIMER 0x04 4462306a36Sopenharmony_ci#define AVM_STATUS0_IRQ_MASK 0x07 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define AVM_STATUS0_RESET 0x01 4762306a36Sopenharmony_ci#define AVM_STATUS0_DIS_TIMER 0x02 4862306a36Sopenharmony_ci#define AVM_STATUS0_RES_TIMER 0x04 4962306a36Sopenharmony_ci#define AVM_STATUS0_ENA_IRQ 0x08 5062306a36Sopenharmony_ci#define AVM_STATUS0_TESTBIT 0x10 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define AVM_STATUS1_INT_SEL 0x0f 5362306a36Sopenharmony_ci#define AVM_STATUS1_ENA_IOM 0x80 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#define HDLC_MODE_ITF_FLG 0x01 5662306a36Sopenharmony_ci#define HDLC_MODE_TRANS 0x02 5762306a36Sopenharmony_ci#define HDLC_MODE_CCR_7 0x04 5862306a36Sopenharmony_ci#define HDLC_MODE_CCR_16 0x08 5962306a36Sopenharmony_ci#define HDLC_FIFO_SIZE_128 0x20 6062306a36Sopenharmony_ci#define HDLC_MODE_TESTLOOP 0x80 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define HDLC_INT_XPR 0x80 6362306a36Sopenharmony_ci#define HDLC_INT_XDU 0x40 6462306a36Sopenharmony_ci#define HDLC_INT_RPR 0x20 6562306a36Sopenharmony_ci#define HDLC_INT_MASK 0xE0 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define HDLC_STAT_RME 0x01 6862306a36Sopenharmony_ci#define HDLC_STAT_RDO 0x10 6962306a36Sopenharmony_ci#define HDLC_STAT_CRCVFRRAB 0x0E 7062306a36Sopenharmony_ci#define HDLC_STAT_CRCVFR 0x06 7162306a36Sopenharmony_ci#define HDLC_STAT_RML_MASK_V1 0x3f00 7262306a36Sopenharmony_ci#define HDLC_STAT_RML_MASK_V2 0x7f00 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci#define HDLC_CMD_XRS 0x80 7562306a36Sopenharmony_ci#define HDLC_CMD_XME 0x01 7662306a36Sopenharmony_ci#define HDLC_CMD_RRS 0x20 7762306a36Sopenharmony_ci#define HDLC_CMD_XML_MASK 0x3f00 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#define HDLC_FIFO_SIZE_V1 32 8062306a36Sopenharmony_ci#define HDLC_FIFO_SIZE_V2 128 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci/* Fritz PCI v2.0 */ 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define AVM_HDLC_FIFO_1 0x10 8562306a36Sopenharmony_ci#define AVM_HDLC_FIFO_2 0x18 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define AVM_HDLC_STATUS_1 0x14 8862306a36Sopenharmony_ci#define AVM_HDLC_STATUS_2 0x1c 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci#define AVM_ISACX_INDEX 0x04 9162306a36Sopenharmony_ci#define AVM_ISACX_DATA 0x08 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci/* data struct */ 9462306a36Sopenharmony_ci#define LOG_SIZE 63 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistruct hdlc_stat_reg { 9762306a36Sopenharmony_ci#ifdef __BIG_ENDIAN 9862306a36Sopenharmony_ci u8 fill; 9962306a36Sopenharmony_ci u8 mode; 10062306a36Sopenharmony_ci u8 xml; 10162306a36Sopenharmony_ci u8 cmd; 10262306a36Sopenharmony_ci#else 10362306a36Sopenharmony_ci u8 cmd; 10462306a36Sopenharmony_ci u8 xml; 10562306a36Sopenharmony_ci u8 mode; 10662306a36Sopenharmony_ci u8 fill; 10762306a36Sopenharmony_ci#endif 10862306a36Sopenharmony_ci} __attribute__((packed)); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistruct hdlc_hw { 11162306a36Sopenharmony_ci union { 11262306a36Sopenharmony_ci u32 ctrl; 11362306a36Sopenharmony_ci struct hdlc_stat_reg sr; 11462306a36Sopenharmony_ci } ctrl; 11562306a36Sopenharmony_ci u32 stat; 11662306a36Sopenharmony_ci}; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_cistruct fritzcard { 11962306a36Sopenharmony_ci struct list_head list; 12062306a36Sopenharmony_ci struct pci_dev *pdev; 12162306a36Sopenharmony_ci char name[MISDN_MAX_IDLEN]; 12262306a36Sopenharmony_ci u8 type; 12362306a36Sopenharmony_ci u8 ctrlreg; 12462306a36Sopenharmony_ci u16 irq; 12562306a36Sopenharmony_ci u32 irqcnt; 12662306a36Sopenharmony_ci u32 addr; 12762306a36Sopenharmony_ci spinlock_t lock; /* hw lock */ 12862306a36Sopenharmony_ci struct isac_hw isac; 12962306a36Sopenharmony_ci struct hdlc_hw hdlc[2]; 13062306a36Sopenharmony_ci struct bchannel bch[2]; 13162306a36Sopenharmony_ci char log[LOG_SIZE + 1]; 13262306a36Sopenharmony_ci}; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic LIST_HEAD(Cards); 13562306a36Sopenharmony_cistatic DEFINE_RWLOCK(card_lock); /* protect Cards */ 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic void 13862306a36Sopenharmony_ci_set_debug(struct fritzcard *card) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci card->isac.dch.debug = debug; 14162306a36Sopenharmony_ci card->bch[0].debug = debug; 14262306a36Sopenharmony_ci card->bch[1].debug = debug; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic int 14662306a36Sopenharmony_ciset_debug(const char *val, const struct kernel_param *kp) 14762306a36Sopenharmony_ci{ 14862306a36Sopenharmony_ci int ret; 14962306a36Sopenharmony_ci struct fritzcard *card; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci ret = param_set_uint(val, kp); 15262306a36Sopenharmony_ci if (!ret) { 15362306a36Sopenharmony_ci read_lock(&card_lock); 15462306a36Sopenharmony_ci list_for_each_entry(card, &Cards, list) 15562306a36Sopenharmony_ci _set_debug(card); 15662306a36Sopenharmony_ci read_unlock(&card_lock); 15762306a36Sopenharmony_ci } 15862306a36Sopenharmony_ci return ret; 15962306a36Sopenharmony_ci} 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ciMODULE_AUTHOR("Karsten Keil"); 16262306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 16362306a36Sopenharmony_ciMODULE_VERSION(AVMFRITZ_REV); 16462306a36Sopenharmony_cimodule_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR); 16562306a36Sopenharmony_ciMODULE_PARM_DESC(debug, "avmfritz debug mask"); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci/* Interface functions */ 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic u8 17062306a36Sopenharmony_ciReadISAC_V1(void *p, u8 offset) 17162306a36Sopenharmony_ci{ 17262306a36Sopenharmony_ci struct fritzcard *fc = p; 17362306a36Sopenharmony_ci u8 idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci outb(idx, fc->addr + CHIP_INDEX); 17662306a36Sopenharmony_ci return inb(fc->addr + CHIP_WINDOW + (offset & 0xf)); 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic void 18062306a36Sopenharmony_ciWriteISAC_V1(void *p, u8 offset, u8 value) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci struct fritzcard *fc = p; 18362306a36Sopenharmony_ci u8 idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci outb(idx, fc->addr + CHIP_INDEX); 18662306a36Sopenharmony_ci outb(value, fc->addr + CHIP_WINDOW + (offset & 0xf)); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic void 19062306a36Sopenharmony_ciReadFiFoISAC_V1(void *p, u8 off, u8 *data, int size) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct fritzcard *fc = p; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci outb(AVM_ISAC_FIFO, fc->addr + CHIP_INDEX); 19562306a36Sopenharmony_ci insb(fc->addr + CHIP_WINDOW, data, size); 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic void 19962306a36Sopenharmony_ciWriteFiFoISAC_V1(void *p, u8 off, u8 *data, int size) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci struct fritzcard *fc = p; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci outb(AVM_ISAC_FIFO, fc->addr + CHIP_INDEX); 20462306a36Sopenharmony_ci outsb(fc->addr + CHIP_WINDOW, data, size); 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic u8 20862306a36Sopenharmony_ciReadISAC_V2(void *p, u8 offset) 20962306a36Sopenharmony_ci{ 21062306a36Sopenharmony_ci struct fritzcard *fc = p; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci outl(offset, fc->addr + AVM_ISACX_INDEX); 21362306a36Sopenharmony_ci return 0xff & inl(fc->addr + AVM_ISACX_DATA); 21462306a36Sopenharmony_ci} 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_cistatic void 21762306a36Sopenharmony_ciWriteISAC_V2(void *p, u8 offset, u8 value) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct fritzcard *fc = p; 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci outl(offset, fc->addr + AVM_ISACX_INDEX); 22262306a36Sopenharmony_ci outl(value, fc->addr + AVM_ISACX_DATA); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_cistatic void 22662306a36Sopenharmony_ciReadFiFoISAC_V2(void *p, u8 off, u8 *data, int size) 22762306a36Sopenharmony_ci{ 22862306a36Sopenharmony_ci struct fritzcard *fc = p; 22962306a36Sopenharmony_ci int i; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci outl(off, fc->addr + AVM_ISACX_INDEX); 23262306a36Sopenharmony_ci for (i = 0; i < size; i++) 23362306a36Sopenharmony_ci data[i] = 0xff & inl(fc->addr + AVM_ISACX_DATA); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic void 23762306a36Sopenharmony_ciWriteFiFoISAC_V2(void *p, u8 off, u8 *data, int size) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci struct fritzcard *fc = p; 24062306a36Sopenharmony_ci int i; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci outl(off, fc->addr + AVM_ISACX_INDEX); 24362306a36Sopenharmony_ci for (i = 0; i < size; i++) 24462306a36Sopenharmony_ci outl(data[i], fc->addr + AVM_ISACX_DATA); 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_cistatic struct bchannel * 24862306a36Sopenharmony_ciSel_BCS(struct fritzcard *fc, u32 channel) 24962306a36Sopenharmony_ci{ 25062306a36Sopenharmony_ci if (test_bit(FLG_ACTIVE, &fc->bch[0].Flags) && 25162306a36Sopenharmony_ci (fc->bch[0].nr & channel)) 25262306a36Sopenharmony_ci return &fc->bch[0]; 25362306a36Sopenharmony_ci else if (test_bit(FLG_ACTIVE, &fc->bch[1].Flags) && 25462306a36Sopenharmony_ci (fc->bch[1].nr & channel)) 25562306a36Sopenharmony_ci return &fc->bch[1]; 25662306a36Sopenharmony_ci else 25762306a36Sopenharmony_ci return NULL; 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic inline void 26162306a36Sopenharmony_ci__write_ctrl_pci(struct fritzcard *fc, struct hdlc_hw *hdlc, u32 channel) { 26262306a36Sopenharmony_ci u32 idx = channel == 2 ? AVM_HDLC_2 : AVM_HDLC_1; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci outl(idx, fc->addr + CHIP_INDEX); 26562306a36Sopenharmony_ci outl(hdlc->ctrl.ctrl, fc->addr + CHIP_WINDOW + HDLC_STATUS); 26662306a36Sopenharmony_ci} 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cistatic inline void 26962306a36Sopenharmony_ci__write_ctrl_pciv2(struct fritzcard *fc, struct hdlc_hw *hdlc, u32 channel) { 27062306a36Sopenharmony_ci outl(hdlc->ctrl.ctrl, fc->addr + (channel == 2 ? AVM_HDLC_STATUS_2 : 27162306a36Sopenharmony_ci AVM_HDLC_STATUS_1)); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic void 27562306a36Sopenharmony_ciwrite_ctrl(struct bchannel *bch, int which) { 27662306a36Sopenharmony_ci struct fritzcard *fc = bch->hw; 27762306a36Sopenharmony_ci struct hdlc_hw *hdlc; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci hdlc = &fc->hdlc[(bch->nr - 1) & 1]; 28062306a36Sopenharmony_ci pr_debug("%s: hdlc %c wr%x ctrl %x\n", fc->name, '@' + bch->nr, 28162306a36Sopenharmony_ci which, hdlc->ctrl.ctrl); 28262306a36Sopenharmony_ci switch (fc->type) { 28362306a36Sopenharmony_ci case AVM_FRITZ_PCIV2: 28462306a36Sopenharmony_ci __write_ctrl_pciv2(fc, hdlc, bch->nr); 28562306a36Sopenharmony_ci break; 28662306a36Sopenharmony_ci case AVM_FRITZ_PCI: 28762306a36Sopenharmony_ci __write_ctrl_pci(fc, hdlc, bch->nr); 28862306a36Sopenharmony_ci break; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_cistatic inline u32 29462306a36Sopenharmony_ci__read_status_pci(u_long addr, u32 channel) 29562306a36Sopenharmony_ci{ 29662306a36Sopenharmony_ci outl(channel == 2 ? AVM_HDLC_2 : AVM_HDLC_1, addr + CHIP_INDEX); 29762306a36Sopenharmony_ci return inl(addr + CHIP_WINDOW + HDLC_STATUS); 29862306a36Sopenharmony_ci} 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_cistatic inline u32 30162306a36Sopenharmony_ci__read_status_pciv2(u_long addr, u32 channel) 30262306a36Sopenharmony_ci{ 30362306a36Sopenharmony_ci return inl(addr + (channel == 2 ? AVM_HDLC_STATUS_2 : 30462306a36Sopenharmony_ci AVM_HDLC_STATUS_1)); 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic u32 30962306a36Sopenharmony_ciread_status(struct fritzcard *fc, u32 channel) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci switch (fc->type) { 31262306a36Sopenharmony_ci case AVM_FRITZ_PCIV2: 31362306a36Sopenharmony_ci return __read_status_pciv2(fc->addr, channel); 31462306a36Sopenharmony_ci case AVM_FRITZ_PCI: 31562306a36Sopenharmony_ci return __read_status_pci(fc->addr, channel); 31662306a36Sopenharmony_ci } 31762306a36Sopenharmony_ci /* dummy */ 31862306a36Sopenharmony_ci return 0; 31962306a36Sopenharmony_ci} 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_cistatic void 32262306a36Sopenharmony_cienable_hwirq(struct fritzcard *fc) 32362306a36Sopenharmony_ci{ 32462306a36Sopenharmony_ci fc->ctrlreg |= AVM_STATUS0_ENA_IRQ; 32562306a36Sopenharmony_ci outb(fc->ctrlreg, fc->addr + 2); 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic void 32962306a36Sopenharmony_cidisable_hwirq(struct fritzcard *fc) 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci fc->ctrlreg &= ~AVM_STATUS0_ENA_IRQ; 33262306a36Sopenharmony_ci outb(fc->ctrlreg, fc->addr + 2); 33362306a36Sopenharmony_ci} 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic int 33662306a36Sopenharmony_cimodehdlc(struct bchannel *bch, int protocol) 33762306a36Sopenharmony_ci{ 33862306a36Sopenharmony_ci struct fritzcard *fc = bch->hw; 33962306a36Sopenharmony_ci struct hdlc_hw *hdlc; 34062306a36Sopenharmony_ci u8 mode; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci hdlc = &fc->hdlc[(bch->nr - 1) & 1]; 34362306a36Sopenharmony_ci pr_debug("%s: hdlc %c protocol %x-->%x ch %d\n", fc->name, 34462306a36Sopenharmony_ci '@' + bch->nr, bch->state, protocol, bch->nr); 34562306a36Sopenharmony_ci hdlc->ctrl.ctrl = 0; 34662306a36Sopenharmony_ci mode = (fc->type == AVM_FRITZ_PCIV2) ? HDLC_FIFO_SIZE_128 : 0; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci switch (protocol) { 34962306a36Sopenharmony_ci case -1: /* used for init */ 35062306a36Sopenharmony_ci bch->state = -1; 35162306a36Sopenharmony_ci fallthrough; 35262306a36Sopenharmony_ci case ISDN_P_NONE: 35362306a36Sopenharmony_ci if (bch->state == ISDN_P_NONE) 35462306a36Sopenharmony_ci break; 35562306a36Sopenharmony_ci hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; 35662306a36Sopenharmony_ci hdlc->ctrl.sr.mode = mode | HDLC_MODE_TRANS; 35762306a36Sopenharmony_ci write_ctrl(bch, 5); 35862306a36Sopenharmony_ci bch->state = ISDN_P_NONE; 35962306a36Sopenharmony_ci test_and_clear_bit(FLG_HDLC, &bch->Flags); 36062306a36Sopenharmony_ci test_and_clear_bit(FLG_TRANSPARENT, &bch->Flags); 36162306a36Sopenharmony_ci break; 36262306a36Sopenharmony_ci case ISDN_P_B_RAW: 36362306a36Sopenharmony_ci bch->state = protocol; 36462306a36Sopenharmony_ci hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; 36562306a36Sopenharmony_ci hdlc->ctrl.sr.mode = mode | HDLC_MODE_TRANS; 36662306a36Sopenharmony_ci write_ctrl(bch, 5); 36762306a36Sopenharmony_ci hdlc->ctrl.sr.cmd = HDLC_CMD_XRS; 36862306a36Sopenharmony_ci write_ctrl(bch, 1); 36962306a36Sopenharmony_ci hdlc->ctrl.sr.cmd = 0; 37062306a36Sopenharmony_ci test_and_set_bit(FLG_TRANSPARENT, &bch->Flags); 37162306a36Sopenharmony_ci break; 37262306a36Sopenharmony_ci case ISDN_P_B_HDLC: 37362306a36Sopenharmony_ci bch->state = protocol; 37462306a36Sopenharmony_ci hdlc->ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; 37562306a36Sopenharmony_ci hdlc->ctrl.sr.mode = mode | HDLC_MODE_ITF_FLG; 37662306a36Sopenharmony_ci write_ctrl(bch, 5); 37762306a36Sopenharmony_ci hdlc->ctrl.sr.cmd = HDLC_CMD_XRS; 37862306a36Sopenharmony_ci write_ctrl(bch, 1); 37962306a36Sopenharmony_ci hdlc->ctrl.sr.cmd = 0; 38062306a36Sopenharmony_ci test_and_set_bit(FLG_HDLC, &bch->Flags); 38162306a36Sopenharmony_ci break; 38262306a36Sopenharmony_ci default: 38362306a36Sopenharmony_ci pr_info("%s: protocol not known %x\n", fc->name, protocol); 38462306a36Sopenharmony_ci return -ENOPROTOOPT; 38562306a36Sopenharmony_ci } 38662306a36Sopenharmony_ci return 0; 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic void 39062306a36Sopenharmony_cihdlc_empty_fifo(struct bchannel *bch, int count) 39162306a36Sopenharmony_ci{ 39262306a36Sopenharmony_ci u32 *ptr; 39362306a36Sopenharmony_ci u8 *p; 39462306a36Sopenharmony_ci u32 val, addr; 39562306a36Sopenharmony_ci int cnt; 39662306a36Sopenharmony_ci struct fritzcard *fc = bch->hw; 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci pr_debug("%s: %s %d\n", fc->name, __func__, count); 39962306a36Sopenharmony_ci if (test_bit(FLG_RX_OFF, &bch->Flags)) { 40062306a36Sopenharmony_ci p = NULL; 40162306a36Sopenharmony_ci bch->dropcnt += count; 40262306a36Sopenharmony_ci } else { 40362306a36Sopenharmony_ci cnt = bchannel_get_rxbuf(bch, count); 40462306a36Sopenharmony_ci if (cnt < 0) { 40562306a36Sopenharmony_ci pr_warn("%s.B%d: No bufferspace for %d bytes\n", 40662306a36Sopenharmony_ci fc->name, bch->nr, count); 40762306a36Sopenharmony_ci return; 40862306a36Sopenharmony_ci } 40962306a36Sopenharmony_ci p = skb_put(bch->rx_skb, count); 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci ptr = (u32 *)p; 41262306a36Sopenharmony_ci if (fc->type == AVM_FRITZ_PCIV2) 41362306a36Sopenharmony_ci addr = fc->addr + (bch->nr == 2 ? 41462306a36Sopenharmony_ci AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1); 41562306a36Sopenharmony_ci else { 41662306a36Sopenharmony_ci addr = fc->addr + CHIP_WINDOW; 41762306a36Sopenharmony_ci outl(bch->nr == 2 ? AVM_HDLC_2 : AVM_HDLC_1, fc->addr); 41862306a36Sopenharmony_ci } 41962306a36Sopenharmony_ci cnt = 0; 42062306a36Sopenharmony_ci while (cnt < count) { 42162306a36Sopenharmony_ci val = le32_to_cpu(inl(addr)); 42262306a36Sopenharmony_ci if (p) { 42362306a36Sopenharmony_ci put_unaligned(val, ptr); 42462306a36Sopenharmony_ci ptr++; 42562306a36Sopenharmony_ci } 42662306a36Sopenharmony_ci cnt += 4; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci if (p && (debug & DEBUG_HW_BFIFO)) { 42962306a36Sopenharmony_ci snprintf(fc->log, LOG_SIZE, "B%1d-recv %s %d ", 43062306a36Sopenharmony_ci bch->nr, fc->name, count); 43162306a36Sopenharmony_ci print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count); 43262306a36Sopenharmony_ci } 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic void 43662306a36Sopenharmony_cihdlc_fill_fifo(struct bchannel *bch) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci struct fritzcard *fc = bch->hw; 43962306a36Sopenharmony_ci struct hdlc_hw *hdlc; 44062306a36Sopenharmony_ci int count, fs, cnt = 0, idx; 44162306a36Sopenharmony_ci bool fillempty = false; 44262306a36Sopenharmony_ci u8 *p; 44362306a36Sopenharmony_ci u32 *ptr, val, addr; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci idx = (bch->nr - 1) & 1; 44662306a36Sopenharmony_ci hdlc = &fc->hdlc[idx]; 44762306a36Sopenharmony_ci fs = (fc->type == AVM_FRITZ_PCIV2) ? 44862306a36Sopenharmony_ci HDLC_FIFO_SIZE_V2 : HDLC_FIFO_SIZE_V1; 44962306a36Sopenharmony_ci if (!bch->tx_skb) { 45062306a36Sopenharmony_ci if (!test_bit(FLG_TX_EMPTY, &bch->Flags)) 45162306a36Sopenharmony_ci return; 45262306a36Sopenharmony_ci count = fs; 45362306a36Sopenharmony_ci p = bch->fill; 45462306a36Sopenharmony_ci fillempty = true; 45562306a36Sopenharmony_ci } else { 45662306a36Sopenharmony_ci count = bch->tx_skb->len - bch->tx_idx; 45762306a36Sopenharmony_ci if (count <= 0) 45862306a36Sopenharmony_ci return; 45962306a36Sopenharmony_ci p = bch->tx_skb->data + bch->tx_idx; 46062306a36Sopenharmony_ci } 46162306a36Sopenharmony_ci hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XME; 46262306a36Sopenharmony_ci if (count > fs) { 46362306a36Sopenharmony_ci count = fs; 46462306a36Sopenharmony_ci } else { 46562306a36Sopenharmony_ci if (test_bit(FLG_HDLC, &bch->Flags)) 46662306a36Sopenharmony_ci hdlc->ctrl.sr.cmd |= HDLC_CMD_XME; 46762306a36Sopenharmony_ci } 46862306a36Sopenharmony_ci ptr = (u32 *)p; 46962306a36Sopenharmony_ci if (!fillempty) { 47062306a36Sopenharmony_ci pr_debug("%s.B%d: %d/%d/%d", fc->name, bch->nr, count, 47162306a36Sopenharmony_ci bch->tx_idx, bch->tx_skb->len); 47262306a36Sopenharmony_ci bch->tx_idx += count; 47362306a36Sopenharmony_ci } else { 47462306a36Sopenharmony_ci pr_debug("%s.B%d: fillempty %d\n", fc->name, bch->nr, count); 47562306a36Sopenharmony_ci } 47662306a36Sopenharmony_ci hdlc->ctrl.sr.xml = ((count == fs) ? 0 : count); 47762306a36Sopenharmony_ci if (fc->type == AVM_FRITZ_PCIV2) { 47862306a36Sopenharmony_ci __write_ctrl_pciv2(fc, hdlc, bch->nr); 47962306a36Sopenharmony_ci addr = fc->addr + (bch->nr == 2 ? 48062306a36Sopenharmony_ci AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1); 48162306a36Sopenharmony_ci } else { 48262306a36Sopenharmony_ci __write_ctrl_pci(fc, hdlc, bch->nr); 48362306a36Sopenharmony_ci addr = fc->addr + CHIP_WINDOW; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci if (fillempty) { 48662306a36Sopenharmony_ci while (cnt < count) { 48762306a36Sopenharmony_ci /* all bytes the same - no worry about endian */ 48862306a36Sopenharmony_ci outl(*ptr, addr); 48962306a36Sopenharmony_ci cnt += 4; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci } else { 49262306a36Sopenharmony_ci while (cnt < count) { 49362306a36Sopenharmony_ci val = get_unaligned(ptr); 49462306a36Sopenharmony_ci outl(cpu_to_le32(val), addr); 49562306a36Sopenharmony_ci ptr++; 49662306a36Sopenharmony_ci cnt += 4; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci if ((debug & DEBUG_HW_BFIFO) && !fillempty) { 50062306a36Sopenharmony_ci snprintf(fc->log, LOG_SIZE, "B%1d-send %s %d ", 50162306a36Sopenharmony_ci bch->nr, fc->name, count); 50262306a36Sopenharmony_ci print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count); 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic void 50762306a36Sopenharmony_ciHDLC_irq_xpr(struct bchannel *bch) 50862306a36Sopenharmony_ci{ 50962306a36Sopenharmony_ci if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len) { 51062306a36Sopenharmony_ci hdlc_fill_fifo(bch); 51162306a36Sopenharmony_ci } else { 51262306a36Sopenharmony_ci dev_kfree_skb(bch->tx_skb); 51362306a36Sopenharmony_ci if (get_next_bframe(bch)) { 51462306a36Sopenharmony_ci hdlc_fill_fifo(bch); 51562306a36Sopenharmony_ci test_and_clear_bit(FLG_TX_EMPTY, &bch->Flags); 51662306a36Sopenharmony_ci } else if (test_bit(FLG_TX_EMPTY, &bch->Flags)) { 51762306a36Sopenharmony_ci hdlc_fill_fifo(bch); 51862306a36Sopenharmony_ci } 51962306a36Sopenharmony_ci } 52062306a36Sopenharmony_ci} 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_cistatic void 52362306a36Sopenharmony_ciHDLC_irq(struct bchannel *bch, u32 stat) 52462306a36Sopenharmony_ci{ 52562306a36Sopenharmony_ci struct fritzcard *fc = bch->hw; 52662306a36Sopenharmony_ci int len, fs; 52762306a36Sopenharmony_ci u32 rmlMask; 52862306a36Sopenharmony_ci struct hdlc_hw *hdlc; 52962306a36Sopenharmony_ci 53062306a36Sopenharmony_ci hdlc = &fc->hdlc[(bch->nr - 1) & 1]; 53162306a36Sopenharmony_ci pr_debug("%s: ch%d stat %#x\n", fc->name, bch->nr, stat); 53262306a36Sopenharmony_ci if (fc->type == AVM_FRITZ_PCIV2) { 53362306a36Sopenharmony_ci rmlMask = HDLC_STAT_RML_MASK_V2; 53462306a36Sopenharmony_ci fs = HDLC_FIFO_SIZE_V2; 53562306a36Sopenharmony_ci } else { 53662306a36Sopenharmony_ci rmlMask = HDLC_STAT_RML_MASK_V1; 53762306a36Sopenharmony_ci fs = HDLC_FIFO_SIZE_V1; 53862306a36Sopenharmony_ci } 53962306a36Sopenharmony_ci if (stat & HDLC_INT_RPR) { 54062306a36Sopenharmony_ci if (stat & HDLC_STAT_RDO) { 54162306a36Sopenharmony_ci pr_warn("%s: ch%d stat %x RDO\n", 54262306a36Sopenharmony_ci fc->name, bch->nr, stat); 54362306a36Sopenharmony_ci hdlc->ctrl.sr.xml = 0; 54462306a36Sopenharmony_ci hdlc->ctrl.sr.cmd |= HDLC_CMD_RRS; 54562306a36Sopenharmony_ci write_ctrl(bch, 1); 54662306a36Sopenharmony_ci hdlc->ctrl.sr.cmd &= ~HDLC_CMD_RRS; 54762306a36Sopenharmony_ci write_ctrl(bch, 1); 54862306a36Sopenharmony_ci if (bch->rx_skb) 54962306a36Sopenharmony_ci skb_trim(bch->rx_skb, 0); 55062306a36Sopenharmony_ci } else { 55162306a36Sopenharmony_ci len = (stat & rmlMask) >> 8; 55262306a36Sopenharmony_ci if (!len) 55362306a36Sopenharmony_ci len = fs; 55462306a36Sopenharmony_ci hdlc_empty_fifo(bch, len); 55562306a36Sopenharmony_ci if (!bch->rx_skb) 55662306a36Sopenharmony_ci goto handle_tx; 55762306a36Sopenharmony_ci if (test_bit(FLG_TRANSPARENT, &bch->Flags)) { 55862306a36Sopenharmony_ci recv_Bchannel(bch, 0, false); 55962306a36Sopenharmony_ci } else if (stat & HDLC_STAT_RME) { 56062306a36Sopenharmony_ci if ((stat & HDLC_STAT_CRCVFRRAB) == 56162306a36Sopenharmony_ci HDLC_STAT_CRCVFR) { 56262306a36Sopenharmony_ci recv_Bchannel(bch, 0, false); 56362306a36Sopenharmony_ci } else { 56462306a36Sopenharmony_ci pr_warn("%s: got invalid frame\n", 56562306a36Sopenharmony_ci fc->name); 56662306a36Sopenharmony_ci skb_trim(bch->rx_skb, 0); 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci } 57162306a36Sopenharmony_cihandle_tx: 57262306a36Sopenharmony_ci if (stat & HDLC_INT_XDU) { 57362306a36Sopenharmony_ci /* Here we lost an TX interrupt, so 57462306a36Sopenharmony_ci * restart transmitting the whole frame on HDLC 57562306a36Sopenharmony_ci * in transparent mode we send the next data 57662306a36Sopenharmony_ci */ 57762306a36Sopenharmony_ci pr_warn("%s: ch%d stat %x XDU %s\n", fc->name, bch->nr, 57862306a36Sopenharmony_ci stat, bch->tx_skb ? "tx_skb" : "no tx_skb"); 57962306a36Sopenharmony_ci if (bch->tx_skb && bch->tx_skb->len) { 58062306a36Sopenharmony_ci if (!test_bit(FLG_TRANSPARENT, &bch->Flags)) 58162306a36Sopenharmony_ci bch->tx_idx = 0; 58262306a36Sopenharmony_ci } else if (test_bit(FLG_FILLEMPTY, &bch->Flags)) { 58362306a36Sopenharmony_ci test_and_set_bit(FLG_TX_EMPTY, &bch->Flags); 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci hdlc->ctrl.sr.xml = 0; 58662306a36Sopenharmony_ci hdlc->ctrl.sr.cmd |= HDLC_CMD_XRS; 58762306a36Sopenharmony_ci write_ctrl(bch, 1); 58862306a36Sopenharmony_ci hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XRS; 58962306a36Sopenharmony_ci HDLC_irq_xpr(bch); 59062306a36Sopenharmony_ci return; 59162306a36Sopenharmony_ci } else if (stat & HDLC_INT_XPR) 59262306a36Sopenharmony_ci HDLC_irq_xpr(bch); 59362306a36Sopenharmony_ci} 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_cistatic inline void 59662306a36Sopenharmony_ciHDLC_irq_main(struct fritzcard *fc) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci u32 stat; 59962306a36Sopenharmony_ci struct bchannel *bch; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci stat = read_status(fc, 1); 60262306a36Sopenharmony_ci if (stat & HDLC_INT_MASK) { 60362306a36Sopenharmony_ci bch = Sel_BCS(fc, 1); 60462306a36Sopenharmony_ci if (bch) 60562306a36Sopenharmony_ci HDLC_irq(bch, stat); 60662306a36Sopenharmony_ci else 60762306a36Sopenharmony_ci pr_debug("%s: spurious ch1 IRQ\n", fc->name); 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci stat = read_status(fc, 2); 61062306a36Sopenharmony_ci if (stat & HDLC_INT_MASK) { 61162306a36Sopenharmony_ci bch = Sel_BCS(fc, 2); 61262306a36Sopenharmony_ci if (bch) 61362306a36Sopenharmony_ci HDLC_irq(bch, stat); 61462306a36Sopenharmony_ci else 61562306a36Sopenharmony_ci pr_debug("%s: spurious ch2 IRQ\n", fc->name); 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci} 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_cistatic irqreturn_t 62062306a36Sopenharmony_ciavm_fritz_interrupt(int intno, void *dev_id) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci struct fritzcard *fc = dev_id; 62362306a36Sopenharmony_ci u8 val; 62462306a36Sopenharmony_ci u8 sval; 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci spin_lock(&fc->lock); 62762306a36Sopenharmony_ci sval = inb(fc->addr + 2); 62862306a36Sopenharmony_ci pr_debug("%s: irq stat0 %x\n", fc->name, sval); 62962306a36Sopenharmony_ci if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK) { 63062306a36Sopenharmony_ci /* shared IRQ from other HW */ 63162306a36Sopenharmony_ci spin_unlock(&fc->lock); 63262306a36Sopenharmony_ci return IRQ_NONE; 63362306a36Sopenharmony_ci } 63462306a36Sopenharmony_ci fc->irqcnt++; 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci if (!(sval & AVM_STATUS0_IRQ_ISAC)) { 63762306a36Sopenharmony_ci val = ReadISAC_V1(fc, ISAC_ISTA); 63862306a36Sopenharmony_ci mISDNisac_irq(&fc->isac, val); 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci if (!(sval & AVM_STATUS0_IRQ_HDLC)) 64162306a36Sopenharmony_ci HDLC_irq_main(fc); 64262306a36Sopenharmony_ci spin_unlock(&fc->lock); 64362306a36Sopenharmony_ci return IRQ_HANDLED; 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_cistatic irqreturn_t 64762306a36Sopenharmony_ciavm_fritzv2_interrupt(int intno, void *dev_id) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci struct fritzcard *fc = dev_id; 65062306a36Sopenharmony_ci u8 val; 65162306a36Sopenharmony_ci u8 sval; 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci spin_lock(&fc->lock); 65462306a36Sopenharmony_ci sval = inb(fc->addr + 2); 65562306a36Sopenharmony_ci pr_debug("%s: irq stat0 %x\n", fc->name, sval); 65662306a36Sopenharmony_ci if (!(sval & AVM_STATUS0_IRQ_MASK)) { 65762306a36Sopenharmony_ci /* shared IRQ from other HW */ 65862306a36Sopenharmony_ci spin_unlock(&fc->lock); 65962306a36Sopenharmony_ci return IRQ_NONE; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci fc->irqcnt++; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (sval & AVM_STATUS0_IRQ_HDLC) 66462306a36Sopenharmony_ci HDLC_irq_main(fc); 66562306a36Sopenharmony_ci if (sval & AVM_STATUS0_IRQ_ISAC) { 66662306a36Sopenharmony_ci val = ReadISAC_V2(fc, ISACX_ISTA); 66762306a36Sopenharmony_ci mISDNisac_irq(&fc->isac, val); 66862306a36Sopenharmony_ci } 66962306a36Sopenharmony_ci if (sval & AVM_STATUS0_IRQ_TIMER) { 67062306a36Sopenharmony_ci pr_debug("%s: timer irq\n", fc->name); 67162306a36Sopenharmony_ci outb(fc->ctrlreg | AVM_STATUS0_RES_TIMER, fc->addr + 2); 67262306a36Sopenharmony_ci udelay(1); 67362306a36Sopenharmony_ci outb(fc->ctrlreg, fc->addr + 2); 67462306a36Sopenharmony_ci } 67562306a36Sopenharmony_ci spin_unlock(&fc->lock); 67662306a36Sopenharmony_ci return IRQ_HANDLED; 67762306a36Sopenharmony_ci} 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_cistatic int 68062306a36Sopenharmony_ciavm_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci struct bchannel *bch = container_of(ch, struct bchannel, ch); 68362306a36Sopenharmony_ci struct fritzcard *fc = bch->hw; 68462306a36Sopenharmony_ci int ret = -EINVAL; 68562306a36Sopenharmony_ci struct mISDNhead *hh = mISDN_HEAD_P(skb); 68662306a36Sopenharmony_ci unsigned long flags; 68762306a36Sopenharmony_ci 68862306a36Sopenharmony_ci switch (hh->prim) { 68962306a36Sopenharmony_ci case PH_DATA_REQ: 69062306a36Sopenharmony_ci spin_lock_irqsave(&fc->lock, flags); 69162306a36Sopenharmony_ci ret = bchannel_senddata(bch, skb); 69262306a36Sopenharmony_ci if (ret > 0) { /* direct TX */ 69362306a36Sopenharmony_ci hdlc_fill_fifo(bch); 69462306a36Sopenharmony_ci ret = 0; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci spin_unlock_irqrestore(&fc->lock, flags); 69762306a36Sopenharmony_ci return ret; 69862306a36Sopenharmony_ci case PH_ACTIVATE_REQ: 69962306a36Sopenharmony_ci spin_lock_irqsave(&fc->lock, flags); 70062306a36Sopenharmony_ci if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags)) 70162306a36Sopenharmony_ci ret = modehdlc(bch, ch->protocol); 70262306a36Sopenharmony_ci else 70362306a36Sopenharmony_ci ret = 0; 70462306a36Sopenharmony_ci spin_unlock_irqrestore(&fc->lock, flags); 70562306a36Sopenharmony_ci if (!ret) 70662306a36Sopenharmony_ci _queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0, 70762306a36Sopenharmony_ci NULL, GFP_KERNEL); 70862306a36Sopenharmony_ci break; 70962306a36Sopenharmony_ci case PH_DEACTIVATE_REQ: 71062306a36Sopenharmony_ci spin_lock_irqsave(&fc->lock, flags); 71162306a36Sopenharmony_ci mISDN_clear_bchannel(bch); 71262306a36Sopenharmony_ci modehdlc(bch, ISDN_P_NONE); 71362306a36Sopenharmony_ci spin_unlock_irqrestore(&fc->lock, flags); 71462306a36Sopenharmony_ci _queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0, 71562306a36Sopenharmony_ci NULL, GFP_KERNEL); 71662306a36Sopenharmony_ci ret = 0; 71762306a36Sopenharmony_ci break; 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci if (!ret) 72062306a36Sopenharmony_ci dev_kfree_skb(skb); 72162306a36Sopenharmony_ci return ret; 72262306a36Sopenharmony_ci} 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_cistatic void 72562306a36Sopenharmony_ciinithdlc(struct fritzcard *fc) 72662306a36Sopenharmony_ci{ 72762306a36Sopenharmony_ci modehdlc(&fc->bch[0], -1); 72862306a36Sopenharmony_ci modehdlc(&fc->bch[1], -1); 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_cistatic void 73262306a36Sopenharmony_ciclear_pending_hdlc_ints(struct fritzcard *fc) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci u32 val; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci val = read_status(fc, 1); 73762306a36Sopenharmony_ci pr_debug("%s: HDLC 1 STA %x\n", fc->name, val); 73862306a36Sopenharmony_ci val = read_status(fc, 2); 73962306a36Sopenharmony_ci pr_debug("%s: HDLC 2 STA %x\n", fc->name, val); 74062306a36Sopenharmony_ci} 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_cistatic void 74362306a36Sopenharmony_cireset_avm(struct fritzcard *fc) 74462306a36Sopenharmony_ci{ 74562306a36Sopenharmony_ci switch (fc->type) { 74662306a36Sopenharmony_ci case AVM_FRITZ_PCI: 74762306a36Sopenharmony_ci fc->ctrlreg = AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER; 74862306a36Sopenharmony_ci break; 74962306a36Sopenharmony_ci case AVM_FRITZ_PCIV2: 75062306a36Sopenharmony_ci fc->ctrlreg = AVM_STATUS0_RESET; 75162306a36Sopenharmony_ci break; 75262306a36Sopenharmony_ci } 75362306a36Sopenharmony_ci if (debug & DEBUG_HW) 75462306a36Sopenharmony_ci pr_notice("%s: reset\n", fc->name); 75562306a36Sopenharmony_ci disable_hwirq(fc); 75662306a36Sopenharmony_ci mdelay(5); 75762306a36Sopenharmony_ci switch (fc->type) { 75862306a36Sopenharmony_ci case AVM_FRITZ_PCI: 75962306a36Sopenharmony_ci fc->ctrlreg = AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER; 76062306a36Sopenharmony_ci disable_hwirq(fc); 76162306a36Sopenharmony_ci outb(AVM_STATUS1_ENA_IOM, fc->addr + 3); 76262306a36Sopenharmony_ci break; 76362306a36Sopenharmony_ci case AVM_FRITZ_PCIV2: 76462306a36Sopenharmony_ci fc->ctrlreg = 0; 76562306a36Sopenharmony_ci disable_hwirq(fc); 76662306a36Sopenharmony_ci break; 76762306a36Sopenharmony_ci } 76862306a36Sopenharmony_ci mdelay(1); 76962306a36Sopenharmony_ci if (debug & DEBUG_HW) 77062306a36Sopenharmony_ci pr_notice("%s: S0/S1 %x/%x\n", fc->name, 77162306a36Sopenharmony_ci inb(fc->addr + 2), inb(fc->addr + 3)); 77262306a36Sopenharmony_ci} 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_cistatic int 77562306a36Sopenharmony_ciinit_card(struct fritzcard *fc) 77662306a36Sopenharmony_ci{ 77762306a36Sopenharmony_ci int ret, cnt = 3; 77862306a36Sopenharmony_ci u_long flags; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci reset_avm(fc); /* disable IRQ */ 78162306a36Sopenharmony_ci if (fc->type == AVM_FRITZ_PCIV2) 78262306a36Sopenharmony_ci ret = request_irq(fc->irq, avm_fritzv2_interrupt, 78362306a36Sopenharmony_ci IRQF_SHARED, fc->name, fc); 78462306a36Sopenharmony_ci else 78562306a36Sopenharmony_ci ret = request_irq(fc->irq, avm_fritz_interrupt, 78662306a36Sopenharmony_ci IRQF_SHARED, fc->name, fc); 78762306a36Sopenharmony_ci if (ret) { 78862306a36Sopenharmony_ci pr_info("%s: couldn't get interrupt %d\n", 78962306a36Sopenharmony_ci fc->name, fc->irq); 79062306a36Sopenharmony_ci return ret; 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci while (cnt--) { 79362306a36Sopenharmony_ci spin_lock_irqsave(&fc->lock, flags); 79462306a36Sopenharmony_ci ret = fc->isac.init(&fc->isac); 79562306a36Sopenharmony_ci if (ret) { 79662306a36Sopenharmony_ci spin_unlock_irqrestore(&fc->lock, flags); 79762306a36Sopenharmony_ci pr_info("%s: ISAC init failed with %d\n", 79862306a36Sopenharmony_ci fc->name, ret); 79962306a36Sopenharmony_ci break; 80062306a36Sopenharmony_ci } 80162306a36Sopenharmony_ci clear_pending_hdlc_ints(fc); 80262306a36Sopenharmony_ci inithdlc(fc); 80362306a36Sopenharmony_ci enable_hwirq(fc); 80462306a36Sopenharmony_ci /* RESET Receiver and Transmitter */ 80562306a36Sopenharmony_ci if (fc->type == AVM_FRITZ_PCIV2) { 80662306a36Sopenharmony_ci WriteISAC_V2(fc, ISACX_MASK, 0); 80762306a36Sopenharmony_ci WriteISAC_V2(fc, ISACX_CMDRD, 0x41); 80862306a36Sopenharmony_ci } else { 80962306a36Sopenharmony_ci WriteISAC_V1(fc, ISAC_MASK, 0); 81062306a36Sopenharmony_ci WriteISAC_V1(fc, ISAC_CMDR, 0x41); 81162306a36Sopenharmony_ci } 81262306a36Sopenharmony_ci spin_unlock_irqrestore(&fc->lock, flags); 81362306a36Sopenharmony_ci /* Timeout 10ms */ 81462306a36Sopenharmony_ci msleep_interruptible(10); 81562306a36Sopenharmony_ci if (debug & DEBUG_HW) 81662306a36Sopenharmony_ci pr_notice("%s: IRQ %d count %d\n", fc->name, 81762306a36Sopenharmony_ci fc->irq, fc->irqcnt); 81862306a36Sopenharmony_ci if (!fc->irqcnt) { 81962306a36Sopenharmony_ci pr_info("%s: IRQ(%d) getting no IRQs during init %d\n", 82062306a36Sopenharmony_ci fc->name, fc->irq, 3 - cnt); 82162306a36Sopenharmony_ci reset_avm(fc); 82262306a36Sopenharmony_ci } else 82362306a36Sopenharmony_ci return 0; 82462306a36Sopenharmony_ci } 82562306a36Sopenharmony_ci free_irq(fc->irq, fc); 82662306a36Sopenharmony_ci return -EIO; 82762306a36Sopenharmony_ci} 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_cistatic int 83062306a36Sopenharmony_cichannel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq) 83162306a36Sopenharmony_ci{ 83262306a36Sopenharmony_ci return mISDN_ctrl_bchannel(bch, cq); 83362306a36Sopenharmony_ci} 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_cistatic int 83662306a36Sopenharmony_ciavm_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg) 83762306a36Sopenharmony_ci{ 83862306a36Sopenharmony_ci struct bchannel *bch = container_of(ch, struct bchannel, ch); 83962306a36Sopenharmony_ci struct fritzcard *fc = bch->hw; 84062306a36Sopenharmony_ci int ret = -EINVAL; 84162306a36Sopenharmony_ci u_long flags; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci pr_debug("%s: %s cmd:%x %p\n", fc->name, __func__, cmd, arg); 84462306a36Sopenharmony_ci switch (cmd) { 84562306a36Sopenharmony_ci case CLOSE_CHANNEL: 84662306a36Sopenharmony_ci test_and_clear_bit(FLG_OPEN, &bch->Flags); 84762306a36Sopenharmony_ci cancel_work_sync(&bch->workq); 84862306a36Sopenharmony_ci spin_lock_irqsave(&fc->lock, flags); 84962306a36Sopenharmony_ci mISDN_clear_bchannel(bch); 85062306a36Sopenharmony_ci modehdlc(bch, ISDN_P_NONE); 85162306a36Sopenharmony_ci spin_unlock_irqrestore(&fc->lock, flags); 85262306a36Sopenharmony_ci ch->protocol = ISDN_P_NONE; 85362306a36Sopenharmony_ci ch->peer = NULL; 85462306a36Sopenharmony_ci module_put(THIS_MODULE); 85562306a36Sopenharmony_ci ret = 0; 85662306a36Sopenharmony_ci break; 85762306a36Sopenharmony_ci case CONTROL_CHANNEL: 85862306a36Sopenharmony_ci ret = channel_bctrl(bch, arg); 85962306a36Sopenharmony_ci break; 86062306a36Sopenharmony_ci default: 86162306a36Sopenharmony_ci pr_info("%s: %s unknown prim(%x)\n", fc->name, __func__, cmd); 86262306a36Sopenharmony_ci } 86362306a36Sopenharmony_ci return ret; 86462306a36Sopenharmony_ci} 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_cistatic int 86762306a36Sopenharmony_cichannel_ctrl(struct fritzcard *fc, struct mISDN_ctrl_req *cq) 86862306a36Sopenharmony_ci{ 86962306a36Sopenharmony_ci int ret = 0; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_ci switch (cq->op) { 87262306a36Sopenharmony_ci case MISDN_CTRL_GETOP: 87362306a36Sopenharmony_ci cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_L1_TIMER3; 87462306a36Sopenharmony_ci break; 87562306a36Sopenharmony_ci case MISDN_CTRL_LOOP: 87662306a36Sopenharmony_ci /* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */ 87762306a36Sopenharmony_ci if (cq->channel < 0 || cq->channel > 3) { 87862306a36Sopenharmony_ci ret = -EINVAL; 87962306a36Sopenharmony_ci break; 88062306a36Sopenharmony_ci } 88162306a36Sopenharmony_ci ret = fc->isac.ctrl(&fc->isac, HW_TESTLOOP, cq->channel); 88262306a36Sopenharmony_ci break; 88362306a36Sopenharmony_ci case MISDN_CTRL_L1_TIMER3: 88462306a36Sopenharmony_ci ret = fc->isac.ctrl(&fc->isac, HW_TIMER3_VALUE, cq->p1); 88562306a36Sopenharmony_ci break; 88662306a36Sopenharmony_ci default: 88762306a36Sopenharmony_ci pr_info("%s: %s unknown Op %x\n", fc->name, __func__, cq->op); 88862306a36Sopenharmony_ci ret = -EINVAL; 88962306a36Sopenharmony_ci break; 89062306a36Sopenharmony_ci } 89162306a36Sopenharmony_ci return ret; 89262306a36Sopenharmony_ci} 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_cistatic int 89562306a36Sopenharmony_ciopen_bchannel(struct fritzcard *fc, struct channel_req *rq) 89662306a36Sopenharmony_ci{ 89762306a36Sopenharmony_ci struct bchannel *bch; 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci if (rq->adr.channel == 0 || rq->adr.channel > 2) 90062306a36Sopenharmony_ci return -EINVAL; 90162306a36Sopenharmony_ci if (rq->protocol == ISDN_P_NONE) 90262306a36Sopenharmony_ci return -EINVAL; 90362306a36Sopenharmony_ci bch = &fc->bch[rq->adr.channel - 1]; 90462306a36Sopenharmony_ci if (test_and_set_bit(FLG_OPEN, &bch->Flags)) 90562306a36Sopenharmony_ci return -EBUSY; /* b-channel can be only open once */ 90662306a36Sopenharmony_ci bch->ch.protocol = rq->protocol; 90762306a36Sopenharmony_ci rq->ch = &bch->ch; 90862306a36Sopenharmony_ci return 0; 90962306a36Sopenharmony_ci} 91062306a36Sopenharmony_ci 91162306a36Sopenharmony_ci/* 91262306a36Sopenharmony_ci * device control function 91362306a36Sopenharmony_ci */ 91462306a36Sopenharmony_cistatic int 91562306a36Sopenharmony_ciavm_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg) 91662306a36Sopenharmony_ci{ 91762306a36Sopenharmony_ci struct mISDNdevice *dev = container_of(ch, struct mISDNdevice, D); 91862306a36Sopenharmony_ci struct dchannel *dch = container_of(dev, struct dchannel, dev); 91962306a36Sopenharmony_ci struct fritzcard *fc = dch->hw; 92062306a36Sopenharmony_ci struct channel_req *rq; 92162306a36Sopenharmony_ci int err = 0; 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci pr_debug("%s: %s cmd:%x %p\n", fc->name, __func__, cmd, arg); 92462306a36Sopenharmony_ci switch (cmd) { 92562306a36Sopenharmony_ci case OPEN_CHANNEL: 92662306a36Sopenharmony_ci rq = arg; 92762306a36Sopenharmony_ci if (rq->protocol == ISDN_P_TE_S0) 92862306a36Sopenharmony_ci err = fc->isac.open(&fc->isac, rq); 92962306a36Sopenharmony_ci else 93062306a36Sopenharmony_ci err = open_bchannel(fc, rq); 93162306a36Sopenharmony_ci if (err) 93262306a36Sopenharmony_ci break; 93362306a36Sopenharmony_ci if (!try_module_get(THIS_MODULE)) 93462306a36Sopenharmony_ci pr_info("%s: cannot get module\n", fc->name); 93562306a36Sopenharmony_ci break; 93662306a36Sopenharmony_ci case CLOSE_CHANNEL: 93762306a36Sopenharmony_ci pr_debug("%s: dev(%d) close from %p\n", fc->name, dch->dev.id, 93862306a36Sopenharmony_ci __builtin_return_address(0)); 93962306a36Sopenharmony_ci module_put(THIS_MODULE); 94062306a36Sopenharmony_ci break; 94162306a36Sopenharmony_ci case CONTROL_CHANNEL: 94262306a36Sopenharmony_ci err = channel_ctrl(fc, arg); 94362306a36Sopenharmony_ci break; 94462306a36Sopenharmony_ci default: 94562306a36Sopenharmony_ci pr_debug("%s: %s unknown command %x\n", 94662306a36Sopenharmony_ci fc->name, __func__, cmd); 94762306a36Sopenharmony_ci return -EINVAL; 94862306a36Sopenharmony_ci } 94962306a36Sopenharmony_ci return err; 95062306a36Sopenharmony_ci} 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_cistatic int 95362306a36Sopenharmony_cisetup_fritz(struct fritzcard *fc) 95462306a36Sopenharmony_ci{ 95562306a36Sopenharmony_ci u32 val, ver; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci if (!request_region(fc->addr, 32, fc->name)) { 95862306a36Sopenharmony_ci pr_info("%s: AVM config port %x-%x already in use\n", 95962306a36Sopenharmony_ci fc->name, fc->addr, fc->addr + 31); 96062306a36Sopenharmony_ci return -EIO; 96162306a36Sopenharmony_ci } 96262306a36Sopenharmony_ci switch (fc->type) { 96362306a36Sopenharmony_ci case AVM_FRITZ_PCI: 96462306a36Sopenharmony_ci val = inl(fc->addr); 96562306a36Sopenharmony_ci outl(AVM_HDLC_1, fc->addr + CHIP_INDEX); 96662306a36Sopenharmony_ci ver = inl(fc->addr + CHIP_WINDOW + HDLC_STATUS) >> 24; 96762306a36Sopenharmony_ci if (debug & DEBUG_HW) { 96862306a36Sopenharmony_ci pr_notice("%s: PCI stat %#x\n", fc->name, val); 96962306a36Sopenharmony_ci pr_notice("%s: PCI Class %X Rev %d\n", fc->name, 97062306a36Sopenharmony_ci val & 0xff, (val >> 8) & 0xff); 97162306a36Sopenharmony_ci pr_notice("%s: HDLC version %x\n", fc->name, ver & 0xf); 97262306a36Sopenharmony_ci } 97362306a36Sopenharmony_ci ASSIGN_FUNC(V1, ISAC, fc->isac); 97462306a36Sopenharmony_ci fc->isac.type = IPAC_TYPE_ISAC; 97562306a36Sopenharmony_ci break; 97662306a36Sopenharmony_ci case AVM_FRITZ_PCIV2: 97762306a36Sopenharmony_ci val = inl(fc->addr); 97862306a36Sopenharmony_ci ver = inl(fc->addr + AVM_HDLC_STATUS_1) >> 24; 97962306a36Sopenharmony_ci if (debug & DEBUG_HW) { 98062306a36Sopenharmony_ci pr_notice("%s: PCI V2 stat %#x\n", fc->name, val); 98162306a36Sopenharmony_ci pr_notice("%s: PCI V2 Class %X Rev %d\n", fc->name, 98262306a36Sopenharmony_ci val & 0xff, (val >> 8) & 0xff); 98362306a36Sopenharmony_ci pr_notice("%s: HDLC version %x\n", fc->name, ver & 0xf); 98462306a36Sopenharmony_ci } 98562306a36Sopenharmony_ci ASSIGN_FUNC(V2, ISAC, fc->isac); 98662306a36Sopenharmony_ci fc->isac.type = IPAC_TYPE_ISACX; 98762306a36Sopenharmony_ci break; 98862306a36Sopenharmony_ci default: 98962306a36Sopenharmony_ci release_region(fc->addr, 32); 99062306a36Sopenharmony_ci pr_info("%s: AVM unknown type %d\n", fc->name, fc->type); 99162306a36Sopenharmony_ci return -ENODEV; 99262306a36Sopenharmony_ci } 99362306a36Sopenharmony_ci pr_notice("%s: %s config irq:%d base:0x%X\n", fc->name, 99462306a36Sopenharmony_ci (fc->type == AVM_FRITZ_PCI) ? "AVM Fritz!CARD PCI" : 99562306a36Sopenharmony_ci "AVM Fritz!CARD PCIv2", fc->irq, fc->addr); 99662306a36Sopenharmony_ci return 0; 99762306a36Sopenharmony_ci} 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_cistatic void 100062306a36Sopenharmony_cirelease_card(struct fritzcard *card) 100162306a36Sopenharmony_ci{ 100262306a36Sopenharmony_ci u_long flags; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci disable_hwirq(card); 100562306a36Sopenharmony_ci spin_lock_irqsave(&card->lock, flags); 100662306a36Sopenharmony_ci modehdlc(&card->bch[0], ISDN_P_NONE); 100762306a36Sopenharmony_ci modehdlc(&card->bch[1], ISDN_P_NONE); 100862306a36Sopenharmony_ci spin_unlock_irqrestore(&card->lock, flags); 100962306a36Sopenharmony_ci card->isac.release(&card->isac); 101062306a36Sopenharmony_ci free_irq(card->irq, card); 101162306a36Sopenharmony_ci mISDN_freebchannel(&card->bch[1]); 101262306a36Sopenharmony_ci mISDN_freebchannel(&card->bch[0]); 101362306a36Sopenharmony_ci mISDN_unregister_device(&card->isac.dch.dev); 101462306a36Sopenharmony_ci release_region(card->addr, 32); 101562306a36Sopenharmony_ci pci_disable_device(card->pdev); 101662306a36Sopenharmony_ci pci_set_drvdata(card->pdev, NULL); 101762306a36Sopenharmony_ci write_lock_irqsave(&card_lock, flags); 101862306a36Sopenharmony_ci list_del(&card->list); 101962306a36Sopenharmony_ci write_unlock_irqrestore(&card_lock, flags); 102062306a36Sopenharmony_ci kfree(card); 102162306a36Sopenharmony_ci AVM_cnt--; 102262306a36Sopenharmony_ci} 102362306a36Sopenharmony_ci 102462306a36Sopenharmony_cistatic int 102562306a36Sopenharmony_cisetup_instance(struct fritzcard *card) 102662306a36Sopenharmony_ci{ 102762306a36Sopenharmony_ci int i, err; 102862306a36Sopenharmony_ci unsigned short minsize; 102962306a36Sopenharmony_ci u_long flags; 103062306a36Sopenharmony_ci 103162306a36Sopenharmony_ci snprintf(card->name, MISDN_MAX_IDLEN - 1, "AVM.%d", AVM_cnt + 1); 103262306a36Sopenharmony_ci write_lock_irqsave(&card_lock, flags); 103362306a36Sopenharmony_ci list_add_tail(&card->list, &Cards); 103462306a36Sopenharmony_ci write_unlock_irqrestore(&card_lock, flags); 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_ci _set_debug(card); 103762306a36Sopenharmony_ci card->isac.name = card->name; 103862306a36Sopenharmony_ci spin_lock_init(&card->lock); 103962306a36Sopenharmony_ci card->isac.hwlock = &card->lock; 104062306a36Sopenharmony_ci mISDNisac_init(&card->isac, card); 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_ci card->isac.dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) | 104362306a36Sopenharmony_ci (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK)); 104462306a36Sopenharmony_ci card->isac.dch.dev.D.ctrl = avm_dctrl; 104562306a36Sopenharmony_ci for (i = 0; i < 2; i++) { 104662306a36Sopenharmony_ci card->bch[i].nr = i + 1; 104762306a36Sopenharmony_ci set_channelmap(i + 1, card->isac.dch.dev.channelmap); 104862306a36Sopenharmony_ci if (AVM_FRITZ_PCIV2 == card->type) 104962306a36Sopenharmony_ci minsize = HDLC_FIFO_SIZE_V2; 105062306a36Sopenharmony_ci else 105162306a36Sopenharmony_ci minsize = HDLC_FIFO_SIZE_V1; 105262306a36Sopenharmony_ci mISDN_initbchannel(&card->bch[i], MAX_DATA_MEM, minsize); 105362306a36Sopenharmony_ci card->bch[i].hw = card; 105462306a36Sopenharmony_ci card->bch[i].ch.send = avm_l2l1B; 105562306a36Sopenharmony_ci card->bch[i].ch.ctrl = avm_bctrl; 105662306a36Sopenharmony_ci card->bch[i].ch.nr = i + 1; 105762306a36Sopenharmony_ci list_add(&card->bch[i].ch.list, &card->isac.dch.dev.bchannels); 105862306a36Sopenharmony_ci } 105962306a36Sopenharmony_ci err = setup_fritz(card); 106062306a36Sopenharmony_ci if (err) 106162306a36Sopenharmony_ci goto error; 106262306a36Sopenharmony_ci err = mISDN_register_device(&card->isac.dch.dev, &card->pdev->dev, 106362306a36Sopenharmony_ci card->name); 106462306a36Sopenharmony_ci if (err) 106562306a36Sopenharmony_ci goto error_reg; 106662306a36Sopenharmony_ci err = init_card(card); 106762306a36Sopenharmony_ci if (!err) { 106862306a36Sopenharmony_ci AVM_cnt++; 106962306a36Sopenharmony_ci pr_notice("AVM %d cards installed DEBUG\n", AVM_cnt); 107062306a36Sopenharmony_ci return 0; 107162306a36Sopenharmony_ci } 107262306a36Sopenharmony_ci mISDN_unregister_device(&card->isac.dch.dev); 107362306a36Sopenharmony_cierror_reg: 107462306a36Sopenharmony_ci release_region(card->addr, 32); 107562306a36Sopenharmony_cierror: 107662306a36Sopenharmony_ci card->isac.release(&card->isac); 107762306a36Sopenharmony_ci mISDN_freebchannel(&card->bch[1]); 107862306a36Sopenharmony_ci mISDN_freebchannel(&card->bch[0]); 107962306a36Sopenharmony_ci write_lock_irqsave(&card_lock, flags); 108062306a36Sopenharmony_ci list_del(&card->list); 108162306a36Sopenharmony_ci write_unlock_irqrestore(&card_lock, flags); 108262306a36Sopenharmony_ci kfree(card); 108362306a36Sopenharmony_ci return err; 108462306a36Sopenharmony_ci} 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_cistatic int 108762306a36Sopenharmony_cifritzpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 108862306a36Sopenharmony_ci{ 108962306a36Sopenharmony_ci int err = -ENOMEM; 109062306a36Sopenharmony_ci struct fritzcard *card; 109162306a36Sopenharmony_ci 109262306a36Sopenharmony_ci card = kzalloc(sizeof(struct fritzcard), GFP_KERNEL); 109362306a36Sopenharmony_ci if (!card) { 109462306a36Sopenharmony_ci pr_info("No kmem for fritzcard\n"); 109562306a36Sopenharmony_ci return err; 109662306a36Sopenharmony_ci } 109762306a36Sopenharmony_ci if (pdev->device == PCI_DEVICE_ID_AVM_A1_V2) 109862306a36Sopenharmony_ci card->type = AVM_FRITZ_PCIV2; 109962306a36Sopenharmony_ci else 110062306a36Sopenharmony_ci card->type = AVM_FRITZ_PCI; 110162306a36Sopenharmony_ci card->pdev = pdev; 110262306a36Sopenharmony_ci err = pci_enable_device(pdev); 110362306a36Sopenharmony_ci if (err) { 110462306a36Sopenharmony_ci kfree(card); 110562306a36Sopenharmony_ci return err; 110662306a36Sopenharmony_ci } 110762306a36Sopenharmony_ci 110862306a36Sopenharmony_ci pr_notice("mISDN: found adapter %s at %s\n", 110962306a36Sopenharmony_ci (char *) ent->driver_data, pci_name(pdev)); 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci card->addr = pci_resource_start(pdev, 1); 111262306a36Sopenharmony_ci card->irq = pdev->irq; 111362306a36Sopenharmony_ci pci_set_drvdata(pdev, card); 111462306a36Sopenharmony_ci err = setup_instance(card); 111562306a36Sopenharmony_ci if (err) 111662306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 111762306a36Sopenharmony_ci return err; 111862306a36Sopenharmony_ci} 111962306a36Sopenharmony_ci 112062306a36Sopenharmony_cistatic void 112162306a36Sopenharmony_cifritz_remove_pci(struct pci_dev *pdev) 112262306a36Sopenharmony_ci{ 112362306a36Sopenharmony_ci struct fritzcard *card = pci_get_drvdata(pdev); 112462306a36Sopenharmony_ci 112562306a36Sopenharmony_ci if (card) 112662306a36Sopenharmony_ci release_card(card); 112762306a36Sopenharmony_ci else 112862306a36Sopenharmony_ci if (debug) 112962306a36Sopenharmony_ci pr_info("%s: drvdata already removed\n", __func__); 113062306a36Sopenharmony_ci} 113162306a36Sopenharmony_ci 113262306a36Sopenharmony_cistatic const struct pci_device_id fcpci_ids[] = { 113362306a36Sopenharmony_ci { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1, PCI_ANY_ID, PCI_ANY_ID, 113462306a36Sopenharmony_ci 0, 0, (unsigned long) "Fritz!Card PCI"}, 113562306a36Sopenharmony_ci { PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1_V2, PCI_ANY_ID, PCI_ANY_ID, 113662306a36Sopenharmony_ci 0, 0, (unsigned long) "Fritz!Card PCI v2" }, 113762306a36Sopenharmony_ci { } 113862306a36Sopenharmony_ci}; 113962306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, fcpci_ids); 114062306a36Sopenharmony_ci 114162306a36Sopenharmony_cistatic struct pci_driver fcpci_driver = { 114262306a36Sopenharmony_ci .name = "fcpci", 114362306a36Sopenharmony_ci .probe = fritzpci_probe, 114462306a36Sopenharmony_ci .remove = fritz_remove_pci, 114562306a36Sopenharmony_ci .id_table = fcpci_ids, 114662306a36Sopenharmony_ci}; 114762306a36Sopenharmony_ci 114862306a36Sopenharmony_cistatic int __init AVM_init(void) 114962306a36Sopenharmony_ci{ 115062306a36Sopenharmony_ci int err; 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci pr_notice("AVM Fritz PCI driver Rev. %s\n", AVMFRITZ_REV); 115362306a36Sopenharmony_ci err = pci_register_driver(&fcpci_driver); 115462306a36Sopenharmony_ci return err; 115562306a36Sopenharmony_ci} 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_cistatic void __exit AVM_cleanup(void) 115862306a36Sopenharmony_ci{ 115962306a36Sopenharmony_ci pci_unregister_driver(&fcpci_driver); 116062306a36Sopenharmony_ci} 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_cimodule_init(AVM_init); 116362306a36Sopenharmony_cimodule_exit(AVM_cleanup); 1164