MIPS: SEAD3: Probe interrupt controllers using DT
authorPaul Burton <paul.burton@imgtec.com>
Fri, 26 Aug 2016 14:17:34 +0000 (15:17 +0100)
committerRalf Baechle <ralf@linux-mips.org>
Tue, 4 Oct 2016 23:31:20 +0000 (01:31 +0200)
Probe the CPU interrupt controller & optional Global Interrupt
Controller (GIC) using devicetree rather than platform code. Because the
bootloader on SEAD3 does not provide a device tree to the kernel & the
device tree is always built in, we patch out the GIC node during boot if
we detect that a GIC is not present in the system.

The appropriate IRQ domain is discovered by platform code setting up
device IRQ numbers temporarily. It will be removed by further patches
which move the devices towards being probed via device tree.

No behavioural change is intended by this patch.

Signed-off-by: Paul Burton <paul.burton@imgtec.com>
Cc: Matt Redfearn <matt.redfearn@imgtec.com>
Cc: Kefeng Wang <wangkefeng.wang@huawei.com>
Cc: Jacek Anaszewski <j.anaszewski@samsung.com>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: linux-mips@linux-mips.org
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Patchwork: https://patchwork.linux-mips.org/patch/14047/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
arch/mips/boot/dts/mti/sead3.dts
arch/mips/include/asm/mach-sead3/sead3-dtshim.h [new file with mode: 0644]
arch/mips/include/asm/mips-boards/sead3int.h
arch/mips/mti-sead3/Makefile
arch/mips/mti-sead3/sead3-dtshim.c [new file with mode: 0644]
arch/mips/mti-sead3/sead3-int.c
arch/mips/mti-sead3/sead3-platform.c
arch/mips/mti-sead3/sead3-setup.c

index e4b317d414f112576bbd04012381ab6804dabf86..051b3a9e5a9e50cd62a952ab92974cac06f7a04b 100644 (file)
@@ -4,10 +4,13 @@
 /memreserve/ 0x00001000 0x000ef000;    // ROM data
 /memreserve/ 0x000f0000 0x004cc000;    // reserved
 
+#include <dt-bindings/interrupt-controller/mips-gic.h>
+
 / {
        #address-cells = <1>;
        #size-cells = <1>;
        compatible = "mti,sead-3";
+       interrupt-parent = <&gic>;
 
        cpus {
                cpu@0 {
                device_type = "memory";
                reg = <0x0 0x08000000>;
        };
+
+       cpu_intc: interrupt-controller {
+               compatible = "mti,cpu-interrupt-controller";
+
+               interrupt-controller;
+               #interrupt-cells = <1>;
+       };
+
+       gic: interrupt-controller@1b1c0000 {
+               compatible = "mti,gic";
+               reg = <0x1b1c0000 0x20000>;
+
+               interrupt-controller;
+               #interrupt-cells = <3>;
+
+               /*
+                * Declare the interrupt-parent even though the mti,gic
+                * binding doesn't require it, such that the kernel can
+                * figure out that cpu_intc is the root interrupt
+                * controller & should be probed first.
+                */
+               interrupt-parent = <&cpu_intc>;
+
+               timer {
+                       compatible = "mti,gic-timer";
+                       interrupts = <GIC_LOCAL 1 IRQ_TYPE_NONE>;
+               };
+       };
 };
diff --git a/arch/mips/include/asm/mach-sead3/sead3-dtshim.h b/arch/mips/include/asm/mach-sead3/sead3-dtshim.h
new file mode 100644 (file)
index 0000000..f5d7d9c
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2016 Imagination Technologies
+ * Author: Paul Burton <paul.burton@imgtec.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#ifndef __MIPS_SEAD3_DTSHIM_H__
+#define __MIPS_SEAD3_DTSHIM_H__
+
+#include <linux/init.h>
+
+#ifdef CONFIG_MIPS_SEAD3
+
+extern void __init *sead3_dt_shim(void *fdt);
+
+#else /* !CONFIG_MIPS_SEAD3 */
+
+static inline void *sead3_dt_shim(void *fdt)
+{
+       return fdt;
+}
+
+#endif /* !CONFIG_MIPS_SEAD3 */
+
+#endif /* __MIPS_SEAD3_DTSHIM_H__ */
index 8932c7de0419887a380af6d38a8669f8d00fda3f..bd85da3d6770adc9d55674f36b214b9124e534c2 100644 (file)
 
 #include <linux/irqchip/mips-gic.h>
 
-/* SEAD-3 GIC address space definitions. */
-#define GIC_BASE_ADDR          0x1b1c0000
-#define GIC_ADDRSPACE_SZ       (128 * 1024)
-
 /* CPU interrupt offsets */
-#define CPU_INT_GIC            2
 #define CPU_INT_EHCI           2
 #define CPU_INT_UART0          4
 #define CPU_INT_UART1          4
index 8b03cfb127599a69d19488b347901ca0865d76a6..aad67aac3dbaea11408bda38516be4b7b21b3b49 100644 (file)
@@ -10,6 +10,7 @@
 #
 obj-y := sead3-lcd.o
 obj-y += sead3-display.o
+obj-y += sead3-dtshim.o
 obj-y += sead3-init.o
 obj-y += sead3-int.o
 obj-y += sead3-platform.o
diff --git a/arch/mips/mti-sead3/sead3-dtshim.c b/arch/mips/mti-sead3/sead3-dtshim.c
new file mode 100644 (file)
index 0000000..3283a7e
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2016 Imagination Technologies
+ * Author: Paul Burton <paul.burton@imgtec.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#define pr_fmt(fmt) "sead3-dtshim: " fmt
+
+#include <linux/errno.h>
+#include <linux/libfdt.h>
+#include <linux/printk.h>
+
+#include <asm/io.h>
+
+#define SEAD_CONFIG                    CKSEG1ADDR(0x1b100110)
+#define SEAD_CONFIG_GIC_PRESENT                BIT(1)
+
+static unsigned char fdt_buf[16 << 10] __initdata;
+
+static int remove_gic(void *fdt)
+{
+       int gic_off, cpu_off, err;
+       uint32_t cfg, cpu_phandle;
+
+       /* leave the GIC node intact if a GIC is present */
+       cfg = __raw_readl((uint32_t *)SEAD_CONFIG);
+       if (cfg & SEAD_CONFIG_GIC_PRESENT)
+               return 0;
+
+       gic_off = fdt_node_offset_by_compatible(fdt, -1, "mti,gic");
+       if (gic_off < 0) {
+               pr_err("unable to find DT GIC node: %d\n", gic_off);
+               return gic_off;
+       }
+
+       err = fdt_nop_node(fdt, gic_off);
+       if (err) {
+               pr_err("unable to nop GIC node\n");
+               return err;
+       }
+
+       cpu_off = fdt_node_offset_by_compatible(fdt, -1,
+                       "mti,cpu-interrupt-controller");
+       if (cpu_off < 0) {
+               pr_err("unable to find CPU intc node: %d\n", cpu_off);
+               return cpu_off;
+       }
+
+       cpu_phandle = fdt_get_phandle(fdt, cpu_off);
+       if (!cpu_phandle) {
+               pr_err("unable to get CPU intc phandle\n");
+               return -EINVAL;
+       }
+
+       err = fdt_setprop_u32(fdt, 0, "interrupt-parent", cpu_phandle);
+       if (err) {
+               pr_err("unable to set root interrupt-parent: %d\n", err);
+               return err;
+       }
+
+       return 0;
+}
+
+void __init *sead3_dt_shim(void *fdt)
+{
+       int err;
+
+       if (fdt_check_header(fdt))
+               panic("Corrupt DT");
+
+       /* if this isn't SEAD3, leave the DT alone */
+       if (fdt_node_check_compatible(fdt, 0, "mti,sead-3"))
+               return fdt;
+
+       err = fdt_open_into(fdt, fdt_buf, sizeof(fdt_buf));
+       if (err)
+               panic("Unable to open FDT: %d", err);
+
+       err = remove_gic(fdt_buf);
+       if (err)
+               panic("Unable to patch FDT: %d", err);
+
+       err = fdt_pack(fdt_buf);
+       if (err)
+               panic("Unable to pack FDT: %d\n", err);
+
+       return fdt_buf;
+}
index e31e17f81eefde2cebd3089641d7c487c21b8c55..2e6b73244ecdacd3c83bdec0ba0bebd8829bf280 100644 (file)
@@ -6,37 +6,18 @@
  * Copyright (C) 2012 MIPS Technologies, Inc.  All rights reserved.
  */
 #include <linux/init.h>
-#include <linux/irq.h>
+#include <linux/irqchip.h>
 #include <linux/irqchip/mips-gic.h>
-#include <linux/io.h>
 
-#include <asm/irq_cpu.h>
-#include <asm/setup.h>
-
-#include <asm/mips-boards/sead3int.h>
-
-#define SEAD_CONFIG_GIC_PRESENT_SHF    1
-#define SEAD_CONFIG_GIC_PRESENT_MSK    (1 << SEAD_CONFIG_GIC_PRESENT_SHF)
-#define SEAD_CONFIG_BASE               0x1b100110
-#define SEAD_CONFIG_SIZE               4
-
-static void __iomem *sead3_config_reg;
+#include <asm/cpu-info.h>
+#include <asm/irq.h>
 
 void __init arch_init_irq(void)
 {
-       if (!cpu_has_veic)
-               mips_cpu_irq_init();
+       irqchip_init();
 
-       sead3_config_reg = ioremap_nocache(SEAD_CONFIG_BASE, SEAD_CONFIG_SIZE);
-       gic_present = (__raw_readl(sead3_config_reg) &
-                      SEAD_CONFIG_GIC_PRESENT_MSK) >>
-               SEAD_CONFIG_GIC_PRESENT_SHF;
        pr_info("GIC: %spresent\n", (gic_present) ? "" : "not ");
        pr_info("EIC: %s\n",
                (current_cpu_data.options & MIPS_CPU_VEIC) ?  "on" : "off");
-
-       if (gic_present)
-               gic_init(GIC_BASE_ADDR, GIC_ADDRSPACE_SZ, CPU_INT_GIC,
-                        MIPS_GIC_IRQ_BASE);
 }
 
index 73b73efbfb05862a2e2fa1074b0426f05a640d39..d6be0298056a3341f8a5fc95d7a7df6934b57317 100644 (file)
@@ -9,8 +9,10 @@
 #include <linux/init.h>
 #include <linux/irq.h>
 #include <linux/irqchip/mips-gic.h>
+#include <linux/irqdomain.h>
 #include <linux/leds.h>
 #include <linux/mtd/physmap.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/serial_8250.h>
 #include <linux/smsc911x.h>
@@ -204,16 +206,41 @@ static struct platform_device *sead3_platform_devices[] __initdata = {
 
 static int __init sead3_platforms_device_init(void)
 {
+       const char *intc_compat;
+       struct device_node *node;
+       struct irq_domain *irqd;
+
+       if (gic_present)
+               intc_compat = "mti,gic";
+       else
+               intc_compat = "mti,cpu-interrupt-controller";
+
+       node = of_find_compatible_node(NULL, NULL, intc_compat);
+       if (!node) {
+               pr_err("unable to find interrupt controller DT node\n");
+               return -ENODEV;
+       }
+
+       irqd = irq_find_host(node);
+       if (!irqd) {
+               pr_err("unable to find interrupt controller IRQ domain\n");
+               return -ENODEV;
+       }
+
        if (gic_present) {
-               uart8250_data[0].irq = MIPS_GIC_IRQ_BASE + GIC_INT_UART0;
-               uart8250_data[1].irq = MIPS_GIC_IRQ_BASE + GIC_INT_UART1;
-               ehci_resources[1].start = MIPS_GIC_IRQ_BASE + GIC_INT_EHCI;
-               sead3_net_resources[1].start = MIPS_GIC_IRQ_BASE + GIC_INT_NET;
+               uart8250_data[0].irq = irq_create_mapping(irqd, GIC_INT_UART0);
+               uart8250_data[1].irq = irq_create_mapping(irqd, GIC_INT_UART1);
+               ehci_resources[1].start =
+                       irq_create_mapping(irqd, GIC_INT_EHCI);
+               sead3_net_resources[1].start =
+                       irq_create_mapping(irqd, GIC_INT_NET);
        } else {
-               uart8250_data[0].irq = MIPS_CPU_IRQ_BASE + CPU_INT_UART0;
-               uart8250_data[1].irq = MIPS_CPU_IRQ_BASE + CPU_INT_UART1;
-               ehci_resources[1].start = MIPS_CPU_IRQ_BASE + CPU_INT_EHCI;
-               sead3_net_resources[1].start = MIPS_CPU_IRQ_BASE + CPU_INT_NET;
+               uart8250_data[0].irq = irq_create_mapping(irqd, CPU_INT_UART0);
+               uart8250_data[1].irq = irq_create_mapping(irqd, CPU_INT_UART1);
+               ehci_resources[1].start =
+                       irq_create_mapping(irqd, CPU_INT_EHCI);
+               sead3_net_resources[1].start =
+                       irq_create_mapping(irqd, CPU_INT_NET);
        }
 
        return platform_add_devices(sead3_platform_devices,
index edfcaf06680d37c917bdf9f3d58a5ec042943a76..c4fc0c6d26543bfaccc1569adc62e38bb7189586 100644 (file)
@@ -13,6 +13,7 @@
 #include <asm/prom.h>
 #include <asm/fw/fw.h>
 
+#include <asm/mach-sead3/sead3-dtshim.h>
 #include <asm/mips-boards/generic.h>
 
 const char *get_system_type(void)
@@ -89,20 +90,16 @@ void __init *plat_get_fdt(void)
 
 void __init plat_mem_setup(void)
 {
+       void *fdt = plat_get_fdt();
+
        /* allow command line/bootloader env to override memory size in DT */
        parse_memsize_param();
 
-       /*
-        * Load the builtin devicetree. This causes the chosen node to be
-        * parsed resulting in our memory appearing
-        */
-       __dt_setup_arch(__dtb_start);
+       fdt = sead3_dt_shim(fdt);
+       __dt_setup_arch(fdt);
 }
 
 void __init device_tree_init(void)
 {
-       if (!initial_boot_params)
-               return;
-
        unflatten_and_copy_device_tree();
 }