summaryrefslogtreecommitdiff
path: root/wct1xxp.c
diff options
context:
space:
mode:
authormarkster <markster@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2001-11-07 17:47:29 +0000
committermarkster <markster@5390a7c7-147a-4af0-8ec9-7488f05a26cb>2001-11-07 17:47:29 +0000
commit6eeafd614e36021635d37840f4bfaa5f34395e34 (patch)
treeeb4e424f1cc4eac3a21e35f379253f6221e07880 /wct1xxp.c
parent3e12fb4c61ed406d67695f65f2be7b543ff54454 (diff)
Version 0.1.0 from FTP
git-svn-id: http://svn.digium.com/svn/zaptel/trunk@22 5390a7c7-147a-4af0-8ec9-7488f05a26cb
Diffstat (limited to 'wct1xxp.c')
-rwxr-xr-xwct1xxp.c514
1 files changed, 514 insertions, 0 deletions
diff --git a/wct1xxp.c b/wct1xxp.c
new file mode 100755
index 0000000..be821cf
--- /dev/null
+++ b/wct1xxp.c
@@ -0,0 +1,514 @@
+/*
+ * Linux Support Services, Inc. Wildcard T100P T1/PRI card Driver
+ *
+ * Written by Mark Spencer <markster@linux-support.net>
+ * Matthew Fredrickson <creslin@linux-support.net>
+ * William Meadows <wmeadows@linux-support.net>
+ *
+ * Copyright (C) 2001, Linux Support Services, Inc.
+ *
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#ifdef STANDALONE_ZAPATA
+#include "zaptel.h"
+#else
+#include <linux/zaptel.h>
+#endif
+
+#define DELAY 0x30 /* 30 = 15 cycles, 10 = 8 cycles, 0 = 3 cycles */
+
+#define WC_CNTL 0x00
+#define WC_OPER 0x01
+#define WC_AUXC 0x02
+#define WC_AUXD 0x03
+#define WC_MASK0 0x04
+#define WC_MASK1 0x05
+#define WC_INTSTAT 0x06
+
+#define WC_DMAWS 0x08
+#define WC_DMAWI 0x0c
+#define WC_DMAWE 0x10
+#define WC_DMARS 0x18
+#define WC_DMARI 0x1c
+#define WC_DMARE 0x20
+#define WC_CURPOS 0x24
+
+#define WC_SERC 0x2d
+
+#define WC_USERREG 0xc0
+
+#define WC_CLOCK 0x0
+#define WC_LEDTEST 0x1
+#define WC_VERSION 0x2
+
+#define BIT_CS (1 << 5)
+
+#define BIT_OK (1 << 0)
+#define BIT_TEST (1 << 1)
+#define BIT_ERROR (1 << 2)
+#define BIT_ALARM (1 << 3)
+
+struct t1xxp {
+ struct pci_dev *dev;
+ char *variety;
+ int usecount;
+ int dead;
+ int alarmtimer;
+ int alarm;
+ int alarmthreshold;
+ unsigned char ledtestreg;
+ unsigned char outbyte;
+ unsigned long ioaddr;
+ dma_addr_t readdma;
+ dma_addr_t writedma;
+ volatile int *writechunk; /* Double-word aligned write memory */
+ volatile int *readchunk; /* Double-word aligned read memory */
+ struct zt_span span; /* Span */
+ struct zt_chan chans[24]; /* Channels */
+};
+
+static inline void select_framer(struct t1xxp *wc)
+{
+ if (wc->outbyte & BIT_CS) {
+ wc->outbyte &= ~BIT_CS;
+ outb(wc->outbyte, wc->ioaddr + WC_AUXD);
+ }
+}
+
+static inline void select_control(struct t1xxp *wc)
+{
+ if (!(wc->outbyte & BIT_CS)) {
+ wc->outbyte |= BIT_CS;
+ outb(wc->outbyte, wc->ioaddr + WC_AUXD);
+ }
+}
+
+static inline void select_page(struct t1xxp *wc, unsigned char reg)
+{
+ int page = (reg & 0x30) << 2;
+ if (wc->outbyte != page) {
+ /* Clear high bits */
+ wc->outbyte &= 0x3f;
+ /* Set page */
+ wc->outbyte |= page;
+ /* Make sure we've turned on the framer */
+ wc->outbyte &= ~BIT_CS;
+ outb(wc->outbyte, wc->ioaddr + WC_AUXD);
+ } else if (wc->outbyte & BIT_CS) {
+ /* Check to be sure we don't still have to enable the framer */
+ select_framer(wc);
+ }
+}
+
+static int t1xxp_open(struct zt_chan *chan)
+{
+ struct t1xxp *wc = chan->pvt;
+ if (wc->dead)
+ return -ENODEV;
+ wc->usecount++;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int t1_get_reg(struct t1xxp *wc, int reg)
+{
+ unsigned char res;
+ select_page(wc, reg);
+ res = inb(wc->ioaddr + WC_USERREG + ((reg & 0xf) << 2));
+ return res;
+}
+
+static int t1_set_reg(struct t1xxp *wc, int reg, unsigned char val)
+{
+ select_page(wc, reg);
+ outb(val, wc->ioaddr + WC_USERREG + ((reg & 0xf) << 2));
+ return 0;
+}
+
+static int control_set_reg(struct t1xxp *wc, int reg, unsigned char val)
+{
+ select_control(wc);
+ outb(val, wc->ioaddr + WC_USERREG + ((reg & 0xf) << 2));
+ return 0;
+}
+
+static int control_get_reg(struct t1xxp *wc, int reg)
+{
+ unsigned char res;
+ select_control(wc);
+ res = inb(wc->ioaddr + WC_USERREG + ((reg & 0xf) << 2));
+ return res;
+}
+
+static void t1xxp_release(struct t1xxp *wc)
+{
+ kfree(wc);
+ printk("Freed a Wildcard\n");
+}
+
+static int t1xxp_close(struct zt_chan *chan)
+{
+ struct t1xxp *wc = chan->pvt;
+ wc->usecount--;
+ MOD_DEC_USE_COUNT;
+ /* If we're dead, release us now */
+ if (!wc->usecount && wc->dead)
+ t1xxp_release(wc);
+ return 0;
+}
+
+static inline void handle_leds(struct t1xxp *wc)
+{
+ if (wc->alarm) {
+ wc->alarmtimer++;
+ if (wc->alarmtimer == 40) {
+ wc->ledtestreg = wc->ledtestreg | BIT_ALARM;
+ control_set_reg(wc, WC_LEDTEST, wc->ledtestreg);
+ } else if (wc->alarmtimer == 160) {
+ wc->ledtestreg = wc->ledtestreg & ~BIT_ALARM;
+ control_set_reg(wc, WC_LEDTEST, wc->ledtestreg);
+ wc->alarmtimer = 0;
+ }
+ }
+#if 0
+ if (wc->alarmtimer == wc->alarmthreshold >> 2) {
+ wc->ledtestreg = wc->ledtestreg ^ BIT_ALARM;
+ control_set_reg(wc, WC_LEDTEST, wc->ledtestreg);
+ }
+ if (wc->alarmtimer == 0xf) {
+ wc->ledtestreg = wc->ledtestreg ^ BIT_ALARM;
+ control_set_reg(wc, WC_LEDTEST, wc->ledtestreg);
+ wc->alarmtimer = -1;
+ wc->alarmthreshold = (wc->alarmthreshold + 1 & 0x1f);
+ }
+#endif
+}
+
+static void t1xxp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ struct t1xxp *wc = dev_id;
+ unsigned char ints;
+ unsigned char b;
+ static int gotint = 0;
+
+ ints = inb(wc->ioaddr + WC_INTSTAT);
+ outb(ints, wc->ioaddr + WC_INTSTAT);
+ if (!gotint) {
+ printk("Got interrupt: 0x%04x\n", ints);
+ gotint++;
+ }
+ /* Initialize Write/Buffers to all blank data */
+ memset((void *)wc->writechunk,0x55,ZT_MAX_CHUNKSIZE * 2 * 2 * 24 * 4);
+ handle_leds(wc);
+
+
+#if 0
+ if (ints & 0x0f) {
+ tjmodem_transmitprep(wc, ints);
+ tjmodem_receiveprep(wc, ints);
+ }
+#endif
+
+ if (ints & 0x10)
+ printk("PCI Master abort\n");
+
+ if (ints & 0x20)
+ printk("PCI Target abort\n");
+
+}
+
+static int t1xxp_reset(struct t1xxp *wc)
+{
+ char c;
+ c = t1_get_reg(wc, 0x30);
+ /* Reset the Line Interface */
+ t1_set_reg(wc, 0x30, c | 0x1);
+
+ /* Wait 50ms for this to finish */
+ mdelay(50);
+
+ /* Clear it */
+ t1_set_reg(wc, 0x30, c);
+
+ /* Reset the elastic stores */
+ t1_set_reg(wc, 0x30, c | 0x40);
+ mdelay(1);
+ t1_set_reg(wc, 0x30, c);
+
+ return 0;
+}
+
+static int t1xxp_hardware_init(struct t1xxp *wc)
+{
+ int x;
+ /* Hardware Tigerjet stuff */
+ /* Reset TJ chip and registers */
+ outb(DELAY | 0x0e, wc->ioaddr + WC_CNTL);
+ /* Set all outputs to 0 */
+ outb(0x00, wc->ioaddr + WC_AUXD);
+ /* Set all to outputs except AUX1 (TDO). */
+ outb(0xfd, wc->ioaddr + WC_AUXC);
+ /* Configure the serial port: double clock, 20ns width, no inversion,
+ MSB first */
+ outb(0xc8, wc->ioaddr + WC_SERC);
+
+ /* Internally delay FSC by one */
+ outb(0x01, wc->ioaddr + 0x2f);
+
+ /* Back to normal, with automatic DMA wrap around */
+ outb(DELAY | 0x01, wc->ioaddr + WC_CNTL);
+
+ /* Make sure serial port and DMA are out of reset */
+ outb(inb(wc->ioaddr + WC_CNTL) & 0xf9, WC_CNTL);
+
+ /* Setup DMA Addresses */
+ outl(wc->writedma, wc->ioaddr + WC_DMAWS); /* Write start */
+ outl(wc->writedma + ZT_CHUNKSIZE * 96, wc->ioaddr + WC_DMAWI); /* Middle (interrupt) */
+ outl(wc->writedma + ZT_CHUNKSIZE * 192 - 4, wc->ioaddr + WC_DMAWE); /* End */
+
+ outl(wc->readdma, wc->ioaddr + WC_DMARS); /* Read start */
+ outl(wc->readdma + ZT_CHUNKSIZE * 96, wc->ioaddr + WC_DMARI); /* Middle (interrupt) */
+ outl(wc->readdma + ZT_CHUNKSIZE * 192 - 4, wc->ioaddr + WC_DMARE); /* End */
+
+ printk("Setting up DMA (write/read = %08lx/%08lx)\n", wc->writedma, wc->readdma);
+
+ /* Clear interrupts */
+ outb(0xff, wc->ioaddr + WC_INTSTAT);
+
+ /* Check out the controller */
+ printk("Controller version: %02x\n", control_get_reg(wc, WC_VERSION));
+ control_set_reg(wc, WC_LEDTEST, 0xff);
+ control_set_reg(wc, WC_CLOCK, 0x00);
+ /* Pretend we're in alarm */
+ wc->alarm = 1;
+#if 0
+ printk("LED/Test Reg test: expected %02x, got %02x\n", 0x55, control_get_reg(wc, WC_LEDTEST));
+ control_set_reg(wc, WC_LEDTEST, 0xAA);
+ printk("LED/Test Reg test: expected %02x, got %02x\n", 0xAA, control_get_reg(wc, WC_LEDTEST));
+
+ control_set_reg(wc, WC_CLOCK, 0x55);
+ printk("Clock Reg test: expected %02x, got %02x\n", 0x55, control_get_reg(wc, WC_CLOCK));
+ control_set_reg(wc, WC_CLOCK, 0xAA);
+ printk("Clock Reg test: expected %02x, got %02x\n", 0xAA, control_get_reg(wc, WC_CLOCK));
+#endif
+ return 0;
+#if 0
+ /* Reset all registers on the 2151 */
+ for (x=0x20;x<0x40;x++)
+ t1_set_reg(wc, x, 0);
+ for (x=0x60;x<0x80;x++)
+ t1_set_reg(wc, x, 0);
+ /* Sanity check the T1 */
+ if (t1_get_reg(wc, 0x7c) != 0) {
+ printk("Unable to talk to T1 part\n");
+ return -ENOSYS;
+ }
+ /* Enable elastic store for transmit and receive,
+ 2.048 Mhz bus */
+ t1_set_reg(wc, 0x37, 0x8c | 0x01);
+
+ /* TSync is an output */
+ t1_set_reg(wc, 0x36, 0x04);
+
+ /* RSync is an input (Elastic Store) */
+ t1_set_reg(wc, 0x2c, 0x08);
+
+ /* ESF/B8ZS */
+ t1_set_reg(wc, 0x38, 0xcc);
+ {
+ /* XXX Take me out XXX */
+ t1_set_reg(wc, 0x2d, 0xff);
+ t1_set_reg(wc, 0x2e, 0xff);
+ t1_set_reg(wc, 0x2f, 0xff);
+ }
+ return t1xxp_reset(wc);
+#endif
+
+}
+
+static void t1xxp_enable_interrupts(struct t1xxp *wc)
+{
+ /* Enable interrupts (we care about all of them) */
+ outb(0x3f, wc->ioaddr + WC_MASK0);
+ /* No external interrupts */
+ outb(0x00, wc->ioaddr + WC_MASK1);
+}
+
+static void t1xxp_start_dma(struct t1xxp *wc)
+{
+ /* Reset Master and TDM */
+ outb(DELAY | 0x0f, wc->ioaddr + WC_CNTL);
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1);
+ outb(DELAY | 0x01, wc->ioaddr + WC_CNTL);
+ outb(0x01, wc->ioaddr + WC_OPER);
+ printk("Started DMA\n");
+ { int x;
+ for (x=0;x<10;x++) {
+ printk("%d) Read at %08lx\n", x, inl(wc->ioaddr + WC_CURPOS));
+ udelay(10000);
+ } }
+ printk("0x0: %02x\n", (unsigned int)inb(wc->ioaddr + 0x0));
+ printk("0x1: %02x\n", (unsigned int)inb(wc->ioaddr + 0x1));
+ printk("0x2b: %02x\n", (unsigned int)inb(wc->ioaddr + 0x2b));
+ printk("0x2c: %02x\n", (unsigned int)inb(wc->ioaddr + 0x2c));
+ printk("0x2d: %02x\n", (unsigned int)inb(wc->ioaddr + 0x2d));
+ printk("0x2e: %02x\n", (unsigned int)inb(wc->ioaddr + 0x2e));
+ printk("0x2f: %02x\n", (unsigned int)inb(wc->ioaddr + 0x2f));
+}
+
+static void t1xxp_stop_dma(struct t1xxp *wc)
+{
+ outb(0x00, wc->ioaddr + WC_OPER);
+}
+
+static void t1xxp_disable_interrupts(struct t1xxp *wc)
+{
+ outb(0x00, wc->ioaddr + WC_MASK0);
+ outb(0x00, wc->ioaddr + WC_MASK1);
+}
+
+
+static int __devinit t1xxp_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+ int res;
+ struct t1xxp *wc;
+
+ if (pci_enable_device(pdev)) {
+ res = -EIO;
+ } else {
+ wc = kmalloc(sizeof(struct t1xxp), GFP_KERNEL);
+ if (wc) {
+ memset(wc, 0x0, sizeof(struct t1xxp));
+ wc->ioaddr = pci_resource_start(pdev, 0);
+ wc->dev = pdev;
+ wc->variety = (char *)(ent->driver_data);
+
+ wc->writechunk =
+ (int *)pci_alloc_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 24 * 4, &wc->writedma);
+ if (!wc->writechunk) {
+ printk("wcmodem: Unable to allocate DMA-able memory\n");
+ return -ENOMEM;
+ }
+
+ /* Read is after the whole write piece (in double words) */
+ wc->readchunk = wc->writechunk + ZT_CHUNKSIZE * 48;
+
+ /* Same thing, but in bytes */
+ wc->readdma = wc->writedma + ZT_CHUNKSIZE * 192;
+
+ /* Initialize Write/Buffers to all blank data */
+ memset((void *)wc->writechunk,0xff,ZT_MAX_CHUNKSIZE * 2 * 2 * 24 * 4);
+
+ /* Enable bus mastering */
+ pci_set_master(pdev);
+
+ /* Keep track of which device we are */
+ pci_set_drvdata(pdev, wc);
+
+ if (request_irq(pdev->irq, t1xxp_interrupt, SA_SHIRQ, "t1xxp", wc)) {
+ printk("t1xxp: Unable to request IRQ %d\n", pdev->irq);
+ kfree(wc);
+ return -EIO;
+ }
+
+
+ t1xxp_hardware_init(wc);
+ /* Enable interrupts */
+ t1xxp_enable_interrupts(wc);
+
+ /* Initialize Write/Buffers to all blank data */
+ memset((void *)wc->writechunk,0,ZT_MAX_CHUNKSIZE * 2 * 2 * 24 * 4);
+ /* Start DMA */
+ t1xxp_start_dma(wc);
+
+ printk("Found a Wildcard: %s\n", wc->variety);
+ res = 0;
+ } else
+ res = -ENOMEM;
+ }
+ return res;
+}
+
+static void __devexit t1xxp_remove_one(struct pci_dev *pdev)
+{
+ struct t1xxp *wc = pci_get_drvdata(pdev);
+ if (wc) {
+
+ /* Stop any DMA */
+ t1xxp_stop_dma(wc);
+
+ /* In case hardware is still there */
+ t1xxp_disable_interrupts(wc);
+
+ /* Immediately free resources */
+ pci_free_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 24 * 4, (void *)wc->writechunk, wc->writedma);
+ free_irq(pdev->irq, wc);
+
+ /* Reset TJ chip and registers */
+ outb(DELAY | 0x0e, wc->ioaddr + WC_CNTL);
+
+ /* Release span, possibly delayed */
+ if (!wc->usecount)
+ t1xxp_release(wc);
+ else
+ wc->dead = 1;
+ }
+}
+
+static struct pci_device_id t1xxp_pci_tbl[] __devinitdata = {
+ { 0xe159, 0x0001, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long) "LSS Wildcard T100P T1/PRI Board" },
+};
+
+static struct pci_driver t1xxp_driver = {
+ name: "t1xxp",
+ probe: t1xxp_init_one,
+ remove: t1xxp_remove_one,
+ suspend: NULL,
+ resume: NULL,
+ id_table: t1xxp_pci_tbl,
+};
+
+static int __init t1xxp_init(void)
+{
+ int res;
+ res = pci_module_init(&t1xxp_driver);
+ if (res)
+ return -ENODEV;
+ return 0;
+}
+
+static void __exit t1xxp_cleanup(void)
+{
+ pci_unregister_driver(&t1xxp_driver);
+}
+
+MODULE_PARM(debug, "i");
+MODULE_DESCRIPTION("Wildcard T100P Zaptel Driver");
+MODULE_AUTHOR("Mark Spencer <markster@linux-support.net>");
+
+module_init(t1xxp_init);
+module_exit(t1xxp_cleanup);