summaryrefslogtreecommitdiff
path: root/wct4xxp
diff options
context:
space:
mode:
authormattf <mattf@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2007-04-04 21:48:48 +0000
committermattf <mattf@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2007-04-04 21:48:48 +0000
commit720527551cd60f2ffe061678aa07819cc55031ce (patch)
treea4277c2366be579fbf1e7069dedbc45258836379 /wct4xxp
parent8a937ac5a4588ffa815a2b20e94b3a96e13651b5 (diff)
Performance related overhaul of the wct4xxp driver.
git-svn-id: http://svn.digium.com/svn/zaptel/trunk@2392 5390a7c7-147a-4af0-8ec9-7488f05a26cb
Diffstat (limited to 'wct4xxp')
-rw-r--r--wct4xxp/base.c243
-rw-r--r--wct4xxp/wct4xxp.h4
2 files changed, 94 insertions, 153 deletions
diff --git a/wct4xxp/base.c b/wct4xxp/base.c
index 996f1bd..533641c 100644
--- a/wct4xxp/base.c
+++ b/wct4xxp/base.c
@@ -48,18 +48,6 @@
#include "wct4xxp.h"
#include "vpm450m.h"
-/*
- * Tasklets provide better system interactive response at the cost of the
- * possibility of losing a frame of data at very infrequent intervals. If
- * you are more concerned with the performance of your machine, enable the
- * tasklets. If you are strict about absolutely no drops, then do not enable
- * tasklets.
- *
- * XXX THIS IS NOT CURRENTLY IMPLEMENTED FOR THIS MODULE. FOR NOW, DO NOT USE!
- */
-
-/* #define ENABLE_TASKLETS */
-
/* Work queues are a way to better distribute load on SMP systems */
#if defined(LINUX26) && (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20))
/*
@@ -245,8 +233,6 @@ static struct devtype wct205 = { "Wildcard TE205P ", FLAG_2NDGEN | FLAG_2PORT };
static struct devtype wct210 = { "Wildcard TE210P ", FLAG_2NDGEN | FLAG_2PORT };
-static int inirq = 0;
-
struct t4;
struct t4_span {
@@ -305,10 +291,6 @@ struct t4 {
int syncsrc; /* active sync source */
struct t4_span *tspans[4]; /* Individual spans */
int numspans; /* Number of spans on the card */
-#ifdef VPM_SUPPORT
- int vpm;
-#endif
-
int blinktimer;
#ifdef FANCY_ALARM
int alarmpos;
@@ -321,37 +303,39 @@ struct t4 {
unsigned int gpio;
unsigned int gpioctl;
int stopdma; /* Set to stop DMA */
- unsigned int dmactrl;
int e1recover; /* E1 recovery timer */
- dma_addr_t readdma;
- dma_addr_t writedma;
- unsigned long memaddr; /* Base address of card */
- unsigned long memlen;
- volatile unsigned int *membase; /* Base address of card */
- int spansstarted; /* number of spans started */
- /* spinlock_t lock; */ /* lock context */
spinlock_t reglock; /* lock register access */
+ int spansstarted; /* number of spans started */
volatile unsigned int *writechunk; /* Double-word aligned write memory */
volatile unsigned int *readchunk; /* Double-word aligned read memory */
unsigned short canary;
#ifdef ENABLE_WORKQUEUES
atomic_t worklist;
struct workqueue_struct *workq;
-#else
-#ifdef ENABLE_TASKLETS
- int taskletrun;
- int taskletsched;
- int taskletpending;
- int taskletexec;
- int txerrors;
- struct tasklet_struct t4_tlet;
-#endif
#endif
unsigned int passno; /* number of interrupt passes */
char *variety;
int last0; /* for detecting double-missed IRQ */
int checktiming; /* Set >0 to cause the timing source to be checked */
+
+ /* DMA related fields */
+ unsigned int dmactrl;
+ dma_addr_t readdma;
+ dma_addr_t writedma;
+ unsigned long memaddr; /* Base address of card */
+ unsigned long memlen;
+ volatile unsigned int *membase; /* Base address of card */
+
+ /* Flags for our bottom half */
+ unsigned long checkflag;
+ struct tasklet_struct t4_tlet;
+ unsigned int vpm400checkstatus;
+
+#ifdef VPM_SUPPORT
struct vpm450m *vpm450m;
+ int vpm;
+#endif
+
};
#define T4_VPM_PRESENT (1 << 28)
@@ -405,9 +389,7 @@ static void __t4_check_sigbits(struct t4 *wc, int span);
#define MAX_T4_CARDS 64
-#ifdef ENABLE_TASKLETS
-static void t4_tasklet(unsigned long data);
-#endif
+static void t4_isr_bh(unsigned long data);
static struct t4 *cards[MAX_T4_CARDS];
@@ -541,13 +523,13 @@ 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 & DEBUG_REGS)
+ if (unlikely(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);
__t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff) | WC_LFRMR_CS | WC_LWRITE);
__t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff));
- if (debug & DEBUG_REGS) printk("Write complete\n");
+ if (unlikely(debug & DEBUG_REGS)) printk("Write complete\n");
#if 0
if ((addr != FRMR_TXFIFO) && (addr != FRMR_CMDR) && (addr != 0xbc))
{ unsigned int tmp;
@@ -2729,9 +2711,9 @@ ZAP_IRQ_HANDLER(t4_interrupt)
printk("Pre-interrupt\n");
#endif
- inirq = 1;
/* Make sure it's really for us */
- status = t4_pci_in(wc, WC_INTR);
+ status = __t4_pci_in(wc, WC_INTR);
+
/* Process framer interrupts */
status2 = t4_framer_in(wc, 0, FRMR_CIS);
if (status2 & 0x0f) {
@@ -2751,7 +2733,7 @@ ZAP_IRQ_HANDLER(t4_interrupt)
return;
#endif
- t4_pci_out(wc, WC_INTR, 0);
+ __t4_pci_out(wc, WC_INTR, 0);
if (!wc->spansstarted) {
printk("Not prepped yet!\n");
@@ -2814,28 +2796,51 @@ ZAP_IRQ_HANDLER(t4_interrupt)
}
#endif
-
-ZAP_IRQ_HANDLER(t4_interrupt_gen2)
+static void t4_isr_bh(unsigned long data)
{
- struct t4 *wc = dev_id;
+ struct t4 *wc = (struct t4 *)data;
unsigned long flags;
unsigned char cis;
- int x;
- int needcheckvpm450=0;
-
- unsigned int status;
-#if 0
- unsigned int status2;
-#endif
-#if 0
- if (wc->intcount < 20)
- printk("2G: Pre-interrupt\n");
+ if (test_and_clear_bit(T4_CHECK_FRAMER, &wc->checkflag)) {
+ spin_lock_irqsave(&wc->reglock, flags);
+ cis = __t4_framer_in(wc, 0, FRMR_CIS);
+ if (cis & FRMR_CIS_GIS1)
+ __t4_framer_interrupt(wc, 0);
+ if (cis & FRMR_CIS_GIS2)
+ __t4_framer_interrupt(wc, 1);
+ if (cis & FRMR_CIS_GIS3)
+ __t4_framer_interrupt(wc, 2);
+ if (cis & FRMR_CIS_GIS4)
+ __t4_framer_interrupt(wc, 3);
+ spin_unlock_irqrestore(&wc->reglock, flags);
+ }
+
+#ifdef VPM_SUPPORT
+ if (wc->vpm) {
+ if (test_and_clear_bit(T4_CHECK_VPM, &wc->checkflag)) {
+ if (wc->vpm450m) {
+ /* How stupid is it that the octasic can't generate an
+ interrupt when there's a tone, in spite of what their
+ documentation says? */
+ t4_check_vpm450(wc);
+ } else {
+ spin_lock_irqsave(&wc->reglock, flags);
+ __t4_check_vpm400(wc, wc->vpm400checkstatus);
+ spin_unlock_irqrestore(&wc->reglock, flags);
+ }
+ }
+ }
#endif
+}
+
+ZAP_IRQ_HANDLER(t4_interrupt_gen2)
+{
+ struct t4 *wc = dev_id;
+ unsigned int status;
- inirq = 1;
/* Make sure it's really for us */
- status = t4_pci_in(wc, WC_INTR);
+ status = __t4_pci_in(wc, WC_INTR);
/* Ignore if it's not for us */
if (!(status & 0x7)) {
@@ -2847,10 +2852,10 @@ ZAP_IRQ_HANDLER(t4_interrupt_gen2)
}
#ifdef ENABLE_WORKQUEUES
- t4_pci_out(wc, WC_INTR, status & 0x00000008);
+ __t4_pci_out(wc, WC_INTR, status & 0x00000008);
#endif
- if (!wc->spansstarted) {
+ if (unlikely(!wc->spansstarted)) {
printk("Not prepped yet!\n");
#ifdef LINUX26
return IRQ_NONE;
@@ -2860,13 +2865,12 @@ ZAP_IRQ_HANDLER(t4_interrupt_gen2)
}
wc->intcount++;
-#if 1
- if ((wc->intcount < 20) && debug)
+
+ if (unlikely((wc->intcount < 20) && debug))
printk("2G: Got interrupt, status = %08x, CIS = %04x\n", status, __t4_framer_in(wc, 0, FRMR_CIS));
-#endif
- if (status & 0x2) {
+ if (likely(status & 0x2)) {
#ifdef ENABLE_WORKQUEUES
int cpus = num_online_cpus();
atomic_set(&wc->worklist, wc->numspans);
@@ -2893,97 +2897,33 @@ ZAP_IRQ_HANDLER(t4_interrupt_gen2)
#endif
}
- spin_lock_irqsave(&wc->reglock, flags);
+ if (unlikely(status & 0x1))
+ set_bit(T4_CHECK_FRAMER, &wc->checkflag);
- if (status & 0x2)
- __t4_do_counters(wc);
-
- if (polling && (status & 0x2)) {
- 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, FRMR_CIS);
- if (cis & FRMR_CIS_GIS1)
- __t4_framer_interrupt(wc, 0);
- if (cis & FRMR_CIS_GIS2)
- __t4_framer_interrupt(wc, 1);
- if (cis & FRMR_CIS_GIS3)
- __t4_framer_interrupt(wc, 2);
- if (cis & FRMR_CIS_GIS4)
- __t4_framer_interrupt(wc, 3);
- }
-#ifdef VPM_SUPPORT
if (wc->vpm) {
- if (!wc->vpm450m && !(wc->intcount % 16) && !(wc->tspans[0]->spanflags & FLAG_VPM2GEN)) {
- /* Check DTMF events */
- int span = (wc->intcount >> 4) & 0x3;
- int y;
- short energy;
- int offset = 1;
- int chip;
- int channel;
- struct t4_span *ts = wc->tspans[span];
- if (!wc->t1e1)
- offset = 5;
- if (ts->dtmfactive) {
- for (y = 0; y < ts->span.channels; y++) {
- if (ts->dtmfactive & (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, ts->span.chans);
- if (energy < (ts->dtmfenergy[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, ts->dtmfenergy[y], channel, chip);
- if (debug & DEBUG_DTMF)
- printk("Finished digit '%c' on channel %d of span %d\n", ts->dtmfdigit[y], y + 1, span);
- if (ts->dtmfmask & (1 << y))
- zt_qevent_lock(&ts->span.chans[y], (ZT_EVENT_DTMFUP | ts->dtmfdigit[y]));
- ts->dtmfenergy[y] = 0;
- ts->dtmfdigit[y] = 0;
- ts->dtmfactive &= ~(1 << y);
- } else if (energy > (ts->dtmfenergy[y])) {
- if (debug & DEBUG_DTMF)
- printk("Increasing digit energy on span %d, channel %d (energy = %02x > %02x)!\n", span, y + 1, energy, ts->dtmfenergy[y]);
- ts->dtmfenergy[y] = energy;
- }
- }
- }
- }
- }
if (wc->vpm450m) {
/* How stupid is it that the octasic can't generate an
interrupt when there's a tone, in spite of what their
documentation says? */
if (!(wc->intcount & 0xf)) {
- needcheckvpm450 = 1;
+ set_bit(T4_CHECK_VPM, &wc->checkflag);
}
- } else if ((status & 0xff00) != 0xff00)
- __t4_check_vpm400(wc, (status & 0xff00) >> 8);
+ } else if ((status & 0xff00) != 0xff00) {
+ wc->vpm400checkstatus = (status & 0xff00) >> 8;
+ set_bit(T4_CHECK_VPM, &wc->checkflag);
+ }
}
-#endif
-#if 1
+ spin_lock(&wc->reglock);
+
+ __t4_do_counters(wc);
__handle_leds(wc);
-#endif
- if (wc->checktiming > 0)
+ if (unlikely(wc->checktiming > 0)) {
__t4_set_timing_source_auto(wc);
- if (wc->stopdma) {
+ }
+
+ if (unlikely(wc->stopdma)) {
/* Stop DMA cleanly if requested */
wc->dmactrl = 0x0;
__t4_pci_out(wc, WC_DMACTRL, 0x00000000);
@@ -2992,15 +2932,14 @@ ZAP_IRQ_HANDLER(t4_interrupt_gen2)
__t4_set_timing_source(wc, 4, 0, 0);
wc->stopdma = 0x0;
}
- spin_unlock_irqrestore(&wc->reglock, flags);
- if (needcheckvpm450 && (vpmdtmfsupport == 1)) {
- t4_check_vpm450(wc);
- needcheckvpm450 = 0;
- }
+ spin_unlock(&wc->reglock);
+
+ if (unlikely(test_bit(T4_CHECK_VPM, &wc->checkflag) || test_bit(T4_CHECK_FRAMER, &wc->checkflag)))
+ tasklet_schedule(&wc->t4_tlet);
#ifndef ENABLE_WORKQUEUES
- t4_pci_out(wc, WC_INTR, 0);
+ __t4_pci_out(wc, WC_INTR, 0);
#endif
#ifdef LINUX26
return IRQ_RETVAL(1);
@@ -3187,8 +3126,8 @@ static void t4_vpm450_init(struct t4 *wc)
#endif
if (vpmdtmfsupport == -1) {
- printk("VPM450: hardware DTMF disabled.\n");
- vpmdtmfsupport = 0;
+ printk("VPM450: hardware DTMF enabled.\n");
+ vpmdtmfsupport = 1;
}
wc->vpm = T4_VPM_PRESENT;
@@ -3494,9 +3433,7 @@ static int __devinit t4_launch(struct t4 *wc)
spin_lock_irqsave(&wc->reglock, flags);
__t4_set_timing_source(wc,4, 0, 0);
spin_unlock_irqrestore(&wc->reglock, flags);
-#ifdef ENABLE_TASKLETS
- tasklet_init(&wc->t4_tlet, t4_tasklet, (unsigned long)wc);
-#endif
+ tasklet_init(&wc->t4_tlet, t4_isr_bh, (unsigned long)wc);
return 0;
}
diff --git a/wct4xxp/wct4xxp.h b/wct4xxp/wct4xxp.h
index aa028a4..8b9de68 100644
--- a/wct4xxp/wct4xxp.h
+++ b/wct4xxp/wct4xxp.h
@@ -105,5 +105,9 @@ struct t4_regs {
unsigned char regs[NUM_REGS];
};
+#define T4_CHECK_FRAMER 0
+#define T4_CHECK_VPM 1
+#define T4_UPDATE_TIMERS 2
+
#define WCT4_GET_REGS _IOW (ZT_CODE, 60, struct t4_regs)