From 71ed2e4b02b98e26263c80ebdd96a5771cab591b Mon Sep 17 00:00:00 2001 From: Jerzy Kasenberg Date: Mon, 20 May 2024 10:49:00 +0200 Subject: [PATCH] drivers: spi: Add power management Smartbond SPI Code adds pm action function that stores SPI configuration before PD_COM is allowed to be turned off. PM_DEVICE_RUNTIME scheme is also supported Signed-off-by: Jerzy Kasenberg --- .../da14695_dk_usb-pinctrl.dtsi | 16 +++ .../renesas/da14695_dk_usb/da14695_dk_usb.dts | 6 +- .../da1469x_dk_pro-pinctrl.dtsi | 16 +++ .../renesas/da1469x_dk_pro/da1469x_dk_pro.dts | 6 +- drivers/spi/spi_smartbond.c | 114 +++++++++++++++++- 5 files changed, 151 insertions(+), 7 deletions(-) diff --git a/boards/renesas/da14695_dk_usb/da14695_dk_usb-pinctrl.dtsi b/boards/renesas/da14695_dk_usb/da14695_dk_usb-pinctrl.dtsi index d16ad945fa8..1db25444b6c 100644 --- a/boards/renesas/da14695_dk_usb/da14695_dk_usb-pinctrl.dtsi +++ b/boards/renesas/da14695_dk_usb/da14695_dk_usb-pinctrl.dtsi @@ -52,6 +52,14 @@ }; }; + /omit-if-no-ref/ spi_sleep: spi_sleep { + group1 { + pinmux = , + , + ; + }; + }; + spi_controller: spi_controller { group1 { pinmux = < SMARTBOND_PINMUX(SPI_CLK, 0, 21) >, @@ -64,6 +72,14 @@ }; }; + /omit-if-no-ref/ spi2_sleep: spi2_sleep { + group1 { + pinmux = , + , + ; + }; + }; + spi2_controller: spi2_controller { group1 { pinmux = < SMARTBOND_PINMUX(SPI2_CLK, 1, 3) >, diff --git a/boards/renesas/da14695_dk_usb/da14695_dk_usb.dts b/boards/renesas/da14695_dk_usb/da14695_dk_usb.dts index eff5299dcff..4e86ee9221b 100644 --- a/boards/renesas/da14695_dk_usb/da14695_dk_usb.dts +++ b/boards/renesas/da14695_dk_usb/da14695_dk_usb.dts @@ -201,13 +201,15 @@ zephyr_udc0: &usbd { &spi { status = "okay"; pinctrl-0 = <&spi_controller>; - pinctrl-names = "default"; + pinctrl-1 = <&spi_sleep>; + pinctrl-names = "default", "sleep"; }; &spi2 { status = "okay"; pinctrl-0 = <&spi2_controller>; - pinctrl-names = "default"; + pinctrl-1 = <&spi2_sleep>; + pinctrl-names = "default", "sleep"; }; mikrobus_1_i2c: &i2c {}; diff --git a/boards/renesas/da1469x_dk_pro/da1469x_dk_pro-pinctrl.dtsi b/boards/renesas/da1469x_dk_pro/da1469x_dk_pro-pinctrl.dtsi index 032a2e0d539..50396701632 100644 --- a/boards/renesas/da1469x_dk_pro/da1469x_dk_pro-pinctrl.dtsi +++ b/boards/renesas/da1469x_dk_pro/da1469x_dk_pro-pinctrl.dtsi @@ -79,6 +79,14 @@ }; }; + /omit-if-no-ref/ spi_sleep: spi_sleep { + group1 { + pinmux = , + , + ; + }; + }; + spi_controller: spi_controller { group1 { pinmux = , @@ -91,6 +99,14 @@ }; }; + /omit-if-no-ref/ spi2_sleep: spi2_sleep { + group1 { + pinmux = , + , + ; + }; + }; + spi2_controller: spi2_controller { group1 { pinmux = < SMARTBOND_PINMUX(SPI2_CLK, 1, 3) >, diff --git a/boards/renesas/da1469x_dk_pro/da1469x_dk_pro.dts b/boards/renesas/da1469x_dk_pro/da1469x_dk_pro.dts index bc3c19d3215..3b09fe4e061 100644 --- a/boards/renesas/da1469x_dk_pro/da1469x_dk_pro.dts +++ b/boards/renesas/da1469x_dk_pro/da1469x_dk_pro.dts @@ -167,11 +167,13 @@ zephyr_udc0: &usbd { &spi { status = "okay"; pinctrl-0 = <&spi_controller>; - pinctrl-names = "default"; + pinctrl-1 = <&spi_sleep>; + pinctrl-names = "default", "sleep"; }; &spi2 { status = "okay"; pinctrl-0 = <&spi2_controller>; - pinctrl-names = "default"; + pinctrl-1 = <&spi2_sleep>; + pinctrl-names = "default", "sleep"; }; diff --git a/drivers/spi/spi_smartbond.c b/drivers/spi/spi_smartbond.c index 7f44c0f29aa..730a3b37a9d 100644 --- a/drivers/spi/spi_smartbond.c +++ b/drivers/spi/spi_smartbond.c @@ -15,8 +15,12 @@ LOG_MODULE_REGISTER(spi_smartbond); #include #include #include +#include +#include +#include #include +#include #define DIVN_CLK 32000000 /* divN_clk 32MHz */ #define SCLK_FREQ_2MHZ (DIVN_CLK / 14) /* 2.285714MHz*/ @@ -33,6 +37,10 @@ struct spi_smartbond_cfg { struct spi_smartbond_data { struct spi_context ctx; uint8_t dfs; +#if defined(CONFIG_PM_DEVICE) + ATOMIC_DEFINE(pm_policy_state_flag, 1); + uint32_t spi_ctrl_reg; +#endif }; static inline void spi_smartbond_enable(const struct spi_smartbond_cfg *cfg, bool enable) @@ -106,6 +114,31 @@ static inline int spi_smartbond_set_word_size(const struct spi_smartbond_cfg *cf return 0; } +static inline void spi_smartbond_pm_policy_state_lock_get(struct spi_smartbond_data *data) +{ +#if defined(CONFIG_PM_DEVICE) + if (atomic_test_and_set_bit(data->pm_policy_state_flag, 0) == 0) { + /* + * Prevent the SoC from entering the normal sleep state as PDC does not support + * waking up the application core following SPI events. + */ + pm_policy_state_lock_get(PM_STATE_STANDBY, PM_ALL_SUBSTATES); + } +#endif +} + +static inline void spi_smartbond_pm_policy_state_lock_put(struct spi_smartbond_data *data) +{ +#if defined(CONFIG_PM_DEVICE) + if (atomic_test_and_clear_bit(data->pm_policy_state_flag, 0) == 1) { + /* + * Allow the SoC to enter the normal sleep state once SPI transactions are done. + */ + pm_policy_state_lock_put(PM_STATE_STANDBY, PM_ALL_SUBSTATES); + } +#endif +} + static int spi_smartbond_configure(const struct spi_smartbond_cfg *cfg, struct spi_smartbond_data *data, const struct spi_config *spi_cfg) @@ -113,6 +146,9 @@ static int spi_smartbond_configure(const struct spi_smartbond_cfg *cfg, int rc; if (spi_context_configured(&data->ctx, spi_cfg)) { +#ifdef CONFIG_PM_DEVICE + spi_smartbond_enable(cfg, true); +#endif return 0; } @@ -182,6 +218,8 @@ static int spi_smartbond_transceive(const struct device *dev, const struct spi_c uint32_t bitmask; int rc; + spi_smartbond_pm_policy_state_lock_get(data); + spi_context_lock(&data->ctx, false, NULL, NULL, spi_cfg); rc = spi_smartbond_configure(cfg, data, spi_cfg); if (rc == 0) { @@ -211,6 +249,8 @@ static int spi_smartbond_transceive(const struct device *dev, const struct spi_c spi_context_cs_control(ctx, false); spi_context_release(&data->ctx, rc); + spi_smartbond_pm_policy_state_lock_put(data); + return rc; } #ifdef CONFIG_SPI_ASYNC @@ -247,7 +287,7 @@ static const struct spi_driver_api spi_smartbond_driver_api = { .release = spi_smartbond_release, }; -static int spi_smartbond_init(const struct device *dev) +static int spi_smartbond_resume(const struct device *dev) { const struct spi_smartbond_cfg *cfg = dev->config; struct spi_smartbond_data *data = dev->data; @@ -273,6 +313,69 @@ static int spi_smartbond_init(const struct device *dev) return 0; } +#if defined(CONFIG_PM_DEVICE) +static int spi_smartbond_suspend(const struct device *dev) +{ + int ret; + const struct spi_smartbond_cfg *config = dev->config; + struct spi_smartbond_data *data = dev->data; + + data->spi_ctrl_reg = config->regs->SPI_CTRL_REG; + /* Disable the SPI digital block */ + config->regs->SPI_CTRL_REG &= ~SPI_SPI_CTRL_REG_SPI_EN_CTRL_Msk; + /* Gate SPI clocking */ + CRG_COM->RESET_CLK_COM_REG = config->periph_clock_config; + + ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_SLEEP); + if (ret < 0) { + LOG_WRN("Failed to configure the SPI pins to inactive state"); + } + + return ret; +} + +static int spi_smartbond_pm_action(const struct device *dev, + enum pm_device_action action) +{ + int ret; + + switch (action) { + case PM_DEVICE_ACTION_RESUME: + da1469x_pd_acquire(MCU_PD_DOMAIN_COM); + ret = spi_smartbond_resume(dev); + break; + case PM_DEVICE_ACTION_SUSPEND: + ret = spi_smartbond_suspend(dev); + da1469x_pd_release(MCU_PD_DOMAIN_COM); + break; + default: + ret = -ENOTSUP; + } + + return ret; +} +#endif + +static int spi_smartbond_init(const struct device *dev) +{ + int ret; + struct spi_smartbond_data *data = dev->data; + +#ifdef CONFIG_PM_DEVICE_RUNTIME + /* Make sure device state is marked as suspended */ + pm_device_init_suspended(dev); + + ret = pm_device_runtime_enable(dev); + +#else + da1469x_pd_acquire(MCU_PD_DOMAIN_COM); + ret = spi_smartbond_resume(dev); +#endif + spi_context_unlock_unconditionally(&data->ctx); + + return ret; +} + #define SPI_SMARTBOND_DEVICE(id) \ PINCTRL_DT_INST_DEFINE(id); \ static const struct spi_smartbond_cfg spi_smartbond_##id##_cfg = { \ @@ -284,8 +387,13 @@ static int spi_smartbond_init(const struct device *dev) SPI_CONTEXT_INIT_LOCK(spi_smartbond_##id##_data, ctx), \ SPI_CONTEXT_INIT_SYNC(spi_smartbond_##id##_data, ctx), \ SPI_CONTEXT_CS_GPIOS_INITIALIZE(DT_DRV_INST(id), ctx)}; \ - DEVICE_DT_INST_DEFINE(id, spi_smartbond_init, NULL, &spi_smartbond_##id##_data, \ - &spi_smartbond_##id##_cfg, POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \ + PM_DEVICE_DT_INST_DEFINE(id, spi_smartbond_pm_action); \ + DEVICE_DT_INST_DEFINE(id, \ + spi_smartbond_init, \ + PM_DEVICE_DT_INST_GET(id), \ + &spi_smartbond_##id##_data, \ + &spi_smartbond_##id##_cfg, \ + POST_KERNEL, CONFIG_SPI_INIT_PRIORITY, \ &spi_smartbond_driver_api); DT_INST_FOREACH_STATUS_OKAY(SPI_SMARTBOND_DEVICE)