Commit | Line | Data |
---|---|---|
1da177e4 LT |
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 | ||
672c3fd9 | 100 | static void /* macro wWordAMD */ |
1da177e4 LT |
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 | ||
672c3fd9 | 108 | static WORD /* macro rWordAMD */ |
1da177e4 LT |
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 | |
c4028958 | 235 | Amd7930_bh(struct work_struct *work) |
1da177e4 | 236 | { |
c4028958 DH |
237 | struct IsdnCardState *cs = |
238 | container_of(work, struct IsdnCardState, tqueue); | |
1da177e4 LT |
239 | struct PStack *stptr; |
240 | ||
241 | if (!cs) | |
242 | return; | |
243 | if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) { | |
244 | if (cs->debug) | |
245 | debugl1(cs, "Amd7930: bh, D-Channel Busy cleared"); | |
246 | stptr = cs->stlist; | |
247 | while (stptr != NULL) { | |
248 | stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL); | |
249 | stptr = stptr->next; | |
250 | } | |
251 | } | |
252 | if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) { | |
253 | if (cs->debug & L1_DEB_ISAC) | |
254 | debugl1(cs, "AMD7930: bh, D_L1STATECHANGE"); | |
255 | Amd7930_new_ph(cs); | |
256 | } | |
257 | ||
258 | if (test_and_clear_bit(D_RCVBUFREADY, &cs->event)) { | |
259 | if (cs->debug & L1_DEB_ISAC) | |
260 | debugl1(cs, "AMD7930: bh, D_RCVBUFREADY"); | |
261 | DChannel_proc_rcv(cs); | |
262 | } | |
263 | ||
264 | if (test_and_clear_bit(D_XMTBUFREADY, &cs->event)) { | |
265 | if (cs->debug & L1_DEB_ISAC) | |
266 | debugl1(cs, "AMD7930: bh, D_XMTBUFREADY"); | |
267 | DChannel_proc_xmt(cs); | |
268 | } | |
269 | } | |
270 | ||
271 | static void | |
272 | Amd7930_empty_Dfifo(struct IsdnCardState *cs, int flag) | |
273 | { | |
274 | ||
275 | BYTE stat, der; | |
276 | BYTE *ptr; | |
277 | struct sk_buff *skb; | |
278 | ||
279 | ||
280 | if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO)) | |
281 | debugl1(cs, "Amd7930: empty_Dfifo"); | |
282 | ||
283 | ||
284 | ptr = cs->rcvbuf + cs->rcvidx; | |
285 | ||
286 | /* AMD interrupts off */ | |
287 | AmdIrqOff(cs); | |
288 | ||
289 | /* read D-Channel-Fifo*/ | |
290 | stat = rByteAMD(cs, 0x07); // DSR2 | |
291 | ||
292 | /* while Data in Fifo ... */ | |
293 | while ( (stat & 2) && ((ptr-cs->rcvbuf) < MAX_DFRAME_LEN_L1) ) { | |
294 | *ptr = rByteAMD(cs, 0x04); // DCRB | |
295 | ptr++; | |
296 | stat = rByteAMD(cs, 0x07); // DSR2 | |
297 | cs->rcvidx = ptr - cs->rcvbuf; | |
298 | ||
299 | /* Paket ready? */ | |
300 | if (stat & 1) { | |
301 | ||
302 | der = rWordAMD(cs, 0x03); | |
303 | ||
304 | /* no errors, packet ok */ | |
305 | if(!der && !flag) { | |
306 | rWordAMD(cs, 0x89); // clear DRCR | |
307 | ||
308 | if ((cs->rcvidx) > 0) { | |
309 | if (!(skb = alloc_skb(cs->rcvidx, GFP_ATOMIC))) | |
310 | printk(KERN_WARNING "HiSax: Amd7930: empty_Dfifo, D receive out of memory!\n"); | |
311 | else { | |
312 | /* Debugging */ | |
313 | if (cs->debug & L1_DEB_ISAC_FIFO) { | |
314 | char *t = cs->dlog; | |
315 | ||
316 | t += sprintf(t, "Amd7930: empty_Dfifo cnt: %d |", cs->rcvidx); | |
317 | QuickHex(t, cs->rcvbuf, cs->rcvidx); | |
318 | debugl1(cs, cs->dlog); | |
319 | } | |
320 | /* moves received data in sk-buffer */ | |
321 | memcpy(skb_put(skb, cs->rcvidx), cs->rcvbuf, cs->rcvidx); | |
322 | skb_queue_tail(&cs->rq, skb); | |
323 | } | |
324 | } | |
325 | ||
326 | } | |
327 | /* throw damaged packets away, reset receive-buffer, indicate RX */ | |
328 | ptr = cs->rcvbuf; | |
329 | cs->rcvidx = 0; | |
330 | schedule_event(cs, D_RCVBUFREADY); | |
331 | } | |
332 | } | |
333 | /* Packet to long, overflow */ | |
334 | if(cs->rcvidx >= MAX_DFRAME_LEN_L1) { | |
335 | if (cs->debug & L1_DEB_WARN) | |
336 | debugl1(cs, "AMD7930: empty_Dfifo L2-Framelength overrun"); | |
337 | cs->rcvidx = 0; | |
338 | return; | |
339 | } | |
340 | /* AMD interrupts on */ | |
341 | AmdIrqOn(cs); | |
342 | } | |
343 | ||
344 | ||
345 | static void | |
346 | Amd7930_fill_Dfifo(struct IsdnCardState *cs) | |
347 | { | |
348 | ||
349 | WORD dtcrr, dtcrw, len, count; | |
350 | BYTE txstat, dmr3; | |
351 | BYTE *ptr, *deb_ptr; | |
352 | ||
353 | if ((cs->debug & L1_DEB_ISAC) && !(cs->debug & L1_DEB_ISAC_FIFO)) | |
354 | debugl1(cs, "Amd7930: fill_Dfifo"); | |
355 | ||
356 | if ((!cs->tx_skb) || (cs->tx_skb->len <= 0)) | |
357 | return; | |
358 | ||
359 | dtcrw = 0; | |
360 | if(!cs->dc.amd7930.tx_xmtlen) | |
361 | /* new Frame */ | |
362 | len = dtcrw = cs->tx_skb->len; | |
363 | /* continue frame */ | |
364 | else len = cs->dc.amd7930.tx_xmtlen; | |
365 | ||
366 | ||
367 | /* AMD interrupts off */ | |
368 | AmdIrqOff(cs); | |
369 | ||
370 | deb_ptr = ptr = cs->tx_skb->data; | |
371 | ||
372 | /* while free place in tx-fifo available and data in sk-buffer */ | |
373 | txstat = 0x10; | |
374 | while((txstat & 0x10) && (cs->tx_cnt < len)) { | |
375 | wByteAMD(cs, 0x04, *ptr); | |
376 | ptr++; | |
377 | cs->tx_cnt++; | |
378 | txstat= rByteAMD(cs, 0x07); | |
379 | } | |
380 | count = ptr - cs->tx_skb->data; | |
381 | skb_pull(cs->tx_skb, count); | |
382 | ||
383 | ||
384 | dtcrr = rWordAMD(cs, 0x85); // DTCR | |
385 | dmr3 = rByteAMD(cs, 0x8E); | |
386 | ||
387 | if (cs->debug & L1_DEB_ISAC) { | |
388 | debugl1(cs, "Amd7930: fill_Dfifo, DMR3: 0x%02X, DTCR read: 0x%04X write: 0x%02X 0x%02X", dmr3, dtcrr, LOBYTE(dtcrw), HIBYTE(dtcrw)); | |
389 | } | |
390 | ||
391 | /* writeing of dtcrw starts transmit */ | |
392 | if(!cs->dc.amd7930.tx_xmtlen) { | |
393 | wWordAMD(cs, 0x85, dtcrw); | |
394 | cs->dc.amd7930.tx_xmtlen = dtcrw; | |
395 | } | |
396 | ||
397 | if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) { | |
398 | debugl1(cs, "Amd7930: fill_Dfifo dbusytimer running"); | |
399 | del_timer(&cs->dbusytimer); | |
400 | } | |
401 | init_timer(&cs->dbusytimer); | |
402 | cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ) / 1000); | |
403 | add_timer(&cs->dbusytimer); | |
404 | ||
405 | if (cs->debug & L1_DEB_ISAC_FIFO) { | |
406 | char *t = cs->dlog; | |
407 | ||
408 | t += sprintf(t, "Amd7930: fill_Dfifo cnt: %d |", count); | |
409 | QuickHex(t, deb_ptr, count); | |
410 | debugl1(cs, cs->dlog); | |
411 | } | |
412 | /* AMD interrupts on */ | |
413 | AmdIrqOn(cs); | |
414 | } | |
415 | ||
416 | ||
417 | void Amd7930_interrupt(struct IsdnCardState *cs, BYTE irflags) | |
418 | { | |
419 | BYTE dsr1, dsr2, lsr; | |
420 | WORD der; | |
421 | ||
422 | while (irflags) | |
423 | { | |
424 | ||
425 | dsr1 = rByteAMD(cs, 0x02); | |
426 | der = rWordAMD(cs, 0x03); | |
427 | dsr2 = rByteAMD(cs, 0x07); | |
428 | lsr = rByteAMD(cs, 0xA1); | |
429 | ||
430 | if (cs->debug & L1_DEB_ISAC) | |
431 | debugl1(cs, "Amd7930: interrupt: flags: 0x%02X, DSR1: 0x%02X, DSR2: 0x%02X, LSR: 0x%02X, DER=0x%04X", irflags, dsr1, dsr2, lsr, der); | |
432 | ||
433 | /* D error -> read DER and DSR2 bit 2 */ | |
434 | if (der || (dsr2 & 4)) { | |
435 | ||
436 | if (cs->debug & L1_DEB_WARN) | |
437 | debugl1(cs, "Amd7930: interrupt: D error DER=0x%04X", der); | |
438 | ||
439 | /* RX, TX abort if collision detected */ | |
440 | if (der & 2) { | |
441 | wByteAMD(cs, 0x21, 0xC2); | |
442 | wByteAMD(cs, 0x21, 0x02); | |
443 | if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) | |
444 | del_timer(&cs->dbusytimer); | |
445 | if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags)) | |
446 | schedule_event(cs, D_CLEARBUSY); | |
447 | /* restart frame */ | |
448 | if (cs->tx_skb) { | |
449 | skb_push(cs->tx_skb, cs->tx_cnt); | |
450 | cs->tx_cnt = 0; | |
451 | cs->dc.amd7930.tx_xmtlen = 0; | |
452 | Amd7930_fill_Dfifo(cs); | |
453 | } else { | |
454 | printk(KERN_WARNING "HiSax: Amd7930 D-Collision, no skb\n"); | |
455 | debugl1(cs, "Amd7930: interrupt: D-Collision, no skb"); | |
456 | } | |
457 | } | |
458 | /* remove damaged data from fifo */ | |
459 | Amd7930_empty_Dfifo(cs, 1); | |
460 | ||
461 | if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) | |
462 | del_timer(&cs->dbusytimer); | |
463 | if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags)) | |
464 | schedule_event(cs, D_CLEARBUSY); | |
465 | /* restart TX-Frame */ | |
466 | if (cs->tx_skb) { | |
467 | skb_push(cs->tx_skb, cs->tx_cnt); | |
468 | cs->tx_cnt = 0; | |
469 | cs->dc.amd7930.tx_xmtlen = 0; | |
470 | Amd7930_fill_Dfifo(cs); | |
471 | } | |
472 | } | |
473 | ||
474 | /* D TX FIFO empty -> fill */ | |
475 | if (irflags & 1) { | |
476 | if (cs->debug & L1_DEB_ISAC) | |
477 | debugl1(cs, "Amd7930: interrupt: clear Timer and fill D-TX-FIFO if data"); | |
478 | ||
479 | /* AMD interrupts off */ | |
480 | AmdIrqOff(cs); | |
481 | ||
482 | if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) | |
483 | del_timer(&cs->dbusytimer); | |
484 | if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags)) | |
485 | schedule_event(cs, D_CLEARBUSY); | |
486 | if (cs->tx_skb) { | |
487 | if (cs->tx_skb->len) | |
488 | Amd7930_fill_Dfifo(cs); | |
489 | } | |
490 | /* AMD interrupts on */ | |
491 | AmdIrqOn(cs); | |
492 | } | |
493 | ||
494 | ||
495 | /* D RX FIFO full or tiny packet in Fifo -> empty */ | |
496 | if ((irflags & 2) || (dsr1 & 2)) { | |
497 | if (cs->debug & L1_DEB_ISAC) | |
498 | debugl1(cs, "Amd7930: interrupt: empty D-FIFO"); | |
499 | Amd7930_empty_Dfifo(cs, 0); | |
500 | } | |
501 | ||
502 | ||
503 | /* D-Frame transmit complete */ | |
504 | if (dsr1 & 64) { | |
505 | if (cs->debug & L1_DEB_ISAC) { | |
506 | debugl1(cs, "Amd7930: interrupt: transmit packet ready"); | |
507 | } | |
508 | /* AMD interrupts off */ | |
509 | AmdIrqOff(cs); | |
510 | ||
511 | if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) | |
512 | del_timer(&cs->dbusytimer); | |
513 | if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags)) | |
514 | schedule_event(cs, D_CLEARBUSY); | |
515 | ||
516 | if (cs->tx_skb) { | |
517 | if (cs->debug & L1_DEB_ISAC) | |
518 | debugl1(cs, "Amd7930: interrupt: TX-Packet ready, freeing skb"); | |
519 | dev_kfree_skb_irq(cs->tx_skb); | |
520 | cs->tx_cnt = 0; | |
521 | cs->dc.amd7930.tx_xmtlen=0; | |
522 | cs->tx_skb = NULL; | |
523 | } | |
524 | if ((cs->tx_skb = skb_dequeue(&cs->sq))) { | |
525 | if (cs->debug & L1_DEB_ISAC) | |
526 | debugl1(cs, "Amd7930: interrupt: TX-Packet ready, next packet dequeued"); | |
527 | cs->tx_cnt = 0; | |
528 | cs->dc.amd7930.tx_xmtlen=0; | |
529 | Amd7930_fill_Dfifo(cs); | |
530 | } | |
531 | else | |
532 | schedule_event(cs, D_XMTBUFREADY); | |
533 | /* AMD interrupts on */ | |
534 | AmdIrqOn(cs); | |
535 | } | |
536 | ||
537 | /* LIU status interrupt -> read LSR, check statechanges */ | |
538 | if (lsr & 0x38) { | |
539 | /* AMD interrupts off */ | |
540 | AmdIrqOff(cs); | |
541 | ||
542 | if (cs->debug & L1_DEB_ISAC) | |
543 | debugl1(cs, "Amd: interrupt: LSR=0x%02X, LIU is in state %d", lsr, ((lsr & 0x7) +2)); | |
544 | ||
545 | cs->dc.amd7930.ph_state = (lsr & 0x7) + 2; | |
546 | ||
547 | schedule_event(cs, D_L1STATECHANGE); | |
548 | /* AMD interrupts on */ | |
549 | AmdIrqOn(cs); | |
550 | } | |
551 | ||
552 | /* reads Interrupt-Register again. If there is a new interrupt-flag: restart handler */ | |
553 | irflags = rByteAMD(cs, 0x00); | |
554 | } | |
555 | ||
556 | } | |
557 | ||
558 | static void | |
559 | Amd7930_l1hw(struct PStack *st, int pr, void *arg) | |
560 | { | |
561 | struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; | |
562 | struct sk_buff *skb = arg; | |
563 | u_long flags; | |
564 | ||
565 | if (cs->debug & L1_DEB_ISAC) | |
566 | debugl1(cs, "Amd7930: l1hw called, pr: 0x%04X", pr); | |
567 | ||
568 | switch (pr) { | |
569 | case (PH_DATA | REQUEST): | |
570 | if (cs->debug & DEB_DLOG_HEX) | |
571 | LogFrame(cs, skb->data, skb->len); | |
572 | if (cs->debug & DEB_DLOG_VERBOSE) | |
573 | dlogframe(cs, skb, 0); | |
574 | spin_lock_irqsave(&cs->lock, flags); | |
575 | if (cs->tx_skb) { | |
576 | skb_queue_tail(&cs->sq, skb); | |
577 | #ifdef L2FRAME_DEBUG /* psa */ | |
578 | if (cs->debug & L1_DEB_LAPD) | |
579 | Logl2Frame(cs, skb, "Amd7930: l1hw: PH_DATA Queued", 0); | |
580 | #endif | |
581 | } else { | |
582 | cs->tx_skb = skb; | |
583 | cs->tx_cnt = 0; | |
584 | cs->dc.amd7930.tx_xmtlen=0; | |
585 | #ifdef L2FRAME_DEBUG /* psa */ | |
586 | if (cs->debug & L1_DEB_LAPD) | |
587 | Logl2Frame(cs, skb, "Amd7930: l1hw: PH_DATA", 0); | |
588 | #endif | |
589 | Amd7930_fill_Dfifo(cs); | |
590 | } | |
591 | spin_unlock_irqrestore(&cs->lock, flags); | |
592 | break; | |
593 | case (PH_PULL | INDICATION): | |
594 | spin_lock_irqsave(&cs->lock, flags); | |
595 | if (cs->tx_skb) { | |
596 | if (cs->debug & L1_DEB_WARN) | |
597 | debugl1(cs, "Amd7930: l1hw: l2l1 tx_skb exist this shouldn't happen"); | |
598 | skb_queue_tail(&cs->sq, skb); | |
599 | break; | |
600 | } | |
601 | if (cs->debug & DEB_DLOG_HEX) | |
602 | LogFrame(cs, skb->data, skb->len); | |
603 | if (cs->debug & DEB_DLOG_VERBOSE) | |
604 | dlogframe(cs, skb, 0); | |
605 | cs->tx_skb = skb; | |
606 | cs->tx_cnt = 0; | |
607 | cs->dc.amd7930.tx_xmtlen=0; | |
608 | #ifdef L2FRAME_DEBUG /* psa */ | |
609 | if (cs->debug & L1_DEB_LAPD) | |
610 | Logl2Frame(cs, skb, "Amd7930: l1hw: PH_DATA_PULLED", 0); | |
611 | #endif | |
612 | Amd7930_fill_Dfifo(cs); | |
613 | spin_unlock_irqrestore(&cs->lock, flags); | |
614 | break; | |
615 | case (PH_PULL | REQUEST): | |
616 | #ifdef L2FRAME_DEBUG /* psa */ | |
617 | if (cs->debug & L1_DEB_LAPD) | |
618 | debugl1(cs, "Amd7930: l1hw: -> PH_REQUEST_PULL, skb: %s", (cs->tx_skb)? "yes":"no"); | |
619 | #endif | |
620 | if (!cs->tx_skb) { | |
621 | test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); | |
622 | st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); | |
623 | } else | |
624 | test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); | |
625 | break; | |
626 | case (HW_RESET | REQUEST): | |
627 | spin_lock_irqsave(&cs->lock, flags); | |
628 | if ((cs->dc.amd7930.ph_state == 8)) { | |
629 | /* b-channels off, PH-AR cleared | |
630 | * change to F3 */ | |
631 | Amd7930_ph_command(cs, 0x20, "HW_RESET REQEST"); //LMR1 bit 5 | |
632 | spin_unlock_irqrestore(&cs->lock, flags); | |
633 | } else { | |
634 | Amd7930_ph_command(cs, 0x40, "HW_RESET REQUEST"); | |
635 | cs->dc.amd7930.ph_state = 2; | |
636 | spin_unlock_irqrestore(&cs->lock, flags); | |
637 | Amd7930_new_ph(cs); | |
638 | } | |
639 | break; | |
640 | case (HW_ENABLE | REQUEST): | |
641 | cs->dc.amd7930.ph_state = 9; | |
642 | Amd7930_new_ph(cs); | |
643 | break; | |
644 | case (HW_INFO3 | REQUEST): | |
645 | // automatic | |
646 | break; | |
647 | case (HW_TESTLOOP | REQUEST): | |
648 | /* not implemented yet */ | |
649 | break; | |
650 | case (HW_DEACTIVATE | RESPONSE): | |
651 | skb_queue_purge(&cs->rq); | |
652 | skb_queue_purge(&cs->sq); | |
653 | if (cs->tx_skb) { | |
654 | dev_kfree_skb(cs->tx_skb); | |
655 | cs->tx_skb = NULL; | |
656 | } | |
657 | if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) | |
658 | del_timer(&cs->dbusytimer); | |
659 | if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags)) | |
660 | schedule_event(cs, D_CLEARBUSY); | |
661 | break; | |
662 | default: | |
663 | if (cs->debug & L1_DEB_WARN) | |
664 | debugl1(cs, "Amd7930: l1hw: unknown %04x", pr); | |
665 | break; | |
666 | } | |
667 | } | |
668 | ||
672c3fd9 | 669 | static void |
1da177e4 LT |
670 | setstack_Amd7930(struct PStack *st, struct IsdnCardState *cs) |
671 | { | |
672 | ||
673 | if (cs->debug & L1_DEB_ISAC) | |
674 | debugl1(cs, "Amd7930: setstack called"); | |
675 | ||
676 | st->l1.l1hw = Amd7930_l1hw; | |
677 | } | |
678 | ||
679 | ||
672c3fd9 | 680 | static void |
1da177e4 LT |
681 | DC_Close_Amd7930(struct IsdnCardState *cs) { |
682 | if (cs->debug & L1_DEB_ISAC) | |
683 | debugl1(cs, "Amd7930: DC_Close called"); | |
684 | } | |
685 | ||
686 | ||
687 | static void | |
688 | dbusy_timer_handler(struct IsdnCardState *cs) | |
689 | { | |
690 | u_long flags; | |
691 | struct PStack *stptr; | |
692 | WORD dtcr, der; | |
693 | BYTE dsr1, dsr2; | |
694 | ||
695 | ||
696 | if (cs->debug & L1_DEB_ISAC) | |
697 | debugl1(cs, "Amd7930: dbusy_timer expired!"); | |
698 | ||
699 | if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) { | |
700 | spin_lock_irqsave(&cs->lock, flags); | |
701 | /* D Transmit Byte Count Register: | |
702 | * Counts down packet's number of Bytes, 0 if packet ready */ | |
703 | dtcr = rWordAMD(cs, 0x85); | |
704 | dsr1 = rByteAMD(cs, 0x02); | |
705 | dsr2 = rByteAMD(cs, 0x07); | |
706 | der = rWordAMD(cs, 0x03); | |
707 | ||
708 | if (cs->debug & L1_DEB_ISAC) | |
709 | 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); | |
710 | ||
711 | if ((cs->dc.amd7930.tx_xmtlen - dtcr) < cs->tx_cnt) { /* D-Channel Busy */ | |
712 | test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags); | |
713 | stptr = cs->stlist; | |
714 | spin_unlock_irqrestore(&cs->lock, flags); | |
715 | while (stptr != NULL) { | |
716 | stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL); | |
717 | stptr = stptr->next; | |
718 | } | |
719 | ||
720 | } else { | |
721 | /* discard frame; reset transceiver */ | |
722 | test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags); | |
723 | if (cs->tx_skb) { | |
724 | dev_kfree_skb_any(cs->tx_skb); | |
725 | cs->tx_cnt = 0; | |
726 | cs->tx_skb = NULL; | |
727 | cs->dc.amd7930.tx_xmtlen = 0; | |
728 | } else { | |
729 | printk(KERN_WARNING "HiSax: Amd7930: D-Channel Busy no skb\n"); | |
730 | debugl1(cs, "Amd7930: D-Channel Busy no skb"); | |
731 | ||
732 | } | |
733 | /* Transmitter reset, abort transmit */ | |
734 | wByteAMD(cs, 0x21, 0x82); | |
735 | wByteAMD(cs, 0x21, 0x02); | |
736 | spin_unlock_irqrestore(&cs->lock, flags); | |
7d12e780 | 737 | cs->irq_func(cs->irq, cs); |
1da177e4 LT |
738 | |
739 | if (cs->debug & L1_DEB_ISAC) | |
740 | debugl1(cs, "Amd7930: dbusy_timer_handler: Transmitter reset"); | |
741 | } | |
742 | } | |
743 | } | |
744 | ||
745 | ||
746 | ||
747 | void __devinit | |
748 | Amd7930_init(struct IsdnCardState *cs) | |
749 | { | |
750 | WORD *ptr; | |
751 | BYTE cmd, cnt; | |
752 | ||
753 | if (cs->debug & L1_DEB_ISAC) | |
754 | debugl1(cs, "Amd7930: initamd called"); | |
755 | ||
756 | cs->dc.amd7930.tx_xmtlen = 0; | |
757 | cs->dc.amd7930.old_state = 0; | |
758 | cs->dc.amd7930.lmr1 = 0x40; | |
759 | cs->dc.amd7930.ph_command = Amd7930_ph_command; | |
760 | cs->setstack_d = setstack_Amd7930; | |
761 | cs->DC_Close = DC_Close_Amd7930; | |
762 | ||
763 | /* AMD Initialisation */ | |
764 | for (ptr = initAMD; *ptr != 0xFFFF; ) { | |
765 | cmd = LOBYTE(*ptr); | |
766 | ||
767 | /* read */ | |
768 | if (*ptr++ >= 0x100) { | |
769 | if (cmd < 8) |