--- /dev/null
+/*
+ * Texas Instruments K3 Secure Proxy Driver
+ * Based on Linux and U-Boot implementation
+ *
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <arch_helpers.h>
+#include <debug.h>
+#include <errno.h>
+#include <mmio.h>
+#include <platform_def.h>
+#include <stdlib.h>
+#include <utils.h>
+#include <utils_def.h>
+
+#include "sec_proxy.h"
+
+/* SEC PROXY RT THREAD STATUS */
+#define RT_THREAD_STATUS (0x0)
+#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)
+
+/* SEC PROXY SCFG THREAD CTRL */
+#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)
+
+/**
+ * struct k3_sec_proxy_desc - Description of secure proxy integration
+ * @timeout_us: Timeout for communication (in Microseconds)
+ * @max_msg_size: Message size in bytes
+ * @data_start_offset: Offset of the First data register of the thread
+ * @data_end_offset: Offset of the Last data register of the thread
+ */
+struct k3_sec_proxy_desc {
+ uint32_t timeout_us;
+ uint16_t max_msg_size;
+ uint16_t data_start_offset;
+ uint16_t data_end_offset;
+};
+
+/**
+ * struct k3_sec_proxy_thread - Description of a secure proxy Thread
+ * @id: Thread ID
+ * @data: Thread Data path region for target
+ * @scfg: Secure Config Region for Thread
+ * @rt: RealTime Region for Thread
+ */
+struct k3_sec_proxy_thread {
+ uint32_t id;
+ uintptr_t data;
+ uintptr_t scfg;
+ uintptr_t rt;
+};
+
+/**
+ * struct k3_sec_proxy_mbox - Description of a Secure Proxy Instance
+ * @desc: Description of the SoC integration
+ * @chans: Array for valid thread instances
+ */
+struct k3_sec_proxy_mbox {
+ const struct k3_sec_proxy_desc desc;
+ struct k3_sec_proxy_thread threads[];
+};
+
+/*
+ * Thread ID #0: DMSC notify
+ * Thread ID #1: DMSC request response
+ * Thread ID #2: DMSC request high priority
+ * Thread ID #3: DMSC request low priority
+ * Thread ID #4: DMSC notify response
+ */
+#define SP_THREAD(_x) \
+ [_x] = { \
+ .id = _x, \
+ .data = SEC_PROXY_THREAD(SEC_PROXY_DATA_BASE, _x), \
+ .scfg = SEC_PROXY_THREAD(SEC_PROXY_SCFG_BASE, _x), \
+ .rt = SEC_PROXY_THREAD(SEC_PROXY_RT_BASE, _x), \
+ }
+
+static struct k3_sec_proxy_mbox spm = {
+ .desc = {
+ .timeout_us = SEC_PROXY_TIMEOUT_US,
+ .max_msg_size = SEC_PROXY_MAX_MESSAGE_SIZE,
+ .data_start_offset = 0x4,
+ .data_end_offset = 0x3C,
+ },
+ .threads = {
+ SP_THREAD(SP_NOTIFY),
+ SP_THREAD(SP_RESPONSE),
+ SP_THREAD(SP_HIGH_PRIORITY),
+ SP_THREAD(SP_LOW_PRIORITY),
+ SP_THREAD(SP_NOTIFY_RESP),
+ },
+};
+
+/**
+ * struct sec_msg_hdr - Message header for secure messages and responses
+ * @checksum: CRC of message for integrity checking
+ */
+union sec_msg_hdr {
+ struct {
+ uint16_t checksum;
+ uint16_t reserved;
+ } __packed;
+ uint32_t data;
+};
+
+/**
+ * k3_sec_proxy_verify_thread() - Verify thread status before
+ * sending/receiving data
+ * @spt: Pointer to Secure Proxy thread description
+ * @dir: Direction of the thread
+ *
+ * Return: 0 if all goes well, else appropriate error message
+ */
+static inline int k3_sec_proxy_verify_thread(struct k3_sec_proxy_thread *spt,
+ uint32_t dir)
+{
+ /* Check for any errors already available */
+ if (mmio_read_32(spt->rt + RT_THREAD_STATUS) &
+ RT_THREAD_STATUS_ERROR_MASK) {
+ ERROR("Thread %d is corrupted, cannot send data\n", spt->id);
+ return -EINVAL;
+ }
+
+ /* Make sure thread is configured for right direction */
+ if ((mmio_read_32(spt->scfg + SCFG_THREAD_CTRL) & SCFG_THREAD_CTRL_DIR_MASK)
+ != (dir << SCFG_THREAD_CTRL_DIR_SHIFT)) {
+ if (dir)
+ ERROR("Trying to receive data on tx Thread %d\n",
+ spt->id);
+ else
+ ERROR("Trying to send data on rx Thread %d\n",
+ spt->id);
+ return -EINVAL;
+ }
+
+ /* Check the message queue before sending/receiving data */
+ uint32_t tick_start = (uint32_t)read_cntpct_el0();
+ uint32_t ticks_per_us = SYS_COUNTER_FREQ_IN_TICKS / 1000000;
+ while (!(mmio_read_32(spt->rt + RT_THREAD_STATUS) & RT_THREAD_STATUS_CUR_CNT_MASK)) {
+ VERBOSE("Waiting for thread %d to clear\n", spt->id);
+ if (((uint32_t)read_cntpct_el0() - tick_start) >
+ (spm.desc.timeout_us * ticks_per_us)) {
+ ERROR("Timeout waiting for thread %d to clear\n", spt->id);
+ return -ETIMEDOUT;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * k3_sec_proxy_send() - Send data over a Secure Proxy thread
+ * @id: Channel Identifier
+ * @msg: Pointer to k3_sec_proxy_msg
+ *
+ * Return: 0 if all goes well, else appropriate error message
+ */
+int k3_sec_proxy_send(enum k3_sec_proxy_chan_id id, const struct k3_sec_proxy_msg *msg)
+{
+ struct k3_sec_proxy_thread *spt = &spm.threads[id];
+ union sec_msg_hdr secure_header;
+ int num_words, trail_bytes, i, ret;
+ uintptr_t data_reg;
+
+ ret = k3_sec_proxy_verify_thread(spt, THREAD_IS_TX);
+ if (ret) {
+ ERROR("Thread %d verification failed (%d)\n", spt->id, ret);
+ return ret;
+ }
+
+ /* Check the message size */
+ if (msg->len + sizeof(secure_header) > spm.desc.max_msg_size) {
+ ERROR("Thread %d message length %lu > max msg size\n",
+ spt->id, msg->len);
+ return -EINVAL;
+ }
+
+ /* TODO: Calculate checksum */
+ secure_header.checksum = 0;
+
+ /* Send the secure header */
+ data_reg = spm.desc.data_start_offset;
+ mmio_write_32(spt->data + data_reg, secure_header.data);
+ data_reg += sizeof(uint32_t);
+
+ /* Send whole words */
+ num_words = msg->len / sizeof(uint32_t);
+ for (i = 0; i < num_words; i++) {
+ mmio_write_32(spt->data + data_reg, ((uint32_t *)msg->buf)[i]);
+ data_reg += sizeof(uint32_t);
+ }
+
+ /* Send remaining bytes */
+ trail_bytes = msg->len % sizeof(uint32_t);
+ if (trail_bytes) {
+ uint32_t data_trail = 0;
+
+ i = msg->len - trail_bytes;
+ while (trail_bytes--) {
+ data_trail <<= 8;
+ data_trail |= msg->buf[i++];
+ }
+
+ mmio_write_32(spt->data + data_reg, data_trail);
+ data_reg += sizeof(uint32_t);
+ }
+ /*
+ * 'data_reg' indicates next register to write. If we did not already
+ * write on tx complete reg(last reg), we must do so for transmit
+ */
+ if (data_reg <= spm.desc.data_end_offset)
+ mmio_write_32(spt->data + spm.desc.data_end_offset, 0);
+
+ VERBOSE("Message successfully sent on thread %ud\n", id);
+
+ return 0;
+}
+
+/**
+ * k3_sec_proxy_recv() - Receive data from a Secure Proxy thread
+ * @id: Channel Identifier
+ * @msg: Pointer to k3_sec_proxy_msg
+ *
+ * Return: 0 if all goes well, else appropriate error message
+ */
+int k3_sec_proxy_recv(uint32_t id, struct k3_sec_proxy_msg *msg)
+{
+ struct k3_sec_proxy_thread *spt = &spm.threads[id];
+ union sec_msg_hdr secure_header;
+ uintptr_t data_reg;
+ int num_words, trail_bytes, i, ret;
+
+ ret = k3_sec_proxy_verify_thread(spt, THREAD_IS_RX);
+ if (ret) {
+ ERROR("Thread %d verification failed (%d)\n", spt->id, ret);
+ return ret;
+ }
+
+ /* Read secure header */
+ data_reg = spm.desc.data_start_offset;
+ secure_header.data = mmio_read_32(spt->data + data_reg);
+ data_reg += sizeof(uint32_t);
+
+ /* Read whole words */
+ num_words = msg->len / sizeof(uint32_t);
+ for (i = 0; i < num_words; i++) {
+ ((uint32_t *)msg->buf)[i] = mmio_read_32(spt->data + data_reg);
+ data_reg += sizeof(uint32_t);
+ }
+
+ /* Read remaining bytes */
+ trail_bytes = msg->len % sizeof(uint32_t);
+ if (trail_bytes) {
+ uint32_t data_trail = mmio_read_32(spt->data + data_reg);
+ data_reg += sizeof(uint32_t);
+
+ i = msg->len - trail_bytes;
+ while (trail_bytes--) {
+ msg->buf[i] = data_trail & 0xff;
+ data_trail >>= 8;
+ }
+ }
+
+ /*
+ * 'data_reg' indicates next register to read. If we did not already
+ * read on rx complete reg(last reg), we must do so for receive
+ */
+ if (data_reg <= spm.desc.data_end_offset)
+ mmio_read_32(spt->data + spm.desc.data_end_offset);
+
+ /* TODO: Verify checksum */
+ (void)secure_header.checksum;
+
+ VERBOSE("Message successfully received from thread %ud\n", id);
+
+ return 0;
+}