diff options
author | markster <markster@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2003-07-09 21:48:21 +0000 |
---|---|---|
committer | markster <markster@5390a7c7-147a-4af0-8ec9-7488f05a26cb> | 2003-07-09 21:48:21 +0000 |
commit | e7a501944bd6aa669edfcda579987089c247e37d (patch) | |
tree | c50d5451b23d8137b7222ae87e9c85ff3934200c /wct4xxp.c | |
parent | c39e312572dbd0769a616c94986d1d935f74807c (diff) |
Add TE410P driver
git-svn-id: http://svn.digium.com/svn/zaptel/trunk@210 5390a7c7-147a-4af0-8ec9-7488f05a26cb
Diffstat (limited to 'wct4xxp.c')
-rwxr-xr-x | wct4xxp.c | 1628 |
1 files changed, 1628 insertions, 0 deletions
diff --git a/wct4xxp.c b/wct4xxp.c new file mode 100755 index 0000000..21c8225 --- /dev/null +++ b/wct4xxp.c @@ -0,0 +1,1628 @@ +/* + * TE410P Quad-T1/E1 PCI Driver version 0.1, 12/16/02 + * + * Written by Mark Spencer <markster@linux-support.net> + * Based on previous works, designs, and archetectures conceived and + * written by Jim Dixon <jim@lambdatel.com>. + * + * Copyright (C) 2001 Jim Dixon / Zapata Telephony. + * 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/pci.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#ifdef STANDALONE_ZAPATA +#include "zaptel.h" +#else +#include <linux/zaptel.h> +#endif +#include "wct4xxp.h" + +/* + * Tasklets provide better system interactive response at the cost of the + * possibility of losing a frame of data at very infrequent intervals. If + * you are more concerned with the performance of your machine, enable the + * tasklets. If you are strict about absolutely no drops, then do not enable + * tasklets. + */ + +/* #define ENABLE_TASKLETS */ + +/* Define to get more attention-grabbing but slightly more I/O using + alarm status */ +#define FANCY_ALARM + +static int debug; +static int timingcable; +static int highestorder; +static int t1e1override = -1; + +#ifdef FANCY_ALARM +static int altab[] = { +0, 0, 0, 1, 2, 3, 4, 6, 8, 9, 11, 13, 16, 18, 20, 22, 24, 25, 27, 28, 29, 30, 31, 31, 32, 31, 31, 30, 29, 28, 27, 25, 23, 22, 20, 18, 16, 13, 11, 9, 8, 6, 4, 3, 2, 1, 0, 0, +}; +#endif + +#define MAX_SPANS 16 + +#define FLAG_STARTED (1 << 0) + +#define TYPE_T1 1 /* is a T1 card */ +#define TYPE_E1 2 /* is an E1 card */ + +#define CANARY 0xc0de + +static int inirq = 0; + +struct t4 { + /* This structure exists one per card */ + struct pci_dev *dev; /* Pointer to PCI device */ + int intcount; + int num; /* Which card we are */ + int t1e1; /* T1/E1 select pins */ + int globalconfig; /* Whether global setup has been done */ + int last; /* XXX DEBUG area XXX */ + int olast; /* XXX DEBUG area XXX */ + int syncsrc; /* active sync source */ + int syncs[4]; /* sync sources */ + int psyncs[4]; /* span-relative sync sources */ + int alarmtimer[4]; /* Alarm timer */ + int blinktimer; +#ifdef FANCY_ALARM + int alarmpos; +#endif + char *type; /* Type of tormenta 2 card */ + int irq; /* IRQ used by device */ + int order; /* Order */ + int flags; /* Device flags */ + int syncpos[4]; /* span-relative sync sources */ + int master; /* Are we master */ + int ledreg; /* LED Register */ + unsigned int dmactrl; + dma_addr_t readdma; + dma_addr_t writedma; + unsigned long memaddr; /* Base address of card */ + unsigned long memlen; + volatile unsigned int *membase; /* Base address of card */ + struct zt_span spans[4]; /* Spans */ + struct zt_chan *chans[4]; /* Pointers to blocks of 24(30/31) contiguous zt_chans for each span */ + unsigned char txsigs[4][16]; /* Copy of tx sig registers */ + int loopupcnt[4]; /* loop up code counter */ + int loopdowncnt[4]; /* loop down code counter */ + int spansstarted; /* number of spans started */ + /* spinlock_t lock; */ /* lock context */ + spinlock_t reglock; /* lock register access */ + unsigned char ec_chunk1[4][31][ZT_CHUNKSIZE]; /* first EC chunk buffer */ + unsigned char ec_chunk2[4][31][ZT_CHUNKSIZE]; /* second EC chunk buffer */ + volatile unsigned int *writechunk; /* Double-word aligned write memory */ + volatile unsigned int *readchunk; /* Double-word aligned read memory */ + unsigned short canary; +#ifdef ENABLE_TASKLETS + int taskletrun; + int taskletsched; + int taskletpending; + int taskletexec; + int txerrors; + struct tasklet_struct tor2_tlet; +#endif + int spantype[4]; /* card type, T1 or E1 */ + unsigned int passno; /* number of interrupt passes */ + char *variety; +}; + +static void set_clear(struct t4 *wc, int span); +static int t4_startup(struct zt_span *span); +static int t4_shutdown(struct zt_span *span); +static int t4_rbsbits(struct zt_chan *chan, int bits); +static int t4_maint(struct zt_span *span, int cmd); +static int t4_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data); + +#define WC_RDADDR 0 +#define WC_WRADDR 1 +#define WC_COUNT 2 +#define WC_DMACTRL 3 +#define WC_INTR 4 +/* #define WC_GPIO 5 */ +#define WC_VERSION 6 +#define WC_LEDS 7 +#define WC_ACTIVATE (1 << 12) +#define WC_GPIOCTL 8 +#define WC_GPIO 9 +#define WC_LADDR 10 +#define WC_LDATA 11 +#define WC_LREAD (1 << 15) +#define WC_LWRITE (1 << 16) + +#define WC_OFF (0) +#define WC_RED (1) +#define WC_GREEN (2) +#define WC_YELLOW (3) + +#define MAX_T4_CARDS 64 + +#ifdef ENABLE_TASKLETS +static void t4_tasklet(unsigned long data); +#endif + +static struct t4 *cards[MAX_T4_CARDS]; + +static inline void __t4_pci_out(struct t4 *wc, const unsigned int addr, const unsigned int value) +{ + unsigned int tmp; + wc->membase[addr] = value; +#if 1 + tmp = wc->membase[addr]; + if ((value != tmp) && (addr != WC_LEDS) && (addr != WC_LDATA) && + (addr != WC_GPIO)) + printk("Tried to load %08x into %08x, but got %08x instead\n", value, addr, tmp); +#endif +} + +static inline unsigned int __t4_pci_in(struct t4 *wc, const unsigned int addr) +{ + return wc->membase[addr]; +} + +static inline void t4_pci_out(struct t4 *wc, const unsigned int addr, const unsigned int value) +{ + long flags; + spin_lock_irqsave(&wc->reglock, flags); + __t4_pci_out(wc, addr, value); + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static inline void __t4_set_led(struct t4 *wc, int span, int color) +{ + int oldreg = wc->ledreg; + wc->ledreg &= ~(0x3 << (span << 1)); + wc->ledreg |= (color << (span << 1)); + if (oldreg != wc->ledreg) + __t4_pci_out(wc, WC_LEDS, wc->ledreg); +} + +static inline void t4_activate(struct t4 *wc) +{ + wc->ledreg |= WC_ACTIVATE; + t4_pci_out(wc, WC_LEDS, wc->ledreg); +} + +static inline unsigned int t4_pci_in(struct t4 *wc, const unsigned int addr) +{ + unsigned int ret; + long flags; + + spin_lock_irqsave(&wc->reglock, flags); + ret = __t4_pci_in(wc, addr); + spin_unlock_irqrestore(&wc->reglock, flags); + return ret; +} + +static inline unsigned int __t4_framer_in(struct t4 *wc, int unit, const unsigned int addr) +{ + unsigned int ret; + unit &= 0x3; + __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff)); + __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff) | ( 1 << 10) | WC_LREAD); + ret = __t4_pci_in(wc, WC_LDATA); + __t4_pci_out(wc, WC_LADDR, 0); + return ret & 0xff; +} + +static inline unsigned int t4_framer_in(struct t4 *wc, int unit, const unsigned int addr) +{ + unsigned long flags; + unsigned int ret; + spin_lock_irqsave(&wc->reglock, flags); + ret = __t4_framer_in(wc, unit, addr); + spin_unlock_irqrestore(&wc->reglock, flags); + return ret; + +} + +static inline void __t4_framer_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value) +{ + unit &= 0x3; + if (debug) + printk("Writing %02x to address %02x of unit %d\n", value, addr, unit); + __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff)); + __t4_pci_out(wc, WC_LDATA, value); + __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff) | (1 << 10)); + __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff) | (1 << 10) | WC_LWRITE); + __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff) | (1 << 10)); + __t4_pci_out(wc, WC_LADDR, (unit << 8) | (addr & 0xff)); + __t4_pci_out(wc, WC_LADDR, 0); + if (debug) printk("Write complete\n"); +#if 0 + { unsigned int tmp; + tmp = t4_framer_in(wc, unit, addr); + if (tmp != value) { + printk("Expected %d from unit %d register %d but got %d instead\n", value, unit, addr, tmp); + } } +#endif +} + +static inline void t4_framer_out(struct t4 *wc, int unit, const unsigned int addr, const unsigned int value) +{ + unsigned long flags; + spin_lock_irqsave(&wc->reglock, flags); + __t4_framer_out(wc, unit, addr, value); + spin_unlock_irqrestore(&wc->reglock, flags); +} + + +static void __set_clear(struct t4 *wc, int span) +{ + int i,j; + unsigned short val=0; + for (i=0;i<24;i++) { + j = (i/8); + if (wc->spans[span].chans[i].flags & ZT_FLAG_CLEAR) + val |= 1 << (7 - (i % 8)); + if ((i % 8)==7) { + if (debug) + printk("Putting %d in register %02x on span %d\n", + val, 0x2f + j, span + 1); + __t4_framer_out(wc,span, 0x2f + j, val); + val = 0; + } + } +} + +static void set_clear(struct t4 *wc, int span) +{ + unsigned long flags; + spin_lock_irqsave(&wc->reglock, flags); + __set_clear(wc, span); + spin_unlock_irqrestore(&wc->reglock, flags); +} + +static int t4_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data) +{ + struct t4_regs regs; + int x; + switch(cmd) { + case WCT4_GET_REGS: + for (x=0;x<NUM_PCI;x++) + regs.pci[x] = t4_pci_in(chan->pvt, x); + for (x=0;x<NUM_REGS;x++) + regs.regs[x] = t4_framer_in(chan->pvt, chan->span->offset, x); + if (copy_to_user((struct t4_regs *)data, ®s, sizeof(regs))) + return -EFAULT; + break; + default: + return -ENOTTY; + } + return 0; +} + +static int t4_maint(struct zt_span *span, int cmd) +{ + struct t4 *wc = span->pvt; + + if (wc->spantype[span->offset] == TYPE_E1) { + switch(cmd) { + case ZT_MAINT_NONE: + printk("XXX Turn off local and remote loops E1 XXX\n"); + break; + case ZT_MAINT_LOCALLOOP: + printk("XXX Turn on local loopback E1 XXX\n"); + break; + case ZT_MAINT_REMOTELOOP: + printk("XXX Turn on remote loopback E1 XXX\n"); + break; + case ZT_MAINT_LOOPUP: + printk("XXX Send loopup code E1 XXX\n"); + break; + case ZT_MAINT_LOOPDOWN: + printk("XXX Send loopdown code E1 XXX\n"); + break; + case ZT_MAINT_LOOPSTOP: + printk("XXX Stop sending loop codes E1 XXX\n"); + break; + default: + printk("TE410P: Unknown E1 maint command: %d\n", cmd); + break; + } + } else { + switch(cmd) { + case ZT_MAINT_NONE: + printk("XXX Turn off local and remote loops T1 XXX\n"); + break; + case ZT_MAINT_LOCALLOOP: + printk("XXX Turn on local loop and no remote loop XXX\n"); + break; + case ZT_MAINT_REMOTELOOP: + printk("XXX Turn on remote loopup XXX\n"); + break; + case ZT_MAINT_LOOPUP: + t4_framer_out(wc, span->offset, 0x21, 0x50); /* FMR5: Nothing but RBS mode */ + break; + case ZT_MAINT_LOOPDOWN: + t4_framer_out(wc, span->offset, 0x21, 0x60); /* FMR5: Nothing but RBS mode */ + break; + case ZT_MAINT_LOOPSTOP: + t4_framer_out(wc, span->offset, 0x21, 0x40); /* FMR5: Nothing but RBS mode */ + break; + default: + printk("TE410P: Unknown E1 maint command: %d\n", cmd); + break; + } + } + return 0; +} + +static int t4_rbsbits(struct zt_chan *chan, int bits) +{ + u_char m,c; + int k,n,b; + struct t4 *wc = chan->pvt; + unsigned long flags; + + spin_lock_irqsave(&wc->reglock, flags); + if(debug) printk("Setting bits to %d on channel %s\n", bits, chan->name); + k = chan->span->offset; + if (wc->spantype[k] == TYPE_E1) { /* do it E1 way */ + if (chan->chanpos == 16) { + spin_unlock_irqrestore(&wc->reglock, flags); + return 0; + } + n = chan->chanpos - 1; + if (chan->chanpos > 15) n--; + b = (n % 15); + c = wc->txsigs[k][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[k][b] = c; + /* output them to the chip */ + __t4_framer_out(wc,k,0x71 + b,c); + } else if (wc->spans[k].lineconfig & ZT_CONFIG_D4) { + n = chan->chanpos - 1; + b = (n/4); + c = wc->txsigs[k][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[k][b] = c; + /* output them to the chip */ + __t4_framer_out(wc,k,0x70 + b,c); + __t4_framer_out(wc,k,0x70 + b + 6,c); + } else if (wc->spans[k].lineconfig & ZT_CONFIG_ESF) { + n = chan->chanpos - 1; + b = (n/2); + c = wc->txsigs[k][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[k][b] = c; + /* output them to the chip */ + __t4_framer_out(wc,k,0x70 + b,c); + } + if (debug) + printk("Finished setting RBS bits\n"); + spin_unlock_irqrestore(&wc->reglock, flags); + return 0; +} + +static int t4_shutdown(struct zt_span *span) +{ + int tspan; + int wasrunning; + unsigned long flags; + struct t4 *wc = span->pvt; + + tspan = span->offset + 1; + if (tspan < 0) { + printk("T4XXP: Span '%d' isn't us?\n", span->spanno); + return -1; + } + + spin_lock_irqsave(&wc->reglock, flags); + wasrunning = span->flags & ZT_FLAG_RUNNING; + + span->flags &= ~ZT_FLAG_RUNNING; + if (wasrunning) + wc->spansstarted--; + __t4_set_led(wc, span->offset, WC_OFF); + if ((!(wc->spans[0].flags & ZT_FLAG_RUNNING)) && + (!(wc->spans[1].flags & ZT_FLAG_RUNNING)) && + (!(wc->spans[2].flags & ZT_FLAG_RUNNING)) && + (!(wc->spans[3].flags & ZT_FLAG_RUNNING))) { + /* No longer in use, disable interrupts */ + printk("TE410P: Disabling interrupts since there are no active spans\n"); + wc->dmactrl = 0x0; + __t4_pci_out(wc, WC_DMACTRL, 0x00000000); + /* Acknowledge any pending interrupts */ + __t4_pci_out(wc, WC_INTR, 0x00000000); + } + spin_unlock_irqrestore(&wc->reglock, flags); + if (debug) + printk("Span %d (%s) shutdown\n", span->spanno, span->name); + return 0; +} + +static int t4_spanconfig(struct zt_span *span, struct zt_lineconfig *lc) +{ + int i; + struct t4 *wc = span->pvt; + + if (debug) + printk("TE410P: Configuring span %d\n", span->spanno); + /* XXX We assume lineconfig is okay and shouldn't XXX */ + span->lineconfig = lc->lineconfig; + span->txlevel = lc->lbo; + span->rxlevel = 0; + span->syncsrc = wc->syncsrc; + + /* remove this span number from the current sync sources, if there */ + for(i = 0; i < 3; i++) { + if (wc->syncs[i] == span->spanno) { + wc->syncs[i] = 0; + wc->psyncs[i] = 0; + } + } + wc->syncpos[span->offset] = lc->sync; + /* if a sync src, put it in proper place */ + if (lc->sync) { + wc->syncs[lc->sync - 1] = span->spanno; + wc->psyncs[lc->sync - 1] = span->offset + 1; + } + /* If we're already running, then go ahead and apply the changes */ + if (span->flags & ZT_FLAG_RUNNING) + return t4_startup(span); + return 0; +} + +static int t4_chanconfig(struct zt_chan *chan, int sigtype) +{ + int alreadyrunning; + unsigned long flags; + struct t4 *wc = chan->pvt; + + alreadyrunning = wc->spans[chan->span->offset].flags & ZT_FLAG_RUNNING; + if (debug) { + if (alreadyrunning) + printk("TE410P: Reconfigured channel %d (%s) sigtype %d\n", chan->channo, chan->name, sigtype); + else + printk("TE410P: Configured channel %d (%s) sigtype %d\n", chan->channo, chan->name, sigtype); + } + /* nothing more to do if an E1 */ + if (wc->spantype[chan->span->offset] == TYPE_E1) return 0; + spin_lock_irqsave(&wc->reglock, flags); + if (alreadyrunning) + __set_clear(wc, chan->span->offset); + spin_unlock_irqrestore(&wc->reglock, flags); + return 0; +} + +static int t4_open(struct zt_chan *chan) +{ + MOD_INC_USE_COUNT; + return 0; +} + +static int t4_close(struct zt_chan *chan) +{ + MOD_DEC_USE_COUNT; + return 0; +} + +static void init_spans(struct t4 *wc) +{ + int x,y,c; + for (x=0;x<4;x++) { + sprintf(wc->spans[x].name, "TE4/%d/%d", + wc->num, x + 1); + sprintf(wc->spans[x].desc, "TE410P (PCI) Card %d Span %d", wc->num, x+1); + wc->spans[x].spanconfig = t4_spanconfig; + wc->spans[x].chanconfig = t4_chanconfig; + wc->spans[x].startup = t4_startup; + wc->spans[x].shutdown = t4_shutdown; + wc->spans[x].rbsbits = t4_rbsbits; + wc->spans[x].maint = t4_maint; + wc->spans[x].open = t4_open; + wc->spans[x].close = t4_close; + if (wc->spantype[x] == TYPE_T1) { + wc->spans[x].channels = 24; + wc->spans[x].deflaw = ZT_LAW_MULAW; + } else { + wc->spans[x].channels = 31; + wc->spans[x].deflaw = ZT_LAW_ALAW; + } + wc->spans[x].chans = wc->chans[x]; + wc->spans[x].flags = ZT_FLAG_RBS; + wc->spans[x].linecompat = ZT_CONFIG_AMI | ZT_CONFIG_B8ZS | ZT_CONFIG_D4 | ZT_CONFIG_ESF; + wc->spans[x].ioctl = t4_ioctl; + wc->spans[x].pvt = wc; + wc->spans[x].offset = x; + init_waitqueue_head(&wc->spans[x].maintq); + for (y=0;y<wc->spans[x].channels;y++) { + struct zt_chan *mychans = wc->chans[x] + y; + sprintf(mychans->name, "TE4/%d/%d/%d", wc->num, x + 1, y + 1); + mychans->sigcap = ZT_SIG_EM | ZT_SIG_CLEAR | ZT_SIG_FXSLS | ZT_SIG_FXSGS | ZT_SIG_FXSKS | + ZT_SIG_FXOLS | ZT_SIG_FXOGS | ZT_SIG_FXOKS | ZT_SIG_CAS; + c = (x * wc->spans[x].channels) + y; + mychans->pvt = wc; + mychans->chanpos = y + 1; + } + } +} + +static void t4_serial_setup(struct t4 *wc, int unit) +{ + if (!wc->globalconfig) { + wc->globalconfig = 1; + printk("TE410P: Setting up global serial parameters\n"); + t4_framer_out(wc, 0, 0x85, 0xe0); /* GPC1: Multiplex mode enabled, FSC is output, active low, RCLK from channel 0 */ + t4_framer_out(wc, 0, 0x08, 0x01); /* IPC: Interrupt push/pull active low */ + + /* Global clocks (8.192 Mhz CLK) */ + t4_framer_out(wc, 0, 0x92, 0x00); + t4_framer_out(wc, 0, 0x93, 0x18); + t4_framer_out(wc, 0, 0x94, 0xfb); + t4_framer_out(wc, 0, 0x95, 0x0b); + t4_framer_out(wc, 0, 0x96, 0x00); + t4_framer_out(wc, 0, 0x97, 0x0b); + t4_framer_out(wc, 0, 0x98, 0xdb); + t4_framer_out(wc, 0, 0x99, 0xdf); + } + + /* Configure interrupts */ + t4_framer_out(wc, unit, 0x46, 0x40); /* GCR: Interrupt on Activation/Deactivation of AIX, LOS */ + + /* Configure system interface */ + t4_framer_out(wc, unit, 0x3e, 0xc2); /* SIC1: 8.192 Mhz clock/bus, double buffer receive / transmit, byte interleaved */ + t4_framer_out(wc, unit, 0x3f, unit << 1); /* SIC2: No FFS, no center receive eliastic buffer, phase */ + t4_framer_out(wc, unit, 0x40, 0x04); /* SIC3: Edges for capture */ + t4_framer_out(wc, unit, 0x45, 0x00); /* CMR2: We provide sync and clock for tx and rx. */ + t4_framer_out(wc, unit, 0x22, 0x00); /* XC0: Normal operation of Sa-bits */ + t4_framer_out(wc, unit, 0x23, 0x04); /* XC1: 0 offset */ + t4_framer_out(wc, unit, 0x24, 0x04); /* RC0: Just shy of 1023 */ + t4_framer_out(wc, unit, 0x25, 0x04); /* RC1: The rest of RC0 */ + + /* Configure ports */ + t4_framer_out(wc, unit, 0x80, 0x00); /* PC1: SPYR/SPYX input on RPA/XPA */ + t4_framer_out(wc, unit, 0x81, 0x22); /* PC2: RMFB/XSIG output/input on RPB/XPB */ + t4_framer_out(wc, unit, 0x82, 0x65); /* PC3: Some unused stuff */ + t4_framer_out(wc, unit, 0x83, 0x35); /* PC4: Some more unused stuff */ + t4_framer_out(wc, unit, 0x84, 0x01); /* PC5: XMFS active low, SCLKR is input, RCLK is output */ + if (debug) + printk("Successfully initialized serial bus for unit %d\n", unit); +} + +static void t4_set_timing_source(struct t4 *wc, int unit) +{ + unsigned int timing; + int x; + timing = 0x34; /* CMR1: RCLK unit, 8.192 Mhz TCLK, RCLK is 8.192 Mhz */ + if ((unit > -1) && (unit < 4)) { + timing |= (unit << 6); + } else { + timing |= 0x1; /* Use TCLK timing */ + } + for (x=0;x<4;x++) + t4_framer_out(wc, x, 0x44, timing); + if ((unit > -1) && (unit < 4)) + t4_pci_out(wc, WC_DMACTRL, wc->dmactrl | (1 << 29)); + else + t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + printk("TE410P: Timing from source %d\n", unit); +} + +static void __t4_configure_t1(struct t4 *wc, int unit, int lineconfig, int txlevel) +{ + unsigned int fmr4, fmr2, fmr1, fmr0, lim2; + char *framing, *line; + int mytxlevel; + if ((txlevel > 7) || (txlevel < 4)) + mytxlevel = 0; + else + mytxlevel = txlevel - 4; + fmr1 = 0x9c; /* FMR1: Mode 1, T1 mode, CRC on for ESF, 8.192 Mhz system data rate, no XAIS */ + fmr2 = 0x22; /* FMR2: no payload loopback, auto send yellow alarm */ + fmr4 = 0x0c; /* FMR4: Lose sync on 2 out of 5 framing bits, auto resync */ + lim2 = 0x21; /* LIM2: 50% peak is a "1", Advanced Loss recovery */ + lim2 |= (mytxlevel << 6); /* LIM2: Add line buildout */ + __t4_framer_out(wc, unit, 0x1d, fmr1); + __t4_framer_out(wc, unit, 0x1e, fmr2); + + /* Configure line interface */ + if (lineconfig & ZT_CONFIG_AMI) { + line = "AMI"; + fmr0 = 0xa0; + } else { + line = "B8ZS"; + fmr0 = 0xf0; + } + if (lineconfig & ZT_CONFIG_D4) { + framing = "D4"; + } else { + framing = "ESF"; + fmr4 |= 0x2; + fmr2 |= 0xc0; + } + __t4_framer_out(wc, unit, 0x1c, fmr0); + __t4_framer_out(wc, unit, 0x20, fmr4); + __t4_framer_out(wc, unit, 0x21, 0x40); /* FMR5: Enable RBS mode */ + + __t4_framer_out(wc, unit, 0x37, 0xf0 ); /* LIM1: Clear data in case of LOS, Set receiver threshold (0.5V), No remote loop, no DRS */ + __t4_framer_out(wc, unit, 0x36, 0x08); /* LIM0: Enable auto long haul mode, no local loop (must be after LIM1) */ + + __t4_framer_out(wc, unit, 0x02, 0x50); /* CMDR: Reset the receiver and transmitter line interface */ + __t4_framer_out(wc, unit, 0x02, 0x00); /* CMDR: Reset the receiver and transmitter line interface */ + + __t4_framer_out(wc, unit, 0x3a, lim2); /* LIM2: 50% peak amplitude is a "1" */ + __t4_framer_out(wc, unit, 0x38, 0x0a); /* PCD: LOS after 176 consecutive "zeros" */ + __t4_framer_out(wc, unit, 0x39, 0x15); /* PCR: 22 "ones" clear LOS */ + + /* Generate pulse mask for T1 */ + __t4_framer_out(wc, unit, 0x26, 0xd7); /* XPM0 */ + __t4_framer_out(wc, unit, 0x27, 0x22); /* XPM1 */ + __t4_framer_out(wc, unit, 0x28, 0x01); /* XPM2 */ + printk("TE410P: Span %d configured for %s/%s\n", unit + 1, framing, line); +} + +static void __t4_configure_e1(struct t4 *wc, int unit, int lineconfig) +{ + unsigned int fmr2, fmr1, fmr0; + char *crc4 = ""; + char *framing, *line; + fmr1 = 0x4c; /* FMR1: E1 mode, Automatic force resync, PCM30 mode, 8.192 Mhz backplane, no XAIS */ + fmr2 = 0x03 /* | 0x4 */; /* FMR2: Auto transmit remote alarm, auto loss of multiframe recovery, no payload loopback */ + if (lineconfig & ZT_CONFIG_CRC4) { + fmr1 |= 0x08; /* CRC4 transmit */ + fmr2 |= 0x80; /* CRC4 receive */ + crc4 = "/CRC4"; + } + __t4_framer_out(wc, unit, 0x1d, fmr1); + __t4_framer_out(wc, unit, 0x1e, fmr2); + + /* Configure line interface */ + if (lineconfig & ZT_CONFIG_AMI) { + line = "AMI"; + fmr0 = 0xa0; + } else { + line = "HDB3"; + fmr0 = 0xf0; + } + if (lineconfig & ZT_CONFIG_CCS) { + framing = "CCS"; + } else { + framing = "CAS"; + } + __t4_framer_out(wc, unit, 0x1c, fmr0); + + __t4_framer_out(wc, unit, 0x37, 0xf0 /*| 0x6 */ ); /* LIM1: Clear data in case of LOS, Set receiver threshold (0.5V), No remote loop, no DRS */ + __t4_framer_out(wc, unit, 0x36, 0x08); /* LIM0: Enable auto long haul mode, no local loop (must be after LIM1) */ + + __t4_framer_out(wc, unit, 0x02, 0x50); /* CMDR: Reset the receiver and transmitter line interface */ + __t4_framer_out(wc, unit, 0x02, 0x00); /* CMDR: Reset the receiver and transmitter line interface */ + + /* Condition receive line interface for E1 after reset */ + __t4_framer_out(wc, unit, 0xbb, 0x17); + __t4_framer_out(wc, unit, 0xbc, 0x55); + __t4_framer_out(wc, unit, 0xbb, 0x97); + __t4_framer_out(wc, unit, 0xbb, 0x11); + __t4_framer_out(wc, unit, 0xbc, 0xaa); + __t4_framer_out(wc, unit, 0xbb, 0x91); + __t4_framer_out(wc, unit, 0xbb, 0x12); + __t4_framer_out(wc, unit, 0xbc, 0x55); + __t4_framer_out(wc, unit, 0xbb, 0x92); + __t4_framer_out(wc, unit, 0xbb, 0x0c); + __t4_framer_out(wc, unit, 0xbb, 0x00); + __t4_framer_out(wc, unit, 0xbb, 0x8c); + + __t4_framer_out(wc, unit, 0x3a, 0x20); /* LIM2: 50% peak amplitude is a "1" */ + __t4_framer_out(wc, unit, 0x38, 0x0a); /* PCD: LOS after 176 consecutive "zeros" */ + __t4_framer_out(wc, unit, 0x39, 0x15); /* PCR: 22 "ones" clear LOS */ + + __t4_framer_out(wc, unit, 0x20, 0x9f); /* XSW: Spare bits all to 1 */ + __t4_framer_out(wc, unit, 0x21, 0x1c); /* XSP: E-bit set when async. AXS auto, XSIF to 1 */ + + + /* Generate pulse mask for E1 */ + __t4_framer_out(wc, unit, 0x26, 0x9c); /* XPM0 */ + __t4_framer_out(wc, unit, 0x27, 0x03); /* XPM1 */ + __t4_framer_out(wc, unit, 0x28, 0x00); /* XPM2 */ + printk("TE410P: Span %d configured for %s/%s%s\n", unit + 1, framing, line, crc4); +} + +static int t4_startup(struct zt_span *span) +{ + int i; + int tspan; + unsigned long flags; + int alreadyrunning; + struct t4 *wc = span->pvt; + + tspan = span->offset + 1; + if (tspan < 0) { + printk("TE410P: Span '%d' isn't us?\n", span->spanno); + return -1; + } + + spin_lock_irqsave(&wc->reglock, flags); + + alreadyrunning = span->flags & ZT_FLAG_RUNNING; + + /* initialize the start value for the entire chunk of last ec buffer */ + for(i = 0; i < span->channels; i++) + { + memset(wc->ec_chunk1[span->offset][i], + ZT_LIN2X(0,&span->chans[i]),ZT_CHUNKSIZE); + memset(wc->ec_chunk2[span->offset][i], + ZT_LIN2X(0,&span->chans[i]),ZT_CHUNKSIZE); + } + /* Force re-evaluation fo timing source */ + if (timingcable) + wc->syncsrc = -1; + + if (wc->spantype[span->offset] == TYPE_E1) { /* if this is an E1 card */ + __t4_configure_e1(wc, span->offset, span->lineconfig); + } else { /* is a T1 card */ + __t4_configure_t1(wc, span->offset, span->lineconfig, span->txlevel); + __set_clear(wc, span->offset); + } + + if (!alreadyrunning) { + span->flags |= ZT_FLAG_RUNNING; + wc->spansstarted++; + /* enable interrupts */ + /* Start DMA, enabling DMA interrupts on read only */ + wc->dmactrl = 0xc0000003 | (1 << 29); + __t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + } + + spin_unlock_irqrestore(&wc->reglock, flags); + if (wc->syncs[0] == span->spanno) printk("SPAN %d: Primary Sync Source\n",span->spanno); + if (wc->syncs[1] == span->spanno) printk("SPAN %d: Secondary Sync Source\n",span->spanno); + if (wc->syncs[2] == span->spanno) printk("SPAN %d: Tertiary Sync Source\n",span->spanno); + if (wc->syncs[3] == span->spanno) printk("SPAN %d: Quaternary Sync Source\n",span->spanno); + return 0; +} + +static int syncsrc = 0; +static int syncnum = 0 /* -1 */; +static int syncspan = 0; +static spinlock_t synclock = SPIN_LOCK_UNLOCKED; + +static int t4_findsync(struct t4 *wc) +{ + int i; + int x; + long flags; + int p; + int nonzero; + int newsyncsrc = 0; /* Zaptel span number */ + int newsyncnum = 0; /* tor2 card number */ + int newsyncspan = 0; /* span on given tor2 card */ + spin_lock_irqsave(&synclock, flags); +#if 1 + if (!wc->num) { + /* If we're the first card, go through all the motions, up to 8 levels + of sync source */ + p = 1; + while(p < 8) { + nonzero = 0; + for (x=0;cards[x];x++) { + for (i=0;i<4;i++) { + if (cards[x]->syncpos[i]) { + nonzero = 1; + if ((cards[x]->syncpos[i] == p) && + !(cards[x]->spans[i].alarms & (ZT_ALARM_RED | ZT_ALARM_BLUE | ZT_ALARM_LOOPBACK)) && + (cards[x]->spans[i].flags & ZT_FLAG_RUNNING)) { + /* This makes a good sync source */ + newsyncsrc = cards[x]->spans[i].spanno; + newsyncnum = x; + newsyncspan = i + 1; + /* Jump out */ + goto found; + } + } + } + } + if (nonzero) + p++; + else + break; + } +found: + if ((syncnum != newsyncnum) || (syncsrc != newsyncsrc) || (newsyncspan != syncspan)) { + syncnum = newsyncnum; + syncsrc = newsyncsrc; + syncspan = newsyncspan; + if (debug) printk("New syncnum: %d, syncsrc: %d, syncspan: %d\n", syncnum, syncsrc, syncspan); + } + } +#endif + /* update sync src info */ + if (wc->syncsrc != syncsrc) { + wc->syncsrc = syncsrc; + /* Update sync sources */ + for(i = 0; i < 4; i++) { + wc->spans[i].syncsrc = wc->syncsrc; + } + if (syncnum == wc->num) { + printk("XXX Set sync span to %d XXX\n", syncspan); + if (debug) printk("Card %d, using sync span %d, master\n", wc->num, syncspan); + wc->master = 1; + } else { + printk("XXX Set timing from external cable XXX\n"); + wc->master = 0; + if (debug) printk("Card %d, using Timing Bus, NOT master\n", wc->num); + } + } + spin_unlock_irqrestore(&synclock, flags); + return 0; +} + +static void t4_receiveprep(struct t4 *wc, int irq) +{ + volatile unsigned int *readchunk; + static int last0 = 1; + int dbl = 0; + int x,y,z; + unsigned int tmp; + if (irq & 1) { + /* First part */ + readchunk = wc->readchunk + 1; + if (!last0) + dbl = 1; + last0 = 0; + } else { + readchunk = wc->readchunk + ZT_CHUNKSIZE * 32 + 1; + if (last0) + dbl = 1; + last0 = 1; + } + if (dbl) + printk("TE410P: Double/missed interrupt detected\n"); + for (x=0;x<ZT_CHUNKSIZE;x++) { + /* Once per chunk */ + for (z=0;z<24;z++) { + /* All T1/E1 channels */ + tmp = readchunk[z]; + wc->spans[3].chans[z].readchunk[x] = tmp & 0xff; + wc->spans[2].chans[z].readchunk[x] = (tmp & 0xff00) >> 8; + wc->spans[1].chans[z].readchunk[x] = (tmp & 0xff0000) >> 16; + wc->spans[0].chans[z].readchunk[x] = tmp >> 24; + } + if (wc->t1e1) { + for (z=24;z<31;z++) { + /* Only E1 channels now */ + tmp = readchunk[z]; + if (wc->spans[3].channels > 24) + wc->spans[3].chans[z].readchunk[x] = tmp & 0xff; + if (wc->spans[2].channels > 24) + wc->spans[2].chans[z].readchunk[x] = (tmp & 0xff00) >> 8; + if (wc->spans[1].channels > 24) + wc->spans[1].chans[z].readchunk[x] = (tmp & 0xff0000) >> 16; + if (wc->spans[0].channels > 24) + wc->spans[0].chans[z].readchunk[x] = tmp >> 24; + } + } + /* Advance pointer by 4 TDM frame lengths */ + readchunk += 32; + } + for (x=0;x<4;x++) { + if (wc->spans[x].flags & ZT_FLAG_RUNNING) { + for (y=0;y<wc->spans[x].channels;y++) { + /* Echo cancel double buffered data */ + zt_ec_chunk(&wc->spans[x].chans[y], + wc->spans[x].chans[y].readchunk, + wc->ec_chunk2[x][y]); + memcpy(wc->ec_chunk2[x][y],wc->ec_chunk1[x][y], + ZT_CHUNKSIZE); + memcpy(wc->ec_chunk1[x][y], + wc->spans[x].chans[y].writechunk, + ZT_CHUNKSIZE); + } + zt_receive(&wc->spans[x]); + } + } +} + +static void t4_transmitprep(struct t4 *wc, int irq) +{ + volatile unsigned int *writechunk; + int x,y,z; + unsigned int tmp; + if (irq & 1) { + /* First part */ + writechunk = wc->writechunk + 1; + } else { + writechunk = wc->writechunk + ZT_CHUNKSIZE * 32 + 1; + } + for (y=0;y<4;y++) { + if (wc->spans[y].flags & ZT_FLAG_RUNNING) + zt_transmit(&wc->spans[y]); + } + + for (x=0;x<ZT_CHUNKSIZE;x++) { + /* Once per chunk */ + for (z=0;z<24;z++) { + /* All T1/E1 channels */ + tmp = (wc->spans[3].chans[z].writechunk[x]) | + (wc->spans[2].chans[z].writechunk[x] << 8) | + (wc->spans[1].chans[z].writechunk[x] << 16) | + (wc->spans[0].chans[z].writechunk[x] << 24); + writechunk[z] = tmp; + } + if (wc->t1e1) { + for (z=24;z<31;z++) { + /* Only E1 channels now */ + tmp = 0; + if (wc->spans[3].channels > 24) + tmp |= wc->spans[3].chans[z].writechunk[x]; + if (wc->spans[2].channels > 24) + tmp |= (wc->spans[2].chans[z].writechunk[x] << 8); + if (wc->spans[1].channels > 24) + tmp |= (wc->spans[1].chans[z].writechunk[x] << 16); + if (wc->spans[0].channels > 24) + tmp |= (wc->spans[0].chans[z].writechunk[x] << 24); + writechunk[z] = tmp; + } + } + /* Advance pointer by 4 TDM frame lengths */ + writechunk += 32; + } + +} + +static void __t4_check_sigbits(struct t4 *wc, int span) +{ + int a,i,rxs; + if (!(wc->spans[span].flags & ZT_FLAG_RUNNING)) + return; + if (wc->spantype[span] == TYPE_E1) { + for (i = 0; i < 15; i++) { + a = __t4_framer_in(wc, span, 0x71 + i); + /* Get high channel in low bits */ + rxs = (a & 0xf); + if (!(wc->spans[span].chans[i+16].sig & ZT_SIG_CLEAR)) { + if (wc->spans[span].chans[i+16].rxsig != rxs) + zt_rbsbits(&wc->spans[span].chans[i+16], rxs); + } + rxs = (a >> 4) & 0xf; + if (!(wc->spans[span].chans[i].sig & ZT_SIG_CLEAR)) { + if (wc->spans[span].chans[i].rxsig != rxs) + zt_rbsbits(&wc->spans[span].chans[i], rxs); + } + } + } else if (wc->spans[span].lineconfig & ZT_CONFIG_D4) { + for (i = 0; i < 24; i+=4) { + a = __t4_framer_in(wc, span, 0x70 + (i>>2)); + /* Get high channel in low bits */ + rxs = (a & 0x3) << 2; + if (!(wc->spans[span].chans[i+3].sig & ZT_SIG_CLEAR)) { + if (wc->spans[span].chans[i+3].rxsig != rxs) + zt_rbsbits(&wc->spans[span].chans[i+3], rxs); + } + rxs = (a & 0xc); + if (!(wc->spans[span].chans[i+2].sig & ZT_SIG_CLEAR)) { + if (wc->spans[span].chans[i+2].rxsig != rxs) + zt_rbsbits(&wc->spans[span].chans[i+2], rxs); + } + rxs = (a >> 2) & 0xc; + if (!(wc->spans[span].chans[i+1].sig & ZT_SIG_CLEAR)) { + if (wc->spans[span].chans[i+1].rxsig != rxs) + zt_rbsbits(&wc->spans[span].chans[i+1], rxs); + } + rxs = (a >> 4) & 0xc; + if (!(wc->spans[span].chans[i].sig & ZT_SIG_CLEAR)) { + if (wc->spans[span].chans[i].rxsig != rxs) + zt_rbsbits(&wc->spans[span].chans[i], rxs); + } + } + } else { + for (i = 0; i < 24; i+=2) { + a = __t4_framer_in(wc, span, 0x70 + (i>>1)); + /* Get high channel in low bits */ + rxs = (a & 0xf); + if (!(wc->spans[span].chans[i+1].sig & ZT_SIG_CLEAR)) { + if (wc->spans[span].chans[i+1].rxsig != rxs) + zt_rbsbits(&wc->spans[span].chans[i+1], rxs); + } + rxs = (a >> 4) & 0xf; + if (!(wc->spans[span].chans[i].sig & ZT_SIG_CLEAR)) { + if (wc->spans[span].chans[i].rxsig != rxs) + zt_rbsbits(&wc->spans[span].chans[i], rxs); + } + } + } +} + +static void __t4_do_counters(struct t4 *wc) +{ + int span; + for (span=0;span<4;span++) { + if (wc->alarmtimer[span]) { + if (!--wc->alarmtimer[span]) { + wc->spans[span].alarms &= ~(ZT_ALARM_RECOVER); + zt_alarm_notify(&wc->spans[span]); + } + } + } +} + +static void __t4_check_alarms(struct t4 *wc, int span) +{ + unsigned char c,d; + int alarms; + int x,j; + + if (!(wc->spans[span].flags & ZT_FLAG_RUNNING)) + return; + + c = __t4_framer_in(wc, span, 0x4c); + d = __t4_framer_in(wc, span, 0x4d); + + /* Assume no alarms */ + alarms = 0; + + /* And consider only carrier alarms */ + wc->spans[span].alarms &= (ZT_ALARM_RED | ZT_ALARM_BLUE | ZT_ALARM_NOTOPEN); + + if (wc->spantype[span] == TYPE_E1) { + /* XXX Implement me XXX */ + } else { + /* Detect loopup code if we're not sending one */ + if ((!wc->spans[span].mainttimer) && (d & 0x08)) { + /* Loop-up code detected */ + if ((wc->loopupcnt[span]++ > 80) && (wc->spans[span].maintstat != ZT_MAINT_REMOTELOOP)) { + __t4_framer_out(wc, span, 0x36, 0x08); /* LIM0: Disable any local loop */ + __t4_framer_out(wc, span, 0x37, 0xf6 ); /* LIM1: Enable remote loop */ + wc->spans[span].maintstat = ZT_MAINT_REMOTELOOP; + } + } else + wc->loopupcnt[span] = 0; + /* Same for loopdown code */ + if ((!wc->spans[span].mainttimer) && (d & 0x10)) { + /* Loop-down code detected */ + if ((wc->loopdowncnt[span]++ > 80) && (wc->spans[span].maintstat == ZT_MAINT_REMOTELOOP)) { + __t4_framer_out(wc, span, 0x36, 0x08); /* LIM0: Disable any local loop */ + __t4_framer_out(wc, span, 0x37, 0xf0 ); /* LIM1: Disable remote loop */ + wc->spans[span].maintstat = ZT_MAINT_NONE; + } + } else + wc->loopdowncnt[span] = 0; + } + + if (wc->spans[span].lineconfig & ZT_CONFIG_NOTOPEN) { + for (x=0,j=0;x < wc->spans[span].channels;x++) + if ((wc->spans[span].chans[x].flags & ZT_FLAG_OPEN) || + (wc->spans[span].chans[x].flags & ZT_FLAG_NETDEV)) + j++; + if (!j) + alarms |= ZT_ALARM_NOTOPEN; + } + + if (c & 0xa0) + alarms |= ZT_ALARM_RED; + if (c & 0x4) + alarms |= ZT_ALARM_BLUE; + /* Keep track of recovering */ + if ((!alarms) && wc->spans[span].alarms) + wc->alarmtimer[span] = ZT_ALARMSETTLE_TIME; + + /* If receiving alarms, go into Yellow alarm state */ + if (alarms && (!wc->spans[span].alarms)) { +#if 0 + printk("Going into yellow alarm\n"); +#endif +#if 0 + /* The quadfalc automatically generates yellow alarm */ + if (wc->spantype[span] == TYPE_E1) + __t1_set_reg(wc, 0x21, 0x7f); + else + __t1_set_reg(wc, 0x35, 0x11); +#endif + } + +#if 0 + if (wc->spans[span].alarms != alarms) { + d = __control_get_reg(wc, WC_CLOCK); + start_alarm(wc); + if (!(alarms & (ZT_ALARM_RED | ZT_ALARM_BLUE | ZT_ALARM_LOOPBACK)) && + wc->sync) { + /* Use the recieve signalling */ + wc->spans[span].syncsrc = wc->spans[span].spanno; + d |= 1; + } else { + wc->spans[span].syncsrc = 0; + d &= ~1; + } + __control_set_reg(wc, WC_CLOCK, d); + } +#endif + if (wc->alarmtimer[span]) + alarms |= ZT_ALARM_RECOVER; + + if (c & 0x10) + alarms |= ZT_ALARM_YELLOW; + + if (wc->spans[span].mainttimer || wc->spans[span].maintstat) + alarms |= ZT_ALARM_LOOPBACK; + + wc->spans[span].alarms = alarms; + + zt_alarm_notify(&wc->spans[span]); +} + +static inline void __handle_leds(struct t4 *wc) +{ + int x; + + wc->blinktimer++; + for (x=0;x<4;x++) { + if (wc->spans[x].flags & ZT_FLAG_RUNNING) { + if (wc->spans[x].alarms & (ZT_ALARM_RED | ZT_ALARM_BLUE)) { +#ifdef FANCY_ALARM + if (wc->blinktimer == (altab[wc->alarmpos] >> 1)) { + __t4_set_led(wc, x, WC_RED); + } + if (wc->blinktimer == 0xf) { + __t4_set_led(wc, x, WC_OFF); + } +#else + if (wc->blinktimer == 160) { + __t4_set_led(wc, x, WC_RED); + } else if (wc->blinktimer == 480) { + __t4_set_led(wc, x, WC_OFF); + } +#endif + } else if (wc->spans[x].alarms & ZT_ALARM_YELLOW) { + /* Yellow Alarm */ + __t4_set_led(wc, x, WC_YELLOW); + } else if (wc->spans[x].mainttimer || wc->spans[x].maintstat) { +#ifdef FANCY_ALARM + if (wc->blinktimer == (altab[wc->alarmpos] >> 1)) { + __t4_set_led(wc, x, WC_GREEN); + } + if (wc->blinktimer == 0xf) { + __t4_set_led(wc, x, WC_OFF); + } +#else + if (wc->blinktimer == 160) { + __t4_set_led(wc, x, WC_GREEN); + } else if (wc->blinktimer == 480) { + __t4_set_led(wc, x, WC_OFF); + } +#endif + } else { + /* No Alarm */ + __t4_set_led(wc, x, WC_GREEN); + } + } else + __t4_set_led(wc, x, WC_OFF); + + } +#ifdef FANCY_ALARM + if (wc->blinktimer == 0xf) { + wc->blinktimer = -1; + wc->alarmpos++; + if (wc->alarmpos >= (sizeof(altab) / sizeof(altab[0]))) + wc->alarmpos = 0; + } +#else + if (wc->blinktimer == 480) + wc->blinktimer = 0; +#endif +} + +static void t4_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct t4 *wc = dev_id; + long flags; + int x; + + unsigned int status; + unsigned int status2; + + if (wc->intcount < 20) + printk("Pre-interrupt\n"); + + inirq = 1; + /* Make sure it's really for us */ + status = t4_pci_in(wc, WC_INTR); + t4_pci_out(wc, WC_INTR, 0); + + /* Ignore if it's not for us */ + if (!status) + return; + + if (!wc->spansstarted) { + printk("Not prepped yet!\n"); + return; + } + + wc->intcount++; + if (wc->intcount < 20) + printk("Got interrupt, status = %08x\n", status); + + if (status & 0x3) { + t4_receiveprep(wc, status); + t4_transmitprep(wc, status); + } + +#if 0 + if ((wc->intcount < 10) || !(wc->intcount % 1000)) { + status2 = t4_framer_in(wc, 0, 0x6f); + printk("Status2: %04x\n", status2); + for (x = 0;x<4;x++) { + status2 = t4_framer_in(wc, x, 0x4c); + printk("FRS0/%d: %04x\n", x, status2); + } + } +#endif + spin_lock_irqsave(&wc->reglock, flags); + + __handle_leds(wc); + + __t4_do_counters(wc); + + x = wc->intcount & 15 /* 63 */; + switch(x) { + case 0: + case 1: + case 2: + case 3: + __t4_check_sigbits(wc, x); + break; + case 4: + case 5: + case 6: + case 7: + __t4_check_alarms(wc, x - 4); + break; + } + + spin_unlock_irqrestore(&wc->reglock, flags); + +} + +static int t4_hardware_init(struct t4 *wc) +{ + int x; + unsigned int version; + unsigned int falcver; + + version = t4_pci_in(wc, WC_VERSION); + printk("TE410P version %08x\n", version); + + /* Make sure DMA engine is not running and interrupts are acknowledged */ + wc->dmactrl = 0x0; + t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + /* Reset Framer and friends */ + t4_pci_out(wc, WC_LEDS, 0x00000000); + + /* Set DMA addresses */ + t4_pci_out(wc, WC_RDADDR, wc->readdma); + t4_pci_out(wc, WC_WRADDR, wc->writedma); + + /* Setup counters, interrupt flags */ + t4_pci_out(wc, WC_COUNT, ((ZT_MAX_CHUNKSIZE * 2 * 32 - 1) << 18) | ((ZT_MAX_CHUNKSIZE * 2 * 32 - 1) << 2)); + + /* Reset pending interrupts */ + t4_pci_out(wc, WC_INTR, 0x00000000); + + /* Read T1/E1 status */ + if (t1e1override > -1) + wc->t1e1 = t1e1override; + else + wc->t1e1 = ((t4_pci_in(wc, WC_LEDS)) & 0x0f00) >> 8; + wc->order = ((t4_pci_in(wc, WC_LEDS)) & 0xf0000000) >> 28; + + /* Setup LEDS, take out of reset */ + t4_pci_out(wc, WC_LEDS, 0x000000ff); + t4_activate(wc); + + t4_framer_out(wc, 0, 0x4a, 0xaa); + falcver = t4_framer_in(wc, 0 ,0x4a); + printk("FALC version: %08x, Board ID: %02x\n", falcver, wc->order); + + for (x=0;x< 11;x++) + printk("Reg %d: 0x%08x\n", x, t4_pci_in(wc, x)); + return 0; +} + +static int __devinit t4_launch(struct t4 *wc) +{ + int x; + if (wc->spans[0].flags & ZT_FLAG_REGISTERED) + return 0; + printk("TE410P: Launching card: %d\n", wc->order); + + /* Setup serial parameters and system interface */ + for (x=0;x<4;x++) + t4_serial_setup(wc, x); + + if (zt_register(&wc->spans[0], 0)) { + printk(KERN_ERR "Unable to register span %s\n", wc->spans[0].name); + return -1; + } + if (zt_register(&wc->spans[1], 0)) { + printk(KERN_ERR "Unable to register span %s\n", wc->spans[1].name); + zt_unregister(&wc->spans[0]); + return -1; + } + if (zt_register(&wc->spans[2], 0)) { + printk(KERN_ERR "Unable to register span %s\n", wc->spans[2].name); + zt_unregister(&wc->spans[0]); + zt_unregister(&wc->spans[1]); + return -1; + } + if (zt_register(&wc->spans[3], 0)) { + printk(KERN_ERR "Unable to register span %s\n", wc->spans[3].name); + zt_unregister(&wc->spans[0]); + zt_unregister(&wc->spans[1]); + zt_unregister(&wc->spans[2]); + return -1; + } + if (debug) + printk("Setting timing source\n"); + t4_set_timing_source(wc, 0); + if (debug) + printk("Timing source selected\n"); +#ifdef ENABLE_TASKLETS + tasklet_init(&wc->t4_tlet, t4_tasklet, (unsigned long)wc); +#endif + return 0; +} + +static int __devinit t4_init_one(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int res; + struct t4 *wc; + unsigned int *canary; + int x,y,f; + + + if (pci_enable_device(pdev)) { + res = -EIO; + } else { + wc = kmalloc(sizeof(struct t4), GFP_KERNEL); + if (wc) { + memset(wc, 0x0, sizeof(struct t4)); + spin_lock_init(&wc->reglock); + wc->variety = (char *)(ent->driver_data); + + wc->memaddr = pci_resource_start(pdev, 0); + wc->memlen = pci_resource_len(pdev, 0); + wc->membase = ioremap(wc->memaddr, wc->memlen); +#if 0 + if (!request_mem_region(wc->memaddr, wc->memlen, wc->variety)) + printk("wct4: Unable to request memory region :(, using anyway...\n"); +#endif + if (pci_request_regions(pdev, wc->variety)) + printk("wct4xxp: Unable to request regions\n"); + + printk("Found TE410P at base address %08lx, remapped to %p\n", wc->memaddr, wc->membase); + + wc->dev = pdev; + + wc->writechunk = + /* 32 channels, Double-buffer, Read/Write, 4 spans */ + (unsigned int *)pci_alloc_consistent(pdev, ZT_MAX_CHUNKSIZE * 32 * 2 * 2 * 4, &wc->writedma); + if (!wc->writechunk) { + printk("wct1xxp: Unable to allocate DMA-able memory\n"); + return -ENOMEM; + } + + /* Read is after the whole write piece (in words) */ + wc->readchunk = wc->writechunk + ZT_CHUNKSIZE * 32 * 2; + + /* Same thing but in bytes... */ + wc->readdma = wc->writedma + ZT_CHUNKSIZE * 32 * 2 * 4; + + /* Initialize Write/Buffers to all blank data */ + memset((void *)wc->writechunk,0xff,ZT_MAX_CHUNKSIZE * 2 * 32 * 4); + memset((void *)wc->readchunk,0xff,ZT_MAX_CHUNKSIZE * 2 * 32 * 4); +#if 0 + memset((void *)wc->readchunk,0xff,ZT_MAX_CHUNKSIZE * 2 * 32 * 4); + /* Initialize canary */ + canary = (unsigned int *)(wc->readchunk + ZT_CHUNKSIZE * 64 * 4 - 4); + *canary = (CANARY << 16) | (0xffff); +#endif + + /* Enable bus mastering */ + pci_set_master(pdev); + + /* Keep track of which device we are */ + pci_set_drvdata(pdev, wc); + + /* Initialize hardware */ + t4_hardware_init(wc); + for(x = 0; x < MAX_T4_CARDS; x++) { + if (!cards[x]) break; + } + if (x >= MAX_T4_CARDS) { + printk("No cards[] slot available!!\n"); + return -ENOMEM; + } + wc->num = x; + cards[x] = wc; + + + if (request_irq(pdev->irq, t4_interrupt, SA_INTERRUPT | SA_SHIRQ, "t4xxp", wc)) { + printk("t4xxp: Unable to request IRQ %d\n", pdev->irq); + kfree(wc); + return -EIO; + } + for (x=0;x<4;x++) { + if (wc->t1e1 & (1 << x)) { + wc->spantype[x] = TYPE_E1; + wc->chans[x] = kmalloc(sizeof(struct zt_chan) * 31,GFP_KERNEL); + memset(wc->chans[x],0,sizeof(struct zt_chan) * 31); + } else { + wc->spantype[x] = TYPE_T1; + wc->chans[x] = kmalloc(sizeof(struct zt_chan) * 24,GFP_KERNEL); + memset(wc->chans[x],0,sizeof(struct zt_chan) * 24); + } + if (!wc->chans[x]) + return -ENOMEM; + } + init_spans(wc); + + /* Launch cards as appropriate */ + x = 0; + for(;;) { + /* Find a card to activate */ + f = 0; + for (x=0;cards[x];x++) { + if (cards[x]->order <= highestorder) { + t4_launch(cards[x]); + if (cards[x]->order == highestorder) + f = 1; + } + } + /* If we found at least one, increment the highest order and search again, otherwise stop */ + if (f) + highestorder++; + else + break; + } + + printk("Found a Wildcard: %s\n", wc->variety); + res = 0; + } else + res = -ENOMEM; + } + return res; +} + +static int t4_hardware_stop(struct t4 *wc) +{ + + /* Turn off DMA, leave interrupts enabled */ + wc->dmactrl = 0x0000000; + t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); /* Turn on only the read interrupts, not the write */ + t4_pci_out(wc, WC_DMACTRL, wc->dmactrl); + t4_pci_out(wc, WC_INTR, 0x00000000); + + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout((25 * HZ) / 1000); + + /* Turn off counter, address, etc */ + t4_pci_out(wc, WC_COUNT, 0x000000); + t4_pci_out(wc, WC_RDADDR, 0x0000000); + t4_pci_out(wc, WC_WRADDR, 0x0000000); + t4_pci_out(wc, WC_GPIO, 0x0000000); + t4_pci_out(wc, WC_LEDS, 0x00000000); + + printk("\nStopped TE410P, Turned off DMA\n"); + return 0; +} + +static void __devexit t4_remove_one(struct pci_dev *pdev) +{ + struct t4 *wc = pci_get_drvdata(pdev); + if (wc) { + /* Stop hardware */ + t4_hardware_stop(wc); + + /* Unregister spans */ + if (wc->spans[0].flags & ZT_FLAG_REGISTERED) + zt_unregister(&wc->spans[0]); + if (wc->spans[1].flags & ZT_FLAG_REGISTERED) + zt_unregister(&wc->spans[1]); + if (wc->spans[2].flags & ZT_FLAG_REGISTERED) + zt_unregister(&wc->spans[2]); + if (wc->spans[3].flags & ZT_FLAG_REGISTERED) + zt_unregister(&wc->spans[3]); +#if 0 + /* Stop any DMA */ + __t1xxp_stop_dma(wc); + + /* In case hardware is still there */ + __t1xxp_disable_interrupts(wc); + + t1xxp_stop_stuff(wc); +#endif + + if (wc->membase) + iounmap((void *)wc->membase); + + pci_release_regions(pdev); +#if 0 + if (wc->memaddr) + release_mem_region(wc->memaddr, wc->memlen); +#endif + + /* Immediately free resources */ + pci_free_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 32 * 4, (void *)wc->writechunk, wc->writedma); +#if 1 + free_irq(pdev->irq, wc); +#endif + cards[wc->num] = NULL; + pci_set_drvdata(pdev, NULL); + } +} + +static struct pci_device_id t4_pci_tbl[] __devinitdata = +{ + { 0x10ee, 0x0314, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)"Wildcard TE410P-Xilinx" }, + { 0, } +}; + +static struct pci_driver t4_driver = { + name: "t4xxp", + probe: t4_init_one, + remove: t4_remove_one, + suspend: NULL, + resume: NULL, + id_table: t4_pci_tbl, +}; + +static int __init t4_init(void) +{ + int res; + res = pci_module_init(&t4_driver); + if (res) + return -ENODEV; + return 0; +} + +static void __exit t4_cleanup(void) +{ + pci_unregister_driver(&t4_driver); +} + + +MODULE_AUTHOR("Mark Spencer"); +MODULE_DESCRIPTION("TE410P PCI Driver"); +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif +MODULE_PARM(debug, "i"); +MODULE_PARM(loopback, "i"); +MODULE_PARM(timingcable, "i"); +MODULE_PARM(t1e1override, "i"); + +MODULE_DEVICE_TABLE(pci, t4_pci_tbl); + +module_init(t4_init); +module_exit(t4_cleanup); + |