Module Name: src Committed By: bouyer Date: Wed Sep 21 20:21:16 UTC 2022
Modified Files: src/sys/arch/arm/sunxi: sunxi_can.c Log Message: Just skipping sunxi_can_rx_intr() if the DATA_OR flag is set isn't enough to properly recover from overrrun in all case. So go the linux way and reset the hardware. Don't write SUNXI_CAN_INT_RX_FLAG to SUNXI_CAN_INT_REG, this could race with hardware and clear the interrupt while there are new packets received. SUNXI_CAN_INT_RX_FLAG clears automatically when all pending packets have been read, so when no more packets are pending just read SUNXI_CAN_INT_REG again and process other interrupts, if any (or RX if there are new packets pending). With this change it seems I get overruns less often in my use case. To generate a diff of this commit: cvs rdiff -u -r1.10 -r1.11 src/sys/arch/arm/sunxi/sunxi_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/sunxi/sunxi_can.c diff -u src/sys/arch/arm/sunxi/sunxi_can.c:1.10 src/sys/arch/arm/sunxi/sunxi_can.c:1.11 --- src/sys/arch/arm/sunxi/sunxi_can.c:1.10 Mon Sep 19 11:21:36 2022 +++ src/sys/arch/arm/sunxi/sunxi_can.c Wed Sep 21 20:21:16 2022 @@ -1,4 +1,4 @@ -/* $NetBSD: sunxi_can.c,v 1.10 2022/09/19 11:21:36 bouyer Exp $ */ +/* $NetBSD: sunxi_can.c,v 1.11 2022/09/21 20:21:16 bouyer Exp $ */ /*- * Copyright (c) 2017,2018 The NetBSD Foundation, Inc. @@ -36,7 +36,7 @@ #include <sys/cdefs.h> -__KERNEL_RCSID(1, "$NetBSD: sunxi_can.c,v 1.10 2022/09/19 11:21:36 bouyer Exp $"); +__KERNEL_RCSID(1, "$NetBSD: sunxi_can.c,v 1.11 2022/09/21 20:21:16 bouyer Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -101,6 +101,8 @@ static void sunxi_can_ifwatchdog(struct static void sunxi_can_enter_reset(struct sunxi_can_softc *); static void sunxi_can_exit_reset(struct sunxi_can_softc *); +static void sunxi_can_ifdown(struct sunxi_can_softc * const); +static int sunxi_can_ifup(struct sunxi_can_softc * const); CFATTACH_DECL_NEW(sunxi_can, sizeof(struct sunxi_can_softc), sunxi_can_match, sunxi_can_attach, NULL, NULL); @@ -344,7 +346,9 @@ sunxi_can_err_intr(struct sunxi_can_soft if (irq & SUNXI_CAN_INT_DATA_OR) { if_statinc(ifp, if_ierrors); + sunxi_can_ifdown(sc); sunxi_can_write(sc, SUNXI_CAN_CMD_REG, SUNXI_CAN_CMD_CLR_OR); + sunxi_can_ifup(sc); } if (irq & SUNXI_CAN_INT_ERR) { reg = sunxi_can_read(sc, SUNXI_CAN_REC_REG); @@ -383,23 +387,31 @@ sunxi_can_intr(void *arg) while ((irq = sunxi_can_read(sc, SUNXI_CAN_INT_REG)) != 0) { uint32_t sts = sunxi_can_read(sc, SUNXI_CAN_STA_REG); rv = 1; + rnd_add_uint32(&sc->sc_rnd_source, irq); - if (irq & SUNXI_CAN_INT_TX_FLAG) { - sunxi_can_tx_intr(sc); - } if ((irq & (SUNXI_CAN_INT_RX_FLAG | SUNXI_CAN_INT_DATA_OR)) == SUNXI_CAN_INT_RX_FLAG) { while (sts & SUNXI_CAN_STA_RX_RDY) { sunxi_can_rx_intr(sc); sts = sunxi_can_read(sc, SUNXI_CAN_STA_REG); } + /* + * Don't write SUNXI_CAN_INT_RX_FLAG to the interrupt + * register, this may clear the RX pending flag + * while there is indeed a packet pending. + * Reading packets should have cleared the RX interrupt, + * so just restart the loop and re-read the interrupt + * register. In the common case irq will now be 0. + */ + continue; + } + if (irq & SUNXI_CAN_INT_TX_FLAG) { + sunxi_can_tx_intr(sc); } if (irq & SUNXI_CAN_INT_ALLERRS) { sunxi_can_err_intr(sc, irq, sts); } sunxi_can_write(sc, SUNXI_CAN_INT_REG, irq); - rnd_add_uint32(&sc->sc_rnd_source, irq); - } mutex_exit(&sc->sc_intr_lock);