1 // SPDX-License-Identifier: GPL-2.0-only
3 * Broadcom BCM63138 DSL SoCs SMP support code
5 * Copyright (C) 2015, Broadcom Corporation
8 #include <linux/delay.h>
9 #include <linux/init.h>
10 #include <linux/smp.h>
13 #include <linux/of_address.h>
15 #include <asm/cacheflush.h>
16 #include <asm/smp_scu.h>
17 #include <asm/smp_plat.h>
20 #include "bcm63xx_smp.h"
22 /* Size of mapped Cortex A9 SCU address space */
23 #define CORTEX_A9_SCU_SIZE 0x58
26 * Enable the Cortex A9 Snoop Control Unit
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.
33 * Return 0 if successful or an error code otherwise.
35 static int __init
scu_a9_enable(void)
37 unsigned long config_base
;
38 void __iomem
*scu_base
;
39 unsigned int i
, ncores
;
41 if (!scu_a9_has_base()) {
42 pr_err("no configuration base address register!\n");
46 /* Config base address register value is zero for uniprocessor */
47 config_base
= scu_a9_get_base();
49 pr_err("hardware reports only one core\n");
53 scu_base
= ioremap((phys_addr_t
)config_base
, CORTEX_A9_SCU_SIZE
);
55 pr_err("failed to remap config base (%lu/%u) for SCU\n",
56 config_base
, CORTEX_A9_SCU_SIZE
);
62 ncores
= scu_base
? scu_get_core_count(scu_base
) : 1;
64 if (ncores
> nr_cpu_ids
) {
65 pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
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.
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.
81 pr_warn("SMP: secondary CPUs lack VFP unit, disabling VFP\n");
84 #ifdef CONFIG_KERNEL_MODE_NEON
85 WARN(1, "SMP: kernel-mode NEON enabled, restricting to UP\n");
91 for (i
= 0; i
< ncores
; i
++)
92 set_cpu_possible(i
, true);
94 iounmap(scu_base
); /* That's the last we'll need of this */
99 static const struct of_device_id bcm63138_bootlut_ids
[] = {
100 { .compatible
= "brcm,bcm63138-bootlut", },
104 #define BOOTLUT_RESET_VECT 0x20
106 static int bcm63138_smp_boot_secondary(unsigned int cpu
,
107 struct task_struct
*idle
)
109 void __iomem
*bootlut_base
;
110 struct device_node
*dn
;
114 dn
= of_find_matching_node(NULL
, bcm63138_bootlut_ids
);
116 pr_err("SMP: unable to find bcm63138 boot LUT node\n");
120 bootlut_base
= of_iomap(dn
, 0);
124 pr_err("SMP: unable to remap boot LUT base register\n");
128 /* Locate the secondary CPU node */
129 dn
= of_get_cpu_node(cpu
, NULL
);
131 pr_err("SMP: failed to locate secondary CPU%d node\n", cpu
);
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
);
140 /* Power up the core, will jump straight to its reset vector when we
143 ret
= bcm63xx_pmb_power_on_cpu(dn
);
148 iounmap(bootlut_base
);
153 static void __init
bcm63138_smp_prepare_cpus(unsigned int max_cpus
)
157 ret
= scu_a9_enable();
159 pr_warn("SMP: Cortex-A9 SCU setup failed\n");
164 static const struct smp_operations bcm63138_smp_ops __initconst
= {
165 .smp_prepare_cpus
= bcm63138_smp_prepare_cpus
,
166 .smp_boot_secondary
= bcm63138_smp_boot_secondary
,
169 CPU_METHOD_OF_DECLARE(bcm63138_smp
, "brcm,bcm63138", &bcm63138_smp_ops
);