blob: b8e7e0d033ab8e27dfe87f9a76916a0af3c54708 [file] [log] [blame]
Tim Wawrzynczak64246482021-06-25 22:44:45 -06001/* SPDX-License-Identifier: GPL-2.0-or-later */
2
3#include <acpi/acpigen.h>
4#include <console/console.h>
5#include <intelblocks/acpi.h>
Tim Wawrzynczak2eb100d2021-07-01 08:20:17 -06006#include <intelblocks/pmc_ipc.h>
7#include <stdlib.h>
Tim Wawrzynczak64246482021-06-25 22:44:45 -06008#include <types.h>
9
10#define LPI_S0_HELPER_UUID "c4eb40a0-6cd2-11e2-bcfd-0800200c9a66"
Tim Wawrzynczak2eb100d2021-07-01 08:20:17 -060011#define PEP_S0IX_UUID "57a6512e-3979-4e9d-9708-ff13b2508972"
Tim Wawrzynczak64246482021-06-25 22:44:45 -060012#define SYSTEM_POWER_MANAGEMENT_HID "INT33A1"
13#define SYSTEM_POWER_MANAGEMENT_CID "PNP0D80"
14#define EC_S0IX_HOOK "\\_SB.PCI0.LPCB.EC0.S0IX"
15#define MAINBOARD_HOOK "\\_SB.MS0X"
16#define ENABLE_PM_BITS_HOOK "\\_SB.PCI0.EGPM"
17#define RESTORE_PM_BITS_HOOK "\\_SB.PCI0.RGPM"
18#define LPI_STATES_ALL 0xff
19#define MIN_DEVICE_STATE ACPI_DEVICE_SLEEP_D0
20#define PEPD_SCOPE "\\_SB.PCI0"
21
Tim Wawrzynczak2eb100d2021-07-01 08:20:17 -060022struct reg_info {
23 uint8_t *addr;
24 size_t buffer_size;
25};
26
27static void read_pmc_lpm_requirements(const struct soc_pmc_lpm *lpm,
28 struct reg_info *info)
29{
30 if (!CONFIG(SOC_INTEL_COMMON_BLOCK_ACPI_PEP_LPM_REQ) || !lpm) {
31 memset(info, 0, sizeof(*info));
32 return;
33 }
34
35 const size_t register_count = lpm->num_substates * lpm->num_req_regs;
36 uint32_t *reg = calloc(register_count, sizeof(uint32_t));
37
38 /* Read the various LPM state requirement registers from the PMC */
39 for (size_t i = 0; i < lpm->num_substates; i++) {
40 if (!(lpm->lpm_enable_mask & BIT(i)))
41 continue;
42
43 for (size_t j = 0; j < lpm->num_req_regs; j++) {
44 const uint32_t offset = lpm->lpm_ipc_offset +
45 i * lpm->req_reg_stride +
46 j * sizeof(uint32_t);
47 const uint32_t cmd_reg = pmc_make_ipc_cmd(PMC_IPC_CMD_RD_PMC_REG, 0, 0);
48 struct pmc_ipc_buffer req = {.buf[0] = offset};
49 struct pmc_ipc_buffer res = {};
50
51 enum cb_err result = pmc_send_ipc_cmd(cmd_reg, &req, &res);
52 if (result != CB_SUCCESS) {
53 printk(BIOS_ERR, "Failed to retrieve LPM substate registers"
54 "from LPM, substate %lu, reg %lu\n", i, j);
55 free(reg);
56 return;
57 }
58
59 uint32_t *ptr = reg + i * lpm->num_req_regs + j;
60 *ptr = res.buf[0];
61 }
62 }
63
64 if (info) {
65 info->addr = (uint8_t *)reg;
66 info->buffer_size = register_count * sizeof(uint32_t);
67 }
68}
69
Tim Wawrzynczak64246482021-06-25 22:44:45 -060070/*
71 * For now there is only one disabled non-existent device, because Windows
72 * expects at least one device and crashes without it with a bluescreen
73 * (`INTERNAL_POWER_ERROR`). Returning an empty package does not work.
74 */
75static void lpi_get_constraints(void *unused)
76{
77 /*
78 * Return (Package() {
79 * Package() { "\NULL", 0,
80 * Package() { 0,
81 * Package() { 0xff, 0 }}}})
82 */
83 acpigen_emit_byte(RETURN_OP);
84 acpigen_write_package(1);
85 {
86 acpigen_write_package(3);
87 {
88 acpigen_emit_namestring("\\NULL");
89 acpigen_write_integer(0); /* device disabled */
90 acpigen_write_package(2);
91 {
92 acpigen_write_integer(0); /* revision */
93 acpigen_write_package(2);
94 {
95 acpigen_write_integer(LPI_STATES_ALL);
96 acpigen_write_integer(MIN_DEVICE_STATE);
97 }
98 acpigen_write_package_end();
99 }
100 acpigen_write_package_end();
101 }
102 acpigen_write_package_end();
103 }
104 acpigen_write_package_end();
105}
106
107static void lpi_s0ix_entry(void *unused)
108{
109 /* Inform the EC */
110 acpigen_write_if_cond_ref_of(EC_S0IX_HOOK);
111 acpigen_emit_namestring(EC_S0IX_HOOK);
112 acpigen_write_integer(1);
113 acpigen_write_if_end();
114
115 /* Provide a board level S0ix hook */
116 acpigen_write_if_cond_ref_of(MAINBOARD_HOOK);
117 acpigen_emit_namestring(MAINBOARD_HOOK);
118 acpigen_write_integer(1);
119 acpigen_write_if_end();
120
121 /* Save the current PM bits then enable GPIO PM with
122 MISCCFG_GPIO_PM_CONFIG_BITS */
123 acpigen_write_if_cond_ref_of(ENABLE_PM_BITS_HOOK);
124 acpigen_emit_namestring(ENABLE_PM_BITS_HOOK);
125 acpigen_write_if_end();
126}
127
128static void lpi_s0ix_exit(void *unused)
129{
130 /* Inform the EC */
131 acpigen_write_if_cond_ref_of(EC_S0IX_HOOK);
132 acpigen_emit_namestring(EC_S0IX_HOOK);
133 acpigen_write_integer(0);
134 acpigen_write_if_end();
135
136 /* Provide a board level S0ix hook */
137 acpigen_write_if_cond_ref_of(MAINBOARD_HOOK);
138 acpigen_emit_namestring(MAINBOARD_HOOK);
139 acpigen_write_integer(0);
140 acpigen_write_if_end();
141
142 /* Restore GPIO all Community PM */
143 acpigen_write_if_cond_ref_of(RESTORE_PM_BITS_HOOK);
144 acpigen_emit_namestring(RESTORE_PM_BITS_HOOK);
145 acpigen_write_if_end();
146}
147
148static void (*lpi_s0_helpers[])(void *) = {
149 NULL, /* enumerate functions (autogenerated) */
150 lpi_get_constraints, /* get device constraints */
151 NULL, /* get crash dump device */
152 NULL, /* display off notify */
153 NULL, /* display on notify */
154 lpi_s0ix_entry, /* s0ix entry */
155 lpi_s0ix_exit, /* s0ix exit */
156};
157
Tim Wawrzynczak2eb100d2021-07-01 08:20:17 -0600158static void pep_s0ix_return_lpm_requirements(void *arg)
Tim Wawrzynczak64246482021-06-25 22:44:45 -0600159{
Tim Wawrzynczak2eb100d2021-07-01 08:20:17 -0600160 if (!CONFIG(SOC_INTEL_COMMON_BLOCK_ACPI_PEP_LPM_REQ)) {
161 acpigen_write_return_singleton_buffer(0x0);
162 return;
163 }
164
165 struct reg_info *info = (struct reg_info *)arg;
166 acpigen_write_return_byte_buffer(info->addr, info->buffer_size);
167}
168
169static void (*pep_s0ix[])(void *) = {
170 NULL, /* enumerate functions (autogenerated) */
171 pep_s0ix_return_lpm_requirements, /* Return LPM requirements */
172};
173
174void generate_acpi_power_engine_with_lpm(const struct soc_pmc_lpm *lpm)
175{
176 struct reg_info info;
177 size_t uuid_count = 1;
178 struct dsm_uuid ids[] = {
179 DSM_UUID(LPI_S0_HELPER_UUID, lpi_s0_helpers, ARRAY_SIZE(lpi_s0_helpers), NULL),
180 DSM_UUID(PEP_S0IX_UUID, pep_s0ix, ARRAY_SIZE(pep_s0ix), &info),
181 };
182
Tim Wawrzynczak64246482021-06-25 22:44:45 -0600183 acpigen_write_scope(PEPD_SCOPE);
184 acpigen_write_device("PEPD");
185
186 acpigen_write_name_string("_HID", SYSTEM_POWER_MANAGEMENT_HID);
187 acpigen_write_name("_CID");
188 acpigen_emit_eisaid(SYSTEM_POWER_MANAGEMENT_CID);
189
Tim Wawrzynczak2eb100d2021-07-01 08:20:17 -0600190 read_pmc_lpm_requirements(lpm, &info);
191 if (info.buffer_size)
192 uuid_count++;
Tim Wawrzynczak64246482021-06-25 22:44:45 -0600193
Tim Wawrzynczak2eb100d2021-07-01 08:20:17 -0600194 acpigen_write_dsm_uuid_arr(ids, uuid_count);
Tim Wawrzynczak64246482021-06-25 22:44:45 -0600195 acpigen_write_device_end();
196 acpigen_write_scope_end();
197
Tim Wawrzynczak2eb100d2021-07-01 08:20:17 -0600198 free(info.addr);
Tim Wawrzynczak64246482021-06-25 22:44:45 -0600199 printk(BIOS_INFO, PEPD_SCOPE ".PEPD: Intel Power Engine Plug-in\n");
200}
Tim Wawrzynczak2eb100d2021-07-01 08:20:17 -0600201
202void generate_acpi_power_engine(void)
203{
204 generate_acpi_power_engine_with_lpm(NULL);
205}