diff --git a/MAINTAINERS.yml b/MAINTAINERS.yml index b7ce60cd3a1..f2553e61c7d 100644 --- a/MAINTAINERS.yml +++ b/MAINTAINERS.yml @@ -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: diff --git a/doc/hardware/peripherals/index.rst b/doc/hardware/peripherals/index.rst index acb12942c23..9a4d7e9edfe 100644 --- a/doc/hardware/peripherals/index.rst +++ b/doc/hardware/peripherals/index.rst @@ -54,6 +54,7 @@ Peripherals retained_mem.rst sdhc.rst sensor/index.rst + sent.rst spi.rst stepper.rst smbus.rst diff --git a/doc/hardware/peripherals/sent.rst b/doc/hardware/peripherals/sent.rst new file mode 100644 index 00000000000..91bf0f42875 --- /dev/null +++ b/doc/hardware/peripherals/sent.rst @@ -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 diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index 062e35c08a7..6838f746426 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -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) diff --git a/drivers/Kconfig b/drivers/Kconfig index c732913913b..2e71fecfb5f 100644 --- a/drivers/Kconfig +++ b/drivers/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" diff --git a/drivers/sent/CMakeLists.txt b/drivers/sent/CMakeLists.txt new file mode 100644 index 00000000000..ca7fdbfc9b4 --- /dev/null +++ b/drivers/sent/CMakeLists.txt @@ -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) diff --git a/drivers/sent/Kconfig b/drivers/sent/Kconfig new file mode 100644 index 00000000000..51e3d8b7d9c --- /dev/null +++ b/drivers/sent/Kconfig @@ -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 diff --git a/drivers/sent/Kconfig.nxp_s32 b/drivers/sent/Kconfig.nxp_s32 new file mode 100644 index 00000000000..83aadfd2536 --- /dev/null +++ b/drivers/sent/Kconfig.nxp_s32 @@ -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. diff --git a/drivers/sent/sent_nxp_s32.c b/drivers/sent/sent_nxp_s32.c new file mode 100644 index 00000000000..d8567ad0a78 --- /dev/null +++ b/drivers/sent/sent_nxp_s32.c @@ -0,0 +1,437 @@ +/* + * Copyright 2025 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_s32_sent + +#include + +LOG_MODULE_REGISTER(nxp_s32_sent, CONFIG_SENT_LOG_LEVEL); + +#include +#include +#include +#include +#include + +#include + +#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) diff --git a/dts/bindings/sent/nxp,s32-sent.yaml b/dts/bindings/sent/nxp,s32-sent.yaml new file mode 100644 index 00000000000..4f1d906e088 --- /dev/null +++ b/dts/bindings/sent/nxp,s32-sent.yaml @@ -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. diff --git a/dts/bindings/sent/sent-controller.yaml b/dts/bindings/sent/sent-controller.yaml new file mode 100644 index 00000000000..a18233b34ad --- /dev/null +++ b/dts/bindings/sent/sent-controller.yaml @@ -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 diff --git a/include/zephyr/drivers/sent/sent.h b/include/zephyr/drivers/sent/sent.h new file mode 100644 index 00000000000..c3d29c69da4 --- /dev/null +++ b/include/zephyr/drivers/sent/sent.h @@ -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 +#include + +#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 + +#endif /* ZEPHYR_INCLUDE_DRIVERS_SENT_H_ */ diff --git a/include/zephyr/dt-bindings/sent/sent.h b/include/zephyr/dt-bindings/sent/sent.h new file mode 100644 index 00000000000..351101d4ec9 --- /dev/null +++ b/include/zephyr/dt-bindings/sent/sent.h @@ -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_ */ diff --git a/west.yml b/west.yml index 762ad8ae2e2..c2039211b86 100644 --- a/west.yml +++ b/west.yml @@ -210,7 +210,7 @@ manifest: groups: - hal - name: hal_nxp - revision: 111f568bda6f119cd896f38ae5843ecde92039bd + revision: fc8aa27bba7ad1f8b98c04f991dae65ba38ec165 path: modules/hal/nxp groups: - hal