18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * SMP support for SoCs with SCU covered by mach-shmobile
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2013  Magnus Damm
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/cpu.h>
88c2ecf20Sopenharmony_ci#include <linux/delay.h>
98c2ecf20Sopenharmony_ci#include <linux/init.h>
108c2ecf20Sopenharmony_ci#include <linux/io.h>
118c2ecf20Sopenharmony_ci#include <linux/smp.h>
128c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
138c2ecf20Sopenharmony_ci#include <asm/smp_plat.h>
148c2ecf20Sopenharmony_ci#include <asm/smp_scu.h>
158c2ecf20Sopenharmony_ci#include "common.h"
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_cistatic phys_addr_t shmobile_scu_base_phys;
198c2ecf20Sopenharmony_cistatic void __iomem *shmobile_scu_base;
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic int shmobile_scu_cpu_prepare(unsigned int cpu)
228c2ecf20Sopenharmony_ci{
238c2ecf20Sopenharmony_ci	/* For this particular CPU register SCU SMP boot vector */
248c2ecf20Sopenharmony_ci	shmobile_smp_hook(cpu, __pa_symbol(shmobile_boot_scu),
258c2ecf20Sopenharmony_ci			  shmobile_scu_base_phys);
268c2ecf20Sopenharmony_ci	return 0;
278c2ecf20Sopenharmony_ci}
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_civoid __init shmobile_smp_scu_prepare_cpus(phys_addr_t scu_base_phys,
308c2ecf20Sopenharmony_ci					  unsigned int max_cpus)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	/* install boot code shared by all CPUs */
338c2ecf20Sopenharmony_ci	shmobile_boot_fn = __pa_symbol(shmobile_smp_boot);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	/* enable SCU and cache coherency on booting CPU */
368c2ecf20Sopenharmony_ci	shmobile_scu_base_phys = scu_base_phys;
378c2ecf20Sopenharmony_ci	shmobile_scu_base = ioremap(scu_base_phys, PAGE_SIZE);
388c2ecf20Sopenharmony_ci	scu_enable(shmobile_scu_base);
398c2ecf20Sopenharmony_ci	scu_power_mode(shmobile_scu_base, SCU_PM_NORMAL);
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	/* Use CPU notifier for reset vector control */
428c2ecf20Sopenharmony_ci	cpuhp_setup_state_nocalls(CPUHP_ARM_SHMOBILE_SCU_PREPARE,
438c2ecf20Sopenharmony_ci				  "arm/shmobile-scu:prepare",
448c2ecf20Sopenharmony_ci				  shmobile_scu_cpu_prepare, NULL);
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
488c2ecf20Sopenharmony_civoid shmobile_smp_scu_cpu_die(unsigned int cpu)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	/* For this particular CPU deregister boot vector */
518c2ecf20Sopenharmony_ci	shmobile_smp_hook(cpu, 0, 0);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	dsb();
548c2ecf20Sopenharmony_ci	flush_cache_all();
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	/* disable cache coherency */
578c2ecf20Sopenharmony_ci	scu_power_mode(shmobile_scu_base, SCU_PM_POWEROFF);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	/* jump to shared mach-shmobile sleep / reset code */
608c2ecf20Sopenharmony_ci	shmobile_smp_sleep();
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic int shmobile_smp_scu_psr_core_disabled(int cpu)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	unsigned long mask = SCU_PM_POWEROFF << (cpu * 8);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	if ((__raw_readl(shmobile_scu_base + 8) & mask) == mask)
688c2ecf20Sopenharmony_ci		return 1;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci	return 0;
718c2ecf20Sopenharmony_ci}
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ciint shmobile_smp_scu_cpu_kill(unsigned int cpu)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	int k;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	/* this function is running on another CPU than the offline target,
788c2ecf20Sopenharmony_ci	 * here we need wait for shutdown code in platform_cpu_die() to
798c2ecf20Sopenharmony_ci	 * finish before asking SoC-specific code to power off the CPU core.
808c2ecf20Sopenharmony_ci	 */
818c2ecf20Sopenharmony_ci	for (k = 0; k < 1000; k++) {
828c2ecf20Sopenharmony_ci		if (shmobile_smp_scu_psr_core_disabled(cpu))
838c2ecf20Sopenharmony_ci			return 1;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci		mdelay(1);
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	return 0;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci#endif
91