/* $NetBSD: hd64570.c,v 1.23 2003/05/03 18:11:17 wiz Exp $ */ /* * Copyright (c) 1999 Christian E. Hopps * Copyright (c) 1998 Vixie Enterprises * 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. * 3. Neither the name of Vixie Enterprises nor the names * of its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY VIXIE ENTERPRISES 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 VIXIE ENTERPRISES 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. * * This software has been written for Vixie Enterprises by Michael Graff * . To learn more about Vixie Enterprises, see * ``http://www.vix.com''. */ /* * TODO: * * o teach the receive logic about errors, and about long frames that * span more than one input buffer. (Right now, receive/transmit is * limited to one descriptor's buffer space, which is MTU + 4 bytes. * This is currently 1504, which is large enough to hold the HDLC * header and the packet itself. Packets which are too long are * silently dropped on transmit and silently dropped on receive. * o write code to handle the msci interrupts, needed only for CD * and CTS changes. * o consider switching back to a "queue tx with DMA active" model which * should help sustain outgoing traffic * o through clever use of bus_dma*() functions, it should be possible * to map the mbuf's data area directly into a descriptor transmit * buffer, removing the need to allocate extra memory. If, however, * we run out of descriptors for this, we will need to then allocate * one large mbuf, copy the fragmented chain into it, and put it onto * a single descriptor. * o use bus_dmamap_sync() with the right offset and lengths, rather * than cheating and always sync'ing the whole region. * * o perhaps allow rx and tx to be in more than one page * if not using DMA. currently the assumption is that * rx uses a page and tx uses a page. */ #include __KERNEL_RCSID(0, "$NetBSD: hd64570.c,v 1.23 2003/05/03 18:11:17 wiz Exp $"); #include "bpfilter.h" #include "opt_inet.h" #include "opt_iso.h" #include #include #include #include #include #include #include #include #include #include #if defined(INET) || defined(INET6) #include #include #include #include #ifdef INET6 #include #endif #endif #ifdef ISO #include #include #include #endif #if NBPFILTER > 0 #include #endif #include #include #include #include #include #include #include #include #define SCA_DEBUG_RX 0x0001 #define SCA_DEBUG_TX 0x0002 #define SCA_DEBUG_CISCO 0x0004 #define SCA_DEBUG_DMA 0x0008 #define SCA_DEBUG_RXPKT 0x0010 #define SCA_DEBUG_TXPKT 0x0020 #define SCA_DEBUG_INTR 0x0040 #define SCA_DEBUG_CLOCK 0x0080 #if 0 #define SCA_DEBUG_LEVEL ( 0xFFFF ) #else #define SCA_DEBUG_LEVEL 0 #endif u_int32_t sca_debug = SCA_DEBUG_LEVEL; #if SCA_DEBUG_LEVEL > 0 #define SCA_DPRINTF(l, x) do { \ if ((l) & sca_debug) \ printf x;\ } while (0) #else #define SCA_DPRINTF(l, x) #endif #if 0 #define SCA_USE_FASTQ /* use a split queue, one for fast traffic */ #endif static inline void msci_write_1(sca_port_t *, u_int, u_int8_t); static inline u_int8_t msci_read_1(sca_port_t *, u_int); static inline void dmac_write_1(sca_port_t *, u_int, u_int8_t); static inline void dmac_write_2(sca_port_t *, u_int, u_int16_t); static inline u_int8_t dmac_read_1(sca_port_t *, u_int); static inline u_int16_t dmac_read_2(sca_port_t *, u_int); static void sca_msci_init(struct sca_softc *, sca_port_t *); static void sca_dmac_init(struct sca_softc *, sca_port_t *); static void sca_dmac_rxinit(sca_port_t *); static int sca_dmac_intr(sca_port_t *, u_int8_t); static int sca_msci_intr(sca_port_t *, u_int8_t); static void sca_get_packets(sca_port_t *); static int sca_frame_avail(sca_port_t *); static void sca_frame_process(sca_port_t *); static void sca_frame_read_done(sca_port_t *); static void sca_port_starttx(sca_port_t *); static void sca_port_up(sca_port_t *); static void sca_port_down(sca_port_t *); static int sca_output __P((struct ifnet *, struct mbuf *, struct sockaddr *, struct rtentry *)); static int sca_ioctl __P((struct ifnet *, u_long, caddr_t)); static void sca_start __P((struct ifnet *)); static void sca_watchdog __P((struct ifnet *)); static struct mbuf *sca_mbuf_alloc(struct sca_softc *, caddr_t, u_int); #if SCA_DEBUG_LEVEL > 0 static void sca_frame_print(sca_port_t *, sca_desc_t *, u_int8_t *); #endif #define sca_read_1(sc, reg) (sc)->sc_read_1(sc, reg) #define sca_read_2(sc, reg) (sc)->sc_read_2(sc, reg) #define sca_write_1(sc, reg, val) (sc)->sc_write_1(sc, reg, val) #define sca_write_2(sc, reg, val) (sc)->sc_write_2(sc, reg, val) #define sca_page_addr(sc, addr) ((bus_addr_t)(u_long)(addr) & (sc)->scu_pagemask) static inline void msci_write_1(sca_port_t *scp, u_int reg, u_int8_t val) { sca_write_1(scp->sca, scp->msci_off + reg, val); } static inline u_int8_t msci_read_1(sca_port_t *scp, u_int reg) { return sca_read_1(scp->sca, scp->msci_off + reg); } static inline void dmac_write_1(sca_port_t *scp, u_int reg, u_int8_t val) { sca_write_1(scp->sca, scp->dmac_off + reg, val); } static inline void dmac_write_2(sca_port_t *scp, u_int reg, u_int16_t val) { sca_write_2(scp->sca, scp->dmac_off + reg, val); } static inline u_int8_t dmac_read_1(sca_port_t *scp, u_int reg) { return sca_read_1(scp->sca, scp->dmac_off + reg); } static inline u_int16_t dmac_read_2(sca_port_t *scp, u_int reg) { return sca_read_2(scp->sca, scp->dmac_off + reg); } /* * read the chain pointer */ static inline u_int16_t sca_desc_read_chainp(struct sca_softc *sc, struct sca_desc *dp) { if (sc->sc_usedma) return ((dp)->sd_chainp); return (bus_space_read_2(sc->scu_memt, sc->scu_memh, sca_page_addr(sc, dp) + offsetof(struct sca_desc, sd_chainp))); } /* * write the chain pointer */ static inline void sca_desc_write_chainp(struct sca_softc *sc, struct sca_desc *dp, u_int16_t cp) { if (sc->sc_usedma) (dp)->sd_chainp = cp; else bus_space_write_2(sc->scu_memt, sc->scu_memh, sca_page_addr(sc, dp) + offsetof(struct sca_desc, sd_chainp), cp); } /* * read the buffer pointer */ static inline u_int32_t sca_desc_read_bufp(struct sca_softc *sc, struct sca_desc *dp) { u_int32_t address; if (sc->sc_usedma) address = dp->sd_bufp | dp->sd_hbufp << 16; else { address = bus_space_read_2(sc->scu_memt, sc->scu_memh, sca_page_addr(sc, dp) + offsetof(struct sca_desc, sd_bufp)); address |= bus_space_read_1(sc->scu_memt, sc->scu_memh, sca_page_addr(sc, dp) + offsetof(struct sca_desc, sd_hbufp)) << 16; } return (address); } /* * write the buffer pointer */ static inline void sca_desc_write_bufp(struct sca_softc *sc, struct sca_desc *dp, u_int32_t bufp) { if (sc->sc_usedma) { dp->sd_bufp = bufp & 0xFFFF; dp->sd_hbufp = (bufp & 0x00FF0000) >> 16; } else { bus_space_write_2(sc->scu_memt, sc->scu_memh, sca_page_addr(sc, dp) + offsetof(struct sca_desc, sd_bufp), bufp & 0xFFFF); bus_space_write_1(sc->scu_memt, sc->scu_memh, sca_page_addr(sc, dp) + offsetof(struct sca_desc, sd_hbufp), (bufp & 0x00FF0000) >> 16); } } /* * read the buffer length */ static inline u_int16_t sca_desc_read_buflen(struct sca_softc *sc, struct sca_desc *dp) { if (sc->sc_usedma) return ((dp)->sd_buflen); return (bus_space_read_2(sc->scu_memt, sc->scu_memh, sca_page_addr(sc, dp) + offsetof(struct sca_desc, sd_buflen))); } /* * write the buffer length */ static inline void sca_desc_write_buflen(struct sca_softc *sc, struct sca_desc *dp, u_int16_t len) { if (sc->sc_usedma) (dp)->sd_buflen = len; else bus_space_write_2(sc->scu_memt, sc->scu_memh, sca_page_addr(sc, dp) + offsetof(struct sca_desc, sd_buflen), len); } /* * read the descriptor status */ static inline u_int8_t sca_desc_read_stat(struct sca_softc *sc, struct sca_desc *dp) { if (sc->sc_usedma) return ((dp)->sd_stat); return (bus_space_read_1(sc->scu_memt, sc->scu_memh, sca_page_addr(sc, dp) + offsetof(struct sca_desc, sd_stat))); } /* * write the descriptor status */ static inline void sca_desc_write_stat(struct sca_softc *sc, struct sca_desc *dp, u_int8_t stat) { if (sc->sc_usedma) (dp)->sd_stat = stat; else bus_space_write_1(sc->scu_memt, sc->scu_memh, sca_page_addr(sc, dp) + offsetof(struct sca_desc, sd_stat), stat); } void sca_init(struct sca_softc *sc) { /* * Do a little sanity check: check number of ports. */ if (sc->sc_numports < 1 || sc->sc_numports > 2) panic("sca can\'t handle more than 2 or less than 1 ports"); /* * disable DMA and MSCI interrupts */ sca_write_1(sc, SCA_DMER, 0); sca_write_1(sc, SCA_IER0, 0); sca_write_1(sc, SCA_IER1, 0); sca_write_1(sc, SCA_IER2, 0); /* * configure interrupt system */ sca_write_1(sc, SCA_ITCR, SCA_ITCR_INTR_PRI_MSCI | SCA_ITCR_ACK_NONE | SCA_ITCR_VOUT_IVR); #if 0 /* these are for the intrerrupt ack cycle which we don't use */ sca_write_1(sc, SCA_IVR, 0x40); sca_write_1(sc, SCA_IMVR, 0x40); #endif /* * set wait control register to zero wait states */ sca_write_1(sc, SCA_PABR0, 0); sca_write_1(sc, SCA_PABR1, 0); sca_write_1(sc, SCA_WCRL, 0); sca_write_1(sc, SCA_WCRM, 0); sca_write_1(sc, SCA_WCRH, 0); /* * disable DMA and reset status */ sca_write_1(sc, SCA_PCR, SCA_PCR_PR2); /* * disable transmit DMA for all channels */ sca_write_1(sc, SCA_DSR0 + SCA_DMAC_OFF_0, 0); sca_write_1(sc, SCA_DCR0 + SCA_DMAC_OFF_0, SCA_DCR_ABRT); sca_write_1(sc, SCA_DSR1 + SCA_DMAC_OFF_0, 0); sca_write_1(sc, SCA_DCR1 + SCA_DMAC_OFF_0, SCA_DCR_ABRT); sca_write_1(sc, SCA_DSR0 + SCA_DMAC_OFF_1, 0); sca_write_1(sc, SCA_DCR0 + SCA_DMAC_OFF_1, SCA_DCR_ABRT); sca_write_1(sc, SCA_DSR1 + SCA_DMAC_OFF_1, 0); sca_write_1(sc, SCA_DCR1 + SCA_DMAC_OFF_1, SCA_DCR_ABRT); /* * enable DMA based on channel enable flags for each channel */ sca_write_1(sc, SCA_DMER, SCA_DMER_EN); /* * Should check to see if the chip is responding, but for now * assume it is. */ } /* * initialize the port and attach it to the networking layer */ void sca_port_attach(struct sca_softc *sc, u_int port) { sca_port_t *scp = &sc->sc_ports[port]; struct ifnet *ifp; static u_int ntwo_unit = 0; scp->sca = sc; /* point back to the parent */ scp->sp_port = port; if (port == 0) { scp->msci_off = SCA_MSCI_OFF_0; scp->dmac_off = SCA_DMAC_OFF_0; if(sc->sc_parent != NULL) ntwo_unit=sc->sc_parent->dv_unit * 2 + 0; else ntwo_unit = 0; /* XXX */ } else { scp->msci_off = SCA_MSCI_OFF_1; scp->dmac_off = SCA_DMAC_OFF_1; if(sc->sc_parent != NULL) ntwo_unit=sc->sc_parent->dv_unit * 2 + 1; else ntwo_unit = 1; /* XXX */ } sca_msci_init(sc, scp); sca_dmac_init(sc, scp); /* * attach to the network layer */ ifp = &scp->sp_if; sprintf(ifp->if_xname, "ntwo%d", ntwo_unit); ifp->if_softc = scp; ifp->if_mtu = SCA_MTU; ifp->if_flags = IFF_POINTOPOINT | IFF_MULTICAST; ifp->if_type = IFT_PTPSERIAL; ifp->if_hdrlen = HDLC_HDRLEN; ifp->if_ioctl = sca_ioctl; ifp->if_output = sca_output; ifp->if_watchdog = sca_watchdog; ifp->if_snd.ifq_maxlen = IFQ_MAXLEN; scp->linkq.ifq_maxlen = 5; /* if we exceed this we are hosed already */ #ifdef SCA_USE_FASTQ scp->fastq.ifq_maxlen = IFQ_MAXLEN; #endif IFQ_SET_READY(&ifp->if_snd); if_attach(ifp); if_alloc_sadl(ifp); #if NBPFILTER > 0 bpfattach(ifp, DLT_HDLC, HDLC_HDRLEN); #endif if (sc->sc_parent == NULL) printf("%s: port %d\n", ifp->if_xname, port); else printf("%s at %s port %d\n", ifp->if_xname, sc->sc_parent->dv_xname, port); /* * reset the last seen times on the cisco keepalive protocol */ scp->cka_lasttx = time.tv_usec; scp->cka_lastrx = 0; } #if 0 /* * returns log2(div), sets 'tmc' for the required freq 'hz' */ static u_int8_t sca_msci_get_baud_rate_values(u_int32_t hz, u_int8_t *tmcp) { u_int32_t tmc, div; u_int32_t clock; /* clock hz = (chipclock / tmc) / 2^(div); */ /* * TD == tmc * 2^(n) * * note: * 1 <= TD <= 256 TD is inc of 1 * 2 <= TD <= 512 TD is inc of 2 * 4 <= TD <= 1024 TD is inc of 4 * ... * 512 <= TD <= 256*512 TD is inc of 512 * * so note there are overlaps. We lose prec * as div increases so we wish to minize div. * * basically we want to do * * tmc = chip / hz, but have tmc <= 256 */ /* assume system clock is 9.8304MHz or 9830400Hz */ clock = clock = 9830400 >> 1; /* round down */ div = 0; while ((tmc = clock / hz) > 256 || (tmc == 256 && (clock / tmc) > hz)) { clock >>= 1; div++; } if (clock / tmc > hz) tmc++; if (!tmc) tmc = 1; if (div > SCA_RXS_DIV_512) { /* set to maximums */ div = SCA_RXS_DIV_512; tmc = 0; } *tmcp = (tmc & 0xFF); /* 0 == 256 */ return (div & 0xFF); } #endif /* * initialize the port's MSCI */ static void sca_msci_init(struct sca_softc *sc, sca_port_t *scp) { /* reset the channel */ msci_write_1(scp, SCA_CMD0, SCA_CMD_RESET); msci_write_1(scp, SCA_MD00, ( SCA_MD0_CRC_1 | SCA_MD0_CRC_CCITT | SCA_MD0_CRC_ENABLE | SCA_MD0_MODE_HDLC)); #if 0 /* immediately send receive reset so the above takes */ msci_write_1(scp, SCA_CMD0, SCA_CMD_RXRESET); #endif msci_write_1(scp, SCA_MD10, SCA_MD1_NOADDRCHK); msci_write_1(scp, SCA_MD20, (SCA_MD2_DUPLEX | SCA_MD2_ADPLLx8 | SCA_MD2_NRZ)); /* be safe and do it again */ msci_write_1(scp, SCA_CMD0, SCA_CMD_RXRESET); /* setup underrun and idle control, and initial RTS state */ msci_write_1(scp, SCA_CTL0, (SCA_CTL_IDLC_PATTERN | SCA_CTL_UDRNC_AFTER_FCS | SCA_CTL_RTS_LOW)); /* reset the transmitter */ msci_write_1(scp, SCA_CMD0, SCA_CMD_TXRESET); /* * set the clock sources */ msci_write_1(scp, SCA_RXS0, scp->sp_rxs); msci_write_1(scp, SCA_TXS0, scp->sp_txs); msci_write_1(scp, SCA_TMC0, scp->sp_tmc); /* set external clock generate as requested */ sc->sc_clock_callback(sc->sc_aux, scp->sp_port, scp->sp_eclock); /* * XXX don't pay attention to CTS or CD changes right now. I can't * simulate one, and the transmitter will try to transmit even if * CD isn't there anyway, so nothing bad SHOULD happen. */ #if 0 msci_write_1(scp, SCA_IE00, 0); msci_write_1(scp, SCA_IE10, 0); /* 0x0c == CD and CTS changes only */ #else /* this would deliver transmitter underrun to ST1/ISR1 */ msci_write_1(scp, SCA_IE10, SCA_ST1_UDRN); msci_write_1(scp, SCA_IE00, SCA_ST0_TXINT); #endif msci_write_1(scp, SCA_IE20, 0); msci_write_1(scp, SCA_FIE0, 0); msci_write_1(scp, SCA_SA00, 0); msci_write_1(scp, SCA_SA10, 0); msci_write_1(scp, SCA_IDL0, 0x7e); msci_write_1(scp, SCA_RRC0, 0x0e); /* msci_write_1(scp, SCA_TRC00, 0x10); */ /* * the correct values here are important for avoiding underruns * for any value less than or equal to TRC0 txrdy is activated * which will start the dmac transfer to the fifo. * for buffer size >= TRC1 + 1 txrdy is cleared which will stop DMA. * * thus if we are using a very fast clock that empties the fifo * quickly, delays in the dmac starting to fill the fifo can * lead to underruns so we want a fairly full fifo to still * cause the dmac to start. for cards with on board ram this * has no effect on system performance. For cards that DMA * to/from system memory it will cause more, shorter, * bus accesses rather than fewer longer ones. */ msci_write_1(scp, SCA_TRC00, 0x00); msci_write_1(scp, SCA_TRC10, 0x1f); } /* * Take the memory for the port and construct two circular linked lists of * descriptors (one tx, one rx) and set the pointers in these descriptors * to point to the buffer space for this port. */ static void sca_dmac_init(struct sca_softc *sc, sca_port_t *scp) { sca_desc_t *desc; u_int32_t desc_p; u_int32_t buf_p; int i; if (sc->sc_usedma) bus_dmamap_sync(sc->scu_dmat, sc->scu_dmam, 0, sc->scu_allocsize, BUS_DMASYNC_PREWRITE); else { /* * XXX assumes that all tx desc and bufs in same page */ sc->scu_page_on(sc); sc->scu_set_page(sc, scp->sp_txdesc_p); } desc = scp->sp_txdesc; desc_p = scp->sp_txdesc_p; buf_p = scp->sp_txbuf_p; scp->sp_txcur = 0; scp->sp_txinuse = 0; #ifdef DEBUG /* make sure that we won't wrap */ if ((desc_p & 0xffff0000) != ((desc_p + sizeof(*desc) * scp->sp_ntxdesc) & 0xffff0000)) panic("sca: tx descriptors cross architecural boundry"); if ((buf_p & 0xff000000) != ((buf_p + SCA_BSIZE * scp->sp_ntxdesc) & 0xff000000)) panic("sca: tx buffers cross architecural boundry"); #endif for (i = 0 ; i < scp->sp_ntxdesc ; i++) { /* * desc_p points to the physcial address of the NEXT desc */ desc_p += sizeof(sca_desc_t); sca_desc_write_chainp(sc, desc, desc_p & 0x0000ffff); sca_desc_write_bufp(sc, desc, buf_p); sca_desc_write_buflen(sc, desc, SCA_BSIZE); sca_desc_write_stat(sc, desc, 0); desc++; /* point to the next descriptor */ buf_p += SCA_BSIZE; } /* * "heal" the circular list by making the last entry point to the * first. */ sca_desc_write_chainp(sc, desc - 1, scp->sp_txdesc_p & 0x0000ffff); /* * Now, initialize the transmit DMA logic * * CPB == chain pointer base address */ dmac_write_1(scp, SCA_DSR1, 0); dmac_write_1(scp, SCA_DCR1, SCA_DCR_ABRT); dmac_write_1(scp, SCA_DMR1, SCA_DMR_TMOD | SCA_DMR_NF); /* XXX1 dmac_write_1(scp, SCA_DIR1, (SCA_DIR_EOT | SCA_DIR_BOF | SCA_DIR_COF)); */ dmac_write_1(scp, SCA_DIR1, (SCA_DIR_EOM | SCA_DIR_EOT | SCA_DIR_BOF | SCA_DIR_COF)); dmac_write_1(scp, SCA_CPB1, (u_int8_t)((scp->sp_txdesc_p & 0x00ff0000) >> 16)); /* * now, do the same thing for receive descriptors * * XXX assumes that all rx desc and bufs in same page */ if (!sc->sc_usedma) sc->scu_set_page(sc, scp->sp_rxdesc_p); desc = scp->sp_rxdesc; desc_p = scp->sp_rxdesc_p; buf_p = scp->sp_rxbuf_p; #ifdef DEBUG /* make sure that we won't wrap */ if ((desc_p & 0xffff0000) != ((desc_p + sizeof(*desc) * scp->sp_nrxdesc) & 0xffff0000)) panic("sca: rx descriptors cross architecural boundry"); if ((buf_p & 0xff000000) != ((buf_p + SCA_BSIZE * scp->sp_nrxdesc) & 0xff000000)) panic("sca: rx buffers cross architecural boundry"); #endif for (i = 0 ; i < scp->sp_nrxdesc; i++) { /* * desc_p points to the physcial address of the NEXT desc */ desc_p += sizeof(sca_desc_t); sca_desc_write_chainp(sc, desc, desc_p & 0x0000ffff); sca_desc_write_bufp(sc, desc, buf_p); /* sca_desc_write_buflen(sc, desc, SCA_BSIZE); */ sca_desc_write_buflen(sc, desc, 0); sca_desc_write_stat(sc, desc, 0); desc++; /* point to the next descriptor */ buf_p += SCA_BSIZE; } /* * "heal" the circular list by making the last entry point to the * first. */ sca_desc_write_chainp(sc, desc - 1, scp->sp_rxdesc_p & 0x0000ffff); sca_dmac_rxinit(scp); if (sc->sc_usedma) bus_dmamap_sync(sc->scu_dmat, sc->scu_dmam, 0, sc->scu_allocsize, BUS_DMASYNC_POSTWRITE); else sc->scu_page_off(sc); } /* * reset and reinitialize the receive DMA logic */ static void sca_dmac_rxinit(sca_port_t *scp) { /* * ... and the receive DMA logic ... */ dmac_write_1(scp, SCA_DSR0, 0); /* disable DMA */ dmac_write_1(scp, SCA_DCR0, SCA_DCR_ABRT); dmac_write_1(scp, SCA_DMR0, SCA_DMR_TMOD | SCA_DMR_NF); dmac_write_2(scp, SCA_BFLL0, SCA_BSIZE); /* reset descriptors to initial state */ scp->sp_rxstart = 0; scp->sp_rxend = scp->sp_nrxdesc - 1; /* * CPB == chain pointer base * CDA == current descriptor address * EDA == error descriptor address (overwrite position) * because cda can't be eda when starting we always * have a single buffer gap between cda and eda */ dmac_write_1(scp, SCA_CPB0, (u_int8_t)((scp->sp_rxdesc_p & 0x00ff0000) >> 16)); dmac_write_2(scp, SCA_CDAL0, (u_int16_t)(scp->sp_rxdesc_p & 0xffff)); dmac_write_2(scp, SCA_EDAL0, (u_int16_t) (scp->sp_rxdesc_p + (sizeof(sca_desc_t) * scp->sp_rxend))); /* * enable receiver DMA */ dmac_write_1(scp, SCA_DIR0, (SCA_DIR_EOT | SCA_DIR_EOM | SCA_DIR_BOF | SCA_DIR_COF)); dmac_write_1(scp, SCA_DSR0, SCA_DSR_DE); } /* * Queue the packet for our start routine to transmit */ static int sca_output(ifp, m, dst, rt0) struct ifnet *ifp; struct mbuf *m; struct sockaddr *dst; struct rtentry *rt0; { #ifdef ISO struct hdlc_llc_header *llc; #endif struct hdlc_header *hdlc; struct ifqueue *ifq = NULL; int s, error, len; short mflags; ALTQ_DECL(struct altq_pktattr pktattr;) error = 0; if ((ifp->if_flags & IFF_UP) != IFF_UP) { error = ENETDOWN; goto bad; } /* * If the queueing discipline needs packet classification, * do it before prepending link headers. */ IFQ_CLASSIFY(&ifp->if_snd, m, dst->sa_family, &pktattr); /* * determine address family, and priority for this packet */ switch (dst->sa_family) { #ifdef INET case AF_INET: #ifdef SCA_USE_FASTQ if ((mtod(m, struct ip *)->ip_tos & IPTOS_LOWDELAY) == IPTOS_LOWDELAY) ifq = &((sca_port_t *)ifp->if_softc)->fastq; #endif /* * Add cisco serial line header. If there is no * space in the first mbuf, allocate another. */ M_PREPEND(m, sizeof(struct hdlc_header), M_DONTWAIT); if (m == 0) return (ENOBUFS); hdlc = mtod(m, struct hdlc_header *); hdlc->h_proto = htons(HDLC_PROTOCOL_IP); break; #endif #ifdef INET6 case AF_INET6: /* * Add cisco serial line header. If there is no * space in the first mbuf, allocate another. */ M_PREPEND(m, sizeof(struct hdlc_header), M_DONTWAIT); if (m == 0) return (ENOBUFS); hdlc = mtod(m, struct hdlc_header *); hdlc->h_proto = htons(HDLC_PROTOCOL_IPV6); break; #endif #ifdef ISO case AF_ISO: /* * Add cisco llc serial line header. If there is no * space in the first mbuf, allocate another. */ M_PREPEND(m, sizeof(struct hdlc_llc_header), M_DONTWAIT); if (m == 0) return (ENOBUFS); hdlc = mtod(m, struct hdlc_header *); llc = mtod(m, struct hdlc_llc_header *); llc->hl_dsap = llc->hl_ssap = LLC_ISO_LSAP; llc->hl_ffb = 0; break; #endif default: printf("%s: address family %d unsupported\n", ifp->if_xname, dst->sa_family); error = EAFNOSUPPORT; goto bad; } /* finish */ if ((m->m_flags & (M_BCAST | M_MCAST)) != 0) hdlc->h_addr = CISCO_MULTICAST; else hdlc->h_addr = CISCO_UNICAST; hdlc->h_resv = 0; /* * queue the packet. If interactive, use the fast queue. */ mflags = m->m_flags; len = m->m_pkthdr.len; s = splnet(); if (ifq != NULL) { if (IF_QFULL(ifq)) { IF_DROP(ifq); m_freem(m); error = ENOBUFS; } else IF_ENQUEUE(ifq, m); } else IFQ_ENQUEUE(&ifp->if_snd, m, &pktattr, error); if (error != 0) { splx(s); ifp->if_oerrors++; ifp->if_collisions++; return (error); } ifp->if_obytes += len; if (mflags & M_MCAST) ifp->if_omcasts++; sca_start(ifp); splx(s); return (error); bad: if (m) m_freem(m); return (error); } static int sca_ioctl(ifp, cmd, addr) struct ifnet *ifp; u_long cmd; caddr_t addr; { struct ifreq *ifr; struct ifaddr *ifa; int error; int s; s = splnet(); ifr = (struct ifreq *)addr; ifa = (struct ifaddr *)addr; error = 0; switch (cmd) { case SIOCSIFADDR: switch(ifa->ifa_addr->sa_family) { #ifdef INET case AF_INET: #endif #ifdef INET6 case AF_INET6: #endif #if defined(INET) || defined(INET6) ifp->if_flags |= IFF_UP; sca_port_up(ifp->if_softc); break; #endif default: error = EAFNOSUPPORT; break; } break; case SIOCSIFDSTADDR: #ifdef INET if (ifa->ifa_addr->sa_family == AF_INET) break; #endif #ifdef INET6 if (ifa->ifa_addr->sa_family == AF_INET6) break; #endif error = EAFNOSUPPORT; break; case SIOCADDMULTI: case SIOCDELMULTI: /* XXX need multicast group management code */ if (ifr == 0) { error = EAFNOSUPPORT; /* XXX */ break; } switch (ifr->ifr_addr.sa_family) { #ifdef INET case AF_INET: break; #endif #ifdef INET6 case AF_INET6: break; #endif default: error = EAFNOSUPPORT; break; } break; case SIOCSIFFLAGS: if (ifr->ifr_flags & IFF_UP) { ifp->if_flags |= IFF_UP; sca_port_up(ifp->if_softc); } else { ifp->if_flags &= ~IFF_UP; sca_port_down(ifp->if_softc); } break; default: error = EINVAL; } splx(s); return error; } /* * start packet transmission on the interface * * MUST BE CALLED AT splnet() */ static void sca_start(ifp) struct ifnet *ifp; { sca_port_t *scp = ifp->if_softc; struct sca_softc *sc = scp->sca; struct mbuf *m, *mb_head; sca_desc_t *desc; u_int8_t *buf, stat; u_int32_t buf_p; int nexttx; int trigger_xmit; u_int len; SCA_DPRINTF(SCA_DEBUG_TX, ("TX: enter start\n")); /* * can't queue when we are full or transmitter is busy */ #ifdef oldcode if ((scp->sp_txinuse >= (scp->sp_ntxdesc - 1)) || ((ifp->if_flags & IFF_OACTIVE) == IFF_OACTIVE)) return; #else if (scp->sp_txinuse || ((ifp->if_flags & IFF_OACTIVE) == IFF_OACTIVE)) return; #endif SCA_DPRINTF(SCA_DEBUG_TX, ("TX: txinuse %d\n", scp->sp_txinuse)); /* * XXX assume that all tx desc and bufs in same page */ if (sc->sc_usedma) bus_dmamap_sync(sc->scu_dmat, sc->scu_dmam, 0, sc->scu_allocsize, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); else { sc->scu_page_on(sc); sc->scu_set_page(sc, scp->sp_txdesc_p); } trigger_xmit = 0; txloop: IF_DEQUEUE(&scp->linkq, mb_head); if (mb_head == NULL) #ifdef SCA_USE_FASTQ IF_DEQUEUE(&scp->fastq, mb_head); if (mb_head == NULL) #endif IFQ_DEQUEUE(&ifp->if_snd, mb_head); if (mb_head == NULL) goto start_xmit; SCA_DPRINTF(SCA_DEBUG_TX, ("TX: got mbuf\n")); #ifdef oldcode if (scp->txinuse != 0) { /* Kill EOT interrupts on the previous descriptor. */ desc = &scp->sp_txdesc[scp->txcur]; stat = sca_desc_read_stat(sc, desc); sca_desc_write_stat(sc, desc, stat & ~SCA_DESC_EOT); /* Figure out what the next free descriptor is. */ nexttx = (scp->sp_txcur + 1) % scp->sp_ntxdesc; } else nexttx = 0; #endif /* oldcode */ if (scp->sp_txinuse) nexttx = (scp->sp_txcur + 1) % scp->sp_ntxdesc; else nexttx = 0; SCA_DPRINTF(SCA_DEBUG_TX, ("TX: nexttx %d\n", nexttx)); buf = scp->sp_txbuf + SCA_BSIZE * nexttx; buf_p = scp->sp_txbuf_p + SCA_BSIZE * nexttx; /* XXX hoping we can delay the desc write till after we don't drop. */ desc = &scp->sp_txdesc[nexttx]; /* XXX isn't this set already?? */ sca_desc_write_bufp(sc, desc, buf_p); len = 0; SCA_DPRINTF(SCA_DEBUG_TX, ("TX: buf %x buf_p %x\n", (u_int)buf, buf_p)); #if 0 /* uncomment this for a core in cc1 */ X #endif /* * Run through the chain, copying data into the descriptor as we * go. If it won't fit in one transmission block, drop the packet. * No, this isn't nice, but most of the time it _will_ fit. */ for (m = mb_head ; m != NULL ; m = m->m_next) { if (m->m_len != 0) { len += m->m_len; if (len > SCA_BSIZE) { m_freem(mb_head); goto txloop; } SCA_DPRINTF(SCA_DEBUG_TX, ("TX: about to mbuf len %d\n", m->m_len)); if (sc->sc_usedma) memcpy(buf, mtod(m, u_int8_t *), m->m_len); else bus_space_write_region_1(sc->scu_memt, sc->scu_memh, sca_page_addr(sc, buf_p), mtod(m, u_int8_t *), m->m_len); buf += m->m_len; buf_p += m->m_len; } } /* set the buffer, the length, and mark end of frame and end of xfer */ sca_desc_write_buflen(sc, desc, len); sca_desc_write_stat(sc, desc, SCA_DESC_EOM); ifp->if_opackets++; #if NBPFILTER > 0 /* * Pass packet to bpf if there is a listener. */ if (ifp->if_bpf) bpf_mtap(ifp->if_bpf, mb_head); #endif m_freem(mb_head); scp->sp_txcur = nexttx; scp->sp_txinuse++; trigger_xmit = 1; SCA_DPRINTF(SCA_DEBUG_TX, ("TX: inuse %d index %d\n", scp->sp_txinuse, scp->sp_txcur)); /* * XXX so didn't this used to limit us to 1?! - multi may be untested * sp_ntxdesc used to be hard coded to 2 with claim of a too hard * to find bug */ #ifdef oldcode if (scp->sp_txinuse < (scp->sp_ntxdesc - 1)) #endif if (scp->sp_txinuse < scp->sp_ntxdesc) goto txloop; start_xmit: SCA_DPRINTF(SCA_DEBUG_TX, ("TX: trigger_xmit %d\n", trigger_xmit)); if (trigger_xmit != 0) { /* set EOT on final descriptor */ desc = &scp->sp_txdesc[scp->sp_txcur]; stat = sca_desc_read_stat(sc, desc); sca_desc_write_stat(sc, desc, stat | SCA_DESC_EOT); } if (sc->sc_usedma) bus_dmamap_sync(sc->scu_dmat, sc->scu_dmam, 0, sc->scu_allocsize, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); if (trigger_xmit != 0) sca_port_starttx(scp); if (!sc->sc_usedma) sc->scu_page_off(sc); } static void sca_watchdog(ifp) struct ifnet *ifp; { } int sca_hardintr(struct sca_softc *sc) { u_int8_t isr0, isr1, isr2; int ret; ret = 0; /* non-zero means we processed at least one interrupt */ SCA_DPRINTF(SCA_DEBUG_INTR, ("sca_hardintr entered\n")); while (1) { /* * read SCA interrupts */ isr0 = sca_read_1(sc, SCA_ISR0); isr1 = sca_read_1(sc, SCA_ISR1); isr2 = sca_read_1(sc, SCA_ISR2); if (isr0 == 0 && isr1 == 0 && isr2 == 0) break; SCA_DPRINTF(SCA_DEBUG_INTR, ("isr0 = %02x, isr1 = %02x, isr2 = %02x\n", isr0, isr1, isr2)); /* * check DMAC interrupt */ if (isr1 & 0x0f) ret += sca_dmac_intr(&sc->sc_ports[0], isr1 & 0x0f); if (isr1 & 0xf0) ret += sca_dmac_intr(&sc->sc_ports[1], (isr1 & 0xf0) >> 4); /* * mcsi intterupts */ if (isr0 & 0x0f) ret += sca_msci_intr(&sc->sc_ports[0], isr0 & 0x0f); if (isr0 & 0xf0) ret += sca_msci_intr(&sc->sc_ports[1], (isr0 & 0xf0) >> 4); #if 0 /* We don't GET timer interrupts, we have them disabled (msci IE20) */ if (isr2) ret += sca_timer_intr(sc, isr2); #endif } return (ret); } static int sca_dmac_intr(sca_port_t *scp, u_int8_t isr) { u_int8_t dsr; int ret; ret = 0; /* * Check transmit channel */ if (isr & (SCA_ISR1_DMAC_TX0A | SCA_ISR1_DMAC_TX0B)) { SCA_DPRINTF(SCA_DEBUG_INTR, ("TX INTERRUPT port %d\n", scp->sp_port)); dsr = 1; while (dsr != 0) { ret++; /* * reset interrupt */ dsr = dmac_read_1(scp, SCA_DSR1); dmac_write_1(scp, SCA_DSR1, dsr | SCA_DSR_DEWD); /* * filter out the bits we don't care about */ dsr &= ( SCA_DSR_COF | SCA_DSR_BOF | SCA_DSR_EOT); if (dsr == 0) break; /* * check for counter overflow */ if (dsr & SCA_DSR_COF) { printf("%s: TXDMA counter overflow\n", scp->sp_if.if_xname); scp->sp_if.if_flags &= ~IFF_OACTIVE; scp->sp_txcur = 0; scp->sp_txinuse = 0; } /* * check for buffer overflow */ if (dsr & SCA_DSR_BOF) { printf("%s: TXDMA buffer overflow, cda 0x%04x, eda 0x%04x, cpb 0x%02x\n", scp->sp_if.if_xname, dmac_read_2(scp, SCA_CDAL1), dmac_read_2(scp, SCA_EDAL1), dmac_read_1(scp, SCA_CPB1)); /* * Yikes. Arrange for a full * transmitter restart. */ scp->sp_if.if_flags &= ~IFF_OACTIVE; scp->sp_txcur = 0; scp->sp_txinuse = 0; } /* * check for end of transfer, which is not * an error. It means that all data queued * was transmitted, and we mark ourself as * not in use and stop the watchdog timer. */ if (dsr & SCA_DSR_EOT) { SCA_DPRINTF(SCA_DEBUG_TX, ("Transmit completed. cda %x eda %x dsr %x\n", dmac_read_2(scp, SCA_CDAL1), dmac_read_2(scp, SCA_EDAL1), dsr)); scp->sp_if.if_flags &= ~IFF_OACTIVE; scp->sp_txcur = 0; scp->sp_txinuse = 0; /* * check for more packets */ sca_start(&scp->sp_if); } } } /* * receive channel check */ if (isr & (SCA_ISR1_DMAC_RX0A | SCA_ISR1_DMAC_RX0B)) { SCA_DPRINTF(SCA_DEBUG_INTR, ("RX INTERRUPT port %d\n", (scp == &scp->sca->sc_ports[0] ? 0 : 1))); dsr = 1; while (dsr != 0) { ret++; dsr = dmac_read_1(scp, SCA_DSR0); dmac_write_1(scp, SCA_DSR0, dsr | SCA_DSR_DEWD); /* * filter out the bits we don't care about */ dsr &= (SCA_DSR_EOM | SCA_DSR_COF | SCA_DSR_BOF | SCA_DSR_EOT); if (dsr == 0) break; /* * End of frame */ if (dsr & SCA_DSR_EOM) { SCA_DPRINTF(SCA_DEBUG_RX, ("Got a frame!\n")); sca_get_packets(scp); } /* * check for counter overflow */ if (dsr & SCA_DSR_COF) { printf("%s: RXDMA counter overflow\n", scp->sp_if.if_xname); sca_dmac_rxinit(scp); } /* * check for end of transfer, which means we * ran out of descriptors to receive into. * This means the line is much faster than * we can handle. */ if (dsr & (SCA_DSR_BOF | SCA_DSR_EOT)) { printf("%s: RXDMA buffer overflow\n", scp->sp_if.if_xname); sca_dmac_rxinit(scp); } } } return ret; } static int sca_msci_intr(sca_port_t *scp, u_int8_t isr) { u_int8_t st1, trc0; /* get and clear the specific interrupt -- should act on it :)*/ if ((st1 = msci_read_1(scp, SCA_ST10))) { /* clear the interrupt */ msci_write_1(scp, SCA_ST10, st1); if (st1 & SCA_ST1_UDRN) { /* underrun -- try to increase ready control */ trc0 = msci_read_1(scp, SCA_TRC00); if (trc0 == 0x1f) printf("TX: underrun - fifo depth maxed\n"); else { if ((trc0 += 2) > 0x1f) trc0 = 0x1f; SCA_DPRINTF(SCA_DEBUG_TX, ("TX: udrn - incr fifo to %d\n", trc0)); msci_write_1(scp, SCA_TRC00, trc0); } } } return (0); } static void sca_get_packets(sca_port_t *scp) { struct sca_softc *sc; SCA_DPRINTF(SCA_DEBUG_RX, ("RX: sca_get_packets\n")); sc = scp->sca; if (sc->sc_usedma) bus_dmamap_sync(sc->scu_dmat, sc->scu_dmam, 0, sc->scu_allocsize, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); else { /* * XXX this code is unable to deal with rx stuff * in more than 1 page */ sc->scu_page_on(sc); sc->scu_set_page(sc, scp->sp_rxdesc_p); } /* process as many frames as are available */ while (sca_frame_avail(scp)) { sca_frame_process(scp); sca_frame_read_done(scp); } if (sc->sc_usedma) bus_dmamap_sync(sc->scu_dmat, sc->scu_dmam, 0, sc->scu_allocsize, BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); else sc->scu_page_off(sc); } /* * Starting with the first descriptor we wanted to read into, up to but * not including the current SCA read descriptor, look for a packet. * * must be called at splnet() */ static int sca_frame_avail(sca_port_t *scp) { struct sca_softc *sc; u_int16_t cda; u_int32_t desc_p; /* physical address (lower 16 bits) */ sca_desc_t *desc; u_int8_t rxstat; int cdaidx, toolong; /* * Read the current descriptor from the SCA. */ sc = scp->sca; cda = dmac_read_2(scp, SCA_CDAL0); /* * calculate the index of the current descriptor */ desc_p = (scp->sp_rxdesc_p & 0xFFFF); desc_p = cda - desc_p; cdaidx = desc_p / sizeof(sca_desc_t); SCA_DPRINTF(SCA_DEBUG_RX, ("RX: cda %x desc_p %x cdaidx %u, nrxdesc %d rxstart %d\n", cda, desc_p, cdaidx, scp->sp_nrxdesc, scp->sp_rxstart)); /* note confusion */ if (cdaidx >= scp->sp_nrxdesc) panic("current descriptor index out of range"); /* see if we have a valid frame available */ toolong = 0; for (; scp->sp_rxstart != cdaidx; sca_frame_read_done(scp)) { /* * We might have a valid descriptor. Set up a pointer * to the kva address for it so we can more easily examine * the contents. */ desc = &scp->sp_rxdesc[scp->sp_rxstart]; rxstat = sca_desc_read_stat(scp->sca, desc); SCA_DPRINTF(SCA_DEBUG_RX, ("port %d RX: idx %d rxstat %x\n", scp->sp_port, scp->sp_rxstart, rxstat)); SCA_DPRINTF(SCA_DEBUG_RX, ("port %d RX: buflen %d\n", scp->sp_port, sca_desc_read_buflen(scp->sca, desc))); /* * check for errors */ if (rxstat & SCA_DESC_ERRORS) { /* * consider an error condition the end * of a frame */ scp->sp_if.if_ierrors++; toolong = 0; continue; } /* * if we aren't skipping overlong frames * we are done, otherwise reset and look for * another good frame */ if (rxstat & SCA_DESC_EOM) { if (!toolong) return (1); toolong = 0; } else if (!toolong) { /* * we currently don't deal with frames * larger than a single buffer (fixed MTU) */ scp->sp_if.if_ierrors++; toolong = 1; } SCA_DPRINTF(SCA_DEBUG_RX, ("RX: idx %d no EOM\n", scp->sp_rxstart)); } SCA_DPRINTF(SCA_DEBUG_RX, ("RX: returning none\n")); return 0; } /* * Pass the packet up to the kernel if it is a packet we want to pay * attention to. * * MUST BE CALLED AT splnet() */ static void sca_frame_process(sca_port_t *scp) { struct ifqueue *ifq; struct hdlc_header *hdlc; struct cisco_pkt *cisco; sca_desc_t *desc; struct mbuf *m; u_int8_t *bufp; u_int16_t len; u_int32_t t; t = (time.tv_sec - boottime.tv_sec) * 1000; desc = &scp->sp_rxdesc[scp->sp_rxstart]; bufp = scp->sp_rxbuf + SCA_BSIZE * scp->sp_rxstart; len = sca_desc_read_buflen(scp->sca, desc); SCA_DPRINTF(SCA_DEBUG_RX, ("RX: desc %lx bufp %lx len %d\n", (bus_addr_t)desc, (bus_addr_t)bufp, len)); #if SCA_DEBUG_LEVEL > 0 if (sca_debug & SCA_DEBUG_RXPKT) sca_frame_print(scp, desc, bufp); #endif /* * skip packets that are too short */ if (len < sizeof(struct hdlc_header)) { scp->sp_if.if_ierrors++; return; } m = sca_mbuf_alloc(scp->sca, bufp, len); if (m == NULL) { SCA_DPRINTF(SCA_DEBUG_RX, ("RX: no mbuf!\n")); return; } /* * read and then strip off the HDLC information */ m = m_pullup(m, sizeof(struct hdlc_header)); if (m == NULL) { SCA_DPRINTF(SCA_DEBUG_RX, ("RX: no m_pullup!\n")); return; } #if NBPFILTER > 0 if (scp->sp_if.if_bpf) bpf_mtap(scp->sp_if.if_bpf, m); #endif scp->sp_if.if_ipackets++; hdlc = mtod(m, struct hdlc_header *); switch (ntohs(hdlc->h_proto)) { #ifdef INET case HDLC_PROTOCOL_IP: SCA_DPRINTF(SCA_DEBUG_RX, ("Received IP packet\n")); m->m_pkthdr.rcvif = &scp->sp_if; m->m_pkthdr.len -= sizeof(struct hdlc_header); m->m_data += sizeof(struct hdlc_header); m->m_len -= sizeof(struct hdlc_header); ifq = &ipintrq; schednetisr(NETISR_IP); break; #endif /* INET */ #ifdef INET6 case HDLC_PROTOCOL_IPV6: SCA_DPRINTF(SCA_DEBUG_RX, ("Received IP packet\n")); m->m_pkthdr.rcvif = &scp->sp_if; m->m_pkthdr.len -= sizeof(struct hdlc_header); m->m_data += sizeof(struct hdlc_header); m->m_len -= sizeof(struct hdlc_header); ifq = &ip6intrq; schednetisr(NETISR_IPV6); break; #endif /* INET6 */ #ifdef ISO case HDLC_PROTOCOL_ISO: if (m->m_pkthdr.len < sizeof(struct hdlc_llc_header)) goto dropit; m->m_pkthdr.rcvif = &scp->sp_if; m->m_pkthdr.len -= sizeof(struct hdlc_llc_header); m->m_data += sizeof(struct hdlc_llc_header); m->m_len -= sizeof(struct hdlc_llc_header); ifq = &clnlintrq; schednetisr(NETISR_ISO); break; #endif /* ISO */ case CISCO_KEEPALIVE: SCA_DPRINTF(SCA_DEBUG_CISCO, ("Received CISCO keepalive packet\n")); if (len < CISCO_PKT_LEN) { SCA_DPRINTF(SCA_DEBUG_CISCO, ("short CISCO packet %d, wanted %d\n", len, CISCO_PKT_LEN)); scp->sp_if.if_ierrors++; goto dropit; } m = m_pullup(m, sizeof(struct cisco_pkt)); if (m == NULL) { SCA_DPRINTF(SCA_DEBUG_RX, ("RX: no m_pullup!\n")); return; } cisco = (struct cisco_pkt *) (mtod(m, u_int8_t *) + HDLC_HDRLEN); m->m_pkthdr.rcvif = &scp->sp_if; switch (ntohl(cisco->type)) { case CISCO_ADDR_REQ: printf("Got CISCO addr_req, ignoring\n"); scp->sp_if.if_ierrors++; goto dropit; case CISCO_ADDR_REPLY: printf("Got CISCO addr_reply, ignoring\n"); scp->sp_if.if_ierrors++; goto dropit; case CISCO_KEEPALIVE_REQ: SCA_DPRINTF(SCA_DEBUG_CISCO, ("Received KA, mseq %d," " yseq %d, rel 0x%04x, t0" " %04x, t1 %04x\n", ntohl(cisco->par1), ntohl(cisco->par2), ntohs(cisco->rel), ntohs(cisco->time0), ntohs(cisco->time1))); scp->cka_lastrx = ntohl(cisco->par1); scp->cka_lasttx++; /* * schedule the transmit right here. */ cisco->par2 = cisco->par1; cisco->par1 = htonl(scp->cka_lasttx); cisco->time0 = htons((u_int16_t)(t >> 16)); cisco->time1 = htons((u_int16_t)(t & 0x0000ffff)); ifq = &scp->linkq; if (IF_QFULL(ifq)) { IF_DROP(ifq); goto dropit; } IF_ENQUEUE(ifq, m); sca_start(&scp->sp_if); /* since start may have reset this fix */ if (!scp->sca->sc_usedma) { scp->sca->scu_set_page(scp->sca, scp->sp_rxdesc_p); scp->sca->scu_page_on(scp->sca); } return; default: SCA_DPRINTF(SCA_DEBUG_CISCO, ("Unknown CISCO keepalive protocol 0x%04x\n", ntohl(cisco->type))); scp->sp_if.if_noproto++; goto dropit; } return; default: SCA_DPRINTF(SCA_DEBUG_RX, ("Unknown/unexpected ethertype 0x%04x\n", ntohs(hdlc->h_proto))); scp->sp_if.if_noproto++; goto dropit; } /* queue the packet */ if (!IF_QFULL(ifq)) { IF_ENQUEUE(ifq, m); } else { IF_DROP(ifq); scp->sp_if.if_iqdrops++; goto dropit; } return; dropit: if (m) m_freem(m); return; } #if SCA_DEBUG_LEVEL > 0 /* * do a hex dump of the packet received into descriptor "desc" with * data buffer "p" */ static void sca_frame_print(sca_port_t *scp, sca_desc_t *desc, u_int8_t *p) { int i; int nothing_yet = 1; struct sca_softc *sc; u_int len; sc = scp->sca; printf("desc va %p: chainp 0x%x bufp 0x%0x stat 0x%0x len %d\n", desc, sca_desc_read_chainp(sc, desc), sca_desc_read_bufp(sc, desc), sca_desc_read_stat(sc, desc), (len = sca_desc_read_buflen(sc, desc))); for (i = 0 ; i < len && i < 256; i++) { if (nothing_yet == 1 && (sc->sc_usedma ? *p : bus_space_read_1(sc->scu_memt, sc->scu_memh, sca_page_addr(sc, p))) == 0) { p++; continue; } nothing_yet = 0; if (i % 16 == 0) printf("\n"); printf("%02x ", (sc->sc_usedma ? *p : bus_space_read_1(sc->scu_memt, sc->scu_memh, sca_page_addr(sc, p)))); p++; } if (i % 16 != 1) printf("\n"); } #endif /* * adjust things becuase we have just read the current starting * frame * * must be called at splnet() */ static void sca_frame_read_done(sca_port_t *scp) { u_int16_t edesc_p; /* update where our indicies are */ scp->sp_rxend = scp->sp_rxstart; scp->sp_rxstart = (scp->sp_rxstart + 1) % scp->sp_nrxdesc; /* update the error [end] descriptor */ edesc_p = (u_int16_t)scp->sp_rxdesc_p + (sizeof(sca_desc_t) * scp->sp_rxend); dmac_write_2(scp, SCA_EDAL0, edesc_p); } /* * set a port to the "up" state */ static void sca_port_up(sca_port_t *scp) { struct sca_softc *sc = scp->sca; #if 0 u_int8_t ier0, ier1; #endif /* * reset things */ #if 0 msci_write_1(scp, SCA_CMD0, SCA_CMD_TXRESET); msci_write_1(scp, SCA_CMD0, SCA_CMD_RXRESET); #endif /* * clear in-use flag */ scp->sp_if.if_flags &= ~IFF_OACTIVE; scp->sp_if.if_flags |= IFF_RUNNING; /* * raise DTR */ sc->sc_dtr_callback(sc->sc_aux, scp->sp_port, 1); /* * raise RTS */ msci_write_1(scp, SCA_CTL0, (msci_read_1(scp, SCA_CTL0) & ~SCA_CTL_RTS_MASK) | SCA_CTL_RTS_HIGH); #if 0 /* * enable interrupts (no timer IER2) */ ier0 = SCA_IER0_MSCI_RXRDY0 | SCA_IER0_MSCI_TXRDY0 | SCA_IER0_MSCI_RXINT0 | SCA_IER0_MSCI_TXINT0; ier1 = SCA_IER1_DMAC_RX0A | SCA_IER1_DMAC_RX0B | SCA_IER1_DMAC_TX0A | SCA_IER1_DMAC_TX0B; if (scp->sp_port == 1) { ier0 <<= 4; ier1 <<= 4; } sca_write_1(sc, SCA_IER0, sca_read_1(sc, SCA_IER0) | ier0); sca_write_1(sc, SCA_IER1, sca_read_1(sc, SCA_IER1) | ier1); #else if (scp->sp_port == 0) { sca_write_1(sc, SCA_IER0, sca_read_1(sc, SCA_IER0) | 0x0f); sca_write_1(sc, SCA_IER1, sca_read_1(sc, SCA_IER1) | 0x0f); } else { sca_write_1(sc, SCA_IER0, sca_read_1(sc, SCA_IER0) | 0xf0); sca_write_1(sc, SCA_IER1, sca_read_1(sc, SCA_IER1) | 0xf0); } #endif /* * enable transmit and receive */ msci_write_1(scp, SCA_CMD0, SCA_CMD_TXENABLE); msci_write_1(scp, SCA_CMD0, SCA_CMD_RXENABLE); /* * reset internal state */ scp->sp_txinuse = 0; scp->sp_txcur = 0; scp->cka_lasttx = time.tv_usec; scp->cka_lastrx = 0; } /* * set a port to the "down" state */ static void sca_port_down(sca_port_t *scp) { struct sca_softc *sc = scp->sca; #if 0 u_int8_t ier0, ier1; #endif /* * lower DTR */ sc->sc_dtr_callback(sc->sc_aux, scp->sp_port, 0); /* * lower RTS */ msci_write_1(scp, SCA_CTL0, (msci_read_1(scp, SCA_CTL0) & ~SCA_CTL_RTS_MASK) | SCA_CTL_RTS_LOW); /* * disable interrupts */ #if 0 ier0 = SCA_IER0_MSCI_RXRDY0 | SCA_IER0_MSCI_TXRDY0 | SCA_IER0_MSCI_RXINT0 | SCA_IER0_MSCI_TXINT0; ier1 = SCA_IER1_DMAC_RX0A | SCA_IER1_DMAC_RX0B | SCA_IER1_DMAC_TX0A | SCA_IER1_DMAC_TX0B; if (scp->sp_port == 1) { ier0 <<= 4; ier1 <<= 4; } sca_write_1(sc, SCA_IER0, sca_read_1(sc, SCA_IER0) & ~ier0); sca_write_1(sc, SCA_IER1, sca_read_1(sc, SCA_IER1) & ~ier1); #else if (scp->sp_port == 0) { sca_write_1(sc, SCA_IER0, sca_read_1(sc, SCA_IER0) & 0xf0); sca_write_1(sc, SCA_IER1, sca_read_1(sc, SCA_IER1) & 0xf0); } else { sca_write_1(sc, SCA_IER0, sca_read_1(sc, SCA_IER0) & 0x0f); sca_write_1(sc, SCA_IER1, sca_read_1(sc, SCA_IER1) & 0x0f); } #endif /* * disable transmit and receive */ msci_write_1(scp, SCA_CMD0, SCA_CMD_RXDISABLE); msci_write_1(scp, SCA_CMD0, SCA_CMD_TXDISABLE); /* * no, we're not in use anymore */ scp->sp_if.if_flags &= ~(IFF_OACTIVE|IFF_RUNNING); } /* * disable all DMA and interrupts for all ports at once. */ void sca_shutdown(struct sca_softc *sca) { /* * disable DMA and interrupts */ sca_write_1(sca, SCA_DMER, 0); sca_write_1(sca, SCA_IER0, 0); sca_write_1(sca, SCA_IER1, 0); } /* * If there are packets to transmit, start the transmit DMA logic. */ static void sca_port_starttx(sca_port_t *scp) { struct sca_softc *sc; u_int32_t startdesc_p, enddesc_p; int enddesc; sc = scp->sca; SCA_DPRINTF(SCA_DEBUG_TX, ("TX: starttx\n")); if (((scp->sp_if.if_flags & IFF_OACTIVE) == IFF_OACTIVE) || scp->sp_txinuse == 0) return; SCA_DPRINTF(SCA_DEBUG_TX, ("TX: setting oactive\n")); scp->sp_if.if_flags |= IFF_OACTIVE; /* * We have something to do, since we have at least one packet * waiting, and we are not already marked as active. */ enddesc = (scp->sp_txcur + 1) % scp->sp_ntxdesc; startdesc_p = scp->sp_txdesc_p; enddesc_p = scp->sp_txdesc_p + sizeof(sca_desc_t) * enddesc; SCA_DPRINTF(SCA_DEBUG_TX, ("TX: start %x end %x\n", startdesc_p, enddesc_p)); dmac_write_2(scp, SCA_EDAL1, (u_int16_t)(enddesc_p & 0x0000ffff)); dmac_write_2(scp, SCA_CDAL1, (u_int16_t)(startdesc_p & 0x0000ffff)); /* * enable the DMA */ dmac_write_1(scp, SCA_DSR1, SCA_DSR_DE); } /* * allocate an mbuf at least long enough to hold "len" bytes. * If "p" is non-NULL, copy "len" bytes from it into the new mbuf, * otherwise let the caller handle copying the data in. */ static struct mbuf * sca_mbuf_alloc(struct sca_softc *sc, caddr_t p, u_int len) { struct mbuf *m; /* * allocate an mbuf and copy the important bits of data * into it. If the packet won't fit in the header, * allocate a cluster for it and store it there. */ MGETHDR(m, M_DONTWAIT, MT_DATA); if (m == NULL) return NULL; if (len > MHLEN) { if (len > MCLBYTES) { m_freem(m); return NULL; } MCLGET(m, M_DONTWAIT); if ((m->m_flags & M_EXT) == 0) { m_freem(m); return NULL; } } if (p != NULL) { /* XXX do we need to sync here? */ if (sc->sc_usedma) memcpy(mtod(m, caddr_t), p, len); else bus_space_read_region_1(sc->scu_memt, sc->scu_memh, sca_page_addr(sc, p), mtod(m, u_int8_t *), len); } m->m_len = len; m->m_pkthdr.len = len; return (m); } /* * get the base clock */ void sca_get_base_clock(struct sca_softc *sc) { struct timeval btv, ctv, dtv; u_int64_t bcnt; u_int32_t cnt; u_int16_t subcnt; /* disable the timer, set prescale to 0 */ sca_write_1(sc, SCA_TCSR0, 0); sca_write_1(sc, SCA_TEPR0, 0); /* reset the counter */ (void)sca_read_1(sc, SCA_TCSR0); subcnt = sca_read_2(sc, SCA_TCNTL0); /* count to max */ sca_write_2(sc, SCA_TCONRL0, 0xffff); cnt = 0; microtime(&btv); /* start the timer -- no interrupt enable */ sca_write_1(sc, SCA_TCSR0, SCA_TCSR_TME); for (;;) { microtime(&ctv); /* end around 3/4 of a second */ timersub(&ctv, &btv, &dtv); if (dtv.tv_usec >= 750000) break; /* spin */ while (!(sca_read_1(sc, SCA_TCSR0) & SCA_TCSR_CMF)) ; /* reset the timer */ (void)sca_read_2(sc, SCA_TCNTL0); cnt++; } /* stop the timer */ sca_write_1(sc, SCA_TCSR0, 0); subcnt = sca_read_2(sc, SCA_TCNTL0); /* add the slop in and get the total timer ticks */ cnt = (cnt << 16) | subcnt; /* cnt is 1/8 the actual time */ bcnt = cnt * 8; /* make it proportional to 3/4 of a second */ bcnt *= (u_int64_t)750000; bcnt /= (u_int64_t)dtv.tv_usec; cnt = bcnt; /* make it Hz */ cnt *= 4; cnt /= 3; SCA_DPRINTF(SCA_DEBUG_CLOCK, ("sca: unadjusted base %lu Hz\n", (u_long)cnt)); /* * round to the nearest 200 -- this allows for +-3 ticks error */ sc->sc_baseclock = ((cnt + 100) / 200) * 200; } /* * print the information about the clock on the ports */ void sca_print_clock_info(struct sca_softc *sc) { struct sca_port *scp; u_int32_t mhz, div; int i; printf("%s: base clock %d Hz\n", sc->sc_parent->dv_xname, sc->sc_baseclock); /* print the information about the port clock selection */ for (i = 0; i < sc->sc_numports; i++) { scp = &sc->sc_ports[i]; mhz = sc->sc_baseclock / (scp->sp_tmc ? scp->sp_tmc : 256); div = scp->sp_rxs & SCA_RXS_DIV_MASK; printf("%s: rx clock: ", scp->sp_if.if_xname); switch (scp->sp_rxs & SCA_RXS_CLK_MASK) { case SCA_RXS_CLK_LINE: printf("line"); break; case SCA_RXS_CLK_LINE_SN: printf("line with noise suppression"); break; case SCA_RXS_CLK_INTERNAL: printf("internal %d Hz", (mhz >> div)); break; case SCA_RXS_CLK_ADPLL_OUT: printf("adpll using internal %d Hz", (mhz >> div)); break; case SCA_RXS_CLK_ADPLL_IN: printf("adpll using line clock"); break; } printf(" tx clock: "); div = scp->sp_txs & SCA_TXS_DIV_MASK; switch (scp->sp_txs & SCA_TXS_CLK_MASK) { case SCA_TXS_CLK_LINE: printf("line\n"); break; case SCA_TXS_CLK_INTERNAL: printf("internal %d Hz\n", (mhz >> div)); break; case SCA_TXS_CLK_RXCLK: printf("rxclock\n"); break; } if (scp->sp_eclock) printf("%s: outputting line clock\n", scp->sp_if.if_xname); } }