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 }; 154