diff --git a/drivers/isdn/hisax/bkm_a4t.c b/drivers/isdn/hisax/bkm_a4t.c new file mode 100644 index 00000000..22df2934 --- /dev/null +++ b/drivers/isdn/hisax/bkm_a4t.c @@ -0,0 +1,385 @@ +/* $Id$ + * bkm_a4t.c low level stuff for T-Berkom A4T + * derived from the original file sedlbauer.c + * derived from the original file niccy.c + * derived from the original file netjet.c + * + * Author Roland Klabunde (R.Klabunde@Berkom.de) + * + * $Log$ + * + */ + +#define __NO_VERSION__ + +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "jade.h" +#include "isdnl1.h" +#include "bkm_ax.h" +#include +#ifndef COMPAT_HAS_NEW_PCI +#include +#endif + +extern const char *CardType[]; + +const char *bkm_a4t_revision = "$Revision$"; + + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_int ret; + long flags; + unsigned int *po = (unsigned int *) adr; /* Postoffice */ + save_flags(flags); + cli(); + *po = (GCS_2 | PO_WRITE | off); + __WAITI20__(po); + *po = (ale | PO_READ); + __WAITI20__(po); + ret = *po; + restore_flags(flags); + return ((unsigned char) ret); +} + + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo read without cli because it's allready done */ + int i; + for (i = 0; i < size; i++) + *data++ = readreg(ale, adr, off); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + unsigned int *po = (unsigned int *) adr; /* Postoffice */ + save_flags(flags); + cli(); + *po = (GCS_2 | PO_WRITE | off); + __WAITI20__(po); + *po = (ale | PO_WRITE | data); + __WAITI20__(po); + restore_flags(flags); +} + + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo write without cli because it's allready done */ + 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 void +bkm_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val = 0; + I20_REGISTER_FILE *pI20_Regs; + + if (!cs) { + printk(KERN_WARNING "HiSax: Telekom A4T: Spurious interrupt!\n"); + return; + } + 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; + } +} + +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) +{ + long flags; + + if (cs->typ == ISDN_CTYPE_BKM_A4T) { + I20_REGISTER_FILE *pI20_Regs = (I20_REGISTER_FILE *) (cs->hw.ax.base); + save_flags(flags); + sti(); + /* Issue the I20 soft reset */ + pI20_Regs->i20SysControl = 0xFF; /* all in */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10 * HZ) / 1000); + /* Remove the soft reset */ + pI20_Regs->i20SysControl = sysRESET | 0xFF; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10 * HZ) / 1000); + /* 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; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10 * HZ) / 1000); + + /* Remove RESET state from ISDN */ + pI20_Regs->i20GuestControl &= ~(g_A4T_ISAC_RES | + g_A4T_JADE_RES | + g_A4T_ISAR_RES); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10 * HZ) / 1000); + restore_flags(flags); + } +} + +static int +BKM_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + /* Disable ints */ + enable_bkm_int(cs, 0); + reset_bkm(cs); + return (0); + case CARD_RELEASE: + /* Sanity */ + enable_bkm_int(cs, 0); + reset_bkm(cs); + release_io_bkm(cs); + return (0); + case CARD_SETIRQ: + return (request_irq(cs->irq, &bkm_interrupt, I4L_IRQ_FLAG | SA_SHIRQ, "HiSax", cs)); + case CARD_INIT: + clear_pending_isac_ints(cs); + clear_pending_jade_ints(cs); + initisac(cs); + initjade(cs); + /* Enable ints */ + enable_bkm_int(cs, 1); + return (0); + case CARD_TEST: + return (0); + } + return (0); +} + +#ifdef COMPAT_HAS_NEW_PCI +static struct pci_dev *dev_a4t __initdata = NULL; +#else +static int pci_index __initdata = 0; +#endif + +__initfunc(int + setup_bkm_a4t(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + u_int pci_memaddr = 0, found = 0; + I20_REGISTER_FILE *pI20_Regs; +#if CONFIG_PCI +#ifndef COMPAT_HAS_NEW_PCI + u_char pci_bus, pci_device_fn, pci_irq = 0; +#endif +#endif + + 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); + +#if CONFIG_PCI +#ifdef COMPAT_HAS_NEW_PCI + if (!pci_present()) { + printk(KERN_ERR "bkm_a4t: no PCI bus present\n"); + return (0); + } + if ((dev_a4t = pci_find_device(I20_VENDOR_ID, I20_DEVICE_ID, dev_a4t))) { + u_int sub_sys_id = 0; + + pci_read_config_dword(dev_a4t, PCI_SUBSYSTEM_ID, &sub_sys_id); + if (sub_sys_id == ((A4T_SUBSYS_ID << 16) | A4T_SUBVEN_ID)) { + found = 1; + pci_memaddr = dev_a4t->base_address[0]; + cs->irq = dev_a4t->irq; + } + } +#else + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device(I20_VENDOR_ID, + I20_DEVICE_ID, + pci_index, + &pci_bus, + &pci_device_fn) == PCIBIOS_SUCCESSFUL) { + u_int sub_sys_id = 0; + + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_SUBSYSTEM_ID, &sub_sys_id); + if (sub_sys_id == ((A4T_SUBSYS_ID << 16) | A4T_SUBVEN_ID)) { + found = 1; + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + cs->irq = pci_irq; + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_0, &pci_memaddr); + break; + } + } + } +#endif /* COMPAT_HAS_NEW_PCI */ + if (!found) { + printk(KERN_WARNING "HiSax: %s: Card not found\n", CardType[card->typ]); + return (0); + } +#ifndef COMPAT_HAS_NEW_PCI + pci_index++; +#endif + if (!cs->irq) { /* IRQ range check ?? */ + printk(KERN_WARNING "HiSax: %s: No IRQ\n", CardType[card->typ]); + return (0); + } + if (!pci_memaddr) { + printk(KERN_WARNING "HiSax: %s: No Memory base address\n", CardType[card->typ]); + return (0); + } + pci_memaddr &= PCI_BASE_ADDRESS_MEM_MASK; + cs->hw.ax.base = (u_int) 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: %s address %x-%x suspecious\n", + CardType[card->typ], 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; +#else + printk(KERN_WARNING "HiSax: %s: NO_PCI_BIOS\n", CardType[card->typ]); + printk(KERN_WARNING "HiSax: %s: unable to configure\n", CardType[card->typ]); + return (0); +#endif /* CONFIG_PCI */ + printk(KERN_INFO "HiSax: %s: Card configured at 0x%X IRQ %d\n", + CardType[card->typ], cs->hw.ax.base, cs->irq); + + reset_bkm(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; + ISACVersion(cs, "Telekom A4T:"); + /* Jade version */ + JadeVersion(cs, "Telekom A4T:"); + return (1); +} diff --git a/drivers/isdn/hisax/bkm_a8.c b/drivers/isdn/hisax/bkm_a8.c new file mode 100644 index 00000000..aacee148 --- /dev/null +++ b/drivers/isdn/hisax/bkm_a8.c @@ -0,0 +1,511 @@ +/* $Id$ + * bkm_a8.c low level stuff for Scitel Quadro (4*S0, passive) + * derived from the original file sedlbauer.c + * derived from the original file niccy.c + * derived from the original file netjet.c + * + * Author Roland Klabunde (R.Klabunde@Berkom.de) + * + * $Log$ + * + */ +#define __NO_VERSION__ + +#include "hisax.h" +#include "isac.h" +#include "ipac.h" +#include "hscx.h" +#include "isdnl1.h" +#include "bkm_ax.h" +#include +#ifndef COMPAT_HAS_NEW_PCI +#include +#endif + +#define ATTEMPT_PCI_REMAPPING /* Required for PLX rev 1 */ + +extern const char *CardType[]; + +const char sct_quadro_revision[] = "$Revision$"; + +/* To survive the startup phase */ +typedef struct { + u_int active; /* true/false */ + u_int base; /* ipac base address */ +} IPAC_STATE; + +static IPAC_STATE ipac_state[4 + 1] __initdata = +{ + {0, 0}, /* dummy */ + {0, 0}, /* SCT_1 */ + {0, 0}, /* SCT_2 */ + {0, 0}, /* SCT_3 */ + {0, 0} /* SCT_4 */ +}; + +static const char *sct_quadro_subtypes[] = +{ + "", + "#1", + "#2", + "#3", + "#4" +}; + + +#define wordout(addr,val) outw(val,addr) +#define wordin(addr) inw(addr) + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + long flags; + save_flags(flags); + cli(); + wordout(ale, off); + ret = wordin(adr) & 0xFF; + restore_flags(flags); + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo read without cli because it's allready done */ + int i; + wordout(ale, off); + for (i = 0; i < size; i++) + data[i] = wordin(adr) & 0xFF; +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + save_flags(flags); + cli(); + wordout(ale, off); + wordout(adr, data); + restore_flags(flags); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo write without cli because it's allready done */ + int i; + wordout(ale, off); + for (i = 0; i < size; i++) + wordout(adr, data[i]); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.ax.base, cs->hw.ax.data_adr, offset | 0x80)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.ax.base, cs->hw.ax.data_adr, offset | 0x80, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.ax.base, cs->hw.ax.data_adr, 0x80, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.ax.base, cs->hw.ax.data_adr, 0x80, data, size); +} + + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readreg(cs->hw.ax.base, cs->hw.ax.data_adr, offset + (hscx ? 0x40 : 0))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.ax.base, cs->hw.ax.data_adr, offset + (hscx ? 0x40 : 0), value); +} + +/* Check whether the specified ipac is already active or not */ +static int +is_ipac_active(u_int ipac_nr) +{ + return (ipac_state[ipac_nr].active); +} + +/* Set the specific ipac to active */ +static void +set_ipac_active(u_int ipac_nr, u_int active) +{ + /* set activation state */ + ipac_state[ipac_nr].active = active; +} + +/* + * fast interrupt HSCX stuff goes here + */ + +#define READHSCX(cs, nr, reg) readreg(cs->hw.ax.base, \ + cs->hw.ax.data_adr, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.ax.base, \ + cs->hw.ax.data_adr, reg + (nr ? 0x40 : 0), data) +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.ax.base, \ + cs->hw.ax.data_adr, (nr ? 0x40 : 0), ptr, cnt) +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.ax.base, \ + cs->hw.ax.data_adr, (nr ? 0x40 : 0), ptr, cnt) + +#include "hscx_irq.c" + +static void +bkm_interrupt_ipac(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char ista, val, icnt = 20; + int i; + if (!cs) { + printk(KERN_WARNING "HiSax: Scitel Quadro: Spurious interrupt!\n"); + return; + } + ista = readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ISTA); + + Start_IPAC: + if (cs->debug & L1_DEB_IPAC) + debugl1(cs, "IPAC ISTA %02X", ista); + if (ista & 0x0f) { + val = readreg(cs->hw.ax.base, cs->hw.ax.data_adr, HSCX_ISTA + 0x40); + if (ista & 0x01) + val |= 0x01; + if (ista & 0x04) + val |= 0x02; + if (ista & 0x08) + val |= 0x04; + if (val) { + hscx_int_main(cs, val); + } + } + if (ista & 0x20) { + val = 0xfe & readreg(cs->hw.ax.base, cs->hw.ax.data_adr, ISAC_ISTA | 0x80); + if (val) { + isac_interrupt(cs, val); + } + } + if (ista & 0x10) { + val = 0x01; + isac_interrupt(cs, val); + } + ista = readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ISTA); + if ((ista & 0x3f) && icnt) { + icnt--; + goto Start_IPAC; + } + if (!icnt) + printk(KERN_WARNING "HiSax: %s (%s) IRQ LOOP\n", + CardType[cs->typ], + sct_quadro_subtypes[cs->subtyp]); + writereg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_MASK, 0xFF); + writereg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_MASK, 0xC0); + + /* Read out all interrupt sources from currently not active ipacs */ + /* "Handle" all interrupts from currently not active ipac by reading the regs */ + for (i = SCT_1; i <= SCT_4; i++) + if (!is_ipac_active(i)) { + u_int base = ipac_state[i].base; + if (readreg(base, base + 4, 0xC1)) { + readreg(base, base + 4, 0xA0); + readreg(base, base + 4, 0xA4); + readreg(base, base + 4, 0x20); + readreg(base, base + 4, 0x24); + readreg(base, base + 4, 0x60); + readreg(base, base + 4, 0x64); + readreg(base, base + 4, 0xC1); + readreg(base, base + 4, ISAC_CIR0 + 0x80); + } + } +} + + +void +release_io_sct_quadro(struct IsdnCardState *cs) +{ + /* ?? */ +} + +static void +enable_bkm_int(struct IsdnCardState *cs, unsigned bEnable) +{ + if (cs->typ == ISDN_CTYPE_SCT_QUADRO) { + if (bEnable) + wordout(cs->hw.ax.plx_adr + 0x4C, (wordin(cs->hw.ax.plx_adr + 0x4C) | 0x41)); + else + /* Issue general di only if no ipac is active */ + if (!is_ipac_active(SCT_1) && + !is_ipac_active(SCT_2) && + !is_ipac_active(SCT_3) && + !is_ipac_active(SCT_4)) + wordout(cs->hw.ax.plx_adr + 0x4C, (wordin(cs->hw.ax.plx_adr + 0x4C) & ~0x41)); + } +} + +static void +reset_bkm(struct IsdnCardState *cs) +{ + long flags; + + if (cs->typ == ISDN_CTYPE_SCT_QUADRO) { + if (!is_ipac_active(SCT_1) && + !is_ipac_active(SCT_2) && + !is_ipac_active(SCT_3) && + !is_ipac_active(SCT_4)) { + /* Issue total reset only if no ipac is active */ + wordout(cs->hw.ax.plx_adr + 0x50, (wordin(cs->hw.ax.plx_adr + 0x50) & ~4)); + + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10 * HZ) / 1000); + + /* Remove the soft reset */ + wordout(cs->hw.ax.plx_adr + 0x50, (wordin(cs->hw.ax.plx_adr + 0x50) | 4)); + + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10 * HZ) / 1000); + restore_flags(flags); + } + } +} + +static int +BKM_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + /* Disable ints */ + set_ipac_active(cs->subtyp, 0); + enable_bkm_int(cs, 0); + reset_bkm(cs); + return (0); + case CARD_RELEASE: + /* Sanity */ + set_ipac_active(cs->subtyp, 0); + enable_bkm_int(cs, 0); + reset_bkm(cs); + release_io_sct_quadro(cs); + return (0); + case CARD_SETIRQ: + return (request_irq(cs->irq, &bkm_interrupt_ipac, I4L_IRQ_FLAG | SA_SHIRQ, "HiSax", cs)); + case CARD_INIT: + cs->debug |= L1_DEB_IPAC; + set_ipac_active(cs->subtyp, 1); + inithscxisac(cs, 3); + /* Enable ints */ + enable_bkm_int(cs, 1); + return (0); + case CARD_TEST: + return (0); + } + return (0); +} + +#ifdef COMPAT_HAS_NEW_PCI +static struct pci_dev *dev_a8 __initdata = NULL; +#else +static int pci_index __initdata = 0; +#endif + +__initfunc(int + setup_sct_quadro(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; +#if CONFIG_PCI + u_char pci_bus = 0, pci_device_fn = 0, pci_irq = 0, pci_rev_id; + u_int found = 0; + u_int pci_ioaddr1, pci_ioaddr2, pci_ioaddr3, pci_ioaddr4, pci_ioaddr5; +#endif + + strcpy(tmp, sct_quadro_revision); + printk(KERN_INFO "HiSax: T-Berkom driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ == ISDN_CTYPE_SCT_QUADRO) { + cs->subtyp = SCT_1; /* Preset */ + } else + return (0); + + /* Identify subtype by para[0] */ + if (card->para[0] >= SCT_1 && card->para[0] <= SCT_4) + cs->subtyp = card->para[0]; + else + printk(KERN_WARNING "HiSax: %s: Invalid subcontroller in configuration, default to 1\n", + CardType[card->typ]); +#if CONFIG_PCI +#ifdef COMPAT_HAS_NEW_PCI + if (!pci_present()) { + printk(KERN_ERR "bkm_a4t: no PCI bus present\n"); + return (0); + } + if ((dev_a8 = pci_find_device(PLX_VENDOR_ID, PLX_DEVICE_ID, dev_a8))) { + u_int sub_sys_id = 0; + + pci_read_config_dword(dev_a8, PCI_SUBSYSTEM_ID, &sub_sys_id); + if (sub_sys_id == ((SCT_SUBSYS_ID << 16) | SCT_SUBVEN_ID)) { + found = 1; + pci_ioaddr1 = dev_a8->base_address[1]; + pci_irq = dev_a8->irq; + pci_bus = dev_a8->bus->number; + pci_device_fn = dev_a8->devfn; + } + } +#else + for (; pci_index < 0xff; pci_index++) { + if (pcibios_find_device( + PLX_VENDOR_ID, + PLX_DEVICE_ID, + pci_index, + &pci_bus, + &pci_device_fn) == PCIBIOS_SUCCESSFUL) { + + u_int sub_sys_id = 0; + + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_SUBSYSTEM_ID, &sub_sys_id); + if (sub_sys_id == ((SCT_SUBSYS_ID << 16) | SCT_SUBVEN_ID)) { + found = 1; + pcibios_read_config_byte(pci_bus, pci_device_fn, + PCI_INTERRUPT_LINE, &pci_irq); + pcibios_read_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, &pci_ioaddr1); + cs->irq = pci_irq; + break; + } + } + } +#endif /* COMPAT_HAS_NEW_PCI */ + if (!found) { + printk(KERN_WARNING "HiSax: %s (%s): Card not found\n", + CardType[card->typ], + sct_quadro_subtypes[cs->subtyp]); + return (0); + } +#ifndef COMPAT_HAS_NEW_PCI + pci_index++; /* need for more as one card */ +#endif + if (!pci_irq) { /* IRQ range check ?? */ + printk(KERN_WARNING "HiSax: %s (%s): No IRQ\n", + CardType[card->typ], + sct_quadro_subtypes[cs->subtyp]); + return (0); + } +#ifdef ATTEMPT_PCI_REMAPPING +/* HACK: PLX revision 1 bug: PLX address bit 7 must not be set */ + pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_REVISION_ID, &pci_rev_id); + if ((pci_ioaddr1 & 0x80) && (pci_rev_id == 1)) { + printk(KERN_WARNING "HiSax: %s (%s): PLX rev 1, remapping required!\n", + CardType[card->typ], + sct_quadro_subtypes[cs->subtyp]); + /* Restart PCI negotiation */ + pcibios_write_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, (u_int) - 1); + /* Move up by 0x80 byte */ + pci_ioaddr1 += 0x80; + pci_ioaddr1 &= PCI_BASE_ADDRESS_IO_MASK; + pcibios_write_config_dword(pci_bus, pci_device_fn, + PCI_BASE_ADDRESS_1, pci_ioaddr1); +#ifdef COMPAT_HAS_NEW_PCI + dev_a8->base_address[1] = pci_ioaddr1; +#endif /* COMPAT_HAS_NEW_PCI */ + } +/* End HACK */ +#endif + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &pci_ioaddr1); + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_2, &pci_ioaddr2); + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_3, &pci_ioaddr3); + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_4, &pci_ioaddr4); + pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_5, &pci_ioaddr5); + if (!pci_ioaddr1 || !pci_ioaddr2 || !pci_ioaddr3 || !pci_ioaddr4 || !pci_ioaddr5) { + printk(KERN_WARNING "HiSax: %s (%s): No IO base address(es)\n", + CardType[card->typ], + sct_quadro_subtypes[cs->subtyp]); + return (0); + } + pci_ioaddr1 &= PCI_BASE_ADDRESS_IO_MASK; + pci_ioaddr2 &= PCI_BASE_ADDRESS_IO_MASK; + pci_ioaddr3 &= PCI_BASE_ADDRESS_IO_MASK; + pci_ioaddr4 &= PCI_BASE_ADDRESS_IO_MASK; + pci_ioaddr5 &= PCI_BASE_ADDRESS_IO_MASK; + /* Take over */ + cs->irq = pci_irq; + /* pci_ioaddr1 is unique to all subdevices */ + /* pci_ioaddr2 is for the fourth subdevice only */ + /* pci_ioaddr3 is for the third subdevice only */ + /* pci_ioaddr4 is for the second subdevice only */ + /* pci_ioaddr5 is for the first subdevice only */ + cs->hw.ax.plx_adr = pci_ioaddr1; + /* Enter all ipac_base addresses */ + ipac_state[SCT_1].base = pci_ioaddr5 + 0x00; + ipac_state[SCT_2].base = pci_ioaddr4 + 0x08; + ipac_state[SCT_3].base = pci_ioaddr3 + 0x10; + ipac_state[SCT_4].base = pci_ioaddr2 + 0x20; + /* For isac and hscx control path */ + cs->hw.ax.base = ipac_state[cs->subtyp].base; + /* For isac and hscx data path */ + cs->hw.ax.data_adr = cs->hw.ax.base + 4; +#else + printk(KERN_WARNING "HiSax: %s (%s): NO_PCI_BIOS\n", + CardType[card->typ], + sct_quadro_subtypes[cs->subtyp]); + printk(KERN_WARNING "HiSax: %s (%s): Unable to configure\n", + CardType[card->typ], + sct_quadro_subtypes[cs->subtyp]); + return (0); +#endif /* CONFIG_PCI */ + printk(KERN_INFO "HiSax: %s (%s) configured at 0x%.4X, 0x%.4X, 0x%.4X and IRQ %d\n", + CardType[card->typ], + sct_quadro_subtypes[cs->subtyp], + cs->hw.ax.plx_adr, + cs->hw.ax.base, + cs->hw.ax.data_adr, + cs->irq); + + test_and_set_bit(HW_IPAC, &cs->HW_Flags); + + /* Disable all currently not active ipacs */ + if (!is_ipac_active(SCT_1)) + set_ipac_active(SCT_1, 0); + if (!is_ipac_active(SCT_2)) + set_ipac_active(SCT_2, 0); + if (!is_ipac_active(SCT_3)) + set_ipac_active(SCT_3, 0); + if (!is_ipac_active(SCT_4)) + set_ipac_active(SCT_4, 0); + + /* Perfom general reset (if possible) */ + reset_bkm(cs); + + 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 = &BKM_card_msg; + + printk(KERN_INFO "HiSax: %s (%s): IPAC Version %d\n", + CardType[card->typ], + sct_quadro_subtypes[cs->subtyp], + readreg(cs->hw.ax.base, cs->hw.ax.data_adr, IPAC_ID)); + return (1); +} diff --git a/drivers/isdn/hisax/bkm_ax.h b/drivers/isdn/hisax/bkm_ax.h new file mode 100644 index 00000000..70508eb1 --- /dev/null +++ b/drivers/isdn/hisax/bkm_ax.h @@ -0,0 +1,130 @@ +/* $Id$ + * bkm_ax.h low level decls for T-Berkom cards A4T and Scitel Quadro (4*S0, passive) + * + * Author Roland Klabunde (R.Klabunde@Berkom.de) + * + * $Log$ + * + * + */ + +#ifndef __BKM_AX_H__ +#define __BKM_AX_H__ + +/* Supported boards (subtypes) */ +#define SCT_1 1 +#define SCT_2 2 +#define SCT_3 3 +#define SCT_4 4 +#define BKM_A4T 5 + + +/* A4T */ +#define I20_DEVICE_ID 0x6120 /* I20 PCI device ID */ +#define I20_VENDOR_ID 0x11DE /* I20 PCI vendor ID */ +#define A4T_SUBVEN_ID 0x0871 +#define A4T_SUBSYS_ID 0xFFA4 +/* Scitel Quadro */ +#define PLX_DEVICE_ID 0x9050 /* Scitel Quadro PLX */ +#define PLX_VENDOR_ID 0x10B5 +#define SCT_SUBVEN_ID 0x0871 +#define SCT_SUBSYS_ID 0xFFA8 + + +#define PLX_ADDR_PLX 0x14 /* Addr PLX configuration */ +#define PLX_ADDR_ISAC 0x18 /* Addr ISAC */ +#define PLX_ADDR_HSCX 0x1C /* Addr HSCX */ +#define PLX_ADDR_ALE 0x20 /* Addr ALE */ +#define PLX_ADDR_ALEPLUS 0x24 /* Next Addr behind ALE */ + +#define PLX_SUBVEN 0x2C /* Offset SubVendor */ +#define PLX_SUBSYS 0x2E /* Offset SubSystem */ + + +/* Application specific registers I20 (Siemens SZB6120H) */ +typedef struct { + /* Video front end horizontal configuration register */ + volatile u_int i20VFEHorzCfg; /* Offset 00 */ + /* Video front end vertical configuration register */ + volatile u_int i20VFEVertCfg; /* Offset 04 */ + /* Video front end scaler and pixel format register */ + volatile u_int i20VFEScaler; /* Offset 08 */ + /* Video display top register */ + volatile u_int i20VDispTop; /* Offset 0C */ + /* Video display bottom register */ + volatile u_int i20VDispBottom; /* Offset 10 */ + /* Video stride, status and frame grab register */ + volatile u_int i20VidFrameGrab;/* Offset 14 */ + /* Video display configuration register */ + volatile u_int i20VDispCfg; /* Offset 18 */ + /* Video masking map top */ + volatile u_int i20VMaskTop; /* Offset 1C */ + /* Video masking map bottom */ + volatile u_int i20VMaskBottom; /* Offset 20 */ + /* Overlay control register */ + volatile u_int i20OvlyControl; /* Offset 24 */ + /* System, PCI and general purpose pins control register */ + volatile u_int i20SysControl; /* Offset 28 */ +#define sysRESET 0x01000000 /* bit 24:Softreset (Low) */ + /* GPIO 4...0: Output fixed for our cfg! */ +#define sysCFG 0x000000E0 /* GPIO 7,6,5: Input */ + /* General purpose pins and guest bus control register */ + volatile u_int i20GuestControl;/* Offset 2C */ +#define guestWAIT_CFG 0x00005555 /* 4 PCI waits for all */ +#define guestISDN_INT_E 0x01000000 /* ISDN Int en (low) */ +#define guestVID_INT_E 0x02000000 /* Video interrupt en (low) */ +#define guestADI1_INT_R 0x04000000 /* ADI #1 int req (low) */ +#define guestADI2_INT_R 0x08000000 /* ADI #2 int req (low) */ +#define guestISDN_RES 0x10000000 /* ISDN reset bit (high) */ +#define guestADI1_INT_S 0x20000000 /* ADI #1 int pending (low) */ +#define guestADI2_INT_S 0x40000000 /* ADI #2 int pending (low) */ +#define guestISDN_INT_S 0x80000000 /* ISAC int pending (low) */ + +#define g_A4T_JADE_RES 0x01000000 /* JADE Reset (High) */ +#define g_A4T_ISAR_RES 0x02000000 /* ISAR Reset (High) */ +#define g_A4T_ISAC_RES 0x04000000 /* ISAC Reset (High) */ +#define g_A4T_JADE_BOOTR 0x08000000 /* JADE enable boot SRAM (Low) NOT USED */ +#define g_A4T_ISAR_BOOTR 0x10000000 /* ISAR enable boot SRAM (Low) NOT USED */ +#define g_A4T_JADE_INT_S 0x20000000 /* JADE interrupt pnd (Low) */ +#define g_A4T_ISAR_INT_S 0x40000000 /* ISAR interrupt pnd (Low) */ +#define g_A4T_ISAC_INT_S 0x80000000 /* ISAC interrupt pnd (Low) */ + + volatile u_int i20CodeSource; /* Offset 30 */ + volatile u_int i20CodeXferCtrl;/* Offset 34 */ + volatile u_int i20CodeMemPtr; /* Offset 38 */ + + volatile u_int i20IntStatus; /* Offset 3C */ + volatile u_int i20IntCtrl; /* Offset 40 */ +#define intISDN 0x40000000 /* GIRQ1En (ISAC/ADI) (High) */ +#define intVID 0x20000000 /* GIRQ0En (VSYNC) (High) */ +#define intCOD 0x10000000 /* CodRepIrqEn (High) */ +#define intPCI 0x01000000 /* PCI IntA enable (High) */ + + volatile u_int i20I2CCtrl; /* Offset 44 */ +} I20_REGISTER_FILE, *PI20_REGISTER_FILE; + +/* + * Postoffice structure for A4T + * + */ +#define PO_OFFSET 0x00000200 /* Postoffice offset from base */ + +#define GCS_0 0x00000000 /* Guest bus chip selects */ +#define GCS_1 0x00100000 +#define GCS_2 0x00200000 +#define GCS_3 0x00300000 + +#define PO_READ 0x00000000 /* R/W from/to guest bus */ +#define PO_WRITE 0x00800000 + +#define PO_PEND 0x02000000 + +#define POSTOFFICE(postoffice) *(volatile unsigned int*)(postoffice) + +/* Wait unlimited (don't worry) */ +#define __WAITI20__(postoffice) \ +do { \ + while ((POSTOFFICE(postoffice) & PO_PEND)) ; \ +} while (0) + +#endif /* __BKM_AX_H__ */ diff --git a/drivers/isdn/hisax/hfc_pci.c b/drivers/isdn/hisax/hfc_pci.c new file mode 100644 index 00000000..cf5e834a --- /dev/null +++ b/drivers/isdn/hisax/hfc_pci.c @@ -0,0 +1,1301 @@ +/* $Id$ + + * hfc_pci.c low level driver for CCD´s hfc-pci based cards + * + * Author Werner Cornelius (werner@isdn4linux.de) + * based on existing driver for CCD hfc ISA cards + * + * Copyright 1999 by Werner Cornelius (werner@isdn4linux.de) + * Copyright 1999 by Karsten Keil (keil@isdn4linux.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$ + * + * + */ + +#define __NO_VERSION__ +#include "hisax.h" +#include "hfc_pci.h" +#include "isdnl1.h" +#include +#ifndef COMPAT_HAS_NEW_PCI +#include +#endif +#include + +extern const char *CardType[]; + +static const char *hfcpci_revision = "$Revision$"; + +#if CONFIG_PCI +/*****************************/ +/* release D- and B-channels */ +/*****************************/ +void +releasehfcpci(struct IsdnCardState *cs) +{ + if (cs->bcs[0].hw.hfc.send) { + kfree(cs->bcs[0].hw.hfc.send); + cs->bcs[0].hw.hfc.send = NULL; + } + if (cs->bcs[1].hw.hfc.send) { + kfree(cs->bcs[1].hw.hfc.send); + cs->bcs[1].hw.hfc.send = NULL; + } +} + +/******************************************/ +/* free hardware resources used by driver */ +/******************************************/ +void +release_io_hfcpci(struct IsdnCardState *cs) +{ +#if CONFIG_PCI + pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, 0); /* disabe memory mapped ports + busmaster */ +#endif /* CONFIG_PCI */ + releasehfcpci(cs); + del_timer(&cs->hw.hfcpci.timer); + kfree(cs->hw.hfcpci.share_start); + cs->hw.hfcpci.share_start = NULL; + vfree(cs->hw.hfcpci.pci_io); +} + +/********************************************************************************/ +/* function called to reset the HFC PCI chip. A complete software reset of chip */ +/* and fifos is done. */ +/********************************************************************************/ +static void +reset_hfcpci(struct IsdnCardState *cs) +{ + long flags; + + pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, PCI_ENA_MEMIO); /* enable memory mapped ports, disable busmaster */ + printk(KERN_INFO "HFC_PCI: resetting card\n"); + pcibios_write_config_word(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, PCI_COMMAND, PCI_ENA_MEMIO + PCI_ENA_MASTER); /* enable memory ports + busmaster */ + Write_hfc(cs, HFCPCI_CIRM, HFCPCI_RESET); /* Reset On */ + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((30 * HZ) / 1000); /* Timeout 30ms */ + schedule(); + Write_hfc(cs, HFCPCI_CIRM, 0); /* Reset Off */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((20 * HZ) / 1000); /* Timeout 20ms */ + schedule(); + if (Read_hfc(cs, HFCPCI_STATUS) & 2) + printk(KERN_WARNING "HFC-PCI init bit busy\n"); + + cs->hw.hfcpci.fifo_en = 0x30; /* only D fifos enabled */ + Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en); + + Write_hfc(cs, HFCPCI_CLKDEL, 0x0e); /* ST-Bit delay for TE-Mode */ + Write_hfc(cs, HFCPCI_SCTRL_E, HFCPCI_AUTO_AWAKE); /* S/T Auto awake */ + cs->hw.hfcpci.ctmt = HFCPCI_TIM3_125 | HFCPCI_AUTO_TIMER; + Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt); + + cs->hw.hfcpci.int_m2 = HFCPCI_IRQ_ENABLE; + cs->hw.hfcpci.int_m1 = HFCPCI_INTS_B1TRANS | HFCPCI_INTS_B2TRANS | + HFCPCI_INTS_DTRANS | HFCPCI_INTS_B1REC | HFCPCI_INTS_B2REC | + HFCPCI_INTS_DREC | HFCPCI_INTS_L1STATE | HFCPCI_CLTIMER; + Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1); + Write_hfc(cs, HFCPCI_INT_M2, cs->hw.hfcpci.int_m2); + + /* Clear already pending ints */ + if (Read_hfc(cs, HFCPCI_INT_S1)); + if (Read_hfc(cs, HFCPCI_INT_S2)); + + Write_hfc(cs, HFCPCI_STATES, HFCPCI_LOAD_STATE | 2); /* HFC ST 2 */ + udelay(10); + Write_hfc(cs, HFCPCI_STATES, 2); /* HFC ST 2 */ + cs->hw.hfcpci.mst_m = HFCPCI_MASTER; /* HFC Master Mode */ + + Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m); + cs->hw.hfcpci.sctrl = 0; + Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl); + restore_flags(flags); +} + +/***************************************************/ +/* Timer function called when kernel timer expires */ +/***************************************************/ +static void +hfcpci_Timer(struct IsdnCardState *cs) +{ + cs->hw.hfcpci.timer.expires = jiffies + 75; + /* WD RESET */ +/* WriteReg(cs, HFCD_DATA, HFCD_CTMT, cs->hw.hfcpci.ctmt | 0x80); + add_timer(&cs->hw.hfcpci.timer); + */ +} + + +/*********************************/ +/* schedule a new D-channel task */ +/*********************************/ +static void +sched_event_D_pci(struct IsdnCardState *cs, int event) +{ + test_and_set_bit(event, &cs->event); + queue_task(&cs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +/*********************************/ +/* schedule a new b_channel task */ +/*********************************/ +static void +hfcpci_sched_event(struct BCState *bcs, int event) +{ + bcs->event |= 1 << event; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +/************************************************/ +/* select a b-channel entry matching and active */ +/************************************************/ +static +struct BCState * +Sel_BCS(struct IsdnCardState *cs, int channel) +{ + if (cs->bcs[0].mode && (cs->bcs[0].channel == channel)) + return (&cs->bcs[0]); + else if (cs->bcs[1].mode && (cs->bcs[1].channel == channel)) + return (&cs->bcs[1]); + else + return (NULL); +} + +/*********************************************/ +/* read a complete B-frame out of the buffer */ +/*********************************************/ +static struct sk_buff +* +hfcpci_empty_fifo(struct BCState *bcs, bzfifo_type * bz, u_char * bdata, int count) +{ + u_char *ptr, *ptr1, new_f2; + struct sk_buff *skb; + struct IsdnCardState *cs = bcs->cs; + int flags, total, maxlen, new_z2; + z_type *zp; + + save_flags(flags); + sti(); + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "hfcpci_empty_fifo"); + zp = &bz->za[bz->f2]; /* point to Z-Regs */ + new_z2 = zp->z2 + count; /* new position in fifo */ + if (new_z2 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z2 -= B_FIFO_SIZE; /* buffer wrap */ + new_f2 = (bz->f2 + 1) & MAX_B_FRAMES; + if ((count > HSCX_BUFMAX + 3) || (count < 4) || + (*(bdata + (zp->z1 - B_SUB_VAL)))) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hfcpci_empty_fifo: incoming packet invalid length %d or crc", count); + bz->za[new_f2].z2 = new_z2; + bz->f2 = new_f2; /* next buffer */ + skb = NULL; + } else if (!(skb = dev_alloc_skb(count - 3))) + printk(KERN_WARNING "HFCPCI: receive out of memory\n"); + else { + SET_SKB_FREE(skb); + total = count; + count -= 3; + ptr = skb_put(skb, count); + + if (zp->z1 >= zp->z2) + maxlen = count; /* complete transfer */ + else + maxlen = B_FIFO_SIZE + B_SUB_VAL - zp->z2; /* maximum */ + + ptr1 = bdata + (zp->z2 - B_SUB_VAL); /* start of data */ + memcpy(ptr, ptr1, maxlen); /* copy data */ + count -= maxlen; + + if (count) { /* rest remaining */ + ptr += maxlen; + ptr1 = bdata; /* start of buffer */ + memcpy(ptr, ptr1, count); /* rest */ + } + bz->za[new_f2].z2 = new_z2; + bz->f2 = new_f2; /* next buffer */ + + } + restore_flags(flags); + return (skb); +} + +/*******************************/ +/* D-channel receive procedure */ +/*******************************/ +static +int +receive_dmsg(struct IsdnCardState *cs) +{ + struct sk_buff *skb; + int maxlen; + int rcnt, total; + int count = 5; + u_char *ptr, *ptr1; + dfifo_type *df; + z_type *zp; + + df = &((fifo_area *) (cs->hw.hfcpci.fifos))->d_chan.d_rx; + if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + debugl1(cs, "rec_dmsg blocked"); + return (1); + } + while (((df->f1 & D_FREG_MASK) != (df->f2 & D_FREG_MASK)) && count--) { + zp = &df->za[df->f2 & D_FREG_MASK]; + rcnt = zp->z1 - zp->z2; + if (rcnt < 0) + rcnt += D_FIFO_SIZE; + rcnt++; + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "hfcpci recd f1(%d) f2(%d) z1(%x) z2(%x) cnt(%d)", + df->f1, df->f2, zp->z1, zp->z2, rcnt); + + if ((rcnt > MAX_DFRAME_LEN + 3) || (rcnt < 4) || + (df->data[zp->z1])) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "empty_fifo hfcpci paket inv. len %d or crc %d", rcnt, df->data[zp->z1]); + df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | (MAX_D_FRAMES + 1); /* next buffer */ + df->za[df->f2 & D_FREG_MASK].z2 = (zp->z2 + rcnt) & (D_FIFO_SIZE - 1); + } else if ((skb = dev_alloc_skb(rcnt - 3))) { + SET_SKB_FREE(skb); + total = rcnt; + rcnt -= 3; + ptr = skb_put(skb, rcnt); + + if (zp->z1 >= zp->z2) + maxlen = rcnt; /* complete transfer */ + else + maxlen = D_FIFO_SIZE - zp->z2; /* maximum */ + + ptr1 = df->data + zp->z2; /* start of data */ + memcpy(ptr, ptr1, maxlen); /* copy data */ + rcnt -= maxlen; + + if (rcnt) { /* rest remaining */ + ptr += maxlen; + ptr1 = df->data; /* start of buffer */ + memcpy(ptr, ptr1, rcnt); /* rest */ + } + df->f2 = ((df->f2 + 1) & MAX_D_FRAMES) | (MAX_D_FRAMES + 1); /* next buffer */ + df->za[df->f2 & D_FREG_MASK].z2 = (zp->z2 + total) & (D_FIFO_SIZE - 1); + + skb_queue_tail(&cs->rq, skb); + sched_event_D_pci(cs, D_RCVBUFREADY); + } else + printk(KERN_WARNING "HFC-PCI: D receive out of memory\n"); + } + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + return (1); +} + +/**********************************/ +/* B-channel main receive routine */ +/**********************************/ +void +main_rec_hfcpci(struct BCState *bcs) +{ + long flags; + struct IsdnCardState *cs = bcs->cs; + int rcnt; + int receive, count = 5; + struct sk_buff *skb; + bzfifo_type *bz; + u_char *bdata; + z_type *zp; + + + save_flags(flags); + if (bcs->channel) { + bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b2; + bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxdat_b2; + } else { + bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxbz_b1; + bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.rxdat_b1; + } + Begin: + count--; + cli(); + if (test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + debugl1(cs, "rec_data %d blocked", bcs->channel); + restore_flags(flags); + return; + } + sti(); + if (bz->f1 != bz->f2) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfcpci rec %d f1(%d) f2(%d)", + bcs->channel, bz->f1, bz->f2); + zp = &bz->za[bz->f2]; + + rcnt = zp->z1 - zp->z2; + if (rcnt < 0) + rcnt += B_FIFO_SIZE; + rcnt++; + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfcpci rec %d z1(%x) z2(%x) cnt(%d)", + bcs->channel, zp->z1, zp->z2, rcnt); + if ((skb = hfcpci_empty_fifo(bcs, bz, bdata, rcnt))) { + cli(); + skb_queue_tail(&bcs->rqueue, skb); + sti(); + hfcpci_sched_event(bcs, B_RCVBUFREADY); + } + rcnt = bz->f1 - bz->f2; + if (rcnt < 0) + rcnt += MAX_B_FRAMES + 1; + if (rcnt > 1) + receive = 1; + else + receive = 0; + } else + receive = 0; + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + if (count && receive) + goto Begin; + restore_flags(flags); + return; +} + +/**************************/ +/* D-channel send routine */ +/**************************/ +static void +hfcpci_fill_dfifo(struct IsdnCardState *cs) +{ + long flags; + int fcnt; + int count, new_z1, maxlen; + dfifo_type *df; + u_char *src, *dst, new_f1; + + if (!cs->tx_skb) + return; + if (cs->tx_skb->len <= 0) + return; + + df = &((fifo_area *) (cs->hw.hfcpci.fifos))->d_chan.d_tx; + + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "hfcpci_fill_Dfifo f1(%d) f2(%d) z1(f1)(%x)", + df->f1, df->f2, + df->za[df->f1 & D_FREG_MASK].z1); + fcnt = df->f1 - df->f2; /* frame count actually buffered */ + if (fcnt < 0) + fcnt += (MAX_D_FRAMES + 1); /* if wrap around */ + if (fcnt > (MAX_D_FRAMES - 1)) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "hfcpci_fill_Dfifo more as 14 frames"); + return; + } + /* now determine free bytes in FIFO buffer */ + count = df->za[df->f1 & D_FREG_MASK].z2 - df->za[df->f1 & D_FREG_MASK].z1; + if (count <= 0) + count += D_FIFO_SIZE; /* count now contains available bytes */ + + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "hfcpci_fill_Dfifo count(%ld/%d)", + cs->tx_skb->len, count); + if (count < cs->tx_skb->len) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "hfcpci_fill_Dfifo no fifo mem"); + return; + } + count = cs->tx_skb->len; /* get frame len */ + new_z1 = (df->za[df->f1 & D_FREG_MASK].z1 + count) & (D_FIFO_SIZE - 1); + new_f1 = ((df->f1 + 1) & D_FREG_MASK) | (D_FREG_MASK + 1); + src = cs->tx_skb->data; /* source pointer */ + dst = df->data + df->za[df->f1 & D_FREG_MASK].z1; + maxlen = D_FIFO_SIZE - df->za[df->f1 & D_FREG_MASK].z1; /* end fifo */ + if (maxlen > count) + maxlen = count; /* limit size */ + memcpy(dst, src, maxlen); /* first copy */ + + count -= maxlen; /* remaining bytes */ + if (count) { + dst = df->data; /* start of buffer */ + src += maxlen; /* new position */ + memcpy(dst, src, count); + } + save_flags(flags); + cli(); + df->za[new_f1 & D_FREG_MASK].z1 = new_z1; /* for next buffer */ + df->za[df->f1 & D_FREG_MASK].z1 = new_z1; /* new pos actual buffer */ + df->f1 = new_f1; /* next frame */ + restore_flags(flags); + + idev_kfree_skb(cs->tx_skb, FREE_WRITE); + cs->tx_skb = NULL; + return; +} + +/**************************/ +/* B-channel send routine */ +/**************************/ +static void +hfcpci_fill_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + int flags, maxlen, fcnt; + int count, new_z1; + bzfifo_type *bz; + u_char *bdata; + u_char new_f1, *src, *dst; + + if (!bcs->tx_skb) + return; + if (bcs->tx_skb->len <= 0) + return; + + save_flags(flags); + sti(); + + if (bcs->channel) { + bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b2; + bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txdat_b2; + } else { + bz = &((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txbz_b1; + bdata = ((fifo_area *) (cs->hw.hfcpci.fifos))->b_chans.txdat_b1; + } + + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfcpci_fill_fifo %d f1(%d) f2(%d) z1(f1)(%x)", + bcs->channel, bz->f1, bz->f2, + bz->za[bz->f1].z1); + + fcnt = bz->f1 - bz->f2; /* frame count actually buffered */ + if (fcnt < 0) + fcnt += (MAX_B_FRAMES + 1); /* if wrap around */ + if (fcnt > (MAX_B_FRAMES - 1)) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfcpci_fill_Bfifo more as 14 frames"); + restore_flags(flags); + return; + } + /* now determine free bytes in FIFO buffer */ + count = bz->za[bz->f1].z2 - bz->za[bz->f1].z1; + if (count <= 0) + count += B_FIFO_SIZE; /* count now contains available bytes */ + + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfcpci_fill_fifo %d count(%ld/%d),%lx", + bcs->channel, bcs->tx_skb->len, + count, current->state); + + if (count < bcs->tx_skb->len) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "hfcpci_fill_fifo no fifo mem"); + restore_flags(flags); + return; + } + count = bcs->tx_skb->len; /* get frame len */ + new_z1 = bz->za[bz->f1].z1 + count; /* new buffer Position */ + if (new_z1 >= (B_FIFO_SIZE + B_SUB_VAL)) + new_z1 -= B_FIFO_SIZE; /* buffer wrap */ + + new_f1 = ((bz->f1 + 1) & MAX_B_FRAMES); + src = bcs->tx_skb->data; /* source pointer */ + dst = bdata + (bz->za[bz->f1].z1 - B_SUB_VAL); + maxlen = (B_FIFO_SIZE + B_SUB_VAL) - bz->za[bz->f1].z1; /* end fifo */ + if (maxlen > count) + maxlen = count; /* limit size */ + memcpy(dst, src, maxlen); /* first copy */ + + count -= maxlen; /* remaining bytes */ + if (count) { + dst = bdata; /* start of buffer */ + src += maxlen; /* new position */ + memcpy(dst, src, count); + } + bcs->tx_cnt -= bcs->tx_skb->len; + if (bcs->st->lli.l1writewakeup && + (PACKET_NOACK != bcs->tx_skb->pkt_type)) + bcs->st->lli.l1writewakeup(bcs->st, bcs->tx_skb->len); + + cli(); + bz->za[new_f1].z1 = new_z1; /* for next buffer */ + bz->f1 = new_f1; /* next frame */ + restore_flags(flags); + + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + return; +} + +/*********************/ +/* Interrupt handler */ +/*********************/ +static void +hfcpci_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char exval; + struct BCState *bcs; + int count = 15; + long flags; + u_char val, stat; + + if (!cs) { + printk(KERN_WARNING "HFC-PCI: Spurious interrupt!\n"); + return; + } + if ((HFCPCI_ANYINT | HFCPCI_NBUSY) & + (stat = Read_hfc(cs, HFCPCI_STATUS))) { + val = Read_hfc(cs, HFCPCI_INT_S1); + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "HFC-PCI: stat(%02x) s1(%02x)", stat, val); + } else + return; + + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "HFC-PCI irq %x %s", val, + test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags) ? + "locked" : "unlocked"); + val &= cs->hw.hfcpci.int_m1; + if (val & 0x40) { /* TE state machine irq */ + exval = Read_hfc(cs, HFCPCI_STATES) & 0xf; + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ph_state chg %d->%d", cs->dc.hfcpci.ph_state, + exval); + cs->dc.hfcpci.ph_state = exval; + sched_event_D_pci(cs, D_L1STATECHANGE); + val &= ~0x40; + } + while (val) { + save_flags(flags); + cli(); + if (test_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + cs->hw.hfcpci.int_s1 |= val; + restore_flags(flags); + return; + } + if (cs->hw.hfcpci.int_s1 & 0x18) { + exval = val; + val = cs->hw.hfcpci.int_s1; + cs->hw.hfcpci.int_s1 = exval; + } + if (val & 0x08) { + if (!(bcs = Sel_BCS(cs, 0))) { + if (cs->debug) + debugl1(cs, "hfcpci spurious 0x08 IRQ"); + } else + main_rec_hfcpci(bcs); + } + if (val & 0x10) { + if (!(bcs = Sel_BCS(cs, 1))) { + if (cs->debug) + debugl1(cs, "hfcpci spurious 0x10 IRQ"); + } else + main_rec_hfcpci(bcs); + } + if (val & 0x01) { + if (!(bcs = Sel_BCS(cs, 0))) { + if (cs->debug) + debugl1(cs, "hfcpci spurious 0x01 IRQ"); + } else { + if (bcs->tx_skb) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcpci_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "fill_data %d blocked", bcs->channel); + } else { + if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcpci_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "fill_data %d blocked", bcs->channel); + } else { + hfcpci_sched_event(bcs, B_XMTBUFREADY); + } + } + } + } + if (val & 0x02) { + if (!(bcs = Sel_BCS(cs, 1))) { + if (cs->debug) + debugl1(cs, "hfcpci spurious 0x02 IRQ"); + } else { + if (bcs->tx_skb) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcpci_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "fill_data %d blocked", bcs->channel); + } else { + if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcpci_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "fill_data %d blocked", bcs->channel); + } else { + hfcpci_sched_event(bcs, B_XMTBUFREADY); + } + } + } + } + if (val & 0x20) { /* receive dframe */ + receive_dmsg(cs); + } + if (val & 0x04) { /* dframe transmitted */ + if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) + del_timer(&cs->dbusytimer); + if (test_and_clear_bit(FLG_L1_DBUSY, &cs->HW_Flags)) + sched_event_D_pci(cs, D_CLEARBUSY); + if (cs->tx_skb) { + if (cs->tx_skb->len) { + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcpci_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else { + debugl1(cs, "hfcpci_fill_dfifo irq blocked"); + } + goto afterXPR; + } else { + idev_kfree_skb(cs->tx_skb, FREE_WRITE); + cs->tx_cnt = 0; + cs->tx_skb = NULL; + } + } + if ((cs->tx_skb = skb_dequeue(&cs->sq))) { + cs->tx_cnt = 0; + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcpci_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else { + debugl1(cs, "hfcpci_fill_dfifo irq blocked"); + } + } else + sched_event_D_pci(cs, D_XMTBUFREADY); + } + afterXPR: + if (cs->hw.hfcpci.int_s1 && count--) { + val = cs->hw.hfcpci.int_s1; + cs->hw.hfcpci.int_s1 = 0; + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "HFC-PCI irq %x loop %d", val, 15 - count); + } else + val = 0; + restore_flags(flags); + } +} + +/********************************************************************/ +/* timer callback for D-chan busy resolution. Currently no function */ +/********************************************************************/ +static void +hfcpci_dbusy_timer(struct IsdnCardState *cs) +{ +#if 0 + struct PStack *stptr; + if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) { + if (cs->debug) + debugl1(cs, "D-Channel Busy"); + test_and_set_bit(FLG_L1_DBUSY, &cs->HW_Flags); + stptr = cs->stlist; + + while (stptr != NULL) { + stptr->l1.l1l2(stptr, PH_PAUSE | INDICATION, NULL); + stptr = stptr->next; + } + } +#endif +} + +/*************************************/ +/* Layer 1 D-channel hardware access */ +/*************************************/ +static void +HFCPCI_l1hw(struct PStack *st, int pr, void *arg) +{ + struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware; + struct sk_buff *skb = arg; + + switch (pr) { + case (PH_DATA | REQUEST): + if (cs->debug & DEB_DLOG_HEX) + LogFrame(cs, skb->data, skb->len); + if (cs->debug & DEB_DLOG_VERBOSE) + dlogframe(cs, skb, 0); + if (cs->tx_skb) { + skb_queue_tail(&cs->sq, skb); +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA Queued", 0); +#endif + } else { + cs->tx_skb = skb; + cs->tx_cnt = 0; +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA", 0); +#endif + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcpci_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "hfcpci_fill_dfifo blocked"); + + } + break; + case (PH_PULL | INDICATION): + if (cs->tx_skb) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, " l2l1 tx_skb exist this shouldn't happen"); + skb_queue_tail(&cs->sq, skb); + break; + } + if (cs->debug & DEB_DLOG_HEX) + LogFrame(cs, skb->data, skb->len); + if (cs->debug & DEB_DLOG_VERBOSE) + dlogframe(cs, skb, 0); + cs->tx_skb = skb; + cs->tx_cnt = 0; +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + Logl2Frame(cs, skb, "PH_DATA_PULLED", 0); +#endif + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcpci_fill_dfifo(cs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "hfcpci_fill_dfifo blocked"); + break; + case (PH_PULL | REQUEST): +#ifdef L2FRAME_DEBUG /* psa */ + if (cs->debug & L1_DEB_LAPD) + debugl1(cs, "-> PH_REQUEST_PULL"); +#endif + if (!cs->tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + case (HW_RESET | REQUEST): + Write_hfc(cs, HFCPCI_STATES, HFCPCI_LOAD_STATE | 3); /* HFC ST 3 */ + udelay(6); + Write_hfc(cs, HFCPCI_STATES, 3); /* HFC ST 2 */ + cs->hw.hfcpci.mst_m |= HFCPCI_MASTER; + Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m); + Write_hfc(cs, HFCPCI_STATES, HFCPCI_ACTIVATE | HFCPCI_DO_ACTION); + l1_msg(cs, HW_POWERUP | CONFIRM, NULL); + break; + case (HW_ENABLE | REQUEST): + Write_hfc(cs, HFCPCI_STATES, HFCPCI_ACTIVATE | HFCPCI_DO_ACTION); + break; + case (HW_DEACTIVATE | REQUEST): + cs->hw.hfcpci.mst_m &= ~HFCPCI_MASTER; + Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m); + break; + case (HW_INFO3 | REQUEST): + cs->hw.hfcpci.mst_m |= HFCPCI_MASTER; + Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m); + break; +#if 0 + case (HW_TESTLOOP | REQUEST): + u_char val = 0; + if (1 & (int) arg) + val |= 0x0c; + if (2 & (int) arg) + val |= 0x3; + if (test_bit(HW_IOM1, &cs->HW_Flags)) { + /* IOM 1 Mode */ + if (!val) { + cs->writeisac(cs, ISAC_SPCR, 0xa); + cs->writeisac(cs, ISAC_ADF1, 0x2); + } else { + cs->writeisac(cs, ISAC_SPCR, val); + cs->writeisac(cs, ISAC_ADF1, 0xa); + } + } else { + /* IOM 2 Mode */ + cs->writeisac(cs, ISAC_SPCR, val); + if (val) + cs->writeisac(cs, ISAC_ADF1, 0x8); + else + cs->writeisac(cs, ISAC_ADF1, 0x0); + } + break; +#endif + default: + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "hfcpci_l1hw unknown pr %4x", pr); + break; + } +} + +/***********************************************/ +/* called during init setting l1 stack pointer */ +/***********************************************/ +void +setstack_hfcpci(struct PStack *st, struct IsdnCardState *cs) +{ + st->l1.l1hw = HFCPCI_l1hw; +} + +/**************************************/ +/* send B-channel data if not blocked */ +/**************************************/ +static void +hfcpci_send_data(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + + if (!test_and_set_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags)) { + hfcpci_fill_fifo(bcs); + test_and_clear_bit(FLG_LOCK_ATOMIC, &cs->HW_Flags); + } else + debugl1(cs, "send_data %d blocked", bcs->channel); +} + +/***************************************************************/ +/* activate/deactivate hardware for selected channels and mode */ +/***************************************************************/ +void +mode_hfcpci(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HFCPCI bchannel mode %d bchan %d/%d", + mode, bc, bcs->channel); + bcs->mode = mode; + bcs->channel = bc; + switch (mode) { + case (L1_MODE_NULL): + if (bc) { + cs->hw.hfcpci.conn |= 0x18; + cs->hw.hfcpci.sctrl &= ~SCTRL_B2_ENA; + cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B2; + } else { + cs->hw.hfcpci.conn |= 0x3; + cs->hw.hfcpci.sctrl &= ~SCTRL_B1_ENA; + cs->hw.hfcpci.fifo_en &= ~HFCPCI_FIFOEN_B1; + } + break; + case (L1_MODE_TRANS): + if (bc) { + cs->hw.hfcpci.ctmt |= 2; + cs->hw.hfcpci.conn &= ~0x18; + cs->hw.hfcpci.sctrl |= SCTRL_B2_ENA; + cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2; + } else { + cs->hw.hfcpci.ctmt |= 1; + cs->hw.hfcpci.conn &= ~0x3; + cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA; + cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B1; + } + break; + case (L1_MODE_HDLC): + if (bc) { + cs->hw.hfcpci.ctmt &= ~2; + cs->hw.hfcpci.conn &= ~0x18; + cs->hw.hfcpci.sctrl |= SCTRL_B2_ENA; + cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B2; + } else { + cs->hw.hfcpci.ctmt &= ~1; + cs->hw.hfcpci.conn &= ~0x3; + cs->hw.hfcpci.sctrl |= SCTRL_B1_ENA; + cs->hw.hfcpci.fifo_en |= HFCPCI_FIFOEN_B1; + } + break; + } + Write_hfc(cs, HFCPCI_FIFO_EN, cs->hw.hfcpci.fifo_en); + Write_hfc(cs, HFCPCI_SCTRL, cs->hw.hfcpci.sctrl); + Write_hfc(cs, HFCPCI_SCTRL_R, cs->hw.hfcpci.sctrl & (SCTRL_B1_ENA + SCTRL_B2_ENA)); + Write_hfc(cs, HFCPCI_CTMT, cs->hw.hfcpci.ctmt); + Write_hfc(cs, HFCPCI_CONNECT, cs->hw.hfcpci.conn); +} + +/******************************/ +/* Layer2 -> Layer 1 Transfer */ +/******************************/ +static void +hfcpci_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + long flags; + + switch (pr) { + case (PH_DATA | REQUEST): + save_flags(flags); + cli(); + if (st->l1.bcs->tx_skb) { + skb_queue_tail(&st->l1.bcs->squeue, skb); + restore_flags(flags); + } else { + st->l1.bcs->tx_skb = skb; +/* test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + */ st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + restore_flags(flags); + } + break; + case (PH_PULL | INDICATION): + if (st->l1.bcs->tx_skb) { + printk(KERN_WARNING "hfc_l2l1: this shouldn't happen\n"); + break; + } + save_flags(flags); + cli(); +/* test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + */ st->l1.bcs->tx_skb = skb; + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + restore_flags(flags); + break; + case (PH_PULL | REQUEST): + if (!st->l1.bcs->tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + case (PH_ACTIVATE | REQUEST): + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + mode_hfcpci(st->l1.bcs, st->l1.mode, st->l1.bc); + l1_msg_b(st, pr, arg); + break; + case (PH_DEACTIVATE | REQUEST): + l1_msg_b(st, pr, arg); + break; + case (PH_DEACTIVATE | CONFIRM): + test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + mode_hfcpci(st->l1.bcs, 0, st->l1.bc); + st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); + break; + } +} + +/******************************************/ +/* deactivate B-channel access and queues */ +/******************************************/ +static void +close_hfcpci(struct BCState *bcs) +{ + mode_hfcpci(bcs, 0, bcs->channel); + if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) { + discard_queue(&bcs->rqueue); + discard_queue(&bcs->squeue); + if (bcs->tx_skb) { + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + } +} + +/*************************************/ +/* init B-channel queues and control */ +/*************************************/ +static int +open_hfcpcistate(struct IsdnCardState *cs, struct BCState *bcs) +{ + if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + bcs->event = 0; + bcs->tx_cnt = 0; + return (0); +} + +/*********************************/ +/* inits the stack for B-channel */ +/*********************************/ +static int +setstack_2b(struct PStack *st, struct BCState *bcs) +{ + bcs->channel = st->l1.bc; + if (open_hfcpcistate(st->l1.hardware, bcs)) + return (-1); + st->l1.bcs = bcs; + st->l2.l2l1 = hfcpci_l2l1; + setstack_manager(st); + bcs->st = st; + setstack_l1_B(st); + return (0); +} + +/***************************/ +/* handle L1 state changes */ +/***************************/ +static void +hfcpci_bh(struct IsdnCardState *cs) +{ +/* struct PStack *stptr; + */ + if (!cs) + return; +#if 0 + if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) { + if (cs->debug) + debugl1(cs, "D-Channel Busy cleared"); + stptr = cs->stlist; + while (stptr != NULL) { + stptr->l1.l1l2(stptr, PH_PAUSE | CONFIRM, NULL); + stptr = stptr->next; + } + } +#endif + if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) { + switch (cs->dc.hfcpci.ph_state) { + case (0): + l1_msg(cs, HW_RESET | INDICATION, NULL); + break; + case (3): + l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL); + break; + case (8): + l1_msg(cs, HW_RSYNC | INDICATION, NULL); + break; + case (6): + l1_msg(cs, HW_INFO2 | INDICATION, NULL); + break; + case (7): + l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL); + break; + default: + break; + } + } + if (test_and_clear_bit(D_RCVBUFREADY, &cs->event)) + DChannel_proc_rcv(cs); + if (test_and_clear_bit(D_XMTBUFREADY, &cs->event)) + DChannel_proc_xmt(cs); +} + + +/*************************************/ +/* Alloc memory send data for queues */ +/*************************************/ +__initfunc(unsigned int + *init_send_hfcpci(int cnt)) +{ + int i, *send; + + if (!(send = kmalloc(cnt * sizeof(unsigned int), GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for hfcpci.send\n"); + return (NULL); + } + for (i = 0; i < cnt; i++) + send[i] = 0x1fff; + return (send); +} + +/********************************/ +/* called for card init message */ +/********************************/ +__initfunc(void + inithfcpci(struct IsdnCardState *cs)) +{ + cs->setstack_d = setstack_hfcpci; + cs->dbusytimer.function = (void *) hfcpci_dbusy_timer; + cs->dbusytimer.data = (long) cs; + init_timer(&cs->dbusytimer); + cs->tqueue.routine = (void *) (void *) hfcpci_bh; +#if 0 + if (!cs->hw.hfcpci.send) + cs->hw.hfcpci.send = init_send_hfcpci(16); +#endif + if (!cs->bcs[0].hw.hfc.send) + cs->bcs[0].hw.hfc.send = init_send_hfcpci(32); + if (!cs->bcs[1].hw.hfc.send) + cs->bcs[1].hw.hfc.send = init_send_hfcpci(32); + cs->BC_Send_Data = &hfcpci_send_data; + cs->bcs[0].BC_SetStack = setstack_2b; + cs->bcs[1].BC_SetStack = setstack_2b; + cs->bcs[0].BC_Close = close_hfcpci; + cs->bcs[1].BC_Close = close_hfcpci; + mode_hfcpci(cs->bcs, 0, 0); + mode_hfcpci(cs->bcs + 1, 0, 1); +} + + + +/*******************************************/ +/* handle card messages from control layer */ +/*******************************************/ +static int +hfcpci_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + long flags; + + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "HFCPCI: card_msg %x", mt); + switch (mt) { + case CARD_RESET: + reset_hfcpci(cs); + return (0); + case CARD_RELEASE: + release_io_hfcpci(cs); + return (0); + case CARD_SETIRQ: + cs->hw.hfcpci.timer.expires = jiffies + 75; + add_timer(&cs->hw.hfcpci.timer); + return (request_irq(cs->irq, &hfcpci_interrupt, + I4L_IRQ_FLAG | SA_SHIRQ, "HiSax", cs)); + case CARD_INIT: + inithfcpci(cs); + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((80 * HZ) / 1000); /* Timeout 80ms */ + schedule(); + /* now switch timer interrupt off */ + cs->hw.hfcpci.int_m1 &= ~HFCPCI_INTS_TIMER; + Write_hfc(cs, HFCPCI_INT_M1, cs->hw.hfcpci.int_m1); + /* reinit mode reg */ + Write_hfc(cs, HFCPCI_MST_MODE, cs->hw.hfcpci.mst_m); + restore_flags(flags); + return (0); + case CARD_TEST: + return (0); + } + return (0); +} + + +/* this variable is used as card index when more than one cards are present */ +#ifdef COMPAT_HAS_NEW_PCI +static struct pci_dev *dev_hfcpci __initdata = NULL; +#else +static int pci_index __initdata = 0; +#endif + +#endif /* CONFIG_PCI */ + +__initfunc(int + setup_hfcpci(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, hfcpci_revision); + printk(KERN_INFO "HiSax: HFC-PCI driver Rev. %s\n", HiSax_getrev(tmp)); +#if CONFIG_PCI + cs->hw.hfcpci.int_s1 = 0; +#if 0 + cs->hw.hfcpci.send = NULL; +#endif + cs->bcs[0].hw.hfc.send = NULL; + cs->bcs[1].hw.hfc.send = NULL; + cs->dc.hfcpci.ph_state = 0; + cs->hw.hfcpci.fifo = 255; + if (cs->typ == ISDN_CTYPE_HFC_PCI) { +#ifdef COMPAT_HAS_NEW_PCI + if (!pci_present()) { + printk(KERN_ERR "HFC-PCI: no PCI bus present\n"); + return (0); + } + if ((dev_hfcpci = pci_find_device(PCI_VENDOR_CCD, + PCI_CCD_PCI_ID, dev_hfcpci))) { + cs->hw.hfcpci.pci_bus = dev_hfcpci->bus->number; + cs->hw.hfcpci.pci_device_fn = dev_hfcpci->devfn; + cs->irq = dev_hfcpci->irq; + if (!cs->irq) { + printk(KERN_WARNING "HFC-PCI: No IRQ for PCI card found\n"); + return (0); + } + cs->hw.hfcpci.pci_io = (char *) + dev_hfcpci->base_address[1]; + } else { + printk(KERN_WARNING "HFC-PCI: No PCI card found\n"); + return (0); + } +#else + for (; pci_index < 255; pci_index++) { + unsigned char irq; + + if (pcibios_find_device(PCI_VENDOR_CCD, + PCI_CCD_PCI_ID, pci_index, + &cs->hw.hfcpci.pci_bus, &cs->hw.hfcpci.pci_device_fn) != 0) { + continue; + } + pcibios_read_config_byte(cs->hw.hfcpci.pci_bus, cs->hw.hfcpci.pci_device_fn, + PCI_INTERRUPT_LINE, &irq); + cs->irq = irq; + + pcibios_read_config_dword(cs->hw.hfcpci.pci_bus, + cs->hw.hfcpci.pci_device_fn, PCI_BASE_ADDRESS_1, + (void *) &cs->hw.hfcpci.pci_io); + break; + } + if (pci_index == 255) { + printk(KERN_WARNING "HFC-PCI: No card found\n"); + return (0); + } + pci_index++; +#endif /* COMPAT_HAS_NEW_PCI */ + if (!cs->hw.hfcpci.pci_io) { + printk(KERN_WARNING "HFC-PCI: No IO-Mem for PCI card found\n"); + return (0); + } + /* Allocate memory for FIFOS */ + /* Because the HFC-PCI needs a 32K physical alignment, we */ + /* need to allocate the double mem and align the address */ + if (!((void *) cs->hw.hfcpci.share_start = kmalloc(65536, GFP_KERNEL))) { + printk(KERN_WARNING "HFC-PCI: Error allocating memory for FIFO!\n"); + return 0; + } + (ulong) cs->hw.hfcpci.fifos = + (((ulong) cs->hw.hfcpci.share_start) & ~0x7FFF) + 0x8000; + pcibios_write_config_dword(cs->hw.hfcpci.pci_bus, + cs->hw.hfcpci.pci_device_fn, 0x80, + (u_int) virt_to_bus(cs->hw.hfcpci.fifos)); + cs->hw.hfcpci.pci_io = ioremap((ulong) cs->hw.hfcpci.pci_io, 256); + printk(KERN_INFO + "HFC-PCI: defined at mem %#x fifo %#x(%#x) IRQ %d HZ %d\n", + (u_int) cs->hw.hfcpci.pci_io, + (u_int) cs->hw.hfcpci.fifos, + (u_int) virt_to_bus(cs->hw.hfcpci.fifos), + cs->irq, HZ); + /* At this point the needed PCI config is done */ + /* registers and fifos are still not enabled */ + } else + return (0); /* no valid card type */ + + + cs->readisac = NULL; + cs->writeisac = NULL; + cs->readisacfifo = NULL; + cs->writeisacfifo = NULL; + cs->BC_Read_Reg = NULL; + cs->BC_Write_Reg = NULL; + + cs->hw.hfcpci.timer.function = (void *) hfcpci_Timer; + cs->hw.hfcpci.timer.data = (long) cs; + init_timer(&cs->hw.hfcpci.timer); + + reset_hfcpci(cs); + cs->cardmsg = &hfcpci_card_msg; + return (1); +#else + printk(KERN_WARNING "HFC-PCI: NO_PCI_BIOS\n"); + return (0); +#endif /* CONFIG_PCI */ +} diff --git a/drivers/isdn/hisax/hfc_pci.h b/drivers/isdn/hisax/hfc_pci.h new file mode 100644 index 00000000..d4c782b0 --- /dev/null +++ b/drivers/isdn/hisax/hfc_pci.h @@ -0,0 +1,243 @@ +/* $Id$ + + * specific defines for CCD's HFC 2BDS0 PCI chips + * + * Author Werner Cornelius (werner@isdn4linux.de) + * + * Copyright 1999 by Werner Cornelius (werner@isdn4linux.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$ + * + * + */ + + +/* defines for PCI config */ + +#define PCI_VENDOR_CCD 0x1397 +#define PCI_CCD_PCI_ID 0x2BD0 +// #define PCI_VENDOR_CCD 0x1043 +// #define PCI_CCD_PCI_ID 0x675 +#define PCI_ENA_MEMIO 0x02 +#define PCI_ENA_MASTER 0x04 + + +/* GCI/IOM bus monitor registers */ + +#define HCFPCI_C_I 0x08 +#define HFCPCI_TRxR 0x0C +#define HFCPCI_MON1_D 0x28 +#define HFCPCI_MON2_D 0x2C + + +/* GCI/IOM bus timeslot registers */ + +#define HFCPCI_B1_SSL 0x80 +#define HFCPCI_B2_SSL 0x84 +#define HFCPCI_AUX1_SSL 0x88 +#define HFCPCI_AUX2_SSL 0x8C +#define HFCPCI_B1_RSL 0x90 +#define HFCPCI_B2_RSL 0x94 +#define HFCPCI_AUX1_RSL 0x98 +#define HFCPCI_AUX2_RSL 0x9C + +/* GCI/IOM bus data registers */ + +#define HFCPCI_B1_D 0xA0 +#define HFCPCI_B2_D 0xA4 +#define HFCPCI_AUX1_D 0xA8 +#define HFCPCI_AUX2_D 0xAC + +/* GCI/IOM bus configuration registers */ + +#define HFCPCI_MST_EMOD 0xB4 +#define HFCPCI_MST_MODE 0xB8 +#define HFCPCI_CONNECT 0xBC + + +/* Interrupt and status registers */ + +#define HFCPCI_FIFO_EN 0x44 +#define HFCPCI_TRM 0x48 +#define HFCPCI_B_MODE 0x4C +#define HFCPCI_CHIP_ID 0x58 +#define HFCPCI_CIRM 0x60 +#define HFCPCI_CTMT 0x64 +#define HFCPCI_INT_M1 0x68 +#define HFCPCI_INT_M2 0x6C +#define HFCPCI_INT_S1 0x78 +#define HFCPCI_INT_S2 0x7C +#define HFCPCI_STATUS 0x70 + +/* S/T section registers */ + +#define HFCPCI_STATES 0xC0 +#define HFCPCI_SCTRL 0xC4 +#define HFCPCI_SCTRL_E 0xC8 +#define HFCPCI_SCTRL_R 0xCC +#define HFCPCI_SQ 0xD0 +#define HFCPCI_CLKDEL 0xDC +#define HFCPCI_B1_REC 0xF0 +#define HFCPCI_B1_SEND 0xF0 +#define HFCPCI_B2_REC 0xF4 +#define HFCPCI_B2_SEND 0xF4 +#define HFCPCI_D_REC 0xF8 +#define HFCPCI_D_SEND 0xF8 +#define HFCPCI_E_REC 0xFC + + +/* bits in status register (READ) */ +#define HFCPCI_PCI_PROC 0x02 +#define HFCPCI_NBUSY 0x04 +#define HFCPCI_TIMER_ELAP 0x10 +#define HFCPCI_STATINT 0x20 +#define HFCPCI_FRAMEINT 0x40 +#define HFCPCI_ANYINT 0x80 + +/* bits in CTMT (Write) */ +#define HFCPCI_CLTIMER 0x80 +#define HFCPCI_TIM3_125 0x00 +#define HFCPCI_TIM25 0x10 +#define HFCPCI_TIM50 0x14 +#define HFCPCI_TIM400 0x18 +#define HFCPCI_TIM800 0x1C +#define HFCPCI_AUTO_TIMER 0x20 +#define HFCPCI_TRANSB2 0x02 +#define HFCPCI_TRANSB1 0x01 + +/* bits in CIRM (Write) */ +#define HFCPCI_AUX_MSK 0x07 +#define HFCPCI_RESET 0x08 +#define HFCPCI_B1_REV 0x40 +#define HFCPCI_B2_REV 0x80 + +/* bits in INT_M1 and INT_S1 */ +#define HFCPCI_INTS_B1TRANS 0x01 +#define HFCPCI_INTS_B2TRANS 0x02 +#define HFCPCI_INTS_DTRANS 0x04 +#define HFCPCI_INTS_B1REC 0x08 +#define HFCPCI_INTS_B2REC 0x10 +#define HFCPCI_INTS_DREC 0x20 +#define HFCPCI_INTS_L1STATE 0x40 +#define HFCPCI_INTS_TIMER 0x80 + +/* bits in INT_M2 */ +#define HFCPCI_PROC_TRANS 0x01 +#define HFCPCI_GCI_I_CHG 0x02 +#define HFCPCI_GCI_MON_REC 0x04 +#define HFCPCI_IRQ_ENABLE 0x08 +#define HFCPCI_PMESEL 0x80 + +/* bits in STATES */ +#define HFCPCI_STATE_MSK 0x0F +#define HFCPCI_LOAD_STATE 0x10 +#define HFCPCI_ACTIVATE 0x20 +#define HFCPCI_DO_ACTION 0x40 +#define HFCPCI_NT_G2_G3 0x80 + +/* bits in HFCD_MST_MODE */ +#define HFCPCI_MASTER 0x01 +#define HFCPCI_SLAVE 0x00 +/* remaining bits are for codecs control */ + +/* bits in HFCD_SCTRL */ +#define SCTRL_B1_ENA 0x01 +#define SCTRL_B2_ENA 0x02 +#define SCTRL_MODE_TE 0x00 +#define SCTRL_MODE_NT 0x04 +#define SCTRL_LOW_PRIO 0x08 +#define SCTRL_SQ_ENA 0x10 +#define SCTRL_TEST 0x20 +#define SCTRL_NONE_CAP 0x40 +#define SCTRL_PWR_DOWN 0x80 + +/* bits in SCTRL_E */ +#define HFCPCI_AUTO_AWAKE 0x01 +#define HFCPCI_DBIT_1 0x04 +#define HFCPCI_IGNORE_COL 0x08 +#define HFCPCI_CHG_B1_B2 0x80 + +/****************************/ +/* bits in FIFO_EN register */ +/****************************/ +#define HFCPCI_FIFOEN_B1 0x03 +#define HFCPCI_FIFOEN_B2 0x0C +#define HFCPCI_FIFOEN_DTX 0x10 + + +/***********************************/ +/* definitions of fifo memory area */ +/***********************************/ +#define MAX_D_FRAMES 15 +#define MAX_B_FRAMES 31 +#define B_SUB_VAL 0x200 +#define B_FIFO_SIZE (0x2000 - B_SUB_VAL) +#define D_FIFO_SIZE 512 +#define D_FREG_MASK 0xF + +typedef struct { + unsigned short z1; /* Z1 pointer 16 Bit */ + unsigned short z2; /* Z2 pointer 16 Bit */ + } z_type; + +typedef struct { + u_char data[D_FIFO_SIZE]; /* FIFO data space */ + u_char fill1[0x20A0-D_FIFO_SIZE]; /* reserved, do not use */ + u_char f1,f2; /* f pointers */ + u_char fill2[0x20C0-0x20A2]; /* reserved, do not use */ + z_type za[MAX_D_FRAMES+1]; /* mask index with D_FREG_MASK for access */ + u_char fill3[0x4000-0x2100]; /* align 16K */ + } dfifo_type; + +typedef struct { + z_type za[MAX_B_FRAMES+1]; /* only range 0x0..0x1F allowed */ + u_char f1,f2; /* f pointers */ + u_char fill[0x2100-0x2082]; /* alignment */ + } bzfifo_type; + + +typedef union { + struct { + dfifo_type d_tx; /* D-send channel */ + dfifo_type d_rx; /* D-receive channel */ + } d_chan; + struct { + u_char fill1[0x200]; + u_char txdat_b1[B_FIFO_SIZE]; + bzfifo_type txbz_b1; + + bzfifo_type txbz_b2; + u_char txdat_b2[B_FIFO_SIZE]; + + u_char fill2[D_FIFO_SIZE]; + + u_char rxdat_b1[B_FIFO_SIZE]; + bzfifo_type rxbz_b1; + + bzfifo_type rxbz_b2; + u_char rxdat_b2[B_FIFO_SIZE]; + } b_chans; + u_char fill[32768]; + } fifo_area; + + +#define Write_hfc(a,b,c) (*(((u_char *)a->hw.hfcpci.pci_io)+b) = c) +#define Read_hfc(a,b) (*(((u_char *)a->hw.hfcpci.pci_io)+b)) + +extern void main_irq_hcpci(struct BCState *bcs); +extern void inithfcpci(struct IsdnCardState *cs); +extern void releasehfcpci(struct IsdnCardState *cs); diff --git a/drivers/isdn/hisax/isurf.c b/drivers/isdn/hisax/isurf.c new file mode 100644 index 00000000..fe28d224 --- /dev/null +++ b/drivers/isdn/hisax/isurf.c @@ -0,0 +1,247 @@ +/* $Id$ + + * isurf.c low level stuff for Siemens I-Surf/I-Talk cards + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * $Log$ + * + * + */ + +#define __NO_VERSION__ +#include +#include "hisax.h" +#include "isac.h" +#include "isar.h" +#include "isdnl1.h" + +extern const char *CardType[]; + +static const char *ISurf_revision = "$Revision$"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define ISURF_ISAR_RESET 1 +#define ISURF_ISAC_RESET 2 +#define ISURF_ISAR_EA 4 +#define ISURF_ARCOFI_RESET 8 +#define ISURF_RESET (ISURF_ISAR_RESET | ISURF_ISAC_RESET | ISURF_ARCOFI_RESET) + +#define ISURF_ISAR_OFFSET 0 +#define ISURF_ISAC_OFFSET 0x100 + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readb(cs->hw.isurf.isac + offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writeb(value, cs->hw.isurf.isac + offset); mb(); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + register int i; + for (i = 0; i < size; i++) + data[i] = readb(cs->hw.isurf.isac); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + register int i; + for (i = 0; i < size; i++){ + writeb(data[i], cs->hw.isurf.isac);mb(); + } +} + +/* ISAR access routines + * mode = 0 access with IRQ on + * mode = 1 access with IRQ off + * mode = 2 access with IRQ off and using last offset + */ + +static u_char +ReadISAR(struct IsdnCardState *cs, int mode, u_char offset) +{ + return(readb(cs->hw.isurf.isar + offset)); +} + +static void +WriteISAR(struct IsdnCardState *cs, int mode, u_char offset, u_char value) +{ + writeb(value, cs->hw.isurf.isar + offset);mb(); +} + +static void +isurf_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val; + int cnt = 20; + + if (!cs) { + printk(KERN_WARNING "ISurf: Spurious interrupt!\n"); + return; + } + + val = readb(cs->hw.isurf.isar + ISAR_IRQBIT); + Start_ISAR: + if (val & ISAR_IRQSTA) + isar_int_main(cs); + val = readb(cs->hw.isurf.isac + ISAC_ISTA); + Start_ISAC: + if (val) + isac_interrupt(cs, val); + val = readb(cs->hw.isurf.isar + ISAR_IRQBIT); + if ((val & ISAR_IRQSTA) && --cnt) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "ISAR IntStat after IntRoutine"); + goto Start_ISAR; + } + val = readb(cs->hw.isurf.isac + ISAC_ISTA); + if (val && --cnt) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + if (!cnt) + printk(KERN_WARNING "ISurf IRQ LOOP\n"); + + writeb(0, cs->hw.isurf.isar + ISAR_IRQBIT); mb(); + writeb(0xFF, cs->hw.isurf.isac + ISAC_MASK);mb(); + writeb(0, cs->hw.isurf.isac + ISAC_MASK);mb(); + writeb(ISAR_IRQMSK, cs->hw.isurf.isar + ISAR_IRQBIT); mb(); +} + +void +release_io_isurf(struct IsdnCardState *cs) +{ + release_region(cs->hw.isurf.reset, 1); +} + +static void +reset_isurf(struct IsdnCardState *cs, u_char chips) +{ + long flags; + + printk(KERN_INFO "ISurf: resetting card\n"); + + byteout(cs->hw.isurf.reset, chips); /* Reset On */ + save_flags(flags); + sti(); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10*HZ)/1000); + byteout(cs->hw.isurf.reset, ISURF_ISAR_EA); /* Reset Off */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((10*HZ)/1000); + restore_flags(flags); +} + +static int +ISurf_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + reset_isurf(cs, ISURF_RESET); + return(0); + case CARD_RELEASE: + release_io_isurf(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &isurf_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + clear_pending_isac_ints(cs); + writeb(0, cs->hw.isurf.isar+ISAR_IRQBIT);mb(); + initisac(cs); + initisar(cs); + /* Reenable ISAC IRQ */ + cs->writeisac(cs, ISAC_MASK, 0); + /* RESET Receiver and Transmitter */ + cs->writeisac(cs, ISAC_CMDR, 0x41); + return(0); + case CARD_TEST: + return(0); + case CARD_LOAD_FIRM: + if (isar_load_firmware(cs, arg)) + return(1); + ll_run(cs); + reset_isurf(cs, ISURF_ISAR_EA | ISURF_ISAC_RESET | + ISURF_ARCOFI_RESET); + initisac(cs); + cs->writeisac(cs, ISAC_MASK, 0); + cs->writeisac(cs, ISAC_CMDR, 0x41); + return(0); + } + return(0); +} + +__initfunc(int +setup_isurf(struct IsdnCard *card)) +{ + int ver; + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, ISurf_revision); + printk(KERN_INFO "HiSax: ISurf driver Rev. %s\n", HiSax_getrev(tmp)); + + if (cs->typ != ISDN_CTYPE_ISURF) + return(0); + if (card->para[1] && card->para[2]) { + cs->hw.isurf.reset = card->para[1]; + cs->hw.isurf.isar = card->para[2] + ISURF_ISAR_OFFSET; + cs->hw.isurf.isac = card->para[2] + ISURF_ISAC_OFFSET; + cs->irq = card->para[0]; + } else { + printk(KERN_WARNING "HiSax: %s port/mem not set\n", + CardType[card->typ]); + return (0); + } + if (check_region(cs->hw.isurf.reset, 1)) { + printk(KERN_WARNING + "HiSax: %s config port %x already in use\n", + CardType[card->typ], + cs->hw.isurf.reset); + return (0); + } else { + request_region(cs->hw.isurf.reset, 1, "isurf isdn"); + } + + printk(KERN_INFO + "ISurf: defined at 0x%x 0x%x IRQ %d\n", + cs->hw.isurf.reset, + cs->hw.isurf.isar, + cs->irq); + + cs->cardmsg = &ISurf_card_msg; + cs->readisac = &ReadISAC; + cs->writeisac = &WriteISAC; + cs->readisacfifo = &ReadISACfifo; + cs->writeisacfifo = &WriteISACfifo; + cs->bcs[0].hw.isar.reg = &cs->hw.isurf.isar_r; + cs->bcs[1].hw.isar.reg = &cs->hw.isurf.isar_r; + reset_isurf(cs, ISURF_RESET); + test_and_set_bit(HW_ISAR, &cs->HW_Flags); + ISACVersion(cs, "ISurf:"); + cs->BC_Read_Reg = &ReadISAR; + cs->BC_Write_Reg = &WriteISAR; + cs->BC_Send_Data = &isar_fill_fifo; + ver = ISARVersion(cs, "ISurf:"); + if (ver < 0) { + printk(KERN_WARNING + "ISurf: wrong ISAR version (ret = %d)\n", ver); + release_io_isurf(cs); + return (0); + } + return (1); +} diff --git a/drivers/isdn/hisax/jade.c b/drivers/isdn/hisax/jade.c new file mode 100644 index 00000000..2e9f4777 --- /dev/null +++ b/drivers/isdn/hisax/jade.c @@ -0,0 +1,323 @@ +/* $Id$ + * + * jade.c JADE stuff (derived from original hscx.c) + * + * Author Roland Klabunde (R.Klabunde@Berkom.de) + * + * $Log$ + * + */ + + +#define __NO_VERSION__ +#include "hisax.h" +#include "hscx.h" +#include "jade.h" +#include "isdnl1.h" +#include + + +HISAX_INITFUNC(int +JadeVersion(struct IsdnCardState *cs, char *s)) +{ + int ver,i; + int to = 50; + cs->BC_Write_Reg(cs, -1, 0x50, 0x19); + i=0; + while (to) { + udelay(1); + ver = cs->BC_Read_Reg(cs, -1, 0x60); + to--; + if (ver) + break; + if (!to) { + printk(KERN_INFO "%s JADE version not obtainable\n", s); + return (0); + } + } + /* Wait for the JADE */ + udelay(10); + /* Read version */ + ver = cs->BC_Read_Reg(cs, -1, 0x60); + printk(KERN_INFO "%s JADE version: %d\n", s, ver); + return (1); +} + +/* Write to indirect accessible jade register set */ +static void +jade_write_indirect(struct IsdnCardState *cs, u_char reg, u_char value) +{ + int to = 50; + long flags; + u_char ret; + save_flags(flags); + cli(); + /* Write the data */ + cs->BC_Write_Reg(cs, -1, COMM_JADE+1, value); + /* Say JADE we wanna write indirect reg 'reg' */ + cs->BC_Write_Reg(cs, -1, COMM_JADE, reg); + to = 50; + /* Wait for RDY goes high */ + while (to) { + udelay(1); + ret = cs->BC_Read_Reg(cs, -1, COMM_JADE); + to--; + if (ret & 1) + /* Got acknowledge */ + break; + if (!to) { + restore_flags(flags); + printk(KERN_INFO "Can not see ready bit from JADE DSP (reg=0x%X, value=0x%X)\n", reg, value); + return; + } + } + restore_flags(flags); +} + + + +void +modejade(struct BCState *bcs, int mode, int bc) +{ + struct IsdnCardState *cs = bcs->cs; + int jade = bcs->hw.hscx.hscx; + + if (cs->debug & L1_DEB_HSCX) { + char tmp[40]; + sprintf(tmp, "jade %c mode %d ichan %d", + 'A' + jade, mode, bc); + debugl1(cs, tmp); + } + bcs->mode = mode; + bcs->channel = bc; + + cs->BC_Write_Reg(cs, jade, jade_HDLC_MODE, (mode == L1_MODE_TRANS ? jadeMODE_TMO:0x00)); + cs->BC_Write_Reg(cs, jade, jade_HDLC_CCR0, (jadeCCR0_PU|jadeCCR0_ITF)); + cs->BC_Write_Reg(cs, jade, jade_HDLC_CCR1, 0x00); + + jade_write_indirect(cs, jade_HDLC1SERRXPATH, 0x08); + jade_write_indirect(cs, jade_HDLC2SERRXPATH, 0x08); + jade_write_indirect(cs, jade_HDLC1SERTXPATH, 0x00); + jade_write_indirect(cs, jade_HDLC2SERTXPATH, 0x00); + + cs->BC_Write_Reg(cs, jade, jade_HDLC_XCCR, 0x07); + cs->BC_Write_Reg(cs, jade, jade_HDLC_RCCR, 0x07); + + if (bc == 0) { + cs->BC_Write_Reg(cs, jade, jade_HDLC_TSAX, 0x00); + cs->BC_Write_Reg(cs, jade, jade_HDLC_TSAR, 0x00); + } else { + cs->BC_Write_Reg(cs, jade, jade_HDLC_TSAX, 0x04); + cs->BC_Write_Reg(cs, jade, jade_HDLC_TSAR, 0x04); + } + switch (mode) { + case (L1_MODE_NULL): + cs->BC_Write_Reg(cs, jade, jade_HDLC_MODE, jadeMODE_TMO); + break; + case (L1_MODE_TRANS): + cs->BC_Write_Reg(cs, jade, jade_HDLC_MODE, (jadeMODE_TMO|jadeMODE_RAC|jadeMODE_XAC)); + break; + case (L1_MODE_HDLC): + cs->BC_Write_Reg(cs, jade, jade_HDLC_MODE, (jadeMODE_RAC|jadeMODE_XAC)); + break; + } + if (mode) { + cs->BC_Write_Reg(cs, jade, jade_HDLC_RCMD, (jadeRCMD_RRES|jadeRCMD_RMC)); + cs->BC_Write_Reg(cs, jade, jade_HDLC_XCMD, jadeXCMD_XRES); + /* Unmask ints */ + cs->BC_Write_Reg(cs, jade, jade_HDLC_IMR, 0xF8); + } + else + /* Mask ints */ + cs->BC_Write_Reg(cs, jade, jade_HDLC_IMR, 0x00); +} + +void +jade_sched_event(struct BCState *bcs, int event) +{ + bcs->event |= 1 << event; + queue_task(&bcs->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void +jade_l2l1(struct PStack *st, int pr, void *arg) +{ + struct sk_buff *skb = arg; + long flags; + + switch (pr) { + case (PH_DATA | REQUEST): + save_flags(flags); + cli(); + if (st->l1.bcs->tx_skb) { + skb_queue_tail(&st->l1.bcs->squeue, skb); + restore_flags(flags); + } else { + st->l1.bcs->tx_skb = skb; + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + st->l1.bcs->hw.hscx.count = 0; + restore_flags(flags); + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + } + break; + case (PH_PULL | INDICATION): + if (st->l1.bcs->tx_skb) { + printk(KERN_WARNING "jade_l2l1: this shouldn't happen\n"); + break; + } + test_and_set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + st->l1.bcs->tx_skb = skb; + st->l1.bcs->hw.hscx.count = 0; + st->l1.bcs->cs->BC_Send_Data(st->l1.bcs); + break; + case (PH_PULL | REQUEST): + if (!st->l1.bcs->tx_skb) { + test_and_clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + st->l1.l1l2(st, PH_PULL | CONFIRM, NULL); + } else + test_and_set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); + break; + case (PH_ACTIVATE | REQUEST): + test_and_set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + modejade(st->l1.bcs, st->l1.mode, st->l1.bc); + l1_msg_b(st, pr, arg); + break; + case (PH_DEACTIVATE | REQUEST): + l1_msg_b(st, pr, arg); + break; + case (PH_DEACTIVATE | CONFIRM): + test_and_clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag); + test_and_clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag); + modejade(st->l1.bcs, 0, st->l1.bc); + st->l1.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL); + break; + } +} + +void +close_jadestate(struct BCState *bcs) +{ + modejade(bcs, 0, bcs->channel); + if (test_and_clear_bit(BC_FLG_INIT, &bcs->Flag)) { + if (bcs->hw.hscx.rcvbuf) { + kfree(bcs->hw.hscx.rcvbuf); + bcs->hw.hscx.rcvbuf = NULL; + } + if (bcs->blog) { + kfree(bcs->blog); + bcs->blog = NULL; + } + discard_queue(&bcs->rqueue); + discard_queue(&bcs->squeue); + if (bcs->tx_skb) { + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + } + } +} + +static int +open_jadestate(struct IsdnCardState *cs, struct BCState *bcs) +{ + if (!test_and_set_bit(BC_FLG_INIT, &bcs->Flag)) { + if (!(bcs->hw.hscx.rcvbuf = kmalloc(HSCX_BUFMAX, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for hscx.rcvbuf\n"); + test_and_clear_bit(BC_FLG_INIT, &bcs->Flag); + return (1); + } + if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) { + printk(KERN_WARNING + "HiSax: No memory for bcs->blog\n"); + test_and_clear_bit(BC_FLG_INIT, &bcs->Flag); + kfree(bcs->hw.hscx.rcvbuf); + bcs->hw.hscx.rcvbuf = NULL; + return (2); + } + skb_queue_head_init(&bcs->rqueue); + skb_queue_head_init(&bcs->squeue); + } + bcs->tx_skb = NULL; + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + bcs->event = 0; + bcs->hw.hscx.rcvidx = 0; + bcs->tx_cnt = 0; + return (0); +} + + +int +setstack_jade(struct PStack *st, struct BCState *bcs) +{ + bcs->channel = st->l1.bc; + if (open_jadestate(st->l1.hardware, bcs)) + return (-1); + st->l1.bcs = bcs; + st->l2.l2l1 = jade_l2l1; + setstack_manager(st); + bcs->st = st; + setstack_l1_B(st); + return (0); +} + +HISAX_INITFUNC(void +clear_pending_jade_ints(struct IsdnCardState *cs)) +{ + int val; + char tmp[64]; + + cs->BC_Write_Reg(cs, 0, jade_HDLC_IMR, 0x00); + cs->BC_Write_Reg(cs, 1, jade_HDLC_IMR, 0x00); + + val = cs->BC_Read_Reg(cs, 1, jade_HDLC_ISR); + sprintf(tmp, "jade B ISTA %x", val); + debugl1(cs, tmp); + val = cs->BC_Read_Reg(cs, 0, jade_HDLC_ISR); + sprintf(tmp, "jade A ISTA %x", val); + debugl1(cs, tmp); + val = cs->BC_Read_Reg(cs, 1, jade_HDLC_STAR); + sprintf(tmp, "jade B STAR %x", val); + debugl1(cs, tmp); + val = cs->BC_Read_Reg(cs, 0, jade_HDLC_STAR); + sprintf(tmp, "jade A STAR %x", val); + debugl1(cs, tmp); + /* Unmask ints */ + cs->BC_Write_Reg(cs, 0, jade_HDLC_IMR, 0xF8); + cs->BC_Write_Reg(cs, 1, jade_HDLC_IMR, 0xF8); +} + +HISAX_INITFUNC(void +initjade(struct IsdnCardState *cs)) +{ + cs->bcs[0].BC_SetStack = setstack_jade; + cs->bcs[1].BC_SetStack = setstack_jade; + cs->bcs[0].BC_Close = close_jadestate; + cs->bcs[1].BC_Close = close_jadestate; + cs->bcs[0].hw.hscx.hscx = 0; + cs->bcs[1].hw.hscx.hscx = 1; + + /* Stop DSP audio tx/rx */ + jade_write_indirect(cs, 0x11, 0x0f); + jade_write_indirect(cs, 0x17, 0x2f); + + /* Transparent Mode, RxTx inactive, No Test, No RFS/TFS */ + cs->BC_Write_Reg(cs, 0, jade_HDLC_MODE, jadeMODE_TMO); + cs->BC_Write_Reg(cs, 1, jade_HDLC_MODE, jadeMODE_TMO); + /* Power down, 1-Idle, RxTx least significant bit first */ + cs->BC_Write_Reg(cs, 0, jade_HDLC_CCR0, 0x00); + cs->BC_Write_Reg(cs, 1, jade_HDLC_CCR0, 0x00); + /* Mask all interrupts */ + cs->BC_Write_Reg(cs, 0, jade_HDLC_IMR, 0x00); + cs->BC_Write_Reg(cs, 1, jade_HDLC_IMR, 0x00); + /* Setup host access to hdlc controller */ + jade_write_indirect(cs, jade_HDLCCNTRACCESS, (jadeINDIRECT_HAH1|jadeINDIRECT_HAH2)); + /* Unmask HDLC int (don´t forget DSP int later on)*/ + cs->BC_Write_Reg(cs, -1,jade_INT, (jadeINT_HDLC1|jadeINT_HDLC2)); + + /* once again TRANSPARENT */ + modejade(cs->bcs, 0, 0); + modejade(cs->bcs + 1, 0, 0); +} + diff --git a/drivers/isdn/hisax/jade.h b/drivers/isdn/hisax/jade.h new file mode 100644 index 00000000..0384a5a1 --- /dev/null +++ b/drivers/isdn/hisax/jade.h @@ -0,0 +1,134 @@ +/* $Id$ + * jade.h JADE specific defines + * + * Author Roland Klabunde (R.Klabunde@Berkom.de) + * + * + * $Log$ + * + */ + +/* All Registers original Siemens Spec */ +#ifndef __JADE_H__ +#define __JADE_H__ + +/* Special registers for access to indirect accessible JADE regs */ +#define DIRECT_IO_JADE 0x0000 /* Jade direct io access area */ +#define COMM_JADE 0x0040 /* Jade communication area */ + +/********************************************************************/ +/* JADE-HDLC registers */ +/********************************************************************/ +#define jade_HDLC_RFIFO 0x00 /* R */ +#define jade_HDLC_XFIFO 0x00 /* W */ + +#define jade_HDLC_STAR 0x20 /* R */ + #define jadeSTAR_XDOV 0x80 + #define jadeSTAR_XFW 0x40 /* Does not work*/ + #define jadeSTAR_XCEC 0x20 + #define jadeSTAR_RCEC 0x10 + #define jadeSTAR_BSY 0x08 + #define jadeSTAR_RNA 0x04 + #define jadeSTAR_STR 0x02 + #define jadeSTAR_STX 0x01 + +#define jade_HDLC_XCMD 0x20 /* W */ + #define jadeXCMD_XF 0x80 + #define jadeXCMD_XME 0x40 + #define jadeXCMD_XRES 0x20 + #define jadeXCMD_STX 0x01 + +#define jade_HDLC_RSTA 0x21 /* R */ + #define jadeRSTA_VFR 0x80 + #define jadeRSTA_RDO 0x40 + #define jadeRSTA_CRC 0x20 + #define jadeRSTA_RAB 0x10 + #define jadeRSTA_MASK 0xF0 + +#define jade_HDLC_MODE 0x22 /* RW*/ + #define jadeMODE_TMO 0x80 + #define jadeMODE_RAC 0x40 + #define jadeMODE_XAC 0x20 + #define jadeMODE_TLP 0x10 + #define jadeMODE_ERFS 0x02 + #define jadeMODE_ETFS 0x01 + +#define jade_HDLC_RBCH 0x24 /* R */ + +#define jade_HDLC_RBCL 0x25 /* R */ +#define jade_HDLC_RCMD 0x25 /* W */ + #define jadeRCMD_RMC 0x80 + #define jadeRCMD_RRES 0x40 + #define jadeRCMD_RMD 0x20 + #define jadeRCMD_STR 0x02 + +#define jade_HDLC_CCR0 0x26 /* RW*/ + #define jadeCCR0_PU 0x80 + #define jadeCCR0_ITF 0x40 + #define jadeCCR0_C32 0x20 + #define jadeCCR0_CRL 0x10 + #define jadeCCR0_RCRC 0x08 + #define jadeCCR0_XCRC 0x04 + #define jadeCCR0_RMSB 0x02 + #define jadeCCR0_XMSB 0x01 + +#define jade_HDLC_CCR1 0x27 /* RW*/ + #define jadeCCR1_RCS0 0x80 + #define jadeCCR1_RCONT 0x40 + #define jadeCCR1_RFDIS 0x20 + #define jadeCCR1_XCS0 0x10 + #define jadeCCR1_XCONT 0x08 + #define jadeCCR1_XFDIS 0x04 + +#define jade_HDLC_TSAR 0x28 /* RW*/ +#define jade_HDLC_TSAX 0x29 /* RW*/ +#define jade_HDLC_RCCR 0x2A /* RW*/ +#define jade_HDLC_XCCR 0x2B /* RW*/ + +#define jade_HDLC_ISR 0x2C /* R */ +#define jade_HDLC_IMR 0x2C /* W */ + #define jadeISR_RME 0x80 + #define jadeISR_RPF 0x40 + #define jadeISR_RFO 0x20 + #define jadeISR_XPR 0x10 + #define jadeISR_XDU 0x08 + #define jadeISR_ALLS 0x04 + +#define jade_INT 0x75 + #define jadeINT_HDLC1 0x02 + #define jadeINT_HDLC2 0x01 + #define jadeINT_DSP 0x04 +#define jade_INTR 0x70 + +/********************************************************************/ +/* Indirect accessible JADE registers of common interest */ +/********************************************************************/ +#define jade_CHIPVERSIONNR 0x00 /* Does not work*/ + +#define jade_HDLCCNTRACCESS 0x10 + #define jadeINDIRECT_HAH1 0x02 + #define jadeINDIRECT_HAH2 0x01 + +#define jade_HDLC1SERRXPATH 0x1D +#define jade_HDLC1SERTXPATH 0x1E +#define jade_HDLC2SERRXPATH 0x1F +#define jade_HDLC2SERTXPATH 0x20 + #define jadeINDIRECT_SLIN1 0x10 + #define jadeINDIRECT_SLIN0 0x08 + #define jadeINDIRECT_LMOD1 0x04 + #define jadeINDIRECT_LMOD0 0x02 + #define jadeINDIRECT_HHR 0x01 + #define jadeINDIRECT_HHX 0x01 + +#define jade_RXAUDIOCH1CFG 0x11 +#define jade_RXAUDIOCH2CFG 0x14 +#define jade_TXAUDIOCH1CFG 0x17 +#define jade_TXAUDIOCH2CFG 0x1A + +extern int JadeVersion(struct IsdnCardState *cs, char *s); +extern void jade_sched_event(struct BCState *bcs, int event); +extern void modejade(struct BCState *bcs, int mode, int bc); +extern void clear_pending_jade_ints(struct IsdnCardState *cs); +extern void initjade(struct IsdnCardState *cs); + +#endif /* __JADE_H__ */ diff --git a/drivers/isdn/hisax/jade_irq.c b/drivers/isdn/hisax/jade_irq.c new file mode 100644 index 00000000..d497ee6b --- /dev/null +++ b/drivers/isdn/hisax/jade_irq.c @@ -0,0 +1,244 @@ +/* $Id$ + * + * jade_irq.c Low level JADE IRQ stuff (derived from original hscx_irq.c) + * + * Author Roland Klabunde (R.Klabunde@Berkom.de) + * + * $Log$ + * + */ + +static inline void +waitforCEC(struct IsdnCardState *cs, int jade, int reg) +{ + int to = 50; + int mask = (reg == jade_HDLC_XCMD ? jadeSTAR_XCEC : jadeSTAR_RCEC); + while ((READJADE(cs, jade, jade_HDLC_STAR) & mask) && to) { + udelay(1); + to--; + } + if (!to) + printk(KERN_WARNING "HiSax: waitforCEC (jade) timeout\n"); +} + + +static inline void +waitforXFW(struct IsdnCardState *cs, int jade) +{ + /* Does not work on older jade versions, don't care */ +} + +static inline void +WriteJADECMDR(struct IsdnCardState *cs, int jade, int reg, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + waitforCEC(cs, jade, reg); + WRITEJADE(cs, jade, reg, data); + restore_flags(flags); +} + + + +static void +jade_empty_fifo(struct BCState *bcs, int count) +{ + u_char *ptr; + struct IsdnCardState *cs = bcs->cs; + long flags; + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "jade_empty_fifo"); + + if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) { + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "jade_empty_fifo: incoming packet too large"); + WriteJADECMDR(cs, bcs->hw.hscx.hscx, jade_HDLC_RCMD, jadeRCMD_RMC); + bcs->hw.hscx.rcvidx = 0; + return; + } + ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx; + bcs->hw.hscx.rcvidx += count; + save_flags(flags); + cli(); + READJADEFIFO(cs, bcs->hw.hscx.hscx, ptr, count); + WriteJADECMDR(cs, bcs->hw.hscx.hscx, jade_HDLC_RCMD, jadeRCMD_RMC); + restore_flags(flags); + if (cs->debug & L1_DEB_HSCX_FIFO) { + char *t = bcs->blog; + + t += sprintf(t, "jade_empty_fifo %c cnt %d", + bcs->hw.hscx.hscx ? 'B' : 'A', count); + QuickHex(t, ptr, count); + debugl1(cs, bcs->blog); + } +} + +static void +jade_fill_fifo(struct BCState *bcs) +{ + struct IsdnCardState *cs = bcs->cs; + int more, count; + int fifo_size = 32; + u_char *ptr; + long flags; + + if ((cs->debug & L1_DEB_HSCX) && !(cs->debug & L1_DEB_HSCX_FIFO)) + debugl1(cs, "jade_fill_fifo"); + + if (!bcs->tx_skb) + return; + if (bcs->tx_skb->len <= 0) + return; + + more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0; + if (bcs->tx_skb->len > fifo_size) { + more = !0; + count = fifo_size; + } else + count = bcs->tx_skb->len; + + waitforXFW(cs, bcs->hw.hscx.hscx); + save_flags(flags); + cli(); + ptr = bcs->tx_skb->data; + skb_pull(bcs->tx_skb, count); + bcs->tx_cnt -= count; + bcs->hw.hscx.count += count; + WRITEJADEFIFO(cs, bcs->hw.hscx.hscx, ptr, count); + WriteJADECMDR(cs, bcs->hw.hscx.hscx, jade_HDLC_XCMD, more ? jadeXCMD_XF : (jadeXCMD_XF|jadeXCMD_XME)); + restore_flags(flags); + if (cs->debug & L1_DEB_HSCX_FIFO) { + char *t = bcs->blog; + + t += sprintf(t, "jade_fill_fifo %c cnt %d", + bcs->hw.hscx.hscx ? 'B' : 'A', count); + QuickHex(t, ptr, count); + debugl1(cs, bcs->blog); + } +} + + +static inline void +jade_interrupt(struct IsdnCardState *cs, u_char val, u_char jade) +{ + u_char r; + struct BCState *bcs = cs->bcs + jade; + struct sk_buff *skb; + int fifo_size = 32; + int count; + int i_jade = (int) jade; /* To satisfy the compiler */ + + if (!test_bit(BC_FLG_INIT, &bcs->Flag)) + return; + + if (val & 0x80) { /* RME */ + r = READJADE(cs, i_jade, jade_HDLC_RSTA); + if ((r & 0xf0) != 0xa0) { + if (!(r & 0x80)) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "JADE %s invalid frame", (jade ? "B":"A")); + if ((r & 0x40) && bcs->mode) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "JADE %c RDO mode=%d", 'A'+jade, bcs->mode); + if (!(r & 0x20)) + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "JADE %c CRC error", 'A'+jade); + WriteJADECMDR(cs, jade, jade_HDLC_RCMD, jadeRCMD_RMC); + } else { + count = READJADE(cs, i_jade, jade_HDLC_RBCL) & 0x1F; + if (count == 0) + count = fifo_size; + jade_empty_fifo(bcs, count); + if ((count = bcs->hw.hscx.rcvidx - 1) > 0) { + if (cs->debug & L1_DEB_HSCX_FIFO) + debugl1(cs, "HX Frame %d", count); + if (!(skb = dev_alloc_skb(count))) + printk(KERN_WARNING "JADE %s receive out of memory\n", (jade ? "B":"A")); + else { + SET_SKB_FREE(skb); + memcpy(skb_put(skb, count), bcs->hw.hscx.rcvbuf, count); + skb_queue_tail(&bcs->rqueue, skb); + } + } + } + bcs->hw.hscx.rcvidx = 0; + jade_sched_event(bcs, B_RCVBUFREADY); + } + if (val & 0x40) { /* RPF */ + jade_empty_fifo(bcs, fifo_size); + if (bcs->mode == L1_MODE_TRANS) { + /* receive audio data */ + if (!(skb = dev_alloc_skb(fifo_size))) + printk(KERN_WARNING "HiSax: receive out of memory\n"); + else { + SET_SKB_FREE(skb); + memcpy(skb_put(skb, fifo_size), bcs->hw.hscx.rcvbuf, fifo_size); + skb_queue_tail(&bcs->rqueue, skb); + } + bcs->hw.hscx.rcvidx = 0; + jade_sched_event(bcs, B_RCVBUFREADY); + } + } + if (val & 0x10) { /* XPR */ + if (bcs->tx_skb) { + if (bcs->tx_skb->len) { + jade_fill_fifo(bcs); + return; + } else { + if (bcs->st->lli.l1writewakeup && + (PACKET_NOACK != bcs->tx_skb->pkt_type)) + bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hscx.count); + idev_kfree_skb(bcs->tx_skb, FREE_WRITE); + bcs->hw.hscx.count = 0; + bcs->tx_skb = NULL; + } + } + if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) { + bcs->hw.hscx.count = 0; + test_and_set_bit(BC_FLG_BUSY, &bcs->Flag); + jade_fill_fifo(bcs); + } else { + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + jade_sched_event(bcs, B_XMTBUFREADY); + } + } +} + +static inline void +jade_int_main(struct IsdnCardState *cs, u_char val, int jade) +{ + struct BCState *bcs; + bcs = cs->bcs + jade; + + if (val & jadeISR_RFO) { + /* handled with RDO */ + val &= ~jadeISR_RFO; + } + if (val & jadeISR_XDU) { + /* relevant in HDLC mode only */ + /* don't reset XPR here */ + if (bcs->mode == 1) + jade_fill_fifo(bcs); + else { + /* Here we lost an TX interrupt, so + * restart transmitting the whole frame. + */ + if (bcs->tx_skb) { + skb_push(bcs->tx_skb, bcs->hw.hscx.count); + bcs->tx_cnt += bcs->hw.hscx.count; + bcs->hw.hscx.count = 0; + } + WriteJADECMDR(cs, bcs->hw.hscx.hscx, jade_HDLC_XCMD, jadeXCMD_XRES); + if (cs->debug & L1_DEB_WARN) + debugl1(cs, "JADE %c EXIR %x Lost TX", 'A'+jade, val); + } + } + if (val & (jadeISR_RME|jadeISR_RPF|jadeISR_XPR)) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "JADE %c interrupt %x", 'A'+jade, val); + jade_interrupt(cs, val, jade); + } +} diff --git a/drivers/isdn/hisax/saphir.c b/drivers/isdn/hisax/saphir.c new file mode 100644 index 00000000..59a92b56 --- /dev/null +++ b/drivers/isdn/hisax/saphir.c @@ -0,0 +1,329 @@ +/* $Id$ + + * saphir.c low level stuff for HST Saphir 1 + * + * Author Karsten Keil (keil@isdn4linux.de) + * + * Thanks to HST High Soft Tech GmbH + * + * + * $Log$ + * + */ + + +#define __NO_VERSION__ +#include "hisax.h" +#include "isac.h" +#include "hscx.h" +#include "isdnl1.h" + +extern const char *CardType[]; +static char *saphir_rev = "$Revision$"; + +#define byteout(addr,val) outb(val,addr) +#define bytein(addr) inb(addr) + +#define ISAC_DATA 0 +#define HSCX_DATA 1 +#define ADDRESS_REG 2 +#define IRQ_REG 3 +#define SPARE_REG 4 +#define RESET_REG 5 + +static inline u_char +readreg(unsigned int ale, unsigned int adr, u_char off) +{ + register u_char ret; + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + ret = bytein(adr); + restore_flags(flags); + return (ret); +} + +static inline void +readfifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo read without cli because it's allready done */ + + byteout(ale, off); + insb(adr, data, size); +} + + +static inline void +writereg(unsigned int ale, unsigned int adr, u_char off, u_char data) +{ + long flags; + + save_flags(flags); + cli(); + byteout(ale, off); + byteout(adr, data); + restore_flags(flags); +} + +static inline void +writefifo(unsigned int ale, unsigned int adr, u_char off, u_char * data, int size) +{ + /* fifo write without cli because it's allready done */ + byteout(ale, off); + outsb(adr, data, size); +} + +/* Interface functions */ + +static u_char +ReadISAC(struct IsdnCardState *cs, u_char offset) +{ + return (readreg(cs->hw.saphir.ale, cs->hw.saphir.isac, offset)); +} + +static void +WriteISAC(struct IsdnCardState *cs, u_char offset, u_char value) +{ + writereg(cs->hw.saphir.ale, cs->hw.saphir.isac, offset, value); +} + +static void +ReadISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + readfifo(cs->hw.saphir.ale, cs->hw.saphir.isac, 0, data, size); +} + +static void +WriteISACfifo(struct IsdnCardState *cs, u_char * data, int size) +{ + writefifo(cs->hw.saphir.ale, cs->hw.saphir.isac, 0, data, size); +} + +static u_char +ReadHSCX(struct IsdnCardState *cs, int hscx, u_char offset) +{ + return (readreg(cs->hw.saphir.ale, cs->hw.saphir.hscx, + offset + (hscx ? 0x40 : 0))); +} + +static void +WriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) +{ + writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, + offset + (hscx ? 0x40 : 0), value); +} + +#define READHSCX(cs, nr, reg) readreg(cs->hw.saphir.ale, \ + cs->hw.saphir.hscx, reg + (nr ? 0x40 : 0)) +#define WRITEHSCX(cs, nr, reg, data) writereg(cs->hw.saphir.ale, \ + cs->hw.saphir.hscx, reg + (nr ? 0x40 : 0), data) + +#define READHSCXFIFO(cs, nr, ptr, cnt) readfifo(cs->hw.saphir.ale, \ + cs->hw.saphir.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#define WRITEHSCXFIFO(cs, nr, ptr, cnt) writefifo(cs->hw.saphir.ale, \ + cs->hw.saphir.hscx, (nr ? 0x40 : 0), ptr, cnt) + +#include "hscx_irq.c" + +static void +saphir_interrupt(int intno, void *dev_id, struct pt_regs *regs) +{ + struct IsdnCardState *cs = dev_id; + u_char val, stat = 0; + + if (!cs) { + printk(KERN_WARNING "saphir: Spurious interrupt!\n"); + return; + } + val = readreg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_ISTA + 0x40); + Start_HSCX: + if (val) { + hscx_int_main(cs, val); + stat |= 1; + } + val = readreg(cs->hw.saphir.ale, cs->hw.saphir.isac, ISAC_ISTA); + Start_ISAC: + if (val) { + isac_interrupt(cs, val); + stat |= 2; + } + val = readreg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_ISTA + 0x40); + if (val) { + if (cs->debug & L1_DEB_HSCX) + debugl1(cs, "HSCX IntStat after IntRoutine"); + goto Start_HSCX; + } + val = readreg(cs->hw.saphir.ale, cs->hw.saphir.isac, ISAC_ISTA); + if (val) { + if (cs->debug & L1_DEB_ISAC) + debugl1(cs, "ISAC IntStat after IntRoutine"); + goto Start_ISAC; + } + /* Watchdog */ + if (cs->hw.saphir.timer.function) { + del_timer(&cs->hw.saphir.timer); + cs->hw.saphir.timer.expires = jiffies + 1*HZ; + add_timer(&cs->hw.saphir.timer); + } else + printk(KERN_WARNING "saphir: Spurious timer!\n"); + if (stat & 1) { + writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK, 0xFF); + writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK + 0x40, 0xFF); + writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK, 0); + writereg(cs->hw.saphir.ale, cs->hw.saphir.hscx, HSCX_MASK + 0x40, 0); + } + if (stat & 2) { + writereg(cs->hw.saphir.ale, cs->hw.saphir.isac, ISAC_MASK, 0xFF); + writereg(cs->hw.saphir.ale, cs->hw.saphir.isac, ISAC_MASK, 0); + } +} + +static void +SaphirWatchDog(struct IsdnCardState *cs) +{ + /* 5 sec WatchDog, so read at least every 4 sec */ + cs->readisac(cs, ISAC_RBCH); + del_timer(&cs->hw.saphir.timer); + cs->hw.saphir.timer.expires = jiffies + 1*HZ; + add_timer(&cs->hw.saphir.timer); +} + +void +release_io_saphir(struct IsdnCardState *cs) +{ + long flags; + + save_flags(flags); + cli(); + byteout(cs->hw.saphir.cfg_reg + IRQ_REG, 0xff); + del_timer(&cs->hw.saphir.timer); + cs->hw.saphir.timer.function = NULL; + restore_flags(flags); + if (cs->hw.saphir.cfg_reg) + release_region(cs->hw.saphir.cfg_reg, 6); +} + +static int +saphir_reset(struct IsdnCardState *cs) +{ + long flags; + u_char irq_val; + + switch(cs->irq) { + case 5: irq_val = 0; + break; + case 3: irq_val = 1; + break; + case 11: + irq_val = 2; + break; + case 12: + irq_val = 3; + break; + case 15: + irq_val = 4; + break; + default: + printk(KERN_WARNING "HiSax: saphir wrong IRQ %d\n", + cs->irq); + return (1); + } + byteout(cs->hw.saphir.cfg_reg + IRQ_REG, irq_val); + save_flags(flags); + sti(); + byteout(cs->hw.saphir.cfg_reg + RESET_REG, 1); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((30*HZ)/1000); /* Timeout 30ms */ + byteout(cs->hw.saphir.cfg_reg + RESET_REG, 0); + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((30*HZ)/1000); /* Timeout 30ms */ + restore_flags(flags); + byteout(cs->hw.saphir.cfg_reg + IRQ_REG, irq_val); + byteout(cs->hw.saphir.cfg_reg + SPARE_REG, 0x02); + return (0); +} + +static int +saphir_card_msg(struct IsdnCardState *cs, int mt, void *arg) +{ + switch (mt) { + case CARD_RESET: + saphir_reset(cs); + return(0); + case CARD_RELEASE: + release_io_saphir(cs); + return(0); + case CARD_SETIRQ: + return(request_irq(cs->irq, &saphir_interrupt, + I4L_IRQ_FLAG, "HiSax", cs)); + case CARD_INIT: + inithscxisac(cs, 3); + return(0); + case CARD_TEST: + return(0); + } + return(0); +} + + +__initfunc(int +setup_saphir(struct IsdnCard *card)) +{ + struct IsdnCardState *cs = card->cs; + char tmp[64]; + + strcpy(tmp, saphir_rev); + printk(KERN_INFO "HiSax: HST Saphir driver Rev. %s\n", HiSax_getrev(tmp)); + if (cs->typ != ISDN_CTYPE_HSTSAPHIR) + return (0); + + /* IO-Ports */ + cs->hw.saphir.cfg_reg = card->para[1]; + cs->hw.saphir.isac = card->para[1] + ISAC_DATA; + cs->hw.saphir.hscx = card->para[1] + HSCX_DATA; + cs->hw.saphir.ale = card->para[1] + ADDRESS_REG; + cs->irq = card->para[0]; + if (check_region((cs->hw.saphir.cfg_reg), 6)) { + printk(KERN_WARNING + "HiSax: %s config port %x-%x already in use\n", + CardType[card->typ], + cs->hw.saphir.cfg_reg, + cs->hw.saphir.cfg_reg + 5); + return (0); + } else + request_region(cs->hw.saphir.cfg_reg,6, "saphir"); + + printk(KERN_INFO + "HiSax: %s config irq:%d io:0x%X\n", + CardType[cs->typ], cs->irq, + cs->hw.saphir.cfg_reg); + + cs->hw.saphir.timer.function = (void *) SaphirWatchDog; + cs->hw.saphir.timer.data = (long) cs; + init_timer(&cs->hw.saphir.timer); + cs->hw.saphir.timer.expires = jiffies + 4*HZ; + add_timer(&cs->hw.saphir.timer); + if (saphir_reset(cs)) { + release_io_saphir(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 = &saphir_card_msg; + ISACVersion(cs, "saphir:"); + if (HscxVersion(cs, "saphir:")) { + printk(KERN_WARNING + "saphir: wrong HSCX versions check IO address\n"); + release_io_saphir(cs); + return (0); + } + return (1); +}