diff options
Diffstat (limited to 'drivers/isdn/hisax/hisax_isac.c')
-rw-r--r-- | drivers/isdn/hisax/hisax_isac.c | 897 |
1 files changed, 897 insertions, 0 deletions
diff --git a/drivers/isdn/hisax/hisax_isac.c b/drivers/isdn/hisax/hisax_isac.c new file mode 100644 index 000000000000..f4972f6c1f5d --- /dev/null +++ b/drivers/isdn/hisax/hisax_isac.c | |||
@@ -0,0 +1,897 @@ | |||
1 | /* | ||
2 | * Driver for ISAC-S and ISAC-SX | ||
3 | * ISDN Subscriber Access Controller for Terminals | ||
4 | * | ||
5 | * Author Kai Germaschewski | ||
6 | * Copyright 2001 by Kai Germaschewski <kai.germaschewski@gmx.de> | ||
7 | * 2001 by Karsten Keil <keil@isdn4linux.de> | ||
8 | * | ||
9 | * based upon Karsten Keil's original isac.c driver | ||
10 | * | ||
11 | * This software may be used and distributed according to the terms | ||
12 | * of the GNU General Public License, incorporated herein by reference. | ||
13 | * | ||
14 | * Thanks to Wizard Computersysteme GmbH, Bremervoerde and | ||
15 | * SoHaNet Technology GmbH, Berlin | ||
16 | * for supporting the development of this driver | ||
17 | */ | ||
18 | |||
19 | /* TODO: | ||
20 | * specifically handle level vs edge triggered? | ||
21 | */ | ||
22 | |||
23 | #include <linux/module.h> | ||
24 | #include <linux/init.h> | ||
25 | #include <linux/netdevice.h> | ||
26 | #include "hisax_isac.h" | ||
27 | |||
28 | // debugging cruft | ||
29 | |||
30 | #define __debug_variable debug | ||
31 | #include "hisax_debug.h" | ||
32 | |||
33 | #ifdef CONFIG_HISAX_DEBUG | ||
34 | static int debug = 1; | ||
35 | module_param(debug, int, 0); | ||
36 | |||
37 | static char *ISACVer[] = { | ||
38 | "2086/2186 V1.1", | ||
39 | "2085 B1", | ||
40 | "2085 B2", | ||
41 | "2085 V2.3" | ||
42 | }; | ||
43 | #endif | ||
44 | |||
45 | MODULE_AUTHOR("Kai Germaschewski <kai.germaschewski@gmx.de>/Karsten Keil <kkeil@suse.de>"); | ||
46 | MODULE_DESCRIPTION("ISAC/ISAC-SX driver"); | ||
47 | MODULE_LICENSE("GPL"); | ||
48 | |||
49 | #define DBG_WARN 0x0001 | ||
50 | #define DBG_IRQ 0x0002 | ||
51 | #define DBG_L1M 0x0004 | ||
52 | #define DBG_PR 0x0008 | ||
53 | #define DBG_RFIFO 0x0100 | ||
54 | #define DBG_RPACKET 0x0200 | ||
55 | #define DBG_XFIFO 0x1000 | ||
56 | #define DBG_XPACKET 0x2000 | ||
57 | |||
58 | // we need to distinguish ISAC-S and ISAC-SX | ||
59 | #define TYPE_ISAC 0x00 | ||
60 | #define TYPE_ISACSX 0x01 | ||
61 | |||
62 | // registers etc. | ||
63 | #define ISAC_MASK 0x20 | ||
64 | #define ISAC_ISTA 0x20 | ||
65 | #define ISAC_ISTA_EXI 0x01 | ||
66 | #define ISAC_ISTA_SIN 0x02 | ||
67 | #define ISAC_ISTA_CISQ 0x04 | ||
68 | #define ISAC_ISTA_XPR 0x10 | ||
69 | #define ISAC_ISTA_RSC 0x20 | ||
70 | #define ISAC_ISTA_RPF 0x40 | ||
71 | #define ISAC_ISTA_RME 0x80 | ||
72 | |||
73 | #define ISAC_STAR 0x21 | ||
74 | #define ISAC_CMDR 0x21 | ||
75 | #define ISAC_CMDR_XRES 0x01 | ||
76 | #define ISAC_CMDR_XME 0x02 | ||
77 | #define ISAC_CMDR_XTF 0x08 | ||
78 | #define ISAC_CMDR_RRES 0x40 | ||
79 | #define ISAC_CMDR_RMC 0x80 | ||
80 | |||
81 | #define ISAC_EXIR 0x24 | ||
82 | #define ISAC_EXIR_MOS 0x04 | ||
83 | #define ISAC_EXIR_XDU 0x40 | ||
84 | #define ISAC_EXIR_XMR 0x80 | ||
85 | |||
86 | #define ISAC_ADF2 0x39 | ||
87 | #define ISAC_SPCR 0x30 | ||
88 | #define ISAC_ADF1 0x38 | ||
89 | |||
90 | #define ISAC_CIR0 0x31 | ||
91 | #define ISAC_CIX0 0x31 | ||
92 | #define ISAC_CIR0_CIC0 0x02 | ||
93 | #define ISAC_CIR0_CIC1 0x01 | ||
94 | |||
95 | #define ISAC_CIR1 0x33 | ||
96 | #define ISAC_CIX1 0x33 | ||
97 | #define ISAC_STCR 0x37 | ||
98 | #define ISAC_MODE 0x22 | ||
99 | |||
100 | #define ISAC_RSTA 0x27 | ||
101 | #define ISAC_RSTA_RDO 0x40 | ||
102 | #define ISAC_RSTA_CRC 0x20 | ||
103 | #define ISAC_RSTA_RAB 0x10 | ||
104 | |||
105 | #define ISAC_RBCL 0x25 | ||
106 | #define ISAC_RBCH 0x2A | ||
107 | #define ISAC_TIMR 0x23 | ||
108 | #define ISAC_SQXR 0x3b | ||
109 | #define ISAC_MOSR 0x3a | ||
110 | #define ISAC_MOCR 0x3a | ||
111 | #define ISAC_MOR0 0x32 | ||
112 | #define ISAC_MOX0 0x32 | ||
113 | #define ISAC_MOR1 0x34 | ||
114 | #define ISAC_MOX1 0x34 | ||
115 | |||
116 | #define ISAC_RBCH_XAC 0x80 | ||
117 | |||
118 | #define ISAC_CMD_TIM 0x0 | ||
119 | #define ISAC_CMD_RES 0x1 | ||
120 | #define ISAC_CMD_SSP 0x2 | ||
121 | #define ISAC_CMD_SCP 0x3 | ||
122 | #define ISAC_CMD_AR8 0x8 | ||
123 | #define ISAC_CMD_AR10 0x9 | ||
124 | #define ISAC_CMD_ARL 0xa | ||
125 | #define ISAC_CMD_DI 0xf | ||
126 | |||
127 | #define ISACSX_MASK 0x60 | ||
128 | #define ISACSX_ISTA 0x60 | ||
129 | #define ISACSX_ISTA_ICD 0x01 | ||
130 | #define ISACSX_ISTA_CIC 0x10 | ||
131 | |||
132 | #define ISACSX_MASKD 0x20 | ||
133 | #define ISACSX_ISTAD 0x20 | ||
134 | #define ISACSX_ISTAD_XDU 0x04 | ||
135 | #define ISACSX_ISTAD_XMR 0x08 | ||
136 | #define ISACSX_ISTAD_XPR 0x10 | ||
137 | #define ISACSX_ISTAD_RFO 0x20 | ||
138 | #define ISACSX_ISTAD_RPF 0x40 | ||
139 | #define ISACSX_ISTAD_RME 0x80 | ||
140 | |||
141 | #define ISACSX_CMDRD 0x21 | ||
142 | #define ISACSX_CMDRD_XRES 0x01 | ||
143 | #define ISACSX_CMDRD_XME 0x02 | ||
144 | #define ISACSX_CMDRD_XTF 0x08 | ||
145 | #define ISACSX_CMDRD_RRES 0x40 | ||
146 | #define ISACSX_CMDRD_RMC 0x80 | ||
147 | |||
148 | #define ISACSX_MODED 0x22 | ||
149 | |||
150 | #define ISACSX_RBCLD 0x26 | ||
151 | |||
152 | #define ISACSX_RSTAD 0x28 | ||
153 | #define ISACSX_RSTAD_RAB 0x10 | ||
154 | #define ISACSX_RSTAD_CRC 0x20 | ||
155 | #define ISACSX_RSTAD_RDO 0x40 | ||
156 | #define ISACSX_RSTAD_VFR 0x80 | ||
157 | |||
158 | #define ISACSX_CIR0 0x2e | ||
159 | #define ISACSX_CIR0_CIC0 0x08 | ||
160 | #define ISACSX_CIX0 0x2e | ||
161 | |||
162 | #define ISACSX_TR_CONF0 0x30 | ||
163 | |||
164 | #define ISACSX_TR_CONF2 0x32 | ||
165 | |||
166 | static struct Fsm l1fsm; | ||
167 | |||
168 | enum { | ||
169 | ST_L1_RESET, | ||
170 | ST_L1_F3_PDOWN, | ||
171 | ST_L1_F3_PUP, | ||
172 | ST_L1_F3_PEND_DEACT, | ||
173 | ST_L1_F4, | ||
174 | ST_L1_F5, | ||
175 | ST_L1_F6, | ||
176 | ST_L1_F7, | ||
177 | ST_L1_F8, | ||
178 | }; | ||
179 | |||
180 | #define L1_STATE_COUNT (ST_L1_F8+1) | ||
181 | |||
182 | static char *strL1State[] = | ||
183 | { | ||
184 | "ST_L1_RESET", | ||
185 | "ST_L1_F3_PDOWN", | ||
186 | "ST_L1_F3_PUP", | ||
187 | "ST_L1_F3_PEND_DEACT", | ||
188 | "ST_L1_F4", | ||
189 | "ST_L1_F5", | ||
190 | "ST_L1_F6", | ||
191 | "ST_L1_F7", | ||
192 | "ST_L1_F8", | ||
193 | }; | ||
194 | |||
195 | enum { | ||
196 | EV_PH_DR, // 0000 | ||
197 | EV_PH_RES, // 0001 | ||
198 | EV_PH_TMA, // 0010 | ||
199 | EV_PH_SLD, // 0011 | ||
200 | EV_PH_RSY, // 0100 | ||
201 | EV_PH_DR6, // 0101 | ||
202 | EV_PH_EI, // 0110 | ||
203 | EV_PH_PU, // 0111 | ||
204 | EV_PH_AR, // 1000 | ||
205 | EV_PH_9, // 1001 | ||
206 | EV_PH_ARL, // 1010 | ||
207 | EV_PH_CVR, // 1011 | ||
208 | EV_PH_AI8, // 1100 | ||
209 | EV_PH_AI10, // 1101 | ||
210 | EV_PH_AIL, // 1110 | ||
211 | EV_PH_DC, // 1111 | ||
212 | EV_PH_ACTIVATE_REQ, | ||
213 | EV_PH_DEACTIVATE_REQ, | ||
214 | EV_TIMER3, | ||
215 | }; | ||
216 | |||
217 | #define L1_EVENT_COUNT (EV_TIMER3 + 1) | ||
218 | |||
219 | static char *strL1Event[] = | ||
220 | { | ||
221 | "EV_PH_DR", // 0000 | ||
222 | "EV_PH_RES", // 0001 | ||
223 | "EV_PH_TMA", // 0010 | ||
224 | "EV_PH_SLD", // 0011 | ||
225 | "EV_PH_RSY", // 0100 | ||
226 | "EV_PH_DR6", // 0101 | ||
227 | "EV_PH_EI", // 0110 | ||
228 | "EV_PH_PU", // 0111 | ||
229 | "EV_PH_AR", // 1000 | ||
230 | "EV_PH_9", // 1001 | ||
231 | "EV_PH_ARL", // 1010 | ||
232 | "EV_PH_CVR", // 1011 | ||
233 | "EV_PH_AI8", // 1100 | ||
234 | "EV_PH_AI10", // 1101 | ||
235 | "EV_PH_AIL", // 1110 | ||
236 | "EV_PH_DC", // 1111 | ||
237 | "EV_PH_ACTIVATE_REQ", | ||
238 | "EV_PH_DEACTIVATE_REQ", | ||
239 | "EV_TIMER3", | ||
240 | }; | ||
241 | |||
242 | static inline void D_L1L2(struct isac *isac, int pr, void *arg) | ||
243 | { | ||
244 | struct hisax_if *ifc = (struct hisax_if *) &isac->hisax_d_if; | ||
245 | |||
246 | DBG(DBG_PR, "pr %#x", pr); | ||
247 | ifc->l1l2(ifc, pr, arg); | ||
248 | } | ||
249 | |||
250 | static void ph_command(struct isac *isac, unsigned int command) | ||
251 | { | ||
252 | DBG(DBG_L1M, "ph_command %#x", command); | ||
253 | switch (isac->type) { | ||
254 | case TYPE_ISAC: | ||
255 | isac->write_isac(isac, ISAC_CIX0, (command << 2) | 3); | ||
256 | break; | ||
257 | case TYPE_ISACSX: | ||
258 | isac->write_isac(isac, ISACSX_CIX0, (command << 4) | (7 << 1)); | ||
259 | break; | ||
260 | } | ||
261 | } | ||
262 | |||
263 | // ---------------------------------------------------------------------- | ||
264 | |||
265 | static void l1_di(struct FsmInst *fi, int event, void *arg) | ||
266 | { | ||
267 | struct isac *isac = fi->userdata; | ||
268 | |||
269 | FsmChangeState(fi, ST_L1_RESET); | ||
270 | ph_command(isac, ISAC_CMD_DI); | ||
271 | } | ||
272 | |||
273 | static void l1_di_deact_ind(struct FsmInst *fi, int event, void *arg) | ||
274 | { | ||
275 | struct isac *isac = fi->userdata; | ||
276 | |||
277 | FsmChangeState(fi, ST_L1_RESET); | ||
278 | D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL); | ||
279 | ph_command(isac, ISAC_CMD_DI); | ||
280 | } | ||
281 | |||
282 | static void l1_go_f3pdown(struct FsmInst *fi, int event, void *arg) | ||
283 | { | ||
284 | FsmChangeState(fi, ST_L1_F3_PDOWN); | ||
285 | } | ||
286 | |||
287 | static void l1_go_f3pend_deact_ind(struct FsmInst *fi, int event, void *arg) | ||
288 | { | ||
289 | struct isac *isac = fi->userdata; | ||
290 | |||
291 | FsmChangeState(fi, ST_L1_F3_PEND_DEACT); | ||
292 | D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL); | ||
293 | ph_command(isac, ISAC_CMD_DI); | ||
294 | } | ||
295 | |||
296 | static void l1_go_f3pend(struct FsmInst *fi, int event, void *arg) | ||
297 | { | ||
298 | struct isac *isac = fi->userdata; | ||
299 | |||
300 | FsmChangeState(fi, ST_L1_F3_PEND_DEACT); | ||
301 | ph_command(isac, ISAC_CMD_DI); | ||
302 | } | ||
303 | |||
304 | static void l1_go_f4(struct FsmInst *fi, int event, void *arg) | ||
305 | { | ||
306 | FsmChangeState(fi, ST_L1_F4); | ||
307 | } | ||
308 | |||
309 | static void l1_go_f5(struct FsmInst *fi, int event, void *arg) | ||
310 | { | ||
311 | FsmChangeState(fi, ST_L1_F5); | ||
312 | } | ||
313 | |||
314 | static void l1_go_f6(struct FsmInst *fi, int event, void *arg) | ||
315 | { | ||
316 | FsmChangeState(fi, ST_L1_F6); | ||
317 | } | ||
318 | |||
319 | static void l1_go_f6_deact_ind(struct FsmInst *fi, int event, void *arg) | ||
320 | { | ||
321 | struct isac *isac = fi->userdata; | ||
322 | |||
323 | FsmChangeState(fi, ST_L1_F6); | ||
324 | D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL); | ||
325 | } | ||
326 | |||
327 | static void l1_go_f7_act_ind(struct FsmInst *fi, int event, void *arg) | ||
328 | { | ||
329 | struct isac *isac = fi->userdata; | ||
330 | |||
331 | FsmDelTimer(&isac->timer, 0); | ||
332 | FsmChangeState(fi, ST_L1_F7); | ||
333 | ph_command(isac, ISAC_CMD_AR8); | ||
334 | D_L1L2(isac, PH_ACTIVATE | INDICATION, NULL); | ||
335 | } | ||
336 | |||
337 | static void l1_go_f8(struct FsmInst *fi, int event, void *arg) | ||
338 | { | ||
339 | FsmChangeState(fi, ST_L1_F8); | ||
340 | } | ||
341 | |||
342 | static void l1_go_f8_deact_ind(struct FsmInst *fi, int event, void *arg) | ||
343 | { | ||
344 | struct isac *isac = fi->userdata; | ||
345 | |||
346 | FsmChangeState(fi, ST_L1_F8); | ||
347 | D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL); | ||
348 | } | ||
349 | |||
350 | static void l1_ar8(struct FsmInst *fi, int event, void *arg) | ||
351 | { | ||
352 | struct isac *isac = fi->userdata; | ||
353 | |||
354 | FsmRestartTimer(&isac->timer, TIMER3_VALUE, EV_TIMER3, NULL, 2); | ||
355 | ph_command(isac, ISAC_CMD_AR8); | ||
356 | } | ||
357 | |||
358 | static void l1_timer3(struct FsmInst *fi, int event, void *arg) | ||
359 | { | ||
360 | struct isac *isac = fi->userdata; | ||
361 | |||
362 | ph_command(isac, ISAC_CMD_DI); | ||
363 | D_L1L2(isac, PH_DEACTIVATE | INDICATION, NULL); | ||
364 | } | ||
365 | |||
366 | // state machines according to data sheet PSB 2186 / 3186 | ||
367 | |||
368 | static struct FsmNode L1FnList[] __initdata = | ||
369 | { | ||
370 | {ST_L1_RESET, EV_PH_RES, l1_di}, | ||
371 | {ST_L1_RESET, EV_PH_EI, l1_di}, | ||
372 | {ST_L1_RESET, EV_PH_DC, l1_go_f3pdown}, | ||
373 | {ST_L1_RESET, EV_PH_AR, l1_go_f6}, | ||
374 | {ST_L1_RESET, EV_PH_AI8, l1_go_f7_act_ind}, | ||
375 | |||
376 | {ST_L1_F3_PDOWN, EV_PH_RES, l1_di}, | ||
377 | {ST_L1_F3_PDOWN, EV_PH_EI, l1_di}, | ||
378 | {ST_L1_F3_PDOWN, EV_PH_AR, l1_go_f6}, | ||
379 | {ST_L1_F3_PDOWN, EV_PH_RSY, l1_go_f5}, | ||
380 | {ST_L1_F3_PDOWN, EV_PH_PU, l1_go_f4}, | ||
381 | {ST_L1_F3_PDOWN, EV_PH_AI8, l1_go_f7_act_ind}, | ||
382 | {ST_L1_F3_PDOWN, EV_PH_ACTIVATE_REQ, l1_ar8}, | ||
383 | {ST_L1_F3_PDOWN, EV_TIMER3, l1_timer3}, | ||
384 | |||
385 | {ST_L1_F3_PEND_DEACT, EV_PH_RES, l1_di}, | ||
386 | {ST_L1_F3_PEND_DEACT, EV_PH_EI, l1_di}, | ||
387 | {ST_L1_F3_PEND_DEACT, EV_PH_DC, l1_go_f3pdown}, | ||
388 | {ST_L1_F3_PEND_DEACT, EV_PH_RSY, l1_go_f5}, | ||
389 | {ST_L1_F3_PEND_DEACT, EV_PH_AR, l1_go_f6}, | ||
390 | {ST_L1_F3_PEND_DEACT, EV_PH_AI8, l1_go_f7_act_ind}, | ||
391 | |||
392 | {ST_L1_F4, EV_PH_RES, l1_di}, | ||
393 | {ST_L1_F4, EV_PH_EI, l1_di}, | ||
394 | {ST_L1_F4, EV_PH_RSY, l1_go_f5}, | ||
395 | {ST_L1_F4, EV_PH_AI8, l1_go_f7_act_ind}, | ||
396 | {ST_L1_F4, EV_TIMER3, l1_timer3}, | ||
397 | {ST_L1_F4, EV_PH_DC, l1_go_f3pdown}, | ||
398 | |||
399 | {ST_L1_F5, EV_PH_RES, l1_di}, | ||
400 | {ST_L1_F5, EV_PH_EI, l1_di}, | ||
401 | {ST_L1_F5, EV_PH_AR, l1_go_f6}, | ||
402 | {ST_L1_F5, EV_PH_AI8, l1_go_f7_act_ind}, | ||
403 | {ST_L1_F5, EV_TIMER3, l1_timer3}, | ||
404 | {ST_L1_F5, EV_PH_DR, l1_go_f3pend}, | ||
405 | {ST_L1_F5, EV_PH_DC, l1_go_f3pdown}, | ||
406 | |||
407 | {ST_L1_F6, EV_PH_RES, l1_di}, | ||
408 | {ST_L1_F6, EV_PH_EI, l1_di}, | ||
409 | {ST_L1_F6, EV_PH_RSY, l1_go_f8}, | ||
410 | {ST_L1_F6, EV_PH_AI8, l1_go_f7_act_ind}, | ||
411 | {ST_L1_F6, EV_PH_DR6, l1_go_f3pend}, | ||
412 | {ST_L1_F6, EV_TIMER3, l1_timer3}, | ||
413 | {ST_L1_F6, EV_PH_DC, l1_go_f3pdown}, | ||
414 | |||
415 | {ST_L1_F7, EV_PH_RES, l1_di_deact_ind}, | ||
416 | {ST_L1_F7, EV_PH_EI, l1_di_deact_ind}, | ||
417 | {ST_L1_F7, EV_PH_AR, l1_go_f6_deact_ind}, | ||
418 | {ST_L1_F7, EV_PH_RSY, l1_go_f8_deact_ind}, | ||
419 | {ST_L1_F7, EV_PH_DR, l1_go_f3pend_deact_ind}, | ||
420 | |||
421 | {ST_L1_F8, EV_PH_RES, l1_di}, | ||
422 | {ST_L1_F8, EV_PH_EI, l1_di}, | ||
423 | {ST_L1_F8, EV_PH_AR, l1_go_f6}, | ||
424 | {ST_L1_F8, EV_PH_DR, l1_go_f3pend}, | ||
425 | {ST_L1_F8, EV_PH_AI8, l1_go_f7_act_ind}, | ||
426 | {ST_L1_F8, EV_TIMER3, l1_timer3}, | ||
427 | {ST_L1_F8, EV_PH_DC, l1_go_f3pdown}, | ||
428 | }; | ||
429 | |||
430 | static void l1m_debug(struct FsmInst *fi, char *fmt, ...) | ||
431 | { | ||
432 | va_list args; | ||
433 | char buf[256]; | ||
434 | |||
435 | va_start(args, fmt); | ||
436 | vsprintf(buf, fmt, args); | ||
437 | DBG(DBG_L1M, "%s", buf); | ||
438 | va_end(args); | ||
439 | } | ||
440 | |||
441 | static void isac_version(struct isac *cs) | ||
442 | { | ||
443 | int val; | ||
444 | |||
445 | val = cs->read_isac(cs, ISAC_RBCH); | ||
446 | DBG(1, "ISAC version (%x): %s", val, ISACVer[(val >> 5) & 3]); | ||
447 | } | ||
448 | |||
449 | static void isac_empty_fifo(struct isac *isac, int count) | ||
450 | { | ||
451 | // this also works for isacsx, since | ||
452 | // CMDR(D) register works the same | ||
453 | u_char *ptr; | ||
454 | |||
455 | DBG(DBG_IRQ, "count %d", count); | ||
456 | |||
457 | if ((isac->rcvidx + count) >= MAX_DFRAME_LEN_L1) { | ||
458 | DBG(DBG_WARN, "overrun %d", isac->rcvidx + count); | ||
459 | isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_RMC); | ||
460 | isac->rcvidx = 0; | ||
461 | return; | ||
462 | } | ||
463 | ptr = isac->rcvbuf + isac->rcvidx; | ||
464 | isac->rcvidx += count; | ||
465 | isac->read_isac_fifo(isac, ptr, count); | ||
466 | isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_RMC); | ||
467 | DBG_PACKET(DBG_RFIFO, ptr, count); | ||
468 | } | ||
469 | |||
470 | static void isac_fill_fifo(struct isac *isac) | ||
471 | { | ||
472 | // this also works for isacsx, since | ||
473 | // CMDR(D) register works the same | ||
474 | |||
475 | int count; | ||
476 | unsigned char cmd; | ||
477 | u_char *ptr; | ||
478 | |||
479 | if (!isac->tx_skb) | ||
480 | BUG(); | ||
481 | |||
482 | count = isac->tx_skb->len; | ||
483 | if (count <= 0) | ||
484 | BUG(); | ||
485 | |||
486 | DBG(DBG_IRQ, "count %d", count); | ||
487 | |||
488 | if (count > 0x20) { | ||
489 | count = 0x20; | ||
490 | cmd = ISAC_CMDR_XTF; | ||
491 | } else { | ||
492 | cmd = ISAC_CMDR_XTF | ISAC_CMDR_XME; | ||
493 | } | ||
494 | |||
495 | ptr = isac->tx_skb->data; | ||
496 | skb_pull(isac->tx_skb, count); | ||
497 | isac->tx_cnt += count; | ||
498 | DBG_PACKET(DBG_XFIFO, ptr, count); | ||
499 | isac->write_isac_fifo(isac, ptr, count); | ||
500 | isac->write_isac(isac, ISAC_CMDR, cmd); | ||
501 | } | ||
502 | |||
503 | static void isac_retransmit(struct isac *isac) | ||
504 | { | ||
505 | if (!isac->tx_skb) { | ||
506 | DBG(DBG_WARN, "no skb"); | ||
507 | return; | ||
508 | } | ||
509 | skb_push(isac->tx_skb, isac->tx_cnt); | ||
510 | isac->tx_cnt = 0; | ||
511 | } | ||
512 | |||
513 | |||
514 | static inline void isac_cisq_interrupt(struct isac *isac) | ||
515 | { | ||
516 | unsigned char val; | ||
517 | |||
518 | val = isac->read_isac(isac, ISAC_CIR0); | ||
519 | DBG(DBG_IRQ, "CIR0 %#x", val); | ||
520 | if (val & ISAC_CIR0_CIC0) { | ||
521 | DBG(DBG_IRQ, "CODR0 %#x", (val >> 2) & 0xf); | ||
522 | FsmEvent(&isac->l1m, (val >> 2) & 0xf, NULL); | ||
523 | } | ||
524 | if (val & ISAC_CIR0_CIC1) { | ||
525 | val = isac->read_isac(isac, ISAC_CIR1); | ||
526 | DBG(DBG_WARN, "ISAC CIR1 %#x", val ); | ||
527 | } | ||
528 | } | ||
529 | |||
530 | static inline void isac_rme_interrupt(struct isac *isac) | ||
531 | { | ||
532 | unsigned char val; | ||
533 | int count; | ||
534 | struct sk_buff *skb; | ||
535 | |||
536 | val = isac->read_isac(isac, ISAC_RSTA); | ||
537 | if ((val & (ISAC_RSTA_RDO | ISAC_RSTA_CRC | ISAC_RSTA_RAB) ) | ||
538 | != ISAC_RSTA_CRC) { | ||
539 | DBG(DBG_WARN, "RSTA %#x, dropped", val); | ||
540 | isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_RMC); | ||
541 | goto out; | ||
542 | } | ||
543 | |||
544 | count = isac->read_isac(isac, ISAC_RBCL) & 0x1f; | ||
545 | DBG(DBG_IRQ, "RBCL %#x", count); | ||
546 | if (count == 0) | ||
547 | count = 0x20; | ||
548 | |||
549 | isac_empty_fifo(isac, count); | ||
550 | count = isac->rcvidx; | ||
551 | if (count < 1) { | ||
552 | DBG(DBG_WARN, "count %d < 1", count); | ||
553 | goto out; | ||
554 | } | ||
555 | |||
556 | skb = alloc_skb(count, GFP_ATOMIC); | ||
557 | if (!skb) { | ||
558 | DBG(DBG_WARN, "no memory, dropping\n"); | ||
559 | goto out; | ||
560 | } | ||
561 | memcpy(skb_put(skb, count), isac->rcvbuf, count); | ||
562 | DBG_SKB(DBG_RPACKET, skb); | ||
563 | D_L1L2(isac, PH_DATA | INDICATION, skb); | ||
564 | out: | ||
565 | isac->rcvidx = 0; | ||
566 | } | ||
567 | |||
568 | static inline void isac_xpr_interrupt(struct isac *isac) | ||
569 | { | ||
570 | if (!isac->tx_skb) | ||
571 | return; | ||
572 | |||
573 | if (isac->tx_skb->len > 0) { | ||
574 | isac_fill_fifo(isac); | ||
575 | return; | ||
576 | } | ||
577 | dev_kfree_skb_irq(isac->tx_skb); | ||
578 | isac->tx_cnt = 0; | ||
579 | isac->tx_skb = NULL; | ||
580 | D_L1L2(isac, PH_DATA | CONFIRM, NULL); | ||
581 | } | ||
582 | |||
583 | static inline void isac_exi_interrupt(struct isac *isac) | ||
584 | { | ||
585 | unsigned char val; | ||
586 | |||
587 | val = isac->read_isac(isac, ISAC_EXIR); | ||
588 | DBG(2, "EXIR %#x", val); | ||
589 | |||
590 | if (val & ISAC_EXIR_XMR) { | ||
591 | DBG(DBG_WARN, "ISAC XMR"); | ||
592 | isac_retransmit(isac); | ||
593 | } | ||
594 | if (val & ISAC_EXIR_XDU) { | ||
595 | DBG(DBG_WARN, "ISAC XDU"); | ||
596 | isac_retransmit(isac); | ||
597 | } | ||
598 | if (val & ISAC_EXIR_MOS) { /* MOS */ | ||
599 | DBG(DBG_WARN, "MOS"); | ||
600 | val = isac->read_isac(isac, ISAC_MOSR); | ||
601 | DBG(2, "ISAC MOSR %#x", val); | ||
602 | } | ||
603 | } | ||
604 | |||
605 | void isac_irq(struct isac *isac) | ||
606 | { | ||
607 | unsigned char val; | ||
608 | |||
609 | val = isac->read_isac(isac, ISAC_ISTA); | ||
610 | DBG(DBG_IRQ, "ISTA %#x", val); | ||
611 | |||
612 | if (val & ISAC_ISTA_EXI) { | ||
613 | DBG(DBG_IRQ, "EXI"); | ||
614 | isac_exi_interrupt(isac); | ||
615 | } | ||
616 | if (val & ISAC_ISTA_XPR) { | ||
617 | DBG(DBG_IRQ, "XPR"); | ||
618 | isac_xpr_interrupt(isac); | ||
619 | } | ||
620 | if (val & ISAC_ISTA_RME) { | ||
621 | DBG(DBG_IRQ, "RME"); | ||
622 | isac_rme_interrupt(isac); | ||
623 | } | ||
624 | if (val & ISAC_ISTA_RPF) { | ||
625 | DBG(DBG_IRQ, "RPF"); | ||
626 | isac_empty_fifo(isac, 0x20); | ||
627 | } | ||
628 | if (val & ISAC_ISTA_CISQ) { | ||
629 | DBG(DBG_IRQ, "CISQ"); | ||
630 | isac_cisq_interrupt(isac); | ||
631 | } | ||
632 | if (val & ISAC_ISTA_RSC) { | ||
633 | DBG(DBG_WARN, "RSC"); | ||
634 | } | ||
635 | if (val & ISAC_ISTA_SIN) { | ||
636 | DBG(DBG_WARN, "SIN"); | ||
637 | } | ||
638 | isac->write_isac(isac, ISAC_MASK, 0xff); | ||
639 | isac->write_isac(isac, ISAC_MASK, 0x00); | ||
640 | } | ||
641 | |||
642 | // ====================================================================== | ||
643 | |||
644 | static inline void isacsx_cic_interrupt(struct isac *isac) | ||
645 | { | ||
646 | unsigned char val; | ||
647 | |||
648 | val = isac->read_isac(isac, ISACSX_CIR0); | ||
649 | DBG(DBG_IRQ, "CIR0 %#x", val); | ||
650 | if (val & ISACSX_CIR0_CIC0) { | ||
651 | DBG(DBG_IRQ, "CODR0 %#x", val >> 4); | ||
652 | FsmEvent(&isac->l1m, val >> 4, NULL); | ||
653 | } | ||
654 | } | ||
655 | |||
656 | static inline void isacsx_rme_interrupt(struct isac *isac) | ||
657 | { | ||
658 | int count; | ||
659 | struct sk_buff *skb; | ||
660 | unsigned char val; | ||
661 | |||
662 | val = isac->read_isac(isac, ISACSX_RSTAD); | ||
663 | if ((val & (ISACSX_RSTAD_VFR | | ||
664 | ISACSX_RSTAD_RDO | | ||
665 | ISACSX_RSTAD_CRC | | ||
666 | ISACSX_RSTAD_RAB)) | ||
667 | != (ISACSX_RSTAD_VFR | ISACSX_RSTAD_CRC)) { | ||
668 | DBG(DBG_WARN, "RSTAD %#x, dropped", val); | ||
669 | isac->write_isac(isac, ISACSX_CMDRD, ISACSX_CMDRD_RMC); | ||
670 | goto out; | ||
671 | } | ||
672 | |||
673 | count = isac->read_isac(isac, ISACSX_RBCLD) & 0x1f; | ||
674 | DBG(DBG_IRQ, "RBCLD %#x", count); | ||
675 | if (count == 0) | ||
676 | count = 0x20; | ||
677 | |||
678 | isac_empty_fifo(isac, count); | ||
679 | // strip trailing status byte | ||
680 | count = isac->rcvidx - 1; | ||
681 | if (count < 1) { | ||
682 | DBG(DBG_WARN, "count %d < 1", count); | ||
683 | goto out; | ||
684 | } | ||
685 | |||
686 | skb = dev_alloc_skb(count); | ||
687 | if (!skb) { | ||
688 | DBG(DBG_WARN, "no memory, dropping"); | ||
689 | goto out; | ||
690 | } | ||
691 | memcpy(skb_put(skb, count), isac->rcvbuf, count); | ||
692 | DBG_SKB(DBG_RPACKET, skb); | ||
693 | D_L1L2(isac, PH_DATA | INDICATION, skb); | ||
694 | out: | ||
695 | isac->rcvidx = 0; | ||
696 | } | ||
697 | |||
698 | static inline void isacsx_xpr_interrupt(struct isac *isac) | ||
699 | { | ||
700 | if (!isac->tx_skb) | ||
701 | return; | ||
702 | |||
703 | if (isac->tx_skb->len > 0) { | ||
704 | isac_fill_fifo(isac); | ||
705 | return; | ||
706 | } | ||
707 | dev_kfree_skb_irq(isac->tx_skb); | ||
708 | isac->tx_skb = NULL; | ||
709 | isac->tx_cnt = 0; | ||
710 | D_L1L2(isac, PH_DATA | CONFIRM, NULL); | ||
711 | } | ||
712 | |||
713 | static inline void isacsx_icd_interrupt(struct isac *isac) | ||
714 | { | ||
715 | unsigned char val; | ||
716 | |||
717 | val = isac->read_isac(isac, ISACSX_ISTAD); | ||
718 | DBG(DBG_IRQ, "ISTAD %#x", val); | ||
719 | if (val & ISACSX_ISTAD_XDU) { | ||
720 | DBG(DBG_WARN, "ISTAD XDU"); | ||
721 | isac_retransmit(isac); | ||
722 | } | ||
723 | if (val & ISACSX_ISTAD_XMR) { | ||
724 | DBG(DBG_WARN, "ISTAD XMR"); | ||
725 | isac_retransmit(isac); | ||
726 | } | ||
727 | if (val & ISACSX_ISTAD_XPR) { | ||
728 | DBG(DBG_IRQ, "ISTAD XPR"); | ||
729 | isacsx_xpr_interrupt(isac); | ||
730 | } | ||
731 | if (val & ISACSX_ISTAD_RFO) { | ||
732 | DBG(DBG_WARN, "ISTAD RFO"); | ||
733 | isac->write_isac(isac, ISACSX_CMDRD, ISACSX_CMDRD_RMC); | ||
734 | } | ||
735 | if (val & ISACSX_ISTAD_RME) { | ||
736 | DBG(DBG_IRQ, "ISTAD RME"); | ||
737 | isacsx_rme_interrupt(isac); | ||
738 | } | ||
739 | if (val & ISACSX_ISTAD_RPF) { | ||
740 | DBG(DBG_IRQ, "ISTAD RPF"); | ||
741 | isac_empty_fifo(isac, 0x20); | ||
742 | } | ||
743 | } | ||
744 | |||
745 | void isacsx_irq(struct isac *isac) | ||
746 | { | ||
747 | unsigned char val; | ||
748 | |||
749 | val = isac->read_isac(isac, ISACSX_ISTA); | ||
750 | DBG(DBG_IRQ, "ISTA %#x", val); | ||
751 | |||
752 | if (val & ISACSX_ISTA_ICD) | ||
753 | isacsx_icd_interrupt(isac); | ||
754 | if (val & ISACSX_ISTA_CIC) | ||
755 | isacsx_cic_interrupt(isac); | ||
756 | } | ||
757 | |||
758 | void isac_init(struct isac *isac) | ||
759 | { | ||
760 | isac->tx_skb = NULL; | ||
761 | isac->l1m.fsm = &l1fsm; | ||
762 | isac->l1m.state = ST_L1_RESET; | ||
763 | #ifdef CONFIG_HISAX_DEBUG | ||
764 | isac->l1m.debug = 1; | ||
765 | #else | ||
766 | isac->l1m.debug = 0; | ||
767 | #endif | ||
768 | isac->l1m.userdata = isac; | ||
769 | isac->l1m.printdebug = l1m_debug; | ||
770 | FsmInitTimer(&isac->l1m, &isac->timer); | ||
771 | } | ||
772 | |||
773 | void isac_setup(struct isac *isac) | ||
774 | { | ||
775 | int val, eval; | ||
776 | |||
777 | isac->type = TYPE_ISAC; | ||
778 | isac_version(isac); | ||
779 | |||
780 | ph_command(isac, ISAC_CMD_RES); | ||
781 | |||
782 | isac->write_isac(isac, ISAC_MASK, 0xff); | ||
783 | isac->mocr = 0xaa; | ||
784 | if (test_bit(ISAC_IOM1, &isac->flags)) { | ||
785 | /* IOM 1 Mode */ | ||
786 | isac->write_isac(isac, ISAC_ADF2, 0x0); | ||
787 | isac->write_isac(isac, ISAC_SPCR, 0xa); | ||
788 | isac->write_isac(isac, ISAC_ADF1, 0x2); | ||
789 | isac->write_isac(isac, ISAC_STCR, 0x70); | ||
790 | isac->write_isac(isac, ISAC_MODE, 0xc9); | ||
791 | } else { | ||
792 | /* IOM 2 Mode */ | ||
793 | if (!isac->adf2) | ||
794 | isac->adf2 = 0x80; | ||
795 | isac->write_isac(isac, ISAC_ADF2, isac->adf2); | ||
796 | isac->write_isac(isac, ISAC_SQXR, 0x2f); | ||
797 | isac->write_isac(isac, ISAC_SPCR, 0x00); | ||
798 | isac->write_isac(isac, ISAC_STCR, 0x70); | ||
799 | isac->write_isac(isac, ISAC_MODE, 0xc9); | ||
800 | isac->write_isac(isac, ISAC_TIMR, 0x00); | ||
801 | isac->write_isac(isac, ISAC_ADF1, 0x00); | ||
802 | } | ||
803 | val = isac->read_isac(isac, ISAC_STAR); | ||
804 | DBG(2, "ISAC STAR %x", val); | ||
805 | val = isac->read_isac(isac, ISAC_MODE); | ||
806 | DBG(2, "ISAC MODE %x", val); | ||
807 | val = isac->read_isac(isac, ISAC_ADF2); | ||
808 | DBG(2, "ISAC ADF2 %x", val); | ||
809 | val = isac->read_isac(isac, ISAC_ISTA); | ||
810 | DBG(2, "ISAC ISTA %x", val); | ||
811 | if (val & 0x01) { | ||
812 | eval = isac->read_isac(isac, ISAC_EXIR); | ||
813 | DBG(2, "ISAC EXIR %x", eval); | ||
814 | } | ||
815 | val = isac->read_isac(isac, ISAC_CIR0); | ||
816 | DBG(2, "ISAC CIR0 %x", val); | ||
817 | FsmEvent(&isac->l1m, (val >> 2) & 0xf, NULL); | ||
818 | |||
819 | isac->write_isac(isac, ISAC_MASK, 0x0); | ||
820 | // RESET Receiver and Transmitter | ||
821 | isac->write_isac(isac, ISAC_CMDR, ISAC_CMDR_XRES | ISAC_CMDR_RRES); | ||
822 | } | ||
823 | |||
824 | void isacsx_setup(struct isac *isac) | ||
825 | { | ||
826 | isac->type = TYPE_ISACSX; | ||
827 | // clear LDD | ||
828 | isac->write_isac(isac, ISACSX_TR_CONF0, 0x00); | ||
829 | // enable transmitter | ||
830 | isac->write_isac(isac, ISACSX_TR_CONF2, 0x00); | ||
831 | // transparent mode 0, RAC, stop/go | ||
832 | isac->write_isac(isac, ISACSX_MODED, 0xc9); | ||
833 | // all HDLC IRQ unmasked | ||
834 | isac->write_isac(isac, ISACSX_MASKD, 0x03); | ||
835 | // unmask ICD, CID IRQs | ||
836 | isac->write_isac(isac, ISACSX_MASK, | ||
837 | ~(ISACSX_ISTA_ICD | ISACSX_ISTA_CIC)); | ||
838 | } | ||
839 | |||
840 | void isac_d_l2l1(struct hisax_if *hisax_d_if, int pr, void *arg) | ||
841 | { | ||
842 | struct isac *isac = hisax_d_if->priv; | ||
843 | struct sk_buff *skb = arg; | ||
844 | |||
845 | DBG(DBG_PR, "pr %#x", pr); | ||
846 | |||
847 | switch (pr) { | ||
848 | case PH_ACTIVATE | REQUEST: | ||
849 | FsmEvent(&isac->l1m, EV_PH_ACTIVATE_REQ, NULL); | ||
850 | break; | ||
851 | case PH_DEACTIVATE | REQUEST: | ||
852 | FsmEvent(&isac->l1m, EV_PH_DEACTIVATE_REQ, NULL); | ||
853 | break; | ||
854 | case PH_DATA | REQUEST: | ||
855 | DBG(DBG_PR, "PH_DATA REQUEST len %d", skb->len); | ||
856 | DBG_SKB(DBG_XPACKET, skb); | ||
857 | if (isac->l1m.state != ST_L1_F7) { | ||
858 | DBG(1, "L1 wrong state %d\n", isac->l1m.state); | ||
859 | dev_kfree_skb(skb); | ||
860 | break; | ||
861 | } | ||
862 | if (isac->tx_skb) | ||
863 | BUG(); | ||
864 | |||
865 | isac->tx_skb = skb; | ||
866 | isac_fill_fifo(isac); | ||
867 | break; | ||
868 | } | ||
869 | } | ||
870 | |||
871 | static int __init hisax_isac_init(void) | ||
872 | { | ||
873 | printk(KERN_INFO "hisax_isac: ISAC-S/ISAC-SX ISDN driver v0.1.0\n"); | ||
874 | |||
875 | l1fsm.state_count = L1_STATE_COUNT; | ||
876 | l1fsm.event_count = L1_EVENT_COUNT; | ||
877 | l1fsm.strState = strL1State; | ||
878 | l1fsm.strEvent = strL1Event; | ||
879 | return FsmNew(&l1fsm, L1FnList, ARRAY_SIZE(L1FnList)); | ||
880 | } | ||
881 | |||
882 | static void __exit hisax_isac_exit(void) | ||
883 | { | ||
884 | FsmFree(&l1fsm); | ||
885 | } | ||
886 | |||
887 | EXPORT_SYMBOL(isac_init); | ||
888 | EXPORT_SYMBOL(isac_d_l2l1); | ||
889 | |||
890 | EXPORT_SYMBOL(isacsx_setup); | ||
891 | EXPORT_SYMBOL(isacsx_irq); | ||
892 | |||
893 | EXPORT_SYMBOL(isac_setup); | ||
894 | EXPORT_SYMBOL(isac_irq); | ||
895 | |||
896 | module_init(hisax_isac_init); | ||
897 | module_exit(hisax_isac_exit); | ||