162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Copyright (C) 2002 ARM Ltd.
462306a36Sopenharmony_ci *  All Rights Reserved
562306a36Sopenharmony_ci */
662306a36Sopenharmony_ci#include <linux/init.h>
762306a36Sopenharmony_ci#include <linux/errno.h>
862306a36Sopenharmony_ci#include <linux/smp.h>
962306a36Sopenharmony_ci#include <linux/io.h>
1062306a36Sopenharmony_ci#include <linux/of_address.h>
1162306a36Sopenharmony_ci#include <linux/vexpress.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <asm/mcpm.h>
1462306a36Sopenharmony_ci#include <asm/smp_scu.h>
1562306a36Sopenharmony_ci#include <asm/mach/map.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "platsmp.h"
1862306a36Sopenharmony_ci#include "vexpress.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cibool __init vexpress_smp_init_ops(void)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci#ifdef CONFIG_MCPM
2362306a36Sopenharmony_ci	int cpu;
2462306a36Sopenharmony_ci	struct device_node *cpu_node, *cci_node;
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci	/*
2762306a36Sopenharmony_ci	 * The best way to detect a multi-cluster configuration
2862306a36Sopenharmony_ci	 * is to detect if the kernel can take over CCI ports
2962306a36Sopenharmony_ci	 * control. Loop over possible CPUs and check if CCI
3062306a36Sopenharmony_ci	 * port control is available.
3162306a36Sopenharmony_ci	 * Override the default vexpress_smp_ops if so.
3262306a36Sopenharmony_ci	 */
3362306a36Sopenharmony_ci	for_each_possible_cpu(cpu) {
3462306a36Sopenharmony_ci		bool available;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci		cpu_node = of_get_cpu_node(cpu, NULL);
3762306a36Sopenharmony_ci		if (WARN(!cpu_node, "Missing cpu device node!"))
3862306a36Sopenharmony_ci			return false;
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci		cci_node = of_parse_phandle(cpu_node, "cci-control-port", 0);
4162306a36Sopenharmony_ci		available = cci_node && of_device_is_available(cci_node);
4262306a36Sopenharmony_ci		of_node_put(cci_node);
4362306a36Sopenharmony_ci		of_node_put(cpu_node);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci		if (!available)
4662306a36Sopenharmony_ci			return false;
4762306a36Sopenharmony_ci	}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	mcpm_smp_set_ops();
5062306a36Sopenharmony_ci	return true;
5162306a36Sopenharmony_ci#else
5262306a36Sopenharmony_ci	return false;
5362306a36Sopenharmony_ci#endif
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic const struct of_device_id vexpress_smp_dt_scu_match[] __initconst = {
5762306a36Sopenharmony_ci	{ .compatible = "arm,cortex-a5-scu", },
5862306a36Sopenharmony_ci	{ .compatible = "arm,cortex-a9-scu", },
5962306a36Sopenharmony_ci	{}
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic void __init vexpress_smp_dt_prepare_cpus(unsigned int max_cpus)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci	struct device_node *scu = of_find_matching_node(NULL,
6562306a36Sopenharmony_ci			vexpress_smp_dt_scu_match);
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	if (scu)
6862306a36Sopenharmony_ci		scu_enable(of_iomap(scu, 0));
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	/*
7162306a36Sopenharmony_ci	 * Write the address of secondary startup into the
7262306a36Sopenharmony_ci	 * system-wide flags register. The boot monitor waits
7362306a36Sopenharmony_ci	 * until it receives a soft interrupt, and then the
7462306a36Sopenharmony_ci	 * secondary CPU branches to this address.
7562306a36Sopenharmony_ci	 */
7662306a36Sopenharmony_ci	vexpress_flags_set(__pa_symbol(versatile_secondary_startup));
7762306a36Sopenharmony_ci}
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
8062306a36Sopenharmony_cistatic void vexpress_cpu_die(unsigned int cpu)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	versatile_immitation_cpu_die(cpu, 0x40);
8362306a36Sopenharmony_ci}
8462306a36Sopenharmony_ci#endif
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ciconst struct smp_operations vexpress_smp_dt_ops __initconst = {
8762306a36Sopenharmony_ci	.smp_prepare_cpus	= vexpress_smp_dt_prepare_cpus,
8862306a36Sopenharmony_ci	.smp_secondary_init	= versatile_secondary_init,
8962306a36Sopenharmony_ci	.smp_boot_secondary	= versatile_boot_secondary,
9062306a36Sopenharmony_ci#ifdef CONFIG_HOTPLUG_CPU
9162306a36Sopenharmony_ci	.cpu_die		= vexpress_cpu_die,
9262306a36Sopenharmony_ci#endif
9362306a36Sopenharmony_ci};
94