xref: /openbmc/linux/arch/arm/mach-bcm/bcm63xx_smp.c (revision c4ba69f00c18524eb89990e9afda9d2a9b54de2f)
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