diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1999-06-17 14:08:29 +0000 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 1999-06-17 14:08:29 +0000 |
commit | 57d569635c05dc4ea9b9f1f8dcec69b9ddc989b2 (patch) | |
tree | 1f703abf7d95dcd50ee52da3b96eb1b4b2b4ea53 /drivers/isdn/eicon/eicon_isa.c | |
parent | 59223edaa18759982db0a8aced0e77457d10c68e (diff) |
The rest of 2.3.6.
Diffstat (limited to 'drivers/isdn/eicon/eicon_isa.c')
-rw-r--r-- | drivers/isdn/eicon/eicon_isa.c | 432 |
1 files changed, 432 insertions, 0 deletions
diff --git a/drivers/isdn/eicon/eicon_isa.c b/drivers/isdn/eicon/eicon_isa.c new file mode 100644 index 000000000..184f1c394 --- /dev/null +++ b/drivers/isdn/eicon/eicon_isa.c @@ -0,0 +1,432 @@ +/* $Id: eicon_isa.c,v 1.5 1999/04/01 12:48:33 armin Exp $ + * + * ISDN low-level module for Eicon.Diehl active ISDN-Cards. + * Hardware-specific code for old ISA cards. + * + * Copyright 1998 by Fritz Elfert (fritz@wuemaus.franken.de) + * Copyright 1998,99 by Armin Schindler (mac@melware.de) + * Copyright 1999 Cytronics & Melware (info@melware.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Log: eicon_isa.c,v $ + * Revision 1.5 1999/04/01 12:48:33 armin + * Changed some log outputs. + * + * Revision 1.4 1999/03/29 11:19:46 armin + * I/O stuff now in seperate file (eicon_io.c) + * Old ISA type cards (S,SX,SCOM,Quadro,S2M) implemented. + * + * Revision 1.3 1999/03/02 12:37:45 armin + * Added some important checks. + * Analog Modem with DSP. + * Channels will be added to Link-Level after loading firmware. + * + * Revision 1.2 1999/01/24 20:14:19 armin + * Changed and added debug stuff. + * Better data sending. (still problems with tty's flip buffer) + * + * Revision 1.1 1999/01/01 18:09:43 armin + * First checkin of new eicon driver. + * DIVA-Server BRI/PCI and PRI/PCI are supported. + * Old diehl code is obsolete. + * + * + */ + +#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.5 $"; + +/* 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) + release_shmem((unsigned long)card->shmem, 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], + (unsigned long)card->shmem, + card->irq); + } +} + +int +eicon_isa_find_card(int Mem, int Irq, char * Id) +{ + int primary = 1; + + if (!strlen(Id)) + 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_shmem(Mem, RAMSIZE)) { + printk(KERN_WARNING "eicon_isa_boot: memory at 0x%x already in use.\n", Mem); + return -1; + } + + writew(0x55aa, Mem + 0x402); + if (readw(Mem + 0x402) != 0x55aa) primary = 0; + writew(0, Mem + 0x402); + if (readw(Mem + 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, Mem + 0x3ffe); + return EICON_CTYPE_ISAPRI; + } else { + printk(KERN_INFO "Eicon: assuming bri card at 0x%x\n", Mem); + writeb(0, Mem + 0x400); + 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; + } + + switch(card->type) { + case EICON_CTYPE_S: + case EICON_CTYPE_SX: + case EICON_CTYPE_SCOM: + case EICON_CTYPE_QUADRO: + case EICON_CTYPE_ISABRI: + card->ramsize = RAMSIZE; + 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->ramsize = RAMSIZE_P; + 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); + return -EINVAL; + } + + /* Register shmem */ + if (check_shmem((unsigned long)card->shmem, card->ramsize)) { + printk(KERN_WARNING "eicon_isa_boot: memory at 0x%lx already in use.\n", + (unsigned long)card->shmem); + kfree(code); + return -EBUSY; + } + request_shmem((unsigned long)card->shmem, card->ramsize, "Eicon ISA ISDN"); + card->mvalid = 1; + + /* clear any pending irq's */ + readb(card->intack); + /* set reset-line active */ + writeb(0, card->stopcpu); + /* 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(20); + + /* Start CPU */ + writeb(cbuf.boot_opt, &boot->ctrl); + writeb(0, card->startcpu); + + /* Delay 0.2 sec. */ + SLEEP(20); + + timeout = jiffies + (HZ * 22); + while (timeout > jiffies) { + if (readb(&boot->ctrl) == 0) + break; + SLEEP(10); + } + if (readb(&boot->ctrl) != 0) { + printk(KERN_WARNING "eicon_isa_boot: CPU test failed\n"); + 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, (unsigned long)card->shmem, card->irq, + ((eicon_card *)card->card)->regname); + 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_boot: 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_boot: 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_boot: 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_boot: 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 + 10; + while (timeout > jiffies) { + if (readb(&boot->ctrl) == 0) + break; + SLEEP(2); + } + if (readb(&boot->ctrl)) { + printk(KERN_WARNING "eicon_isa_boot: 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 (timeout > jiffies) { + if (readw(&boot->signature) == 0x4447) + break; + SLEEP(2); + } + if (readw(&boot->signature) != 0x4447) { + printk(KERN_WARNING "eicon_isa_boot: 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 + 20; + while (timeout > jiffies) { + if (card->irqprobe > 1) + break; + SLEEP(2); + } + if (card->irqprobe == 1) { + printk(KERN_WARNING "eicon_isa_boot: IRQ test failed\n"); + eicon_isa_release(card); + return -EIO; + } + } + 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; +} |