[dahdi-commits] dahdi/linux.git branch "master" updated.

SVN commits to the DAHDI project dahdi-commits at lists.digium.com
Wed May 29 10:50:41 CDT 2013


branch "master" has been updated
       via  e2f492595c9191ba6d556ccac1bde4c1bb892938 (commit)
      from  1f20b5f8fa3c0c28ad1c0fac51050e1fba16e7c8 (commit)

Summary of changes:
 drivers/dahdi/Kbuild            |    8 +
 drivers/dahdi/firmware/Makefile |   50 +-
 drivers/dahdi/wcte13xp-base.c   | 3243 +++++++++++++++++++++++++++++++++++++++
 3 files changed, 3299 insertions(+), 2 deletions(-)
 create mode 100644 drivers/dahdi/wcte13xp-base.c


- Log -----------------------------------------------------------------
commit e2f492595c9191ba6d556ccac1bde4c1bb892938
Author: Russ Meyerriecks <rmeyerriecks at digium.com>
Date:   Mon May 20 16:46:21 2013 -0500

    wcte13xp: New driver for digium's te13x product range
    
    This patch adds support for Digium's new single span T1/E1 cards with built-in
    octasic echo canceler.
    
    PCI IDs:
    d161:800a te133 - pci express
    d161:800b te134 - pci
    
    Signed-off-by: Russ Meyerriecks <rmeyerriecks at digium.com>

diff --git a/drivers/dahdi/Kbuild b/drivers/dahdi/Kbuild
index f9152d7..5450dff 100644
--- a/drivers/dahdi/Kbuild
+++ b/drivers/dahdi/Kbuild
@@ -11,6 +11,14 @@ obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCT4XXP)		+= wct4xxp/
 obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTC4XXP)		+= wctc4xxp/
 obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTDM24XXP)	+= wctdm24xxp/
 obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTE12XP)		+= wcte12xp/
+obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTE13XP)		+= wcte13xp.o
+
+wcte13xp-objs := wcte13xp-base.o oct612x/lib.a
+CFLAGS_wcte13xp-base.o += -I$(src)/oct612x/include -I$(src)/oct612x/octdeviceapi -I$(src)/oct612x/octdeviceapi/oct6100api
+ifeq ($(HOTPLUG_FIRMWARE),yes)
+  CFLAGS_wcte13xp-base.o += -DHOTPLUG_FIRMWARE
+endif
+
 obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCTDM)		+= wctdm.o
 obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_VOICEBUS)		+= voicebus/
 obj-$(DAHDI_BUILD_ALL)$(CONFIG_DAHDI_WCB4XXP)		+= wcb4xxp/
diff --git a/drivers/dahdi/firmware/Makefile b/drivers/dahdi/firmware/Makefile
index e9ba388..fc7efeb 100644
--- a/drivers/dahdi/firmware/Makefile
+++ b/drivers/dahdi/firmware/Makefile
@@ -22,6 +22,7 @@
 
 .PHONY: dist-clean clean all uninstall have_download install object-build hotplug-install hotplug-dirs hotplug-uninstall make_firmware_object firmware-loaders
 
+OCT6114_032_VERSION:=1.05.01
 OCT6114_064_VERSION:=1.05.01
 OCT6114_128_VERSION:=1.05.01
 OCT6114_256_VERSION:=1.05.01
@@ -30,10 +31,12 @@ VPMADT032_VERSION:=1.25.0
 HX8_VERSION:=2.06
 VPMOCT032_VERSION:=1.12.0
 WCT820_VERSION:=1.76
+TE133_VERSION:=6f0017
+TE134_VERSION:=6f0017
 
 FIRMWARE_URL:=http://downloads.digium.com/pub/telephony/firmware/releases
 
-ALL_FIRMWARE=FIRMWARE-OCT6114-064 FIRMWARE-OCT6114-128 FIRMWARE-OCT6114-256 FIRMWARE-TC400M FIRMWARE-HX8 FIRMWARE-VPMOCT032 FIRMWARE-TE820
+ALL_FIRMWARE=FIRMWARE-OCT6114-032 FIRMWARE-OCT6114-064 FIRMWARE-OCT6114-128 FIRMWARE-OCT6114-256 FIRMWARE-TC400M FIRMWARE-HX8 FIRMWARE-VPMOCT032 FIRMWARE-TE820 FIRMWARE-TE133 FIRMWARE-TE134
 
 # Firmware files should use the naming convention: dahdi-fw-<base name>-<sub name>-<version> or dahdi-fw-<base name>-<version>
 # First example: dahdi-fw-oct6114-064-1.05.01
@@ -42,17 +45,21 @@ ALL_FIRMWARE=FIRMWARE-OCT6114-064 FIRMWARE-OCT6114-128 FIRMWARE-OCT6114-256 FIRM
 # This means this is version MR5.6 of the tc400m firmware
 
 # Build a list of firmware package filenames we need
-FIRMWARE:=$(ALL_FIRMWARE:FIRMWARE-OCT6114-064=dahdi-fw-oct6114-064-$(OCT6114_064_VERSION).tar.gz)
+FIRMWARE:=$(ALL_FIRMWARE:FIRMWARE-OCT6114-032=dahdi-fw-oct6114-032-$(OCT6114_032_VERSION).tar.gz)
+FIRMWARE:=$(FIRMWARE:FIRMWARE-OCT6114-064=dahdi-fw-oct6114-064-$(OCT6114_064_VERSION).tar.gz)
 FIRMWARE:=$(FIRMWARE:FIRMWARE-OCT6114-128=dahdi-fw-oct6114-128-$(OCT6114_128_VERSION).tar.gz)
 FIRMWARE:=$(FIRMWARE:FIRMWARE-OCT6114-256=dahdi-fw-oct6114-256-$(OCT6114_256_VERSION).tar.gz)
 FIRMWARE:=$(FIRMWARE:FIRMWARE-TC400M=dahdi-fw-tc400m-$(TC400M_VERSION).tar.gz)
 FIRMWARE:=$(FIRMWARE:FIRMWARE-HX8=dahdi-fw-hx8-$(HX8_VERSION).tar.gz)
 FIRMWARE:=$(FIRMWARE:FIRMWARE-VPMOCT032=dahdi-fw-vpmoct032-$(VPMOCT032_VERSION).tar.gz)
 FIRMWARE:=$(FIRMWARE:FIRMWARE-TE820=dahdi-fw-te820-$(WCT820_VERSION).tar.gz)
+FIRMWARE:=$(FIRMWARE:FIRMWARE-TE133=dahdi-fw-te133-$(TE133_VERSION).tar.gz)
+FIRMWARE:=$(FIRMWARE:FIRMWARE-TE134=dahdi-fw-te134-$(TE134_VERSION).tar.gz)
 
 FWLOADERS:=dahdi-fwload-vpmadt032-$(VPMADT032_VERSION).tar.gz
 
 # Build a list of object files if hotplug will not be used
+OBJECT_FILES:=$(ALL_FIRMWARE:FIRMWARE-OCT6114-032=dahdi-fw-oct6114-032.o)
 OBJECT_FILES:=$(ALL_FIRMWARE:FIRMWARE-OCT6114-064=dahdi-fw-oct6114-064.o)
 OBJECT_FILES:=$(OBJECT_FILES:FIRMWARE-OCT6114-128=dahdi-fw-oct6114-128.o)
 OBJECT_FILES:=$(OBJECT_FILES:FIRMWARE-OCT6114-256=dahdi-fw-oct6114-256.o)
@@ -107,6 +114,17 @@ $(DESTDIR)/usr/lib/hotplug/firmware $(DESTDIR)/lib/firmware:
 
 # Install all downloaded firmware images for hotplug usage
 hotplug-install: $(DESTDIR)/usr/lib/hotplug/firmware $(DESTDIR)/lib/firmware $(FIRMWARE)
+ifeq ($(shell if ( [ -f $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-oct6114-032-$(OCT6114_032_VERSION) ] ) && ( [ -f $(DESTDIR)/lib/firmware/.dahdi-fw-oct6114-032-$(OCT6114_032_VERSION) ] ); then echo "no"; else echo "yes"; fi),yes)
+	@echo "Installing dahdi-fw-oct6114-032.bin to hotplug firmware directories"
+	@install -m 644 dahdi-fw-oct6114-032.bin $(DESTDIR)/usr/lib/hotplug/firmware
+	@rm -rf $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-oct6114-032-*
+	@touch $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-oct6114-032-$(OCT6114_032_VERSION)
+	@install -m 644 dahdi-fw-oct6114-032.bin $(DESTDIR)/lib/firmware
+	@rm -rf $(DESTDIR)/lib/firmware/.dahdi-fw-oct6114-032-*
+	@touch $(DESTDIR)/lib/firmware/.dahdi-fw-oct6114-032-$(OCT6114_032_VERSION)
+else
+	@echo "Firmware dahdi-fw-oct6114-032.bin is already installed with required version $(OCT6114_032_VERSION)"
+endif
 ifeq ($(shell if ( [ -f $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-oct6114-064-$(OCT6114_064_VERSION) ] ) && ( [ -f $(DESTDIR)/lib/firmware/.dahdi-fw-oct6114-064-$(OCT6114_064_VERSION) ] ); then echo "no"; else echo "yes"; fi),yes)
 	@echo "Installing dahdi-fw-oct6114-064.bin to hotplug firmware directories"
 	@install -m 644 dahdi-fw-oct6114-064.bin $(DESTDIR)/usr/lib/hotplug/firmware
@@ -185,6 +203,29 @@ else
 	@echo "Firmware dahdi-fw-te820.bin is already installed with required version $(WCT820_VERSION)"
 endif
 
+ifeq ($(shell if ( [ -f $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-te133-$(TE133_VERSION) ] ) && ( [ -f $(DESTDIR)/lib/firmware/.dahdi-fw-te133-$(TE133_VERSION) ] ); then echo "no"; else echo "yes"; fi),yes)
+	@echo "Installing dahdi-fw-te133.bin to hotplug firmware directories"
+	@install -m 644 dahdi-fw-te133.bin $(DESTDIR)/usr/lib/hotplug/firmware
+	@rm -rf $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-te133-*
+	@touch $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-te133-$(TE133_VERSION)
+	@install -m 644 dahdi-fw-te133.bin $(DESTDIR)/lib/firmware
+	@rm -rf $(DESTDIR)/lib/firmware/.dahdi-fw-te133-*
+	@touch $(DESTDIR)/lib/firmware/.dahdi-fw-te133-$(TE133_VERSION)
+else
+	@echo "Firmware dahdi-fw-te133.bin is already installed with required version $(TE133_VERSION)"
+endif
+ifeq ($(shell if ( [ -f $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-te134-$(TE134_VERSION) ] ) && ( [ -f $(DESTDIR)/lib/firmware/.dahdi-fw-te134-$(TE134_VERSION) ] ); then echo "no"; else echo "yes"; fi),yes)
+	@echo "Installing dahdi-fw-te134.bin to hotplug firmware directories"
+	@install -m 644 dahdi-fw-te134.bin $(DESTDIR)/usr/lib/hotplug/firmware
+	@rm -rf $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-te134-*
+	@touch $(DESTDIR)/usr/lib/hotplug/firmware/.dahdi-fw-te134-$(TE134_VERSION)
+	@install -m 644 dahdi-fw-te134.bin $(DESTDIR)/lib/firmware
+	@rm -rf $(DESTDIR)/lib/firmware/.dahdi-fw-te134-*
+	@touch $(DESTDIR)/lib/firmware/.dahdi-fw-te134-$(TE134_VERSION)
+else
+	@echo "Firmware dahdi-fw-te134.bin is already installed with required version $(TE134_VERSION)"
+endif
+
 # Uninstall any installed dahdi firmware images from hotplug firmware directories
 hotplug-uninstall:
 	if [ -d $(DESTDIR)/usr/lib/hotplug/firmware ]; then \
@@ -202,6 +243,11 @@ make_firmware_object: make_firmware_object.in ../dahdi-base.o
 	 sed -e s/BFDNAME/$${BFDNAME}/ -e s/BFDARCH/$${BFDARCH}/ $< > $@
 	@chmod +x $@
 
+# Build object file of an oct6114 032 firmware image for linking
+dahdi-fw-oct6114-032.o: dahdi-fw-oct6114-032-$(OCT6114_032_VERSION).tar.gz dahdi-fw-oct6114-032.bin make_firmware_object
+	@echo Making firmware object file for dahdi-fw-oct6114-032.bin
+	./make_firmware_object dahdi-fw-oct6114-032.bin $@
+
 # Build object file of an oct6114 064 firmware image for linking
 dahdi-fw-oct6114-064.o: dahdi-fw-oct6114-064-$(OCT6114_064_VERSION).tar.gz dahdi-fw-oct6114-064.bin make_firmware_object
 	@echo Making firmware object file for dahdi-fw-oct6114-064.bin
diff --git a/drivers/dahdi/wcte13xp-base.c b/drivers/dahdi/wcte13xp-base.c
new file mode 100644
index 0000000..eb2850b
--- /dev/null
+++ b/drivers/dahdi/wcte13xp-base.c
@@ -0,0 +1,3243 @@
+/*
+ * Digium, Inc.  Wildcard te13xp T1/E1 card Driver
+ *
+ * Written by Russ Meyerriecks <rmeyerriecks at digium.com>
+ * Copyright (C) 2012 - 2013, Digium, Inc.
+ * All rights reserved.
+ *
+ */
+
+/*
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2 as published by the
+ * Free Software Foundation. See the LICENSE file included with
+ * this program for more details.
+ */
+
+#define pr_fmt(fmt)             KBUILD_MODNAME ": " fmt
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/moduleparam.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/crc32.h>
+
+#include <stdbool.h>
+#include <dahdi/kernel.h>
+
+#include "wct4xxp/wct4xxp.h"	/* For certain definitions */
+
+#define VPM_SUPPORT
+#define WC_MAX_IFACES 8
+
+enum linemode {
+	T1 = 1,
+	E1,
+	J1,
+};
+
+/* FPGA Status definitions */
+#define OCT_CPU_RESET		(1 << 0)
+#define OCT_CPU_DRAM_CKE	(1 << 1)
+#define STATUS_LED_GREEN	(1 << 9)
+#define STATUS_LED_RED		(1 << 10)
+#define FALC_CPU_RESET		(1 << 11)
+
+/* Descriptor ring definitions */
+#define DRING_SIZE (1 << 7) /* Must be in multiples of 2 */
+#define DRING_SIZE_MASK (DRING_SIZE-1)
+#define DESC_EOR (1 << 0)
+#define DESC_INT (1 << 1)
+#define DESC_OWN (1 << 31)
+#define DESC_DEFAULT_STATUS 0xdeadbeef
+#define DMA_CHAN_SIZE 128
+
+/* DMA definitions */
+#define TDM_DRING_ADDR 0x2000
+#define TDM_CONTROL (TDM_DRING_ADDR + 0x4)
+#define ENABLE_ECHOCAN_TDM	(1 << 0)
+#define TDM_RECOVER_CLOCK	(1 << 1)
+#define ENABLE_DMA		(1 << 2)
+#define	DMA_RUNNING		(1 << 3)
+#define DMA_LOOPBACK		(1 << 4)
+#define AUTHENTICATED		(1 << 5)
+#define TDM_VERSION (TDM_DRING_ADDR + 0x24)
+
+/* Interrupt definitions */
+#define INTERRUPT_CONTROL 0x300
+#define ISR (INTERRUPT_CONTROL + 0x0)
+#define IPR (INTERRUPT_CONTROL + 0x4)
+#define IER (INTERRUPT_CONTROL + 0x8)
+#define IAR (INTERRUPT_CONTROL + 0xc)
+#define SIE (INTERRUPT_CONTROL + 0x10)
+#define CIE (INTERRUPT_CONTROL + 0x14)
+#define IVR (INTERRUPT_CONTROL + 0x18)
+#define MER (INTERRUPT_CONTROL + 0x1c)
+#define MER_ME			(1<<0)
+#define MER_HIE			(1<<1)
+#define DESC_UNDERRUN		(1<<0)
+#define DESC_COMPLETE		(1<<1)
+#define OCT_INT			(1<<2)
+#define FALC_INT		(1<<3)
+#define SPI_INT			(1<<4)
+
+struct t13x_hw_desc {
+	volatile __be32 status;
+	__be32 tx_buf;
+	__be32 rx_buf;
+	volatile __be32 control;
+} __packed;
+
+struct t13x_meta_desc {
+	void *tx_buf_virt;
+	void *rx_buf_virt;
+};
+
+struct t13x {
+	struct pci_dev *dev;
+	spinlock_t reglock;
+	unsigned long framecount;
+	void __iomem *membase;
+
+	struct t13x_meta_desc *meta_dring;
+	struct t13x_hw_desc *hw_dring;
+	unsigned int dma_head;
+	unsigned int dma_tail;
+	dma_addr_t hw_dring_phys;
+	struct dma_pool *pool;
+	u8 latency;
+
+	const struct t13x_desc *devtype;
+	struct {
+		unsigned int nmf:1;
+		unsigned int sendingyellow:1;
+	} flags;
+	unsigned char txsigs[16];	/* Copy of tx sig registers */
+	int alarmcount;			/* How much red alarm we've seen */
+	int losalarmcount;
+	int aisalarmcount;
+	int yelalarmcount;
+	const char *name;
+	unsigned long blinktimer;
+	int loopupcnt;
+	int loopdowncnt;
+#define INITIALIZED    1
+#define SHUTDOWN       2
+#define READY	       3
+#define LATENCY_LOCKED 4
+	unsigned long bit_flags;
+	unsigned long alarmtimer;
+	u32 ledstate;
+	struct dahdi_device *ddev;
+	struct dahdi_span span;			/* Span */
+	struct dahdi_chan *chans[32];		/* Channels */
+	struct dahdi_echocan_state *ec[32];	/* Echocan state for channels */
+
+	/* protected by t1.reglock */
+	struct timer_list timer;
+	struct work_struct timer_work;
+	struct workqueue_struct *wq;
+	unsigned int not_ready;	/* 0 when entire card is ready to go */
+#ifdef VPM_SUPPORT
+	struct vpm450m *vpm;
+#endif
+	struct mutex lock;
+};
+
+static void reset_dring(struct t13x *);
+
+/* Maintenance Mode Registers */
+#define LIM0		0x36
+#define LIM0_LL		(1<<1)
+#define LIM1		0x37
+#define LIM1_RL	(1<<1)
+#define LIM1_JATT	(1<<2)
+
+/* Clear Channel Registers */
+#define CCB1		0x2f
+#define CCB2		0x30
+#define CCB3		0x31
+
+/* pci memory map offsets */
+#define DMA1		0x00	/* dma addresses */
+#define DMA2		0x04
+#define DMA3		0x08
+#define CNTL		0x0C	/* fpga control register */
+#define INT_MASK	0x10	/* interrupt vectors from oct and framer */
+#define INT_STAT	0x14
+#define FRAMER_BASE	0x00000800	/* framer's address space */
+
+#define TE13X_DEFAULT_LATENCY 3
+
+static int debug;
+static int alarmdebounce = 2500; /* LOF/LFA def to 2.5s AT&T TR54016*/
+static int losalarmdebounce = 2500; /* LOS def to 2.5s AT&T TR54016*/
+static int aisalarmdebounce = 2500; /* AIS(blue) def to 2.5s AT&T TR54016*/
+static int yelalarmdebounce = 500; /* RAI(yellow) def to 0.5s AT&T devguide */
+static char *default_linemode = "t1"; /* 'e1', 't1', or 'j1' */
+static int force_firmware;
+static int latency = TE13X_DEFAULT_LATENCY;
+
+struct t13x_firm_header {
+	u8	header[6];
+	__le32	chksum;
+	u8	pad[18];
+	__le32	version;
+} __packed;
+
+#ifdef VPM_SUPPORT
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/string.h>
+#include <linux/time.h>
+#include <linux/version.h>
+#include <linux/firmware.h>
+#include "oct6100api/oct6100_api.h"
+
+#define ECHOCAN_NUM_CHANS 32
+
+#define FLAGBIT_DTMF	 1
+#define FLAGBIT_MUTE	 2
+#define FLAGBIT_ECHO	 3
+#define FLAGBIT_ALAW	 4
+
+#define OCT_CHIP_ID			0
+#define OCT_MAX_TDM_STREAMS		4
+#define OCT_TONEEVENT_BUFFER_SIZE	128
+#define SOUT_STREAM			1
+#define RIN_STREAM			0
+#define SIN_STREAM			2
+
+#define OCT_OFFSET		(wc->membase + 0x10000)
+#define OCT_CONTROL_REG		(OCT_OFFSET + 0)
+#define OCT_DATA_REG		(OCT_OFFSET + 0x4)
+#define OCT_ADDRESS_HIGH_REG	(OCT_OFFSET + 0x8)
+#define OCT_ADDRESS_LOW_REG	(OCT_OFFSET + 0xa)
+#define OCT_DIRECT_WRITE_MASK	0x3001
+#define OCT_INDIRECT_READ_MASK	0x0101
+#define OCT_INDIRECT_WRITE_MASK	0x3101
+
+
+static int vpmsupport = 1;
+static const char *vpm_name = "VPMOCT032";
+static void t13x_vpm_init(struct t13x *wc);
+static void echocan_free(struct dahdi_chan *chan,
+		struct dahdi_echocan_state *ec);
+static const struct dahdi_echocan_features vpm_ec_features = {
+	.NLP_automatic = 1,
+	.CED_tx_detect = 1,
+	.CED_rx_detect = 1,
+};
+static const struct dahdi_echocan_ops vpm_ec_ops = {
+	.echocan_free = echocan_free,
+};
+
+struct vpm450m {
+	tPOCT6100_INSTANCE_API pApiInstance;
+	UINT32 aulEchoChanHndl[ECHOCAN_NUM_CHANS];
+	int ecmode[ECHOCAN_NUM_CHANS];
+	unsigned long chanflags[ECHOCAN_NUM_CHANS];
+};
+
+static void oct_reset(struct t13x *wc)
+{
+	unsigned long flags;
+	int reg;
+	spin_lock_irqsave(&wc->reglock, flags);
+	reg = ioread32be(wc->membase);
+	iowrite32be((reg & ~OCT_CPU_RESET), wc->membase);
+	spin_unlock_irqrestore(&wc->reglock, flags);
+
+	msleep_interruptible(1);
+
+	spin_lock_irqsave(&wc->reglock, flags);
+	reg = ioread32be(wc->membase);
+	iowrite32be((reg | OCT_CPU_RESET), wc->membase);
+	spin_unlock_irqrestore(&wc->reglock, flags);
+
+	dev_info(&wc->dev->dev, "Reset octasic\n");
+}
+
+static void oct_enable_dram(struct t13x *wc)
+{
+	unsigned long flags;
+	int reg;
+	spin_lock_irqsave(&wc->reglock, flags);
+	reg = ioread32be(wc->membase);
+	iowrite32be((reg | OCT_CPU_DRAM_CKE), wc->membase);
+	spin_unlock_irqrestore(&wc->reglock, flags);
+}
+
+static unsigned int oct_get_reg_indirect(void *data, uint32_t address)
+{
+	struct t13x *wc = data;
+	uint16_t highaddress = ((address >> 20) & 0xfff);
+	uint16_t lowaddress = ((address >> 4) & 0xfffff);
+	unsigned long stop = jiffies + HZ/10;
+	unsigned long flags;
+	uint16_t ret;
+
+	spin_lock_irqsave(&wc->reglock, flags);
+	iowrite16be(highaddress, OCT_ADDRESS_HIGH_REG);
+	iowrite16be(lowaddress, OCT_ADDRESS_LOW_REG);
+
+	iowrite16be(OCT_INDIRECT_READ_MASK | ((address & 0xe) << 8),
+				OCT_CONTROL_REG);
+	do {
+		ret = ioread16be(OCT_CONTROL_REG);
+	} while ((ret & (1<<8)) && time_before(jiffies, stop));
+
+	WARN_ON_ONCE(time_after_eq(jiffies, stop));
+
+	ret = ioread16be(OCT_DATA_REG);
+	spin_unlock_irqrestore(&wc->reglock, flags);
+
+	return ret;
+}
+
+static void oct_set_reg_indirect(void *data, uint32_t address, uint16_t val)
+{
+	struct t13x *wc = data;
+	unsigned long flags;
+	uint16_t ret;
+	uint16_t highaddress = ((address >> 20) & 0xfff);
+	uint16_t lowaddress = ((address >> 4) & 0xffff);
+	unsigned long stop = jiffies + HZ/10;
+
+	spin_lock_irqsave(&wc->reglock, flags);
+	iowrite16be(highaddress, OCT_ADDRESS_HIGH_REG);
+	iowrite16be(lowaddress, OCT_ADDRESS_LOW_REG);
+
+	iowrite16be(val, OCT_DATA_REG);
+	iowrite16be(OCT_INDIRECT_WRITE_MASK | ((address & 0xe) << 8),
+			OCT_CONTROL_REG);
+
+	/* No write should take longer than 100ms */
+	do {
+		ret = ioread16be(OCT_CONTROL_REG);
+	} while ((ret & (1<<8)) && time_before(jiffies, stop));
+	spin_unlock_irqrestore(&wc->reglock, flags);
+
+	WARN_ON_ONCE(time_after_eq(jiffies, stop));
+}
+
+/* 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, const void *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
+	pr_debug("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_indirect(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; x < f_pSmearParams->ulWriteLength; x++) {
+		oct_set_reg_indirect(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; x < f_pBurstParams->ulWriteLength; x++) {
+		oct_set_reg_indirect(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_indirect(
+			f_pReadParams->pProcessContext,
+			f_pReadParams->ulReadAddress);
+	return cOCT6100_ERR_OK;
+}
+
+UINT32 Oct6100UserDriverReadBurstApi(tPOCT6100_READ_BURST_PARAMS f_pBurstParams)
+{
+	unsigned int x;
+	for (x = 0; x < f_pBurstParams->ulReadLength; x++) {
+		f_pBurstParams->pusReadData[x] = oct_get_reg_indirect(
+				f_pBurstParams->pProcessContext,
+				f_pBurstParams->ulReadAddress + (x << 1));
+	}
+	return cOCT6100_ERR_OK;
+}
+
+static void vpm450m_setecmode(struct vpm450m *vpm450m, int channel, int mode)
+{
+	tOCT6100_CHANNEL_MODIFY *modify;
+	UINT32 ulResult;
+
+	if (vpm450m->ecmode[channel] == mode)
+		return;
+	modify = kzalloc(sizeof(tOCT6100_CHANNEL_MODIFY), GFP_ATOMIC);
+	if (!modify) {
+		pr_notice("Unable to allocate memory for setec!\n");
+		return;
+	}
+	Oct6100ChannelModifyDef(modify);
+	modify->ulEchoOperationMode = mode;
+	modify->ulChannelHndl = vpm450m->aulEchoChanHndl[channel];
+	ulResult = Oct6100ChannelModify(vpm450m->pApiInstance, modify);
+	if (ulResult != GENERIC_OK) {
+		pr_notice("Failed to apply echo can changes on channel %d %d %08x!\n",
+			  vpm450m->aulEchoChanHndl[channel], channel, ulResult);
+	} else {
+#ifdef OCTASIC_DEBUG
+		pr_debug("Echo can on channel %d set to %d\n", channel, mode);
+#endif
+		vpm450m->ecmode[channel] = mode;
+	}
+	kfree(modify);
+}
+
+static void vpm450m_set_alaw_companding(struct vpm450m *vpm450m, int channel,
+					bool alaw)
+{
+	tOCT6100_CHANNEL_MODIFY *modify;
+	UINT32 ulResult;
+	UINT32		law_to_use = (alaw) ? cOCT6100_PCM_A_LAW :
+					      cOCT6100_PCM_U_LAW;
+
+	/* If we're already in this companding mode, no need to do anything. */
+	if (alaw == (test_bit(FLAGBIT_ALAW, &vpm450m->chanflags[channel]) > 0))
+		return;
+
+	modify = kzalloc(sizeof(tOCT6100_CHANNEL_MODIFY), GFP_ATOMIC);
+	if (!modify) {
+		pr_notice("Unable to allocate memory for setec!\n");
+		return;
+	}
+
+	Oct6100ChannelModifyDef(modify);
+	modify->ulChannelHndl =		      vpm450m->aulEchoChanHndl[channel];
+	modify->fTdmConfigModified =		TRUE;
+	modify->TdmConfig.ulSinPcmLaw =		law_to_use;
+	modify->TdmConfig.ulRinPcmLaw =		law_to_use;
+	modify->TdmConfig.ulSoutPcmLaw =	law_to_use;
+	modify->TdmConfig.ulRoutPcmLaw =	law_to_use;
+	ulResult = Oct6100ChannelModify(vpm450m->pApiInstance, modify);
+	if (ulResult != GENERIC_OK) {
+		pr_notice("Failed to apply echo can changes on channel %d %d %08x!\n",
+			  vpm450m->aulEchoChanHndl[channel], channel, ulResult);
+	} else {
+		pr_info("Changed companding on channel %d to %s.\n", channel,
+			(alaw) ? "alaw" : "ulaw");
+		if (alaw)
+			set_bit(FLAGBIT_ALAW, &vpm450m->chanflags[channel]);
+		else
+			clear_bit(FLAGBIT_ALAW, &vpm450m->chanflags[channel]);
+	}
+	kfree(modify);
+}
+
+static void vpm450m_setec(struct vpm450m *vpm450m, int channel, int eclen)
+{
+	if (eclen) {
+		set_bit(FLAGBIT_ECHO, &vpm450m->chanflags[channel]);
+		vpm450m_setecmode(vpm450m, channel,
+				cOCT6100_ECHO_OP_MODE_NORMAL);
+	} else {
+		unsigned long *flags = &vpm450m->chanflags[channel];
+		clear_bit(FLAGBIT_ECHO, &vpm450m->chanflags[channel]);
+		if (test_bit(FLAGBIT_DTMF, flags) ||
+		    test_bit(FLAGBIT_MUTE, flags)) {
+			vpm450m_setecmode(vpm450m, channel,
+					cOCT6100_ECHO_OP_MODE_HT_RESET);
+		} else {
+			vpm450m_setecmode(vpm450m, channel,
+					cOCT6100_ECHO_OP_MODE_POWER_DOWN);
+		}
+	}
+}
+
+static UINT32 tdmmode_chan_to_slot_map(int channel)
+{
+	/* Four phases on the tdm bus, skip three of them per channel */
+	/* Due to a bug in the octasic, we had to move the data onto phase 2 */
+	return 1+(channel*4);
+}
+
+static int echocan_initialize_channel(
+		struct vpm450m *vpm, int channel, int mode)
+{
+	tOCT6100_CHANNEL_OPEN	ChannelOpen;
+	UINT32		law_to_use = (mode) ? cOCT6100_PCM_A_LAW :
+					      cOCT6100_PCM_U_LAW;
+	UINT32		tdmslot_setting;
+	UINT32		ulResult;
+
+	if (0 > channel || ECHOCAN_NUM_CHANS <= channel)
+		return -1;
+
+	tdmslot_setting = tdmmode_chan_to_slot_map(channel);
+
+	/* Fill Open channel structure with defaults */
+	Oct6100ChannelOpenDef(&ChannelOpen);
+
+	/* Assign the handle memory.*/
+	ChannelOpen.pulChannelHndl = &vpm->aulEchoChanHndl[channel];
+	ChannelOpen.ulUserChanId = channel;
+	/* Enable Tone disabling for Fax and Modems */
+	ChannelOpen.fEnableToneDisabler = TRUE;
+
+	/* Passthrough TDM data by default, no echocan */
+	ChannelOpen.ulEchoOperationMode = cOCT6100_ECHO_OP_MODE_POWER_DOWN;
+
+	/* Configure the TDM settings.*/
+	/* Input from the framer */
+	ChannelOpen.TdmConfig.ulSinStream		= SIN_STREAM;
+	ChannelOpen.TdmConfig.ulSinTimeslot		= tdmslot_setting;
+	ChannelOpen.TdmConfig.ulSinPcmLaw		= law_to_use;
+
+	/* Input from the Host (pre-framer) */
+	ChannelOpen.TdmConfig.ulRinStream		= RIN_STREAM;
+	ChannelOpen.TdmConfig.ulRinTimeslot		= tdmslot_setting;
+	ChannelOpen.TdmConfig.ulRinPcmLaw		= law_to_use;
+
+	/* Output to the Host */
+	ChannelOpen.TdmConfig.ulSoutStream		= SOUT_STREAM;
+	ChannelOpen.TdmConfig.ulSoutTimeslot		= tdmslot_setting;
+	ChannelOpen.TdmConfig.ulSoutPcmLaw		= law_to_use;
+
+	/* From asterisk after echo-cancellation - goes nowhere */
+	ChannelOpen.TdmConfig.ulRoutStream		= cOCT6100_UNASSIGNED;
+	ChannelOpen.TdmConfig.ulRoutTimeslot		= cOCT6100_UNASSIGNED;
+	ChannelOpen.TdmConfig.ulRoutPcmLaw		= law_to_use;
+
+	/* Set the desired VQE features.*/
+	ChannelOpen.VqeConfig.fEnableNlp		= TRUE;
+	ChannelOpen.VqeConfig.fRinDcOffsetRemoval	= TRUE;
+	ChannelOpen.VqeConfig.fSinDcOffsetRemoval	= TRUE;
+	ChannelOpen.VqeConfig.ulComfortNoiseMode =
+						cOCT6100_COMFORT_NOISE_NORMAL;
+
+	/* Open the channel.*/
+	ulResult = Oct6100ChannelOpen(vpm->pApiInstance, &ChannelOpen);
+
+	return ulResult;
+}
+
+static struct vpm450m *init_vpm450m(struct t13x *wc, int isalaw,
+					const struct firmware *firmware)
+{
+	tOCT6100_CHIP_OPEN *ChipOpen;
+	tOCT6100_GET_INSTANCE_SIZE InstanceSize;
+	tOCT6100_CHANNEL_OPEN *ChannelOpen;
+	UINT32 ulResult;
+	struct vpm450m *vpm450m;
+	int x, i, reg;
+
+	vpm450m = kzalloc(sizeof(struct vpm450m), GFP_KERNEL);
+	if (!vpm450m) {
+		dev_info(&wc->dev->dev, "Unable to allocate vpm450m struct\n");
+		return NULL;
+	}
+
+	ChipOpen = kzalloc(sizeof(tOCT6100_CHIP_OPEN), GFP_KERNEL);
+	if (!ChipOpen) {
+		dev_info(&wc->dev->dev, "Unable to allocate ChipOpen\n");
+		kfree(vpm450m);
+		return NULL;
+	}
+
+	ChannelOpen = kzalloc(sizeof(tOCT6100_CHANNEL_OPEN), GFP_KERNEL);
+	if (!ChannelOpen) {
+		dev_info(&wc->dev->dev, "Unable to allocate ChannelOpen\n");
+		kfree(vpm450m);
+		kfree(ChipOpen);
+		return NULL;
+	}
+
+	for (x = 0; x < ARRAY_SIZE(vpm450m->ecmode); x++)
+		vpm450m->ecmode[x] = -1;
+
+	dev_info(&wc->dev->dev, "Echo cancellation for %d channels\n",
+		 ECHOCAN_NUM_CHANS);
+
+	Oct6100ChipOpenDef(ChipOpen);
+	ChipOpen->pProcessContext = (void *)wc;
+
+	/* Change default parameters as needed */
+	/* upclk oscillator is at 33.33 Mhz */
+	ChipOpen->ulUpclkFreq = cOCT6100_UPCLK_FREQ_33_33_MHZ;
+
+	/* mclk will be generated by internal PLL at 133 Mhz */
+	ChipOpen->fEnableMemClkOut	= TRUE;
+	ChipOpen->ulMemClkFreq		= cOCT6100_MCLK_FREQ_133_MHZ;
+
+	/* User defined Chip ID.*/
+	ChipOpen->ulUserChipId		= OCT_CHIP_ID;
+
+	/* Set the maximums that the chip needs to support */
+	ChipOpen->ulMaxChannels		= ECHOCAN_NUM_CHANS;
+	ChipOpen->ulMaxTdmStreams	= OCT_MAX_TDM_STREAMS;
+
+	/* External Memory Settings */
+	/* Use DDR memory.*/
+	ChipOpen->ulMemoryType		= cOCT6100_MEM_TYPE_DDR;
+	ChipOpen->ulNumMemoryChips	= 1;
+	ChipOpen->ulMemoryChipSize	= cOCT6100_MEMORY_CHIP_SIZE_32MB;
+
+	ChipOpen->pbyImageFile = (PUINT8) firmware->data;
+	ChipOpen->ulImageSize = firmware->size;
+
+	/* Set TDM data stream frequency */
+	for (i = 0; i < ChipOpen->ulMaxTdmStreams; i++)
+		ChipOpen->aulTdmStreamFreqs[i] = cOCT6100_TDM_STREAM_FREQ_8MHZ;
+
+	/* Configure TDM sampling */
+	ChipOpen->ulTdmSampling = cOCT6100_TDM_SAMPLE_AT_FALLING_EDGE;
+	/* Disable to save RAM footprint space */
+	ChipOpen->fEnableChannelRecording = FALSE;
+
+	/* In this example we will maintain the API using polling so
+	   interrupts must be disabled */
+	ChipOpen->InterruptConfig.ulErrorH100Config =
+						cOCT6100_INTERRUPT_DISABLE;
+	ChipOpen->InterruptConfig.ulErrorMemoryConfig =
+						cOCT6100_INTERRUPT_DISABLE;
+	ChipOpen->InterruptConfig.ulFatalGeneralConfig =
+						cOCT6100_INTERRUPT_DISABLE;
+	ChipOpen->InterruptConfig.ulFatalMemoryConfig =
+						cOCT6100_INTERRUPT_DISABLE;
+
+	ChipOpen->ulSoftToneEventsBufSize = OCT_TONEEVENT_BUFFER_SIZE;
+
+	/* Inserting default values into tOCT6100_GET_INSTANCE_SIZE
+	   structure parameters. */
+	Oct6100GetInstanceSizeDef(&InstanceSize);
+
+	/* Reset octasic device */
+	oct_reset(wc);
+
+	/* Get the size of the OCT6100 instance structure. */
+	ulResult = Oct6100GetInstanceSize(ChipOpen, &InstanceSize);
+	if (ulResult != cOCT6100_ERR_OK) {
+		dev_info(&wc->dev->dev, "Unable to get instance size: %x\n",
+				ulResult);
+		return NULL;
+	}
+
+	vpm450m->pApiInstance = vmalloc(InstanceSize.ulApiInstanceSize);
+	if (!vpm450m->pApiInstance) {
+		dev_info(&wc->dev->dev,
+			"Out of memory (can't allocate %d bytes)!\n",
+			InstanceSize.ulApiInstanceSize);
+		return NULL;
+	}
+
+	/* Perform the actual configuration of the chip. */
+	oct_enable_dram(wc);
+	ulResult = Oct6100ChipOpen(vpm450m->pApiInstance, ChipOpen);
+	if (ulResult != cOCT6100_ERR_OK) {
+		dev_info(&wc->dev->dev, "Unable to Oct6100ChipOpen: %x\n",
+				ulResult);
+		return NULL;
+	}
+
+	/* OCT6100 is now booted and channels can be opened */
+	/* Open all channels */
+	for (i = 0; i < ECHOCAN_NUM_CHANS; i++) {
+		ulResult = echocan_initialize_channel(vpm450m, i, isalaw);
+		if (0 != ulResult) {
+			dev_info(&wc->dev->dev,
+				"Unable to echocan_initialize_channel: %x\n",
+				ulResult);
+			return NULL;
+		} else if (isalaw) {
+			set_bit(FLAGBIT_ALAW, &vpm450m->chanflags[i]);
+		}
+	}
+
+	reg = ioread32be(wc->membase + TDM_CONTROL);
+	if (vpmsupport == 1)
+		iowrite32be(reg | ENABLE_ECHOCAN_TDM,
+				wc->membase + TDM_CONTROL);
+	else
+		iowrite32be(reg & ~ENABLE_ECHOCAN_TDM,
+				wc->membase + TDM_CONTROL);
+
+	kfree(ChipOpen);
+	kfree(ChannelOpen);
+	return vpm450m;
+}
+
+static void release_vpm450m(struct vpm450m *vpm450m)
+{
+	UINT32 ulResult;
+	tOCT6100_CHIP_CLOSE ChipClose;
+
+	Oct6100ChipCloseDef(&ChipClose);
+	ulResult = Oct6100ChipClose(vpm450m->pApiInstance, &ChipClose);
+	if (ulResult != cOCT6100_ERR_OK)
+		pr_notice("Failed to close chip, code %08x!\n", ulResult);
+	vfree(vpm450m->pApiInstance);
+	kfree(vpm450m);
+}
+
+static const char *__t13x_echocan_name(struct t13x *wc)
+{
+	if (wc->vpm)
+		return vpm_name;
+	else
+		return NULL;
+}
+
+static const char *t13x_echocan_name(const struct dahdi_chan *chan)
+{
+	struct t13x *wc = chan->pvt;
+	return __t13x_echocan_name(wc);
+}
+
+static int t13x_echocan_create(struct dahdi_chan *chan,
+			     struct dahdi_echocanparams *ecp,
+			     struct dahdi_echocanparam *p,
+			     struct dahdi_echocan_state **ec)
+{
+	struct t13x *wc = chan->pvt;
+	const int channel = chan->chanpos - 1;
+	const struct dahdi_echocan_ops *ops;
+	const struct dahdi_echocan_features *features;
+	const bool alaw = (chan->span->deflaw == 2);
+
+	if (!vpmsupport || !wc->vpm)
+		return -ENODEV;
+
+	ops = &vpm_ec_ops;
+	features = &vpm_ec_features;
+
+	if (ecp->param_count > 0) {
+		dev_warn(&wc->dev->dev, "%s echo canceller does not support parameters; failing request\n",
+			 chan->ec_factory->get_name(chan));
+		return -EINVAL;
+	}
+
+	*ec = wc->ec[channel];
+	(*ec)->ops = ops;
+	(*ec)->features = *features;
+
+	vpm450m_set_alaw_companding(wc->vpm, channel, alaw);
+	vpm450m_setec(wc->vpm, channel, ecp->tap_length);
+	return 0;
+}
+
+static void echocan_free(struct dahdi_chan *chan,
+			struct dahdi_echocan_state *ec)
+{
+	struct t13x *wc = chan->pvt;
+	const int channel = chan->chanpos - 1;
+	if (!wc->vpm)
+		return;
+	memset(ec, 0, sizeof(*ec));
+	vpm450m_setec(wc->vpm, channel, 0);
+}
+
+static void t13x_vpm_init(struct t13x *wc)
+{
+	int companding = 0;
+	struct firmware embedded_firmware;
+	const struct firmware *firmware = &embedded_firmware;
+#if !defined(HOTPLUG_FIRMWARE)
+	extern void _binary_dahdi_fw_oct6114_032_bin_size;
+	extern u8 _binary_dahdi_fw_oct6114_032_bin_start[];
+#else
+	static const char oct032_firmware[] = "dahdi-fw-oct6114-032.bin";
+#endif
+
+	if (!vpmsupport) {
+		dev_info(&wc->dev->dev, "VPM450: Support Disabled\n");
+		return;
+	}
+
+#if defined(HOTPLUG_FIRMWARE)
+	if ((request_firmware(&firmware, oct032_firmware, &wc->dev->dev) != 0)
+			|| !firmware) {
+		dev_notice(&wc->dev->dev, "VPM450: firmware %s not available from userspace\n",
+				oct032_firmware);
+		return;
+	}
+#else
+	embedded_firmware.data = _binary_dahdi_fw_oct6114_032_bin_start;
+	/* Yes... this is weird. objcopy gives us a symbol containing
+	   the size of the firmware, not a pointer a variable containing
+	   the size. The only way we can get the value of the symbol
+	   is to take its address, so we define it as a pointer and
+	   then cast that value to the proper type.
+      */
+	embedded_firmware.size = (size_t)&_binary_dahdi_fw_oct6114_032_bin_size;
+#endif
+
+	companding = dahdi_is_e1_span(&wc->span);
+
+	wc->vpm = init_vpm450m(wc, companding, firmware);
+	if (!wc->vpm) {
+		dev_notice(&wc->dev->dev, "VPM450: Failed to initialize\n");
+		if (firmware != &embedded_firmware)
+			release_firmware(firmware);
+		return;
+	}
+
+	if (firmware != &embedded_firmware)
+		release_firmware(firmware);
+
+	dev_info(&wc->dev->dev,
+		"VPM450: Present and operational servicing %d span\n", 1);
+
+}
+#endif /* VPM_SUPPORT */
+
+static int t1xxp_clear_maint(struct dahdi_span *span);
+
+static struct t13x *ifaces[WC_MAX_IFACES];
+
+struct t13x_desc {
+	const char *name;
+};
+
+static const struct t13x_desc te133 = {"Wildcard TE133"}; /* pci express */
+static const struct t13x_desc te134 = {"Wildcard TE134"}; /* legacy pci */
+
+static inline bool is_pcie(const struct t13x *t1)
+{
+	return t1->devtype == &te133;
+}
+
+static int __t1_pci_get(struct t13x *wc, unsigned int addr)
+{
+	unsigned int res = ioread8(wc->membase + addr);
+	return res;
+}
+
+static inline int __t1_pci_set(struct t13x *wc, unsigned int addr, int val)
+{
+	iowrite8(val, wc->membase + addr);
+	__t1_pci_get(wc, 0);
+	return 0;
+}
+
+static inline int t1_pci_get(struct t13x *wc, int addr)
+{
+	unsigned int ret;
+	unsigned long flags;
+
+	spin_lock_irqsave(&wc->reglock, flags);
+	ret = __t1_pci_get(wc, addr);
+	spin_unlock_irqrestore(&wc->reglock, flags);
+	return ret;
+}
+
+static inline int t1_pci_set(struct t13x *wc, int addr, int val)
+{
+	unsigned long flags;
+	unsigned int ret;
+	spin_lock_irqsave(&wc->reglock, flags);
+	ret = __t1_pci_set(wc, addr, val);
+	spin_unlock_irqrestore(&wc->reglock, flags);
+	return ret;
+}
+
+static inline int __t1_framer_set(struct t13x *wc, int addr, int val)
+{
+	return __t1_pci_set(wc, FRAMER_BASE + addr, val);
+}
+
+static inline int t1_framer_set(struct t13x *wc, int addr, int val)
+{
+	return t1_pci_set(wc, FRAMER_BASE + addr, val);
+}
+
+static inline int __t1_framer_get(struct t13x *wc, int addr)
+{
+	return __t1_pci_get(wc, FRAMER_BASE + addr);
+}
+
+static inline int t1_framer_get(struct t13x *wc, int addr)
+{
+	return t1_pci_get(wc, FRAMER_BASE + addr);
+}
+
+
+static void t1_framer_reset(struct t13x *wc)
+{
+	unsigned long flags;
+	u32 scratch;
+	spin_lock_irqsave(&wc->reglock, flags);
+	scratch = ioread32be(wc->membase);
+	iowrite32be(scratch & ~(1UL << 11), wc->membase);
+	ioread32be(wc->membase);
+	udelay(100);
+	iowrite32be(scratch, wc->membase);
+	ioread32be(wc->membase);
+	spin_unlock_irqrestore(&wc->reglock, flags);
+}
+
+static void t1_setleds(struct t13x *wc, u32 leds)
+{
+	unsigned long flags;
+	static const u32 LED_MASK = 0x600;
+	u32 scratch;
+	spin_lock_irqsave(&wc->reglock, flags);
+	scratch = ((ioread32be(wc->membase) & ~LED_MASK) | (leds & LED_MASK));
+	iowrite32be(scratch, wc->membase);
+	spin_unlock_irqrestore(&wc->reglock, flags);
+}
+
+static void __t1xxp_set_clear(struct t13x *wc)
+{
+	int i, offset;
+	int ret;
+	unsigned short reg[3] = {0, 0, 0};
+
+	/* Calculate all states on all 24 channels using the channel
+	   flags, then write all 3 clear channel registers at once */
+
+	for (i = 0; i < wc->span.channels; i++) {
+		offset = i/8;
+		if (wc->span.chans[i]->flags & DAHDI_FLAG_CLEAR)
+			reg[offset] |= 1 << (7 - (i % 8));
+		else
+			reg[offset] &= ~(1 << (7 - (i % 8)));
+	}
+
+	ret = t1_framer_set(wc, CCB1, reg[0]);
+	if (ret < 0)
+		dev_info(&wc->dev->dev, "Unable to set clear/rbs mode!\n");
+
+	ret = t1_framer_set(wc, CCB2, reg[1]);
+	if (ret < 0)
+		dev_info(&wc->dev->dev, "Unable to set clear/rbs mode!\n");
+
+	ret = t1_framer_set(wc, CCB3, reg[2]);
+	if (ret < 0)
+		dev_info(&wc->dev->dev, "Unable to set clear/rbs mode!\n");
+}
+
+/**
+ * _t1_free_channels - Free the memory allocated for the channels.
+ *
+ * Must be called with wc->reglock held.
+ *
+ */
+static void _t1_free_channels(struct t13x *wc)
+{
+	int x;
+	for (x = 0; x < ARRAY_SIZE(wc->chans); x++) {
+		kfree(wc->chans[x]);
+		kfree(wc->ec[x]);
+		wc->chans[x] = NULL;
+		wc->ec[x] = NULL;
+	}
+}
+
+static void free_wc(struct t13x *wc)
+{
+	unsigned long flags;
+	LIST_HEAD(list);
+
+	spin_lock_irqsave(&wc->reglock, flags);
+	_t1_free_channels(wc);
+	spin_unlock_irqrestore(&wc->reglock, flags);
+
+	if (wc->wq)
+		destroy_workqueue(wc->wq);
+
+	kfree(wc->ddev->location);
+	kfree(wc->ddev->devicetype);
+	kfree(wc->ddev->hardware_id);
+	if (wc->ddev)
+		dahdi_free_device(wc->ddev);
+	kfree(wc->name);
+	kfree(wc);
+}
+
+static void t4_serial_setup(struct t13x *wc)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&wc->reglock, flags);
+	__t1_framer_set(wc, 0x85, 0xe0);	/* GPC1: Multiplex mode
+						   enabled, FSC is output,
+						   active low, RCLK from
+						   channel 0 */
+	__t1_framer_set(wc, 0x08, 0x05);	/* IPC: Interrupt push/pull
+						   active low */
+
+	/* Global clocks (8.192 Mhz CLK) */
+	__t1_framer_set(wc, 0x92, 0x00);
+	__t1_framer_set(wc, 0x93, 0x18);
+	__t1_framer_set(wc, 0x94, 0xfb);
+	__t1_framer_set(wc, 0x95, 0x0b);
+	__t1_framer_set(wc, 0x96, 0x00);
+	__t1_framer_set(wc, 0x97, 0x0b);
+	__t1_framer_set(wc, 0x98, 0xdb);
+	__t1_framer_set(wc, 0x99, 0xdf);
+
+	/* Configure interrupts */
+	__t1_framer_set(wc, 0x46, 0xc0);	/* GCR: Interrupt on
+						   Activation/Deactivation of
+						   AIX, LOS */
+
+	/* Configure system interface */
+	__t1_framer_set(wc, 0x3e, 0xc2);	/* SIC1: 8.192 Mhz clock/bus,
+						   double buffer receive /
+						   transmit, byte interleaved
+						 */
+	__t1_framer_set(wc, 0x3f, 0x02);	/* SIC2: No FFS, no center
+						   receive eliastic buffer,
+						   phase 1 */
+	__t1_framer_set(wc, 0x40, 0x04);	/* SIC3: Edges for capture */
+	__t1_framer_set(wc, 0x44, 0x30);	/* CMR1: RCLK is at 8.192 Mhz
+						   dejittered */
+	__t1_framer_set(wc, 0x45, 0x00);	/* CMR2: We provide sync and
+						   clock for tx and rx. */
+	__t1_framer_set(wc, 0x22, 0x00);	/* XC0: Normal operation of
+						   Sa-bits */
+	__t1_framer_set(wc, 0x23, 0x02);	/* XC1: 0 offset */
+	__t1_framer_set(wc, 0x24, 0x00);	/* RC0: Just shy of 255 */
+	__t1_framer_set(wc, 0x25, 0x03);	/* RC1: The rest of RC0 */
+
+	/* Configure ports */
+	__t1_framer_set(wc, 0x80, 0x00);	/* PC1: SPYR/SPYX input on
+						   RPA/XPA */
+	__t1_framer_set(wc, 0x81, 0x22);	/* PC2: RMFB/XSIG output/input
+						   on RPB/XPB */
+	__t1_framer_set(wc, 0x82, 0x65);	/* PC3: Unused stuff */
+	__t1_framer_set(wc, 0x83, 0x35);	/* PC4: Unused stuff */
+	__t1_framer_set(wc, 0x84, 0x31);	/* PC5: XMFS active low, SCLKR
+						   is input, RCLK is output */
+	__t1_framer_set(wc, 0x86, 0x03);	/* PC6: CLK1 is Tx Clock
+						   output, CLK2 is 8.192 Mhz
+						   from DCO-R */
+	__t1_framer_set(wc, 0x3b, 0x00);	/* Clear LCR1 */
+	spin_unlock_irqrestore(&wc->reglock, flags);
+}
+
+static void t1_configure_t1(struct t13x *wc, int lineconfig, int txlevel)
+{
+	unsigned int fmr4, fmr2, fmr1, fmr0, lim2;
+	char *framing, *line;
+	int mytxlevel;
+	unsigned long flags;
+
+	if ((txlevel > 7) || (txlevel < 4))
+		mytxlevel = 0;
+	else
+		mytxlevel = txlevel - 4;
+	fmr1 = 0x9c; /* FMR1: Mode 0, T1 mode, CRC on for ESF, 2.048 Mhz system
+			data rate, no XAIS */
+	fmr2 = 0x20; /* FMR2: no payload loopback, don't auto yellow alarm */
+
+
+	if (SPANTYPE_DIGITAL_J1 == wc->span.spantype)
+		fmr4 = 0x1c;
+	else
+		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 */
+
+	spin_lock_irqsave(&wc->reglock, flags);
+
+	__t1_framer_set(wc, 0x1d, fmr1);
+	__t1_framer_set(wc, 0x1e, fmr2);
+
+	/* Configure line interface */
+	if (lineconfig & DAHDI_CONFIG_AMI) {
+		line = "AMI";
+		fmr0 = 0xa0;
+	} else {
+		line = "B8ZS";
+		fmr0 = 0xf0;
+	}
+	if (lineconfig & DAHDI_CONFIG_D4) {
+		framing = "D4";
+	} else {
+		framing = "ESF";
+		fmr4 |= 0x2;
+		fmr2 |= 0xc0;
+	}
+	__t1_framer_set(wc, 0x1c, fmr0);
+
+	__t1_framer_set(wc, 0x20, fmr4);
+	__t1_framer_set(wc, 0x21, 0x40);	/* FMR5: Enable RBS mode */
+
+	__t1_framer_set(wc, 0x37, 0xf8);	/* LIM1: Clear data in case of
+						   LOS, Set receiver threshold
+						   (0.5V), No remote loop, no
+						   DRS */
+	__t1_framer_set(wc, 0x36, 0x08);	/* LIM0: Enable auto long haul
+						   mode, no local loop (must be
+						   after LIM1) */
+
+	__t1_framer_set(wc, 0x02, 0x50);	/* CMDR: Reset the receiver and
+						   transmitter line interface
+						 */
+	__t1_framer_set(wc, 0x02, 0x00);	/* CMDR: Reset the receiver and
+						   transmitter line interface
+						 */
+
+	__t1_framer_set(wc, 0x3a, lim2);	/* LIM2: 50% peak amplitude is
+						   a "1" */
+	__t1_framer_set(wc, 0x38, 0x0a);	/* PCD: LOS after 176
+						   consecutive "zeros" */
+	__t1_framer_set(wc, 0x39, 0x15);	/* PCR: 22 "ones" clear LOS */
+
+	if (SPANTYPE_DIGITAL_J1 == wc->span.spantype)
+		__t1_framer_set(wc, 0x24, 0x80); /* J1 overide */
+
+	/* Generate pulse mask for T1 */
+	switch (mytxlevel) {
+	case 3:
+		__t1_framer_set(wc, 0x26, 0x07);	/* XPM0 */
+		__t1_framer_set(wc, 0x27, 0x01);	/* XPM1 */
+		__t1_framer_set(wc, 0x28, 0x00);	/* XPM2 */
+		break;
+	case 2:
+		__t1_framer_set(wc, 0x26, 0x8c);	/* XPM0 */
+		__t1_framer_set(wc, 0x27, 0x11);	/* XPM1 */
+		__t1_framer_set(wc, 0x28, 0x01);	/* XPM2 */
+		break;
+	case 1:
+		__t1_framer_set(wc, 0x26, 0x8c);	/* XPM0 */
+		__t1_framer_set(wc, 0x27, 0x01);	/* XPM1 */
+		__t1_framer_set(wc, 0x28, 0x00);	/* XPM2 */
+		break;
+	case 0:
+	default:
+		__t1_framer_set(wc, 0x26, 0x1a);	/* XPM0 */
+		__t1_framer_set(wc, 0x27, 0x1f);	/* XPM1 */
+		__t1_framer_set(wc, 0x28, 0x01);	/* XPM2 */
+		break;
+	}
+
+	spin_unlock_irqrestore(&wc->reglock, flags);
+	dev_info(&wc->dev->dev, "Span configured for %s/%s\n", framing, line);
+}
+
+static void t1_configure_e1(struct t13x *wc, int lineconfig)
+{
+	unsigned int fmr2, fmr1, fmr0;
+	unsigned int cas = 0;
+	char *crc4 = "";
+	char *framing, *line;
+	unsigned long flags;
+
+	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 (lineconfig & DAHDI_CONFIG_CRC4) {
+		fmr1 |= 0x08;	/* CRC4 transmit */
+		fmr2 |= 0xc0;	/* CRC4 receive */
+		crc4 = "/CRC4";
+	}
+
+	spin_lock_irqsave(&wc->reglock, flags);
+
+	__t1_framer_set(wc, 0x1d, fmr1);
+	__t1_framer_set(wc, 0x1e, fmr2);
+
+	/* Configure line interface */
+	if (lineconfig & DAHDI_CONFIG_AMI) {
+		line = "AMI";
+		fmr0 = 0xa0;
+	} else {
+		line = "HDB3";
+		fmr0 = 0xf0;
+	}
+	if (lineconfig & DAHDI_CONFIG_CCS) {
+		framing = "CCS";
+	} else {
+		framing = "CAS";
+		cas = 0x40;
+	}
+	__t1_framer_set(wc, 0x1c, fmr0);
+
+	__t1_framer_set(wc, 0x37, 0xf0);	/* LIM1: Clear data in case of
+						   LOS, Set receiver threshold
+						   (0.5V), No remote loop, no
+						   DRS */
+	__t1_framer_set(wc, 0x36, 0x08);	/* LIM0: Enable auto long haul
+						   mode, no local loop (must be
+						   after LIM1) */
+
+	__t1_framer_set(wc, 0x02, 0x50);	/* CMDR: Reset the receiver and
+						   transmitter line interface
+						 */
+	__t1_framer_set(wc, 0x02, 0x00);	/* CMDR: Reset the receiver and
+						   transmitter line interface
+						 */
+
+	/* Condition receive line interface for E1 after reset */
+	__t1_framer_set(wc, 0xbb, 0x17);
+	__t1_framer_set(wc, 0xbc, 0x55);
+	__t1_framer_set(wc, 0xbb, 0x97);
+	__t1_framer_set(wc, 0xbb, 0x11);
+	__t1_framer_set(wc, 0xbc, 0xaa);
+	__t1_framer_set(wc, 0xbb, 0x91);
+	__t1_framer_set(wc, 0xbb, 0x12);
+	__t1_framer_set(wc, 0xbc, 0x55);
+	__t1_framer_set(wc, 0xbb, 0x92);
+	__t1_framer_set(wc, 0xbb, 0x0c);
+	__t1_framer_set(wc, 0xbb, 0x00);
+	__t1_framer_set(wc, 0xbb, 0x8c);
+
+	__t1_framer_set(wc, 0x3a, 0x20);	/* LIM2: 50% peak amplitude is
+						   a "1" */
+	__t1_framer_set(wc, 0x38, 0x0a);	/* PCD: LOS after 176
+						   consecutive "zeros" */
+	__t1_framer_set(wc, 0x39, 0x15);	/* PCR: 22 "ones" clear LOS */
+
+	__t1_framer_set(wc, 0x20, 0x9f);	/* XSW: Spare bits all to 1 */
+	__t1_framer_set(wc, 0x21, 0x1c|cas);	/* XSP: E-bit set when async.
+						   AXS auto, XSIF to 1 */
+
+	/* Generate pulse mask for E1 */
+	__t1_framer_set(wc, 0x26, 0x74);	/* XPM0 */
+	__t1_framer_set(wc, 0x27, 0x02);	/* XPM1 */
+	__t1_framer_set(wc, 0x28, 0x00);	/* XPM2 */
+
+	spin_unlock_irqrestore(&wc->reglock, flags);
+
+	dev_info(&wc->dev->dev,
+			"Span configured for %s/%s%s\n", framing, line, crc4);
+}
+
+static void t1xxp_framer_start(struct t13x *wc)
+{
+	if (dahdi_is_e1_span(&wc->span)) {
+		t1_configure_e1(wc, wc->span.lineconfig);
+	} else { /* is a T1 card */
+		t1_configure_t1(wc, wc->span.lineconfig, wc->span.txlevel);
+		__t1xxp_set_clear(wc);
+	}
+
+	set_bit(DAHDI_FLAGBIT_RUNNING, &wc->span.flags);
+}
+
+static void set_span_devicetype(struct t13x *wc)
+{
+	const char *olddevicetype;
+	olddevicetype = wc->ddev->devicetype;
+
+#ifndef VPM_SUPPORT
+	wc->ddev->devicetype = kasprintf(GFP_KERNEL,
+				 "%s (VPMOCT032)", wc->devtype->name);
+#else
+	wc->ddev->devicetype = kasprintf(GFP_KERNEL, "%s", wc->devtype->name);
+#endif /* VPM_SUPPORT */
+
+	/* On the off chance that we were able to allocate it previously. */
+	if (!wc->ddev->devicetype)
+		wc->ddev->devicetype = olddevicetype;
+	else
+		kfree(olddevicetype);
+}
+
+static int t1xxp_startup(struct file *file, struct dahdi_span *span)
+{
+	struct t13x *wc = container_of(span, struct t13x, span);
+
+	set_span_devicetype(wc);
+
+	/* Reset framer with proper parameters and start */
+	t1xxp_framer_start(wc);
+	dev_info(&wc->dev->dev,
+			"Calling startup (flags is %lu)\n", span->flags);
+
+	return 0;
+}
+
+static inline bool is_initialized(struct t13x *wc)
+{
+	WARN_ON(wc->not_ready < 0);
+	return (wc->not_ready == 0);
+}
+
+/**
+ * t1_wait_for_ready
+ *
+ * Check if the board has finished any setup and is ready to start processing
+ * calls.
+ */
+static int t1_wait_for_ready(struct t13x *wc)
+{
+	while (!is_initialized(wc)) {
+		if (fatal_signal_pending(current))
+			return -EIO;
+		msleep_interruptible(250);
+	}
+	return 0;
+}
+
+static int t1xxp_chanconfig(struct file *file,
+			    struct dahdi_chan *chan, int sigtype)
+{
+	struct t13x *wc = chan->pvt;
+
+	if (file->f_flags & O_NONBLOCK && !is_initialized(wc))
+		return -EAGAIN;
+	else
+		t1_wait_for_ready(wc);
+
+	if (test_bit(DAHDI_FLAGBIT_RUNNING, &chan->span->flags) &&
+	    dahdi_is_t1_span(&wc->span)) {
+		__t1xxp_set_clear(wc);
+	}
+	return 0;
+}
+
+static int t1xxp_rbsbits(struct dahdi_chan *chan, int bits)
+{
+	u_char m, c;
+	int n, b;
+	struct t13x *wc = chan->pvt;
+	unsigned long flags;
+
+	if (dahdi_is_e1_span(&wc->span)) { /* do it E1 way */
+		if (chan->chanpos == 16)
+			return 0;
+
+		n = chan->chanpos - 1;
+		if (chan->chanpos > 15)
+			n--;
+		b = (n % 15);
+		spin_lock_irqsave(&wc->reglock, flags);
+		c = wc->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 */
+		wc->txsigs[b] = c;
+		/* output them to the chip */
+		__t1_framer_set(wc, 0x71 + b, c);
+		spin_unlock_irqrestore(&wc->reglock, flags);
+	} else if (wc->span.lineconfig & DAHDI_CONFIG_D4) {
+		n = chan->chanpos - 1;
+		b = (n / 4);
+		spin_lock_irqsave(&wc->reglock, flags);
+		c = wc->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 */
+		wc->txsigs[b] = c;
+		/* output them to the chip */
+		__t1_framer_set(wc, 0x70 + b, c);
+		__t1_framer_set(wc, 0x70 + b + 6, c);
+		spin_unlock_irqrestore(&wc->reglock, flags);
+	} else if (wc->span.lineconfig & DAHDI_CONFIG_ESF) {
+		n = chan->chanpos - 1;
+		b = (n / 2);
+		spin_lock_irqsave(&wc->reglock, flags);
+		c = wc->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 */
+		wc->txsigs[b] = c;
+		/* output them to the chip */
+		__t1_framer_set(wc, 0x70 + b, c);
+		spin_unlock_irqrestore(&wc->reglock, flags);
+	}
+
+	return 0;
+}
+
+static inline void t1_check_sigbits(struct t13x *wc)
+{
+	int a, i, rxs;
+
+	if (!(test_bit(DAHDI_FLAGBIT_RUNNING, &wc->span.flags)))
+		return;
+	if (dahdi_is_e1_span(&wc->span)) {
+		for (i = 0; i < 15; i++) {
+			a = t1_framer_get(wc, 0x71 + i);
+			if (a > -1) {
+				/* Get high channel in low bits */
+				rxs = (a & 0xf);
+				if (!(wc->span.chans[i+16]->sig & DAHDI_SIG_CLEAR))
+					if (wc->span.chans[i+16]->rxsig != rxs)
+						dahdi_rbsbits(wc->span.chans[i+16], rxs);
+
+				rxs = (a >> 4) & 0xf;
+				if (!(wc->span.chans[i]->sig & DAHDI_SIG_CLEAR))
+					if (wc->span.chans[i]->rxsig != rxs)
+						dahdi_rbsbits(wc->span.chans[i], rxs);
+			}
+		}
+	} else if (wc->span.lineconfig & DAHDI_CONFIG_D4) {
+		for (i = 0; i < 24; i += 4) {
+			a = t1_framer_get(wc, 0x70 + (i>>2));
+			if (a > -1) {
+				/* Get high channel in low bits */
+				rxs = (a & 0x3) << 2;
+				if (!(wc->span.chans[i+3]->sig & DAHDI_SIG_CLEAR))
+					if (wc->span.chans[i+3]->rxsig != rxs)
+						dahdi_rbsbits(wc->span.chans[i+3], rxs);
+
+				rxs = (a & 0xc);
+				if (!(wc->span.chans[i+2]->sig & DAHDI_SIG_CLEAR))
+					if (wc->span.chans[i+2]->rxsig != rxs)
+						dahdi_rbsbits(wc->span.chans[i+2], rxs);
+
+				rxs = (a >> 2) & 0xc;
+				if (!(wc->span.chans[i+1]->sig & DAHDI_SIG_CLEAR))
+					if (wc->span.chans[i+1]->rxsig != rxs)
+						dahdi_rbsbits(wc->span.chans[i+1], rxs);
+
+				rxs = (a >> 4) & 0xc;
+				if (!(wc->span.chans[i]->sig & DAHDI_SIG_CLEAR))
+					if (wc->span.chans[i]->rxsig != rxs)
+						dahdi_rbsbits(wc->span.chans[i], rxs);
+			}
+		}
+	} else {
+		for (i = 0; i < 24; i += 2) {
+			a = t1_framer_get(wc, 0x70 + (i>>1));
+			if (a > -1) {
+				/* Get high channel in low bits */
+				rxs = (a & 0xf);
+				if (!(wc->span.chans[i+1]->sig & DAHDI_SIG_CLEAR))
+					if (wc->span.chans[i+1]->rxsig != rxs)
+						dahdi_rbsbits(wc->span.chans[i+1], rxs);
+
+				rxs = (a >> 4) & 0xf;
+				if (!(wc->span.chans[i]->sig & DAHDI_SIG_CLEAR))
+					if (wc->span.chans[i]->rxsig != rxs)
+						dahdi_rbsbits(wc->span.chans[i], rxs);
+			}
+		}
+	}
+}
+
+struct maint_work_struct {
+	struct work_struct work;
+	struct t13x *wc;
+	int cmd;
+	struct dahdi_span *span;
+};
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+static void t1xxp_maint_work(void *data)
+{
+	struct maint_work_struct *w = data;
+#else
+static void t1xxp_maint_work(struct work_struct *work)
+{
+	struct maint_work_struct *w = container_of(work,
+					struct maint_work_struct, work);
+#endif
+
+	struct t13x *wc = w->wc;
+	struct dahdi_span *span = w->span;
+	int reg = 0;
+	int cmd = w->cmd;
+	unsigned long flags;
+
+	if (dahdi_is_e1_span(&wc->span)) {
+		switch (cmd) {
+		case DAHDI_MAINT_NONE:
+			dev_info(&wc->dev->dev, "Clearing all maint modes\n");
+			t1xxp_clear_maint(span);
+			break;
+		case DAHDI_MAINT_LOCALLOOP:
+			dev_info(&wc->dev->dev, "Turning on local loopback\n");
+			t1xxp_clear_maint(span);
+			spin_lock_irqsave(&wc->reglock, flags);
+			reg = __t1_framer_get(wc, LIM0);
+			if (reg < 0) {
+				spin_unlock_irqrestore(&wc->reglock, flags);
+				goto cleanup;
+			}
+			__t1_framer_set(wc, LIM0, reg | LIM0_LL);
+			spin_unlock_irqrestore(&wc->reglock, flags);
+			break;
+		case DAHDI_MAINT_NETWORKLINELOOP:
+			dev_info(&wc->dev->dev,
+					"Turning on network line loopback\n");
+			t1xxp_clear_maint(span);
+			spin_lock_irqsave(&wc->reglock, flags);
+			reg = __t1_framer_get(wc, LIM1);
+			if (reg < 0) {
+				spin_unlock_irqrestore(&wc->reglock, flags);
+				goto cleanup;
+			}
+			__t1_framer_set(wc, LIM1, reg | LIM1_RL);
+			spin_unlock_irqrestore(&wc->reglock, flags);
+			break;
+		case DAHDI_MAINT_NETWORKPAYLOADLOOP:
+			dev_info(&wc->dev->dev,
+				"Turning on network payload loopback\n");
+			t1xxp_clear_maint(span);
+			spin_lock_irqsave(&wc->reglock, flags);
+			reg = __t1_framer_get(wc, LIM1);
+			if (reg < 0) {
+				spin_unlock_irqrestore(&wc->reglock, flags);
+				goto cleanup;
+			}
+			__t1_framer_set(wc, LIM1, reg | (LIM1_RL | LIM1_JATT));
+			spin_unlock_irqrestore(&wc->reglock, flags);
+			break;
+		default:
+			dev_info(&wc->dev->dev,
+					"Unknown E1 maint command: %d\n", cmd);
+			goto cleanup;
+		}
+	} else {
+		switch (cmd) {
+		case DAHDI_MAINT_NONE:
+			dev_info(&wc->dev->dev, "Clearing all maint modes\n");
+			t1xxp_clear_maint(span);
+			break;
+		case DAHDI_MAINT_LOCALLOOP:
+			dev_info(&wc->dev->dev, "Turning on local loopback\n");
+			t1xxp_clear_maint(span);
+			spin_lock_irqsave(&wc->reglock, flags);
+			reg = __t1_framer_get(wc, LIM0);
+			if (reg < 0) {
+				spin_unlock_irqrestore(&wc->reglock, flags);
+				goto cleanup;
+			}
+			__t1_framer_set(wc, LIM0, reg | LIM0_LL);
+			spin_unlock_irqrestore(&wc->reglock, flags);
+			break;
+		case DAHDI_MAINT_NETWORKLINELOOP:
+			dev_info(&wc->dev->dev,
+					"Turning on network line loopback\n");
+			t1xxp_clear_maint(span);
+			spin_lock_irqsave(&wc->reglock, flags);
+			reg = __t1_framer_get(wc, LIM1);
+			if (reg < 0) {
+				spin_unlock_irqrestore(&wc->reglock, flags);
+				goto cleanup;
+			}
+			__t1_framer_set(wc, LIM1, reg | LIM1_RL);
+			spin_unlock_irqrestore(&wc->reglock, flags);
+			break;
+		case DAHDI_MAINT_NETWORKPAYLOADLOOP:
+			dev_info(&wc->dev->dev,
+				"Turning on network payload loopback\n");
+			t1xxp_clear_maint(span);
+			spin_lock_irqsave(&wc->reglock, flags);
+			reg = __t1_framer_get(wc, LIM1);
+			if (reg < 0) {
+				spin_unlock_irqrestore(&wc->reglock, flags);
+				goto cleanup;
+			}
+			__t1_framer_set(wc, LIM1, reg | (LIM1_RL | LIM1_JATT));
+			spin_unlock_irqrestore(&wc->reglock, flags);
+			break;
+		case DAHDI_MAINT_LOOPUP:
+			dev_info(&wc->dev->dev, "Transmitting loopup code\n");
+			t1xxp_clear_maint(span);
+			t1_framer_set(wc, 0x21, 0x50);
+			break;
+		case DAHDI_MAINT_LOOPDOWN:
+			dev_info(&wc->dev->dev, "Transmitting loopdown code\n");
+			t1xxp_clear_maint(span);
+			t1_framer_set(wc, 0x21, 0x60);
+			break;
+		default:
+			dev_info(&wc->dev->dev,
+					"Unknown T1 maint command: %d\n", cmd);
+			return;
+		}
+	}
+
+cleanup:
+	kfree(w);
+	return;
+}
+
+static int t1xxp_maint(struct dahdi_span *span, int cmd)
+{
+	struct maint_work_struct *work;
+	struct t13x *wc = container_of(span, struct t13x, span);
+
+	if (dahdi_is_e1_span(&wc->span)) {
+		switch (cmd) {
+		case DAHDI_MAINT_NONE:
+		case DAHDI_MAINT_LOCALLOOP:
+		case DAHDI_MAINT_NETWORKLINELOOP:
+		case DAHDI_MAINT_NETWORKPAYLOADLOOP:
+			break;
+		case DAHDI_MAINT_LOOPUP:
+		case DAHDI_MAINT_LOOPDOWN:
+			dev_info(&wc->dev->dev,
+				"Only local loop supported in E1 mode\n");
+			return -ENOSYS;
+		default:
+			dev_info(&wc->dev->dev,
+					"Unknown E1 maint command: %d\n", cmd);
+			return -ENOSYS;
+		}
+	} else {
+		switch (cmd) {
+		case DAHDI_MAINT_NONE:
+		case DAHDI_MAINT_LOCALLOOP:
+		case DAHDI_MAINT_NETWORKLINELOOP:
+		case DAHDI_MAINT_NETWORKPAYLOADLOOP:
+		case DAHDI_MAINT_LOOPUP:
+		case DAHDI_MAINT_LOOPDOWN:
+			break;
+		default:
+			dev_info(&wc->dev->dev,
+					"Unknown T1 maint command: %d\n", cmd);
+			return -ENOSYS;
+		}
+	}
+
+	work = kzalloc(sizeof(*work), GFP_ATOMIC);
+	if (!work) {
+		dev_info(&wc->dev->dev,
+				"Failed to allocate memory for workqueue\n");
+		return -ENOMEM;
+	}
+
+	work->span = span;
+	work->wc = wc;
+	work->cmd = cmd;
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 20)
+	INIT_WORK(&work->work, t1xxp_maint_work, work);
+#else
+	INIT_WORK(&work->work, t1xxp_maint_work);
+#endif
+	queue_work(wc->wq, &work->work);
+	return 0;
+}
+
+static int t1xxp_clear_maint(struct dahdi_span *span)
+{
+	struct t13x *wc = container_of(span, struct t13x, span);
+	int reg = 0;
+	unsigned long flags;
+
+	/* Turn off local loop */
+	spin_lock_irqsave(&wc->reglock, flags);
+	reg = __t1_framer_get(wc, LIM0);
+	if (reg < 0) {
+		spin_unlock_irqrestore(&wc->reglock, flags);
+		return -EIO;
+	}
+	__t1_framer_set(wc, LIM0, reg & ~LIM0_LL);
+
+	/* Turn off remote loop & jitter attenuator */
+	reg = __t1_framer_get(wc, LIM1);
+	if (reg < 0) {
+		spin_unlock_irqrestore(&wc->reglock, flags);
+		return -EIO;
+	}
+	__t1_framer_set(wc, LIM1, reg & ~(LIM1_RL | LIM1_JATT));
+
+	/* Clear loopup/loopdown signals on the line */
+	__t1_framer_set(wc, 0x21, 0x40);
+	spin_unlock_irqrestore(&wc->reglock, flags);
+	return 0;
+}
+
+
+static int t1xxp_ioctl(struct dahdi_chan *chan, unsigned int cmd,
+			unsigned long data)
+{
+	struct t4_regs regs;
+	unsigned int x;
+	struct t13x *wc;
+
+	switch (cmd) {
+	case WCT4_GET_REGS:
+		wc = chan->pvt;
+		for (x = 0; x < sizeof(regs.regs) / sizeof(regs.regs[0]); x++)
+			regs.regs[x] = t1_framer_get(wc, x);
+
+		if (copy_to_user((void __user *) data, &regs, sizeof(regs)))
+			return -EFAULT;
+		break;
+	default:
+		return -ENOTTY;
+	}
+	return 0;
+}
+
+static void t1_chan_set_sigcap(struct dahdi_span *span, int x)
+{
+	struct t13x *wc = container_of(span, struct t13x, span);
+	struct dahdi_chan *chan = wc->chans[x];
+	chan->sigcap = DAHDI_SIG_CLEAR;
+	/* E&M variant supported depends on span type */
+	if (dahdi_is_e1_span(&wc->span)) {
+		/* E1 sigcap setup */
+		if (span->lineconfig & DAHDI_CONFIG_CCS) {
+			/* CCS setup */
+			chan->sigcap |= DAHDI_SIG_MTP2 | DAHDI_SIG_SF;
+			return;
+		}
+		/* clear out sig and sigcap for channel 16 on E1 CAS
+		 * lines, otherwise, set it correctly */
+		if (x == 15) {
+			/* CAS signaling channel setup */
+			wc->chans[15]->sigcap = 0;
+			wc->chans[15]->sig = 0;
+			return;
+		}
+		/* normal CAS setup */
+		chan->sigcap |= DAHDI_SIG_EM_E1 | DAHDI_SIG_FXSLS |
+			DAHDI_SIG_FXSGS | DAHDI_SIG_FXSKS | DAHDI_SIG_SF |
+			DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS | DAHDI_SIG_FXOKS |
+			DAHDI_SIG_CAS | DAHDI_SIG_DACS_RBS;
+	} else {
+		/* T1 sigcap setup */
+		chan->sigcap |= DAHDI_SIG_EM | DAHDI_SIG_FXSLS |
+			DAHDI_SIG_FXSGS | DAHDI_SIG_FXSKS | DAHDI_SIG_MTP2 |
+			DAHDI_SIG_SF | DAHDI_SIG_FXOLS | DAHDI_SIG_FXOGS |
+			DAHDI_SIG_FXOKS | DAHDI_SIG_CAS | DAHDI_SIG_DACS_RBS;
+	}
+}
+
+static int
+t1xxp_spanconfig(struct file *file, struct dahdi_span *span,
+		 struct dahdi_lineconfig *lc)
+{
+	struct t13x *wc = container_of(span, struct t13x, span);
+	int i, reg;
+
+	if (file->f_flags & O_NONBLOCK) {
+		if (!is_initialized(wc))
+			return -EAGAIN;
+	} else {
+		t1_wait_for_ready(wc);
+	}
+
+	reg = ioread32be(wc->membase + TDM_CONTROL);
+	iowrite32be(reg & ~ENABLE_DMA, wc->membase+TDM_CONTROL);
+	msleep(200);
+
+	/* Do we want to SYNC on receive or not */
+	if (lc->sync) {
+		reg = ioread32be(wc->membase + TDM_CONTROL);
+		iowrite32be(reg | TDM_RECOVER_CLOCK, wc->membase+TDM_CONTROL);
+		span->syncsrc = span->spanno;
+	} else {
+		reg = ioread32be(wc->membase + TDM_CONTROL);
+		iowrite32be(reg & ~TDM_RECOVER_CLOCK, wc->membase+TDM_CONTROL);
+		span->syncsrc = 0;
+	}
+
+	reset_dring(wc);
+	reg = ioread32be(wc->membase + TDM_CONTROL);
+	iowrite32be(reg | ENABLE_DMA, wc->membase+TDM_CONTROL);
+
+	/* make sure that sigcaps gets updated if necessary */
+	for (i = 0; i < wc->span.channels; i++)
+		t1_chan_set_sigcap(span, i);
+
+	/* If already running, apply changes immediately */
+	if (test_bit(DAHDI_FLAGBIT_RUNNING, &span->flags))
+		return t1xxp_startup(file, span);
+
+	return 0;
+}
+
+/**
+ * t1_software_init - Initialize the board for the given type.
+ * @wc:		The board to initialize.
+ * @type:	The type of board we are, T1 / E1
+ *
+ * This function is called at startup and when the type of the span is changed
+ * via the dahdi_device before the span is assigned a number.
+ *
+ */
+static int t1_software_init(struct t13x *wc, enum linemode type)
+{
+	int x;
+	struct dahdi_chan *chans[32] = {NULL,};
+	struct dahdi_echocan_state *ec[32] = {NULL,};
+	unsigned long flags;
+	int res = 0;
+
... 1406 lines suppressed ...


-- 
dahdi/linux.git



More information about the dahdi-commits mailing list