summaryrefslogtreecommitdiff
path: root/torisa.c
diff options
context:
space:
mode:
authormarkster <markster@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2001-10-27 20:51:21 +0000
committermarkster <markster@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2001-10-27 20:51:21 +0000
commitd18a439c45a0300519463b14c76e282bbf3eddec (patch)
tree738f0d4519a805860117f03582bacad6ebd3b8f9 /torisa.c
parente243f0857eaa359e4c6e7b9c2dd0e317d4d8b988 (diff)
Version 0.1.0 from FTP
git-svn-id: http://svn.digium.com/svn/zaptel/trunk@17 5390a7c7-147a-4af0-8ec9-7488f05a26cb
Diffstat (limited to 'torisa.c')
-rwxr-xr-xtorisa.c902
1 files changed, 902 insertions, 0 deletions
diff --git a/torisa.c b/torisa.c
new file mode 100755
index 0000000..c4fa92d
--- /dev/null
+++ b/torisa.c
@@ -0,0 +1,902 @@
+/*
+ * Zapata Telephony "Tormenta" ISA card LINUX driver, version 2.0 X/X/01
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Modified from original tor.c by Mark Spencer <markster@linux-support.net>
+ * original by Jim Dixon <jim@lambdatel.com>
+ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+
+#ifdef STANDALONE_ZAPATA
+#include "zaptel.h"
+#include "torisa.h"
+#else
+#include <linux/zaptel.h>
+#include <linux/torisa.h>
+#endif
+
+/* Board address offsets (specified in word (not byte) offsets) */
+#define DDATA 0 /* Data I/O Register */
+#define DADDR 0x100 /* Dallas Card Address Reg., 0x200 in byte offset higher */
+#define CTLREG 0x100 /* Control/Status Reg., 0x200 in byte offset */
+
+/* Control register bits */
+#define MASTERCLOCK 0 /* Value for Master Clock */
+#define OUTBIT 8 /* Status output bit (for external measurements) */
+#define INTENA 4 /* Interrupt enable bit */
+#define ENA16 0x80 /* 16 bit bus cycle enable bit */
+
+/* signalling bits */
+#define TOR_ABIT 8
+#define TOR_BBIT 4
+
+static int syncsrc;
+
+static int syncs[2];
+
+static int debug;
+
+/* clock values */
+static u_char clockvals[] = {0,0x12,0x22,0};
+
+/* translations of data channels for 24 channels in a 32 bit PCM highway */
+unsigned datxlt[] = { 0,
+ 1 ,2 ,3 ,5 ,6 ,7 ,9 ,10,11,13,14,15,17,18,19,21,22,23,25,26,27,29,30,31 };
+
+/* This is the order that the data (audio) channels get
+scanned in. This was done in this rather poopy manner because when outputting
+(and inputting) a sine wave, such as in the case of TDD, any repeated samples
+(because of PCM bus contention) will result in nasty-sounding distortion. The
+Mitel STPA chips (MT8920) have a contention mechanism, which results in a
+situation where, if the processor accesses a timeslot that is currently
+being transmitted or received, it will HOLD the bus until it is done with
+the timeslot. This means that there can be cases where we are trying
+to write to a timeslot, and its already outputting the same value
+as the last one (since we didnt get there in time), and in a sine-wave
+output, distortion will occur. In any other output, it will be utterly
+un-noticeable. So, what we do is use a pattern that gives us the most
+flexibility in how long our interrupt latency is (note: Even with this,
+our interrupt latency must be between 4 and 28 microseconds!!!) Essentially
+we receive the interrupt just after the 24th channel is read. It will
+take us AT LEAST 30 microseconds to read it, but could take as much as
+35 microseconds to read all the channels. In any case it's the very
+first thing we do in the interrupt handler. Worst case (30 microseconds)
+is that the MT8920 has only moved 7 channels. That's where the 6 comes from.
+*/
+
+static int chseq[] =
+ { 6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,1,2,3,4,5 } ;
+
+struct torisa_pvt {
+ int span;
+};
+
+static struct zt_span spans[2];
+static struct zt_chan chans[48];
+static struct torisa_pvt pvts[48];
+static u_char txsigs[2][6];
+static int loopupcnt[2];
+static int loopdowncnt[2];
+static int alarmtimer[2];
+
+static int prefmaster = 0;
+
+static int spansstarted = 0;
+
+static rwlock_t torisa;
+
+static u_char readdata[2][48][ZT_MAX_CHUNKSIZE];
+static u_char writedata[2][48][ZT_MAX_CHUNKSIZE];
+static int curread;
+
+static unsigned long base;
+volatile static unsigned short *maddr;
+
+static int irq;
+static unsigned int irqcount = 0;
+static unsigned int taskletsched = 0;
+static unsigned int taskletrun = 0;
+static unsigned int taskletexec = 0;
+
+/* set the control register */
+static void setctlreg(unsigned char val)
+{
+volatile register char *cp;
+
+ cp = (char *) &maddr[CTLREG];
+ *cp = val;
+}
+
+/* output a byte to one of the registers in one of the Dallas T-1 chips */
+static void t1out(int spanno, int loc, unsigned char val)
+{
+register int n;
+volatile register char *cp;
+
+ /* get the memory offset */
+ n = spanno << 9;
+ /* point a char * at the address location */
+ cp = (char *) &maddr[DADDR + n];
+ *cp = loc; /* set address in T1 chip */
+ /* point a char * at the data location */
+ cp = (char *) &maddr[DDATA + n];
+ *cp = val; /* out the value */
+}
+
+/* get a byte from one of the registers in one of the Dallas T-1 chips */
+static unsigned char t1in(int spanno, int loc)
+{
+register int n;
+volatile register char *cp;
+
+ /* get the memory offset */
+ n = spanno << 9;
+ /* point a char * at the address location */
+ cp = (char *) &maddr[DADDR + n];
+ *cp = loc; /* set address in T1 chip */
+ cp = (char *) &maddr[DDATA + n];
+ /* point a char * at the data location */
+ return(*cp);
+}
+
+/* get input from the status register */
+static unsigned char getctlreg(void)
+{
+register char *cp;
+
+ cp = (char *) &maddr[CTLREG];
+ return(*cp);
+}
+
+static void set_clear(void)
+{
+ int i,j,s;
+ unsigned short val=0;
+ for (s=0;s<2;s++) {
+ for (i=0;i<24;i++) {
+ j = (i/8);
+ if (spans[s].chans[i].flags & ZT_FLAG_CLEAR)
+ val |= 1 << (i % 8);
+
+ if ((i % 8)==7) {
+#if 0
+ printk("Putting %d in register %02x on span %d\n",
+ val, 0x39 + j, 1 + s);
+#endif
+ t1out(1 + s, 0x39 + j, val);
+ val = 0;
+ }
+ }
+ }
+
+}
+
+/* device probe routine .. determines if the Tormenta device is present in
+ the system */
+static int
+tor_probe(void)
+{
+ int i,status;
+ u_char c1,c2;
+ maddr = phys_to_virt(base);
+
+ status = -1; /* default status return is 'not present' */
+
+ /* initialize control register */
+ setctlreg(MASTERCLOCK);
+
+ /* init all the registers in first T-1 chip to 0 */
+ for(i = 0x20; i < 0x40; i++) t1out(1,i,0); /* set register to 0 */
+ for(i = 0x60; i < 0x80; i++) t1out(1,i,0); /* set register to 0 */
+ /* simple test that will fail if tried in an array of standard memory */
+ /* put an 0x55 here */
+ t1out(1,0x2b,0x55);
+ /* put an 0xaa here */
+ t1out(1,0x2c,0xaa);
+ /* get input from first location */
+ c1 = t1in(1,0x2b);
+ /* get input from second location */
+ c2 = t1in(1,0x2c);
+ /* see if we read back what we put in */
+ if ((c1 == 0x55) && (c2 == 0xaa))
+ {
+ /* Try to get the irq if the user didn't specify one */
+ if (irq < 1) {
+ autoirq_setup(0);
+ setctlreg(MASTERCLOCK|INTENA);
+ /* Wait a jiffie -- that's plenty of time */
+ irq = autoirq_report(5);
+ }
+ /* disable interrupts having gotten one */
+ setctlreg(MASTERCLOCK);
+ if (irq == 2)
+ irq = 9;
+ if (irq) {
+ /* init both STPA's to all silence */
+ for(i = 0; i < 32; i++) maddr[i] = 0x7f7f;
+
+ status = 0; /* found */
+ if (debug)
+ printk("ISA Tormenta Card found at base addr 0x%lx, irq %d\n",base,irq);
+ } else
+ printk("ISA Tormenta Card found at base addr 0x%lx, but unable to determine IRQ. Try using irq= option\n", base);
+ }
+ return status;
+}
+
+static void make_chans(void)
+{
+ int x,y;
+ int c;
+ for (x=0;x<2;x++)
+ for (y=0;y<24;y++) {
+ c = x * 24 + y;
+ sprintf(chans[c].name, "TorISA/%d/%d", x + 1, y + 1);
+ chans[c].sigcap = ZT_SIG_EM | ZT_SIG_CLEAR | ZT_SIG_FXSLS | ZT_SIG_FXSGS | ZT_SIG_FXSKS |
+ ZT_SIG_FXOLS | ZT_SIG_FXOGS | ZT_SIG_FXOKS;
+ chans[c].pvt = &pvts[c];
+ pvts[c].span = x;
+ chans[c].chanpos = y + 1;
+ }
+
+}
+
+static int torisa_rbsbits(struct zt_chan *chan, int bits)
+{
+ u_char m,c;
+ int k,n,b;
+ struct torisa_pvt *p = chan->pvt;
+ unsigned int flags;
+#if 0
+ printk("Setting bits to %d on channel %s\n", bits, chan->name);
+#endif
+ n = chan->chanpos - 1;
+ k = p->span;
+ b = (n / 8); /* get byte number */
+ m = 1 << (n & 7); /* get mask */
+ c = txsigs[k][b];
+ c &= ~m; /* clear mask bit */
+ /* set mask bit, if bit is to be set */
+ if (bits & ZT_ABIT) c |= m;
+ txsigs[k][b] = c;
+ write_lock_irqsave(&torisa, flags);
+ t1out(k + 1,0x70 + b,c);
+ t1out(k + 1,0x76 + b,c);
+ b += 3; /* now points to b bit stuff */
+ /* get current signalling values */
+ c = txsigs[k][b];
+ c &= ~m; /* clear mask bit */
+ /* set mask bit, if bit is to be set */
+ if (bits & ZT_BBIT) c |= m;
+ /* save new signalling values */
+ txsigs[k][b] = c;
+ /* output them into the chip */
+ t1out(k + 1,0x70 + b,c);
+ t1out(k + 1,0x76 + b,c);
+ write_unlock_irqrestore(&torisa, flags);
+ return 0;
+}
+
+static inline int getspan(struct zt_span *span)
+{
+ if (span == spans)
+ return 1;
+ if (span == spans + 1)
+ return 2;
+ return -1;
+}
+
+static int torisa_shutdown(struct zt_span *span)
+{
+ int i;
+ int tspan;
+ int wasrunning;
+ unsigned int flags;
+
+ tspan = getspan(span);
+ if (tspan < 0) {
+ printk("TorISA: Span '%d' isn't us?\n", span->spanno);
+ return -1;
+ }
+
+ write_lock_irqsave(&torisa, flags);
+ wasrunning = span->flags & ZT_FLAG_RUNNING;
+
+ span->flags &= ~ZT_FLAG_RUNNING;
+ /* Zero out all registers */
+ for (i = 0x20; i< 0x40; i++)
+ t1out(tspan, i, 0);
+ for (i = 0x60; i< 0x80; i++)
+ t1out(tspan, i, 0);
+ if (wasrunning)
+ spansstarted--;
+ write_unlock_irqrestore(&torisa, flags);
+ if (!spans[0].flags & ZT_FLAG_RUNNING &&
+ !spans[1].flags & ZT_FLAG_RUNNING)
+ /* No longer in use, disable interrupts */
+ setctlreg(clockvals[syncsrc]);
+
+ if (debug)
+ printk("Span %d (%s) shutdown\n", span->spanno, span->name);
+ return 0;
+}
+
+static int torisa_startup(struct zt_span *span)
+{
+ unsigned long endjif;
+ int i;
+ int tspan;
+ unsigned int flags;
+ char *coding;
+ char *framing;
+ int alreadyrunning;
+
+ tspan = getspan(span);
+ if (tspan < 0) {
+ printk("TorISA: Span '%d' isn't us?\n", span->spanno);
+ return -1;
+ }
+
+
+ write_lock_irqsave(&torisa, flags);
+
+ alreadyrunning = span->flags & ZT_FLAG_RUNNING;
+
+ if (!alreadyrunning) {
+
+ setctlreg(MASTERCLOCK);
+ /* Zero out all registers */
+ for (i = 0x20; i< 0x40; i++)
+ t1out(tspan, i, 0);
+ for (i = 0x60; i< 0x80; i++)
+ t1out(tspan, i, 0);
+
+ /* Full-on Sync required (RCR1) */
+ t1out(tspan, 0x2b, 8);
+ /* RSYNC is an input (RCR2) */
+ t1out(tspan, 0x2c, 8);
+ /* RBS enable (TCR1) */
+ t1out(tspan, 0x35, 0x10);
+ /* TSYNC to be output (TCR2) */
+ t1out(tspan, 0x36, 4);
+ /* Tx & Rx Elastic store, sysclk = 2.048 mhz, loopback controls (CCR1) */
+ t1out(tspan, 0x37, 0x8c);
+ }
+ /* Enable F bits pattern */
+ i = 0x20;
+ if (span->lineconfig & ZT_CONFIG_ESF)
+ i = 0x88;
+ if (span->lineconfig & ZT_CONFIG_B8ZS)
+ i |= 0x44;
+ t1out(tspan, 0x38, i);
+ if (i & 0x80)
+ coding = "ESF";
+ else
+ coding = "SF";
+ if (i & 0x40)
+ framing = "B8ZS";
+ else {
+ framing = "AMI";
+ t1out(tspan,0x7e,0x1c); /* F bits pattern (0x1c) into FDL register */
+ }
+ t1out(tspan, 0x7c, span->txlevel << 5);
+
+ if (!alreadyrunning) {
+ /* LIRST to 1 in CCR3 */
+ t1out(tspan, 0x30, 1);
+
+ /* Wait 100 ms */
+ endjif = jiffies + 10;
+ write_unlock_irqrestore(&torisa, flags);
+
+ while(jiffies < endjif); /* wait 100 ms */
+
+ write_lock_irqsave(&torisa, flags);
+ t1out(tspan,0x30,0x40); /* set CCR3 to 0x40, resetting Elastic Store */
+
+ span->flags |= ZT_FLAG_RUNNING;
+ spansstarted++;
+
+#if 0
+ printk("Enabling interrupts: %d\n", clockvals[syncsrc] | INTENA);
+#endif
+
+ /* output the clock info and enable interrupts */
+ setctlreg(clockvals[syncsrc] | INTENA);
+ }
+
+ set_clear();
+
+ write_unlock_irqrestore(&torisa, flags);
+
+ if (debug) {
+ if (alreadyrunning)
+ printk("TorISA: Reconfigured span %d (%s/%s) LBO: %s\n", span->spanno, coding, framing, zt_lboname(span->txlevel));
+ else
+ printk("TorISA: Startup span %d (%s/%s) LBO: %s\n", span->spanno, coding, framing, zt_lboname(span->txlevel));
+ }
+ if (syncs[0] == span->spanno) printk("SPAN %d: Primary Sync Source\n",span->spanno);
+ if (syncs[1] == span->spanno) printk("SPAN %d: Secondary Sync Source\n",span->spanno);
+ return 0;
+}
+
+static int torisa_spanconfig(struct zt_span *span, struct zt_lineconfig *lc)
+{
+ if (debug)
+ printk("TorISA: Configuring span %d\n", span->spanno);
+ /* XXX We assume lineconfig is okay and shouldn't XXX */
+ span->lineconfig = lc->lineconfig;
+ span->txlevel = lc->lbo;
+ span->rxlevel = 0;
+ span->syncsrc = syncsrc;
+
+ /* remove this span number from the current sync sources, if there */
+ if (syncs[0] == span->spanno) syncs[0] = 0;
+ if (syncs[1] == span->spanno) syncs[1] = 0;
+ /* if a sync src, put it in proper place */
+ if (lc->sync) syncs[lc->sync - 1] = span->spanno;
+
+ /* If we're already running, then go ahead and apply the changes */
+ if (span->flags & ZT_FLAG_RUNNING)
+ return torisa_startup(span);
+ return 0;
+}
+
+static int torisa_chanconfig(struct zt_chan *chan, int sigtype)
+{
+ int alreadyrunning;
+ unsigned int flags;
+ alreadyrunning = chan->span->flags & ZT_FLAG_RUNNING;
+ if (debug) {
+ if (alreadyrunning)
+ printk("TorISA: Reconfigured channel %d (%s) sigtype %d\n", chan->channo, chan->name, sigtype);
+ else
+ printk("TorISA: Configured channel %d (%s) sigtype %d\n", chan->channo, chan->name, sigtype);
+ }
+ write_lock_irqsave(&torisa, flags);
+ if (alreadyrunning)
+ set_clear();
+ write_unlock_irqrestore(&torisa, flags);
+ return 0;
+}
+
+static int torisa_open(struct zt_chan *chan)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int torisa_close(struct zt_chan *chan)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int torisa_maint(struct zt_span *span, int cmd)
+{
+ int tspan = getspan(span);
+
+ switch(cmd) {
+ case ZT_MAINT_NONE:
+ t1out(tspan,0x37,0x8c); /* clear system */
+ break;
+ case ZT_MAINT_LOCALLOOP:
+ t1out(tspan,0x37,0xcc); /* local loopback */
+ break;
+ case ZT_MAINT_REMOTELOOP:
+ t1out(tspan,0x37,0x9c); /* remote loopback */
+ break;
+ case ZT_MAINT_LOOPUP:
+ t1out(tspan,0x30,2); /* send loopup code */
+ break;
+ case ZT_MAINT_LOOPDOWN:
+ t1out(tspan,0x30,4); /* send loopdown code */
+ break;
+ case ZT_MAINT_LOOPSTOP:
+ t1out(tspan,0x30,0); /* stop sending loopup code */
+ break;
+ default:
+ printk("torisa: Unknown maint command: %d\n", cmd);
+ break;
+ }
+ return 0;
+}
+
+static int taskletpending;
+
+static struct tasklet_struct torisa_tlet;
+
+static void torisa_tasklet(unsigned long data)
+{
+
+ taskletrun++;
+ if (taskletpending) {
+ taskletexec++;
+ /* Perform receive data calculations. Reverse to run most
+ likely master last */
+ if (spans[1].flags & ZT_FLAG_RUNNING)
+ zt_receive(&spans[1]);
+ if (spans[0].flags & ZT_FLAG_RUNNING)
+ zt_receive(&spans[0]);
+
+ /* Prepare next set for transmission */
+ if (spans[1].flags & ZT_FLAG_RUNNING)
+ zt_transmit(&spans[1]);
+ if (spans[0].flags & ZT_FLAG_RUNNING)
+ zt_transmit(&spans[0]);
+ }
+ taskletpending = 0;
+}
+
+static int txerrors;
+
+static void torisa_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ static unsigned int passno = 0;
+ int n, n1, i, j, k, x;
+ static unsigned short rxword[25],txword[25];
+ unsigned char txc, rxc, c;
+ unsigned char abits, bbits;
+
+
+ irqcount++;
+
+ /* 1. Do all I/O Immediately -- Normally we would ask for
+ the transmission first, but because of the incredibly
+ tight timing we're lucky to be able to do the I/O
+ at this point */
+
+ /* make sure its a real interrupt for us */
+ if (!(getctlreg() & 1)) /* if not, just return */
+ {
+ return;
+ }
+
+ /* set outbit and put int 16 bit bus mode, reset interrupt enable */
+ setctlreg(clockvals[syncsrc] | OUTBIT | ENA16);
+
+#if 0
+ if (!passno)
+ printk("Interrupt handler\n");
+#endif
+
+ /* Do the actual transmit and receive in poopy order */
+ for(n1 = 0; n1 < 24; n1++)
+ {
+ n = chseq[n1];
+ maddr[DDATA + datxlt[n]] = txword[n];
+ rxword[n] = maddr[DDATA + datxlt[n]]; /* get rx word */
+ }
+
+ setctlreg(clockvals[syncsrc] | OUTBIT); /* clear 16 bit mode */
+
+
+ /* Calculate the transmit, go thru all the chans */
+ for(n1 = 0; n1 < 24; n1++)
+ {
+ n = chseq[n1];
+ txword[n] = 0;
+ /* go thru both spans */
+ for(j = 0; j < 2; j++)
+ {
+ /* enter the transmit stuff with i being channel number,
+ leaving with txc being character to transmit */
+ txc = writedata[curread][j * 24 + n-1][passno % ZT_CHUNKSIZE];
+ txword[n] |= txc << (j * 8);
+ }
+ }
+
+
+ /* do the receive for all chans, both spans */
+ for(n1 = 0; n1 < 24; n1++)
+ {
+ n = chseq[n1];
+ /* go thru both spans */
+ for(j = 0; j <= 1; j++)
+ {
+ i = n + (j * 24); /* calc chan number */
+ rxc = (rxword[n] >> (j * 8)) & 0xff;
+ readdata[curread][j * 24 + n - 1][passno % ZT_CHUNKSIZE] = rxc;
+ }
+ }
+
+ i = passno & 511;
+ if (i < 6) {
+ k = (i / 3); /* get span */
+ n = (i % 3); /* get base */
+ abits = t1in(k + 1, 0x60 + n);
+ bbits = t1in(k + 1, 0x63 + n);
+ for (j=0; j< 8; j++) {
+ /* Get channel number */
+ i = (k * 24) + (n * 8) + j;
+ rxc = 0;
+ if (abits & (1 << j)) rxc |= ZT_ABIT;
+ if (bbits & (1 << j)) rxc |= ZT_BBIT;
+ if (chans[i].rxsig != rxc) {
+ /* Check for changes in received bits */
+ if (!(chans[i].sig & ZT_SIG_CLEAR))
+ zt_rbsbits(&chans[i], rxc);
+ }
+ }
+ }
+
+ if (!(passno & 0x7)) {
+ for(i = 0; i < 2; i++)
+ {
+ /* if alarm timer, and it's timed out */
+ if (alarmtimer[i]) {
+ if (!--alarmtimer[i])
+ {
+ /* clear recover status */
+ spans[i].alarms &= ~ZT_ALARM_RECOVER;
+ t1out(i + 1,0x35,0x10); /* turn off yel */
+ zt_alarm_notify(&spans[i]); /* let them know */
+ }
+ }
+ }
+ }
+
+ i = passno & 511;
+ if ((i == 100) || (i == 101))
+ {
+ j = 0; /* clear this alarm status */
+ i -= 100;
+ c = t1in(i + 1,0x31); /* get RIR2 */
+ spans[i].rxlevel = c >> 6; /* get rx level */
+ t1out(i + 1,0x20,0xff);
+ c = t1in(i + 1,0x20); /* get the status */
+ /* detect the code, only if we are not sending one */
+ if ((!spans[i].mainttimer) && (c & 0x80)) /* if loop-up code detected */
+ {
+ /* set into remote loop, if not there already */
+ if ((loopupcnt[i]++ > 80) &&
+ (spans[i].maintstat != ZT_MAINT_REMOTELOOP))
+ {
+ t1out(i + 1,0x37,0x9c); /* remote loopback */
+ spans[i].maintstat = ZT_MAINT_REMOTELOOP;
+ }
+ } else loopupcnt[i] = 0;
+ /* detect the code, only if we are not sending one */
+ if ((!spans[i].mainttimer) && (c & 0x40)) /* if loop-down code detected */
+ {
+ /* if in remote loop, get out of it */
+ if ((loopdowncnt[i]++ > 80) &&
+ (spans[i].maintstat == ZT_MAINT_REMOTELOOP))
+ {
+ t1out(i + 1,0x37,0x8c); /* normal */
+ spans[i].maintstat = ZT_MAINT_NONE;
+ }
+ } else loopdowncnt[i] = 0;
+ if (c & 3) /* if red alarm */
+ {
+ j |= ZT_ALARM_RED;
+ }
+ if (c & 8) /* if blue alarm */
+ {
+ j |= ZT_ALARM_BLUE;
+ }
+ /* only consider previous carrier alarm state */
+ spans[i].alarms &= (ZT_ALARM_RED | ZT_ALARM_BLUE | ZT_ALARM_NOTOPEN);
+ n = 1; /* set to 1 so will not be in yellow alarm if we dont
+ care about open channels */
+ /* if to have yellow alarm if nothing open */
+ if (spans[i].lineconfig & ZT_CONFIG_NOTOPEN)
+ {
+ /* go thru all chans, and count # open */
+ for(n = 0,k = (i * 24); k < (i * 24) + 24; k++)
+ {
+ if ((chans[k].flags & ZT_FLAG_OPEN) ||
+ (chans[k].flags & ZT_FLAG_NETDEV)) n++;
+ }
+ /* if none open, set alarm condition */
+ if (!n) j |= ZT_ALARM_NOTOPEN;
+ }
+ /* if no more alarms, and we had some */
+ if ((!j) && spans[i].alarms)
+ {
+ alarmtimer[i] = ZT_ALARMSETTLE_TIME;
+ }
+ if (alarmtimer[i]) j |= ZT_ALARM_RECOVER;
+ /* if going into alarm state, set yellow alarm */
+ if ((j) && (!spans[i].alarms)) t1out(i + 1,0x35,0x11);
+ if (c & 4) /* if yellow alarm */
+ j |= ZT_ALARM_YELLOW;
+ if (spans[i].maintstat || spans[i].mainttimer) j |= ZT_ALARM_LOOPBACK;
+ spans[i].alarms = j;
+ zt_alarm_notify(&spans[i]);
+ }
+ if (!(passno % 8000)) /* even second boundary */
+ {
+ /* do both spans */
+ for(i = 1; i <= 2; i++)
+ {
+ /* add this second's BPV count to total one */
+ spans[i - 1].bpvcount += t1in(i,0x24) + (t1in(i,0x23) << 8);
+ }
+ }
+ /* re-evaluate active sync src */
+ syncsrc = 0;
+ /* if primary sync specified, see if we can use it */
+ if (syncs[0])
+ {
+ /* if no alarms, use it */
+ if (!(spans[syncs[0] - 1].alarms & (ZT_ALARM_RED | ZT_ALARM_BLUE |
+ ZT_ALARM_LOOPBACK))) syncsrc = syncs[0];
+ }
+ /* if we dont have one yet, and there is a secondary, see if we can use it */
+ if ((!syncsrc) && (syncs[1]))
+ {
+ /* if no alarms, use it */
+ if (!(spans[syncs[1] - 1].alarms & (ZT_ALARM_RED | ZT_ALARM_BLUE |
+ ZT_ALARM_LOOPBACK))) syncsrc = syncs[1];
+ }
+ /* update sync src info */
+ spans[0].syncsrc = spans[1].syncsrc = syncsrc;
+ /* If this is the last pass, then prepare the next set */
+ if ((passno % ZT_CHUNKSIZE) == (ZT_CHUNKSIZE - 1)) {
+ /* Swap buffers */
+ for (x = 0;x<48;x++) {
+ chans[x].readchunk = readdata[curread][x];
+ chans[x].writechunk = writedata[curread][x];
+ }
+ /* Lets work with the others now which presumably have been filled */
+ curread = 1 - curread;
+ if (!taskletpending) {
+ taskletpending = 1;
+ taskletsched++;
+ tasklet_hi_schedule(&torisa_tlet);
+ } else {
+ txerrors++;
+ }
+
+ }
+ passno++;
+ /* clear outbit, restore interrupt enable */
+ setctlreg(clockvals[syncsrc] | INTENA);
+}
+
+
+static int torisa_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data)
+{
+ struct torisa_debug td;
+ switch(cmd) {
+ case TORISA_GETDEBUG:
+ td.txerrors = txerrors;
+ td.irqcount = irqcount;
+ td.taskletsched = taskletsched;
+ td.taskletrun = taskletrun;
+ td.taskletexec = taskletexec;
+ td.span1flags = spans[0].flags;
+ td.span2flags = spans[1].flags;
+ if (copy_to_user((struct torisa_debug *)data, &td, sizeof(td)))
+ return -EFAULT;
+ return 0;
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static int __init tor_init(void)
+{
+ if (!base) {
+ printk("Specify address with base=0xNNNNN\n");
+ return -EIO;
+ }
+ if (tor_probe()) {
+ printk(KERN_ERR "No ISA tormenta card found at %05lx\n", base);
+ return -EIO;
+ }
+ if (request_irq(irq, torisa_intr, SA_INTERRUPT, "torisa", NULL)) {
+ printk(KERN_ERR "Unable to request tormenta IRQ %d\n", irq);
+ return -EIO;
+ }
+ if (!request_mem_region(base, 4096, "Tormenta ISA")) {
+ printk(KERN_ERR "Unable to request 4k memory window at %lx\n", base);
+ free_irq(irq, NULL);
+ return -EIO;
+ }
+ strcpy(spans[0].name, "TorISA/1");
+ strcpy(spans[0].desc, "ISA Tormenta Span 1");
+ spans[0].spanconfig = torisa_spanconfig;
+ spans[0].chanconfig = torisa_chanconfig;
+ spans[0].startup = torisa_startup;
+ spans[0].shutdown = torisa_shutdown;
+ spans[0].rbsbits = torisa_rbsbits;
+ spans[0].maint = torisa_maint;
+ spans[0].open = torisa_open;
+ spans[0].close = torisa_close;
+ spans[0].channels = 24;
+ spans[0].chans = &chans[0];
+ spans[0].flags = ZT_FLAG_RBS;
+ spans[0].linecompat = ZT_CONFIG_AMI | ZT_CONFIG_B8ZS | ZT_CONFIG_D4 | ZT_CONFIG_ESF;
+ spans[0].ioctl = torisa_ioctl;
+ spans[0].irq = irq;
+ init_waitqueue_head(&spans[0].maintq);
+
+
+ strcpy(spans[1].name, "TorISA/2");
+ strcpy(spans[1].desc, "ISA Tormenta Span 2");
+ spans[1].spanconfig = torisa_spanconfig;
+ spans[1].chanconfig = torisa_chanconfig;
+ spans[1].startup = torisa_startup;
+ spans[1].shutdown = torisa_shutdown;
+ spans[1].rbsbits = torisa_rbsbits;
+ spans[1].maint = torisa_maint;
+ spans[1].open = torisa_open;
+ spans[1].close = torisa_close;
+ spans[1].channels = 24;
+ spans[1].chans = &chans[24];
+ spans[1].flags = ZT_FLAG_RBS;
+ spans[1].linecompat = ZT_CONFIG_AMI | ZT_CONFIG_B8ZS | ZT_CONFIG_D4 | ZT_CONFIG_ESF;
+ spans[1].ioctl = torisa_ioctl;
+ spans[1].irq = irq;
+ init_waitqueue_head(&spans[1].maintq);
+
+ make_chans();
+ if (zt_register(&spans[0], prefmaster)) {
+ printk(KERN_ERR "Unable to register span %s\n", spans[0].name);
+ return -EIO;
+ }
+ if (zt_register(&spans[1], 0)) {
+ printk(KERN_ERR "Unable to register span %s\n", spans[1].name);
+ zt_unregister(&spans[0]);
+ return -EIO;
+ }
+ tasklet_init(&torisa_tlet, torisa_tasklet, (long)0);
+ printk("TORISA Loaded\n");
+ return 0;
+}
+
+
+static int __init set_tor_base(char *str)
+{
+ base = simple_strtol(str, NULL, 0);
+ return 1;
+}
+
+__setup("tor=", set_tor_base);
+
+static void __exit tor_exit(void)
+{
+ free_irq(irq, NULL);
+ release_mem_region(base, 4096);
+ if (spans[0].flags & ZT_FLAG_REGISTERED)
+ zt_unregister(&spans[0]);
+ if (spans[1].flags & ZT_FLAG_REGISTERED)
+ zt_unregister(&spans[1]);
+}
+
+MODULE_AUTHOR("Mark Spencer <markster@linux-support.net>");
+MODULE_DESCRIPTION("Tormenta ISA Zapata Telephony Driver");
+MODULE_PARM(prefmaster, "i");
+MODULE_PARM(base, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(syncsrc, "i");
+MODULE_PARM(debug, "i");
+
+module_init(tor_init);
+module_exit(tor_exit);