[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, ®s, 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