Merge remote-tracking branches 'regulator/fix/88pm800', 'regulator/fix/max8973',...
[linux-drm-fsl-dcu.git] / arch / arm / mach-bcm / bcm63xx_smp.c
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);