Browse Source

drivers: introduce Single Edge Nibble Transmission (SENT) driver

This driver allows to communication (receive data) with SENT device

Signed-off-by: Cong Nguyen Huu <cong.nguyenhuu@nxp.com>
pull/89637/merge
Cong Nguyen Huu 9 months ago committed by Dan Kalowsky
parent
commit
003e391a8f
  1. 17
      MAINTAINERS.yml
  2. 1
      doc/hardware/peripherals/index.rst
  3. 22
      doc/hardware/peripherals/sent.rst
  4. 1
      drivers/CMakeLists.txt
  5. 1
      drivers/Kconfig
  6. 8
      drivers/sent/CMakeLists.txt
  7. 23
      drivers/sent/Kconfig
  8. 10
      drivers/sent/Kconfig.nxp_s32
  9. 437
      drivers/sent/sent_nxp_s32.c
  10. 39
      dts/bindings/sent/nxp,s32-sent.yaml
  11. 92
      dts/bindings/sent/sent-controller.yaml
  12. 229
      include/zephyr/drivers/sent/sent.h
  13. 47
      include/zephyr/dt-bindings/sent/sent.h
  14. 2
      west.yml

17
MAINTAINERS.yml

@ -2120,6 +2120,23 @@ Release Notes: @@ -2120,6 +2120,23 @@ Release Notes:
tests:
- drivers.sensor
"Drivers: SENT":
status: maintained
maintainers:
- manuargue
- Dat-NguyenDuy
collaborators:
- congnguyenhuu
files:
- drivers/sent/
- include/zephyr/drivers/sent/
- dts/bindings/sent/
- doc/hardware/peripherals/sent.rst
labels:
- "area: SENT"
tests:
- drivers.sent
"Drivers: SMBus":
status: maintained
maintainers:

1
doc/hardware/peripherals/index.rst

@ -54,6 +54,7 @@ Peripherals @@ -54,6 +54,7 @@ Peripherals
retained_mem.rst
sdhc.rst
sensor/index.rst
sent.rst
spi.rst
stepper.rst
smbus.rst

22
doc/hardware/peripherals/sent.rst

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
.. _sent_api:
Single Edge Nibble Transmission (SENT)
######################################
Overview
********
The SENT API provides functionality to communicate with Single Edge Nibble
Transmission (SENT) devices.
Configuration Options
*********************
Related configuration options:
* :kconfig:option:`CONFIG_SENT`
API Reference
*************
.. doxygengroup:: sent_interface

1
drivers/CMakeLists.txt

@ -82,6 +82,7 @@ add_subdirectory_ifdef(CONFIG_RETAINED_MEM retained_mem) @@ -82,6 +82,7 @@ add_subdirectory_ifdef(CONFIG_RETAINED_MEM retained_mem)
add_subdirectory_ifdef(CONFIG_RTC rtc)
add_subdirectory_ifdef(CONFIG_SDHC sdhc)
add_subdirectory_ifdef(CONFIG_SENSOR sensor)
add_subdirectory_ifdef(CONFIG_SENT sent)
add_subdirectory_ifdef(CONFIG_SERIAL serial)
add_subdirectory_ifdef(CONFIG_SMBUS smbus)
add_subdirectory_ifdef(CONFIG_SPI spi)

1
drivers/Kconfig

@ -79,6 +79,7 @@ source "drivers/retained_mem/Kconfig" @@ -79,6 +79,7 @@ source "drivers/retained_mem/Kconfig"
source "drivers/rtc/Kconfig"
source "drivers/sdhc/Kconfig"
source "drivers/sensor/Kconfig"
source "drivers/sent/Kconfig"
source "drivers/serial/Kconfig"
source "drivers/sip_svc/Kconfig"
source "drivers/smbus/Kconfig"

8
drivers/sent/CMakeLists.txt

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
# Copyright 2025 NXP
# SPDX-License-Identifier: Apache-2.0
zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/sent/sent.h)
zephyr_library()
zephyr_library_sources_ifdef(CONFIG_SENT_NXP_S32 sent_nxp_s32.c)

23
drivers/sent/Kconfig

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
# Copyright 2025 NXP
# SPDX-License-Identifier: Apache-2.0
menuconfig SENT
bool "SENT Single Edge Nibble Transmission (SENT)"
help
Enable SENT Driver Configuration
if SENT
module = SENT
module-str = sent
source "subsys/logging/Kconfig.template.log_config"
config SENT_INIT_PRIORITY
int "SENT driver init priority"
default KERNEL_INIT_PRIORITY_DEVICE
help
SENT driver device initialization priority.
source "drivers/sent/Kconfig.nxp_s32"
endif # SENT

10
drivers/sent/Kconfig.nxp_s32

@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
# Copyright 2025 NXP
# SPDX-License-Identifier: Apache-2.0
config SENT_NXP_S32
bool "NXP S32 SENT driver"
default y
depends on DT_HAS_NXP_S32_SENT_ENABLED
select PINCTRL if $(dt_compat_any_has_prop,$(DT_COMPAT_NXP_S32_SENT),pinctrl-0)
help
Enable support for NXP S32 SENT driver.

437
drivers/sent/sent_nxp_s32.c

@ -0,0 +1,437 @@ @@ -0,0 +1,437 @@
/*
* 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)

39
dts/bindings/sent/nxp,s32-sent.yaml

@ -0,0 +1,39 @@ @@ -0,0 +1,39 @@
# Copyright 2025 NXP
#
# SPDX-License-Identifier: Apache-2.0
description: NXP S32 SENT (Single Edge Nibble Transmission) Receiver Controller
compatible: "nxp,s32-sent"
include: [sent-controller.yaml, pinctrl-device.yaml]
properties:
interrupts:
required: true
clocks:
required: true
pinctrl-0:
required: true
pinctrl-names:
required: true
child-binding:
properties:
bus-timeout-cycles:
type: int
enum:
- 0
- 256
- 512
- 1024
- 2048
default: 0
description: |
Specifies the number of bus timeout cycles. This value determines the maximum number
of cycles the bus will wait before timing out. The default value is 0, which disables
the bus timeout.

92
dts/bindings/sent/sent-controller.yaml

@ -0,0 +1,92 @@ @@ -0,0 +1,92 @@
# Copyright 2025 NXP
#
# SPDX-License-Identifier: Apache-2.0
description: SENT (Single Edge Nibble Transmission) Controller
include: base.yaml
properties:
reg:
required: true
child-binding:
description: |
Each child node defines the configuration of a SENT channel
properties:
reg:
type: int
required: true
description: Channel identifier.
num-data-nibbles:
type: int
enum:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
required: true
description: |
Specifies the number of data nibbles. A nibble is a four-bit aggregation.
This property defines how many such nibbles are used in the data transmission.
clock-tick-length-us:
type: int
required: true
description: |
Specifies the clock tick length in microseconds (us). This value determines the
base time unit for the system's timing operations, effectively setting the resolution
of the timer.
successive-calib-pulse-method:
type: int
required: true
enum:
- 1
- 2
description: |
Specifies the successive calibration pulse check method options.
Option 1 is the preferred high-latency method, while option 2 provides
a low-latency alternative.
calib-pulse-tolerance-percent:
type: int
required: true
enum:
- 20
- 25
description: |
Specifies the acceptable calibration pulse tolerance. The available options are
20% and 25%.
pause-pulse-detect:
type: boolean
description: |
Indicates whether the channel is enabled to detect a pause pulse.
fast-crc:
type: int
required: true
description: |
Specifies the fast message CRC configurations is applied when calculating the CRC.
Allowed value combinations are defined in dt-bindings/sent/sent.h:
- FAST_CRC_DISABLE
- FAST_CRC_RECOMMENDED_IMPLEMENTATION
- FAST_CRC_LEGACY_IMPLEMENTATION
- FAST_CRC_RECOMMENDED_IMPLEMENTATION | FAST_CRC_STATUS_INCLUDE
- FAST_CRC_LEGACY_IMPLEMENTATION | FAST_CRC_STATUS_INCLUDE
short-serial-crc:
type: int
required: true
description: |
Specifies the short serial message CRC configurations is applied when calculating the CRC.
Allowed values are defined in dt-bindings/sent/sent.h:
- SHORT_CRC_RECOMMENDED_IMPLEMENTATION
- SHORT_CRC_LEGACY_IMPLEMENTATION

229
include/zephyr/drivers/sent/sent.h

@ -0,0 +1,229 @@ @@ -0,0 +1,229 @@
/*
* Copyright 2025 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Single Edge Nibble Transmission (SENT) driver API.
*/
#ifndef ZEPHYR_INCLUDE_DRIVERS_SENT_H_
#define ZEPHYR_INCLUDE_DRIVERS_SENT_H_
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief SENT Interface
* @defgroup sent_interface SENT Interface
* @since 4.2
* @version 0.1.0
* @ingroup io_interfaces
* @{
*/
/**
* @brief SENT frame type
*/
enum sent_frame_type {
/** Short serial message frame */
SENT_SHORT_SERIAL_FRAME,
/** Enhanced serial message frame with 4-bit message ID */
SENT_ENHANCED_SERIAL_FRAME_4_BIT_ID,
/** Enhanced serial message frame with 8-bit message ID */
SENT_ENHANCED_SERIAL_FRAME_8_BIT_ID,
/** Fast message frame */
SENT_FAST_FRAME
};
/**
* @brief Maximum number of data nibbles
*/
#define SENT_MAX_DATA_NIBBLES 8
/**
* @brief SENT frame structure
*/
struct sent_frame {
/** Type of SENT frame */
enum sent_frame_type type;
union {
/**
* @brief Serial message
*/
struct {
/** Serial message ID */
uint8_t id;
/** Serial message data */
uint16_t data;
} serial;
/**
* @brief Fast message
*/
struct {
/** Array of fast message data nibbles */
uint8_t data_nibbles[SENT_MAX_DATA_NIBBLES];
} fast;
};
/** Timestamp of when the frame was captured */
uint32_t timestamp;
/** CRC checksum for message integrity validation */
uint8_t crc;
};
/**
* @brief Defines the application callback handler function signature for receiving frame.
*
* @param dev Pointer to the device structure for the driver instance.
* @param channel The hardware channel of the driver instance.
* @param num_frame Number of received frame.
* @param user_data User data provided when receiving frame.
*/
typedef void (*sent_rx_frame_callback_t)(const struct device *dev, uint8_t channel,
uint32_t num_frame, void *user_data);
/** @cond INTERNAL_HIDDEN */
/**
* @brief Callback API upon starting receive frame
* See @a sent_start_listening() for argument description
*/
typedef int (*sent_start_listening_t)(const struct device *dev, uint8_t channel);
/**
* @brief Callback API upon stopping receive frame
* See @a sent_stop_listening() for argument description
*/
typedef int (*sent_stop_listening_t)(const struct device *dev, uint8_t channel);
/**
* @brief Configuration structure for RX callback
*/
struct sent_rx_callback_config {
/** Callback function invoked on frame reception */
sent_rx_frame_callback_t callback;
/** Pointer to the buffer for storing received frames */
struct sent_frame *frame;
/** Maximum number of frames to store */
uint32_t max_num_frame;
/** Pointer to user data passed to the callback */
void *user_data;
};
/**
* @brief Composite configuration structure for RX callback registration
*/
struct sent_rx_callback_configs {
/** Configuration for the serial message callback */
struct sent_rx_callback_config *serial;
/** Configuration for the fast message callback */
struct sent_rx_callback_config *fast;
};
/**
* @brief Callback API upon adding RX callback
* See @a sent_register_callback() for argument description
*/
typedef int (*sent_register_callback_t)(const struct device *dev, uint8_t channel,
struct sent_rx_callback_configs callback_configs);
__subsystem struct sent_driver_api {
sent_start_listening_t start_listening;
sent_stop_listening_t stop_listening;
sent_register_callback_t register_callback;
};
/** @endcond */
/**
* @brief Enable a specific channel to start receiving from the bus
*
* @param dev Pointer to the device structure for the driver instance.
* @param channel The hardware channel of the driver instance.
* @retval 0 successful.
* @retval -EINVAL invalid channel is given.
* @retval -EALREADY device is already started.
* @retval -EIO general input/output error, failed to start device.
*/
__syscall int sent_start_listening(const struct device *dev, uint8_t channel);
static inline int z_impl_sent_start_listening(const struct device *dev, uint8_t channel)
{
const struct sent_driver_api *api = (const struct sent_driver_api *)dev->api;
if (api->start_listening) {
return api->start_listening(dev, channel);
}
return -ENOSYS;
}
/**
* @brief Disable a specific channel to stop receiving from the bus
*
* @param dev Pointer to the device structure for the driver instance.
* @param channel The hardware channel of the driver instance.
* @retval 0 successful.
* @retval -EINVAL invalid channel.
* @retval -EALREADY device is already stopped.
* @retval -EIO general input/output error, failed to stop device.
*/
__syscall int sent_stop_listening(const struct device *dev, uint8_t channel);
static inline int z_impl_sent_stop_listening(const struct device *dev, uint8_t channel)
{
const struct sent_driver_api *api = (const struct sent_driver_api *)dev->api;
if (api->stop_listening) {
return api->stop_listening(dev, channel);
}
return -ENOSYS;
}
/**
* @brief Add a callback function to handle messages received for a specific channel
*
* @param dev Pointer to the device structure for the driver instance.
* @param channel The hardware channel of the driver instance.
* @param callback_configs The callback configurations.
* @retval 0 successful.
* @retval -EINVAL invalid channel.
*/
__syscall int sent_register_callback(const struct device *dev, uint8_t channel,
struct sent_rx_callback_configs callback_configs);
static inline int z_impl_sent_register_callback(const struct device *dev, uint8_t channel,
struct sent_rx_callback_configs callback_configs)
{
const struct sent_driver_api *api = (const struct sent_driver_api *)dev->api;
if (api->register_callback) {
return api->register_callback(dev, channel, callback_configs);
}
return -ENOSYS;
}
#ifdef __cplusplus
}
#endif
/**
* @}
*/
#include <zephyr/syscalls/sent.h>
#endif /* ZEPHYR_INCLUDE_DRIVERS_SENT_H_ */

47
include/zephyr/dt-bindings/sent/sent.h

@ -0,0 +1,47 @@ @@ -0,0 +1,47 @@
/*
* Copyright 2025 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_DT_BINDINGS_SENT_H_
#define ZEPHYR_INCLUDE_DT_BINDINGS_SENT_H_
/**
* @brief SENT Interface
* @defgroup sent_interface SENT Interface
* @ingroup io_interfaces
* @{
*/
/**
* @name Fast Message CRC Configuration Flags
* @{
*/
/** Disable CRC check for fast message */
#define FAST_CRC_DISABLE 0
/** Use legacy CRC algorithm for fast message */
#define FAST_CRC_LEGACY_IMPLEMENTATION 1
/** Use the recommended CRC algorithm for fast message */
#define FAST_CRC_RECOMMENDED_IMPLEMENTATION 2
/** Include CRC status in fast message */
#define FAST_CRC_STATUS_INCLUDE 4
/** @} */
/**
* @name Short Serial Message CRC Configuration Flags
* @{
*/
/** Legacy CRC algorithm for short serial message */
#define SHORT_CRC_LEGACY_IMPLEMENTATION 0
/** Recommended CRC algorithm for short serial message */
#define SHORT_CRC_RECOMMENDED_IMPLEMENTATION 1
/** @} */
/** @} */
#endif /* ZEPHYR_INCLUDE_DT_BINDINGS_SENT_H_ */

2
west.yml

@ -210,7 +210,7 @@ manifest: @@ -210,7 +210,7 @@ manifest:
groups:
- hal
- name: hal_nxp
revision: 111f568bda6f119cd896f38ae5843ecde92039bd
revision: fc8aa27bba7ad1f8b98c04f991dae65ba38ec165
path: modules/hal/nxp
groups:
- hal

Loading…
Cancel
Save