/* $NetBSD: iavc.c,v 1.7 2008/04/08 12:07:26 cegger Exp $ */ /* * Copyright (c) 2001-2003 Cubical Solutions Ltd. 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. * * The AVM ISDN controllers' card specific support routines. * * $FreeBSD: src/sys/i4b/capi/iavc/iavc_card.c,v 1.1.2.1 2001/08/10 14:08:34 obrien Exp $ */ #include __KERNEL_RCSID(0, "$NetBSD: iavc.c,v 1.7 2008/04/08 12:07:26 cegger Exp $"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* // AVM B1 (active BRI, PIO mode) */ int iavc_b1_detect(iavc_softc_t *sc) { if ((iavc_read_port(sc, B1_INSTAT) & 0xfc) || (iavc_read_port(sc, B1_OUTSTAT) & 0xfc)) return (1); b1io_outp(sc, B1_INSTAT, 0x02); b1io_outp(sc, B1_OUTSTAT, 0x02); if ((iavc_read_port(sc, B1_INSTAT) & 0xfe) != 2 || (iavc_read_port(sc, B1_OUTSTAT) & 0xfe) != 2) return (2); b1io_outp(sc, B1_INSTAT, 0x00); b1io_outp(sc, B1_OUTSTAT, 0x00); if ((iavc_read_port(sc, B1_INSTAT) & 0xfe) || (iavc_read_port(sc, B1_OUTSTAT) & 0xfe)) return (3); return (0); /* found */ } void iavc_b1_disable_irq(iavc_softc_t *sc) { b1io_outp(sc, B1_INSTAT, 0x00); } void iavc_b1_reset(iavc_softc_t *sc) { b1io_outp(sc, B1_RESET, 0); DELAY(55*2*1000); b1io_outp(sc, B1_RESET, 1); DELAY(55*2*1000); b1io_outp(sc, B1_RESET, 0); DELAY(55*2*1000); } /* // Newer PCI-based B1's, and T1's, supports DMA */ int iavc_b1dma_detect(iavc_softc_t *sc) { AMCC_WRITE(sc, AMCC_MCSR, 0); DELAY(10*1000); AMCC_WRITE(sc, AMCC_MCSR, 0x0f000000); DELAY(10*1000); AMCC_WRITE(sc, AMCC_MCSR, 0); DELAY(42*1000); AMCC_WRITE(sc, AMCC_RXLEN, 0); AMCC_WRITE(sc, AMCC_TXLEN, 0); sc->sc_csr = 0; AMCC_WRITE(sc, AMCC_INTCSR, sc->sc_csr); if (AMCC_READ(sc, AMCC_INTCSR) != 0) return 1; AMCC_WRITE(sc, AMCC_RXPTR, 0xffffffff); AMCC_WRITE(sc, AMCC_TXPTR, 0xffffffff); if ((AMCC_READ(sc, AMCC_RXPTR) != 0xfffffffc) || (AMCC_READ(sc, AMCC_TXPTR) != 0xfffffffc)) return 2; AMCC_WRITE(sc, AMCC_RXPTR, 0); AMCC_WRITE(sc, AMCC_TXPTR, 0); if ((AMCC_READ(sc, AMCC_RXPTR) != 0) || (AMCC_READ(sc, AMCC_TXPTR) != 0)) return 3; iavc_write_port(sc, 0x10, 0x00); iavc_write_port(sc, 0x07, 0x00); iavc_write_port(sc, 0x02, 0x02); iavc_write_port(sc, 0x03, 0x02); if (((iavc_read_port(sc, 0x02) & 0xfe) != 0x02) || (iavc_read_port(sc, 0x03) != 0x03)) return 4; iavc_write_port(sc, 0x02, 0x00); iavc_write_port(sc, 0x03, 0x00); if (((iavc_read_port(sc, 0x02) & 0xfe) != 0x00) || (iavc_read_port(sc, 0x03) != 0x01)) return 5; return (0); /* found */ } void iavc_b1dma_reset(iavc_softc_t *sc) { int s = splnet(); sc->sc_csr = 0; AMCC_WRITE(sc, AMCC_INTCSR, sc->sc_csr); AMCC_WRITE(sc, AMCC_MCSR, 0); AMCC_WRITE(sc, AMCC_RXLEN, 0); AMCC_WRITE(sc, AMCC_TXLEN, 0); iavc_write_port(sc, 0x10, 0x00); /* XXX magic numbers from */ iavc_write_port(sc, 0x07, 0x00); /* XXX the linux driver */ splx(s); AMCC_WRITE(sc, AMCC_MCSR, 0); DELAY(10 * 1000); AMCC_WRITE(sc, AMCC_MCSR, 0x0f000000); DELAY(10 * 1000); AMCC_WRITE(sc, AMCC_MCSR, 0); DELAY(42 * 1000); } /* // AVM T1 (active PRI) */ #define b1dma_tx_empty(sc) (b1io_read_reg((sc), T1_OUTSTAT) & 1) #define b1dma_rx_full(sc) (b1io_read_reg((sc), T1_INSTAT) & 1) static int b1dma_tolink(iavc_softc_t *sc, void *buf, int len) { volatile int spin; char *s = (char*) buf; while (len--) { spin = 0; while (!b1dma_tx_empty(sc) && spin < 100000) spin++; if (!b1dma_tx_empty(sc)) return -1; t1io_outp(sc, 1, *s++); } return 0; } static int b1dma_fromlink(iavc_softc_t *sc, void *buf, int len) { volatile int spin; char *s = (char*) buf; while (len--) { spin = 0; while (!b1dma_rx_full(sc) && spin < 100000) spin++; if (!b1dma_rx_full(sc)) return -1; *s++ = t1io_inp(sc, 0); } return 0; } static int WriteReg(iavc_softc_t *sc, u_int32_t reg, u_int8_t val) { u_int8_t cmd = 0; if (b1dma_tolink(sc, &cmd, 1) == 0 && b1dma_tolink(sc, ®, 4) == 0) { u_int32_t tmp = val; return b1dma_tolink(sc, &tmp, 4); } return -1; } static u_int8_t ReadReg(iavc_softc_t *sc, u_int32_t reg) { u_int8_t cmd = 1; if (b1dma_tolink(sc, &cmd, 1) == 0 && b1dma_tolink(sc, ®, 4) == 0) { u_int32_t tmp; if (b1dma_fromlink(sc, &tmp, 4) == 0) return (u_int8_t) tmp; } return 0xff; } int iavc_t1_detect(iavc_softc_t *sc) { int ret = iavc_b1dma_detect(sc); if (ret) return ret; if ((WriteReg(sc, 0x80001000, 0x11) != 0) || (WriteReg(sc, 0x80101000, 0x22) != 0) || (WriteReg(sc, 0x80201000, 0x33) != 0) || (WriteReg(sc, 0x80301000, 0x44) != 0)) return 6; if ((ReadReg(sc, 0x80001000) != 0x11) || (ReadReg(sc, 0x80101000) != 0x22) || (ReadReg(sc, 0x80201000) != 0x33) || (ReadReg(sc, 0x80301000) != 0x44)) return 7; if ((WriteReg(sc, 0x80001000, 0x55) != 0) || (WriteReg(sc, 0x80101000, 0x66) != 0) || (WriteReg(sc, 0x80201000, 0x77) != 0) || (WriteReg(sc, 0x80301000, 0x88) != 0)) return 8; if ((ReadReg(sc, 0x80001000) != 0x55) || (ReadReg(sc, 0x80101000) != 0x66) || (ReadReg(sc, 0x80201000) != 0x77) || (ReadReg(sc, 0x80301000) != 0x88)) return 9; return 0; /* found */ } void iavc_t1_disable_irq(iavc_softc_t *sc) { iavc_write_port(sc, T1_IRQMASTER, 0x00); } void iavc_t1_reset(iavc_softc_t *sc) { iavc_b1_reset(sc); iavc_write_port(sc, B1_INSTAT, 0x00); iavc_write_port(sc, B1_OUTSTAT, 0x00); iavc_write_port(sc, T1_IRQMASTER, 0x00); iavc_write_port(sc, T1_RESETBOARD, 0x0f); } /* Forward declarations of local subroutines... */ static int iavc_send_init(iavc_softc_t *); static void iavc_handle_rx(iavc_softc_t *); static void iavc_start_tx(iavc_softc_t *); static uint32_t iavc_tx_capimsg(iavc_softc_t *, struct mbuf *); static uint32_t iavc_tx_ctrlmsg(iavc_softc_t *, struct mbuf *); /* // Callbacks from the upper (capi) layer: // -------------------------------------- // // iavc_load // Resets the board and loads the firmware, then initiates // board startup. // // iavc_register // Registers a CAPI application id. // // iavc_release // Releases a CAPI application id. // // iavc_send // Sends a capi message. */ int iavc_load(capi_softc_t *capi_sc, int len, u_int8_t *cp) { iavc_softc_t *sc = (iavc_softc_t*) capi_sc->ctx; u_int8_t val; aprint_debug_dev(&sc->sc_dev, "reset card ....\n"); if (sc->sc_dma) iavc_b1dma_reset(sc); /* PCI cards */ else if (sc->sc_t1) iavc_t1_reset(sc); /* ISA attachment T1 */ else iavc_b1_reset(sc); /* ISA attachment B1 */ DELAY(1000); aprint_debug_dev(&sc->sc_dev, "start loading %d bytes firmware\n", len); while (len && b1io_save_put_byte(sc, *cp++) == 0) len--; if (len) { aprint_error_dev(&sc->sc_dev, "loading failed, can't write to card, len = %d\n", len); return (EIO); } aprint_debug_dev(&sc->sc_dev, "firmware loaded, wait for ACK\n"); if(sc->sc_capi.card_type == CARD_TYPEC_AVM_B1_ISA) iavc_put_byte(sc, SEND_POLL); else iavc_put_byte(sc, SEND_POLLACK); for (len = 0; len < 1000 && !iavc_rx_full(sc); len++) DELAY(100); if (!iavc_rx_full(sc)) { aprint_error_dev(&sc->sc_dev, "loading failed, no ack\n"); return (EIO); } val = iavc_get_byte(sc); if ((sc->sc_dma && val != RECEIVE_POLLDWORD) || (!sc->sc_dma && val != RECEIVE_POLL)) { aprint_error_dev(&sc->sc_dev, "loading failed, bad ack = %02x\n", val); return (EIO); } aprint_debug_dev(&sc->sc_dev, "got ACK = 0x%02x\n", val); /* Start the DMA engine */ if (sc->sc_dma) { int s; s = splnet(); sc->sc_csr = AVM_FLAG; AMCC_WRITE(sc, AMCC_INTCSR, sc->sc_csr); AMCC_WRITE(sc, AMCC_MCSR, (EN_A2P_TRANSFERS|EN_P2A_TRANSFERS| A2P_HI_PRIORITY|P2A_HI_PRIORITY| RESET_A2P_FLAGS|RESET_P2A_FLAGS)); iavc_write_port(sc, 0x07, 0x30); /* XXX magic numbers from */ iavc_write_port(sc, 0x10, 0xf0); /* XXX the linux driver */ bus_dmamap_sync(sc->dmat, sc->rx_map, 0, sc->rx_map->dm_mapsize, BUS_DMASYNC_PREREAD); sc->sc_recv1 = 0; AMCC_WRITE(sc, AMCC_RXPTR, sc->rx_map->dm_segs[0].ds_addr); AMCC_WRITE(sc, AMCC_RXLEN, 4); sc->sc_csr |= EN_RX_TC_INT|EN_TX_TC_INT; AMCC_WRITE(sc, AMCC_INTCSR, sc->sc_csr); splx(s); } #ifdef notyet /* good happy place */ if(sc->sc_capi.card_type == CARD_TYPEC_AVM_B1_ISA) b1isa_setup_irq(sc); #endif iavc_send_init(sc); return 0; } int iavc_register(capi_softc_t *capi_sc, int applid, int nchan) { iavc_softc_t *sc = (iavc_softc_t*) capi_sc->ctx; struct mbuf *m = i4b_Dgetmbuf(23); u_int8_t *p; if (!m) { aprint_error("iavc%d: can't get memory\n", sc->sc_unit); return (ENOMEM); } /* * byte 0x12 = SEND_REGISTER * dword ApplId * dword NumMessages * dword NumB3Connections 0..nbch * dword NumB3Blocks * dword B3Size */ p = amcc_put_byte(mtod(m, u_int8_t*), 0); p = amcc_put_byte(p, 0); p = amcc_put_byte(p, SEND_REGISTER); p = amcc_put_word(p, applid); #if 0 p = amcc_put_word(p, 1024 + (nchan + 1)); #else p = amcc_put_word(p, 1024 * (nchan + 1)); #endif p = amcc_put_word(p, nchan); p = amcc_put_word(p, 8); p = amcc_put_word(p, 2048); IF_ENQUEUE(&sc->sc_txq, m); iavc_start_tx(sc); return 0; } int iavc_release(capi_softc_t *capi_sc, int applid) { iavc_softc_t *sc = (iavc_softc_t*) capi_sc->ctx; struct mbuf *m = i4b_Dgetmbuf(7); u_int8_t *p; if (!m) { aprint_error_dev(&sc->sc_dev, "can't get memory\n"); return (ENOMEM); } /* * byte 0x14 = SEND_RELEASE * dword ApplId */ p = amcc_put_byte(mtod(m, u_int8_t*), 0); p = amcc_put_byte(p, 0); p = amcc_put_byte(p, SEND_RELEASE); p = amcc_put_word(p, applid); IF_ENQUEUE(&sc->sc_txq, m); iavc_start_tx(sc); return 0; } int iavc_send(capi_softc_t *capi_sc, struct mbuf *m) { iavc_softc_t *sc = (iavc_softc_t*) capi_sc->ctx; if (sc->sc_state != IAVC_UP) { aprint_error_dev(&sc->sc_dev, "attempt to send before device up\n"); if (m->m_next) i4b_Bfreembuf(m->m_next); i4b_Dfreembuf(m); return (ENXIO); } if (IF_QFULL(&sc->sc_txq)) { IF_DROP(&sc->sc_txq); aprint_error_dev(&sc->sc_dev, "tx overflow, message dropped\n"); if (m->m_next) i4b_Bfreembuf(m->m_next); i4b_Dfreembuf(m); } else { IF_ENQUEUE(&sc->sc_txq, m); iavc_start_tx(sc); } return 0; } /* // Functions called by ourself during the initialization sequence: // --------------------------------------------------------------- // // iavc_send_init // Sends the system initialization message to a newly loaded // board, and sets state to INIT. */ static int iavc_send_init(iavc_softc_t *sc) { struct mbuf *m = i4b_Dgetmbuf(15); u_int8_t *p; int s; if (!m) { aprint_error_dev(&sc->sc_dev, "can't get memory\n"); return (ENOMEM); } /* * byte 0x11 = SEND_INIT * dword NumApplications * dword NumNCCIs * dword BoardNumber */ p = amcc_put_byte(mtod(m, u_int8_t*), 0); p = amcc_put_byte(p, 0); p = amcc_put_byte(p, SEND_INIT); p = amcc_put_word(p, 1); /* XXX MaxAppl XXX */ p = amcc_put_word(p, sc->sc_capi.sc_nbch); p = amcc_put_word(p, sc->sc_unit); s = splnet(); IF_ENQUEUE(&sc->sc_txq, m); iavc_start_tx(sc); sc->sc_state = IAVC_INIT; splx(s); return 0; } /* // Functions called during normal operation: // ----------------------------------------- // // iavc_receive_init // Reads the initialization reply and calls capi_ll_control(). // // iavc_receive_new_ncci // Reads a new NCCI notification and calls capi_ll_control(). // // iavc_receive_free_ncci // Reads a freed NCCI notification and calls capi_ll_control(). // // iavc_receive_task_ready // Reads a task ready message -- which should not occur XXX. // // iavc_receive_debugmsg // Reads a debug message -- which should not occur XXX. // // iavc_receive_start // Reads a START TRANSMIT message and unblocks device. // // iavc_receive_stop // Reads a STOP TRANSMIT message and blocks device. // // iavc_receive // Reads an incoming message and calls capi_ll_receive(). */ static int iavc_receive_init(iavc_softc_t *sc, u_int8_t *dmabuf) { u_int32_t Length; u_int8_t *p; u_int8_t *cardtype, *serial, *profile, *vers, *caps, *prot; if (sc->sc_dma) { p = amcc_get_word(dmabuf, &Length); } else { Length = iavc_get_slice(sc, sc->sc_recvbuf); p = sc->sc_recvbuf; } #if 0 { int len = 0; printf("%s: rx_init: ", device_xname(&sc->sc_dev)); while (len < Length) { printf(" %02x", p[len]); if (len && (len % 16) == 0) printf("\n"); len++; } if (len % 16) printf("\n"); } #endif vers = (p + 1); p += (*p + 1); /* driver version */ cardtype = (p + 1); p += (*p + 1); /* card type */ p += (*p + 1); /* hardware ID */ serial = (p + 1); p += (*p + 1); /* serial number */ caps = (p + 1); p += (*p + 1); /* supported options */ prot = (p + 1); p += (*p + 1); /* supported protocols */ profile = (p + 1); if (cardtype && serial && profile) { int nbch = ((profile[3]<<8) | profile[2]); aprint_normal_dev(&sc->sc_dev, "AVM %s, s/n %s, %d chans, f/w rev %s, prot %s\n", cardtype, serial, nbch, vers, prot); aprint_verbose_dev(&sc->sc_dev, "%s\n", caps); capi_ll_control(&sc->sc_capi, CAPI_CTRL_PROFILE, (int) profile); } else { printf("%s: no profile data in info response?\n", device_xname(&sc->sc_dev)); } sc->sc_blocked = 1; /* controller will send START when ready */ return 0; } static int iavc_receive_start(iavc_softc_t *sc) { struct mbuf *m = i4b_Dgetmbuf(3); u_int8_t *p; if (sc->sc_blocked && sc->sc_state == IAVC_UP) printf("%s: receive_start\n", device_xname(&sc->sc_dev)); if (!m) { aprint_error_dev(&sc->sc_dev, "can't get memory\n"); return (ENOMEM); } /* * byte 0x73 = SEND_POLLACK */ p = amcc_put_byte(mtod(m, u_int8_t*), 0); p = amcc_put_byte(p, 0); p = amcc_put_byte(p, SEND_POLLACK); IF_PREPEND(&sc->sc_txq, m); NDBGL4(L4_IAVCDBG, "%s: blocked = %d, state = %d", device_xname(&sc->sc_dev), sc->sc_blocked, sc->sc_state); sc->sc_blocked = 0; iavc_start_tx(sc); /* If this was our first START, register our readiness */ if (sc->sc_state != IAVC_UP) { sc->sc_state = IAVC_UP; capi_ll_control(&sc->sc_capi, CAPI_CTRL_READY, 1); } return 0; } static int iavc_receive_stop(iavc_softc_t *sc) { printf("%s: receive_stop\n", device_xname(&sc->sc_dev)); sc->sc_blocked = 1; return 0; } static int iavc_receive_new_ncci(iavc_softc_t *sc, u_int8_t *dmabuf) { u_int32_t ApplId, NCCI, WindowSize; if (sc->sc_dma) { dmabuf = amcc_get_word(dmabuf, &ApplId); dmabuf = amcc_get_word(dmabuf, &NCCI); dmabuf = amcc_get_word(dmabuf, &WindowSize); } else { ApplId = iavc_get_word(sc); NCCI = iavc_get_word(sc); WindowSize = iavc_get_word(sc); } capi_ll_control(&sc->sc_capi, CAPI_CTRL_NEW_NCCI, NCCI); return 0; } static int iavc_receive_free_ncci(iavc_softc_t *sc, u_int8_t *dmabuf) { u_int32_t ApplId, NCCI; if (sc->sc_dma) { dmabuf = amcc_get_word(dmabuf, &ApplId); dmabuf = amcc_get_word(dmabuf, &NCCI); } else { ApplId = iavc_get_word(sc); NCCI = iavc_get_word(sc); } capi_ll_control(&sc->sc_capi, CAPI_CTRL_FREE_NCCI, NCCI); return 0; } static int iavc_receive_task_ready(iavc_softc_t *sc, u_int8_t *dmabuf) { u_int32_t TaskId, Length; u_int8_t *p; printf("%s: receive_task_ready\n", device_xname(&sc->sc_dev)); if (sc->sc_dma) { p = amcc_get_word(dmabuf, &TaskId); p = amcc_get_word(p, &Length); } else { TaskId = iavc_get_word(sc); Length = iavc_get_slice(sc, sc->sc_recvbuf); p = sc->sc_recvbuf; } /* XXX could show the message if trace enabled? XXX */ return 0; } static int iavc_receive_debugmsg(iavc_softc_t *sc, u_int8_t *dmabuf) { u_int32_t Length; u_int8_t *p; printf("%s: receive_debugmsg\n", device_xname(&sc->sc_dev)); if (sc->sc_dma) { p = amcc_get_word(dmabuf, &Length); } else { Length = iavc_get_slice(sc, sc->sc_recvbuf); p = sc->sc_recvbuf; } /* XXX could show the message if trace enabled? XXX */ return 0; } static int iavc_receive(iavc_softc_t *sc, u_int8_t *dmabuf, int b3data) { struct mbuf *m; u_int32_t ApplId, Length; /* * byte 0x21 = RECEIVE_MESSAGE * dword ApplId * dword length * ... CAPI msg * * --or-- * * byte 0x22 = RECEIVE_DATA_B3_IND * dword ApplId * dword length * ... CAPI msg * dword datalen * ... B3 data */ if (sc->sc_dma) { dmabuf = amcc_get_word(dmabuf, &ApplId); dmabuf = amcc_get_word(dmabuf, &Length); } else { ApplId = iavc_get_word(sc); Length = iavc_get_slice(sc, sc->sc_recvbuf); dmabuf = sc->sc_recvbuf; } m = i4b_Dgetmbuf(Length); if (!m) { aprint_error_dev(&sc->sc_dev, "can't get memory for receive\n"); return (ENOMEM); } memcpy(mtod(m, u_int8_t*), dmabuf, Length); #if 0 { u_int8_t *p = mtod(m, u_int8_t*); int len = 0; printf("%s: applid=%d, len=%d\n", device_xname(&sc->sc_dev), ApplId, Length); while (len < m->m_len) { printf(" %02x", p[len]); if (len && (len % 16) == 0) printf("\n"); len++; } if (len % 16) printf("\n"); } #endif if (b3data) { if (sc->sc_dma) { dmabuf = amcc_get_word(dmabuf + Length, &Length); } else { Length = iavc_get_slice(sc, sc->sc_recvbuf); dmabuf = sc->sc_recvbuf; } m->m_next = i4b_Bgetmbuf(Length); if (!m->m_next) { aprint_error_dev(&sc->sc_dev, "can't get memory for receive\n"); i4b_Dfreembuf(m); return (ENOMEM); } memcpy(mtod(m->m_next, u_int8_t*), dmabuf, Length); } capi_ll_receive(&sc->sc_capi, m); return 0; } /* // iavc_handle_intr // Checks device interrupt status and calls iavc_handle_{rx,tx}() // as necessary. // // iavc_handle_rx // Reads in the command byte and calls the subroutines above. // // iavc_start_tx // Initiates DMA on the next queued message if possible. */ int iavc_handle_intr(iavc_softc_t *sc) { u_int32_t status; u_int32_t newcsr; if (!sc->sc_dma) { while (iavc_rx_full(sc)) iavc_handle_rx(sc); return 0; } status = AMCC_READ(sc, AMCC_INTCSR); if ((status & ANY_S5933_INT) == 0) return 0; newcsr = sc->sc_csr | (status & ALL_INT); if (status & TX_TC_INT) newcsr &= ~EN_TX_TC_INT; if (status & RX_TC_INT) newcsr &= ~EN_RX_TC_INT; AMCC_WRITE(sc, AMCC_INTCSR, newcsr); sc->sc_intr = 1; if (status & RX_TC_INT) { u_int32_t rxlen; bus_dmamap_sync(sc->dmat, sc->rx_map, 0, sc->rx_map->dm_mapsize, BUS_DMASYNC_POSTREAD); if (sc->sc_recv1 == 0) { sc->sc_recv1 = *(u_int32_t*)(sc->sc_recvbuf); rxlen = (sc->sc_recv1 + 3) & ~3; AMCC_WRITE(sc, AMCC_RXPTR, sc->rx_map->dm_segs[0].ds_addr); AMCC_WRITE(sc, AMCC_RXLEN, rxlen ? rxlen : 4); } else { iavc_handle_rx(sc); sc->sc_recv1 = 0; AMCC_WRITE(sc, AMCC_RXPTR, sc->rx_map->dm_segs[0].ds_addr); AMCC_WRITE(sc, AMCC_RXLEN, 4); } } if (status & TX_TC_INT) { bus_dmamap_sync(sc->dmat, sc->tx_map, 0, sc->tx_map->dm_mapsize, BUS_DMASYNC_POSTWRITE); sc->sc_csr &= ~EN_TX_TC_INT; iavc_start_tx(sc); } AMCC_WRITE(sc, AMCC_INTCSR, sc->sc_csr); sc->sc_intr = 0; return 0; } static void iavc_handle_rx(iavc_softc_t *sc) { u_int8_t *dmabuf = 0, cmd; if (sc->sc_dma) { dmabuf = amcc_get_byte(&sc->sc_recvbuf[0], &cmd); } else { cmd = iavc_get_byte(sc); } NDBGL4(L4_IAVCDBG, "iavc%d: command = 0x%02x", sc->sc_unit, cmd); switch (cmd) { case RECEIVE_DATA_B3_IND: iavc_receive(sc, dmabuf, 1); break; case RECEIVE_MESSAGE: iavc_receive(sc, dmabuf, 0); break; case RECEIVE_NEW_NCCI: iavc_receive_new_ncci(sc, dmabuf); break; case RECEIVE_FREE_NCCI: iavc_receive_free_ncci(sc, dmabuf); break; case RECEIVE_START: iavc_receive_start(sc); break; case RECEIVE_STOP: iavc_receive_stop(sc); break; case RECEIVE_INIT: iavc_receive_init(sc, dmabuf); break; case RECEIVE_TASK_READY: iavc_receive_task_ready(sc, dmabuf); break; case RECEIVE_DEBUGMSG: iavc_receive_debugmsg(sc, dmabuf); break; default: aprint_error_dev(&sc->sc_dev, "unknown msg %02x\n", cmd); } } static void iavc_start_tx(iavc_softc_t *sc) { struct mbuf *m; u_int32_t txlen; /* If device has put us on hold, punt. */ if (sc->sc_blocked) { return; } /* If using DMA and transmitter busy, punt. */ if (sc->sc_dma && (sc->sc_csr & EN_TX_TC_INT)) { return; } /* Else, see if we have messages to send. */ IF_DEQUEUE(&sc->sc_txq, m); if (!m) { return; } /* Have message, will send. */ if (CAPIMSG_LEN(m->m_data)) { /* A proper CAPI message, possibly with B3 data */ txlen = iavc_tx_capimsg(sc, m); } else { /* A board control message to be sent as is */ txlen = iavc_tx_ctrlmsg(sc, m); } if (m->m_next) { i4b_Bfreembuf(m->m_next); m->m_next = NULL; } i4b_Dfreembuf(m); /* Kick DMA into motion if applicable */ if (sc->sc_dma) { txlen = (txlen + 3) & ~3; bus_dmamap_sync(sc->dmat, sc->tx_map, 0, txlen, BUS_DMASYNC_PREWRITE); AMCC_WRITE(sc, AMCC_TXPTR, sc->tx_map->dm_segs[0].ds_addr); AMCC_WRITE(sc, AMCC_TXLEN, txlen); sc->sc_csr |= EN_TX_TC_INT; if (!sc->sc_intr) AMCC_WRITE(sc, AMCC_INTCSR, sc->sc_csr); } } static uint32_t iavc_tx_capimsg(iavc_softc_t *sc, struct mbuf *m) { uint32_t txlen = 0; u_int8_t *dmabuf; if (sc->sc_dma) { /* Copy message to DMA buffer. */ if (m->m_next) dmabuf = amcc_put_byte(&sc->sc_sendbuf[0], SEND_DATA_B3_REQ); else dmabuf = amcc_put_byte(&sc->sc_sendbuf[0], SEND_MESSAGE); dmabuf = amcc_put_word(dmabuf, m->m_len); memcpy(dmabuf, m->m_data, m->m_len); dmabuf += m->m_len; txlen = 5 + m->m_len; if (m->m_next) { dmabuf = amcc_put_word(dmabuf, m->m_next->m_len); memcpy(dmabuf, m->m_next->m_data, m->m_next->m_len); txlen += 4 + m->m_next->m_len; } } else { /* Use PIO. */ if (m->m_next) { iavc_put_byte(sc, SEND_DATA_B3_REQ); NDBGL4(L4_IAVCDBG, "iavc%d: tx SDB3R msg, len = %d", sc->sc_unit, m->m_len); } else { iavc_put_byte(sc, SEND_MESSAGE); NDBGL4(L4_IAVCDBG, "iavc%d: tx SM msg, len = %d", sc->sc_unit, m->m_len); } #if 0 { u_int8_t *p = mtod(m, u_int8_t*); int len; for (len = 0; len < m->m_len; len++) { printf(" %02x", *p++); if (len && (len % 16) == 0) printf("\n"); } if (len % 16) printf("\n"); } #endif iavc_put_slice(sc, m->m_data, m->m_len); if (m->m_next) iavc_put_slice(sc, m->m_next->m_data, m->m_next->m_len); } return txlen; } static uint32_t iavc_tx_ctrlmsg(iavc_softc_t *sc, struct mbuf *m) { uint32_t txlen = 0; uint8_t *dmabuf; if (sc->sc_dma) { memcpy(&sc->sc_sendbuf[0], m->m_data + 2, m->m_len - 2); txlen = m->m_len - 2; } else { #if 0 { u_int8_t *p = mtod(m, u_int8_t*) + 2; int len; printf("%s: tx BDC msg, len = %d, msg =", device_xname(&sc->sc_dev), m->m_len-2); for (len = 0; len < m->m_len-2; len++) { printf(" %02x", *p++); if (len && (len % 16) == 0) printf("\n"); } if (len % 16) printf("\n"); } #endif /* no DMA */ txlen = m->m_len - 2; dmabuf = mtod(m, char*) + 2; while(txlen--) b1io_put_byte(sc, *dmabuf++); } return txlen; }