ARM: hi3xxx: add smp support
authorKevin Hilman <khilman@linaro.org>
Wed, 18 Dec 2013 00:23:49 +0000 (16:23 -0800)
committerKevin Hilman <khilman@linaro.org>
Wed, 18 Dec 2013 00:43:34 +0000 (16:43 -0800)
Enable SMP support on hi3xxx platform

Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org>
Tested-by: Zhang Mingjun <zhang.mingjun@linaro.org>
Tested-by: Li Xin <li.xin@linaro.org>
Signed-off-by: Haojian Zhuang <haojian.zhuang@linaro.org>
[khilman: fix checkpatch errors]
Signed-off-by: Kevin Hilman <khilman@linaro.org>
Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
arch/arm/boot/dts/hi3620.dtsi
arch/arm/mach-hi3xxx/Kconfig
arch/arm/mach-hi3xxx/Makefile
arch/arm/mach-hi3xxx/core.h [new file with mode: 0644]
arch/arm/mach-hi3xxx/hi3xxx.c
arch/arm/mach-hi3xxx/platsmp.c [new file with mode: 0644]

index 21a7336..8c7a465 100644 (file)
@@ -4,3 +4,29 @@ Hisilicon Platforms Device Tree Bindings
 Hi4511 Board
 Required root node properties:
        - compatible = "hisilicon,hi3620-hi4511";
+
+Hisilicon system controller
+
+Required properties:
+- compatible : "hisilicon,sysctrl"
+- reg : Register address and size
+
+Optional properties:
+- smp-offset : offset in sysctrl for notifying slave cpu booting
+               cpu 1, reg;
+               cpu 2, reg + 0x4;
+               cpu 3, reg + 0x8;
+               If reg value is not zero, cpun exit wfi and go
+- resume-offset : offset in sysctrl for notifying cpu0 when resume
+- reboot-offset : offset in sysctrl for system reboot
+
+Example:
+
+       /* for Hi3620 */
+       sysctrl: system-controller@fc802000 {
+               compatible = "hisilicon,sysctrl";
+               reg = <0xfc802000 0x1000>;
+               smp-offset = <0x31c>;
+               resume-offset = <0x308>;
+               reboot-offset = <0x4>;
+       };
index b9d8679..e311937 100644 (file)
                        reg = <0x0>;
                        next-level-cache = <&L2>;
                };
+
+               cpu@1 {
+                       compatible = "arm,cortex-a9";
+                       device_type = "cpu";
+                       reg = <1>;
+                       next-level-cache = <&L2>;
+               };
+
+               cpu@2 {
+                       compatible = "arm,cortex-a9";
+                       device_type = "cpu";
+                       reg = <2>;
+                       next-level-cache = <&L2>;
+               };
+
+               cpu@3 {
+                       compatible = "arm,cortex-a9";
+                       device_type = "cpu";
+                       reg = <3>;
+                       next-level-cache = <&L2>;
+               };
        };
 
        amba {
                        reg = <0x1000 0x1000>, <0x100 0x100>;
                };
 
+               sysctrl: system-controller@802000 {
+                       compatible = "hisilicon,sysctrl";
+                       reg = <0x802000 0x1000>;
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       smp-offset = <0x31c>;
+                       resume-offset = <0x308>;
+                       reboot-offset = <0x4>;
+               };
+
                dual_timer0: dual_timer@800000 {
                        compatible = "arm,sp804", "arm,primecell";
                        reg = <0x800000 0x1000>;
                        status = "disabled";
                };
 
+               timer5: timer@600 {
+                       compatible = "arm,cortex-a9-twd-timer";
+                       reg = <0x600 0x20>;
+                       interrupts = <1 13 0xf01>;
+               };
+
                uart0: uart@b00000 {
                        compatible = "arm,pl011", "arm,primecell";
                        reg = <0xb00000 0x1000>;
index 8a502d1..018ad67 100644 (file)
@@ -7,7 +7,11 @@ config ARCH_HI3xxx
        select CACHE_L2X0
        select CLKSRC_OF
        select GENERIC_CLOCKEVENTS
+       select HAVE_ARM_SCU
+       select HAVE_ARM_TWD
+       select HAVE_SMP
        select PINCTRL
        select PINCTRL_SINGLE
+       select SMP
        help
          Support for Hisilicon Hi36xx/Hi37xx processor family
index d68ebb3..7a869a7 100644 (file)
@@ -3,3 +3,4 @@
 #
 
 obj-y  += hi3xxx.o
+obj-$(CONFIG_SMP)              += platsmp.o
diff --git a/arch/arm/mach-hi3xxx/core.h b/arch/arm/mach-hi3xxx/core.h
new file mode 100644 (file)
index 0000000..226f020
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef __HISILICON_CORE_H
+#define __HISILICON_CORE_H
+
+#include <linux/reboot.h>
+
+extern void hi3xxx_set_cpu_jump(int cpu, void *jump_addr);
+extern int hi3xxx_get_cpu_jump(int cpu);
+extern void secondary_startup(void);
+extern struct smp_operations hi3xxx_smp_ops;
+
+#endif
index fe56daf..661a912 100644 (file)
@@ -1,4 +1,4 @@
-5/*
+/*
  * (Hisilicon's Hi36xx/Hi37xx SoC based) flattened device tree enabled machine
  *
  * Copyright (c) 2012-2013 Hisilicon Ltd.
 #include <linux/clk-provider.h>
 #include <linux/clocksource.h>
 #include <linux/irqchip.h>
+#include <linux/of_address.h>
 #include <linux/of_platform.h>
 
+#include <asm/proc-fns.h>
+
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
 
+#include "core.h"
+
+#define HI3620_SYSCTRL_PHYS_BASE               0xfc802000
+#define HI3620_SYSCTRL_VIRT_BASE               0xfe802000
+
 /*
  * This table is only for optimization. Since ioremap() could always share
  * the same mapping if it's defined as static IO mapping.
@@ -29,8 +37,9 @@
  */
 static struct map_desc hi3620_io_desc[] __initdata = {
        {
-               .pfn            = __phys_to_pfn(0xfc802000),
-               .virtual        = 0xfe802000,
+               /* sysctrl */
+               .pfn            = __phys_to_pfn(HI3620_SYSCTRL_PHYS_BASE),
+               .virtual        = HI3620_SYSCTRL_VIRT_BASE,
                .length         = 0x1000,
                .type           = MT_DEVICE,
        },
@@ -48,6 +57,32 @@ static void __init hi3xxx_timer_init(void)
        clocksource_of_init();
 }
 
+static void hi3xxx_restart(enum reboot_mode mode, const char *cmd)
+{
+       struct device_node *np;
+       void __iomem *base;
+       int offset;
+
+       np = of_find_compatible_node(NULL, NULL, "hisilicon,sysctrl");
+       if (!np) {
+               pr_err("failed to find hisilicon,sysctrl node\n");
+               return;
+       }
+       base = of_iomap(np, 0);
+       if (!base) {
+               pr_err("failed to map address in hisilicon,sysctrl node\n");
+               return;
+       }
+       if (of_property_read_u32(np, "reboot-offset", &offset) < 0) {
+               pr_err("failed to find reboot-offset property\n");
+               return;
+       }
+       writel_relaxed(0xdeadbeef, base + offset);
+
+       while (1)
+               cpu_do_idle();
+}
+
 static const char *hi3xxx_compat[] __initconst = {
        "hisilicon,hi3620-hi4511",
        NULL,
@@ -57,4 +92,6 @@ DT_MACHINE_START(HI3620, "Hisilicon Hi3620 (Flattened Device Tree)")
        .map_io         = hi3620_map_io,
        .init_time      = hi3xxx_timer_init,
        .dt_compat      = hi3xxx_compat,
+       .smp            = smp_ops(hi3xxx_smp_ops),
+       .restart        = hi3xxx_restart,
 MACHINE_END
diff --git a/arch/arm/mach-hi3xxx/platsmp.c b/arch/arm/mach-hi3xxx/platsmp.c
new file mode 100644 (file)
index 0000000..8ebfbe7
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2013 Linaro Ltd.
+ * Copyright (c) 2013 Hisilicon Limited.
+ * Based on arch/arm/mach-vexpress/platsmp.c, Copyright (C) 2002 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+#include <linux/smp.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+
+#include <asm/cacheflush.h>
+#include <asm/smp_plat.h>
+#include <asm/smp_scu.h>
+
+#include "core.h"
+
+static void __iomem *ctrl_base;
+
+void hi3xxx_set_cpu_jump(int cpu, void *jump_addr)
+{
+       cpu = cpu_logical_map(cpu);
+       if (!cpu || !ctrl_base)
+               return;
+       writel_relaxed(virt_to_phys(jump_addr), ctrl_base + ((cpu - 1) << 2));
+}
+
+int hi3xxx_get_cpu_jump(int cpu)
+{
+       cpu = cpu_logical_map(cpu);
+       if (!cpu || !ctrl_base)
+               return 0;
+       return readl_relaxed(ctrl_base + ((cpu - 1) << 2));
+}
+
+static void __init hi3xxx_smp_prepare_cpus(unsigned int max_cpus)
+{
+       struct device_node *np = NULL;
+       unsigned long base = 0;
+       u32 offset = 0;
+       void __iomem *scu_base = NULL;
+
+       if (scu_a9_has_base()) {
+               base = scu_a9_get_base();
+               scu_base = ioremap(base, SZ_4K);
+               if (!scu_base) {
+                       pr_err("ioremap(scu_base) failed\n");
+                       return;
+               }
+               scu_enable(scu_base);
+               iounmap(scu_base);
+       }
+       if (!ctrl_base) {
+               np = of_find_compatible_node(NULL, NULL, "hisilicon,sysctrl");
+               if (!np) {
+                       pr_err("failed to find hisilicon,sysctrl node\n");
+                       return;
+               }
+               ctrl_base = of_iomap(np, 0);
+               if (!ctrl_base) {
+                       pr_err("failed to map address\n");
+                       return;
+               }
+               if (of_property_read_u32(np, "smp-offset", &offset) < 0) {
+                       pr_err("failed to find smp-offset property\n");
+                       return;
+               }
+               ctrl_base += offset;
+       }
+}
+
+static int hi3xxx_boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+       hi3xxx_set_cpu_jump(cpu, secondary_startup);
+       arch_send_wakeup_ipi_mask(cpumask_of(cpu));
+       return 0;
+}
+
+struct smp_operations hi3xxx_smp_ops __initdata = {
+       .smp_prepare_cpus       = hi3xxx_smp_prepare_cpus,
+       .smp_boot_secondary     = hi3xxx_boot_secondary,
+};