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