18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * avm_fritz.c    low level stuff for AVM FRITZ!CARD PCI ISDN cards
48c2ecf20Sopenharmony_ci *                Thanks to AVM, Berlin for informations
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Author       Karsten Keil <keil@isdn4linux.de>
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/pci.h>
138c2ecf20Sopenharmony_ci#include <linux/delay.h>
148c2ecf20Sopenharmony_ci#include <linux/mISDNhw.h>
158c2ecf20Sopenharmony_ci#include <linux/slab.h>
168c2ecf20Sopenharmony_ci#include <asm/unaligned.h>
178c2ecf20Sopenharmony_ci#include "ipac.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#define AVMFRITZ_REV	"2.3"
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_cistatic int AVM_cnt;
238c2ecf20Sopenharmony_cistatic int debug;
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cienum {
268c2ecf20Sopenharmony_ci	AVM_FRITZ_PCI,
278c2ecf20Sopenharmony_ci	AVM_FRITZ_PCIV2,
288c2ecf20Sopenharmony_ci};
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define HDLC_FIFO		0x0
318c2ecf20Sopenharmony_ci#define HDLC_STATUS		0x4
328c2ecf20Sopenharmony_ci#define CHIP_WINDOW		0x10
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#define CHIP_INDEX		0x4
358c2ecf20Sopenharmony_ci#define AVM_HDLC_1		0x00
368c2ecf20Sopenharmony_ci#define AVM_HDLC_2		0x01
378c2ecf20Sopenharmony_ci#define AVM_ISAC_FIFO		0x02
388c2ecf20Sopenharmony_ci#define AVM_ISAC_REG_LOW	0x04
398c2ecf20Sopenharmony_ci#define AVM_ISAC_REG_HIGH	0x06
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#define AVM_STATUS0_IRQ_ISAC	0x01
428c2ecf20Sopenharmony_ci#define AVM_STATUS0_IRQ_HDLC	0x02
438c2ecf20Sopenharmony_ci#define AVM_STATUS0_IRQ_TIMER	0x04
448c2ecf20Sopenharmony_ci#define AVM_STATUS0_IRQ_MASK	0x07
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci#define AVM_STATUS0_RESET	0x01
478c2ecf20Sopenharmony_ci#define AVM_STATUS0_DIS_TIMER	0x02
488c2ecf20Sopenharmony_ci#define AVM_STATUS0_RES_TIMER	0x04
498c2ecf20Sopenharmony_ci#define AVM_STATUS0_ENA_IRQ	0x08
508c2ecf20Sopenharmony_ci#define AVM_STATUS0_TESTBIT	0x10
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci#define AVM_STATUS1_INT_SEL	0x0f
538c2ecf20Sopenharmony_ci#define AVM_STATUS1_ENA_IOM	0x80
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci#define HDLC_MODE_ITF_FLG	0x01
568c2ecf20Sopenharmony_ci#define HDLC_MODE_TRANS		0x02
578c2ecf20Sopenharmony_ci#define HDLC_MODE_CCR_7		0x04
588c2ecf20Sopenharmony_ci#define HDLC_MODE_CCR_16	0x08
598c2ecf20Sopenharmony_ci#define HDLC_FIFO_SIZE_128	0x20
608c2ecf20Sopenharmony_ci#define HDLC_MODE_TESTLOOP	0x80
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci#define HDLC_INT_XPR		0x80
638c2ecf20Sopenharmony_ci#define HDLC_INT_XDU		0x40
648c2ecf20Sopenharmony_ci#define HDLC_INT_RPR		0x20
658c2ecf20Sopenharmony_ci#define HDLC_INT_MASK		0xE0
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci#define HDLC_STAT_RME		0x01
688c2ecf20Sopenharmony_ci#define HDLC_STAT_RDO		0x10
698c2ecf20Sopenharmony_ci#define HDLC_STAT_CRCVFRRAB	0x0E
708c2ecf20Sopenharmony_ci#define HDLC_STAT_CRCVFR	0x06
718c2ecf20Sopenharmony_ci#define HDLC_STAT_RML_MASK_V1	0x3f00
728c2ecf20Sopenharmony_ci#define HDLC_STAT_RML_MASK_V2	0x7f00
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci#define HDLC_CMD_XRS		0x80
758c2ecf20Sopenharmony_ci#define HDLC_CMD_XME		0x01
768c2ecf20Sopenharmony_ci#define HDLC_CMD_RRS		0x20
778c2ecf20Sopenharmony_ci#define HDLC_CMD_XML_MASK	0x3f00
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci#define HDLC_FIFO_SIZE_V1	32
808c2ecf20Sopenharmony_ci#define HDLC_FIFO_SIZE_V2	128
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci/* Fritz PCI v2.0 */
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci#define AVM_HDLC_FIFO_1		0x10
858c2ecf20Sopenharmony_ci#define AVM_HDLC_FIFO_2		0x18
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci#define AVM_HDLC_STATUS_1	0x14
888c2ecf20Sopenharmony_ci#define AVM_HDLC_STATUS_2	0x1c
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci#define AVM_ISACX_INDEX		0x04
918c2ecf20Sopenharmony_ci#define AVM_ISACX_DATA		0x08
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/* data struct */
948c2ecf20Sopenharmony_ci#define LOG_SIZE		63
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistruct hdlc_stat_reg {
978c2ecf20Sopenharmony_ci#ifdef __BIG_ENDIAN
988c2ecf20Sopenharmony_ci	u8 fill;
998c2ecf20Sopenharmony_ci	u8 mode;
1008c2ecf20Sopenharmony_ci	u8 xml;
1018c2ecf20Sopenharmony_ci	u8 cmd;
1028c2ecf20Sopenharmony_ci#else
1038c2ecf20Sopenharmony_ci	u8 cmd;
1048c2ecf20Sopenharmony_ci	u8 xml;
1058c2ecf20Sopenharmony_ci	u8 mode;
1068c2ecf20Sopenharmony_ci	u8 fill;
1078c2ecf20Sopenharmony_ci#endif
1088c2ecf20Sopenharmony_ci} __attribute__((packed));
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_cistruct hdlc_hw {
1118c2ecf20Sopenharmony_ci	union {
1128c2ecf20Sopenharmony_ci		u32 ctrl;
1138c2ecf20Sopenharmony_ci		struct hdlc_stat_reg sr;
1148c2ecf20Sopenharmony_ci	} ctrl;
1158c2ecf20Sopenharmony_ci	u32 stat;
1168c2ecf20Sopenharmony_ci};
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_cistruct fritzcard {
1198c2ecf20Sopenharmony_ci	struct list_head	list;
1208c2ecf20Sopenharmony_ci	struct pci_dev		*pdev;
1218c2ecf20Sopenharmony_ci	char			name[MISDN_MAX_IDLEN];
1228c2ecf20Sopenharmony_ci	u8			type;
1238c2ecf20Sopenharmony_ci	u8			ctrlreg;
1248c2ecf20Sopenharmony_ci	u16			irq;
1258c2ecf20Sopenharmony_ci	u32			irqcnt;
1268c2ecf20Sopenharmony_ci	u32			addr;
1278c2ecf20Sopenharmony_ci	spinlock_t		lock; /* hw lock */
1288c2ecf20Sopenharmony_ci	struct isac_hw		isac;
1298c2ecf20Sopenharmony_ci	struct hdlc_hw		hdlc[2];
1308c2ecf20Sopenharmony_ci	struct bchannel		bch[2];
1318c2ecf20Sopenharmony_ci	char			log[LOG_SIZE + 1];
1328c2ecf20Sopenharmony_ci};
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_cistatic LIST_HEAD(Cards);
1358c2ecf20Sopenharmony_cistatic DEFINE_RWLOCK(card_lock); /* protect Cards */
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_cistatic void
1388c2ecf20Sopenharmony_ci_set_debug(struct fritzcard *card)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	card->isac.dch.debug = debug;
1418c2ecf20Sopenharmony_ci	card->bch[0].debug = debug;
1428c2ecf20Sopenharmony_ci	card->bch[1].debug = debug;
1438c2ecf20Sopenharmony_ci}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic int
1468c2ecf20Sopenharmony_ciset_debug(const char *val, const struct kernel_param *kp)
1478c2ecf20Sopenharmony_ci{
1488c2ecf20Sopenharmony_ci	int ret;
1498c2ecf20Sopenharmony_ci	struct fritzcard *card;
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci	ret = param_set_uint(val, kp);
1528c2ecf20Sopenharmony_ci	if (!ret) {
1538c2ecf20Sopenharmony_ci		read_lock(&card_lock);
1548c2ecf20Sopenharmony_ci		list_for_each_entry(card, &Cards, list)
1558c2ecf20Sopenharmony_ci			_set_debug(card);
1568c2ecf20Sopenharmony_ci		read_unlock(&card_lock);
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci	return ret;
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ciMODULE_AUTHOR("Karsten Keil");
1628c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
1638c2ecf20Sopenharmony_ciMODULE_VERSION(AVMFRITZ_REV);
1648c2ecf20Sopenharmony_cimodule_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
1658c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug, "avmfritz debug mask");
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci/* Interface functions */
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_cistatic u8
1708c2ecf20Sopenharmony_ciReadISAC_V1(void *p, u8 offset)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	struct fritzcard *fc = p;
1738c2ecf20Sopenharmony_ci	u8 idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ci	outb(idx, fc->addr + CHIP_INDEX);
1768c2ecf20Sopenharmony_ci	return inb(fc->addr + CHIP_WINDOW + (offset & 0xf));
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic void
1808c2ecf20Sopenharmony_ciWriteISAC_V1(void *p, u8 offset, u8 value)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	struct fritzcard *fc = p;
1838c2ecf20Sopenharmony_ci	u8 idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	outb(idx, fc->addr + CHIP_INDEX);
1868c2ecf20Sopenharmony_ci	outb(value, fc->addr + CHIP_WINDOW + (offset & 0xf));
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic void
1908c2ecf20Sopenharmony_ciReadFiFoISAC_V1(void *p, u8 off, u8 *data, int size)
1918c2ecf20Sopenharmony_ci{
1928c2ecf20Sopenharmony_ci	struct fritzcard *fc = p;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	outb(AVM_ISAC_FIFO, fc->addr + CHIP_INDEX);
1958c2ecf20Sopenharmony_ci	insb(fc->addr + CHIP_WINDOW, data, size);
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic void
1998c2ecf20Sopenharmony_ciWriteFiFoISAC_V1(void *p, u8 off, u8 *data, int size)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	struct fritzcard *fc = p;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	outb(AVM_ISAC_FIFO, fc->addr + CHIP_INDEX);
2048c2ecf20Sopenharmony_ci	outsb(fc->addr + CHIP_WINDOW, data, size);
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic u8
2088c2ecf20Sopenharmony_ciReadISAC_V2(void *p, u8 offset)
2098c2ecf20Sopenharmony_ci{
2108c2ecf20Sopenharmony_ci	struct fritzcard *fc = p;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	outl(offset, fc->addr + AVM_ISACX_INDEX);
2138c2ecf20Sopenharmony_ci	return 0xff & inl(fc->addr + AVM_ISACX_DATA);
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cistatic void
2178c2ecf20Sopenharmony_ciWriteISAC_V2(void *p, u8 offset, u8 value)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	struct fritzcard *fc = p;
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_ci	outl(offset, fc->addr + AVM_ISACX_INDEX);
2228c2ecf20Sopenharmony_ci	outl(value, fc->addr + AVM_ISACX_DATA);
2238c2ecf20Sopenharmony_ci}
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_cistatic void
2268c2ecf20Sopenharmony_ciReadFiFoISAC_V2(void *p, u8 off, u8 *data, int size)
2278c2ecf20Sopenharmony_ci{
2288c2ecf20Sopenharmony_ci	struct fritzcard *fc = p;
2298c2ecf20Sopenharmony_ci	int i;
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	outl(off, fc->addr + AVM_ISACX_INDEX);
2328c2ecf20Sopenharmony_ci	for (i = 0; i < size; i++)
2338c2ecf20Sopenharmony_ci		data[i] = 0xff & inl(fc->addr + AVM_ISACX_DATA);
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic void
2378c2ecf20Sopenharmony_ciWriteFiFoISAC_V2(void *p, u8 off, u8 *data, int size)
2388c2ecf20Sopenharmony_ci{
2398c2ecf20Sopenharmony_ci	struct fritzcard *fc = p;
2408c2ecf20Sopenharmony_ci	int i;
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	outl(off, fc->addr + AVM_ISACX_INDEX);
2438c2ecf20Sopenharmony_ci	for (i = 0; i < size; i++)
2448c2ecf20Sopenharmony_ci		outl(data[i], fc->addr + AVM_ISACX_DATA);
2458c2ecf20Sopenharmony_ci}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_cistatic struct bchannel *
2488c2ecf20Sopenharmony_ciSel_BCS(struct fritzcard *fc, u32 channel)
2498c2ecf20Sopenharmony_ci{
2508c2ecf20Sopenharmony_ci	if (test_bit(FLG_ACTIVE, &fc->bch[0].Flags) &&
2518c2ecf20Sopenharmony_ci	    (fc->bch[0].nr & channel))
2528c2ecf20Sopenharmony_ci		return &fc->bch[0];
2538c2ecf20Sopenharmony_ci	else if (test_bit(FLG_ACTIVE, &fc->bch[1].Flags) &&
2548c2ecf20Sopenharmony_ci		 (fc->bch[1].nr & channel))
2558c2ecf20Sopenharmony_ci		return &fc->bch[1];
2568c2ecf20Sopenharmony_ci	else
2578c2ecf20Sopenharmony_ci		return NULL;
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_cistatic inline void
2618c2ecf20Sopenharmony_ci__write_ctrl_pci(struct fritzcard *fc, struct hdlc_hw *hdlc, u32 channel) {
2628c2ecf20Sopenharmony_ci	u32 idx = channel == 2 ? AVM_HDLC_2 : AVM_HDLC_1;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	outl(idx, fc->addr + CHIP_INDEX);
2658c2ecf20Sopenharmony_ci	outl(hdlc->ctrl.ctrl, fc->addr + CHIP_WINDOW + HDLC_STATUS);
2668c2ecf20Sopenharmony_ci}
2678c2ecf20Sopenharmony_ci
2688c2ecf20Sopenharmony_cistatic inline void
2698c2ecf20Sopenharmony_ci__write_ctrl_pciv2(struct fritzcard *fc, struct hdlc_hw *hdlc, u32 channel) {
2708c2ecf20Sopenharmony_ci	outl(hdlc->ctrl.ctrl, fc->addr + (channel == 2 ? AVM_HDLC_STATUS_2 :
2718c2ecf20Sopenharmony_ci					  AVM_HDLC_STATUS_1));
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic void
2758c2ecf20Sopenharmony_ciwrite_ctrl(struct bchannel *bch, int which) {
2768c2ecf20Sopenharmony_ci	struct fritzcard *fc = bch->hw;
2778c2ecf20Sopenharmony_ci	struct hdlc_hw *hdlc;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	hdlc = &fc->hdlc[(bch->nr - 1) & 1];
2808c2ecf20Sopenharmony_ci	pr_debug("%s: hdlc %c wr%x ctrl %x\n", fc->name, '@' + bch->nr,
2818c2ecf20Sopenharmony_ci		 which, hdlc->ctrl.ctrl);
2828c2ecf20Sopenharmony_ci	switch (fc->type) {
2838c2ecf20Sopenharmony_ci	case AVM_FRITZ_PCIV2:
2848c2ecf20Sopenharmony_ci		__write_ctrl_pciv2(fc, hdlc, bch->nr);
2858c2ecf20Sopenharmony_ci		break;
2868c2ecf20Sopenharmony_ci	case AVM_FRITZ_PCI:
2878c2ecf20Sopenharmony_ci		__write_ctrl_pci(fc, hdlc, bch->nr);
2888c2ecf20Sopenharmony_ci		break;
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci}
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_cistatic inline u32
2948c2ecf20Sopenharmony_ci__read_status_pci(u_long addr, u32 channel)
2958c2ecf20Sopenharmony_ci{
2968c2ecf20Sopenharmony_ci	outl(channel == 2 ? AVM_HDLC_2 : AVM_HDLC_1, addr + CHIP_INDEX);
2978c2ecf20Sopenharmony_ci	return inl(addr + CHIP_WINDOW + HDLC_STATUS);
2988c2ecf20Sopenharmony_ci}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic inline u32
3018c2ecf20Sopenharmony_ci__read_status_pciv2(u_long addr, u32 channel)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	return inl(addr + (channel == 2 ? AVM_HDLC_STATUS_2 :
3048c2ecf20Sopenharmony_ci			   AVM_HDLC_STATUS_1));
3058c2ecf20Sopenharmony_ci}
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_cistatic u32
3098c2ecf20Sopenharmony_ciread_status(struct fritzcard *fc, u32 channel)
3108c2ecf20Sopenharmony_ci{
3118c2ecf20Sopenharmony_ci	switch (fc->type) {
3128c2ecf20Sopenharmony_ci	case AVM_FRITZ_PCIV2:
3138c2ecf20Sopenharmony_ci		return __read_status_pciv2(fc->addr, channel);
3148c2ecf20Sopenharmony_ci	case AVM_FRITZ_PCI:
3158c2ecf20Sopenharmony_ci		return __read_status_pci(fc->addr, channel);
3168c2ecf20Sopenharmony_ci	}
3178c2ecf20Sopenharmony_ci	/* dummy */
3188c2ecf20Sopenharmony_ci	return 0;
3198c2ecf20Sopenharmony_ci}
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_cistatic void
3228c2ecf20Sopenharmony_cienable_hwirq(struct fritzcard *fc)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	fc->ctrlreg |= AVM_STATUS0_ENA_IRQ;
3258c2ecf20Sopenharmony_ci	outb(fc->ctrlreg, fc->addr + 2);
3268c2ecf20Sopenharmony_ci}
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_cistatic void
3298c2ecf20Sopenharmony_cidisable_hwirq(struct fritzcard *fc)
3308c2ecf20Sopenharmony_ci{
3318c2ecf20Sopenharmony_ci	fc->ctrlreg &= ~AVM_STATUS0_ENA_IRQ;
3328c2ecf20Sopenharmony_ci	outb(fc->ctrlreg, fc->addr + 2);
3338c2ecf20Sopenharmony_ci}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_cistatic int
3368c2ecf20Sopenharmony_cimodehdlc(struct bchannel *bch, int protocol)
3378c2ecf20Sopenharmony_ci{
3388c2ecf20Sopenharmony_ci	struct fritzcard *fc = bch->hw;
3398c2ecf20Sopenharmony_ci	struct hdlc_hw *hdlc;
3408c2ecf20Sopenharmony_ci	u8 mode;
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	hdlc = &fc->hdlc[(bch->nr - 1) & 1];
3438c2ecf20Sopenharmony_ci	pr_debug("%s: hdlc %c protocol %x-->%x ch %d\n", fc->name,
3448c2ecf20Sopenharmony_ci		 '@' + bch->nr, bch->state, protocol, bch->nr);
3458c2ecf20Sopenharmony_ci	hdlc->ctrl.ctrl = 0;
3468c2ecf20Sopenharmony_ci	mode = (fc->type == AVM_FRITZ_PCIV2) ? HDLC_FIFO_SIZE_128 : 0;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	switch (protocol) {
3498c2ecf20Sopenharmony_ci	case -1: /* used for init */
3508c2ecf20Sopenharmony_ci		bch->state = -1;
3518c2ecf20Sopenharmony_ci		fallthrough;
3528c2ecf20Sopenharmony_ci	case ISDN_P_NONE:
3538c2ecf20Sopenharmony_ci		if (bch->state == ISDN_P_NONE)
3548c2ecf20Sopenharmony_ci			break;
3558c2ecf20Sopenharmony_ci		hdlc->ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
3568c2ecf20Sopenharmony_ci		hdlc->ctrl.sr.mode = mode | HDLC_MODE_TRANS;
3578c2ecf20Sopenharmony_ci		write_ctrl(bch, 5);
3588c2ecf20Sopenharmony_ci		bch->state = ISDN_P_NONE;
3598c2ecf20Sopenharmony_ci		test_and_clear_bit(FLG_HDLC, &bch->Flags);
3608c2ecf20Sopenharmony_ci		test_and_clear_bit(FLG_TRANSPARENT, &bch->Flags);
3618c2ecf20Sopenharmony_ci		break;
3628c2ecf20Sopenharmony_ci	case ISDN_P_B_RAW:
3638c2ecf20Sopenharmony_ci		bch->state = protocol;
3648c2ecf20Sopenharmony_ci		hdlc->ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
3658c2ecf20Sopenharmony_ci		hdlc->ctrl.sr.mode = mode | HDLC_MODE_TRANS;
3668c2ecf20Sopenharmony_ci		write_ctrl(bch, 5);
3678c2ecf20Sopenharmony_ci		hdlc->ctrl.sr.cmd = HDLC_CMD_XRS;
3688c2ecf20Sopenharmony_ci		write_ctrl(bch, 1);
3698c2ecf20Sopenharmony_ci		hdlc->ctrl.sr.cmd = 0;
3708c2ecf20Sopenharmony_ci		test_and_set_bit(FLG_TRANSPARENT, &bch->Flags);
3718c2ecf20Sopenharmony_ci		break;
3728c2ecf20Sopenharmony_ci	case ISDN_P_B_HDLC:
3738c2ecf20Sopenharmony_ci		bch->state = protocol;
3748c2ecf20Sopenharmony_ci		hdlc->ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
3758c2ecf20Sopenharmony_ci		hdlc->ctrl.sr.mode = mode | HDLC_MODE_ITF_FLG;
3768c2ecf20Sopenharmony_ci		write_ctrl(bch, 5);
3778c2ecf20Sopenharmony_ci		hdlc->ctrl.sr.cmd = HDLC_CMD_XRS;
3788c2ecf20Sopenharmony_ci		write_ctrl(bch, 1);
3798c2ecf20Sopenharmony_ci		hdlc->ctrl.sr.cmd = 0;
3808c2ecf20Sopenharmony_ci		test_and_set_bit(FLG_HDLC, &bch->Flags);
3818c2ecf20Sopenharmony_ci		break;
3828c2ecf20Sopenharmony_ci	default:
3838c2ecf20Sopenharmony_ci		pr_info("%s: protocol not known %x\n", fc->name, protocol);
3848c2ecf20Sopenharmony_ci		return -ENOPROTOOPT;
3858c2ecf20Sopenharmony_ci	}
3868c2ecf20Sopenharmony_ci	return 0;
3878c2ecf20Sopenharmony_ci}
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_cistatic void
3908c2ecf20Sopenharmony_cihdlc_empty_fifo(struct bchannel *bch, int count)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	u32 *ptr;
3938c2ecf20Sopenharmony_ci	u8 *p;
3948c2ecf20Sopenharmony_ci	u32  val, addr;
3958c2ecf20Sopenharmony_ci	int cnt;
3968c2ecf20Sopenharmony_ci	struct fritzcard *fc = bch->hw;
3978c2ecf20Sopenharmony_ci
3988c2ecf20Sopenharmony_ci	pr_debug("%s: %s %d\n", fc->name, __func__, count);
3998c2ecf20Sopenharmony_ci	if (test_bit(FLG_RX_OFF, &bch->Flags)) {
4008c2ecf20Sopenharmony_ci		p = NULL;
4018c2ecf20Sopenharmony_ci		bch->dropcnt += count;
4028c2ecf20Sopenharmony_ci	} else {
4038c2ecf20Sopenharmony_ci		cnt = bchannel_get_rxbuf(bch, count);
4048c2ecf20Sopenharmony_ci		if (cnt < 0) {
4058c2ecf20Sopenharmony_ci			pr_warn("%s.B%d: No bufferspace for %d bytes\n",
4068c2ecf20Sopenharmony_ci				fc->name, bch->nr, count);
4078c2ecf20Sopenharmony_ci			return;
4088c2ecf20Sopenharmony_ci		}
4098c2ecf20Sopenharmony_ci		p = skb_put(bch->rx_skb, count);
4108c2ecf20Sopenharmony_ci	}
4118c2ecf20Sopenharmony_ci	ptr = (u32 *)p;
4128c2ecf20Sopenharmony_ci	if (fc->type == AVM_FRITZ_PCIV2)
4138c2ecf20Sopenharmony_ci		addr = fc->addr + (bch->nr == 2 ?
4148c2ecf20Sopenharmony_ci				   AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1);
4158c2ecf20Sopenharmony_ci	else {
4168c2ecf20Sopenharmony_ci		addr = fc->addr + CHIP_WINDOW;
4178c2ecf20Sopenharmony_ci		outl(bch->nr == 2 ? AVM_HDLC_2 : AVM_HDLC_1, fc->addr);
4188c2ecf20Sopenharmony_ci	}
4198c2ecf20Sopenharmony_ci	cnt = 0;
4208c2ecf20Sopenharmony_ci	while (cnt < count) {
4218c2ecf20Sopenharmony_ci		val = le32_to_cpu(inl(addr));
4228c2ecf20Sopenharmony_ci		if (p) {
4238c2ecf20Sopenharmony_ci			put_unaligned(val, ptr);
4248c2ecf20Sopenharmony_ci			ptr++;
4258c2ecf20Sopenharmony_ci		}
4268c2ecf20Sopenharmony_ci		cnt += 4;
4278c2ecf20Sopenharmony_ci	}
4288c2ecf20Sopenharmony_ci	if (p && (debug & DEBUG_HW_BFIFO)) {
4298c2ecf20Sopenharmony_ci		snprintf(fc->log, LOG_SIZE, "B%1d-recv %s %d ",
4308c2ecf20Sopenharmony_ci			 bch->nr, fc->name, count);
4318c2ecf20Sopenharmony_ci		print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count);
4328c2ecf20Sopenharmony_ci	}
4338c2ecf20Sopenharmony_ci}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_cistatic void
4368c2ecf20Sopenharmony_cihdlc_fill_fifo(struct bchannel *bch)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	struct fritzcard *fc = bch->hw;
4398c2ecf20Sopenharmony_ci	struct hdlc_hw *hdlc;
4408c2ecf20Sopenharmony_ci	int count, fs, cnt = 0, idx;
4418c2ecf20Sopenharmony_ci	bool fillempty = false;
4428c2ecf20Sopenharmony_ci	u8 *p;
4438c2ecf20Sopenharmony_ci	u32 *ptr, val, addr;
4448c2ecf20Sopenharmony_ci
4458c2ecf20Sopenharmony_ci	idx = (bch->nr - 1) & 1;
4468c2ecf20Sopenharmony_ci	hdlc = &fc->hdlc[idx];
4478c2ecf20Sopenharmony_ci	fs = (fc->type == AVM_FRITZ_PCIV2) ?
4488c2ecf20Sopenharmony_ci		HDLC_FIFO_SIZE_V2 : HDLC_FIFO_SIZE_V1;
4498c2ecf20Sopenharmony_ci	if (!bch->tx_skb) {
4508c2ecf20Sopenharmony_ci		if (!test_bit(FLG_TX_EMPTY, &bch->Flags))
4518c2ecf20Sopenharmony_ci			return;
4528c2ecf20Sopenharmony_ci		count = fs;
4538c2ecf20Sopenharmony_ci		p = bch->fill;
4548c2ecf20Sopenharmony_ci		fillempty = true;
4558c2ecf20Sopenharmony_ci	} else {
4568c2ecf20Sopenharmony_ci		count = bch->tx_skb->len - bch->tx_idx;
4578c2ecf20Sopenharmony_ci		if (count <= 0)
4588c2ecf20Sopenharmony_ci			return;
4598c2ecf20Sopenharmony_ci		p = bch->tx_skb->data + bch->tx_idx;
4608c2ecf20Sopenharmony_ci	}
4618c2ecf20Sopenharmony_ci	hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XME;
4628c2ecf20Sopenharmony_ci	if (count > fs) {
4638c2ecf20Sopenharmony_ci		count = fs;
4648c2ecf20Sopenharmony_ci	} else {
4658c2ecf20Sopenharmony_ci		if (test_bit(FLG_HDLC, &bch->Flags))
4668c2ecf20Sopenharmony_ci			hdlc->ctrl.sr.cmd |= HDLC_CMD_XME;
4678c2ecf20Sopenharmony_ci	}
4688c2ecf20Sopenharmony_ci	ptr = (u32 *)p;
4698c2ecf20Sopenharmony_ci	if (!fillempty) {
4708c2ecf20Sopenharmony_ci		pr_debug("%s.B%d: %d/%d/%d", fc->name, bch->nr, count,
4718c2ecf20Sopenharmony_ci			 bch->tx_idx, bch->tx_skb->len);
4728c2ecf20Sopenharmony_ci		bch->tx_idx += count;
4738c2ecf20Sopenharmony_ci	} else {
4748c2ecf20Sopenharmony_ci		pr_debug("%s.B%d: fillempty %d\n", fc->name, bch->nr, count);
4758c2ecf20Sopenharmony_ci	}
4768c2ecf20Sopenharmony_ci	hdlc->ctrl.sr.xml = ((count == fs) ? 0 : count);
4778c2ecf20Sopenharmony_ci	if (fc->type == AVM_FRITZ_PCIV2) {
4788c2ecf20Sopenharmony_ci		__write_ctrl_pciv2(fc, hdlc, bch->nr);
4798c2ecf20Sopenharmony_ci		addr = fc->addr + (bch->nr == 2 ?
4808c2ecf20Sopenharmony_ci				   AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1);
4818c2ecf20Sopenharmony_ci	} else {
4828c2ecf20Sopenharmony_ci		__write_ctrl_pci(fc, hdlc, bch->nr);
4838c2ecf20Sopenharmony_ci		addr = fc->addr + CHIP_WINDOW;
4848c2ecf20Sopenharmony_ci	}
4858c2ecf20Sopenharmony_ci	if (fillempty) {
4868c2ecf20Sopenharmony_ci		while (cnt < count) {
4878c2ecf20Sopenharmony_ci			/* all bytes the same - no worry about endian */
4888c2ecf20Sopenharmony_ci			outl(*ptr, addr);
4898c2ecf20Sopenharmony_ci			cnt += 4;
4908c2ecf20Sopenharmony_ci		}
4918c2ecf20Sopenharmony_ci	} else {
4928c2ecf20Sopenharmony_ci		while (cnt < count) {
4938c2ecf20Sopenharmony_ci			val = get_unaligned(ptr);
4948c2ecf20Sopenharmony_ci			outl(cpu_to_le32(val), addr);
4958c2ecf20Sopenharmony_ci			ptr++;
4968c2ecf20Sopenharmony_ci			cnt += 4;
4978c2ecf20Sopenharmony_ci		}
4988c2ecf20Sopenharmony_ci	}
4998c2ecf20Sopenharmony_ci	if ((debug & DEBUG_HW_BFIFO) && !fillempty) {
5008c2ecf20Sopenharmony_ci		snprintf(fc->log, LOG_SIZE, "B%1d-send %s %d ",
5018c2ecf20Sopenharmony_ci			 bch->nr, fc->name, count);
5028c2ecf20Sopenharmony_ci		print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count);
5038c2ecf20Sopenharmony_ci	}
5048c2ecf20Sopenharmony_ci}
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_cistatic void
5078c2ecf20Sopenharmony_ciHDLC_irq_xpr(struct bchannel *bch)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len) {
5108c2ecf20Sopenharmony_ci		hdlc_fill_fifo(bch);
5118c2ecf20Sopenharmony_ci	} else {
5128c2ecf20Sopenharmony_ci		dev_kfree_skb(bch->tx_skb);
5138c2ecf20Sopenharmony_ci		if (get_next_bframe(bch)) {
5148c2ecf20Sopenharmony_ci			hdlc_fill_fifo(bch);
5158c2ecf20Sopenharmony_ci			test_and_clear_bit(FLG_TX_EMPTY, &bch->Flags);
5168c2ecf20Sopenharmony_ci		} else if (test_bit(FLG_TX_EMPTY, &bch->Flags)) {
5178c2ecf20Sopenharmony_ci			hdlc_fill_fifo(bch);
5188c2ecf20Sopenharmony_ci		}
5198c2ecf20Sopenharmony_ci	}
5208c2ecf20Sopenharmony_ci}
5218c2ecf20Sopenharmony_ci
5228c2ecf20Sopenharmony_cistatic void
5238c2ecf20Sopenharmony_ciHDLC_irq(struct bchannel *bch, u32 stat)
5248c2ecf20Sopenharmony_ci{
5258c2ecf20Sopenharmony_ci	struct fritzcard *fc = bch->hw;
5268c2ecf20Sopenharmony_ci	int		len, fs;
5278c2ecf20Sopenharmony_ci	u32		rmlMask;
5288c2ecf20Sopenharmony_ci	struct hdlc_hw	*hdlc;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	hdlc = &fc->hdlc[(bch->nr - 1) & 1];
5318c2ecf20Sopenharmony_ci	pr_debug("%s: ch%d stat %#x\n", fc->name, bch->nr, stat);
5328c2ecf20Sopenharmony_ci	if (fc->type == AVM_FRITZ_PCIV2) {
5338c2ecf20Sopenharmony_ci		rmlMask = HDLC_STAT_RML_MASK_V2;
5348c2ecf20Sopenharmony_ci		fs = HDLC_FIFO_SIZE_V2;
5358c2ecf20Sopenharmony_ci	} else {
5368c2ecf20Sopenharmony_ci		rmlMask = HDLC_STAT_RML_MASK_V1;
5378c2ecf20Sopenharmony_ci		fs = HDLC_FIFO_SIZE_V1;
5388c2ecf20Sopenharmony_ci	}
5398c2ecf20Sopenharmony_ci	if (stat & HDLC_INT_RPR) {
5408c2ecf20Sopenharmony_ci		if (stat & HDLC_STAT_RDO) {
5418c2ecf20Sopenharmony_ci			pr_warn("%s: ch%d stat %x RDO\n",
5428c2ecf20Sopenharmony_ci				fc->name, bch->nr, stat);
5438c2ecf20Sopenharmony_ci			hdlc->ctrl.sr.xml = 0;
5448c2ecf20Sopenharmony_ci			hdlc->ctrl.sr.cmd |= HDLC_CMD_RRS;
5458c2ecf20Sopenharmony_ci			write_ctrl(bch, 1);
5468c2ecf20Sopenharmony_ci			hdlc->ctrl.sr.cmd &= ~HDLC_CMD_RRS;
5478c2ecf20Sopenharmony_ci			write_ctrl(bch, 1);
5488c2ecf20Sopenharmony_ci			if (bch->rx_skb)
5498c2ecf20Sopenharmony_ci				skb_trim(bch->rx_skb, 0);
5508c2ecf20Sopenharmony_ci		} else {
5518c2ecf20Sopenharmony_ci			len = (stat & rmlMask) >> 8;
5528c2ecf20Sopenharmony_ci			if (!len)
5538c2ecf20Sopenharmony_ci				len = fs;
5548c2ecf20Sopenharmony_ci			hdlc_empty_fifo(bch, len);
5558c2ecf20Sopenharmony_ci			if (!bch->rx_skb)
5568c2ecf20Sopenharmony_ci				goto handle_tx;
5578c2ecf20Sopenharmony_ci			if (test_bit(FLG_TRANSPARENT, &bch->Flags)) {
5588c2ecf20Sopenharmony_ci				recv_Bchannel(bch, 0, false);
5598c2ecf20Sopenharmony_ci			} else if (stat & HDLC_STAT_RME) {
5608c2ecf20Sopenharmony_ci				if ((stat & HDLC_STAT_CRCVFRRAB) ==
5618c2ecf20Sopenharmony_ci				    HDLC_STAT_CRCVFR) {
5628c2ecf20Sopenharmony_ci					recv_Bchannel(bch, 0, false);
5638c2ecf20Sopenharmony_ci				} else {
5648c2ecf20Sopenharmony_ci					pr_warn("%s: got invalid frame\n",
5658c2ecf20Sopenharmony_ci						fc->name);
5668c2ecf20Sopenharmony_ci					skb_trim(bch->rx_skb, 0);
5678c2ecf20Sopenharmony_ci				}
5688c2ecf20Sopenharmony_ci			}
5698c2ecf20Sopenharmony_ci		}
5708c2ecf20Sopenharmony_ci	}
5718c2ecf20Sopenharmony_cihandle_tx:
5728c2ecf20Sopenharmony_ci	if (stat & HDLC_INT_XDU) {
5738c2ecf20Sopenharmony_ci		/* Here we lost an TX interrupt, so
5748c2ecf20Sopenharmony_ci		 * restart transmitting the whole frame on HDLC
5758c2ecf20Sopenharmony_ci		 * in transparent mode we send the next data
5768c2ecf20Sopenharmony_ci		 */
5778c2ecf20Sopenharmony_ci		pr_warn("%s: ch%d stat %x XDU %s\n", fc->name, bch->nr,
5788c2ecf20Sopenharmony_ci			stat, bch->tx_skb ? "tx_skb" : "no tx_skb");
5798c2ecf20Sopenharmony_ci		if (bch->tx_skb && bch->tx_skb->len) {
5808c2ecf20Sopenharmony_ci			if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
5818c2ecf20Sopenharmony_ci				bch->tx_idx = 0;
5828c2ecf20Sopenharmony_ci		} else if (test_bit(FLG_FILLEMPTY, &bch->Flags)) {
5838c2ecf20Sopenharmony_ci			test_and_set_bit(FLG_TX_EMPTY, &bch->Flags);
5848c2ecf20Sopenharmony_ci		}
5858c2ecf20Sopenharmony_ci		hdlc->ctrl.sr.xml = 0;
5868c2ecf20Sopenharmony_ci		hdlc->ctrl.sr.cmd |= HDLC_CMD_XRS;
5878c2ecf20Sopenharmony_ci		write_ctrl(bch, 1);
5888c2ecf20Sopenharmony_ci		hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XRS;
5898c2ecf20Sopenharmony_ci		HDLC_irq_xpr(bch);
5908c2ecf20Sopenharmony_ci		return;
5918c2ecf20Sopenharmony_ci	} else if (stat & HDLC_INT_XPR)
5928c2ecf20Sopenharmony_ci		HDLC_irq_xpr(bch);
5938c2ecf20Sopenharmony_ci}
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_cistatic inline void
5968c2ecf20Sopenharmony_ciHDLC_irq_main(struct fritzcard *fc)
5978c2ecf20Sopenharmony_ci{
5988c2ecf20Sopenharmony_ci	u32 stat;
5998c2ecf20Sopenharmony_ci	struct bchannel *bch;
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	stat = read_status(fc, 1);
6028c2ecf20Sopenharmony_ci	if (stat & HDLC_INT_MASK) {
6038c2ecf20Sopenharmony_ci		bch = Sel_BCS(fc, 1);
6048c2ecf20Sopenharmony_ci		if (bch)
6058c2ecf20Sopenharmony_ci			HDLC_irq(bch, stat);
6068c2ecf20Sopenharmony_ci		else
6078c2ecf20Sopenharmony_ci			pr_debug("%s: spurious ch1 IRQ\n", fc->name);
6088c2ecf20Sopenharmony_ci	}
6098c2ecf20Sopenharmony_ci	stat = read_status(fc, 2);
6108c2ecf20Sopenharmony_ci	if (stat & HDLC_INT_MASK) {
6118c2ecf20Sopenharmony_ci		bch = Sel_BCS(fc, 2);
6128c2ecf20Sopenharmony_ci		if (bch)
6138c2ecf20Sopenharmony_ci			HDLC_irq(bch, stat);
6148c2ecf20Sopenharmony_ci		else
6158c2ecf20Sopenharmony_ci			pr_debug("%s: spurious ch2 IRQ\n", fc->name);
6168c2ecf20Sopenharmony_ci	}
6178c2ecf20Sopenharmony_ci}
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_cistatic irqreturn_t
6208c2ecf20Sopenharmony_ciavm_fritz_interrupt(int intno, void *dev_id)
6218c2ecf20Sopenharmony_ci{
6228c2ecf20Sopenharmony_ci	struct fritzcard *fc = dev_id;
6238c2ecf20Sopenharmony_ci	u8 val;
6248c2ecf20Sopenharmony_ci	u8 sval;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci	spin_lock(&fc->lock);
6278c2ecf20Sopenharmony_ci	sval = inb(fc->addr + 2);
6288c2ecf20Sopenharmony_ci	pr_debug("%s: irq stat0 %x\n", fc->name, sval);
6298c2ecf20Sopenharmony_ci	if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK) {
6308c2ecf20Sopenharmony_ci		/* shared  IRQ from other HW */
6318c2ecf20Sopenharmony_ci		spin_unlock(&fc->lock);
6328c2ecf20Sopenharmony_ci		return IRQ_NONE;
6338c2ecf20Sopenharmony_ci	}
6348c2ecf20Sopenharmony_ci	fc->irqcnt++;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci	if (!(sval & AVM_STATUS0_IRQ_ISAC)) {
6378c2ecf20Sopenharmony_ci		val = ReadISAC_V1(fc, ISAC_ISTA);
6388c2ecf20Sopenharmony_ci		mISDNisac_irq(&fc->isac, val);
6398c2ecf20Sopenharmony_ci	}
6408c2ecf20Sopenharmony_ci	if (!(sval & AVM_STATUS0_IRQ_HDLC))
6418c2ecf20Sopenharmony_ci		HDLC_irq_main(fc);
6428c2ecf20Sopenharmony_ci	spin_unlock(&fc->lock);
6438c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
6448c2ecf20Sopenharmony_ci}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_cistatic irqreturn_t
6478c2ecf20Sopenharmony_ciavm_fritzv2_interrupt(int intno, void *dev_id)
6488c2ecf20Sopenharmony_ci{
6498c2ecf20Sopenharmony_ci	struct fritzcard *fc = dev_id;
6508c2ecf20Sopenharmony_ci	u8 val;
6518c2ecf20Sopenharmony_ci	u8 sval;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	spin_lock(&fc->lock);
6548c2ecf20Sopenharmony_ci	sval = inb(fc->addr + 2);
6558c2ecf20Sopenharmony_ci	pr_debug("%s: irq stat0 %x\n", fc->name, sval);
6568c2ecf20Sopenharmony_ci	if (!(sval & AVM_STATUS0_IRQ_MASK)) {
6578c2ecf20Sopenharmony_ci		/* shared  IRQ from other HW */
6588c2ecf20Sopenharmony_ci		spin_unlock(&fc->lock);
6598c2ecf20Sopenharmony_ci		return IRQ_NONE;
6608c2ecf20Sopenharmony_ci	}
6618c2ecf20Sopenharmony_ci	fc->irqcnt++;
6628c2ecf20Sopenharmony_ci
6638c2ecf20Sopenharmony_ci	if (sval & AVM_STATUS0_IRQ_HDLC)
6648c2ecf20Sopenharmony_ci		HDLC_irq_main(fc);
6658c2ecf20Sopenharmony_ci	if (sval & AVM_STATUS0_IRQ_ISAC) {
6668c2ecf20Sopenharmony_ci		val = ReadISAC_V2(fc, ISACX_ISTA);
6678c2ecf20Sopenharmony_ci		mISDNisac_irq(&fc->isac, val);
6688c2ecf20Sopenharmony_ci	}
6698c2ecf20Sopenharmony_ci	if (sval & AVM_STATUS0_IRQ_TIMER) {
6708c2ecf20Sopenharmony_ci		pr_debug("%s: timer irq\n", fc->name);
6718c2ecf20Sopenharmony_ci		outb(fc->ctrlreg | AVM_STATUS0_RES_TIMER, fc->addr + 2);
6728c2ecf20Sopenharmony_ci		udelay(1);
6738c2ecf20Sopenharmony_ci		outb(fc->ctrlreg, fc->addr + 2);
6748c2ecf20Sopenharmony_ci	}
6758c2ecf20Sopenharmony_ci	spin_unlock(&fc->lock);
6768c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
6778c2ecf20Sopenharmony_ci}
6788c2ecf20Sopenharmony_ci
6798c2ecf20Sopenharmony_cistatic int
6808c2ecf20Sopenharmony_ciavm_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb)
6818c2ecf20Sopenharmony_ci{
6828c2ecf20Sopenharmony_ci	struct bchannel *bch = container_of(ch, struct bchannel, ch);
6838c2ecf20Sopenharmony_ci	struct fritzcard *fc = bch->hw;
6848c2ecf20Sopenharmony_ci	int ret = -EINVAL;
6858c2ecf20Sopenharmony_ci	struct mISDNhead *hh = mISDN_HEAD_P(skb);
6868c2ecf20Sopenharmony_ci	unsigned long flags;
6878c2ecf20Sopenharmony_ci
6888c2ecf20Sopenharmony_ci	switch (hh->prim) {
6898c2ecf20Sopenharmony_ci	case PH_DATA_REQ:
6908c2ecf20Sopenharmony_ci		spin_lock_irqsave(&fc->lock, flags);
6918c2ecf20Sopenharmony_ci		ret = bchannel_senddata(bch, skb);
6928c2ecf20Sopenharmony_ci		if (ret > 0) { /* direct TX */
6938c2ecf20Sopenharmony_ci			hdlc_fill_fifo(bch);
6948c2ecf20Sopenharmony_ci			ret = 0;
6958c2ecf20Sopenharmony_ci		}
6968c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fc->lock, flags);
6978c2ecf20Sopenharmony_ci		return ret;
6988c2ecf20Sopenharmony_ci	case PH_ACTIVATE_REQ:
6998c2ecf20Sopenharmony_ci		spin_lock_irqsave(&fc->lock, flags);
7008c2ecf20Sopenharmony_ci		if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
7018c2ecf20Sopenharmony_ci			ret = modehdlc(bch, ch->protocol);
7028c2ecf20Sopenharmony_ci		else
7038c2ecf20Sopenharmony_ci			ret = 0;
7048c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fc->lock, flags);
7058c2ecf20Sopenharmony_ci		if (!ret)
7068c2ecf20Sopenharmony_ci			_queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
7078c2ecf20Sopenharmony_ci				    NULL, GFP_KERNEL);
7088c2ecf20Sopenharmony_ci		break;
7098c2ecf20Sopenharmony_ci	case PH_DEACTIVATE_REQ:
7108c2ecf20Sopenharmony_ci		spin_lock_irqsave(&fc->lock, flags);
7118c2ecf20Sopenharmony_ci		mISDN_clear_bchannel(bch);
7128c2ecf20Sopenharmony_ci		modehdlc(bch, ISDN_P_NONE);
7138c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fc->lock, flags);
7148c2ecf20Sopenharmony_ci		_queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
7158c2ecf20Sopenharmony_ci			    NULL, GFP_KERNEL);
7168c2ecf20Sopenharmony_ci		ret = 0;
7178c2ecf20Sopenharmony_ci		break;
7188c2ecf20Sopenharmony_ci	}
7198c2ecf20Sopenharmony_ci	if (!ret)
7208c2ecf20Sopenharmony_ci		dev_kfree_skb(skb);
7218c2ecf20Sopenharmony_ci	return ret;
7228c2ecf20Sopenharmony_ci}
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_cistatic void
7258c2ecf20Sopenharmony_ciinithdlc(struct fritzcard *fc)
7268c2ecf20Sopenharmony_ci{
7278c2ecf20Sopenharmony_ci	modehdlc(&fc->bch[0], -1);
7288c2ecf20Sopenharmony_ci	modehdlc(&fc->bch[1], -1);
7298c2ecf20Sopenharmony_ci}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_cistatic void
7328c2ecf20Sopenharmony_ciclear_pending_hdlc_ints(struct fritzcard *fc)
7338c2ecf20Sopenharmony_ci{
7348c2ecf20Sopenharmony_ci	u32 val;
7358c2ecf20Sopenharmony_ci
7368c2ecf20Sopenharmony_ci	val = read_status(fc, 1);
7378c2ecf20Sopenharmony_ci	pr_debug("%s: HDLC 1 STA %x\n", fc->name, val);
7388c2ecf20Sopenharmony_ci	val = read_status(fc, 2);
7398c2ecf20Sopenharmony_ci	pr_debug("%s: HDLC 2 STA %x\n", fc->name, val);
7408c2ecf20Sopenharmony_ci}
7418c2ecf20Sopenharmony_ci
7428c2ecf20Sopenharmony_cistatic void
7438c2ecf20Sopenharmony_cireset_avm(struct fritzcard *fc)
7448c2ecf20Sopenharmony_ci{
7458c2ecf20Sopenharmony_ci	switch (fc->type) {
7468c2ecf20Sopenharmony_ci	case AVM_FRITZ_PCI:
7478c2ecf20Sopenharmony_ci		fc->ctrlreg = AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER;
7488c2ecf20Sopenharmony_ci		break;
7498c2ecf20Sopenharmony_ci	case AVM_FRITZ_PCIV2:
7508c2ecf20Sopenharmony_ci		fc->ctrlreg = AVM_STATUS0_RESET;
7518c2ecf20Sopenharmony_ci		break;
7528c2ecf20Sopenharmony_ci	}
7538c2ecf20Sopenharmony_ci	if (debug & DEBUG_HW)
7548c2ecf20Sopenharmony_ci		pr_notice("%s: reset\n", fc->name);
7558c2ecf20Sopenharmony_ci	disable_hwirq(fc);
7568c2ecf20Sopenharmony_ci	mdelay(5);
7578c2ecf20Sopenharmony_ci	switch (fc->type) {
7588c2ecf20Sopenharmony_ci	case AVM_FRITZ_PCI:
7598c2ecf20Sopenharmony_ci		fc->ctrlreg = AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER;
7608c2ecf20Sopenharmony_ci		disable_hwirq(fc);
7618c2ecf20Sopenharmony_ci		outb(AVM_STATUS1_ENA_IOM, fc->addr + 3);
7628c2ecf20Sopenharmony_ci		break;
7638c2ecf20Sopenharmony_ci	case AVM_FRITZ_PCIV2:
7648c2ecf20Sopenharmony_ci		fc->ctrlreg = 0;
7658c2ecf20Sopenharmony_ci		disable_hwirq(fc);
7668c2ecf20Sopenharmony_ci		break;
7678c2ecf20Sopenharmony_ci	}
7688c2ecf20Sopenharmony_ci	mdelay(1);
7698c2ecf20Sopenharmony_ci	if (debug & DEBUG_HW)
7708c2ecf20Sopenharmony_ci		pr_notice("%s: S0/S1 %x/%x\n", fc->name,
7718c2ecf20Sopenharmony_ci			  inb(fc->addr + 2), inb(fc->addr + 3));
7728c2ecf20Sopenharmony_ci}
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_cistatic int
7758c2ecf20Sopenharmony_ciinit_card(struct fritzcard *fc)
7768c2ecf20Sopenharmony_ci{
7778c2ecf20Sopenharmony_ci	int		ret, cnt = 3;
7788c2ecf20Sopenharmony_ci	u_long		flags;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	reset_avm(fc); /* disable IRQ */
7818c2ecf20Sopenharmony_ci	if (fc->type == AVM_FRITZ_PCIV2)
7828c2ecf20Sopenharmony_ci		ret = request_irq(fc->irq, avm_fritzv2_interrupt,
7838c2ecf20Sopenharmony_ci				  IRQF_SHARED, fc->name, fc);
7848c2ecf20Sopenharmony_ci	else
7858c2ecf20Sopenharmony_ci		ret = request_irq(fc->irq, avm_fritz_interrupt,
7868c2ecf20Sopenharmony_ci				  IRQF_SHARED, fc->name, fc);
7878c2ecf20Sopenharmony_ci	if (ret) {
7888c2ecf20Sopenharmony_ci		pr_info("%s: couldn't get interrupt %d\n",
7898c2ecf20Sopenharmony_ci			fc->name, fc->irq);
7908c2ecf20Sopenharmony_ci		return ret;
7918c2ecf20Sopenharmony_ci	}
7928c2ecf20Sopenharmony_ci	while (cnt--) {
7938c2ecf20Sopenharmony_ci		spin_lock_irqsave(&fc->lock, flags);
7948c2ecf20Sopenharmony_ci		ret = fc->isac.init(&fc->isac);
7958c2ecf20Sopenharmony_ci		if (ret) {
7968c2ecf20Sopenharmony_ci			spin_unlock_irqrestore(&fc->lock, flags);
7978c2ecf20Sopenharmony_ci			pr_info("%s: ISAC init failed with %d\n",
7988c2ecf20Sopenharmony_ci				fc->name, ret);
7998c2ecf20Sopenharmony_ci			break;
8008c2ecf20Sopenharmony_ci		}
8018c2ecf20Sopenharmony_ci		clear_pending_hdlc_ints(fc);
8028c2ecf20Sopenharmony_ci		inithdlc(fc);
8038c2ecf20Sopenharmony_ci		enable_hwirq(fc);
8048c2ecf20Sopenharmony_ci		/* RESET Receiver and Transmitter */
8058c2ecf20Sopenharmony_ci		if (fc->type == AVM_FRITZ_PCIV2) {
8068c2ecf20Sopenharmony_ci			WriteISAC_V2(fc, ISACX_MASK, 0);
8078c2ecf20Sopenharmony_ci			WriteISAC_V2(fc, ISACX_CMDRD, 0x41);
8088c2ecf20Sopenharmony_ci		} else {
8098c2ecf20Sopenharmony_ci			WriteISAC_V1(fc, ISAC_MASK, 0);
8108c2ecf20Sopenharmony_ci			WriteISAC_V1(fc, ISAC_CMDR, 0x41);
8118c2ecf20Sopenharmony_ci		}
8128c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fc->lock, flags);
8138c2ecf20Sopenharmony_ci		/* Timeout 10ms */
8148c2ecf20Sopenharmony_ci		msleep_interruptible(10);
8158c2ecf20Sopenharmony_ci		if (debug & DEBUG_HW)
8168c2ecf20Sopenharmony_ci			pr_notice("%s: IRQ %d count %d\n", fc->name,
8178c2ecf20Sopenharmony_ci				  fc->irq, fc->irqcnt);
8188c2ecf20Sopenharmony_ci		if (!fc->irqcnt) {
8198c2ecf20Sopenharmony_ci			pr_info("%s: IRQ(%d) getting no IRQs during init %d\n",
8208c2ecf20Sopenharmony_ci				fc->name, fc->irq, 3 - cnt);
8218c2ecf20Sopenharmony_ci			reset_avm(fc);
8228c2ecf20Sopenharmony_ci		} else
8238c2ecf20Sopenharmony_ci			return 0;
8248c2ecf20Sopenharmony_ci	}
8258c2ecf20Sopenharmony_ci	free_irq(fc->irq, fc);
8268c2ecf20Sopenharmony_ci	return -EIO;
8278c2ecf20Sopenharmony_ci}
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_cistatic int
8308c2ecf20Sopenharmony_cichannel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
8318c2ecf20Sopenharmony_ci{
8328c2ecf20Sopenharmony_ci	return mISDN_ctrl_bchannel(bch, cq);
8338c2ecf20Sopenharmony_ci}
8348c2ecf20Sopenharmony_ci
8358c2ecf20Sopenharmony_cistatic int
8368c2ecf20Sopenharmony_ciavm_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
8378c2ecf20Sopenharmony_ci{
8388c2ecf20Sopenharmony_ci	struct bchannel *bch = container_of(ch, struct bchannel, ch);
8398c2ecf20Sopenharmony_ci	struct fritzcard *fc = bch->hw;
8408c2ecf20Sopenharmony_ci	int ret = -EINVAL;
8418c2ecf20Sopenharmony_ci	u_long flags;
8428c2ecf20Sopenharmony_ci
8438c2ecf20Sopenharmony_ci	pr_debug("%s: %s cmd:%x %p\n", fc->name, __func__, cmd, arg);
8448c2ecf20Sopenharmony_ci	switch (cmd) {
8458c2ecf20Sopenharmony_ci	case CLOSE_CHANNEL:
8468c2ecf20Sopenharmony_ci		test_and_clear_bit(FLG_OPEN, &bch->Flags);
8478c2ecf20Sopenharmony_ci		cancel_work_sync(&bch->workq);
8488c2ecf20Sopenharmony_ci		spin_lock_irqsave(&fc->lock, flags);
8498c2ecf20Sopenharmony_ci		mISDN_clear_bchannel(bch);
8508c2ecf20Sopenharmony_ci		modehdlc(bch, ISDN_P_NONE);
8518c2ecf20Sopenharmony_ci		spin_unlock_irqrestore(&fc->lock, flags);
8528c2ecf20Sopenharmony_ci		ch->protocol = ISDN_P_NONE;
8538c2ecf20Sopenharmony_ci		ch->peer = NULL;
8548c2ecf20Sopenharmony_ci		module_put(THIS_MODULE);
8558c2ecf20Sopenharmony_ci		ret = 0;
8568c2ecf20Sopenharmony_ci		break;
8578c2ecf20Sopenharmony_ci	case CONTROL_CHANNEL:
8588c2ecf20Sopenharmony_ci		ret = channel_bctrl(bch, arg);
8598c2ecf20Sopenharmony_ci		break;
8608c2ecf20Sopenharmony_ci	default:
8618c2ecf20Sopenharmony_ci		pr_info("%s: %s unknown prim(%x)\n", fc->name, __func__, cmd);
8628c2ecf20Sopenharmony_ci	}
8638c2ecf20Sopenharmony_ci	return ret;
8648c2ecf20Sopenharmony_ci}
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_cistatic int
8678c2ecf20Sopenharmony_cichannel_ctrl(struct fritzcard  *fc, struct mISDN_ctrl_req *cq)
8688c2ecf20Sopenharmony_ci{
8698c2ecf20Sopenharmony_ci	int	ret = 0;
8708c2ecf20Sopenharmony_ci
8718c2ecf20Sopenharmony_ci	switch (cq->op) {
8728c2ecf20Sopenharmony_ci	case MISDN_CTRL_GETOP:
8738c2ecf20Sopenharmony_ci		cq->op = MISDN_CTRL_LOOP | MISDN_CTRL_L1_TIMER3;
8748c2ecf20Sopenharmony_ci		break;
8758c2ecf20Sopenharmony_ci	case MISDN_CTRL_LOOP:
8768c2ecf20Sopenharmony_ci		/* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */
8778c2ecf20Sopenharmony_ci		if (cq->channel < 0 || cq->channel > 3) {
8788c2ecf20Sopenharmony_ci			ret = -EINVAL;
8798c2ecf20Sopenharmony_ci			break;
8808c2ecf20Sopenharmony_ci		}
8818c2ecf20Sopenharmony_ci		ret = fc->isac.ctrl(&fc->isac, HW_TESTLOOP, cq->channel);
8828c2ecf20Sopenharmony_ci		break;
8838c2ecf20Sopenharmony_ci	case MISDN_CTRL_L1_TIMER3:
8848c2ecf20Sopenharmony_ci		ret = fc->isac.ctrl(&fc->isac, HW_TIMER3_VALUE, cq->p1);
8858c2ecf20Sopenharmony_ci		break;
8868c2ecf20Sopenharmony_ci	default:
8878c2ecf20Sopenharmony_ci		pr_info("%s: %s unknown Op %x\n", fc->name, __func__, cq->op);
8888c2ecf20Sopenharmony_ci		ret = -EINVAL;
8898c2ecf20Sopenharmony_ci		break;
8908c2ecf20Sopenharmony_ci	}
8918c2ecf20Sopenharmony_ci	return ret;
8928c2ecf20Sopenharmony_ci}
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_cistatic int
8958c2ecf20Sopenharmony_ciopen_bchannel(struct fritzcard *fc, struct channel_req *rq)
8968c2ecf20Sopenharmony_ci{
8978c2ecf20Sopenharmony_ci	struct bchannel		*bch;
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	if (rq->adr.channel == 0 || rq->adr.channel > 2)
9008c2ecf20Sopenharmony_ci		return -EINVAL;
9018c2ecf20Sopenharmony_ci	if (rq->protocol == ISDN_P_NONE)
9028c2ecf20Sopenharmony_ci		return -EINVAL;
9038c2ecf20Sopenharmony_ci	bch = &fc->bch[rq->adr.channel - 1];
9048c2ecf20Sopenharmony_ci	if (test_and_set_bit(FLG_OPEN, &bch->Flags))
9058c2ecf20Sopenharmony_ci		return -EBUSY; /* b-channel can be only open once */
9068c2ecf20Sopenharmony_ci	bch->ch.protocol = rq->protocol;
9078c2ecf20Sopenharmony_ci	rq->ch = &bch->ch;
9088c2ecf20Sopenharmony_ci	return 0;
9098c2ecf20Sopenharmony_ci}
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci/*
9128c2ecf20Sopenharmony_ci * device control function
9138c2ecf20Sopenharmony_ci */
9148c2ecf20Sopenharmony_cistatic int
9158c2ecf20Sopenharmony_ciavm_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
9168c2ecf20Sopenharmony_ci{
9178c2ecf20Sopenharmony_ci	struct mISDNdevice	*dev = container_of(ch, struct mISDNdevice, D);
9188c2ecf20Sopenharmony_ci	struct dchannel		*dch = container_of(dev, struct dchannel, dev);
9198c2ecf20Sopenharmony_ci	struct fritzcard	*fc = dch->hw;
9208c2ecf20Sopenharmony_ci	struct channel_req	*rq;
9218c2ecf20Sopenharmony_ci	int			err = 0;
9228c2ecf20Sopenharmony_ci
9238c2ecf20Sopenharmony_ci	pr_debug("%s: %s cmd:%x %p\n", fc->name, __func__, cmd, arg);
9248c2ecf20Sopenharmony_ci	switch (cmd) {
9258c2ecf20Sopenharmony_ci	case OPEN_CHANNEL:
9268c2ecf20Sopenharmony_ci		rq = arg;
9278c2ecf20Sopenharmony_ci		if (rq->protocol == ISDN_P_TE_S0)
9288c2ecf20Sopenharmony_ci			err = fc->isac.open(&fc->isac, rq);
9298c2ecf20Sopenharmony_ci		else
9308c2ecf20Sopenharmony_ci			err = open_bchannel(fc, rq);
9318c2ecf20Sopenharmony_ci		if (err)
9328c2ecf20Sopenharmony_ci			break;
9338c2ecf20Sopenharmony_ci		if (!try_module_get(THIS_MODULE))
9348c2ecf20Sopenharmony_ci			pr_info("%s: cannot get module\n", fc->name);
9358c2ecf20Sopenharmony_ci		break;
9368c2ecf20Sopenharmony_ci	case CLOSE_CHANNEL:
9378c2ecf20Sopenharmony_ci		pr_debug("%s: dev(%d) close from %p\n", fc->name, dch->dev.id,
9388c2ecf20Sopenharmony_ci			 __builtin_return_address(0));
9398c2ecf20Sopenharmony_ci		module_put(THIS_MODULE);
9408c2ecf20Sopenharmony_ci		break;
9418c2ecf20Sopenharmony_ci	case CONTROL_CHANNEL:
9428c2ecf20Sopenharmony_ci		err = channel_ctrl(fc, arg);
9438c2ecf20Sopenharmony_ci		break;
9448c2ecf20Sopenharmony_ci	default:
9458c2ecf20Sopenharmony_ci		pr_debug("%s: %s unknown command %x\n",
9468c2ecf20Sopenharmony_ci			 fc->name, __func__, cmd);
9478c2ecf20Sopenharmony_ci		return -EINVAL;
9488c2ecf20Sopenharmony_ci	}
9498c2ecf20Sopenharmony_ci	return err;
9508c2ecf20Sopenharmony_ci}
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_cistatic int
9538c2ecf20Sopenharmony_cisetup_fritz(struct fritzcard *fc)
9548c2ecf20Sopenharmony_ci{
9558c2ecf20Sopenharmony_ci	u32 val, ver;
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci	if (!request_region(fc->addr, 32, fc->name)) {
9588c2ecf20Sopenharmony_ci		pr_info("%s: AVM config port %x-%x already in use\n",
9598c2ecf20Sopenharmony_ci			fc->name, fc->addr, fc->addr + 31);
9608c2ecf20Sopenharmony_ci		return -EIO;
9618c2ecf20Sopenharmony_ci	}
9628c2ecf20Sopenharmony_ci	switch (fc->type) {
9638c2ecf20Sopenharmony_ci	case AVM_FRITZ_PCI:
9648c2ecf20Sopenharmony_ci		val = inl(fc->addr);
9658c2ecf20Sopenharmony_ci		outl(AVM_HDLC_1, fc->addr + CHIP_INDEX);
9668c2ecf20Sopenharmony_ci		ver = inl(fc->addr + CHIP_WINDOW + HDLC_STATUS) >> 24;
9678c2ecf20Sopenharmony_ci		if (debug & DEBUG_HW) {
9688c2ecf20Sopenharmony_ci			pr_notice("%s: PCI stat %#x\n", fc->name, val);
9698c2ecf20Sopenharmony_ci			pr_notice("%s: PCI Class %X Rev %d\n", fc->name,
9708c2ecf20Sopenharmony_ci				  val & 0xff, (val >> 8) & 0xff);
9718c2ecf20Sopenharmony_ci			pr_notice("%s: HDLC version %x\n", fc->name, ver & 0xf);
9728c2ecf20Sopenharmony_ci		}
9738c2ecf20Sopenharmony_ci		ASSIGN_FUNC(V1, ISAC, fc->isac);
9748c2ecf20Sopenharmony_ci		fc->isac.type = IPAC_TYPE_ISAC;
9758c2ecf20Sopenharmony_ci		break;
9768c2ecf20Sopenharmony_ci	case AVM_FRITZ_PCIV2:
9778c2ecf20Sopenharmony_ci		val = inl(fc->addr);
9788c2ecf20Sopenharmony_ci		ver = inl(fc->addr + AVM_HDLC_STATUS_1) >> 24;
9798c2ecf20Sopenharmony_ci		if (debug & DEBUG_HW) {
9808c2ecf20Sopenharmony_ci			pr_notice("%s: PCI V2 stat %#x\n", fc->name, val);
9818c2ecf20Sopenharmony_ci			pr_notice("%s: PCI V2 Class %X Rev %d\n", fc->name,
9828c2ecf20Sopenharmony_ci				  val & 0xff, (val >> 8) & 0xff);
9838c2ecf20Sopenharmony_ci			pr_notice("%s: HDLC version %x\n", fc->name, ver & 0xf);
9848c2ecf20Sopenharmony_ci		}
9858c2ecf20Sopenharmony_ci		ASSIGN_FUNC(V2, ISAC, fc->isac);
9868c2ecf20Sopenharmony_ci		fc->isac.type = IPAC_TYPE_ISACX;
9878c2ecf20Sopenharmony_ci		break;
9888c2ecf20Sopenharmony_ci	default:
9898c2ecf20Sopenharmony_ci		release_region(fc->addr, 32);
9908c2ecf20Sopenharmony_ci		pr_info("%s: AVM unknown type %d\n", fc->name, fc->type);
9918c2ecf20Sopenharmony_ci		return -ENODEV;
9928c2ecf20Sopenharmony_ci	}
9938c2ecf20Sopenharmony_ci	pr_notice("%s: %s config irq:%d base:0x%X\n", fc->name,
9948c2ecf20Sopenharmony_ci		  (fc->type == AVM_FRITZ_PCI) ? "AVM Fritz!CARD PCI" :
9958c2ecf20Sopenharmony_ci		  "AVM Fritz!CARD PCIv2", fc->irq, fc->addr);
9968c2ecf20Sopenharmony_ci	return 0;
9978c2ecf20Sopenharmony_ci}
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_cistatic void
10008c2ecf20Sopenharmony_cirelease_card(struct fritzcard *card)
10018c2ecf20Sopenharmony_ci{
10028c2ecf20Sopenharmony_ci	u_long flags;
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci	disable_hwirq(card);
10058c2ecf20Sopenharmony_ci	spin_lock_irqsave(&card->lock, flags);
10068c2ecf20Sopenharmony_ci	modehdlc(&card->bch[0], ISDN_P_NONE);
10078c2ecf20Sopenharmony_ci	modehdlc(&card->bch[1], ISDN_P_NONE);
10088c2ecf20Sopenharmony_ci	spin_unlock_irqrestore(&card->lock, flags);
10098c2ecf20Sopenharmony_ci	card->isac.release(&card->isac);
10108c2ecf20Sopenharmony_ci	free_irq(card->irq, card);
10118c2ecf20Sopenharmony_ci	mISDN_freebchannel(&card->bch[1]);
10128c2ecf20Sopenharmony_ci	mISDN_freebchannel(&card->bch[0]);
10138c2ecf20Sopenharmony_ci	mISDN_unregister_device(&card->isac.dch.dev);
10148c2ecf20Sopenharmony_ci	release_region(card->addr, 32);
10158c2ecf20Sopenharmony_ci	pci_disable_device(card->pdev);
10168c2ecf20Sopenharmony_ci	pci_set_drvdata(card->pdev, NULL);
10178c2ecf20Sopenharmony_ci	write_lock_irqsave(&card_lock, flags);
10188c2ecf20Sopenharmony_ci	list_del(&card->list);
10198c2ecf20Sopenharmony_ci	write_unlock_irqrestore(&card_lock, flags);
10208c2ecf20Sopenharmony_ci	kfree(card);
10218c2ecf20Sopenharmony_ci	AVM_cnt--;
10228c2ecf20Sopenharmony_ci}
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_cistatic int
10258c2ecf20Sopenharmony_cisetup_instance(struct fritzcard *card)
10268c2ecf20Sopenharmony_ci{
10278c2ecf20Sopenharmony_ci	int i, err;
10288c2ecf20Sopenharmony_ci	unsigned short minsize;
10298c2ecf20Sopenharmony_ci	u_long flags;
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	snprintf(card->name, MISDN_MAX_IDLEN - 1, "AVM.%d", AVM_cnt + 1);
10328c2ecf20Sopenharmony_ci	write_lock_irqsave(&card_lock, flags);
10338c2ecf20Sopenharmony_ci	list_add_tail(&card->list, &Cards);
10348c2ecf20Sopenharmony_ci	write_unlock_irqrestore(&card_lock, flags);
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci	_set_debug(card);
10378c2ecf20Sopenharmony_ci	card->isac.name = card->name;
10388c2ecf20Sopenharmony_ci	spin_lock_init(&card->lock);
10398c2ecf20Sopenharmony_ci	card->isac.hwlock = &card->lock;
10408c2ecf20Sopenharmony_ci	mISDNisac_init(&card->isac, card);
10418c2ecf20Sopenharmony_ci
10428c2ecf20Sopenharmony_ci	card->isac.dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
10438c2ecf20Sopenharmony_ci		(1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
10448c2ecf20Sopenharmony_ci	card->isac.dch.dev.D.ctrl = avm_dctrl;
10458c2ecf20Sopenharmony_ci	for (i = 0; i < 2; i++) {
10468c2ecf20Sopenharmony_ci		card->bch[i].nr = i + 1;
10478c2ecf20Sopenharmony_ci		set_channelmap(i + 1, card->isac.dch.dev.channelmap);
10488c2ecf20Sopenharmony_ci		if (AVM_FRITZ_PCIV2 == card->type)
10498c2ecf20Sopenharmony_ci			minsize = HDLC_FIFO_SIZE_V2;
10508c2ecf20Sopenharmony_ci		else
10518c2ecf20Sopenharmony_ci			minsize = HDLC_FIFO_SIZE_V1;
10528c2ecf20Sopenharmony_ci		mISDN_initbchannel(&card->bch[i], MAX_DATA_MEM, minsize);
10538c2ecf20Sopenharmony_ci		card->bch[i].hw = card;
10548c2ecf20Sopenharmony_ci		card->bch[i].ch.send = avm_l2l1B;
10558c2ecf20Sopenharmony_ci		card->bch[i].ch.ctrl = avm_bctrl;
10568c2ecf20Sopenharmony_ci		card->bch[i].ch.nr = i + 1;
10578c2ecf20Sopenharmony_ci		list_add(&card->bch[i].ch.list, &card->isac.dch.dev.bchannels);
10588c2ecf20Sopenharmony_ci	}
10598c2ecf20Sopenharmony_ci	err = setup_fritz(card);
10608c2ecf20Sopenharmony_ci	if (err)
10618c2ecf20Sopenharmony_ci		goto error;
10628c2ecf20Sopenharmony_ci	err = mISDN_register_device(&card->isac.dch.dev, &card->pdev->dev,
10638c2ecf20Sopenharmony_ci				    card->name);
10648c2ecf20Sopenharmony_ci	if (err)
10658c2ecf20Sopenharmony_ci		goto error_reg;
10668c2ecf20Sopenharmony_ci	err = init_card(card);
10678c2ecf20Sopenharmony_ci	if (!err)  {
10688c2ecf20Sopenharmony_ci		AVM_cnt++;
10698c2ecf20Sopenharmony_ci		pr_notice("AVM %d cards installed DEBUG\n", AVM_cnt);
10708c2ecf20Sopenharmony_ci		return 0;
10718c2ecf20Sopenharmony_ci	}
10728c2ecf20Sopenharmony_ci	mISDN_unregister_device(&card->isac.dch.dev);
10738c2ecf20Sopenharmony_cierror_reg:
10748c2ecf20Sopenharmony_ci	release_region(card->addr, 32);
10758c2ecf20Sopenharmony_cierror:
10768c2ecf20Sopenharmony_ci	card->isac.release(&card->isac);
10778c2ecf20Sopenharmony_ci	mISDN_freebchannel(&card->bch[1]);
10788c2ecf20Sopenharmony_ci	mISDN_freebchannel(&card->bch[0]);
10798c2ecf20Sopenharmony_ci	write_lock_irqsave(&card_lock, flags);
10808c2ecf20Sopenharmony_ci	list_del(&card->list);
10818c2ecf20Sopenharmony_ci	write_unlock_irqrestore(&card_lock, flags);
10828c2ecf20Sopenharmony_ci	kfree(card);
10838c2ecf20Sopenharmony_ci	return err;
10848c2ecf20Sopenharmony_ci}
10858c2ecf20Sopenharmony_ci
10868c2ecf20Sopenharmony_cistatic int
10878c2ecf20Sopenharmony_cifritzpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
10888c2ecf20Sopenharmony_ci{
10898c2ecf20Sopenharmony_ci	int err = -ENOMEM;
10908c2ecf20Sopenharmony_ci	struct fritzcard *card;
10918c2ecf20Sopenharmony_ci
10928c2ecf20Sopenharmony_ci	card = kzalloc(sizeof(struct fritzcard), GFP_KERNEL);
10938c2ecf20Sopenharmony_ci	if (!card) {
10948c2ecf20Sopenharmony_ci		pr_info("No kmem for fritzcard\n");
10958c2ecf20Sopenharmony_ci		return err;
10968c2ecf20Sopenharmony_ci	}
10978c2ecf20Sopenharmony_ci	if (pdev->device == PCI_DEVICE_ID_AVM_A1_V2)
10988c2ecf20Sopenharmony_ci		card->type = AVM_FRITZ_PCIV2;
10998c2ecf20Sopenharmony_ci	else
11008c2ecf20Sopenharmony_ci		card->type = AVM_FRITZ_PCI;
11018c2ecf20Sopenharmony_ci	card->pdev = pdev;
11028c2ecf20Sopenharmony_ci	err = pci_enable_device(pdev);
11038c2ecf20Sopenharmony_ci	if (err) {
11048c2ecf20Sopenharmony_ci		kfree(card);
11058c2ecf20Sopenharmony_ci		return err;
11068c2ecf20Sopenharmony_ci	}
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	pr_notice("mISDN: found adapter %s at %s\n",
11098c2ecf20Sopenharmony_ci		  (char *) ent->driver_data, pci_name(pdev));
11108c2ecf20Sopenharmony_ci
11118c2ecf20Sopenharmony_ci	card->addr = pci_resource_start(pdev, 1);
11128c2ecf20Sopenharmony_ci	card->irq = pdev->irq;
11138c2ecf20Sopenharmony_ci	pci_set_drvdata(pdev, card);
11148c2ecf20Sopenharmony_ci	err = setup_instance(card);
11158c2ecf20Sopenharmony_ci	if (err)
11168c2ecf20Sopenharmony_ci		pci_set_drvdata(pdev, NULL);
11178c2ecf20Sopenharmony_ci	return err;
11188c2ecf20Sopenharmony_ci}
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_cistatic void
11218c2ecf20Sopenharmony_cifritz_remove_pci(struct pci_dev *pdev)
11228c2ecf20Sopenharmony_ci{
11238c2ecf20Sopenharmony_ci	struct fritzcard *card = pci_get_drvdata(pdev);
11248c2ecf20Sopenharmony_ci
11258c2ecf20Sopenharmony_ci	if (card)
11268c2ecf20Sopenharmony_ci		release_card(card);
11278c2ecf20Sopenharmony_ci	else
11288c2ecf20Sopenharmony_ci		if (debug)
11298c2ecf20Sopenharmony_ci			pr_info("%s: drvdata already removed\n", __func__);
11308c2ecf20Sopenharmony_ci}
11318c2ecf20Sopenharmony_ci
11328c2ecf20Sopenharmony_cistatic const struct pci_device_id fcpci_ids[] = {
11338c2ecf20Sopenharmony_ci	{ PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1, PCI_ANY_ID, PCI_ANY_ID,
11348c2ecf20Sopenharmony_ci	  0, 0, (unsigned long) "Fritz!Card PCI"},
11358c2ecf20Sopenharmony_ci	{ PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1_V2, PCI_ANY_ID, PCI_ANY_ID,
11368c2ecf20Sopenharmony_ci	  0, 0, (unsigned long) "Fritz!Card PCI v2" },
11378c2ecf20Sopenharmony_ci	{ }
11388c2ecf20Sopenharmony_ci};
11398c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, fcpci_ids);
11408c2ecf20Sopenharmony_ci
11418c2ecf20Sopenharmony_cistatic struct pci_driver fcpci_driver = {
11428c2ecf20Sopenharmony_ci	.name = "fcpci",
11438c2ecf20Sopenharmony_ci	.probe = fritzpci_probe,
11448c2ecf20Sopenharmony_ci	.remove = fritz_remove_pci,
11458c2ecf20Sopenharmony_ci	.id_table = fcpci_ids,
11468c2ecf20Sopenharmony_ci};
11478c2ecf20Sopenharmony_ci
11488c2ecf20Sopenharmony_cistatic int __init AVM_init(void)
11498c2ecf20Sopenharmony_ci{
11508c2ecf20Sopenharmony_ci	int err;
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_ci	pr_notice("AVM Fritz PCI driver Rev. %s\n", AVMFRITZ_REV);
11538c2ecf20Sopenharmony_ci	err = pci_register_driver(&fcpci_driver);
11548c2ecf20Sopenharmony_ci	return err;
11558c2ecf20Sopenharmony_ci}
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_cistatic void __exit AVM_cleanup(void)
11588c2ecf20Sopenharmony_ci{
11598c2ecf20Sopenharmony_ci	pci_unregister_driver(&fcpci_driver);
11608c2ecf20Sopenharmony_ci}
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_cimodule_init(AVM_init);
11638c2ecf20Sopenharmony_cimodule_exit(AVM_cleanup);
1164