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