From 6c8391d6f756f8a7ec44729cb4a4975a300a2556 Mon Sep 17 00:00:00 2001 From: Cong Nguyen Huu Date: Fri, 13 Sep 2024 17:06:11 +0700 Subject: [PATCH 1/6] drivers: introduce support Peripheral Sensor Interface (PSI5) driver This driver allows to communication (send, receive) with PSI5 device Signed-off-by: Cong Nguyen Huu --- drivers/CMakeLists.txt | 1 + drivers/Kconfig | 1 + drivers/psi5/CMakeLists.txt | 8 + drivers/psi5/Kconfig | 23 ++ drivers/psi5/Kconfig.nxp_s32 | 9 + drivers/psi5/psi5_nxp_s32.c | 485 ++++++++++++++++++++++++++++ dts/bindings/psi5/nxp,s32-psi5.yaml | 123 +++++++ include/zephyr/drivers/psi5/psi5.h | 236 ++++++++++++++ 8 files changed, 886 insertions(+) create mode 100644 drivers/psi5/CMakeLists.txt create mode 100644 drivers/psi5/Kconfig create mode 100644 drivers/psi5/Kconfig.nxp_s32 create mode 100644 drivers/psi5/psi5_nxp_s32.c create mode 100644 dts/bindings/psi5/nxp,s32-psi5.yaml create mode 100644 include/zephyr/drivers/psi5/psi5.h diff --git a/drivers/CMakeLists.txt b/drivers/CMakeLists.txt index a4975dbe249c9..9aae902099317 100644 --- a/drivers/CMakeLists.txt +++ b/drivers/CMakeLists.txt @@ -74,6 +74,7 @@ add_subdirectory_ifdef(CONFIG_PINCTRL pinctrl) add_subdirectory_ifdef(CONFIG_PM_CPU_OPS pm_cpu_ops) add_subdirectory_ifdef(CONFIG_POWER_DOMAIN power_domain) add_subdirectory_ifdef(CONFIG_PS2 ps2) +add_subdirectory_ifdef(CONFIG_PSI5 psi5) add_subdirectory_ifdef(CONFIG_PTP_CLOCK ptp_clock) add_subdirectory_ifdef(CONFIG_PWM pwm) add_subdirectory_ifdef(CONFIG_REGULATOR regulator) diff --git a/drivers/Kconfig b/drivers/Kconfig index 4819d5059dfbe..fa67a2cac2002 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -71,6 +71,7 @@ source "drivers/pinctrl/Kconfig" source "drivers/pm_cpu_ops/Kconfig" source "drivers/power_domain/Kconfig" source "drivers/ps2/Kconfig" +source "drivers/psi5/Kconfig" source "drivers/ptp_clock/Kconfig" source "drivers/pwm/Kconfig" source "drivers/regulator/Kconfig" diff --git a/drivers/psi5/CMakeLists.txt b/drivers/psi5/CMakeLists.txt new file mode 100644 index 0000000000000..04b3c639fc4f4 --- /dev/null +++ b/drivers/psi5/CMakeLists.txt @@ -0,0 +1,8 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/psi5/psi5.h) + +zephyr_library() + +zephyr_library_sources_ifdef(CONFIG_PSI5_NXP_S32 psi5_nxp_s32.c) diff --git a/drivers/psi5/Kconfig b/drivers/psi5/Kconfig new file mode 100644 index 0000000000000..d11dff9bdcd55 --- /dev/null +++ b/drivers/psi5/Kconfig @@ -0,0 +1,23 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +menuconfig PSI5 + bool "Peripheral Sensor Interface (PSI5) driver" + help + Enable PSI5 Driver Configuration + +if PSI5 + +module = PSI5 +module-str = psi5 +source "subsys/logging/Kconfig.template.log_config" + +config PSI5_INIT_PRIORITY + int "PSI5 driver init priority" + default KERNEL_INIT_PRIORITY_DEVICE + help + PSI5 driver device initialization priority. + +source "drivers/psi5/Kconfig.nxp_s32" + +endif # PSI5 diff --git a/drivers/psi5/Kconfig.nxp_s32 b/drivers/psi5/Kconfig.nxp_s32 new file mode 100644 index 0000000000000..8a4248899add2 --- /dev/null +++ b/drivers/psi5/Kconfig.nxp_s32 @@ -0,0 +1,9 @@ +# Copyright 2024 NXP +# SPDX-License-Identifier: Apache-2.0 + +config PSI5_NXP_S32 + bool "NXP S32 PSI5 driver" + default y + depends on DT_HAS_NXP_S32_PSI5_ENABLED + help + Enable support for NXP S32 PSI5 driver. diff --git a/drivers/psi5/psi5_nxp_s32.c b/drivers/psi5/psi5_nxp_s32.c new file mode 100644 index 0000000000000..6c95a9eec0897 --- /dev/null +++ b/drivers/psi5/psi5_nxp_s32.c @@ -0,0 +1,485 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_s32_psi5 + +#include + +LOG_MODULE_REGISTER(nxp_s32_psi5, CONFIG_PSI5_LOG_LEVEL); + +#include +#include +#include + +#include + +#include "Psi5_Ip.h" + +struct psi5_nxp_s32_config { + uint8_t ctrl_inst; + 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_rx_callback { + psi5_rx_callback_t callback; + struct psi5_frame frame; + void *user_data; +}; + +struct psi5_nxp_s32_channel_data { + boolean started; + struct psi5_nxp_s32_tx_callback tx_callback; + struct psi5_nxp_s32_rx_callback rx_callback; + 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]; +}; + +static int psi5_nxp_s32_start(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; + } + + k_mutex_lock(&channel_data->lock, K_FOREVER); + + err = Psi5_Ip_SetChannelSync(config->ctrl_inst, channel, true); + + if (err) { + LOG_ERR("Failed to start PSI5 %d channel %d", config->ctrl_inst, channel); + k_mutex_unlock(&channel_data->lock); + return -EIO; + } + + k_mutex_unlock(&channel_data->lock); + + channel_data->started = true; + + return 0; +} + +static int psi5_nxp_s32_stop(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; + } + + k_mutex_lock(&channel_data->lock, K_FOREVER); + + err = Psi5_Ip_SetChannelSync(config->ctrl_inst, channel, false); + + if (err) { + LOG_ERR("Failed to stop 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_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; + uint64_t start_time; + + if (!(config->channel_mask & BIT(channel))) { + return -EINVAL; + } + + if (!channel_data->started) { + return -ENETDOWN; + } + + if (k_sem_take(&channel_data->tx_sem, timeout) != 0) { + return -EAGAIN; + } + + if (callback != NULL) { + channel_data->tx_callback.callback = callback; + channel_data->tx_callback.user_data = user_data; + } + + k_mutex_lock(&channel_data->lock, K_FOREVER); + + err = Psi5_Ip_Transmit(config->ctrl_inst, channel, psi5_data); + + if (err) { + LOG_ERR("Failed to transmit PSI5 %d channel %d (err %d)", config->ctrl_inst, + channel, err); + k_sem_give(&channel_data->tx_sem); + goto unlock; + return -EIO; + } + + if (callback != NULL) { + goto unlock; + return 0; + } + + start_time = k_uptime_ticks(); + + while (!Psi5_Ip_GetTransmissionStatus(config->ctrl_inst, channel)) { + if (k_uptime_ticks() - start_time >= timeout.ticks) { + LOG_ERR("Timeout for waiting transmision PSI5 %d channel %d", + config->ctrl_inst, channel); + k_sem_give(&channel_data->tx_sem); + goto unlock; + return -EAGAIN; + } + } + + k_sem_give(&channel_data->tx_sem); + +unlock: + k_mutex_unlock(&channel_data->lock); + + return 0; +} + +static int psi5_nxp_s32_add_rx_callback(const struct device *dev, uint8_t channel, + psi5_rx_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->rx_callback.callback == callback) && + (channel_data->rx_callback.user_data == user_data)) { + return 0; + } + + if (channel_data->rx_callback.callback) { + return -EBUSY; + } + + k_mutex_lock(&channel_data->lock, K_FOREVER); + + channel_data->rx_callback.callback = callback; + channel_data->rx_callback.user_data = user_data; + + k_mutex_unlock(&channel_data->lock); + + return 0; +} + +static const struct psi5_driver_api psi5_nxp_s32_driver_api = { + .start = psi5_nxp_s32_start, + .stop = psi5_nxp_s32_stop, + .send = psi5_nxp_s32_send, + .add_rx_callback = psi5_nxp_s32_add_rx_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; \ + Psi5_Ip_Psi5FrameType ip_frame; \ + Psi5_Ip_SmcFrameType ip_smc_frame; \ + uint8_t channel = DT_REG_ADDR(node_id); \ + struct psi5_nxp_s32_data *data = dev->data; \ + 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_nxp_s32_rx_callback *rx_callback = &channel_data->rx_callback; \ + \ + if (event.Psi5_DriverReadyToTransmit) { \ + if (tx_callback->callback) { \ + tx_callback->callback(dev, channel, PSI5_STATE_TX_READY, \ + tx_callback->user_data); \ + } \ + k_sem_give(&channel_data->tx_sem); \ + } \ + if (event.Psi5_TxDataOverwrite) { \ + if (tx_callback->callback) { \ + tx_callback->callback(dev, channel, PSI5_STATE_TX_OVERWRITE, \ + tx_callback->user_data); \ + } \ + k_sem_give(&channel_data->tx_sem); \ + } \ + if (event.Psi5_Psi5MessageReceived) { \ + Psi5_Ip_GetPsi5Frame(config->ctrl_inst, channel, &ip_frame); \ + \ + rx_callback->frame.msg.data = ip_frame.DATA_REGION; \ + rx_callback->frame.msg.timestamp = ip_frame.TIME_STAMP; \ + rx_callback->frame.msg.crc = ip_frame.CRC; \ + \ + if (rx_callback->callback) { \ + rx_callback->callback(dev, channel, &rx_callback->frame, \ + PSI5_STATE_MSG_RECEIVED, \ + rx_callback->user_data); \ + } \ + } \ + if (event.Psi5_Psi5MessageOverwrite) { \ + if (rx_callback->callback) { \ + rx_callback->callback(dev, channel, NULL, \ + PSI5_STATE_MSG_OVERWRITE, \ + rx_callback->user_data); \ + } \ + } \ + if (event.Psi5_Psi5MessageErrorsPresent) { \ + if (rx_callback->callback) { \ + rx_callback->callback(dev, channel, NULL, PSI5_STATE_MSG_ERR, \ + rx_callback->user_data); \ + } \ + } \ + if (event.Psi5_SmcMessageReceived) { \ + Psi5_Ip_GetSmcFrame(config->ctrl_inst, channel, &ip_smc_frame); \ + \ + if (ip_smc_frame.C) { \ + rx_callback->frame.smc_msg.id = ip_smc_frame.ID; \ + rx_callback->frame.smc_msg.data = \ + FIELD_PREP(GENMASK(15, 12), (ip_smc_frame.IDDATA)) | \ + FIELD_PREP(GENMASK(11, 0), (ip_smc_frame.DATA)); \ + } else { \ + rx_callback->frame.smc_msg.id = \ + FIELD_PREP(GENMASK(7, 4), (ip_smc_frame.ID)) | \ + FIELD_PREP(GENMASK(3, 0), (ip_smc_frame.IDDATA)); \ + rx_callback->frame.smc_msg.data = ip_smc_frame.DATA; \ + } \ + rx_callback->frame.smc_msg.crc = ip_smc_frame.CRC; \ + \ + if (rx_callback->callback) { \ + rx_callback->callback(dev, channel, &rx_callback->frame, \ + PSI5_STATE_SMC_MSG_RECEIVED, \ + rx_callback->user_data); \ + } \ + } \ + if (event.Psi5_SmcMessageOverwrite) { \ + if (rx_callback->callback) { \ + rx_callback->callback(dev, channel, NULL, \ + PSI5_STATE_SMC_MSG_OVERWRITE, \ + rx_callback->user_data); \ + } \ + } \ + if (event.Psi5_SmcMessageCRCError) { \ + if (rx_callback->callback) { \ + rx_callback->callback(dev, channel, NULL, PSI5_STATE_SMC_MSG_ERR, \ + rx_callback->user_data); \ + } \ + } \ + } + +#define _PSI5_NXP_S32_CHANNEL_RX_SLOT_CONFIG(i, node_id) \ + { \ + .slotId = UTIL_INC(i), \ + .slotLen = DT_PROP_BY_IDX(node_id, array_slot_duration_us, i), \ + .startOffs = DT_PROP_BY_IDX(node_id, array_slot_start_offset_us, i), \ + .dataSize = DT_PROP_BY_IDX(node_id, array_slot_data_length, i), \ + }, + +#define PSI5_NXP_S32_CHANNEL_RX_SLOT_CONFIG(node_id) \ + BUILD_ASSERT(DT_PROP_LEN(node_id, array_slot_duration_us) == \ + DT_PROP_LEN(node_id, array_slot_start_offset_us) && \ + DT_PROP_LEN(node_id, array_slot_duration_us) == \ + DT_PROP_LEN(node_id, array_slot_data_length), \ + "Invalid channel RX slot configuration"); \ + static const Psi5_Ip_SlotConfigType _CONCAT( \ + psi5_nxp_s32_channel_rx_slot_config, \ + node_id)[DT_PROP_LEN(node_id, array_slot_duration_us)] = { \ + LISTIFY(DT_PROP_LEN(node_id, array_slot_duration_us), \ + _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, rx_buf_size), \ + .bitRate = DT_ENUM_IDX(node_id, bitrate_kbps), \ + .slotConfig = &_CONCAT(psi5_nxp_s32_channel_rx_slot_config, node_id)[0], \ + .numOfSlotConfigs = DT_PROP_LEN(node_id, array_slot_duration_us), \ + }; + +#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(node_id, target_pulse_us), \ + .decoderOffset = DT_PROP(node_id, decoder_offset_us), \ + .pulse0Width = DT_PROP(node_id, low_pulse_width_us), \ + .pulse1Width = DT_PROP(node_id, high_pulse_width_us), \ + .txMode = DT_ENUM_IDX(node_id, tx_mode), \ + .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 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), \ + .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 i = 0; i < PSI5_CHANNEL_COUNT; i++) { \ + k_sem_init(&data->channel_data[i].tx_sem, 1, 1); \ + k_mutex_init(&data->channel_data[i].lock); \ + } \ + \ + /* 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) diff --git a/dts/bindings/psi5/nxp,s32-psi5.yaml b/dts/bindings/psi5/nxp,s32-psi5.yaml new file mode 100644 index 0000000000000..0f37ed7dd6752 --- /dev/null +++ b/dts/bindings/psi5/nxp,s32-psi5.yaml @@ -0,0 +1,123 @@ +# Copyright 2024 NXP +# +# SPDX-License-Identifier: Apache-2.0 + +description: Properties for Peripheral Sensor Interface (PSI5) + +compatible: "nxp,s32-psi5" + +include: [base.yaml, pinctrl-device.yaml] + +child-binding: + description: Properties for Peripheral Sensor Interface (PSI5) channel + + compatible: "nxp,s32-psi5-channel" + + include: base.yaml + + properties: + async-mode: + type: boolean + description: | + Determines the channel operation mode. When set to true, the channel operates in + asynchronous mode with only the receive function active. When set to false, + the channel operates in synchronous mode with both transmit and receive functions active. + + # Channel TX configuration + target-pulse-us: + type: int + required: true + description: | + Specifies the duration of the internally generated synchronization pulses, measured in + microseconds (us). This value determines the length of each synchronization pulse used + in the system. + + decoder-offset-us: + type: int + required: true + description: | + Specifies the duration for which the Manchester decoder remains inactive after the + falling edge of a synchronization pulse, measured in microseconds (us). This value + determines the delay before the decoder starts processing incoming signals again. + + low-pulse-width-us: + required: true + type: int + description: | + Specifies the duration of the pulse width for a data value of '0', measured in + microseconds (us). This value determines how long the pulse lasts when representing + a binary '0'. + + high-pulse-width-us: + required: true + type: int + description: | + Specifies the duration of the pulse width for a data value of '1', measured in + microseconds (us). This value determines how long the pulse lasts when representing + a binary '1'. + + tx-mode: + type: string + required: true + enum: + - short-frame-31 + - short-frame-5 + - long-frame-31 + - long-frame-5 + - x-long-frame-31 + - x-long-frame-5 + - xx-long-frame + - non-standard-frame + description: | + Specifies the transmitter mode. Each mode defines the frame length and + the start condition for data transmission: + - short-frame-31: Short Frame (V1.3) with 31 "1s" as the start condition + - short-frame-5: Short Frame (V1.3) with 5 "0s" as the start condition + - long-frame-31: Long Frame (V1.3) with 31 "1s" as the start condition + - long-frame-5: Long Frame (V1.3) with 5 "0s" as the start condition + - x-long-fram-31: X-Long Frame (V1.3) with 31 "1s" as the start condition + - x-long-frame-5: X-Long Frame (V1.3) with 5 "0s" as the start condition + - xx-long-frame: XX-Long (V2.0) + - non-standard-frame: Non Standard Length + + # Channel RX configuration + rx-buf-size: + type: int + required: true + description: | + Specifies the maximum number of receive buffers used for storing PSI5 messages. + The value can range from 1 to 32, determining how many messages can be stored in + the buffer at any given time. + + bitrate-kbps: + type: int + required: true + enum: + - 125 + - 189 + description: | + Selects the receive message bitrate in kbps. This setting determines the speed at + which data is received. + + array-slot-duration-us: + type: array + required: true + description: | + Contains the slot durations in microseconds (us). Each value in the array represents + the duration of a slot, starting from the rising edge of the timing synchronization pulse + and ending at the final slot. + + array-slot-start-offset-us: + type: array + required: true + description: | + Contains the start offsets for each slot in microseconds (us). Each value in the array + represents the time offset at which the slot should start, measured from the rising edge + of the timing synchronization pulse. + + array-slot-data-length: + type: array + required: true + description: | + Contains the data length in bits for each slot. Each value in the array specifies the number + of bits in a slot, with valid lengths ranging from 8 to 28 bits. diff --git a/include/zephyr/drivers/psi5/psi5.h b/include/zephyr/drivers/psi5/psi5.h new file mode 100644 index 0000000000000..10894423fa95f --- /dev/null +++ b/include/zephyr/drivers/psi5/psi5.h @@ -0,0 +1,236 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief Peripheral Sensor Interface (PSI5) driver API. + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_PSI5_H_ +#define ZEPHYR_INCLUDE_DRIVERS_PSI5_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief PSI5 frame structure + */ +struct psi5_frame { + union { + /* PSI5 recevice data frame */ + struct { + uint32_t data; + uint32_t timestamp; + uint8_t crc; + } msg; + + /* PSI5 serial message channel (SMC) data frame */ + struct { + uint16_t data; + uint8_t id; + uint8_t crc; + } smc_msg; + }; +}; + +/** + * @brief PSI5 state + */ +enum psi5_state { + PSI5_STATE_TX_READY, /* Driver is ready to transmit new data */ + PSI5_STATE_TX_OVERWRITE, /* Data register overwrite */ + PSI5_STATE_MSG_RECEIVED, /* PSI5 Message Received Event */ + PSI5_STATE_MSG_OVERWRITE, /* PSI5 Message Overwrite Event */ + PSI5_STATE_MSG_ERR, /* PSI5 Message Errors Present Event */ + PSI5_STATE_SMC_MSG_RECEIVED, /* PSI5 SMC Message Received Event */ + PSI5_STATE_SMC_MSG_OVERWRITE, /* PSI5 SMC Message Overwrite Event */ + PSI5_STATE_SMC_MSG_ERR, /* PSI5 SMC Message Errors Present Event */ +}; + +/** @cond INTERNAL_HIDDEN */ + +/** + * @brief Defines the application callback handler function signature + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The hardware channel of the driver instance. + * @param state State of PSI5. + * @param user_data User data provided when the frame was sent. + */ +typedef void (*psi5_tx_callback_t)(const struct device *dev, uint8_t channel, enum psi5_state state, + void *user_data); + +/** + * @brief Defines the application callback handler function signature for receiving. + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The hardware channel of the driver instance. + * @param frame Received frame. + * @param state State of PSI5. + * @param user_data User data provided when the filter was added. + */ +typedef void (*psi5_rx_callback_t)(const struct device *dev, uint8_t channel, + struct psi5_frame *frame, enum psi5_state state, + void *user_data); + +/** + * @brief Callback API upon starting PSI5 + * See @a psi5_start() for argument description + */ +typedef int (*psi5_start_t)(const struct device *dev, uint8_t channel); + +/** + * @brief Callback API upon stopping PSI5 + * See @a psi5_stop() for argument description + */ +typedef int (*psi5_stop_t)(const struct device *dev, uint8_t channel); + +/** + * @brief Callback API upon sending PSI5 frame + * See @a psi5_send() for argument description + */ +typedef int (*psi5_send_t)(const struct device *dev, uint8_t channel, const uint64_t data, + k_timeout_t timeout, psi5_tx_callback_t callback, void *user_data); + +/** + * @brief Callback API upon adding RX callback + * See @a psi5_add_rx_callback() for argument description + */ +typedef int (*psi5_add_rx_callback_t)(const struct device *dev, uint8_t channel, + psi5_rx_callback_t callback, void *user_data); + +__subsystem struct psi5_driver_api { + psi5_start_t start; + psi5_stop_t stop; + psi5_send_t send; + psi5_add_rx_callback_t add_rx_callback; +}; + +/** @endcond */ + +/** + * @brief Start PSI5 + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The hardware channel of the driver instance. + * @retval 0 if successful. + * @retval -EINVAL if an invalid channel is given. + * @retval -EALREADY if the device is already started. + * @retval -EIO General input/output error, failed to start device. + */ +__syscall int psi5_start(const struct device *dev, uint8_t channel); + +static inline int z_impl_psi5_start(const struct device *dev, uint8_t channel) +{ + const struct psi5_driver_api *api = (const struct psi5_driver_api *)dev->api; + + if (api->start) { + return api->start(dev, channel); + } + + return -ENOSYS; +} + +/** + * @brief Stop PSI5 + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The hardware channel of the driver instance. + * @retval 0 if successful. + * @retval -EINVAL if an invalid channel is given. + * @retval -EALREADY if the device is already stopped. + * @retval -EIO General input/output error, failed to stop device. + */ +__syscall int psi5_stop(const struct device *dev, uint8_t channel); + +static inline int z_impl_psi5_stop(const struct device *dev, uint8_t channel) +{ + const struct psi5_driver_api *api = (const struct psi5_driver_api *)dev->api; + + if (api->stop) { + return api->stop(dev, channel); + } + + return -ENOSYS; +} + +/** + * @brief Transmitting PSI5 frames + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The hardware channel of the driver instance. + * @param data PSI5 data to transmit. + * @param timeout Timeout waiting for ready to transmit new data. + * @param callback Optional callback for when the frame was sent or a + * transmission error occurred. If ``NULL``, this function is + * blocking until frame is sent. + * @param user_data User data to pass to callback function. + * + * @retval 0 if successful. + * @retval -EINVAL if an invalid channel is given. + * @retval -ENOTSUP if an unsupported parameter was passed to the function. + * @retval -ENETDOWN if PSI5 is in stopped state. + * @retval -EIO if a general transmit error occurred. + * @retval -EAGAIN on timeout. + */ +__syscall int psi5_send(const struct device *dev, uint8_t channel, const uint64_t data, + k_timeout_t timeout, psi5_tx_callback_t callback, void *user_data); + +static inline int z_impl_psi5_send(const struct device *dev, uint8_t channel, const uint64_t data, + k_timeout_t timeout, psi5_tx_callback_t callback, + void *user_data) +{ + const struct psi5_driver_api *api = (const struct psi5_driver_api *)dev->api; + + if (api->send) { + return api->send(dev, channel, data, timeout, callback, user_data); + } + + return -ENOSYS; +} + +/** + * @name Receiving PSI5 frames + * + * @{ + */ + +/** + * @brief Add a callback function for receiving PSI5 frames + * + * @param dev Pointer to the device structure for the driver instance. + * @param channel The hardware channel of the driver instance. + * @param callback This function is called by PSI5 driver whenever a frame is received. + * @param user_data User data to pass to callback function. + * @retval 0 if successful. + * @retval -EINVAL if an invalid channel is given. + */ +__syscall int psi5_add_rx_callback(const struct device *dev, uint8_t channel, + psi5_rx_callback_t callback, void *user_data); + +static inline int z_impl_psi5_add_rx_callback(const struct device *dev, uint8_t channel, + psi5_rx_callback_t callback, void *user_data) +{ + const struct psi5_driver_api *api = (const struct psi5_driver_api *)dev->api; + + if (api->add_rx_callback) { + return api->add_rx_callback(dev, channel, callback, user_data); + } + + return -ENOSYS; +} + +#ifdef __cplusplus +} +#endif + +#include + +#endif /* ZEPHYR_INCLUDE_DRIVERS_PSI5_H_ */ From 44f76dfb6e4373d0ce576d570fee2d0ba88b8ee0 Mon Sep 17 00:00:00 2001 From: Cong Nguyen Huu Date: Mon, 14 Oct 2024 13:52:33 +0700 Subject: [PATCH 2/6] boards: s32z270: enable support psi5 enable support psi5 Signed-off-by: Cong Nguyen Huu --- dts/arm/nxp/nxp_s32z27x_r52.dtsi | 73 +++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/dts/arm/nxp/nxp_s32z27x_r52.dtsi b/dts/arm/nxp/nxp_s32z27x_r52.dtsi index 974ee633e1513..2ef705c245e1f 100644 --- a/dts/arm/nxp/nxp_s32z27x_r52.dtsi +++ b/dts/arm/nxp/nxp_s32z27x_r52.dtsi @@ -1347,7 +1347,6 @@ pwm { compatible = "nxp,s32-emios-pwm"; #pwm-cells = <3>; - status = "disabled"; }; }; @@ -1452,5 +1451,77 @@ #size-cells = <0>; status = "disabled"; }; + + psi5_0: psi5@401e0000 { + compatible = "nxp,s32-psi5"; + reg = <0x401e0000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + + psi5_0_ch0: ch@0 { + compatible = "nxp,s32-psi5-channel"; + reg = <0>; + interrupts = ; + status = "disabled"; + }; + + psi5_0_ch1: ch@1 { + compatible = "nxp,s32-psi5-channel"; + reg = <1>; + interrupts = ; + status = "disabled"; + }; + + psi5_0_ch2: ch@2 { + compatible = "nxp,s32-psi5-channel"; + reg = <2>; + interrupts = ; + status = "disabled"; + }; + + psi5_0_ch3: ch@3 { + compatible = "nxp,s32-psi5-channel"; + reg = <3>; + interrupts = ; + status = "disabled"; + }; + }; + + psi5_1: psi5@421e0000 { + compatible = "nxp,s32-psi5"; + reg = <0x421e0000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + + psi5_1_ch0: ch@0 { + compatible = "nxp,s32-psi5-channel"; + reg = <0>; + interrupts = ; + status = "disabled"; + }; + + psi5_1_ch1: ch@1 { + compatible = "nxp,s32-psi5-channel"; + reg = <1>; + interrupts = ; + status = "disabled"; + }; + + psi5_1_ch2: ch@2 { + compatible = "nxp,s32-psi5-channel"; + reg = <2>; + interrupts = ; + status = "disabled"; + }; + + psi5_1_ch3: ch@3 { + compatible = "nxp,s32-psi5-channel"; + reg = <3>; + interrupts = ; + status = "disabled"; + }; + }; }; }; From 4a59b5dd60a1588578d20567c6b1c9f0a3e7e089 Mon Sep 17 00:00:00 2001 From: Cong Nguyen Huu Date: Wed, 25 Sep 2024 14:00:55 +0700 Subject: [PATCH 3/6] samples: boards: nxp: add sample for using psi5 driver Add sample for using psi5 driver on s32z Signed-off-by: Cong Nguyen Huu --- samples/boards/nxp/s32/psi5/CMakeLists.txt | 8 +++ .../boards/s32z2xxdc2_s32z270_rtu0.overlay | 52 +++++++++++++++++++ .../boards/s32z2xxdc2_s32z270_rtu1.overlay | 52 +++++++++++++++++++ samples/boards/nxp/s32/psi5/prj.conf | 3 ++ samples/boards/nxp/s32/psi5/sample.yaml | 14 +++++ samples/boards/nxp/s32/psi5/src/main.c | 43 +++++++++++++++ 6 files changed, 172 insertions(+) create mode 100644 samples/boards/nxp/s32/psi5/CMakeLists.txt create mode 100644 samples/boards/nxp/s32/psi5/boards/s32z2xxdc2_s32z270_rtu0.overlay create mode 100644 samples/boards/nxp/s32/psi5/boards/s32z2xxdc2_s32z270_rtu1.overlay create mode 100644 samples/boards/nxp/s32/psi5/prj.conf create mode 100644 samples/boards/nxp/s32/psi5/sample.yaml create mode 100644 samples/boards/nxp/s32/psi5/src/main.c diff --git a/samples/boards/nxp/s32/psi5/CMakeLists.txt b/samples/boards/nxp/s32/psi5/CMakeLists.txt new file mode 100644 index 0000000000000..cc19ea188af9b --- /dev/null +++ b/samples/boards/nxp/s32/psi5/CMakeLists.txt @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(psi5) + +target_sources(app PRIVATE src/main.c) diff --git a/samples/boards/nxp/s32/psi5/boards/s32z2xxdc2_s32z270_rtu0.overlay b/samples/boards/nxp/s32/psi5/boards/s32z2xxdc2_s32z270_rtu0.overlay new file mode 100644 index 0000000000000..ce6ed4e0edf78 --- /dev/null +++ b/samples/boards/nxp/s32/psi5/boards/s32z2xxdc2_s32z270_rtu0.overlay @@ -0,0 +1,52 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&pinctrl { + psi5_0_default: psi5_0_default { + group1 { + pinmux = , ; + output-enable; + }; + group2 { + pinmux = , ; + input-enable; + }; + }; +}; + +&psi5_0 { + pinctrl-0 = <&psi5_0_default>; + pinctrl-names = "default"; + status = "okay"; +}; + +&psi5_0_ch1 { + target-pulse-us = <8>; + decoder-offset-us = <0>; + low-pulse-width-us = <4>; + high-pulse-width-us = <4>; + tx-mode = "short-frame-31"; + rx-buf-size = <32>; + bitrate-kbps = <125>; + array-slot-duration-us = <1000 1000>; + array-slot-start-offset-us = <0 0>; + array-slot-data-length = <16 16>; + status = "okay"; +}; + +&psi5_0_ch2 { + target-pulse-us = <8>; + decoder-offset-us = <0>; + low-pulse-width-us = <4>; + high-pulse-width-us = <4>; + tx-mode = "short-frame-31"; + rx-buf-size = <32>; + bitrate-kbps = <125>; + array-slot-duration-us = <1000 1000>; + array-slot-start-offset-us = <0 0>; + array-slot-data-length = <16 16>; + status = "okay"; +}; diff --git a/samples/boards/nxp/s32/psi5/boards/s32z2xxdc2_s32z270_rtu1.overlay b/samples/boards/nxp/s32/psi5/boards/s32z2xxdc2_s32z270_rtu1.overlay new file mode 100644 index 0000000000000..ce6ed4e0edf78 --- /dev/null +++ b/samples/boards/nxp/s32/psi5/boards/s32z2xxdc2_s32z270_rtu1.overlay @@ -0,0 +1,52 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&pinctrl { + psi5_0_default: psi5_0_default { + group1 { + pinmux = , ; + output-enable; + }; + group2 { + pinmux = , ; + input-enable; + }; + }; +}; + +&psi5_0 { + pinctrl-0 = <&psi5_0_default>; + pinctrl-names = "default"; + status = "okay"; +}; + +&psi5_0_ch1 { + target-pulse-us = <8>; + decoder-offset-us = <0>; + low-pulse-width-us = <4>; + high-pulse-width-us = <4>; + tx-mode = "short-frame-31"; + rx-buf-size = <32>; + bitrate-kbps = <125>; + array-slot-duration-us = <1000 1000>; + array-slot-start-offset-us = <0 0>; + array-slot-data-length = <16 16>; + status = "okay"; +}; + +&psi5_0_ch2 { + target-pulse-us = <8>; + decoder-offset-us = <0>; + low-pulse-width-us = <4>; + high-pulse-width-us = <4>; + tx-mode = "short-frame-31"; + rx-buf-size = <32>; + bitrate-kbps = <125>; + array-slot-duration-us = <1000 1000>; + array-slot-start-offset-us = <0 0>; + array-slot-data-length = <16 16>; + status = "okay"; +}; diff --git a/samples/boards/nxp/s32/psi5/prj.conf b/samples/boards/nxp/s32/psi5/prj.conf new file mode 100644 index 0000000000000..bac255b3c7052 --- /dev/null +++ b/samples/boards/nxp/s32/psi5/prj.conf @@ -0,0 +1,3 @@ +CONFIG_PSI5=y +CONFIG_PSI5_LOG_LEVEL_DBG=y +CONFIG_LOG=y diff --git a/samples/boards/nxp/s32/psi5/sample.yaml b/samples/boards/nxp/s32/psi5/sample.yaml new file mode 100644 index 0000000000000..81ac63c0b838c --- /dev/null +++ b/samples/boards/nxp/s32/psi5/sample.yaml @@ -0,0 +1,14 @@ +sample: + description: Sample for using PSI5 driver + name: NXP S32 PSI5 sample + +tests: + sample.boards.nxp_s32.psi5: + platform_allow: + - s32z2xxdc2/s32z270/rtu0 + - s32z2xxdc2/s32z270/rtu1 + - s32z2xxdc2@D/s32z270/rtu0 + - s32z2xxdc2@D/s32z270/rtu1 + depends_on: psi5 + tags: psi5 + harness: console diff --git a/samples/boards/nxp/s32/psi5/src/main.c b/samples/boards/nxp/s32/psi5/src/main.c new file mode 100644 index 0000000000000..6fc2a4172bcfc --- /dev/null +++ b/samples/boards/nxp/s32/psi5/src/main.c @@ -0,0 +1,43 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +LOG_MODULE_REGISTER(nxp_s32_psi5_sample, LOG_LEVEL_DBG); + +#include + +#include + +#define PSI5_NODE DT_INST(0, nxp_s32_psi5) + +void tx_cb(const struct device *dev, uint8_t channel_id, enum psi5_state state, void *user_data) +{ + LOG_INF("tx ch %d", channel_id); +} + +void rx_cb(const struct device *dev, uint8_t channel_id, struct psi5_frame *frame, + enum psi5_state state, void *user_data) +{ + LOG_INF("rx ch %d", channel_id); +} + +int main(void) +{ + const struct device *const dev = DEVICE_DT_GET(PSI5_NODE); + uint64_t send_data = 0x123456789ABCDEF; + + psi5_add_rx_callback(dev, 2, rx_cb, NULL); + + psi5_start(dev, 1); + + psi5_send(dev, 1, send_data, K_MSEC(100), tx_cb, NULL); + + psi5_stop(dev, 1); + + k_sleep(K_MSEC(100)); + + return 0; +} From 6319d3d6710ea15997af513265db304d8ac7dd67 Mon Sep 17 00:00:00 2001 From: Tu Nguyen Van Date: Mon, 18 Nov 2024 01:20:09 +0700 Subject: [PATCH 4/6] drivers: introduce support Peripheral Sensor Interface (PSI5_S) driver This driver allows to communication (send, receive) with PSI5_S device Signed-off-by: Tu Nguyen Van --- drivers/psi5/CMakeLists.txt | 1 + drivers/psi5/Kconfig.nxp_s32 | 7 + drivers/psi5/psi5_s_nxp_s32.c | 460 ++++++++++++++++++++++++++ dts/bindings/psi5/nxp,s32-psi5_s.yaml | 131 ++++++++ 4 files changed, 599 insertions(+) create mode 100644 drivers/psi5/psi5_s_nxp_s32.c create mode 100644 dts/bindings/psi5/nxp,s32-psi5_s.yaml diff --git a/drivers/psi5/CMakeLists.txt b/drivers/psi5/CMakeLists.txt index 04b3c639fc4f4..32fa04a83c038 100644 --- a/drivers/psi5/CMakeLists.txt +++ b/drivers/psi5/CMakeLists.txt @@ -6,3 +6,4 @@ zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/psi5/psi5.h) zephyr_library() zephyr_library_sources_ifdef(CONFIG_PSI5_NXP_S32 psi5_nxp_s32.c) +zephyr_library_sources_ifdef(CONFIG_PSI5_S_NXP_S32 psi5_s_nxp_s32.c) diff --git a/drivers/psi5/Kconfig.nxp_s32 b/drivers/psi5/Kconfig.nxp_s32 index 8a4248899add2..2987ac84fd189 100644 --- a/drivers/psi5/Kconfig.nxp_s32 +++ b/drivers/psi5/Kconfig.nxp_s32 @@ -7,3 +7,10 @@ config PSI5_NXP_S32 depends on DT_HAS_NXP_S32_PSI5_ENABLED help Enable support for NXP S32 PSI5 driver. + +config PSI5_S_NXP_S32 + bool "NXP S32 PSI5_S driver" + default y + depends on DT_HAS_NXP_S32_PSI5_S_ENABLED + help + Enable support for NXP S32 PSI5_S driver. diff --git a/drivers/psi5/psi5_s_nxp_s32.c b/drivers/psi5/psi5_s_nxp_s32.c new file mode 100644 index 0000000000000..dd68d6707d933 --- /dev/null +++ b/drivers/psi5/psi5_s_nxp_s32.c @@ -0,0 +1,460 @@ +/* + * Copyright 2024 NXP + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT nxp_s32_psi5_s + +#include + +LOG_MODULE_REGISTER(nxp_s32_psi5_s, CONFIG_PSI5_LOG_LEVEL); + +#include +#include +#include +#include + +#include + +#include "Psi5_S_Ip.h" + +#define PSI5_S_CHANNEL_COUNT 8 + +struct psi5_s_nxp_s32_config { + uint8_t ctrl_inst; + uint8_t channel_mask; + const struct device *clock_dev; + clock_control_subsys_t clock_subsys; + const struct pinctrl_dev_config *pin_cfg; + void (*irq_config_func)(void); +}; + +struct psi5_s_nxp_s32_tx_callback { + psi5_tx_callback_t callback; + void *user_data; +}; +struct psi5_s_nxp_s32_rx_callback { + psi5_rx_callback_t callback; + struct psi5_frame frame; + void *user_data; +}; + +struct psi5_s_nxp_s32_channel_data { + boolean started; + struct psi5_s_nxp_s32_tx_callback tx_callback; + struct psi5_s_nxp_s32_rx_callback rx_callback; + struct k_sem tx_sem; + struct k_mutex lock; +}; + +struct psi5_s_nxp_s32_data { + struct psi5_s_nxp_s32_channel_data channel_data[PSI5_S_CHANNEL_COUNT]; +}; + +static int psi5_s_nxp_s32_start(const struct device *dev, uint8_t channel) +{ + const struct psi5_s_nxp_s32_config *config = dev->config; + struct psi5_s_nxp_s32_data *data = dev->data; + struct psi5_s_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; + } + + k_mutex_lock(&channel_data->lock, K_FOREVER); + + err = Psi5_S_Ip_SetChannelSync(config->ctrl_inst, channel, true); + + if (err) { + LOG_ERR("Failed to start PSI5_S %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_s_nxp_s32_stop(const struct device *dev, uint8_t channel) +{ + const struct psi5_s_nxp_s32_config *config = dev->config; + struct psi5_s_nxp_s32_data *data = dev->data; + struct psi5_s_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; + } + + k_mutex_lock(&channel_data->lock, K_FOREVER); + + err = Psi5_S_Ip_SetChannelSync(config->ctrl_inst, channel, false); + + if (err) { + LOG_ERR("Failed to stop PSI5_S %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_s_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_s_nxp_s32_config *config = dev->config; + struct psi5_s_nxp_s32_data *data = dev->data; + struct psi5_s_nxp_s32_channel_data *channel_data = &data->channel_data[channel]; + int err; + uint64_t start_time; + + if (!(config->channel_mask & BIT(channel))) { + return -EINVAL; + } + + if (!channel_data->started) { + return -ENETDOWN; + } + + if (k_sem_take(&channel_data->tx_sem, timeout) != 0) { + return -EAGAIN; + } + + if (callback != NULL) { + channel_data->tx_callback.callback = callback; + channel_data->tx_callback.user_data = user_data; + } + + k_mutex_lock(&channel_data->lock, K_FOREVER); + + err = Psi5_S_Ip_Transmit(config->ctrl_inst, channel, psi5_data); + if (err) { + LOG_ERR("Failed to transmit PSI5_S %d channel %d (err %d)", config->ctrl_inst, + channel, err); + k_sem_give(&channel_data->tx_sem); + goto unlock; + return -EIO; + } + + if (callback != NULL) { + goto unlock; + return 0; + } + + start_time = k_uptime_ticks(); + + while (!Psi5_S_Ip_GetTransmissionStatus(config->ctrl_inst, channel)) { + if (k_uptime_ticks() - start_time >= timeout.ticks) { + LOG_ERR("Timeout for waiting transmision PSI5_S %d channel %d", + config->ctrl_inst, channel); + k_sem_give(&channel_data->tx_sem); + goto unlock; + return -EAGAIN; + } + } + + k_sem_give(&channel_data->tx_sem); + +unlock: + k_mutex_unlock(&channel_data->lock); + + return 0; +} + +static int psi5_s_nxp_s32_add_rx_callback(const struct device *dev, uint8_t channel, + psi5_rx_callback_t callback, void *user_data) +{ + const struct psi5_s_nxp_s32_config *config = dev->config; + struct psi5_s_nxp_s32_data *data = dev->data; + struct psi5_s_nxp_s32_channel_data *channel_data = &data->channel_data[channel]; + + if (!(config->channel_mask & BIT(channel))) { + return -EINVAL; + } + + if ((channel_data->rx_callback.callback == callback) && + (channel_data->rx_callback.user_data == user_data)) { + return 0; + } + + if (channel_data->rx_callback.callback) { + return -EBUSY; + } + + k_mutex_lock(&channel_data->lock, K_FOREVER); + + channel_data->rx_callback.callback = callback; + channel_data->rx_callback.user_data = user_data; + + k_mutex_unlock(&channel_data->lock); + + return 0; +} + +static const struct psi5_driver_api psi5_s_nxp_s32_driver_api = { + .start = psi5_s_nxp_s32_start, + .stop = psi5_s_nxp_s32_stop, + .send = psi5_s_nxp_s32_send, + .add_rx_callback = psi5_s_nxp_s32_add_rx_callback, +}; + +#define PSI5_S_NXP_S32_HW_INSTANCE_CHECK(i, n) \ + ((DT_INST_REG_ADDR(n) == IP_PSI5_S_##i##_BASE) ? i : 0) + +#define PSI5_S_NXP_S32_HW_INSTANCE(n) \ + LISTIFY(PSI5_S_INSTANCE_COUNT, PSI5_S_NXP_S32_HW_INSTANCE_CHECK, (|), n) + +#define PSI5_S_NXP_S32_CHANNEL_CALLBACK(node_id) \ + void _CONCAT(psi5_s_nxp_s32_channel_tx_callBack, node_id)(Psi5_S_EventType event) \ + { \ + const struct device *dev = DEVICE_DT_GET(DT_PARENT(node_id)); \ + uint8_t channel = DT_REG_ADDR(node_id); \ + struct psi5_s_nxp_s32_data *data = dev->data; \ + struct psi5_s_nxp_s32_channel_data *channel_data = &data->channel_data[channel]; \ + struct psi5_s_nxp_s32_tx_callback *tx_callback = &channel_data->tx_callback; \ + if (event.Psi5S_ReadyToTransmit) { \ + if (tx_callback->callback) { \ + tx_callback->callback(dev, channel, PSI5_STATE_TX_READY, \ + tx_callback->user_data); \ + } \ + k_sem_give(&channel_data->tx_sem); \ + } \ + if (event.Psi5S_TxDataOverwrite) { \ + if (tx_callback->callback) { \ + tx_callback->callback(dev, channel, PSI5_STATE_TX_OVERWRITE, \ + tx_callback->user_data); \ + } \ + k_sem_give(&channel_data->tx_sem); \ + } \ + } \ + void _CONCAT(psi5_s_nxp_s32_channel_rx_callBack, node_id)( \ + Psi5_S_Ip_InstanceIdType Psi5SInstanceId, Psi5_S_Ip_Psi5SFrameType Psi5SFramePtr) \ + { \ + const struct device *dev = DEVICE_DT_GET(DT_PARENT(node_id)); \ + uint8_t channel = DT_REG_ADDR(node_id); \ + struct psi5_s_nxp_s32_data *data = dev->data; \ + struct psi5_s_nxp_s32_channel_data *channel_data = &data->channel_data[channel]; \ + struct psi5_s_nxp_s32_rx_callback *rx_callback = &channel_data->rx_callback; \ + \ + rx_callback->frame.msg.data = Psi5SFramePtr.PS_DATA; \ + rx_callback->frame.msg.timestamp = Psi5SFramePtr.TIME_STAMP; \ + rx_callback->frame.msg.crc = Psi5SFramePtr.CRC; \ + if (rx_callback->callback) { \ + rx_callback->callback(dev, channel, &rx_callback->frame, \ + PSI5_STATE_MSG_RECEIVED, rx_callback->user_data); \ + } \ + } + +#define _PSI5_S_NXP_S32_CHANNEL_RX_SLOT_CONFIG(i, node_id) \ + { \ + .slotId = UTIL_INC(i), \ + .useCRC = true, \ + .payloadSize = DT_PROP_BY_IDX(node_id, slots_pay_load_size, i), \ + }, + +#define PSI5_S_NXP_S32_CHANNEL_RX_SLOT_CONFIG(node_id) \ + static const Psi5_S_Ip_SlotConfigType _CONCAT( \ + psi5_s_nxp_s32_channel_rx_slot_config, \ + node_id)[DT_PROP_LEN(node_id, slots_pay_load_size)] = { \ + LISTIFY(DT_PROP_LEN(node_id, slots_pay_load_size), \ + _PSI5_S_NXP_S32_CHANNEL_RX_SLOT_CONFIG, (), node_id)}; + +#define PSI5_S_NXP_S32_CHANNEL_RX_CONFIG(node_id) \ + const Psi5_S_Ip_ChannelRxConfigType _CONCAT(psi5_s_nxp_s32_channel_rx_config, node_id) = { \ + .slotConfig = &_CONCAT(psi5_s_nxp_s32_channel_rx_slot_config, node_id)[0], \ + .numOfSlotConfigs = DT_PROP_LEN(node_id, slots_pay_load_size), \ + }; + +#define PSI5_S_NXP_S32_CHANNEL_TX_CONFIG(node_id) \ + const Psi5_S_Ip_ChannelTxConfigType _CONCAT(psi5_s_nxp_s32_channel_tx_config, node_id) = { \ + .clockSel = IPG_CLK_PS_DDTRIG, \ + .initCMD = DT_PROP(node_id, init_cmd), \ + .initACMD = DT_PROP(node_id, init_acmd), \ + .targetPeriod = DT_PROP(node_id, target_period), \ + .counterDelay = DT_PROP(node_id, counter_delay), \ + .txMode = DT_ENUM_IDX(node_id, tx_mode), \ + }; + +/* + * The macro get index of array configuration that corresponds to each the ID of HW channel. + * Assign 0xff to unused channels. + */ + +#define PSI5_S_NXP_S32_CHANNEL_NODE(n, i) DT_INST_CHILD(n, DT_CAT(ch_, i)) + +#define PSI5_S_NXP_S32_ID_CFG_CNT(i, node_id, n) \ + (DT_NODE_HAS_STATUS(PSI5_S_NXP_S32_CHANNEL_NODE(n, i), okay) && \ + (DT_REG_ADDR(PSI5_S_NXP_S32_CHANNEL_NODE(n, i)) < (DT_REG_ADDR(node_id))) \ + ? 1 \ + : 0) + +#define PSI5_S_NXP_S32_ID_CFG(node_id, n) \ + COND_CODE_1(DT_NODE_HAS_STATUS(node_id, okay), \ + (LISTIFY(PSI5_S_CHANNEL_COUNT, PSI5_S_NXP_S32_ID_CFG_CNT, (+), node_id, n), ), \ + (0xff, )) + +#define PSI5_S_NXP_S32_CHANNEL_CONFIG(node_id) \ + { \ + .channelId = DT_REG_ADDR(node_id), \ + .mode = DT_PROP(node_id, async_mode), \ + .callbackRx = _CONCAT(psi5_s_nxp_s32_channel_rx_callBack, node_id), \ + .callbackTx = _CONCAT(psi5_s_nxp_s32_channel_tx_callBack, node_id), \ + .timestamp = PSI5_S_TIME_STAMP_A, \ + .useCRC = true, \ + .rxConfig = &_CONCAT(psi5_s_nxp_s32_channel_rx_config, node_id), \ + .txConfig = &_CONCAT(psi5_s_nxp_s32_channel_tx_config, node_id), \ + }, + +/* Define array channel configuration */ +#define PSI5_S_NXP_S32_ARRAY_CHANNEL_CONFIG(n) \ + DT_INST_FOREACH_CHILD_STATUS_OKAY(n, PSI5_S_NXP_S32_CHANNEL_CALLBACK) \ + DT_INST_FOREACH_CHILD_STATUS_OKAY(n, PSI5_S_NXP_S32_CHANNEL_RX_SLOT_CONFIG) \ + DT_INST_FOREACH_CHILD_STATUS_OKAY(n, PSI5_S_NXP_S32_CHANNEL_RX_CONFIG) \ + DT_INST_FOREACH_CHILD_STATUS_OKAY(n, PSI5_S_NXP_S32_CHANNEL_TX_CONFIG) \ + const Psi5_S_Ip_ChannelConfigType \ + psi5_s_nxp_s32_channel_array_config_##n[DT_INST_CHILD_NUM_STATUS_OKAY(n)] = { \ + DT_INST_FOREACH_CHILD_STATUS_OKAY(n, PSI5_S_NXP_S32_CHANNEL_CONFIG)}; \ + const uint8_t psi5_s_nxp_s32_map_idex_array_config_##n[PSI5_S_CHANNEL_COUNT] = { \ + DT_INST_FOREACH_CHILD_VARGS(n, PSI5_S_NXP_S32_ID_CFG, n)}; + +DT_INST_FOREACH_STATUS_OKAY(PSI5_S_NXP_S32_ARRAY_CHANNEL_CONFIG) + +#define PSI5_S_NXP_S32_UART_CONFIG(n) \ + Psi5_S_Ip_UartConfigType psi5_s_nxp_s32_uart_config_##n = { \ + .Uart_baud_rate = DT_INST_PROP(n, uart_baud_rate), \ + .Uart_preset_timeout = DT_INST_PROP(n, uart_preset_timeout), \ + .Uart_tx_idle_delay_time_enable = true, \ + .Uart_tx_idle_delay_time = DT_INST_PROP(n, uart_tx_idle_delay_time), \ + .Uart_reduced_over_sampling_enable = true, \ + .Uart_over_sampling_rate = DT_INST_PROP(n, uart_reduced_over_sampling), \ + .Uart_sampling_point = DT_INST_PROP(n, uart_sampling_point), \ + }; + +DT_INST_FOREACH_STATUS_OKAY(PSI5_S_NXP_S32_UART_CONFIG) + +/* Define array instance configuration */ +#define PSI5_S_NXP_S32_INST_CONFIG(n) \ + { \ + .instanceId = PSI5_S_NXP_S32_HW_INSTANCE(n), \ + .channelConfig = &psi5_s_nxp_s32_channel_array_config_##n[0], \ + .numOfChannels = DT_INST_CHILD_NUM_STATUS_OKAY(n), \ + .chHwIdToIndexArrayConfig = &psi5_s_nxp_s32_map_idex_array_config_##n[0], \ + .uartConfig = &psi5_s_nxp_s32_uart_config_##n, \ + }, + +static const Psi5_S_Ip_InstanceType psi5_s_nxp_s32_array_inst_config[DT_NUM_INST_STATUS_OKAY( + DT_DRV_COMPAT)] = {DT_INST_FOREACH_STATUS_OKAY(PSI5_S_NXP_S32_INST_CONFIG)}; + +/* The structure configuration for all controller instnaces that used for Psi5_S_Ip_Init() */ +static const Psi5_S_Ip_ConfigType psi5_s_nxp_s32_controller_config = { + .instancesConfig = &psi5_s_nxp_s32_array_inst_config[0], + .numOfInstances = DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT), +}; + +#define PSI5_S_NXP_S32_CHANNEL_ISR(node_id) \ + static void _CONCAT(psi5_s_nxp_s32_channel_isr, node_id)(const struct device *dev) \ + { \ + const struct psi5_s_nxp_s32_config *config = dev->config; \ + \ + Psi5_S_Ip_IRQ_Handler_Tx(config->ctrl_inst, DT_REG_ADDR(node_id)); \ + Psi5_S_Ip_IRQ_Handler_Rx(config->ctrl_inst, DT_REG_ADDR(node_id)); \ + } + +#define PSI5_S_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_s_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_S_NXP_S32_IRQ_CONFIG(n) \ + DT_INST_FOREACH_CHILD_STATUS_OKAY(n, PSI5_S_NXP_S32_CHANNEL_ISR) \ + static void psi5_s_irq_config_##n(void) \ + { \ + DT_INST_FOREACH_CHILD_STATUS_OKAY_VARGS(n, PSI5_S_NXP_S32_CHANNEL_IRQ_CONFIG, n) \ + } + +#define PSI5_S_NXP_S32_CHANNEL_BIT_MASK(node_id) BIT(DT_REG_ADDR(node_id)) + +#define PSI5_S_NXP_S32_INIT(n) \ + PINCTRL_DT_INST_DEFINE(n); \ + PSI5_S_NXP_S32_IRQ_CONFIG(n) \ + static struct psi5_s_nxp_s32_config psi5_s_nxp_s32_config_##n = { \ + .ctrl_inst = PSI5_S_NXP_S32_HW_INSTANCE(n), \ + .channel_mask = DT_INST_FOREACH_CHILD_STATUS_OKAY_SEP( \ + n, PSI5_S_NXP_S32_CHANNEL_BIT_MASK, (|)), \ + .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 = psi5_s_irq_config_##n, \ + }; \ + static struct psi5_s_nxp_s32_data psi5_s_nxp_s32_data_##n; \ + static int psi5_s_nxp_s32_init_##n(const struct device *dev) \ + { \ + const struct psi5_s_nxp_s32_config *config = dev->config; \ + struct psi5_s_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 *)&psi5_s_nxp_s32_uart_config_##n.Uart_baud_clock, &rate, \ + sizeof(uint32_t)); \ + \ + err = pinctrl_apply_state(config->pin_cfg, PINCTRL_STATE_DEFAULT); \ + if (err < 0) { \ + LOG_ERR("PSI5_S pinctrl setup failed (%d)", err); \ + return err; \ + } \ + \ + for (int i = 0; i < PSI5_S_CHANNEL_COUNT; i++) { \ + k_sem_init(&data->channel_data[i].tx_sem, 1, 1); \ + k_mutex_init(&data->channel_data[i].lock); \ + } \ + \ + /* Common configuration setup for all controller instances */ \ + if (n == UTIL_DEC(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT))) { \ + Psi5_S_Ip_Init(&psi5_s_nxp_s32_controller_config); \ + } \ + \ + config->irq_config_func(); \ + \ + return 0; \ + } \ + DEVICE_DT_INST_DEFINE(n, psi5_s_nxp_s32_init_##n, NULL, &psi5_s_nxp_s32_data_##n, \ + &psi5_s_nxp_s32_config_##n, POST_KERNEL, CONFIG_PSI5_INIT_PRIORITY, \ + &psi5_s_nxp_s32_driver_api); + +DT_INST_FOREACH_STATUS_OKAY(PSI5_S_NXP_S32_INIT) diff --git a/dts/bindings/psi5/nxp,s32-psi5_s.yaml b/dts/bindings/psi5/nxp,s32-psi5_s.yaml new file mode 100644 index 0000000000000..6e5984cb64f28 --- /dev/null +++ b/dts/bindings/psi5/nxp,s32-psi5_s.yaml @@ -0,0 +1,131 @@ +# Copyright 2024 NXP +# +# SPDX-License-Identifier: Apache-2.0 + +description: Properties for Peripheral Sensor Interface with Serial PHY (PSI5_S) + +compatible: "nxp,s32-psi5_s" + +include: [base.yaml, pinctrl-device.yaml] + +properties: + uart-baud-rate: + type: int + required: true + enum: + - 1200 + - 2400 + - 4800 + - 7200 + - 9600 + - 14400 + - 19200 + - 28800 + - 38400 + - 57600 + - 115200 + - 230400 + - 460800 + - 921600 + - 1843200 + description: | + uart-baud-rate + + uart-preset-timeout: + type: int + required: true + description: | + Preset timeout defines the preset value of timeout counter. + A zero-value is forbidden + + uart-tx-idle-delay-time: + type: int + required: true + description: | + This register is used to add a programmable idle time between the data that is transmitted + on the UART Tx line. The duration of this delay is programmable between 1 to 16 bit times. + The programmable delay is such that when the normal ECU to Sensor communication(no direct command) + is happening, then it comes into play between each byte of transmitted data, When the ECU + to Sensor direct command is used then this delay comes into play between each direct command. + The direct commands can be programmed as 1,2 and 4 bytes depending on the GLCR[DIRCMD_LEN] register + + uart-reduced-over-sampling: + type: int + required: true + description: | + uart-reduced-over-sampling + + uart-sampling-point: + type: int + required: true + description: | + uart-sampling-point + +child-binding: + description: Properties for Peripheral Sensor Interface with Serial PHY (PSI5_S) channel + + compatible: "nxp,s32-psi5_s-channel" + + include: base.yaml + + properties: + async-mode: + type: boolean + description: | + This setting enables the channel operation asynchronous mode for Rx only. + Otherwise enables the channel operation synchronous mode for both Rx and Tx. + + # Channel TX configuration + init-cmd: + required: true + type: int + description: | + Initial reset reload value for the integrated "CMD" format + + init-acmd: + required: true + type: int + description: | + Initial reset reload value for the integrated "ACMD" format + + target-period: + required: true + type: int + description: | + Subsequent reload values for the integrated period generator + + counter-delay: + required: true + type: int + description: | + Time in us for which the manchester decoder is disabled after the falling + edge of a sync pulse + + tx-mode: + type: string + required: true + enum: + - short-frame-31 + - short-frame-5 + - long-frame-31 + - long-frame-5 + - x-long-frame-31 + - x-long-frame-5 + - xx-long-frame + - non-standard-frame + description: | + Transmitter mode: + - short-frame-31: Short Frame (V1.3) with 31 "1s" as the start condition + - short-frame-5: Short Frame (V1.3) with 5 "0s" as the start condition + - long-frame-31: Long Frame (V1.3) with 31 "1s" as the start condition + - long-frame-5: Long Frame (V1.3) with 5 "0s" as the start condition + - x-long-fram-31: X-Long Frame (V1.3) with 31 "1s" as the start condition + - x-long-frame-5: X-Long Frame (V1.3) with 5 "0s" as the start condition + - xx-long-frame: XX-Long (V2.0) + - non-standard-frame: Non Standard Length + + slots-pay-load-size: + type: array + required: true + description: | + Payload region length From 61b20b407a31f60ac6b540f76751759f255e5592 Mon Sep 17 00:00:00 2001 From: Tu Nguyen Van Date: Mon, 18 Nov 2024 01:21:21 +0700 Subject: [PATCH 5/6] boards: s32z270: enable support psi5s enable support psi5s Signed-off-by: Tu Nguyen Van --- dts/arm/nxp/nxp_s32z27x_r52.dtsi | 130 +++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/dts/arm/nxp/nxp_s32z27x_r52.dtsi b/dts/arm/nxp/nxp_s32z27x_r52.dtsi index 2ef705c245e1f..dc267ffd9878f 100644 --- a/dts/arm/nxp/nxp_s32z27x_r52.dtsi +++ b/dts/arm/nxp/nxp_s32z27x_r52.dtsi @@ -1523,5 +1523,135 @@ status = "disabled"; }; }; + + psi5_s_0: psi5_s@401f0000 { + compatible = "nxp,s32-psi5_s"; + reg = <0x401f0000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clock NXP_S32_P0_PSI5_S_UART_CLK>; + status = "disabled"; + + psi5_s_0_ch0: ch@0 { + compatible = "nxp,s32-psi5_s-channel"; + reg = <0>; + interrupts = ; + status = "disabled"; + }; + + psi5_s_0_ch1: ch@1 { + compatible = "nxp,s32-psi5_s-channel"; + reg = <1>; + interrupts = ; + status = "disabled"; + }; + + psi5_s_0_ch2: ch@2 { + compatible = "nxp,s32-psi5_s-channel"; + reg = <2>; + interrupts = ; + status = "disabled"; + }; + + psi5_s_0_ch3: ch@3 { + compatible = "nxp,s32-psi5_s-channel"; + reg = <3>; + interrupts = ; + status = "disabled"; + }; + + psi5_s_0_ch4: ch@4 { + compatible = "nxp,s32-psi5_s-channel"; + reg = <4>; + interrupts = ; + status = "disabled"; + }; + + psi5_s_0_ch5: ch@5 { + compatible = "nxp,s32-psi5_s-channel"; + reg = <5>; + interrupts = ; + status = "disabled"; + }; + + psi5_s_0_ch6: ch@6 { + compatible = "nxp,s32-psi5_s-channel"; + reg = <6>; + interrupts = ; + status = "disabled"; + }; + + psi5_s_0_ch7: ch@7 { + compatible = "nxp,s32-psi5_s-channel"; + reg = <7>; + interrupts = ; + status = "disabled"; + }; + }; + + psi5_s_1: psi5_s@421f0000 { + compatible = "nxp,s32-psi5_s"; + reg = <0x421f0000 0x1000>; + #address-cells = <1>; + #size-cells = <0>; + clocks = <&clock NXP_S32_P0_PSI5_S_UART_CLK>; + status = "disabled"; + + psi5_s_1_ch0: ch@0 { + compatible = "nxp,s32-psi5_s-channel"; + reg = <0>; + interrupts = ; + status = "disabled"; + }; + + psi5_s_1_ch1: ch@1 { + compatible = "nxp,s32-psi5_s-channel"; + reg = <1>; + interrupts = ; + status = "disabled"; + }; + + psi5_s_1_ch2: ch@2 { + compatible = "nxp,s32-psi5_s-channel"; + reg = <2>; + interrupts = ; + status = "disabled"; + }; + + psi5_s_1_ch3: ch@3 { + compatible = "nxp,s32-psi5_s-channel"; + reg = <3>; + interrupts = ; + status = "disabled"; + }; + + psi5_s_1_ch4: ch@4 { + compatible = "nxp,s32-psi5_s-channel"; + reg = <4>; + interrupts = ; + status = "disabled"; + }; + + psi5_s_1_ch5: ch@5 { + compatible = "nxp,s32-psi5_s-channel"; + reg = <5>; + interrupts = ; + status = "disabled"; + }; + + psi5_s_1_ch6: ch@6 { + compatible = "nxp,s32-psi5_s-channel"; + reg = <6>; + interrupts = ; + status = "disabled"; + }; + + psi5_s_1_ch7: ch@7 { + compatible = "nxp,s32-psi5_s-channel"; + reg = <7>; + interrupts = ; + status = "disabled"; + }; + }; }; }; From 3552a9570d6d4eda9420caa3f0b3e125cdcd6f34 Mon Sep 17 00:00:00 2001 From: Tu Nguyen Van Date: Mon, 18 Nov 2024 01:22:10 +0700 Subject: [PATCH 6/6] samples: boards: nxp: add sample for using psi5_s driver add sample for using psi5_s driver in S32Z270 device Signed-off-by: Tu Nguyen Van --- .../boards/s32z2xxdc2_s32z270_rtu0.overlay | 48 ++++++++++++++++++- samples/boards/nxp/s32/psi5/src/main.c | 10 +++- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/samples/boards/nxp/s32/psi5/boards/s32z2xxdc2_s32z270_rtu0.overlay b/samples/boards/nxp/s32/psi5/boards/s32z2xxdc2_s32z270_rtu0.overlay index ce6ed4e0edf78..9df8ace8e2308 100644 --- a/samples/boards/nxp/s32/psi5/boards/s32z2xxdc2_s32z270_rtu0.overlay +++ b/samples/boards/nxp/s32/psi5/boards/s32z2xxdc2_s32z270_rtu0.overlay @@ -15,12 +15,23 @@ input-enable; }; }; + + psi5_s_0_default: psi5_s_0_default { + group1 { + pinmux = , ; + output-enable; + }; + group2 { + pinmux = , ; + input-enable; + }; + }; }; &psi5_0 { pinctrl-0 = <&psi5_0_default>; pinctrl-names = "default"; - status = "okay"; + status = "disabled"; }; &psi5_0_ch1 { @@ -34,7 +45,7 @@ array-slot-duration-us = <1000 1000>; array-slot-start-offset-us = <0 0>; array-slot-data-length = <16 16>; - status = "okay"; + status = "disabled"; }; &psi5_0_ch2 { @@ -48,5 +59,38 @@ array-slot-duration-us = <1000 1000>; array-slot-start-offset-us = <0 0>; array-slot-data-length = <16 16>; + status = "disabled"; +}; + +&psi5_s_0 { + pinctrl-0 = <&psi5_s_0_default>; + pinctrl-names = "default"; + + uart-baud-rate = <115200>; + uart-preset-timeout = <5>; + uart-tx-idle-delay-time = <0>; + uart-reduced-over-sampling = <4>; + uart-sampling-point = <3>; + + status = "okay"; +}; + +&psi5_s_0_ch1 { + init-cmd = <500>; + init-acmd = <500>; + target-period = <500>; + counter-delay = <500>; + tx-mode = "short-frame-31"; + slots-pay-load-size = <16 16>; + status = "okay"; +}; + +&psi5_s_0_ch2 { + init-cmd = <500>; + init-acmd = <500>; + target-period = <500>; + counter-delay = <500>; + tx-mode = "short-frame-31"; + slots-pay-load-size = <16 16>; status = "okay"; }; diff --git a/samples/boards/nxp/s32/psi5/src/main.c b/samples/boards/nxp/s32/psi5/src/main.c index 6fc2a4172bcfc..458a6378754e9 100644 --- a/samples/boards/nxp/s32/psi5/src/main.c +++ b/samples/boards/nxp/s32/psi5/src/main.c @@ -11,7 +11,11 @@ LOG_MODULE_REGISTER(nxp_s32_psi5_sample, LOG_LEVEL_DBG); #include +#if DT_HAS_COMPAT_STATUS_OKAY(nxp_s32_psi5) #define PSI5_NODE DT_INST(0, nxp_s32_psi5) +#else +#define PSI5_NODE DT_INST(0, nxp_s32_psi5_s) +#endif void tx_cb(const struct device *dev, uint8_t channel_id, enum psi5_state state, void *user_data) { @@ -33,8 +37,10 @@ int main(void) psi5_start(dev, 1); - psi5_send(dev, 1, send_data, K_MSEC(100), tx_cb, NULL); - + while (1) { + psi5_send(dev, 1, send_data, K_MSEC(100), tx_cb, NULL); + k_sleep(K_MSEC(1000)); + } psi5_stop(dev, 1); k_sleep(K_MSEC(100));