drivers: pwm_nrf5_sw: Treat pwm
parameter as PWM channel, not SoC pin
Align with other PWM drivers and treat the `pwm` parameter (described ambiguously as "PWM pin") of the `pwm_pin_set_cycles` function as a PWM channel, not an SoC pin. This will also make the driver consistent with the `pwm-cells` property definition in the "nordic,nrf-sw-pwm" binding and with related `DT_PWMS_*` macros. The change described above requires also providing a way to specify SoC pins that are to be assigned to the PWM channels. Hence, the commit introduces in the "nordic,nrf-sw-pwm" binding the `channel-gpios` property that replaces the `channel-count` one. Signed-off-by: Andrzej Głąbek <andrzej.glabek@nordicsemi.no>
This commit is contained in:
parent
e7a075f460
commit
c23a449bfb
10 changed files with 68 additions and 72 deletions
|
@ -31,11 +31,12 @@ BUILD_ASSERT(DT_INST_PROP(0, clock_prescaler) == 0,
|
|||
#define GENERATOR_ADDR ((NRF_TIMER_Type *) DT_REG_ADDR(GENERATOR_NODE))
|
||||
#endif
|
||||
|
||||
#define PWM_0_MAP_SIZE DT_INST_PROP_LEN(0, channel_gpios)
|
||||
|
||||
/* One compare channel is needed to set the PWM period, hence +1. */
|
||||
#if ((DT_INST_PROP(0, channel_count) + 1) > GENERATOR_CC_NUM)
|
||||
#if ((PWM_0_MAP_SIZE + 1) > GENERATOR_CC_NUM)
|
||||
#error "Invalid number of PWM channels configured."
|
||||
#endif
|
||||
#define PWM_0_MAP_SIZE DT_INST_PROP(0, channel_count)
|
||||
|
||||
/* When RTC is used, one more PPI task endpoint is required for clearing
|
||||
* the counter, so when FORK feature is not available, one more PPI channel
|
||||
|
@ -52,20 +53,16 @@ struct pwm_config {
|
|||
NRF_RTC_Type *rtc;
|
||||
NRF_TIMER_Type *timer;
|
||||
};
|
||||
uint8_t psel_ch[PWM_0_MAP_SIZE];
|
||||
uint8_t map_size;
|
||||
uint8_t prescaler;
|
||||
};
|
||||
|
||||
struct chan_map {
|
||||
uint32_t pwm;
|
||||
uint32_t pulse_cycles;
|
||||
};
|
||||
|
||||
struct pwm_data {
|
||||
uint32_t period_cycles;
|
||||
uint32_t pulse_cycles[PWM_0_MAP_SIZE];
|
||||
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];
|
||||
};
|
||||
|
||||
static inline NRF_RTC_Type *pwm_config_rtc(const struct pwm_config *config)
|
||||
|
@ -87,7 +84,7 @@ static inline NRF_TIMER_Type *pwm_config_timer(const struct pwm_config *config)
|
|||
}
|
||||
|
||||
static uint32_t pwm_period_check(struct pwm_data *data, uint8_t map_size,
|
||||
uint32_t pwm, uint32_t period_cycles,
|
||||
uint32_t channel, uint32_t period_cycles,
|
||||
uint32_t pulse_cycles)
|
||||
{
|
||||
uint8_t i;
|
||||
|
@ -99,8 +96,8 @@ static uint32_t pwm_period_check(struct pwm_data *data, uint8_t map_size,
|
|||
|
||||
/* fail if requested period does not match already running period */
|
||||
for (i = 0U; i < map_size; i++) {
|
||||
if ((data->map[i].pwm != pwm) &&
|
||||
(data->map[i].pulse_cycles != 0U) &&
|
||||
if ((i != channel) &&
|
||||
(data->pulse_cycles[i] != 0U) &&
|
||||
(period_cycles != data->period_cycles)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -109,29 +106,6 @@ static uint32_t pwm_period_check(struct pwm_data *data, uint8_t map_size,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static uint8_t pwm_channel_map(struct pwm_data *data, uint8_t map_size,
|
||||
uint32_t pwm)
|
||||
{
|
||||
uint8_t i;
|
||||
|
||||
/* find pin, if already present */
|
||||
for (i = 0U; i < map_size; i++) {
|
||||
if (pwm == data->map[i].pwm) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
/* find a free entry */
|
||||
i = map_size;
|
||||
while (i--) {
|
||||
if (data->map[i].pulse_cycles == 0U) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
|
||||
static int pwm_nrf5_sw_pin_set(const struct device *dev, uint32_t pwm,
|
||||
uint32_t period_cycles, uint32_t pulse_cycles,
|
||||
pwm_flags_t flags)
|
||||
|
@ -141,7 +115,8 @@ static int pwm_nrf5_sw_pin_set(const struct device *dev, uint32_t pwm,
|
|||
NRF_RTC_Type *rtc = pwm_config_rtc(config);
|
||||
struct pwm_data *data = dev->data;
|
||||
uint32_t ppi_mask;
|
||||
uint8_t channel;
|
||||
uint8_t channel = pwm;
|
||||
uint8_t psel_ch;
|
||||
uint8_t gpiote_ch;
|
||||
const uint8_t *ppi_chs;
|
||||
uint32_t ret;
|
||||
|
@ -151,10 +126,15 @@ static int pwm_nrf5_sw_pin_set(const struct device *dev, uint32_t pwm,
|
|||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
if (channel >= config->map_size) {
|
||||
LOG_ERR("Invalid channel: %u.", channel);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* check if requested period is allowed while other channels are
|
||||
* active.
|
||||
*/
|
||||
ret = pwm_period_check(data, config->map_size, pwm, period_cycles,
|
||||
ret = pwm_period_check(data, config->map_size, channel, period_cycles,
|
||||
pulse_cycles);
|
||||
if (ret) {
|
||||
LOG_ERR("Incompatible period");
|
||||
|
@ -172,23 +152,18 @@ static int pwm_nrf5_sw_pin_set(const struct device *dev, uint32_t pwm,
|
|||
* resolution, use that info in config struct.
|
||||
*/
|
||||
if (period_cycles > UINT16_MAX) {
|
||||
LOG_ERR("Too long period (%u), adjust pwm prescaler!",
|
||||
LOG_ERR("Too long period (%u), adjust PWM prescaler!",
|
||||
period_cycles);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* map pwm pin to GPIOTE config/channel */
|
||||
channel = pwm_channel_map(data, config->map_size, pwm);
|
||||
psel_ch = config->psel_ch[channel];
|
||||
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;
|
||||
}
|
||||
|
||||
LOG_DBG("PWM %d, period %u, pulse %u", pwm,
|
||||
period_cycles, pulse_cycles);
|
||||
LOG_DBG("channel %u, period %u, pulse %u",
|
||||
channel, period_cycles, pulse_cycles);
|
||||
|
||||
/* clear GPIOTE config */
|
||||
NRF_GPIOTE->CONFIG[gpiote_ch] = 0;
|
||||
|
@ -199,20 +174,20 @@ static int pwm_nrf5_sw_pin_set(const struct device *dev, uint32_t pwm,
|
|||
NRF_PPI->CHENCLR = ppi_mask;
|
||||
|
||||
/* configure GPIO pin as output */
|
||||
nrf_gpio_cfg_output(pwm);
|
||||
nrf_gpio_cfg_output(psel_ch);
|
||||
if (pulse_cycles == 0U) {
|
||||
/* 0% duty cycle, keep pin low */
|
||||
nrf_gpio_pin_clear(pwm);
|
||||
nrf_gpio_pin_clear(psel_ch);
|
||||
|
||||
goto pin_set_pwm_off;
|
||||
} else if (pulse_cycles == period_cycles) {
|
||||
/* 100% duty cycle, keep pin high */
|
||||
nrf_gpio_pin_set(pwm);
|
||||
nrf_gpio_pin_set(psel_ch);
|
||||
|
||||
goto pin_set_pwm_off;
|
||||
} else {
|
||||
/* x% duty cycle, start PWM with pin low */
|
||||
nrf_gpio_pin_clear(pwm);
|
||||
nrf_gpio_pin_clear(psel_ch);
|
||||
}
|
||||
|
||||
/* configure RTC / TIMER */
|
||||
|
@ -238,7 +213,7 @@ static int pwm_nrf5_sw_pin_set(const struct device *dev, uint32_t pwm,
|
|||
}
|
||||
|
||||
/* configure GPIOTE, toggle with initialise output high */
|
||||
NRF_GPIOTE->CONFIG[gpiote_ch] = 0x00130003 | (pwm << 8);
|
||||
NRF_GPIOTE->CONFIG[gpiote_ch] = 0x00130003 | (psel_ch << 8);
|
||||
|
||||
/* setup PPI */
|
||||
if (USE_RTC) {
|
||||
|
@ -278,20 +253,19 @@ static int pwm_nrf5_sw_pin_set(const struct device *dev, uint32_t pwm,
|
|||
timer->TASKS_START = 1;
|
||||
}
|
||||
|
||||
/* store the pwm/pin and its param */
|
||||
/* store the period and pulse cycles */
|
||||
data->period_cycles = period_cycles;
|
||||
data->map[channel].pwm = pwm;
|
||||
data->map[channel].pulse_cycles = pulse_cycles;
|
||||
data->pulse_cycles[channel] = pulse_cycles;
|
||||
|
||||
return 0;
|
||||
|
||||
pin_set_pwm_off:
|
||||
data->map[channel].pulse_cycles = 0U;
|
||||
data->pulse_cycles[channel] = 0U;
|
||||
bool pwm_active = false;
|
||||
|
||||
/* stop timer if all channels are inactive */
|
||||
for (channel = 0U; channel < config->map_size; channel++) {
|
||||
if (data->map[channel].pulse_cycles) {
|
||||
if (data->pulse_cycles[channel]) {
|
||||
pwm_active = true;
|
||||
break;
|
||||
}
|
||||
|
@ -389,8 +363,14 @@ static int pwm_nrf5_sw_init(const struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#define PSEL_AND_COMMA(_node_id, _prop, _idx) \
|
||||
NRF_DT_GPIOS_TO_PSEL_BY_IDX(_node_id, _prop, _idx),
|
||||
|
||||
static const struct pwm_config pwm_nrf5_sw_0_config = {
|
||||
COND_CODE_1(USE_RTC, (.rtc), (.timer)) = GENERATOR_ADDR,
|
||||
.psel_ch = {
|
||||
DT_INST_FOREACH_PROP_ELEM(0, channel_gpios, PSEL_AND_COMMA)
|
||||
},
|
||||
.map_size = PWM_0_MAP_SIZE,
|
||||
.prescaler = DT_INST_PROP(0, clock_prescaler),
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue