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