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