GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -0,0 +1,323 @@
|
||||
// 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 <remoteproc.h>
|
||||
#include <errno.h>
|
||||
#include <mailbox.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(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(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(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(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_alloc_size = sizeof(struct k3_sysctrler_privdata),
|
||||
};
|
||||
@@ -0,0 +1,278 @@
|
||||
// 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 <remoteproc.h>
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Basic function to verify ELF image format */
|
||||
int rproc_elf_sanity_check(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_sanity_check(addr, size);
|
||||
else
|
||||
return rproc_elf32_sanity_check(addr, size);
|
||||
}
|
||||
|
||||
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) {
|
||||
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));
|
||||
++phdr;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,436 @@
|
||||
// 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 <malloc.h>
|
||||
#include <remoteproc.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_platdata(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_platdata(dev);
|
||||
|
||||
/* See if we need to populate via fdt */
|
||||
|
||||
if (!dev->platdata) {
|
||||
#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->platdata;
|
||||
|
||||
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_platdata_auto_alloc_size =
|
||||
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->flags & 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_platdata(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_platdata(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);
|
||||
};
|
||||
@@ -0,0 +1,357 @@
|
||||
// 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 <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_platdata(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_platdata(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_platdata(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_platdata(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_platdata(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_platdata(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_platdata(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_platdata(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_alloc_size = 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_DEVICE(proc_3_demo) = {
|
||||
.name = "sandbox_test_proc",
|
||||
.platdata = &proc_3_test,
|
||||
};
|
||||
@@ -0,0 +1,258 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2019, STMicroelectronics - All Rights Reserved
|
||||
*/
|
||||
#define pr_fmt(fmt) "%s: " fmt, __func__
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <errno.h>
|
||||
#include <fdtdec.h>
|
||||
#include <regmap.h>
|
||||
#include <remoteproc.h>
|
||||
#include <reset.h>
|
||||
#include <syscon.h>
|
||||
#include <asm/io.h>
|
||||
|
||||
#define RCC_GCR_HOLD_BOOT 0
|
||||
#define RCC_GCR_RELEASE_BOOT 1
|
||||
|
||||
/**
|
||||
* struct stm32_copro_privdata - power processor private data
|
||||
* @reset_ctl: reset controller handle
|
||||
* @hold_boot_regmap: regmap for remote processor reset hold boot
|
||||
* @hold_boot_offset: offset of the register controlling the hold boot setting
|
||||
* @hold_boot_mask: bitmask of the register for the hold boot field
|
||||
* @is_running: is the remote processor running
|
||||
*/
|
||||
struct stm32_copro_privdata {
|
||||
struct reset_ctl reset_ctl;
|
||||
struct regmap *hold_boot_regmap;
|
||||
uint hold_boot_offset;
|
||||
uint hold_boot_mask;
|
||||
bool is_running;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
struct regmap *regmap;
|
||||
const fdt32_t *cell;
|
||||
int len, ret;
|
||||
|
||||
priv = dev_get_priv(dev);
|
||||
|
||||
regmap = syscon_regmap_lookup_by_phandle(dev, "st,syscfg-holdboot");
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(dev, "unable to find holdboot regmap (%ld)\n",
|
||||
PTR_ERR(regmap));
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
cell = dev_read_prop(dev, "st,syscfg-holdboot", &len);
|
||||
if (len < 3 * sizeof(fdt32_t)) {
|
||||
dev_err(dev, "holdboot offset and mask not available\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
priv->hold_boot_regmap = regmap;
|
||||
priv->hold_boot_offset = fdtdec_get_number(cell + 1, 1);
|
||||
priv->hold_boot_mask = fdtdec_get_number(cell + 2, 1);
|
||||
|
||||
ret = reset_get_by_index(dev, 0, &priv->reset_ctl);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to get reset (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "probed\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* stm32_copro_set_hold_boot() - Hold boot bit management
|
||||
* @dev: corresponding STM32 remote processor device
|
||||
* @hold: hold boot value
|
||||
* @return 0 if all went ok, else corresponding -ve error
|
||||
*/
|
||||
static int stm32_copro_set_hold_boot(struct udevice *dev, bool hold)
|
||||
{
|
||||
struct stm32_copro_privdata *priv;
|
||||
uint val;
|
||||
int ret;
|
||||
|
||||
priv = dev_get_priv(dev);
|
||||
|
||||
val = hold ? RCC_GCR_HOLD_BOOT : RCC_GCR_RELEASE_BOOT;
|
||||
|
||||
/*
|
||||
* Note: shall run an SMC call (STM32_SMC_RCC) if platform is secured.
|
||||
* To be updated when the code for this SMC service is available which
|
||||
* is not the case for the time being.
|
||||
*/
|
||||
ret = regmap_update_bits(priv->hold_boot_regmap, priv->hold_boot_offset,
|
||||
priv->hold_boot_mask, val);
|
||||
if (ret)
|
||||
dev_err(dev, "failed to set hold boot\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
int ret;
|
||||
|
||||
priv = dev_get_priv(dev);
|
||||
|
||||
ret = stm32_copro_set_hold_boot(dev, true);
|
||||
if (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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
/* move hold boot from true to false start the copro */
|
||||
ret = stm32_copro_set_hold_boot(dev, false);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* Once copro running, reset hold boot flag to avoid copro
|
||||
* rebooting autonomously
|
||||
*/
|
||||
ret = stm32_copro_set_hold_boot(dev, true);
|
||||
priv->is_running = !ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = stm32_copro_set_hold_boot(dev, true);
|
||||
if (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;
|
||||
}
|
||||
|
||||
priv->is_running = false;
|
||||
|
||||
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 1 if the remote processor is running, 0 otherwise
|
||||
*/
|
||||
static int stm32_copro_is_running(struct udevice *dev)
|
||||
{
|
||||
struct stm32_copro_privdata *priv;
|
||||
|
||||
priv = dev_get_priv(dev);
|
||||
return priv->is_running;
|
||||
}
|
||||
|
||||
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_alloc_size = sizeof(struct stm32_copro_privdata),
|
||||
};
|
||||
@@ -0,0 +1,229 @@
|
||||
// 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 <linux/soc/ti/ti_sci_protocol.h>
|
||||
#include "ti_sci_proc.h"
|
||||
|
||||
#define INVALID_ID 0xffff
|
||||
|
||||
#define GTC_CNTCR_REG 0x0
|
||||
#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_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;
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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 = 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_alloc_size = sizeof(struct k3_arm64_privdata),
|
||||
.flags = DM_FLAG_DEFAULT_PD_CTRL_OFF,
|
||||
};
|
||||
@@ -0,0 +1,354 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Texas Instruments' K3 DSP Remoteproc driver
|
||||
*
|
||||
* Copyright (C) 2018-2019 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 <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_privdata - Structure representing Remote processor data.
|
||||
* @rproc_rst: rproc reset control data
|
||||
* @tsp: Pointer to TISCI proc contrl handle
|
||||
* @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_mem *mem;
|
||||
int num_mems;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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);
|
||||
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 = rproc_elf_load_image(dev, addr, size);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "Loading elf failed %d\n", ret);
|
||||
goto proc_release;
|
||||
}
|
||||
|
||||
boot_vector = rproc_elf_get_boot_addr(dev, addr);
|
||||
|
||||
dev_dbg(dev, "%s: Boot vector = 0x%x\n", __func__, boot_vector);
|
||||
|
||||
ret = ti_sci_proc_set_config(&dsp->tsp, boot_vector, 0, 0);
|
||||
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);
|
||||
int ret;
|
||||
|
||||
dev_dbg(dev, "%s\n", __func__);
|
||||
|
||||
ret = ti_sci_proc_request(&dsp->tsp);
|
||||
if (ret)
|
||||
return ret;
|
||||
/*
|
||||
* 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 = ti_sci_proc_power_domain_on(&dsp->tsp);
|
||||
if (ret)
|
||||
goto proc_release;
|
||||
|
||||
ret = reset_deassert(&dsp->dsp_rst);
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 udevice_id k3_dsp_ids[] = {
|
||||
{ .compatible = "ti,j721e-c66-dsp"},
|
||||
{ .compatible = "ti,j721e-c71-dsp"},
|
||||
{}
|
||||
};
|
||||
|
||||
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_alloc_size = sizeof(struct k3_dsp_privdata),
|
||||
};
|
||||
@@ -0,0 +1,816 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Texas Instruments' K3 R5 Remoteproc driver
|
||||
*
|
||||
* Copyright (C) 2018-2019 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 <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
|
||||
|
||||
/* 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_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
|
||||
* @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_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;
|
||||
|
||||
dev_dbg(dev, "%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(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(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]) {
|
||||
printf("Secondary core is not probed in this cluster\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (cluster->mode == CLUSTER_MODE_LOCKSTEP && !is_primary_core(core)) {
|
||||
dev_err(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(dev, "Invalid seq: Enable primary core before loading secondary core\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
u32 boot_vector;
|
||||
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 = k3_r5f_prepare(dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "R5f prepare failed for core %d\n",
|
||||
core->tsp.proc_id);
|
||||
goto proc_release;
|
||||
}
|
||||
|
||||
/* Zero out TCMs so that ECC can be effective on all TCM addresses */
|
||||
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);
|
||||
|
||||
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%x\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(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;
|
||||
|
||||
dev_dbg(dev, "%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;
|
||||
u64 boot_vec = 0;
|
||||
int ret;
|
||||
|
||||
dev_dbg(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 */
|
||||
if (cluster->mode && is_primary_core(core) &&
|
||||
!(sts & PROC_BOOT_STATUS_FLAG_R5_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
|
||||
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(dev, "%s\n", __func__);
|
||||
|
||||
core->atcm_enable = dev_read_u32_default(core->dev, "atcm-enable", 0);
|
||||
core->btcm_enable = dev_read_u32_default(core->dev, "btcm-enable", 1);
|
||||
core->loczrama = dev_read_u32_default(core->dev, "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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
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 udevice_id k3_r5f_rproc_ids[] = {
|
||||
{ .compatible = "ti,am654-r5f"},
|
||||
{ .compatible = "ti,j721e-r5f"},
|
||||
{}
|
||||
};
|
||||
|
||||
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_alloc_size = 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, "lockstep-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"},
|
||||
{}
|
||||
};
|
||||
|
||||
U_BOOT_DRIVER(k3_r5fss) = {
|
||||
.name = "k3_r5fss",
|
||||
.of_match = k3_r5fss_ids,
|
||||
.id = UCLASS_MISC,
|
||||
.probe = k3_r5f_cluster_probe,
|
||||
.priv_auto_alloc_size = sizeof(struct k3_r5f_cluster),
|
||||
};
|
||||
@@ -0,0 +1,180 @@
|
||||
// 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 <remoteproc.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_platdata(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_platdata(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_platdata(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_alloc_size = sizeof(struct ti_powerproc_privdata),
|
||||
};
|
||||
@@ -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 */
|
||||
Reference in New Issue
Block a user