diff options
Diffstat (limited to 'drivers/isdn/hisax/teles3.c')
-rw-r--r-- | drivers/isdn/hisax/teles3.c | 499 |
1 files changed, 499 insertions, 0 deletions
diff --git a/drivers/isdn/hisax/teles3.c b/drivers/isdn/hisax/teles3.c new file mode 100644 index 00000000..220b919f --- /dev/null +++ b/drivers/isdn/hisax/teles3.c @@ -0,0 +1,499 @@ +/* $Id: teles3.c,v 2.19.2.4 2004/01/13 23:48:39 keil Exp $ + * + * low level stuff for Teles 16.3 & PNP isdn cards + * + * Author Karsten Keil + * Copyright by Karsten Keil <keil@isdn4linux.de> + * + * This software may be used and distributed according to the terms + * of the GNU General Public License, incorporated herein by reference. + * + * Thanks to Jan den Ouden + * Fritz Elfert + * Beat Doebeli + * + */ +#include <linux/init.h> +#include <linux/isapnp.h> +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" + +static const char *teles3_revision = "$Revision: 2.19.2.4 $"; + +#define byteout(addr, val) outb(val, addr) +#define bytein(addr) inb(addr) + +static inline u_char +readreg(unsigned int adr, u_char off) +{ + return (bytein(adr + off)); +} + +static inline void +writereg(unsigned int adr, u_char off, u_char data) +{ + byteout(adr + off, data); +} + + +static inline void +read_fifo(unsigned int adr, u_char *data, int size) +{ + insb(adr, data, size); +} + +static void +write_fifo(unsigned int adr, u_char *data, int size) +{ + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.teles3.isac, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.teles3.isac, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char *data, int size) +{ + read_fifo(cs->hw.teles3.isacfifo, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char *data, int size) +{ + write_fifo(cs->hw.teles3.isacfifo, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readreg(cs->hw.teles3.hscx[hscx], offset)); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.teles3.hscx[hscx], offset, value); +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) readreg(cs->hw.teles3.hscx[nr], reg) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.teles3.hscx[nr], reg, data) +#define READHSCXFIFO(cs, nr, ptr, cnt) read_fifo(cs->hw.teles3.hscxfifo[nr], ptr, cnt) +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) write_fifo(cs->hw.teles3.hscxfifo[nr], ptr, cnt) + +#include "hscx_irq.c" + +static irqreturn_t +teles3_interrupt(int intno, void *dev_id) +{ +#define MAXCOUNT 5 + struct IsdnCardState *cs = dev_id; + u_char val; + u_long flags; + int count = 0; + + spin_lock_irqsave(&cs->lock, flags); + val = readreg(cs->hw.teles3.hscx[1], HSCX_ISTA); +Start_HSCX: + if (val) + hscx_int_main(cs, val); + val = readreg(cs->hw.teles3.isac, ISAC_ISTA); +Start_ISAC: + if (val) + isac_interrupt(cs, val); + count++; + val = readreg(cs->hw.teles3.hscx[1], HSCX_ISTA); + if (val && count < MAXCOUNT) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = readreg(cs->hw.teles3.isac, ISAC_ISTA); + if (val && count < MAXCOUNT) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + if (count >= MAXCOUNT) + printk(KERN_WARNING "Teles3: more than %d loops in teles3_interrupt\n", count); + writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0xFF); + writereg(cs->hw.teles3.hscx[1], HSCX_MASK, 0xFF); + writereg(cs->hw.teles3.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.teles3.isac, ISAC_MASK, 0x0); + writereg(cs->hw.teles3.hscx[0], HSCX_MASK, 0x0); + writereg(cs->hw.teles3.hscx[1], HSCX_MASK, 0x0); + spin_unlock_irqrestore(&cs->lock, flags); + return IRQ_HANDLED; +} + +static inline void +release_ioregs(struct IsdnCardState *cs, int mask) +{ + if (mask & 1) + release_region(cs->hw.teles3.isac + 32, 32); + if (mask & 2) + release_region(cs->hw.teles3.hscx[0] + 32, 32); + if (mask & 4) + release_region(cs->hw.teles3.hscx[1] + 32, 32); +} + +static void +release_io_teles3(struct IsdnCardState *cs) +{ + if (cs->typ == ISDN_CTYPE_TELESPCMCIA) { + release_region(cs->hw.teles3.hscx[1], 96); + } else { + if (cs->hw.teles3.cfg_reg) { + if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + release_region(cs->hw.teles3.cfg_reg, 1); + } else { + release_region(cs->hw.teles3.cfg_reg, 8); + } + } + release_ioregs(cs, 0x7); + } +} + +static int +reset_teles3(struct IsdnCardState *cs) +{ + u_char irqcfg; + + if (cs->typ != ISDN_CTYPE_TELESPCMCIA) { + if ((cs->hw.teles3.cfg_reg) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA)) { + switch (cs->irq) { + case 2: + case 9: + irqcfg = 0x00; + break; + case 3: + irqcfg = 0x02; + break; + case 4: + irqcfg = 0x04; + break; + case 5: + irqcfg = 0x06; + break; + case 10: + irqcfg = 0x08; + break; + case 11: + irqcfg = 0x0A; + break; + case 12: + irqcfg = 0x0C; + break; + case 15: + irqcfg = 0x0E; + break; + default: + return (1); + } + byteout(cs->hw.teles3.cfg_reg + 4, irqcfg); + HZDELAY(HZ / 10 + 1); + byteout(cs->hw.teles3.cfg_reg + 4, irqcfg | 1); + HZDELAY(HZ / 10 + 1); + } else if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + byteout(cs->hw.teles3.cfg_reg, 0xff); + HZDELAY(2); + byteout(cs->hw.teles3.cfg_reg, 0x00); + HZDELAY(2); + } else { + /* Reset off for 16.3 PnP , thanks to Georg Acher */ + byteout(cs->hw.teles3.isac + 0x3c, 0); + HZDELAY(2); + byteout(cs->hw.teles3.isac + 0x3c, 1); + HZDELAY(2); + } + } + return (0); +} + +static int +Teles_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + u_long flags; + + switch (mt) { + case CARD_RESET: + spin_lock_irqsave(&cs->lock, flags); + reset_teles3(cs); + spin_unlock_irqrestore(&cs->lock, flags); + return (0); + case CARD_RELEASE: + release_io_teles3(cs); + return (0); + case CARD_INIT: + spin_lock_irqsave(&cs->lock, flags); + inithscxisac(cs, 3); + spin_unlock_irqrestore(&cs->lock, flags); + return (0); + case CARD_TEST: + return (0); + } + return (0); +} + +#ifdef __ISAPNP__ + +static struct isapnp_device_id teles_ids[] __devinitdata = { + { ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x2110), + ISAPNP_VENDOR('T', 'A', 'G'), ISAPNP_FUNCTION(0x2110), + (unsigned long) "Teles 16.3 PnP" }, + { ISAPNP_VENDOR('C', 'T', 'X'), ISAPNP_FUNCTION(0x0), + ISAPNP_VENDOR('C', 'T', 'X'), ISAPNP_FUNCTION(0x0), + (unsigned long) "Creatix 16.3 PnP" }, + { ISAPNP_VENDOR('C', 'P', 'Q'), ISAPNP_FUNCTION(0x1002), + ISAPNP_VENDOR('C', 'P', 'Q'), ISAPNP_FUNCTION(0x1002), + (unsigned long) "Compaq ISDN S0" }, + { 0, } +}; + +static struct isapnp_device_id *ipid __devinitdata = &teles_ids[0]; +static struct pnp_card *pnp_c __devinitdata = NULL; +#endif + +int __devinit +setup_teles3(struct IsdnCard *card) +{ + u_char val; + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, teles3_revision); + printk(KERN_INFO "HiSax: Teles IO driver Rev. %s\n", HiSax_getrev(tmp)); + if ((cs->typ != ISDN_CTYPE_16_3) && (cs->typ != ISDN_CTYPE_PNP) + && (cs->typ != ISDN_CTYPE_TELESPCMCIA) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA)) + return (0); + +#ifdef __ISAPNP__ + if (!card->para[1] && isapnp_present()) { + struct pnp_dev *pnp_d; + while (ipid->card_vendor) { + if ((pnp_c = pnp_find_card(ipid->card_vendor, + ipid->card_device, pnp_c))) { + pnp_d = NULL; + if ((pnp_d = pnp_find_dev(pnp_c, + ipid->vendor, ipid->function, pnp_d))) { + int err; + + printk(KERN_INFO "HiSax: %s detected\n", + (char *)ipid->driver_data); + pnp_disable_dev(pnp_d); + err = pnp_activate_dev(pnp_d); + if (err < 0) { + printk(KERN_WARNING "%s: pnp_activate_dev ret(%d)\n", + __func__, err); + return (0); + } + card->para[3] = pnp_port_start(pnp_d, 2); + card->para[2] = pnp_port_start(pnp_d, 1); + card->para[1] = pnp_port_start(pnp_d, 0); + card->para[0] = pnp_irq(pnp_d, 0); + if (!card->para[0] || !card->para[1] || !card->para[2]) { + printk(KERN_ERR "Teles PnP:some resources are missing %ld/%lx/%lx\n", + card->para[0], card->para[1], card->para[2]); + pnp_disable_dev(pnp_d); + return (0); + } + break; + } else { + printk(KERN_ERR "Teles PnP: PnP error card found, no device\n"); + } + } + ipid++; + pnp_c = NULL; + } + if (!ipid->card_vendor) { + printk(KERN_INFO "Teles PnP: no ISAPnP card found\n"); + return (0); + } + } +#endif + if (cs->typ == ISDN_CTYPE_16_3) { + cs->hw.teles3.cfg_reg = card->para[1]; + switch (cs->hw.teles3.cfg_reg) { + case 0x180: + case 0x280: + case 0x380: + cs->hw.teles3.cfg_reg |= 0xc00; + break; + } + cs->hw.teles3.isac = cs->hw.teles3.cfg_reg - 0x420; + cs->hw.teles3.hscx[0] = cs->hw.teles3.cfg_reg - 0xc20; + cs->hw.teles3.hscx[1] = cs->hw.teles3.cfg_reg - 0x820; + } else if (cs->typ == ISDN_CTYPE_TELESPCMCIA) { + cs->hw.teles3.cfg_reg = 0; + cs->hw.teles3.hscx[0] = card->para[1] - 0x20; + cs->hw.teles3.hscx[1] = card->para[1]; + cs->hw.teles3.isac = card->para[1] + 0x20; + } else if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + cs->hw.teles3.cfg_reg = card->para[3]; + cs->hw.teles3.isac = card->para[2] - 32; + cs->hw.teles3.hscx[0] = card->para[1] - 32; + cs->hw.teles3.hscx[1] = card->para[1]; + } else { /* PNP */ + cs->hw.teles3.cfg_reg = 0; + cs->hw.teles3.isac = card->para[1] - 32; + cs->hw.teles3.hscx[0] = card->para[2] - 32; + cs->hw.teles3.hscx[1] = card->para[2]; + } + cs->irq = card->para[0]; + cs->hw.teles3.isacfifo = cs->hw.teles3.isac + 0x3e; + cs->hw.teles3.hscxfifo[0] = cs->hw.teles3.hscx[0] + 0x3e; + cs->hw.teles3.hscxfifo[1] = cs->hw.teles3.hscx[1] + 0x3e; + if (cs->typ == ISDN_CTYPE_TELESPCMCIA) { + if (!request_region(cs->hw.teles3.hscx[1], 96, "HiSax Teles PCMCIA")) { + printk(KERN_WARNING + "HiSax: %s ports %x-%x already in use\n", + CardType[cs->typ], + cs->hw.teles3.hscx[1], + cs->hw.teles3.hscx[1] + 96); + return (0); + } + cs->irq_flags |= IRQF_SHARED; /* cardbus can share */ + } else { + if (cs->hw.teles3.cfg_reg) { + if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + if (!request_region(cs->hw.teles3.cfg_reg, 1, "teles3 cfg")) { + printk(KERN_WARNING + "HiSax: %s config port %x already in use\n", + CardType[card->typ], + cs->hw.teles3.cfg_reg); + return (0); + } + } else { + if (!request_region(cs->hw.teles3.cfg_reg, 8, "teles3 cfg")) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.teles3.cfg_reg, + cs->hw.teles3.cfg_reg + 8); + return (0); + } + } + } + if (!request_region(cs->hw.teles3.isac + 32, 32, "HiSax isac")) { + printk(KERN_WARNING + "HiSax: %s isac ports %x-%x already in use\n", + CardType[cs->typ], + cs->hw.teles3.isac + 32, + cs->hw.teles3.isac + 64); + if (cs->hw.teles3.cfg_reg) { + if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + release_region(cs->hw.teles3.cfg_reg, 1); + } else { + release_region(cs->hw.teles3.cfg_reg, 8); + } + } + return (0); + } + if (!request_region(cs->hw.teles3.hscx[0] + 32, 32, "HiSax hscx A")) { + printk(KERN_WARNING + "HiSax: %s hscx A ports %x-%x already in use\n", + CardType[cs->typ], + cs->hw.teles3.hscx[0] + 32, + cs->hw.teles3.hscx[0] + 64); + if (cs->hw.teles3.cfg_reg) { + if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + release_region(cs->hw.teles3.cfg_reg, 1); + } else { + release_region(cs->hw.teles3.cfg_reg, 8); + } + } + release_ioregs(cs, 1); + return (0); + } + if (!request_region(cs->hw.teles3.hscx[1] + 32, 32, "HiSax hscx B")) { + printk(KERN_WARNING + "HiSax: %s hscx B ports %x-%x already in use\n", + CardType[cs->typ], + cs->hw.teles3.hscx[1] + 32, + cs->hw.teles3.hscx[1] + 64); + if (cs->hw.teles3.cfg_reg) { + if (cs->typ == ISDN_CTYPE_COMPAQ_ISA) { + release_region(cs->hw.teles3.cfg_reg, 1); + } else { + release_region(cs->hw.teles3.cfg_reg, 8); + } + } + release_ioregs(cs, 3); + return (0); + } + } + if ((cs->hw.teles3.cfg_reg) && (cs->typ != ISDN_CTYPE_COMPAQ_ISA)) { + if ((val = bytein(cs->hw.teles3.cfg_reg + 0)) != 0x51) { + printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n", + cs->hw.teles3.cfg_reg + 0, val); + release_io_teles3(cs); + return (0); + } + if ((val = bytein(cs->hw.teles3.cfg_reg + 1)) != 0x93) { + printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n", + cs->hw.teles3.cfg_reg + 1, val); + release_io_teles3(cs); + return (0); + } + val = bytein(cs->hw.teles3.cfg_reg + 2);/* 0x1e=without AB + * 0x1f=with AB + * 0x1c 16.3 ??? + * 0x39 16.3 1.1 + * 0x38 16.3 1.3 + * 0x46 16.3 with AB + Video (Teles-Vision) + */ + if (val != 0x46 && val != 0x39 && val != 0x38 && val != 0x1c && val != 0x1e && val != 0x1f) { + printk(KERN_WARNING "Teles: 16.3 Byte at %x is %x\n", + cs->hw.teles3.cfg_reg + 2, val); + release_io_teles3(cs); + return (0); + } + } + printk(KERN_INFO + "HiSax: %s config irq:%d isac:0x%X cfg:0x%X\n", + CardType[cs->typ], cs->irq, + cs->hw.teles3.isac + 32, cs->hw.teles3.cfg_reg); + printk(KERN_INFO + "HiSax: hscx A:0x%X hscx B:0x%X\n", + cs->hw.teles3.hscx[0] + 32, cs->hw.teles3.hscx[1] + 32); + + setup_isac(cs); + if (reset_teles3(cs)) { + printk(KERN_WARNING "Teles3: wrong IRQ\n"); + release_io_teles3(cs); + return (0); + } + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->BC_Read_Reg = &ReadHSCX; + cs->BC_Write_Reg = &WriteHSCX; + cs->BC_Send_Data = &hscx_fill_fifo; + cs->cardmsg = &Teles_card_msg; + cs->irq_func = &teles3_interrupt; + ISACVersion(cs, "Teles3:"); + if (HscxVersion(cs, "Teles3:")) { + printk(KERN_WARNING + "Teles3: wrong HSCX versions check IO address\n"); + release_io_teles3(cs); + return (0); + } + return (1); +} |