diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clk/Kconfig | 7 | ||||
-rw-r--r-- | drivers/clk/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/clk-cdce9xx.c | 254 | ||||
-rw-r--r-- | drivers/core/device.c | 11 | ||||
-rw-r--r-- | drivers/mmc/am654_sdhci.c | 13 | ||||
-rw-r--r-- | drivers/remoteproc/Kconfig | 20 | ||||
-rw-r--r-- | drivers/remoteproc/Makefile | 2 | ||||
-rw-r--r-- | drivers/remoteproc/rproc-elf-loader.c | 179 | ||||
-rw-r--r-- | drivers/remoteproc/sandbox_testproc.c | 4 | ||||
-rw-r--r-- | drivers/remoteproc/stm32_copro.c | 21 | ||||
-rw-r--r-- | drivers/remoteproc/ti_k3_dsp_rproc.c | 354 | ||||
-rw-r--r-- | drivers/remoteproc/ti_k3_r5f_rproc.c | 816 | ||||
-rw-r--r-- | drivers/remoteproc/ti_sci_proc.h | 27 |
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 */ |