From 60b4618a841a2aecd5a9a37bd451f1e6af7e6b1a Mon Sep 17 00:00:00 2001 From: Shaunak Saha Date: Tue, 2 Aug 2016 17:25:13 -0700 Subject: soc/apollolake: Return correct wake status in _SWS Wake status is calculated from the four pairs of gpe0 in cbmem CBMEM_ID_POWER_STATE which is filled very early in romstage and depends on the routing information in PMC GPE_CFG register. Coreboot sets the proper value of routing based on devicetree from pmc_init. But when system goes to S3 on waking up PMC is writing default values again in GPE_CFG which results in returning wrong wake status in _SWS. This patch corrects that behaviour by correcting the gpe0 pairs in cbmem after PMC sets the routing table in resume path. BUG=chrome-os-partner:54876 TEST=On resume through powerbtn, lidopen, keyboard press, etc. we are getting proper wake status. Change-Id: I5942d5c20d8c6aef73468dc611190bb7c49c7c7a Signed-off-by: Shaunak Saha Reviewed-on: https://review.coreboot.org/16040 Tested-by: build bot (Jenkins) Reviewed-by: Aaron Durbin Reviewed-by: Brandon Breitenstein --- src/soc/intel/apollolake/Kconfig | 1 + src/soc/intel/apollolake/acpi.c | 35 +++++++++++++++++++++++++++++++ src/soc/intel/apollolake/include/soc/pm.h | 3 +++ src/soc/intel/apollolake/pmc.c | 3 +++ src/soc/intel/apollolake/pmutil.c | 25 ++++++++++++++++++++++ 5 files changed, 67 insertions(+) (limited to 'src') diff --git a/src/soc/intel/apollolake/Kconfig b/src/soc/intel/apollolake/Kconfig index 07456795b5..a85d94a49c 100644 --- a/src/soc/intel/apollolake/Kconfig +++ b/src/soc/intel/apollolake/Kconfig @@ -44,6 +44,7 @@ config CPU_SPECIFIC_OPTIONS select SMM_TSEG select SOC_INTEL_COMMON select SOC_INTEL_COMMON_ACPI + select SOC_INTEL_COMMON_ACPI_WAKE_SOURCE select SOC_INTEL_COMMON_LPSS_I2C select SOC_INTEL_COMMON_SMI select SPI_FLASH diff --git a/src/soc/intel/apollolake/acpi.c b/src/soc/intel/apollolake/acpi.c index 262e140e6c..4f4276ad72 100644 --- a/src/soc/intel/apollolake/acpi.c +++ b/src/soc/intel/apollolake/acpi.c @@ -168,6 +168,41 @@ static void acpi_create_gnvs(struct global_nvs_t *gnvs) /* Enable DPTF based on mainboard configuration */ gnvs->dpte = cfg->dptf_enable; + + /* Set unknown wake source */ + gnvs->pm1i = ~0ULL; +} + +/* Save wake source information for calculating ACPI _SWS values */ +int soc_fill_acpi_wake(uint32_t *pm1, uint32_t **gpe0) +{ + struct chipset_power_state *ps; + static uint32_t gpe0_sts[GPE0_REG_MAX]; + uint32_t pm1_en; + int i; + + ps = cbmem_find(CBMEM_ID_POWER_STATE); + if (ps == NULL) + return -1; + + /* + * PM1_EN to check the basic wake events which can happen through + * powerbtn or any other wake source like lidopen, key board press etc. + * WAK_STS bit is set when the system is in one of the sleep states + * (via the SLP_EN bit) and an enabled wake event occurs. Upon setting + * this bit, the PMC will transition the system to the ON state and + * can only be set by hardware and can only be cleared by writing a one + * to this bit position. + */ + pm1_en = ps->pm1_en | WAK_STS | RTC_EN | PWRBTN_EN; + *pm1 = ps->pm1_sts & pm1_en; + + /* Mask off GPE0 status bits that are not enabled */ + *gpe0 = &gpe0_sts[0]; + for (i = 0; i < GPE0_REG_MAX; i++) + gpe0_sts[i] = ps->gpe0_sts[i] & ps->gpe0_en[i]; + + return GPE0_REG_MAX; } void southbridge_inject_dsdt(device_t device) diff --git a/src/soc/intel/apollolake/include/soc/pm.h b/src/soc/intel/apollolake/include/soc/pm.h index d8eb50bd02..467c8f159c 100644 --- a/src/soc/intel/apollolake/include/soc/pm.h +++ b/src/soc/intel/apollolake/include/soc/pm.h @@ -25,6 +25,7 @@ #define PM1_STS 0x00 #define WAK_STS (1 << 15) +#define RTC_STS (1 << 10) #define PWRBTN_STS (1 << 8) #define PM1_EN 0x02 @@ -162,6 +163,8 @@ struct chipset_power_state { int fill_power_state(struct chipset_power_state *ps); int chipset_prev_sleep_state(struct chipset_power_state *ps); +/* Rewrite the gpe0 registers in cbmem to proper values as per routing table */ +void fixup_power_state(void); /* Power Management Utility Functions. */ uint32_t clear_smi_status(void); diff --git a/src/soc/intel/apollolake/pmc.c b/src/soc/intel/apollolake/pmc.c index 9d1be5c709..f303632cb2 100644 --- a/src/soc/intel/apollolake/pmc.c +++ b/src/soc/intel/apollolake/pmc.c @@ -117,6 +117,9 @@ static void pmc_gpe_init(void) /* Set the routes in the GPIO communities as well. */ gpio_route_gpe(dw1, dw2, dw3); + + /* Reset the power state in cbmem as routing */ + fixup_power_state(); } static void pmc_init(struct device *dev) diff --git a/src/soc/intel/apollolake/pmutil.c b/src/soc/intel/apollolake/pmutil.c index 61aa6375d3..dabc2683bc 100644 --- a/src/soc/intel/apollolake/pmutil.c +++ b/src/soc/intel/apollolake/pmutil.c @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -327,6 +328,30 @@ int chipset_prev_sleep_state(struct chipset_power_state *ps) return prev_sleep_state; } +/* + * This function re-writes the gpe0 register values in power state + * cbmem variable. After system wakes from sleep state internal PMC logic + * writes default values in GPE_CFG register which gives a wrong offset to + * calculate the wake reason. So we need to set it again to the routing + * table as per the devicetree. + */ +void fixup_power_state(void) +{ + int i; + struct chipset_power_state *ps; + + ps = cbmem_find(CBMEM_ID_POWER_STATE); + if (ps == NULL) + return; + + 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]); + } +} + /* returns prev_sleep_state */ int fill_power_state(struct chipset_power_state *ps) { -- cgit v1.2.3