diff options
Diffstat (limited to 'sys/i4b/layer1/isic/i4b_hscx.c')
-rw-r--r-- | sys/i4b/layer1/isic/i4b_hscx.c | 667 |
1 files changed, 667 insertions, 0 deletions
diff --git a/sys/i4b/layer1/isic/i4b_hscx.c b/sys/i4b/layer1/isic/i4b_hscx.c new file mode 100644 index 0000000..7731cb2 --- /dev/null +++ b/sys/i4b/layer1/isic/i4b_hscx.c @@ -0,0 +1,667 @@ +/* + * Copyright (c) 1997, 2000 Hellmuth Michaelis. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + *--------------------------------------------------------------------------- + * + * i4b - Siemens HSCX chip (B-channel) handling + * -------------------------------------------- + * + * $Id: i4b_hscx.c,v 1.7 2000/05/29 15:41:41 hm Exp $ + * + * $FreeBSD$ + * + * last edit-date: [Mon May 29 16:44:50 2000] + * + *---------------------------------------------------------------------------*/ + +#include "isic.h" + +#if NISIC > 0 + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/mbuf.h> +#include <sys/socket.h> + +#include <machine/stdarg.h> +#include <machine/clock.h> + +#include <net/if.h> + +#include <machine/i4b_debug.h> +#include <machine/i4b_ioctl.h> +#include <machine/i4b_trace.h> + +#include <i4b/layer1/isic/i4b_isic.h> +#include <i4b/layer1/isic/i4b_isac.h> +#include <i4b/layer1/isic/i4b_hscx.h> + +#include <i4b/layer1/i4b_l1.h> + +#include <i4b/include/i4b_global.h> +#include <i4b/include/i4b_mbuf.h> + +/*---------------------------------------------------------------------------* + * HSCX IRQ Handler + *---------------------------------------------------------------------------*/ +void +isic_hscx_irq(register struct l1_softc *sc, u_char ista, int h_chan, u_char ex_irq) +{ + register l1_bchan_state_t *chan = &sc->sc_chan[h_chan]; + u_char exir = 0; + int activity = -1; + u_char cmd = 0; + + NDBGL1(L1_H_IRQ, "%#x", ista); + + if(ex_irq) + { + /* get channel extended irq reg */ + + exir = HSCX_READ(h_chan, H_EXIR); + + if(exir & HSCX_EXIR_RFO) + { + chan->stat_RFO++; + NDBGL1(L1_H_XFRERR, "ex_irq: receive data overflow"); + } + + if((exir & HSCX_EXIR_XDU) && (chan->bprot != BPROT_NONE))/* xmit data underrun */ + { + chan->stat_XDU++; + NDBGL1(L1_H_XFRERR, "ex_irq: xmit data underrun"); + isic_hscx_cmd(sc, h_chan, HSCX_CMDR_XRES); + + if (chan->out_mbuf_head != NULL) /* don't continue to transmit this buffer */ + { + i4b_Bfreembuf(chan->out_mbuf_head); + chan->out_mbuf_cur = chan->out_mbuf_head = NULL; + } + } + + } + + /* rx message end, end of frame */ + + if(ista & HSCX_ISTA_RME) + { + register int fifo_data_len; + u_char rsta; + int error = 0; + + rsta = HSCX_READ(h_chan, H_RSTA); + + if((rsta & 0xf0) != 0xa0) + { + if((rsta & HSCX_RSTA_VFR) == 0) + { + chan->stat_VFR++; + cmd |= (HSCX_CMDR_RHR); + NDBGL1(L1_H_XFRERR, "received invalid Frame"); + error++; + } + + if(rsta & HSCX_RSTA_RDO) + { + chan->stat_RDO++; + NDBGL1(L1_H_XFRERR, "receive data overflow"); + error++; + } + + if((rsta & HSCX_RSTA_CRC) == 0) + { + chan->stat_CRC++; + cmd |= (HSCX_CMDR_RHR); + NDBGL1(L1_H_XFRERR, "CRC check failed"); + error++; + } + + if(rsta & HSCX_RSTA_RAB) + { + chan->stat_RAB++; + NDBGL1(L1_H_XFRERR, "Receive message aborted"); + error++; + } + } + + fifo_data_len = ((HSCX_READ(h_chan, H_RBCL)) & + ((sc->sc_bfifolen)-1)); + + if(fifo_data_len == 0) + fifo_data_len = sc->sc_bfifolen; + + /* all error conditions checked, now decide and take action */ + + if(error == 0) + { + if(chan->in_mbuf == NULL) + { + if((chan->in_mbuf = i4b_Bgetmbuf(BCH_MAX_DATALEN)) == NULL) + panic("L1 isic_hscx_irq: RME, cannot allocate mbuf!\n"); + chan->in_cbptr = chan->in_mbuf->m_data; + chan->in_len = 0; + } + + fifo_data_len -= 1; /* last byte in fifo is RSTA ! */ + + if((chan->in_len + fifo_data_len) <= BCH_MAX_DATALEN) + { + /* read data from HSCX fifo */ + + HSCX_RDFIFO(h_chan, chan->in_cbptr, fifo_data_len); + + cmd |= (HSCX_CMDR_RMC); + isic_hscx_cmd(sc, h_chan, cmd); + cmd = 0; + + chan->in_len += fifo_data_len; + chan->rxcount += fifo_data_len; + + /* setup mbuf data length */ + + chan->in_mbuf->m_len = chan->in_len; + chan->in_mbuf->m_pkthdr.len = chan->in_len; + + if(sc->sc_trace & TRACE_B_RX) + { + i4b_trace_hdr_t hdr; + hdr.unit = L0ISICUNIT(sc->sc_unit); + hdr.type = (h_chan == HSCX_CH_A ? TRC_CH_B1 : TRC_CH_B2); + hdr.dir = FROM_NT; + hdr.count = ++sc->sc_trace_bcount; + MICROTIME(hdr.time); + i4b_l1_trace_ind(&hdr, chan->in_mbuf->m_len, chan->in_mbuf->m_data); + } + + (*chan->isic_drvr_linktab->bch_rx_data_ready)(chan->isic_drvr_linktab->unit); + + activity = ACT_RX; + + /* mark buffer ptr as unused */ + + chan->in_mbuf = NULL; + chan->in_cbptr = NULL; + chan->in_len = 0; + } + else + { + NDBGL1(L1_H_XFRERR, "RAWHDLC rx buffer overflow in RME, in_len=%d, fifolen=%d", chan->in_len, fifo_data_len); + chan->in_cbptr = chan->in_mbuf->m_data; + chan->in_len = 0; + cmd |= (HSCX_CMDR_RHR | HSCX_CMDR_RMC); + } + } + else + { + if (chan->in_mbuf != NULL) + { + i4b_Bfreembuf(chan->in_mbuf); + chan->in_mbuf = NULL; + chan->in_cbptr = NULL; + chan->in_len = 0; + } + cmd |= (HSCX_CMDR_RMC); + } + } + + /* rx fifo full */ + + if(ista & HSCX_ISTA_RPF) + { + if(chan->in_mbuf == NULL) + { + if((chan->in_mbuf = i4b_Bgetmbuf(BCH_MAX_DATALEN)) == NULL) + panic("L1 isic_hscx_irq: RPF, cannot allocate mbuf!\n"); + chan->in_cbptr = chan->in_mbuf->m_data; + chan->in_len = 0; + } + + chan->rxcount += sc->sc_bfifolen; + + if((chan->in_len + sc->sc_bfifolen) <= BCH_MAX_DATALEN) + { + /* read data from HSCX fifo */ + + HSCX_RDFIFO(h_chan, chan->in_cbptr, sc->sc_bfifolen); + + chan->in_cbptr += sc->sc_bfifolen; + chan->in_len += sc->sc_bfifolen; + } + else + { + if(chan->bprot == BPROT_NONE) + { + /* setup mbuf data length */ + + chan->in_mbuf->m_len = chan->in_len; + chan->in_mbuf->m_pkthdr.len = chan->in_len; + + if(sc->sc_trace & TRACE_B_RX) + { + i4b_trace_hdr_t hdr; + hdr.unit = L0ISICUNIT(sc->sc_unit); + hdr.type = (h_chan == HSCX_CH_A ? TRC_CH_B1 : TRC_CH_B2); + hdr.dir = FROM_NT; + hdr.count = ++sc->sc_trace_bcount; + MICROTIME(hdr.time); + i4b_l1_trace_ind(&hdr, chan->in_mbuf->m_len, chan->in_mbuf->m_data); + } + + /* silence detection */ + + if(!(i4b_l1_bchan_tel_silence(chan->in_mbuf->m_data, chan->in_mbuf->m_len))) + activity = ACT_RX; + + if(!(IF_QFULL(&chan->rx_queue))) + { + IF_ENQUEUE(&chan->rx_queue, chan->in_mbuf); + } + else + { + i4b_Bfreembuf(chan->in_mbuf); + } + + /* signal upper driver that data is available */ + + (*chan->isic_drvr_linktab->bch_rx_data_ready)(chan->isic_drvr_linktab->unit); + + /* alloc new buffer */ + + if((chan->in_mbuf = i4b_Bgetmbuf(BCH_MAX_DATALEN)) == NULL) + panic("L1 isic_hscx_irq: RPF, cannot allocate new mbuf!\n"); + + /* setup new data ptr */ + + chan->in_cbptr = chan->in_mbuf->m_data; + + /* read data from HSCX fifo */ + + HSCX_RDFIFO(h_chan, chan->in_cbptr, sc->sc_bfifolen); + + chan->in_cbptr += sc->sc_bfifolen; + chan->in_len = sc->sc_bfifolen; + + chan->rxcount += sc->sc_bfifolen; + } + else + { + NDBGL1(L1_H_XFRERR, "RAWHDLC rx buffer overflow in RPF, in_len=%d", chan->in_len); + chan->in_cbptr = chan->in_mbuf->m_data; + chan->in_len = 0; + cmd |= (HSCX_CMDR_RHR); + } + } + + /* command to release fifo space */ + + cmd |= HSCX_CMDR_RMC; + } + + /* transmit fifo empty, new data can be written to fifo */ + + if(ista & HSCX_ISTA_XPR) + { + /* + * for a description what is going on here, please have + * a look at isic_bchannel_start() in i4b_bchan.c ! + */ + + int activity = -1; + int len; + int nextlen; + + NDBGL1(L1_H_IRQ, "unit %d, chan %d - XPR, Tx Fifo Empty!", sc->sc_unit, h_chan); + + if(chan->out_mbuf_cur == NULL) /* last frame is transmitted */ + { + IF_DEQUEUE(&chan->tx_queue, chan->out_mbuf_head); + + if(chan->out_mbuf_head == NULL) + { + chan->state &= ~HSCX_TX_ACTIVE; + (*chan->isic_drvr_linktab->bch_tx_queue_empty)(chan->isic_drvr_linktab->unit); + } + else + { + chan->state |= HSCX_TX_ACTIVE; + chan->out_mbuf_cur = chan->out_mbuf_head; + chan->out_mbuf_cur_ptr = chan->out_mbuf_cur->m_data; + chan->out_mbuf_cur_len = chan->out_mbuf_cur->m_len; + + if(sc->sc_trace & TRACE_B_TX) + { + i4b_trace_hdr_t hdr; + hdr.unit = L0ISICUNIT(sc->sc_unit); + hdr.type = (h_chan == HSCX_CH_A ? TRC_CH_B1 : TRC_CH_B2); + hdr.dir = FROM_TE; + hdr.count = ++sc->sc_trace_bcount; + MICROTIME(hdr.time); + i4b_l1_trace_ind(&hdr, chan->out_mbuf_cur->m_len, chan->out_mbuf_cur->m_data); + } + + if(chan->bprot == BPROT_NONE) + { + if(!(i4b_l1_bchan_tel_silence(chan->out_mbuf_cur->m_data, chan->out_mbuf_cur->m_len))) + activity = ACT_TX; + } + else + { + activity = ACT_TX; + } + } + } + + len = 0; + + while(chan->out_mbuf_cur && len != sc->sc_bfifolen) + { + nextlen = min(chan->out_mbuf_cur_len, sc->sc_bfifolen - len); + +#ifdef NOTDEF + printf("i:mh=%x, mc=%x, mcp=%x, mcl=%d l=%d nl=%d # ", + chan->out_mbuf_head, + chan->out_mbuf_cur, + chan->out_mbuf_cur_ptr, + chan->out_mbuf_cur_len, + len, + next_len); +#endif + + isic_hscx_waitxfw(sc, h_chan); /* necessary !!! */ + + HSCX_WRFIFO(h_chan, chan->out_mbuf_cur_ptr, nextlen); + cmd |= HSCX_CMDR_XTF; + + len += nextlen; + chan->txcount += nextlen; + + chan->out_mbuf_cur_ptr += nextlen; + chan->out_mbuf_cur_len -= nextlen; + + if(chan->out_mbuf_cur_len == 0) + { + if((chan->out_mbuf_cur = chan->out_mbuf_cur->m_next) != NULL) + { + chan->out_mbuf_cur_ptr = chan->out_mbuf_cur->m_data; + chan->out_mbuf_cur_len = chan->out_mbuf_cur->m_len; + + if(sc->sc_trace & TRACE_B_TX) + { + i4b_trace_hdr_t hdr; + hdr.unit = L0ISICUNIT(sc->sc_unit); + hdr.type = (h_chan == HSCX_CH_A ? TRC_CH_B1 : TRC_CH_B2); + hdr.dir = FROM_TE; + hdr.count = ++sc->sc_trace_bcount; + MICROTIME(hdr.time); + i4b_l1_trace_ind(&hdr, chan->out_mbuf_cur->m_len, chan->out_mbuf_cur->m_data); + } + } + else + { + if (chan->bprot != BPROT_NONE) + cmd |= HSCX_CMDR_XME; + i4b_Bfreembuf(chan->out_mbuf_head); + chan->out_mbuf_head = NULL; + } + + } + } + } + + if(cmd) /* is there a command for the HSCX ? */ + { + isic_hscx_cmd(sc, h_chan, cmd); /* yes, to HSCX */ + } + + /* call timeout handling routine */ + + if(activity == ACT_RX || activity == ACT_TX) + (*chan->isic_drvr_linktab->bch_activity)(chan->isic_drvr_linktab->unit, activity); +} + +/*---------------------------------------------------------------------------* + * HSCX initialization + * + * for telephony: extended transparent mode 1 + * for raw hdlc: transparent mode 0 + *---------------------------------------------------------------------------*/ +void +isic_hscx_init(struct l1_softc *sc, int h_chan, int activate) +{ + l1_bchan_state_t *chan = &sc->sc_chan[h_chan]; + + HSCX_WRITE(h_chan, H_MASK, 0xff); /* mask irq's */ + + if(sc->sc_ipac) + { + /* CCR1: Power Up, Clock Mode 5 */ + HSCX_WRITE(h_chan, H_CCR1, HSCX_CCR1_PU | /* power up */ + HSCX_CCR1_CM1); /* IPAC clock mode 5 */ + } + else + { + /* CCR1: Power Up, Clock Mode 5 */ + HSCX_WRITE(h_chan, H_CCR1, HSCX_CCR1_PU | /* power up */ + HSCX_CCR1_CM2 | /* HSCX clock mode 5 */ + HSCX_CCR1_CM0); + } + + /* XAD1: Transmit Address Byte 1 */ + HSCX_WRITE(h_chan, H_XAD1, 0xff); + + /* XAD2: Transmit Address Byte 2 */ + HSCX_WRITE(h_chan, H_XAD2, 0xff); + + /* RAH2: Receive Address Byte High Reg. 2 */ + HSCX_WRITE(h_chan, H_RAH2, 0xff); + + /* XBCH: reset Transmit Byte Count High */ + HSCX_WRITE(h_chan, H_XBCH, 0x00); + + /* RLCR: reset Receive Length Check Register */ + HSCX_WRITE(h_chan, H_RLCR, 0x00); + + /* CCR2: set tx/rx clock shift bit 0 */ + /* disable CTS irq, disable RIE irq*/ + HSCX_WRITE(h_chan, H_CCR2, HSCX_CCR2_XCS0|HSCX_CCR2_RCS0); + + /* XCCR: tx bit count per time slot */ + HSCX_WRITE(h_chan, H_XCCR, 0x07); + + /* RCCR: rx bit count per time slot */ + HSCX_WRITE(h_chan, H_RCCR, 0x07); + + if(sc->sc_bustyp == BUS_TYPE_IOM2) + { + switch(h_chan) + { + case HSCX_CH_A: /* Prepare HSCX channel A */ + /* TSAX: tx clock shift bits 1 & 2 */ + /* tx time slot number */ + HSCX_WRITE(h_chan, H_TSAX, 0x2f); + + /* TSAR: rx clock shift bits 1 & 2 */ + /* rx time slot number */ + HSCX_WRITE(h_chan, H_TSAR, 0x2f); + break; + + case HSCX_CH_B: /* Prepare HSCX channel B */ + /* TSAX: tx clock shift bits 1 & 2 */ + /* tx time slot number */ + HSCX_WRITE(h_chan, H_TSAX, 0x03); + + /* TSAR: rx clock shift bits 1 & 2 */ + /* rx time slot number */ + HSCX_WRITE(h_chan, H_TSAR, 0x03); + break; + } + } + else /* IOM 1 setup */ + { + /* TSAX: tx clock shift bits 1 & 2 */ + /* tx time slot number */ + HSCX_WRITE(h_chan, H_TSAX, 0x07); + + /* TSAR: rx clock shift bits 1 & 2 */ + /* rx time slot number */ + HSCX_WRITE(h_chan, H_TSAR, 0x07); + } + + if(activate) + { + if(chan->bprot == BPROT_RHDLC) + { + /* HDLC Frames, transparent mode 0 */ + HSCX_WRITE(h_chan, H_MODE, + HSCX_MODE_MDS1|HSCX_MODE_RAC|HSCX_MODE_RTS); + } + else + { + /* Raw Telephony, extended transparent mode 1 */ + HSCX_WRITE(h_chan, H_MODE, + HSCX_MODE_MDS1|HSCX_MODE_MDS0|HSCX_MODE_ADM|HSCX_MODE_RTS); + } +#if 0 + isic_hscx_cmd(sc, h_chan, HSCX_CMDR_RHR|HSCX_CMDR_XRES); +#else + isic_hscx_cmd(sc, h_chan, HSCX_CMDR_RHR); +#endif + } + else + { + /* TSAX: tx time slot */ + HSCX_WRITE(h_chan, H_TSAX, 0xff); + + /* TSAR: rx time slot */ + HSCX_WRITE(h_chan, H_TSAR, 0xff); + + /* Raw Telephony, extended transparent mode 1 */ + HSCX_WRITE(h_chan, H_MODE, + HSCX_MODE_MDS1|HSCX_MODE_MDS0|HSCX_MODE_ADM|HSCX_MODE_RTS); + } + + /* don't touch ICA, EXA and EXB bits, this could be HSCX_CH_B */ + /* always disable RSC and TIN */ + + chan->hscx_mask |= HSCX_MASK_RSC | HSCX_MASK_TIN; + + if(activate) + { + /* enable */ + chan->hscx_mask &= ~(HSCX_MASK_RME | HSCX_MASK_RPF | HSCX_MASK_XPR); + } + else + { + /* disable */ + chan->hscx_mask |= HSCX_MASK_RME | HSCX_MASK_RPF | HSCX_MASK_XPR; + } + + /* handle ICA, EXA, and EXB via interrupt mask of channel b */ + + if (h_chan == HSCX_CH_A) + { + if (activate) + HSCX_B_IMASK &= ~(HSCX_MASK_EXA | HSCX_MASK_ICA); + else + HSCX_B_IMASK |= HSCX_MASK_EXA | HSCX_MASK_ICA; + HSCX_WRITE(HSCX_CH_A, H_MASK, HSCX_A_IMASK); + HSCX_WRITE(HSCX_CH_B, H_MASK, HSCX_B_IMASK); + } + else + { + if (activate) + HSCX_B_IMASK &= ~HSCX_MASK_EXB; + else + HSCX_B_IMASK |= HSCX_MASK_EXB; + HSCX_WRITE(HSCX_CH_B, H_MASK, HSCX_B_IMASK); + } + + /* clear spurious interrupts left over */ + + if(h_chan == HSCX_CH_A) + { + HSCX_READ(h_chan, H_EXIR); + HSCX_READ(h_chan, H_ISTA); + } + else /* mask ICA, because it must not be cleared by reading ISTA */ + { + HSCX_WRITE(HSCX_CH_B, H_MASK, HSCX_B_IMASK | HSCX_MASK_ICA); + HSCX_READ(h_chan, H_EXIR); + HSCX_READ(h_chan, H_ISTA); + HSCX_WRITE(HSCX_CH_B, H_MASK, HSCX_B_IMASK); + } +} + +/*---------------------------------------------------------------------------* + * write command to HSCX command register + *---------------------------------------------------------------------------*/ +void +isic_hscx_cmd(struct l1_softc *sc, int h_chan, unsigned char cmd) +{ + int timeout = 20; + + while(((HSCX_READ(h_chan, H_STAR)) & HSCX_STAR_CEC) && timeout) + { + DELAY(10); + timeout--; + } + + if(timeout == 0) + { + NDBGL1(L1_H_ERR, "HSCX wait for CEC timeout!"); + } + + HSCX_WRITE(h_chan, H_CMDR, cmd); +} + +/*---------------------------------------------------------------------------* + * wait for HSCX transmit FIFO write enable + *---------------------------------------------------------------------------*/ +void +isic_hscx_waitxfw(struct l1_softc *sc, int h_chan) +{ +#define WAITVAL 50 +#define WAITTO 200 + + int timeout = WAITTO; + + while((!(((HSCX_READ(h_chan, H_STAR)) & + (HSCX_STAR_CEC | HSCX_STAR_XFW)) == HSCX_STAR_XFW)) && timeout) + { + DELAY(WAITVAL); + timeout--; + } + + if(timeout == 0) + { + NDBGL1(L1_H_ERR, "HSCX wait for XFW timeout!"); + } + else if (timeout != WAITTO) + { + NDBGL1(L1_H_XFRERR, "HSCX wait for XFW time: %d uS", (WAITTO-timeout)*50); + } +} + +#endif /* NISIC > 0 */ |