diff options
Diffstat (limited to 'drivers/isdn/hisax/avm_pci.c')
-rw-r--r-- | drivers/isdn/hisax/avm_pci.c | 865 |
1 files changed, 865 insertions, 0 deletions
diff --git a/drivers/isdn/hisax/avm_pci.c b/drivers/isdn/hisax/avm_pci.c new file mode 100644 index 000000000000..6fcb2cf7b0b6 --- /dev/null +++ b/drivers/isdn/hisax/avm_pci.c | |||
@@ -0,0 +1,865 @@ | |||
1 | /* $Id: avm_pci.c,v 1.29.2.4 2004/02/11 13:21:32 keil Exp $ | ||
2 | * | ||
3 | * low level stuff for AVM Fritz!PCI and ISA PnP isdn cards | ||
4 | * | ||
5 | * Author Karsten Keil | ||
6 | * Copyright by Karsten Keil <keil@isdn4linux.de> | ||
7 | * | ||
8 | * This software may be used and distributed according to the terms | ||
9 | * of the GNU General Public License, incorporated herein by reference. | ||
10 | * | ||
11 | * Thanks to AVM, Berlin for information | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #include <linux/config.h> | ||
16 | #include <linux/init.h> | ||
17 | #include "hisax.h" | ||
18 | #include "isac.h" | ||
19 | #include "isdnl1.h" | ||
20 | #include <linux/pci.h> | ||
21 | #include <linux/isapnp.h> | ||
22 | #include <linux/interrupt.h> | ||
23 | |||
24 | extern const char *CardType[]; | ||
25 | static const char *avm_pci_rev = "$Revision: 1.29.2.4 $"; | ||
26 | |||
27 | #define AVM_FRITZ_PCI 1 | ||
28 | #define AVM_FRITZ_PNP 2 | ||
29 | |||
30 | #define HDLC_FIFO 0x0 | ||
31 | #define HDLC_STATUS 0x4 | ||
32 | |||
33 | #define AVM_HDLC_1 0x00 | ||
34 | #define AVM_HDLC_2 0x01 | ||
35 | #define AVM_ISAC_FIFO 0x02 | ||
36 | #define AVM_ISAC_REG_LOW 0x04 | ||
37 | #define AVM_ISAC_REG_HIGH 0x06 | ||
38 | |||
39 | #define AVM_STATUS0_IRQ_ISAC 0x01 | ||
40 | #define AVM_STATUS0_IRQ_HDLC 0x02 | ||
41 | #define AVM_STATUS0_IRQ_TIMER 0x04 | ||
42 | #define AVM_STATUS0_IRQ_MASK 0x07 | ||
43 | |||
44 | #define AVM_STATUS0_RESET 0x01 | ||
45 | #define AVM_STATUS0_DIS_TIMER 0x02 | ||
46 | #define AVM_STATUS0_RES_TIMER 0x04 | ||
47 | #define AVM_STATUS0_ENA_IRQ 0x08 | ||
48 | #define AVM_STATUS0_TESTBIT 0x10 | ||
49 | |||
50 | #define AVM_STATUS1_INT_SEL 0x0f | ||
51 | #define AVM_STATUS1_ENA_IOM 0x80 | ||
52 | |||
53 | #define HDLC_MODE_ITF_FLG 0x01 | ||
54 | #define HDLC_MODE_TRANS 0x02 | ||
55 | #define HDLC_MODE_CCR_7 0x04 | ||
56 | #define HDLC_MODE_CCR_16 0x08 | ||
57 | #define HDLC_MODE_TESTLOOP 0x80 | ||
58 | |||
59 | #define HDLC_INT_XPR 0x80 | ||
60 | #define HDLC_INT_XDU 0x40 | ||
61 | #define HDLC_INT_RPR 0x20 | ||
62 | #define HDLC_INT_MASK 0xE0 | ||
63 | |||
64 | #define HDLC_STAT_RME 0x01 | ||
65 | #define HDLC_STAT_RDO 0x10 | ||
66 | #define HDLC_STAT_CRCVFRRAB 0x0E | ||
67 | #define HDLC_STAT_CRCVFR 0x06 | ||
68 | #define HDLC_STAT_RML_MASK 0x3f00 | ||
69 | |||
70 | #define HDLC_CMD_XRS 0x80 | ||
71 | #define HDLC_CMD_XME 0x01 | ||
72 | #define HDLC_CMD_RRS 0x20 | ||
73 | #define HDLC_CMD_XML_MASK 0x3f00 | ||
74 | |||
75 | |||
76 | /* Interface functions */ | ||
77 | |||
78 | static u_char | ||
79 | ReadISAC(struct IsdnCardState *cs, u_char offset) | ||
80 | { | ||
81 | register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW; | ||
82 | register u_char val; | ||
83 | |||
84 | outb(idx, cs->hw.avm.cfg_reg + 4); | ||
85 | val = inb(cs->hw.avm.isac + (offset & 0xf)); | ||
86 | return (val); | ||
87 | } | ||
88 | |||
89 | static void | ||
90 | WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) | ||
91 | { | ||
92 | register u_char idx = (offset > 0x2f) ? AVM_ISAC_REG_HIGH : AVM_ISAC_REG_LOW; | ||
93 | |||
94 | outb(idx, cs->hw.avm.cfg_reg + 4); | ||
95 | outb(value, cs->hw.avm.isac + (offset & 0xf)); | ||
96 | } | ||
97 | |||
98 | static void | ||
99 | ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) | ||
100 | { | ||
101 | outb(AVM_ISAC_FIFO, cs->hw.avm.cfg_reg + 4); | ||
102 | insb(cs->hw.avm.isac, data, size); | ||
103 | } | ||
104 | |||
105 | static void | ||
106 | WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) | ||
107 | { | ||
108 | outb(AVM_ISAC_FIFO, cs->hw.avm.cfg_reg + 4); | ||
109 | outsb(cs->hw.avm.isac, data, size); | ||
110 | } | ||
111 | |||
112 | static inline u_int | ||
113 | ReadHDLCPCI(struct IsdnCardState *cs, int chan, u_char offset) | ||
114 | { | ||
115 | register u_int idx = chan ? AVM_HDLC_2 : AVM_HDLC_1; | ||
116 | register u_int val; | ||
117 | |||
118 | outl(idx, cs->hw.avm.cfg_reg + 4); | ||
119 | val = inl(cs->hw.avm.isac + offset); | ||
120 | return (val); | ||
121 | } | ||
122 | |||
123 | static inline void | ||
124 | WriteHDLCPCI(struct IsdnCardState *cs, int chan, u_char offset, u_int value) | ||
125 | { | ||
126 | register u_int idx = chan ? AVM_HDLC_2 : AVM_HDLC_1; | ||
127 | |||
128 | outl(idx, cs->hw.avm.cfg_reg + 4); | ||
129 | outl(value, cs->hw.avm.isac + offset); | ||
130 | } | ||
131 | |||
132 | static inline u_char | ||
133 | ReadHDLCPnP(struct IsdnCardState *cs, int chan, u_char offset) | ||
134 | { | ||
135 | register u_char idx = chan ? AVM_HDLC_2 : AVM_HDLC_1; | ||
136 | register u_char val; | ||
137 | |||
138 | outb(idx, cs->hw.avm.cfg_reg + 4); | ||
139 | val = inb(cs->hw.avm.isac + offset); | ||
140 | return (val); | ||
141 | } | ||
142 | |||
143 | static inline void | ||
144 | WriteHDLCPnP(struct IsdnCardState *cs, int chan, u_char offset, u_char value) | ||
145 | { | ||
146 | register u_char idx = chan ? AVM_HDLC_2 : AVM_HDLC_1; | ||
147 | |||
148 | outb(idx, cs->hw.avm.cfg_reg + 4); | ||
149 | outb(value, cs->hw.avm.isac + offset); | ||
150 | } | ||
151 | |||
152 | static u_char | ||
153 | ReadHDLC_s(struct IsdnCardState *cs, int chan, u_char offset) | ||
154 | { | ||
155 | return(0xff & ReadHDLCPCI(cs, chan, offset)); | ||
156 | } | ||
157 | |||
158 | static void | ||
159 | WriteHDLC_s(struct IsdnCardState *cs, int chan, u_char offset, u_char value) | ||
160 | { | ||
161 | WriteHDLCPCI(cs, chan, offset, value); | ||
162 | } | ||
163 | |||
164 | static inline | ||
165 | struct BCState *Sel_BCS(struct IsdnCardState *cs, int channel) | ||
166 | { | ||
167 | if (cs->bcs[0].mode && (cs->bcs[0].channel == channel)) | ||
168 | return(&cs->bcs[0]); | ||
169 | else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel)) | ||
170 | return(&cs->bcs[1]); | ||
171 | else | ||
172 | return(NULL); | ||
173 | } | ||
174 | |||
175 | void | ||
176 | write_ctrl(struct BCState *bcs, int which) { | ||
177 | |||
178 | if (bcs->cs->debug & L1_DEB_HSCX) | ||
179 | debugl1(bcs->cs, "hdlc %c wr%x ctrl %x", | ||
180 | 'A' + bcs->channel, which, bcs->hw.hdlc.ctrl.ctrl); | ||
181 | if (bcs->cs->subtyp == AVM_FRITZ_PCI) { | ||
182 | WriteHDLCPCI(bcs->cs, bcs->channel, HDLC_STATUS, bcs->hw.hdlc.ctrl.ctrl); | ||
183 | } else { | ||
184 | if (which & 4) | ||
185 | WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS + 2, | ||
186 | bcs->hw.hdlc.ctrl.sr.mode); | ||
187 | if (which & 2) | ||
188 | WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS + 1, | ||
189 | bcs->hw.hdlc.ctrl.sr.xml); | ||
190 | if (which & 1) | ||
191 | WriteHDLCPnP(bcs->cs, bcs->channel, HDLC_STATUS, | ||
192 | bcs->hw.hdlc.ctrl.sr.cmd); | ||
193 | } | ||
194 | } | ||
195 | |||
196 | void | ||
197 | modehdlc(struct BCState *bcs, int mode, int bc) | ||
198 | { | ||
199 | struct IsdnCardState *cs = bcs->cs; | ||
200 | int hdlc = bcs->channel; | ||
201 | |||
202 | if (cs->debug & L1_DEB_HSCX) | ||
203 | debugl1(cs, "hdlc %c mode %d --> %d ichan %d --> %d", | ||
204 | 'A' + hdlc, bcs->mode, mode, hdlc, bc); | ||
205 | bcs->hw.hdlc.ctrl.ctrl = 0; | ||
206 | switch (mode) { | ||
207 | case (-1): /* used for init */ | ||
208 | bcs->mode = 1; | ||
209 | bcs->channel = bc; | ||
210 | bc = 0; | ||
211 | case (L1_MODE_NULL): | ||
212 | if (bcs->mode == L1_MODE_NULL) | ||
213 | return; | ||
214 | bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; | ||
215 | bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS; | ||
216 | write_ctrl(bcs, 5); | ||
217 | bcs->mode = L1_MODE_NULL; | ||
218 | bcs->channel = bc; | ||
219 | break; | ||
220 | case (L1_MODE_TRANS): | ||
221 | bcs->mode = mode; | ||
222 | bcs->channel = bc; | ||
223 | bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; | ||
224 | bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_TRANS; | ||
225 | write_ctrl(bcs, 5); | ||
226 | bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS; | ||
227 | write_ctrl(bcs, 1); | ||
228 | bcs->hw.hdlc.ctrl.sr.cmd = 0; | ||
229 | schedule_event(bcs, B_XMTBUFREADY); | ||
230 | break; | ||
231 | case (L1_MODE_HDLC): | ||
232 | bcs->mode = mode; | ||
233 | bcs->channel = bc; | ||
234 | bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS | HDLC_CMD_RRS; | ||
235 | bcs->hw.hdlc.ctrl.sr.mode = HDLC_MODE_ITF_FLG; | ||
236 | write_ctrl(bcs, 5); | ||
237 | bcs->hw.hdlc.ctrl.sr.cmd = HDLC_CMD_XRS; | ||
238 | write_ctrl(bcs, 1); | ||
239 | bcs->hw.hdlc.ctrl.sr.cmd = 0; | ||
240 | schedule_event(bcs, B_XMTBUFREADY); | ||
241 | break; | ||
242 | } | ||
243 | } | ||
244 | |||
245 | static inline void | ||
246 | hdlc_empty_fifo(struct BCState *bcs, int count) | ||
247 | { | ||
248 | register u_int *ptr; | ||
249 | u_char *p; | ||
250 | u_char idx = bcs->channel ? AVM_HDLC_2 : AVM_HDLC_1; | ||
251 | int cnt=0; | ||
252 | struct IsdnCardState *cs = bcs->cs; | ||
253 | |||
254 | if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) | ||
255 | debugl1(cs, "hdlc_empty_fifo %d", count); | ||
256 | if (bcs->hw.hdlc.rcvidx + count > HSCX_BUFMAX) { | ||
257 | if (cs->debug & L1_DEB_WARN) | ||
258 | debugl1(cs, "hdlc_empty_fifo: incoming packet too large"); | ||
259 | return; | ||
260 | } | ||
261 | p = bcs->hw.hdlc.rcvbuf + bcs->hw.hdlc.rcvidx; | ||
262 | ptr = (u_int *)p; | ||
263 | bcs->hw.hdlc.rcvidx += count; | ||
264 | if (cs->subtyp == AVM_FRITZ_PCI) { | ||
265 | outl(idx, cs->hw.avm.cfg_reg + 4); | ||
266 | while (cnt < count) { | ||
267 | #ifdef __powerpc__ | ||
268 | #ifdef CONFIG_APUS | ||
269 | *ptr++ = in_le32((unsigned *)(cs->hw.avm.isac +_IO_BASE)); | ||
270 | #else | ||
271 | *ptr++ = in_be32((unsigned *)(cs->hw.avm.isac +_IO_BASE)); | ||
272 | #endif /* CONFIG_APUS */ | ||
273 | #else | ||
274 | *ptr++ = inl(cs->hw.avm.isac); | ||
275 | #endif /* __powerpc__ */ | ||
276 | cnt += 4; | ||
277 | } | ||
278 | } else { | ||
279 | outb(idx, cs->hw.avm.cfg_reg + 4); | ||
280 | while (cnt < count) { | ||
281 | *p++ = inb(cs->hw.avm.isac); | ||
282 | cnt++; | ||
283 | } | ||
284 | } | ||
285 | if (cs->debug & L1_DEB_HSCX_FIFO) { | ||
286 | char *t = bcs->blog; | ||
287 | |||
288 | if (cs->subtyp == AVM_FRITZ_PNP) | ||
289 | p = (u_char *) ptr; | ||
290 | t += sprintf(t, "hdlc_empty_fifo %c cnt %d", | ||
291 | bcs->channel ? 'B' : 'A', count); | ||
292 | QuickHex(t, p, count); | ||
293 | debugl1(cs, bcs->blog); | ||
294 | } | ||
295 | } | ||
296 | |||
297 | static inline void | ||
298 | hdlc_fill_fifo(struct BCState *bcs) | ||
299 | { | ||
300 | struct IsdnCardState *cs = bcs->cs; | ||
301 | int count, cnt =0; | ||
302 | int fifo_size = 32; | ||
303 | u_char *p; | ||
304 | u_int *ptr; | ||
305 | |||
306 | if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) | ||
307 | debugl1(cs, "hdlc_fill_fifo"); | ||
308 | if (!bcs->tx_skb) | ||
309 | return; | ||
310 | if (bcs->tx_skb->len <= 0) | ||
311 | return; | ||
312 | |||
313 | bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_XME; | ||
314 | if (bcs->tx_skb->len > fifo_size) { | ||
315 | count = fifo_size; | ||
316 | } else { | ||
317 | count = bcs->tx_skb->len; | ||
318 | if (bcs->mode != L1_MODE_TRANS) | ||
319 | bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_XME; | ||
320 | } | ||
321 | if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) | ||
322 | debugl1(cs, "hdlc_fill_fifo %d/%ld", count, bcs->tx_skb->len); | ||
323 | p = bcs->tx_skb->data; | ||
324 | ptr = (u_int *)p; | ||
325 | skb_pull(bcs->tx_skb, count); | ||
326 | bcs->tx_cnt -= count; | ||
327 | bcs->hw.hdlc.count += count; | ||
328 | bcs->hw.hdlc.ctrl.sr.xml = ((count == fifo_size) ? 0 : count); | ||
329 | write_ctrl(bcs, 3); /* sets the correct index too */ | ||
330 | if (cs->subtyp == AVM_FRITZ_PCI) { | ||
331 | while (cnt<count) { | ||
332 | #ifdef __powerpc__ | ||
333 | #ifdef CONFIG_APUS | ||
334 | out_le32((unsigned *)(cs->hw.avm.isac +_IO_BASE), *ptr++); | ||
335 | #else | ||
336 | out_be32((unsigned *)(cs->hw.avm.isac +_IO_BASE), *ptr++); | ||
337 | #endif /* CONFIG_APUS */ | ||
338 | #else | ||
339 | outl(*ptr++, cs->hw.avm.isac); | ||
340 | #endif /* __powerpc__ */ | ||
341 | cnt += 4; | ||
342 | } | ||
343 | } else { | ||
344 | while (cnt<count) { | ||
345 | outb(*p++, cs->hw.avm.isac); | ||
346 | cnt++; | ||
347 | } | ||
348 | } | ||
349 | if (cs->debug & L1_DEB_HSCX_FIFO) { | ||
350 | char *t = bcs->blog; | ||
351 | |||
352 | if (cs->subtyp == AVM_FRITZ_PNP) | ||
353 | p = (u_char *) ptr; | ||
354 | t += sprintf(t, "hdlc_fill_fifo %c cnt %d", | ||
355 | bcs->channel ? 'B' : 'A', count); | ||
356 | QuickHex(t, p, count); | ||
357 | debugl1(cs, bcs->blog); | ||
358 | } | ||
359 | } | ||
360 | |||
361 | static inline void | ||
362 | HDLC_irq(struct BCState *bcs, u_int stat) { | ||
363 | int len; | ||
364 | struct sk_buff *skb; | ||
365 | |||
366 | if (bcs->cs->debug & L1_DEB_HSCX) | ||
367 | debugl1(bcs->cs, "ch%d stat %#x", bcs->channel, stat); | ||
368 | if (stat & HDLC_INT_RPR) { | ||
369 | if (stat & HDLC_STAT_RDO) { | ||
370 | if (bcs->cs->debug & L1_DEB_HSCX) | ||
371 | debugl1(bcs->cs, "RDO"); | ||
372 | else | ||
373 | debugl1(bcs->cs, "ch%d stat %#x", bcs->channel, stat); | ||
374 | bcs->hw.hdlc.ctrl.sr.xml = 0; | ||
375 | bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_RRS; | ||
376 | write_ctrl(bcs, 1); | ||
377 | bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_RRS; | ||
378 | write_ctrl(bcs, 1); | ||
379 | bcs->hw.hdlc.rcvidx = 0; | ||
380 | } else { | ||
381 | if (!(len = (stat & HDLC_STAT_RML_MASK)>>8)) | ||
382 | len = 32; | ||
383 | hdlc_empty_fifo(bcs, len); | ||
384 | if ((stat & HDLC_STAT_RME) || (bcs->mode == L1_MODE_TRANS)) { | ||
385 | if (((stat & HDLC_STAT_CRCVFRRAB)==HDLC_STAT_CRCVFR) || | ||
386 | (bcs->mode == L1_MODE_TRANS)) { | ||
387 | if (!(skb = dev_alloc_skb(bcs->hw.hdlc.rcvidx))) | ||
388 | printk(KERN_WARNING "HDLC: receive out of memory\n"); | ||
389 | else { | ||
390 | memcpy(skb_put(skb, bcs->hw.hdlc.rcvidx), | ||
391 | bcs->hw.hdlc.rcvbuf, bcs->hw.hdlc.rcvidx); | ||
392 | skb_queue_tail(&bcs->rqueue, skb); | ||
393 | } | ||
394 | bcs->hw.hdlc.rcvidx = 0; | ||
395 | schedule_event(bcs, B_RCVBUFREADY); | ||
396 | } else { | ||
397 | if (bcs->cs->debug & L1_DEB_HSCX) | ||
398 | debugl1(bcs->cs, "invalid frame"); | ||
399 | else | ||
400 | debugl1(bcs->cs, "ch%d invalid frame %#x", bcs->channel, stat); | ||
401 | bcs->hw.hdlc.rcvidx = 0; | ||
402 | } | ||
403 | } | ||
404 | } | ||
405 | } | ||
406 | if (stat & HDLC_INT_XDU) { | ||
407 | /* Here we lost an TX interrupt, so | ||
408 | * restart transmitting the whole frame. | ||
409 | */ | ||
410 | if (bcs->tx_skb) { | ||
411 | skb_push(bcs->tx_skb, bcs->hw.hdlc.count); | ||
412 | bcs->tx_cnt += bcs->hw.hdlc.count; | ||
413 | bcs->hw.hdlc.count = 0; | ||
414 | if (bcs->cs->debug & L1_DEB_WARN) | ||
415 | debugl1(bcs->cs, "ch%d XDU", bcs->channel); | ||
416 | } else if (bcs->cs->debug & L1_DEB_WARN) | ||
417 | debugl1(bcs->cs, "ch%d XDU without skb", bcs->channel); | ||
418 | bcs->hw.hdlc.ctrl.sr.xml = 0; | ||
419 | bcs->hw.hdlc.ctrl.sr.cmd |= HDLC_CMD_XRS; | ||
420 | write_ctrl(bcs, 1); | ||
421 | bcs->hw.hdlc.ctrl.sr.cmd &= ~HDLC_CMD_XRS; | ||
422 | write_ctrl(bcs, 1); | ||
423 | hdlc_fill_fifo(bcs); | ||
424 | } else if (stat & HDLC_INT_XPR) { | ||
425 | if (bcs->tx_skb) { | ||
426 | if (bcs->tx_skb->len) { | ||
427 | hdlc_fill_fifo(bcs); | ||
428 | return; | ||
429 | } else { | ||
430 | if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag) && | ||
431 | (PACKET_NOACK != bcs->tx_skb->pkt_type)) { | ||
432 | u_long flags; | ||
433 | spin_lock_irqsave(&bcs->aclock, flags); | ||
434 | bcs->ackcnt += bcs->hw.hdlc.count; | ||
435 | spin_unlock_irqrestore(&bcs->aclock, flags); | ||
436 | schedule_event(bcs, B_ACKPENDING); | ||
437 | } | ||
438 | dev_kfree_skb_irq(bcs->tx_skb); | ||
439 | bcs->hw.hdlc.count = 0; | ||
440 | bcs->tx_skb = NULL; | ||
441 | } | ||
442 | } | ||
443 | if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) { | ||
444 | bcs->hw.hdlc.count = 0; | ||
445 | test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); | ||
446 | hdlc_fill_fifo(bcs); | ||
447 | } else { | ||
448 | test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); | ||
449 | schedule_event(bcs, B_XMTBUFREADY); | ||
450 | } | ||
451 | } | ||
452 | } | ||
453 | |||
454 | inline void | ||
455 | HDLC_irq_main(struct IsdnCardState *cs) | ||
456 | { | ||
457 | u_int stat; | ||
458 | struct BCState *bcs; | ||
459 | |||
460 | if (cs->subtyp == AVM_FRITZ_PCI) { | ||
461 | stat = ReadHDLCPCI(cs, 0, HDLC_STATUS); | ||
462 | } else { | ||
463 | stat = ReadHDLCPnP(cs, 0, HDLC_STATUS); | ||
464 | if (stat & HDLC_INT_RPR) | ||
465 | stat |= (ReadHDLCPnP(cs, 0, HDLC_STATUS+1))<<8; | ||
466 | } | ||
467 | if (stat & HDLC_INT_MASK) { | ||
468 | if (!(bcs = Sel_BCS(cs, 0))) { | ||
469 | if (cs->debug) | ||
470 | debugl1(cs, "hdlc spurious channel 0 IRQ"); | ||
471 | } else | ||
472 | HDLC_irq(bcs, stat); | ||
473 | } | ||
474 | if (cs->subtyp == AVM_FRITZ_PCI) { | ||
475 | stat = ReadHDLCPCI(cs, 1, HDLC_STATUS); | ||
476 | } else { | ||
477 | stat = ReadHDLCPnP(cs, 1, HDLC_STATUS); | ||
478 | if (stat & HDLC_INT_RPR) | ||
479 | stat |= (ReadHDLCPnP(cs, 1, HDLC_STATUS+1))<<8; | ||
480 | } | ||
481 | if (stat & HDLC_INT_MASK) { | ||
482 | if (!(bcs = Sel_BCS(cs, 1))) { | ||
483 | if (cs->debug) | ||
484 | debugl1(cs, "hdlc spurious channel 1 IRQ"); | ||
485 | } else | ||
486 | HDLC_irq(bcs, stat); | ||
487 | } | ||
488 | } | ||
489 | |||
490 | void | ||
491 | hdlc_l2l1(struct PStack *st, int pr, void *arg) | ||
492 | { | ||
493 | struct BCState *bcs = st->l1.bcs; | ||
494 | struct sk_buff *skb = arg; | ||
495 | u_long flags; | ||
496 | |||
497 | switch (pr) { | ||
498 | case (PH_DATA | REQUEST): | ||
499 | spin_lock_irqsave(&bcs->cs->lock, flags); | ||
500 | if (bcs->tx_skb) { | ||
501 | skb_queue_tail(&bcs->squeue, skb); | ||
502 | } else { | ||
503 | bcs->tx_skb = skb; | ||
504 | test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); | ||
505 | bcs->hw.hdlc.count = 0; | ||
506 | bcs->cs->BC_Send_Data(bcs); | ||
507 | } | ||
508 | spin_unlock_irqrestore(&bcs->cs->lock, flags); | ||
509 | break; | ||
510 | case (PH_PULL | INDICATION): | ||
511 | spin_lock_irqsave(&bcs->cs->lock, flags); | ||
512 | if (bcs->tx_skb) { | ||
513 | printk(KERN_WARNING "hdlc_l2l1: this shouldn't happen\n"); | ||
514 | } else { | ||
515 | test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); | ||
516 | bcs->tx_skb = skb; | ||
517 | bcs->hw.hdlc.count = 0; | ||
518 | bcs->cs->BC_Send_Data(bcs); | ||
519 | } | ||
520 | spin_unlock_irqrestore(&bcs->cs->lock, flags); | ||
521 | break; | ||
522 | case (PH_PULL | REQUEST): | ||
523 | if (!bcs->tx_skb) { | ||
524 | test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); | ||
525 | st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); | ||
526 | } else | ||
527 | test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); | ||
528 | break; | ||
529 | case (PH_ACTIVATE | REQUEST): | ||
530 | spin_lock_irqsave(&bcs->cs->lock, flags); | ||
531 | test_and_set_bit(BC_FLG_ACTIV, &bcs->Flag); | ||
532 | modehdlc(bcs, st->l1.mode, st->l1.bc); | ||
533 | spin_unlock_irqrestore(&bcs->cs->lock, flags); | ||
534 | l1_msg_b(st, pr, arg); | ||
535 | break; | ||
536 | case (PH_DEACTIVATE | REQUEST): | ||
537 | l1_msg_b(st, pr, arg); | ||
538 | break; | ||
539 | case (PH_DEACTIVATE | CONFIRM): | ||
540 | spin_lock_irqsave(&bcs->cs->lock, flags); | ||
541 | test_and_clear_bit(BC_FLG_ACTIV, &bcs->Flag); | ||
542 | test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); | ||
543 | modehdlc(bcs, 0, st->l1.bc); | ||
544 | spin_unlock_irqrestore(&bcs->cs->lock, flags); | ||
545 | st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); | ||
546 | break; | ||
547 | } | ||
548 | } | ||
549 | |||
550 | void | ||
551 | close_hdlcstate(struct BCState *bcs) | ||
552 | { | ||
553 | modehdlc(bcs, 0, 0); | ||
554 | if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) { | ||
555 | if (bcs->hw.hdlc.rcvbuf) { | ||
556 | kfree(bcs->hw.hdlc.rcvbuf); | ||
557 | bcs->hw.hdlc.rcvbuf = NULL; | ||
558 | } | ||
559 | if (bcs->blog) { | ||
560 | kfree(bcs->blog); | ||
561 | bcs->blog = NULL; | ||
562 | } | ||
563 | skb_queue_purge(&bcs->rqueue); | ||
564 | skb_queue_purge(&bcs->squeue); | ||
565 | if (bcs->tx_skb) { | ||
566 | dev_kfree_skb_any(bcs->tx_skb); | ||
567 | bcs->tx_skb = NULL; | ||
568 | test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); | ||
569 | } | ||
570 | } | ||
571 | } | ||
572 | |||
573 | int | ||
574 | open_hdlcstate(struct IsdnCardState *cs, struct BCState *bcs) | ||
575 | { | ||
576 | if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { | ||
577 | if (!(bcs->hw.hdlc.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) { | ||
578 | printk(KERN_WARNING | ||
579 | "HiSax: No memory for hdlc.rcvbuf\n"); | ||
580 | return (1); | ||
581 | } | ||
582 | if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) { | ||
583 | printk(KERN_WARNING | ||
584 | "HiSax: No memory for bcs->blog\n"); | ||
585 | test_and_clear_bit(BC_FLG_INIT, &bcs->Flag); | ||
586 | kfree(bcs->hw.hdlc.rcvbuf); | ||
587 | bcs->hw.hdlc.rcvbuf = NULL; | ||
588 | return (2); | ||
589 | } | ||
590 | skb_queue_head_init(&bcs->rqueue); | ||
591 | skb_queue_head_init(&bcs->squeue); | ||
592 | } | ||
593 | bcs->tx_skb = NULL; | ||
594 | test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); | ||
595 | bcs->event = 0; | ||
596 | bcs->hw.hdlc.rcvidx = 0; | ||
597 | bcs->tx_cnt = 0; | ||
598 | return (0); | ||
599 | } | ||
600 | |||
601 | int | ||
602 | setstack_hdlc(struct PStack *st, struct BCState *bcs) | ||
603 | { | ||
604 | bcs->channel = st->l1.bc; | ||
605 | if (open_hdlcstate(st->l1.hardware, bcs)) | ||
606 | return (-1); | ||
607 | st->l1.bcs = bcs; | ||
608 | st->l2.l2l1 = hdlc_l2l1; | ||
609 | setstack_manager(st); | ||
610 | bcs->st = st; | ||
611 | setstack_l1_B(st); | ||
612 | return (0); | ||
613 | } | ||
614 | |||
615 | void __init | ||
616 | clear_pending_hdlc_ints(struct IsdnCardState *cs) | ||
617 | { | ||
618 | u_int val; | ||
619 | |||
620 | if (cs->subtyp == AVM_FRITZ_PCI) { | ||
621 | val = ReadHDLCPCI(cs, 0, HDLC_STATUS); | ||
622 | debugl1(cs, "HDLC 1 STA %x", val); | ||
623 | val = ReadHDLCPCI(cs, 1, HDLC_STATUS); | ||
624 | debugl1(cs, "HDLC 2 STA %x", val); | ||
625 | } else { | ||
626 | val = ReadHDLCPnP(cs, 0, HDLC_STATUS); | ||
627 | debugl1(cs, "HDLC 1 STA %x", val); | ||
628 | val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 1); | ||
629 | debugl1(cs, "HDLC 1 RML %x", val); | ||
630 | val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 2); | ||
631 | debugl1(cs, "HDLC 1 MODE %x", val); | ||
632 | val = ReadHDLCPnP(cs, 0, HDLC_STATUS + 3); | ||
633 | debugl1(cs, "HDLC 1 VIN %x", val); | ||
634 | val = ReadHDLCPnP(cs, 1, HDLC_STATUS); | ||
635 | debugl1(cs, "HDLC 2 STA %x", val); | ||
636 | val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 1); | ||
637 | debugl1(cs, "HDLC 2 RML %x", val); | ||
638 | val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 2); | ||
639 | debugl1(cs, "HDLC 2 MODE %x", val); | ||
640 | val = ReadHDLCPnP(cs, 1, HDLC_STATUS + 3); | ||
641 | debugl1(cs, "HDLC 2 VIN %x", val); | ||
642 | } | ||
643 | } | ||
644 | |||
645 | void __init | ||
646 | inithdlc(struct IsdnCardState *cs) | ||
647 | { | ||
648 | cs->bcs[0].BC_SetStack = setstack_hdlc; | ||
649 | cs->bcs[1].BC_SetStack = setstack_hdlc; | ||
650 | cs->bcs[0].BC_Close = close_hdlcstate; | ||
651 | cs->bcs[1].BC_Close = close_hdlcstate; | ||
652 | modehdlc(cs->bcs, -1, 0); | ||
653 | modehdlc(cs->bcs + 1, -1, 1); | ||
654 | } | ||
655 | |||
656 | static irqreturn_t | ||
657 | avm_pcipnp_interrupt(int intno, void *dev_id, struct pt_regs *regs) | ||
658 | { | ||
659 | struct IsdnCardState *cs = dev_id; | ||
660 | u_long flags; | ||
661 | u_char val; | ||
662 | u_char sval; | ||
663 | |||
664 | spin_lock_irqsave(&cs->lock, flags); | ||
665 | sval = inb(cs->hw.avm.cfg_reg + 2); | ||
666 | if ((sval & AVM_STATUS0_IRQ_MASK) == AVM_STATUS0_IRQ_MASK) { | ||
667 | /* possible a shared IRQ reqest */ | ||
668 | spin_unlock_irqrestore(&cs->lock, flags); | ||
669 | return IRQ_NONE; | ||
670 | } | ||
671 | if (!(sval & AVM_STATUS0_IRQ_ISAC)) { | ||
672 | val = ReadISAC(cs, ISAC_ISTA); | ||
673 | isac_interrupt(cs, val); | ||
674 | } | ||
675 | if (!(sval & AVM_STATUS0_IRQ_HDLC)) { | ||
676 | HDLC_irq_main(cs); | ||
677 | } | ||
678 | WriteISAC(cs, ISAC_MASK, 0xFF); | ||
679 | WriteISAC(cs, ISAC_MASK, 0x0); | ||
680 | spin_unlock_irqrestore(&cs->lock, flags); | ||
681 | return IRQ_HANDLED; | ||
682 | } | ||
683 | |||
684 | static void | ||
685 | reset_avmpcipnp(struct IsdnCardState *cs) | ||
686 | { | ||
687 | printk(KERN_INFO "AVM PCI/PnP: reset\n"); | ||
688 | outb(AVM_STATUS0_RESET | AVM_STATUS0_DIS_TIMER, cs->hw.avm.cfg_reg + 2); | ||
689 | mdelay(10); | ||
690 | outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER | AVM_STATUS0_ENA_IRQ, cs->hw.avm.cfg_reg + 2); | ||
691 | outb(AVM_STATUS1_ENA_IOM | cs->irq, cs->hw.avm.cfg_reg + 3); | ||
692 | mdelay(10); | ||
693 | printk(KERN_INFO "AVM PCI/PnP: S1 %x\n", inb(cs->hw.avm.cfg_reg + 3)); | ||
694 | } | ||
695 | |||
696 | static int | ||
697 | AVM_card_msg(struct IsdnCardState *cs, int mt, void *arg) | ||
698 | { | ||
699 | u_long flags; | ||
700 | |||
701 | switch (mt) { | ||
702 | case CARD_RESET: | ||
703 | spin_lock_irqsave(&cs->lock, flags); | ||
704 | reset_avmpcipnp(cs); | ||
705 | spin_unlock_irqrestore(&cs->lock, flags); | ||
706 | return(0); | ||
707 | case CARD_RELEASE: | ||
708 | outb(0, cs->hw.avm.cfg_reg + 2); | ||
709 | release_region(cs->hw.avm.cfg_reg, 32); | ||
710 | return(0); | ||
711 | case CARD_INIT: | ||
712 | spin_lock_irqsave(&cs->lock, flags); | ||
713 | reset_avmpcipnp(cs); | ||
714 | clear_pending_isac_ints(cs); | ||
715 | initisac(cs); | ||
716 | inithdlc(cs); | ||
717 | outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER, | ||
718 | cs->hw.avm.cfg_reg + 2); | ||
719 | WriteISAC(cs, ISAC_MASK, 0); | ||
720 | outb(AVM_STATUS0_DIS_TIMER | AVM_STATUS0_RES_TIMER | | ||
721 | AVM_STATUS0_ENA_IRQ, cs->hw.avm.cfg_reg + 2); | ||
722 | /* RESET Receiver and Transmitter */ | ||
723 | WriteISAC(cs, ISAC_CMDR, 0x41); | ||
724 | spin_unlock_irqrestore(&cs->lock, flags); | ||
725 | return(0); | ||
726 | case CARD_TEST: | ||
727 | return(0); | ||
728 | } | ||
729 | return(0); | ||
730 | } | ||
731 | |||
732 | #ifdef CONFIG_PCI | ||
733 | static struct pci_dev *dev_avm __initdata = NULL; | ||
734 | #endif | ||
735 | #ifdef __ISAPNP__ | ||
736 | static struct pnp_card *pnp_avm_c __initdata = NULL; | ||
737 | #endif | ||
738 | |||
739 | int __init | ||
740 | setup_avm_pcipnp(struct IsdnCard *card) | ||
741 | { | ||
742 | u_int val, ver; | ||
743 | struct IsdnCardState *cs = card->cs; | ||
744 | char tmp[64]; | ||
745 | |||
746 | strcpy(tmp, avm_pci_rev); | ||
747 | printk(KERN_INFO "HiSax: AVM PCI driver Rev. %s\n", HiSax_getrev(tmp)); | ||
748 | if (cs->typ != ISDN_CTYPE_FRITZPCI) | ||
749 | return (0); | ||
750 | if (card->para[1]) { | ||
751 | /* old manual method */ | ||
752 | cs->hw.avm.cfg_reg = card->para[1]; | ||
753 | cs->irq = card->para[0]; | ||
754 | cs->subtyp = AVM_FRITZ_PNP; | ||
755 | goto ready; | ||
756 | } | ||
757 | #ifdef __ISAPNP__ | ||
758 | if (isapnp_present()) { | ||
759 | struct pnp_dev *pnp_avm_d = NULL; | ||
760 | if ((pnp_avm_c = pnp_find_card( | ||
761 | ISAPNP_VENDOR('A', 'V', 'M'), | ||
762 | ISAPNP_FUNCTION(0x0900), pnp_avm_c))) { | ||
763 | if ((pnp_avm_d = pnp_find_dev(pnp_avm_c, | ||
764 | ISAPNP_VENDOR('A', 'V', 'M'), | ||
765 | ISAPNP_FUNCTION(0x0900), pnp_avm_d))) { | ||
766 | int err; | ||
767 | |||
768 | pnp_disable_dev(pnp_avm_d); | ||
769 | err = pnp_activate_dev(pnp_avm_d); | ||
770 | if (err<0) { | ||
771 | printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n", | ||
772 | __FUNCTION__, err); | ||
773 | return(0); | ||
774 | } | ||
775 | cs->hw.avm.cfg_reg = | ||
776 | pnp_port_start(pnp_avm_d, 0); | ||
777 | cs->irq = pnp_irq(pnp_avm_d, 0); | ||
778 | if (!cs->irq) { | ||
779 | printk(KERN_ERR "FritzPnP:No IRQ\n"); | ||
780 | return(0); | ||
781 | } | ||
782 | if (!cs->hw.avm.cfg_reg) { | ||
783 | printk(KERN_ERR "FritzPnP:No IO address\n"); | ||
784 | return(0); | ||
785 | } | ||
786 | cs->subtyp = AVM_FRITZ_PNP; | ||
787 | goto ready; | ||
788 | } | ||
789 | } | ||
790 | } else { | ||
791 | printk(KERN_INFO "FritzPnP: no ISA PnP present\n"); | ||
792 | } | ||
793 | #endif | ||
794 | #ifdef CONFIG_PCI | ||
795 | if ((dev_avm = pci_find_device(PCI_VENDOR_ID_AVM, | ||
796 | PCI_DEVICE_ID_AVM_A1, dev_avm))) { | ||
797 | if (pci_enable_device(dev_avm)) | ||
798 | return(0); | ||
799 | cs->irq = dev_avm->irq; | ||
800 | if (!cs->irq) { | ||
801 | printk(KERN_ERR "FritzPCI: No IRQ for PCI card found\n"); | ||
802 | return(0); | ||
803 | } | ||
804 | cs->hw.avm.cfg_reg = pci_resource_start(dev_avm, 1); | ||
805 | if (!cs->hw.avm.cfg_reg) { | ||
806 | printk(KERN_ERR "FritzPCI: No IO-Adr for PCI card found\n"); | ||
807 | return(0); | ||
808 | } | ||
809 | cs->subtyp = AVM_FRITZ_PCI; | ||
810 | } else { | ||
811 | printk(KERN_WARNING "FritzPCI: No PCI card found\n"); | ||
812 | return(0); | ||
813 | } | ||
814 | cs->irq_flags |= SA_SHIRQ; | ||
815 | #else | ||
816 | printk(KERN_WARNING "FritzPCI: NO_PCI_BIOS\n"); | ||
817 | return (0); | ||
818 | #endif /* CONFIG_PCI */ | ||
819 | ready: | ||
820 | cs->hw.avm.isac = cs->hw.avm.cfg_reg + 0x10; | ||
821 | if (!request_region(cs->hw.avm.cfg_reg, 32, | ||
822 | (cs->subtyp == AVM_FRITZ_PCI) ? "avm PCI" : "avm PnP")) { | ||
823 | printk(KERN_WARNING | ||
824 | "HiSax: %s config port %x-%x already in use\n", | ||
825 | CardType[card->typ], | ||
826 | cs->hw.avm.cfg_reg, | ||
827 | cs->hw.avm.cfg_reg + 31); | ||
828 | return (0); | ||
829 | } | ||
830 | switch (cs->subtyp) { | ||
831 | case AVM_FRITZ_PCI: | ||
832 | val = inl(cs->hw.avm.cfg_reg); | ||
833 | printk(KERN_INFO "AVM PCI: stat %#x\n", val); | ||
834 | printk(KERN_INFO "AVM PCI: Class %X Rev %d\n", | ||
835 | val & 0xff, (val>>8) & 0xff); | ||
836 | cs->BC_Read_Reg = &ReadHDLC_s; | ||
837 | cs->BC_Write_Reg = &WriteHDLC_s; | ||
838 | break; | ||
839 | case AVM_FRITZ_PNP: | ||
840 | val = inb(cs->hw.avm.cfg_reg); | ||
841 | ver = inb(cs->hw.avm.cfg_reg + 1); | ||
842 | printk(KERN_INFO "AVM PnP: Class %X Rev %d\n", val, ver); | ||
843 | cs->BC_Read_Reg = &ReadHDLCPnP; | ||
844 | cs->BC_Write_Reg = &WriteHDLCPnP; | ||
845 | break; | ||
846 | default: | ||
847 | printk(KERN_WARNING "AVM unknown subtype %d\n", cs->subtyp); | ||
848 | return(0); | ||
849 | } | ||
850 | printk(KERN_INFO "HiSax: %s config irq:%d base:0x%X\n", | ||
851 | (cs->subtyp == AVM_FRITZ_PCI) ? "AVM Fritz!PCI" : "AVM Fritz!PnP", | ||
852 | cs->irq, cs->hw.avm.cfg_reg); | ||
853 | |||
854 | setup_isac(cs); | ||
855 | cs->readisac = &ReadISAC; | ||
856 | cs->writeisac = &WriteISAC; | ||
857 | cs->readisacfifo = &ReadISACfifo; | ||
858 | cs->writeisacfifo = &WriteISACfifo; | ||
859 | cs->BC_Send_Data = &hdlc_fill_fifo; | ||
860 | cs->cardmsg = &AVM_card_msg; | ||
861 | cs->irq_func = &avm_pcipnp_interrupt; | ||
862 | cs->writeisac(cs, ISAC_MASK, 0xFF); | ||
863 | ISACVersion(cs, (cs->subtyp == AVM_FRITZ_PCI) ? "AVM PCI:" : "AVM PnP:"); | ||
864 | return (1); | ||
865 | } | ||