1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Broadcom BCM63138 DSL SoCs SMP support code 4 * 5 * Copyright (C) 2015, Broadcom Corporation 6 */ 7 8 #include <linux/delay.h> 9 #include <linux/init.h> 10 #include <linux/smp.h> 11 #include <linux/io.h> 12 #include <linux/of.h> 13 #include <linux/of_address.h> 14 15 #include <asm/cacheflush.h> 16 #include <asm/smp_scu.h> 17 #include <asm/smp_plat.h> 18 #include <asm/vfp.h> 19 20 #include "bcm63xx_smp.h" 21 22 /* Size of mapped Cortex A9 SCU address space */ 23 #define CORTEX_A9_SCU_SIZE 0x58 24 25 /* 26 * Enable the Cortex A9 Snoop Control Unit 27 * 28 * By the time this is called we already know there are multiple 29 * cores present. We assume we're running on a Cortex A9 processor, 30 * so any trouble getting the base address register or getting the 31 * SCU base is a problem. 32 * 33 * Return 0 if successful or an error code otherwise. 34 */ 35 static int __init scu_a9_enable(void) 36 { 37 unsigned long config_base; 38 void __iomem *scu_base; 39 unsigned int i, ncores; 40 41 if (!scu_a9_has_base()) { 42 pr_err("no configuration base address register!\n"); 43 return -ENXIO; 44 } 45 46 /* Config base address register value is zero for uniprocessor */ 47 config_base = scu_a9_get_base(); 48 if (!config_base) { 49 pr_err("hardware reports only one core\n"); 50 return -ENOENT; 51 } 52 53 scu_base = ioremap((phys_addr_t)config_base, CORTEX_A9_SCU_SIZE); 54 if (!scu_base) { 55 pr_err("failed to remap config base (%lu/%u) for SCU\n", 56 config_base, CORTEX_A9_SCU_SIZE); 57 return -ENOMEM; 58 } 59 60 scu_enable(scu_base); 61 62 ncores = scu_base ? scu_get_core_count(scu_base) : 1; 63 64 if (ncores > nr_cpu_ids) { 65 pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", 66 ncores, nr_cpu_ids); 67 ncores = nr_cpu_ids; 68 } 69 70 /* The BCM63138 SoC has two Cortex-A9 CPUs, CPU0 features a complete 71 * and fully functional VFP unit that can be used, but CPU1 does not. 72 * Since we will not be able to trap kernel-mode NEON to force 73 * migration to CPU0, just do not advertise VFP support at all. 74 * 75 * This will make vfp_init bail out and do not attempt to use VFP at 76 * all, for kernel-mode NEON, we do not want to introduce any 77 * conditionals in hot-paths, so we just restrict the system to UP. 78 */ 79 #ifdef CONFIG_VFP 80 if (ncores > 1) { 81 pr_warn("SMP: secondary CPUs lack VFP unit, disabling VFP\n"); 82 vfp_disable(); 83 84 #ifdef CONFIG_KERNEL_MODE_NEON 85 WARN(1, "SMP: kernel-mode NEON enabled, restricting to UP\n"); 86 ncores = 1; 87 #endif 88 } 89 #endif 90 91 for (i = 0; i < ncores; i++) 92 set_cpu_possible(i, true); 93 94 iounmap(scu_base); /* That's the last we'll need of this */ 95 96 return 0; 97 } 98 99 static const struct of_device_id bcm63138_bootlut_ids[] = { 100 { .compatible = "brcm,bcm63138-bootlut", }, 101 { /* sentinel */ }, 102 }; 103 104 #define BOOTLUT_RESET_VECT 0x20 105 106 static int bcm63138_smp_boot_secondary(unsigned int cpu, 107 struct task_struct *idle) 108 { 109 void __iomem *bootlut_base; 110 struct device_node *dn; 111 int ret = 0; 112 u32 val; 113 114 dn = of_find_matching_node(NULL, bcm63138_bootlut_ids); 115 if (!dn) { 116 pr_err("SMP: unable to find bcm63138 boot LUT node\n"); 117 return -ENODEV; 118 } 119 120 bootlut_base = of_iomap(dn, 0); 121 of_node_put(dn); 122 123 if (!bootlut_base) { 124 pr_err("SMP: unable to remap boot LUT base register\n"); 125 return -ENOMEM; 126 } 127 128 /* Locate the secondary CPU node */ 129 dn = of_get_cpu_node(cpu, NULL); 130 if (!dn) { 131 pr_err("SMP: failed to locate secondary CPU%d node\n", cpu); 132 ret = -ENODEV; 133 goto out; 134 } 135 136 /* Write the secondary init routine to the BootLUT reset vector */ 137 val = __pa_symbol(secondary_startup); 138 writel_relaxed(val, bootlut_base + BOOTLUT_RESET_VECT); 139 140 /* Power up the core, will jump straight to its reset vector when we 141 * return 142 */ 143 ret = bcm63xx_pmb_power_on_cpu(dn); 144 of_node_put(dn); 145 146 out: 147 iounmap(bootlut_base); 148 149 return ret; 150 } 151 152 static void __init bcm63138_smp_prepare_cpus(unsigned int max_cpus) 153 { 154 int ret; 155 156 ret = scu_a9_enable(); 157 if (ret) { 158 pr_warn("SMP: Cortex-A9 SCU setup failed\n"); 159 return; 160 } 161 } 162 163 static const struct smp_operations bcm63138_smp_ops __initconst = { 164 .smp_prepare_cpus = bcm63138_smp_prepare_cpus, 165 .smp_boot_secondary = bcm63138_smp_boot_secondary, 166 }; 167 168 CPU_METHOD_OF_DECLARE(bcm63138_smp, "brcm,bcm63138", &bcm63138_smp_ops); 169
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.