19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2aa7eb2bbSMichal Simek /*
3aa7eb2bbSMichal Simek * This file contains Xilinx specific SMP code, used to start up
4aa7eb2bbSMichal Simek * the second processor.
5aa7eb2bbSMichal Simek *
6aa7eb2bbSMichal Simek * Copyright (C) 2011-2013 Xilinx
7aa7eb2bbSMichal Simek *
8aa7eb2bbSMichal Simek * based on linux/arch/arm/mach-realview/platsmp.c
9aa7eb2bbSMichal Simek *
10aa7eb2bbSMichal Simek * Copyright (C) 2002 ARM Ltd.
11aa7eb2bbSMichal Simek */
12aa7eb2bbSMichal Simek
13aa7eb2bbSMichal Simek #include <linux/export.h>
14aa7eb2bbSMichal Simek #include <linux/jiffies.h>
15aa7eb2bbSMichal Simek #include <linux/init.h>
16aa7eb2bbSMichal Simek #include <linux/io.h>
17aa7eb2bbSMichal Simek #include <asm/cacheflush.h>
18*6c6b3f1fSQuanyang Wang #include <asm/smp_plat.h>
19aa7eb2bbSMichal Simek #include <asm/smp_scu.h>
20aa7eb2bbSMichal Simek #include <linux/irqchip/arm-gic.h>
21aa7eb2bbSMichal Simek #include "common.h"
22aa7eb2bbSMichal Simek
23aa7eb2bbSMichal Simek /*
24aa7eb2bbSMichal Simek * Store number of cores in the system
25aa7eb2bbSMichal Simek * Because of scu_get_core_count() must be in __init section and can't
268bd26e3aSPaul Gortmaker * be called from zynq_cpun_start() because it is not in __init section.
27aa7eb2bbSMichal Simek */
28aa7eb2bbSMichal Simek static int ncores;
29aa7eb2bbSMichal Simek
zynq_cpun_start(u32 address,int cpu)308bd26e3aSPaul Gortmaker int zynq_cpun_start(u32 address, int cpu)
31aa7eb2bbSMichal Simek {
32aa7eb2bbSMichal Simek u32 trampoline_code_size = &zynq_secondary_trampoline_end -
33aa7eb2bbSMichal Simek &zynq_secondary_trampoline;
34*6c6b3f1fSQuanyang Wang u32 phy_cpuid = cpu_logical_map(cpu);
35aa7eb2bbSMichal Simek
36aa7eb2bbSMichal Simek /* MS: Expectation that SLCR are directly map and accessible */
37aa7eb2bbSMichal Simek /* Not possible to jump to non aligned address */
38aa7eb2bbSMichal Simek if (!(address & 3) && (!address || (address >= trampoline_code_size))) {
39aa7eb2bbSMichal Simek /* Store pointer to ioremap area which points to address 0x0 */
40aa7eb2bbSMichal Simek static u8 __iomem *zero;
41aa7eb2bbSMichal Simek u32 trampoline_size = &zynq_secondary_trampoline_jump -
42aa7eb2bbSMichal Simek &zynq_secondary_trampoline;
43aa7eb2bbSMichal Simek
44*6c6b3f1fSQuanyang Wang zynq_slcr_cpu_stop(phy_cpuid);
4588cd4e88SMichal Simek if (address) {
46aa7eb2bbSMichal Simek if (__pa(PAGE_OFFSET)) {
47aa7eb2bbSMichal Simek zero = ioremap(0, trampoline_code_size);
48aa7eb2bbSMichal Simek if (!zero) {
49aa7eb2bbSMichal Simek pr_warn("BOOTUP jump vectors not accessible\n");
50aa7eb2bbSMichal Simek return -1;
51aa7eb2bbSMichal Simek }
52aa7eb2bbSMichal Simek } else {
53aa7eb2bbSMichal Simek zero = (__force u8 __iomem *)PAGE_OFFSET;
54aa7eb2bbSMichal Simek }
55aa7eb2bbSMichal Simek
56aa7eb2bbSMichal Simek /*
57aa7eb2bbSMichal Simek * This is elegant way how to jump to any address
58aa7eb2bbSMichal Simek * 0x0: Load address at 0x8 to r0
59aa7eb2bbSMichal Simek * 0x4: Jump by mov instruction
60aa7eb2bbSMichal Simek * 0x8: Jumping address
61aa7eb2bbSMichal Simek */
62b7005d4eSLuis Araneda memcpy_toio(zero, &zynq_secondary_trampoline,
63aa7eb2bbSMichal Simek trampoline_size);
64aa7eb2bbSMichal Simek writel(address, zero + trampoline_size);
65aa7eb2bbSMichal Simek
66aa7eb2bbSMichal Simek flush_cache_all();
67aa7eb2bbSMichal Simek outer_flush_range(0, trampoline_code_size);
68aa7eb2bbSMichal Simek smp_wmb();
69aa7eb2bbSMichal Simek
70aa7eb2bbSMichal Simek if (__pa(PAGE_OFFSET))
71aa7eb2bbSMichal Simek iounmap(zero);
7288cd4e88SMichal Simek }
73*6c6b3f1fSQuanyang Wang zynq_slcr_cpu_start(phy_cpuid);
74aa7eb2bbSMichal Simek
75aa7eb2bbSMichal Simek return 0;
76aa7eb2bbSMichal Simek }
77aa7eb2bbSMichal Simek
78aa7eb2bbSMichal Simek pr_warn("Can't start CPU%d: Wrong starting address %x\n", cpu, address);
79aa7eb2bbSMichal Simek
80aa7eb2bbSMichal Simek return -1;
81aa7eb2bbSMichal Simek }
82aa7eb2bbSMichal Simek EXPORT_SYMBOL(zynq_cpun_start);
83aa7eb2bbSMichal Simek
zynq_boot_secondary(unsigned int cpu,struct task_struct * idle)8402b4e275SRussell King static int zynq_boot_secondary(unsigned int cpu, struct task_struct *idle)
85aa7eb2bbSMichal Simek {
865f595063SLuis Araneda return zynq_cpun_start(__pa_symbol(secondary_startup_arm), cpu);
87aa7eb2bbSMichal Simek }
88aa7eb2bbSMichal Simek
89aa7eb2bbSMichal Simek /*
90aa7eb2bbSMichal Simek * Initialise the CPU possible map early - this describes the CPUs
91aa7eb2bbSMichal Simek * which may be present or become present in the system.
92aa7eb2bbSMichal Simek */
zynq_smp_init_cpus(void)93aa7eb2bbSMichal Simek static void __init zynq_smp_init_cpus(void)
94aa7eb2bbSMichal Simek {
95aa7eb2bbSMichal Simek int i;
96aa7eb2bbSMichal Simek
97aa7eb2bbSMichal Simek ncores = scu_get_core_count(zynq_scu_base);
98aa7eb2bbSMichal Simek
99aa7eb2bbSMichal Simek for (i = 0; i < ncores && i < CONFIG_NR_CPUS; i++)
100aa7eb2bbSMichal Simek set_cpu_possible(i, true);
101aa7eb2bbSMichal Simek }
102aa7eb2bbSMichal Simek
zynq_smp_prepare_cpus(unsigned int max_cpus)103aa7eb2bbSMichal Simek static void __init zynq_smp_prepare_cpus(unsigned int max_cpus)
104aa7eb2bbSMichal Simek {
105aa7eb2bbSMichal Simek scu_enable(zynq_scu_base);
106aa7eb2bbSMichal Simek }
107aa7eb2bbSMichal Simek
108ae88b85eSSoren Brinkmann /**
109ae88b85eSSoren Brinkmann * zynq_secondary_init - Initialize secondary CPU cores
110ae88b85eSSoren Brinkmann * @cpu: CPU that is initialized
111ae88b85eSSoren Brinkmann *
112ae88b85eSSoren Brinkmann * This function is in the hotplug path. Don't move it into the
113ae88b85eSSoren Brinkmann * init section!!
114ae88b85eSSoren Brinkmann */
zynq_secondary_init(unsigned int cpu)115ae88b85eSSoren Brinkmann static void zynq_secondary_init(unsigned int cpu)
116ae88b85eSSoren Brinkmann {
117ae88b85eSSoren Brinkmann zynq_core_pm_init();
118ae88b85eSSoren Brinkmann }
119ae88b85eSSoren Brinkmann
120f1fd2fa6SMichal Simek #ifdef CONFIG_HOTPLUG_CPU
zynq_cpu_kill(unsigned cpu)121f1fd2fa6SMichal Simek static int zynq_cpu_kill(unsigned cpu)
122f1fd2fa6SMichal Simek {
12350c7960aSSoren Brinkmann unsigned long timeout = jiffies + msecs_to_jiffies(50);
12450c7960aSSoren Brinkmann
12550c7960aSSoren Brinkmann while (zynq_slcr_cpu_state_read(cpu))
12650c7960aSSoren Brinkmann if (time_after(jiffies, timeout))
12750c7960aSSoren Brinkmann return 0;
12850c7960aSSoren Brinkmann
129f1fd2fa6SMichal Simek zynq_slcr_cpu_stop(cpu);
130f1fd2fa6SMichal Simek return 1;
131f1fd2fa6SMichal Simek }
132caf86a73SSoren Brinkmann
133ed62e330SSoren Brinkmann /**
134ed62e330SSoren Brinkmann * zynq_cpu_die - Let a CPU core die
135ed62e330SSoren Brinkmann * @cpu: Dying CPU
136caf86a73SSoren Brinkmann *
137ed62e330SSoren Brinkmann * Platform-specific code to shutdown a CPU.
138ed62e330SSoren Brinkmann * Called with IRQs disabled on the dying CPU.
139caf86a73SSoren Brinkmann */
zynq_cpu_die(unsigned int cpu)140ed62e330SSoren Brinkmann static void zynq_cpu_die(unsigned int cpu)
141caf86a73SSoren Brinkmann {
142caf86a73SSoren Brinkmann zynq_slcr_cpu_state_write(cpu, true);
143caf86a73SSoren Brinkmann
144caf86a73SSoren Brinkmann /*
145caf86a73SSoren Brinkmann * there is no power-control hardware on this platform, so all
146caf86a73SSoren Brinkmann * we can do is put the core into WFI; this is safe as the calling
147caf86a73SSoren Brinkmann * code will have already disabled interrupts
148caf86a73SSoren Brinkmann */
149caf86a73SSoren Brinkmann for (;;)
150caf86a73SSoren Brinkmann cpu_do_idle();
151caf86a73SSoren Brinkmann }
152f1fd2fa6SMichal Simek #endif
153f1fd2fa6SMichal Simek
15475305275SMasahiro Yamada const struct smp_operations zynq_smp_ops __initconst = {
155aa7eb2bbSMichal Simek .smp_init_cpus = zynq_smp_init_cpus,
156aa7eb2bbSMichal Simek .smp_prepare_cpus = zynq_smp_prepare_cpus,
157aa7eb2bbSMichal Simek .smp_boot_secondary = zynq_boot_secondary,
158ae88b85eSSoren Brinkmann .smp_secondary_init = zynq_secondary_init,
159c7c28b0fSMichal Simek #ifdef CONFIG_HOTPLUG_CPU
160ed62e330SSoren Brinkmann .cpu_die = zynq_cpu_die,
161f1fd2fa6SMichal Simek .cpu_kill = zynq_cpu_kill,
162c7c28b0fSMichal Simek #endif
163aa7eb2bbSMichal Simek };
164