Patrick Rudolph | 7efbdbb | 2024-10-23 15:19:50 +0200 | [diff] [blame] | 1 | // SPDX-License-Identifier: GPL-2.0+ |
| 2 | /* |
| 3 | * Based on acpi.c from coreboot |
| 4 | * |
| 5 | * Copyright (C) 2024 9elements GmbH |
| 6 | */ |
| 7 | |
| 8 | #define LOG_CATEGORY LOGC_ACPI |
| 9 | |
Patrick Rudolph | 2f6f8d9 | 2024-10-23 15:20:13 +0200 | [diff] [blame] | 10 | #include <bloblist.h> |
| 11 | #include <cpu_func.h> |
| 12 | #include <efi_loader.h> |
| 13 | #include <malloc.h> |
Patrick Rudolph | 7efbdbb | 2024-10-23 15:19:50 +0200 | [diff] [blame] | 14 | #include <string.h> |
Patrick Rudolph | 2f6f8d9 | 2024-10-23 15:20:13 +0200 | [diff] [blame] | 15 | #include <tables_csum.h> |
Patrick Rudolph | 7efbdbb | 2024-10-23 15:19:50 +0200 | [diff] [blame] | 16 | #include <acpi/acpigen.h> |
| 17 | #include <acpi/acpi_device.h> |
| 18 | #include <acpi/acpi_table.h> |
Patrick Rudolph | 2f6f8d9 | 2024-10-23 15:20:13 +0200 | [diff] [blame] | 19 | #include <asm-generic/io.h> |
Patrick Rudolph | bff7c57 | 2024-10-23 15:19:51 +0200 | [diff] [blame] | 20 | #include <dm/acpi.h> |
| 21 | #include <dm/uclass.h> |
Patrick Rudolph | 2f6f8d9 | 2024-10-23 15:20:13 +0200 | [diff] [blame] | 22 | #include <linux/log2.h> |
| 23 | #include <linux/sizes.h> |
| 24 | |
| 25 | /* defined in assembly file */ |
| 26 | /** |
| 27 | * acpi_pp_code_size - Spinloop code size * |
| 28 | */ |
| 29 | extern u16 acpi_pp_code_size; |
| 30 | |
| 31 | /** |
| 32 | * acpi_pp_tables - Start of ACPI PP tables. |
| 33 | */ |
| 34 | extern ulong acpi_pp_tables; |
| 35 | |
| 36 | /** |
| 37 | * acpi_pp_etables - End of ACPI PP tables. |
| 38 | */ |
| 39 | extern ulong acpi_pp_etables; |
| 40 | |
| 41 | /** |
| 42 | * acpi_pp_code_start() - Spinloop code |
| 43 | * |
| 44 | * Architectural spinloop code to be installed in each parking protocol |
| 45 | * page. The spinloop code must be less than 2048 bytes. |
| 46 | * |
| 47 | * The spinloop code will be entered after calling |
| 48 | * acpi_parking_protocol_install(). |
| 49 | * |
| 50 | */ |
| 51 | void acpi_pp_code_start(void); |
Patrick Rudolph | 7efbdbb | 2024-10-23 15:19:50 +0200 | [diff] [blame] | 52 | |
| 53 | void acpi_write_madt_gicc(struct acpi_madt_gicc *gicc, uint cpu_num, |
| 54 | uint perf_gsiv, ulong phys_base, ulong gicv, |
| 55 | ulong gich, uint vgic_maint_irq, u64 gicr_base, |
| 56 | ulong mpidr, uint efficiency) |
| 57 | { |
| 58 | memset(gicc, '\0', sizeof(struct acpi_madt_gicc)); |
| 59 | gicc->type = ACPI_APIC_GICC; |
| 60 | gicc->length = sizeof(struct acpi_madt_gicc); |
| 61 | gicc->cpu_if_num = cpu_num; |
| 62 | gicc->processor_id = cpu_num; |
| 63 | gicc->flags = ACPI_MADTF_ENABLED; |
| 64 | gicc->perf_gsiv = perf_gsiv; |
| 65 | gicc->phys_base = phys_base; |
| 66 | gicc->gicv = gicv; |
| 67 | gicc->gich = gich; |
| 68 | gicc->vgic_maint_irq = vgic_maint_irq; |
| 69 | gicc->gicr_base = gicr_base; |
| 70 | gicc->mpidr = mpidr; |
| 71 | gicc->efficiency = efficiency; |
| 72 | } |
| 73 | |
| 74 | void acpi_write_madt_gicd(struct acpi_madt_gicd *gicd, uint gic_id, |
| 75 | ulong phys_base, uint gic_version) |
| 76 | { |
| 77 | memset(gicd, '\0', sizeof(struct acpi_madt_gicd)); |
| 78 | gicd->type = ACPI_APIC_GICD; |
| 79 | gicd->length = sizeof(struct acpi_madt_gicd); |
| 80 | gicd->gic_id = gic_id; |
| 81 | gicd->phys_base = phys_base; |
| 82 | gicd->gic_version = gic_version; |
| 83 | } |
| 84 | |
| 85 | void acpi_write_madt_gicr(struct acpi_madt_gicr *gicr, |
| 86 | u64 discovery_range_base_address, |
| 87 | u32 discovery_range_length) |
| 88 | { |
| 89 | memset(gicr, '\0', sizeof(struct acpi_madt_gicr)); |
| 90 | gicr->type = ACPI_APIC_GICR; |
| 91 | gicr->length = sizeof(struct acpi_madt_gicr); |
| 92 | gicr->discovery_range_base_address = discovery_range_base_address; |
| 93 | gicr->discovery_range_length = discovery_range_length; |
| 94 | } |
| 95 | |
| 96 | void acpi_write_madt_its(struct acpi_madt_its *its, |
| 97 | u32 its_id, |
| 98 | u64 physical_base_address) |
| 99 | { |
| 100 | memset(its, '\0', sizeof(struct acpi_madt_its)); |
| 101 | its->type = ACPI_APIC_ITS; |
| 102 | its->length = sizeof(struct acpi_madt_its); |
| 103 | its->gic_its_id = its_id; |
| 104 | its->physical_base_address = physical_base_address; |
| 105 | } |
| 106 | |
| 107 | int acpi_pptt_add_proc(struct acpi_ctx *ctx, const u32 flags, const u32 parent, |
| 108 | const u32 proc_id, const u32 num_resources, |
| 109 | const u32 *resource_list) |
| 110 | { |
| 111 | struct acpi_pptt_proc *proc = ctx->current; |
| 112 | int offset; |
| 113 | |
| 114 | offset = ctx->current - ctx->tab_start; |
| 115 | proc->hdr.type = ACPI_PPTT_TYPE_PROC; |
| 116 | proc->flags = flags; |
| 117 | proc->parent = parent; |
| 118 | proc->proc_id = proc_id; |
| 119 | proc->num_resources = num_resources; |
| 120 | proc->hdr.length = sizeof(struct acpi_pptt_proc) + |
| 121 | sizeof(u32) * num_resources; |
| 122 | |
| 123 | if (resource_list) |
| 124 | memcpy(proc + 1, resource_list, sizeof(u32) * num_resources); |
| 125 | |
| 126 | acpi_inc(ctx, proc->hdr.length); |
| 127 | |
| 128 | return offset; |
| 129 | } |
| 130 | |
| 131 | int acpi_pptt_add_cache(struct acpi_ctx *ctx, const u32 flags, |
| 132 | const u32 next_cache_level, const u32 size, |
| 133 | const u32 sets, const u8 assoc, const u8 attributes, |
| 134 | const u16 line_size) |
| 135 | { |
| 136 | struct acpi_pptt_cache *cache = ctx->current; |
| 137 | int offset; |
| 138 | |
| 139 | offset = ctx->current - ctx->tab_start; |
| 140 | cache->hdr.type = ACPI_PPTT_TYPE_CACHE; |
| 141 | cache->hdr.length = sizeof(struct acpi_pptt_cache); |
| 142 | cache->flags = flags; |
| 143 | cache->next_cache_level = next_cache_level; |
| 144 | cache->size = size; |
| 145 | cache->sets = sets; |
| 146 | cache->assoc = assoc; |
| 147 | cache->attributes = attributes; |
| 148 | cache->line_size = line_size; |
| 149 | acpi_inc(ctx, cache->hdr.length); |
| 150 | |
| 151 | return offset; |
| 152 | } |
Patrick Rudolph | bff7c57 | 2024-10-23 15:19:51 +0200 | [diff] [blame] | 153 | |
| 154 | void *acpi_fill_madt(struct acpi_madt *madt, struct acpi_ctx *ctx) |
| 155 | { |
| 156 | uclass_probe_all(UCLASS_CPU); |
| 157 | uclass_probe_all(UCLASS_IRQ); |
| 158 | |
| 159 | /* All SoCs must use the driver model */ |
| 160 | acpi_fill_madt_subtbl(ctx); |
| 161 | |
| 162 | return ctx->current; |
| 163 | } |
Patrick Rudolph | 2f6f8d9 | 2024-10-23 15:20:13 +0200 | [diff] [blame] | 164 | |
| 165 | /** |
| 166 | * acpi_write_pp_setup_one_page() - Fill out one page used by the PP |
| 167 | * |
| 168 | * Fill out the struct acpi_pp_page to contain the spin-loop |
| 169 | * code and the mailbox area. After this function the page is ready for |
| 170 | * the secondary core's to enter the spin-loop code. |
| 171 | * |
| 172 | * @page: Pointer to current parking protocol page |
| 173 | * @gicc: Pointer to corresponding GICC sub-table |
| 174 | */ |
| 175 | static void acpi_write_pp_setup_one_page(struct acpi_pp_page *page, |
| 176 | struct acpi_madt_gicc *gicc) |
| 177 | { |
| 178 | void *reloc; |
| 179 | |
| 180 | /* Update GICC. Mark parking protocol as available. */ |
| 181 | gicc->parking_proto = ACPI_PP_VERSION; |
| 182 | gicc->parked_addr = virt_to_phys(page); |
| 183 | |
| 184 | /* Prepare parking protocol page */ |
| 185 | memset(page, '\0', sizeof(struct acpi_pp_page)); |
| 186 | |
| 187 | /* Init mailbox. Set MPIDR so core's will find their page. */ |
| 188 | page->cpu_id = gicc->mpidr; |
| 189 | page->jumping_address = ACPI_PP_JMP_ADR_INVALID; |
| 190 | |
| 191 | /* Relocate spinning code */ |
| 192 | reloc = &page->spinning_code[0]; |
| 193 | |
| 194 | log_debug("Relocating spin table from %lx to %lx (size %x)\n", |
| 195 | (ulong)&acpi_pp_code_start, (ulong)reloc, acpi_pp_code_size); |
| 196 | memcpy(reloc, &acpi_pp_code_start, acpi_pp_code_size); |
| 197 | |
| 198 | if (!CONFIG_IS_ENABLED(SYS_DCACHE_OFF)) |
| 199 | flush_dcache_range((unsigned long)page, |
| 200 | (unsigned long)(page + 1)); |
| 201 | } |
| 202 | |
| 203 | void acpi_write_park(struct acpi_madt *madt) |
| 204 | { |
| 205 | struct acpi_pp_page *start, *page; |
| 206 | struct acpi_madt_gicc *gicc; |
| 207 | int ret, i, ncpus = 0; |
| 208 | |
| 209 | /* |
| 210 | * According to the "Multi-processor Startup for ARM Platforms": |
| 211 | * - Every CPU as specified by MADT GICC has it's own 4K page |
| 212 | * - Every page is divided into two sections: OS and FW reserved |
| 213 | * - Memory occupied by "Parking Protocol" must be marked 'Reserved' |
| 214 | * - Spinloop code should reside in FW reserved 2048 bytes |
| 215 | * - Spinloop code will check the mailbox in OS reserved area |
| 216 | */ |
| 217 | |
| 218 | if (acpi_pp_code_size > sizeof(page->spinning_code)) { |
| 219 | log_err("Spinning code too big to fit: %d\n", |
| 220 | acpi_pp_code_size); |
| 221 | return; |
| 222 | } |
| 223 | |
| 224 | /* Count all MADT GICCs including BSP */ |
| 225 | for (i = sizeof(struct acpi_madt); i < madt->header.length; |
| 226 | i += gicc->length) { |
| 227 | gicc = (struct acpi_madt_gicc *)((void *)madt + i); |
| 228 | if (gicc->type != ACPI_APIC_GICC) |
| 229 | continue; |
| 230 | ncpus++; |
| 231 | } |
| 232 | log_debug("Found %#x GICCs in MADT\n", ncpus); |
| 233 | |
| 234 | /* Allocate pages linearly due to assembly code requirements */ |
| 235 | start = bloblist_add(BLOBLISTT_ACPI_PP, ACPI_PP_PAGE_SIZE * ncpus, |
| 236 | ilog2(SZ_4K)); |
| 237 | if (!start) { |
| 238 | log_err("Failed to allocate memory for ACPI-parking-protocol pages\n"); |
| 239 | return; |
| 240 | } |
| 241 | log_debug("Allocated parking protocol at %p\n", start); |
| 242 | page = start; |
| 243 | |
| 244 | if (IS_ENABLED(CONFIG_EFI_LOADER)) { |
| 245 | /* Default mapping is 'BOOT CODE'. Mark as reserved instead. */ |
| 246 | ret = efi_add_memory_map((u64)(uintptr_t)start, |
| 247 | ncpus * ACPI_PP_PAGE_SIZE, |
| 248 | EFI_RESERVED_MEMORY_TYPE); |
| 249 | |
| 250 | if (ret) |
| 251 | log_err("Reserved memory mapping failed addr %p size %x\n", |
| 252 | start, ncpus * ACPI_PP_PAGE_SIZE); |
| 253 | } |
| 254 | |
| 255 | /* Prepare the parking protocol pages */ |
| 256 | for (i = sizeof(struct acpi_madt); i < madt->header.length; |
| 257 | i += gicc->length) { |
| 258 | gicc = (struct acpi_madt_gicc *)((void *)madt + i); |
| 259 | if (gicc->type != ACPI_APIC_GICC) |
| 260 | continue; |
| 261 | |
| 262 | acpi_write_pp_setup_one_page(page++, gicc); |
| 263 | } |
| 264 | |
| 265 | acpi_pp_etables = virt_to_phys(start) + |
| 266 | ACPI_PP_PAGE_SIZE * ncpus; |
| 267 | acpi_pp_tables = virt_to_phys(start); |
| 268 | |
| 269 | /* Make sure other cores see written value in memory */ |
| 270 | if (!CONFIG_IS_ENABLED(SYS_DCACHE_OFF)) |
| 271 | flush_dcache_all(); |
| 272 | |
| 273 | /* Send an event to wake up the secondary CPU. */ |
| 274 | asm("dsb ishst\n" |
| 275 | "sev"); |
| 276 | } |