Various e500 core have different cache architecture, so they
need different cache flush operations. Therefore, add a callback
function cpu_flush_caches to the struct cpu_spec. The cache flush
operation for the specific kind of e500 is selected at init time.
The callback function will flush all caches inside the current cpu.
Signed-off-by: Chenhui Zhao <redacted>
---
arch/powerpc/include/asm/cacheflush.h | 2 -
arch/powerpc/include/asm/cputable.h | 11 +++
arch/powerpc/kernel/asm-offsets.c | 3 +
arch/powerpc/kernel/cpu_setup_fsl_booke.S | 114 +++++++++++++++++++++++++++++-
arch/powerpc/kernel/cputable.c | 4 ++
arch/powerpc/kernel/head_fsl_booke.S | 74 -------------------
arch/powerpc/platforms/85xx/smp.c | 3 +-
7 files changed, 133 insertions(+), 78 deletions(-)
@@ -43,6 +43,13 @@ extern int machine_check_e500(struct pt_regs *regs);externintmachine_check_e200(structpt_regs*regs);externintmachine_check_47x(structpt_regs*regs);+#if defined(CONFIG_E500) || defined(CONFIG_PPC_E500MC)+externvoid__flush_caches_e500v2(void);+externvoid__flush_caches_e500mc(void);+externvoid__flush_caches_e5500(void);+externvoid__flush_caches_e6500(void);+#endif+/* NOTE WELL: Update identify_cpu() if fields are added or removed! */structcpu_spec{/* CPU is matched via (PVR & pvr_mask) == pvr_value */
@@ -59,6 +66,10 @@ struct cpu_spec {unsignedinticache_bsize;unsignedintdcache_bsize;+#if defined(CONFIG_E500) || defined(CONFIG_PPC_E500MC)+/* flush caches inside the current cpu */+void(*cpu_flush_caches)(void);+#endif/* number of performance monitor counters */unsignedintnum_pmcs;enumpowerpc_pmc_typepmc_type;
In sleep mode, the clocks of e500 cores and unused IP blocks is
turned off. The IP blocks which are allowed to wake up the processor
are still running.
The sleep mode is equal to the Standby state in Linux. Use the
command to enter sleep mode:
echo standby > /sys/power/state
Signed-off-by: Chenhui Zhao <redacted>
---
arch/powerpc/Kconfig | 3 +-
arch/powerpc/platforms/85xx/Kconfig | 5 +++
arch/powerpc/platforms/85xx/Makefile | 1 +
arch/powerpc/platforms/85xx/qoriq_pm.c | 59 ++++++++++++++++++++++++++++++++++
arch/powerpc/platforms/86xx/Kconfig | 1 +
5 files changed, 67 insertions(+), 2 deletions(-)
create mode 100644 arch/powerpc/platforms/85xx/qoriq_pm.c
@@ -0,0 +1,23 @@+* Run Control and Power Management++The RCPM performs all device-level tasks associated with device run control+and power management.++Required properites:+ - reg : Offset and length of the register set of RCPM block.+ - compatible : Specifies the compatibility list for the RCPM. The type+ should be string, such as "fsl,qoriq-rcpm-1.0", "fsl,qoriq-rcpm-2.0".++Example:+The RCPM node for T4240:+ rcpm: global-utilities@e2000 {+ compatible = "fsl,t4240-rcpm", "fsl,qoriq-rcpm-2.0";+ reg = <0xe2000 0x1000>;+ };++The RCPM node for P4080:+ rcpm: global-utilities@e2000 {+ compatible = "fsl,qoriq-rcpm-1.0";+ reg = <0xe2000 0x1000>;+ #sleep-cells = <1>;+ };
@@ -0,0 +1,49 @@+/*+*SupportPowerManagement+*+*Copyright2014-2015FreescaleSemiconductorInc.+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodifyit+*underthetermsoftheGNUGeneralPublicLicenseaspublishedbythe+*FreeSoftwareFoundation;eitherversion2oftheLicense,or(atyour+*option)anylaterversion.+*/+#ifndef __PPC_FSL_PM_H+#define __PPC_FSL_PM_H+#ifdef __KERNEL__++#define E500_PM_PH10 1+#define E500_PM_PH15 2+#define E500_PM_PH20 3+#define E500_PM_PH30 4+#define E500_PM_DOZE E500_PM_PH10+#define E500_PM_NAP E500_PM_PH15++#define PLAT_PM_SLEEP 20+#define PLAT_PM_LPM20 30++#define FSL_PM_SLEEP (1 << 0)+#define FSL_PM_DEEP_SLEEP (1 << 1)++structfsl_pm_ops{+/* mask pending interrupts to the RCPM from MPIC */+void(*irq_mask)(intcpu);+/* unmask pending interrupts to the RCPM from MPIC */+void(*irq_unmask)(intcpu);+/* place the CPU in the specified state */+void(*cpu_enter_state)(intcpu,intstate);+/* exit the CPU from the specified state */+void(*cpu_exit_state)(intcpu,intstate);+/* place the platform in the sleep state */+int(*plat_enter_sleep)(void);+/* freeze the time base */+void(*freeze_time_base)(intfreeze);+/* keep the power of IP blocks during sleep/deep sleep */+void(*set_ip_power)(intenable,u32*mask);+/* get platform supported power management modes */+unsignedint(*get_pm_modes)(void);+};++externconststructfsl_pm_ops*qoriq_pm_ops;+#endif /* __KERNEL__ */+#endif /* __PPC_FSL_PM_H */
@@ -0,0 +1,353 @@+/*+*RCPM(RunControl/PowerManagement)support+*+*Copyright2012-2015FreescaleSemiconductorInc.+*+*Author:ChenhuiZhao<chenhui.zhao@freescale.com>+*+*Thisprogramisfreesoftware;youcanredistributeitand/ormodifyit+*underthetermsoftheGNUGeneralPublicLicenseaspublishedbythe+*FreeSoftwareFoundation;eitherversion2oftheLicense,or(atyour+*option)anylaterversion.+*/+#include<linux/types.h>+#include<linux/errno.h>+#include<linux/of_address.h>+#include<linux/export.h>++#include<asm/io.h>+#include<asm/fsl_guts.h>+#include<asm/cputhreads.h>+#include<asm/fsl_pm.h>++#define RCPM_V1 1+#define RCPM_V2 2++conststructfsl_pm_ops*qoriq_pm_ops;++staticstructccsr_rcpm_v1__iomem*rcpm_v1_regs;+staticstructccsr_rcpm_v2__iomem*rcpm_v2_regs;+staticunsignedintfsl_supported_pm_modes;++staticvoidrcpm_v1_irq_mask(intcpu)+{+inthw_cpu=get_hard_smp_processor_id(cpu);+unsignedintmask=1<<hw_cpu;++setbits32(&rcpm_v1_regs->cpmimr,mask);+setbits32(&rcpm_v1_regs->cpmcimr,mask);+setbits32(&rcpm_v1_regs->cpmmcmr,mask);+setbits32(&rcpm_v1_regs->cpmnmimr,mask);+}++staticvoidrcpm_v2_irq_mask(intcpu)+{+inthw_cpu=get_hard_smp_processor_id(cpu);+unsignedintmask=1<<hw_cpu;++setbits32(&rcpm_v2_regs->tpmimr0,mask);+setbits32(&rcpm_v2_regs->tpmcimr0,mask);+setbits32(&rcpm_v2_regs->tpmmcmr0,mask);+setbits32(&rcpm_v2_regs->tpmnmimr0,mask);+}++staticvoidrcpm_v1_irq_unmask(intcpu)+{+inthw_cpu=get_hard_smp_processor_id(cpu);+unsignedintmask=1<<hw_cpu;++clrbits32(&rcpm_v1_regs->cpmimr,mask);+clrbits32(&rcpm_v1_regs->cpmcimr,mask);+clrbits32(&rcpm_v1_regs->cpmmcmr,mask);+clrbits32(&rcpm_v1_regs->cpmnmimr,mask);+}++staticvoidrcpm_v2_irq_unmask(intcpu)+{+inthw_cpu=get_hard_smp_processor_id(cpu);+unsignedintmask=1<<hw_cpu;++clrbits32(&rcpm_v2_regs->tpmimr0,mask);+clrbits32(&rcpm_v2_regs->tpmcimr0,mask);+clrbits32(&rcpm_v2_regs->tpmmcmr0,mask);+clrbits32(&rcpm_v2_regs->tpmnmimr0,mask);+}++staticvoidrcpm_v1_set_ip_power(intenable,u32*mask)+{+if(enable)+setbits32(&rcpm_v1_regs->ippdexpcr,*mask);+else+clrbits32(&rcpm_v1_regs->ippdexpcr,*mask);+}++staticvoidrcpm_v2_set_ip_power(intenable,u32*mask)+{+if(enable)+setbits32(&rcpm_v2_regs->ippdexpcr[0],*mask);+else+clrbits32(&rcpm_v2_regs->ippdexpcr[0],*mask);+}++staticvoidrcpm_v1_cpu_enter_state(intcpu,intstate)+{+inthw_cpu=get_hard_smp_processor_id(cpu);+unsignedintmask=1<<hw_cpu;++switch(state){+caseE500_PM_PH10:+setbits32(&rcpm_v1_regs->cdozcr,mask);+break;+caseE500_PM_PH15:+setbits32(&rcpm_v1_regs->cnapcr,mask);+break;+default:+pr_err("%s: Unknown cpu PM state (%d)\n",__func__,state);+break;+}+}++staticvoidrcpm_v2_cpu_enter_state(intcpu,intstate)+{+inthw_cpu=get_hard_smp_processor_id(cpu);+u32mask=1<<cpu_core_index_of_thread(hw_cpu);++switch(state){+caseE500_PM_PH10:+/* one bit corresponds to one thread for PH10 of 6500 */+setbits32(&rcpm_v2_regs->tph10setr0,1<<hw_cpu);+break;+caseE500_PM_PH15:+setbits32(&rcpm_v2_regs->pcph15setr,mask);+break;+caseE500_PM_PH20:+setbits32(&rcpm_v2_regs->pcph20setr,mask);+break;+caseE500_PM_PH30:+setbits32(&rcpm_v2_regs->pcph30setr,mask);+break;+default:+pr_err("%s: Unknown cpu PM state (%d)\n",__func__,state);+}+}++staticvoidrcpm_v1_cpu_exit_state(intcpu,intstate)+{+inthw_cpu=get_hard_smp_processor_id(cpu);+unsignedintmask=1<<hw_cpu;++switch(state){+caseE500_PM_PH10:+clrbits32(&rcpm_v1_regs->cdozcr,mask);+break;+caseE500_PM_PH15:+clrbits32(&rcpm_v1_regs->cnapcr,mask);+break;+default:+pr_err("%s: Unknown cpu PM state (%d)\n",__func__,state);+break;+}+}++staticvoidrcpm_v2_cpu_exit_state(intcpu,intstate)+{+inthw_cpu=get_hard_smp_processor_id(cpu);+u32mask=1<<cpu_core_index_of_thread(hw_cpu);++switch(state){+caseE500_PM_PH10:+setbits32(&rcpm_v2_regs->tph10clrr0,1<<hw_cpu);+break;+caseE500_PM_PH15:+setbits32(&rcpm_v2_regs->pcph15clrr,mask);+break;+caseE500_PM_PH20:+setbits32(&rcpm_v2_regs->pcph20clrr,mask);+break;+caseE500_PM_PH30:+setbits32(&rcpm_v2_regs->pcph30clrr,mask);+break;+default:+pr_err("%s: Unknown cpu PM state (%d)\n",__func__,state);+}+}++staticintrcpm_v1_plat_enter_state(intstate)+{+u32*pmcsr_reg=&rcpm_v1_regs->powmgtcsr;+intret=0;+intresult;++switch(state){+casePLAT_PM_SLEEP:+setbits32(pmcsr_reg,RCPM_POWMGTCSR_SLP);++/* At this point, the device is in sleep mode. */++/* Upon resume, wait for RCPM_POWMGTCSR_SLP bit to be clear. */+result=spin_event_timeout(+!(in_be32(pmcsr_reg)&RCPM_POWMGTCSR_SLP),10000,10);+if(!result){+pr_err("%s: timeout waiting for SLP bit to be cleared\n",+__func__);+ret=-ETIMEDOUT;+}+break;+default:+pr_err("%s: Unknown platform PM state (%d)\n",+__func__,state);+ret=-EINVAL;+}++returnret;+}++staticintrcpm_v2_plat_enter_state(intstate)+{+u32*pmcsr_reg=&rcpm_v2_regs->powmgtcsr;+intret=0;+intresult;++switch(state){+casePLAT_PM_LPM20:+/* clear previous LPM20 status */+setbits32(pmcsr_reg,RCPM_POWMGTCSR_P_LPM20_ST);+/* enter LPM20 status */+setbits32(pmcsr_reg,RCPM_POWMGTCSR_LPM20_RQ);++/* At this point, the device is in LPM20 status. */++/* resume ... */+result=spin_event_timeout(+!(in_be32(pmcsr_reg)&RCPM_POWMGTCSR_LPM20_ST),10000,10);+if(!result){+pr_err("%s: timeout waiting for LPM20 bit to be cleared\n",+__func__);+ret=-ETIMEDOUT;+}+break;+default:+pr_err("%s: Unknown platform PM state (%d)\n",+__func__,state);+ret=-EINVAL;+}++returnret;+}++staticintrcpm_v1_plat_enter_sleep(void)+{+returnrcpm_v1_plat_enter_state(PLAT_PM_SLEEP);+}++staticintrcpm_v2_plat_enter_sleep(void)+{+returnrcpm_v2_plat_enter_state(PLAT_PM_LPM20);+}++staticvoidrcpm_common_freeze_time_base(u32*tben_reg,intfreeze)+{+staticu32mask;++if(freeze){+mask=in_be32(tben_reg);+clrbits32(tben_reg,mask);+}else{+setbits32(tben_reg,mask);+}++/* read back to push the previous write */+in_be32(tben_reg);+}++staticvoidrcpm_v1_freeze_time_base(intfreeze)+{+rcpm_common_freeze_time_base(&rcpm_v1_regs->ctbenr,freeze);+}++staticvoidrcpm_v2_freeze_time_base(intfreeze)+{+rcpm_common_freeze_time_base(&rcpm_v2_regs->pctbenr,freeze);+}++staticunsignedintrcpm_get_pm_modes(void)+{+returnfsl_supported_pm_modes;+}++staticconststructfsl_pm_opsqoriq_rcpm_v1_ops={+.irq_mask=rcpm_v1_irq_mask,+.irq_unmask=rcpm_v1_irq_unmask,+.cpu_enter_state=rcpm_v1_cpu_enter_state,+.cpu_exit_state=rcpm_v1_cpu_exit_state,+.plat_enter_sleep=rcpm_v1_plat_enter_sleep,+.set_ip_power=rcpm_v1_set_ip_power,+.freeze_time_base=rcpm_v1_freeze_time_base,+.get_pm_modes=rcpm_get_pm_modes,+};++staticconststructfsl_pm_opsqoriq_rcpm_v2_ops={+.irq_mask=rcpm_v2_irq_mask,+.irq_unmask=rcpm_v2_irq_unmask,+.cpu_enter_state=rcpm_v2_cpu_enter_state,+.cpu_exit_state=rcpm_v2_cpu_exit_state,+.plat_enter_sleep=rcpm_v2_plat_enter_sleep,+.set_ip_power=rcpm_v2_set_ip_power,+.freeze_time_base=rcpm_v2_freeze_time_base,+.get_pm_modes=rcpm_get_pm_modes,+};++staticconststructof_device_idrcpm_matches[]={+{+.compatible="fsl,qoriq-rcpm-1.0",+.data=(void*)RCPM_V1,+},+{+.compatible="fsl,qoriq-rcpm-2.0",+.data=(void*)RCPM_V2,+},+{},+};++intfsl_rcpm_init(void)+{+structdevice_node*np;+conststructof_device_id*match;+void__iomem*base;++np=of_find_matching_node_and_match(NULL,rcpm_matches,&match);+if(!np){+pr_err("%s: can't find the rcpm node.\n",__func__);+return-ENODEV;+}++base=of_iomap(np,0);+if(!base){+pr_err("%s: of_iomap() error.\n",__func__);+return-ENOMEM;+}++/* support sleep by default */+fsl_supported_pm_modes=FSL_PM_SLEEP;+of_node_put(np);++switch((unsignedlong)match->data){+caseRCPM_V1:+rcpm_v1_regs=base;+qoriq_pm_ops=&qoriq_rcpm_v1_ops;+break;++caseRCPM_V2:+rcpm_v2_regs=base;+qoriq_pm_ops=&qoriq_rcpm_v2_ops;+break;++default:+break;+}++return0;+}++/* need to call this before SMP init */+early_initcall(fsl_rcpm_init);
Implemented CPU hotplug on e500mc, e5500 and e6500, and support
multiple threads mode and 64-bits mode.
For e6500 with two threads, if one thread is online, it can
enable/disable the other thread in the same core. If two threads of
one core are offline, the core will enter the PH20 state (a low power
state). When the core is up again, Thread0 is up first, and it will be
bound with the present booting cpu. This way, all CPUs can hotplug
separately.
Signed-off-by: Chenhui Zhao <redacted>
---
arch/powerpc/Kconfig | 2 +-
arch/powerpc/include/asm/fsl_pm.h | 4 +
arch/powerpc/include/asm/smp.h | 2 +
arch/powerpc/kernel/head_64.S | 20 +++--
arch/powerpc/kernel/smp.c | 5 ++
arch/powerpc/platforms/85xx/smp.c | 182 +++++++++++++++++++++++++++++---------
arch/powerpc/sysdev/fsl_rcpm.c | 56 ++++++++++++
7 files changed, 220 insertions(+), 51 deletions(-)
@@ -34,6 +34,10 @@ struct fsl_pm_ops {void(*cpu_enter_state)(intcpu,intstate);/* exit the CPU from the specified state */void(*cpu_exit_state)(intcpu,intstate);+/* cpu up */+void(*cpu_up)(intcpu);+/* cpu die */+void(*cpu_die)(intcpu);/* place the platform in the sleep state */int(*plat_enter_sleep)(void);/* freeze the time base */
@@ -454,6 +454,11 @@ int generic_check_cpu_restart(unsigned int cpu)returnper_cpu(cpu_state,cpu)==CPU_UP_PREPARE;}+intgeneric_check_cpu_dead(unsignedintcpu)+{+returnper_cpu(cpu_state,cpu)==CPU_DEAD;+}+staticboolsecondaries_inhibited(void){returnkvm_hv_mode_active();
@@ -43,10 +44,20 @@ struct epapr_spin_table {u32pir;};-staticstructccsr_guts__iomem*guts;+#ifdef CONFIG_HOTPLUG_CPUstaticu64timebase;staticinttb_req;staticinttb_valid;+/* if it is non-zero, synchronize time base */+staticintsync_tb;++#ifdef CONFIG_PPC_E500MC+staticvoidmpc85xx_timebase_freeze(intfreeze)+{+qoriq_pm_ops->freeze_time_base(freeze);+}+#else+staticstructccsr_guts__iomem*guts;staticvoidmpc85xx_timebase_freeze(intfreeze){
@@ -203,28 +273,46 @@ static int smp_85xx_kick_cpu(int nr)pr_debug("smp_85xx_kick_cpu: kick CPU #%d\n",nr);+#ifdef CONFIG_HOTPLUG_CPU+sync_tb=0;+smp_mb();+#endif#ifdef CONFIG_PPC64-/* Threads don't use the spin table */-if(cpu_thread_in_core(nr)!=0){+if(threads_per_core>1){intprimary=cpu_first_thread_sibling(nr);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;+/*+*Ifeitheroneofthreadsinthesamecoreisonline,+*usetheonlineonetostarttheother.+*/+if(cpu_online(primary)||cpu_online(primary+1)){+qoriq_pm_ops->cpu_up(nr);+if(cpu_online(primary))+smp_call_function_single(primary,+wake_hw_thread,&nr,1);+else+smp_call_function_single(primary+1,+wake_hw_thread,&nr,1);+return0;}--if(!cpu_online(primary)){-pr_err("%s: cpu %d: primary %d not online\n",-__func__,nr,primary);-return-ENOENT;+/*+*Ifboththreadsareoffline,resetcoretostart.+*Whencoreisup,Thread0alwaysgetsupfirst,+*sobindthecurrentlogicalcpuwithThread0.+*/+if(hw_cpu!=cpu_first_thread_sibling(hw_cpu)){+inthw_cpu1,hw_cpu2;++hw_cpu1=get_hard_smp_processor_id(primary);+hw_cpu2=get_hard_smp_processor_id(primary+1);+set_hard_smp_processor_id(primary,hw_cpu2);+set_hard_smp_processor_id(primary+1,hw_cpu1);+/* get new physical cpu id */+hw_cpu=get_hard_smp_processor_id(nr);}--smp_call_function_single(primary,wake_hw_thread,&nr,0);-return0;}#endif
@@ -252,11 +340,7 @@ static int smp_85xx_kick_cpu(int nr)spin_table=phys_to_virt(*cpu_rel_addr);local_irq_save(flags);-#ifdef CONFIG_PPC32#ifdef CONFIG_HOTPLUG_CPU-/* Corresponding to generic_set_cpu_dead() */-generic_set_cpu_up(nr);-if(system_state==SYSTEM_RUNNING){/**Tokeepitcompatiblewitholdbootprogramwhichuses
@@ -269,11 +353,16 @@ static int smp_85xx_kick_cpu(int nr)out_be32(&spin_table->addr_l,0);flush_spin_table(spin_table);+#ifdef CONFIG_PPC_E500MC+qoriq_pm_ops->cpu_up(nr);+#endif/**Wedon'tsettheBPTRregisterheresinceitalreadypoints*tothebootpageproperly.*/mpic_reset_core(nr);+sync_tb=1;+smp_mb();/**waituntilcoreisready...
@@ -292,7 +381,12 @@ static int smp_85xx_kick_cpu(int nr)/* clear the acknowledge status */__secondary_hold_acknowledge=-1;}++/* Corresponding to generic_set_cpu_dead() */+generic_set_cpu_up(nr);#endif++#ifdef CONFIG_PPC32flush_spin_table(spin_table);out_be32(&spin_table->pir,hw_cpu);out_be32(&spin_table->addr_l,__pa(__early_start));
@@ -304,9 +398,7 @@ static int smp_85xx_kick_cpu(int nr)pr_err("%s: timeout waiting for core %d to ack\n",__func__,hw_cpu);ret=-ENOENT;-gotoout;}-out:#elsesmp_generic_kick_cpu(nr);
@@ -131,6 +131,46 @@ static void rcpm_v2_cpu_enter_state(int cpu, int state)}}+staticvoidrcpm_v1_cpu_die(intcpu)+{+rcpm_v1_cpu_enter_state(cpu,E500_PM_PH15);+}++staticvoidqoriq_disable_thread(void*info)+{+inthw_cpu=get_hard_smp_processor_id(*(constint*)info);+intthread=cpu_thread_in_core(hw_cpu);++mtspr(SPRN_TENC,TEN_THREAD(thread));+}++staticvoidrcpm_v2_cpu_die(intcpu)+{+intprimary;++if(threads_per_core==1){+rcpm_v2_cpu_enter_state(cpu,E500_PM_PH20);+return;+}++primary=cpu_first_thread_sibling(cpu);+if(cpu_is_offline(primary)&&cpu_is_offline(primary+1)){+/* when two threads are all offline, put core in PH20 */+rcpm_v2_cpu_enter_state(cpu,E500_PM_PH20);+}else{+/*+*Whenonethreadisoffline,disablethethread+*byrunningqoriq_disable_thread()ontheotherthread.+*/+if(cpu_online(primary))+smp_call_function_single(primary,+qoriq_disable_thread,&cpu,1);+else+smp_call_function_single(primary+1,+qoriq_disable_thread,&cpu,1);+}+}+staticvoidrcpm_v1_cpu_exit_state(intcpu,intstate){inthw_cpu=get_hard_smp_processor_id(cpu);
@@ -149,6 +189,12 @@ static void rcpm_v1_cpu_exit_state(int cpu, int state)}}+staticvoidrcpm_v1_cpu_up(intcpu)+{+rcpm_v1_cpu_exit_state(cpu,E500_PM_PH15);+rcpm_v1_irq_unmask(cpu);+}+staticvoidrcpm_v2_cpu_exit_state(intcpu,intstate){inthw_cpu=get_hard_smp_processor_id(cpu);
@@ -172,6 +218,12 @@ static void rcpm_v2_cpu_exit_state(int cpu, int state)}}+staticvoidrcpm_v2_cpu_up(intcpu)+{+rcpm_v2_cpu_exit_state(cpu,E500_PM_PH20);+rcpm_v2_irq_unmask(cpu);+}+staticintrcpm_v1_plat_enter_state(intstate){u32*pmcsr_reg=&rcpm_v1_regs->powmgtcsr;
From: Scott Wood <hidden> Date: 2015-03-31 01:10:41
On Thu, Mar 26, 2015 at 06:18:12PM +0800, chenhui zhao wrote:
quoted hunk
Various e500 core have different cache architecture, so they
need different cache flush operations. Therefore, add a callback
function cpu_flush_caches to the struct cpu_spec. The cache flush
operation for the specific kind of e500 is selected at init time.
The callback function will flush all caches inside the current cpu.
Signed-off-by: Chenhui Zhao <redacted>
---
arch/powerpc/include/asm/cacheflush.h | 2 -
arch/powerpc/include/asm/cputable.h | 11 +++
arch/powerpc/kernel/asm-offsets.c | 3 +
arch/powerpc/kernel/cpu_setup_fsl_booke.S | 114 +++++++++++++++++++++++++++++-
arch/powerpc/kernel/cputable.c | 4 ++
arch/powerpc/kernel/head_fsl_booke.S | 74 -------------------
arch/powerpc/platforms/85xx/smp.c | 3 +-
7 files changed, 133 insertions(+), 78 deletions(-)
/* NOTE WELL: Update identify_cpu() if fields are added or removed! */
struct cpu_spec {
/* CPU is matched via (PVR & pvr_mask) == pvr_value */
@@ -59,6 +66,10 @@ struct cpu_spec { unsigned int icache_bsize; unsigned int dcache_bsize;+#if defined(CONFIG_E500) || defined(CONFIG_PPC_E500MC)
CONFIG_PPC_E500MC implies CONFIG_E500. Why do we need this ifdef?
+ /* flush caches inside the current cpu */
+ void (*cpu_flush_caches)(void);
+#endif
It seems you literally mean "in the cpu" -- If it's a threaded core, then
by "cpu" do you mean "thread" (like we usually do) and thus no caches get
flushed (ignore the fact that it's moot on e6500 -- this is an interface
and needs to be clear).
Also, no-oping L1 flush on e6500 is not compliant with the claim that
you're flushing the cache. You're relying on an unstated assumption that
you'll invalidate that cache later instead.
If you want to make this "flush whatever needs to be flushed for
suspend/hotplug", call it that.
-Scott
@@ -0,0 +1,23 @@+* Run Control and Power Management++The RCPM performs all device-level tasks associated with device run control+and power management.++Required properites:+ - reg : Offset and length of the register set of RCPM block.+ - compatible : Specifies the compatibility list for the RCPM. The type+ should be string, such as "fsl,qoriq-rcpm-1.0", "fsl,qoriq-rcpm-2.0".++Example:+The RCPM node for T4240:+ rcpm: global-utilities@e2000 {+ compatible = "fsl,t4240-rcpm", "fsl,qoriq-rcpm-2.0";+ reg = <0xe2000 0x1000>;+ };++The RCPM node for P4080:+ rcpm: global-utilities@e2000 {+ compatible = "fsl,qoriq-rcpm-1.0";+ reg = <0xe2000 0x1000>;+ #sleep-cells = <1>;+ };
Where is #sleep-cells documented? It's copy-and-paste from something
that was never finished from many years ago.
+#define E500_PM_PH10 1
+#define E500_PM_PH15 2
+#define E500_PM_PH20 3
+#define E500_PM_PH30 4
+#define E500_PM_DOZE E500_PM_PH10
+#define E500_PM_NAP E500_PM_PH15
+
+#define PLAT_PM_SLEEP 20
+#define PLAT_PM_LPM20 30
+
+#define FSL_PM_SLEEP (1 << 0)
+#define FSL_PM_DEEP_SLEEP (1 << 1)
+
+struct fsl_pm_ops {
+ /* mask pending interrupts to the RCPM from MPIC */
+ void (*irq_mask)(int cpu);
+ /* unmask pending interrupts to the RCPM from MPIC */
+ void (*irq_unmask)(int cpu);
+ /* place the CPU in the specified state */
+ void (*cpu_enter_state)(int cpu, int state);
+ /* exit the CPU from the specified state */
+ void (*cpu_exit_state)(int cpu, int state);
+ /* place the platform in the sleep state */
+ int (*plat_enter_sleep)(void);
+ /* freeze the time base */
+ void (*freeze_time_base)(int freeze);
+ /* keep the power of IP blocks during sleep/deep sleep */
+ void (*set_ip_power)(int enable, u32 *mask);
+ /* get platform supported power management modes */
+ unsigned int (*get_pm_modes)(void);
+};
Drop the comments that are basically just a restatement of the function
name. Where there are comments, it'd be easier to read with a blank line
between a function and the next comment.
s/int enable/bool enable/
s/int freeze/bool freeze/
+ default:
+ pr_err("%s: Unknown cpu PM state (%d)\n", __func__, state);
WARN?
+static int rcpm_v2_plat_enter_state(int state)
+{
+ u32 *pmcsr_reg = &rcpm_v2_regs->powmgtcsr;
+ int ret = 0;
+ int result;
+
+ switch (state) {
+ case PLAT_PM_LPM20:
+ /* clear previous LPM20 status */
+ setbits32(pmcsr_reg, RCPM_POWMGTCSR_P_LPM20_ST);
How would the bit be set when you enter here, given that you wait for it
to clear when leaving?
+ /* enter LPM20 status */
+ setbits32(pmcsr_reg, RCPM_POWMGTCSR_LPM20_RQ);
+
+ /* At this point, the device is in LPM20 status. */
+
+ /* resume ... */
+ result = spin_event_timeout(
+ !(in_be32(pmcsr_reg) & RCPM_POWMGTCSR_LPM20_ST), 10000, 10);
+ if (!result) {
+ pr_err("%s: timeout waiting for LPM20 bit to be cleared\n",
+ __func__);
+ ret = -ETIMEDOUT;
+ }
+ break;
"At this point" is a bit misleading. I think it's clear enough if you
just drop that comment.
+ default:
+ pr_err("%s: Unknown platform PM state (%d)\n",
+ __func__, state);
+ ret = -EINVAL;
+ }
From: Scott Wood <hidden> Date: 2015-03-31 02:07:35
On Thu, Mar 26, 2015 at 06:18:14PM +0800, chenhui zhao wrote:
Implemented CPU hotplug on e500mc, e5500 and e6500, and support
multiple threads mode and 64-bits mode.
For e6500 with two threads, if one thread is online, it can
enable/disable the other thread in the same core. If two threads of
one core are offline, the core will enter the PH20 state (a low power
state). When the core is up again, Thread0 is up first, and it will be
bound with the present booting cpu. This way, all CPUs can hotplug
separately.
Signed-off-by: Chenhui Zhao <redacted>
---
arch/powerpc/Kconfig | 2 +-
arch/powerpc/include/asm/fsl_pm.h | 4 +
arch/powerpc/include/asm/smp.h | 2 +
arch/powerpc/kernel/head_64.S | 20 +++--
arch/powerpc/kernel/smp.c | 5 ++
arch/powerpc/platforms/85xx/smp.c | 182 +++++++++++++++++++++++++++++---------
arch/powerpc/sysdev/fsl_rcpm.c | 56 ++++++++++++
7 files changed, 220 insertions(+), 51 deletions(-)
Please factor out changes to generic code (including but not limited to
cur_boot_cpu and PIR handling) into separate patches with clear
explanations.
@@ -34,6 +34,10 @@ struct fsl_pm_ops {void(*cpu_enter_state)(intcpu,intstate);/* exit the CPU from the specified state */void(*cpu_exit_state)(intcpu,intstate);+/* cpu up */+void(*cpu_up)(intcpu);
Again, this sort of comment is useless. Tell us what "cpu up" *does*,
when it should be called, etc.
quoted hunk
@@ -189,16 +193,14 @@ _GLOBAL(fsl_secondary_thread_init) isync /*- * Fix PIR to match the linear numbering in the device tree.- *- * On e6500, the reset value of PIR uses the low three bits for- * the thread within a core, and the upper bits for the core- * number. There are two threads per core, so shift everything- * but the low bit right by two bits so that the cpu numbering is- * continuous.
Why are you getting rid of this? If it's to avoid doing it twice on the
same thread, in my work-in-progress kexec patches I instead check to see
whether BUCSR has already been set up -- if it has, I assume we've
already been here.
+ * The current thread has been in 64-bit mode,
+ * see the value of TMRN_IMSR.
I don't see what the relevance of this comment is here.
Please save non-volatile registers for things that need to stick around
for a while.
mtspr SPRN_PIR, r3
If __cur_boot_cpu is meant to be the PIR of the currently booting CPU,
it's a misleading. It looks like it's supposed to have something to do
with the boot cpu (not "booting").
Also please don't put leading underscores on symbols just because the
adjacent symbols have them.
-#ifdef CONFIG_HOTPLUG_CPU
+#ifdef CONFIG_PPC_E500MC
+static void qoriq_cpu_wait_die(void)
+{
+ unsigned int cpu = smp_processor_id();
+
+ hard_irq_disable();
+ /* mask all irqs to prevent cpu wakeup */
+ qoriq_pm_ops->irq_mask(cpu);
+ idle_task_exit();
+
+ mtspr(SPRN_TCR, 0);
+ mtspr(SPRN_TSR, mfspr(SPRN_TSR));
+
+ cur_cpu_spec->cpu_flush_caches();
+
+ generic_set_cpu_dead(cpu);
+ smp_mb();
Comment memory barriers, as checkpatch says.
+ while (1)
+ ;
Indent the ;
quoted hunk
@@ -174,17 +232,29 @@ 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 imsr1, inia1;+ unsigned long imsr, inia; int nr = *(const int *)info;-- imsr1 = MSR_KERNEL;- inia1 = *(unsigned long *)fsl_secondary_thread_init;-- mttmr(TMRN_IMSR1, imsr1);- mttmr(TMRN_INIA1, inia1);- mtspr(SPRN_TENS, TEN_THREAD(1));+ int hw_cpu = get_hard_smp_processor_id(nr);+ int thread_idx = cpu_thread_in_core(hw_cpu);++ __cur_boot_cpu = (u32)hw_cpu;+ imsr = MSR_KERNEL;+ inia = *(unsigned long *)fsl_secondary_thread_init;+ smp_mb();+ if (thread_idx == 0) {+ mttmr(TMRN_IMSR0, imsr);+ mttmr(TMRN_INIA0, inia);+ } else {+ mttmr(TMRN_IMSR1, imsr);+ mttmr(TMRN_INIA1, inia);+ }+ isync();+ mtspr(SPRN_TENS, TEN_THREAD(thread_idx));
Support for waking a secondary core should be a separate patch (I have
similar code on the way for kexec). Likewise adding smp_mb()/isync() if
it's really needed. In general, this patch tries to do too much at once.
@@ -203,28 +273,46 @@ static int smp_85xx_kick_cpu(int nr) pr_debug("smp_85xx_kick_cpu: kick CPU #%d\n", nr);+#ifdef CONFIG_HOTPLUG_CPU+ sync_tb = 0;+ smp_mb();+#endif
Timebase synchronization should also be separate.
#ifdef CONFIG_PPC64
- /* Threads don't use the spin table */
- if (cpu_thread_in_core(nr) != 0) {
+ if (threads_per_core > 1) {
int primary = cpu_first_thread_sibling(nr);
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;
+ /*
+ * If either one of threads in the same core is online,
+ * use the online one to start the other.
+ */
+ if (cpu_online(primary) || cpu_online(primary + 1)) {
+ qoriq_pm_ops->cpu_up(nr);
What if we don't have qoriq_pm_ops (e.g. VM guest, or some failure)?
+ if (cpu_online(primary))
+ smp_call_function_single(primary,
+ wake_hw_thread, &nr, 1);
+ else
+ smp_call_function_single(primary + 1,
+ wake_hw_thread, &nr, 1);
+ return 0;
}
-
- if (!cpu_online(primary)) {
- pr_err("%s: cpu %d: primary %d not online\n",
- __func__, nr, primary);
- return -ENOENT;
+ /*
+ * If both threads are offline, reset core to start.
+ * When core is up, Thread 0 always gets up first,
+ * so bind the current logical cpu with Thread 0.
+ */
What if the core is not in a PM state that requires a reset?
Where does this reset occur?
+ if (hw_cpu != cpu_first_thread_sibling(hw_cpu)) {
+ int hw_cpu1, hw_cpu2;
+
+ hw_cpu1 = get_hard_smp_processor_id(primary);
+ hw_cpu2 = get_hard_smp_processor_id(primary + 1);
+ set_hard_smp_processor_id(primary, hw_cpu2);
+ set_hard_smp_processor_id(primary + 1, hw_cpu1);
+ /* get new physical cpu id */
+ hw_cpu = get_hard_smp_processor_id(nr);
From: Scott Wood <hidden> Date: 2015-03-31 02:35:19
On Thu, Mar 26, 2015 at 06:18:15PM +0800, chenhui zhao wrote:
quoted hunk
In sleep mode, the clocks of e500 cores and unused IP blocks is
turned off. The IP blocks which are allowed to wake up the processor
are still running.
The sleep mode is equal to the Standby state in Linux. Use the
command to enter sleep mode:
echo standby > /sys/power/state
Signed-off-by: Chenhui Zhao <redacted>
---
arch/powerpc/Kconfig | 3 +-
arch/powerpc/platforms/85xx/Kconfig | 5 +++
arch/powerpc/platforms/85xx/Makefile | 1 +
arch/powerpc/platforms/85xx/qoriq_pm.c | 59 ++++++++++++++++++++++++++++++++++
arch/powerpc/platforms/86xx/Kconfig | 1 +
5 files changed, 67 insertions(+), 2 deletions(-)
create mode 100644 arch/powerpc/platforms/85xx/qoriq_pm.c
________________________________________
From: Wood Scott-B07421
Sent: Tuesday, March 31, 2015 9:10
To: Zhao Chenhui-B35336
Cc: linuxppc-dev@lists.ozlabs.org; devicetree@vger.kernel.org; linux-kernel=
@vger.kernel.org; Jin Zhengxiong-R64188
Subject: Re: [1/4] powerpc/cache: add cache flush operation for various e50=
0
On Thu, Mar 26, 2015 at 06:18:12PM +0800, chenhui zhao wrote:
Various e500 core have different cache architecture, so they
need different cache flush operations. Therefore, add a callback
function cpu_flush_caches to the struct cpu_spec. The cache flush
operation for the specific kind of e500 is selected at init time.
The callback function will flush all caches inside the current cpu.
Signed-off-by: Chenhui Zhao <redacted>
---
arch/powerpc/include/asm/cacheflush.h | 2 -
arch/powerpc/include/asm/cputable.h | 11 +++
arch/powerpc/kernel/asm-offsets.c | 3 +
arch/powerpc/kernel/cpu_setup_fsl_booke.S | 114 ++++++++++++++++++++++++=
Why the leading underscores?
[chenhui] Will get rid of them.
quoted hunk
/* NOTE WELL: Update identify_cpu() if fields are added or removed! */
struct cpu_spec {
/* CPU is matched via (PVR & pvr_mask) =3D=3D pvr_value */
@@ -59,6 +66,10 @@ struct cpu_spec { unsigned int icache_bsize; unsigned int dcache_bsize;+#if defined(CONFIG_E500) || defined(CONFIG_PPC_E500MC)
CONFIG_PPC_E500MC implies CONFIG_E500. Why do we need this ifdef?
[chenhui] Change to "#ifdef CONFIG_E500".
+ /* flush caches inside the current cpu */
+ void (*cpu_flush_caches)(void);
+#endif
It seems you literally mean "in the cpu" -- If it's a threaded core, then
by "cpu" do you mean "thread" (like we usually do) and thus no caches get
flushed (ignore the fact that it's moot on e6500 -- this is an interface
and needs to be clear).
Also, no-oping L1 flush on e6500 is not compliant with the claim that
you're flushing the cache. You're relying on an unstated assumption that
you'll invalidate that cache later instead.
If you want to make this "flush whatever needs to be flushed for
suspend/hotplug", call it that.
-Scott
[chenhui] OK. Then, call "cpu_down_flush".
________________________________________
From: Wood Scott-B07421
Sent: Tuesday, March 31, 2015 9:30
To: Zhao Chenhui-B35336
Cc: linuxppc-dev@lists.ozlabs.org; devicetree@vger.kernel.org; linux-kernel=
@vger.kernel.org; Jin Zhengxiong-R64188
Subject: Re: [2/4] powerpc/rcpm: add RCPM driver
On Thu, Mar 26, 2015 at 06:18:13PM +0800, chenhui zhao wrote:
There is a RCPM (Run Control/Power Management) in Freescale QorIQ
series processors. The device performs tasks associated with device
run control and power management.
The driver implements some features: mask/unmask irq, enter/exit low
power states, freeze time base, etc.
Signed-off-by: Chenhui Zhao <redacted>
---
Documentation/devicetree/bindings/soc/fsl/rcpm.txt | 23 ++
arch/powerpc/include/asm/fsl_guts.h | 105 ++++++
arch/powerpc/include/asm/fsl_pm.h | 49 +++
arch/powerpc/platforms/85xx/Kconfig | 1 +
arch/powerpc/sysdev/Kconfig | 5 +
arch/powerpc/sysdev/Makefile | 1 +
arch/powerpc/sysdev/fsl_rcpm.c | 353 +++++++++++++++=
@@ -0,0 +1,23 @@+* Run Control and Power Management++The RCPM performs all device-level tasks associated with device run cont=
rol
+and power management.
+
+Required properites:
+ - reg : Offset and length of the register set of RCPM block.
+ - compatible : Specifies the compatibility list for the RCPM. The type
+ should be string, such as "fsl,qoriq-rcpm-1.0", "fsl,qoriq-rcpm-2.0"=
+ * under the terms of the GNU General Public License as published by =
the
+ * Free Software Foundation; either version 2 of the License, or (at y=
our
+ * option) any later version.
+ */
+#ifndef __PPC_FSL_PM_H
+#define __PPC_FSL_PM_H
+#ifdef __KERNEL__
Put a space after #ifdef, not a tab.
[Chenhui] Will change it.
+#define E500_PM_PH10 1
+#define E500_PM_PH15 2
+#define E500_PM_PH20 3
+#define E500_PM_PH30 4
+#define E500_PM_DOZE E500_PM_PH10
+#define E500_PM_NAP E500_PM_PH15
+
+#define PLAT_PM_SLEEP 20
+#define PLAT_PM_LPM20 30
+
+#define FSL_PM_SLEEP (1 << 0)
+#define FSL_PM_DEEP_SLEEP (1 << 1)
+
+struct fsl_pm_ops {
+ /* mask pending interrupts to the RCPM from MPIC */
+ void (*irq_mask)(int cpu);
+ /* unmask pending interrupts to the RCPM from MPIC */
+ void (*irq_unmask)(int cpu);
+ /* place the CPU in the specified state */
+ void (*cpu_enter_state)(int cpu, int state);
+ /* exit the CPU from the specified state */
+ void (*cpu_exit_state)(int cpu, int state);
+ /* place the platform in the sleep state */
+ int (*plat_enter_sleep)(void);
+ /* freeze the time base */
+ void (*freeze_time_base)(int freeze);
+ /* keep the power of IP blocks during sleep/deep sleep */
+ void (*set_ip_power)(int enable, u32 *mask);
+ /* get platform supported power management modes */
+ unsigned int (*get_pm_modes)(void);
+};
Drop the comments that are basically just a restatement of the function
name. Where there are comments, it'd be easier to read with a blank line
between a function and the next comment.
s/int enable/bool enable/
s/int freeze/bool freeze/
[chenhui] Yes, you are right.
+ default:
+ pr_err("%s: Unknown cpu PM state (%d)\n", __func__, state);
WARN?
+static int rcpm_v2_plat_enter_state(int state)
+{
+ u32 *pmcsr_reg =3D &rcpm_v2_regs->powmgtcsr;
+ int ret =3D 0;
+ int result;
+
+ switch (state) {
+ case PLAT_PM_LPM20:
+ /* clear previous LPM20 status */
+ setbits32(pmcsr_reg, RCPM_POWMGTCSR_P_LPM20_ST);
How would the bit be set when you enter here, given that you wait for it
to clear when leaving?
[chenhui] Actually, the bit is not used by software. Just follow the instru=
ction in RM.
+ /* enter LPM20 status */
+ setbits32(pmcsr_reg, RCPM_POWMGTCSR_LPM20_RQ);
+
+ /* At this point, the device is in LPM20 status. */
+
+ /* resume ... */
+ result =3D spin_event_timeout(
+ !(in_be32(pmcsr_reg) & RCPM_POWMGTCSR_LPM20_ST), 10000, 1=
0);
+ if (!result) {
+ pr_err("%s: timeout waiting for LPM20 bit to be cle=
ared\n",
+ __func__);
+ ret =3D -ETIMEDOUT;
+ }
+ break;
"At this point" is a bit misleading. I think it's clear enough if you
just drop that comment.
+ default:
+ pr_err("%s: Unknown platform PM state (%d)\n",
+ __func__, state);
+ ret =3D -EINVAL;
+ }
________________________________________
From: Wood Scott-B07421
Sent: Tuesday, March 31, 2015 10:07
To: Zhao Chenhui-B35336
Cc: linuxppc-dev@lists.ozlabs.org; devicetree@vger.kernel.org; linux-kernel=
@vger.kernel.org; Jin Zhengxiong-R64188
Subject: Re: [3/4] powerpc: support CPU hotplug for e500mc, e5500 and e6500
On Thu, Mar 26, 2015 at 06:18:14PM +0800, chenhui zhao wrote:
Implemented CPU hotplug on e500mc, e5500 and e6500, and support
multiple threads mode and 64-bits mode.
For e6500 with two threads, if one thread is online, it can
enable/disable the other thread in the same core. If two threads of
one core are offline, the core will enter the PH20 state (a low power
state). When the core is up again, Thread0 is up first, and it will be
bound with the present booting cpu. This way, all CPUs can hotplug
separately.
Signed-off-by: Chenhui Zhao <redacted>
---
arch/powerpc/Kconfig | 2 +-
arch/powerpc/include/asm/fsl_pm.h | 4 +
arch/powerpc/include/asm/smp.h | 2 +
arch/powerpc/kernel/head_64.S | 20 +++--
arch/powerpc/kernel/smp.c | 5 ++
arch/powerpc/platforms/85xx/smp.c | 182 +++++++++++++++++++++++++++++---=
Please factor out changes to generic code (including but not limited to
cur_boot_cpu and PIR handling) into separate patches with clear
explanations.
[chenhui] OK.
@@ -34,6 +34,10 @@ struct fsl_pm_ops {void(*cpu_enter_state)(intcpu,intstate);/* exit the CPU from the specified state */void(*cpu_exit_state)(intcpu,intstate);+/* cpu up */+void(*cpu_up)(intcpu);
Again, this sort of comment is useless. Tell us what "cpu up" *does*,
when it should be called, etc.
quoted hunk
@@ -189,16 +193,14 @@ _GLOBAL(fsl_secondary_thread_init) isync /*- * Fix PIR to match the linear numbering in the device tree.- *- * On e6500, the reset value of PIR uses the low three bits for- * the thread within a core, and the upper bits for the core- * number. There are two threads per core, so shift everything- * but the low bit right by two bits so that the cpu numbering is- * continuous.
Why are you getting rid of this? If it's to avoid doing it twice on the
same thread, in my work-in-progress kexec patches I instead check to see
whether BUCSR has already been set up -- if it has, I assume we've
already been here.
[chenhui] I didn't delete the branch prediction related code.
+ * The current thread has been in 64-bit mode,
+ * see the value of TMRN_IMSR.
I don't see what the relevance of this comment is here.
[chenhui] Will explain it more clear.
Please save non-volatile registers for things that need to stick around
for a while.
[chenhui] OK.
mtspr SPRN_PIR, r3
If __cur_boot_cpu is meant to be the PIR of the currently booting CPU,
it's a misleading. It looks like it's supposed to have something to do
with the boot cpu (not "booting").
[chenhui] I mean the PIR of the currently booting CPU. Change to "booting_c=
pu_hwid".
Also please don't put leading underscores on symbols just because the
adjacent symbols have them.
-#ifdef CONFIG_HOTPLUG_CPU
+#ifdef CONFIG_PPC_E500MC
+static void qoriq_cpu_wait_die(void)
+{
+ unsigned int cpu =3D smp_processor_id();
+
+ hard_irq_disable();
+ /* mask all irqs to prevent cpu wakeup */
+ qoriq_pm_ops->irq_mask(cpu);
+ idle_task_exit();
+
+ mtspr(SPRN_TCR, 0);
+ mtspr(SPRN_TSR, mfspr(SPRN_TSR));
+
+ cur_cpu_spec->cpu_flush_caches();
+
+ generic_set_cpu_dead(cpu);
+ smp_mb();
static void wake_hw_thread(void *info)
{
void fsl_secondary_thread_init(void);
- unsigned long imsr1, inia1;
+ unsigned long imsr, inia;
int nr =3D *(const int *)info;
-
- imsr1 =3D MSR_KERNEL;
- inia1 =3D *(unsigned long *)fsl_secondary_thread_init;
-
- mttmr(TMRN_IMSR1, imsr1);
- mttmr(TMRN_INIA1, inia1);
- mtspr(SPRN_TENS, TEN_THREAD(1));
+ int hw_cpu =3D get_hard_smp_processor_id(nr);
+ int thread_idx =3D cpu_thread_in_core(hw_cpu);
+
+ __cur_boot_cpu =3D (u32)hw_cpu;
+ imsr =3D MSR_KERNEL;
+ inia =3D *(unsigned long *)fsl_secondary_thread_init;
+ smp_mb();
+ if (thread_idx =3D=3D 0) {
+ mttmr(TMRN_IMSR0, imsr);
+ mttmr(TMRN_INIA0, inia);
+ } else {
+ mttmr(TMRN_IMSR1, imsr);
+ mttmr(TMRN_INIA1, inia);
+ }
+ isync();
+ mtspr(SPRN_TENS, TEN_THREAD(thread_idx));
Support for waking a secondary core should be a separate patch (I have
similar code on the way for kexec). Likewise adding smp_mb()/isync() if
it's really needed. In general, this patch tries to do too much at once.
@@ -203,28 +273,46 @@ static int smp_85xx_kick_cpu(int nr) pr_debug("smp_85xx_kick_cpu: kick CPU #%d\n", nr);+#ifdef CONFIG_HOTPLUG_CPU+ sync_tb =3D 0;+ smp_mb();+#endif
Timebase synchronization should also be separate.
#ifdef CONFIG_PPC64
- /* Threads don't use the spin table */
- if (cpu_thread_in_core(nr) !=3D 0) {
+ if (threads_per_core > 1) {
int primary =3D cpu_first_thread_sibling(nr);
if (WARN_ON_ONCE(!cpu_has_feature(CPU_FTR_SMT)))
return -ENOENT;
- if (cpu_thread_in_core(nr) !=3D 1) {
- pr_err("%s: cpu %d: invalid hw thread %d\n",
- __func__, nr, cpu_thread_in_core(nr));
- return -ENOENT;
+ /*
+ * If either one of threads in the same core is online,
+ * use the online one to start the other.
+ */
+ if (cpu_online(primary) || cpu_online(primary + 1)) {
+ qoriq_pm_ops->cpu_up(nr);
What if we don't have qoriq_pm_ops (e.g. VM guest, or some failure)?
[chenhui] Will put it in an if statement.
+ if (cpu_online(primary))
+ smp_call_function_single(primary,
+ wake_hw_thread, &nr, 1);
+ else
+ smp_call_function_single(primary + 1,
+ wake_hw_thread, &nr, 1);
+ return 0;
}
-
- if (!cpu_online(primary)) {
- pr_err("%s: cpu %d: primary %d not online\n",
- __func__, nr, primary);
- return -ENOENT;
+ /*
+ * If both threads are offline, reset core to start.
+ * When core is up, Thread 0 always gets up first,
+ * so bind the current logical cpu with Thread 0.
+ */
What if the core is not in a PM state that requires a reset?
Where does this reset occur?
[chenhui] Reset occurs in the function mpic_reset_core().
+ if (hw_cpu !=3D cpu_first_thread_sibling(hw_cpu)) {
+ int hw_cpu1, hw_cpu2;
+
+ hw_cpu1 =3D get_hard_smp_processor_id(primary);
+ hw_cpu2 =3D get_hard_smp_processor_id(primary + 1);
+ set_hard_smp_processor_id(primary, hw_cpu2);
+ set_hard_smp_processor_id(primary + 1, hw_cpu1);
+ /* get new physical cpu id */
+ hw_cpu =3D get_hard_smp_processor_id(nr);
Why are you swapping the hard smp ids?
[chenhui] For example, Core1 has two threads, Thread0 and Thread1. In norma=
l boot, Thread0 is CPU2, and Thread1 is CPU3.
But, if CPU2 and CPU3 are all off, user wants CPU3 up first. we need to cal=
l Thread0 as CPU3 and Thead1 as CPU2, considering
the limitation, after core is reset, only Thread0 is up, then Thread0 kicks=
up Thread1.
________________________________________
From: Wood Scott-B07421
Sent: Tuesday, March 31, 2015 10:35
To: Zhao Chenhui-B35336
Cc: linuxppc-dev@lists.ozlabs.org; devicetree@vger.kernel.org; linux-kernel=
@vger.kernel.org; Jin Zhengxiong-R64188
Subject: Re: [4/4] powerpc/85xx: support sleep feature on QorIQ SoCs with R=
CPM
On Thu, Mar 26, 2015 at 06:18:15PM +0800, chenhui zhao wrote:
In sleep mode, the clocks of e500 cores and unused IP blocks is
turned off. The IP blocks which are allowed to wake up the processor
are still running.
The sleep mode is equal to the Standby state in Linux. Use the
command to enter sleep mode:
echo standby > /sys/power/state
Signed-off-by: Chenhui Zhao <redacted>
---
arch/powerpc/Kconfig | 3 +-
arch/powerpc/platforms/85xx/Kconfig | 5 +++
arch/powerpc/platforms/85xx/Makefile | 1 +
arch/powerpc/platforms/85xx/qoriq_pm.c | 59 ++++++++++++++++++++++++++++=
From: Scott Wood <hidden> Date: 2015-04-02 15:51:01
On Thu, 2015-04-02 at 05:33 -0500, Zhao Chenhui-B35336 wrote:
quoted
+static int rcpm_v2_plat_enter_state(int state)
+{
+ u32 *pmcsr_reg = &rcpm_v2_regs->powmgtcsr;
+ int ret = 0;
+ int result;
+
+ switch (state) {
+ case PLAT_PM_LPM20:
+ /* clear previous LPM20 status */
+ setbits32(pmcsr_reg, RCPM_POWMGTCSR_P_LPM20_ST);
How would the bit be set when you enter here, given that you wait for it
to clear when leaving?
[chenhui] Actually, the bit is not used by software. Just follow the instruction in RM.
Sorry, I missed the "_P_" and thought it was RCPM_POWMGTCSR_LPM20_ST.
-Scott
From: Scott Wood <hidden> Date: 2015-04-02 16:03:29
On Thu, 2015-04-02 at 06:16 -0500, Zhao Chenhui-B35336 wrote:
________________________________________
From: Wood Scott-B07421
Sent: Tuesday, March 31, 2015 10:07
To: Zhao Chenhui-B35336
Cc: linuxppc-dev@lists.ozlabs.org; devicetree@vger.kernel.org; linux-kernel@vger.kernel.org; Jin Zhengxiong-R64188
Subject: Re: [3/4] powerpc: support CPU hotplug for e500mc, e5500 and e6500
On Thu, Mar 26, 2015 at 06:18:14PM +0800, chenhui zhao wrote:
quoted
@@ -189,16 +193,14 @@ _GLOBAL(fsl_secondary_thread_init) isync /*- * Fix PIR to match the linear numbering in the device tree.- *- * On e6500, the reset value of PIR uses the low three bits for- * the thread within a core, and the upper bits for the core- * number. There are two threads per core, so shift everything- * but the low bit right by two bits so that the cpu numbering is- * continuous.
Why are you getting rid of this? If it's to avoid doing it twice on the
same thread, in my work-in-progress kexec patches I instead check to see
whether BUCSR has already been set up -- if it has, I assume we've
already been here.
[chenhui] I didn't delete the branch prediction related code.
I didn't say you did. I'm saying that you can check whether BUCSR has
been set up, to determine whether PIR has already been adjusted, if your
concern is avoiding running this twice on a thread between core resets.
If that's not your concern, then please explain.
quoted
+ /*
+ * If both threads are offline, reset core to start.
+ * When core is up, Thread 0 always gets up first,
+ * so bind the current logical cpu with Thread 0.
+ */
What if the core is not in a PM state that requires a reset?
Where does this reset occur?
[chenhui] Reset occurs in the function mpic_reset_core().
quoted
+ if (hw_cpu != cpu_first_thread_sibling(hw_cpu)) {
+ int hw_cpu1, hw_cpu2;
+
+ hw_cpu1 = get_hard_smp_processor_id(primary);
+ hw_cpu2 = get_hard_smp_processor_id(primary + 1);
+ set_hard_smp_processor_id(primary, hw_cpu2);
+ set_hard_smp_processor_id(primary + 1, hw_cpu1);
+ /* get new physical cpu id */
+ hw_cpu = get_hard_smp_processor_id(nr);
Why are you swapping the hard smp ids?
[chenhui] For example, Core1 has two threads, Thread0 and Thread1. In normal boot, Thread0 is CPU2, and Thread1 is CPU3.
But, if CPU2 and CPU3 are all off, user wants CPU3 up first. we need to call Thread0 as CPU3 and Thead1 as CPU2, considering
the limitation, after core is reset, only Thread0 is up, then Thread0 kicks up Thread1.
There's no need for this. I have booting from a thread1, and having it
kick its thread0, working locally without messing with the hwid/cpu
mapping.
________________________________________
From: Wood Scott-B07421
Sent: Friday, April 3, 2015 0:03
To: Zhao Chenhui-B35336
Cc: linuxppc-dev@lists.ozlabs.org; devicetree@vger.kernel.org; linux-kernel=
@vger.kernel.org; Jin Zhengxiong-R64188
Subject: Re: [3/4] powerpc: support CPU hotplug for e500mc, e5500 and e6500
On Thu, 2015-04-02 at 06:16 -0500, Zhao Chenhui-B35336 wrote:
________________________________________
From: Wood Scott-B07421
Sent: Tuesday, March 31, 2015 10:07
To: Zhao Chenhui-B35336
Cc: linuxppc-dev@lists.ozlabs.org; devicetree@vger.kernel.org; linux-kern=
el@vger.kernel.org; Jin Zhengxiong-R64188
Subject: Re: [3/4] powerpc: support CPU hotplug for e500mc, e5500 and e65=
00
On Thu, Mar 26, 2015 at 06:18:14PM +0800, chenhui zhao wrote:
quoted
@@ -189,16 +193,14 @@ _GLOBAL(fsl_secondary_thread_init) isync /*- * Fix PIR to match the linear numbering in the device tree.- *- * On e6500, the reset value of PIR uses the low three bits for- * the thread within a core, and the upper bits for the core- * number. There are two threads per core, so shift everything- * but the low bit right by two bits so that the cpu numbering is- * continuous.
Why are you getting rid of this? If it's to avoid doing it twice on the
same thread, in my work-in-progress kexec patches I instead check to see
whether BUCSR has already been set up -- if it has, I assume we've
already been here.
[chenhui] I didn't delete the branch prediction related code.
I didn't say you did. I'm saying that you can check whether BUCSR has
been set up, to determine whether PIR has already been adjusted, if your
concern is avoiding running this twice on a thread between core resets.
If that's not your concern, then please explain.
[chenhui] If no need to change PIR in CPU hotplug, I will change the code a=
s you mentioned.
quoted
+ /*
+ * If both threads are offline, reset core to start.
+ * When core is up, Thread 0 always gets up first,
+ * so bind the current logical cpu with Thread 0.
+ */
What if the core is not in a PM state that requires a reset?
Where does this reset occur?
[chenhui] Reset occurs in the function mpic_reset_core().
+ set_hard_smp_processor_id(primary, hw_cpu2);
+ set_hard_smp_processor_id(primary + 1, hw_cpu1);
+ /* get new physical cpu id */
+ hw_cpu =3D get_hard_smp_processor_id(nr);
Why are you swapping the hard smp ids?
[chenhui] For example, Core1 has two threads, Thread0 and Thread1. In nor=
mal boot, Thread0 is CPU2, and Thread1 is CPU3.
But, if CPU2 and CPU3 are all off, user wants CPU3 up first. we need to c=
all Thread0 as CPU3 and Thead1 as CPU2, considering
the limitation, after core is reset, only Thread0 is up, then Thread0 kic=
ks up Thread1.
There's no need for this. I have booting from a thread1, and having it
kick its thread0, working locally without messing with the hwid/cpu
mapping.
[chenhui] Great. If you have completed your patches, can we merge our code?