Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 26 Jan 2018 17:55:17 +0000 (UTC)
From:      Ian Lepore <ian@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r328442 - head/sys/arm/freescale/imx
Message-ID:  <201801261755.w0QHtHuS003312@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: ian
Date: Fri Jan 26 17:55:17 2018
New Revision: 328442
URL: https://svnweb.freebsd.org/changeset/base/328442

Log:
  Add support to the imx5/6 watchdog for the external reset signal.  Also, if
  the "power down" watchdog used by the ROM boot code is still active when the
  regular watchdog is activated, turn off the power-down watchdog.
  
  This adds support for the "fsl,ext-reset-output" FDT property.  When
  present, that property indicates that a chip reset is accomplished by
  asserting the WDOG1_B external signal, which is supposed to trigger some
  external component such as a PMIC to ready the hardware for reset (for
  example, adjusting voltages from idle to full-power levels), and assert the
  POR signal to SoC when ready.  To guard against misconfiguation leading to a
  non-rebootable system, the external reset signal is backstopped by code
  that asserts a normal internal chip reset if nothing responds to the
  external reset signal within one second.

Modified:
  head/sys/arm/freescale/imx/imx_machdep.c
  head/sys/arm/freescale/imx/imx_wdog.c

Modified: head/sys/arm/freescale/imx/imx_machdep.c
==============================================================================
--- head/sys/arm/freescale/imx/imx_machdep.c	Fri Jan 26 17:40:13 2018	(r328441)
+++ head/sys/arm/freescale/imx/imx_machdep.c	Fri Jan 26 17:55:17 2018	(r328442)
@@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$");
 
 #include <machine/armreg.h>
 #include <machine/bus.h>
+#include <machine/cpu.h>
 #include <machine/machdep.h>
 
 #include <arm/freescale/imx/imx_machdep.h>
@@ -65,27 +66,51 @@ SYSCTL_STRING(_hw_imx, OID_AUTO, last_reset_reason, CT
 void
 imx_wdog_cpu_reset(vm_offset_t wdcr_physaddr)
 {
-	volatile uint16_t * pcr;
+	volatile uint16_t cr, *pcr;
 
+	if ((pcr = devmap_ptov(wdcr_physaddr, sizeof(*pcr))) == NULL) {
+		printf("imx_wdog_cpu_reset(): "
+		    "cannot find control register... locking up now.");
+		for (;;)
+			cpu_spinwait();
+	}
+	cr = *pcr;
+
 	/*
-	 * Trigger an immediate reset by clearing the SRS bit in the watchdog
-	 * control register.  The reset happens on the next cycle of the wdog
-	 * 32KHz clock, so hang out in a spin loop until the reset takes effect.
+	 * If the watchdog hardware has been set up to trigger an external reset
+	 * signal on watchdog timeout, then we do software-requested rebooting
+	 * the same way, by asserting the external reset signal.
 	 *
+	 * Asserting external reset is supposed to result in some external
+	 * component asserting the POR pin on the SoC, possibly after adjusting
+	 * and stabilizing system voltages, or taking other system-wide reset
+	 * actions.  Just in case there is some kind of misconfiguration, we
+	 * hang out and do nothing for a full second, then continue on into
+	 * the code to assert a software reset as well.
+	 */
+	if (cr & WDOG_CR_WDT) {
+		cr &= ~WDOG_CR_WDA; /* Assert active-low ext reset bit. */
+		*pcr = cr;
+		DELAY(1000000);
+		printf("imx_wdog_cpu_reset(): "
+		    "External reset failed, trying internal cpu-reset\n");
+		DELAY(10000); /* Time for printf to appear */
+	}
+
+	/*
 	 * Imx6 erratum ERR004346 says the SRS bit has to be cleared twice
 	 * within the same cycle of the 32khz clock to reliably trigger the
 	 * reset.  Writing it 3 times in a row ensures at least 2 of the writes
 	 * happen in the same 32k clock cycle.
 	 */
-	if ((pcr = devmap_ptov(wdcr_physaddr, sizeof(*pcr))) == NULL) {
-		printf("cpu_reset() can't find its control register... locking up now.");
-	} else {
-		*pcr &= ~WDOG_CR_SRS;
-		*pcr &= ~WDOG_CR_SRS;
-		*pcr &= ~WDOG_CR_SRS;
-	}
+	cr &= ~WDOG_CR_SRS; /* Assert active-low software reset bit. */
+	*pcr = cr;
+	*pcr = cr;
+	*pcr = cr;
+
+	/* Reset happens on the next tick of the 32khz clock, wait for it. */
 	for (;;)
-		continue;
+		cpu_spinwait();
 }
 
 void

Modified: head/sys/arm/freescale/imx/imx_wdog.c
==============================================================================
--- head/sys/arm/freescale/imx/imx_wdog.c	Fri Jan 26 17:40:13 2018	(r328441)
+++ head/sys/arm/freescale/imx/imx_wdog.c	Fri Jan 26 17:55:17 2018	(r328442)
@@ -49,13 +49,16 @@ __FBSDID("$FreeBSD$");
 #include <dev/ofw/ofw_bus.h>
 #include <dev/ofw/ofw_bus_subr.h>
 
+#include <arm/freescale/imx/imx_machdep.h>
 #include <arm/freescale/imx/imx_wdogreg.h>
 
 struct imx_wdog_softc {
 	struct mtx		sc_mtx;
 	device_t		sc_dev;
 	struct resource		*sc_res[2];
+	void 			*sc_ih;
 	uint32_t		sc_timeout;
+	bool			sc_pde_enabled;
 };
 
 static struct resource_spec imx_wdog_spec[] = {
@@ -122,6 +125,12 @@ imx_watchdog(void *arg, u_int cmd, int *error)
 			/* Refresh counter */
 			WR2(sc, WDOG_SR_REG, WDOG_SR_STEP1);
 			WR2(sc, WDOG_SR_REG, WDOG_SR_STEP2);
+			/* Watchdog active, can disable rom-boot watchdog. */
+			if (sc->sc_pde_enabled) {
+				sc->sc_pde_enabled = false;
+				reg = RD2(sc, WDOG_MCR_REG);
+				WR2(sc, WDOG_MCR_REG, reg & ~WDOG_MCR_PDE);
+			}
 			*error = 0;
 		}
 	}
@@ -129,6 +138,27 @@ imx_watchdog(void *arg, u_int cmd, int *error)
 }
 
 static int
+imx_wdog_intr(void *arg)
+{
+	struct imx_wdog_softc *sc = arg;
+
+	/*
+	 * When configured for external reset, the actual reset is supposed to
+	 * happen when some external device responds to the assertion of the
+	 * WDOG_B signal by asserting the POR signal to the chip.  This
+	 * interrupt handler is a backstop mechanism; it is set up to fire
+	 * simultaneously with WDOG_B, and if the external reset happens we'll
+	 * never actually make it to here.  If we do make it here, just trigger
+	 * a software reset.  That code will see that external reset is
+	 * configured, and it will wait for 1 second for it to take effect, then
+	 * it will do a software reset as a fallback.
+	 */
+	imx_wdog_cpu_reset(BUS_SPACE_PHYSADDR(sc->sc_res[MEMRES], WDOG_CR_REG));
+
+	return (FILTER_HANDLED); /* unreached */
+}
+
+static int
 imx_wdog_probe(device_t dev)
 {
 
@@ -157,7 +187,26 @@ imx_wdog_attach(device_t dev)
 
 	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), "imx_wdt", MTX_DEF);
 
-	/* TODO: handle interrupt */
+	/*
+	 * If we're configured to assert an external reset signal, set up the
+	 * hardware to do so, and install an interrupt handler whose only
+	 * purpose is to backstop the external reset.  Don't worry if the
+	 * interrupt setup fails, since it's only a backstop measure.
+	 */
+	if (ofw_bus_has_prop(sc->sc_dev, "fsl,ext-reset-output")) {
+		WR2(sc, WDOG_CR_REG, WDOG_CR_WDT | RD2(sc, WDOG_CR_REG));
+		bus_setup_intr(sc->sc_dev, sc->sc_res[IRQRES],
+		    INTR_TYPE_MISC | INTR_MPSAFE, imx_wdog_intr, NULL, sc,
+		    &sc->sc_ih);
+		WR2(sc, WDOG_ICR_REG, WDOG_ICR_WIE); /* Enable, count is 0. */
+	}
+
+	/*
+	 * Note whether the rom-boot so-called "power-down" watchdog is active,
+	 * so we can disable it when the regular watchdog is first enabled.
+	 */
+	if (RD2(sc, WDOG_MCR_REG) & WDOG_MCR_PDE)
+		sc->sc_pde_enabled = true;
 
 	EVENTHANDLER_REGISTER(watchdog_list, imx_watchdog, sc, 0);
 	return (0);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201801261755.w0QHtHuS003312>