Skip to content

Commit

Permalink
drivers: pwm: pwm_nrf5_sw: Use runtime resources allocation
Browse files Browse the repository at this point in the history
Use nrfx_gpiote and nrfx_ppi allocators to allocate channels
at runtime instead of fixed, device-tree based allocation which
is harder to maintain.

Signed-off-by: Krzysztof Chruscinski <krzysztof.chruscinski@nordicsemi.no>
  • Loading branch information
nordic-krch authored and carlescufi committed Nov 26, 2021
1 parent 5287ce8 commit 9886bdc
Show file tree
Hide file tree
Showing 15 changed files with 55 additions and 91 deletions.
4 changes: 3 additions & 1 deletion drivers/pwm/Kconfig.nrf5_sw
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
config PWM_NRF5_SW
bool "Nordic Semiconductor nRF5x series S/W PWM"
default y if !PWM_NRFX
depends on SOC_FAMILY_NRF
depends on HAS_HW_NRF_PPI
select NRFX_GPIOTE
select NRFX_PPI
help
Enable driver to utilize PWM on the Nordic Semiconductor nRF5x series.

Expand Down
76 changes: 50 additions & 26 deletions drivers/pwm/pwm_nrf5_sw.c
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
#define DT_DRV_COMPAT nordic_nrf_sw_pwm

#include <soc.h>
#include <nrfx_gpiote.h>
#include <nrfx_ppi.h>
#include <hal/nrf_gpio.h>

#include <drivers/pwm.h>
Expand Down Expand Up @@ -35,13 +37,14 @@ BUILD_ASSERT(DT_INST_PROP(0, clock_prescaler) == 0,
#endif
#define PWM_0_MAP_SIZE DT_INST_PROP(0, channel_count)

/* When RTC is used, one more PPI channel is required. */
#define PPI_PER_CH (2 + USE_RTC)

struct pwm_config {
union {
NRF_RTC_Type *rtc;
NRF_TIMER_Type *timer;
};
uint8_t gpiote_base;
uint8_t ppi_base;
uint8_t map_size;
uint8_t prescaler;
};
Expand All @@ -53,6 +56,8 @@ struct chan_map {

struct pwm_data {
uint32_t period_cycles;
uint8_t ppi_ch[PWM_0_MAP_SIZE][PPI_PER_CH];
uint8_t gpiote_ch[PWM_0_MAP_SIZE];
struct chan_map map[PWM_0_MAP_SIZE];
};

Expand Down Expand Up @@ -128,9 +133,10 @@ static int pwm_nrf5_sw_pin_set(const struct device *dev, uint32_t pwm,
NRF_TIMER_Type *timer = pwm_config_timer(config);
NRF_RTC_Type *rtc = pwm_config_rtc(config);
struct pwm_data *data = dev->data;
uint8_t ppi_index;
uint32_t ppi_mask;
uint8_t channel;
uint8_t gpiote_ch;
const uint8_t *ppi_chs;
uint32_t ret;

if (flags) {
Expand Down Expand Up @@ -167,6 +173,8 @@ static int pwm_nrf5_sw_pin_set(const struct device *dev, uint32_t pwm,

/* map pwm pin to GPIOTE config/channel */
channel = pwm_channel_map(data, config->map_size, pwm);
gpiote_ch = data->gpiote_ch[channel];
ppi_chs = data->ppi_ch[channel];
if (channel >= config->map_size) {
LOG_ERR("No more channels available");
return -ENOMEM;
Expand All @@ -176,17 +184,10 @@ static int pwm_nrf5_sw_pin_set(const struct device *dev, uint32_t pwm,
period_cycles, pulse_cycles);

/* clear GPIOTE config */
NRF_GPIOTE->CONFIG[config->gpiote_base + channel] = 0;
NRF_GPIOTE->CONFIG[gpiote_ch] = 0;

/* clear PPI used */
if (USE_RTC) {
ppi_index = config->ppi_base + (channel * 3);
ppi_mask = BIT(ppi_index) | BIT(ppi_index + 1) |
BIT(ppi_index + 2);
} else {
ppi_index = config->ppi_base + (channel * 2);
ppi_mask = BIT(ppi_index) | BIT(ppi_index + 1);
}
ppi_mask = BIT(ppi_chs[0]) | BIT(ppi_chs[1]) | (USE_RTC ? BIT(ppi_chs[2]) : 0);
NRF_PPI->CHENCLR = ppi_mask;

/* configure GPIO pin as output */
Expand Down Expand Up @@ -229,31 +230,30 @@ static int pwm_nrf5_sw_pin_set(const struct device *dev, uint32_t pwm,
}

/* configure GPIOTE, toggle with initialise output high */
NRF_GPIOTE->CONFIG[config->gpiote_base + channel] = 0x00130003 |
(pwm << 8);
NRF_GPIOTE->CONFIG[gpiote_ch] = 0x00130003 | (pwm << 8);

/* setup PPI */
if (USE_RTC) {
NRF_PPI->CH[ppi_index].EEP =
NRF_PPI->CH[ppi_chs[0]].EEP =
(uint32_t) &(rtc->EVENTS_COMPARE[channel]);
NRF_PPI->CH[ppi_index].TEP =
NRF_PPI->CH[ppi_chs[0]].TEP =
(uint32_t) &(NRF_GPIOTE->TASKS_OUT[channel]);
NRF_PPI->CH[ppi_index + 1].EEP =
NRF_PPI->CH[ppi_chs[1]].EEP =
(uint32_t) &(rtc->EVENTS_COMPARE[config->map_size]);
NRF_PPI->CH[ppi_index + 1].TEP =
NRF_PPI->CH[ppi_chs[1]].TEP =
(uint32_t) &(NRF_GPIOTE->TASKS_OUT[channel]);
NRF_PPI->CH[ppi_index + 2].EEP =
NRF_PPI->CH[ppi_chs[2]].EEP =
(uint32_t) &(rtc->EVENTS_COMPARE[config->map_size]);
NRF_PPI->CH[ppi_index + 2].TEP =
NRF_PPI->CH[ppi_chs[2]].TEP =
(uint32_t) &(rtc->TASKS_CLEAR);
} else {
NRF_PPI->CH[ppi_index].EEP =
NRF_PPI->CH[ppi_chs[0]].EEP =
(uint32_t) &(timer->EVENTS_COMPARE[channel]);
NRF_PPI->CH[ppi_index].TEP =
NRF_PPI->CH[ppi_chs[0]].TEP =
(uint32_t) &(NRF_GPIOTE->TASKS_OUT[channel]);
NRF_PPI->CH[ppi_index + 1].EEP =
NRF_PPI->CH[ppi_chs[1]].EEP =
(uint32_t) &(timer->EVENTS_COMPARE[config->map_size]);
NRF_PPI->CH[ppi_index + 1].TEP =
NRF_PPI->CH[ppi_chs[1]].TEP =
(uint32_t) &(NRF_GPIOTE->TASKS_OUT[channel]);
}
NRF_PPI->CHENSET = ppi_mask;
Expand Down Expand Up @@ -327,9 +327,35 @@ static const struct pwm_driver_api pwm_nrf5_sw_drv_api_funcs = {
static int pwm_nrf5_sw_init(const struct device *dev)
{
const struct pwm_config *config = dev->config;
struct pwm_data *data = dev->data;
NRF_TIMER_Type *timer = pwm_config_timer(config);
NRF_RTC_Type *rtc = pwm_config_rtc(config);

/* Allocate resources. */
for (uint32_t i = 0; i < config->map_size; i++) {
nrfx_err_t err;

for (uint32_t j = 0; j < PPI_PER_CH; j++) {
err = nrfx_ppi_channel_alloc(&data->ppi_ch[i][j]);
if (err != NRFX_SUCCESS) {
/* Do not free allocated resource. It is a fatal condition,
* system requires reconfiguration.
*/
LOG_ERR("Failed to allocate PPI channel");
return -ENOMEM;
}
}

err = nrfx_gpiote_channel_alloc(&data->gpiote_ch[i]);
if (err != NRFX_SUCCESS) {
/* Do not free allocated resource. It is a fatal condition,
* system requires reconfiguration.
*/
LOG_ERR("Failed to allocate GPIOTE channel");
return -ENOMEM;
}
}

if (USE_RTC) {
/* setup RTC */
rtc->PRESCALER = 0;
Expand Down Expand Up @@ -361,8 +387,6 @@ static int pwm_nrf5_sw_init(const struct device *dev)

static const struct pwm_config pwm_nrf5_sw_0_config = {
COND_CODE_1(USE_RTC, (.rtc), (.timer)) = GENERATOR_ADDR,
.ppi_base = DT_INST_PROP(0, ppi_base),
.gpiote_base = DT_INST_PROP(0, gpiote_base),
.map_size = PWM_0_MAP_SIZE,
.prescaler = DT_INST_PROP(0, clock_prescaler),
};
Expand Down
2 changes: 0 additions & 2 deletions dts/arm/nordic/nrf51822.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -251,8 +251,6 @@
generator = <&timer1>;
channel-count = <3>;
clock-prescaler = <0>;
ppi-base = <0>;
gpiote-base = <0>;
#pwm-cells = <1>;
};
};
Expand Down
2 changes: 0 additions & 2 deletions dts/arm/nordic/nrf52805.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,6 @@
generator = <&timer2>;
channel-count = <3>;
clock-prescaler = <0>;
ppi-base = <0>;
gpiote-base = <0>;
#pwm-cells = <1>;
};
};
Expand Down
2 changes: 0 additions & 2 deletions dts/arm/nordic/nrf52810.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -269,8 +269,6 @@
generator = <&timer2>;
channel-count = <3>;
clock-prescaler = <0>;
ppi-base = <0>;
gpiote-base = <0>;
#pwm-cells = <1>;
};
};
Expand Down
2 changes: 0 additions & 2 deletions dts/arm/nordic/nrf52811.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -295,8 +295,6 @@
generator = <&timer2>;
channel-count = <3>;
clock-prescaler = <0>;
ppi-base = <0>;
gpiote-base = <0>;
#pwm-cells = <1>;
};
};
Expand Down
2 changes: 0 additions & 2 deletions dts/arm/nordic/nrf52820.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -335,8 +335,6 @@
generator = <&timer2>;
channel-count = <3>;
clock-prescaler = <0>;
ppi-base = <0>;
gpiote-base = <0>;
#pwm-cells = <1>;
};
};
Expand Down
2 changes: 0 additions & 2 deletions dts/arm/nordic/nrf52832.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -406,8 +406,6 @@
generator = <&timer2>;
channel-count = <3>;
clock-prescaler = <0>;
ppi-base = <0>;
gpiote-base = <0>;
#pwm-cells = <1>;
};
};
Expand Down
2 changes: 0 additions & 2 deletions dts/arm/nordic/nrf52833.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -464,8 +464,6 @@
generator = <&timer2>;
channel-count = <3>;
clock-prescaler = <0>;
ppi-base = <0>;
gpiote-base = <0>;
#pwm-cells = <1>;
};
};
Expand Down
2 changes: 0 additions & 2 deletions dts/arm/nordic/nrf52840.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -484,8 +484,6 @@
generator = <&timer2>;
channel-count = <3>;
clock-prescaler = <0>;
ppi-base = <0>;
gpiote-base = <0>;
#pwm-cells = <1>;
};
};
Expand Down
10 changes: 0 additions & 10 deletions dts/bindings/pwm/nordic,nrf-sw-pwm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,6 @@ properties:
TIMER: 16 MHz / 2^prescaler base clock is used for PWM generation.
required: true

ppi-base:
type: int
description: PPI base used for PPI index calculation used for PWM output generation
required: true

gpiote-base:
type: int
description: GPIOTE base used for GPIOTE index calculation used for PWM output generation
required: true

"#pwm-cells":
const: 1

Expand Down
24 changes: 2 additions & 22 deletions modules/hal_nordic/nrfx/nrfx_glue.h
Original file line number Diff line number Diff line change
Expand Up @@ -229,16 +229,15 @@ void nrfx_busy_wait(uint32_t usec_to_wait);
/** @brief Bitmask that defines PPI channels that are reserved for use outside of the nrfx library. */
#define NRFX_PPI_CHANNELS_USED (NRFX_PPI_CHANNELS_USED_BY_BT_CTLR | \
NRFX_PPI_CHANNELS_USED_BY_802154_DRV | \
NRFX_PPI_CHANNELS_USED_BY_MPSL | \
NRFX_PPI_CHANNELS_USED_BY_PWM_SW)
NRFX_PPI_CHANNELS_USED_BY_MPSL)

/** @brief Bitmask that defines PPI groups that are reserved for use outside of the nrfx library. */
#define NRFX_PPI_GROUPS_USED (NRFX_PPI_GROUPS_USED_BY_BT_CTLR | \
NRFX_PPI_GROUPS_USED_BY_802154_DRV | \
NRFX_PPI_GROUPS_USED_BY_MPSL)

/** @brief Bitmask that defines GPIOTE channels that are reserved for use outside of the nrfx library. */
#define NRFX_GPIOTE_CHANNELS_USED NRFX_GPIOTE_CHANNELS_USED_BY_PWM_SW
#define NRFX_GPIOTE_CHANNELS_USED 0

#if defined(CONFIG_BT_CTLR)
extern const uint32_t z_bt_ctlr_used_nrf_ppi_channels;
Expand Down Expand Up @@ -271,25 +270,6 @@ extern const uint32_t z_mpsl_used_nrf_ppi_groups;
#define NRFX_PPI_GROUPS_USED_BY_MPSL 0
#endif

#if defined(CONFIG_PWM_NRF5_SW)
#define PWM_NRF5_SW_NODE DT_INST(0, nordic_nrf_sw_pwm)
#define PWM_NRF5_SW_GENERATOR_NODE DT_PHANDLE(PWM_NRF5_SW_NODE, generator)
#if DT_NODE_HAS_COMPAT(PWM_NRF5_SW_GENERATOR_NODE, nordic_nrf_rtc)
#define PWM_NRF5_SW_PPI_CHANNELS_PER_PIN 3
#else
#define PWM_NRF5_SW_PPI_CHANNELS_PER_PIN 2
#endif /* DT_NODE_HAS_COMPAT(PWM_NRF5_SW_GENERATOR_NODE, nordic_nrf_rtc) */
#define NRFX_PPI_CHANNELS_USED_BY_PWM_SW \
(BIT_MASK(DT_PROP(PWM_NRF5_SW_NODE, channel_count) * \
PWM_NRF5_SW_PPI_CHANNELS_PER_PIN) \
<< DT_PROP(PWM_NRF5_SW_NODE, ppi_base))
#define NRFX_GPIOTE_CHANNELS_USED_BY_PWM_SW \
DT_PROP(PWM_NRF5_SW_NODE, channel_count)
#else
#define NRFX_PPI_CHANNELS_USED_BY_PWM_SW 0
#define NRFX_GPIOTE_CHANNELS_USED_BY_PWM_SW 0
#endif

/** @brief Bitmask that defines EGU instances that are reserved for use outside of the nrfx library. */
#define NRFX_EGUS_USED 0

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,9 @@

#include <nrf_peripherals.h>

#if defined(CONFIG_PWM_NRF5_SW)
#define HAL_PALNA_GPIOTE_CHAN 3
#define HAL_PDN_GPIOTE_CHAN 4
#define HAL_CSN_GPIOTE_CHAN 5
#else
#define HAL_PALNA_GPIOTE_CHAN 0
#define HAL_PDN_GPIOTE_CHAN 1
#define HAL_CSN_GPIOTE_CHAN 2
#endif

/* This has to come before the ppi/dppi includes below. */
#include "radio_nrf5_fem.h"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -691,11 +691,6 @@ static inline void hal_radio_group_task_disable_ppi_setup(void)
#define HAL_USED_PPI_CHANNELS_6 0
#endif

BUILD_ASSERT(
(HAL_USED_PPI_CHANNELS & NRFX_PPI_CHANNELS_USED_BY_PWM_SW) == 0,
"PPI channels used by the Bluetooth controller overlap with those "
"assigned to the pwm_nrf5_sw driver.");

#if defined(SW_SWITCH_TIMER_TASK_GROUP_BASE)
#define HAL_USED_PPI_GROUPS \
(BIT(SW_SWITCH_TIMER_TASK_GROUP_BASE) | \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -709,11 +709,6 @@ static inline void hal_radio_sw_switch_ppi_group_setup(void)
#define HAL_USED_PPI_CHANNELS_6 0
#endif

BUILD_ASSERT(
(HAL_USED_PPI_CHANNELS & NRFX_PPI_CHANNELS_USED_BY_PWM_SW) == 0,
"PPI channels used by the Bluetooth controller overlap with those "
"assigned to the pwm_nrf5_sw driver.");

#if defined(SW_SWITCH_TIMER_TASK_GROUP_BASE)
#define HAL_USED_PPI_GROUPS \
(BIT(SW_SWITCH_TIMER_TASK_GROUP_BASE) | \
Expand Down

0 comments on commit 9886bdc

Please sign in to comment.