diff options
-rw-r--r-- | arch/arm64/Kconfig | 9 | ||||
-rw-r--r-- | arch/arm64/include/asm/acpi.h | 19 | ||||
-rw-r--r-- | arch/arm64/include/asm/hardirq.h | 2 | ||||
-rw-r--r-- | arch/arm64/include/asm/smp.h | 9 | ||||
-rw-r--r-- | arch/arm64/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/arm64/kernel/acpi_parking_protocol.c | 153 | ||||
-rw-r--r-- | arch/arm64/kernel/cpu_ops.c | 27 | ||||
-rw-r--r-- | arch/arm64/kernel/smp.c | 28 |
8 files changed, 242 insertions, 6 deletions
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 8cc62289a63e..53e48a617fef 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig | |||
@@ -756,6 +756,15 @@ endmenu | |||
756 | 756 | ||
757 | menu "Boot options" | 757 | menu "Boot options" |
758 | 758 | ||
759 | config ARM64_ACPI_PARKING_PROTOCOL | ||
760 | bool "Enable support for the ARM64 ACPI parking protocol" | ||
761 | depends on ACPI | ||
762 | help | ||
763 | Enable support for the ARM64 ACPI parking protocol. If disabled | ||
764 | the kernel will not allow booting through the ARM64 ACPI parking | ||
765 | protocol even if the corresponding data is present in the ACPI | ||
766 | MADT table. | ||
767 | |||
759 | config CMDLINE | 768 | config CMDLINE |
760 | string "Default kernel command string" | 769 | string "Default kernel command string" |
761 | default "" | 770 | default "" |
diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h index caafd63b8092..aee323b13802 100644 --- a/arch/arm64/include/asm/acpi.h +++ b/arch/arm64/include/asm/acpi.h | |||
@@ -87,9 +87,26 @@ void __init acpi_init_cpus(void); | |||
87 | static inline void acpi_init_cpus(void) { } | 87 | static inline void acpi_init_cpus(void) { } |
88 | #endif /* CONFIG_ACPI */ | 88 | #endif /* CONFIG_ACPI */ |
89 | 89 | ||
90 | #ifdef CONFIG_ARM64_ACPI_PARKING_PROTOCOL | ||
91 | bool acpi_parking_protocol_valid(int cpu); | ||
92 | void __init | ||
93 | acpi_set_mailbox_entry(int cpu, struct acpi_madt_generic_interrupt *processor); | ||
94 | #else | ||
95 | static inline bool acpi_parking_protocol_valid(int cpu) { return false; } | ||
96 | static inline void | ||
97 | acpi_set_mailbox_entry(int cpu, struct acpi_madt_generic_interrupt *processor) | ||
98 | {} | ||
99 | #endif | ||
100 | |||
90 | static inline const char *acpi_get_enable_method(int cpu) | 101 | static inline const char *acpi_get_enable_method(int cpu) |
91 | { | 102 | { |
92 | return acpi_psci_present() ? "psci" : NULL; | 103 | if (acpi_psci_present()) |
104 | return "psci"; | ||
105 | |||
106 | if (acpi_parking_protocol_valid(cpu)) | ||
107 | return "parking-protocol"; | ||
108 | |||
109 | return NULL; | ||
93 | } | 110 | } |
94 | 111 | ||
95 | #ifdef CONFIG_ACPI_APEI | 112 | #ifdef CONFIG_ACPI_APEI |
diff --git a/arch/arm64/include/asm/hardirq.h b/arch/arm64/include/asm/hardirq.h index a57601f9d17c..8740297dac77 100644 --- a/arch/arm64/include/asm/hardirq.h +++ b/arch/arm64/include/asm/hardirq.h | |||
@@ -20,7 +20,7 @@ | |||
20 | #include <linux/threads.h> | 20 | #include <linux/threads.h> |
21 | #include <asm/irq.h> | 21 | #include <asm/irq.h> |
22 | 22 | ||
23 | #define NR_IPI 5 | 23 | #define NR_IPI 6 |
24 | 24 | ||
25 | typedef struct { | 25 | typedef struct { |
26 | unsigned int __softirq_pending; | 26 | unsigned int __softirq_pending; |
diff --git a/arch/arm64/include/asm/smp.h b/arch/arm64/include/asm/smp.h index d9c3d6a6100a..2013a4dc5124 100644 --- a/arch/arm64/include/asm/smp.h +++ b/arch/arm64/include/asm/smp.h | |||
@@ -64,6 +64,15 @@ extern void secondary_entry(void); | |||
64 | extern void arch_send_call_function_single_ipi(int cpu); | 64 | extern void arch_send_call_function_single_ipi(int cpu); |
65 | extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); | 65 | extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); |
66 | 66 | ||
67 | #ifdef CONFIG_ARM64_ACPI_PARKING_PROTOCOL | ||
68 | extern void arch_send_wakeup_ipi_mask(const struct cpumask *mask); | ||
69 | #else | ||
70 | static inline void arch_send_wakeup_ipi_mask(const struct cpumask *mask) | ||
71 | { | ||
72 | BUILD_BUG(); | ||
73 | } | ||
74 | #endif | ||
75 | |||
67 | extern int __cpu_disable(void); | 76 | extern int __cpu_disable(void); |
68 | 77 | ||
69 | extern void __cpu_die(unsigned int cpu); | 78 | extern void __cpu_die(unsigned int cpu); |
diff --git a/arch/arm64/kernel/Makefile b/arch/arm64/kernel/Makefile index 83cd7e68e83b..8a9c65ccb636 100644 --- a/arch/arm64/kernel/Makefile +++ b/arch/arm64/kernel/Makefile | |||
@@ -41,6 +41,7 @@ arm64-obj-$(CONFIG_EFI) += efi.o efi-entry.stub.o | |||
41 | arm64-obj-$(CONFIG_PCI) += pci.o | 41 | arm64-obj-$(CONFIG_PCI) += pci.o |
42 | arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o | 42 | arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o |
43 | arm64-obj-$(CONFIG_ACPI) += acpi.o | 43 | arm64-obj-$(CONFIG_ACPI) += acpi.o |
44 | arm64-obj-$(CONFIG_ARM64_ACPI_PARKING_PROTOCOL) += acpi_parking_protocol.o | ||
44 | arm64-obj-$(CONFIG_PARAVIRT) += paravirt.o | 45 | arm64-obj-$(CONFIG_PARAVIRT) += paravirt.o |
45 | 46 | ||
46 | obj-y += $(arm64-obj-y) vdso/ | 47 | obj-y += $(arm64-obj-y) vdso/ |
diff --git a/arch/arm64/kernel/acpi_parking_protocol.c b/arch/arm64/kernel/acpi_parking_protocol.c new file mode 100644 index 000000000000..4b1e5a7a98da --- /dev/null +++ b/arch/arm64/kernel/acpi_parking_protocol.c | |||
@@ -0,0 +1,153 @@ | |||
1 | /* | ||
2 | * ARM64 ACPI Parking Protocol implementation | ||
3 | * | ||
4 | * Authors: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> | ||
5 | * Mark Salter <msalter@redhat.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License version 2 as | ||
9 | * published by the Free Software Foundation. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | * | ||
16 | * You should have received a copy of the GNU General Public License | ||
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
18 | */ | ||
19 | #include <linux/acpi.h> | ||
20 | #include <linux/types.h> | ||
21 | |||
22 | #include <asm/cpu_ops.h> | ||
23 | |||
24 | struct cpu_mailbox_entry { | ||
25 | phys_addr_t mailbox_addr; | ||
26 | u8 version; | ||
27 | u8 gic_cpu_id; | ||
28 | }; | ||
29 | |||
30 | static struct cpu_mailbox_entry cpu_mailbox_entries[NR_CPUS]; | ||
31 | |||
32 | void __init acpi_set_mailbox_entry(int cpu, | ||
33 | struct acpi_madt_generic_interrupt *p) | ||
34 | { | ||
35 | struct cpu_mailbox_entry *cpu_entry = &cpu_mailbox_entries[cpu]; | ||
36 | |||
37 | cpu_entry->mailbox_addr = p->parked_address; | ||
38 | cpu_entry->version = p->parking_version; | ||
39 | cpu_entry->gic_cpu_id = p->cpu_interface_number; | ||
40 | } | ||
41 | |||
42 | bool acpi_parking_protocol_valid(int cpu) | ||
43 | { | ||
44 | struct cpu_mailbox_entry *cpu_entry = &cpu_mailbox_entries[cpu]; | ||
45 | |||
46 | return cpu_entry->mailbox_addr && cpu_entry->version; | ||
47 | } | ||
48 | |||
49 | static int acpi_parking_protocol_cpu_init(unsigned int cpu) | ||
50 | { | ||
51 | pr_debug("%s: ACPI parked addr=%llx\n", __func__, | ||
52 | cpu_mailbox_entries[cpu].mailbox_addr); | ||
53 | |||
54 | return 0; | ||
55 | } | ||
56 | |||
57 | static int acpi_parking_protocol_cpu_prepare(unsigned int cpu) | ||
58 | { | ||
59 | return 0; | ||
60 | } | ||
61 | |||
62 | struct parking_protocol_mailbox { | ||
63 | __le32 cpu_id; | ||
64 | __le32 reserved; | ||
65 | __le64 entry_point; | ||
66 | }; | ||
67 | |||
68 | static int acpi_parking_protocol_cpu_boot(unsigned int cpu) | ||
69 | { | ||
70 | struct cpu_mailbox_entry *cpu_entry = &cpu_mailbox_entries[cpu]; | ||
71 | struct parking_protocol_mailbox __iomem *mailbox; | ||
72 | __le32 cpu_id; | ||
73 | |||
74 | /* | ||
75 | * Map mailbox memory with attribute device nGnRE (ie ioremap - | ||
76 | * this deviates from the parking protocol specifications since | ||
77 | * the mailboxes are required to be mapped nGnRnE; the attribute | ||
78 | * discrepancy is harmless insofar as the protocol specification | ||
79 | * is concerned). | ||
80 | * If the mailbox is mistakenly allocated in the linear mapping | ||
81 | * by FW ioremap will fail since the mapping will be prevented | ||
82 | * by the kernel (it clashes with the linear mapping attributes | ||
83 | * specifications). | ||
84 | */ | ||
85 | mailbox = ioremap(cpu_entry->mailbox_addr, sizeof(*mailbox)); | ||
86 | if (!mailbox) | ||
87 | return -EIO; | ||
88 | |||
89 | cpu_id = readl_relaxed(&mailbox->cpu_id); | ||
90 | /* | ||
91 | * Check if firmware has set-up the mailbox entry properly | ||
92 | * before kickstarting the respective cpu. | ||
93 | */ | ||
94 | if (cpu_id != ~0U) { | ||
95 | iounmap(mailbox); | ||
96 | return -ENXIO; | ||
97 | } | ||
98 | |||
99 | /* | ||
100 | * We write the entry point and cpu id as LE regardless of the | ||
101 | * native endianness of the kernel. Therefore, any boot-loaders | ||
102 | * that read this address need to convert this address to the | ||
103 | * Boot-Loader's endianness before jumping. | ||
104 | */ | ||
105 | writeq_relaxed(__pa(secondary_entry), &mailbox->entry_point); | ||
106 | writel_relaxed(cpu_entry->gic_cpu_id, &mailbox->cpu_id); | ||
107 | |||
108 | arch_send_wakeup_ipi_mask(cpumask_of(cpu)); | ||
109 | |||
110 | iounmap(mailbox); | ||
111 | |||
112 | return 0; | ||
113 | } | ||
114 | |||
115 | static void acpi_parking_protocol_cpu_postboot(void) | ||
116 | { | ||
117 | int cpu = smp_processor_id(); | ||
118 | struct cpu_mailbox_entry *cpu_entry = &cpu_mailbox_entries[cpu]; | ||
119 | struct parking_protocol_mailbox __iomem *mailbox; | ||
120 | __le64 entry_point; | ||
121 | |||
122 | /* | ||
123 | * Map mailbox memory with attribute device nGnRE (ie ioremap - | ||
124 | * this deviates from the parking protocol specifications since | ||
125 | * the mailboxes are required to be mapped nGnRnE; the attribute | ||
126 | * discrepancy is harmless insofar as the protocol specification | ||
127 | * is concerned). | ||
128 | * If the mailbox is mistakenly allocated in the linear mapping | ||
129 | * by FW ioremap will fail since the mapping will be prevented | ||
130 | * by the kernel (it clashes with the linear mapping attributes | ||
131 | * specifications). | ||
132 | */ | ||
133 | mailbox = ioremap(cpu_entry->mailbox_addr, sizeof(*mailbox)); | ||
134 | if (!mailbox) | ||
135 | return; | ||
136 | |||
137 | entry_point = readl_relaxed(&mailbox->entry_point); | ||
138 | /* | ||
139 | * Check if firmware has cleared the entry_point as expected | ||
140 | * by the protocol specification. | ||
141 | */ | ||
142 | WARN_ON(entry_point); | ||
143 | |||
144 | iounmap(mailbox); | ||
145 | } | ||
146 | |||
147 | const struct cpu_operations acpi_parking_protocol_ops = { | ||
148 | .name = "parking-protocol", | ||
149 | .cpu_init = acpi_parking_protocol_cpu_init, | ||
150 | .cpu_prepare = acpi_parking_protocol_cpu_prepare, | ||
151 | .cpu_boot = acpi_parking_protocol_cpu_boot, | ||
152 | .cpu_postboot = acpi_parking_protocol_cpu_postboot | ||
153 | }; | ||
diff --git a/arch/arm64/kernel/cpu_ops.c b/arch/arm64/kernel/cpu_ops.c index b6bd7d447768..c7cfb8fe06f9 100644 --- a/arch/arm64/kernel/cpu_ops.c +++ b/arch/arm64/kernel/cpu_ops.c | |||
@@ -25,19 +25,30 @@ | |||
25 | #include <asm/smp_plat.h> | 25 | #include <asm/smp_plat.h> |
26 | 26 | ||
27 | extern const struct cpu_operations smp_spin_table_ops; | 27 | extern const struct cpu_operations smp_spin_table_ops; |
28 | extern const struct cpu_operations acpi_parking_protocol_ops; | ||
28 | extern const struct cpu_operations cpu_psci_ops; | 29 | extern const struct cpu_operations cpu_psci_ops; |
29 | 30 | ||
30 | const struct cpu_operations *cpu_ops[NR_CPUS]; | 31 | const struct cpu_operations *cpu_ops[NR_CPUS]; |
31 | 32 | ||
32 | static const struct cpu_operations *supported_cpu_ops[] __initconst = { | 33 | static const struct cpu_operations *dt_supported_cpu_ops[] __initconst = { |
33 | &smp_spin_table_ops, | 34 | &smp_spin_table_ops, |
34 | &cpu_psci_ops, | 35 | &cpu_psci_ops, |
35 | NULL, | 36 | NULL, |
36 | }; | 37 | }; |
37 | 38 | ||
39 | static const struct cpu_operations *acpi_supported_cpu_ops[] __initconst = { | ||
40 | #ifdef CONFIG_ARM64_ACPI_PARKING_PROTOCOL | ||
41 | &acpi_parking_protocol_ops, | ||
42 | #endif | ||
43 | &cpu_psci_ops, | ||
44 | NULL, | ||
45 | }; | ||
46 | |||
38 | static const struct cpu_operations * __init cpu_get_ops(const char *name) | 47 | static const struct cpu_operations * __init cpu_get_ops(const char *name) |
39 | { | 48 | { |
40 | const struct cpu_operations **ops = supported_cpu_ops; | 49 | const struct cpu_operations **ops; |
50 | |||
51 | ops = acpi_disabled ? dt_supported_cpu_ops : acpi_supported_cpu_ops; | ||
41 | 52 | ||
42 | while (*ops) { | 53 | while (*ops) { |
43 | if (!strcmp(name, (*ops)->name)) | 54 | if (!strcmp(name, (*ops)->name)) |
@@ -75,8 +86,16 @@ static const char *__init cpu_read_enable_method(int cpu) | |||
75 | } | 86 | } |
76 | } else { | 87 | } else { |
77 | enable_method = acpi_get_enable_method(cpu); | 88 | enable_method = acpi_get_enable_method(cpu); |
78 | if (!enable_method) | 89 | if (!enable_method) { |
79 | pr_err("Unsupported ACPI enable-method\n"); | 90 | /* |
91 | * In ACPI systems the boot CPU does not require | ||
92 | * checking the enable method since for some | ||
93 | * boot protocol (ie parking protocol) it need not | ||
94 | * be initialized. Don't warn spuriously. | ||
95 | */ | ||
96 | if (cpu != 0) | ||
97 | pr_err("Unsupported ACPI enable-method\n"); | ||
98 | } | ||
80 | } | 99 | } |
81 | 100 | ||
82 | return enable_method; | 101 | return enable_method; |
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c index 68e7f79630d4..24cb4f800033 100644 --- a/arch/arm64/kernel/smp.c +++ b/arch/arm64/kernel/smp.c | |||
@@ -70,6 +70,7 @@ enum ipi_msg_type { | |||
70 | IPI_CPU_STOP, | 70 | IPI_CPU_STOP, |
71 | IPI_TIMER, | 71 | IPI_TIMER, |
72 | IPI_IRQ_WORK, | 72 | IPI_IRQ_WORK, |
73 | IPI_WAKEUP | ||
73 | }; | 74 | }; |
74 | 75 | ||
75 | /* | 76 | /* |
@@ -443,6 +444,17 @@ acpi_map_gic_cpu_interface(struct acpi_madt_generic_interrupt *processor) | |||
443 | /* map the logical cpu id to cpu MPIDR */ | 444 | /* map the logical cpu id to cpu MPIDR */ |
444 | cpu_logical_map(cpu_count) = hwid; | 445 | cpu_logical_map(cpu_count) = hwid; |
445 | 446 | ||
447 | /* | ||
448 | * Set-up the ACPI parking protocol cpu entries | ||
449 | * while initializing the cpu_logical_map to | ||
450 | * avoid parsing MADT entries multiple times for | ||
451 | * nothing (ie a valid cpu_logical_map entry should | ||
452 | * contain a valid parking protocol data set to | ||
453 | * initialize the cpu if the parking protocol is | ||
454 | * the only available enable method). | ||
455 | */ | ||
456 | acpi_set_mailbox_entry(cpu_count, processor); | ||
457 | |||
446 | cpu_count++; | 458 | cpu_count++; |
447 | } | 459 | } |
448 | 460 | ||
@@ -625,6 +637,7 @@ static const char *ipi_types[NR_IPI] __tracepoint_string = { | |||
625 | S(IPI_CPU_STOP, "CPU stop interrupts"), | 637 | S(IPI_CPU_STOP, "CPU stop interrupts"), |
626 | S(IPI_TIMER, "Timer broadcast interrupts"), | 638 | S(IPI_TIMER, "Timer broadcast interrupts"), |
627 | S(IPI_IRQ_WORK, "IRQ work interrupts"), | 639 | S(IPI_IRQ_WORK, "IRQ work interrupts"), |
640 | S(IPI_WAKEUP, "CPU wake-up interrupts"), | ||
628 | }; | 641 | }; |
629 | 642 | ||
630 | static void smp_cross_call(const struct cpumask *target, unsigned int ipinr) | 643 | static void smp_cross_call(const struct cpumask *target, unsigned int ipinr) |
@@ -668,6 +681,13 @@ void arch_send_call_function_single_ipi(int cpu) | |||
668 | smp_cross_call(cpumask_of(cpu), IPI_CALL_FUNC); | 681 | smp_cross_call(cpumask_of(cpu), IPI_CALL_FUNC); |
669 | } | 682 | } |
670 | 683 | ||
684 | #ifdef CONFIG_ARM64_ACPI_PARKING_PROTOCOL | ||
685 | void arch_send_wakeup_ipi_mask(const struct cpumask *mask) | ||
686 | { | ||
687 | smp_cross_call(mask, IPI_WAKEUP); | ||
688 | } | ||
689 | #endif | ||
690 | |||
671 | #ifdef CONFIG_IRQ_WORK | 691 | #ifdef CONFIG_IRQ_WORK |
672 | void arch_irq_work_raise(void) | 692 | void arch_irq_work_raise(void) |
673 | { | 693 | { |
@@ -745,6 +765,14 @@ void handle_IPI(int ipinr, struct pt_regs *regs) | |||
745 | break; | 765 | break; |
746 | #endif | 766 | #endif |
747 | 767 | ||
768 | #ifdef CONFIG_ARM64_ACPI_PARKING_PROTOCOL | ||
769 | case IPI_WAKEUP: | ||
770 | WARN_ONCE(!acpi_parking_protocol_valid(cpu), | ||
771 | "CPU%u: Wake-up IPI outside the ACPI parking protocol\n", | ||
772 | cpu); | ||
773 | break; | ||
774 | #endif | ||
775 | |||
748 | default: | 776 | default: |
749 | pr_crit("CPU%u: Unknown IPI message 0x%x\n", cpu, ipinr); | 777 | pr_crit("CPU%u: Unknown IPI message 0x%x\n", cpu, ipinr); |
750 | break; | 778 | break; |