diff options
author | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-04-16 18:20:36 -0400 |
commit | 1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch) | |
tree | 0bba044c4ce775e45a88a51686b5d9f90697ea9d /drivers/isdn/act2000/act2000_isa.c |
Linux-2.6.12-rc2
Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.
Let it rip!
Diffstat (limited to 'drivers/isdn/act2000/act2000_isa.c')
-rw-r--r-- | drivers/isdn/act2000/act2000_isa.c | 449 |
1 files changed, 449 insertions, 0 deletions
diff --git a/drivers/isdn/act2000/act2000_isa.c b/drivers/isdn/act2000/act2000_isa.c new file mode 100644 index 00000000000..bc98d77c5ec --- /dev/null +++ b/drivers/isdn/act2000/act2000_isa.c | |||
@@ -0,0 +1,449 @@ | |||
1 | /* $Id: act2000_isa.c,v 1.11.6.3 2001/09/23 22:24:32 kai Exp $ | ||
2 | * | ||
3 | * ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version). | ||
4 | * | ||
5 | * Author Fritz Elfert | ||
6 | * Copyright by Fritz Elfert <fritz@isdn4linux.de> | ||
7 | * | ||
8 | * This software may be used and distributed according to the terms | ||
9 | * of the GNU General Public License, incorporated herein by reference. | ||
10 | * | ||
11 | * Thanks to Friedemann Baitinger and IBM Germany | ||
12 | * | ||
13 | */ | ||
14 | |||
15 | #include "act2000.h" | ||
16 | #include "act2000_isa.h" | ||
17 | #include "capi.h" | ||
18 | |||
19 | static act2000_card *irq2card_map[16]; | ||
20 | |||
21 | /* | ||
22 | * Reset Controller, then try to read the Card's signature. | ||
23 | + Return: | ||
24 | * 1 = Signature found. | ||
25 | * 0 = Signature not found. | ||
26 | */ | ||
27 | static int | ||
28 | act2000_isa_reset(unsigned short portbase) | ||
29 | { | ||
30 | unsigned char reg; | ||
31 | int i; | ||
32 | int found; | ||
33 | int serial = 0; | ||
34 | |||
35 | found = 0; | ||
36 | if ((reg = inb(portbase + ISA_COR)) != 0xff) { | ||
37 | outb(reg | ISA_COR_RESET, portbase + ISA_COR); | ||
38 | mdelay(10); | ||
39 | outb(reg, portbase + ISA_COR); | ||
40 | mdelay(10); | ||
41 | |||
42 | for (i = 0; i < 16; i++) { | ||
43 | if (inb(portbase + ISA_ISR) & ISA_ISR_SERIAL) | ||
44 | serial |= 0x10000; | ||
45 | serial >>= 1; | ||
46 | } | ||
47 | if (serial == ISA_SER_ID) | ||
48 | found++; | ||
49 | } | ||
50 | return found; | ||
51 | } | ||
52 | |||
53 | int | ||
54 | act2000_isa_detect(unsigned short portbase) | ||
55 | { | ||
56 | int ret = 0; | ||
57 | |||
58 | if (request_region(portbase, ACT2000_PORTLEN, "act2000isa")) { | ||
59 | ret = act2000_isa_reset(portbase); | ||
60 | release_region(portbase, ISA_REGION); | ||
61 | } | ||
62 | return ret; | ||
63 | } | ||
64 | |||
65 | static irqreturn_t | ||
66 | act2000_isa_interrupt(int irq, void *dev_id, struct pt_regs *regs) | ||
67 | { | ||
68 | act2000_card *card = irq2card_map[irq]; | ||
69 | u_char istatus; | ||
70 | |||
71 | if (!card) { | ||
72 | printk(KERN_WARNING | ||
73 | "act2000: Spurious interrupt!\n"); | ||
74 | return IRQ_NONE; | ||
75 | } | ||
76 | istatus = (inb(ISA_PORT_ISR) & 0x07); | ||
77 | if (istatus & ISA_ISR_OUT) { | ||
78 | /* RX fifo has data */ | ||
79 | istatus &= ISA_ISR_OUT_MASK; | ||
80 | outb(0, ISA_PORT_SIS); | ||
81 | act2000_isa_receive(card); | ||
82 | outb(ISA_SIS_INT, ISA_PORT_SIS); | ||
83 | } | ||
84 | if (istatus & ISA_ISR_ERR) { | ||
85 | /* Error Interrupt */ | ||
86 | istatus &= ISA_ISR_ERR_MASK; | ||
87 | printk(KERN_WARNING "act2000: errIRQ\n"); | ||
88 | } | ||
89 | if (istatus) | ||
90 | printk(KERN_DEBUG "act2000: ?IRQ %d %02x\n", irq, istatus); | ||
91 | return IRQ_HANDLED; | ||
92 | } | ||
93 | |||
94 | static void | ||
95 | act2000_isa_select_irq(act2000_card * card) | ||
96 | { | ||
97 | unsigned char reg; | ||
98 | |||
99 | reg = (inb(ISA_PORT_COR) & ~ISA_COR_IRQOFF) | ISA_COR_PERR; | ||
100 | switch (card->irq) { | ||
101 | case 3: | ||
102 | reg = ISA_COR_IRQ03; | ||
103 | break; | ||
104 | case 5: | ||
105 | reg = ISA_COR_IRQ05; | ||
106 | break; | ||
107 | case 7: | ||
108 | reg = ISA_COR_IRQ07; | ||
109 | break; | ||
110 | case 10: | ||
111 | reg = ISA_COR_IRQ10; | ||
112 | break; | ||
113 | case 11: | ||
114 | reg = ISA_COR_IRQ11; | ||
115 | break; | ||
116 | case 12: | ||
117 | reg = ISA_COR_IRQ12; | ||
118 | break; | ||
119 | case 15: | ||
120 | reg = ISA_COR_IRQ15; | ||
121 | break; | ||
122 | } | ||
123 | outb(reg, ISA_PORT_COR); | ||
124 | } | ||
125 | |||
126 | static void | ||
127 | act2000_isa_enable_irq(act2000_card * card) | ||
128 | { | ||
129 | act2000_isa_select_irq(card); | ||
130 | /* Enable READ irq */ | ||
131 | outb(ISA_SIS_INT, ISA_PORT_SIS); | ||
132 | } | ||
133 | |||
134 | /* | ||
135 | * Install interrupt handler, enable irq on card. | ||
136 | * If irq is -1, choose next free irq, else irq is given explicitely. | ||
137 | */ | ||
138 | int | ||
139 | act2000_isa_config_irq(act2000_card * card, short irq) | ||
140 | { | ||
141 | if (card->flags & ACT2000_FLAGS_IVALID) { | ||
142 | free_irq(card->irq, NULL); | ||
143 | irq2card_map[card->irq] = NULL; | ||
144 | } | ||
145 | card->flags &= ~ACT2000_FLAGS_IVALID; | ||
146 | outb(ISA_COR_IRQOFF, ISA_PORT_COR); | ||
147 | if (!irq) | ||
148 | return 0; | ||
149 | |||
150 | if (!request_irq(irq, &act2000_isa_interrupt, 0, card->regname, NULL)) { | ||
151 | card->irq = irq; | ||
152 | irq2card_map[card->irq] = card; | ||
153 | card->flags |= ACT2000_FLAGS_IVALID; | ||
154 | printk(KERN_WARNING | ||
155 | "act2000: Could not request irq %d\n",irq); | ||
156 | return -EBUSY; | ||
157 | } else { | ||
158 | act2000_isa_select_irq(card); | ||
159 | /* Disable READ and WRITE irq */ | ||
160 | outb(0, ISA_PORT_SIS); | ||
161 | outb(0, ISA_PORT_SOS); | ||
162 | } | ||
163 | return 0; | ||
164 | } | ||
165 | |||
166 | int | ||
167 | act2000_isa_config_port(act2000_card * card, unsigned short portbase) | ||
168 | { | ||
169 | if (card->flags & ACT2000_FLAGS_PVALID) { | ||
170 | release_region(card->port, ISA_REGION); | ||
171 | card->flags &= ~ACT2000_FLAGS_PVALID; | ||
172 | } | ||
173 | if (request_region(portbase, ACT2000_PORTLEN, card->regname) == NULL) | ||
174 | return -EBUSY; | ||
175 | else { | ||
176 | card->port = portbase; | ||
177 | card->flags |= ACT2000_FLAGS_PVALID; | ||
178 | return 0; | ||
179 | } | ||
180 | } | ||
181 | |||
182 | /* | ||
183 | * Release ressources, used by an adaptor. | ||
184 | */ | ||
185 | void | ||
186 | act2000_isa_release(act2000_card * card) | ||
187 | { | ||
188 | unsigned long flags; | ||
189 | |||
190 | spin_lock_irqsave(&card->lock, flags); | ||
191 | if (card->flags & ACT2000_FLAGS_IVALID) { | ||
192 | free_irq(card->irq, NULL); | ||
193 | irq2card_map[card->irq] = NULL; | ||
194 | } | ||
195 | card->flags &= ~ACT2000_FLAGS_IVALID; | ||
196 | if (card->flags & ACT2000_FLAGS_PVALID) | ||
197 | release_region(card->port, ISA_REGION); | ||
198 | card->flags &= ~ACT2000_FLAGS_PVALID; | ||
199 | spin_unlock_irqrestore(&card->lock, flags); | ||
200 | } | ||
201 | |||
202 | static int | ||
203 | act2000_isa_writeb(act2000_card * card, u_char data) | ||
204 | { | ||
205 | u_char timeout = 40; | ||
206 | |||
207 | while (timeout) { | ||
208 | if (inb(ISA_PORT_SOS) & ISA_SOS_READY) { | ||
209 | outb(data, ISA_PORT_SDO); | ||
210 | return 0; | ||
211 | } else { | ||
212 | timeout--; | ||
213 | udelay(10); | ||
214 | } | ||
215 | } | ||
216 | return 1; | ||
217 | } | ||
218 | |||
219 | static int | ||
220 | act2000_isa_readb(act2000_card * card, u_char * data) | ||
221 | { | ||
222 | u_char timeout = 40; | ||
223 | |||
224 | while (timeout) { | ||
225 | if (inb(ISA_PORT_SIS) & ISA_SIS_READY) { | ||
226 | *data = inb(ISA_PORT_SDI); | ||
227 | return 0; | ||
228 | } else { | ||
229 | timeout--; | ||
230 | udelay(10); | ||
231 | } | ||
232 | } | ||
233 | return 1; | ||
234 | } | ||
235 | |||
236 | void | ||
237 | act2000_isa_receive(act2000_card *card) | ||
238 | { | ||
239 | u_char c; | ||
240 | |||
241 | if (test_and_set_bit(ACT2000_LOCK_RX, (void *) &card->ilock) != 0) | ||
242 | return; | ||
243 | while (!act2000_isa_readb(card, &c)) { | ||
244 | if (card->idat.isa.rcvidx < 8) { | ||
245 | card->idat.isa.rcvhdr[card->idat.isa.rcvidx++] = c; | ||
246 | if (card->idat.isa.rcvidx == 8) { | ||
247 | int valid = actcapi_chkhdr(card, (actcapi_msghdr *)&card->idat.isa.rcvhdr); | ||
248 | |||
249 | if (valid) { | ||
250 | card->idat.isa.rcvlen = ((actcapi_msghdr *)&card->idat.isa.rcvhdr)->len; | ||
251 | card->idat.isa.rcvskb = dev_alloc_skb(card->idat.isa.rcvlen); | ||
252 | if (card->idat.isa.rcvskb == NULL) { | ||
253 | card->idat.isa.rcvignore = 1; | ||
254 | printk(KERN_WARNING | ||
255 | "act2000_isa_receive: no memory\n"); | ||
256 | test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock); | ||
257 | return; | ||
258 | } | ||
259 | memcpy(skb_put(card->idat.isa.rcvskb, 8), card->idat.isa.rcvhdr, 8); | ||
260 | card->idat.isa.rcvptr = skb_put(card->idat.isa.rcvskb, card->idat.isa.rcvlen - 8); | ||
261 | } else { | ||
262 | card->idat.isa.rcvidx = 0; | ||
263 | printk(KERN_WARNING | ||
264 | "act2000_isa_receive: Invalid CAPI msg\n"); | ||
265 | { | ||
266 | int i; __u8 *p; __u8 *c; __u8 tmp[30]; | ||
267 | for (i = 0, p = (__u8 *)&card->idat.isa.rcvhdr, c = tmp; i < 8; i++) | ||
268 | c += sprintf(c, "%02x ", *(p++)); | ||
269 | printk(KERN_WARNING "act2000_isa_receive: %s\n", tmp); | ||
270 | } | ||
271 | } | ||
272 | } | ||
273 | } else { | ||
274 | if (!card->idat.isa.rcvignore) | ||
275 | *card->idat.isa.rcvptr++ = c; | ||
276 | if (++card->idat.isa.rcvidx >= card->idat.isa.rcvlen) { | ||
277 | if (!card->idat.isa.rcvignore) { | ||
278 | skb_queue_tail(&card->rcvq, card->idat.isa.rcvskb); | ||
279 | act2000_schedule_rx(card); | ||
280 | } | ||
281 | card->idat.isa.rcvidx = 0; | ||
282 | card->idat.isa.rcvlen = 8; | ||
283 | card->idat.isa.rcvignore = 0; | ||
284 | card->idat.isa.rcvskb = NULL; | ||
285 | card->idat.isa.rcvptr = card->idat.isa.rcvhdr; | ||
286 | } | ||
287 | } | ||
288 | } | ||
289 | if (!(card->flags & ACT2000_FLAGS_IVALID)) { | ||
290 | /* In polling mode, schedule myself */ | ||
291 | if ((card->idat.isa.rcvidx) && | ||
292 | (card->idat.isa.rcvignore || | ||
293 | (card->idat.isa.rcvidx < card->idat.isa.rcvlen))) | ||
294 | act2000_schedule_poll(card); | ||
295 | } | ||
296 | test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock); | ||
297 | } | ||
298 | |||
299 | void | ||
300 | act2000_isa_send(act2000_card * card) | ||
301 | { | ||
302 | unsigned long flags; | ||
303 | struct sk_buff *skb; | ||
304 | actcapi_msg *msg; | ||
305 | int l; | ||
306 | |||
307 | if (test_and_set_bit(ACT2000_LOCK_TX, (void *) &card->ilock) != 0) | ||
308 | return; | ||
309 | while (1) { | ||
310 | spin_lock_irqsave(&card->lock, flags); | ||
311 | if (!(card->sbuf)) { | ||
312 | if ((card->sbuf = skb_dequeue(&card->sndq))) { | ||
313 | card->ack_msg = card->sbuf->data; | ||
314 | msg = (actcapi_msg *)card->sbuf->data; | ||
315 | if ((msg->hdr.cmd.cmd == 0x86) && | ||
316 | (msg->hdr.cmd.subcmd == 0) ) { | ||
317 | /* Save flags in message */ | ||
318 | card->need_b3ack = msg->msg.data_b3_req.flags; | ||
319 | msg->msg.data_b3_req.flags = 0; | ||
320 | } | ||
321 | } | ||
322 | } | ||
323 | spin_unlock_irqrestore(&card->lock, flags); | ||
324 | if (!(card->sbuf)) { | ||
325 | /* No more data to send */ | ||
326 | test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock); | ||
327 | return; | ||
328 | } | ||
329 | skb = card->sbuf; | ||
330 | l = 0; | ||
331 | while (skb->len) { | ||
332 | if (act2000_isa_writeb(card, *(skb->data))) { | ||
333 | /* Fifo is full, but more data to send */ | ||
334 | test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock); | ||
335 | /* Schedule myself */ | ||
336 | act2000_schedule_tx(card); | ||
337 | return; | ||
338 | } | ||
339 | skb_pull(skb, 1); | ||
340 | l++; | ||
341 | } | ||
342 | msg = (actcapi_msg *)card->ack_msg; | ||
343 | if ((msg->hdr.cmd.cmd == 0x86) && | ||
344 | (msg->hdr.cmd.subcmd == 0) ) { | ||
345 | /* | ||
346 | * If it's user data, reset data-ptr | ||
347 | * and put skb into ackq. | ||
348 | */ | ||
349 | skb->data = card->ack_msg; | ||
350 | /* Restore flags in message */ | ||
351 | msg->msg.data_b3_req.flags = card->need_b3ack; | ||
352 | skb_queue_tail(&card->ackq, skb); | ||
353 | } else | ||
354 | dev_kfree_skb(skb); | ||
355 | card->sbuf = NULL; | ||
356 | } | ||
357 | } | ||
358 | |||
359 | /* | ||
360 | * Get firmware ID, check for 'ISDN' signature. | ||
361 | */ | ||
362 | static int | ||
363 | act2000_isa_getid(act2000_card * card) | ||
364 | { | ||
365 | |||
366 | act2000_fwid fid; | ||
367 | u_char *p = (u_char *) & fid; | ||
368 | int count = 0; | ||
369 | |||
370 | while (1) { | ||
371 | if (count > 510) | ||
372 | return -EPROTO; | ||
373 | if (act2000_isa_readb(card, p++)) | ||
374 | break; | ||
375 | count++; | ||
376 | } | ||
377 | if (count <= 20) { | ||
378 | printk(KERN_WARNING "act2000: No Firmware-ID!\n"); | ||
379 | return -ETIME; | ||
380 | } | ||
381 | *p = '\0'; | ||
382 | fid.revlen[0] = '\0'; | ||
383 | if (strcmp(fid.isdn, "ISDN")) { | ||
384 | printk(KERN_WARNING "act2000: Wrong Firmware-ID!\n"); | ||
385 | return -EPROTO; | ||
386 | } | ||
387 | if ((p = strchr(fid.revision, '\n'))) | ||
388 | *p = '\0'; | ||
389 | printk(KERN_INFO "act2000: Firmware-ID: %s\n", fid.revision); | ||
390 | if (card->flags & ACT2000_FLAGS_IVALID) { | ||
391 | printk(KERN_DEBUG "Enabling Interrupts ...\n"); | ||
392 | act2000_isa_enable_irq(card); | ||
393 | } | ||
394 | return 0; | ||
395 | } | ||
396 | |||
397 | /* | ||
398 | * Download microcode into card, check Firmware signature. | ||
399 | */ | ||
400 | int | ||
401 | act2000_isa_download(act2000_card * card, act2000_ddef __user * cb) | ||
402 | { | ||
403 | unsigned int length; | ||
404 | int l; | ||
405 | int c; | ||
406 | long timeout; | ||
407 | u_char *b; | ||
408 | u_char __user *p; | ||
409 | u_char *buf; | ||
410 | act2000_ddef cblock; | ||
411 | |||
412 | if (!act2000_isa_reset(card->port)) | ||
413 | return -ENXIO; | ||
414 | msleep_interruptible(500); | ||
415 | if (copy_from_user(&cblock, cb, sizeof(cblock))) | ||
416 | return -EFAULT; | ||
417 | length = cblock.length; | ||
418 | p = cblock.buffer; | ||
419 | if (!access_ok(VERIFY_READ, p, length)) | ||
420 | return -EFAULT; | ||
421 | buf = (u_char *) kmalloc(1024, GFP_KERNEL); | ||
422 | if (!buf) | ||
423 | return -ENOMEM; | ||
424 | timeout = 0; | ||
425 | while (length) { | ||
426 | l = (length > 1024) ? 1024 : length; | ||
427 | c = 0; | ||
428 | b = buf; | ||
429 | if (copy_from_user(buf, p, l)) { | ||
430 | kfree(buf); | ||
431 | return -EFAULT; | ||
432 | } | ||
433 | while (c < l) { | ||
434 | if (act2000_isa_writeb(card, *b++)) { | ||
435 | printk(KERN_WARNING | ||
436 | "act2000: loader timed out" | ||
437 | " len=%d c=%d\n", length, c); | ||
438 | kfree(buf); | ||
439 | return -ETIME; | ||
440 | } | ||
441 | c++; | ||
442 | } | ||
443 | length -= l; | ||
444 | p += l; | ||
445 | } | ||
446 | kfree(buf); | ||
447 | msleep_interruptible(500); | ||
448 | return (act2000_isa_getid(card)); | ||
449 | } | ||