Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

drivers: pwm: nrfx: Add runtime PM and fix stopping PWM #2081

Merged
merged 4 commits into from
Oct 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
142 changes: 43 additions & 99 deletions drivers/pwm/pwm_nrfx.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,13 @@
* to 0 or 1, hence the use of #if IS_ENABLED().
*/
#if IS_ENABLED(NRFX_PWM_NRF52_ANOMALY_109_WORKAROUND_ENABLED)
#define ANOMALY_109_IRQ_CONNECT(...) IRQ_CONNECT(__VA_ARGS__)
#define ANOMALY_109_EGU_IRQ_CONNECT(idx) _EGU_IRQ_CONNECT(idx)
#define _EGU_IRQ_CONNECT(idx) \
extern void nrfx_egu_##idx##_irq_handler(void); \
IRQ_CONNECT(DT_IRQN(DT_NODELABEL(egu##idx)), \
DT_IRQ(DT_NODELABEL(egu##idx), priority), \
nrfx_isr, nrfx_egu_##idx##_irq_handler, 0)
#else
#define ANOMALY_109_IRQ_CONNECT(...)
#define ANOMALY_109_EGU_IRQ_CONNECT(idx)
#endif

Expand Down Expand Up @@ -63,6 +61,12 @@
return (uint16_t *)config->seq.values.p_raw;
}

static void pwm_handler(nrfx_pwm_evt_type_t event_type, void *p_context)
{
ARG_UNUSED(event_type);
ARG_UNUSED(p_context);
}

static bool pwm_period_check_and_set(const struct device *dev,
uint32_t channel, uint32_t period_cycles)
{
Expand Down Expand Up @@ -229,7 +233,8 @@
* until another playback is requested (new values will be
* loaded then) or the PWM peripheral is stopped.
*/
nrfx_pwm_simple_playback(&config->pwm, &config->seq, 1, 0);
nrfx_pwm_simple_playback(&config->pwm, &config->seq, 1,
NRFX_PWM_FLAG_NO_EVT_FINISHED);
}

return 0;
Expand All @@ -252,18 +257,12 @@
.get_cycles_per_sec = pwm_nrfx_get_cycles_per_sec,
};

static int pwm_nrfx_init(const struct device *dev)
static void pwm_resume(const struct device *dev)
{
const struct pwm_nrfx_config *config = dev->config;
uint8_t initially_inverted = 0;

int ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);

ANOMALY_109_EGU_IRQ_CONNECT(NRFX_PWM_NRF52_ANOMALY_109_EGU_INSTANCE);

if (ret < 0) {
return ret;
}
(void)pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);

for (size_t i = 0; i < NRF_PWM_CHANNEL_COUNT; i++) {
uint32_t psel;
Expand All @@ -283,64 +282,53 @@

seq_values_ptr_get(dev)[i] = PWM_NRFX_CH_VALUE(0, inverted);
}

nrfx_err_t result = nrfx_pwm_init(&config->pwm,
&config->initial_config,
NULL,
NULL);
if (result != NRFX_SUCCESS) {
LOG_ERR("Failed to initialize device: %s", dev->name);
return -EBUSY;
}

return 0;
}

#ifdef CONFIG_PM_DEVICE
static void pwm_nrfx_uninit(const struct device *dev)
static void pwm_suspend(const struct device *dev)
{
const struct pwm_nrfx_config *config = dev->config;

nrfx_pwm_uninit(&config->pwm);
nrfx_pwm_stop(&config->pwm, false);
while (!nrfx_pwm_stopped_check(&config->pwm)) {
}

memset(dev->data, 0, sizeof(struct pwm_nrfx_data));
(void)pinctrl_apply_state(config->pcfg, PINCTRL_STATE_SLEEP);
}

static int pwm_nrfx_pm_action(const struct device *dev,
enum pm_device_action action)
{
const struct pwm_nrfx_config *config = dev->config;
int ret = 0;
if (action == PM_DEVICE_ACTION_RESUME) {
pwm_resume(dev);
} else if (IS_ENABLED(CONFIG_PM_DEVICE) && (action == PM_DEVICE_ACTION_SUSPEND)) {
pwm_suspend(dev);
} else {
return -ENOTSUP;
}

switch (action) {
case PM_DEVICE_ACTION_RESUME:
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
if (ret < 0) {
return ret;
}
ret = pwm_nrfx_init(dev);
break;
return 0;
}

case PM_DEVICE_ACTION_SUSPEND:
pwm_nrfx_uninit(dev);
static int pwm_nrfx_init(const struct device *dev)
{
const struct pwm_nrfx_config *config = dev->config;
nrfx_err_t err;

ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_SLEEP);
if (ret < 0) {
return ret;
}
break;
ANOMALY_109_EGU_IRQ_CONNECT(NRFX_PWM_NRF52_ANOMALY_109_EGU_INSTANCE);

default:
return -ENOTSUP;
if (IS_ENABLED(CONFIG_PM_DEVICE_RUNTIME)) {
(void)pinctrl_apply_state(config->pcfg, PINCTRL_STATE_SLEEP);
}

return ret;
}
#else

#define pwm_nrfx_pm_action NULL
err = nrfx_pwm_init(&config->pwm, &config->initial_config, pwm_handler, dev->data);
if (err != NRFX_SUCCESS) {
LOG_ERR("Failed to initialize device: %s", dev->name);
return -EBUSY;
}

#endif /* CONFIG_PM_DEVICE */
return pm_device_driver_init(dev, pwm_nrfx_pm_action);
}

#define PWM(dev_idx) DT_NODELABEL(pwm##dev_idx)
#define PWM_PROP(dev_idx, prop) DT_PROP(PWM(dev_idx), prop)
Expand Down Expand Up @@ -377,9 +365,8 @@
}; \
static int pwm_nrfx_init##idx(const struct device *dev) \
{ \
ANOMALY_109_IRQ_CONNECT( \
DT_IRQN(PWM(idx)), DT_IRQ(PWM(idx), priority), \
nrfx_isr, nrfx_pwm_##idx##_irq_handler, 0); \
IRQ_CONNECT(DT_IRQN(PWM(idx)), DT_IRQ(PWM(idx), priority), \
nrfx_isr, nrfx_pwm_##idx##_irq_handler, 0); \
return pwm_nrfx_init(dev); \
}; \
PM_DEVICE_DT_DEFINE(PWM(idx), pwm_nrfx_pm_action); \
Expand All @@ -390,50 +377,7 @@
POST_KERNEL, CONFIG_PWM_INIT_PRIORITY, \
&pwm_nrfx_drv_api_funcs)

#ifdef CONFIG_HAS_HW_NRF_PWM0
PWM_NRFX_DEVICE(0);
#endif

#ifdef CONFIG_HAS_HW_NRF_PWM1
PWM_NRFX_DEVICE(1);
#endif

#ifdef CONFIG_HAS_HW_NRF_PWM2
PWM_NRFX_DEVICE(2);
#endif

#ifdef CONFIG_HAS_HW_NRF_PWM3
PWM_NRFX_DEVICE(3);
#endif

#ifdef CONFIG_HAS_HW_NRF_PWM20
PWM_NRFX_DEVICE(20);
#endif

#ifdef CONFIG_HAS_HW_NRF_PWM21
PWM_NRFX_DEVICE(21);
#endif

#ifdef CONFIG_HAS_HW_NRF_PWM22
PWM_NRFX_DEVICE(22);
#endif

#ifdef CONFIG_HAS_HW_NRF_PWM120
PWM_NRFX_DEVICE(120);
#endif
#define COND_PWM_NRFX_DEVICE(unused, prefix, i, _) \
IF_ENABLED(CONFIG_HAS_HW_NRF_PWM##prefix##i, (PWM_NRFX_DEVICE(prefix##i);))

Check notice on line 381 in drivers/pwm/pwm_nrfx.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/pwm/pwm_nrfx.c:381 -#define PWM_NRFX_DEVICE(idx) \ - NRF_DT_CHECK_NODE_HAS_PINCTRL_SLEEP(PWM(idx)); \ - static struct pwm_nrfx_data pwm_nrfx_##idx##_data; \ - static uint16_t pwm_##idx##_seq_values[NRF_PWM_CHANNEL_COUNT] \ - PWM_MEMORY_SECTION(idx); \ - PINCTRL_DT_DEFINE(PWM(idx)); \ - static const struct pwm_nrfx_config pwm_nrfx_##idx##_config = { \ - .pwm = NRFX_PWM_INSTANCE(idx), \ - .initial_config = { \ - .skip_gpio_cfg = true, \ - .skip_psel_cfg = true, \ - .base_clock = NRF_PWM_CLK_1MHz, \ - .count_mode = (PWM_PROP(idx, center_aligned) \ - ? NRF_PWM_MODE_UP_AND_DOWN \ - : NRF_PWM_MODE_UP), \ - .top_value = 1000, \ - .load_mode = NRF_PWM_LOAD_INDIVIDUAL, \ - .step_mode = NRF_PWM_STEP_TRIGGERED, \ - }, \ - .seq.values.p_raw = pwm_##idx##_seq_values, \ - .seq.length = NRF_PWM_CHANNEL_COUNT, \ - .pcfg = PINCTRL_DT_DEV_CONFIG_GET(PWM(idx)), \ - }; \ - static int pwm_nrfx_init##idx(const struct device *dev) \ - { \ - IRQ_CONNECT(DT_IRQN(PWM(idx)), DT_IRQ(PWM(idx), priority), \ - nrfx_isr, nrfx_pwm_##idx##_irq_handler, 0); \ - return pwm_nrfx_init(dev); \ - }; \ - PM_DEVICE_DT_DEFINE(PWM(idx), pwm_nrfx_pm_action); \ - DEVICE_DT_DEFINE(PWM(idx), \ - pwm_nrfx_init##idx, PM_DEVICE_DT_GET(PWM(idx)), \ - &pwm_nrfx_##idx##_data, \ - &pwm_nrfx_##idx##_config, \ - POST_KERNEL, CONFIG_PWM_INIT_PRIORITY, \ - &pwm_nrfx_drv_api_funcs) - -#define COND_PWM_NRFX_DEVICE(unused, prefix, i, _) \ +#define PWM_NRFX_DEVICE(idx) \ + NRF_DT_CHECK_NODE_HAS_PINCTRL_SLEEP(PWM(idx)); \ + static struct pwm_nrfx_data pwm_nrfx_##idx##_data; \ + static uint16_t pwm_##idx##_seq_values[NRF_PWM_CHANNEL_COUNT] PWM_MEMORY_SECTION(idx); \ + PINCTRL_DT_DEFINE(PWM(idx)); \ + static const struct pwm_nrfx_config pwm_nrfx_##idx##_config = { \ + .pwm = NRFX_PWM_INSTANCE(idx), \ + .initial_config = \ + { \ + .skip_gpio_cfg = true, \ + .skip_psel_cfg = true, \ + .base_clock = NRF_PWM_CLK_1MHz, \ + .count_mode = \ + (PWM_PROP(idx, center_aligned) ? NRF_PWM_MODE_UP_AND_DOWN \ + : NRF_PWM_MODE_UP), \ + .top_value = 1000, \ + .load_mode = NRF_PWM_LOAD_INDIVIDUAL, \ + .step_mode = NRF_PWM_STEP_TRIGGERED, \ + }, \ + .seq.values.p_raw = pwm_##idx##_seq_values, \ + .seq.length = NRF_PWM_CHANNEL_COUNT, \ + .pcfg = PINCTRL_DT_DEV_CONFIG_GET(PWM(idx)), \ + }; \ + static int pwm_nrfx_init##idx(const struct device *dev) \ + { \ + IRQ_CONNECT(DT_IRQN(PWM(idx)), DT_IRQ(PWM(idx), priority), nrfx_isr, \ + nrfx_pwm_##idx##_irq_handler, 0); \ + return pwm_nrfx_init(dev);

#ifdef CONFIG_HAS_HW_NRF_PWM130
PWM_NRFX_DEVICE(130);
#endif

#ifdef CONFIG_HAS_HW_NRF_PWM131
PWM_NRFX_DEVICE(131);
#endif

#ifdef CONFIG_HAS_HW_NRF_PWM132
PWM_NRFX_DEVICE(132);
#endif

#ifdef CONFIG_HAS_HW_NRF_PWM133
PWM_NRFX_DEVICE(133);
#endif
NRFX_FOREACH_PRESENT(PWM, COND_PWM_NRFX_DEVICE, (), (), _)
5 changes: 3 additions & 2 deletions include/zephyr/pm/device.h
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,8 @@ bool pm_device_is_powered(const struct device *dev);
* This helper function is intended to be called at the end of a driver
* init function to automatically setup the device into the lowest power
* mode. It assumes that the device has been configured as if it is in
* @ref PM_DEVICE_STATE_OFF.
* @ref PM_DEVICE_STATE_OFF, or @ref PM_DEVICE_STATE_SUSPENDED if device can
* never be powered off.
*
* @param dev Device instance.
* @param action_cb Device PM control callback function.
Expand Down Expand Up @@ -718,7 +719,7 @@ static inline int pm_device_driver_init(const struct device *dev, pm_device_acti

/* When power management is not enabled, all drivers should initialise to active state */
rc = action_cb(dev, PM_DEVICE_ACTION_TURN_ON);
if (rc < 0) {
if ((rc < 0) && (rc != -ENOTSUP)) {
return rc;
}

Expand Down
2 changes: 1 addition & 1 deletion subsys/pm/device.c
Original file line number Diff line number Diff line change
Expand Up @@ -370,7 +370,7 @@ int pm_device_driver_init(const struct device *dev,

/* Run power-up logic */
rc = action_cb(dev, PM_DEVICE_ACTION_TURN_ON);
if (rc != 0) {
if ((rc < 0) && (rc != -ENOTSUP)) {
return rc;
}

Expand Down
Loading