wcb4xxp: Add support for zarlink echocan

Introduces support for new series b43x cards which make use of the zarlink echo
canceller. Made as few changes to the b410 operation as possible, but the
critical region had some cleanups, so it may be a bit more performant.

Signed-off-by: Russ Meyerriecks <rmeyerriecks@digium.com>
This commit is contained in:
Russ Meyerriecks 2014-12-19 13:50:01 -06:00
parent ae5fa08abd
commit a008cc03b7
2 changed files with 547 additions and 162 deletions

View File

@ -128,7 +128,10 @@ struct devtype {
enum cards_ids card_type; /* Card type - Digium B410P, ... */
};
static struct devtype wcb4xxp = {"Wildcard B410P", .ports = 4, .card_type = B410P };
static struct devtype wcb41xp = {"Wildcard B410P", .ports = 4,
.card_type = B410P};
static struct devtype wcb43xp = {"Wildcard B430P", .ports = 4,
.card_type = B430P};
static struct devtype hfc2s = {"HFC-2S Junghanns.NET duoBRI PCI", .ports = 2, .card_type = DUOBRI };
static struct devtype hfc4s = {"HFC-4S Junghanns.NET quadBRI PCI", .ports = 4, .card_type = QUADBRI };
static struct devtype hfc8s = {"HFC-8S Junghanns.NET octoBRI PCI", .ports = 8, .card_type = OCTOBRI };
@ -142,9 +145,12 @@ static struct devtype hfc4s_SW = {"Swyx 4xS0 SX2 QuadBri", .ports = 4, .card_typ
static struct devtype hfc4s_EV = {"CCD HFC-4S Eval. Board", .ports = 4,
.card_type = QUADBRI_EVAL };
#define CARD_HAS_EC(card) ((card)->card_type == B410P)
#define IS_B430P(card) ((card)->card_type == B430P)
#define IS_B410P(card) ((card)->card_type == B410P)
#define CARD_HAS_EC(card) (IS_B410P(card) || IS_B430P(card))
static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec);
static void b4xxp_update_leds(struct b4xxp *b4);
static const struct dahdi_echocan_features my_ec_features = {
.NLP_automatic = 1,
@ -156,11 +162,6 @@ static const struct dahdi_echocan_ops my_ec_ops = {
.echocan_free = echocan_free,
};
#if 0
static const char *wcb4xxp_rcsdata = "$RCSfile: base.c,v $ $Revision$";
static const char *build_stamp = "" __DATE__ " " __TIME__ "";
#endif
/*
* lowlevel PCI access functions
* These are simply wrappers for the normal PCI access functions that the kernel provides,
@ -410,6 +411,17 @@ static unsigned char b4xxp_getreg_ra(struct b4xxp *b4, unsigned char r, unsigned
return val;
}
/* This gpio register set wrapper protects bit 0 on b430 devices from being
* cleared, as it's used for reset */
static void hfc_gpio_set(struct b4xxp *b4, unsigned char bits)
{
if (IS_B430P(b4)) {
b4xxp_setreg8(b4, R_GPIO_OUT1, bits | 0x01);
flush_pci();
} else {
b4xxp_setreg8(b4, R_GPIO_OUT1, bits);
}
}
/*
* HFC-4S GPIO routines
@ -431,13 +443,18 @@ static void hfc_gpio_init(struct b4xxp *b4)
spin_lock_irqsave(&b4->seqlock, irq_flags);
flush_pci(); /* flush any pending PCI writes */
flush_pci();
mb();
b4xxp_setreg8(b4, R_GPIO_EN0, 0x00); /* GPIO0..7 input */
b4xxp_setreg8(b4, R_GPIO_EN1, 0xf7); /* GPIO8..10,12..15 outputs, GPIO11 input */
b4xxp_setreg8(b4, R_GPIO_OUT1, 0x00); /* disable power, CPLD reg 0 */
/* GPIO0..7 input */
b4xxp_setreg8(b4, R_GPIO_EN0, 0x00);
if (IS_B430P(b4))
b4xxp_setreg8(b4, R_GPIO_EN1, 0xf1);
else
b4xxp_setreg8(b4, R_GPIO_EN1, 0xf7);
hfc_gpio_set(b4, 0x00);
mb();
@ -540,7 +557,7 @@ static inline void writepcibridge(struct b4xxp *b4, unsigned char address, unsig
/* CPLD access code, more or less copied verbatim from code provided by mattf. */
static inline void cpld_select_reg(struct b4xxp *b4, unsigned char reg)
{
b4xxp_setreg8(b4, R_GPIO_OUT1, reg);
hfc_gpio_set(b4, reg);
flush_pci();
}
@ -590,6 +607,76 @@ static inline unsigned short ec_read_data(struct b4xxp *b4)
return addr & 0x1ff;
}
static unsigned char hfc_sram_read(struct b4xxp *b4)
{
unsigned char data;
enablepcibridge(b4);
data = readpcibridge(b4, 1);
disablepcibridge(b4);
cpld_select_reg(b4, 0);
return data;
}
static void hfc_sram_write(struct b4xxp *b4, char data)
{
enablepcibridge(b4);
writepcibridge(b4, 1, data);
cpld_select_reg(b4, 0);
disablepcibridge(b4);
}
static void set_dsp_address(struct b4xxp *b4, unsigned short addr, bool dir)
{
hfc_gpio_set(b4, 0x30);
hfc_sram_write(b4, 0xff & addr);
hfc_gpio_set(b4, 0x40);
/* dir = 1 write */
if (dir)
hfc_sram_write(b4, 0x03 & (addr >> 8));
else
hfc_sram_write(b4, 0x80 | (0x03 & (addr >> 8)));
}
/* Read from zarlink echocan without locks */
static unsigned char __zl_read(struct b4xxp *b4, unsigned short addr)
{
unsigned char data;
set_dsp_address(b4, addr, 0);
hfc_gpio_set(b4, ((addr & 0x0400) >> 3) | 0x50);
data = hfc_sram_read(b4);
hfc_gpio_set(b4, 0x00);
return data;
}
/* Write to zarlink echocan unlocked */
static void __zl_write(struct b4xxp *b4, unsigned short addr,
unsigned char data)
{
unsigned char val;
hfc_gpio_set(b4, 0x00);
set_dsp_address(b4, addr, 1);
hfc_sram_write(b4, data);
val = ((addr & 0x0400) >> 3);
val = (val | 0x50);
hfc_gpio_set(b4, val);
hfc_gpio_set(b4, 0x00);
}
/* Write to zarlink echocan locked */
static void zl_write(struct b4xxp *b4, unsigned short addr, unsigned char data)
{
unsigned long flags;
spin_lock_irqsave(&b4->seqlock, flags);
__zl_write(b4, addr, data);
spin_unlock_irqrestore(&b4->seqlock, flags);
}
static inline unsigned char ec_read(struct b4xxp *b4, int which, unsigned short addr)
{
unsigned char data;
@ -648,19 +735,95 @@ static inline void ec_write(struct b4xxp *b4, int which, unsigned short addr, un
#define NUM_EC 2
#define MAX_TDM_CHAN 32
#if 0
void ec_set_dtmf_threshold(struct b4xxp *b4, int threshold)
static void zl_init(struct b4xxp *b4)
{
unsigned int x;
int i, offset;
int group_addr[4] = {0x00, 0x40, 0x80, 0xc0};
for (x = 0; x < NUM_EC; x++) {
ec_write(b4, x, 0xC4, (threshold >> 8) & 0xFF);
ec_write(b4, x, 0xC5, (threshold & 0xFF));
dev_info(&b4->pdev->dev, "Initializing Zarlink echocan\n");
/* There are 4 "groups" of echocans with two channels in each */
/* Main group control reg 0-3 */
/* Hardware Reset Sequence */
b4xxp_setreg8(b4, R_GPIO_OUT1, 0x00);
udelay(100);
b4xxp_setreg8(b4, R_GPIO_OUT1, 0x01);
udelay(500);
/* Software reset sequence */
zl_write(b4, 0x400, 0x41);
zl_write(b4, 0x401, 0x01);
zl_write(b4, 0x402, 0x01);
zl_write(b4, 0x403, 0x01);
udelay(250);
zl_write(b4, 0x400, 0x40);
zl_write(b4, 0x401, 0x00);
zl_write(b4, 0x402, 0x00);
zl_write(b4, 0x403, 0x00);
udelay(500);
/* Power up & Configure echo can gruops */
if (!strcasecmp(companding, "alaw")) {
zl_write(b4, 0x400, 0x47);
zl_write(b4, 0x401, 0x07);
zl_write(b4, 0x402, 0x07);
zl_write(b4, 0x403, 0x07);
} else {
zl_write(b4, 0x400, 0x45);
zl_write(b4, 0x401, 0x05);
zl_write(b4, 0x402, 0x05);
zl_write(b4, 0x403, 0x05);
}
printk("VPM: DTMF threshold set to %d\n", threshold);
}
#endif
udelay(250);
for (i = 0; i <= 3; i++) {
int group = group_addr[i];
/* Control reg 1, bank A & B, channel bypass mode */
zl_write(b4, group + 0x00, 0x08);
/* Control reg 1 on bank B must be written twicer as */
/* per the datasheet */
zl_write(b4, group + 0x20, 0x0a);
zl_write(b4, group + 0x20, 0x0a);
/* Two channels of the echocan must be set separately,
one at offset 0x00 and one at offset 0x20 */
for (offset = group; offset <= (group + 0x20); offset += 0x20) {
/* Control reg 2 */
zl_write(b4, offset + 0x01, 0x00);
/* Flat Delay */
zl_write(b4, offset + 0x04, 0x00);
/* Decay Step Size Reg */
zl_write(b4, offset + 0x06, 0x04);
/* Decay Step Number */
zl_write(b4, offset + 0x07, 0x00);
/* Control reg 3 */
zl_write(b4, offset + 0x08, 0xfb);
/* Control reg 4 */
zl_write(b4, offset + 0x09, 0x54);
/* Noise Scaling */
zl_write(b4, offset + 0x0a, 0x16);
/* Noise Control */
zl_write(b4, offset + 0x0b, 0x45);
/* DTDT Reg 1*/
zl_write(b4, offset + 0x14, 0x00);
/* DTDT Reg 2*/
zl_write(b4, offset + 0x15, 0x48);
/* NLPTHR reg 1*/
zl_write(b4, offset + 0x18, 0xe0);
/* NLPTHR reg 2*/
zl_write(b4, offset + 0x19, 0x0c);
/* Step Size, MU reg 1*/
zl_write(b4, offset + 0x1a, 0x00);
/* Step Size, MU reg 2*/
zl_write(b4, offset + 0x1b, 0x40);
/* Gains reg 1*/
zl_write(b4, offset + 0x1c, 0x44);
/* Gains reg 2*/
zl_write(b4, offset + 0x1d, 0x44);
}
}
}
static void ec_init(struct b4xxp *b4)
{
@ -670,6 +833,12 @@ static void ec_init(struct b4xxp *b4)
if (!CARD_HAS_EC(b4))
return;
/* Short circuit to the new zarlink echocan logic */
if (IS_B430P(b4)) {
zl_init(b4);
return;
}
/* Setup GPIO */
for (i=0; i < NUM_EC; i++) {
b = ec_read(b4, i, 0x1a0);
@ -716,12 +885,6 @@ static void ec_init(struct b4xxp *b4)
if (DBG)
dev_info(&b4->pdev->dev, "reg 0x20 is 0x%02x\n", b);
// ec_write(b4, i, 0x20, 0x38);
#if 0
ec_write(b4, i, 0x24, 0x02);
b = ec_read(b4, i, 0x24);
#endif
if (DBG) {
dev_info(&b4->pdev->dev,
"NLP threshold is set to %d (0x%02x)\n", b, b);
@ -748,43 +911,29 @@ static void ec_init(struct b4xxp *b4)
ec_write(b4, i, 0x78 + j, 0x01);
}
}
#if 0
ec_set_dtmf_threshold(b4, 1250);
#endif
}
/* performs a register write and then waits for the HFC "busy" bit to clear */
static void hfc_setreg_waitbusy(struct b4xxp *b4, const unsigned int reg, const unsigned int val)
{
int timeout = 0;
unsigned long start;
const int TIMEOUT = HZ/4; /* 250ms */
/* V_BUSY is not supposed to take longer than 1us */
/* Since this func can be called with interrupts locked
* we should just a regular loop, as jiffies may not update
*/
int TIMEOUT = 0.002 * 3000000;
int x = 0;
start = jiffies;
while (unlikely((b4xxp_getreg8(b4, R_STATUS) & V_BUSY))) {
if (time_after(jiffies, start + TIMEOUT)) {
timeout = 1;
break;
}
};
mb();
b4xxp_setreg8(b4, reg, val);
mb();
start = jiffies;
while (likely((b4xxp_getreg8(b4, R_STATUS) & V_BUSY))) {
if (time_after(jiffies, start + TIMEOUT)) {
timeout = 1;
break;
while (b4xxp_getreg8(b4, R_STATUS) & V_BUSY) {
if (x++ > TIMEOUT) {
if (printk_ratelimit()) {
dev_info(&b4->pdev->dev,
"hfc_setreg_waitbusy(write 0x%02x to 0x%02x) timed out waiting for busy flag to clear!\n",
val, reg);
}
return;
}
};
if (timeout && printk_ratelimit()) {
dev_warn(&b4->pdev->dev,
"hfc_setreg_waitbusy(write 0x%02x to 0x%02x) timed "
"out waiting for busy flag to clear!\n", val, reg);
}
}
@ -880,7 +1029,11 @@ static void hfc_reset(struct b4xxp *b4)
b4xxp_setreg8(b4, R_PCM_MD0, V_PCM_MD | V_PCM_IDX_MD1);
flush_pci();
if (IS_B430P(b4))
b4xxp_setreg8(b4, R_PCM_MD1, V_PLL_ADJ_00 | V_PCM_DR_4096);
else
b4xxp_setreg8(b4, R_PCM_MD1, V_PLL_ADJ_00 | V_PCM_DR_2048);
flush_pci();
/* now wait for R_F0_CNTL to reach at least 2 before continuing */
@ -1019,6 +1172,102 @@ static void hfc_assign_bchan_fifo_ec(struct b4xxp *b4, int port, int bchan)
spin_unlock_irqrestore(&b4->fifolock, irq_flags);
}
static void hfc_assign_fifo_zl(struct b4xxp *b4, int port, int bchan)
{
int fifo, hfc_chan, ts;
unsigned long irq_flags;
static int first = 1;
if (first) {
first = 0;
dev_info(&b4->pdev->dev, "Zarlink echo cancellation enabled.\n");
}
fifo = port * 2;
hfc_chan = port * 4;
ts = port * 8;
if (bchan) {
fifo += 1;
hfc_chan += 1;
ts += 4;
}
/* record the host's FIFO # in the span fifo array */
b4->spans[3-port].fifos[bchan] = fifo;
spin_lock_irqsave(&b4->fifolock, irq_flags);
if (DBG) {
dev_info(&b4->pdev->dev,
"port %d, B channel %d\n\tS/T -> PCM ts %d uses HFC chan %d via FIFO %d\n",
port, bchan, ts + 1, hfc_chan, 16 + fifo);
}
/* S/T RX -> PCM TX FIFO, transparent mode, no IRQ. */
hfc_setreg_waitbusy(b4, R_FIFO, ((16 + fifo) << V_FIFO_NUM_SHIFT));
b4xxp_setreg8(b4, A_CON_HDLC, V_IFF | V_HDLC_TRP | V_DATA_FLOW_110);
b4xxp_setreg8(b4, A_CHANNEL, (hfc_chan << V_CH_FNUM_SHIFT));
b4xxp_setreg8(b4, R_SLOT, (((fifo * 2)+1) << V_SL_NUM_SHIFT));
b4xxp_setreg8(b4,
A_SL_CFG, V_ROUT_TX_STIO1 | (hfc_chan << V_CH_SNUM_SHIFT));
hfc_setreg_waitbusy(b4, A_INC_RES_FIFO, V_RES_FIFO);
if (DBG) {
pr_info("\tPCM ts %d -> host uses HFC chan %d via FIFO %d\n",
ts + 1, 16 + hfc_chan, fifo);
}
/* PCM RX -> Host TX FIFO, transparent mode, enable IRQ. */
hfc_setreg_waitbusy(b4, R_FIFO,
(fifo << V_FIFO_NUM_SHIFT) | V_FIFO_DIR);
b4xxp_setreg8(b4, A_CON_HDLC, V_IFF | V_HDLC_TRP | V_DATA_FLOW_001);
b4xxp_setreg8(b4, A_CHANNEL,
((16 + hfc_chan) << V_CH_FNUM_SHIFT) | V_CH_FDIR);
b4xxp_setreg8(b4, R_SLOT,
(((fifo * 2) + 4) << V_SL_NUM_SHIFT) | V_SL_DIR);
b4xxp_setreg8(b4, A_SL_CFG,
V_ROUT_RX_STIO2 | ((16 + hfc_chan) << V_CH_SNUM_SHIFT) |
V_CH_SDIR);
hfc_setreg_waitbusy(b4, A_INC_RES_FIFO, V_RES_FIFO);
if (DBG) {
pr_info("\thost -> PCM ts %d uses HFC chan %d via FIFO %d\n",
ts, 16 + hfc_chan, fifo);
}
/* Host FIFO -> PCM TX */
hfc_setreg_waitbusy(b4, R_FIFO, (fifo << V_FIFO_NUM_SHIFT));
b4xxp_setreg8(b4, A_CON_HDLC, V_IFF | V_HDLC_TRP | V_DATA_FLOW_001);
b4xxp_setreg8(b4, A_CHANNEL, ((16 + hfc_chan) << V_CH_FNUM_SHIFT));
b4xxp_setreg8(b4, R_SLOT, ((fifo * 2) << V_SL_NUM_SHIFT));
b4xxp_setreg8(b4, A_SL_CFG,
V_ROUT_TX_STIO1 | ((16 + hfc_chan) << V_CH_SNUM_SHIFT));
hfc_setreg_waitbusy(b4, A_INC_RES_FIFO, V_RES_FIFO);
if (DBG) {
pr_info("\tPCM ts %d -> S/T uses HFC chan %d via FIFO %d\n",
ts, hfc_chan, 16 + fifo);
}
/* PCM -> S/T */
hfc_setreg_waitbusy(b4, R_FIFO,
((16 + fifo) << V_FIFO_NUM_SHIFT) | V_FIFO_DIR);
b4xxp_setreg8(b4, A_CON_HDLC, V_IFF | V_HDLC_TRP | V_DATA_FLOW_110);
b4xxp_setreg8(b4, A_CHANNEL, (hfc_chan << V_CH_FNUM_SHIFT) | V_CH_FDIR);
b4xxp_setreg8(b4, R_SLOT, (((fifo*2)+3) << V_SL_NUM_SHIFT) | V_SL_DIR);
b4xxp_setreg8(b4, A_SL_CFG,
V_ROUT_RX_STIO2 | (hfc_chan << V_CH_SNUM_SHIFT) | V_CH_SDIR);
hfc_setreg_waitbusy(b4, A_INC_RES_FIFO, V_RES_FIFO);
if (DBG) {
pr_info("\tPCM ts %d -> S/T uses HFC chan %d via FIFO %d\n",
ts, hfc_chan, 16 + fifo);
}
flush_pci();
spin_unlock_irqrestore(&b4->fifolock, irq_flags);
}
static void hfc_assign_bchan_fifo_noec(struct b4xxp *b4, int port, int bchan)
{
int fifo, hfc_chan, ts;
@ -1096,6 +1345,9 @@ static void hfc_assign_dchan_fifo(struct b4xxp *b4, int port)
hfc_chan = (port * 4) + 2;
/* record the host's FIFO # in the span fifo array */
if (IS_B430P(b4))
b4->spans[3-port].fifos[2] = fifo;
else
b4->spans[port].fifos[2] = fifo;
if (DBG) {
@ -1157,10 +1409,6 @@ static void b4xxp_set_sync_src(struct b4xxp *b4, int port)
{
int b;
#if 0
printk("Setting sync to be port %d\n", (port >= 0) ? port + 1 : port);
#endif
if (port == -1) /* automatic */
b = 0;
else
@ -1329,10 +1577,10 @@ static void hfc_timer_expire(struct b4xxp_span *s, int t_no)
switch(t_no) {
case HFC_T1: /* switch to G4 (pending deact.), resume auto mode */
hfc_force_st_state(b4, s->port, 4, 1);
hfc_force_st_state(b4, s->phy_port, 4, 1);
break;
case HFC_T2: /* switch to G1 (deactivated), resume auto mode */
hfc_force_st_state(b4, s->port, 1, 1);
hfc_force_st_state(b4, s->phy_port, 1, 1);
break;
case HFC_T3: /* switch to F3 (deactivated), resume auto mode */
hfc_stop_st(s);
@ -1403,16 +1651,17 @@ static void hfc_handle_state(struct b4xxp_span *s)
b4 = s->parent;
nt = !s->te_mode;
state = b4xxp_getreg_ra(b4, R_ST_SEL, s->port, A_ST_RD_STA);
state = b4xxp_getreg_ra(b4, R_ST_SEL, s->phy_port, A_ST_RD_STA);
sta = (state & V_ST_STA_MASK);
if (DBG_ST) {
char *x;
x = hfc_decode_st_state(b4, s->port, state, 1);
x = hfc_decode_st_state(b4, s->phy_port, state, 1);
dev_info(&b4->pdev->dev,
"port %d A_ST_RD_STA old=0x%02x now=0x%02x, "
"decoded: %s\n", s->port + 1, s->oldstate, state, x);
"port %d phy_port %d A_ST_RD_STA old=0x%02x now=0x%02x, decoded: %s\n",
s->port + 1, s->phy_port, s->oldstate, state, x);
kfree(x);
}
@ -1512,7 +1761,7 @@ static void hfc_stop_st(struct b4xxp_span *s)
hfc_stop_all_timers(s);
b4xxp_setreg_ra(b4, R_ST_SEL, s->port, A_ST_WR_STA,
b4xxp_setreg_ra(b4, R_ST_SEL, s->phy_port, A_ST_WR_STA,
V_ST_ACT_DEACTIVATE);
}
@ -1529,7 +1778,7 @@ static void hfc_reset_st(struct b4xxp_span *s)
hfc_stop_st(s);
/* force state G0/F0 (reset), then force state 1/2 (deactivated/sensing) */
b4xxp_setreg_ra(b4, R_ST_SEL, s->port, A_ST_WR_STA, V_ST_LD_STA);
b4xxp_setreg_ra(b4, R_ST_SEL, s->phy_port, A_ST_WR_STA, V_ST_LD_STA);
flush_pci(); /* make sure write hit hardware */
s->span.alarms = DAHDI_ALARM_RED;
@ -1547,6 +1796,7 @@ static void hfc_reset_st(struct b4xxp_span *s)
b4xxp_setreg8(b4, A_ST_CLK_DLY, b);
/* set TE/NT mode, enable B and D channels. */
b4xxp_setreg8(b4, A_ST_CTRL0, V_B1_EN | V_B2_EN | (s->te_mode ? 0 : V_ST_MD));
b4xxp_setreg8(b4, A_ST_CTRL1, V_G2_G3_EN | V_E_IGNO);
b4xxp_setreg8(b4, A_ST_CTRL2, V_B1_RX_EN | V_B2_RX_EN);
@ -1562,7 +1812,8 @@ static void hfc_start_st(struct b4xxp_span *s)
{
struct b4xxp *b4 = s->parent;
b4xxp_setreg_ra(b4, R_ST_SEL, s->port, A_ST_WR_STA, V_ST_ACT_ACTIVATE);
b4xxp_setreg_ra(b4, R_ST_SEL, s->phy_port, A_ST_WR_STA,
V_ST_ACT_ACTIVATE);
/* start T1 if in NT mode, T3 if in TE mode */
if (s->te_mode) {
@ -1597,25 +1848,52 @@ static void hfc_start_st(struct b4xxp_span *s)
*/
static void hfc_init_all_st(struct b4xxp *b4)
{
int i, gpio, nt;
int gpio = 0;
int i, nt;
struct b4xxp_span *s;
/* All other cards supported by this driver read jumpers for modes */
if (!IS_B430P(b4))
gpio = b4xxp_getreg8(b4, R_GPI_IN3);
for (i=0; i < b4->numspans; i++) {
s = &b4->spans[i];
s->parent = b4;
if (IS_B430P(b4)) {
/* The physical ports are reversed on the b430 */
/* Port 0-3 in b4->spans[] are physically ports 3-0 */
s->phy_port = b4->numspans-1-i;
s->port = i;
} else {
s->phy_port = i;
s->port = i;
}
/* The way the Digium B410P card reads the NT/TE mode
* jumper is the oposite of how other HFC-4S cards do:
* - In B410P: GPIO=0: NT
* - In Junghanns: GPIO=0: TE
*/
if (b4->card_type == B410P)
if (IS_B410P(b4)) {
nt = ((gpio & (1 << (i + 4))) == 0);
} else if (IS_B430P(b4)) {
/* Read default digital lineconfig reg on B430 */
int reg;
unsigned long flags;
spin_lock_irqsave(&b4->seqlock, flags);
hfc_gpio_set(b4, 0x20);
reg = hfc_sram_read(b4);
spin_unlock_irqrestore(&b4->seqlock, flags);
if (reg & (1 << (4 + s->phy_port)))
nt = 1;
else
nt = 0;
} else {
nt = ((gpio & (1 << (i + 4))) != 0);
}
s->te_mode = !nt;
@ -1694,10 +1972,6 @@ static int hfc_poll_fifos(struct b4xxp *b4)
continue;
/* TODO: Make sure S/T port is in active state */
#if 0
if (span_not_active(s))
continue;
#endif
ret = hfc_poll_one_bchan_fifo(&b4->spans[span], 0);
ret |= hfc_poll_one_bchan_fifo(&b4->spans[span], 1);
}
@ -1990,11 +2264,15 @@ static void b4xxp_init_stage1(struct b4xxp *b4)
* incoming clock.
*/
if ((b4->card_type == B410P) || (b4->card_type == QUADBRI_EVAL))
if (IS_B410P(b4) || IS_B430P(b4) || (b4->card_type == QUADBRI_EVAL))
b4xxp_setreg8(b4, R_BRG_PCM_CFG, 0x02);
else
b4xxp_setreg8(b4, R_BRG_PCM_CFG, V_PCM_CLK);
/* Reset LED state */
b4->ledreg = 0;
b4xxp_update_leds(b4);
flush_pci();
udelay(100); /* wait a bit for clock to settle */
@ -2021,10 +2299,16 @@ static void b4xxp_init_stage2(struct b4xxp *b4)
*/
b4xxp_setreg8(b4, R_PCM_MD0, V_PCM_MD | V_PCM_IDX_MD1);
flush_pci();
b4xxp_setreg8(b4, R_PCM_MD1, V_PLL_ADJ_00 | V_PCM_DR_2048);
if (IS_B430P(b4)) {
b4xxp_setreg8(b4, R_PCM_MD1, V_PLL_ADJ_00 | V_PCM_DR_4096);
b4xxp_setreg8(b4, R_PWM_MD, 0x50);
b4xxp_setreg8(b4, R_PWM0, 0x38);
} else {
b4xxp_setreg8(b4, R_PCM_MD1, V_PLL_ADJ_00 | V_PCM_DR_2048);
b4xxp_setreg8(b4, R_PWM_MD, 0xa0);
b4xxp_setreg8(b4, R_PWM0, 0x1b);
}
/*
* set up the flow controller.
@ -2071,6 +2355,11 @@ static void b4xxp_init_stage2(struct b4xxp *b4)
* D channel FIFOs are operated in HDLC mode and interrupt on end of frame.
*/
for (span=0; span < b4->numspans; span++) {
if (IS_B430P(b4)) {
hfc_assign_fifo_zl(b4, span, 0);
hfc_assign_fifo_zl(b4, span, 1);
hfc_assign_dchan_fifo(b4, span);
} else {
if ((vpmsupport) && (CARD_HAS_EC(b4))) {
hfc_assign_bchan_fifo_ec(b4, span, 0);
hfc_assign_bchan_fifo_ec(b4, span, 1);
@ -2080,6 +2369,7 @@ static void b4xxp_init_stage2(struct b4xxp *b4)
}
hfc_assign_dchan_fifo(b4, span);
}
}
/* set up the timer interrupt for 1ms intervals */
b4xxp_setreg8(b4, R_TI_WD, (2 << V_EV_TS_SHIFT));
@ -2163,7 +2453,7 @@ static void b4xxp_update_leds_hfc(struct b4xxp *b4)
For green: in both R_GPIO_EN1 and R_GPIO_OUT1. */
leds |= green_leds;
b4xxp_setreg8(b4, R_GPIO_EN1, leds);
b4xxp_setreg8(b4, R_GPIO_OUT1, green_leds);
hfc_gpio_set(b4, green_leds);
if (b4->blinktimer == 0xff)
b4->blinktimer = -1;
@ -2171,6 +2461,18 @@ static void b4xxp_update_leds_hfc(struct b4xxp *b4)
static void b4xxp_set_span_led(struct b4xxp *b4, int span, unsigned char val)
{
if (IS_B430P(b4)) {
unsigned long flags;
b4->ledreg &= ~(0x03 << span*2);
b4->ledreg |= (val << span*2);
spin_lock_irqsave(&b4->seqlock, flags);
/* Set multiplexer for led R/W */
hfc_gpio_set(b4, 0x10);
hfc_sram_write(b4, b4->ledreg);
spin_unlock_irqrestore(&b4->seqlock, flags);
} else {
int shift, spanmask;
shift = span << 1;
@ -2179,6 +2481,7 @@ static void b4xxp_set_span_led(struct b4xxp *b4, int span, unsigned char val)
b4->ledreg &= spanmask;
b4->ledreg |= (val << shift);
b4xxp_setleds(b4, b4->ledreg);
}
}
static void b4xxp_update_leds(struct b4xxp *b4)
@ -2192,7 +2495,7 @@ static void b4xxp_update_leds(struct b4xxp *b4)
return;
}
if (b4->card_type != B410P) {
if (!IS_B410P(b4) && !IS_B430P(b4)) {
/* Use the alternative function for non-Digium HFC-4S cards */
b4xxp_update_leds_hfc(b4);
return;
@ -2233,8 +2536,15 @@ static const char *b4xxp_echocan_name(const struct dahdi_chan *chan)
{
struct b4xxp_span *bspan = container_of(chan->span, struct b4xxp_span,
span);
if (vpmsupport && (B410P == bspan->parent->card_type))
if (!vpmsupport)
return NULL;
if (IS_B410P(bspan->parent))
return "LASVEGAS2";
if (IS_B430P(bspan->parent))
return "ZARLINK";
return NULL;
}
@ -2268,7 +2578,22 @@ static int b4xxp_echocan_create(struct dahdi_chan *chan,
channel = (chan->span->offset * 8) + ((chan->chanpos - 1) * 4) + 1;
if (IS_B430P(bspan->parent)) {
/* Zarlink has 4 groups of 2 channel echo cancelers */
/* Each channel has it's own individual control reg */
int group = (3 - chan->span->offset) * 0x40;
int chan_offset = (chan->chanpos % 2) ? 0x00 : 0x20;
int reg;
unsigned long flags;
spin_lock_irqsave(&bspan->parent->seqlock, flags);
reg = __zl_read(bspan->parent, group + chan_offset);
reg &= ~(1 << 3);
__zl_write(bspan->parent, group + chan_offset, reg);
spin_unlock_irqrestore(&bspan->parent->seqlock, flags);
} else {
ec_write(bspan->parent, chan->chanpos - 1, channel, 0x7e);
}
return 0;
}
@ -2285,7 +2610,22 @@ static void echocan_free(struct dahdi_chan *chan, struct dahdi_echocan_state *ec
channel = (chan->span->offset * 8) + ((chan->chanpos - 1) * 4) + 1;
if (IS_B430P(bspan->parent)) {
/* Zarlink has 4 groups of 2 channel echo cancelers */
/* Each channel has it's own individual control reg */
int group = (3 - chan->span->offset) * 0x40;
int chan_offset = (chan->chanpos % 2) ? 0x00 : 0x20;
int reg;
unsigned long flags;
spin_lock_irqsave(&bspan->parent->seqlock, flags);
reg = __zl_read(bspan->parent, group + chan_offset);
reg |= (1 << 3);
__zl_write(bspan->parent, group + chan_offset, reg);
spin_unlock_irqrestore(&bspan->parent->seqlock, flags);
} else {
ec_write(bspan->parent, chan->chanpos - 1, channel, 0x01);
}
}
/*
@ -2344,14 +2684,6 @@ static int b4xxp_spanconfig(struct file *file, struct dahdi_span *span,
if (DBG)
dev_info(&b4->pdev->dev, "Configuring span %d offset %d to be sync %d\n", span->spanno, span->offset, lc->sync);
#if 0
if (lc->sync > 0 && !bspan->te_mode) {
dev_info(&b4->pdev->dev, "Span %d is not in NT mode, removing "
"from sync source list\n", span->spanno);
lc->sync = 0;
}
#endif
if (lc->sync < 0 || lc->sync > 4) {
dev_info(&b4->pdev->dev,
"Span %d has invalid sync priority (%d), removing "
@ -2369,6 +2701,54 @@ static int b4xxp_spanconfig(struct file *file, struct dahdi_span *span,
if (lc->sync)
b4->spans[lc->sync - 1].sync = (span->offset + 1);
/* B430 sets TE/NT and Termination resistance modes via dahdi_cfg */
if (IS_B430P(b4)) {
int te_mode, term, reg;
unsigned long flags;
te_mode = (lc->lineconfig & DAHDI_CONFIG_NTTE) ? 0 : 1;
term = (lc->lineconfig & DAHDI_CONFIG_TERM) ? 1 : 0;
dev_info(&b4->pdev->dev,
"Configuring span %d in %s mode with termination resistance %s\n",
bspan->port+1, (te_mode) ? "TE" : "NT",
(term) ? "ENABLED" : "DISABLED");
if (!te_mode && lc->sync) {
dev_info(&b4->pdev->dev,
"NT Spans cannot be timing sources. Changing priority to 0\n");
lc->sync = 0;
}
/* Setup NT/TE */
/* Bits 7 downto 5 correspond to spans 4-1 */
/* 1 sets NT mode, 0 sets TE mode */
spin_lock_irqsave(&b4->seqlock, flags);
hfc_gpio_set(b4, 0x20);
reg = hfc_sram_read(b4);
if (te_mode)
reg &= ~(1 << (4 + bspan->port));
else
reg |= (1 << (4 + bspan->port));
/* Setup Termination resistance */
/* Bits 4 downto 0 correspond to spans 4-1 */
/* 1 sets resistance mode, 0 sets no resistance */
if (term)
reg |= (1 << (bspan->port));
else
reg &= ~(1 << (bspan->port));
hfc_gpio_set(b4, 0x20);
hfc_sram_write(b4, reg);
spin_unlock_irqrestore(&b4->seqlock, flags);
bspan->te_mode = te_mode;
bspan->span.spantype = (bspan->te_mode)
? SPANTYPE_DIGITAL_BRI_TE
: SPANTYPE_DIGITAL_BRI_NT;
}
hfc_reset_st(bspan);
if (persistentlayer1)
hfc_start_st(bspan);
@ -2532,7 +2912,8 @@ static void init_spans(struct b4xxp *b4)
bspan->span.linecompat = DAHDI_CONFIG_AMI |
DAHDI_CONFIG_B8ZS | DAHDI_CONFIG_D4 |
DAHDI_CONFIG_ESF | DAHDI_CONFIG_HDB3 |
DAHDI_CONFIG_CCS | DAHDI_CONFIG_CRC4;
DAHDI_CONFIG_CCS | DAHDI_CONFIG_CRC4 |
DAHDI_CONFIG_NTTE | DAHDI_CONFIG_TERM;
sprintf(bspan->span.name, "B4/%d/%d", b4->cardno, i+1);
sprintf(bspan->span.desc, "B4XXP (PCI) Card %d Span %d", b4->cardno, i+1);
@ -2673,6 +3054,9 @@ static void b4xxp_bottom_half(unsigned long data)
* Yuck. It works well, but yuck.
*/
do {
if (IS_B430P(b4))
k = hdlc_tx_frame(&b4->spans[3-(fifo - fifo_low)]);
else
k = hdlc_tx_frame(&b4->spans[fifo - fifo_low]);
} while (k);
} else {
@ -2690,6 +3074,9 @@ static void b4xxp_bottom_half(unsigned long data)
* i.e. I get an int when F1 changes, not when F1 != F2.
*/
do {
if (IS_B430P(b4))
k = hdlc_rx_frame(&b4->spans[3-(fifo - fifo_low)]);
else
k = hdlc_rx_frame(&b4->spans[fifo - fifo_low]);
} while (k);
} else {
@ -2722,16 +3109,21 @@ static void b4xxp_bottom_half(unsigned long data)
b4xxp_update_leds(b4);
/* every 100ms or so, look at the S/T interfaces to see if they changed state */
/* Poll interface state at 100ms interval */
if (!(b4->ticks % 100)) {
b = b4xxp_getreg8(b4, R_SCI);
if (b) {
for (i=0; i < b4->numspans; i++) {
if (b & (1 << i))
if (b & (1 << i)) {
/* physical spans are reversed for b430 */
if (IS_B430P(b4))
hfc_handle_state(&b4->spans[b4->numspans-1-i]);
else
hfc_handle_state(&b4->spans[i]);
}
}
}
}
/* We're supposed to kick DAHDI here, too, but again, seeing too much latency between the interrupt and the bottom-half. */
@ -2919,7 +3311,6 @@ static int __devinit b4xx_probe(struct pci_dev *pdev, const struct pci_device_id
use_flag_somehow();
*/
/* TODO: determine whether this is a 2, 4 or 8 port card */
b4->numspans = dt->ports;
b4->syncspan = -1; /* sync span is unknown */
if (b4->numspans > MAX_SPANS_PER_CARD) {
@ -2949,6 +3340,18 @@ static int __devinit b4xx_probe(struct pci_dev *pdev, const struct pci_device_id
b4xxp_init_stage1(b4);
if (IS_B430P(b4)) {
int version;
unsigned long flags;
spin_lock_irqsave(&b4->seqlock, flags);
hfc_gpio_set(b4, 0x60);
version = hfc_sram_read(b4);
spin_unlock_irqrestore(&b4->seqlock, flags);
dev_info(&b4->pdev->dev, "CPLD ver: %x\n", version);
}
create_sysfs_files(b4);
if (request_irq(pdev->irq, b4xxp_interrupt, DAHDI_IRQ_SHARED_DISABLED, "b4xxp", b4)) {
@ -2990,29 +3393,7 @@ static int __devinit b4xx_probe(struct pci_dev *pdev, const struct pci_device_id
goto err_out_unreg_spans;
}
#if 0
/* Launch cards as appropriate */
for (;;) {
/* Find a card to activate */
f = 0;
for (x=0; cards[x]; x++) {
if (cards[x]->order <= highestorder) {
b4_launch(cards[x]);
if (cards[x]->order == highestorder)
f = 1;
}
}
/* If we found at least one, increment the highest order and search again, otherwise stop */
if (f)
highestorder++;
else
break;
}
#else
dev_info(&b4->pdev->dev, "Did not do the highestorder stuff\n");
#endif
ret = b4xxp_startdefaultspan(b4);
if (ret)
@ -3090,25 +3471,27 @@ static void __devexit b4xxp_remove(struct pci_dev *pdev)
static DEFINE_PCI_DEVICE_TABLE(b4xx_ids) =
{
{ 0xd161, 0xb410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&wcb4xxp },
{ 0x1397, 0x16b8, 0x1397, 0xb552, 0, 0, (unsigned long)&hfc8s },
{ 0x1397, 0x16b8, 0x1397, 0xb55b, 0, 0, (unsigned long)&hfc8s },
{ 0x1397, 0x08b4, 0x1397, 0xb520, 0, 0, (unsigned long)&hfc4s },
{ 0x1397, 0x08b4, 0x1397, 0xb550, 0, 0, (unsigned long)&hfc4s },
{ 0x1397, 0x08b4, 0x1397, 0xb752, 0, 0, (unsigned long)&hfc4s },
{ 0x1397, 0x08b4, 0x1397, 0xb556, 0, 0, (unsigned long)&hfc2s },
{ 0x1397, 0x08b4, 0x1397, 0xe884, 0, 0, (unsigned long)&hfc2s_OV },
{ 0x1397, 0x08b4, 0x1397, 0xe888, 0, 0, (unsigned long)&hfc4s_OV },
{ 0x1397, 0x16b8, 0x1397, 0xe998, 0, 0, (unsigned long)&hfc8s_OV },
{ 0x1397, 0x08b4, 0x1397, 0xb566, 0, 0, (unsigned long)&hfc2s_BN },
{ 0x1397, 0x08b4, 0x1397, 0xb761, 0, 0, (unsigned long)&hfc2s_BN },
{ 0x1397, 0x08b4, 0x1397, 0xb560, 0, 0, (unsigned long)&hfc4s_BN },
{ 0x1397, 0x08b4, 0x1397, 0xb550, 0, 0, (unsigned long)&hfc4s_BN },
{ 0x1397, 0x08b4, 0x1397, 0xb762, 0, 0, (unsigned long)&hfc4s_BN },
{ 0x1397, 0x16b8, 0x1397, 0xb562, 0, 0, (unsigned long)&hfc8s_BN },
{ 0x1397, 0x16b8, 0x1397, 0xb56b, 0, 0, (unsigned long)&hfc8s_BN },
{ 0x1397, 0x08b4, 0x1397, 0xb540, 0, 0, (unsigned long)&hfc4s_SW },
{ 0x1397, 0x08b4, 0x1397, 0x08b4, 0, 0, (unsigned long)&hfc4s_EV },
{0xd161, 0xb410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&wcb41xp},
{0xd161, 0x8014, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&wcb43xp},
{0xd161, 0x8015, PCI_ANY_ID, PCI_ANY_ID, 0, 0, (unsigned long)&wcb43xp},
{0x1397, 0x16b8, 0x1397, 0xb552, 0, 0, (unsigned long)&hfc8s},
{0x1397, 0x16b8, 0x1397, 0xb55b, 0, 0, (unsigned long)&hfc8s},
{0x1397, 0x08b4, 0x1397, 0xb520, 0, 0, (unsigned long)&hfc4s},
{0x1397, 0x08b4, 0x1397, 0xb550, 0, 0, (unsigned long)&hfc4s},
{0x1397, 0x08b4, 0x1397, 0xb752, 0, 0, (unsigned long)&hfc4s},
{0x1397, 0x08b4, 0x1397, 0xb556, 0, 0, (unsigned long)&hfc2s},
{0x1397, 0x08b4, 0x1397, 0xe884, 0, 0, (unsigned long)&hfc2s_OV},
{0x1397, 0x08b4, 0x1397, 0xe888, 0, 0, (unsigned long)&hfc4s_OV},
{0x1397, 0x16b8, 0x1397, 0xe998, 0, 0, (unsigned long)&hfc8s_OV},
{0x1397, 0x08b4, 0x1397, 0xb566, 0, 0, (unsigned long)&hfc2s_BN},
{0x1397, 0x08b4, 0x1397, 0xb761, 0, 0, (unsigned long)&hfc2s_BN},
{0x1397, 0x08b4, 0x1397, 0xb560, 0, 0, (unsigned long)&hfc4s_BN},
{0x1397, 0x08b4, 0x1397, 0xb550, 0, 0, (unsigned long)&hfc4s_BN},
{0x1397, 0x08b4, 0x1397, 0xb762, 0, 0, (unsigned long)&hfc4s_BN},
{0x1397, 0x16b8, 0x1397, 0xb562, 0, 0, (unsigned long)&hfc8s_BN},
{0x1397, 0x16b8, 0x1397, 0xb56b, 0, 0, (unsigned long)&hfc8s_BN},
{0x1397, 0x08b4, 0x1397, 0xb540, 0, 0, (unsigned long)&hfc4s_SW},
{0x1397, 0x08b4, 0x1397, 0x08b4, 0, 0, (unsigned long)&hfc4s_EV},
{0, }
};

View File

@ -248,8 +248,8 @@
#define V_ROUT_TX_STIO2 (0x3 << 6) /* output data to STIO2 */
#define V_ROUT_RX_DIS (0x0 << 6) /* disabled, input data ignored */
#define V_ROUT_RX_LOOP (0x1 << 6) /* internally looped, input data ignored */
#define V_ROUT_RX_STIO2 (0x2 << 6) /* channel data comes from STIO1 */
#define V_ROUT_RX_STIO1 (0x3 << 6) /* channel data comes from STIO2 */
#define V_ROUT_RX_STIO2 (0x2 << 6) /* channel data from STIO2 */
#define V_ROUT_RX_STIO1 (0x3 << 6) /* channel data from STIO1 */
#define V_CH_SNUM_SHIFT (1)
#define V_CH_SNUM_MASK (31 << 1)
@ -385,7 +385,8 @@
struct b4xxp_span {
struct b4xxp *parent;
int port; /* which S/T port this span belongs to */
int port; /* virtual port */
int phy_port; /* physical port */
unsigned char writechunk[WCB4XXP_CHANNELS_PER_SPAN * DAHDI_CHUNKSIZE];
unsigned char readchunk[WCB4XXP_CHANNELS_PER_SPAN * DAHDI_CHUNKSIZE];
@ -426,7 +427,8 @@ enum cards_ids { /* Cards ==> Brand & Model */
BN4S0, /* Beronet BN4S0 */
BN8S0, /* BeroNet BN8S0 */
BSWYX_SX2, /* Swyx 4xS0 SX2 QuadBri */
QUADBRI_EVAL /* HFC-4S CCD Eval. Board */
QUADBRI_EVAL, /* HFC-4S CCD Eval. Board */
B430P /* Digium B430P */
};
/* This structure exists one per card */