Path: blob/master/drivers/isdn/act2000/act2000_isa.c
17498 views
/* $Id: act2000_isa.c,v 1.11.6.3 2001/09/23 22:24:32 kai Exp $1*2* ISDN lowlevel-module for the IBM ISDN-S0 Active 2000 (ISA-Version).3*4* Author Fritz Elfert5* Copyright by Fritz Elfert <[email protected]>6*7* This software may be used and distributed according to the terms8* of the GNU General Public License, incorporated herein by reference.9*10* Thanks to Friedemann Baitinger and IBM Germany11*12*/1314#include "act2000.h"15#include "act2000_isa.h"16#include "capi.h"1718/*19* Reset Controller, then try to read the Card's signature.20+ Return:21* 1 = Signature found.22* 0 = Signature not found.23*/24static int25act2000_isa_reset(unsigned short portbase)26{27unsigned char reg;28int i;29int found;30int serial = 0;3132found = 0;33if ((reg = inb(portbase + ISA_COR)) != 0xff) {34outb(reg | ISA_COR_RESET, portbase + ISA_COR);35mdelay(10);36outb(reg, portbase + ISA_COR);37mdelay(10);3839for (i = 0; i < 16; i++) {40if (inb(portbase + ISA_ISR) & ISA_ISR_SERIAL)41serial |= 0x10000;42serial >>= 1;43}44if (serial == ISA_SER_ID)45found++;46}47return found;48}4950int51act2000_isa_detect(unsigned short portbase)52{53int ret = 0;5455if (request_region(portbase, ACT2000_PORTLEN, "act2000isa")) {56ret = act2000_isa_reset(portbase);57release_region(portbase, ISA_REGION);58}59return ret;60}6162static irqreturn_t63act2000_isa_interrupt(int dummy, void *dev_id)64{65act2000_card *card = dev_id;66u_char istatus;6768istatus = (inb(ISA_PORT_ISR) & 0x07);69if (istatus & ISA_ISR_OUT) {70/* RX fifo has data */71istatus &= ISA_ISR_OUT_MASK;72outb(0, ISA_PORT_SIS);73act2000_isa_receive(card);74outb(ISA_SIS_INT, ISA_PORT_SIS);75}76if (istatus & ISA_ISR_ERR) {77/* Error Interrupt */78istatus &= ISA_ISR_ERR_MASK;79printk(KERN_WARNING "act2000: errIRQ\n");80}81if (istatus)82printk(KERN_DEBUG "act2000: ?IRQ %d %02x\n", card->irq, istatus);83return IRQ_HANDLED;84}8586static void87act2000_isa_select_irq(act2000_card * card)88{89unsigned char reg;9091reg = (inb(ISA_PORT_COR) & ~ISA_COR_IRQOFF) | ISA_COR_PERR;92switch (card->irq) {93case 3:94reg = ISA_COR_IRQ03;95break;96case 5:97reg = ISA_COR_IRQ05;98break;99case 7:100reg = ISA_COR_IRQ07;101break;102case 10:103reg = ISA_COR_IRQ10;104break;105case 11:106reg = ISA_COR_IRQ11;107break;108case 12:109reg = ISA_COR_IRQ12;110break;111case 15:112reg = ISA_COR_IRQ15;113break;114}115outb(reg, ISA_PORT_COR);116}117118static void119act2000_isa_enable_irq(act2000_card * card)120{121act2000_isa_select_irq(card);122/* Enable READ irq */123outb(ISA_SIS_INT, ISA_PORT_SIS);124}125126/*127* Install interrupt handler, enable irq on card.128* If irq is -1, choose next free irq, else irq is given explicitly.129*/130int131act2000_isa_config_irq(act2000_card * card, short irq)132{133int old_irq;134135if (card->flags & ACT2000_FLAGS_IVALID) {136free_irq(card->irq, card);137}138card->flags &= ~ACT2000_FLAGS_IVALID;139outb(ISA_COR_IRQOFF, ISA_PORT_COR);140if (!irq)141return 0;142143old_irq = card->irq;144card->irq = irq;145if (request_irq(irq, &act2000_isa_interrupt, 0, card->regname, card)) {146card->irq = old_irq;147card->flags |= ACT2000_FLAGS_IVALID;148printk(KERN_WARNING149"act2000: Could not request irq %d\n",irq);150return -EBUSY;151} else {152act2000_isa_select_irq(card);153/* Disable READ and WRITE irq */154outb(0, ISA_PORT_SIS);155outb(0, ISA_PORT_SOS);156}157return 0;158}159160int161act2000_isa_config_port(act2000_card * card, unsigned short portbase)162{163if (card->flags & ACT2000_FLAGS_PVALID) {164release_region(card->port, ISA_REGION);165card->flags &= ~ACT2000_FLAGS_PVALID;166}167if (request_region(portbase, ACT2000_PORTLEN, card->regname) == NULL)168return -EBUSY;169else {170card->port = portbase;171card->flags |= ACT2000_FLAGS_PVALID;172return 0;173}174}175176/*177* Release ressources, used by an adaptor.178*/179void180act2000_isa_release(act2000_card * card)181{182unsigned long flags;183184spin_lock_irqsave(&card->lock, flags);185if (card->flags & ACT2000_FLAGS_IVALID)186free_irq(card->irq, card);187188card->flags &= ~ACT2000_FLAGS_IVALID;189if (card->flags & ACT2000_FLAGS_PVALID)190release_region(card->port, ISA_REGION);191card->flags &= ~ACT2000_FLAGS_PVALID;192spin_unlock_irqrestore(&card->lock, flags);193}194195static int196act2000_isa_writeb(act2000_card * card, u_char data)197{198u_char timeout = 40;199200while (timeout) {201if (inb(ISA_PORT_SOS) & ISA_SOS_READY) {202outb(data, ISA_PORT_SDO);203return 0;204} else {205timeout--;206udelay(10);207}208}209return 1;210}211212static int213act2000_isa_readb(act2000_card * card, u_char * data)214{215u_char timeout = 40;216217while (timeout) {218if (inb(ISA_PORT_SIS) & ISA_SIS_READY) {219*data = inb(ISA_PORT_SDI);220return 0;221} else {222timeout--;223udelay(10);224}225}226return 1;227}228229void230act2000_isa_receive(act2000_card *card)231{232u_char c;233234if (test_and_set_bit(ACT2000_LOCK_RX, (void *) &card->ilock) != 0)235return;236while (!act2000_isa_readb(card, &c)) {237if (card->idat.isa.rcvidx < 8) {238card->idat.isa.rcvhdr[card->idat.isa.rcvidx++] = c;239if (card->idat.isa.rcvidx == 8) {240int valid = actcapi_chkhdr(card, (actcapi_msghdr *)&card->idat.isa.rcvhdr);241242if (valid) {243card->idat.isa.rcvlen = ((actcapi_msghdr *)&card->idat.isa.rcvhdr)->len;244card->idat.isa.rcvskb = dev_alloc_skb(card->idat.isa.rcvlen);245if (card->idat.isa.rcvskb == NULL) {246card->idat.isa.rcvignore = 1;247printk(KERN_WARNING248"act2000_isa_receive: no memory\n");249test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock);250return;251}252memcpy(skb_put(card->idat.isa.rcvskb, 8), card->idat.isa.rcvhdr, 8);253card->idat.isa.rcvptr = skb_put(card->idat.isa.rcvskb, card->idat.isa.rcvlen - 8);254} else {255card->idat.isa.rcvidx = 0;256printk(KERN_WARNING257"act2000_isa_receive: Invalid CAPI msg\n");258{259int i; __u8 *p; __u8 *t; __u8 tmp[30];260for (i = 0, p = (__u8 *)&card->idat.isa.rcvhdr, t = tmp; i < 8; i++)261t += sprintf(t, "%02x ", *(p++));262printk(KERN_WARNING "act2000_isa_receive: %s\n", tmp);263}264}265}266} else {267if (!card->idat.isa.rcvignore)268*card->idat.isa.rcvptr++ = c;269if (++card->idat.isa.rcvidx >= card->idat.isa.rcvlen) {270if (!card->idat.isa.rcvignore) {271skb_queue_tail(&card->rcvq, card->idat.isa.rcvskb);272act2000_schedule_rx(card);273}274card->idat.isa.rcvidx = 0;275card->idat.isa.rcvlen = 8;276card->idat.isa.rcvignore = 0;277card->idat.isa.rcvskb = NULL;278card->idat.isa.rcvptr = card->idat.isa.rcvhdr;279}280}281}282if (!(card->flags & ACT2000_FLAGS_IVALID)) {283/* In polling mode, schedule myself */284if ((card->idat.isa.rcvidx) &&285(card->idat.isa.rcvignore ||286(card->idat.isa.rcvidx < card->idat.isa.rcvlen)))287act2000_schedule_poll(card);288}289test_and_clear_bit(ACT2000_LOCK_RX, (void *) &card->ilock);290}291292void293act2000_isa_send(act2000_card * card)294{295unsigned long flags;296struct sk_buff *skb;297actcapi_msg *msg;298int l;299300if (test_and_set_bit(ACT2000_LOCK_TX, (void *) &card->ilock) != 0)301return;302while (1) {303spin_lock_irqsave(&card->lock, flags);304if (!(card->sbuf)) {305if ((card->sbuf = skb_dequeue(&card->sndq))) {306card->ack_msg = card->sbuf->data;307msg = (actcapi_msg *)card->sbuf->data;308if ((msg->hdr.cmd.cmd == 0x86) &&309(msg->hdr.cmd.subcmd == 0) ) {310/* Save flags in message */311card->need_b3ack = msg->msg.data_b3_req.flags;312msg->msg.data_b3_req.flags = 0;313}314}315}316spin_unlock_irqrestore(&card->lock, flags);317if (!(card->sbuf)) {318/* No more data to send */319test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock);320return;321}322skb = card->sbuf;323l = 0;324while (skb->len) {325if (act2000_isa_writeb(card, *(skb->data))) {326/* Fifo is full, but more data to send */327test_and_clear_bit(ACT2000_LOCK_TX, (void *) &card->ilock);328/* Schedule myself */329act2000_schedule_tx(card);330return;331}332skb_pull(skb, 1);333l++;334}335msg = (actcapi_msg *)card->ack_msg;336if ((msg->hdr.cmd.cmd == 0x86) &&337(msg->hdr.cmd.subcmd == 0) ) {338/*339* If it's user data, reset data-ptr340* and put skb into ackq.341*/342skb->data = card->ack_msg;343/* Restore flags in message */344msg->msg.data_b3_req.flags = card->need_b3ack;345skb_queue_tail(&card->ackq, skb);346} else347dev_kfree_skb(skb);348card->sbuf = NULL;349}350}351352/*353* Get firmware ID, check for 'ISDN' signature.354*/355static int356act2000_isa_getid(act2000_card * card)357{358359act2000_fwid fid;360u_char *p = (u_char *) & fid;361int count = 0;362363while (1) {364if (count > 510)365return -EPROTO;366if (act2000_isa_readb(card, p++))367break;368count++;369}370if (count <= 20) {371printk(KERN_WARNING "act2000: No Firmware-ID!\n");372return -ETIME;373}374*p = '\0';375fid.revlen[0] = '\0';376if (strcmp(fid.isdn, "ISDN")) {377printk(KERN_WARNING "act2000: Wrong Firmware-ID!\n");378return -EPROTO;379}380if ((p = strchr(fid.revision, '\n')))381*p = '\0';382printk(KERN_INFO "act2000: Firmware-ID: %s\n", fid.revision);383if (card->flags & ACT2000_FLAGS_IVALID) {384printk(KERN_DEBUG "Enabling Interrupts ...\n");385act2000_isa_enable_irq(card);386}387return 0;388}389390/*391* Download microcode into card, check Firmware signature.392*/393int394act2000_isa_download(act2000_card * card, act2000_ddef __user * cb)395{396unsigned int length;397int l;398int c;399long timeout;400u_char *b;401u_char __user *p;402u_char *buf;403act2000_ddef cblock;404405if (!act2000_isa_reset(card->port))406return -ENXIO;407msleep_interruptible(500);408if (copy_from_user(&cblock, cb, sizeof(cblock)))409return -EFAULT;410length = cblock.length;411p = cblock.buffer;412if (!access_ok(VERIFY_READ, p, length))413return -EFAULT;414buf = kmalloc(1024, GFP_KERNEL);415if (!buf)416return -ENOMEM;417timeout = 0;418while (length) {419l = (length > 1024) ? 1024 : length;420c = 0;421b = buf;422if (copy_from_user(buf, p, l)) {423kfree(buf);424return -EFAULT;425}426while (c < l) {427if (act2000_isa_writeb(card, *b++)) {428printk(KERN_WARNING429"act2000: loader timed out"430" len=%d c=%d\n", length, c);431kfree(buf);432return -ETIME;433}434c++;435}436length -= l;437p += l;438}439kfree(buf);440msleep_interruptible(500);441return (act2000_isa_getid(card));442}443444445