19952f691SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
29c4566a1SDinh Nguyen /*
39c4566a1SDinh Nguyen * Copyright 2010-2011 Calxeda, Inc.
49c4566a1SDinh Nguyen * Copyright 2012 Pavel Machek <pavel@denx.de>
59c4566a1SDinh Nguyen * Based on platsmp.c, Copyright (C) 2002 ARM Ltd.
69c4566a1SDinh Nguyen * Copyright (C) 2012 Altera Corporation
79c4566a1SDinh Nguyen */
89c4566a1SDinh Nguyen #include <linux/delay.h>
99c4566a1SDinh Nguyen #include <linux/init.h>
109c4566a1SDinh Nguyen #include <linux/smp.h>
119c4566a1SDinh Nguyen #include <linux/io.h>
129c4566a1SDinh Nguyen #include <linux/of.h>
139c4566a1SDinh Nguyen #include <linux/of_address.h>
149c4566a1SDinh Nguyen
159c4566a1SDinh Nguyen #include <asm/cacheflush.h>
169c4566a1SDinh Nguyen #include <asm/smp_scu.h>
179c4566a1SDinh Nguyen #include <asm/smp_plat.h>
189c4566a1SDinh Nguyen
199c4566a1SDinh Nguyen #include "core.h"
209c4566a1SDinh Nguyen
socfpga_boot_secondary(unsigned int cpu,struct task_struct * idle)218bd26e3aSPaul Gortmaker static int socfpga_boot_secondary(unsigned int cpu, struct task_struct *idle)
229c4566a1SDinh Nguyen {
23*187bea47STakashi Iwai int trampoline_size = secondary_trampoline_end - secondary_trampoline;
249c4566a1SDinh Nguyen
253a4356c0SDinh Nguyen if (socfpga_cpu1start_addr) {
26d686ce42SAlan Tull /* This will put CPU #1 into reset. */
27d686ce42SAlan Tull writel(RSTMGR_MPUMODRST_CPU1,
28d686ce42SAlan Tull rst_manager_base_addr + SOCFPGA_RSTMGR_MODMPURST);
29d686ce42SAlan Tull
30*187bea47STakashi Iwai memcpy(phys_to_virt(0), secondary_trampoline, trampoline_size);
319c4566a1SDinh Nguyen
3264fc2a94SFlorian Fainelli writel(__pa_symbol(secondary_startup),
33d686ce42SAlan Tull sys_manager_base_addr + (socfpga_cpu1start_addr & 0x000000ff));
349c4566a1SDinh Nguyen
359c4566a1SDinh Nguyen flush_cache_all();
369c4566a1SDinh Nguyen smp_wmb();
379c4566a1SDinh Nguyen outer_clean_range(0, trampoline_size);
389c4566a1SDinh Nguyen
399c4566a1SDinh Nguyen /* This will release CPU #1 out of reset. */
40d686ce42SAlan Tull writel(0, rst_manager_base_addr + SOCFPGA_RSTMGR_MODMPURST);
41d6dd735fSDinh Nguyen }
429c4566a1SDinh Nguyen
439c4566a1SDinh Nguyen return 0;
449c4566a1SDinh Nguyen }
459c4566a1SDinh Nguyen
socfpga_a10_boot_secondary(unsigned int cpu,struct task_struct * idle)4645be0cdbSDinh Nguyen static int socfpga_a10_boot_secondary(unsigned int cpu, struct task_struct *idle)
479c4566a1SDinh Nguyen {
48*187bea47STakashi Iwai int trampoline_size = secondary_trampoline_end - secondary_trampoline;
499c4566a1SDinh Nguyen
5045be0cdbSDinh Nguyen if (socfpga_cpu1start_addr) {
5145be0cdbSDinh Nguyen writel(RSTMGR_MPUMODRST_CPU1, rst_manager_base_addr +
5245be0cdbSDinh Nguyen SOCFPGA_A10_RSTMGR_MODMPURST);
53*187bea47STakashi Iwai memcpy(phys_to_virt(0), secondary_trampoline, trampoline_size);
549c4566a1SDinh Nguyen
5564fc2a94SFlorian Fainelli writel(__pa_symbol(secondary_startup),
5645be0cdbSDinh Nguyen sys_manager_base_addr + (socfpga_cpu1start_addr & 0x00000fff));
579c4566a1SDinh Nguyen
5845be0cdbSDinh Nguyen flush_cache_all();
5945be0cdbSDinh Nguyen smp_wmb();
6045be0cdbSDinh Nguyen outer_clean_range(0, trampoline_size);
6145be0cdbSDinh Nguyen
6245be0cdbSDinh Nguyen /* This will release CPU #1 out of reset. */
6345be0cdbSDinh Nguyen writel(0, rst_manager_base_addr + SOCFPGA_A10_RSTMGR_MODMPURST);
649c4566a1SDinh Nguyen }
659c4566a1SDinh Nguyen
6645be0cdbSDinh Nguyen return 0;
679c4566a1SDinh Nguyen }
689c4566a1SDinh Nguyen
socfpga_smp_prepare_cpus(unsigned int max_cpus)699c4566a1SDinh Nguyen static void __init socfpga_smp_prepare_cpus(unsigned int max_cpus)
709c4566a1SDinh Nguyen {
71122694a0SDinh Nguyen struct device_node *np;
72122694a0SDinh Nguyen void __iomem *socfpga_scu_base_addr;
73122694a0SDinh Nguyen
74122694a0SDinh Nguyen np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
75122694a0SDinh Nguyen if (!np) {
76122694a0SDinh Nguyen pr_err("%s: missing scu\n", __func__);
77122694a0SDinh Nguyen return;
78122694a0SDinh Nguyen }
79122694a0SDinh Nguyen
80122694a0SDinh Nguyen socfpga_scu_base_addr = of_iomap(np, 0);
81122694a0SDinh Nguyen if (!socfpga_scu_base_addr)
82122694a0SDinh Nguyen return;
839c4566a1SDinh Nguyen scu_enable(socfpga_scu_base_addr);
849c4566a1SDinh Nguyen }
859c4566a1SDinh Nguyen
865d37e80bSArnd Bergmann #ifdef CONFIG_HOTPLUG_CPU
879c4566a1SDinh Nguyen /*
889c4566a1SDinh Nguyen * platform-specific code to shutdown a CPU
899c4566a1SDinh Nguyen *
909c4566a1SDinh Nguyen * Called with IRQs disabled
919c4566a1SDinh Nguyen */
socfpga_cpu_die(unsigned int cpu)929c4566a1SDinh Nguyen static void socfpga_cpu_die(unsigned int cpu)
939c4566a1SDinh Nguyen {
94d686ce42SAlan Tull /* Do WFI. If we wake up early, go back into WFI */
95d686ce42SAlan Tull while (1)
969c4566a1SDinh Nguyen cpu_do_idle();
979c4566a1SDinh Nguyen }
989c4566a1SDinh Nguyen
99b33612e1SHiraku Toyooka /*
100b33612e1SHiraku Toyooka * We need a dummy function so that platform_can_cpu_hotplug() knows
101b33612e1SHiraku Toyooka * we support CPU hotplug. However, the function does not need to do
102b33612e1SHiraku Toyooka * anything, because CPUs going offline just do WFI. We could reset
103b33612e1SHiraku Toyooka * the CPUs but it would increase power consumption.
104b33612e1SHiraku Toyooka */
socfpga_cpu_kill(unsigned int cpu)105b33612e1SHiraku Toyooka static int socfpga_cpu_kill(unsigned int cpu)
106b33612e1SHiraku Toyooka {
107b33612e1SHiraku Toyooka return 1;
108b33612e1SHiraku Toyooka }
1095d37e80bSArnd Bergmann #endif
110b33612e1SHiraku Toyooka
11175305275SMasahiro Yamada static const struct smp_operations socfpga_smp_ops __initconst = {
1129c4566a1SDinh Nguyen .smp_prepare_cpus = socfpga_smp_prepare_cpus,
1139c4566a1SDinh Nguyen .smp_boot_secondary = socfpga_boot_secondary,
1149c4566a1SDinh Nguyen #ifdef CONFIG_HOTPLUG_CPU
1159c4566a1SDinh Nguyen .cpu_die = socfpga_cpu_die,
116b33612e1SHiraku Toyooka .cpu_kill = socfpga_cpu_kill,
1179c4566a1SDinh Nguyen #endif
1189c4566a1SDinh Nguyen };
1195f763ef8SDinh Nguyen
12075305275SMasahiro Yamada static const struct smp_operations socfpga_a10_smp_ops __initconst = {
12145be0cdbSDinh Nguyen .smp_prepare_cpus = socfpga_smp_prepare_cpus,
12245be0cdbSDinh Nguyen .smp_boot_secondary = socfpga_a10_boot_secondary,
12345be0cdbSDinh Nguyen #ifdef CONFIG_HOTPLUG_CPU
12445be0cdbSDinh Nguyen .cpu_die = socfpga_cpu_die,
125b33612e1SHiraku Toyooka .cpu_kill = socfpga_cpu_kill,
12645be0cdbSDinh Nguyen #endif
12745be0cdbSDinh Nguyen };
12845be0cdbSDinh Nguyen
1295f763ef8SDinh Nguyen CPU_METHOD_OF_DECLARE(socfpga_smp, "altr,socfpga-smp", &socfpga_smp_ops);
13045be0cdbSDinh Nguyen CPU_METHOD_OF_DECLARE(socfpga_a10_smp, "altr,socfpga-a10-smp", &socfpga_a10_smp_ops);
131