svn commit: r323404 - stable/11/sys/arm/freescale/imx

Ian Lepore ian at FreeBSD.org
Sun Sep 10 23:48:21 UTC 2017


Author: ian
Date: Sun Sep 10 23:48:20 2017
New Revision: 323404
URL: https://svnweb.freebsd.org/changeset/base/323404

Log:
  MFC r315589, r315591, r316659, r316661:
  
  Eliminate unnecessary read/modify/write sequences during eventtimer setup.
  
  Replace the hard-coded way-too-small minimum event timer period with a value
  calculated at runtime based on how long it takes to set up an event in
  hardware.  This fixes the intermittant 1-minute hang at boot on imx5
  systems, and also the occasional oversleeping while running.  It doesn't
  affect imx6 systems, which use different hardware for eventtimers.
  
  Remove some old interrupt handling workaround code from the pre-INTRNG days.
  At this point, INTRNG is not going away (the option may go away, but the
  code is not), so we no longer need code to support workarounds that handled
  the lack of INTRNG functionality.
  
  Update the code that compensates for the lack of a GPC interrupt controller
  driver for imx6.  Some newer dts source puts the GIC node at the root
  instead of under /soc, so look in both places.  Also, sometimes the GIC
  node doesn't list itself as its own interrupt-parent, allow that too.

Modified:
  stable/11/sys/arm/freescale/imx/imx6_machdep.c
  stable/11/sys/arm/freescale/imx/imx_gpt.c
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/sys/arm/freescale/imx/imx6_machdep.c
==============================================================================
--- stable/11/sys/arm/freescale/imx/imx6_machdep.c	Sun Sep 10 23:41:23 2017	(r323403)
+++ stable/11/sys/arm/freescale/imx/imx6_machdep.c	Sun Sep 10 23:48:20 2017	(r323404)
@@ -52,45 +52,7 @@ __FBSDID("$FreeBSD$");
 
 #include "platform_if.h"
 
-static uint32_t gpio1_node;
-
-#ifndef INTRNG
 /*
- * Work around the linux workaround for imx6 erratum 006687, in which some
- * ethernet interrupts don't go to the GPC and thus won't wake the system from
- * Wait mode. We don't use Wait mode (which halts the GIC, leaving only GPC
- * interrupts able to wake the system), so we don't experience the bug at all.
- * The linux workaround is to reconfigure GPIO1_6 as the ENET interrupt by
- * writing magic values to an undocumented IOMUX register, then letting the gpio
- * interrupt driver notify the ethernet driver.  We'll be able to do all that
- * (even though we don't need to) once the INTRNG project is committed and the
- * imx_gpio driver becomes an interrupt driver.  Until then, this crazy little
- * workaround watches for requests to map an interrupt 6 with the interrupt
- * controller node referring to gpio1, and it substitutes the proper ffec
- * interrupt number.
- */
-static int
-imx6_decode_fdt(uint32_t iparent, uint32_t *intr, int *interrupt,
-    int *trig, int *pol)
-{
-
-	if (fdt32_to_cpu(intr[0]) == 6 && 
-	    OF_node_from_xref(iparent) == gpio1_node) {
-		*interrupt = 150;
-		*trig = INTR_TRIGGER_CONFORM;
-		*pol  = INTR_POLARITY_CONFORM;
-		return (0);
-	}
-	return (gic_decode_fdt(iparent, intr, interrupt, trig, pol));
-}
-
-fdt_pic_decode_t fdt_pic_table[] = {
-	&imx6_decode_fdt,
-	NULL
-};
-#endif
-
-/*
  * Fix FDT data related to interrupts.
  *
  * Driven by the needs of linux and its drivers (as always), the published FDT
@@ -109,7 +71,7 @@ fdt_pic_decode_t fdt_pic_table[] = {
  * We validate that we have data that looks like we expect before changing it:
  *  - SOC node exists and has GPC as its interrupt parent.
  *  - GPC node exists and has GIC as its interrupt parent.
- *  - GIC node exists and is its own interrupt parent.
+ *  - GIC node exists and is its own interrupt parent or has no parent.
  *
  * This applies to all models of imx6.  Luckily all of them have the devices
  * involved at the same addresses on the same busses, so we don't need any
@@ -133,14 +95,20 @@ fix_fdt_interrupt_data(void)
 	if (result <= 0)
 		return;
 
+	/* GIC node may be child of soc node, or appear directly at root. */
 	gicnode = OF_finddevice("/soc/interrupt-controller at 00a01000");
+	if (gicnode == -1) {
+		gicnode = OF_finddevice("/interrupt-controller at 00a01000");
 	if (gicnode == -1)
 		return;
+	}
+	gicxref = OF_xref_from_node(gicnode);
+
+	/* If gic node has no parent, pretend it is its own parent. */
 	result = OF_getencprop(gicnode, "interrupt-parent", &gicipar,
 	    sizeof(gicipar));
 	if (result <= 0)
-		return;
-	gicxref = OF_xref_from_node(gicnode);
+		gicipar = gicxref;
 
 	gpcnode = OF_finddevice("/soc/aips-bus at 02000000/gpc at 020dc000");
 	if (gpcnode == -1)
@@ -184,10 +152,6 @@ imx6_late_init(platform_t plat)
 	const uint32_t IMX6_WDOG_SR_PHYS = 0x020bc004;
 
 	imx_wdog_init_last_reset(IMX6_WDOG_SR_PHYS);
-
-	/* Cache the gpio1 node handle for imx6_decode_fdt() workaround code. */
-	gpio1_node = OF_node_from_xref(
-	    OF_finddevice("/soc/aips-bus at 02000000/gpio at 0209c000"));
 }
 
 /*

Modified: stable/11/sys/arm/freescale/imx/imx_gpt.c
==============================================================================
--- stable/11/sys/arm/freescale/imx/imx_gpt.c	Sun Sep 10 23:41:23 2017	(r323403)
+++ stable/11/sys/arm/freescale/imx/imx_gpt.c	Sun Sep 10 23:48:20 2017	(r323404)
@@ -84,6 +84,7 @@ struct imx_gpt_softc {
 	uint32_t 		sc_period;
 	uint32_t 		sc_clksrc;
 	uint32_t 		clkfreq;
+	uint32_t		ir_reg;
 	struct eventtimer 	et;
 };
 
@@ -102,10 +103,6 @@ static const int imx_gpt_delay_count = 78;
 /* Try to divide down an available fast clock to this frequency. */
 #define	TARGET_FREQUENCY	1000000000
 
-/* Don't try to set an event timer period smaller than this. */
-#define	MIN_ET_PERIOD		10LLU
-
-
 static struct resource_spec imx_gpt_spec[] = {
 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
 	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
@@ -143,7 +140,7 @@ imx_gpt_attach(device_t dev)
 {
 	struct imx_gpt_softc *sc;
 	int ctlreg, err;
-	uint32_t basefreq, prescale;
+	uint32_t basefreq, prescale, setup_ticks, t1, t2;
 
 	sc = device_get_softc(dev);
 
@@ -249,13 +246,25 @@ imx_gpt_attach(device_t dev)
 		return (ENXIO);
 	}
 
+	/*
+	 * Measure how many clock ticks it takes to setup a one-shot event (it's
+	 * longer than you might think, due to wait states in accessing gpt
+	 * registers).  Scale up the result by a factor of 1.5 to be safe,
+	 * and use that to set the minimum eventtimer period we can schedule. In
+	 * the real world, the value works out to about 750ns on imx5 hardware.
+	 */
+	t1 = READ4(sc, IMX_GPT_CNT);
+	WRITE4(sc, IMX_GPT_OCR3, 0);
+	t2 = READ4(sc, IMX_GPT_CNT);
+	setup_ticks = ((t2 - t1 + 1) * 3) / 2;
+
 	/* Register as an eventtimer. */
 	sc->et.et_name = "iMXGPT";
 	sc->et.et_flags = ET_FLAGS_ONESHOT | ET_FLAGS_PERIODIC;
 	sc->et.et_quality = 800;
 	sc->et.et_frequency = sc->clkfreq;
-	sc->et.et_min_period = (MIN_ET_PERIOD << 32) / sc->et.et_frequency;
-	sc->et.et_max_period = (0xfffffffeLLU << 32) / sc->et.et_frequency;
+	sc->et.et_min_period = ((uint64_t)setup_ticks << 32) / sc->clkfreq;
+	sc->et.et_max_period = ((uint64_t)0xfffffffe  << 32) / sc->clkfreq;
 	sc->et.et_start = imx_gpt_timer_start;
 	sc->et.et_stop = imx_gpt_timer_stop;
 	sc->et.et_priv = sc;
@@ -285,16 +294,20 @@ imx_gpt_timer_start(struct eventtimer *et, sbintime_t 
 		/* Set expected value */
 		WRITE4(sc, IMX_GPT_OCR2, READ4(sc, IMX_GPT_CNT) + sc->sc_period);
 		/* Enable compare register 2 Interrupt */
-		SET4(sc, IMX_GPT_IR, GPT_IR_OF2);
+		sc->ir_reg |= GPT_IR_OF2;
+		WRITE4(sc, IMX_GPT_IR, sc->ir_reg);
 		return (0);
 	} else if (first != 0) {
+		/* Enable compare register 3 interrupt if not already on. */
+		if ((sc->ir_reg & GPT_IR_OF3) == 0) {
+			sc->ir_reg |= GPT_IR_OF3;
+			WRITE4(sc, IMX_GPT_IR, sc->ir_reg);
+		}
 		ticks = ((uint32_t)et->et_frequency * first) >> 32;
 		/* Do not disturb, otherwise event will be lost */
 		spinlock_enter();
 		/* Set expected value */
 		WRITE4(sc, IMX_GPT_OCR3, READ4(sc, IMX_GPT_CNT) + ticks);
-		/* Enable compare register 1 Interrupt */
-		SET4(sc, IMX_GPT_IR, GPT_IR_OF3);
 		/* Now everybody can relax */
 		spinlock_exit();
 		return (0);
@@ -310,9 +323,10 @@ imx_gpt_timer_stop(struct eventtimer *et)
 
 	sc = (struct imx_gpt_softc *)et->et_priv;
 
-	/* Disable OF2 Interrupt */
-	CLEAR4(sc, IMX_GPT_IR, GPT_IR_OF2);
-	WRITE4(sc, IMX_GPT_SR, GPT_IR_OF2);
+	/* Disable interrupts and clear any pending status. */
+	sc->ir_reg &= ~(GPT_IR_OF2 | GPT_IR_OF3);
+	WRITE4(sc, IMX_GPT_IR, sc->ir_reg);
+	WRITE4(sc, IMX_GPT_SR, GPT_IR_OF2 | GPT_IR_OF3);
 	sc->sc_period = 0;
 
 	return (0);


More information about the svn-src-all mailing list