| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 | } |