summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/arm64/Kconfig9
-rw-r--r--arch/arm64/include/asm/acpi.h19
-rw-r--r--arch/arm64/include/asm/hardirq.h2
-rw-r--r--arch/arm64/include/asm/smp.h9
-rw-r--r--arch/arm64/kernel/Makefile1
-rw-r--r--arch/arm64/kernel/acpi_parking_protocol.c153
-rw-r--r--arch/arm64/kernel/cpu_ops.c27
-rw-r--r--arch/arm64/kernel/smp.c28
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
757menu "Boot options" 757menu "Boot options"
758 758
759config 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
759config CMDLINE 768config 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);
87static inline void acpi_init_cpus(void) { } 87static inline void acpi_init_cpus(void) { }
88#endif /* CONFIG_ACPI */ 88#endif /* CONFIG_ACPI */
89 89
90#ifdef CONFIG_ARM64_ACPI_PARKING_PROTOCOL
91bool acpi_parking_protocol_valid(int cpu);
92void __init
93acpi_set_mailbox_entry(int cpu, struct acpi_madt_generic_interrupt *processor);
94#else
95static inline bool acpi_parking_protocol_valid(int cpu) { return false; }
96static inline void
97acpi_set_mailbox_entry(int cpu, struct acpi_madt_generic_interrupt *processor)
98{}
99#endif
100
90static inline const char *acpi_get_enable_method(int cpu) 101static 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
25typedef struct { 25typedef 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);
64extern void arch_send_call_function_single_ipi(int cpu); 64extern void arch_send_call_function_single_ipi(int cpu);
65extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); 65extern void arch_send_call_function_ipi_mask(const struct cpumask *mask);
66 66
67#ifdef CONFIG_ARM64_ACPI_PARKING_PROTOCOL
68extern void arch_send_wakeup_ipi_mask(const struct cpumask *mask);
69#else
70static inline void arch_send_wakeup_ipi_mask(const struct cpumask *mask)
71{
72 BUILD_BUG();
73}
74#endif
75
67extern int __cpu_disable(void); 76extern int __cpu_disable(void);
68 77
69extern void __cpu_die(unsigned int cpu); 78extern 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
41arm64-obj-$(CONFIG_PCI) += pci.o 41arm64-obj-$(CONFIG_PCI) += pci.o
42arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o 42arm64-obj-$(CONFIG_ARMV8_DEPRECATED) += armv8_deprecated.o
43arm64-obj-$(CONFIG_ACPI) += acpi.o 43arm64-obj-$(CONFIG_ACPI) += acpi.o
44arm64-obj-$(CONFIG_ARM64_ACPI_PARKING_PROTOCOL) += acpi_parking_protocol.o
44arm64-obj-$(CONFIG_PARAVIRT) += paravirt.o 45arm64-obj-$(CONFIG_PARAVIRT) += paravirt.o
45 46
46obj-y += $(arm64-obj-y) vdso/ 47obj-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
24struct cpu_mailbox_entry {
25 phys_addr_t mailbox_addr;
26 u8 version;
27 u8 gic_cpu_id;
28};
29
30static struct cpu_mailbox_entry cpu_mailbox_entries[NR_CPUS];
31
32void __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
42bool 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
49static 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
57static int acpi_parking_protocol_cpu_prepare(unsigned int cpu)
58{
59 return 0;
60}
61
62struct parking_protocol_mailbox {
63 __le32 cpu_id;
64 __le32 reserved;
65 __le64 entry_point;
66};
67
68static 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
115static 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
147const 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
27extern const struct cpu_operations smp_spin_table_ops; 27extern const struct cpu_operations smp_spin_table_ops;
28extern const struct cpu_operations acpi_parking_protocol_ops;
28extern const struct cpu_operations cpu_psci_ops; 29extern const struct cpu_operations cpu_psci_ops;
29 30
30const struct cpu_operations *cpu_ops[NR_CPUS]; 31const struct cpu_operations *cpu_ops[NR_CPUS];
31 32
32static const struct cpu_operations *supported_cpu_ops[] __initconst = { 33static 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
39static 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
38static const struct cpu_operations * __init cpu_get_ops(const char *name) 47static 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
630static void smp_cross_call(const struct cpumask *target, unsigned int ipinr) 643static 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
685void 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
672void arch_irq_work_raise(void) 692void 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;