You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
990 lines
32 KiB
990 lines
32 KiB
/* |
|
* Copyright 2023-2025 NXP |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#include <zephyr/device.h> |
|
#include <zephyr/drivers/pwm.h> |
|
#include <zephyr/drivers/pinctrl.h> |
|
#include <zephyr/drivers/clock_control.h> |
|
#include <zephyr/irq.h> |
|
|
|
#include <Emios_Mcl_Ip.h> |
|
#include <Emios_Pwm_Ip.h> |
|
|
|
#ifdef CONFIG_PWM_CAPTURE |
|
#include <Emios_Icu_Ip.h> |
|
#endif |
|
|
|
#define LOG_MODULE_NAME nxp_s32_emios_pwm |
|
#include <zephyr/logging/log.h> |
|
LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_PWM_LOG_LEVEL); |
|
|
|
#define DT_DRV_COMPAT nxp_s32_emios_pwm |
|
|
|
#if !defined(EMIOS_PWM_IP_NUM_OF_CHANNELS_USED) |
|
#define EMIOS_PWM_IP_NUM_OF_CHANNELS_USED EMIOS_PWM_IP_NUM_OF_CHANNELS_USED_U8 |
|
#endif |
|
|
|
/* |
|
* Need to fill to this array at runtime, cannot do at build time like |
|
* the HAL over configuration tool due to limitation of the integration |
|
*/ |
|
#if EMIOS_PWM_IP_USED |
|
|
|
#if defined(CONFIG_SOC_SERIES_S32ZE) |
|
#define eMios_Icu_Ip_IndexInChState eMios_Icu_Ip_u8IndexInChState |
|
#endif |
|
|
|
extern uint8 eMios_Pwm_Ip_IndexInChState[EMIOS_PWM_IP_INSTANCE_COUNT][EMIOS_PWM_IP_CHANNEL_COUNT]; |
|
#define EMIOS_PWM_MASTER_CHANNEL(channel, bus) \ |
|
((bus == EMIOS_PWM_IP_BUS_A) ? 23 : \ |
|
((bus == EMIOS_PWM_IP_BUS_F) ? 22 : \ |
|
((bus == EMIOS_PWM_IP_BUS_BCDE) ? ((channel >> 3) * 8) : channel))) |
|
#endif |
|
|
|
#ifdef CONFIG_PWM_CAPTURE |
|
extern uint8 eMios_Icu_Ip_IndexInChState[EMIOS_ICU_IP_INSTANCE_COUNT][EMIOS_ICU_IP_NUM_OF_CHANNELS]; |
|
|
|
#define EMIOS_ICU_MASTER_CHANNEL(channel, bus) \ |
|
((bus == EMIOS_ICU_BUS_A) ? 23 : \ |
|
(IF_DISABLED(CONFIG_SOC_SERIES_S32ZE, (bus == EMIOS_ICU_BUS_F) ? 22 :) \ |
|
((bus == EMIOS_ICU_BUS_DIVERSE) ? ((channel >> 3) * 8) : channel))) |
|
|
|
/* We need maximum three edges for measure both period and cycle */ |
|
#define MAX_NUM_EDGE 3 |
|
#endif |
|
|
|
struct pwm_nxp_s32_channel_data { |
|
bool inverted; |
|
uint8_t master_channel; |
|
|
|
#if EMIOS_PWM_IP_USED |
|
uint32_t curr_period; |
|
#endif |
|
|
|
#ifdef CONFIG_PWM_CAPTURE |
|
bool continuous; |
|
bool pulse_capture; |
|
bool period_capture; |
|
void *user_data; |
|
pwm_capture_callback_handler_t callback; |
|
eMios_Icu_ValueType edge_buff[MAX_NUM_EDGE]; |
|
#endif |
|
}; |
|
|
|
struct pwm_nxp_s32_data { |
|
uint32_t emios_clk; |
|
#if EMIOS_PWM_IP_USED |
|
uint8_t start_pwm_ch; |
|
#endif |
|
struct pwm_nxp_s32_channel_data ch_data[eMIOS_CH_UC_UC_COUNT]; |
|
}; |
|
|
|
#if EMIOS_PWM_IP_USED |
|
struct pwm_nxp_s32_pulse_info { |
|
uint8_t pwm_pulse_channels; |
|
Emios_Pwm_Ip_ChannelConfigType *pwm_info; |
|
}; |
|
#endif |
|
|
|
struct pwm_nxp_s32_config { |
|
eMIOS_Type *base; |
|
uint8_t instance; |
|
const struct device *clock_dev; |
|
clock_control_subsys_t clock_subsys; |
|
const struct pinctrl_dev_config *pincfg; |
|
|
|
#if EMIOS_PWM_IP_USED |
|
struct pwm_nxp_s32_pulse_info *pulse_info; |
|
#endif |
|
|
|
#ifdef CONFIG_PWM_CAPTURE |
|
eMios_Icu_Ip_ConfigType * icu_cfg; |
|
#endif |
|
}; |
|
|
|
#if EMIOS_PWM_IP_USED |
|
#ifdef EMIOS_PWM_IP_MODE_OPWFMB_USED |
|
static int pwm_nxp_s32_set_cycles_opwfmb(const struct device *dev, uint32_t channel, |
|
uint32_t period_cycles, uint32_t pulse_cycles, |
|
pwm_flags_t flags) |
|
{ |
|
const struct pwm_nxp_s32_config *config = dev->config; |
|
struct pwm_nxp_s32_data *data = dev->data; |
|
struct pwm_nxp_s32_channel_data *ch_data = &data->ch_data[channel]; |
|
|
|
Emios_Pwm_Ip_PolarityType polarity; |
|
|
|
unsigned int key; |
|
|
|
if ((ch_data->inverted != flags) || (!ch_data->curr_period)) { |
|
/* If PWM flag is changed or this is the first time PWM channel is configured */ |
|
polarity = (flags & PWM_POLARITY_MASK) ? EMIOS_PWM_IP_ACTIVE_LOW : |
|
EMIOS_PWM_IP_ACTIVE_HIGH; |
|
|
|
config->base->CH.UC[channel].C &= ~(eMIOS_C_MODE_MASK | |
|
eMIOS_C_EDPOL_MASK); |
|
config->base->CH.UC[channel].A = pulse_cycles; |
|
config->base->CH.UC[channel].B = period_cycles; |
|
|
|
/* |
|
* When entering OPWFMB mode, Output = Cn[EDPOL]. Unless 100% pulse cycle is |
|
* expected, Cn[EDPOL] is set to complement value (i.e 0 if active high and |
|
* 1 if active low). |
|
*/ |
|
if (pulse_cycles) { |
|
config->base->CH.UC[channel].C |= eMIOS_C_EDPOL(polarity); |
|
} else { |
|
config->base->CH.UC[channel].C |= eMIOS_C_EDPOL(!polarity); |
|
} |
|
|
|
key = irq_lock(); |
|
config->base->CH.UC[channel].C |= eMIOS_C_MODE(EMIOS_PWM_IP_MODE_OPWFMB_FLAG); |
|
|
|
if (pulse_cycles) { |
|
/* Restore expected value for Cn[EDPOL] */ |
|
config->base->CH.UC[channel].C = (config->base->CH.UC[channel].C & |
|
~(eMIOS_C_EDPOL_MASK)) | |
|
eMIOS_C_EDPOL(!polarity); |
|
} |
|
irq_unlock(key); |
|
|
|
ch_data->curr_period = period_cycles; |
|
ch_data->inverted = flags; |
|
} else { |
|
key = irq_lock(); |
|
config->base->CH.UC[channel].A = pulse_cycles; |
|
config->base->CH.UC[channel].B = period_cycles; |
|
irq_unlock(key); |
|
} |
|
|
|
return 0; |
|
} |
|
#endif |
|
|
|
#if defined(EMIOS_PWM_IP_MODE_OPWMCB_USED) |
|
static int pwm_nxp_s32_set_cycles_opwmcb(const struct device *dev, uint32_t channel, |
|
uint32_t period_cycles, uint32_t pulse_cycles, |
|
Emios_Pwm_Ip_ChannelConfigType *pwm_info, |
|
pwm_flags_t flags) |
|
{ |
|
const struct pwm_nxp_s32_config *config = dev->config; |
|
struct pwm_nxp_s32_data *data = dev->data; |
|
struct pwm_nxp_s32_channel_data *ch_data = &data->ch_data[channel]; |
|
struct pwm_nxp_s32_channel_data *master_ch_data = &data->ch_data[ch_data->master_channel]; |
|
|
|
unsigned int key; |
|
Emios_Pwm_Ip_PolarityType polarity; |
|
|
|
/* Convert to written value into eMIOS register */ |
|
if (pulse_cycles == 0) { |
|
pulse_cycles = EMIOS_PWM_IP_MAX_CNT_VAL; |
|
} else if (pulse_cycles == period_cycles) { |
|
pulse_cycles = 1; |
|
} else { |
|
pulse_cycles = period_cycles - (pulse_cycles >> 1); |
|
} |
|
|
|
if ((ch_data->inverted != flags) || (!ch_data->curr_period)) { |
|
/* If PWM flag is changed or this is the first time PWM channel is configured */ |
|
polarity = (flags & PWM_POLARITY_MASK) ? EMIOS_PWM_IP_ACTIVE_LOW : |
|
EMIOS_PWM_IP_ACTIVE_HIGH; |
|
|
|
if (master_ch_data->curr_period != period_cycles) { |
|
/* |
|
* Move timebase channel to GPIO mode --> configure period --> MCB mode |
|
* Period can be shared between multiple PWM channels, only configure |
|
* when needed. |
|
*/ |
|
config->base->CH.UC[ch_data->master_channel].C &= ~eMIOS_C_MODE_MASK; |
|
config->base->CH.UC[ch_data->master_channel].A = period_cycles; |
|
} |
|
|
|
config->base->CH.UC[channel].C &= ~(eMIOS_C_MODE_MASK | |
|
eMIOS_C_EDPOL_MASK | |
|
eMIOS_C_BSL_MASK); |
|
|
|
/* |
|
* When entering OPWMCB mode, Output = !Cn[EDPOL]. If 100% pulse cycle is expected |
|
* Cn[EDPOL] is set to complement value (i.e 0 if active high and 1 if active low). |
|
*/ |
|
if (pulse_cycles == 1) { |
|
/* 100% pulse cycle */ |
|
config->base->CH.UC[channel].C |= eMIOS_C_EDPOL(!polarity); |
|
} else { |
|
config->base->CH.UC[channel].C |= eMIOS_C_EDPOL(polarity); |
|
} |
|
|
|
config->base->CH.UC[channel].A = pulse_cycles; |
|
config->base->CH.UC[channel].B = pwm_info->DeadTime; |
|
|
|
key = irq_lock(); |
|
config->base->CH.UC[channel].C |= (eMIOS_C_MODE(pwm_info->Mode) | |
|
eMIOS_C_BSL(pwm_info->Timebase)); |
|
|
|
if (pulse_cycles == 1) { |
|
config->base->CH.UC[channel].C = (config->base->CH.UC[channel].C & |
|
~eMIOS_C_EDPOL_MASK) | |
|
eMIOS_C_EDPOL(polarity); |
|
} |
|
irq_unlock(key); |
|
|
|
if (master_ch_data->curr_period != period_cycles) { |
|
config->base->CH.UC[ch_data->master_channel].C |= |
|
eMIOS_C_MODE(EMIOS_IP_MCB_UP_DOWN_COUNTER); |
|
master_ch_data->curr_period = period_cycles; |
|
} |
|
|
|
ch_data->inverted = flags; |
|
ch_data->curr_period = period_cycles; |
|
} else if (master_ch_data->curr_period != period_cycles) { |
|
key = irq_lock(); |
|
config->base->CH.UC[ch_data->master_channel].A = period_cycles; |
|
config->base->CH.UC[channel].A = pulse_cycles; |
|
irq_unlock(key); |
|
|
|
master_ch_data->curr_period = period_cycles; |
|
} else { |
|
config->base->CH.UC[channel].A = pulse_cycles; |
|
} |
|
|
|
return 0; |
|
} |
|
#endif |
|
|
|
#if defined(EMIOS_PWM_IP_MODE_OPWMB_USED) |
|
static int pwm_nxp_s32_set_cycles_opwmb(const struct device *dev, uint32_t channel, |
|
uint32_t period_cycles, uint32_t pulse_cycles, |
|
Emios_Pwm_Ip_ChannelConfigType *pwm_info, |
|
pwm_flags_t flags) |
|
{ |
|
const struct pwm_nxp_s32_config *config = dev->config; |
|
struct pwm_nxp_s32_data *data = dev->data; |
|
struct pwm_nxp_s32_channel_data *ch_data = &data->ch_data[channel]; |
|
struct pwm_nxp_s32_channel_data *master_ch_data = &data->ch_data[ch_data->master_channel]; |
|
|
|
Emios_Pwm_Ip_PolarityType polarity; |
|
|
|
unsigned int key; |
|
|
|
if ((ch_data->inverted != flags) || (!ch_data->curr_period)) { |
|
/* If PWM flag is changed or this is the first time PWM channel is configured */ |
|
polarity = (flags & PWM_POLARITY_MASK) ? EMIOS_PWM_IP_ACTIVE_LOW : |
|
EMIOS_PWM_IP_ACTIVE_HIGH; |
|
|
|
if (master_ch_data->curr_period != period_cycles) { |
|
/* |
|
* Move timebase channel to GPIO mode --> configure period --> MCB mode |
|
* Period can be shared between multiple PWM channels, only configure |
|
* when needed. |
|
*/ |
|
config->base->CH.UC[ch_data->master_channel].C &= ~eMIOS_C_MODE_MASK; |
|
config->base->CH.UC[ch_data->master_channel].A = period_cycles; |
|
} |
|
|
|
config->base->CH.UC[channel].C &= ~(eMIOS_C_MODE_MASK | |
|
eMIOS_C_EDPOL_MASK | eMIOS_C_BSL_MASK); |
|
|
|
config->base->CH.UC[channel].A = pwm_info->PhaseShift; |
|
config->base->CH.UC[channel].B = pulse_cycles; |
|
|
|
/* |
|
* When entering OPWMB mode, Output = Cn[EDPOL]. Unless 100% pulse cycle is |
|
* expected, Cn[EDPOL] is set to complement value (i.e 0 if active high and |
|
* 1 if active low). |
|
*/ |
|
if (pulse_cycles == period_cycles) { |
|
config->base->CH.UC[channel].C |= eMIOS_C_EDPOL(polarity); |
|
} else { |
|
config->base->CH.UC[channel].C |= eMIOS_C_EDPOL(!polarity); |
|
} |
|
|
|
key = irq_lock(); |
|
config->base->CH.UC[channel].C |= eMIOS_C_MODE(pwm_info->Mode) | |
|
eMIOS_C_BSL(pwm_info->Timebase); |
|
|
|
if (pulse_cycles != period_cycles) { |
|
config->base->CH.UC[channel].C = (config->base->CH.UC[channel].C & |
|
~(eMIOS_C_EDPOL_MASK)) | |
|
eMIOS_C_EDPOL(polarity); |
|
} |
|
|
|
if (!pwm_info->PhaseShift) { |
|
/* |
|
* If Phase Shift == 0, Force Match A to ensure PWM pulse can be |
|
* generated immediately. Otherwise, it will need to wait until |
|
* next period boundary. |
|
*/ |
|
config->base->CH.UC[channel].C |= eMIOS_C_FORCMA(1); |
|
} |
|
irq_unlock(key); |
|
|
|
if (master_ch_data->curr_period != period_cycles) { |
|
config->base->CH.UC[ch_data->master_channel].C |= |
|
eMIOS_C_MODE(EMIOS_IP_MCB_UP_COUNTER); |
|
master_ch_data->curr_period = period_cycles; |
|
} |
|
|
|
ch_data->inverted = flags; |
|
ch_data->curr_period = period_cycles; |
|
} else if (master_ch_data->curr_period != period_cycles) { |
|
key = irq_lock(); |
|
config->base->CH.UC[ch_data->master_channel].A = period_cycles; |
|
config->base->CH.UC[channel].B = pulse_cycles; |
|
irq_unlock(key); |
|
|
|
master_ch_data->curr_period = period_cycles; |
|
} else { |
|
config->base->CH.UC[channel].B = pulse_cycles; |
|
} |
|
|
|
return 0; |
|
} |
|
#endif |
|
|
|
static int pwm_nxp_s32_set_cycles(const struct device *dev, uint32_t channel, |
|
uint32_t period_cycles, uint32_t pulse_cycles, |
|
pwm_flags_t flags) |
|
{ |
|
const struct pwm_nxp_s32_config *config = dev->config; |
|
struct pwm_nxp_s32_data *data = dev->data; |
|
|
|
Emios_Pwm_Ip_ChannelConfigType *pwm_info; |
|
uint8_t logic_ch; |
|
|
|
if (channel >= EMIOS_PWM_IP_CHANNEL_COUNT) { |
|
LOG_ERR("invalid channel %d", channel); |
|
return -EINVAL; |
|
} |
|
|
|
if (eMios_Pwm_Ip_IndexInChState[config->instance][channel] >= |
|
EMIOS_PWM_IP_NUM_OF_CHANNELS_USED) { |
|
LOG_ERR("Channel %d is not configured for PWM", channel); |
|
return -EINVAL; |
|
} |
|
|
|
logic_ch = eMios_Pwm_Ip_IndexInChState[config->instance][channel] - data->start_pwm_ch; |
|
pwm_info = &config->pulse_info->pwm_info[logic_ch]; |
|
|
|
switch (pwm_info->Mode) { |
|
#ifdef EMIOS_PWM_IP_MODE_OPWFMB_USED |
|
case EMIOS_PWM_IP_MODE_OPWFMB_FLAG: |
|
|
|
if ((period_cycles > EMIOS_PWM_IP_MAX_CNT_VAL) || |
|
(period_cycles <= EMIOS_PWM_IP_MIN_CNT_VAL)) { |
|
LOG_ERR("Period cycles is out of range"); |
|
return -EINVAL; |
|
} |
|
|
|
return pwm_nxp_s32_set_cycles_opwfmb(dev, channel, period_cycles, |
|
pulse_cycles, flags); |
|
#endif |
|
|
|
#ifdef EMIOS_PWM_IP_MODE_OPWMCB_USED |
|
case EMIOS_PWM_IP_MODE_OPWMCB_TRAIL_EDGE_FLAG: |
|
case EMIOS_PWM_IP_MODE_OPWMCB_LEAD_EDGE_FLAG: |
|
|
|
period_cycles = (period_cycles + 2) / 2; |
|
|
|
if ((period_cycles > EMIOS_PWM_IP_MAX_CNT_VAL) || |
|
(period_cycles <= EMIOS_PWM_IP_MIN_CNT_VAL)) { |
|
LOG_ERR("Period cycles is out of range"); |
|
return -EINVAL; |
|
} |
|
|
|
return pwm_nxp_s32_set_cycles_opwmcb(dev, channel, period_cycles, |
|
pulse_cycles, pwm_info, flags); |
|
#endif |
|
|
|
#if defined(EMIOS_PWM_IP_MODE_OPWMB_USED) |
|
case EMIOS_PWM_IP_MODE_OPWMB_FLAG: |
|
|
|
if ((period_cycles > EMIOS_PWM_IP_MAX_CNT_VAL) || |
|
(period_cycles <= EMIOS_PWM_IP_MIN_CNT_VAL)) { |
|
LOG_ERR("Period cycles is out of range"); |
|
return -EINVAL; |
|
} |
|
|
|
pulse_cycles += pwm_info->PhaseShift; |
|
|
|
if (pulse_cycles > period_cycles) { |
|
LOG_ERR("Pulse cycles is out of range"); |
|
return -EINVAL; |
|
} |
|
|
|
return pwm_nxp_s32_set_cycles_opwmb(dev, channel, period_cycles, |
|
pulse_cycles, pwm_info, flags); |
|
#endif |
|
|
|
default: |
|
/* Never reach here */ |
|
return 0; |
|
} |
|
} |
|
#endif |
|
|
|
#ifdef CONFIG_PWM_CAPTURE |
|
static ALWAYS_INLINE eMios_Icu_ValueType pwm_nxp_s32_capture_calc(eMios_Icu_ValueType first_cnt, |
|
eMios_Icu_ValueType second_cnt) |
|
{ |
|
if (first_cnt < second_cnt) { |
|
return second_cnt - first_cnt; |
|
} |
|
|
|
/* Counter top value is always 0xFFFF */ |
|
return EMIOS_ICU_IP_COUNTER_MASK - first_cnt + second_cnt; |
|
} |
|
|
|
static ALWAYS_INLINE eMios_Icu_ValueType pwm_nxp_s32_pulse_calc(bool inverted, |
|
eMios_Icu_ValueType *edge_buff, |
|
eMios_Icu_Ip_LevelType input_state) |
|
{ |
|
eMios_Icu_ValueType first_cnt, second_cnt; |
|
|
|
if (input_state ^ inverted) { |
|
/* 3 edges captured is raise, fall, raise */ |
|
first_cnt = edge_buff[0]; |
|
second_cnt = edge_buff[1]; |
|
} else { |
|
/* 3 edges captured is fall, raise, fall */ |
|
first_cnt = edge_buff[1]; |
|
second_cnt = edge_buff[2]; |
|
} |
|
|
|
return pwm_nxp_s32_capture_calc(first_cnt, second_cnt); |
|
} |
|
|
|
static int pwm_nxp_s32_capture_configure(const struct device *dev, |
|
uint32_t channel, |
|
pwm_flags_t flags, |
|
pwm_capture_callback_handler_t cb, |
|
void *user_data) |
|
{ |
|
const struct pwm_nxp_s32_config *config = dev->config; |
|
struct pwm_nxp_s32_data *data = dev->data; |
|
|
|
if (channel >= EMIOS_ICU_IP_NUM_OF_CHANNELS) { |
|
LOG_ERR("Invalid channel %d", channel); |
|
return -EINVAL; |
|
} |
|
|
|
if (!flags) { |
|
LOG_ERR("Invalid PWM capture flag"); |
|
return -EINVAL; |
|
} |
|
|
|
if (eMios_Icu_Ip_IndexInChState[config->instance][channel] >= |
|
EMIOS_ICU_IP_NUM_OF_CHANNELS_USED) { |
|
LOG_ERR("Channel %d is not configured for PWM", channel); |
|
return -EINVAL; |
|
} |
|
|
|
/* If interrupt is enabled --> channel is on-going */ |
|
if (config->base->CH.UC[channel].C & eMIOS_C_FEN_MASK) { |
|
LOG_ERR("Channel %d is busy", channel); |
|
return -EBUSY; |
|
} |
|
|
|
data->ch_data[channel].continuous = (flags & PWM_CAPTURE_MODE_MASK); |
|
data->ch_data[channel].inverted = (flags & PWM_POLARITY_MASK); |
|
data->ch_data[channel].pulse_capture = (flags & PWM_CAPTURE_TYPE_PULSE); |
|
data->ch_data[channel].period_capture = (flags & PWM_CAPTURE_TYPE_PERIOD); |
|
data->ch_data[channel].callback = cb; |
|
data->ch_data[channel].user_data = user_data; |
|
|
|
return 0; |
|
} |
|
|
|
static int pwm_nxp_s32_capture_enable(const struct device *dev, uint32_t channel) |
|
{ |
|
const struct pwm_nxp_s32_config *config = dev->config; |
|
struct pwm_nxp_s32_data *data = dev->data; |
|
|
|
eMios_Icu_Ip_EdgeType edge; |
|
uint8_t num_edge; |
|
|
|
if (channel >= EMIOS_ICU_IP_NUM_OF_CHANNELS) { |
|
LOG_ERR("Invalid channel %d", channel); |
|
return -EINVAL; |
|
} |
|
|
|
if (eMios_Icu_Ip_IndexInChState[config->instance][channel] >= |
|
EMIOS_ICU_IP_NUM_OF_CHANNELS_USED) { |
|
LOG_ERR("Channel %d is not configured for PWM", channel); |
|
return -EINVAL; |
|
} |
|
|
|
if (!data->ch_data[channel].callback) { |
|
LOG_ERR("Callback is not configured"); |
|
return -EINVAL; |
|
} |
|
|
|
/* If interrupt is enabled --> channel is on-going */ |
|
if (config->base->CH.UC[channel].C & eMIOS_C_FEN_MASK) { |
|
LOG_ERR("Channel %d is busy", channel); |
|
return -EBUSY; |
|
} |
|
|
|
/* If just measure period, we just need 2 edges */ |
|
if (data->ch_data[channel].period_capture && !data->ch_data[channel].pulse_capture) { |
|
num_edge = 2U; |
|
edge = EMIOS_ICU_RISING_EDGE; |
|
} else { |
|
num_edge = 3U; |
|
edge = EMIOS_ICU_BOTH_EDGES; |
|
} |
|
|
|
Emios_Icu_Ip_SetActivation(config->instance, channel, edge); |
|
|
|
Emios_Icu_Ip_EnableNotification(config->instance, channel); |
|
|
|
Emios_Icu_Ip_StartTimestamp(config->instance, channel, |
|
data->ch_data[channel].edge_buff, |
|
MAX_NUM_EDGE, num_edge); |
|
|
|
return 0; |
|
} |
|
|
|
static int pwm_nxp_s32_capture_disable(const struct device *dev, uint32_t channel) |
|
{ |
|
const struct pwm_nxp_s32_config *config = dev->config; |
|
|
|
if (channel >= EMIOS_ICU_IP_NUM_OF_CHANNELS) { |
|
LOG_ERR("Invalid channel %d", channel); |
|
return -EINVAL; |
|
} |
|
|
|
if (eMios_Icu_Ip_IndexInChState[config->instance][channel] >= |
|
EMIOS_ICU_IP_NUM_OF_CHANNELS_USED) { |
|
LOG_ERR("Channel %d is not configured for PWM", channel); |
|
return -EINVAL; |
|
} |
|
|
|
Emios_Icu_Ip_StopTimestamp(config->instance, channel); |
|
|
|
return 0; |
|
} |
|
#endif |
|
|
|
static int pwm_nxp_s32_get_cycles_per_sec(const struct device *dev, |
|
uint32_t channel, uint64_t *cycles) |
|
{ |
|
const struct pwm_nxp_s32_config *config = dev->config; |
|
struct pwm_nxp_s32_data *data = dev->data; |
|
|
|
uint8_t internal_prescaler, global_prescaler, master_channel; |
|
|
|
master_channel = data->ch_data[channel].master_channel; |
|
internal_prescaler = (config->base->CH.UC[master_channel].C2 & eMIOS_C2_UCEXTPRE_MASK) >> |
|
eMIOS_C2_UCEXTPRE_SHIFT; |
|
|
|
/* Clock source for internal prescaler is from either eMIOS or eMIOS / global prescaler */ |
|
if (config->base->CH.UC[master_channel].C2 & eMIOS_C2_UCPRECLK_MASK) { |
|
*cycles = data->emios_clk / (internal_prescaler + 1); |
|
} else { |
|
global_prescaler = (config->base->MCR & eMIOS_MCR_GPRE_MASK) >> |
|
eMIOS_MCR_GPRE_SHIFT; |
|
*cycles = data->emios_clk / ((internal_prescaler + 1) * (global_prescaler + 1)); |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
#if EMIOS_PWM_IP_USED |
|
static int pwm_nxp_s32_pulse_gen_init(const struct device *dev) |
|
{ |
|
const struct pwm_nxp_s32_config *config = dev->config; |
|
struct pwm_nxp_s32_data *data = dev->data; |
|
|
|
struct pwm_nxp_s32_channel_data *ch_data; |
|
Emios_Pwm_Ip_ChannelConfigType pwm_info; |
|
|
|
uint8_t ch_id; |
|
static uint8_t logic_ch; |
|
|
|
data->start_pwm_ch = logic_ch; |
|
|
|
for (ch_id = 0; ch_id < config->pulse_info->pwm_pulse_channels; ch_id++) { |
|
memcpy(&pwm_info, &config->pulse_info->pwm_info[ch_id], |
|
sizeof(Emios_Pwm_Ip_ChannelConfigType)); |
|
|
|
/* |
|
* Let eMIOS channel is in GPIO mode, the actual PWM mode will be |
|
* configured at the first time pwm_set* is called. |
|
*/ |
|
pwm_info.Mode = EMIOS_PWM_IP_MODE_GPO; |
|
eMios_Pwm_Ip_IndexInChState[config->instance][pwm_info.ChannelId] = logic_ch++; |
|
Emios_Pwm_Ip_InitChannel(config->instance, &pwm_info); |
|
|
|
ch_data = &data->ch_data[pwm_info.ChannelId]; |
|
ch_data->master_channel = EMIOS_PWM_MASTER_CHANNEL(pwm_info.ChannelId, |
|
pwm_info.Timebase); |
|
} |
|
|
|
return 0; |
|
} |
|
#endif |
|
|
|
#ifdef CONFIG_PWM_CAPTURE |
|
static int pwm_nxp_s32_pulse_capture_init(const struct device *dev) |
|
{ |
|
const struct pwm_nxp_s32_config *config = dev->config; |
|
struct pwm_nxp_s32_data *data = dev->data; |
|
|
|
struct pwm_nxp_s32_channel_data *ch_data; |
|
const eMios_Icu_Ip_ChannelConfigType *icu_info; |
|
|
|
uint8_t ch_id; |
|
static uint8_t logic_ch; |
|
|
|
for (ch_id = 0; ch_id < config->icu_cfg->nNumChannels; ch_id++) { |
|
icu_info = &(*config->icu_cfg->pChannelsConfig)[ch_id]; |
|
ch_data = &data->ch_data[icu_info->hwChannel]; |
|
|
|
eMios_Icu_Ip_IndexInChState[config->instance][icu_info->hwChannel] = logic_ch++; |
|
ch_data->master_channel = EMIOS_ICU_MASTER_CHANNEL(icu_info->hwChannel, |
|
icu_info->CntBus); |
|
} |
|
|
|
if (Emios_Icu_Ip_Init(config->instance, config->icu_cfg)) { |
|
return -EINVAL; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static void pwm_nxp_s32_capture_callback(const struct device *dev, uint32_t channel) |
|
{ |
|
const struct pwm_nxp_s32_config *config = dev->config; |
|
struct pwm_nxp_s32_data *data = dev->data; |
|
|
|
uint32_t period = 0, pulse = 0; |
|
|
|
if (data->ch_data[channel].period_capture && !data->ch_data[channel].pulse_capture) { |
|
period = pwm_nxp_s32_capture_calc(data->ch_data[channel].edge_buff[0], |
|
data->ch_data[channel].edge_buff[1]); |
|
} else { |
|
if (data->ch_data[channel].pulse_capture) { |
|
pulse = pwm_nxp_s32_pulse_calc(data->ch_data[channel].inverted, |
|
data->ch_data[channel].edge_buff, |
|
Emios_Icu_Ip_GetInputLevel(config->instance, |
|
channel)); |
|
} |
|
|
|
if (data->ch_data[channel].period_capture) { |
|
period = pwm_nxp_s32_capture_calc(data->ch_data[channel].edge_buff[0], |
|
data->ch_data[channel].edge_buff[2]); |
|
} |
|
} |
|
|
|
if (!data->ch_data[channel].continuous) { |
|
Emios_Icu_Ip_StopTimestamp(config->instance, channel); |
|
} |
|
|
|
data->ch_data[channel].callback(dev, channel, period, pulse, 0, |
|
data->ch_data[channel].user_data); |
|
} |
|
#endif |
|
|
|
static int pwm_nxp_s32_init(const struct device *dev) |
|
{ |
|
const struct pwm_nxp_s32_config *config = dev->config; |
|
struct pwm_nxp_s32_data *data = dev->data; |
|
|
|
int err = 0; |
|
|
|
if (!device_is_ready(config->clock_dev)) { |
|
return -ENODEV; |
|
} |
|
|
|
if (clock_control_get_rate(config->clock_dev, config->clock_subsys, |
|
&data->emios_clk)) { |
|
return -EINVAL; |
|
} |
|
|
|
err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT); |
|
if (err < 0) { |
|
return err; |
|
} |
|
|
|
#if EMIOS_PWM_IP_USED |
|
err = pwm_nxp_s32_pulse_gen_init(dev); |
|
if (err < 0) { |
|
return err; |
|
} |
|
#endif |
|
|
|
#ifdef CONFIG_PWM_CAPTURE |
|
err = pwm_nxp_s32_pulse_capture_init(dev); |
|
if (err < 0) { |
|
return err; |
|
} |
|
#endif |
|
|
|
return err; |
|
} |
|
|
|
static DEVICE_API(pwm, pwm_nxp_s32_driver_api) = { |
|
.set_cycles = pwm_nxp_s32_set_cycles, |
|
.get_cycles_per_sec = pwm_nxp_s32_get_cycles_per_sec, |
|
#ifdef CONFIG_PWM_CAPTURE |
|
.configure_capture = pwm_nxp_s32_capture_configure, |
|
.enable_capture = pwm_nxp_s32_capture_enable, |
|
.disable_capture = pwm_nxp_s32_capture_disable, |
|
#endif |
|
}; |
|
|
|
#define EMIOS_PWM_IS_MODE_OPWFMB(node_id) \ |
|
DT_ENUM_HAS_VALUE(node_id, pwm_mode, opwfmb) |
|
|
|
#define EMIOS_PWM_IS_MODE_OPWMCB(node_id) \ |
|
UTIL_OR(DT_ENUM_HAS_VALUE(node_id, pwm_mode, opwmcb_trail_edge), \ |
|
DT_ENUM_HAS_VALUE(node_id, pwm_mode, opwmcb_lead_edge)) \ |
|
|
|
#define EMIOS_PWM_IS_MODE_OPWMB(node_id) \ |
|
DT_ENUM_HAS_VALUE(node_id, pwm_mode, opwmb) |
|
|
|
#define EMIOS_PWM_IS_MODE_SAIC(node_id) \ |
|
DT_ENUM_HAS_VALUE(node_id, pwm_mode, saic) |
|
|
|
#define EMIOS_PWM_IS_CAPTURE_MODE(node_id) \ |
|
EMIOS_PWM_IS_MODE_SAIC(node_id) |
|
|
|
#define EMIOS_PWM_LOG(node_id, msg) \ |
|
DT_NODE_PATH(node_id) ": " DT_PROP(node_id, pwm_mode) ": " msg \ |
|
|
|
#define EMIOS_PWM_VERIFY_MASTER_BUS(node_id) \ |
|
BUILD_ASSERT(BIT(DT_PROP(node_id, channel)) & \ |
|
DT_PROP_BY_PHANDLE(node_id, master_bus, channel_mask), \ |
|
EMIOS_PWM_LOG(node_id, "invalid master bus")); |
|
|
|
#define EMIOS_PWM_PULSE_GEN_COMMON_VERIFY(node_id) \ |
|
BUILD_ASSERT(DT_NODE_HAS_PROP(node_id, input_filter), \ |
|
EMIOS_PWM_LOG(node_id, "input-filter is not used")); |
|
|
|
#define EMIOS_PWM_VERIFY_MODE_OPWFMB(node_id) \ |
|
EMIOS_PWM_PULSE_GEN_COMMON_VERIFY(node_id) \ |
|
BUILD_ASSERT(!DT_NODE_HAS_PROP(node_id, master_bus), \ |
|
EMIOS_PWM_LOG(node_id, "master-bus must not be configured")); \ |
|
BUILD_ASSERT(DT_PROP(node_id, dead_time) == 0, \ |
|
EMIOS_PWM_LOG(node_id, "dead-time is not used")); \ |
|
BUILD_ASSERT(DT_PROP(node_id, phase_shift) == 0, \ |
|
EMIOS_PWM_LOG(node_id, "phase-shift is not used")); |
|
|
|
#define EMIOS_PWM_VERIFY_MODE_OPWMCB(node_id) \ |
|
EMIOS_PWM_PULSE_GEN_COMMON_VERIFY(node_id) \ |
|
BUILD_ASSERT(DT_ENUM_HAS_VALUE(DT_PHANDLE(node_id, master_bus), mode, \ |
|
MCB_UP_DOWN_COUNTER), \ |
|
EMIOS_PWM_LOG(node_id, "master-bus must be configured in MCB up-down")); \ |
|
BUILD_ASSERT(DT_PROP(node_id, phase_shift) == 0, \ |
|
EMIOS_PWM_LOG(node_id, "phase-shift is not used")); \ |
|
BUILD_ASSERT(!DT_NODE_HAS_PROP(node_id, prescaler), \ |
|
EMIOS_PWM_LOG(node_id, "prescaler is not used," \ |
|
" driver takes the value from master bus")); \ |
|
BUILD_ASSERT(DT_ENUM_HAS_VALUE(node_id, prescaler_src, prescaled_clock), \ |
|
EMIOS_PWM_LOG(node_id, "prescaler-src is not used," \ |
|
" always use prescalered source")); \ |
|
|
|
#define EMIOS_PWM_VERIFY_MODE_OPWMB(node_id) \ |
|
EMIOS_PWM_PULSE_GEN_COMMON_VERIFY(node_id) \ |
|
BUILD_ASSERT(DT_ENUM_HAS_VALUE(DT_PHANDLE(node_id, master_bus), mode, mcb_up_counter), \ |
|
EMIOS_PWM_LOG(node_id, "master-bus must be configured in MCB up")); \ |
|
BUILD_ASSERT(DT_PROP(node_id, dead_time) == 0, \ |
|
EMIOS_PWM_LOG(node_id, "dead-time is not used")); \ |
|
BUILD_ASSERT(!DT_NODE_HAS_PROP(node_id, prescaler), \ |
|
EMIOS_PWM_LOG(node_id, "prescaler is not used")); \ |
|
BUILD_ASSERT(DT_ENUM_HAS_VALUE(node_id, prescaler_src, prescaled_clock), \ |
|
EMIOS_PWM_LOG(node_id, "prescaler-src is not used," \ |
|
" always use prescalered source")); \ |
|
|
|
#define EMIOS_PWM_VERIFY_MODE_SAIC(node_id) \ |
|
IF_ENABLED(DT_NODE_HAS_PROP(node_id, master_bus), \ |
|
(BUILD_ASSERT( \ |
|
DT_ENUM_HAS_VALUE(DT_PHANDLE(node_id, master_bus), mode, mcb_up_counter), \ |
|
EMIOS_PWM_LOG(node_id, "master-bus must be configured in MCB up"));)) \ |
|
IF_ENABLED(UTIL_NOT(DT_NODE_HAS_PROP(node_id, master_bus)), \ |
|
(BUILD_ASSERT( \ |
|
BIT(DT_PROP(node_id, channel)) & DT_PROP(DT_GPARENT(node_id), internal_cnt),\ |
|
EMIOS_PWM_LOG(node_id, "master-bus must be chosen," \ |
|
" channel do not have has counter")))); \ |
|
IF_ENABLED(UTIL_NOT(DT_NODE_HAS_PROP(node_id, master_bus)), \ |
|
(BUILD_ASSERT(DT_NODE_HAS_PROP(node_id, prescaler), \ |
|
EMIOS_PWM_LOG(node_id, "if use internal counter, prescaler must" \ |
|
" be configured")))); \ |
|
BUILD_ASSERT(DT_ENUM_HAS_VALUE(node_id, prescaler_src, prescaled_clock), \ |
|
EMIOS_PWM_LOG(node_id, "prescaler-src is not used," \ |
|
" always use prescalered source")); |
|
|
|
#define _EMIOS_PWM_VERIFY_CONFIG(node_id) \ |
|
IF_ENABLED(DT_NODE_HAS_PROP(node_id, master_bus), \ |
|
(EMIOS_PWM_VERIFY_MASTER_BUS(node_id))) \ |
|
IF_ENABLED(EMIOS_PWM_IS_MODE_OPWFMB(node_id), \ |
|
(EMIOS_PWM_VERIFY_MODE_OPWFMB(node_id))) \ |
|
IF_ENABLED(EMIOS_PWM_IS_MODE_OPWMCB(node_id), \ |
|
(EMIOS_PWM_VERIFY_MODE_OPWMCB(node_id))) \ |
|
IF_ENABLED(EMIOS_PWM_IS_MODE_OPWMB(node_id), \ |
|
(EMIOS_PWM_VERIFY_MODE_OPWMB(node_id))) \ |
|
IF_ENABLED(EMIOS_PWM_IS_MODE_SAIC(node_id), \ |
|
(EMIOS_PWM_VERIFY_MODE_SAIC(node_id))) |
|
|
|
#if EMIOS_PWM_IP_USED |
|
/* Macros used to glue devicetree with RTD's definition */ |
|
#define EMIOS_PWM_BUS_A EMIOS_PWM_IP_BUS_A |
|
#define EMIOS_PWM_BUS_B EMIOS_PWM_IP_BUS_BCDE |
|
#define EMIOS_PWM_BUS_C EMIOS_PWM_IP_BUS_BCDE |
|
#define EMIOS_PWM_BUS_D EMIOS_PWM_IP_BUS_BCDE |
|
#define EMIOS_PWM_BUS_E EMIOS_PWM_IP_BUS_BCDE |
|
#define EMIOS_PWM_BUS_F EMIOS_PWM_IP_BUS_F |
|
|
|
#define EMIOS_PWM_BUS(mode) DT_CAT(EMIOS_PWM_, mode) |
|
#define EMIOS_PWM_MODE(mode) DT_CAT3(EMIOS_PWM_IP_MODE_, mode, _FLAG) |
|
#define EMIOS_PWM_PS_SRC(mode) DT_CAT(EMIOS_PWM_IP_PS_SRC_, mode) |
|
|
|
/* Keep minimal configuration used at driver initialization, no PWM signal is produced */ |
|
#define _EMIOS_PWM_PULSE_GEN_CONFIG(node_id) \ |
|
IF_ENABLED(UTIL_NOT(EMIOS_PWM_IS_CAPTURE_MODE(node_id)), \ |
|
({ \ |
|
.ChannelId = DT_PROP(node_id, channel), \ |
|
.Mode = EMIOS_PWM_MODE(DT_STRING_TOKEN(node_id, pwm_mode)), \ |
|
.InternalPsSrc = EMIOS_PWM_PS_SRC(DT_STRING_TOKEN(node_id, prescaler_src)), \ |
|
.InternalPs = DT_PROP_OR(node_id, prescaler, \ |
|
DT_PROP_BY_PHANDLE(node_id, master_bus, prescaler)) - 1,\ |
|
.Timebase = COND_CODE_1(DT_NODE_HAS_PROP(node_id, master_bus), \ |
|
(EMIOS_PWM_BUS(DT_STRING_TOKEN( \ |
|
DT_PHANDLE(node_id, master_bus), bus_type))), \ |
|
(EMIOS_PWM_IP_BUS_INTERNAL)), \ |
|
.PhaseShift = DT_PROP(node_id, phase_shift), \ |
|
.DeadTime = DT_PROP(node_id, dead_time), \ |
|
.OutputDisableSource = EMIOS_PWM_IP_OUTPUT_DISABLE_NONE, \ |
|
.OutputPolarity = EMIOS_PWM_IP_ACTIVE_LOW, \ |
|
.DebugMode = DT_PROP(node_id, freeze), \ |
|
.PeriodCount = 0, \ |
|
.DutyCycle = 0, \ |
|
},)) |
|
|
|
#define EMIOS_PWM_PULSE_GEN_CONFIG(n) \ |
|
const Emios_Pwm_Ip_ChannelConfigType emios_pwm_##n##_init[] = { \ |
|
DT_INST_FOREACH_CHILD_STATUS_OKAY(n, _EMIOS_PWM_PULSE_GEN_CONFIG) \ |
|
}; \ |
|
const struct pwm_nxp_s32_pulse_info emios_pwm_##n##_info = { \ |
|
.pwm_pulse_channels = ARRAY_SIZE(emios_pwm_##n##_init), \ |
|
.pwm_info = (Emios_Pwm_Ip_ChannelConfigType *)emios_pwm_##n##_init, \ |
|
}; |
|
|
|
#define EMIOS_PWM_PULSE_GEN_GET_CONFIG(n) \ |
|
.pulse_info = (struct pwm_nxp_s32_pulse_info *)&emios_pwm_##n##_info, |
|
#else |
|
#define EMIOS_PWM_PULSE_GEN_CONFIG(n) |
|
#define EMIOS_PWM_PULSE_GEN_GET_CONFIG(n) |
|
#endif |
|
|
|
#ifdef CONFIG_PWM_CAPTURE |
|
/* Macros used to glue devicetree with RTD's definition */ |
|
#define EMIOS_ICU_BUS_A EMIOS_ICU_BUS_A |
|
#define EMIOS_ICU_BUS_B EMIOS_ICU_BUS_DIVERSE |
|
#define EMIOS_ICU_BUS_C EMIOS_ICU_BUS_DIVERSE |
|
#define EMIOS_ICU_BUS_D EMIOS_ICU_BUS_DIVERSE |
|
#define EMIOS_ICU_BUS_E EMIOS_ICU_BUS_DIVERSE |
|
#if !defined(CONFIG_SOC_SERIES_S32ZE) |
|
#define EMIOS_ICU_BUS_F EMIOS_ICU_BUS_F |
|
#endif |
|
|
|
#define DIGITAL_FILTER_0 EMIOS_DIGITAL_FILTER_BYPASSED |
|
#define DIGITAL_FILTER_2 EMIOS_DIGITAL_FILTER_02 |
|
#define DIGITAL_FILTER_4 EMIOS_DIGITAL_FILTER_04 |
|
#define DIGITAL_FILTER_8 EMIOS_DIGITAL_FILTER_08 |
|
#define DIGITAL_FILTER_16 EMIOS_DIGITAL_FILTER_16 |
|
|
|
#define EMIOS_PWM_CAPTURE_FILTER(filter) DT_CAT(DIGITAL_FILTER_, filter) |
|
#define EMIOS_PWM_CAPTURE_MODE(mode) DT_CAT(EMIOS_ICU_, mode) |
|
#define EMIOS_PWM_CAPTURE_BUS(mode) DT_CAT(EMIOS_ICU_, mode) |
|
|
|
#define EMIOS_PWM_CAPTURE_CB(n, ch) \ |
|
DT_CAT5(pwm_nxp_s32_, n, _channel_, ch, _capture_callback) |
|
|
|
#define EMIOS_PWM_CALLBACK_DECLARE(node_id, n) \ |
|
void EMIOS_PWM_CAPTURE_CB(n, DT_PROP(node_id, channel))(void) \ |
|
{ \ |
|
pwm_nxp_s32_capture_callback(DEVICE_DT_INST_GET(n), DT_PROP(node_id, channel)); \ |
|
} \ |
|
|
|
#define _EMIOS_PWM_PULSE_CAPTURE_CONFIG(node_id, n) \ |
|
IF_ENABLED(EMIOS_PWM_IS_CAPTURE_MODE(node_id), \ |
|
({ \ |
|
.hwChannel = DT_PROP(node_id, channel), \ |
|
.ucMode = EMIOS_PWM_CAPTURE_MODE(DT_STRING_TOKEN(node_id, pwm_mode)), \ |
|
.FreezeEn = DT_PROP(node_id, freeze), \ |
|
.Prescaler = COND_CODE_1(DT_NODE_HAS_PROP(node_id, master_bus), \ |
|
(DT_PROP_BY_PHANDLE(node_id, master_bus, prescaler)), \ |
|
(DT_PROP(node_id, prescaler))) - 1, \ |
|
.CntBus = COND_CODE_1(DT_NODE_HAS_PROP(node_id, master_bus), \ |
|
(EMIOS_PWM_CAPTURE_BUS(DT_STRING_TOKEN( \ |
|
DT_PHANDLE(node_id, master_bus), bus_type))),\ |
|
(EMIOS_ICU_BUS_INTERNAL_COUNTER)), \ |
|
.chMode = EMIOS_ICU_MODE_TIMESTAMP, \ |
|
.chSubMode = EMIOS_ICU_MODE_WITHOUT_DMA, \ |
|
.measurementMode = EMIOS_ICU_NO_MEASUREMENT, \ |
|
.edgeAlignement = EMIOS_ICU_BOTH_EDGES, \ |
|
.Filter = EMIOS_PWM_CAPTURE_FILTER(DT_PROP(node_id, input_filter)), \ |
|
.callback = NULL_PTR, \ |
|
.logicChStateCallback = NULL_PTR, \ |
|
.callbackParams = 255U, \ |
|
.bWithoutInterrupt = FALSE, \ |
|
.timestampBufferType = EMIOS_ICU_CIRCULAR_BUFFER, \ |
|
.eMiosChannelNotification = EMIOS_PWM_CAPTURE_CB(n, DT_PROP(node_id, channel)), \ |
|
.eMiosOverflowNotification = NULL_PTR, \ |
|
},)) |
|
|
|
#define EMIOS_PWM_PULSE_CAPTURE_CONFIG(n) \ |
|
DT_INST_FOREACH_CHILD_STATUS_OKAY_VARGS(n, EMIOS_PWM_CALLBACK_DECLARE, n) \ |
|
const eMios_Icu_Ip_ChannelConfigType emios_pwm_##n##_capture_init[] = { \ |
|
DT_INST_FOREACH_CHILD_STATUS_OKAY_VARGS(n, _EMIOS_PWM_PULSE_CAPTURE_CONFIG, n) \ |
|
}; \ |
|
const eMios_Icu_Ip_ConfigType emios_pwm_##n##_capture_info = { \ |
|
.nNumChannels = ARRAY_SIZE(emios_pwm_##n##_capture_init), \ |
|
.pChannelsConfig = &emios_pwm_##n##_capture_init, \ |
|
}; |
|
|
|
#define EMIOS_PWM_PULSE_CAPTURE_GET_CONFIG(n) \ |
|
.icu_cfg = (eMios_Icu_Ip_ConfigType *)&emios_pwm_##n##_capture_info, |
|
#else |
|
#define EMIOS_PWM_PULSE_CAPTURE_CONFIG(n) |
|
#define EMIOS_PWM_PULSE_CAPTURE_GET_CONFIG(n) |
|
#endif |
|
|
|
#define EMIOS_PWM_VERIFY_CONFIG(n) \ |
|
DT_INST_FOREACH_CHILD_STATUS_OKAY(n, _EMIOS_PWM_VERIFY_CONFIG) |
|
|
|
#define EMIOS_NXP_S32_INSTANCE_CHECK(idx, node_id) \ |
|
((DT_REG_ADDR(node_id) == IP_EMIOS_##idx##_BASE) ? idx : 0) |
|
|
|
#define EMIOS_NXP_S32_GET_INSTANCE(node_id) \ |
|
LISTIFY(__DEBRACKET eMIOS_INSTANCE_COUNT, EMIOS_NXP_S32_INSTANCE_CHECK, (|), node_id) |
|
|
|
#define PWM_NXP_S32_INIT_DEVICE(n) \ |
|
PINCTRL_DT_INST_DEFINE(n); \ |
|
EMIOS_PWM_VERIFY_CONFIG(n) \ |
|
EMIOS_PWM_PULSE_GEN_CONFIG(n) \ |
|
EMIOS_PWM_PULSE_CAPTURE_CONFIG(n) \ |
|
static const struct pwm_nxp_s32_config pwm_nxp_s32_config_##n = { \ |
|
.base = (eMIOS_Type *)DT_REG_ADDR(DT_INST_PARENT(n)), \ |
|
.instance = EMIOS_NXP_S32_GET_INSTANCE(DT_INST_PARENT(n)), \ |
|
.clock_dev = DEVICE_DT_GET(DT_CLOCKS_CTLR(DT_INST_PARENT(n))), \ |
|
.clock_subsys = (clock_control_subsys_t)DT_CLOCKS_CELL(DT_INST_PARENT(n), name),\ |
|
.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ |
|
EMIOS_PWM_PULSE_GEN_GET_CONFIG(n) \ |
|
EMIOS_PWM_PULSE_CAPTURE_GET_CONFIG(n) \ |
|
}; \ |
|
static struct pwm_nxp_s32_data pwm_nxp_s32_data_##n; \ |
|
DEVICE_DT_INST_DEFINE(n, \ |
|
&pwm_nxp_s32_init, \ |
|
NULL, \ |
|
&pwm_nxp_s32_data_##n, \ |
|
&pwm_nxp_s32_config_##n, \ |
|
POST_KERNEL, \ |
|
CONFIG_PWM_INIT_PRIORITY, \ |
|
&pwm_nxp_s32_driver_api); |
|
|
|
DT_INST_FOREACH_STATUS_OKAY(PWM_NXP_S32_INIT_DEVICE)
|
|
|