/* $Id: eicon_isa.c,v 1.1.1.1 2003/06/23 22:18:27 jharrell Exp $ * * ISDN low-level module for Eicon active ISDN-Cards. * Hardware-specific code for old ISA cards. * * Copyright 1998 by Fritz Elfert (fritz@isdn4linux.de) * Copyright 1998-2000 by Armin Schindler (mac@melware.de) * Copyright 1999,2000 Cytronics & Melware (info@melware.de) * * This software may be used and distributed according to the terms * of the GNU General Public License, incorporated herein by reference. * */ #include #include "eicon.h" #include "eicon_isa.h" #define check_shmem check_region #define release_shmem release_region #define request_shmem request_region char *eicon_isa_revision = "$Revision: 1.1.1.1 $"; #undef EICON_MCA_DEBUG #ifdef CONFIG_ISDN_DRV_EICON_ISA /* Mask for detecting invalid IRQ parameter */ static int eicon_isa_valid_irq[] = { 0x1c1c, /* 2, 3, 4, 10, 11, 12 (S)*/ 0x1c1c, /* 2, 3, 4, 10, 11, 12 (SX) */ 0x1cbc, /* 2, 3, 4, 5, 7, 10, 11, 12 (SCOM) */ 0x1cbc, /* 2, 3, 4, 5, 6, 10, 11, 12 (Quadro) */ 0x1cbc /* 2, 3, 4, 5, 7, 10, 11, 12 (S2M) */ }; static void eicon_isa_release_shmem(eicon_isa_card *card) { if (card->mvalid) { iounmap(card->shmem); release_mem_region(card->physmem, card->ramsize); } card->mvalid = 0; } static void eicon_isa_release_irq(eicon_isa_card *card) { if (!card->master) return; if (card->ivalid) free_irq(card->irq, card); card->ivalid = 0; } void eicon_isa_release(eicon_isa_card *card) { eicon_isa_release_irq(card); eicon_isa_release_shmem(card); } void eicon_isa_printpar(eicon_isa_card *card) { switch (card->type) { case EICON_CTYPE_S: case EICON_CTYPE_SX: case EICON_CTYPE_SCOM: case EICON_CTYPE_QUADRO: case EICON_CTYPE_S2M: printk(KERN_INFO "Eicon %s at 0x%lx, irq %d.\n", eicon_ctype_name[card->type], card->physmem, card->irq); } } int eicon_isa_find_card(int Mem, int Irq, char * Id) { int primary = 1; unsigned long amem; if (!strlen(Id)) return -1; if (Mem == -1) return -1; /* Check for valid membase address */ if ((Mem < 0x0c0000) || (Mem > 0x0fc000) || (Mem & 0xfff)) { printk(KERN_WARNING "eicon_isa: illegal membase 0x%x for %s\n", Mem, Id); return -1; } if (check_mem_region(Mem, RAMSIZE)) { printk(KERN_WARNING "eicon_isa_boot: memory at 0x%x already in use.\n", Mem); return -1; } amem = (unsigned long) ioremap(Mem, RAMSIZE); writew(0x55aa, amem + 0x402); if (readw(amem + 0x402) != 0x55aa) primary = 0; writew(0, amem + 0x402); if (readw(amem + 0x402) != 0) primary = 0; printk(KERN_INFO "Eicon: Driver-ID: %s\n", Id); if (primary) { printk(KERN_INFO "Eicon: assuming pri card at 0x%x\n", Mem); writeb(0, amem + 0x3ffe); iounmap((unsigned char *)amem); return EICON_CTYPE_ISAPRI; } else { printk(KERN_INFO "Eicon: assuming bri card at 0x%x\n", Mem); writeb(0, amem + 0x400); iounmap((unsigned char *)amem); return EICON_CTYPE_ISABRI; } return -1; } int eicon_isa_bootload(eicon_isa_card *card, eicon_isa_codebuf *cb) { int tmp; int timeout; eicon_isa_codebuf cbuf; unsigned char *code; eicon_isa_boot *boot; if (copy_from_user(&cbuf, cb, sizeof(eicon_isa_codebuf))) return -EFAULT; /* Allocate code-buffer and copy code from userspace */ if (cbuf.bootstrap_len > 1024) { printk(KERN_WARNING "eicon_isa_boot: Invalid startup-code size %ld\n", cbuf.bootstrap_len); return -EINVAL; } if (!(code = kmalloc(cbuf.bootstrap_len, GFP_KERNEL))) { printk(KERN_WARNING "eicon_isa_boot: Couldn't allocate code buffer\n"); return -ENOMEM; } if (copy_from_user(code, &cb->code, cbuf.bootstrap_len)) { kfree(code); return -EFAULT; } if (card->type == EICON_CTYPE_ISAPRI) card->ramsize = RAMSIZE_P; else card->ramsize = RAMSIZE; if (check_mem_region(card->physmem, card->ramsize)) { printk(KERN_WARNING "eicon_isa_boot: memory at 0x%lx already in use.\n", card->physmem); kfree(code); return -EBUSY; } request_mem_region(card->physmem, card->ramsize, "Eicon ISA ISDN"); card->shmem = (eicon_isa_shmem *) ioremap(card->physmem, card->ramsize); #ifdef EICON_MCA_DEBUG printk(KERN_INFO "eicon_isa_boot: card->ramsize = %d.\n", card->ramsize); #endif card->mvalid = 1; switch(card->type) { case EICON_CTYPE_S: case EICON_CTYPE_SX: case EICON_CTYPE_SCOM: case EICON_CTYPE_QUADRO: case EICON_CTYPE_ISABRI: card->intack = (__u8 *)card->shmem + INTACK; card->startcpu = (__u8 *)card->shmem + STARTCPU; card->stopcpu = (__u8 *)card->shmem + STOPCPU; break; case EICON_CTYPE_S2M: case EICON_CTYPE_ISAPRI: card->intack = (__u8 *)card->shmem + INTACK_P; card->startcpu = (__u8 *)card->shmem + STARTCPU_P; card->stopcpu = (__u8 *)card->shmem + STOPCPU_P; break; default: printk(KERN_WARNING "eicon_isa_boot: Invalid card type %d\n", card->type); eicon_isa_release_shmem(card); kfree(code); return -EINVAL; } /* clear any pending irq's */ readb(card->intack); #ifdef CONFIG_MCA if (MCA_bus) { if (card->type == EICON_CTYPE_SCOM) { outb_p(0,card->io+1); } else { printk(KERN_WARNING "eicon_isa_boot: Card type not supported yet.\n"); eicon_isa_release_shmem(card); return -EINVAL; }; #ifdef EICON_MCA_DEBUG printk(KERN_INFO "eicon_isa_boot: card->io = %x.\n", card->io); printk(KERN_INFO "eicon_isa_boot: card->irq = %d.\n", (int)card->irq); #endif } #else /* set reset-line active */ writeb(0, card->stopcpu); #endif /* CONFIG_MCA */ /* clear irq-requests */ writeb(0, card->intack); readb(card->intack); /* Copy code into card */ memcpy_toio(&card->shmem->c, code, cbuf.bootstrap_len); /* Check for properly loaded code */ if (!check_signature((unsigned long)&card->shmem->c, code, 1020)) { printk(KERN_WARNING "eicon_isa_boot: Could not load startup-code\n"); eicon_isa_release_shmem(card); kfree(code); return -EIO; } /* if 16k-ramsize, duplicate the reset-jump-code */ if (card->ramsize == RAMSIZE_P) memcpy_toio((__u8 *)card->shmem + 0x3ff0, &code[0x3f0], 12); kfree(code); boot = &card->shmem->boot; /* Delay 0.2 sec. */ SLEEP(HZ / 5); /* Start CPU */ writeb(cbuf.boot_opt, &boot->ctrl); #ifdef CONFIG_MCA if (MCA_bus) { outb_p(0, card->io); } #else writeb(0, card->startcpu); #endif /* CONFIG_MCA */ /* Delay 0.2 sec. */ SLEEP(HZ / 5); timeout = jiffies + (HZ * 22); while (time_before(jiffies, timeout)) { if (readb(&boot->ctrl) == 0) break; SLEEP(10); } if (readb(&boot->ctrl) != 0) { printk(KERN_WARNING "eicon_isa_boot: CPU test failed.\n"); #ifdef EICON_MCA_DEBUG printk(KERN_INFO "eicon_isa_boot: &boot->ctrl = %d.\n", readb(&boot->ctrl)); #endif eicon_isa_release_shmem(card); return -EIO; } /* Check for memory-test errors */ if (readw(&boot->ebit)) { printk(KERN_WARNING "eicon_isa_boot: memory test failed (bit 0x%04x at 0x%08x)\n", readw(&boot->ebit), readl(&boot->eloc)); eicon_isa_release_shmem(card); return -EIO; } /* Check card type and memory size */ tmp = readb(&boot->card); if ((tmp < 0) || (tmp > 4)) { printk(KERN_WARNING "eicon_isa_boot: Type detect failed\n"); eicon_isa_release_shmem(card); return -EIO; } card->type = tmp; ((eicon_card *)card->card)->type = tmp; tmp = readb(&boot->msize); if (tmp != 8 && tmp != 16 && tmp != 24 && tmp != 32 && tmp != 48 && tmp != 60) { printk(KERN_WARNING "eicon_isa_boot: invalid memsize\n"); eicon_isa_release_shmem(card); return -EIO; } printk(KERN_INFO "%s: startup-code loaded\n", eicon_ctype_name[card->type]); if ((card->type == EICON_CTYPE_QUADRO) && (card->master)) { tmp = eicon_addcard(card->type, card->physmem, card->irq, ((eicon_card *)card->card)->regname, 0); printk(KERN_INFO "Eicon: %d adapters added\n", tmp); } return 0; } int eicon_isa_load(eicon_isa_card *card, eicon_isa_codebuf *cb) { eicon_isa_boot *boot; int tmp; int timeout; int j; eicon_isa_codebuf cbuf; unsigned char *code; unsigned char *p; if (copy_from_user(&cbuf, cb, sizeof(eicon_isa_codebuf))) return -EFAULT; if (!(code = kmalloc(cbuf.firmware_len, GFP_KERNEL))) { printk(KERN_WARNING "eicon_isa_load: Couldn't allocate code buffer\n"); return -ENOMEM; } if (copy_from_user(code, &cb->code, cbuf.firmware_len)) { kfree(code); return -EFAULT; } boot = &card->shmem->boot; if ((!card->ivalid) && card->master) { card->irqprobe = 1; /* Check for valid IRQ */ if ((card->irq < 0) || (card->irq > 15) || (!((1 << card->irq) & eicon_isa_valid_irq[card->type & 0x0f]))) { printk(KERN_WARNING "eicon_isa_load: illegal irq: %d\n", card->irq); eicon_isa_release_shmem(card); kfree(code); return -EINVAL; } /* Register irq */ if (!request_irq(card->irq, &eicon_irq, 0, "Eicon ISA ISDN", card)) card->ivalid = 1; else { printk(KERN_WARNING "eicon_isa_load: irq %d already in use.\n", card->irq); eicon_isa_release_shmem(card); kfree(code); return -EBUSY; } } tmp = readb(&boot->msize); if (tmp != 8 && tmp != 16 && tmp != 24 && tmp != 32 && tmp != 48 && tmp != 60) { printk(KERN_WARNING "eicon_isa_load: invalid memsize\n"); eicon_isa_release_shmem(card); return -EIO; } eicon_isa_printpar(card); /* Download firmware */ printk(KERN_INFO "%s %dkB, loading firmware ...\n", eicon_ctype_name[card->type], tmp * 16); tmp = cbuf.firmware_len >> 8; p = code; while (tmp--) { memcpy_toio(&boot->b, p, 256); writeb(1, &boot->ctrl); timeout = jiffies + HZ / 10; while (time_before(jiffies, timeout)) { if (readb(&boot->ctrl) == 0) break; SLEEP(2); } if (readb(&boot->ctrl)) { printk(KERN_WARNING "eicon_isa_load: download timeout at 0x%x\n", p-code); eicon_isa_release(card); kfree(code); return -EIO; } p += 256; } kfree(code); /* Initialize firmware parameters */ memcpy_toio(&card->shmem->c[8], &cbuf.tei, 14); memcpy_toio(&card->shmem->c[32], &cbuf.oad, 96); memcpy_toio(&card->shmem->c[128], &cbuf.oad, 96); /* Start firmware, wait for signature */ writeb(2, &boot->ctrl); timeout = jiffies + (5*HZ); while (time_before(jiffies, timeout)) { if (readw(&boot->signature) == 0x4447) break; SLEEP(2); } if (readw(&boot->signature) != 0x4447) { printk(KERN_WARNING "eicon_isa_load: firmware selftest failed %04x\n", readw(&boot->signature)); eicon_isa_release(card); return -EIO; } card->channels = readb(&card->shmem->c[0x3f6]); /* clear irq-requests, reset irq-count */ readb(card->intack); writeb(0, card->intack); if (card->master) { card->irqprobe = 1; /* Trigger an interrupt and check if it is delivered */ tmp = readb(&card->shmem->com.ReadyInt); tmp ++; writeb(tmp, &card->shmem->com.ReadyInt); timeout = jiffies + HZ / 5; while (time_before(jiffies, timeout)) { if (card->irqprobe > 1) break; SLEEP(2); } if (card->irqprobe == 1) { printk(KERN_WARNING "eicon_isa_load: IRQ # %d test failed\n", card->irq); eicon_isa_release(card); return -EIO; } } #ifdef EICON_MCA_DEBUG printk(KERN_INFO "eicon_isa_load: IRQ # %d test succeeded.\n", card->irq); #endif writeb(card->irq, &card->shmem->com.Int); /* initializing some variables */ ((eicon_card *)card->card)->ReadyInt = 0; ((eicon_card *)card->card)->ref_in = 1; ((eicon_card *)card->card)->ref_out = 1; for(j=0; j<256; j++) ((eicon_card *)card->card)->IdTable[j] = NULL; for(j=0; j< (card->channels + 1); j++) { ((eicon_card *)card->card)->bch[j].e.busy = 0; ((eicon_card *)card->card)->bch[j].e.D3Id = 0; ((eicon_card *)card->card)->bch[j].e.B2Id = 0; ((eicon_card *)card->card)->bch[j].e.ref = 0; ((eicon_card *)card->card)->bch[j].e.Req = 0; ((eicon_card *)card->card)->bch[j].e.complete = 1; ((eicon_card *)card->card)->bch[j].fsm_state = EICON_STATE_NULL; } printk(KERN_INFO "Eicon: Supported channels: %d\n", card->channels); printk(KERN_INFO "%s successfully started\n", eicon_ctype_name[card->type]); /* Enable normal IRQ processing */ card->irqprobe = 0; return 0; } #endif /* CONFIG_ISDN_DRV_EICON_ISA */