Skip to content

Commit

Permalink
drivers: pwm: nrfx: add global hsfll request for fast PWM
Browse files Browse the repository at this point in the history
Added clock control api for global hsfll used in fast PWM120
driver.

Signed-off-by: Michał Stasiak <[email protected]>
  • Loading branch information
mstasiaknordic committed Dec 4, 2024
1 parent afba775 commit 1e59406
Showing 1 changed file with 135 additions and 6 deletions.
141 changes: 135 additions & 6 deletions drivers/pwm/pwm_nrfx.c
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
#ifdef CONFIG_SOC_NRF54H20_GPD
#include <nrf/gpd.h>
#endif
#ifdef CONFIG_CLOCK_CONTROL
#include <zephyr/drivers/clock_control/nrf_clock_control.h>
#endif

#include <zephyr/logging/log.h>

Expand All @@ -35,6 +38,20 @@ LOG_MODULE_REGISTER(pwm_nrfx, CONFIG_PWM_LOG_LEVEL);
#define ANOMALY_109_EGU_IRQ_CONNECT(idx)
#endif

#define PWM(dev_idx) DT_NODELABEL(pwm##dev_idx)
#define PWM_PROP(dev_idx, prop) DT_PROP(PWM(dev_idx), prop)
#define PWM_HAS_PROP(idx, prop) DT_NODE_HAS_PROP(PWM(idx), prop)

#define PWM_NRFX_IS_FAST(unused, prefix, idx, _) \
COND_CODE_1(DT_NODE_HAS_STATUS_OKAY(PWM(idx)), \
(COND_CODE_1(UTIL_AND(CONFIG_SOC_NRF54H20_GPD, PWM_HAS_PROP(idx, power_domains)), \
(COND_CODE_1(DT_PHA(PWM(idx), power_domains, id), (0), (1))), \
(0))), (0))

#if NRFX_FOREACH_PRESENT(PWM, PWM_NRFX_IS_FAST, (||), (0))
#define PWM_NRFX_FAST_PRESENT 1
#endif

#define PWM_NRFX_CH_POLARITY_MASK BIT(15)
#define PWM_NRFX_CH_COMPARE_MASK BIT_MASK(15)
#define PWM_NRFX_CH_VALUE(compare_value, inverted) \
Expand All @@ -49,6 +66,10 @@ struct pwm_nrfx_config {
#ifdef CONFIG_DCACHE
uint32_t mem_attr;
#endif
#ifdef PWM_NRFX_FAST_PRESENT
const struct device *clk_dev;
struct nrf_clock_spec clk_spec;
#endif
};

struct pwm_nrfx_data {
Expand All @@ -57,6 +78,11 @@ struct pwm_nrfx_data {
uint8_t pwm_needed;
uint8_t prescaler;
bool stop_requested;
#ifdef PWM_NRFX_FAST_PRESENT
struct onoff_client clk_cli;
struct k_sem *sem;
int res;
#endif
};
/* Ensure the pwm_needed bit mask can accommodate all available channels. */
#if (NRF_PWM_CHANNEL_COUNT > 8)
Expand Down Expand Up @@ -134,6 +160,69 @@ static bool channel_psel_get(uint32_t channel, uint32_t *psel,
== PWM_PSEL_OUT_CONNECT_Connected);
}

#ifdef PWM_NRFX_FAST_PRESENT
static void pwm_nrfx_hsfll_cb(struct onoff_manager *mgr, struct onoff_client *cli,
uint32_t state, int res)
{
ARG_UNUSED(state);

struct pwm_nrfx_data *data = CONTAINER_OF(cli, struct pwm_nrfx_data, clk_cli);

data->res = res;
k_sem_give(data->sem);
}

static int pwm_nrfx_hsfll_request(const struct device *dev)
{
const struct pwm_nrfx_config *config = dev->config;
struct pwm_nrfx_data *data = dev->data;

struct k_sem sem;

data->sem = &sem;
k_sem_init(data->sem, 0, 1);

sys_notify_init_callback(&data->clk_cli.notify, pwm_nrfx_hsfll_cb);

int err;

err = nrf_clock_control_request(config->clk_dev, &config->clk_spec, &data->clk_cli);
if (err < 0) {
LOG_ERR("Clock request failed: %d", err);
return err;
}

err = k_sem_take(data->sem, K_SECONDS(2)); /* Based on clock_control sample */
if (err < 0) {
LOG_ERR("Callback failed: %d", err);
return err;
}

if (data->res < 0) {
LOG_ERR("Clock request response failed: %d", data->res);
return data->res;
}

return 0;
}

static int pwm_nrfx_hsfll_release(const struct device *dev)
{
const struct pwm_nrfx_config *config = dev->config;

int err;

err = nrf_clock_control_release(config->clk_dev, &config->clk_spec);

if (err < 0) {
LOG_ERR("Clock release failed: %d", err);
return err;
}

return 0;
}
#endif

static int pwm_nrfx_set_cycles(const struct device *dev, uint32_t channel,
uint32_t period_cycles, uint32_t pulse_cycles,
pwm_flags_t flags)
Expand Down Expand Up @@ -229,6 +318,14 @@ static int pwm_nrfx_set_cycles(const struct device *dev, uint32_t channel,
* ensure it is stopped before starting the next playback.
*/
nrfx_pwm_stop(&config->pwm, false);
#if PWM_NRFX_FAST_PRESENT
int ret = pwm_nrfx_hsfll_release(dev);

if (ret < 0) {
LOG_ERR("Global HSFLL release failed: %d", ret);
return ret;
}
#endif
data->stop_requested = true;
} else {
if (data->stop_requested) {
Expand All @@ -248,6 +345,14 @@ static int pwm_nrfx_set_cycles(const struct device *dev, uint32_t channel,
* until another playback is requested (new values will be
* loaded then) or the PWM peripheral is stopped.
*/
#if PWM_NRFX_FAST_PRESENT
int ret = pwm_nrfx_hsfll_request(dev);

if (ret < 0) {
LOG_ERR("Global HSFLL request failed: %d", ret);
return ret;
}
#endif
nrfx_pwm_simple_playback(&config->pwm, &config->seq, 1,
NRFX_PWM_FLAG_NO_EVT_FINISHED);
}
Expand Down Expand Up @@ -306,6 +411,14 @@ static void pwm_suspend(const struct device *dev)
const struct pwm_nrfx_config *config = dev->config;

nrfx_pwm_stop(&config->pwm, false);
#if PWM_NRFX_FAST_PRESENT
int ret = pwm_nrfx_hsfll_release(dev);

if (ret < 0) {
LOG_ERR("Global HSFLL release failed: %d", ret);
return ret;
}
#endif
while (!nrfx_pwm_stopped_check(&config->pwm)) {
}

Expand Down Expand Up @@ -351,9 +464,6 @@ static int pwm_nrfx_init(const struct device *dev)
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)
#define PWM_HAS_PROP(idx, prop) DT_NODE_HAS_PROP(PWM(idx), prop)
#define PWM_MEM_REGION(idx) DT_PHANDLE(PWM(idx), memory_regions)

#define PWM_MEMORY_SECTION(idx) \
Expand All @@ -366,6 +476,20 @@ static int pwm_nrfx_init(const struct device *dev)
COND_CODE_1(PWM_HAS_PROP(idx, memory_regions), \
(DT_PROP_OR(PWM_MEM_REGION(idx), zephyr_memory_attr, 0)), (0))

#define DT_PROP_LAST(node, prop) \
DT_PROP_BY_IDX(node, prop, UTIL_CAT(Z_UTIL_DEC_, DT_PROP_LEN(node, prop)))

#define PWM_CLOCK_FREQUENCY(idx) \
COND_CODE_1(UTIL_AND(DT_CLOCKS_HAS_IDX(PWM(idx), 0), \
DT_NODE_HAS_PROP(DT_CLOCKS_CTLR(PWM(idx)), clock_frequency)), \
(DT_PROP(DT_CLOCKS_CTLR(PWM(idx)), clock_frequency)), \
(16ul * 1000ul * 1000ul))

#define PWM_MAX_CLOCK_FREQUENCY(idx) \
COND_CODE_1(DT_NODE_HAS_PROP(DT_CLOCKS_CTLR(PWM(idx)), supported_clock_frequencies), \
(DT_PROP_LAST(DT_CLOCKS_CTLR(PWM(idx)), supported_clock_frequencies)), \
(PWM_CLOCK_FREQUENCY(idx)))

#define PWM_NRFX_DEVICE(idx) \
NRF_DT_CHECK_NODE_HAS_PINCTRL_SLEEP(PWM(idx)); \
static struct pwm_nrfx_data pwm_nrfx_##idx##_data; \
Expand All @@ -388,11 +512,16 @@ static int pwm_nrfx_init(const struct device *dev)
.seq.values.p_raw = pwm_##idx##_seq_values, \
.seq.length = NRF_PWM_CHANNEL_COUNT, \
.pcfg = PINCTRL_DT_DEV_CONFIG_GET(PWM(idx)), \
.clock_freq = COND_CODE_1(DT_CLOCKS_HAS_IDX(PWM(idx), 0), \
(DT_PROP(DT_CLOCKS_CTLR(PWM(idx)), clock_frequency)), \
(16ul * 1000ul * 1000ul)), \
.clock_freq = PWM_CLOCK_FREQUENCY(idx), \
IF_ENABLED(CONFIG_DCACHE, \
(.mem_attr = PWM_GET_MEM_ATTR(idx),)) \
IF_ENABLED(PWM_NRFX_IS_FAST(_, _, idx, _), \
(.clk_dev = DEVICE_DT_GET(DT_CLOCKS_CTLR(PWM(idx))), \
.clk_spec = { \
.frequency = PWM_MAX_CLOCK_FREQUENCY(idx), \
.accuracy = 0, \
.precision = NRF_CLOCK_CONTROL_PRECISION_DEFAULT, \
},)) \
}; \
static int pwm_nrfx_init##idx(const struct device *dev) \
{ \
Expand Down

0 comments on commit 1e59406

Please sign in to comment.