1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com> 4 */ 5 6 #include <common.h> 7 #include <asm/acpi_table.h> 8 #include <asm/io.h> 9 #include <asm/tables.h> 10 11 static struct acpi_rsdp *acpi_valid_rsdp(struct acpi_rsdp *rsdp) 12 { 13 if (strncmp((char *)rsdp, RSDP_SIG, sizeof(RSDP_SIG) - 1) != 0) 14 return NULL; 15 16 debug("Looking on %p for valid checksum\n", rsdp); 17 18 if (table_compute_checksum((void *)rsdp, 20) != 0) 19 return NULL; 20 debug("acpi rsdp checksum 1 passed\n"); 21 22 if ((rsdp->revision > 1) && 23 (table_compute_checksum((void *)rsdp, rsdp->length) != 0)) 24 return NULL; 25 debug("acpi rsdp checksum 2 passed\n"); 26 27 return rsdp; 28 } 29 30 struct acpi_fadt *acpi_find_fadt(void) 31 { 32 char *p, *end; 33 struct acpi_rsdp *rsdp = NULL; 34 struct acpi_rsdt *rsdt; 35 struct acpi_fadt *fadt = NULL; 36 int i; 37 38 /* Find RSDP */ 39 for (p = (char *)ROM_TABLE_ADDR; p < (char *)ROM_TABLE_END; p += 16) { 40 rsdp = acpi_valid_rsdp((struct acpi_rsdp *)p); 41 if (rsdp) 42 break; 43 } 44 45 if (!rsdp) 46 return NULL; 47 48 debug("RSDP found at %p\n", rsdp); 49 rsdt = (struct acpi_rsdt *)(uintptr_t)rsdp->rsdt_address; 50 51 end = (char *)rsdt + rsdt->header.length; 52 debug("RSDT found at %p ends at %p\n", rsdt, end); 53 54 for (i = 0; ((char *)&rsdt->entry[i]) < end; i++) { 55 fadt = (struct acpi_fadt *)(uintptr_t)rsdt->entry[i]; 56 if (strncmp((char *)fadt, "FACP", 4) == 0) 57 break; 58 fadt = NULL; 59 } 60 61 if (!fadt) 62 return NULL; 63 64 debug("FADT found at %p\n", fadt); 65 return fadt; 66 } 67 68 void *acpi_find_wakeup_vector(struct acpi_fadt *fadt) 69 { 70 struct acpi_facs *facs; 71 void *wake_vec; 72 73 debug("Trying to find the wakeup vector...\n"); 74 75 facs = (struct acpi_facs *)(uintptr_t)fadt->firmware_ctrl; 76 77 if (!facs) { 78 debug("No FACS found, wake up from S3 not possible.\n"); 79 return NULL; 80 } 81 82 debug("FACS found at %p\n", facs); 83 wake_vec = (void *)(uintptr_t)facs->firmware_waking_vector; 84 debug("OS waking vector is %p\n", wake_vec); 85 86 return wake_vec; 87 } 88 89 void enter_acpi_mode(int pm1_cnt) 90 { 91 u16 val = inw(pm1_cnt); 92 93 /* 94 * PM1_CNT register bit0 selects the power management event to be 95 * either an SCI or SMI interrupt. When this bit is set, then power 96 * management events will generate an SCI interrupt. When this bit 97 * is reset power management events will generate an SMI interrupt. 98 * 99 * Per ACPI spec, it is the responsibility of the hardware to set 100 * or reset this bit. OSPM always preserves this bit position. 101 * 102 * U-Boot does not support SMI. And we don't have plan to support 103 * anything running in SMM within U-Boot. To create a legacy-free 104 * system, and expose ourselves to OSPM as working under ACPI mode 105 * already, turn this bit on. 106 */ 107 outw(val | PM1_CNT_SCI_EN, pm1_cnt); 108 } 109