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.
521 lines
26 KiB
521 lines
26 KiB
/* |
|
* Copyright 2025 NXP |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#define DT_DRV_COMPAT nxp_s32_psi5 |
|
|
|
#include <zephyr/logging/log.h> |
|
|
|
LOG_MODULE_REGISTER(nxp_s32_psi5, CONFIG_PSI5_LOG_LEVEL); |
|
|
|
#include <zephyr/kernel.h> |
|
#include <zephyr/device.h> |
|
#include <zephyr/drivers/pinctrl.h> |
|
|
|
#include <zephyr/drivers/psi5/psi5.h> |
|
|
|
#include "Psi5_Ip.h" |
|
|
|
struct psi5_nxp_s32_config { |
|
uint8_t ctrl_inst; |
|
PSI5_Type *base; |
|
uint8_t channel_mask; |
|
const struct pinctrl_dev_config *pin_cfg; |
|
void (*irq_config_func)(void); |
|
}; |
|
|
|
struct psi5_nxp_s32_tx_callback { |
|
psi5_tx_callback_t callback; |
|
void *user_data; |
|
}; |
|
|
|
struct psi5_nxp_s32_channel_data { |
|
bool started; |
|
bool async_mode; |
|
struct psi5_nxp_s32_tx_callback tx_callback; |
|
struct psi5_rx_callback_configs callback_configs; |
|
uint32_t serial_frame_cnt; |
|
uint32_t data_frame_cnt; |
|
struct k_sem tx_sem; |
|
struct k_mutex lock; |
|
}; |
|
|
|
struct psi5_nxp_s32_data { |
|
struct psi5_nxp_s32_channel_data channel_data[PSI5_CHANNEL_COUNT]; |
|
}; |
|
|
|
struct psi5_nxp_s32_tx_default_cb_ctx { |
|
struct k_sem done; |
|
int status; |
|
}; |
|
|
|
static int psi5_nxp_s32_start_sync(const struct device *dev, uint8_t channel) |
|
{ |
|
const struct psi5_nxp_s32_config *config = dev->config; |
|
struct psi5_nxp_s32_data *data = dev->data; |
|
struct psi5_nxp_s32_channel_data *channel_data = &data->channel_data[channel]; |
|
int err; |
|
|
|
if (!(config->channel_mask & BIT(channel))) { |
|
return -EINVAL; |
|
} |
|
|
|
if (channel_data->started) { |
|
return -EALREADY; |
|
} |
|
|
|
if (channel_data->async_mode) { |
|
return -ENOTSUP; |
|
} |
|
|
|
k_mutex_lock(&channel_data->lock, K_FOREVER); |
|
|
|
err = Psi5_Ip_SetChannelSync(config->ctrl_inst, channel, true); |
|
|
|
if (err) { |
|
LOG_ERR("Failed to start sync PSI5 %d channel %d", config->ctrl_inst, channel); |
|
k_mutex_unlock(&channel_data->lock); |
|
return -EIO; |
|
} |
|
|
|
channel_data->started = true; |
|
|
|
k_mutex_unlock(&channel_data->lock); |
|
|
|
|
|
return 0; |
|
} |
|
|
|
static int psi5_nxp_s32_stop_sync(const struct device *dev, uint8_t channel) |
|
{ |
|
const struct psi5_nxp_s32_config *config = dev->config; |
|
struct psi5_nxp_s32_data *data = dev->data; |
|
struct psi5_nxp_s32_channel_data *channel_data = &data->channel_data[channel]; |
|
int err; |
|
|
|
if (!(config->channel_mask & BIT(channel))) { |
|
return -EINVAL; |
|
} |
|
|
|
if (!channel_data->started) { |
|
return -EALREADY; |
|
} |
|
|
|
if (channel_data->async_mode) { |
|
return -ENOTSUP; |
|
} |
|
|
|
k_mutex_lock(&channel_data->lock, K_FOREVER); |
|
|
|
err = Psi5_Ip_SetChannelSync(config->ctrl_inst, channel, false); |
|
|
|
if (err) { |
|
LOG_ERR("Failed to stop sync PSI5 %d channel %d", config->ctrl_inst, channel); |
|
k_mutex_unlock(&channel_data->lock); |
|
return -EIO; |
|
} |
|
|
|
channel_data->started = false; |
|
|
|
k_mutex_unlock(&channel_data->lock); |
|
|
|
return 0; |
|
} |
|
|
|
static int psi5_nxp_s32_do_send(const struct device *dev, uint8_t channel, uint64_t psi5_data, |
|
k_timeout_t timeout, psi5_tx_callback_t callback, void *user_data) |
|
{ |
|
const struct psi5_nxp_s32_config *config = dev->config; |
|
struct psi5_nxp_s32_data *data = dev->data; |
|
struct psi5_nxp_s32_channel_data *channel_data = &data->channel_data[channel]; |
|
int err; |
|
|
|
if (k_sem_take(&channel_data->tx_sem, timeout) != 0) { |
|
return -EAGAIN; |
|
} |
|
|
|
channel_data->tx_callback.callback = callback; |
|
channel_data->tx_callback.user_data = user_data; |
|
|
|
err = Psi5_Ip_Transmit(config->ctrl_inst, channel, psi5_data); |
|
if (err) { |
|
LOG_ERR("Failed to transmit PSI5 %d channel %d", config->ctrl_inst, channel); |
|
k_sem_give(&channel_data->tx_sem); |
|
return -EIO; |
|
} |
|
|
|
return 0; |
|
} |
|
|
|
static void psi5_nxp_s32_tx_default_cb(const struct device *dev, uint8_t channel_id, |
|
int status, void *user_data) |
|
{ |
|
ARG_UNUSED(dev); |
|
ARG_UNUSED(channel_id); |
|
struct psi5_nxp_s32_tx_default_cb_ctx *ctx = user_data; |
|
|
|
ctx->status = status; |
|
|
|
k_sem_give(&ctx->done); |
|
} |
|
|
|
static int psi5_nxp_s32_send(const struct device *dev, uint8_t channel, uint64_t psi5_data, |
|
k_timeout_t timeout, psi5_tx_callback_t callback, void *user_data) |
|
{ |
|
const struct psi5_nxp_s32_config *config = dev->config; |
|
struct psi5_nxp_s32_data *data = dev->data; |
|
struct psi5_nxp_s32_channel_data *channel_data = &data->channel_data[channel]; |
|
|
|
if (!(config->channel_mask & BIT(channel))) { |
|
return -EINVAL; |
|
} |
|
|
|
if (!channel_data->started) { |
|
return -ENETDOWN; |
|
} |
|
|
|
if (channel_data->async_mode) { |
|
return -ENOTSUP; |
|
} |
|
|
|
if (callback == NULL) { |
|
struct psi5_nxp_s32_tx_default_cb_ctx ctx; |
|
int err; |
|
|
|
k_sem_init(&ctx.done, 0, 1); |
|
|
|
err = psi5_nxp_s32_do_send(dev, channel, psi5_data, timeout, |
|
psi5_nxp_s32_tx_default_cb, &ctx); |
|
if (err != 0) { |
|
return err; |
|
} |
|
|
|
k_sem_take(&ctx.done, K_FOREVER); |
|
|
|
return ctx.status; |
|
} |
|
|
|
return psi5_nxp_s32_do_send(dev, channel, psi5_data, timeout, callback, user_data); |
|
} |
|
|
|
static int psi5_nxp_s32_register_callback(const struct device *dev, uint8_t channel, |
|
struct psi5_rx_callback_configs callback_configs) |
|
{ |
|
const struct psi5_nxp_s32_config *config = dev->config; |
|
struct psi5_nxp_s32_data *data = dev->data; |
|
struct psi5_nxp_s32_channel_data *channel_data = &data->channel_data[channel]; |
|
|
|
if (!(config->channel_mask & BIT(channel))) { |
|
return -EINVAL; |
|
} |
|
|
|
k_mutex_lock(&channel_data->lock, K_FOREVER); |
|
|
|
channel_data->callback_configs = callback_configs; |
|
|
|
k_mutex_unlock(&channel_data->lock); |
|
|
|
return 0; |
|
} |
|
|
|
static DEVICE_API(psi5, psi5_nxp_s32_driver_api) = { |
|
.start_sync = psi5_nxp_s32_start_sync, |
|
.stop_sync = psi5_nxp_s32_stop_sync, |
|
.send = psi5_nxp_s32_send, |
|
.register_callback = psi5_nxp_s32_register_callback, |
|
}; |
|
|
|
#define PSI5_NXP_S32_HW_INSTANCE_CHECK(i, n) ((DT_INST_REG_ADDR(n) == IP_PSI5_##i##_BASE) ? i : 0) |
|
|
|
#define PSI5_NXP_S32_HW_INSTANCE(n) \ |
|
LISTIFY(PSI5_INSTANCE_COUNT, PSI5_NXP_S32_HW_INSTANCE_CHECK, (|), n) |
|
|
|
#define PSI5_NXP_S32_CHANNEL_CALLBACK(node_id) \ |
|
void _CONCAT(psi5_nxp_s32_channel_callBack, node_id)(Psi5_EventType event) \ |
|
{ \ |
|
const struct device *dev = DEVICE_DT_GET(DT_PARENT(node_id)); \ |
|
const struct psi5_nxp_s32_config *config = dev->config; \ |
|
struct psi5_nxp_s32_data *data = dev->data; \ |
|
uint8_t channel = DT_REG_ADDR(node_id); \ |
|
struct psi5_nxp_s32_channel_data *channel_data = &data->channel_data[channel]; \ |
|
struct psi5_nxp_s32_tx_callback *tx_callback = &channel_data->tx_callback; \ |
|
struct psi5_rx_callback_config *serial_frame_cb = \ |
|
channel_data->callback_configs.serial_frame; \ |
|
struct psi5_rx_callback_config *data_frame_cb = \ |
|
channel_data->callback_configs.data_frame; \ |
|
\ |
|
if (event.Psi5_DriverReadyToTransmit) { \ |
|
if (tx_callback->callback) { \ |
|
tx_callback->callback(dev, channel, 0, \ |
|
tx_callback->user_data); \ |
|
k_sem_give(&channel_data->tx_sem); \ |
|
} \ |
|
} else if (event.Psi5_TxDataOverwrite) { \ |
|
if (tx_callback->callback) { \ |
|
tx_callback->callback(dev, channel, -EIO, \ |
|
tx_callback->user_data); \ |
|
k_sem_give(&channel_data->tx_sem); \ |
|
} \ |
|
} else if (event.Psi5_Psi5MessageReceived && data_frame_cb) { \ |
|
Psi5_Ip_Psi5FrameType ip_frame; \ |
|
uint32_t *data_frame_cnt = &channel_data->data_frame_cnt; \ |
|
Psi5_Ip_GetPsi5Frame(config->ctrl_inst, channel, &ip_frame); \ |
|
\ |
|
if (!!(ip_frame.C | ip_frame.F | ip_frame.EM | ip_frame.E | ip_frame.T)) { \ |
|
data_frame_cb->callback(dev, channel, \ |
|
*data_frame_cnt, \ |
|
data_frame_cb->user_data); \ |
|
*data_frame_cnt = 0; \ |
|
} else { \ |
|
data_frame_cb->frame[*data_frame_cnt].type = PSI5_DATA_FRAME; \ |
|
data_frame_cb->frame[*data_frame_cnt].data = ip_frame.DATA_REGION; \ |
|
data_frame_cb->frame[*data_frame_cnt].timestamp = \ |
|
ip_frame.TIME_STAMP; \ |
|
data_frame_cb->frame[*data_frame_cnt].crc = ip_frame.CRC; \ |
|
data_frame_cb->frame[*data_frame_cnt].slot_number = \ |
|
ip_frame.SLOT_COUNTER; \ |
|
(*data_frame_cnt)++; \ |
|
if (*data_frame_cnt == data_frame_cb->max_num_frame) { \ |
|
data_frame_cb->callback(dev, channel, \ |
|
*data_frame_cnt, \ |
|
data_frame_cb->user_data); \ |
|
*data_frame_cnt = 0; \ |
|
} \ |
|
} \ |
|
} else if (event.Psi5_SmcMessageReceived && serial_frame_cb) { \ |
|
Psi5_Ip_SmcFrameType ip_smc_frame; \ |
|
uint32_t *serial_frame_cnt = &channel_data->serial_frame_cnt; \ |
|
Psi5_Ip_GetSmcFrame(config->ctrl_inst, channel, &ip_smc_frame); \ |
|
\ |
|
if (!!(ip_smc_frame.CER | ip_smc_frame.OW)) { \ |
|
serial_frame_cb->callback(dev, channel, \ |
|
*serial_frame_cnt, \ |
|
serial_frame_cb->user_data); \ |
|
*serial_frame_cnt = 0; \ |
|
} else { \ |
|
if (ip_smc_frame.C) { \ |
|
serial_frame_cb->frame[*serial_frame_cnt].type = \ |
|
PSI5_SERIAL_FRAME_4_BIT_ID; \ |
|
serial_frame_cb->frame[*serial_frame_cnt].serial.id = \ |
|
ip_smc_frame.ID; \ |
|
serial_frame_cb->frame[*serial_frame_cnt].serial.data = \ |
|
FIELD_PREP(GENMASK(15, 12), \ |
|
(ip_smc_frame.IDDATA)) | \ |
|
FIELD_PREP(GENMASK(11, 0), (ip_smc_frame.DATA)); \ |
|
} else { \ |
|
serial_frame_cb->frame[*serial_frame_cnt].type = \ |
|
PSI5_SERIAL_FRAME_8_BIT_ID; \ |
|
serial_frame_cb->frame[0].serial.id = \ |
|
FIELD_PREP(GENMASK(7, 4), (ip_smc_frame.ID)) | \ |
|
FIELD_PREP(GENMASK(3, 0), (ip_smc_frame.IDDATA)); \ |
|
serial_frame_cb->frame[0].serial.data = ip_smc_frame.DATA; \ |
|
} \ |
|
serial_frame_cb->frame[*serial_frame_cnt].crc = ip_smc_frame.CRC; \ |
|
serial_frame_cb->frame[*serial_frame_cnt].slot_number = \ |
|
ip_smc_frame.SLOT_NO; \ |
|
(*serial_frame_cnt)++; \ |
|
\ |
|
if (*serial_frame_cnt == serial_frame_cb->max_num_frame) { \ |
|
serial_frame_cb->callback( \ |
|
dev, channel, \ |
|
*serial_frame_cnt, serial_frame_cb->user_data); \ |
|
*serial_frame_cnt = 0; \ |
|
} \ |
|
} \ |
|
} \ |
|
} |
|
|
|
#define PSI5_NXP_S32_SLOT_NODE(ch_node_id, slot) DT_CHILD(ch_node_id, slot_##slot) |
|
|
|
#define PSI5_NXP_S32_SLOT_CNT(slot, ch_node_id) \ |
|
(DT_NODE_HAS_STATUS(PSI5_NXP_S32_SLOT_NODE(ch_node_id, slot), okay) ? 1 : 0) |
|
|
|
#define __PSI5_NXP_S32_CHANNEL_RX_SLOT_CONFIG(slot, ch_node_id) \ |
|
{ \ |
|
.slotId = DT_REG_ADDR(PSI5_NXP_S32_SLOT_NODE(ch_node_id, slot)) + 1, \ |
|
.slotLen = DT_PROP(PSI5_NXP_S32_SLOT_NODE(ch_node_id, slot), duration_us), \ |
|
.startOffs = DT_PROP(PSI5_NXP_S32_SLOT_NODE(ch_node_id, slot), start_offset_us), \ |
|
.dataSize = DT_PROP(PSI5_NXP_S32_SLOT_NODE(ch_node_id, slot), data_length), \ |
|
.msbFirst = DT_PROP(PSI5_NXP_S32_SLOT_NODE(ch_node_id, slot), data_msb_first), \ |
|
.hasSMC = DT_PROP(PSI5_NXP_S32_SLOT_NODE(ch_node_id, slot), has_smc), \ |
|
.hasParity = DT_PROP(PSI5_NXP_S32_SLOT_NODE(ch_node_id, slot), has_parity), \ |
|
}, |
|
|
|
#define _PSI5_NXP_S32_CHANNEL_RX_SLOT_CONFIG(slot, ch_node_id) \ |
|
IF_ENABLED(DT_NODE_HAS_STATUS(PSI5_NXP_S32_SLOT_NODE(ch_node_id, slot), okay), \ |
|
(__PSI5_NXP_S32_CHANNEL_RX_SLOT_CONFIG(slot, ch_node_id))) |
|
|
|
#define PSI5_NXP_S32_CHANNEL_RX_SLOT_CONFIG(node_id) \ |
|
static const Psi5_Ip_SlotConfigType _CONCAT(psi5_nxp_s32_channel_rx_slot_config, \ |
|
node_id)[PSI5_CHANNEL_CH_SFCR_COUNT] = { \ |
|
LISTIFY(PSI5_CHANNEL_CH_SFCR_COUNT, \ |
|
_PSI5_NXP_S32_CHANNEL_RX_SLOT_CONFIG, (), node_id)}; |
|
|
|
#define PSI5_NXP_S32_CHANNEL_RX_CONFIG(node_id) \ |
|
const Psi5_Ip_ChannelRxConfigType _CONCAT(psi5_nxp_s32_channel_rx_config, node_id) = { \ |
|
.rxBufSize = DT_PROP(node_id, num_rx_buf), \ |
|
.bitRate = DT_ENUM_IDX(node_id, rx_bitrate_kbps), \ |
|
.slotConfig = &_CONCAT(psi5_nxp_s32_channel_rx_slot_config, node_id)[0], \ |
|
.numOfSlotConfigs = \ |
|
LISTIFY(PSI5_CHANNEL_CH_SFCR_COUNT, PSI5_NXP_S32_SLOT_CNT, (+), node_id), \ |
|
.watermarkInterruptLevel = GENMASK(UTIL_DEC(DT_PROP(node_id, num_rx_buf)), 0), \ |
|
}; |
|
|
|
#define PSI5_NXP_S32_CHANNEL_TX_CONFIG(node_id) \ |
|
const Psi5_Ip_ChannelTxConfigType _CONCAT(psi5_nxp_s32_channel_tx_config, node_id) = { \ |
|
.targetPulse = DT_PROP_OR(node_id, period_sync_pulse_us, 0), \ |
|
.decoderOffset = DT_PROP_OR(node_id, decoder_start_offset_us, 0), \ |
|
.pulse0Width = DT_PROP_OR(node_id, pulse_width_0_us, 0), \ |
|
.pulse1Width = DT_PROP_OR(node_id, pulse_width_1_us, 0), \ |
|
.txMode = DT_ENUM_IDX_OR(node_id, tx_frame, 0), \ |
|
.syncState = PSI5_SYNC_STATE_2, \ |
|
.txSize = 64, /* This setting is applicable only in NON STANDARD FRAME */ \ |
|
}; |
|
|
|
#define PSI5_NXP_S32_CHANNEL_ERR_SEL_CONFIG(node_id) \ |
|
const Psi5_Ip_ErrorSelectConfigType _CONCAT(psi5_nxp_s32_channel_err_sel_config, \ |
|
node_id) = { \ |
|
.errorSelect0 = true, \ |
|
.errorSelect1 = true, \ |
|
.errorSelect2 = true, \ |
|
.errorSelect3 = true, \ |
|
.errorSelect4 = true, \ |
|
}; |
|
|
|
/* |
|
* The macro get index of array configuration that corresponds to each the ID of HW channel. |
|
* Assign 0xff to unused channels. |
|
*/ |
|
|
|
#define PSI5_NXP_S32_CHANNEL_NODE(n, i) DT_INST_CHILD(n, DT_CAT(ch_, i)) |
|
|
|
#define PSI5_NXP_S32_ID_CFG_CNT(i, node_id, n) \ |
|
(DT_NODE_HAS_STATUS(PSI5_NXP_S32_CHANNEL_NODE(n, i), okay) && \ |
|
(DT_REG_ADDR(PSI5_NXP_S32_CHANNEL_NODE(n, i)) < (DT_REG_ADDR(node_id))) \ |
|
? 1 \ |
|
: 0) |
|
|
|
#define PSI5_NXP_S32_ID_CFG(node_id, n) \ |
|
COND_CODE_1(DT_NODE_HAS_STATUS(node_id, okay), \ |
|
(LISTIFY(PSI5_CHANNEL_COUNT, PSI5_NXP_S32_ID_CFG_CNT, (+), node_id, n),), (0xff,)) |
|
|
|
#define PSI5_NXP_S32_CHANNEL_CONFIG(node_id) \ |
|
{ \ |
|
.channelId = DT_REG_ADDR(node_id), \ |
|
.channelMode = !DT_PROP(node_id, async_mode), \ |
|
.callback = _CONCAT(psi5_nxp_s32_channel_callBack, node_id), \ |
|
.rxConfig = &_CONCAT(psi5_nxp_s32_channel_rx_config, node_id), \ |
|
.txConfig = &_CONCAT(psi5_nxp_s32_channel_tx_config, node_id), \ |
|
.errorSelectConfig = &_CONCAT(psi5_nxp_s32_channel_err_sel_config, node_id), \ |
|
}, |
|
|
|
/* Define array channel configuration */ |
|
#define PSI5_NXP_S32_ARRAY_CHANNEL_CONFIG(n) \ |
|
DT_INST_FOREACH_CHILD_STATUS_OKAY(n, PSI5_NXP_S32_CHANNEL_CALLBACK) \ |
|
DT_INST_FOREACH_CHILD_STATUS_OKAY(n, PSI5_NXP_S32_CHANNEL_RX_SLOT_CONFIG) \ |
|
DT_INST_FOREACH_CHILD_STATUS_OKAY(n, PSI5_NXP_S32_CHANNEL_RX_CONFIG) \ |
|
DT_INST_FOREACH_CHILD_STATUS_OKAY(n, PSI5_NXP_S32_CHANNEL_TX_CONFIG) \ |
|
DT_INST_FOREACH_CHILD_STATUS_OKAY(n, PSI5_NXP_S32_CHANNEL_ERR_SEL_CONFIG) \ |
|
const Psi5_Ip_ChannelConfigType \ |
|
psi5_nxp_s32_channel_array_config_##n[DT_INST_CHILD_NUM_STATUS_OKAY(n)] = { \ |
|
DT_INST_FOREACH_CHILD_STATUS_OKAY(n, PSI5_NXP_S32_CHANNEL_CONFIG)}; \ |
|
const uint8_t psi5_nxp_s32_map_idex_array_config_##n[PSI5_CHANNEL_COUNT] = { \ |
|
DT_INST_FOREACH_CHILD_VARGS(n, PSI5_NXP_S32_ID_CFG, n)}; |
|
|
|
DT_INST_FOREACH_STATUS_OKAY(PSI5_NXP_S32_ARRAY_CHANNEL_CONFIG) |
|
|
|
/* Define array instance configuration */ |
|
#define PSI5_NXP_S32_INST_CONFIG(n) \ |
|
{ \ |
|
.instanceId = PSI5_NXP_S32_HW_INSTANCE(n), \ |
|
.channelConfig = &psi5_nxp_s32_channel_array_config_##n[0], \ |
|
.numOfChannels = DT_INST_CHILD_NUM_STATUS_OKAY(n), \ |
|
.chHwIdToIndexArrayConfig = &psi5_nxp_s32_map_idex_array_config_##n[0], \ |
|
}, |
|
|
|
static const Psi5_Ip_InstanceType psi5_nxp_s32_array_inst_config[DT_NUM_INST_STATUS_OKAY( |
|
DT_DRV_COMPAT)] = {DT_INST_FOREACH_STATUS_OKAY(PSI5_NXP_S32_INST_CONFIG)}; |
|
|
|
/* The structure configuration for all controller instances that used for Psi5_Ip_Init() */ |
|
static const Psi5_Ip_ConfigType psi5_nxp_s32_controller_config = { |
|
.instancesConfig = &psi5_nxp_s32_array_inst_config[0], |
|
.numOfInstances = DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT), |
|
}; |
|
|
|
#define PSI5_NXP_S32_CHANNEL_ISR(node_id) \ |
|
static void _CONCAT(psi5_nxp_s32_channel_isr, node_id)(const struct device *dev) \ |
|
{ \ |
|
const struct psi5_nxp_s32_config *config = dev->config; \ |
|
\ |
|
Psi5_Ip_IRQ_Handler(config->ctrl_inst, DT_REG_ADDR(node_id)); \ |
|
} |
|
|
|
#define PSI5_NXP_S32_CHANNEL_IRQ_CONFIG(node_id, n) \ |
|
do { \ |
|
IRQ_CONNECT(DT_IRQ_BY_IDX(node_id, 0, irq), DT_IRQ_BY_IDX(node_id, 0, priority), \ |
|
_CONCAT(psi5_nxp_s32_channel_isr, node_id), DEVICE_DT_INST_GET(n), \ |
|
DT_IRQ_BY_IDX(node_id, 0, flags)); \ |
|
irq_enable(DT_IRQN(node_id)); \ |
|
} while (false); |
|
|
|
#define PSI5_NXP_S32_IRQ_CONFIG(n) \ |
|
DT_INST_FOREACH_CHILD_STATUS_OKAY(n, PSI5_NXP_S32_CHANNEL_ISR) \ |
|
static void psi5_irq_config_##n(void) \ |
|
{ \ |
|
DT_INST_FOREACH_CHILD_STATUS_OKAY_VARGS(n, PSI5_NXP_S32_CHANNEL_IRQ_CONFIG, n) \ |
|
} |
|
|
|
#define PSI5_NXP_S32_CHANNEL_BIT_MASK(node_id) BIT(DT_REG_ADDR(node_id)) |
|
|
|
#define PSI5_NXP_S32_CHANNEL_ASYNC_MODE(node_id) \ |
|
data->channel_data[DT_REG_ADDR(node_id)].async_mode = DT_PROP(node_id, async_mode); |
|
|
|
#define DEV_PSI5_NXP_S32_INIT(n) \ |
|
PINCTRL_DT_INST_DEFINE(n); \ |
|
PSI5_NXP_S32_IRQ_CONFIG(n) \ |
|
static struct psi5_nxp_s32_config psi5_nxp_s32_config_##n = { \ |
|
.ctrl_inst = PSI5_NXP_S32_HW_INSTANCE(n), \ |
|
.base = (PSI5_Type *)DT_INST_REG_ADDR(n), \ |
|
.channel_mask = DT_INST_FOREACH_CHILD_STATUS_OKAY_SEP( \ |
|
n, PSI5_NXP_S32_CHANNEL_BIT_MASK, (|)), \ |
|
.pin_cfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \ |
|
.irq_config_func = psi5_irq_config_##n, \ |
|
}; \ |
|
static struct psi5_nxp_s32_data psi5_nxp_s32_data_##n; \ |
|
static int psi5_nxp_s32_init_##n(const struct device *dev) \ |
|
{ \ |
|
const struct psi5_nxp_s32_config *config = dev->config; \ |
|
struct psi5_nxp_s32_data *data = dev->data; \ |
|
int err = 0; \ |
|
\ |
|
err = pinctrl_apply_state(config->pin_cfg, PINCTRL_STATE_DEFAULT); \ |
|
if (err < 0) { \ |
|
LOG_ERR("PSI5 pinctrl setup failed (%d)", err); \ |
|
return err; \ |
|
} \ |
|
\ |
|
for (int ch = 0; ch < PSI5_CHANNEL_COUNT; ch++) { \ |
|
if (config->channel_mask & BIT(ch)) { \ |
|
k_sem_init(&data->channel_data[ch].tx_sem, 1, 1); \ |
|
k_mutex_init(&data->channel_data[ch].lock); \ |
|
} \ |
|
} \ |
|
\ |
|
DT_INST_FOREACH_CHILD_STATUS_OKAY(n, PSI5_NXP_S32_CHANNEL_ASYNC_MODE) \ |
|
\ |
|
/* Common configuration setup for all controller instances */ \ |
|
if (n == UTIL_DEC(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT))) { \ |
|
Psi5_Ip_Init(&psi5_nxp_s32_controller_config); \ |
|
} \ |
|
\ |
|
config->irq_config_func(); \ |
|
\ |
|
return 0; \ |
|
} \ |
|
DEVICE_DT_INST_DEFINE(n, psi5_nxp_s32_init_##n, NULL, &psi5_nxp_s32_data_##n, \ |
|
&psi5_nxp_s32_config_##n, POST_KERNEL, CONFIG_PSI5_INIT_PRIORITY, \ |
|
&psi5_nxp_s32_driver_api); |
|
|
|
DT_INST_FOREACH_STATUS_OKAY(DEV_PSI5_NXP_S32_INIT)
|
|
|