| /* $Id: bkm_a4t.c,v 1.22.2.4 2004/01/14 16:04:48 keil Exp $ |
| * |
| * low level stuff for T-Berkom A4T |
| * |
| * Author Roland Klabunde |
| * Copyright by Roland Klabunde <R.Klabunde@Berkom.de> |
| * |
| * This software may be used and distributed according to the terms |
| * of the GNU General Public License, incorporated herein by reference. |
| * |
| */ |
| |
| |
| #include <linux/init.h> |
| #include "hisax.h" |
| #include "isac.h" |
| #include "hscx.h" |
| #include "jade.h" |
| #include "isdnl1.h" |
| #include <linux/pci.h> |
| #include "bkm_ax.h" |
| |
| static const char *bkm_a4t_revision = "$Revision: 1.22.2.4 $"; |
| |
| |
| static inline u_char |
| readreg(unsigned int ale, unsigned long adr, u_char off) |
| { |
| register u_int ret; |
| unsigned int *po = (unsigned int *) adr; /* Postoffice */ |
| |
| *po = (GCS_2 | PO_WRITE | off); |
| __WAITI20__(po); |
| *po = (ale | PO_READ); |
| __WAITI20__(po); |
| ret = *po; |
| return ((unsigned char) ret); |
| } |
| |
| |
| static inline void |
| readfifo(unsigned int ale, unsigned long adr, u_char off, u_char * data, int size) |
| { |
| int i; |
| for (i = 0; i < size; i++) |
| *data++ = readreg(ale, adr, off); |
| } |
| |
| |
| static inline void |
| writereg(unsigned int ale, unsigned long adr, u_char off, u_char data) |
| { |
| unsigned int *po = (unsigned int *) adr; /* Postoffice */ |
| *po = (GCS_2 | PO_WRITE | off); |
| __WAITI20__(po); |
| *po = (ale | PO_WRITE | data); |
| __WAITI20__(po); |
| } |
| |
| |
| static inline void |
| writefifo(unsigned int ale, unsigned long adr, u_char off, u_char * data, int size) |
| { |
| int i; |
| |
| for (i = 0; i < size; i++) |
| writereg(ale, adr, off, *data++); |
| } |
| |
| |
| /* Interface functions */ |
| |
| static u_char |
| ReadISAC(struct IsdnCardState *cs, u_char offset) |
| { |
| return (readreg(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, offset)); |
| } |
| |
| static void |
| WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) |
| { |
| writereg(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, offset, value); |
| } |
| |
| static void |
| ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) |
| { |
| readfifo(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, 0, data, size); |
| } |
| |
| static void |
| WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) |
| { |
| writefifo(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, 0, data, size); |
| } |
| |
| static u_char |
| ReadJADE(struct IsdnCardState *cs, int jade, u_char offset) |
| { |
| return (readreg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, offset + (jade == -1 ? 0 : (jade ? 0xC0 : 0x80)))); |
| } |
| |
| static void |
| WriteJADE(struct IsdnCardState *cs, int jade, u_char offset, u_char value) |
| { |
| writereg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, offset + (jade == -1 ? 0 : (jade ? 0xC0 : 0x80)), value); |
| } |
| |
| /* |
| * fast interrupt JADE stuff goes here |
| */ |
| |
| #define READJADE(cs, nr, reg) readreg(cs->hw.ax.jade_ale,\ |
| cs->hw.ax.jade_adr, reg + (nr == -1 ? 0 : (nr ? 0xC0 : 0x80))) |
| #define WRITEJADE(cs, nr, reg, data) writereg(cs->hw.ax.jade_ale,\ |
| cs->hw.ax.jade_adr, reg + (nr == -1 ? 0 : (nr ? 0xC0 : 0x80)), data) |
| |
| #define READJADEFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ax.jade_ale,\ |
| cs->hw.ax.jade_adr, (nr == -1 ? 0 : (nr ? 0xC0 : 0x80)), ptr, cnt) |
| #define WRITEJADEFIFO(cs, nr, ptr, cnt) writefifo( cs->hw.ax.jade_ale,\ |
| cs->hw.ax.jade_adr, (nr == -1 ? 0 : (nr ? 0xC0 : 0x80)), ptr, cnt) |
| |
| #include "jade_irq.c" |
| |
| static irqreturn_t |
| bkm_interrupt(int intno, void *dev_id) |
| { |
| struct IsdnCardState *cs = dev_id; |
| u_char val = 0; |
| u_long flags; |
| I20_REGISTER_FILE *pI20_Regs; |
| |
| spin_lock_irqsave(&cs->lock, flags); |
| pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base); |
| |
| /* ISDN interrupt pending? */ |
| if (pI20_Regs->i20IntStatus & intISDN) { |
| /* Reset the ISDN interrupt */ |
| pI20_Regs->i20IntStatus = intISDN; |
| /* Disable ISDN interrupt */ |
| pI20_Regs->i20IntCtrl &= ~intISDN; |
| /* Channel A first */ |
| val = readreg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, jade_HDLC_ISR + 0x80); |
| if (val) { |
| jade_int_main(cs, val, 0); |
| } |
| /* Channel B */ |
| val = readreg(cs->hw.ax.jade_ale, cs->hw.ax.jade_adr, jade_HDLC_ISR + 0xC0); |
| if (val) { |
| jade_int_main(cs, val, 1); |
| } |
| /* D-Channel */ |
| val = readreg(cs->hw.ax.isac_ale, cs->hw.ax.isac_adr, ISAC_ISTA); |
| if (val) { |
| isac_interrupt(cs, val); |
| } |
| /* Reenable ISDN interrupt */ |
| pI20_Regs->i20IntCtrl |= intISDN; |
| spin_unlock_irqrestore(&cs->lock, flags); |
| return IRQ_HANDLED; |
| } else { |
| spin_unlock_irqrestore(&cs->lock, flags); |
| return IRQ_NONE; |
| } |
| } |
| |
| static void |
| release_io_bkm(struct IsdnCardState *cs) |
| { |
| if (cs->hw.ax.base) { |
| iounmap((void *) cs->hw.ax.base); |
| cs->hw.ax.base = 0; |
| } |
| } |
| |
| static void |
| enable_bkm_int(struct IsdnCardState *cs, unsigned bEnable) |
| { |
| if (cs->typ == ISDN_CTYPE_BKM_A4T) { |
| I20_REGISTER_FILE *pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base); |
| if (bEnable) |
| pI20_Regs->i20IntCtrl |= (intISDN | intPCI); |
| else |
| /* CAUTION: This disables the video capture driver too */ |
| pI20_Regs->i20IntCtrl &= ~(intISDN | intPCI); |
| } |
| } |
| |
| static void |
| reset_bkm(struct IsdnCardState *cs) |
| { |
| if (cs->typ == ISDN_CTYPE_BKM_A4T) { |
| I20_REGISTER_FILE *pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base); |
| /* Issue the I20 soft reset */ |
| pI20_Regs->i20SysControl = 0xFF; /* all in */ |
| mdelay(10); |
| /* Remove the soft reset */ |
| pI20_Regs->i20SysControl = sysRESET | 0xFF; |
| mdelay(10); |
| /* Set our configuration */ |
| pI20_Regs->i20SysControl = sysRESET | sysCFG; |
| /* Issue ISDN reset */ |
| pI20_Regs->i20GuestControl = guestWAIT_CFG | |
| g_A4T_JADE_RES | |
| g_A4T_ISAR_RES | |
| g_A4T_ISAC_RES | |
| g_A4T_JADE_BOOTR | |
| g_A4T_ISAR_BOOTR; |
| mdelay(10); |
| |
| /* Remove RESET state from ISDN */ |
| pI20_Regs->i20GuestControl &= ~(g_A4T_ISAC_RES | |
| g_A4T_JADE_RES | |
| g_A4T_ISAR_RES); |
| mdelay(10); |
| } |
| } |
| |
| static int |
| BKM_card_msg(struct IsdnCardState *cs, int mt, void *arg) |
| { |
| u_long flags; |
| |
| switch (mt) { |
| case CARD_RESET: |
| /* Disable ints */ |
| spin_lock_irqsave(&cs->lock, flags); |
| enable_bkm_int(cs, 0); |
| reset_bkm(cs); |
| spin_unlock_irqrestore(&cs->lock, flags); |
| return (0); |
| case CARD_RELEASE: |
| /* Sanity */ |
| spin_lock_irqsave(&cs->lock, flags); |
| enable_bkm_int(cs, 0); |
| reset_bkm(cs); |
| spin_unlock_irqrestore(&cs->lock, flags); |
| release_io_bkm(cs); |
| return (0); |
| case CARD_INIT: |
| spin_lock_irqsave(&cs->lock, flags); |
| clear_pending_isac_ints(cs); |
| clear_pending_jade_ints(cs); |
| initisac(cs); |
| initjade(cs); |
| /* Enable ints */ |
| enable_bkm_int(cs, 1); |
| spin_unlock_irqrestore(&cs->lock, flags); |
| return (0); |
| case CARD_TEST: |
| return (0); |
| } |
| return (0); |
| } |
| |
| static int __devinit a4t_pci_probe(struct pci_dev *dev_a4t, |
| struct IsdnCardState *cs, |
| u_int *found, |
| u_int *pci_memaddr) |
| { |
| u16 sub_sys; |
| u16 sub_vendor; |
| |
| sub_vendor = dev_a4t->subsystem_vendor; |
| sub_sys = dev_a4t->subsystem_device; |
| if ((sub_sys == PCI_DEVICE_ID_BERKOM_A4T) && (sub_vendor == PCI_VENDOR_ID_BERKOM)) { |
| if (pci_enable_device(dev_a4t)) |
| return (0); /* end loop & function */ |
| *found = 1; |
| *pci_memaddr = pci_resource_start(dev_a4t, 0); |
| cs->irq = dev_a4t->irq; |
| return (1); /* end loop */ |
| } |
| |
| return (-1); /* continue looping */ |
| } |
| |
| static int __devinit a4t_cs_init(struct IsdnCard *card, |
| struct IsdnCardState *cs, |
| u_int pci_memaddr) |
| { |
| I20_REGISTER_FILE *pI20_Regs; |
| |
| if (!cs->irq) { /* IRQ range check ?? */ |
| printk(KERN_WARNING "HiSax: Telekom A4T: No IRQ\n"); |
| return (0); |
| } |
| cs->hw.ax.base = (long) ioremap(pci_memaddr, 4096); |
| /* Check suspecious address */ |
| pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base); |
| if ((pI20_Regs->i20IntStatus & 0x8EFFFFFF) != 0) { |
| printk(KERN_WARNING "HiSax: Telekom A4T address " |
| "%lx-%lx suspicious\n", |
| cs->hw.ax.base, cs->hw.ax.base + 4096); |
| iounmap((void *) cs->hw.ax.base); |
| cs->hw.ax.base = 0; |
| return (0); |
| } |
| cs->hw.ax.isac_adr = cs->hw.ax.base + PO_OFFSET; |
| cs->hw.ax.jade_adr = cs->hw.ax.base + PO_OFFSET; |
| cs->hw.ax.isac_ale = GCS_1; |
| cs->hw.ax.jade_ale = GCS_3; |
| |
| printk(KERN_INFO "HiSax: Telekom A4T: Card configured at " |
| "0x%lX IRQ %d\n", |
| cs->hw.ax.base, cs->irq); |
| |
| setup_isac(cs); |
| cs->readisac = &ReadISAC; |
| cs->writeisac = &WriteISAC; |
| cs->readisacfifo = &ReadISACfifo; |
| cs->writeisacfifo = &WriteISACfifo; |
| cs->BC_Read_Reg = &ReadJADE; |
| cs->BC_Write_Reg = &WriteJADE; |
| cs->BC_Send_Data = &jade_fill_fifo; |
| cs->cardmsg = &BKM_card_msg; |
| cs->irq_func = &bkm_interrupt; |
| cs->irq_flags |= IRQF_SHARED; |
| ISACVersion(cs, "Telekom A4T:"); |
| /* Jade version */ |
| JadeVersion(cs, "Telekom A4T:"); |
| |
| return (1); |
| } |
| |
| static struct pci_dev *dev_a4t __devinitdata = NULL; |
| |
| int __devinit |
| setup_bkm_a4t(struct IsdnCard *card) |
| { |
| struct IsdnCardState *cs = card->cs; |
| char tmp[64]; |
| u_int pci_memaddr = 0, found = 0; |
| int ret; |
| |
| strcpy(tmp, bkm_a4t_revision); |
| printk(KERN_INFO "HiSax: T-Berkom driver Rev. %s\n", HiSax_getrev(tmp)); |
| if (cs->typ == ISDN_CTYPE_BKM_A4T) { |
| cs->subtyp = BKM_A4T; |
| } else |
| return (0); |
| |
| while ((dev_a4t = hisax_find_pci_device(PCI_VENDOR_ID_ZORAN, |
| PCI_DEVICE_ID_ZORAN_36120, dev_a4t))) { |
| ret = a4t_pci_probe(dev_a4t, cs, &found, &pci_memaddr); |
| if (!ret) |
| return (0); |
| if (ret > 0) |
| break; |
| } |
| if (!found) { |
| printk(KERN_WARNING "HiSax: Telekom A4T: Card not found\n"); |
| return (0); |
| } |
| if (!pci_memaddr) { |
| printk(KERN_WARNING "HiSax: Telekom A4T: " |
| "No Memory base address\n"); |
| return (0); |
| } |
| |
| return a4t_cs_init(card, cs, pci_memaddr); |
| } |