diff options
131 files changed, 7858 insertions, 418 deletions
@@ -1,5 +1,5 @@ # -# Copyright (c) 2013-2019, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -209,7 +209,10 @@ PP = $(CC) -E $(TF_CFLAGS_$(ARCH)) else ifneq ($(findstring clang,$(notdir $(CC))),) TF_CFLAGS_aarch32 = $(target32-directive) $(march32-directive) TF_CFLAGS_aarch64 = -target aarch64-elf $(march64-directive) -LD = $(LINKER) +LD = ld.lld +ifeq (, $(shell which $(LD))) +$(error "No $(LD) in PATH, make sure it is installed or set LD to a different linker") +endif AS = $(CC) -c -x assembler-with-cpp $(TF_CFLAGS_$(ARCH)) CPP = $(CC) -E PP = $(CC) -E @@ -317,10 +320,13 @@ endif GCC_V_OUTPUT := $(shell $(CC) -v 2>&1) +# LD = armlink ifneq ($(findstring armlink,$(notdir $(LD))),) TF_LDFLAGS += --diag_error=warning --lto_level=O1 TF_LDFLAGS += --remove --info=unused,unusedsymbols TF_LDFLAGS += $(TF_LDFLAGS_$(ARCH)) + +# LD = gcc (used when GCC LTO is enabled) else ifneq ($(findstring gcc,$(notdir $(LD))),) # Pass ld options with Wl or Xlinker switches TF_LDFLAGS += -Wl,--fatal-warnings -O1 @@ -337,14 +343,20 @@ ifneq (${ERRATA_A53_843419},1) endif TF_LDFLAGS += -nostdlib TF_LDFLAGS += $(subst --,-Xlinker --,$(TF_LDFLAGS_$(ARCH))) + +# LD = gcc-ld (ld) or llvm-ld (ld.lld) or other else TF_LDFLAGS += --fatal-warnings -O1 TF_LDFLAGS += --gc-sections +# ld.lld doesn't recognize the errata flags, +# therefore don't add those in that case +ifeq ($(findstring ld.lld,$(notdir $(LD))),) TF_LDFLAGS += $(TF_LDFLAGS_$(ARCH)) endif +endif DTC_FLAGS += -I dts -O dtb -DTC_CPPFLAGS += -nostdinc -Iinclude -undef -x assembler-with-cpp +DTC_CPPFLAGS += -P -nostdinc -Iinclude -Ifdts -undef -x assembler-with-cpp ################################################################################ # Common sources and include directories diff --git a/common/fdt_fixup.c b/common/fdt_fixup.c index 99d0eee982..d518eb2a4f 100644 --- a/common/fdt_fixup.c +++ b/common/fdt_fixup.c @@ -95,7 +95,7 @@ int dt_add_psci_node(void *fdt) * or none have to be patched in the first place. * Returns 1 if *one* such subnode has been found and successfully changed * to "psci". - * Returns -1 on error. + * Returns negative values on error. * * Call in a loop until it returns 0. Recalculate the node offset after * it has returned 1. @@ -109,20 +109,23 @@ static int dt_update_one_cpu_node(void *fdt, int offset) offs = fdt_next_subnode(fdt, offs)) { const char *prop; int len; + int ret; prop = fdt_getprop(fdt, offs, "device_type", &len); - if (!prop) + if (prop == NULL) continue; - if (memcmp(prop, "cpu", 4) != 0 || len != 4) + if ((strcmp(prop, "cpu") != 0) || (len != 4)) continue; /* Ignore any nodes which already use "psci". */ prop = fdt_getprop(fdt, offs, "enable-method", &len); - if (prop && memcmp(prop, "psci", 5) == 0 && len == 5) + if ((prop != NULL) && + (strcmp(prop, "psci") == 0) && (len == 5)) continue; - if (fdt_setprop_string(fdt, offs, "enable-method", "psci")) - return -1; + ret = fdt_setprop_string(fdt, offs, "enable-method", "psci"); + if (ret < 0) + return ret; /* * Subnode found and patched. * Restart to accommodate potentially changed offsets. diff --git a/docs/change-log-upcoming.rst b/docs/change-log-upcoming.rst new file mode 100644 index 0000000000..3d7d5094d2 --- /dev/null +++ b/docs/change-log-upcoming.rst @@ -0,0 +1,145 @@ +Change Log for Upcoming Release +=============================== + +This document contains a summary of the new features, changes, fixes and known +issues to be included in the upcoming release of Trusted Firmware-A. The contents +of this file will be moved to the collective change-log.rst file at the time of +release code freeze. + + +Upcoming Release Version 2.3 +---------------------------- + +**Trusted Firmware-A Contributors, +Please log all relevant new features, changes, fixes, and known issues for the +upcoming release. For the CPU support, drivers, and tools sections please preface +the log description with the relevant key word, example: "<CPU>: <CPU Support +addition>". Use the RST format convention already used in the Change Log.** + +New Features +^^^^^^^^^^^^ + +- Arm Architecture + - Example: "Add support for Branch Target Identification (BTI)" + +- Build System + - Example: "Add support for default stack-protector flag" + +- CPU Support + - Example: "cortex-a55: Workaround for erratum 1221012" + +- Drivers + - Example: "console: Allow the console to register multiple times" + +- Libraries + - Example: "Introduce BTI support in Library at ROM (romlib)" + +- New Platforms Support + - Example: "qemu/qemu_sbsa: New platform support added for QEMU SBSA platform" + +- Platforms + - Example: "arm/common: Introduce wrapper functions to setup secure watchdog" + +- PSCI + - Example: "Adding new optional PSCI hook ``pwr_domain_on_finish_late``" + +- Security + - Example: "UBSAN support and handlers" + +- Tools + - Example: "fiptool: Add support to build fiptool on Windows." + + +Changed +^^^^^^^ + +- Arm Architecture + - Example: "Refactor ARMv8.3 Pointer Authentication support code" + +- BL-Specific + - Example: "BL2: Invalidate dcache build option for BL2 entry at EL3" + +- Boot Flow + - Example: "Add helper to parse BL31 parameters (both versions)" + +- Drivers + - Example: "gicv3: Prevent pending G1S interrupt from becoming G0 interrupt" + +- Platforms + - Example: "arm/common: Shorten the Firmware Update (FWU) process" + +- PSCI + - Example: "PSCI: Lookup list of parent nodes to lock only once" + +- Secure Partition Manager (SPM) + - Example: "Move shim layer to TTBR1_EL1" + +- Security + - Example: "Refactor SPSR initialisation code" + +- Tools + - Example: "cert_create: Remove RSA PKCS#1 v1.5 support" + + +Resolved Issues +^^^^^^^^^^^^^^^ + +- Arm Architecture + - Example: "Fix restoration of PAuth context" + +- BL-Specific + - Example: "Fix BL31 crash reporting on AArch64 only platforms" + +- Build System + - Example: "Remove several warnings reported with W=2 and W=1" + +- Code Quality + - Example: "Unify type of "cpu_idx" across PSCI module" + +- CPU Support + - Example: "cortex-a12: Fix MIDR mask" + +- Drivers + - Example: "scmi: Fix wrong payload length" + +- Library Code + - Example: "libc: Fix memchr implementation" + +- Platforms + - Example: "rpi: rpi3: Fix compilation error when stack protector is enabled" + +- Security + - Example: "AArch32: Disable Secure Cycle Counter" + +Deprecations +^^^^^^^^^^^^ + +- Common Code + - Example: "Remove MULTI_CONSOLE_API flag and references to it" + +- Drivers + - Example: "console: Remove deprecated finish_console_register" + +- Secure Partition Manager (SPM): + - Example: "Prototype SPCI-based SPM (services/std_svc/spm) will be replaced + with alternative methods of secure partitioning support." + +Known Issues +^^^^^^^^^^^^ + +- Build System + - dtb: DTB creation not supported when building on a Windows host. + + This step in the build process is skipped when running on a Windows host. A + known issue from the 1.6 release. + +- Platforms + - arm/juno: System suspend from Linux does not function as documented in the + user guide + + Following the instructions provided in the user guide document does not + result in the platform entering system suspend state as expected. A message + relating to the hdlcd driver failing to suspend will be emitted on the + Linux terminal. + + - mediatek/mt6795: This platform does not build in this release diff --git a/docs/components/secure-partition-manager-design.rst b/docs/components/secure-partition-manager-design.rst index 9aba954505..52b1c03e8f 100644 --- a/docs/components/secure-partition-manager-design.rst +++ b/docs/components/secure-partition-manager-design.rst @@ -119,7 +119,7 @@ Interface). This will be referred to as the *Standalone MM Secure Partition* in the rest of this document. To enable SPM support in TF-A, the source code must be compiled with the build -flag ``ENABLE_SPM=1``, along with ``EL3_EXCEPTION_HANDLING=1``. On Arm +flag ``SPM_MM=1``, along with ``EL3_EXCEPTION_HANDLING=1``. On Arm platforms the build option ``ARM_BL31_IN_DRAM`` must be set to 1. Also, the location of the binary that contains the BL32 image (``BL32=path/to/image.bin``) must be specified. @@ -133,7 +133,7 @@ image in the FIP: .. code:: shell BL32=path/to/standalone/mm/sp BL33=path/to/bl33.bin \ - make PLAT=fvp ENABLE_SPM=1 ARM_BL31_IN_DRAM=1 fip all + make PLAT=fvp SPM_MM=1 EL3_EXCEPTION_HANDLING=1 ARM_BL31_IN_DRAM=1 all fip Describing Secure Partition resources ------------------------------------- diff --git a/docs/getting_started/initial-build.rst b/docs/getting_started/initial-build.rst index 893aba2457..d4a8f0114e 100644 --- a/docs/getting_started/initial-build.rst +++ b/docs/getting_started/initial-build.rst @@ -18,9 +18,10 @@ Performing an Initial Build It is possible to build TF-A using Clang or Arm Compiler 6. To do so ``CC`` needs to point to the clang or armclang binary, which will - also select the clang or armclang assembler. Be aware that the - GNU linker is used by default. In case of being needed the linker - can be overridden using the ``LD`` variable. Clang linker version 6 is + also select the clang or armclang assembler. Be aware that for Arm Compiler, + the GNU linker is used by default. However for Clang LLVM linker (LLD) + is used by default. In case of being needed the linker can be overridden + using the ``LD`` variable. LLVM linker (LLD) version 9 is known to work with TF-A. In both cases ``CROSS_COMPILE`` should be set as described above. @@ -114,4 +115,4 @@ Performing an Initial Build -------------- -*Copyright (c) 2019, Arm Limited. All rights reserved.* +*Copyright (c) 2020, Arm Limited. All rights reserved.* diff --git a/docs/index.rst b/docs/index.rst index baadd42cf2..5088bfd878 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -15,6 +15,7 @@ Trusted Firmware-A Documentation perf/index security_advisories/index change-log + change-log-upcoming glossary license diff --git a/docs/plat/stm32mp1.rst b/docs/plat/stm32mp1.rst index 88251d6afb..2c372a6a38 100644 --- a/docs/plat/stm32mp1.rst +++ b/docs/plat/stm32mp1.rst @@ -76,21 +76,34 @@ ROM code -> BL2 (compiled with BL2_AT_EL3) -> OP-TEE -> BL33 (U-Boot) Build Instructions ------------------ +Boot media(s) supported by BL2 must be specified in the build command. +Available storage medias are: +- ``STM32MP_SDMMC`` +- ``STM32MP_EMMC`` +- ``STM32MP_RAW_NAND`` +- ``STM32MP_SPI_NAND`` +- ``STM32MP_SPI_NOR`` -To build with SP_min: +To build with SP_min and support for all bootable devices: .. code:: bash - make CROSS_COMPILE=arm-linux-gnueabihf- PLAT=stm32mp1 ARCH=aarch32 ARM_ARCH_MAJOR=7 AARCH32_SP=sp_min DTB_FILE_NAME=stm32mp157c-ev1.dtb + make CROSS_COMPILE=arm-linux-gnueabihf- PLAT=stm32mp1 ARCH=aarch32 ARM_ARCH_MAJOR=7 AARCH32_SP=sp_min STM32MP_SDMMC=1 STM32MP_EMMC=1 STM32MP_RAW_NAND=1 STM32MP_SPI_NAND=1 + STM32MP_SPI_NOR=1 DTB_FILE_NAME=stm32mp157c-ev1.dtb cd <u-boot_directory> make stm32mp15_trusted_defconfig make DEVICE_TREE=stm32mp157c-ev1 all -To build TF-A with with Op-TEE support: - +To build TF-A with OP-TEE support for all bootable devices: .. code:: bash - make CROSS_COMPILE=arm-linux-gnueabihf- PLAT=stm32mp1 ARCH=aarch32 ARM_ARCH_MAJOR=7 AARCH32_SP=optee + make CROSS_COMPILE=arm-linux-gnueabihf- PLAT=stm32mp1 ARCH=aarch32 ARM_ARCH_MAJOR=7 AARCH32_SP=optee STM32MP_SDMMC=1 STM32MP_EMMC=1 STM32MP_RAW_NAND=1 STM32MP_SPI_NAND=1 STM32MP_SPI_NOR=1 DTB_FILE_NAME=stm32mp157c-ev1.dtb + cd <optee_directory> + make CROSS_COMPILE=arm-linux-gnueabihf- ARCH=arm PLATFORM=stm32mp1 CFG_EMBED_DTB_SOURCE_FILE=stm32mp157c-ev1.dts + cd <u-boot_directory> + make stm32mp15_optee_defconfig + make DEVICE_TREE=stm32mp157c-ev1 all + The following build options are supported: diff --git a/docs/plat/xilinx-versal.rst b/docs/plat/xilinx-versal.rst index 231286e7e7..57a363bc95 100644 --- a/docs/plat/xilinx-versal.rst +++ b/docs/plat/xilinx-versal.rst @@ -14,7 +14,7 @@ To build: make RESET_TO_BL31=1 CROSS_COMPILE=aarch64-none-elf- PLAT=versal bl31 ``` -To build ATF for different platform (for now its just versal virtual "versal_virt") +To build ATF for different platform (supported are "silicon"(default) and "versal_virt") ```bash make RESET_TO_BL31=1 CROSS_COMPILE=aarch64-none-elf- PLAT=versal VERSAL_PLATFORM=versal_virt bl31 ``` @@ -33,3 +33,11 @@ Xilinx Versal platform specific build options * `VERSAL_PLATFORM`: Select the platform. Options: - `versal_virt` : Versal Virtual platform + +# PLM->TF-A Parameter Passing +------------------------------ +The PLM populates a data structure with image information for the TF-A. The TF-A +uses that data to hand off to the loaded images. The address of the handoff +data structure is passed in the ```PMC_GLOBAL_GLOB_GEN_STORAGE4``` register. +The register is free to be used by other software once the TF-A is bringing up +further firmware images. diff --git a/drivers/intel/soc/stratix10/io/s10_memmap_qspi.c b/drivers/intel/soc/stratix10/io/s10_memmap_qspi.c index a0fc034d88..dcd1991484 100644 --- a/drivers/intel/soc/stratix10/io/s10_memmap_qspi.c +++ b/drivers/intel/soc/stratix10/io/s10_memmap_qspi.c @@ -26,9 +26,9 @@ typedef struct { * valid. */ int in_use; - uintptr_t base; - size_t file_pos; - size_t size; + uintptr_t base; + unsigned long long file_pos; + unsigned long long size; } file_state_t; static file_state_t current_file = {0}; @@ -44,7 +44,7 @@ static int memmap_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info); static int memmap_block_open(io_dev_info_t *dev_info, const uintptr_t spec, io_entity_t *entity); static int memmap_block_seek(io_entity_t *entity, int mode, - ssize_t offset); + signed long long offset); static int memmap_block_len(io_entity_t *entity, size_t *length); static int memmap_block_read(io_entity_t *entity, uintptr_t buffer, size_t length, size_t *length_read); @@ -131,7 +131,8 @@ static int memmap_block_open(io_dev_info_t *dev_info, const uintptr_t spec, /* Seek to a particular file offset on the memmap device */ -static int memmap_block_seek(io_entity_t *entity, int mode, ssize_t offset) +static int memmap_block_seek(io_entity_t *entity, int mode, + signed long long offset) { int result = -ENOENT; file_state_t *fp; @@ -143,7 +144,8 @@ static int memmap_block_seek(io_entity_t *entity, int mode, ssize_t offset) fp = (file_state_t *) entity->info; /* Assert that new file position is valid */ - assert((offset >= 0) && (offset < fp->size)); + assert((offset >= 0) && + ((unsigned long long)offset < fp->size)); /* Reset file position */ fp->file_pos = offset; @@ -171,7 +173,7 @@ static int memmap_block_read(io_entity_t *entity, uintptr_t buffer, size_t length, size_t *length_read) { file_state_t *fp; - size_t pos_after; + unsigned long long pos_after; assert(entity != NULL); assert(length_read != NULL); @@ -198,7 +200,7 @@ static int memmap_block_write(io_entity_t *entity, const uintptr_t buffer, size_t length, size_t *length_written) { file_state_t *fp; - size_t pos_after; + unsigned long long pos_after; assert(entity != NULL); assert(length_written != NULL); diff --git a/drivers/io/io_block.c b/drivers/io/io_block.c index f190a4307b..5d45c2f176 100644 --- a/drivers/io/io_block.c +++ b/drivers/io/io_block.c @@ -19,17 +19,17 @@ typedef struct { io_block_dev_spec_t *dev_spec; uintptr_t base; - size_t file_pos; - size_t size; + unsigned long long file_pos; + unsigned long long size; } block_dev_state_t; -#define is_power_of_2(x) ((x != 0) && ((x & (x - 1)) == 0)) +#define is_power_of_2(x) (((x) != 0U) && (((x) & ((x) - 1U)) == 0U)) io_type_t device_type_block(void); static int block_open(io_dev_info_t *dev_info, const uintptr_t spec, io_entity_t *entity); -static int block_seek(io_entity_t *entity, int mode, ssize_t offset); +static int block_seek(io_entity_t *entity, int mode, signed long long offset); static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length, size_t *length_read); static int block_write(io_entity_t *entity, const uintptr_t buffer, @@ -148,21 +148,21 @@ static int block_open(io_dev_info_t *dev_info, const uintptr_t spec, } /* parameter offset is relative address at here */ -static int block_seek(io_entity_t *entity, int mode, ssize_t offset) +static int block_seek(io_entity_t *entity, int mode, signed long long offset) { block_dev_state_t *cur; assert(entity->info != (uintptr_t)NULL); cur = (block_dev_state_t *)entity->info; - assert((offset >= 0) && (offset < cur->size)); + assert((offset >= 0) && ((unsigned long long)offset < cur->size)); switch (mode) { case IO_SEEK_SET: - cur->file_pos = offset; + cur->file_pos = (unsigned long long)offset; break; case IO_SEEK_CUR: - cur->file_pos += offset; + cur->file_pos += (unsigned long long)offset; break; default: return -EINVAL; @@ -270,7 +270,7 @@ static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length, buf = &(cur->dev_spec->buffer); block_size = cur->dev_spec->block_size; assert((length <= cur->size) && - (length > 0) && + (length > 0U) && (ops->read != 0)); /* @@ -279,7 +279,7 @@ static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length, * on the low level driver. */ count = 0; - for (left = length; left > 0; left -= nbytes) { + for (left = length; left > 0U; left -= nbytes) { /* * We must only request operations aligned to the block * size. Therefore if file_pos is not block-aligned, @@ -288,7 +288,7 @@ static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length, * similarly, the number of bytes requested must be a * block size multiple */ - skip = cur->file_pos & (block_size - 1); + skip = cur->file_pos & (block_size - 1U); /* * Calculate the block number containing file_pos @@ -296,7 +296,7 @@ static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length, */ lba = (cur->file_pos + cur->base) / block_size; - if (skip + left > buf->length) { + if ((skip + left) > buf->length) { /* * The underlying read buffer is too small to * read all the required data - limit to just @@ -311,7 +311,8 @@ static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length, * block size. */ request = skip + left; - request = (request + (block_size - 1)) & ~(block_size - 1); + request = (request + (block_size - 1U)) & + ~(block_size - 1U); } request = ops->read(lba, buf->offset, request); @@ -330,7 +331,7 @@ static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length, * the read data when copying to the user buffer. */ nbytes = request - skip; - padding = (nbytes > left) ? nbytes - left : 0; + padding = (nbytes > left) ? nbytes - left : 0U; nbytes -= padding; memcpy((void *)(buffer + count), @@ -381,7 +382,7 @@ static int block_write(io_entity_t *entity, const uintptr_t buffer, buf = &(cur->dev_spec->buffer); block_size = cur->dev_spec->block_size; assert((length <= cur->size) && - (length > 0) && + (length > 0U) && (ops->read != 0) && (ops->write != 0)); @@ -391,7 +392,7 @@ static int block_write(io_entity_t *entity, const uintptr_t buffer, * on the low level driver. */ count = 0; - for (left = length; left > 0; left -= nbytes) { + for (left = length; left > 0U; left -= nbytes) { /* * We must only request operations aligned to the block * size. Therefore if file_pos is not block-aligned, @@ -400,7 +401,7 @@ static int block_write(io_entity_t *entity, const uintptr_t buffer, * similarly, the number of bytes requested must be a * block size multiple */ - skip = cur->file_pos & (block_size - 1); + skip = cur->file_pos & (block_size - 1U); /* * Calculate the block number containing file_pos @@ -408,7 +409,7 @@ static int block_write(io_entity_t *entity, const uintptr_t buffer, */ lba = (cur->file_pos + cur->base) / block_size; - if (skip + left > buf->length) { + if ((skip + left) > buf->length) { /* * The underlying read buffer is too small to * read all the required data - limit to just @@ -423,7 +424,8 @@ static int block_write(io_entity_t *entity, const uintptr_t buffer, * block size. */ request = skip + left; - request = (request + (block_size - 1)) & ~(block_size - 1); + request = (request + (block_size - 1U)) & + ~(block_size - 1U); } /* @@ -432,7 +434,7 @@ static int block_write(io_entity_t *entity, const uintptr_t buffer, * of the current request. */ nbytes = request - skip; - padding = (nbytes > left) ? nbytes - left : 0; + padding = (nbytes > left) ? nbytes - left : 0U; nbytes -= padding; /* @@ -440,14 +442,14 @@ static int block_write(io_entity_t *entity, const uintptr_t buffer, * some content and it means that we have to read before * writing */ - if (skip > 0 || padding > 0) { + if ((skip > 0U) || (padding > 0U)) { request = ops->read(lba, buf->offset, request); /* * The read may return size less than * requested. Round down to the nearest block * boundary */ - request &= ~(block_size-1); + request &= ~(block_size - 1U); if (request <= skip) { /* * We couldn't read enough bytes to jump over @@ -458,7 +460,7 @@ static int block_write(io_entity_t *entity, const uintptr_t buffer, return -EIO; } nbytes = request - skip; - padding = (nbytes > left) ? nbytes - left : 0; + padding = (nbytes > left) ? nbytes - left : 0U; nbytes -= padding; } @@ -477,7 +479,7 @@ static int block_write(io_entity_t *entity, const uintptr_t buffer, * buffer */ nbytes = request - skip; - padding = (nbytes > left) ? nbytes - left : 0; + padding = (nbytes > left) ? nbytes - left : 0U; nbytes -= padding; cur->file_pos += nbytes; @@ -505,7 +507,7 @@ static int block_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info) assert(dev_info != NULL); result = allocate_dev_info(&info); - if (result) + if (result != 0) return -ENOENT; cur = (block_dev_state_t *)info->info; @@ -513,10 +515,10 @@ static int block_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info) cur->dev_spec = (io_block_dev_spec_t *)dev_spec; buffer = &(cur->dev_spec->buffer); block_size = cur->dev_spec->block_size; - assert((block_size > 0) && - (is_power_of_2(block_size) != 0) && - ((buffer->offset % block_size) == 0) && - ((buffer->length % block_size) == 0)); + assert((block_size > 0U) && + (is_power_of_2(block_size) != 0U) && + ((buffer->offset % block_size) == 0U) && + ((buffer->length % block_size) == 0U)); *dev_info = info; /* cast away const */ (void)block_size; diff --git a/drivers/io/io_fip.c b/drivers/io/io_fip.c index 544b37dbea..5d49fffaa3 100644 --- a/drivers/io/io_fip.c +++ b/drivers/io/io_fip.c @@ -304,7 +304,8 @@ static int fip_file_open(io_dev_info_t *dev_info, const uintptr_t spec, } /* Seek past the FIP header into the Table of Contents */ - result = io_seek(backend_handle, IO_SEEK_SET, sizeof(fip_toc_header_t)); + result = io_seek(backend_handle, IO_SEEK_SET, + (signed long long)sizeof(fip_toc_header_t)); if (result != 0) { WARN("fip_file_open: failed to seek\n"); result = -ENOENT; @@ -389,7 +390,8 @@ static int fip_file_read(io_entity_t *entity, uintptr_t buffer, size_t length, /* Seek to the position in the FIP where the payload lives */ file_offset = fp->entry.offset_address + fp->file_pos; - result = io_seek(backend_handle, IO_SEEK_SET, file_offset); + result = io_seek(backend_handle, IO_SEEK_SET, + (signed long long)file_offset); if (result != 0) { WARN("fip_file_read: failed to seek\n"); result = -ENOENT; diff --git a/drivers/io/io_memmap.c b/drivers/io/io_memmap.c index 96590b6c03..eed50cc086 100644 --- a/drivers/io/io_memmap.c +++ b/drivers/io/io_memmap.c @@ -23,10 +23,10 @@ typedef struct { /* Use the 'in_use' flag as any value for base and file_pos could be * valid. */ - int in_use; - uintptr_t base; - size_t file_pos; - size_t size; + int in_use; + uintptr_t base; + unsigned long long file_pos; + unsigned long long size; } file_state_t; static file_state_t current_file = {0}; @@ -42,7 +42,7 @@ static int memmap_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info); static int memmap_block_open(io_dev_info_t *dev_info, const uintptr_t spec, io_entity_t *entity); static int memmap_block_seek(io_entity_t *entity, int mode, - ssize_t offset); + signed long long offset); static int memmap_block_len(io_entity_t *entity, size_t *length); static int memmap_block_read(io_entity_t *entity, uintptr_t buffer, size_t length, size_t *length_read); @@ -129,7 +129,8 @@ static int memmap_block_open(io_dev_info_t *dev_info, const uintptr_t spec, /* Seek to a particular file offset on the memmap device */ -static int memmap_block_seek(io_entity_t *entity, int mode, ssize_t offset) +static int memmap_block_seek(io_entity_t *entity, int mode, + signed long long offset) { int result = -ENOENT; file_state_t *fp; @@ -141,10 +142,11 @@ static int memmap_block_seek(io_entity_t *entity, int mode, ssize_t offset) fp = (file_state_t *) entity->info; /* Assert that new file position is valid */ - assert((offset >= 0) && (offset < fp->size)); + assert((offset >= 0) && + ((unsigned long long)offset < fp->size)); /* Reset file position */ - fp->file_pos = offset; + fp->file_pos = (unsigned long long)offset; result = 0; } @@ -158,7 +160,7 @@ static int memmap_block_len(io_entity_t *entity, size_t *length) assert(entity != NULL); assert(length != NULL); - *length = ((file_state_t *)entity->info)->size; + *length = (size_t)((file_state_t *)entity->info)->size; return 0; } @@ -169,7 +171,7 @@ static int memmap_block_read(io_entity_t *entity, uintptr_t buffer, size_t length, size_t *length_read) { file_state_t *fp; - size_t pos_after; + unsigned long long pos_after; assert(entity != NULL); assert(length_read != NULL); @@ -180,7 +182,8 @@ static int memmap_block_read(io_entity_t *entity, uintptr_t buffer, pos_after = fp->file_pos + length; assert((pos_after >= fp->file_pos) && (pos_after <= fp->size)); - memcpy((void *)buffer, (void *)(fp->base + fp->file_pos), length); + memcpy((void *)buffer, + (void *)((uintptr_t)(fp->base + fp->file_pos)), length); *length_read = length; @@ -196,7 +199,7 @@ static int memmap_block_write(io_entity_t *entity, const uintptr_t buffer, size_t length, size_t *length_written) { file_state_t *fp; - size_t pos_after; + unsigned long long pos_after; assert(entity != NULL); assert(length_written != NULL); @@ -207,7 +210,8 @@ static int memmap_block_write(io_entity_t *entity, const uintptr_t buffer, pos_after = fp->file_pos + length; assert((pos_after >= fp->file_pos) && (pos_after <= fp->size)); - memcpy((void *)(fp->base + fp->file_pos), (void *)buffer, length); + memcpy((void *)((uintptr_t)(fp->base + fp->file_pos)), + (void *)buffer, length); *length_written = length; diff --git a/drivers/io/io_mtd.c b/drivers/io/io_mtd.c new file mode 100644 index 0000000000..7575fa2503 --- /dev/null +++ b/drivers/io/io_mtd.c @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> +#include <string.h> + +#include <platform_def.h> + +#include <common/debug.h> +#include <drivers/io/io_driver.h> +#include <drivers/io/io_mtd.h> +#include <lib/utils.h> + +typedef struct { + io_mtd_dev_spec_t *dev_spec; + uintptr_t base; + unsigned long long offset; /* Offset in bytes */ + unsigned long long size; /* Size of device in bytes */ +} mtd_dev_state_t; + +io_type_t device_type_mtd(void); + +static int mtd_open(io_dev_info_t *dev_info, const uintptr_t spec, + io_entity_t *entity); +static int mtd_seek(io_entity_t *entity, int mode, signed long long offset); +static int mtd_read(io_entity_t *entity, uintptr_t buffer, size_t length, + size_t *length_read); +static int mtd_close(io_entity_t *entity); +static int mtd_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info); +static int mtd_dev_close(io_dev_info_t *dev_info); + +static const io_dev_connector_t mtd_dev_connector = { + .dev_open = mtd_dev_open +}; + +static const io_dev_funcs_t mtd_dev_funcs = { + .type = device_type_mtd, + .open = mtd_open, + .seek = mtd_seek, + .read = mtd_read, + .close = mtd_close, + .dev_close = mtd_dev_close, +}; + +static mtd_dev_state_t state_pool[MAX_IO_MTD_DEVICES]; +static io_dev_info_t dev_info_pool[MAX_IO_MTD_DEVICES]; + +io_type_t device_type_mtd(void) +{ + return IO_TYPE_MTD; +} + +/* Locate a MTD state in the pool, specified by address */ +static int find_first_mtd_state(const io_mtd_dev_spec_t *dev_spec, + unsigned int *index_out) +{ + unsigned int index; + int result = -ENOENT; + + for (index = 0U; index < MAX_IO_MTD_DEVICES; index++) { + /* dev_spec is used as identifier since it's unique */ + if (state_pool[index].dev_spec == dev_spec) { + result = 0; + *index_out = index; + break; + } + } + + return result; +} + +/* Allocate a device info from the pool */ +static int allocate_dev_info(io_dev_info_t **dev_info) +{ + unsigned int index = 0U; + int result; + + result = find_first_mtd_state(NULL, &index); + if (result != 0) { + return -ENOMEM; + } + + dev_info_pool[index].funcs = &mtd_dev_funcs; + dev_info_pool[index].info = (uintptr_t)&state_pool[index]; + *dev_info = &dev_info_pool[index]; + + return 0; +} + +/* Release a device info from the pool */ +static int free_dev_info(io_dev_info_t *dev_info) +{ + int result; + unsigned int index = 0U; + mtd_dev_state_t *state; + + state = (mtd_dev_state_t *)dev_info->info; + result = find_first_mtd_state(state->dev_spec, &index); + if (result != 0) { + return result; + } + + zeromem(state, sizeof(mtd_dev_state_t)); + zeromem(dev_info, sizeof(io_dev_info_t)); + + return 0; +} + +static int mtd_open(io_dev_info_t *dev_info, const uintptr_t spec, + io_entity_t *entity) +{ + mtd_dev_state_t *cur; + + assert((dev_info->info != 0UL) && (entity->info == 0UL)); + + cur = (mtd_dev_state_t *)dev_info->info; + entity->info = (uintptr_t)cur; + cur->offset = 0U; + + return 0; +} + +/* Seek to a specific position using offset */ +static int mtd_seek(io_entity_t *entity, int mode, signed long long offset) +{ + mtd_dev_state_t *cur; + + assert((entity->info != (uintptr_t)NULL) && (offset >= 0)); + + cur = (mtd_dev_state_t *)entity->info; + + switch (mode) { + case IO_SEEK_SET: + if ((offset >= 0) && + ((unsigned long long)offset >= cur->size)) { + return -EINVAL; + } + + cur->offset = offset; + break; + case IO_SEEK_CUR: + if (((cur->offset + (unsigned long long)offset) >= + cur->size) || + ((cur->offset + (unsigned long long)offset) < + cur->offset)) { + return -EINVAL; + } + + cur->offset += (unsigned long long)offset; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int mtd_read(io_entity_t *entity, uintptr_t buffer, size_t length, + size_t *out_length) +{ + mtd_dev_state_t *cur; + io_mtd_ops_t *ops; + int ret; + + assert(entity->info != (uintptr_t)NULL); + assert((length > 0U) && (buffer != (uintptr_t)NULL)); + + cur = (mtd_dev_state_t *)entity->info; + ops = &cur->dev_spec->ops; + assert(ops->read != NULL); + + VERBOSE("Read at %llx into %lx, length %zi\n", + cur->offset, buffer, length); + if ((cur->offset + length) > cur->dev_spec->device_size) { + return -EINVAL; + } + + ret = ops->read(cur->offset, buffer, length, out_length); + if (ret < 0) { + return ret; + } + + assert(*out_length == length); + cur->offset += *out_length; + + return 0; +} + +static int mtd_close(io_entity_t *entity) +{ + entity->info = (uintptr_t)NULL; + + return 0; +} + +static int mtd_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info) +{ + mtd_dev_state_t *cur; + io_dev_info_t *info; + io_mtd_ops_t *ops; + int result; + + result = allocate_dev_info(&info); + if (result != 0) { + return -ENOENT; + } + + cur = (mtd_dev_state_t *)info->info; + cur->dev_spec = (io_mtd_dev_spec_t *)dev_spec; + *dev_info = info; + ops = &(cur->dev_spec->ops); + if (ops->init != NULL) { + result = ops->init(&cur->dev_spec->device_size, + &cur->dev_spec->erase_size); + } + + if (result == 0) { + cur->size = cur->dev_spec->device_size; + } else { + cur->size = 0ULL; + } + + return result; +} + +static int mtd_dev_close(io_dev_info_t *dev_info) +{ + return free_dev_info(dev_info); +} + +/* Exported functions */ + +/* Register the MTD driver in the IO abstraction */ +int register_io_dev_mtd(const io_dev_connector_t **dev_con) +{ + int result; + + result = io_register_device(&dev_info_pool[0]); + if (result == 0) { + *dev_con = &mtd_dev_connector; + } + + return result; +} diff --git a/drivers/io/io_semihosting.c b/drivers/io/io_semihosting.c index 23d09c1188..4ceddc6cc0 100644 --- a/drivers/io/io_semihosting.c +++ b/drivers/io/io_semihosting.c @@ -25,7 +25,7 @@ static io_type_t device_type_sh(void) static int sh_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info); static int sh_file_open(io_dev_info_t *dev_info, const uintptr_t spec, io_entity_t *entity); -static int sh_file_seek(io_entity_t *entity, int mode, ssize_t offset); +static int sh_file_seek(io_entity_t *entity, int mode, signed long long offset); static int sh_file_len(io_entity_t *entity, size_t *length); static int sh_file_read(io_entity_t *entity, uintptr_t buffer, size_t length, size_t *length_read); @@ -90,7 +90,7 @@ static int sh_file_open(io_dev_info_t *dev_info __unused, /* Seek to a particular file offset on the semi-hosting device */ -static int sh_file_seek(io_entity_t *entity, int mode, ssize_t offset) +static int sh_file_seek(io_entity_t *entity, int mode, signed long long offset) { long file_handle, sh_result; @@ -98,7 +98,7 @@ static int sh_file_seek(io_entity_t *entity, int mode, ssize_t offset) file_handle = (long)entity->info; - sh_result = semihosting_file_seek(file_handle, offset); + sh_result = semihosting_file_seek(file_handle, (ssize_t)offset); return (sh_result == 0) ? 0 : -ENOENT; } diff --git a/drivers/io/io_storage.c b/drivers/io/io_storage.c index e444f87f72..b8c1d6479f 100644 --- a/drivers/io/io_storage.c +++ b/drivers/io/io_storage.c @@ -237,7 +237,7 @@ int io_open(uintptr_t dev_handle, const uintptr_t spec, uintptr_t *handle) /* Seek to a specific position in an IO entity */ -int io_seek(uintptr_t handle, io_seek_mode_t mode, ssize_t offset) +int io_seek(uintptr_t handle, io_seek_mode_t mode, signed long long offset) { int result = -ENODEV; assert(is_valid_entity(handle) && is_valid_seek_mode(mode)); diff --git a/drivers/mtd/nand/core.c b/drivers/mtd/nand/core.c new file mode 100644 index 0000000000..44b001e35b --- /dev/null +++ b/drivers/mtd/nand/core.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> +#include <stddef.h> + +#include <platform_def.h> + +#include <common/debug.h> +#include <drivers/delay_timer.h> +#include <drivers/nand.h> +#include <lib/utils.h> + +/* + * Define a single nand_device used by specific NAND frameworks. + */ +static struct nand_device nand_dev; +static uint8_t scratch_buff[PLATFORM_MTD_MAX_PAGE_SIZE]; + +int nand_read(unsigned int offset, uintptr_t buffer, size_t length, + size_t *length_read) +{ + unsigned int block = offset / nand_dev.block_size; + unsigned int end_block = (offset + length - 1U) / nand_dev.block_size; + unsigned int page_start = + (offset % nand_dev.block_size) / nand_dev.page_size; + unsigned int nb_pages = nand_dev.block_size / nand_dev.page_size; + unsigned int start_offset = offset % nand_dev.page_size; + unsigned int page; + unsigned int bytes_read; + int is_bad; + int ret; + + VERBOSE("Block %u - %u, page_start %u, nb %u, length %zu, offset %u\n", + block, end_block, page_start, nb_pages, length, offset); + + *length_read = 0UL; + + if (((start_offset != 0U) || (length % nand_dev.page_size) != 0U) && + (sizeof(scratch_buff) < nand_dev.page_size)) { + return -EINVAL; + } + + while (block <= end_block) { + is_bad = nand_dev.mtd_block_is_bad(block); + if (is_bad < 0) { + return is_bad; + } + + if (is_bad == 1) { + /* Skip the block */ + uint32_t max_block = + nand_dev.size / nand_dev.block_size; + + block++; + end_block++; + if ((block < max_block) && (end_block < max_block)) { + continue; + } + + return -EIO; + } + + for (page = page_start; page < nb_pages; page++) { + if ((start_offset != 0U) || + (length < nand_dev.page_size)) { + ret = nand_dev.mtd_read_page( + &nand_dev, + (block * nb_pages) + page, + (uintptr_t)scratch_buff); + if (ret != 0) { + return ret; + } + + bytes_read = MIN((size_t)(nand_dev.page_size - + start_offset), + length); + + memcpy((uint8_t *)buffer, + scratch_buff + start_offset, + bytes_read); + + start_offset = 0U; + } else { + ret = nand_dev.mtd_read_page(&nand_dev, + (block * nb_pages) + page, + buffer); + if (ret != 0) { + return ret; + } + + bytes_read = nand_dev.page_size; + } + + length -= bytes_read; + buffer += bytes_read; + *length_read += bytes_read; + + if (length == 0U) { + break; + } + } + + page_start = 0U; + block++; + } + + return 0; +} + +struct nand_device *get_nand_device(void) +{ + return &nand_dev; +} diff --git a/drivers/mtd/nand/raw_nand.c b/drivers/mtd/nand/raw_nand.c new file mode 100644 index 0000000000..48131fcb25 --- /dev/null +++ b/drivers/mtd/nand/raw_nand.c @@ -0,0 +1,446 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> +#include <stddef.h> + +#include <platform_def.h> + +#include <common/debug.h> +#include <drivers/delay_timer.h> +#include <drivers/raw_nand.h> +#include <lib/utils.h> + +#define ONFI_SIGNATURE_ADDR 0x20U + +/* CRC calculation */ +#define CRC_POLYNOM 0x8005U +#define CRC_INIT_VALUE 0x4F4EU + +/* Status register */ +#define NAND_STATUS_READY BIT(6) + +#define SZ_128M 0x08000000U +#define SZ_512 0x200U + +static struct rawnand_device rawnand_dev; + +#pragma weak plat_get_raw_nand_data +int plat_get_raw_nand_data(struct rawnand_device *device) +{ + return 0; +} + +static int nand_send_cmd(uint8_t cmd, unsigned int tim) +{ + struct nand_req req; + + zeromem(&req, sizeof(struct nand_req)); + req.nand = rawnand_dev.nand_dev; + req.type = NAND_REQ_CMD | cmd; + req.inst_delay = tim; + + return rawnand_dev.ops->exec(&req); +} + +static int nand_send_addr(uint8_t addr, unsigned int tim) +{ + struct nand_req req; + + zeromem(&req, sizeof(struct nand_req)); + req.nand = rawnand_dev.nand_dev; + req.type = NAND_REQ_ADDR; + req.addr = &addr; + req.inst_delay = tim; + + return rawnand_dev.ops->exec(&req); +} + +static int nand_send_wait(unsigned int delay, unsigned int tim) +{ + struct nand_req req; + + zeromem(&req, sizeof(struct nand_req)); + req.nand = rawnand_dev.nand_dev; + req.type = NAND_REQ_WAIT; + req.inst_delay = tim; + req.delay_ms = delay; + + return rawnand_dev.ops->exec(&req); +} + + +static int nand_read_data(uint8_t *data, unsigned int length, bool use_8bit) +{ + struct nand_req req; + + zeromem(&req, sizeof(struct nand_req)); + req.nand = rawnand_dev.nand_dev; + req.type = NAND_REQ_DATAIN | (use_8bit ? NAND_REQ_BUS_WIDTH_8 : 0U); + req.addr = data; + req.length = length; + + return rawnand_dev.ops->exec(&req); +} + +int nand_change_read_column_cmd(unsigned int offset, uintptr_t buffer, + unsigned int len) +{ + int ret; + uint8_t addr[2]; + unsigned int i; + + ret = nand_send_cmd(NAND_CMD_CHANGE_1ST, 0U); + if (ret != 0) { + return ret; + } + + if (rawnand_dev.nand_dev->buswidth == NAND_BUS_WIDTH_16) { + offset /= 2U; + } + + addr[0] = offset; + addr[1] = offset >> 8; + + for (i = 0; i < 2U; i++) { + ret = nand_send_addr(addr[i], 0U); + if (ret != 0) { + return ret; + } + } + + ret = nand_send_cmd(NAND_CMD_CHANGE_2ND, NAND_TCCS_MIN); + if (ret != 0) { + return ret; + } + + return nand_read_data((uint8_t *)buffer, len, false); +} + +int nand_read_page_cmd(unsigned int page, unsigned int offset, + uintptr_t buffer, unsigned int len) +{ + uint8_t addr[5]; + uint8_t i = 0U; + uint8_t j; + int ret; + + VERBOSE(">%s page %u offset %u buffer 0x%lx\n", __func__, page, offset, + buffer); + + if (rawnand_dev.nand_dev->buswidth == NAND_BUS_WIDTH_16) { + offset /= 2U; + } + + addr[i++] = offset; + addr[i++] = offset >> 8; + + addr[i++] = page; + addr[i++] = page >> 8; + if (rawnand_dev.nand_dev->size > SZ_128M) { + addr[i++] = page >> 16; + } + + ret = nand_send_cmd(NAND_CMD_READ_1ST, 0U); + if (ret != 0) { + return ret; + } + + for (j = 0U; j < i; j++) { + ret = nand_send_addr(addr[j], 0U); + if (ret != 0) { + return ret; + } + } + + ret = nand_send_cmd(NAND_CMD_READ_2ND, NAND_TWB_MAX); + if (ret != 0) { + return ret; + } + + ret = nand_send_wait(PSEC_TO_MSEC(NAND_TR_MAX), NAND_TRR_MIN); + if (ret != 0) { + return ret; + } + + if (buffer != 0U) { + ret = nand_read_data((uint8_t *)buffer, len, false); + } + + return ret; +} + +static int nand_status(uint8_t *status) +{ + int ret; + + ret = nand_send_cmd(NAND_CMD_STATUS, NAND_TWHR_MIN); + if (ret != 0) { + return ret; + } + + if (status != NULL) { + ret = nand_read_data(status, 1U, true); + } + + return ret; +} + +int nand_wait_ready(unsigned long delay) +{ + uint8_t status; + int ret; + uint64_t timeout; + + /* Wait before reading status */ + udelay(1); + + ret = nand_status(NULL); + if (ret != 0) { + return ret; + } + + timeout = timeout_init_us(delay); + while (!timeout_elapsed(timeout)) { + ret = nand_read_data(&status, 1U, true); + if (ret != 0) { + return ret; + } + + if ((status & NAND_STATUS_READY) != 0U) { + return nand_send_cmd(NAND_CMD_READ_1ST, 0U); + } + + udelay(10); + } + + return -ETIMEDOUT; +} + +#if NAND_ONFI_DETECT +static uint16_t nand_check_crc(uint16_t crc, uint8_t *data_in, + unsigned int data_len) +{ + uint32_t i; + uint32_t j; + uint32_t bit; + + for (i = 0U; i < data_len; i++) { + uint8_t cur_param = *data_in++; + + for (j = BIT(7); j != 0U; j >>= 1) { + bit = crc & BIT(15); + crc <<= 1; + + if ((cur_param & j) != 0U) { + bit ^= BIT(15); + } + + if (bit != 0U) { + crc ^= CRC_POLYNOM; + } + } + + crc &= GENMASK(15, 0); + } + + return crc; +} + +static int nand_read_id(uint8_t addr, uint8_t *id, unsigned int size) +{ + int ret; + + ret = nand_send_cmd(NAND_CMD_READID, 0U); + if (ret != 0) { + return ret; + } + + ret = nand_send_addr(addr, NAND_TWHR_MIN); + if (ret != 0) { + return ret; + } + + return nand_read_data(id, size, true); +} + +static int nand_reset(void) +{ + int ret; + + ret = nand_send_cmd(NAND_CMD_RESET, NAND_TWB_MAX); + if (ret != 0) { + return ret; + } + + return nand_send_wait(PSEC_TO_MSEC(NAND_TRST_MAX), 0U); +} + +static int nand_read_param_page(void) +{ + struct nand_param_page page; + uint8_t addr = 0U; + int ret; + + ret = nand_send_cmd(NAND_CMD_READ_PARAM_PAGE, 0U); + if (ret != 0) { + return ret; + } + + ret = nand_send_addr(addr, NAND_TWB_MAX); + if (ret != 0) { + return ret; + } + + ret = nand_send_wait(PSEC_TO_MSEC(NAND_TR_MAX), NAND_TRR_MIN); + if (ret != 0) { + return ret; + } + + ret = nand_read_data((uint8_t *)&page, sizeof(page), true); + if (ret != 0) { + return ret; + } + + if (strncmp((char *)&page.page_sig, "ONFI", 4) != 0) { + WARN("Error ONFI detection\n"); + return -EINVAL; + } + + if (nand_check_crc(CRC_INIT_VALUE, (uint8_t *)&page, 254U) != + page.crc16) { + WARN("Error reading param\n"); + return -EINVAL; + } + + if ((page.features & ONFI_FEAT_BUS_WIDTH_16) != 0U) { + rawnand_dev.nand_dev->buswidth = NAND_BUS_WIDTH_16; + } else { + rawnand_dev.nand_dev->buswidth = NAND_BUS_WIDTH_8; + } + + rawnand_dev.nand_dev->block_size = page.num_pages_per_blk * + page.bytes_per_page; + rawnand_dev.nand_dev->page_size = page.bytes_per_page; + rawnand_dev.nand_dev->size = page.num_pages_per_blk * + page.bytes_per_page * + page.num_blk_in_lun * page.num_lun; + + if (page.nb_ecc_bits != GENMASK_32(7, 0)) { + rawnand_dev.nand_dev->ecc.max_bit_corr = page.nb_ecc_bits; + rawnand_dev.nand_dev->ecc.size = SZ_512; + } + + VERBOSE("Page size %u, block_size %u, Size %llu, ecc %u, buswidth %u\n", + rawnand_dev.nand_dev->page_size, + rawnand_dev.nand_dev->block_size, rawnand_dev.nand_dev->size, + rawnand_dev.nand_dev->ecc.max_bit_corr, + rawnand_dev.nand_dev->buswidth); + + return 0; +} + +static int detect_onfi(void) +{ + int ret; + char id[4]; + + ret = nand_reset(); + if (ret != 0) { + return ret; + } + + ret = nand_read_id(ONFI_SIGNATURE_ADDR, (uint8_t *)id, sizeof(id)); + if (ret != 0) { + return ret; + } + + if (strncmp(id, "ONFI", sizeof(id)) != 0) { + WARN("NAND Non ONFI detected\n"); + return -ENODEV; + } + + return nand_read_param_page(); +} +#endif + +static int nand_mtd_block_is_bad(unsigned int block) +{ + unsigned int nbpages_per_block = rawnand_dev.nand_dev->block_size / + rawnand_dev.nand_dev->page_size; + uint8_t bbm_marker[2]; + uint8_t page; + int ret; + + for (page = 0U; page < 2U; page++) { + ret = nand_read_page_cmd(block * nbpages_per_block, + rawnand_dev.nand_dev->page_size, + (uintptr_t)bbm_marker, + sizeof(bbm_marker)); + if (ret != 0) { + return ret; + } + + if ((bbm_marker[0] != GENMASK_32(7, 0)) || + (bbm_marker[1] != GENMASK_32(7, 0))) { + WARN("Block %u is bad\n", block); + return 1; + } + } + + return 0; +} + +static int nand_mtd_read_page_raw(struct nand_device *nand, unsigned int page, + uintptr_t buffer) +{ + return nand_read_page_cmd(page, 0U, buffer, + rawnand_dev.nand_dev->page_size); +} + +void nand_raw_ctrl_init(const struct nand_ctrl_ops *ops) +{ + rawnand_dev.ops = ops; +} + +int nand_raw_init(unsigned long long *size, unsigned int *erase_size) +{ + rawnand_dev.nand_dev = get_nand_device(); + if (rawnand_dev.nand_dev == NULL) { + return -EINVAL; + } + + rawnand_dev.nand_dev->mtd_block_is_bad = nand_mtd_block_is_bad; + rawnand_dev.nand_dev->mtd_read_page = nand_mtd_read_page_raw; + rawnand_dev.nand_dev->ecc.mode = NAND_ECC_NONE; + + if ((rawnand_dev.ops->setup == NULL) || + (rawnand_dev.ops->exec == NULL)) { + return -ENODEV; + } + +#if NAND_ONFI_DETECT + if (detect_onfi() != 0) { + WARN("Detect ONFI failed\n"); + } +#endif + + if (plat_get_raw_nand_data(&rawnand_dev) != 0) { + return -EINVAL; + } + + assert((rawnand_dev.nand_dev->page_size != 0U) && + (rawnand_dev.nand_dev->block_size != 0U) && + (rawnand_dev.nand_dev->size != 0U)); + + *size = rawnand_dev.nand_dev->size; + *erase_size = rawnand_dev.nand_dev->block_size; + + rawnand_dev.ops->setup(rawnand_dev.nand_dev); + + return 0; +} diff --git a/drivers/mtd/nand/spi_nand.c b/drivers/mtd/nand/spi_nand.c new file mode 100644 index 0000000000..d01a11963f --- /dev/null +++ b/drivers/mtd/nand/spi_nand.c @@ -0,0 +1,320 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> +#include <stddef.h> + +#include <platform_def.h> + +#include <common/debug.h> +#include <drivers/delay_timer.h> +#include <drivers/spi_nand.h> +#include <lib/utils.h> + +#define SPI_NAND_MAX_ID_LEN 4U +#define DELAY_US_400MS 400000U +#define MACRONIX_ID 0xC2U + +static struct spinand_device spinand_dev; + +#pragma weak plat_get_spi_nand_data +int plat_get_spi_nand_data(struct spinand_device *device) +{ + return 0; +} + +static int spi_nand_reg(bool read_reg, uint8_t reg, uint8_t *val, + enum spi_mem_data_dir dir) +{ + struct spi_mem_op op; + + zeromem(&op, sizeof(struct spi_mem_op)); + if (read_reg) { + op.cmd.opcode = SPI_NAND_OP_GET_FEATURE; + } else { + op.cmd.opcode = SPI_NAND_OP_SET_FEATURE; + } + + op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + op.addr.val = reg; + op.addr.nbytes = 1U; + op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + op.data.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + op.data.dir = dir; + op.data.nbytes = 1U; + op.data.buf = val; + + return spi_mem_exec_op(&op); +} + +static int spi_nand_read_reg(uint8_t reg, uint8_t *val) +{ + return spi_nand_reg(true, reg, val, SPI_MEM_DATA_IN); +} + +static int spi_nand_write_reg(uint8_t reg, uint8_t val) +{ + return spi_nand_reg(false, reg, &val, SPI_MEM_DATA_OUT); +} + +static int spi_nand_update_cfg(uint8_t mask, uint8_t val) +{ + int ret; + uint8_t cfg = spinand_dev.cfg_cache; + + cfg &= ~mask; + cfg |= val; + + if (cfg == spinand_dev.cfg_cache) { + return 0; + } + + ret = spi_nand_write_reg(SPI_NAND_REG_CFG, cfg); + if (ret == 0) { + spinand_dev.cfg_cache = cfg; + } + + return ret; +} + +static int spi_nand_ecc_enable(bool enable) +{ + return spi_nand_update_cfg(SPI_NAND_CFG_ECC_EN, + enable ? SPI_NAND_CFG_ECC_EN : 0U); +} + +static int spi_nand_quad_enable(uint8_t manufacturer_id) +{ + bool enable = false; + + if (manufacturer_id != MACRONIX_ID) { + return 0; + } + + if (spinand_dev.spi_read_cache_op.data.buswidth == + SPI_MEM_BUSWIDTH_4_LINE) { + enable = true; + } + + return spi_nand_update_cfg(SPI_NAND_CFG_QE, + enable ? SPI_NAND_CFG_QE : 0U); +} + +static int spi_nand_wait_ready(uint8_t *status) +{ + int ret; + uint64_t timeout = timeout_init_us(DELAY_US_400MS); + + while (!timeout_elapsed(timeout)) { + ret = spi_nand_read_reg(SPI_NAND_REG_STATUS, status); + if (ret != 0) { + return ret; + } + + VERBOSE("%s Status %x\n", __func__, *status); + if ((*status & SPI_NAND_STATUS_BUSY) == 0U) { + return 0; + } + } + + return -ETIMEDOUT; +} + +static int spi_nand_reset(void) +{ + struct spi_mem_op op; + uint8_t status; + int ret; + + zeromem(&op, sizeof(struct spi_mem_op)); + op.cmd.opcode = SPI_NAND_OP_RESET; + op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + + ret = spi_mem_exec_op(&op); + if (ret != 0) { + return ret; + } + + return spi_nand_wait_ready(&status); +} + +static int spi_nand_read_id(uint8_t *id) +{ + struct spi_mem_op op; + + zeromem(&op, sizeof(struct spi_mem_op)); + op.cmd.opcode = SPI_NAND_OP_READ_ID; + op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + op.data.dir = SPI_MEM_DATA_IN; + op.data.nbytes = SPI_NAND_MAX_ID_LEN; + op.data.buf = id; + op.data.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + + return spi_mem_exec_op(&op); +} + +static int spi_nand_load_page(unsigned int page) +{ + struct spi_mem_op op; + uint32_t block_nb = page / spinand_dev.nand_dev->block_size; + uint32_t page_nb = page - (block_nb * spinand_dev.nand_dev->page_size); + uint32_t nbpages_per_block = spinand_dev.nand_dev->block_size / + spinand_dev.nand_dev->page_size; + uint32_t block_sh = __builtin_ctz(nbpages_per_block) + 1U; + + zeromem(&op, sizeof(struct spi_mem_op)); + op.cmd.opcode = SPI_NAND_OP_LOAD_PAGE; + op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + op.addr.val = (block_nb << block_sh) | page_nb; + op.addr.nbytes = 3U; + op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + + return spi_mem_exec_op(&op); +} + +static int spi_nand_read_from_cache(unsigned int page, unsigned int offset, + uint8_t *buffer, unsigned int len) +{ + uint32_t nbpages_per_block = spinand_dev.nand_dev->block_size / + spinand_dev.nand_dev->page_size; + uint32_t block_nb = page / nbpages_per_block; + uint32_t page_sh = __builtin_ctz(spinand_dev.nand_dev->page_size) + 1U; + + spinand_dev.spi_read_cache_op.addr.val = offset; + + if ((spinand_dev.nand_dev->nb_planes > 1U) && ((block_nb % 2U) == 1U)) { + spinand_dev.spi_read_cache_op.addr.val |= 1U << page_sh; + } + + spinand_dev.spi_read_cache_op.data.buf = buffer; + spinand_dev.spi_read_cache_op.data.nbytes = len; + + return spi_mem_exec_op(&spinand_dev.spi_read_cache_op); +} + +static int spi_nand_read_page(unsigned int page, unsigned int offset, + uint8_t *buffer, unsigned int len, + bool ecc_enabled) +{ + uint8_t status; + int ret; + + ret = spi_nand_ecc_enable(ecc_enabled); + if (ret != 0) { + return ret; + } + + ret = spi_nand_load_page(page); + if (ret != 0) { + return ret; + } + + ret = spi_nand_wait_ready(&status); + if (ret != 0) { + return ret; + } + + ret = spi_nand_read_from_cache(page, offset, buffer, len); + if (ret != 0) { + return ret; + } + + if (ecc_enabled && ((status & SPI_NAND_STATUS_ECC_UNCOR) != 0U)) { + return -EBADMSG; + } + + return 0; +} + +static int spi_nand_mtd_block_is_bad(unsigned int block) +{ + unsigned int nbpages_per_block = spinand_dev.nand_dev->block_size / + spinand_dev.nand_dev->page_size; + uint8_t bbm_marker[2]; + int ret; + + ret = spi_nand_read_page(block * nbpages_per_block, + spinand_dev.nand_dev->page_size, + bbm_marker, sizeof(bbm_marker), false); + if (ret != 0) { + return ret; + } + + if ((bbm_marker[0] != GENMASK_32(7, 0)) || + (bbm_marker[1] != GENMASK_32(7, 0))) { + WARN("Block %i is bad\n", block); + return 1; + } + + return 0; +} + +static int spi_nand_mtd_read_page(struct nand_device *nand, unsigned int page, + uintptr_t buffer) +{ + return spi_nand_read_page(page, 0, (uint8_t *)buffer, + spinand_dev.nand_dev->page_size, true); +} + +int spi_nand_init(unsigned long long *size, unsigned int *erase_size) +{ + uint8_t id[SPI_NAND_MAX_ID_LEN]; + int ret; + + spinand_dev.nand_dev = get_nand_device(); + if (spinand_dev.nand_dev == NULL) { + return -EINVAL; + } + + spinand_dev.nand_dev->mtd_block_is_bad = spi_nand_mtd_block_is_bad; + spinand_dev.nand_dev->mtd_read_page = spi_nand_mtd_read_page; + spinand_dev.nand_dev->nb_planes = 1; + + spinand_dev.spi_read_cache_op.cmd.opcode = SPI_NAND_OP_READ_FROM_CACHE; + spinand_dev.spi_read_cache_op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + spinand_dev.spi_read_cache_op.addr.nbytes = 2U; + spinand_dev.spi_read_cache_op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + spinand_dev.spi_read_cache_op.dummy.nbytes = 1U; + spinand_dev.spi_read_cache_op.dummy.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + spinand_dev.spi_read_cache_op.data.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + + if (plat_get_spi_nand_data(&spinand_dev) != 0) { + return -EINVAL; + } + + ret = spi_nand_reset(); + if (ret != 0) { + return ret; + } + + ret = spi_nand_read_id(id); + if (ret != 0) { + return ret; + } + + ret = spi_nand_read_reg(SPI_NAND_REG_CFG, &spinand_dev.cfg_cache); + if (ret != 0) { + return ret; + } + + ret = spi_nand_quad_enable(id[0]); + if (ret != 0) { + return ret; + } + + VERBOSE("SPI_NAND Detected ID 0x%x 0x%x\n", id[0], id[1]); + + VERBOSE("Page size %i, Block size %i, size %lli\n", + spinand_dev.nand_dev->page_size, + spinand_dev.nand_dev->block_size, + spinand_dev.nand_dev->size); + + *size = spinand_dev.nand_dev->size; + *erase_size = spinand_dev.nand_dev->block_size; + + return 0; +} diff --git a/drivers/mtd/nor/spi_nor.c b/drivers/mtd/nor/spi_nor.c new file mode 100644 index 0000000000..22d3ae3d9e --- /dev/null +++ b/drivers/mtd/nor/spi_nor.c @@ -0,0 +1,387 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> +#include <stddef.h> + +#include <common/debug.h> +#include <drivers/delay_timer.h> +#include <drivers/spi_nor.h> +#include <lib/utils.h> + +#define SR_WIP BIT(0) /* Write in progress */ +#define CR_QUAD_EN_SPAN BIT(1) /* Spansion Quad I/O */ +#define SR_QUAD_EN_MX BIT(6) /* Macronix Quad I/O */ +#define FSR_READY BIT(7) /* Device status, 0 = Busy, 1 = Ready */ + +/* Defined IDs for supported memories */ +#define SPANSION_ID 0x01U +#define MACRONIX_ID 0xC2U +#define MICRON_ID 0x2CU + +#define BANK_SIZE 0x1000000U + +#define SPI_READY_TIMEOUT_US 40000U + +static struct nor_device nor_dev; + +#pragma weak plat_get_nor_data +int plat_get_nor_data(struct nor_device *device) +{ + return 0; +} + +static int spi_nor_reg(uint8_t reg, uint8_t *buf, size_t len, + enum spi_mem_data_dir dir) +{ + struct spi_mem_op op; + + zeromem(&op, sizeof(struct spi_mem_op)); + op.cmd.opcode = reg; + op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + op.data.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + op.data.dir = dir; + op.data.nbytes = len; + op.data.buf = buf; + + return spi_mem_exec_op(&op); +} + +static inline int spi_nor_read_id(uint8_t *id) +{ + return spi_nor_reg(SPI_NOR_OP_READ_ID, id, 1U, SPI_MEM_DATA_IN); +} + +static inline int spi_nor_read_cr(uint8_t *cr) +{ + return spi_nor_reg(SPI_NOR_OP_READ_CR, cr, 1U, SPI_MEM_DATA_IN); +} + +static inline int spi_nor_read_sr(uint8_t *sr) +{ + return spi_nor_reg(SPI_NOR_OP_READ_SR, sr, 1U, SPI_MEM_DATA_IN); +} + +static inline int spi_nor_read_fsr(uint8_t *fsr) +{ + return spi_nor_reg(SPI_NOR_OP_READ_FSR, fsr, 1U, SPI_MEM_DATA_IN); +} + +static inline int spi_nor_write_en(void) +{ + return spi_nor_reg(SPI_NOR_OP_WREN, NULL, 0U, SPI_MEM_DATA_OUT); +} + +/* + * Check if device is ready. + * + * Return 0 if ready, 1 if busy or a negative error code otherwise + */ +static int spi_nor_ready(void) +{ + uint8_t sr; + int ret; + + ret = spi_nor_read_sr(&sr); + if (ret != 0) { + return ret; + } + + if ((nor_dev.flags & SPI_NOR_USE_FSR) != 0U) { + uint8_t fsr; + + ret = spi_nor_read_fsr(&fsr); + if (ret != 0) { + return ret; + } + + return (((fsr & FSR_READY) != 0U) && ((sr & SR_WIP) == 0U)) ? + 0 : 1; + } + + return (((sr & SR_WIP) != 0U) ? 1 : 0); +} + +static int spi_nor_wait_ready(void) +{ + int ret; + uint64_t timeout = timeout_init_us(SPI_READY_TIMEOUT_US); + + while (!timeout_elapsed(timeout)) { + ret = spi_nor_ready(); + if (ret <= 0) { + return ret; + } + } + + return -ETIMEDOUT; +} + +static int spi_nor_macronix_quad_enable(void) +{ + uint8_t sr; + int ret; + + ret = spi_nor_read_sr(&sr); + if (ret != 0) { + return ret; + } + + if ((sr & SR_QUAD_EN_MX) == 0U) { + return 0; + } + + ret = spi_nor_write_en(); + if (ret != 0) { + return ret; + } + + sr |= SR_QUAD_EN_MX; + ret = spi_nor_reg(SPI_NOR_OP_WRSR, &sr, 1, SPI_MEM_DATA_OUT); + if (ret != 0) { + return ret; + } + + ret = spi_nor_wait_ready(); + if (ret != 0) { + return ret; + } + + ret = spi_nor_read_sr(&sr); + if ((ret != 0) || ((sr & SR_QUAD_EN_MX) == 0U)) { + return -EINVAL; + } + + return 0; +} + +static int spi_nor_write_sr_cr(uint8_t *sr_cr) +{ + int ret; + + ret = spi_nor_write_en(); + if (ret != 0) { + return ret; + } + + ret = spi_nor_reg(SPI_NOR_OP_WRSR, sr_cr, 2, SPI_MEM_DATA_OUT); + if (ret != 0) { + return -EINVAL; + } + + ret = spi_nor_wait_ready(); + if (ret != 0) { + return ret; + } + + return 0; +} + +static int spi_nor_quad_enable(void) +{ + uint8_t sr_cr[2]; + int ret; + + ret = spi_nor_read_cr(&sr_cr[1]); + if (ret != 0) { + return ret; + } + + if ((sr_cr[1] & CR_QUAD_EN_SPAN) != 0U) { + return 0; + } + + sr_cr[1] |= CR_QUAD_EN_SPAN; + ret = spi_nor_read_sr(&sr_cr[0]); + if (ret != 0) { + return ret; + } + + ret = spi_nor_write_sr_cr(sr_cr); + if (ret != 0) { + return ret; + } + + ret = spi_nor_read_cr(&sr_cr[1]); + if ((ret != 0) || ((sr_cr[1] & CR_QUAD_EN_SPAN) == 0U)) { + return -EINVAL; + } + + return 0; +} + +static int spi_nor_clean_bar(void) +{ + int ret; + + if (nor_dev.selected_bank == 0U) { + return 0; + } + + nor_dev.selected_bank = 0U; + + ret = spi_nor_write_en(); + if (ret != 0) { + return ret; + } + + return spi_nor_reg(nor_dev.bank_write_cmd, &nor_dev.selected_bank, + 1, SPI_MEM_DATA_OUT); +} + +static int spi_nor_write_bar(uint32_t offset) +{ + uint8_t selected_bank = offset / BANK_SIZE; + int ret; + + if (selected_bank == nor_dev.selected_bank) { + return 0; + } + + ret = spi_nor_write_en(); + if (ret != 0) { + return ret; + } + + ret = spi_nor_reg(nor_dev.bank_write_cmd, &selected_bank, + 1, SPI_MEM_DATA_OUT); + if (ret != 0) { + return ret; + } + + nor_dev.selected_bank = selected_bank; + + return 0; +} + +static int spi_nor_read_bar(void) +{ + uint8_t selected_bank = 0; + int ret; + + ret = spi_nor_reg(nor_dev.bank_read_cmd, &selected_bank, + 1, SPI_MEM_DATA_IN); + if (ret != 0) { + return ret; + } + + nor_dev.selected_bank = selected_bank; + + return 0; +} + +int spi_nor_read(unsigned int offset, uintptr_t buffer, size_t length, + size_t *length_read) +{ + size_t remain_len; + int ret; + + *length_read = 0; + nor_dev.read_op.addr.val = offset; + nor_dev.read_op.data.buf = (void *)buffer; + + VERBOSE("%s offset %i length %zu\n", __func__, offset, length); + + while (length != 0U) { + if ((nor_dev.flags & SPI_NOR_USE_BANK) != 0U) { + ret = spi_nor_write_bar(nor_dev.read_op.addr.val); + if (ret != 0) { + return ret; + } + + remain_len = (BANK_SIZE * (nor_dev.selected_bank + 1)) - + nor_dev.read_op.addr.val; + nor_dev.read_op.data.nbytes = MIN(length, remain_len); + } else { + nor_dev.read_op.data.nbytes = length; + } + + ret = spi_mem_exec_op(&nor_dev.read_op); + if (ret != 0) { + spi_nor_clean_bar(); + return ret; + } + + length -= nor_dev.read_op.data.nbytes; + nor_dev.read_op.addr.val += nor_dev.read_op.data.nbytes; + nor_dev.read_op.data.buf += nor_dev.read_op.data.nbytes; + *length_read += nor_dev.read_op.data.nbytes; + } + + if ((nor_dev.flags & SPI_NOR_USE_BANK) != 0U) { + ret = spi_nor_clean_bar(); + if (ret != 0) { + return ret; + } + } + + return 0; +} + +int spi_nor_init(unsigned long long *size, unsigned int *erase_size) +{ + int ret = 0; + uint8_t id; + + /* Default read command used */ + nor_dev.read_op.cmd.opcode = SPI_NOR_OP_READ; + nor_dev.read_op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + nor_dev.read_op.addr.nbytes = 3U; + nor_dev.read_op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + nor_dev.read_op.data.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + nor_dev.read_op.data.dir = SPI_MEM_DATA_IN; + + if (plat_get_nor_data(&nor_dev) != 0) { + return -EINVAL; + } + + assert(nor_dev.size != 0); + + if (nor_dev.size > BANK_SIZE) { + nor_dev.flags |= SPI_NOR_USE_BANK; + } + + *size = nor_dev.size; + + ret = spi_nor_read_id(&id); + if (ret != 0) { + return ret; + } + + if ((nor_dev.flags & SPI_NOR_USE_BANK) != 0U) { + switch (id) { + case SPANSION_ID: + nor_dev.bank_read_cmd = SPINOR_OP_BRRD; + nor_dev.bank_write_cmd = SPINOR_OP_BRWR; + break; + default: + nor_dev.bank_read_cmd = SPINOR_OP_RDEAR; + nor_dev.bank_write_cmd = SPINOR_OP_WREAR; + break; + } + } + + if (nor_dev.read_op.data.buswidth == 4U) { + switch (id) { + case MACRONIX_ID: + WARN("Enable Macronix quad support\n"); + ret = spi_nor_macronix_quad_enable(); + break; + case MICRON_ID: + break; + default: + ret = spi_nor_quad_enable(); + break; + } + } + + if ((ret == 0) && ((nor_dev.flags & SPI_NOR_USE_BANK) != 0U)) { + ret = spi_nor_read_bar(); + } + + return ret; +} diff --git a/drivers/mtd/spi-mem/spi_mem.c b/drivers/mtd/spi-mem/spi_mem.c new file mode 100644 index 0000000000..63ea7699b8 --- /dev/null +++ b/drivers/mtd/spi-mem/spi_mem.c @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> + +#include <libfdt.h> + +#include <drivers/spi_mem.h> +#include <lib/utils_def.h> + +#define SPI_MEM_DEFAULT_SPEED_HZ 100000U + +/* + * struct spi_slave - Representation of a SPI slave. + * + * @max_hz: Maximum speed for this slave in Hertz. + * @cs: ID of the chip select connected to the slave. + * @mode: SPI mode to use for this slave (see SPI mode flags). + * @ops: Ops defined by the bus. + */ +struct spi_slave { + unsigned int max_hz; + unsigned int cs; + unsigned int mode; + const struct spi_bus_ops *ops; +}; + +static struct spi_slave spi_slave; + +static bool spi_mem_check_buswidth_req(uint8_t buswidth, bool tx) +{ + switch (buswidth) { + case 1U: + return true; + + case 2U: + if ((tx && (spi_slave.mode & (SPI_TX_DUAL | SPI_TX_QUAD)) != + 0U) || + (!tx && (spi_slave.mode & (SPI_RX_DUAL | SPI_RX_QUAD)) != + 0U)) { + return true; + } + break; + + case 4U: + if ((tx && (spi_slave.mode & SPI_TX_QUAD) != 0U) || + (!tx && (spi_slave.mode & SPI_RX_QUAD) != 0U)) { + return true; + } + break; + + default: + break; + } + + return false; +} + +static bool spi_mem_supports_op(const struct spi_mem_op *op) +{ + if (!spi_mem_check_buswidth_req(op->cmd.buswidth, true)) { + return false; + } + + if ((op->addr.nbytes != 0U) && + !spi_mem_check_buswidth_req(op->addr.buswidth, true)) { + return false; + } + + if ((op->dummy.nbytes != 0U) && + !spi_mem_check_buswidth_req(op->dummy.buswidth, true)) { + return false; + } + + if ((op->data.nbytes != 0U) && + !spi_mem_check_buswidth_req(op->data.buswidth, + op->data.dir == SPI_MEM_DATA_OUT)) { + return false; + } + + return true; +} + +static int spi_mem_set_speed_mode(void) +{ + const struct spi_bus_ops *ops = spi_slave.ops; + int ret; + + ret = ops->set_speed(spi_slave.max_hz); + if (ret != 0) { + VERBOSE("Cannot set speed (err=%d)\n", ret); + return ret; + } + + ret = ops->set_mode(spi_slave.mode); + if (ret != 0) { + VERBOSE("Cannot set mode (err=%d)\n", ret); + return ret; + } + + return 0; +} + +static int spi_mem_check_bus_ops(const struct spi_bus_ops *ops) +{ + bool error = false; + + if (ops->claim_bus == NULL) { + VERBOSE("Ops claim bus is not defined\n"); + error = true; + } + + if (ops->release_bus == NULL) { + VERBOSE("Ops release bus is not defined\n"); + error = true; + } + + if (ops->exec_op == NULL) { + VERBOSE("Ops exec op is not defined\n"); + error = true; + } + + if (ops->set_speed == NULL) { + VERBOSE("Ops set speed is not defined\n"); + error = true; + } + + if (ops->set_mode == NULL) { + VERBOSE("Ops set mode is not defined\n"); + error = true; + } + + return error ? -EINVAL : 0; +} + +/* + * spi_mem_exec_op() - Execute a memory operation. + * @op: The memory operation to execute. + * + * This function first checks that @op is supported and then tries to execute + * it. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int spi_mem_exec_op(const struct spi_mem_op *op) +{ + const struct spi_bus_ops *ops = spi_slave.ops; + int ret; + + VERBOSE("%s: cmd:%x mode:%d.%d.%d.%d addqr:%llx len:%x\n", + __func__, op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth, + op->dummy.buswidth, op->data.buswidth, + op->addr.val, op->data.nbytes); + + if (!spi_mem_supports_op(op)) { + WARN("Error in spi_mem_support\n"); + return -ENOTSUP; + } + + ret = ops->claim_bus(spi_slave.cs); + if (ret != 0) { + WARN("Error claim_bus\n"); + return ret; + } + + ret = ops->exec_op(op); + + ops->release_bus(); + + return ret; +} + +/* + * spi_mem_init_slave() - SPI slave device initialization. + * @fdt: Pointer to the device tree blob. + * @bus_node: Offset of the bus node. + * @ops: The SPI bus ops defined. + * + * This function first checks that @ops are supported and then tries to find + * a SPI slave device. + * + * Return: 0 in case of success, a negative error code otherwise. + */ +int spi_mem_init_slave(void *fdt, int bus_node, const struct spi_bus_ops *ops) +{ + int ret; + int mode = 0; + int nchips = 0; + int bus_subnode = 0; + const fdt32_t *cuint = NULL; + + ret = spi_mem_check_bus_ops(ops); + if (ret != 0) { + return ret; + } + + fdt_for_each_subnode(bus_subnode, fdt, bus_node) { + nchips++; + } + + if (nchips != 1) { + ERROR("Only one SPI device is currently supported\n"); + return -EINVAL; + } + + fdt_for_each_subnode(bus_subnode, fdt, bus_node) { + /* Get chip select */ + cuint = fdt_getprop(fdt, bus_subnode, "reg", NULL); + if (cuint == NULL) { + ERROR("Chip select not well defined\n"); + return -EINVAL; + } + spi_slave.cs = fdt32_to_cpu(*cuint); + + /* Get max slave frequency */ + spi_slave.max_hz = SPI_MEM_DEFAULT_SPEED_HZ; + cuint = fdt_getprop(fdt, bus_subnode, + "spi-max-frequency", NULL); + if (cuint != NULL) { + spi_slave.max_hz = fdt32_to_cpu(*cuint); + } + + /* Get mode */ + if ((fdt_getprop(fdt, bus_subnode, "spi-cpol", NULL)) != NULL) { + mode |= SPI_CPOL; + } + if ((fdt_getprop(fdt, bus_subnode, "spi-cpha", NULL)) != NULL) { + mode |= SPI_CPHA; + } + if ((fdt_getprop(fdt, bus_subnode, "spi-cs-high", NULL)) != + NULL) { + mode |= SPI_CS_HIGH; + } + if ((fdt_getprop(fdt, bus_subnode, "spi-3wire", NULL)) != + NULL) { + mode |= SPI_3WIRE; + } + if ((fdt_getprop(fdt, bus_subnode, "spi-half-duplex", NULL)) != + NULL) { + mode |= SPI_PREAMBLE; + } + + /* Get dual/quad mode */ + cuint = fdt_getprop(fdt, bus_subnode, "spi-tx-bus-width", NULL); + if (cuint != NULL) { + switch (fdt32_to_cpu(*cuint)) { + case 1U: + break; + case 2U: + mode |= SPI_TX_DUAL; + break; + case 4U: + mode |= SPI_TX_QUAD; + break; + default: + WARN("spi-tx-bus-width %d not supported\n", + fdt32_to_cpu(*cuint)); + return -EINVAL; + } + } + + cuint = fdt_getprop(fdt, bus_subnode, "spi-rx-bus-width", NULL); + if (cuint != NULL) { + switch (fdt32_to_cpu(*cuint)) { + case 1U: + break; + case 2U: + mode |= SPI_RX_DUAL; + break; + case 4U: + mode |= SPI_RX_QUAD; + break; + default: + WARN("spi-rx-bus-width %d not supported\n", + fdt32_to_cpu(*cuint)); + return -EINVAL; + } + } + + spi_slave.mode = mode; + spi_slave.ops = ops; + } + + return spi_mem_set_speed_mode(); +} diff --git a/drivers/renesas/rcar/io/io_emmcdrv.c b/drivers/renesas/rcar/io/io_emmcdrv.c index 4b464fb3e4..84240d2606 100644 --- a/drivers/renesas/rcar/io/io_emmcdrv.c +++ b/drivers/renesas/rcar/io/io_emmcdrv.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, Renesas Electronics Corporation. All rights reserved. + * Copyright (c) 2015-2019, Renesas Electronics Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -25,7 +25,7 @@ static int32_t emmcdrv_dev_close(io_dev_info_t *dev_info); typedef struct { uint32_t in_use; uintptr_t base; - ssize_t file_pos; + signed long long file_pos; EMMC_PARTITION_ID partition; } file_state_t; @@ -39,7 +39,7 @@ static io_type_t device_type_emmcdrv(void) } static int32_t emmcdrv_block_seek(io_entity_t *entity, int32_t mode, - ssize_t offset) + signed long long offset) { if (mode != IO_SEEK_SET) return IO_FAIL; @@ -59,12 +59,12 @@ static int32_t emmcdrv_block_read(io_entity_t *entity, uintptr_t buffer, sector_add = current_file.file_pos >> EMMC_SECTOR_SIZE_SHIFT; sector_num = (length + EMMC_SECTOR_SIZE - 1U) >> EMMC_SECTOR_SIZE_SHIFT; - NOTICE("BL2: Load dst=0x%lx src=(p:%d)0x%lx(%d) len=0x%lx(%d)\n", + NOTICE("BL2: Load dst=0x%lx src=(p:%d)0x%llx(%d) len=0x%lx(%d)\n", buffer, current_file.partition, current_file.file_pos, sector_add, length, sector_num); - if (buffer + length - 1 <= UINT32_MAX) + if ((buffer + length - 1U) <= (uintptr_t)UINT32_MAX) emmc_dma = LOADIMAGE_FLAGS_DMA_ENABLE; if (emmc_read_sector((uint32_t *) buffer, sector_add, sector_num, @@ -72,7 +72,7 @@ static int32_t emmcdrv_block_read(io_entity_t *entity, uintptr_t buffer, result = IO_FAIL; *length_read = length; - fp->file_pos += length; + fp->file_pos += (signed long long)length; return result; } @@ -82,7 +82,7 @@ static int32_t emmcdrv_block_open(io_dev_info_t *dev_info, { const io_drv_spec_t *block_spec = (io_drv_spec_t *) spec; - if (current_file.in_use) { + if (current_file.in_use != 0U) { WARN("mmc_block: Only one open spec at a time\n"); return IO_RESOURCES_EXHAUSTED; } @@ -103,9 +103,9 @@ static int32_t emmcdrv_block_open(io_dev_info_t *dev_info, return IO_FAIL; } - if (PARTITION_ID_USER == block_spec->partition || - PARTITION_ID_BOOT_1 == block_spec->partition || - PARTITION_ID_BOOT_2 == block_spec->partition) + if ((PARTITION_ID_USER == block_spec->partition) || + (PARTITION_ID_BOOT_1 == block_spec->partition) || + (PARTITION_ID_BOOT_2 == block_spec->partition)) current_file.partition = block_spec->partition; else current_file.partition = emmcdrv_bootpartition; diff --git a/drivers/renesas/rcar/io/io_memdrv.c b/drivers/renesas/rcar/io/io_memdrv.c index 3f6b4c71b1..7e8c1d3a65 100644 --- a/drivers/renesas/rcar/io/io_memdrv.c +++ b/drivers/renesas/rcar/io/io_memdrv.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2018, Renesas Electronics Corporation. All rights reserved. + * Copyright (c) 2015-2019, Renesas Electronics Corporation. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -28,7 +28,7 @@ static int32_t memdrv_dev_close(io_dev_info_t *dev_info); typedef struct { uint32_t in_use; uintptr_t base; - ssize_t file_pos; + signed long long file_pos; } file_state_t; static file_state_t current_file = { 0 }; @@ -47,7 +47,7 @@ static int32_t memdrv_block_open(io_dev_info_t *dev_info, const uintptr_t spec, * spec at a time. When we have dynamic memory we can malloc and set * entity->info. */ - if (current_file.in_use) + if (current_file.in_use != 0U) return IO_RESOURCES_EXHAUSTED; /* File cursor offset for seek and incremental reads etc. */ @@ -61,7 +61,7 @@ static int32_t memdrv_block_open(io_dev_info_t *dev_info, const uintptr_t spec, } static int32_t memdrv_block_seek(io_entity_t *entity, int32_t mode, - ssize_t offset) + signed long long offset) { if (mode != IO_SEEK_SET) return IO_FAIL; @@ -78,16 +78,17 @@ static int32_t memdrv_block_read(io_entity_t *entity, uintptr_t buffer, fp = (file_state_t *) entity->info; - NOTICE("BL2: dst=0x%lx src=0x%lx len=%ld(0x%lx)\n", - buffer, fp->base + fp->file_pos, length, length); + NOTICE("BL2: dst=0x%lx src=0x%llx len=%ld(0x%lx)\n", + buffer, (unsigned long long)fp->base + + (unsigned long long)fp->file_pos, length, length); - if (FLASH_MEMORY_SIZE < fp->file_pos + length) { + if (FLASH_MEMORY_SIZE < (fp->file_pos + (signed long long)length)) { ERROR("BL2: check load image (source address)\n"); return IO_FAIL; } - rcar_dma_exec(buffer, fp->base + fp->file_pos, length); - fp->file_pos += length; + rcar_dma_exec(buffer, fp->base + (uintptr_t)fp->file_pos, length); + fp->file_pos += (signed long long)length; *cnt = length; return IO_SUCCESS; diff --git a/drivers/st/fmc/stm32_fmc2_nand.c b/drivers/st/fmc/stm32_fmc2_nand.c new file mode 100644 index 0000000000..b694fff6bf --- /dev/null +++ b/drivers/st/fmc/stm32_fmc2_nand.c @@ -0,0 +1,877 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause + */ + +#include <assert.h> +#include <errno.h> +#include <limits.h> +#include <stdint.h> + +#include <libfdt.h> + +#include <platform_def.h> + +#include <common/debug.h> +#include <drivers/delay_timer.h> +#include <drivers/raw_nand.h> +#include <drivers/st/stm32_fmc2_nand.h> +#include <drivers/st/stm32_gpio.h> +#include <drivers/st/stm32mp_reset.h> +#include <lib/mmio.h> +#include <lib/utils_def.h> + +/* FMC2 Compatibility */ +#define DT_FMC2_COMPAT "st,stm32mp15-fmc2" +#define MAX_CS 2U + +/* FMC2 Controller Registers */ +#define FMC2_BCR1 0x00U +#define FMC2_PCR 0x80U +#define FMC2_SR 0x84U +#define FMC2_PMEM 0x88U +#define FMC2_PATT 0x8CU +#define FMC2_HECCR 0x94U +#define FMC2_BCHISR 0x254U +#define FMC2_BCHDSR0 0x27CU +#define FMC2_BCHDSR1 0x280U +#define FMC2_BCHDSR2 0x284U +#define FMC2_BCHDSR3 0x288U +#define FMC2_BCHDSR4 0x28CU + +/* FMC2_BCR1 register */ +#define FMC2_BCR1_FMC2EN BIT(31) +/* FMC2_PCR register */ +#define FMC2_PCR_PWAITEN BIT(1) +#define FMC2_PCR_PBKEN BIT(2) +#define FMC2_PCR_PWID_MASK GENMASK_32(5, 4) +#define FMC2_PCR_PWID(x) (((x) << 4) & FMC2_PCR_PWID_MASK) +#define FMC2_PCR_PWID_8 0x0U +#define FMC2_PCR_PWID_16 0x1U +#define FMC2_PCR_ECCEN BIT(6) +#define FMC2_PCR_ECCALG BIT(8) +#define FMC2_PCR_TCLR_MASK GENMASK_32(12, 9) +#define FMC2_PCR_TCLR(x) (((x) << 9) & FMC2_PCR_TCLR_MASK) +#define FMC2_PCR_TCLR_DEFAULT 0xFU +#define FMC2_PCR_TAR_MASK GENMASK_32(16, 13) +#define FMC2_PCR_TAR(x) (((x) << 13) & FMC2_PCR_TAR_MASK) +#define FMC2_PCR_TAR_DEFAULT 0xFU +#define FMC2_PCR_ECCSS_MASK GENMASK_32(19, 17) +#define FMC2_PCR_ECCSS(x) (((x) << 17) & FMC2_PCR_ECCSS_MASK) +#define FMC2_PCR_ECCSS_512 0x1U +#define FMC2_PCR_ECCSS_2048 0x3U +#define FMC2_PCR_BCHECC BIT(24) +#define FMC2_PCR_WEN BIT(25) +/* FMC2_SR register */ +#define FMC2_SR_NWRF BIT(6) +/* FMC2_PMEM register*/ +#define FMC2_PMEM_MEMSET(x) (((x) & GENMASK_32(7, 0)) << 0) +#define FMC2_PMEM_MEMWAIT(x) (((x) & GENMASK_32(7, 0)) << 8) +#define FMC2_PMEM_MEMHOLD(x) (((x) & GENMASK_32(7, 0)) << 16) +#define FMC2_PMEM_MEMHIZ(x) (((x) & GENMASK_32(7, 0)) << 24) +#define FMC2_PMEM_DEFAULT 0x0A0A0A0AU +/* FMC2_PATT register */ +#define FMC2_PATT_ATTSET(x) (((x) & GENMASK_32(7, 0)) << 0) +#define FMC2_PATT_ATTWAIT(x) (((x) & GENMASK_32(7, 0)) << 8) +#define FMC2_PATT_ATTHOLD(x) (((x) & GENMASK_32(7, 0)) << 16) +#define FMC2_PATT_ATTHIZ(x) (((x) & GENMASK_32(7, 0)) << 24) +#define FMC2_PATT_DEFAULT 0x0A0A0A0AU +/* FMC2_BCHISR register */ +#define FMC2_BCHISR_DERF BIT(1) +/* FMC2_BCHDSR0 register */ +#define FMC2_BCHDSR0_DUE BIT(0) +#define FMC2_BCHDSR0_DEF BIT(1) +#define FMC2_BCHDSR0_DEN_MASK GENMASK_32(7, 4) +#define FMC2_BCHDSR0_DEN_SHIFT 4U +/* FMC2_BCHDSR1 register */ +#define FMC2_BCHDSR1_EBP1_MASK GENMASK_32(12, 0) +#define FMC2_BCHDSR1_EBP2_MASK GENMASK_32(28, 16) +#define FMC2_BCHDSR1_EBP2_SHIFT 16U +/* FMC2_BCHDSR2 register */ +#define FMC2_BCHDSR2_EBP3_MASK GENMASK_32(12, 0) +#define FMC2_BCHDSR2_EBP4_MASK GENMASK_32(28, 16) +#define FMC2_BCHDSR2_EBP4_SHIFT 16U +/* FMC2_BCHDSR3 register */ +#define FMC2_BCHDSR3_EBP5_MASK GENMASK_32(12, 0) +#define FMC2_BCHDSR3_EBP6_MASK GENMASK_32(28, 16) +#define FMC2_BCHDSR3_EBP6_SHIFT 16U +/* FMC2_BCHDSR4 register */ +#define FMC2_BCHDSR4_EBP7_MASK GENMASK_32(12, 0) +#define FMC2_BCHDSR4_EBP8_MASK GENMASK_32(28, 16) +#define FMC2_BCHDSR4_EBP8_SHIFT 16U + +/* Timings */ +#define FMC2_THIZ 0x01U +#define FMC2_TIO 8000U +#define FMC2_TSYNC 3000U +#define FMC2_PCR_TIMING_MASK GENMASK_32(3, 0) +#define FMC2_PMEM_PATT_TIMING_MASK GENMASK_32(7, 0) + +#define FMC2_BBM_LEN 2U +#define FMC2_MAX_ECC_BYTES 14U +#define TIMEOUT_US_10_MS 10000U +#define FMC2_PSEC_PER_MSEC (1000UL * 1000UL * 1000UL) + +enum stm32_fmc2_ecc { + FMC2_ECC_HAM = 1U, + FMC2_ECC_BCH4 = 4U, + FMC2_ECC_BCH8 = 8U +}; + +struct stm32_fmc2_cs_reg { + uintptr_t data_base; + uintptr_t cmd_base; + uintptr_t addr_base; +}; + +struct stm32_fmc2_nand_timings { + uint8_t tclr; + uint8_t tar; + uint8_t thiz; + uint8_t twait; + uint8_t thold_mem; + uint8_t tset_mem; + uint8_t thold_att; + uint8_t tset_att; +}; + +struct stm32_fmc2_nfc { + uintptr_t reg_base; + struct stm32_fmc2_cs_reg cs[MAX_CS]; + unsigned long clock_id; + unsigned int reset_id; + uint8_t cs_sel; +}; + +static struct stm32_fmc2_nfc stm32_fmc2; + +static uintptr_t fmc2_base(void) +{ + return stm32_fmc2.reg_base; +} + +static void stm32_fmc2_nand_setup_timing(void) +{ + struct stm32_fmc2_nand_timings tims; + unsigned long hclk = stm32mp_clk_get_rate(stm32_fmc2.clock_id); + unsigned long hclkp = FMC2_PSEC_PER_MSEC / (hclk / 1000U); + unsigned long timing, tar, tclr, thiz, twait; + unsigned long tset_mem, tset_att, thold_mem, thold_att; + uint32_t pcr, pmem, patt; + + tar = MAX(hclkp, NAND_TAR_MIN); + timing = div_round_up(tar, hclkp) - 1U; + tims.tar = MIN(timing, (unsigned long)FMC2_PCR_TIMING_MASK); + + tclr = MAX(hclkp, NAND_TCLR_MIN); + timing = div_round_up(tclr, hclkp) - 1U; + tims.tclr = MIN(timing, (unsigned long)FMC2_PCR_TIMING_MASK); + + tims.thiz = FMC2_THIZ; + thiz = (tims.thiz + 1U) * hclkp; + + /* + * tWAIT > tRP + * tWAIT > tWP + * tWAIT > tREA + tIO + */ + twait = MAX(hclkp, NAND_TRP_MIN); + twait = MAX(twait, NAND_TWP_MIN); + twait = MAX(twait, NAND_TREA_MAX + FMC2_TIO); + timing = div_round_up(twait, hclkp); + tims.twait = CLAMP(timing, 1UL, + (unsigned long)FMC2_PMEM_PATT_TIMING_MASK); + + /* + * tSETUP_MEM > tCS - tWAIT + * tSETUP_MEM > tALS - tWAIT + * tSETUP_MEM > tDS - (tWAIT - tHIZ) + */ + tset_mem = hclkp; + if ((twait < NAND_TCS_MIN) && (tset_mem < (NAND_TCS_MIN - twait))) { + tset_mem = NAND_TCS_MIN - twait; + } + if ((twait < NAND_TALS_MIN) && (tset_mem < (NAND_TALS_MIN - twait))) { + tset_mem = NAND_TALS_MIN - twait; + } + if ((twait > thiz) && ((twait - thiz) < NAND_TDS_MIN) && + (tset_mem < (NAND_TDS_MIN - (twait - thiz)))) { + tset_mem = NAND_TDS_MIN - (twait - thiz); + } + timing = div_round_up(tset_mem, hclkp); + tims.tset_mem = CLAMP(timing, 1UL, + (unsigned long)FMC2_PMEM_PATT_TIMING_MASK); + + /* + * tHOLD_MEM > tCH + * tHOLD_MEM > tREH - tSETUP_MEM + * tHOLD_MEM > max(tRC, tWC) - (tSETUP_MEM + tWAIT) + */ + thold_mem = MAX(hclkp, NAND_TCH_MIN); + if ((tset_mem < NAND_TREH_MIN) && + (thold_mem < (NAND_TREH_MIN - tset_mem))) { + thold_mem = NAND_TREH_MIN - tset_mem; + } + if (((tset_mem + twait) < NAND_TRC_MIN) && + (thold_mem < (NAND_TRC_MIN - (tset_mem + twait)))) { + thold_mem = NAND_TRC_MIN - (tset_mem + twait); + } + if (((tset_mem + twait) < NAND_TWC_MIN) && + (thold_mem < (NAND_TWC_MIN - (tset_mem + twait)))) { + thold_mem = NAND_TWC_MIN - (tset_mem + twait); + } + timing = div_round_up(thold_mem, hclkp); + tims.thold_mem = CLAMP(timing, 1UL, + (unsigned long)FMC2_PMEM_PATT_TIMING_MASK); + + /* + * tSETUP_ATT > tCS - tWAIT + * tSETUP_ATT > tCLS - tWAIT + * tSETUP_ATT > tALS - tWAIT + * tSETUP_ATT > tRHW - tHOLD_MEM + * tSETUP_ATT > tDS - (tWAIT - tHIZ) + */ + tset_att = hclkp; + if ((twait < NAND_TCS_MIN) && (tset_att < (NAND_TCS_MIN - twait))) { + tset_att = NAND_TCS_MIN - twait; + } + if ((twait < NAND_TCLS_MIN) && (tset_att < (NAND_TCLS_MIN - twait))) { + tset_att = NAND_TCLS_MIN - twait; + } + if ((twait < NAND_TALS_MIN) && (tset_att < (NAND_TALS_MIN - twait))) { + tset_att = NAND_TALS_MIN - twait; + } + if ((thold_mem < NAND_TRHW_MIN) && + (tset_att < (NAND_TRHW_MIN - thold_mem))) { + tset_att = NAND_TRHW_MIN - thold_mem; + } + if ((twait > thiz) && ((twait - thiz) < NAND_TDS_MIN) && + (tset_att < (NAND_TDS_MIN - (twait - thiz)))) { + tset_att = NAND_TDS_MIN - (twait - thiz); + } + timing = div_round_up(tset_att, hclkp); + tims.tset_att = CLAMP(timing, 1UL, + (unsigned long)FMC2_PMEM_PATT_TIMING_MASK); + + /* + * tHOLD_ATT > tALH + * tHOLD_ATT > tCH + * tHOLD_ATT > tCLH + * tHOLD_ATT > tCOH + * tHOLD_ATT > tDH + * tHOLD_ATT > tWB + tIO + tSYNC - tSETUP_MEM + * tHOLD_ATT > tADL - tSETUP_MEM + * tHOLD_ATT > tWH - tSETUP_MEM + * tHOLD_ATT > tWHR - tSETUP_MEM + * tHOLD_ATT > tRC - (tSETUP_ATT + tWAIT) + * tHOLD_ATT > tWC - (tSETUP_ATT + tWAIT) + */ + thold_att = MAX(hclkp, NAND_TALH_MIN); + thold_att = MAX(thold_att, NAND_TCH_MIN); + thold_att = MAX(thold_att, NAND_TCLH_MIN); + thold_att = MAX(thold_att, NAND_TCOH_MIN); + thold_att = MAX(thold_att, NAND_TDH_MIN); + if (((NAND_TWB_MAX + FMC2_TIO + FMC2_TSYNC) > tset_mem) && + (thold_att < (NAND_TWB_MAX + FMC2_TIO + FMC2_TSYNC - tset_mem))) { + thold_att = NAND_TWB_MAX + FMC2_TIO + FMC2_TSYNC - tset_mem; + } + if ((tset_mem < NAND_TADL_MIN) && + (thold_att < (NAND_TADL_MIN - tset_mem))) { + thold_att = NAND_TADL_MIN - tset_mem; + } + if ((tset_mem < NAND_TWH_MIN) && + (thold_att < (NAND_TWH_MIN - tset_mem))) { + thold_att = NAND_TWH_MIN - tset_mem; + } + if ((tset_mem < NAND_TWHR_MIN) && + (thold_att < (NAND_TWHR_MIN - tset_mem))) { + thold_att = NAND_TWHR_MIN - tset_mem; + } + if (((tset_att + twait) < NAND_TRC_MIN) && + (thold_att < (NAND_TRC_MIN - (tset_att + twait)))) { + thold_att = NAND_TRC_MIN - (tset_att + twait); + } + if (((tset_att + twait) < NAND_TWC_MIN) && + (thold_att < (NAND_TWC_MIN - (tset_att + twait)))) { + thold_att = NAND_TWC_MIN - (tset_att + twait); + } + timing = div_round_up(thold_att, hclkp); + tims.thold_att = CLAMP(timing, 1UL, + (unsigned long)FMC2_PMEM_PATT_TIMING_MASK); + + VERBOSE("NAND timings: %u - %u - %u - %u - %u - %u - %u - %u\n", + tims.tclr, tims.tar, tims.thiz, tims.twait, + tims.thold_mem, tims.tset_mem, + tims.thold_att, tims.tset_att); + + /* Set tclr/tar timings */ + pcr = mmio_read_32(fmc2_base() + FMC2_PCR); + pcr &= ~FMC2_PCR_TCLR_MASK; + pcr |= FMC2_PCR_TCLR(tims.tclr); + pcr &= ~FMC2_PCR_TAR_MASK; + pcr |= FMC2_PCR_TAR(tims.tar); + + /* Set tset/twait/thold/thiz timings in common bank */ + pmem = FMC2_PMEM_MEMSET(tims.tset_mem); + pmem |= FMC2_PMEM_MEMWAIT(tims.twait); + pmem |= FMC2_PMEM_MEMHOLD(tims.thold_mem); + pmem |= FMC2_PMEM_MEMHIZ(tims.thiz); + + /* Set tset/twait/thold/thiz timings in attribute bank */ + patt = FMC2_PATT_ATTSET(tims.tset_att); + patt |= FMC2_PATT_ATTWAIT(tims.twait); + patt |= FMC2_PATT_ATTHOLD(tims.thold_att); + patt |= FMC2_PATT_ATTHIZ(tims.thiz); + + mmio_write_32(fmc2_base() + FMC2_PCR, pcr); + mmio_write_32(fmc2_base() + FMC2_PMEM, pmem); + mmio_write_32(fmc2_base() + FMC2_PATT, patt); +} + +static void stm32_fmc2_set_buswidth_16(bool set) +{ + mmio_clrsetbits_32(fmc2_base() + FMC2_PCR, FMC2_PCR_PWID_MASK, + (set ? FMC2_PCR_PWID(FMC2_PCR_PWID_16) : 0U)); +} + +static void stm32_fmc2_set_ecc(bool enable) +{ + mmio_clrsetbits_32(fmc2_base() + FMC2_PCR, FMC2_PCR_ECCEN, + (enable ? FMC2_PCR_ECCEN : 0U)); +} + +static int stm32_fmc2_ham_correct(uint8_t *buffer, uint8_t *eccbuffer, + uint8_t *ecc) +{ + uint8_t xor_ecc_ones; + uint16_t xor_ecc_1b, xor_ecc_2b, xor_ecc_3b; + union { + uint32_t val; + uint8_t bytes[4]; + } xor_ecc; + + /* Page size--------ECC_Code Size + * 256---------------22 bits LSB (ECC_CODE & 0x003FFFFF) + * 512---------------24 bits (ECC_CODE & 0x00FFFFFF) + * 1024--------------26 bits (ECC_CODE & 0x03FFFFFF) + * 2048--------------28 bits (ECC_CODE & 0x0FFFFFFF) + * 4096--------------30 bits (ECC_CODE & 0x3FFFFFFF) + * 8192--------------32 bits (ECC_CODE & 0xFFFFFFFF) + */ + + /* For Page size 512, ECC_Code size 24 bits */ + xor_ecc_1b = ecc[0] ^ eccbuffer[0]; + xor_ecc_2b = ecc[1] ^ eccbuffer[1]; + xor_ecc_3b = ecc[2] ^ eccbuffer[2]; + + xor_ecc.val = 0L; + xor_ecc.bytes[2] = xor_ecc_3b; + xor_ecc.bytes[1] = xor_ecc_2b; + xor_ecc.bytes[0] = xor_ecc_1b; + + if (xor_ecc.val == 0U) { + return 0; /* No Error */ + } + + xor_ecc_ones = __builtin_popcount(xor_ecc.val); + if (xor_ecc_ones < 23U) { + if (xor_ecc_ones == 12U) { + uint16_t bit_address, byte_address; + + /* Correctable ERROR */ + bit_address = ((xor_ecc_1b >> 1) & BIT(0)) | + ((xor_ecc_1b >> 2) & BIT(1)) | + ((xor_ecc_1b >> 3) & BIT(2)); + + byte_address = ((xor_ecc_1b >> 7) & BIT(0)) | + ((xor_ecc_2b) & BIT(1)) | + ((xor_ecc_2b >> 1) & BIT(2)) | + ((xor_ecc_2b >> 2) & BIT(3)) | + ((xor_ecc_2b >> 3) & BIT(4)) | + ((xor_ecc_3b << 4) & BIT(5)) | + ((xor_ecc_3b << 3) & BIT(6)) | + ((xor_ecc_3b << 2) & BIT(7)) | + ((xor_ecc_3b << 1) & BIT(8)); + + /* Correct bit error in the data */ + buffer[byte_address] = + buffer[byte_address] ^ BIT(bit_address); + VERBOSE("Hamming: 1 ECC error corrected\n"); + + return 0; + } + + /* Non Correctable ERROR */ + ERROR("%s: Uncorrectable ECC Errors\n", __func__); + return -1; + } + + /* ECC ERROR */ + ERROR("%s: Hamming correction error\n", __func__); + return -1; +} + + +static int stm32_fmc2_ham_calculate(uint8_t *buffer, uint8_t *ecc) +{ + uint32_t heccr; + uint64_t timeout = timeout_init_us(TIMEOUT_US_10_MS); + + while ((mmio_read_32(fmc2_base() + FMC2_SR) & FMC2_SR_NWRF) == 0U) { + if (timeout_elapsed(timeout)) { + return -ETIMEDOUT; + } + } + + heccr = mmio_read_32(fmc2_base() + FMC2_HECCR); + + ecc[0] = heccr; + ecc[1] = heccr >> 8; + ecc[2] = heccr >> 16; + + /* Disable ECC */ + stm32_fmc2_set_ecc(false); + + return 0; +} + +static int stm32_fmc2_bch_correct(uint8_t *buffer, unsigned int eccsize) +{ + uint32_t bchdsr0, bchdsr1, bchdsr2, bchdsr3, bchdsr4; + uint16_t pos[8]; + int i, den; + uint64_t timeout = timeout_init_us(TIMEOUT_US_10_MS); + + while ((mmio_read_32(fmc2_base() + FMC2_BCHISR) & + FMC2_BCHISR_DERF) == 0U) { + if (timeout_elapsed(timeout)) { + return -ETIMEDOUT; + } + } + + bchdsr0 = mmio_read_32(fmc2_base() + FMC2_BCHDSR0); + bchdsr1 = mmio_read_32(fmc2_base() + FMC2_BCHDSR1); + bchdsr2 = mmio_read_32(fmc2_base() + FMC2_BCHDSR2); + bchdsr3 = mmio_read_32(fmc2_base() + FMC2_BCHDSR3); + bchdsr4 = mmio_read_32(fmc2_base() + FMC2_BCHDSR4); + + /* Disable ECC */ + stm32_fmc2_set_ecc(false); + + /* No error found */ + if ((bchdsr0 & FMC2_BCHDSR0_DEF) == 0U) { + return 0; + } + + /* Too many errors detected */ + if ((bchdsr0 & FMC2_BCHDSR0_DUE) != 0U) { + return -EBADMSG; + } + + pos[0] = bchdsr1 & FMC2_BCHDSR1_EBP1_MASK; + pos[1] = (bchdsr1 & FMC2_BCHDSR1_EBP2_MASK) >> FMC2_BCHDSR1_EBP2_SHIFT; + pos[2] = bchdsr2 & FMC2_BCHDSR2_EBP3_MASK; + pos[3] = (bchdsr2 & FMC2_BCHDSR2_EBP4_MASK) >> FMC2_BCHDSR2_EBP4_SHIFT; + pos[4] = bchdsr3 & FMC2_BCHDSR3_EBP5_MASK; + pos[5] = (bchdsr3 & FMC2_BCHDSR3_EBP6_MASK) >> FMC2_BCHDSR3_EBP6_SHIFT; + pos[6] = bchdsr4 & FMC2_BCHDSR4_EBP7_MASK; + pos[7] = (bchdsr4 & FMC2_BCHDSR4_EBP8_MASK) >> FMC2_BCHDSR4_EBP8_SHIFT; + + den = (bchdsr0 & FMC2_BCHDSR0_DEN_MASK) >> FMC2_BCHDSR0_DEN_SHIFT; + for (i = 0; i < den; i++) { + if (pos[i] < (eccsize * 8U)) { + uint8_t bitmask = BIT(pos[i] % 8U); + uint32_t offset = pos[i] / 8U; + + *(buffer + offset) ^= bitmask; + } + } + + return 0; +} + +static void stm32_fmc2_hwctl(struct nand_device *nand) +{ + stm32_fmc2_set_ecc(false); + + if (nand->ecc.max_bit_corr != FMC2_ECC_HAM) { + mmio_clrbits_32(fmc2_base() + FMC2_PCR, FMC2_PCR_WEN); + } + + stm32_fmc2_set_ecc(true); +} + +static int stm32_fmc2_read_page(struct nand_device *nand, + unsigned int page, uintptr_t buffer) +{ + unsigned int eccsize = nand->ecc.size; + unsigned int eccbytes = nand->ecc.bytes; + unsigned int eccsteps = nand->page_size / eccsize; + uint8_t ecc_corr[FMC2_MAX_ECC_BYTES]; + uint8_t ecc_cal[FMC2_MAX_ECC_BYTES] = {0U}; + uint8_t *p; + unsigned int i; + unsigned int s; + int ret; + + VERBOSE(">%s page %i buffer %lx\n", __func__, page, buffer); + + ret = nand_read_page_cmd(page, 0U, 0U, 0U); + if (ret != 0) { + return ret; + } + + for (s = 0U, i = nand->page_size + FMC2_BBM_LEN, p = (uint8_t *)buffer; + s < eccsteps; + s++, i += eccbytes, p += eccsize) { + stm32_fmc2_hwctl(nand); + + /* Read the NAND page sector (512 bytes) */ + ret = nand_change_read_column_cmd(s * eccsize, (uintptr_t)p, + eccsize); + if (ret != 0) { + return ret; + } + + if (nand->ecc.max_bit_corr == FMC2_ECC_HAM) { + ret = stm32_fmc2_ham_calculate(p, ecc_cal); + if (ret != 0) { + return ret; + } + } + + /* Read the corresponding ECC bytes */ + ret = nand_change_read_column_cmd(i, (uintptr_t)ecc_corr, + eccbytes); + if (ret != 0) { + return ret; + } + + /* Correct the data */ + if (nand->ecc.max_bit_corr == FMC2_ECC_HAM) { + ret = stm32_fmc2_ham_correct(p, ecc_corr, ecc_cal); + } else { + ret = stm32_fmc2_bch_correct(p, eccsize); + } + + if (ret != 0) { + return ret; + } + } + + return 0; +} + +static void stm32_fmc2_read_data(struct nand_device *nand, + uint8_t *buff, unsigned int length, + bool use_bus8) +{ + uintptr_t data_base = stm32_fmc2.cs[stm32_fmc2.cs_sel].data_base; + + if (use_bus8 && (nand->buswidth == NAND_BUS_WIDTH_16)) { + stm32_fmc2_set_buswidth_16(false); + } + + if ((((uintptr_t)buff & BIT(0)) != 0U) && (length != 0U)) { + *buff = mmio_read_8(data_base); + buff += sizeof(uint8_t); + length -= sizeof(uint8_t); + } + + if ((((uintptr_t)buff & GENMASK_32(1, 0)) != 0U) && + (length >= sizeof(uint16_t))) { + *(uint16_t *)buff = mmio_read_16(data_base); + buff += sizeof(uint16_t); + length -= sizeof(uint16_t); + } + + /* 32bit aligned */ + while (length >= sizeof(uint32_t)) { + *(uint32_t *)buff = mmio_read_32(data_base); + buff += sizeof(uint32_t); + length -= sizeof(uint32_t); + } + + /* Read remaining bytes */ + if (length >= sizeof(uint16_t)) { + *(uint16_t *)buff = mmio_read_16(data_base); + buff += sizeof(uint16_t); + length -= sizeof(uint16_t); + } + + if (length != 0U) { + *buff = mmio_read_8(data_base); + } + + if (use_bus8 && (nand->buswidth == NAND_BUS_WIDTH_16)) { + /* Reconfigure bus width to 16-bit */ + stm32_fmc2_set_buswidth_16(true); + } +} + +static void stm32_fmc2_write_data(struct nand_device *nand, + uint8_t *buff, unsigned int length, + bool use_bus8) +{ + uintptr_t data_base = stm32_fmc2.cs[stm32_fmc2.cs_sel].data_base; + + if (use_bus8 && (nand->buswidth == NAND_BUS_WIDTH_16)) { + /* Reconfigure bus width to 8-bit */ + stm32_fmc2_set_buswidth_16(false); + } + + if ((((uintptr_t)buff & BIT(0)) != 0U) && (length != 0U)) { + mmio_write_8(data_base, *buff); + buff += sizeof(uint8_t); + length -= sizeof(uint8_t); + } + + if ((((uintptr_t)buff & GENMASK_32(1, 0)) != 0U) && + (length >= sizeof(uint16_t))) { + mmio_write_16(data_base, *(uint16_t *)buff); + buff += sizeof(uint16_t); + length -= sizeof(uint16_t); + } + + /* 32bits aligned */ + while (length >= sizeof(uint32_t)) { + mmio_write_32(data_base, *(uint32_t *)buff); + buff += sizeof(uint32_t); + length -= sizeof(uint32_t); + } + + /* Read remaining bytes */ + if (length >= sizeof(uint16_t)) { + mmio_write_16(data_base, *(uint16_t *)buff); + buff += sizeof(uint16_t); + length -= sizeof(uint16_t); + } + + if (length != 0U) { + mmio_write_8(data_base, *buff); + } + + if (use_bus8 && (nand->buswidth == NAND_BUS_WIDTH_16)) { + /* Reconfigure bus width to 16-bit */ + stm32_fmc2_set_buswidth_16(true); + } +} + +static void stm32_fmc2_ctrl_init(void) +{ + uint32_t pcr = mmio_read_32(fmc2_base() + FMC2_PCR); + uint32_t bcr1 = mmio_read_32(fmc2_base() + FMC2_BCR1); + + /* Enable wait feature and NAND flash memory bank */ + pcr |= FMC2_PCR_PWAITEN; + pcr |= FMC2_PCR_PBKEN; + + /* Set buswidth to 8 bits mode for identification */ + pcr &= ~FMC2_PCR_PWID_MASK; + + /* ECC logic is disabled */ + pcr &= ~FMC2_PCR_ECCEN; + + /* Default mode */ + pcr &= ~FMC2_PCR_ECCALG; + pcr &= ~FMC2_PCR_BCHECC; + pcr &= ~FMC2_PCR_WEN; + + /* Set default ECC sector size */ + pcr &= ~FMC2_PCR_ECCSS_MASK; + pcr |= FMC2_PCR_ECCSS(FMC2_PCR_ECCSS_2048); + + /* Set default TCLR/TAR timings */ + pcr &= ~FMC2_PCR_TCLR_MASK; + pcr |= FMC2_PCR_TCLR(FMC2_PCR_TCLR_DEFAULT); + pcr &= ~FMC2_PCR_TAR_MASK; + pcr |= FMC2_PCR_TAR(FMC2_PCR_TAR_DEFAULT); + + /* Enable FMC2 controller */ + bcr1 |= FMC2_BCR1_FMC2EN; + + mmio_write_32(fmc2_base() + FMC2_BCR1, bcr1); + mmio_write_32(fmc2_base() + FMC2_PCR, pcr); + mmio_write_32(fmc2_base() + FMC2_PMEM, FMC2_PMEM_DEFAULT); + mmio_write_32(fmc2_base() + FMC2_PATT, FMC2_PATT_DEFAULT); +} + +static int stm32_fmc2_exec(struct nand_req *req) +{ + int ret = 0; + + switch (req->type & NAND_REQ_MASK) { + case NAND_REQ_CMD: + VERBOSE("Write CMD %x\n", (uint8_t)req->type); + mmio_write_8(stm32_fmc2.cs[stm32_fmc2.cs_sel].cmd_base, + (uint8_t)req->type); + break; + case NAND_REQ_ADDR: + VERBOSE("Write ADDR %x\n", *(req->addr)); + mmio_write_8(stm32_fmc2.cs[stm32_fmc2.cs_sel].addr_base, + *(req->addr)); + break; + case NAND_REQ_DATAIN: + VERBOSE("Read data\n"); + stm32_fmc2_read_data(req->nand, req->addr, req->length, + ((req->type & NAND_REQ_BUS_WIDTH_8) != + 0U)); + break; + case NAND_REQ_DATAOUT: + VERBOSE("Write data\n"); + stm32_fmc2_write_data(req->nand, req->addr, req->length, + ((req->type & NAND_REQ_BUS_WIDTH_8) != + 0U)); + break; + case NAND_REQ_WAIT: + VERBOSE("WAIT Ready\n"); + ret = nand_wait_ready(req->delay_ms); + break; + default: + ret = -EINVAL; + break; + }; + + return ret; +} + +static void stm32_fmc2_setup(struct nand_device *nand) +{ + uint32_t pcr = mmio_read_32(fmc2_base() + FMC2_PCR); + + /* Set buswidth */ + pcr &= ~FMC2_PCR_PWID_MASK; + if (nand->buswidth == NAND_BUS_WIDTH_16) { + pcr |= FMC2_PCR_PWID(FMC2_PCR_PWID_16); + } + + if (nand->ecc.mode == NAND_ECC_HW) { + nand->mtd_read_page = stm32_fmc2_read_page; + + pcr &= ~FMC2_PCR_ECCALG; + pcr &= ~FMC2_PCR_BCHECC; + + pcr &= ~FMC2_PCR_ECCSS_MASK; + pcr |= FMC2_PCR_ECCSS(FMC2_PCR_ECCSS_512); + + switch (nand->ecc.max_bit_corr) { + case FMC2_ECC_HAM: + nand->ecc.bytes = 3; + break; + case FMC2_ECC_BCH8: + pcr |= FMC2_PCR_ECCALG; + pcr |= FMC2_PCR_BCHECC; + nand->ecc.bytes = 13; + break; + default: + /* Use FMC2 ECC BCH4 */ + pcr |= FMC2_PCR_ECCALG; + nand->ecc.bytes = 7; + break; + } + + if ((nand->buswidth & NAND_BUS_WIDTH_16) != 0) { + nand->ecc.bytes++; + } + } + + mmio_write_32(stm32_fmc2.reg_base + FMC2_PCR, pcr); +} + +static const struct nand_ctrl_ops ctrl_ops = { + .setup = stm32_fmc2_setup, + .exec = stm32_fmc2_exec +}; + +int stm32_fmc2_init(void) +{ + int fmc_node; + int fmc_subnode = 0; + int nchips = 0; + unsigned int i; + void *fdt = NULL; + const fdt32_t *cuint; + struct dt_node_info info; + + if (fdt_get_address(&fdt) == 0) { + return -FDT_ERR_NOTFOUND; + } + + fmc_node = dt_get_node(&info, -1, DT_FMC2_COMPAT); + if (fmc_node == -FDT_ERR_NOTFOUND) { + WARN("No FMC2 node found\n"); + return fmc_node; + } + + if (info.status == DT_DISABLED) { + return -FDT_ERR_NOTFOUND; + } + + stm32_fmc2.reg_base = info.base; + + if ((info.clock < 0) || (info.reset < 0)) { + return -FDT_ERR_BADVALUE; + } + + stm32_fmc2.clock_id = (unsigned long)info.clock; + stm32_fmc2.reset_id = (unsigned int)info.reset; + + cuint = fdt_getprop(fdt, fmc_node, "reg", NULL); + if (cuint == NULL) { + return -FDT_ERR_BADVALUE; + } + + cuint += 2; + + for (i = 0U; i < MAX_CS; i++) { + stm32_fmc2.cs[i].data_base = fdt32_to_cpu(*cuint); + stm32_fmc2.cs[i].cmd_base = fdt32_to_cpu(*(cuint + 2)); + stm32_fmc2.cs[i].addr_base = fdt32_to_cpu(*(cuint + 4)); + cuint += 6; + } + + /* Pinctrl initialization */ + if (dt_set_pinctrl_config(fmc_node) != 0) { + return -FDT_ERR_BADVALUE; + } + + /* Parse flash nodes */ + fdt_for_each_subnode(fmc_subnode, fdt, fmc_node) { + nchips++; + } + + if (nchips != 1) { + WARN("Only one SLC NAND device supported\n"); + return -FDT_ERR_BADVALUE; + } + + fdt_for_each_subnode(fmc_subnode, fdt, fmc_node) { + /* Get chip select */ + cuint = fdt_getprop(fdt, fmc_subnode, "reg", NULL); + if (cuint == NULL) { + WARN("Chip select not well defined\n"); + return -FDT_ERR_BADVALUE; + } + stm32_fmc2.cs_sel = fdt32_to_cpu(*cuint); + VERBOSE("NAND CS %i\n", stm32_fmc2.cs_sel); + } + + /* Enable Clock */ + stm32mp_clk_enable(stm32_fmc2.clock_id); + + /* Reset IP */ + stm32mp_reset_assert(stm32_fmc2.reset_id); + stm32mp_reset_deassert(stm32_fmc2.reset_id); + + /* Setup default IP registers */ + stm32_fmc2_ctrl_init(); + + /* Setup default timings */ + stm32_fmc2_nand_setup_timing(); + + /* Init NAND RAW framework */ + nand_raw_ctrl_init(&ctrl_ops); + + return 0; +} diff --git a/drivers/st/io/io_mmc.c b/drivers/st/io/io_mmc.c index a239b5f3ad..44b7d19074 100644 --- a/drivers/st/io/io_mmc.c +++ b/drivers/st/io/io_mmc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2019, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -20,14 +20,15 @@ static int mmc_dev_open(const uintptr_t init_params, io_dev_info_t **dev_info); static int mmc_block_open(io_dev_info_t *dev_info, const uintptr_t spec, io_entity_t *entity); static int mmc_dev_init(io_dev_info_t *dev_info, const uintptr_t init_params); -static int mmc_block_seek(io_entity_t *entity, int mode, ssize_t offset); +static int mmc_block_seek(io_entity_t *entity, int mode, + signed long long offset); static int mmc_block_read(io_entity_t *entity, uintptr_t buffer, size_t length, size_t *length_read); static int mmc_block_close(io_entity_t *entity); static int mmc_dev_close(io_dev_info_t *dev_info); static io_type_t device_type_mmc(void); -static ssize_t seek_offset; +static signed long long seek_offset; static const io_dev_connector_t mmc_dev_connector = { .dev_open = mmc_dev_open @@ -85,7 +86,8 @@ static int mmc_block_open(io_dev_info_t *dev_info, const uintptr_t spec, } /* Seek to a particular file offset on the mmc device */ -static int mmc_block_seek(io_entity_t *entity, int mode, ssize_t offset) +static int mmc_block_seek(io_entity_t *entity, int mode, + signed long long offset) { seek_offset = offset; return 0; diff --git a/drivers/st/spi/stm32_qspi.c b/drivers/st/spi/stm32_qspi.c new file mode 100644 index 0000000000..188d2ff806 --- /dev/null +++ b/drivers/st/spi/stm32_qspi.c @@ -0,0 +1,500 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause + */ + +#include <libfdt.h> + +#include <platform_def.h> + +#include <common/debug.h> +#include <drivers/delay_timer.h> +#include <drivers/spi_mem.h> +#include <drivers/st/stm32_gpio.h> +#include <drivers/st/stm32mp_reset.h> +#include <lib/mmio.h> +#include <lib/utils_def.h> + +/* QUADSPI registers */ +#define QSPI_CR 0x00U +#define QSPI_DCR 0x04U +#define QSPI_SR 0x08U +#define QSPI_FCR 0x0CU +#define QSPI_DLR 0x10U +#define QSPI_CCR 0x14U +#define QSPI_AR 0x18U +#define QSPI_ABR 0x1CU +#define QSPI_DR 0x20U +#define QSPI_PSMKR 0x24U +#define QSPI_PSMAR 0x28U +#define QSPI_PIR 0x2CU +#define QSPI_LPTR 0x30U + +/* QUADSPI control register */ +#define QSPI_CR_EN BIT(0) +#define QSPI_CR_ABORT BIT(1) +#define QSPI_CR_DMAEN BIT(2) +#define QSPI_CR_TCEN BIT(3) +#define QSPI_CR_SSHIFT BIT(4) +#define QSPI_CR_DFM BIT(6) +#define QSPI_CR_FSEL BIT(7) +#define QSPI_CR_FTHRES_SHIFT 8U +#define QSPI_CR_TEIE BIT(16) +#define QSPI_CR_TCIE BIT(17) +#define QSPI_CR_FTIE BIT(18) +#define QSPI_CR_SMIE BIT(19) +#define QSPI_CR_TOIE BIT(20) +#define QSPI_CR_APMS BIT(22) +#define QSPI_CR_PMM BIT(23) +#define QSPI_CR_PRESCALER_MASK GENMASK_32(31, 24) +#define QSPI_CR_PRESCALER_SHIFT 24U + +/* QUADSPI device configuration register */ +#define QSPI_DCR_CKMODE BIT(0) +#define QSPI_DCR_CSHT_MASK GENMASK_32(10, 8) +#define QSPI_DCR_CSHT_SHIFT 8U +#define QSPI_DCR_FSIZE_MASK GENMASK_32(20, 16) +#define QSPI_DCR_FSIZE_SHIFT 16U + +/* QUADSPI status register */ +#define QSPI_SR_TEF BIT(0) +#define QSPI_SR_TCF BIT(1) +#define QSPI_SR_FTF BIT(2) +#define QSPI_SR_SMF BIT(3) +#define QSPI_SR_TOF BIT(4) +#define QSPI_SR_BUSY BIT(5) + +/* QUADSPI flag clear register */ +#define QSPI_FCR_CTEF BIT(0) +#define QSPI_FCR_CTCF BIT(1) +#define QSPI_FCR_CSMF BIT(3) +#define QSPI_FCR_CTOF BIT(4) + +/* QUADSPI communication configuration register */ +#define QSPI_CCR_DDRM BIT(31) +#define QSPI_CCR_DHHC BIT(30) +#define QSPI_CCR_SIOO BIT(28) +#define QSPI_CCR_FMODE_SHIFT 26U +#define QSPI_CCR_DMODE_SHIFT 24U +#define QSPI_CCR_DCYC_SHIFT 18U +#define QSPI_CCR_ABSIZE_SHIFT 16U +#define QSPI_CCR_ABMODE_SHIFT 14U +#define QSPI_CCR_ADSIZE_SHIFT 12U +#define QSPI_CCR_ADMODE_SHIFT 10U +#define QSPI_CCR_IMODE_SHIFT 8U +#define QSPI_CCR_IND_WRITE 0U +#define QSPI_CCR_IND_READ 1U +#define QSPI_CCR_MEM_MAP 3U + +#define QSPI_MAX_CHIP 2U + +#define QSPI_FIFO_TIMEOUT_US 30U +#define QSPI_CMD_TIMEOUT_US 1000U +#define QSPI_BUSY_TIMEOUT_US 100U +#define QSPI_ABT_TIMEOUT_US 100U + +#define DT_QSPI_COMPAT "st,stm32f469-qspi" + +#define FREQ_100MHZ 100000000U + +struct stm32_qspi_ctrl { + uintptr_t reg_base; + uintptr_t mm_base; + size_t mm_size; + unsigned long clock_id; + unsigned int reset_id; +}; + +static struct stm32_qspi_ctrl stm32_qspi; + +static uintptr_t qspi_base(void) +{ + return stm32_qspi.reg_base; +} + +static int stm32_qspi_wait_for_not_busy(void) +{ + uint64_t timeout = timeout_init_us(QSPI_BUSY_TIMEOUT_US); + + while ((mmio_read_32(qspi_base() + QSPI_SR) & QSPI_SR_BUSY) != 0U) { + if (timeout_elapsed(timeout)) { + ERROR("%s: busy timeout\n", __func__); + return -ETIMEDOUT; + } + } + + return 0; +} + +static int stm32_qspi_wait_cmd(const struct spi_mem_op *op) +{ + int ret = 0; + uint64_t timeout; + + if (op->data.nbytes == 0U) { + return stm32_qspi_wait_for_not_busy(); + } + + timeout = timeout_init_us(QSPI_CMD_TIMEOUT_US); + while ((mmio_read_32(qspi_base() + QSPI_SR) & QSPI_SR_TCF) == 0U) { + if (timeout_elapsed(timeout)) { + ret = -ETIMEDOUT; + break; + } + } + + if (ret == 0) { + if ((mmio_read_32(qspi_base() + QSPI_SR) & QSPI_SR_TEF) != 0U) { + ERROR("%s: transfer error\n", __func__); + ret = -EIO; + } + } else { + ERROR("%s: cmd timeout\n", __func__); + } + + /* Clear flags */ + mmio_write_32(qspi_base() + QSPI_FCR, QSPI_FCR_CTCF | QSPI_FCR_CTEF); + + return ret; +} + +static void stm32_qspi_read_fifo(uint8_t *val, uintptr_t addr) +{ + *val = mmio_read_8(addr); +} + +static void stm32_qspi_write_fifo(uint8_t *val, uintptr_t addr) +{ + mmio_write_8(addr, *val); +} + +static int stm32_qspi_poll(const struct spi_mem_op *op) +{ + void (*fifo)(uint8_t *val, uintptr_t addr); + uint32_t len = op->data.nbytes; + uint8_t *buf; + uint64_t timeout; + + if (op->data.dir == SPI_MEM_DATA_IN) { + fifo = stm32_qspi_read_fifo; + } else { + fifo = stm32_qspi_write_fifo; + } + + buf = (uint8_t *)op->data.buf; + + for (len = op->data.nbytes; len != 0U; len--) { + timeout = timeout_init_us(QSPI_FIFO_TIMEOUT_US); + while ((mmio_read_32(qspi_base() + QSPI_SR) & + QSPI_SR_FTF) == 0U) { + if (timeout_elapsed(timeout)) { + ERROR("%s: fifo timeout\n", __func__); + return -ETIMEDOUT; + } + } + + fifo(buf++, qspi_base() + QSPI_DR); + } + + return 0; +} + +static int stm32_qspi_mm(const struct spi_mem_op *op) +{ + memcpy(op->data.buf, + (void *)(stm32_qspi.mm_base + (size_t)op->addr.val), + op->data.nbytes); + + return 0; +} + +static int stm32_qspi_tx(const struct spi_mem_op *op, uint8_t mode) +{ + if (op->data.nbytes == 0U) { + return 0; + } + + if (mode == QSPI_CCR_MEM_MAP) { + return stm32_qspi_mm(op); + } + + return stm32_qspi_poll(op); +} + +static unsigned int stm32_qspi_get_mode(uint8_t buswidth) +{ + if (buswidth == 4U) { + return 3U; + } + + return buswidth; +} + +static int stm32_qspi_exec_op(const struct spi_mem_op *op) +{ + uint64_t timeout; + uint32_t ccr; + size_t addr_max; + uint8_t mode = QSPI_CCR_IND_WRITE; + int ret; + + VERBOSE("%s: cmd:%x mode:%d.%d.%d.%d addr:%llx len:%x\n", + __func__, op->cmd.opcode, op->cmd.buswidth, op->addr.buswidth, + op->dummy.buswidth, op->data.buswidth, + op->addr.val, op->data.nbytes); + + ret = stm32_qspi_wait_for_not_busy(); + if (ret != 0) { + return ret; + } + + addr_max = op->addr.val + op->data.nbytes + 1U; + + if ((op->data.dir == SPI_MEM_DATA_IN) && (op->data.nbytes != 0U)) { + if ((addr_max < stm32_qspi.mm_size) && + (op->addr.buswidth != 0U)) { + mode = QSPI_CCR_MEM_MAP; + } else { + mode = QSPI_CCR_IND_READ; + } + } + + if (op->data.nbytes != 0U) { + mmio_write_32(qspi_base() + QSPI_DLR, op->data.nbytes - 1U); + } + + ccr = mode << QSPI_CCR_FMODE_SHIFT; + ccr |= op->cmd.opcode; + ccr |= stm32_qspi_get_mode(op->cmd.buswidth) << QSPI_CCR_IMODE_SHIFT; + + if (op->addr.nbytes != 0U) { + ccr |= (op->addr.nbytes - 1U) << QSPI_CCR_ADSIZE_SHIFT; + ccr |= stm32_qspi_get_mode(op->addr.buswidth) << + QSPI_CCR_ADMODE_SHIFT; + } + + if ((op->dummy.buswidth != 0U) && (op->dummy.nbytes != 0U)) { + ccr |= (op->dummy.nbytes * 8U / op->dummy.buswidth) << + QSPI_CCR_DCYC_SHIFT; + } + + if (op->data.nbytes != 0U) { + ccr |= stm32_qspi_get_mode(op->data.buswidth) << + QSPI_CCR_DMODE_SHIFT; + } + + mmio_write_32(qspi_base() + QSPI_CCR, ccr); + + if ((op->addr.nbytes != 0U) && (mode != QSPI_CCR_MEM_MAP)) { + mmio_write_32(qspi_base() + QSPI_AR, op->addr.val); + } + + ret = stm32_qspi_tx(op, mode); + + /* + * Abort in: + * - Error case. + * - Memory mapped read: prefetching must be stopped if we read the last + * byte of device (device size - fifo size). If device size is not + * known then prefetching is always stopped. + */ + if ((ret != 0) || (mode == QSPI_CCR_MEM_MAP)) { + goto abort; + } + + /* Wait end of TX in indirect mode */ + ret = stm32_qspi_wait_cmd(op); + if (ret != 0) { + goto abort; + } + + return 0; + +abort: + mmio_setbits_32(qspi_base() + QSPI_CR, QSPI_CR_ABORT); + + /* Wait clear of abort bit by hardware */ + timeout = timeout_init_us(QSPI_ABT_TIMEOUT_US); + while ((mmio_read_32(qspi_base() + QSPI_CR) & QSPI_CR_ABORT) != 0U) { + if (timeout_elapsed(timeout)) { + ret = -ETIMEDOUT; + break; + } + } + + mmio_write_32(qspi_base() + QSPI_FCR, QSPI_FCR_CTCF); + + if (ret != 0) { + ERROR("%s: exec op error\n", __func__); + } + + return ret; +} + +static int stm32_qspi_claim_bus(unsigned int cs) +{ + uint32_t cr; + + if (cs >= QSPI_MAX_CHIP) { + return -ENODEV; + } + + /* Set chip select and enable the controller */ + cr = QSPI_CR_EN; + if (cs == 1U) { + cr |= QSPI_CR_FSEL; + } + + mmio_clrsetbits_32(qspi_base() + QSPI_CR, QSPI_CR_FSEL, cr); + + return 0; +} + +static void stm32_qspi_release_bus(void) +{ + mmio_clrbits_32(qspi_base() + QSPI_CR, QSPI_CR_EN); +} + +static int stm32_qspi_set_speed(unsigned int hz) +{ + unsigned long qspi_clk = stm32mp_clk_get_rate(stm32_qspi.clock_id); + uint32_t prescaler = UINT8_MAX; + uint32_t csht; + int ret; + + if (qspi_clk == 0U) { + return -EINVAL; + } + + if (hz > 0U) { + prescaler = div_round_up(qspi_clk, hz) - 1U; + if (prescaler > UINT8_MAX) { + prescaler = UINT8_MAX; + } + } + + csht = div_round_up((5U * qspi_clk) / (prescaler + 1U), FREQ_100MHZ); + csht = ((csht - 1U) << QSPI_DCR_CSHT_SHIFT) & QSPI_DCR_CSHT_MASK; + + ret = stm32_qspi_wait_for_not_busy(); + if (ret != 0) { + return ret; + } + + mmio_clrsetbits_32(qspi_base() + QSPI_CR, QSPI_CR_PRESCALER_MASK, + prescaler << QSPI_CR_PRESCALER_SHIFT); + + mmio_clrsetbits_32(qspi_base() + QSPI_DCR, QSPI_DCR_CSHT_MASK, csht); + + VERBOSE("%s: speed=%lu\n", __func__, qspi_clk / (prescaler + 1U)); + + return 0; +} + +static int stm32_qspi_set_mode(unsigned int mode) +{ + int ret; + + ret = stm32_qspi_wait_for_not_busy(); + if (ret != 0) { + return ret; + } + + if ((mode & SPI_CS_HIGH) != 0U) { + return -ENODEV; + } + + if (((mode & SPI_CPHA) != 0U) && ((mode & SPI_CPOL) != 0U)) { + mmio_setbits_32(qspi_base() + QSPI_DCR, QSPI_DCR_CKMODE); + } else if (((mode & SPI_CPHA) == 0U) && ((mode & SPI_CPOL) == 0U)) { + mmio_clrbits_32(qspi_base() + QSPI_DCR, QSPI_DCR_CKMODE); + } else { + return -ENODEV; + } + + VERBOSE("%s: mode=0x%x\n", __func__, mode); + + if ((mode & SPI_RX_QUAD) != 0U) { + VERBOSE("rx: quad\n"); + } else if ((mode & SPI_RX_DUAL) != 0U) { + VERBOSE("rx: dual\n"); + } else { + VERBOSE("rx: single\n"); + } + + if ((mode & SPI_TX_QUAD) != 0U) { + VERBOSE("tx: quad\n"); + } else if ((mode & SPI_TX_DUAL) != 0U) { + VERBOSE("tx: dual\n"); + } else { + VERBOSE("tx: single\n"); + } + + return 0; +} + +static const struct spi_bus_ops stm32_qspi_bus_ops = { + .claim_bus = stm32_qspi_claim_bus, + .release_bus = stm32_qspi_release_bus, + .set_speed = stm32_qspi_set_speed, + .set_mode = stm32_qspi_set_mode, + .exec_op = stm32_qspi_exec_op, +}; + +int stm32_qspi_init(void) +{ + size_t size; + int qspi_node; + struct dt_node_info info; + void *fdt = NULL; + int ret; + + if (fdt_get_address(&fdt) == 0) { + return -FDT_ERR_NOTFOUND; + } + + qspi_node = dt_get_node(&info, -1, DT_QSPI_COMPAT); + if (qspi_node < 0) { + ERROR("No QSPI ctrl found\n"); + return -FDT_ERR_NOTFOUND; + } + + if (info.status == DT_DISABLED) { + return -FDT_ERR_NOTFOUND; + } + + ret = fdt_get_reg_props_by_name(qspi_node, "qspi", + &stm32_qspi.reg_base, &size); + if (ret != 0) { + return ret; + } + + ret = fdt_get_reg_props_by_name(qspi_node, "qspi_mm", + &stm32_qspi.mm_base, + &stm32_qspi.mm_size); + if (ret != 0) { + return ret; + } + + if (dt_set_pinctrl_config(qspi_node) != 0) { + return -FDT_ERR_BADVALUE; + } + + if ((info.clock < 0) || (info.reset < 0)) { + return -FDT_ERR_BADVALUE; + } + + stm32_qspi.clock_id = (unsigned long)info.clock; + stm32_qspi.reset_id = (unsigned int)info.reset; + + stm32mp_clk_enable(stm32_qspi.clock_id); + + stm32mp_reset_assert(stm32_qspi.reset_id); + stm32mp_reset_deassert(stm32_qspi.reset_id); + + mmio_write_32(qspi_base() + QSPI_CR, QSPI_CR_SSHIFT); + mmio_write_32(qspi_base() + QSPI_DCR, QSPI_DCR_FSIZE_MASK); + + return spi_mem_init_slave(fdt, qspi_node, &stm32_qspi_bus_ops); +}; diff --git a/fdts/fvp-base-gicv2-psci-aarch32.dts b/fdts/fvp-base-gicv2-psci-aarch32.dts index 87ac68d67c..e71a39519d 100644 --- a/fdts/fvp-base-gicv2-psci-aarch32.dts +++ b/fdts/fvp-base-gicv2-psci-aarch32.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -279,7 +279,7 @@ <0 0 41 &gic 0 41 4>, <0 0 42 &gic 0 42 4>; - /include/ "rtsm_ve-motherboard-aarch32.dtsi" + #include "rtsm_ve-motherboard-aarch32.dtsi" }; panels { diff --git a/fdts/fvp-base-gicv2-psci.dts b/fdts/fvp-base-gicv2-psci.dts index 941040d8e7..c9c9d95947 100644 --- a/fdts/fvp-base-gicv2-psci.dts +++ b/fdts/fvp-base-gicv2-psci.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -233,7 +233,7 @@ <4 0 0 0x0c000000 0x04000000>, <5 0 0 0x10000000 0x04000000>; - /include/ "rtsm_ve-motherboard.dtsi" + #include "rtsm_ve-motherboard.dtsi" }; panels { diff --git a/fdts/fvp-base-gicv3-psci-1t.dts b/fdts/fvp-base-gicv3-psci-1t.dts index 36fbd44442..3c82f7b138 100644 --- a/fdts/fvp-base-gicv3-psci-1t.dts +++ b/fdts/fvp-base-gicv3-psci-1t.dts @@ -1,12 +1,12 @@ /* - * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ /dts-v1/; -/include/ "fvp-base-gicv3-psci-common.dtsi" +#include "fvp-base-gicv3-psci-common.dtsi" &CPU0 { reg = <0x0 0x0>; diff --git a/fdts/fvp-base-gicv3-psci-aarch32-1t.dts b/fdts/fvp-base-gicv3-psci-aarch32-1t.dts index f7c936210f..d1d33485c9 100644 --- a/fdts/fvp-base-gicv3-psci-aarch32-1t.dts +++ b/fdts/fvp-base-gicv3-psci-aarch32-1t.dts @@ -1,12 +1,12 @@ /* - * Copyright (c) 2019, Arm Limited. All rights reserved. + * Copyright (c) 2019-2020, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ /dts-v1/; -/include/ "fvp-base-gicv3-psci-aarch32-common.dtsi" +#include "fvp-base-gicv3-psci-aarch32-common.dtsi" &CPU0 { reg = <0x0>; diff --git a/fdts/fvp-base-gicv3-psci-aarch32-common.dtsi b/fdts/fvp-base-gicv3-psci-aarch32-common.dtsi index 6179c66e7d..f9809db8bd 100644 --- a/fdts/fvp-base-gicv3-psci-aarch32-common.dtsi +++ b/fdts/fvp-base-gicv3-psci-aarch32-common.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -286,7 +286,7 @@ <0 0 41 &gic 0 0 0 41 4>, <0 0 42 &gic 0 0 0 42 4>; - /include/ "rtsm_ve-motherboard-aarch32.dtsi" + #include "rtsm_ve-motherboard-aarch32.dtsi" }; panels { diff --git a/fdts/fvp-base-gicv3-psci-aarch32.dts b/fdts/fvp-base-gicv3-psci-aarch32.dts index 2833cdfaca..513014b324 100644 --- a/fdts/fvp-base-gicv3-psci-aarch32.dts +++ b/fdts/fvp-base-gicv3-psci-aarch32.dts @@ -1,9 +1,9 @@ /* - * Copyright (c) 2016-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2016-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ /dts-v1/; -/include/ "fvp-base-gicv3-psci-aarch32-common.dtsi" +#include "fvp-base-gicv3-psci-aarch32-common.dtsi" diff --git a/fdts/fvp-base-gicv3-psci-common.dtsi b/fdts/fvp-base-gicv3-psci-common.dtsi index 631c4e3453..94ed67d558 100644 --- a/fdts/fvp-base-gicv3-psci-common.dtsi +++ b/fdts/fvp-base-gicv3-psci-common.dtsi @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -240,7 +240,7 @@ <4 0 0 0x0c000000 0x04000000>, <5 0 0 0x10000000 0x04000000>; - /include/ "rtsm_ve-motherboard.dtsi" + #include "rtsm_ve-motherboard.dtsi" }; panels { diff --git a/fdts/fvp-base-gicv3-psci-dynamiq-2t.dts b/fdts/fvp-base-gicv3-psci-dynamiq-2t.dts index f35eaf4822..48269a0655 100644 --- a/fdts/fvp-base-gicv3-psci-dynamiq-2t.dts +++ b/fdts/fvp-base-gicv3-psci-dynamiq-2t.dts @@ -1,12 +1,12 @@ /* - * Copyright (c) 2019, Arm Limited. All rights reserved. + * Copyright (c) 2019-2020, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ /dts-v1/; -/include/ "fvp-base-gicv3-psci-common.dtsi" +#include "fvp-base-gicv3-psci-common.dtsi" &CPU0 { reg = <0x0 0x0>; diff --git a/fdts/fvp-base-gicv3-psci-dynamiq.dts b/fdts/fvp-base-gicv3-psci-dynamiq.dts index 614c5d5c01..51c7acacf0 100644 --- a/fdts/fvp-base-gicv3-psci-dynamiq.dts +++ b/fdts/fvp-base-gicv3-psci-dynamiq.dts @@ -1,12 +1,12 @@ /* - * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ /dts-v1/; -/include/ "fvp-base-gicv3-psci-common.dtsi" +#include "fvp-base-gicv3-psci-common.dtsi" &CPU0 { reg = <0x0 0x0>; diff --git a/fdts/fvp-base-gicv3-psci.dts b/fdts/fvp-base-gicv3-psci.dts index 3ea429ce97..65fa4b0d94 100644 --- a/fdts/fvp-base-gicv3-psci.dts +++ b/fdts/fvp-base-gicv3-psci.dts @@ -1,9 +1,9 @@ /* - * Copyright (c) 2013-2017, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ /dts-v1/; -/include/ "fvp-base-gicv3-psci-common.dtsi" +#include "fvp-base-gicv3-psci-common.dtsi" diff --git a/fdts/fvp-foundation-gicv2-psci.dts b/fdts/fvp-foundation-gicv2-psci.dts index 03b61dd73b..b6da905492 100644 --- a/fdts/fvp-foundation-gicv2-psci.dts +++ b/fdts/fvp-foundation-gicv2-psci.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -182,6 +182,6 @@ <4 0 0 0x0c000000 0x04000000>, <5 0 0 0x10000000 0x04000000>; - /include/ "fvp-foundation-motherboard.dtsi" + #include "fvp-foundation-motherboard.dtsi" }; }; diff --git a/fdts/fvp-foundation-gicv3-psci.dts b/fdts/fvp-foundation-gicv3-psci.dts index 1488ed7f75..81071e255b 100644 --- a/fdts/fvp-foundation-gicv3-psci.dts +++ b/fdts/fvp-foundation-gicv3-psci.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2017, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -191,6 +191,6 @@ <4 0 0 0x0c000000 0x04000000>, <5 0 0 0x10000000 0x04000000>; - /include/ "fvp-foundation-motherboard.dtsi" + #include "fvp-foundation-motherboard.dtsi" }; }; diff --git a/fdts/fvp-ve-Cortex-A5x1.dts b/fdts/fvp-ve-Cortex-A5x1.dts index 0f766016d7..9d2d1d552d 100644 --- a/fdts/fvp-ve-Cortex-A5x1.dts +++ b/fdts/fvp-ve-Cortex-A5x1.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Arm Limited. All rights reserved. + * Copyright (c) 2019-2020, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -139,6 +139,6 @@ <0 0 5 &gic 0 5 4>, <0 0 42 &gic 0 42 4>; - /include/ "rtsm_ve-motherboard-aarch32.dtsi" + #include "rtsm_ve-motherboard-aarch32.dtsi" }; }; diff --git a/fdts/fvp-ve-Cortex-A7x1.dts b/fdts/fvp-ve-Cortex-A7x1.dts index fca3d90ab5..28de91d3c8 100644 --- a/fdts/fvp-ve-Cortex-A7x1.dts +++ b/fdts/fvp-ve-Cortex-A7x1.dts @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, Arm Limited. All rights reserved. + * Copyright (c) 2019-2020, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -71,6 +71,6 @@ <0 0 5 &gic 0 5 4>, <0 0 42 &gic 0 42 4>; - /include/ "rtsm_ve-motherboard-aarch32.dtsi" + #include "rtsm_ve-motherboard-aarch32.dtsi" }; }; diff --git a/fdts/stm32mp157-pinctrl.dtsi b/fdts/stm32mp157-pinctrl.dtsi index 8e480b2c19..7fd902bd2d 100644 --- a/fdts/stm32mp157-pinctrl.dtsi +++ b/fdts/stm32mp157-pinctrl.dtsi @@ -1,6 +1,6 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* - * Copyright (C) STMicroelectronics 2017 - All Rights Reserved + * Copyright (C) STMicroelectronics 2017-2019 - All Rights Reserved * Author: Ludovic Barre <ludovic.barre@st.com> for STMicroelectronics. */ #include <dt-bindings/pinctrl/stm32-pinfunc.h> @@ -135,6 +135,31 @@ status = "disabled"; }; + fmc_pins_a: fmc-0 { + pins1 { + pinmux = <STM32_PINMUX('D', 4, AF12)>, /* FMC_NOE */ + <STM32_PINMUX('D', 5, AF12)>, /* FMC_NWE */ + <STM32_PINMUX('D', 11, AF12)>, /* FMC_A16_FMC_CLE */ + <STM32_PINMUX('D', 12, AF12)>, /* FMC_A17_FMC_ALE */ + <STM32_PINMUX('D', 14, AF12)>, /* FMC_D0 */ + <STM32_PINMUX('D', 15, AF12)>, /* FMC_D1 */ + <STM32_PINMUX('D', 0, AF12)>, /* FMC_D2 */ + <STM32_PINMUX('D', 1, AF12)>, /* FMC_D3 */ + <STM32_PINMUX('E', 7, AF12)>, /* FMC_D4 */ + <STM32_PINMUX('E', 8, AF12)>, /* FMC_D5 */ + <STM32_PINMUX('E', 9, AF12)>, /* FMC_D6 */ + <STM32_PINMUX('E', 10, AF12)>, /* FMC_D7 */ + <STM32_PINMUX('G', 9, AF12)>; /* FMC_NE2_FMC_NCE */ + bias-disable; + drive-push-pull; + slew-rate = <1>; + }; + pins2 { + pinmux = <STM32_PINMUX('D', 6, AF12)>; /* FMC_NWAIT */ + bias-pull-up; + }; + }; + qspi_bk1_pins_a: qspi-bk1-0 { pins1 { pinmux = <STM32_PINMUX('F', 8, AF10)>, /* QSPI_BK1_IO0 */ diff --git a/fdts/stm32mp157c-ev1.dts b/fdts/stm32mp157c-ev1.dts index cfde8ed90e..50c0b939b3 100644 --- a/fdts/stm32mp157c-ev1.dts +++ b/fdts/stm32mp157c-ev1.dts @@ -1,6 +1,6 @@ // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* - * Copyright (C) STMicroelectronics 2017 - All Rights Reserved + * Copyright (C) STMicroelectronics 2017-2019 - All Rights Reserved * Author: Ludovic Barre <ludovic.barre@st.com> for STMicroelectronics. */ /dts-v1/; @@ -21,21 +21,20 @@ }; &fmc { + pinctrl-names = "default"; + pinctrl-0 = <&fmc_pins_a>; status = "okay"; #address-cells = <1>; #size-cells = <0>; nand: nand@0 { reg = <0>; - nand-on-flash-bbt; - #address-cells = <1>; - #size-cells = <1>; }; }; &qspi { pinctrl-names = "default"; - pinctrl-0 = <&qspi_clk_pins_a &qspi_bk1_pins_a &qspi_bk2_pins_a>; + pinctrl-0 = <&qspi_clk_pins_a &qspi_bk1_pins_a>; reg = <0x58003000 0x1000>, <0x70000000 0x4000000>; #address-cells = <1>; #size-cells = <0>; @@ -49,15 +48,6 @@ #address-cells = <1>; #size-cells = <1>; }; - - flash1: mx66l51235l@1 { - compatible = "jedec,spi-nor"; - reg = <1>; - spi-rx-bus-width = <4>; - spi-max-frequency = <108000000>; - #address-cells = <1>; - #size-cells = <1>; - }; }; &usart3 { diff --git a/include/drivers/io/io_driver.h b/include/drivers/io/io_driver.h index 2b704f491e..d8bb435aa9 100644 --- a/include/drivers/io/io_driver.h +++ b/include/drivers/io/io_driver.h @@ -39,7 +39,7 @@ typedef struct io_dev_funcs { io_type_t (*type)(void); int (*open)(io_dev_info_t *dev_info, const uintptr_t spec, io_entity_t *entity); - int (*seek)(io_entity_t *entity, int mode, ssize_t offset); + int (*seek)(io_entity_t *entity, int mode, signed long long offset); int (*size)(io_entity_t *entity, size_t *length); int (*read)(io_entity_t *entity, uintptr_t buffer, size_t length, size_t *length_read); diff --git a/include/drivers/io/io_mtd.h b/include/drivers/io/io_mtd.h new file mode 100644 index 0000000000..1395ff6019 --- /dev/null +++ b/include/drivers/io/io_mtd.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2019, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef IO_MTD_H +#define IO_MTD_H + +#include <stdint.h> +#include <stdio.h> + +#include <drivers/io/io_storage.h> + +/* MTD devices ops */ +typedef struct io_mtd_ops { + /* + * Initialize MTD framework and retrieve device information. + * + * @size: [out] MTD device size in bytes. + * @erase_size: [out] MTD erase size in bytes. + * Return 0 on success, a negative error code otherwise. + */ + int (*init)(unsigned long long *size, unsigned int *erase_size); + + /* + * Execute a read memory operation. + * + * @offset: Offset in bytes to start read operation. + * @buffer: [out] Buffer to store read data. + * @length: Required length to be read in bytes. + * @out_length: [out] Length read in bytes. + * Return 0 on success, a negative error code otherwise. + */ + int (*read)(unsigned int offset, uintptr_t buffer, size_t length, + size_t *out_length); + + /* + * Execute a write memory operation. + * + * @offset: Offset in bytes to start write operation. + * @buffer: Buffer to be written in device. + * @length: Required length to be written in bytes. + * Return 0 on success, a negative error code otherwise. + */ + int (*write)(unsigned int offset, uintptr_t buffer, size_t length); +} io_mtd_ops_t; + +typedef struct io_mtd_dev_spec { + unsigned long long device_size; + unsigned int erase_size; + io_mtd_ops_t ops; +} io_mtd_dev_spec_t; + +struct io_dev_connector; + +int register_io_dev_mtd(const struct io_dev_connector **dev_con); + +#endif /* IO_MTD_H */ diff --git a/include/drivers/io/io_storage.h b/include/drivers/io/io_storage.h index 084c67c470..0e6ffd6193 100644 --- a/include/drivers/io/io_storage.h +++ b/include/drivers/io/io_storage.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2019, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -22,6 +22,7 @@ typedef enum { IO_TYPE_DUMMY, IO_TYPE_FIRMWARE_IMAGE_PACKAGE, IO_TYPE_BLOCK, + IO_TYPE_MTD, IO_TYPE_MMC, IO_TYPE_STM32IMAGE, IO_TYPE_MAX @@ -86,7 +87,7 @@ int io_dev_close(uintptr_t dev_handle); /* Synchronous operations */ int io_open(uintptr_t dev_handle, const uintptr_t spec, uintptr_t *handle); -int io_seek(uintptr_t handle, io_seek_mode_t mode, ssize_t offset); +int io_seek(uintptr_t handle, io_seek_mode_t mode, signed long long offset); int io_size(uintptr_t handle, size_t *length); diff --git a/include/drivers/nand.h b/include/drivers/nand.h new file mode 100644 index 0000000000..1dbb008f9c --- /dev/null +++ b/include/drivers/nand.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef DRIVERS_NAND_H +#define DRIVERS_NAND_H + +#include <stddef.h> +#include <stdint.h> + +#include <lib/utils_def.h> + +#define PSEC_TO_MSEC(x) div_round_up((x), 1000000000ULL) + +struct ecc { + unsigned int mode; /* ECC mode NAND_ECC_MODE_{NONE|HW|ONDIE} */ + unsigned int size; /* Data byte per ECC step */ + unsigned int bytes; /* ECC bytes per step */ + unsigned int max_bit_corr; /* Max correctible bits per ECC steps */ +}; + +struct nand_device { + unsigned int block_size; + unsigned int page_size; + unsigned long long size; + unsigned int nb_planes; + unsigned int buswidth; + struct ecc ecc; + int (*mtd_block_is_bad)(unsigned int block); + int (*mtd_read_page)(struct nand_device *nand, unsigned int page, + uintptr_t buffer); +}; + +/* + * Read bytes from NAND device + * + * @offset: Byte offset to read from in device + * @buffer: [out] Bytes read from device + * @length: Number of bytes to read + * @length_read: [out] Number of bytes read from device + * Return: 0 on success, a negative errno on failure + */ +int nand_read(unsigned int offset, uintptr_t buffer, size_t length, + size_t *length_read); + +/* + * Get NAND device instance + * + * Return: NAND device instance reference + */ +struct nand_device *get_nand_device(void); + +#endif /* DRIVERS_NAND_H */ diff --git a/include/drivers/raw_nand.h b/include/drivers/raw_nand.h new file mode 100644 index 0000000000..18e4b73da3 --- /dev/null +++ b/include/drivers/raw_nand.h @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef DRIVERS_RAW_NAND_H +#define DRIVERS_RAW_NAND_H + +#include <stdint.h> + +#include <drivers/nand.h> + +/* NAND ONFI default value mode 0 in picosecond */ +#define NAND_TADL_MIN 400000UL +#define NAND_TALH_MIN 20000UL +#define NAND_TALS_MIN 50000UL +#define NAND_TAR_MIN 25000UL +#define NAND_TCCS_MIN 500000UL +#define NAND_TCEA_MIN 100000UL +#define NAND_TCEH_MIN 20000UL +#define NAND_TCH_MIN 20000UL +#define NAND_TCHZ_MAX 100000UL +#define NAND_TCLH_MIN 20000UL +#define NAND_TCLR_MIN 20000UL +#define NAND_TCLS_MIN 50000UL +#define NAND_TCOH_MIN 0UL +#define NAND_TCS_MIN 70000UL +#define NAND_TDH_MIN 20000UL +#define NAND_TDS_MIN 40000UL +#define NAND_TFEAT_MAX 1000000UL +#define NAND_TIR_MIN 10000UL +#define NAND_TITC_MIN 1000000UL +#define NAND_TR_MAX 200000000UL +#define NAND_TRC_MIN 100000UL +#define NAND_TREA_MAX 40000UL +#define NAND_TREH_MIN 30000UL +#define NAND_TRHOH_MIN 0UL +#define NAND_TRHW_MIN 200000UL +#define NAND_TRHZ_MAX 200000UL +#define NAND_TRLOH_MIN 0UL +#define NAND_TRP_MIN 50000UL +#define NAND_TRR_MIN 40000UL +#define NAND_TRST_MAX 250000000000ULL +#define NAND_TWB_MAX 200000UL +#define NAND_TWC_MIN 100000UL +#define NAND_TWH_MIN 30000UL +#define NAND_TWHR_MIN 120000UL +#define NAND_TWP_MIN 50000UL +#define NAND_TWW_MIN 100000UL + +/* NAND request types */ +#define NAND_REQ_CMD 0x0000U +#define NAND_REQ_ADDR 0x1000U +#define NAND_REQ_DATAIN 0x2000U +#define NAND_REQ_DATAOUT 0x3000U +#define NAND_REQ_WAIT 0x4000U +#define NAND_REQ_MASK GENMASK(14, 12) +#define NAND_REQ_BUS_WIDTH_8 BIT(15) + +#define PARAM_PAGE_SIZE 256 + +/* NAND ONFI commands */ +#define NAND_CMD_READ_1ST 0x00U +#define NAND_CMD_CHANGE_1ST 0x05U +#define NAND_CMD_READID_SIG_ADDR 0x20U +#define NAND_CMD_READ_2ND 0x30U +#define NAND_CMD_STATUS 0x70U +#define NAND_CMD_READID 0x90U +#define NAND_CMD_CHANGE_2ND 0xE0U +#define NAND_CMD_READ_PARAM_PAGE 0xECU +#define NAND_CMD_RESET 0xFFU + +#define ONFI_REV_21 BIT(3) +#define ONFI_FEAT_BUS_WIDTH_16 BIT(0) +#define ONFI_FEAT_EXTENDED_PARAM BIT(7) + +/* NAND ECC type */ +#define NAND_ECC_NONE U(0) +#define NAND_ECC_HW U(1) +#define NAND_ECC_ONDIE U(2) + +/* NAND bus width */ +#define NAND_BUS_WIDTH_8 U(0) +#define NAND_BUS_WIDTH_16 U(1) + +struct nand_req { + struct nand_device *nand; + uint16_t type; + uint8_t *addr; + unsigned int length; + unsigned int delay_ms; + unsigned int inst_delay; +}; + +struct nand_param_page { + /* Rev information and feature block */ + uint32_t page_sig; + uint16_t rev; + uint16_t features; + uint16_t opt_cmd; + uint8_t jtg; + uint8_t train_cmd; + uint16_t ext_param_length; + uint8_t nb_param_pages; + uint8_t reserved1[17]; + /* Manufacturer information */ + uint8_t manufacturer[12]; + uint8_t model[20]; + uint8_t manufacturer_id; + uint16_t data_code; + uint8_t reserved2[13]; + /* Memory organization */ + uint32_t bytes_per_page; + uint16_t spare_per_page; + uint32_t bytes_per_partial; + uint16_t spare_per_partial; + uint32_t num_pages_per_blk; + uint32_t num_blk_in_lun; + uint8_t num_lun; + uint8_t num_addr_cycles; + uint8_t bit_per_cell; + uint16_t max_bb_per_lun; + uint16_t blk_endur; + uint8_t valid_blk_begin; + uint16_t blk_enbur_valid; + uint8_t nb_prog_page; + uint8_t partial_prog_attr; + uint8_t nb_ecc_bits; + uint8_t plane_addr; + uint8_t mplanes_ops; + uint8_t ez_nand; + uint8_t reserved3[12]; + /* Electrical parameters */ + uint8_t io_pin_cap_max; + uint16_t sdr_timing_mode; + uint16_t sdr_prog_cache_timing; + uint16_t tprog; + uint16_t tbers; + uint16_t tr; + uint16_t tccs; + uint8_t nvddr_timing_mode; + uint8_t nvddr2_timing_mode; + uint8_t nvddr_features; + uint16_t clk_input_cap_typ; + uint16_t io_pin_cap_typ; + uint16_t input_pin_cap_typ; + uint8_t input_pin_cap_max; + uint8_t drv_strength_support; + uint16_t tr_max; + uint16_t tadl; + uint16_t tr_typ; + uint8_t reserved4[6]; + /* Vendor block */ + uint16_t vendor_revision; + uint8_t vendor[88]; + uint16_t crc16; +} __packed; + +struct nand_ctrl_ops { + int (*exec)(struct nand_req *req); + void (*setup)(struct nand_device *nand); +}; + +struct rawnand_device { + struct nand_device *nand_dev; + const struct nand_ctrl_ops *ops; +}; + +int nand_raw_init(unsigned long long *size, unsigned int *erase_size); +int nand_wait_ready(unsigned long delay); +int nand_read_page_cmd(unsigned int page, unsigned int offset, + uintptr_t buffer, unsigned int len); +int nand_change_read_column_cmd(unsigned int offset, uintptr_t buffer, + unsigned int len); +void nand_raw_ctrl_init(const struct nand_ctrl_ops *ops); + +/* + * Platform can implement this to override default raw NAND instance + * configuration. + * + * @device: target raw NAND instance. + * Return 0 on success, negative value otherwise. + */ +int plat_get_raw_nand_data(struct rawnand_device *device); + +#endif /* DRIVERS_RAW_NAND_H */ diff --git a/include/drivers/spi_mem.h b/include/drivers/spi_mem.h new file mode 100644 index 0000000000..d1953acf4c --- /dev/null +++ b/include/drivers/spi_mem.h @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef DRIVERS_SPI_MEM_H +#define DRIVERS_SPI_MEM_H + +#include <errno.h> +#include <stdbool.h> +#include <stdint.h> + +#define SPI_MEM_BUSWIDTH_1_LINE 1U +#define SPI_MEM_BUSWIDTH_2_LINE 2U +#define SPI_MEM_BUSWIDTH_4_LINE 4U + +/* + * enum spi_mem_data_dir - Describes the direction of a SPI memory data + * transfer from the controller perspective. + * @SPI_MEM_DATA_IN: data coming from the SPI memory. + * @SPI_MEM_DATA_OUT: data sent to the SPI memory. + */ +enum spi_mem_data_dir { + SPI_MEM_DATA_IN, + SPI_MEM_DATA_OUT, +}; + +/* + * struct spi_mem_op - Describes a SPI memory operation. + * + * @cmd.buswidth: Number of IO lines used to transmit the command. + * @cmd.opcode: Operation opcode. + * @addr.nbytes: Number of address bytes to send. Can be zero if the operation + * does not need to send an address. + * @addr.buswidth: Number of IO lines used to transmit the address. + * @addr.val: Address value. This value is always sent MSB first on the bus. + * Note that only @addr.nbytes are taken into account in this + * address value, so users should make sure the value fits in the + * assigned number of bytes. + * @dummy.nbytes: Number of dummy bytes to send after an opcode or address. Can + * be zero if the operation does not require dummy bytes. + * @dummy.buswidth: Number of IO lines used to transmit the dummy bytes. + * @data.buswidth: Number of IO lines used to send/receive the data. + * @data.dir: Direction of the transfer. + * @data.nbytes: Number of data bytes to transfer. + * @data.buf: Input or output data buffer depending on data::dir. + */ +struct spi_mem_op { + struct { + uint8_t buswidth; + uint8_t opcode; + } cmd; + + struct { + uint8_t nbytes; + uint8_t buswidth; + uint64_t val; + } addr; + + struct { + uint8_t nbytes; + uint8_t buswidth; + } dummy; + + struct { + uint8_t buswidth; + enum spi_mem_data_dir dir; + unsigned int nbytes; + void *buf; + } data; +}; + +/* SPI mode flags */ +#define SPI_CPHA BIT(0) /* clock phase */ +#define SPI_CPOL BIT(1) /* clock polarity */ +#define SPI_CS_HIGH BIT(2) /* CS active high */ +#define SPI_LSB_FIRST BIT(3) /* per-word bits-on-wire */ +#define SPI_3WIRE BIT(4) /* SI/SO signals shared */ +#define SPI_PREAMBLE BIT(5) /* Skip preamble bytes */ +#define SPI_TX_DUAL BIT(6) /* transmit with 2 wires */ +#define SPI_TX_QUAD BIT(7) /* transmit with 4 wires */ +#define SPI_RX_DUAL BIT(8) /* receive with 2 wires */ +#define SPI_RX_QUAD BIT(9) /* receive with 4 wires */ + +struct spi_bus_ops { + /* + * Claim the bus and prepare it for communication. + * + * @cs: The chip select. + * Returns: 0 if the bus was claimed successfully, or a negative value + * if it wasn't. + */ + int (*claim_bus)(unsigned int cs); + + /* + * Release the SPI bus. + */ + void (*release_bus)(void); + + /* + * Set transfer speed. + * + * @hz: The transfer speed in Hertz. + * Returns: 0 on success, a negative error code otherwise. + */ + int (*set_speed)(unsigned int hz); + + /* + * Set the SPI mode/flags. + * + * @mode: Requested SPI mode (SPI_... flags). + * Returns: 0 on success, a negative error code otherwise. + */ + int (*set_mode)(unsigned int mode); + + /* + * Execute a SPI memory operation. + * + * @op: The memory operation to execute. + * Returns: 0 on success, a negative error code otherwise. + */ + int (*exec_op)(const struct spi_mem_op *op); +}; + +int spi_mem_exec_op(const struct spi_mem_op *op); +int spi_mem_init_slave(void *fdt, int bus_node, + const struct spi_bus_ops *ops); + +#endif /* DRIVERS_SPI_MEM_H */ diff --git a/include/drivers/spi_nand.h b/include/drivers/spi_nand.h new file mode 100644 index 0000000000..40e2063751 --- /dev/null +++ b/include/drivers/spi_nand.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef DRIVERS_SPI_NAND_H +#define DRIVERS_SPI_NAND_H + +#include <drivers/nand.h> +#include <drivers/spi_mem.h> + +#define SPI_NAND_OP_GET_FEATURE 0x0FU +#define SPI_NAND_OP_SET_FEATURE 0x1FU +#define SPI_NAND_OP_READ_ID 0x9FU +#define SPI_NAND_OP_LOAD_PAGE 0x13U +#define SPI_NAND_OP_RESET 0xFFU +#define SPI_NAND_OP_READ_FROM_CACHE 0x03U +#define SPI_NAND_OP_READ_FROM_CACHE_2X 0x3BU +#define SPI_NAND_OP_READ_FROM_CACHE_4X 0x6BU + +/* Configuration register */ +#define SPI_NAND_REG_CFG 0xB0U +#define SPI_NAND_CFG_ECC_EN BIT(4) +#define SPI_NAND_CFG_QE BIT(0) + +/* Status register */ +#define SPI_NAND_REG_STATUS 0xC0U +#define SPI_NAND_STATUS_BUSY BIT(0) +#define SPI_NAND_STATUS_ECC_UNCOR BIT(5) + +struct spinand_device { + struct nand_device *nand_dev; + struct spi_mem_op spi_read_cache_op; + uint8_t cfg_cache; /* Cached value of SPI NAND device register CFG */ +}; + +int spi_nand_init(unsigned long long *size, unsigned int *erase_size); + +/* + * Platform can implement this to override default SPI-NAND instance + * configuration. + * + * @device: target SPI-NAND instance. + * Return 0 on success, negative value otherwise. + */ +int plat_get_spi_nand_data(struct spinand_device *device); + +#endif /* DRIVERS_SPI_NAND_H */ diff --git a/include/drivers/spi_nor.h b/include/drivers/spi_nor.h new file mode 100644 index 0000000000..72cfe5b346 --- /dev/null +++ b/include/drivers/spi_nor.h @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef DRIVERS_SPI_NOR_H +#define DRIVERS_SPI_NOR_H + +#include <drivers/spi_mem.h> + +/* OPCODE */ +#define SPI_NOR_OP_WREN 0x06U /* Write enable */ +#define SPI_NOR_OP_WRSR 0x01U /* Write status register 1 byte */ +#define SPI_NOR_OP_READ_ID 0x9FU /* Read JEDEC ID */ +#define SPI_NOR_OP_READ_CR 0x35U /* Read configuration register */ +#define SPI_NOR_OP_READ_SR 0x05U /* Read status register */ +#define SPI_NOR_OP_READ_FSR 0x70U /* Read flag status register */ +#define SPINOR_OP_RDEAR 0xC8U /* Read Extended Address Register */ +#define SPINOR_OP_WREAR 0xC5U /* Write Extended Address Register */ + +/* Used for Spansion flashes only. */ +#define SPINOR_OP_BRWR 0x17U /* Bank register write */ +#define SPINOR_OP_BRRD 0x16U /* Bank register read */ + +#define SPI_NOR_OP_READ 0x03U /* Read data bytes (low frequency) */ +#define SPI_NOR_OP_READ_FAST 0x0BU /* Read data bytes (high frequency) */ +#define SPI_NOR_OP_READ_1_1_2 0x3BU /* Read data bytes (Dual Output SPI) */ +#define SPI_NOR_OP_READ_1_2_2 0xBBU /* Read data bytes (Dual I/O SPI) */ +#define SPI_NOR_OP_READ_1_1_4 0x6BU /* Read data bytes (Quad Output SPI) */ +#define SPI_NOR_OP_READ_1_4_4 0xEBU /* Read data bytes (Quad I/O SPI) */ + +/* Flags for NOR specific configuration */ +#define SPI_NOR_USE_FSR BIT(0) +#define SPI_NOR_USE_BANK BIT(1) + +struct nor_device { + struct spi_mem_op read_op; + uint32_t size; + uint32_t flags; + uint8_t selected_bank; + uint8_t bank_write_cmd; + uint8_t bank_read_cmd; +}; + +int spi_nor_read(unsigned int offset, uintptr_t buffer, size_t length, + size_t *length_read); +int spi_nor_init(unsigned long long *device_size, unsigned int *erase_size); + +/* + * Platform can implement this to override default NOR instance configuration. + * + * @device: target NOR instance. + * Return 0 on success, negative value otherwise. + */ +int plat_get_nor_data(struct nor_device *device); + +#endif /* DRIVERS_SPI_NOR_H */ diff --git a/include/drivers/st/io_stm32image.h b/include/drivers/st/io_stm32image.h index 68060558ba..f9fa3630c6 100644 --- a/include/drivers/st/io_stm32image.h +++ b/include/drivers/st/io_stm32image.h @@ -23,7 +23,7 @@ struct stm32image_part_info { struct stm32image_device_info { struct stm32image_part_info part_info[STM32_PART_NUM]; - uint32_t device_size; + unsigned long long device_size; uint32_t lba_size; }; diff --git a/include/drivers/st/stm32_fmc2_nand.h b/include/drivers/st/stm32_fmc2_nand.h new file mode 100644 index 0000000000..81d5b9de19 --- /dev/null +++ b/include/drivers/st/stm32_fmc2_nand.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause + */ + +#ifndef STM32_FMC2_NAND_H +#define STM32_FMC2_NAND_H + +int stm32_fmc2_init(void); + +#endif /* STM32_FMC2_NAND_H */ diff --git a/include/drivers/st/stm32_qspi.h b/include/drivers/st/stm32_qspi.h new file mode 100644 index 0000000000..f47fca445e --- /dev/null +++ b/include/drivers/st/stm32_qspi.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: GPL-2.0+ OR BSD-3-Clause + */ + +#ifndef STM32_QSPI_H +#define STM32_QSPI_H + +int stm32_qspi_init(void); + +#endif /* STM32_QSPI_H */ diff --git a/include/lib/utils_def.h b/include/lib/utils_def.h index 35ae33a682..23f59bdc33 100644 --- a/include/lib/utils_def.h +++ b/include/lib/utils_def.h @@ -77,6 +77,15 @@ _x > _y ? _x : _y; \ }) +#define CLAMP(x, min, max) __extension__ ({ \ + __typeof__(x) _x = (x); \ + __typeof__(min) _min = (min); \ + __typeof__(max) _max = (max); \ + (void)(&_x == &_min); \ + (void)(&_x == &_max); \ + (_x > _max ? _max : (_x < _min ? _min : _x)); \ +}) + /* * The round_up() macro rounds up a value to the given boundary in a * type-agnostic yet type-safe manner. The boundary must be a power of two. diff --git a/lib/compiler-rt/builtins/arm/aeabi_ldivmod.S b/lib/compiler-rt/builtins/arm/aeabi_ldivmod.S new file mode 100644 index 0000000000..038ae5d723 --- /dev/null +++ b/lib/compiler-rt/builtins/arm/aeabi_ldivmod.S @@ -0,0 +1,46 @@ +//===-- aeabi_ldivmod.S - EABI ldivmod implementation ---------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "../assembly.h" + +// struct { int64_t quot, int64_t rem} +// __aeabi_ldivmod(int64_t numerator, int64_t denominator) { +// int64_t rem, quot; +// quot = __divmoddi4(numerator, denominator, &rem); +// return {quot, rem}; +// } + +#if defined(__MINGW32__) +#define __aeabi_ldivmod __rt_sdiv64 +#endif + + .syntax unified + .p2align 2 +DEFINE_COMPILERRT_FUNCTION(__aeabi_ldivmod) + push {r6, lr} + sub sp, sp, #16 + add r6, sp, #8 + str r6, [sp] +#if defined(__MINGW32__) + movs r6, r0 + movs r0, r2 + movs r2, r6 + movs r6, r1 + movs r1, r3 + movs r3, r6 +#endif + bl SYMBOL_NAME(__divmoddi4) + ldr r2, [sp, #8] + ldr r3, [sp, #12] + add sp, sp, #16 + pop {r6, pc} +END_COMPILERRT_FUNCTION(__aeabi_ldivmod) + +NO_EXEC_STACK_DIRECTIVE + diff --git a/lib/compiler-rt/builtins/divdi3.c b/lib/compiler-rt/builtins/divdi3.c new file mode 100644 index 0000000000..b8eebcb204 --- /dev/null +++ b/lib/compiler-rt/builtins/divdi3.c @@ -0,0 +1,29 @@ +/* ===-- divdi3.c - Implement __divdi3 -------------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __divdi3 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: a / b */ + +COMPILER_RT_ABI di_int +__divdi3(di_int a, di_int b) +{ + const int bits_in_dword_m1 = (int)(sizeof(di_int) * CHAR_BIT) - 1; + di_int s_a = a >> bits_in_dword_m1; /* s_a = a < 0 ? -1 : 0 */ + di_int s_b = b >> bits_in_dword_m1; /* s_b = b < 0 ? -1 : 0 */ + a = (a ^ s_a) - s_a; /* negate if s_a == -1 */ + b = (b ^ s_b) - s_b; /* negate if s_b == -1 */ + s_a ^= s_b; /*sign of quotient */ + return (__udivmoddi4(a, b, (du_int*)0) ^ s_a) - s_a; /* negate if s_a == -1 */ +} diff --git a/lib/compiler-rt/builtins/divmoddi4.c b/lib/compiler-rt/builtins/divmoddi4.c new file mode 100644 index 0000000000..0d4df67a63 --- /dev/null +++ b/lib/compiler-rt/builtins/divmoddi4.c @@ -0,0 +1,25 @@ +/*===-- divmoddi4.c - Implement __divmoddi4 --------------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __divmoddi4 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: a / b, *rem = a % b */ + +COMPILER_RT_ABI di_int +__divmoddi4(di_int a, di_int b, di_int* rem) +{ + di_int d = __divdi3(a,b); + *rem = a - (d*b); + return d; +} diff --git a/lib/compiler-rt/builtins/popcountdi2.c b/lib/compiler-rt/builtins/popcountdi2.c new file mode 100644 index 0000000000..5e8a62f075 --- /dev/null +++ b/lib/compiler-rt/builtins/popcountdi2.c @@ -0,0 +1,36 @@ +/* ===-- popcountdi2.c - Implement __popcountdi2 ----------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __popcountdi2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: count of 1 bits */ + +COMPILER_RT_ABI si_int +__popcountdi2(di_int a) +{ + du_int x2 = (du_int)a; + x2 = x2 - ((x2 >> 1) & 0x5555555555555555uLL); + /* Every 2 bits holds the sum of every pair of bits (32) */ + x2 = ((x2 >> 2) & 0x3333333333333333uLL) + (x2 & 0x3333333333333333uLL); + /* Every 4 bits holds the sum of every 4-set of bits (3 significant bits) (16) */ + x2 = (x2 + (x2 >> 4)) & 0x0F0F0F0F0F0F0F0FuLL; + /* Every 8 bits holds the sum of every 8-set of bits (4 significant bits) (8) */ + su_int x = (su_int)(x2 + (x2 >> 32)); + /* The lower 32 bits hold four 16 bit sums (5 significant bits). */ + /* Upper 32 bits are garbage */ + x = x + (x >> 16); + /* The lower 16 bits hold two 32 bit sums (6 significant bits). */ + /* Upper 16 bits are garbage */ + return (x + (x >> 8)) & 0x0000007F; /* (7 significant bits) */ +} diff --git a/lib/compiler-rt/builtins/popcountsi2.c b/lib/compiler-rt/builtins/popcountsi2.c new file mode 100644 index 0000000000..44544ff498 --- /dev/null +++ b/lib/compiler-rt/builtins/popcountsi2.c @@ -0,0 +1,33 @@ +/* ===-- popcountsi2.c - Implement __popcountsi2 ---------------------------=== + * + * The LLVM Compiler Infrastructure + * + * This file is dual licensed under the MIT and the University of Illinois Open + * Source Licenses. See LICENSE.TXT for details. + * + * ===----------------------------------------------------------------------=== + * + * This file implements __popcountsi2 for the compiler_rt library. + * + * ===----------------------------------------------------------------------=== + */ + +#include "int_lib.h" + +/* Returns: count of 1 bits */ + +COMPILER_RT_ABI si_int +__popcountsi2(si_int a) +{ + su_int x = (su_int)a; + x = x - ((x >> 1) & 0x55555555); + /* Every 2 bits holds the sum of every pair of bits */ + x = ((x >> 2) & 0x33333333) + (x & 0x33333333); + /* Every 4 bits holds the sum of every 4-set of bits (3 significant bits) */ + x = (x + (x >> 4)) & 0x0F0F0F0F; + /* Every 8 bits holds the sum of every 8-set of bits (4 significant bits) */ + x = (x + (x >> 16)); + /* The lower 16 bits hold two 8 bit sums (5 significant bits).*/ + /* Upper 16 bits are garbage */ + return (x + (x >> 8)) & 0x0000003F; /* (6 significant bits) */ +} diff --git a/lib/compiler-rt/compiler-rt.mk b/lib/compiler-rt/compiler-rt.mk index 49e497eb83..40c669f989 100644 --- a/lib/compiler-rt/compiler-rt.mk +++ b/lib/compiler-rt/compiler-rt.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: @@ -28,9 +28,15 @@ # POSSIBILITY OF SUCH DAMAGE. # +COMPILER_RT_SRCS := lib/compiler-rt/builtins/popcountdi2.c \ + lib/compiler-rt/builtins/popcountsi2.c + ifeq (${ARCH},aarch32) -COMPILER_RT_SRCS := lib/compiler-rt/builtins/arm/aeabi_uldivmod.S \ - lib/compiler-rt/builtins/udivmoddi4.c \ +COMPILER_RT_SRCS += lib/compiler-rt/builtins/arm/aeabi_ldivmod.S \ + lib/compiler-rt/builtins/arm/aeabi_uldivmod.S \ lib/compiler-rt/builtins/ctzdi2.c \ - lib/compiler-rt/builtins/lshrdi3.c + lib/compiler-rt/builtins/divdi3.c \ + lib/compiler-rt/builtins/divmoddi4.c \ + lib/compiler-rt/builtins/lshrdi3.c \ + lib/compiler-rt/builtins/udivmoddi4.c endif diff --git a/lib/cpus/cpu-ops.mk b/lib/cpus/cpu-ops.mk index c9b9b387ee..398edf9dc1 100644 --- a/lib/cpus/cpu-ops.mk +++ b/lib/cpus/cpu-ops.mk @@ -240,7 +240,7 @@ ERRATA_HERCULES_1688305 ?=0 # Flag to apply T32 CLREX workaround during reset. This erratum applies # only to r0p0 and r1p0 of the Neoverse N1 cpu. -ERRATA_N1_1043202 ?=1 +ERRATA_N1_1043202 ?=0 # Flag to apply erratum 1073348 workaround during reset. This erratum applies # only to revision r0p0 and r1p0 of the Neoverse N1 cpu. @@ -280,7 +280,7 @@ ERRATA_N1_1275112 ?=0 # Flag to apply erratum 1315703 workaround during reset. This erratum applies # to revisions before r3p1 of the Neoverse N1 cpu. -ERRATA_N1_1315703 ?=1 +ERRATA_N1_1315703 ?=0 # Flag to apply erratum 1542419 workaround during reset. This erratum applies # to revisions r3p0 - r4p0 of the Neoverse N1 cpu. diff --git a/make_helpers/build_macros.mk b/make_helpers/build_macros.mk index 47f3ebd861..6b72cfd0d4 100644 --- a/make_helpers/build_macros.mk +++ b/make_helpers/build_macros.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -513,7 +513,7 @@ $(DOBJ): $(2) $(filter-out %.d,$(MAKEFILE_LIST)) | fdt_dirs $(eval DTBS := $(addprefix $(1)/,$(call SOURCES_TO_DTBS,$(2)))) $$(Q)$$(PP) $$(DTC_CPPFLAGS) -MT $(DTBS) -MMD -MF $(DTSDEP) -o $(DPRE) $$< $${ECHO} " DTC $$<" - $$(Q)$$(DTC) $$(DTC_FLAGS) -i fdts -d $(DTBDEP) -o $$@ $(DPRE) + $$(Q)$$(DTC) $$(DTC_FLAGS) -d $(DTBDEP) -o $$@ $(DPRE) -include $(DTBDEP) -include $(DTSDEP) diff --git a/plat/allwinner/common/include/platform_def.h b/plat/allwinner/common/include/platform_def.h index 0a00076679..4de8b0bc46 100644 --- a/plat/allwinner/common/include/platform_def.h +++ b/plat/allwinner/common/include/platform_def.h @@ -32,7 +32,7 @@ #define CACHE_WRITEBACK_SHIFT 6 #define CACHE_WRITEBACK_GRANULE (1 << CACHE_WRITEBACK_SHIFT) -#define MAX_MMAP_REGIONS (4 + PLATFORM_MMAP_REGIONS) +#define MAX_MMAP_REGIONS (3 + PLATFORM_MMAP_REGIONS) #define MAX_XLAT_TABLES 1 #define PLAT_MAX_PWR_LVL_STATES U(2) diff --git a/plat/allwinner/common/sunxi_common.c b/plat/allwinner/common/sunxi_common.c index cff8268199..3759c285e2 100644 --- a/plat/allwinner/common/sunxi_common.c +++ b/plat/allwinner/common/sunxi_common.c @@ -20,15 +20,15 @@ static const mmap_region_t sunxi_mmap[PLATFORM_MMAP_REGIONS + 1] = { MAP_REGION_FLAT(SUNXI_SRAM_BASE, SUNXI_SRAM_SIZE, - MT_MEMORY | MT_RW | MT_SECURE), + MT_RW_DATA | MT_SECURE), MAP_REGION_FLAT(SUNXI_DEV_BASE, SUNXI_DEV_SIZE, - MT_DEVICE | MT_RW | MT_SECURE), + MT_DEVICE | MT_RW | MT_SECURE | MT_EXECUTE_NEVER), MAP_REGION(SUNXI_DRAM_BASE, SUNXI_DRAM_VIRT_BASE, SUNXI_DRAM_SEC_SIZE, - MT_MEMORY | MT_RW | MT_SECURE), + MT_RW_DATA | MT_SECURE), MAP_REGION(PLAT_SUNXI_NS_IMAGE_OFFSET, SUNXI_DRAM_VIRT_BASE + SUNXI_DRAM_SEC_SIZE, SUNXI_DRAM_MAP_SIZE, - MT_MEMORY | MT_RO | MT_NS), + MT_RO_DATA | MT_NS), {}, }; @@ -48,9 +48,6 @@ uintptr_t plat_get_ns_image_entrypoint(void) void sunxi_configure_mmu_el3(int flags) { - mmap_add_region(BL31_BASE, BL31_BASE, - BL31_LIMIT - BL31_BASE, - MT_MEMORY | MT_RW | MT_SECURE); mmap_add_region(BL_CODE_BASE, BL_CODE_BASE, BL_CODE_END - BL_CODE_BASE, MT_CODE | MT_SECURE); diff --git a/plat/imx/common/plat_imx8_gic.c b/plat/imx/common/plat_imx8_gic.c index 3a7dcfec6d..afb9d1f1a9 100644 --- a/plat/imx/common/plat_imx8_gic.c +++ b/plat/imx/common/plat_imx8_gic.c @@ -20,9 +20,7 @@ uintptr_t rdistif_base_addrs[PLATFORM_CORE_COUNT]; static const interrupt_prop_t g01s_interrupt_props[] = { - INTR_PROP_DESC(6, GIC_HIGHEST_SEC_PRIORITY, - INTR_GROUP1S, GIC_INTR_CFG_LEVEL), - INTR_PROP_DESC(7, GIC_HIGHEST_SEC_PRIORITY, + INTR_PROP_DESC(8, GIC_HIGHEST_SEC_PRIORITY, INTR_GROUP0, GIC_INTR_CFG_LEVEL), }; diff --git a/plat/imx/imx8m/imx8mm/imx8mm_bl31_setup.c b/plat/imx/imx8m/imx8mm/imx8mm_bl31_setup.c index c3cd0d0cb9..4c5f4f0d19 100644 --- a/plat/imx/imx8m/imx8mm/imx8mm_bl31_setup.c +++ b/plat/imx/imx8m/imx8mm/imx8mm_bl31_setup.c @@ -124,6 +124,18 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, bl33_image_ep_info.spsr = get_spsr_for_bl33_entry(); SET_SECURITY_STATE(bl33_image_ep_info.h.attr, NON_SECURE); +#ifdef SPD_opteed + /* Populate entry point information for BL32 */ + SET_PARAM_HEAD(&bl32_image_ep_info, PARAM_EP, VERSION_1, 0); + SET_SECURITY_STATE(bl32_image_ep_info.h.attr, SECURE); + bl32_image_ep_info.pc = BL32_BASE; + bl32_image_ep_info.spsr = 0; + + /* Pass TEE base and size to bl33 */ + bl33_image_ep_info.args.arg1 = BL32_BASE; + bl33_image_ep_info.args.arg2 = BL32_SIZE; +#endif + bl31_tzc380_setup(); } diff --git a/plat/imx/imx8m/imx8mm/include/platform_def.h b/plat/imx/imx8m/imx8mm/include/platform_def.h index de9e3b5c85..fc35dac146 100644 --- a/plat/imx/imx8m/imx8mm/include/platform_def.h +++ b/plat/imx/imx8m/imx8mm/include/platform_def.h @@ -31,7 +31,6 @@ #define BL31_BASE U(0x920000) #define BL31_LIMIT U(0x940000) -#define BL32_BASE U(0xbe000000) /* non-secure uboot base */ #define PLAT_NS_IMAGE_OFFSET U(0x40200000) diff --git a/plat/imx/imx8m/imx8mm/platform.mk b/plat/imx/imx8m/imx8mm/platform.mk index 6d32dbba32..c0cb6c2a5c 100644 --- a/plat/imx/imx8m/imx8mm/platform.mk +++ b/plat/imx/imx8m/imx8mm/platform.mk @@ -45,3 +45,9 @@ A53_DISABLE_NON_TEMPORAL_HINT := 0 ERRATA_A53_835769 := 1 ERRATA_A53_843419 := 1 ERRATA_A53_855873 := 1 + +BL32_BASE ?= 0xbe000000 +$(eval $(call add_define,BL32_BASE)) + +BL32_SIZE ?= 0x2000000 +$(eval $(call add_define,BL32_SIZE)) diff --git a/plat/imx/imx8m/imx8mq/imx8mq_bl31_setup.c b/plat/imx/imx8m/imx8mq/imx8mq_bl31_setup.c index 26a3b364e9..a347389a26 100644 --- a/plat/imx/imx8m/imx8mq/imx8mq_bl31_setup.c +++ b/plat/imx/imx8m/imx8mq/imx8mq_bl31_setup.c @@ -146,6 +146,18 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, bl33_image_ep_info.spsr = get_spsr_for_bl33_entry(); SET_SECURITY_STATE(bl33_image_ep_info.h.attr, NON_SECURE); +#ifdef SPD_opteed + /* Populate entry point information for BL32 */ + SET_PARAM_HEAD(&bl32_image_ep_info, PARAM_EP, VERSION_1, 0); + SET_SECURITY_STATE(bl32_image_ep_info.h.attr, SECURE); + bl32_image_ep_info.pc = BL32_BASE; + bl32_image_ep_info.spsr = 0; + + /* Pass TEE base and size to bl33 */ + bl33_image_ep_info.args.arg1 = BL32_BASE; + bl33_image_ep_info.args.arg2 = BL32_SIZE; +#endif + bl31_tz380_setup(); } diff --git a/plat/imx/imx8m/imx8mq/include/platform_def.h b/plat/imx/imx8m/imx8mq/include/platform_def.h index 3c212e3782..9aa759fd37 100644 --- a/plat/imx/imx8m/imx8mq/include/platform_def.h +++ b/plat/imx/imx8m/imx8mq/include/platform_def.h @@ -32,7 +32,6 @@ #define BL31_BASE U(0x910000) #define BL31_LIMIT U(0x920000) -#define BL32_BASE U(0xfe000000) /* non-secure uboot base */ #define PLAT_NS_IMAGE_OFFSET U(0x40200000) diff --git a/plat/imx/imx8m/imx8mq/platform.mk b/plat/imx/imx8m/imx8mq/platform.mk index 44ce555871..80ebe40622 100644 --- a/plat/imx/imx8m/imx8mq/platform.mk +++ b/plat/imx/imx8m/imx8mq/platform.mk @@ -44,3 +44,9 @@ A53_DISABLE_NON_TEMPORAL_HINT := 0 ERRATA_A53_835769 := 1 ERRATA_A53_843419 := 1 ERRATA_A53_855873 := 1 + +BL32_BASE ?= 0xfe000000 +$(eval $(call add_define,BL32_BASE)) + +BL32_SIZE ?= 0x2000000 +$(eval $(call add_define,BL32_SIZE)) diff --git a/plat/nvidia/tegra/include/t194/tegra_def.h b/plat/nvidia/tegra/include/t194/tegra_def.h index e20b2c6343..a98aa2da8d 100644 --- a/plat/nvidia/tegra/include/t194/tegra_def.h +++ b/plat/nvidia/tegra/include/t194/tegra_def.h @@ -172,16 +172,16 @@ * Tegra scratch registers constants ******************************************************************************/ #define TEGRA_SCRATCH_BASE U(0x0C390000) -#define SECURE_SCRATCH_RSV44_LO U(0x1C4) -#define SECURE_SCRATCH_RSV44_HI U(0x1C8) +#define SECURE_SCRATCH_RSV81_LO U(0x2EC) +#define SECURE_SCRATCH_RSV81_HI U(0x2F0) #define SECURE_SCRATCH_RSV97 U(0x36C) #define SECURE_SCRATCH_RSV99_LO U(0x37C) #define SECURE_SCRATCH_RSV99_HI U(0x380) #define SECURE_SCRATCH_RSV109_LO U(0x3CC) #define SECURE_SCRATCH_RSV109_HI U(0x3D0) -#define SCRATCH_BL31_PARAMS_ADDR SECURE_SCRATCH_RSV44_LO -#define SCRATCH_BL31_PLAT_PARAMS_ADDR SECURE_SCRATCH_RSV44_HI +#define SCRATCH_BL31_PARAMS_ADDR SECURE_SCRATCH_RSV81_LO +#define SCRATCH_BL31_PLAT_PARAMS_ADDR SECURE_SCRATCH_RSV81_HI #define SCRATCH_SECURE_BOOTP_FCFG SECURE_SCRATCH_RSV97 #define SCRATCH_SMMU_TABLE_ADDR_LO SECURE_SCRATCH_RSV99_LO #define SCRATCH_SMMU_TABLE_ADDR_HI SECURE_SCRATCH_RSV99_HI diff --git a/plat/nvidia/tegra/include/t194/tegra_mc_def.h b/plat/nvidia/tegra/include/t194/tegra_mc_def.h index 1433a2ea6c..09dcce3c05 100644 --- a/plat/nvidia/tegra/include/t194/tegra_mc_def.h +++ b/plat/nvidia/tegra/include/t194/tegra_mc_def.h @@ -27,8 +27,8 @@ #define MC_CLIENT_ORDER_ID_28_PCIE5W_MASK (0x3U << 12) #define MC_CLIENT_ORDER_ID_28_PCIE5W_ORDER_ID (2U << 12) -#define mc_client_order_id(id, client) \ - (~MC_CLIENT_ORDER_ID_##id##_##client##_MASK | \ +#define mc_client_order_id(val, id, client) \ + ((val & ~MC_CLIENT_ORDER_ID_##id##_##client##_MASK) | \ MC_CLIENT_ORDER_ID_##id##_##client##_ORDER_ID) /******************************************************************************* @@ -53,8 +53,8 @@ #define MC_HUB_PC_VC_ID_4_NIC_VC_ID_MASK (0x3U << 28) #define MC_HUB_PC_VC_ID_4_NIC_VC_ID (VC_NISO << 28) -#define mc_hub_vc_id(id, client) \ - (~MC_HUB_PC_VC_ID_##id##_##client##_VC_ID_MASK | \ +#define mc_hub_vc_id(val, id, client) \ + ((val & ~MC_HUB_PC_VC_ID_##id##_##client##_VC_ID_MASK) | \ MC_HUB_PC_VC_ID_##id##_##client##_VC_ID) /******************************************************************************* @@ -530,6 +530,9 @@ #define MC_CLIENT_HOTRESET_CTRL2_PCIE4A_FLUSH_ENB (1U << 25) #define MC_CLIENT_HOTRESET_STATUS2 0x1898U +#define MC_COALESCE_CTRL 0x2930U +#define MC_COALESCE_CTRL_COALESCER_ENABLE (1U << 31) + /******************************************************************************* * Tegra TSA Controller constants ******************************************************************************/ diff --git a/plat/nvidia/tegra/soc/t194/drivers/include/mce_private.h b/plat/nvidia/tegra/soc/t194/drivers/include/mce_private.h index 226ab5bc51..9741d084fa 100644 --- a/plat/nvidia/tegra/soc/t194/drivers/include/mce_private.h +++ b/plat/nvidia/tegra/soc/t194/drivers/include/mce_private.h @@ -7,6 +7,7 @@ #ifndef MCE_PRIVATE_H #define MCE_PRIVATE_H +#include <stdbool.h> #include <tegra_def.h> /******************************************************************************* @@ -67,8 +68,12 @@ uint64_t nvg_cache_clean(void); uint64_t nvg_cache_clean_inval(void); uint64_t nvg_cache_inval_all(void); void nvg_enable_strict_checking_mode(void); +void nvg_system_shutdown(void); +void nvg_system_reboot(void); /* MCE helper functions */ void mce_enable_strict_checking(void); +void mce_system_shutdown(void); +void mce_system_reboot(void); #endif /* MCE_PRIVATE_H */ diff --git a/plat/nvidia/tegra/soc/t194/drivers/include/t194_nvg.h b/plat/nvidia/tegra/soc/t194/drivers/include/t194_nvg.h index cc0da80bf6..06cbb4a2e2 100644 --- a/plat/nvidia/tegra/soc/t194/drivers/include/t194_nvg.h +++ b/plat/nvidia/tegra/soc/t194/drivers/include/t194_nvg.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -128,6 +128,11 @@ typedef enum { TEGRA_NVG_SYSTEM_SC8 = 8 } tegra_nvg_system_sleep_state_t; +typedef enum { + TEGRA_NVG_SHUTDOWN = 0U, + TEGRA_NVG_REBOOT = 1U, +} tegra_nvg_shutdown_reboot_state_t; + // --------------------------------------------------------------------------- // NVG Data subformats // --------------------------------------------------------------------------- diff --git a/plat/nvidia/tegra/soc/t194/drivers/mce/mce.c b/plat/nvidia/tegra/soc/t194/drivers/mce/mce.c index ba8436be42..00c671bcca 100644 --- a/plat/nvidia/tegra/soc/t194/drivers/mce/mce.c +++ b/plat/nvidia/tegra/soc/t194/drivers/mce/mce.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -186,6 +186,7 @@ void mce_verify_firmware_version(void) } } +#if ENABLE_STRICT_CHECKING_MODE /******************************************************************************* * Handler to enable the strict checking mode ******************************************************************************/ @@ -235,3 +236,20 @@ void mce_enable_strict_checking(void) nvg_enable_strict_checking_mode(); } } +#endif + +/******************************************************************************* + * Handler to power down the entire system + ******************************************************************************/ +void mce_system_shutdown(void) +{ + nvg_system_shutdown(); +} + +/******************************************************************************* + * Handler to reboot the entire system + ******************************************************************************/ +void mce_system_reboot(void) +{ + nvg_system_reboot(); +} diff --git a/plat/nvidia/tegra/soc/t194/drivers/mce/nvg.c b/plat/nvidia/tegra/soc/t194/drivers/mce/nvg.c index 536ed57c00..a095fdd075 100644 --- a/plat/nvidia/tegra/soc/t194/drivers/mce/nvg.c +++ b/plat/nvidia/tegra/soc/t194/drivers/mce/nvg.c @@ -288,6 +288,7 @@ int32_t nvg_enter_cstate(uint32_t state, uint32_t wake_time) return ret; } +#if ENABLE_STRICT_CHECKING_MODE /* * Enable strict checking mode * @@ -300,3 +301,26 @@ void nvg_enable_strict_checking_mode(void) nvg_set_request_data(TEGRA_NVG_CHANNEL_SECURITY_CONFIG, params); } +#endif + +/* + * Request a reboot + * + * NVGDATA[0]: reboot command + */ +void nvg_system_reboot(void) +{ + /* issue command for reboot */ + nvg_set_request_data(TEGRA_NVG_CHANNEL_SHUTDOWN, TEGRA_NVG_REBOOT); +} + +/* + * Request a shutdown + * + * NVGDATA[0]: shutdown command + */ +void nvg_system_shutdown(void) +{ + /* issue command for shutdown */ + nvg_set_request_data(TEGRA_NVG_CHANNEL_SHUTDOWN, TEGRA_NVG_SHUTDOWN); +} diff --git a/plat/nvidia/tegra/soc/t194/plat_memctrl.c b/plat/nvidia/tegra/soc/t194/plat_memctrl.c index 54dbe7cdb4..57186503bb 100644 --- a/plat/nvidia/tegra/soc/t194/plat_memctrl.c +++ b/plat/nvidia/tegra/soc/t194/plat_memctrl.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -478,8 +478,9 @@ static void tegra194_memctrl_reconfig_mss_clients(void) mc_set_txn_override(VICSRD, CGID_TAG_DEFAULT, SO_DEV_ZERO, NO_OVERRIDE, NO_OVERRIDE); mc_set_txn_override(VICSRD1, CGID_TAG_DEFAULT, SO_DEV_ZERO, NO_OVERRIDE, NO_OVERRIDE); mc_set_txn_override(VICSWR, CGID_TAG_DEFAULT, SO_DEV_ZERO, NO_OVERRIDE, NO_OVERRIDE); - mc_set_txn_override(VIFALR, CGID_TAG_DEFAULT, SO_DEV_ZERO, NO_OVERRIDE, NO_OVERRIDE); - mc_set_txn_override(VIFALW, CGID_TAG_DEFAULT, SO_DEV_ZERO, NO_OVERRIDE, NO_OVERRIDE); + mc_set_txn_override(VIW, CGID_TAG_DEFAULT, SO_DEV_ZERO, FORCE_NON_COHERENT, FORCE_NON_COHERENT); + mc_set_txn_override(VIFALR, CGID_TAG_DEFAULT, SO_DEV_ZERO, FORCE_NON_COHERENT, FORCE_NON_COHERENT); + mc_set_txn_override(VIFALW, CGID_TAG_DEFAULT, SO_DEV_ZERO, FORCE_NON_COHERENT, FORCE_NON_COHERENT); mc_set_txn_override(XUSB_DEVR, CGID_TAG_DEFAULT, SO_DEV_ZERO, NO_OVERRIDE, NO_OVERRIDE); mc_set_txn_override(XUSB_DEVW, CGID_TAG_DEFAULT, SO_DEV_ZERO, FORCE_NON_COHERENT, FORCE_COHERENT_SNOOP); @@ -577,36 +578,29 @@ static void tegra194_memctrl_reconfig_mss_clients(void) tegra_mc_write_32(MC_PCFIFO_CLIENT_CONFIG7, reg_val); /* Set Order Id only for the clients having non zero order id */ - reg_val = MC_CLIENT_ORDER_ID_9_RESET_VAL & - mc_client_order_id(9, XUSB_HOSTW); + reg_val = mc_client_order_id(MC_CLIENT_ORDER_ID_9_RESET_VAL, 9, XUSB_HOSTW); tegra_mc_write_32(MC_CLIENT_ORDER_ID_9, reg_val); - reg_val = MC_CLIENT_ORDER_ID_27_RESET_VAL & - mc_client_order_id(27, PCIE0W); + reg_val = mc_client_order_id(MC_CLIENT_ORDER_ID_27_RESET_VAL, 27, PCIE0W); tegra_mc_write_32(MC_CLIENT_ORDER_ID_27, reg_val); - reg_val = MC_CLIENT_ORDER_ID_28_RESET_VAL & - mc_client_order_id(28, PCIE4W) & - mc_client_order_id(28, PCIE5W); + reg_val = mc_client_order_id(MC_CLIENT_ORDER_ID_28_RESET_VAL, 28, PCIE4W); + reg_val = mc_client_order_id(reg_val, 28, PCIE5W); tegra_mc_write_32(MC_CLIENT_ORDER_ID_28, reg_val); - /* Set VC Id only for the clients having different reset values */ - reg_val = MC_HUB_PC_VC_ID_0_RESET_VAL & - /* - * SDMMCRAB, SDMMCWAB, SESRD, SESWR, TSECSRD,TSECSRDB, - * TSECSWR and TSECSWRB clients - */ - mc_hub_vc_id(0, APB); + /* + * Set VC Id only for the clients having different reset values like + * SDMMCRAB, SDMMCWAB, SESRD, SESWR, TSECSRD,TSECSRDB, TSECSWR and + * TSECSWRB clients + */ + reg_val = mc_hub_vc_id(MC_HUB_PC_VC_ID_0_RESET_VAL, 0, APB); tegra_mc_write_32(MC_HUB_PC_VC_ID_0, reg_val); - reg_val = MC_HUB_PC_VC_ID_2_RESET_VAL & /* SDMMCRAB and SDMMCWAB clients */ - mc_hub_vc_id(2, SD); + reg_val = mc_hub_vc_id(MC_HUB_PC_VC_ID_2_RESET_VAL, 2, SD); tegra_mc_write_32(MC_HUB_PC_VC_ID_2, reg_val); - reg_val = MC_HUB_PC_VC_ID_4_RESET_VAL & - /* AXIR and AXIW clients */ - mc_hub_vc_id(4, NIC); + reg_val = mc_hub_vc_id(MC_HUB_PC_VC_ID_4_RESET_VAL, 4, NIC); tegra_mc_write_32(MC_HUB_PC_VC_ID_4, reg_val); wdata_0 = MC_CLIENT_HOTRESET_CTRL0_RESET_VAL; @@ -617,6 +611,9 @@ static void tegra194_memctrl_reconfig_mss_clients(void) wdata_2 = MC_CLIENT_HOTRESET_CTRL2_RESET_VAL; tegra_mc_write_32(MC_CLIENT_HOTRESET_CTRL2, wdata_2); + + reg_val = MC_COALESCE_CTRL_COALESCER_ENABLE; + tegra_mc_write_32(MC_COALESCE_CTRL, reg_val); } /******************************************************************************* diff --git a/plat/nvidia/tegra/soc/t194/plat_psci_handlers.c b/plat/nvidia/tegra/soc/t194/plat_psci_handlers.c index fa5ba6263f..b7e3703362 100644 --- a/plat/nvidia/tegra/soc/t194/plat_psci_handlers.c +++ b/plat/nvidia/tegra/soc/t194/plat_psci_handlers.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -77,6 +77,12 @@ int32_t tegra_soc_validate_power_state(uint32_t power_state, /* Sanity check the requested state id */ switch (state_id) { case PSTATE_ID_CORE_IDLE: + + /* Core idle request */ + req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE; + req_state->pwr_domain_state[MPIDR_AFFLVL1] = PSCI_LOCAL_STATE_RUN; + break; + case PSTATE_ID_CORE_POWERDN: /* Core powerdown request */ @@ -94,6 +100,25 @@ int32_t tegra_soc_validate_power_state(uint32_t power_state, return ret; } +int32_t tegra_soc_cpu_standby(plat_local_state_t cpu_state) +{ + uint32_t cpu = plat_my_core_pos(); + mce_cstate_info_t cstate_info = { 0 }; + + /* Program default wake mask */ + cstate_info.wake_mask = TEGRA194_CORE_WAKE_MASK; + cstate_info.update_wake_mask = 1; + mce_update_cstate_info(&cstate_info); + + /* Enter CPU idle */ + (void)mce_command_handler((uint64_t)MCE_CMD_ENTER_CSTATE, + (uint64_t)TEGRA_NVG_CORE_C6, + t19x_percpu_data[cpu].wake_time, + 0U); + + return PSCI_E_SUCCESS; +} + int32_t tegra_soc_pwr_domain_suspend(const psci_power_state_t *target_state) { const plat_local_state_t *pwr_domain_state; @@ -103,6 +128,7 @@ int32_t tegra_soc_pwr_domain_suspend(const psci_power_state_t *target_state) uint32_t val; mce_cstate_info_t sc7_cstate_info = { .cluster = (uint32_t)TEGRA_NVG_CLUSTER_CC6, + .ccplex = (uint32_t)TEGRA_NVG_CG_CG7, .system = (uint32_t)TEGRA_NVG_SYSTEM_SC7, .system_state_force = 1U, .update_wake_mask = 1U, @@ -117,15 +143,13 @@ int32_t tegra_soc_pwr_domain_suspend(const psci_power_state_t *target_state) stateid_afflvl2 = pwr_domain_state[PLAT_MAX_PWR_LVL] & TEGRA194_STATE_ID_MASK; - if ((stateid_afflvl0 == PSTATE_ID_CORE_IDLE) || - (stateid_afflvl0 == PSTATE_ID_CORE_POWERDN)) { + if ((stateid_afflvl0 == PSTATE_ID_CORE_POWERDN)) { - /* Enter CPU idle/powerdown */ - val = (stateid_afflvl0 == PSTATE_ID_CORE_IDLE) ? - (uint32_t)TEGRA_NVG_CORE_C6 : (uint32_t)TEGRA_NVG_CORE_C7; - ret = mce_command_handler((uint64_t)MCE_CMD_ENTER_CSTATE, (uint64_t)val, - t19x_percpu_data[cpu].wake_time, 0); - assert(ret == 0); + /* Enter CPU powerdown */ + (void)mce_command_handler((uint64_t)MCE_CMD_ENTER_CSTATE, + (uint64_t)TEGRA_NVG_CORE_C7, + t19x_percpu_data[cpu].wake_time, + 0U); } else if (stateid_afflvl2 == PSTATE_ID_SOC_POWERDN) { @@ -357,11 +381,13 @@ int32_t tegra_soc_pwr_domain_on_finish(const psci_power_state_t *target_state) */ if (stateid_afflvl2 == PSTATE_ID_SOC_POWERDN) { +#if ENABLE_STRICT_CHECKING_MODE /* * Enable strict checking after programming the GSC for * enabling TZSRAM and TZDRAM */ mce_enable_strict_checking(); +#endif /* Init SMMU */ tegra_smmu_init(); @@ -370,6 +396,52 @@ int32_t tegra_soc_pwr_domain_on_finish(const psci_power_state_t *target_state) tegra_se_resume(); /* + * Program XUSB STREAMIDs + * ====================== + * T19x XUSB has support for XUSB virtualization. It will + * have one physical function (PF) and four Virtual functions + * (VF) + * + * There were below two SIDs for XUSB until T186. + * 1) #define TEGRA_SID_XUSB_HOST 0x1bU + * 2) #define TEGRA_SID_XUSB_DEV 0x1cU + * + * We have below four new SIDs added for VF(s) + * 3) #define TEGRA_SID_XUSB_VF0 0x5dU + * 4) #define TEGRA_SID_XUSB_VF1 0x5eU + * 5) #define TEGRA_SID_XUSB_VF2 0x5fU + * 6) #define TEGRA_SID_XUSB_VF3 0x60U + * + * When virtualization is enabled then we have to disable SID + * override and program above SIDs in below newly added SID + * registers in XUSB PADCTL MMIO space. These registers are + * TZ protected and so need to be done in ATF. + * + * a) #define XUSB_PADCTL_HOST_AXI_STREAMID_PF_0 (0x136cU) + * b) #define XUSB_PADCTL_DEV_AXI_STREAMID_PF_0 (0x139cU) + * c) #define XUSB_PADCTL_HOST_AXI_STREAMID_VF_0 (0x1370U) + * d) #define XUSB_PADCTL_HOST_AXI_STREAMID_VF_1 (0x1374U) + * e) #define XUSB_PADCTL_HOST_AXI_STREAMID_VF_2 (0x1378U) + * f) #define XUSB_PADCTL_HOST_AXI_STREAMID_VF_3 (0x137cU) + * + * This change disables SID override and programs XUSB SIDs + * in above registers to support both virtualization and + * non-virtualization platforms + */ + mmio_write_32(TEGRA_XUSB_PADCTL_BASE + + XUSB_PADCTL_HOST_AXI_STREAMID_PF_0, TEGRA_SID_XUSB_HOST); + mmio_write_32(TEGRA_XUSB_PADCTL_BASE + + XUSB_PADCTL_HOST_AXI_STREAMID_VF_0, TEGRA_SID_XUSB_VF0); + mmio_write_32(TEGRA_XUSB_PADCTL_BASE + + XUSB_PADCTL_HOST_AXI_STREAMID_VF_1, TEGRA_SID_XUSB_VF1); + mmio_write_32(TEGRA_XUSB_PADCTL_BASE + + XUSB_PADCTL_HOST_AXI_STREAMID_VF_2, TEGRA_SID_XUSB_VF2); + mmio_write_32(TEGRA_XUSB_PADCTL_BASE + + XUSB_PADCTL_HOST_AXI_STREAMID_VF_3, TEGRA_SID_XUSB_VF3); + mmio_write_32(TEGRA_XUSB_PADCTL_BASE + + XUSB_PADCTL_DEV_AXI_STREAMID_PF_0, TEGRA_SID_XUSB_DEV); + + /* * Reset power state info for the last core doing SC7 * entry and exit, we set deepest power state as CC7 * and SC7 for SC7 entry which may not be requested by @@ -403,8 +475,7 @@ int32_t tegra_soc_pwr_domain_off(const psci_power_state_t *target_state) __dead2 void tegra_soc_prepare_system_off(void) { /* System power off */ - - /* SC8 */ + mce_system_shutdown(); wfi(); @@ -416,5 +487,8 @@ __dead2 void tegra_soc_prepare_system_off(void) int32_t tegra_soc_prepare_system_reset(void) { + /* System reboot */ + mce_system_reboot(); + return PSCI_E_SUCCESS; } diff --git a/plat/nvidia/tegra/soc/t194/plat_setup.c b/plat/nvidia/tegra/soc/t194/plat_setup.c index 8c5710557d..3b582444c0 100644 --- a/plat/nvidia/tegra/soc/t194/plat_setup.c +++ b/plat/nvidia/tegra/soc/t194/plat_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. + * Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -197,12 +197,13 @@ void plat_enable_console(int32_t id) ******************************************************************************/ void plat_early_platform_setup(void) { - /* sanity check MCE firmware compatibility */ mce_verify_firmware_version(); - /* Program XUSB STREAMIDs - * Xavier XUSB has support for XUSB virtualization. It will have one + /* + * Program XUSB STREAMIDs + * ====================== + * T19x XUSB has support for XUSB virtualization. It will have one * physical function (PF) and four Virtual function (VF) * * There were below two SIDs for XUSB until T186. @@ -227,14 +228,8 @@ void plat_early_platform_setup(void) * f) #define XUSB_PADCTL_HOST_AXI_STREAMID_VF_3 (0x137cU) * * This change disables SID override and programs XUSB SIDs in - * above registers to support both virtualization and non-virtualization - * - * Known Limitations: - * If xusb interface disables SMMU in XUSB DT in non-virtualization - * setup then there will be SMMU fault. We need to use WAR at - * https:\\git-master.nvidia.com/r/1529227/ to the issue. - * - * More details can be found in the bug 1971161 + * above registers to support both virtualization and + * non-virtualization platforms */ mmio_write_32(TEGRA_XUSB_PADCTL_BASE + XUSB_PADCTL_HOST_AXI_STREAMID_PF_0, TEGRA_SID_XUSB_HOST); @@ -298,9 +293,11 @@ plat_params_from_bl2_t *plat_get_bl31_plat_params(void) void plat_late_platform_setup(void) { +#if ENABLE_STRICT_CHECKING_MODE /* * Enable strict checking after programming the GSC for * enabling TZSRAM and TZDRAM */ mce_enable_strict_checking(); +#endif } diff --git a/plat/nvidia/tegra/soc/t194/platform_t194.mk b/plat/nvidia/tegra/soc/t194/platform_t194.mk index 5ec1af2966..f114b7e5be 100644 --- a/plat/nvidia/tegra/soc/t194/platform_t194.mk +++ b/plat/nvidia/tegra/soc/t194/platform_t194.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2019, NVIDIA CORPORATION. All rights reserved. +# Copyright (c) 2019-2020, NVIDIA CORPORATION. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause # @@ -8,17 +8,8 @@ ENABLE_CONSOLE_SPE := 0 $(eval $(call add_define,ENABLE_CONSOLE_SPE)) -ENABLE_ROC_FOR_ORDERING_CLIENT_REQUESTS := 0 -$(eval $(call add_define,ENABLE_ROC_FOR_ORDERING_CLIENT_REQUESTS)) - -RELOCATE_TO_BL31_BASE := 1 -$(eval $(call add_define,RELOCATE_TO_BL31_BASE)) - -ENABLE_CHIP_VERIFICATION_HARNESS := 0 -$(eval $(call add_define,ENABLE_CHIP_VERIFICATION_HARNESS)) - -ENABLE_SMMU_DEVICE := 1 -$(eval $(call add_define,ENABLE_SMMU_DEVICE)) +ENABLE_STRICT_CHECKING_MODE := 1 +$(eval $(call add_define,ENABLE_STRICT_CHECKING_MODE)) RESET_TO_BL31 := 1 diff --git a/plat/renesas/rcar/bl2_plat_setup.c b/plat/renesas/rcar/bl2_plat_setup.c index b4762f3309..193d80e708 100644 --- a/plat/renesas/rcar/bl2_plat_setup.c +++ b/plat/renesas/rcar/bl2_plat_setup.c @@ -415,6 +415,9 @@ static void bl2_populate_compatible_string(void *dt) uint32_t reg; int ret; + fdt_setprop_u32(dt, 0, "#address-cells", 2); + fdt_setprop_u32(dt, 0, "#size-cells", 2); + /* Populate compatible string */ rcar_get_board_type(&board_type, &board_rev); switch (board_type) { diff --git a/plat/rpi/common/rpi3_pm.c b/plat/rpi/common/rpi3_pm.c index 8c2d070c44..2a6bf076b0 100644 --- a/plat/rpi/common/rpi3_pm.c +++ b/plat/rpi/common/rpi3_pm.c @@ -123,6 +123,15 @@ static void rpi3_pwr_domain_off(const psci_power_state_t *target_state) #endif } +void __dead2 plat_secondary_cold_boot_setup(void); + +static void __dead2 +rpi3_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state) +{ + disable_mmu_el3(); + plat_secondary_cold_boot_setup(); +} + /******************************************************************************* * Platform handler called when a power domain is about to be turned on. The * mpidr determines the CPU to be turned on. @@ -224,6 +233,7 @@ static void __dead2 rpi3_system_off(void) static const plat_psci_ops_t plat_rpi3_psci_pm_ops = { .cpu_standby = rpi3_cpu_standby, .pwr_domain_off = rpi3_pwr_domain_off, + .pwr_domain_pwr_down_wfi = rpi3_pwr_domain_pwr_down_wfi, .pwr_domain_on = rpi3_pwr_domain_on, .pwr_domain_on_finish = rpi3_pwr_domain_on_finish, .system_off = rpi3_system_off, diff --git a/plat/st/common/bl2_io_storage.c b/plat/st/common/bl2_io_storage.c index 38b2a0bd79..3ec7d4048a 100644 --- a/plat/st/common/bl2_io_storage.c +++ b/plat/st/common/bl2_io_storage.c @@ -14,11 +14,17 @@ #include <drivers/io/io_block.h> #include <drivers/io/io_driver.h> #include <drivers/io/io_dummy.h> +#include <drivers/io/io_mtd.h> #include <drivers/io/io_storage.h> #include <drivers/mmc.h> #include <drivers/partition/partition.h> +#include <drivers/raw_nand.h> +#include <drivers/spi_nand.h> +#include <drivers/spi_nor.h> #include <drivers/st/io_mmc.h> #include <drivers/st/io_stm32image.h> +#include <drivers/st/stm32_fmc2_nand.h> +#include <drivers/st/stm32_qspi.h> #include <drivers/st/stm32_sdmmc2.h> #include <lib/mmio.h> #include <lib/utils.h> @@ -30,7 +36,9 @@ static uintptr_t dummy_dev_handle; static uintptr_t dummy_dev_spec; static uintptr_t image_dev_handle; +static uintptr_t storage_dev_handle; +#if STM32MP_SDMMC || STM32MP_EMMC static io_block_spec_t gpt_block_spec = { .offset = 0, .length = 34 * MMC_BLOCK_SIZE, /* Size of GPT table */ @@ -51,8 +59,41 @@ static const io_block_dev_spec_t mmc_block_dev_spec = { .block_size = MMC_BLOCK_SIZE, }; -static uintptr_t storage_dev_handle; static const io_dev_connector_t *mmc_dev_con; +#endif /* STM32MP_SDMMC || STM32MP_EMMC */ + +#if STM32MP_SPI_NOR +static io_mtd_dev_spec_t spi_nor_dev_spec = { + .ops = { + .init = spi_nor_init, + .read = spi_nor_read, + }, +}; +#endif + +#if STM32MP_RAW_NAND +static io_mtd_dev_spec_t nand_dev_spec = { + .ops = { + .init = nand_raw_init, + .read = nand_read, + }, +}; + +static const io_dev_connector_t *nand_dev_con; +#endif + +#if STM32MP_SPI_NAND +static io_mtd_dev_spec_t spi_nand_dev_spec = { + .ops = { + .init = spi_nand_init, + .read = nand_read, + }, +}; +#endif + +#if STM32MP_SPI_NAND || STM32MP_SPI_NOR +static const io_dev_connector_t *spi_dev_con; +#endif #ifdef AARCH32_SP_OPTEE static const struct stm32image_part_info optee_header_partition_spec = { @@ -96,7 +137,7 @@ enum { IMG_IDX_NUM }; -static struct stm32image_device_info stm32image_dev_info_spec = { +static struct stm32image_device_info stm32image_dev_info_spec __unused = { .lba_size = MMC_BLOCK_SIZE, .part_info[IMG_IDX_BL33] = { .name = BL33_IMAGE_NAME, @@ -123,7 +164,7 @@ static io_block_spec_t stm32image_block_spec = { .length = 0, }; -static const io_dev_connector_t *stm32image_dev_con; +static const io_dev_connector_t *stm32image_dev_con __unused; static int open_dummy(const uintptr_t spec); static int open_image(const uintptr_t spec); @@ -169,11 +210,13 @@ static const struct plat_io_policy policies[] = { .image_spec = (uintptr_t)&bl33_partition_spec, .check = open_image }, +#if STM32MP_SDMMC || STM32MP_EMMC [GPT_IMAGE_ID] = { .dev_handle = &storage_dev_handle, .image_spec = (uintptr_t)&gpt_block_spec, .check = open_storage }, +#endif [STM32_IMAGE_ID] = { .dev_handle = &storage_dev_handle, .image_spec = (uintptr_t)&stm32image_block_spec, @@ -205,6 +248,15 @@ static void print_boot_device(boot_api_context_t *boot_context) case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_EMMC: INFO("Using EMMC\n"); break; + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_QSPI: + INFO("Using QSPI NOR\n"); + break; + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_FMC: + INFO("Using FMC NAND\n"); + break; + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_QSPI: + INFO("Using SPI NAND\n"); + break; default: ERROR("Boot interface not found\n"); panic(); @@ -216,6 +268,7 @@ static void print_boot_device(boot_api_context_t *boot_context) } } +#if STM32MP_SDMMC || STM32MP_EMMC static void boot_mmc(enum mmc_device_type mmc_dev_type, uint16_t boot_interface_instance) { @@ -305,6 +358,167 @@ static void boot_mmc(enum mmc_device_type mmc_dev_type, &image_dev_handle); assert(io_result == 0); } +#endif /* STM32MP_SDMMC || STM32MP_EMMC */ + +#if STM32MP_SPI_NOR +static void boot_spi_nor(boot_api_context_t *boot_context) +{ + int io_result __unused; + uint8_t idx; + struct stm32image_part_info *part; + + io_result = stm32_qspi_init(); + assert(io_result == 0); + + io_result = register_io_dev_mtd(&spi_dev_con); + assert(io_result == 0); + + /* Open connections to device */ + io_result = io_dev_open(spi_dev_con, + (uintptr_t)&spi_nor_dev_spec, + &storage_dev_handle); + assert(io_result == 0); + + stm32image_dev_info_spec.device_size = spi_nor_dev_spec.device_size; + + idx = IMG_IDX_BL33; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = STM32MP_NOR_BL33_OFFSET; + part->bkp_offset = 0U; + +#ifdef AARCH32_SP_OPTEE + idx = IMG_IDX_OPTEE_HEADER; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = STM32MP_NOR_TEEH_OFFSET; + part->bkp_offset = 0U; + + idx = IMG_IDX_OPTEE_PAGED; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = STM32MP_NOR_TEED_OFFSET; + part->bkp_offset = 0U; + + idx = IMG_IDX_OPTEE_PAGER; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = STM32MP_NOR_TEEX_OFFSET; + part->bkp_offset = 0U; +#endif + + io_result = register_io_dev_stm32image(&stm32image_dev_con); + assert(io_result == 0); + + io_result = io_dev_open(stm32image_dev_con, + (uintptr_t)&stm32image_dev_info_spec, + &image_dev_handle); + assert(io_result == 0); +} +#endif /* STM32MP_SPI_NOR */ + +#if STM32MP_RAW_NAND +static void boot_fmc2_nand(boot_api_context_t *boot_context) +{ + int io_result __unused; + uint8_t idx; + struct stm32image_part_info *part; + + io_result = stm32_fmc2_init(); + assert(io_result == 0); + + /* Register the IO device on this platform */ + io_result = register_io_dev_mtd(&nand_dev_con); + assert(io_result == 0); + + /* Open connections to device */ + io_result = io_dev_open(nand_dev_con, (uintptr_t)&nand_dev_spec, + &storage_dev_handle); + assert(io_result == 0); + + stm32image_dev_info_spec.device_size = nand_dev_spec.device_size; + + idx = IMG_IDX_BL33; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = STM32MP_NAND_BL33_OFFSET; + part->bkp_offset = nand_dev_spec.erase_size; + +#ifdef AARCH32_SP_OPTEE + idx = IMG_IDX_OPTEE_HEADER; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = STM32MP_NAND_TEEH_OFFSET; + part->bkp_offset = nand_dev_spec.erase_size; + + idx = IMG_IDX_OPTEE_PAGED; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = STM32MP_NAND_TEED_OFFSET; + part->bkp_offset = nand_dev_spec.erase_size; + + idx = IMG_IDX_OPTEE_PAGER; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = STM32MP_NAND_TEEX_OFFSET; + part->bkp_offset = nand_dev_spec.erase_size; +#endif + + io_result = register_io_dev_stm32image(&stm32image_dev_con); + assert(io_result == 0); + + io_result = io_dev_open(stm32image_dev_con, + (uintptr_t)&stm32image_dev_info_spec, + &image_dev_handle); + assert(io_result == 0); +} +#endif /* STM32MP_RAW_NAND */ + +#if STM32MP_SPI_NAND +static void boot_spi_nand(boot_api_context_t *boot_context) +{ + int io_result __unused; + uint8_t idx; + struct stm32image_part_info *part; + + io_result = stm32_qspi_init(); + assert(io_result == 0); + + io_result = register_io_dev_mtd(&spi_dev_con); + assert(io_result == 0); + + /* Open connections to device */ + io_result = io_dev_open(spi_dev_con, + (uintptr_t)&spi_nand_dev_spec, + &storage_dev_handle); + assert(io_result == 0); + + stm32image_dev_info_spec.device_size = + spi_nand_dev_spec.device_size; + + idx = IMG_IDX_BL33; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = STM32MP_NAND_BL33_OFFSET; + part->bkp_offset = spi_nand_dev_spec.erase_size; + +#ifdef AARCH32_SP_OPTEE + idx = IMG_IDX_OPTEE_HEADER; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = STM32MP_NAND_TEEH_OFFSET; + part->bkp_offset = spi_nand_dev_spec.erase_size; + + idx = IMG_IDX_OPTEE_PAGED; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = STM32MP_NAND_TEED_OFFSET; + part->bkp_offset = spi_nand_dev_spec.erase_size; + + idx = IMG_IDX_OPTEE_PAGER; + part = &stm32image_dev_info_spec.part_info[idx]; + part->part_offset = STM32MP_NAND_TEEX_OFFSET; + part->bkp_offset = spi_nand_dev_spec.erase_size; +#endif + + io_result = register_io_dev_stm32image(&stm32image_dev_con); + assert(io_result == 0); + + io_result = io_dev_open(stm32image_dev_con, + (uintptr_t)&stm32image_dev_info_spec, + &image_dev_handle); + assert(io_result == 0); +} +#endif /* STM32MP_SPI_NAND */ void stm32mp_io_setup(void) { @@ -328,14 +542,36 @@ void stm32mp_io_setup(void) assert(io_result == 0); switch (boot_context->boot_interface_selected) { +#if STM32MP_SDMMC case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_SD: dmbsy(); boot_mmc(MMC_IS_SD, boot_context->boot_interface_instance); break; +#endif +#if STM32MP_EMMC case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_EMMC: dmbsy(); boot_mmc(MMC_IS_EMMC, boot_context->boot_interface_instance); break; +#endif +#if STM32MP_SPI_NOR + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_QSPI: + dmbsy(); + boot_spi_nor(boot_context); + break; +#endif +#if STM32MP_RAW_NAND + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_FMC: + dmbsy(); + boot_fmc2_nand(boot_context); + break; +#endif +#if STM32MP_SPI_NAND + case BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_QSPI: + dmbsy(); + boot_spi_nand(boot_context); + break; +#endif default: ERROR("Boot interface %d not supported\n", diff --git a/plat/st/common/include/stm32mp_dt.h b/plat/st/common/include/stm32mp_dt.h index 74b01b3aa5..a29d9148d0 100644 --- a/plat/st/common/include/stm32mp_dt.h +++ b/plat/st/common/include/stm32mp_dt.h @@ -32,6 +32,8 @@ uint32_t fdt_read_uint32_default(int node, const char *prop_name, uint32_t dflt_value); int fdt_read_uint32_array(int node, const char *prop_name, uint32_t *array, uint32_t count); +int fdt_get_reg_props_by_name(int node, const char *name, uintptr_t *base, + size_t *size); int dt_set_stdout_pinctrl(void); void dt_fill_device_info(struct dt_node_info *info, int node); int dt_get_node(struct dt_node_info *info, int offset, const char *compat); diff --git a/plat/st/common/stm32mp_dt.c b/plat/st/common/stm32mp_dt.c index 17da4904ac..4fa796f0c8 100644 --- a/plat/st/common/stm32mp_dt.c +++ b/plat/st/common/stm32mp_dt.c @@ -93,6 +93,46 @@ uint8_t fdt_get_status(int node) } /******************************************************************************* + * This function returns the address cells from the node parent. + * Returns: + * - #address-cells value if success. + * - invalid value if error. + * - a default value if undefined #address-cells property as per libfdt + * implementation. + ******************************************************************************/ +int fdt_get_node_parent_address_cells(int node) +{ + int parent; + + parent = fdt_parent_offset(fdt, node); + if (parent < 0) { + return -FDT_ERR_NOTFOUND; + } + + return fdt_address_cells(fdt, parent); +} + +/******************************************************************************* + * This function returns the size cells from the node parent. + * Returns: + * - #size-cells value if success. + * - invalid value if error. + * - a default value if undefined #size-cells property as per libfdt + * implementation. + ******************************************************************************/ +int fdt_get_node_parent_size_cells(int node) +{ + int parent; + + parent = fdt_parent_offset(fdt, node); + if (parent < 0) { + return -FDT_ERR_NOTFOUND; + } + + return fdt_size_cells(fdt, parent); +} + +/******************************************************************************* * This function reads a value of a node property (generic use of fdt * library). * Returns value if success, and a default value if property not found. @@ -146,6 +186,46 @@ int fdt_read_uint32_array(int node, const char *prop_name, uint32_t *array, } /******************************************************************************* + * This function fills reg node info (base & size) with an index found by + * checking the reg-names node. + * Returns 0 on success and a negative FDT error code on failure. + ******************************************************************************/ +int fdt_get_reg_props_by_name(int node, const char *name, uintptr_t *base, + size_t *size) +{ + const fdt32_t *cuint; + int index, len; + + assert((fdt_get_node_parent_address_cells(node) == 1) && + (fdt_get_node_parent_size_cells(node) == 1)); + + index = fdt_stringlist_search(fdt, node, "reg-names", name); + if (index < 0) { + return index; + } + + cuint = fdt_getprop(fdt, node, "reg", &len); + if (cuint == NULL) { + return -FDT_ERR_NOTFOUND; + } + + if ((index * (int)sizeof(uint32_t)) > len) { + return -FDT_ERR_BADVALUE; + } + + cuint += index << 1; + if (base != NULL) { + *base = fdt32_to_cpu(*cuint); + } + cuint++; + if (size != NULL) { + *size = fdt32_to_cpu(*cuint); + } + + return 0; +} + +/******************************************************************************* * This function gets the stdout path node. * It reads the value indicated inside the device tree. * Returns node offset on success and a negative FDT error code on failure. @@ -215,6 +295,8 @@ void dt_fill_device_info(struct dt_node_info *info, int node) { const fdt32_t *cuint; + assert(fdt_get_node_parent_address_cells(node) == 1); + cuint = fdt_getprop(fdt, node, "reg", NULL); if (cuint != NULL) { info->base = fdt32_to_cpu(*cuint); @@ -309,6 +391,9 @@ uintptr_t dt_get_ddrctrl_base(void) return 0; } + assert((fdt_get_node_parent_address_cells(node) == 1) && + (fdt_get_node_parent_size_cells(node) == 1)); + if (fdt_read_uint32_array(node, "reg", array, 4) < 0) { return 0; } @@ -331,6 +416,9 @@ uintptr_t dt_get_ddrphyc_base(void) return 0; } + assert((fdt_get_node_parent_address_cells(node) == 1) && + (fdt_get_node_parent_size_cells(node) == 1)); + if (fdt_read_uint32_array(node, "reg", array, 4) < 0) { return 0; } @@ -353,6 +441,8 @@ uintptr_t dt_get_pwr_base(void) return 0; } + assert(fdt_get_node_parent_address_cells(node) == 1); + cuint = fdt_getprop(fdt, node, "reg", NULL); if (cuint == NULL) { return 0; @@ -415,6 +505,8 @@ uintptr_t dt_get_syscfg_base(void) return 0; } + assert(fdt_get_node_parent_address_cells(node) == 1); + cuint = fdt_getprop(fdt, node, "reg", NULL); if (cuint == NULL) { return 0; diff --git a/plat/st/stm32mp1/include/boot_api.h b/plat/st/stm32mp1/include/boot_api.h index c80aef6dd7..c16639ac40 100644 --- a/plat/st/stm32mp1/include/boot_api.h +++ b/plat/st/stm32mp1/include/boot_api.h @@ -33,6 +33,15 @@ /* Boot occurred on EMMC */ #define BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_EMMC 0x2U +/* Boot occurred on FMC */ +#define BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_FMC 0x3U + +/* Boot occurred on QSPI NOR */ +#define BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NOR_QSPI 0x4U + +/* Boot occurred on QSPI NAND */ +#define BOOT_API_CTX_BOOT_INTERFACE_SEL_FLASH_NAND_QSPI 0x7U + /** * @brief Possible value of boot context field 'EmmcXferStatus' */ diff --git a/plat/st/stm32mp1/include/platform_def.h b/plat/st/stm32mp1/include/platform_def.h index 263e6d6e1f..450a9d404c 100644 --- a/plat/st/stm32mp1/include/platform_def.h +++ b/plat/st/stm32mp1/include/platform_def.h @@ -51,6 +51,7 @@ #define MAX_IO_DEVICES U(4) #define MAX_IO_HANDLES U(4) #define MAX_IO_BLOCK_DEVICES U(1) +#define MAX_IO_MTD_DEVICES U(1) /******************************************************************************* * BL2 specific defines. diff --git a/plat/st/stm32mp1/include/stm32mp1_boot_device.h b/plat/st/stm32mp1/include/stm32mp1_boot_device.h new file mode 100644 index 0000000000..a74598395f --- /dev/null +++ b/plat/st/stm32mp1/include/stm32mp1_boot_device.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STM32MP1_BOOT_DEVICE_H +#define STM32MP1_BOOT_DEVICE_H + +#include <drivers/raw_nand.h> +#include <drivers/spi_nand.h> +#include <drivers/spi_nor.h> + +int plat_get_raw_nand_data(struct rawnand_device *device); +int plat_get_spi_nand_data(struct spinand_device *device); +int plat_get_nor_data(struct nor_device *device); + +#endif /* STM32MP1_BOOT_DEVICE_H */ diff --git a/plat/st/stm32mp1/platform.mk b/plat/st/stm32mp1/platform.mk index 90b3e3c1e5..b86287b669 100644 --- a/plat/st/stm32mp1/platform.mk +++ b/plat/st/stm32mp1/platform.mk @@ -24,6 +24,29 @@ PLAT_PARTITION_MAX_ENTRIES := $(shell echo $$(($(STM32_TF_A_COPIES) + 1))) endif $(eval $(call add_define,PLAT_PARTITION_MAX_ENTRIES)) +# Boot devices +STM32MP_EMMC ?= 0 +STM32MP_SDMMC ?= 0 +STM32MP_RAW_NAND ?= 0 +STM32MP_SPI_NAND ?= 0 +STM32MP_SPI_NOR ?= 0 + +ifeq ($(filter 1,${STM32MP_EMMC} ${STM32MP_SDMMC} ${STM32MP_RAW_NAND} \ + ${STM32MP_SPI_NAND} ${STM32MP_SPI_NOR}),) +$(error "No boot device driver is enabled") +endif + +$(eval $(call assert_boolean,STM32MP_EMMC)) +$(eval $(call assert_boolean,STM32MP_SDMMC)) +$(eval $(call assert_boolean,STM32MP_RAW_NAND)) +$(eval $(call assert_boolean,STM32MP_SPI_NAND)) +$(eval $(call assert_boolean,STM32MP_SPI_NOR)) +$(eval $(call add_define,STM32MP_EMMC)) +$(eval $(call add_define,STM32MP_SDMMC)) +$(eval $(call add_define,STM32MP_RAW_NAND)) +$(eval $(call add_define,STM32MP_SPI_NAND)) +$(eval $(call add_define,STM32MP_SPI_NOR)) + PLAT_INCLUDES := -Iplat/st/common/include/ PLAT_INCLUDES += -Iplat/st/stm32mp1/include/ @@ -70,6 +93,7 @@ PLAT_BL_COMMON_SOURCES += drivers/arm/tzc/tzc400.c \ BL2_SOURCES += drivers/io/io_block.c \ drivers/io/io_dummy.c \ + drivers/io/io_mtd.c \ drivers/io/io_storage.c \ drivers/st/crypto/stm32_hash.c \ drivers/st/io/io_stm32image.c \ @@ -77,11 +101,40 @@ BL2_SOURCES += drivers/io/io_block.c \ plat/st/common/bl2_io_storage.c \ plat/st/stm32mp1/bl2_plat_setup.c +ifneq ($(filter 1,${STM32MP_EMMC} ${STM32MP_SDMMC}),) BL2_SOURCES += drivers/mmc/mmc.c \ drivers/partition/gpt.c \ drivers/partition/partition.c \ drivers/st/io/io_mmc.c \ drivers/st/mmc/stm32_sdmmc2.c +endif + +ifeq (${STM32MP_RAW_NAND},1) +$(eval $(call add_define_val,NAND_ONFI_DETECT,1)) +BL2_SOURCES += drivers/mtd/nand/raw_nand.c \ + drivers/st/fmc/stm32_fmc2_nand.c +endif + +ifeq (${STM32MP_SPI_NAND},1) +BL2_SOURCES += drivers/mtd/nand/spi_nand.c +endif + +ifeq (${STM32MP_SPI_NOR},1) +BL2_SOURCES += drivers/mtd/nor/spi_nor.c +endif + +ifneq ($(filter 1,${STM32MP_SPI_NAND} ${STM32MP_SPI_NOR}),) +BL2_SOURCES += drivers/mtd/spi-mem/spi_mem.c \ + drivers/st/spi/stm32_qspi.c +endif + +ifneq ($(filter 1,${STM32MP_RAW_NAND} ${STM32MP_SPI_NAND}),) +BL2_SOURCES += drivers/mtd/nand/core.c +endif + +ifneq ($(filter 1,${STM32MP_RAW_NAND} ${STM32MP_SPI_NAND} ${STM32MP_SPI_NOR}),) +BL2_SOURCES += plat/st/stm32mp1/stm32mp1_boot_device.c +endif BL2_SOURCES += drivers/st/ddr/stm32mp1_ddr.c \ drivers/st/ddr/stm32mp1_ram.c diff --git a/plat/st/stm32mp1/stm32mp1_boot_device.c b/plat/st/stm32mp1/stm32mp1_boot_device.c new file mode 100644 index 0000000000..2d8eccff7d --- /dev/null +++ b/plat/st/stm32mp1/stm32mp1_boot_device.c @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2019, STMicroelectronics - All Rights Reserved + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <errno.h> + +#include <drivers/nand.h> +#include <lib/utils.h> +#include <plat/common/platform.h> + +#define SZ_512 0x200U +#define SZ_64M 0x4000000U + +#if STM32MP_RAW_NAND || STM32MP_SPI_NAND +static int get_data_from_otp(struct nand_device *nand_dev, bool is_slc) +{ + int result; + uint32_t nand_param; + + /* Check if NAND parameters are stored in OTP */ + result = bsec_shadow_read_otp(&nand_param, NAND_OTP); + if (result != BSEC_OK) { + ERROR("BSEC: NAND_OTP Error %i\n", result); + return -EACCES; + } + + if (nand_param == 0U) { + return 0; + } + + if ((nand_param & NAND_PARAM_STORED_IN_OTP) == 0U) { + goto ecc; + } + + /* NAND parameter shall be read from OTP */ + if ((nand_param & NAND_WIDTH_MASK) != 0U) { + nand_dev->buswidth = NAND_BUS_WIDTH_16; + } else { + nand_dev->buswidth = NAND_BUS_WIDTH_8; + } + + switch ((nand_param & NAND_PAGE_SIZE_MASK) >> NAND_PAGE_SIZE_SHIFT) { + case NAND_PAGE_SIZE_2K: + nand_dev->page_size = 0x800U; + break; + + case NAND_PAGE_SIZE_4K: + nand_dev->page_size = 0x1000U; + break; + + case NAND_PAGE_SIZE_8K: + nand_dev->page_size = 0x2000U; + break; + + default: + ERROR("Cannot read NAND page size\n"); + return -EINVAL; + } + + switch ((nand_param & NAND_BLOCK_SIZE_MASK) >> NAND_BLOCK_SIZE_SHIFT) { + case NAND_BLOCK_SIZE_64_PAGES: + nand_dev->block_size = 64U * nand_dev->page_size; + break; + + case NAND_BLOCK_SIZE_128_PAGES: + nand_dev->block_size = 128U * nand_dev->page_size; + break; + + case NAND_BLOCK_SIZE_256_PAGES: + nand_dev->block_size = 256U * nand_dev->page_size; + break; + + default: + ERROR("Cannot read NAND block size\n"); + return -EINVAL; + } + + nand_dev->size = ((nand_param & NAND_BLOCK_NB_MASK) >> + NAND_BLOCK_NB_SHIFT) * + NAND_BLOCK_NB_UNIT * nand_dev->block_size; + +ecc: + if (is_slc) { + switch ((nand_param & NAND_ECC_BIT_NB_MASK) >> + NAND_ECC_BIT_NB_SHIFT) { + case NAND_ECC_BIT_NB_1_BITS: + nand_dev->ecc.max_bit_corr = 1U; + break; + + case NAND_ECC_BIT_NB_4_BITS: + nand_dev->ecc.max_bit_corr = 4U; + break; + + case NAND_ECC_BIT_NB_8_BITS: + nand_dev->ecc.max_bit_corr = 8U; + break; + + case NAND_ECC_ON_DIE: + nand_dev->ecc.mode = NAND_ECC_ONDIE; + break; + + default: + if (nand_dev->ecc.max_bit_corr == 0U) { + ERROR("No valid eccbit number\n"); + return -EINVAL; + } + } + } else { + /* Selected multiple plane NAND */ + if ((nand_param & NAND_PLANE_BIT_NB_MASK) != 0U) { + nand_dev->nb_planes = 2U; + } else { + nand_dev->nb_planes = 1U; + } + } + + VERBOSE("OTP: Block %i Page %i Size %lli\n", nand_dev->block_size, + nand_dev->page_size, nand_dev->size); + + return 0; +} +#endif /* STM32MP_RAW_NAND || STM32MP_SPI_NAND */ + +#if STM32MP_RAW_NAND +int plat_get_raw_nand_data(struct rawnand_device *device) +{ + device->nand_dev->ecc.mode = NAND_ECC_HW; + device->nand_dev->ecc.size = SZ_512; + + return get_data_from_otp(device->nand_dev, true); +} +#endif + +#if STM32MP_SPI_NAND +int plat_get_spi_nand_data(struct spinand_device *device) +{ + zeromem(&device->spi_read_cache_op, sizeof(struct spi_mem_op)); + device->spi_read_cache_op.cmd.opcode = SPI_NAND_OP_READ_FROM_CACHE_4X; + device->spi_read_cache_op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + device->spi_read_cache_op.addr.nbytes = 2U; + device->spi_read_cache_op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + device->spi_read_cache_op.dummy.nbytes = 1U; + device->spi_read_cache_op.dummy.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + device->spi_read_cache_op.data.buswidth = SPI_MEM_BUSWIDTH_4_LINE; + device->spi_read_cache_op.data.dir = SPI_MEM_DATA_IN; + + return get_data_from_otp(device->nand_dev, false); +} +#endif + +#if STM32MP_SPI_NOR +int plat_get_nor_data(struct nor_device *device) +{ + device->size = SZ_64M; + + zeromem(&device->read_op, sizeof(struct spi_mem_op)); + device->read_op.cmd.opcode = SPI_NOR_OP_READ_1_1_4; + device->read_op.cmd.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + device->read_op.addr.nbytes = 3U; + device->read_op.addr.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + device->read_op.dummy.nbytes = 1U; + device->read_op.dummy.buswidth = SPI_MEM_BUSWIDTH_1_LINE; + device->read_op.data.buswidth = SPI_MEM_BUSWIDTH_4_LINE; + device->read_op.data.dir = SPI_MEM_DATA_IN; + + return 0; +} +#endif diff --git a/plat/st/stm32mp1/stm32mp1_def.h b/plat/st/stm32mp1/stm32mp1_def.h index a40852bde7..11b01ab882 100644 --- a/plat/st/stm32mp1/stm32mp1_def.h +++ b/plat/st/stm32mp1/stm32mp1_def.h @@ -23,6 +23,7 @@ #include <stm32mp_common.h> #include <stm32mp_dt.h> #include <stm32mp_shres_helpers.h> +#include <stm32mp1_boot_device.h> #include <stm32mp1_dbgmcu.h> #include <stm32mp1_private.h> #endif @@ -74,7 +75,7 @@ enum ddr_type { #endif /* Section used inside TF binaries */ -#define STM32MP_PARAM_LOAD_SIZE U(0x00002400) /* 9 Ko for param */ +#define STM32MP_PARAM_LOAD_SIZE U(0x00002400) /* 9 KB for param */ /* 256 Octets reserved for header */ #define STM32MP_HEADER_SIZE U(0x00000100) @@ -95,9 +96,9 @@ enum ddr_type { STM32MP_OPTEE_BASE) #else #if STACK_PROTECTOR_ENABLED -#define STM32MP_BL32_SIZE U(0x00012000) /* 72 Ko for BL32 */ +#define STM32MP_BL32_SIZE U(0x00012000) /* 72 KB for BL32 */ #else -#define STM32MP_BL32_SIZE U(0x00011000) /* 68 Ko for BL32 */ +#define STM32MP_BL32_SIZE U(0x00011000) /* 68 KB for BL32 */ #endif #endif @@ -107,23 +108,23 @@ enum ddr_type { #ifdef AARCH32_SP_OPTEE #if STACK_PROTECTOR_ENABLED -#define STM32MP_BL2_SIZE U(0x00019000) /* 100 Ko for BL2 */ +#define STM32MP_BL2_SIZE U(0x0001A000) /* 100 KB for BL2 */ #else -#define STM32MP_BL2_SIZE U(0x00017000) /* 92 Ko for BL2 */ +#define STM32MP_BL2_SIZE U(0x00018000) /* 92 KB for BL2 */ #endif #else #if STACK_PROTECTOR_ENABLED -#define STM32MP_BL2_SIZE U(0x00018000) /* 96 Ko for BL2 */ +#define STM32MP_BL2_SIZE U(0x00019000) /* 96 KB for BL2 */ #else -#define STM32MP_BL2_SIZE U(0x00016000) /* 88 Ko for BL2 */ +#define STM32MP_BL2_SIZE U(0x00017000) /* 88 KB for BL2 */ #endif #endif #define STM32MP_BL2_BASE (STM32MP_BL32_BASE - \ STM32MP_BL2_SIZE) -/* BL2 and BL32/sp_min require 5 tables */ -#define MAX_XLAT_TABLES 5 +/* BL2 and BL32/sp_min require 4 tables */ +#define MAX_XLAT_TABLES U(4) /* 16 KB for mapping */ /* * MAX_MMAP_REGIONS is usually: @@ -137,13 +138,33 @@ enum ddr_type { #endif /* DTB initialization value */ -#define STM32MP_DTB_SIZE U(0x00005000) /* 20Ko for DTB */ +#define STM32MP_DTB_SIZE U(0x00005000) /* 20 KB for DTB */ #define STM32MP_DTB_BASE (STM32MP_BL2_BASE - \ STM32MP_DTB_SIZE) #define STM32MP_BL33_BASE (STM32MP_DDR_BASE + U(0x100000)) +/* Define maximum page size for NAND devices */ +#define PLATFORM_MTD_MAX_PAGE_SIZE U(0x1000) + +/******************************************************************************* + * STM32MP1 RAW partition offset for MTD devices + ******************************************************************************/ +#define STM32MP_NOR_BL33_OFFSET U(0x00080000) +#ifdef AARCH32_SP_OPTEE +#define STM32MP_NOR_TEEH_OFFSET U(0x00280000) +#define STM32MP_NOR_TEED_OFFSET U(0x002C0000) +#define STM32MP_NOR_TEEX_OFFSET U(0x00300000) +#endif + +#define STM32MP_NAND_BL33_OFFSET U(0x00200000) +#ifdef AARCH32_SP_OPTEE +#define STM32MP_NAND_TEEH_OFFSET U(0x00600000) +#define STM32MP_NAND_TEED_OFFSET U(0x00680000) +#define STM32MP_NAND_TEEX_OFFSET U(0x00700000) +#endif + /******************************************************************************* * STM32MP1 device/io map related constants (used for MMU) ******************************************************************************/ @@ -266,6 +287,7 @@ enum ddr_type { /* OTP offsets */ #define DATA0_OTP U(0) #define PART_NUMBER_OTP U(1) +#define NAND_OTP U(9) #define PACKAGE_OTP U(16) #define HW2_OTP U(18) @@ -289,6 +311,45 @@ enum ddr_type { /* HW2 OTP */ #define HW2_OTP_PRODUCT_BELOW_2V5 BIT(13) +/* NAND OTP */ +/* NAND parameter storage flag */ +#define NAND_PARAM_STORED_IN_OTP BIT(31) + +/* NAND page size in bytes */ +#define NAND_PAGE_SIZE_MASK GENMASK_32(30, 29) +#define NAND_PAGE_SIZE_SHIFT 29 +#define NAND_PAGE_SIZE_2K U(0) +#define NAND_PAGE_SIZE_4K U(1) +#define NAND_PAGE_SIZE_8K U(2) + +/* NAND block size in pages */ +#define NAND_BLOCK_SIZE_MASK GENMASK_32(28, 27) +#define NAND_BLOCK_SIZE_SHIFT 27 +#define NAND_BLOCK_SIZE_64_PAGES U(0) +#define NAND_BLOCK_SIZE_128_PAGES U(1) +#define NAND_BLOCK_SIZE_256_PAGES U(2) + +/* NAND number of block (in unit of 256 blocs) */ +#define NAND_BLOCK_NB_MASK GENMASK_32(26, 19) +#define NAND_BLOCK_NB_SHIFT 19 +#define NAND_BLOCK_NB_UNIT U(256) + +/* NAND bus width in bits */ +#define NAND_WIDTH_MASK BIT(18) +#define NAND_WIDTH_SHIFT 18 + +/* NAND number of ECC bits per 512 bytes */ +#define NAND_ECC_BIT_NB_MASK GENMASK_32(17, 15) +#define NAND_ECC_BIT_NB_SHIFT 15 +#define NAND_ECC_BIT_NB_UNSET U(0) +#define NAND_ECC_BIT_NB_1_BITS U(1) +#define NAND_ECC_BIT_NB_4_BITS U(2) +#define NAND_ECC_BIT_NB_8_BITS U(3) +#define NAND_ECC_ON_DIE U(4) + +/* NAND number of planes */ +#define NAND_PLANE_BIT_NB_MASK BIT(14) + /******************************************************************************* * STM32MP1 TAMP ******************************************************************************/ diff --git a/plat/xilinx/common/include/plat_startup.h b/plat/xilinx/common/include/plat_startup.h new file mode 100644 index 0000000000..66e7933733 --- /dev/null +++ b/plat/xilinx/common/include/plat_startup.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2020, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PLAT_STARTUP_H +#define PLAT_STARTUP_H + +/* For FSBL handover */ +enum fsbl_handoff { + FSBL_HANDOFF_SUCCESS = 0, + FSBL_HANDOFF_NO_STRUCT, + FSBL_HANDOFF_INVAL_STRUCT, + FSBL_HANDOFF_TOO_MANY_PARTS +}; + +enum fsbl_handoff fsbl_atf_handover(entry_point_info_t *bl32_image_ep_info, + entry_point_info_t *bl33_image_ep_info, + uint64_t atf_handoff_addr); + +#endif /* PLAT_STARTUP_H */ diff --git a/plat/xilinx/zynqmp/pm_service/pm_client.h b/plat/xilinx/common/include/pm_client.h index adbb76f9b1..e91bb8f6c2 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_client.h +++ b/plat/xilinx/common/include/pm_client.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2019, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -19,11 +19,14 @@ void pm_client_suspend(const struct pm_proc *proc, unsigned int state); void pm_client_abort_suspend(void); void pm_client_wakeup(const struct pm_proc *proc); -enum pm_ret_status set_ocm_retention(void); -enum pm_ret_status pm_set_suspend_mode(uint32_t mode); -const struct pm_proc *pm_get_proc_by_node(enum pm_node_id nid); /* Global variables to be set in pm_client.c */ extern const struct pm_proc *primary_proc; +#ifndef VERSAL_PLATFORM +enum pm_ret_status set_ocm_retention(void); +enum pm_ret_status pm_set_suspend_mode(uint32_t mode); +const struct pm_proc *pm_get_proc_by_node(enum pm_node_id nid); +#endif + #endif /* PM_CLIENT_H */ diff --git a/plat/xilinx/common/include/pm_ipi.h b/plat/xilinx/common/include/pm_ipi.h index 16db5c5482..7bcf596267 100644 --- a/plat/xilinx/common/include/pm_ipi.h +++ b/plat/xilinx/common/include/pm_ipi.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -26,5 +26,8 @@ void pm_ipi_buff_read_callb(unsigned int *value, size_t count); void pm_ipi_irq_enable(const struct pm_proc *proc); void pm_ipi_irq_clear(const struct pm_proc *proc); uint32_t pm_ipi_irq_status(const struct pm_proc *proc); +#if ZYNQMP_IPI_CRC_CHECK +uint32_t calculate_crc(uint32_t payload[PAYLOAD_ARG_CNT], uint32_t buffersize); +#endif #endif /* PM_IPI_H */ diff --git a/plat/xilinx/zynqmp/ipi_mailbox_service/ipi_mailbox_svc.c b/plat/xilinx/common/ipi_mailbox_service/ipi_mailbox_svc.c index c499d78e91..f531158853 100644 --- a/plat/xilinx/zynqmp/ipi_mailbox_service/ipi_mailbox_svc.c +++ b/plat/xilinx/common/ipi_mailbox_service/ipi_mailbox_svc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/xilinx/zynqmp/ipi_mailbox_service/ipi_mailbox_svc.h b/plat/xilinx/common/ipi_mailbox_service/ipi_mailbox_svc.h index 197c788193..10682d8356 100644 --- a/plat/xilinx/zynqmp/ipi_mailbox_service/ipi_mailbox_svc.h +++ b/plat/xilinx/common/ipi_mailbox_service/ipi_mailbox_svc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ diff --git a/plat/xilinx/zynqmp/plat_startup.c b/plat/xilinx/common/plat_startup.c index cd2c3bac6e..8c9a049dd9 100644 --- a/plat/xilinx/zynqmp/plat_startup.c +++ b/plat/xilinx/common/plat_startup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,10 +8,8 @@ #include <arch_helpers.h> #include <common/debug.h> -#include <lib/mmio.h> -#include <plat_private.h> +#include <plat_startup.h> -#include "zynqmp_def.h" /* * ATFHandoffParams @@ -147,6 +145,7 @@ static int get_fsbl_estate(const struct xfsbl_partition *partition) * Populates the bl32 and bl33 image info structures * @bl32: BL32 image info structure * @bl33: BL33 image info structure + * atf_handoff_addr: ATF handoff address * * Process the handoff paramters from the FSBL and populate the BL32 and BL33 * image info structures accordingly. @@ -154,12 +153,11 @@ static int get_fsbl_estate(const struct xfsbl_partition *partition) * Return: Return the status of the handoff. The value will be from the * fsbl_handoff enum. */ -enum fsbl_handoff fsbl_atf_handover(entry_point_info_t *bl32, entry_point_info_t *bl33) +enum fsbl_handoff fsbl_atf_handover(entry_point_info_t *bl32, + entry_point_info_t *bl33, + uint64_t atf_handoff_addr) { - uint64_t atf_handoff_addr; const struct xfsbl_atf_handoff_params *ATFHandoffParams; - - atf_handoff_addr = mmio_read_32(PMU_GLOBAL_GEN_STORAGE6); assert((atf_handoff_addr < BL31_BASE) || (atf_handoff_addr > (uint64_t)&__BL31_END__)); if (!atf_handoff_addr) { diff --git a/plat/xilinx/common/pm_service/pm_ipi.c b/plat/xilinx/common/pm_service/pm_ipi.c index 034cd5bc8c..c83d25b0d9 100644 --- a/plat/xilinx/common/pm_service/pm_ipi.c +++ b/plat/xilinx/common/pm_service/pm_ipi.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -57,6 +57,9 @@ static enum pm_ret_status pm_ipi_send_common(const struct pm_proc *proc, uintptr_t buffer_base = proc->ipi->buffer_base + IPI_BUFFER_TARGET_REMOTE_OFFSET + IPI_BUFFER_REQ_OFFSET; +#if ZYNQMP_IPI_CRC_CHECK + payload[PAYLOAD_CRC_POS] = calculate_crc(payload, IPI_W0_TO_W6_SIZE); +#endif /* Write payload into IPI buffer */ for (size_t i = 0; i < PAYLOAD_ARG_CNT; i++) { @@ -132,6 +135,10 @@ static enum pm_ret_status pm_ipi_buff_read(const struct pm_proc *proc, unsigned int *value, size_t count) { size_t i; +#if ZYNQMP_IPI_CRC_CHECK + size_t j; + unsigned int response_payload[PAYLOAD_ARG_CNT]; +#endif uintptr_t buffer_base = proc->ipi->buffer_base + IPI_BUFFER_TARGET_REMOTE_OFFSET + IPI_BUFFER_RESP_OFFSET; @@ -147,6 +154,16 @@ static enum pm_ret_status pm_ipi_buff_read(const struct pm_proc *proc, *value = mmio_read_32(buffer_base + (i * PAYLOAD_ARG_SIZE)); value++; } +#if ZYNQMP_IPI_CRC_CHECK + for (j = 0; j < PAYLOAD_ARG_CNT; j++) + response_payload[j] = mmio_read_32(buffer_base + + (j * PAYLOAD_ARG_SIZE)); + + if (response_payload[PAYLOAD_CRC_POS] != + calculate_crc(response_payload, IPI_W0_TO_W6_SIZE)) + NOTICE("ERROR in CRC response payload value:0x%x\n", + response_payload[PAYLOAD_CRC_POS]); +#endif return mmio_read_32(buffer_base); } @@ -162,6 +179,10 @@ static enum pm_ret_status pm_ipi_buff_read(const struct pm_proc *proc, void pm_ipi_buff_read_callb(unsigned int *value, size_t count) { size_t i; +#if ZYNQMP_IPI_CRC_CHECK + size_t j; + unsigned int response_payload[PAYLOAD_ARG_CNT]; +#endif uintptr_t buffer_base = IPI_BUFFER_REMOTE_BASE + IPI_BUFFER_TARGET_LOCAL_OFFSET + IPI_BUFFER_REQ_OFFSET; @@ -173,6 +194,16 @@ void pm_ipi_buff_read_callb(unsigned int *value, size_t count) *value = mmio_read_32(buffer_base + (i * PAYLOAD_ARG_SIZE)); value++; } +#if ZYNQMP_IPI_CRC_CHECK + for (j = 0; j < PAYLOAD_ARG_CNT; j++) + response_payload[j] = mmio_read_32(buffer_base + + (j * PAYLOAD_ARG_SIZE)); + + if (response_payload[PAYLOAD_CRC_POS] != + calculate_crc(response_payload, IPI_W0_TO_W6_SIZE)) + NOTICE("ERROR in CRC response payload value:0x%x\n", + response_payload[PAYLOAD_CRC_POS]); +#endif } /** @@ -228,3 +259,34 @@ uint32_t pm_ipi_irq_status(const struct pm_proc *proc) else return 0; } + +#if ZYNQMP_IPI_CRC_CHECK +uint32_t calculate_crc(uint32_t *payload, uint32_t bufsize) +{ + uint32_t crcinit = CRC_INIT_VALUE; + uint32_t order = CRC_ORDER; + uint32_t polynom = CRC_POLYNOM; + uint32_t i, j, c, bit, datain, crcmask, crchighbit; + uint32_t crc = crcinit; + + crcmask = ((uint32_t)((1U << (order - 1U)) - 1U) << 1U) | 1U; + crchighbit = (uint32_t)(1U << (order - 1U)); + + for (i = 0U; i < bufsize; i++) { + datain = mmio_read_8((unsigned long)payload + i); + c = datain; + j = 0x80U; + while (j != 0U) { + bit = crc & crchighbit; + crc <<= 1U; + if (0U != (c & j)) + bit ^= crchighbit; + if (bit != 0U) + crc ^= polynom; + j >>= 1U; + } + crc &= crcmask; + } + return crc; +} +#endif diff --git a/plat/xilinx/versal/aarch64/versal_common.c b/plat/xilinx/versal/aarch64/versal_common.c index 587b797d76..2fa8476584 100644 --- a/plat/xilinx/versal/aarch64/versal_common.c +++ b/plat/xilinx/versal/aarch64/versal_common.c @@ -1,18 +1,18 @@ /* - * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ +#include <plat_ipi.h> +#include <versal_def.h> +#include <plat_private.h> #include <common/debug.h> #include <drivers/generic_delay_timer.h> #include <lib/mmio.h> #include <lib/xlat_tables/xlat_tables.h> #include <plat/common/platform.h> -#include "../versal_def.h" -#include "../versal_private.h" - /* * Table of regions to map using the MMU. * This doesn't include TZRAM as the 'mem_layout' argument passed to @@ -22,6 +22,8 @@ const mmap_region_t plat_versal_mmap[] = { MAP_REGION_FLAT(DEVICE0_BASE, DEVICE0_SIZE, MT_DEVICE | MT_RW | MT_SECURE), MAP_REGION_FLAT(DEVICE1_BASE, DEVICE1_SIZE, MT_DEVICE | MT_RW | MT_SECURE), MAP_REGION_FLAT(CRF_BASE, CRF_SIZE, MT_DEVICE | MT_RW | MT_SECURE), + MAP_REGION_FLAT(FPD_MAINCCI_BASE, FPD_MAINCCI_SIZE, MT_DEVICE | MT_RW | + MT_SECURE), { 0 } }; @@ -39,11 +41,10 @@ void versal_config_setup(void) { uint32_t val; - versal_print_platform_name(); + /* Configure IPI data for versal */ + versal_ipi_config_table_init(); - mmio_write_32(VERSAL_CRL_IOU_SWITCH_CTRL, - VERSAL_IOU_SWITCH_CTRL_CLKACT_BIT | - (0x20 << VERSAL_IOU_SWITCH_CTRL_DIVISOR0_SHIFT)); + versal_print_platform_name(); /* Global timer init - Program time stamp reference clk */ val = mmio_read_32(VERSAL_CRL_TIMESTAMP_REF_CTRL); @@ -66,11 +67,3 @@ unsigned int plat_get_syscnt_freq2(void) return VERSAL_CPU_CLOCK; } -uintptr_t plat_get_ns_image_entrypoint(void) -{ -#ifdef PRELOADED_BL33_BASE - return PRELOADED_BL33_BASE; -#else - return PLAT_VERSAL_NS_IMAGE_OFFSET; -#endif -} diff --git a/plat/xilinx/versal/bl31_versal_setup.c b/plat/xilinx/versal/bl31_versal_setup.c index d7e07e036a..a5cf05e9af 100644 --- a/plat/xilinx/versal/bl31_versal_setup.c +++ b/plat/xilinx/versal/bl31_versal_setup.c @@ -1,21 +1,24 @@ /* - * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ #include <assert.h> #include <errno.h> - +#include <plat_arm.h> +#include <plat_private.h> #include <bl31/bl31.h> #include <common/bl_common.h> #include <common/debug.h> #include <drivers/arm/pl011.h> #include <drivers/console.h> +#include <lib/mmio.h> #include <lib/xlat_tables/xlat_tables.h> #include <plat/common/platform.h> - -#include "versal_private.h" +#include <versal_def.h> +#include <plat_private.h> +#include <plat_startup.h> static entry_point_info_t bl32_image_ep_info; static entry_point_info_t bl33_image_ep_info; @@ -38,6 +41,18 @@ entry_point_info_t *bl31_plat_get_next_image_ep_info(uint32_t type) } /* + * Set the build time defaults,if we can't find any config data. + */ +static inline void bl31_set_default_config(void) +{ + bl32_image_ep_info.pc = BL32_BASE; + bl32_image_ep_info.spsr = arm_get_spsr_for_bl32_entry(); + bl33_image_ep_info.pc = plat_get_ns_image_entrypoint(); + bl33_image_ep_info.spsr = SPSR_64(MODE_EL2, MODE_SP_ELX, + DISABLE_ALL_EXCEPTIONS); +} + +/* * Perform any BL31 specific platform actions. Here is an opportunity to copy * parameters passed by the calling EL (S-EL1 in BL2 & S-EL3 in BL1) before they * are lost (potentially). This needs to be done before the MMU is initialized @@ -46,6 +61,7 @@ entry_point_info_t *bl31_plat_get_next_image_ep_info(uint32_t type) void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, u_register_t arg2, u_register_t arg3) { + uint64_t atf_handoff_addr; /* Initialize the console to provide early debug support */ int rc = console_pl011_register(VERSAL_UART_BASE, @@ -77,12 +93,15 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, SET_PARAM_HEAD(&bl33_image_ep_info, PARAM_EP, VERSION_1, 0); SET_SECURITY_STATE(bl33_image_ep_info.h.attr, NON_SECURE); - /* use build time defaults in JTAG boot mode */ - bl32_image_ep_info.pc = BL32_BASE; - bl32_image_ep_info.spsr = 0; - bl33_image_ep_info.pc = plat_get_ns_image_entrypoint(); - bl33_image_ep_info.spsr = SPSR_64(MODE_EL2, MODE_SP_ELX, - DISABLE_ALL_EXCEPTIONS); + atf_handoff_addr = mmio_read_32(PMC_GLOBAL_GLOB_GEN_STORAGE4); + enum fsbl_handoff ret = fsbl_atf_handover(&bl32_image_ep_info, + &bl33_image_ep_info, + atf_handoff_addr); + if (ret == FSBL_HANDOFF_NO_STRUCT) { + bl31_set_default_config(); + } else if (ret != FSBL_HANDOFF_SUCCESS) { + panic(); + } NOTICE("BL31: Secure code at 0x%lx\n", bl32_image_ep_info.pc); NOTICE("BL31: Non secure code at 0x%lx\n", bl33_image_ep_info.pc); @@ -104,6 +123,9 @@ void bl31_plat_runtime_setup(void) */ void bl31_plat_arch_setup(void) { + plat_arm_interconnect_init(); + plat_arm_interconnect_enter_coherency(); + const mmap_region_t bl_regions[] = { MAP_REGION_FLAT(BL31_BASE, BL31_END - BL31_BASE, MT_MEMORY | MT_RW | MT_SECURE), diff --git a/plat/xilinx/versal/include/plat_ipi.h b/plat/xilinx/versal/include/plat_ipi.h new file mode 100644 index 0000000000..6b08f322d1 --- /dev/null +++ b/plat/xilinx/versal/include/plat_ipi.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2019, Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* Versal IPI management enums and defines */ + +#ifndef PLAT_IPI_H +#define PLAT_IPI_H + +#include <ipi.h> +#include <stdint.h> + +/********************************************************************* + * IPI agent IDs macros + ********************************************************************/ +#define IPI_ID_PMC 1U +#define IPI_ID_APU 2U +#define IPI_ID_RPU0 3U +#define IPI_ID_RPU1 4U +#define IPI_ID_3 5U +#define IPI_ID_4 6U +#define IPI_ID_5 7U + +/********************************************************************* + * IPI message buffers + ********************************************************************/ +#define IPI_BUFFER_BASEADDR 0xFF3F0000U + +#define IPI_BUFFER_APU_BASE (IPI_BUFFER_BASEADDR + 0x400U) +#define IPI_BUFFER_PMC_BASE (IPI_BUFFER_BASEADDR + 0x200U) + +#define IPI_BUFFER_TARGET_APU_OFFSET 0x0U +#define IPI_BUFFER_TARGET_PMC_OFFSET 0x40U + +#define IPI_BUFFER_LOCAL_BASE IPI_BUFFER_APU_BASE +#define IPI_BUFFER_REMOTE_BASE IPI_BUFFER_PMC_BASE + +#define IPI_BUFFER_TARGET_LOCAL_OFFSET IPI_BUFFER_TARGET_APU_OFFSET +#define IPI_BUFFER_TARGET_REMOTE_OFFSET IPI_BUFFER_TARGET_PMC_OFFSET + +#define IPI_BUFFER_MAX_WORDS 8 + +#define IPI_BUFFER_REQ_OFFSET 0x0U +#define IPI_BUFFER_RESP_OFFSET 0x20U + +/********************************************************************* + * Platform specific IPI API declarations + ********************************************************************/ + +/* Configure IPI table for versal */ +void versal_ipi_config_table_init(void); + +#endif /* PLAT_IPI_H */ diff --git a/plat/xilinx/versal/include/plat_pm_common.h b/plat/xilinx/versal/include/plat_pm_common.h new file mode 100644 index 0000000000..2d00801688 --- /dev/null +++ b/plat/xilinx/versal/include/plat_pm_common.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2019, Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Contains platform specific definitions of commonly used macros data types + * for PU Power Management. This file should be common for all PU's. + */ + +#ifndef PLAT_PM_COMMON_H +#define PLAT_PM_COMMON_H + +#include <common/debug.h> +#include <stdint.h> +#include "pm_defs.h" + +#define PAYLOAD_ARG_CNT 6U +#define PAYLOAD_ARG_SIZE 4U /* size in bytes */ + +#define VERSAL_TZ_VERSION_MAJOR 1 +#define VERSAL_TZ_VERSION_MINOR 0 +#define VERSAL_TZ_VERSION ((VERSAL_TZ_VERSION_MAJOR << 16) | \ + VERSAL_TZ_VERSION_MINOR) +#endif /* PLAT_PM_COMMON_H */ diff --git a/plat/xilinx/versal/versal_private.h b/plat/xilinx/versal/include/plat_private.h index 5d98d080c5..e302096ebd 100644 --- a/plat/xilinx/versal/versal_private.h +++ b/plat/xilinx/versal/include/plat_private.h @@ -1,11 +1,11 @@ /* - * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2019, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ -#ifndef VERSAL_PRIVATE_H -#define VERSAL_PRIVATE_H +#ifndef PLAT_PRIVATE_H +#define PLAT_PRIVATE_H #include <lib/xlat_tables/xlat_tables.h> @@ -18,7 +18,9 @@ void plat_versal_gic_init(void); void plat_versal_gic_cpuif_enable(void); void plat_versal_gic_cpuif_disable(void); void plat_versal_gic_pcpu_init(void); +void plat_versal_gic_save(void); +void plat_versal_gic_resume(void); unsigned int versal_calc_core_pos(u_register_t mpidr); -#endif /* VERSAL_PRIVATE_H */ +#endif /* PLAT_PRIVATE_H */ diff --git a/plat/xilinx/versal/include/platform_def.h b/plat/xilinx/versal/include/platform_def.h index 0c4b9544f2..9f8392ce91 100644 --- a/plat/xilinx/versal/include/platform_def.h +++ b/plat/xilinx/versal/include/platform_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -8,8 +8,7 @@ #define PLATFORM_DEF_H #include <arch.h> - -#include "../versal_def.h" +#include "versal_def.h" /******************************************************************************* * Generic platform constants @@ -32,7 +31,7 @@ * little space for growth. */ #ifndef VERSAL_ATF_MEM_BASE -# define BL31_BASE 0xfffea000 +# define BL31_BASE 0xfffe0000 # define BL31_LIMIT 0xffffffff #else # define BL31_BASE (VERSAL_ATF_MEM_BASE) @@ -57,9 +56,9 @@ * BL33 specific defines. ******************************************************************************/ #ifndef PRELOADED_BL33_BASE -# define PLAT_VERSAL_NS_IMAGE_OFFSET 0x8000000 +# define PLAT_ARM_NS_IMAGE_BASE 0x8000000 #else -# define PLAT_VERSAL_NS_IMAGE_OFFSET PRELOADED_BL33_BASE +# define PLAT_ARM_NS_IMAGE_BASE PRELOADED_BL33_BASE #endif /******************************************************************************* @@ -76,7 +75,7 @@ ******************************************************************************/ #define PLAT_PHY_ADDR_SPACE_SIZE (1ull << 32) #define PLAT_VIRT_ADDR_SPACE_SIZE (1ull << 32) -#define MAX_MMAP_REGIONS 7 +#define MAX_MMAP_REGIONS 8 #define MAX_XLAT_TABLES 5 #define CACHE_WRITEBACK_SHIFT 6 diff --git a/plat/xilinx/versal/versal_def.h b/plat/xilinx/versal/include/versal_def.h index 41c65b94de..9a9b7c0178 100644 --- a/plat/xilinx/versal/versal_def.h +++ b/plat/xilinx/versal/include/versal_def.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -19,6 +19,7 @@ /* List all supported platforms */ #define VERSAL_PLATFORM_ID_versal_virt 1 +#define VERSAL_PLATFORM_ID_silicon 4 #define VERSAL_PLATFORM_IS(con) (VERSAL_PLATFORM_ID_ ## con == VERSAL_PLATFORM) @@ -35,13 +36,10 @@ /* CRL */ #define VERSAL_CRL 0xFF5E0000 -#define VERSAL_CRL_IOU_SWITCH_CTRL (VERSAL_CRL + 0x114) #define VERSAL_CRL_TIMESTAMP_REF_CTRL (VERSAL_CRL + 0x14C) #define VERSAL_CRL_RST_TIMESTAMP_OFFSET (VERSAL_CRL + 0x348) #define VERSAL_CRL_APB_TIMESTAMP_REF_CTRL_CLKACT_BIT (1 << 25) -#define VERSAL_IOU_SWITCH_CTRL_CLKACT_BIT (1 << 25) -#define VERSAL_IOU_SWITCH_CTRL_DIVISOR0_SHIFT 8 /* IOU SCNTRS */ #define VERSAL_IOU_SCNTRS 0xFF140000 @@ -56,6 +54,13 @@ #define VERSAL_IRQ_SEC_PHY_TIMER 29 /******************************************************************************* + * CCI-400 related constants + ******************************************************************************/ +#define PLAT_ARM_CCI_BASE 0xFD000000 +#define PLAT_ARM_CCI_CLUSTER0_SL_IFACE_IX 4 +#define PLAT_ARM_CCI_CLUSTER1_SL_IFACE_IX 5 + +/******************************************************************************* * UART related constants ******************************************************************************/ #define VERSAL_UART0_BASE 0xFF000000 @@ -80,7 +85,12 @@ # define PLATFORM_NAME "Versal Virt" # define VERSAL_UART_CLOCK 25000000 # define VERSAL_UART_BAUDRATE 115200 -# define VERSAL_CPU_CLOCK 62500000 +# define VERSAL_CPU_CLOCK 2720000 +#elif VERSAL_PLATFORM_IS(silicon) +# define PLATFORM_NAME "Versal Silicon" +# define VERSAL_UART_CLOCK 100000000 +# define VERSAL_UART_BAUDRATE 115200 +# define VERSAL_CPU_CLOCK 100000000 #endif /* Access control register defines */ @@ -97,6 +107,9 @@ #define CRF_RST_APU_ACPU_RESET (1 << 0) #define CRF_RST_APU_ACPU_PWRON_RESET (1 << 10) +#define FPD_MAINCCI_BASE 0xFD000000 +#define FPD_MAINCCI_SIZE 0x00100000 + /* APU registers and bitfields */ #define FPD_APU_BASE 0xFD5C0000 #define FPD_APU_CONFIG_0 (FPD_APU_BASE + 0x20) @@ -105,5 +118,26 @@ #define FPD_APU_PWRCTL (FPD_APU_BASE + 0x90) #define FPD_APU_CONFIG_0_VINITHI_SHIFT 8 +#define APU_0_PWRCTL_CPUPWRDWNREQ_MASK 1 +#define APU_1_PWRCTL_CPUPWRDWNREQ_MASK 2 + +/* PMC registers and bitfields */ +#define PMC_GLOBAL_BASE 0xF1110000 +#define PMC_GLOBAL_GLOB_GEN_STORAGE4 (PMC_GLOBAL_BASE + 0x40) + +/* IPI registers and bitfields */ +#define IPI0_REG_BASE 0xFF330000 +#define IPI0_TRIG_BIT (1 << 2) +#define PMC_IPI_TRIG_BIT (1 << 1) +#define IPI1_REG_BASE 0xFF340000 +#define IPI1_TRIG_BIT (1 << 3) +#define IPI2_REG_BASE 0xFF350000 +#define IPI2_TRIG_BIT (1 << 4) +#define IPI3_REG_BASE 0xFF360000 +#define IPI3_TRIG_BIT (1 << 5) +#define IPI4_REG_BASE 0xFF370000 +#define IPI4_TRIG_BIT (1 << 5) +#define IPI5_REG_BASE 0xFF380000 +#define IPI5_TRIG_BIT (1 << 6) #endif /* VERSAL_DEF_H */ diff --git a/plat/xilinx/versal/plat_psci.c b/plat/xilinx/versal/plat_psci.c index 4a443697f0..39550858ad 100644 --- a/plat/xilinx/versal/plat_psci.c +++ b/plat/xilinx/versal/plat_psci.c @@ -1,63 +1,109 @@ /* - * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2019, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ +#include <assert.h> +#include <plat_arm.h> +#include <plat_private.h> +#include <pm_common.h> #include <common/debug.h> #include <lib/mmio.h> #include <lib/psci/psci.h> #include <plat/common/platform.h> +#include <plat/arm/common/plat_arm.h> -#include "versal_private.h" +#include "pm_api_sys.h" +#include "pm_client.h" static uintptr_t versal_sec_entry; -static int versal_nopmc_pwr_domain_on(u_register_t mpidr) +static int versal_pwr_domain_on(u_register_t mpidr) { - uint32_t r; unsigned int cpu_id = plat_core_pos_by_mpidr(mpidr); + const struct pm_proc *proc; VERBOSE("%s: mpidr: 0x%lx\n", __func__, mpidr); if (cpu_id == -1) return PSCI_E_INTERN_FAIL; - /* - * program RVBAR - */ - mmio_write_32(FPD_APU_RVBAR_L_0 + (cpu_id << 3), versal_sec_entry); - mmio_write_32(FPD_APU_RVBAR_H_0 + (cpu_id << 3), versal_sec_entry >> 32); + proc = pm_get_proc(cpu_id); - /* - * clear VINITHI - */ - r = mmio_read_32(FPD_APU_CONFIG_0); - r &= ~(1 << FPD_APU_CONFIG_0_VINITHI_SHIFT << cpu_id); - mmio_write_32(FPD_APU_CONFIG_0, r); + /* Send request to PMC to wake up selected ACPU core */ + pm_req_wakeup(proc->node_id, (versal_sec_entry & 0xFFFFFFFF) | 0x1, + versal_sec_entry >> 32, 0); - /* - * FIXME: Add power up sequence, By default it works - * now without the need of it as it was powered up by - * default. - */ + /* Clear power down request */ + pm_client_wakeup(proc); - /* - * clear power down request - */ - r = mmio_read_32(FPD_APU_PWRCTL); - r &= ~(1 << cpu_id); - mmio_write_32(FPD_APU_PWRCTL, r); + return PSCI_E_SUCCESS; +} - /* - * release core reset - */ - r = mmio_read_32(CRF_RST_APU); - r &= ~((CRF_RST_APU_ACPU_PWRON_RESET | - CRF_RST_APU_ACPU_RESET) << cpu_id); - mmio_write_32(CRF_RST_APU, r); +/** + * versal_pwr_domain_suspend() - This function sends request to PMC to suspend + * core. + * + * @target_state Targated state + */ +static void versal_pwr_domain_suspend(const psci_power_state_t *target_state) +{ + unsigned int state; + unsigned int cpu_id = plat_my_core_pos(); + const struct pm_proc *proc = pm_get_proc(cpu_id); - return PSCI_E_SUCCESS; + for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) + VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", + __func__, i, target_state->pwr_domain_state[i]); + + plat_versal_gic_cpuif_disable(); + + plat_versal_gic_save(); + + state = target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE ? + PM_STATE_SUSPEND_TO_RAM : PM_STATE_CPU_IDLE; + + /* Send request to PMC to suspend this core */ + pm_self_suspend(proc->node_id, MAX_LATENCY, state, versal_sec_entry); + + /* APU is to be turned off */ + if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) { + /* disable coherency */ + plat_arm_interconnect_exit_coherency(); + } +} + +/** + * versal_pwr_domain_suspend_finish() - This function performs actions to finish + * suspend procedure. + * + * @target_state Targated state + */ +static void versal_pwr_domain_suspend_finish( + const psci_power_state_t *target_state) +{ + unsigned int cpu_id = plat_my_core_pos(); + const struct pm_proc *proc = pm_get_proc(cpu_id); + + for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) + VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", + __func__, i, target_state->pwr_domain_state[i]); + + /* Clear the APU power control register for this cpu */ + pm_client_wakeup(proc); + + /* enable coherency */ + plat_arm_interconnect_enter_coherency(); + + /* APU was turned off, so restore GIC context */ + if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) { + plat_versal_gic_resume(); + plat_versal_gic_cpuif_enable(); + } else { + plat_versal_gic_cpuif_enable(); + plat_versal_gic_pcpu_init(); + } } void versal_pwr_domain_on_finish(const psci_power_state_t *target_state) @@ -69,9 +115,114 @@ void versal_pwr_domain_on_finish(const psci_power_state_t *target_state) plat_versal_gic_cpuif_enable(); } +/** + * versal_system_off() - This function sends the system off request + * to firmware. This function does not return. + */ +static void __dead2 versal_system_off(void) +{ + /* Send the power down request to the PMC */ + pm_system_shutdown(XPM_SHUTDOWN_TYPE_SHUTDOWN, + pm_get_shutdown_scope()); + + while (1) + wfi(); +} + +/** + * versal_system_reset() - This function sends the reset request + * to firmware for the system to reset. This function does not return. + */ +static void __dead2 versal_system_reset(void) +{ + /* Send the system reset request to the PMC */ + pm_system_shutdown(XPM_SHUTDOWN_TYPE_RESET, + pm_get_shutdown_scope()); + + while (1) + wfi(); +} + +/** + * versal_pwr_domain_off() - This function performs actions to turn off core + * + * @target_state Targated state + */ +static void versal_pwr_domain_off(const psci_power_state_t *target_state) +{ + unsigned int cpu_id = plat_my_core_pos(); + const struct pm_proc *proc = pm_get_proc(cpu_id); + + for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) + VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", + __func__, i, target_state->pwr_domain_state[i]); + + /* Prevent interrupts from spuriously waking up this cpu */ + plat_versal_gic_cpuif_disable(); + + /* + * Send request to PMC to power down the appropriate APU CPU + * core. + * According to PSCI specification, CPU_off function does not + * have resume address and CPU core can only be woken up + * invoking CPU_on function, during which resume address will + * be set. + */ + pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0); +} + +/** + * versal_validate_power_state() - This function ensures that the power state + * parameter in request is valid. + * + * @power_state Power state of core + * @req_state Requested state + * + * @return Returns status, either success or reason + */ +static int versal_validate_power_state(unsigned int power_state, + psci_power_state_t *req_state) +{ + VERBOSE("%s: power_state: 0x%x\n", __func__, power_state); + + int pstate = psci_get_pstate_type(power_state); + + assert(req_state); + + /* Sanity check the requested state */ + if (pstate == PSTATE_TYPE_STANDBY) + req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE; + else + req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_OFF_STATE; + + /* We expect the 'state id' to be zero */ + if (psci_get_pstate_id(power_state)) + return PSCI_E_INVALID_PARAMS; + + return PSCI_E_SUCCESS; +} + +/** + * versal_get_sys_suspend_power_state() - Get power state for system suspend + * + * @req_state Requested state + */ +static void versal_get_sys_suspend_power_state(psci_power_state_t *req_state) +{ + req_state->pwr_domain_state[PSCI_CPU_PWR_LVL] = PLAT_MAX_OFF_STATE; + req_state->pwr_domain_state[1] = PLAT_MAX_OFF_STATE; +} + static const struct plat_psci_ops versal_nopmc_psci_ops = { - .pwr_domain_on = versal_nopmc_pwr_domain_on, + .pwr_domain_on = versal_pwr_domain_on, + .pwr_domain_off = versal_pwr_domain_off, .pwr_domain_on_finish = versal_pwr_domain_on_finish, + .pwr_domain_suspend = versal_pwr_domain_suspend, + .pwr_domain_suspend_finish = versal_pwr_domain_suspend_finish, + .system_off = versal_system_off, + .system_reset = versal_system_reset, + .validate_power_state = versal_validate_power_state, + .get_sys_suspend_power_state = versal_get_sys_suspend_power_state, }; /******************************************************************************* diff --git a/plat/xilinx/versal/plat_versal.c b/plat/xilinx/versal/plat_versal.c index 642867da29..a080a76a95 100644 --- a/plat/xilinx/versal/plat_versal.c +++ b/plat/xilinx/versal/plat_versal.c @@ -1,13 +1,12 @@ /* - * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2019, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ +#include <plat_private.h> #include <plat/common/platform.h> -#include "versal_private.h" - int plat_core_pos_by_mpidr(u_register_t mpidr) { if (mpidr & MPIDR_CLUSTER_MASK) diff --git a/plat/xilinx/versal/platform.mk b/plat/xilinx/versal/platform.mk index 1c56364c85..1e231cce8f 100644 --- a/plat/xilinx/versal/platform.mk +++ b/plat/xilinx/versal/platform.mk @@ -1,4 +1,4 @@ -# Copyright (c) 2018-2019, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2018-2020, ARM Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause @@ -31,33 +31,50 @@ ifdef VERSAL_BL32_MEM_BASE $(eval $(call add_define,VERSAL_BL32_MEM_SIZE)) endif -VERSAL_PLATFORM ?= versal_virt +VERSAL_PLATFORM ?= silicon $(eval $(call add_define_val,VERSAL_PLATFORM,VERSAL_PLATFORM_ID_${VERSAL_PLATFORM})) VERSAL_CONSOLE ?= pl011 $(eval $(call add_define_val,VERSAL_CONSOLE,VERSAL_CONSOLE_ID_${VERSAL_CONSOLE})) -PLAT_INCLUDES := -Iplat/xilinx/versal/include/ +PLAT_INCLUDES := -Iinclude/plat/arm/common/ \ + -Iplat/xilinx/common/include/ \ + -Iplat/xilinx/common/ipi_mailbox_service/ \ + -Iplat/xilinx/versal/include/ \ + -Iplat/xilinx/versal/pm_service/ PLAT_BL_COMMON_SOURCES := lib/xlat_tables/xlat_tables_common.c \ lib/xlat_tables/aarch64/xlat_tables.c \ drivers/delay_timer/delay_timer.c \ drivers/delay_timer/generic_delay_timer.c \ drivers/arm/gic/common/gic_common.c \ + drivers/arm/gic/v3/arm_gicv3_common.c \ + drivers/arm/gic/v3/gic500.c \ drivers/arm/gic/v3/gicv3_main.c \ drivers/arm/gic/v3/gicv3_helpers.c \ drivers/arm/pl011/aarch64/pl011_console.S \ plat/common/aarch64/crash_console_helpers.S \ + plat/arm/common/arm_cci.c \ + plat/arm/common/arm_common.c \ plat/common/plat_gicv3.c \ plat/xilinx/versal/aarch64/versal_helpers.S \ plat/xilinx/versal/aarch64/versal_common.c -BL31_SOURCES += lib/cpus/aarch64/cortex_a53.S \ +BL31_SOURCES += drivers/arm/cci/cci.c \ + lib/cpus/aarch64/cortex_a53.S \ lib/cpus/aarch64/cortex_a72.S \ plat/common/plat_psci_common.c \ + plat/xilinx/common/ipi.c \ + plat/xilinx/common/plat_startup.c \ + plat/xilinx/common/ipi_mailbox_service/ipi_mailbox_svc.c \ + plat/xilinx/common/pm_service/pm_ipi.c \ plat/xilinx/versal/bl31_versal_setup.c \ plat/xilinx/versal/plat_psci.c \ plat/xilinx/versal/plat_versal.c \ plat/xilinx/versal/plat_topology.c \ plat/xilinx/versal/sip_svc_setup.c \ - plat/xilinx/versal/versal_gicv3.c + plat/xilinx/versal/versal_gicv3.c \ + plat/xilinx/versal/versal_ipi.c \ + plat/xilinx/versal/pm_service/pm_svc_main.c \ + plat/xilinx/versal/pm_service/pm_api_sys.c \ + plat/xilinx/versal/pm_service/pm_client.c diff --git a/plat/xilinx/versal/pm_service/pm_api_sys.c b/plat/xilinx/versal/pm_service/pm_api_sys.c new file mode 100644 index 0000000000..dbe94e6249 --- /dev/null +++ b/plat/xilinx/versal/pm_service/pm_api_sys.c @@ -0,0 +1,885 @@ +/* + * Copyright (c) 2019, Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Versal system level PM-API functions and communication with PMC via + * IPI interrupts + */ + +#include <pm_common.h> +#include <pm_ipi.h> +#include <plat/common/platform.h> +#include "pm_api_sys.h" +#include "pm_client.h" + +/********************************************************************* + * Target module IDs macros + ********************************************************************/ +#define LIBPM_MODULE_ID 0x2 +#define LOADER_MODULE_ID 0x7 + +/* default shutdown/reboot scope is system(2) */ +static unsigned int pm_shutdown_scope = XPM_SHUTDOWN_SUBTYPE_RST_SYSTEM; + +/** + * pm_get_shutdown_scope() - Get the currently set shutdown scope + * + * @return Shutdown scope value + */ +unsigned int pm_get_shutdown_scope(void) +{ + return pm_shutdown_scope; +} + +/** + * Assigning of argument values into array elements. + */ +#define PM_PACK_PAYLOAD1(pl, mid, arg0) { \ + pl[0] = (uint32_t)((uint32_t)((arg0) & 0xFF) | (mid << 8)); \ +} + +#define PM_PACK_PAYLOAD2(pl, mid, arg0, arg1) { \ + pl[1] = (uint32_t)(arg1); \ + PM_PACK_PAYLOAD1(pl, mid, arg0); \ +} + +#define PM_PACK_PAYLOAD3(pl, mid, arg0, arg1, arg2) { \ + pl[2] = (uint32_t)(arg2); \ + PM_PACK_PAYLOAD2(pl, mid, arg0, arg1); \ +} + +#define PM_PACK_PAYLOAD4(pl, mid, arg0, arg1, arg2, arg3) { \ + pl[3] = (uint32_t)(arg3); \ + PM_PACK_PAYLOAD3(pl, mid, arg0, arg1, arg2); \ +} + +#define PM_PACK_PAYLOAD5(pl, mid, arg0, arg1, arg2, arg3, arg4) { \ + pl[4] = (uint32_t)(arg4); \ + PM_PACK_PAYLOAD4(pl, mid, arg0, arg1, arg2, arg3); \ +} + +#define PM_PACK_PAYLOAD6(pl, mid, arg0, arg1, arg2, arg3, arg4, arg5) { \ + pl[5] = (uint32_t)(arg5); \ + PM_PACK_PAYLOAD5(pl, mid, arg0, arg1, arg2, arg3, arg4); \ +} + +/* PM API functions */ + +/** + * pm_get_api_version() - Get version number of PMC PM firmware + * @version Returns 32-bit version number of PMC Power Management Firmware + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_get_api_version(unsigned int *version) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD1(payload, LIBPM_MODULE_ID, PM_GET_API_VERSION); + return pm_ipi_send_sync(primary_proc, payload, version, 1); +} + +/** + * pm_self_suspend() - PM call for processor to suspend itself + * @nid Node id of the processor or subsystem + * @latency Requested maximum wakeup latency (not supported) + * @state Requested state + * @address Resume address + * + * This is a blocking call, it will return only once PMU has responded. + * On a wakeup, resume address will be automatically set by PMU. + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_self_suspend(uint32_t nid, + unsigned int latency, + unsigned int state, + uintptr_t address) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + unsigned int cpuid = plat_my_core_pos(); + const struct pm_proc *proc = pm_get_proc(cpuid); + + if (!proc) { + WARN("Failed to get proc %d\n", cpuid); + return PM_RET_ERROR_INTERNAL; + } + + /* + * Do client specific suspend operations + * (e.g. set powerdown request bit) + */ + pm_client_suspend(proc, state); + + /* Send request to the PLM */ + PM_PACK_PAYLOAD6(payload, LIBPM_MODULE_ID, PM_SELF_SUSPEND, + proc->node_id, latency, state, address, + (address >> 32)); + return pm_ipi_send_sync(proc, payload, NULL, 0); +} + +/** + * pm_abort_suspend() - PM call to announce that a prior suspend request + * is to be aborted. + * @reason Reason for the abort + * + * Calling PU expects the PMU to abort the initiated suspend procedure. + * This is a non-blocking call without any acknowledge. + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_abort_suspend(enum pm_abort_reason reason) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* + * Do client specific abort suspend operations + * (e.g. enable interrupts and clear powerdown request bit) + */ + pm_client_abort_suspend(); + + /* Send request to the PLM */ + PM_PACK_PAYLOAD3(payload, LIBPM_MODULE_ID, PM_ABORT_SUSPEND, reason, + primary_proc->node_id); + return pm_ipi_send(primary_proc, payload); +} + +/** + * pm_req_suspend() - PM call to request for another PU or subsystem to + * be suspended gracefully. + * @target Node id of the targeted PU or subsystem + * @ack Flag to specify whether acknowledge is requested + * @latency Requested wakeup latency (not supported) + * @state Requested state (not supported) + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_req_suspend(uint32_t target, uint8_t ack, + unsigned int latency, unsigned int state) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD4(payload, LIBPM_MODULE_ID, PM_REQ_SUSPEND, target, + latency, state); + if (ack == IPI_BLOCKING) + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); + else + return pm_ipi_send(primary_proc, payload); +} + +/** + * pm_req_wakeup() - PM call for processor to wake up selected processor + * or subsystem + * @target Device ID of the processor or subsystem to wake up + * @set_address Resume address presence indicator + * 1 - resume address specified, 0 - otherwise + * @address Resume address + * @ack Flag to specify whether acknowledge requested + * + * This API function is either used to power up another APU core for SMP + * (by PSCI) or to power up an entirely different PU or subsystem, such + * as RPU0, RPU, or PL_CORE_xx. Resume address for the target PU will be + * automatically set by PMC. + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_req_wakeup(uint32_t target, uint32_t set_address, + uintptr_t address, uint8_t ack) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC to perform the wake of the PU */ + PM_PACK_PAYLOAD5(payload, LIBPM_MODULE_ID, PM_REQ_WAKEUP, target, + set_address, address, ack); + + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_request_device() - Request a device + * @device_id Device ID + * @capabilities Requested capabilities for the device + * @qos Required Quality of Service + * @ack Flag to specify whether acknowledge requested + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_request_device(uint32_t device_id, uint32_t capabilities, + uint32_t qos, uint32_t ack) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD5(payload, LIBPM_MODULE_ID, PM_REQUEST_DEVICE, + device_id, capabilities, qos, ack); + + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_release_device() - Release a device + * @device_id Device ID + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_release_device(uint32_t device_id) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD2(payload, LIBPM_MODULE_ID, PM_RELEASE_DEVICE, + device_id); + + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_set_requirement() - Set requirement for the device + * @device_id Device ID + * @capabilities Requested capabilities for the device + * @latency Requested maximum latency + * @qos Required Quality of Service + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_set_requirement(uint32_t device_id, uint32_t capabilities, + uint32_t latency, uint32_t qos) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD5(payload, LIBPM_MODULE_ID, PM_SET_REQUIREMENT, + device_id, capabilities, latency, qos); + + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_get_device_status() - Get device's status + * @device_id Device ID + * @response Buffer to store device status response + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_get_device_status(uint32_t device_id, uint32_t *response) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD2(payload, LIBPM_MODULE_ID, PM_GET_DEVICE_STATUS, + device_id); + + return pm_ipi_send_sync(primary_proc, payload, response, 3); +} + +/** + * pm_reset_assert() - Assert/De-assert reset + * @reset Reset ID + * @assert Assert (1) or de-assert (0) + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_reset_assert(uint32_t reset, bool assert) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD3(payload, LIBPM_MODULE_ID, PM_RESET_ASSERT, reset, + assert); + + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_reset_get_status() - Get current status of a reset line + * @reset Reset ID + * @status Returns current status of selected reset ID + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_reset_get_status(uint32_t reset, uint32_t *status) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD2(payload, LIBPM_MODULE_ID, PM_RESET_ASSERT, reset); + + return pm_ipi_send_sync(primary_proc, payload, status, 1); +} + +/** + * pm_get_callbackdata() - Read from IPI response buffer + * @data - array of PAYLOAD_ARG_CNT elements + * + * Read value from ipi buffer response buffer. + */ +void pm_get_callbackdata(uint32_t *data, size_t count) +{ + /* Return if interrupt is not from PMU */ + if (!pm_ipi_irq_status(primary_proc)) + return; + + pm_ipi_buff_read_callb(data, count); + pm_ipi_irq_clear(primary_proc); +} + +/** + * pm_pinctrl_request() - Request a pin + * @pin Pin ID + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_pinctrl_request(uint32_t pin) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD2(payload, LIBPM_MODULE_ID, PM_PINCTRL_REQUEST, pin); + + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_pinctrl_release() - Release a pin + * @pin Pin ID + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_pinctrl_release(uint32_t pin) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD2(payload, LIBPM_MODULE_ID, PM_PINCTRL_RELEASE, pin); + + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_pinctrl_set_function() - Set pin function + * @pin Pin ID + * @function Function ID + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_pinctrl_set_function(uint32_t pin, uint32_t function) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD3(payload, LIBPM_MODULE_ID, PM_PINCTRL_SET_FUNCTION, pin, + function) + + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_pinctrl_get_function() - Get function set on the pin + * @pin Pin ID + * @function Function set on the pin + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_pinctrl_get_function(uint32_t pin, uint32_t *function) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD2(payload, LIBPM_MODULE_ID, PM_PINCTRL_SET_FUNCTION, + pin); + + return pm_ipi_send_sync(primary_proc, payload, function, 1); +} + +/** + * pm_pinctrl_set_pin_param() - Set configuration parameter for the pin + * @pin Pin ID + * @param Parameter ID + * @value Parameter value + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_pinctrl_set_pin_param(uint32_t pin, uint32_t param, + uint32_t value) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD4(payload, LIBPM_MODULE_ID, PM_PINCTRL_CONFIG_PARAM_SET, + pin, param, value); + + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_pinctrl_get_pin_param() - Get configuration parameter value for the pin + * @pin Pin ID + * @param Parameter ID + * @value Buffer to store parameter value + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_pinctrl_get_pin_param(uint32_t pin, uint32_t param, + uint32_t *value) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD3(payload, LIBPM_MODULE_ID, PM_PINCTRL_CONFIG_PARAM_GET, + pin, param); + + return pm_ipi_send_sync(primary_proc, payload, value, 1); +} + +/** + * pm_clock_enable() - Enable the clock + * @clk_id Clock ID + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_clock_enable(uint32_t clk_id) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD2(payload, LIBPM_MODULE_ID, PM_CLOCK_ENABLE, clk_id); + + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_clock_disable() - Disable the clock + * @clk_id Clock ID + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_clock_disable(uint32_t clk_id) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD2(payload, LIBPM_MODULE_ID, PM_CLOCK_DISABLE, clk_id); + + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_clock_get_state() - Get clock status + * @clk_id Clock ID + * @state: Buffer to store clock status (1: Enabled, 0:Disabled) + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_clock_get_state(uint32_t clk_id, uint32_t *state) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD2(payload, LIBPM_MODULE_ID, PM_CLOCK_GETSTATE, clk_id); + + return pm_ipi_send_sync(primary_proc, payload, state, 1); +} + +/** + * pm_clock_set_divider() - Set divider for the clock + * @clk_id Clock ID + * @divider Divider value + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_clock_set_divider(uint32_t clk_id, uint32_t divider) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD3(payload, LIBPM_MODULE_ID, PM_CLOCK_SETDIVIDER, clk_id, + divider); + + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_clock_get_divider() - Get divider value for the clock + * @clk_id Clock ID + * @divider: Buffer to store clock divider value + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_clock_get_divider(uint32_t clk_id, uint32_t *divider) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD2(payload, LIBPM_MODULE_ID, PM_CLOCK_GETDIVIDER, clk_id); + + return pm_ipi_send_sync(primary_proc, payload, divider, 1); +} + +/** + * pm_clock_set_parent() - Set parent for the clock + * @clk_id Clock ID + * @parent Parent ID + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_clock_set_parent(uint32_t clk_id, uint32_t parent) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD3(payload, LIBPM_MODULE_ID, PM_CLOCK_SETPARENT, clk_id, + parent); + + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_clock_get_parent() - Get parent value for the clock + * @clk_id Clock ID + * @parent: Buffer to store clock parent value + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_clock_get_parent(uint32_t clk_id, uint32_t *parent) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD2(payload, LIBPM_MODULE_ID, PM_CLOCK_GETPARENT, clk_id); + + return pm_ipi_send_sync(primary_proc, payload, parent, 1); +} + +/** + * pm_pll_set_param() - Set PLL parameter + * @clk_id PLL clock ID + * @param PLL parameter ID + * @value Value to set for PLL parameter + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_pll_set_param(uint32_t clk_id, uint32_t param, + uint32_t value) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD4(payload, LIBPM_MODULE_ID, PM_PLL_SET_PARAMETER, clk_id, + param, value); + + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_pll_get_param() - Get PLL parameter value + * @clk_id PLL clock ID + * @param PLL parameter ID + * @value: Buffer to store PLL parameter value + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_pll_get_param(uint32_t clk_id, uint32_t param, + uint32_t *value) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD3(payload, LIBPM_MODULE_ID, PM_PLL_GET_PARAMETER, clk_id, + param); + + return pm_ipi_send_sync(primary_proc, payload, value, 1); +} + +/** + * pm_pll_set_mode() - Set PLL mode + * @clk_id PLL clock ID + * @mode PLL mode + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_pll_set_mode(uint32_t clk_id, uint32_t mode) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD3(payload, LIBPM_MODULE_ID, PM_PLL_SET_MODE, clk_id, + mode); + + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_pll_get_mode() - Get PLL mode + * @clk_id PLL clock ID + * @mode: Buffer to store PLL mode + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_pll_get_mode(uint32_t clk_id, uint32_t *mode) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD2(payload, LIBPM_MODULE_ID, PM_PLL_GET_MODE, clk_id); + + return pm_ipi_send_sync(primary_proc, payload, mode, 1); +} + +/** + * pm_force_powerdown() - PM call to request for another PU or subsystem to + * be powered down forcefully + * @target Device ID of the PU node to be forced powered down. + * @ack Flag to specify whether acknowledge is requested + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_force_powerdown(uint32_t target, uint8_t ack) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD3(payload, LIBPM_MODULE_ID, PM_FORCE_POWERDOWN, target, + ack); + + if (ack == IPI_BLOCKING) + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); + else + return pm_ipi_send(primary_proc, payload); +} + +/** + * pm_system_shutdown() - PM call to request a system shutdown or restart + * @type Shutdown or restart? 0=shutdown, 1=restart, 2=setscope + * @subtype Scope: 0=APU-subsystem, 1=PS, 2=system + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_system_shutdown(uint32_t type, uint32_t subtype) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + if (type == XPM_SHUTDOWN_TYPE_SETSCOPE_ONLY) { + /* Setting scope for subsequent PSCI reboot or shutdown */ + pm_shutdown_scope = subtype; + return PM_RET_SUCCESS; + } + + /* Send request to the PMC */ + PM_PACK_PAYLOAD3(payload, LIBPM_MODULE_ID, PM_SYSTEM_SHUTDOWN, type, + subtype); + + return pm_ipi_send_non_blocking(primary_proc, payload); +} + +/** +* pm_query_data() - PM API for querying firmware data +* @qid The type of data to query +* @arg1 Argument 1 to requested query data call +* @arg2 Argument 2 to requested query data call +* @arg3 Argument 3 to requested query data call +* @data Returned output data +* +* This function returns requested data. +*/ +enum pm_ret_status pm_query_data(uint32_t qid, uint32_t arg1, uint32_t arg2, + uint32_t arg3, uint32_t *data) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD5(payload, LIBPM_MODULE_ID, PM_QUERY_DATA, qid, arg1, + arg2, arg3); + return pm_ipi_send_sync(primary_proc, payload, data, 4); +} +/** + * pm_api_ioctl() - PM IOCTL API for device control and configs + * @device_id Device ID + * @ioctl_id ID of the requested IOCTL + * @arg1 Argument 1 to requested IOCTL call + * @arg2 Argument 2 to requested IOCTL call + * @value Returned output value + * + * This function calls IOCTL to firmware for device control and configuration. + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_api_ioctl(uint32_t device_id, uint32_t ioctl_id, + uint32_t arg1, uint32_t arg2, uint32_t *value) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + switch (ioctl_id) { + case IOCTL_SET_PLL_FRAC_MODE: + return pm_pll_set_mode(arg1, arg2); + case IOCTL_GET_PLL_FRAC_MODE: + return pm_pll_get_mode(arg1, value); + case IOCTL_SET_PLL_FRAC_DATA: + return pm_pll_set_param(arg1, PM_PLL_PARAM_DATA, arg2); + case IOCTL_GET_PLL_FRAC_DATA: + return pm_pll_get_param(arg1, PM_PLL_PARAM_DATA, value); + default: + /* Send request to the PMC */ + PM_PACK_PAYLOAD5(payload, LIBPM_MODULE_ID, PM_IOCTL, device_id, + ioctl_id, arg1, arg2); + return pm_ipi_send_sync(primary_proc, payload, value, 1); + } +} + +/** + * pm_set_wakeup_source() - PM call to specify the wakeup source while suspended + * @target Device id of the targeted PU or subsystem + * @wkup_node Device id of the wakeup peripheral + * @enable Enable or disable the specified peripheral as wake source + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_set_wakeup_source(uint32_t target, uint32_t wkup_device, + uint8_t enable) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + PM_PACK_PAYLOAD4(payload, LIBPM_MODULE_ID, PM_SET_WAKEUP_SOURCE, target, + wkup_device, enable); + return pm_ipi_send(primary_proc, payload); +} + +/** + * pm_get_chipid() - Read silicon ID registers + * @value Buffer for return values. Must be large enough + * to hold 8 bytes. + * + * @return Returns silicon ID registers + */ +enum pm_ret_status pm_get_chipid(uint32_t *value) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD1(payload, LIBPM_MODULE_ID, PM_GET_CHIPID); + + return pm_ipi_send_sync(primary_proc, payload, value, 2); +} + +/** + * pm_feature_check() - Returns the supported API version if supported + * @api_id API ID to check + * @value Returned supported API version + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_feature_check(uint32_t api_id, unsigned int *version) +{ + uint32_t payload[PAYLOAD_ARG_CNT], fw_api_version; + uint32_t status; + + switch (api_id) { + case PM_GET_CALLBACK_DATA: + case PM_GET_TRUSTZONE_VERSION: + case PM_INIT_FINALIZE: + *version = (PM_API_BASE_VERSION << 16); + return PM_RET_SUCCESS; + case PM_GET_API_VERSION: + case PM_GET_DEVICE_STATUS: + case PM_GET_OP_CHARACTERISTIC: + case PM_REQ_SUSPEND: + case PM_SELF_SUSPEND: + case PM_FORCE_POWERDOWN: + case PM_ABORT_SUSPEND: + case PM_REQ_WAKEUP: + case PM_SET_WAKEUP_SOURCE: + case PM_SYSTEM_SHUTDOWN: + case PM_REQUEST_DEVICE: + case PM_RELEASE_DEVICE: + case PM_SET_REQUIREMENT: + case PM_RESET_ASSERT: + case PM_RESET_GET_STATUS: + case PM_PINCTRL_REQUEST: + case PM_PINCTRL_RELEASE: + case PM_PINCTRL_GET_FUNCTION: + case PM_PINCTRL_SET_FUNCTION: + case PM_PINCTRL_CONFIG_PARAM_GET: + case PM_PINCTRL_CONFIG_PARAM_SET: + case PM_IOCTL: + case PM_QUERY_DATA: + case PM_CLOCK_ENABLE: + case PM_CLOCK_DISABLE: + case PM_CLOCK_GETSTATE: + case PM_CLOCK_SETDIVIDER: + case PM_CLOCK_GETDIVIDER: + case PM_CLOCK_SETPARENT: + case PM_CLOCK_GETPARENT: + case PM_PLL_SET_PARAMETER: + case PM_PLL_GET_PARAMETER: + case PM_PLL_SET_MODE: + case PM_PLL_GET_MODE: + case PM_FEATURE_CHECK: + *version = (PM_API_BASE_VERSION << 16); + break; + case PM_LOAD_PDI: + *version = (PM_API_BASE_VERSION << 16); + return PM_RET_SUCCESS; + default: + *version = 0U; + return PM_RET_ERROR_NOFEATURE; + } + + PM_PACK_PAYLOAD2(payload, LIBPM_MODULE_ID, PM_FEATURE_CHECK, api_id); + + status = pm_ipi_send_sync(primary_proc, payload, &fw_api_version, 1); + if (status != PM_RET_SUCCESS) + return status; + + *version |= fw_api_version; + + return PM_RET_SUCCESS; +} + +/** + * pm_load_pdi() - Load the PDI + * + * This function provides support to load PDI from linux + * + * src: Source device of pdi(DDR, OCM, SD etc) + * address_low: lower 32-bit Linear memory space address + * address_high: higher 32-bit Linear memory space address + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_load_pdi(uint32_t src, + uint32_t address_low, uint32_t address_high) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMU */ + PM_PACK_PAYLOAD4(payload, LOADER_MODULE_ID, PM_LOAD_PDI, src, + address_high, address_low); + return pm_ipi_send_sync(primary_proc, payload, NULL, 0); +} + +/** + * pm_get_op_characteristic() - PM call to request operating characteristics + * of a device + * @device_id Device id + * @type Type of the operating characteristic + * (power, temperature and latency) + * @result Returns the operating characteristic for the requested device, + * specified by the type + * + * @return Returns status, either success or error+reason + */ +enum pm_ret_status pm_get_op_characteristic(uint32_t device_id, + enum pm_opchar_type type, + uint32_t *result) +{ + uint32_t payload[PAYLOAD_ARG_CNT]; + + /* Send request to the PMC */ + PM_PACK_PAYLOAD3(payload, LIBPM_MODULE_ID, PM_GET_OP_CHARACTERISTIC, + device_id, type); + return pm_ipi_send_sync(primary_proc, payload, result, 1); +} diff --git a/plat/xilinx/versal/pm_service/pm_api_sys.h b/plat/xilinx/versal/pm_service/pm_api_sys.h new file mode 100644 index 0000000000..4de592a2f4 --- /dev/null +++ b/plat/xilinx/versal/pm_service/pm_api_sys.h @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2019, Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PM_API_SYS_H +#define PM_API_SYS_H + +#include <stdint.h> +#include "pm_defs.h" + +/********************************************************** + * PM API function declarations + **********************************************************/ + +enum pm_ret_status pm_get_api_version(unsigned int *version); +enum pm_ret_status pm_self_suspend(uint32_t nid, + unsigned int latency, + unsigned int state, + uintptr_t address); +enum pm_ret_status pm_abort_suspend(enum pm_abort_reason reason); +enum pm_ret_status pm_req_suspend(uint32_t target, + uint8_t ack, + unsigned int latency, + unsigned int state); +enum pm_ret_status pm_req_wakeup(uint32_t target, uint32_t set_address, + uintptr_t address, uint8_t ack); +enum pm_ret_status pm_set_wakeup_source(uint32_t target, uint32_t device_id, + uint8_t enable); +enum pm_ret_status pm_request_device(uint32_t device_id, uint32_t capabilities, + uint32_t qos, uint32_t ack); +enum pm_ret_status pm_release_device(uint32_t device_id); +enum pm_ret_status pm_set_requirement(uint32_t device_id, uint32_t capabilities, + uint32_t latency, uint32_t qos); +enum pm_ret_status pm_get_device_status(uint32_t device_id, uint32_t *response); +enum pm_ret_status pm_reset_assert(uint32_t reset, bool assert); +enum pm_ret_status pm_reset_get_status(uint32_t reset, uint32_t *status); +void pm_get_callbackdata(uint32_t *data, size_t count); +enum pm_ret_status pm_pinctrl_request(uint32_t pin); +enum pm_ret_status pm_pinctrl_release(uint32_t pin); +enum pm_ret_status pm_pinctrl_set_function(uint32_t pin, uint32_t function); +enum pm_ret_status pm_pinctrl_get_function(uint32_t pin, uint32_t *function); +enum pm_ret_status pm_pinctrl_set_pin_param(uint32_t pin, uint32_t param, + uint32_t value); +enum pm_ret_status pm_pinctrl_get_pin_param(uint32_t pin, uint32_t param, + uint32_t *value); +enum pm_ret_status pm_clock_enable(uint32_t clk_id); +enum pm_ret_status pm_clock_disable(uint32_t clk_id); +enum pm_ret_status pm_clock_get_state(uint32_t clk_id, uint32_t *state); +enum pm_ret_status pm_clock_set_divider(uint32_t clk_id, uint32_t divider); +enum pm_ret_status pm_clock_get_divider(uint32_t clk_id, uint32_t *divider); +enum pm_ret_status pm_clock_set_parent(uint32_t clk_id, uint32_t parent); +enum pm_ret_status pm_clock_get_parent(uint32_t clk_id, uint32_t *parent); +enum pm_ret_status pm_pll_set_param(uint32_t clk_id, uint32_t param, + uint32_t value); +enum pm_ret_status pm_pll_get_param(uint32_t clk_id, uint32_t param, + uint32_t *value); +enum pm_ret_status pm_pll_set_mode(uint32_t clk_id, uint32_t mode); +enum pm_ret_status pm_pll_get_mode(uint32_t clk_id, uint32_t *mode); +enum pm_ret_status pm_force_powerdown(uint32_t target, uint8_t ack); +enum pm_ret_status pm_system_shutdown(uint32_t type, uint32_t subtype); +enum pm_ret_status pm_api_ioctl(uint32_t device_id, uint32_t ioctl_id, + uint32_t arg1, uint32_t arg2, uint32_t *value); +enum pm_ret_status pm_query_data(uint32_t qid, uint32_t arg1, uint32_t arg2, + uint32_t arg3, uint32_t *data); +unsigned int pm_get_shutdown_scope(void); +enum pm_ret_status pm_get_chipid(uint32_t *value); +enum pm_ret_status pm_feature_check(uint32_t api_id, unsigned int *version); +enum pm_ret_status pm_load_pdi(uint32_t src, uint32_t address_low, + uint32_t address_high); +enum pm_ret_status pm_get_op_characteristic(uint32_t device_id, + enum pm_opchar_type type, + uint32_t *result); +#endif /* PM_API_SYS_H */ diff --git a/plat/xilinx/versal/pm_service/pm_client.c b/plat/xilinx/versal/pm_service/pm_client.c new file mode 100644 index 0000000000..5b47838e90 --- /dev/null +++ b/plat/xilinx/versal/pm_service/pm_client.c @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2019, Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * APU specific definition of processors in the subsystem as well as functions + * for getting information about and changing state of the APU. + */ + +#include <assert.h> +#include <plat_ipi.h> +#include <platform_def.h> +#include <versal_def.h> +#include <lib/bakery_lock.h> +#include <lib/mmio.h> +#include <lib/utils.h> +#include <drivers/arm/gicv3.h> +#include <drivers/arm/gic_common.h> +#include <plat/common/platform.h> +#include "pm_api_sys.h" +#include "pm_client.h" + +#define UNDEFINED_CPUID (~0) +#define IRQ_MAX 142 +#define NUM_GICD_ISENABLER ((IRQ_MAX >> 5) + 1) + +DEFINE_BAKERY_LOCK(pm_client_secure_lock); + +static const struct pm_ipi apu_ipi = { + .local_ipi_id = IPI_ID_APU, + .remote_ipi_id = IPI_ID_PMC, + .buffer_base = IPI_BUFFER_APU_BASE, +}; + +/* Order in pm_procs_all array must match cpu ids */ +static const struct pm_proc pm_procs_all[] = { + { + .node_id = XPM_DEVID_ACPU_0, + .ipi = &apu_ipi, + .pwrdn_mask = APU_0_PWRCTL_CPUPWRDWNREQ_MASK, + }, + { + .node_id = XPM_DEVID_ACPU_1, + .ipi = &apu_ipi, + .pwrdn_mask = APU_1_PWRCTL_CPUPWRDWNREQ_MASK, + } +}; + +const struct pm_proc *primary_proc = &pm_procs_all[0]; + +/* Interrupt to PM node index map */ +static enum pm_device_node_idx irq_node_map[IRQ_MAX + 1] = { + [13] = XPM_NODEIDX_DEV_GPIO, + [14] = XPM_NODEIDX_DEV_I2C_0, + [15] = XPM_NODEIDX_DEV_I2C_1, + [16] = XPM_NODEIDX_DEV_SPI_0, + [17] = XPM_NODEIDX_DEV_SPI_1, + [18] = XPM_NODEIDX_DEV_UART_0, + [19] = XPM_NODEIDX_DEV_UART_1, + [20] = XPM_NODEIDX_DEV_CAN_FD_0, + [21] = XPM_NODEIDX_DEV_CAN_FD_1, + [22] = XPM_NODEIDX_DEV_USB_0, + [23] = XPM_NODEIDX_DEV_USB_0, + [24] = XPM_NODEIDX_DEV_USB_0, + [25] = XPM_NODEIDX_DEV_USB_0, + [26] = XPM_NODEIDX_DEV_USB_0, + [37] = XPM_NODEIDX_DEV_TTC_0, + [38] = XPM_NODEIDX_DEV_TTC_0, + [39] = XPM_NODEIDX_DEV_TTC_0, + [40] = XPM_NODEIDX_DEV_TTC_1, + [41] = XPM_NODEIDX_DEV_TTC_1, + [42] = XPM_NODEIDX_DEV_TTC_1, + [43] = XPM_NODEIDX_DEV_TTC_2, + [44] = XPM_NODEIDX_DEV_TTC_2, + [45] = XPM_NODEIDX_DEV_TTC_2, + [46] = XPM_NODEIDX_DEV_TTC_3, + [47] = XPM_NODEIDX_DEV_TTC_3, + [48] = XPM_NODEIDX_DEV_TTC_3, + [56] = XPM_NODEIDX_DEV_GEM_0, + [57] = XPM_NODEIDX_DEV_GEM_0, + [58] = XPM_NODEIDX_DEV_GEM_1, + [59] = XPM_NODEIDX_DEV_GEM_1, + [60] = XPM_NODEIDX_DEV_ADMA_0, + [61] = XPM_NODEIDX_DEV_ADMA_1, + [62] = XPM_NODEIDX_DEV_ADMA_2, + [63] = XPM_NODEIDX_DEV_ADMA_3, + [64] = XPM_NODEIDX_DEV_ADMA_4, + [65] = XPM_NODEIDX_DEV_ADMA_5, + [66] = XPM_NODEIDX_DEV_ADMA_6, + [67] = XPM_NODEIDX_DEV_ADMA_7, + [74] = XPM_NODEIDX_DEV_USB_0, + [126] = XPM_NODEIDX_DEV_SDIO_0, + [127] = XPM_NODEIDX_DEV_SDIO_0, + [128] = XPM_NODEIDX_DEV_SDIO_1, + [129] = XPM_NODEIDX_DEV_SDIO_1, + [142] = XPM_NODEIDX_DEV_RTC, +}; + +/** + * irq_to_pm_node_idx - Get PM node index corresponding to the interrupt number + * @irq: Interrupt number + * + * Return: PM node index corresponding to the specified interrupt + */ +static enum pm_device_node_idx irq_to_pm_node_idx(unsigned int irq) +{ + assert(irq <= IRQ_MAX); + return irq_node_map[irq]; +} + +/** + * pm_client_set_wakeup_sources - Set all devices with enabled interrupts as + * wake sources in the LibPM. + */ +static void pm_client_set_wakeup_sources(void) +{ + uint32_t reg_num; + uint32_t device_id; + uint8_t pm_wakeup_nodes_set[XPM_NODEIDX_DEV_MAX]; + uintptr_t isenabler1 = PLAT_VERSAL_GICD_BASE + GICD_ISENABLER + 4; + + zeromem(&pm_wakeup_nodes_set, sizeof(pm_wakeup_nodes_set)); + + for (reg_num = 0; reg_num < NUM_GICD_ISENABLER; reg_num++) { + uint32_t base_irq = reg_num << ISENABLER_SHIFT; + uint32_t reg = mmio_read_32(isenabler1 + (reg_num << 2)); + + if (!reg) + continue; + + while (reg) { + enum pm_device_node_idx node_idx; + uint32_t idx, ret, irq, lowest_set = reg & (-reg); + + idx = __builtin_ctz(lowest_set); + irq = base_irq + idx; + + if (irq > IRQ_MAX) + break; + + node_idx = irq_to_pm_node_idx(irq); + reg &= ~lowest_set; + + if ((node_idx != XPM_NODEIDX_DEV_MIN) && + (!pm_wakeup_nodes_set[node_idx])) { + /* Get device ID from node index */ + device_id = PERIPH_DEVID(node_idx); + ret = pm_set_wakeup_source(XPM_DEVID_ACPU_0, + device_id, 1); + pm_wakeup_nodes_set[node_idx] = !ret; + } + } + } +} + +/** + * pm_client_suspend() - Client-specific suspend actions + * + * This function should contain any PU-specific actions + * required prior to sending suspend request to PMU + * Actions taken depend on the state system is suspending to. + */ +void pm_client_suspend(const struct pm_proc *proc, unsigned int state) +{ + bakery_lock_get(&pm_client_secure_lock); + + if (state == PM_STATE_SUSPEND_TO_RAM) + pm_client_set_wakeup_sources(); + + /* Set powerdown request */ + mmio_write_32(FPD_APU_PWRCTL, mmio_read_32(FPD_APU_PWRCTL) | + proc->pwrdn_mask); + + bakery_lock_release(&pm_client_secure_lock); +} + +/** + * pm_client_abort_suspend() - Client-specific abort-suspend actions + * + * This function should contain any PU-specific actions + * required for aborting a prior suspend request + */ +void pm_client_abort_suspend(void) +{ + /* Enable interrupts at processor level (for current cpu) */ + gicv3_cpuif_enable(plat_my_core_pos()); + + bakery_lock_get(&pm_client_secure_lock); + + /* Clear powerdown request */ + mmio_write_32(FPD_APU_PWRCTL, mmio_read_32(FPD_APU_PWRCTL) & + ~primary_proc->pwrdn_mask); + + bakery_lock_release(&pm_client_secure_lock); +} + +/** + * pm_get_cpuid() - get the local cpu ID for a global node ID + * @nid: node id of the processor + * + * Return: the cpu ID (starting from 0) for the subsystem + */ +static unsigned int pm_get_cpuid(uint32_t nid) +{ + for (size_t i = 0; i < ARRAY_SIZE(pm_procs_all); i++) { + if (pm_procs_all[i].node_id == nid) + return i; + } + return UNDEFINED_CPUID; +} + +/** + * pm_client_wakeup() - Client-specific wakeup actions + * + * This function should contain any PU-specific actions + * required for waking up another APU core + */ +void pm_client_wakeup(const struct pm_proc *proc) +{ + unsigned int cpuid = pm_get_cpuid(proc->node_id); + + if (cpuid == UNDEFINED_CPUID) + return; + + bakery_lock_get(&pm_client_secure_lock); + + /* clear powerdown bit for affected cpu */ + uint32_t val = mmio_read_32(FPD_APU_PWRCTL); + val &= ~(proc->pwrdn_mask); + mmio_write_32(FPD_APU_PWRCTL, val); + + bakery_lock_release(&pm_client_secure_lock); +} + +/** + * pm_get_proc() - returns pointer to the proc structure + * @cpuid: id of the cpu whose proc struct pointer should be returned + * + * Return: pointer to a proc structure if proc is found, otherwise NULL + */ +const struct pm_proc *pm_get_proc(unsigned int cpuid) +{ + if (cpuid < ARRAY_SIZE(pm_procs_all)) + return &pm_procs_all[cpuid]; + + return NULL; +} diff --git a/plat/xilinx/versal/pm_service/pm_defs.h b/plat/xilinx/versal/pm_service/pm_defs.h new file mode 100644 index 0000000000..966b00bb50 --- /dev/null +++ b/plat/xilinx/versal/pm_service/pm_defs.h @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2019, Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* Versal power management enums and defines */ + +#ifndef PM_DEFS_H +#define PM_DEFS_H + +#include "pm_node.h" + +/********************************************************************* + * Macro definitions + ********************************************************************/ + +/* State arguments of the self suspend */ +#define PM_STATE_CPU_IDLE 0x0U +#define PM_STATE_SUSPEND_TO_RAM 0xFU + +#define MAX_LATENCY (~0U) +#define MAX_QOS 100U + +/* Processor core device IDs */ +#define APU_DEVID(IDX) NODEID(XPM_NODECLASS_DEVICE, XPM_NODESUBCL_DEV_CORE, \ + XPM_NODETYPE_DEV_CORE_APU, (IDX)) + +#define XPM_DEVID_ACPU_0 APU_DEVID(XPM_NODEIDX_DEV_ACPU_0) +#define XPM_DEVID_ACPU_1 APU_DEVID(XPM_NODEIDX_DEV_ACPU_1) + +#define PERIPH_DEVID(IDX) NODEID(XPM_NODECLASS_DEVICE, \ + XPM_NODESUBCL_DEV_PERIPH, \ + XPM_NODETYPE_DEV_PERIPH, (IDX)) + +#define PM_GET_CALLBACK_DATA 0xa01 +#define PM_GET_TRUSTZONE_VERSION 0xa03 + +/* PM API Versions */ +#define PM_API_BASE_VERSION 1U + +/* PM API ids */ +#define PM_GET_API_VERSION 1U +#define PM_GET_DEVICE_STATUS 3U +#define PM_GET_OP_CHARACTERISTIC 4U +#define PM_REQ_SUSPEND 6U +#define PM_SELF_SUSPEND 7U +#define PM_FORCE_POWERDOWN 8U +#define PM_ABORT_SUSPEND 9U +#define PM_REQ_WAKEUP 10U +#define PM_SET_WAKEUP_SOURCE 11U +#define PM_SYSTEM_SHUTDOWN 12U +#define PM_REQUEST_DEVICE 13U +#define PM_RELEASE_DEVICE 14U +#define PM_SET_REQUIREMENT 15U +#define PM_RESET_ASSERT 17U +#define PM_RESET_GET_STATUS 18U +#define PM_INIT_FINALIZE 21U +#define PM_GET_CHIPID 24U +#define PM_PINCTRL_REQUEST 28U +#define PM_PINCTRL_RELEASE 29U +#define PM_PINCTRL_GET_FUNCTION 30U +#define PM_PINCTRL_SET_FUNCTION 31U +#define PM_PINCTRL_CONFIG_PARAM_GET 32U +#define PM_PINCTRL_CONFIG_PARAM_SET 33U +#define PM_IOCTL 34U +#define PM_QUERY_DATA 35U +#define PM_CLOCK_ENABLE 36U +#define PM_CLOCK_DISABLE 37U +#define PM_CLOCK_GETSTATE 38U +#define PM_CLOCK_SETDIVIDER 39U +#define PM_CLOCK_GETDIVIDER 40U +#define PM_CLOCK_SETRATE 41U +#define PM_CLOCK_GETRATE 42U +#define PM_CLOCK_SETPARENT 43U +#define PM_CLOCK_GETPARENT 44U +#define PM_PLL_SET_PARAMETER 48U +#define PM_PLL_GET_PARAMETER 49U +#define PM_PLL_SET_MODE 50U +#define PM_PLL_GET_MODE 51U +#define PM_FEATURE_CHECK 63U + +/* Loader API ids */ +#define PM_LOAD_PDI 0x701U + +/* IOCTL IDs for clock driver */ +#define IOCTL_SET_PLL_FRAC_MODE 8 +#define IOCTL_GET_PLL_FRAC_MODE 9 +#define IOCTL_SET_PLL_FRAC_DATA 10 +#define IOCTL_GET_PLL_FRAC_DATA 11 + +/* Parameter ID for PLL IOCTLs */ +/* Fractional data portion for PLL */ +#define PM_PLL_PARAM_DATA 2 + +/* System shutdown macros */ +#define XPM_SHUTDOWN_TYPE_SHUTDOWN 0U +#define XPM_SHUTDOWN_TYPE_RESET 1U +#define XPM_SHUTDOWN_TYPE_SETSCOPE_ONLY 2U + +#define XPM_SHUTDOWN_SUBTYPE_RST_SUBSYSTEM 0U +#define XPM_SHUTDOWN_SUBTYPE_RST_PS_ONLY 1U +#define XPM_SHUTDOWN_SUBTYPE_RST_SYSTEM 2U + +/********************************************************************* + * Enum definitions + ********************************************************************/ + +enum pm_abort_reason { + ABORT_REASON_WKUP_EVENT = 100, + ABORT_REASON_PU_BUSY, + ABORT_REASON_NO_PWRDN, + ABORT_REASON_UNKNOWN, +}; + +enum pm_opchar_type { + PM_OPCHAR_TYPE_POWER = 1, + PM_OPCHAR_TYPE_TEMP, + PM_OPCHAR_TYPE_LATENCY, +}; + +/** + * Subsystem IDs + */ +typedef enum { + XPM_SUBSYSID_PMC, + XPM_SUBSYSID_PSM, + XPM_SUBSYSID_APU, + XPM_SUBSYSID_RPU0_LOCK, + XPM_SUBSYSID_RPU0_0, + XPM_SUBSYSID_RPU0_1, + XPM_SUBSYSID_DDR0, + XPM_SUBSYSID_ME, + XPM_SUBSYSID_PL, + XPM_SUBSYSID_MAX, +} XPm_SubsystemId; + +/** + * @PM_RET_SUCCESS: success + * @PM_RET_ERROR_ARGS: illegal arguments provided (deprecated) + * @PM_RET_ERROR_NOTSUPPORTED: feature not supported (deprecated) + * @PM_RET_ERROR_NOFEATURE: feature is not available + * @PM_RET_ERROR_INTERNAL: internal error + * @PM_RET_ERROR_CONFLICT: conflict + * @PM_RET_ERROR_ACCESS: access rights violation + * @PM_RET_ERROR_INVALID_NODE: invalid node + * @PM_RET_ERROR_DOUBLE_REQ: duplicate request for same node + * @PM_RET_ERROR_ABORT_SUSPEND: suspend procedure has been aborted + * @PM_RET_ERROR_TIMEOUT: timeout in communication with PMU + * @PM_RET_ERROR_NODE_USED: node is already in use + */ +enum pm_ret_status { + PM_RET_SUCCESS, + PM_RET_ERROR_ARGS = 1, + PM_RET_ERROR_NOTSUPPORTED = 4, + PM_RET_ERROR_NOFEATURE = 19, + PM_RET_ERROR_INTERNAL = 2000, + PM_RET_ERROR_CONFLICT = 2001, + PM_RET_ERROR_ACCESS = 2002, + PM_RET_ERROR_INVALID_NODE = 2003, + PM_RET_ERROR_DOUBLE_REQ = 2004, + PM_RET_ERROR_ABORT_SUSPEND = 2005, + PM_RET_ERROR_TIMEOUT = 2006, + PM_RET_ERROR_NODE_USED = 2007 +}; +#endif /* PM_DEFS_H */ diff --git a/plat/xilinx/versal/pm_service/pm_node.h b/plat/xilinx/versal/pm_service/pm_node.h new file mode 100644 index 0000000000..1b82ec70de --- /dev/null +++ b/plat/xilinx/versal/pm_service/pm_node.h @@ -0,0 +1,192 @@ +/* + * Copyright (c) 2019, Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* Versal PM nodes enums and defines */ + +#ifndef PM_NODE_H +#define PM_NODE_H + +/********************************************************************* + * Macro definitions + ********************************************************************/ + +#define NODE_CLASS_SHIFT 26U +#define NODE_SUBCLASS_SHIFT 20U +#define NODE_TYPE_SHIFT 14U +#define NODE_INDEX_SHIFT 0U +#define NODE_CLASS_MASK_BITS 0x3F +#define NODE_SUBCLASS_MASK_BITS 0x3F +#define NODE_TYPE_MASK_BITS 0x3F +#define NODE_INDEX_MASK_BITS 0x3FFF +#define NODE_CLASS_MASK (NODE_CLASS_MASK_BITS << NODE_CLASS_SHIFT) +#define NODE_SUBCLASS_MASK (NODE_SUBCLASS_MASK_BITS << NODE_SUBCLASS_SHIFT) +#define NODE_TYPE_MASK (NODE_TYPE_MASK_BITS << NODE_TYPE_SHIFT) +#define NODE_INDEX_MASK (NODE_INDEX_MASK_BITS << NODE_INDEX_SHIFT) + +#define NODEID(CLASS, SUBCLASS, TYPE, INDEX) \ + ((((CLASS) & NODE_CLASS_MASK_BITS) << NODE_CLASS_SHIFT) | \ + (((SUBCLASS) & NODE_SUBCLASS_MASK_BITS) << NODE_SUBCLASS_SHIFT) | \ + (((TYPE) & NODE_TYPE_MASK_BITS) << NODE_TYPE_SHIFT) | \ + (((INDEX) & NODE_INDEX_MASK_BITS) << NODE_INDEX_SHIFT)) + +#define NODECLASS(ID) (((ID) & NODE_CLASS_MASK) >> NODE_CLASS_SHIFT) +#define NODESUBCLASS(ID) (((ID) & NODE_SUBCLASS_MASK) >> \ + NODE_SUBCLASS_SHIFT) +#define NODETYPE(ID) (((ID) & NODE_TYPE_MASK) >> NODE_TYPE_SHIFT) +#define NODEINDEX(ID) (((ID) & NODE_INDEX_MASK) >> NODE_INDEX_SHIFT) + +/********************************************************************* + * Enum definitions + ********************************************************************/ + +/* Node class types */ +enum pm_node_class { + XPM_NODECLASS_MIN, + + XPM_NODECLASS_POWER, + XPM_NODECLASS_CLOCK, + XPM_NODECLASS_RESET, + XPM_NODECLASS_MEMIC, + XPM_NODECLASS_STMIC, + XPM_NODECLASS_DEVICE, + + XPM_NODECLASS_MAX +}; + +enum pm_device_node_subclass { + /* Device types */ + XPM_NODESUBCL_DEV_CORE = 1, + XPM_NODESUBCL_DEV_PERIPH, + XPM_NODESUBCL_DEV_MEM, + XPM_NODESUBCL_DEV_SOC, + XPM_NODESUBCL_DEV_MEM_CTRLR, + XPM_NODESUBCL_DEV_PHY, +}; + +enum pm_device_node_type { + /* Device types */ + XPM_NODETYPE_DEV_CORE_PMC = 1, + XPM_NODETYPE_DEV_CORE_PSM, + XPM_NODETYPE_DEV_CORE_APU, + XPM_NODETYPE_DEV_CORE_RPU, + XPM_NODETYPE_DEV_OCM, + XPM_NODETYPE_DEV_TCM, + XPM_NODETYPE_DEV_L2CACHE, + XPM_NODETYPE_DEV_DDR, + XPM_NODETYPE_DEV_PERIPH, + XPM_NODETYPE_DEV_SOC, + XPM_NODETYPE_DEV_GT, +}; + +/* Device node Indexes */ +enum pm_device_node_idx { + /* Device nodes */ + XPM_NODEIDX_DEV_MIN, + + /* Processor devices */ + XPM_NODEIDX_DEV_PMC_PROC, + XPM_NODEIDX_DEV_PSM_PROC, + XPM_NODEIDX_DEV_ACPU_0, + XPM_NODEIDX_DEV_ACPU_1, + XPM_NODEIDX_DEV_RPU0_0, + XPM_NODEIDX_DEV_RPU0_1, + + /* Memory devices */ + XPM_NODEIDX_DEV_OCM_0, + XPM_NODEIDX_DEV_OCM_1, + XPM_NODEIDX_DEV_OCM_2, + XPM_NODEIDX_DEV_OCM_3, + XPM_NODEIDX_DEV_TCM_0_A, + XPM_NODEIDX_DEV_TCM_0_B, + XPM_NODEIDX_DEV_TCM_1_A, + XPM_NODEIDX_DEV_TCM_1_B, + XPM_NODEIDX_DEV_L2_BANK_0, + XPM_NODEIDX_DEV_DDR_0, + XPM_NODEIDX_DEV_DDR_1, + XPM_NODEIDX_DEV_DDR_2, + XPM_NODEIDX_DEV_DDR_3, + XPM_NODEIDX_DEV_DDR_4, + XPM_NODEIDX_DEV_DDR_5, + XPM_NODEIDX_DEV_DDR_6, + XPM_NODEIDX_DEV_DDR_7, + + /* LPD Peripheral devices */ + XPM_NODEIDX_DEV_USB_0, + XPM_NODEIDX_DEV_GEM_0, + XPM_NODEIDX_DEV_GEM_1, + XPM_NODEIDX_DEV_SPI_0, + XPM_NODEIDX_DEV_SPI_1, + XPM_NODEIDX_DEV_I2C_0, + XPM_NODEIDX_DEV_I2C_1, + XPM_NODEIDX_DEV_CAN_FD_0, + XPM_NODEIDX_DEV_CAN_FD_1, + XPM_NODEIDX_DEV_UART_0, + XPM_NODEIDX_DEV_UART_1, + XPM_NODEIDX_DEV_GPIO, + XPM_NODEIDX_DEV_TTC_0, + XPM_NODEIDX_DEV_TTC_1, + XPM_NODEIDX_DEV_TTC_2, + XPM_NODEIDX_DEV_TTC_3, + XPM_NODEIDX_DEV_SWDT_LPD, + + /* FPD Peripheral devices */ + XPM_NODEIDX_DEV_SWDT_FPD, + + /* PMC Peripheral devices */ + XPM_NODEIDX_DEV_OSPI, + XPM_NODEIDX_DEV_QSPI, + XPM_NODEIDX_DEV_GPIO_PMC, + XPM_NODEIDX_DEV_I2C_PMC, + XPM_NODEIDX_DEV_SDIO_0, + XPM_NODEIDX_DEV_SDIO_1, + + XPM_NODEIDX_DEV_PL_0, + XPM_NODEIDX_DEV_PL_1, + XPM_NODEIDX_DEV_PL_2, + XPM_NODEIDX_DEV_PL_3, + XPM_NODEIDX_DEV_RTC, + XPM_NODEIDX_DEV_ADMA_0, + XPM_NODEIDX_DEV_ADMA_1, + XPM_NODEIDX_DEV_ADMA_2, + XPM_NODEIDX_DEV_ADMA_3, + XPM_NODEIDX_DEV_ADMA_4, + XPM_NODEIDX_DEV_ADMA_5, + XPM_NODEIDX_DEV_ADMA_6, + XPM_NODEIDX_DEV_ADMA_7, + XPM_NODEIDX_DEV_IPI_0, + XPM_NODEIDX_DEV_IPI_1, + XPM_NODEIDX_DEV_IPI_2, + XPM_NODEIDX_DEV_IPI_3, + XPM_NODEIDX_DEV_IPI_4, + XPM_NODEIDX_DEV_IPI_5, + XPM_NODEIDX_DEV_IPI_6, + + /* Entire SoC */ + XPM_NODEIDX_DEV_SOC, + + /* DDR memory controllers */ + XPM_NODEIDX_DEV_DDRMC_0, + XPM_NODEIDX_DEV_DDRMC_1, + XPM_NODEIDX_DEV_DDRMC_2, + XPM_NODEIDX_DEV_DDRMC_3, + + /* GT devices */ + XPM_NODEIDX_DEV_GT_0, + XPM_NODEIDX_DEV_GT_1, + XPM_NODEIDX_DEV_GT_2, + XPM_NODEIDX_DEV_GT_3, + XPM_NODEIDX_DEV_GT_4, + XPM_NODEIDX_DEV_GT_5, + XPM_NODEIDX_DEV_GT_6, + XPM_NODEIDX_DEV_GT_7, + XPM_NODEIDX_DEV_GT_8, + XPM_NODEIDX_DEV_GT_9, + XPM_NODEIDX_DEV_GT_10, + + XPM_NODEIDX_DEV_MAX +}; + +#endif /* PM_NODE_H */ diff --git a/plat/xilinx/versal/pm_service/pm_svc_main.c b/plat/xilinx/versal/pm_service/pm_svc_main.c new file mode 100644 index 0000000000..a3a9f43165 --- /dev/null +++ b/plat/xilinx/versal/pm_service/pm_svc_main.c @@ -0,0 +1,328 @@ +/* + * Copyright (c) 2019, Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Top-level SMC handler for Versal power management calls and + * IPI setup functions for communication with PMC. + */ + +#include <errno.h> +#include <plat_private.h> +#include <stdbool.h> +#include <common/runtime_svc.h> +#include "pm_api_sys.h" +#include "pm_client.h" +#include "pm_ipi.h" + +/* pm_up = true - UP, pm_up = false - DOWN */ +static bool pm_up; + +/** + * pm_setup() - PM service setup + * + * @return On success, the initialization function must return 0. + * Any other return value will cause the framework to ignore + * the service + * + * Initialization functions for Versal power management for + * communicaton with PMC. + * + * Called from sip_svc_setup initialization function with the + * rt_svc_init signature. + */ +int pm_setup(void) +{ + int status, ret = 0; + + status = pm_ipi_init(primary_proc); + + if (status < 0) { + INFO("BL31: PM Service Init Failed, Error Code %d!\n", status); + ret = status; + } else { + pm_up = true; + } + + return ret; +} + +/** + * pm_smc_handler() - SMC handler for PM-API calls coming from EL1/EL2. + * @smc_fid - Function Identifier + * @x1 - x4 - Arguments + * @cookie - Unused + * @handler - Pointer to caller's context structure + * + * @return - Unused + * + * Determines that smc_fid is valid and supported PM SMC Function ID from the + * list of pm_api_ids, otherwise completes the request with + * the unknown SMC Function ID + * + * The SMC calls for PM service are forwarded from SIP Service SMC handler + * function with rt_svc_handle signature + */ +uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, + uint64_t x4, void *cookie, void *handle, uint64_t flags) +{ + enum pm_ret_status ret; + + uint32_t pm_arg[4]; + + /* Handle case where PM wasn't initialized properly */ + if (!pm_up) + SMC_RET1(handle, SMC_UNK); + + pm_arg[0] = (uint32_t)x1; + pm_arg[1] = (uint32_t)(x1 >> 32); + pm_arg[2] = (uint32_t)x2; + pm_arg[3] = (uint32_t)(x2 >> 32); + + switch (smc_fid & FUNCID_NUM_MASK) { + /* PM API Functions */ + case PM_SELF_SUSPEND: + ret = pm_self_suspend(pm_arg[0], pm_arg[1], pm_arg[2], + pm_arg[3]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_FORCE_POWERDOWN: + ret = pm_force_powerdown(pm_arg[0], pm_arg[1]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_REQ_SUSPEND: + ret = pm_req_suspend(pm_arg[0], pm_arg[1], pm_arg[2], + pm_arg[3]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_ABORT_SUSPEND: + ret = pm_abort_suspend(pm_arg[0]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_SYSTEM_SHUTDOWN: + ret = pm_system_shutdown(pm_arg[0], pm_arg[1]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_REQ_WAKEUP: + ret = pm_req_wakeup(pm_arg[0], pm_arg[1], pm_arg[2], pm_arg[3]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_SET_WAKEUP_SOURCE: + ret = pm_set_wakeup_source(pm_arg[0], pm_arg[1], pm_arg[2]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_REQUEST_DEVICE: + ret = pm_request_device(pm_arg[0], pm_arg[1], pm_arg[2], + pm_arg[3]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_RELEASE_DEVICE: + ret = pm_release_device(pm_arg[0]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_SET_REQUIREMENT: + ret = pm_set_requirement(pm_arg[0], pm_arg[1], pm_arg[2], + pm_arg[3]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_GET_API_VERSION: + { + uint32_t api_version; + + ret = pm_get_api_version(&api_version); + SMC_RET1(handle, (uint64_t)PM_RET_SUCCESS | + ((uint64_t)api_version << 32)); + } + + case PM_GET_DEVICE_STATUS: + { + uint32_t buff[3]; + + ret = pm_get_device_status(pm_arg[0], buff); + SMC_RET2(handle, (uint64_t)ret | ((uint64_t)buff[0] << 32), + (uint64_t)buff[1] | ((uint64_t)buff[2] << 32)); + } + + case PM_RESET_ASSERT: + ret = pm_reset_assert(pm_arg[0], pm_arg[1]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_RESET_GET_STATUS: + { + uint32_t reset_status; + + ret = pm_reset_get_status(pm_arg[0], &reset_status); + SMC_RET1(handle, (uint64_t)ret | + ((uint64_t)reset_status << 32)); + } + + case PM_INIT_FINALIZE: + SMC_RET1(handle, (uint64_t)PM_RET_SUCCESS); + + case PM_GET_CALLBACK_DATA: + { + uint32_t result[4] = {0}; + + pm_get_callbackdata(result, sizeof(result)); + SMC_RET2(handle, + (uint64_t)result[0] | ((uint64_t)result[1] << 32), + (uint64_t)result[2] | ((uint64_t)result[3] << 32)); + } + + case PM_PINCTRL_REQUEST: + ret = pm_pinctrl_request(pm_arg[0]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_PINCTRL_RELEASE: + ret = pm_pinctrl_release(pm_arg[0]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_PINCTRL_GET_FUNCTION: + { + uint32_t value = 0; + + ret = pm_pinctrl_get_function(pm_arg[0], &value); + SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32); + } + + case PM_PINCTRL_SET_FUNCTION: + ret = pm_pinctrl_set_function(pm_arg[0], pm_arg[1]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_PINCTRL_CONFIG_PARAM_GET: + { + uint32_t value; + + ret = pm_pinctrl_get_pin_param(pm_arg[0], pm_arg[1], &value); + SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32); + } + + case PM_PINCTRL_CONFIG_PARAM_SET: + ret = pm_pinctrl_set_pin_param(pm_arg[0], pm_arg[1], pm_arg[2]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_IOCTL: + { + uint32_t value; + + ret = pm_api_ioctl(pm_arg[0], pm_arg[1], pm_arg[2], + pm_arg[3], &value); + SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32); + } + + case PM_QUERY_DATA: + { + uint32_t data[4] = { 0 }; + + ret = pm_query_data(pm_arg[0], pm_arg[1], pm_arg[2], + pm_arg[3], data); + SMC_RET2(handle, (uint64_t)ret | ((uint64_t)data[0] << 32), + (uint64_t)data[1] | ((uint64_t)data[2] << 32)); + } + + case PM_CLOCK_ENABLE: + ret = pm_clock_enable(pm_arg[0]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_CLOCK_DISABLE: + ret = pm_clock_disable(pm_arg[0]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_CLOCK_GETSTATE: + { + uint32_t value; + + ret = pm_clock_get_state(pm_arg[0], &value); + SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32); + } + + case PM_CLOCK_SETDIVIDER: + ret = pm_clock_set_divider(pm_arg[0], pm_arg[1]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_CLOCK_GETDIVIDER: + { + uint32_t value; + + ret = pm_clock_get_divider(pm_arg[0], &value); + SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32); + } + + case PM_CLOCK_SETPARENT: + ret = pm_clock_set_parent(pm_arg[0], pm_arg[1]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_CLOCK_GETPARENT: + { + uint32_t value; + + ret = pm_clock_get_parent(pm_arg[0], &value); + SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value) << 32); + } + + case PM_PLL_SET_PARAMETER: + ret = pm_pll_set_param(pm_arg[0], pm_arg[1], pm_arg[2]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_PLL_GET_PARAMETER: + { + uint32_t value; + + ret = pm_pll_get_param(pm_arg[0], pm_arg[1], &value); + SMC_RET1(handle, (uint64_t)ret | ((uint64_t)value << 32)); + } + + case PM_PLL_SET_MODE: + ret = pm_pll_set_mode(pm_arg[0], pm_arg[1]); + SMC_RET1(handle, (uint64_t)ret); + + case PM_PLL_GET_MODE: + { + uint32_t mode; + + ret = pm_pll_get_mode(pm_arg[0], &mode); + SMC_RET1(handle, (uint64_t)ret | ((uint64_t)mode << 32)); + } + + case PM_GET_TRUSTZONE_VERSION: + SMC_RET1(handle, (uint64_t)PM_RET_SUCCESS | + ((uint64_t)VERSAL_TZ_VERSION << 32)); + + case PM_GET_CHIPID: + { + uint32_t result[2]; + + ret = pm_get_chipid(result); + SMC_RET2(handle, (uint64_t)ret | ((uint64_t)result[0] << 32), + result[1]); + } + + case PM_FEATURE_CHECK: + { + uint32_t version; + + ret = pm_feature_check(pm_arg[0], &version); + SMC_RET1(handle, (uint64_t)ret | ((uint64_t)version << 32)); + } + + case PM_LOAD_PDI: + { + ret = pm_load_pdi(pm_arg[0], pm_arg[1], pm_arg[2]); + SMC_RET1(handle, (uint64_t)ret); + } + + case PM_GET_OP_CHARACTERISTIC: + { + uint32_t result; + + ret = pm_get_op_characteristic(pm_arg[0], pm_arg[1], &result); + SMC_RET1(handle, (uint64_t)ret | ((uint64_t)result << 32)); + } + + default: + WARN("Unimplemented PM Service Call: 0x%x\n", smc_fid); + SMC_RET1(handle, SMC_UNK); + } +} diff --git a/plat/xilinx/versal/pm_service/pm_svc_main.h b/plat/xilinx/versal/pm_service/pm_svc_main.h new file mode 100644 index 0000000000..71329ca93f --- /dev/null +++ b/plat/xilinx/versal/pm_service/pm_svc_main.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2019, Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef PM_SVC_MAIN_H +#define PM_SVC_MAIN_H + +#include <pm_common.h> + +int pm_setup(void); +uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, + uint64_t x4, void *cookie, void *handle, + uint64_t flags); + +#endif /* PM_SVC_MAIN_H */ diff --git a/plat/xilinx/versal/sip_svc_setup.c b/plat/xilinx/versal/sip_svc_setup.c index 8f2180b211..bc7d8b7af7 100644 --- a/plat/xilinx/versal/sip_svc_setup.c +++ b/plat/xilinx/versal/sip_svc_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2019, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -10,6 +10,9 @@ #include <common/runtime_svc.h> #include <tools_share/uuid.h> +#include "ipi_mailbox_svc.h" +#include "pm_svc_main.h" + /* SMC function IDs for SiP Service queries */ #define VERSAL_SIP_SVC_CALL_COUNT 0x8200ff00 #define VERSAL_SIP_SVC_UID 0x8200ff01 @@ -22,7 +25,9 @@ /* These macros are used to identify PM calls from the SMC function ID */ #define PM_FID_MASK 0xf000u #define PM_FID_VALUE 0u +#define IPI_FID_VALUE 0x1000u #define is_pm_fid(_fid) (((_fid) & PM_FID_MASK) == PM_FID_VALUE) +#define is_ipi_fid(_fid) (((_fid) & PM_FID_MASK) == IPI_FID_VALUE) /* SiP Service UUID */ DEFINE_SVC_UUID2(versal_sip_uuid, @@ -36,6 +41,9 @@ DEFINE_SVC_UUID2(versal_sip_uuid, */ static int32_t sip_svc_setup(void) { + /* PM implementation as SiP Service */ + pm_setup(); + return 0; } @@ -55,6 +63,18 @@ uintptr_t sip_svc_smc_handler(uint32_t smc_fid, u_register_t flags) { /* Let PM SMC handler deal with PM-related requests */ + if (is_pm_fid(smc_fid)) { + return pm_smc_handler(smc_fid, x1, x2, x3, x4, cookie, handle, + flags); + } + + /* Let IPI SMC handler deal with IPI-related requests */ + if (is_ipi_fid(smc_fid)) { + return ipi_smc_handler(smc_fid, x1, x2, x3, x4, cookie, handle, + flags); + } + + /* Let PM SMC handler deal with PM-related requests */ switch (smc_fid) { case VERSAL_SIP_SVC_CALL_COUNT: /* PM functions + default functions */ diff --git a/plat/xilinx/versal/versal_gicv3.c b/plat/xilinx/versal/versal_gicv3.c index dcf23b4253..08e7cf95ac 100644 --- a/plat/xilinx/versal/versal_gicv3.c +++ b/plat/xilinx/versal/versal_gicv3.c @@ -1,9 +1,10 @@ /* - * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2018-2019, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ +#include <plat_private.h> #include <platform_def.h> #include <common/interrupt_props.h> @@ -11,8 +12,6 @@ #include <lib/utils.h> #include <plat/common/platform.h> -#include "versal_private.h" - /****************************************************************************** * The following functions are defined as weak to allow a platform to override * the way the GICv3 driver is initialised and used. diff --git a/plat/xilinx/versal/versal_ipi.c b/plat/xilinx/versal/versal_ipi.c new file mode 100644 index 0000000000..27541ff4b5 --- /dev/null +++ b/plat/xilinx/versal/versal_ipi.c @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2019, Xilinx, Inc. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Versal IPI agent registers access management + */ + +#include <errno.h> +#include <ipi.h> +#include <plat_ipi.h> +#include <plat_private.h> +#include <string.h> +#include <common/debug.h> +#include <common/runtime_svc.h> +#include <lib/bakery_lock.h> +#include <lib/mmio.h> + +/* versal ipi configuration table */ +const static struct ipi_config versal_ipi_table[] = { + /* A72 IPI */ + [IPI_ID_APU] = { + .ipi_bit_mask = IPI0_TRIG_BIT, + .ipi_reg_base = IPI0_REG_BASE, + .secure_only = 0, + }, + + /* PMC IPI */ + [IPI_ID_PMC] = { + .ipi_bit_mask = PMC_IPI_TRIG_BIT, + .ipi_reg_base = IPI0_REG_BASE, + .secure_only = 0, + }, + + /* RPU0 IPI */ + [IPI_ID_RPU0] = { + .ipi_bit_mask = IPI1_TRIG_BIT, + .ipi_reg_base = IPI1_REG_BASE, + .secure_only = 0, + }, + + /* RPU1 IPI */ + [IPI_ID_RPU1] = { + .ipi_bit_mask = IPI2_TRIG_BIT, + .ipi_reg_base = IPI2_REG_BASE, + .secure_only = 0, + }, + + /* IPI3 IPI */ + [IPI_ID_3] = { + .ipi_bit_mask = IPI3_TRIG_BIT, + .ipi_reg_base = IPI3_REG_BASE, + .secure_only = 0, + }, + + /* IPI4 IPI */ + [IPI_ID_4] = { + .ipi_bit_mask = IPI4_TRIG_BIT, + .ipi_reg_base = IPI4_REG_BASE, + .secure_only = 0, + }, + + /* IPI5 IPI */ + [IPI_ID_5] = { + .ipi_bit_mask = IPI5_TRIG_BIT, + .ipi_reg_base = IPI5_REG_BASE, + .secure_only = 0, + }, +}; + +/* versal_ipi_config_table_init() - Initialize versal IPI configuration data + * + * @ipi_config_table - IPI configuration table + * @ipi_total - Total number of IPI available + * + */ +void versal_ipi_config_table_init(void) +{ + ipi_config_table_init(versal_ipi_table, ARRAY_SIZE(versal_ipi_table)); +} diff --git a/plat/xilinx/zynqmp/bl31_zynqmp_setup.c b/plat/xilinx/zynqmp/bl31_zynqmp_setup.c index 285a4eb8bd..6e0e811d48 100644 --- a/plat/xilinx/zynqmp/bl31_zynqmp_setup.c +++ b/plat/xilinx/zynqmp/bl31_zynqmp_setup.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -13,8 +13,11 @@ #include <drivers/console.h> #include <plat/arm/common/plat_arm.h> #include <plat/common/platform.h> +#include <lib/mmio.h> +#include <plat_startup.h> #include <plat_private.h> +#include <zynqmp_def.h> static entry_point_info_t bl32_image_ep_info; static entry_point_info_t bl33_image_ep_info; @@ -57,6 +60,7 @@ static inline void bl31_set_default_config(void) void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, u_register_t arg2, u_register_t arg3) { + uint64_t atf_handoff_addr; /* Register the console to provide early debug support */ static console_cdns_t bl31_boot_console; (void)console_cdns_register(ZYNQMP_UART_BASE, @@ -86,12 +90,15 @@ void bl31_early_platform_setup2(u_register_t arg0, u_register_t arg1, SET_PARAM_HEAD(&bl33_image_ep_info, PARAM_EP, VERSION_1, 0); SET_SECURITY_STATE(bl33_image_ep_info.h.attr, NON_SECURE); + atf_handoff_addr = mmio_read_32(PMU_GLOBAL_GEN_STORAGE6); + if (zynqmp_get_bootmode() == ZYNQMP_BOOTMODE_JTAG) { bl31_set_default_config(); } else { /* use parameters from FSBL */ enum fsbl_handoff ret = fsbl_atf_handover(&bl32_image_ep_info, - &bl33_image_ep_info); + &bl33_image_ep_info, + atf_handoff_addr); if (ret == FSBL_HANDOFF_NO_STRUCT) bl31_set_default_config(); else if (ret != FSBL_HANDOFF_SUCCESS) diff --git a/plat/xilinx/zynqmp/include/plat_pm_common.h b/plat/xilinx/zynqmp/include/plat_pm_common.h index 1b371cc367..56a747a4f6 100644 --- a/plat/xilinx/zynqmp/include/plat_pm_common.h +++ b/plat/xilinx/zynqmp/include/plat_pm_common.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -16,7 +16,16 @@ #include <common/debug.h> #include "pm_defs.h" -#define PAYLOAD_ARG_CNT 6U +#if ZYNQMP_IPI_CRC_CHECK +#define PAYLOAD_ARG_CNT 8U +#define IPI_W0_TO_W6_SIZE 28U +#define PAYLOAD_CRC_POS 7U +#define CRC_INIT_VALUE 0x4F4EU +#define CRC_ORDER 16U +#define CRC_POLYNOM 0x8005U +#else +#define PAYLOAD_ARG_CNT 6U +#endif #define PAYLOAD_ARG_SIZE 4U /* size in bytes */ #define ZYNQMP_TZ_VERSION_MAJOR 1 diff --git a/plat/xilinx/zynqmp/include/plat_private.h b/plat/xilinx/zynqmp/include/plat_private.h index 8bdf429677..288cc53011 100644 --- a/plat/xilinx/zynqmp/include/plat_private.h +++ b/plat/xilinx/zynqmp/include/plat_private.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -21,13 +21,6 @@ unsigned int zynqmp_calc_core_pos(u_register_t mpidr); unsigned int zynqmp_get_uart_clk(void); unsigned int zynqmp_get_bootmode(void); -/* For FSBL handover */ -enum fsbl_handoff { - FSBL_HANDOFF_SUCCESS = 0, - FSBL_HANDOFF_NO_STRUCT, - FSBL_HANDOFF_INVAL_STRUCT, - FSBL_HANDOFF_TOO_MANY_PARTS, -}; #if ZYNQMP_WDT_RESTART /* @@ -37,7 +30,4 @@ enum fsbl_handoff { int request_intr_type_el3(uint32_t, interrupt_type_handler_t); #endif -enum fsbl_handoff fsbl_atf_handover(entry_point_info_t *bl32_image_ep_info, - entry_point_info_t *bl33_image_ep_info); - #endif /* PLAT_PRIVATE_H */ diff --git a/plat/xilinx/zynqmp/plat_psci.c b/plat/xilinx/zynqmp/plat_psci.c index a32e08988a..f579f795f2 100644 --- a/plat/xilinx/zynqmp/plat_psci.c +++ b/plat/xilinx/zynqmp/plat_psci.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2019, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -100,9 +100,8 @@ static void zynqmp_pwr_domain_on_finish(const psci_power_state_t *target_state) for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", __func__, i, target_state->pwr_domain_state[i]); - + plat_arm_gic_pcpu_init(); gicv2_cpuif_enable(); - gicv2_pcpu_distif_init(); } static void zynqmp_pwr_domain_suspend_finish(const psci_power_state_t *target_state) diff --git a/plat/xilinx/zynqmp/platform.mk b/plat/xilinx/zynqmp/platform.mk index c34a51674a..44f20f69f6 100644 --- a/plat/xilinx/zynqmp/platform.mk +++ b/plat/xilinx/zynqmp/platform.mk @@ -1,5 +1,5 @@ # -# Copyright (c) 2013-2019, ARM Limited and Contributors. All rights reserved. +# Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved. # # SPDX-License-Identifier: BSD-3-Clause @@ -9,7 +9,10 @@ PSCI_EXTENDED_STATE_ID := 1 A53_DISABLE_NON_TEMPORAL_HINT := 0 SEPARATE_CODE_AND_RODATA := 1 ZYNQMP_WDT_RESTART := 0 +ZYNQMP_IPI_CRC_CHECK := 0 override RESET_TO_BL31 := 1 +override GICV2_G0_FOR_EL3 := 1 +override WARMBOOT_ENABLE_DCACHE_EARLY := 1 # Do not enable SVE ENABLE_SVE_FOR_NS := 0 @@ -45,11 +48,16 @@ ifdef ZYNQMP_WDT_RESTART $(eval $(call add_define,ZYNQMP_WDT_RESTART)) endif -PLAT_INCLUDES := -Iinclude/plat/arm/common/aarch64/ \ +ifdef ZYNQMP_IPI_CRC_CHECK + $(eval $(call add_define,ZYNQMP_IPI_CRC_CHECK)) +endif + +PLAT_INCLUDES := -Iinclude/plat/arm/common/ \ + -Iinclude/plat/arm/common/aarch64/ \ -Iplat/xilinx/common/include/ \ + -Iplat/xilinx/common/ipi_mailbox_service/ \ -Iplat/xilinx/zynqmp/include/ \ -Iplat/xilinx/zynqmp/pm_service/ \ - -Iplat/xilinx/zynqmp/ipi_mailbox_service/ PLAT_BL_COMMON_SOURCES := lib/xlat_tables/xlat_tables_common.c \ lib/xlat_tables/aarch64/xlat_tables.c \ @@ -72,11 +80,12 @@ BL31_SOURCES += drivers/arm/cci/cci.c \ lib/cpus/aarch64/aem_generic.S \ lib/cpus/aarch64/cortex_a53.S \ plat/common/plat_psci_common.c \ + plat/xilinx/common/ipi_mailbox_service/ipi_mailbox_svc.c \ plat/xilinx/common/pm_service/pm_ipi.c \ + plat/xilinx/common/plat_startup.c \ plat/xilinx/zynqmp/bl31_zynqmp_setup.c \ plat/xilinx/zynqmp/plat_psci.c \ plat/xilinx/zynqmp/plat_zynqmp.c \ - plat/xilinx/zynqmp/plat_startup.c \ plat/xilinx/zynqmp/plat_topology.c \ plat/xilinx/zynqmp/sip_svc_setup.c \ plat/xilinx/zynqmp/pm_service/pm_svc_main.c \ @@ -84,5 +93,8 @@ BL31_SOURCES += drivers/arm/cci/cci.c \ plat/xilinx/zynqmp/pm_service/pm_api_pinctrl.c \ plat/xilinx/zynqmp/pm_service/pm_api_ioctl.c \ plat/xilinx/zynqmp/pm_service/pm_api_clock.c \ - plat/xilinx/zynqmp/pm_service/pm_client.c \ - plat/xilinx/zynqmp/ipi_mailbox_service/ipi_mailbox_svc.c + plat/xilinx/zynqmp/pm_service/pm_client.c + +ifneq (${RESET_TO_BL31},1) + $(error "Using BL31 as the reset vector is only one option supported on ZynqMP. Please set RESET_TO_BL31 to 1.") +endif diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c index e3050724d2..852f92763c 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.c @@ -30,6 +30,10 @@ #define CLK_TYPE_SHIFT U(2) #define CLK_CLKFLAGS_SHIFT U(8) #define CLK_TYPEFLAGS_SHIFT U(24) +#define CLK_TYPEFLAGS2_SHIFT U(4) +#define CLK_TYPEFLAGS_BITS_MASK U(0xFF) +#define CLK_TYPEFLAGS2_BITS_MASK U(0x0F00) +#define CLK_TYPEFLAGS_BITS U(8) #define CLK_EXTERNAL_PARENT (PARENT_CLK_EXTERNAL << CLK_PARENTS_ID_LEN) @@ -364,9 +368,8 @@ static struct pm_clock_node dp_audio_video_ref_nodes[] = { .offset = PERIPH_MUX_SHIFT, .width = PERIPH_MUX_WIDTH, .clkflags = CLK_SET_RATE_NO_REPARENT | - CLK_SET_RATE_PARENT | - CLK_FRAC | CLK_IS_BASIC, - .typeflags = NA_TYPE_FLAGS, + CLK_SET_RATE_PARENT | CLK_IS_BASIC, + .typeflags = CLK_FRAC, .mult = NA_MULT, .div = NA_DIV, }, @@ -375,8 +378,9 @@ static struct pm_clock_node dp_audio_video_ref_nodes[] = { .offset = PERIPH_DIV1_SHIFT, .width = PERIPH_DIV1_WIDTH, .clkflags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT | - CLK_FRAC | CLK_IS_BASIC, - .typeflags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + CLK_IS_BASIC, + .typeflags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO | + CLK_FRAC, .mult = NA_MULT, .div = NA_DIV, }, @@ -385,8 +389,9 @@ static struct pm_clock_node dp_audio_video_ref_nodes[] = { .offset = PERIPH_DIV2_SHIFT, .width = PERIPH_DIV2_WIDTH, .clkflags = CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT | - CLK_FRAC | CLK_IS_BASIC, - .typeflags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, + CLK_IS_BASIC, + .typeflags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO | + CLK_FRAC, .mult = NA_MULT, .div = NA_DIV, }, @@ -577,7 +582,8 @@ static struct pm_clock_node gem_ref_ungated_nodes[] = { .type = TYPE_DIV2, .offset = 16, .width = 6, - .clkflags = CLK_SET_RATE_NO_REPARENT | CLK_IS_BASIC, + .clkflags = CLK_SET_RATE_NO_REPARENT | CLK_IS_BASIC | + CLK_SET_RATE_PARENT, .typeflags = CLK_DIVIDER_ONE_BASED | CLK_DIVIDER_ALLOW_ZERO, .mult = NA_MULT, .div = NA_DIV, @@ -2352,7 +2358,6 @@ static uint32_t pm_clk_invalid_list[] = {CLK_USB0, CLK_USB1, CLK_CSU_SPB, CLK_DBG_TSTMP, CLK_DDR_REF, CLK_TOPSW_MAIN, - CLK_TOPSW_LSBUS, CLK_GTGREF0_REF, CLK_LPD_SWITCH, CLK_CPU_R5, @@ -2462,6 +2467,7 @@ enum pm_ret_status pm_api_clock_get_topology(unsigned int clock_id, struct pm_clock_node *clock_nodes; uint8_t num_nodes; unsigned int i; + uint16_t typeflags; if (!pm_clock_valid(clock_id)) return PM_RET_ERROR_ARGS; @@ -2481,11 +2487,14 @@ enum pm_ret_status pm_api_clock_get_topology(unsigned int clock_id, for (i = 0; i < 3U; i++) { if ((index + i) == num_nodes) break; - topology[i] = clock_nodes[index + i].type; + topology[i] = clock_nodes[index + i].type; topology[i] |= clock_nodes[index + i].clkflags << CLK_CLKFLAGS_SHIFT; - topology[i] |= clock_nodes[index + i].typeflags << + typeflags = clock_nodes[index + i].typeflags; + topology[i] |= (typeflags & CLK_TYPEFLAGS_BITS_MASK) << CLK_TYPEFLAGS_SHIFT; + topology[i] |= (typeflags & CLK_TYPEFLAGS2_BITS_MASK) >> + (CLK_TYPEFLAGS_BITS - CLK_TYPEFLAGS2_SHIFT); } return PM_RET_SUCCESS; @@ -2612,6 +2621,42 @@ enum pm_ret_status pm_api_clock_get_attributes(unsigned int clock_id, } /** + * pm_api_clock_get_max_divisor - PM call to get max divisor + * @clock_id Clock ID + * @div_type Divisor Type (TYPE_DIV1 or TYPE_DIV2) + * @max_div Maximum supported divisor + * + * This function is used by master to get maximum supported value. + * + * Return: Returns status, either success or error+reason. + */ +enum pm_ret_status pm_api_clock_get_max_divisor(enum clock_id clock_id, + uint8_t div_type, + uint32_t *max_div) +{ + uint32_t i; + struct pm_clock_node *nodes; + + if (clock_id >= CLK_MAX_OUTPUT_CLK) + return PM_RET_ERROR_ARGS; + + nodes = *clocks[clock_id].nodes; + for (i = 0; i < clocks[clock_id].num_nodes; i++) { + if (nodes[i].type == div_type) { + if (CLK_DIVIDER_POWER_OF_TWO & + nodes[i].typeflags) { + *max_div = (1 << (BIT(nodes[i].width) - 1)); + } else { + *max_div = BIT(nodes[i].width) - 1; + } + return PM_RET_SUCCESS; + } + } + + return PM_RET_ERROR_ARGS; +} + +/** * struct pm_pll - PLL related data required to map IOCTL-based PLL control * implemented by linux to system-level EEMI APIs * @nid: PLL node ID diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h index 5bed5a6176..301ed24b65 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_clock.h +++ b/plat/xilinx/zynqmp/pm_service/pm_api_clock.h @@ -43,7 +43,6 @@ #define CLK_IS_CRITICAL BIT(11) /* do not gate, ever */ /* parents need enable during gate/ungate, set rate and re-parent */ #define CLK_OPS_PARENT_ENABLE BIT(12) -#define CLK_FRAC BIT(13) #define CLK_DIVIDER_ONE_BASED BIT(0) #define CLK_DIVIDER_POWER_OF_TWO BIT(1) @@ -52,6 +51,7 @@ #define CLK_DIVIDER_ROUND_CLOSEST BIT(4) #define CLK_DIVIDER_READ_ONLY BIT(5) #define CLK_DIVIDER_MAX_AT_ZERO BIT(6) +#define CLK_FRAC BIT(8) #define END_OF_CLK "END_OF_CLK" @@ -307,6 +307,9 @@ enum pm_ret_status pm_api_clock_get_parents(unsigned int clock_id, uint32_t *parents); enum pm_ret_status pm_api_clock_get_attributes(unsigned int clock_id, uint32_t *attr); +enum pm_ret_status pm_api_clock_get_max_divisor(enum clock_id clock_id, + uint8_t div_type, + uint32_t *max_div); enum pm_ret_status pm_clock_get_pll_node_id(enum clock_id clock_id, enum pm_node_id *node_id); diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c index e0b9816e4f..b1720d9f6c 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.c +++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -630,6 +630,22 @@ enum pm_ret_status pm_aes_engine(uint32_t address_high, } /** + * pm_get_callbackdata() - Read from IPI response buffer + * @data - array of PAYLOAD_ARG_CNT elements + * + * Read value from ipi buffer response buffer. + */ +void pm_get_callbackdata(uint32_t *data, size_t count) +{ + /* Return if interrupt is not from PMU */ + if (!pm_ipi_irq_status(primary_proc)) + return; + + pm_ipi_buff_read_callb(data, count); + pm_ipi_irq_clear(primary_proc); +} + +/** * pm_pinctrl_request() - Request Pin from firmware * @pin Pin number to request * @@ -741,6 +757,23 @@ enum pm_ret_status pm_ioctl(enum pm_node_id nid, } /** + * pm_clock_get_max_divisor - PM call to get max divisor + * @clock_id Clock ID + * @div_type Divisor ID (TYPE_DIV1 or TYPE_DIV2) + * @max_div Maximum supported divisor + * + * This function is used by master to get maximum supported value. + * + * Return: Returns status, either success or error+reason. + */ +static enum pm_ret_status pm_clock_get_max_divisor(unsigned int clock_id, + uint8_t div_type, + uint32_t *max_div) +{ + return pm_api_clock_get_max_divisor(clock_id, div_type, max_div); +} + +/** * pm_clock_get_num_clocks - PM call to request number of clocks * @nclockss: Number of clocks * @@ -1322,6 +1355,11 @@ enum pm_ret_status pm_query_data(enum pm_query_id qid, ret = pm_clock_get_num_clocks(&data[1]); data[0] = (unsigned int)ret; break; + + case PM_QID_CLOCK_GET_MAX_DIVISOR: + ret = pm_clock_get_max_divisor(arg1, arg2, &data[1]); + data[0] = (unsigned int)ret; + break; default: ret = PM_RET_ERROR_ARGS; WARN("Unimplemented query service call: 0x%x\n", qid); diff --git a/plat/xilinx/zynqmp/pm_service/pm_api_sys.h b/plat/xilinx/zynqmp/pm_service/pm_api_sys.h index 282ca3dc65..ff66d3f024 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_api_sys.h +++ b/plat/xilinx/zynqmp/pm_service/pm_api_sys.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -25,6 +25,7 @@ enum pm_query_id { PM_QID_PINCTRL_GET_FUNCTION_GROUPS, PM_QID_PINCTRL_GET_PIN_GROUPS, PM_QID_CLOCK_GET_NUM_CLOCKS, + PM_QID_CLOCK_GET_MAX_DIVISOR, }; /********************************************************** @@ -116,6 +117,7 @@ enum pm_ret_status pm_secure_rsaaes(uint32_t address_high, uint32_t size, uint32_t flags); unsigned int pm_get_shutdown_scope(void); +void pm_get_callbackdata(uint32_t *data, size_t count); enum pm_ret_status pm_pinctrl_request(unsigned int pin); enum pm_ret_status pm_pinctrl_release(unsigned int pin); enum pm_ret_status pm_pinctrl_get_function(unsigned int pin, diff --git a/plat/xilinx/zynqmp/pm_service/pm_svc_main.c b/plat/xilinx/zynqmp/pm_service/pm_svc_main.c index faa2827450..98dbe7d6e8 100644 --- a/plat/xilinx/zynqmp/pm_service/pm_svc_main.c +++ b/plat/xilinx/zynqmp/pm_service/pm_svc_main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2018, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -25,6 +25,7 @@ #include "pm_client.h" #include "pm_ipi.h" +#define PM_GET_CALLBACK_DATA 0xa01 #define PM_SET_SUSPEND_MODE 0xa02 #define PM_GET_TRUSTZONE_VERSION 0xa03 @@ -76,8 +77,12 @@ static void trigger_wdt_restart(void) INFO("Active Cores: %d\n", active_cores); - /* trigger SGI to active cores */ - gicv2_raise_sgi(ARM_IRQ_SEC_SGI_7, target_cpu_list); + for (i = PLATFORM_CORE_COUNT - 1; i >= 0; i--) { + if (target_cpu_list & (1 << i)) { + /* trigger SGI to active cores */ + plat_ic_raise_el3_sgi(ARM_IRQ_SEC_SGI_7, i); + } + } } /** @@ -105,6 +110,8 @@ static uint64_t ttc_fiq_handler(uint32_t id, uint32_t flags, void *handle, { INFO("BL31: Got TTC FIQ\n"); + plat_ic_end_of_interrupt(id); + /* Clear TTC interrupt by reading interrupt register */ mmio_read_32(TTC3_INTR_REGISTER_1); @@ -412,6 +419,16 @@ uint64_t pm_smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, pm_arg[3]); SMC_RET1(handle, (uint64_t)ret); + case PM_GET_CALLBACK_DATA: + { + uint32_t result[4] = {0}; + + pm_get_callbackdata(result, (sizeof(result)/sizeof(uint32_t))); + SMC_RET2(handle, + (uint64_t)result[0] | ((uint64_t)result[1] << 32), + (uint64_t)result[2] | ((uint64_t)result[3] << 32)); + } + case PM_PINCTRL_REQUEST: ret = pm_pinctrl_request(pm_arg[0]); SMC_RET1(handle, (uint64_t)ret); diff --git a/tools/cert_create/src/cert.c b/tools/cert_create/src/cert.c index c68a265b43..153f5557df 100644 --- a/tools/cert_create/src/cert.c +++ b/tools/cert_create/src/cert.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -15,12 +15,6 @@ #include <openssl/sha.h> #include <openssl/x509v3.h> -#if USE_TBBR_DEFS -#include <tbbr_oid.h> -#else -#include <platform_oid.h> -#endif - #include "cert.h" #include "cmd_opt.h" #include "debug.h" diff --git a/tools/cert_create/src/key.c b/tools/cert_create/src/key.c index 0f80cce9b7..fcc9d53162 100644 --- a/tools/cert_create/src/key.c +++ b/tools/cert_create/src/key.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -13,12 +13,6 @@ #include <openssl/evp.h> #include <openssl/pem.h> -#if USE_TBBR_DEFS -#include <tbbr_oid.h> -#else -#include <platform_oid.h> -#endif - #include "cert.h" #include "cmd_opt.h" #include "debug.h" diff --git a/tools/cert_create/src/main.c b/tools/cert_create/src/main.c index 0cbd2196bc..863db7b6e8 100644 --- a/tools/cert_create/src/main.c +++ b/tools/cert_create/src/main.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2019, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause */ @@ -19,21 +19,12 @@ #include <openssl/sha.h> #include <openssl/x509v3.h> -#if USE_TBBR_DEFS -#include <tbbr_oid.h> -#else -#include <platform_oid.h> -#endif - #include "cert.h" #include "cmd_opt.h" #include "debug.h" #include "ext.h" #include "key.h" #include "sha.h" -#include "tbbr/tbb_cert.h" -#include "tbbr/tbb_ext.h" -#include "tbbr/tbb_key.h" /* * Helper macros to simplify the code. This macro assigns the return value of |