18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Symmetric Multi Processing (SMP) support for Armada XP
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2012 Marvell
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Lior Amsalem <alior@marvell.com>
78c2ecf20Sopenharmony_ci * Yehuda Yitschak <yehuday@marvell.com>
88c2ecf20Sopenharmony_ci * Gregory CLEMENT <gregory.clement@free-electrons.com>
98c2ecf20Sopenharmony_ci * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci * This file is licensed under the terms of the GNU General Public
128c2ecf20Sopenharmony_ci * License version 2.  This program is licensed "as is" without any
138c2ecf20Sopenharmony_ci * warranty of any kind, whether express or implied.
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci * The Armada XP SoC has 4 ARMv7 PJ4B CPUs running in full HW coherency
168c2ecf20Sopenharmony_ci * This file implements the routines for preparing the SMP infrastructure
178c2ecf20Sopenharmony_ci * and waking up the secondary CPUs
188c2ecf20Sopenharmony_ci */
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include <linux/init.h>
218c2ecf20Sopenharmony_ci#include <linux/smp.h>
228c2ecf20Sopenharmony_ci#include <linux/clk.h>
238c2ecf20Sopenharmony_ci#include <linux/of.h>
248c2ecf20Sopenharmony_ci#include <linux/of_address.h>
258c2ecf20Sopenharmony_ci#include <linux/mbus.h>
268c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
278c2ecf20Sopenharmony_ci#include <asm/smp_plat.h>
288c2ecf20Sopenharmony_ci#include "common.h"
298c2ecf20Sopenharmony_ci#include "armada-370-xp.h"
308c2ecf20Sopenharmony_ci#include "pmsu.h"
318c2ecf20Sopenharmony_ci#include "coherency.h"
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci#define ARMADA_XP_MAX_CPUS 4
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci#define AXP_BOOTROM_BASE 0xfff00000
368c2ecf20Sopenharmony_ci#define AXP_BOOTROM_SIZE 0x100000
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic struct clk *boot_cpu_clk;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_cistatic struct clk *get_cpu_clk(int cpu)
418c2ecf20Sopenharmony_ci{
428c2ecf20Sopenharmony_ci	struct clk *cpu_clk;
438c2ecf20Sopenharmony_ci	struct device_node *np = of_get_cpu_node(cpu, NULL);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	if (WARN(!np, "missing cpu node\n"))
468c2ecf20Sopenharmony_ci		return NULL;
478c2ecf20Sopenharmony_ci	cpu_clk = of_clk_get(np, 0);
488c2ecf20Sopenharmony_ci	if (WARN_ON(IS_ERR(cpu_clk)))
498c2ecf20Sopenharmony_ci		return NULL;
508c2ecf20Sopenharmony_ci	return cpu_clk;
518c2ecf20Sopenharmony_ci}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_cistatic int armada_xp_boot_secondary(unsigned int cpu, struct task_struct *idle)
548c2ecf20Sopenharmony_ci{
558c2ecf20Sopenharmony_ci	int ret, hw_cpu;
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	pr_info("Booting CPU %d\n", cpu);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	hw_cpu = cpu_logical_map(cpu);
608c2ecf20Sopenharmony_ci	mvebu_pmsu_set_cpu_boot_addr(hw_cpu, armada_xp_secondary_startup);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	/*
638c2ecf20Sopenharmony_ci	 * This is needed to wake up CPUs in the offline state after
648c2ecf20Sopenharmony_ci	 * using CPU hotplug.
658c2ecf20Sopenharmony_ci	 */
668c2ecf20Sopenharmony_ci	arch_send_wakeup_ipi_mask(cpumask_of(cpu));
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	/*
698c2ecf20Sopenharmony_ci	 * This is needed to take secondary CPUs out of reset on the
708c2ecf20Sopenharmony_ci	 * initial boot.
718c2ecf20Sopenharmony_ci	 */
728c2ecf20Sopenharmony_ci	ret = mvebu_cpu_reset_deassert(hw_cpu);
738c2ecf20Sopenharmony_ci	if (ret) {
748c2ecf20Sopenharmony_ci		pr_warn("unable to boot CPU: %d\n", ret);
758c2ecf20Sopenharmony_ci		return ret;
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	return 0;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci/*
828c2ecf20Sopenharmony_ci * When a CPU is brought back online, either through CPU hotplug, or
838c2ecf20Sopenharmony_ci * because of the boot of a kexec'ed kernel, the PMSU configuration
848c2ecf20Sopenharmony_ci * for this CPU might be in the deep idle state, preventing this CPU
858c2ecf20Sopenharmony_ci * from receiving interrupts. Here, we therefore take out the current
868c2ecf20Sopenharmony_ci * CPU from this state, which was entered by armada_xp_cpu_die()
878c2ecf20Sopenharmony_ci * below.
888c2ecf20Sopenharmony_ci */
898c2ecf20Sopenharmony_cistatic void armada_xp_secondary_init(unsigned int cpu)
908c2ecf20Sopenharmony_ci{
918c2ecf20Sopenharmony_ci	mvebu_v7_pmsu_idle_exit();
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_cistatic void __init armada_xp_smp_init_cpus(void)
958c2ecf20Sopenharmony_ci{
968c2ecf20Sopenharmony_ci	unsigned int ncores = num_possible_cpus();
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if (ncores == 0 || ncores > ARMADA_XP_MAX_CPUS)
998c2ecf20Sopenharmony_ci		panic("Invalid number of CPUs in DT\n");
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic int armada_xp_sync_secondary_clk(unsigned int cpu)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	struct clk *cpu_clk = get_cpu_clk(cpu);
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	if (!cpu_clk || !boot_cpu_clk)
1078c2ecf20Sopenharmony_ci		return 0;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	clk_prepare_enable(cpu_clk);
1108c2ecf20Sopenharmony_ci	clk_set_rate(cpu_clk, clk_get_rate(boot_cpu_clk));
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	return 0;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic void __init armada_xp_smp_prepare_cpus(unsigned int max_cpus)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	struct device_node *node;
1188c2ecf20Sopenharmony_ci	struct resource res;
1198c2ecf20Sopenharmony_ci	int err;
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	flush_cache_all();
1228c2ecf20Sopenharmony_ci	set_cpu_coherent();
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	boot_cpu_clk = get_cpu_clk(smp_processor_id());
1258c2ecf20Sopenharmony_ci	if (boot_cpu_clk) {
1268c2ecf20Sopenharmony_ci		clk_prepare_enable(boot_cpu_clk);
1278c2ecf20Sopenharmony_ci		cpuhp_setup_state_nocalls(CPUHP_AP_ARM_MVEBU_SYNC_CLOCKS,
1288c2ecf20Sopenharmony_ci					  "arm/mvebu/sync_clocks:online",
1298c2ecf20Sopenharmony_ci					  armada_xp_sync_secondary_clk, NULL);
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	/*
1338c2ecf20Sopenharmony_ci	 * In order to boot the secondary CPUs we need to ensure
1348c2ecf20Sopenharmony_ci	 * the bootROM is mapped at the correct address.
1358c2ecf20Sopenharmony_ci	 */
1368c2ecf20Sopenharmony_ci	node = of_find_compatible_node(NULL, NULL, "marvell,bootrom");
1378c2ecf20Sopenharmony_ci	if (!node)
1388c2ecf20Sopenharmony_ci		panic("Cannot find 'marvell,bootrom' compatible node");
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ci	err = of_address_to_resource(node, 0, &res);
1418c2ecf20Sopenharmony_ci	of_node_put(node);
1428c2ecf20Sopenharmony_ci	if (err < 0)
1438c2ecf20Sopenharmony_ci		panic("Cannot get 'bootrom' node address");
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	if (res.start != AXP_BOOTROM_BASE ||
1468c2ecf20Sopenharmony_ci	    resource_size(&res) != AXP_BOOTROM_SIZE)
1478c2ecf20Sopenharmony_ci		panic("The address for the BootROM is incorrect");
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
1518c2ecf20Sopenharmony_cistatic void armada_xp_cpu_die(unsigned int cpu)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	/*
1548c2ecf20Sopenharmony_ci	 * CPU hotplug is implemented by putting offline CPUs into the
1558c2ecf20Sopenharmony_ci	 * deep idle sleep state.
1568c2ecf20Sopenharmony_ci	 */
1578c2ecf20Sopenharmony_ci	armada_370_xp_pmsu_idle_enter(true);
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci/*
1618c2ecf20Sopenharmony_ci * We need a dummy function, so that platform_can_cpu_hotplug() knows
1628c2ecf20Sopenharmony_ci * we support CPU hotplug. However, the function does not need to do
1638c2ecf20Sopenharmony_ci * anything, because CPUs going offline can enter the deep idle state
1648c2ecf20Sopenharmony_ci * by themselves, without any help from a still alive CPU.
1658c2ecf20Sopenharmony_ci */
1668c2ecf20Sopenharmony_cistatic int armada_xp_cpu_kill(unsigned int cpu)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	return 1;
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci#endif
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ciconst struct smp_operations armada_xp_smp_ops __initconst = {
1738c2ecf20Sopenharmony_ci	.smp_init_cpus		= armada_xp_smp_init_cpus,
1748c2ecf20Sopenharmony_ci	.smp_prepare_cpus	= armada_xp_smp_prepare_cpus,
1758c2ecf20Sopenharmony_ci	.smp_boot_secondary	= armada_xp_boot_secondary,
1768c2ecf20Sopenharmony_ci	.smp_secondary_init     = armada_xp_secondary_init,
1778c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
1788c2ecf20Sopenharmony_ci	.cpu_die		= armada_xp_cpu_die,
1798c2ecf20Sopenharmony_ci	.cpu_kill               = armada_xp_cpu_kill,
1808c2ecf20Sopenharmony_ci#endif
1818c2ecf20Sopenharmony_ci};
1828c2ecf20Sopenharmony_ci
1838c2ecf20Sopenharmony_ciCPU_METHOD_OF_DECLARE(armada_xp_smp, "marvell,armada-xp-smp",
1848c2ecf20Sopenharmony_ci		      &armada_xp_smp_ops);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci#define MV98DX3236_CPU_RESUME_CTRL_REG 0x08
1878c2ecf20Sopenharmony_ci#define MV98DX3236_CPU_RESUME_ADDR_REG 0x04
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic const struct of_device_id of_mv98dx3236_resume_table[] = {
1908c2ecf20Sopenharmony_ci	{
1918c2ecf20Sopenharmony_ci		.compatible = "marvell,98dx3336-resume-ctrl",
1928c2ecf20Sopenharmony_ci	},
1938c2ecf20Sopenharmony_ci	{ /* end of list */ },
1948c2ecf20Sopenharmony_ci};
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_cistatic int mv98dx3236_resume_set_cpu_boot_addr(int hw_cpu, void *boot_addr)
1978c2ecf20Sopenharmony_ci{
1988c2ecf20Sopenharmony_ci	struct device_node *np;
1998c2ecf20Sopenharmony_ci	void __iomem *base;
2008c2ecf20Sopenharmony_ci	WARN_ON(hw_cpu != 1);
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	np = of_find_matching_node(NULL, of_mv98dx3236_resume_table);
2038c2ecf20Sopenharmony_ci	if (!np)
2048c2ecf20Sopenharmony_ci		return -ENODEV;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	base = of_io_request_and_map(np, 0, of_node_full_name(np));
2078c2ecf20Sopenharmony_ci	of_node_put(np);
2088c2ecf20Sopenharmony_ci	if (IS_ERR(base))
2098c2ecf20Sopenharmony_ci		return PTR_ERR(base);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	writel(0, base + MV98DX3236_CPU_RESUME_CTRL_REG);
2128c2ecf20Sopenharmony_ci	writel(__pa_symbol(boot_addr), base + MV98DX3236_CPU_RESUME_ADDR_REG);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	iounmap(base);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	return 0;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic int mv98dx3236_boot_secondary(unsigned int cpu, struct task_struct *idle)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	int ret, hw_cpu;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	hw_cpu = cpu_logical_map(cpu);
2248c2ecf20Sopenharmony_ci	mv98dx3236_resume_set_cpu_boot_addr(hw_cpu,
2258c2ecf20Sopenharmony_ci					    armada_xp_secondary_startup);
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	/*
2288c2ecf20Sopenharmony_ci	 * This is needed to wake up CPUs in the offline state after
2298c2ecf20Sopenharmony_ci	 * using CPU hotplug.
2308c2ecf20Sopenharmony_ci	 */
2318c2ecf20Sopenharmony_ci	arch_send_wakeup_ipi_mask(cpumask_of(cpu));
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	/*
2348c2ecf20Sopenharmony_ci	 * This is needed to take secondary CPUs out of reset on the
2358c2ecf20Sopenharmony_ci	 * initial boot.
2368c2ecf20Sopenharmony_ci	 */
2378c2ecf20Sopenharmony_ci	ret = mvebu_cpu_reset_deassert(hw_cpu);
2388c2ecf20Sopenharmony_ci	if (ret) {
2398c2ecf20Sopenharmony_ci		pr_warn("unable to boot CPU: %d\n", ret);
2408c2ecf20Sopenharmony_ci		return ret;
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	return 0;
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic const struct smp_operations mv98dx3236_smp_ops __initconst = {
2478c2ecf20Sopenharmony_ci	.smp_init_cpus		= armada_xp_smp_init_cpus,
2488c2ecf20Sopenharmony_ci	.smp_prepare_cpus	= armada_xp_smp_prepare_cpus,
2498c2ecf20Sopenharmony_ci	.smp_boot_secondary	= mv98dx3236_boot_secondary,
2508c2ecf20Sopenharmony_ci	.smp_secondary_init     = armada_xp_secondary_init,
2518c2ecf20Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
2528c2ecf20Sopenharmony_ci	.cpu_die		= armada_xp_cpu_die,
2538c2ecf20Sopenharmony_ci	.cpu_kill               = armada_xp_cpu_kill,
2548c2ecf20Sopenharmony_ci#endif
2558c2ecf20Sopenharmony_ci};
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ciCPU_METHOD_OF_DECLARE(mv98dx3236_smp, "marvell,98dx3236-smp",
2588c2ecf20Sopenharmony_ci		      &mv98dx3236_smp_ops);
259