summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/Kconfig7
-rw-r--r--drivers/clk/Makefile1
-rw-r--r--drivers/clk/clk-cdce9xx.c254
-rw-r--r--drivers/core/device.c11
-rw-r--r--drivers/mmc/am654_sdhci.c13
-rw-r--r--drivers/remoteproc/Kconfig20
-rw-r--r--drivers/remoteproc/Makefile2
-rw-r--r--drivers/remoteproc/rproc-elf-loader.c179
-rw-r--r--drivers/remoteproc/sandbox_testproc.c4
-rw-r--r--drivers/remoteproc/stm32_copro.c21
-rw-r--r--drivers/remoteproc/ti_k3_dsp_rproc.c354
-rw-r--r--drivers/remoteproc/ti_k3_r5f_rproc.c816
-rw-r--r--drivers/remoteproc/ti_sci_proc.h27
13 files changed, 1681 insertions, 28 deletions
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 0035f0a9c6..16d4237f89 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -134,6 +134,13 @@ config CLK_STM32MP1
Enable the STM32 clock (RCC) driver. Enable support for
manipulating STM32MP1's on-SoC clocks.
+config CLK_CDCE9XX
+ bool "Enable CDCD9XX clock driver"
+ depends on CLK
+ help
+ Enable the clock synthesizer driver for CDCE913/925/937/949
+ series of chips.
+
source "drivers/clk/analogbits/Kconfig"
source "drivers/clk/at91/Kconfig"
source "drivers/clk/exynos/Kconfig"
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index d7cea3b8bf..8de6777468 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -44,3 +44,4 @@ obj-$(CONFIG_SANDBOX_CLK_CCF) += clk_sandbox_ccf.o
obj-$(CONFIG_STM32H7) += clk_stm32h7.o
obj-$(CONFIG_CLK_TI_SCI) += clk-ti-sci.o
obj-$(CONFIG_CLK_VERSAL) += clk_versal.o
+obj-$(CONFIG_CLK_CDCE9XX) += clk-cdce9xx.o
diff --git a/drivers/clk/clk-cdce9xx.c b/drivers/clk/clk-cdce9xx.c
new file mode 100644
index 0000000000..5d1489ab0e
--- /dev/null
+++ b/drivers/clk/clk-cdce9xx.c
@@ -0,0 +1,254 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Texas Instruments CDCE913/925/937/949 clock synthesizer driver
+ *
+ * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
+ * Tero Kristo <t-kristo@ti.com>
+ *
+ * Based on Linux kernel clk-cdce925.c.
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <clk-uclass.h>
+#include <i2c.h>
+
+#define MAX_NUMBER_OF_PLLS 4
+#define MAX_NUMER_OF_OUTPUTS 9
+
+#define CDCE9XX_REG_GLOBAL1 0x01
+#define CDCE9XX_REG_Y1SPIPDIVH 0x02
+#define CDCE9XX_REG_PDIV1L 0x03
+#define CDCE9XX_REG_XCSEL 0x05
+
+#define CDCE9XX_PDIV1_H_MASK 0x3
+
+#define CDCE9XX_REG_PDIV(clk) (0x16 + (((clk) - 1) & 1) + \
+ ((clk) - 1) / 2 * 0x10)
+
+#define CDCE9XX_PDIV_MASK 0x7f
+
+#define CDCE9XX_BYTE_TRANSFER BIT(7)
+
+struct cdce9xx_chip_info {
+ int num_plls;
+ int num_outputs;
+};
+
+struct cdce9xx_clk_data {
+ struct udevice *i2c;
+ struct cdce9xx_chip_info *chip;
+ u32 xtal_rate;
+};
+
+static const struct cdce9xx_chip_info cdce913_chip_info = {
+ .num_plls = 1, .num_outputs = 3,
+};
+
+static const struct cdce9xx_chip_info cdce925_chip_info = {
+ .num_plls = 2, .num_outputs = 5,
+};
+
+static const struct cdce9xx_chip_info cdce937_chip_info = {
+ .num_plls = 3, .num_outputs = 7,
+};
+
+static const struct cdce9xx_chip_info cdce949_chip_info = {
+ .num_plls = 4, .num_outputs = 9,
+};
+
+static int cdce9xx_reg_read(struct udevice *dev, u8 addr, u8 *buf)
+{
+ struct cdce9xx_clk_data *data = dev_get_priv(dev);
+ int ret;
+
+ ret = dm_i2c_read(data->i2c, addr | CDCE9XX_BYTE_TRANSFER, buf, 1);
+ if (ret)
+ dev_err(dev, "%s: failed for addr:%x, ret:%d\n", __func__,
+ addr, ret);
+
+ return ret;
+}
+
+static int cdce9xx_reg_write(struct udevice *dev, u8 addr, u8 val)
+{
+ struct cdce9xx_clk_data *data = dev_get_priv(dev);
+ int ret;
+
+ ret = dm_i2c_write(data->i2c, addr | CDCE9XX_BYTE_TRANSFER, &val, 1);
+ if (ret)
+ dev_err(dev, "%s: failed for addr:%x, ret:%d\n", __func__,
+ addr, ret);
+
+ return ret;
+}
+
+static int cdce9xx_clk_of_xlate(struct clk *clk,
+ struct ofnode_phandle_args *args)
+{
+ struct cdce9xx_clk_data *data = dev_get_priv(clk->dev);
+
+ if (args->args_count != 1)
+ return -EINVAL;
+
+ if (args->args[0] > data->chip->num_outputs)
+ return -EINVAL;
+
+ clk->id = args->args[0];
+
+ return 0;
+}
+
+static int cdce9xx_clk_probe(struct udevice *dev)
+{
+ struct cdce9xx_clk_data *data = dev_get_priv(dev);
+ struct cdce9xx_chip_info *chip = (void *)dev_get_driver_data(dev);
+ int ret;
+ u32 val;
+ struct clk clk;
+
+ val = (u32)dev_read_addr_ptr(dev);
+
+ ret = i2c_get_chip(dev->parent, val, 1, &data->i2c);
+ if (ret) {
+ dev_err(dev, "I2C probe failed.\n");
+ return ret;
+ }
+
+ data->chip = chip;
+
+ ret = clk_get_by_index(dev, 0, &clk);
+ data->xtal_rate = clk_get_rate(&clk);
+
+ val = dev_read_u32_default(dev, "xtal-load-pf", -1);
+ if (val >= 0)
+ cdce9xx_reg_write(dev, CDCE9XX_REG_XCSEL, val << 3);
+
+ return 0;
+}
+
+static u16 cdce9xx_clk_get_pdiv(struct clk *clk)
+{
+ u8 val;
+ u16 pdiv;
+ int ret;
+
+ if (clk->id == 0) {
+ ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_Y1SPIPDIVH, &val);
+ if (ret)
+ return 0;
+
+ pdiv = (val & CDCE9XX_PDIV1_H_MASK) << 8;
+
+ ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_PDIV1L, &val);
+ if (ret)
+ return 0;
+
+ pdiv |= val;
+ } else {
+ ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_PDIV(clk->id),
+ &val);
+ if (ret)
+ return 0;
+
+ pdiv = val & CDCE9XX_PDIV_MASK;
+ }
+
+ return pdiv;
+}
+
+static u32 cdce9xx_clk_get_parent_rate(struct clk *clk)
+{
+ struct cdce9xx_clk_data *data = dev_get_priv(clk->dev);
+
+ return data->xtal_rate;
+}
+
+static ulong cdce9xx_clk_get_rate(struct clk *clk)
+{
+ u32 parent_rate;
+ u16 pdiv;
+
+ parent_rate = cdce9xx_clk_get_parent_rate(clk);
+
+ pdiv = cdce9xx_clk_get_pdiv(clk);
+
+ return parent_rate / pdiv;
+}
+
+static ulong cdce9xx_clk_set_rate(struct clk *clk, ulong rate)
+{
+ u32 parent_rate;
+ int pdiv;
+ u32 diff;
+ u8 val;
+ int ret;
+
+ parent_rate = cdce9xx_clk_get_parent_rate(clk);
+
+ pdiv = parent_rate / rate;
+
+ diff = rate - parent_rate / pdiv;
+
+ if (rate - parent_rate / (pdiv + 1) < diff)
+ pdiv++;
+
+ if (clk->id == 0) {
+ ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_Y1SPIPDIVH, &val);
+ if (ret)
+ return ret;
+
+ val &= ~CDCE9XX_PDIV1_H_MASK;
+
+ val |= (pdiv >> 8);
+
+ ret = cdce9xx_reg_write(clk->dev, CDCE9XX_REG_Y1SPIPDIVH, val);
+ if (ret)
+ return ret;
+
+ ret = cdce9xx_reg_write(clk->dev, CDCE9XX_REG_PDIV1L,
+ (pdiv & 0xff));
+ if (ret)
+ return ret;
+ } else {
+ ret = cdce9xx_reg_read(clk->dev, CDCE9XX_REG_PDIV(clk->id),
+ &val);
+ if (ret)
+ return ret;
+
+ val &= ~CDCE9XX_PDIV_MASK;
+
+ val |= pdiv;
+
+ ret = cdce9xx_reg_write(clk->dev, CDCE9XX_REG_PDIV(clk->id),
+ val);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct udevice_id cdce9xx_clk_of_match[] = {
+ { .compatible = "ti,cdce913", .data = (u32)&cdce913_chip_info },
+ { .compatible = "ti,cdce925", .data = (u32)&cdce925_chip_info },
+ { .compatible = "ti,cdce937", .data = (u32)&cdce937_chip_info },
+ { .compatible = "ti,cdce949", .data = (u32)&cdce949_chip_info },
+ { /* sentinel */ },
+};
+
+static const struct clk_ops cdce9xx_clk_ops = {
+ .of_xlate = cdce9xx_clk_of_xlate,
+ .get_rate = cdce9xx_clk_get_rate,
+ .set_rate = cdce9xx_clk_set_rate,
+};
+
+U_BOOT_DRIVER(cdce9xx_clk) = {
+ .name = "cdce9xx-clk",
+ .id = UCLASS_CLK,
+ .of_match = cdce9xx_clk_of_match,
+ .probe = cdce9xx_clk_probe,
+ .priv_auto_alloc_size = sizeof(struct cdce9xx_clk_data),
+ .ops = &cdce9xx_clk_ops,
+};
diff --git a/drivers/core/device.c b/drivers/core/device.c
index 84f0f0fbf0..ce66c72e5e 100644
--- a/drivers/core/device.c
+++ b/drivers/core/device.c
@@ -568,6 +568,17 @@ int device_get_child(struct udevice *parent, int index, struct udevice **devp)
return -ENODEV;
}
+int device_get_child_count(struct udevice *parent)
+{
+ struct udevice *dev;
+ int count = 0;
+
+ list_for_each_entry(dev, &parent->child_head, sibling_node)
+ count++;
+
+ return count;
+}
+
int device_find_child_by_seq(struct udevice *parent, int seq_or_req_seq,
bool find_req_seq, struct udevice **devp)
{
diff --git a/drivers/mmc/am654_sdhci.c b/drivers/mmc/am654_sdhci.c
index 1793a3f99a..7cd5516197 100644
--- a/drivers/mmc/am654_sdhci.c
+++ b/drivers/mmc/am654_sdhci.c
@@ -219,23 +219,10 @@ static int am654_sdhci_probe(struct udevice *dev)
struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
struct sdhci_host *host = dev_get_priv(dev);
struct mmc_config *cfg = &plat->cfg;
- struct power_domain sdhci_pwrdmn;
struct clk clk;
unsigned long clock;
int ret;
- ret = power_domain_get_by_index(dev, &sdhci_pwrdmn, 0);
- if (!ret) {
- ret = power_domain_on(&sdhci_pwrdmn);
- if (ret) {
- dev_err(dev, "Power domain on failed (%d)\n", ret);
- return ret;
- }
- } else if (ret != -ENOENT && ret != -ENODEV && ret != -ENOSYS) {
- dev_err(dev, "failed to get power domain (%d)\n", ret);
- return ret;
- }
-
ret = clk_get_by_index(dev, 0, &clk);
if (ret) {
dev_err(dev, "failed to get clock\n");
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index f54a245424..7c2e4804b5 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -52,6 +52,26 @@ config REMOTEPROC_TI_K3_ARM64
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
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 271ba55b09..69ae7bd1e8 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -11,4 +11,6 @@ obj-$(CONFIG_K3_SYSTEM_CONTROLLER) += k3_system_controller.o
obj-$(CONFIG_REMOTEPROC_SANDBOX) += sandbox_testproc.o
obj-$(CONFIG_REMOTEPROC_STM32_COPRO) += stm32_copro.o
obj-$(CONFIG_REMOTEPROC_TI_K3_ARM64) += ti_k3_arm64_rproc.o
+obj-$(CONFIG_REMOTEPROC_TI_K3_DSP) += ti_k3_dsp_rproc.o
+obj-$(CONFIG_REMOTEPROC_TI_K3_R5F) += ti_k3_r5f_rproc.o
obj-$(CONFIG_REMOTEPROC_TI_POWER) += ti_power_proc.o
diff --git a/drivers/remoteproc/rproc-elf-loader.c b/drivers/remoteproc/rproc-elf-loader.c
index 67937a7595..b38a226065 100644
--- a/drivers/remoteproc/rproc-elf-loader.c
+++ b/drivers/remoteproc/rproc-elf-loader.c
@@ -64,13 +64,90 @@ int rproc_elf32_sanity_check(ulong addr, ulong size)
return 0;
}
-/* A very simple elf loader, assumes the image is valid */
-int rproc_elf32_load_image(struct udevice *dev, unsigned long addr)
+/* 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;
+ 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);
@@ -86,7 +163,8 @@ int rproc_elf32_load_image(struct udevice *dev, unsigned long addr)
continue;
if (ops->device_to_virt)
- dst = ops->device_to_virt(dev, (ulong)dst);
+ 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);
@@ -104,3 +182,96 @@ int rproc_elf32_load_image(struct udevice *dev, unsigned long addr)
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);
+}
diff --git a/drivers/remoteproc/sandbox_testproc.c b/drivers/remoteproc/sandbox_testproc.c
index 5f35119ab7..eeee49c4dd 100644
--- a/drivers/remoteproc/sandbox_testproc.c
+++ b/drivers/remoteproc/sandbox_testproc.c
@@ -306,9 +306,11 @@ static int sandbox_testproc_ping(struct udevice *dev)
* 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)
+static void *sandbox_testproc_device_to_virt(struct udevice *dev, ulong da,
+ ulong size)
{
u64 paddr;
diff --git a/drivers/remoteproc/stm32_copro.c b/drivers/remoteproc/stm32_copro.c
index ad941f67e8..40bba37211 100644
--- a/drivers/remoteproc/stm32_copro.c
+++ b/drivers/remoteproc/stm32_copro.c
@@ -107,11 +107,13 @@ static int stm32_copro_set_hold_boot(struct udevice *dev, bool hold)
* 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)
+static void *stm32_copro_device_to_virt(struct udevice *dev, ulong da,
+ ulong size)
{
- fdt32_t in_addr = cpu_to_be32(da);
+ fdt32_t in_addr = cpu_to_be32(da), end_addr;
u64 paddr;
paddr = dev_translate_dma_address(dev, &in_addr);
@@ -120,6 +122,12 @@ static void *stm32_copro_device_to_virt(struct udevice *dev, ulong 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);
}
@@ -147,14 +155,7 @@ static int stm32_copro_load(struct udevice *dev, ulong addr, ulong size)
return ret;
}
- /* Support only ELF32 image */
- ret = rproc_elf32_sanity_check(addr, size);
- if (ret) {
- dev_err(dev, "Invalid ELF32 image (%d)\n", ret);
- return ret;
- }
-
- return rproc_elf32_load_image(dev, addr);
+ return rproc_elf32_load_image(dev, addr, size);
}
/**
diff --git a/drivers/remoteproc/ti_k3_dsp_rproc.c b/drivers/remoteproc/ti_k3_dsp_rproc.c
new file mode 100644
index 0000000000..c5dc6b25da
--- /dev/null
+++ b/drivers/remoteproc/ti_k3_dsp_rproc.c
@@ -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),
+};
diff --git a/drivers/remoteproc/ti_k3_r5f_rproc.c b/drivers/remoteproc/ti_k3_r5f_rproc.c
new file mode 100644
index 0000000000..ae1e4b9e04
--- /dev/null
+++ b/drivers/remoteproc/ti_k3_r5f_rproc.c
@@ -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),
+};
diff --git a/drivers/remoteproc/ti_sci_proc.h b/drivers/remoteproc/ti_sci_proc.h
index ccfc39ec88..f8299d1aff 100644
--- a/drivers/remoteproc/ti_sci_proc.h
+++ b/drivers/remoteproc/ti_sci_proc.h
@@ -19,12 +19,14 @@
* @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)
@@ -118,4 +120,29 @@ static inline int ti_sci_proc_set_control(struct ti_sci_proc *tsp,
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 */