summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHannah Williams <hannah.williams@intel.com>2016-02-04 20:13:34 -0800
committerMartin Roth <martinroth@google.com>2016-04-15 16:23:55 +0200
commit01bc897dfa4abd0f6fe3ca1896ade1f06e412979 (patch)
tree8d98216369aba3f52b61f751dd18acfbb7de9741
parent9c6c791351d78b11aaaa1f31d4085da16b5579f6 (diff)
soc/apollolake: Add helper functions to access Power Management Registers
Change-Id: I928efea33030e03cbbaead6812c617d20446f7c9 Signed-off-by: Hannah Williams <hannah.williams@intel.com> Reviewed-on: https://review.coreboot.org/14289 Reviewed-by: Aaron Durbin <adurbin@chromium.org> Tested-by: build bot (Jenkins)
-rw-r--r--src/soc/intel/apollolake/Makefile.inc3
-rw-r--r--src/soc/intel/apollolake/include/soc/pm.h117
-rw-r--r--src/soc/intel/apollolake/pmutil.c327
3 files changed, 444 insertions, 3 deletions
diff --git a/src/soc/intel/apollolake/Makefile.inc b/src/soc/intel/apollolake/Makefile.inc
index 3e307f5b86..f0a33f763d 100644
--- a/src/soc/intel/apollolake/Makefile.inc
+++ b/src/soc/intel/apollolake/Makefile.inc
@@ -26,8 +26,10 @@ romstage-$(CONFIG_SOC_UART_DEBUG) += uart_early.c
romstage-y += memmap.c
romstage-y += mmap_boot.c
romstage-y += tsc_freq.c
+romstage-y += pmutil.c
smm-y += placeholders.c
+smm-y += pmutil.c
ramstage-$(CONFIG_HAVE_ACPI_TABLES) += acpi.c
ramstage-y += cpu.c
@@ -42,6 +44,7 @@ ramstage-y += uart.c
ramstage-y += northbridge.c
ramstage-y += spi.c
ramstage-y += tsc_freq.c
+ramstage-y += pmutil.c
postcar-y += exit_car.S
postcar-y += memmap.c
diff --git a/src/soc/intel/apollolake/include/soc/pm.h b/src/soc/intel/apollolake/include/soc/pm.h
index bc32501700..fe7a4230e9 100644
--- a/src/soc/intel/apollolake/include/soc/pm.h
+++ b/src/soc/intel/apollolake/include/soc/pm.h
@@ -1,7 +1,7 @@
/*
* This file is part of the coreboot project.
*
- * Copyright (C) 2016 Intel Corp.
+ * Copyright (C) 2015-2016 Intel Corp.
* (Written by Lance Zhao <lijian.zhao@intel.com> for Intel Corp.)
*
* This program is free software; you can redistribute it and/or modify
@@ -18,19 +18,130 @@
#ifndef _SOC_APOLLOLAKE_PM_H_
#define _SOC_APOLLOLAKE_PM_H_
-/* ACPI_BASE_ADDRESS / PMBASE */
+#include <stdint.h>
+
+/* ACPI_BASE_ADDRESS */
#define PM1_STS 0x00
+#define WAK_STS (1 << 15)
+#define PWRBTN_STS (1 << 8)
+
#define PM1_EN 0x02
+#define PCIEXPWAK_DIS (1 << 14)
+#define USB_WAKE_EN (1 << 13)
+#define RTC_EN (1 << 10)
+#define PWRBTN_EN (1 << 8)
+#define GBL_EN (1 << 5)
+#define TMROF_EN (1 << 0)
+
#define PM1_CNT 0x04
+#define SLP_EN (1 << 13)
+#define SLP_TYP_SHIFT 10
+#define SLP_TYP (7 << SLP_TYP_SHIFT)
+#define SLP_TYP_S0 0
+#define SLP_TYP_S3 5
+#define SLP_TYP_S4 6
+#define SLP_TYP_S5 7
+#define SCI_EN (1 << 0)
+
#define PM1_TMR 0x08
+
#define SMI_EN 0x40
+
+#define SMI_OCP_CSE 27
+#define SMI_SPI 26
+#define SMI_SPI_SSMI 25
+#define SMI_SCC2 21
+#define SMI_PCIE 20
+#define SMI_SCS 19
+#define SMI_HOST_SMBUS 18
+#define SMI_XHCI 17
+#define SMI_SMBUS 16
+#define SMI_SERIRQ 15
+#define SMI_PERIODIC 14
+#define SMI_TCO 13
+#define SMI_MCSMI 12
+#define SMI_GPIO_UNLOCK_SSMI 11
+#define SMI_GPIO 10
+#define SMI_BIOS_RLS 7
+#define SMI_SWSMI_TMR 6
+#define SMI_APMC 5
+#define SMI_SLP 4
+#define SMI_LEGACY_USB 3
+#define SMI_BIOS 2
+#define SMI_EOS 1
+#define SMI_GBL 0
+
+#define USB_EN (1 << SMI_XHCI) /* Legacy USB2 SMI logic */
+#define PERIODIC_EN (1 << SMI_PERIODIC) /* SMI on PERIODIC_STS in SMI_STS */
+#define TCO_EN (1 << SMI_TCO) /* Enable TCO Logic (BIOSWE et al) */
+#define BIOS_RLS (1 << SMI_BIOS_RLS) /* asserts SCI on bit set */
+#define SWSMI_TMR_EN (1 << SMI_SWSMI_TMR) /* start software smi timer on bit set */
+#define APMC_EN (1 << SMI_APMC) /* Writes to APM_CNT cause SMI# */
+#define SLP_SMI_EN (1 << SMI_SLP) /* Write to SLP_EN in PM1_CNT asserts SMI# */
+#define BIOS_EN (1 << SMI_BIOS) /* Assert SMI# on GBL_RLS bit */
+#define EOS (1 << SMI_EOS) /* End of SMI (deassert SMI#) */
+#define GBL_SMI_EN (1 << SMI_GBL) /* Global SMI Enable */
+
#define SMI_STS 0x44
#define GPE_CNTL 0x50
#define DEVACT_STS 0x4c
+#define TCO_STS 0x64
+#define TCO_TIMEOUT (1 << 3)
+#define TCO1_CNT 0x68
#define GPE0_REG_MAX 4
+#define GPE0_REG_SIZE 32
#define GPE0_STS(x) (0x20 + (x * 4))
#define GPE0_EN(x) (0x30 + (x * 4))
+#define PME_B0_EN (1 << 13)
+
+/* Memory mapped IO registers behind PMC_BASE_ADDRESS */
+#define PRSTS 0x1000
+#define GEN_PMCON1 0x1020
+# define PWR_FLR (1 << 16)
+# define SUS_PWR_FLR (1 << 14)
+#define GEN_PMCON2 0x1024
+# define RPS (1 << 2)
+#define GEN_PMCON3 0x1028
+
+/* Generic sleep state types */
+#define SLEEP_STATE_S0 0
+#define SLEEP_STATE_S3 3
+#define SLEEP_STATE_S5 5
+
+/* Track power state from reset to log events. */
+struct chipset_power_state {
+ uint16_t pm1_sts;
+ uint16_t pm1_en;
+ uint32_t pm1_cnt;
+ uint32_t gpe0_sts[GPE0_REG_MAX];
+ uint32_t gpe0_en[GPE0_REG_MAX];
+ uint32_t tco_sts;
+ uint32_t prsts;
+ uint32_t gen_pmcon1;
+ uint32_t gen_pmcon2;
+ uint32_t gen_pmcon3;
+ uint32_t prev_sleep_state;
+} __attribute__((packed));
+
+int fill_power_state(struct chipset_power_state *ps);
+int chipset_prev_sleep_state(struct chipset_power_state *ps);
+
+/* Power Management Utility Functions. */
+uint32_t clear_smi_status(void);
+uint16_t clear_pm1_status(void);
+uint32_t clear_tco_status(void);
+uint32_t clear_gpe_status(void);
+void clear_pmc_status(void);
+uint32_t get_smi_en(void);
+void enable_smi(uint32_t mask);
+void disable_smi(uint32_t mask);
+void enable_pm1(uint16_t events);
+void enable_pm1_control(uint32_t mask);
+void disable_pm1_control(uint32_t mask);
+void enable_gpe(uint32_t mask);
+void disable_gpe(uint32_t mask);
+void disable_all_gpe(void);
-#endif /* _SOC_APOLLOLAKE_PM_H_ */
+#endif
diff --git a/src/soc/intel/apollolake/pmutil.c b/src/soc/intel/apollolake/pmutil.c
new file mode 100644
index 0000000000..ce822ffa19
--- /dev/null
+++ b/src/soc/intel/apollolake/pmutil.c
@@ -0,0 +1,327 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2013 Google Inc.
+ * Copyright (C) 2015-2016 Intel Corp.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <arch/io.h>
+#include <console/console.h>
+#include <rules.h>
+#include <device/pci_def.h>
+#include <soc/iomap.h>
+#include <soc/pm.h>
+#include <device/device.h>
+#include <device/pci.h>
+
+
+static void print_num_status_bits(int num_bits, uint32_t status,
+ const char * const bit_names[])
+{
+ int i;
+
+ if (!status)
+ return;
+
+ for (i = num_bits - 1; i >= 0; i--) {
+ if (status & (1 << i)) {
+ if (bit_names[i])
+ printk(BIOS_DEBUG, "%s ", bit_names[i]);
+ else
+ printk(BIOS_DEBUG, "BIT%d ", i);
+ }
+ }
+}
+
+static uint32_t print_smi_status(uint32_t smi_sts)
+{
+ static const char * const smi_sts_bits[] = {
+ [2] = "BIOS",
+ [3] = "LEGACY USB",
+ [4] = "SLP_SMI",
+ [5] = "APM",
+ [6] = "SWSMI_TMR",
+ [10]= "GPIO_SMI",
+ [11]= "GPIO_UNLOCK_SSMI",
+ [12] = "MCSMI",
+ [13] = "TCO",
+ [14] = "PERIODIC",
+ [15] = "SERIRQ",
+ [16] = "SMBUS_SMI",
+ [17] = "XHCI",
+ [18] = "HOST_SMBUS",
+ [19] = "SCS",
+ [20] = "PCI_EXP_SMI",
+ [21] = "SCC2",
+ [25] = "SPI_SSMI",
+ [26] = "SPI",
+ [27] = "OCP_CSE",
+ };
+
+ if (!smi_sts)
+ return 0;
+
+ printk(BIOS_DEBUG, "SMI_STS: ");
+ print_num_status_bits(ARRAY_SIZE(smi_sts_bits), smi_sts, smi_sts_bits);
+ printk(BIOS_DEBUG, "\n");
+
+ return smi_sts;
+}
+
+static uint32_t reset_smi_status(void)
+{
+ uint32_t smi_sts = inl(ACPI_PMIO_BASE + SMI_STS);
+ outl(smi_sts, ACPI_PMIO_BASE + SMI_STS);
+ return smi_sts;
+}
+
+uint32_t clear_smi_status(void)
+{
+ return print_smi_status(reset_smi_status());
+}
+
+uint32_t get_smi_en(void)
+{
+ return inl(ACPI_PMIO_BASE + SMI_EN);
+}
+
+void enable_smi(uint32_t mask)
+{
+ uint32_t smi_en = inl(ACPI_PMIO_BASE + SMI_EN);
+ smi_en |= mask;
+ outl(smi_en, ACPI_PMIO_BASE + SMI_EN);
+}
+
+void disable_smi(uint32_t mask)
+{
+ uint32_t smi_en = inl(ACPI_PMIO_BASE + SMI_EN);
+ smi_en &= ~mask;
+ outl(smi_en, ACPI_PMIO_BASE + SMI_EN);
+}
+
+void enable_pm1_control(uint32_t mask)
+{
+ uint32_t pm1_cnt = inl(ACPI_PMIO_BASE + PM1_CNT);
+ pm1_cnt |= mask;
+ outl(pm1_cnt, ACPI_PMIO_BASE + PM1_CNT);
+}
+
+void disable_pm1_control(uint32_t mask)
+{
+ uint32_t pm1_cnt = inl(ACPI_PMIO_BASE + PM1_CNT);
+ pm1_cnt &= ~mask;
+ outl(pm1_cnt, ACPI_PMIO_BASE + PM1_CNT);
+}
+
+static uint16_t reset_pm1_status(void)
+{
+ uint16_t pm1_sts = inw(ACPI_PMIO_BASE + PM1_STS);
+ outw(pm1_sts, ACPI_PMIO_BASE + PM1_STS);
+ return pm1_sts;
+}
+
+static uint16_t print_pm1_status(uint16_t pm1_sts)
+{
+ static const char * const pm1_sts_bits[] = {
+ [0] = "TMROF",
+ [5] = "GBL",
+ [8] = "PWRBTN",
+ [10] = "RTC",
+ [11] = "PRBTNOR",
+ [13] = "USB",
+ [14] = "PCIEXPWAK",
+ [15] = "WAK",
+ };
+
+ if (!pm1_sts)
+ return 0;
+
+ printk(BIOS_SPEW, "PM1_STS: ");
+ print_num_status_bits(ARRAY_SIZE(pm1_sts_bits), pm1_sts, pm1_sts_bits);
+ printk(BIOS_SPEW, "\n");
+
+ return pm1_sts;
+}
+
+uint16_t clear_pm1_status(void)
+{
+ return print_pm1_status(reset_pm1_status());
+}
+
+void enable_pm1(uint16_t events)
+{
+ outw(events, ACPI_PMIO_BASE + PM1_EN);
+}
+
+static uint32_t print_tco_status(uint32_t tco_sts)
+{
+ static const char * const tco_sts_bits[] = {
+ [3] = "TIMEOUT",
+ [17] = "SECOND_TO",
+ };
+
+ if (!tco_sts)
+ return 0;
+
+ printk(BIOS_DEBUG, "TCO_STS: ");
+ print_num_status_bits(ARRAY_SIZE(tco_sts_bits), tco_sts, tco_sts_bits);
+ printk(BIOS_DEBUG, "\n");
+
+ return tco_sts;
+}
+
+static uint32_t reset_tco_status(void)
+{
+ uint32_t tco_sts = inl(ACPI_PMIO_BASE + TCO_STS);
+ uint32_t tco_en = inl(ACPI_PMIO_BASE + TCO1_CNT);
+
+ outl(tco_sts, ACPI_PMIO_BASE + TCO_STS);
+ return tco_sts & tco_en;
+}
+
+uint32_t clear_tco_status(void)
+{
+ return print_tco_status(reset_tco_status());
+}
+
+void enable_gpe(uint32_t mask)
+{
+ uint32_t gpe0a_en = inl(ACPI_PMIO_BASE + GPE0_EN(0));
+ gpe0a_en |= mask;
+ outl(gpe0a_en, ACPI_PMIO_BASE + GPE0_EN(0));
+}
+
+void disable_gpe(uint32_t mask)
+{
+ uint32_t gpe0a_en = inl(ACPI_PMIO_BASE + GPE0_EN(0));
+ gpe0a_en &= ~mask;
+ outl(gpe0a_en, ACPI_PMIO_BASE + GPE0_EN(0));
+}
+
+void disable_all_gpe(void)
+{
+ disable_gpe(~0);
+}
+
+
+static uint32_t reset_gpe_status(void)
+{
+ uint32_t gpe_sts = inl(ACPI_PMIO_BASE + GPE0_STS(0));
+ outl(gpe_sts, ACPI_PMIO_BASE + GPE0_STS(0));
+ return gpe_sts;
+}
+
+static uint32_t print_gpe_sts(uint32_t gpe_sts)
+{
+ static const char * const gpe_sts_bits[] = {
+ [0] = "PCIE_SCI",
+ [2] = "SWGPE",
+ [3] = "PCIE_WAKE0",
+ [4] = "PUNIT",
+ [6] = "PCIE_WAKE1",
+ [7] = "PCIE_WAKE2",
+ [8] = "PCIE_WAKE3",
+ [9] = "PCI_EXP",
+ [10] = "BATLOW",
+ [11] = "CSE_PME",
+ [12] = "XDCI_PME",
+ [13] = "XHCI_PME",
+ [14] = "AVS_PME",
+ [15] = "GPIO_TIER1_SCI",
+ [16] = "SMB_WAK",
+ [17] = "SATA_PME",
+ };
+
+ if (!gpe_sts)
+ return gpe_sts;
+
+ printk(BIOS_DEBUG, "GPE0a_STS: ");
+ print_num_status_bits(ARRAY_SIZE(gpe_sts_bits), gpe_sts, gpe_sts_bits);
+ printk(BIOS_DEBUG, "\n");
+
+ return gpe_sts;
+}
+
+uint32_t clear_gpe_status(void)
+{
+ return print_gpe_sts(reset_gpe_status());
+}
+
+void clear_pmc_status(void)
+{
+ uint32_t prsts;
+ uint32_t gen_pmcon1;
+
+ prsts = read32((void *)(PMC_BAR0 + PRSTS));
+ gen_pmcon1 = read32((void *)(PMC_BAR0 + GEN_PMCON1));
+
+ /* Clear the status bits. The RPS field is cleared on a 0 write. */
+ write32((void *)(PMC_BAR0 + GEN_PMCON1), gen_pmcon1 & ~RPS);
+ write32((void *)(PMC_BAR0 + PRSTS), prsts);
+}
+
+
+/* Return 0, 3, or 5 to indicate the previous sleep state. */
+int chipset_prev_sleep_state(struct chipset_power_state *ps)
+{
+ /* Default to S0. */
+ int prev_sleep_state = SLEEP_STATE_S0;
+
+ if (ps->pm1_sts & WAK_STS) {
+ switch ((ps->pm1_cnt & SLP_TYP) >> SLP_TYP_SHIFT) {
+#if IS_ENABLED(CONFIG_HAVE_ACPI_RESUME)
+ case SLP_TYP_S3:
+ prev_sleep_state = SLEEP_STATE_S3;
+ break;
+#endif
+ case SLP_TYP_S5:
+ prev_sleep_state = SLEEP_STATE_S5;
+ break;
+ }
+ }
+ return prev_sleep_state;
+}
+
+/* returns prev_sleep_state */
+int fill_power_state(struct chipset_power_state *ps)
+{
+ int i;
+ ps->pm1_sts = inw(ACPI_PMIO_BASE + PM1_STS);
+ ps->pm1_en = inw(ACPI_PMIO_BASE + PM1_EN);
+ ps->pm1_cnt = inl(ACPI_PMIO_BASE + PM1_CNT);
+ ps->tco_sts = inl(ACPI_PMIO_BASE + TCO_STS);
+ ps->prsts = read32((void *)(PMC_BAR0 + PRSTS));
+ ps->gen_pmcon1 =read32((void *)(PMC_BAR0 + GEN_PMCON1));
+ ps->gen_pmcon2 = read32((void *)(PMC_BAR0 + GEN_PMCON2));
+ ps->gen_pmcon3 = read32((void *)(PMC_BAR0 + GEN_PMCON3));
+
+ ps->prev_sleep_state = chipset_prev_sleep_state(ps);
+
+ printk(BIOS_DEBUG, "pm1_sts: %04x pm1_en: %04x pm1_cnt: %08x\n",
+ ps->pm1_sts, ps->pm1_en, ps->pm1_cnt);
+ printk(BIOS_DEBUG, "prsts: %08x tco_sts: %08x\n",
+ ps->prsts, ps->tco_sts);
+ printk(BIOS_DEBUG,
+ "gen_pmcon1: %08x gen_pmcon2: %08x gen_pmcon3: %08x\n",
+ ps->gen_pmcon1, ps->gen_pmcon2, ps->gen_pmcon3);
+ printk(BIOS_DEBUG, "smi_en: %08x smi_sts: %08x\n",
+ inl(ACPI_PMIO_BASE + SMI_EN), inl(ACPI_PMIO_BASE + SMI_STS));
+ for (i=0; i < GPE0_REG_MAX; i++) {
+ ps->gpe0_sts[i] = inl(ACPI_PMIO_BASE + GPE0_STS(i));
+ ps->gpe0_en[i] = inl(ACPI_PMIO_BASE + GPE0_EN(i));
+ printk(BIOS_DEBUG, "gpe0_sts[%d]: %08x gpe0_en[%d]: %08x\n",
+ i, ps->gpe0_sts[i], i, ps->gpe0_en[i]);
+ }
+ printk(BIOS_DEBUG, "prev_sleep_state %d\n", ps->prev_sleep_state);
+ return ps->prev_sleep_state;
+}