powerpc/mpc85xx: Add CPU hotplug support for E6500
authorchenhui zhao <chenhui.zhao@freescale.com>
Fri, 20 Nov 2015 09:14:02 +0000 (17:14 +0800)
committerScott Wood <oss@buserror.net>
Sat, 5 Mar 2016 05:58:38 +0000 (23:58 -0600)
Support Freescale E6500 core-based platforms, like t4240.
Support disabling/enabling individual CPU thread dynamically.

Signed-off-by: Chenhui Zhao <chenhui.zhao@freescale.com>
arch/powerpc/include/asm/cputhreads.h
arch/powerpc/include/asm/smp.h
arch/powerpc/kernel/head_64.S
arch/powerpc/platforms/85xx/smp.c

index 81f5b338c76d9e5426e734c438ab453bad43a440..666bef4ebfae72a88c4bceebacbc18a7bfa7b390 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef _ASM_POWERPC_CPUTHREADS_H
 #define _ASM_POWERPC_CPUTHREADS_H
 
+#ifndef __ASSEMBLY__
 #include <linux/cpumask.h>
 
 /*
@@ -103,7 +104,12 @@ static inline u32 get_tensr(void)
        return 1;
 }
 
+void book3e_start_thread(int thread, unsigned long addr);
 void book3e_stop_thread(int thread);
 
+#endif /* __ASSEMBLY__ */
+
+#define INVALID_THREAD_HWID    0x0fff
+
 #endif /* _ASM_POWERPC_CPUTHREADS_H */
 
index bdb811116d859e9ad929f688f5383a9a027673e8..174271ef2767c0c748daae4db37eb464dec874e2 100644 (file)
@@ -200,6 +200,7 @@ extern void generic_secondary_thread_init(void);
 extern unsigned long __secondary_hold_spinloop;
 extern unsigned long __secondary_hold_acknowledge;
 extern char __secondary_hold;
+extern unsigned int booting_thread_hwid;
 
 extern void __early_start(void);
 #endif /* __ASSEMBLY__ */
index 603625368cebad03eecbeb52d1adbd50c240fb54..291628320fbe3fa9392cb0d3a1660b0071d2931c 100644 (file)
@@ -40,6 +40,7 @@
 #include <asm/kvm_book3s_asm.h>
 #include <asm/ptrace.h>
 #include <asm/hw_irq.h>
+#include <asm/cputhreads.h>
 
 /* The physical memory is laid out such that the secondary processor
  * spin code sits at 0x0000...0x00ff. On server, the vectors follow
@@ -181,6 +182,45 @@ exception_marker:
 #endif
 
 #ifdef CONFIG_PPC_BOOK3E
+/*
+ * The booting_thread_hwid holds the thread id we want to boot in cpu
+ * hotplug case. It is set by cpu hotplug code, and is invalid by default.
+ * The thread id is the same as the initial value of SPRN_PIR[THREAD_ID]
+ * bit field.
+ */
+       .globl  booting_thread_hwid
+booting_thread_hwid:
+       .long  INVALID_THREAD_HWID
+       .align 3
+/*
+ * start a thread in the same core
+ * input parameters:
+ * r3 = the thread physical id
+ * r4 = the entry point where thread starts
+ */
+_GLOBAL(book3e_start_thread)
+       LOAD_REG_IMMEDIATE(r5, MSR_KERNEL)
+       cmpi    0, r3, 0
+       beq     10f
+       cmpi    0, r3, 1
+       beq     11f
+       /* If the thread id is invalid, just exit. */
+       b       13f
+10:
+       mttmr   TMRN_IMSR0, r5
+       mttmr   TMRN_INIA0, r4
+       b       12f
+11:
+       mttmr   TMRN_IMSR1, r5
+       mttmr   TMRN_INIA1, r4
+12:
+       isync
+       li      r6, 1
+       sld     r6, r6, r3
+       mtspr   SPRN_TENS, r6
+13:
+       blr
+
 /*
  * stop a thread in the same core
  * input parameter:
@@ -280,6 +320,44 @@ _GLOBAL(generic_secondary_smp_init)
        mr      r3,r24
        mr      r4,r25
        bl      book3e_secondary_core_init
+
+/*
+ * After common core init has finished, check if the current thread is the
+ * one we wanted to boot. If not, start the specified thread and stop the
+ * current thread.
+ */
+       LOAD_REG_ADDR(r4, booting_thread_hwid)
+       lwz     r3, 0(r4)
+       li      r5, INVALID_THREAD_HWID
+       cmpw    r3, r5
+       beq     20f
+
+       /*
+        * The value of booting_thread_hwid has been stored in r3,
+        * so make it invalid.
+        */
+       stw     r5, 0(r4)
+
+       /*
+        * Get the current thread id and check if it is the one we wanted.
+        * If not, start the one specified in booting_thread_hwid and stop
+        * the current thread.
+        */
+       mfspr   r8, SPRN_TIR
+       cmpw    r3, r8
+       beq     20f
+
+       /* start the specified thread */
+       LOAD_REG_ADDR(r5, fsl_secondary_thread_init)
+       ld      r4, 0(r5)
+       bl      book3e_start_thread
+
+       /* stop the current thread */
+       mr      r3, r8
+       bl      book3e_stop_thread
+10:
+       b       10b
+20:
 #endif
 
 generic_secondary_common_init:
index d7cc538ca9dd25378f5031bda9cd5d6ed60974e2..fe9f19e5e935f538dec748c40390e41d2313ff09 100644 (file)
@@ -180,24 +180,11 @@ static inline u32 read_spin_table_addr_l(void *spin_table)
 static void wake_hw_thread(void *info)
 {
        void fsl_secondary_thread_init(void);
-       unsigned long imsr, inia;
-       int nr = *(const int *)info;
+       unsigned long inia;
+       int cpu = *(const int *)info;
 
-       imsr = MSR_KERNEL;
        inia = *(unsigned long *)fsl_secondary_thread_init;
-
-       if (cpu_thread_in_core(nr) == 0) {
-               /* For when we boot on a secondary thread with kdump */
-               mttmr(TMRN_IMSR0, imsr);
-               mttmr(TMRN_INIA0, inia);
-               mtspr(SPRN_TENS, TEN_THREAD(0));
-       } else {
-               mttmr(TMRN_IMSR1, imsr);
-               mttmr(TMRN_INIA1, inia);
-               mtspr(SPRN_TENS, TEN_THREAD(1));
-       }
-
-       smp_generic_kick_cpu(nr);
+       book3e_start_thread(cpu_thread_in_core(cpu), inia);
 }
 #endif
 
@@ -292,33 +279,54 @@ static int smp_85xx_kick_cpu(int nr)
        pr_debug("kick CPU #%d\n", nr);
 
 #ifdef CONFIG_PPC64
-       /* Threads don't use the spin table */
-       if (cpu_thread_in_core(nr) != 0) {
-               int primary = cpu_first_thread_sibling(nr);
-
+       if (threads_per_core == 2) {
                if (WARN_ON_ONCE(!cpu_has_feature(CPU_FTR_SMT)))
                        return -ENOENT;
 
-               if (cpu_thread_in_core(nr) != 1) {
-                       pr_err("%s: cpu %d: invalid hw thread %d\n",
-                              __func__, nr, cpu_thread_in_core(nr));
-                       return -ENOENT;
-               }
+               booting_thread_hwid = cpu_thread_in_core(nr);
+               primary = cpu_first_thread_sibling(nr);
 
-               if (!cpu_online(primary)) {
-                       pr_err("%s: cpu %d: primary %d not online\n",
-                              __func__, nr, primary);
-                       return -ENOENT;
+               if (qoriq_pm_ops)
+                       qoriq_pm_ops->cpu_up_prepare(nr);
+
+               /*
+                * If either thread in the core is online, use it to start
+                * the other.
+                */
+               if (cpu_online(primary)) {
+                       smp_call_function_single(primary,
+                                       wake_hw_thread, &nr, 1);
+                       goto done;
+               } else if (cpu_online(primary + 1)) {
+                       smp_call_function_single(primary + 1,
+                                       wake_hw_thread, &nr, 1);
+                       goto done;
                }
 
-               smp_call_function_single(primary, wake_hw_thread, &nr, 0);
-               return 0;
+               /*
+                * If getting here, it means both threads in the core are
+                * offline. So start the primary thread, then it will start
+                * the thread specified in booting_thread_hwid, the one
+                * corresponding to nr.
+                */
+
+       } else if (threads_per_core == 1) {
+               /*
+                * If one core has only one thread, set booting_thread_hwid to
+                * an invalid value.
+                */
+               booting_thread_hwid = INVALID_THREAD_HWID;
+
+       } else if (threads_per_core > 2) {
+               pr_err("Do not support more than 2 threads per CPU.");
+               return -EINVAL;
        }
 
        ret = smp_85xx_start_cpu(primary);
        if (ret)
                return ret;
 
+done:
        paca[nr].cpu_start = 1;
        generic_set_cpu_up(nr);