diff options
Diffstat (limited to 'roms/u-boot/drivers/remoteproc')
-rw-r--r-- | roms/u-boot/drivers/remoteproc/Kconfig | 84 | ||||
-rw-r--r-- | roms/u-boot/drivers/remoteproc/Makefile | 16 | ||||
-rw-r--r-- | roms/u-boot/drivers/remoteproc/k3_system_controller.c | 326 | ||||
-rw-r--r-- | roms/u-boot/drivers/remoteproc/rproc-elf-loader.c | 534 | ||||
-rw-r--r-- | roms/u-boot/drivers/remoteproc/rproc-uclass.c | 437 | ||||
-rw-r--r-- | roms/u-boot/drivers/remoteproc/sandbox_testproc.c | 358 | ||||
-rw-r--r-- | roms/u-boot/drivers/remoteproc/stm32_copro.c | 227 | ||||
-rw-r--r-- | roms/u-boot/drivers/remoteproc/ti_k3_arm64_rproc.c | 246 | ||||
-rw-r--r-- | roms/u-boot/drivers/remoteproc/ti_k3_dsp_rproc.c | 460 | ||||
-rw-r--r-- | roms/u-boot/drivers/remoteproc/ti_k3_r5f_rproc.c | 906 | ||||
-rw-r--r-- | roms/u-boot/drivers/remoteproc/ti_power_proc.c | 182 | ||||
-rw-r--r-- | roms/u-boot/drivers/remoteproc/ti_sci_proc.h | 148 |
12 files changed, 3924 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/remoteproc/Kconfig b/roms/u-boot/drivers/remoteproc/Kconfig new file mode 100644 index 000000000..7c2e4804b --- /dev/null +++ b/roms/u-boot/drivers/remoteproc/Kconfig @@ -0,0 +1,84 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2015 +# Texas Instruments Incorporated - http://www.ti.com/ +# + +menu "Remote Processor drivers" + +# REMOTEPROC gets selected by drivers as needed +# All users should depend on DM +config REMOTEPROC + bool + depends on DM + +# Please keep the configuration alphabetically sorted. +config K3_SYSTEM_CONTROLLER + bool "Support for TI' K3 System Controller" + select REMOTEPROC + depends on DM + depends on ARCH_K3 + depends on OF_CONTROL + help + Say 'y' here to add support for TI' K3 System Controller. + +config REMOTEPROC_SANDBOX + bool "Support for Test processor for Sandbox" + select REMOTEPROC + depends on DM + depends on SANDBOX + help + Say 'y' here to add support for test processor which does dummy + operations for sandbox platform. + +config REMOTEPROC_STM32_COPRO + bool "Support for STM32 coprocessor" + select REMOTEPROC + depends on DM + depends on ARCH_STM32MP + depends on OF_CONTROL + help + Say 'y' here to add support for STM32 Cortex-M4 coprocessors via the + remoteproc framework. + +config REMOTEPROC_TI_K3_ARM64 + bool "Support for TI's K3 based ARM64 remoteproc driver" + select REMOTEPROC + depends on DM + depends on ARCH_K3 + depends on OF_CONTROL + help + Say y here to support TI's ARM64 processor subsystems + on various TI K3 family of SoCs through the remote processor + framework. + +config REMOTEPROC_TI_K3_DSP + bool "TI K3 C66 and C71 remoteproc support" + select REMOTEPROC + depends on ARCH_K3 + depends on TI_SCI_PROTOCOL + help + Say y here to support TI's C66/C71 remote processor subsystems + on various TI K3 family of SoCs through the remote processor + framework. + +config REMOTEPROC_TI_K3_R5F + bool "TI K3 R5F remoteproc support" + select REMOTEPROC + depends on ARCH_K3 + depends on TI_SCI_PROTOCOL + help + Say y here to support TI's R5F remote processor subsystems + on various TI K3 family of SoCs through the remote processor + framework. + +config REMOTEPROC_TI_POWER + bool "Support for TI Power processor" + select REMOTEPROC + depends on DM + depends on ARCH_KEYSTONE + depends on OF_CONTROL + help + Say 'y' here to add support for TI power processors such as those + found on certain TI keystone and OMAP generation SoCs. +endmenu diff --git a/roms/u-boot/drivers/remoteproc/Makefile b/roms/u-boot/drivers/remoteproc/Makefile new file mode 100644 index 000000000..69ae7bd1e --- /dev/null +++ b/roms/u-boot/drivers/remoteproc/Makefile @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2015 +# Texas Instruments Incorporated - http://www.ti.com/ +# + +obj-$(CONFIG_$(SPL_)REMOTEPROC) += rproc-uclass.o rproc-elf-loader.o + +# Remote proc drivers - Please keep this list alphabetically sorted. +obj-$(CONFIG_K3_SYSTEM_CONTROLLER) += k3_system_controller.o +obj-$(CONFIG_REMOTEPROC_SANDBOX) += sandbox_testproc.o +obj-$(CONFIG_REMOTEPROC_STM32_COPRO) += stm32_copro.o +obj-$(CONFIG_REMOTEPROC_TI_K3_ARM64) += ti_k3_arm64_rproc.o +obj-$(CONFIG_REMOTEPROC_TI_K3_DSP) += ti_k3_dsp_rproc.o +obj-$(CONFIG_REMOTEPROC_TI_K3_R5F) += ti_k3_r5f_rproc.o +obj-$(CONFIG_REMOTEPROC_TI_POWER) += ti_power_proc.o diff --git a/roms/u-boot/drivers/remoteproc/k3_system_controller.c b/roms/u-boot/drivers/remoteproc/k3_system_controller.c new file mode 100644 index 000000000..89cb90207 --- /dev/null +++ b/roms/u-boot/drivers/remoteproc/k3_system_controller.c @@ -0,0 +1,326 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Texas Instruments' K3 System Controller Driver + * + * Copyright (C) 2017-2018 Texas Instruments Incorporated - http://www.ti.com/ + * Lokesh Vutla <lokeshvutla@ti.com> + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <remoteproc.h> +#include <errno.h> +#include <mailbox.h> +#include <dm/device_compat.h> +#include <linux/soc/ti/k3-sec-proxy.h> + +#define K3_MSG_R5_TO_M3_M3FW 0x8105 +#define K3_MSG_M3_TO_R5_CERT_RESULT 0x8805 +#define K3_MSG_M3_TO_R5_BOOT_NOTIFICATION 0x000A + +#define K3_FLAGS_MSG_CERT_AUTH_PASS 0x555555 +#define K3_FLAGS_MSG_CERT_AUTH_FAIL 0xffffff + +/** + * struct k3_sysctrler_msg_hdr - Generic Header for Messages and responses. + * @cmd_id: Message ID. One of K3_MSG_* + * @host_id: Host ID of the message + * @seq_ne: Message identifier indicating a transfer sequence. + * @flags: Flags for the message. + */ +struct k3_sysctrler_msg_hdr { + u16 cmd_id; + u8 host_id; + u8 seq_nr; + u32 flags; +} __packed; + +/** + * struct k3_sysctrler_load_msg - Message format for Firmware loading + * @hdr: Generic message hdr + * @buffer_address: Address at which firmware is located. + * @buffer_size: Size of the firmware. + */ +struct k3_sysctrler_load_msg { + struct k3_sysctrler_msg_hdr hdr; + u32 buffer_address; + u32 buffer_size; +} __packed; + +/** + * struct k3_sysctrler_boot_notification_msg - Message format for boot + * notification + * @checksum: Checksum for the entire message + * @reserved: Reserved for future use. + * @hdr: Generic message hdr + */ +struct k3_sysctrler_boot_notification_msg { + u16 checksum; + u16 reserved; + struct k3_sysctrler_msg_hdr hdr; +} __packed; + +/** + * struct k3_sysctrler_desc - Description of SoC integration. + * @host_id: Host identifier representing the compute entity + * @max_rx_timeout_ms: Timeout for communication with SoC (in Milliseconds) + * @max_msg_size: Maximum size of data per message that can be handled. + */ +struct k3_sysctrler_desc { + u8 host_id; + int max_rx_timeout_us; + int max_msg_size; +}; + +/** + * struct k3_sysctrler_privdata - Structure representing System Controller data. + * @chan_tx: Transmit mailbox channel + * @chan_rx: Receive mailbox channel + * @desc: SoC description for this instance + * @seq_nr: Counter for number of messages sent. + */ +struct k3_sysctrler_privdata { + struct mbox_chan chan_tx; + struct mbox_chan chan_rx; + struct k3_sysctrler_desc *desc; + u32 seq_nr; +}; + +static inline +void k3_sysctrler_load_msg_setup(struct k3_sysctrler_load_msg *fw, + struct k3_sysctrler_privdata *priv, + ulong addr, ulong size) +{ + fw->hdr.cmd_id = K3_MSG_R5_TO_M3_M3FW; + fw->hdr.host_id = priv->desc->host_id; + fw->hdr.seq_nr = priv->seq_nr++; + fw->hdr.flags = 0x0; + fw->buffer_address = addr; + fw->buffer_size = size; +} + +static int k3_sysctrler_load_response(struct udevice *dev, u32 *buf) +{ + struct k3_sysctrler_load_msg *fw; + + fw = (struct k3_sysctrler_load_msg *)buf; + + /* Check for proper response ID */ + if (fw->hdr.cmd_id != K3_MSG_M3_TO_R5_CERT_RESULT) { + dev_err(dev, "%s: Command expected 0x%x, but received 0x%x\n", + __func__, K3_MSG_M3_TO_R5_CERT_RESULT, fw->hdr.cmd_id); + return -EINVAL; + } + + /* Check for certificate authentication result */ + if (fw->hdr.flags == K3_FLAGS_MSG_CERT_AUTH_FAIL) { + dev_err(dev, "%s: Firmware certificate authentication failed\n", + __func__); + return -EINVAL; + } else if (fw->hdr.flags != K3_FLAGS_MSG_CERT_AUTH_PASS) { + dev_err(dev, "%s: Firmware Load response Invalid %d\n", + __func__, fw->hdr.flags); + return -EINVAL; + } + + debug("%s: Firmware authentication passed\n", __func__); + + return 0; +} + +static int k3_sysctrler_boot_notification_response(struct udevice *dev, + u32 *buf) +{ + struct k3_sysctrler_boot_notification_msg *boot; + + boot = (struct k3_sysctrler_boot_notification_msg *)buf; + + /* ToDo: Verify checksum */ + + /* Check for proper response ID */ + if (boot->hdr.cmd_id != K3_MSG_M3_TO_R5_BOOT_NOTIFICATION) { + dev_err(dev, "%s: Command expected 0x%x, but received 0x%x\n", + __func__, K3_MSG_M3_TO_R5_BOOT_NOTIFICATION, + boot->hdr.cmd_id); + return -EINVAL; + } + + debug("%s: Boot notification received\n", __func__); + + return 0; +} + +/** + * k3_sysctrler_load() - Loadup the K3 remote processor + * @dev: corresponding K3 remote processor device + * @addr: Address in memory where image binary is stored + * @size: Size in bytes of the image binary + * + * Return: 0 if all goes good, else appropriate error message. + */ +static int k3_sysctrler_load(struct udevice *dev, ulong addr, ulong size) +{ + struct k3_sysctrler_privdata *priv = dev_get_priv(dev); + struct k3_sysctrler_load_msg firmware; + struct k3_sec_proxy_msg msg; + int ret; + + debug("%s: Loading binary from 0x%08lX, size 0x%08lX\n", + __func__, addr, size); + + memset(&firmware, 0, sizeof(firmware)); + memset(&msg, 0, sizeof(msg)); + + /* Setup the message */ + k3_sysctrler_load_msg_setup(&firmware, priv, addr, size); + msg.len = sizeof(firmware); + msg.buf = (u32 *)&firmware; + + /* Send the message */ + ret = mbox_send(&priv->chan_tx, &msg); + if (ret) { + dev_err(dev, "%s: Firmware Loading failed. ret = %d\n", + __func__, ret); + return ret; + } + + /* Receive the response */ + ret = mbox_recv(&priv->chan_rx, &msg, priv->desc->max_rx_timeout_us); + if (ret) { + dev_err(dev, "%s: Firmware Load response failed. ret = %d\n", + __func__, ret); + return ret; + } + + /* Process the response */ + ret = k3_sysctrler_load_response(dev, msg.buf); + if (ret) + return ret; + + debug("%s: Firmware Loaded successfully on dev %s\n", + __func__, dev->name); + + return 0; +} + +/** + * k3_sysctrler_start() - Start the remote processor + * Note that while technically the K3 system controller starts up + * automatically after its firmware got loaded we still want to + * utilize the rproc start operation for other startup-related + * tasks. + * @dev: device to operate upon + * + * Return: 0 if all went ok, else return appropriate error + */ +static int k3_sysctrler_start(struct udevice *dev) +{ + struct k3_sysctrler_privdata *priv = dev_get_priv(dev); + struct k3_sec_proxy_msg msg; + int ret; + + debug("%s(dev=%p)\n", __func__, dev); + + /* Receive the boot notification. Note that it is sent only once. */ + ret = mbox_recv(&priv->chan_rx, &msg, priv->desc->max_rx_timeout_us); + if (ret) { + dev_err(dev, "%s: Boot Notification response failed. ret = %d\n", + __func__, ret); + return ret; + } + + /* Process the response */ + ret = k3_sysctrler_boot_notification_response(dev, msg.buf); + if (ret) + return ret; + + debug("%s: Boot notification received successfully on dev %s\n", + __func__, dev->name); + + return 0; +} + +static const struct dm_rproc_ops k3_sysctrler_ops = { + .load = k3_sysctrler_load, + .start = k3_sysctrler_start, +}; + +/** + * k3_of_to_priv() - generate private data from device tree + * @dev: corresponding k3 remote processor device + * @priv: pointer to driver specific private data + * + * Return: 0 if all goes good, else appropriate error message. + */ +static int k3_of_to_priv(struct udevice *dev, + struct k3_sysctrler_privdata *priv) +{ + int ret; + + ret = mbox_get_by_name(dev, "tx", &priv->chan_tx); + if (ret) { + dev_err(dev, "%s: Acquiring Tx channel failed. ret = %d\n", + __func__, ret); + return ret; + } + + ret = mbox_get_by_name(dev, "rx", &priv->chan_rx); + if (ret) { + dev_err(dev, "%s: Acquiring Rx channel failed. ret = %d\n", + __func__, ret); + return ret; + } + + return 0; +} + +/** + * k3_sysctrler_probe() - Basic probe + * @dev: corresponding k3 remote processor device + * + * Return: 0 if all goes good, else appropriate error message. + */ +static int k3_sysctrler_probe(struct udevice *dev) +{ + struct k3_sysctrler_privdata *priv; + int ret; + + debug("%s(dev=%p)\n", __func__, dev); + + priv = dev_get_priv(dev); + + ret = k3_of_to_priv(dev, priv); + if (ret) { + dev_err(dev, "%s: Probe failed with error %d\n", __func__, ret); + return ret; + } + + priv->desc = (void *)dev_get_driver_data(dev); + priv->seq_nr = 0; + + return 0; +} + +static const struct k3_sysctrler_desc k3_sysctrler_am654_desc = { + .host_id = 4, /* HOST_ID_R5_1 */ + .max_rx_timeout_us = 800000, + .max_msg_size = 60, +}; + +static const struct udevice_id k3_sysctrler_ids[] = { + { + .compatible = "ti,am654-system-controller", + .data = (ulong)&k3_sysctrler_am654_desc, + }, + {} +}; + +U_BOOT_DRIVER(k3_sysctrler) = { + .name = "k3_system_controller", + .of_match = k3_sysctrler_ids, + .id = UCLASS_REMOTEPROC, + .ops = &k3_sysctrler_ops, + .probe = k3_sysctrler_probe, + .priv_auto = sizeof(struct k3_sysctrler_privdata), +}; diff --git a/roms/u-boot/drivers/remoteproc/rproc-elf-loader.c b/roms/u-boot/drivers/remoteproc/rproc-elf-loader.c new file mode 100644 index 000000000..b185a6caf --- /dev/null +++ b/roms/u-boot/drivers/remoteproc/rproc-elf-loader.c @@ -0,0 +1,534 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2019, STMicroelectronics - All Rights Reserved + */ +#include <common.h> +#include <cpu_func.h> +#include <dm.h> +#include <elf.h> +#include <log.h> +#include <remoteproc.h> +#include <asm/cache.h> +#include <dm/device_compat.h> +#include <linux/compat.h> + +/** + * struct resource_table - firmware resource table header + * @ver: version number + * @num: number of resource entries + * @reserved: reserved (must be zero) + * @offset: array of offsets pointing at the various resource entries + * + * A resource table is essentially a list of system resources required + * by the remote processor. It may also include configuration entries. + * If needed, the remote processor firmware should contain this table + * as a dedicated ".resource_table" ELF section. + * + * Some resources entries are mere announcements, where the host is informed + * of specific remoteproc configuration. Other entries require the host to + * do something (e.g. allocate a system resource). Sometimes a negotiation + * is expected, where the firmware requests a resource, and once allocated, + * the host should provide back its details (e.g. address of an allocated + * memory region). + * + * The header of the resource table, as expressed by this structure, + * contains a version number (should we need to change this format in the + * future), the number of available resource entries, and their offsets + * in the table. + * + * Immediately following this header are the resource entries themselves. + */ +struct resource_table { + u32 ver; + u32 num; + u32 reserved[2]; + u32 offset[0]; +} __packed; + +/* Basic function to verify ELF32 image format */ +int rproc_elf32_sanity_check(ulong addr, ulong size) +{ + Elf32_Ehdr *ehdr; + char class; + + if (!addr) { + pr_debug("Invalid fw address?\n"); + return -EFAULT; + } + + if (size < sizeof(Elf32_Ehdr)) { + pr_debug("Image is too small\n"); + return -ENOSPC; + } + + ehdr = (Elf32_Ehdr *)addr; + class = ehdr->e_ident[EI_CLASS]; + + if (!IS_ELF(*ehdr) || ehdr->e_type != ET_EXEC || class != ELFCLASS32) { + pr_debug("Not an executable ELF32 image\n"); + return -EPROTONOSUPPORT; + } + + /* We assume the firmware has the same endianness as the host */ +# ifdef __LITTLE_ENDIAN + if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) { +# else /* BIG ENDIAN */ + if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB) { +# endif + pr_debug("Unsupported firmware endianness\n"); + return -EILSEQ; + } + + if (size < ehdr->e_shoff + sizeof(Elf32_Shdr)) { + pr_debug("Image is too small\n"); + return -ENOSPC; + } + + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) { + pr_debug("Image is corrupted (bad magic)\n"); + return -EBADF; + } + + if (ehdr->e_phnum == 0) { + pr_debug("No loadable segments\n"); + return -ENOEXEC; + } + + if (ehdr->e_phoff > size) { + pr_debug("Firmware size is too small\n"); + return -ENOSPC; + } + + return 0; +} + +/* Basic function to verify ELF64 image format */ +int rproc_elf64_sanity_check(ulong addr, ulong size) +{ + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)addr; + char class; + + if (!addr) { + pr_debug("Invalid fw address?\n"); + return -EFAULT; + } + + if (size < sizeof(Elf64_Ehdr)) { + pr_debug("Image is too small\n"); + return -ENOSPC; + } + + class = ehdr->e_ident[EI_CLASS]; + + if (!IS_ELF(*ehdr) || ehdr->e_type != ET_EXEC || class != ELFCLASS64) { + pr_debug("Not an executable ELF64 image\n"); + return -EPROTONOSUPPORT; + } + + /* We assume the firmware has the same endianness as the host */ +# ifdef __LITTLE_ENDIAN + if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) { +# else /* BIG ENDIAN */ + if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB) { +# endif + pr_debug("Unsupported firmware endianness\n"); + return -EILSEQ; + } + + if (size < ehdr->e_shoff + sizeof(Elf64_Shdr)) { + pr_debug("Image is too small\n"); + return -ENOSPC; + } + + if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) { + pr_debug("Image is corrupted (bad magic)\n"); + return -EBADF; + } + + if (ehdr->e_phnum == 0) { + pr_debug("No loadable segments\n"); + return -ENOEXEC; + } + + if (ehdr->e_phoff > size) { + pr_debug("Firmware size is too small\n"); + return -ENOSPC; + } + + return 0; +} + +int rproc_elf32_load_image(struct udevice *dev, unsigned long addr, ulong size) +{ + Elf32_Ehdr *ehdr; /* Elf header structure pointer */ + Elf32_Phdr *phdr; /* Program header structure pointer */ + const struct dm_rproc_ops *ops; + unsigned int i, ret; + + ret = rproc_elf32_sanity_check(addr, size); + if (ret) { + dev_err(dev, "Invalid ELF32 Image %d\n", ret); + return ret; + } + + ehdr = (Elf32_Ehdr *)addr; + phdr = (Elf32_Phdr *)(addr + ehdr->e_phoff); + + ops = rproc_get_ops(dev); + + /* Load each program header */ + for (i = 0; i < ehdr->e_phnum; i++, phdr++) { + void *dst = (void *)(uintptr_t)phdr->p_paddr; + void *src = (void *)addr + phdr->p_offset; + + if (phdr->p_type != PT_LOAD) + continue; + + if (ops->device_to_virt) + dst = ops->device_to_virt(dev, (ulong)dst, + phdr->p_memsz); + + dev_dbg(dev, "Loading phdr %i to 0x%p (%i bytes)\n", + i, dst, phdr->p_filesz); + if (phdr->p_filesz) + memcpy(dst, src, phdr->p_filesz); + if (phdr->p_filesz != phdr->p_memsz) + memset(dst + phdr->p_filesz, 0x00, + phdr->p_memsz - phdr->p_filesz); + flush_cache(rounddown((unsigned long)dst, ARCH_DMA_MINALIGN), + roundup((unsigned long)dst + phdr->p_filesz, + ARCH_DMA_MINALIGN) - + rounddown((unsigned long)dst, ARCH_DMA_MINALIGN)); + } + + return 0; +} + +int rproc_elf64_load_image(struct udevice *dev, ulong addr, ulong size) +{ + const struct dm_rproc_ops *ops = rproc_get_ops(dev); + u64 da, memsz, filesz, offset; + Elf64_Ehdr *ehdr; + Elf64_Phdr *phdr; + int i, ret = 0; + void *ptr; + + dev_dbg(dev, "%s: addr = 0x%lx size = 0x%lx\n", __func__, addr, size); + + if (rproc_elf64_sanity_check(addr, size)) + return -EINVAL; + + ehdr = (Elf64_Ehdr *)addr; + phdr = (Elf64_Phdr *)(addr + (ulong)ehdr->e_phoff); + + /* go through the available ELF segments */ + for (i = 0; i < ehdr->e_phnum; i++, phdr++) { + da = phdr->p_paddr; + memsz = phdr->p_memsz; + filesz = phdr->p_filesz; + offset = phdr->p_offset; + + if (phdr->p_type != PT_LOAD) + continue; + + dev_dbg(dev, "%s:phdr: type %d da 0x%llx memsz 0x%llx filesz 0x%llx\n", + __func__, phdr->p_type, da, memsz, filesz); + + ptr = (void *)(uintptr_t)da; + if (ops->device_to_virt) { + ptr = ops->device_to_virt(dev, da, phdr->p_memsz); + if (!ptr) { + dev_err(dev, "bad da 0x%llx mem 0x%llx\n", da, + memsz); + ret = -EINVAL; + break; + } + } + + if (filesz) + memcpy(ptr, (void *)addr + offset, filesz); + if (filesz != memsz) + memset(ptr + filesz, 0x00, memsz - filesz); + + flush_cache(rounddown((ulong)ptr, ARCH_DMA_MINALIGN), + roundup((ulong)ptr + filesz, ARCH_DMA_MINALIGN) - + rounddown((ulong)ptr, ARCH_DMA_MINALIGN)); + } + + return ret; +} + +int rproc_elf_load_image(struct udevice *dev, ulong addr, ulong size) +{ + Elf32_Ehdr *ehdr = (Elf32_Ehdr *)addr; + + if (!addr) { + dev_err(dev, "Invalid firmware address\n"); + return -EFAULT; + } + + if (ehdr->e_ident[EI_CLASS] == ELFCLASS64) + return rproc_elf64_load_image(dev, addr, size); + else + return rproc_elf32_load_image(dev, addr, size); +} + +static ulong rproc_elf32_get_boot_addr(ulong addr) +{ + Elf32_Ehdr *ehdr = (Elf32_Ehdr *)addr; + + return ehdr->e_entry; +} + +static ulong rproc_elf64_get_boot_addr(ulong addr) +{ + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)addr; + + return ehdr->e_entry; +} + +ulong rproc_elf_get_boot_addr(struct udevice *dev, ulong addr) +{ + Elf32_Ehdr *ehdr = (Elf32_Ehdr *)addr; + + if (ehdr->e_ident[EI_CLASS] == ELFCLASS64) + return rproc_elf64_get_boot_addr(addr); + else + return rproc_elf32_get_boot_addr(addr); +} + +/* + * Search for the resource table in an ELF32 image. + * Returns the address of the resource table section if found, NULL if there is + * no resource table section, or error pointer. + */ +static Elf32_Shdr *rproc_elf32_find_rsc_table(struct udevice *dev, + ulong fw_addr, ulong fw_size) +{ + int ret; + unsigned int i; + const char *name_table; + struct resource_table *table; + const u8 *elf_data = (void *)fw_addr; + Elf32_Ehdr *ehdr = (Elf32_Ehdr *)fw_addr; + Elf32_Shdr *shdr; + + ret = rproc_elf32_sanity_check(fw_addr, fw_size); + if (ret) { + pr_debug("Invalid ELF32 Image %d\n", ret); + return ERR_PTR(ret); + } + + /* look for the resource table and handle it */ + shdr = (Elf32_Shdr *)(elf_data + ehdr->e_shoff); + name_table = (const char *)(elf_data + + shdr[ehdr->e_shstrndx].sh_offset); + + for (i = 0; i < ehdr->e_shnum; i++, shdr++) { + u32 size = shdr->sh_size; + u32 offset = shdr->sh_offset; + + if (strcmp(name_table + shdr->sh_name, ".resource_table")) + continue; + + table = (struct resource_table *)(elf_data + offset); + + /* make sure we have the entire table */ + if (offset + size > fw_size) { + pr_debug("resource table truncated\n"); + return ERR_PTR(-ENOSPC); + } + + /* make sure table has at least the header */ + if (sizeof(*table) > size) { + pr_debug("header-less resource table\n"); + return ERR_PTR(-ENOSPC); + } + + /* we don't support any version beyond the first */ + if (table->ver != 1) { + pr_debug("unsupported fw ver: %d\n", table->ver); + return ERR_PTR(-EPROTONOSUPPORT); + } + + /* make sure reserved bytes are zeroes */ + if (table->reserved[0] || table->reserved[1]) { + pr_debug("non zero reserved bytes\n"); + return ERR_PTR(-EBADF); + } + + /* make sure the offsets array isn't truncated */ + if (table->num * sizeof(table->offset[0]) + + sizeof(*table) > size) { + pr_debug("resource table incomplete\n"); + return ERR_PTR(-ENOSPC); + } + + return shdr; + } + + return NULL; +} + +/* Load the resource table from an ELF32 image */ +int rproc_elf32_load_rsc_table(struct udevice *dev, ulong fw_addr, + ulong fw_size, ulong *rsc_addr, ulong *rsc_size) +{ + const struct dm_rproc_ops *ops; + Elf32_Shdr *shdr; + void *src, *dst; + + shdr = rproc_elf32_find_rsc_table(dev, fw_addr, fw_size); + if (!shdr) + return -ENODATA; + if (IS_ERR(shdr)) + return PTR_ERR(shdr); + + ops = rproc_get_ops(dev); + *rsc_addr = (ulong)shdr->sh_addr; + *rsc_size = (ulong)shdr->sh_size; + + src = (void *)fw_addr + shdr->sh_offset; + if (ops->device_to_virt) + dst = (void *)ops->device_to_virt(dev, *rsc_addr, *rsc_size); + else + dst = (void *)rsc_addr; + + dev_dbg(dev, "Loading resource table to 0x%8lx (%ld bytes)\n", + (ulong)dst, *rsc_size); + + memcpy(dst, src, *rsc_size); + flush_cache(rounddown((unsigned long)dst, ARCH_DMA_MINALIGN), + roundup((unsigned long)dst + *rsc_size, + ARCH_DMA_MINALIGN) - + rounddown((unsigned long)dst, ARCH_DMA_MINALIGN)); + + return 0; +} + +/* + * Search for the resource table in an ELF64 image. + * Returns the address of the resource table section if found, NULL if there is + * no resource table section, or error pointer. + */ +static Elf64_Shdr *rproc_elf64_find_rsc_table(struct udevice *dev, + ulong fw_addr, ulong fw_size) +{ + int ret; + unsigned int i; + const char *name_table; + struct resource_table *table; + const u8 *elf_data = (void *)fw_addr; + Elf64_Ehdr *ehdr = (Elf64_Ehdr *)fw_addr; + Elf64_Shdr *shdr; + + ret = rproc_elf64_sanity_check(fw_addr, fw_size); + if (ret) { + pr_debug("Invalid ELF64 Image %d\n", ret); + return ERR_PTR(ret); + } + + /* look for the resource table and handle it */ + shdr = (Elf64_Shdr *)(elf_data + ehdr->e_shoff); + name_table = (const char *)(elf_data + + shdr[ehdr->e_shstrndx].sh_offset); + + for (i = 0; i < ehdr->e_shnum; i++, shdr++) { + u64 size = shdr->sh_size; + u64 offset = shdr->sh_offset; + + if (strcmp(name_table + shdr->sh_name, ".resource_table")) + continue; + + table = (struct resource_table *)(elf_data + offset); + + /* make sure we have the entire table */ + if (offset + size > fw_size) { + pr_debug("resource table truncated\n"); + return ERR_PTR(-ENOSPC); + } + + /* make sure table has at least the header */ + if (sizeof(*table) > size) { + pr_debug("header-less resource table\n"); + return ERR_PTR(-ENOSPC); + } + + /* we don't support any version beyond the first */ + if (table->ver != 1) { + pr_debug("unsupported fw ver: %d\n", table->ver); + return ERR_PTR(-EPROTONOSUPPORT); + } + + /* make sure reserved bytes are zeroes */ + if (table->reserved[0] || table->reserved[1]) { + pr_debug("non zero reserved bytes\n"); + return ERR_PTR(-EBADF); + } + + /* make sure the offsets array isn't truncated */ + if (table->num * sizeof(table->offset[0]) + + sizeof(*table) > size) { + pr_debug("resource table incomplete\n"); + return ERR_PTR(-ENOSPC); + } + + return shdr; + } + + return NULL; +} + +/* Load the resource table from an ELF64 image */ +int rproc_elf64_load_rsc_table(struct udevice *dev, ulong fw_addr, + ulong fw_size, ulong *rsc_addr, ulong *rsc_size) +{ + const struct dm_rproc_ops *ops; + Elf64_Shdr *shdr; + void *src, *dst; + + shdr = rproc_elf64_find_rsc_table(dev, fw_addr, fw_size); + if (!shdr) + return -ENODATA; + if (IS_ERR(shdr)) + return PTR_ERR(shdr); + + ops = rproc_get_ops(dev); + *rsc_addr = (ulong)shdr->sh_addr; + *rsc_size = (ulong)shdr->sh_size; + + src = (void *)fw_addr + shdr->sh_offset; + if (ops->device_to_virt) + dst = (void *)ops->device_to_virt(dev, *rsc_addr, *rsc_size); + else + dst = (void *)rsc_addr; + + dev_dbg(dev, "Loading resource table to 0x%8lx (%ld bytes)\n", + (ulong)dst, *rsc_size); + + memcpy(dst, src, *rsc_size); + flush_cache(rounddown((unsigned long)dst, ARCH_DMA_MINALIGN), + roundup((unsigned long)dst + *rsc_size, + ARCH_DMA_MINALIGN) - + rounddown((unsigned long)dst, ARCH_DMA_MINALIGN)); + + return 0; +} + +/* Load the resource table from an ELF32 or ELF64 image */ +int rproc_elf_load_rsc_table(struct udevice *dev, ulong fw_addr, + ulong fw_size, ulong *rsc_addr, ulong *rsc_size) + +{ + Elf32_Ehdr *ehdr = (Elf32_Ehdr *)fw_addr; + + if (!fw_addr) + return -EFAULT; + + if (ehdr->e_ident[EI_CLASS] == ELFCLASS64) + return rproc_elf64_load_rsc_table(dev, fw_addr, fw_size, + rsc_addr, rsc_size); + else + return rproc_elf32_load_rsc_table(dev, fw_addr, fw_size, + rsc_addr, rsc_size); +} diff --git a/roms/u-boot/drivers/remoteproc/rproc-uclass.c b/roms/u-boot/drivers/remoteproc/rproc-uclass.c new file mode 100644 index 000000000..5919c8bb9 --- /dev/null +++ b/roms/u-boot/drivers/remoteproc/rproc-uclass.c @@ -0,0 +1,437 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 + * Texas Instruments Incorporated - http://www.ti.com/ + */ +#define pr_fmt(fmt) "%s: " fmt, __func__ +#include <common.h> +#include <errno.h> +#include <fdtdec.h> +#include <log.h> +#include <malloc.h> +#include <remoteproc.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <dm/device-internal.h> +#include <dm.h> +#include <dm/uclass.h> +#include <dm/uclass-internal.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * for_each_remoteproc_device() - iterate through the list of rproc devices + * @fn: check function to call per match, if this function returns fail, + * iteration is aborted with the resultant error value + * @skip_dev: Device to skip calling the callback about. + * @data: Data to pass to the callback function + * + * Return: 0 if none of the callback returned a non 0 result, else returns the + * result from the callback function + */ +static int for_each_remoteproc_device(int (*fn) (struct udevice *dev, + struct dm_rproc_uclass_pdata *uc_pdata, + const void *data), + struct udevice *skip_dev, + const void *data) +{ + struct udevice *dev; + struct dm_rproc_uclass_pdata *uc_pdata; + int ret; + + for (ret = uclass_find_first_device(UCLASS_REMOTEPROC, &dev); dev; + ret = uclass_find_next_device(&dev)) { + if (ret || dev == skip_dev) + continue; + uc_pdata = dev_get_uclass_plat(dev); + ret = fn(dev, uc_pdata, data); + if (ret) + return ret; + } + + return 0; +} + +/** + * _rproc_name_is_unique() - iteration helper to check if rproc name is unique + * @dev: device that we are checking name for + * @uc_pdata: uclass platform data + * @data: compare data (this is the name we want to ensure is unique) + * + * Return: 0 is there is no match(is unique); if there is a match(we dont + * have a unique name), return -EINVAL. + */ +static int _rproc_name_is_unique(struct udevice *dev, + struct dm_rproc_uclass_pdata *uc_pdata, + const void *data) +{ + const char *check_name = data; + + /* devices not yet populated with data - so skip them */ + if (!uc_pdata->name || !check_name) + return 0; + + /* Return 0 to search further if we dont match */ + if (strlen(uc_pdata->name) != strlen(check_name)) + return 0; + + if (!strcmp(uc_pdata->name, check_name)) + return -EINVAL; + + return 0; +} + +/** + * rproc_name_is_unique() - Check if the rproc name is unique + * @check_dev: Device we are attempting to ensure is unique + * @check_name: Name we are trying to ensure is unique. + * + * Return: true if we have a unique name, false if name is not unique. + */ +static bool rproc_name_is_unique(struct udevice *check_dev, + const char *check_name) +{ + int ret; + + ret = for_each_remoteproc_device(_rproc_name_is_unique, + check_dev, check_name); + return ret ? false : true; +} + +/** + * rproc_pre_probe() - Pre probe accessor for the uclass + * @dev: device for which we are preprobing + * + * Parses and fills up the uclass pdata for use as needed by core and + * remote proc drivers. + * + * Return: 0 if all wernt ok, else appropriate error value. + */ +static int rproc_pre_probe(struct udevice *dev) +{ + struct dm_rproc_uclass_pdata *uc_pdata; + const struct dm_rproc_ops *ops; + + uc_pdata = dev_get_uclass_plat(dev); + + /* See if we need to populate via fdt */ + + if (!dev_get_plat(dev)) { +#if CONFIG_IS_ENABLED(OF_CONTROL) + int node = dev_of_offset(dev); + const void *blob = gd->fdt_blob; + bool tmp; + if (!blob) { + debug("'%s' no dt?\n", dev->name); + return -EINVAL; + } + debug("'%s': using fdt\n", dev->name); + uc_pdata->name = fdt_getprop(blob, node, + "remoteproc-name", NULL); + + /* Default is internal memory mapped */ + uc_pdata->mem_type = RPROC_INTERNAL_MEMORY_MAPPED; + tmp = fdtdec_get_bool(blob, node, + "remoteproc-internal-memory-mapped"); + if (tmp) + uc_pdata->mem_type = RPROC_INTERNAL_MEMORY_MAPPED; +#else + /* Nothing much we can do about this, can we? */ + return -EINVAL; +#endif + + } else { + struct dm_rproc_uclass_pdata *pdata = dev_get_plat(dev); + + debug("'%s': using legacy data\n", dev->name); + if (pdata->name) + uc_pdata->name = pdata->name; + uc_pdata->mem_type = pdata->mem_type; + uc_pdata->driver_plat_data = pdata->driver_plat_data; + } + + /* Else try using device Name */ + if (!uc_pdata->name) + uc_pdata->name = dev->name; + if (!uc_pdata->name) { + debug("Unnamed device!"); + return -EINVAL; + } + + if (!rproc_name_is_unique(dev, uc_pdata->name)) { + debug("%s duplicate name '%s'\n", dev->name, uc_pdata->name); + return -EINVAL; + } + + ops = rproc_get_ops(dev); + if (!ops) { + debug("%s driver has no ops?\n", dev->name); + return -EINVAL; + } + + if (!ops->load || !ops->start) { + debug("%s driver has missing mandatory ops?\n", dev->name); + return -EINVAL; + } + + return 0; +} + +/** + * rproc_post_probe() - post probe accessor for the uclass + * @dev: deivce we finished probing + * + * initiate init function after the probe is completed. This allows + * the remote processor drivers to split up the initializations between + * probe and init as needed. + * + * Return: if the remote proc driver has a init routine, invokes it and + * hands over the return value. overall, 0 if all went well, else appropriate + * error value. + */ +static int rproc_post_probe(struct udevice *dev) +{ + const struct dm_rproc_ops *ops; + + ops = rproc_get_ops(dev); + if (!ops) { + debug("%s driver has no ops?\n", dev->name); + return -EINVAL; + } + + if (ops->init) + return ops->init(dev); + + return 0; +} + +UCLASS_DRIVER(rproc) = { + .id = UCLASS_REMOTEPROC, + .name = "remoteproc", + .flags = DM_UC_FLAG_SEQ_ALIAS, + .pre_probe = rproc_pre_probe, + .post_probe = rproc_post_probe, + .per_device_plat_auto = sizeof(struct dm_rproc_uclass_pdata), +}; + +/* Remoteproc subsystem access functions */ +/** + * _rproc_probe_dev() - iteration helper to probe a rproc device + * @dev: device to probe + * @uc_pdata: uclass data allocated for the device + * @data: unused + * + * Return: 0 if all ok, else appropriate error value. + */ +static int _rproc_probe_dev(struct udevice *dev, + struct dm_rproc_uclass_pdata *uc_pdata, + const void *data) +{ + int ret; + + ret = device_probe(dev); + + if (ret) + debug("%s: Failed to initialize - %d\n", dev->name, ret); + return ret; +} + +/** + * _rproc_dev_is_probed() - check if the device has been probed + * @dev: device to check + * @uc_pdata: unused + * @data: unused + * + * Return: -EAGAIN if not probed else return 0 + */ +static int _rproc_dev_is_probed(struct udevice *dev, + struct dm_rproc_uclass_pdata *uc_pdata, + const void *data) +{ + if (dev_get_flags(dev) & DM_FLAG_ACTIVATED) + return 0; + + return -EAGAIN; +} + +bool rproc_is_initialized(void) +{ + int ret = for_each_remoteproc_device(_rproc_dev_is_probed, NULL, NULL); + return ret ? false : true; +} + +int rproc_init(void) +{ + int ret; + + if (rproc_is_initialized()) { + debug("Already initialized\n"); + return -EINVAL; + } + + ret = for_each_remoteproc_device(_rproc_probe_dev, NULL, NULL); + return ret; +} + +int rproc_dev_init(int id) +{ + struct udevice *dev = NULL; + int ret; + + ret = uclass_get_device_by_seq(UCLASS_REMOTEPROC, id, &dev); + if (ret) { + debug("Unknown remote processor id '%d' requested(%d)\n", + id, ret); + return ret; + } + + ret = device_probe(dev); + if (ret) + debug("%s: Failed to initialize - %d\n", dev->name, ret); + + return ret; +} + +int rproc_load(int id, ulong addr, ulong size) +{ + struct udevice *dev = NULL; + struct dm_rproc_uclass_pdata *uc_pdata; + const struct dm_rproc_ops *ops; + int ret; + + ret = uclass_get_device_by_seq(UCLASS_REMOTEPROC, id, &dev); + if (ret) { + debug("Unknown remote processor id '%d' requested(%d)\n", + id, ret); + return ret; + } + + uc_pdata = dev_get_uclass_plat(dev); + + ops = rproc_get_ops(dev); + if (!ops) { + debug("%s driver has no ops?\n", dev->name); + return -EINVAL; + } + + debug("Loading to '%s' from address 0x%08lX size of %lu bytes\n", + uc_pdata->name, addr, size); + if (ops->load) + return ops->load(dev, addr, size); + + debug("%s: data corruption?? mandatory function is missing!\n", + dev->name); + + return -EINVAL; +}; + +/* + * Completely internal helper enums.. + * Keeping this isolated helps this code evolve independent of other + * parts.. + */ +enum rproc_ops { + RPROC_START, + RPROC_STOP, + RPROC_RESET, + RPROC_PING, + RPROC_RUNNING, +}; + +/** + * _rproc_ops_wrapper() - wrapper for invoking remote proc driver callback + * @id: id of the remote processor + * @op: one of rproc_ops that indicate what operation to invoke + * + * Most of the checks and verification for remoteproc operations are more + * or less same for almost all operations. This allows us to put a wrapper + * and use the common checks to allow the driver to function appropriately. + * + * Return: 0 if all ok, else appropriate error value. + */ +static int _rproc_ops_wrapper(int id, enum rproc_ops op) +{ + struct udevice *dev = NULL; + struct dm_rproc_uclass_pdata *uc_pdata; + const struct dm_rproc_ops *ops; + int (*fn)(struct udevice *dev); + bool mandatory = false; + char *op_str; + int ret; + + ret = uclass_get_device_by_seq(UCLASS_REMOTEPROC, id, &dev); + if (ret) { + debug("Unknown remote processor id '%d' requested(%d)\n", + id, ret); + return ret; + } + + uc_pdata = dev_get_uclass_plat(dev); + + ops = rproc_get_ops(dev); + if (!ops) { + debug("%s driver has no ops?\n", dev->name); + return -EINVAL; + } + switch (op) { + case RPROC_START: + fn = ops->start; + mandatory = true; + op_str = "Starting"; + break; + case RPROC_STOP: + fn = ops->stop; + op_str = "Stopping"; + break; + case RPROC_RESET: + fn = ops->reset; + op_str = "Resetting"; + break; + case RPROC_RUNNING: + fn = ops->is_running; + op_str = "Checking if running:"; + break; + case RPROC_PING: + fn = ops->ping; + op_str = "Pinging"; + break; + default: + debug("what is '%d' operation??\n", op); + return -EINVAL; + } + + debug("%s %s...\n", op_str, uc_pdata->name); + if (fn) + return fn(dev); + + if (mandatory) + debug("%s: data corruption?? mandatory function is missing!\n", + dev->name); + + return -ENOSYS; +} + +int rproc_start(int id) +{ + return _rproc_ops_wrapper(id, RPROC_START); +}; + +int rproc_stop(int id) +{ + return _rproc_ops_wrapper(id, RPROC_STOP); +}; + +int rproc_reset(int id) +{ + return _rproc_ops_wrapper(id, RPROC_RESET); +}; + +int rproc_ping(int id) +{ + return _rproc_ops_wrapper(id, RPROC_PING); +}; + +int rproc_is_running(int id) +{ + return _rproc_ops_wrapper(id, RPROC_RUNNING); +}; diff --git a/roms/u-boot/drivers/remoteproc/sandbox_testproc.c b/roms/u-boot/drivers/remoteproc/sandbox_testproc.c new file mode 100644 index 000000000..6836eca4c --- /dev/null +++ b/roms/u-boot/drivers/remoteproc/sandbox_testproc.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015 + * Texas Instruments Incorporated - http://www.ti.com/ + */ +#define pr_fmt(fmt) "%s: " fmt, __func__ +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <log.h> +#include <remoteproc.h> +#include <asm/io.h> + +/** + * enum sandbox_state - different device states + * @sb_booted: Entry condition, just booted + * @sb_init: Initialized (basic environment is ready) + * @sb_reset: Held in reset (accessible, but not running) + * @sb_loaded: Loaded with image (but not running) + * @sb_running: Processor is running + */ +enum sandbox_state { + sb_booted, + sb_init, + sb_reset, + sb_loaded, + sb_running +}; + +/** + * struct sandbox_test_devdata - private data per device + * @current_state: device current state + */ +struct sandbox_test_devdata { + enum sandbox_state current_state; +}; + +/** + * sandbox_dev_move_to_state() - statemachine for our dummy device + * @dev: device to switch state + * @next_state: next proposed state + * + * This tries to follow the following statemachine: + * Entry + * | + * v + * +-------+ + * +---+ init | + * | | | <---------------------+ + * | +-------+ | + * | | + * | | + * | +--------+ | + * Load| | reset | | + * | | | <----------+ | + * | +--------+ | | + * | |Load | | + * | | | | + * | +----v----+ reset | | + * +-> | | (opt) | | + * | Loaded +-----------+ | + * | | | + * +----+----+ | + * | Start | + * +---v-----+ (opt) | + * +->| Running | Stop | + * Ping +- | +--------------------+ + * (opt) +---------+ + * + * (is_running does not change state) + * + * Return: 0 when valid state transition is seen, else returns -EINVAL + */ +static int sandbox_dev_move_to_state(struct udevice *dev, + enum sandbox_state next_state) +{ + struct sandbox_test_devdata *ddata = dev_get_priv(dev); + + /* No state transition is OK */ + if (ddata->current_state == next_state) + return 0; + + debug("current_state=%d, next_state=%d\n", ddata->current_state, + next_state); + switch (ddata->current_state) { + case sb_booted: + if (next_state == sb_init) + goto ok_state; + break; + + case sb_init: + if (next_state == sb_reset || next_state == sb_loaded) + goto ok_state; + break; + + case sb_reset: + if (next_state == sb_loaded || next_state == sb_init) + goto ok_state; + break; + + case sb_loaded: + if (next_state == sb_reset || next_state == sb_init || + next_state == sb_running) + goto ok_state; + break; + + case sb_running: + if (next_state == sb_reset || next_state == sb_init) + goto ok_state; + break; + }; + return -EINVAL; + +ok_state: + ddata->current_state = next_state; + return 0; +} + +/** + * sandbox_testproc_probe() - basic probe function + * @dev: test proc device that is being probed. + * + * Return: 0 if all went ok, else return appropriate error + */ +static int sandbox_testproc_probe(struct udevice *dev) +{ + struct dm_rproc_uclass_pdata *uc_pdata; + struct sandbox_test_devdata *ddata; + int ret; + + uc_pdata = dev_get_uclass_plat(dev); + ddata = dev_get_priv(dev); + if (!ddata) { + debug("%s: platform private data missing\n", uc_pdata->name); + return -EINVAL; + } + ret = sandbox_dev_move_to_state(dev, sb_booted); + debug("%s: called(%d)\n", uc_pdata->name, ret); + + return ret; +} + +/** + * sandbox_testproc_init() - Simple initialization function + * @dev: device to operate upon + * + * Return: 0 if all went ok, else return appropriate error + */ +static int sandbox_testproc_init(struct udevice *dev) +{ + struct dm_rproc_uclass_pdata *uc_pdata; + int ret; + + uc_pdata = dev_get_uclass_plat(dev); + + ret = sandbox_dev_move_to_state(dev, sb_init); + + debug("%s: called(%d)\n", uc_pdata->name, ret); + if (ret) + debug("%s init failed\n", uc_pdata->name); + + return ret; +} + +/** + * sandbox_testproc_reset() - Reset the remote processor + * @dev: device to operate upon + * + * Return: 0 if all went ok, else return appropriate error + */ +static int sandbox_testproc_reset(struct udevice *dev) +{ + struct dm_rproc_uclass_pdata *uc_pdata; + int ret; + + uc_pdata = dev_get_uclass_plat(dev); + + ret = sandbox_dev_move_to_state(dev, sb_reset); + + debug("%s: called(%d)\n", uc_pdata->name, ret); + + if (ret) + debug("%s reset failed\n", uc_pdata->name); + return ret; +} + +/** + * sandbox_testproc_load() - (replace: short desc) + * @dev: device to operate upon + * @addr: Address of the binary image to load + * @size: Size (in bytes) of the binary image to load + * + * Return: 0 if all went ok, else return appropriate error + */ +static int sandbox_testproc_load(struct udevice *dev, ulong addr, ulong size) +{ + struct dm_rproc_uclass_pdata *uc_pdata; + int ret; + + uc_pdata = dev_get_uclass_plat(dev); + + ret = sandbox_dev_move_to_state(dev, sb_loaded); + + debug("%s: called(%d) Loading to %08lX %lu size\n", + uc_pdata->name, ret, addr, size); + + if (ret) + debug("%s load failed\n", uc_pdata->name); + return ret; +} + +/** + * sandbox_testproc_start() - Start the remote processor + * @dev: device to operate upon + * + * Return: 0 if all went ok, else return appropriate error + */ +static int sandbox_testproc_start(struct udevice *dev) +{ + struct dm_rproc_uclass_pdata *uc_pdata; + int ret; + + uc_pdata = dev_get_uclass_plat(dev); + + ret = sandbox_dev_move_to_state(dev, sb_running); + + debug("%s: called(%d)\n", uc_pdata->name, ret); + + if (ret) + debug("%s start failed\n", uc_pdata->name); + return ret; +} + +/** + * sandbox_testproc_stop() - Stop the remote processor + * @dev: device to operate upon + * + * Return: 0 if all went ok, else return appropriate error + */ +static int sandbox_testproc_stop(struct udevice *dev) +{ + struct dm_rproc_uclass_pdata *uc_pdata; + int ret; + + uc_pdata = dev_get_uclass_plat(dev); + + ret = sandbox_dev_move_to_state(dev, sb_init); + + debug("%s: called(%d)\n", uc_pdata->name, ret); + + if (ret) + debug("%s stop failed\n", uc_pdata->name); + return ret; +} + +/** + * sandbox_testproc_is_running() - Check if remote processor is running + * @dev: device to operate upon + * + * Return: 0 if running, 1 if not running + */ +static int sandbox_testproc_is_running(struct udevice *dev) +{ + struct dm_rproc_uclass_pdata *uc_pdata; + struct sandbox_test_devdata *ddata; + int ret = 1; + + uc_pdata = dev_get_uclass_plat(dev); + ddata = dev_get_priv(dev); + + if (ddata->current_state == sb_running) + ret = 0; + debug("%s: called(%d)\n", uc_pdata->name, ret); + + return ret; +} + +/** + * sandbox_testproc_ping() - Try pinging remote processor + * @dev: device to operate upon + * + * Return: 0 if running, -EINVAL if not running + */ +static int sandbox_testproc_ping(struct udevice *dev) +{ + struct dm_rproc_uclass_pdata *uc_pdata; + struct sandbox_test_devdata *ddata; + int ret; + + uc_pdata = dev_get_uclass_plat(dev); + ddata = dev_get_priv(dev); + + if (ddata->current_state == sb_running) + ret = 0; + else + ret = -EINVAL; + + debug("%s: called(%d)\n", uc_pdata->name, ret); + if (ret) + debug("%s: No response.(Not started?)\n", uc_pdata->name); + + return ret; +} + +#define SANDBOX_RPROC_DEV_TO_PHY_OFFSET 0x1000 +/** + * sandbox_testproc_device_to_virt() - Convert device address to virtual address + * @dev: device to operate upon + * @da: device address + * @size: Size of the memory region @da is pointing to + * @return converted virtual address + */ +static void *sandbox_testproc_device_to_virt(struct udevice *dev, ulong da, + ulong size) +{ + u64 paddr; + + /* Use a simple offset conversion */ + paddr = da + SANDBOX_RPROC_DEV_TO_PHY_OFFSET; + + return phys_to_virt(paddr); +} + +static const struct dm_rproc_ops sandbox_testproc_ops = { + .init = sandbox_testproc_init, + .reset = sandbox_testproc_reset, + .load = sandbox_testproc_load, + .start = sandbox_testproc_start, + .stop = sandbox_testproc_stop, + .is_running = sandbox_testproc_is_running, + .ping = sandbox_testproc_ping, + .device_to_virt = sandbox_testproc_device_to_virt, +}; + +static const struct udevice_id sandbox_ids[] = { + {.compatible = "sandbox,test-processor"}, + {} +}; + +U_BOOT_DRIVER(sandbox_testproc) = { + .name = "sandbox_test_proc", + .of_match = sandbox_ids, + .id = UCLASS_REMOTEPROC, + .ops = &sandbox_testproc_ops, + .probe = sandbox_testproc_probe, + .priv_auto = sizeof(struct sandbox_test_devdata), +}; + +/* TODO(nm@ti.com): Remove this along with non-DT support */ +static struct dm_rproc_uclass_pdata proc_3_test = { + .name = "proc_3_legacy", + .mem_type = RPROC_INTERNAL_MEMORY_MAPPED, +}; + +U_BOOT_DRVINFO(proc_3_demo) = { + .name = "sandbox_test_proc", + .plat = &proc_3_test, +}; diff --git a/roms/u-boot/drivers/remoteproc/stm32_copro.c b/roms/u-boot/drivers/remoteproc/stm32_copro.c new file mode 100644 index 000000000..4c5f24857 --- /dev/null +++ b/roms/u-boot/drivers/remoteproc/stm32_copro.c @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause +/* + * Copyright (C) 2019, STMicroelectronics - All Rights Reserved + */ +#define LOG_CATEGORY UCLASS_REMOTEPROC + +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <log.h> +#include <remoteproc.h> +#include <reset.h> +#include <asm/io.h> +#include <dm/device_compat.h> +#include <linux/err.h> + +/** + * struct stm32_copro_privdata - power processor private data + * @reset_ctl: reset controller handle + * @hold_boot: hold boot controller handle + * @rsc_table_addr: resource table address + */ +struct stm32_copro_privdata { + struct reset_ctl reset_ctl; + struct reset_ctl hold_boot; + ulong rsc_table_addr; +}; + +/** + * stm32_copro_probe() - Basic probe + * @dev: corresponding STM32 remote processor device + * @return 0 if all went ok, else corresponding -ve error + */ +static int stm32_copro_probe(struct udevice *dev) +{ + struct stm32_copro_privdata *priv; + int ret; + + priv = dev_get_priv(dev); + + ret = reset_get_by_name(dev, "mcu_rst", &priv->reset_ctl); + if (ret) { + dev_err(dev, "failed to get reset (%d)\n", ret); + return ret; + } + + ret = reset_get_by_name(dev, "hold_boot", &priv->hold_boot); + if (ret) { + dev_err(dev, "failed to get hold boot (%d)\n", ret); + return ret; + } + + dev_dbg(dev, "probed\n"); + + return 0; +} + +/** + * stm32_copro_device_to_virt() - Convert device address to virtual address + * @dev: corresponding STM32 remote processor device + * @da: device address + * @size: Size of the memory region @da is pointing to + * @return converted virtual address + */ +static void *stm32_copro_device_to_virt(struct udevice *dev, ulong da, + ulong size) +{ + fdt32_t in_addr = cpu_to_be32(da), end_addr; + u64 paddr; + + paddr = dev_translate_dma_address(dev, &in_addr); + if (paddr == OF_BAD_ADDR) { + dev_err(dev, "Unable to convert address %ld\n", da); + return NULL; + } + + end_addr = cpu_to_be32(da + size - 1); + if (dev_translate_dma_address(dev, &end_addr) == OF_BAD_ADDR) { + dev_err(dev, "Unable to convert address %ld\n", da + size - 1); + return NULL; + } + + return phys_to_virt(paddr); +} + +/** + * stm32_copro_load() - Loadup the STM32 remote processor + * @dev: corresponding STM32 remote processor device + * @addr: Address in memory where image is stored + * @size: Size in bytes of the image + * @return 0 if all went ok, else corresponding -ve error + */ +static int stm32_copro_load(struct udevice *dev, ulong addr, ulong size) +{ + struct stm32_copro_privdata *priv; + ulong rsc_table_size; + int ret; + + priv = dev_get_priv(dev); + + ret = reset_assert(&priv->hold_boot); + if (ret) { + dev_err(dev, "Unable to assert hold boot (ret=%d)\n", ret); + return ret; + } + + ret = reset_assert(&priv->reset_ctl); + if (ret) { + dev_err(dev, "Unable to assert reset line (ret=%d)\n", ret); + return ret; + } + + if (rproc_elf32_load_rsc_table(dev, addr, size, &priv->rsc_table_addr, + &rsc_table_size)) { + priv->rsc_table_addr = 0; + dev_warn(dev, "No valid resource table for this firmware\n"); + } + + return rproc_elf32_load_image(dev, addr, size); +} + +/** + * stm32_copro_start() - Start the STM32 remote processor + * @dev: corresponding STM32 remote processor device + * @return 0 if all went ok, else corresponding -ve error + */ +static int stm32_copro_start(struct udevice *dev) +{ + struct stm32_copro_privdata *priv; + int ret; + + priv = dev_get_priv(dev); + + ret = reset_deassert(&priv->hold_boot); + if (ret) { + dev_err(dev, "Unable to deassert hold boot (ret=%d)\n", ret); + return ret; + } + + /* + * Once copro running, reset hold boot flag to avoid copro + * rebooting autonomously (error should never occur) + */ + ret = reset_assert(&priv->hold_boot); + if (ret) + dev_err(dev, "Unable to assert hold boot (ret=%d)\n", ret); + + /* indicates that copro is running */ + writel(TAMP_COPRO_STATE_CRUN, TAMP_COPRO_STATE); + /* Store rsc_address in bkp register */ + writel(priv->rsc_table_addr, TAMP_COPRO_RSC_TBL_ADDRESS); + + return 0; +} + +/** + * stm32_copro_reset() - Reset the STM32 remote processor + * @dev: corresponding STM32 remote processor device + * @return 0 if all went ok, else corresponding -ve error + */ +static int stm32_copro_reset(struct udevice *dev) +{ + struct stm32_copro_privdata *priv; + int ret; + + priv = dev_get_priv(dev); + + ret = reset_assert(&priv->hold_boot); + if (ret) { + dev_err(dev, "Unable to assert hold boot (ret=%d)\n", ret); + return ret; + } + + ret = reset_assert(&priv->reset_ctl); + if (ret) { + dev_err(dev, "Unable to assert reset line (ret=%d)\n", ret); + return ret; + } + + writel(TAMP_COPRO_STATE_OFF, TAMP_COPRO_STATE); + + return 0; +} + +/** + * stm32_copro_stop() - Stop the STM32 remote processor + * @dev: corresponding STM32 remote processor device + * @return 0 if all went ok, else corresponding -ve error + */ +static int stm32_copro_stop(struct udevice *dev) +{ + return stm32_copro_reset(dev); +} + +/** + * stm32_copro_is_running() - Is the STM32 remote processor running + * @dev: corresponding STM32 remote processor device + * @return 0 if the remote processor is running, 1 otherwise + */ +static int stm32_copro_is_running(struct udevice *dev) +{ + return (readl(TAMP_COPRO_STATE) == TAMP_COPRO_STATE_OFF); +} + +static const struct dm_rproc_ops stm32_copro_ops = { + .load = stm32_copro_load, + .start = stm32_copro_start, + .stop = stm32_copro_stop, + .reset = stm32_copro_reset, + .is_running = stm32_copro_is_running, + .device_to_virt = stm32_copro_device_to_virt, +}; + +static const struct udevice_id stm32_copro_ids[] = { + {.compatible = "st,stm32mp1-m4"}, + {} +}; + +U_BOOT_DRIVER(stm32_copro) = { + .name = "stm32_m4_proc", + .of_match = stm32_copro_ids, + .id = UCLASS_REMOTEPROC, + .ops = &stm32_copro_ops, + .probe = stm32_copro_probe, + .priv_auto = sizeof(struct stm32_copro_privdata), +}; diff --git a/roms/u-boot/drivers/remoteproc/ti_k3_arm64_rproc.c b/roms/u-boot/drivers/remoteproc/ti_k3_arm64_rproc.c new file mode 100644 index 000000000..1f2415dc1 --- /dev/null +++ b/roms/u-boot/drivers/remoteproc/ti_k3_arm64_rproc.c @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Texas Instruments' K3 ARM64 Remoteproc driver + * + * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ + * Lokesh Vutla <lokeshvutla@ti.com> + * + */ + +#include <common.h> +#include <dm.h> +#include <remoteproc.h> +#include <errno.h> +#include <clk.h> +#include <reset.h> +#include <asm/io.h> +#include <power-domain.h> +#include <dm/device_compat.h> +#include <linux/err.h> +#include <linux/soc/ti/ti_sci_protocol.h> +#include "ti_sci_proc.h" + +#define INVALID_ID 0xffff + +#define GTC_CNTCR_REG 0x0 +#define GTC_CNTFID0_REG 0x20 +#define GTC_CNTR_EN 0x3 + +/** + * struct k3_arm64_privdata - Structure representing Remote processor data. + * @rproc_pwrdmn: rproc power domain data + * @rproc_rst: rproc reset control data + * @sci: Pointer to TISCI handle + * @tsp: TISCI processor control helper structure + * @gtc_clk: GTC clock description + * @gtc_base: Timer base address. + */ +struct k3_arm64_privdata { + struct power_domain rproc_pwrdmn; + struct power_domain gtc_pwrdmn; + struct reset_ctl rproc_rst; + struct ti_sci_proc tsp; + struct clk gtc_clk; + void *gtc_base; +}; + +/** + * k3_arm64_load() - Load up the Remote processor image + * @dev: rproc device pointer + * @addr: Address at which image is available + * @size: size of the image + * + * Return: 0 if all goes good, else appropriate error message. + */ +static int k3_arm64_load(struct udevice *dev, ulong addr, ulong size) +{ + struct k3_arm64_privdata *rproc = dev_get_priv(dev); + int ret; + + dev_dbg(dev, "%s addr = 0x%lx, size = 0x%lx\n", __func__, addr, size); + + /* request for the processor */ + ret = ti_sci_proc_request(&rproc->tsp); + if (ret) + return ret; + + return ti_sci_proc_set_config(&rproc->tsp, addr, 0, 0); +} + +/** + * k3_arm64_start() - Start the remote processor + * @dev: rproc device pointer + * + * Return: 0 if all went ok, else return appropriate error + */ +static int k3_arm64_start(struct udevice *dev) +{ + struct k3_arm64_privdata *rproc = dev_get_priv(dev); + ulong gtc_rate; + int ret; + + dev_dbg(dev, "%s\n", __func__); + + ret = power_domain_on(&rproc->gtc_pwrdmn); + if (ret) { + dev_err(dev, "power_domain_on() failed: %d\n", ret); + return ret; + } + + gtc_rate = clk_get_rate(&rproc->gtc_clk); + dev_dbg(dev, "GTC RATE= %d\n", (u32) gtc_rate); + /* Store the clock frequency down for GTC users to pick up */ + writel((u32)gtc_rate, rproc->gtc_base + GTC_CNTFID0_REG); + + /* Enable the timer before starting remote core */ + writel(GTC_CNTR_EN, rproc->gtc_base + GTC_CNTCR_REG); + + /* + * Setting the right clock frequency would have taken care by + * assigned-clock-rates during the device probe. So no need to + * set the frequency again here. + */ + ret = power_domain_on(&rproc->rproc_pwrdmn); + if (ret) { + dev_err(dev, "power_domain_on() failed: %d\n", ret); + return ret; + } + + return ti_sci_proc_release(&rproc->tsp); +} + +/** + * k3_arm64_init() - Initialize the remote processor + * @dev: rproc device pointer + * + * Return: 0 if all went ok, else return appropriate error + */ +static int k3_arm64_init(struct udevice *dev) +{ + dev_dbg(dev, "%s\n", __func__); + + /* Enable the module */ + dev_dbg(dev, "%s: rproc successfully initialized\n", __func__); + + return 0; +} + +static const struct dm_rproc_ops k3_arm64_ops = { + .init = k3_arm64_init, + .load = k3_arm64_load, + .start = k3_arm64_start, +}; + +static int ti_sci_proc_of_to_priv(struct udevice *dev, struct ti_sci_proc *tsp) +{ + dev_dbg(dev, "%s\n", __func__); + + tsp->sci = ti_sci_get_by_phandle(dev, "ti,sci"); + if (IS_ERR(tsp->sci)) { + dev_err(dev, "ti_sci get failed: %ld\n", PTR_ERR(tsp->sci)); + return PTR_ERR(tsp->sci); + } + + tsp->proc_id = dev_read_u32_default(dev, "ti,sci-proc-id", INVALID_ID); + if (tsp->proc_id == INVALID_ID) { + dev_err(dev, "proc id not populated\n"); + return -ENOENT; + } + tsp->host_id = dev_read_u32_default(dev, "ti,sci-host-id", INVALID_ID); + tsp->ops = &tsp->sci->ops.proc_ops; + + return 0; +} + +/** + * k3_of_to_priv() - generate private data from device tree + * @dev: corresponding k3 remote processor device + * @priv: pointer to driver specific private data + * + * Return: 0 if all goes good, else appropriate error message. + */ +static int k3_arm64_of_to_priv(struct udevice *dev, + struct k3_arm64_privdata *rproc) +{ + int ret; + + dev_dbg(dev, "%s\n", __func__); + + ret = power_domain_get_by_index(dev, &rproc->rproc_pwrdmn, 1); + if (ret) { + dev_err(dev, "power_domain_get() failed: %d\n", ret); + return ret; + } + + ret = power_domain_get_by_index(dev, &rproc->gtc_pwrdmn, 0); + if (ret) { + dev_err(dev, "power_domain_get() failed: %d\n", ret); + return ret; + } + + ret = clk_get_by_index(dev, 0, &rproc->gtc_clk); + if (ret) { + dev_err(dev, "clk_get failed: %d\n", ret); + return ret; + } + + ret = reset_get_by_index(dev, 0, &rproc->rproc_rst); + if (ret) { + dev_err(dev, "reset_get() failed: %d\n", ret); + return ret; + } + + ret = ti_sci_proc_of_to_priv(dev, &rproc->tsp); + if (ret) + return ret; + + rproc->gtc_base = dev_read_addr_ptr(dev); + if (!rproc->gtc_base) { + dev_err(dev, "Get address failed\n"); + return -ENODEV; + } + + return 0; +} + +/** + * k3_arm64_probe() - Basic probe + * @dev: corresponding k3 remote processor device + * + * Return: 0 if all goes good, else appropriate error message. + */ +static int k3_arm64_probe(struct udevice *dev) +{ + struct k3_arm64_privdata *priv; + int ret; + + dev_dbg(dev, "%s\n", __func__); + + priv = dev_get_priv(dev); + + ret = k3_arm64_of_to_priv(dev, priv); + if (ret) { + dev_dbg(dev, "%s: Probe failed with error %d\n", __func__, ret); + return ret; + } + + dev_dbg(dev, "Remoteproc successfully probed\n"); + + return 0; +} + +static const struct udevice_id k3_arm64_ids[] = { + { .compatible = "ti,am654-arm64"}, + { .compatible = "ti,am654-rproc"}, + {} +}; + +U_BOOT_DRIVER(k3_arm64) = { + .name = "k3_arm64", + .of_match = k3_arm64_ids, + .id = UCLASS_REMOTEPROC, + .ops = &k3_arm64_ops, + .probe = k3_arm64_probe, + .priv_auto = sizeof(struct k3_arm64_privdata), + .flags = DM_FLAG_DEFAULT_PD_CTRL_OFF, +}; diff --git a/roms/u-boot/drivers/remoteproc/ti_k3_dsp_rproc.c b/roms/u-boot/drivers/remoteproc/ti_k3_dsp_rproc.c new file mode 100644 index 000000000..1a2e9dd1f --- /dev/null +++ b/roms/u-boot/drivers/remoteproc/ti_k3_dsp_rproc.c @@ -0,0 +1,460 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Texas Instruments' K3 DSP Remoteproc driver + * + * Copyright (C) 2018-2020 Texas Instruments Incorporated - http://www.ti.com/ + * Lokesh Vutla <lokeshvutla@ti.com> + * Suman Anna <s-anna@ti.com> + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <remoteproc.h> +#include <errno.h> +#include <clk.h> +#include <reset.h> +#include <asm/io.h> +#include <power-domain.h> +#include <dm/device_compat.h> +#include <linux/err.h> +#include <linux/sizes.h> +#include <linux/soc/ti/ti_sci_protocol.h> +#include "ti_sci_proc.h" + +#define KEYSTONE_RPROC_LOCAL_ADDRESS_MASK (SZ_16M - 1) + +/** + * struct k3_dsp_mem - internal memory structure + * @cpu_addr: MPU virtual address of the memory region + * @bus_addr: Bus address used to access the memory region + * @dev_addr: Device address from remoteproc view + * @size: Size of the memory region + */ +struct k3_dsp_mem { + void __iomem *cpu_addr; + phys_addr_t bus_addr; + phys_addr_t dev_addr; + size_t size; +}; + +/** + * struct k3_dsp_boot_data - internal data structure used for boot + * @boot_align_addr: Boot vector address alignment granularity + * @uses_lreset: Flag to denote the need for local reset management + */ +struct k3_dsp_boot_data { + u32 boot_align_addr; + bool uses_lreset; +}; + +/** + * struct k3_dsp_privdata - Structure representing Remote processor data. + * @rproc_rst: rproc reset control data + * @tsp: Pointer to TISCI proc contrl handle + * @data: Pointer to DSP specific boot data structure + * @mem: Array of available memories + * @num_mem: Number of available memories + */ +struct k3_dsp_privdata { + struct reset_ctl dsp_rst; + struct ti_sci_proc tsp; + struct k3_dsp_boot_data *data; + struct k3_dsp_mem *mem; + int num_mems; +}; + +/* + * The C66x DSP cores have a local reset that affects only the CPU, and a + * generic module reset that powers on the device and allows the DSP internal + * memories to be accessed while the local reset is asserted. This function is + * used to release the global reset on C66x DSPs to allow loading into the DSP + * internal RAMs. This helper function is invoked in k3_dsp_load() before any + * actual firmware loading and is undone only in k3_dsp_stop(). The local reset + * on C71x cores is a no-op and the global reset cannot be released on C71x + * cores until after the firmware images are loaded, so this function does + * nothing for C71x cores. + */ +static int k3_dsp_prepare(struct udevice *dev) +{ + struct k3_dsp_privdata *dsp = dev_get_priv(dev); + struct k3_dsp_boot_data *data = dsp->data; + int ret; + + /* local reset is no-op on C71x processors */ + if (!data->uses_lreset) + return 0; + + ret = ti_sci_proc_power_domain_on(&dsp->tsp); + if (ret) + dev_err(dev, "cannot enable internal RAM loading, ret = %d\n", + ret); + + return ret; +} + +/* + * This function is the counterpart to k3_dsp_prepare() and is used to assert + * the global reset on C66x DSP cores (no-op for C71x DSP cores). This completes + * the second step of powering down the C66x DSP cores. The cores themselves + * are halted through the local reset in first step. This function is invoked + * in k3_dsp_stop() after the local reset is asserted. + */ +static int k3_dsp_unprepare(struct udevice *dev) +{ + struct k3_dsp_privdata *dsp = dev_get_priv(dev); + struct k3_dsp_boot_data *data = dsp->data; + + /* local reset is no-op on C71x processors */ + if (!data->uses_lreset) + return 0; + + return ti_sci_proc_power_domain_off(&dsp->tsp); +} + +/** + * k3_dsp_load() - Load up the Remote processor image + * @dev: rproc device pointer + * @addr: Address at which image is available + * @size: size of the image + * + * Return: 0 if all goes good, else appropriate error message. + */ +static int k3_dsp_load(struct udevice *dev, ulong addr, ulong size) +{ + struct k3_dsp_privdata *dsp = dev_get_priv(dev); + struct k3_dsp_boot_data *data = dsp->data; + u32 boot_vector; + int ret; + + dev_dbg(dev, "%s addr = 0x%lx, size = 0x%lx\n", __func__, addr, size); + ret = ti_sci_proc_request(&dsp->tsp); + if (ret) + return ret; + + ret = k3_dsp_prepare(dev); + if (ret) { + dev_err(dev, "DSP prepare failed for core %d\n", + dsp->tsp.proc_id); + goto proc_release; + } + + ret = rproc_elf_load_image(dev, addr, size); + if (ret < 0) { + dev_err(dev, "Loading elf failed %d\n", ret); + goto unprepare; + } + + boot_vector = rproc_elf_get_boot_addr(dev, addr); + if (boot_vector & (data->boot_align_addr - 1)) { + ret = -EINVAL; + dev_err(dev, "Boot vector 0x%x not aligned on 0x%x boundary\n", + boot_vector, data->boot_align_addr); + goto proc_release; + } + + dev_dbg(dev, "%s: Boot vector = 0x%x\n", __func__, boot_vector); + + ret = ti_sci_proc_set_config(&dsp->tsp, boot_vector, 0, 0); +unprepare: + if (ret) + k3_dsp_unprepare(dev); +proc_release: + ti_sci_proc_release(&dsp->tsp); + return ret; +} + +/** + * k3_dsp_start() - Start the remote processor + * @dev: rproc device pointer + * + * Return: 0 if all went ok, else return appropriate error + */ +static int k3_dsp_start(struct udevice *dev) +{ + struct k3_dsp_privdata *dsp = dev_get_priv(dev); + struct k3_dsp_boot_data *data = dsp->data; + int ret; + + dev_dbg(dev, "%s\n", __func__); + + ret = ti_sci_proc_request(&dsp->tsp); + if (ret) + return ret; + + if (!data->uses_lreset) { + ret = ti_sci_proc_power_domain_on(&dsp->tsp); + if (ret) + goto proc_release; + } + + ret = reset_deassert(&dsp->dsp_rst); + if (ret) { + if (!data->uses_lreset) + ti_sci_proc_power_domain_off(&dsp->tsp); + } + +proc_release: + ti_sci_proc_release(&dsp->tsp); + + return ret; +} + +static int k3_dsp_stop(struct udevice *dev) +{ + struct k3_dsp_privdata *dsp = dev_get_priv(dev); + + dev_dbg(dev, "%s\n", __func__); + + ti_sci_proc_request(&dsp->tsp); + reset_assert(&dsp->dsp_rst); + ti_sci_proc_power_domain_off(&dsp->tsp); + ti_sci_proc_release(&dsp->tsp); + + return 0; +} + +/** + * k3_dsp_init() - Initialize the remote processor + * @dev: rproc device pointer + * + * Return: 0 if all went ok, else return appropriate error + */ +static int k3_dsp_init(struct udevice *dev) +{ + dev_dbg(dev, "%s\n", __func__); + + return 0; +} + +static int k3_dsp_reset(struct udevice *dev) +{ + dev_dbg(dev, "%s\n", __func__); + + return 0; +} + +static void *k3_dsp_da_to_va(struct udevice *dev, ulong da, ulong len) +{ + struct k3_dsp_privdata *dsp = dev_get_priv(dev); + phys_addr_t bus_addr, dev_addr; + void __iomem *va = NULL; + size_t size; + u32 offset; + int i; + + dev_dbg(dev, "%s\n", __func__); + + if (len <= 0) + return NULL; + + for (i = 0; i < dsp->num_mems; i++) { + bus_addr = dsp->mem[i].bus_addr; + dev_addr = dsp->mem[i].dev_addr; + size = dsp->mem[i].size; + + if (da >= dev_addr && ((da + len) <= (dev_addr + size))) { + offset = da - dev_addr; + va = dsp->mem[i].cpu_addr + offset; + return (__force void *)va; + } + + if (da >= bus_addr && (da + len) <= (bus_addr + size)) { + offset = da - bus_addr; + va = dsp->mem[i].cpu_addr + offset; + return (__force void *)va; + } + } + + /* Assume it is DDR region and return da */ + return map_physmem(da, len, MAP_NOCACHE); +} + +static const struct dm_rproc_ops k3_dsp_ops = { + .init = k3_dsp_init, + .load = k3_dsp_load, + .start = k3_dsp_start, + .stop = k3_dsp_stop, + .reset = k3_dsp_reset, + .device_to_virt = k3_dsp_da_to_va, +}; + +static int ti_sci_proc_of_to_priv(struct udevice *dev, struct ti_sci_proc *tsp) +{ + u32 ids[2]; + int ret; + + dev_dbg(dev, "%s\n", __func__); + + tsp->sci = ti_sci_get_by_phandle(dev, "ti,sci"); + if (IS_ERR(tsp->sci)) { + dev_err(dev, "ti_sci get failed: %ld\n", PTR_ERR(tsp->sci)); + return PTR_ERR(tsp->sci); + } + + ret = dev_read_u32_array(dev, "ti,sci-proc-ids", ids, 2); + if (ret) { + dev_err(dev, "Proc IDs not populated %d\n", ret); + return ret; + } + + tsp->ops = &tsp->sci->ops.proc_ops; + tsp->proc_id = ids[0]; + tsp->host_id = ids[1]; + tsp->dev_id = dev_read_u32_default(dev, "ti,sci-dev-id", + TI_SCI_RESOURCE_NULL); + if (tsp->dev_id == TI_SCI_RESOURCE_NULL) { + dev_err(dev, "Device ID not populated %d\n", ret); + return -ENODEV; + } + + return 0; +} + +static int k3_dsp_of_get_memories(struct udevice *dev) +{ + static const char * const mem_names[] = {"l2sram", "l1pram", "l1dram"}; + struct k3_dsp_privdata *dsp = dev_get_priv(dev); + int i; + + dev_dbg(dev, "%s\n", __func__); + + dsp->num_mems = ARRAY_SIZE(mem_names); + dsp->mem = calloc(dsp->num_mems, sizeof(*dsp->mem)); + if (!dsp->mem) + return -ENOMEM; + + for (i = 0; i < dsp->num_mems; i++) { + /* C71 cores only have a L1P Cache, there are no L1P SRAMs */ + if (device_is_compatible(dev, "ti,j721e-c71-dsp") && + !strcmp(mem_names[i], "l1pram")) { + dsp->mem[i].bus_addr = FDT_ADDR_T_NONE; + dsp->mem[i].dev_addr = FDT_ADDR_T_NONE; + dsp->mem[i].cpu_addr = NULL; + dsp->mem[i].size = 0; + continue; + } + + dsp->mem[i].bus_addr = dev_read_addr_size_name(dev, mem_names[i], + (fdt_addr_t *)&dsp->mem[i].size); + if (dsp->mem[i].bus_addr == FDT_ADDR_T_NONE) { + dev_err(dev, "%s bus address not found\n", mem_names[i]); + return -EINVAL; + } + dsp->mem[i].cpu_addr = map_physmem(dsp->mem[i].bus_addr, + dsp->mem[i].size, + MAP_NOCACHE); + dsp->mem[i].dev_addr = dsp->mem[i].bus_addr & + KEYSTONE_RPROC_LOCAL_ADDRESS_MASK; + + dev_dbg(dev, "memory %8s: bus addr %pa size 0x%zx va %p da %pa\n", + mem_names[i], &dsp->mem[i].bus_addr, + dsp->mem[i].size, dsp->mem[i].cpu_addr, + &dsp->mem[i].dev_addr); + } + + return 0; +} + +/** + * k3_of_to_priv() - generate private data from device tree + * @dev: corresponding k3 dsp processor device + * @dsp: pointer to driver specific private data + * + * Return: 0 if all goes good, else appropriate error message. + */ +static int k3_dsp_of_to_priv(struct udevice *dev, struct k3_dsp_privdata *dsp) +{ + int ret; + + dev_dbg(dev, "%s\n", __func__); + + ret = reset_get_by_index(dev, 0, &dsp->dsp_rst); + if (ret) { + dev_err(dev, "reset_get() failed: %d\n", ret); + return ret; + } + + ret = ti_sci_proc_of_to_priv(dev, &dsp->tsp); + if (ret) + return ret; + + ret = k3_dsp_of_get_memories(dev); + if (ret) + return ret; + + dsp->data = (struct k3_dsp_boot_data *)dev_get_driver_data(dev); + + return 0; +} + +/** + * k3_dsp_probe() - Basic probe + * @dev: corresponding k3 remote processor device + * + * Return: 0 if all goes good, else appropriate error message. + */ +static int k3_dsp_probe(struct udevice *dev) +{ + struct k3_dsp_privdata *dsp; + int ret; + + dev_dbg(dev, "%s\n", __func__); + + dsp = dev_get_priv(dev); + + ret = k3_dsp_of_to_priv(dev, dsp); + if (ret) { + dev_dbg(dev, "%s: Probe failed with error %d\n", __func__, ret); + return ret; + } + + /* + * The DSP local resets are deasserted by default on Power-On-Reset. + * Assert the local resets to ensure the DSPs don't execute bogus code + * in .load() callback when the module reset is released to support + * internal memory loading. This is needed for C66x DSPs, and is a + * no-op on C71x DSPs. + */ + reset_assert(&dsp->dsp_rst); + + dev_dbg(dev, "Remoteproc successfully probed\n"); + + return 0; +} + +static int k3_dsp_remove(struct udevice *dev) +{ + struct k3_dsp_privdata *dsp = dev_get_priv(dev); + + free(dsp->mem); + + return 0; +} + +static const struct k3_dsp_boot_data c66_data = { + .boot_align_addr = SZ_1K, + .uses_lreset = true, +}; + +static const struct k3_dsp_boot_data c71_data = { + .boot_align_addr = SZ_2M, + .uses_lreset = false, +}; + +static const struct udevice_id k3_dsp_ids[] = { + { .compatible = "ti,j721e-c66-dsp", .data = (ulong)&c66_data, }, + { .compatible = "ti,j721e-c71-dsp", .data = (ulong)&c71_data, }, + {} +}; + +U_BOOT_DRIVER(k3_dsp) = { + .name = "k3_dsp", + .of_match = k3_dsp_ids, + .id = UCLASS_REMOTEPROC, + .ops = &k3_dsp_ops, + .probe = k3_dsp_probe, + .remove = k3_dsp_remove, + .priv_auto = sizeof(struct k3_dsp_privdata), +}; diff --git a/roms/u-boot/drivers/remoteproc/ti_k3_r5f_rproc.c b/roms/u-boot/drivers/remoteproc/ti_k3_r5f_rproc.c new file mode 100644 index 000000000..3c569a3b7 --- /dev/null +++ b/roms/u-boot/drivers/remoteproc/ti_k3_r5f_rproc.c @@ -0,0 +1,906 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Texas Instruments' K3 R5 Remoteproc driver + * + * Copyright (C) 2018-2020 Texas Instruments Incorporated - https://www.ti.com/ + * Lokesh Vutla <lokeshvutla@ti.com> + * Suman Anna <s-anna@ti.com> + */ + +#include <common.h> +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <remoteproc.h> +#include <errno.h> +#include <clk.h> +#include <reset.h> +#include <asm/io.h> +#include <dm/device_compat.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/soc/ti/ti_sci_protocol.h> +#include "ti_sci_proc.h" + +/* + * R5F's view of this address can either be for ATCM or BTCM with the other + * at address 0x0 based on loczrama signal. + */ +#define K3_R5_TCM_DEV_ADDR 0x41010000 + +/* R5 TI-SCI Processor Configuration Flags */ +#define PROC_BOOT_CFG_FLAG_R5_DBG_EN 0x00000001 +#define PROC_BOOT_CFG_FLAG_R5_DBG_NIDEN 0x00000002 +#define PROC_BOOT_CFG_FLAG_R5_LOCKSTEP 0x00000100 +#define PROC_BOOT_CFG_FLAG_R5_TEINIT 0x00000200 +#define PROC_BOOT_CFG_FLAG_R5_NMFI_EN 0x00000400 +#define PROC_BOOT_CFG_FLAG_R5_TCM_RSTBASE 0x00000800 +#define PROC_BOOT_CFG_FLAG_R5_BTCM_EN 0x00001000 +#define PROC_BOOT_CFG_FLAG_R5_ATCM_EN 0x00002000 +#define PROC_BOOT_CFG_FLAG_GEN_IGN_BOOTVECTOR 0x10000000 +/* Available from J7200 SoCs onwards */ +#define PROC_BOOT_CFG_FLAG_R5_MEM_INIT_DIS 0x00004000 + +/* R5 TI-SCI Processor Control Flags */ +#define PROC_BOOT_CTRL_FLAG_R5_CORE_HALT 0x00000001 + +/* R5 TI-SCI Processor Status Flags */ +#define PROC_BOOT_STATUS_FLAG_R5_WFE 0x00000001 +#define PROC_BOOT_STATUS_FLAG_R5_WFI 0x00000002 +#define PROC_BOOT_STATUS_FLAG_R5_CLK_GATED 0x00000004 +#define PROC_BOOT_STATUS_FLAG_R5_LOCKSTEP_PERMITTED 0x00000100 + +#define NR_CORES 2 + +enum cluster_mode { + CLUSTER_MODE_SPLIT = 0, + CLUSTER_MODE_LOCKSTEP, +}; + +/** + * struct k3_r5f_ip_data - internal data structure used for IP variations + * @tcm_is_double: flag to denote the larger unified TCMs in certain modes + * @tcm_ecc_autoinit: flag to denote the auto-initialization of TCMs for ECC + */ +struct k3_r5f_ip_data { + bool tcm_is_double; + bool tcm_ecc_autoinit; +}; + +/** + * struct k3_r5_mem - internal memory structure + * @cpu_addr: MPU virtual address of the memory region + * @bus_addr: Bus address used to access the memory region + * @dev_addr: Device address from remoteproc view + * @size: Size of the memory region + */ +struct k3_r5f_mem { + void __iomem *cpu_addr; + phys_addr_t bus_addr; + u32 dev_addr; + size_t size; +}; + +/** + * struct k3_r5f_core - K3 R5 core structure + * @dev: cached device pointer + * @cluster: pointer to the parent cluster. + * @reset: reset control handle + * @tsp: TI-SCI processor control handle + * @ipdata: cached pointer to R5F IP specific feature data + * @mem: Array of available internal memories + * @num_mem: Number of available memories + * @atcm_enable: flag to control ATCM enablement + * @btcm_enable: flag to control BTCM enablement + * @loczrama: flag to dictate which TCM is at device address 0x0 + * @in_use: flag to tell if the core is already in use. + */ +struct k3_r5f_core { + struct udevice *dev; + struct k3_r5f_cluster *cluster; + struct reset_ctl reset; + struct ti_sci_proc tsp; + struct k3_r5f_ip_data *ipdata; + struct k3_r5f_mem *mem; + int num_mems; + u32 atcm_enable; + u32 btcm_enable; + u32 loczrama; + bool in_use; +}; + +/** + * struct k3_r5f_cluster - K3 R5F Cluster structure + * @mode: Mode to configure the Cluster - Split or LockStep + * @cores: Array of pointers to R5 cores within the cluster + */ +struct k3_r5f_cluster { + enum cluster_mode mode; + struct k3_r5f_core *cores[NR_CORES]; +}; + +static bool is_primary_core(struct k3_r5f_core *core) +{ + return core == core->cluster->cores[0]; +} + +static int k3_r5f_proc_request(struct k3_r5f_core *core) +{ + struct k3_r5f_cluster *cluster = core->cluster; + int i, ret; + + if (cluster->mode == CLUSTER_MODE_LOCKSTEP) { + for (i = 0; i < NR_CORES; i++) { + ret = ti_sci_proc_request(&cluster->cores[i]->tsp); + if (ret) + goto proc_release; + } + } else { + ret = ti_sci_proc_request(&core->tsp); + } + + return 0; + +proc_release: + while (i >= 0) { + ti_sci_proc_release(&cluster->cores[i]->tsp); + i--; + } + return ret; +} + +static void k3_r5f_proc_release(struct k3_r5f_core *core) +{ + struct k3_r5f_cluster *cluster = core->cluster; + int i; + + if (cluster->mode == CLUSTER_MODE_LOCKSTEP) + for (i = 0; i < NR_CORES; i++) + ti_sci_proc_release(&cluster->cores[i]->tsp); + else + ti_sci_proc_release(&core->tsp); +} + +static int k3_r5f_lockstep_release(struct k3_r5f_cluster *cluster) +{ + int ret, c; + + debug("%s\n", __func__); + + for (c = NR_CORES - 1; c >= 0; c--) { + ret = ti_sci_proc_power_domain_on(&cluster->cores[c]->tsp); + if (ret) + goto unroll_module_reset; + } + + /* deassert local reset on all applicable cores */ + for (c = NR_CORES - 1; c >= 0; c--) { + ret = reset_deassert(&cluster->cores[c]->reset); + if (ret) + goto unroll_local_reset; + } + + return 0; + +unroll_local_reset: + while (c < NR_CORES) { + reset_assert(&cluster->cores[c]->reset); + c++; + } + c = 0; +unroll_module_reset: + while (c < NR_CORES) { + ti_sci_proc_power_domain_off(&cluster->cores[c]->tsp); + c++; + } + + return ret; +} + +static int k3_r5f_split_release(struct k3_r5f_core *core) +{ + int ret; + + dev_dbg(core->dev, "%s\n", __func__); + + ret = ti_sci_proc_power_domain_on(&core->tsp); + if (ret) { + dev_err(core->dev, "module-reset deassert failed, ret = %d\n", + ret); + return ret; + } + + ret = reset_deassert(&core->reset); + if (ret) { + dev_err(core->dev, "local-reset deassert failed, ret = %d\n", + ret); + if (ti_sci_proc_power_domain_off(&core->tsp)) + dev_warn(core->dev, "module-reset assert back failed\n"); + } + + return ret; +} + +static int k3_r5f_prepare(struct udevice *dev) +{ + struct k3_r5f_core *core = dev_get_priv(dev); + struct k3_r5f_cluster *cluster = core->cluster; + int ret = 0; + + dev_dbg(dev, "%s\n", __func__); + + if (cluster->mode == CLUSTER_MODE_LOCKSTEP) + ret = k3_r5f_lockstep_release(cluster); + else + ret = k3_r5f_split_release(core); + + if (ret) + dev_err(dev, "Unable to enable cores for TCM loading %d\n", + ret); + + return ret; +} + +static int k3_r5f_core_sanity_check(struct k3_r5f_core *core) +{ + struct k3_r5f_cluster *cluster = core->cluster; + + if (core->in_use) { + dev_err(core->dev, + "Invalid op: Trying to load/start on already running core %d\n", + core->tsp.proc_id); + return -EINVAL; + } + + if (cluster->mode == CLUSTER_MODE_LOCKSTEP && !cluster->cores[1]) { + dev_err(core->dev, + "Secondary core is not probed in this cluster\n"); + return -EAGAIN; + } + + if (cluster->mode == CLUSTER_MODE_LOCKSTEP && !is_primary_core(core)) { + dev_err(core->dev, + "Invalid op: Trying to start secondary core %d in lockstep mode\n", + core->tsp.proc_id); + return -EINVAL; + } + + if (cluster->mode == CLUSTER_MODE_SPLIT && !is_primary_core(core)) { + if (!core->cluster->cores[0]->in_use) { + dev_err(core->dev, + "Invalid seq: Enable primary core before loading secondary core\n"); + return -EINVAL; + } + } + + return 0; +} + +/* Zero out TCMs so that ECC can be effective on all TCM addresses */ +void k3_r5f_init_tcm_memories(struct k3_r5f_core *core, bool auto_inited) +{ + if (core->ipdata->tcm_ecc_autoinit && auto_inited) + return; + + if (core->atcm_enable) + memset(core->mem[0].cpu_addr, 0x00, core->mem[0].size); + if (core->btcm_enable) + memset(core->mem[1].cpu_addr, 0x00, core->mem[1].size); +} + +/** + * k3_r5f_load() - Load up the Remote processor image + * @dev: rproc device pointer + * @addr: Address at which image is available + * @size: size of the image + * + * Return: 0 if all goes good, else appropriate error message. + */ +static int k3_r5f_load(struct udevice *dev, ulong addr, ulong size) +{ + struct k3_r5f_core *core = dev_get_priv(dev); + u64 boot_vector; + u32 ctrl, sts, cfg = 0; + bool mem_auto_init; + int ret; + + dev_dbg(dev, "%s addr = 0x%lx, size = 0x%lx\n", __func__, addr, size); + + ret = k3_r5f_core_sanity_check(core); + if (ret) + return ret; + + ret = k3_r5f_proc_request(core); + if (ret) + return ret; + + ret = ti_sci_proc_get_status(&core->tsp, &boot_vector, &cfg, &ctrl, + &sts); + if (ret) + return ret; + mem_auto_init = !(cfg & PROC_BOOT_CFG_FLAG_R5_MEM_INIT_DIS); + + ret = k3_r5f_prepare(dev); + if (ret) { + dev_err(dev, "R5f prepare failed for core %d\n", + core->tsp.proc_id); + goto proc_release; + } + + k3_r5f_init_tcm_memories(core, mem_auto_init); + + ret = rproc_elf_load_image(dev, addr, size); + if (ret < 0) { + dev_err(dev, "Loading elf failedi %d\n", ret); + goto proc_release; + } + + boot_vector = rproc_elf_get_boot_addr(dev, addr); + + dev_dbg(dev, "%s: Boot vector = 0x%llx\n", __func__, boot_vector); + + ret = ti_sci_proc_set_config(&core->tsp, boot_vector, 0, 0); + +proc_release: + k3_r5f_proc_release(core); + + return ret; +} + +static int k3_r5f_core_halt(struct k3_r5f_core *core) +{ + int ret; + + ret = ti_sci_proc_set_control(&core->tsp, + PROC_BOOT_CTRL_FLAG_R5_CORE_HALT, 0); + if (ret) + dev_err(core->dev, "Core %d failed to stop\n", + core->tsp.proc_id); + + return ret; +} + +static int k3_r5f_core_run(struct k3_r5f_core *core) +{ + int ret; + + ret = ti_sci_proc_set_control(&core->tsp, + 0, PROC_BOOT_CTRL_FLAG_R5_CORE_HALT); + if (ret) { + dev_err(core->dev, "Core %d failed to start\n", + core->tsp.proc_id); + return ret; + } + + return 0; +} + +/** + * k3_r5f_start() - Start the remote processor + * @dev: rproc device pointer + * + * Return: 0 if all went ok, else return appropriate error + */ +static int k3_r5f_start(struct udevice *dev) +{ + struct k3_r5f_core *core = dev_get_priv(dev); + struct k3_r5f_cluster *cluster = core->cluster; + int ret, c; + + dev_dbg(dev, "%s\n", __func__); + + ret = k3_r5f_core_sanity_check(core); + if (ret) + return ret; + + ret = k3_r5f_proc_request(core); + if (ret) + return ret; + + if (cluster->mode == CLUSTER_MODE_LOCKSTEP) { + if (is_primary_core(core)) { + for (c = NR_CORES - 1; c >= 0; c--) { + ret = k3_r5f_core_run(cluster->cores[c]); + if (ret) + goto unroll_core_run; + } + } else { + dev_err(dev, "Invalid op: Trying to start secondary core %d in lockstep mode\n", + core->tsp.proc_id); + ret = -EINVAL; + goto proc_release; + } + } else { + ret = k3_r5f_core_run(core); + if (ret) + goto proc_release; + } + + core->in_use = true; + + k3_r5f_proc_release(core); + return 0; + +unroll_core_run: + while (c < NR_CORES) { + k3_r5f_core_halt(cluster->cores[c]); + c++; + } +proc_release: + k3_r5f_proc_release(core); + + return ret; +} + +static int k3_r5f_split_reset(struct k3_r5f_core *core) +{ + int ret; + + dev_dbg(core->dev, "%s\n", __func__); + + if (reset_assert(&core->reset)) + ret = -EINVAL; + + if (ti_sci_proc_power_domain_off(&core->tsp)) + ret = -EINVAL; + + return ret; +} + +static int k3_r5f_lockstep_reset(struct k3_r5f_cluster *cluster) +{ + int ret = 0, c; + + debug("%s\n", __func__); + + for (c = 0; c < NR_CORES; c++) + if (reset_assert(&cluster->cores[c]->reset)) + ret = -EINVAL; + + /* disable PSC modules on all applicable cores */ + for (c = 0; c < NR_CORES; c++) + if (ti_sci_proc_power_domain_off(&cluster->cores[c]->tsp)) + ret = -EINVAL; + + return ret; +} + +static int k3_r5f_unprepare(struct udevice *dev) +{ + struct k3_r5f_core *core = dev_get_priv(dev); + struct k3_r5f_cluster *cluster = core->cluster; + int ret; + + dev_dbg(dev, "%s\n", __func__); + + if (cluster->mode == CLUSTER_MODE_LOCKSTEP) { + if (is_primary_core(core)) + ret = k3_r5f_lockstep_reset(cluster); + } else { + ret = k3_r5f_split_reset(core); + } + + if (ret) + dev_warn(dev, "Unable to enable cores for TCM loading %d\n", + ret); + + return 0; +} + +static int k3_r5f_stop(struct udevice *dev) +{ + struct k3_r5f_core *core = dev_get_priv(dev); + struct k3_r5f_cluster *cluster = core->cluster; + int c, ret; + + dev_dbg(dev, "%s\n", __func__); + + ret = k3_r5f_proc_request(core); + if (ret) + return ret; + + core->in_use = false; + + if (cluster->mode == CLUSTER_MODE_LOCKSTEP) { + if (is_primary_core(core)) { + for (c = 0; c < NR_CORES; c++) + k3_r5f_core_halt(cluster->cores[c]); + } else { + dev_err(dev, "Invalid op: Trying to stop secondary core in lockstep mode\n"); + ret = -EINVAL; + goto proc_release; + } + } else { + k3_r5f_core_halt(core); + } + + ret = k3_r5f_unprepare(dev); +proc_release: + k3_r5f_proc_release(core); + return ret; +} + +static void *k3_r5f_da_to_va(struct udevice *dev, ulong da, ulong size) +{ + struct k3_r5f_core *core = dev_get_priv(dev); + void __iomem *va = NULL; + phys_addr_t bus_addr; + u32 dev_addr, offset; + ulong mem_size; + int i; + + dev_dbg(dev, "%s\n", __func__); + + if (size <= 0) + return NULL; + + for (i = 0; i < core->num_mems; i++) { + bus_addr = core->mem[i].bus_addr; + dev_addr = core->mem[i].dev_addr; + mem_size = core->mem[i].size; + + if (da >= bus_addr && (da + size) <= (bus_addr + mem_size)) { + offset = da - bus_addr; + va = core->mem[i].cpu_addr + offset; + return (__force void *)va; + } + + if (da >= dev_addr && (da + size) <= (dev_addr + mem_size)) { + offset = da - dev_addr; + va = core->mem[i].cpu_addr + offset; + return (__force void *)va; + } + } + + /* Assume it is DDR region and return da */ + return map_physmem(da, size, MAP_NOCACHE); +} + +static int k3_r5f_init(struct udevice *dev) +{ + return 0; +} + +static int k3_r5f_reset(struct udevice *dev) +{ + return 0; +} + +static const struct dm_rproc_ops k3_r5f_rproc_ops = { + .init = k3_r5f_init, + .reset = k3_r5f_reset, + .start = k3_r5f_start, + .stop = k3_r5f_stop, + .load = k3_r5f_load, + .device_to_virt = k3_r5f_da_to_va, +}; + +static int k3_r5f_rproc_configure(struct k3_r5f_core *core) +{ + struct k3_r5f_cluster *cluster = core->cluster; + u32 set_cfg = 0, clr_cfg = 0, cfg, ctrl, sts; + bool lockstep_permitted; + u64 boot_vec = 0; + int ret; + + dev_dbg(core->dev, "%s\n", __func__); + + ret = ti_sci_proc_request(&core->tsp); + if (ret < 0) + return ret; + + /* Do not touch boot vector now. Load will take care of it. */ + clr_cfg |= PROC_BOOT_CFG_FLAG_GEN_IGN_BOOTVECTOR; + + ret = ti_sci_proc_get_status(&core->tsp, &boot_vec, &cfg, &ctrl, &sts); + if (ret) + goto out; + + /* Sanity check for Lockstep mode */ + lockstep_permitted = !!(sts & + PROC_BOOT_STATUS_FLAG_R5_LOCKSTEP_PERMITTED); + if (cluster->mode && is_primary_core(core) && !lockstep_permitted) { + dev_err(core->dev, "LockStep mode not permitted on this device\n"); + ret = -EINVAL; + goto out; + } + + /* Primary core only configuration */ + if (is_primary_core(core)) { + /* always enable ARM mode */ + clr_cfg |= PROC_BOOT_CFG_FLAG_R5_TEINIT; + if (cluster->mode == CLUSTER_MODE_LOCKSTEP) + set_cfg |= PROC_BOOT_CFG_FLAG_R5_LOCKSTEP; + else if (lockstep_permitted) + clr_cfg |= PROC_BOOT_CFG_FLAG_R5_LOCKSTEP; + } + + if (core->atcm_enable) + set_cfg |= PROC_BOOT_CFG_FLAG_R5_ATCM_EN; + else + clr_cfg |= PROC_BOOT_CFG_FLAG_R5_ATCM_EN; + + if (core->btcm_enable) + set_cfg |= PROC_BOOT_CFG_FLAG_R5_BTCM_EN; + else + clr_cfg |= PROC_BOOT_CFG_FLAG_R5_BTCM_EN; + + if (core->loczrama) + set_cfg |= PROC_BOOT_CFG_FLAG_R5_TCM_RSTBASE; + else + clr_cfg |= PROC_BOOT_CFG_FLAG_R5_TCM_RSTBASE; + + ret = k3_r5f_core_halt(core); + if (ret) + goto out; + + ret = ti_sci_proc_set_config(&core->tsp, boot_vec, set_cfg, clr_cfg); +out: + ti_sci_proc_release(&core->tsp); + return ret; +} + +static int ti_sci_proc_of_to_priv(struct udevice *dev, struct ti_sci_proc *tsp) +{ + u32 ids[2]; + int ret; + + dev_dbg(dev, "%s\n", __func__); + + tsp->sci = ti_sci_get_by_phandle(dev, "ti,sci"); + if (IS_ERR(tsp->sci)) { + dev_err(dev, "ti_sci get failed: %ld\n", PTR_ERR(tsp->sci)); + return PTR_ERR(tsp->sci); + } + + ret = dev_read_u32_array(dev, "ti,sci-proc-ids", ids, 2); + if (ret) { + dev_err(dev, "Proc IDs not populated %d\n", ret); + return ret; + } + + tsp->ops = &tsp->sci->ops.proc_ops; + tsp->proc_id = ids[0]; + tsp->host_id = ids[1]; + tsp->dev_id = dev_read_u32_default(dev, "ti,sci-dev-id", + TI_SCI_RESOURCE_NULL); + if (tsp->dev_id == TI_SCI_RESOURCE_NULL) { + dev_err(dev, "Device ID not populated %d\n", ret); + return -ENODEV; + } + + return 0; +} + +static int k3_r5f_of_to_priv(struct k3_r5f_core *core) +{ + int ret; + + dev_dbg(core->dev, "%s\n", __func__); + + core->atcm_enable = dev_read_u32_default(core->dev, "ti,atcm-enable", 0); + core->btcm_enable = dev_read_u32_default(core->dev, "ti,btcm-enable", 1); + core->loczrama = dev_read_u32_default(core->dev, "ti,loczrama", 1); + + ret = ti_sci_proc_of_to_priv(core->dev, &core->tsp); + if (ret) + return ret; + + ret = reset_get_by_index(core->dev, 0, &core->reset); + if (ret) { + dev_err(core->dev, "Reset lines not available: %d\n", ret); + return ret; + } + + core->ipdata = (struct k3_r5f_ip_data *)dev_get_driver_data(core->dev); + + return 0; +} + +static int k3_r5f_core_of_get_memories(struct k3_r5f_core *core) +{ + static const char * const mem_names[] = {"atcm", "btcm"}; + struct udevice *dev = core->dev; + int i; + + dev_dbg(dev, "%s\n", __func__); + + core->num_mems = ARRAY_SIZE(mem_names); + core->mem = calloc(core->num_mems, sizeof(*core->mem)); + if (!core->mem) + return -ENOMEM; + + for (i = 0; i < core->num_mems; i++) { + core->mem[i].bus_addr = dev_read_addr_size_name(dev, + mem_names[i], + (fdt_addr_t *)&core->mem[i].size); + if (core->mem[i].bus_addr == FDT_ADDR_T_NONE) { + dev_err(dev, "%s bus address not found\n", + mem_names[i]); + return -EINVAL; + } + core->mem[i].cpu_addr = map_physmem(core->mem[i].bus_addr, + core->mem[i].size, + MAP_NOCACHE); + if (!strcmp(mem_names[i], "atcm")) { + core->mem[i].dev_addr = core->loczrama ? + 0 : K3_R5_TCM_DEV_ADDR; + } else { + core->mem[i].dev_addr = core->loczrama ? + K3_R5_TCM_DEV_ADDR : 0; + } + + dev_dbg(dev, "memory %8s: bus addr %pa size 0x%zx va %p da 0x%x\n", + mem_names[i], &core->mem[i].bus_addr, + core->mem[i].size, core->mem[i].cpu_addr, + core->mem[i].dev_addr); + } + + return 0; +} + +/* + * Each R5F core within a typical R5FSS instance has a total of 64 KB of TCMs, + * split equally into two 32 KB banks between ATCM and BTCM. The TCMs from both + * cores are usable in Split-mode, but only the Core0 TCMs can be used in + * LockStep-mode. The newer revisions of the R5FSS IP maximizes these TCMs by + * leveraging the Core1 TCMs as well in certain modes where they would have + * otherwise been unusable (Eg: LockStep-mode on J7200 SoCs). This is done by + * making a Core1 TCM visible immediately after the corresponding Core0 TCM. + * The SoC memory map uses the larger 64 KB sizes for the Core0 TCMs, and the + * dts representation reflects this increased size on supported SoCs. The Core0 + * TCM sizes therefore have to be adjusted to only half the original size in + * Split mode. + */ +static void k3_r5f_core_adjust_tcm_sizes(struct k3_r5f_core *core) +{ + struct k3_r5f_cluster *cluster = core->cluster; + + if (cluster->mode == CLUSTER_MODE_LOCKSTEP) + return; + + if (!core->ipdata->tcm_is_double) + return; + + if (core == cluster->cores[0]) { + core->mem[0].size /= 2; + core->mem[1].size /= 2; + + dev_dbg(core->dev, "adjusted TCM sizes, ATCM = 0x%zx BTCM = 0x%zx\n", + core->mem[0].size, core->mem[1].size); + } +} + +/** + * k3_r5f_probe() - Basic probe + * @dev: corresponding k3 remote processor device + * + * Return: 0 if all goes good, else appropriate error message. + */ +static int k3_r5f_probe(struct udevice *dev) +{ + struct k3_r5f_cluster *cluster = dev_get_priv(dev->parent); + struct k3_r5f_core *core = dev_get_priv(dev); + bool r_state; + int ret; + + dev_dbg(dev, "%s\n", __func__); + + core->dev = dev; + ret = k3_r5f_of_to_priv(core); + if (ret) + return ret; + + core->cluster = cluster; + /* Assume Primary core gets probed first */ + if (!cluster->cores[0]) + cluster->cores[0] = core; + else + cluster->cores[1] = core; + + ret = k3_r5f_core_of_get_memories(core); + if (ret) { + dev_err(dev, "Rproc getting internal memories failed\n"); + return ret; + } + + ret = core->tsp.sci->ops.dev_ops.is_on(core->tsp.sci, core->tsp.dev_id, + &r_state, &core->in_use); + if (ret) + return ret; + + if (core->in_use) { + dev_info(dev, "Core %d is already in use. No rproc commands work\n", + core->tsp.proc_id); + return 0; + } + + /* Make sure Local reset is asserted. Redundant? */ + reset_assert(&core->reset); + + ret = k3_r5f_rproc_configure(core); + if (ret) { + dev_err(dev, "rproc configure failed %d\n", ret); + return ret; + } + + k3_r5f_core_adjust_tcm_sizes(core); + + dev_dbg(dev, "Remoteproc successfully probed\n"); + + return 0; +} + +static int k3_r5f_remove(struct udevice *dev) +{ + struct k3_r5f_core *core = dev_get_priv(dev); + + free(core->mem); + + ti_sci_proc_release(&core->tsp); + + return 0; +} + +static const struct k3_r5f_ip_data k3_data = { + .tcm_is_double = false, + .tcm_ecc_autoinit = false, +}; + +static const struct k3_r5f_ip_data j7200_data = { + .tcm_is_double = true, + .tcm_ecc_autoinit = true, +}; + +static const struct udevice_id k3_r5f_rproc_ids[] = { + { .compatible = "ti,am654-r5f", .data = (ulong)&k3_data, }, + { .compatible = "ti,j721e-r5f", .data = (ulong)&k3_data, }, + { .compatible = "ti,j7200-r5f", .data = (ulong)&j7200_data, }, + {} +}; + +U_BOOT_DRIVER(k3_r5f_rproc) = { + .name = "k3_r5f_rproc", + .of_match = k3_r5f_rproc_ids, + .id = UCLASS_REMOTEPROC, + .ops = &k3_r5f_rproc_ops, + .probe = k3_r5f_probe, + .remove = k3_r5f_remove, + .priv_auto = sizeof(struct k3_r5f_core), +}; + +static int k3_r5f_cluster_probe(struct udevice *dev) +{ + struct k3_r5f_cluster *cluster = dev_get_priv(dev); + + dev_dbg(dev, "%s\n", __func__); + + cluster->mode = dev_read_u32_default(dev, "ti,cluster-mode", + CLUSTER_MODE_LOCKSTEP); + + if (device_get_child_count(dev) != 2) { + dev_err(dev, "Invalid number of R5 cores"); + return -EINVAL; + } + + dev_dbg(dev, "%s: Cluster successfully probed in %s mode\n", + __func__, cluster->mode ? "lockstep" : "split"); + + return 0; +} + +static const struct udevice_id k3_r5fss_ids[] = { + { .compatible = "ti,am654-r5fss"}, + { .compatible = "ti,j721e-r5fss"}, + { .compatible = "ti,j7200-r5fss"}, + {} +}; + +U_BOOT_DRIVER(k3_r5fss) = { + .name = "k3_r5fss", + .of_match = k3_r5fss_ids, + .id = UCLASS_MISC, + .probe = k3_r5f_cluster_probe, + .priv_auto = sizeof(struct k3_r5f_cluster), + .flags = DM_FLAG_DEFAULT_PD_CTRL_OFF, +}; diff --git a/roms/u-boot/drivers/remoteproc/ti_power_proc.c b/roms/u-boot/drivers/remoteproc/ti_power_proc.c new file mode 100644 index 000000000..86d544cc8 --- /dev/null +++ b/roms/u-boot/drivers/remoteproc/ti_power_proc.c @@ -0,0 +1,182 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2015-2016 + * Texas Instruments Incorporated - http://www.ti.com/ + */ +#define pr_fmt(fmt) "%s: " fmt, __func__ +#include <common.h> +#include <dm.h> +#include <errno.h> +#include <fdtdec.h> +#include <log.h> +#include <remoteproc.h> +#include <asm/global_data.h> +#include <mach/psc_defs.h> + +DECLARE_GLOBAL_DATA_PTR; + +/** + * struct ti_powerproc_privdata - power processor private data + * @loadaddr: base address for loading the power processor + * @psc_module: psc module address. + */ +struct ti_powerproc_privdata { + phys_addr_t loadaddr; + u32 psc_module; +}; + +/** + * ti_of_to_priv() - generate private data from device tree + * @dev: corresponding ti remote processor device + * @priv: pointer to driver specific private data + * + * Return: 0 if all went ok, else corresponding -ve error + */ +static int ti_of_to_priv(struct udevice *dev, + struct ti_powerproc_privdata *priv) +{ + int node = dev_of_offset(dev); + const void *blob = gd->fdt_blob; + int tmp; + + if (!blob) { + debug("'%s' no dt?\n", dev->name); + return -EINVAL; + } + + priv->loadaddr = fdtdec_get_addr(blob, node, "reg"); + if (priv->loadaddr == FDT_ADDR_T_NONE) { + debug("'%s': no 'reg' property\n", dev->name); + return -EINVAL; + } + + tmp = fdtdec_get_int(blob, node, "ti,lpsc_module", -EINVAL); + if (tmp < 0) { + debug("'%s': no 'ti,lpsc_module' property\n", dev->name); + return tmp; + } + priv->psc_module = tmp; + + return 0; +} + +/** + * ti_powerproc_probe() - Basic probe + * @dev: corresponding ti remote processor device + * + * Return: 0 if all went ok, else corresponding -ve error + */ +static int ti_powerproc_probe(struct udevice *dev) +{ + struct dm_rproc_uclass_pdata *uc_pdata; + struct ti_powerproc_privdata *priv; + int ret; + + uc_pdata = dev_get_uclass_plat(dev); + priv = dev_get_priv(dev); + + ret = ti_of_to_priv(dev, priv); + + debug("%s probed with slave_addr=0x%08lX module=%d(%d)\n", + uc_pdata->name, priv->loadaddr, priv->psc_module, ret); + + return ret; +} + +/** + * ti_powerproc_load() - Loadup the TI remote processor + * @dev: corresponding ti remote processor device + * @addr: Address in memory where image binary is stored + * @size: Size in bytes of the image binary + * + * Return: 0 if all went ok, else corresponding -ve error + */ +static int ti_powerproc_load(struct udevice *dev, ulong addr, ulong size) +{ + struct dm_rproc_uclass_pdata *uc_pdata; + struct ti_powerproc_privdata *priv; + int ret; + + uc_pdata = dev_get_uclass_plat(dev); + if (!uc_pdata) { + debug("%s: no uc pdata!\n", dev->name); + return -EINVAL; + } + + priv = dev_get_priv(dev); + ret = psc_module_keep_in_reset_enabled(priv->psc_module, false); + if (ret) { + debug("%s Unable to disable module '%d'(ret=%d)\n", + uc_pdata->name, priv->psc_module, ret); + return ret; + } + + debug("%s: Loading binary from 0x%08lX, size 0x%08lX to 0x%08lX\n", + uc_pdata->name, addr, size, priv->loadaddr); + + memcpy((void *)priv->loadaddr, (void *)addr, size); + + debug("%s: Complete!\n", uc_pdata->name); + return 0; +} + +/** + * ti_powerproc_start() - (replace: short desc) + * @dev: corresponding ti remote processor device + * + * Return: 0 if all went ok, else corresponding -ve error + */ +static int ti_powerproc_start(struct udevice *dev) +{ + struct dm_rproc_uclass_pdata *uc_pdata; + struct ti_powerproc_privdata *priv; + int ret; + + uc_pdata = dev_get_uclass_plat(dev); + if (!uc_pdata) { + debug("%s: no uc pdata!\n", dev->name); + return -EINVAL; + } + + priv = dev_get_priv(dev); + ret = psc_disable_module(priv->psc_module); + if (ret) { + debug("%s Unable to disable module '%d'(ret=%d)\n", + uc_pdata->name, priv->psc_module, ret); + return ret; + } + + ret = psc_module_release_from_reset(priv->psc_module); + if (ret) { + debug("%s Failed to wait for module '%d'(ret=%d)\n", + uc_pdata->name, priv->psc_module, ret); + return ret; + } + ret = psc_enable_module(priv->psc_module); + if (ret) { + debug("%s Unable to disable module '%d'(ret=%d)\n", + uc_pdata->name, priv->psc_module, ret); + return ret; + } + + return 0; +} + +static const struct dm_rproc_ops ti_powerproc_ops = { + .load = ti_powerproc_load, + .start = ti_powerproc_start, +}; + +static const struct udevice_id ti_powerproc_ids[] = { + {.compatible = "ti,power-processor"}, + {} +}; + +U_BOOT_DRIVER(ti_powerproc) = { + .name = "ti_power_proc", + .of_match = ti_powerproc_ids, + .id = UCLASS_REMOTEPROC, + .ops = &ti_powerproc_ops, + .probe = ti_powerproc_probe, + .priv_auto = sizeof(struct ti_powerproc_privdata), +}; diff --git a/roms/u-boot/drivers/remoteproc/ti_sci_proc.h b/roms/u-boot/drivers/remoteproc/ti_sci_proc.h new file mode 100644 index 000000000..f8299d1af --- /dev/null +++ b/roms/u-boot/drivers/remoteproc/ti_sci_proc.h @@ -0,0 +1,148 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Texas Instruments TI-SCI Processor Controller Helper Functions + * + * Copyright (C) 2018-2019 Texas Instruments Incorporated - http://www.ti.com/ + * Lokesh Vutla <lokeshvutla@ti.com> + * Suman Anna <s-anna@ti.com> + */ + +#ifndef REMOTEPROC_TI_SCI_PROC_H +#define REMOTEPROC_TI_SCI_PROC_H + +#define TISCI_INVALID_HOST 0xff + +/** + * struct ti_sci_proc - structure representing a processor control client + * @sci: cached TI-SCI protocol handle + * @ops: cached TI-SCI proc ops + * @proc_id: processor id for the consumer remoteproc device + * @host_id: host id to pass the control over for this consumer remoteproc + * device + * @dev_id: Device ID as identified by system controller. + */ +struct ti_sci_proc { + const struct ti_sci_handle *sci; + const struct ti_sci_proc_ops *ops; + u8 proc_id; + u8 host_id; + u16 dev_id; +}; + +static inline int ti_sci_proc_request(struct ti_sci_proc *tsp) +{ + int ret; + + debug("%s: proc_id = %d\n", __func__, tsp->proc_id); + + ret = tsp->ops->proc_request(tsp->sci, tsp->proc_id); + if (ret) + pr_err("ti-sci processor request failed: %d\n", ret); + return ret; +} + +static inline int ti_sci_proc_release(struct ti_sci_proc *tsp) +{ + int ret; + + debug("%s: proc_id = %d\n", __func__, tsp->proc_id); + + if (tsp->host_id != TISCI_INVALID_HOST) + ret = tsp->ops->proc_handover(tsp->sci, tsp->proc_id, + tsp->host_id); + else + ret = tsp->ops->proc_release(tsp->sci, tsp->proc_id); + + if (ret) + pr_err("ti-sci processor release failed: %d\n", ret); + return ret; +} + +static inline int ti_sci_proc_handover(struct ti_sci_proc *tsp) +{ + int ret; + + debug("%s: proc_id = %d\n", __func__, tsp->proc_id); + + ret = tsp->ops->proc_handover(tsp->sci, tsp->proc_id, tsp->host_id); + if (ret) + pr_err("ti-sci processor handover of %d to %d failed: %d\n", + tsp->proc_id, tsp->host_id, ret); + return ret; +} + +static inline int ti_sci_proc_get_status(struct ti_sci_proc *tsp, + u64 *boot_vector, u32 *cfg_flags, + u32 *ctrl_flags, u32 *status_flags) +{ + int ret; + + ret = tsp->ops->get_proc_boot_status(tsp->sci, tsp->proc_id, + boot_vector, cfg_flags, ctrl_flags, + status_flags); + if (ret) + pr_err("ti-sci processor get_status failed: %d\n", ret); + + debug("%s: proc_id = %d, boot_vector = 0x%llx, cfg_flags = 0x%x, ctrl_flags = 0x%x, sts = 0x%x\n", + __func__, tsp->proc_id, *boot_vector, *cfg_flags, *ctrl_flags, + *status_flags); + return ret; +} + +static inline int ti_sci_proc_set_config(struct ti_sci_proc *tsp, + u64 boot_vector, + u32 cfg_set, u32 cfg_clr) +{ + int ret; + + debug("%s: proc_id = %d, boot_vector = 0x%llx, cfg_set = 0x%x, cfg_clr = 0x%x\n", + __func__, tsp->proc_id, boot_vector, cfg_set, cfg_clr); + + ret = tsp->ops->set_proc_boot_cfg(tsp->sci, tsp->proc_id, boot_vector, + cfg_set, cfg_clr); + if (ret) + pr_err("ti-sci processor set_config failed: %d\n", ret); + return ret; +} + +static inline int ti_sci_proc_set_control(struct ti_sci_proc *tsp, + u32 ctrl_set, u32 ctrl_clr) +{ + int ret; + + debug("%s: proc_id = %d, ctrl_set = 0x%x, ctrl_clr = 0x%x\n", __func__, + tsp->proc_id, ctrl_set, ctrl_clr); + + ret = tsp->ops->set_proc_boot_ctrl(tsp->sci, tsp->proc_id, ctrl_set, + ctrl_clr); + if (ret) + pr_err("ti-sci processor set_control failed: %d\n", ret); + return ret; +} + +static inline int ti_sci_proc_power_domain_on(struct ti_sci_proc *tsp) +{ + int ret; + + debug("%s: dev_id = %d\n", __func__, tsp->dev_id); + + ret = tsp->sci->ops.dev_ops.get_device_exclusive(tsp->sci, tsp->dev_id); + if (ret) + pr_err("Power-domain on failed for dev = %d\n", tsp->dev_id); + + return ret; +} + +static inline int ti_sci_proc_power_domain_off(struct ti_sci_proc *tsp) +{ + int ret; + + debug("%s: dev_id = %d\n", __func__, tsp->dev_id); + + ret = tsp->sci->ops.dev_ops.put_device(tsp->sci, tsp->dev_id); + if (ret) + pr_err("Power-domain off failed for dev = %d\n", tsp->dev_id); + + return ret; +} +#endif /* REMOTEPROC_TI_SCI_PROC_H */ |