blob: 904be5c2a04d2ae549e5430ba9c80b2f47913864 [file] [log] [blame]
Lee Leahyb0005132015-05-12 18:19:47 -07001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2014 Google Inc.
Lee Leahy1d14b3e2015-05-12 18:23:27 -07005 * Copyright (C) 2015 Intel Corporation.
Lee Leahyb0005132015-05-12 18:19:47 -07006 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
Lee Leahyb0005132015-05-12 18:19:47 -070015 */
16
17/*
18 * Helper functions for dealing with power management registers
19 * and the differences between PCH variants.
20 */
21
22#include <arch/io.h>
23#include <device/device.h>
24#include <device/pci.h>
25#include <device/pci_def.h>
26#include <console/console.h>
Aaron Durbin38613d02016-07-14 00:56:58 -050027#include <halt.h>
Furquan Shaikh97e0a652016-08-18 21:42:36 -070028#include <rules.h>
Lee Leahy1d14b3e2015-05-12 18:23:27 -070029#include <stdlib.h>
30#include <soc/gpio.h>
Lee Leahyb0005132015-05-12 18:19:47 -070031#include <soc/iomap.h>
32#include <soc/lpc.h>
33#include <soc/pci_devs.h>
34#include <soc/pm.h>
Lee Leahy1d14b3e2015-05-12 18:23:27 -070035#include <soc/pmc.h>
36#include <soc/smbus.h>
Lee Leahyb0005132015-05-12 18:19:47 -070037
38/* Print status bits with descriptive names */
39static void print_status_bits(u32 status, const char *bit_names[])
40{
41 int i;
42
43 if (!status)
44 return;
45
Lee Leahy1d14b3e2015-05-12 18:23:27 -070046 for (i = 31; i >= 0; i--) {
Lee Leahyb0005132015-05-12 18:19:47 -070047 if (status & (1 << i)) {
48 if (bit_names[i])
49 printk(BIOS_DEBUG, "%s ", bit_names[i]);
50 else
51 printk(BIOS_DEBUG, "BIT%d ", i);
52 }
53 }
54}
55
56/* Print status bits as GPIO numbers */
57static void print_gpio_status(u32 status, int start)
58{
59 int i;
60
61 if (!status)
62 return;
63
Lee Leahy1d14b3e2015-05-12 18:23:27 -070064 for (i = 31; i >= 0; i--) {
Lee Leahyb0005132015-05-12 18:19:47 -070065 if (status & (1 << i))
66 printk(BIOS_DEBUG, "GPIO%d ", start + i);
67 }
68}
69
70
71/*
72 * PM1_CNT
73 */
74
75/* Enable events in PM1 control register */
76void enable_pm1_control(u32 mask)
77{
78 u32 pm1_cnt = inl(ACPI_BASE_ADDRESS + PM1_CNT);
79 pm1_cnt |= mask;
80 outl(pm1_cnt, ACPI_BASE_ADDRESS + PM1_CNT);
81}
82
83/* Disable events in PM1 control register */
84void disable_pm1_control(u32 mask)
85{
86 u32 pm1_cnt = inl(ACPI_BASE_ADDRESS + PM1_CNT);
87 pm1_cnt &= ~mask;
88 outl(pm1_cnt, ACPI_BASE_ADDRESS + PM1_CNT);
89}
90
91
92/*
93 * PM1
94 */
95
96/* Clear and return PM1 status register */
97static u16 reset_pm1_status(void)
98{
99 u16 pm1_sts = inw(ACPI_BASE_ADDRESS + PM1_STS);
100 outw(pm1_sts, ACPI_BASE_ADDRESS + PM1_STS);
101 return pm1_sts;
102}
103
104/* Print PM1 status bits */
105static u16 print_pm1_status(u16 pm1_sts)
106{
107 const char *pm1_sts_bits[] = {
108 [0] = "TMROF",
109 [4] = "BM",
110 [5] = "GBL",
111 [8] = "PWRBTN",
112 [10] = "RTC",
113 [11] = "PRBTNOR",
114 [14] = "PCIEXPWAK",
115 [15] = "WAK",
116 };
117
118 if (!pm1_sts)
119 return 0;
120
121 printk(BIOS_SPEW, "PM1_STS: ");
122 print_status_bits(pm1_sts, pm1_sts_bits);
123 printk(BIOS_SPEW, "\n");
124
125 return pm1_sts;
126}
127
128/* Print, clear, and return PM1 status */
129u16 clear_pm1_status(void)
130{
131 return print_pm1_status(reset_pm1_status());
132}
133
134/* Set the PM1 register to events */
135void enable_pm1(u16 events)
136{
137 outw(events, ACPI_BASE_ADDRESS + PM1_EN);
138}
139
140
141/*
142 * SMI
143 */
144
145/* Clear and return SMI status register */
146static u32 reset_smi_status(void)
147{
148 u32 smi_sts = inl(ACPI_BASE_ADDRESS + SMI_STS);
149 outl(smi_sts, ACPI_BASE_ADDRESS + SMI_STS);
150 return smi_sts;
151}
152
153/* Print SMI status bits */
154static u32 print_smi_status(u32 smi_sts)
155{
156 const char *smi_sts_bits[] = {
157 [2] = "BIOS",
158 [3] = "LEGACY_USB",
159 [4] = "SLP_SMI",
160 [5] = "APM",
161 [6] = "SWSMI_TMR",
162 [8] = "PM1",
163 [9] = "GPE0",
164 [10] = "GPI",
165 [11] = "MCSMI",
166 [12] = "DEVMON",
167 [13] = "TCO",
168 [14] = "PERIODIC",
169 [15] = "SERIRQ_SMI",
170 [16] = "SMBUS_SMI",
171 [17] = "LEGACY_USB2",
172 [18] = "INTEL_USB2",
173 [20] = "PCI_EXP_SMI",
174 [21] = "MONITOR",
175 [26] = "SPI",
176 [27] = "GPIO_UNLOCK"
177 };
178
179 if (!smi_sts)
180 return 0;
181
182 printk(BIOS_DEBUG, "SMI_STS: ");
183 print_status_bits(smi_sts, smi_sts_bits);
184 printk(BIOS_DEBUG, "\n");
185
186 return smi_sts;
187}
188
189/* Print, clear, and return SMI status */
190u32 clear_smi_status(void)
191{
192 return print_smi_status(reset_smi_status());
193}
194
195/* Enable SMI event */
196void enable_smi(u32 mask)
197{
198 u32 smi_en = inl(ACPI_BASE_ADDRESS + SMI_EN);
199 smi_en |= mask;
200 outl(smi_en, ACPI_BASE_ADDRESS + SMI_EN);
201}
202
203/* Disable SMI event */
204void disable_smi(u32 mask)
205{
206 u32 smi_en = inl(ACPI_BASE_ADDRESS + SMI_EN);
207 smi_en &= ~mask;
208 outl(smi_en, ACPI_BASE_ADDRESS + SMI_EN);
209}
210
Lee Leahyb0005132015-05-12 18:19:47 -0700211/*
212 * TCO
213 */
214
215/* Clear TCO status and return events that are enabled and active */
216static u32 reset_tco_status(void)
217{
Lee Leahy1d14b3e2015-05-12 18:23:27 -0700218 u16 tco1_sts;
219 u16 tco2_sts;
220 u16 tcobase;
Lee Leahyb0005132015-05-12 18:19:47 -0700221
Barnali Sarkar49eca132016-08-12 00:05:27 +0530222 tcobase = smbus_tco_regs();
Lee Leahyb0005132015-05-12 18:19:47 -0700223
Lee Leahy1d14b3e2015-05-12 18:23:27 -0700224 /* TCO Status 2 register*/
225 tco2_sts = inw(tcobase + TCO2_STS);
226 tco2_sts |= (TCO2_STS_SECOND_TO | TCO2_STS_BOOT);
227 outw(tco2_sts, tcobase + TCO2_STS);
Lee Leahyb0005132015-05-12 18:19:47 -0700228
Lee Leahy1d14b3e2015-05-12 18:23:27 -0700229 /* TCO Status 1 register*/
230 tco1_sts = inw(tcobase + TCO1_STS);
231
232 /* Clear SECOND_TO_STS bit */
233 if (tco2_sts & TCO2_STS_SECOND_TO)
234 outw(tco2_sts & ~TCO2_STS_SECOND_TO, tcobase + TCO2_STS);
235
236 return (tco2_sts << 16) | tco1_sts;
Lee Leahyb0005132015-05-12 18:19:47 -0700237}
238
239/* Print TCO status bits */
240static u32 print_tco_status(u32 tco_sts)
241{
242 const char *tco_sts_bits[] = {
243 [0] = "NMI2SMI",
244 [1] = "SW_TCO",
245 [2] = "TCO_INT",
246 [3] = "TIMEOUT",
247 [7] = "NEWCENTURY",
248 [8] = "BIOSWR",
249 [9] = "DMISCI",
250 [10] = "DMISMI",
251 [12] = "DMISERR",
252 [13] = "SLVSEL",
253 [16] = "INTRD_DET",
254 [17] = "SECOND_TO",
255 [18] = "BOOT",
256 [20] = "SMLINK_SLV"
257 };
258
259 if (!tco_sts)
260 return 0;
261
262 printk(BIOS_DEBUG, "TCO_STS: ");
263 print_status_bits(tco_sts, tco_sts_bits);
264 printk(BIOS_DEBUG, "\n");
265
266 return tco_sts;
267}
268
269/* Print, clear, and return TCO status */
270u32 clear_tco_status(void)
271{
272 return print_tco_status(reset_tco_status());
273}
274
275/* Enable TCO SCI */
276void enable_tco_sci(void)
277{
278 /* Clear pending events */
Lee Leahy1d14b3e2015-05-12 18:23:27 -0700279 outl(TCOSCI_STS, ACPI_BASE_ADDRESS + GPE0_STS(3));
Lee Leahyb0005132015-05-12 18:19:47 -0700280
281 /* Enable TCO SCI events */
282 enable_gpe(TCOSCI_EN);
283}
284
285
286/*
287 * GPE0
288 */
289
290/* Clear a GPE0 status and return events that are enabled and active */
291static u32 reset_gpe(u16 sts_reg, u16 en_reg)
292{
293 u32 gpe0_sts = inl(ACPI_BASE_ADDRESS + sts_reg);
294 u32 gpe0_en = inl(ACPI_BASE_ADDRESS + en_reg);
295
296 outl(gpe0_sts, ACPI_BASE_ADDRESS + sts_reg);
297
298 /* Only report enabled events */
299 return gpe0_sts & gpe0_en;
300}
301
302/* Print GPE0 status bits */
303static u32 print_gpe_status(u32 gpe0_sts, const char *bit_names[])
304{
305 if (!gpe0_sts)
306 return 0;
307
308 printk(BIOS_DEBUG, "GPE0_STS: ");
309 print_status_bits(gpe0_sts, bit_names);
310 printk(BIOS_DEBUG, "\n");
311
312 return gpe0_sts;
313}
314
315/* Print GPE0 GPIO status bits */
316static u32 print_gpe_gpio(u32 gpe0_sts, int start)
317{
318 if (!gpe0_sts)
319 return 0;
320
321 printk(BIOS_DEBUG, "GPE0_STS: ");
322 print_gpio_status(gpe0_sts, start);
323 printk(BIOS_DEBUG, "\n");
324
325 return gpe0_sts;
326}
327
328/* Clear all GPE status and return "standard" GPE event status */
329u32 clear_gpe_status(void)
330{
331 const char *gpe0_sts_3_bits[] = {
332 [1] = "HOTPLUG",
333 [2] = "SWGPE",
334 [6] = "TCO_SCI",
335 [7] = "SMB_WAK",
336 [9] = "PCI_EXP",
337 [10] = "BATLOW",
338 [11] = "PME",
339 [12] = "ME",
340 [13] = "PME_B0",
Aaron Durbin7f788492015-07-24 17:10:31 -0500341 [14] = "eSPI",
342 [15] = "GPIO Tier-2",
343 [16] = "LAN_WAKE",
Lee Leahyb0005132015-05-12 18:19:47 -0700344 [18] = "WADT"
345 };
346
347 print_gpe_gpio(reset_gpe(GPE0_STS(GPE_31_0), GPE0_EN(GPE_31_0)), 0);
348 print_gpe_gpio(reset_gpe(GPE0_STS(GPE_63_32), GPE0_EN(GPE_63_32)), 32);
Aaron Durbin7f788492015-07-24 17:10:31 -0500349 print_gpe_gpio(reset_gpe(GPE0_STS(GPE_95_64), GPE0_EN(GPE_95_64)), 64);
Lee Leahyb0005132015-05-12 18:19:47 -0700350 return print_gpe_status(reset_gpe(GPE0_STS(GPE_STD), GPE0_EN(GPE_STD)),
351 gpe0_sts_3_bits);
352}
353
Duncan Laurie64ce1d12016-10-25 20:05:31 -0700354/* Read and clear GPE status (defined in arch/acpi.h) */
355int acpi_get_gpe(int gpe)
356{
357 int bank;
358 uint32_t mask, sts;
359
360 if (gpe < 0 || gpe > GPE0_WADT)
361 return -1;
362
363 bank = gpe / 32;
364 mask = 1 << (gpe % 32);
365
366 sts = inl(ACPI_BASE_ADDRESS + GPE0_STS(bank));
367 if (sts & mask) {
368 outl(mask, ACPI_BASE_ADDRESS + GPE0_STS(bank));
369 return 1;
370 }
371 return 0;
372}
373
Lee Leahyb0005132015-05-12 18:19:47 -0700374/* Enable all requested GPE */
375void enable_all_gpe(u32 set1, u32 set2, u32 set3, u32 set4)
376{
377 outl(set1, ACPI_BASE_ADDRESS + GPE0_EN(GPE_31_0));
378 outl(set2, ACPI_BASE_ADDRESS + GPE0_EN(GPE_63_32));
Aaron Durbin7f788492015-07-24 17:10:31 -0500379 outl(set3, ACPI_BASE_ADDRESS + GPE0_EN(GPE_95_64));
Lee Leahyb0005132015-05-12 18:19:47 -0700380 outl(set4, ACPI_BASE_ADDRESS + GPE0_EN(GPE_STD));
381}
382
383/* Disable all GPE */
384void disable_all_gpe(void)
385{
386 enable_all_gpe(0, 0, 0, 0);
387}
388
389/* Enable a standard GPE */
390void enable_gpe(u32 mask)
391{
392 u32 gpe0_en = inl(ACPI_BASE_ADDRESS + GPE0_EN(GPE_STD));
393 gpe0_en |= mask;
394 outl(gpe0_en, ACPI_BASE_ADDRESS + GPE0_EN(GPE_STD));
395}
396
397/* Disable a standard GPE */
398void disable_gpe(u32 mask)
399{
400 u32 gpe0_en = inl(ACPI_BASE_ADDRESS + GPE0_EN(GPE_STD));
401 gpe0_en &= ~mask;
402 outl(gpe0_en, ACPI_BASE_ADDRESS + GPE0_EN(GPE_STD));
403}
404
405int acpi_sci_irq(void)
406{
Lee Leahy1d14b3e2015-05-12 18:23:27 -0700407 int scis = pci_read_config32(PCH_DEV_PMC, ACTL) & SCI_IRQ_SEL;
Lee Leahyb0005132015-05-12 18:19:47 -0700408 int sci_irq = 9;
409
410 /* Determine how SCI is routed. */
411 switch (scis) {
412 case SCIS_IRQ9:
413 case SCIS_IRQ10:
414 case SCIS_IRQ11:
415 sci_irq = scis - SCIS_IRQ9 + 9;
416 break;
417 case SCIS_IRQ20:
418 case SCIS_IRQ21:
419 case SCIS_IRQ22:
420 case SCIS_IRQ23:
421 sci_irq = scis - SCIS_IRQ20 + 20;
422 break;
423 default:
424 printk(BIOS_DEBUG, "Invalid SCI route! Defaulting to IRQ9.\n");
425 sci_irq = 9;
426 break;
427 }
428
429 printk(BIOS_DEBUG, "SCI is IRQ%d\n", sci_irq);
430 return sci_irq;
431}
Lee Leahy1d14b3e2015-05-12 18:23:27 -0700432
433uint8_t *pmc_mmio_regs(void)
434{
435 uint32_t reg32;
436
437 reg32 = pci_read_config32(PCH_DEV_PMC, PWRMBASE);
438
439 /* 4KiB alignment. */
440 reg32 &= ~0xfff;
441
442 return (void *)(uintptr_t)reg32;
443}
444
Barnali Sarkar49eca132016-08-12 00:05:27 +0530445uint16_t smbus_tco_regs(void)
Lee Leahy1d14b3e2015-05-12 18:23:27 -0700446{
447 uint16_t reg16;
448
449 reg16 = pci_read_config16(PCH_DEV_SMBUS, TCOBASE);
450
451 reg16 &= ~0x1f;
452
453 return reg16;
454}
Aaron Durbin38613d02016-07-14 00:56:58 -0500455
456void poweroff(void)
457{
458 enable_pm1_control(SLP_EN | (SLP_TYP_S5 << SLP_TYP_SHIFT));
Furquan Shaikh97e0a652016-08-18 21:42:36 -0700459
460 /*
461 * Setting SLP_TYP_S5 in PM1 triggers SLP_SMI, which is handled by SMM
462 * to transition to S5 state. If halt is called in SMM, then it prevents
463 * the SMI handler from being triggered and system never enters S5.
464 */
465 if (!ENV_SMM)
466 halt();
Aaron Durbin38613d02016-07-14 00:56:58 -0500467}