]>
Commit | Line | Data |
---|---|---|
867bcb63 SS |
1 | /* |
2 | * Based on acpi.c from coreboot | |
3 | * | |
4 | * Copyright (C) 2015, Saket Sinha <saket.sinha89@gmail.com> | |
ab5efd57 | 5 | * Copyright (C) 2016, Bin Meng <bmeng.cn@gmail.com> |
867bcb63 SS |
6 | * |
7 | * SPDX-License-Identifier: GPL-2.0+ | |
8 | */ | |
9 | ||
10 | #include <common.h> | |
11 | #include <cpu.h> | |
12 | #include <dm.h> | |
13 | #include <dm/uclass-internal.h> | |
684c4cd0 | 14 | #include <version.h> |
79c2c257 | 15 | #include <asm/acpi/global_nvs.h> |
867bcb63 | 16 | #include <asm/acpi_table.h> |
6aef68dc | 17 | #include <asm/io.h> |
b156da91 | 18 | #include <asm/ioapic.h> |
867bcb63 | 19 | #include <asm/lapic.h> |
b156da91 | 20 | #include <asm/mpspec.h> |
867bcb63 | 21 | #include <asm/tables.h> |
79c2c257 | 22 | #include <asm/arch/global_nvs.h> |
867bcb63 SS |
23 | |
24 | /* | |
dfbb18bc BM |
25 | * IASL compiles the dsdt entries and writes the hex values |
26 | * to a C array AmlCode[] (see dsdt.c). | |
867bcb63 SS |
27 | */ |
28 | extern const unsigned char AmlCode[]; | |
29 | ||
3469bf42 | 30 | /* ACPI RSDP address to be used in boot parameters */ |
45410da7 | 31 | static ulong acpi_rsdp_addr; |
3469bf42 | 32 | |
ab5efd57 BM |
33 | static void acpi_write_rsdp(struct acpi_rsdp *rsdp, struct acpi_rsdt *rsdt, |
34 | struct acpi_xsdt *xsdt) | |
35 | { | |
36 | memset(rsdp, 0, sizeof(struct acpi_rsdp)); | |
37 | ||
38 | memcpy(rsdp->signature, RSDP_SIG, 8); | |
39 | memcpy(rsdp->oem_id, OEM_ID, 6); | |
40 | ||
41 | rsdp->length = sizeof(struct acpi_rsdp); | |
42 | rsdp->rsdt_address = (u32)rsdt; | |
43 | ||
44 | /* | |
45 | * Revision: ACPI 1.0: 0, ACPI 2.0/3.0/4.0: 2 | |
46 | * | |
47 | * Some OSes expect an XSDT to be present for RSD PTR revisions >= 2. | |
48 | * If we don't have an ACPI XSDT, force ACPI 1.0 (and thus RSD PTR | |
49 | * revision 0) | |
50 | */ | |
51 | if (xsdt == NULL) { | |
52 | rsdp->revision = ACPI_RSDP_REV_ACPI_1_0; | |
53 | } else { | |
54 | rsdp->xsdt_address = (u64)(u32)xsdt; | |
55 | rsdp->revision = ACPI_RSDP_REV_ACPI_2_0; | |
56 | } | |
57 | ||
58 | /* Calculate checksums */ | |
59 | rsdp->checksum = table_compute_checksum((void *)rsdp, 20); | |
60 | rsdp->ext_checksum = table_compute_checksum((void *)rsdp, | |
61 | sizeof(struct acpi_rsdp)); | |
62 | } | |
63 | ||
64 | void acpi_fill_header(struct acpi_table_header *header, char *signature) | |
65 | { | |
66 | memcpy(header->signature, signature, 4); | |
67 | memcpy(header->oem_id, OEM_ID, 6); | |
68 | memcpy(header->oem_table_id, OEM_TABLE_ID, 8); | |
684c4cd0 | 69 | header->oem_revision = U_BOOT_BUILD_DATE; |
ab5efd57 BM |
70 | memcpy(header->aslc_id, ASLC_ID, 4); |
71 | } | |
72 | ||
73 | static void acpi_write_rsdt(struct acpi_rsdt *rsdt) | |
74 | { | |
75 | struct acpi_table_header *header = &(rsdt->header); | |
76 | ||
77 | /* Fill out header fields */ | |
78 | acpi_fill_header(header, "RSDT"); | |
79 | header->length = sizeof(struct acpi_rsdt); | |
7e6343ef | 80 | header->revision = 1; |
ab5efd57 BM |
81 | |
82 | /* Entries are filled in later, we come with an empty set */ | |
83 | ||
84 | /* Fix checksum */ | |
85 | header->checksum = table_compute_checksum((void *)rsdt, | |
86 | sizeof(struct acpi_rsdt)); | |
87 | } | |
88 | ||
89 | static void acpi_write_xsdt(struct acpi_xsdt *xsdt) | |
90 | { | |
91 | struct acpi_table_header *header = &(xsdt->header); | |
92 | ||
93 | /* Fill out header fields */ | |
94 | acpi_fill_header(header, "XSDT"); | |
95 | header->length = sizeof(struct acpi_xsdt); | |
7e6343ef | 96 | header->revision = 1; |
ab5efd57 BM |
97 | |
98 | /* Entries are filled in later, we come with an empty set */ | |
99 | ||
100 | /* Fix checksum */ | |
101 | header->checksum = table_compute_checksum((void *)xsdt, | |
102 | sizeof(struct acpi_xsdt)); | |
103 | } | |
104 | ||
867bcb63 | 105 | /** |
ab5efd57 BM |
106 | * Add an ACPI table to the RSDT (and XSDT) structure, recalculate length |
107 | * and checksum. | |
108 | */ | |
867bcb63 SS |
109 | static void acpi_add_table(struct acpi_rsdp *rsdp, void *table) |
110 | { | |
111 | int i, entries_num; | |
112 | struct acpi_rsdt *rsdt; | |
113 | struct acpi_xsdt *xsdt = NULL; | |
114 | ||
115 | /* The RSDT is mandatory while the XSDT is not */ | |
116 | rsdt = (struct acpi_rsdt *)rsdp->rsdt_address; | |
117 | ||
118 | if (rsdp->xsdt_address) | |
119 | xsdt = (struct acpi_xsdt *)((u32)rsdp->xsdt_address); | |
120 | ||
121 | /* This should always be MAX_ACPI_TABLES */ | |
122 | entries_num = ARRAY_SIZE(rsdt->entry); | |
123 | ||
124 | for (i = 0; i < entries_num; i++) { | |
125 | if (rsdt->entry[i] == 0) | |
126 | break; | |
127 | } | |
128 | ||
129 | if (i >= entries_num) { | |
dca4d1a2 | 130 | debug("ACPI: Error: too many tables\n"); |
867bcb63 SS |
131 | return; |
132 | } | |
133 | ||
134 | /* Add table to the RSDT */ | |
135 | rsdt->entry[i] = (u32)table; | |
136 | ||
137 | /* Fix RSDT length or the kernel will assume invalid entries */ | |
8a8c0352 | 138 | rsdt->header.length = sizeof(struct acpi_table_header) + |
ab5efd57 | 139 | (sizeof(u32) * (i + 1)); |
867bcb63 SS |
140 | |
141 | /* Re-calculate checksum */ | |
142 | rsdt->header.checksum = 0; | |
143 | rsdt->header.checksum = table_compute_checksum((u8 *)rsdt, | |
ab5efd57 | 144 | rsdt->header.length); |
867bcb63 SS |
145 | |
146 | /* | |
147 | * And now the same thing for the XSDT. We use the same index as for | |
148 | * now we want the XSDT and RSDT to always be in sync in U-Boot | |
149 | */ | |
150 | if (xsdt) { | |
151 | /* Add table to the XSDT */ | |
152 | xsdt->entry[i] = (u64)(u32)table; | |
153 | ||
154 | /* Fix XSDT length */ | |
8a8c0352 | 155 | xsdt->header.length = sizeof(struct acpi_table_header) + |
ab5efd57 | 156 | (sizeof(u64) * (i + 1)); |
867bcb63 SS |
157 | |
158 | /* Re-calculate checksum */ | |
159 | xsdt->header.checksum = 0; | |
160 | xsdt->header.checksum = table_compute_checksum((u8 *)xsdt, | |
161 | xsdt->header.length); | |
162 | } | |
163 | } | |
164 | ||
ab5efd57 BM |
165 | static void acpi_create_facs(struct acpi_facs *facs) |
166 | { | |
167 | memset((void *)facs, 0, sizeof(struct acpi_facs)); | |
168 | ||
169 | memcpy(facs->signature, "FACS", 4); | |
170 | facs->length = sizeof(struct acpi_facs); | |
171 | facs->hardware_signature = 0; | |
172 | facs->firmware_waking_vector = 0; | |
173 | facs->global_lock = 0; | |
174 | facs->flags = 0; | |
175 | facs->x_firmware_waking_vector_l = 0; | |
176 | facs->x_firmware_waking_vector_h = 0; | |
177 | facs->version = 1; | |
178 | } | |
179 | ||
867bcb63 | 180 | static int acpi_create_madt_lapic(struct acpi_madt_lapic *lapic, |
ab5efd57 | 181 | u8 cpu, u8 apic) |
867bcb63 | 182 | { |
ab5efd57 | 183 | lapic->type = ACPI_APIC_LAPIC; |
867bcb63 | 184 | lapic->length = sizeof(struct acpi_madt_lapic); |
ab5efd57 | 185 | lapic->flags = LOCAL_APIC_FLAG_ENABLED; |
867bcb63 SS |
186 | lapic->processor_id = cpu; |
187 | lapic->apic_id = apic; | |
188 | ||
189 | return lapic->length; | |
190 | } | |
191 | ||
fc4f5ccc | 192 | int acpi_create_madt_lapics(u32 current) |
867bcb63 SS |
193 | { |
194 | struct udevice *dev; | |
8a1a7595 | 195 | int total_length = 0; |
867bcb63 SS |
196 | |
197 | for (uclass_find_first_device(UCLASS_CPU, &dev); | |
198 | dev; | |
199 | uclass_find_next_device(&dev)) { | |
200 | struct cpu_platdata *plat = dev_get_parent_platdata(dev); | |
8a1a7595 GM |
201 | int length = acpi_create_madt_lapic( |
202 | (struct acpi_madt_lapic *)current, | |
203 | plat->cpu_id, plat->cpu_id); | |
fc4f5ccc | 204 | current += length; |
8a1a7595 | 205 | total_length += length; |
ab5efd57 BM |
206 | } |
207 | ||
8a1a7595 | 208 | return total_length; |
867bcb63 SS |
209 | } |
210 | ||
ab5efd57 BM |
211 | int acpi_create_madt_ioapic(struct acpi_madt_ioapic *ioapic, u8 id, |
212 | u32 addr, u32 gsi_base) | |
867bcb63 | 213 | { |
8a8c0352 | 214 | ioapic->type = ACPI_APIC_IOAPIC; |
867bcb63 SS |
215 | ioapic->length = sizeof(struct acpi_madt_ioapic); |
216 | ioapic->reserved = 0x00; | |
217 | ioapic->gsi_base = gsi_base; | |
218 | ioapic->ioapic_id = id; | |
219 | ioapic->ioapic_addr = addr; | |
220 | ||
221 | return ioapic->length; | |
222 | } | |
223 | ||
224 | int acpi_create_madt_irqoverride(struct acpi_madt_irqoverride *irqoverride, | |
ab5efd57 | 225 | u8 bus, u8 source, u32 gsirq, u16 flags) |
867bcb63 | 226 | { |
8a8c0352 | 227 | irqoverride->type = ACPI_APIC_IRQ_SRC_OVERRIDE; |
867bcb63 SS |
228 | irqoverride->length = sizeof(struct acpi_madt_irqoverride); |
229 | irqoverride->bus = bus; | |
230 | irqoverride->source = source; | |
231 | irqoverride->gsirq = gsirq; | |
232 | irqoverride->flags = flags; | |
233 | ||
234 | return irqoverride->length; | |
235 | } | |
236 | ||
237 | int acpi_create_madt_lapic_nmi(struct acpi_madt_lapic_nmi *lapic_nmi, | |
ab5efd57 | 238 | u8 cpu, u16 flags, u8 lint) |
867bcb63 | 239 | { |
8a8c0352 | 240 | lapic_nmi->type = ACPI_APIC_LAPIC_NMI; |
867bcb63 SS |
241 | lapic_nmi->length = sizeof(struct acpi_madt_lapic_nmi); |
242 | lapic_nmi->flags = flags; | |
243 | lapic_nmi->processor_id = cpu; | |
244 | lapic_nmi->lint = lint; | |
245 | ||
246 | return lapic_nmi->length; | |
247 | } | |
248 | ||
b156da91 AS |
249 | static int acpi_create_madt_irq_overrides(u32 current) |
250 | { | |
251 | struct acpi_madt_irqoverride *irqovr; | |
252 | u16 sci_flags = MP_IRQ_TRIGGER_LEVEL | MP_IRQ_POLARITY_HIGH; | |
253 | int length = 0; | |
254 | ||
255 | irqovr = (void *)current; | |
256 | length += acpi_create_madt_irqoverride(irqovr, 0, 0, 2, 0); | |
257 | ||
258 | irqovr = (void *)(current + length); | |
259 | length += acpi_create_madt_irqoverride(irqovr, 0, 9, 9, sci_flags); | |
260 | ||
261 | return length; | |
262 | } | |
263 | ||
264 | __weak u32 acpi_fill_madt(u32 current) | |
265 | { | |
266 | current += acpi_create_madt_lapics(current); | |
267 | ||
268 | current += acpi_create_madt_ioapic((struct acpi_madt_ioapic *)current, | |
269 | io_apic_read(IO_APIC_ID) >> 24, IO_APIC_ADDR, 0); | |
270 | ||
271 | current += acpi_create_madt_irq_overrides(current); | |
272 | ||
273 | return current; | |
274 | } | |
275 | ||
867bcb63 SS |
276 | static void acpi_create_madt(struct acpi_madt *madt) |
277 | { | |
8a8c0352 | 278 | struct acpi_table_header *header = &(madt->header); |
7e79a6bc | 279 | u32 current = (u32)madt + sizeof(struct acpi_madt); |
867bcb63 SS |
280 | |
281 | memset((void *)madt, 0, sizeof(struct acpi_madt)); | |
282 | ||
283 | /* Fill out header fields */ | |
dfbb18bc | 284 | acpi_fill_header(header, "APIC"); |
867bcb63 | 285 | header->length = sizeof(struct acpi_madt); |
7e6343ef | 286 | header->revision = 4; |
867bcb63 SS |
287 | |
288 | madt->lapic_addr = LAPIC_DEFAULT_BASE; | |
8a8c0352 | 289 | madt->flags = ACPI_MADT_PCAT_COMPAT; |
867bcb63 SS |
290 | |
291 | current = acpi_fill_madt(current); | |
292 | ||
293 | /* (Re)calculate length and checksum */ | |
7e79a6bc | 294 | header->length = current - (u32)madt; |
867bcb63 SS |
295 | |
296 | header->checksum = table_compute_checksum((void *)madt, header->length); | |
297 | } | |
298 | ||
ace7762b AS |
299 | int acpi_create_mcfg_mmconfig(struct acpi_mcfg_mmconfig *mmconfig, u32 base, |
300 | u16 seg_nr, u8 start, u8 end) | |
867bcb63 SS |
301 | { |
302 | memset(mmconfig, 0, sizeof(*mmconfig)); | |
8a8c0352 BM |
303 | mmconfig->base_address_l = base; |
304 | mmconfig->base_address_h = 0; | |
867bcb63 SS |
305 | mmconfig->pci_segment_group_number = seg_nr; |
306 | mmconfig->start_bus_number = start; | |
307 | mmconfig->end_bus_number = end; | |
308 | ||
309 | return sizeof(struct acpi_mcfg_mmconfig); | |
310 | } | |
311 | ||
ace7762b | 312 | __weak u32 acpi_fill_mcfg(u32 current) |
867bcb63 SS |
313 | { |
314 | current += acpi_create_mcfg_mmconfig | |
315 | ((struct acpi_mcfg_mmconfig *)current, | |
ab5efd57 | 316 | CONFIG_PCIE_ECAM_BASE, 0x0, 0x0, 255); |
867bcb63 SS |
317 | |
318 | return current; | |
319 | } | |
320 | ||
321 | /* MCFG is defined in the PCI Firmware Specification 3.0 */ | |
322 | static void acpi_create_mcfg(struct acpi_mcfg *mcfg) | |
323 | { | |
8a8c0352 | 324 | struct acpi_table_header *header = &(mcfg->header); |
7e79a6bc | 325 | u32 current = (u32)mcfg + sizeof(struct acpi_mcfg); |
867bcb63 SS |
326 | |
327 | memset((void *)mcfg, 0, sizeof(struct acpi_mcfg)); | |
328 | ||
329 | /* Fill out header fields */ | |
dfbb18bc | 330 | acpi_fill_header(header, "MCFG"); |
867bcb63 | 331 | header->length = sizeof(struct acpi_mcfg); |
7e6343ef | 332 | header->revision = 1; |
867bcb63 SS |
333 | |
334 | current = acpi_fill_mcfg(current); | |
335 | ||
336 | /* (Re)calculate length and checksum */ | |
7e79a6bc | 337 | header->length = current - (u32)mcfg; |
867bcb63 SS |
338 | header->checksum = table_compute_checksum((void *)mcfg, header->length); |
339 | } | |
340 | ||
99572785 | 341 | void enter_acpi_mode(int pm1_cnt) |
6aef68dc | 342 | { |
b208d191 BM |
343 | u16 val = inw(pm1_cnt); |
344 | ||
6aef68dc BM |
345 | /* |
346 | * PM1_CNT register bit0 selects the power management event to be | |
347 | * either an SCI or SMI interrupt. When this bit is set, then power | |
348 | * management events will generate an SCI interrupt. When this bit | |
349 | * is reset power management events will generate an SMI interrupt. | |
350 | * | |
351 | * Per ACPI spec, it is the responsibility of the hardware to set | |
352 | * or reset this bit. OSPM always preserves this bit position. | |
353 | * | |
354 | * U-Boot does not support SMI. And we don't have plan to support | |
355 | * anything running in SMM within U-Boot. To create a legacy-free | |
356 | * system, and expose ourselves to OSPM as working under ACPI mode | |
357 | * already, turn this bit on. | |
358 | */ | |
b208d191 | 359 | outw(val | PM1_CNT_SCI_EN, pm1_cnt); |
6aef68dc BM |
360 | } |
361 | ||
fa287b15 | 362 | /* |
7b36dbde | 363 | * QEMU's version of write_acpi_tables is defined in drivers/misc/qfw.c |
fa287b15 | 364 | */ |
42fd8c19 | 365 | ulong write_acpi_tables(ulong start) |
867bcb63 | 366 | { |
358bb3ff | 367 | u32 current; |
867bcb63 SS |
368 | struct acpi_rsdp *rsdp; |
369 | struct acpi_rsdt *rsdt; | |
370 | struct acpi_xsdt *xsdt; | |
371 | struct acpi_facs *facs; | |
8a8c0352 | 372 | struct acpi_table_header *dsdt; |
867bcb63 SS |
373 | struct acpi_fadt *fadt; |
374 | struct acpi_mcfg *mcfg; | |
375 | struct acpi_madt *madt; | |
79c2c257 | 376 | int i; |
867bcb63 SS |
377 | |
378 | current = start; | |
379 | ||
ab5efd57 | 380 | /* Align ACPI tables to 16 byte */ |
867bcb63 SS |
381 | current = ALIGN(current, 16); |
382 | ||
42fd8c19 | 383 | debug("ACPI: Writing ACPI tables at %lx\n", start); |
867bcb63 SS |
384 | |
385 | /* We need at least an RSDP and an RSDT Table */ | |
386 | rsdp = (struct acpi_rsdp *)current; | |
387 | current += sizeof(struct acpi_rsdp); | |
388 | current = ALIGN(current, 16); | |
389 | rsdt = (struct acpi_rsdt *)current; | |
390 | current += sizeof(struct acpi_rsdt); | |
391 | current = ALIGN(current, 16); | |
392 | xsdt = (struct acpi_xsdt *)current; | |
393 | current += sizeof(struct acpi_xsdt); | |
25e133ec BM |
394 | /* |
395 | * Per ACPI spec, the FACS table address must be aligned to a 64 byte | |
396 | * boundary (Windows checks this, but Linux does not). | |
397 | */ | |
398 | current = ALIGN(current, 64); | |
867bcb63 SS |
399 | |
400 | /* clear all table memory */ | |
401 | memset((void *)start, 0, current - start); | |
402 | ||
403 | acpi_write_rsdp(rsdp, rsdt, xsdt); | |
404 | acpi_write_rsdt(rsdt); | |
405 | acpi_write_xsdt(xsdt); | |
406 | ||
407 | debug("ACPI: * FACS\n"); | |
408 | facs = (struct acpi_facs *)current; | |
409 | current += sizeof(struct acpi_facs); | |
410 | current = ALIGN(current, 16); | |
411 | ||
412 | acpi_create_facs(facs); | |
413 | ||
414 | debug("ACPI: * DSDT\n"); | |
8a8c0352 BM |
415 | dsdt = (struct acpi_table_header *)current; |
416 | memcpy(dsdt, &AmlCode, sizeof(struct acpi_table_header)); | |
10fcabed BM |
417 | current += sizeof(struct acpi_table_header); |
418 | memcpy((char *)current, | |
419 | (char *)&AmlCode + sizeof(struct acpi_table_header), | |
420 | dsdt->length - sizeof(struct acpi_table_header)); | |
421 | current += dsdt->length - sizeof(struct acpi_table_header); | |
867bcb63 SS |
422 | current = ALIGN(current, 16); |
423 | ||
79c2c257 BM |
424 | /* Pack GNVS into the ACPI table area */ |
425 | for (i = 0; i < dsdt->length; i++) { | |
426 | u32 *gnvs = (u32 *)((u32)dsdt + i); | |
427 | if (*gnvs == ACPI_GNVS_ADDR) { | |
428 | debug("Fix up global NVS in DSDT to 0x%08x\n", current); | |
429 | *gnvs = current; | |
430 | break; | |
431 | } | |
432 | } | |
433 | ||
434 | /* Update DSDT checksum since we patched the GNVS address */ | |
435 | dsdt->checksum = 0; | |
436 | dsdt->checksum = table_compute_checksum((void *)dsdt, dsdt->length); | |
437 | ||
438 | /* Fill in platform-specific global NVS variables */ | |
439 | acpi_create_gnvs((struct acpi_global_nvs *)current); | |
440 | current += sizeof(struct acpi_global_nvs); | |
441 | current = ALIGN(current, 16); | |
442 | ||
867bcb63 SS |
443 | debug("ACPI: * FADT\n"); |
444 | fadt = (struct acpi_fadt *)current; | |
445 | current += sizeof(struct acpi_fadt); | |
446 | current = ALIGN(current, 16); | |
447 | acpi_create_fadt(fadt, facs, dsdt); | |
448 | acpi_add_table(rsdp, fadt); | |
449 | ||
867bcb63 SS |
450 | debug("ACPI: * MADT\n"); |
451 | madt = (struct acpi_madt *)current; | |
452 | acpi_create_madt(madt); | |
10fcabed BM |
453 | current += madt->header.length; |
454 | acpi_add_table(rsdp, madt); | |
867bcb63 SS |
455 | current = ALIGN(current, 16); |
456 | ||
ab5efd57 BM |
457 | debug("ACPI: * MCFG\n"); |
458 | mcfg = (struct acpi_mcfg *)current; | |
459 | acpi_create_mcfg(mcfg); | |
10fcabed BM |
460 | current += mcfg->header.length; |
461 | acpi_add_table(rsdp, mcfg); | |
462 | current = ALIGN(current, 16); | |
ab5efd57 | 463 | |
dca4d1a2 | 464 | debug("current = %x\n", current); |
867bcb63 | 465 | |
3469bf42 | 466 | acpi_rsdp_addr = (unsigned long)rsdp; |
dca4d1a2 | 467 | debug("ACPI: done\n"); |
867bcb63 | 468 | |
382fabb2 AS |
469 | /* Don't touch ACPI hardware on HW reduced platforms */ |
470 | if (fadt->flags & ACPI_FADT_HW_REDUCED_ACPI) | |
471 | return current; | |
472 | ||
6aef68dc BM |
473 | /* |
474 | * Other than waiting for OSPM to request us to switch to ACPI mode, | |
475 | * do it by ourselves, since SMI will not be triggered. | |
476 | */ | |
477 | enter_acpi_mode(fadt->pm1a_cnt_blk); | |
478 | ||
867bcb63 SS |
479 | return current; |
480 | } | |
e76bf38f | 481 | |
45410da7 BM |
482 | ulong acpi_get_rsdp_addr(void) |
483 | { | |
484 | return acpi_rsdp_addr; | |
485 | } | |
486 | ||
e76bf38f BM |
487 | static struct acpi_rsdp *acpi_valid_rsdp(struct acpi_rsdp *rsdp) |
488 | { | |
489 | if (strncmp((char *)rsdp, RSDP_SIG, sizeof(RSDP_SIG) - 1) != 0) | |
490 | return NULL; | |
491 | ||
492 | debug("Looking on %p for valid checksum\n", rsdp); | |
493 | ||
494 | if (table_compute_checksum((void *)rsdp, 20) != 0) | |
495 | return NULL; | |
496 | debug("acpi rsdp checksum 1 passed\n"); | |
497 | ||
498 | if ((rsdp->revision > 1) && | |
499 | (table_compute_checksum((void *)rsdp, rsdp->length) != 0)) | |
500 | return NULL; | |
501 | debug("acpi rsdp checksum 2 passed\n"); | |
502 | ||
503 | return rsdp; | |
504 | } | |
505 | ||
0f4e2588 | 506 | struct acpi_fadt *acpi_find_fadt(void) |
e76bf38f BM |
507 | { |
508 | char *p, *end; | |
509 | struct acpi_rsdp *rsdp = NULL; | |
510 | struct acpi_rsdt *rsdt; | |
511 | struct acpi_fadt *fadt = NULL; | |
e76bf38f BM |
512 | int i; |
513 | ||
e76bf38f BM |
514 | /* Find RSDP */ |
515 | for (p = (char *)ROM_TABLE_ADDR; p < (char *)ROM_TABLE_END; p += 16) { | |
516 | rsdp = acpi_valid_rsdp((struct acpi_rsdp *)p); | |
517 | if (rsdp) | |
518 | break; | |
519 | } | |
520 | ||
521 | if (rsdp == NULL) | |
522 | return NULL; | |
523 | ||
524 | debug("RSDP found at %p\n", rsdp); | |
525 | rsdt = (struct acpi_rsdt *)rsdp->rsdt_address; | |
526 | ||
527 | end = (char *)rsdt + rsdt->header.length; | |
528 | debug("RSDT found at %p ends at %p\n", rsdt, end); | |
529 | ||
530 | for (i = 0; ((char *)&rsdt->entry[i]) < end; i++) { | |
531 | fadt = (struct acpi_fadt *)rsdt->entry[i]; | |
532 | if (strncmp((char *)fadt, "FACP", 4) == 0) | |
533 | break; | |
534 | fadt = NULL; | |
535 | } | |
536 | ||
537 | if (fadt == NULL) | |
538 | return NULL; | |
539 | ||
540 | debug("FADT found at %p\n", fadt); | |
0f4e2588 BM |
541 | return fadt; |
542 | } | |
543 | ||
544 | void *acpi_find_wakeup_vector(struct acpi_fadt *fadt) | |
545 | { | |
546 | struct acpi_facs *facs; | |
547 | void *wake_vec; | |
548 | ||
549 | debug("Trying to find the wakeup vector...\n"); | |
550 | ||
e76bf38f BM |
551 | facs = (struct acpi_facs *)fadt->firmware_ctrl; |
552 | ||
553 | if (facs == NULL) { | |
554 | debug("No FACS found, wake up from S3 not possible.\n"); | |
555 | return NULL; | |
556 | } | |
557 | ||
558 | debug("FACS found at %p\n", facs); | |
559 | wake_vec = (void *)facs->firmware_waking_vector; | |
560 | debug("OS waking vector is %p\n", wake_vec); | |
561 | ||
562 | return wake_vec; | |
563 | } |