Browse Source

drivers: introduce support Peripheral Sensor Interface (PSI5) driver

This driver allows to communication (send, receive) with PSI5 device

Signed-off-by: Cong Nguyen Huu <cong.nguyenhuu@nxp.com>
pull/88157/merge
Cong Nguyen Huu 10 months ago committed by Dan Kalowsky
parent
commit
c52ba71f94
  1. 17
      MAINTAINERS.yml
  2. 1
      doc/hardware/peripherals/index.rst
  3. 22
      doc/hardware/peripherals/psi5.rst
  4. 1
      drivers/CMakeLists.txt
  5. 1
      drivers/Kconfig
  6. 8
      drivers/psi5/CMakeLists.txt
  7. 23
      drivers/psi5/Kconfig
  8. 10
      drivers/psi5/Kconfig.nxp_s32
  9. 521
      drivers/psi5/psi5_nxp_s32.c
  10. 32
      dts/bindings/psi5/nxp,s32-psi5.yaml
  11. 141
      dts/bindings/psi5/psi5-controller.yaml
  12. 278
      include/zephyr/drivers/psi5/psi5.h
  13. 2
      west.yml

17
MAINTAINERS.yml

@ -2008,6 +2008,23 @@ Release Notes: @@ -2008,6 +2008,23 @@ Release Notes:
tests:
- sample.drivers.espi.ps2
"Drivers: PSI5":
status: maintained
maintainers:
- manuargue
- Dat-NguyenDuy
collaborators:
- congnguyenhuu
files:
- drivers/psi5/
- include/zephyr/drivers/psi5/
- dts/bindings/psi5/
- doc/hardware/peripherals/psi5.rst
labels:
- "area: PSI5"
tests:
- drivers.psi5
"Drivers: PTP Clock":
status: maintained
maintainers:

1
doc/hardware/peripherals/index.rst

@ -47,6 +47,7 @@ Peripherals @@ -47,6 +47,7 @@ Peripherals
pcie.rst
peci.rst
ps2.rst
psi5.rst
pwm.rst
rtc.rst
regulators.rst

22
doc/hardware/peripherals/psi5.rst

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
.. _psi5_api:
Peripheral Sensor Interface (PSI5)
##################################
Overview
********
The PSI5 API provides functionality to communicate with Peripheral Sensor Interface (PSI5)
devices.
Configuration Options
*********************
Related configuration options:
* :kconfig:option:`CONFIG_PSI5`
API Reference
*************
.. doxygengroup:: psi5_interface

1
drivers/CMakeLists.txt

@ -74,6 +74,7 @@ add_subdirectory_ifdef(CONFIG_PINCTRL pinctrl) @@ -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)

1
drivers/Kconfig

@ -71,6 +71,7 @@ source "drivers/pinctrl/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"

8
drivers/psi5/CMakeLists.txt

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

23
drivers/psi5/Kconfig

@ -0,0 +1,23 @@ @@ -0,0 +1,23 @@
# Copyright 2025 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

10
drivers/psi5/Kconfig.nxp_s32

@ -0,0 +1,10 @@ @@ -0,0 +1,10 @@
# Copyright 2025 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
select PINCTRL if $(dt_compat_any_has_prop,$(DT_COMPAT_NXP_S32_PSI5),pinctrl-0)
help
Enable support for NXP S32 PSI5 driver.

521
drivers/psi5/psi5_nxp_s32.c

@ -0,0 +1,521 @@ @@ -0,0 +1,521 @@
/*
* Copyright 2025 NXP
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT nxp_s32_psi5
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(nxp_s32_psi5, CONFIG_PSI5_LOG_LEVEL);
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/drivers/psi5/psi5.h>
#include "Psi5_Ip.h"
struct psi5_nxp_s32_config {
uint8_t ctrl_inst;
PSI5_Type *base;
uint8_t channel_mask;
const struct pinctrl_dev_config *pin_cfg;
void (*irq_config_func)(void);
};
struct psi5_nxp_s32_tx_callback {
psi5_tx_callback_t callback;
void *user_data;
};
struct psi5_nxp_s32_channel_data {
bool started;
bool async_mode;
struct psi5_nxp_s32_tx_callback tx_callback;
struct psi5_rx_callback_configs callback_configs;
uint32_t serial_frame_cnt;
uint32_t data_frame_cnt;
struct k_sem tx_sem;
struct k_mutex lock;
};
struct psi5_nxp_s32_data {
struct psi5_nxp_s32_channel_data channel_data[PSI5_CHANNEL_COUNT];
};
struct psi5_nxp_s32_tx_default_cb_ctx {
struct k_sem done;
int status;
};
static int psi5_nxp_s32_start_sync(const struct device *dev, uint8_t channel)
{
const struct psi5_nxp_s32_config *config = dev->config;
struct psi5_nxp_s32_data *data = dev->data;
struct psi5_nxp_s32_channel_data *channel_data = &data->channel_data[channel];
int err;
if (!(config->channel_mask & BIT(channel))) {
return -EINVAL;
}
if (channel_data->started) {
return -EALREADY;
}
if (channel_data->async_mode) {
return -ENOTSUP;
}
k_mutex_lock(&channel_data->lock, K_FOREVER);
err = Psi5_Ip_SetChannelSync(config->ctrl_inst, channel, true);
if (err) {
LOG_ERR("Failed to start sync PSI5 %d channel %d", config->ctrl_inst, channel);
k_mutex_unlock(&channel_data->lock);
return -EIO;
}
channel_data->started = true;
k_mutex_unlock(&channel_data->lock);
return 0;
}
static int psi5_nxp_s32_stop_sync(const struct device *dev, uint8_t channel)
{
const struct psi5_nxp_s32_config *config = dev->config;
struct psi5_nxp_s32_data *data = dev->data;
struct psi5_nxp_s32_channel_data *channel_data = &data->channel_data[channel];
int err;
if (!(config->channel_mask & BIT(channel))) {
return -EINVAL;
}
if (!channel_data->started) {
return -EALREADY;
}
if (channel_data->async_mode) {
return -ENOTSUP;
}
k_mutex_lock(&channel_data->lock, K_FOREVER);
err = Psi5_Ip_SetChannelSync(config->ctrl_inst, channel, false);
if (err) {
LOG_ERR("Failed to stop sync PSI5 %d channel %d", config->ctrl_inst, channel);
k_mutex_unlock(&channel_data->lock);
return -EIO;
}
channel_data->started = false;
k_mutex_unlock(&channel_data->lock);
return 0;
}
static int psi5_nxp_s32_do_send(const struct device *dev, uint8_t channel, uint64_t psi5_data,
k_timeout_t timeout, psi5_tx_callback_t callback, void *user_data)
{
const struct psi5_nxp_s32_config *config = dev->config;
struct psi5_nxp_s32_data *data = dev->data;
struct psi5_nxp_s32_channel_data *channel_data = &data->channel_data[channel];
int err;
if (k_sem_take(&channel_data->tx_sem, timeout) != 0) {
return -EAGAIN;
}
channel_data->tx_callback.callback = callback;
channel_data->tx_callback.user_data = user_data;
err = Psi5_Ip_Transmit(config->ctrl_inst, channel, psi5_data);
if (err) {
LOG_ERR("Failed to transmit PSI5 %d channel %d", config->ctrl_inst, channel);
k_sem_give(&channel_data->tx_sem);
return -EIO;
}
return 0;
}
static void psi5_nxp_s32_tx_default_cb(const struct device *dev, uint8_t channel_id,
int status, void *user_data)
{
ARG_UNUSED(dev);
ARG_UNUSED(channel_id);
struct psi5_nxp_s32_tx_default_cb_ctx *ctx = user_data;
ctx->status = status;
k_sem_give(&ctx->done);
}
static int psi5_nxp_s32_send(const struct device *dev, uint8_t channel, uint64_t psi5_data,
k_timeout_t timeout, psi5_tx_callback_t callback, void *user_data)
{
const struct psi5_nxp_s32_config *config = dev->config;
struct psi5_nxp_s32_data *data = dev->data;
struct psi5_nxp_s32_channel_data *channel_data = &data->channel_data[channel];
if (!(config->channel_mask & BIT(channel))) {
return -EINVAL;
}
if (!channel_data->started) {
return -ENETDOWN;
}
if (channel_data->async_mode) {
return -ENOTSUP;
}
if (callback == NULL) {
struct psi5_nxp_s32_tx_default_cb_ctx ctx;
int err;
k_sem_init(&ctx.done, 0, 1);
err = psi5_nxp_s32_do_send(dev, channel, psi5_data, timeout,
psi5_nxp_s32_tx_default_cb, &ctx);
if (err != 0) {
return err;
}
k_sem_take(&ctx.done, K_FOREVER);
return ctx.status;
}
return psi5_nxp_s32_do_send(dev, channel, psi5_data, timeout, callback, user_data);
}
static int psi5_nxp_s32_register_callback(const struct device *dev, uint8_t channel,
struct psi5_rx_callback_configs callback_configs)
{
const struct psi5_nxp_s32_config *config = dev->config;
struct psi5_nxp_s32_data *data = dev->data;
struct psi5_nxp_s32_channel_data *channel_data = &data->channel_data[channel];
if (!(config->channel_mask & BIT(channel))) {
return -EINVAL;
}
k_mutex_lock(&channel_data->lock, K_FOREVER);
channel_data->callback_configs = callback_configs;
k_mutex_unlock(&channel_data->lock);
return 0;
}
static DEVICE_API(psi5, psi5_nxp_s32_driver_api) = {
.start_sync = psi5_nxp_s32_start_sync,
.stop_sync = psi5_nxp_s32_stop_sync,
.send = psi5_nxp_s32_send,
.register_callback = psi5_nxp_s32_register_callback,
};
#define PSI5_NXP_S32_HW_INSTANCE_CHECK(i, n) ((DT_INST_REG_ADDR(n) == IP_PSI5_##i##_BASE) ? i : 0)
#define PSI5_NXP_S32_HW_INSTANCE(n) \
LISTIFY(PSI5_INSTANCE_COUNT, PSI5_NXP_S32_HW_INSTANCE_CHECK, (|), n)
#define PSI5_NXP_S32_CHANNEL_CALLBACK(node_id) \
void _CONCAT(psi5_nxp_s32_channel_callBack, node_id)(Psi5_EventType event) \
{ \
const struct device *dev = DEVICE_DT_GET(DT_PARENT(node_id)); \
const struct psi5_nxp_s32_config *config = dev->config; \
struct psi5_nxp_s32_data *data = dev->data; \
uint8_t channel = DT_REG_ADDR(node_id); \
struct psi5_nxp_s32_channel_data *channel_data = &data->channel_data[channel]; \
struct psi5_nxp_s32_tx_callback *tx_callback = &channel_data->tx_callback; \
struct psi5_rx_callback_config *serial_frame_cb = \
channel_data->callback_configs.serial_frame; \
struct psi5_rx_callback_config *data_frame_cb = \
channel_data->callback_configs.data_frame; \
\
if (event.Psi5_DriverReadyToTransmit) { \
if (tx_callback->callback) { \
tx_callback->callback(dev, channel, 0, \
tx_callback->user_data); \
k_sem_give(&channel_data->tx_sem); \
} \
} else if (event.Psi5_TxDataOverwrite) { \
if (tx_callback->callback) { \
tx_callback->callback(dev, channel, -EIO, \
tx_callback->user_data); \
k_sem_give(&channel_data->tx_sem); \
} \
} else if (event.Psi5_Psi5MessageReceived && data_frame_cb) { \
Psi5_Ip_Psi5FrameType ip_frame; \
uint32_t *data_frame_cnt = &channel_data->data_frame_cnt; \
Psi5_Ip_GetPsi5Frame(config->ctrl_inst, channel, &ip_frame); \
\
if (!!(ip_frame.C | ip_frame.F | ip_frame.EM | ip_frame.E | ip_frame.T)) { \
data_frame_cb->callback(dev, channel, \
*data_frame_cnt, \
data_frame_cb->user_data); \
*data_frame_cnt = 0; \
} else { \
data_frame_cb->frame[*data_frame_cnt].type = PSI5_DATA_FRAME; \
data_frame_cb->frame[*data_frame_cnt].data = ip_frame.DATA_REGION; \
data_frame_cb->frame[*data_frame_cnt].timestamp = \
ip_frame.TIME_STAMP; \
data_frame_cb->frame[*data_frame_cnt].crc = ip_frame.CRC; \
data_frame_cb->frame[*data_frame_cnt].slot_number = \
ip_frame.SLOT_COUNTER; \
(*data_frame_cnt)++; \
if (*data_frame_cnt == data_frame_cb->max_num_frame) { \
data_frame_cb->callback(dev, channel, \
*data_frame_cnt, \
data_frame_cb->user_data); \
*data_frame_cnt = 0; \
} \
} \
} else if (event.Psi5_SmcMessageReceived && serial_frame_cb) { \
Psi5_Ip_SmcFrameType ip_smc_frame; \
uint32_t *serial_frame_cnt = &channel_data->serial_frame_cnt; \
Psi5_Ip_GetSmcFrame(config->ctrl_inst, channel, &ip_smc_frame); \
\
if (!!(ip_smc_frame.CER | ip_smc_frame.OW)) { \
serial_frame_cb->callback(dev, channel, \
*serial_frame_cnt, \
serial_frame_cb->user_data); \
*serial_frame_cnt = 0; \
} else { \
if (ip_smc_frame.C) { \
serial_frame_cb->frame[*serial_frame_cnt].type = \
PSI5_SERIAL_FRAME_4_BIT_ID; \
serial_frame_cb->frame[*serial_frame_cnt].serial.id = \
ip_smc_frame.ID; \
serial_frame_cb->frame[*serial_frame_cnt].serial.data = \
FIELD_PREP(GENMASK(15, 12), \
(ip_smc_frame.IDDATA)) | \
FIELD_PREP(GENMASK(11, 0), (ip_smc_frame.DATA)); \
} else { \
serial_frame_cb->frame[*serial_frame_cnt].type = \
PSI5_SERIAL_FRAME_8_BIT_ID; \
serial_frame_cb->frame[0].serial.id = \
FIELD_PREP(GENMASK(7, 4), (ip_smc_frame.ID)) | \
FIELD_PREP(GENMASK(3, 0), (ip_smc_frame.IDDATA)); \
serial_frame_cb->frame[0].serial.data = ip_smc_frame.DATA; \
} \
serial_frame_cb->frame[*serial_frame_cnt].crc = ip_smc_frame.CRC; \
serial_frame_cb->frame[*serial_frame_cnt].slot_number = \
ip_smc_frame.SLOT_NO; \
(*serial_frame_cnt)++; \
\
if (*serial_frame_cnt == serial_frame_cb->max_num_frame) { \
serial_frame_cb->callback( \
dev, channel, \
*serial_frame_cnt, serial_frame_cb->user_data); \
*serial_frame_cnt = 0; \
} \
} \
} \
}
#define PSI5_NXP_S32_SLOT_NODE(ch_node_id, slot) DT_CHILD(ch_node_id, slot_##slot)
#define PSI5_NXP_S32_SLOT_CNT(slot, ch_node_id) \
(DT_NODE_HAS_STATUS(PSI5_NXP_S32_SLOT_NODE(ch_node_id, slot), okay) ? 1 : 0)
#define __PSI5_NXP_S32_CHANNEL_RX_SLOT_CONFIG(slot, ch_node_id) \
{ \
.slotId = DT_REG_ADDR(PSI5_NXP_S32_SLOT_NODE(ch_node_id, slot)) + 1, \
.slotLen = DT_PROP(PSI5_NXP_S32_SLOT_NODE(ch_node_id, slot), duration_us), \
.startOffs = DT_PROP(PSI5_NXP_S32_SLOT_NODE(ch_node_id, slot), start_offset_us), \
.dataSize = DT_PROP(PSI5_NXP_S32_SLOT_NODE(ch_node_id, slot), data_length), \
.msbFirst = DT_PROP(PSI5_NXP_S32_SLOT_NODE(ch_node_id, slot), data_msb_first), \
.hasSMC = DT_PROP(PSI5_NXP_S32_SLOT_NODE(ch_node_id, slot), has_smc), \
.hasParity = DT_PROP(PSI5_NXP_S32_SLOT_NODE(ch_node_id, slot), has_parity), \
},
#define _PSI5_NXP_S32_CHANNEL_RX_SLOT_CONFIG(slot, ch_node_id) \
IF_ENABLED(DT_NODE_HAS_STATUS(PSI5_NXP_S32_SLOT_NODE(ch_node_id, slot), okay), \
(__PSI5_NXP_S32_CHANNEL_RX_SLOT_CONFIG(slot, ch_node_id)))
#define PSI5_NXP_S32_CHANNEL_RX_SLOT_CONFIG(node_id) \
static const Psi5_Ip_SlotConfigType _CONCAT(psi5_nxp_s32_channel_rx_slot_config, \
node_id)[PSI5_CHANNEL_CH_SFCR_COUNT] = { \
LISTIFY(PSI5_CHANNEL_CH_SFCR_COUNT, \
_PSI5_NXP_S32_CHANNEL_RX_SLOT_CONFIG, (), node_id)};
#define PSI5_NXP_S32_CHANNEL_RX_CONFIG(node_id) \
const Psi5_Ip_ChannelRxConfigType _CONCAT(psi5_nxp_s32_channel_rx_config, node_id) = { \
.rxBufSize = DT_PROP(node_id, num_rx_buf), \
.bitRate = DT_ENUM_IDX(node_id, rx_bitrate_kbps), \
.slotConfig = &_CONCAT(psi5_nxp_s32_channel_rx_slot_config, node_id)[0], \
.numOfSlotConfigs = \
LISTIFY(PSI5_CHANNEL_CH_SFCR_COUNT, PSI5_NXP_S32_SLOT_CNT, (+), node_id), \
.watermarkInterruptLevel = GENMASK(UTIL_DEC(DT_PROP(node_id, num_rx_buf)), 0), \
};
#define PSI5_NXP_S32_CHANNEL_TX_CONFIG(node_id) \
const Psi5_Ip_ChannelTxConfigType _CONCAT(psi5_nxp_s32_channel_tx_config, node_id) = { \
.targetPulse = DT_PROP_OR(node_id, period_sync_pulse_us, 0), \
.decoderOffset = DT_PROP_OR(node_id, decoder_start_offset_us, 0), \
.pulse0Width = DT_PROP_OR(node_id, pulse_width_0_us, 0), \
.pulse1Width = DT_PROP_OR(node_id, pulse_width_1_us, 0), \
.txMode = DT_ENUM_IDX_OR(node_id, tx_frame, 0), \
.syncState = PSI5_SYNC_STATE_2, \
.txSize = 64, /* This setting is applicable only in NON STANDARD FRAME */ \
};
#define PSI5_NXP_S32_CHANNEL_ERR_SEL_CONFIG(node_id) \
const Psi5_Ip_ErrorSelectConfigType _CONCAT(psi5_nxp_s32_channel_err_sel_config, \
node_id) = { \
.errorSelect0 = true, \
.errorSelect1 = true, \
.errorSelect2 = true, \
.errorSelect3 = true, \
.errorSelect4 = true, \
};
/*
* The macro get index of array configuration that corresponds to each the ID of HW channel.
* Assign 0xff to unused channels.
*/
#define PSI5_NXP_S32_CHANNEL_NODE(n, i) DT_INST_CHILD(n, DT_CAT(ch_, i))
#define PSI5_NXP_S32_ID_CFG_CNT(i, node_id, n) \
(DT_NODE_HAS_STATUS(PSI5_NXP_S32_CHANNEL_NODE(n, i), okay) && \
(DT_REG_ADDR(PSI5_NXP_S32_CHANNEL_NODE(n, i)) < (DT_REG_ADDR(node_id))) \
? 1 \
: 0)
#define PSI5_NXP_S32_ID_CFG(node_id, n) \
COND_CODE_1(DT_NODE_HAS_STATUS(node_id, okay), \
(LISTIFY(PSI5_CHANNEL_COUNT, PSI5_NXP_S32_ID_CFG_CNT, (+), node_id, n),), (0xff,))
#define PSI5_NXP_S32_CHANNEL_CONFIG(node_id) \
{ \
.channelId = DT_REG_ADDR(node_id), \
.channelMode = !DT_PROP(node_id, async_mode), \
.callback = _CONCAT(psi5_nxp_s32_channel_callBack, node_id), \
.rxConfig = &_CONCAT(psi5_nxp_s32_channel_rx_config, node_id), \
.txConfig = &_CONCAT(psi5_nxp_s32_channel_tx_config, node_id), \
.errorSelectConfig = &_CONCAT(psi5_nxp_s32_channel_err_sel_config, node_id), \
},
/* Define array channel configuration */
#define PSI5_NXP_S32_ARRAY_CHANNEL_CONFIG(n) \
DT_INST_FOREACH_CHILD_STATUS_OKAY(n, PSI5_NXP_S32_CHANNEL_CALLBACK) \
DT_INST_FOREACH_CHILD_STATUS_OKAY(n, PSI5_NXP_S32_CHANNEL_RX_SLOT_CONFIG) \
DT_INST_FOREACH_CHILD_STATUS_OKAY(n, PSI5_NXP_S32_CHANNEL_RX_CONFIG) \
DT_INST_FOREACH_CHILD_STATUS_OKAY(n, PSI5_NXP_S32_CHANNEL_TX_CONFIG) \
DT_INST_FOREACH_CHILD_STATUS_OKAY(n, PSI5_NXP_S32_CHANNEL_ERR_SEL_CONFIG) \
const Psi5_Ip_ChannelConfigType \
psi5_nxp_s32_channel_array_config_##n[DT_INST_CHILD_NUM_STATUS_OKAY(n)] = { \
DT_INST_FOREACH_CHILD_STATUS_OKAY(n, PSI5_NXP_S32_CHANNEL_CONFIG)}; \
const uint8_t psi5_nxp_s32_map_idex_array_config_##n[PSI5_CHANNEL_COUNT] = { \
DT_INST_FOREACH_CHILD_VARGS(n, PSI5_NXP_S32_ID_CFG, n)};
DT_INST_FOREACH_STATUS_OKAY(PSI5_NXP_S32_ARRAY_CHANNEL_CONFIG)
/* Define array instance configuration */
#define PSI5_NXP_S32_INST_CONFIG(n) \
{ \
.instanceId = PSI5_NXP_S32_HW_INSTANCE(n), \
.channelConfig = &psi5_nxp_s32_channel_array_config_##n[0], \
.numOfChannels = DT_INST_CHILD_NUM_STATUS_OKAY(n), \
.chHwIdToIndexArrayConfig = &psi5_nxp_s32_map_idex_array_config_##n[0], \
},
static const Psi5_Ip_InstanceType psi5_nxp_s32_array_inst_config[DT_NUM_INST_STATUS_OKAY(
DT_DRV_COMPAT)] = {DT_INST_FOREACH_STATUS_OKAY(PSI5_NXP_S32_INST_CONFIG)};
/* The structure configuration for all controller instances that used for Psi5_Ip_Init() */
static const Psi5_Ip_ConfigType psi5_nxp_s32_controller_config = {
.instancesConfig = &psi5_nxp_s32_array_inst_config[0],
.numOfInstances = DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT),
};
#define PSI5_NXP_S32_CHANNEL_ISR(node_id) \
static void _CONCAT(psi5_nxp_s32_channel_isr, node_id)(const struct device *dev) \
{ \
const struct psi5_nxp_s32_config *config = dev->config; \
\
Psi5_Ip_IRQ_Handler(config->ctrl_inst, DT_REG_ADDR(node_id)); \
}
#define PSI5_NXP_S32_CHANNEL_IRQ_CONFIG(node_id, n) \
do { \
IRQ_CONNECT(DT_IRQ_BY_IDX(node_id, 0, irq), DT_IRQ_BY_IDX(node_id, 0, priority), \
_CONCAT(psi5_nxp_s32_channel_isr, node_id), DEVICE_DT_INST_GET(n), \
DT_IRQ_BY_IDX(node_id, 0, flags)); \
irq_enable(DT_IRQN(node_id)); \
} while (false);
#define PSI5_NXP_S32_IRQ_CONFIG(n) \
DT_INST_FOREACH_CHILD_STATUS_OKAY(n, PSI5_NXP_S32_CHANNEL_ISR) \
static void psi5_irq_config_##n(void) \
{ \
DT_INST_FOREACH_CHILD_STATUS_OKAY_VARGS(n, PSI5_NXP_S32_CHANNEL_IRQ_CONFIG, n) \
}
#define PSI5_NXP_S32_CHANNEL_BIT_MASK(node_id) BIT(DT_REG_ADDR(node_id))
#define PSI5_NXP_S32_CHANNEL_ASYNC_MODE(node_id) \
data->channel_data[DT_REG_ADDR(node_id)].async_mode = DT_PROP(node_id, async_mode);
#define DEV_PSI5_NXP_S32_INIT(n) \
PINCTRL_DT_INST_DEFINE(n); \
PSI5_NXP_S32_IRQ_CONFIG(n) \
static struct psi5_nxp_s32_config psi5_nxp_s32_config_##n = { \
.ctrl_inst = PSI5_NXP_S32_HW_INSTANCE(n), \
.base = (PSI5_Type *)DT_INST_REG_ADDR(n), \
.channel_mask = DT_INST_FOREACH_CHILD_STATUS_OKAY_SEP( \
n, PSI5_NXP_S32_CHANNEL_BIT_MASK, (|)), \
.pin_cfg = PINCTRL_DT_INST_DEV_CONFIG_GET(n), \
.irq_config_func = psi5_irq_config_##n, \
}; \
static struct psi5_nxp_s32_data psi5_nxp_s32_data_##n; \
static int psi5_nxp_s32_init_##n(const struct device *dev) \
{ \
const struct psi5_nxp_s32_config *config = dev->config; \
struct psi5_nxp_s32_data *data = dev->data; \
int err = 0; \
\
err = pinctrl_apply_state(config->pin_cfg, PINCTRL_STATE_DEFAULT); \
if (err < 0) { \
LOG_ERR("PSI5 pinctrl setup failed (%d)", err); \
return err; \
} \
\
for (int ch = 0; ch < PSI5_CHANNEL_COUNT; ch++) { \
if (config->channel_mask & BIT(ch)) { \
k_sem_init(&data->channel_data[ch].tx_sem, 1, 1); \
k_mutex_init(&data->channel_data[ch].lock); \
} \
} \
\
DT_INST_FOREACH_CHILD_STATUS_OKAY(n, PSI5_NXP_S32_CHANNEL_ASYNC_MODE) \
\
/* Common configuration setup for all controller instances */ \
if (n == UTIL_DEC(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT))) { \
Psi5_Ip_Init(&psi5_nxp_s32_controller_config); \
} \
\
config->irq_config_func(); \
\
return 0; \
} \
DEVICE_DT_INST_DEFINE(n, psi5_nxp_s32_init_##n, NULL, &psi5_nxp_s32_data_##n, \
&psi5_nxp_s32_config_##n, POST_KERNEL, CONFIG_PSI5_INIT_PRIORITY, \
&psi5_nxp_s32_driver_api);
DT_INST_FOREACH_STATUS_OKAY(DEV_PSI5_NXP_S32_INIT)

32
dts/bindings/psi5/nxp,s32-psi5.yaml

@ -0,0 +1,32 @@ @@ -0,0 +1,32 @@
# Copyright 2025 NXP
#
# SPDX-License-Identifier: Apache-2.0
description: NXP S32 PSI5 (Peripheral Sensor Interface) Controller
compatible: "nxp,s32-psi5"
include: [psi5-controller.yaml, pinctrl-device.yaml]
properties:
pinctrl-0:
required: true
pinctrl-names:
required: true
child-binding:
properties:
interrupts:
type: array
required: true
description: Information about the channel interrupts.
num-rx-buf:
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.

141
dts/bindings/psi5/psi5-controller.yaml

@ -0,0 +1,141 @@ @@ -0,0 +1,141 @@
# Copyright 2025 NXP
#
# SPDX-License-Identifier: Apache-2.0
description: PSI5 (Peripheral Sensor Interface) Controller
include: base.yaml
properties:
reg:
required: true
child-binding:
description: |
Each child node defines the configuration of a PSI5 channel
properties:
reg:
type: int
required: true
description: Channel identifier.
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.
period-sync-pulse-us:
type: int
description: |
Specifies the period of the internally generated synchronization pulses, measured in
microseconds (us). This value determines the length of each synchronization pulse used
in the system.
pulse-width-0-us:
type: int
description: |
Specifies the duration of the pulse width for a data bit value '0', measured in
microseconds (us).
pulse-width-1-us:
type: int
description: |
Specifies the duration of the pulse width for a data bit value '1', measured in
microseconds (us).
decoder-start-offset-us:
type: int
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.
tx-frame:
type: string
enum:
- "short-31-1s"
- "short-5-0s"
- "long-31-1s"
- "long-5-0s"
- "x-long-31-1s"
- "x-long-frame-5-0s"
- "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-31-1s: Short Frame (V1.3) with at least 31 consecutive '1' bits
as the start condition
- short-5-0s: Short Frame (V1.3) with at least 5 consecutive '0' bits
as the start condition
- long-31-1s: Long Frame (V1.3) with at least 31 consecutive '1' bits
as the start condition
- long-5-0s: Long Frame (V1.3) with at least 5 consecutive '0' bits
as the start condition
- x-long-31-1s: X-Long Frame (V1.3) with at least 31 consecutive '1' bits
as the start condition
- x-long-5-0s: X-Long Frame (V1.3) with at least 5 consecutive '0' bits
as the start condition
- xx-long: XX-Long (V2.0)
- non-standard: Non Standard Length
rx-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.
child-binding:
description: |
Each child node defines the configuration of a channel RX slot
properties:
reg:
type: int
required: true
description: Channel RX slot identifier.
duration-us:
type: int
required: true
description: |
Specifies the duration of a slot, starting from the rising edge of the
timing synchronization pulse and ending at the final slot.
start-offset-us:
type: int
required: true
description: |
Specifies the time offset at which the slot should start, measured from the
rising edge of the timing synchronization pulse.
data-length:
type: int
required: true
description: |
Specifies the number of bits in a slot, with valid lengths ranging from 8 to 28 bits.
data-msb-first:
type: boolean
description: |
Specifies the endianness type for data slot. Set to 1 when data is interpreted with the
Most Significant Bit (MSB) first.
has-smc:
type: boolean
description: |
Specifies whether data slot has Serial Messaging Channel (SMC) field. Set to 1 when
the bit (M0, M1) SMC is present in the Rx Message.
has-parity:
type: boolean
description: |
Specifies whether data slot has parity field. Set to 1 when the Parity field is
present in the Rx Message; otherwise, the CRC field is present.

278
include/zephyr/drivers/psi5/psi5.h

@ -0,0 +1,278 @@ @@ -0,0 +1,278 @@
/*
* Copyright 2025 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 <zephyr/kernel.h>
#include <zephyr/device.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief PSI5 Interface
* @defgroup psi5_interface PSI5 Interface
* @since 4.2
* @version 0.1.0
* @ingroup io_interfaces
* @{
*/
/**
* @brief PSI5 frame type
*/
enum psi5_frame_type {
/** Serial message frame with 4-bit message ID */
PSI5_SERIAL_FRAME_4_BIT_ID,
/** Serial message frame with 8-bit message ID */
PSI5_SERIAL_FRAME_8_BIT_ID,
/** Data frame */
PSI5_DATA_FRAME
};
/**
* @brief PSI5 frame structure
*/
struct psi5_frame {
/** Type of PSI5 frame */
enum psi5_frame_type type;
union {
/** Message data */
uint32_t data;
/**
* @brief Serial message
*/
struct {
/** Serial message ID */
uint8_t id;
/** Serial message data */
uint16_t data;
} serial;
};
/** Timestamp of when the frame was captured */
uint32_t timestamp;
/** CRC checksum for message integrity validation */
uint8_t crc;
/** Slot Number */
uint8_t slot_number;
};
/**
* @brief Defines the application callback handler function signature for sending.
*
* @param dev Pointer to the device structure for the driver instance.
* @param channel The hardware channel of the driver instance.
* @param status PSI5 status (0: transmission completed successfully,
* -EIO: transmission error occurred).
* @param user_data User data provided when the frame was sent.
*/
typedef void (*psi5_tx_callback_t)(const struct device *dev, uint8_t channel, int status,
void *user_data);
/**
* @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 (*psi5_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 sync PSI5
* See @a psi5_start_sync() for argument description
*/
typedef int (*psi5_start_sync_t)(const struct device *dev, uint8_t channel);
/**
* @brief Callback API upon stopping sync PSI5
* See @a psi5_stop_sync() for argument description
*/
typedef int (*psi5_stop_sync_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 Configuration structure for RX callback
*/
struct psi5_rx_callback_config {
/** Callback function invoked on frame reception */
psi5_rx_frame_callback_t callback;
/** Pointer to the buffer for storing received frames */
struct psi5_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 psi5_rx_callback_configs {
/** Configuration for the serial message callback */
struct psi5_rx_callback_config *serial_frame;
/** Configuration for the data message callback */
struct psi5_rx_callback_config *data_frame;
};
/**
* @brief Callback API upon adding RX callback
* See @a psi5_register_callback() for argument description
*/
typedef int (*psi5_register_callback_t)(const struct device *dev, uint8_t channel,
struct psi5_rx_callback_configs callback_configs);
__subsystem struct psi5_driver_api {
psi5_start_sync_t start_sync;
psi5_stop_sync_t stop_sync;
psi5_send_t send;
psi5_register_callback_t register_callback;
};
/** @endcond */
/**
* @brief Start the sync pulse generator on a specific channel
*
* @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 started.
* @retval -EIO general input/output error, failed to start device.
*/
__syscall int psi5_start_sync(const struct device *dev, uint8_t channel);
static inline int z_impl_psi5_start_sync(const struct device *dev, uint8_t channel)
{
const struct psi5_driver_api *api = (const struct psi5_driver_api *)dev->api;
if (api->start_sync) {
return api->start_sync(dev, channel);
}
return -ENOSYS;
}
/**
* @brief Stop the sync pulse generator on a specific channel
*
* @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 started.
* @retval -EIO general input/output error, failed to stop device.
*/
__syscall int psi5_stop_sync(const struct device *dev, uint8_t channel);
static inline int z_impl_psi5_stop_sync(const struct device *dev, uint8_t channel)
{
const struct psi5_driver_api *api = (const struct psi5_driver_api *)dev->api;
if (api->stop_sync) {
return api->stop_sync(dev, channel);
}
return -ENOSYS;
}
/**
* @brief Transmitting PSI5 data on a specific channel
*
* The channel must be configured to synchronous mode and can only begin transmission after
* the sync pulse generator has started.
*
* @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 successful.
* @retval -EINVAL invalid channel.
* @retval -ENOTSUP unsupported parameter was passed to the function.
* @retval -ENETDOWN stopped state.
* @retval -EIO general transmit error occurred.
* @retval -EAGAIN 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;
}
/**
* @brief Add a callback function to handle messages received for a specific channel
*
* The callback must be registered before the sync pulse generator started when the channel
* is configured to synchronous mode.
*
* @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 psi5_register_callback(const struct device *dev, uint8_t channel,
struct psi5_rx_callback_configs callback_configs);
static inline int z_impl_psi5_register_callback(const struct device *dev, uint8_t channel,
struct psi5_rx_callback_configs callback_configs)
{
const struct psi5_driver_api *api = (const struct psi5_driver_api *)dev->api;
if (api->register_callback) {
return api->register_callback(dev, channel, callback_configs);
}
return -ENOSYS;
}
#ifdef __cplusplus
}
#endif
/**
* @}
*/
#include <zephyr/syscalls/psi5.h>
#endif /* ZEPHYR_INCLUDE_DRIVERS_PSI5_H_ */

2
west.yml

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

Loading…
Cancel
Save