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