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