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.
 
 
 
 
 
 

437 lines
25 KiB

/*
* Copyright 2025 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT nxp_s32_sent
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(nxp_s32_sent, CONFIG_SENT_LOG_LEVEL);
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/dt-bindings/sent/sent.h>
#include <zephyr/drivers/sent/sent.h>
#include "Srx_Ip.h"
#define CHANNEL_ID_INVAL 0xFF
#if (SRX_IP_TIMESTAMP_FEATURE_ENABLE == STD_ON)
#define TIMESTAMP_FEATURE_ENABLE 1
#endif
struct sent_nxp_s32_config {
uint8_t ctrl_inst;
uint8_t ctrl_id;
uint8_t num_channels;
uint8_t channel_map[SRX_CNL_COUNT];
const struct device *clock_dev;
clock_control_subsys_t clock_subsys;
const struct pinctrl_dev_config *pin_cfg;
void (*irq_config_func)(void);
};
struct sent_nxp_s32_channel_data {
uint8_t channel_id;
bool started;
struct sent_rx_callback_configs callback_configs;
uint32_t serial_frame_cnt;
uint32_t fast_frame_cnt;
struct k_mutex lock;
};
struct sent_nxp_s32_data {
struct sent_nxp_s32_channel_data channel_data[SRX_CNL_COUNT];
};
static int sent_nxp_s32_start_listening(const struct device *dev, uint8_t channel)
{
const struct sent_nxp_s32_config *config = dev->config;
struct sent_nxp_s32_data *data = dev->data;
struct sent_nxp_s32_channel_data *channel_data = &data->channel_data[channel];
Srx_Ip_StatusType err;
if (channel_data->channel_id == CHANNEL_ID_INVAL) {
return -EINVAL;
}
if (channel_data->started) {
return -EALREADY;
}
k_mutex_lock(&channel_data->lock, K_FOREVER);
err = Srx_Ip_StartChannelReceiving(config->ctrl_id, channel_data->channel_id);
if (err) {
LOG_ERR("Failed to start SENT %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 sent_nxp_s32_stop_listening(const struct device *dev, uint8_t channel)
{
const struct sent_nxp_s32_config *config = dev->config;
struct sent_nxp_s32_data *data = dev->data;
struct sent_nxp_s32_channel_data *channel_data = &data->channel_data[channel];
Srx_Ip_StatusType err;
if (channel_data->channel_id == CHANNEL_ID_INVAL) {
return -EINVAL;
}
if (!channel_data->started) {
return -EALREADY;
}
k_mutex_lock(&channel_data->lock, K_FOREVER);
err = Srx_Ip_StopChannelReceiving(config->ctrl_id, channel_data->channel_id);
if (err) {
LOG_ERR("Failed to stop SENT %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 sent_nxp_s32_register_callback(const struct device *dev, uint8_t channel,
struct sent_rx_callback_configs callback_configs)
{
struct sent_nxp_s32_data *data = dev->data;
struct sent_nxp_s32_channel_data *channel_data = &data->channel_data[channel];
if (channel_data->channel_id == CHANNEL_ID_INVAL) {
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(sent, sent_nxp_s32_driver_api) = {
.start_listening = sent_nxp_s32_start_listening,
.stop_listening = sent_nxp_s32_stop_listening,
.register_callback = sent_nxp_s32_register_callback,
};
static void sent_nxp_s32_isr_serial_msg(const struct device *dev)
{
const struct sent_nxp_s32_config *config = dev->config;
Srx_Ip_ProcessMsgCombinedInterrupt(config->ctrl_inst, SRX_IP_SERIAL_MSG_ONLY);
}
static void sent_nxp_s32_isr_fast_msg(const struct device *dev)
{
const struct sent_nxp_s32_config *config = dev->config;
Srx_Ip_ProcessMsgCombinedInterrupt(config->ctrl_inst, SRX_IP_FAST_MSG_ONLY);
}
static void sent_nxp_s32_isr_error(const struct device *dev)
{
const struct sent_nxp_s32_config *config = dev->config;
Srx_Ip_ProcessErrorCombinedInterrupt(config->ctrl_inst);
}
#define SENT_NXP_S32_HW_INSTANCE_CHECK(i, n) \
((DT_INST_REG_ADDR(n) == IP_SRX_##i##_BASE) ? i : 0)
#define SENT_NXP_S32_HW_INSTANCE(n) \
LISTIFY(SRX_INSTANCE_COUNT, SENT_NXP_S32_HW_INSTANCE_CHECK, (|), n)
#define SENT_NXP_S32_CHANNEL_NODE(n, i) DT_INST_CHILD(n, DT_CAT(ch_, i))
#define SENT_NXP_S32_CHANNEL_ID_CNT(i, node_id, n) \
(DT_NODE_HAS_STATUS(SENT_NXP_S32_CHANNEL_NODE(n, i), okay) && \
(DT_REG_ADDR(SENT_NXP_S32_CHANNEL_NODE(n, i)) < DT_REG_ADDR(node_id)) \
? 1 \
: 0)
#define SENT_NXP_S32_CHANNEL_ID(node_id, n) \
LISTIFY(SRX_CNL_COUNT, SENT_NXP_S32_CHANNEL_ID_CNT, (+), node_id, n)
#define SENT_NXP_S32_CHANNEL_CONFIG(node_id, n) \
BUILD_ASSERT(DT_PROP(node_id, fast_crc) == FAST_CRC_DISABLE || \
DT_PROP(node_id, fast_crc) == FAST_CRC_RECOMMENDED_IMPLEMENTATION || \
DT_PROP(node_id, fast_crc) == FAST_CRC_LEGACY_IMPLEMENTATION || \
DT_PROP(node_id, fast_crc) == (FAST_CRC_RECOMMENDED_IMPLEMENTATION | \
FAST_CRC_STATUS_INCLUDE) || \
DT_PROP(node_id, fast_crc) == (FAST_CRC_LEGACY_IMPLEMENTATION | \
FAST_CRC_STATUS_INCLUDE), \
"Fast CRC configuration is invalid"); \
BUILD_ASSERT(DT_PROP(node_id, short_serial_crc) == SHORT_CRC_RECOMMENDED_IMPLEMENTATION || \
DT_PROP(node_id, short_serial_crc) == SHORT_CRC_LEGACY_IMPLEMENTATION, \
"Short serial CRC configuration is invalid"); \
Srx_Ip_ChannelUserConfigType _CONCAT(sent_nxp_s32_channel_config, node_id) = { \
.ControllerId = n, \
.ControllerHwOffset = SENT_NXP_S32_HW_INSTANCE(n), \
.ChannelId = SENT_NXP_S32_CHANNEL_ID(node_id, n), \
.ChannelHwOffset = DT_REG_ADDR(node_id), \
.ChannelDataLength = DT_PROP(node_id, num_data_nibbles), \
.ChannelTickLengthUs = DT_PROP(node_id, clock_tick_length_us), \
.ChannelConfigReg = \
{ \
.BusTimeout = COND_CODE_0(DT_PROP(node_id, bus_timeout_cycles), \
(SRX_IP_BUS_TIMEOUT_DISABLED), \
(UTIL_CAT(SRX_IP_RECEIVER_CLOCK_TICK_COUNTS_, \
DT_PROP(node_id, bus_timeout_cycles)))), \
.FastCrcCheckOff = DT_PROP(node_id, fast_crc) == \
FAST_CRC_DISABLE ? true : false, \
.FastCrcType = DT_PROP(node_id, fast_crc) & \
FAST_CRC_RECOMMENDED_IMPLEMENTATION ? \
SRX_IP_RECOMMENDED_IMPLEMENTATION : \
SRX_IP_LEGACY_IMPLEMENTATION, \
.SlowCrcType = DT_PROP(node_id, short_serial_crc) == \
SHORT_CRC_RECOMMENDED_IMPLEMENTATION ? \
SRX_IP_RECOMMENDED_IMPLEMENTATION : \
SRX_IP_LEGACY_IMPLEMENTATION, \
.SuccessiveCalibCheck = \
DT_PROP(node_id, successive_calib_pulse_method) == 1 ? \
SRX_IP_OPTION_1_PREFERRED : SRX_IP_OPTION_2_LOW_LATENCY, \
.SentValidCalibrationPulse = \
DT_PROP(node_id, calib_pulse_tolerance_percent) == 20 ? \
SRX_IP_RANGE_20 : SRX_IP_RANGE_25, \
.CrcStatusNibbleIncluding = DT_PROP(node_id, fast_crc) & \
FAST_CRC_STATUS_INCLUDE ? true : false, \
}, \
};
#define SENT_NXP_S32_CHANNEL_CONFIG_PTR(node_id) \
&_CONCAT(sent_nxp_s32_channel_config, node_id),
/* Define array channel configuration */
#define SENT_NXP_S32_ARRAY_CHANNEL_CONFIG(n) \
DT_INST_FOREACH_CHILD_STATUS_OKAY_VARGS(n, SENT_NXP_S32_CHANNEL_CONFIG, n) \
static Srx_Ip_ChannelUserConfigType const *const \
sent_nxp_s32_channel_array_config_##n[DT_INST_CHILD_NUM_STATUS_OKAY(n)] = { \
DT_INST_FOREACH_CHILD_STATUS_OKAY(n, \
SENT_NXP_S32_CHANNEL_CONFIG_PTR)};
#define SENT_NXP_S32_CALLBACK(n) \
void sent_nxp_s32_cb_fast_msg_##n(uint8 ctrl_id, uint8 channel_id, \
Srx_Ip_FastMsgType * fast_frame) \
{ \
const struct device *dev = DEVICE_DT_INST_GET(n); \
struct sent_nxp_s32_data *data = dev->data; \
const struct sent_nxp_s32_config *config = dev->config; \
uint8_t channel = config->channel_map[channel_id]; \
uint32_t *frame_cnt = &data->channel_data[channel].fast_frame_cnt; \
struct sent_rx_callback_config *rx_callback = \
data->channel_data[channel].callback_configs.fast; \
\
if (rx_callback) { \
for (int i = 0; i < fast_frame->Length; i++) { \
rx_callback->frame[*frame_cnt].fast.data_nibbles[i] = \
fast_frame->DataNibble[i]; \
} \
rx_callback->frame[*frame_cnt].type = SENT_FAST_FRAME; \
IF_ENABLED(TIMESTAMP_FEATURE_ENABLE, \
(rx_callback->frame[*frame_cnt].timestamp = \
fast_frame->TimestampFast;)) \
rx_callback->frame[*frame_cnt].crc = fast_frame->FastCrc; \
\
(*frame_cnt)++; \
\
if (*frame_cnt == rx_callback->max_num_frame) { \
rx_callback->callback(dev, channel, \
*frame_cnt, rx_callback->user_data); \
*frame_cnt = 0; \
} \
} \
} \
void sent_nxp_s32_cb_serial_msg_##n(uint8 ctrl_id, uint8 channel_id, \
Srx_Ip_SerialMsgType * serial_frame) \
{ \
const struct device *dev = DEVICE_DT_INST_GET(n); \
struct sent_nxp_s32_data *data = dev->data; \
const struct sent_nxp_s32_config *config = dev->config; \
uint8_t channel = config->channel_map[channel_id]; \
uint32_t *frame_cnt = &data->channel_data[channel].serial_frame_cnt; \
struct sent_rx_callback_config *rx_callback = \
data->channel_data[channel].callback_configs.serial; \
\
if (rx_callback) { \
rx_callback->frame[*frame_cnt].type = serial_frame->MsgType == \
SRX_IP_SHORT_SERIAL ? \
SENT_SHORT_SERIAL_FRAME : \
(serial_frame->MsgType == \
SRX_IP_ENHANCED_SERIAL_4_ID ? \
SENT_ENHANCED_SERIAL_FRAME_4_BIT_ID : \
SENT_ENHANCED_SERIAL_FRAME_8_BIT_ID); \
rx_callback->frame[*frame_cnt].serial.id = serial_frame->MessageId; \
rx_callback->frame[*frame_cnt].serial.data = serial_frame->MessageData; \
IF_ENABLED(TIMESTAMP_FEATURE_ENABLE, \
(rx_callback->frame[*frame_cnt].timestamp = \
serial_frame->TimestampSerial;)) \
rx_callback->frame[*frame_cnt].crc = serial_frame->SerialCrc; \
\
(*frame_cnt)++; \
\
if (*frame_cnt == rx_callback->max_num_frame) { \
rx_callback->callback(dev, channel, \
*frame_cnt, rx_callback->user_data); \
*frame_cnt = 0; \
} \
} \
} \
void sent_nxp_s32_error_cb_fast_msg_##n(uint8 ctrl_id, uint8 channel_id, \
Srx_Ip_ChannelStatusType event) \
{ \
ARG_UNUSED(event); \
const struct device *dev = DEVICE_DT_INST_GET(n); \
struct sent_nxp_s32_data *data = dev->data; \
const struct sent_nxp_s32_config *config = dev->config; \
uint8_t channel = config->channel_map[channel_id]; \
uint32_t *frame_cnt = &data->channel_data[channel].fast_frame_cnt; \
struct sent_rx_callback_config *rx_callback = \
data->channel_data[channel].callback_configs.fast; \
\
if (rx_callback) { \
rx_callback->callback(dev, channel, \
*frame_cnt, rx_callback->user_data); \
*frame_cnt = 0; \
} \
} \
void sent_nxp_s32_error_cb_serial_msg_##n(uint8 ctrl_id, uint8 channel_id, \
Srx_Ip_ChannelStatusType event) \
{ \
ARG_UNUSED(event); \
const struct device *dev = DEVICE_DT_INST_GET(n); \
struct sent_nxp_s32_data *data = dev->data; \
const struct sent_nxp_s32_config *config = dev->config; \
uint8_t channel = config->channel_map[channel_id]; \
uint32_t *frame_cnt = &data->channel_data[channel].serial_frame_cnt; \
struct sent_rx_callback_config *rx_callback = \
data->channel_data[channel].callback_configs.serial; \
\
if (rx_callback) { \
rx_callback->callback(dev, channel, \
*frame_cnt, rx_callback->user_data); \
*frame_cnt = 0; \
} \
}
#define _SENT_NXP_S32_IRQ_CONFIG(node_id, prop, idx) \
do { \
IRQ_CONNECT(DT_IRQ_BY_IDX(node_id, idx, irq), \
DT_IRQ_BY_IDX(node_id, idx, priority), \
UTIL_CAT(sent_nxp_s32_isr_, \
DT_STRING_TOKEN_BY_IDX(node_id, prop, idx)), \
DEVICE_DT_GET(node_id), DT_IRQ_BY_IDX(node_id, idx, flags)); \
irq_enable(DT_IRQ_BY_IDX(node_id, idx, irq)); \
} while (false);
#define SENT_NXP_S32_IRQ_CONFIG(n) \
static void sent_irq_config_##n(void) \
{ \
DT_INST_FOREACH_PROP_ELEM(n, interrupt_names, _SENT_NXP_S32_IRQ_CONFIG); \
}
#define DEV_SENT_NXP_S32_INIT(n) \
PINCTRL_DT_INST_DEFINE(n); \
SENT_NXP_S32_IRQ_CONFIG(n) \
SENT_NXP_S32_ARRAY_CHANNEL_CONFIG(n) \
SENT_NXP_S32_CALLBACK(n) \
static struct sent_nxp_s32_config sent_nxp_s32_config_##n = { \
.ctrl_inst = SENT_NXP_S32_HW_INSTANCE(n), \
.ctrl_id = n, \
.num_channels = DT_INST_CHILD_NUM_STATUS_OKAY(n), \
.channel_map = {DT_INST_FOREACH_CHILD_STATUS_OKAY_SEP(n, DT_REG_ADDR, (,))}, \
.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
.clock_subsys = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(n, name), \
.pin_cfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
.irq_config_func = sent_irq_config_##n, \
}; \
static struct sent_nxp_s32_data sent_nxp_s32_data_##n; \
static Srx_Ip_ControllerConfigType sent_nxp_s32_controller_config_##n = { \
.ControllerId = n, \
.ControllerHwOffset = SENT_NXP_S32_HW_INSTANCE(n), \
.ControllerMode = SRX_IP_INTERRUPT, \
.NumberChnlConfigured = DT_INST_CHILD_NUM_STATUS_OKAY(n), \
.ChnlConfig = &sent_nxp_s32_channel_array_config_##n[0], \
.FastErrorNotification = &sent_nxp_s32_error_cb_fast_msg_##n, \
.SerialErrorNotification = &sent_nxp_s32_error_cb_serial_msg_##n, \
.FastFrameNotification = &sent_nxp_s32_cb_fast_msg_##n, \
.SerialFrameNotification = &sent_nxp_s32_cb_serial_msg_##n, \
}; \
static int sent_nxp_s32_init_##n(const struct device *dev) \
{ \
const struct sent_nxp_s32_config *config = dev->config; \
struct sent_nxp_s32_data *data = dev->data; \
int err = 0; \
uint32_t rate; \
\
if (!device_is_ready(config->clock_dev)) { \
LOG_ERR("Clock control device not ready"); \
return -ENODEV; \
} \
\
err = clock_control_on(config->clock_dev, config->clock_subsys); \
if (err) { \
LOG_ERR("Failed to enable clock"); \
return err; \
} \
\
err = clock_control_get_rate(config->clock_dev, config->clock_subsys, &rate); \
if (err) { \
LOG_ERR("Failed to get clock"); \
return err; \
} \
\
memcpy((uint32_t *)&sent_nxp_s32_controller_config_##n.HighFreqRxClock, &rate, \
sizeof(uint32_t)); \
\
err = pinctrl_apply_state(config->pin_cfg, PINCTRL_STATE_DEFAULT); \
if (err < 0) { \
LOG_ERR("SENT pinctrl setup failed (%d)", err); \
return err; \
} \
\
for (int ch = 0; ch < SRX_CNL_COUNT; ch++) { \
data->channel_data[ch].channel_id = CHANNEL_ID_INVAL; \
} \
\
/* Update the channel ID and initialize the mutex for the enabled channel */ \
for (int ch_id = 0; ch_id < config->num_channels ; ch_id++) { \
data->channel_data[config->channel_map[ch_id]].channel_id = ch_id; \
k_mutex_init(&data->channel_data[config->channel_map[ch_id]].lock); \
} \
\
Srx_Ip_InitController(&sent_nxp_s32_controller_config_##n); \
\
config->irq_config_func(); \
\
return 0; \
} \
DEVICE_DT_INST_DEFINE(n, sent_nxp_s32_init_##n, NULL, &sent_nxp_s32_data_##n, \
&sent_nxp_s32_config_##n, POST_KERNEL, \
CONFIG_SENT_INIT_PRIORITY, &sent_nxp_s32_driver_api);
DT_INST_FOREACH_STATUS_OKAY(DEV_SENT_NXP_S32_INIT)