aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZhangfei Gao <zhangfei.gao@linaro.org>2013-08-06 09:50:33 +0800
committerZhangfei Gao <zhangfei.gao@linaro.org>2013-08-13 14:33:20 +0800
commit48b9274a17d621dc95653689b4d889a5b8a11665 (patch)
treef2681da430d5c76cd9e85b41cf2258316f6f23ba
parent5829cf8b6b6d88ddec5d9500296c948c64e6f1e5 (diff)
ARM: hs: add hotplug supporttracking-hilt-smp
Enable hotplug support on hi3xxx platform How to test: cat proc/interrupts echo 0 > /sys/devices/system/cpu/cpuX/online cat proc/interrupts echo 1 > /sys/devices/system/cpu/cpuX/online Signed-off-by: Zhangfei Gao <zhangfei.gao@linaro.org> Tested-by: Zhang Mingjun <zhang.mingjun@linaro.org>
-rw-r--r--arch/arm/mach-hs/Makefile1
-rw-r--r--arch/arm/mach-hs/core.h5
-rw-r--r--arch/arm/mach-hs/hotplug.c154
-rw-r--r--arch/arm/mach-hs/hs-dt.c1
-rw-r--r--arch/arm/mach-hs/platsmp.c5
5 files changed, 166 insertions, 0 deletions
diff --git a/arch/arm/mach-hs/Makefile b/arch/arm/mach-hs/Makefile
index ce83a4df2472..bb8556cd4989 100644
--- a/arch/arm/mach-hs/Makefile
+++ b/arch/arm/mach-hs/Makefile
@@ -4,3 +4,4 @@
obj-$(CONFIG_MACH_HS_DT) += hs-dt.o system.o
obj-$(CONFIG_SMP) += platsmp.o
+obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
diff --git a/arch/arm/mach-hs/core.h b/arch/arm/mach-hs/core.h
index f4c17b8d37f6..ca319b85f52c 100644
--- a/arch/arm/mach-hs/core.h
+++ b/arch/arm/mach-hs/core.h
@@ -10,4 +10,9 @@ extern void hs_map_io(void);
extern struct smp_operations hs_smp_ops;
extern void hs_restart(char mode, const char *cmd);
+extern void __init hs_hotplug_init(void);
+extern void hs_cpu_die(unsigned int cpu);
+extern int hs_cpu_kill(unsigned int cpu);
+extern void hs_set_cpu(int cpu, bool enable);
+
#endif
diff --git a/arch/arm/mach-hs/hotplug.c b/arch/arm/mach-hs/hotplug.c
new file mode 100644
index 000000000000..c67f8a22a890
--- /dev/null
+++ b/arch/arm/mach-hs/hotplug.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2013 Linaro Ltd.
+ * Copyright (c) 2013 Hisilicon Limited.
+ *
+ * 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/cpu.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <asm/cacheflush.h>
+#include <asm/smp_plat.h>
+#include "core.h"
+
+enum {
+ HI3620_CTRL,
+ HI3716_CTRL,
+};
+
+static void __iomem *ctrl_base;
+static int id;
+
+void hs_set_cpu(int cpu, bool enable)
+{
+ u32 val = 0;
+
+ if (!ctrl_base)
+ return;
+
+ if (id == HI3620_CTRL) {
+ if (enable) {
+ /* MTCMOS */
+ writel_relaxed((0x01 << (cpu + 3)), ctrl_base + 0xD0);
+ writel_relaxed((0x1011 << cpu), ctrl_base + 0x414);
+ writel_relaxed((0x401011 << cpu), ctrl_base + 0x410);
+
+ /* ISO disable */
+ writel((0x01 << (cpu + 3)), ctrl_base + 0x0C4);
+
+ /* WFI Mask */
+ val = readl(ctrl_base + 0x200);
+ val &= ~(0x1 << (cpu+28));
+ writel(val, ctrl_base + 0x200);
+
+ /* Enable core */
+ writel_relaxed((0x01 << cpu), ctrl_base + 0xf4);
+ /* Unreset */
+ writel_relaxed((0x401011 << cpu), ctrl_base + 0x414);
+ } else {
+ /* iso enable */
+ writel_relaxed((0x01 << (cpu + 3)), ctrl_base + 0xC0);
+
+ /* MTCMOS */
+ writel_relaxed((0x01 << (cpu + 3)), ctrl_base + 0xD4);
+
+ /* wfi mask */
+ val = readl_relaxed(ctrl_base + 0x200);
+ val |= (0x1 << (cpu+28));
+ writel_relaxed(val, ctrl_base + 0x200);
+
+ /* disable core*/
+ writel_relaxed((0x01 << cpu), ctrl_base + 0xf8);
+ /* Reset */
+ writel_relaxed((0x401011 << cpu), ctrl_base + 0x410);
+ }
+ } else if (id == HI3716_CTRL) {
+ if (enable) {
+ /* power on cpu1 */
+ val = readl_relaxed(ctrl_base + 0x1000);
+ val &= ~(0x1 << 8);
+ val |= (0x1 << 7);
+ val &= ~(0x1 << 3);
+ writel_relaxed(val, ctrl_base + 0x1000);
+
+ /* unreset */
+ val = readl_relaxed(ctrl_base + 0x50);
+ val &= ~(0x1 << 17);
+ writel_relaxed(val, ctrl_base + 0x50);
+ } else {
+ /* power down cpu1 */
+ val = readl_relaxed(ctrl_base + 0x1000);
+ val &= ~(0x1 << 8);
+ val |= (0x1 << 7);
+ val |= (0x1 << 3);
+ writel_relaxed(val, ctrl_base + 0x1000);
+
+ /* reset */
+ val = readl_relaxed(ctrl_base + 0x50);
+ val |= (0x1 << 17);
+ writel_relaxed(val, ctrl_base + 0x50);
+ }
+ }
+}
+
+void __init hs_hotplug_init(void)
+{
+ struct device_node *node;
+
+ node = of_find_compatible_node(NULL, NULL, "hisilicon,cpuctrl");
+ if (node) {
+ ctrl_base = of_iomap(node, 0);
+ id = HI3716_CTRL;
+ return;
+ }
+ node = of_find_compatible_node(NULL, NULL, "hisilicon,sctrl");
+ if (node) {
+ ctrl_base = of_iomap(node, 0);
+ id = HI3620_CTRL;
+ return;
+ }
+}
+
+static inline void cpu_enter_lowpower(void)
+{
+ unsigned int v;
+
+ flush_cache_all();
+ asm volatile(
+ /*
+ * Turn off coherency and L1 D-cache
+ */
+ " mrc p15, 0, %0, c1, c0, 1\n"
+ " bic %0, %0, #0x40\n"
+ " mcr p15, 0, %0, c1, c0, 1\n"
+ " mrc p15, 0, %0, c1, c0, 0\n"
+ " bic %0, %0, #0x04\n"
+ " mcr p15, 0, %0, c1, c0, 0\n"
+ : "=&r" (v)
+ : "r" (0)
+ : "cc");
+}
+
+void hs_cpu_die(unsigned int cpu)
+{
+ cpu_enter_lowpower();
+ hs_set_cpu_jump(cpu, phys_to_virt(0));
+ cpu_do_idle();
+
+ /* We should have never returned from idle */
+ panic("cpu %d unexpectedly exit from shutdown\n", cpu);
+}
+
+int hs_cpu_kill(unsigned int cpu)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(50);
+
+ while (hs_get_cpu_jump(cpu))
+ if (time_after(jiffies, timeout))
+ return 0;
+ hs_set_cpu(cpu, false);
+ return 1;
+}
diff --git a/arch/arm/mach-hs/hs-dt.c b/arch/arm/mach-hs/hs-dt.c
index 16240b029ebb..dbec66c3f4d7 100644
--- a/arch/arm/mach-hs/hs-dt.c
+++ b/arch/arm/mach-hs/hs-dt.c
@@ -52,6 +52,7 @@ static void __init hs_timer_init(void)
u32 data[2];
hs_map_io();
+ hs_hotplug_init();
of_clk_init(NULL);
node = of_find_matching_node(NULL, hs_timer_match);
diff --git a/arch/arm/mach-hs/platsmp.c b/arch/arm/mach-hs/platsmp.c
index a76a3cca22e7..6a086307abbf 100644
--- a/arch/arm/mach-hs/platsmp.c
+++ b/arch/arm/mach-hs/platsmp.c
@@ -32,6 +32,7 @@ static void __init hs_smp_prepare_cpus(unsigned int max_cpus)
static int hs_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
+ hs_set_cpu(cpu, true);
hs_set_cpu_jump(cpu, secondary_startup);
arch_send_wakeup_ipi_mask(cpumask_of(cpu));
return 0;
@@ -40,4 +41,8 @@ static int hs_boot_secondary(unsigned int cpu, struct task_struct *idle)
struct smp_operations hs_smp_ops __initdata = {
.smp_prepare_cpus = hs_smp_prepare_cpus,
.smp_boot_secondary = hs_boot_secondary,
+#ifdef CONFIG_HOTPLUG_CPU
+ .cpu_die = hs_cpu_die,
+ .cpu_kill = hs_cpu_kill,
+#endif
};