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