Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/arm/mach-bcm/bcm63xx_smp.c
26292 views
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
170