2 * Broadcom BCM63138 DSL SoCs SMP support code
4 * Copyright (C) 2015, Broadcom Corporation
6 * Licensed under the terms of the GPLv2
9 #include <linux/delay.h>
10 #include <linux/init.h>
11 #include <linux/smp.h>
14 #include <linux/of_address.h>
16 #include <asm/cacheflush.h>
17 #include <asm/smp_scu.h>
18 #include <asm/smp_plat.h>
21 #include "bcm63xx_smp.h"
23 /* Size of mapped Cortex A9 SCU address space */
24 #define CORTEX_A9_SCU_SIZE 0x58
27 * Enable the Cortex A9 Snoop Control Unit
29 * By the time this is called we already know there are multiple
30 * cores present. We assume we're running on a Cortex A9 processor,
31 * so any trouble getting the base address register or getting the
32 * SCU base is a problem.
34 * Return 0 if successful or an error code otherwise.
36 static int __init scu_a9_enable(void)
38 unsigned long config_base;
39 void __iomem *scu_base;
40 unsigned int i, ncores;
42 if (!scu_a9_has_base()) {
43 pr_err("no configuration base address register!\n");
47 /* Config base address register value is zero for uniprocessor */
48 config_base = scu_a9_get_base();
50 pr_err("hardware reports only one core\n");
54 scu_base = ioremap((phys_addr_t)config_base, CORTEX_A9_SCU_SIZE);
56 pr_err("failed to remap config base (%lu/%u) for SCU\n",
57 config_base, CORTEX_A9_SCU_SIZE);
63 ncores = scu_base ? scu_get_core_count(scu_base) : 1;
65 if (ncores > nr_cpu_ids) {
66 pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
71 /* The BCM63138 SoC has two Cortex-A9 CPUs, CPU0 features a complete
72 * and fully functional VFP unit that can be used, but CPU1 does not.
73 * Since we will not be able to trap kernel-mode NEON to force
74 * migration to CPU0, just do not advertise VFP support at all.
76 * This will make vfp_init bail out and do not attempt to use VFP at
77 * all, for kernel-mode NEON, we do not want to introduce any
78 * conditionals in hot-paths, so we just restrict the system to UP.
82 pr_warn("SMP: secondary CPUs lack VFP unit, disabling VFP\n");
85 #ifdef CONFIG_KERNEL_MODE_NEON
86 WARN(1, "SMP: kernel-mode NEON enabled, restricting to UP\n");
92 for (i = 0; i < ncores; i++)
93 set_cpu_possible(i, true);
95 iounmap(scu_base); /* That's the last we'll need of this */
100 static const struct of_device_id bcm63138_bootlut_ids[] = {
101 { .compatible = "brcm,bcm63138-bootlut", },
105 #define BOOTLUT_RESET_VECT 0x20
107 static int bcm63138_smp_boot_secondary(unsigned int cpu,
108 struct task_struct *idle)
110 void __iomem *bootlut_base;
111 struct device_node *dn;
115 dn = of_find_matching_node(NULL, bcm63138_bootlut_ids);
117 pr_err("SMP: unable to find bcm63138 boot LUT node\n");
121 bootlut_base = of_iomap(dn, 0);
125 pr_err("SMP: unable to remap boot LUT base register\n");
129 /* Locate the secondary CPU node */
130 dn = of_get_cpu_node(cpu, NULL);
132 pr_err("SMP: failed to locate secondary CPU%d node\n", cpu);
137 /* Write the secondary init routine to the BootLUT reset vector */
138 val = virt_to_phys(secondary_startup);
139 writel_relaxed(val, bootlut_base + BOOTLUT_RESET_VECT);
141 /* Power up the core, will jump straight to its reset vector when we
144 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 struct smp_operations bcm63138_smp_ops __initdata = {
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);