diff --git a/drivers/pwm/Kconfig.b9x b/drivers/pwm/Kconfig.b9x index 2aa1c6819356a..fac8059f6e0fa 100644 --- a/drivers/pwm/Kconfig.b9x +++ b/drivers/pwm/Kconfig.b9x @@ -7,3 +7,10 @@ config PWM_TELINK_B9X depends on DT_HAS_TELINK_B9X_PWM_ENABLED help Enables Telink B9x PWM driver. + +config PWM_TELINK_B9X_MAX_CHANNELS + int "Maximum number of PWM channels supported by B9x PWM" + default 6 + depends on PWM_TELINK_B9X + help + Defines maximum number of PWM channels supported by Telink B9x PWM hardware. diff --git a/drivers/pwm/Kconfig.tlx b/drivers/pwm/Kconfig.tlx index 6d9bfc950ba8b..76ea3de0b6081 100644 --- a/drivers/pwm/Kconfig.tlx +++ b/drivers/pwm/Kconfig.tlx @@ -7,3 +7,10 @@ config PWM_TELINK_TLX depends on DT_HAS_TELINK_TLX_PWM_ENABLED help Enables Telink TLx PWM driver. + +config PWM_TELINK_TLX_MAX_CHANNELS + int "Maximum number of PWM channels supported by TLx PWM" + default 6 + depends on PWM_TELINK_TLX + help + Defines maximum number of PWM channels supported by Telink TLx PWM hardware. diff --git a/drivers/pwm/pwm_b9x.c b/drivers/pwm/pwm_b9x.c index 76a5636d94fdf..4ddbf069880a8 100644 --- a/drivers/pwm/pwm_b9x.c +++ b/drivers/pwm/pwm_b9x.c @@ -10,6 +10,13 @@ #include #include #include +#include + +struct pwm_b9x_channel_state { + uint32_t period; + uint32_t pulse; + pwm_flags_t flags; +}; struct pwm_b9x_config { const pinctrl_soc_pin_t *pins; @@ -20,15 +27,22 @@ struct pwm_b9x_config { struct pwm_b9x_data { uint8_t out_pin_ch_connected; +#ifdef CONFIG_PM_DEVICE + struct pwm_b9x_channel_state ch[CONFIG_PWM_TELINK_B9X_MAX_CHANNELS]; +#endif /* CONFIG_PM_DEVICE */ }; /* API implementation: init */ static int pwm_b9x_init(const struct device *dev) { const struct pwm_b9x_config *config = dev->config; - uint32_t pwm_clk_div; + /* Check maximum number of PWM channels */ + if (config->channels > CONFIG_PWM_TELINK_B9X_MAX_CHANNELS) { + return -EINVAL; + } + /* Calculate and check PWM clock divider */ pwm_clk_div = sys_clk.pclk * 1000 * 1000 / config->clock_frequency - 1; if (pwm_clk_div > 255) { @@ -36,7 +50,7 @@ static int pwm_b9x_init(const struct device *dev) } /* Set PWM Peripheral clock */ - pwm_set_clk((unsigned char) (pwm_clk_div & 0xFF)); + pwm_set_clk((unsigned char)(pwm_clk_div & 0xFF)); return 0; } @@ -75,7 +89,7 @@ static int pwm_b9x_set_cycles(const struct device *dev, uint32_t channel, #if CONFIG_SOC_RISCV_TELINK_B91 pwm_start(channel); #elif CONFIG_SOC_RISCV_TELINK_B92 - pwm_start((channel == 0)?FLD_PWM0_EN:BIT(channel)); + pwm_start((channel == 0) ? FLD_PWM0_EN : BIT(channel)); #endif /* switch to 32K */ @@ -84,8 +98,7 @@ static int pwm_b9x_set_cycles(const struct device *dev, uint32_t channel, } /* connect output */ - if (!(data->out_pin_ch_connected & BIT(channel)) && - config->pins[channel] != UINT32_MAX) { + if (config->pins[channel] != UINT32_MAX) { const struct pinctrl_state pinctrl_state = { .pins = &config->pins[channel], .pin_cnt = 1, @@ -103,6 +116,12 @@ static int pwm_b9x_set_cycles(const struct device *dev, uint32_t channel, } } +#ifdef CONFIG_PM_DEVICE + data->ch[channel].period = period_cycles; + data->ch[channel].pulse = pulse_cycles; + data->ch[channel].flags = flags; +#endif /* CONFIG_PM_DEVICE */ + return 0; } @@ -126,6 +145,62 @@ static int pwm_b9x_get_cycles_per_sec(const struct device *dev, return 0; } +#ifdef CONFIG_PM_DEVICE + +static int pwm_b9x_pm_action(const struct device *dev, enum pm_device_action action) +{ + const struct pwm_b9x_config *config = dev->config; + struct pwm_b9x_data *data = dev->data; + + extern volatile bool b9x_deep_sleep_retention; + + switch (action) { + case PM_DEVICE_ACTION_RESUME: +#if CONFIG_SOC_SERIES_RISCV_TELINK_B9X_RETENTION + { + if (b9x_deep_sleep_retention) { + pwm_b9x_init(dev); + } + + for (uint8_t channel = 0; channel < config->channels; channel++) { + if (data->out_pin_ch_connected & BIT(channel)) { + pwm_b9x_set_cycles(dev, channel, data->ch[channel].period, + data->ch[channel].pulse, + data->ch[channel].flags); + } + } + } +#endif /* CONFIG_SOC_SERIES_RISCV_TELINK_B9X_RETENTION */ + break; + + case PM_DEVICE_ACTION_SUSPEND: +#if CONFIG_SOC_SERIES_RISCV_TELINK_B9X_RETENTION + { + for (uint8_t channel = 0; channel < config->channels; channel++) { + if (data->out_pin_ch_connected & BIT(channel)) { + uint32_t pin = B9x_PINMUX_GET_PIN(config->pins[channel]); + +#if CONFIG_SOC_RISCV_TELINK_B91 + pwm_stop(channel); +#elif CONFIG_SOC_RISCV_TELINK_B92 + pwm_stop((channel == 0) ? FLD_PWM0_EN : BIT(channel)); +#endif + gpio_shutdown(pin); + } + } + } +#endif /* CONFIG_SOC_SERIES_RISCV_TELINK_B9X_RETENTION */ + break; + + default: + return -ENOTSUP; + } + + return 0; +} + +#endif /* CONFIG_PM_DEVICE */ + /* PWM driver APIs structure */ static const struct pwm_driver_api pwm_b9x_driver_api = { .set_cycles = pwm_b9x_set_cycles, @@ -135,6 +210,8 @@ static const struct pwm_driver_api pwm_b9x_driver_api = { /* PWM driver registration */ #define PWM_b9x_INIT(n) \ \ + PM_DEVICE_DT_INST_DEFINE(n, pwm_b9x_pm_action); \ + \ static const pinctrl_soc_pin_t pwm_b9x_pins##n[] = { \ COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), pinctrl_ch0), \ (Z_PINCTRL_STATE_PIN_INIT(DT_DRV_INST(n), pinctrl_ch0, 0)), \ @@ -170,10 +247,10 @@ static const struct pwm_driver_api pwm_b9x_driver_api = { ), \ }; \ \ - struct pwm_b9x_data data##n; \ + static struct pwm_b9x_data data##n; \ \ DEVICE_DT_INST_DEFINE(n, pwm_b9x_init, \ - NULL, &data##n, &config##n, \ + PM_DEVICE_DT_INST_GET(n), &data##n, &config##n, \ POST_KERNEL, CONFIG_PWM_INIT_PRIORITY, \ &pwm_b9x_driver_api); diff --git a/drivers/pwm/pwm_tlx.c b/drivers/pwm/pwm_tlx.c index 08c3c5d98f8b5..e85dd13b56f8c 100644 --- a/drivers/pwm/pwm_tlx.c +++ b/drivers/pwm/pwm_tlx.c @@ -10,6 +10,13 @@ #include #include #include +#include + +struct pwm_tlx_channel_state { + uint32_t period; + uint32_t pulse; + pwm_flags_t flags; +}; struct pwm_tlx_config { const pinctrl_soc_pin_t *pins; @@ -20,15 +27,22 @@ struct pwm_tlx_config { struct pwm_tlx_data { uint8_t out_pin_ch_connected; +#ifdef CONFIG_PM_DEVICE + struct pwm_tlx_channel_state ch[CONFIG_PWM_TELINK_TLX_MAX_CHANNELS]; +#endif /* CONFIG_PM_DEVICE */ }; /* API implementation: init */ static int pwm_tlx_init(const struct device *dev) { const struct pwm_tlx_config *config = dev->config; - uint32_t pwm_clk_div; + /* Check maximum number of PWM channels */ + if (config->channels > CONFIG_PWM_TELINK_TLX_MAX_CHANNELS) { + return -EINVAL; + } + /* Calculate and check PWM clock divider */ pwm_clk_div = sys_clk.pclk * 1000 * 1000 / config->clock_frequency - 1; if (pwm_clk_div > 255) { @@ -36,7 +50,7 @@ static int pwm_tlx_init(const struct device *dev) } /* Set PWM Peripheral clock */ - pwm_set_clk((unsigned char) (pwm_clk_div & 0xFF)); + pwm_set_clk((unsigned char)(pwm_clk_div & 0xFF)); return 0; } @@ -72,8 +86,7 @@ static int pwm_tlx_set_cycles(const struct device *dev, uint32_t channel, pwm_set_tmax(channel, period_cycles); /* start pwm */ - pwm_start((channel == 0)?FLD_PWM0_EN:BIT(channel)); - + pwm_start((channel == 0) ? FLD_PWM0_EN : BIT(channel)); /* switch to 32K */ if (config->clk32k_ch_enable & BIT(channel)) { @@ -81,8 +94,7 @@ static int pwm_tlx_set_cycles(const struct device *dev, uint32_t channel, } /* connect output */ - if (!(data->out_pin_ch_connected & BIT(channel)) && - config->pins[channel] != UINT32_MAX) { + if (config->pins[channel] != UINT32_MAX) { const struct pinctrl_state pinctrl_state = { .pins = &config->pins[channel], .pin_cnt = 1, @@ -100,6 +112,12 @@ static int pwm_tlx_set_cycles(const struct device *dev, uint32_t channel, } } +#ifdef CONFIG_PM_DEVICE + data->ch[channel].period = period_cycles; + data->ch[channel].pulse = pulse_cycles; + data->ch[channel].flags = flags; +#endif /* CONFIG_PM_DEVICE */ + return 0; } @@ -123,6 +141,58 @@ static int pwm_tlx_get_cycles_per_sec(const struct device *dev, return 0; } +#ifdef CONFIG_PM_DEVICE + +static int pwm_tlx_pm_action(const struct device *dev, enum pm_device_action action) +{ + const struct pwm_tlx_config *config = dev->config; + struct pwm_tlx_data *data = dev->data; + + extern volatile bool tlx_deep_sleep_retention; + + switch (action) { + case PM_DEVICE_ACTION_RESUME: +#if CONFIG_SOC_SERIES_RISCV_TELINK_TLX_RETENTION + { + if (tlx_deep_sleep_retention) { + pwm_tlx_init(dev); + } + + for (uint8_t channel = 0; channel < config->channels; channel++) { + if (data->out_pin_ch_connected & BIT(channel)) { + pwm_tlx_set_cycles(dev, channel, data->ch[channel].period, + data->ch[channel].pulse, + data->ch[channel].flags); + } + } + } +#endif /* CONFIG_SOC_SERIES_RISCV_TELINK_TLX_RETENTION */ + break; + + case PM_DEVICE_ACTION_SUSPEND: +#if CONFIG_SOC_SERIES_RISCV_TELINK_TLX_RETENTION + { + for (uint8_t channel = 0; channel < config->channels; channel++) { + if (data->out_pin_ch_connected & BIT(channel)) { + uint32_t pin = TLX_PINMUX_GET_PIN(config->pins[channel]); + + pwm_stop((channel == 0) ? FLD_PWM0_EN : BIT(channel)); + gpio_shutdown(pin); + } + } + } +#endif /* CONFIG_SOC_SERIES_RISCV_TELINK_TLX_RETENTION */ + break; + + default: + return -ENOTSUP; + } + + return 0; +} + +#endif /* CONFIG_PM_DEVICE */ + /* PWM driver APIs structure */ static const struct pwm_driver_api pwm_tlx_driver_api = { .set_cycles = pwm_tlx_set_cycles, @@ -132,6 +202,8 @@ static const struct pwm_driver_api pwm_tlx_driver_api = { /* PWM driver registration */ #define PWM_tlx_INIT(n) \ \ + PM_DEVICE_DT_INST_DEFINE(n, pwm_tlx_pm_action); \ + \ static const pinctrl_soc_pin_t pwm_tlx_pins##n[] = { \ COND_CODE_1(DT_NODE_HAS_PROP(DT_DRV_INST(n), pinctrl_ch0), \ (Z_PINCTRL_STATE_PIN_INIT(DT_DRV_INST(n), pinctrl_ch0, 0)), \ @@ -167,10 +239,10 @@ static const struct pwm_driver_api pwm_tlx_driver_api = { ), \ }; \ \ - struct pwm_tlx_data data##n; \ + static struct pwm_tlx_data data##n; \ \ DEVICE_DT_INST_DEFINE(n, pwm_tlx_init, \ - NULL, &data##n, &config##n, \ + PM_DEVICE_DT_INST_GET(n), &data##n, &config##n, \ POST_KERNEL, CONFIG_PWM_INIT_PRIORITY, \ &pwm_tlx_driver_api);