xref: /openbmc/linux/drivers/isdn/hardware/mISDN/avmfritz.c (revision 6115d2f3fcaebed5b88fa9cefd178bb5b07461ff)
1*6115d2f3SKarsten Keil /*
2*6115d2f3SKarsten Keil  * avm_fritz.c    low level stuff for AVM FRITZ!CARD PCI ISDN cards
3*6115d2f3SKarsten Keil  *                Thanks to AVM, Berlin for informations
4*6115d2f3SKarsten Keil  *
5*6115d2f3SKarsten Keil  * Author       Karsten Keil <keil@isdn4linux.de>
6*6115d2f3SKarsten Keil  *
7*6115d2f3SKarsten Keil  * Copyright 2009  by Karsten Keil <keil@isdn4linux.de>
8*6115d2f3SKarsten Keil  *
9*6115d2f3SKarsten Keil  * This program is free software; you can redistribute it and/or modify
10*6115d2f3SKarsten Keil  * it under the terms of the GNU General Public License version 2 as
11*6115d2f3SKarsten Keil  * published by the Free Software Foundation.
12*6115d2f3SKarsten Keil  *
13*6115d2f3SKarsten Keil  * This program is distributed in the hope that it will be useful,
14*6115d2f3SKarsten Keil  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15*6115d2f3SKarsten Keil  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16*6115d2f3SKarsten Keil  * GNU General Public License for more details.
17*6115d2f3SKarsten Keil  *
18*6115d2f3SKarsten Keil  * You should have received a copy of the GNU General Public License
19*6115d2f3SKarsten Keil  * along with this program; if not, write to the Free Software
20*6115d2f3SKarsten Keil  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21*6115d2f3SKarsten Keil  *
22*6115d2f3SKarsten Keil  */
23*6115d2f3SKarsten Keil #include <linux/module.h>
24*6115d2f3SKarsten Keil #include <linux/pci.h>
25*6115d2f3SKarsten Keil #include <linux/delay.h>
26*6115d2f3SKarsten Keil #include <linux/mISDNhw.h>
27*6115d2f3SKarsten Keil #include <asm/unaligned.h>
28*6115d2f3SKarsten Keil #include "ipac.h"
29*6115d2f3SKarsten Keil 
30*6115d2f3SKarsten Keil 
31*6115d2f3SKarsten Keil #define AVMFRITZ_REV	"2.1"
32*6115d2f3SKarsten Keil 
33*6115d2f3SKarsten Keil static int AVM_cnt;
34*6115d2f3SKarsten Keil static int debug;
35*6115d2f3SKarsten Keil 
36*6115d2f3SKarsten Keil enum {
37*6115d2f3SKarsten Keil 	AVM_FRITZ_PCI,
38*6115d2f3SKarsten Keil 	AVM_FRITZ_PCIV2,
39*6115d2f3SKarsten Keil };
40*6115d2f3SKarsten Keil 
41*6115d2f3SKarsten Keil #define HDLC_FIFO		0x0
42*6115d2f3SKarsten Keil #define HDLC_STATUS		0x4
43*6115d2f3SKarsten Keil #define CHIP_WINDOW		0x10
44*6115d2f3SKarsten Keil 
45*6115d2f3SKarsten Keil #define CHIP_INDEX		0x4
46*6115d2f3SKarsten Keil #define AVM_HDLC_1		0x00
47*6115d2f3SKarsten Keil #define AVM_HDLC_2		0x01
48*6115d2f3SKarsten Keil #define AVM_ISAC_FIFO		0x02
49*6115d2f3SKarsten Keil #define AVM_ISAC_REG_LOW	0x04
50*6115d2f3SKarsten Keil #define AVM_ISAC_REG_HIGH	0x06
51*6115d2f3SKarsten Keil 
52*6115d2f3SKarsten Keil #define AVM_STATUS0_IRQ_ISAC	0x01
53*6115d2f3SKarsten Keil #define AVM_STATUS0_IRQ_HDLC	0x02
54*6115d2f3SKarsten Keil #define AVM_STATUS0_IRQ_TIMER	0x04
55*6115d2f3SKarsten Keil #define AVM_STATUS0_IRQ_MASK	0x07
56*6115d2f3SKarsten Keil 
57*6115d2f3SKarsten Keil #define AVM_STATUS0_RESET	0x01
58*6115d2f3SKarsten Keil #define AVM_STATUS0_DIS_TIMER	0x02
59*6115d2f3SKarsten Keil #define AVM_STATUS0_RES_TIMER	0x04
60*6115d2f3SKarsten Keil #define AVM_STATUS0_ENA_IRQ	0x08
61*6115d2f3SKarsten Keil #define AVM_STATUS0_TESTBIT	0x10
62*6115d2f3SKarsten Keil 
63*6115d2f3SKarsten Keil #define AVM_STATUS1_INT_SEL	0x0f
64*6115d2f3SKarsten Keil #define AVM_STATUS1_ENA_IOM	0x80
65*6115d2f3SKarsten Keil 
66*6115d2f3SKarsten Keil #define HDLC_MODE_ITF_FLG	0x01
67*6115d2f3SKarsten Keil #define HDLC_MODE_TRANS		0x02
68*6115d2f3SKarsten Keil #define HDLC_MODE_CCR_7		0x04
69*6115d2f3SKarsten Keil #define HDLC_MODE_CCR_16	0x08
70*6115d2f3SKarsten Keil #define HDLC_MODE_TESTLOOP	0x80
71*6115d2f3SKarsten Keil 
72*6115d2f3SKarsten Keil #define HDLC_INT_XPR		0x80
73*6115d2f3SKarsten Keil #define HDLC_INT_XDU		0x40
74*6115d2f3SKarsten Keil #define HDLC_INT_RPR		0x20
75*6115d2f3SKarsten Keil #define HDLC_INT_MASK		0xE0
76*6115d2f3SKarsten Keil 
77*6115d2f3SKarsten Keil #define HDLC_STAT_RME		0x01
78*6115d2f3SKarsten Keil #define HDLC_STAT_RDO		0x10
79*6115d2f3SKarsten Keil #define HDLC_STAT_CRCVFRRAB	0x0E
80*6115d2f3SKarsten Keil #define HDLC_STAT_CRCVFR	0x06
81*6115d2f3SKarsten Keil #define HDLC_STAT_RML_MASK	0x3f00
82*6115d2f3SKarsten Keil 
83*6115d2f3SKarsten Keil #define HDLC_CMD_XRS		0x80
84*6115d2f3SKarsten Keil #define HDLC_CMD_XME		0x01
85*6115d2f3SKarsten Keil #define HDLC_CMD_RRS		0x20
86*6115d2f3SKarsten Keil #define HDLC_CMD_XML_MASK	0x3f00
87*6115d2f3SKarsten Keil #define HDLC_FIFO_SIZE		32
88*6115d2f3SKarsten Keil 
89*6115d2f3SKarsten Keil /* Fritz PCI v2.0 */
90*6115d2f3SKarsten Keil 
91*6115d2f3SKarsten Keil #define AVM_HDLC_FIFO_1		0x10
92*6115d2f3SKarsten Keil #define AVM_HDLC_FIFO_2		0x18
93*6115d2f3SKarsten Keil 
94*6115d2f3SKarsten Keil #define AVM_HDLC_STATUS_1	0x14
95*6115d2f3SKarsten Keil #define AVM_HDLC_STATUS_2	0x1c
96*6115d2f3SKarsten Keil 
97*6115d2f3SKarsten Keil #define AVM_ISACX_INDEX		0x04
98*6115d2f3SKarsten Keil #define AVM_ISACX_DATA		0x08
99*6115d2f3SKarsten Keil 
100*6115d2f3SKarsten Keil /* data struct */
101*6115d2f3SKarsten Keil #define LOG_SIZE		63
102*6115d2f3SKarsten Keil 
103*6115d2f3SKarsten Keil struct hdlc_stat_reg {
104*6115d2f3SKarsten Keil #ifdef __BIG_ENDIAN
105*6115d2f3SKarsten Keil 	u8 fill;
106*6115d2f3SKarsten Keil 	u8 mode;
107*6115d2f3SKarsten Keil 	u8 xml;
108*6115d2f3SKarsten Keil 	u8 cmd;
109*6115d2f3SKarsten Keil #else
110*6115d2f3SKarsten Keil 	u8 cmd;
111*6115d2f3SKarsten Keil 	u8 xml;
112*6115d2f3SKarsten Keil 	u8 mode;
113*6115d2f3SKarsten Keil 	u8 fill;
114*6115d2f3SKarsten Keil #endif
115*6115d2f3SKarsten Keil } __attribute__((packed));
116*6115d2f3SKarsten Keil 
117*6115d2f3SKarsten Keil struct hdlc_hw {
118*6115d2f3SKarsten Keil 	union {
119*6115d2f3SKarsten Keil 		u32 ctrl;
120*6115d2f3SKarsten Keil 		struct hdlc_stat_reg sr;
121*6115d2f3SKarsten Keil 	} ctrl;
122*6115d2f3SKarsten Keil 	u32 stat;
123*6115d2f3SKarsten Keil };
124*6115d2f3SKarsten Keil 
125*6115d2f3SKarsten Keil struct fritzcard {
126*6115d2f3SKarsten Keil 	struct list_head	list;
127*6115d2f3SKarsten Keil 	struct pci_dev		*pdev;
128*6115d2f3SKarsten Keil 	char			name[MISDN_MAX_IDLEN];
129*6115d2f3SKarsten Keil 	u8			type;
130*6115d2f3SKarsten Keil 	u8			ctrlreg;
131*6115d2f3SKarsten Keil 	u16			irq;
132*6115d2f3SKarsten Keil 	u32			irqcnt;
133*6115d2f3SKarsten Keil 	u32			addr;
134*6115d2f3SKarsten Keil 	spinlock_t		lock; /* hw lock */
135*6115d2f3SKarsten Keil 	struct isac_hw		isac;
136*6115d2f3SKarsten Keil 	struct hdlc_hw		hdlc[2];
137*6115d2f3SKarsten Keil 	struct bchannel		bch[2];
138*6115d2f3SKarsten Keil 	char			log[LOG_SIZE + 1];
139*6115d2f3SKarsten Keil };
140*6115d2f3SKarsten Keil 
141*6115d2f3SKarsten Keil static LIST_HEAD(Cards);
142*6115d2f3SKarsten Keil static DEFINE_RWLOCK(card_lock); /* protect Cards */
143*6115d2f3SKarsten Keil 
144*6115d2f3SKarsten Keil static void
145*6115d2f3SKarsten Keil _set_debug(struct fritzcard *card)
146*6115d2f3SKarsten Keil {
147*6115d2f3SKarsten Keil 	card->isac.dch.debug = debug;
148*6115d2f3SKarsten Keil 	card->bch[0].debug = debug;
149*6115d2f3SKarsten Keil 	card->bch[1].debug = debug;
150*6115d2f3SKarsten Keil }
151*6115d2f3SKarsten Keil 
152*6115d2f3SKarsten Keil static int
153*6115d2f3SKarsten Keil set_debug(const char *val, struct kernel_param *kp)
154*6115d2f3SKarsten Keil {
155*6115d2f3SKarsten Keil 	int ret;
156*6115d2f3SKarsten Keil 	struct fritzcard *card;
157*6115d2f3SKarsten Keil 
158*6115d2f3SKarsten Keil 	ret = param_set_uint(val, kp);
159*6115d2f3SKarsten Keil 	if (!ret) {
160*6115d2f3SKarsten Keil 		read_lock(&card_lock);
161*6115d2f3SKarsten Keil 		list_for_each_entry(card, &Cards, list)
162*6115d2f3SKarsten Keil 			_set_debug(card);
163*6115d2f3SKarsten Keil 		read_unlock(&card_lock);
164*6115d2f3SKarsten Keil 	}
165*6115d2f3SKarsten Keil 	return ret;
166*6115d2f3SKarsten Keil }
167*6115d2f3SKarsten Keil 
168*6115d2f3SKarsten Keil MODULE_AUTHOR("Karsten Keil");
169*6115d2f3SKarsten Keil MODULE_LICENSE("GPL v2");
170*6115d2f3SKarsten Keil MODULE_VERSION(AVMFRITZ_REV);
171*6115d2f3SKarsten Keil module_param_call(debug, set_debug, param_get_uint, &debug, S_IRUGO | S_IWUSR);
172*6115d2f3SKarsten Keil MODULE_PARM_DESC(debug, "avmfritz debug mask");
173*6115d2f3SKarsten Keil 
174*6115d2f3SKarsten Keil /* Interface functions */
175*6115d2f3SKarsten Keil 
176*6115d2f3SKarsten Keil static u8
177*6115d2f3SKarsten Keil ReadISAC_V1(void *p, u8 offset)
178*6115d2f3SKarsten Keil {
179*6115d2f3SKarsten Keil 	struct fritzcard *fc = p;
180*6115d2f3SKarsten Keil 	u8 idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
181*6115d2f3SKarsten Keil 
182*6115d2f3SKarsten Keil 	outb(idx, fc->addr + CHIP_INDEX);
183*6115d2f3SKarsten Keil 	return inb(fc->addr + CHIP_WINDOW + (offset & 0xf));
184*6115d2f3SKarsten Keil }
185*6115d2f3SKarsten Keil 
186*6115d2f3SKarsten Keil static void
187*6115d2f3SKarsten Keil WriteISAC_V1(void *p, u8 offset, u8 value)
188*6115d2f3SKarsten Keil {
189*6115d2f3SKarsten Keil 	struct fritzcard *fc = p;
190*6115d2f3SKarsten Keil 	u8 idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW;
191*6115d2f3SKarsten Keil 
192*6115d2f3SKarsten Keil 	outb(idx, fc->addr + CHIP_INDEX);
193*6115d2f3SKarsten Keil 	outb(value, fc->addr + CHIP_WINDOW + (offset & 0xf));
194*6115d2f3SKarsten Keil }
195*6115d2f3SKarsten Keil 
196*6115d2f3SKarsten Keil static void
197*6115d2f3SKarsten Keil ReadFiFoISAC_V1(void *p, u8 off, u8 *data, int size)
198*6115d2f3SKarsten Keil {
199*6115d2f3SKarsten Keil 	struct fritzcard *fc = p;
200*6115d2f3SKarsten Keil 
201*6115d2f3SKarsten Keil 	outb(AVM_ISAC_FIFO, fc->addr + CHIP_INDEX);
202*6115d2f3SKarsten Keil 	insb(fc->addr + CHIP_WINDOW, data, size);
203*6115d2f3SKarsten Keil }
204*6115d2f3SKarsten Keil 
205*6115d2f3SKarsten Keil static void
206*6115d2f3SKarsten Keil WriteFiFoISAC_V1(void *p, u8 off, u8 *data, int size)
207*6115d2f3SKarsten Keil {
208*6115d2f3SKarsten Keil 	struct fritzcard *fc = p;
209*6115d2f3SKarsten Keil 
210*6115d2f3SKarsten Keil 	outb(AVM_ISAC_FIFO, fc->addr + CHIP_INDEX);
211*6115d2f3SKarsten Keil 	outsb(fc->addr + CHIP_WINDOW, data, size);
212*6115d2f3SKarsten Keil }
213*6115d2f3SKarsten Keil 
214*6115d2f3SKarsten Keil static u8
215*6115d2f3SKarsten Keil ReadISAC_V2(void *p, u8 offset)
216*6115d2f3SKarsten Keil {
217*6115d2f3SKarsten Keil 	struct fritzcard *fc = p;
218*6115d2f3SKarsten Keil 
219*6115d2f3SKarsten Keil 	outl(offset, fc->addr + AVM_ISACX_INDEX);
220*6115d2f3SKarsten Keil 	return 0xff & inl(fc->addr + AVM_ISACX_DATA);
221*6115d2f3SKarsten Keil }
222*6115d2f3SKarsten Keil 
223*6115d2f3SKarsten Keil static void
224*6115d2f3SKarsten Keil WriteISAC_V2(void *p, u8 offset, u8 value)
225*6115d2f3SKarsten Keil {
226*6115d2f3SKarsten Keil 	struct fritzcard *fc = p;
227*6115d2f3SKarsten Keil 
228*6115d2f3SKarsten Keil 	outl(offset, fc->addr + AVM_ISACX_INDEX);
229*6115d2f3SKarsten Keil 	outl(value, fc->addr + AVM_ISACX_DATA);
230*6115d2f3SKarsten Keil }
231*6115d2f3SKarsten Keil 
232*6115d2f3SKarsten Keil static void
233*6115d2f3SKarsten Keil ReadFiFoISAC_V2(void *p, u8 off, u8 *data, int size)
234*6115d2f3SKarsten Keil {
235*6115d2f3SKarsten Keil 	struct fritzcard *fc = p;
236*6115d2f3SKarsten Keil 	int i;
237*6115d2f3SKarsten Keil 
238*6115d2f3SKarsten Keil 	outl(off, fc->addr + AVM_ISACX_INDEX);
239*6115d2f3SKarsten Keil 	for (i = 0; i < size; i++)
240*6115d2f3SKarsten Keil 		data[i] = 0xff & inl(fc->addr + AVM_ISACX_DATA);
241*6115d2f3SKarsten Keil }
242*6115d2f3SKarsten Keil 
243*6115d2f3SKarsten Keil static void
244*6115d2f3SKarsten Keil WriteFiFoISAC_V2(void *p, u8 off, u8 *data, int size)
245*6115d2f3SKarsten Keil {
246*6115d2f3SKarsten Keil 	struct fritzcard *fc = p;
247*6115d2f3SKarsten Keil 	int i;
248*6115d2f3SKarsten Keil 
249*6115d2f3SKarsten Keil 	outl(off, fc->addr + AVM_ISACX_INDEX);
250*6115d2f3SKarsten Keil 	for (i = 0; i < size; i++)
251*6115d2f3SKarsten Keil 		outl(data[i], fc->addr + AVM_ISACX_DATA);
252*6115d2f3SKarsten Keil }
253*6115d2f3SKarsten Keil 
254*6115d2f3SKarsten Keil static struct bchannel *
255*6115d2f3SKarsten Keil Sel_BCS(struct fritzcard *fc, u32 channel)
256*6115d2f3SKarsten Keil {
257*6115d2f3SKarsten Keil 	if (test_bit(FLG_ACTIVE, &fc->bch[0].Flags) &&
258*6115d2f3SKarsten Keil 		(fc->bch[0].nr & channel))
259*6115d2f3SKarsten Keil 		return &fc->bch[0];
260*6115d2f3SKarsten Keil 	else if (test_bit(FLG_ACTIVE, &fc->bch[1].Flags) &&
261*6115d2f3SKarsten Keil 		(fc->bch[1].nr & channel))
262*6115d2f3SKarsten Keil 		return &fc->bch[1];
263*6115d2f3SKarsten Keil 	else
264*6115d2f3SKarsten Keil 		return NULL;
265*6115d2f3SKarsten Keil }
266*6115d2f3SKarsten Keil 
267*6115d2f3SKarsten Keil static inline void
268*6115d2f3SKarsten Keil __write_ctrl_pci(struct fritzcard *fc, struct hdlc_hw *hdlc, u32 channel) {
269*6115d2f3SKarsten Keil 	u32 idx = channel == 2 ? AVM_HDLC_2 : AVM_HDLC_1;
270*6115d2f3SKarsten Keil 
271*6115d2f3SKarsten Keil 	outl(idx, fc->addr + CHIP_INDEX);
272*6115d2f3SKarsten Keil 	outl(hdlc->ctrl.ctrl, fc->addr + CHIP_WINDOW + HDLC_STATUS);
273*6115d2f3SKarsten Keil }
274*6115d2f3SKarsten Keil 
275*6115d2f3SKarsten Keil static inline void
276*6115d2f3SKarsten Keil __write_ctrl_pciv2(struct fritzcard *fc, struct hdlc_hw *hdlc, u32 channel) {
277*6115d2f3SKarsten Keil 	outl(hdlc->ctrl.ctrl, fc->addr + (channel == 2 ? AVM_HDLC_STATUS_2 :
278*6115d2f3SKarsten Keil 		AVM_HDLC_STATUS_1));
279*6115d2f3SKarsten Keil }
280*6115d2f3SKarsten Keil 
281*6115d2f3SKarsten Keil void
282*6115d2f3SKarsten Keil write_ctrl(struct bchannel *bch, int which) {
283*6115d2f3SKarsten Keil 	struct fritzcard *fc = bch->hw;
284*6115d2f3SKarsten Keil 	struct hdlc_hw *hdlc;
285*6115d2f3SKarsten Keil 
286*6115d2f3SKarsten Keil 	hdlc = &fc->hdlc[(bch->nr - 1) & 1];
287*6115d2f3SKarsten Keil 	pr_debug("%s: hdlc %c wr%x ctrl %x\n", fc->name, '@' + bch->nr,
288*6115d2f3SKarsten Keil 		which, hdlc->ctrl.ctrl);
289*6115d2f3SKarsten Keil 	switch (fc->type) {
290*6115d2f3SKarsten Keil 	case AVM_FRITZ_PCIV2:
291*6115d2f3SKarsten Keil 		__write_ctrl_pciv2(fc, hdlc, bch->nr);
292*6115d2f3SKarsten Keil 		break;
293*6115d2f3SKarsten Keil 	case AVM_FRITZ_PCI:
294*6115d2f3SKarsten Keil 		__write_ctrl_pci(fc, hdlc, bch->nr);
295*6115d2f3SKarsten Keil 		break;
296*6115d2f3SKarsten Keil 	}
297*6115d2f3SKarsten Keil }
298*6115d2f3SKarsten Keil 
299*6115d2f3SKarsten Keil 
300*6115d2f3SKarsten Keil static inline u32
301*6115d2f3SKarsten Keil __read_status_pci(u_long addr, u32 channel)
302*6115d2f3SKarsten Keil {
303*6115d2f3SKarsten Keil 	outl(channel == 2 ? AVM_HDLC_2 : AVM_HDLC_1, addr + CHIP_INDEX);
304*6115d2f3SKarsten Keil 	return inl(addr + CHIP_WINDOW + HDLC_STATUS);
305*6115d2f3SKarsten Keil }
306*6115d2f3SKarsten Keil 
307*6115d2f3SKarsten Keil static inline u32
308*6115d2f3SKarsten Keil __read_status_pciv2(u_long addr, u32 channel)
309*6115d2f3SKarsten Keil {
310*6115d2f3SKarsten Keil 	return inl(addr + (channel == 2 ? AVM_HDLC_STATUS_2 :
311*6115d2f3SKarsten Keil 		AVM_HDLC_STATUS_1));
312*6115d2f3SKarsten Keil }
313*6115d2f3SKarsten Keil 
314*6115d2f3SKarsten Keil 
315*6115d2f3SKarsten Keil static u32
316*6115d2f3SKarsten Keil read_status(struct fritzcard *fc, u32 channel)
317*6115d2f3SKarsten Keil {
318*6115d2f3SKarsten Keil 	switch (fc->type) {
319*6115d2f3SKarsten Keil 	case AVM_FRITZ_PCIV2:
320*6115d2f3SKarsten Keil 		return __read_status_pciv2(fc->addr, channel);
321*6115d2f3SKarsten Keil 	case AVM_FRITZ_PCI:
322*6115d2f3SKarsten Keil 		return __read_status_pci(fc->addr, channel);
323*6115d2f3SKarsten Keil 	}
324*6115d2f3SKarsten Keil 	/* dummy */
325*6115d2f3SKarsten Keil 	return 0;
326*6115d2f3SKarsten Keil }
327*6115d2f3SKarsten Keil 
328*6115d2f3SKarsten Keil static void
329*6115d2f3SKarsten Keil enable_hwirq(struct fritzcard *fc)
330*6115d2f3SKarsten Keil {
331*6115d2f3SKarsten Keil 	fc->ctrlreg |= AVM_STATUS0_ENA_IRQ;
332*6115d2f3SKarsten Keil 	outb(fc->ctrlreg, fc->addr + 2);
333*6115d2f3SKarsten Keil }
334*6115d2f3SKarsten Keil 
335*6115d2f3SKarsten Keil static void
336*6115d2f3SKarsten Keil disable_hwirq(struct fritzcard *fc)
337*6115d2f3SKarsten Keil {
338*6115d2f3SKarsten Keil 	fc->ctrlreg &= ~AVM_STATUS0_ENA_IRQ;
339*6115d2f3SKarsten Keil 	outb(fc->ctrlreg, fc->addr + 2);
340*6115d2f3SKarsten Keil }
341*6115d2f3SKarsten Keil 
342*6115d2f3SKarsten Keil static int
343*6115d2f3SKarsten Keil modehdlc(struct bchannel *bch, int protocol)
344*6115d2f3SKarsten Keil {
345*6115d2f3SKarsten Keil 	struct fritzcard *fc = bch->hw;
346*6115d2f3SKarsten Keil 	struct hdlc_hw *hdlc;
347*6115d2f3SKarsten Keil 
348*6115d2f3SKarsten Keil 	hdlc = &fc->hdlc[(bch->nr - 1) & 1];
349*6115d2f3SKarsten Keil 	pr_debug("%s: hdlc %c protocol %x-->%x ch %d\n", fc->name,
350*6115d2f3SKarsten Keil 		'@' + bch->nr, bch->state, protocol, bch->nr);
351*6115d2f3SKarsten Keil 	hdlc->ctrl.ctrl = 0;
352*6115d2f3SKarsten Keil 	switch (protocol) {
353*6115d2f3SKarsten Keil 	case -1: /* used for init */
354*6115d2f3SKarsten Keil 		bch->state = -1;
355*6115d2f3SKarsten Keil 	case ISDN_P_NONE:
356*6115d2f3SKarsten Keil 		if (bch->state == ISDN_P_NONE)
357*6115d2f3SKarsten Keil 			break;
358*6115d2f3SKarsten Keil 		hdlc->ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
359*6115d2f3SKarsten Keil 		hdlc->ctrl.sr.mode = HDLC_MODE_TRANS;
360*6115d2f3SKarsten Keil 		write_ctrl(bch, 5);
361*6115d2f3SKarsten Keil 		bch->state = ISDN_P_NONE;
362*6115d2f3SKarsten Keil 		test_and_clear_bit(FLG_HDLC, &bch->Flags);
363*6115d2f3SKarsten Keil 		test_and_clear_bit(FLG_TRANSPARENT, &bch->Flags);
364*6115d2f3SKarsten Keil 		break;
365*6115d2f3SKarsten Keil 	case ISDN_P_B_RAW:
366*6115d2f3SKarsten Keil 		bch->state = protocol;
367*6115d2f3SKarsten Keil 		hdlc->ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
368*6115d2f3SKarsten Keil 		hdlc->ctrl.sr.mode = HDLC_MODE_TRANS;
369*6115d2f3SKarsten Keil 		write_ctrl(bch, 5);
370*6115d2f3SKarsten Keil 		hdlc->ctrl.sr.cmd = HDLC_CMD_XRS;
371*6115d2f3SKarsten Keil 		write_ctrl(bch, 1);
372*6115d2f3SKarsten Keil 		hdlc->ctrl.sr.cmd = 0;
373*6115d2f3SKarsten Keil 		test_and_set_bit(FLG_TRANSPARENT, &bch->Flags);
374*6115d2f3SKarsten Keil 		break;
375*6115d2f3SKarsten Keil 	case ISDN_P_B_HDLC:
376*6115d2f3SKarsten Keil 		bch->state = protocol;
377*6115d2f3SKarsten Keil 		hdlc->ctrl.sr.cmd  = HDLC_CMD_XRS | HDLC_CMD_RRS;
378*6115d2f3SKarsten Keil 		hdlc->ctrl.sr.mode = HDLC_MODE_ITF_FLG;
379*6115d2f3SKarsten Keil 		write_ctrl(bch, 5);
380*6115d2f3SKarsten Keil 		hdlc->ctrl.sr.cmd = HDLC_CMD_XRS;
381*6115d2f3SKarsten Keil 		write_ctrl(bch, 1);
382*6115d2f3SKarsten Keil 		hdlc->ctrl.sr.cmd = 0;
383*6115d2f3SKarsten Keil 		test_and_set_bit(FLG_HDLC, &bch->Flags);
384*6115d2f3SKarsten Keil 		break;
385*6115d2f3SKarsten Keil 	default:
386*6115d2f3SKarsten Keil 		pr_info("%s: protocol not known %x\n", fc->name, protocol);
387*6115d2f3SKarsten Keil 		return -ENOPROTOOPT;
388*6115d2f3SKarsten Keil 	}
389*6115d2f3SKarsten Keil 	return 0;
390*6115d2f3SKarsten Keil }
391*6115d2f3SKarsten Keil 
392*6115d2f3SKarsten Keil static void
393*6115d2f3SKarsten Keil hdlc_empty_fifo(struct bchannel *bch, int count)
394*6115d2f3SKarsten Keil {
395*6115d2f3SKarsten Keil 	u32 *ptr;
396*6115d2f3SKarsten Keil 	u8 *p;
397*6115d2f3SKarsten Keil 	u32  val, addr;
398*6115d2f3SKarsten Keil 	int cnt = 0;
399*6115d2f3SKarsten Keil 	struct fritzcard *fc = bch->hw;
400*6115d2f3SKarsten Keil 
401*6115d2f3SKarsten Keil 	pr_debug("%s: %s %d\n", fc->name, __func__, count);
402*6115d2f3SKarsten Keil 	if (!bch->rx_skb) {
403*6115d2f3SKarsten Keil 		bch->rx_skb = mI_alloc_skb(bch->maxlen, GFP_ATOMIC);
404*6115d2f3SKarsten Keil 		if (!bch->rx_skb) {
405*6115d2f3SKarsten Keil 			pr_info("%s: B receive out of memory\n",
406*6115d2f3SKarsten Keil 				fc->name);
407*6115d2f3SKarsten Keil 			return;
408*6115d2f3SKarsten Keil 		}
409*6115d2f3SKarsten Keil 	}
410*6115d2f3SKarsten Keil 	if ((bch->rx_skb->len + count) > bch->maxlen) {
411*6115d2f3SKarsten Keil 		pr_debug("%s: overrun %d\n", fc->name,
412*6115d2f3SKarsten Keil 			bch->rx_skb->len + count);
413*6115d2f3SKarsten Keil 		return;
414*6115d2f3SKarsten Keil 	}
415*6115d2f3SKarsten Keil 	p = skb_put(bch->rx_skb, count);
416*6115d2f3SKarsten Keil 	ptr = (u32 *)p;
417*6115d2f3SKarsten Keil 	if (AVM_FRITZ_PCIV2 == fc->type)
418*6115d2f3SKarsten Keil 		addr = fc->addr + (bch->nr == 2 ?
419*6115d2f3SKarsten Keil 			AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1);
420*6115d2f3SKarsten Keil 	else {
421*6115d2f3SKarsten Keil 		addr = fc->addr + CHIP_WINDOW;
422*6115d2f3SKarsten Keil 		outl(bch->nr == 2 ? AVM_HDLC_2 : AVM_HDLC_1, fc->addr);
423*6115d2f3SKarsten Keil 	}
424*6115d2f3SKarsten Keil 	while (cnt < count) {
425*6115d2f3SKarsten Keil 		val = le32_to_cpu(inl(addr));
426*6115d2f3SKarsten Keil 		put_unaligned(val, ptr);
427*6115d2f3SKarsten Keil 		ptr++;
428*6115d2f3SKarsten Keil 		cnt += 4;
429*6115d2f3SKarsten Keil 	}
430*6115d2f3SKarsten Keil 	if (debug & DEBUG_HW_BFIFO) {
431*6115d2f3SKarsten Keil 		snprintf(fc->log, LOG_SIZE, "B%1d-recv %s %d ",
432*6115d2f3SKarsten Keil 			bch->nr, fc->name, count);
433*6115d2f3SKarsten Keil 		print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count);
434*6115d2f3SKarsten Keil 	}
435*6115d2f3SKarsten Keil }
436*6115d2f3SKarsten Keil 
437*6115d2f3SKarsten Keil static void
438*6115d2f3SKarsten Keil hdlc_fill_fifo(struct bchannel *bch)
439*6115d2f3SKarsten Keil {
440*6115d2f3SKarsten Keil 	struct fritzcard *fc = bch->hw;
441*6115d2f3SKarsten Keil 	struct hdlc_hw *hdlc;
442*6115d2f3SKarsten Keil 	int count, cnt = 0;
443*6115d2f3SKarsten Keil 	u8 *p;
444*6115d2f3SKarsten Keil 	u32 *ptr, val, addr;
445*6115d2f3SKarsten Keil 
446*6115d2f3SKarsten Keil 	hdlc = &fc->hdlc[(bch->nr - 1) & 1];
447*6115d2f3SKarsten Keil 	if (!bch->tx_skb)
448*6115d2f3SKarsten Keil 		return;
449*6115d2f3SKarsten Keil 	count = bch->tx_skb->len - bch->tx_idx;
450*6115d2f3SKarsten Keil 	if (count <= 0)
451*6115d2f3SKarsten Keil 		return;
452*6115d2f3SKarsten Keil 	p = bch->tx_skb->data + bch->tx_idx;
453*6115d2f3SKarsten Keil 	hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XME;
454*6115d2f3SKarsten Keil 	if (count > HDLC_FIFO_SIZE) {
455*6115d2f3SKarsten Keil 		count = HDLC_FIFO_SIZE;
456*6115d2f3SKarsten Keil 	} else {
457*6115d2f3SKarsten Keil 		if (test_bit(FLG_HDLC, &bch->Flags))
458*6115d2f3SKarsten Keil 			hdlc->ctrl.sr.cmd |= HDLC_CMD_XME;
459*6115d2f3SKarsten Keil 	}
460*6115d2f3SKarsten Keil 	pr_debug("%s: %s %d/%d/%d", fc->name, __func__, count,
461*6115d2f3SKarsten Keil 		bch->tx_idx, bch->tx_skb->len);
462*6115d2f3SKarsten Keil 	ptr = (u32 *)p;
463*6115d2f3SKarsten Keil 	bch->tx_idx += count;
464*6115d2f3SKarsten Keil 	hdlc->ctrl.sr.xml = ((count == HDLC_FIFO_SIZE) ? 0 : count);
465*6115d2f3SKarsten Keil 	if (AVM_FRITZ_PCIV2 == fc->type) {
466*6115d2f3SKarsten Keil 		__write_ctrl_pciv2(fc, hdlc, bch->nr);
467*6115d2f3SKarsten Keil 		addr = fc->addr + (bch->nr == 2 ?
468*6115d2f3SKarsten Keil 			AVM_HDLC_FIFO_2 : AVM_HDLC_FIFO_1);
469*6115d2f3SKarsten Keil 	} else {
470*6115d2f3SKarsten Keil 		__write_ctrl_pci(fc, hdlc, bch->nr);
471*6115d2f3SKarsten Keil 		addr = fc->addr + CHIP_WINDOW;
472*6115d2f3SKarsten Keil 	}
473*6115d2f3SKarsten Keil 	while (cnt < count) {
474*6115d2f3SKarsten Keil 		val = get_unaligned(ptr);
475*6115d2f3SKarsten Keil 		outl(cpu_to_le32(val), addr);
476*6115d2f3SKarsten Keil 		ptr++;
477*6115d2f3SKarsten Keil 		cnt += 4;
478*6115d2f3SKarsten Keil 	}
479*6115d2f3SKarsten Keil 	if (debug & DEBUG_HW_BFIFO) {
480*6115d2f3SKarsten Keil 		snprintf(fc->log, LOG_SIZE, "B%1d-send %s %d ",
481*6115d2f3SKarsten Keil 			bch->nr, fc->name, count);
482*6115d2f3SKarsten Keil 		print_hex_dump_bytes(fc->log, DUMP_PREFIX_OFFSET, p, count);
483*6115d2f3SKarsten Keil 	}
484*6115d2f3SKarsten Keil }
485*6115d2f3SKarsten Keil 
486*6115d2f3SKarsten Keil static void
487*6115d2f3SKarsten Keil HDLC_irq_xpr(struct bchannel *bch)
488*6115d2f3SKarsten Keil {
489*6115d2f3SKarsten Keil 	if (bch->tx_skb && bch->tx_idx < bch->tx_skb->len)
490*6115d2f3SKarsten Keil 		hdlc_fill_fifo(bch);
491*6115d2f3SKarsten Keil 	else {
492*6115d2f3SKarsten Keil 		if (bch->tx_skb) {
493*6115d2f3SKarsten Keil 			/* send confirm, on trans, free on hdlc. */
494*6115d2f3SKarsten Keil 			if (test_bit(FLG_TRANSPARENT, &bch->Flags))
495*6115d2f3SKarsten Keil 				confirm_Bsend(bch);
496*6115d2f3SKarsten Keil 			dev_kfree_skb(bch->tx_skb);
497*6115d2f3SKarsten Keil 		}
498*6115d2f3SKarsten Keil 		if (get_next_bframe(bch))
499*6115d2f3SKarsten Keil 			hdlc_fill_fifo(bch);
500*6115d2f3SKarsten Keil 	}
501*6115d2f3SKarsten Keil }
502*6115d2f3SKarsten Keil 
503*6115d2f3SKarsten Keil static void
504*6115d2f3SKarsten Keil HDLC_irq(struct bchannel *bch, u32 stat)
505*6115d2f3SKarsten Keil {
506*6115d2f3SKarsten Keil 	struct fritzcard *fc = bch->hw;
507*6115d2f3SKarsten Keil 	int		len;
508*6115d2f3SKarsten Keil 	struct hdlc_hw	*hdlc;
509*6115d2f3SKarsten Keil 
510*6115d2f3SKarsten Keil 	hdlc = &fc->hdlc[(bch->nr - 1) & 1];
511*6115d2f3SKarsten Keil 	pr_debug("%s: ch%d stat %#x\n", fc->name, bch->nr, stat);
512*6115d2f3SKarsten Keil 	if (stat & HDLC_INT_RPR) {
513*6115d2f3SKarsten Keil 		if (stat & HDLC_STAT_RDO) {
514*6115d2f3SKarsten Keil 			hdlc->ctrl.sr.xml = 0;
515*6115d2f3SKarsten Keil 			hdlc->ctrl.sr.cmd |= HDLC_CMD_RRS;
516*6115d2f3SKarsten Keil 			write_ctrl(bch, 1);
517*6115d2f3SKarsten Keil 			hdlc->ctrl.sr.cmd &= ~HDLC_CMD_RRS;
518*6115d2f3SKarsten Keil 			write_ctrl(bch, 1);
519*6115d2f3SKarsten Keil 			if (bch->rx_skb)
520*6115d2f3SKarsten Keil 				skb_trim(bch->rx_skb, 0);
521*6115d2f3SKarsten Keil 		} else {
522*6115d2f3SKarsten Keil 			len = (stat & HDLC_STAT_RML_MASK) >> 8;
523*6115d2f3SKarsten Keil 			if (!len)
524*6115d2f3SKarsten Keil 				len = 32;
525*6115d2f3SKarsten Keil 			hdlc_empty_fifo(bch, len);
526*6115d2f3SKarsten Keil 			if (!bch->rx_skb)
527*6115d2f3SKarsten Keil 				goto handle_tx;
528*6115d2f3SKarsten Keil 			if ((stat & HDLC_STAT_RME) || test_bit(FLG_TRANSPARENT,
529*6115d2f3SKarsten Keil 			    &bch->Flags)) {
530*6115d2f3SKarsten Keil 				if (((stat & HDLC_STAT_CRCVFRRAB) ==
531*6115d2f3SKarsten Keil 				    HDLC_STAT_CRCVFR) ||
532*6115d2f3SKarsten Keil 				    test_bit(FLG_TRANSPARENT, &bch->Flags)) {
533*6115d2f3SKarsten Keil 					recv_Bchannel(bch, 0);
534*6115d2f3SKarsten Keil 				} else {
535*6115d2f3SKarsten Keil 					pr_debug("%s: got invalid frame\n",
536*6115d2f3SKarsten Keil 						fc->name);
537*6115d2f3SKarsten Keil 					skb_trim(bch->rx_skb, 0);
538*6115d2f3SKarsten Keil 				}
539*6115d2f3SKarsten Keil 			}
540*6115d2f3SKarsten Keil 		}
541*6115d2f3SKarsten Keil 	}
542*6115d2f3SKarsten Keil handle_tx:
543*6115d2f3SKarsten Keil 	if (stat & HDLC_INT_XDU) {
544*6115d2f3SKarsten Keil 		/* Here we lost an TX interrupt, so
545*6115d2f3SKarsten Keil 		 * restart transmitting the whole frame on HDLC
546*6115d2f3SKarsten Keil 		 * in transparent mode we send the next data
547*6115d2f3SKarsten Keil 		 */
548*6115d2f3SKarsten Keil 		if (bch->tx_skb)
549*6115d2f3SKarsten Keil 			pr_debug("%s: ch%d XDU len(%d) idx(%d) Flags(%lx)\n",
550*6115d2f3SKarsten Keil 				fc->name, bch->nr, bch->tx_skb->len,
551*6115d2f3SKarsten Keil 				bch->tx_idx, bch->Flags);
552*6115d2f3SKarsten Keil 		else
553*6115d2f3SKarsten Keil 			pr_debug("%s: ch%d XDU no tx_skb Flags(%lx)\n",
554*6115d2f3SKarsten Keil 				fc->name, bch->nr, bch->Flags);
555*6115d2f3SKarsten Keil 		if (bch->tx_skb && bch->tx_skb->len) {
556*6115d2f3SKarsten Keil 			if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
557*6115d2f3SKarsten Keil 				bch->tx_idx = 0;
558*6115d2f3SKarsten Keil 		}
559*6115d2f3SKarsten Keil 		hdlc->ctrl.sr.xml = 0;
560*6115d2f3SKarsten Keil 		hdlc->ctrl.sr.cmd |= HDLC_CMD_XRS;
561*6115d2f3SKarsten Keil 		write_ctrl(bch, 1);
562*6115d2f3SKarsten Keil 		hdlc->ctrl.sr.cmd &= ~HDLC_CMD_XRS;
563*6115d2f3SKarsten Keil 		HDLC_irq_xpr(bch);
564*6115d2f3SKarsten Keil 		return;
565*6115d2f3SKarsten Keil 	} else if (stat & HDLC_INT_XPR)
566*6115d2f3SKarsten Keil 		HDLC_irq_xpr(bch);
567*6115d2f3SKarsten Keil }
568*6115d2f3SKarsten Keil 
569*6115d2f3SKarsten Keil static inline void
570*6115d2f3SKarsten Keil HDLC_irq_main(struct fritzcard *fc)
571*6115d2f3SKarsten Keil {
572*6115d2f3SKarsten Keil 	u32 stat;
573*6115d2f3SKarsten Keil 	struct bchannel *bch;
574*6115d2f3SKarsten Keil 
575*6115d2f3SKarsten Keil 	stat = read_status(fc, 1);
576*6115d2f3SKarsten Keil 	if (stat & HDLC_INT_MASK) {
577*6115d2f3SKarsten Keil 		bch = Sel_BCS(fc, 1);
578*6115d2f3SKarsten Keil 		if (bch)
579*6115d2f3SKarsten Keil 			HDLC_irq(bch, stat);
580*6115d2f3SKarsten Keil 		else
581*6115d2f3SKarsten Keil 			pr_debug("%s: spurious ch1 IRQ\n", fc->name);
582*6115d2f3SKarsten Keil 	}
583*6115d2f3SKarsten Keil 	stat = read_status(fc, 2);
584*6115d2f3SKarsten Keil 	if (stat & HDLC_INT_MASK) {
585*6115d2f3SKarsten Keil 		bch = Sel_BCS(fc, 2);
586*6115d2f3SKarsten Keil 		if (bch)
587*6115d2f3SKarsten Keil 			HDLC_irq(bch, stat);
588*6115d2f3SKarsten Keil 		else
589*6115d2f3SKarsten Keil 			pr_debug("%s: spurious ch2 IRQ\n", fc->name);
590*6115d2f3SKarsten Keil 	}
591*6115d2f3SKarsten Keil }
592*6115d2f3SKarsten Keil 
593*6115d2f3SKarsten Keil static irqreturn_t
594*6115d2f3SKarsten Keil avm_fritz_interrupt(int intno, void *dev_id)
595*6115d2f3SKarsten Keil {
596*6115d2f3SKarsten Keil 	struct fritzcard *fc = dev_id;
597*6115d2f3SKarsten Keil 	u8 val;
598*6115d2f3SKarsten Keil 	u8 sval;
599*6115d2f3SKarsten Keil 
600*6115d2f3SKarsten Keil 	spin_lock(&fc->lock);
601*6115d2f3SKarsten Keil 	sval = inb(fc->addr + 2);
602*6115d2f3SKarsten Keil 	pr_debug("%s: irq stat0 %x\n", fc->name, sval);
603*6115d2f3SKarsten Keil 	if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK) {
604*6115d2f3SKarsten Keil 		/* shared  IRQ from other HW */
605*6115d2f3SKarsten Keil 		spin_unlock(&fc->lock);
606*6115d2f3SKarsten Keil 		return IRQ_NONE;
607*6115d2f3SKarsten Keil 	}
608*6115d2f3SKarsten Keil 	fc->irqcnt++;
609*6115d2f3SKarsten Keil 
610*6115d2f3SKarsten Keil 	if (!(sval & AVM_STATUS0_IRQ_ISAC)) {
611*6115d2f3SKarsten Keil 		val = ReadISAC_V1(fc, ISAC_ISTA);
612*6115d2f3SKarsten Keil 		mISDNisac_irq(&fc->isac, val);
613*6115d2f3SKarsten Keil 	}
614*6115d2f3SKarsten Keil 	if (!(sval & AVM_STATUS0_IRQ_HDLC))
615*6115d2f3SKarsten Keil 		HDLC_irq_main(fc);
616*6115d2f3SKarsten Keil 	spin_unlock(&fc->lock);
617*6115d2f3SKarsten Keil 	return IRQ_HANDLED;
618*6115d2f3SKarsten Keil }
619*6115d2f3SKarsten Keil 
620*6115d2f3SKarsten Keil static irqreturn_t
621*6115d2f3SKarsten Keil avm_fritzv2_interrupt(int intno, void *dev_id)
622*6115d2f3SKarsten Keil {
623*6115d2f3SKarsten Keil 	struct fritzcard *fc = dev_id;
624*6115d2f3SKarsten Keil 	u8 val;
625*6115d2f3SKarsten Keil 	u8 sval;
626*6115d2f3SKarsten Keil 
627*6115d2f3SKarsten Keil 	spin_lock(&fc->lock);
628*6115d2f3SKarsten Keil 	sval = inb(fc->addr + 2);
629*6115d2f3SKarsten Keil 	pr_debug("%s: irq stat0 %x\n", fc->name, sval);
630*6115d2f3SKarsten Keil 	if (!(sval & AVM_STATUS0_IRQ_MASK)) {
631*6115d2f3SKarsten Keil 		/* shared  IRQ from other HW */
632*6115d2f3SKarsten Keil 		spin_unlock(&fc->lock);
633*6115d2f3SKarsten Keil 		return IRQ_NONE;
634*6115d2f3SKarsten Keil 	}
635*6115d2f3SKarsten Keil 	fc->irqcnt++;
636*6115d2f3SKarsten Keil 
637*6115d2f3SKarsten Keil 	if (sval & AVM_STATUS0_IRQ_HDLC)
638*6115d2f3SKarsten Keil 		HDLC_irq_main(fc);
639*6115d2f3SKarsten Keil 	if (sval & AVM_STATUS0_IRQ_ISAC) {
640*6115d2f3SKarsten Keil 		val = ReadISAC_V2(fc, ISACX_ISTA);
641*6115d2f3SKarsten Keil 		mISDNisac_irq(&fc->isac, val);
642*6115d2f3SKarsten Keil 	}
643*6115d2f3SKarsten Keil 	if (sval & AVM_STATUS0_IRQ_TIMER) {
644*6115d2f3SKarsten Keil 		pr_debug("%s: timer irq\n", fc->name);
645*6115d2f3SKarsten Keil 		outb(fc->ctrlreg | AVM_STATUS0_RES_TIMER, fc->addr + 2);
646*6115d2f3SKarsten Keil 		udelay(1);
647*6115d2f3SKarsten Keil 		outb(fc->ctrlreg, fc->addr + 2);
648*6115d2f3SKarsten Keil 	}
649*6115d2f3SKarsten Keil 	spin_unlock(&fc->lock);
650*6115d2f3SKarsten Keil 	return IRQ_HANDLED;
651*6115d2f3SKarsten Keil }
652*6115d2f3SKarsten Keil 
653*6115d2f3SKarsten Keil static int
654*6115d2f3SKarsten Keil avm_l2l1B(struct mISDNchannel *ch, struct sk_buff *skb)
655*6115d2f3SKarsten Keil {
656*6115d2f3SKarsten Keil 	struct bchannel *bch = container_of(ch, struct bchannel, ch);
657*6115d2f3SKarsten Keil 	struct fritzcard *fc = bch->hw;
658*6115d2f3SKarsten Keil 	int ret = -EINVAL;
659*6115d2f3SKarsten Keil 	struct mISDNhead *hh = mISDN_HEAD_P(skb);
660*6115d2f3SKarsten Keil 	u32 id;
661*6115d2f3SKarsten Keil 	u_long flags;
662*6115d2f3SKarsten Keil 
663*6115d2f3SKarsten Keil 	switch (hh->prim) {
664*6115d2f3SKarsten Keil 	case PH_DATA_REQ:
665*6115d2f3SKarsten Keil 		spin_lock_irqsave(&fc->lock, flags);
666*6115d2f3SKarsten Keil 		ret = bchannel_senddata(bch, skb);
667*6115d2f3SKarsten Keil 		if (ret > 0) { /* direct TX */
668*6115d2f3SKarsten Keil 			id = hh->id; /* skb can be freed */
669*6115d2f3SKarsten Keil 			hdlc_fill_fifo(bch);
670*6115d2f3SKarsten Keil 			ret = 0;
671*6115d2f3SKarsten Keil 			spin_unlock_irqrestore(&fc->lock, flags);
672*6115d2f3SKarsten Keil 			if (!test_bit(FLG_TRANSPARENT, &bch->Flags))
673*6115d2f3SKarsten Keil 				queue_ch_frame(ch, PH_DATA_CNF, id, NULL);
674*6115d2f3SKarsten Keil 		} else
675*6115d2f3SKarsten Keil 			spin_unlock_irqrestore(&fc->lock, flags);
676*6115d2f3SKarsten Keil 		return ret;
677*6115d2f3SKarsten Keil 	case PH_ACTIVATE_REQ:
678*6115d2f3SKarsten Keil 		spin_lock_irqsave(&fc->lock, flags);
679*6115d2f3SKarsten Keil 		if (!test_and_set_bit(FLG_ACTIVE, &bch->Flags))
680*6115d2f3SKarsten Keil 			ret = modehdlc(bch, ch->protocol);
681*6115d2f3SKarsten Keil 		else
682*6115d2f3SKarsten Keil 			ret = 0;
683*6115d2f3SKarsten Keil 		spin_unlock_irqrestore(&fc->lock, flags);
684*6115d2f3SKarsten Keil 		if (!ret)
685*6115d2f3SKarsten Keil 			_queue_data(ch, PH_ACTIVATE_IND, MISDN_ID_ANY, 0,
686*6115d2f3SKarsten Keil 				NULL, GFP_KERNEL);
687*6115d2f3SKarsten Keil 		break;
688*6115d2f3SKarsten Keil 	case PH_DEACTIVATE_REQ:
689*6115d2f3SKarsten Keil 		spin_lock_irqsave(&fc->lock, flags);
690*6115d2f3SKarsten Keil 		mISDN_clear_bchannel(bch);
691*6115d2f3SKarsten Keil 		modehdlc(bch, ISDN_P_NONE);
692*6115d2f3SKarsten Keil 		spin_unlock_irqrestore(&fc->lock, flags);
693*6115d2f3SKarsten Keil 		_queue_data(ch, PH_DEACTIVATE_IND, MISDN_ID_ANY, 0,
694*6115d2f3SKarsten Keil 			NULL, GFP_KERNEL);
695*6115d2f3SKarsten Keil 		ret = 0;
696*6115d2f3SKarsten Keil 		break;
697*6115d2f3SKarsten Keil 	}
698*6115d2f3SKarsten Keil 	if (!ret)
699*6115d2f3SKarsten Keil 		dev_kfree_skb(skb);
700*6115d2f3SKarsten Keil 	return ret;
701*6115d2f3SKarsten Keil }
702*6115d2f3SKarsten Keil 
703*6115d2f3SKarsten Keil static void
704*6115d2f3SKarsten Keil inithdlc(struct fritzcard *fc)
705*6115d2f3SKarsten Keil {
706*6115d2f3SKarsten Keil 	modehdlc(&fc->bch[0], -1);
707*6115d2f3SKarsten Keil 	modehdlc(&fc->bch[1], -1);
708*6115d2f3SKarsten Keil }
709*6115d2f3SKarsten Keil 
710*6115d2f3SKarsten Keil void
711*6115d2f3SKarsten Keil clear_pending_hdlc_ints(struct fritzcard *fc)
712*6115d2f3SKarsten Keil {
713*6115d2f3SKarsten Keil 	u32 val;
714*6115d2f3SKarsten Keil 
715*6115d2f3SKarsten Keil 	val = read_status(fc, 1);
716*6115d2f3SKarsten Keil 	pr_debug("%s: HDLC 1 STA %x\n", fc->name, val);
717*6115d2f3SKarsten Keil 	val = read_status(fc, 2);
718*6115d2f3SKarsten Keil 	pr_debug("%s: HDLC 2 STA %x\n", fc->name, val);
719*6115d2f3SKarsten Keil }
720*6115d2f3SKarsten Keil 
721*6115d2f3SKarsten Keil static void
722*6115d2f3SKarsten Keil reset_avm(struct fritzcard *fc)
723*6115d2f3SKarsten Keil {
724*6115d2f3SKarsten Keil 	switch (fc->type) {
725*6115d2f3SKarsten Keil 	case AVM_FRITZ_PCI:
726*6115d2f3SKarsten Keil 		fc->ctrlreg = AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER;
727*6115d2f3SKarsten Keil 		break;
728*6115d2f3SKarsten Keil 	case AVM_FRITZ_PCIV2:
729*6115d2f3SKarsten Keil 		fc->ctrlreg = AVM_STATUS0_RESET;
730*6115d2f3SKarsten Keil 		break;
731*6115d2f3SKarsten Keil 	}
732*6115d2f3SKarsten Keil 	if (debug & DEBUG_HW)
733*6115d2f3SKarsten Keil 		pr_notice("%s: reset\n", fc->name);
734*6115d2f3SKarsten Keil 	disable_hwirq(fc);
735*6115d2f3SKarsten Keil 	mdelay(5);
736*6115d2f3SKarsten Keil 	switch (fc->type) {
737*6115d2f3SKarsten Keil 	case AVM_FRITZ_PCI:
738*6115d2f3SKarsten Keil 		fc->ctrlreg = AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER;
739*6115d2f3SKarsten Keil 		disable_hwirq(fc);
740*6115d2f3SKarsten Keil 		outb(AVM_STATUS1_ENA_IOM, fc->addr + 3);
741*6115d2f3SKarsten Keil 		break;
742*6115d2f3SKarsten Keil 	case AVM_FRITZ_PCIV2:
743*6115d2f3SKarsten Keil 		fc->ctrlreg = 0;
744*6115d2f3SKarsten Keil 		disable_hwirq(fc);
745*6115d2f3SKarsten Keil 		break;
746*6115d2f3SKarsten Keil 	}
747*6115d2f3SKarsten Keil 	mdelay(1);
748*6115d2f3SKarsten Keil 	if (debug & DEBUG_HW)
749*6115d2f3SKarsten Keil 		pr_notice("%s: S0/S1 %x/%x\n", fc->name,
750*6115d2f3SKarsten Keil 			inb(fc->addr + 2), inb(fc->addr + 3));
751*6115d2f3SKarsten Keil }
752*6115d2f3SKarsten Keil 
753*6115d2f3SKarsten Keil static int
754*6115d2f3SKarsten Keil init_card(struct fritzcard *fc)
755*6115d2f3SKarsten Keil {
756*6115d2f3SKarsten Keil 	int		ret, cnt = 3;
757*6115d2f3SKarsten Keil 	u_long		flags;
758*6115d2f3SKarsten Keil 
759*6115d2f3SKarsten Keil 	reset_avm(fc); /* disable IRQ */
760*6115d2f3SKarsten Keil 	if (fc->type == AVM_FRITZ_PCIV2)
761*6115d2f3SKarsten Keil 		ret = request_irq(fc->irq, avm_fritzv2_interrupt,
762*6115d2f3SKarsten Keil 			IRQF_SHARED, fc->name, fc);
763*6115d2f3SKarsten Keil 	else
764*6115d2f3SKarsten Keil 		ret = request_irq(fc->irq, avm_fritz_interrupt,
765*6115d2f3SKarsten Keil 			IRQF_SHARED, fc->name, fc);
766*6115d2f3SKarsten Keil 	if (ret) {
767*6115d2f3SKarsten Keil 		pr_info("%s: couldn't get interrupt %d\n",
768*6115d2f3SKarsten Keil 			fc->name, fc->irq);
769*6115d2f3SKarsten Keil 		return ret;
770*6115d2f3SKarsten Keil 	}
771*6115d2f3SKarsten Keil 	while (cnt--) {
772*6115d2f3SKarsten Keil 		spin_lock_irqsave(&fc->lock, flags);
773*6115d2f3SKarsten Keil 		ret = fc->isac.init(&fc->isac);
774*6115d2f3SKarsten Keil 		if (ret) {
775*6115d2f3SKarsten Keil 			spin_unlock_irqrestore(&fc->lock, flags);
776*6115d2f3SKarsten Keil 			pr_info("%s: ISAC init failed with %d\n",
777*6115d2f3SKarsten Keil 				fc->name, ret);
778*6115d2f3SKarsten Keil 			break;
779*6115d2f3SKarsten Keil 		}
780*6115d2f3SKarsten Keil 		clear_pending_hdlc_ints(fc);
781*6115d2f3SKarsten Keil 		inithdlc(fc);
782*6115d2f3SKarsten Keil 		enable_hwirq(fc);
783*6115d2f3SKarsten Keil 		/* RESET Receiver and Transmitter */
784*6115d2f3SKarsten Keil 		if (AVM_FRITZ_PCIV2 == fc->type) {
785*6115d2f3SKarsten Keil 			WriteISAC_V2(fc, ISACX_MASK, 0);
786*6115d2f3SKarsten Keil 			WriteISAC_V2(fc, ISACX_CMDRD, 0x41);
787*6115d2f3SKarsten Keil 		} else {
788*6115d2f3SKarsten Keil 			WriteISAC_V1(fc, ISAC_MASK, 0);
789*6115d2f3SKarsten Keil 			WriteISAC_V1(fc, ISAC_CMDR, 0x41);
790*6115d2f3SKarsten Keil 		}
791*6115d2f3SKarsten Keil 		spin_unlock_irqrestore(&fc->lock, flags);
792*6115d2f3SKarsten Keil 		/* Timeout 10ms */
793*6115d2f3SKarsten Keil 		msleep_interruptible(10);
794*6115d2f3SKarsten Keil 		if (debug & DEBUG_HW)
795*6115d2f3SKarsten Keil 			pr_notice("%s: IRQ %d count %d\n", fc->name,
796*6115d2f3SKarsten Keil 				fc->irq, fc->irqcnt);
797*6115d2f3SKarsten Keil 		if (!fc->irqcnt) {
798*6115d2f3SKarsten Keil 			pr_info("%s: IRQ(%d) getting no IRQs during init %d\n",
799*6115d2f3SKarsten Keil 				fc->name, fc->irq, 3 - cnt);
800*6115d2f3SKarsten Keil 			reset_avm(fc);
801*6115d2f3SKarsten Keil 		} else
802*6115d2f3SKarsten Keil 			return 0;
803*6115d2f3SKarsten Keil 	}
804*6115d2f3SKarsten Keil 	free_irq(fc->irq, fc);
805*6115d2f3SKarsten Keil 	return -EIO;
806*6115d2f3SKarsten Keil }
807*6115d2f3SKarsten Keil 
808*6115d2f3SKarsten Keil static int
809*6115d2f3SKarsten Keil channel_bctrl(struct bchannel *bch, struct mISDN_ctrl_req *cq)
810*6115d2f3SKarsten Keil {
811*6115d2f3SKarsten Keil 	int ret = 0;
812*6115d2f3SKarsten Keil 	struct fritzcard *fc = bch->hw;
813*6115d2f3SKarsten Keil 
814*6115d2f3SKarsten Keil 	switch (cq->op) {
815*6115d2f3SKarsten Keil 	case MISDN_CTRL_GETOP:
816*6115d2f3SKarsten Keil 		cq->op = 0;
817*6115d2f3SKarsten Keil 		break;
818*6115d2f3SKarsten Keil 	/* Nothing implemented yet */
819*6115d2f3SKarsten Keil 	case MISDN_CTRL_FILL_EMPTY:
820*6115d2f3SKarsten Keil 	default:
821*6115d2f3SKarsten Keil 		pr_info("%s: %s unknown Op %x\n", fc->name, __func__, cq->op);
822*6115d2f3SKarsten Keil 		ret = -EINVAL;
823*6115d2f3SKarsten Keil 		break;
824*6115d2f3SKarsten Keil 	}
825*6115d2f3SKarsten Keil 	return ret;
826*6115d2f3SKarsten Keil }
827*6115d2f3SKarsten Keil 
828*6115d2f3SKarsten Keil static int
829*6115d2f3SKarsten Keil avm_bctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
830*6115d2f3SKarsten Keil {
831*6115d2f3SKarsten Keil 	struct bchannel *bch = container_of(ch, struct bchannel, ch);
832*6115d2f3SKarsten Keil 	struct fritzcard *fc = bch->hw;
833*6115d2f3SKarsten Keil 	int ret = -EINVAL;
834*6115d2f3SKarsten Keil 	u_long flags;
835*6115d2f3SKarsten Keil 
836*6115d2f3SKarsten Keil 	pr_debug("%s: %s cmd:%x %p\n", fc->name, __func__, cmd, arg);
837*6115d2f3SKarsten Keil 	switch (cmd) {
838*6115d2f3SKarsten Keil 	case CLOSE_CHANNEL:
839*6115d2f3SKarsten Keil 		test_and_clear_bit(FLG_OPEN, &bch->Flags);
840*6115d2f3SKarsten Keil 		if (test_bit(FLG_ACTIVE, &bch->Flags)) {
841*6115d2f3SKarsten Keil 			spin_lock_irqsave(&fc->lock, flags);
842*6115d2f3SKarsten Keil 			mISDN_freebchannel(bch);
843*6115d2f3SKarsten Keil 			test_and_clear_bit(FLG_TX_BUSY, &bch->Flags);
844*6115d2f3SKarsten Keil 			test_and_clear_bit(FLG_ACTIVE, &bch->Flags);
845*6115d2f3SKarsten Keil 			modehdlc(bch, ISDN_P_NONE);
846*6115d2f3SKarsten Keil 			spin_unlock_irqrestore(&fc->lock, flags);
847*6115d2f3SKarsten Keil 		}
848*6115d2f3SKarsten Keil 		ch->protocol = ISDN_P_NONE;
849*6115d2f3SKarsten Keil 		ch->peer = NULL;
850*6115d2f3SKarsten Keil 		module_put(THIS_MODULE);
851*6115d2f3SKarsten Keil 		ret = 0;
852*6115d2f3SKarsten Keil 		break;
853*6115d2f3SKarsten Keil 	case CONTROL_CHANNEL:
854*6115d2f3SKarsten Keil 		ret = channel_bctrl(bch, arg);
855*6115d2f3SKarsten Keil 		break;
856*6115d2f3SKarsten Keil 	default:
857*6115d2f3SKarsten Keil 		pr_info("%s: %s unknown prim(%x)\n", fc->name, __func__, cmd);
858*6115d2f3SKarsten Keil 	}
859*6115d2f3SKarsten Keil 	return ret;
860*6115d2f3SKarsten Keil }
861*6115d2f3SKarsten Keil 
862*6115d2f3SKarsten Keil static int
863*6115d2f3SKarsten Keil channel_ctrl(struct fritzcard  *fc, struct mISDN_ctrl_req *cq)
864*6115d2f3SKarsten Keil {
865*6115d2f3SKarsten Keil 	int	ret = 0;
866*6115d2f3SKarsten Keil 
867*6115d2f3SKarsten Keil 	switch (cq->op) {
868*6115d2f3SKarsten Keil 	case MISDN_CTRL_GETOP:
869*6115d2f3SKarsten Keil 		cq->op = MISDN_CTRL_LOOP;
870*6115d2f3SKarsten Keil 		break;
871*6115d2f3SKarsten Keil 	case MISDN_CTRL_LOOP:
872*6115d2f3SKarsten Keil 		/* cq->channel: 0 disable, 1 B1 loop 2 B2 loop, 3 both */
873*6115d2f3SKarsten Keil 		if (cq->channel < 0 || cq->channel > 3) {
874*6115d2f3SKarsten Keil 			ret = -EINVAL;
875*6115d2f3SKarsten Keil 			break;
876*6115d2f3SKarsten Keil 		}
877*6115d2f3SKarsten Keil 		ret = fc->isac.ctrl(&fc->isac, HW_TESTLOOP, cq->channel);
878*6115d2f3SKarsten Keil 		break;
879*6115d2f3SKarsten Keil 	default:
880*6115d2f3SKarsten Keil 		pr_info("%s: %s unknown Op %x\n", fc->name, __func__, cq->op);
881*6115d2f3SKarsten Keil 		ret = -EINVAL;
882*6115d2f3SKarsten Keil 		break;
883*6115d2f3SKarsten Keil 	}
884*6115d2f3SKarsten Keil 	return ret;
885*6115d2f3SKarsten Keil }
886*6115d2f3SKarsten Keil 
887*6115d2f3SKarsten Keil static int
888*6115d2f3SKarsten Keil open_bchannel(struct fritzcard *fc, struct channel_req *rq)
889*6115d2f3SKarsten Keil {
890*6115d2f3SKarsten Keil 	struct bchannel		*bch;
891*6115d2f3SKarsten Keil 
892*6115d2f3SKarsten Keil 	if (rq->adr.channel > 2)
893*6115d2f3SKarsten Keil 		return -EINVAL;
894*6115d2f3SKarsten Keil 	if (rq->protocol == ISDN_P_NONE)
895*6115d2f3SKarsten Keil 		return -EINVAL;
896*6115d2f3SKarsten Keil 	bch = &fc->bch[rq->adr.channel - 1];
897*6115d2f3SKarsten Keil 	if (test_and_set_bit(FLG_OPEN, &bch->Flags))
898*6115d2f3SKarsten Keil 		return -EBUSY; /* b-channel can be only open once */
899*6115d2f3SKarsten Keil 	test_and_clear_bit(FLG_FILLEMPTY, &bch->Flags);
900*6115d2f3SKarsten Keil 	bch->ch.protocol = rq->protocol;
901*6115d2f3SKarsten Keil 	rq->ch = &bch->ch;
902*6115d2f3SKarsten Keil 	return 0;
903*6115d2f3SKarsten Keil }
904*6115d2f3SKarsten Keil 
905*6115d2f3SKarsten Keil /*
906*6115d2f3SKarsten Keil  * device control function
907*6115d2f3SKarsten Keil  */
908*6115d2f3SKarsten Keil static int
909*6115d2f3SKarsten Keil avm_dctrl(struct mISDNchannel *ch, u32 cmd, void *arg)
910*6115d2f3SKarsten Keil {
911*6115d2f3SKarsten Keil 	struct mISDNdevice	*dev = container_of(ch, struct mISDNdevice, D);
912*6115d2f3SKarsten Keil 	struct dchannel		*dch = container_of(dev, struct dchannel, dev);
913*6115d2f3SKarsten Keil 	struct fritzcard	*fc = dch->hw;
914*6115d2f3SKarsten Keil 	struct channel_req	*rq;
915*6115d2f3SKarsten Keil 	int			err = 0;
916*6115d2f3SKarsten Keil 
917*6115d2f3SKarsten Keil 	pr_debug("%s: %s cmd:%x %p\n", fc->name, __func__, cmd, arg);
918*6115d2f3SKarsten Keil 	switch (cmd) {
919*6115d2f3SKarsten Keil 	case OPEN_CHANNEL:
920*6115d2f3SKarsten Keil 		rq = arg;
921*6115d2f3SKarsten Keil 		if (rq->protocol == ISDN_P_TE_S0)
922*6115d2f3SKarsten Keil 			err = fc->isac.open(&fc->isac, rq);
923*6115d2f3SKarsten Keil 		else
924*6115d2f3SKarsten Keil 			err = open_bchannel(fc, rq);
925*6115d2f3SKarsten Keil 		if (err)
926*6115d2f3SKarsten Keil 			break;
927*6115d2f3SKarsten Keil 		if (!try_module_get(THIS_MODULE))
928*6115d2f3SKarsten Keil 			pr_info("%s: cannot get module\n", fc->name);
929*6115d2f3SKarsten Keil 		break;
930*6115d2f3SKarsten Keil 	case CLOSE_CHANNEL:
931*6115d2f3SKarsten Keil 		pr_debug("%s: dev(%d) close from %p\n", fc->name, dch->dev.id,
932*6115d2f3SKarsten Keil 			__builtin_return_address(0));
933*6115d2f3SKarsten Keil 		module_put(THIS_MODULE);
934*6115d2f3SKarsten Keil 		break;
935*6115d2f3SKarsten Keil 	case CONTROL_CHANNEL:
936*6115d2f3SKarsten Keil 		err = channel_ctrl(fc, arg);
937*6115d2f3SKarsten Keil 		break;
938*6115d2f3SKarsten Keil 	default:
939*6115d2f3SKarsten Keil 		pr_debug("%s: %s unknown command %x\n",
940*6115d2f3SKarsten Keil 			fc->name, __func__, cmd);
941*6115d2f3SKarsten Keil 		return -EINVAL;
942*6115d2f3SKarsten Keil 	}
943*6115d2f3SKarsten Keil 	return err;
944*6115d2f3SKarsten Keil }
945*6115d2f3SKarsten Keil 
946*6115d2f3SKarsten Keil int
947*6115d2f3SKarsten Keil setup_fritz(struct fritzcard *fc)
948*6115d2f3SKarsten Keil {
949*6115d2f3SKarsten Keil 	u32 val, ver;
950*6115d2f3SKarsten Keil 
951*6115d2f3SKarsten Keil 	if (!request_region(fc->addr, 32, fc->name)) {
952*6115d2f3SKarsten Keil 		pr_info("%s: AVM config port %x-%x already in use\n",
953*6115d2f3SKarsten Keil 			fc->name, fc->addr, fc->addr + 31);
954*6115d2f3SKarsten Keil 		return -EIO;
955*6115d2f3SKarsten Keil 	}
956*6115d2f3SKarsten Keil 	switch (fc->type) {
957*6115d2f3SKarsten Keil 	case AVM_FRITZ_PCI:
958*6115d2f3SKarsten Keil 		val = inl(fc->addr);
959*6115d2f3SKarsten Keil 		outl(AVM_HDLC_1, fc->addr + CHIP_INDEX);
960*6115d2f3SKarsten Keil 		ver = inl(fc->addr + CHIP_WINDOW + HDLC_STATUS) >> 24;
961*6115d2f3SKarsten Keil 		if (debug & DEBUG_HW) {
962*6115d2f3SKarsten Keil 			pr_notice("%s: PCI stat %#x\n", fc->name, val);
963*6115d2f3SKarsten Keil 			pr_notice("%s: PCI Class %X Rev %d\n", fc->name,
964*6115d2f3SKarsten Keil 				val & 0xff, (val >> 8) & 0xff);
965*6115d2f3SKarsten Keil 			pr_notice("%s: HDLC version %x\n", fc->name, ver & 0xf);
966*6115d2f3SKarsten Keil 		}
967*6115d2f3SKarsten Keil 		ASSIGN_FUNC(V1, ISAC, fc->isac);
968*6115d2f3SKarsten Keil 		fc->isac.type = IPAC_TYPE_ISAC;
969*6115d2f3SKarsten Keil 		break;
970*6115d2f3SKarsten Keil 	case AVM_FRITZ_PCIV2:
971*6115d2f3SKarsten Keil 		val = inl(fc->addr);
972*6115d2f3SKarsten Keil 		ver = inl(fc->addr + AVM_HDLC_STATUS_1) >> 24;
973*6115d2f3SKarsten Keil 		if (debug & DEBUG_HW) {
974*6115d2f3SKarsten Keil 			pr_notice("%s: PCI V2 stat %#x\n", fc->name, val);
975*6115d2f3SKarsten Keil 			pr_notice("%s: PCI V2 Class %X Rev %d\n", fc->name,
976*6115d2f3SKarsten Keil 				val & 0xff, (val>>8) & 0xff);
977*6115d2f3SKarsten Keil 			pr_notice("%s: HDLC version %x\n", fc->name, ver & 0xf);
978*6115d2f3SKarsten Keil 		}
979*6115d2f3SKarsten Keil 		ASSIGN_FUNC(V2, ISAC, fc->isac);
980*6115d2f3SKarsten Keil 		fc->isac.type = IPAC_TYPE_ISACX;
981*6115d2f3SKarsten Keil 		break;
982*6115d2f3SKarsten Keil 	default:
983*6115d2f3SKarsten Keil 		release_region(fc->addr, 32);
984*6115d2f3SKarsten Keil 		pr_info("%s: AVM unknown type %d\n", fc->name, fc->type);
985*6115d2f3SKarsten Keil 		return -ENODEV;
986*6115d2f3SKarsten Keil 	}
987*6115d2f3SKarsten Keil 	pr_notice("%s: %s config irq:%d base:0x%X\n", fc->name,
988*6115d2f3SKarsten Keil 		(fc->type == AVM_FRITZ_PCI) ? "AVM Fritz!CARD PCI" :
989*6115d2f3SKarsten Keil 		"AVM Fritz!CARD PCIv2", fc->irq, fc->addr);
990*6115d2f3SKarsten Keil 	return 0;
991*6115d2f3SKarsten Keil }
992*6115d2f3SKarsten Keil 
993*6115d2f3SKarsten Keil static void
994*6115d2f3SKarsten Keil release_card(struct fritzcard *card)
995*6115d2f3SKarsten Keil {
996*6115d2f3SKarsten Keil 	u_long flags;
997*6115d2f3SKarsten Keil 
998*6115d2f3SKarsten Keil 	disable_hwirq(card);
999*6115d2f3SKarsten Keil 	spin_lock_irqsave(&card->lock, flags);
1000*6115d2f3SKarsten Keil 	modehdlc(&card->bch[0], ISDN_P_NONE);
1001*6115d2f3SKarsten Keil 	modehdlc(&card->bch[1], ISDN_P_NONE);
1002*6115d2f3SKarsten Keil 	spin_unlock_irqrestore(&card->lock, flags);
1003*6115d2f3SKarsten Keil 	card->isac.release(&card->isac);
1004*6115d2f3SKarsten Keil 	free_irq(card->irq, card);
1005*6115d2f3SKarsten Keil 	mISDN_freebchannel(&card->bch[1]);
1006*6115d2f3SKarsten Keil 	mISDN_freebchannel(&card->bch[0]);
1007*6115d2f3SKarsten Keil 	mISDN_unregister_device(&card->isac.dch.dev);
1008*6115d2f3SKarsten Keil 	release_region(card->addr, 32);
1009*6115d2f3SKarsten Keil 	pci_disable_device(card->pdev);
1010*6115d2f3SKarsten Keil 	pci_set_drvdata(card->pdev, NULL);
1011*6115d2f3SKarsten Keil 	write_lock_irqsave(&card_lock, flags);
1012*6115d2f3SKarsten Keil 	list_del(&card->list);
1013*6115d2f3SKarsten Keil 	write_unlock_irqrestore(&card_lock, flags);
1014*6115d2f3SKarsten Keil 	kfree(card);
1015*6115d2f3SKarsten Keil 	AVM_cnt--;
1016*6115d2f3SKarsten Keil }
1017*6115d2f3SKarsten Keil 
1018*6115d2f3SKarsten Keil static int __devinit
1019*6115d2f3SKarsten Keil setup_instance(struct fritzcard *card)
1020*6115d2f3SKarsten Keil {
1021*6115d2f3SKarsten Keil 	int i, err;
1022*6115d2f3SKarsten Keil 	u_long flags;
1023*6115d2f3SKarsten Keil 
1024*6115d2f3SKarsten Keil 	snprintf(card->name, MISDN_MAX_IDLEN - 1, "AVM.%d", AVM_cnt + 1);
1025*6115d2f3SKarsten Keil 	write_lock_irqsave(&card_lock, flags);
1026*6115d2f3SKarsten Keil 	list_add_tail(&card->list, &Cards);
1027*6115d2f3SKarsten Keil 	write_unlock_irqrestore(&card_lock, flags);
1028*6115d2f3SKarsten Keil 
1029*6115d2f3SKarsten Keil 	_set_debug(card);
1030*6115d2f3SKarsten Keil 	card->isac.name = card->name;
1031*6115d2f3SKarsten Keil 	spin_lock_init(&card->lock);
1032*6115d2f3SKarsten Keil 	card->isac.hwlock = &card->lock;
1033*6115d2f3SKarsten Keil 	mISDNisac_init(&card->isac, card);
1034*6115d2f3SKarsten Keil 
1035*6115d2f3SKarsten Keil 	card->isac.dch.dev.Bprotocols = (1 << (ISDN_P_B_RAW & ISDN_P_B_MASK)) |
1036*6115d2f3SKarsten Keil 	    (1 << (ISDN_P_B_HDLC & ISDN_P_B_MASK));
1037*6115d2f3SKarsten Keil 	card->isac.dch.dev.D.ctrl = avm_dctrl;
1038*6115d2f3SKarsten Keil 	for (i = 0; i < 2; i++) {
1039*6115d2f3SKarsten Keil 		card->bch[i].nr = i + 1;
1040*6115d2f3SKarsten Keil 		set_channelmap(i + 1, card->isac.dch.dev.channelmap);
1041*6115d2f3SKarsten Keil 		mISDN_initbchannel(&card->bch[i], MAX_DATA_MEM);
1042*6115d2f3SKarsten Keil 		card->bch[i].hw = card;
1043*6115d2f3SKarsten Keil 		card->bch[i].ch.send = avm_l2l1B;
1044*6115d2f3SKarsten Keil 		card->bch[i].ch.ctrl = avm_bctrl;
1045*6115d2f3SKarsten Keil 		card->bch[i].ch.nr = i + 1;
1046*6115d2f3SKarsten Keil 		list_add(&card->bch[i].ch.list, &card->isac.dch.dev.bchannels);
1047*6115d2f3SKarsten Keil 	}
1048*6115d2f3SKarsten Keil 	err = setup_fritz(card);
1049*6115d2f3SKarsten Keil 	if (err)
1050*6115d2f3SKarsten Keil 		goto error;
1051*6115d2f3SKarsten Keil 	err = mISDN_register_device(&card->isac.dch.dev, &card->pdev->dev,
1052*6115d2f3SKarsten Keil 		card->name);
1053*6115d2f3SKarsten Keil 	if (err)
1054*6115d2f3SKarsten Keil 		goto error_reg;
1055*6115d2f3SKarsten Keil 	err = init_card(card);
1056*6115d2f3SKarsten Keil 	if (!err)  {
1057*6115d2f3SKarsten Keil 		AVM_cnt++;
1058*6115d2f3SKarsten Keil 		pr_notice("AVM %d cards installed DEBUG\n", AVM_cnt);
1059*6115d2f3SKarsten Keil 		return 0;
1060*6115d2f3SKarsten Keil 	}
1061*6115d2f3SKarsten Keil 	mISDN_unregister_device(&card->isac.dch.dev);
1062*6115d2f3SKarsten Keil error_reg:
1063*6115d2f3SKarsten Keil 	release_region(card->addr, 32);
1064*6115d2f3SKarsten Keil error:
1065*6115d2f3SKarsten Keil 	card->isac.release(&card->isac);
1066*6115d2f3SKarsten Keil 	mISDN_freebchannel(&card->bch[1]);
1067*6115d2f3SKarsten Keil 	mISDN_freebchannel(&card->bch[0]);
1068*6115d2f3SKarsten Keil 	write_lock_irqsave(&card_lock, flags);
1069*6115d2f3SKarsten Keil 	list_del(&card->list);
1070*6115d2f3SKarsten Keil 	write_unlock_irqrestore(&card_lock, flags);
1071*6115d2f3SKarsten Keil 	kfree(card);
1072*6115d2f3SKarsten Keil 	return err;
1073*6115d2f3SKarsten Keil }
1074*6115d2f3SKarsten Keil 
1075*6115d2f3SKarsten Keil static int __devinit
1076*6115d2f3SKarsten Keil fritzpci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
1077*6115d2f3SKarsten Keil {
1078*6115d2f3SKarsten Keil 	int err = -ENOMEM;
1079*6115d2f3SKarsten Keil 	struct fritzcard *card;
1080*6115d2f3SKarsten Keil 
1081*6115d2f3SKarsten Keil 	card = kzalloc(sizeof(struct fritzcard), GFP_KERNEL);
1082*6115d2f3SKarsten Keil 	if (!card) {
1083*6115d2f3SKarsten Keil 		pr_info("No kmem for fritzcard\n");
1084*6115d2f3SKarsten Keil 		return err;
1085*6115d2f3SKarsten Keil 	}
1086*6115d2f3SKarsten Keil 	if (pdev->device == PCI_DEVICE_ID_AVM_A1_V2)
1087*6115d2f3SKarsten Keil 		card->type = AVM_FRITZ_PCIV2;
1088*6115d2f3SKarsten Keil 	else
1089*6115d2f3SKarsten Keil 		card->type = AVM_FRITZ_PCI;
1090*6115d2f3SKarsten Keil 	card->pdev = pdev;
1091*6115d2f3SKarsten Keil 	err = pci_enable_device(pdev);
1092*6115d2f3SKarsten Keil 	if (err) {
1093*6115d2f3SKarsten Keil 		kfree(card);
1094*6115d2f3SKarsten Keil 		return err;
1095*6115d2f3SKarsten Keil 	}
1096*6115d2f3SKarsten Keil 
1097*6115d2f3SKarsten Keil 	pr_notice("mISDN: found adapter %s at %s\n",
1098*6115d2f3SKarsten Keil 	       (char *) ent->driver_data, pci_name(pdev));
1099*6115d2f3SKarsten Keil 
1100*6115d2f3SKarsten Keil 	card->addr = pci_resource_start(pdev, 1);
1101*6115d2f3SKarsten Keil 	card->irq = pdev->irq;
1102*6115d2f3SKarsten Keil 	pci_set_drvdata(pdev, card);
1103*6115d2f3SKarsten Keil 	err = setup_instance(card);
1104*6115d2f3SKarsten Keil 	if (err)
1105*6115d2f3SKarsten Keil 		pci_set_drvdata(pdev, NULL);
1106*6115d2f3SKarsten Keil 	return err;
1107*6115d2f3SKarsten Keil }
1108*6115d2f3SKarsten Keil 
1109*6115d2f3SKarsten Keil static void __devexit
1110*6115d2f3SKarsten Keil fritz_remove_pci(struct pci_dev *pdev)
1111*6115d2f3SKarsten Keil {
1112*6115d2f3SKarsten Keil 	struct fritzcard *card = pci_get_drvdata(pdev);
1113*6115d2f3SKarsten Keil 
1114*6115d2f3SKarsten Keil 	if (card)
1115*6115d2f3SKarsten Keil 		release_card(card);
1116*6115d2f3SKarsten Keil 	else
1117*6115d2f3SKarsten Keil 		if (debug)
1118*6115d2f3SKarsten Keil 			pr_info("%s: drvdata allready removed\n", __func__);
1119*6115d2f3SKarsten Keil }
1120*6115d2f3SKarsten Keil 
1121*6115d2f3SKarsten Keil static struct pci_device_id fcpci_ids[] __devinitdata = {
1122*6115d2f3SKarsten Keil 	{ PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1, PCI_ANY_ID, PCI_ANY_ID,
1123*6115d2f3SKarsten Keil 	  0, 0, (unsigned long) "Fritz!Card PCI"},
1124*6115d2f3SKarsten Keil 	{ PCI_VENDOR_ID_AVM, PCI_DEVICE_ID_AVM_A1_V2, PCI_ANY_ID, PCI_ANY_ID,
1125*6115d2f3SKarsten Keil 	  0, 0, (unsigned long) "Fritz!Card PCI v2" },
1126*6115d2f3SKarsten Keil 	{ }
1127*6115d2f3SKarsten Keil };
1128*6115d2f3SKarsten Keil MODULE_DEVICE_TABLE(pci, fcpci_ids);
1129*6115d2f3SKarsten Keil 
1130*6115d2f3SKarsten Keil static struct pci_driver fcpci_driver = {
1131*6115d2f3SKarsten Keil 	.name = "fcpci",
1132*6115d2f3SKarsten Keil 	.probe = fritzpci_probe,
1133*6115d2f3SKarsten Keil 	.remove = __devexit_p(fritz_remove_pci),
1134*6115d2f3SKarsten Keil 	.id_table = fcpci_ids,
1135*6115d2f3SKarsten Keil };
1136*6115d2f3SKarsten Keil 
1137*6115d2f3SKarsten Keil static int __init AVM_init(void)
1138*6115d2f3SKarsten Keil {
1139*6115d2f3SKarsten Keil 	int err;
1140*6115d2f3SKarsten Keil 
1141*6115d2f3SKarsten Keil 	pr_notice("AVM Fritz PCI driver Rev. %s\n", AVMFRITZ_REV);
1142*6115d2f3SKarsten Keil 	err = pci_register_driver(&fcpci_driver);
1143*6115d2f3SKarsten Keil 	return err;
1144*6115d2f3SKarsten Keil }
1145*6115d2f3SKarsten Keil 
1146*6115d2f3SKarsten Keil static void __exit AVM_cleanup(void)
1147*6115d2f3SKarsten Keil {
1148*6115d2f3SKarsten Keil 	pci_unregister_driver(&fcpci_driver);
1149*6115d2f3SKarsten Keil }
1150*6115d2f3SKarsten Keil 
1151*6115d2f3SKarsten Keil module_init(AVM_init);
1152*6115d2f3SKarsten Keil module_exit(AVM_cleanup);
1153