diff options
Diffstat (limited to 'drivers/isdn/hisax/amd7930_fn.c')
-rw-r--r-- | drivers/isdn/hisax/amd7930_fn.c | 796 |
1 files changed, 796 insertions, 0 deletions
diff --git a/drivers/isdn/hisax/amd7930_fn.c b/drivers/isdn/hisax/amd7930_fn.c new file mode 100644 index 000000000000..c4f861a5db25 --- /dev/null +++ b/drivers/isdn/hisax/amd7930_fn.c | |||
@@ -0,0 +1,796 @@ | |||
1 | /* gerdes_amd7930.c,v 0.99 2001/10/02 | ||
2 | * | ||
3 | * gerdes_amd7930.c Amd 79C30A and 79C32A specific routines | ||
4 | * (based on HiSax driver by Karsten Keil) | ||
5 | * | ||
6 | * Author Christoph Ersfeld <info@formula-n.de> | ||
7 | * Formula-n Europe AG (www.formula-n.com) | ||
8 | * previously Gerdes AG | ||
9 | * | ||
10 | * | ||
11 | * This file is (c) under GNU PUBLIC LICENSE | ||
12 | * | ||
13 | * | ||
14 | * Notes: | ||
15 | * Version 0.99 is the first release of this driver and there are | ||
16 | * certainly a few bugs. | ||
17 | * | ||
18 | * Please don't report any malfunction to me without sending | ||
19 | * (compressed) debug-logs. | ||
20 | * It would be nearly impossible to retrace it. | ||
21 | * | ||
22 | * Log D-channel-processing as follows: | ||
23 | * | ||
24 | * 1. Load hisax with card-specific parameters, this example ist for | ||
25 | * Formula-n enter:now ISDN PCI and compatible | ||
26 | * (f.e. Gerdes Power ISDN PCI) | ||
27 | * | ||
28 | * modprobe hisax type=41 protocol=2 id=gerdes | ||
29 | * | ||
30 | * if you chose an other value for id, you need to modify the | ||
31 | * code below, too. | ||
32 | * | ||
33 | * 2. set debug-level | ||
34 | * | ||
35 | * hisaxctrl gerdes 1 0x3ff | ||
36 | * hisaxctrl gerdes 11 0x4f | ||
37 | * cat /dev/isdnctrl >> ~/log & | ||
38 | * | ||
39 | * Please take also a look into /var/log/messages if there is | ||
40 | * anything importand concerning HISAX. | ||
41 | * | ||
42 | * | ||
43 | * Credits: | ||
44 | * Programming the driver for Formula-n enter:now ISDN PCI and | ||
45 | * necessary this driver for the used Amd 7930 D-channel-controller | ||
46 | * was spnsored by Formula-n Europe AG. | ||
47 | * Thanks to Karsten Keil and Petr Novak, who gave me support in | ||
48 | * Hisax-specific questions. | ||
49 | * I want so say special thanks to Carl-Friedrich Braun, who had to | ||
50 | * answer a lot of questions about generally ISDN and about handling | ||
51 | * of the Amd-Chip. | ||
52 | * | ||
53 | */ | ||
54 | |||
55 | |||
56 | #include "hisax.h" | ||
57 | #include "isdnl1.h" | ||
58 | #include "isac.h" | ||
59 | #include "amd7930_fn.h" | ||
60 | #include <linux/interrupt.h> | ||
61 | #include <linux/init.h> | ||
62 | |||
63 | static void Amd7930_new_ph(struct IsdnCardState *cs); | ||
64 | |||
65 | static WORD initAMD[] = { | ||
66 | 0x0100, | ||
67 | |||
68 | 0x00A5, 3, 0x01, 0x40, 0x58, // LPR, LMR1, LMR2 | ||
69 | 0x0086, 1, 0x0B, // DMR1 (D-Buffer TH-Interrupts on) | ||
70 | 0x0087, 1, 0xFF, // DMR2 | ||
71 | 0x0092, 1, 0x03, // EFCR (extended mode d-channel-fifo on) | ||
72 | 0x0090, 4, 0xFE, 0xFF, 0x02, 0x0F, // FRAR4, SRAR4, DMR3, DMR4 (address recognition ) | ||
73 | 0x0084, 2, 0x80, 0x00, // DRLR | ||
74 | 0x00C0, 1, 0x47, // PPCR1 | ||
75 | 0x00C8, 1, 0x01, // PPCR2 | ||
76 | |||
77 | 0x0102, | ||
78 | 0x0107, | ||
79 | 0x01A1, 1, | ||
80 | 0x0121, 1, | ||
81 | 0x0189, 2, | ||
82 | |||
83 | 0x0045, 4, 0x61, 0x72, 0x00, 0x00, // MCR1, MCR2, MCR3, MCR4 | ||
84 | 0x0063, 2, 0x08, 0x08, // GX | ||
85 | 0x0064, 2, 0x08, 0x08, // GR | ||
86 | 0x0065, 2, 0x99, 0x00, // GER | ||
87 | 0x0066, 2, 0x7C, 0x8B, // STG | ||
88 | 0x0067, 2, 0x00, 0x00, // FTGR1, FTGR2 | ||
89 | 0x0068, 2, 0x20, 0x20, // ATGR1, ATGR2 | ||
90 | 0x0069, 1, 0x4F, // MMR1 | ||
91 | 0x006A, 1, 0x00, // MMR2 | ||
92 | 0x006C, 1, 0x40, // MMR3 | ||
93 | 0x0021, 1, 0x02, // INIT | ||
94 | 0x00A3, 1, 0x40, // LMR1 | ||
95 | |||
96 | 0xFFFF | ||
97 | }; | ||
98 | |||
99 | |||
100 | void /* macro wWordAMD */ | ||
101 | WriteWordAmd7930(struct IsdnCardState *cs, BYTE reg, WORD val) | ||
102 | { | ||
103 | wByteAMD(cs, 0x00, reg); | ||
104 | wByteAMD(cs, 0x01, LOBYTE(val)); | ||
105 | wByteAMD(cs, 0x01, HIBYTE(val)); | ||
106 | } | ||
107 | |||
108 | WORD /* macro rWordAMD */ | ||
109 | ReadWordAmd7930(struct IsdnCardState *cs, BYTE reg) | ||
110 | { | ||
111 | WORD res; | ||
112 | /* direct access register */ | ||
113 | if(reg < 8) { | ||
114 | res = rByteAMD(cs, reg); | ||
115 | res += 256*rByteAMD(cs, reg); | ||
116 | } | ||
117 | /* indirect access register */ | ||
118 | else { | ||
119 | wByteAMD(cs, 0x00, reg); | ||
120 | res = rByteAMD(cs, 0x01); | ||
121 | res += 256*rByteAMD(cs, 0x01); | ||
122 | } | ||
123 | return (res); | ||
124 | } | ||
125 | |||
126 | |||
127 | static void | ||
128 | Amd7930_ph_command(struct IsdnCardState *cs, u_char command, char *s) | ||
129 | { | ||
130 | if (cs->debug & L1_DEB_ISAC) | ||
131 | debugl1(cs, "AMD7930: %s: ph_command 0x%02X", s, command); | ||
132 | |||
133 | cs->dc.amd7930.lmr1 = command; | ||
134 | wByteAMD(cs, 0xA3, command); | ||
135 | } | ||
136 | |||
137 | |||
138 | |||
139 | static BYTE i430States[] = { | ||
140 | // to reset F3 F4 F5 F6 F7 F8 AR from | ||
141 | 0x01, 0x02, 0x00, 0x00, 0x00, 0x07, 0x05, 0x00, // init | ||
142 | 0x01, 0x02, 0x00, 0x00, 0x00, 0x07, 0x05, 0x00, // reset | ||
143 | 0x01, 0x02, 0x00, 0x00, 0x00, 0x09, 0x05, 0x04, // F3 | ||
144 | 0x01, 0x02, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, // F4 | ||
145 | 0x01, 0x02, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, // F5 | ||
146 | 0x01, 0x03, 0x00, 0x00, 0x00, 0x06, 0x05, 0x00, // F6 | ||
147 | 0x11, 0x13, 0x00, 0x00, 0x1B, 0x00, 0x15, 0x00, // F7 | ||
148 | 0x01, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, // F8 | ||
149 | 0x01, 0x03, 0x00, 0x00, 0x00, 0x09, 0x00, 0x0A}; // AR | ||
150 | |||
151 | |||
152 | /* Row init - reset F3 F4 F5 F6 F7 F8 AR */ | ||
153 | static BYTE stateHelper[] = { 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; | ||
154 | |||
155 | |||
156 | |||
157 | |||
158 | static void | ||
159 | Amd7930_get_state(struct IsdnCardState *cs) { | ||
160 | BYTE lsr = rByteAMD(cs, 0xA1); | ||
161 | cs->dc.amd7930.ph_state = (lsr & 0x7) + 2; | ||
162 | Amd7930_new_ph(cs); | ||
163 | } | ||
164 | |||
165 | |||
166 | |||
167 | static void | ||
168 | Amd7930_new_ph(struct IsdnCardState *cs) | ||
169 | { | ||
170 | u_char index = stateHelper[cs->dc.amd7930.old_state]*8 + stateHelper[cs->dc.amd7930.ph_state]-1; | ||
171 | u_char message = i430States[index]; | ||
172 | |||
173 | if (cs->debug & L1_DEB_ISAC) | ||
174 | debugl1(cs, "AMD7930: new_ph %d, old_ph %d, message %d, index %d", | ||
175 | cs->dc.amd7930.ph_state, cs->dc.amd7930.old_state, message & 0x0f, index); | ||
176 | |||
177 | cs->dc.amd7930.old_state = cs->dc.amd7930.ph_state; | ||
178 | |||
179 | /* abort transmit if nessesary */ | ||
180 | if ((message & 0xf0) && (cs->tx_skb)) { | ||
181 | wByteAMD(cs, 0x21, 0xC2); | ||
182 | wByteAMD(cs, 0x21, 0x02); | ||
183 | } | ||
184 | |||
185 | switch (message & 0x0f) { | ||
186 | |||
187 | case (1): | ||
188 | l1_msg(cs, HW_RESET | INDICATION, NULL); | ||
189 | Amd7930_get_state(cs); | ||
190 | break; | ||
191 | case (2): /* init, Card starts in F3 */ | ||
192 | l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL); | ||
193 | break; | ||
194 | case (3): | ||
195 | l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL); | ||
196 | break; | ||
197 | case (4): | ||
198 | l1_msg(cs, HW_POWERUP | CONFIRM, NULL); | ||
199 | Amd7930_ph_command(cs, 0x50, "HW_ENABLE REQUEST"); | ||
200 | break; | ||
201 | case (5): | ||
202 | l1_msg(cs, HW_RSYNC | INDICATION, NULL); | ||
203 | break; | ||
204 | case (6): | ||
205 | l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL); | ||
206 | break; | ||
207 | case (7): /* init, Card starts in F7 */ | ||
208 | l1_msg(cs, HW_RSYNC | INDICATION, NULL); | ||
209 | l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL); | ||
210 | break; | ||
211 | case (8): | ||
212 | l1_msg(cs, HW_POWERUP | CONFIRM, NULL); | ||
213 | /* fall through */ | ||
214 | case (9): | ||
215 | Amd7930_ph_command(cs, 0x40, "HW_ENABLE REQ cleared if set"); | ||
216 | l1_msg(cs, HW_RSYNC | INDICATION, NULL); | ||
217 | l1_msg(cs, HW_INFO2 | INDICATION, NULL); | ||
218 | l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL); | ||
219 | break; | ||
220 | case (10): | ||
221 | Amd7930_ph_command(cs, 0x40, "T3 expired, HW_ENABLE REQ cleared"); | ||
222 | cs->dc.amd7930.old_state = 3; | ||
223 | break; | ||
224 | case (11): | ||
225 | l1_msg(cs, HW_INFO2 | INDICATION, NULL); | ||
226 | break; | ||
227 | default: | ||
228 | break; | ||
229 | } | ||
230 | } | ||
231 | |||
232 | |||
233 | |||
234 | static void | ||
235 | Amd7930_bh(struct IsdnCardState *cs) | ||
236 | { | ||
237 | |||
238 | struct PStack *stptr; | ||
239 | |||
240 | if (!cs) | ||
241 | return; | ||
242 | if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) { | ||
243 | if (cs->debug) | ||
244 | debugl1(cs, "Amd7930: bh, D-Channel Busy cleared"); | ||
245 | stptr = cs->stlist; | ||
246 | while (stptr != NULL) { | ||
247 | stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL); | ||
248 | stptr = stptr->next; | ||
249 | } | ||
250 | } | ||
251 | if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) { | ||
252 | if (cs->debug & L1_DEB_ISAC) | ||
253 | debugl1(cs, "AMD7930: bh, D_L1STATECHANGE"); | ||
254 | Amd7930_new_ph(cs); | ||
255 | } | ||
256 | |||
257 | if (test_and_clear_bit(D_RCVBUFREADY, &cs->event)) { | ||
258 | if (cs->debug & L1_DEB_ISAC) | ||
259 | debugl1(cs, "AMD7930: bh, D_RCVBUFREADY"); | ||
260 | DChannel_proc_rcv(cs); | ||
261 | } | ||
262 | |||
263 | if (test_and_clear_bit(D_XMTBUFREADY, &cs->event)) { | ||
264 | if (cs->debug & L1_DEB_ISAC) | ||
265 | debugl1(cs, "AMD7930: bh, D_XMTBUFREADY"); | ||
266 | DChannel_proc_xmt(cs); | ||
267 | } | ||
268 | } | ||
269 | |||
270 | static void | ||
271 | Amd7930_empty_Dfifo(struct IsdnCardState *cs, int flag) | ||
272 | { | ||
273 | |||
274 | BYTE stat, der; | ||
275 | BYTE *ptr; | ||
276 | struct sk_buff *skb; | ||
277 | |||
278 | |||
279 | if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO)) | ||
280 | debugl1(cs, "Amd7930: empty_Dfifo"); | ||
281 | |||
282 | |||
283 | ptr = cs->rcvbuf + cs->rcvidx; | ||
284 | |||
285 | /* AMD interrupts off */ | ||
286 | AmdIrqOff(cs); | ||
287 | |||
288 | /* read D-Channel-Fifo*/ | ||
289 | stat = rByteAMD(cs, 0x07); // DSR2 | ||
290 | |||
291 | /* while Data in Fifo ... */ | ||
292 | while ( (stat & 2) && ((ptr-cs->rcvbuf) < MAX_DFRAME_LEN_L1) ) { | ||
293 | *ptr = rByteAMD(cs, 0x04); // DCRB | ||
294 | ptr++; | ||
295 | stat = rByteAMD(cs, 0x07); // DSR2 | ||
296 | cs->rcvidx = ptr - cs->rcvbuf; | ||
297 | |||
298 | /* Paket ready? */ | ||
299 | if (stat & 1) { | ||
300 | |||
301 | der = rWordAMD(cs, 0x03); | ||
302 | |||
303 | /* no errors, packet ok */ | ||
304 | if(!der && !flag) { | ||
305 | rWordAMD(cs, 0x89); // clear DRCR | ||
306 | |||
307 | if ((cs->rcvidx) > 0) { | ||
308 | if (!(skb = alloc_skb(cs->rcvidx, GFP_ATOMIC))) | ||
309 | printk(KERN_WARNING "HiSax: Amd7930: empty_Dfifo, D receive out of memory!\n"); | ||
310 | else { | ||
311 | /* Debugging */ | ||
312 | if (cs->debug & L1_DEB_ISAC_FIFO) { | ||
313 | char *t = cs->dlog; | ||
314 | |||
315 | t += sprintf(t, "Amd7930: empty_Dfifo cnt: %d |", cs->rcvidx); | ||
316 | QuickHex(t, cs->rcvbuf, cs->rcvidx); | ||
317 | debugl1(cs, cs->dlog); | ||
318 | } | ||
319 | /* moves received data in sk-buffer */ | ||
320 | memcpy(skb_put(skb, cs->rcvidx), cs->rcvbuf, cs->rcvidx); | ||
321 | skb_queue_tail(&cs->rq, skb); | ||
322 | } | ||
323 | } | ||
324 | |||
325 | } | ||
326 | /* throw damaged packets away, reset receive-buffer, indicate RX */ | ||
327 | ptr = cs->rcvbuf; | ||
328 | cs->rcvidx = 0; | ||
329 | schedule_event(cs, D_RCVBUFREADY); | ||
330 | } | ||
331 | } | ||
332 | /* Packet to long, overflow */ | ||
333 | if(cs->rcvidx >= MAX_DFRAME_LEN_L1) { | ||
334 | if (cs->debug & L1_DEB_WARN) | ||
335 | debugl1(cs, "AMD7930: empty_Dfifo L2-Framelength overrun"); | ||
336 | cs->rcvidx = 0; | ||
337 | return; | ||
338 | } | ||
339 | /* AMD interrupts on */ | ||
340 | AmdIrqOn(cs); | ||
341 | } | ||
342 | |||
343 | |||
344 | static void | ||
345 | Amd7930_fill_Dfifo(struct IsdnCardState *cs) | ||
346 | { | ||
347 | |||
348 | WORD dtcrr, dtcrw, len, count; | ||
349 | BYTE txstat, dmr3; | ||
350 | BYTE *ptr, *deb_ptr; | ||
351 | |||
352 | if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO)) | ||
353 | debugl1(cs, "Amd7930: fill_Dfifo"); | ||
354 | |||
355 | if ((!cs->tx_skb) || (cs->tx_skb->len <= 0)) | ||
356 | return; | ||
357 | |||
358 | dtcrw = 0; | ||
359 | if(!cs->dc.amd7930.tx_xmtlen) | ||
360 | /* new Frame */ | ||
361 | len = dtcrw = cs->tx_skb->len; | ||
362 | /* continue frame */ | ||
363 | else len = cs->dc.amd7930.tx_xmtlen; | ||
364 | |||
365 | |||
366 | /* AMD interrupts off */ | ||
367 | AmdIrqOff(cs); | ||
368 | |||
369 | deb_ptr = ptr = cs->tx_skb->data; | ||
370 | |||
371 | /* while free place in tx-fifo available and data in sk-buffer */ | ||
372 | txstat = 0x10; | ||
373 | while((txstat & 0x10) && (cs->tx_cnt < len)) { | ||
374 | wByteAMD(cs, 0x04, *ptr); | ||
375 | ptr++; | ||
376 | cs->tx_cnt++; | ||
377 | txstat= rByteAMD(cs, 0x07); | ||
378 | } | ||
379 | count = ptr - cs->tx_skb->data; | ||
380 | skb_pull(cs->tx_skb, count); | ||
381 | |||
382 | |||
383 | dtcrr = rWordAMD(cs, 0x85); // DTCR | ||
384 | dmr3 = rByteAMD(cs, 0x8E); | ||
385 | |||
386 | if (cs->debug & L1_DEB_ISAC) { | ||
387 | debugl1(cs, "Amd7930: fill_Dfifo, DMR3: 0x%02X, DTCR read: 0x%04X write: 0x%02X 0x%02X", dmr3, dtcrr, LOBYTE(dtcrw), HIBYTE(dtcrw)); | ||
388 | } | ||
389 | |||
390 | /* writeing of dtcrw starts transmit */ | ||
391 | if(!cs->dc.amd7930.tx_xmtlen) { | ||
392 | wWordAMD(cs, 0x85, dtcrw); | ||
393 | cs->dc.amd7930.tx_xmtlen = dtcrw; | ||
394 | } | ||
395 | |||
396 | if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) { | ||
397 | debugl1(cs, "Amd7930: fill_Dfifo dbusytimer running"); | ||
398 | del_timer(&cs->dbusytimer); | ||
399 | } | ||
400 | init_timer(&cs->dbusytimer); | ||
401 | cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ) / 1000); | ||
402 | add_timer(&cs->dbusytimer); | ||
403 | |||
404 | if (cs->debug & L1_DEB_ISAC_FIFO) { | ||
405 | char *t = cs->dlog; | ||
406 | |||
407 | t += sprintf(t, "Amd7930: fill_Dfifo cnt: %d |", count); | ||
408 | QuickHex(t, deb_ptr, count); | ||
409 | debugl1(cs, cs->dlog); | ||
410 | } | ||
411 | /* AMD interrupts on */ | ||
412 | AmdIrqOn(cs); | ||
413 | } | ||
414 | |||
415 | |||
416 | void Amd7930_interrupt(struct IsdnCardState *cs, BYTE irflags) | ||
417 | { | ||
418 | BYTE dsr1, dsr2, lsr; | ||
419 | WORD der; | ||
420 | |||
421 | while (irflags) | ||
422 | { | ||
423 | |||
424 | dsr1 = rByteAMD(cs, 0x02); | ||
425 | der = rWordAMD(cs, 0x03); | ||
426 | dsr2 = rByteAMD(cs, 0x07); | ||
427 | lsr = rByteAMD(cs, 0xA1); | ||
428 | |||
429 | if (cs->debug & L1_DEB_ISAC) | ||
430 | debugl1(cs, "Amd7930: interrupt: flags: 0x%02X, DSR1: 0x%02X, DSR2: 0x%02X, LSR: 0x%02X, DER=0x%04X", irflags, dsr1, dsr2, lsr, der); | ||
431 | |||
432 | /* D error -> read DER and DSR2 bit 2 */ | ||
433 | if (der || (dsr2 & 4)) { | ||
434 | |||
435 | if (cs->debug & L1_DEB_WARN) | ||
436 | debugl1(cs, "Amd7930: interrupt: D error DER=0x%04X", der); | ||
437 | |||
438 | /* RX, TX abort if collision detected */ | ||
439 | if (der & 2) { | ||
440 | wByteAMD(cs, 0x21, 0xC2); | ||
441 | wByteAMD(cs, 0x21, 0x02); | ||
442 | if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) | ||
443 | del_timer(&cs->dbusytimer); | ||
444 | if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags)) | ||
445 | schedule_event(cs, D_CLEARBUSY); | ||
446 | /* restart frame */ | ||
447 | if (cs->tx_skb) { | ||
448 | skb_push(cs->tx_skb, cs->tx_cnt); | ||
449 | cs->tx_cnt = 0; | ||
450 | cs->dc.amd7930.tx_xmtlen = 0; | ||
451 | Amd7930_fill_Dfifo(cs); | ||
452 | } else { | ||
453 | printk(KERN_WARNING "HiSax: Amd7930 D-Collision, no skb\n"); | ||
454 | debugl1(cs, "Amd7930: interrupt: D-Collision, no skb"); | ||
455 | } | ||
456 | } | ||
457 | /* remove damaged data from fifo */ | ||
458 | Amd7930_empty_Dfifo(cs, 1); | ||
459 | |||
460 | if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) | ||
461 | del_timer(&cs->dbusytimer); | ||
462 | if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags)) | ||
463 | schedule_event(cs, D_CLEARBUSY); | ||
464 | /* restart TX-Frame */ | ||
465 | if (cs->tx_skb) { | ||
466 | skb_push(cs->tx_skb, cs->tx_cnt); | ||
467 | cs->tx_cnt = 0; | ||
468 | cs->dc.amd7930.tx_xmtlen = 0; | ||
469 | Amd7930_fill_Dfifo(cs); | ||
470 | } | ||
471 | } | ||
472 | |||
473 | /* D TX FIFO empty -> fill */ | ||
474 | if (irflags & 1) { | ||
475 | if (cs->debug & L1_DEB_ISAC) | ||
476 | debugl1(cs, "Amd7930: interrupt: clear Timer and fill D-TX-FIFO if data"); | ||
477 | |||
478 | /* AMD interrupts off */ | ||
479 | AmdIrqOff(cs); | ||
480 | |||
481 | if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) | ||
482 | del_timer(&cs->dbusytimer); | ||
483 | if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags)) | ||
484 | schedule_event(cs, D_CLEARBUSY); | ||
485 | if (cs->tx_skb) { | ||
486 | if (cs->tx_skb->len) | ||
487 | Amd7930_fill_Dfifo(cs); | ||
488 | } | ||
489 | /* AMD interrupts on */ | ||
490 | AmdIrqOn(cs); | ||
491 | } | ||
492 | |||
493 | |||
494 | /* D RX FIFO full or tiny packet in Fifo -> empty */ | ||
495 | if ((irflags & 2) || (dsr1 & 2)) { | ||
496 | if (cs->debug & L1_DEB_ISAC) | ||
497 | debugl1(cs, "Amd7930: interrupt: empty D-FIFO"); | ||
498 | Amd7930_empty_Dfifo(cs, 0); | ||
499 | } | ||
500 | |||
501 | |||
502 | /* D-Frame transmit complete */ | ||
503 | if (dsr1 & 64) { | ||
504 | if (cs->debug & L1_DEB_ISAC) { | ||
505 | debugl1(cs, "Amd7930: interrupt: transmit packet ready"); | ||
506 | } | ||
507 | /* AMD interrupts off */ | ||
508 | AmdIrqOff(cs); | ||
509 | |||
510 | if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) | ||
511 | del_timer(&cs->dbusytimer); | ||
512 | if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags)) | ||
513 | schedule_event(cs, D_CLEARBUSY); | ||
514 | |||
515 | if (cs->tx_skb) { | ||
516 | if (cs->debug & L1_DEB_ISAC) | ||
517 | debugl1(cs, "Amd7930: interrupt: TX-Packet ready, freeing skb"); | ||
518 | dev_kfree_skb_irq(cs->tx_skb); | ||
519 | cs->tx_cnt = 0; | ||
520 | cs->dc.amd7930.tx_xmtlen=0; | ||
521 | cs->tx_skb = NULL; | ||
522 | } | ||
523 | if ((cs->tx_skb = skb_dequeue(&cs->sq))) { | ||
524 | if (cs->debug & L1_DEB_ISAC) | ||
525 | debugl1(cs, "Amd7930: interrupt: TX-Packet ready, next packet dequeued"); | ||
526 | cs->tx_cnt = 0; | ||
527 | cs->dc.amd7930.tx_xmtlen=0; | ||
528 | Amd7930_fill_Dfifo(cs); | ||
529 | } | ||
530 | else | ||
531 | schedule_event(cs, D_XMTBUFREADY); | ||
532 | /* AMD interrupts on */ | ||
533 | AmdIrqOn(cs); | ||
534 | } | ||
535 | |||
536 | /* LIU status interrupt -> read LSR, check statechanges */ | ||
537 | if (lsr & 0x38) { | ||
538 | /* AMD interrupts off */ | ||
539 | AmdIrqOff(cs); | ||
540 | |||
541 | if (cs->debug & L1_DEB_ISAC) | ||
542 | debugl1(cs, "Amd: interrupt: LSR=0x%02X, LIU is in state %d", lsr, ((lsr & 0x7) +2)); | ||
543 | |||
544 | cs->dc.amd7930.ph_state = (lsr & 0x7) + 2; | ||
545 | |||
546 | schedule_event(cs, D_L1STATECHANGE); | ||
547 | /* AMD interrupts on */ | ||
548 | AmdIrqOn(cs); | ||
549 | } | ||
550 | |||
551 | /* reads Interrupt-Register again. If there is a new interrupt-flag: restart handler */ | ||
552 | irflags = rByteAMD(cs, 0x00); | ||
553 | } | ||
554 | |||
555 | } | ||
556 | |||
557 | static void | ||
558 | Amd7930_l1hw(struct PStack *st, int pr, void *arg) | ||
559 | { | ||
560 | struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; | ||
561 | struct sk_buff *skb = arg; | ||
562 | u_long flags; | ||
563 | |||
564 | if (cs->debug & L1_DEB_ISAC) | ||
565 | debugl1(cs, "Amd7930: l1hw called, pr: 0x%04X", pr); | ||
566 | |||
567 | switch (pr) { | ||
568 | case (PH_DATA | REQUEST): | ||
569 | if (cs->debug & DEB_DLOG_HEX) | ||
570 | LogFrame(cs, skb->data, skb->len); | ||
571 | if (cs->debug & DEB_DLOG_VERBOSE) | ||
572 | dlogframe(cs, skb, 0); | ||
573 | spin_lock_irqsave(&cs->lock, flags); | ||
574 | if (cs->tx_skb) { | ||
575 | skb_queue_tail(&cs->sq, skb); | ||
576 | #ifdef L2FRAME_DEBUG /* psa */ | ||
577 | if (cs->debug & L1_DEB_LAPD) | ||
578 | Logl2Frame(cs, skb, "Amd7930: l1hw: PH_DATA Queued", 0); | ||
579 | #endif | ||
580 | } else { | ||
581 | cs->tx_skb = skb; | ||
582 | cs->tx_cnt = 0; | ||
583 | cs->dc.amd7930.tx_xmtlen=0; | ||
584 | #ifdef L2FRAME_DEBUG /* psa */ | ||
585 | if (cs->debug & L1_DEB_LAPD) | ||
586 | Logl2Frame(cs, skb, "Amd7930: l1hw: PH_DATA", 0); | ||
587 | #endif | ||
588 | Amd7930_fill_Dfifo(cs); | ||
589 | } | ||
590 | spin_unlock_irqrestore(&cs->lock, flags); | ||
591 | break; | ||
592 | case (PH_PULL | INDICATION): | ||
593 | spin_lock_irqsave(&cs->lock, flags); | ||
594 | if (cs->tx_skb) { | ||
595 | if (cs->debug & L1_DEB_WARN) | ||
596 | debugl1(cs, "Amd7930: l1hw: l2l1 tx_skb exist this shouldn't happen"); | ||
597 | skb_queue_tail(&cs->sq, skb); | ||
598 | break; | ||
599 | } | ||
600 | if (cs->debug & DEB_DLOG_HEX) | ||
601 | LogFrame(cs, skb->data, skb->len); | ||
602 | if (cs->debug & DEB_DLOG_VERBOSE) | ||
603 | dlogframe(cs, skb, 0); | ||
604 | cs->tx_skb = skb; | ||
605 | cs->tx_cnt = 0; | ||
606 | cs->dc.amd7930.tx_xmtlen=0; | ||
607 | #ifdef L2FRAME_DEBUG /* psa */ | ||
608 | if (cs->debug & L1_DEB_LAPD) | ||
609 | Logl2Frame(cs, skb, "Amd7930: l1hw: PH_DATA_PULLED", 0); | ||
610 | #endif | ||
611 | Amd7930_fill_Dfifo(cs); | ||
612 | spin_unlock_irqrestore(&cs->lock, flags); | ||
613 | break; | ||
614 | case (PH_PULL | REQUEST): | ||
615 | #ifdef L2FRAME_DEBUG /* psa */ | ||
616 | if (cs->debug & L1_DEB_LAPD) | ||
617 | debugl1(cs, "Amd7930: l1hw: -> PH_REQUEST_PULL, skb: %s", (cs->tx_skb)? "yes":"no"); | ||
618 | #endif | ||
619 | if (!cs->tx_skb) { | ||
620 | test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); | ||
621 | st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); | ||
622 | } else | ||
623 | test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); | ||
624 | break; | ||
625 | case (HW_RESET | REQUEST): | ||
626 | spin_lock_irqsave(&cs->lock, flags); | ||
627 | if ((cs->dc.amd7930.ph_state == 8)) { | ||
628 | /* b-channels off, PH-AR cleared | ||
629 | * change to F3 */ | ||
630 | Amd7930_ph_command(cs, 0x20, "HW_RESET REQEST"); //LMR1 bit 5 | ||
631 | spin_unlock_irqrestore(&cs->lock, flags); | ||
632 | } else { | ||
633 | Amd7930_ph_command(cs, 0x40, "HW_RESET REQUEST"); | ||
634 | cs->dc.amd7930.ph_state = 2; | ||
635 | spin_unlock_irqrestore(&cs->lock, flags); | ||
636 | Amd7930_new_ph(cs); | ||
637 | } | ||
638 | break; | ||
639 | case (HW_ENABLE | REQUEST): | ||
640 | cs->dc.amd7930.ph_state = 9; | ||
641 | Amd7930_new_ph(cs); | ||
642 | break; | ||
643 | case (HW_INFO3 | REQUEST): | ||
644 | // automatic | ||
645 | break; | ||
646 | case (HW_TESTLOOP | REQUEST): | ||
647 | /* not implemented yet */ | ||
648 | break; | ||
649 | case (HW_DEACTIVATE | RESPONSE): | ||
650 | skb_queue_purge(&cs->rq); | ||
651 | skb_queue_purge(&cs->sq); | ||
652 | if (cs->tx_skb) { | ||
653 | dev_kfree_skb(cs->tx_skb); | ||
654 | cs->tx_skb = NULL; | ||
655 | } | ||
656 | if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) | ||
657 | del_timer(&cs->dbusytimer); | ||
658 | if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags)) | ||
659 | schedule_event(cs, D_CLEARBUSY); | ||
660 | break; | ||
661 | default: | ||
662 | if (cs->debug & L1_DEB_WARN) | ||
663 | debugl1(cs, "Amd7930: l1hw: unknown %04x", pr); | ||
664 | break; | ||
665 | } | ||
666 | } | ||
667 | |||
668 | void | ||
669 | setstack_Amd7930(struct PStack *st, struct IsdnCardState *cs) | ||
670 | { | ||
671 | |||
672 | if (cs->debug & L1_DEB_ISAC) | ||
673 | debugl1(cs, "Amd7930: setstack called"); | ||
674 | |||
675 | st->l1.l1hw = Amd7930_l1hw; | ||
676 | } | ||
677 | |||
678 | |||
679 | void | ||
680 | DC_Close_Amd7930(struct IsdnCardState *cs) { | ||
681 | if (cs->debug & L1_DEB_ISAC) | ||
682 | debugl1(cs, "Amd7930: DC_Close called"); | ||
683 | } | ||
684 | |||
685 | |||
686 | static void | ||
687 | dbusy_timer_handler(struct IsdnCardState *cs) | ||
688 | { | ||
689 | u_long flags; | ||
690 | struct PStack *stptr; | ||
691 | WORD dtcr, der; | ||
692 | BYTE dsr1, dsr2; | ||
693 | |||
694 | |||
695 | if (cs->debug & L1_DEB_ISAC) | ||
696 | debugl1(cs, "Amd7930: dbusy_timer expired!"); | ||
697 | |||
698 | if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) { | ||
699 | spin_lock_irqsave(&cs->lock, flags); | ||
700 | /* D Transmit Byte Count Register: | ||
701 | * Counts down packet's number of Bytes, 0 if packet ready */ | ||
702 | dtcr = rWordAMD(cs, 0x85); | ||
703 | dsr1 = rByteAMD(cs, 0x02); | ||
704 | dsr2 = rByteAMD(cs, 0x07); | ||
705 | der = rWordAMD(cs, 0x03); | ||
706 | |||
707 | if (cs->debug & L1_DEB_ISAC) | ||
708 | debugl1(cs, "Amd7930: dbusy_timer_handler: DSR1=0x%02X, DSR2=0x%02X, DER=0x%04X, cs->tx_skb->len=%u, tx_stat=%u, dtcr=%u, cs->tx_cnt=%u", dsr1, dsr2, der, cs->tx_skb->len, cs->dc.amd7930.tx_xmtlen, dtcr, cs->tx_cnt); | ||
709 | |||
710 | if ((cs->dc.amd7930.tx_xmtlen - dtcr) < cs->tx_cnt) { /* D-Channel Busy */ | ||
711 | test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags); | ||
712 | stptr = cs->stlist; | ||
713 | spin_unlock_irqrestore(&cs->lock, flags); | ||
714 | while (stptr != NULL) { | ||
715 | stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL); | ||
716 | stptr = stptr->next; | ||
717 | } | ||
718 | |||
719 | } else { | ||
720 | /* discard frame; reset transceiver */ | ||
721 | test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags); | ||
722 | if (cs->tx_skb) { | ||
723 | dev_kfree_skb_any(cs->tx_skb); | ||
724 | cs->tx_cnt = 0; | ||
725 | cs->tx_skb = NULL; | ||
726 | cs->dc.amd7930.tx_xmtlen = 0; | ||
727 | } else { | ||
728 | printk(KERN_WARNING "HiSax: Amd7930: D-Channel Busy no skb\n"); | ||
729 | debugl1(cs, "Amd7930: D-Channel Busy no skb"); | ||
730 | |||
731 | } | ||
732 | /* Transmitter reset, abort transmit */ | ||
733 | wByteAMD(cs, 0x21, 0x82); | ||
734 | wByteAMD(cs, 0x21, 0x02); | ||
735 | spin_unlock_irqrestore(&cs->lock, flags); | ||
736 | cs->irq_func(cs->irq, cs, NULL); | ||
737 | |||
738 | if (cs->debug & L1_DEB_ISAC) | ||
739 | debugl1(cs, "Amd7930: dbusy_timer_handler: Transmitter reset"); | ||
740 | } | ||
741 | } | ||
742 | } | ||
743 | |||
744 | |||
745 | |||
746 | void __devinit | ||
747 | Amd7930_init(struct IsdnCardState *cs) | ||
748 | { | ||
749 | WORD *ptr; | ||
750 | BYTE cmd, cnt; | ||
751 | |||
752 | if (cs->debug & L1_DEB_ISAC) | ||
753 | debugl1(cs, "Amd7930: initamd called"); | ||
754 | |||
755 | cs->dc.amd7930.tx_xmtlen = 0; | ||
756 | cs->dc.amd7930.old_state = 0; | ||
757 | cs->dc.amd7930.lmr1 = 0x40; | ||
758 | cs->dc.amd7930.ph_command = Amd7930_ph_command; | ||
759 | cs->setstack_d = setstack_Amd7930; | ||
760 | cs->DC_Close = DC_Close_Amd7930; | ||
761 | |||
762 | /* AMD Initialisation */ | ||
763 | for (ptr = initAMD; *ptr != 0xFFFF; ) { | ||
764 | cmd = LOBYTE(*ptr); | ||
765 | |||
766 | /* read */ | ||
767 | if (*ptr++ >= 0x100) { | ||
768 | if (cmd < 8) | ||
769 | /* setzt Register zurück */ | ||
770 | rByteAMD(cs, cmd); | ||
771 | else { | ||
772 | wByteAMD(cs, 0x00, cmd); | ||
773 | for (cnt = *ptr++; cnt > 0; cnt--) | ||
774 | rByteAMD(cs, 0x01); | ||
775 | } | ||
776 | } | ||
777 | /* write */ | ||
778 | else if (cmd < 8) | ||
779 | wByteAMD(cs, cmd, LOBYTE(*ptr++)); | ||
780 | |||
781 | else { | ||
782 | wByteAMD(cs, 0x00, cmd); | ||
783 | for (cnt = *ptr++; cnt > 0; cnt--) | ||
784 | wByteAMD(cs, 0x01, LOBYTE(*ptr++)); | ||
785 | } | ||
786 | } | ||
787 | } | ||
788 | |||
789 | void __devinit | ||
790 | setup_Amd7930(struct IsdnCardState *cs) | ||
791 | { | ||
792 | INIT_WORK(&cs->tqueue, (void *)(void *) Amd7930_bh, cs); | ||
793 | cs->dbusytimer.function = (void *) dbusy_timer_handler; | ||
794 | cs->dbusytimer.data = (long) cs; | ||
795 | init_timer(&cs->dbusytimer); | ||
796 | } | ||