/* * 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 #include #ifdef STANDALONE_ZAPATA #include "zaptel.h" #else #include #endif #define WC_MAX_CARDS 32 /* #define TEST_REGS */ /* Define to get more attention-grabbing but slightly more I/O using alarm status */ #define FANCY_ALARM #define DELAY 0x0 /* 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_FSCDELAY 0x2f #define WC_USERREG 0xc0 #define WC_CLOCK 0x0 #define WC_LEDTEST 0x1 #define WC_VERSION 0x2 /* Offset between transmit and receive */ #define WC_OFFSET 4 #define BIT_CS (1 << 7) #define BIT_ADDR (0xf << 3) #define BIT_LED0 (1 << 0) #define BIT_LED1 (1 << 1) #define BIT_TEST (1 << 2) static char *chips[] = { "DS2152", "DS21352", "DS21552", "Unknown Chip (3)", "DS2154", "DS21354", "DS21554", "Unknown Chip (4)", }; static int chanmap[] = { 2,1,0, 6,5,4, 10,9,8, 14,13,12, 18,17,16, 22,21,20, 26,25,24, 30,29,28 }; #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 struct t1xxp { struct pci_dev *dev; spinlock_t lock; int num; /* Our offset for finding channel 1 */ int offset; char *variety; int intcount; int usecount; int dead; int blinktimer; int alarmtimer; #ifdef FANCY_ALARM int alarmpos; #endif unsigned char ledtestreg; unsigned char outbyte; unsigned long ioaddr; unsigned char txsiga[3]; unsigned char txsigb[3]; dma_addr_t readdma; dma_addr_t writedma; volatile unsigned char *writechunk; /* Double-word aligned write memory */ volatile unsigned char *readchunk; /* Double-word aligned read memory */ struct zt_span span; /* Span */ struct zt_chan chans[24]; /* Channels */ }; static struct t1xxp *cards[WC_MAX_CARDS]; static inline void start_alarm(struct t1xxp *wc) { #ifdef FANCY_ALARM wc->alarmpos = 0; #endif wc->blinktimer = 0; } static inline void stop_alarm(struct t1xxp *wc) { #ifdef FANCY_ALARM wc->alarmpos = 0; #endif wc->blinktimer = 0; } static inline void __select_framer(struct t1xxp *wc, int reg) { /* Top four bits of address from AUX 6-3 */ wc->outbyte &= ~BIT_CS; wc->outbyte &= ~BIT_ADDR; wc->outbyte |= (reg & 0xf0) >> 1; 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 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_framer(wc, reg); /* Get value */ 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_framer(wc, reg); /* Send address */ 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_set_reg(struct t1xxp *wc, int reg, unsigned char val) { long flags; int res; spin_lock_irqsave(&wc->lock, flags); res = __control_set_reg(wc, reg, val); spin_unlock_irqrestore(&wc->lock, flags); return res; } 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 int control_get_reg(struct t1xxp *wc, int reg) { long flags; int res; spin_lock_irqsave(&wc->lock, flags); res = __control_get_reg(wc, reg); spin_unlock_irqrestore(&wc->lock, flags); return res; } static void t1xxp_release(struct t1xxp *wc) { zt_unregister(&wc->span); 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 void t1xxp_enable_interrupts(struct t1xxp *wc) { /* Clear interrupts */ outb(0xff, wc->ioaddr + WC_INTSTAT); /* 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"); } 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 void t1xxp_framer_start(struct t1xxp *wc) { int i; char *coding, *framing; unsigned long endjiffies; int alreadyrunning = wc->span.flags & ZT_FLAG_RUNNING; long flags; spin_lock_irqsave(&wc->lock, flags); /* Build up config */ i = 0x20; if (wc->span.lineconfig & ZT_CONFIG_ESF) { coding = "ESF"; i = 0x88; } else { coding = "SF"; } if (wc->span.lineconfig & ZT_CONFIG_B8ZS) { framing = "B8ZS"; i |= 0x44; } else { framing = "AMI"; } __t1_set_reg(wc, 0x38, i); if (!(wc->span.lineconfig & ZT_CONFIG_ESF)) { /* 1c in FDL bit */ __t1_set_reg(wc, 0x7e, 0x1c); } else { __t1_set_reg(wc, 0x7e, 0x00); } /* Set outgoing LBO */ __t1_set_reg(wc, 0x7c, wc->span.txlevel << 5); printk("Using %s/%s coding/framing\n", coding, framing); if (!alreadyrunning) { /* Set LIRST bit to 1 */ __t1_set_reg(wc, 0x0a, 0x80); spin_unlock_irqrestore(&wc->lock, flags); /* Wait 100ms to give plenty of time for reset */ endjiffies = jiffies + 10; while(endjiffies < jiffies); spin_lock_irqsave(&wc->lock, flags); /* Reset LIRST bit and reset elastic stores */ __t1_set_reg(wc, 0xa, 0x30); wc->span.flags |= ZT_FLAG_RUNNING; } spin_unlock_irqrestore(&wc->lock, flags); } static int t1xxp_framer_sanity_check(struct t1xxp *wc) { int res; int chipid; long flags; /* Sanity check */ spin_lock_irqsave(&wc->lock, flags); res = __t1_get_reg(wc, 0x0f); chipid = ((res & 0x80) >> 5) | ((res & 0x30) >> 4); spin_unlock_irqrestore(&wc->lock, flags); printk("Framer: %s, Revision: %d\n", chips[chipid], res & 0xf); return 0; } static int t1xxp_framer_hard_reset(struct t1xxp *wc) { int x; long flags; spin_lock_irqsave(&wc->lock, flags); /* Initialize all registers to 0 */ for (x=0x0;x<0x96;x++) __t1_set_reg(wc, x, 0); /* Full-on sync required */ __t1_set_reg(wc, 0x2b, 0x08); /* RSYNC is an input */ __t1_set_reg(wc, 0x2c, 0x08); /* Enable tx RBS bits */ __t1_set_reg(wc, 0x35, 0x10); /* TSYNC is output */ __t1_set_reg(wc, 0x36, 0x04); /* Tx and Rx elastic store enabled, 2.048 Mhz (in theory) */ __t1_set_reg(wc, 0x37, 0x9c); /* Setup Loopup / Loopdown codes */ __t1_set_reg(wc, 0x12, 0x22); __t1_set_reg(wc, 0x14, 0x80); __t1_set_reg(wc, 0x15, 0x80); spin_unlock_irqrestore(&wc->lock, flags); return 0; } static int t1xxp_rbsbits(struct zt_chan *chan, int bits) { struct t1xxp *wc = chan->pvt; long flags; int b,o; unsigned char mask; /* Byte offset */ spin_lock_irqsave(&wc->lock, flags); b = (chan->chanpos - 1) / 8; o = (chan->chanpos - 1) % 8; mask = (1 << o); if (bits & ZT_ABIT) { /* Set A-bit */ wc->txsiga[b] |= mask; } else { /* Clear A-bit */ wc->txsiga[b] &= ~mask; } if (bits & ZT_BBIT) { /* Set B-bit */ wc->txsigb[b] |= mask; } else { wc->txsigb[b] &= ~mask; } /* Output new values */ __t1_set_reg(wc, 0x70 + b, wc->txsiga[b]); __t1_set_reg(wc, 0x73 + b, wc->txsigb[b]); __t1_set_reg(wc, 0x76 + b, wc->txsiga[b]); __t1_set_reg(wc, 0x79 + b, wc->txsigb[b]); spin_unlock_irqrestore(&wc->lock, flags); return 0; } static int t1xxp_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data) { switch(cmd) { default: return -ENOTTY; } } static int t1xxp_startup(struct zt_span *span) { struct t1xxp *wc = span->pvt; /* Reset framer with proper parameters and start */ t1xxp_framer_start(wc); if (!(span->flags & ZT_FLAG_RUNNING)) { /* Only if we're not already going */ t1xxp_enable_interrupts(wc); t1xxp_start_dma(wc); span->flags |= ZT_FLAG_RUNNING; } return 0; } static int t1xxp_shutdown(struct zt_span *span) { struct t1xxp *wc = span->pvt; long flags; spin_lock_irqsave(&wc->lock, flags); __t1xxp_stop_dma(wc); __t1xxp_disable_interrupts(wc); span->flags &= ~ZT_FLAG_RUNNING; spin_unlock_irqrestore(&wc->lock, flags); t1xxp_framer_hard_reset(wc); return 0; } static int t1xxp_maint(struct zt_span *span, int mode) { /* XXX Implement me XXX */ return -1; } static void __t1xxp_set_clear(struct t1xxp *wc) { /* Setup registers */ int x,y; unsigned char b; for (x=0;x<3;x++) { b = 0; for (y=0;y<8;y++) if (wc->chans[x * 8 + y].sig & ZT_SIG_CLEAR) b |= (1 << y); __t1_set_reg(wc, 0x39 + x, b); } } static int t1xxp_chanconfig(struct zt_chan *chan, int sigtype) { struct t1xxp *wc = chan->pvt; int flags; int alreadyrunning = chan->span->flags & ZT_FLAG_RUNNING; spin_lock_irqsave(&wc->lock, flags); if (alreadyrunning) __t1xxp_set_clear(wc); spin_unlock_irqrestore(&wc->lock, flags); return 0; } static int t1xxp_spanconfig(struct zt_span *span, struct zt_lineconfig *lc) { span->lineconfig = lc->lineconfig; span->txlevel = lc->lbo; span->rxlevel = 0; /* Do we want to SYNC on receive or not */ span->syncsrc = lc->sync; /* If already running, apply changes immediately */ if (span->flags & ZT_FLAG_RUNNING) return t1xxp_startup(span); return 0; } static int t1xxp_software_init(struct t1xxp *wc) { int x; /* Find position */ for (x=0;x= WC_MAX_CARDS) return -1; wc->num = x; sprintf(wc->span.name, "WCT1/%d", wc->num); sprintf(wc->span.desc, "%s card %d", wc->variety, wc->num); wc->span.spanconfig = t1xxp_spanconfig; wc->span.chanconfig = t1xxp_chanconfig; wc->span.startup = t1xxp_startup; wc->span.shutdown = t1xxp_shutdown; wc->span.rbsbits = t1xxp_rbsbits; wc->span.maint = t1xxp_maint; wc->span.open = t1xxp_open; wc->span.close = t1xxp_close; wc->span.channels = 24; wc->span.chans = wc->chans; wc->span.flags = ZT_FLAG_RBS; wc->span.linecompat = ZT_CONFIG_AMI | ZT_CONFIG_B8ZS | ZT_CONFIG_D4 | ZT_CONFIG_ESF; wc->span.ioctl = t1xxp_ioctl; wc->span.pvt = wc; wc->span.deflaw = ZT_LAW_MULAW; init_waitqueue_head(&wc->span.maintq); for (x=0;x<24;x++) { sprintf(wc->chans[x].name, "WCT1/%d/%d", wc->num, x + 1); wc->chans[x].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; wc->chans[x].pvt = wc; wc->chans[x].chanpos = x + 1; } if (zt_register(&wc->span, 0)) { printk("Unable to register span with zaptel\n"); return -1; } return 0; } static inline void __handle_leds(struct t1xxp *wc) { int oldreg; wc->blinktimer++; if (wc->span.alarms & (ZT_ALARM_RED | ZT_ALARM_BLUE)) { /* Red/Blue alarm */ #ifdef FANCY_ALARM if (wc->blinktimer == (altab[wc->alarmpos] >> 1)) { wc->ledtestreg = (wc->ledtestreg | BIT_LED1) & ~BIT_LED0; __control_set_reg(wc, WC_LEDTEST, wc->ledtestreg); } if (wc->blinktimer == 0xf) { wc->ledtestreg = wc->ledtestreg & ~(BIT_LED0 | BIT_LED1); __control_set_reg(wc, WC_LEDTEST, wc->ledtestreg); wc->blinktimer = -1; wc->alarmpos++; if (wc->alarmpos >= (sizeof(altab) / sizeof(altab[0]))) wc->alarmpos = 0; } #else if (wc->blinktimer == 160) { wc->ledtestreg = (wc->ledtestreg | BIT_LED1) & ~BIT_LED0; __control_set_reg(wc, WC_LEDTEST, wc->ledtestreg); } else if (wc->blinktimer == 480) { wc->ledtestreg = wc->ledtestreg & ~(BIT_LED0 | BIT_LED1); __control_set_reg(wc, WC_LEDTEST, wc->ledtestreg); wc->blinktimer = 0; } #endif } else if (wc->span.alarms & ZT_ALARM_YELLOW) { /* Yellow Alarm */ if (!(wc->blinktimer % 2)) wc->ledtestreg = (wc->ledtestreg | BIT_LED1) & ~BIT_LED0; else wc->ledtestreg = (wc->ledtestreg | BIT_LED0) & ~BIT_LED1; __control_set_reg(wc, WC_LEDTEST, wc->ledtestreg); } else { /* No Alarm */ oldreg = wc->ledtestreg; if (wc->span.flags & ZT_FLAG_RUNNING) wc->ledtestreg = (wc->ledtestreg | BIT_LED0) & ~BIT_LED1; else wc->ledtestreg = wc->ledtestreg & ~(BIT_LED0 | BIT_LED1); if (oldreg != wc->ledtestreg) __control_set_reg(wc, WC_LEDTEST, wc->ledtestreg); } } static void t1xxp_transmitprep(struct t1xxp *wc, int ints) { volatile unsigned char *txbuf; int x,y; if (ints & 0x01) { /* We just finished sending the first buffer, start filling it now */ txbuf = wc->writechunk; } else { /* Just finished sending second buffer, fill it now */ txbuf = wc->writechunk + 32 * ZT_CHUNKSIZE; } zt_transmit(&wc->span); for (y=0;yoffset) & 0x1f)] = wc->chans[x].writechunk[y]; } } } static void t1xxp_receiveprep(struct t1xxp *wc, int ints) { volatile unsigned char *rxbuf; int x; int y; if (ints & 0x08) { /* Just received first buffer */ rxbuf = wc->readchunk; } else { rxbuf = wc->readchunk + ZT_CHUNKSIZE * 32; } for (x=3;x<32;x+=4) { if (rxbuf[(x + WC_OFFSET) & 0x1f] == 0x7f) { if (wc->offset != (x-3)) { wc->offset = x - 3; #if 1 printk("New offset: %d\n", wc->offset); #endif } } } for (y=0;ychans[x].readchunk[y] = rxbuf[32 * y + ((chanmap[x] + WC_OFFSET + wc->offset) & 0x1f)]; } } zt_receive(&wc->span); } static void __t1xxp_check_sigbits(struct t1xxp *wc, int x) { int a,b,i,y,rxs; a = __t1_get_reg(wc, 0x60 + x); b = __t1_get_reg(wc, 0x64 + x); for (y=0;y<8;y++) { i = x * 8 + y; rxs = 0; if (a & (1 << y)) rxs |= ZT_ABIT; if (b & (1 << y)) rxs |= ZT_BBIT; if (!(wc->chans[i].sig & ZT_SIG_CLEAR)) { if (wc->chans[i].rxsig != rxs) zt_rbsbits(&wc->chans[i], rxs); } } } static void __t1xxp_check_alarms(struct t1xxp *wc) { unsigned char c; int alarms; int x,j; /* Get RIR3 */ c = __t1_get_reg(wc, 0x10); wc->span.rxlevel = c >> 6; /* Get status register s*/ __t1_set_reg(wc, 0x20, 0xff); c = __t1_get_reg(wc, 0x20); /* Assume no alarms */ alarms = 0; /* And consider only carrier alarms */ wc->span.alarms &= (ZT_ALARM_RED | ZT_ALARM_BLUE | ZT_ALARM_NOTOPEN); if (wc->span.lineconfig & ZT_CONFIG_NOTOPEN) { for (x=0,j=0;x < wc->span.channels;x++) if ((wc->chans[x].flags & ZT_FLAG_OPEN) || (wc->chans[x].flags & ZT_FLAG_NETDEV)) j++; if (!j) alarms |= ZT_ALARM_NOTOPEN; } /* Check actual alarm status */ if (c & 0x3) alarms |= ZT_ALARM_RED; if (c & 0x8) alarms |= ZT_ALARM_BLUE; /* Keep track of recovering */ if (!alarms && wc->span.alarms) wc->alarmtimer = ZT_ALARMSETTLE_TIME; if (wc->alarmtimer) alarms |= ZT_ALARM_RECOVER; /* If receiving alarms, go into Yellow alarm state */ if (alarms && !wc->span.alarms) { printk("Going into yellow alarm\n"); __t1_set_reg(wc, 0x35, 0x11); } if (c & 0x4) alarms |= ZT_ALARM_YELLOW; wc->span.alarms = alarms; zt_alarm_notify(&wc->span); } static void __t1xxp_do_counters(struct t1xxp *wc) { if (wc->alarmtimer) { if (!--wc->alarmtimer) { wc->span.alarms &= ~ZT_ALARM_RECOVER; /* Clear yellow alarm */ printk("Coming out of yellow alarm\n"); __t1_set_reg(wc, 0x35, 0x10); zt_alarm_notify(&wc->span); } } } static void t1xxp_interrupt(int irq, void *dev_id, struct pt_regs *regs) { struct t1xxp *wc = dev_id; unsigned char ints; long flags; int x; ints = inb(wc->ioaddr + WC_INTSTAT); outb(ints, wc->ioaddr + WC_INTSTAT); if (!ints) return; if (!wc->intcount) { printk("Got interrupt: 0x%04x\n", ints); } wc->intcount++; if (ints & 0x0f) { t1xxp_receiveprep(wc, ints); t1xxp_transmitprep(wc, ints); } spin_lock_irqsave(&wc->lock, flags); __handle_leds(wc); /* Count down timers */ __t1xxp_do_counters(wc); /* Do some things that we don't have to do very often */ x = wc->intcount & 63; switch(x) { case 0: case 1: case 2: case 3: #if 0 __t1xxp_check_sigbits(wc, x); #endif break; case 4: __t1xxp_check_alarms(wc); break; } spin_unlock_irqrestore(&wc->lock, flags); if (ints & 0x10) printk("PCI Master abort\n"); if (ints & 0x20) printk("PCI Target abort\n"); } static int t1xxp_hardware_init(struct t1xxp *wc) { /* Hardware PCI stuff */ /* Reset 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 + WC_FSCDELAY); /* 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 */ /* Start at writedma */ outl(wc->writedma, wc->ioaddr + WC_DMAWS); /* Write start */ /* First frame */ outl(wc->writedma + ZT_CHUNKSIZE * 32, wc->ioaddr + WC_DMAWI); /* Middle (interrupt) */ /* Second frame */ outl(wc->writedma + ZT_CHUNKSIZE * 32 * 2 - 4, wc->ioaddr + WC_DMAWE); /* End */ outl(wc->readdma, wc->ioaddr + WC_DMARS); /* Read start */ /* First frame */ outl(wc->readdma + ZT_CHUNKSIZE * 32, wc->ioaddr + WC_DMARI); /* Middle (interrupt) */ /* Second frame */ outl(wc->readdma + ZT_CHUNKSIZE * 32 * 2 - 4, wc->ioaddr + WC_DMARE); /* End */ printk("Setting up DMA (write/read = %08x/%08x)\n", wc->writedma, wc->readdma); /* Check out the controller */ printk("Controller version: %02x\n", control_get_reg(wc, WC_VERSION)); control_set_reg(wc, WC_LEDTEST, 0x00); control_set_reg(wc, WC_CLOCK, 0x00); if (t1xxp_framer_sanity_check(wc)) return -1; /* Reset the T1 and report */ t1xxp_framer_hard_reset(wc); start_alarm(wc); return 0; } 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)); spin_lock_init(&wc->lock); wc->ioaddr = pci_resource_start(pdev, 0); wc->dev = pdev; wc->variety = (char *)(ent->driver_data); wc->writechunk = /* 32 channels, Double-buffer, Read/Write */ (unsigned char *)pci_alloc_consistent(pdev, ZT_MAX_CHUNKSIZE * 32 * 2 * 2, &wc->writedma); if (!wc->writechunk) { printk("wcmodem: Unable to allocate DMA-able memory\n"); return -ENOMEM; } /* Read is after the whole write piece (in bytes) */ wc->readchunk = wc->writechunk + ZT_CHUNKSIZE * 32 * 2; /* Same thing... */ wc->readdma = wc->writedma + ZT_CHUNKSIZE * 32 * 2; /* Initialize Write/Buffers to all blank data */ memset((void *)wc->writechunk,0x00,ZT_MAX_CHUNKSIZE * 2 * 2 * 32); /* 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; } /* Initialize hardware */ t1xxp_hardware_init(wc); /* Misc. software stuff */ t1xxp_software_init(wc); printk("Found a Wildcard: %s\n", wc->variety); res = 0; } else res = -ENOMEM; } return res; } static void t1xxp_stop_stuff(struct t1xxp *wc) { /* Kill clock */ control_set_reg(wc, WC_CLOCK, 0); /* Turn off LED's */ control_set_reg(wc, WC_LEDTEST, 0); /* Reset the T1 */ t1xxp_framer_hard_reset(wc); } 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); t1xxp_stop_stuff(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 PCI 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 "); #ifdef MODULE_LICENSE MODULE_LICENSE("GPL"); #endif module_init(t1xxp_init); module_exit(t1xxp_cleanup);