1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * R9A06G032 Second CA7 enabler. |
4 | * |
5 | * Copyright (C) 2018 Renesas Electronics Europe Limited |
6 | * |
7 | * Michel Pollet <michel.pollet@bp.renesas.com>, <buserror@gmail.com> |
8 | * Derived from actions,s500-smp |
9 | */ |
10 | |
11 | #include <linux/io.h> |
12 | #include <linux/of.h> |
13 | #include <linux/of_address.h> |
14 | #include <linux/smp.h> |
15 | |
16 | /* |
17 | * The second CPU is parked in ROM at boot time. It requires waking it after |
18 | * writing an address into the BOOTADDR register of sysctrl. |
19 | * |
20 | * So the default value of the "cpu-release-addr" corresponds to BOOTADDR... |
21 | * |
22 | * *However* the BOOTADDR register is not available when the kernel |
23 | * starts in NONSEC mode. |
24 | * |
25 | * So for NONSEC mode, the bootloader re-parks the second CPU into a pen |
26 | * in SRAM, and changes the "cpu-release-addr" of linux's DT to a SRAM address, |
27 | * which is not restricted. |
28 | */ |
29 | |
30 | static void __iomem *cpu_bootaddr; |
31 | |
32 | static DEFINE_SPINLOCK(cpu_lock); |
33 | |
34 | static int |
35 | r9a06g032_smp_boot_secondary(unsigned int cpu, |
36 | struct task_struct *idle) |
37 | { |
38 | if (!cpu_bootaddr) |
39 | return -ENODEV; |
40 | |
41 | spin_lock(lock: &cpu_lock); |
42 | |
43 | writel(__pa_symbol(secondary_startup), cpu_bootaddr); |
44 | arch_send_wakeup_ipi_mask(cpumask_of(cpu)); |
45 | |
46 | spin_unlock(lock: &cpu_lock); |
47 | |
48 | return 0; |
49 | } |
50 | |
51 | static void __init r9a06g032_smp_prepare_cpus(unsigned int max_cpus) |
52 | { |
53 | struct device_node *dn; |
54 | int ret = -EINVAL, dns; |
55 | u32 bootaddr; |
56 | |
57 | dn = of_get_cpu_node(cpu: 1, NULL); |
58 | if (!dn) { |
59 | pr_err("CPU#1: missing device tree node\n" ); |
60 | return; |
61 | } |
62 | /* |
63 | * Determine the address from which the CPU is polling. |
64 | * The bootloader *does* change this property. |
65 | * Note: The property can be either 64 or 32 bits, so handle both cases |
66 | */ |
67 | if (of_find_property(np: dn, name: "cpu-release-addr" , lenp: &dns)) { |
68 | if (dns == sizeof(u64)) { |
69 | u64 temp; |
70 | |
71 | ret = of_property_read_u64(np: dn, |
72 | propname: "cpu-release-addr" , out_value: &temp); |
73 | bootaddr = temp; |
74 | } else { |
75 | ret = of_property_read_u32(np: dn, |
76 | propname: "cpu-release-addr" , |
77 | out_value: &bootaddr); |
78 | } |
79 | } |
80 | of_node_put(node: dn); |
81 | if (ret) { |
82 | pr_err("CPU#1: invalid cpu-release-addr property\n" ); |
83 | return; |
84 | } |
85 | pr_info("CPU#1: cpu-release-addr %08x\n" , bootaddr); |
86 | |
87 | cpu_bootaddr = ioremap(offset: bootaddr, size: sizeof(bootaddr)); |
88 | } |
89 | |
90 | static const struct smp_operations r9a06g032_smp_ops __initconst = { |
91 | .smp_prepare_cpus = r9a06g032_smp_prepare_cpus, |
92 | .smp_boot_secondary = r9a06g032_smp_boot_secondary, |
93 | }; |
94 | |
95 | CPU_METHOD_OF_DECLARE(r9a06g032_smp, |
96 | "renesas,r9a06g032-smp" , &r9a06g032_smp_ops); |
97 | |