From 6eeafd614e36021635d37840f4bfaa5f34395e34 Mon Sep 17 00:00:00 2001 From: markster Date: Wed, 7 Nov 2001 17:47:29 +0000 Subject: Version 0.1.0 from FTP git-svn-id: http://svn.digium.com/svn/zaptel/trunk@22 5390a7c7-147a-4af0-8ec9-7488f05a26cb --- wct1xxp.c | 514 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 514 insertions(+) create mode 100755 wct1xxp.c (limited to 'wct1xxp.c') 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 + * Matthew Fredrickson + * William Meadows + * + * 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 +#include +#include +#include +#include +#include +#include +#ifdef STANDALONE_ZAPATA +#include "zaptel.h" +#else +#include +#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 "); + +module_init(t1xxp_init); +module_exit(t1xxp_cleanup); -- cgit v1.2.3