drivers: pwm_nrfx: Add support for pinctrl

Add support for the new pinctrl API to the nRF PWM driver.
Update code of the driver and the related devicetree binding.

Signed-off-by: Andrzej Głąbek <andrzej.glabek@nordicsemi.no>
This commit is contained in:
Andrzej Głąbek 2022-03-09 10:54:05 +01:00 committed by Carles Cufí
commit 44feb7d527
2 changed files with 141 additions and 51 deletions

View file

@ -6,6 +6,8 @@
#include <nrfx_pwm.h> #include <nrfx_pwm.h>
#include <drivers/pwm.h> #include <drivers/pwm.h>
#include <pm/device.h> #include <pm/device.h>
#include <drivers/pinctrl.h>
#include <soc.h>
#include <hal/nrf_gpio.h> #include <hal/nrf_gpio.h>
#include <stdbool.h> #include <stdbool.h>
@ -15,14 +17,16 @@ LOG_MODULE_REGISTER(pwm_nrfx);
#define PWM_NRFX_CH_POLARITY_MASK BIT(15) #define PWM_NRFX_CH_POLARITY_MASK BIT(15)
#define PWM_NRFX_CH_PULSE_CYCLES_MASK BIT_MASK(15) #define PWM_NRFX_CH_PULSE_CYCLES_MASK BIT_MASK(15)
#define PWM_NRFX_CH_VALUE_NORMAL PWM_NRFX_CH_POLARITY_MASK #define PWM_NRFX_CH_VALUE(value, inverted) \
#define PWM_NRFX_CH_VALUE_INVERTED (0) (value | (inverted ? 0 : PWM_NRFX_CH_POLARITY_MASK))
#define PWM_NRFX_CH_PIN_MASK ~NRFX_PWM_PIN_INVERTED
struct pwm_nrfx_config { struct pwm_nrfx_config {
nrfx_pwm_t pwm; nrfx_pwm_t pwm;
nrfx_pwm_config_t initial_config; nrfx_pwm_config_t initial_config;
nrf_pwm_sequence_t seq; nrf_pwm_sequence_t seq;
#ifdef CONFIG_PINCTRL
const struct pinctrl_dev_config *pcfg;
#endif
}; };
struct pwm_nrfx_data { struct pwm_nrfx_data {
@ -30,6 +34,7 @@ struct pwm_nrfx_data {
uint16_t current[NRF_PWM_CHANNEL_COUNT]; uint16_t current[NRF_PWM_CHANNEL_COUNT];
uint16_t countertop; uint16_t countertop;
uint8_t prescaler; uint8_t prescaler;
uint8_t inverted_channels;
}; };
@ -84,14 +89,14 @@ static int pwm_period_check_and_set(const struct pwm_nrfx_config *config,
return -EINVAL; return -EINVAL;
} }
static uint8_t pwm_channel_map(const uint8_t *output_pins, uint32_t pwm) static uint8_t pwm_channel_map(const struct pwm_nrfx_config *config,
uint32_t pwm)
{ {
uint8_t i; uint8_t i;
/* Find pin, return channel number */ /* Find pin, return channel number */
for (i = 0U; i < NRF_PWM_CHANNEL_COUNT; i++) { for (i = 0U; i < NRF_PWM_CHANNEL_COUNT; i++) {
if (output_pins[i] != NRFX_PWM_PIN_NOT_USED if (nrf_pwm_pin_get(config->pwm.p_registers, i) == pwm) {
&& (pwm == (output_pins[i] & PWM_NRFX_CH_PIN_MASK))) {
return i; return i;
} }
} }
@ -146,7 +151,7 @@ static int pwm_nrfx_pin_set(const struct device *dev, uint32_t pwm,
* Return its array index (channel number), * Return its array index (channel number),
* or NRF_PWM_CHANNEL_COUNT if not initialized through DTS. * or NRF_PWM_CHANNEL_COUNT if not initialized through DTS.
*/ */
channel = pwm_channel_map(config->initial_config.output_pins, pwm); channel = pwm_channel_map(config, pwm);
if (channel == NRF_PWM_CHANNEL_COUNT) { if (channel == NRF_PWM_CHANNEL_COUNT) {
LOG_ERR("PWM pin %d not enabled through DTS configuration.", LOG_ERR("PWM pin %d not enabled through DTS configuration.",
pwm); pwm);
@ -211,8 +216,7 @@ static int pwm_nrfx_pin_set(const struct device *dev, uint32_t pwm,
* If pulse 100% and pin not inverted, set HIGH. * If pulse 100% and pin not inverted, set HIGH.
*/ */
bool channel_inverted_state = bool channel_inverted_state =
config->initial_config.output_pins[channel] data->inverted_channels & BIT(channel);
& NRFX_PWM_PIN_INVERTED;
bool pulse_0_and_not_inverted = bool pulse_0_and_not_inverted =
(pulse_cycles == 0U) (pulse_cycles == 0U)
@ -274,12 +278,33 @@ static int pwm_nrfx_init(const struct device *dev)
const struct pwm_nrfx_config *config = dev->config; const struct pwm_nrfx_config *config = dev->config;
struct pwm_nrfx_data *data = dev->data; struct pwm_nrfx_data *data = dev->data;
for (size_t i = 0; i < ARRAY_SIZE(data->current); i++) { #ifdef CONFIG_PINCTRL
bool inverted = config->initial_config.output_pins[i] & NRFX_PWM_PIN_INVERTED; int ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
uint16_t value = (inverted)?(PWM_NRFX_CH_VALUE_INVERTED):(PWM_NRFX_CH_VALUE_NORMAL);
data->current[i] = value; if (ret < 0) {
}; return ret;
}
data->inverted_channels = 0;
for (size_t i = 0; i < ARRAY_SIZE(data->current); i++) {
uint32_t psel = nrf_pwm_pin_get(config->pwm.p_registers, i);
/* Mark channels as inverted according to what initial state
* of their outputs has been set by pinctrl (high idle state
* means that the channel is inverted).
*/
if (((psel & PWM_PSEL_OUT_CONNECT_Msk) >> PWM_PSEL_OUT_CONNECT_Pos)
== PWM_PSEL_OUT_CONNECT_Connected) {
data->inverted_channels |=
nrf_gpio_pin_out_read(psel) ? BIT(i) : 0;
}
}
#endif
for (size_t i = 0; i < ARRAY_SIZE(data->current); i++) {
bool inverted = data->inverted_channels & BIT(i);
data->current[i] = PWM_NRFX_CH_VALUE(0, inverted);
}
nrfx_err_t result = nrfx_pwm_init(&config->pwm, nrfx_err_t result = nrfx_pwm_init(&config->pwm,
&config->initial_config, &config->initial_config,
@ -306,20 +331,38 @@ static void pwm_nrfx_uninit(const struct device *dev)
static int pwm_nrfx_pm_action(const struct device *dev, static int pwm_nrfx_pm_action(const struct device *dev,
enum pm_device_action action) enum pm_device_action action)
{ {
int err = 0; #ifdef CONFIG_PINCTRL
const struct pwm_nrfx_config *config = dev->config;
#endif
int ret = 0;
switch (action) { switch (action) {
case PM_DEVICE_ACTION_RESUME: case PM_DEVICE_ACTION_RESUME:
err = pwm_nrfx_init(dev); #ifdef CONFIG_PINCTRL
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_DEFAULT);
if (ret < 0) {
return ret;
}
#endif
ret = pwm_nrfx_init(dev);
break; break;
case PM_DEVICE_ACTION_SUSPEND: case PM_DEVICE_ACTION_SUSPEND:
pwm_nrfx_uninit(dev); pwm_nrfx_uninit(dev);
#ifdef CONFIG_PINCTRL
ret = pinctrl_apply_state(config->pcfg, PINCTRL_STATE_SLEEP);
if (ret < 0) {
return ret;
}
#endif
break; break;
default: default:
return -ENOTSUP; return -ENOTSUP;
} }
return err; return ret;
} }
#else #else
@ -330,41 +373,52 @@ static int pwm_nrfx_pm_action(const struct device *dev,
#define PWM(dev_idx) DT_NODELABEL(pwm##dev_idx) #define PWM(dev_idx) DT_NODELABEL(pwm##dev_idx)
#define PWM_PROP(dev_idx, prop) DT_PROP(PWM(dev_idx), prop) #define PWM_PROP(dev_idx, prop) DT_PROP(PWM(dev_idx), prop)
#define PWM_NRFX_IS_INVERTED(dev_idx, ch_idx) \ #define PWM_CH_INVERTED(dev_idx, ch_idx) \
PWM_PROP(dev_idx, ch##ch_idx##_inverted) PWM_PROP(dev_idx, ch##ch_idx##_inverted)
#define PWM_NRFX_CH_PIN(dev_idx, ch_idx) \ #define PWM_OUTPUT_PIN(dev_idx, ch_idx) \
COND_CODE_1(DT_NODE_HAS_PROP(PWM(dev_idx), ch##ch_idx##_pin), \ COND_CODE_1(DT_NODE_HAS_PROP(PWM(dev_idx), ch##ch_idx##_pin), \
(PWM_PROP(dev_idx, ch##ch_idx##_pin)), \ (PWM_PROP(dev_idx, ch##ch_idx##_pin) | \
(PWM_CH_INVERTED(dev_idx, ch_idx) \
? NRFX_PWM_PIN_INVERTED : 0)), \
(NRFX_PWM_PIN_NOT_USED)) (NRFX_PWM_PIN_NOT_USED))
#define PWM_NRFX_OUTPUT_PIN(dev_idx, ch_idx) \
(PWM_NRFX_CH_PIN(dev_idx, ch_idx) | \
(PWM_NRFX_IS_INVERTED(dev_idx, ch_idx) ? NRFX_PWM_PIN_INVERTED : 0))
#define PWM_NRFX_COUNT_MODE(dev_idx) \
(PWM_PROP(dev_idx, center_aligned) ? \
NRF_PWM_MODE_UP_AND_DOWN : NRF_PWM_MODE_UP)
#define PWM_NRFX_DEVICE(idx) \ #define PWM_NRFX_DEVICE(idx) \
static struct pwm_nrfx_data pwm_nrfx_##idx##_data; \ NRF_DT_ENSURE_PINS_ASSIGNED(PWM(idx), \
ch0_pin, ch1_pin, ch2_pin, ch3_pin); \
static struct pwm_nrfx_data pwm_nrfx_##idx##_data = { \
COND_CODE_1(CONFIG_PINCTRL, (), \
(.inverted_channels = \
(PWM_CH_INVERTED(idx, 0) ? BIT(0) : 0) | \
(PWM_CH_INVERTED(idx, 1) ? BIT(1) : 0) | \
(PWM_CH_INVERTED(idx, 2) ? BIT(2) : 0) | \
(PWM_CH_INVERTED(idx, 3) ? BIT(3) : 0),)) \
}; \
IF_ENABLED(CONFIG_PINCTRL, (PINCTRL_DT_DEFINE(PWM(idx)))); \
static const struct pwm_nrfx_config pwm_nrfx_##idx##config = { \ static const struct pwm_nrfx_config pwm_nrfx_##idx##config = { \
.pwm = NRFX_PWM_INSTANCE(idx), \ .pwm = NRFX_PWM_INSTANCE(idx), \
.initial_config = { \ .initial_config = { \
.output_pins = { \ COND_CODE_1(CONFIG_PINCTRL, \
PWM_NRFX_OUTPUT_PIN(idx, 0), \ (.skip_gpio_cfg = true, \
PWM_NRFX_OUTPUT_PIN(idx, 1), \ .skip_psel_cfg = true,), \
PWM_NRFX_OUTPUT_PIN(idx, 2), \ (.output_pins = { \
PWM_NRFX_OUTPUT_PIN(idx, 3), \ PWM_OUTPUT_PIN(idx, 0), \
}, \ PWM_OUTPUT_PIN(idx, 1), \
PWM_OUTPUT_PIN(idx, 2), \
PWM_OUTPUT_PIN(idx, 3), \
},)) \
.base_clock = NRF_PWM_CLK_1MHz, \ .base_clock = NRF_PWM_CLK_1MHz, \
.count_mode = PWM_NRFX_COUNT_MODE(idx), \ .count_mode = (PWM_PROP(idx, center_aligned) \
? NRF_PWM_MODE_UP_AND_DOWN \
: NRF_PWM_MODE_UP), \
.top_value = 1000, \ .top_value = 1000, \
.load_mode = NRF_PWM_LOAD_INDIVIDUAL, \ .load_mode = NRF_PWM_LOAD_INDIVIDUAL, \
.step_mode = NRF_PWM_STEP_TRIGGERED, \ .step_mode = NRF_PWM_STEP_TRIGGERED, \
}, \ }, \
.seq.values.p_raw = pwm_nrfx_##idx##_data.current, \ .seq.values.p_raw = pwm_nrfx_##idx##_data.current, \
.seq.length = NRF_PWM_CHANNEL_COUNT \ .seq.length = NRF_PWM_CHANNEL_COUNT, \
IF_ENABLED(CONFIG_PINCTRL, \
(.pcfg = PINCTRL_DT_DEV_CONFIG_GET(PWM(idx)),)) \
}; \ }; \
PM_DEVICE_DT_DEFINE(PWM(idx), pwm_nrfx_pm_action); \ PM_DEVICE_DT_DEFINE(PWM(idx), pwm_nrfx_pm_action); \
DEVICE_DT_DEFINE(PWM(idx), \ DEVICE_DT_DEFINE(PWM(idx), \

View file

@ -2,7 +2,7 @@ description: nRF PWM
compatible: "nordic,nrf-pwm" compatible: "nordic,nrf-pwm"
include: [pwm-controller.yaml, base.yaml] include: [pwm-controller.yaml, base.yaml, pinctrl-device.yaml]
properties: properties:
reg: reg:
@ -17,6 +17,9 @@ properties:
type: int type: int
required: false required: false
description: | description: |
IMPORTANT: This option will only be used if the new pin control driver
is not enabled. It will be deprecated in the future.
The channel 0 pin to use. The channel 0 pin to use.
For pins P0.0 through P0.31, use the pin number. For example, For pins P0.0 through P0.31, use the pin number. For example,
@ -32,43 +35,76 @@ properties:
ch0-inverted: ch0-inverted:
type: boolean type: boolean
required: false required: false
description: Set this to invert channel 0. description: |
IMPORTANT: This option will only be used if the new pin control driver
is not enabled. It will be deprecated in the future.
When the pin control driver is enabled, use the "nordic,invert" property
in the corresponding pin configuration group instead.
Set this to invert channel 0.
ch1-pin: ch1-pin:
type: int type: int
required: false required: false
description: | description: |
IMPORTANT: This option will only be used if the new pin control driver
is not enabled. It will be deprecated in the future.
The channel 1 pin to use. The pin numbering scheme is the same The channel 1 pin to use. The pin numbering scheme is the same
as the ch0-pin property's. as the ch0-pin property's.
ch1-inverted: ch1-inverted:
type: boolean type: boolean
required: false required: false
description: Set this to invert channel 1. description: |
IMPORTANT: This option will only be used if the new pin control driver
is not enabled. It will be deprecated in the future.
When the pin control driver is enabled, use the "nordic,invert" property
in the corresponding pin configuration group instead.
Set this to invert channel 1.
ch2-pin: ch2-pin:
type: int type: int
required: false required: false
description: | description: |
IMPORTANT: This option will only be used if the new pin control driver
is not enabled. It will be deprecated in the future.
The channel 2 pin to use. The pin numbering scheme is the same The channel 2 pin to use. The pin numbering scheme is the same
as the ch0-pin property's. as the ch0-pin property's.
ch2-inverted: ch2-inverted:
type: boolean type: boolean
required: false required: false
description: Set this to invert channel 2. description: |
IMPORTANT: This option will only be used if the new pin control driver
is not enabled. It will be deprecated in the future.
When the pin control driver is enabled, use the "nordic,invert" property
in the corresponding pin configuration group instead.
Set this to invert channel 2.
ch3-pin: ch3-pin:
type: int type: int
required: false required: false
description: | description: |
IMPORTANT: This option will only be used if the new pin control driver
is not enabled. It will be deprecated in the future.
The channel 3 pin to use. The pin numbering scheme is the same The channel 3 pin to use. The pin numbering scheme is the same
as the ch0-pin property's. as the ch0-pin property's.
ch3-inverted: ch3-inverted:
type: boolean type: boolean
required: false required: false
description: Set this to invert channel 3. description: |
IMPORTANT: This option will only be used if the new pin control driver
is not enabled. It will be deprecated in the future.
When the pin control driver is enabled, use the "nordic,invert" property
in the corresponding pin configuration group instead.
Set this to invert channel 3.
"#pwm-cells": "#pwm-cells":
const: 1 const: 1