File: /usr/src/linux/drivers/isdn/act2000/act2000_isa.c

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