Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
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

/*
* 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)