From 1cc73074d0540caa8dd7ea9501afefea2e410791 Mon Sep 17 00:00:00 2001 From: Mulin Chao Date: Thu, 22 Apr 2021 01:45:23 -0700 Subject: [PATCH] driver: gpio: npcx: fixed leakage current in npcx7 series. It was found that npcx7 series' GPIOs which support low-voltage power supply, there is an excessive power consumption if they are selected to low-voltage mode and their input voltage is 1.8V. To avoid this excessive power consumption, this CL suspends the connection between IO pads and hardware instances before ec enters deep sleep mode. Then restore them after waking up. Signed-off-by: Mulin Chao --- drivers/gpio/gpio_npcx.c | 28 ++++++++++++++++++++++++ drivers/interrupt_controller/intc_miwu.c | 14 ++++++++++++ soc/arm/nuvoton_npcx/common/scfg.c | 21 ++++++++++++++++++ soc/arm/nuvoton_npcx/common/soc_gpio.h | 16 ++++++++++++++ soc/arm/nuvoton_npcx/common/soc_miwu.h | 14 ++++++++++++ soc/arm/nuvoton_npcx/common/soc_pins.h | 16 ++++++++++++++ soc/arm/nuvoton_npcx/npcx7/power.c | 10 +++++++++ 7 files changed, 119 insertions(+) diff --git a/drivers/gpio/gpio_npcx.c b/drivers/gpio/gpio_npcx.c index 204126d90df313..9f713adb892fc8 100644 --- a/drivers/gpio/gpio_npcx.c +++ b/drivers/gpio/gpio_npcx.c @@ -58,6 +58,34 @@ const struct device *npcx_get_gpio_dev(int port) return gpio_devs[port]; } +void npcx_gpio_enable_io_pads(const struct device *dev, int pin) +{ + const struct gpio_npcx_config *const config = DRV_CONFIG(dev); + const struct npcx_wui *io_wui = &config->wui_maps[pin]; + + /* + * If this pin is configurred as a GPIO interrupt source, do not + * implement bypass. Or ec cannot wake up via this event. + */ + if (pin < NPCX_GPIO_PORT_PIN_NUM && !npcx_miwu_irq_get_state(io_wui)) { + npcx_miwu_io_enable(io_wui); + } +} + +void npcx_gpio_disable_io_pads(const struct device *dev, int pin) +{ + const struct gpio_npcx_config *const config = DRV_CONFIG(dev); + const struct npcx_wui *io_wui = &config->wui_maps[pin]; + + /* + * If this pin is configurred as a GPIO interrupt source, do not + * implement bypass. Or ec cannot wake up via this event. + */ + if (pin < NPCX_GPIO_PORT_PIN_NUM && !npcx_miwu_irq_get_state(io_wui)) { + npcx_miwu_io_disable(io_wui); + } +} + /* GPIO api functions */ static int gpio_npcx_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags) diff --git a/drivers/interrupt_controller/intc_miwu.c b/drivers/interrupt_controller/intc_miwu.c index 541a6ecf1ec3b9..1b365bb0ab4196 100644 --- a/drivers/interrupt_controller/intc_miwu.c +++ b/drivers/interrupt_controller/intc_miwu.c @@ -170,6 +170,20 @@ void npcx_miwu_irq_disable(const struct npcx_wui *wui) NPCX_WKEN(base, wui->group) &= ~BIT(wui->bit); } +void npcx_miwu_io_enable(const struct npcx_wui *wui) +{ + const uint32_t base = DRV_CONFIG(miwu_devs[wui->table])->base; + + NPCX_WKINEN(base, wui->group) |= BIT(wui->bit); +} + +void npcx_miwu_io_disable(const struct npcx_wui *wui) +{ + const uint32_t base = DRV_CONFIG(miwu_devs[wui->table])->base; + + NPCX_WKINEN(base, wui->group) &= ~BIT(wui->bit); +} + bool npcx_miwu_irq_get_state(const struct npcx_wui *wui) { const uint32_t base = DRV_CONFIG(miwu_devs[wui->table])->base; diff --git a/soc/arm/nuvoton_npcx/common/scfg.c b/soc/arm/nuvoton_npcx/common/scfg.c index d89bde88022916..3f1aa026403917 100644 --- a/soc/arm/nuvoton_npcx/common/scfg.c +++ b/soc/arm/nuvoton_npcx/common/scfg.c @@ -5,10 +5,13 @@ */ #include +#include #include #include #include +#include "soc_gpio.h" + #include LOG_MODULE_REGISTER(pimux_npcx, LOG_LEVEL_ERR); @@ -106,6 +109,24 @@ void npcx_lvol_pads_configure(void) } } +void npcx_lvol_restore_io_pads(void) +{ + for (int i = 0; i < ARRAY_SIZE(def_lvols); i++) { + npcx_gpio_enable_io_pads( + npcx_get_gpio_dev(def_lvols[i].io_port), + def_lvols[i].io_bit); + } +} + +void npcx_lvol_suspend_io_pads(void) +{ + for (int i = 0; i < ARRAY_SIZE(def_lvols); i++) { + npcx_gpio_disable_io_pads( + npcx_get_gpio_dev(def_lvols[i].io_port), + def_lvols[i].io_bit); + } +} + void npcx_pinctrl_i2c_port_sel(int controller, int port) { struct glue_reg *const inst_glue = HAL_GLUE_INST(); diff --git a/soc/arm/nuvoton_npcx/common/soc_gpio.h b/soc/arm/nuvoton_npcx/common/soc_gpio.h index 711182e8a155a4..bf36245c04c4e2 100644 --- a/soc/arm/nuvoton_npcx/common/soc_gpio.h +++ b/soc/arm/nuvoton_npcx/common/soc_gpio.h @@ -24,6 +24,22 @@ extern "C" { */ const struct device *npcx_get_gpio_dev(int port); +/** + * @brief Enable the connection between io pads and GPIO instance + * + * @param dev Pointer to device structure for the gpio driver instance. + * @param pin Pin number. + */ +void npcx_gpio_enable_io_pads(const struct device *dev, int pin); + +/** + * @brief Disable the connection between io pads and GPIO instance + * + * @param dev Pointer to device structure for the gpio driver instance. + * @param pin Pin number. + */ +void npcx_gpio_disable_io_pads(const struct device *dev, int pin); + #ifdef __cplusplus } #endif diff --git a/soc/arm/nuvoton_npcx/common/soc_miwu.h b/soc/arm/nuvoton_npcx/common/soc_miwu.h index e797e285618052..7526667f29ff81 100644 --- a/soc/arm/nuvoton_npcx/common/soc_miwu.h +++ b/soc/arm/nuvoton_npcx/common/soc_miwu.h @@ -130,6 +130,20 @@ void npcx_miwu_irq_enable(const struct npcx_wui *wui); */ void npcx_miwu_irq_disable(const struct npcx_wui *wui); +/** + * @brief Connect io to the wake-up input source + * + * @param wui A pointer on wake-up input source + */ +void npcx_miwu_io_enable(const struct npcx_wui *wui); + +/** + * @brief Disconnect io to the wake-up input source + * + * @param wui A pointer on wake-up input source + */ +void npcx_miwu_io_disable(const struct npcx_wui *wui); + /** * @brief Get interrupt state of the wake-up input source * diff --git a/soc/arm/nuvoton_npcx/common/soc_pins.h b/soc/arm/nuvoton_npcx/common/soc_pins.h index 8f0b64693c9336..d0b8b16a52b894 100644 --- a/soc/arm/nuvoton_npcx/common/soc_pins.h +++ b/soc/arm/nuvoton_npcx/common/soc_pins.h @@ -142,6 +142,22 @@ void npcx_pinctrl_psl_input_configure(void); */ bool npcx_pinctrl_psl_input_asserted(uint32_t i); +/** + * @brief Restore all connections between IO pads that support low-voltage power + * supply and GPIO hardware devices. This utility is used for solving a + * leakage current issue found in npcx7 series. The npcx9 and later + * series fixed the issue and needn't it. + */ +void npcx_lvol_restore_io_pads(void); + +/** + * @brief Disable all connections between IO pads that support low-voltage power + * supply and GPIO hardware devices. This utility is used for solving a + * leakage current issue found in npcx7 series. The npcx9 and later + * series fixed the issue and needn't it. + */ +void npcx_lvol_suspend_io_pads(void); + #ifdef __cplusplus } #endif diff --git a/soc/arm/nuvoton_npcx/npcx7/power.c b/soc/arm/nuvoton_npcx/npcx7/power.c index ce15fcd2f65598..cebb8bd380d4e6 100644 --- a/soc/arm/nuvoton_npcx/npcx7/power.c +++ b/soc/arm/nuvoton_npcx/npcx7/power.c @@ -118,6 +118,11 @@ static void npcx_power_enter_system_sleep(int slp_mode, int wk_mode) npcx_clock_control_turn_on_system_sleep(slp_mode == NPCX_DEEP_SLEEP, wk_mode == NPCX_INSTANT_WAKE_UP); + /* A bypass in npcx7 series to prevent leakage in low-voltage pads */ + if (IS_ENABLED(CONFIG_SOC_SERIES_NPCX7)) { + npcx_lvol_suspend_io_pads(); + } + /* Turn on host access wake-up interrupt. */ npcx_host_enable_access_interrupt(); @@ -139,6 +144,11 @@ static void npcx_power_enter_system_sleep(int slp_mode, int wk_mode) /* Turn off host access wake-up interrupt. */ npcx_host_disable_access_interrupt(); + /* A bypass in npcx7 series to prevent leakage in low-voltage pads */ + if (IS_ENABLED(CONFIG_SOC_SERIES_NPCX7)) { + npcx_lvol_restore_io_pads(); + } + /* Turn off system sleep mode. */ npcx_clock_control_turn_off_system_sleep(); }