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