diff options
author | Florian Fainelli <f.fainelli@gmail.com> | 2014-08-14 22:37:45 -0400 |
---|---|---|
committer | Florian Fainelli <f.fainelli@gmail.com> | 2015-05-20 18:08:44 -0400 |
commit | ed5cd8163da8d3e02ef83b84e42d555d40bab96a (patch) | |
tree | c316c3a4108dc4fabb4b600d5c7a09b6e6d08b84 | |
parent | 7d7d7a413c5b8dddfde56dce1dd42e2199033c6c (diff) |
ARM: BCM63xx: Add SMP support for BCM63138
Add support for booting the secondary CPU on BCM63138, this involves:
- locating the bootlut to write the reset vector
- powering up the second CPU when we need to using the DT-supplied PMB
references
- disabling VFP when enabled such that we can keep having SMP
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
-rw-r--r-- | arch/arm/mach-bcm/Makefile | 7 | ||||
-rw-r--r-- | arch/arm/mach-bcm/bcm63xx_headsmp.S | 23 | ||||
-rw-r--r-- | arch/arm/mach-bcm/bcm63xx_smp.c | 169 | ||||
-rw-r--r-- | arch/arm/mach-bcm/bcm63xx_smp.h | 9 |
4 files changed, 207 insertions, 1 deletions
diff --git a/arch/arm/mach-bcm/Makefile b/arch/arm/mach-bcm/Makefile index 4c38674c73ec..55d3a1213876 100644 --- a/arch/arm/mach-bcm/Makefile +++ b/arch/arm/mach-bcm/Makefile | |||
@@ -38,7 +38,12 @@ obj-$(CONFIG_ARCH_BCM2835) += board_bcm2835.o | |||
38 | obj-$(CONFIG_ARCH_BCM_5301X) += bcm_5301x.o | 38 | obj-$(CONFIG_ARCH_BCM_5301X) += bcm_5301x.o |
39 | 39 | ||
40 | # BCM63XXx | 40 | # BCM63XXx |
41 | obj-$(CONFIG_ARCH_BCM_63XX) := bcm63xx.o | 41 | ifeq ($(CONFIG_ARCH_BCM_63XX),y) |
42 | CFLAGS_bcm63xx_headsmp.o += -march=armv7-a | ||
43 | obj-y += bcm63xx.o | ||
44 | obj-$(CONFIG_SMP) += bcm63xx_smp.o bcm63xx_headsmp.o \ | ||
45 | bcm63xx_pmb.o | ||
46 | endif | ||
42 | 47 | ||
43 | ifeq ($(CONFIG_ARCH_BRCMSTB),y) | 48 | ifeq ($(CONFIG_ARCH_BRCMSTB),y) |
44 | CFLAGS_platsmp-brcmstb.o += -march=armv7-a | 49 | CFLAGS_platsmp-brcmstb.o += -march=armv7-a |
diff --git a/arch/arm/mach-bcm/bcm63xx_headsmp.S b/arch/arm/mach-bcm/bcm63xx_headsmp.S new file mode 100644 index 000000000000..c7af397c7f14 --- /dev/null +++ b/arch/arm/mach-bcm/bcm63xx_headsmp.S | |||
@@ -0,0 +1,23 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2015, Broadcom Corporation | ||
3 | * All Rights Reserved | ||
4 | * | ||
5 | * This program is free software; you can redistribute it and/or modify | ||
6 | * it under the terms of the GNU General Public License version 2 as | ||
7 | * published by the Free Software Foundation. | ||
8 | */ | ||
9 | #include <linux/linkage.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <asm/assembler.h> | ||
12 | |||
13 | ENTRY(bcm63138_secondary_startup) | ||
14 | ARM_BE8(setend be) | ||
15 | /* | ||
16 | * L1 cache does have unpredictable contents at power-up clean its | ||
17 | * contents without flushing | ||
18 | */ | ||
19 | bl v7_invalidate_l1 | ||
20 | nop | ||
21 | |||
22 | b secondary_startup | ||
23 | ENDPROC(bcm63138_secondary_startup) | ||
diff --git a/arch/arm/mach-bcm/bcm63xx_smp.c b/arch/arm/mach-bcm/bcm63xx_smp.c new file mode 100644 index 000000000000..3f014f18cea5 --- /dev/null +++ b/arch/arm/mach-bcm/bcm63xx_smp.c | |||
@@ -0,0 +1,169 @@ | |||
1 | /* | ||
2 | * Broadcom BCM63138 DSL SoCs SMP support code | ||
3 | * | ||
4 | * Copyright (C) 2015, Broadcom Corporation | ||
5 | * | ||
6 | * Licensed under the terms of the GPLv2 | ||
7 | */ | ||
8 | |||
9 | #include <linux/delay.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/smp.h> | ||
12 | #include <linux/io.h> | ||
13 | #include <linux/of.h> | ||
14 | #include <linux/of_address.h> | ||
15 | |||
16 | #include <asm/cacheflush.h> | ||
17 | #include <asm/smp_scu.h> | ||
18 | #include <asm/smp_plat.h> | ||
19 | #include <asm/vfp.h> | ||
20 | |||
21 | #include "bcm63xx_smp.h" | ||
22 | |||
23 | /* Size of mapped Cortex A9 SCU address space */ | ||
24 | #define CORTEX_A9_SCU_SIZE 0x58 | ||
25 | |||
26 | /* | ||
27 | * Enable the Cortex A9 Snoop Control Unit | ||
28 | * | ||
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. | ||
33 | * | ||
34 | * Return 0 if successful or an error code otherwise. | ||
35 | */ | ||
36 | static int __init scu_a9_enable(void) | ||
37 | { | ||
38 | unsigned long config_base; | ||
39 | void __iomem *scu_base; | ||
40 | unsigned int i, ncores; | ||
41 | |||
42 | if (!scu_a9_has_base()) { | ||
43 | pr_err("no configuration base address register!\n"); | ||
44 | return -ENXIO; | ||
45 | } | ||
46 | |||
47 | /* Config base address register value is zero for uniprocessor */ | ||
48 | config_base = scu_a9_get_base(); | ||
49 | if (!config_base) { | ||
50 | pr_err("hardware reports only one core\n"); | ||
51 | return -ENOENT; | ||
52 | } | ||
53 | |||
54 | scu_base = ioremap((phys_addr_t)config_base, CORTEX_A9_SCU_SIZE); | ||
55 | if (!scu_base) { | ||
56 | pr_err("failed to remap config base (%lu/%u) for SCU\n", | ||
57 | config_base, CORTEX_A9_SCU_SIZE); | ||
58 | return -ENOMEM; | ||
59 | } | ||
60 | |||
61 | scu_enable(scu_base); | ||
62 | |||
63 | ncores = scu_base ? scu_get_core_count(scu_base) : 1; | ||
64 | |||
65 | if (ncores > nr_cpu_ids) { | ||
66 | pr_warn("SMP: %u cores greater than maximum (%u), clipping\n", | ||
67 | ncores, nr_cpu_ids); | ||
68 | ncores = nr_cpu_ids; | ||
69 | } | ||
70 | |||
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. | ||
75 | * | ||
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. | ||
79 | */ | ||
80 | #ifdef CONFIG_VFP | ||
81 | if (ncores > 1) { | ||
82 | pr_warn("SMP: secondary CPUs lack VFP unit, disabling VFP\n"); | ||
83 | vfp_disable(); | ||
84 | |||
85 | #ifdef CONFIG_KERNEL_MODE_NEON | ||
86 | WARN(1, "SMP: kernel-mode NEON enabled, restricting to UP\n"); | ||
87 | ncores = 1; | ||
88 | #endif | ||
89 | } | ||
90 | #endif | ||
91 | |||
92 | for (i = 0; i < ncores; i++) | ||
93 | set_cpu_possible(i, true); | ||
94 | |||
95 | iounmap(scu_base); /* That's the last we'll need of this */ | ||
96 | |||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | static const struct of_device_id bcm63138_bootlut_ids[] = { | ||
101 | { .compatible = "brcm,bcm63138-bootlut", }, | ||
102 | { /* sentinel */ }, | ||
103 | }; | ||
104 | |||
105 | #define BOOTLUT_RESET_VECT 0x20 | ||
106 | |||
107 | static int bcm63138_smp_boot_secondary(unsigned int cpu, | ||
108 | struct task_struct *idle) | ||
109 | { | ||
110 | void __iomem *bootlut_base; | ||
111 | struct device_node *dn; | ||
112 | int ret = 0; | ||
113 | u32 val; | ||
114 | |||
115 | dn = of_find_matching_node(NULL, bcm63138_bootlut_ids); | ||
116 | if (!dn) { | ||
117 | pr_err("SMP: unable to find bcm63138 boot LUT node\n"); | ||
118 | return -ENODEV; | ||
119 | } | ||
120 | |||
121 | bootlut_base = of_iomap(dn, 0); | ||
122 | of_node_put(dn); | ||
123 | |||
124 | if (!bootlut_base) { | ||
125 | pr_err("SMP: unable to remap boot LUT base register\n"); | ||
126 | return -ENOMEM; | ||
127 | } | ||
128 | |||
129 | /* Locate the secondary CPU node */ | ||
130 | dn = of_get_cpu_node(cpu_logical_map(cpu), NULL); | ||
131 | if (!dn) { | ||
132 | pr_err("SMP: failed to locate secondary CPU%d node\n", cpu); | ||
133 | ret = -ENODEV; | ||
134 | goto out; | ||
135 | } | ||
136 | |||
137 | /* Write the secondary init routine to the BootLUT reset vector */ | ||
138 | val = virt_to_phys(bcm63138_secondary_startup); | ||
139 | writel_relaxed(val, bootlut_base + BOOTLUT_RESET_VECT); | ||
140 | |||
141 | /* Power up the core, will jump straight to its reset vector when we | ||
142 | * return | ||
143 | */ | ||
144 | ret = bcm63xx_pmb_power_on_cpu(dn); | ||
145 | if (ret) | ||
146 | goto out; | ||
147 | out: | ||
148 | iounmap(bootlut_base); | ||
149 | |||
150 | return ret; | ||
151 | } | ||
152 | |||
153 | static void __init bcm63138_smp_prepare_cpus(unsigned int max_cpus) | ||
154 | { | ||
155 | int ret; | ||
156 | |||
157 | ret = scu_a9_enable(); | ||
158 | if (ret) { | ||
159 | pr_warn("SMP: Cortex-A9 SCU setup failed\n"); | ||
160 | return; | ||
161 | } | ||
162 | } | ||
163 | |||
164 | struct smp_operations bcm63138_smp_ops __initdata = { | ||
165 | .smp_prepare_cpus = bcm63138_smp_prepare_cpus, | ||
166 | .smp_boot_secondary = bcm63138_smp_boot_secondary, | ||
167 | }; | ||
168 | |||
169 | CPU_METHOD_OF_DECLARE(bcm63138_smp, "brcm,bcm63138", &bcm63138_smp_ops); | ||
diff --git a/arch/arm/mach-bcm/bcm63xx_smp.h b/arch/arm/mach-bcm/bcm63xx_smp.h new file mode 100644 index 000000000000..50b76044536e --- /dev/null +++ b/arch/arm/mach-bcm/bcm63xx_smp.h | |||
@@ -0,0 +1,9 @@ | |||
1 | #ifndef __BCM63XX_SMP_H | ||
2 | #define __BCM63XX_SMP_H | ||
3 | |||
4 | struct device_node; | ||
5 | |||
6 | extern void bcm63138_secondary_startup(void); | ||
7 | extern int bcm63xx_pmb_power_on_cpu(struct device_node *dn); | ||
8 | |||
9 | #endif /* __BCM63XX_SMP_H */ | ||