1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * ARM64 ACPI Parking Protocol implementation |
4 | * |
5 | * Authors: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> |
6 | * Mark Salter <msalter@redhat.com> |
7 | */ |
8 | #include <linux/acpi.h> |
9 | #include <linux/mm.h> |
10 | #include <linux/types.h> |
11 | |
12 | #include <asm/cpu_ops.h> |
13 | |
14 | struct parking_protocol_mailbox { |
15 | __le32 cpu_id; |
16 | __le32 reserved; |
17 | __le64 entry_point; |
18 | }; |
19 | |
20 | struct cpu_mailbox_entry { |
21 | struct parking_protocol_mailbox __iomem *mailbox; |
22 | phys_addr_t mailbox_addr; |
23 | u8 version; |
24 | u8 gic_cpu_id; |
25 | }; |
26 | |
27 | static struct cpu_mailbox_entry cpu_mailbox_entries[NR_CPUS]; |
28 | |
29 | void __init acpi_set_mailbox_entry(int cpu, |
30 | struct acpi_madt_generic_interrupt *p) |
31 | { |
32 | struct cpu_mailbox_entry *cpu_entry = &cpu_mailbox_entries[cpu]; |
33 | |
34 | cpu_entry->mailbox_addr = p->parked_address; |
35 | cpu_entry->version = p->parking_version; |
36 | cpu_entry->gic_cpu_id = p->cpu_interface_number; |
37 | } |
38 | |
39 | bool acpi_parking_protocol_valid(int cpu) |
40 | { |
41 | struct cpu_mailbox_entry *cpu_entry = &cpu_mailbox_entries[cpu]; |
42 | |
43 | return cpu_entry->mailbox_addr && cpu_entry->version; |
44 | } |
45 | |
46 | static int acpi_parking_protocol_cpu_init(unsigned int cpu) |
47 | { |
48 | pr_debug("%s: ACPI parked addr=%llx\n" , __func__, |
49 | cpu_mailbox_entries[cpu].mailbox_addr); |
50 | |
51 | return 0; |
52 | } |
53 | |
54 | static int acpi_parking_protocol_cpu_prepare(unsigned int cpu) |
55 | { |
56 | return 0; |
57 | } |
58 | |
59 | static int acpi_parking_protocol_cpu_boot(unsigned int cpu) |
60 | { |
61 | struct cpu_mailbox_entry *cpu_entry = &cpu_mailbox_entries[cpu]; |
62 | struct parking_protocol_mailbox __iomem *mailbox; |
63 | u32 cpu_id; |
64 | |
65 | /* |
66 | * Map mailbox memory with attribute device nGnRE (ie ioremap - |
67 | * this deviates from the parking protocol specifications since |
68 | * the mailboxes are required to be mapped nGnRnE; the attribute |
69 | * discrepancy is harmless insofar as the protocol specification |
70 | * is concerned). |
71 | * If the mailbox is mistakenly allocated in the linear mapping |
72 | * by FW ioremap will fail since the mapping will be prevented |
73 | * by the kernel (it clashes with the linear mapping attributes |
74 | * specifications). |
75 | */ |
76 | mailbox = ioremap(offset: cpu_entry->mailbox_addr, size: sizeof(*mailbox)); |
77 | if (!mailbox) |
78 | return -EIO; |
79 | |
80 | cpu_id = readl_relaxed(&mailbox->cpu_id); |
81 | /* |
82 | * Check if firmware has set-up the mailbox entry properly |
83 | * before kickstarting the respective cpu. |
84 | */ |
85 | if (cpu_id != ~0U) { |
86 | iounmap(addr: mailbox); |
87 | return -ENXIO; |
88 | } |
89 | |
90 | /* |
91 | * stash the mailbox address mapping to use it for further FW |
92 | * checks in the postboot method |
93 | */ |
94 | cpu_entry->mailbox = mailbox; |
95 | |
96 | /* |
97 | * We write the entry point and cpu id as LE regardless of the |
98 | * native endianness of the kernel. Therefore, any boot-loaders |
99 | * that read this address need to convert this address to the |
100 | * Boot-Loader's endianness before jumping. |
101 | */ |
102 | writeq_relaxed(__pa_symbol(secondary_entry), |
103 | &mailbox->entry_point); |
104 | writel_relaxed(cpu_entry->gic_cpu_id, &mailbox->cpu_id); |
105 | |
106 | arch_send_wakeup_ipi(cpu); |
107 | |
108 | return 0; |
109 | } |
110 | |
111 | static void acpi_parking_protocol_cpu_postboot(void) |
112 | { |
113 | int cpu = smp_processor_id(); |
114 | struct cpu_mailbox_entry *cpu_entry = &cpu_mailbox_entries[cpu]; |
115 | struct parking_protocol_mailbox __iomem *mailbox = cpu_entry->mailbox; |
116 | u64 entry_point; |
117 | |
118 | entry_point = readq_relaxed(&mailbox->entry_point); |
119 | /* |
120 | * Check if firmware has cleared the entry_point as expected |
121 | * by the protocol specification. |
122 | */ |
123 | WARN_ON(entry_point); |
124 | } |
125 | |
126 | const struct cpu_operations acpi_parking_protocol_ops = { |
127 | .name = "parking-protocol" , |
128 | .cpu_init = acpi_parking_protocol_cpu_init, |
129 | .cpu_prepare = acpi_parking_protocol_cpu_prepare, |
130 | .cpu_boot = acpi_parking_protocol_cpu_boot, |
131 | .cpu_postboot = acpi_parking_protocol_cpu_postboot |
132 | }; |
133 | |