Browse Source
This driver allows to communication (send, receive) with PSI5 device Signed-off-by: Cong Nguyen Huu <cong.nguyenhuu@nxp.com>pull/88157/merge
13 changed files with 1056 additions and 1 deletions
@ -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 |
@ -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) |
@ -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 |
@ -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. |
@ -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) |
@ -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. |
@ -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. |
@ -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_ */ |
Loading…
Reference in new issue