summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xwcfxs.c672
-rwxr-xr-xwct1xxp.c464
-rwxr-xr-xwctdm.c672
3 files changed, 1383 insertions, 425 deletions
diff --git a/wcfxs.c b/wcfxs.c
index 0a9783b..e4a22b2 100755
--- a/wcfxs.c
+++ b/wcfxs.c
@@ -33,6 +33,7 @@
#include <linux/pci.h>
#include "proslic.h"
+#include "wcfxs.h"
static alpha indirect_regs[] =
{
@@ -49,11 +50,11 @@ static alpha indirect_regs[] =
{10,"DTMF_COL_2ND_ARM",0x0202},
{11,"DTMF_PWR_MIN_TRES",0x00E5},
{12,"DTMF_OT_LIM_TRES",0x0A1C},
-{13,"OSC1_COEF",0x6D40},
-{14,"OSC1X",0x0470},
+{13,"OSC1_COEF",0x7B30},
+{14,"OSC1X",0x0063},
{15,"OSC1Y",0x0000},
-{16,"OSC2_COEF",0x4A80},
-{17,"OSC2X",0x0830},
+{16,"OSC2_COEF",0x7870},
+{17,"OSC2X",0x007D},
{18,"OSC2Y",0x0000},
{19,"RING_V_OFF",0x0000},
{20,"RING_OSC",0x7EF0},
@@ -70,17 +71,19 @@ static alpha indirect_regs[] =
{29,"RING_TRIP_TRES",0x3600},
{30,"COMMON_MIN_TRES",0x1000},
{31,"COMMON_MAX_TRES",0x0200},
-{32,"PWR_ALARM_Q1Q2",0x0550},
+{32,"PWR_ALARM_Q1Q2",0x07C0},
{33,"PWR_ALARM_Q3Q4",0x2600},
{34,"PWR_ALARM_Q5Q6",0x1B80},
{35,"LOOP_CLOSURE_FILTER",0x8000},
{36,"RING_TRIP_FILTER",0x0320},
-{37,"TERM_LP_POLE_Q1Q2",0x0100},
+{37,"TERM_LP_POLE_Q1Q2",0x008C},
{38,"TERM_LP_POLE_Q3Q4",0x0100},
{39,"TERM_LP_POLE_Q5Q6",0x0010},
{40,"CM_BIAS_RINGING",0x0C00},
{41,"DCDC_MIN_V",0x0C00},
{42,"DCDC_XTRA",0x1000},
+{43,"LOOP_CLOSE_TRES_LOW",0x1000},
+{97,"RCV_FLTR", 0}
};
#ifdef STANDALONE_ZAPATA
@@ -111,6 +114,14 @@ static alpha indirect_regs[] =
#define WC_SERCTL 0x2d
#define WC_FSCDELAY 0x2f
+#define WC_REGBASE 0xc0
+
+#define WC_LEDS 0x0
+#define WC_TEST 0x1
+#define WC_CS 0x2
+#define WC_VER 0x3
+#define WC_SYNC 0x4
+
#define BIT_CS (1 << 2)
#define BIT_SCLK (1 << 3)
#define BIT_SDI (1 << 4)
@@ -125,11 +136,12 @@ static alpha indirect_regs[] =
#define FLAG_DOUBLE_CLOCK (1 << 0)
+#define NUM_CARDS 4
+
struct wcfxs {
struct pci_dev *dev;
char *variety;
struct zt_span span;
- struct zt_chan chan;
unsigned char ios;
int usecount;
int intcount;
@@ -138,12 +150,15 @@ struct wcfxs {
int flags;
int freeregion;
int alt;
+ int curcard;
+ int cards;
+ spinlock_t lock;
/* Receive hook state and debouncing */
- int oldrxhook;
- int debouncehook;
- int lastrxhook;
- int debounce;
+ int oldrxhook[NUM_CARDS];
+ int debouncehook[NUM_CARDS];
+ int lastrxhook[NUM_CARDS];
+ int debounce[NUM_CARDS];
int idletxhookstate; /* IDLE changing hook state */
unsigned long ioaddr;
@@ -151,6 +166,7 @@ struct wcfxs {
dma_addr_t writedma;
volatile int *writechunk; /* Double-word aligned write memory */
volatile int *readchunk; /* Double-word aligned read memory */
+ struct zt_chan chans[NUM_CARDS];
};
@@ -159,7 +175,7 @@ struct wcfxs_desc {
int flags;
};
-static struct wcfxs_desc wcfxs = { "Wildcard Prototype", 0 };
+static struct wcfxs_desc wcfxs = { "Wildcard S400P Prototype", 0 };
static struct wcfxs *ifaces[WC_MAX_IFACES];
@@ -181,7 +197,13 @@ static inline void wcfxs_transmitprep(struct wcfxs *wc, unsigned char ints)
for (x=0;x<ZT_CHUNKSIZE;x++) {
/* Send a sample, as a 32-bit word */
- writechunk[x] = wc->chan.writechunk[x] << 24;
+ writechunk[x] = (wc->chans[0].writechunk[x] << 24);
+ if (wc->cards > 1)
+ writechunk[x] |= (wc->chans[1].writechunk[x] << 16);
+ if (wc->cards > 2)
+ writechunk[x] |= (wc->chans[2].writechunk[x] << 8);
+ if (wc->cards > 3)
+ writechunk[x] |= (wc->chans[3].writechunk[x]);
}
@@ -198,25 +220,37 @@ static inline void wcfxs_receiveprep(struct wcfxs *wc, unsigned char ints)
/* Read is not at interrupt address. Valid data is available at normal offset */
readchunk = wc->readchunk;
for (x=0;x<ZT_CHUNKSIZE;x++) {
- wc->chan.readchunk[x] = (readchunk[x] >> 24) & 0xff;
+ wc->chans[0].readchunk[x] = (readchunk[x] >> 24) & 0xff;
+ if (wc->cards > 1)
+ wc->chans[1].readchunk[x] = (readchunk[x] >> 16) & 0xff;
+ if (wc->cards > 2)
+ wc->chans[2].readchunk[x] = (readchunk[x] >> 8) & 0xff;
+ if (wc->cards > 3)
+ wc->chans[3].readchunk[x] = (readchunk[x]) & 0xff;
}
zt_receive(&wc->span);
}
-static inline void wcfxs_check_hook(struct wcfxs *wc);
+static inline void wcfxs_check_hook(struct wcfxs *wc, int card);
static void wcfxs_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct wcfxs *wc = dev_id;
unsigned char ints;
+ static int p=0;
ints = inb(wc->ioaddr + WC_INTSTAT);
outb(ints, wc->ioaddr + WC_INTSTAT);
+ if (!p) {
+ printk("Interrupt!\n");
+ p++;
+ }
if (!ints)
return;
+
if (ints & 0x10) {
printk("PCI Master abort\n");
return;
@@ -226,12 +260,15 @@ static void wcfxs_interrupt(int irq, void *dev_id, struct pt_regs *regs)
printk("PCI Target abort\n");
return;
}
+
+
if (ints & 0x0f) {
wc->intcount++;
- if (!(wc->intcount % 10))
- wcfxs_check_hook(wc);
- if (ints & 3) wcfxs_transmitprep(wc, ints);
+ if ((wc->intcount % 10) < wc->cards) {
+ wcfxs_check_hook(wc, wc->intcount % 10);
+ }
if (ints & 0xc) wcfxs_receiveprep(wc, ints);
+ if (ints & 3) wcfxs_transmitprep(wc, ints);
}
}
@@ -295,85 +332,133 @@ static inline unsigned char read_8bits(struct wcfxs *wc)
return res;
}
-static void wcfxs_setreg(struct wcfxs *wc, unsigned char reg, unsigned char value)
+static void wcfxs_setcreg(struct wcfxs *wc, unsigned char reg, unsigned char val)
{
+ outb(val, wc->ioaddr + WC_REGBASE + ((reg & 0xf) << 2));
+}
+
+static unsigned char wcfxs_getcreg(struct wcfxs *wc, unsigned char reg)
+{
+ return inb(wc->ioaddr + WC_REGBASE + ((reg & 0xf) << 2));
+}
+
+static inline void wcfxs_setcard(struct wcfxs *wc, int card)
+{
+ if (wc->curcard != card) {
+ wcfxs_setcreg(wc, WC_CS, (1 << card));
+ wc->curcard = card;
+ }
+}
+
+static void __wcfxs_setreg(struct wcfxs *wc, int card, unsigned char reg, unsigned char value)
+{
+ wcfxs_setcard(wc, card);
write_8bits(wc, reg & 0x7f);
write_8bits(wc, value);
}
-static unsigned char wcfxs_getreg(struct wcfxs *wc, unsigned char reg)
+static void wcfxs_setreg(struct wcfxs *wc, int card, unsigned char reg, unsigned char value)
+{
+ long flags;
+ spin_lock_irqsave(&wc->lock, flags);
+ __wcfxs_setreg(wc, card, reg, value);
+ spin_unlock_irqrestore(&wc->lock, flags);
+}
+
+static unsigned char __wcfxs_getreg(struct wcfxs *wc, int card, unsigned char reg)
{
+ wcfxs_setcard(wc, card);
write_8bits(wc, reg | 0x80);
return read_8bits(wc);
}
-static int wait_access(struct wcfxs *wc)
+static unsigned char wcfxs_getreg(struct wcfxs *wc, int card, unsigned char reg)
{
- unsigned char count, data;
- count = 0;
- #define MAX 60
+ long flags;
+ unsigned char res;
+ spin_lock_irqsave(&wc->lock, flags);
+ res = __wcfxs_getreg(wc, card, reg);
+ spin_unlock_irqrestore(&wc->lock, flags);
+ return res;
+}
+
+static int __wait_access(struct wcfxs *wc, int card)
+{
+ unsigned char data;
+ long origjiffies;
+ int count = 0;
+
+ #define MAX 6000 /* attempts */
+ origjiffies = jiffies;
/* Wait for indirect access */
while (count++ < MAX)
{
- data = wcfxs_getreg(wc, I_STATUS);
+ data = __wcfxs_getreg(wc, card, I_STATUS);
if (!data)
return 0;
}
- if(count > (MAX-1)) printk(" ##### Loop error #####\n");
+ if(count > (MAX-1)) printk(" ##### Loop error (%02x) #####\n", data);
- return -1;
+ return 0;
}
-static int wcfxs_setreg_indirect(struct wcfxs *wc, unsigned char address, unsigned short data)
+static int wcfxs_setreg_indirect(struct wcfxs *wc, int card, unsigned char address, unsigned short data)
{
-
- if(!wait_access(wc))
- {
- wcfxs_setreg(wc, IDA_LO,(unsigned char)(data & 0xFF));
- wcfxs_setreg(wc, IDA_HI,(unsigned char)((data & 0xFF00)>>8));
- wcfxs_setreg(wc, IAA,address);
- return 0;
- }
-
- return -1;
+ long flags;
+ int res = -1;
+ spin_lock_irqsave(&wc->lock, flags);
+ if(!__wait_access(wc, card)) {
+ __wcfxs_setreg(wc, card, IDA_LO,(unsigned char)(data & 0xFF));
+ __wcfxs_setreg(wc, card, IDA_HI,(unsigned char)((data & 0xFF00)>>8));
+ __wcfxs_setreg(wc, card, IAA,address);
+ res = 0;
+ };
+ spin_unlock_irqrestore(&wc->lock, flags);
+ return res;
}
-static int wcfxs_getreg_indirect(struct wcfxs *wc, unsigned char address)
+static int wcfxs_getreg_indirect(struct wcfxs *wc, int card, unsigned char address)
{
- if (!wait_access(wc)) {
- wcfxs_setreg(wc, IAA, address);
- if (!wait_access(wc)) {
- unsigned char data1, data2;
- data1 = wcfxs_getreg(wc, IDA_LO);
- data2 = wcfxs_getreg(wc, IDA_HI);
- return data1 | (data2 << 8);
+ long flags;
+ int res = -1;
+ char *p=NULL;
+ spin_lock_irqsave(&wc->lock, flags);
+ if (!__wait_access(wc, card)) {
+ __wcfxs_setreg(wc, card, IAA, address);
+ if (!__wait_access(wc, card)) {
+ unsigned char data1, data2;
+ data1 = __wcfxs_getreg(wc, card, IDA_LO);
+ data2 = __wcfxs_getreg(wc, card, IDA_HI);
+ res = data1 | (data2 << 8);
+ } else
+ p = "Failed to wait inside\n";
} else
- printk("Failed to wait inside\n");
- } else
- printk("failed to wait\n");
-
- return -1;
+ p = "failed to wait\n";
+ spin_unlock_irqrestore(&wc->lock, flags);
+ if (p)
+ printk(p);
+ return res;
}
-static int wcfxs_init_indirect_regs(struct wcfxs *wc)
+static int wcfxs_init_indirect_regs(struct wcfxs *wc, int card)
{
unsigned char i;
for (i=0; i<43; i++)
{
- if(wcfxs_setreg_indirect(wc, i,indirect_regs[i].initial))
+ if(wcfxs_setreg_indirect(wc, card, i,indirect_regs[i].initial))
return -1;
}
return 0;
}
-static int wcfxs_verify_indirect_regs(struct wcfxs *wc)
+static int wcfxs_verify_indirect_regs(struct wcfxs *wc, int card)
{
int passed = 1;
unsigned short i, initial;
@@ -381,7 +466,7 @@ static int wcfxs_verify_indirect_regs(struct wcfxs *wc)
for (i=0; i<43; i++)
{
- if((j = wcfxs_getreg_indirect(wc, (unsigned char) i)) < 0) {
+ if((j = wcfxs_getreg_indirect(wc, card, (unsigned char) i)) < 0) {
printk("Failed to read indirect register %d\n", i);
return -1;
}
@@ -405,77 +490,293 @@ static int wcfxs_verify_indirect_regs(struct wcfxs *wc)
return 0;
}
-static int wcfxs_calibrate(struct wcfxs *wc)
-{
- unsigned char x;
+static int wcfxs_proslic_insane(struct wcfxs *wc, int card)
+{
+ int blah;
- wcfxs_setreg(wc, 92, 0xc8);
- wcfxs_setreg(wc, 97, 0);
- wcfxs_setreg(wc, 93, 0x19);
- wcfxs_setreg(wc, 14, 0);
- wcfxs_setreg(wc, 93, 0x99);
+ blah = wcfxs_getreg(wc, card, 0);
+ if (debug)
+ printk("ProSLIC on module %d, product %d, version %d\n", card, (blah & 0x30) >> 4, (blah & 0xf));
- x = wcfxs_getreg(wc, 93);
- if (debug)
- printk("DC Cal x=%x\n",x);
- wcfxs_setreg(wc, 97, 0);
- wcfxs_setreg(wc, CALIBR1, CALIBRATE_LINE);
- x = wcfxs_getreg(wc, CALIBR1);
- wcfxs_setreg(wc, LINE_STATE, ACTIVATE_LINE);
+ if ((blah & 0x30) >> 4) {
+ printk("ProSLIC on module %d is not a 3210.\n", card);
+ return -1;
+ }
+ if ((blah & 0xf) < 3) {
+ printk("ProSLIC 3210 version %d is too old\n", blah & 0xf);
+ return -1;
+ }
+
+ blah = wcfxs_getreg(wc, card, 8);
+ if (blah != 0x2) {
+ printk("ProSLIC on module %d insane (1)\n", card);
+ return -1;
+ }
+
+ blah = wcfxs_getreg(wc, card, 64);
+ if (blah != 0x0) {
+ printk("ProSLIC on module %d insane (2)\n", card);
+ return -1;
+ }
+
+ blah = wcfxs_getreg(wc, card, 11);
+ if (blah != 0x33) {
+ printk("ProSLIC on module %d insane (3)\n", card);
+ return -1;
+ }
+
+ /* Just be sure it's setup right. */
+ wcfxs_setreg(wc, card, 30, 0);
+ if (debug)
+ printk("ProSLIC on module %d seems sane.\n", card);
return 0;
}
-static int wcfxs_init_proslic(struct wcfxs *wc)
+static int wcfxs_powerleak_test(struct wcfxs *wc, int card)
{
- int blah;
+ unsigned long origjiffies;
+ unsigned char vbat;
- /* By default, always send on hook */
- wc->idletxhookstate = 2;
+ /* Turn off linefeed */
+ wcfxs_setreg(wc, card, 64, 0);
- /* Disable Auto Power Alarm Detect and other "features" */
- wcfxs_setreg(wc, 67, 0x0e);
- blah = wcfxs_getreg(wc, 67);
+ /* Power down */
+ wcfxs_setreg(wc, card, 14, 0x10);
- if (wcfxs_init_indirect_regs(wc)) {
- printk(KERN_INFO "Indirect Registers failed to initialize.\n");
- return -1;
+ /* Wait for one second */
+ origjiffies = jiffies;
+
+ while((vbat = wcfxs_getreg(wc, card, 82)) > 0x3) {
+ if ((jiffies - origjiffies) >= HZ)
+ break;;
}
- if (wcfxs_verify_indirect_regs(wc)) {
- printk(KERN_INFO "Indirect Registers failed verification.\n");
- return -1;
+
+ if (vbat < 0x04) {
+ printk("Excessive leakage detected on module %d: %d volts (%02x) after %d ms\n", card,
+ 376 * vbat / 1000, vbat, (int)((jiffies - origjiffies) * 1000 / HZ));
+ return -1;
+ } else if (debug) {
+ printk("Post-leakage voltage: %d volts\n", 376 * vbat / 1000);
}
- if (wcfxs_calibrate(wc)) {
- printk(KERN_INFO "ProSlic Died on Activation.\n");
- return -1;
+ return 0;
+}
+
+static int wcfxs_powerup_proslic(struct wcfxs *wc, int card)
+{
+ unsigned char vbat;
+ unsigned long origjiffies;
+
+ /* Set period of DC-DC converter to 1/64 khz */
+ wcfxs_setreg(wc, card, 92, 0xff);
+
+ /* Engage DC-DC converter */
+ wcfxs_setreg(wc, card, 93, 0x19);
+
+ /* Wait for VBat to powerup */
+ origjiffies = jiffies;
+
+ /* Disable powerdown */
+ wcfxs_setreg(wc, card, 14, 0);
+
+ while((vbat = wcfxs_getreg(wc, card, 82)) < 0xc0) {
+ /* Wait no more than 500ms */
+ if ((jiffies - origjiffies) > HZ/2) {
+ break;
+ }
+ }
+
+#if 0
+ printk("jiffies - origjiffies: %d\n", ((int)(jiffies - origjiffies)));
+#endif
+
+ if (vbat < 0xc0) {
+ printk("ProSLIC on module %d failed to powerup within %d ms\n",
+ card, (int)(((jiffies - origjiffies) * 1000 / HZ)));
+ return -1;
+ } else if (debug) {
+ printk("ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n",
+ card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ)));
}
- if (wcfxs_setreg_indirect(wc, 97, 0x0)) { // Stanley: for the bad recording fix
+ /* Perform DC-DC calibration */
+ wcfxs_setreg(wc, card, 93, 0x80);
+
+ origjiffies = jiffies;
+ while(0x80 & wcfxs_getreg(wc, card, 93)) {
+ if ((jiffies - origjiffies) > 2 * HZ) {
+ printk("Timeout waiting for DC-DC calibration on module %d\n", card);
+ return -1;
+ }
+ }
+
+#if 0
+ /* Wait a full two seconds */
+ while((jiffies - origjiffies) < 2 * HZ);
+
+ /* Just check to be sure */
+ vbat = wcfxs_getreg(wc, card, 82);
+ printk("ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n",
+ card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ)));
+#endif
+ return 0;
+
+}
+
+static int wcfxs_calibrate(struct wcfxs *wc, int card)
+{
+ unsigned long origjiffies;
+ int x;
+ /* Perform all calibrations */
+ wcfxs_setreg(wc, card, 97, 0x1f);
+
+ /* Begin, no speedup */
+ wcfxs_setreg(wc, card, 96, 0x5f);
+
+ /* Wait for it to finish */
+ origjiffies = jiffies;
+ while(wcfxs_getreg(wc, card, 96)) {
+ if ((jiffies - origjiffies) > 2 * HZ) {
+ printk("Timeout waiting for calibration of module %d\n", card);
+ return -1;
+ }
+ }
+
+ if (debug) {
+ /* Print calibration parameters */
+ printk("Calibration Vector Regs 98 - 107: \n");
+ for (x=98;x<108;x++) {
+ printk("%d: %02x\n", x, wcfxs_getreg(wc, card, x));
+ }
+ }
+ return 0;
+}
+
+static int wcfxs_init_proslic(struct wcfxs *wc, int card)
+{
+
+ unsigned short tmp[5];
+ int x;
+
+ /* By default, always send on hook */
+ wc->idletxhookstate = 2;
+
+ /* Sanity check the ProSLIC */
+ if (wcfxs_proslic_insane(wc, card))
+ return -1;
+
+ if (wcfxs_init_indirect_regs(wc, card)) {
+ printk(KERN_INFO "Indirect Registers failed to initialize on module %d.\n", card);
+ return -1;
+ }
+
+ /* Clear scratch pad area */
+ wcfxs_setreg_indirect(wc, card, 97,0);
+
+ /* Clear digital loopback */
+ wcfxs_setreg(wc, card, 8, 0);
+
+ /* Revision C optimization */
+ wcfxs_setreg(wc, card, 108, 0xeb);
+
+ /* Disable automatic VBat switching for safety to prevent
+ Q7 from accidently turning on and burning out. */
+ wcfxs_setreg(wc, card, 67, 0x17);
+
+ /* Turn off Q7 */
+ wcfxs_setreg(wc, card, 66, 1);
+
+ /* Flush ProSLIC digital filters by setting to clear, while
+ saving old values */
+ for (x=0;x<5;x++) {
+ tmp[x] = wcfxs_getreg_indirect(wc, card, x + 35);
+ wcfxs_setreg_indirect(wc, card, x + 35, 0x8000);
+ }
+
+ /* Power up the DC-DC converter */
+ if (wcfxs_powerup_proslic(wc, card)) {
+ printk("Unable to do INITIAL ProSLIC powerup on module %d\n", card);
+ return -1;
+ }
+
+ /* Check for power leaks */
+ if (wcfxs_powerleak_test(wc, card)) {
+ printk("ProSLIC module %d failed leakage test. Check for short circuit\n", card);
+ return -1;
+ }
+
+ /* Power up again */
+ if (wcfxs_powerup_proslic(wc, card)) {
+ printk("Unable to do FINAL ProSLIC powerup on module %d\n", card);
+ return -1;
+ }
+
+ /* Perform calibration */
+ if (wcfxs_calibrate(wc, card)) {
+ printk("ProSlic died on Calibration.\n");
+ return -1;
+ }
+
+ /* Calibration complete, restore original values */
+ for (x=0;x<5;x++) {
+ wcfxs_setreg_indirect(wc, card, x + 35, tmp[x]);
+ }
+
+ if (wcfxs_verify_indirect_regs(wc, card)) {
+ printk(KERN_INFO "Indirect Registers failed verification.\n");
+ return -1;
+ }
+
+
+#if 0
+ /* Disable Auto Power Alarm Detect and other "features" */
+ wcfxs_setreg(wc, card, 67, 0x0e);
+ blah = wcfxs_getreg(wc, card, 67);
+#endif
+
+#if 0
+ if (wcfxs_setreg_indirect(wc, card, 97, 0x0)) { // Stanley: for the bad recording fix
printk(KERN_INFO "ProSlic IndirectReg Died.\n");
return -1;
}
- wcfxs_setreg(wc, 1, 0x2a);
- // U-Law GCI 8-bit interface
- wcfxs_setreg(wc, 2, 0); // Tx Start count low byte 0
- wcfxs_setreg(wc, 3, 0); // Tx Start count high byte 0
- wcfxs_setreg(wc, 4, 0); // Rx Start count low byte 0
- wcfxs_setreg(wc, 5, 0); // Rx Start count high byte 0
- wcfxs_setreg(wc, 8, 0x0); // disable loopback
- wcfxs_setreg(wc, 18, 0xff); // clear all interrupt
- wcfxs_setreg(wc, 19, 0xff);
- wcfxs_setreg(wc, 20, 0xff);
- wcfxs_setreg(wc, 21, 0x00); // enable interrupt
- wcfxs_setreg(wc, 22, 0x02); // Loop detection interrupt
- wcfxs_setreg(wc, 23, 0x01); // DTMF detection interrupt
- wcfxs_setreg(wc, 72, 0x20);
+#endif
+
+ wcfxs_setreg(wc, card, 1, 0x28);
+ // U-Law 8-bit interface
+ wcfxs_setreg(wc, card, 2, 0); // Tx Start count low byte 0
+ wcfxs_setreg(wc, card, 3, 0); // Tx Start count high byte 0
+ wcfxs_setreg(wc, card, 4, 0); // Rx Start count low byte 0
+ wcfxs_setreg(wc, card, 5, 0); // Rx Start count high byte 0
+ wcfxs_setreg(wc, card, 18, 0xff); // clear all interrupt
+ wcfxs_setreg(wc, card, 19, 0xff);
+ wcfxs_setreg(wc, card, 20, 0xff);
+ wcfxs_setreg(wc, card, 73, 0x04);
+
+#if 0
+ wcfxs_setreg(wc, card, 21, 0x00); // enable interrupt
+ wcfxs_setreg(wc, card, 22, 0x02); // Loop detection interrupt
+ wcfxs_setreg(wc, card, 23, 0x01); // DTMF detection interrupt
+ wcfxs_setreg(wc, card, 72, 0x20);
+#endif
+
+#if 0
+ /* Enable loopback */
+ wcfxs_setreg(wc, card, 8, 0x2);
+ wcfxs_setreg(wc, card, 14, 0x0);
+ wcfxs_setreg(wc, card, 64, 0x0);
+ wcfxs_setreg(wc, card, 1, 0x08);
+#endif
+
+ printk("Loopback: %02x\n", wcfxs_getreg(wc, card, 8));
+
#ifdef BOOST_RINGER
/* Beef up Ringing voltage to 89V */
- if (wcfxs_setreg_indirect(wc, 23, 0x1d1))
+ if (wcfxs_setreg_indirect(wc, card, 23, 0x1d1))
return -1;
#endif
return 0;
}
-static inline void wcfxs_check_hook(struct wcfxs *wc)
+static inline void wcfxs_check_hook(struct wcfxs *wc, int card)
{
char res;
int hook;
@@ -483,36 +784,76 @@ static inline void wcfxs_check_hook(struct wcfxs *wc)
/* For some reason we have to debounce the
hook detector. */
- res = wcfxs_getreg(wc, 68);
+ res = wcfxs_getreg(wc, card, 68);
hook = (res & 1);
- if (hook != wc->lastrxhook) {
+ if (hook != wc->lastrxhook[card]) {
/* Reset the debounce */
- wc->debounce = 3;
+ wc->debounce[card] = 50;
+#if 0
+ printk("Resetting debounce card %d hook %d, %d\n", card, hook, wc->debounce[card]);
+#endif
} else {
- if (wc->debounce > -1)
- wc->debounce--;
+ if (wc->debounce[card] > -1) {
+ wc->debounce[card]--;
+#if 0
+ printk("Sustaining hook %d, %d\n", hook, wc->debounce[card]);
+#endif
+ }
+ }
+ wc->lastrxhook[card] = hook;
+ if (!wc->debounce[card]) {
+#if 0
+ printk("Counted down debounce, newhook: %d...\n", hook);
+#endif
+ wc->debouncehook[card] = hook;
}
- wc->lastrxhook = hook;
- if (!wc->debounce)
- wc->debouncehook = hook;
- if (!wc->oldrxhook && wc->debouncehook) {
+ if (!wc->oldrxhook[card] && wc->debouncehook[card]) {
/* Off hook */
if (debug)
- printk("wcfxs: Going off hook\n");
- zt_hooksig(&wc->chan, ZT_RXSIG_OFFHOOK);
- wc->oldrxhook = 1;
+ printk("wcfxs: Card %d Going off hook\n", card);
+ zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK);
+ wc->oldrxhook[card] = 1;
- } else if (wc->oldrxhook && !wc->debouncehook) {
+ } else if (wc->oldrxhook[card] && !wc->debouncehook[card]) {
/* On hook */
if (debug)
- printk("wcfxs: Going on hook\n");
- zt_hooksig(&wc->chan, ZT_RXSIG_ONHOOK);
- wc->oldrxhook = 0;
+ printk("wcfxs: Card %d Going on hook\n", card);
+ zt_hooksig(&wc->chans[card], ZT_RXSIG_ONHOOK);
+ wc->oldrxhook[card] = 0;
}
}
+static int wcfxs_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data)
+{
+ struct wcfxs_stats stats;
+ struct wcfxs_regs regs;
+ struct wcfxs *wc = chan->pvt;
+ int x;
+ switch (cmd) {
+ case WCFXS_GET_STATS:
+ stats.tipvolt = wcfxs_getreg(wc, chan->chanpos - 1, 80) * -376;
+ stats.ringvolt = wcfxs_getreg(wc, chan->chanpos - 1, 81) * -376;
+ stats.batvolt = wcfxs_getreg(wc, chan->chanpos - 1, 82) * -376;
+ if (copy_to_user((struct wcfxs_stats *)data, &stats, sizeof(stats)))
+ return -EFAULT;
+ break;
+ case WCFXS_GET_REGS:
+ for (x=0;x<NUM_INDIRECT_REGS;x++)
+ regs.indirect[x] = wcfxs_getreg_indirect(wc, chan->chanpos -1, x);
+ for (x=0;x<NUM_REGS;x++)
+ regs.direct[x] = wcfxs_getreg(wc, chan->chanpos - 1, x);
+ if (copy_to_user((struct wcfxs_regs *)data, &regs, sizeof(regs)))
+ return -EFAULT;
+ break;
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+
+}
+
static int wcfxs_open(struct zt_chan *chan)
{
struct wcfxs *wc = chan->pvt;
@@ -566,29 +907,35 @@ static int wcfxs_hooksig(struct zt_chan *chan, zt_txsig_t txsig)
if (debug)
printk("Setting hook state to %d (%02x)\n", txsig, reg);
- wcfxs_setreg(wc, 64, txhook);
+#if 1
+ wcfxs_setreg(wc, chan->chanpos - 1, 64, txhook);
+#endif
return 0;
}
static int wcfxs_initialize(struct wcfxs *wc)
{
+ int x;
/* Zapata stuff */
sprintf(wc->span.name, "WCFXS/%d", wc->pos);
- sprintf(wc->span.desc, "%s Board %d\n", wc->variety, wc->pos + 1);
+ sprintf(wc->span.desc, "%s Board %d, %d modules\n", wc->variety, wc->pos + 1, wc->cards);
wc->span.deflaw = ZT_LAW_MULAW;
- sprintf(wc->chan.name, "WCFXS/%d/%d", wc->pos, 0);
- wc->chan.sigcap = ZT_SIG_FXOKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS;
- wc->chan.chanpos = 1;
- wc->span.chans = &wc->chan;
- wc->span.channels = 1;
+ for (x=0;x<wc->cards;x++) {
+ sprintf(wc->chans[x].name, "WCFXS/%d/%d", wc->pos, x);
+ wc->chans[x].sigcap = ZT_SIG_FXOKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS;
+ wc->chans[x].chanpos = x+1;
+ wc->chans[x].pvt = wc;
+ }
+ wc->span.chans = wc->chans;
+ wc->span.channels = wc->cards;
wc->span.hooksig = wcfxs_hooksig;
wc->span.open = wcfxs_open;
wc->span.close = wcfxs_close;
wc->span.flags = ZT_FLAG_RBS;
+ wc->span.ioctl = wcfxs_ioctl;
init_waitqueue_head(&wc->span.maintq);
wc->span.pvt = wc;
- wc->chan.pvt = wc;
if (zt_register(&wc->span, 0)) {
printk("Unable to register span with zaptel\n");
return -1;
@@ -600,6 +947,36 @@ static int wcfxs_hardware_init(struct wcfxs *wc)
{
/* Hardware stuff */
long oldjiffies;
+ unsigned char ver;
+ unsigned char x,y;
+ int failed;
+
+ /* Check Freshmaker chip */
+ ver = wcfxs_getcreg(wc, WC_VER);
+ failed = 0;
+ if (ver != 0x59) {
+ printk("Freshmaker version: %02x\n", ver);
+ for (x=0;x<254;x++) {
+ /* Test registers */
+ wcfxs_setcreg(wc, WC_TEST, x);
+ y = wcfxs_getcreg(wc, WC_TEST);
+ if (x != y) {
+ printk("%02x != %02x\n", x, y);
+ failed++;
+ }
+ }
+ if (!failed) {
+ printk("Freshmaker passed register test\n");
+ } else {
+ printk("Freshmaker failed register test\n");
+ return -1;
+ }
+ /* Go to half-duty FSYNC */
+ wcfxs_setcreg(wc, WC_SYNC, 1);
+ } else {
+ printk("No freshmaker chip\n");
+ }
+
/* Reset PCI Interface chip and registers (and serial) */
outb(0x0e, wc->ioaddr + WC_CNTL);
/* Setup our proper outputs for when we switch for our "serial" port */
@@ -607,12 +984,13 @@ static int wcfxs_hardware_init(struct wcfxs *wc)
outb(wc->ios, wc->ioaddr + WC_AUXD);
- /* Set all to outputs except AUX 5 and 0, which are inputs */
- outb(0xde, wc->ioaddr + WC_AUXC);
+ /* Set all to outputs except AUX 5, which is an input */
+ outb(0xdf, wc->ioaddr + WC_AUXC);
- /* Wait a sec */
+ /* Wait 1/4 of a sec */
oldjiffies = jiffies;
- while(jiffies - oldjiffies < 2);
+ while(jiffies - oldjiffies < (HZ / 4) + 1);
+
/* Back to normal, with automatic DMA wrap around */
outb(0x30 | 0x01, wc->ioaddr + WC_CNTL);
@@ -620,10 +998,7 @@ static int wcfxs_hardware_init(struct wcfxs *wc)
outb(inb(wc->ioaddr + WC_CNTL) & 0xf9, WC_CNTL);
/* Configure serial port for MSB->LSB operation */
- if (wc->flags & FLAG_DOUBLE_CLOCK)
- outb(0xc1, wc->ioaddr + WC_SERCTL);
- else
- outb(0xc0, wc->ioaddr + WC_SERCTL);
+ outb(0xc1, wc->ioaddr + WC_SERCTL);
/* Delay FSC by 0 so it's properly aligned */
outb(0x0, wc->ioaddr + WC_FSCDELAY);
@@ -639,7 +1014,18 @@ static int wcfxs_hardware_init(struct wcfxs *wc)
/* Clear interrupts */
outb(0xff, wc->ioaddr + WC_INTSTAT);
- return wcfxs_init_proslic(wc);
+
+ /* Wait 1/4 of a second more */
+ oldjiffies = jiffies;
+ while(jiffies - oldjiffies < (HZ / 4) + 1);
+
+ for (x=0;x<wc->cards;x++) {
+ if (wcfxs_init_proslic(wc, x)) {
+ printk("Error initializing card %d\n", x);
+ return -1;
+ }
+ }
+ return 0;
}
static void wcfxs_enable_interrupts(struct wcfxs *wc)
@@ -697,6 +1083,9 @@ static int __devinit wcfxs_init_one(struct pci_dev *pdev, const struct pci_devic
if (wc) {
ifaces[x] = wc;
memset(wc, 0, sizeof(struct wcfxs));
+ spin_lock_init(&wc->lock);
+ wc->curcard = -1;
+ wc->cards = 1;
wc->ioaddr = pci_resource_start(pdev, 0);
wc->dev = pdev;
wc->pos = x;
@@ -723,6 +1112,7 @@ static int __devinit wcfxs_init_one(struct pci_dev *pdev, const struct pci_devic
printk("wcfxs: Unable to intialize FXS\n");
if (wc->freeregion)
release_region(wc->ioaddr, 0xff);
+ pci_free_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, (void *)wc->writechunk, wc->writedma);
kfree(wc);
return -EIO;
}
@@ -737,15 +1127,20 @@ static int __devinit wcfxs_init_one(struct pci_dev *pdev, const struct pci_devic
printk("wcfxs: Unable to request IRQ %d\n", pdev->irq);
if (wc->freeregion)
release_region(wc->ioaddr, 0xff);
+ pci_free_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, (void *)wc->writechunk, wc->writedma);
+ pci_set_drvdata(pdev, NULL);
kfree(wc);
return -EIO;
}
if (wcfxs_hardware_init(wc)) {
+ free_irq(pdev->irq, wc);
zt_unregister(&wc->span);
if (wc->freeregion)
release_region(wc->ioaddr, 0xff);
+ pci_free_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, (void *)wc->writechunk, wc->writedma);
+ pci_set_drvdata(pdev, NULL);
kfree(wc);
return -EIO;
}
@@ -757,7 +1152,7 @@ static int __devinit wcfxs_init_one(struct pci_dev *pdev, const struct pci_devic
/* Start DMA */
wcfxs_start_dma(wc);
- printk("Found a Wildcard FXS: %s\n", wc->variety);
+ printk("Found a Wildcard FXS: %s (%d modules)\n", wc->variety, wc->cards);
res = 0;
} else
res = -ENOMEM;
@@ -830,6 +1225,9 @@ static void __exit wcfxs_cleanup(void)
MODULE_PARM(debug, "i");
MODULE_DESCRIPTION("Wildcard S100P Zaptel Driver");
MODULE_AUTHOR("Mark Spencer <markster@linux-support.net>");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
module_init(wcfxs_init);
module_exit(wcfxs_cleanup);
diff --git a/wct1xxp.c b/wct1xxp.c
index 82a634c..c612f59 100755
--- a/wct1xxp.c
+++ b/wct1xxp.c
@@ -41,7 +41,9 @@
#define WC_MAX_CARDS 32
+/*
#define TEST_REGS
+*/
/* Define to get more attention-grabbing but slightly more I/O using
alarm status */
@@ -79,11 +81,23 @@
#define WC_OFFSET 4
#define BIT_CS (1 << 7)
-
-#define BIT_OK (1 << 0)
-#define BIT_TEST (1 << 1)
-#define BIT_ERROR (1 << 2)
-#define BIT_ALARM (1 << 3)
+#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,
@@ -108,11 +122,11 @@ struct t1xxp {
/* Our offset for finding channel 1 */
int offset;
char *variety;
+ int intcount;
int usecount;
int dead;
+ int blinktimer;
int alarmtimer;
- int alarm;
- int alreadyrunning;
#ifdef FANCY_ALARM
int alarmpos;
#endif
@@ -133,31 +147,30 @@ static struct t1xxp *cards[WC_MAX_CARDS];
static inline void start_alarm(struct t1xxp *wc)
{
- wc->alarm = 1;
#ifdef FANCY_ALARM
wc->alarmpos = 0;
#endif
- wc->alarmtimer = 0;
+ wc->blinktimer = 0;
}
static inline void stop_alarm(struct t1xxp *wc)
{
- wc->alarm = 0;
#ifdef FANCY_ALARM
wc->alarmpos = 0;
#endif
- wc->alarmtimer = 0;
+ wc->blinktimer = 0;
}
-static inline void select_framer(struct t1xxp *wc)
+static inline void __select_framer(struct t1xxp *wc, int reg)
{
- if (wc->outbyte & BIT_CS) {
- wc->outbyte &= ~BIT_CS;
- outb(wc->outbyte, wc->ioaddr + WC_AUXD);
- }
+ /* 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)
+static inline void __select_control(struct t1xxp *wc)
{
if (!(wc->outbyte & BIT_CS)) {
wc->outbyte |= BIT_CS;
@@ -175,41 +188,58 @@ static int t1xxp_open(struct zt_chan *chan)
return 0;
}
-static int t1_get_reg(struct t1xxp *wc, int reg)
+static int __t1_get_reg(struct t1xxp *wc, int reg)
{
unsigned char res;
- select_framer(wc);
- /* Send address */
- outb(reg & 0xff, wc->ioaddr + WC_USERREG + 4);
+ __select_framer(wc, reg);
/* Get value */
- res = inb(wc->ioaddr + WC_USERREG);
+ res = inb(wc->ioaddr + WC_USERREG + ((reg & 0xf) << 2));
return res;
}
-static int t1_set_reg(struct t1xxp *wc, int reg, unsigned char val)
+static int __t1_set_reg(struct t1xxp *wc, int reg, unsigned char val)
{
- select_framer(wc);
+ __select_framer(wc, reg);
/* Send address */
- outb(reg & 0xff, wc->ioaddr + WC_USERREG + 4);
- outb(val, wc->ioaddr + WC_USERREG);
+ outb(val, wc->ioaddr + WC_USERREG + ((reg & 0xf) << 2));
return 0;
}
-static int control_set_reg(struct t1xxp *wc, int reg, unsigned char val)
+static int __control_set_reg(struct t1xxp *wc, int reg, unsigned char val)
{
- select_control(wc);
+ __select_control(wc);
outb(val, wc->ioaddr + WC_USERREG + ((reg & 0xf) << 2));
return 0;
}
-static int control_get_reg(struct t1xxp *wc, int reg)
+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);
+ __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);
@@ -249,12 +279,12 @@ static void t1xxp_start_dma(struct t1xxp *wc)
printk("Started DMA\n");
}
-static void t1xxp_stop_dma(struct t1xxp *wc)
+static void __t1xxp_stop_dma(struct t1xxp *wc)
{
outb(0x00, wc->ioaddr + WC_OPER);
}
-static void t1xxp_disable_interrupts(struct t1xxp *wc)
+static void __t1xxp_disable_interrupts(struct t1xxp *wc)
{
outb(0x00, wc->ioaddr + WC_MASK0);
outb(0x00, wc->ioaddr + WC_MASK1);
@@ -265,6 +295,11 @@ 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) {
@@ -279,105 +314,101 @@ static void t1xxp_framer_start(struct t1xxp *wc)
} else {
framing = "AMI";
}
- t1_set_reg(wc, 0x38, i);
+ __t1_set_reg(wc, 0x38, i);
if (!(wc->span.lineconfig & ZT_CONFIG_ESF)) {
/* 1c in FDL bit */
- t1_set_reg(wc, 0x7e, 0x1c);
+ __t1_set_reg(wc, 0x7e, 0x1c);
} else {
- t1_set_reg(wc, 0x7e, 0x00);
+ __t1_set_reg(wc, 0x7e, 0x00);
}
/* Set outgoing LBO */
- t1_set_reg(wc, 0x7c, wc->span.txlevel << 5);
+ __t1_set_reg(wc, 0x7c, wc->span.txlevel << 5);
printk("Using %s/%s coding/framing\n", coding, framing);
- if (!wc->alreadyrunning) {
+ if (!alreadyrunning) {
/* Set LIRST bit to 1 */
- t1_set_reg(wc, 0x30, 0x1);
+ __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);
- /* Reset elastic stores */
- t1_set_reg(wc, 0x30, 0x40);
- wc->alreadyrunning = 1;
- }
-#if 0
- {
- t1_set_reg(wc, 0x3c, 0xff);
- t1_set_reg(wc, 0x3d, 0xff);
- t1_set_reg(wc, 0x3e, 0xff);
- int x;
- for (i=0x20;i<0x40;i++) {
- x = t1_get_reg(wc, i);
- if (x)
- printk("%02x: %02x\n", i, x);
- }
- for (i=0x60;i<0x80;i++) {
- x = t1_get_reg(wc, i);
- if (x)
- printk("%02x: %02x\n", i, x);
- }
+ 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;
}
-#endif
+ 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=0x20;x<0x40;x++)
- t1_set_reg(wc, x, 0);
- for (x=0x60;x<0x80;x++)
- t1_set_reg(wc, x, 0);
+ for (x=0x0;x<0x96;x++)
+ __t1_set_reg(wc, x, 0);
/* Full-on sync required */
- t1_set_reg(wc, 0x2b, 0x08);
+ __t1_set_reg(wc, 0x2b, 0x08);
/* RSYNC is an input */
- t1_set_reg(wc, 0x2c, 0x08);
+ __t1_set_reg(wc, 0x2c, 0x08);
/* Enable tx RBS bits */
- t1_set_reg(wc, 0x35, 0x10);
+ __t1_set_reg(wc, 0x35, 0x10);
/* TSYNC is output */
- t1_set_reg(wc, 0x36, 0x04);
+ __t1_set_reg(wc, 0x36, 0x04);
/* Tx and Rx elastic store enabled, 2.048 Mhz (in theory) */
- t1_set_reg(wc, 0x37, 0x8c);
-#ifdef TEST_REGS
- printk("Testing framer registers...\n");
- t1_set_reg(wc, 0x6c, 0xff);
- printk("Expecting ff, got %02x\n", t1_get_reg(wc, 0x6c));
- t1_set_reg(wc, 0x6c, 0x00);
- printk("Expecting 00, got %02x\n", t1_get_reg(wc, 0x6c));
- t1_set_reg(wc, 0x6c, 0xaa);
- printk("Expecting aa, got %02x\n", t1_get_reg(wc, 0x6c));
- t1_set_reg(wc, 0x6c, 0x55);
- printk("Expecting 55, got %02x\n", t1_get_reg(wc, 0x6c));
- t1_set_reg(wc, 0x6c, 0x00);
- printk("Testing control registers...\n");
- control_set_reg(wc, WC_LEDTEST, 0xff);
- printk("Expecting ff, got %02x\n", control_get_reg(wc, WC_LEDTEST));
- control_set_reg(wc, WC_LEDTEST, 0x00);
- printk("Expecting 00, got %02x\n", control_get_reg(wc, WC_LEDTEST));
- control_set_reg(wc, WC_LEDTEST, 0xaa);
- printk("Expecting aa, got %02x\n", control_get_reg(wc, WC_LEDTEST));
- control_set_reg(wc, WC_LEDTEST, 0x55);
- printk("Expecting 55, got %02x\n", control_get_reg(wc, WC_LEDTEST));
- control_set_reg(wc, WC_LEDTEST, 0x00);
-#endif
+ __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 */
- int b = (chan->chanpos - 1) / 8;
- int o = (chan->chanpos - 1) % 8;
- unsigned char mask = (1 << o);
+ 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;
@@ -392,10 +423,11 @@ static int t1xxp_rbsbits(struct zt_chan *chan, int bits)
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]);
+ __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;
}
@@ -412,6 +444,7 @@ 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);
@@ -424,9 +457,15 @@ static int t1xxp_startup(struct zt_span *span)
static int t1xxp_shutdown(struct zt_span *span)
{
struct t1xxp *wc = span->pvt;
- t1xxp_stop_dma(wc);
- t1xxp_disable_interrupts(wc);
+ 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;
}
@@ -436,7 +475,7 @@ static int t1xxp_maint(struct zt_span *span, int mode)
return -1;
}
-static void t1xxp_set_clear(struct t1xxp *wc)
+static void __t1xxp_set_clear(struct t1xxp *wc)
{
/* Setup registers */
int x,y;
@@ -446,7 +485,7 @@ static void t1xxp_set_clear(struct t1xxp *wc)
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);
+ __t1_set_reg(wc, 0x39 + x, b);
}
}
@@ -455,9 +494,12 @@ 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);
+ __t1xxp_set_clear(wc);
+
spin_unlock_irqrestore(&wc->lock, flags);
return 0;
}
@@ -504,6 +546,7 @@ static int t1xxp_software_init(struct t1xxp *wc)
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);
@@ -521,34 +564,51 @@ static int t1xxp_software_init(struct t1xxp *wc)
return 0;
}
-static inline void handle_leds(struct t1xxp *wc)
+static inline void __handle_leds(struct t1xxp *wc)
{
- if (wc->alarm) {
+ int oldreg;
+ wc->blinktimer++;
+ if (wc->span.alarms & (ZT_ALARM_RED | ZT_ALARM_BLUE)) {
+ /* Red/Blue alarm */
#ifdef FANCY_ALARM
- if (wc->alarmtimer == (altab[wc->alarmpos] >> 1)) {
- wc->ledtestreg = wc->ledtestreg | BIT_ALARM;
- control_set_reg(wc, WC_LEDTEST, wc->ledtestreg);
+ 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->alarmtimer == 0xf) {
- wc->ledtestreg = wc->ledtestreg & ~BIT_ALARM;
- control_set_reg(wc, WC_LEDTEST, wc->ledtestreg);
- wc->alarmtimer = -1;
+ 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;
}
- wc->alarmtimer++;
#else
- wc->alarmtimer++;
- if (wc->alarmtimer == 160) {
- wc->ledtestreg = wc->ledtestreg | BIT_ALARM;
- control_set_reg(wc, WC_LEDTEST, wc->ledtestreg);
- } else if (wc->alarmtimer == 480) {
- wc->ledtestreg = wc->ledtestreg & ~BIT_ALARM;
- control_set_reg(wc, WC_LEDTEST, wc->ledtestreg);
- wc->alarmtimer = 0;
+ 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);
}
}
@@ -606,50 +666,140 @@ static void t1xxp_receiveprep(struct t1xxp *wc, int ints)
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, a,b, rxs;
- static int gotint = 0;
- int x,y,i;
+ unsigned char ints;
+ long flags;
+ int x;
ints = inb(wc->ioaddr + WC_INTSTAT);
+ outb(ints, wc->ioaddr + WC_INTSTAT);
+
if (!ints)
return;
- outb(ints, wc->ioaddr + WC_INTSTAT);
- if (!gotint) {
+ if (!wc->intcount) {
printk("Got interrupt: 0x%04x\n", ints);
}
- gotint++;
+ wc->intcount++;
if (ints & 0x0f) {
- t1xxp_transmitprep(wc, ints);
t1xxp_receiveprep(wc, ints);
+ t1xxp_transmitprep(wc, ints);
}
- handle_leds(wc);
+ spin_lock_irqsave(&wc->lock, flags);
-#if 1
- if (!(gotint & 0xf0)) {
- x = gotint & 0xf;
- if (x < 3) {
- 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);
- }
- }
- }
- }
+ __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");
@@ -698,12 +848,16 @@ static int t1xxp_hardware_init(struct t1xxp *wc)
/* 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);
- /* Pretend we're in alarm */
start_alarm(wc);
return 0;
@@ -720,6 +874,7 @@ static int __devinit t1xxp_init_one(struct pci_dev *pdev, const struct pci_devic
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);
@@ -773,6 +928,10 @@ static void t1xxp_stop_stuff(struct t1xxp *wc)
/* 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)
@@ -781,10 +940,10 @@ static void __devexit t1xxp_remove_one(struct pci_dev *pdev)
if (wc) {
/* Stop any DMA */
- t1xxp_stop_dma(wc);
+ __t1xxp_stop_dma(wc);
/* In case hardware is still there */
- t1xxp_disable_interrupts(wc);
+ __t1xxp_disable_interrupts(wc);
t1xxp_stop_stuff(wc);
@@ -833,6 +992,9 @@ static void __exit t1xxp_cleanup(void)
MODULE_PARM(debug, "i");
MODULE_DESCRIPTION("Wildcard T100P Zaptel Driver");
MODULE_AUTHOR("Mark Spencer <markster@linux-support.net>");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
module_init(t1xxp_init);
module_exit(t1xxp_cleanup);
diff --git a/wctdm.c b/wctdm.c
index 0a9783b..e4a22b2 100755
--- a/wctdm.c
+++ b/wctdm.c
@@ -33,6 +33,7 @@
#include <linux/pci.h>
#include "proslic.h"
+#include "wcfxs.h"
static alpha indirect_regs[] =
{
@@ -49,11 +50,11 @@ static alpha indirect_regs[] =
{10,"DTMF_COL_2ND_ARM",0x0202},
{11,"DTMF_PWR_MIN_TRES",0x00E5},
{12,"DTMF_OT_LIM_TRES",0x0A1C},
-{13,"OSC1_COEF",0x6D40},
-{14,"OSC1X",0x0470},
+{13,"OSC1_COEF",0x7B30},
+{14,"OSC1X",0x0063},
{15,"OSC1Y",0x0000},
-{16,"OSC2_COEF",0x4A80},
-{17,"OSC2X",0x0830},
+{16,"OSC2_COEF",0x7870},
+{17,"OSC2X",0x007D},
{18,"OSC2Y",0x0000},
{19,"RING_V_OFF",0x0000},
{20,"RING_OSC",0x7EF0},
@@ -70,17 +71,19 @@ static alpha indirect_regs[] =
{29,"RING_TRIP_TRES",0x3600},
{30,"COMMON_MIN_TRES",0x1000},
{31,"COMMON_MAX_TRES",0x0200},
-{32,"PWR_ALARM_Q1Q2",0x0550},
+{32,"PWR_ALARM_Q1Q2",0x07C0},
{33,"PWR_ALARM_Q3Q4",0x2600},
{34,"PWR_ALARM_Q5Q6",0x1B80},
{35,"LOOP_CLOSURE_FILTER",0x8000},
{36,"RING_TRIP_FILTER",0x0320},
-{37,"TERM_LP_POLE_Q1Q2",0x0100},
+{37,"TERM_LP_POLE_Q1Q2",0x008C},
{38,"TERM_LP_POLE_Q3Q4",0x0100},
{39,"TERM_LP_POLE_Q5Q6",0x0010},
{40,"CM_BIAS_RINGING",0x0C00},
{41,"DCDC_MIN_V",0x0C00},
{42,"DCDC_XTRA",0x1000},
+{43,"LOOP_CLOSE_TRES_LOW",0x1000},
+{97,"RCV_FLTR", 0}
};
#ifdef STANDALONE_ZAPATA
@@ -111,6 +114,14 @@ static alpha indirect_regs[] =
#define WC_SERCTL 0x2d
#define WC_FSCDELAY 0x2f
+#define WC_REGBASE 0xc0
+
+#define WC_LEDS 0x0
+#define WC_TEST 0x1
+#define WC_CS 0x2
+#define WC_VER 0x3
+#define WC_SYNC 0x4
+
#define BIT_CS (1 << 2)
#define BIT_SCLK (1 << 3)
#define BIT_SDI (1 << 4)
@@ -125,11 +136,12 @@ static alpha indirect_regs[] =
#define FLAG_DOUBLE_CLOCK (1 << 0)
+#define NUM_CARDS 4
+
struct wcfxs {
struct pci_dev *dev;
char *variety;
struct zt_span span;
- struct zt_chan chan;
unsigned char ios;
int usecount;
int intcount;
@@ -138,12 +150,15 @@ struct wcfxs {
int flags;
int freeregion;
int alt;
+ int curcard;
+ int cards;
+ spinlock_t lock;
/* Receive hook state and debouncing */
- int oldrxhook;
- int debouncehook;
- int lastrxhook;
- int debounce;
+ int oldrxhook[NUM_CARDS];
+ int debouncehook[NUM_CARDS];
+ int lastrxhook[NUM_CARDS];
+ int debounce[NUM_CARDS];
int idletxhookstate; /* IDLE changing hook state */
unsigned long ioaddr;
@@ -151,6 +166,7 @@ struct wcfxs {
dma_addr_t writedma;
volatile int *writechunk; /* Double-word aligned write memory */
volatile int *readchunk; /* Double-word aligned read memory */
+ struct zt_chan chans[NUM_CARDS];
};
@@ -159,7 +175,7 @@ struct wcfxs_desc {
int flags;
};
-static struct wcfxs_desc wcfxs = { "Wildcard Prototype", 0 };
+static struct wcfxs_desc wcfxs = { "Wildcard S400P Prototype", 0 };
static struct wcfxs *ifaces[WC_MAX_IFACES];
@@ -181,7 +197,13 @@ static inline void wcfxs_transmitprep(struct wcfxs *wc, unsigned char ints)
for (x=0;x<ZT_CHUNKSIZE;x++) {
/* Send a sample, as a 32-bit word */
- writechunk[x] = wc->chan.writechunk[x] << 24;
+ writechunk[x] = (wc->chans[0].writechunk[x] << 24);
+ if (wc->cards > 1)
+ writechunk[x] |= (wc->chans[1].writechunk[x] << 16);
+ if (wc->cards > 2)
+ writechunk[x] |= (wc->chans[2].writechunk[x] << 8);
+ if (wc->cards > 3)
+ writechunk[x] |= (wc->chans[3].writechunk[x]);
}
@@ -198,25 +220,37 @@ static inline void wcfxs_receiveprep(struct wcfxs *wc, unsigned char ints)
/* Read is not at interrupt address. Valid data is available at normal offset */
readchunk = wc->readchunk;
for (x=0;x<ZT_CHUNKSIZE;x++) {
- wc->chan.readchunk[x] = (readchunk[x] >> 24) & 0xff;
+ wc->chans[0].readchunk[x] = (readchunk[x] >> 24) & 0xff;
+ if (wc->cards > 1)
+ wc->chans[1].readchunk[x] = (readchunk[x] >> 16) & 0xff;
+ if (wc->cards > 2)
+ wc->chans[2].readchunk[x] = (readchunk[x] >> 8) & 0xff;
+ if (wc->cards > 3)
+ wc->chans[3].readchunk[x] = (readchunk[x]) & 0xff;
}
zt_receive(&wc->span);
}
-static inline void wcfxs_check_hook(struct wcfxs *wc);
+static inline void wcfxs_check_hook(struct wcfxs *wc, int card);
static void wcfxs_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct wcfxs *wc = dev_id;
unsigned char ints;
+ static int p=0;
ints = inb(wc->ioaddr + WC_INTSTAT);
outb(ints, wc->ioaddr + WC_INTSTAT);
+ if (!p) {
+ printk("Interrupt!\n");
+ p++;
+ }
if (!ints)
return;
+
if (ints & 0x10) {
printk("PCI Master abort\n");
return;
@@ -226,12 +260,15 @@ static void wcfxs_interrupt(int irq, void *dev_id, struct pt_regs *regs)
printk("PCI Target abort\n");
return;
}
+
+
if (ints & 0x0f) {
wc->intcount++;
- if (!(wc->intcount % 10))
- wcfxs_check_hook(wc);
- if (ints & 3) wcfxs_transmitprep(wc, ints);
+ if ((wc->intcount % 10) < wc->cards) {
+ wcfxs_check_hook(wc, wc->intcount % 10);
+ }
if (ints & 0xc) wcfxs_receiveprep(wc, ints);
+ if (ints & 3) wcfxs_transmitprep(wc, ints);
}
}
@@ -295,85 +332,133 @@ static inline unsigned char read_8bits(struct wcfxs *wc)
return res;
}
-static void wcfxs_setreg(struct wcfxs *wc, unsigned char reg, unsigned char value)
+static void wcfxs_setcreg(struct wcfxs *wc, unsigned char reg, unsigned char val)
{
+ outb(val, wc->ioaddr + WC_REGBASE + ((reg & 0xf) << 2));
+}
+
+static unsigned char wcfxs_getcreg(struct wcfxs *wc, unsigned char reg)
+{
+ return inb(wc->ioaddr + WC_REGBASE + ((reg & 0xf) << 2));
+}
+
+static inline void wcfxs_setcard(struct wcfxs *wc, int card)
+{
+ if (wc->curcard != card) {
+ wcfxs_setcreg(wc, WC_CS, (1 << card));
+ wc->curcard = card;
+ }
+}
+
+static void __wcfxs_setreg(struct wcfxs *wc, int card, unsigned char reg, unsigned char value)
+{
+ wcfxs_setcard(wc, card);
write_8bits(wc, reg & 0x7f);
write_8bits(wc, value);
}
-static unsigned char wcfxs_getreg(struct wcfxs *wc, unsigned char reg)
+static void wcfxs_setreg(struct wcfxs *wc, int card, unsigned char reg, unsigned char value)
+{
+ long flags;
+ spin_lock_irqsave(&wc->lock, flags);
+ __wcfxs_setreg(wc, card, reg, value);
+ spin_unlock_irqrestore(&wc->lock, flags);
+}
+
+static unsigned char __wcfxs_getreg(struct wcfxs *wc, int card, unsigned char reg)
{
+ wcfxs_setcard(wc, card);
write_8bits(wc, reg | 0x80);
return read_8bits(wc);
}
-static int wait_access(struct wcfxs *wc)
+static unsigned char wcfxs_getreg(struct wcfxs *wc, int card, unsigned char reg)
{
- unsigned char count, data;
- count = 0;
- #define MAX 60
+ long flags;
+ unsigned char res;
+ spin_lock_irqsave(&wc->lock, flags);
+ res = __wcfxs_getreg(wc, card, reg);
+ spin_unlock_irqrestore(&wc->lock, flags);
+ return res;
+}
+
+static int __wait_access(struct wcfxs *wc, int card)
+{
+ unsigned char data;
+ long origjiffies;
+ int count = 0;
+
+ #define MAX 6000 /* attempts */
+ origjiffies = jiffies;
/* Wait for indirect access */
while (count++ < MAX)
{
- data = wcfxs_getreg(wc, I_STATUS);
+ data = __wcfxs_getreg(wc, card, I_STATUS);
if (!data)
return 0;
}
- if(count > (MAX-1)) printk(" ##### Loop error #####\n");
+ if(count > (MAX-1)) printk(" ##### Loop error (%02x) #####\n", data);
- return -1;
+ return 0;
}
-static int wcfxs_setreg_indirect(struct wcfxs *wc, unsigned char address, unsigned short data)
+static int wcfxs_setreg_indirect(struct wcfxs *wc, int card, unsigned char address, unsigned short data)
{
-
- if(!wait_access(wc))
- {
- wcfxs_setreg(wc, IDA_LO,(unsigned char)(data & 0xFF));
- wcfxs_setreg(wc, IDA_HI,(unsigned char)((data & 0xFF00)>>8));
- wcfxs_setreg(wc, IAA,address);
- return 0;
- }
-
- return -1;
+ long flags;
+ int res = -1;
+ spin_lock_irqsave(&wc->lock, flags);
+ if(!__wait_access(wc, card)) {
+ __wcfxs_setreg(wc, card, IDA_LO,(unsigned char)(data & 0xFF));
+ __wcfxs_setreg(wc, card, IDA_HI,(unsigned char)((data & 0xFF00)>>8));
+ __wcfxs_setreg(wc, card, IAA,address);
+ res = 0;
+ };
+ spin_unlock_irqrestore(&wc->lock, flags);
+ return res;
}
-static int wcfxs_getreg_indirect(struct wcfxs *wc, unsigned char address)
+static int wcfxs_getreg_indirect(struct wcfxs *wc, int card, unsigned char address)
{
- if (!wait_access(wc)) {
- wcfxs_setreg(wc, IAA, address);
- if (!wait_access(wc)) {
- unsigned char data1, data2;
- data1 = wcfxs_getreg(wc, IDA_LO);
- data2 = wcfxs_getreg(wc, IDA_HI);
- return data1 | (data2 << 8);
+ long flags;
+ int res = -1;
+ char *p=NULL;
+ spin_lock_irqsave(&wc->lock, flags);
+ if (!__wait_access(wc, card)) {
+ __wcfxs_setreg(wc, card, IAA, address);
+ if (!__wait_access(wc, card)) {
+ unsigned char data1, data2;
+ data1 = __wcfxs_getreg(wc, card, IDA_LO);
+ data2 = __wcfxs_getreg(wc, card, IDA_HI);
+ res = data1 | (data2 << 8);
+ } else
+ p = "Failed to wait inside\n";
} else
- printk("Failed to wait inside\n");
- } else
- printk("failed to wait\n");
-
- return -1;
+ p = "failed to wait\n";
+ spin_unlock_irqrestore(&wc->lock, flags);
+ if (p)
+ printk(p);
+ return res;
}
-static int wcfxs_init_indirect_regs(struct wcfxs *wc)
+static int wcfxs_init_indirect_regs(struct wcfxs *wc, int card)
{
unsigned char i;
for (i=0; i<43; i++)
{
- if(wcfxs_setreg_indirect(wc, i,indirect_regs[i].initial))
+ if(wcfxs_setreg_indirect(wc, card, i,indirect_regs[i].initial))
return -1;
}
return 0;
}
-static int wcfxs_verify_indirect_regs(struct wcfxs *wc)
+static int wcfxs_verify_indirect_regs(struct wcfxs *wc, int card)
{
int passed = 1;
unsigned short i, initial;
@@ -381,7 +466,7 @@ static int wcfxs_verify_indirect_regs(struct wcfxs *wc)
for (i=0; i<43; i++)
{
- if((j = wcfxs_getreg_indirect(wc, (unsigned char) i)) < 0) {
+ if((j = wcfxs_getreg_indirect(wc, card, (unsigned char) i)) < 0) {
printk("Failed to read indirect register %d\n", i);
return -1;
}
@@ -405,77 +490,293 @@ static int wcfxs_verify_indirect_regs(struct wcfxs *wc)
return 0;
}
-static int wcfxs_calibrate(struct wcfxs *wc)
-{
- unsigned char x;
+static int wcfxs_proslic_insane(struct wcfxs *wc, int card)
+{
+ int blah;
- wcfxs_setreg(wc, 92, 0xc8);
- wcfxs_setreg(wc, 97, 0);
- wcfxs_setreg(wc, 93, 0x19);
- wcfxs_setreg(wc, 14, 0);
- wcfxs_setreg(wc, 93, 0x99);
+ blah = wcfxs_getreg(wc, card, 0);
+ if (debug)
+ printk("ProSLIC on module %d, product %d, version %d\n", card, (blah & 0x30) >> 4, (blah & 0xf));
- x = wcfxs_getreg(wc, 93);
- if (debug)
- printk("DC Cal x=%x\n",x);
- wcfxs_setreg(wc, 97, 0);
- wcfxs_setreg(wc, CALIBR1, CALIBRATE_LINE);
- x = wcfxs_getreg(wc, CALIBR1);
- wcfxs_setreg(wc, LINE_STATE, ACTIVATE_LINE);
+ if ((blah & 0x30) >> 4) {
+ printk("ProSLIC on module %d is not a 3210.\n", card);
+ return -1;
+ }
+ if ((blah & 0xf) < 3) {
+ printk("ProSLIC 3210 version %d is too old\n", blah & 0xf);
+ return -1;
+ }
+
+ blah = wcfxs_getreg(wc, card, 8);
+ if (blah != 0x2) {
+ printk("ProSLIC on module %d insane (1)\n", card);
+ return -1;
+ }
+
+ blah = wcfxs_getreg(wc, card, 64);
+ if (blah != 0x0) {
+ printk("ProSLIC on module %d insane (2)\n", card);
+ return -1;
+ }
+
+ blah = wcfxs_getreg(wc, card, 11);
+ if (blah != 0x33) {
+ printk("ProSLIC on module %d insane (3)\n", card);
+ return -1;
+ }
+
+ /* Just be sure it's setup right. */
+ wcfxs_setreg(wc, card, 30, 0);
+ if (debug)
+ printk("ProSLIC on module %d seems sane.\n", card);
return 0;
}
-static int wcfxs_init_proslic(struct wcfxs *wc)
+static int wcfxs_powerleak_test(struct wcfxs *wc, int card)
{
- int blah;
+ unsigned long origjiffies;
+ unsigned char vbat;
- /* By default, always send on hook */
- wc->idletxhookstate = 2;
+ /* Turn off linefeed */
+ wcfxs_setreg(wc, card, 64, 0);
- /* Disable Auto Power Alarm Detect and other "features" */
- wcfxs_setreg(wc, 67, 0x0e);
- blah = wcfxs_getreg(wc, 67);
+ /* Power down */
+ wcfxs_setreg(wc, card, 14, 0x10);
- if (wcfxs_init_indirect_regs(wc)) {
- printk(KERN_INFO "Indirect Registers failed to initialize.\n");
- return -1;
+ /* Wait for one second */
+ origjiffies = jiffies;
+
+ while((vbat = wcfxs_getreg(wc, card, 82)) > 0x3) {
+ if ((jiffies - origjiffies) >= HZ)
+ break;;
}
- if (wcfxs_verify_indirect_regs(wc)) {
- printk(KERN_INFO "Indirect Registers failed verification.\n");
- return -1;
+
+ if (vbat < 0x04) {
+ printk("Excessive leakage detected on module %d: %d volts (%02x) after %d ms\n", card,
+ 376 * vbat / 1000, vbat, (int)((jiffies - origjiffies) * 1000 / HZ));
+ return -1;
+ } else if (debug) {
+ printk("Post-leakage voltage: %d volts\n", 376 * vbat / 1000);
}
- if (wcfxs_calibrate(wc)) {
- printk(KERN_INFO "ProSlic Died on Activation.\n");
- return -1;
+ return 0;
+}
+
+static int wcfxs_powerup_proslic(struct wcfxs *wc, int card)
+{
+ unsigned char vbat;
+ unsigned long origjiffies;
+
+ /* Set period of DC-DC converter to 1/64 khz */
+ wcfxs_setreg(wc, card, 92, 0xff);
+
+ /* Engage DC-DC converter */
+ wcfxs_setreg(wc, card, 93, 0x19);
+
+ /* Wait for VBat to powerup */
+ origjiffies = jiffies;
+
+ /* Disable powerdown */
+ wcfxs_setreg(wc, card, 14, 0);
+
+ while((vbat = wcfxs_getreg(wc, card, 82)) < 0xc0) {
+ /* Wait no more than 500ms */
+ if ((jiffies - origjiffies) > HZ/2) {
+ break;
+ }
+ }
+
+#if 0
+ printk("jiffies - origjiffies: %d\n", ((int)(jiffies - origjiffies)));
+#endif
+
+ if (vbat < 0xc0) {
+ printk("ProSLIC on module %d failed to powerup within %d ms\n",
+ card, (int)(((jiffies - origjiffies) * 1000 / HZ)));
+ return -1;
+ } else if (debug) {
+ printk("ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n",
+ card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ)));
}
- if (wcfxs_setreg_indirect(wc, 97, 0x0)) { // Stanley: for the bad recording fix
+ /* Perform DC-DC calibration */
+ wcfxs_setreg(wc, card, 93, 0x80);
+
+ origjiffies = jiffies;
+ while(0x80 & wcfxs_getreg(wc, card, 93)) {
+ if ((jiffies - origjiffies) > 2 * HZ) {
+ printk("Timeout waiting for DC-DC calibration on module %d\n", card);
+ return -1;
+ }
+ }
+
+#if 0
+ /* Wait a full two seconds */
+ while((jiffies - origjiffies) < 2 * HZ);
+
+ /* Just check to be sure */
+ vbat = wcfxs_getreg(wc, card, 82);
+ printk("ProSLIC on module %d powered up to -%d volts (%02x) in %d ms\n",
+ card, vbat * 376 / 1000, vbat, (int)(((jiffies - origjiffies) * 1000 / HZ)));
+#endif
+ return 0;
+
+}
+
+static int wcfxs_calibrate(struct wcfxs *wc, int card)
+{
+ unsigned long origjiffies;
+ int x;
+ /* Perform all calibrations */
+ wcfxs_setreg(wc, card, 97, 0x1f);
+
+ /* Begin, no speedup */
+ wcfxs_setreg(wc, card, 96, 0x5f);
+
+ /* Wait for it to finish */
+ origjiffies = jiffies;
+ while(wcfxs_getreg(wc, card, 96)) {
+ if ((jiffies - origjiffies) > 2 * HZ) {
+ printk("Timeout waiting for calibration of module %d\n", card);
+ return -1;
+ }
+ }
+
+ if (debug) {
+ /* Print calibration parameters */
+ printk("Calibration Vector Regs 98 - 107: \n");
+ for (x=98;x<108;x++) {
+ printk("%d: %02x\n", x, wcfxs_getreg(wc, card, x));
+ }
+ }
+ return 0;
+}
+
+static int wcfxs_init_proslic(struct wcfxs *wc, int card)
+{
+
+ unsigned short tmp[5];
+ int x;
+
+ /* By default, always send on hook */
+ wc->idletxhookstate = 2;
+
+ /* Sanity check the ProSLIC */
+ if (wcfxs_proslic_insane(wc, card))
+ return -1;
+
+ if (wcfxs_init_indirect_regs(wc, card)) {
+ printk(KERN_INFO "Indirect Registers failed to initialize on module %d.\n", card);
+ return -1;
+ }
+
+ /* Clear scratch pad area */
+ wcfxs_setreg_indirect(wc, card, 97,0);
+
+ /* Clear digital loopback */
+ wcfxs_setreg(wc, card, 8, 0);
+
+ /* Revision C optimization */
+ wcfxs_setreg(wc, card, 108, 0xeb);
+
+ /* Disable automatic VBat switching for safety to prevent
+ Q7 from accidently turning on and burning out. */
+ wcfxs_setreg(wc, card, 67, 0x17);
+
+ /* Turn off Q7 */
+ wcfxs_setreg(wc, card, 66, 1);
+
+ /* Flush ProSLIC digital filters by setting to clear, while
+ saving old values */
+ for (x=0;x<5;x++) {
+ tmp[x] = wcfxs_getreg_indirect(wc, card, x + 35);
+ wcfxs_setreg_indirect(wc, card, x + 35, 0x8000);
+ }
+
+ /* Power up the DC-DC converter */
+ if (wcfxs_powerup_proslic(wc, card)) {
+ printk("Unable to do INITIAL ProSLIC powerup on module %d\n", card);
+ return -1;
+ }
+
+ /* Check for power leaks */
+ if (wcfxs_powerleak_test(wc, card)) {
+ printk("ProSLIC module %d failed leakage test. Check for short circuit\n", card);
+ return -1;
+ }
+
+ /* Power up again */
+ if (wcfxs_powerup_proslic(wc, card)) {
+ printk("Unable to do FINAL ProSLIC powerup on module %d\n", card);
+ return -1;
+ }
+
+ /* Perform calibration */
+ if (wcfxs_calibrate(wc, card)) {
+ printk("ProSlic died on Calibration.\n");
+ return -1;
+ }
+
+ /* Calibration complete, restore original values */
+ for (x=0;x<5;x++) {
+ wcfxs_setreg_indirect(wc, card, x + 35, tmp[x]);
+ }
+
+ if (wcfxs_verify_indirect_regs(wc, card)) {
+ printk(KERN_INFO "Indirect Registers failed verification.\n");
+ return -1;
+ }
+
+
+#if 0
+ /* Disable Auto Power Alarm Detect and other "features" */
+ wcfxs_setreg(wc, card, 67, 0x0e);
+ blah = wcfxs_getreg(wc, card, 67);
+#endif
+
+#if 0
+ if (wcfxs_setreg_indirect(wc, card, 97, 0x0)) { // Stanley: for the bad recording fix
printk(KERN_INFO "ProSlic IndirectReg Died.\n");
return -1;
}
- wcfxs_setreg(wc, 1, 0x2a);
- // U-Law GCI 8-bit interface
- wcfxs_setreg(wc, 2, 0); // Tx Start count low byte 0
- wcfxs_setreg(wc, 3, 0); // Tx Start count high byte 0
- wcfxs_setreg(wc, 4, 0); // Rx Start count low byte 0
- wcfxs_setreg(wc, 5, 0); // Rx Start count high byte 0
- wcfxs_setreg(wc, 8, 0x0); // disable loopback
- wcfxs_setreg(wc, 18, 0xff); // clear all interrupt
- wcfxs_setreg(wc, 19, 0xff);
- wcfxs_setreg(wc, 20, 0xff);
- wcfxs_setreg(wc, 21, 0x00); // enable interrupt
- wcfxs_setreg(wc, 22, 0x02); // Loop detection interrupt
- wcfxs_setreg(wc, 23, 0x01); // DTMF detection interrupt
- wcfxs_setreg(wc, 72, 0x20);
+#endif
+
+ wcfxs_setreg(wc, card, 1, 0x28);
+ // U-Law 8-bit interface
+ wcfxs_setreg(wc, card, 2, 0); // Tx Start count low byte 0
+ wcfxs_setreg(wc, card, 3, 0); // Tx Start count high byte 0
+ wcfxs_setreg(wc, card, 4, 0); // Rx Start count low byte 0
+ wcfxs_setreg(wc, card, 5, 0); // Rx Start count high byte 0
+ wcfxs_setreg(wc, card, 18, 0xff); // clear all interrupt
+ wcfxs_setreg(wc, card, 19, 0xff);
+ wcfxs_setreg(wc, card, 20, 0xff);
+ wcfxs_setreg(wc, card, 73, 0x04);
+
+#if 0
+ wcfxs_setreg(wc, card, 21, 0x00); // enable interrupt
+ wcfxs_setreg(wc, card, 22, 0x02); // Loop detection interrupt
+ wcfxs_setreg(wc, card, 23, 0x01); // DTMF detection interrupt
+ wcfxs_setreg(wc, card, 72, 0x20);
+#endif
+
+#if 0
+ /* Enable loopback */
+ wcfxs_setreg(wc, card, 8, 0x2);
+ wcfxs_setreg(wc, card, 14, 0x0);
+ wcfxs_setreg(wc, card, 64, 0x0);
+ wcfxs_setreg(wc, card, 1, 0x08);
+#endif
+
+ printk("Loopback: %02x\n", wcfxs_getreg(wc, card, 8));
+
#ifdef BOOST_RINGER
/* Beef up Ringing voltage to 89V */
- if (wcfxs_setreg_indirect(wc, 23, 0x1d1))
+ if (wcfxs_setreg_indirect(wc, card, 23, 0x1d1))
return -1;
#endif
return 0;
}
-static inline void wcfxs_check_hook(struct wcfxs *wc)
+static inline void wcfxs_check_hook(struct wcfxs *wc, int card)
{
char res;
int hook;
@@ -483,36 +784,76 @@ static inline void wcfxs_check_hook(struct wcfxs *wc)
/* For some reason we have to debounce the
hook detector. */
- res = wcfxs_getreg(wc, 68);
+ res = wcfxs_getreg(wc, card, 68);
hook = (res & 1);
- if (hook != wc->lastrxhook) {
+ if (hook != wc->lastrxhook[card]) {
/* Reset the debounce */
- wc->debounce = 3;
+ wc->debounce[card] = 50;
+#if 0
+ printk("Resetting debounce card %d hook %d, %d\n", card, hook, wc->debounce[card]);
+#endif
} else {
- if (wc->debounce > -1)
- wc->debounce--;
+ if (wc->debounce[card] > -1) {
+ wc->debounce[card]--;
+#if 0
+ printk("Sustaining hook %d, %d\n", hook, wc->debounce[card]);
+#endif
+ }
+ }
+ wc->lastrxhook[card] = hook;
+ if (!wc->debounce[card]) {
+#if 0
+ printk("Counted down debounce, newhook: %d...\n", hook);
+#endif
+ wc->debouncehook[card] = hook;
}
- wc->lastrxhook = hook;
- if (!wc->debounce)
- wc->debouncehook = hook;
- if (!wc->oldrxhook && wc->debouncehook) {
+ if (!wc->oldrxhook[card] && wc->debouncehook[card]) {
/* Off hook */
if (debug)
- printk("wcfxs: Going off hook\n");
- zt_hooksig(&wc->chan, ZT_RXSIG_OFFHOOK);
- wc->oldrxhook = 1;
+ printk("wcfxs: Card %d Going off hook\n", card);
+ zt_hooksig(&wc->chans[card], ZT_RXSIG_OFFHOOK);
+ wc->oldrxhook[card] = 1;
- } else if (wc->oldrxhook && !wc->debouncehook) {
+ } else if (wc->oldrxhook[card] && !wc->debouncehook[card]) {
/* On hook */
if (debug)
- printk("wcfxs: Going on hook\n");
- zt_hooksig(&wc->chan, ZT_RXSIG_ONHOOK);
- wc->oldrxhook = 0;
+ printk("wcfxs: Card %d Going on hook\n", card);
+ zt_hooksig(&wc->chans[card], ZT_RXSIG_ONHOOK);
+ wc->oldrxhook[card] = 0;
}
}
+static int wcfxs_ioctl(struct zt_chan *chan, unsigned int cmd, unsigned long data)
+{
+ struct wcfxs_stats stats;
+ struct wcfxs_regs regs;
+ struct wcfxs *wc = chan->pvt;
+ int x;
+ switch (cmd) {
+ case WCFXS_GET_STATS:
+ stats.tipvolt = wcfxs_getreg(wc, chan->chanpos - 1, 80) * -376;
+ stats.ringvolt = wcfxs_getreg(wc, chan->chanpos - 1, 81) * -376;
+ stats.batvolt = wcfxs_getreg(wc, chan->chanpos - 1, 82) * -376;
+ if (copy_to_user((struct wcfxs_stats *)data, &stats, sizeof(stats)))
+ return -EFAULT;
+ break;
+ case WCFXS_GET_REGS:
+ for (x=0;x<NUM_INDIRECT_REGS;x++)
+ regs.indirect[x] = wcfxs_getreg_indirect(wc, chan->chanpos -1, x);
+ for (x=0;x<NUM_REGS;x++)
+ regs.direct[x] = wcfxs_getreg(wc, chan->chanpos - 1, x);
+ if (copy_to_user((struct wcfxs_regs *)data, &regs, sizeof(regs)))
+ return -EFAULT;
+ break;
+ default:
+ return -ENOTTY;
+ }
+ return 0;
+
+}
+
static int wcfxs_open(struct zt_chan *chan)
{
struct wcfxs *wc = chan->pvt;
@@ -566,29 +907,35 @@ static int wcfxs_hooksig(struct zt_chan *chan, zt_txsig_t txsig)
if (debug)
printk("Setting hook state to %d (%02x)\n", txsig, reg);
- wcfxs_setreg(wc, 64, txhook);
+#if 1
+ wcfxs_setreg(wc, chan->chanpos - 1, 64, txhook);
+#endif
return 0;
}
static int wcfxs_initialize(struct wcfxs *wc)
{
+ int x;
/* Zapata stuff */
sprintf(wc->span.name, "WCFXS/%d", wc->pos);
- sprintf(wc->span.desc, "%s Board %d\n", wc->variety, wc->pos + 1);
+ sprintf(wc->span.desc, "%s Board %d, %d modules\n", wc->variety, wc->pos + 1, wc->cards);
wc->span.deflaw = ZT_LAW_MULAW;
- sprintf(wc->chan.name, "WCFXS/%d/%d", wc->pos, 0);
- wc->chan.sigcap = ZT_SIG_FXOKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS;
- wc->chan.chanpos = 1;
- wc->span.chans = &wc->chan;
- wc->span.channels = 1;
+ for (x=0;x<wc->cards;x++) {
+ sprintf(wc->chans[x].name, "WCFXS/%d/%d", wc->pos, x);
+ wc->chans[x].sigcap = ZT_SIG_FXOKS | ZT_SIG_FXOLS | ZT_SIG_FXOGS;
+ wc->chans[x].chanpos = x+1;
+ wc->chans[x].pvt = wc;
+ }
+ wc->span.chans = wc->chans;
+ wc->span.channels = wc->cards;
wc->span.hooksig = wcfxs_hooksig;
wc->span.open = wcfxs_open;
wc->span.close = wcfxs_close;
wc->span.flags = ZT_FLAG_RBS;
+ wc->span.ioctl = wcfxs_ioctl;
init_waitqueue_head(&wc->span.maintq);
wc->span.pvt = wc;
- wc->chan.pvt = wc;
if (zt_register(&wc->span, 0)) {
printk("Unable to register span with zaptel\n");
return -1;
@@ -600,6 +947,36 @@ static int wcfxs_hardware_init(struct wcfxs *wc)
{
/* Hardware stuff */
long oldjiffies;
+ unsigned char ver;
+ unsigned char x,y;
+ int failed;
+
+ /* Check Freshmaker chip */
+ ver = wcfxs_getcreg(wc, WC_VER);
+ failed = 0;
+ if (ver != 0x59) {
+ printk("Freshmaker version: %02x\n", ver);
+ for (x=0;x<254;x++) {
+ /* Test registers */
+ wcfxs_setcreg(wc, WC_TEST, x);
+ y = wcfxs_getcreg(wc, WC_TEST);
+ if (x != y) {
+ printk("%02x != %02x\n", x, y);
+ failed++;
+ }
+ }
+ if (!failed) {
+ printk("Freshmaker passed register test\n");
+ } else {
+ printk("Freshmaker failed register test\n");
+ return -1;
+ }
+ /* Go to half-duty FSYNC */
+ wcfxs_setcreg(wc, WC_SYNC, 1);
+ } else {
+ printk("No freshmaker chip\n");
+ }
+
/* Reset PCI Interface chip and registers (and serial) */
outb(0x0e, wc->ioaddr + WC_CNTL);
/* Setup our proper outputs for when we switch for our "serial" port */
@@ -607,12 +984,13 @@ static int wcfxs_hardware_init(struct wcfxs *wc)
outb(wc->ios, wc->ioaddr + WC_AUXD);
- /* Set all to outputs except AUX 5 and 0, which are inputs */
- outb(0xde, wc->ioaddr + WC_AUXC);
+ /* Set all to outputs except AUX 5, which is an input */
+ outb(0xdf, wc->ioaddr + WC_AUXC);
- /* Wait a sec */
+ /* Wait 1/4 of a sec */
oldjiffies = jiffies;
- while(jiffies - oldjiffies < 2);
+ while(jiffies - oldjiffies < (HZ / 4) + 1);
+
/* Back to normal, with automatic DMA wrap around */
outb(0x30 | 0x01, wc->ioaddr + WC_CNTL);
@@ -620,10 +998,7 @@ static int wcfxs_hardware_init(struct wcfxs *wc)
outb(inb(wc->ioaddr + WC_CNTL) & 0xf9, WC_CNTL);
/* Configure serial port for MSB->LSB operation */
- if (wc->flags & FLAG_DOUBLE_CLOCK)
- outb(0xc1, wc->ioaddr + WC_SERCTL);
- else
- outb(0xc0, wc->ioaddr + WC_SERCTL);
+ outb(0xc1, wc->ioaddr + WC_SERCTL);
/* Delay FSC by 0 so it's properly aligned */
outb(0x0, wc->ioaddr + WC_FSCDELAY);
@@ -639,7 +1014,18 @@ static int wcfxs_hardware_init(struct wcfxs *wc)
/* Clear interrupts */
outb(0xff, wc->ioaddr + WC_INTSTAT);
- return wcfxs_init_proslic(wc);
+
+ /* Wait 1/4 of a second more */
+ oldjiffies = jiffies;
+ while(jiffies - oldjiffies < (HZ / 4) + 1);
+
+ for (x=0;x<wc->cards;x++) {
+ if (wcfxs_init_proslic(wc, x)) {
+ printk("Error initializing card %d\n", x);
+ return -1;
+ }
+ }
+ return 0;
}
static void wcfxs_enable_interrupts(struct wcfxs *wc)
@@ -697,6 +1083,9 @@ static int __devinit wcfxs_init_one(struct pci_dev *pdev, const struct pci_devic
if (wc) {
ifaces[x] = wc;
memset(wc, 0, sizeof(struct wcfxs));
+ spin_lock_init(&wc->lock);
+ wc->curcard = -1;
+ wc->cards = 1;
wc->ioaddr = pci_resource_start(pdev, 0);
wc->dev = pdev;
wc->pos = x;
@@ -723,6 +1112,7 @@ static int __devinit wcfxs_init_one(struct pci_dev *pdev, const struct pci_devic
printk("wcfxs: Unable to intialize FXS\n");
if (wc->freeregion)
release_region(wc->ioaddr, 0xff);
+ pci_free_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, (void *)wc->writechunk, wc->writedma);
kfree(wc);
return -EIO;
}
@@ -737,15 +1127,20 @@ static int __devinit wcfxs_init_one(struct pci_dev *pdev, const struct pci_devic
printk("wcfxs: Unable to request IRQ %d\n", pdev->irq);
if (wc->freeregion)
release_region(wc->ioaddr, 0xff);
+ pci_free_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, (void *)wc->writechunk, wc->writedma);
+ pci_set_drvdata(pdev, NULL);
kfree(wc);
return -EIO;
}
if (wcfxs_hardware_init(wc)) {
+ free_irq(pdev->irq, wc);
zt_unregister(&wc->span);
if (wc->freeregion)
release_region(wc->ioaddr, 0xff);
+ pci_free_consistent(pdev, ZT_MAX_CHUNKSIZE * 2 * 2 * 2 * 4, (void *)wc->writechunk, wc->writedma);
+ pci_set_drvdata(pdev, NULL);
kfree(wc);
return -EIO;
}
@@ -757,7 +1152,7 @@ static int __devinit wcfxs_init_one(struct pci_dev *pdev, const struct pci_devic
/* Start DMA */
wcfxs_start_dma(wc);
- printk("Found a Wildcard FXS: %s\n", wc->variety);
+ printk("Found a Wildcard FXS: %s (%d modules)\n", wc->variety, wc->cards);
res = 0;
} else
res = -ENOMEM;
@@ -830,6 +1225,9 @@ static void __exit wcfxs_cleanup(void)
MODULE_PARM(debug, "i");
MODULE_DESCRIPTION("Wildcard S100P Zaptel Driver");
MODULE_AUTHOR("Mark Spencer <markster@linux-support.net>");
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
module_init(wcfxs_init);
module_exit(wcfxs_cleanup);