blob: da78d5c424dfedb89b17301fa4ece9b6116c5745 [file] [log] [blame]
Angel Pons0612b272020-04-05 15:46:56 +02001/* SPDX-License-Identifier: GPL-2.0-only */
Shaunak Saha9dffbdd2017-03-08 19:27:17 -08002
Kyösti Mälkki27872372021-01-21 16:05:26 +02003#include <acpi/acpi_pm.h>
Shaunak Saha9dffbdd2017-03-08 19:27:17 -08004#include <arch/io.h>
Lean Sheng Tan508dc162021-06-16 01:32:22 -07005#include <assert.h>
Bill XIE516c0a52020-02-24 23:08:35 +08006#include <bootmode.h>
Kyösti Mälkki13f66502019-03-03 08:01:05 +02007#include <device/mmio.h>
Shaunak Saha9dffbdd2017-03-08 19:27:17 -08008#include <cbmem.h>
Arthur Heymans08c646c2020-11-19 13:56:41 +01009#include <cpu/x86/smm.h>
Shaunak Saha9dffbdd2017-03-08 19:27:17 -080010#include <console/console.h>
11#include <halt.h>
12#include <intelblocks/pmclib.h>
13#include <intelblocks/gpio.h>
Subrata Banik7bc4dc52018-05-17 18:40:32 +053014#include <intelblocks/tco.h>
Kyösti Mälkkicbf95712020-01-05 08:05:45 +020015#include <option.h>
Bill XIE516c0a52020-02-24 23:08:35 +080016#include <security/vboot/vboot_common.h>
Shaunak Saha9dffbdd2017-03-08 19:27:17 -080017#include <soc/pm.h>
Nico Huber6bbabef2019-08-05 21:24:00 +020018#include <stdint.h>
Shaunak Sahaf0738722017-10-02 15:01:33 -070019#include <string.h>
Shaunak Saha9dffbdd2017-03-08 19:27:17 -080020#include <timer.h>
Shaunak Saha9dffbdd2017-03-08 19:27:17 -080021
Arthur Heymansea6dd742019-05-25 10:32:31 +020022static struct chipset_power_state power_state;
Shaunak Sahaf0738722017-10-02 15:01:33 -070023
V Sowmya186250f2020-09-02 16:40:24 +053024/* List of Minimum Assertion durations in microseconds */
25enum min_assert_dur {
26 MinAssertDur0s = 0,
27 MinAssertDur60us = 60,
28 MinAssertDur1ms = 1000,
29 MinAssertDur50ms = 50000,
30 MinAssertDur98ms = 98000,
31 MinAssertDur500ms = 500000,
32 MinAssertDur1s = 1000000,
33 MinAssertDur2s = 2000000,
34 MinAssertDur3s = 3000000,
35 MinAssertDur4s = 4000000,
36};
37
38/* Signal Assertion duration values */
39struct cfg_assert_dur {
40 /* Minimum assertion duration of SLP_A signal */
41 enum min_assert_dur slp_a;
42
43 /* Minimum assertion duration of SLP_4 signal */
44 enum min_assert_dur slp_s4;
45
46 /* Minimum assertion duration of SLP_3 signal */
47 enum min_assert_dur slp_s3;
48
49 /* PCH PM Power Cycle duration */
50 enum min_assert_dur pm_pwr_cyc_dur;
51};
52
53/* Default value of PchPmPwrCycDur */
54#define PCH_PM_PWR_CYC_DUR 0
55
Shaunak Sahaf0738722017-10-02 15:01:33 -070056struct chipset_power_state *pmc_get_power_state(void)
57{
58 struct chipset_power_state *ptr = NULL;
59
60 if (cbmem_possibly_online())
Kyösti Mälkki27872372021-01-21 16:05:26 +020061 ptr = acpi_get_pm_state();
Shaunak Sahaf0738722017-10-02 15:01:33 -070062
63 /* cbmem is online but ptr is not populated yet */
64 if (ptr == NULL && !(ENV_RAMSTAGE || ENV_POSTCAR))
Arthur Heymansea6dd742019-05-25 10:32:31 +020065 return &power_state;
Shaunak Sahaf0738722017-10-02 15:01:33 -070066
67 return ptr;
68}
69
70static void migrate_power_state(int is_recovery)
71{
72 struct chipset_power_state *ps_cbmem;
Shaunak Sahaf0738722017-10-02 15:01:33 -070073
Shaunak Sahaf0738722017-10-02 15:01:33 -070074 ps_cbmem = cbmem_add(CBMEM_ID_POWER_STATE, sizeof(*ps_cbmem));
75
76 if (ps_cbmem == NULL) {
77 printk(BIOS_DEBUG, "Not adding power state to cbmem!\n");
78 return;
79 }
Arthur Heymansea6dd742019-05-25 10:32:31 +020080 memcpy(ps_cbmem, &power_state, sizeof(*ps_cbmem));
Shaunak Sahaf0738722017-10-02 15:01:33 -070081}
82ROMSTAGE_CBMEM_INIT_HOOK(migrate_power_state)
83
Shaunak Saha9dffbdd2017-03-08 19:27:17 -080084static void print_num_status_bits(int num_bits, uint32_t status,
85 const char *const bit_names[])
86{
87 int i;
88
89 if (!status)
90 return;
91
92 for (i = num_bits - 1; i >= 0; i--) {
93 if (status & (1 << i)) {
94 if (bit_names[i])
95 printk(BIOS_DEBUG, "%s ", bit_names[i]);
96 else
97 printk(BIOS_DEBUG, "BIT%d ", i);
98 }
99 }
100}
101
Aaron Durbin64031672018-04-21 14:45:32 -0600102__weak uint32_t soc_get_smi_status(uint32_t generic_sts)
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800103{
104 return generic_sts;
105}
106
Bora Guvendik3cb0e272018-09-28 16:19:00 -0700107int acpi_get_sleep_type(void)
108{
109 struct chipset_power_state *ps;
John Zhao0ccfc0c2018-10-16 10:48:00 -0700110 int prev_sleep_state = ACPI_S0;
Bora Guvendik3cb0e272018-09-28 16:19:00 -0700111
112 ps = pmc_get_power_state();
John Zhao0ccfc0c2018-10-16 10:48:00 -0700113 if (ps)
114 prev_sleep_state = ps->prev_sleep_state;
115
116 return prev_sleep_state;
Bora Guvendik3cb0e272018-09-28 16:19:00 -0700117}
118
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800119static uint32_t pmc_reset_smi_status(void)
120{
121 uint32_t smi_sts = inl(ACPI_BASE_ADDRESS + SMI_STS);
122 outl(smi_sts, ACPI_BASE_ADDRESS + SMI_STS);
123
124 return soc_get_smi_status(smi_sts);
125}
126
127static uint32_t print_smi_status(uint32_t smi_sts)
128{
129 size_t array_size;
130 const char *const *smi_arr;
131
132 if (!smi_sts)
133 return 0;
134
135 printk(BIOS_DEBUG, "SMI_STS: ");
136
137 smi_arr = soc_smi_sts_array(&array_size);
138
139 print_num_status_bits(array_size, smi_sts, smi_arr);
140 printk(BIOS_DEBUG, "\n");
141
142 return smi_sts;
143}
144
Shaunak Saha25cc76f2017-09-28 15:13:05 -0700145/*
146 * Update supplied events in PM1_EN register. This does not disable any already
147 * set events.
148 */
149void pmc_update_pm1_enable(u16 events)
150{
151 u16 pm1_en = pmc_read_pm1_enable();
152 pm1_en |= events;
153 pmc_enable_pm1(pm1_en);
154}
155
156/* Read events set in PM1_EN register. */
157uint16_t pmc_read_pm1_enable(void)
158{
159 return inw(ACPI_BASE_ADDRESS + PM1_EN);
160}
161
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800162uint32_t pmc_clear_smi_status(void)
163{
164 uint32_t sts = pmc_reset_smi_status();
165
166 return print_smi_status(sts);
167}
168
169uint32_t pmc_get_smi_en(void)
170{
171 return inl(ACPI_BASE_ADDRESS + SMI_EN);
172}
173
174void pmc_enable_smi(uint32_t mask)
175{
176 uint32_t smi_en = inl(ACPI_BASE_ADDRESS + SMI_EN);
177 smi_en |= mask;
178 outl(smi_en, ACPI_BASE_ADDRESS + SMI_EN);
179}
180
181void pmc_disable_smi(uint32_t mask)
182{
183 uint32_t smi_en = inl(ACPI_BASE_ADDRESS + SMI_EN);
184 smi_en &= ~mask;
185 outl(smi_en, ACPI_BASE_ADDRESS + SMI_EN);
186}
187
188/* PM1 */
189void pmc_enable_pm1(uint16_t events)
190{
191 outw(events, ACPI_BASE_ADDRESS + PM1_EN);
192}
193
Furquan Shaikhab620182017-10-16 22:19:13 -0700194uint32_t pmc_read_pm1_control(void)
195{
196 return inl(ACPI_BASE_ADDRESS + PM1_CNT);
197}
198
199void pmc_write_pm1_control(uint32_t pm1_cnt)
200{
201 outl(pm1_cnt, ACPI_BASE_ADDRESS + PM1_CNT);
202}
203
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800204void pmc_enable_pm1_control(uint32_t mask)
205{
Furquan Shaikhab620182017-10-16 22:19:13 -0700206 uint32_t pm1_cnt = pmc_read_pm1_control();
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800207 pm1_cnt |= mask;
Furquan Shaikhab620182017-10-16 22:19:13 -0700208 pmc_write_pm1_control(pm1_cnt);
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800209}
210
211void pmc_disable_pm1_control(uint32_t mask)
212{
Furquan Shaikhab620182017-10-16 22:19:13 -0700213 uint32_t pm1_cnt = pmc_read_pm1_control();
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800214 pm1_cnt &= ~mask;
Furquan Shaikhab620182017-10-16 22:19:13 -0700215 pmc_write_pm1_control(pm1_cnt);
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800216}
217
218static uint16_t reset_pm1_status(void)
219{
220 uint16_t pm1_sts = inw(ACPI_BASE_ADDRESS + PM1_STS);
221 outw(pm1_sts, ACPI_BASE_ADDRESS + PM1_STS);
222 return pm1_sts;
223}
224
225static uint16_t print_pm1_status(uint16_t pm1_sts)
226{
227 static const char *const pm1_sts_bits[] = {
228 [0] = "TMROF",
229 [5] = "GBL",
230 [8] = "PWRBTN",
231 [10] = "RTC",
232 [11] = "PRBTNOR",
233 [13] = "USB",
234 [14] = "PCIEXPWAK",
235 [15] = "WAK",
236 };
237
238 if (!pm1_sts)
239 return 0;
240
241 printk(BIOS_SPEW, "PM1_STS: ");
242 print_num_status_bits(ARRAY_SIZE(pm1_sts_bits), pm1_sts, pm1_sts_bits);
243 printk(BIOS_SPEW, "\n");
244
245 return pm1_sts;
246}
247
248uint16_t pmc_clear_pm1_status(void)
249{
250 return print_pm1_status(reset_pm1_status());
251}
252
253/* TCO */
254
255static uint32_t print_tco_status(uint32_t tco_sts)
256{
257 size_t array_size;
258 const char *const *tco_arr;
259
260 if (!tco_sts)
261 return 0;
262
263 printk(BIOS_DEBUG, "TCO_STS: ");
264
265 tco_arr = soc_tco_sts_array(&array_size);
266
267 print_num_status_bits(array_size, tco_sts, tco_arr);
268 printk(BIOS_DEBUG, "\n");
269
270 return tco_sts;
271}
272
273uint32_t pmc_clear_tco_status(void)
274{
Subrata Banik7bc4dc52018-05-17 18:40:32 +0530275 return print_tco_status(tco_reset_status());
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800276}
277
278/* GPE */
Furquan Shaikhc4e652f2017-10-11 14:44:29 -0700279static void pmc_enable_gpe(int gpe, uint32_t mask)
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800280{
Furquan Shaikhc4e652f2017-10-11 14:44:29 -0700281 uint32_t gpe0_en = inl(ACPI_BASE_ADDRESS + GPE0_EN(gpe));
282 gpe0_en |= mask;
283 outl(gpe0_en, ACPI_BASE_ADDRESS + GPE0_EN(gpe));
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800284}
285
Furquan Shaikhc4e652f2017-10-11 14:44:29 -0700286static void pmc_disable_gpe(int gpe, uint32_t mask)
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800287{
Furquan Shaikhc4e652f2017-10-11 14:44:29 -0700288 uint32_t gpe0_en = inl(ACPI_BASE_ADDRESS + GPE0_EN(gpe));
289 gpe0_en &= ~mask;
290 outl(gpe0_en, ACPI_BASE_ADDRESS + GPE0_EN(gpe));
291}
292
293void pmc_enable_std_gpe(uint32_t mask)
294{
295 pmc_enable_gpe(GPE_STD, mask);
296}
297
298void pmc_disable_std_gpe(uint32_t mask)
299{
300 pmc_disable_gpe(GPE_STD, mask);
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800301}
302
303void pmc_disable_all_gpe(void)
304{
Furquan Shaikhc4e652f2017-10-11 14:44:29 -0700305 int i;
306 for (i = 0; i < GPE0_REG_MAX; i++)
307 pmc_disable_gpe(i, ~0);
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800308}
309
310/* Clear the gpio gpe0 status bits in ACPI registers */
Furquan Shaikhc4e652f2017-10-11 14:44:29 -0700311static void pmc_clear_gpi_gpe_status(void)
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800312{
313 int i;
314
315 for (i = 0; i < GPE0_REG_MAX; i++) {
316 /* This is reserved GPE block and specific to chipset */
317 if (i == GPE_STD)
318 continue;
319 uint32_t gpe_sts = inl(ACPI_BASE_ADDRESS + GPE0_STS(i));
320 outl(gpe_sts, ACPI_BASE_ADDRESS + GPE0_STS(i));
321 }
322}
323
Furquan Shaikhc4e652f2017-10-11 14:44:29 -0700324static uint32_t reset_std_gpe_status(void)
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800325{
326 uint32_t gpe_sts = inl(ACPI_BASE_ADDRESS + GPE0_STS(GPE_STD));
327 outl(gpe_sts, ACPI_BASE_ADDRESS + GPE0_STS(GPE_STD));
328 return gpe_sts;
329}
330
Furquan Shaikhc4e652f2017-10-11 14:44:29 -0700331static uint32_t print_std_gpe_sts(uint32_t gpe_sts)
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800332{
333 size_t array_size;
334 const char *const *sts_arr;
335
336 if (!gpe_sts)
337 return gpe_sts;
338
Furquan Shaikhc4e652f2017-10-11 14:44:29 -0700339 printk(BIOS_DEBUG, "GPE0 STD STS: ");
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800340
Furquan Shaikhc4e652f2017-10-11 14:44:29 -0700341 sts_arr = soc_std_gpe_sts_array(&array_size);
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800342 print_num_status_bits(array_size, gpe_sts, sts_arr);
343 printk(BIOS_DEBUG, "\n");
344
345 return gpe_sts;
346}
347
Furquan Shaikhc4e652f2017-10-11 14:44:29 -0700348static void pmc_clear_std_gpe_status(void)
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800349{
Furquan Shaikhc4e652f2017-10-11 14:44:29 -0700350 print_std_gpe_sts(reset_std_gpe_status());
351}
352
353void pmc_clear_all_gpe_status(void)
354{
355 pmc_clear_std_gpe_status();
356 pmc_clear_gpi_gpe_status();
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800357}
358
Aaron Durbin64031672018-04-21 14:45:32 -0600359__weak
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800360void soc_clear_pm_registers(uintptr_t pmc_bar)
361{
362}
363
Furquan Shaikhc4e652f2017-10-11 14:44:29 -0700364void pmc_clear_prsts(void)
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800365{
366 uint32_t prsts;
367 uintptr_t pmc_bar;
368
369 /* Read PMC base address from soc */
370 pmc_bar = soc_read_pmc_base();
371
Angel Ponsf585c6e2021-06-25 10:09:35 +0200372 prsts = read32p(pmc_bar + PRSTS);
373 write32p(pmc_bar + PRSTS, prsts);
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800374
375 soc_clear_pm_registers(pmc_bar);
376}
377
Aaron Durbin64031672018-04-21 14:45:32 -0600378__weak
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800379int soc_prev_sleep_state(const struct chipset_power_state *ps,
380 int prev_sleep_state)
381{
382 return prev_sleep_state;
383}
384
385/*
386 * Returns prev_sleep_state and also prints all power management registers.
Jonathan Neuschäfer5268b762018-02-12 12:24:25 +0100387 * Calls soc_prev_sleep_state which may be implemented by SOC.
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800388 */
389static int pmc_prev_sleep_state(const struct chipset_power_state *ps)
390{
391 /* Default to S0. */
392 int prev_sleep_state = ACPI_S0;
393
394 if (ps->pm1_sts & WAK_STS) {
395 switch (acpi_sleep_from_pm1(ps->pm1_cnt)) {
396 case ACPI_S3:
Julius Wernercd49cce2019-03-05 16:53:33 -0800397 if (CONFIG(HAVE_ACPI_RESUME))
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800398 prev_sleep_state = ACPI_S3;
399 break;
400 case ACPI_S5:
401 prev_sleep_state = ACPI_S5;
402 break;
403 }
404
405 /* Clear SLP_TYP. */
Furquan Shaikhab620182017-10-16 22:19:13 -0700406 pmc_write_pm1_control(ps->pm1_cnt & ~(SLP_TYP));
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800407 }
408 return soc_prev_sleep_state(ps, prev_sleep_state);
409}
410
Furquan Shaikhe48fb542017-10-17 16:00:10 -0700411void pmc_fill_pm_reg_info(struct chipset_power_state *ps)
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800412{
413 int i;
414
Furquan Shaikhe48fb542017-10-17 16:00:10 -0700415 memset(ps, 0, sizeof(*ps));
416
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800417 ps->pm1_sts = inw(ACPI_BASE_ADDRESS + PM1_STS);
418 ps->pm1_en = inw(ACPI_BASE_ADDRESS + PM1_EN);
Furquan Shaikhab620182017-10-16 22:19:13 -0700419 ps->pm1_cnt = pmc_read_pm1_control();
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800420
421 printk(BIOS_DEBUG, "pm1_sts: %04x pm1_en: %04x pm1_cnt: %08x\n",
422 ps->pm1_sts, ps->pm1_en, ps->pm1_cnt);
423
424 for (i = 0; i < GPE0_REG_MAX; i++) {
425 ps->gpe0_sts[i] = inl(ACPI_BASE_ADDRESS + GPE0_STS(i));
426 ps->gpe0_en[i] = inl(ACPI_BASE_ADDRESS + GPE0_EN(i));
427 printk(BIOS_DEBUG, "gpe0_sts[%d]: %08x gpe0_en[%d]: %08x\n",
428 i, ps->gpe0_sts[i], i, ps->gpe0_en[i]);
429 }
430
431 soc_fill_power_state(ps);
Furquan Shaikhe48fb542017-10-17 16:00:10 -0700432}
433
434/* Reads and prints ACPI specific PM registers */
435int pmc_fill_power_state(struct chipset_power_state *ps)
436{
437 pmc_fill_pm_reg_info(ps);
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800438
439 ps->prev_sleep_state = pmc_prev_sleep_state(ps);
440 printk(BIOS_DEBUG, "prev_sleep_state %d\n", ps->prev_sleep_state);
441
442 return ps->prev_sleep_state;
443}
444
Julius Wernercd49cce2019-03-05 16:53:33 -0800445#if CONFIG(PMC_GLOBAL_RESET_ENABLE_LOCK)
Michael Niewöhner1c6ea922019-11-02 12:20:53 +0100446void pmc_global_reset_disable_and_lock(void)
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800447{
Michael Niewöhner1c6ea922019-11-02 12:20:53 +0100448 uint32_t *etr = soc_pmc_etr_addr();
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800449 uint32_t reg;
450
Michael Niewöhner1c6ea922019-11-02 12:20:53 +0100451 reg = read32(etr);
452 reg = (reg & ~CF9_GLB_RST) | CF9_LOCK;
453 write32(etr, reg);
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800454}
455
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800456void pmc_global_reset_enable(bool enable)
457{
Michael Niewöhner1c6ea922019-11-02 12:20:53 +0100458 uint32_t *etr = soc_pmc_etr_addr();
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800459 uint32_t reg;
460
Michael Niewöhner1c6ea922019-11-02 12:20:53 +0100461 reg = read32(etr);
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800462 reg = enable ? reg | CF9_GLB_RST : reg & ~CF9_GLB_RST;
Michael Niewöhner1c6ea922019-11-02 12:20:53 +0100463 write32(etr, reg);
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800464}
Julien Viard de Galbert2912e8e2018-08-14 16:15:26 +0200465#endif // CONFIG_PMC_GLOBAL_RESET_ENABLE_LOCK
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800466
Bill XIE516c0a52020-02-24 23:08:35 +0800467int platform_is_resuming(void)
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800468{
469 if (!(inw(ACPI_BASE_ADDRESS + PM1_STS) & WAK_STS))
470 return 0;
471
Furquan Shaikhab620182017-10-16 22:19:13 -0700472 return acpi_sleep_from_pm1(pmc_read_pm1_control()) == ACPI_S3;
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800473}
474
Furquan Shaikh76cedd22020-05-02 10:24:23 -0700475/* Read and clear GPE status (defined in acpi/acpi.h) */
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800476int acpi_get_gpe(int gpe)
477{
478 int bank;
479 uint32_t mask, sts;
480 struct stopwatch sw;
481 int rc = 0;
482
483 if (gpe < 0 || gpe > GPE_MAX)
484 return -1;
485
486 bank = gpe / 32;
487 mask = 1 << (gpe % 32);
488
489 /* Wait up to 1ms for GPE status to clear */
490 stopwatch_init_msecs_expire(&sw, 1);
491 do {
492 if (stopwatch_expired(&sw))
493 return rc;
494
495 sts = inl(ACPI_BASE_ADDRESS + GPE0_STS(bank));
496 if (sts & mask) {
497 outl(mask, ACPI_BASE_ADDRESS + GPE0_STS(bank));
498 rc = 1;
499 }
500 } while (sts & mask);
501
502 return rc;
503}
504
505/*
506 * The PM1 control is set to S5 when vboot requests a reboot because the power
507 * state code above may not have collected its data yet. Therefore, set it to
508 * S5 when vboot requests a reboot. That's necessary if vboot fails in the
509 * resume path and requests a reboot. This prevents a reboot loop where the
510 * error is continually hit on the failing vboot resume path.
511 */
512void vboot_platform_prepare_reboot(void)
513{
Furquan Shaikhab620182017-10-16 22:19:13 -0700514 uint32_t pm1_cnt;
515 pm1_cnt = (pmc_read_pm1_control() & ~(SLP_TYP)) |
516 (SLP_TYP_S5 << SLP_TYP_SHIFT);
517 pmc_write_pm1_control(pm1_cnt);
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800518}
519
520void poweroff(void)
521{
522 pmc_enable_pm1_control(SLP_EN | (SLP_TYP_S5 << SLP_TYP_SHIFT));
523
524 /*
525 * Setting SLP_TYP_S5 in PM1 triggers SLP_SMI, which is handled by SMM
526 * to transition to S5 state. If halt is called in SMM, then it prevents
527 * the SMI handler from being triggered and system never enters S5.
528 */
529 if (!ENV_SMM)
530 halt();
531}
532
533void pmc_gpe_init(void)
534{
535 uint32_t gpio_cfg = 0;
536 uint32_t gpio_cfg_reg;
537 uint8_t dw0, dw1, dw2;
538
539 /* Read PMC base address from soc. This is implemented in soc */
540 uintptr_t pmc_bar = soc_read_pmc_base();
541
542 /*
543 * Get the dwX values for pmc gpe settings.
544 */
Furquan Shaikhc4e652f2017-10-11 14:44:29 -0700545 soc_get_gpi_gpe_configs(&dw0, &dw1, &dw2);
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800546
547 const uint32_t gpio_cfg_mask =
548 (GPE0_DWX_MASK << GPE0_DW_SHIFT(0)) |
549 (GPE0_DWX_MASK << GPE0_DW_SHIFT(1)) |
550 (GPE0_DWX_MASK << GPE0_DW_SHIFT(2));
551
552 /* Making sure that bad values don't bleed into the other fields */
553 dw0 &= GPE0_DWX_MASK;
554 dw1 &= GPE0_DWX_MASK;
555 dw2 &= GPE0_DWX_MASK;
556
557 /*
558 * Route the GPIOs to the GPE0 block. Determine that all values
559 * are different, and if they aren't use the reset values.
560 */
561 if (dw0 == dw1 || dw1 == dw2) {
562 printk(BIOS_INFO, "PMC: Using default GPE route.\n");
Angel Ponsf585c6e2021-06-25 10:09:35 +0200563 gpio_cfg = read32p(pmc_bar + GPIO_GPE_CFG);
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800564
565 dw0 = (gpio_cfg >> GPE0_DW_SHIFT(0)) & GPE0_DWX_MASK;
566 dw1 = (gpio_cfg >> GPE0_DW_SHIFT(1)) & GPE0_DWX_MASK;
567 dw2 = (gpio_cfg >> GPE0_DW_SHIFT(2)) & GPE0_DWX_MASK;
568 } else {
569 gpio_cfg |= (uint32_t) dw0 << GPE0_DW_SHIFT(0);
570 gpio_cfg |= (uint32_t) dw1 << GPE0_DW_SHIFT(1);
571 gpio_cfg |= (uint32_t) dw2 << GPE0_DW_SHIFT(2);
572 }
573
Angel Ponsf585c6e2021-06-25 10:09:35 +0200574 gpio_cfg_reg = read32p(pmc_bar + GPIO_GPE_CFG) & ~gpio_cfg_mask;
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800575 gpio_cfg_reg |= gpio_cfg & gpio_cfg_mask;
576
Angel Ponsf585c6e2021-06-25 10:09:35 +0200577 write32p(pmc_bar + GPIO_GPE_CFG, gpio_cfg_reg);
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800578
579 /* Set the routes in the GPIO communities as well. */
580 gpio_route_gpe(dw0, dw1, dw2);
Shaunak Saha9dffbdd2017-03-08 19:27:17 -0800581}
Nico Huberef19ce52019-08-05 19:19:59 +0200582
583void pmc_set_power_failure_state(const bool target_on)
584{
Angel Pons88dcb312021-04-26 17:10:28 +0200585 const unsigned int state = get_uint_option("power_on_after_fail",
Angel Pons62719a32021-04-19 13:15:28 +0200586 CONFIG_MAINBOARD_POWER_FAILURE_STATE);
Nico Huber6bbabef2019-08-05 21:24:00 +0200587
Nico Huberdca081b2021-06-15 17:19:10 +0200588 /*
589 * On the shutdown path (target_on == false), we only need to
590 * update the register for MAINBOARD_POWER_STATE_PREVIOUS. For
591 * all other cases, we don't write the register to avoid clob-
592 * bering the value set on the boot path. This is necessary,
593 * for instance, when we can't access the option backend in SMM.
594 */
595
Nico Huberef19ce52019-08-05 19:19:59 +0200596 switch (state) {
597 case MAINBOARD_POWER_STATE_OFF:
Nico Huberdca081b2021-06-15 17:19:10 +0200598 if (!target_on)
599 break;
Nico Huberef19ce52019-08-05 19:19:59 +0200600 printk(BIOS_INFO, "Set power off after power failure.\n");
Nico Huberdca081b2021-06-15 17:19:10 +0200601 pmc_soc_set_afterg3_en(false);
Nico Huberef19ce52019-08-05 19:19:59 +0200602 break;
603 case MAINBOARD_POWER_STATE_ON:
Nico Huberdca081b2021-06-15 17:19:10 +0200604 if (!target_on)
605 break;
Nico Huberef19ce52019-08-05 19:19:59 +0200606 printk(BIOS_INFO, "Set power on after power failure.\n");
Nico Huberdca081b2021-06-15 17:19:10 +0200607 pmc_soc_set_afterg3_en(true);
Nico Huberef19ce52019-08-05 19:19:59 +0200608 break;
609 case MAINBOARD_POWER_STATE_PREVIOUS:
610 printk(BIOS_INFO, "Keep power state after power failure.\n");
Nico Huberdca081b2021-06-15 17:19:10 +0200611 pmc_soc_set_afterg3_en(target_on);
Nico Huberef19ce52019-08-05 19:19:59 +0200612 break;
613 default:
Julius Wernere9665952022-01-21 17:06:20 -0800614 printk(BIOS_WARNING, "Unknown power-failure state: %d\n", state);
Nico Huberef19ce52019-08-05 19:19:59 +0200615 break;
616 }
Nico Huberef19ce52019-08-05 19:19:59 +0200617}
V Sowmya186250f2020-09-02 16:40:24 +0530618
619/* This function returns the highest assertion duration of the SLP_Sx assertion widths */
620static enum min_assert_dur get_high_assert_width(const struct cfg_assert_dur *cfg_assert_dur)
621{
622 enum min_assert_dur max_assert_dur = cfg_assert_dur->slp_s4;
623
624 if (max_assert_dur < cfg_assert_dur->slp_s3)
625 max_assert_dur = cfg_assert_dur->slp_s3;
626
627 if (max_assert_dur < cfg_assert_dur->slp_a)
628 max_assert_dur = cfg_assert_dur->slp_a;
629
630 return max_assert_dur;
631}
632
633/* This function converts assertion durations from register-encoded to microseconds */
634static void get_min_assert_dur(uint8_t slp_s4_min_assert, uint8_t slp_s3_min_assert,
635 uint8_t slp_a_min_assert, uint8_t pm_pwr_cyc_dur,
636 struct cfg_assert_dur *cfg_assert_dur)
637{
638 /*
639 * Ensure slp_x_dur_list[] elements in the devicetree config are in sync with
640 * FSP encoded values.
641 */
642
643 /* slp_s4_assert_dur_list : 1s, 1s(default), 2s, 3s, 4s */
644 const enum min_assert_dur slp_s4_assert_dur_list[] = {
645 MinAssertDur1s, MinAssertDur1s, MinAssertDur2s, MinAssertDur3s, MinAssertDur4s
646 };
647
648 /* slp_s3_assert_dur_list: 50ms, 60us, 1ms, 50ms (Default), 2s */
649 const enum min_assert_dur slp_s3_assert_dur_list[] = {
650 MinAssertDur50ms, MinAssertDur60us, MinAssertDur1ms, MinAssertDur50ms,
651 MinAssertDur2s
652 };
653
654 /* slp_a_assert_dur_list: 2s, 0s, 4s, 98ms, 2s(Default) */
655 const enum min_assert_dur slp_a_assert_dur_list[] = {
656 MinAssertDur2s, MinAssertDur0s, MinAssertDur4s, MinAssertDur98ms, MinAssertDur2s
657 };
658
659 /* pm_pwr_cyc_dur_list: 4s(Default), 1s, 2s, 3s, 4s */
660 const enum min_assert_dur pm_pwr_cyc_dur_list[] = {
661 MinAssertDur4s, MinAssertDur1s, MinAssertDur2s, MinAssertDur3s, MinAssertDur4s
662 };
663
664 /* Get signal assertion width */
665 if (slp_s4_min_assert < ARRAY_SIZE(slp_s4_assert_dur_list))
666 cfg_assert_dur->slp_s4 = slp_s4_assert_dur_list[slp_s4_min_assert];
667
668 if (slp_s3_min_assert < ARRAY_SIZE(slp_s3_assert_dur_list))
669 cfg_assert_dur->slp_s3 = slp_s3_assert_dur_list[slp_s3_min_assert];
670
671 if (slp_a_min_assert < ARRAY_SIZE(slp_a_assert_dur_list))
672 cfg_assert_dur->slp_a = slp_a_assert_dur_list[slp_a_min_assert];
673
674 if (pm_pwr_cyc_dur < ARRAY_SIZE(pm_pwr_cyc_dur_list))
675 cfg_assert_dur->pm_pwr_cyc_dur = pm_pwr_cyc_dur_list[pm_pwr_cyc_dur];
676}
677
678/*
679 * This function ensures that the duration programmed in the PchPmPwrCycDur will never be
680 * smaller than the SLP_Sx assertion widths.
681 * If the pm_pwr_cyc_dur is less than any of the SLP_Sx assertion widths then it returns the
682 * default value PCH_PM_PWR_CYC_DUR.
683 */
684uint8_t get_pm_pwr_cyc_dur(uint8_t slp_s4_min_assert, uint8_t slp_s3_min_assert,
685 uint8_t slp_a_min_assert, uint8_t pm_pwr_cyc_dur)
686{
687 /* Set default values for the minimum assertion duration */
688 struct cfg_assert_dur cfg_assert_dur = {
689 .slp_a = MinAssertDur2s,
690 .slp_s4 = MinAssertDur1s,
691 .slp_s3 = MinAssertDur50ms,
692 .pm_pwr_cyc_dur = MinAssertDur4s
693 };
694
695 enum min_assert_dur high_assert_width;
696
697 /* Convert assertion durations from register-encoded to microseconds */
698 get_min_assert_dur(slp_s4_min_assert, slp_s3_min_assert, slp_a_min_assert,
699 pm_pwr_cyc_dur, &cfg_assert_dur);
700
701 /* Get the highest assertion duration among PCH EDS specified signals for pwr_cyc_dur */
702 high_assert_width = get_high_assert_width(&cfg_assert_dur);
703
704 if (cfg_assert_dur.pm_pwr_cyc_dur >= high_assert_width)
705 return pm_pwr_cyc_dur;
706
707 printk(BIOS_DEBUG,
708 "Set PmPwrCycDur to 4s as configured PmPwrCycDur (%d) violates PCH EDS "
709 "spec\n", pm_pwr_cyc_dur);
710
711 return PCH_PM_PWR_CYC_DUR;
712}
Subrata Banik3e959d82020-09-28 17:50:00 +0530713
Arthur Heymans08c646c2020-11-19 13:56:41 +0100714void pmc_set_acpi_mode(void)
715{
Arthur Heymans8461cec2020-11-19 14:22:24 +0100716 if (!CONFIG(NO_SMM) && !acpi_is_wakeup_s3()) {
Arthur Heymans08c646c2020-11-19 13:56:41 +0100717 apm_control(APM_CNT_ACPI_DISABLE);
718 }
719}
Lean Sheng Tan508dc162021-06-16 01:32:22 -0700720
721enum pch_pmc_xtal pmc_get_xtal_freq(void)
722{
Lean Sheng Tan75020002021-06-30 01:47:48 -0700723 if (!CONFIG(SOC_INTEL_COMMON_BLOCK_PMC_EPOC))
Lean Sheng Tan508dc162021-06-16 01:32:22 -0700724 dead_code();
725
Lean Sheng Tanbed1b602021-06-16 09:41:37 -0700726 uint32_t xtal_freq = 0;
727 const uint32_t epoc = read32p(soc_read_pmc_base() + PCH_PMC_EPOC);
Lean Sheng Tan508dc162021-06-16 01:32:22 -0700728
Lean Sheng Tanbed1b602021-06-16 09:41:37 -0700729 /* XTAL frequency in bits 21, 20, 17 */
730 xtal_freq |= !!(epoc & (1 << 21)) << 2;
731 xtal_freq |= !!(epoc & (1 << 20)) << 1;
732 xtal_freq |= !!(epoc & (1 << 17)) << 0;
733 switch (xtal_freq) {
734 case 0:
735 return XTAL_24_MHZ;
736 case 1:
737 return XTAL_19_2_MHZ;
738 case 2:
739 return XTAL_38_4_MHZ;
740 default:
741 printk(BIOS_ERR, "Unknown EPOC XTAL frequency setting %u\n", xtal_freq);
742 return XTAL_UNKNOWN_FREQ;
743 }
Lean Sheng Tan508dc162021-06-16 01:32:22 -0700744}