csky: CPU-hotplug supported for SMP
authorGuo Ren <ren_guo@c-sky.com>
Wed, 19 Dec 2018 11:56:14 +0000 (19:56 +0800)
committerGuo Ren <ren_guo@c-sky.com>
Mon, 31 Dec 2018 15:03:53 +0000 (23:03 +0800)
This is a simple implement of CPU-hotplug for power saving. CPU use
wait instruction to enter power saving mode and waiting for IPI wakeup
signal.

Signed-off-by: Guo Ren <ren_guo@c-sky.com>
arch/csky/Kconfig
arch/csky/include/asm/smp.h
arch/csky/kernel/smp.c

index cb64f8dacd08ee6fa56f622ddb89299f1305f607..8bdbe9219662246da7d3eaa700105a290d10ba4e 100644 (file)
@@ -198,6 +198,15 @@ config RAM_BASE
        hex "DRAM start addr (the same with memory-section in dts)"
        default 0x0
 
+config HOTPLUG_CPU
+       bool "Support for hot-pluggable CPUs"
+       select GENERIC_IRQ_MIGRATION
+       depends on SMP
+       help
+         Say Y here to allow turning CPUs off and on. CPUs can be
+         controlled through /sys/devices/system/cpu/cpu1/hotplug/target.
+
+         Say N if you want to disable CPU hotplug.
 endmenu
 
 source "kernel/Kconfig.hz"
index 4a929c4d6437fd43fc97a72db8b1fa6a6cda483b..668b79ce29ea111d700f66d4ccf348acd85a3f7b 100644 (file)
@@ -21,6 +21,10 @@ void __init set_send_ipi(void (*func)(const struct cpumask *mask), int irq);
 
 #define raw_smp_processor_id() (current_thread_info()->cpu)
 
+int __cpu_disable(void);
+
+void __cpu_die(unsigned int cpu);
+
 #endif /* CONFIG_SMP */
 
 #endif /* __ASM_CSKY_SMP_H */
index 74d627300c55f58a372ed631eee505da9f18f987..ddc4dd79f2826f837b3557a239043f7b0aa1c162 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/of.h>
 #include <linux/sched/task_stack.h>
 #include <linux/sched/mm.h>
+#include <linux/sched/hotplug.h>
 #include <asm/irq.h>
 #include <asm/traps.h>
 #include <asm/sections.h>
@@ -112,12 +113,8 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
 {
 }
 
-static void __init enable_smp_ipi(void)
-{
-       enable_percpu_irq(ipi_irq, 0);
-}
-
 static int ipi_dummy_dev;
+
 void __init setup_smp_ipi(void)
 {
        int rc;
@@ -130,7 +127,7 @@ void __init setup_smp_ipi(void)
        if (rc)
                panic("%s IRQ request failed\n", __func__);
 
-       enable_smp_ipi();
+       enable_percpu_irq(ipi_irq, 0);
 }
 
 void __init setup_smp(void)
@@ -161,12 +158,10 @@ volatile unsigned int secondary_stack;
 
 int __cpu_up(unsigned int cpu, struct task_struct *tidle)
 {
-       unsigned int tmp;
-
-       secondary_stack = (unsigned int)tidle->stack + THREAD_SIZE;
+       unsigned long mask = 1 << cpu;
 
+       secondary_stack = (unsigned int)tidle->stack + THREAD_SIZE - 8;
        secondary_hint = mfcr("cr31");
-
        secondary_ccr  = mfcr("cr18");
 
        /*
@@ -176,10 +171,13 @@ int __cpu_up(unsigned int cpu, struct task_struct *tidle)
         */
        mtcr("cr17", 0x22);
 
-       /* Enable cpu in SMP reset ctrl reg */
-       tmp = mfcr("cr<29, 0>");
-       tmp |= 1 << cpu;
-       mtcr("cr<29, 0>", tmp);
+       if (mask & mfcr("cr<29, 0>")) {
+               send_arch_ipi(cpumask_of(cpu));
+       } else {
+               /* Enable cpu in SMP reset ctrl reg */
+               mask |= mfcr("cr<29, 0>");
+               mtcr("cr<29, 0>", mask);
+       }
 
        /* Wait for the cpu online */
        while (!cpu_online(cpu));
@@ -219,7 +217,7 @@ void csky_start_secondary(void)
        init_fpu();
 #endif
 
-       enable_smp_ipi();
+       enable_percpu_irq(ipi_irq, 0);
 
        mmget(mm);
        mmgrab(mm);
@@ -235,3 +233,46 @@ void csky_start_secondary(void)
        preempt_disable();
        cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);
 }
+
+#ifdef CONFIG_HOTPLUG_CPU
+int __cpu_disable(void)
+{
+       unsigned int cpu = smp_processor_id();
+
+       set_cpu_online(cpu, false);
+
+       irq_migrate_all_off_this_cpu();
+
+       clear_tasks_mm_cpumask(cpu);
+
+       return 0;
+}
+
+void __cpu_die(unsigned int cpu)
+{
+       if (!cpu_wait_death(cpu, 5)) {
+               pr_crit("CPU%u: shutdown failed\n", cpu);
+               return;
+       }
+       pr_notice("CPU%u: shutdown\n", cpu);
+}
+
+void arch_cpu_idle_dead(void)
+{
+       idle_task_exit();
+
+       cpu_report_death();
+
+       while (!secondary_stack)
+               arch_cpu_idle();
+
+       local_irq_disable();
+
+       asm volatile(
+               "mov    sp, %0\n"
+               "mov    r8, %0\n"
+               "jmpi   csky_start_secondary"
+               :
+               : "r" (secondary_stack));
+}
+#endif