From ff729b007e96a90d1d78b0271480779859d41b04 Mon Sep 17 00:00:00 2001 From: kpfleming Date: Sun, 27 Aug 2006 02:02:42 +0000 Subject: merge in new, cleaner Octasic API integration git-svn-id: http://svn.digium.com/svn/zaptel/branches/1.2@1359 5390a7c7-147a-4af0-8ec9-7488f05a26cb --- wct4xxp/Kbuild | 21 + wct4xxp/Makefile | 27 + wct4xxp/OCT6114-128D.ima | Bin 0 -> 249159 bytes wct4xxp/base.c | 3394 ++++++++++++++++++++++++++++++++++++++++++++++ wct4xxp/fw2h.c | 47 + wct4xxp/vpm450m.c | 498 +++++++ wct4xxp/vpm450m.h | 33 + wct4xxp/wct4xxp-diag.c | 414 ++++++ wct4xxp/wct4xxp.h | 37 + 9 files changed, 4471 insertions(+) create mode 100644 wct4xxp/Kbuild create mode 100644 wct4xxp/Makefile create mode 100644 wct4xxp/OCT6114-128D.ima create mode 100644 wct4xxp/base.c create mode 100644 wct4xxp/fw2h.c create mode 100644 wct4xxp/vpm450m.c create mode 100644 wct4xxp/vpm450m.h create mode 100644 wct4xxp/wct4xxp-diag.c create mode 100644 wct4xxp/wct4xxp.h (limited to 'wct4xxp') diff --git a/wct4xxp/Kbuild b/wct4xxp/Kbuild new file mode 100644 index 0000000..8060a97 --- /dev/null +++ b/wct4xxp/Kbuild @@ -0,0 +1,21 @@ +# yes, this is redundant... the Kbuild system is changing to hostprogs-y, +# but we need to be able to support older verions as well +host-progs := fw2h +hostprogs-y := fw2h + +obj-m += wct4xxp.o + +EXTRA_CFLAGS := -I$(src)/.. $(shell $(src)/../oct612x/octasic-helper cflags $(src)/../oct612x) -Wno-undef + +wct4xxp-objs := base.o vpm450m.o $(shell $(src)/../oct612x/octasic-helper objects ../oct612x) + +$(obj)/base.o: $(src)/vpm450m.h $(src)/wct4xxp.h +$(obj)/base.o: $(src)/../zaptel.h + +$(obj)/vpm450m.o: $(obj)/vpm450m_fw.h $(src)/vpm450m.h +$(obj)/vpm450m.o: $(src)/../oct612x/include/oct6100api/oct6100_api.h + +$(obj)/vpm450m_fw.h: $(src)/OCT6114-128D.ima $(obj)/fw2h + $(obj)/fw2h $< $@ + +clean-files := vpm450m_fw.h diff --git a/wct4xxp/Makefile b/wct4xxp/Makefile new file mode 100644 index 0000000..6d992a7 --- /dev/null +++ b/wct4xxp/Makefile @@ -0,0 +1,27 @@ +ifneq ($(KBUILD_EXTMOD),) + +include $(obj)/Kbuild + +else + +all: wct4xxp.o + +base.o: base.c ../zaptel.h vpm450m.h wct4xxp.h + $(CC) $(KFLAGS) -o $@ -c $< + +vpm450m.o: vpm450m.c vpm450m.h vpm450m_fw.h ../oct612x/include/oct6100api/oct6100_api.h + $(CC) $(KFLAGS) $(shell ../oct612x/octasic-helper cflags ../oct612x) -Wno-undef -o $@ -c $< + +wct4xxp.o: base.o vpm450m.o + $(LD) -r -o $@ $^ + +fw2h: CFLAGS= + +vpm450m_fw.h: OCT6114-128D.ima fw2h + ./fw2h $< $@ + +clean: + rm -f *.o fw2h + rm -f vpm450m_fw.h + +endif diff --git a/wct4xxp/OCT6114-128D.ima b/wct4xxp/OCT6114-128D.ima new file mode 100644 index 0000000..2644bc5 Binary files /dev/null and b/wct4xxp/OCT6114-128D.ima differ diff --git a/wct4xxp/base.c b/wct4xxp/base.c new file mode 100644 index 0000000..8594611 --- /dev/null +++ b/wct4xxp/base.c @@ -0,0 +1,3394 @@ +/* + * TE410P Quad-T1/E1 PCI Driver version 0.1, 12/16/02 + * + * Written by Mark Spencer + * Based on previous works, designs, and archetectures conceived and + * written by Jim Dixon . + * + * Copyright (C) 2001 Jim Dixon / Zapata Telephony. + * Copyright (C) 2001-2005, Digium, 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 +#include +#include +#include +#include +#include +#include +#ifdef STANDALONE_ZAPATA +#include "zaptel.h" +#else +#include +#endif +#ifdef LINUX26 +#include +#ifdef HOTPLUG_FIRMWARE +#ifndef CONFIG_FW_LOADER +#undef HOTPLUG_FIRMWARE +#else +#include +#endif +#endif +#endif +#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 */ +#ifdef LINUX26 +/* + * Work queues can significantly improve performance and scalability + * on multi-processor machines, but requires bypassing some kernel + * API's, so it's not guaranteed to be compatible with all kernels. + */ +/* #define ENABLE_WORKQUEUES */ +#endif + +/* Enable prefetching may help performance */ +#define ENABLE_PREFETCH + +/* Support first generation cards? */ +#define SUPPORT_GEN1 + +/* Define to get more attention-grabbing but slightly more I/O using + alarm status */ +#define FANCY_ALARM + +/* Define to support Digium Voice Processing Module expansion card */ +#define VPM_SUPPORT + +#define DEBUG_MAIN (1 << 0) +#define DEBUG_DTMF (1 << 1) +#define DEBUG_REGS (1 << 2) +#define DEBUG_TSI (1 << 3) +#define DEBUG_ECHOCAN (1 << 4) +#define DEBUG_RBS (1 << 5) +#define DEBUG_FRAMER (1 << 6) + +#ifdef ENABLE_WORKQUEUES +#include + +/* XXX UGLY!!!! XXX We have to access the direct structures of the workqueue which + are only defined within workqueue.c because they don't give us a routine to allow us + to nail a work to a particular thread of the CPU. Nailing to threads gives us substantially + higher scalability in multi-CPU environments though! */ + +/* + * The per-CPU workqueue (if single thread, we always use cpu 0's). + * + * The sequence counters are for flush_scheduled_work(). It wants to wait + * until until all currently-scheduled works are completed, but it doesn't + * want to be livelocked by new, incoming ones. So it waits until + * remove_sequence is >= the insert_sequence which pertained when + * flush_scheduled_work() was called. + */ + +struct cpu_workqueue_struct { + + spinlock_t lock; + + long remove_sequence; /* Least-recently added (next to run) */ + long insert_sequence; /* Next to add */ + + struct list_head worklist; + wait_queue_head_t more_work; + wait_queue_head_t work_done; + + struct workqueue_struct *wq; + task_t *thread; + + int run_depth; /* Detect run_workqueue() recursion depth */ +} ____cacheline_aligned; + +/* + * The externally visible workqueue abstraction is an array of + * per-CPU workqueues: + */ +struct workqueue_struct { + struct cpu_workqueue_struct cpu_wq[NR_CPUS]; + const char *name; + struct list_head list; /* Empty if single thread */ +}; + +/* Preempt must be disabled. */ +static void __t4_queue_work(struct cpu_workqueue_struct *cwq, + struct work_struct *work) +{ + unsigned long flags; + + spin_lock_irqsave(&cwq->lock, flags); + work->wq_data = cwq; + list_add_tail(&work->entry, &cwq->worklist); + cwq->insert_sequence++; + wake_up(&cwq->more_work); + spin_unlock_irqrestore(&cwq->lock, flags); +} + +/* + * Queue work on a workqueue. Return non-zero if it was successfully + * added. + * + * We queue the work to the CPU it was submitted, but there is no + * guarantee that it will be processed by that CPU. + */ +static inline int t4_queue_work(struct workqueue_struct *wq, struct work_struct *work, int cpu) +{ + int ret = 0; + + if (!test_and_set_bit(0, &work->pending)) { + BUG_ON(!list_empty(&work->entry)); + __t4_queue_work(wq->cpu_wq + cpu, work); + ret = 1; + } + return ret; +} + +#endif + +static int debug=0; +static int timingcable; +static int highestorder; +static int t1e1override = -1; +static int j1mode = 0; +static int loopback = 0; +static int alarmdebounce = 0; +#ifdef VPM_SUPPORT +static int vpmsupport = 1; +static int vpmdtmfsupport = 0; +static int vpmspans = 4; +#define VPM_DEFAULT_DTMFTHRESHOLD 1000 +static int dtmfthreshold = VPM_DEFAULT_DTMFTHRESHOLD; +static int lastdtmfthreshold = VPM_DEFAULT_DTMFTHRESHOLD; +#endif +/* Enabling bursting can more efficiently utilize PCI bus bandwidth, but + can also cause PCI bus starvation, especially in combination with other + aggressive cards. Please note that burst mode has no effect on CPU + utilization / max number of calls / etc. */ +static int noburst = 1; +static int debugslips = 0; +static int polling = 0; + +#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 FLAG_NMF (1 << 1) +#define FLAG_SENDINGYELLOW (1 << 2) + + +#define TYPE_T1 1 /* is a T1 card */ +#define TYPE_E1 2 /* is an E1 card */ +#define TYPE_J1 3 /* is a running J1 */ + +#define FLAG_2NDGEN (1 << 3) +#define FLAG_2PORT (1 << 4) +#define FLAG_VPM2GEN (1 << 5) +#define FLAG_OCTOPT (1 << 6) +#define FLAG_3RDGEN (1 << 7) + +#define CANARY 0xc0de + +struct devtype { + char *desc; + unsigned int flags; +}; + +static struct devtype wct4xxp = { "Wildcard TE410P/TE405P (1st Gen)", 0 }; +static struct devtype wct410p3 = { "Wildcard TE410P (3rd Gen)", FLAG_2NDGEN | FLAG_3RDGEN }; +static struct devtype wct405p3 = { "Wildcard TE405P (3rd Gen)", FLAG_2NDGEN | FLAG_3RDGEN }; +static struct devtype wct410p2 = { "Wildcard TE410P (2nd Gen)", FLAG_2NDGEN }; +static struct devtype wct405p2 = { "Wildcard TE405P (2nd Gen)", FLAG_2NDGEN }; +static struct devtype wct205p3 = { "Wildcard TE205P (3rd Gen)", FLAG_2NDGEN | FLAG_3RDGEN | FLAG_2PORT }; +static struct devtype wct210p3 = { "Wildcard TE210P (3rd Gen)", FLAG_2NDGEN | FLAG_3RDGEN | FLAG_2PORT }; +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 { + struct t4 *owner; + unsigned int *writechunk; /* Double-word aligned write memory */ + unsigned int *readchunk; /* Double-word aligned read memory */ + int spantype; /* card type, T1 or E1 or J1 */ + int sync; + int psync; + int alarmtimer; + int redalarms; + int notclear; + int alarmcount; + int spanflags; + int syncpos; +#ifdef SUPPORT_GEN1 + int e1check; /* E1 check */ +#endif + struct zt_span span; + unsigned char txsigs[16]; /* Transmit sigs */ + int loopupcnt; + int loopdowncnt; +#ifdef SUPPORT_GEN1 + unsigned char ec_chunk1[31][ZT_CHUNKSIZE]; /* first EC chunk buffer */ + unsigned char ec_chunk2[31][ZT_CHUNKSIZE]; /* second EC chunk buffer */ +#endif + int irqmisses; +#ifdef VPM_SUPPORT + unsigned int dtmfactive; + unsigned int dtmfmask; + unsigned int dtmfmutemask; + short dtmfenergy[31]; + short dtmfdigit[31]; +#endif +#ifdef ENABLE_WORKQUEUES + struct work_struct swork; +#endif + struct zt_chan chans[0]; /* Individual channels */ +}; + +struct t4 { + /* This structure exists one per card */ + struct pci_dev *dev; /* Pointer to PCI device */ + unsigned int intcount; + int num; /* Which card we are */ + int t1e1; /* T1/E1 select pins */ + int globalconfig; /* Whether global setup has been done */ + 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; +#endif + int irq; /* IRQ used by device */ + int order; /* Order */ + int flags; /* Device flags */ + int master; /* Are we master */ + int ledreg; /* LED Register */ + 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 */ + 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 */ + struct vpm450m *vpm450m; +}; + +#define T4_VPM_PRESENT (1 << 28) + + +#ifdef VPM_SUPPORT +static void t4_vpm400_init(struct t4 *wc); +static void t4_vpm450_init(struct t4 *wc); +static void t4_vpm_set_dtmf_threshold(struct t4 *wc, unsigned int threshold); +#endif +static void __set_clear(struct t4 *wc, int span); +static int t4_startup(struct zt_span *span); +static int t4_shutdown(struct zt_span *span); +static int t4_rbsbits(struct zt_chan *chan, int bits); +static int t4_maint(struct zt_span *span, int cmd); +#ifdef SUPPORT_GEN1 +static int t4_reset_dma(struct t4 *wc); +#endif +static int t4_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data); +static void t4_tsi_assign(struct t4 *wc, int fromspan, int fromchan, int tospan, int tochan); +static void t4_tsi_unassign(struct t4 *wc, int tospan, int tochan); +static void __t4_set_timing_source(struct t4 *wc, int unit); +static void __t4_check_alarms(struct t4 *wc, int span); +static void __t4_check_sigbits(struct t4 *wc, int span); + +#define WC_RDADDR 0 +#define WC_WRADDR 1 +#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_LCS (1 << 11) +#define WC_LCS2 (1 << 12) +#define WC_LALE (1 << 13) +#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]; + + +#define MAX_TDM_CHAN 32 +#define MAX_DTMF_DET 16 + +static inline void __t4_pci_out(struct t4 *wc, const unsigned int addr, const unsigned int value) +{ + unsigned int tmp; + wc->membase[addr] = cpu_to_le32(value); + tmp = le32_to_cpu(wc->membase[WC_VERSION]); + if ((tmp & 0xffff0000) != 0xc01a0000) + printk("TE4XXP: Version Synchronization Error!\n"); +#if 0 + tmp = le32_to_cpu(wc->membase[addr]); + if ((value != tmp) && (addr != WC_LEDS) && (addr != WC_LDATA) && + (addr != WC_GPIO) && (addr != WC_INTR)) + 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 le32_to_cpu(wc->membase[addr]); +} + +static inline void __t4_gpio_set(struct t4 *wc, unsigned bits, unsigned int val) +{ + unsigned int newgpio; + newgpio = wc->gpio & (~bits); + newgpio |= val; + if (newgpio != wc->gpio) { + wc->gpio = newgpio; + __t4_pci_out(wc, WC_GPIO, wc->gpio); + } +} + +static inline void __t4_gpio_setdir(struct t4 *wc, unsigned int bits, unsigned int val) +{ + unsigned int newgpioctl; + newgpioctl = wc->gpioctl & (~bits); + newgpioctl |= val; + if (newgpioctl != wc->gpioctl) { + wc->gpioctl = newgpioctl; + __t4_pci_out(wc, WC_GPIOCTL, wc->gpioctl); + } +} + +static inline void t4_gpio_setdir(struct t4 *wc, unsigned int bits, unsigned int val) +{ + unsigned long flags; + spin_lock_irqsave(&wc->reglock, flags); + __t4_gpio_setdir(wc, bits, val); + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static inline void t4_gpio_set(struct t4 *wc, unsigned int bits, unsigned int val) +{ + unsigned long flags; + spin_lock_irqsave(&wc->reglock, flags); + __t4_gpio_set(wc, bits, val); + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static inline void t4_pci_out(struct t4 *wc, const unsigned int addr, const unsigned int value) +{ + unsigned 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; + unsigned 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); + __t4_pci_out(wc, WC_VERSION, 0); + 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 & 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) | (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 & DEBUG_REGS) 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); +} + +#ifdef VPM_SUPPORT + +static inline void wait_a_little(void) +{ + unsigned long newjiffies=jiffies+2; + while(jiffies < newjiffies); +} + +static inline unsigned int __t4_vpm_in(struct t4 *wc, int unit, const unsigned int addr) +{ + unsigned int ret; + unit &= 0x7; + __t4_pci_out(wc, WC_LADDR, (addr & 0x1ff) | ( unit << 12)); + __t4_pci_out(wc, WC_LADDR, (addr & 0x1ff) | ( unit << 12) | (1 << 11) | WC_LREAD); + ret = __t4_pci_in(wc, WC_LDATA); + __t4_pci_out(wc, WC_LADDR, 0); + return ret & 0xff; +} + +static inline void __t4_raw_oct_out(struct t4 *wc, const unsigned int addr, const unsigned int value) +{ + int octopt = wc->tspans[0]->spanflags & FLAG_OCTOPT; + if (!octopt) + __t4_gpio_set(wc, 0xff, (addr >> 8)); + __t4_pci_out(wc, WC_LDATA, 0x10000 | (addr & 0xffff)); + if (!octopt) + __t4_pci_out(wc, WC_LADDR, (WC_LWRITE)); + __t4_pci_out(wc, WC_LADDR, (WC_LWRITE | WC_LALE)); + if (!octopt) + __t4_gpio_set(wc, 0xff, (value >> 8)); + __t4_pci_out(wc, WC_LDATA, (value & 0xffff)); + __t4_pci_out(wc, WC_LADDR, (WC_LWRITE | WC_LALE | WC_LCS)); + __t4_pci_out(wc, WC_LADDR, (0)); +} + +static inline unsigned int __t4_raw_oct_in(struct t4 *wc, const unsigned int addr) +{ + unsigned int ret; + int octopt = wc->tspans[0]->spanflags & FLAG_OCTOPT; + if (!octopt) + __t4_gpio_set(wc, 0xff, (addr >> 8)); + __t4_pci_out(wc, WC_LDATA, 0x10000 | (addr & 0xffff)); + if (!octopt) + __t4_pci_out(wc, WC_LADDR, (WC_LWRITE)); + __t4_pci_out(wc, WC_LADDR, (WC_LWRITE | WC_LALE)); +#ifdef PEDANTIC_OCTASIC_CHECKING + __t4_pci_out(wc, WC_LADDR, (WC_LALE)); +#endif + if (!octopt) { + __t4_gpio_setdir(wc, 0xff, 0x00); + __t4_gpio_set(wc, 0xff, 0x00); + } + __t4_pci_out(wc, WC_LADDR, (WC_LREAD | WC_LALE | WC_LCS)); + if (octopt) { + ret = __t4_pci_in(wc, WC_LDATA) & 0xffff; + } else { + ret = __t4_pci_in(wc, WC_LDATA) & 0xff; + ret |= (__t4_pci_in(wc, WC_GPIO) & 0xff) << 8; + } + __t4_pci_out(wc, WC_LADDR, (0)); + if (!octopt) + __t4_gpio_setdir(wc, 0xff, 0xff); + return ret & 0xffff; +} + +static inline unsigned int __t4_oct_in(struct t4 *wc, unsigned int addr) +{ +#ifdef PEDANTIC_OCTASIC_CHECKING + int count = 1000; +#endif + __t4_raw_oct_out(wc, 0x0008, (addr >> 20)); + __t4_raw_oct_out(wc, 0x000a, (addr >> 4) & ((1 << 16) - 1)); + __t4_raw_oct_out(wc, 0x0000, (((addr >> 1) & 0x7) << 9) | (1 << 8) | (1)); +#ifdef PEDANTIC_OCTASIC_CHECKING + while((__t4_raw_oct_in(wc, 0x0000) & (1 << 8)) && --count); + if (count != 1000) + printk("Yah, read can be slow...\n"); + if (!count) + printk("Read timed out!\n"); +#endif + return __t4_raw_oct_in(wc, 0x0004); +} + +static inline unsigned int t4_oct_in(struct t4 *wc, const unsigned int addr) +{ + unsigned long flags; + unsigned int ret; + spin_lock_irqsave(&wc->reglock, flags); + ret = __t4_oct_in(wc, addr); + spin_unlock_irqrestore(&wc->reglock, flags); + return ret; +} + +static inline unsigned int t4_vpm_in(struct t4 *wc, int unit, const unsigned int addr) +{ + unsigned long flags; + unsigned int ret; + spin_lock_irqsave(&wc->reglock, flags); + ret = __t4_vpm_in(wc, unit, addr); + spin_unlock_irqrestore(&wc->reglock, flags); + return ret; +} + +static inline void __t4_vpm_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value) +{ + unit &= 0x7; + if (debug & DEBUG_REGS) + printk("Writing %02x to address %02x of ec unit %d\n", value, addr, unit); + __t4_pci_out(wc, WC_LADDR, (addr & 0xff)); + __t4_pci_out(wc, WC_LDATA, value); + __t4_pci_out(wc, WC_LADDR, (unit << 12) | (addr & 0x1ff) | (1 << 11)); + __t4_pci_out(wc, WC_LADDR, (unit << 12) | (addr & 0x1ff) | (1 << 11) | WC_LWRITE); + __t4_pci_out(wc, WC_LADDR, (unit << 12) | (addr & 0x1ff) | (1 << 11)); + __t4_pci_out(wc, WC_LADDR, (unit << 12) | (addr & 0x1ff)); + __t4_pci_out(wc, WC_LADDR, 0); + if (debug & DEBUG_REGS) printk("Write complete\n"); + + +#if 0 + { unsigned int tmp; + tmp = t4_vpm_in(wc, unit, addr); + if (tmp != value) { + printk("Expected %d from unit %d echo register %d but got %d instead\n", value, unit, addr, tmp); + } } +#endif +} + +static inline void __t4_oct_out(struct t4 *wc, unsigned int addr, unsigned int value) +{ +#ifdef PEDANTIC_OCTASIC_CHECKING + int count = 1000; +#endif + __t4_raw_oct_out(wc, 0x0008, (addr >> 20)); + __t4_raw_oct_out(wc, 0x000a, (addr >> 4) & ((1 << 16) - 1)); + __t4_raw_oct_out(wc, 0x0004, value); + __t4_raw_oct_out(wc, 0x0000, (((addr >> 1) & 0x7) << 9) | (1 << 8) | (3 << 12) | 1); +#ifdef PEDANTIC_OCTASIC_CHECKING + while((__t4_raw_oct_in(wc, 0x0000) & (1 << 8)) && --count); + if (count != 1000) + printk("Yah, write can be slow\n"); + if (!count) + printk("Write timed out!\n"); +#endif +} + +static inline void t4_oct_out(struct t4 *wc, const unsigned int addr, const unsigned int value) +{ + unsigned long flags; + spin_lock_irqsave(&wc->reglock, flags); + __t4_oct_out(wc, addr, value); + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static inline void t4_vpm_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value) +{ + unsigned long flags; + spin_lock_irqsave(&wc->reglock, flags); + __t4_vpm_out(wc, unit, addr, value); + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static const char vpm_digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', '*', '#'}; + +static void t4_check_vpm450(struct t4 *wc) +{ + int channel, tone, start, span; +#if 0 + /* There's no point checking the interrupt, it's pointless. */ + vpm450m_checkirq(wc->vpm450m); +#endif + while(vpm450m_getdtmf(wc->vpm450m, &channel, &tone, &start)) { + span = channel & 0x3; + channel >>= 2; + if (!wc->t1e1)channel -= 5; + else + channel -= 1; + if (debug) + printk("Got tone %s of '%c' on channel %d of span %d\n",(start ? "START" : "STOP"), tone, channel, span + 1); + if ((wc->tspans[span]->dtmfmask & (1 << channel)) && (tone != 'u')) { + if (start) { + /* The octasic is supposed to mute us, but... Yah, you guessed it. */ + if (wc->tspans[span]->dtmfmutemask & (1 << channel)) { + unsigned long flags; + struct zt_chan *chan = &wc->tspans[span]->span.chans[channel]; + int y; + spin_lock_irqsave(&chan->lock, flags); + for (y=0;ynumbufs;y++) { + if ((chan->inreadbuf > -1) && (chan->readidx[y])) + memset(chan->readbuf[chan->inreadbuf], ZT_XLAW(0, chan), chan->readidx[y]); + } + spin_unlock_irqrestore(&chan->lock, flags); + } + wc->tspans[span]->dtmfactive |= (1 << channel); + zt_qevent_lock(&wc->tspans[span]->span.chans[channel], (ZT_EVENT_DTMFDOWN | tone)); + } else { + wc->tspans[span]->dtmfactive &= ~(1 << channel); + zt_qevent_lock(&wc->tspans[span]->span.chans[channel], (ZT_EVENT_DTMFUP | tone)); + } + } + } +} + +static void __t4_check_vpm400(struct t4 *wc, unsigned int newio) +{ + unsigned int digit, regval = 0; + unsigned int regbyte; + int x, i; + short energy=0; + static unsigned int lastio = 0; + struct t4_span *ts; + + if (debug && (newio != lastio)) + printk("Last was %08x, new is %08x\n", lastio, newio); + + lastio = newio; + + for(x = 0; x < 8; x++) { + if (newio & (1 << (7 - x))) + continue; + ts = wc->tspans[x%4]; + /* Start of DTMF detection process */ + regbyte = __t4_vpm_in(wc, x, 0xb8); + __t4_vpm_out(wc, x, 0xb8, regbyte); /* Write 1 to clear */ + regval = regbyte << 8; + regbyte = __t4_vpm_in(wc, x, 0xb9); + __t4_vpm_out(wc, x, 0xb9, regbyte); + regval |= regbyte; + + for(i = 0; (i < MAX_DTMF_DET) && regval; i++) { + if(regval & 0x0001) { + int channel = (i << 1) + (x >> 2); + int base = channel - 1; + + if (!wc->t1e1) + base -= 4; + regbyte = __t4_vpm_in(wc, x, 0xa8 + i); + digit = vpm_digits[regbyte]; + if (!(wc->tspans[0]->spanflags & FLAG_VPM2GEN)) { + energy = __t4_vpm_in(wc, x, 0x58 + channel); + energy = ZT_XLAW(energy, ts->chans); + ts->dtmfenergy[base] = energy; + } + ts->dtmfactive |= (1 << base); + if (ts->dtmfdigit[base]) { + if (ts->dtmfmask & (1 << base)) + zt_qevent_lock(&ts->span.chans[base], (ZT_EVENT_DTMFUP | ts->dtmfdigit[base])); + } + ts->dtmfdigit[base] = digit; + if (ts->dtmfmask & (1 << base)) + zt_qevent_lock(&ts->span.chans[base], (ZT_EVENT_DTMFDOWN | digit)); + if (ts->dtmfmutemask & (1 << base)) { + /* Mute active receive buffer*/ + unsigned long flags; + struct zt_chan *chan = &ts->span.chans[base]; + int y; + spin_lock_irqsave(&chan->lock, flags); + for (y=0;ynumbufs;y++) { + if ((chan->inreadbuf > -1) && (chan->readidx[y])) + memset(chan->readbuf[chan->inreadbuf], ZT_XLAW(0, chan), chan->readidx[y]); + } + spin_unlock_irqrestore(&chan->lock, flags); + } + if (debug) + printk("Digit Seen: %d, Span: %d, channel: %d, energy: %02x, 'channel %d' chip %d\n", digit, x % 4, base + 1, energy, channel, x); + + } + regval = regval >> 1; + } + if (!(wc->tspans[0]->spanflags & FLAG_VPM2GEN)) + continue; + + /* Start of DTMF off detection process */ + regbyte = __t4_vpm_in(wc, x, 0xbc); + __t4_vpm_out(wc, x, 0xbc, regbyte); /* Write 1 to clear */ + regval = regbyte << 8; + regbyte = __t4_vpm_in(wc, x, 0xbd); + __t4_vpm_out(wc, x, 0xbd, regbyte); + regval |= regbyte; + + for(i = 0; (i < MAX_DTMF_DET) && regval; i++) { + if(regval & 0x0001) { + int channel = (i << 1) + (x >> 2); + int base = channel - 1; + + if (!wc->t1e1) + base -= 4; + ts->dtmfactive &= ~(1 << base); + if (ts->dtmfdigit[base]) { + if (ts->dtmfmask & (1 << base)) + zt_qevent_lock(&ts->span.chans[base], (ZT_EVENT_DTMFUP | ts->dtmfdigit[base])); + } + digit = ts->dtmfdigit[base]; + ts->dtmfdigit[base] = 0; + if (debug) + printk("Digit Gone: %d, Span: %d, channel: %d, energy: %02x, 'channel %d' chip %d\n", digit, x % 4, base + 1, energy, channel, x); + + } + regval = regval >> 1; + } + + } +} +#endif + + +static void __set_clear(struct t4 *wc, int span) +{ + int i,j; + int oldnotclear; + unsigned short val=0; + struct t4_span *ts = wc->tspans[span]; + + oldnotclear = ts->notclear; + if (ts->spantype == TYPE_T1) { + for (i=0;i<24;i++) { + j = (i/8); + if (ts->span.chans[i].flags & ZT_FLAG_CLEAR) { + val |= 1 << (7 - (i % 8)); + ts->notclear &= ~(1 << i); + } else + ts->notclear |= (1 << i); + 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; + } + } + } else { + for (i=0;i<31;i++) { + if (ts->span.chans[i].flags & ZT_FLAG_CLEAR) + ts->notclear &= ~(1 << i); + else + ts->notclear |= (1 << i); + } + } + if (ts->notclear != oldnotclear) { + unsigned char reg; + reg = __t4_framer_in(wc, span, 0x14); + if (ts->notclear) + reg &= ~0x08; + else + reg |= 0x08; + __t4_framer_out(wc, span, 0x14, reg); + } +} + +#if 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); +} +#endif + +static int t4_dacs(struct zt_chan *dst, struct zt_chan *src) +{ + struct t4 *wc; + struct t4_span *ts; + wc = dst->pvt; + ts = wc->tspans[dst->span->offset]; + if (src && (src->pvt != dst->pvt)) { + if (ts->spanflags & FLAG_2NDGEN) + t4_tsi_unassign(wc, dst->span->offset, dst->chanpos); + wc = src->pvt; + if (ts->spanflags & FLAG_2NDGEN) + t4_tsi_unassign(wc, src->span->offset, src->chanpos); + if (debug) + printk("Unassigning %d/%d by default and...\n", src->span->offset, src->chanpos); + if (debug) + printk("Unassigning %d/%d by default\n", dst->span->offset, dst->chanpos); + return -1; + } + if (src) { + t4_tsi_assign(wc, src->span->offset, src->chanpos, dst->span->offset, dst->chanpos); + if (debug) + printk("Assigning channel %d/%d -> %d/%d!\n", src->span->offset, src->chanpos, dst->span->offset, dst->chanpos); + } else { + t4_tsi_unassign(wc, dst->span->offset, dst->chanpos); + if (debug) + printk("Unassigning channel %d/%d!\n", dst->span->offset, dst->chanpos); + } + return 0; +} + +#ifdef VPM_SUPPORT + +void oct_set_reg(void *data, unsigned int reg, unsigned int val) +{ + struct t4 *wc = data; + t4_oct_out(wc, reg, val); +} + +unsigned int oct_get_reg(void *data, unsigned int reg) +{ + struct t4 *wc = data; + unsigned int ret; + ret = t4_oct_in(wc, reg); + return ret; +} + +static int t4_vpm_unit(int span, int channel) +{ + int unit = 0; + switch(vpmspans) { + case 4: + unit = span; + unit += (channel & 1) << 2; + break; + case 2: + unit = span; + unit += (channel & 0x3) << 1; + break; + case 1: + unit = span; + unit += (channel & 0x7); + } + return unit; +} + +static int t4_echocan(struct zt_chan *chan, int eclen) +{ + struct t4 *wc = chan->pvt; + int channel; + int unit; + + if (!wc->vpm) + return -ENODEV; + + if (chan->span->offset >= vpmspans) + return -ENODEV; + + if (wc->t1e1) + channel = chan->chanpos; + else + channel = chan->chanpos + 4; + if (wc->vpm450m) { + channel = channel << 2; + channel |= chan->span->offset; + if(debug & DEBUG_ECHOCAN) + printk("echocan: Card is %d, Channel is %d, Span is %d, offset is %d length %d\n", wc->num, chan->chanpos, chan->span->offset, channel, eclen); + vpm450m_setec(wc->vpm450m, channel, eclen); + // Mark msleep(10); + // // msleep(100); // longer test + } else { + unit = t4_vpm_unit(chan->span->offset, channel); + if(debug & DEBUG_ECHOCAN) + printk("echocan: Card is %d, Channel is %d, Span is %d, unit is %d, unit offset is %d length %d\n", wc->num, chan->chanpos, chan->span->offset, unit, channel, eclen); + if (eclen) + t4_vpm_out(wc,unit,channel,0x3e); + else + t4_vpm_out(wc,unit,channel,0x01); + } + return 0; +} +#endif + +static int t4_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data) +{ + struct t4_regs regs; + int x; + struct t4 *wc = chan->pvt; +#ifdef VPM_SUPPORT + int j; + int channel; + struct t4_span *ts = wc->tspans[chan->span->offset]; +#endif + +#ifdef VPM_SUPPORT + if (dtmfthreshold == 0) + dtmfthreshold = VPM_DEFAULT_DTMFTHRESHOLD; + if (lastdtmfthreshold != dtmfthreshold) { + lastdtmfthreshold = dtmfthreshold; + t4_vpm_set_dtmf_threshold(wc, dtmfthreshold); + } +#endif + + switch(cmd) { + case WCT4_GET_REGS: + for (x=0;xspan->offset, x); + if (copy_to_user((struct t4_regs *)data, ®s, sizeof(regs))) + return -EFAULT; + break; +#ifdef VPM_SUPPORT + case ZT_TONEDETECT: + if (get_user(j, (int *)data)) + return -EFAULT; + if (!wc->vpm) + return -ENOSYS; + if (j && !vpmdtmfsupport) + return -ENOSYS; + if (j & ZT_TONEDETECT_ON) + ts->dtmfmask |= (1 << (chan->chanpos - 1)); + else + ts->dtmfmask &= ~(1 << (chan->chanpos - 1)); + if (j & ZT_TONEDETECT_MUTE) + ts->dtmfmutemask |= (1 << (chan->chanpos - 1)); + else + ts->dtmfmutemask &= ~(1 << (chan->chanpos - 1)); + if (wc->vpm450m) { + channel = (chan->chanpos) << 2; + if (!wc->t1e1) + channel += (4 << 2); + channel |= chan->span->offset; + vpm450m_setdtmf(wc->vpm450m, channel, j & ZT_TONEDETECT_ON, j & ZT_TONEDETECT_MUTE); + } + return 0; +#endif + default: + return -ENOTTY; + } + return 0; +} + +static int t4_maint(struct zt_span *span, int cmd) +{ + struct t4_span *ts = span->pvt; + struct t4 *wc = ts->owner; + + if (ts->spantype == 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("TE%dXXP: Unknown E1 maint command: %d\n", wc->numspans, 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("TE%dXXP: Unknown T1 maint command: %d\n", wc->numspans, 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; + struct t4_span *ts = wc->tspans[chan->span->offset]; + unsigned long flags; + + if(debug & DEBUG_RBS) printk("Setting bits to %d on channel %s\n", bits, chan->name); + spin_lock_irqsave(&wc->reglock, flags); + k = chan->span->offset; + if (ts->spantype == 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 = ts->txsigs[b]; + m = (n / 15) << 2; /* nibble selector */ + c &= (0xf << m); /* keep the other nibble */ + c |= (bits & 0xf) << (4 - m); /* put our new nibble here */ + ts->txsigs[b] = c; + /* output them to the chip */ + __t4_framer_out(wc,k,0x71 + b,c); + } else if (ts->span.lineconfig & ZT_CONFIG_D4) { + n = chan->chanpos - 1; + b = (n/4); + c = ts->txsigs[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 */ + ts->txsigs[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 (ts->span.lineconfig & ZT_CONFIG_ESF) { + n = chan->chanpos - 1; + b = (n/2); + c = ts->txsigs[b]; + m = ((n % 2) << 2); /* nibble selector */ + c &= (0xf << m); /* keep the other nibble */ + c |= (bits & 0xf) << (4 - m); /* put our new nibble here */ + ts->txsigs[b] = c; + /* output them to the chip */ + __t4_framer_out(wc,k,0x70 + b,c); + } + spin_unlock_irqrestore(&wc->reglock, flags); + if (debug & DEBUG_RBS) + printk("Finished setting RBS bits\n"); + return 0; +} + +static int t4_shutdown(struct zt_span *span) +{ + int tspan; + int wasrunning; + unsigned long flags; + struct t4_span *ts = span->pvt; + struct t4 *wc = ts->owner; + + tspan = span->offset + 1; + if (tspan < 0) { + printk("T%dXXP: Span '%d' isn't us?\n", wc->numspans, 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->numspans == 4) && + (!(wc->tspans[0]->span.flags & ZT_FLAG_RUNNING)) && + (!(wc->tspans[1]->span.flags & ZT_FLAG_RUNNING)) && + (!(wc->tspans[2]->span.flags & ZT_FLAG_RUNNING)) && + (!(wc->tspans[3]->span.flags & ZT_FLAG_RUNNING))) + || + ((wc->numspans == 2) && + (!(wc->tspans[0]->span.flags & ZT_FLAG_RUNNING)) && + (!(wc->tspans[1]->span.flags & ZT_FLAG_RUNNING)))) { + /* No longer in use, disable interrupts */ + printk("TE%dXXP: Disabling interrupts since there are no active spans\n", wc->numspans); + wc->stopdma = 1; + } else wc->checktiming = 1; + spin_unlock_irqrestore(&wc->reglock, flags); + if (debug & DEBUG_MAIN) + 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_span *ts = span->pvt; + struct t4 *wc = ts->owner; + + printk("About to enter spanconfig!\n"); + if (debug & DEBUG_MAIN) + printk("TE%dXXP: Configuring span %d\n", wc->numspans, span->spanno); + /* XXX We assume lineconfig is okay and shouldn't XXX */ + span->lineconfig = lc->lineconfig; + span->txlevel = lc->lbo; + span->rxlevel = 0; + if (lc->sync < 0) + lc->sync = 0; + if (lc->sync > 4) + lc->sync = 0; + + /* remove this span number from the current sync sources, if there */ + for(i = 0; i < wc->numspans; i++) { + if (wc->tspans[i]->sync == span->spanno) { + wc->tspans[i]->sync = 0; + wc->tspans[i]->psync = 0; + } + } + wc->tspans[span->offset]->syncpos = lc->sync; + /* if a sync src, put it in proper place */ + if (lc->sync) { + wc->tspans[lc->sync - 1]->sync = span->spanno; + wc->tspans[lc->sync - 1]->psync = span->offset + 1; + } + wc->checktiming = 1; + /* If we're already running, then go ahead and apply the changes */ + if (span->flags & ZT_FLAG_RUNNING) + return t4_startup(span); + printk("Done with spanconfig!\n"); + return 0; +} + +static int t4_chanconfig(struct zt_chan *chan, int sigtype) +{ + int alreadyrunning; + unsigned long flags; + struct t4 *wc = chan->pvt; + + alreadyrunning = wc->tspans[chan->span->offset]->span.flags & ZT_FLAG_RUNNING; + if (debug & DEBUG_MAIN) { + if (alreadyrunning) + printk("TE%dXXP: Reconfigured channel %d (%s) sigtype %d\n", wc->numspans, chan->channo, chan->name, sigtype); + else + printk("TE%dXXP: Configured channel %d (%s) sigtype %d\n", wc->numspans, chan->channo, chan->name, sigtype); + } + 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) +{ +#ifndef LINUX26 + MOD_INC_USE_COUNT; +#else + try_module_get(THIS_MODULE); +#endif + + return 0; +} + +static int t4_close(struct zt_chan *chan) +{ +#ifndef LINUX26 + MOD_DEC_USE_COUNT; +#else + module_put(THIS_MODULE); +#endif + return 0; +} + +static void init_spans(struct t4 *wc) +{ + int x,y,c; + int gen2; + int offset = 1; + struct t4_span *ts; + + gen2 = (wc->tspans[0]->spanflags & FLAG_2NDGEN); + if (!wc->t1e1) + offset += 4; + for (x=0;xnumspans;x++) { + ts = wc->tspans[x]; + sprintf(ts->span.name, "TE%d/%d/%d", wc->numspans, + wc->num, x + 1); + sprintf(ts->span.desc, "T%dXXP (PCI) Card %d Span %d", wc->numspans, wc->num, x+1); + ts->span.spanconfig = t4_spanconfig; + ts->span.chanconfig = t4_chanconfig; + ts->span.startup = t4_startup; + ts->span.shutdown = t4_shutdown; + ts->span.rbsbits = t4_rbsbits; + ts->span.maint = t4_maint; + ts->span.open = t4_open; + ts->span.close = t4_close; + if (ts->spantype == TYPE_T1 || ts->spantype == TYPE_J1) { + ts->span.channels = 24; + ts->span.deflaw = ZT_LAW_MULAW; + } else { + ts->span.channels = 31; + ts->span.deflaw = ZT_LAW_ALAW; + } + ts->span.chans = ts->chans; + ts->span.flags = ZT_FLAG_RBS; + ts->span.linecompat = ZT_CONFIG_AMI | ZT_CONFIG_B8ZS | ZT_CONFIG_D4 | ZT_CONFIG_ESF; + ts->span.ioctl = t4_ioctl; + if (gen2) { +#ifdef VPM_SUPPORT + ts->span.echocan = t4_echocan; +#endif + ts->span.dacs = t4_dacs; + } + ts->span.pvt = ts; + ts->owner = wc; + ts->span.offset = x; + ts->writechunk = (void *)(wc->writechunk + x * 32 * 2); + ts->readchunk = (void *)(wc->readchunk + x * 32 * 2); + init_waitqueue_head(&ts->span.maintq); + for (y=0;ytspans[x]->span.channels;y++) { + struct zt_chan *mychans = ts->chans + y; + sprintf(mychans->name, "TE%d/%d/%d/%d", wc->numspans, 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 | ZT_SIG_EM_E1 | ZT_SIG_DACS_RBS; + c = (x * ts->span.channels) + y; + mychans->pvt = wc; + mychans->chanpos = y + 1; + if (gen2) { + mychans->writechunk = (void *)(wc->writechunk + (x * 32 + y + offset) * 2); + mychans->readchunk = (void *)(wc->readchunk + (x * 32 + y + offset) * 2); + } + } + } +} + +static void t4_serial_setup(struct t4 *wc, int unit) +{ + if (!wc->globalconfig) { + wc->globalconfig = 1; + printk("TE%dXXP: Setting up global serial parameters\n", wc->numspans); + 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, 0x00); /* GCR: Interrupt on Activation/Deactivation of each */ + + /* Configure system interface */ + t4_framer_out(wc, unit, 0x3e, 0xc2); /* SIC1: 8.192 Mhz clock/bus, double buffer receive / transmit, byte interleaved */ + t4_framer_out(wc, unit, 0x3f, 0x20 | (unit << 1)); /* SIC2: No FFS, no center receive eliastic buffer, phase */ + t4_framer_out(wc, unit, 0x40, 0x04); /* SIC3: Edges for capture */ + t4_framer_out(wc, unit, 0x45, 0x00); /* CMR2: We provide sync and clock for tx and rx. */ + if (!wc->t1e1) { /* T1 mode */ + t4_framer_out(wc, unit, 0x22, 0x03); /* XC0: Normal operation of Sa-bits */ + t4_framer_out(wc, unit, 0x23, 0x84); /* XC1: 0 offset */ + if (wc->tspans[unit]->spantype == TYPE_J1) + t4_framer_out(wc, unit, 0x24, 0x83); /* RC0: Just shy of 1023 */ + else + t4_framer_out(wc, unit, 0x24, 0x03); /* RC0: Just shy of 1023 */ + t4_framer_out(wc, unit, 0x25, 0x84); /* RC1: The rest of RC0 */ + } else { /* E1 mode */ + 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 & DEBUG_MAIN) + 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; + if (unit != wc->syncsrc) { + timing = 0x34; /* CMR1: RCLK unit, 8.192 Mhz TCLK, RCLK is 8.192 Mhz */ + if ((unit > -1) && (unit < 4)) { + timing |= (unit << 6); + for (x=0;xnumspans;x++) /* set all 4 receive reference clocks to unit */ + __t4_framer_out(wc, x, 0x44, timing); + wc->dmactrl |= (1 << 29); + __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + } else { + for (x=0;xnumspans;x++) /* set each receive reference clock to itself */ + __t4_framer_out(wc, x, 0x44, timing | (x << 6)); + wc->dmactrl &= ~(1 << 29); + __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + } + wc->syncsrc = unit; + if ((unit < 0) || (unit > 3)) + unit = 0; + else + unit++; + for (x=0;xnumspans;x++) + wc->tspans[x]->span.syncsrc = unit; + } else { + if (debug & DEBUG_MAIN) + printk("TE%dXXP: Timing source already set to %d\n", wc->numspans, unit); + } +#if 0 + printk("wct4xxp: Timing source set to %d\n",unit); +#endif +} + +static void __t4_set_timing_source_auto(struct t4 *wc) +{ + int x; + wc->checktiming = 0; + for (x=0;xnumspans;x++) { + if (wc->tspans[x]->sync) { + if ((wc->tspans[wc->tspans[x]->psync - 1]->span.flags & ZT_FLAG_RUNNING) && + !(wc->tspans[wc->tspans[x]->psync - 1]->span.alarms & (ZT_ALARM_RED | ZT_ALARM_BLUE) )) { + /* Valid timing source */ + __t4_set_timing_source(wc, wc->tspans[x]->psync - 1); + return; + } + } + } + __t4_set_timing_source(wc, 4); +} + +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 */ + if (loopback) + fmr2 |= 0x4; + 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 */ + switch(mytxlevel) { + case 3: + __t4_framer_out(wc, unit, 0x26, 0x07); /* XPM0 */ + __t4_framer_out(wc, unit, 0x27, 0x01); /* XPM1 */ + __t4_framer_out(wc, unit, 0x28, 0x00); /* XPM2 */ + break; + case 2: + __t4_framer_out(wc, unit, 0x26, 0x8c); /* XPM0 */ + __t4_framer_out(wc, unit, 0x27, 0x11); /* XPM1 */ + __t4_framer_out(wc, unit, 0x28, 0x01); /* XPM2 */ + break; + case 1: + __t4_framer_out(wc, unit, 0x26, 0x8c); /* XPM0 */ + __t4_framer_out(wc, unit, 0x27, 0x01); /* XPM1 */ + __t4_framer_out(wc, unit, 0x28, 0x00); /* XPM2 */ + break; + case 0: + default: + __t4_framer_out(wc, unit, 0x26, 0xd7); /* XPM0 */ + __t4_framer_out(wc, unit, 0x27, 0x22); /* XPM1 */ + __t4_framer_out(wc, unit, 0x28, 0x01); /* XPM2 */ + break; + } + + __t4_framer_out(wc, unit, 0x14, 0xff); /* IMR0: We care about CAS changes, etc */ + __t4_framer_out(wc, unit, 0x15, 0xff); /* IMR1: We care about nothing */ + __t4_framer_out(wc, unit, 0x16, 0x00); /* IMR2: We care about all the alarm stuff! */ + if (debugslips) { + __t4_framer_out(wc, unit, 0x17, 0xf4); /* IMR3: We care about AIS and friends */ + __t4_framer_out(wc, unit, 0x18, 0x3f); /* IMR4: We care about slips on transmit */ + } else { + __t4_framer_out(wc, unit, 0x17, 0xf7); /* IMR3: We care about AIS and friends */ + __t4_framer_out(wc, unit, 0x18, 0xff); /* IMR4: We don't care about slips on transmit */ + } + + if (!polling) { + __t4_check_alarms(wc, unit); + __t4_check_sigbits(wc, unit); + } + + printk("TE%dXXP: Span %d configured for %s/%s\n", wc->numspans, unit + 1, framing, line); +} + +static void __t4_configure_e1(struct t4 *wc, int unit, int lineconfig) +{ + unsigned int fmr2, fmr1, fmr0; + unsigned int cas = 0; + unsigned int imr3extra=0; + char *crc4 = ""; + char *framing, *line; + fmr1 = 0x44; /* FMR1: E1 mode, Automatic force resync, PCM30 mode, 8.192 Mhz backplane, no XAIS */ + fmr2 = 0x03; /* FMR2: Auto transmit remote alarm, auto loss of multiframe recovery, no payload loopback */ + if (loopback) + fmr2 |= 0x4; + if (lineconfig & ZT_CONFIG_CRC4) { + fmr1 |= 0x08; /* CRC4 transmit */ + fmr2 |= 0xc0; /* 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"; + imr3extra = 0x28; + } else { + framing = "CAS"; + cas = 0x40; + } + __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|cas); /* XSP: E-bit set when async. AXS auto, XSIF to 1 */ + + + /* Generate pulse mask for E1 */ + __t4_framer_out(wc, unit, 0x26, 0x54); /* XPM0 */ + __t4_framer_out(wc, unit, 0x27, 0x02); /* XPM1 */ + __t4_framer_out(wc, unit, 0x28, 0x00); /* XPM2 */ + + __t4_framer_out(wc, unit, 0x14, 0xff); /* IMR0: We care about CRC errors, CAS changes, etc */ + __t4_framer_out(wc, unit, 0x15, 0x3f); /* IMR1: We care about loopup / loopdown */ + __t4_framer_out(wc, unit, 0x16, 0x00); /* IMR2: We care about all the alarm stuff! */ + if (debugslips) { + __t4_framer_out(wc, unit, 0x17, 0xc4 | imr3extra); /* IMR3: We care about AIS and friends */ + __t4_framer_out(wc, unit, 0x18, 0x3f); /* IMR4: We care about slips on transmit */ + } else { + __t4_framer_out(wc, unit, 0x17, 0xc7 | imr3extra); /* IMR3: We care about AIS and friends */ + __t4_framer_out(wc, unit, 0x18, 0xff); /* IMR4: We don't care about slips on transmit */ + } + if (!polling) { + __t4_check_alarms(wc, unit); + __t4_check_sigbits(wc, unit); + } + printk("TE%dXXP: Span %d configured for %s/%s%s\n", wc->numspans, unit + 1, framing, line, crc4); +} + +static int t4_startup(struct zt_span *span) +{ +#ifdef SUPPORT_GEN1 + int i; +#endif + int tspan; + unsigned long flags; + int alreadyrunning; + struct t4_span *ts = span->pvt; + struct t4 *wc = ts->owner; + + printk("About to enter startup!\n"); + tspan = span->offset + 1; + if (tspan < 0) { + printk("TE%dXXP: Span '%d' isn't us?\n", wc->numspans, span->spanno); + return -1; + } + + spin_lock_irqsave(&wc->reglock, flags); + + alreadyrunning = span->flags & ZT_FLAG_RUNNING; + +#ifdef SUPPORT_GEN1 + /* initialize the start value for the entire chunk of last ec buffer */ + for(i = 0; i < span->channels; i++) + { + memset(ts->ec_chunk1[i], + ZT_LIN2X(0,&span->chans[i]),ZT_CHUNKSIZE); + memset(ts->ec_chunk2[i], + ZT_LIN2X(0,&span->chans[i]),ZT_CHUNKSIZE); + } +#endif + /* Force re-evaluation fo timing source */ + if (timingcable) + wc->syncsrc = -1; + + if (ts->spantype == 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); + } + + /* Note clear channel status */ + wc->tspans[span->offset]->notclear = 0; + __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 = 1 << 29; + wc->dmactrl |= (ts->spanflags & FLAG_2NDGEN) ? 0xc0000000 : 0xc0000003; +#ifdef VPM_SUPPORT + wc->dmactrl |= wc->vpm; +#endif + /* Seed interrupt register */ + __t4_pci_out(wc, WC_INTR, 0x0c); + if (noburst) + wc->dmactrl |= (1 << 26); + __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + if (!polling) { + __t4_check_alarms(wc, span->offset); + __t4_check_sigbits(wc, span->offset); + } + } + + spin_unlock_irqrestore(&wc->reglock, flags); + if (wc->tspans[0]->sync == span->spanno) printk("SPAN %d: Primary Sync Source\n",span->spanno); + if (wc->tspans[1]->sync == span->spanno) printk("SPAN %d: Secondary Sync Source\n",span->spanno); + if (wc->numspans == 4) { + if (wc->tspans[2]->sync == span->spanno) printk("SPAN %d: Tertiary Sync Source\n",span->spanno); + if (wc->tspans[3]->sync == span->spanno) printk("SPAN %d: Quaternary Sync Source\n",span->spanno); + } +#ifdef VPM_SUPPORT + if (!alreadyrunning && !wc->vpm) { + wait_a_little(); + t4_vpm400_init(wc); + if (!wc->vpm) + t4_vpm450_init(wc); + wc->dmactrl |= wc->vpm; + t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + } +#endif + printk("Completed startup!\n"); + return 0; +} + +#ifdef SUPPORT_GEN1 +static inline void e1_check(struct t4 *wc, int span, int val) +{ + struct t4_span *ts = wc->tspans[span]; + if ((ts->span.channels > 24) && + (ts->span.flags & ZT_FLAG_RUNNING) && + !(ts->span.alarms) && + (!wc->e1recover)) { + if (val != 0x1b) { + ts->e1check++; + } else + ts->e1check = 0; + if (ts->e1check > 100) { + /* Wait 1000 ms */ + wc->e1recover = 1000 * 8; + wc->tspans[0]->e1check = wc->tspans[1]->e1check = 0; + if (wc->numspans == 4) + wc->tspans[2]->e1check = wc->tspans[3]->e1check = 0; + if (debug & DEBUG_MAIN) + printk("Detected loss of E1 alignment on span %d!\n", span); + t4_reset_dma(wc); + } + } +} + +static void t4_receiveprep(struct t4 *wc, int irq) +{ + volatile unsigned int *readchunk; + int dbl = 0; + int x,y,z; + unsigned int tmp; + int offset=0; + if (!wc->t1e1) + offset = 4; + if (irq & 1) { + /* First part */ + readchunk = wc->readchunk; + if (!wc->last0) + dbl = 1; + wc->last0 = 0; + } else { + readchunk = wc->readchunk + ZT_CHUNKSIZE * 32; + if (wc->last0) + dbl = 1; + wc->last0 = 1; + } + if (dbl) { + for (x=0;xnumspans;x++) + wc->tspans[x]->irqmisses++; + if (debug & DEBUG_MAIN) + printk("TE%dXXP: Double/missed interrupt detected\n", wc->numspans); + } + for (x=0;xnumspans == 4) { + wc->tspans[3]->span.chans[z].readchunk[x] = tmp & 0xff; + wc->tspans[2]->span.chans[z].readchunk[x] = (tmp & 0xff00) >> 8; + } + wc->tspans[1]->span.chans[z].readchunk[x] = (tmp & 0xff0000) >> 16; + wc->tspans[0]->span.chans[z].readchunk[x] = tmp >> 24; + } + if (wc->t1e1) { + if (wc->e1recover > 0) + wc->e1recover--; + tmp = readchunk[0]; + if (wc->numspans == 4) { + e1_check(wc, 3, (tmp & 0x7f)); + e1_check(wc, 2, (tmp & 0x7f00) >> 8); + } + e1_check(wc, 1, (tmp & 0x7f0000) >> 16); + e1_check(wc, 0, (tmp & 0x7f000000) >> 24); + for (z=24;z<31;z++) { + /* Only E1 channels now */ + tmp = readchunk[z+1]; + if (wc->numspans == 4) { + if (wc->tspans[3]->span.channels > 24) + wc->tspans[3]->span.chans[z].readchunk[x] = tmp & 0xff; + if (wc->tspans[2]->span.channels > 24) + wc->tspans[2]->span.chans[z].readchunk[x] = (tmp & 0xff00) >> 8; + } + if (wc->tspans[1]->span.channels > 24) + wc->tspans[1]->span.chans[z].readchunk[x] = (tmp & 0xff0000) >> 16; + if (wc->tspans[0]->span.channels > 24) + wc->tspans[0]->span.chans[z].readchunk[x] = tmp >> 24; + } + } + /* Advance pointer by 4 TDM frame lengths */ + readchunk += 32; + } + for (x=0;xnumspans;x++) { + if (wc->tspans[x]->span.flags & ZT_FLAG_RUNNING) { + for (y=0;ytspans[x]->span.channels;y++) { + /* Echo cancel double buffered data */ + zt_ec_chunk(&wc->tspans[x]->span.chans[y], + wc->tspans[x]->span.chans[y].readchunk, + wc->tspans[x]->ec_chunk2[y]); + memcpy(wc->tspans[x]->ec_chunk2[y],wc->tspans[x]->ec_chunk1[y], + ZT_CHUNKSIZE); + memcpy(wc->tspans[x]->ec_chunk1[y], + wc->tspans[x]->span.chans[y].writechunk, + ZT_CHUNKSIZE); + } + zt_receive(&wc->tspans[x]->span); + } + } +} +#endif + +#if (ZT_CHUNKSIZE != 8) +#error Sorry, nextgen does not support chunksize != 8 +#endif + +static inline void __receive_span(struct t4_span *ts) +{ +#ifdef VPM_SUPPORT + int y; + unsigned int merged; + merged = ts->dtmfactive & ts->dtmfmutemask; + if (merged) { + for (y=0;yspan.channels;y++) { + /* Mute any DTMFs which are supposed to be muted */ + if (merged & (1 << y)) { + memset(ts->span.chans[y].readchunk, ZT_XLAW(0, (ts->span.chans + y)), ZT_CHUNKSIZE); } + } + } +#endif + +#ifdef ENABLE_PREFETCH + prefetch((void *)(ts->readchunk)); + prefetch((void *)(ts->writechunk)); + prefetch((void *)(ts->readchunk + 8)); + prefetch((void *)(ts->writechunk + 8)); + prefetch((void *)(ts->readchunk + 16)); + prefetch((void *)(ts->writechunk + 16)); + prefetch((void *)(ts->readchunk + 24)); + prefetch((void *)(ts->writechunk + 24)); + prefetch((void *)(ts->readchunk + 32)); + prefetch((void *)(ts->writechunk + 32)); + prefetch((void *)(ts->readchunk + 40)); + prefetch((void *)(ts->writechunk + 40)); + prefetch((void *)(ts->readchunk + 48)); + prefetch((void *)(ts->writechunk + 48)); + prefetch((void *)(ts->readchunk + 56)); + prefetch((void *)(ts->writechunk + 56)); +#endif + + zt_ec_span(&ts->span); + zt_receive(&ts->span); +} + +static inline void __transmit_span(struct t4_span *ts) +{ + zt_transmit(&ts->span); +} + +#ifdef ENABLE_WORKQUEUES +static void workq_handlespan(void *data) +{ + struct t4_span *ts = data; + struct t4 *wc = ts->owner; + + __receive_span(ts); + __transmit_span(ts); + atomic_dec(&wc->worklist); + if (!atomic_read(&wc->worklist)) + t4_pci_out(wc, WC_INTR, 0); +} +#else +static void t4_prep_gen2(struct t4 *wc) +{ + int x; + for (x=0;xnumspans;x++) { + if (wc->tspans[x]->span.flags & ZT_FLAG_RUNNING) { + __receive_span(wc->tspans[x]); + __transmit_span(wc->tspans[x]); + } + } +} + +#endif +#ifdef SUPPORT_GEN1 +static void t4_transmitprep(struct t4 *wc, int irq) +{ + volatile unsigned int *writechunk; + int x,y,z; + unsigned int tmp; + int offset=0; + if (!wc->t1e1) + offset = 4; + if (irq & 1) { + /* First part */ + writechunk = wc->writechunk + 1; + } else { + writechunk = wc->writechunk + ZT_CHUNKSIZE * 32 + 1; + } + for (y=0;ynumspans;y++) { + if (wc->tspans[y]->span.flags & ZT_FLAG_RUNNING) + zt_transmit(&wc->tspans[y]->span); + } + + for (x=0;xtspans[3]->span.chans[z].writechunk[x]) | + (wc->tspans[2]->span.chans[z].writechunk[x] << 8) | + (wc->tspans[1]->span.chans[z].writechunk[x] << 16) | + (wc->tspans[0]->span.chans[z].writechunk[x] << 24); + writechunk[z+offset] = tmp; + } + if (wc->t1e1) { + for (z=24;z<31;z++) { + /* Only E1 channels now */ + tmp = 0; + if (wc->numspans == 4) { + if (wc->tspans[3]->span.channels > 24) + tmp |= wc->tspans[3]->span.chans[z].writechunk[x]; + if (wc->tspans[2]->span.channels > 24) + tmp |= (wc->tspans[2]->span.chans[z].writechunk[x] << 8); + } + if (wc->tspans[1]->span.channels > 24) + tmp |= (wc->tspans[1]->span.chans[z].writechunk[x] << 16); + if (wc->tspans[0]->span.channels > 24) + tmp |= (wc->tspans[0]->span.chans[z].writechunk[x] << 24); + writechunk[z] = tmp; + } + } + /* Advance pointer by 4 TDM frame lengths */ + writechunk += 32; + } + +} +#endif + +static void __t4_check_sigbits(struct t4 *wc, int span) +{ + int a,i,rxs; + struct t4_span *ts = wc->tspans[span]; + + if (debug & DEBUG_RBS) + printk("Checking sigbits on span %d\n", span + 1); + + if (!(ts->span.flags & ZT_FLAG_RUNNING)) + return; + if (ts->spantype == 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 (!(ts->span.chans[i+16].sig & ZT_SIG_CLEAR)) { + if (ts->span.chans[i+16].rxsig != rxs) + zt_rbsbits(&ts->span.chans[i+16], rxs); + } + rxs = (a >> 4) & 0xf; + if (!(ts->span.chans[i].sig & ZT_SIG_CLEAR)) { + if (ts->span.chans[i].rxsig != rxs) + zt_rbsbits(&ts->span.chans[i], rxs); + } + } + } else if (ts->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 (!(ts->span.chans[i+3].sig & ZT_SIG_CLEAR)) { + if (ts->span.chans[i+3].rxsig != rxs) + zt_rbsbits(&ts->span.chans[i+3], rxs); + } + rxs = (a & 0xc); + if (!(ts->span.chans[i+2].sig & ZT_SIG_CLEAR)) { + if (ts->span.chans[i+2].rxsig != rxs) + zt_rbsbits(&ts->span.chans[i+2], rxs); + } + rxs = (a >> 2) & 0xc; + if (!(ts->span.chans[i+1].sig & ZT_SIG_CLEAR)) { + if (ts->span.chans[i+1].rxsig != rxs) + zt_rbsbits(&ts->span.chans[i+1], rxs); + } + rxs = (a >> 4) & 0xc; + if (!(ts->span.chans[i].sig & ZT_SIG_CLEAR)) { + if (ts->span.chans[i].rxsig != rxs) + zt_rbsbits(&ts->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 (!(ts->span.chans[i+1].sig & ZT_SIG_CLEAR)) { + /* XXX Not really reset on every trans! XXX */ + if (ts->span.chans[i+1].rxsig != rxs) { + zt_rbsbits(&ts->span.chans[i+1], rxs); + } + } + rxs = (a >> 4) & 0xf; + if (!(ts->span.chans[i].sig & ZT_SIG_CLEAR)) { + /* XXX Not really reset on every trans! XXX */ + if (ts->span.chans[i].rxsig != rxs) { + zt_rbsbits(&ts->span.chans[i], rxs); + } + } + } + } +} + +static void __t4_check_alarms(struct t4 *wc, int span) +{ + unsigned char c,d; + int alarms; + int x,j; + struct t4_span *ts = wc->tspans[span]; + + if (!(ts->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 */ + ts->span.alarms &= (ZT_ALARM_RED | ZT_ALARM_BLUE | ZT_ALARM_NOTOPEN); + + if (ts->spantype == TYPE_E1) { + if (c & 0x04) { + /* No multiframe found, force RAI high after 400ms only if + we haven't found a multiframe since last loss + of frame */ + if (!(ts->spanflags & FLAG_NMF)) { + __t4_framer_out(wc, span, 0x20, 0x9f | 0x20); /* LIM0: Force RAI High */ + ts->spanflags |= FLAG_NMF; + printk("NMF workaround on!\n"); + } + __t4_framer_out(wc, span, 0x1e, 0xc3); /* Reset to CRC4 mode */ + __t4_framer_out(wc, span, 0x1c, 0xf2); /* Force Resync */ + __t4_framer_out(wc, span, 0x1c, 0xf0); /* Force Resync */ + } else if (!(c & 0x02)) { + if ((ts->spanflags & FLAG_NMF)) { + __t4_framer_out(wc, span, 0x20, 0x9f); /* LIM0: Clear forced RAI */ + ts->spanflags &= ~FLAG_NMF; + printk("NMF workaround off!\n"); + } + } + } else { + /* Detect loopup code if we're not sending one */ + if ((!ts->span.mainttimer) && (d & 0x08)) { + /* Loop-up code detected */ + if ((ts->loopupcnt++ > 80) && (ts->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 */ + ts->span.maintstat = ZT_MAINT_REMOTELOOP; + } + } else + ts->loopupcnt = 0; + /* Same for loopdown code */ + if ((!ts->span.mainttimer) && (d & 0x10)) { + /* Loop-down code detected */ + if ((ts->loopdowncnt++ > 80) && (ts->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 */ + ts->span.maintstat = ZT_MAINT_NONE; + } + } else + ts->loopdowncnt = 0; + } + + if (ts->span.lineconfig & ZT_CONFIG_NOTOPEN) { + for (x=0,j=0;x < ts->span.channels;x++) + if ((ts->span.chans[x].flags & ZT_FLAG_OPEN) || + (ts->span.chans[x].flags & ZT_FLAG_NETDEV)) + j++; + if (!j) + alarms |= ZT_ALARM_NOTOPEN; + } + + if (c & 0xa0) { + if (ts->alarmcount >= alarmdebounce) + alarms |= ZT_ALARM_RED; + else + ts->alarmcount++; + } else + ts->alarmcount = 0; + if (c & 0x4) + alarms |= ZT_ALARM_BLUE; + + if (((!ts->span.alarms) && alarms) || + (ts->span.alarms && (!alarms))) + wc->checktiming = 1; + + /* Keep track of recovering */ + if ((!alarms) && ts->span.alarms) + ts->alarmtimer = ZT_ALARMSETTLE_TIME; + if (ts->alarmtimer) + alarms |= ZT_ALARM_RECOVER; + + /* If receiving alarms, go into Yellow alarm state */ + if (alarms && !(ts->spanflags & FLAG_SENDINGYELLOW)) { + unsigned char fmr4; +#if 1 + printk("wct%dxxp: Setting yellow alarm on span %d\n", wc->numspans, span + 1); +#endif + /* We manually do yellow alarm to handle RECOVER and NOTOPEN, otherwise it's auto anyway */ + fmr4 = __t4_framer_in(wc, span, 0x20); + __t4_framer_out(wc, span, 0x20, fmr4 | 0x20); + ts->spanflags |= FLAG_SENDINGYELLOW; + } else if ((!alarms) && (ts->spanflags & FLAG_SENDINGYELLOW)) { + unsigned char fmr4; +#if 1 + printk("wct%dxxp: Clearing yellow alarm on span %d\n", wc->numspans, span + 1); +#endif + /* We manually do yellow alarm to handle RECOVER */ + fmr4 = __t4_framer_in(wc, span, 0x20); + __t4_framer_out(wc, span, 0x20, fmr4 & ~0x20); + ts->spanflags &= ~FLAG_SENDINGYELLOW; + } + + /* Re-check the timing source when we enter/leave alarm, not withstanding + yellow alarm */ + if (c & 0x10) + alarms |= ZT_ALARM_YELLOW; + if (ts->span.mainttimer || ts->span.maintstat) + alarms |= ZT_ALARM_LOOPBACK; + ts->span.alarms = alarms; + zt_alarm_notify(&ts->span); +} + +static void __t4_do_counters(struct t4 *wc) +{ + int span; + for (span=0;spannumspans;span++) { + struct t4_span *ts = wc->tspans[span]; + int docheck=0; + if (ts->loopupcnt || ts->loopdowncnt) + docheck++; + if (ts->alarmtimer) { + if (!--ts->alarmtimer) { + docheck++; + ts->span.alarms &= ~(ZT_ALARM_RECOVER); + } + } + if (docheck) { + if (!polling) + __t4_check_alarms(wc, span); + zt_alarm_notify(&ts->span); + } + } +} + +static inline void __handle_leds(struct t4 *wc) +{ + int x; + + wc->blinktimer++; + for (x=0;xnumspans;x++) { + struct t4_span *ts = wc->tspans[x]; + if (ts->span.flags & ZT_FLAG_RUNNING) { + if (ts->span.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 (ts->span.alarms & ZT_ALARM_YELLOW) { + /* Yellow Alarm */ + __t4_set_led(wc, x, WC_YELLOW); + } else if (ts->span.mainttimer || ts->span.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 +} + +#ifdef SUPPORT_GEN1 +#ifdef LINUX26 +static irqreturn_t t4_interrupt(int irq, void *dev_id, struct pt_regs *regs) +#else +static void t4_interrupt(int irq, void *dev_id, struct pt_regs *regs) +#endif +{ + struct t4 *wc = dev_id; + unsigned long flags; + int x; + + unsigned int status; +#if 0 + unsigned int status2; +#endif + +#if 0 + if (wc->intcount < 20) + printk("Pre-interrupt\n"); +#endif + + 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) +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif + + if (!wc->spansstarted) { + printk("Not prepped yet!\n"); +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif + } + + wc->intcount++; +#if 0 + if (wc->intcount < 20) + printk("Got interrupt, status = %08x\n", status); +#endif + + 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; + } + + if (wc->checktiming > 0) + __t4_set_timing_source_auto(wc); + spin_unlock_irqrestore(&wc->reglock, flags); +#ifdef LINUX26 + return IRQ_RETVAL(1); +#endif +} +#endif + +static inline void __t4_framer_interrupt(struct t4 *wc, int span) +{ + /* Check interrupts for a given span */ + unsigned char gis, isr0=0, isr1=0, isr2=0, isr3=0, isr4; + struct t4_span *ts; + + if (debug & DEBUG_FRAMER) + printk("framer interrupt span %d:%d!\n", wc->num, span + 1); + ts = wc->tspans[span]; + + gis = __t4_framer_in(wc, span, 0x6e); + + if (ts->spantype == TYPE_E1) { + /* E1 checks */ + if (gis & 0x1) + isr0 = __t4_framer_in(wc, span, 0x68); + if (gis & 0x2) + isr1 = __t4_framer_in(wc, span, 0x69); + if (gis & 0x4) + isr2 = __t4_framer_in(wc, span, 0x6a); + if (gis & 0x8) + isr3 = __t4_framer_in(wc, span, 0x6b); + + + if (isr0) + __t4_check_sigbits(wc, span); + + if ((isr3 & 0x38) || isr2 || isr1) + __t4_check_alarms(wc, span); + if (debug & DEBUG_FRAMER) + printk("gis: %02x, isr0: %02x, isr1: %02x, isr2: %02x, isr3: %02x\n", gis, isr0, isr1, isr2, isr3); + } else { + /* T1 checks */ + if (gis & 0x1) + isr0 = __t4_framer_in(wc, span, 0x68); + if (gis & 0x4) + isr2 = __t4_framer_in(wc, span, 0x6a); + if (gis & 0x8) + isr3 = __t4_framer_in(wc, span, 0x6b); + + if (isr0) + __t4_check_sigbits(wc, span); + if (isr2 || (isr3 & 0x08)) + __t4_check_alarms(wc, span); + if (debug & DEBUG_FRAMER) + printk("gis: %02x, isr0: %02x, isr1: %02x, irs2: %02x, isr3: %02x\n", gis, isr0, isr1, isr2, isr3); + } + if (debugslips && !ts->span.alarms) { + if (isr3 & 0x02) + printk("TE%d10P: RECEIVE slip NEGATIVE on span %d\n", wc->numspans, span + 1); + if (isr3 & 0x01) + printk("TE%d10P: RECEIVE slip POSITIVE on span %d\n", wc->numspans, span + 1); + if (gis & 0x10) + isr4 = __t4_framer_in(wc, span, 0x6c); + else + isr4 = 0; + if (isr4 & 0x80) + printk("TE%dXXP: TRANSMIT slip POSITIVE on span %d\n", wc->numspans, span + 1); + if (isr4 & 0x40) + printk("TE%d10P: TRANSMIT slip NEGATIVE on span %d\n", wc->numspans, span + 1); + } +} + +#ifdef LINUX26 +static irqreturn_t t4_interrupt_gen2(int irq, void *dev_id, struct pt_regs *regs) +#else +static void t4_interrupt_gen2(int irq, void *dev_id, struct pt_regs *regs) +#endif +{ + struct t4 *wc = dev_id; + unsigned long flags; + unsigned char cis; + int x; + int needcheckvpm450=0; + + unsigned int status; +#if 0 + unsigned int status2; +#endif + +#if 0 + if (wc->intcount < 20) + printk("2G: Pre-interrupt\n"); +#endif + + inirq = 1; + /* Make sure it's really for us */ + status = t4_pci_in(wc, WC_INTR); +#if 1 + t4_pci_out(wc, WC_INTR, status & 0x00000008); +#endif + + /* Ignore if it's not for us */ + if (!(status & 0x7)) { +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif + } + if (!wc->spansstarted) { + printk("Not prepped yet!\n"); +#ifdef LINUX26 + return IRQ_NONE; +#else + return; +#endif + } + + wc->intcount++; +#if 1 + if ((wc->intcount < 20) && debug) + printk("2G: Got interrupt, status = %08x, GIS = %04x\n", status, __t4_framer_in(wc, 0, 0x6f)); +#endif + + if (status & 0x2) { +#ifdef ENABLE_WORKQUEUES + int cpus = num_online_cpus(); + atomic_set(&wc->worklist, wc->numspans); + if (wc->tspans[0]->span.flags & ZT_FLAG_RUNNING) + t4_queue_work(wc->workq, &wc->tspans[0]->swork, 0); + else + atomic_dec(&wc->worklist); + if (wc->tspans[1]->span.flags & ZT_FLAG_RUNNING) + t4_queue_work(wc->workq, &wc->tspans[1]->swork, 1 % cpus); + else + atomic_dec(&wc->worklist); + if (wc->numspans == 4) { + if (wc->tspans[2]->span.flags & ZT_FLAG_RUNNING) + t4_queue_work(wc->workq, &wc->tspans[2]->swork, 2 % cpus); + else + atomic_dec(&wc->worklist); + if (wc->tspans[3]->span.flags & ZT_FLAG_RUNNING) + t4_queue_work(wc->workq, &wc->tspans[3]->swork, 3 % cpus); + else + atomic_dec(&wc->worklist); + } +#else + t4_prep_gen2(wc); +#endif + } + + spin_lock_irqsave(&wc->reglock, flags); + + 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, 0x6f); + if (cis & 0x1) + __t4_framer_interrupt(wc, 0); + if (cis & 0x2) + __t4_framer_interrupt(wc, 1); + if (cis & 0x4) + __t4_framer_interrupt(wc, 2); + if (cis & 0x8) + __t4_framer_interrupt(wc, 3); + } +#ifdef VPM_SUPPORT + if (wc->vpm) { + if (!wc->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; + } + } else if ((status & 0xff00) != 0xff00) + __t4_check_vpm400(wc, (status & 0xff00) >> 8); + } +#endif + +#if 1 + __handle_leds(wc); +#endif + + if (wc->checktiming > 0) + __t4_set_timing_source_auto(wc); + if (wc->stopdma) { + /* Stop DMA cleanly if requested */ + wc->dmactrl = 0x0; + __t4_pci_out(wc, WC_DMACTRL, 0x00000000); + /* Acknowledge any pending interrupts */ + __t4_pci_out(wc, WC_INTR, 0x00000000); + __t4_set_timing_source(wc, 4); + wc->stopdma = 0x0; + } + spin_unlock_irqrestore(&wc->reglock, flags); + + if (needcheckvpm450 && vpmdtmfsupport) { + t4_check_vpm450(wc); + needcheckvpm450 = 0; + } + +#ifndef ENABLE_WORKQUEUES + t4_pci_out(wc, WC_INTR, 0); +#endif +#ifdef LINUX26 + return IRQ_RETVAL(1); +#endif +} + +#ifdef SUPPORT_GEN1 +static int t4_reset_dma(struct t4 *wc) +{ + /* Turn off DMA and such */ + wc->dmactrl = 0x0; + t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + t4_pci_out(wc, WC_COUNT, 0); + t4_pci_out(wc, WC_RDADDR, 0); + t4_pci_out(wc, WC_WRADDR, 0); + t4_pci_out(wc, WC_INTR, 0); + /* Turn it all back on */ + t4_pci_out(wc, WC_RDADDR, wc->readdma); + t4_pci_out(wc, WC_WRADDR, wc->writedma); + t4_pci_out(wc, WC_COUNT, ((ZT_MAX_CHUNKSIZE * 2 * 32 - 1) << 18) | ((ZT_MAX_CHUNKSIZE * 2 * 32 - 1) << 2)); + t4_pci_out(wc, WC_INTR, 0); +#ifdef VPM_SUPPORT + wc->dmactrl = 0xc0000000 | (1 << 29) | wc->vpm; +#else + wc->dmactrl = 0xc0000000 | (1 << 29); +#endif + if (noburst) + wc->dmactrl |= (1 << 26); + t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + return 0; +} +#endif + +#ifdef VPM_SUPPORT +static void t4_vpm_set_dtmf_threshold(struct t4 *wc, unsigned int threshold) +{ + unsigned int x; + + for (x = 0; x < 8; x++) { + t4_vpm_out(wc, x, 0xC4, (threshold >> 8) & 0xFF); + t4_vpm_out(wc, x, 0xC5, (threshold & 0xFF)); + } + printk("VPM: DTMF threshold set to %d\n", threshold); +} + +static unsigned int t4_vpm_mask(int chip) +{ + unsigned int mask=0; + switch(vpmspans) { + case 4: + mask = 0x55555555 << (chip >> 2); + break; + case 2: + mask = 0x11111111 << (chip >> 1); + break; + case 1: + mask = 0x01010101 << chip; + break; + } + return mask; +} + +static int t4_vpm_spanno(int chip) +{ + int spanno = 0; + switch(vpmspans) { + case 4: + spanno = chip & 0x3; + break; + case 2: + spanno = chip & 0x1; + break; + /* Case 1 is implicit */ + } + return spanno; +} + +static int t4_vpm_echotail(void) +{ + int echotail = 0x01ff; + switch(vpmspans) { + case 4: + echotail = 0x007f; + break; + case 2: + echotail = 0x00ff; + break; + /* Case 1 is implicit */ + } + return echotail; +} + +static void t4_vpm450_init(struct t4 *wc) +{ + unsigned int check1, check2; + int laws[4] = { 0, }; + int x; +#ifdef HOTPLUG_FIRMWARE + const struct firmware *firmware; +#endif + + if (!vpmsupport) { + printk("VPM450M: Support Disabled\n"); + return; + } + /* Turn on GPIO/DATA mux if supported */ + t4_gpio_setdir(wc, (1 << 24), (1 << 24)); + __t4_raw_oct_out(wc, 0x000a, 0x5678); + __t4_raw_oct_out(wc, 0x0004, 0x1234); + check1 = __t4_raw_oct_in(wc, 0x0004); + check2 = __t4_raw_oct_in(wc, 0x000a); + if (1 || debug) + printk("OCT Result: %04x/%04x\n", __t4_raw_oct_in(wc, 0x0004), __t4_raw_oct_in(wc, 0x000a)); + if (__t4_raw_oct_in(wc, 0x0004) != 0x1234) { + printk("VPM450: Not Present\n"); + return; + } +#ifdef HOTPLUG_FIRMWARE + if (request_firmware(&firmware, "OCT6114-128D.ima", &wc->dev->dev) != 0 || !firmware) { + printk("VPM450M: Firmware not found to be loaded\n"); + return; + } + printk("VPM450: Firmware of size %zd found\n", firmware->size); +#else + printk("VPM450: Using classic method of loading firmware\n"); +#endif + /* Setup alaw vs ulaw rules */ + for (x=0;xnumspans;x++) { + if (wc->tspans[x]->span.channels > 24) + laws[x] = 1; + } +#ifdef HOTPLUG_FIRMWARE + if (!(wc->vpm450m = init_vpm450m(wc, laws, firmware))) { +#else + if (!(wc->vpm450m = init_vpm450m(wc, laws))) { +#endif + printk("VPM450: Failed to initialize\n"); + return; + } +#ifdef HOTPLUG_FIRMWARE + release_firmware(firmware); +#endif + wc->vpm = T4_VPM_PRESENT; + printk("VPM450: Present and operational servicing %d span(s)\n", wc->numspans); +} + +static void t4_vpm400_init(struct t4 *wc) +{ + unsigned char reg; + unsigned int mask; + unsigned int ver; + unsigned int i, x, y, gen2vpm=0; + + if (!vpmsupport) { + printk("VPM400M: Support Disabled\n"); + return; + } + + switch(vpmspans) { + case 4: + case 2: + case 1: + break; + default: + printk("VPM: %d is not a valid vpmspans value, using 4\n", vpmspans); + vpmspans = 4; + } + + for (x=0;x<8;x++) { + int spanno = t4_vpm_spanno(x); + struct t4_span *ts = wc->tspans[spanno]; + int echotail = t4_vpm_echotail(); + + ver = t4_vpm_in(wc, x, 0x1a0); /* revision */ + if ((ver != 0x26) && (ver != 0x33)) { + printk("VPM400: %s\n", x ? "Inoperable" : "Not Present"); + return; + } + if (ver == 0x33) { + if (x && !gen2vpm) { + printk("VPM400: Inconsistent\n"); + return; + } + ts->spanflags |= FLAG_VPM2GEN; + gen2vpm++; + } else if (gen2vpm) { + printk("VPM400: Inconsistent\n"); + return; + } + + + /* Setup GPIO's */ + for (y=0;y<4;y++) { + t4_vpm_out(wc, x, 0x1a8 + y, 0x00); /* GPIO out */ + t4_vpm_out(wc, x, 0x1ac + y, 0x00); /* GPIO dir */ + t4_vpm_out(wc, x, 0x1b0 + y, 0x00); /* GPIO sel */ + } + + /* Setup TDM path - sets fsync and tdm_clk as inputs */ + reg = t4_vpm_in(wc, x, 0x1a3); /* misc_con */ + t4_vpm_out(wc, x, 0x1a3, reg & ~2); + + /* Setup timeslots */ + t4_vpm_out(wc, x, 0x02f, 0x20 | (spanno << 3)); + + /* Setup Echo length (128 taps) */ + t4_vpm_out(wc, x, 0x022, (echotail >> 8)); + t4_vpm_out(wc, x, 0x023, (echotail & 0xff)); + + /* Setup the tdm channel masks for all chips*/ + mask = t4_vpm_mask(x); + for (i = 0; i < 4; i++) + t4_vpm_out(wc, x, 0x30 + i, (mask >> (i << 3)) & 0xff); + + /* Setup convergence rate */ + reg = t4_vpm_in(wc,x,0x20); + reg &= 0xE0; + if (ts->spantype == TYPE_E1) { + if (x < vpmspans) + printk("VPM400: Span %d A-law mode\n", spanno); + reg |= 0x01; + } else { + if (x < vpmspans) + printk("VPM400: Span %d U-law mode\n", spanno); + reg &= ~0x01; + } + t4_vpm_out(wc,x,0x20,(reg | 0x20)); + + /* Initialize echo cans */ + for (i = 0 ; i < MAX_TDM_CHAN; i++) { + if (mask & (0x00000001 << i)) + t4_vpm_out(wc,x,i,0x00); + } + + wait_a_little(); + + /* Put in bypass mode */ + for (i = 0 ; i < MAX_TDM_CHAN ; i++) { + if (mask & (0x00000001 << i)) { + t4_vpm_out(wc,x,i,0x01); + } + } + + /* Enable bypass */ + for (i = 0 ; i < MAX_TDM_CHAN ; i++) { + if (mask & (0x00000001 << i)) + t4_vpm_out(wc,x,0x78 + i,0x01); + } + + /* set DTMF detection threshold */ + t4_vpm_set_dtmf_threshold(wc, dtmfthreshold); + + /* Enable DTMF detectors (always DTMF detect all spans) */ + for (i = 0; i < MAX_DTMF_DET; i++) { + t4_vpm_out(wc, x, 0x98 + i, 0x40 | (i * 2) | ((x < 4) ? 0 : 1)); + } + for (i = 0x34; i < 0x38; i++) + t4_vpm_out(wc, x, i, 0x00); + for (i = 0x3C; i < 0x40; i++) + t4_vpm_out(wc, x, i, 0x00); + + for (i = 0x48; i < 0x4B; i++) + t4_vpm_out(wc, x, i, 0x00); + for (i = 0x50; i < 0x53; i++) + t4_vpm_out(wc, x, i, 0x00); + for (i = 0xB8; i < 0xBE; i++) + t4_vpm_out(wc, x, i, 0xFF); + if (gen2vpm) { + for (i = 0xBE; i < 0xC0; i++) + t4_vpm_out(wc, x, i, 0xFF); + } else { + for (i = 0xBE; i < 0xC0; i++) + t4_vpm_out(wc, x, i, 0x00); + } + for (i = 0xC0; i < 0xC4; i++) + t4_vpm_out(wc, x, i, (x < 4) ? 0x55 : 0xAA); + + } + printk("VPM400%s: Present and operational servicing %d span(s)\n", (gen2vpm ? "(2nd Gen)" : ""), wc->numspans); + wc->vpm = T4_VPM_PRESENT; +} + +#endif + +static void t4_tsi_reset(struct t4 *wc) +{ + int x; + for (x=0;x<128;x++) { + wc->dmactrl &= ~0x00007fff; + wc->dmactrl |= (0x00004000 | (x << 7)); + t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + } + wc->dmactrl &= ~0x00007fff; + t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); +} + +/* Note that channels here start from 1 */ +static void t4_tsi_assign(struct t4 *wc, int fromspan, int fromchan, int tospan, int tochan) +{ + unsigned long flags; + int fromts, tots; + + fromts = (fromspan << 5) |(fromchan); + tots = (tospan << 5) | (tochan); + + if (!wc->t1e1) { + fromts += 4; + tots += 4; + } + spin_lock_irqsave(&wc->reglock, flags); + wc->dmactrl &= ~0x00007fff; + wc->dmactrl |= (0x00004000 | (tots << 7) | (fromts)); + __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + wc->dmactrl &= ~0x00007fff; + __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static void t4_tsi_unassign(struct t4 *wc, int tospan, int tochan) +{ + unsigned long flags; + int tots; + + tots = (tospan << 5) | (tochan); + + if (!wc->t1e1) + tots += 4; + spin_lock_irqsave(&wc->reglock, flags); + wc->dmactrl &= ~0x00007fff; + wc->dmactrl |= (0x00004000 | (tots << 7)); + __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + if (debug & DEBUG_TSI) + printk("Sending '%08x\n", wc->dmactrl); + wc->dmactrl &= ~0x00007fff; + __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + spin_unlock_irqrestore(&wc->reglock, flags); +} +static int t4_hardware_init_1(struct t4 *wc, int gen2) +{ + unsigned int version; + + version = t4_pci_in(wc, WC_VERSION); + printk("TE%dXXP version %08x, burst %s, slip debug: %s\n", wc->numspans, version, noburst ? "OFF" : "ON", debugslips ? "ON" : "OFF"); +#ifdef ENABLE_WORKQUEUES + printk("TE%dXXP running with work queues.\n", wc->numspans); +#endif + + /* 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 (ignored in Gen2) */ + if (gen2) { + t4_tsi_reset(wc); + } else { + t4_pci_out(wc, WC_COUNT, ((ZT_MAX_CHUNKSIZE * 2 * 32 - 1) << 18) | ((ZT_MAX_CHUNKSIZE * 2 * 32 - 1) << 2)); + } + + /* Reset pending interrupts */ + t4_pci_out(wc, WC_INTR, 0x00000000); + + /* 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; + return 0; +} + +static int t4_hardware_init_2(struct t4 *wc) +{ + int x; + unsigned int falcver; + + /* 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; + unsigned long flags; + if (wc->tspans[0]->span.flags & ZT_FLAG_REGISTERED) + return 0; + printk("TE%dXXP: Launching card: %d\n", wc->numspans, wc->order); + + /* Setup serial parameters and system interface */ + for (x=0;x<4;x++) + t4_serial_setup(wc, x); + + if (zt_register(&wc->tspans[0]->span, 0)) { + printk(KERN_ERR "Unable to register span %s\n", wc->tspans[0]->span.name); + return -1; + } + if (zt_register(&wc->tspans[1]->span, 0)) { + printk(KERN_ERR "Unable to register span %s\n", wc->tspans[1]->span.name); + zt_unregister(&wc->tspans[0]->span); + return -1; + } + + if (wc->numspans == 4) { + if (zt_register(&wc->tspans[2]->span, 0)) { + printk(KERN_ERR "Unable to register span %s\n", wc->tspans[2]->span.name); + zt_unregister(&wc->tspans[0]->span); + zt_unregister(&wc->tspans[1]->span); + return -1; + } + if (zt_register(&wc->tspans[3]->span, 0)) { + printk(KERN_ERR "Unable to register span %s\n", wc->tspans[3]->span.name); + zt_unregister(&wc->tspans[0]->span); + zt_unregister(&wc->tspans[1]->span); + zt_unregister(&wc->tspans[2]->span); + return -1; + } + } + wc->checktiming = 1; + spin_lock_irqsave(&wc->reglock, flags); + __t4_set_timing_source(wc,4); + spin_unlock_irqrestore(&wc->reglock, flags); +#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; + struct devtype *dt; + int x,f; + int basesize; +#if 0 + int y; + unsigned int *canary; +#endif + + + 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); + dt = (struct devtype *)(ent->driver_data); + if (dt->flags & FLAG_2NDGEN) + basesize = ZT_MAX_CHUNKSIZE * 32 * 4; + else + basesize = ZT_MAX_CHUNKSIZE * 32 * 2 * 4; + + if (dt->flags & FLAG_2PORT) + wc->numspans = 2; + else + wc->numspans = 4; + + wc->variety = dt->desc; + + wc->memaddr = pci_resource_start(pdev, 0); + wc->memlen = pci_resource_len(pdev, 0); + wc->membase = ioremap(wc->memaddr, wc->memlen); + /* This rids of the Double missed interrupt message after loading */ + wc->last0 = 1; +#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("wct%dxxp: Unable to request regions\n", wc->numspans); + + printk("Found TE%dXXP at base address %08lx, remapped to %p\n", wc->numspans, wc->memaddr, wc->membase); + + wc->dev = pdev; + + wc->writechunk = + /* 32 channels, Double-buffer, Read/Write, 4 spans */ + (unsigned int *)pci_alloc_consistent(pdev, basesize * 2, &wc->writedma); + if (!wc->writechunk) { + printk("wct%dxxp: Unable to allocate DMA-able memory\n", wc->numspans); + return -ENOMEM; + } + + /* Read is after the whole write piece (in words) */ + wc->readchunk = wc->writechunk + basesize / 4; + + /* Same thing but in bytes... */ + wc->readdma = wc->writedma + basesize; + + /* Initialize Write/Buffers to all blank data */ + memset((void *)wc->writechunk,0x00, basesize); + memset((void *)wc->readchunk,0xff, basesize); +#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_1(wc, dt->flags & FLAG_2NDGEN); + + 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; + + +#ifdef ENABLE_WORKQUEUES + if (dt->flags & FLAG_2NDGEN) { + char tmp[20]; + sprintf(tmp, "te%dxxp[%d]", wc->numspans, wc->num); + wc->workq = create_workqueue(tmp); + } +#endif + + /* Allocate pieces we need here */ + for (x=0;x<4;x++) { + if (wc->t1e1 & (1 << x)) { + wc->tspans[x] = kmalloc(sizeof(struct t4_span) + sizeof(struct zt_chan) * 31, GFP_KERNEL); + if (wc->tspans[x]) { + memset(wc->tspans[x], 0, sizeof(struct t4_span) + sizeof(struct zt_chan) * 31); + wc->tspans[x]->spantype = TYPE_E1; + } + } else { + wc->tspans[x] = kmalloc(sizeof(struct t4_span) + sizeof(struct zt_chan) * 24, GFP_KERNEL); + if (wc->tspans[x]) { + memset(wc->tspans[x], 0, sizeof(struct t4_span) + sizeof(struct zt_chan) * 24); + if (j1mode) + wc->tspans[x]->spantype = TYPE_J1; + else + wc->tspans[x]->spantype = TYPE_T1; + } + } + if (!wc->tspans[x]) + return -ENOMEM; +#ifdef ENABLE_WORKQUEUES + INIT_WORK(&wc->tspans[x]->swork, workq_handlespan, wc->tspans[x]); +#endif + wc->tspans[x]->spanflags |= dt->flags; + } + + + /* Continue hardware intiialization */ + t4_hardware_init_2(wc); + + +#ifdef SUPPORT_GEN1 + if (request_irq(pdev->irq, (dt->flags & FLAG_2NDGEN) ? t4_interrupt_gen2 :t4_interrupt, SA_INTERRUPT | SA_SHIRQ, (wc->numspans == 2) ? "wct2xxp" : "wct4xxp", wc)) +#else + if (!(wc->tspans[0]->spanflags & FLAG_2NDGEN)) { + printk("This driver does not support 1st gen modules\n"); + kfree(wc); + return -ENODEV; + } + if (request_irq(pdev->irq, t4_interrupt_gen2, SA_INTERRUPT | SA_SHIRQ, "t4xxp", wc)) +#endif + { + printk("t4xxp: Unable to request IRQ %d\n", pdev->irq); + kfree(wc); + return -EIO; + } + + 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); + wc->gpio = 0x00000000; + t4_pci_out(wc, WC_GPIO, wc->gpio); + t4_gpio_setdir(wc, (1 << 17), (1 << 17)); + t4_gpio_setdir(wc, (0xff), (0xff)); +#if 0 + for (x=0;x<0x10000;x++) { + __t4_raw_oct_out(wc, 0x0004, x); + __t4_raw_oct_out(wc, 0x000a, x ^ 0xffff); + if (__t4_raw_oct_in(wc, 0x0004) != x) + printk("Register 4 failed %04x\n", x); + if (__t4_raw_oct_in(wc, 0x000a) != (x ^ 0xffff)) + printk("Register 10 failed %04x\n", x); + } +#endif + res = 0; + } else + res = -ENOMEM; + } + return res; +} + +static int t4_hardware_stop(struct t4 *wc) +{ + + /* Turn off DMA, leave interrupts enabled */ + wc->stopdma = 1; + + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout((25 * HZ) / 1000); + + /* Turn off counter, address, etc */ + if (wc->tspans[0]->spanflags & FLAG_2NDGEN) { + t4_tsi_reset(wc); + } else { + t4_pci_out(wc, WC_COUNT, 0x000000); + } + t4_pci_out(wc, WC_RDADDR, 0x0000000); + t4_pci_out(wc, WC_WRADDR, 0x0000000); + wc->gpio = 0x00000000; + t4_pci_out(wc, WC_GPIO, wc->gpio); + t4_pci_out(wc, WC_LEDS, 0x00000000); + + printk("\nStopped TE%dXXP, Turned off DMA\n", wc->numspans); + return 0; +} + +static void __devexit t4_remove_one(struct pci_dev *pdev) +{ + struct t4 *wc = pci_get_drvdata(pdev); + int x; + if (wc) { + /* Stop hardware */ + t4_hardware_stop(wc); + + /* Release vpm450m */ + if (wc->vpm450m) + release_vpm450m(wc->vpm450m); + wc->vpm450m = NULL; + + /* Unregister spans */ + if (wc->tspans[0]->span.flags & ZT_FLAG_REGISTERED) + zt_unregister(&wc->tspans[0]->span); + if (wc->tspans[1]->span.flags & ZT_FLAG_REGISTERED) + zt_unregister(&wc->tspans[1]->span); + if (wc->numspans == 4) { + if (wc->tspans[2]->span.flags & ZT_FLAG_REGISTERED) + zt_unregister(&wc->tspans[2]->span); + if (wc->tspans[3]->span.flags & ZT_FLAG_REGISTERED) + zt_unregister(&wc->tspans[3]->span); + } +#ifdef ENABLE_WORKQUEUES + if (wc->workq) { + flush_workqueue(wc->workq); + destroy_workqueue(wc->workq); + } +#endif +#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); + for (x=0;xnumspans;x++) { + if (wc->tspans[x]) + kfree(wc->tspans[x]); + } + kfree(wc); + } +} + + +static struct pci_device_id t4_pci_tbl[] __devinitdata = +{ + { 0x10ee, 0x0314, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&wct4xxp }, + { 0xd161, 0x0410, 0x0003, PCI_ANY_ID, 0, 0, (unsigned long)&wct410p3 }, + { 0xd161, 0x0405, 0x0003, PCI_ANY_ID, 0, 0, (unsigned long)&wct405p3 }, + { 0xd161, 0x0410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&wct410p2 }, + { 0xd161, 0x0405, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&wct405p2 }, + { 0xd161, 0x0205, 0x0003, PCI_ANY_ID, 0, 0, (unsigned long)&wct205p3 }, + { 0xd161, 0x0210, 0x0003, PCI_ANY_ID, 0, 0, (unsigned long)&wct210p3 }, + { 0xd161, 0x0205, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&wct205 }, + { 0xd161, 0x0210, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&wct210 }, + { 0, } +}; + +static struct pci_driver t4_driver = { + name: "Unified t4xxp-t2xxp driver", + probe: t4_init_one, +#ifdef LINUX26 + remove: __devexit_p(t4_remove_one), +#else + remove: t4_remove_one, +#endif + suspend: NULL, + resume: NULL, + id_table: t4_pci_tbl, +}; + +static int __init t4_init(void) +{ + int res; + res = zap_pci_module(&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("Unified TE4XXP-TE2XXP PCI Driver"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +#ifdef LINUX26 +module_param(debug, int, 0600); +module_param(loopback, int, 0600); +module_param(noburst, int, 0600); +module_param(debugslips, int, 0600); +module_param(polling, int, 0600); +module_param(timingcable, int, 0600); +module_param(t1e1override, int, 0600); +module_param(alarmdebounce, int, 0600); +module_param(j1mode, int, 0600); +#ifdef VPM_SUPPORT +module_param(vpmsupport, int, 0600); +module_param(vpmdtmfsupport, int, 0600); +module_param(vpmspans, int, 0600); +module_param(dtmfthreshold, int, 0600); +#endif +#else +MODULE_PARM(debug, "i"); +MODULE_PARM(loopback, "i"); +MODULE_PARM(noburst, "i"); +MODULE_PARM(debugslips, "i"); +MODULE_PARM(polling, "i"); +MODULE_PARM(timingcable, "i"); +MODULE_PARM(t1e1override, "i"); +MODULE_PARM(alarmdebounce, "i"); +MODULE_PARM(j1mode, "i"); +#ifdef VPM_SUPPORT +MODULE_PARM(vpmsupport, "i"); +MODULE_PARM(vpmdtmfsupport, "i"); +MODULE_PARM(vpmspans, "i"); +MODULE_PARM(dtmfthreshold, "i"); +#endif +#endif + +MODULE_DEVICE_TABLE(pci, t4_pci_tbl); + +module_init(t4_init); +module_exit(t4_cleanup); diff --git a/wct4xxp/fw2h.c b/wct4xxp/fw2h.c new file mode 100644 index 0000000..5b6c73a --- /dev/null +++ b/wct4xxp/fw2h.c @@ -0,0 +1,47 @@ +#include +#include +#include +#include +#include +#include +int main(int argc, char *argv[]) +{ + char *c; + int fd; + FILE *f; + int res; + int x; + unsigned char buf[1024]; + if (argc != 3) { + fprintf(stderr, "Usage: fw2h "); + exit(1); + } + fd = open(argv[1], O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Unable to open '%s': %s\n", argv[1], strerror(errno)); + exit(1); + } + f = fopen(argv[2], "w+"); + if (!f) { + fprintf(stderr, "Unable to open '%s' for writing: %s\n", argv[2], strerror(errno)); + exit(1); + } + c = strrchr(argv[2], '.'); + if (c) *c = '\0'; + fprintf(f, "static unsigned char %s[] = {\t", argv[2]); + while ((res = read(fd, buf, sizeof(buf))) > 0) { + for (x = 0; x < res; x++) { + if (!(x % 16)) + fprintf(f, "\n\t"); + fprintf(f, "0x%02x, ", buf[x]); + } + } + fprintf(f, "\n};\n"); + if (res < 0) { + fprintf(stderr, "Error reading file: %s\n", strerror(errno)); + exit(1); + } + fclose(f); + close(fd); + exit(0); +} diff --git a/wct4xxp/vpm450m.c b/wct4xxp/vpm450m.c new file mode 100644 index 0000000..d423c12 --- /dev/null +++ b/wct4xxp/vpm450m.c @@ -0,0 +1,498 @@ +/* + * Copyright (C) 2005-2006 Digium, Inc. + * + * Mark Spencer + * + * All Rights Reserved + */ + +#include +#include +#include +#include +#ifdef HOTPLUG_FIRMWARE +#include +#else +#include "vpm450m_fw.h" +#endif + +#include "oct6100api/oct6100_api.h" +#include "vpm450m.h" + +/* API for Octasic access */ +UINT32 Oct6100UserGetTime(tPOCT6100_GET_TIME f_pTime) +{ + /* Why couldn't they just take a timeval like everyone else? */ + struct timeval tv; + unsigned long long total_usecs; + unsigned int mask = ~0; + + do_gettimeofday(&tv); + total_usecs = (((unsigned long long)(tv.tv_sec)) * 1000000) + + (((unsigned long long)(tv.tv_usec))); + f_pTime->aulWallTimeUs[0] = (total_usecs & mask); + f_pTime->aulWallTimeUs[1] = (total_usecs >> 32); + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserMemSet(PVOID f_pAddress, UINT32 f_ulPattern, UINT32 f_ulLength) +{ + memset(f_pAddress, f_ulPattern, f_ulLength); + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserMemCopy(PVOID f_pDestination, PVOID f_pSource, UINT32 f_ulLength) +{ + memcpy(f_pDestination, f_pSource, f_ulLength); + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserCreateSerializeObject(tPOCT6100_CREATE_SERIALIZE_OBJECT f_pCreate) +{ + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDestroySerializeObject(tPOCT6100_DESTROY_SERIALIZE_OBJECT f_pDestroy) +{ +#ifdef OCTASIC_DEBUG + printk("I should never be called! (destroy serialize object)\n"); +#endif + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserSeizeSerializeObject(tPOCT6100_SEIZE_SERIALIZE_OBJECT f_pSeize) +{ + /* Not needed */ + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserReleaseSerializeObject(tPOCT6100_RELEASE_SERIALIZE_OBJECT f_pRelease) +{ + /* Not needed */ + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDriverWriteApi(tPOCT6100_WRITE_PARAMS f_pWriteParams) +{ + oct_set_reg(f_pWriteParams->pProcessContext, f_pWriteParams->ulWriteAddress, f_pWriteParams->usWriteData); + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDriverWriteSmearApi(tPOCT6100_WRITE_SMEAR_PARAMS f_pSmearParams) +{ + unsigned int x; + for (x=0;xulWriteLength;x++) { + oct_set_reg(f_pSmearParams->pProcessContext, f_pSmearParams->ulWriteAddress + (x << 1), f_pSmearParams->usWriteData); + } + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDriverWriteBurstApi(tPOCT6100_WRITE_BURST_PARAMS f_pBurstParams) +{ + unsigned int x; + for (x=0;xulWriteLength;x++) { + oct_set_reg(f_pBurstParams->pProcessContext, f_pBurstParams->ulWriteAddress + (x << 1), f_pBurstParams->pusWriteData[x]); + } + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDriverReadApi(tPOCT6100_READ_PARAMS f_pReadParams) +{ + *(f_pReadParams->pusReadData) = oct_get_reg(f_pReadParams->pProcessContext, f_pReadParams->ulReadAddress); + return cOCT6100_ERR_OK; +} + +UINT32 Oct6100UserDriverReadBurstApi(tPOCT6100_READ_BURST_PARAMS f_pBurstParams) +{ + unsigned int x; + for (x=0;xulReadLength;x++) { + f_pBurstParams->pusReadData[x] = oct_get_reg(f_pBurstParams->pProcessContext, f_pBurstParams->ulReadAddress + (x << 1)); + } + return cOCT6100_ERR_OK; +} + +#define SOUT_G168_1100GB_ON 0x40000004 +#define SOUT_DTMF_1 0x40000011 +#define SOUT_DTMF_2 0x40000012 +#define SOUT_DTMF_3 0x40000013 +#define SOUT_DTMF_A 0x4000001A +#define SOUT_DTMF_4 0x40000014 +#define SOUT_DTMF_5 0x40000015 +#define SOUT_DTMF_6 0x40000016 +#define SOUT_DTMF_B 0x4000001B +#define SOUT_DTMF_7 0x40000017 +#define SOUT_DTMF_8 0x40000018 +#define SOUT_DTMF_9 0x40000019 +#define SOUT_DTMF_C 0x4000001C +#define SOUT_DTMF_STAR 0x4000001E +#define SOUT_DTMF_0 0x40000010 +#define SOUT_DTMF_POUND 0x4000001F +#define SOUT_DTMF_D 0x4000001D + +#define ROUT_G168_2100GB_ON 0x10000000 +#define ROUT_G168_2100GB_WSPR 0x10000002 +#define ROUT_SOUT_G168_2100HB_END 0x50000003 +#define ROUT_G168_1100GB_ON 0x10000004 + +#define ROUT_DTMF_1 0x10000011 +#define ROUT_DTMF_2 0x10000012 +#define ROUT_DTMF_3 0x10000013 +#define ROUT_DTMF_A 0x1000001A +#define ROUT_DTMF_4 0x10000014 +#define ROUT_DTMF_5 0x10000015 +#define ROUT_DTMF_6 0x10000016 +#define ROUT_DTMF_B 0x1000001B +#define ROUT_DTMF_7 0x10000017 +#define ROUT_DTMF_8 0x10000018 +#define ROUT_DTMF_9 0x10000019 +#define ROUT_DTMF_C 0x1000001C +#define ROUT_DTMF_STAR 0x1000001E +#define ROUT_DTMF_0 0x10000010 +#define ROUT_DTMF_POUND 0x1000001F +#define ROUT_DTMF_D 0x1000001D + +#if 0 +#define cOCT6100_ECHO_OP_MODE_DIGITAL cOCT6100_ECHO_OP_MODE_HT_FREEZE +#else +#define cOCT6100_ECHO_OP_MODE_DIGITAL cOCT6100_ECHO_OP_MODE_POWER_DOWN +#endif + +struct vpm450m { + tPOCT6100_INSTANCE_API pApiInstance; + UINT32 aulEchoChanHndl[ 128 ]; + int chanflags[128]; + int ecmode[128]; +}; + +#define FLAG_DTMF (1 << 0) +#define FLAG_MUTE (1 << 1) +#define FLAG_ECHO (1 << 2) + +static unsigned int tones[] = { + SOUT_DTMF_1, + SOUT_DTMF_2, + SOUT_DTMF_3, + SOUT_DTMF_A, + SOUT_DTMF_4, + SOUT_DTMF_5, + SOUT_DTMF_6, + SOUT_DTMF_B, + SOUT_DTMF_7, + SOUT_DTMF_8, + SOUT_DTMF_9, + SOUT_DTMF_C, + SOUT_DTMF_STAR, + SOUT_DTMF_0, + SOUT_DTMF_POUND, + SOUT_DTMF_D, + SOUT_G168_1100GB_ON, + + ROUT_DTMF_1, + ROUT_DTMF_2, + ROUT_DTMF_3, + ROUT_DTMF_A, + ROUT_DTMF_4, + ROUT_DTMF_5, + ROUT_DTMF_6, + ROUT_DTMF_B, + ROUT_DTMF_7, + ROUT_DTMF_8, + ROUT_DTMF_9, + ROUT_DTMF_C, + ROUT_DTMF_STAR, + ROUT_DTMF_0, + ROUT_DTMF_POUND, + ROUT_DTMF_D, + ROUT_G168_1100GB_ON, +}; + +static void vpm450m_setecmode(struct vpm450m *vpm450m, int channel, int mode) +{ + tOCT6100_CHANNEL_MODIFY modify; + UINT32 ulResult; + + if (vpm450m->ecmode[channel] == mode) + return; + Oct6100ChannelModifyDef(&modify); + modify.ulEchoOperationMode = mode; + modify.ulChannelHndl = vpm450m->aulEchoChanHndl[channel]; + ulResult = Oct6100ChannelModify(vpm450m->pApiInstance, &modify); + if (ulResult != GENERIC_OK) { + printk("Failed to apply echo can changes on channel %d!\n", channel); + } else { +#ifdef OCTASIC_DEBUG + printk("Echo can on channel %d set to %d\n", channel, mode); +#endif + vpm450m->ecmode[channel] = mode; + } +} + +void vpm450m_setdtmf(struct vpm450m *vpm450m, int channel, int detect, int mute) +{ + tOCT6100_CHANNEL_MODIFY modify; + UINT32 ulResult; + + Oct6100ChannelModifyDef(&modify); + modify.ulChannelHndl = vpm450m->aulEchoChanHndl[channel]; + if (mute) { + vpm450m->chanflags[channel] |= FLAG_MUTE; + modify.VqeConfig.fDtmfToneRemoval = TRUE; + } else { + vpm450m->chanflags[channel] &= ~FLAG_MUTE; + modify.VqeConfig.fDtmfToneRemoval = FALSE; + } + if (detect) + vpm450m->chanflags[channel] |= FLAG_DTMF; + else + vpm450m->chanflags[channel] &= ~FLAG_DTMF; + if (vpm450m->chanflags[channel] & (FLAG_DTMF|FLAG_MUTE)) { + if (!(vpm450m->chanflags[channel] & FLAG_ECHO)) { + vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_RESET); + vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_FREEZE); + } + } else { + if (!(vpm450m->chanflags[channel] & FLAG_ECHO)) + vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_DIGITAL); + } + + ulResult = Oct6100ChannelModify(vpm450m->pApiInstance, &modify); + if (ulResult != GENERIC_OK) { + printk("Failed to apply dtmf mute changes on channel %d!\n", channel); + } +/* printk("VPM450m: Setting DTMF on channel %d: %s / %s\n", channel, (detect ? "DETECT" : "NO DETECT"), (mute ? "MUTE" : "NO MUTE")); */ +} + +void vpm450m_setec(struct vpm450m *vpm450m, int channel, int eclen) +{ + if (eclen) { + vpm450m->chanflags[channel] |= FLAG_ECHO; + vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_RESET); + vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_NORMAL); + } else { + vpm450m->chanflags[channel] &= ~FLAG_ECHO; + if (vpm450m->chanflags[channel] & (FLAG_DTMF | FLAG_MUTE)) { + vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_RESET); + vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_HT_FREEZE); + } else + vpm450m_setecmode(vpm450m, channel, cOCT6100_ECHO_OP_MODE_DIGITAL); + } +/* printk("VPM450m: Setting EC on channel %d to %d\n", channel, eclen); */ +} + +int vpm450m_checkirq(struct vpm450m *vpm450m) +{ + tOCT6100_INTERRUPT_FLAGS InterruptFlags; + + Oct6100InterruptServiceRoutineDef(&InterruptFlags); + Oct6100InterruptServiceRoutine(vpm450m->pApiInstance, &InterruptFlags); + if (InterruptFlags.fToneEventsPending) { + printk("VPM450M: Tone events pending!\n"); + return 1; + } + return 0; +} + +int vpm450m_getdtmf(struct vpm450m *vpm450m, int *channel, int *tone, int *start) +{ + tOCT6100_TONE_EVENT tonefound; + tOCT6100_EVENT_GET_TONE tonesearch; + UINT32 ulResult; + + Oct6100EventGetToneDef(&tonesearch); + tonesearch.pToneEvent = &tonefound; + tonesearch.ulMaxToneEvent = 1; + ulResult = Oct6100EventGetTone(vpm450m->pApiInstance, &tonesearch); + if (tonesearch.ulNumValidToneEvent) { + if (channel) + *channel = tonefound.ulUserChanId; + if (tone) { + switch(tonefound.ulToneDetected) { + case SOUT_DTMF_1: + *tone = '1'; + break; + case SOUT_DTMF_2: + *tone = '2'; + break; + case SOUT_DTMF_3: + *tone = '3'; + break; + case SOUT_DTMF_A: + *tone = 'A'; + break; + case SOUT_DTMF_4: + *tone = '4'; + break; + case SOUT_DTMF_5: + *tone = '5'; + break; + case SOUT_DTMF_6: + *tone = '6'; + break; + case SOUT_DTMF_B: + *tone = 'B'; + break; + case SOUT_DTMF_7: + *tone = '7'; + break; + case SOUT_DTMF_8: + *tone = '8'; + break; + case SOUT_DTMF_9: + *tone = '9'; + break; + case SOUT_DTMF_C: + *tone = 'C'; + break; + case SOUT_DTMF_STAR: + *tone = '*'; + break; + case SOUT_DTMF_0: + *tone = '0'; + break; + case SOUT_DTMF_POUND: + *tone = '#'; + break; + case SOUT_DTMF_D: + *tone = 'D'; + break; + case SOUT_G168_1100GB_ON: + *tone = 'f'; + break; + default: + printk("Unknown tone value %08x\n", tonefound.ulToneDetected); + *tone = 'u'; + break; + } + } + if (start) + *start = (tonefound.ulEventType == cOCT6100_TONE_PRESENT); + return 1; + } + return 0; +} + +#ifdef HOTPLUG_FIRMWARE +struct vpm450m *init_vpm450m(void *wc, int *isalaw, const struct firmware *firmware) +#else +struct vpm450m *init_vpm450m(void *wc, int *isalaw) +#endif +{ + tOCT6100_CHIP_OPEN ChipOpen; + tOCT6100_GET_INSTANCE_SIZE InstanceSize; + tOCT6100_CHANNEL_OPEN ChannelOpen; + UINT32 ulResult; + struct vpm450m *vpm450m; + int x,y,law; + + vpm450m = kmalloc(sizeof(struct vpm450m), GFP_KERNEL); + if (!vpm450m) + return NULL; + memset(vpm450m, 0, sizeof(struct vpm450m)); + for (x=0;x<128;x++) + vpm450m->ecmode[x] = -1; + + Oct6100ChipOpenDef(&ChipOpen); + + /* Setup Chip Open Parameters */ + ChipOpen.ulUpclkFreq = cOCT6100_UPCLK_FREQ_33_33_MHZ; + Oct6100GetInstanceSizeDef(&InstanceSize); + + ChipOpen.pProcessContext = wc; +#ifdef HOTPLUG_FIRMWARE + ChipOpen.pbyImageFile = firmware->data; + ChipOpen.ulImageSize = firmware->size; +#else + ChipOpen.pbyImageFile = vpm450m_fw; + ChipOpen.ulImageSize = sizeof(vpm450m_fw); +#endif + ChipOpen.fEnableMemClkOut = TRUE; + ChipOpen.ulMemClkFreq = cOCT6100_MCLK_FREQ_133_MHZ; + ChipOpen.ulMaxChannels = 128; + ChipOpen.ulMemoryType = cOCT6100_MEM_TYPE_DDR; + ChipOpen.ulMemoryChipSize = cOCT6100_MEMORY_CHIP_SIZE_32MB; + ChipOpen.ulNumMemoryChips = 1; + ChipOpen.ulMaxTdmStreams = 4; + ChipOpen.aulTdmStreamFreqs[0] = cOCT6100_TDM_STREAM_FREQ_8MHZ; + ChipOpen.ulTdmSampling = cOCT6100_TDM_SAMPLE_AT_FALLING_EDGE; +#if 0 + ChipOpen.fEnableAcousticEcho = TRUE; +#endif + + ulResult = Oct6100GetInstanceSize(&ChipOpen, &InstanceSize); + if (ulResult != cOCT6100_ERR_OK) { + printk("Failed to get instance size, code %08x!\n", ulResult); + kfree(vpm450m); + return NULL; + } + + + vpm450m->pApiInstance = vmalloc(InstanceSize.ulApiInstanceSize); + if (!vpm450m->pApiInstance) { + printk("Out of memory (can't allocate %d bytes)!\n", InstanceSize.ulApiInstanceSize); + kfree(vpm450m); + return NULL; + } + + printk("Before chip open!\n"); + ulResult = Oct6100ChipOpen(vpm450m->pApiInstance, &ChipOpen); + printk("After chip open!\n"); + if (ulResult != cOCT6100_ERR_OK) { + printk("Failed to open chip, code %08x!\n", ulResult); + kfree(vpm450m); + return NULL; + } + for (x=0;x<128;x++) { + if (isalaw[x >> 5]) + law = cOCT6100_PCM_A_LAW; + else + law = cOCT6100_PCM_U_LAW; + Oct6100ChannelOpenDef(&ChannelOpen); + ChannelOpen.pulChannelHndl = &vpm450m->aulEchoChanHndl[x]; + ChannelOpen.ulUserChanId = x; + ChannelOpen.TdmConfig.ulRinPcmLaw = law; + ChannelOpen.TdmConfig.ulRinStream = 0; + ChannelOpen.TdmConfig.ulRinTimeslot = x; + ChannelOpen.TdmConfig.ulSinPcmLaw = law; + ChannelOpen.TdmConfig.ulSinStream = 1; + ChannelOpen.TdmConfig.ulSinTimeslot = x; + ChannelOpen.TdmConfig.ulSoutPcmLaw = law; + ChannelOpen.TdmConfig.ulSoutStream = 2; + ChannelOpen.TdmConfig.ulSoutTimeslot = x; + ChannelOpen.TdmConfig.ulRoutPcmLaw = law; + ChannelOpen.TdmConfig.ulRoutStream = 3; + ChannelOpen.TdmConfig.ulRoutTimeslot = x; + ChannelOpen.VqeConfig.fEnableNlp = TRUE; + ChannelOpen.VqeConfig.fRinDcOffsetRemoval = TRUE; + ChannelOpen.VqeConfig.fSinDcOffsetRemoval = TRUE; + + ChannelOpen.fEnableToneDisabler = TRUE; + ChannelOpen.ulEchoOperationMode = cOCT6100_ECHO_OP_MODE_DIGITAL; + + ulResult = Oct6100ChannelOpen(vpm450m->pApiInstance, &ChannelOpen); + if (ulResult != GENERIC_OK) { + printk("Failed to open channel %d!\n", x); + } + for (y=0;yaulEchoChanHndl[x]; + enable.ulToneNumber = tones[y]; + if (Oct6100ToneDetectionEnable(vpm450m->pApiInstance, &enable) != GENERIC_OK) + printk("Failed to enable tone detection on channel %d for tone %d!\n", x, y); + } + } + return vpm450m; +} + +void release_vpm450m(struct vpm450m *vpm450m) +{ + UINT32 ulResult; + tOCT6100_CHIP_CLOSE ChipClose; + Oct6100ChipCloseDef(&ChipClose); + ulResult = Oct6100ChipClose(vpm450m->pApiInstance, &ChipClose); + if (ulResult != cOCT6100_ERR_OK) { + printk("Failed to close chip, code %08x!\n", ulResult); + } + vfree(vpm450m->pApiInstance); + kfree(vpm450m); +} diff --git a/wct4xxp/vpm450m.h b/wct4xxp/vpm450m.h new file mode 100644 index 0000000..350ef1c --- /dev/null +++ b/wct4xxp/vpm450m.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2005-2006 Digium, Inc. + * + * Mark Spencer + * + * All Rights Reserved + */ + +#ifndef _VPM450M_H +#define _VPM450M_H + + +struct vpm450m; +#ifdef HOTPLUG_FIRMWARE +struct firmware; +#endif + +/* From driver */ +extern unsigned int oct_get_reg(void *data, unsigned int reg); +extern void oct_set_reg(void *data, unsigned int reg, unsigned int val); + +/* From vpm450m2 */ +#ifdef HOTPLUG_FIRMWARE +extern struct vpm450m *init_vpm450m(void *wc, int *isalaw, const struct firmware *firmware); +#else +extern struct vpm450m *init_vpm450m(void *wc, int *isalaw); +#endif +extern void vpm450m_setec(struct vpm450m *instance, int channel, int eclen); +extern void vpm450m_setdtmf(struct vpm450m *instance, int channel, int dtmfdetect, int dtmfmute); +extern int vpm450m_checkirq(struct vpm450m *vpm450m); +extern int vpm450m_getdtmf(struct vpm450m *vpm450m, int *channel, int *tone, int *start); +extern void release_vpm450m(struct vpm450m *instance); +#endif diff --git a/wct4xxp/wct4xxp-diag.c b/wct4xxp/wct4xxp-diag.c new file mode 100644 index 0000000..50a328d --- /dev/null +++ b/wct4xxp/wct4xxp-diag.c @@ -0,0 +1,414 @@ +#include +#include +#include +#include +#include +#include +#include +#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" }, + { 0x4f, "Old FRS1" }, + { 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 \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, ®s)) { + fprintf(stderr, "Unable to get registers: %s\n", strerror(errno)); + exit(1); + } + printf("PCI Registers:\n"); + for (x=0;x + * + * 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 + +#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) + -- cgit v1.2.3