Skip to content

Commit

Permalink
LoongArch: Remove clock setting during cpu hotplug stage
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
bibo-mao authored and chenhuacai committed Jul 29, 2022
1 parent f62b762 commit 71610ab
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 101 deletions.
113 changes: 13 additions & 100 deletions arch/loongarch/kernel/smp.c
Expand Up @@ -278,116 +278,29 @@ void loongson3_cpu_die(unsigned int cpu)
mb();
}

/*
* 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)
void play_dead(void)
{
register int val;
register void *addr;
register uint64_t addr;
register void (*init_fn)(void);

__asm__ __volatile__(
" 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));

idle_task_exit();
local_irq_enable();
change_csr_ecfg(ECFG0_IM, ECFGF_IPI);
set_csr_ecfg(ECFGF_IPI);
__this_cpu_write(cpu_state, CPU_DEAD);

__smp_mb();
do {
__asm__ __volatile__("idle 0\n\t");
addr = iocsr_read64(LOONGARCH_IOCSR_MBUF0);
} while (addr == 0);

__asm__ __volatile__(
" idle 0 \n"
" li.w $t0, 0x1020 \n"
" iocsrrd.d %[init_fn], $t0 \n" /* Get init PC */
: [init_fn] "=&r" (addr)
: /* No Input */
: "a0");
init_fn = __va(addr);
init_fn = (void *)TO_CACHE(addr);
iocsr_write32(0xffffffff, LOONGARCH_IOCSR_IPI_CLEAR);

init_fn();
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

/*
Expand Down
1 change: 0 additions & 1 deletion include/linux/cpuhotplug.h
Expand Up @@ -130,7 +130,6 @@ enum cpuhp_state {
CPUHP_ZCOMP_PREPARE,
CPUHP_TIMERS_PREPARE,
CPUHP_MIPS_SOC_PREPARE,
CPUHP_LOONGARCH_SOC_PREPARE,
CPUHP_BP_PREPARE_DYN,
CPUHP_BP_PREPARE_DYN_END = CPUHP_BP_PREPARE_DYN + 20,
CPUHP_BRINGUP_CPU,
Expand Down

0 comments on commit 71610ab

Please sign in to comment.