Merge tag 'mips_5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/mips/linux
[linux-2.6-microblaze.git] / arch / arm / mach-bcm / bcm63xx_smp.c
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         if (ret)
145                 goto out;
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);