Merge tag 'soc-5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
[linux-2.6-microblaze.git] / arch / arm / mach-berlin / platsmp.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2014 Marvell Technology Group Ltd.
4  *
5  * Antoine Ténart <antoine.tenart@free-electrons.com>
6  */
7
8 #include <linux/io.h>
9 #include <linux/delay.h>
10 #include <linux/of.h>
11 #include <linux/of_address.h>
12
13 #include <asm/cacheflush.h>
14 #include <asm/cp15.h>
15 #include <asm/memory.h>
16 #include <asm/smp_plat.h>
17 #include <asm/smp_scu.h>
18
19 /*
20  * There are two reset registers, one with self-clearing (SC)
21  * reset and one with non-self-clearing reset (NON_SC).
22  */
23 #define CPU_RESET_SC            0x00
24 #define CPU_RESET_NON_SC        0x20
25
26 #define RESET_VECT              0x00
27 #define SW_RESET_ADDR           0x94
28
29 extern u32 boot_inst;
30
31 static void __iomem *cpu_ctrl;
32
33 static inline void berlin_perform_reset_cpu(unsigned int cpu)
34 {
35         u32 val;
36
37         val = readl(cpu_ctrl + CPU_RESET_NON_SC);
38         val &= ~BIT(cpu_logical_map(cpu));
39         writel(val, cpu_ctrl + CPU_RESET_NON_SC);
40         val |= BIT(cpu_logical_map(cpu));
41         writel(val, cpu_ctrl + CPU_RESET_NON_SC);
42 }
43
44 static int berlin_boot_secondary(unsigned int cpu, struct task_struct *idle)
45 {
46         if (!cpu_ctrl)
47                 return -EFAULT;
48
49         /*
50          * Reset the CPU, making it to execute the instruction in the reset
51          * exception vector.
52          */
53         berlin_perform_reset_cpu(cpu);
54
55         return 0;
56 }
57
58 static void __init berlin_smp_prepare_cpus(unsigned int max_cpus)
59 {
60         struct device_node *np;
61         void __iomem *scu_base;
62         void __iomem *vectors_base;
63
64         np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
65         scu_base = of_iomap(np, 0);
66         of_node_put(np);
67         if (!scu_base)
68                 return;
69
70         np = of_find_compatible_node(NULL, NULL, "marvell,berlin-cpu-ctrl");
71         cpu_ctrl = of_iomap(np, 0);
72         of_node_put(np);
73         if (!cpu_ctrl)
74                 goto unmap_scu;
75
76         vectors_base = ioremap(VECTORS_BASE, SZ_32K);
77         if (!vectors_base)
78                 goto unmap_scu;
79
80         scu_enable(scu_base);
81
82         /*
83          * Write the first instruction the CPU will execute after being reset
84          * in the reset exception vector.
85          */
86         writel(boot_inst, vectors_base + RESET_VECT);
87
88         /*
89          * Write the secondary startup address into the SW reset address
90          * vector. This is used by boot_inst.
91          */
92         writel(__pa_symbol(secondary_startup), vectors_base + SW_RESET_ADDR);
93
94         iounmap(vectors_base);
95 unmap_scu:
96         iounmap(scu_base);
97 }
98
99 #ifdef CONFIG_HOTPLUG_CPU
100 static void berlin_cpu_die(unsigned int cpu)
101 {
102         v7_exit_coherency_flush(louis);
103         while (1)
104                 cpu_do_idle();
105 }
106
107 static int berlin_cpu_kill(unsigned int cpu)
108 {
109         u32 val;
110
111         val = readl(cpu_ctrl + CPU_RESET_NON_SC);
112         val &= ~BIT(cpu_logical_map(cpu));
113         writel(val, cpu_ctrl + CPU_RESET_NON_SC);
114
115         return 1;
116 }
117 #endif
118
119 static const struct smp_operations berlin_smp_ops __initconst = {
120         .smp_prepare_cpus       = berlin_smp_prepare_cpus,
121         .smp_boot_secondary     = berlin_boot_secondary,
122 #ifdef CONFIG_HOTPLUG_CPU
123         .cpu_die                = berlin_cpu_die,
124         .cpu_kill               = berlin_cpu_kill,
125 #endif
126 };
127 CPU_METHOD_OF_DECLARE(berlin_smp, "marvell,berlin-smp", &berlin_smp_ops);