blob: 7d83e9f2409eddfc9ce62970d454d844062ae28b [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
Duncan Laurief0ba2252016-10-25 20:03:56 -070022#define __SIMPLE_DEVICE__
23
Lee Leahyb0005132015-05-12 18:19:47 -070024#include <arch/io.h>
25#include <device/device.h>
26#include <device/pci.h>
27#include <device/pci_def.h>
28#include <console/console.h>
Aaron Durbin38613d02016-07-14 00:56:58 -050029#include <halt.h>
Furquan Shaikh97e0a652016-08-18 21:42:36 -070030#include <rules.h>
Lee Leahy1d14b3e2015-05-12 18:23:27 -070031#include <stdlib.h>
Duncan Laurief0ba2252016-10-25 20:03:56 -070032#include <soc/gpe.h>
Lee Leahy1d14b3e2015-05-12 18:23:27 -070033#include <soc/gpio.h>
Lee Leahyb0005132015-05-12 18:19:47 -070034#include <soc/iomap.h>
35#include <soc/lpc.h>
36#include <soc/pci_devs.h>
37#include <soc/pm.h>
Lee Leahy1d14b3e2015-05-12 18:23:27 -070038#include <soc/pmc.h>
39#include <soc/smbus.h>
Duncan Laurief0ba2252016-10-25 20:03:56 -070040#include "chip.h"
Lee Leahyb0005132015-05-12 18:19:47 -070041
42/* Print status bits with descriptive names */
43static void print_status_bits(u32 status, const char *bit_names[])
44{
45 int i;
46
47 if (!status)
48 return;
49
Lee Leahy1d14b3e2015-05-12 18:23:27 -070050 for (i = 31; i >= 0; i--) {
Lee Leahyb0005132015-05-12 18:19:47 -070051 if (status & (1 << i)) {
52 if (bit_names[i])
53 printk(BIOS_DEBUG, "%s ", bit_names[i]);
54 else
55 printk(BIOS_DEBUG, "BIT%d ", i);
56 }
57 }
58}
59
60/* Print status bits as GPIO numbers */
61static void print_gpio_status(u32 status, int start)
62{
63 int i;
64
65 if (!status)
66 return;
67
Lee Leahy1d14b3e2015-05-12 18:23:27 -070068 for (i = 31; i >= 0; i--) {
Lee Leahyb0005132015-05-12 18:19:47 -070069 if (status & (1 << i))
70 printk(BIOS_DEBUG, "GPIO%d ", start + i);
71 }
72}
73
74
75/*
76 * PM1_CNT
77 */
78
79/* Enable events in PM1 control register */
80void enable_pm1_control(u32 mask)
81{
82 u32 pm1_cnt = inl(ACPI_BASE_ADDRESS + PM1_CNT);
83 pm1_cnt |= mask;
84 outl(pm1_cnt, ACPI_BASE_ADDRESS + PM1_CNT);
85}
86
87/* Disable events in PM1 control register */
88void disable_pm1_control(u32 mask)
89{
90 u32 pm1_cnt = inl(ACPI_BASE_ADDRESS + PM1_CNT);
91 pm1_cnt &= ~mask;
92 outl(pm1_cnt, ACPI_BASE_ADDRESS + PM1_CNT);
93}
94
95
96/*
97 * PM1
98 */
99
100/* Clear and return PM1 status register */
101static u16 reset_pm1_status(void)
102{
103 u16 pm1_sts = inw(ACPI_BASE_ADDRESS + PM1_STS);
104 outw(pm1_sts, ACPI_BASE_ADDRESS + PM1_STS);
105 return pm1_sts;
106}
107
108/* Print PM1 status bits */
109static u16 print_pm1_status(u16 pm1_sts)
110{
111 const char *pm1_sts_bits[] = {
112 [0] = "TMROF",
113 [4] = "BM",
114 [5] = "GBL",
115 [8] = "PWRBTN",
116 [10] = "RTC",
117 [11] = "PRBTNOR",
118 [14] = "PCIEXPWAK",
119 [15] = "WAK",
120 };
121
122 if (!pm1_sts)
123 return 0;
124
125 printk(BIOS_SPEW, "PM1_STS: ");
126 print_status_bits(pm1_sts, pm1_sts_bits);
127 printk(BIOS_SPEW, "\n");
128
129 return pm1_sts;
130}
131
132/* Print, clear, and return PM1 status */
133u16 clear_pm1_status(void)
134{
135 return print_pm1_status(reset_pm1_status());
136}
137
138/* Set the PM1 register to events */
139void enable_pm1(u16 events)
140{
141 outw(events, ACPI_BASE_ADDRESS + PM1_EN);
142}
143
144
145/*
146 * SMI
147 */
148
149/* Clear and return SMI status register */
150static u32 reset_smi_status(void)
151{
152 u32 smi_sts = inl(ACPI_BASE_ADDRESS + SMI_STS);
153 outl(smi_sts, ACPI_BASE_ADDRESS + SMI_STS);
154 return smi_sts;
155}
156
157/* Print SMI status bits */
158static u32 print_smi_status(u32 smi_sts)
159{
160 const char *smi_sts_bits[] = {
161 [2] = "BIOS",
162 [3] = "LEGACY_USB",
163 [4] = "SLP_SMI",
164 [5] = "APM",
165 [6] = "SWSMI_TMR",
166 [8] = "PM1",
167 [9] = "GPE0",
168 [10] = "GPI",
169 [11] = "MCSMI",
170 [12] = "DEVMON",
171 [13] = "TCO",
172 [14] = "PERIODIC",
173 [15] = "SERIRQ_SMI",
174 [16] = "SMBUS_SMI",
175 [17] = "LEGACY_USB2",
176 [18] = "INTEL_USB2",
177 [20] = "PCI_EXP_SMI",
178 [21] = "MONITOR",
179 [26] = "SPI",
180 [27] = "GPIO_UNLOCK"
181 };
182
183 if (!smi_sts)
184 return 0;
185
186 printk(BIOS_DEBUG, "SMI_STS: ");
187 print_status_bits(smi_sts, smi_sts_bits);
188 printk(BIOS_DEBUG, "\n");
189
190 return smi_sts;
191}
192
193/* Print, clear, and return SMI status */
194u32 clear_smi_status(void)
195{
196 return print_smi_status(reset_smi_status());
197}
198
199/* Enable SMI event */
200void enable_smi(u32 mask)
201{
202 u32 smi_en = inl(ACPI_BASE_ADDRESS + SMI_EN);
203 smi_en |= mask;
204 outl(smi_en, ACPI_BASE_ADDRESS + SMI_EN);
205}
206
207/* Disable SMI event */
208void disable_smi(u32 mask)
209{
210 u32 smi_en = inl(ACPI_BASE_ADDRESS + SMI_EN);
211 smi_en &= ~mask;
212 outl(smi_en, ACPI_BASE_ADDRESS + SMI_EN);
213}
214
Lee Leahyb0005132015-05-12 18:19:47 -0700215/*
216 * TCO
217 */
218
219/* Clear TCO status and return events that are enabled and active */
220static u32 reset_tco_status(void)
221{
Lee Leahy1d14b3e2015-05-12 18:23:27 -0700222 u16 tco1_sts;
223 u16 tco2_sts;
224 u16 tcobase;
Lee Leahyb0005132015-05-12 18:19:47 -0700225
Barnali Sarkar49eca132016-08-12 00:05:27 +0530226 tcobase = smbus_tco_regs();
Lee Leahyb0005132015-05-12 18:19:47 -0700227
Lee Leahy1d14b3e2015-05-12 18:23:27 -0700228 /* TCO Status 2 register*/
229 tco2_sts = inw(tcobase + TCO2_STS);
230 tco2_sts |= (TCO2_STS_SECOND_TO | TCO2_STS_BOOT);
231 outw(tco2_sts, tcobase + TCO2_STS);
Lee Leahyb0005132015-05-12 18:19:47 -0700232
Lee Leahy1d14b3e2015-05-12 18:23:27 -0700233 /* TCO Status 1 register*/
234 tco1_sts = inw(tcobase + TCO1_STS);
235
236 /* Clear SECOND_TO_STS bit */
237 if (tco2_sts & TCO2_STS_SECOND_TO)
238 outw(tco2_sts & ~TCO2_STS_SECOND_TO, tcobase + TCO2_STS);
239
240 return (tco2_sts << 16) | tco1_sts;
Lee Leahyb0005132015-05-12 18:19:47 -0700241}
242
243/* Print TCO status bits */
244static u32 print_tco_status(u32 tco_sts)
245{
246 const char *tco_sts_bits[] = {
247 [0] = "NMI2SMI",
248 [1] = "SW_TCO",
249 [2] = "TCO_INT",
250 [3] = "TIMEOUT",
251 [7] = "NEWCENTURY",
252 [8] = "BIOSWR",
253 [9] = "DMISCI",
254 [10] = "DMISMI",
255 [12] = "DMISERR",
256 [13] = "SLVSEL",
257 [16] = "INTRD_DET",
258 [17] = "SECOND_TO",
259 [18] = "BOOT",
260 [20] = "SMLINK_SLV"
261 };
262
263 if (!tco_sts)
264 return 0;
265
266 printk(BIOS_DEBUG, "TCO_STS: ");
267 print_status_bits(tco_sts, tco_sts_bits);
268 printk(BIOS_DEBUG, "\n");
269
270 return tco_sts;
271}
272
273/* Print, clear, and return TCO status */
274u32 clear_tco_status(void)
275{
276 return print_tco_status(reset_tco_status());
277}
278
279/* Enable TCO SCI */
280void enable_tco_sci(void)
281{
282 /* Clear pending events */
Lee Leahy1d14b3e2015-05-12 18:23:27 -0700283 outl(TCOSCI_STS, ACPI_BASE_ADDRESS + GPE0_STS(3));
Lee Leahyb0005132015-05-12 18:19:47 -0700284
285 /* Enable TCO SCI events */
286 enable_gpe(TCOSCI_EN);
287}
288
289
290/*
291 * GPE0
292 */
293
294/* Clear a GPE0 status and return events that are enabled and active */
295static u32 reset_gpe(u16 sts_reg, u16 en_reg)
296{
297 u32 gpe0_sts = inl(ACPI_BASE_ADDRESS + sts_reg);
298 u32 gpe0_en = inl(ACPI_BASE_ADDRESS + en_reg);
299
300 outl(gpe0_sts, ACPI_BASE_ADDRESS + sts_reg);
301
302 /* Only report enabled events */
303 return gpe0_sts & gpe0_en;
304}
305
306/* Print GPE0 status bits */
307static u32 print_gpe_status(u32 gpe0_sts, const char *bit_names[])
308{
309 if (!gpe0_sts)
310 return 0;
311
312 printk(BIOS_DEBUG, "GPE0_STS: ");
313 print_status_bits(gpe0_sts, bit_names);
314 printk(BIOS_DEBUG, "\n");
315
316 return gpe0_sts;
317}
318
319/* Print GPE0 GPIO status bits */
320static u32 print_gpe_gpio(u32 gpe0_sts, int start)
321{
322 if (!gpe0_sts)
323 return 0;
324
325 printk(BIOS_DEBUG, "GPE0_STS: ");
326 print_gpio_status(gpe0_sts, start);
327 printk(BIOS_DEBUG, "\n");
328
329 return gpe0_sts;
330}
331
332/* Clear all GPE status and return "standard" GPE event status */
333u32 clear_gpe_status(void)
334{
335 const char *gpe0_sts_3_bits[] = {
336 [1] = "HOTPLUG",
337 [2] = "SWGPE",
338 [6] = "TCO_SCI",
339 [7] = "SMB_WAK",
340 [9] = "PCI_EXP",
341 [10] = "BATLOW",
342 [11] = "PME",
343 [12] = "ME",
344 [13] = "PME_B0",
Aaron Durbin7f788492015-07-24 17:10:31 -0500345 [14] = "eSPI",
346 [15] = "GPIO Tier-2",
347 [16] = "LAN_WAKE",
Lee Leahyb0005132015-05-12 18:19:47 -0700348 [18] = "WADT"
349 };
350
351 print_gpe_gpio(reset_gpe(GPE0_STS(GPE_31_0), GPE0_EN(GPE_31_0)), 0);
352 print_gpe_gpio(reset_gpe(GPE0_STS(GPE_63_32), GPE0_EN(GPE_63_32)), 32);
Aaron Durbin7f788492015-07-24 17:10:31 -0500353 print_gpe_gpio(reset_gpe(GPE0_STS(GPE_95_64), GPE0_EN(GPE_95_64)), 64);
Lee Leahyb0005132015-05-12 18:19:47 -0700354 return print_gpe_status(reset_gpe(GPE0_STS(GPE_STD), GPE0_EN(GPE_STD)),
355 gpe0_sts_3_bits);
356}
357
Duncan Laurie64ce1d12016-10-25 20:05:31 -0700358/* Read and clear GPE status (defined in arch/acpi.h) */
359int acpi_get_gpe(int gpe)
360{
361 int bank;
362 uint32_t mask, sts;
363
364 if (gpe < 0 || gpe > GPE0_WADT)
365 return -1;
366
367 bank = gpe / 32;
368 mask = 1 << (gpe % 32);
369
370 sts = inl(ACPI_BASE_ADDRESS + GPE0_STS(bank));
371 if (sts & mask) {
372 outl(mask, ACPI_BASE_ADDRESS + GPE0_STS(bank));
373 return 1;
374 }
375 return 0;
376}
377
Lee Leahyb0005132015-05-12 18:19:47 -0700378/* Enable all requested GPE */
379void enable_all_gpe(u32 set1, u32 set2, u32 set3, u32 set4)
380{
381 outl(set1, ACPI_BASE_ADDRESS + GPE0_EN(GPE_31_0));
382 outl(set2, ACPI_BASE_ADDRESS + GPE0_EN(GPE_63_32));
Aaron Durbin7f788492015-07-24 17:10:31 -0500383 outl(set3, ACPI_BASE_ADDRESS + GPE0_EN(GPE_95_64));
Lee Leahyb0005132015-05-12 18:19:47 -0700384 outl(set4, ACPI_BASE_ADDRESS + GPE0_EN(GPE_STD));
385}
386
387/* Disable all GPE */
388void disable_all_gpe(void)
389{
390 enable_all_gpe(0, 0, 0, 0);
391}
392
393/* Enable a standard GPE */
394void enable_gpe(u32 mask)
395{
396 u32 gpe0_en = inl(ACPI_BASE_ADDRESS + GPE0_EN(GPE_STD));
397 gpe0_en |= mask;
398 outl(gpe0_en, ACPI_BASE_ADDRESS + GPE0_EN(GPE_STD));
399}
400
401/* Disable a standard GPE */
402void disable_gpe(u32 mask)
403{
404 u32 gpe0_en = inl(ACPI_BASE_ADDRESS + GPE0_EN(GPE_STD));
405 gpe0_en &= ~mask;
406 outl(gpe0_en, ACPI_BASE_ADDRESS + GPE0_EN(GPE_STD));
407}
408
409int acpi_sci_irq(void)
410{
Lee Leahy1d14b3e2015-05-12 18:23:27 -0700411 int scis = pci_read_config32(PCH_DEV_PMC, ACTL) & SCI_IRQ_SEL;
Lee Leahyb0005132015-05-12 18:19:47 -0700412 int sci_irq = 9;
413
414 /* Determine how SCI is routed. */
415 switch (scis) {
416 case SCIS_IRQ9:
417 case SCIS_IRQ10:
418 case SCIS_IRQ11:
419 sci_irq = scis - SCIS_IRQ9 + 9;
420 break;
421 case SCIS_IRQ20:
422 case SCIS_IRQ21:
423 case SCIS_IRQ22:
424 case SCIS_IRQ23:
425 sci_irq = scis - SCIS_IRQ20 + 20;
426 break;
427 default:
428 printk(BIOS_DEBUG, "Invalid SCI route! Defaulting to IRQ9.\n");
429 sci_irq = 9;
430 break;
431 }
432
433 printk(BIOS_DEBUG, "SCI is IRQ%d\n", sci_irq);
434 return sci_irq;
435}
Lee Leahy1d14b3e2015-05-12 18:23:27 -0700436
437uint8_t *pmc_mmio_regs(void)
438{
439 uint32_t reg32;
440
441 reg32 = pci_read_config32(PCH_DEV_PMC, PWRMBASE);
442
443 /* 4KiB alignment. */
444 reg32 &= ~0xfff;
445
446 return (void *)(uintptr_t)reg32;
447}
448
Barnali Sarkar49eca132016-08-12 00:05:27 +0530449uint16_t smbus_tco_regs(void)
Lee Leahy1d14b3e2015-05-12 18:23:27 -0700450{
451 uint16_t reg16;
452
453 reg16 = pci_read_config16(PCH_DEV_SMBUS, TCOBASE);
454
455 reg16 &= ~0x1f;
456
457 return reg16;
458}
Aaron Durbin38613d02016-07-14 00:56:58 -0500459
460void poweroff(void)
461{
462 enable_pm1_control(SLP_EN | (SLP_TYP_S5 << SLP_TYP_SHIFT));
Furquan Shaikh97e0a652016-08-18 21:42:36 -0700463
464 /*
465 * Setting SLP_TYP_S5 in PM1 triggers SLP_SMI, which is handled by SMM
466 * to transition to S5 state. If halt is called in SMM, then it prevents
467 * the SMI handler from being triggered and system never enters S5.
468 */
469 if (!ENV_SMM)
470 halt();
Aaron Durbin38613d02016-07-14 00:56:58 -0500471}
Duncan Laurief0ba2252016-10-25 20:03:56 -0700472
473void pmc_gpe_init(void)
474{
475 ROMSTAGE_CONST struct soc_intel_skylake_config *config;
476 ROMSTAGE_CONST struct device *dev = dev_find_slot(0, PCH_DEVFN_PMC);
477 uint8_t *pmc_regs;
478 uint32_t gpio_cfg;
479 uint32_t gpio_cfg_reg;
480 const uint32_t gpio_cfg_mask =
481 (GPE0_DWX_MASK << GPE0_DW0_SHIFT) |
482 (GPE0_DWX_MASK << GPE0_DW1_SHIFT) |
483 (GPE0_DWX_MASK << GPE0_DW2_SHIFT);
484
485 /* Look up the device in devicetree */
486 if (!dev || !dev->chip_info) {
487 printk(BIOS_ERR, "BUG! Could not find SOC devicetree config\n");
488 return;
489 }
490 config = dev->chip_info;
491 pmc_regs = pmc_mmio_regs();
492
493 /* Route the GPIOs to the GPE0 block. Determine that all values
494 * are different, and if they aren't use the reset values. */
495 gpio_cfg = 0;
496 if (config->gpe0_dw0 == config->gpe0_dw1 ||
497 config->gpe0_dw1 == config->gpe0_dw2) {
498 printk(BIOS_INFO, "PMC: Using default GPE route.\n");
499 gpio_cfg = read32(pmc_regs + GPIO_CFG);
500 } else {
501 gpio_cfg |= (uint32_t)config->gpe0_dw0 << GPE0_DW0_SHIFT;
502 gpio_cfg |= (uint32_t)config->gpe0_dw1 << GPE0_DW1_SHIFT;
503 gpio_cfg |= (uint32_t)config->gpe0_dw2 << GPE0_DW2_SHIFT;
504 }
505 gpio_cfg_reg = read32(pmc_regs + GPIO_CFG) & ~gpio_cfg_mask;
506 gpio_cfg_reg |= gpio_cfg & gpio_cfg_mask;
507 write32(pmc_regs + GPIO_CFG, gpio_cfg_reg);
508
509 /* Set the routes in the GPIO communities as well. */
510 gpio_route_gpe(gpio_cfg_reg >> GPE0_DW0_SHIFT);
511
512 /* Set GPE enables based on devictree. */
513 enable_all_gpe(config->gpe0_en_1, config->gpe0_en_2,
514 config->gpe0_en_3, config->gpe0_en_4);
515}