diff options
author | markster <markster@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2005-05-02 20:15:07 +0000 |
---|---|---|
committer | markster <markster@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2005-05-02 20:15:07 +0000 |
commit | fa78643fc4a67112067ee2b090660c84886e9a04 (patch) | |
tree | 67d56dd508097931ed41b9a4092f3980a6f790ae /wct4xxp.c | |
parent | 86952759dcdb07b5cada67f976d4d8eb14b55ebf (diff) |
Add support for TE410P/TE405P 2nd gen features (TSI, ECHOCAN, NATIVELAYOUT, IRQ, DTMFDETECT)
git-svn-id: http://svn.digium.com/svn/zaptel/trunk@634 5390a7c7-147a-4af0-8ec9-7488f05a26cb
Diffstat (limited to 'wct4xxp.c')
-rwxr-xr-x | wct4xxp.c | 817 |
1 files changed, 769 insertions, 48 deletions
@@ -1,12 +1,12 @@ /* * TE410P Quad-T1/E1 PCI Driver version 0.1, 12/16/02 * - * Written by Mark Spencer <markster@linux-support.net> + * Written by Mark Spencer <markster@digium.com> * Based on previous works, designs, and archetectures conceived and * written by Jim Dixon <jim@lambdatel.com>. * * Copyright (C) 2001 Jim Dixon / Zapata Telephony. - * Copyright (C) 2001, Linux Support Services, Inc. + * Copyright (C) 2001-2005, Digium, Inc. * * All rights reserved. * @@ -57,6 +57,16 @@ alarm status */ #define FANCY_ALARM +/* Define to support Digium Voice Processing Module expansion card */ +#define VPM_SUPPORT + +#define DEBUG_MAIN (1 << 0) +#define DEBUG_DTMF (1 << 1) +#define DEBUG_REGS (1 << 2) +#define DEBUG_TSI (1 << 3) +#define DEBUG_ECHOCAN (1 << 4) +#define DEBUG_RBS (1 << 5) + static int debug; static int timingcable; static int highestorder; @@ -64,7 +74,12 @@ static int t1e1override = -1; static int j1mode = 0; static int loopback = 0; static int alarmdebounce = 0; +#ifdef VPM_SUPPORT +static int vpmsupport = 1; +#endif static int noburst = 0; +static int debugslips = 1; +static int polling = 0; #ifdef FANCY_ALARM static int altab[] = { @@ -83,14 +98,25 @@ static int altab[] = { #define TYPE_E1 2 /* is an E1 card */ #define TYPE_J1 3 /* is a running J1 */ +#define FLAG_2NDGEN (1 << 3) + #define CANARY 0xc0de +struct devtype { + char *desc; + unsigned int flags; +}; + +static struct devtype wct4xxp = { "Wildcard TE410P/TE405P (1st Gen)", 0 }; +static struct devtype wct4xxp2 = { "Wildcard TE410P/TE405P (2nd Gen)", FLAG_2NDGEN }; + + static int inirq = 0; struct t4 { /* This structure exists one per card */ struct pci_dev *dev; /* Pointer to PCI device */ - int intcount; + unsigned int intcount; int num; /* Which card we are */ int t1e1; /* T1/E1 select pins */ int globalconfig; /* Whether global setup has been done */ @@ -132,6 +158,14 @@ struct t4 { volatile unsigned int *writechunk; /* Double-word aligned write memory */ volatile unsigned int *readchunk; /* Double-word aligned read memory */ unsigned short canary; +#ifdef VPM_SUPPORT + int vpm; /* VPM Present */ + unsigned int dtmfactive[4]; + unsigned int dtmfmask[4]; + unsigned int dtmfmutemask[4]; + short dtmfenergy[4][31]; + short dtmfdigit[4][31]; +#endif #ifdef ENABLE_TASKLETS int taskletrun; int taskletsched; @@ -147,7 +181,12 @@ struct t4 { int checktiming; /* Set >0 to cause the timing source to be checked */ }; +#define T4_VPM_PRESENT (1 << 28) + +#ifdef VPM_SUPPORT +static void t4_vpm_init(struct t4 *wc); +#endif static void __set_clear(struct t4 *wc, int span); static int t4_startup(struct zt_span *span); static int t4_shutdown(struct zt_span *span); @@ -155,7 +194,11 @@ static int t4_rbsbits(struct zt_chan *chan, int bits); static int t4_maint(struct zt_span *span, int cmd); static int t4_reset_dma(struct t4 *wc); static int t4_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data); +static void t4_tsi_assign(struct t4 *wc, int fromspan, int fromchan, int tospan, int tochan); +static void t4_tsi_unassign(struct t4 *wc, int tospan, int tochan); static void __t4_set_timing_source(struct t4 *wc, int unit); +static void __t4_check_alarms(struct t4 *wc, int span); +static void __t4_check_sigbits(struct t4 *wc, int span); #define WC_RDADDR 0 #define WC_WRADDR 1 @@ -186,6 +229,10 @@ static void t4_tasklet(unsigned long data); static struct t4 *cards[MAX_T4_CARDS]; + +#define MAX_TDM_CHAN 32 +#define MAX_DTMF_DET 16 + static inline void __t4_pci_out(struct t4 *wc, const unsigned int addr, const unsigned int value) { unsigned int tmp; @@ -262,7 +309,7 @@ static inline unsigned int t4_framer_in(struct t4 *wc, int unit, const unsigned static inline void __t4_framer_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value) { unit &= 0x3; - if (debug) + if (debug & DEBUG_REGS) printk("Writing %02x to address %02x of unit %d\n", value, addr, unit); __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff)); __t4_pci_out(wc, WC_LDATA, value); @@ -271,7 +318,7 @@ static inline void __t4_framer_out(struct t4 *wc, int unit, const unsigned int a __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff) | (1 << 10)); __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff)); __t4_pci_out(wc, WC_LADDR, 0); - if (debug) printk("Write complete\n"); + if (debug & DEBUG_REGS) printk("Write complete\n"); #if 0 { unsigned int tmp; tmp = t4_framer_in(wc, unit, addr); @@ -289,6 +336,133 @@ static inline void t4_framer_out(struct t4 *wc, int unit, const unsigned int add spin_unlock_irqrestore(&wc->reglock, flags); } +#ifdef VPM_SUPPORT + +static inline void wait_a_little(void) +{ + unsigned long newjiffies=jiffies+2; + while(jiffies < newjiffies); +} + +static inline unsigned int __t4_vpm_in(struct t4 *wc, int unit, const unsigned int addr) +{ + unsigned int ret; + unit &= 0x7; + __t4_pci_out(wc, WC_LADDR, (addr & 0x1ff) | ( unit << 12)); + __t4_pci_out(wc, WC_LADDR, (addr & 0x1ff) | ( unit << 12) | (1 << 11) | WC_LREAD); + ret = __t4_pci_in(wc, WC_LDATA); + __t4_pci_out(wc, WC_LADDR, 0); + return ret & 0xff; +} + +static inline unsigned int t4_vpm_in(struct t4 *wc, int unit, const unsigned int addr) +{ + unsigned long flags; + unsigned int ret; + spin_lock_irqsave(&wc->reglock, flags); + ret = __t4_vpm_in(wc, unit, addr); + spin_unlock_irqrestore(&wc->reglock, flags); + return ret; +} + +static inline void __t4_vpm_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value) +{ + unit &= 0x7; + if (debug & DEBUG_REGS) + printk("Writing %02x to address %02x of ec unit %d\n", value, addr, unit); + __t4_pci_out(wc, WC_LADDR, (addr & 0xff)); + __t4_pci_out(wc, WC_LDATA, value); + __t4_pci_out(wc, WC_LADDR, (unit << 12) | (addr & 0x1ff) | (1 << 11)); + __t4_pci_out(wc, WC_LADDR, (unit << 12) | (addr & 0x1ff) | (1 << 11) | WC_LWRITE); + __t4_pci_out(wc, WC_LADDR, (unit << 12) | (addr & 0x1ff) | (1 << 11)); + __t4_pci_out(wc, WC_LADDR, (unit << 12) | (addr & 0x1ff)); + __t4_pci_out(wc, WC_LADDR, 0); + if (debug & DEBUG_REGS) printk("Write complete\n"); + + +#if 0 + { unsigned int tmp; + tmp = t4_vpm_in(wc, unit, addr); + if (tmp != value) { + printk("Expected %d from unit %d echo register %d but got %d instead\n", value, unit, addr, tmp); + } } +#endif +} + +static inline void t4_vpm_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value) +{ + unsigned long flags; + spin_lock_irqsave(&wc->reglock, flags); + __t4_vpm_out(wc, unit, addr, value); + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static void __t4_check_vpm(struct t4 *wc, unsigned int newio) +{ + unsigned int digit, regval = 0; + int x, i; + short energy; + static unsigned int lastio = 0; + + if (debug && (newio != lastio)) + printk("Last was %08x, new is %08x\n", lastio, newio); + + lastio = newio; + + for(x = 0; x < 8; x++) { + if (newio & (1 << (7 - x))) + continue; + /* Start of DTMF detection process */ + regval = __t4_vpm_in(wc, x, 0xb8); + __t4_vpm_out(wc, x, 0xb8, regval); /* Write 1 to clear */ + regval = regval << 8; + regval |= __t4_vpm_in(wc, x, 0xb9); + __t4_vpm_out(wc, x, 0xb9, regval & 0xff); + + for(i = 0; (i < MAX_DTMF_DET) && regval; i++) { + if(regval & 0x0001) { + int channel = (i << 1) + (x >> 2); + int base = channel - 1; + if (!wc->t1e1) + base -= 4; + digit = __t4_vpm_in(wc, x, 0xa8 + i); + if (digit < 10) + digit += '0'; + else if (digit < 0xe) + digit += 'A' - 0xe; + else if (digit == 0xe) + digit = '*'; + else if (digit == 0xf) + digit = '#'; + energy = __t4_vpm_in(wc, x, 0x58 + channel); + energy = ZT_XLAW(energy, wc->spans[x%4].chans); + wc->dtmfactive[x % 4] |= (1 << base); + wc->dtmfenergy[x % 4][base] = energy; + wc->dtmfdigit[x % 4][base] = digit; + if (wc->dtmfmask[x % 4] & (1 << base)) + zt_qevent_lock(&wc->spans[x%4].chans[base], (ZT_EVENT_DTMFDOWN | digit)); + if (wc->dtmfmutemask[x % 4] & (1 << base)) { + /* Mute active receive buffer*/ + unsigned long flags; + struct zt_chan *chan = &wc->spans[x % 4].chans[base]; + int y; + spin_lock_irqsave(&chan->lock, flags); + for (y=0;y<chan->numbufs;y++) { + if (chan->readidx[y]) + memset(chan->readbuf[chan->inreadbuf], ZT_XLAW(0, chan), chan->readidx[y]); + } + spin_unlock_irqrestore(&chan->lock, flags); + } + if (debug) + printk("Digit Seen: %d, Span: %d, channel: %d, energy: %02x, 'channel %d' chip %d\n", digit, x % 4, base + 1, energy, channel, x); + + } + regval = regval >> 1; + } + } +} +#endif + static void __set_clear(struct t4 *wc, int span) { @@ -318,19 +492,90 @@ static void set_clear(struct t4 *wc, int span) } #endif +static int t4_dacs(struct zt_chan *dst, struct zt_chan *src) +{ + struct t4 *wc; + wc = dst->pvt; + if (src && (src->pvt != dst->pvt)) { + if (wc->spanflags[0] & FLAG_2NDGEN) + t4_tsi_unassign(wc, dst->span->offset, dst->chanpos); + wc = src->pvt; + if (wc->spanflags[0] & FLAG_2NDGEN) + t4_tsi_unassign(wc, src->span->offset, src->chanpos); + if (debug) + printk("Unassigning %d/%d by default and...\n", src->span->offset, src->chanpos); + if (debug) + printk("Unassigning %d/%d by default\n", dst->span->offset, dst->chanpos); + return -1; + } + if (src) { + t4_tsi_assign(wc, src->span->offset, src->chanpos, dst->span->offset, dst->chanpos); + if (debug) + printk("Assigning channel %d/%d -> %d/%d!\n", src->span->offset, src->chanpos, dst->span->offset, dst->chanpos); + } else { + t4_tsi_unassign(wc, dst->span->offset, dst->chanpos); + if (debug) + printk("Unassigning channel %d/%d!\n", dst->span->offset, dst->chanpos); + } + return 0; +} + +static int t4_echocan(struct zt_chan *chan, int eclen) +{ + struct t4 *wc = chan->pvt; + int channel; + int unit; + if (!wc->vpm) + return -ENODEV; + unit = chan->span->offset; + if (wc->t1e1) + channel = chan->chanpos; + else + channel = chan->chanpos + 4; + if ((channel & 1)) + unit += 4; + if(debug & DEBUG_ECHOCAN) + printk("echocan: Channel is %d, Span is %d, unit is %d, unit offset is %d length %d\n", + chan->chanpos, chan->span->offset, unit, channel, eclen); + if (eclen) + t4_vpm_out(wc,unit,channel,0x3e); + else + t4_vpm_out(wc,unit,channel,0x01); + return 0; +} + static int t4_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data) { struct t4_regs regs; - int x; + int j,x; + struct t4 * wc; switch(cmd) { case WCT4_GET_REGS: + wc = chan->pvt; for (x=0;x<NUM_PCI;x++) - regs.pci[x] = t4_pci_in(chan->pvt, x); + regs.pci[x] = t4_pci_in(wc, x); for (x=0;x<NUM_REGS;x++) - regs.regs[x] = t4_framer_in(chan->pvt, chan->span->offset, x); + regs.regs[x] = t4_framer_in(wc, chan->span->offset, x); if (copy_to_user((struct t4_regs *)data, ®s, sizeof(regs))) return -EFAULT; break; +#ifdef VPM_SUPPORT + case ZT_TONEDETECT: + if (get_user(j, (int *)data)) + return -EFAULT; + wc = chan->pvt; + if (!wc->vpm) + return -ENOSYS; + if (j & ZT_TONEDETECT_ON) + wc->dtmfmask[chan->span->offset] |= (1 << (chan->chanpos - 1)); + else + wc->dtmfmask[chan->span->offset] &= ~(1 << (chan->chanpos - 1)); + if (j & ZT_TONEDETECT_MUTE) + wc->dtmfmutemask[chan->span->offset] |= (1 << (chan->chanpos - 1)); + else + wc->dtmfmutemask[chan->span->offset] &= ~(1 << (chan->chanpos - 1)); + return 0; +#endif default: return -ENOTTY; } @@ -400,7 +645,7 @@ static int t4_rbsbits(struct zt_chan *chan, int bits) struct t4 *wc = chan->pvt; unsigned long flags; - if(debug) printk("Setting bits to %d on channel %s\n", bits, chan->name); + if(debug & DEBUG_RBS) printk("Setting bits to %d on channel %s\n", bits, chan->name); spin_lock_irqsave(&wc->reglock, flags); k = chan->span->offset; if (wc->spantype[k] == TYPE_E1) { /* do it E1 way */ @@ -441,7 +686,7 @@ static int t4_rbsbits(struct zt_chan *chan, int bits) __t4_framer_out(wc,k,0x70 + b,c); } spin_unlock_irqrestore(&wc->reglock, flags); - if (debug) + if (debug & DEBUG_RBS) printk("Finished setting RBS bits\n"); return 0; } @@ -479,7 +724,7 @@ static int t4_shutdown(struct zt_span *span) __t4_set_timing_source(wc,4); } else wc->checktiming = 1; spin_unlock_irqrestore(&wc->reglock, flags); - if (debug) + if (debug & DEBUG_MAIN) printk("Span %d (%s) shutdown\n", span->spanno, span->name); return 0; } @@ -489,7 +734,7 @@ static int t4_spanconfig(struct zt_span *span, struct zt_lineconfig *lc) int i; struct t4 *wc = span->pvt; - if (debug) + if (debug & DEBUG_MAIN) printk("TE410P: Configuring span %d\n", span->spanno); /* XXX We assume lineconfig is okay and shouldn't XXX */ span->lineconfig = lc->lineconfig; @@ -527,7 +772,7 @@ static int t4_chanconfig(struct zt_chan *chan, int sigtype) struct t4 *wc = chan->pvt; alreadyrunning = wc->spans[chan->span->offset].flags & ZT_FLAG_RUNNING; - if (debug) { + if (debug & DEBUG_MAIN) { if (alreadyrunning) printk("TE410P: Reconfigured channel %d (%s) sigtype %d\n", chan->channo, chan->name, sigtype); else @@ -566,6 +811,12 @@ static int t4_close(struct zt_chan *chan) static void init_spans(struct t4 *wc) { int x,y,c; + int gen2; + int offset = 1; + + gen2 = (wc->spanflags[0] & FLAG_2NDGEN); + if (!wc->t1e1) + offset += 4; for (x=0;x<4;x++) { sprintf(wc->spans[x].name, "TE4/%d/%d", wc->num, x + 1); @@ -589,6 +840,10 @@ static void init_spans(struct t4 *wc) wc->spans[x].flags = ZT_FLAG_RBS; wc->spans[x].linecompat = ZT_CONFIG_AMI | ZT_CONFIG_B8ZS | ZT_CONFIG_D4 | ZT_CONFIG_ESF; wc->spans[x].ioctl = t4_ioctl; + if (gen2) { + wc->spans[x].echocan = t4_echocan; + wc->spans[x].dacs = t4_dacs; + } wc->spans[x].pvt = wc; wc->spans[x].offset = x; init_waitqueue_head(&wc->spans[x].maintq); @@ -600,6 +855,10 @@ static void init_spans(struct t4 *wc) c = (x * wc->spans[x].channels) + y; mychans->pvt = wc; mychans->chanpos = y + 1; + if (gen2) { + mychans->writechunk = (void *)(wc->writechunk + (x * 32 + y + offset) * 2); + mychans->readchunk = (void *)(wc->readchunk + (x * 32 + y + offset) * 2); + } } } } @@ -624,11 +883,11 @@ static void t4_serial_setup(struct t4 *wc, int unit) } /* Configure interrupts */ - t4_framer_out(wc, unit, 0x46, 0x40); /* GCR: Interrupt on Activation/Deactivation of AIX, LOS */ + t4_framer_out(wc, unit, 0x46, 0x00); /* GCR: Interrupt on Activation/Deactivation of each */ /* Configure system interface */ t4_framer_out(wc, unit, 0x3e, 0xc2); /* SIC1: 8.192 Mhz clock/bus, double buffer receive / transmit, byte interleaved */ - t4_framer_out(wc, unit, 0x3f, unit << 1); /* SIC2: No FFS, no center receive eliastic buffer, phase */ + t4_framer_out(wc, unit, 0x3f, 0x20 | (unit << 1)); /* SIC2: No FFS, no center receive eliastic buffer, phase */ t4_framer_out(wc, unit, 0x40, 0x04); /* SIC3: Edges for capture */ t4_framer_out(wc, unit, 0x45, 0x00); /* CMR2: We provide sync and clock for tx and rx. */ if (!wc->t1e1) { /* T1 mode */ @@ -639,7 +898,7 @@ static void t4_serial_setup(struct t4 *wc, int unit) else t4_framer_out(wc, unit, 0x24, 0x03); /* RC0: Just shy of 1023 */ t4_framer_out(wc, unit, 0x25, 0x84); /* RC1: The rest of RC0 */ - } else { /* E1 mode */ + } else { /* E1 mode */ t4_framer_out(wc, unit, 0x22, 0x00); /* XC0: Normal operation of Sa-bits */ t4_framer_out(wc, unit, 0x23, 0x04); /* XC1: 0 offset */ t4_framer_out(wc, unit, 0x24, 0x04); /* RC0: Just shy of 1023 */ @@ -652,7 +911,7 @@ static void t4_serial_setup(struct t4 *wc, int unit) t4_framer_out(wc, unit, 0x82, 0x65); /* PC3: Some unused stuff */ t4_framer_out(wc, unit, 0x83, 0x35); /* PC4: Some more unused stuff */ t4_framer_out(wc, unit, 0x84, 0x01); /* PC5: XMFS active low, SCLKR is input, RCLK is output */ - if (debug) + if (debug & DEBUG_MAIN) printk("Successfully initialized serial bus for unit %d\n", unit); } @@ -666,10 +925,12 @@ static void __t4_set_timing_source(struct t4 *wc, int unit) timing |= (unit << 6); for (x=0;x<4;x++) /* set all 4 receive reference clocks to unit */ __t4_framer_out(wc, x, 0x44, timing); - __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl | (1 << 29)); + wc->dmactrl |= (1 << 29); + __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); } else { for (x=0;x<4;x++) /* set each receive reference clock to itself */ __t4_framer_out(wc, x, 0x44, timing | (x << 6)); + wc->dmactrl &= ~(1 << 29); __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); } wc->syncsrc = unit; @@ -680,7 +941,7 @@ static void __t4_set_timing_source(struct t4 *wc, int unit) for (x=0;x<4;x++) wc->spans[x].syncsrc = unit; } else { - if (debug) + if (debug & DEBUG_MAIN) printk("TE410P: Timing source already set to %d\n", unit); } #if 0 @@ -752,7 +1013,7 @@ static void __t4_configure_t1(struct t4 *wc, int unit, int lineconfig, int txlev __t4_framer_out(wc, unit, 0x3a, lim2); /* LIM2: 50% peak amplitude is a "1" */ __t4_framer_out(wc, unit, 0x38, 0x0a); /* PCD: LOS after 176 consecutive "zeros" */ __t4_framer_out(wc, unit, 0x39, 0x15); /* PCR: 22 "ones" clear LOS */ - + /* Generate pulse mask for T1 */ switch(mytxlevel) { case 3: @@ -777,6 +1038,23 @@ static void __t4_configure_t1(struct t4 *wc, int unit, int lineconfig, int txlev __t4_framer_out(wc, unit, 0x28, 0x01); /* XPM2 */ break; } + + __t4_framer_out(wc, unit, 0x14, 0xf7); /* IMR0: We care about CAS changes, etc */ + __t4_framer_out(wc, unit, 0x15, 0xff); /* IMR1: We care about nothing */ + __t4_framer_out(wc, unit, 0x16, 0x00); /* IMR2: We care about all the alarm stuff! */ + if (debugslips) { + __t4_framer_out(wc, unit, 0x17, 0xf4); /* IMR3: We care about AIS and friends */ + __t4_framer_out(wc, unit, 0x18, 0x3f); /* IMR4: We care about slips on transmit */ + } else { + __t4_framer_out(wc, unit, 0x17, 0xf7); /* IMR3: We care about AIS and friends */ + __t4_framer_out(wc, unit, 0x18, 0xff); /* IMR4: We don't care about slips on transmit */ + } + + if (!polling) { + __t4_check_alarms(wc, unit); + __t4_check_sigbits(wc, unit); + } + printk("TE410P: Span %d configured for %s/%s\n", unit + 1, framing, line); } @@ -846,6 +1124,21 @@ static void __t4_configure_e1(struct t4 *wc, int unit, int lineconfig) __t4_framer_out(wc, unit, 0x26, 0x54); /* XPM0 */ __t4_framer_out(wc, unit, 0x27, 0x02); /* XPM1 */ __t4_framer_out(wc, unit, 0x28, 0x00); /* XPM2 */ + + __t4_framer_out(wc, unit, 0x14, 0xf7); /* IMR0: We care about CRC errors, CAS changes, etc */ + __t4_framer_out(wc, unit, 0x15, 0x3f); /* IMR1: We care about loopup / loopdown */ + __t4_framer_out(wc, unit, 0x16, 0x00); /* IMR2: We care about all the alarm stuff! */ + if (debugslips) { + __t4_framer_out(wc, unit, 0x17, 0xc4); /* IMR3: We care about AIS and friends */ + __t4_framer_out(wc, unit, 0x18, 0x3f); /* IMR4: We care about slips on transmit */ + } else { + __t4_framer_out(wc, unit, 0x17, 0xc7); /* IMR3: We care about AIS and friends */ + __t4_framer_out(wc, unit, 0x18, 0xff); /* IMR4: We don't care about slips on transmit */ + } + if (!polling) { + __t4_check_alarms(wc, unit); + __t4_check_sigbits(wc, unit); + } printk("TE410P: Span %d configured for %s/%s%s\n", unit + 1, framing, line, crc4); } @@ -891,10 +1184,14 @@ static int t4_startup(struct zt_span *span) wc->spansstarted++; /* enable interrupts */ /* Start DMA, enabling DMA interrupts on read only */ - wc->dmactrl = 0xc0000003 | (1 << 29); + wc->dmactrl = 0xc0000000 | (1 << 29) | wc->vpm; if (noburst) wc->dmactrl |= (1 << 26); __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + if (!polling) { + __t4_check_alarms(wc, span->offset); + __t4_check_sigbits(wc, span->offset); + } } spin_unlock_irqrestore(&wc->reglock, flags); @@ -902,6 +1199,12 @@ static int t4_startup(struct zt_span *span) if (wc->syncs[1] == span->spanno) printk("SPAN %d: Secondary Sync Source\n",span->spanno); if (wc->syncs[2] == span->spanno) printk("SPAN %d: Tertiary Sync Source\n",span->spanno); if (wc->syncs[3] == span->spanno) printk("SPAN %d: Quaternary Sync Source\n",span->spanno); +#ifdef VPM_SUPPORT + if (!alreadyrunning && !wc->vpm) { + wait_a_little(); + t4_vpm_init(wc); + } +#endif return 0; } @@ -919,7 +1222,7 @@ static inline void e1_check(struct t4 *wc, int span, int val) /* Wait 1000 ms */ wc->e1recover = 1000 * 8; memset(wc->e1check, 0, sizeof(wc->e1check)); - if (debug) + if (debug & DEBUG_MAIN) printk("Detected loss of E1 alignment on span %d!\n", span); t4_reset_dma(wc); } @@ -950,7 +1253,7 @@ static void t4_receiveprep(struct t4 *wc, int irq) if (dbl) { for (x=0;x<4;x++) wc->spans[x].irqmisses++; - if (debug) + if (debug & DEBUG_MAIN) printk("TE410P: Double/missed interrupt detected\n"); } for (x=0;x<ZT_CHUNKSIZE;x++) { @@ -1004,6 +1307,42 @@ static void t4_receiveprep(struct t4 *wc, int irq) } } + +#if (ZT_CHUNKSIZE != 8) +#error Sorry, nextgen doesn't support chunksize != 8 +#endif + +static void t4_receiveprep_gen2(struct t4 *wc) +{ + int x; + int y; + unsigned int merged; + + for (x=0;x<4;x++) { + if (wc->spans[x].flags & ZT_FLAG_RUNNING) { + if ((merged = wc->dtmfactive[x] & wc->dtmfmutemask[x])) { + for (y=0;y<wc->spans[x].channels;y++) { + /* Mute any DTMFs which are supposed to be muted */ + if (merged & (1 << y)) + memset(wc->spans[x].chans[y].readchunk, ZT_XLAW(0, (wc->spans[x].chans + y)), ZT_CHUNKSIZE); + } + } + zt_ec_span(&wc->spans[x]); + zt_receive(&wc->spans[x]); + } + } +} + +static void t4_transmitprep_gen2(struct t4 *wc) +{ + int x; + for (x=0;x<4;x++) { + if (wc->spans[x].flags & ZT_FLAG_RUNNING) + zt_transmit(&wc->spans[x]); + } +} + + static void t4_transmitprep(struct t4 *wc, int irq) { volatile unsigned int *writechunk; @@ -1016,7 +1355,7 @@ static void t4_transmitprep(struct t4 *wc, int irq) /* First part */ writechunk = wc->writechunk + 1; } else { - writechunk = wc->writechunk + ZT_CHUNKSIZE * 32 + 1; + writechunk = wc->writechunk + ZT_CHUNKSIZE * 32 + 1; } for (y=0;y<4;y++) { if (wc->spans[y].flags & ZT_FLAG_RUNNING) @@ -1057,6 +1396,7 @@ static void t4_transmitprep(struct t4 *wc, int irq) static void __t4_check_sigbits(struct t4 *wc, int span) { int a,i,rxs; + if (!(wc->spans[span].flags & ZT_FLAG_RUNNING)) return; if (wc->spantype[span] == TYPE_E1) { @@ -1105,26 +1445,17 @@ static void __t4_check_sigbits(struct t4 *wc, int span) /* Get high channel in low bits */ rxs = (a & 0xf); if (!(wc->spans[span].chans[i+1].sig & ZT_SIG_CLEAR)) { - if (wc->spans[span].chans[i+1].rxsig != rxs) + /* XXX Not really reset on every trans! XXX */ + if (wc->spans[span].chans[i+1].rxsig != rxs) { zt_rbsbits(&wc->spans[span].chans[i+1], rxs); + } } rxs = (a >> 4) & 0xf; if (!(wc->spans[span].chans[i].sig & ZT_SIG_CLEAR)) { - if (wc->spans[span].chans[i].rxsig != rxs) + /* XXX Not really reset on every trans! XXX */ + if (wc->spans[span].chans[i].rxsig != rxs) { zt_rbsbits(&wc->spans[span].chans[i], rxs); - } - } - } -} - -static void __t4_do_counters(struct t4 *wc) -{ - int span; - for (span=0;span<4;span++) { - if (wc->alarmtimer[span]) { - if (!--wc->alarmtimer[span]) { - wc->spans[span].alarms &= ~(ZT_ALARM_RECOVER); - zt_alarm_notify(&wc->spans[span]); + } } } } @@ -1251,6 +1582,21 @@ static void __t4_check_alarms(struct t4 *wc, int span) zt_alarm_notify(&wc->spans[span]); } +static void __t4_do_counters(struct t4 *wc) +{ + int span; + for (span=0;span<4;span++) { + if (wc->alarmtimer[span]) { + if (!--wc->alarmtimer[span]) { + wc->spans[span].alarms &= ~(ZT_ALARM_RECOVER); + if (!polling) + __t4_check_alarms(wc, span); + zt_alarm_notify(&wc->spans[span]); + } + } + } +} + static inline void __handle_leds(struct t4 *wc) { int x; @@ -1405,6 +1751,199 @@ static void t4_interrupt(int irq, void *dev_id, struct pt_regs *regs) #endif } +static inline void __t4_framer_interrupt(struct t4 *wc, int span) +{ + /* Check interrupts for a given span */ + unsigned char gis, isr0=0, isr1=0, isr2=0, isr3=0, isr4; + gis = __t4_framer_in(wc, span, 0x6e); + + if (wc->spantype[span] == TYPE_E1) { + /* E1 checks */ + if (gis & 0x1) + isr0 = __t4_framer_in(wc, span, 0x68); + if (gis & 0x2) + isr1 = __t4_framer_in(wc, span, 0x69); + if (gis & 0x4) + isr2 = __t4_framer_in(wc, span, 0x6a); + if (gis & 0x8) + isr3 = __t4_framer_in(wc, span, 0x6b); + + + if (isr0) + __t4_check_sigbits(wc, span); + if ((isr3 & 0x38) || isr2 || isr1) + __t4_check_alarms(wc, span); + } else { + /* T1 checks */ + if (gis & 0x1) + isr0 = __t4_framer_in(wc, span, 0x68); + if (gis & 0x4) + isr2 = __t4_framer_in(wc, span, 0x6a); + if (gis & 0x8) + isr3 = __t4_framer_in(wc, span, 0x6b); + + if (isr0) + __t4_check_sigbits(wc, span); + if (isr2 || (isr3 & 0x08)) + __t4_check_alarms(wc, span); + } + if (debugslips && !wc->spans[span].alarms) { + if (isr3 & 0x02) + printk("TE410P: RECEIVE slip NEGATIVE on span %d\n", span + 1); + if (isr3 & 0x01) + printk("TE410P: RECEIVE slip POSITIVE on span %d\n", span + 1); + if (gis & 0x10) + isr4 = __t4_framer_in(wc, span, 0x6c); + else + isr4 = 0; + if (isr4 & 0x80) + printk("TE410P: TRANSMIT slip POSITIVE on span %d\n", span + 1); + if (isr4 & 0x40) + printk("TE410P: TRANSMIT slip NEGATIVE on span %d\n", span + 1); + } +} + +#ifdef LINUX26 +static irqreturn_t t4_interrupt_gen2(int irq, void *dev_id, struct pt_regs *regs) +#else +static void t4_interrupt_gen2(int irq, void *dev_id, struct pt_regs *regs) +#endif +{ + struct t4 *wc = dev_id; + unsigned long flags; + unsigned char cis; + int x; + + unsigned int status; +#if 0 + unsigned int status2; +#endif + +#if 1 + if (wc->intcount < 20) + printk("2G: Pre-interrupt\n"); +#endif + + inirq = 1; + /* Make sure it's really for us */ + status = t4_pci_in(wc, WC_INTR); + + /* Ignore if it's not for us */ + if (!(status & 0x2)) +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif + + if (!wc->spansstarted) { + printk("Not prepped yet!\n"); +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif + } + + wc->intcount++; +#if 1 + if (wc->intcount < 20) + printk("2G: Got interrupt, status = %08x, GIS = %04x\n", status, __t4_framer_in(wc, 0, 0x6f)); +#endif + + if (status & 0x2) { + t4_receiveprep_gen2(wc); + t4_transmitprep_gen2(wc); + } + + spin_lock_irqsave(&wc->reglock, flags); + + __t4_do_counters(wc); + + if (polling) { + x = wc->intcount & 15 /* 63 */; + switch(x) { + case 0: + case 1: + case 2: + case 3: + __t4_check_sigbits(wc, x); + break; + case 4: + case 5: + case 6: + case 7: + __t4_check_alarms(wc, x - 4); + break; + } + } else if (status & 0x1) { + cis = __t4_framer_in(wc, 0, 0x6f); + if (cis & 0x1) + __t4_framer_interrupt(wc, 0); + if (cis & 0x2) + __t4_framer_interrupt(wc, 1); + if (cis & 0x4) + __t4_framer_interrupt(wc, 2); + if (cis & 0x8) + __t4_framer_interrupt(wc, 3); + } +#ifdef VPM_SUPPORT + if (wc->vpm) { + if (!(wc->intcount % 16)) { + /* Check DTMF events */ + if (wc->vpm) { + int span = (wc->intcount >> 4) & 0x3; + int y; + short energy; + int offset = 1; + int chip; + int channel; + if (!wc->t1e1) + offset = 5; + if (wc->dtmfactive[span]) { + for (y = 0; y < wc->spans[span].channels; y++) { + if (wc->dtmfactive[span] & (1 << y)) { + channel = y + offset; + chip = span + ((channel & 0x1) << 2); + /* Have an active channel, check its energy! */ + energy = __t4_vpm_in(wc, chip, 0x58 + channel); + energy = ZT_XLAW(energy, wc->spans[span].chans); + if (energy < (wc->dtmfenergy[span][y])) { + if (debug & DEBUG_DTMF) + printk("Finished digit on span %d, channel %d (energy = %02x < %02x) 'channel' %d, chip %d!\n", span, y + 1, energy, wc->dtmfenergy[span][y], channel, chip); + if (debug & DEBUG_DTMF) + printk("Finished digit '%c' on channel %d of span %d\n", wc->dtmfdigit[span][y], y + 1, span); + if (wc->dtmfmask[span] & (1 << y)) + zt_qevent_lock(&wc->spans[span].chans[y], (ZT_EVENT_DTMFUP | wc->dtmfdigit[span][y])); + wc->dtmfenergy[span][y] = 0; + wc->dtmfdigit[span][y] = 0; + wc->dtmfactive[span] &= ~(1 << y); + } else if (energy > (wc->dtmfenergy[span][y])) { + if (debug & DEBUG_DTMF) + printk("Increasing digit energy on span %d, channel %d (energy = %02x > %02x)!\n", span, y + 1, energy, wc->dtmfenergy[span][y]); + wc->dtmfenergy[span][y] = energy; + } + } + } + } + } + } else if ((status & 0xff00) != 0xff00) + __t4_check_vpm(wc, (status & 0xff00) >> 8); + } +#endif + + __handle_leds(wc); + + if (wc->checktiming > 0) + __t4_set_timing_source_auto(wc); + spin_unlock_irqrestore(&wc->reglock, flags); + + t4_pci_out(wc, WC_INTR, 0); +#ifdef LINUX26 + return IRQ_RETVAL(1); +#endif +} + static int t4_reset_dma(struct t4 *wc) { /* Turn off DMA and such */ @@ -1419,13 +1958,172 @@ static int t4_reset_dma(struct t4 *wc) t4_pci_out(wc, WC_WRADDR, wc->writedma); t4_pci_out(wc, WC_COUNT, ((ZT_MAX_CHUNKSIZE * 2 * 32 - 1) << 18) | ((ZT_MAX_CHUNKSIZE * 2 * 32 - 1) << 2)); t4_pci_out(wc, WC_INTR, 0); - wc->dmactrl = 0xc0000003 | (1 << 29); + wc->dmactrl = 0xc0000000 | (1 << 29) | wc->vpm; if (noburst) wc->dmactrl |= (1 << 26); t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); return 0; } +#ifdef VPM_SUPPORT +static void t4_vpm_init(struct t4 *wc) +{ + unsigned char reg; + unsigned int mask; + unsigned int ver; + int i,x,y; + if (!vpmsupport) { + printk("VPM: Support Disabled\n"); + return; + } + + for (x=0;x<8;x++) { + ver = t4_vpm_in(wc, x, 0x1a0); /* revision */ + if (ver != 0x26) { + if (x) + printk("VPM: Inopperable\n"); + else + printk("VPM: Not Present\n"); + return; + } + + /* Setup GPIO's */ + for (y=0;y<4;y++) { + t4_vpm_out(wc, x, 0x1a8 + y, 0x00); /* GPIO out */ + t4_vpm_out(wc, x, 0x1ac + y, 0x00); /* GPIO dir */ + t4_vpm_out(wc, x, 0x1b0 + y, 0x00); /* GPIO sel */ + } + + /* Setup TDM path - sets fsync and tdm_clk as inputs */ + reg = t4_vpm_in(wc, x, 0x1a3); /* misc_con */ + t4_vpm_out(wc, x, 0x1a3, reg & ~2); + + /* Setup timeslots */ + t4_vpm_out(wc, x, 0x02f, 0x20 | ((x%4) << 3)); + if (x < 4) + mask = 0x55555555; + else + mask = 0xaaaaaaaa; + + /* Setup Echo length (128 taps) */ + t4_vpm_out(wc, x, 0x022, 0x00); + t4_vpm_out(wc, x, 0x023, 0x7f); + + /* Setup the tdm channel masks for all LV's*/ + for (i=0;i<4;i++) + t4_vpm_out(wc, x, 0x30+i, (mask >> (i << 3)) & 0xff); + + /* Setup convergence rate */ + reg = t4_vpm_in(wc,x,0x20); + reg &= 0xE0; + if (wc->spantype[x & 0x3] == TYPE_E1) { + if (x < 4) + printk("VPM: Span %d A-law mode\n", x & 0x3); + reg |= 0x01; + } else { + if (x < 4) + printk("VPM: Span %d U-law mode\n", x & 0x3); + reg &= ~0x01; + } + t4_vpm_out(wc,x,0x20,(reg | 0x20)); + + /* Initialize echo cans */ + for (i = 0 ; i < MAX_TDM_CHAN ; i++) { + if (mask & (0x00000001 << i)) + t4_vpm_out(wc,x,i,0x00); + } + + wait_a_little(); + + /* Put in bypass mode */ + for (i = 0 ; i < MAX_TDM_CHAN ; i++) { + if (mask & (0x00000001 << i)) { + t4_vpm_out(wc,x,i,0x01); + } + } + + /* Enable bypass */ + for (i = 0 ; i < MAX_TDM_CHAN ; i++) { + if (mask & (0x00000001 << i)) + t4_vpm_out(wc,x,0x78 + i,0x01); + } + + /* Enable DTMF detectors */ + for (i=0;i<MAX_DTMF_DET;i++) { + if(x < 4) + t4_vpm_out(wc, x, 0x98+i,(i*2)|0x40); + else + t4_vpm_out(wc, x, 0x98+i, ((i*2)+1)|0x40); + } + for (i=0xb8;i<0xbe;i++) + t4_vpm_out(wc, x, i, 0xff); + if(x < 4) { + for(i=0;i<4;i++) + t4_vpm_out(wc, x, 0xc0+i, 0x55); + } else { + for(i = 0; i < 4; i++) + t4_vpm_out(wc, x, 0xc0+i, 0xaa); + } + } + printk("VPM: Present and operational\n"); + wc->vpm = T4_VPM_PRESENT; +} + +#endif + +static void t4_tsi_reset(struct t4 *wc) +{ + int x; + for (x=0;x<128;x++) { + wc->dmactrl &= ~0x00007fff; + wc->dmactrl |= (0x00004000 | (x << 7)); + t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + } + wc->dmactrl &= ~0x00007fff; + t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); +} + +/* Note that channels here start from 1 */ +static void t4_tsi_assign(struct t4 *wc, int fromspan, int fromchan, int tospan, int tochan) +{ + unsigned long flags; + int fromts, tots; + + fromts = (fromspan << 5) |(fromchan); + tots = (tospan << 5) | (tochan); + + if (!wc->t1e1) { + fromts += 4; + tots += 4; + } + spin_lock_irqsave(&wc->reglock, flags); + wc->dmactrl &= ~0x00007fff; + wc->dmactrl |= (0x00004000 | (tots << 7) | (fromts)); + __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + wc->dmactrl &= ~0x00007fff; + __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static void t4_tsi_unassign(struct t4 *wc, int tospan, int tochan) +{ + unsigned long flags; + int tots; + + tots = (tospan << 5) | (tochan); + + if (!wc->t1e1) + tots += 4; + spin_lock_irqsave(&wc->reglock, flags); + wc->dmactrl &= ~0x00007fff; + wc->dmactrl |= (0x00004000 | (tots << 7)); + __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + if (debug & DEBUG_TSI) + printk("Sending '%08x\n", wc->dmactrl); + wc->dmactrl &= ~0x00007fff; + __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + spin_unlock_irqrestore(&wc->reglock, flags); +} static int t4_hardware_init(struct t4 *wc) { int x; @@ -1433,7 +2131,7 @@ static int t4_hardware_init(struct t4 *wc) unsigned int falcver; version = t4_pci_in(wc, WC_VERSION); - printk("TE410P version %08x, burst %s\n", version, noburst ? "OFF" : "ON"); + printk("TE410P version %08x, burst %s, slip debug: %s\n", version, noburst ? "OFF" : "ON", debugslips ? "ON" : "OFF"); /* Make sure DMA engine is not running and interrupts are acknowledged */ wc->dmactrl = 0x0; @@ -1445,8 +2143,12 @@ static int t4_hardware_init(struct t4 *wc) t4_pci_out(wc, WC_RDADDR, wc->readdma); t4_pci_out(wc, WC_WRADDR, wc->writedma); - /* Setup counters, interrupt flags */ - t4_pci_out(wc, WC_COUNT, ((ZT_MAX_CHUNKSIZE * 2 * 32 - 1) << 18) | ((ZT_MAX_CHUNKSIZE * 2 * 32 - 1) << 2)); + /* Setup counters, interrupt flags (ignored in Gen2) */ + if (wc->spanflags[0] & FLAG_2NDGEN) { + t4_tsi_reset(wc); + } else { + t4_pci_out(wc, WC_COUNT, ((ZT_MAX_CHUNKSIZE * 2 * 32 - 1) << 18) | ((ZT_MAX_CHUNKSIZE * 2 * 32 - 1) << 2)); + } /* Reset pending interrupts */ t4_pci_out(wc, WC_INTR, 0x00000000); @@ -1512,6 +2214,8 @@ static int __devinit t4_launch(struct t4 *wc) #ifdef ENABLE_TASKLETS tasklet_init(&wc->t4_tlet, t4_tasklet, (unsigned long)wc); #endif + t4_tsi_assign(wc, 0, 1, 0, 2); + t4_tsi_assign(wc, 0, 2, 0, 1); return 0; } @@ -1519,6 +2223,7 @@ static int __devinit t4_init_one(struct pci_dev *pdev, const struct pci_device_i { int res; struct t4 *wc; + struct devtype *dt; int x,f; #if 0 int y; @@ -1533,7 +2238,10 @@ static int __devinit t4_init_one(struct pci_dev *pdev, const struct pci_device_i if (wc) { memset(wc, 0x0, sizeof(struct t4)); spin_lock_init(&wc->reglock); - wc->variety = (char *)(ent->driver_data); + dt = (struct devtype *)(ent->driver_data); + for (x=0;x<4;x++) + wc->spanflags[x] |= dt->flags; + wc->variety = dt->desc; wc->memaddr = pci_resource_start(pdev, 0); wc->memlen = pci_resource_len(pdev, 0); @@ -1566,7 +2274,7 @@ static int __devinit t4_init_one(struct pci_dev *pdev, const struct pci_device_i wc->readdma = wc->writedma + ZT_CHUNKSIZE * 32 * 2 * 4; /* Initialize Write/Buffers to all blank data */ - memset((void *)wc->writechunk,0xff,ZT_MAX_CHUNKSIZE * 2 * 32 * 4); + memset((void *)wc->writechunk,0x00,ZT_MAX_CHUNKSIZE * 2 * 32 * 4); memset((void *)wc->readchunk,0xff,ZT_MAX_CHUNKSIZE * 2 * 32 * 4); #if 0 memset((void *)wc->readchunk,0xff,ZT_MAX_CHUNKSIZE * 2 * 32 * 4); @@ -1594,7 +2302,7 @@ static int __devinit t4_init_one(struct pci_dev *pdev, const struct pci_device_i cards[x] = wc; - if (request_irq(pdev->irq, t4_interrupt, SA_INTERRUPT | SA_SHIRQ, "t4xxp", wc)) { + if (request_irq(pdev->irq, (wc->spanflags[0] & FLAG_2NDGEN) ? t4_interrupt_gen2 :t4_interrupt, SA_INTERRUPT | SA_SHIRQ, "t4xxp", wc)) { printk("t4xxp: Unable to request IRQ %d\n", pdev->irq); kfree(wc); return -EIO; @@ -1657,7 +2365,11 @@ static int t4_hardware_stop(struct t4 *wc) schedule_timeout((25 * HZ) / 1000); /* Turn off counter, address, etc */ - t4_pci_out(wc, WC_COUNT, 0x000000); + if (wc->spanflags[0] & FLAG_2NDGEN) { + t4_tsi_reset(wc); + } else { + t4_pci_out(wc, WC_COUNT, 0x000000); + } t4_pci_out(wc, WC_RDADDR, 0x0000000); t4_pci_out(wc, WC_WRADDR, 0x0000000); t4_pci_out(wc, WC_GPIO, 0x0000000); @@ -1718,9 +2430,11 @@ static void __devexit t4_remove_one(struct pci_dev *pdev) } } + static struct pci_device_id t4_pci_tbl[] __devinitdata = { - { 0x10ee, 0x0314, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)"Wildcard TE410P-Xilinx" }, + { 0x10ee, 0x0314, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&wct4xxp }, + { 0x10ee, 0x0271, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&wct4xxp2 }, { 0, } }; @@ -1765,11 +2479,18 @@ module_param(timingcable, int, 0600); module_param(t1e1override, int, 0600); module_param(alarmdebounce, int, 0600); module_param(j1mode, int, 0600); +module_param(debugslips, int, 0600); +module_param(polling, int, 0600); #else MODULE_PARM(debug, "i"); MODULE_PARM(loopback, "i"); MODULE_PARM(noburst, "i"); +MODULE_PARM(debugslips, "i"); +MODULE_PARM(polling, "i"); MODULE_PARM(timingcable, "i"); +#ifdef VPM_SUPPORT +MODULE_PARM(vpmsupport,"i"); +#endif MODULE_PARM(t1e1override, "i"); MODULE_PARM(alarmdebounce, "i"); MODULE_PARM(j1mode, "i"); |