summaryrefslogtreecommitdiffstats
path: root/sys/arm/freescale
diff options
context:
space:
mode:
authorian <ian@FreeBSD.org>2016-02-23 01:08:39 +0000
committerian <ian@FreeBSD.org>2016-02-23 01:08:39 +0000
commitd67c524ed823c00f5be2ebeafb66dc1f65c4a0bf (patch)
tree24f7bdc7643f7f8f02b32a8c9629ba087a931845 /sys/arm/freescale
parenta4f5ccb7fc77161a2e775d9061a797a98826c0a8 (diff)
downloadFreeBSD-src-d67c524ed823c00f5be2ebeafb66dc1f65c4a0bf.zip
FreeBSD-src-d67c524ed823c00f5be2ebeafb66dc1f65c4a0bf.tar.gz
Make imx6 systems work again after recent import of new dts files.
Linux-driven changes to the way the chip's two interrupt controllers are defined (we only support one of them) led to no interrupt processing, so the system would hang after device instantiation. This workaround just rewrites the FDT data on the fly to get interrupt handling back under the control of the main GIC device. If/when we ever support deep sleep modes that involve powering down the main GIC, we'll have to undo this change, write a driver for the GPC-PIC, and somehow manage the handoff of responsibilities between the two drivers as the chip transitions in/out of deep sleep mode.
Diffstat (limited to 'sys/arm/freescale')
-rw-r--r--sys/arm/freescale/imx/imx6_machdep.c72
1 files changed, 72 insertions, 0 deletions
diff --git a/sys/arm/freescale/imx/imx6_machdep.c b/sys/arm/freescale/imx/imx6_machdep.c
index 1abbd6c..061355d 100644
--- a/sys/arm/freescale/imx/imx6_machdep.c
+++ b/sys/arm/freescale/imx/imx6_machdep.c
@@ -94,6 +94,74 @@ fdt_pic_decode_t fdt_pic_table[] = {
};
#endif
+/*
+ * Fix FDT data related to interrupts.
+ *
+ * Driven by the needs of linux and its drivers (as always), the published FDT
+ * data for imx6 now sets the interrupt parent for most devices to the GPC
+ * interrupt controller, which is for use when the chip is in deep-sleep mode.
+ * We don't support deep sleep or have a GPC-PIC driver; we need all interrupts
+ * to be handled by the GIC.
+ *
+ * Luckily, the change to the FDT data was to assign the GPC as the interrupt
+ * parent for the soc node and letting that get inherited by all other devices
+ * (except a few that directly name GIC as their interrupt parent). So we can
+ * set the world right by just changing the interrupt-parent property of the soc
+ * node to refer to GIC instead of GPC. This will get us by until we write our
+ * own GPC driver (or until linux changes its mind and the FDT data again).
+ *
+ * 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.
+ *
+ * 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
+ * per-soc logic. We handle this at platform attach time rather than via the
+ * fdt_fixup_table, because the latter requires matching on the FDT "model"
+ * property, and this applies to all boards including those not yet invented.
+ */
+static void
+fix_fdt_interrupt_data(void)
+{
+ phandle_t gicipar, gicnode, gicxref;
+ phandle_t gpcipar, gpcnode, gpcxref;
+ phandle_t socipar, socnode;
+ int result;
+
+ socnode = OF_finddevice("/soc");
+ if (socnode == -1)
+ return;
+ result = OF_getencprop(socnode, "interrupt-parent", &socipar,
+ sizeof(socipar));
+ if (result <= 0)
+ return;
+
+ gicnode = OF_finddevice("/soc/interrupt-controller@00a01000");
+ if (gicnode == -1)
+ return;
+ result = OF_getencprop(gicnode, "interrupt-parent", &gicipar,
+ sizeof(gicipar));
+ if (result <= 0)
+ return;
+ gicxref = OF_xref_from_node(gicnode);
+
+ gpcnode = OF_finddevice("/soc/aips-bus@02000000/gpc@020dc000");
+ if (gpcnode == -1)
+ return;
+ result = OF_getencprop(gpcnode, "interrupt-parent", &gpcipar,
+ sizeof(gpcipar));
+ if (result <= 0)
+ return;
+ gpcxref = OF_xref_from_node(gpcnode);
+
+ if (socipar != gpcxref || gpcipar != gicxref || gicipar != gicxref)
+ return;
+
+ gicxref = cpu_to_fdt32(gicxref);
+ OF_setprop(socnode, "interrupt-parent", &gicxref, sizeof(gicxref));
+}
+
static vm_offset_t
imx6_lastaddr(platform_t plat)
{
@@ -104,6 +172,10 @@ imx6_lastaddr(platform_t plat)
static int
imx6_attach(platform_t plat)
{
+
+ /* Fix soc interrupt-parent property. */
+ fix_fdt_interrupt_data();
+
/* Inform the MPCore timer driver that its clock is variable. */
arm_tmr_change_frequency(ARM_TMR_FREQUENCY_VARIES);
OpenPOWER on IntegriCloud