feat(ti): add support for TI mailbox driver
New devices like the AM62L will use a mailbox to communicate with the
security firmware.
Change-Id: I33080d443d73d4aff685ada5b40c067a3ff6a137
Signed-off-by: Dhruva Gole <d-gole@ti.com>
diff --git a/drivers/ti/ipc/mailbox.c b/drivers/ti/ipc/mailbox.c
new file mode 100644
index 0000000..65458a5
--- /dev/null
+++ b/drivers/ti/ipc/mailbox.c
@@ -0,0 +1,145 @@
+/*
+ * Texas Instruments Mailbox Driver
+ *
+ * Copyright (C) 2024-2025 Texas Instruments Incorporated - https://www.ti.com/
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include <arch_helpers.h>
+#include <common/debug.h>
+#include <drivers/delay_timer.h>
+#include <lib/mmio.h>
+#include <lib/utils.h>
+#include <lib/utils_def.h>
+#include <ti_sci_transport.h>
+
+#include <platform_def.h>
+
+/*
+ * TI_MAILBOX_RX/TX_BASE and the MAILBOX_MAX_MESSAGE_SIZE values are expected
+ * to come from platform specific header file ie. platform_def.h
+ */
+
+#define TI_MAILBOX_SYSC UL(0x10)
+#define TI_MAILBOX_MSG UL(0x40)
+#define TI_MAILBOX_FIFO_STATUS UL(0x80)
+#define TI_MAILBOX_MSG_STATUS UL(0xc0)
+
+/*
+ * Function to poll for mailbox rx messages
+ * IRQ model is currently not in scope of this driver
+ */
+static int8_t ti_mailbox_poll_rx_status(void)
+{
+ uint32_t num_messages_pending = 0U;
+ uint32_t retry_count = 100U;
+
+ /*
+ * Keep polling till we get a message for 100 times
+ * with intervals of 10 milliseconds.
+ */
+ while (num_messages_pending == 0U) {
+ num_messages_pending = mmio_read_32(TI_MAILBOX_RX_BASE + TI_MAILBOX_MSG_STATUS);
+ if (retry_count-- == 0U) {
+ return -ETIMEDOUT;
+ }
+ mdelay(10);
+ }
+ return 0;
+}
+
+int ti_sci_transport_clear_rx_thread(enum ti_sci_transport_chan_id id)
+{
+ /* MSG_STATUS tells us how many pending messages */
+ uint32_t try_count = mmio_read_32(TI_MAILBOX_RX_BASE + TI_MAILBOX_MSG_STATUS);
+
+ /* Run the loop till the status register is cleared */
+ while (mmio_read_32(TI_MAILBOX_RX_BASE + TI_MAILBOX_MSG_STATUS) != 0U) {
+ WARN("Clearing message from mailbox FIFO\n");
+ /* The act of reading the mailbox msg itself clears it */
+ mmio_read_32(TI_MAILBOX_RX_BASE + TI_MAILBOX_MSG);
+ /*
+ * The try_count is kept independent of the value of the status register
+ * because if at any point a new mailbox message arrives while this loop
+ * is in progress, we would want to know that message arrived and not clear
+ * it. We would rather print the error than clear the message thus indicating
+ * that the system is probably in a bad/async state.
+ */
+ if (!(try_count--)) {
+ ERROR("Could not clear all messages from mailbox FIFO\n");
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+int ti_sci_transport_send(enum ti_sci_transport_chan_id id, const struct ti_sci_msg *msg)
+{
+ uint32_t num_bytes;
+ void *dst_ptr = (void *)MAILBOX_TX_START_REGION;
+
+ assert(msg != NULL);
+
+ num_bytes = msg->len;
+
+ /*
+ * Only a simple check because even if there's 1 pending message
+ * we will be in a bad state if we try to send another message
+ * due to the absence of any interrupt or buffer mgmt model.
+ */
+ if (mmio_read_32(TI_MAILBOX_TX_BASE + TI_MAILBOX_FIFO_STATUS)) {
+ ERROR("Mailbox FIFO has pending messages!\n");
+ return -EINVAL;
+ }
+
+ if (num_bytes > MAILBOX_MAX_MESSAGE_SIZE) {
+ ERROR("message length %lu > max msg size\n", msg->len);
+ return -EINVAL;
+ }
+
+ /*
+ * Move the buffer contents into the SRAM to be accessed by TIFS
+ */
+ memmove(dst_ptr, msg->buf, num_bytes);
+
+ mmio_write_32(TI_MAILBOX_TX_BASE + TI_MAILBOX_MSG, (uint64_t)(void *)dst_ptr);
+
+ return 0;
+}
+
+int ti_sci_transport_recv(enum ti_sci_transport_chan_id id, struct ti_sci_msg *msg)
+{
+ uint32_t num_bytes;
+ uint64_t rcv_addr;
+
+ assert(msg != NULL);
+
+ num_bytes = msg->len;
+
+ if (ti_mailbox_poll_rx_status() == -ETIMEDOUT) {
+ ERROR("Timeout waiting for receive\n");
+ return -ETIMEDOUT;
+ }
+
+ rcv_addr = mmio_read_32(TI_MAILBOX_RX_BASE + TI_MAILBOX_MSG);
+
+ if (rcv_addr != MAILBOX_RX_START_REGION) {
+ ERROR("message address %lu is not valid\n", rcv_addr);
+ return -EINVAL;
+ }
+
+ if (num_bytes > MAILBOX_MAX_MESSAGE_SIZE) {
+ ERROR("message length %lu > max msg size\n", msg->len);
+ return -EINVAL;
+ }
+
+ memmove(msg->buf, (uint8_t *)(rcv_addr), num_bytes);
+
+ return 0;
+}