]>
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> | |
5a0e3ad6 | 62 | #include <linux/gfp.h> |
1da177e4 LT |
63 | |
64 | static void Amd7930_new_ph(struct IsdnCardState *cs); | |
65 | ||
66 | static WORD initAMD[] = { | |
67 | 0x0100, | |
68 | ||
69 | 0x00A5, 3, 0x01, 0x40, 0x58, // LPR, LMR1, LMR2 | |
70 | 0x0086, 1, 0x0B, // DMR1 (D-Buffer TH-Interrupts on) | |
71 | 0x0087, 1, 0xFF, // DMR2 | |
72 | 0x0092, 1, 0x03, // EFCR (extended mode d-channel-fifo on) | |
73 | 0x0090, 4, 0xFE, 0xFF, 0x02, 0x0F, // FRAR4, SRAR4, DMR3, DMR4 (address recognition ) | |
74 | 0x0084, 2, 0x80, 0x00, // DRLR | |
75 | 0x00C0, 1, 0x47, // PPCR1 | |
76 | 0x00C8, 1, 0x01, // PPCR2 | |
77 | ||
78 | 0x0102, | |
79 | 0x0107, | |
80 | 0x01A1, 1, | |
81 | 0x0121, 1, | |
82 | 0x0189, 2, | |
83 | ||
84 | 0x0045, 4, 0x61, 0x72, 0x00, 0x00, // MCR1, MCR2, MCR3, MCR4 | |
85 | 0x0063, 2, 0x08, 0x08, // GX | |
86 | 0x0064, 2, 0x08, 0x08, // GR | |
87 | 0x0065, 2, 0x99, 0x00, // GER | |
88 | 0x0066, 2, 0x7C, 0x8B, // STG | |
89 | 0x0067, 2, 0x00, 0x00, // FTGR1, FTGR2 | |
90 | 0x0068, 2, 0x20, 0x20, // ATGR1, ATGR2 | |
91 | 0x0069, 1, 0x4F, // MMR1 | |
92 | 0x006A, 1, 0x00, // MMR2 | |
93 | 0x006C, 1, 0x40, // MMR3 | |
94 | 0x0021, 1, 0x02, // INIT | |
95 | 0x00A3, 1, 0x40, // LMR1 | |
96 | ||
97 | 0xFFFF | |
98 | }; | |
99 | ||
100 | ||
672c3fd9 | 101 | static void /* macro wWordAMD */ |
1da177e4 LT |
102 | WriteWordAmd7930(struct IsdnCardState *cs, BYTE reg, WORD val) |
103 | { | |
475be4d8 JP |
104 | wByteAMD(cs, 0x00, reg); |
105 | wByteAMD(cs, 0x01, LOBYTE(val)); | |
106 | wByteAMD(cs, 0x01, HIBYTE(val)); | |
1da177e4 LT |
107 | } |
108 | ||
672c3fd9 | 109 | static WORD /* macro rWordAMD */ |
1da177e4 LT |
110 | ReadWordAmd7930(struct IsdnCardState *cs, BYTE reg) |
111 | { | |
475be4d8 JP |
112 | WORD res; |
113 | /* direct access register */ | |
114 | if (reg < 8) { | |
115 | res = rByteAMD(cs, reg); | |
116 | res += 256 * rByteAMD(cs, reg); | |
117 | } | |
118 | /* indirect access register */ | |
119 | else { | |
120 | wByteAMD(cs, 0x00, reg); | |
121 | res = rByteAMD(cs, 0x01); | |
122 | res += 256 * rByteAMD(cs, 0x01); | |
123 | } | |
1da177e4 LT |
124 | return (res); |
125 | } | |
126 | ||
127 | ||
128 | static void | |
129 | Amd7930_ph_command(struct IsdnCardState *cs, u_char command, char *s) | |
130 | { | |
131 | if (cs->debug & L1_DEB_ISAC) | |
132 | debugl1(cs, "AMD7930: %s: ph_command 0x%02X", s, command); | |
133 | ||
475be4d8 JP |
134 | cs->dc.amd7930.lmr1 = command; |
135 | wByteAMD(cs, 0xA3, command); | |
1da177e4 LT |
136 | } |
137 | ||
138 | ||
139 | ||
140 | static BYTE i430States[] = { | |
141 | // to reset F3 F4 F5 F6 F7 F8 AR from | |
475be4d8 JP |
142 | 0x01, 0x02, 0x00, 0x00, 0x00, 0x07, 0x05, 0x00, // init |
143 | 0x01, 0x02, 0x00, 0x00, 0x00, 0x07, 0x05, 0x00, // reset | |
144 | 0x01, 0x02, 0x00, 0x00, 0x00, 0x09, 0x05, 0x04, // F3 | |
145 | 0x01, 0x02, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, // F4 | |
146 | 0x01, 0x02, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, // F5 | |
147 | 0x01, 0x03, 0x00, 0x00, 0x00, 0x06, 0x05, 0x00, // F6 | |
148 | 0x11, 0x13, 0x00, 0x00, 0x1B, 0x00, 0x15, 0x00, // F7 | |
149 | 0x01, 0x03, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, // F8 | |
150 | 0x01, 0x03, 0x00, 0x00, 0x00, 0x09, 0x00, 0x0A}; // AR | |
1da177e4 LT |
151 | |
152 | ||
153 | /* Row init - reset F3 F4 F5 F6 F7 F8 AR */ | |
154 | static BYTE stateHelper[] = { 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 }; | |
155 | ||
156 | ||
157 | ||
158 | ||
159 | static void | |
160 | Amd7930_get_state(struct IsdnCardState *cs) { | |
475be4d8 JP |
161 | BYTE lsr = rByteAMD(cs, 0xA1); |
162 | cs->dc.amd7930.ph_state = (lsr & 0x7) + 2; | |
163 | Amd7930_new_ph(cs); | |
1da177e4 LT |
164 | } |
165 | ||
166 | ||
167 | ||
168 | static void | |
169 | Amd7930_new_ph(struct IsdnCardState *cs) | |
170 | { | |
475be4d8 JP |
171 | u_char index = stateHelper[cs->dc.amd7930.old_state] * 8 + stateHelper[cs->dc.amd7930.ph_state] - 1; |
172 | u_char message = i430States[index]; | |
1da177e4 | 173 | |
475be4d8 | 174 | if (cs->debug & L1_DEB_ISAC) |
1da177e4 | 175 | debugl1(cs, "AMD7930: new_ph %d, old_ph %d, message %d, index %d", |
475be4d8 | 176 | cs->dc.amd7930.ph_state, cs->dc.amd7930.old_state, message & 0x0f, index); |
1da177e4 | 177 | |
475be4d8 | 178 | cs->dc.amd7930.old_state = cs->dc.amd7930.ph_state; |
1da177e4 | 179 | |
475be4d8 JP |
180 | /* abort transmit if nessesary */ |
181 | if ((message & 0xf0) && (cs->tx_skb)) { | |
182 | wByteAMD(cs, 0x21, 0xC2); | |
183 | wByteAMD(cs, 0x21, 0x02); | |
184 | } | |
1da177e4 LT |
185 | |
186 | switch (message & 0x0f) { | |
187 | ||
475be4d8 JP |
188 | case (1): |
189 | l1_msg(cs, HW_RESET | INDICATION, NULL); | |
190 | Amd7930_get_state(cs); | |
191 | break; | |
192 | case (2): /* init, Card starts in F3 */ | |
193 | l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL); | |
194 | break; | |
195 | case (3): | |
196 | l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL); | |
197 | break; | |
198 | case (4): | |
199 | l1_msg(cs, HW_POWERUP | CONFIRM, NULL); | |
200 | Amd7930_ph_command(cs, 0x50, "HW_ENABLE REQUEST"); | |
201 | break; | |
202 | case (5): | |
203 | l1_msg(cs, HW_RSYNC | INDICATION, NULL); | |
204 | break; | |
205 | case (6): | |
206 | l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL); | |
207 | break; | |
208 | case (7): /* init, Card starts in F7 */ | |
209 | l1_msg(cs, HW_RSYNC | INDICATION, NULL); | |
210 | l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL); | |
211 | break; | |
212 | case (8): | |
213 | l1_msg(cs, HW_POWERUP | CONFIRM, NULL); | |
214 | /* fall through */ | |
215 | case (9): | |
216 | Amd7930_ph_command(cs, 0x40, "HW_ENABLE REQ cleared if set"); | |
217 | l1_msg(cs, HW_RSYNC | INDICATION, NULL); | |
218 | l1_msg(cs, HW_INFO2 | INDICATION, NULL); | |
219 | l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL); | |
220 | break; | |
221 | case (10): | |
222 | Amd7930_ph_command(cs, 0x40, "T3 expired, HW_ENABLE REQ cleared"); | |
223 | cs->dc.amd7930.old_state = 3; | |
224 | break; | |
225 | case (11): | |
226 | l1_msg(cs, HW_INFO2 | INDICATION, NULL); | |
227 | break; | |
228 | default: | |
229 | break; | |
1da177e4 LT |
230 | } |
231 | } | |
232 | ||
233 | ||
234 | ||
235 | static void | |
c4028958 | 236 | Amd7930_bh(struct work_struct *work) |
1da177e4 | 237 | { |
c4028958 DH |
238 | struct IsdnCardState *cs = |
239 | container_of(work, struct IsdnCardState, tqueue); | |
475be4d8 | 240 | struct PStack *stptr; |
1da177e4 | 241 | |
1da177e4 | 242 | if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) { |
475be4d8 | 243 | if (cs->debug) |
1da177e4 LT |
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)) { | |
475be4d8 JP |
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 | } | |
1da177e4 LT |
268 | } |
269 | ||
270 | static void | |
271 | Amd7930_empty_Dfifo(struct IsdnCardState *cs, int flag) | |
272 | { | |
273 | ||
475be4d8 | 274 | BYTE stat, der; |
1da177e4 LT |
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 | ||
475be4d8 JP |
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); | |
1da177e4 | 318 | } |
475be4d8 JP |
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); | |
1da177e4 | 322 | } |
1da177e4 | 323 | } |
475be4d8 | 324 | |
1da177e4 | 325 | } |
475be4d8 JP |
326 | /* throw damaged packets away, reset receive-buffer, indicate RX */ |
327 | ptr = cs->rcvbuf; | |
1da177e4 | 328 | cs->rcvidx = 0; |
475be4d8 | 329 | schedule_event(cs, D_RCVBUFREADY); |
1da177e4 | 330 | } |
475be4d8 JP |
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 | } | |
1da177e4 LT |
339 | /* AMD interrupts on */ |
340 | AmdIrqOn(cs); | |
341 | } | |
342 | ||
343 | ||
344 | static void | |
345 | Amd7930_fill_Dfifo(struct IsdnCardState *cs) | |
346 | { | |
347 | ||
475be4d8 JP |
348 | WORD dtcrr, dtcrw, len, count; |
349 | BYTE txstat, dmr3; | |
350 | BYTE *ptr, *deb_ptr; | |
1da177e4 LT |
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 | ||
475be4d8 JP |
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; | |
1da177e4 LT |
364 | |
365 | ||
366 | /* AMD interrupts off */ | |
367 | AmdIrqOff(cs); | |
368 | ||
475be4d8 JP |
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; | |
1da177e4 LT |
380 | skb_pull(cs->tx_skb, count); |
381 | ||
382 | ||
475be4d8 JP |
383 | dtcrr = rWordAMD(cs, 0x85); // DTCR |
384 | dmr3 = rByteAMD(cs, 0x8E); | |
1da177e4 LT |
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)); | |
475be4d8 | 388 | } |
1da177e4 | 389 | |
475be4d8 JP |
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 | } | |
1da177e4 LT |
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 */ | |
475be4d8 | 412 | AmdIrqOn(cs); |
1da177e4 LT |
413 | } |
414 | ||
415 | ||
416 | void Amd7930_interrupt(struct IsdnCardState *cs, BYTE irflags) | |
417 | { | |
418 | BYTE dsr1, dsr2, lsr; | |
475be4d8 | 419 | WORD der; |
1da177e4 | 420 | |
475be4d8 JP |
421 | while (irflags) |
422 | { | |
1da177e4 | 423 | |
475be4d8 JP |
424 | dsr1 = rByteAMD(cs, 0x02); |
425 | der = rWordAMD(cs, 0x03); | |
426 | dsr2 = rByteAMD(cs, 0x07); | |
427 | lsr = rByteAMD(cs, 0xA1); | |
1da177e4 | 428 | |
475be4d8 JP |
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); | |
1da177e4 | 431 | |
475be4d8 JP |
432 | /* D error -> read DER and DSR2 bit 2 */ |
433 | if (der || (dsr2 & 4)) { | |
1da177e4 | 434 | |
475be4d8 JP |
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); | |
1da177e4 | 459 | |
1da177e4 LT |
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); | |
475be4d8 JP |
464 | /* restart TX-Frame */ |
465 | if (cs->tx_skb) { | |
1da177e4 LT |
466 | skb_push(cs->tx_skb, cs->tx_cnt); |
467 | cs->tx_cnt = 0; | |
475be4d8 | 468 | cs->dc.amd7930.tx_xmtlen = 0; |
1da177e4 | 469 | Amd7930_fill_Dfifo(cs); |
1da177e4 | 470 | } |
1da177e4 | 471 | } |
1da177e4 | 472 | |
475be4d8 JP |
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"); | |
1da177e4 | 477 | |
475be4d8 JP |
478 | /* AMD interrupts off */ |
479 | AmdIrqOff(cs); | |
1da177e4 | 480 | |
475be4d8 JP |
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); | |
1da177e4 | 491 | } |
1da177e4 LT |
492 | |
493 | ||
475be4d8 JP |
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 | } | |
1da177e4 LT |
500 | |
501 | ||
475be4d8 JP |
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); | |
1da177e4 | 509 | |
475be4d8 JP |
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); | |
1da177e4 | 514 | |
475be4d8 JP |
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); | |
1da177e4 | 534 | } |
1da177e4 | 535 | |
475be4d8 JP |
536 | /* LIU status interrupt -> read LSR, check statechanges */ |
537 | if (lsr & 0x38) { | |
538 | /* AMD interrupts off */ | |
539 | AmdIrqOff(cs); | |
1da177e4 | 540 | |
475be4d8 JP |
541 | if (cs->debug & L1_DEB_ISAC) |
542 | debugl1(cs, "Amd: interrupt: LSR=0x%02X, LIU is in state %d", lsr, ((lsr & 0x7) + 2)); | |
1da177e4 | 543 | |
475be4d8 | 544 | cs->dc.amd7930.ph_state = (lsr & 0x7) + 2; |
1da177e4 | 545 | |
475be4d8 JP |
546 | schedule_event(cs, D_L1STATECHANGE); |
547 | /* AMD interrupts on */ | |
548 | AmdIrqOn(cs); | |
549 | } | |
1da177e4 | 550 | |
475be4d8 JP |
551 | /* reads Interrupt-Register again. If there is a new interrupt-flag: restart handler */ |
552 | irflags = rByteAMD(cs, 0x00); | |
553 | } | |
1da177e4 LT |
554 | |
555 | } | |
556 | ||
557 | static void | |
558 | Amd7930_l1hw(struct PStack *st, int pr, void *arg) | |
559 | { | |
475be4d8 | 560 | struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; |
1da177e4 LT |
561 | struct sk_buff *skb = arg; |
562 | u_long flags; | |
563 | ||
475be4d8 | 564 | if (cs->debug & L1_DEB_ISAC) |
1da177e4 LT |
565 | debugl1(cs, "Amd7930: l1hw called, pr: 0x%04X", pr); |
566 | ||
567 | switch (pr) { | |
475be4d8 JP |
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); | |
1da177e4 | 576 | #ifdef L2FRAME_DEBUG /* psa */ |
475be4d8 JP |
577 | if (cs->debug & L1_DEB_LAPD) |
578 | Logl2Frame(cs, skb, "Amd7930: l1hw: PH_DATA Queued", 0); | |
1da177e4 | 579 | #endif |
475be4d8 | 580 | } else { |
1da177e4 LT |
581 | cs->tx_skb = skb; |
582 | cs->tx_cnt = 0; | |
475be4d8 | 583 | cs->dc.amd7930.tx_xmtlen = 0; |
1da177e4 LT |
584 | #ifdef L2FRAME_DEBUG /* psa */ |
585 | if (cs->debug & L1_DEB_LAPD) | |
475be4d8 | 586 | Logl2Frame(cs, skb, "Amd7930: l1hw: PH_DATA", 0); |
1da177e4 LT |
587 | #endif |
588 | Amd7930_fill_Dfifo(cs); | |
475be4d8 JP |
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); | |
1da177e4 LT |
598 | spin_unlock_irqrestore(&cs->lock, flags); |
599 | break; | |
475be4d8 JP |
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; | |
1da177e4 | 608 | #ifdef L2FRAME_DEBUG /* psa */ |
475be4d8 JP |
609 | if (cs->debug & L1_DEB_LAPD) |
610 | Logl2Frame(cs, skb, "Amd7930: l1hw: PH_DATA_PULLED", 0); | |
1da177e4 | 611 | #endif |
475be4d8 JP |
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; | |
1da177e4 LT |
666 | } |
667 | } | |
668 | ||
672c3fd9 | 669 | static void |
1da177e4 LT |
670 | setstack_Amd7930(struct PStack *st, struct IsdnCardState *cs) |
671 | { | |
672 | ||
475be4d8 | 673 | if (cs->debug & L1_DEB_ISAC) |
1da177e4 LT |
674 | debugl1(cs, "Amd7930: setstack called"); |
675 | ||
475be4d8 | 676 | st->l1.l1hw = Amd7930_l1hw; |
1da177e4 LT |
677 | } |
678 | ||
679 | ||
672c3fd9 | 680 | static void |
1da177e4 | 681 | DC_Close_Amd7930(struct IsdnCardState *cs) { |
475be4d8 | 682 | if (cs->debug & L1_DEB_ISAC) |
1da177e4 LT |
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; | |
475be4d8 JP |
692 | WORD dtcr, der; |
693 | BYTE dsr1, dsr2; | |
1da177e4 LT |
694 | |
695 | ||
475be4d8 | 696 | if (cs->debug & L1_DEB_ISAC) |
1da177e4 LT |
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); | |
475be4d8 JP |
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) | |
1da177e4 LT |
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; | |
475be4d8 | 727 | cs->dc.amd7930.tx_xmtlen = 0; |
1da177e4 LT |
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 | 738 | |
475be4d8 | 739 | if (cs->debug & L1_DEB_ISAC) |
1da177e4 LT |
740 | debugl1(cs, "Amd7930: dbusy_timer_handler: Transmitter reset"); |
741 | } | |
742 | } | |
743 | } | |
744 | ||
745 | ||
746 | ||
054b0e2b | 747 | void Amd7930_init(struct IsdnCardState *cs) |
1da177e4 | 748 | { |
475be4d8 JP |
749 | WORD *ptr; |
750 | BYTE cmd, cnt; | |
1da177e4 | 751 | |
475be4d8 | 752 | if (cs->debug & L1_DEB_ISAC) |
1da177e4 LT |
753 | debugl1(cs, "Amd7930: initamd called"); |
754 | ||
475be4d8 JP |
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; | |
1da177e4 LT |
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 | ||
475be4d8 JP |
766 | /* read */ |
767 | if (*ptr++ >= 0x100) { | |
1da177e4 | 768 | if (cmd < 8) |
475be4d8 JP |
769 | /* reset register */ |
770 | rByteAMD(cs, cmd); | |
1da177e4 LT |
771 | else { |
772 | wByteAMD(cs, 0x00, cmd); | |
773 | for (cnt = *ptr++; cnt > 0; cnt--) | |
774 | rByteAMD(cs, 0x01); | |
775 | } | |
776 | } | |
475be4d8 JP |
777 | /* write */ |
778 | else if (cmd < 8) | |
1da177e4 LT |
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 | { | |
475be4d8 | 792 | INIT_WORK(&cs->tqueue, Amd7930_bh); |
1da177e4 LT |
793 | cs->dbusytimer.function = (void *) dbusy_timer_handler; |
794 | cs->dbusytimer.data = (long) cs; | |
795 | init_timer(&cs->dbusytimer); | |
796 | } |