summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormarkster <markster@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2003-07-09 21:48:21 +0000
committermarkster <markster@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2003-07-09 21:48:21 +0000
commite7a501944bd6aa669edfcda579987089c247e37d (patch)
treec50d5451b23d8137b7222ae87e9c85ff3934200c
parentc39e312572dbd0769a616c94986d1d935f74807c (diff)
Add TE410P driver
git-svn-id: http://svn.digium.com/svn/zaptel/trunk@210 5390a7c7-147a-4af0-8ec9-7488f05a26cb
-rwxr-xr-xMakefile8
-rwxr-xr-xwct4xxp-diag.c413
-rwxr-xr-xwct4xxp.c1628
-rwxr-xr-xwct4xxp.h37
4 files changed, 2085 insertions, 1 deletions
diff --git a/Makefile b/Makefile
index aaf77e5..b3de148 100755
--- a/Makefile
+++ b/Makefile
@@ -87,7 +87,7 @@ DEVFS=$(shell ps ax | grep -v grep | grep devfsd)
TZOBJS=zonedata.lo tonezone.lo
LIBTONEZONE=libtonezone.so.1.0
MODULES=zaptel.o tor2.o torisa.o wcusb.o wcfxo.o wcfxs.o \
- ztdynamic.o ztd-eth.o wct1xxp.o # ztdummy.o
+ ztdynamic.o ztd-eth.o wct1xxp.o wct4xxp.o # ztdummy.o
#MODULES+=wcfxsusb.o
ZTTOOL=$(shell if [ -f /usr/include/newt.h ]; then echo zttool; fi)
#PRIMARY=wcfxsusb
@@ -121,6 +121,9 @@ wcfxo.o: wcfxo.c zaptel.h
wct1xxp.o:wct1xxp.c zaptel.h
gcc $(KFLAGS) -c wct1xxp.c
+wct4xxp.o:wct4xxp.c zaptel.h
+ gcc $(KFLAGS) -c wct4xxp.c
+
wcfxs.o:wcfxs.c zaptel.h
gcc $(KFLAGS) -c wcfxs.c
@@ -284,6 +287,9 @@ install: all devices
if ! grep "post-install wct1xxp" $(MODCONF); then \
echo "post-install wct1xxp /sbin/ztcfg" >> $(MODCONF); \
fi
+ if ! grep "post-install wct4xxp" $(MODCONF); then \
+ echo "post-install wct4xxp /sbin/ztcfg" >> $(MODCONF); \
+ fi
if [ -d /etc/modutils ]; then \
/sbin/update-modules ; \
fi
diff --git a/wct4xxp-diag.c b/wct4xxp-diag.c
new file mode 100755
index 0000000..7c174bf
--- /dev/null
+++ b/wct4xxp-diag.c
@@ -0,0 +1,413 @@
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <string.h>
+#include "zaptel.h"
+#include "wct4xxp.h"
+
+static struct t4_reg_def {
+ int reg;
+ char *name;
+ int global;
+};
+static struct t4_reg_def xreginfo[] = {
+ { 0x00, "RDADDR" },
+ { 0x01, "WRADDR" },
+ { 0x02, "COUNT" },
+ { 0x03, "DMACTRL" },
+ { 0x04, "WCINTR" },
+ { 0x06, "VERSION" },
+ { 0x07, "LEDS" },
+ { 0x08, "GPIOCTL" },
+ { 0x09, "GPIO" },
+ { 0x0A, "LADDR" },
+ { 0x0b, "LDATA" },
+};
+
+static struct t4_reg_def reginfo[] = {
+ { 0x00, "XFIFO" },
+ { 0x01, "XFIFO" },
+ { 0x02, "CMDR" },
+ { 0x03, "MODE" },
+ { 0x04, "RAH1" },
+ { 0x05, "RAH2" },
+ { 0x06, "RAL1" },
+ { 0x07, "RAL2" },
+ { 0x08, "IPC", 1 },
+ { 0x09, "CCR1" },
+ { 0x0a, "CCR2" },
+ { 0x0c, "RTR1" },
+ { 0x0d, "RTR2" },
+ { 0x0e, "RTR3" },
+ { 0x0f, "RTR4" },
+ { 0x10, "TTR1" },
+ { 0x11, "TTR2" },
+ { 0x12, "TTR3" },
+ { 0x13, "TTR4" },
+ { 0x14, "IMR0" },
+ { 0x15, "IMR1" },
+ { 0x16, "IMR2" },
+ { 0x17, "IMR3" },
+ { 0x18, "IMR4" },
+ { 0x1b, "IERR" },
+ { 0x1c, "FMR0" },
+ { 0x1d, "FMR1" },
+ { 0x1e, "FMR2" },
+ { 0x1f, "LOOP" },
+ { 0x20, "XSW" },
+ { 0x21, "XSP" },
+ { 0x22, "XC0" },
+ { 0x23, "XC1" },
+ { 0x24, "RC0" },
+ { 0x25, "RC1" },
+ { 0x26, "XPM0" },
+ { 0x27, "XPM1" },
+ { 0x28, "XPM2" },
+ { 0x29, "TSWM" },
+ { 0x2b, "IDLE" },
+ { 0x2c, "XSA4" },
+ { 0x2d, "XSA5" },
+ { 0x2e, "XSA6" },
+ { 0x2f, "XSA7" },
+ { 0x30, "XSA8" },
+ { 0x31, "FMR3" },
+ { 0x32, "ICB1" },
+ { 0x33, "ICB2" },
+ { 0x34, "ICB3" },
+ { 0x35, "ICB4" },
+ { 0x36, "LIM0" },
+ { 0x37, "LIM1" },
+ { 0x38, "PCD" },
+ { 0x39, "PCR" },
+ { 0x3a, "LIM2" },
+ { 0x3b, "LCR1" },
+ { 0x3c, "LCR2" },
+ { 0x3d, "LCR3" },
+ { 0x3e, "SIC1" },
+ { 0x3f, "SIC2" },
+ { 0x40, "SIC3" },
+ { 0x44, "CMR1" },
+ { 0x45, "CMR2" },
+ { 0x46, "GCR" },
+ { 0x47, "ESM" },
+ { 0x60, "DEC" },
+ { 0x70, "XS1" },
+ { 0x71, "XS2" },
+ { 0x72, "XS3" },
+ { 0x73, "XS4" },
+ { 0x74, "XS5" },
+ { 0x75, "XS6" },
+ { 0x76, "XS7" },
+ { 0x77, "XS8" },
+ { 0x78, "XS9" },
+ { 0x79, "XS10" },
+ { 0x7a, "XS11" },
+ { 0x7b, "XS12" },
+ { 0x7c, "XS13" },
+ { 0x7d, "XS14" },
+ { 0x7e, "XS15" },
+ { 0x7f, "XS16" },
+ { 0x80, "PC1" },
+ { 0x81, "PC2" },
+ { 0x82, "PC3" },
+ { 0x83, "PC4" },
+ { 0x84, "PC5" },
+ { 0x85, "GPC1", 1 },
+ { 0x87, "CMDR2" },
+ { 0x8d, "CCR5" },
+ { 0x92, "GCM1", 1 },
+ { 0x93, "GCM2", 1 },
+ { 0x94, "GCM3", 1 },
+ { 0x95, "GCM4", 1 },
+ { 0x96, "GCM5", 1 },
+ { 0x97, "GCM6", 1 },
+ { 0x98, "GCM7", 1 },
+ { 0x99, "GCM8", 1 },
+ { 0xa0, "TSEO" },
+ { 0xa1, "TSBS1" },
+ { 0xa8, "TPC0" },
+};
+
+static struct t4_reg_def t1_reginfo[] = {
+ { 0x00, "XFIFO" },
+ { 0x01, "XFIFO" },
+ { 0x02, "CMDR" },
+ { 0x03, "MODE" },
+ { 0x04, "RAH1" },
+ { 0x05, "RAH2" },
+ { 0x06, "RAL1" },
+ { 0x07, "RAL2" },
+ { 0x08, "IPC", 1 },
+ { 0x09, "CCR1" },
+ { 0x0a, "CCR2" },
+ { 0x0c, "RTR1" },
+ { 0x0d, "RTR2" },
+ { 0x0e, "RTR3" },
+ { 0x0f, "RTR4" },
+ { 0x10, "TTR1" },
+ { 0x11, "TTR2" },
+ { 0x12, "TTR3" },
+ { 0x13, "TTR4" },
+ { 0x14, "IMR0" },
+ { 0x15, "IMR1" },
+ { 0x16, "IMR2" },
+ { 0x17, "IMR3" },
+ { 0x18, "IMR4" },
+ { 0x1b, "IERR" },
+ { 0x1c, "FMR0" },
+ { 0x1d, "FMR1" },
+ { 0x1e, "FMR2" },
+ { 0x1f, "LOOP" },
+ { 0x20, "FMR4" },
+ { 0x21, "FMR5" },
+ { 0x22, "XC0" },
+ { 0x23, "XC1" },
+ { 0x24, "RC0" },
+ { 0x25, "RC1" },
+ { 0x26, "XPM0" },
+ { 0x27, "XPM1" },
+ { 0x28, "XPM2" },
+ { 0x2b, "IDLE" },
+ { 0x2c, "XDL1" },
+ { 0x2d, "XDL2" },
+ { 0x2e, "XDL3" },
+ { 0x2f, "CCB1" },
+ { 0x30, "CCB2" },
+ { 0x31, "CCB3" },
+ { 0x32, "ICB1" },
+ { 0x33, "ICB2" },
+ { 0x34, "ICB3" },
+ { 0x36, "LIM0" },
+ { 0x37, "LIM1" },
+ { 0x38, "PCD" },
+ { 0x39, "PCR" },
+ { 0x3a, "LIM2" },
+ { 0x3b, "LCR1" },
+ { 0x3c, "LCR2" },
+ { 0x3d, "LCR3" },
+ { 0x3e, "SIC1" },
+ { 0x3f, "SIC2" },
+ { 0x40, "SIC3" },
+ { 0x44, "CMR1" },
+ { 0x45, "CMR2" },
+ { 0x46, "GCR" },
+ { 0x47, "ESM" },
+ { 0x60, "DEC" },
+ { 0x70, "XS1" },
+ { 0x71, "XS2" },
+ { 0x72, "XS3" },
+ { 0x73, "XS4" },
+ { 0x74, "XS5" },
+ { 0x75, "XS6" },
+ { 0x76, "XS7" },
+ { 0x77, "XS8" },
+ { 0x78, "XS9" },
+ { 0x79, "XS10" },
+ { 0x7a, "XS11" },
+ { 0x7b, "XS12" },
+ { 0x80, "PC1" },
+ { 0x81, "PC2" },
+ { 0x82, "PC3" },
+ { 0x83, "PC4" },
+ { 0x84, "PC5" },
+ { 0x85, "GPC1", 1 },
+ { 0x87, "CMDR2" },
+ { 0x8d, "CCR5" },
+ { 0x92, "GCM1", 1 },
+ { 0x93, "GCM2", 1 },
+ { 0x94, "GCM3", 1 },
+ { 0x95, "GCM4", 1 },
+ { 0x96, "GCM5", 1 },
+ { 0x97, "GCM6", 1 },
+ { 0x98, "GCM7", 1 },
+ { 0x99, "GCM8", 1 },
+ { 0xa0, "TSEO" },
+ { 0xa1, "TSBS1" },
+ { 0xa8, "TPC0" },
+};
+
+static struct t4_reg_def t1_sreginfo[] = {
+ { 0x00, "RFIFO" },
+ { 0x01, "RFIFO" },
+ { 0x49, "RBD" },
+ { 0x4a, "VSTR", 1 },
+ { 0x4b, "RES" },
+ { 0x4c, "FRS0" },
+ { 0x4d, "FRS1" },
+ { 0x4e, "FRS2" },
+ { 0x50, "FECL" },
+ { 0x51, "FECH" },
+ { 0x52, "CVCL" },
+ { 0x53, "CVCH" },
+ { 0x54, "CECL" },
+ { 0x55, "CECH" },
+ { 0x56, "EBCL" },
+ { 0x57, "EBCH" },
+ { 0x58, "BECL" },
+ { 0x59, "BECH" },
+ { 0x5a, "COEC" },
+ { 0x5c, "RDL1" },
+ { 0x5d, "RDL2" },
+ { 0x5e, "RDL3" },
+ { 0x62, "RSP1" },
+ { 0x63, "RSP2" },
+ { 0x64, "SIS" },
+ { 0x65, "RSIS" },
+ { 0x66, "RBCL" },
+ { 0x67, "RBCH" },
+ { 0x68, "ISR0" },
+ { 0x69, "ISR1" },
+ { 0x6a, "ISR2" },
+ { 0x6b, "ISR3" },
+ { 0x6c, "ISR4" },
+ { 0x6e, "GIS" },
+ { 0x6f, "CIS", 1 },
+ { 0x70, "RS1" },
+ { 0x71, "RS2" },
+ { 0x72, "RS3" },
+ { 0x73, "RS4" },
+ { 0x74, "RS5" },
+ { 0x75, "RS6" },
+ { 0x76, "RS7" },
+ { 0x77, "RS8" },
+ { 0x78, "RS9" },
+ { 0x79, "RS10" },
+ { 0x7a, "RS11" },
+ { 0x7b, "RS12" },
+};
+
+static struct t4_reg_def sreginfo[] = {
+ { 0x00, "RFIFO" },
+ { 0x01, "RFIFO" },
+ { 0x49, "RBD" },
+ { 0x4a, "VSTR", 1 },
+ { 0x4b, "RES" },
+ { 0x4c, "FRS0" },
+ { 0x4d, "FRS1" },
+ { 0x4e, "RSW" },
+ { 0x4f, "RSP" },
+ { 0x50, "FECL" },
+ { 0x51, "FECH" },
+ { 0x52, "CVCL" },
+ { 0x53, "CVCH" },
+ { 0x54, "CEC1L" },
+ { 0x55, "CEC1H" },
+ { 0x56, "EBCL" },
+ { 0x57, "EBCH" },
+ { 0x58, "CEC2L" },
+ { 0x59, "CEC2H" },
+ { 0x5a, "CEC3L" },
+ { 0x5b, "CEC3H" },
+ { 0x5c, "RSA4" },
+ { 0x5d, "RSA5" },
+ { 0x5e, "RSA6" },
+ { 0x5f, "RSA7" },
+ { 0x60, "RSA8" },
+ { 0x61, "RSA6S" },
+ { 0x62, "RSP1" },
+ { 0x63, "RSP2" },
+ { 0x64, "SIS" },
+ { 0x65, "RSIS" },
+ { 0x66, "RBCL" },
+ { 0x67, "RBCH" },
+ { 0x68, "ISR0" },
+ { 0x69, "ISR1" },
+ { 0x6a, "ISR2" },
+ { 0x6b, "ISR3" },
+ { 0x6c, "ISR4" },
+ { 0x6e, "GIS" },
+ { 0x6f, "CIS", 1 },
+ { 0x70, "RS1" },
+ { 0x71, "RS2" },
+ { 0x72, "RS3" },
+ { 0x73, "RS4" },
+ { 0x74, "RS5" },
+ { 0x75, "RS6" },
+ { 0x76, "RS7" },
+ { 0x77, "RS8" },
+ { 0x78, "RS9" },
+ { 0x79, "RS10" },
+ { 0x7a, "RS11" },
+ { 0x7b, "RS12" },
+ { 0x7c, "RS13" },
+ { 0x7d, "RS14" },
+ { 0x7e, "RS15" },
+ { 0x7f, "RS16" },
+};
+
+static char *tobin(int x)
+{
+ static char s[9] = "";
+ int y,z=0;
+ for (y=7;y>=0;y--) {
+ if (x & (1 << y))
+ s[z++] = '1';
+ else
+ s[z++] = '0';
+ }
+ s[z] = '\0';
+ return s;
+}
+
+static char *tobin32(unsigned int x)
+{
+ static char s[33] = "";
+ int y,z=0;
+ for (y=31;y>=0;y--) {
+ if (x & (1 << y))
+ s[z++] = '1';
+ else
+ s[z++] = '0';
+ }
+ s[z] = '\0';
+ return s;
+}
+
+int main(int argc, char *argv[])
+{
+ int fd;
+ int x;
+ char fn[256];
+ struct t4_regs regs;
+ if ((argc < 2) || ((*(argv[1]) != '/') && !atoi(argv[1]))) {
+ fprintf(stderr, "Usage: wct4xxp-diag <channel>\n");
+ exit(1);
+ }
+ if (*(argv[1]) == '/')
+ strncpy(fn, argv[1], sizeof(fn) - 1);
+ else
+ snprintf(fn, sizeof(fn), "/dev/zap/%d", atoi(argv[1]));
+ fd = open(fn, O_RDWR);
+ if (fd <0) {
+ fprintf(stderr, "Unable to open '%s': %s\n", fn, strerror(errno));
+ exit(1);
+ }
+ if (ioctl(fd, WCT4_GET_REGS, &regs)) {
+ fprintf(stderr, "Unable to get registers: %s\n", strerror(errno));
+ exit(1);
+ }
+ printf("PCI Registers:\n");
+ for (x=0;x<sizeof(xreginfo) / sizeof(xreginfo[0]);x++) {
+ fprintf(stdout, "%s (%02x): %08x (%s)\n", xreginfo[x].name, xreginfo[x].reg, regs.pci[xreginfo[x].reg], tobin32(regs.pci[xreginfo[x].reg]));
+ }
+ printf("\nE1 Control Registers:\n");
+ for (x=0;x<sizeof(reginfo) / sizeof(reginfo[0]);x++) {
+ fprintf(stdout, "%s (%02x): %02x (%s)\n", reginfo[x].name, reginfo[x].reg, regs.regs[reginfo[x].reg], tobin(regs.regs[reginfo[x].reg]));
+ }
+ printf("\nE1 Status Registers:\n");
+ for (x=0;x<sizeof(sreginfo) / sizeof(sreginfo[0]);x++) {
+ fprintf(stdout, "%s (%02x): %02x (%s)\n", sreginfo[x].name, sreginfo[x].reg, regs.regs[sreginfo[x].reg], tobin(regs.regs[sreginfo[x].reg]));
+ }
+ printf("\nT1 Control Registers:\n");
+ for (x=0;x<sizeof(t1_reginfo) / sizeof(t1_reginfo[0]);x++) {
+ fprintf(stdout, "%s (%02x): %02x (%s)\n", t1_reginfo[x].name, t1_reginfo[x].reg, regs.regs[t1_reginfo[x].reg], tobin(regs.regs[t1_reginfo[x].reg]));
+ }
+ printf("\nT1 Status Registers:\n");
+ for (x=0;x<sizeof(t1_sreginfo) / sizeof(t1_sreginfo[0]);x++) {
+ fprintf(stdout, "%s (%02x): %02x (%s)\n", t1_sreginfo[x].name, t1_sreginfo[x].reg, regs.regs[t1_sreginfo[x].reg], tobin(regs.regs[t1_sreginfo[x].reg]));
+ }
+ exit(0);
+}
diff --git a/wct4xxp.c b/wct4xxp.c
new file mode 100755
index 0000000..21c8225
--- /dev/null
+++ b/wct4xxp.c
@@ -0,0 +1,1628 @@
+/*
+ * TE410P Quad-T1/E1 PCI Driver version 0.1, 12/16/02
+ *
+ * Written by Mark Spencer <markster@linux-support.net>
+ * 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.
+ *
+ * All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#ifdef STANDALONE_ZAPATA
+#include "zaptel.h"
+#else
+#include <linux/zaptel.h>
+#endif
+#include "wct4xxp.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.
+ */
+
+/* #define ENABLE_TASKLETS */
+
+/* Define to get more attention-grabbing but slightly more I/O using
+ alarm status */
+#define FANCY_ALARM
+
+static int debug;
+static int timingcable;
+static int highestorder;
+static int t1e1override = -1;
+
+#ifdef FANCY_ALARM
+static int altab[] = {
+0, 0, 0, 1, 2, 3, 4, 6, 8, 9, 11, 13, 16, 18, 20, 22, 24, 25, 27, 28, 29, 30, 31, 31, 32, 31, 31, 30, 29, 28, 27, 25, 23, 22, 20, 18, 16, 13, 11, 9, 8, 6, 4, 3, 2, 1, 0, 0,
+};
+#endif
+
+#define MAX_SPANS 16
+
+#define FLAG_STARTED (1 << 0)
+
+#define TYPE_T1 1 /* is a T1 card */
+#define TYPE_E1 2 /* is an E1 card */
+
+#define CANARY 0xc0de
+
+static int inirq = 0;
+
+struct t4 {
+ /* This structure exists one per card */
+ struct pci_dev *dev; /* Pointer to PCI device */
+ int intcount;
+ int num; /* Which card we are */
+ int t1e1; /* T1/E1 select pins */
+ int globalconfig; /* Whether global setup has been done */
+ int last; /* XXX DEBUG area XXX */
+ int olast; /* XXX DEBUG area XXX */
+ int syncsrc; /* active sync source */
+ int syncs[4]; /* sync sources */
+ int psyncs[4]; /* span-relative sync sources */
+ int alarmtimer[4]; /* Alarm timer */
+ int blinktimer;
+#ifdef FANCY_ALARM
+ int alarmpos;
+#endif
+ char *type; /* Type of tormenta 2 card */
+ int irq; /* IRQ used by device */
+ int order; /* Order */
+ int flags; /* Device flags */
+ int syncpos[4]; /* span-relative sync sources */
+ int master; /* Are we master */
+ int ledreg; /* LED Register */
+ 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 */
+ struct zt_span spans[4]; /* Spans */
+ struct zt_chan *chans[4]; /* Pointers to blocks of 24(30/31) contiguous zt_chans for each span */
+ unsigned char txsigs[4][16]; /* Copy of tx sig registers */
+ int loopupcnt[4]; /* loop up code counter */
+ int loopdowncnt[4]; /* loop down code counter */
+ int spansstarted; /* number of spans started */
+ /* spinlock_t lock; */ /* lock context */
+ spinlock_t reglock; /* lock register access */
+ unsigned char ec_chunk1[4][31][ZT_CHUNKSIZE]; /* first EC chunk buffer */
+ unsigned char ec_chunk2[4][31][ZT_CHUNKSIZE]; /* second EC chunk buffer */
+ volatile unsigned int *writechunk; /* Double-word aligned write memory */
+ volatile unsigned int *readchunk; /* Double-word aligned read memory */
+ unsigned short canary;
+#ifdef ENABLE_TASKLETS
+ int taskletrun;
+ int taskletsched;
+ int taskletpending;
+ int taskletexec;
+ int txerrors;
+ struct tasklet_struct tor2_tlet;
+#endif
+ int spantype[4]; /* card type, T1 or E1 */
+ unsigned int passno; /* number of interrupt passes */
+ char *variety;
+};
+
+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);
+static int t4_rbsbits(struct zt_chan *chan, int bits);
+static int t4_maint(struct zt_span *span, int cmd);
+static int t4_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data);
+
+#define WC_RDADDR 0
+#define WC_WRADDR 1
+#define WC_COUNT 2
+#define WC_DMACTRL 3
+#define WC_INTR 4
+/* #define WC_GPIO 5 */
+#define WC_VERSION 6
+#define WC_LEDS 7
+#define WC_ACTIVATE (1 << 12)
+#define WC_GPIOCTL 8
+#define WC_GPIO 9
+#define WC_LADDR 10
+#define WC_LDATA 11
+#define WC_LREAD (1 << 15)
+#define WC_LWRITE (1 << 16)
+
+#define WC_OFF (0)
+#define WC_RED (1)
+#define WC_GREEN (2)
+#define WC_YELLOW (3)
+
+#define MAX_T4_CARDS 64
+
+#ifdef ENABLE_TASKLETS
+static void t4_tasklet(unsigned long data);
+#endif
+
+static struct t4 *cards[MAX_T4_CARDS];
+
+static inline void __t4_pci_out(struct t4 *wc, const unsigned int addr, const unsigned int value)
+{
+ unsigned int tmp;
+ wc->membase[addr] = value;
+#if 1
+ tmp = wc->membase[addr];
+ if ((value != tmp) && (addr != WC_LEDS) && (addr != WC_LDATA) &&
+ (addr != WC_GPIO))
+ printk("Tried to load %08x into %08x, but got %08x instead\n", value, addr, tmp);
+#endif
+}
+
+static inline unsigned int __t4_pci_in(struct t4 *wc, const unsigned int addr)
+{
+ return wc->membase[addr];
+}
+
+static inline void t4_pci_out(struct t4 *wc, const unsigned int addr, const unsigned int value)
+{
+ long flags;
+ spin_lock_irqsave(&wc->reglock, flags);
+ __t4_pci_out(wc, addr, value);
+ spin_unlock_irqrestore(&wc->reglock, flags);
+}
+
+static inline void __t4_set_led(struct t4 *wc, int span, int color)
+{
+ int oldreg = wc->ledreg;
+ wc->ledreg &= ~(0x3 << (span << 1));
+ wc->ledreg |= (color << (span << 1));
+ if (oldreg != wc->ledreg)
+ __t4_pci_out(wc, WC_LEDS, wc->ledreg);
+}
+
+static inline void t4_activate(struct t4 *wc)
+{
+ wc->ledreg |= WC_ACTIVATE;
+ t4_pci_out(wc, WC_LEDS, wc->ledreg);
+}
+
+static inline unsigned int t4_pci_in(struct t4 *wc, const unsigned int addr)
+{
+ unsigned int ret;
+ long flags;
+
+ spin_lock_irqsave(&wc->reglock, flags);
+ ret = __t4_pci_in(wc, addr);
+ spin_unlock_irqrestore(&wc->reglock, flags);
+ return ret;
+}
+
+static inline unsigned int __t4_framer_in(struct t4 *wc, int unit, const unsigned int addr)
+{
+ unsigned int ret;
+ unit &= 0x3;
+ __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff));
+ __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff) | ( 1 << 10) | WC_LREAD);
+ ret = __t4_pci_in(wc, WC_LDATA);
+ __t4_pci_out(wc, WC_LADDR, 0);
+ return ret & 0xff;
+}
+
+static inline unsigned int t4_framer_in(struct t4 *wc, int unit, const unsigned int addr)
+{
+ unsigned long flags;
+ unsigned int ret;
+ spin_lock_irqsave(&wc->reglock, flags);
+ ret = __t4_framer_in(wc, unit, addr);
+ spin_unlock_irqrestore(&wc->reglock, flags);
+ return ret;
+
+}
+
+static inline void __t4_framer_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value)
+{
+ unit &= 0x3;
+ if (debug)
+ 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) | (1 << 10));
+ __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff) | (1 << 10) | WC_LWRITE);
+ __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 0
+ { unsigned int tmp;
+ tmp = t4_framer_in(wc, unit, addr);
+ if (tmp != value) {
+ printk("Expected %d from unit %d register %d but got %d instead\n", value, unit, addr, tmp);
+ } }
+#endif
+}
+
+static inline void t4_framer_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&wc->reglock, flags);
+ __t4_framer_out(wc, unit, addr, value);
+ spin_unlock_irqrestore(&wc->reglock, flags);
+}
+
+
+static void __set_clear(struct t4 *wc, int span)
+{
+ int i,j;
+ unsigned short val=0;
+ for (i=0;i<24;i++) {
+ j = (i/8);
+ if (wc->spans[span].chans[i].flags & ZT_FLAG_CLEAR)
+ val |= 1 << (7 - (i % 8));
+ if ((i % 8)==7) {
+ if (debug)
+ printk("Putting %d in register %02x on span %d\n",
+ val, 0x2f + j, span + 1);
+ __t4_framer_out(wc,span, 0x2f + j, val);
+ val = 0;
+ }
+ }
+}
+
+static void set_clear(struct t4 *wc, int span)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&wc->reglock, flags);
+ __set_clear(wc, span);
+ spin_unlock_irqrestore(&wc->reglock, flags);
+}
+
+static int t4_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data)
+{
+ struct t4_regs regs;
+ int x;
+ switch(cmd) {
+ case WCT4_GET_REGS:
+ for (x=0;x<NUM_PCI;x++)
+ regs.pci[x] = t4_pci_in(chan->pvt, x);
+ for (x=0;x<NUM_REGS;x++)
+ regs.regs[x] = t4_framer_in(chan->pvt, chan->span->offset, x);
+ if (copy_to_user((struct t4_regs *)data, &regs, sizeof(regs)))
+ return -EFAULT;
+ break;
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+}
+
+static int t4_maint(struct zt_span *span, int cmd)
+{
+ struct t4 *wc = span->pvt;
+
+ if (wc->spantype[span->offset] == TYPE_E1) {
+ switch(cmd) {
+ case ZT_MAINT_NONE:
+ printk("XXX Turn off local and remote loops E1 XXX\n");
+ break;
+ case ZT_MAINT_LOCALLOOP:
+ printk("XXX Turn on local loopback E1 XXX\n");
+ break;
+ case ZT_MAINT_REMOTELOOP:
+ printk("XXX Turn on remote loopback E1 XXX\n");
+ break;
+ case ZT_MAINT_LOOPUP:
+ printk("XXX Send loopup code E1 XXX\n");
+ break;
+ case ZT_MAINT_LOOPDOWN:
+ printk("XXX Send loopdown code E1 XXX\n");
+ break;
+ case ZT_MAINT_LOOPSTOP:
+ printk("XXX Stop sending loop codes E1 XXX\n");
+ break;
+ default:
+ printk("TE410P: Unknown E1 maint command: %d\n", cmd);
+ break;
+ }
+ } else {
+ switch(cmd) {
+ case ZT_MAINT_NONE:
+ printk("XXX Turn off local and remote loops T1 XXX\n");
+ break;
+ case ZT_MAINT_LOCALLOOP:
+ printk("XXX Turn on local loop and no remote loop XXX\n");
+ break;
+ case ZT_MAINT_REMOTELOOP:
+ printk("XXX Turn on remote loopup XXX\n");
+ break;
+ case ZT_MAINT_LOOPUP:
+ t4_framer_out(wc, span->offset, 0x21, 0x50); /* FMR5: Nothing but RBS mode */
+ break;
+ case ZT_MAINT_LOOPDOWN:
+ t4_framer_out(wc, span->offset, 0x21, 0x60); /* FMR5: Nothing but RBS mode */
+ break;
+ case ZT_MAINT_LOOPSTOP:
+ t4_framer_out(wc, span->offset, 0x21, 0x40); /* FMR5: Nothing but RBS mode */
+ break;
+ default:
+ printk("TE410P: Unknown E1 maint command: %d\n", cmd);
+ break;
+ }
+ }
+ return 0;
+}
+
+static int t4_rbsbits(struct zt_chan *chan, int bits)
+{
+ u_char m,c;
+ int k,n,b;
+ struct t4 *wc = chan->pvt;
+ unsigned long flags;
+
+ spin_lock_irqsave(&wc->reglock, flags);
+ if(debug) printk("Setting bits to %d on channel %s\n", bits, chan->name);
+ k = chan->span->offset;
+ if (wc->spantype[k] == TYPE_E1) { /* do it E1 way */
+ if (chan->chanpos == 16) {
+ spin_unlock_irqrestore(&wc->reglock, flags);
+ return 0;
+ }
+ n = chan->chanpos - 1;
+ if (chan->chanpos > 15) n--;
+ b = (n % 15);
+ c = wc->txsigs[k][b];
+ m = (n / 15) << 2; /* nibble selector */
+ c &= (0xf << m); /* keep the other nibble */
+ c |= (bits & 0xf) << (4 - m); /* put our new nibble here */
+ wc->txsigs[k][b] = c;
+ /* output them to the chip */
+ __t4_framer_out(wc,k,0x71 + b,c);
+ } else if (wc->spans[k].lineconfig & ZT_CONFIG_D4) {
+ n = chan->chanpos - 1;
+ b = (n/4);
+ c = wc->txsigs[k][b];
+ m = ((3 - (n % 4)) << 1); /* nibble selector */
+ c &= ~(0x3 << m); /* keep the other nibble */
+ c |= ((bits >> 2) & 0x3) << m; /* put our new nibble here */
+ wc->txsigs[k][b] = c;
+ /* output them to the chip */
+ __t4_framer_out(wc,k,0x70 + b,c);
+ __t4_framer_out(wc,k,0x70 + b + 6,c);
+ } else if (wc->spans[k].lineconfig & ZT_CONFIG_ESF) {
+ n = chan->chanpos - 1;
+ b = (n/2);
+ c = wc->txsigs[k][b];
+ m = ((n % 2) << 2); /* nibble selector */
+ c &= (0xf << m); /* keep the other nibble */
+ c |= (bits & 0xf) << (4 - m); /* put our new nibble here */
+ wc->txsigs[k][b] = c;
+ /* output them to the chip */
+ __t4_framer_out(wc,k,0x70 + b,c);
+ }
+ if (debug)
+ printk("Finished setting RBS bits\n");
+ spin_unlock_irqrestore(&wc->reglock, flags);
+ return 0;
+}
+
+static int t4_shutdown(struct zt_span *span)
+{
+ int tspan;
+ int wasrunning;
+ unsigned long flags;
+ struct t4 *wc = span->pvt;
+
+ tspan = span->offset + 1;
+ if (tspan < 0) {
+ printk("T4XXP: Span '%d' isn't us?\n", span->spanno);
+ return -1;
+ }
+
+ spin_lock_irqsave(&wc->reglock, flags);
+ wasrunning = span->flags & ZT_FLAG_RUNNING;
+
+ span->flags &= ~ZT_FLAG_RUNNING;
+ if (wasrunning)
+ wc->spansstarted--;
+ __t4_set_led(wc, span->offset, WC_OFF);
+ if ((!(wc->spans[0].flags & ZT_FLAG_RUNNING)) &&
+ (!(wc->spans[1].flags & ZT_FLAG_RUNNING)) &&
+ (!(wc->spans[2].flags & ZT_FLAG_RUNNING)) &&
+ (!(wc->spans[3].flags & ZT_FLAG_RUNNING))) {
+ /* No longer in use, disable interrupts */
+ printk("TE410P: Disabling interrupts since there are no active spans\n");
+ wc->dmactrl = 0x0;
+ __t4_pci_out(wc, WC_DMACTRL, 0x00000000);
+ /* Acknowledge any pending interrupts */
+ __t4_pci_out(wc, WC_INTR, 0x00000000);
+ }
+ spin_unlock_irqrestore(&wc->reglock, flags);
+ if (debug)
+ printk("Span %d (%s) shutdown\n", span->spanno, span->name);
+ return 0;
+}
+
+static int t4_spanconfig(struct zt_span *span, struct zt_lineconfig *lc)
+{
+ int i;
+ struct t4 *wc = span->pvt;
+
+ if (debug)
+ printk("TE410P: 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 = wc->syncsrc;
+
+ /* remove this span number from the current sync sources, if there */
+ for(i = 0; i < 3; i++) {
+ if (wc->syncs[i] == span->spanno) {
+ wc->syncs[i] = 0;
+ wc->psyncs[i] = 0;
+ }
+ }
+ wc->syncpos[span->offset] = lc->sync;
+ /* if a sync src, put it in proper place */
+ if (lc->sync) {
+ wc->syncs[lc->sync - 1] = span->spanno;
+ wc->psyncs[lc->sync - 1] = span->offset + 1;
+ }
+ /* If we're already running, then go ahead and apply the changes */
+ if (span->flags & ZT_FLAG_RUNNING)
+ return t4_startup(span);
+ return 0;
+}
+
+static int t4_chanconfig(struct zt_chan *chan, int sigtype)
+{
+ int alreadyrunning;
+ unsigned long flags;
+ struct t4 *wc = chan->pvt;
+
+ alreadyrunning = wc->spans[chan->span->offset].flags & ZT_FLAG_RUNNING;
+ if (debug) {
+ if (alreadyrunning)
+ printk("TE410P: Reconfigured channel %d (%s) sigtype %d\n", chan->channo, chan->name, sigtype);
+ else
+ printk("TE410P: Configured channel %d (%s) sigtype %d\n", chan->channo, chan->name, sigtype);
+ }
+ /* nothing more to do if an E1 */
+ if (wc->spantype[chan->span->offset] == TYPE_E1) return 0;
+ spin_lock_irqsave(&wc->reglock, flags);
+ if (alreadyrunning)
+ __set_clear(wc, chan->span->offset);
+ spin_unlock_irqrestore(&wc->reglock, flags);
+ return 0;
+}
+
+static int t4_open(struct zt_chan *chan)
+{
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int t4_close(struct zt_chan *chan)
+{
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static void init_spans(struct t4 *wc)
+{
+ int x,y,c;
+ for (x=0;x<4;x++) {
+ sprintf(wc->spans[x].name, "TE4/%d/%d",
+ wc->num, x + 1);
+ sprintf(wc->spans[x].desc, "TE410P (PCI) Card %d Span %d", wc->num, x+1);
+ wc->spans[x].spanconfig = t4_spanconfig;
+ wc->spans[x].chanconfig = t4_chanconfig;
+ wc->spans[x].startup = t4_startup;
+ wc->spans[x].shutdown = t4_shutdown;
+ wc->spans[x].rbsbits = t4_rbsbits;
+ wc->spans[x].maint = t4_maint;
+ wc->spans[x].open = t4_open;
+ wc->spans[x].close = t4_close;
+ if (wc->spantype[x] == TYPE_T1) {
+ wc->spans[x].channels = 24;
+ wc->spans[x].deflaw = ZT_LAW_MULAW;
+ } else {
+ wc->spans[x].channels = 31;
+ wc->spans[x].deflaw = ZT_LAW_ALAW;
+ }
+ wc->spans[x].chans = wc->chans[x];
+ 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;
+ wc->spans[x].pvt = wc;
+ wc->spans[x].offset = x;
+ init_waitqueue_head(&wc->spans[x].maintq);
+ for (y=0;y<wc->spans[x].channels;y++) {
+ struct zt_chan *mychans = wc->chans[x] + y;
+ sprintf(mychans->name, "TE4/%d/%d/%d", wc->num, x + 1, y + 1);
+ mychans->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 | ZT_SIG_CAS;
+ c = (x * wc->spans[x].channels) + y;
+ mychans->pvt = wc;
+ mychans->chanpos = y + 1;
+ }
+ }
+}
+
+static void t4_serial_setup(struct t4 *wc, int unit)
+{
+ if (!wc->globalconfig) {
+ wc->globalconfig = 1;
+ printk("TE410P: Setting up global serial parameters\n");
+ t4_framer_out(wc, 0, 0x85, 0xe0); /* GPC1: Multiplex mode enabled, FSC is output, active low, RCLK from channel 0 */
+ t4_framer_out(wc, 0, 0x08, 0x01); /* IPC: Interrupt push/pull active low */
+
+ /* Global clocks (8.192 Mhz CLK) */
+ t4_framer_out(wc, 0, 0x92, 0x00);
+ t4_framer_out(wc, 0, 0x93, 0x18);
+ t4_framer_out(wc, 0, 0x94, 0xfb);
+ t4_framer_out(wc, 0, 0x95, 0x0b);
+ t4_framer_out(wc, 0, 0x96, 0x00);
+ t4_framer_out(wc, 0, 0x97, 0x0b);
+ t4_framer_out(wc, 0, 0x98, 0xdb);
+ t4_framer_out(wc, 0, 0x99, 0xdf);
+ }
+
+ /* Configure interrupts */
+ t4_framer_out(wc, unit, 0x46, 0x40); /* GCR: Interrupt on Activation/Deactivation of AIX, LOS */
+
+ /* 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, 0x40, 0x04); /* SIC3: Edges for capture */
+ t4_framer_out(wc, unit, 0x45, 0x00); /* CMR2: We provide sync and clock for tx and rx. */
+ 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 */
+ t4_framer_out(wc, unit, 0x25, 0x04); /* RC1: The rest of RC0 */
+
+ /* Configure ports */
+ t4_framer_out(wc, unit, 0x80, 0x00); /* PC1: SPYR/SPYX input on RPA/XPA */
+ t4_framer_out(wc, unit, 0x81, 0x22); /* PC2: RMFB/XSIG output/input on RPB/XPB */
+ 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)
+ printk("Successfully initialized serial bus for unit %d\n", unit);
+}
+
+static void t4_set_timing_source(struct t4 *wc, int unit)
+{
+ unsigned int timing;
+ int x;
+ timing = 0x34; /* CMR1: RCLK unit, 8.192 Mhz TCLK, RCLK is 8.192 Mhz */
+ if ((unit > -1) && (unit < 4)) {
+ timing |= (unit << 6);
+ } else {
+ timing |= 0x1; /* Use TCLK timing */
+ }
+ for (x=0;x<4;x++)
+ t4_framer_out(wc, x, 0x44, timing);
+ if ((unit > -1) && (unit < 4))
+ t4_pci_out(wc, WC_DMACTRL, wc->dmactrl | (1 << 29));
+ else
+ t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
+ printk("TE410P: Timing from source %d\n", unit);
+}
+
+static void __t4_configure_t1(struct t4 *wc, int unit, int lineconfig, int txlevel)
+{
+ unsigned int fmr4, fmr2, fmr1, fmr0, lim2;
+ char *framing, *line;
+ int mytxlevel;
+ if ((txlevel > 7) || (txlevel < 4))
+ mytxlevel = 0;
+ else
+ mytxlevel = txlevel - 4;
+ fmr1 = 0x9c; /* FMR1: Mode 1, T1 mode, CRC on for ESF, 8.192 Mhz system data rate, no XAIS */
+ fmr2 = 0x22; /* FMR2: no payload loopback, auto send yellow alarm */
+ fmr4 = 0x0c; /* FMR4: Lose sync on 2 out of 5 framing bits, auto resync */
+ lim2 = 0x21; /* LIM2: 50% peak is a "1", Advanced Loss recovery */
+ lim2 |= (mytxlevel << 6); /* LIM2: Add line buildout */
+ __t4_framer_out(wc, unit, 0x1d, fmr1);
+ __t4_framer_out(wc, unit, 0x1e, fmr2);
+
+ /* Configure line interface */
+ if (lineconfig & ZT_CONFIG_AMI) {
+ line = "AMI";
+ fmr0 = 0xa0;
+ } else {
+ line = "B8ZS";
+ fmr0 = 0xf0;
+ }
+ if (lineconfig & ZT_CONFIG_D4) {
+ framing = "D4";
+ } else {
+ framing = "ESF";
+ fmr4 |= 0x2;
+ fmr2 |= 0xc0;
+ }
+ __t4_framer_out(wc, unit, 0x1c, fmr0);
+ __t4_framer_out(wc, unit, 0x20, fmr4);
+ __t4_framer_out(wc, unit, 0x21, 0x40); /* FMR5: Enable RBS mode */
+
+ __t4_framer_out(wc, unit, 0x37, 0xf0 ); /* LIM1: Clear data in case of LOS, Set receiver threshold (0.5V), No remote loop, no DRS */
+ __t4_framer_out(wc, unit, 0x36, 0x08); /* LIM0: Enable auto long haul mode, no local loop (must be after LIM1) */
+
+ __t4_framer_out(wc, unit, 0x02, 0x50); /* CMDR: Reset the receiver and transmitter line interface */
+ __t4_framer_out(wc, unit, 0x02, 0x00); /* CMDR: Reset the receiver and transmitter line interface */
+
+ __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 */
+ __t4_framer_out(wc, unit, 0x26, 0xd7); /* XPM0 */
+ __t4_framer_out(wc, unit, 0x27, 0x22); /* XPM1 */
+ __t4_framer_out(wc, unit, 0x28, 0x01); /* XPM2 */
+ printk("TE410P: Span %d configured for %s/%s\n", unit + 1, framing, line);
+}
+
+static void __t4_configure_e1(struct t4 *wc, int unit, int lineconfig)
+{
+ unsigned int fmr2, fmr1, fmr0;
+ char *crc4 = "";
+ char *framing, *line;
+ fmr1 = 0x4c; /* FMR1: E1 mode, Automatic force resync, PCM30 mode, 8.192 Mhz backplane, no XAIS */
+ fmr2 = 0x03 /* | 0x4 */; /* FMR2: Auto transmit remote alarm, auto loss of multiframe recovery, no payload loopback */
+ if (lineconfig & ZT_CONFIG_CRC4) {
+ fmr1 |= 0x08; /* CRC4 transmit */
+ fmr2 |= 0x80; /* CRC4 receive */
+ crc4 = "/CRC4";
+ }
+ __t4_framer_out(wc, unit, 0x1d, fmr1);
+ __t4_framer_out(wc, unit, 0x1e, fmr2);
+
+ /* Configure line interface */
+ if (lineconfig & ZT_CONFIG_AMI) {
+ line = "AMI";
+ fmr0 = 0xa0;
+ } else {
+ line = "HDB3";
+ fmr0 = 0xf0;
+ }
+ if (lineconfig & ZT_CONFIG_CCS) {
+ framing = "CCS";
+ } else {
+ framing = "CAS";
+ }
+ __t4_framer_out(wc, unit, 0x1c, fmr0);
+
+ __t4_framer_out(wc, unit, 0x37, 0xf0 /*| 0x6 */ ); /* LIM1: Clear data in case of LOS, Set receiver threshold (0.5V), No remote loop, no DRS */
+ __t4_framer_out(wc, unit, 0x36, 0x08); /* LIM0: Enable auto long haul mode, no local loop (must be after LIM1) */
+
+ __t4_framer_out(wc, unit, 0x02, 0x50); /* CMDR: Reset the receiver and transmitter line interface */
+ __t4_framer_out(wc, unit, 0x02, 0x00); /* CMDR: Reset the receiver and transmitter line interface */
+
+ /* Condition receive line interface for E1 after reset */
+ __t4_framer_out(wc, unit, 0xbb, 0x17);
+ __t4_framer_out(wc, unit, 0xbc, 0x55);
+ __t4_framer_out(wc, unit, 0xbb, 0x97);
+ __t4_framer_out(wc, unit, 0xbb, 0x11);
+ __t4_framer_out(wc, unit, 0xbc, 0xaa);
+ __t4_framer_out(wc, unit, 0xbb, 0x91);
+ __t4_framer_out(wc, unit, 0xbb, 0x12);
+ __t4_framer_out(wc, unit, 0xbc, 0x55);
+ __t4_framer_out(wc, unit, 0xbb, 0x92);
+ __t4_framer_out(wc, unit, 0xbb, 0x0c);
+ __t4_framer_out(wc, unit, 0xbb, 0x00);
+ __t4_framer_out(wc, unit, 0xbb, 0x8c);
+
+ __t4_framer_out(wc, unit, 0x3a, 0x20); /* 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 */
+
+ __t4_framer_out(wc, unit, 0x20, 0x9f); /* XSW: Spare bits all to 1 */
+ __t4_framer_out(wc, unit, 0x21, 0x1c); /* XSP: E-bit set when async. AXS auto, XSIF to 1 */
+
+
+ /* Generate pulse mask for E1 */
+ __t4_framer_out(wc, unit, 0x26, 0x9c); /* XPM0 */
+ __t4_framer_out(wc, unit, 0x27, 0x03); /* XPM1 */
+ __t4_framer_out(wc, unit, 0x28, 0x00); /* XPM2 */
+ printk("TE410P: Span %d configured for %s/%s%s\n", unit + 1, framing, line, crc4);
+}
+
+static int t4_startup(struct zt_span *span)
+{
+ int i;
+ int tspan;
+ unsigned long flags;
+ int alreadyrunning;
+ struct t4 *wc = span->pvt;
+
+ tspan = span->offset + 1;
+ if (tspan < 0) {
+ printk("TE410P: Span '%d' isn't us?\n", span->spanno);
+ return -1;
+ }
+
+ spin_lock_irqsave(&wc->reglock, flags);
+
+ alreadyrunning = span->flags & ZT_FLAG_RUNNING;
+
+ /* initialize the start value for the entire chunk of last ec buffer */
+ for(i = 0; i < span->channels; i++)
+ {
+ memset(wc->ec_chunk1[span->offset][i],
+ ZT_LIN2X(0,&span->chans[i]),ZT_CHUNKSIZE);
+ memset(wc->ec_chunk2[span->offset][i],
+ ZT_LIN2X(0,&span->chans[i]),ZT_CHUNKSIZE);
+ }
+ /* Force re-evaluation fo timing source */
+ if (timingcable)
+ wc->syncsrc = -1;
+
+ if (wc->spantype[span->offset] == TYPE_E1) { /* if this is an E1 card */
+ __t4_configure_e1(wc, span->offset, span->lineconfig);
+ } else { /* is a T1 card */
+ __t4_configure_t1(wc, span->offset, span->lineconfig, span->txlevel);
+ __set_clear(wc, span->offset);
+ }
+
+ if (!alreadyrunning) {
+ span->flags |= ZT_FLAG_RUNNING;
+ wc->spansstarted++;
+ /* enable interrupts */
+ /* Start DMA, enabling DMA interrupts on read only */
+ wc->dmactrl = 0xc0000003 | (1 << 29);
+ __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
+ }
+
+ spin_unlock_irqrestore(&wc->reglock, flags);
+ if (wc->syncs[0] == span->spanno) printk("SPAN %d: Primary Sync Source\n",span->spanno);
+ 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);
+ return 0;
+}
+
+static int syncsrc = 0;
+static int syncnum = 0 /* -1 */;
+static int syncspan = 0;
+static spinlock_t synclock = SPIN_LOCK_UNLOCKED;
+
+static int t4_findsync(struct t4 *wc)
+{
+ int i;
+ int x;
+ long flags;
+ int p;
+ int nonzero;
+ int newsyncsrc = 0; /* Zaptel span number */
+ int newsyncnum = 0; /* tor2 card number */
+ int newsyncspan = 0; /* span on given tor2 card */
+ spin_lock_irqsave(&synclock, flags);
+#if 1
+ if (!wc->num) {
+ /* If we're the first card, go through all the motions, up to 8 levels
+ of sync source */
+ p = 1;
+ while(p < 8) {
+ nonzero = 0;
+ for (x=0;cards[x];x++) {
+ for (i=0;i<4;i++) {
+ if (cards[x]->syncpos[i]) {
+ nonzero = 1;
+ if ((cards[x]->syncpos[i] == p) &&
+ !(cards[x]->spans[i].alarms & (ZT_ALARM_RED | ZT_ALARM_BLUE | ZT_ALARM_LOOPBACK)) &&
+ (cards[x]->spans[i].flags & ZT_FLAG_RUNNING)) {
+ /* This makes a good sync source */
+ newsyncsrc = cards[x]->spans[i].spanno;
+ newsyncnum = x;
+ newsyncspan = i + 1;
+ /* Jump out */
+ goto found;
+ }
+ }
+ }
+ }
+ if (nonzero)
+ p++;
+ else
+ break;
+ }
+found:
+ if ((syncnum != newsyncnum) || (syncsrc != newsyncsrc) || (newsyncspan != syncspan)) {
+ syncnum = newsyncnum;
+ syncsrc = newsyncsrc;
+ syncspan = newsyncspan;
+ if (debug) printk("New syncnum: %d, syncsrc: %d, syncspan: %d\n", syncnum, syncsrc, syncspan);
+ }
+ }
+#endif
+ /* update sync src info */
+ if (wc->syncsrc != syncsrc) {
+ wc->syncsrc = syncsrc;
+ /* Update sync sources */
+ for(i = 0; i < 4; i++) {
+ wc->spans[i].syncsrc = wc->syncsrc;
+ }
+ if (syncnum == wc->num) {
+ printk("XXX Set sync span to %d XXX\n", syncspan);
+ if (debug) printk("Card %d, using sync span %d, master\n", wc->num, syncspan);
+ wc->master = 1;
+ } else {
+ printk("XXX Set timing from external cable XXX\n");
+ wc->master = 0;
+ if (debug) printk("Card %d, using Timing Bus, NOT master\n", wc->num);
+ }
+ }
+ spin_unlock_irqrestore(&synclock, flags);
+ return 0;
+}
+
+static void t4_receiveprep(struct t4 *wc, int irq)
+{
+ volatile unsigned int *readchunk;
+ static int last0 = 1;
+ int dbl = 0;
+ int x,y,z;
+ unsigned int tmp;
+ if (irq & 1) {
+ /* First part */
+ readchunk = wc->readchunk + 1;
+ if (!last0)
+ dbl = 1;
+ last0 = 0;
+ } else {
+ readchunk = wc->readchunk + ZT_CHUNKSIZE * 32 + 1;
+ if (last0)
+ dbl = 1;
+ last0 = 1;
+ }
+ if (dbl)
+ printk("TE410P: Double/missed interrupt detected\n");
+ for (x=0;x<ZT_CHUNKSIZE;x++) {
+ /* Once per chunk */
+ for (z=0;z<24;z++) {
+ /* All T1/E1 channels */
+ tmp = readchunk[z];
+ wc->spans[3].chans[z].readchunk[x] = tmp & 0xff;
+ wc->spans[2].chans[z].readchunk[x] = (tmp & 0xff00) >> 8;
+ wc->spans[1].chans[z].readchunk[x] = (tmp & 0xff0000) >> 16;
+ wc->spans[0].chans[z].readchunk[x] = tmp >> 24;
+ }
+ if (wc->t1e1) {
+ for (z=24;z<31;z++) {
+ /* Only E1 channels now */
+ tmp = readchunk[z];
+ if (wc->spans[3].channels > 24)
+ wc->spans[3].chans[z].readchunk[x] = tmp & 0xff;
+ if (wc->spans[2].channels > 24)
+ wc->spans[2].chans[z].readchunk[x] = (tmp & 0xff00) >> 8;
+ if (wc->spans[1].channels > 24)
+ wc->spans[1].chans[z].readchunk[x] = (tmp & 0xff0000) >> 16;
+ if (wc->spans[0].channels > 24)
+ wc->spans[0].chans[z].readchunk[x] = tmp >> 24;
+ }
+ }
+ /* Advance pointer by 4 TDM frame lengths */
+ readchunk += 32;
+ }
+ for (x=0;x<4;x++) {
+ if (wc->spans[x].flags & ZT_FLAG_RUNNING) {
+ for (y=0;y<wc->spans[x].channels;y++) {
+ /* Echo cancel double buffered data */
+ zt_ec_chunk(&wc->spans[x].chans[y],
+ wc->spans[x].chans[y].readchunk,
+ wc->ec_chunk2[x][y]);
+ memcpy(wc->ec_chunk2[x][y],wc->ec_chunk1[x][y],
+ ZT_CHUNKSIZE);
+ memcpy(wc->ec_chunk1[x][y],
+ wc->spans[x].chans[y].writechunk,
+ ZT_CHUNKSIZE);
+ }
+ zt_receive(&wc->spans[x]);
+ }
+ }
+}
+
+static void t4_transmitprep(struct t4 *wc, int irq)
+{
+ volatile unsigned int *writechunk;
+ int x,y,z;
+ unsigned int tmp;
+ if (irq & 1) {
+ /* First part */
+ writechunk = wc->writechunk + 1;
+ } else {
+ writechunk = wc->writechunk + ZT_CHUNKSIZE * 32 + 1;
+ }
+ for (y=0;y<4;y++) {
+ if (wc->spans[y].flags & ZT_FLAG_RUNNING)
+ zt_transmit(&wc->spans[y]);
+ }
+
+ for (x=0;x<ZT_CHUNKSIZE;x++) {
+ /* Once per chunk */
+ for (z=0;z<24;z++) {
+ /* All T1/E1 channels */
+ tmp = (wc->spans[3].chans[z].writechunk[x]) |
+ (wc->spans[2].chans[z].writechunk[x] << 8) |
+ (wc->spans[1].chans[z].writechunk[x] << 16) |
+ (wc->spans[0].chans[z].writechunk[x] << 24);
+ writechunk[z] = tmp;
+ }
+ if (wc->t1e1) {
+ for (z=24;z<31;z++) {
+ /* Only E1 channels now */
+ tmp = 0;
+ if (wc->spans[3].channels > 24)
+ tmp |= wc->spans[3].chans[z].writechunk[x];
+ if (wc->spans[2].channels > 24)
+ tmp |= (wc->spans[2].chans[z].writechunk[x] << 8);
+ if (wc->spans[1].channels > 24)
+ tmp |= (wc->spans[1].chans[z].writechunk[x] << 16);
+ if (wc->spans[0].channels > 24)
+ tmp |= (wc->spans[0].chans[z].writechunk[x] << 24);
+ writechunk[z] = tmp;
+ }
+ }
+ /* Advance pointer by 4 TDM frame lengths */
+ writechunk += 32;
+ }
+
+}
+
+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) {
+ for (i = 0; i < 15; i++) {
+ a = __t4_framer_in(wc, span, 0x71 + i);
+ /* Get high channel in low bits */
+ rxs = (a & 0xf);
+ if (!(wc->spans[span].chans[i+16].sig & ZT_SIG_CLEAR)) {
+ if (wc->spans[span].chans[i+16].rxsig != rxs)
+ zt_rbsbits(&wc->spans[span].chans[i+16], rxs);
+ }
+ rxs = (a >> 4) & 0xf;
+ if (!(wc->spans[span].chans[i].sig & ZT_SIG_CLEAR)) {
+ if (wc->spans[span].chans[i].rxsig != rxs)
+ zt_rbsbits(&wc->spans[span].chans[i], rxs);
+ }
+ }
+ } else if (wc->spans[span].lineconfig & ZT_CONFIG_D4) {
+ for (i = 0; i < 24; i+=4) {
+ a = __t4_framer_in(wc, span, 0x70 + (i>>2));
+ /* Get high channel in low bits */
+ rxs = (a & 0x3) << 2;
+ if (!(wc->spans[span].chans[i+3].sig & ZT_SIG_CLEAR)) {
+ if (wc->spans[span].chans[i+3].rxsig != rxs)
+ zt_rbsbits(&wc->spans[span].chans[i+3], rxs);
+ }
+ rxs = (a & 0xc);
+ if (!(wc->spans[span].chans[i+2].sig & ZT_SIG_CLEAR)) {
+ if (wc->spans[span].chans[i+2].rxsig != rxs)
+ zt_rbsbits(&wc->spans[span].chans[i+2], rxs);
+ }
+ rxs = (a >> 2) & 0xc;
+ if (!(wc->spans[span].chans[i+1].sig & ZT_SIG_CLEAR)) {
+ if (wc->spans[span].chans[i+1].rxsig != rxs)
+ zt_rbsbits(&wc->spans[span].chans[i+1], rxs);
+ }
+ rxs = (a >> 4) & 0xc;
+ if (!(wc->spans[span].chans[i].sig & ZT_SIG_CLEAR)) {
+ if (wc->spans[span].chans[i].rxsig != rxs)
+ zt_rbsbits(&wc->spans[span].chans[i], rxs);
+ }
+ }
+ } else {
+ for (i = 0; i < 24; i+=2) {
+ a = __t4_framer_in(wc, span, 0x70 + (i>>1));
+ /* 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)
+ 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)
+ 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]);
+ }
+ }
+ }
+}
+
+static void __t4_check_alarms(struct t4 *wc, int span)
+{
+ unsigned char c,d;
+ int alarms;
+ int x,j;
+
+ if (!(wc->spans[span].flags & ZT_FLAG_RUNNING))
+ return;
+
+ c = __t4_framer_in(wc, span, 0x4c);
+ d = __t4_framer_in(wc, span, 0x4d);
+
+ /* Assume no alarms */
+ alarms = 0;
+
+ /* And consider only carrier alarms */
+ wc->spans[span].alarms &= (ZT_ALARM_RED | ZT_ALARM_BLUE | ZT_ALARM_NOTOPEN);
+
+ if (wc->spantype[span] == TYPE_E1) {
+ /* XXX Implement me XXX */
+ } else {
+ /* Detect loopup code if we're not sending one */
+ if ((!wc->spans[span].mainttimer) && (d & 0x08)) {
+ /* Loop-up code detected */
+ if ((wc->loopupcnt[span]++ > 80) && (wc->spans[span].maintstat != ZT_MAINT_REMOTELOOP)) {
+ __t4_framer_out(wc, span, 0x36, 0x08); /* LIM0: Disable any local loop */
+ __t4_framer_out(wc, span, 0x37, 0xf6 ); /* LIM1: Enable remote loop */
+ wc->spans[span].maintstat = ZT_MAINT_REMOTELOOP;
+ }
+ } else
+ wc->loopupcnt[span] = 0;
+ /* Same for loopdown code */
+ if ((!wc->spans[span].mainttimer) && (d & 0x10)) {
+ /* Loop-down code detected */
+ if ((wc->loopdowncnt[span]++ > 80) && (wc->spans[span].maintstat == ZT_MAINT_REMOTELOOP)) {
+ __t4_framer_out(wc, span, 0x36, 0x08); /* LIM0: Disable any local loop */
+ __t4_framer_out(wc, span, 0x37, 0xf0 ); /* LIM1: Disable remote loop */
+ wc->spans[span].maintstat = ZT_MAINT_NONE;
+ }
+ } else
+ wc->loopdowncnt[span] = 0;
+ }
+
+ if (wc->spans[span].lineconfig & ZT_CONFIG_NOTOPEN) {
+ for (x=0,j=0;x < wc->spans[span].channels;x++)
+ if ((wc->spans[span].chans[x].flags & ZT_FLAG_OPEN) ||
+ (wc->spans[span].chans[x].flags & ZT_FLAG_NETDEV))
+ j++;
+ if (!j)
+ alarms |= ZT_ALARM_NOTOPEN;
+ }
+
+ if (c & 0xa0)
+ alarms |= ZT_ALARM_RED;
+ if (c & 0x4)
+ alarms |= ZT_ALARM_BLUE;
+ /* Keep track of recovering */
+ if ((!alarms) && wc->spans[span].alarms)
+ wc->alarmtimer[span] = ZT_ALARMSETTLE_TIME;
+
+ /* If receiving alarms, go into Yellow alarm state */
+ if (alarms && (!wc->spans[span].alarms)) {
+#if 0
+ printk("Going into yellow alarm\n");
+#endif
+#if 0
+ /* The quadfalc automatically generates yellow alarm */
+ if (wc->spantype[span] == TYPE_E1)
+ __t1_set_reg(wc, 0x21, 0x7f);
+ else
+ __t1_set_reg(wc, 0x35, 0x11);
+#endif
+ }
+
+#if 0
+ if (wc->spans[span].alarms != alarms) {
+ d = __control_get_reg(wc, WC_CLOCK);
+ start_alarm(wc);
+ if (!(alarms & (ZT_ALARM_RED | ZT_ALARM_BLUE | ZT_ALARM_LOOPBACK)) &&
+ wc->sync) {
+ /* Use the recieve signalling */
+ wc->spans[span].syncsrc = wc->spans[span].spanno;
+ d |= 1;
+ } else {
+ wc->spans[span].syncsrc = 0;
+ d &= ~1;
+ }
+ __control_set_reg(wc, WC_CLOCK, d);
+ }
+#endif
+ if (wc->alarmtimer[span])
+ alarms |= ZT_ALARM_RECOVER;
+
+ if (c & 0x10)
+ alarms |= ZT_ALARM_YELLOW;
+
+ if (wc->spans[span].mainttimer || wc->spans[span].maintstat)
+ alarms |= ZT_ALARM_LOOPBACK;
+
+ wc->spans[span].alarms = alarms;
+
+ zt_alarm_notify(&wc->spans[span]);
+}
+
+static inline void __handle_leds(struct t4 *wc)
+{
+ int x;
+
+ wc->blinktimer++;
+ for (x=0;x<4;x++) {
+ if (wc->spans[x].flags & ZT_FLAG_RUNNING) {
+ if (wc->spans[x].alarms & (ZT_ALARM_RED | ZT_ALARM_BLUE)) {
+#ifdef FANCY_ALARM
+ if (wc->blinktimer == (altab[wc->alarmpos] >> 1)) {
+ __t4_set_led(wc, x, WC_RED);
+ }
+ if (wc->blinktimer == 0xf) {
+ __t4_set_led(wc, x, WC_OFF);
+ }
+#else
+ if (wc->blinktimer == 160) {
+ __t4_set_led(wc, x, WC_RED);
+ } else if (wc->blinktimer == 480) {
+ __t4_set_led(wc, x, WC_OFF);
+ }
+#endif
+ } else if (wc->spans[x].alarms & ZT_ALARM_YELLOW) {
+ /* Yellow Alarm */
+ __t4_set_led(wc, x, WC_YELLOW);
+ } else if (wc->spans[x].mainttimer || wc->spans[x].maintstat) {
+#ifdef FANCY_ALARM
+ if (wc->blinktimer == (altab[wc->alarmpos] >> 1)) {
+ __t4_set_led(wc, x, WC_GREEN);
+ }
+ if (wc->blinktimer == 0xf) {
+ __t4_set_led(wc, x, WC_OFF);
+ }
+#else
+ if (wc->blinktimer == 160) {
+ __t4_set_led(wc, x, WC_GREEN);
+ } else if (wc->blinktimer == 480) {
+ __t4_set_led(wc, x, WC_OFF);
+ }
+#endif
+ } else {
+ /* No Alarm */
+ __t4_set_led(wc, x, WC_GREEN);
+ }
+ } else
+ __t4_set_led(wc, x, WC_OFF);
+
+ }
+#ifdef FANCY_ALARM
+ if (wc->blinktimer == 0xf) {
+ wc->blinktimer = -1;
+ wc->alarmpos++;
+ if (wc->alarmpos >= (sizeof(altab) / sizeof(altab[0])))
+ wc->alarmpos = 0;
+ }
+#else
+ if (wc->blinktimer == 480)
+ wc->blinktimer = 0;
+#endif
+}
+
+static void t4_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct t4 *wc = dev_id;
+ long flags;
+ int x;
+
+ unsigned int status;
+ unsigned int status2;
+
+ if (wc->intcount < 20)
+ printk("Pre-interrupt\n");
+
+ inirq = 1;
+ /* Make sure it's really for us */
+ status = t4_pci_in(wc, WC_INTR);
+ t4_pci_out(wc, WC_INTR, 0);
+
+ /* Ignore if it's not for us */
+ if (!status)
+ return;
+
+ if (!wc->spansstarted) {
+ printk("Not prepped yet!\n");
+ return;
+ }
+
+ wc->intcount++;
+ if (wc->intcount < 20)
+ printk("Got interrupt, status = %08x\n", status);
+
+ if (status & 0x3) {
+ t4_receiveprep(wc, status);
+ t4_transmitprep(wc, status);
+ }
+
+#if 0
+ if ((wc->intcount < 10) || !(wc->intcount % 1000)) {
+ status2 = t4_framer_in(wc, 0, 0x6f);
+ printk("Status2: %04x\n", status2);
+ for (x = 0;x<4;x++) {
+ status2 = t4_framer_in(wc, x, 0x4c);
+ printk("FRS0/%d: %04x\n", x, status2);
+ }
+ }
+#endif
+ spin_lock_irqsave(&wc->reglock, flags);
+
+ __handle_leds(wc);
+
+ __t4_do_counters(wc);
+
+ 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;
+ }
+
+ spin_unlock_irqrestore(&wc->reglock, flags);
+
+}
+
+static int t4_hardware_init(struct t4 *wc)
+{
+ int x;
+ unsigned int version;
+ unsigned int falcver;
+
+ version = t4_pci_in(wc, WC_VERSION);
+ printk("TE410P version %08x\n", version);
+
+ /* Make sure DMA engine is not running and interrupts are acknowledged */
+ wc->dmactrl = 0x0;
+ t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
+ /* Reset Framer and friends */
+ t4_pci_out(wc, WC_LEDS, 0x00000000);
+
+ /* Set DMA addresses */
+ 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));
+
+ /* Reset pending interrupts */
+ t4_pci_out(wc, WC_INTR, 0x00000000);
+
+ /* Read T1/E1 status */
+ if (t1e1override > -1)
+ wc->t1e1 = t1e1override;
+ else
+ wc->t1e1 = ((t4_pci_in(wc, WC_LEDS)) & 0x0f00) >> 8;
+ wc->order = ((t4_pci_in(wc, WC_LEDS)) & 0xf0000000) >> 28;
+
+ /* Setup LEDS, take out of reset */
+ t4_pci_out(wc, WC_LEDS, 0x000000ff);
+ t4_activate(wc);
+
+ t4_framer_out(wc, 0, 0x4a, 0xaa);
+ falcver = t4_framer_in(wc, 0 ,0x4a);
+ printk("FALC version: %08x, Board ID: %02x\n", falcver, wc->order);
+
+ for (x=0;x< 11;x++)
+ printk("Reg %d: 0x%08x\n", x, t4_pci_in(wc, x));
+ return 0;
+}
+
+static int __devinit t4_launch(struct t4 *wc)
+{
+ int x;
+ if (wc->spans[0].flags & ZT_FLAG_REGISTERED)
+ return 0;
+ printk("TE410P: Launching card: %d\n", wc->order);
+
+ /* Setup serial parameters and system interface */
+ for (x=0;x<4;x++)
+ t4_serial_setup(wc, x);
+
+ if (zt_register(&wc->spans[0], 0)) {
+ printk(KERN_ERR "Unable to register span %s\n", wc->spans[0].name);
+ return -1;
+ }
+ if (zt_register(&wc->spans[1], 0)) {
+ printk(KERN_ERR "Unable to register span %s\n", wc->spans[1].name);
+ zt_unregister(&wc->spans[0]);
+ return -1;
+ }
+ if (zt_register(&wc->spans[2], 0)) {
+ printk(KERN_ERR "Unable to register span %s\n", wc->spans[2].name);
+ zt_unregister(&wc->spans[0]);
+ zt_unregister(&wc->spans[1]);
+ return -1;
+ }
+ if (zt_register(&wc->spans[3], 0)) {
+ printk(KERN_ERR "Unable to register span %s\n", wc->spans[3].name);
+ zt_unregister(&wc->spans[0]);
+ zt_unregister(&wc->spans[1]);
+ zt_unregister(&wc->spans[2]);
+ return -1;
+ }
+ if (debug)
+ printk("Setting timing source\n");
+ t4_set_timing_source(wc, 0);
+ if (debug)
+ printk("Timing source selected\n");
+#ifdef ENABLE_TASKLETS
+ tasklet_init(&wc->t4_tlet, t4_tasklet, (unsigned long)wc);
+#endif
+ return 0;
+}
+
+static int __devinit t4_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ int res;
+ struct t4 *wc;
+ unsigned int *canary;
+ int x,y,f;
+
+
+ if (pci_enable_device(pdev)) {
+ res = -EIO;
+ } else {
+ wc = kmalloc(sizeof(struct t4), GFP_KERNEL);
+ if (wc) {
+ memset(wc, 0x0, sizeof(struct t4));
+ spin_lock_init(&wc->reglock);
+ wc->variety = (char *)(ent->driver_data);
+
+ wc->memaddr = pci_resource_start(pdev, 0);
+ wc->memlen = pci_resource_len(pdev, 0);
+ wc->membase = ioremap(wc->memaddr, wc->memlen);
+#if 0
+ if (!request_mem_region(wc->memaddr, wc->memlen, wc->variety))
+ printk("wct4: Unable to request memory region :(, using anyway...\n");
+#endif
+ if (pci_request_regions(pdev, wc->variety))
+ printk("wct4xxp: Unable to request regions\n");
+
+ printk("Found TE410P at base address %08lx, remapped to %p\n", wc->memaddr, wc->membase);
+
+ wc->dev = pdev;
+
+ wc->writechunk =
+ /* 32 channels, Double-buffer, Read/Write, 4 spans */
+ (unsigned int *)pci_alloc_consistent(pdev, ZT_MAX_CHUNKSIZE * 32 * 2 * 2 * 4, &wc->writedma);
+ if (!wc->writechunk) {
+ printk("wct1xxp: Unable to allocate DMA-able memory\n");
+ return -ENOMEM;
+ }
+
+ /* Read is after the whole write piece (in words) */
+ wc->readchunk = wc->writechunk + ZT_CHUNKSIZE * 32 * 2;
+
+ /* Same thing but in bytes... */
+ 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->readchunk,0xff,ZT_MAX_CHUNKSIZE * 2 * 32 * 4);
+#if 0
+ memset((void *)wc->readchunk,0xff,ZT_MAX_CHUNKSIZE * 2 * 32 * 4);
+ /* Initialize canary */
+ canary = (unsigned int *)(wc->readchunk + ZT_CHUNKSIZE * 64 * 4 - 4);
+ *canary = (CANARY << 16) | (0xffff);
+#endif
+
+ /* Enable bus mastering */
+ pci_set_master(pdev);
+
+ /* Keep track of which device we are */
+ pci_set_drvdata(pdev, wc);
+
+ /* Initialize hardware */
+ t4_hardware_init(wc);
+ for(x = 0; x < MAX_T4_CARDS; x++) {
+ if (!cards[x]) break;
+ }
+ if (x >= MAX_T4_CARDS) {
+ printk("No cards[] slot available!!\n");
+ return -ENOMEM;
+ }
+ wc->num = x;
+ cards[x] = wc;
+
+
+ if (request_irq(pdev->irq, t4_interrupt, SA_INTERRUPT | SA_SHIRQ, "t4xxp", wc)) {
+ printk("t4xxp: Unable to request IRQ %d\n", pdev->irq);
+ kfree(wc);
+ return -EIO;
+ }
+ for (x=0;x<4;x++) {
+ if (wc->t1e1 & (1 << x)) {
+ wc->spantype[x] = TYPE_E1;
+ wc->chans[x] = kmalloc(sizeof(struct zt_chan) * 31,GFP_KERNEL);
+ memset(wc->chans[x],0,sizeof(struct zt_chan) * 31);
+ } else {
+ wc->spantype[x] = TYPE_T1;
+ wc->chans[x] = kmalloc(sizeof(struct zt_chan) * 24,GFP_KERNEL);
+ memset(wc->chans[x],0,sizeof(struct zt_chan) * 24);
+ }
+ if (!wc->chans[x])
+ return -ENOMEM;
+ }
+ init_spans(wc);
+
+ /* Launch cards as appropriate */
+ x = 0;
+ for(;;) {
+ /* Find a card to activate */
+ f = 0;
+ for (x=0;cards[x];x++) {
+ if (cards[x]->order <= highestorder) {
+ t4_launch(cards[x]);
+ if (cards[x]->order == highestorder)
+ f = 1;
+ }
+ }
+ /* If we found at least one, increment the highest order and search again, otherwise stop */
+ if (f)
+ highestorder++;
+ else
+ break;
+ }
+
+ printk("Found a Wildcard: %s\n", wc->variety);
+ res = 0;
+ } else
+ res = -ENOMEM;
+ }
+ return res;
+}
+
+static int t4_hardware_stop(struct t4 *wc)
+{
+
+ /* Turn off DMA, leave interrupts enabled */
+ wc->dmactrl = 0x0000000;
+ t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); /* Turn on only the read interrupts, not the write */
+ t4_pci_out(wc, WC_DMACTRL, wc->dmactrl);
+ t4_pci_out(wc, WC_INTR, 0x00000000);
+
+ current->state = TASK_UNINTERRUPTIBLE;
+ schedule_timeout((25 * HZ) / 1000);
+
+ /* Turn off counter, address, etc */
+ 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);
+ t4_pci_out(wc, WC_LEDS, 0x00000000);
+
+ printk("\nStopped TE410P, Turned off DMA\n");
+ return 0;
+}
+
+static void __devexit t4_remove_one(struct pci_dev *pdev)
+{
+ struct t4 *wc = pci_get_drvdata(pdev);
+ if (wc) {
+ /* Stop hardware */
+ t4_hardware_stop(wc);
+
+ /* Unregister spans */
+ if (wc->spans[0].flags & ZT_FLAG_REGISTERED)
+ zt_unregister(&wc->spans[0]);
+ if (wc->spans[1].flags & ZT_FLAG_REGISTERED)
+ zt_unregister(&wc->spans[1]);
+ if (wc->spans[2].flags & ZT_FLAG_REGISTERED)
+ zt_unregister(&wc->spans[2]);
+ if (wc->spans[3].flags & ZT_FLAG_REGISTERED)
+ zt_unregister(&wc->spans[3]);
+#if 0
+ /* Stop any DMA */
+ __t1xxp_stop_dma(wc);
+
+ /* In case hardware is still there */
+ __t1xxp_disable_interrupts(wc);
+
+ t1xxp_stop_stuff(wc);
+#endif
+
+ if (wc->membase)
+ iounmap((void *)wc->membase);
+
+ pci_release_regions(pdev);
+#if 0
+ if (wc->memaddr)
+ release_mem_region(wc->memaddr, wc->memlen);
+#endif
+
+ /* Immediately free resources */
+ pci_free_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 32 * 4, (void *)wc->writechunk, wc->writedma);
+#if 1
+ free_irq(pdev->irq, wc);
+#endif
+ cards[wc->num] = NULL;
+ pci_set_drvdata(pdev, NULL);
+ }
+}
+
+static struct pci_device_id t4_pci_tbl[] __devinitdata =
+{
+ { 0x10ee, 0x0314, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)"Wildcard TE410P-Xilinx" },
+ { 0, }
+};
+
+static struct pci_driver t4_driver = {
+ name: "t4xxp",
+ probe: t4_init_one,
+ remove: t4_remove_one,
+ suspend: NULL,
+ resume: NULL,
+ id_table: t4_pci_tbl,
+};
+
+static int __init t4_init(void)
+{
+ int res;
+ res = pci_module_init(&t4_driver);
+ if (res)
+ return -ENODEV;
+ return 0;
+}
+
+static void __exit t4_cleanup(void)
+{
+ pci_unregister_driver(&t4_driver);
+}
+
+
+MODULE_AUTHOR("Mark Spencer");
+MODULE_DESCRIPTION("TE410P PCI Driver");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
+MODULE_PARM(debug, "i");
+MODULE_PARM(loopback, "i");
+MODULE_PARM(timingcable, "i");
+MODULE_PARM(t1e1override, "i");
+
+MODULE_DEVICE_TABLE(pci, t4_pci_tbl);
+
+module_init(t4_init);
+module_exit(t4_cleanup);
+
diff --git a/wct4xxp.h b/wct4xxp.h
new file mode 100755
index 0000000..b58544b
--- /dev/null
+++ b/wct4xxp.h
@@ -0,0 +1,37 @@
+/*
+ * Wilcard T400P FXS Interface Driver for Zapata Telephony interface
+ *
+ * Written by Mark Spencer <markster@linux-support.net>
+ *
+ * Copyright (C) 2001, Linux Support Services, Inc.
+ *
+ * All rights reserved.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/ioctl.h>
+
+#define NUM_REGS 0xa9
+#define NUM_PCI 12
+
+struct t4_regs {
+ unsigned int pci[NUM_PCI];
+ unsigned char regs[NUM_REGS];
+};
+
+#define WCT4_GET_REGS _IOW (ZT_CODE, 60, struct t4_regs)
+