From 963fda5e4a5c380e19eca6fd2760f142cc122b5e Mon Sep 17 00:00:00 2001 From: Dave Joseph Date: Fri, 9 May 2025 09:55:09 +0530 Subject: [PATCH] drivers: mbox: TI Secure Proxy mbox drivers Added secure proxy mailbox driver for supported devices using the binding ti,secure-proxy. This is used to communicate with a device manager running on a separate core via a secure proxy mailbox for TI K3 devices. Required for enabling TISCI layer communication. Refer: https://software-dl.ti.com/tisci/esd/latest/1_intro/TISCI.html Tested on MAIN_CORTEX_R5_0 on AM243x EVM. Refer:https://www.ti.com/lit/pdf/spruim2 Co-developed-by: Vaishnav Achath Signed-off-by: Dave Joseph --- drivers/mbox/CMakeLists.txt | 1 + drivers/mbox/Kconfig | 2 +- drivers/mbox/Kconfig.ti_secproxy | 15 + drivers/mbox/mbox_ti_secproxy.c | 388 +++++++++++++++++++++++++ dts/bindings/mbox/ti,secure-proxy.yaml | 18 ++ 5 files changed, 423 insertions(+), 1 deletion(-) create mode 100644 drivers/mbox/Kconfig.ti_secproxy create mode 100644 drivers/mbox/mbox_ti_secproxy.c create mode 100644 dts/bindings/mbox/ti,secure-proxy.yaml diff --git a/drivers/mbox/CMakeLists.txt b/drivers/mbox/CMakeLists.txt index 5e4f61b9d03..6a45598684f 100644 --- a/drivers/mbox/CMakeLists.txt +++ b/drivers/mbox/CMakeLists.txt @@ -22,3 +22,4 @@ zephyr_library_sources_ifdef(CONFIG_MBOX_IVSHMEM mbox_ivshmem.c) zephyr_library_sources_ifdef(CONFIG_MBOX_TI_OMAP_MAILBOX mbox_ti_omap.c) zephyr_library_sources_ifdef(CONFIG_MBOX_RENESAS_RZ_MHU mbox_renesas_rz_mhu.c) zephyr_library_sources_ifdef(CONFIG_MBOX_MHUV3 mbox_mhuv3.c) +zephyr_library_sources_ifdef(CONFIG_MBOX_TI_SECURE_PROXY mbox_ti_secproxy.c) diff --git a/drivers/mbox/Kconfig b/drivers/mbox/Kconfig index fe456b0b89e..8649df68081 100644 --- a/drivers/mbox/Kconfig +++ b/drivers/mbox/Kconfig @@ -26,7 +26,7 @@ source "drivers/mbox/Kconfig.ivshmem" source "drivers/mbox/Kconfig.ti_omap" source "drivers/mbox/Kconfig.renesas_rz" source "drivers/mbox/Kconfig.mhuv3" - +source "drivers/mbox/Kconfig.ti_secproxy" config MBOX_INIT_PRIORITY int "MBOX init priority" diff --git a/drivers/mbox/Kconfig.ti_secproxy b/drivers/mbox/Kconfig.ti_secproxy new file mode 100644 index 00000000000..0f081fceea3 --- /dev/null +++ b/drivers/mbox/Kconfig.ti_secproxy @@ -0,0 +1,15 @@ +# Copyright 2025 Texas Instruments Incorporated. +# SPDX-License-Identifier: Apache-2.0 + +config MBOX_TI_SECURE_PROXY + bool "TI Secureproxy Mailbox driver" + default y + depends on DT_HAS_TI_SECURE_PROXY_ENABLED + help + Driver for TI Secure Proxy Mailbox. + +config MBOX_TI_SECURE_PROXY_PRIORITY + int "MBOX_TI_SECURE_PROXY_PRIORITY" + default KERNEL_INIT_PRIORITY_OBJECTS + help + Mbox secproxy initialization priority. diff --git a/drivers/mbox/mbox_ti_secproxy.c b/drivers/mbox/mbox_ti_secproxy.c new file mode 100644 index 00000000000..f6c8a451433 --- /dev/null +++ b/drivers/mbox/mbox_ti_secproxy.c @@ -0,0 +1,388 @@ +/* + * Copyright (c) 2025 Texas Instruments Incorporated. + * + * TI Secureproxy Mailbox driver for Zephyr's MBOX model. + */ + +#include +#include +#include +#include +#include +#include +#define LOG_LEVEL CONFIG_MBOX_LOG_LEVEL +#include +LOG_MODULE_REGISTER(ti_secure_proxy); + +#define DT_DRV_COMPAT ti_secure_proxy + +#define DEV_CFG(dev) ((const struct secproxy_mailbox_config *)((dev)->config)) +#define DEV_DATA(dev) ((struct secproxy_mailbox_data *)(dev)->data) + +#define DEV_TDATA(_dev) DEVICE_MMIO_NAMED_GET(_dev, target_data) +#define DEV_RT(_dev) DEVICE_MMIO_NAMED_GET(_dev, rt) +#define DEV_SCFG(_dev) DEVICE_MMIO_NAMED_GET(_dev, scfg) + +#define RT_THREAD_STATUS 0x0 +#define RT_THREAD_THRESHOLD 0x4 +#define RT_THREAD_STATUS_ERROR_SHIFT 31 +#define RT_THREAD_STATUS_ERROR_MASK BIT(31) +#define RT_THREAD_STATUS_CUR_CNT_SHIFT 0 +#define RT_THREAD_STATUS_CUR_CNT_MASK GENMASK(7, 0) + +#define SCFG_THREAD_CTRL 0x1000 +#define SCFG_THREAD_CTRL_DIR_SHIFT 31 +#define SCFG_THREAD_CTRL_DIR_MASK BIT(31) + +#define SEC_PROXY_THREAD(base, x) ((base) + (0x1000 * (x))) +#define THREAD_IS_RX 1 +#define THREAD_IS_TX 0 + +#define SECPROXY_MAILBOX_NUM_MSGS 5 +#define MAILBOX_MAX_CHANNELS 32 +#define MAILBOX_MBOX_SIZE 60 + +#define SEC_PROXY_DATA_START_OFFS 0x4 +#define SEC_PROXY_DATA_END_OFFS 0x3c + +#define SEC_PROXY_TIMEOUT_US 1000000 +#define MAILBOX_MBOX_SIZE 60 + +#define GET_MSG_SEQ(buffer) ((uint32_t *)buffer)[1] +struct secproxy_thread { + mem_addr_t target_data; + mem_addr_t rt; + mem_addr_t scfg; +}; + +/** + * @struct rx_msg + * @brief Received message details + * @param seq: Message sequence number + * @param size: Message size in bytes + * @param buf: Buffer for message data + */ +struct rx_msg { + uint8_t seq; + size_t size; + void *buf; +}; + +struct secproxy_mailbox_data { + mbox_callback_t cb[MAILBOX_MAX_CHANNELS]; + void *user_data[MAILBOX_MAX_CHANNELS]; + bool channel_enable[MAILBOX_MAX_CHANNELS]; + + DEVICE_MMIO_NAMED_RAM(target_data); + DEVICE_MMIO_NAMED_RAM(rt); + DEVICE_MMIO_NAMED_RAM(scfg); + struct k_spinlock lock; +}; + +struct secproxy_mailbox_config { + DEVICE_MMIO_NAMED_ROM(target_data); + DEVICE_MMIO_NAMED_ROM(rt); + DEVICE_MMIO_NAMED_ROM(scfg); + uint32_t irq; +}; + +static inline int secproxy_verify_thread(struct secproxy_thread *spt, uint8_t dir) +{ + /* Check for any errors already available */ + if (sys_read32(spt->rt + RT_THREAD_STATUS) & RT_THREAD_STATUS_ERROR_MASK) { + LOG_ERR("Thread is corrupted, cannot send data.\n"); + return -EINVAL; + } + + /* Make sure thread is configured for right direction */ + if ((sys_read32(spt->scfg + SCFG_THREAD_CTRL) & SCFG_THREAD_CTRL_DIR_MASK) != + (dir << SCFG_THREAD_CTRL_DIR_SHIFT)) { + if (dir == THREAD_IS_TX) { + LOG_ERR("Trying to send data on RX Thread\n"); + } else { + LOG_ERR("Trying to receive data on TX Thread\n"); + } + return -EINVAL; + } + + /* Check the message queue before sending/receiving data */ + int timeout_ms = SEC_PROXY_TIMEOUT_US; + int waited_ms = 0; + const int poll_interval_ms = 1000; + + while (!(sys_read32(spt->rt + RT_THREAD_STATUS) & RT_THREAD_STATUS_CUR_CNT_MASK)) { + k_busy_wait(poll_interval_ms); + waited_ms += poll_interval_ms; + if (waited_ms >= timeout_ms) { + LOG_ERR("Timeout waiting for thread to %s\n", + (dir == THREAD_IS_TX) ? "empty" : "fill"); + return -ETIMEDOUT; + } + } + + return 0; +} + +static void secproxy_mailbox_isr(const struct device *dev) +{ + struct secproxy_mailbox_data *data = DEV_DATA(dev); + struct secproxy_thread spt; + uint32_t data_word; + mem_addr_t data_reg; + + for (int i_channel = 0; i_channel < MAILBOX_MAX_CHANNELS; i_channel++) { + if (!data->channel_enable[i_channel]) { + continue; + } + + spt.target_data = SEC_PROXY_THREAD(DEV_TDATA(dev), i_channel); + spt.rt = SEC_PROXY_THREAD(DEV_RT(dev), i_channel); + spt.scfg = SEC_PROXY_THREAD(DEV_SCFG(dev), i_channel); + + uint32_t status = sys_read32(spt.rt + RT_THREAD_STATUS); + + if (status & RT_THREAD_STATUS_ERROR_MASK) { + LOG_ERR("Thread %d error state detected in ISR", i_channel); + continue; + } + + if (secproxy_verify_thread(&spt, THREAD_IS_RX)) { + LOG_ERR("Thread %d is in error state\n", i_channel); + continue; + } + + if (!(sys_read32(spt.rt) & 0x7F)) { + continue; + } + + data_reg = spt.target_data + SEC_PROXY_DATA_START_OFFS; + size_t msg_len = MAILBOX_MBOX_SIZE; + size_t num_words = msg_len / sizeof(uint32_t); + size_t i; + struct rx_msg *rx_data = data->user_data[i_channel]; + + if (!rx_data || !rx_data->buf) { + LOG_ERR("No buffer provided for channel %d", i_channel); + continue; + } + + if (rx_data->size < MAILBOX_MBOX_SIZE) { + LOG_ERR("Buffer too small for channel %d", i_channel); + continue; + } + + uint8_t *buf = (uint8_t *)rx_data->buf; + + /* Copy full words */ + for (i = 0; i < num_words; i++) { + data_word = sys_read32(data_reg); + memcpy(&buf[i * 4], &data_word, sizeof(uint32_t)); + data_reg += sizeof(uint32_t); + } + + /* Handle trail bytes */ + size_t trail_bytes = msg_len % sizeof(uint32_t); + + if (trail_bytes) { + uint32_t data_trail = sys_read32(data_reg); + + i = msg_len - trail_bytes; + + while (trail_bytes--) { + buf[i++] = data_trail & 0xff; + data_trail >>= 8; + } + } + + /* Ensure we read the last register if we haven't already */ + if (data_reg <= (spt.target_data + SEC_PROXY_DATA_END_OFFS)) { + sys_read32(spt.target_data + SEC_PROXY_DATA_END_OFFS); + } + + rx_data->size = msg_len; + rx_data->seq = GET_MSG_SEQ(buf); + if (data->cb[i_channel]) { + data->cb[i_channel](dev, i_channel, NULL, NULL); + } + } +} + +static int secproxy_mailbox_send(const struct device *dev, uint32_t channel, + const struct mbox_msg *msg) +{ + struct secproxy_thread spt; + struct secproxy_mailbox_data *data = DEV_DATA(dev); + mem_addr_t data_reg; + k_spinlock_key_t key; + + if (!dev || !msg || !msg->data) { + LOG_ERR("Invalid parameters"); + return -EINVAL; + } + + if (msg->size == 0) { + LOG_ERR("Empty message not allowed"); + return -EINVAL; + } + + if (channel >= MAILBOX_MAX_CHANNELS) { + LOG_ERR("Channel %d exceeds max channels", channel); + return -EINVAL; + } + + key = k_spin_lock(&data->lock); + spt.target_data = SEC_PROXY_THREAD(DEV_TDATA(dev), channel); + spt.rt = SEC_PROXY_THREAD(DEV_RT(dev), channel); + spt.scfg = SEC_PROXY_THREAD(DEV_SCFG(dev), channel); + + if (secproxy_verify_thread(&spt, THREAD_IS_TX)) { + LOG_ERR("Thread is in error state\n"); + k_spin_unlock(&data->lock, key); + return -EBUSY; + } + + uint32_t status = sys_read32(spt.rt + RT_THREAD_STATUS); + + if ((status & RT_THREAD_STATUS_CUR_CNT_MASK) == (SECPROXY_MAILBOX_NUM_MSGS)) { + k_spin_unlock(&data->lock, key); + return -EBUSY; + } + + if (msg->size > MAILBOX_MBOX_SIZE) { + LOG_ERR("Message size %zu exceeds max size %d", msg->size, MAILBOX_MBOX_SIZE); + k_spin_unlock(&data->lock, key); + return -EMSGSIZE; + } + + /* Start writing from the data start offset */ + data_reg = spt.target_data + SEC_PROXY_DATA_START_OFFS; + + /* Handle full words */ + const uint8_t *byte_data = msg->data; + + for (int num_words = msg->size / sizeof(uint32_t); num_words > 0; num_words--) { + uint32_t word_val; + + memcpy(&word_val, byte_data, sizeof(uint32_t)); + sys_write32(word_val, data_reg); + data_reg += sizeof(uint32_t); + byte_data += sizeof(uint32_t); + } + + /* Handle trailing bytes */ + int trail_bytes = msg->size % sizeof(uint32_t); + + if (trail_bytes) { + uint32_t data_trail = 0; + + for (int i = 0; i < trail_bytes; i++) { + data_trail <<= 8; + data_trail |= byte_data[i]; + } + sys_write32(data_trail, data_reg); + data_reg += sizeof(uint32_t); + } + + /* Only write end marker if needed */ + if (data_reg <= (spt.target_data + SEC_PROXY_DATA_END_OFFS)) { + sys_write32(0, spt.target_data + SEC_PROXY_DATA_END_OFFS); + } + + k_spin_unlock(&data->lock, key); + return 0; +} + +static int secproxy_mailbox_register_callback(const struct device *dev, uint32_t channel, + mbox_callback_t cb, void *user_data) +{ + struct secproxy_mailbox_data *data = DEV_DATA(dev); + k_spinlock_key_t key; + + if (channel >= MAILBOX_MAX_CHANNELS) { + return -EINVAL; + } + + key = k_spin_lock(&data->lock); + data->cb[channel] = cb; + data->user_data[channel] = user_data; + k_spin_unlock(&data->lock, key); + + return 0; +} + +static int secproxy_mailbox_mtu_get(const struct device *dev) +{ + ARG_UNUSED(dev); + + return MAILBOX_MBOX_SIZE; +} + +static uint32_t secproxy_mailbox_max_channels_get(const struct device *dev) +{ + ARG_UNUSED(dev); + + return MAILBOX_MAX_CHANNELS; +} + +static int secproxy_mailbox_set_enabled(const struct device *dev, uint32_t channel, bool enable) +{ + const struct secproxy_mailbox_config *cfg = DEV_CFG(dev); + struct secproxy_mailbox_data *data = DEV_DATA(dev); + k_spinlock_key_t key; + + if (channel >= MAILBOX_MAX_CHANNELS) { + return -EINVAL; + } + + if (enable && data->channel_enable[channel]) { + return -EALREADY; + } + + key = k_spin_lock(&data->lock); + data->channel_enable[channel] = enable; + + if (enable) { + irq_enable(cfg->irq); + } else { + irq_disable(cfg->irq); + } + + k_spin_unlock(&data->lock, key); + + return 0; +} + +static DEVICE_API(mbox, secproxy_mailbox_driver_api) = { + .send = secproxy_mailbox_send, + .register_callback = secproxy_mailbox_register_callback, + .mtu_get = secproxy_mailbox_mtu_get, + .max_channels_get = secproxy_mailbox_max_channels_get, + .set_enabled = secproxy_mailbox_set_enabled, +}; + +#define MAILBOX_INSTANCE_DEFINE(idx) \ + static struct secproxy_mailbox_data secproxy_mailbox_##idx##_data; \ + const static struct secproxy_mailbox_config secproxy_mailbox_##idx##_config = { \ + DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(target_data, DT_DRV_INST(idx)), \ + DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(rt, DT_DRV_INST(idx)), \ + DEVICE_MMIO_NAMED_ROM_INIT_BY_NAME(scfg, DT_DRV_INST(idx)), \ + .irq = DT_INST_IRQN(idx), \ + }; \ + static int secproxy_mailbox_##idx##_init(const struct device *dev) \ + { \ + DEVICE_MMIO_NAMED_MAP(dev, target_data, K_MEM_CACHE_NONE); \ + DEVICE_MMIO_NAMED_MAP(dev, rt, K_MEM_CACHE_NONE); \ + DEVICE_MMIO_NAMED_MAP(dev, scfg, K_MEM_CACHE_NONE); \ + IRQ_CONNECT(DT_INST_IRQN(idx), DT_INST_IRQ(idx, priority), secproxy_mailbox_isr, \ + DEVICE_DT_INST_GET(idx), \ + COND_CODE_1(DT_INST_IRQ_HAS_CELL(idx, flags), \ + (DT_INST_IRQ(idx, flags)), (0))); \ + return 0; \ + } \ + DEVICE_DT_INST_DEFINE(idx, secproxy_mailbox_##idx##_init, NULL, \ + &secproxy_mailbox_##idx##_data, &secproxy_mailbox_##idx##_config, \ + PRE_KERNEL_1, CONFIG_MBOX_TI_SECURE_PROXY_PRIORITY, \ + &secproxy_mailbox_driver_api) + +#define MAILBOX_INST(idx) MAILBOX_INSTANCE_DEFINE(idx); +DT_INST_FOREACH_STATUS_OKAY(MAILBOX_INST) diff --git a/dts/bindings/mbox/ti,secure-proxy.yaml b/dts/bindings/mbox/ti,secure-proxy.yaml new file mode 100644 index 00000000000..807f78f7a2a --- /dev/null +++ b/dts/bindings/mbox/ti,secure-proxy.yaml @@ -0,0 +1,18 @@ +# Copyright 2025 Texas Instruments Incorporated. +# SPDX-License-Identifier: Apache-2.0 + +description: TI Secure Proxy MAILBOX + +compatible: "ti,secure-proxy" + +include: base.yaml + +properties: + reg: + required: true + + interrupts: + required: true + +mbox-cells: + - channel