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