1/* $Id: act2000_isa.c,v 1.1.1.1 2007/08/03 18:52:34 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 irq, 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", 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 explicitely. 130 */ 131int 132act2000_isa_config_irq(act2000_card * card, short irq) 133{ 134 if (card->flags & ACT2000_FLAGS_IVALID) { 135 free_irq(card->irq, card); 136 } 137 card->flags &= ~ACT2000_FLAGS_IVALID; 138 outb(ISA_COR_IRQOFF, ISA_PORT_COR); 139 if (!irq) 140 return 0; 141 142 if (!request_irq(irq, &act2000_isa_interrupt, 0, card->regname, card)) { 143 card->irq = irq; 144 card->flags |= ACT2000_FLAGS_IVALID; 145 printk(KERN_WARNING 146 "act2000: Could not request irq %d\n",irq); 147 return -EBUSY; 148 } else { 149 act2000_isa_select_irq(card); 150 /* Disable READ and WRITE irq */ 151 outb(0, ISA_PORT_SIS); 152 outb(0, ISA_PORT_SOS); 153 } 154 return 0; 155} 156 157int 158act2000_isa_config_port(act2000_card * card, unsigned short portbase) 159{ 160 if (card->flags & ACT2000_FLAGS_PVALID) { 161 release_region(card->port, ISA_REGION); 162 card->flags &= ~ACT2000_FLAGS_PVALID; 163 } 164 if (request_region(portbase, ACT2000_PORTLEN, card->regname) == NULL) 165 return -EBUSY; 166 else { 167 card->port = portbase; 168 card->flags |= ACT2000_FLAGS_PVALID; 169 return 0; 170 } 171} 172 173/* 174 * Release ressources, used by an adaptor. 175 */ 176void 177act2000_isa_release(act2000_card * card) 178{ 179 unsigned long flags; 180 181 spin_lock_irqsave(&card->lock, flags); 182 if (card->flags & ACT2000_FLAGS_IVALID) 183 free_irq(card->irq, card); 184 185 card->flags &= ~ACT2000_FLAGS_IVALID; 186 if (card->flags & ACT2000_FLAGS_PVALID) 187 release_region(card->port, ISA_REGION); 188 card->flags &= ~ACT2000_FLAGS_PVALID; 189 spin_unlock_irqrestore(&card->lock, flags); 190} 191 192static int 193act2000_isa_writeb(act2000_card * card, u_char data) 194{ 195 u_char timeout = 40; 196 197 while (timeout) { 198 if (inb(ISA_PORT_SOS) & ISA_SOS_READY) { 199 outb(data, ISA_PORT_SDO); 200 return 0; 201 } else { 202 timeout--; 203 udelay(10); 204 } 205 } 206 return 1; 207} 208 209static int 210act2000_isa_readb(act2000_card * card, u_char * data) 211{ 212 u_char timeout = 40; 213 214 while (timeout) { 215 if (inb(ISA_PORT_SIS) & ISA_SIS_READY) { 216 *data = inb(ISA_PORT_SDI); 217 return 0; 218 } else { 219 timeout--; 220 udelay(10); 221 } 222 } 223 return 1; 224} 225 226void 227act2000_isa_receive(act2000_card *card) 228{ 229 u_char c; 230 231 if (test_and_set_bit(ACT2000_LOCK_RX, (void *) &card->ilock) != 0) 232 return; 233 while (!act2000_isa_readb(card, &c)) { 234 if (card->idat.isa.rcvidx < 8) { 235 card->idat.isa.rcvhdr[card->idat.isa.rcvidx++] = c; 236 if (card->idat.isa.rcvidx == 8) { 237 int valid = actcapi_chkhdr(card, (actcapi_msghdr *)&card->idat.isa.rcvhdr); 238 239 if (valid) { 240 card->idat.isa.rcvlen = ((actcapi_msghdr *)&card->idat.isa.rcvhdr)->len; 241 card->idat.isa.rcvskb = dev_alloc_skb(card->idat.isa.rcvlen); 242 if (card->idat.isa.rcvskb == NULL) { 243 card->idat.isa.rcvignore = 1; 244 printk(KERN_WARNING 245 "act2000_isa_receive: no memory\n"); 246 test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock); 247 return; 248 } 249 memcpy(skb_put(card->idat.isa.rcvskb, 8), card->idat.isa.rcvhdr, 8); 250 card->idat.isa.rcvptr = skb_put(card->idat.isa.rcvskb, card->idat.isa.rcvlen - 8); 251 } else { 252 card->idat.isa.rcvidx = 0; 253 printk(KERN_WARNING 254 "act2000_isa_receive: Invalid CAPI msg\n"); 255 { 256 int i; __u8 *p; __u8 *c; __u8 tmp[30]; 257 for (i = 0, p = (__u8 *)&card->idat.isa.rcvhdr, c = tmp; i < 8; i++) 258 c += sprintf(c, "%02x ", *(p++)); 259 printk(KERN_WARNING "act2000_isa_receive: %s\n", tmp); 260 } 261 } 262 } 263 } else { 264 if (!card->idat.isa.rcvignore) 265 *card->idat.isa.rcvptr++ = c; 266 if (++card->idat.isa.rcvidx >= card->idat.isa.rcvlen) { 267 if (!card->idat.isa.rcvignore) { 268 skb_queue_tail(&card->rcvq, card->idat.isa.rcvskb); 269 act2000_schedule_rx(card); 270 } 271 card->idat.isa.rcvidx = 0; 272 card->idat.isa.rcvlen = 8; 273 card->idat.isa.rcvignore = 0; 274 card->idat.isa.rcvskb = NULL; 275 card->idat.isa.rcvptr = card->idat.isa.rcvhdr; 276 } 277 } 278 } 279 if (!(card->flags & ACT2000_FLAGS_IVALID)) { 280 /* In polling mode, schedule myself */ 281 if ((card->idat.isa.rcvidx) && 282 (card->idat.isa.rcvignore || 283 (card->idat.isa.rcvidx < card->idat.isa.rcvlen))) 284 act2000_schedule_poll(card); 285 } 286 test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock); 287} 288 289void 290act2000_isa_send(act2000_card * card) 291{ 292 unsigned long flags; 293 struct sk_buff *skb; 294 actcapi_msg *msg; 295 int l; 296 297 if (test_and_set_bit(ACT2000_LOCK_TX, (void *) &card->ilock) != 0) 298 return; 299 while (1) { 300 spin_lock_irqsave(&card->lock, flags); 301 if (!(card->sbuf)) { 302 if ((card->sbuf = skb_dequeue(&card->sndq))) { 303 card->ack_msg = card->sbuf->data; 304 msg = (actcapi_msg *)card->sbuf->data; 305 if ((msg->hdr.cmd.cmd == 0x86) && 306 (msg->hdr.cmd.subcmd == 0) ) { 307 /* Save flags in message */ 308 card->need_b3ack = msg->msg.data_b3_req.flags; 309 msg->msg.data_b3_req.flags = 0; 310 } 311 } 312 } 313 spin_unlock_irqrestore(&card->lock, flags); 314 if (!(card->sbuf)) { 315 /* No more data to send */ 316 test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock); 317 return; 318 } 319 skb = card->sbuf; 320 l = 0; 321 while (skb->len) { 322 if (act2000_isa_writeb(card, *(skb->data))) { 323 /* Fifo is full, but more data to send */ 324 test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock); 325 /* Schedule myself */ 326 act2000_schedule_tx(card); 327 return; 328 } 329 skb_pull(skb, 1); 330 l++; 331 } 332 msg = (actcapi_msg *)card->ack_msg; 333 if ((msg->hdr.cmd.cmd == 0x86) && 334 (msg->hdr.cmd.subcmd == 0) ) { 335 /* 336 * If it's user data, reset data-ptr 337 * and put skb into ackq. 338 */ 339 skb->data = card->ack_msg; 340 /* Restore flags in message */ 341 msg->msg.data_b3_req.flags = card->need_b3ack; 342 skb_queue_tail(&card->ackq, skb); 343 } else 344 dev_kfree_skb(skb); 345 card->sbuf = NULL; 346 } 347} 348 349/* 350 * Get firmware ID, check for 'ISDN' signature. 351 */ 352static int 353act2000_isa_getid(act2000_card * card) 354{ 355 356 act2000_fwid fid; 357 u_char *p = (u_char *) & fid; 358 int count = 0; 359 360 while (1) { 361 if (count > 510) 362 return -EPROTO; 363 if (act2000_isa_readb(card, p++)) 364 break; 365 count++; 366 } 367 if (count <= 20) { 368 printk(KERN_WARNING "act2000: No Firmware-ID!\n"); 369 return -ETIME; 370 } 371 *p = '\0'; 372 fid.revlen[0] = '\0'; 373 if (strcmp(fid.isdn, "ISDN")) { 374 printk(KERN_WARNING "act2000: Wrong Firmware-ID!\n"); 375 return -EPROTO; 376 } 377 if ((p = strchr(fid.revision, '\n'))) 378 *p = '\0'; 379 printk(KERN_INFO "act2000: Firmware-ID: %s\n", fid.revision); 380 if (card->flags & ACT2000_FLAGS_IVALID) { 381 printk(KERN_DEBUG "Enabling Interrupts ...\n"); 382 act2000_isa_enable_irq(card); 383 } 384 return 0; 385} 386 387/* 388 * Download microcode into card, check Firmware signature. 389 */ 390int 391act2000_isa_download(act2000_card * card, act2000_ddef __user * cb) 392{ 393 unsigned int length; 394 int l; 395 int c; 396 long timeout; 397 u_char *b; 398 u_char __user *p; 399 u_char *buf; 400 act2000_ddef cblock; 401 402 if (!act2000_isa_reset(card->port)) 403 return -ENXIO; 404 msleep_interruptible(500); 405 if (copy_from_user(&cblock, cb, sizeof(cblock))) 406 return -EFAULT; 407 length = cblock.length; 408 p = cblock.buffer; 409 if (!access_ok(VERIFY_READ, p, length)) 410 return -EFAULT; 411 buf = kmalloc(1024, GFP_KERNEL); 412 if (!buf) 413 return -ENOMEM; 414 timeout = 0; 415 while (length) { 416 l = (length > 1024) ? 1024 : length; 417 c = 0; 418 b = buf; 419 if (copy_from_user(buf, p, l)) { 420 kfree(buf); 421 return -EFAULT; 422 } 423 while (c < l) { 424 if (act2000_isa_writeb(card, *b++)) { 425 printk(KERN_WARNING 426 "act2000: loader timed out" 427 " len=%d c=%d\n", length, c); 428 kfree(buf); 429 return -ETIME; 430 } 431 c++; 432 } 433 length -= l; 434 p += l; 435 } 436 kfree(buf); 437 msleep_interruptible(500); 438 return (act2000_isa_getid(card)); 439} 440