MIPS: smp-cps: Add support for CPU hotplug of MIPSr6 processors
authorMatt Redfearn <matt.redfearn@imgtec.com>
Thu, 7 Jul 2016 07:50:39 +0000 (08:50 +0100)
committerRalf Baechle <ralf@linux-mips.org>
Sun, 24 Jul 2016 10:19:57 +0000 (12:19 +0200)
Introduce support for hotplug of Virtual Processors in MIPSr6 systems.
The method is simpler than the VPE parallel from the now-deprecated MT
ASE, it can now simply write the VP_STOP register with the mask of VPs
to halt, and use the VP_RUNNING register to determine when the VP has
halted.

Signed-off-by: Matt Redfearn <matt.redfearn@imgtec.com>
Reviewed-by: Paul Burton <paul.burton@imgtec.com>
Cc: Matt Redfearn <matt.redfearn@imgtec.com>
Cc: Qais Yousef <qais.yousef@imgtec.com>
Cc: linux-mips@linux-mips.org
Cc: linux-kernel@vger.kernel.org
Patchwork: https://patchwork.linux-mips.org/patch/13752/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
arch/mips/kernel/smp-cps.c

index 006e99d..234e7e7 100644 (file)
@@ -412,14 +412,16 @@ static enum {
 
 void play_dead(void)
 {
-       unsigned cpu, core;
+       unsigned int cpu, core, vpe_id;
 
        local_irq_disable();
        idle_task_exit();
        cpu = smp_processor_id();
        cpu_death = CPU_DEATH_POWER;
 
-       if (cpu_has_mipsmt) {
+       pr_debug("CPU%d going offline\n", cpu);
+
+       if (cpu_has_mipsmt || cpu_has_vp) {
                core = cpu_data[cpu].core;
 
                /* Look for another online VPE within the core */
@@ -440,10 +442,21 @@ void play_dead(void)
        complete(&cpu_death_chosen);
 
        if (cpu_death == CPU_DEATH_HALT) {
-               /* Halt this TC */
-               write_c0_tchalt(TCHALT_H);
-               instruction_hazard();
+               vpe_id = cpu_vpe_id(&cpu_data[cpu]);
+
+               pr_debug("Halting core %d VP%d\n", core, vpe_id);
+               if (cpu_has_mipsmt) {
+                       /* Halt this TC */
+                       write_c0_tchalt(TCHALT_H);
+                       instruction_hazard();
+               } else if (cpu_has_vp) {
+                       write_cpc_cl_vp_stop(1 << vpe_id);
+
+                       /* Ensure that the VP_STOP register is written */
+                       wmb();
+               }
        } else {
+               pr_debug("Gating power to core %d\n", core);
                /* Power down the core */
                cps_pm_enter_state(CPS_PM_POWER_GATED);
        }
@@ -470,6 +483,7 @@ static void wait_for_sibling_halt(void *ptr_cpu)
 static void cps_cpu_die(unsigned int cpu)
 {
        unsigned core = cpu_data[cpu].core;
+       unsigned int vpe_id = cpu_vpe_id(&cpu_data[cpu]);
        unsigned stat;
        int err;
 
@@ -498,10 +512,12 @@ static void cps_cpu_die(unsigned int cpu)
                 * in which case the CPC will refuse to power down the core.
                 */
                do {
+                       mips_cm_lock_other(core, vpe_id);
                        mips_cpc_lock_other(core);
                        stat = read_cpc_co_stat_conf();
                        stat &= CPC_Cx_STAT_CONF_SEQSTATE_MSK;
                        mips_cpc_unlock_other();
+                       mips_cm_unlock_other();
                } while (stat != CPC_Cx_STAT_CONF_SEQSTATE_D0 &&
                         stat != CPC_Cx_STAT_CONF_SEQSTATE_D2 &&
                         stat != CPC_Cx_STAT_CONF_SEQSTATE_U2);
@@ -518,6 +534,12 @@ static void cps_cpu_die(unsigned int cpu)
                                               (void *)(unsigned long)cpu, 1);
                if (err)
                        panic("Failed to call remote sibling CPU\n");
+       } else if (cpu_has_vp) {
+               do {
+                       mips_cm_lock_other(core, vpe_id);
+                       stat = read_cpc_co_vp_running();
+                       mips_cm_unlock_other();
+               } while (stat & (1 << vpe_id));
        }
 }