LoongArch: Remove clock setting during cpu hotplug stage

On physical machine we can save power by disabling clock of hot removed
cpu. However as different platforms require different methods to
configure clocks, the code is platform-specific, and probably belongs to
firmware/pmu or cpu regulator, rather than generic arch/loongarch code.

Also, there is no such register on QEMU virt machine since the
clock/frequency regulation is not emulated.

This patch removes the hard-coded clock register accesses in generic
LoongArch cpu hotplug flow.

Reviewed-by: WANG Xuerui <git@xen0n.name>
Signed-off-by: Bibo Mao <maobibo@loongson.cn>
Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
This commit is contained in:
Bibo Mao 2022-07-20 15:21:51 +08:00 committed by Huacai Chen
parent f62b7626cb
commit 71610ab1d0
2 changed files with 13 additions and 101 deletions

View File

@ -278,116 +278,29 @@ void loongson3_cpu_die(unsigned int cpu)
mb(); mb();
} }
/* void play_dead(void)
* The target CPU should go to XKPRANGE (uncached area) and flush
* ICache/DCache/VCache before the control CPU can safely disable its clock.
*/
static void loongson3_play_dead(int *state_addr)
{ {
register int val; register uint64_t addr;
register void *addr;
register void (*init_fn)(void); register void (*init_fn)(void);
__asm__ __volatile__( idle_task_exit();
" li.d %[addr], 0x8000000000000000\n"
"1: cacop 0x8, %[addr], 0 \n" /* flush ICache */
" cacop 0x8, %[addr], 1 \n"
" cacop 0x8, %[addr], 2 \n"
" cacop 0x8, %[addr], 3 \n"
" cacop 0x9, %[addr], 0 \n" /* flush DCache */
" cacop 0x9, %[addr], 1 \n"
" cacop 0x9, %[addr], 2 \n"
" cacop 0x9, %[addr], 3 \n"
" addi.w %[sets], %[sets], -1 \n"
" addi.d %[addr], %[addr], 0x40 \n"
" bnez %[sets], 1b \n"
" li.d %[addr], 0x8000000000000000\n"
"2: cacop 0xa, %[addr], 0 \n" /* flush VCache */
" cacop 0xa, %[addr], 1 \n"
" cacop 0xa, %[addr], 2 \n"
" cacop 0xa, %[addr], 3 \n"
" cacop 0xa, %[addr], 4 \n"
" cacop 0xa, %[addr], 5 \n"
" cacop 0xa, %[addr], 6 \n"
" cacop 0xa, %[addr], 7 \n"
" cacop 0xa, %[addr], 8 \n"
" cacop 0xa, %[addr], 9 \n"
" cacop 0xa, %[addr], 10 \n"
" cacop 0xa, %[addr], 11 \n"
" cacop 0xa, %[addr], 12 \n"
" cacop 0xa, %[addr], 13 \n"
" cacop 0xa, %[addr], 14 \n"
" cacop 0xa, %[addr], 15 \n"
" addi.w %[vsets], %[vsets], -1 \n"
" addi.d %[addr], %[addr], 0x40 \n"
" bnez %[vsets], 2b \n"
" li.w %[val], 0x7 \n" /* *state_addr = CPU_DEAD; */
" st.w %[val], %[state_addr], 0 \n"
" dbar 0 \n"
" cacop 0x11, %[state_addr], 0 \n" /* flush entry of *state_addr */
: [addr] "=&r" (addr), [val] "=&r" (val)
: [state_addr] "r" (state_addr),
[sets] "r" (cpu_data[smp_processor_id()].dcache.sets),
[vsets] "r" (cpu_data[smp_processor_id()].vcache.sets));
local_irq_enable(); local_irq_enable();
change_csr_ecfg(ECFG0_IM, ECFGF_IPI); set_csr_ecfg(ECFGF_IPI);
__this_cpu_write(cpu_state, CPU_DEAD);
__asm__ __volatile__( __smp_mb();
" idle 0 \n" do {
" li.w $t0, 0x1020 \n" __asm__ __volatile__("idle 0\n\t");
" iocsrrd.d %[init_fn], $t0 \n" /* Get init PC */ addr = iocsr_read64(LOONGARCH_IOCSR_MBUF0);
: [init_fn] "=&r" (addr) } while (addr == 0);
: /* No Input */
: "a0"); init_fn = (void *)TO_CACHE(addr);
init_fn = __va(addr); iocsr_write32(0xffffffff, LOONGARCH_IOCSR_IPI_CLEAR);
init_fn(); init_fn();
unreachable(); unreachable();
} }
void play_dead(void)
{
int *state_addr;
unsigned int cpu = smp_processor_id();
void (*play_dead_uncached)(int *s);
idle_task_exit();
play_dead_uncached = (void *)TO_UNCACHE(__pa((unsigned long)loongson3_play_dead));
state_addr = &per_cpu(cpu_state, cpu);
mb();
play_dead_uncached(state_addr);
}
static int loongson3_enable_clock(unsigned int cpu)
{
uint64_t core_id = cpu_data[cpu].core;
uint64_t package_id = cpu_data[cpu].package;
LOONGSON_FREQCTRL(package_id) |= 1 << (core_id * 4 + 3);
return 0;
}
static int loongson3_disable_clock(unsigned int cpu)
{
uint64_t core_id = cpu_data[cpu].core;
uint64_t package_id = cpu_data[cpu].package;
LOONGSON_FREQCTRL(package_id) &= ~(1 << (core_id * 4 + 3));
return 0;
}
static int register_loongson3_notifier(void)
{
return cpuhp_setup_state_nocalls(CPUHP_LOONGARCH_SOC_PREPARE,
"loongarch/loongson:prepare",
loongson3_enable_clock,
loongson3_disable_clock);
}
early_initcall(register_loongson3_notifier);
#endif #endif
/* /*

View File

@ -130,7 +130,6 @@ enum cpuhp_state {
CPUHP_ZCOMP_PREPARE, CPUHP_ZCOMP_PREPARE,
CPUHP_TIMERS_PREPARE, CPUHP_TIMERS_PREPARE,
CPUHP_MIPS_SOC_PREPARE, CPUHP_MIPS_SOC_PREPARE,
CPUHP_LOONGARCH_SOC_PREPARE,
CPUHP_BP_PREPARE_DYN, CPUHP_BP_PREPARE_DYN,
CPUHP_BP_PREPARE_DYN_END = CPUHP_BP_PREPARE_DYN + 20, CPUHP_BP_PREPARE_DYN_END = CPUHP_BP_PREPARE_DYN + 20,
CPUHP_BRINGUP_CPU, CPUHP_BRINGUP_CPU,