From b0b956cc0b17a04ffdff7d15292ecb33b4b8235b Mon Sep 17 00:00:00 2001 From: "Hu, Hebo" Date: Wed, 8 May 2019 17:12:59 +0800 Subject: ish/ish5: fixed both-edge triggered gpio configuration blocking D0ix issue ISH PMU does not support both-edge interrupt triggered gpio configuration. If both edges are configured, then the ISH can't stay in low power mode because it will exit immediately. As a W/A, we scan all gpio pins which have been configured as both-edge triggered, and then temporarily set each gpio pin to the single edge trigger that is opposite of its value, then restore the both-edge trigger configuration immediately after exiting low power mode. BUG=b:132001235 BRANCH=none TEST= tested on arcada platform, console should freeze after entered low power mode Change-Id: I83a43d9fbee6cfd1a6820bdb44c1446f109ffb32 Signed-off-by: Hu, Hebo Reviewed-on: https://chromium-review.googlesource.com/1600310 Commit-Ready: ChromeOS CL Exonerator Bot Tested-by: Jett Rink Reviewed-by: Jett Rink Reviewed-by: Hebo Hu Reviewed-by: Jack Rosenthal --- chip/ish/aontaskfw/ish_aontask.c | 5 ++ chip/ish/power_mgt.c | 134 +++++++++++++++++++++++++++------------ 2 files changed, 97 insertions(+), 42 deletions(-) diff --git a/chip/ish/aontaskfw/ish_aontask.c b/chip/ish/aontaskfw/ish_aontask.c index 95b6d9ce5d..7c0809b4a2 100644 --- a/chip/ish/aontaskfw/ish_aontask.c +++ b/chip/ish/aontaskfw/ish_aontask.c @@ -546,6 +546,11 @@ static void handle_reset(int pm_state) /* disable watch dog */ WDT_CONTROL &= ~WDT_CONTROL_ENABLE_BIT; + /* disable all gpio interrupts */ + ISH_GPIO_GRER = 0; + ISH_GPIO_GFER = 0; + ISH_GPIO_GIMR = 0; + /* disable CSME CSR irq */ IPC_PIMR &= ~IPC_PIMR_CSME_CSR_BIT; diff --git a/chip/ish/power_mgt.c b/chip/ish/power_mgt.c index 12645ab6b9..7fe5031b32 100644 --- a/chip/ish/power_mgt.c +++ b/chip/ish/power_mgt.c @@ -265,33 +265,73 @@ static void handle_reset_in_aontask(int pm_state) static void enter_d0i0(void) { - timestamp_t t0, t1; - - t0 = get_time(); + uint32_t t0, t1; + t0 = __hw_clock_source_read(); pm_ctx.aon_share->pm_state = ISH_PM_STATE_D0I0; /* halt ISH cpu, will wakeup from any interrupt */ ish_mia_halt(); - t1 = get_time(); - + t1 = __hw_clock_source_read(); pm_ctx.aon_share->pm_state = ISH_PM_STATE_D0; - - pm_stats.d0i0_time_us += t1.val - t0.val; + pm_stats.d0i0_time_us += t1 - t0; pm_stats.d0i0_cnt++; } +/** + * ISH PMU does not support both-edge interrupt triggered gpio configuration. + * If both edges are configured, then the ISH can't stay in low poer mode + * because it will exit immediately. + * + * As a workaround, we scan all gpio pins which have been configured as + * both-edge triggered, and then temporarily set each gpio pin to the single + * edge trigger that is opposite of its value, then restore the both-edge + * trigger configuration immediately after exiting low power mode. + */ +static uint32_t __unused convert_both_edge_gpio_to_single_edge(void) +{ + uint32_t both_edge_pins = 0; + int i = 0; + + /** + * scan GPIO GFER, GRER and GIMR registers to find the both edge + * interrupt trigger mode enabled pins. + */ + for (i = 0; i < 32; i++) { + if (ISH_GPIO_GIMR & BIT(i) && + ISH_GPIO_GRER & BIT(i) && + ISH_GPIO_GFER & BIT(i)) { + + /* Record the pin so we can restore it later */ + both_edge_pins |= BIT(i); + + if (ISH_GPIO_GPLR & BIT(i)) { + /* pin is high, just keep falling edge mode */ + ISH_GPIO_GRER &= ~BIT(i); + } else { + /* pin is low, just keep rising edge mode */ + ISH_GPIO_GFER &= ~BIT(i); + } + } + } + + return both_edge_pins; +} + +static void __unused restore_both_edge_gpio_config(uint32_t both_edge_pin_map) +{ + ISH_GPIO_GRER |= both_edge_pin_map; + ISH_GPIO_GFER |= both_edge_pin_map; +} + #ifdef CONFIG_ISH_PM_D0I1 static void enter_d0i1(void) { uint64_t current_irq_map; - - timestamp_t t0, t1; - t0 = get_time(); - - pm_ctx.aon_share->pm_state = ISH_PM_STATE_D0I1; + uint32_t both_edge_gpio_pins; + uint32_t t0, t1; /* only enable PMU wakeup interrupt */ current_irq_map = disable_all_interrupts(); @@ -301,6 +341,11 @@ static void enter_d0i1(void) task_enable_irq(ISH_RESET_PREP_IRQ); #endif + t0 = __hw_clock_source_read(); + pm_ctx.aon_share->pm_state = ISH_PM_STATE_D0I1; + + both_edge_gpio_pins = convert_both_edge_gpio_to_single_edge(); + /* enable Trunk Clock Gating (TCG) of ISH */ CCU_TCG_EN = 1; @@ -310,15 +355,16 @@ static void enter_d0i1(void) /* disable Trunk Clock Gating (TCG) of ISH */ CCU_TCG_EN = 0; - /* restore interrupts */ - task_disable_irq(ISH_PMU_WAKEUP_IRQ); - restore_interrupts(current_irq_map); + restore_both_edge_gpio_config(both_edge_gpio_pins); pm_ctx.aon_share->pm_state = ISH_PM_STATE_D0; - - t1 = get_time(); - pm_stats.d0i1_time_us += t1.val - t0.val; + t1 = __hw_clock_source_read(); + pm_stats.d0i1_time_us += t1 - t0; pm_stats.d0i1_cnt++; + + /* restore interrupts */ + task_disable_irq(ISH_PMU_WAKEUP_IRQ); + restore_interrupts(current_irq_map); } #endif @@ -328,11 +374,8 @@ static void enter_d0i1(void) static void enter_d0i2(void) { uint64_t current_irq_map; - - timestamp_t t0, t1; - t0 = get_time(); - - pm_ctx.aon_share->pm_state = ISH_PM_STATE_D0I2; + uint32_t both_edge_gpio_pins; + uint32_t t0, t1; /* only enable PMU wakeup interrupt */ current_irq_map = disable_all_interrupts(); @@ -342,6 +385,11 @@ static void enter_d0i2(void) task_enable_irq(ISH_RESET_PREP_IRQ); #endif + t0 = __hw_clock_source_read(); + pm_ctx.aon_share->pm_state = ISH_PM_STATE_D0I2; + + both_edge_gpio_pins = convert_both_edge_gpio_to_single_edge(); + /* enable Trunk Clock Gating (TCG) of ISH */ CCU_TCG_EN = 1; @@ -358,16 +406,16 @@ static void enter_d0i2(void) /* disable Trunk Clock Gating (TCG) of ISH */ CCU_TCG_EN = 0; - /* restore interrupts */ - task_disable_irq(ISH_PMU_WAKEUP_IRQ); - restore_interrupts(current_irq_map); - - t1 = get_time(); + restore_both_edge_gpio_config(both_edge_gpio_pins); + t1 = __hw_clock_source_read(); pm_ctx.aon_share->pm_state = ISH_PM_STATE_D0; - - pm_stats.d0i2_time_us += t1.val - t0.val; + pm_stats.d0i2_time_us += t1 - t0; pm_stats.d0i2_cnt++; + + /* restore interrupts */ + task_disable_irq(ISH_PMU_WAKEUP_IRQ); + restore_interrupts(current_irq_map); } #endif @@ -377,11 +425,8 @@ static void enter_d0i2(void) static void enter_d0i3(void) { uint64_t current_irq_map; - timestamp_t t0, t1; - - t0 = get_time(); - - pm_ctx.aon_share->pm_state = ISH_PM_STATE_D0I3; + uint32_t both_edge_gpio_pins; + uint32_t t0, t1; /* only enable PMU wakeup interrupt */ current_irq_map = disable_all_interrupts(); @@ -391,6 +436,11 @@ static void enter_d0i3(void) task_enable_irq(ISH_RESET_PREP_IRQ); #endif + t0 = __hw_clock_source_read(); + pm_ctx.aon_share->pm_state = ISH_PM_STATE_D0I3; + + both_edge_gpio_pins = convert_both_edge_gpio_to_single_edge(); + /* enable Trunk Clock Gating (TCG) of ISH */ CCU_TCG_EN = 1; @@ -407,16 +457,16 @@ static void enter_d0i3(void) /* disable Trunk Clock Gating (TCG) of ISH */ CCU_TCG_EN = 0; - /* restore interrupts */ - task_disable_irq(ISH_PMU_WAKEUP_IRQ); - restore_interrupts(current_irq_map); - - t1 = get_time(); + restore_both_edge_gpio_config(both_edge_gpio_pins); + t1 = __hw_clock_source_read(); pm_ctx.aon_share->pm_state = ISH_PM_STATE_D0; - - pm_stats.d0i3_time_us += t1.val - t0.val; + pm_stats.d0i3_time_us += t1 - t0; pm_stats.d0i3_cnt++; + + /* restore interrupts */ + task_disable_irq(ISH_PMU_WAKEUP_IRQ); + restore_interrupts(current_irq_map); } #endif -- cgit v1.2.1