xref: /openbmc/u-boot/arch/x86/lib/acpi.c (revision afaea1f5)
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