Linux-2.6.12-rc2
[GitHub/mt8127/android_kernel_alcatel_ttab.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 /*
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 }