1eb14767cSJisheng Zhang // SPDX-License-Identifier: GPL-2.0
27b7dfdd2SAntoine Ténart /*
37b7dfdd2SAntoine Ténart * Copyright (C) 2014 Marvell Technology Group Ltd.
47b7dfdd2SAntoine Ténart *
57b7dfdd2SAntoine Ténart * Antoine Ténart <antoine.tenart@free-electrons.com>
67b7dfdd2SAntoine Ténart */
77b7dfdd2SAntoine Ténart
87b7dfdd2SAntoine Ténart #include <linux/io.h>
97b7dfdd2SAntoine Ténart #include <linux/delay.h>
107b7dfdd2SAntoine Ténart #include <linux/of.h>
117b7dfdd2SAntoine Ténart #include <linux/of_address.h>
127b7dfdd2SAntoine Ténart
137b7dfdd2SAntoine Ténart #include <asm/cacheflush.h>
14a7b3d5a7SJisheng Zhang #include <asm/cp15.h>
15*a9ff6961SLinus Walleij #include <asm/page.h>
167b7dfdd2SAntoine Ténart #include <asm/smp_plat.h>
177b7dfdd2SAntoine Ténart #include <asm/smp_scu.h>
187b7dfdd2SAntoine Ténart
19ac7fc233SJisheng Zhang /*
20ac7fc233SJisheng Zhang * There are two reset registers, one with self-clearing (SC)
21ac7fc233SJisheng Zhang * reset and one with non-self-clearing reset (NON_SC).
22ac7fc233SJisheng Zhang */
23ac7fc233SJisheng Zhang #define CPU_RESET_SC 0x00
24ac7fc233SJisheng Zhang #define CPU_RESET_NON_SC 0x20
257b7dfdd2SAntoine Ténart
267b7dfdd2SAntoine Ténart #define RESET_VECT 0x00
277b7dfdd2SAntoine Ténart #define SW_RESET_ADDR 0x94
287b7dfdd2SAntoine Ténart
297b7dfdd2SAntoine Ténart extern u32 boot_inst;
307b7dfdd2SAntoine Ténart
317b7dfdd2SAntoine Ténart static void __iomem *cpu_ctrl;
327b7dfdd2SAntoine Ténart
berlin_perform_reset_cpu(unsigned int cpu)337b7dfdd2SAntoine Ténart static inline void berlin_perform_reset_cpu(unsigned int cpu)
347b7dfdd2SAntoine Ténart {
357b7dfdd2SAntoine Ténart u32 val;
367b7dfdd2SAntoine Ténart
37ac7fc233SJisheng Zhang val = readl(cpu_ctrl + CPU_RESET_NON_SC);
38ac7fc233SJisheng Zhang val &= ~BIT(cpu_logical_map(cpu));
39ac7fc233SJisheng Zhang writel(val, cpu_ctrl + CPU_RESET_NON_SC);
407b7dfdd2SAntoine Ténart val |= BIT(cpu_logical_map(cpu));
41ac7fc233SJisheng Zhang writel(val, cpu_ctrl + CPU_RESET_NON_SC);
427b7dfdd2SAntoine Ténart }
437b7dfdd2SAntoine Ténart
berlin_boot_secondary(unsigned int cpu,struct task_struct * idle)447b7dfdd2SAntoine Ténart static int berlin_boot_secondary(unsigned int cpu, struct task_struct *idle)
457b7dfdd2SAntoine Ténart {
467b7dfdd2SAntoine Ténart if (!cpu_ctrl)
477b7dfdd2SAntoine Ténart return -EFAULT;
487b7dfdd2SAntoine Ténart
497b7dfdd2SAntoine Ténart /*
507b7dfdd2SAntoine Ténart * Reset the CPU, making it to execute the instruction in the reset
517b7dfdd2SAntoine Ténart * exception vector.
527b7dfdd2SAntoine Ténart */
537b7dfdd2SAntoine Ténart berlin_perform_reset_cpu(cpu);
547b7dfdd2SAntoine Ténart
557b7dfdd2SAntoine Ténart return 0;
567b7dfdd2SAntoine Ténart }
577b7dfdd2SAntoine Ténart
berlin_smp_prepare_cpus(unsigned int max_cpus)587b7dfdd2SAntoine Ténart static void __init berlin_smp_prepare_cpus(unsigned int max_cpus)
597b7dfdd2SAntoine Ténart {
607b7dfdd2SAntoine Ténart struct device_node *np;
617b7dfdd2SAntoine Ténart void __iomem *scu_base;
627b7dfdd2SAntoine Ténart void __iomem *vectors_base;
637b7dfdd2SAntoine Ténart
647b7dfdd2SAntoine Ténart np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
657b7dfdd2SAntoine Ténart scu_base = of_iomap(np, 0);
667b7dfdd2SAntoine Ténart of_node_put(np);
677b7dfdd2SAntoine Ténart if (!scu_base)
687b7dfdd2SAntoine Ténart return;
697b7dfdd2SAntoine Ténart
707b7dfdd2SAntoine Ténart np = of_find_compatible_node(NULL, NULL, "marvell,berlin-cpu-ctrl");
717b7dfdd2SAntoine Ténart cpu_ctrl = of_iomap(np, 0);
727b7dfdd2SAntoine Ténart of_node_put(np);
737b7dfdd2SAntoine Ténart if (!cpu_ctrl)
747b7dfdd2SAntoine Ténart goto unmap_scu;
757b7dfdd2SAntoine Ténart
76d2ca5f24SAfzal Mohammed vectors_base = ioremap(VECTORS_BASE, SZ_32K);
777b7dfdd2SAntoine Ténart if (!vectors_base)
787b7dfdd2SAntoine Ténart goto unmap_scu;
797b7dfdd2SAntoine Ténart
807b7dfdd2SAntoine Ténart scu_enable(scu_base);
817b7dfdd2SAntoine Ténart
827b7dfdd2SAntoine Ténart /*
837b7dfdd2SAntoine Ténart * Write the first instruction the CPU will execute after being reset
847b7dfdd2SAntoine Ténart * in the reset exception vector.
857b7dfdd2SAntoine Ténart */
867b7dfdd2SAntoine Ténart writel(boot_inst, vectors_base + RESET_VECT);
877b7dfdd2SAntoine Ténart
887b7dfdd2SAntoine Ténart /*
897b7dfdd2SAntoine Ténart * Write the secondary startup address into the SW reset address
907b7dfdd2SAntoine Ténart * vector. This is used by boot_inst.
917b7dfdd2SAntoine Ténart */
9264fc2a94SFlorian Fainelli writel(__pa_symbol(secondary_startup), vectors_base + SW_RESET_ADDR);
937b7dfdd2SAntoine Ténart
947b7dfdd2SAntoine Ténart iounmap(vectors_base);
957b7dfdd2SAntoine Ténart unmap_scu:
967b7dfdd2SAntoine Ténart iounmap(scu_base);
977b7dfdd2SAntoine Ténart }
987b7dfdd2SAntoine Ténart
99a7b3d5a7SJisheng Zhang #ifdef CONFIG_HOTPLUG_CPU
berlin_cpu_die(unsigned int cpu)100a7b3d5a7SJisheng Zhang static void berlin_cpu_die(unsigned int cpu)
101a7b3d5a7SJisheng Zhang {
102a7b3d5a7SJisheng Zhang v7_exit_coherency_flush(louis);
103a7b3d5a7SJisheng Zhang while (1)
104a7b3d5a7SJisheng Zhang cpu_do_idle();
105a7b3d5a7SJisheng Zhang }
106a7b3d5a7SJisheng Zhang
berlin_cpu_kill(unsigned int cpu)107a7b3d5a7SJisheng Zhang static int berlin_cpu_kill(unsigned int cpu)
108a7b3d5a7SJisheng Zhang {
109a7b3d5a7SJisheng Zhang u32 val;
110a7b3d5a7SJisheng Zhang
111a7b3d5a7SJisheng Zhang val = readl(cpu_ctrl + CPU_RESET_NON_SC);
112a7b3d5a7SJisheng Zhang val &= ~BIT(cpu_logical_map(cpu));
113a7b3d5a7SJisheng Zhang writel(val, cpu_ctrl + CPU_RESET_NON_SC);
114a7b3d5a7SJisheng Zhang
115a7b3d5a7SJisheng Zhang return 1;
116a7b3d5a7SJisheng Zhang }
117a7b3d5a7SJisheng Zhang #endif
118a7b3d5a7SJisheng Zhang
11975305275SMasahiro Yamada static const struct smp_operations berlin_smp_ops __initconst = {
1207b7dfdd2SAntoine Ténart .smp_prepare_cpus = berlin_smp_prepare_cpus,
1217b7dfdd2SAntoine Ténart .smp_boot_secondary = berlin_boot_secondary,
122a7b3d5a7SJisheng Zhang #ifdef CONFIG_HOTPLUG_CPU
123a7b3d5a7SJisheng Zhang .cpu_die = berlin_cpu_die,
124a7b3d5a7SJisheng Zhang .cpu_kill = berlin_cpu_kill,
125a7b3d5a7SJisheng Zhang #endif
1267b7dfdd2SAntoine Ténart };
1277b7dfdd2SAntoine Ténart CPU_METHOD_OF_DECLARE(berlin_smp, "marvell,berlin-smp", &berlin_smp_ops);
128