diff options
author | 2023-10-10 14:33:42 +0000 | |
---|---|---|
committer | 2023-10-10 14:33:42 +0000 | |
commit | af1a266670d040d2f4083ff309d732d648afba2a (patch) | |
tree | 2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/u-boot/drivers/mailbox | |
parent | e02cda008591317b1625707ff8e115a4841aa889 (diff) |
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/u-boot/drivers/mailbox')
-rw-r--r-- | roms/u-boot/drivers/mailbox/Kconfig | 50 | ||||
-rw-r--r-- | roms/u-boot/drivers/mailbox/Makefile | 12 | ||||
-rw-r--r-- | roms/u-boot/drivers/mailbox/k3-sec-proxy.c | 436 | ||||
-rw-r--r-- | roms/u-boot/drivers/mailbox/mailbox-uclass.c | 154 | ||||
-rw-r--r-- | roms/u-boot/drivers/mailbox/sandbox-mbox-test.c | 54 | ||||
-rw-r--r-- | roms/u-boot/drivers/mailbox/sandbox-mbox.c | 105 | ||||
-rw-r--r-- | roms/u-boot/drivers/mailbox/stm32-ipcc.c | 170 | ||||
-rw-r--r-- | roms/u-boot/drivers/mailbox/tegra-hsp.c | 194 | ||||
-rw-r--r-- | roms/u-boot/drivers/mailbox/zynqmp-ipi.c | 142 |
9 files changed, 1317 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/mailbox/Kconfig b/roms/u-boot/drivers/mailbox/Kconfig new file mode 100644 index 000000000..dd4b0ac0c --- /dev/null +++ b/roms/u-boot/drivers/mailbox/Kconfig @@ -0,0 +1,50 @@ +menu "Mailbox Controller Support" + +config DM_MAILBOX + bool "Enable mailbox controllers using Driver Model" + depends on DM && OF_CONTROL + help + Enable support for the mailbox driver class. Mailboxes provide the + ability to transfer small messages and/or notifications from one + CPU to another CPU, or sometimes to dedicated HW modules. They form + the basis of a variety of inter-process/inter-CPU communication + protocols. + +config SANDBOX_MBOX + bool "Enable the sandbox mailbox test driver" + depends on DM_MAILBOX && SANDBOX + help + Enable support for a test mailbox implementation, which simply echos + back a modified version of any message that is sent. + +config TEGRA_HSP + bool "Enable Tegra HSP controller support" + depends on DM_MAILBOX && ARCH_TEGRA + help + This enables support for the NVIDIA Tegra HSP Hw module, which + implements doorbells, mailboxes, semaphores, and shared interrupts. + +config STM32_IPCC + bool "Enable STM32 IPCC controller support" + depends on DM_MAILBOX && ARCH_STM32MP + help + This enables support for the STM32MP IPCC Hw module, which + implements doorbells between 2 processors. + +config K3_SEC_PROXY + bool "Texas Instruments K3 Secure Proxy Driver" + depends on DM_MAILBOX && ARCH_K3 + help + An implementation of Secure proxy slave driver for K3 SoCs from + Texas Instruments. Secure proxy is a communication entity mainly + used for communication between multiple processors with the SoC. + Select this driver if your platform has support for this hardware + block. + +config ZYNQMP_IPI + bool "Xilinx ZynqMP IPI controller support" + depends on DM_MAILBOX && ARCH_ZYNQMP + help + This enables support for the Xilinx ZynqMP Inter Processor Interrupt + communication controller. +endmenu diff --git a/roms/u-boot/drivers/mailbox/Makefile b/roms/u-boot/drivers/mailbox/Makefile new file mode 100644 index 000000000..d2ace8cd2 --- /dev/null +++ b/roms/u-boot/drivers/mailbox/Makefile @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Copyright (c) 2016, NVIDIA CORPORATION. +# + +obj-$(CONFIG_$(SPL_)DM_MAILBOX) += mailbox-uclass.o +obj-$(CONFIG_SANDBOX_MBOX) += sandbox-mbox.o +obj-$(CONFIG_SANDBOX_MBOX) += sandbox-mbox-test.o +obj-$(CONFIG_STM32_IPCC) += stm32-ipcc.o +obj-$(CONFIG_TEGRA_HSP) += tegra-hsp.o +obj-$(CONFIG_K3_SEC_PROXY) += k3-sec-proxy.o +obj-$(CONFIG_ZYNQMP_IPI) += zynqmp-ipi.o diff --git a/roms/u-boot/drivers/mailbox/k3-sec-proxy.c b/roms/u-boot/drivers/mailbox/k3-sec-proxy.c new file mode 100644 index 000000000..88f320515 --- /dev/null +++ b/roms/u-boot/drivers/mailbox/k3-sec-proxy.c @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Texas Instruments' K3 Secure proxy Driver + * + * Copyright (C) 2017-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Lokesh Vutla <lokeshvutla@ti.com> + */ + +#include <common.h> +#include <log.h> +#include <malloc.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <dm/device_compat.h> +#include <linux/types.h> +#include <linux/bitops.h> +#include <linux/soc/ti/k3-sec-proxy.h> +#include <dm.h> +#include <mailbox-uclass.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* SEC PROXY RT THREAD STATUS */ +#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) + +/* 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. + * @thread_count: Number of Threads. + * @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 + * @valid_threads: List of Valid threads that the processor can access + * @num_valid_threads: Number of valid threads. + */ +struct k3_sec_proxy_desc { + u16 thread_count; + u16 max_msg_size; + u16 data_start_offset; + u16 data_end_offset; + const u32 *valid_threads; + u32 num_valid_threads; +}; + +/** + * 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 + * @rx_buf: Receive buffer data, max message size. + */ +struct k3_sec_proxy_thread { + u32 id; + void __iomem *data; + void __iomem *scfg; + void __iomem *rt; + u32 *rx_buf; +}; + +/** + * struct k3_sec_proxy_mbox - Description of a Secure Proxy Instance + * @chan: Mailbox Channel + * @desc: Description of the SoC integration + * @chans: Array for valid thread instances + * @target_data: Secure Proxy region for Target Data + * @scfg: Secure Proxy Region for Secure configuration. + * @rt: Secure proxy Region for Real Time Region. + */ +struct k3_sec_proxy_mbox { + struct mbox_chan chan; + struct k3_sec_proxy_desc *desc; + struct k3_sec_proxy_thread *chans; + phys_addr_t target_data; + phys_addr_t scfg; + phys_addr_t rt; +}; + +static inline u32 sp_readl(void __iomem *addr, unsigned int offset) +{ + return readl(addr + offset); +} + +static inline void sp_writel(void __iomem *addr, unsigned int offset, u32 data) +{ + writel(data, addr + offset); +} + +/** + * k3_sec_proxy_of_xlate() - Translation of phandle to channel + * @chan: Mailbox channel + * @args: Phandle Pointer + * + * Translates the phandle args and fills up the Mailbox channel from client. + * Return: 0 if all goes good, else return corresponding error message. + */ +static int k3_sec_proxy_of_xlate(struct mbox_chan *chan, + struct ofnode_phandle_args *args) +{ + struct k3_sec_proxy_mbox *spm = dev_get_priv(chan->dev); + int ind, i; + + debug("%s(chan=%p)\n", __func__, chan); + + if (args->args_count != 1) { + debug("Invaild args_count: %d\n", args->args_count); + return -EINVAL; + } + ind = args->args[0]; + + for (i = 0; i < spm->desc->num_valid_threads; i++) + if (spm->chans[i].id == ind) { + chan->id = ind; + chan->con_priv = &spm->chans[i]; + return 0; + } + + dev_err(chan->dev, "%s: Invalid Thread ID %d\n", __func__, ind); + return -ENOENT; +} + +/** + * k3_sec_proxy_request() - Request for mailbox channel + * @chan: Channel Pointer + */ +static int k3_sec_proxy_request(struct mbox_chan *chan) +{ + debug("%s(chan=%p)\n", __func__, chan); + + return 0; +} + +/** + * k3_sec_proxy_free() - Free the mailbox channel + * @chan: Channel Pointer + */ +static int k3_sec_proxy_free(struct mbox_chan *chan) +{ + debug("%s(chan=%p)\n", __func__, chan); + + return 0; +} + +/** + * 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 good, else appropriate error message. + */ +static inline int k3_sec_proxy_verify_thread(struct k3_sec_proxy_thread *spt, + u8 dir) +{ + /* Check for any errors already available */ + if (sp_readl(spt->rt, RT_THREAD_STATUS) & + RT_THREAD_STATUS_ERROR_MASK) { + printf("%s: Thread %d is corrupted, cannot send data.\n", + __func__, spt->id); + return -EINVAL; + } + + /* Make sure thread is configured for right direction */ + if ((sp_readl(spt->scfg, SCFG_THREAD_CTRL) + & SCFG_THREAD_CTRL_DIR_MASK) >> SCFG_THREAD_CTRL_DIR_SHIFT != dir) { + if (dir) + printf("%s: Trying to receive data on tx Thread %d\n", + __func__, spt->id); + else + printf("%s: Trying to send data on rx Thread %d\n", + __func__, spt->id); + return -EINVAL; + } + + /* Check the message queue before sending/receiving data */ + if (!(sp_readl(spt->rt, RT_THREAD_STATUS) & + RT_THREAD_STATUS_CUR_CNT_MASK)) + return -ENODATA; + + return 0; +} + +/** + * k3_sec_proxy_send() - Send data via mailbox channel + * @chan: Channel Pointer + * @data: Pointer to k3_sec_proxy_msg + * + * Return: 0 if all goes good, else appropriate error message. + */ +static int k3_sec_proxy_send(struct mbox_chan *chan, const void *data) +{ + const struct k3_sec_proxy_msg *msg = (struct k3_sec_proxy_msg *)data; + struct k3_sec_proxy_mbox *spm = dev_get_priv(chan->dev); + struct k3_sec_proxy_thread *spt = chan->con_priv; + int num_words, trail_bytes, ret; + void __iomem *data_reg; + u32 *word_data; + + debug("%s(chan=%p, data=%p)\n", __func__, chan, data); + + ret = k3_sec_proxy_verify_thread(spt, THREAD_IS_TX); + if (ret) { + dev_err(chan->dev, + "%s: Thread%d verification failed. ret = %d\n", + __func__, spt->id, ret); + return ret; + } + + /* Check the message size. */ + if (msg->len > spm->desc->max_msg_size) { + dev_err(chan->dev, + "%s: Thread %ld message length %zu > max msg size %d\n", + __func__, chan->id, msg->len, spm->desc->max_msg_size); + return -EINVAL; + } + + /* Send the message */ + data_reg = spt->data + spm->desc->data_start_offset; + for (num_words = msg->len / sizeof(u32), word_data = (u32 *)msg->buf; + num_words; + num_words--, data_reg += sizeof(u32), word_data++) + writel(*word_data, data_reg); + + trail_bytes = msg->len % sizeof(u32); + if (trail_bytes) { + u32 data_trail = *word_data; + + /* Ensure all unused data is 0 */ + data_trail &= 0xFFFFFFFF >> (8 * (sizeof(u32) - trail_bytes)); + writel(data_trail, data_reg); + data_reg++; + } + + /* + * '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 <= (spt->data + spm->desc->data_end_offset)) + sp_writel(spt->data, spm->desc->data_end_offset, 0); + + debug("%s: Message successfully sent on thread %ld\n", + __func__, chan->id); + + return 0; +} + +/** + * k3_sec_proxy_recv() - Receive data via mailbox channel + * @chan: Channel Pointer + * @data: Pointer to k3_sec_proxy_msg + * + * Return: 0 if all goes good, else appropriate error message. + */ +static int k3_sec_proxy_recv(struct mbox_chan *chan, void *data) +{ + struct k3_sec_proxy_mbox *spm = dev_get_priv(chan->dev); + struct k3_sec_proxy_thread *spt = chan->con_priv; + struct k3_sec_proxy_msg *msg = data; + void __iomem *data_reg; + int num_words, ret; + u32 *word_data; + + debug("%s(chan=%p, data=%p)\n", __func__, chan, data); + + ret = k3_sec_proxy_verify_thread(spt, THREAD_IS_RX); + if (ret) + return ret; + + msg->len = spm->desc->max_msg_size; + msg->buf = spt->rx_buf; + data_reg = spt->data + spm->desc->data_start_offset; + word_data = spt->rx_buf; + for (num_words = spm->desc->max_msg_size / sizeof(u32); + num_words; + num_words--, data_reg += sizeof(u32), word_data++) + *word_data = readl(data_reg); + + debug("%s: Message successfully received from thread %ld\n", + __func__, chan->id); + + return 0; +} + +struct mbox_ops k3_sec_proxy_mbox_ops = { + .of_xlate = k3_sec_proxy_of_xlate, + .request = k3_sec_proxy_request, + .rfree = k3_sec_proxy_free, + .send = k3_sec_proxy_send, + .recv = k3_sec_proxy_recv, +}; + +/** + * k3_sec_proxy_of_to_priv() - generate private data from device tree + * @dev: corresponding k3 secure proxy device + * @spm: pointer to driver specific private data + * + * Return: 0 if all went ok, else corresponding error message. + */ +static int k3_sec_proxy_of_to_priv(struct udevice *dev, + struct k3_sec_proxy_mbox *spm) +{ + const void *blob = gd->fdt_blob; + + if (!blob) { + debug("'%s' no dt?\n", dev->name); + return -ENODEV; + } + + spm->target_data = devfdt_get_addr_name(dev, "target_data"); + if (spm->target_data == FDT_ADDR_T_NONE) { + dev_err(dev, "No reg property for target data base\n"); + return -EINVAL; + } + + spm->scfg = devfdt_get_addr_name(dev, "scfg"); + if (spm->rt == FDT_ADDR_T_NONE) { + dev_err(dev, "No reg property for Secure Cfg base\n"); + return -EINVAL; + } + + spm->rt = devfdt_get_addr_name(dev, "rt"); + if (spm->rt == FDT_ADDR_T_NONE) { + dev_err(dev, "No reg property for Real Time Cfg base\n"); + return -EINVAL; + } + + return 0; +} + +/** + * k3_sec_proxy_thread_setup - Initialize the parameters for all valid threads + * @spm: Mailbox instance for which threads needs to be initialized + * + * Return: 0 if all went ok, else corresponding error message + */ +static int k3_sec_proxy_thread_setup(struct k3_sec_proxy_mbox *spm) +{ + struct k3_sec_proxy_thread *spt; + int i, ind; + + for (i = 0; i < spm->desc->num_valid_threads; i++) { + spt = &spm->chans[i]; + ind = spm->desc->valid_threads[i]; + spt->id = ind; + spt->data = (void *)SEC_PROXY_THREAD(spm->target_data, ind); + spt->scfg = (void *)SEC_PROXY_THREAD(spm->scfg, ind); + spt->rt = (void *)SEC_PROXY_THREAD(spm->rt, ind); + spt->rx_buf = calloc(1, spm->desc->max_msg_size); + if (!spt->rx_buf) + return -ENOMEM; + } + + return 0; +} + +/** + * k3_sec_proxy_probe() - Basic probe + * @dev: corresponding mailbox device + * + * Return: 0 if all went ok, else corresponding error message + */ +static int k3_sec_proxy_probe(struct udevice *dev) +{ + struct k3_sec_proxy_mbox *spm = dev_get_priv(dev); + int ret; + + debug("%s(dev=%p)\n", __func__, dev); + + ret = k3_sec_proxy_of_to_priv(dev, spm); + if (ret) + return ret; + + spm->desc = (void *)dev_get_driver_data(dev); + spm->chans = calloc(spm->desc->num_valid_threads, + sizeof(struct k3_sec_proxy_thread)); + if (!spm->chans) + return -ENOMEM; + + ret = k3_sec_proxy_thread_setup(spm); + if (ret) { + debug("%s: secure proxy thread setup failed\n", __func__); + return ret; + } + + return 0; +} + +static int k3_sec_proxy_remove(struct udevice *dev) +{ + struct k3_sec_proxy_mbox *spm = dev_get_priv(dev); + + debug("%s(dev=%p)\n", __func__, dev); + + free(spm->chans); + + return 0; +} + +static const u32 am6x_valid_threads[] = { 0, 1, 4, 5, 6, 7, 8, 9, 11, 12, 13 }; + +static const struct k3_sec_proxy_desc am654_desc = { + .thread_count = 90, + .max_msg_size = 60, + .data_start_offset = 0x4, + .data_end_offset = 0x3C, + .valid_threads = am6x_valid_threads, + .num_valid_threads = ARRAY_SIZE(am6x_valid_threads), +}; + +static const struct udevice_id k3_sec_proxy_ids[] = { + { .compatible = "ti,am654-secure-proxy", .data = (ulong)&am654_desc}, + { } +}; + +U_BOOT_DRIVER(k3_sec_proxy) = { + .name = "k3-secure-proxy", + .id = UCLASS_MAILBOX, + .of_match = k3_sec_proxy_ids, + .probe = k3_sec_proxy_probe, + .remove = k3_sec_proxy_remove, + .priv_auto = sizeof(struct k3_sec_proxy_mbox), + .ops = &k3_sec_proxy_mbox_ops, +}; diff --git a/roms/u-boot/drivers/mailbox/mailbox-uclass.c b/roms/u-boot/drivers/mailbox/mailbox-uclass.c new file mode 100644 index 000000000..c972d8460 --- /dev/null +++ b/roms/u-boot/drivers/mailbox/mailbox-uclass.c @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <mailbox.h> +#include <mailbox-uclass.h> +#include <malloc.h> +#include <time.h> + +static inline struct mbox_ops *mbox_dev_ops(struct udevice *dev) +{ + return (struct mbox_ops *)dev->driver->ops; +} + +static int mbox_of_xlate_default(struct mbox_chan *chan, + struct ofnode_phandle_args *args) +{ + debug("%s(chan=%p)\n", __func__, chan); + + if (args->args_count != 1) { + debug("Invaild args_count: %d\n", args->args_count); + return -EINVAL; + } + + chan->id = args->args[0]; + + return 0; +} + +int mbox_get_by_index(struct udevice *dev, int index, struct mbox_chan *chan) +{ + struct ofnode_phandle_args args; + int ret; + struct udevice *dev_mbox; + struct mbox_ops *ops; + + debug("%s(dev=%p, index=%d, chan=%p)\n", __func__, dev, index, chan); + + ret = dev_read_phandle_with_args(dev, "mboxes", "#mbox-cells", 0, index, + &args); + if (ret) { + debug("%s: dev_read_phandle_with_args failed: %d\n", __func__, + ret); + return ret; + } + + ret = uclass_get_device_by_ofnode(UCLASS_MAILBOX, args.node, &dev_mbox); + if (ret) { + debug("%s: uclass_get_device_by_of_offset failed: %d\n", + __func__, ret); + + /* Test with parent node */ + ret = uclass_get_device_by_ofnode(UCLASS_MAILBOX, + ofnode_get_parent(args.node), + &dev_mbox); + if (ret) { + debug("%s: mbox node from parent failed: %d\n", + __func__, ret); + return ret; + }; + } + ops = mbox_dev_ops(dev_mbox); + + chan->dev = dev_mbox; + if (ops->of_xlate) + ret = ops->of_xlate(chan, &args); + else + ret = mbox_of_xlate_default(chan, &args); + if (ret) { + debug("of_xlate() failed: %d\n", ret); + return ret; + } + + if (ops->request) + ret = ops->request(chan); + if (ret) { + debug("ops->request() failed: %d\n", ret); + return ret; + } + + return 0; +} + +int mbox_get_by_name(struct udevice *dev, const char *name, + struct mbox_chan *chan) +{ + int index; + + debug("%s(dev=%p, name=%s, chan=%p)\n", __func__, dev, name, chan); + + index = dev_read_stringlist_search(dev, "mbox-names", name); + if (index < 0) { + debug("fdt_stringlist_search() failed: %d\n", index); + return index; + } + + return mbox_get_by_index(dev, index, chan); +} + +int mbox_free(struct mbox_chan *chan) +{ + struct mbox_ops *ops = mbox_dev_ops(chan->dev); + + debug("%s(chan=%p)\n", __func__, chan); + + if (ops->rfree) + return ops->rfree(chan); + + return 0; +} + +int mbox_send(struct mbox_chan *chan, const void *data) +{ + struct mbox_ops *ops = mbox_dev_ops(chan->dev); + + debug("%s(chan=%p, data=%p)\n", __func__, chan, data); + + return ops->send(chan, data); +} + +int mbox_recv(struct mbox_chan *chan, void *data, ulong timeout_us) +{ + struct mbox_ops *ops = mbox_dev_ops(chan->dev); + ulong start_time; + int ret; + + debug("%s(chan=%p, data=%p, timeout_us=%ld)\n", __func__, chan, data, + timeout_us); + + start_time = timer_get_us(); + /* + * Account for partial us ticks, but if timeout_us is 0, ensure we + * still don't wait at all. + */ + if (timeout_us) + timeout_us++; + + for (;;) { + ret = ops->recv(chan, data); + if (ret != -ENODATA) + return ret; + if ((timer_get_us() - start_time) >= timeout_us) + return -ETIMEDOUT; + } +} + +UCLASS_DRIVER(mailbox) = { + .id = UCLASS_MAILBOX, + .name = "mailbox", +}; diff --git a/roms/u-boot/drivers/mailbox/sandbox-mbox-test.c b/roms/u-boot/drivers/mailbox/sandbox-mbox-test.c new file mode 100644 index 000000000..ffd4674d1 --- /dev/null +++ b/roms/u-boot/drivers/mailbox/sandbox-mbox-test.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + */ + +#include <common.h> +#include <dm.h> +#include <mailbox.h> +#include <malloc.h> +#include <asm/io.h> + +struct sandbox_mbox_test { + struct mbox_chan chan; +}; + +int sandbox_mbox_test_get(struct udevice *dev) +{ + struct sandbox_mbox_test *sbmt = dev_get_priv(dev); + + return mbox_get_by_name(dev, "test", &sbmt->chan); +} + +int sandbox_mbox_test_send(struct udevice *dev, uint32_t msg) +{ + struct sandbox_mbox_test *sbmt = dev_get_priv(dev); + + return mbox_send(&sbmt->chan, &msg); +} + +int sandbox_mbox_test_recv(struct udevice *dev, uint32_t *msg) +{ + struct sandbox_mbox_test *sbmt = dev_get_priv(dev); + + return mbox_recv(&sbmt->chan, msg, 100); +} + +int sandbox_mbox_test_free(struct udevice *dev) +{ + struct sandbox_mbox_test *sbmt = dev_get_priv(dev); + + return mbox_free(&sbmt->chan); +} + +static const struct udevice_id sandbox_mbox_test_ids[] = { + { .compatible = "sandbox,mbox-test" }, + { } +}; + +U_BOOT_DRIVER(sandbox_mbox_test) = { + .name = "sandbox_mbox_test", + .id = UCLASS_MISC, + .of_match = sandbox_mbox_test_ids, + .priv_auto = sizeof(struct sandbox_mbox_test), +}; diff --git a/roms/u-boot/drivers/mailbox/sandbox-mbox.c b/roms/u-boot/drivers/mailbox/sandbox-mbox.c new file mode 100644 index 000000000..87d38de0c --- /dev/null +++ b/roms/u-boot/drivers/mailbox/sandbox-mbox.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <mailbox-uclass.h> +#include <malloc.h> +#include <asm/io.h> +#include <asm/mbox.h> + +#define SANDBOX_MBOX_CHANNELS 2 + +struct sandbox_mbox_chan { + bool rx_msg_valid; + uint32_t rx_msg; +}; + +struct sandbox_mbox { + struct sandbox_mbox_chan chans[SANDBOX_MBOX_CHANNELS]; +}; + +static int sandbox_mbox_request(struct mbox_chan *chan) +{ + debug("%s(chan=%p)\n", __func__, chan); + + if (chan->id >= SANDBOX_MBOX_CHANNELS) + return -EINVAL; + + return 0; +} + +static int sandbox_mbox_free(struct mbox_chan *chan) +{ + debug("%s(chan=%p)\n", __func__, chan); + + return 0; +} + +static int sandbox_mbox_send(struct mbox_chan *chan, const void *data) +{ + struct sandbox_mbox *sbm = dev_get_priv(chan->dev); + const uint32_t *pmsg = data; + + debug("%s(chan=%p, data=%p)\n", __func__, chan, data); + + sbm->chans[chan->id].rx_msg = *pmsg ^ SANDBOX_MBOX_PING_XOR; + sbm->chans[chan->id].rx_msg_valid = true; + + return 0; +} + +static int sandbox_mbox_recv(struct mbox_chan *chan, void *data) +{ + struct sandbox_mbox *sbm = dev_get_priv(chan->dev); + uint32_t *pmsg = data; + + debug("%s(chan=%p, data=%p)\n", __func__, chan, data); + + if (!sbm->chans[chan->id].rx_msg_valid) + return -ENODATA; + + *pmsg = sbm->chans[chan->id].rx_msg; + sbm->chans[chan->id].rx_msg_valid = false; + + return 0; +} + +static int sandbox_mbox_bind(struct udevice *dev) +{ + debug("%s(dev=%p)\n", __func__, dev); + + return 0; +} + +static int sandbox_mbox_probe(struct udevice *dev) +{ + debug("%s(dev=%p)\n", __func__, dev); + + return 0; +} + +static const struct udevice_id sandbox_mbox_ids[] = { + { .compatible = "sandbox,mbox" }, + { } +}; + +struct mbox_ops sandbox_mbox_mbox_ops = { + .request = sandbox_mbox_request, + .rfree = sandbox_mbox_free, + .send = sandbox_mbox_send, + .recv = sandbox_mbox_recv, +}; + +U_BOOT_DRIVER(sandbox_mbox) = { + .name = "sandbox_mbox", + .id = UCLASS_MAILBOX, + .of_match = sandbox_mbox_ids, + .bind = sandbox_mbox_bind, + .probe = sandbox_mbox_probe, + .priv_auto = sizeof(struct sandbox_mbox), + .ops = &sandbox_mbox_mbox_ops, +}; diff --git a/roms/u-boot/drivers/mailbox/stm32-ipcc.c b/roms/u-boot/drivers/mailbox/stm32-ipcc.c new file mode 100644 index 000000000..69c86e059 --- /dev/null +++ b/roms/u-boot/drivers/mailbox/stm32-ipcc.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) STMicroelectronics 2019 - All Rights Reserved + */ + +#define LOG_CATEGORY UCLASS_MAILBOX + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <log.h> +#include <mailbox-uclass.h> +#include <malloc.h> +#include <asm/io.h> +#include <dm/device_compat.h> +#include <linux/bitops.h> + +/* + * IPCC has one set of registers per CPU + * IPCC_PROC_OFFST allows to define cpu registers set base address + * according to the assigned proc_id. + */ + +#define IPCC_PROC_OFFST 0x010 + +#define IPCC_XSCR 0x008 +#define IPCC_XTOYSR 0x00c + +#define IPCC_HWCFGR 0x3f0 +#define IPCFGR_CHAN_MASK GENMASK(7, 0) + +#define RX_BIT_CHAN(chan) BIT(chan) +#define TX_BIT_SHIFT 16 +#define TX_BIT_CHAN(chan) BIT(TX_BIT_SHIFT + (chan)) + +#define STM32_MAX_PROCS 2 + +struct stm32_ipcc { + void __iomem *reg_base; + void __iomem *reg_proc; + u32 proc_id; + u32 n_chans; +}; + +static int stm32_ipcc_request(struct mbox_chan *chan) +{ + struct stm32_ipcc *ipcc = dev_get_priv(chan->dev); + + dev_dbg(chan->dev, "chan=%p\n", chan); + + if (chan->id >= ipcc->n_chans) { + dev_dbg(chan->dev, "failed to request channel: %ld\n", + chan->id); + return -EINVAL; + } + + return 0; +} + +static int stm32_ipcc_free(struct mbox_chan *chan) +{ + dev_dbg(chan->dev, "chan=%p\n", chan); + + return 0; +} + +static int stm32_ipcc_send(struct mbox_chan *chan, const void *data) +{ + struct stm32_ipcc *ipcc = dev_get_priv(chan->dev); + + dev_dbg(chan->dev, "chan=%p, data=%p\n", chan, data); + + if (readl(ipcc->reg_proc + IPCC_XTOYSR) & BIT(chan->id)) + return -EBUSY; + + /* set channel n occupied */ + setbits_le32(ipcc->reg_proc + IPCC_XSCR, TX_BIT_CHAN(chan->id)); + + return 0; +} + +static int stm32_ipcc_recv(struct mbox_chan *chan, void *data) +{ + struct stm32_ipcc *ipcc = dev_get_priv(chan->dev); + u32 val; + int proc_offset; + + dev_dbg(chan->dev, "chan=%p, data=%p\n", chan, data); + + /* read 'channel occupied' status from other proc */ + proc_offset = ipcc->proc_id ? -IPCC_PROC_OFFST : IPCC_PROC_OFFST; + val = readl(ipcc->reg_proc + proc_offset + IPCC_XTOYSR); + + if (!(val & BIT(chan->id))) + return -ENODATA; + + setbits_le32(ipcc->reg_proc + IPCC_XSCR, RX_BIT_CHAN(chan->id)); + + return 0; +} + +static int stm32_ipcc_probe(struct udevice *dev) +{ + struct stm32_ipcc *ipcc = dev_get_priv(dev); + fdt_addr_t addr; + struct clk clk; + int ret; + + dev_dbg(dev, "\n"); + + addr = dev_read_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + ipcc->reg_base = (void __iomem *)addr; + + /* proc_id */ + ret = dev_read_u32_index(dev, "st,proc_id", 1, &ipcc->proc_id); + if (ret) { + dev_dbg(dev, "Missing st,proc_id\n"); + return -EINVAL; + } + + if (ipcc->proc_id >= STM32_MAX_PROCS) { + dev_err(dev, "Invalid proc_id (%d)\n", ipcc->proc_id); + return -EINVAL; + } + + ipcc->reg_proc = ipcc->reg_base + ipcc->proc_id * IPCC_PROC_OFFST; + + ret = clk_get_by_index(dev, 0, &clk); + if (ret) + return ret; + + ret = clk_enable(&clk); + if (ret) + goto clk_free; + + /* get channel number */ + ipcc->n_chans = readl(ipcc->reg_base + IPCC_HWCFGR); + ipcc->n_chans &= IPCFGR_CHAN_MASK; + + return 0; + +clk_free: + clk_free(&clk); + + return ret; +} + +static const struct udevice_id stm32_ipcc_ids[] = { + { .compatible = "st,stm32mp1-ipcc" }, + { } +}; + +struct mbox_ops stm32_ipcc_mbox_ops = { + .request = stm32_ipcc_request, + .rfree = stm32_ipcc_free, + .send = stm32_ipcc_send, + .recv = stm32_ipcc_recv, +}; + +U_BOOT_DRIVER(stm32_ipcc) = { + .name = "stm32_ipcc", + .id = UCLASS_MAILBOX, + .of_match = stm32_ipcc_ids, + .probe = stm32_ipcc_probe, + .priv_auto = sizeof(struct stm32_ipcc), + .ops = &stm32_ipcc_mbox_ops, +}; diff --git a/roms/u-boot/drivers/mailbox/tegra-hsp.c b/roms/u-boot/drivers/mailbox/tegra-hsp.c new file mode 100644 index 000000000..1d66d95fe --- /dev/null +++ b/roms/u-boot/drivers/mailbox/tegra-hsp.c @@ -0,0 +1,194 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2016, NVIDIA CORPORATION. + */ + +#include <common.h> +#include <log.h> +#include <malloc.h> +#include <asm/io.h> +#include <dm.h> +#include <mailbox-uclass.h> +#include <dt-bindings/mailbox/tegra186-hsp.h> +#include <linux/bitops.h> + +#define TEGRA_HSP_INT_DIMENSIONING 0x380 +#define TEGRA_HSP_INT_DIMENSIONING_NSI_SHIFT 16 +#define TEGRA_HSP_INT_DIMENSIONING_NSI_MASK 0xf +#define TEGRA_HSP_INT_DIMENSIONING_NDB_SHIFT 12 +#define TEGRA_HSP_INT_DIMENSIONING_NDB_MASK 0xf +#define TEGRA_HSP_INT_DIMENSIONING_NAS_SHIFT 8 +#define TEGRA_HSP_INT_DIMENSIONING_NAS_MASK 0xf +#define TEGRA_HSP_INT_DIMENSIONING_NSS_SHIFT 4 +#define TEGRA_HSP_INT_DIMENSIONING_NSS_MASK 0xf +#define TEGRA_HSP_INT_DIMENSIONING_NSM_SHIFT 0 +#define TEGRA_HSP_INT_DIMENSIONING_NSM_MASK 0xf + +#define TEGRA_HSP_DB_REG_TRIGGER 0x0 +#define TEGRA_HSP_DB_REG_ENABLE 0x4 +#define TEGRA_HSP_DB_REG_RAW 0x8 +#define TEGRA_HSP_DB_REG_PENDING 0xc + +#define TEGRA_HSP_DB_ID_CCPLEX 1 +#define TEGRA_HSP_DB_ID_BPMP 3 +#define TEGRA_HSP_DB_ID_NUM 7 + +struct tegra_hsp { + fdt_addr_t regs; + uint32_t db_base; +}; + +static uint32_t *tegra_hsp_reg(struct tegra_hsp *thsp, uint32_t db_id, + uint32_t reg) +{ + return (uint32_t *)(thsp->regs + thsp->db_base + (db_id * 0x100) + reg); +} + +static uint32_t tegra_hsp_readl(struct tegra_hsp *thsp, uint32_t db_id, + uint32_t reg) +{ + uint32_t *r = tegra_hsp_reg(thsp, db_id, reg); + return readl(r); +} + +static void tegra_hsp_writel(struct tegra_hsp *thsp, uint32_t val, + uint32_t db_id, uint32_t reg) +{ + uint32_t *r = tegra_hsp_reg(thsp, db_id, reg); + + writel(val, r); + readl(r); +} + +static int tegra_hsp_db_id(ulong chan_id) +{ + switch (chan_id) { + case (HSP_MBOX_TYPE_DB << 16) | HSP_DB_MASTER_BPMP: + return TEGRA_HSP_DB_ID_BPMP; + default: + debug("Invalid channel ID\n"); + return -EINVAL; + } +} + +static int tegra_hsp_of_xlate(struct mbox_chan *chan, + struct ofnode_phandle_args *args) +{ + debug("%s(chan=%p)\n", __func__, chan); + + if (args->args_count != 2) { + debug("Invaild args_count: %d\n", args->args_count); + return -EINVAL; + } + + chan->id = (args->args[0] << 16) | args->args[1]; + + return 0; +} + +static int tegra_hsp_request(struct mbox_chan *chan) +{ + int db_id; + + debug("%s(chan=%p)\n", __func__, chan); + + db_id = tegra_hsp_db_id(chan->id); + if (db_id < 0) { + debug("tegra_hsp_db_id() failed: %d\n", db_id); + return -EINVAL; + } + + return 0; +} + +static int tegra_hsp_free(struct mbox_chan *chan) +{ + debug("%s(chan=%p)\n", __func__, chan); + + return 0; +} + +static int tegra_hsp_send(struct mbox_chan *chan, const void *data) +{ + struct tegra_hsp *thsp = dev_get_priv(chan->dev); + int db_id; + + debug("%s(chan=%p, data=%p)\n", __func__, chan, data); + + db_id = tegra_hsp_db_id(chan->id); + tegra_hsp_writel(thsp, 1, db_id, TEGRA_HSP_DB_REG_TRIGGER); + + return 0; +} + +static int tegra_hsp_recv(struct mbox_chan *chan, void *data) +{ + struct tegra_hsp *thsp = dev_get_priv(chan->dev); + uint32_t db_id = TEGRA_HSP_DB_ID_CCPLEX; + uint32_t val; + + debug("%s(chan=%p, data=%p)\n", __func__, chan, data); + + val = tegra_hsp_readl(thsp, db_id, TEGRA_HSP_DB_REG_RAW); + if (!(val & BIT(chan->id))) + return -ENODATA; + + tegra_hsp_writel(thsp, BIT(chan->id), db_id, TEGRA_HSP_DB_REG_RAW); + + return 0; +} + +static int tegra_hsp_bind(struct udevice *dev) +{ + debug("%s(dev=%p)\n", __func__, dev); + + return 0; +} + +static int tegra_hsp_probe(struct udevice *dev) +{ + struct tegra_hsp *thsp = dev_get_priv(dev); + u32 val; + int nr_sm, nr_ss, nr_as; + + debug("%s(dev=%p)\n", __func__, dev); + + thsp->regs = dev_read_addr(dev); + if (thsp->regs == FDT_ADDR_T_NONE) + return -ENODEV; + + val = readl(thsp->regs + TEGRA_HSP_INT_DIMENSIONING); + nr_sm = (val >> TEGRA_HSP_INT_DIMENSIONING_NSM_SHIFT) & + TEGRA_HSP_INT_DIMENSIONING_NSM_MASK; + nr_ss = (val >> TEGRA_HSP_INT_DIMENSIONING_NSS_SHIFT) & + TEGRA_HSP_INT_DIMENSIONING_NSS_MASK; + nr_as = (val >> TEGRA_HSP_INT_DIMENSIONING_NAS_SHIFT) & + TEGRA_HSP_INT_DIMENSIONING_NAS_MASK; + + thsp->db_base = (1 + (nr_sm >> 1) + nr_ss + nr_as) << 16; + + return 0; +} + +static const struct udevice_id tegra_hsp_ids[] = { + { .compatible = "nvidia,tegra186-hsp" }, + { } +}; + +struct mbox_ops tegra_hsp_mbox_ops = { + .of_xlate = tegra_hsp_of_xlate, + .request = tegra_hsp_request, + .rfree = tegra_hsp_free, + .send = tegra_hsp_send, + .recv = tegra_hsp_recv, +}; + +U_BOOT_DRIVER(tegra_hsp) = { + .name = "tegra-hsp", + .id = UCLASS_MAILBOX, + .of_match = tegra_hsp_ids, + .bind = tegra_hsp_bind, + .probe = tegra_hsp_probe, + .priv_auto = sizeof(struct tegra_hsp), + .ops = &tegra_hsp_mbox_ops, +}; diff --git a/roms/u-boot/drivers/mailbox/zynqmp-ipi.c b/roms/u-boot/drivers/mailbox/zynqmp-ipi.c new file mode 100644 index 000000000..959cce923 --- /dev/null +++ b/roms/u-boot/drivers/mailbox/zynqmp-ipi.c @@ -0,0 +1,142 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Xilinx Zynq MPSoC Mailbox driver + * + * Copyright (C) 2018-2019 Xilinx, Inc. + */ + +#include <common.h> +#include <log.h> +#include <asm/io.h> +#include <dm.h> +#include <mailbox-uclass.h> +#include <dm/device_compat.h> +#include <mach/sys_proto.h> +#include <linux/ioport.h> +#include <linux/io.h> +#include <wait_bit.h> + +/* IPI bitmasks, register base */ +/* TODO: move reg base to DT */ +#define IPI_BIT_MASK_PMU0 0x10000 +#define IPI_INT_REG_BASE_APU 0xFF300000 + +struct ipi_int_regs { + u32 trig; /* 0x0 */ + u32 obs; /* 0x4 */ + u32 dummy0; + u32 dummy1; + u32 isr; /* 0x10 */ + u32 imr; /* 0x14 */ + u32 ier; /* 0x18 */ + u32 idr; /* 0x1C */ +}; + +#define ipi_int_apu ((struct ipi_int_regs *)IPI_INT_REG_BASE_APU) + +struct zynqmp_ipi { + void __iomem *local_req_regs; + void __iomem *local_res_regs; + void __iomem *remote_req_regs; + void __iomem *remote_res_regs; +}; + +static int zynqmp_ipi_send(struct mbox_chan *chan, const void *data) +{ + const struct zynqmp_ipi_msg *msg = (struct zynqmp_ipi_msg *)data; + struct zynqmp_ipi *zynqmp = dev_get_priv(chan->dev); + u32 ret; + u32 *mbx = (u32 *)zynqmp->local_req_regs; + + for (size_t i = 0; i < msg->len; i++) + writel(msg->buf[i], &mbx[i]); + + /* Write trigger interrupt */ + writel(IPI_BIT_MASK_PMU0, &ipi_int_apu->trig); + + /* Wait until observation bit is cleared */ + ret = wait_for_bit_le32(&ipi_int_apu->obs, IPI_BIT_MASK_PMU0, false, + 1000, false); + + debug("%s, send %ld bytes\n", __func__, msg->len); + return ret; +}; + +static int zynqmp_ipi_recv(struct mbox_chan *chan, void *data) +{ + struct zynqmp_ipi_msg *msg = (struct zynqmp_ipi_msg *)data; + struct zynqmp_ipi *zynqmp = dev_get_priv(chan->dev); + u32 *mbx = (u32 *)zynqmp->local_res_regs; + + /* + * PMU Firmware does not trigger IPI interrupt for API call responses so + * there is no need to check ISR flags + */ + for (size_t i = 0; i < msg->len; i++) + msg->buf[i] = readl(&mbx[i]); + + debug("%s, recv %ld bytes\n", __func__, msg->len); + return 0; +}; + +static int zynqmp_ipi_probe(struct udevice *dev) +{ + struct zynqmp_ipi *zynqmp = dev_get_priv(dev); + struct resource res; + ofnode node; + + debug("%s(dev=%p)\n", __func__, dev); + + /* Get subnode where the regs are defined */ + /* Note IPI mailbox node needs to be the first one in DT */ + node = ofnode_first_subnode(dev_ofnode(dev)); + + if (ofnode_read_resource_byname(node, "local_request_region", &res)) { + dev_err(dev, "No reg property for local_request_region\n"); + return -EINVAL; + }; + zynqmp->local_req_regs = devm_ioremap(dev, res.start, + (res.start - res.end)); + + if (ofnode_read_resource_byname(node, "local_response_region", &res)) { + dev_err(dev, "No reg property for local_response_region\n"); + return -EINVAL; + }; + zynqmp->local_res_regs = devm_ioremap(dev, res.start, + (res.start - res.end)); + + if (ofnode_read_resource_byname(node, "remote_request_region", &res)) { + dev_err(dev, "No reg property for remote_request_region\n"); + return -EINVAL; + }; + zynqmp->remote_req_regs = devm_ioremap(dev, res.start, + (res.start - res.end)); + + if (ofnode_read_resource_byname(node, "remote_response_region", &res)) { + dev_err(dev, "No reg property for remote_response_region\n"); + return -EINVAL; + }; + zynqmp->remote_res_regs = devm_ioremap(dev, res.start, + (res.start - res.end)); + + return 0; +}; + +static const struct udevice_id zynqmp_ipi_ids[] = { + { .compatible = "xlnx,zynqmp-ipi-mailbox" }, + { } +}; + +struct mbox_ops zynqmp_ipi_mbox_ops = { + .send = zynqmp_ipi_send, + .recv = zynqmp_ipi_recv, +}; + +U_BOOT_DRIVER(zynqmp_ipi) = { + .name = "zynqmp_ipi", + .id = UCLASS_MAILBOX, + .of_match = zynqmp_ipi_ids, + .probe = zynqmp_ipi_probe, + .priv_auto = sizeof(struct zynqmp_ipi), + .ops = &zynqmp_ipi_mbox_ops, +}; |