18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Broadcom BCM63138 DSL SoCs SMP support code 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2015, Broadcom Corporation 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/delay.h> 98c2ecf20Sopenharmony_ci#include <linux/init.h> 108c2ecf20Sopenharmony_ci#include <linux/smp.h> 118c2ecf20Sopenharmony_ci#include <linux/io.h> 128c2ecf20Sopenharmony_ci#include <linux/of.h> 138c2ecf20Sopenharmony_ci#include <linux/of_address.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include <asm/cacheflush.h> 168c2ecf20Sopenharmony_ci#include <asm/smp_scu.h> 178c2ecf20Sopenharmony_ci#include <asm/smp_plat.h> 188c2ecf20Sopenharmony_ci#include <asm/vfp.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#include "bcm63xx_smp.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/* Size of mapped Cortex A9 SCU address space */ 238c2ecf20Sopenharmony_ci#define CORTEX_A9_SCU_SIZE 0x58 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* 268c2ecf20Sopenharmony_ci * Enable the Cortex A9 Snoop Control Unit 278c2ecf20Sopenharmony_ci * 288c2ecf20Sopenharmony_ci * By the time this is called we already know there are multiple 298c2ecf20Sopenharmony_ci * cores present. We assume we're running on a Cortex A9 processor, 308c2ecf20Sopenharmony_ci * so any trouble getting the base address register or getting the 318c2ecf20Sopenharmony_ci * SCU base is a problem. 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * Return 0 if successful or an error code otherwise. 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_cistatic int __init scu_a9_enable(void) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci unsigned long config_base; 388c2ecf20Sopenharmony_ci void __iomem *scu_base; 398c2ecf20Sopenharmony_ci unsigned int i, ncores; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci if (!scu_a9_has_base()) { 428c2ecf20Sopenharmony_ci pr_err("no configuration base address register!\n"); 438c2ecf20Sopenharmony_ci return -ENXIO; 448c2ecf20Sopenharmony_ci } 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci /* Config base address register value is zero for uniprocessor */ 478c2ecf20Sopenharmony_ci config_base = scu_a9_get_base(); 488c2ecf20Sopenharmony_ci if (!config_base) { 498c2ecf20Sopenharmony_ci pr_err("hardware reports only one core\n"); 508c2ecf20Sopenharmony_ci return -ENOENT; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci scu_base = ioremap((phys_addr_t)config_base, CORTEX_A9_SCU_SIZE); 548c2ecf20Sopenharmony_ci if (!scu_base) { 558c2ecf20Sopenharmony_ci pr_err("failed to remap config base (%lu/%u) for SCU\n", 568c2ecf20Sopenharmony_ci config_base, CORTEX_A9_SCU_SIZE); 578c2ecf20Sopenharmony_ci return -ENOMEM; 588c2ecf20Sopenharmony_ci } 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci scu_enable(scu_base); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci ncores = scu_base ? scu_get_core_count(scu_base) : 1; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (ncores > nr_cpu_ids) { 658c2ecf20Sopenharmony_ci pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", 668c2ecf20Sopenharmony_ci ncores, nr_cpu_ids); 678c2ecf20Sopenharmony_ci ncores = nr_cpu_ids; 688c2ecf20Sopenharmony_ci } 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* The BCM63138 SoC has two Cortex-A9 CPUs, CPU0 features a complete 718c2ecf20Sopenharmony_ci * and fully functional VFP unit that can be used, but CPU1 does not. 728c2ecf20Sopenharmony_ci * Since we will not be able to trap kernel-mode NEON to force 738c2ecf20Sopenharmony_ci * migration to CPU0, just do not advertise VFP support at all. 748c2ecf20Sopenharmony_ci * 758c2ecf20Sopenharmony_ci * This will make vfp_init bail out and do not attempt to use VFP at 768c2ecf20Sopenharmony_ci * all, for kernel-mode NEON, we do not want to introduce any 778c2ecf20Sopenharmony_ci * conditionals in hot-paths, so we just restrict the system to UP. 788c2ecf20Sopenharmony_ci */ 798c2ecf20Sopenharmony_ci#ifdef CONFIG_VFP 808c2ecf20Sopenharmony_ci if (ncores > 1) { 818c2ecf20Sopenharmony_ci pr_warn("SMP: secondary CPUs lack VFP unit, disabling VFP\n"); 828c2ecf20Sopenharmony_ci vfp_disable(); 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci#ifdef CONFIG_KERNEL_MODE_NEON 858c2ecf20Sopenharmony_ci WARN(1, "SMP: kernel-mode NEON enabled, restricting to UP\n"); 868c2ecf20Sopenharmony_ci ncores = 1; 878c2ecf20Sopenharmony_ci#endif 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci#endif 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci for (i = 0; i < ncores; i++) 928c2ecf20Sopenharmony_ci set_cpu_possible(i, true); 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci iounmap(scu_base); /* That's the last we'll need of this */ 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return 0; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic const struct of_device_id bcm63138_bootlut_ids[] = { 1008c2ecf20Sopenharmony_ci { .compatible = "brcm,bcm63138-bootlut", }, 1018c2ecf20Sopenharmony_ci { /* sentinel */ }, 1028c2ecf20Sopenharmony_ci}; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci#define BOOTLUT_RESET_VECT 0x20 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_cistatic int bcm63138_smp_boot_secondary(unsigned int cpu, 1078c2ecf20Sopenharmony_ci struct task_struct *idle) 1088c2ecf20Sopenharmony_ci{ 1098c2ecf20Sopenharmony_ci void __iomem *bootlut_base; 1108c2ecf20Sopenharmony_ci struct device_node *dn; 1118c2ecf20Sopenharmony_ci int ret = 0; 1128c2ecf20Sopenharmony_ci u32 val; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci dn = of_find_matching_node(NULL, bcm63138_bootlut_ids); 1158c2ecf20Sopenharmony_ci if (!dn) { 1168c2ecf20Sopenharmony_ci pr_err("SMP: unable to find bcm63138 boot LUT node\n"); 1178c2ecf20Sopenharmony_ci return -ENODEV; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci bootlut_base = of_iomap(dn, 0); 1218c2ecf20Sopenharmony_ci of_node_put(dn); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci if (!bootlut_base) { 1248c2ecf20Sopenharmony_ci pr_err("SMP: unable to remap boot LUT base register\n"); 1258c2ecf20Sopenharmony_ci return -ENOMEM; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* Locate the secondary CPU node */ 1298c2ecf20Sopenharmony_ci dn = of_get_cpu_node(cpu, NULL); 1308c2ecf20Sopenharmony_ci if (!dn) { 1318c2ecf20Sopenharmony_ci pr_err("SMP: failed to locate secondary CPU%d node\n", cpu); 1328c2ecf20Sopenharmony_ci ret = -ENODEV; 1338c2ecf20Sopenharmony_ci goto out; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* Write the secondary init routine to the BootLUT reset vector */ 1378c2ecf20Sopenharmony_ci val = __pa_symbol(secondary_startup); 1388c2ecf20Sopenharmony_ci writel_relaxed(val, bootlut_base + BOOTLUT_RESET_VECT); 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* Power up the core, will jump straight to its reset vector when we 1418c2ecf20Sopenharmony_ci * return 1428c2ecf20Sopenharmony_ci */ 1438c2ecf20Sopenharmony_ci ret = bcm63xx_pmb_power_on_cpu(dn); 1448c2ecf20Sopenharmony_ci of_node_put(dn); 1458c2ecf20Sopenharmony_ci if (ret) 1468c2ecf20Sopenharmony_ci goto out; 1478c2ecf20Sopenharmony_ciout: 1488c2ecf20Sopenharmony_ci iounmap(bootlut_base); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return ret; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic void __init bcm63138_smp_prepare_cpus(unsigned int max_cpus) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci int ret; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci ret = scu_a9_enable(); 1588c2ecf20Sopenharmony_ci if (ret) { 1598c2ecf20Sopenharmony_ci pr_warn("SMP: Cortex-A9 SCU setup failed\n"); 1608c2ecf20Sopenharmony_ci return; 1618c2ecf20Sopenharmony_ci } 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic const struct smp_operations bcm63138_smp_ops __initconst = { 1658c2ecf20Sopenharmony_ci .smp_prepare_cpus = bcm63138_smp_prepare_cpus, 1668c2ecf20Sopenharmony_ci .smp_boot_secondary = bcm63138_smp_boot_secondary, 1678c2ecf20Sopenharmony_ci}; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ciCPU_METHOD_OF_DECLARE(bcm63138_smp, "brcm,bcm63138", &bcm63138_smp_ops); 170