Module Name: src Committed By: bouyer Date: Thu Apr 20 13:00:52 UTC 2017
Modified Files: src/sys/arch/arm/allwinner [bouyer-socketcan]: awin_can.c Log Message: Handle interface errors. Use a watchdog for transmit. To generate a diff of this commit: cvs rdiff -u -r1.1.2.2 -r1.1.2.3 src/sys/arch/arm/allwinner/awin_can.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/allwinner/awin_can.c diff -u src/sys/arch/arm/allwinner/awin_can.c:1.1.2.2 src/sys/arch/arm/allwinner/awin_can.c:1.1.2.3 --- src/sys/arch/arm/allwinner/awin_can.c:1.1.2.2 Wed Apr 19 17:54:18 2017 +++ src/sys/arch/arm/allwinner/awin_can.c Thu Apr 20 13:00:52 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: awin_can.c,v 1.1.2.2 2017/04/19 17:54:18 bouyer Exp $ */ +/* $NetBSD: awin_can.c,v 1.1.2.3 2017/04/20 13:00:52 bouyer Exp $ */ /*- * Copyright (c) 2017 The NetBSD Foundation, Inc. @@ -36,7 +36,7 @@ #include <sys/cdefs.h> -__KERNEL_RCSID(1, "$NetBSD: awin_can.c,v 1.1.2.2 2017/04/19 17:54:18 bouyer Exp $"); +__KERNEL_RCSID(1, "$NetBSD: awin_can.c,v 1.1.2.3 2017/04/20 13:00:52 bouyer Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -60,6 +60,15 @@ __KERNEL_RCSID(1, "$NetBSD: awin_can.c,v #include <arm/allwinner/awin_reg.h> #include <arm/allwinner/awin_var.h> +/* shortcut for all error interrupts */ +#define AWIN_CAN_INT_ALLERRS (\ + AWIN_CAN_INT_BERR | \ + AWIN_CAN_INT_ARB_LOST | \ + AWIN_CAN_INT_ERR_PASSIVE | \ + AWIN_CAN_INT_DATA_OR | \ + AWIN_CAN_INT_ERR \ + ) + struct awin_can_softc { struct canif_softc sc_cansc; bus_space_tag_t sc_bst; @@ -164,6 +173,8 @@ awin_can_attach(device_t parent, device_ CAN_LINKMODE_3SAMPLES | CAN_LINKMODE_LISTENONLY | CAN_LINKMODE_LOOPBACK; can_ifinit_timings(&sc->sc_cansc); + sc->sc_timings.clt_prop = 0; + sc->sc_timings.clt_sjw = 1; aprint_naive("\n"); aprint_normal(": CAN bus controller\n"); @@ -222,6 +233,7 @@ awin_can_rx_intr(struct awin_can_softc * int dlc; int regd, i; + KASSERT(mutex_owned(&sc->sc_intr_lock)); reg0v = awin_can_read(sc, AWIN_CAN_TXBUF0_REG); dlc = reg0v & AWIN_CAN_TXBUF0_DL; @@ -268,6 +280,7 @@ awin_can_rx_intr(struct awin_can_softc * ifp->if_ibytes += m->m_len; m_set_rcvif(m, ifp); bpf_mtap(ifp, m); + printf("can_input1\n"); can_input(ifp, m); } @@ -281,11 +294,13 @@ awin_can_tx_intr(struct awin_can_softc * uint32_t reg0val; int i; + KASSERT(mutex_owned(&sc->sc_intr_lock)); if ((m = sc->sc_m_transmit) != NULL) { ifp->if_obytes += m->m_len; ifp->if_opackets++; can_mbuf_tag_clean(m); m_set_rcvif(m, ifp); + printf("can_input2\n"); can_input(ifp, m); /* loopback */ sc->sc_m_transmit = NULL; } @@ -331,11 +346,68 @@ awin_can_tx_intr(struct awin_can_softc * } awin_can_write(sc, AWIN_CAN_TXBUF0_REG, reg0val); - awin_can_write(sc, AWIN_CAN_CMD_REG, AWIN_CAN_CMD_TANS_REQ); + if (sc->sc_linkmodes & CAN_LINKMODE_LOOPBACK) { + awin_can_write(sc, AWIN_CAN_CMD_REG, + AWIN_CAN_CMD_TANS_REQ | AWIN_CAN_CMD_SELF_REQ); + } else { + awin_can_write(sc, AWIN_CAN_CMD_REG, AWIN_CAN_CMD_TANS_REQ); + } ifp->if_flags |= IFF_OACTIVE; + ifp->if_timer = 5; bpf_mtap(ifp, m); } +static int +awin_can_tx_abort(struct awin_can_softc *sc) +{ + KASSERT(mutex_owned(&sc->sc_intr_lock)); + if (sc->sc_m_transmit) { + m_freem(sc->sc_m_transmit); + sc->sc_m_transmit = NULL; + sc->sc_ifp->if_timer = 0; + /* + * the transmit abort will trigger a TX interrupt + * which will restart the queue or cleae OACTIVE, + * as appropriate + */ + awin_can_write(sc, AWIN_CAN_CMD_REG, AWIN_CAN_CMD_ABT_REQ); + return 1; + } + return 0; +} + +static void +awin_can_err_intr(struct awin_can_softc *sc, uint32_t irq, uint32_t sts) +{ + struct ifnet * const ifp = sc->sc_ifp; + KASSERT(mutex_owned(&sc->sc_intr_lock)); + int txerr = 0; + + if (irq & AWIN_CAN_INT_DATA_OR) { + ifp->if_ierrors++; + awin_can_write(sc, AWIN_CAN_CMD_REG, AWIN_CAN_CMD_CLR_OR); + } + if (irq & AWIN_CAN_INT_ERR) { + /* XXX todo */ + } + if (irq & AWIN_CAN_INT_BERR) { + if (sts & AWIN_CAN_STA_TX) + txerr++; + if (sts & AWIN_CAN_STA_RX) + ifp->if_ierrors++; + } + if (irq & AWIN_CAN_INT_ERR_PASSIVE) { + /* XXX todo */ + } + if (irq & AWIN_CAN_INT_ARB_LOST) { + txerr++; + } + if (txerr) { + ifp->if_oerrors += txerr; + (void) awin_can_tx_abort(sc); + } +} + int awin_can_intr(void *arg) { @@ -358,6 +430,9 @@ awin_can_intr(void *arg) sts = awin_can_read(sc, AWIN_CAN_STA_REG); } } + if (irq & AWIN_CAN_INT_ALLERRS) { + awin_can_err_intr(sc, irq, sts); + } awin_can_write(sc, AWIN_CAN_INT_REG, irq); rnd_add_uint32(&sc->sc_rnd_source, irq); @@ -424,7 +499,7 @@ awin_can_ifup(struct awin_can_softc * co awin_can_exit_reset(sc); awin_can_write(sc, AWIN_CAN_INTE_REG, - AWIN_CAN_INT_TX_FLAG | AWIN_CAN_INT_RX_FLAG); + AWIN_CAN_INT_TX_FLAG | AWIN_CAN_INT_RX_FLAG | AWIN_CAN_INT_ALLERRS); sc->sc_ifp->if_flags |= IFF_RUNNING; return 0; } @@ -433,6 +508,7 @@ static void awin_can_ifdown(struct awin_can_softc * const sc) { sc->sc_ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + sc->sc_ifp->if_timer = 0; awin_can_enter_reset(sc); awin_can_write(sc, AWIN_CAN_INTE_REG, 0); awin_can_write(sc, AWIN_CAN_INT_REG, @@ -484,6 +560,22 @@ awin_can_ifioctl(struct ifnet *ifp, u_lo void awin_can_ifwatchdog(struct ifnet *ifp) { + struct awin_can_softc * const sc = ifp->if_softc; + printf("%s: watchdog timeout\n", device_xname(sc->sc_dev)); + + mutex_enter(&sc->sc_intr_lock); + printf("irq 0x%x en 0x%x mode 0x%x status 0x%x timings 0x%x err 0x%x\n", + awin_can_read(sc, AWIN_CAN_INT_REG), + awin_can_read(sc, AWIN_CAN_INTE_REG), + awin_can_read(sc, AWIN_CAN_MODSEL_REG), + awin_can_read(sc, AWIN_CAN_STA_REG), + awin_can_read(sc, AWIN_CAN_BUS_TIME_REG), + awin_can_read(sc, AWIN_CAN_REC_REG)); + /* if there is a transmit in progress abort */ + if (awin_can_tx_abort(sc)) { + ifp->if_oerrors++; + } + mutex_exit(&sc->sc_intr_lock); } static void