133eea064SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2ed5cd816SFlorian Fainelli /*
3ed5cd816SFlorian Fainelli * Broadcom BCM63138 DSL SoCs SMP support code
4ed5cd816SFlorian Fainelli *
5ed5cd816SFlorian Fainelli * Copyright (C) 2015, Broadcom Corporation
6ed5cd816SFlorian Fainelli */
7ed5cd816SFlorian Fainelli
8ed5cd816SFlorian Fainelli #include <linux/delay.h>
9ed5cd816SFlorian Fainelli #include <linux/init.h>
10ed5cd816SFlorian Fainelli #include <linux/smp.h>
11ed5cd816SFlorian Fainelli #include <linux/io.h>
12ed5cd816SFlorian Fainelli #include <linux/of.h>
13ed5cd816SFlorian Fainelli #include <linux/of_address.h>
14ed5cd816SFlorian Fainelli
15ed5cd816SFlorian Fainelli #include <asm/cacheflush.h>
16ed5cd816SFlorian Fainelli #include <asm/smp_scu.h>
17ed5cd816SFlorian Fainelli #include <asm/smp_plat.h>
18ed5cd816SFlorian Fainelli #include <asm/vfp.h>
19ed5cd816SFlorian Fainelli
20ed5cd816SFlorian Fainelli #include "bcm63xx_smp.h"
21ed5cd816SFlorian Fainelli
22ed5cd816SFlorian Fainelli /* Size of mapped Cortex A9 SCU address space */
23ed5cd816SFlorian Fainelli #define CORTEX_A9_SCU_SIZE 0x58
24ed5cd816SFlorian Fainelli
25ed5cd816SFlorian Fainelli /*
26ed5cd816SFlorian Fainelli * Enable the Cortex A9 Snoop Control Unit
27ed5cd816SFlorian Fainelli *
28ed5cd816SFlorian Fainelli * By the time this is called we already know there are multiple
29ed5cd816SFlorian Fainelli * cores present. We assume we're running on a Cortex A9 processor,
30ed5cd816SFlorian Fainelli * so any trouble getting the base address register or getting the
31ed5cd816SFlorian Fainelli * SCU base is a problem.
32ed5cd816SFlorian Fainelli *
33ed5cd816SFlorian Fainelli * Return 0 if successful or an error code otherwise.
34ed5cd816SFlorian Fainelli */
scu_a9_enable(void)35ed5cd816SFlorian Fainelli static int __init scu_a9_enable(void)
36ed5cd816SFlorian Fainelli {
37ed5cd816SFlorian Fainelli unsigned long config_base;
38ed5cd816SFlorian Fainelli void __iomem *scu_base;
39ed5cd816SFlorian Fainelli unsigned int i, ncores;
40ed5cd816SFlorian Fainelli
41ed5cd816SFlorian Fainelli if (!scu_a9_has_base()) {
42ed5cd816SFlorian Fainelli pr_err("no configuration base address register!\n");
43ed5cd816SFlorian Fainelli return -ENXIO;
44ed5cd816SFlorian Fainelli }
45ed5cd816SFlorian Fainelli
46ed5cd816SFlorian Fainelli /* Config base address register value is zero for uniprocessor */
47ed5cd816SFlorian Fainelli config_base = scu_a9_get_base();
48ed5cd816SFlorian Fainelli if (!config_base) {
49ed5cd816SFlorian Fainelli pr_err("hardware reports only one core\n");
50ed5cd816SFlorian Fainelli return -ENOENT;
51ed5cd816SFlorian Fainelli }
52ed5cd816SFlorian Fainelli
53ed5cd816SFlorian Fainelli scu_base = ioremap((phys_addr_t)config_base, CORTEX_A9_SCU_SIZE);
54ed5cd816SFlorian Fainelli if (!scu_base) {
55ed5cd816SFlorian Fainelli pr_err("failed to remap config base (%lu/%u) for SCU\n",
56ed5cd816SFlorian Fainelli config_base, CORTEX_A9_SCU_SIZE);
57ed5cd816SFlorian Fainelli return -ENOMEM;
58ed5cd816SFlorian Fainelli }
59ed5cd816SFlorian Fainelli
60ed5cd816SFlorian Fainelli scu_enable(scu_base);
61ed5cd816SFlorian Fainelli
62ed5cd816SFlorian Fainelli ncores = scu_base ? scu_get_core_count(scu_base) : 1;
63ed5cd816SFlorian Fainelli
64ed5cd816SFlorian Fainelli if (ncores > nr_cpu_ids) {
65ed5cd816SFlorian Fainelli pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
66ed5cd816SFlorian Fainelli ncores, nr_cpu_ids);
67ed5cd816SFlorian Fainelli ncores = nr_cpu_ids;
68ed5cd816SFlorian Fainelli }
69ed5cd816SFlorian Fainelli
70ed5cd816SFlorian Fainelli /* The BCM63138 SoC has two Cortex-A9 CPUs, CPU0 features a complete
71ed5cd816SFlorian Fainelli * and fully functional VFP unit that can be used, but CPU1 does not.
72ed5cd816SFlorian Fainelli * Since we will not be able to trap kernel-mode NEON to force
73ed5cd816SFlorian Fainelli * migration to CPU0, just do not advertise VFP support at all.
74ed5cd816SFlorian Fainelli *
75ed5cd816SFlorian Fainelli * This will make vfp_init bail out and do not attempt to use VFP at
76ed5cd816SFlorian Fainelli * all, for kernel-mode NEON, we do not want to introduce any
77ed5cd816SFlorian Fainelli * conditionals in hot-paths, so we just restrict the system to UP.
78ed5cd816SFlorian Fainelli */
79ed5cd816SFlorian Fainelli #ifdef CONFIG_VFP
80ed5cd816SFlorian Fainelli if (ncores > 1) {
81ed5cd816SFlorian Fainelli pr_warn("SMP: secondary CPUs lack VFP unit, disabling VFP\n");
82ed5cd816SFlorian Fainelli vfp_disable();
83ed5cd816SFlorian Fainelli
84ed5cd816SFlorian Fainelli #ifdef CONFIG_KERNEL_MODE_NEON
85ed5cd816SFlorian Fainelli WARN(1, "SMP: kernel-mode NEON enabled, restricting to UP\n");
86ed5cd816SFlorian Fainelli ncores = 1;
87ed5cd816SFlorian Fainelli #endif
88ed5cd816SFlorian Fainelli }
89ed5cd816SFlorian Fainelli #endif
90ed5cd816SFlorian Fainelli
91ed5cd816SFlorian Fainelli for (i = 0; i < ncores; i++)
92ed5cd816SFlorian Fainelli set_cpu_possible(i, true);
93ed5cd816SFlorian Fainelli
94ed5cd816SFlorian Fainelli iounmap(scu_base); /* That's the last we'll need of this */
95ed5cd816SFlorian Fainelli
96ed5cd816SFlorian Fainelli return 0;
97ed5cd816SFlorian Fainelli }
98ed5cd816SFlorian Fainelli
99ed5cd816SFlorian Fainelli static const struct of_device_id bcm63138_bootlut_ids[] = {
100ed5cd816SFlorian Fainelli { .compatible = "brcm,bcm63138-bootlut", },
101ed5cd816SFlorian Fainelli { /* sentinel */ },
102ed5cd816SFlorian Fainelli };
103ed5cd816SFlorian Fainelli
104ed5cd816SFlorian Fainelli #define BOOTLUT_RESET_VECT 0x20
105ed5cd816SFlorian Fainelli
bcm63138_smp_boot_secondary(unsigned int cpu,struct task_struct * idle)106ed5cd816SFlorian Fainelli static int bcm63138_smp_boot_secondary(unsigned int cpu,
107ed5cd816SFlorian Fainelli struct task_struct *idle)
108ed5cd816SFlorian Fainelli {
109ed5cd816SFlorian Fainelli void __iomem *bootlut_base;
110ed5cd816SFlorian Fainelli struct device_node *dn;
111ed5cd816SFlorian Fainelli int ret = 0;
112ed5cd816SFlorian Fainelli u32 val;
113ed5cd816SFlorian Fainelli
114ed5cd816SFlorian Fainelli dn = of_find_matching_node(NULL, bcm63138_bootlut_ids);
115ed5cd816SFlorian Fainelli if (!dn) {
116ed5cd816SFlorian Fainelli pr_err("SMP: unable to find bcm63138 boot LUT node\n");
117ed5cd816SFlorian Fainelli return -ENODEV;
118ed5cd816SFlorian Fainelli }
119ed5cd816SFlorian Fainelli
120ed5cd816SFlorian Fainelli bootlut_base = of_iomap(dn, 0);
121ed5cd816SFlorian Fainelli of_node_put(dn);
122ed5cd816SFlorian Fainelli
123ed5cd816SFlorian Fainelli if (!bootlut_base) {
124ed5cd816SFlorian Fainelli pr_err("SMP: unable to remap boot LUT base register\n");
125ed5cd816SFlorian Fainelli return -ENOMEM;
126ed5cd816SFlorian Fainelli }
127ed5cd816SFlorian Fainelli
128ed5cd816SFlorian Fainelli /* Locate the secondary CPU node */
129a6b4b25bSSudeep Holla dn = of_get_cpu_node(cpu, NULL);
130ed5cd816SFlorian Fainelli if (!dn) {
131ed5cd816SFlorian Fainelli pr_err("SMP: failed to locate secondary CPU%d node\n", cpu);
132ed5cd816SFlorian Fainelli ret = -ENODEV;
133ed5cd816SFlorian Fainelli goto out;
134ed5cd816SFlorian Fainelli }
135ed5cd816SFlorian Fainelli
136ed5cd816SFlorian Fainelli /* Write the secondary init routine to the BootLUT reset vector */
13764fc2a94SFlorian Fainelli val = __pa_symbol(secondary_startup);
138ed5cd816SFlorian Fainelli writel_relaxed(val, bootlut_base + BOOTLUT_RESET_VECT);
139ed5cd816SFlorian Fainelli
140ed5cd816SFlorian Fainelli /* Power up the core, will jump straight to its reset vector when we
141ed5cd816SFlorian Fainelli * return
142ed5cd816SFlorian Fainelli */
143ed5cd816SFlorian Fainelli ret = bcm63xx_pmb_power_on_cpu(dn);
1448abc5a7cSWen Yang of_node_put(dn);
145*a4667439SDario Binacchi
146ed5cd816SFlorian Fainelli out:
147ed5cd816SFlorian Fainelli iounmap(bootlut_base);
148ed5cd816SFlorian Fainelli
149ed5cd816SFlorian Fainelli return ret;
150ed5cd816SFlorian Fainelli }
151ed5cd816SFlorian Fainelli
bcm63138_smp_prepare_cpus(unsigned int max_cpus)152ed5cd816SFlorian Fainelli static void __init bcm63138_smp_prepare_cpus(unsigned int max_cpus)
153ed5cd816SFlorian Fainelli {
154ed5cd816SFlorian Fainelli int ret;
155ed5cd816SFlorian Fainelli
156ed5cd816SFlorian Fainelli ret = scu_a9_enable();
157ed5cd816SFlorian Fainelli if (ret) {
158ed5cd816SFlorian Fainelli pr_warn("SMP: Cortex-A9 SCU setup failed\n");
159ed5cd816SFlorian Fainelli return;
160ed5cd816SFlorian Fainelli }
161ed5cd816SFlorian Fainelli }
162ed5cd816SFlorian Fainelli
16375305275SMasahiro Yamada static const struct smp_operations bcm63138_smp_ops __initconst = {
164ed5cd816SFlorian Fainelli .smp_prepare_cpus = bcm63138_smp_prepare_cpus,
165ed5cd816SFlorian Fainelli .smp_boot_secondary = bcm63138_smp_boot_secondary,
166ed5cd816SFlorian Fainelli };
167ed5cd816SFlorian Fainelli
168ed5cd816SFlorian Fainelli CPU_METHOD_OF_DECLARE(bcm63138_smp, "brcm,bcm63138", &bcm63138_smp_ops);
169