Commit 98c4217c authored by Kai Germaschewski's avatar Kai Germaschewski

ISDN: Add support for Eicon Diva 2.02

(by Joerg Petersohn/Karsten Keil)
parent 91239976
...@@ -41,9 +41,10 @@ ELSA Quickstep 3000 (same settings as QS1000) ...@@ -41,9 +41,10 @@ ELSA Quickstep 3000 (same settings as QS1000)
ELSA Quickstep 3000PCI ELSA Quickstep 3000PCI
ELSA PCMCIA ELSA PCMCIA
ITK ix1-micro Rev.2 ITK ix1-micro Rev.2
Eicon.Diehl Diva 2.0 ISA and PCI (S0 and U interface, no PRO version) Eicon Diva 2.0 ISA and PCI (S0 and U interface, no PRO version)
Eicon.Diehl Diva 2.01 ISA and PCI Eicon Diva 2.01 ISA and PCI
Eicon.Diehl Diva Piccola Eicon Diva 2.02 PCI
Eicon Diva Piccola
ASUSCOM NETWORK INC. ISDNLink 128K PC adapter (order code I-IN100-ST-D) ASUSCOM NETWORK INC. ISDNLink 128K PC adapter (order code I-IN100-ST-D)
Dynalink IS64PH (OEM version of ASUSCOM NETWORK INC. ISDNLink 128K adapter) Dynalink IS64PH (OEM version of ASUSCOM NETWORK INC. ISDNLink 128K adapter)
PCBIT-DP (OEM version of ASUSCOM NETWORK INC. ISDNLink) PCBIT-DP (OEM version of ASUSCOM NETWORK INC. ISDNLink)
......
...@@ -39,7 +39,7 @@ hisax-objs-$(CONFIG_HISAX_AVM_A1_PCMCIA) += avm_a1p.o isac.o arcofi.o hscx.o ...@@ -39,7 +39,7 @@ hisax-objs-$(CONFIG_HISAX_AVM_A1_PCMCIA) += avm_a1p.o isac.o arcofi.o hscx.o
hisax-objs-$(CONFIG_HISAX_FRITZPCI) += avm_pci.o isac.o arcofi.o hisax-objs-$(CONFIG_HISAX_FRITZPCI) += avm_pci.o isac.o arcofi.o
hisax-objs-$(CONFIG_HISAX_ELSA) += elsa.o isac.o arcofi.o hscx.o hisax-objs-$(CONFIG_HISAX_ELSA) += elsa.o isac.o arcofi.o hscx.o
hisax-objs-$(CONFIG_HISAX_IX1MICROR2) += ix1_micro.o isac.o arcofi.o hscx.o hisax-objs-$(CONFIG_HISAX_IX1MICROR2) += ix1_micro.o isac.o arcofi.o hscx.o
hisax-objs-$(CONFIG_HISAX_DIEHLDIVA) += diva.o isac.o arcofi.o hscx.o hisax-objs-$(CONFIG_HISAX_DIEHLDIVA) += diva.o isac.o arcofi.o hscx.o ipacx.o
hisax-objs-$(CONFIG_HISAX_ASUSCOM) += asuscom.o isac.o arcofi.o hscx.o hisax-objs-$(CONFIG_HISAX_ASUSCOM) += asuscom.o isac.o arcofi.o hscx.o
hisax-objs-$(CONFIG_HISAX_TELEINT) += teleint.o isac.o arcofi.o hfc_2bs0.o hisax-objs-$(CONFIG_HISAX_TELEINT) += teleint.o isac.o arcofi.o hfc_2bs0.o
hisax-objs-$(CONFIG_HISAX_SEDLBAUER) += sedlbauer.o isac.o arcofi.o hscx.o isar.o hisax-objs-$(CONFIG_HISAX_SEDLBAUER) += sedlbauer.o isac.o arcofi.o hscx.o isar.o
......
...@@ -2094,6 +2094,7 @@ static struct pci_device_id hisax_pci_tbl[] __initdata = { ...@@ -2094,6 +2094,7 @@ static struct pci_device_id hisax_pci_tbl[] __initdata = {
{PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA20, PCI_ANY_ID, PCI_ANY_ID}, {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA20, PCI_ANY_ID, PCI_ANY_ID},
{PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA20_U, PCI_ANY_ID, PCI_ANY_ID}, {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA20_U, PCI_ANY_ID, PCI_ANY_ID},
{PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA201, PCI_ANY_ID, PCI_ANY_ID}, {PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA201, PCI_ANY_ID, PCI_ANY_ID},
{PCI_VENDOR_ID_EICON, PCI_DEVICE_ID_EICON_DIVA202, PCI_ANY_ID, PCI_ANY_ID},
#endif #endif
#ifdef CONFIG_HISAX_ELSA #ifdef CONFIG_HISAX_ELSA
{PCI_VENDOR_ID_ELSA, PCI_DEVICE_ID_ELSA_MICROLINK, PCI_ANY_ID, PCI_ANY_ID}, {PCI_VENDOR_ID_ELSA, PCI_DEVICE_ID_ELSA_MICROLINK, PCI_ANY_ID, PCI_ANY_ID},
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "isac.h" #include "isac.h"
#include "hscx.h" #include "hscx.h"
#include "ipac.h" #include "ipac.h"
#include "ipacx.h"
#include "isdnl1.h" #include "isdnl1.h"
#include <linux/pci.h> #include <linux/pci.h>
...@@ -49,6 +50,7 @@ const char *Diva_revision = "$Revision: 1.25.6.5 $"; ...@@ -49,6 +50,7 @@ const char *Diva_revision = "$Revision: 1.25.6.5 $";
#define DIVA_PCI 2 #define DIVA_PCI 2
#define DIVA_IPAC_ISA 3 #define DIVA_IPAC_ISA 3
#define DIVA_IPAC_PCI 4 #define DIVA_IPAC_PCI 4
#define DIVA_IPACX_PCI 5
/* CTRL (Read) */ /* CTRL (Read) */
#define DIVA_IRQ_STAT 0x01 #define DIVA_IRQ_STAT 0x01
...@@ -68,10 +70,12 @@ const char *Diva_revision = "$Revision: 1.25.6.5 $"; ...@@ -68,10 +70,12 @@ const char *Diva_revision = "$Revision: 1.25.6.5 $";
#define PITA_MISC_REG 0x1c #define PITA_MISC_REG 0x1c
#ifdef __BIG_ENDIAN #ifdef __BIG_ENDIAN
#define PITA_PARA_SOFTRESET 0x00000001 #define PITA_PARA_SOFTRESET 0x00000001
#define PITA_SER_SOFTRESET 0x00000002
#define PITA_PARA_MPX_MODE 0x00000004 #define PITA_PARA_MPX_MODE 0x00000004
#define PITA_INT0_ENABLE 0x00000200 #define PITA_INT0_ENABLE 0x00000200
#else #else
#define PITA_PARA_SOFTRESET 0x01000000 #define PITA_PARA_SOFTRESET 0x01000000
#define PITA_SER_SOFTRESET 0x02000000
#define PITA_PARA_MPX_MODE 0x04000000 #define PITA_PARA_MPX_MODE 0x04000000
#define PITA_INT0_ENABLE 0x00020000 #define PITA_INT0_ENABLE 0x00020000
#endif #endif
...@@ -239,6 +243,47 @@ MemWriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value) ...@@ -239,6 +243,47 @@ MemWriteHSCX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
memwritereg(cs->hw.diva.cfg_reg, offset + (hscx ? 0x40 : 0), value); memwritereg(cs->hw.diva.cfg_reg, offset + (hscx ? 0x40 : 0), value);
} }
/* IO-Functions for IPACX type cards */
static u_char
MemReadISAC_IPACX(struct IsdnCardState *cs, u_char offset)
{
return (memreadreg(cs->hw.diva.cfg_reg, offset));
}
static void
MemWriteISAC_IPACX(struct IsdnCardState *cs, u_char offset, u_char value)
{
memwritereg(cs->hw.diva.cfg_reg, offset, value);
}
static void
MemReadISACfifo_IPACX(struct IsdnCardState *cs, u_char * data, int size)
{
while(size--)
*data++ = memreadreg(cs->hw.diva.cfg_reg, 0);
}
static void
MemWriteISACfifo_IPACX(struct IsdnCardState *cs, u_char * data, int size)
{
while(size--)
memwritereg(cs->hw.diva.cfg_reg, 0, *data++);
}
static u_char
MemReadHSCX_IPACX(struct IsdnCardState *cs, int hscx, u_char offset)
{
return(memreadreg(cs->hw.diva.cfg_reg, offset +
(hscx ? IPACX_OFF_B2 : IPACX_OFF_B1)));
}
static void
MemWriteHSCX_IPACX(struct IsdnCardState *cs, int hscx, u_char offset, u_char value)
{
memwritereg(cs->hw.diva.cfg_reg, offset +
(hscx ? IPACX_OFF_B2 : IPACX_OFF_B1), value);
}
/* /*
* fast interrupt HSCX stuff goes here * fast interrupt HSCX stuff goes here
*/ */
...@@ -549,7 +594,7 @@ Memhscx_int_main(struct IsdnCardState *cs, u_char val) ...@@ -549,7 +594,7 @@ Memhscx_int_main(struct IsdnCardState *cs, u_char val)
u_char exval; u_char exval;
struct BCState *bcs; struct BCState *bcs;
if (val & 0x01) { if (val & 0x01) { // EXB
bcs = cs->bcs + 1; bcs = cs->bcs + 1;
exval = MemReadHSCX(cs, 1, HSCX_EXIR); exval = MemReadHSCX(cs, 1, HSCX_EXIR);
if (exval & 0x40) { if (exval & 0x40) {
...@@ -576,7 +621,7 @@ Memhscx_int_main(struct IsdnCardState *cs, u_char val) ...@@ -576,7 +621,7 @@ Memhscx_int_main(struct IsdnCardState *cs, u_char val)
debugl1(cs, "HSCX B interrupt %x", val); debugl1(cs, "HSCX B interrupt %x", val);
Memhscx_interrupt(cs, val, 1); Memhscx_interrupt(cs, val, 1);
} }
if (val & 0x02) { if (val & 0x02) { // EXA
bcs = cs->bcs; bcs = cs->bcs;
exval = MemReadHSCX(cs, 0, HSCX_EXIR); exval = MemReadHSCX(cs, 0, HSCX_EXIR);
if (exval & 0x40) { if (exval & 0x40) {
...@@ -598,7 +643,7 @@ Memhscx_int_main(struct IsdnCardState *cs, u_char val) ...@@ -598,7 +643,7 @@ Memhscx_int_main(struct IsdnCardState *cs, u_char val)
} else if (cs->debug & L1_DEB_HSCX) } else if (cs->debug & L1_DEB_HSCX)
debugl1(cs, "HSCX A EXIR %x", exval); debugl1(cs, "HSCX A EXIR %x", exval);
} }
if (val & 0x04) { if (val & 0x04) { // ICA
exval = MemReadHSCX(cs, 0, HSCX_ISTA); exval = MemReadHSCX(cs, 0, HSCX_ISTA);
if (cs->debug & L1_DEB_HSCX) if (cs->debug & L1_DEB_HSCX)
debugl1(cs, "HSCX A interrupt %x", exval); debugl1(cs, "HSCX A interrupt %x", exval);
...@@ -659,12 +704,31 @@ diva_irq_ipac_pci(int intno, void *dev_id, struct pt_regs *regs) ...@@ -659,12 +704,31 @@ diva_irq_ipac_pci(int intno, void *dev_id, struct pt_regs *regs)
memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xC0); memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xC0);
} }
static void
diva_irq_ipacx_pci(int intno, void *dev_id, struct pt_regs *regs)
{
struct IsdnCardState *cs = dev_id;
u_char val;
u_char *cfg;
if (!cs) {
printk(KERN_WARNING "Diva: Spurious interrupt!\n");
return;
}
cfg = (u_char *) cs->hw.diva.pci_cfg;
val = *cfg;
if (!(val &PITA_INT0_STATUS)) return; // other shared IRQ
interrupt_ipacx(cs); // handler for chip
*cfg = PITA_INT0_STATUS; // Reset PLX interrupt
}
void void
release_io_diva(struct IsdnCardState *cs) release_io_diva(struct IsdnCardState *cs)
{ {
int bytecnt; int bytecnt;
if (cs->subtyp == DIVA_IPAC_PCI) { if ((cs->subtyp == DIVA_IPAC_PCI) ||
(cs->subtyp == DIVA_IPACX_PCI) ) {
u_int *cfg = (unsigned int *)cs->hw.diva.pci_cfg; u_int *cfg = (unsigned int *)cs->hw.diva.pci_cfg;
*cfg = 0; /* disable INT0/1 */ *cfg = 0; /* disable INT0/1 */
...@@ -711,6 +775,16 @@ reset_diva(struct IsdnCardState *cs) ...@@ -711,6 +775,16 @@ reset_diva(struct IsdnCardState *cs)
set_current_state(TASK_UNINTERRUPTIBLE); set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout((10*HZ)/1000); schedule_timeout((10*HZ)/1000);
memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xc0); memwritereg(cs->hw.diva.cfg_reg, IPAC_MASK, 0xc0);
} else if (cs->subtyp == DIVA_IPACX_PCI) {
unsigned int *ireg = (unsigned int *)(cs->hw.diva.pci_cfg +
PITA_MISC_REG);
*ireg = PITA_PARA_SOFTRESET | PITA_PARA_MPX_MODE;
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout((10*HZ)/1000);
*ireg = PITA_PARA_MPX_MODE | PITA_SER_SOFTRESET;
set_current_state(TASK_UNINTERRUPTIBLE);
schedule_timeout((10*HZ)/1000);
MemWriteISAC_IPACX(cs, IPACX_MASK, 0xff); // Interrupts off
} else { /* DIVA 2.0 */ } else { /* DIVA 2.0 */
cs->hw.diva.ctrl_reg = 0; /* Reset On */ cs->hw.diva.ctrl_reg = 0; /* Reset On */
byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg); byteout(cs->hw.diva.ctrl, cs->hw.diva.ctrl_reg);
...@@ -739,7 +813,10 @@ diva_led_handler(struct IsdnCardState *cs) ...@@ -739,7 +813,10 @@ diva_led_handler(struct IsdnCardState *cs)
{ {
int blink = 0; int blink = 0;
if ((cs->subtyp == DIVA_IPAC_ISA) || (cs->subtyp == DIVA_IPAC_PCI)) // if ((cs->subtyp == DIVA_IPAC_ISA) || (cs->subtyp == DIVA_IPAC_PCI))
if ((cs->subtyp == DIVA_IPAC_ISA) ||
(cs->subtyp == DIVA_IPAC_PCI) ||
(cs->subtyp == DIVA_IPACX_PCI) )
return; return;
del_timer(&cs->hw.diva.tl); del_timer(&cs->hw.diva.tl);
if (cs->hw.diva.status & DIVA_ASSIGN) if (cs->hw.diva.status & DIVA_ASSIGN)
...@@ -782,6 +859,12 @@ Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg) ...@@ -782,6 +859,12 @@ Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg)
release_io_diva(cs); release_io_diva(cs);
return(0); return(0);
case CARD_INIT: case CARD_INIT:
if (cs->subtyp == DIVA_IPACX_PCI) {
ireg = (unsigned int *)cs->hw.diva.pci_cfg;
*ireg = PITA_INT0_ENABLE;
init_ipacx(cs, 3); // init chip and enable interrupts
return (0);
}
if (cs->subtyp == DIVA_IPAC_PCI) { if (cs->subtyp == DIVA_IPAC_PCI) {
ireg = (unsigned int *)cs->hw.diva.pci_cfg; ireg = (unsigned int *)cs->hw.diva.pci_cfg;
*ireg = PITA_INT0_ENABLE; *ireg = PITA_INT0_ENABLE;
...@@ -818,7 +901,9 @@ Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg) ...@@ -818,7 +901,9 @@ Diva_card_msg(struct IsdnCardState *cs, int mt, void *arg)
} }
break; break;
} }
if ((cs->subtyp != DIVA_IPAC_ISA) && (cs->subtyp != DIVA_IPAC_PCI)) if ((cs->subtyp != DIVA_IPAC_ISA) &&
(cs->subtyp != DIVA_IPAC_PCI) &&
(cs->subtyp != DIVA_IPACX_PCI) )
diva_led_handler(cs); diva_led_handler(cs);
return(0); return(0);
} }
...@@ -916,7 +1001,8 @@ setup_diva(struct IsdnCard *card) ...@@ -916,7 +1001,8 @@ setup_diva(struct IsdnCard *card)
printk(KERN_WARNING "Diva: unable to config DIVA PCI\n"); printk(KERN_WARNING "Diva: unable to config DIVA PCI\n");
return (0); return (0);
#endif /* CONFIG_PCI */ #endif /* CONFIG_PCI */
if (cs->subtyp == DIVA_IPAC_PCI) { if ((cs->subtyp == DIVA_IPAC_PCI) ||
(cs->subtyp == DIVA_IPACX_PCI) ) {
cs->hw.diva.ctrl = 0; cs->hw.diva.ctrl = 0;
cs->hw.diva.isac = 0; cs->hw.diva.isac = 0;
cs->hw.diva.hscx = 0; cs->hw.diva.hscx = 0;
...@@ -938,13 +1024,18 @@ setup_diva(struct IsdnCard *card) ...@@ -938,13 +1024,18 @@ setup_diva(struct IsdnCard *card)
"Diva: %s card configured at %#lx IRQ %d\n", "Diva: %s card configured at %#lx IRQ %d\n",
(cs->subtyp == DIVA_PCI) ? "PCI" : (cs->subtyp == DIVA_PCI) ? "PCI" :
(cs->subtyp == DIVA_ISA) ? "ISA" : (cs->subtyp == DIVA_ISA) ? "ISA" :
(cs->subtyp == DIVA_IPAC_ISA) ? "IPAC ISA" : "IPAC PCI", (cs->subtyp == DIVA_IPAC_ISA) ? "IPAC ISA" :
(cs->subtyp == DIVA_IPAC_PCI) ? "IPAC PCI" : "IPACX PCI",
cs->hw.diva.cfg_reg, cs->irq); cs->hw.diva.cfg_reg, cs->irq);
if ((cs->subtyp == DIVA_IPAC_PCI) || (cs->subtyp == DIVA_PCI)) if ((cs->subtyp == DIVA_IPAC_PCI) ||
printk(KERN_INFO "Diva: %s PCI space at %#lx\n", (cs->subtyp == DIVA_IPACX_PCI) ||
(cs->subtyp == DIVA_PCI) ? "PCI" : "IPAC PCI", (cs->subtyp == DIVA_PCI) )
printk(KERN_INFO "Diva: %s space at %#lx\n",
(cs->subtyp == DIVA_PCI) ? "PCI" :
(cs->subtyp == DIVA_IPAC_PCI) ? "IPAC PCI" : "IPACX PCI",
cs->hw.diva.pci_cfg); cs->hw.diva.pci_cfg);
if (cs->subtyp != DIVA_IPAC_PCI) { if ((cs->subtyp != DIVA_IPAC_PCI) &&
(cs->subtyp != DIVA_IPACX_PCI) ) {
if (check_region(cs->hw.diva.cfg_reg, bytecnt)) { if (check_region(cs->hw.diva.cfg_reg, bytecnt)) {
printk(KERN_WARNING printk(KERN_WARNING
"HiSax: %s config port %lx-%lx already in use\n", "HiSax: %s config port %lx-%lx already in use\n",
...@@ -980,6 +1071,17 @@ setup_diva(struct IsdnCard *card) ...@@ -980,6 +1071,17 @@ setup_diva(struct IsdnCard *card)
cs->irq_func = &diva_irq_ipac_pci; cs->irq_func = &diva_irq_ipac_pci;
val = memreadreg(cs->hw.diva.cfg_reg, IPAC_ID); val = memreadreg(cs->hw.diva.cfg_reg, IPAC_ID);
printk(KERN_INFO "Diva: IPAC version %x\n", val); printk(KERN_INFO "Diva: IPAC version %x\n", val);
} else if (cs->subtyp == DIVA_IPACX_PCI) {
cs->readisac = &MemReadISAC_IPACX;
cs->writeisac = &MemWriteISAC_IPACX;
cs->readisacfifo = &MemReadISACfifo_IPACX;
cs->writeisacfifo = &MemWriteISACfifo_IPACX;
cs->BC_Read_Reg = &MemReadHSCX_IPACX;
cs->BC_Write_Reg = &MemWriteHSCX_IPACX;
cs->BC_Send_Data = 0; // function located in ipacx module
cs->irq_func = &diva_irq_ipacx_pci;
printk(KERN_INFO "Diva: IPACX Design Id: %x\n",
MemReadISAC_IPACX(cs, IPACX_ID) &0x3F);
} else { /* DIVA 2.0 */ } else { /* DIVA 2.0 */
cs->hw.diva.tl.function = (void *) diva_led_handler; cs->hw.diva.tl.function = (void *) diva_led_handler;
cs->hw.diva.tl.data = (long) cs; cs->hw.diva.tl.data = (long) cs;
......
/*
*
* IPACX specific routines
*
* Author Joerg Petersohn
* Derived from hisax_isac.c, isac.c, hscx.c and others
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
#define __NO_VERSION__
#include <linux/kernel.h>
#include <linux/config.h>
#include <linux/init.h>
#include "hisax_if.h"
#include "hisax.h"
#include "isdnl1.h"
#include "ipacx.h"
#define DBUSY_TIMER_VALUE 80
#define TIMER3_VALUE 7000
#define MAX_DFRAME_LEN_L1 300
#define B_FIFO_SIZE 64
#define D_FIFO_SIZE 32
// ipacx interrupt mask values
#define _MASK_IMASK 0x2E // global mask
#define _MASKB_IMASK 0x0B
#define _MASKD_IMASK 0x03 // all on
//----------------------------------------------------------
// local function declarations
//----------------------------------------------------------
static void ph_command(struct IsdnCardState *cs, unsigned int command);
static inline void cic_int(struct IsdnCardState *cs);
static void dch_l2l1(struct PStack *st, int pr, void *arg);
static void dbusy_timer_handler(struct IsdnCardState *cs);
static void ipacx_new_ph(struct IsdnCardState *cs);
static void dch_bh(struct IsdnCardState *cs);
static void dch_sched_event(struct IsdnCardState *cs, int event);
static void dch_empty_fifo(struct IsdnCardState *cs, int count);
static void dch_fill_fifo(struct IsdnCardState *cs);
static inline void dch_int(struct IsdnCardState *cs);
static void __devinit dch_setstack(struct PStack *st, struct IsdnCardState *cs);
static void __devinit dch_init(struct IsdnCardState *cs);
static void bch_l2l1(struct PStack *st, int pr, void *arg);
static void bch_sched_event(struct BCState *bcs, int event);
static void bch_empty_fifo(struct BCState *bcs, int count);
static void bch_fill_fifo(struct BCState *bcs);
static void bch_int(struct IsdnCardState *cs, u_char hscx);
static void bch_mode(struct BCState *bcs, int mode, int bc);
static void bch_close_state(struct BCState *bcs);
static int bch_open_state(struct IsdnCardState *cs, struct BCState *bcs);
static int bch_setstack(struct PStack *st, struct BCState *bcs);
static void __devinit bch_init(struct IsdnCardState *cs, int hscx);
static void __init clear_pending_ints(struct IsdnCardState *cs);
//----------------------------------------------------------
// Issue Layer 1 command to chip
//----------------------------------------------------------
static void
ph_command(struct IsdnCardState *cs, unsigned int command)
{
if (cs->debug &L1_DEB_ISAC)
debugl1(cs, "ph_command (%#x) in (%#x)", command,
cs->dc.isac.ph_state);
//###################################
// printk(KERN_INFO "ph_command (%#x)\n", command);
//###################################
cs->writeisac(cs, IPACX_CIX0, (command << 4) | 0x0E);
}
//----------------------------------------------------------
// Transceiver interrupt handler
//----------------------------------------------------------
static inline void
cic_int(struct IsdnCardState *cs)
{
u_char event;
event = cs->readisac(cs, IPACX_CIR0) >> 4;
if (cs->debug &L1_DEB_ISAC) debugl1(cs, "cic_int(event=%#x)", event);
//#########################################
// printk(KERN_INFO "cic_int(%x)\n", event);
//#########################################
cs->dc.isac.ph_state = event;
dch_sched_event(cs, D_L1STATECHANGE);
}
//==========================================================
// D channel functions
//==========================================================
//----------------------------------------------------------
// Command entry point
//----------------------------------------------------------
static void
dch_l2l1(struct PStack *st, int pr, void *arg)
{
struct IsdnCardState *cs = (struct IsdnCardState *) st->l1.hardware;
struct sk_buff *skb = arg;
u_char cda1_cr, cda2_cr;
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
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
if (cs->debug &L1_DEB_LAPD) Logl2Frame(cs, skb, "PH_DATA", 0);
#endif
dch_fill_fifo(cs);
}
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
if (cs->debug & L1_DEB_LAPD) Logl2Frame(cs, skb, "PH_DATA_PULLED", 0);
#endif
dch_fill_fifo(cs);
break;
case (PH_PULL | REQUEST):
#ifdef L2FRAME_DEBUG
if (cs->debug & L1_DEB_LAPD) debugl1(cs, "-> PH_REQUEST_PULL");
#endif
if (!cs->tx_skb) {
clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
st->l2.l1l2(st, PH_PULL | CONFIRM, NULL);
} else
set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
case (HW_RESET | REQUEST):
case (HW_ENABLE | REQUEST):
ph_command(cs, IPACX_CMD_TIM);
break;
case (HW_INFO3 | REQUEST):
ph_command(cs, IPACX_CMD_AR8);
break;
case (HW_TESTLOOP | REQUEST):
cs->writeisac(cs, IPACX_CDA_TSDP10, 0x80); // Timeslot 0 is B1
cs->writeisac(cs, IPACX_CDA_TSDP11, 0x81); // Timeslot 0 is B1
cda1_cr = cs->readisac(cs, IPACX_CDA1_CR);
cda2_cr = cs->readisac(cs, IPACX_CDA2_CR);
if ((long)arg &1) { // loop B1
cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr |0x0a);
}
else { // B1 off
cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr &~0x0a);
}
if ((long)arg &2) { // loop B2
cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr |0x14);
}
else { // B2 off
cs->writeisac(cs, IPACX_CDA1_CR, cda1_cr &~0x14);
}
break;
case (HW_DEACTIVATE | RESPONSE):
skb_queue_purge(&cs->rq);
skb_queue_purge(&cs->sq);
if (cs->tx_skb) {
dev_kfree_skb_any(cs->tx_skb);
cs->tx_skb = NULL;
}
if (test_and_clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags))
del_timer(&cs->dbusytimer);
break;
default:
if (cs->debug &L1_DEB_WARN) debugl1(cs, "dch_l2l1 unknown %04x", pr);
break;
}
}
//----------------------------------------------------------
//----------------------------------------------------------
static void
dbusy_timer_handler(struct IsdnCardState *cs)
{
struct PStack *st;
int rbchd, stard;
if (test_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
rbchd = cs->readisac(cs, IPACX_RBCHD);
stard = cs->readisac(cs, IPACX_STARD);
if (cs->debug)
debugl1(cs, "D-Channel Busy RBCHD %02x STARD %02x", rbchd, stard);
if (!(stard &0x40)) { // D-Channel Busy
set_bit(FLG_L1_DBUSY, &cs->HW_Flags);
for (st = cs->stlist; st; st = st->next) {
st->l2.l1l2(st, PH_PAUSE | INDICATION, NULL); // flow control on
}
} else {
// seems we lost an interrupt; reset transceiver */
clear_bit(FLG_DBUSY_TIMER, &cs->HW_Flags);
if (cs->tx_skb) {
dev_kfree_skb_any(cs->tx_skb);
cs->tx_cnt = 0;
cs->tx_skb = NULL;
} else {
printk(KERN_WARNING "HiSax: ISAC D-Channel Busy no skb\n");
debugl1(cs, "D-Channel Busy no skb");
}
cs->writeisac(cs, IPACX_CMDRD, 0x01); // Tx reset, generates XPR
}
}
}
//----------------------------------------------------------
// L1 state machine intermediate layer to isdnl1 module
//----------------------------------------------------------
static void
ipacx_new_ph(struct IsdnCardState *cs)
{
switch (cs->dc.isac.ph_state) {
case (IPACX_IND_RES):
ph_command(cs, IPACX_CMD_DI);
l1_msg(cs, HW_RESET | INDICATION, NULL);
break;
case (IPACX_IND_DC):
l1_msg(cs, HW_DEACTIVATE | CONFIRM, NULL);
break;
case (IPACX_IND_DR):
l1_msg(cs, HW_DEACTIVATE | INDICATION, NULL);
break;
case (IPACX_IND_PU):
l1_msg(cs, HW_POWERUP | CONFIRM, NULL);
break;
case (IPACX_IND_RSY):
l1_msg(cs, HW_RSYNC | INDICATION, NULL);
break;
case (IPACX_IND_AR):
l1_msg(cs, HW_INFO2 | INDICATION, NULL);
break;
case (IPACX_IND_AI8):
l1_msg(cs, HW_INFO4_P8 | INDICATION, NULL);
break;
case (IPACX_IND_AI10):
l1_msg(cs, HW_INFO4_P10 | INDICATION, NULL);
break;
default:
break;
}
}
//----------------------------------------------------------
// bottom half handler for D channel
//----------------------------------------------------------
static void
dch_bh(struct IsdnCardState *cs)
{
struct PStack *st;
if (!cs) return;
if (test_and_clear_bit(D_CLEARBUSY, &cs->event)) {
if (cs->debug) debugl1(cs, "D-Channel Busy cleared");
for (st = cs->stlist; st; st = st->next) {
st->l2.l1l2(st, PH_PAUSE | CONFIRM, NULL);
}
}
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);
}
if (test_and_clear_bit(D_L1STATECHANGE, &cs->event)) {
ipacx_new_ph(cs);
}
}
//----------------------------------------------------------
// proceed with bottom half handler dch_bh()
//----------------------------------------------------------
static void
dch_sched_event(struct IsdnCardState *cs, int event)
{
set_bit(event, &cs->event);
queue_task(&cs->tqueue, &tq_immediate);
mark_bh(IMMEDIATE_BH);
}
//----------------------------------------------------------
// Fill buffer from receive FIFO
//----------------------------------------------------------
static void
dch_empty_fifo(struct IsdnCardState *cs, int count)
{
long flags;
u_char *ptr;
if ((cs->debug &L1_DEB_ISAC) && !(cs->debug &L1_DEB_ISAC_FIFO))
debugl1(cs, "dch_empty_fifo()");
// message too large, remove
if ((cs->rcvidx + count) >= MAX_DFRAME_LEN_L1) {
if (cs->debug &L1_DEB_WARN)
debugl1(cs, "dch_empty_fifo() incoming message too large");
cs->writeisac(cs, IPACX_CMDRD, 0x80); // RMC
cs->rcvidx = 0;
return;
}
ptr = cs->rcvbuf + cs->rcvidx;
cs->rcvidx += count;
save_flags(flags);
cli();
cs->readisacfifo(cs, ptr, count);
cs->writeisac(cs, IPACX_CMDRD, 0x80); // RMC
restore_flags(flags);
if (cs->debug &L1_DEB_ISAC_FIFO) {
char *t = cs->dlog;
t += sprintf(t, "dch_empty_fifo() cnt %d", count);
QuickHex(t, ptr, count);
debugl1(cs, cs->dlog);
}
}
//----------------------------------------------------------
// Fill transmit FIFO
//----------------------------------------------------------
static void
dch_fill_fifo(struct IsdnCardState *cs)
{
long flags;
int count;
u_char cmd, *ptr;
if ((cs->debug &L1_DEB_ISAC) && !(cs->debug &L1_DEB_ISAC_FIFO))
debugl1(cs, "dch_fill_fifo()");
if (!cs->tx_skb) return;
count = cs->tx_skb->len;
if (count <= 0) return;
if (count > D_FIFO_SIZE) {
count = D_FIFO_SIZE;
cmd = 0x08; // XTF
} else {
cmd = 0x0A; // XTF | XME
}
save_flags(flags);
cli();
ptr = cs->tx_skb->data;
skb_pull(cs->tx_skb, count);
cs->tx_cnt += count;
cs->writeisacfifo(cs, ptr, count);
cs->writeisac(cs, IPACX_CMDRD, cmd);
// set timeout for transmission contol
if (test_and_set_bit(FLG_DBUSY_TIMER, &cs->HW_Flags)) {
debugl1(cs, "dch_fill_fifo dbusytimer running");
del_timer(&cs->dbusytimer);
}
init_timer(&cs->dbusytimer);
cs->dbusytimer.expires = jiffies + ((DBUSY_TIMER_VALUE * HZ)/1000);
add_timer(&cs->dbusytimer);
restore_flags(flags);
if (cs->debug &L1_DEB_ISAC_FIFO) {
char *t = cs->dlog;
t += sprintf(t, "dch_fill_fifo() cnt %d", count);
QuickHex(t, ptr, count);
debugl1(cs, cs->dlog);
}
}
//----------------------------------------------------------
// D channel interrupt handler
//----------------------------------------------------------
static inline void
dch_int(struct IsdnCardState *cs)
{
struct sk_buff *skb;
u_char istad, rstad;
long flags;
int count;
istad = cs->readisac(cs, IPACX_ISTAD);
//##############################################
// printk(KERN_WARNING "dch_int(istad=%02x)\n", istad);
//##############################################
if (istad &0x80) { // RME
rstad = cs->readisac(cs, IPACX_RSTAD);
if ((rstad &0xf0) != 0xa0) { // !(VFR && !RDO && CRC && !RAB)
if (!(rstad &0x80))
if (cs->debug &L1_DEB_WARN)
debugl1(cs, "dch_int(): invalid frame");
if ((rstad &0x40))
if (cs->debug &L1_DEB_WARN)
debugl1(cs, "dch_int(): RDO");
if (!(rstad &0x20))
if (cs->debug &L1_DEB_WARN)
debugl1(cs, "dch_int(): CRC error");
cs->writeisac(cs, IPACX_CMDRD, 0x80); // RMC
} else { // received frame ok
count = cs->readisac(cs, IPACX_RBCLD);
if (count) count--; // RSTAB is last byte
count &= D_FIFO_SIZE-1;
if (count == 0) count = D_FIFO_SIZE;
dch_empty_fifo(cs, count);
save_flags(flags);
cli();
if ((count = cs->rcvidx) > 0) {
cs->rcvidx = 0;
if (!(skb = dev_alloc_skb(count)))
printk(KERN_WARNING "HiSax dch_int(): receive out of memory\n");
else {
memcpy(skb_put(skb, count), cs->rcvbuf, count);
skb_queue_tail(&cs->rq, skb);
}
}
restore_flags(flags);
}
cs->rcvidx = 0;
dch_sched_event(cs, D_RCVBUFREADY);
}
if (istad &0x40) { // RPF
dch_empty_fifo(cs, D_FIFO_SIZE);
}
if (istad &0x20) { // RFO
if (cs->debug &L1_DEB_WARN) debugl1(cs, "dch_int(): RFO");
cs->writeisac(cs, IPACX_CMDRD, 0x40); //RRES
}
if (istad &0x10) { // XPR
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))
dch_sched_event(cs, D_CLEARBUSY);
if (cs->tx_skb) {
if (cs->tx_skb->len) {
dch_fill_fifo(cs);
goto afterXPR;
}
else {
dev_kfree_skb_irq(cs->tx_skb);
cs->tx_skb = NULL;
cs->tx_cnt = 0;
}
}
if ((cs->tx_skb = skb_dequeue(&cs->sq))) {
cs->tx_cnt = 0;
dch_fill_fifo(cs);
}
else {
dch_sched_event(cs, D_XMTBUFREADY);
}
}
afterXPR:
if (istad &0x0C) { // XDU or XMR
if (cs->debug &L1_DEB_WARN) debugl1(cs, "dch_int(): XDU");
if (cs->tx_skb) {
skb_push(cs->tx_skb, cs->tx_cnt); // retransmit
cs->tx_cnt = 0;
dch_fill_fifo(cs);
} else {
printk(KERN_WARNING "HiSax: ISAC XDU no skb\n");
debugl1(cs, "ISAC XDU no skb");
}
}
}
//----------------------------------------------------------
//----------------------------------------------------------
static void __devinit
dch_setstack(struct PStack *st, struct IsdnCardState *cs)
{
st->l1.l1hw = dch_l2l1;
}
//----------------------------------------------------------
//----------------------------------------------------------
static void __devinit
dch_init(struct IsdnCardState *cs)
{
printk(KERN_INFO "HiSax: IPACX ISDN driver v0.1.0\n");
cs->tqueue.routine = (void *)(void *) dch_bh;
cs->setstack_d = dch_setstack;
cs->dbusytimer.function = (void *) dbusy_timer_handler;
cs->dbusytimer.data = (long) cs;
init_timer(&cs->dbusytimer);
cs->writeisac(cs, IPACX_TR_CONF0, 0x00); // clear LDD
cs->writeisac(cs, IPACX_TR_CONF2, 0x00); // enable transmitter
cs->writeisac(cs, IPACX_MODED, 0xC9); // transparent mode 0, RAC, stop/go
cs->writeisac(cs, IPACX_MON_CR, 0x00); // disable monitor channel
}
//==========================================================
// B channel functions
//==========================================================
//----------------------------------------------------------
// Entry point for commands
//----------------------------------------------------------
static void
bch_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;
set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
st->l1.bcs->hw.hscx.count = 0;
restore_flags(flags);
bch_fill_fifo(st->l1.bcs);
}
break;
case (PH_PULL | INDICATION):
if (st->l1.bcs->tx_skb) {
printk(KERN_WARNING "HiSax bch_l2l1(): this shouldn't happen\n");
break;
}
set_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
st->l1.bcs->tx_skb = skb;
st->l1.bcs->hw.hscx.count = 0;
bch_fill_fifo(st->l1.bcs);
break;
case (PH_PULL | REQUEST):
if (!st->l1.bcs->tx_skb) {
clear_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
st->l2.l1l2(st, PH_PULL | CONFIRM, NULL);
} else
set_bit(FLG_L1_PULL_REQ, &st->l1.Flags);
break;
case (PH_ACTIVATE | REQUEST):
set_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
bch_mode(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):
clear_bit(BC_FLG_ACTIV, &st->l1.bcs->Flag);
clear_bit(BC_FLG_BUSY, &st->l1.bcs->Flag);
bch_mode(st->l1.bcs, 0, st->l1.bc);
st->l2.l1l2(st, PH_DEACTIVATE | CONFIRM, NULL);
break;
}
}
//----------------------------------------------------------
// proceed with bottom half handler BChannel_bh()
//----------------------------------------------------------
static void
bch_sched_event(struct BCState *bcs, int event)
{
bcs->event |= 1 << event;
queue_task(&bcs->tqueue, &tq_immediate);
mark_bh(IMMEDIATE_BH);
}
//----------------------------------------------------------
// Read B channel fifo to receive buffer
//----------------------------------------------------------
static void
bch_empty_fifo(struct BCState *bcs, int count)
{
u_char *ptr, hscx;
struct IsdnCardState *cs;
long flags;
int cnt;
cs = bcs->cs;
hscx = bcs->hw.hscx.hscx;
if ((cs->debug &L1_DEB_HSCX) && !(cs->debug &L1_DEB_HSCX_FIFO))
debugl1(cs, "bch_empty_fifo()");
// message too large, remove
if (bcs->hw.hscx.rcvidx + count > HSCX_BUFMAX) {
if (cs->debug &L1_DEB_WARN)
debugl1(cs, "bch_empty_fifo() incoming packet too large");
cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x80); // RMC
bcs->hw.hscx.rcvidx = 0;
return;
}
// Read data uninterruptible
save_flags(flags);
cli();
ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
cnt = count;
while (cnt--) *ptr++ = cs->BC_Read_Reg(cs, hscx, IPACX_RFIFOB);
cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x80); // RMC
ptr = bcs->hw.hscx.rcvbuf + bcs->hw.hscx.rcvidx;
bcs->hw.hscx.rcvidx += count;
restore_flags(flags);
if (cs->debug &L1_DEB_HSCX_FIFO) {
char *t = bcs->blog;
t += sprintf(t, "bch_empty_fifo() B-%d cnt %d", hscx, count);
QuickHex(t, ptr, count);
debugl1(cs, bcs->blog);
}
}
//----------------------------------------------------------
// Fill buffer to transmit FIFO
//----------------------------------------------------------
static void
bch_fill_fifo(struct BCState *bcs)
{
struct IsdnCardState *cs;
int more, count, cnt;
u_char *ptr, *p, hscx;
long flags;
cs = bcs->cs;
if ((cs->debug &L1_DEB_HSCX) && !(cs->debug &L1_DEB_HSCX_FIFO))
debugl1(cs, "bch_fill_fifo()");
if (!bcs->tx_skb) return;
if (bcs->tx_skb->len <= 0) return;
hscx = bcs->hw.hscx.hscx;
more = (bcs->mode == L1_MODE_TRANS) ? 1 : 0;
if (bcs->tx_skb->len > B_FIFO_SIZE) {
more = 1;
count = B_FIFO_SIZE;
} else {
count = bcs->tx_skb->len;
}
cnt = count;
save_flags(flags);
cli();
p = ptr = bcs->tx_skb->data;
skb_pull(bcs->tx_skb, count);
bcs->tx_cnt -= count;
bcs->hw.hscx.count += count;
while (cnt--) cs->BC_Write_Reg(cs, hscx, IPACX_XFIFOB, *p++);
cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, (more ? 0x08 : 0x0a));
restore_flags(flags);
if (cs->debug &L1_DEB_HSCX_FIFO) {
char *t = bcs->blog;
t += sprintf(t, "chb_fill_fifo() B-%d cnt %d", hscx, count);
QuickHex(t, ptr, count);
debugl1(cs, bcs->blog);
}
}
//----------------------------------------------------------
// B channel interrupt handler
//----------------------------------------------------------
static void
bch_int(struct IsdnCardState *cs, u_char hscx)
{
u_char istab;
struct BCState *bcs;
struct sk_buff *skb;
int count;
u_char rstab;
bcs = cs->bcs + hscx;
istab = cs->BC_Read_Reg(cs, hscx, IPACX_ISTAB);
//##############################################
// printk(KERN_WARNING "bch_int(istab=%02x)\n", istab);
//##############################################
if (!test_bit(BC_FLG_INIT, &bcs->Flag)) return;
if (istab &0x80) { // RME
rstab = cs->BC_Read_Reg(cs, hscx, IPACX_RSTAB);
if ((rstab &0xf0) != 0xa0) { // !(VFR && !RDO && CRC && !RAB)
if (!(rstab &0x80))
if (cs->debug &L1_DEB_WARN)
debugl1(cs, "bch_int() B-%d: invalid frame", hscx);
if ((rstab &0x40) && (bcs->mode != L1_MODE_NULL))
if (cs->debug &L1_DEB_WARN)
debugl1(cs, "bch_int() B-%d: RDO mode=%d", hscx, bcs->mode);
if (!(rstab &0x20))
if (cs->debug &L1_DEB_WARN)
debugl1(cs, "bch_int() B-%d: CRC error", hscx);
cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x80); // RMC
}
else { // received frame ok
count = cs->BC_Read_Reg(cs, hscx, IPACX_RBCLB) &(B_FIFO_SIZE-1);
if (count == 0) count = B_FIFO_SIZE;
bch_empty_fifo(bcs, count);
if ((count = bcs->hw.hscx.rcvidx - 1) > 0) {
if (cs->debug &L1_DEB_HSCX_FIFO)
debugl1(cs, "bch_int Frame %d", count);
if (!(skb = dev_alloc_skb(count)))
printk(KERN_WARNING "HiSax bch_int(): receive frame out of memory\n");
else {
memcpy(skb_put(skb, count), bcs->hw.hscx.rcvbuf, count);
skb_queue_tail(&bcs->rqueue, skb);
}
}
}
bcs->hw.hscx.rcvidx = 0;
bch_sched_event(bcs, B_RCVBUFREADY);
}
if (istab &0x40) { // RPF
bch_empty_fifo(bcs, B_FIFO_SIZE);
if (bcs->mode == L1_MODE_TRANS) { // queue every chunk
// receive transparent audio data
if (!(skb = dev_alloc_skb(B_FIFO_SIZE)))
printk(KERN_WARNING "HiSax bch_int(): receive transparent out of memory\n");
else {
memcpy(skb_put(skb, B_FIFO_SIZE), bcs->hw.hscx.rcvbuf, B_FIFO_SIZE);
skb_queue_tail(&bcs->rqueue, skb);
}
bcs->hw.hscx.rcvidx = 0;
bch_sched_event(bcs, B_RCVBUFREADY);
}
}
if (istab &0x20) { // RFO
if (cs->debug &L1_DEB_WARN)
debugl1(cs, "bch_int() B-%d: RFO error", hscx);
cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x40); // RRES
}
if (istab &0x10) { // XPR
if (bcs->tx_skb) {
if (bcs->tx_skb->len) {
bch_fill_fifo(bcs);
goto afterXPR;
}
else {
if (bcs->st->lli.l1writewakeup &&
(PACKET_NOACK != bcs->tx_skb->pkt_type)) {
bcs->st->lli.l1writewakeup(bcs->st, bcs->hw.hscx.count);
}
dev_kfree_skb_irq(bcs->tx_skb);
bcs->hw.hscx.count = 0;
bcs->tx_skb = NULL;
}
}
if ((bcs->tx_skb = skb_dequeue(&bcs->squeue))) {
bcs->hw.hscx.count = 0;
set_bit(BC_FLG_BUSY, &bcs->Flag);
bch_fill_fifo(bcs);
} else {
clear_bit(BC_FLG_BUSY, &bcs->Flag);
bch_sched_event(bcs, B_XMTBUFREADY);
}
}
afterXPR:
if (istab &0x04) { // XDU
if (bcs->mode == L1_MODE_TRANS) {
bch_fill_fifo(bcs);
}
else {
if (bcs->tx_skb) { // restart transmitting the whole frame
skb_push(bcs->tx_skb, bcs->hw.hscx.count);
bcs->tx_cnt += bcs->hw.hscx.count;
bcs->hw.hscx.count = 0;
}
cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x01); // XRES
if (cs->debug &L1_DEB_WARN)
debugl1(cs, "bch_int() B-%d XDU error", hscx);
}
}
}
//----------------------------------------------------------
//----------------------------------------------------------
static void
bch_mode(struct BCState *bcs, int mode, int bc)
{
struct IsdnCardState *cs = bcs->cs;
int hscx = bcs->hw.hscx.hscx;
bc = bc ? 1 : 0; // in case bc is greater than 1
if (cs->debug & L1_DEB_HSCX)
debugl1(cs, "mode_bch() switch B-% mode %d chan %d", hscx, mode, bc);
bcs->mode = mode;
bcs->channel = bc;
// map controller to according timeslot
if (!hscx)
{
cs->writeisac(cs, IPACX_BCHA_TSDP_BC1, 0x80 | bc);
cs->writeisac(cs, IPACX_BCHA_CR, 0x88);
}
else
{
cs->writeisac(cs, IPACX_BCHB_TSDP_BC1, 0x80 | bc);
cs->writeisac(cs, IPACX_BCHB_CR, 0x88);
}
switch (mode) {
case (L1_MODE_NULL):
cs->BC_Write_Reg(cs, hscx, IPACX_MODEB, 0xC0); // rec off
cs->BC_Write_Reg(cs, hscx, IPACX_EXMB, 0x30); // std adj.
cs->BC_Write_Reg(cs, hscx, IPACX_MASKB, 0xFF); // ints off
cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x41); // validate adjustments
break;
case (L1_MODE_TRANS):
cs->BC_Write_Reg(cs, hscx, IPACX_MODEB, 0x88); // ext transp mode
cs->BC_Write_Reg(cs, hscx, IPACX_EXMB, 0x00); // xxx00000
cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x41); // validate adjustments
cs->BC_Write_Reg(cs, hscx, IPACX_MASKB, _MASKB_IMASK);
break;
case (L1_MODE_HDLC):
cs->BC_Write_Reg(cs, hscx, IPACX_MODEB, 0xC8); // transp mode 0
cs->BC_Write_Reg(cs, hscx, IPACX_EXMB, 0x01); // idle=hdlc flags crc enabled
cs->BC_Write_Reg(cs, hscx, IPACX_CMDRB, 0x41); // validate adjustments
cs->BC_Write_Reg(cs, hscx, IPACX_MASKB, _MASKB_IMASK);
break;
}
}
//----------------------------------------------------------
//----------------------------------------------------------
static void
bch_close_state(struct BCState *bcs)
{
bch_mode(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;
}
skb_queue_purge(&bcs->rqueue);
skb_queue_purge(&bcs->squeue);
if (bcs->tx_skb) {
dev_kfree_skb_any(bcs->tx_skb);
bcs->tx_skb = NULL;
clear_bit(BC_FLG_BUSY, &bcs->Flag);
}
}
}
//----------------------------------------------------------
//----------------------------------------------------------
static int
bch_open_state(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 open_bchstate(): No memory for hscx.rcvbuf\n");
clear_bit(BC_FLG_INIT, &bcs->Flag);
return (1);
}
if (!(bcs->blog = kmalloc(MAX_BLOG_SPACE, GFP_ATOMIC))) {
printk(KERN_WARNING
"HiSax open_bchstate: No memory for bcs->blog\n");
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;
clear_bit(BC_FLG_BUSY, &bcs->Flag);
bcs->event = 0;
bcs->hw.hscx.rcvidx = 0;
bcs->tx_cnt = 0;
return (0);
}
//----------------------------------------------------------
//----------------------------------------------------------
static int
bch_setstack(struct PStack *st, struct BCState *bcs)
{
bcs->channel = st->l1.bc;
if (bch_open_state(st->l1.hardware, bcs)) return (-1);
st->l1.bcs = bcs;
st->l1.l2l1 = bch_l2l1;
setstack_manager(st);
bcs->st = st;
setstack_l1_B(st);
return (0);
}
//----------------------------------------------------------
//----------------------------------------------------------
static void __devinit
bch_init(struct IsdnCardState *cs, int hscx)
{
cs->bcs[hscx].BC_SetStack = bch_setstack;
cs->bcs[hscx].BC_Close = bch_close_state;
cs->bcs[hscx].hw.hscx.hscx = hscx;
cs->bcs[hscx].cs = cs;
bch_mode(cs->bcs + hscx, 0, hscx);
}
//==========================================================
// Shared functions
//==========================================================
//----------------------------------------------------------
// Main interrupt handler
//----------------------------------------------------------
void
interrupt_ipacx(struct IsdnCardState *cs)
{
u_char ista;
while ((ista = cs->readisac(cs, IPACX_ISTA))) {
//#################################################
// printk(KERN_WARNING "interrupt_ipacx(ista=%02x)\n", ista);
//#################################################
if (ista &0x80) bch_int(cs, 0); // B channel interrupts
if (ista &0x40) bch_int(cs, 1);
if (ista &0x01) dch_int(cs); // D channel
if (ista &0x10) cic_int(cs); // Layer 1 state
}
}
//----------------------------------------------------------
// Clears chip interrupt status
//----------------------------------------------------------
static void __init
clear_pending_ints(struct IsdnCardState *cs)
{
int ista;
// all interrupts off
cs->writeisac(cs, IPACX_MASK, 0xff);
cs->writeisac(cs, IPACX_MASKD, 0xff);
cs->BC_Write_Reg(cs, 0, IPACX_MASKB, 0xff);
cs->BC_Write_Reg(cs, 1, IPACX_MASKB, 0xff);
ista = cs->readisac(cs, IPACX_ISTA);
if (ista &0x80) cs->BC_Read_Reg(cs, 0, IPACX_ISTAB);
if (ista &0x40) cs->BC_Read_Reg(cs, 1, IPACX_ISTAB);
if (ista &0x10) cs->readisac(cs, IPACX_CIR0);
if (ista &0x01) cs->readisac(cs, IPACX_ISTAD);
}
//----------------------------------------------------------
// Does chip configuration work
// Work to do depends on bit mask in part
//----------------------------------------------------------
void __init
init_ipacx(struct IsdnCardState *cs, int part)
{
if (part &1) { // initialise chip
//##################################################
// printk(KERN_INFO "init_ipacx(%x)\n", part);
//##################################################
clear_pending_ints(cs);
bch_init(cs, 0);
bch_init(cs, 1);
dch_init(cs);
}
if (part &2) { // reenable all interrupts and start chip
cs->BC_Write_Reg(cs, 0, IPACX_MASKB, _MASKB_IMASK);
cs->BC_Write_Reg(cs, 1, IPACX_MASKB, _MASKB_IMASK);
cs->writeisac(cs, IPACX_MASKD, _MASKD_IMASK);
cs->writeisac(cs, IPACX_MASK, _MASK_IMASK); // global mask register
// reset HDLC Transmitters/receivers
cs->writeisac(cs, IPACX_CMDRD, 0x41);
cs->BC_Write_Reg(cs, 0, IPACX_CMDRB, 0x41);
cs->BC_Write_Reg(cs, 1, IPACX_CMDRB, 0x41);
ph_command(cs, IPACX_CMD_RES);
}
}
//----------------- end of file -----------------------
/*
*
* IPACX specific defines
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*/
/* All Registers original Siemens Spec */
#ifndef INCLUDE_IPACX_H
#define INCLUDE_IPACX_H
/* D-channel registers */
#define IPACX_RFIFOD 0x00 /* RD */
#define IPACX_XFIFOD 0x00 /* WR */
#define IPACX_ISTAD 0x20 /* RD */
#define IPACX_MASKD 0x20 /* WR */
#define IPACX_STARD 0x21 /* RD */
#define IPACX_CMDRD 0x21 /* WR */
#define IPACX_MODED 0x22 /* RD/WR */
#define IPACX_EXMD1 0x23 /* RD/WR */
#define IPACX_TIMR1 0x24 /* RD/WR */
#define IPACX_SAP1 0x25 /* WR */
#define IPACX_SAP2 0x26 /* WR */
#define IPACX_RBCLD 0x26 /* RD */
#define IPACX_RBCHD 0x27 /* RD */
#define IPACX_TEI1 0x27 /* WR */
#define IPACX_TEI2 0x28 /* WR */
#define IPACX_RSTAD 0x28 /* RD */
#define IPACX_TMD 0x29 /* RD/WR */
#define IPACX_CIR0 0x2E /* RD */
#define IPACX_CIX0 0x2E /* WR */
#define IPACX_CIR1 0x2F /* RD */
#define IPACX_CIX1 0x2F /* WR */
/* Transceiver registers */
#define IPACX_TR_CONF0 0x30 /* RD/WR */
#define IPACX_TR_CONF1 0x31 /* RD/WR */
#define IPACX_TR_CONF2 0x32 /* RD/WR */
#define IPACX_TR_STA 0x33 /* RD */
#define IPACX_TR_CMD 0x34 /* RD/WR */
#define IPACX_SQRR1 0x35 /* RD */
#define IPACX_SQXR1 0x35 /* WR */
#define IPACX_SQRR2 0x36 /* RD */
#define IPACX_SQXR2 0x36 /* WR */
#define IPACX_SQRR3 0x37 /* RD */
#define IPACX_SQXR3 0x37 /* WR */
#define IPACX_ISTATR 0x38 /* RD */
#define IPACX_MASKTR 0x39 /* RD/WR */
#define IPACX_TR_MODE 0x3A /* RD/WR */
#define IPACX_ACFG1 0x3C /* RD/WR */
#define IPACX_ACFG2 0x3D /* RD/WR */
#define IPACX_AOE 0x3E /* RD/WR */
#define IPACX_ARX 0x3F /* RD */
#define IPACX_ATX 0x3F /* WR */
/* IOM: Timeslot, DPS, CDA */
#define IPACX_CDA10 0x40 /* RD/WR */
#define IPACX_CDA11 0x41 /* RD/WR */
#define IPACX_CDA20 0x42 /* RD/WR */
#define IPACX_CDA21 0x43 /* RD/WR */
#define IPACX_CDA_TSDP10 0x44 /* RD/WR */
#define IPACX_CDA_TSDP11 0x45 /* RD/WR */
#define IPACX_CDA_TSDP20 0x46 /* RD/WR */
#define IPACX_CDA_TSDP21 0x47 /* RD/WR */
#define IPACX_BCHA_TSDP_BC1 0x48 /* RD/WR */
#define IPACX_BCHA_TSDP_BC2 0x49 /* RD/WR */
#define IPACX_BCHB_TSDP_BC1 0x4A /* RD/WR */
#define IPACX_BCHB_TSDP_BC2 0x4B /* RD/WR */
#define IPACX_TR_TSDP_BC1 0x4C /* RD/WR */
#define IPACX_TR_TSDP_BC2 0x4D /* RD/WR */
#define IPACX_CDA1_CR 0x4E /* RD/WR */
#define IPACX_CDA2_CR 0x4F /* RD/WR */
/* IOM: Contol, Sync transfer, Monitor */
#define IPACX_TR_CR 0x50 /* RD/WR */
#define IPACX_TRC_CR 0x50 /* RD/WR */
#define IPACX_BCHA_CR 0x51 /* RD/WR */
#define IPACX_BCHB_CR 0x52 /* RD/WR */
#define IPACX_DCI_CR 0x53 /* RD/WR */
#define IPACX_DCIC_CR 0x53 /* RD/WR */
#define IPACX_MON_CR 0x54 /* RD/WR */
#define IPACX_SDS1_CR 0x55 /* RD/WR */
#define IPACX_SDS2_CR 0x56 /* RD/WR */
#define IPACX_IOM_CR 0x57 /* RD/WR */
#define IPACX_STI 0x58 /* RD */
#define IPACX_ASTI 0x58 /* WR */
#define IPACX_MSTI 0x59 /* RD/WR */
#define IPACX_SDS_CONF 0x5A /* RD/WR */
#define IPACX_MCDA 0x5B /* RD */
#define IPACX_MOR 0x5C /* RD */
#define IPACX_MOX 0x5C /* WR */
#define IPACX_MOSR 0x5D /* RD */
#define IPACX_MOCR 0x5E /* RD/WR */
#define IPACX_MSTA 0x5F /* RD */
#define IPACX_MCONF 0x5F /* WR */
/* Interrupt and general registers */
#define IPACX_ISTA 0x60 /* RD */
#define IPACX_MASK 0x60 /* WR */
#define IPACX_AUXI 0x61 /* RD */
#define IPACX_AUXM 0x61 /* WR */
#define IPACX_MODE1 0x62 /* RD/WR */
#define IPACX_MODE2 0x63 /* RD/WR */
#define IPACX_ID 0x64 /* RD */
#define IPACX_SRES 0x64 /* WR */
#define IPACX_TIMR2 0x65 /* RD/WR */
/* B-channel registers */
#define IPACX_OFF_B1 0x70
#define IPACX_OFF_B2 0x80
#define IPACX_ISTAB 0x00 /* RD */
#define IPACX_MASKB 0x00 /* WR */
#define IPACX_STARB 0x01 /* RD */
#define IPACX_CMDRB 0x01 /* WR */
#define IPACX_MODEB 0x02 /* RD/WR */
#define IPACX_EXMB 0x03 /* RD/WR */
#define IPACX_RAH1 0x05 /* WR */
#define IPACX_RAH2 0x06 /* WR */
#define IPACX_RBCLB 0x06 /* RD */
#define IPACX_RBCHB 0x07 /* RD */
#define IPACX_RAL1 0x07 /* WR */
#define IPACX_RAL2 0x08 /* WR */
#define IPACX_RSTAB 0x08 /* RD */
#define IPACX_TMB 0x09 /* RD/WR */
#define IPACX_RFIFOB 0x0A /*- RD */
#define IPACX_XFIFOB 0x0A /*- WR */
/* Layer 1 Commands */
#define IPACX_CMD_TIM 0x0
#define IPACX_CMD_RES 0x1
#define IPACX_CMD_SSP 0x2
#define IPACX_CMD_SCP 0x3
#define IPACX_CMD_AR8 0x8
#define IPACX_CMD_AR10 0x9
#define IPACX_CMD_ARL 0xa
#define IPACX_CMD_DI 0xf
/* Layer 1 Indications */
#define IPACX_IND_DR 0x0
#define IPACX_IND_RES 0x1
#define IPACX_IND_TMA 0x2
#define IPACX_IND_SLD 0x3
#define IPACX_IND_RSY 0x4
#define IPACX_IND_DR6 0x5
#define IPACX_IND_PU 0x7
#define IPACX_IND_AR 0x8
#define IPACX_IND_ARL 0xa
#define IPACX_IND_CVR 0xb
#define IPACX_IND_AI8 0xc
#define IPACX_IND_AI10 0xd
#define IPACX_IND_AIL 0xe
#define IPACX_IND_DC 0xf
extern void init_ipacx(struct IsdnCardState *cs, int part);
extern void interrupt_ipacx(struct IsdnCardState *cs);
#endif
...@@ -1054,6 +1054,7 @@ ...@@ -1054,6 +1054,7 @@
#define PCI_DEVICE_ID_EICON_DIVA20PRO_U 0xe003 #define PCI_DEVICE_ID_EICON_DIVA20PRO_U 0xe003
#define PCI_DEVICE_ID_EICON_DIVA20_U 0xe004 #define PCI_DEVICE_ID_EICON_DIVA20_U 0xe004
#define PCI_DEVICE_ID_EICON_DIVA201 0xe005 #define PCI_DEVICE_ID_EICON_DIVA201 0xe005
#define PCI_DEVICE_ID_EICON_DIVA202 0xe00b
#define PCI_DEVICE_ID_EICON_MAESTRA 0xe010 #define PCI_DEVICE_ID_EICON_MAESTRA 0xe010
#define PCI_DEVICE_ID_EICON_MAESTRAQ 0xe012 #define PCI_DEVICE_ID_EICON_MAESTRAQ 0xe012
#define PCI_DEVICE_ID_EICON_MAESTRAQ_U 0xe013 #define PCI_DEVICE_ID_EICON_MAESTRAQ_U 0xe013
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment