diff options
114 files changed, 11390 insertions, 732 deletions
@@ -45,6 +45,8 @@ include ${MAKE_HELPERS_DIRECTORY}build_env.mk # Default values for build configurations ################################################################################ +# The Target build architecture. Supported values are: aarch64, aarch32. +ARCH := aarch64 # Build verbosity V := 0 # Debug build @@ -54,6 +56,8 @@ DEFAULT_PLAT := fvp PLAT := ${DEFAULT_PLAT} # SPD choice SPD := none +# The AArch32 Secure Payload to be built as BL32 image +AARCH32_SP := none # Base commit to perform code check on BASE_COMMIT := origin/master # NS timer register save and restore @@ -198,14 +202,20 @@ OD := ${CROSS_COMPILE}objdump NM := ${CROSS_COMPILE}nm PP := ${CROSS_COMPILE}gcc -E +ASFLAGS_aarch64 = -mgeneral-regs-only +TF_CFLAGS_aarch64 = -mgeneral-regs-only -mstrict-align + +ASFLAGS_aarch32 = -march=armv8-a +TF_CFLAGS_aarch32 = -march=armv8-a + ASFLAGS += -nostdinc -ffreestanding -Wa,--fatal-warnings \ -Werror -Wmissing-include-dirs \ - -mgeneral-regs-only -D__ASSEMBLY__ \ + -D__ASSEMBLY__ $(ASFLAGS_$(ARCH)) \ ${DEFINES} ${INCLUDES} TF_CFLAGS += -nostdinc -ffreestanding -Wall \ -Werror -Wmissing-include-dirs \ - -mgeneral-regs-only -mstrict-align \ -std=c99 -c -Os \ + $(TF_CFLAGS_$(ARCH)) \ ${DEFINES} ${INCLUDES} TF_CFLAGS += -ffunction-sections -fdata-sections @@ -220,26 +230,27 @@ include lib/stdlib/stdlib.mk BL_COMMON_SOURCES += common/bl_common.c \ common/tf_printf.c \ - common/aarch64/debug.S \ - lib/aarch64/cache_helpers.S \ - lib/aarch64/misc_helpers.S \ - plat/common/aarch64/platform_helpers.S \ + common/${ARCH}/debug.S \ + lib/${ARCH}/cache_helpers.S \ + lib/${ARCH}/misc_helpers.S \ + plat/common/${ARCH}/platform_helpers.S \ ${STDLIB_SRCS} INCLUDES += -Iinclude/bl1 \ -Iinclude/bl31 \ -Iinclude/common \ - -Iinclude/common/aarch64 \ + -Iinclude/common/${ARCH} \ -Iinclude/drivers \ -Iinclude/drivers/arm \ -Iinclude/drivers/auth \ -Iinclude/drivers/io \ -Iinclude/drivers/ti/uart \ -Iinclude/lib \ - -Iinclude/lib/aarch64 \ - -Iinclude/lib/cpus/aarch64 \ + -Iinclude/lib/${ARCH} \ + -Iinclude/lib/cpus/${ARCH} \ -Iinclude/lib/el3_runtime \ - -Iinclude/lib/el3_runtime/aarch64 \ + -Iinclude/lib/el3_runtime/${ARCH} \ + -Iinclude/lib/pmf \ -Iinclude/lib/psci \ -Iinclude/plat/common \ -Iinclude/services \ @@ -267,6 +278,9 @@ INCLUDE_TBBR_MK := 1 ################################################################################ ifneq (${SPD},none) +ifeq (${ARCH},aarch32) + $(error "Error: SPD is incompatible with AArch32.") +endif ifdef EL3_PAYLOAD_BASE $(warning "SPD and EL3_PAYLOAD_BASE are incompatible build options.") $(warning "The SPD and its BL32 companion will be present but ignored.") @@ -299,6 +313,8 @@ endif include ${PLAT_MAKEFILE_FULL} +# Platform compatibility is not supported in AArch32 +ifneq (${ARCH},aarch32) # If the platform has not defined ENABLE_PLAT_COMPAT, then enable it by default ifndef ENABLE_PLAT_COMPAT ENABLE_PLAT_COMPAT := 1 @@ -308,6 +324,7 @@ endif ifneq (${ENABLE_PLAT_COMPAT}, 0) include plat/compat/plat_compat.mk endif +endif # Include the CPU specific operations makefile, which provides default # values for all CPU errata workarounds and CPU specific optimisations. @@ -468,11 +485,18 @@ else $(eval $(call add_define,PRELOADED_BL33_BASE)) endif endif +# Define the AARCH32/AARCH64 flag based on the ARCH flag +ifeq (${ARCH},aarch32) + $(eval $(call add_define,AARCH32)) +else + $(eval $(call add_define,AARCH64)) +endif ################################################################################ # Include BL specific makefiles ################################################################################ - +# BL31 is not needed and BL1, BL2 & BL2U are not currently supported in AArch32 +ifneq (${ARCH},aarch32) ifdef BL1_SOURCES NEED_BL1 := yes include bl1/bl1.mk @@ -496,7 +520,27 @@ NEED_BL31 := yes include bl31/bl31.mk endif endif +endif + +ifeq (${ARCH},aarch32) +NEED_BL32 := yes +################################################################################ +# Build `AARCH32_SP` as BL32 image for AArch32 +################################################################################ +ifneq (${AARCH32_SP},none) +# We expect to locate an sp.mk under the specified AARCH32_SP directory +AARCH32_SP_MAKE := $(wildcard bl32/${AARCH32_SP}/${AARCH32_SP}.mk) + +ifeq (${AARCH32_SP_MAKE},) + $(error Error: No bl32/${AARCH32_SP}/${AARCH32_SP}.mk located) +endif + +$(info Including ${AARCH32_SP_MAKE}) +include ${AARCH32_SP_MAKE} +endif + +endif ################################################################################ # Build targets @@ -665,7 +709,8 @@ help: @echo " bl2 Build the BL2 binary" @echo " bl2u Build the BL2U binary" @echo " bl31 Build the BL31 binary" - @echo " bl32 Build the BL32 binary" + @echo " bl32 Build the BL32 binary. If ARCH=aarch32, then " + @echo " this builds secure payload specified by AARCH32_SP" @echo " certificates Build the certificates (requires 'GENERATE_COT=1')" @echo " fip Build the Firmware Image Package (FIP)" @echo " fwu_fip Build the FWU Firmware Image Package (FIP)" diff --git a/bl1/bl1_main.c b/bl1/bl1_main.c index 3cca1769..cb1bc186 100644 --- a/bl1/bl1_main.c +++ b/bl1/bl1_main.c @@ -38,6 +38,7 @@ #include <platform.h> #include <platform_def.h> #include <smcc_helpers.h> +#include <utils.h> #include "bl1_private.h" #include <uuid.h> @@ -32,6 +32,6 @@ BL2_SOURCES += bl2/bl2_main.c \ bl2/aarch64/bl2_entrypoint.S \ bl2/aarch64/bl2_arch_setup.c \ common/aarch64/early_exceptions.S \ - lib/locks/exclusive/spinlock.S + lib/locks/exclusive/aarch64/spinlock.S BL2_LINKERFILE := bl2/bl2.ld.S diff --git a/bl32/sp_min/aarch32/entrypoint.S b/bl32/sp_min/aarch32/entrypoint.S new file mode 100644 index 00000000..33d35b9b --- /dev/null +++ b/bl32/sp_min/aarch32/entrypoint.S @@ -0,0 +1,331 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <arch.h> +#include <asm_macros.S> +#include <bl_common.h> +#include <context.h> +#include <runtime_svc.h> +#include <smcc_helpers.h> +#include <smcc_macros.S> +#include <xlat_tables.h> + + .globl sp_min_vector_table + .globl sp_min_entrypoint + .globl sp_min_warm_entrypoint + +func sp_min_vector_table + b sp_min_entrypoint + b plat_panic_handler /* Undef */ + b handle_smc /* Syscall */ + b plat_panic_handler /* Prefetch abort */ + b plat_panic_handler /* Data abort */ + b plat_panic_handler /* Reserved */ + b plat_panic_handler /* IRQ */ + b plat_panic_handler /* FIQ */ +endfunc sp_min_vector_table + +func handle_smc + smcc_save_gp_mode_regs + + /* r0 points to smc_context */ + mov r2, r0 /* handle */ + ldcopr r0, SCR + + /* Save SCR in stack */ + push {r0} + and r3, r0, #SCR_NS_BIT /* flags */ + + /* Switch to Secure Mode*/ + bic r0, #SCR_NS_BIT + stcopr r0, SCR + isb + ldr r0, [r2, #SMC_CTX_GPREG_R0] /* smc_fid */ + /* Check whether an SMC64 is issued */ + tst r0, #(FUNCID_CC_MASK << FUNCID_CC_SHIFT) + beq 1f /* SMC32 is detected */ + mov r0, #SMC_UNK + str r0, [r2, #SMC_CTX_GPREG_R0] + mov r0, r2 + b 2f /* Skip handling the SMC */ +1: + mov r1, #0 /* cookie */ + bl handle_runtime_svc +2: + /* r0 points to smc context */ + + /* Restore SCR from stack */ + pop {r1} + stcopr r1, SCR + isb + + b sp_min_exit +endfunc handle_smc + +/* + * The Cold boot/Reset entrypoint for SP_MIN + */ +func sp_min_entrypoint + + /* + * The caches and TLBs are disabled at reset. If any implementation + * allows the caches/TLB to be hit while they are disabled, ensure + * that they are invalidated here + */ + + /* Make sure we are in Secure Mode*/ + ldcopr r0, SCR + bic r0, #SCR_NS_BIT + stcopr r0, SCR + isb + + /* Switch to monitor mode */ + cps #MODE32_mon + isb + + /* + * Set sane values for NS SCTLR as well. + * Switch to non secure mode for this. + */ + ldr r0, =(SCTLR_RES1) + ldcopr r1, SCR + orr r2, r1, #SCR_NS_BIT + stcopr r2, SCR + isb + + ldcopr r2, SCTLR + orr r0, r0, r2 + stcopr r0, SCTLR + isb + + stcopr r1, SCR + isb + + /* + * Set the CPU endianness before doing anything that might involve + * memory reads or writes. + */ + ldcopr r0, SCTLR + bic r0, r0, #SCTLR_EE_BIT + stcopr r0, SCTLR + isb + + /* Run the CPU Specific Reset handler */ + bl reset_handler + + /* + * Enable the instruction cache and data access + * alignment checks + */ + ldcopr r0, SCTLR + ldr r1, =(SCTLR_RES1 | SCTLR_A_BIT | SCTLR_I_BIT) + orr r0, r0, r1 + stcopr r0, SCTLR + isb + + /* Set the vector tables */ + ldr r0, =sp_min_vector_table + stcopr r0, VBAR + stcopr r0, MVBAR + isb + + /* + * Enable the SIF bit to disable instruction fetches + * from Non-secure memory. + */ + ldcopr r0, SCR + orr r0, r0, #SCR_SIF_BIT + stcopr r0, SCR + + /* + * Enable the SError interrupt now that the exception vectors have been + * setup. + */ + cpsie a + isb + + /* Enable access to Advanced SIMD registers */ + ldcopr r0, NSACR + bic r0, r0, #NSASEDIS_BIT + orr r0, r0, #(NASCR_CP10_BIT | NASCR_CP11_BIT) + stcopr r0, NSACR + isb + + /* + * Enable access to Advanced SIMD, Floating point and to the Trace + * functionality as well. + */ + ldcopr r0, CPACR + bic r0, r0, #ASEDIS_BIT + bic r0, r0, #TRCDIS_BIT + orr r0, r0, #CPACR_ENABLE_FP_ACCESS + stcopr r0, CPACR + isb + + vmrs r0, FPEXC + orr r0, r0, #FPEXC_EN_BIT + vmsr FPEXC, r0 + + /* Detect whether Warm or Cold boot */ + bl plat_get_my_entrypoint + cmp r0, #0 + /* If warm boot detected, jump to warm boot entry */ + bxne r0 + + /* Setup C runtime stack */ + bl plat_set_my_stack + + /* Perform platform specific memory initialization */ + bl platform_mem_init + + /* Initialize the C Runtime Environment */ + + /* + * Invalidate the RW memory used by SP_MIN image. This includes + * the data and NOBITS sections. This is done to safeguard against + * possible corruption of this memory by dirty cache lines in a system + * cache as a result of use by an earlier boot loader stage. + */ + ldr r0, =__RW_START__ + ldr r1, =__RW_END__ + sub r1, r1, r0 + bl inv_dcache_range + + ldr r0, =__BSS_START__ + ldr r1, =__BSS_SIZE__ + bl zeromem + +#if USE_COHERENT_MEM + ldr r0, =__COHERENT_RAM_START__ + ldr r1, =__COHERENT_RAM_UNALIGNED_SIZE__ + bl zeromem +#endif + + /* Perform platform specific early arch. setup */ + bl sp_min_early_platform_setup + bl sp_min_plat_arch_setup + + /* Jump to the main function */ + bl sp_min_main + + /* ------------------------------------------------------------- + * Clean the .data & .bss sections to main memory. This ensures + * that any global data which was initialised by the primary CPU + * is visible to secondary CPUs before they enable their data + * caches and participate in coherency. + * ------------------------------------------------------------- + */ + ldr r0, =__DATA_START__ + ldr r1, =__DATA_END__ + sub r1, r1, r0 + bl clean_dcache_range + + ldr r0, =__BSS_START__ + ldr r1, =__BSS_END__ + sub r1, r1, r0 + bl clean_dcache_range + + /* Program the registers in cpu_context and exit monitor mode */ + mov r0, #NON_SECURE + bl cm_get_context + + /* Restore the SCR */ + ldr r2, [r0, #CTX_REGS_OFFSET + CTX_SCR] + stcopr r2, SCR + isb + + /* Restore the SCTLR */ + ldr r2, [r0, #CTX_REGS_OFFSET + CTX_NS_SCTLR] + stcopr r2, SCTLR + + bl smc_get_next_ctx + /* The other cpu_context registers have been copied to smc context */ + b sp_min_exit +endfunc sp_min_entrypoint + +/* + * The Warm boot entrypoint for SP_MIN. + */ +func sp_min_warm_entrypoint + + /* Setup C runtime stack */ + bl plat_set_my_stack + + /* -------------------------------------------- + * Enable the MMU with the DCache disabled. It + * is safe to use stacks allocated in normal + * memory as a result. All memory accesses are + * marked nGnRnE when the MMU is disabled. So + * all the stack writes will make it to memory. + * All memory accesses are marked Non-cacheable + * when the MMU is enabled but D$ is disabled. + * So used stack memory is guaranteed to be + * visible immediately after the MMU is enabled + * Enabling the DCache at the same time as the + * MMU can lead to speculatively fetched and + * possibly stale stack memory being read from + * other caches. This can lead to coherency + * issues. + * -------------------------------------------- + */ + mov r0, #DISABLE_DCACHE + bl bl32_plat_enable_mmu + + bl sp_min_warm_boot + + /* Program the registers in cpu_context and exit monitor mode */ + mov r0, #NON_SECURE + bl cm_get_context + + /* Restore the SCR */ + ldr r2, [r0, #CTX_REGS_OFFSET + CTX_SCR] + stcopr r2, SCR + isb + + /* Restore the SCTLR */ + ldr r2, [r0, #CTX_REGS_OFFSET + CTX_NS_SCTLR] + stcopr r2, SCTLR + + bl smc_get_next_ctx + + /* The other cpu_context registers have been copied to smc context */ + b sp_min_exit +endfunc sp_min_warm_entrypoint + +/* + * The function to restore the registers from SMC context and return + * to the mode restored to SPSR. + * + * Arguments : r0 must point to the SMC context to restore from. + */ +func sp_min_exit + smcc_restore_gp_mode_regs + eret +endfunc sp_min_exit diff --git a/bl32/sp_min/sp_min.ld.S b/bl32/sp_min/sp_min.ld.S new file mode 100644 index 00000000..b158db16 --- /dev/null +++ b/bl32/sp_min/sp_min.ld.S @@ -0,0 +1,232 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <platform_def.h> + +OUTPUT_FORMAT(elf32-littlearm) +OUTPUT_ARCH(arm) +ENTRY(sp_min_vector_table) + +MEMORY { + RAM (rwx): ORIGIN = BL32_BASE, LENGTH = BL32_LIMIT - BL32_BASE +} + + +SECTIONS +{ + . = BL32_BASE; + ASSERT(. == ALIGN(4096), + "BL32_BASE address is not aligned on a page boundary.") + +#if SEPARATE_CODE_AND_RODATA + .text . : { + __TEXT_START__ = .; + *entrypoint.o(.text*) + *(.text*) + . = NEXT(4096); + __TEXT_END__ = .; + } >RAM + + .rodata . : { + __RODATA_START__ = .; + *(.rodata*) + + /* Ensure 4-byte alignment for descriptors and ensure inclusion */ + . = ALIGN(4); + __RT_SVC_DESCS_START__ = .; + KEEP(*(rt_svc_descs)) + __RT_SVC_DESCS_END__ = .; + + /* + * Ensure 4-byte alignment for cpu_ops so that its fields are also + * aligned. Also ensure cpu_ops inclusion. + */ + . = ALIGN(4); + __CPU_OPS_START__ = .; + KEEP(*(cpu_ops)) + __CPU_OPS_END__ = .; + + . = NEXT(4096); + __RODATA_END__ = .; + } >RAM +#else + ro . : { + __RO_START__ = .; + *entrypoint.o(.text*) + *(.text*) + *(.rodata*) + + /* Ensure 4-byte alignment for descriptors and ensure inclusion */ + . = ALIGN(4); + __RT_SVC_DESCS_START__ = .; + KEEP(*(rt_svc_descs)) + __RT_SVC_DESCS_END__ = .; + + /* + * Ensure 4-byte alignment for cpu_ops so that its fields are also + * aligned. Also ensure cpu_ops inclusion. + */ + . = ALIGN(4); + __CPU_OPS_START__ = .; + KEEP(*(cpu_ops)) + __CPU_OPS_END__ = .; + + __RO_END_UNALIGNED__ = .; + + /* + * Memory page(s) mapped to this section will be marked as + * read-only, executable. No RW data from the next section must + * creep in. Ensure the rest of the current memory block is unused. + */ + . = NEXT(4096); + __RO_END__ = .; + } >RAM +#endif + + ASSERT(__CPU_OPS_END__ > __CPU_OPS_START__, + "cpu_ops not defined for this platform.") + /* + * Define a linker symbol to mark start of the RW memory area for this + * image. + */ + __RW_START__ = . ; + + .data . : { + __DATA_START__ = .; + *(.data*) + __DATA_END__ = .; + } >RAM + + stacks (NOLOAD) : { + __STACKS_START__ = .; + *(tzfw_normal_stacks) + __STACKS_END__ = .; + } >RAM + + /* + * The .bss section gets initialised to 0 at runtime. + * Its base address must be 16-byte aligned. + */ + .bss (NOLOAD) : ALIGN(16) { + __BSS_START__ = .; + *(.bss*) + *(COMMON) +#if !USE_COHERENT_MEM + /* + * Bakery locks are stored in normal .bss memory + * + * Each lock's data is spread across multiple cache lines, one per CPU, + * but multiple locks can share the same cache line. + * The compiler will allocate enough memory for one CPU's bakery locks, + * the remaining cache lines are allocated by the linker script + */ + . = ALIGN(CACHE_WRITEBACK_GRANULE); + __BAKERY_LOCK_START__ = .; + *(bakery_lock) + . = ALIGN(CACHE_WRITEBACK_GRANULE); + __PERCPU_BAKERY_LOCK_SIZE__ = ABSOLUTE(. - __BAKERY_LOCK_START__); + . = . + (__PERCPU_BAKERY_LOCK_SIZE__ * (PLATFORM_CORE_COUNT - 1)); + __BAKERY_LOCK_END__ = .; +#ifdef PLAT_PERCPU_BAKERY_LOCK_SIZE + ASSERT(__PERCPU_BAKERY_LOCK_SIZE__ == PLAT_PERCPU_BAKERY_LOCK_SIZE, + "PLAT_PERCPU_BAKERY_LOCK_SIZE does not match bakery lock requirements"); +#endif +#endif + +#if ENABLE_PMF + /* + * Time-stamps are stored in normal .bss memory + * + * The compiler will allocate enough memory for one CPU's time-stamps, + * the remaining memory for other CPU's is allocated by the + * linker script + */ + . = ALIGN(CACHE_WRITEBACK_GRANULE); + __PMF_TIMESTAMP_START__ = .; + KEEP(*(pmf_timestamp_array)) + . = ALIGN(CACHE_WRITEBACK_GRANULE); + __PMF_PERCPU_TIMESTAMP_END__ = .; + __PERCPU_TIMESTAMP_SIZE__ = ABSOLUTE(. - __PMF_TIMESTAMP_START__); + . = . + (__PERCPU_TIMESTAMP_SIZE__ * (PLATFORM_CORE_COUNT - 1)); + __PMF_TIMESTAMP_END__ = .; +#endif /* ENABLE_PMF */ + + __BSS_END__ = .; + } >RAM + + /* + * The xlat_table section is for full, aligned page tables (4K). + * Removing them from .bss avoids forcing 4K alignment on + * the .bss section and eliminates the unecessary zero init + */ + xlat_table (NOLOAD) : { + *(xlat_table) + } >RAM + + __BSS_SIZE__ = SIZEOF(.bss); + +#if USE_COHERENT_MEM + /* + * The base address of the coherent memory section must be page-aligned (4K) + * to guarantee that the coherent data are stored on their own pages and + * are not mixed with normal data. This is required to set up the correct + * memory attributes for the coherent data page tables. + */ + coherent_ram (NOLOAD) : ALIGN(4096) { + __COHERENT_RAM_START__ = .; + /* + * Bakery locks are stored in coherent memory + * + * Each lock's data is contiguous and fully allocated by the compiler + */ + *(bakery_lock) + *(tzfw_coherent_mem) + __COHERENT_RAM_END_UNALIGNED__ = .; + /* + * Memory page(s) mapped to this section will be marked + * as device memory. No other unexpected data must creep in. + * Ensure the rest of the current memory page is unused. + */ + . = NEXT(4096); + __COHERENT_RAM_END__ = .; + } >RAM + + __COHERENT_RAM_UNALIGNED_SIZE__ = + __COHERENT_RAM_END_UNALIGNED__ - __COHERENT_RAM_START__; +#endif + + /* + * Define a linker symbol to mark end of the RW memory area for this + * image. + */ + __RW_END__ = .; + + __BL32_END__ = .; +} diff --git a/bl32/sp_min/sp_min.mk b/bl32/sp_min/sp_min.mk new file mode 100644 index 00000000..a8b572e0 --- /dev/null +++ b/bl32/sp_min/sp_min.mk @@ -0,0 +1,63 @@ +# +# Copyright (c) 2016, 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: +# +# Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# Neither the name of ARM nor the names of its contributors may be used +# to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +ifneq (${ARCH}, aarch32) + $(error SP_MIN is only supported on AArch32 platforms) +endif + +include lib/psci/psci_lib.mk + +INCLUDES += -Iinclude/bl32/sp_min + +BL32_SOURCES += bl32/sp_min/sp_min_main.c \ + bl32/sp_min/aarch32/entrypoint.S \ + common/runtime_svc.c \ + services/std_svc/std_svc_setup.c \ + ${PSCI_LIB_SOURCES} + +ifeq (${ENABLE_PMF}, 1) +BL32_SOURCES += lib/pmf/pmf_main.c +endif + +BL32_LINKERFILE := bl32/sp_min/sp_min.ld.S + +# Include the platform-specific SP_MIN Makefile +# If no platform-specific SP_MIN Makefile exists, it means SP_MIN is not supported +# on this platform. +SP_MIN_PLAT_MAKEFILE := $(wildcard ${PLAT_DIR}/sp_min/sp_min-${PLAT}.mk) +ifeq (,${SP_MIN_PLAT_MAKEFILE}) + $(error SP_MIN is not supported on platform ${PLAT}) +else + include ${SP_MIN_PLAT_MAKEFILE} +endif + +RESET_TO_SP_MIN := 1 +$(eval $(call add_define,RESET_TO_SP_MIN)) +$(eval $(call assert_boolean,RESET_TO_SP_MIN)) diff --git a/bl32/sp_min/sp_min_main.c b/bl32/sp_min/sp_min_main.c new file mode 100644 index 00000000..31cab3df --- /dev/null +++ b/bl32/sp_min/sp_min_main.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <arch.h> +#include <arch_helpers.h> +#include <assert.h> +#include <bl_common.h> +#include <context.h> +#include <context_mgmt.h> +#include <debug.h> +#include <platform.h> +#include <platform_def.h> +#include <platform_sp_min.h> +#include <psci.h> +#include <runtime_svc.h> +#include <smcc_helpers.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <types.h> +#include "sp_min_private.h" + +/* Pointers to per-core cpu contexts */ +static void *sp_min_cpu_ctx_ptr[PLATFORM_CORE_COUNT]; + +/* SP_MIN only stores the non secure smc context */ +static smc_ctx_t sp_min_smc_context[PLATFORM_CORE_COUNT]; + +/****************************************************************************** + * Define the smcc helper library API's + *****************************************************************************/ +void *smc_get_ctx(int security_state) +{ + assert(security_state == NON_SECURE); + return &sp_min_smc_context[plat_my_core_pos()]; +} + +void smc_set_next_ctx(int security_state) +{ + assert(security_state == NON_SECURE); + /* SP_MIN stores only non secure smc context. Nothing to do here */ +} + +void *smc_get_next_ctx(void) +{ + return &sp_min_smc_context[plat_my_core_pos()]; +} + +/******************************************************************************* + * This function returns a pointer to the most recent 'cpu_context' structure + * for the calling CPU that was set as the context for the specified security + * state. NULL is returned if no such structure has been specified. + ******************************************************************************/ +void *cm_get_context(uint32_t security_state) +{ + assert(security_state == NON_SECURE); + return sp_min_cpu_ctx_ptr[plat_my_core_pos()]; +} + +/******************************************************************************* + * This function sets the pointer to the current 'cpu_context' structure for the + * specified security state for the calling CPU + ******************************************************************************/ +void cm_set_context(void *context, uint32_t security_state) +{ + assert(security_state == NON_SECURE); + sp_min_cpu_ctx_ptr[plat_my_core_pos()] = context; +} + +/******************************************************************************* + * This function returns a pointer to the most recent 'cpu_context' structure + * for the CPU identified by `cpu_idx` that was set as the context for the + * specified security state. NULL is returned if no such structure has been + * specified. + ******************************************************************************/ +void *cm_get_context_by_index(unsigned int cpu_idx, + unsigned int security_state) +{ + assert(security_state == NON_SECURE); + return sp_min_cpu_ctx_ptr[cpu_idx]; +} + +/******************************************************************************* + * This function sets the pointer to the current 'cpu_context' structure for the + * specified security state for the CPU identified by CPU index. + ******************************************************************************/ +void cm_set_context_by_index(unsigned int cpu_idx, void *context, + unsigned int security_state) +{ + assert(security_state == NON_SECURE); + sp_min_cpu_ctx_ptr[cpu_idx] = context; +} + +static void copy_cpu_ctx_to_smc_stx(const regs_t *cpu_reg_ctx, + smc_ctx_t *next_smc_ctx) +{ + next_smc_ctx->r0 = read_ctx_reg(cpu_reg_ctx, CTX_GPREG_R0); + next_smc_ctx->lr_mon = read_ctx_reg(cpu_reg_ctx, CTX_LR); + next_smc_ctx->spsr_mon = read_ctx_reg(cpu_reg_ctx, CTX_SPSR); +} + +/******************************************************************************* + * This function invokes the PSCI library interface to initialize the + * non secure cpu context and copies the relevant cpu context register values + * to smc context. These registers will get programmed during `smc_exit`. + ******************************************************************************/ +static void sp_min_prepare_next_image_entry(void) +{ + entry_point_info_t *next_image_info; + + /* Program system registers to proceed to non-secure */ + next_image_info = sp_min_plat_get_bl33_ep_info(); + assert(next_image_info); + assert(NON_SECURE == GET_SECURITY_STATE(next_image_info->h.attr)); + + INFO("SP_MIN: Preparing exit to normal world\n"); + + psci_prepare_next_non_secure_ctx(next_image_info); + smc_set_next_ctx(NON_SECURE); + + /* Copy r0, lr and spsr from cpu context to SMC context */ + copy_cpu_ctx_to_smc_stx(get_regs_ctx(cm_get_context(NON_SECURE)), + smc_get_next_ctx()); +} + +/****************************************************************************** + * The SP_MIN main function. Do the platform and PSCI Library setup. Also + * initialize the runtime service framework. + *****************************************************************************/ +void sp_min_main(void) +{ + /* Perform platform setup in TSP MIN */ + sp_min_platform_setup(); + + /* + * Initialize the PSCI library and perform the remaining generic + * architectural setup from PSCI. + */ + psci_setup((uintptr_t)sp_min_warm_entrypoint); + + /* + * Initialize the runtime services e.g. psci + * This is where the monitor mode will be initialized + */ + INFO("SP_MIN: Initializing runtime services\n"); + runtime_svc_init(); + + /* + * We are ready to enter the next EL. Prepare entry into the image + * corresponding to the desired security state after the next ERET. + */ + sp_min_prepare_next_image_entry(); +} + +/****************************************************************************** + * This function is invoked during warm boot. Invoke the PSCI library + * warm boot entry point which takes care of Architectural and platform setup/ + * restore. Copy the relevant cpu_context register values to smc context which + * will get programmed during `smc_exit`. + *****************************************************************************/ +void sp_min_warm_boot(void) +{ + smc_ctx_t *next_smc_ctx; + + psci_warmboot_entrypoint(); + + smc_set_next_ctx(NON_SECURE); + + next_smc_ctx = smc_get_next_ctx(); + memset(next_smc_ctx, 0, sizeof(smc_ctx_t)); + + copy_cpu_ctx_to_smc_stx(get_regs_ctx(cm_get_context(NON_SECURE)), + next_smc_ctx); +} diff --git a/bl32/sp_min/sp_min_private.h b/bl32/sp_min/sp_min_private.h new file mode 100644 index 00000000..0042f405 --- /dev/null +++ b/bl32/sp_min/sp_min_private.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __SP_MIN_H__ +#define __SP_MIN_H__ + +void sp_min_warm_entrypoint(void); +void sp_min_main(void); +void sp_min_warm_boot(void); + +#endif /* __SP_MIN_H__ */ diff --git a/bl32/tsp/tsp.mk b/bl32/tsp/tsp.mk index a5024284..2f3391e5 100644 --- a/bl32/tsp/tsp.mk +++ b/bl32/tsp/tsp.mk @@ -37,7 +37,7 @@ BL32_SOURCES += bl32/tsp/tsp_main.c \ bl32/tsp/tsp_interrupt.c \ bl32/tsp/tsp_timer.c \ common/aarch64/early_exceptions.S \ - lib/locks/exclusive/spinlock.S + lib/locks/exclusive/aarch64/spinlock.S BL32_LINKERFILE := bl32/tsp/tsp.ld.S diff --git a/common/aarch32/debug.S b/common/aarch32/debug.S new file mode 100644 index 00000000..01ec1e38 --- /dev/null +++ b/common/aarch32/debug.S @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <arch.h> +#include <asm_macros.S> + + .globl do_panic + + /*********************************************************** + * The common implementation of do_panic for all BL stages + ***********************************************************/ +func do_panic + b plat_panic_handler +endfunc do_panic + diff --git a/common/bl_common.c b/common/bl_common.c index be56256b..6dcd4c18 100644 --- a/common/bl_common.c +++ b/common/bl_common.c @@ -321,12 +321,16 @@ int load_image(meminfo_t *mem_layout, (void *) image_base, image_size); } +#if !TRUSTED_BOARD_BOOT /* * File has been successfully loaded. - * Flush the image in Trusted SRAM so that the next exception level can - * see it. + * Flush the image to main memory so that it can be executed later by + * any CPU, regardless of cache and MMU state. + * When TBB is enabled the image is flushed later, after image + * authentication. */ flush_dcache_range(image_base, image_size); +#endif /* TRUSTED_BOARD_BOOT */ INFO("Image id=%u loaded at address %p, size = 0x%zx\n", image_id, (void *) image_base, image_size); @@ -388,10 +392,12 @@ int load_auth_image(meminfo_t *mem_layout, image_data->image_size); return -EAUTH; } - - /* After working with data, invalidate the data cache */ - inv_dcache_range(image_data->image_base, - (size_t)image_data->image_size); + /* + * File has been successfully loaded and authenticated. + * Flush the image to main memory so that it can be executed later by + * any CPU, regardless of cache and MMU state. + */ + flush_dcache_range(image_data->image_base, image_data->image_size); #endif /* TRUSTED_BOARD_BOOT */ return 0; diff --git a/common/runtime_svc.c b/common/runtime_svc.c index b8af6cd8..df0d64ca 100644 --- a/common/runtime_svc.c +++ b/common/runtime_svc.c @@ -52,6 +52,34 @@ static rt_svc_desc_t *rt_svc_descs; / sizeof(rt_svc_desc_t)) /******************************************************************************* + * Function to invoke the registered `handle` corresponding to the smc_fid. + ******************************************************************************/ +uintptr_t handle_runtime_svc(uint32_t smc_fid, + void *cookie, + void *handle, + unsigned int flags) +{ + u_register_t x1, x2, x3, x4; + int index, idx; + const rt_svc_desc_t *rt_svc_descs; + + assert(handle); + idx = get_unique_oen_from_smc_fid(smc_fid); + assert(idx >= 0 && idx < MAX_RT_SVCS); + + index = rt_svc_descs_indices[idx]; + if (index < 0 || index >= RT_SVC_DECS_NUM) + SMC_RET1(handle, SMC_UNK); + + rt_svc_descs = (rt_svc_desc_t *) RT_SVC_DESCS_START; + + get_smc_params_from_ctx(handle, x1, x2, x3, x4); + + return rt_svc_descs[index].handle(smc_fid, x1, x2, x3, x4, cookie, + handle, flags); +} + +/******************************************************************************* * Simple routine to sanity check a runtime service descriptor before using it ******************************************************************************/ static int32_t validate_rt_svc_desc(const rt_svc_desc_t *desc) diff --git a/common/tf_printf.c b/common/tf_printf.c index ad0b90aa..8c1857e5 100644 --- a/common/tf_printf.c +++ b/common/tf_printf.c @@ -27,7 +27,11 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ +#include <arch.h> +#include <arch_helpers.h> +#include <assert.h> #include <debug.h> +#include <limits.h> #include <stdarg.h> #include <stdint.h> @@ -49,6 +53,85 @@ static void string_print(const char *str) putchar(*str++); } +#ifdef AARCH32 +#define unsigned_num_print(unum, radix) \ + do { \ + if ((radix) == 16) \ + unsigned_hex_print(unum); \ + else if ((radix) == 10) \ + unsigned_dec_print(unum); \ + else \ + string_print("tf_printf : Unsupported radix");\ + } while (0); + +/* + * Utility function to print an unsigned number in decimal format for AArch32. + * The function doesn't support printing decimal integers higher than 32 bits + * to avoid having to implement 64-bit integer compiler library functions. + */ +static void unsigned_dec_print(unsigned long long int unum) +{ + unsigned int local_num; + /* Just need enough space to store 32 bit decimal integer */ + unsigned char num_buf[10]; + int i = 0, rem; + + if (unum > UINT_MAX) { + string_print("tf_printf : decimal numbers higher than 32 bits" + " not supported\n"); + return; + } + + local_num = (unsigned int)unum; + + do { + rem = local_num % 10; + num_buf[i++] = '0' + rem; + } while (local_num /= 10); + + while (--i >= 0) + putchar(num_buf[i]); +} + +/* + * Utility function to print an unsigned number in hexadecimal format for + * AArch32. The function doesn't use 64-bit integer arithmetic to avoid + * having to implement 64-bit compiler library functions. It splits the + * 64 bit number into two 32 bit numbers and converts them into equivalent + * ASCII characters. + */ +static void unsigned_hex_print(unsigned long long int unum) +{ + /* Just need enough space to store 16 characters */ + unsigned char num_buf[16]; + int i = 0, rem; + uint32_t num_local = 0, num_msb = 0; + + /* Get the LSB of 64 bit unum */ + num_local = (uint32_t)unum; + /* Get the MSB of 64 bit unum. This works only on Little Endian */ + assert((read_sctlr() & SCTLR_EE_BIT) == 0); + num_msb = *(((uint32_t *) &unum) + 1); + + do { + do { + rem = (num_local & 0xf); + if (rem < 0xa) + num_buf[i++] = '0' + rem; + else + num_buf[i++] = 'a' + (rem - 0xa); + } while (num_local >>= 4); + + num_local = num_msb; + num_msb = 0; + } while (num_local); + + while (--i >= 0) + putchar(num_buf[i]); +} + +#else + static void unsigned_num_print(unsigned long long int unum, unsigned int radix) { /* Just need enough space to store 64 bit decimal integer */ @@ -66,6 +149,7 @@ static void unsigned_num_print(unsigned long long int unum, unsigned int radix) while (--i >= 0) putchar(num_buf[i]); } +#endif /* AARCH32 */ /******************************************************************* * Reduced format print for Trusted firmware. diff --git a/docs/user-guide.md b/docs/user-guide.md index 948e3ab2..a6959b14 100644 --- a/docs/user-guide.md +++ b/docs/user-guide.md @@ -208,11 +208,21 @@ performed. platform name must be subdirectory of any depth under `plat/`, and must contain a platform makefile named `platform.mk`. +* `ARCH` : Choose the target build architecture for ARM Trusted Firmware. + It can take either `aarch64` or `aarch32` as values. By default, it is + defined to `aarch64`. + * `SPD`: Choose a Secure Payload Dispatcher component to be built into the - Trusted Firmware. The value should be the path to the directory containing - the SPD source, relative to `services/spd/`; the directory is expected to + Trusted Firmware. This build option is only valid if `ARCH=aarch64`. The + value should be the path to the directory containing the SPD source, + relative to `services/spd/`; the directory is expected to contain a makefile called `<spd-value>.mk`. +* `AARCH32_SP` : Choose the AArch32 Secure Payload component to be built as + as the BL32 image when `ARCH=aarch32`. The value should be the path to the + directory containing the SP source, relative to the `bl32/`; the directory + is expected to contain a makefile called `<aarch32_sp-value>.mk`. + * `V`: Verbose build. If assigned anything other than 0, the build commands are printed. Default is 0. diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c index e0170338..d13e2c9c 100644 --- a/drivers/arm/gic/v3/gicv3_main.c +++ b/drivers/arm/gic/v3/gicv3_main.c @@ -75,8 +75,12 @@ void gicv3_driver_init(const gicv3_driver_data_t *plat_driver_data) plat_driver_data->g1s_interrupt_num == 0); /* Check for system register support */ +#ifdef AARCH32 + assert(read_id_pfr1() & (ID_PFR1_GIC_MASK << ID_PFR1_GIC_SHIFT)); +#else assert(read_id_aa64pfr0_el1() & (ID_AA64PFR0_GIC_MASK << ID_AA64PFR0_GIC_SHIFT)); +#endif /* AARCH32 */ /* The GIC version should be 3.0 */ gic_version = gicd_read_pidr2(plat_driver_data->gicd_base); diff --git a/drivers/arm/gic/v3/gicv3_private.h b/drivers/arm/gic/v3/gicv3_private.h index 9aa83382..1344a885 100644 --- a/drivers/arm/gic/v3/gicv3_private.h +++ b/drivers/arm/gic/v3/gicv3_private.h @@ -79,9 +79,13 @@ * Macro to convert a GICR_TYPER affinity value into a MPIDR value. Bits[31:24] * are zeroes. */ +#ifdef AARCH32 +#define mpidr_from_gicr_typer(typer_val) (((typer_val) >> 32) & 0xffffff) +#else #define mpidr_from_gicr_typer(typer_val) \ - ((((typer_val >> 56) & MPIDR_AFFLVL_MASK) << MPIDR_AFF3_SHIFT) | \ - ((typer_val >> 32) & 0xffffff)) + (((((typer_val) >> 56) & MPIDR_AFFLVL_MASK) << MPIDR_AFF3_SHIFT) | \ + (((typer_val) >> 32) & 0xffffff)) +#endif /******************************************************************************* * Private GICv3 function prototypes for accessing entire registers. diff --git a/drivers/arm/pl011/aarch32/pl011_console.S b/drivers/arm/pl011/aarch32/pl011_console.S new file mode 100644 index 00000000..21ed7ab8 --- /dev/null +++ b/drivers/arm/pl011/aarch32/pl011_console.S @@ -0,0 +1,160 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <arch.h> +#include <asm_macros.S> +#include <pl011.h> + +/* + * Pull in generic functions to provide backwards compatibility for + * platform makefiles + */ +#include "../../../console/aarch32/console.S" + + .globl console_core_init + .globl console_core_putc + .globl console_core_getc + + + /* ----------------------------------------------- + * int console_core_init(uintptr_t base_addr, + * unsigned int uart_clk, unsigned int baud_rate) + * Function to initialize the console without a + * C Runtime to print debug information. This + * function will be accessed by console_init and + * crash reporting. + * In: r0 - console base address + * r1 - Uart clock in Hz + * r2 - Baud rate + * Out: return 1 on success else 0 on error + * Clobber list : r1, r2, r3 + * ----------------------------------------------- + */ +func console_core_init + /* Check the input base address */ + cmp r0, #0 + beq core_init_fail +#if !PL011_GENERIC_UART + /* Check baud rate and uart clock for sanity */ + cmp r1, #0 + beq core_init_fail + cmp r2, #0 + beq core_init_fail + /* Disable the UART before initialization */ + ldr r3, [r0, #UARTCR] + bic r3, r3, #PL011_UARTCR_UARTEN + str r3, [r0, #UARTCR] + /* Program the baudrate */ + /* Divisor = (Uart clock * 4) / baudrate */ + lsl r1, r1, #2 + udiv r2, r1, r2 + /* IBRD = Divisor >> 6 */ + lsr r1, r2, #6 + /* Write the IBRD */ + str r1, [r0, #UARTIBRD] + /* FBRD = Divisor & 0x3F */ + and r1, r2, #0x3f + /* Write the FBRD */ + str r1, [r0, #UARTFBRD] + mov r1, #PL011_LINE_CONTROL + str r1, [r0, #UARTLCR_H] + /* Clear any pending errors */ + mov r1, #0 + str r1, [r0, #UARTECR] + /* Enable tx, rx, and uart overall */ + ldr r1, =(PL011_UARTCR_RXE | PL011_UARTCR_TXE | PL011_UARTCR_UARTEN) + str r1, [r0, #UARTCR] +#endif + mov r0, #1 + bx lr +core_init_fail: + mov r0, #0 + bx lr +endfunc console_core_init + + /* -------------------------------------------------------- + * int console_core_putc(int c, uintptr_t base_addr) + * Function to output a character over the console. It + * returns the character printed on success or -1 on error. + * In : r0 - character to be printed + * r1 - console base address + * Out : return -1 on error else return character. + * Clobber list : r2 + * -------------------------------------------------------- + */ +func console_core_putc + /* Check the input parameter */ + cmp r1, #0 + beq putc_error + /* Prepend '\r' to '\n' */ + cmp r0, #0xA + bne 2f +1: + /* Check if the transmit FIFO is full */ + ldr r2, [r1, #UARTFR] + tst r2, #PL011_UARTFR_TXFF_BIT + beq 1b + mov r2, #0xD + str r2, [r1, #UARTDR] +2: + /* Check if the transmit FIFO is full */ + ldr r2, [r1, #UARTFR] + tst r2, #PL011_UARTFR_TXFF_BIT + beq 2b + str r0, [r1, #UARTDR] + bx lr +putc_error: + mov r0, #-1 + bx lr +endfunc console_core_putc + + /* --------------------------------------------- + * int console_core_getc(uintptr_t base_addr) + * Function to get a character from the console. + * It returns the character grabbed on success + * or -1 on error. + * In : r0 - console base address + * Clobber list : r0, r1 + * --------------------------------------------- + */ +func console_core_getc + cmp r0, #0 + beq getc_error +1: + /* Check if the receive FIFO is empty */ + ldr r1, [r0, #UARTFR] + tst r1, #PL011_UARTFR_RXFE_BIT + beq 1b + ldr r1, [r0, #UARTDR] + mov r0, r1 + bx lr +getc_error: + mov r0, #-1 + bx lr +endfunc console_core_getc diff --git a/drivers/arm/pl011/aarch64/pl011_console.S b/drivers/arm/pl011/aarch64/pl011_console.S new file mode 100644 index 00000000..11e3df77 --- /dev/null +++ b/drivers/arm/pl011/aarch64/pl011_console.S @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2013-2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <arch.h> +#include <asm_macros.S> +#include <pl011.h> + +/* + * Pull in generic functions to provide backwards compatibility for + * platform makefiles + */ +#include "../../../console/aarch64/console.S" + + + .globl console_core_init + .globl console_core_putc + .globl console_core_getc + + + /* ----------------------------------------------- + * int console_core_init(uintptr_t base_addr, + * unsigned int uart_clk, unsigned int baud_rate) + * Function to initialize the console without a + * C Runtime to print debug information. This + * function will be accessed by console_init and + * crash reporting. + * In: x0 - console base address + * w1 - Uart clock in Hz + * w2 - Baud rate + * Out: return 1 on success else 0 on error + * Clobber list : x1, x2, x3, x4 + * ----------------------------------------------- + */ +func console_core_init + /* Check the input base address */ + cbz x0, core_init_fail +#if !PL011_GENERIC_UART + /* Check baud rate and uart clock for sanity */ + cbz w1, core_init_fail + cbz w2, core_init_fail + /* Disable uart before programming */ + ldr w3, [x0, #UARTCR] + mov w4, #PL011_UARTCR_UARTEN + bic w3, w3, w4 + str w3, [x0, #UARTCR] + /* Program the baudrate */ + /* Divisor = (Uart clock * 4) / baudrate */ + lsl w1, w1, #2 + udiv w2, w1, w2 + /* IBRD = Divisor >> 6 */ + lsr w1, w2, #6 + /* Write the IBRD */ + str w1, [x0, #UARTIBRD] + /* FBRD = Divisor & 0x3F */ + and w1, w2, #0x3f + /* Write the FBRD */ + str w1, [x0, #UARTFBRD] + mov w1, #PL011_LINE_CONTROL + str w1, [x0, #UARTLCR_H] + /* Clear any pending errors */ + str wzr, [x0, #UARTECR] + /* Enable tx, rx, and uart overall */ + mov w1, #(PL011_UARTCR_RXE | PL011_UARTCR_TXE | PL011_UARTCR_UARTEN) + str w1, [x0, #UARTCR] +#endif + mov w0, #1 + ret +core_init_fail: + mov w0, wzr + ret +endfunc console_core_init + + /* -------------------------------------------------------- + * int console_core_putc(int c, uintptr_t base_addr) + * Function to output a character over the console. It + * returns the character printed on success or -1 on error. + * In : w0 - character to be printed + * x1 - console base address + * Out : return -1 on error else return character. + * Clobber list : x2 + * -------------------------------------------------------- + */ +func console_core_putc + /* Check the input parameter */ + cbz x1, putc_error + /* Prepend '\r' to '\n' */ + cmp w0, #0xA + b.ne 2f +1: + /* Check if the transmit FIFO is full */ + ldr w2, [x1, #UARTFR] + tbnz w2, #PL011_UARTFR_TXFF_BIT, 1b + mov w2, #0xD + str w2, [x1, #UARTDR] +2: + /* Check if the transmit FIFO is full */ + ldr w2, [x1, #UARTFR] + tbnz w2, #PL011_UARTFR_TXFF_BIT, 2b + str w0, [x1, #UARTDR] + ret +putc_error: + mov w0, #-1 + ret +endfunc console_core_putc + + /* --------------------------------------------- + * int console_core_getc(uintptr_t base_addr) + * Function to get a character from the console. + * It returns the character grabbed on success + * or -1 on error. + * In : x0 - console base address + * Clobber list : x0, x1 + * --------------------------------------------- + */ +func console_core_getc + cbz x0, getc_error +1: + /* Check if the receive FIFO is empty */ + ldr w1, [x0, #UARTFR] + tbnz w1, #PL011_UARTFR_RXFE_BIT, 1b + ldr w1, [x0, #UARTDR] + mov w0, w1 + ret +getc_error: + mov w0, #-1 + ret +endfunc console_core_getc diff --git a/drivers/arm/pl011/pl011_console.S b/drivers/arm/pl011/pl011_console.S index 5e97e911..44aafc2d 100644 --- a/drivers/arm/pl011/pl011_console.S +++ b/drivers/arm/pl011/pl011_console.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2016, 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: @@ -27,127 +27,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -#include <arch.h> -#include <asm_macros.S> -#include <pl011.h> -/* - * Pull in generic functions to provide backwards compatibility for - * platform makefiles - */ -#include "../../console/console.S" - - - .globl console_core_init - .globl console_core_putc - .globl console_core_getc - - - /* ----------------------------------------------- - * int console_core_init(uintptr_t base_addr, - * unsigned int uart_clk, unsigned int baud_rate) - * Function to initialize the console without a - * C Runtime to print debug information. This - * function will be accessed by console_init and - * crash reporting. - * In: x0 - console base address - * w1 - Uart clock in Hz - * w2 - Baud rate - * Out: return 1 on success else 0 on error - * Clobber list : x1, x2, x3, x4 - * ----------------------------------------------- - */ -func console_core_init - /* Check the input base address */ - cbz x0, core_init_fail -#if !PL011_GENERIC_UART - /* Check baud rate and uart clock for sanity */ - cbz w1, core_init_fail - cbz w2, core_init_fail - /* Disable uart before programming */ - ldr w3, [x0, #UARTCR] - mov w4, #PL011_UARTCR_UARTEN - bic w3, w3, w4 - str w3, [x0, #UARTCR] - /* Program the baudrate */ - /* Divisor = (Uart clock * 4) / baudrate */ - lsl w1, w1, #2 - udiv w2, w1, w2 - /* IBRD = Divisor >> 6 */ - lsr w1, w2, #6 - /* Write the IBRD */ - str w1, [x0, #UARTIBRD] - /* FBRD = Divisor & 0x3F */ - and w1, w2, #0x3f - /* Write the FBRD */ - str w1, [x0, #UARTFBRD] - mov w1, #PL011_LINE_CONTROL - str w1, [x0, #UARTLCR_H] - /* Clear any pending errors */ - str wzr, [x0, #UARTECR] - /* Enable tx, rx, and uart overall */ - mov w1, #(PL011_UARTCR_RXE | PL011_UARTCR_TXE | PL011_UARTCR_UARTEN) - str w1, [x0, #UARTCR] +#if !ERROR_DEPRECATED +#include "./aarch64/pl011_console.S" #endif - mov w0, #1 - ret -core_init_fail: - mov w0, wzr - ret -endfunc console_core_init - - /* -------------------------------------------------------- - * int console_core_putc(int c, uintptr_t base_addr) - * Function to output a character over the console. It - * returns the character printed on success or -1 on error. - * In : w0 - character to be printed - * x1 - console base address - * Out : return -1 on error else return character. - * Clobber list : x2 - * -------------------------------------------------------- - */ -func console_core_putc - /* Check the input parameter */ - cbz x1, putc_error - /* Prepend '\r' to '\n' */ - cmp w0, #0xA - b.ne 2f -1: - /* Check if the transmit FIFO is full */ - ldr w2, [x1, #UARTFR] - tbnz w2, #PL011_UARTFR_TXFF_BIT, 1b - mov w2, #0xD - str w2, [x1, #UARTDR] -2: - /* Check if the transmit FIFO is full */ - ldr w2, [x1, #UARTFR] - tbnz w2, #PL011_UARTFR_TXFF_BIT, 2b - str w0, [x1, #UARTDR] - ret -putc_error: - mov w0, #-1 - ret -endfunc console_core_putc - - /* --------------------------------------------- - * int console_core_getc(uintptr_t base_addr) - * Function to get a character from the console. - * It returns the character grabbed on success - * or -1 on error. - * In : x0 - console base address - * Clobber list : x0, x1 - * --------------------------------------------- - */ -func console_core_getc - cbz x0, getc_error -1: - /* Check if the receive FIFO is empty */ - ldr w1, [x0, #UARTFR] - tbnz w1, #PL011_UARTFR_RXFE_BIT, 1b - ldr w1, [x0, #UARTDR] - mov w0, w1 - ret -getc_error: - mov w0, #-1 - ret -endfunc console_core_getc diff --git a/drivers/arm/tzc/tzc400.c b/drivers/arm/tzc/tzc400.c index e5335872..ca088c32 100644 --- a/drivers/arm/tzc/tzc400.c +++ b/drivers/arm/tzc/tzc400.c @@ -206,7 +206,7 @@ void tzc400_configure_region(unsigned int filters, * Do address range check based on TZC configuration. A 64bit address is * the max and expected case. */ - assert(((region_top <= (UINT64_MAX >> (64 - tzc400.addr_width))) && + assert(((region_top <= _tzc_get_max_top_addr(tzc400.addr_width)) && (region_base < region_top))); /* region_base and (region_top + 1) must be 4KB aligned */ diff --git a/drivers/arm/tzc/tzc_common_private.c b/drivers/arm/tzc/tzc_common_private.c index dae6c3ac..8b1ddf49 100644 --- a/drivers/arm/tzc/tzc_common_private.c +++ b/drivers/arm/tzc/tzc_common_private.c @@ -28,6 +28,8 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include <arch.h> +#include <arch_helpers.h> #include <mmio.h> #include <tzc_common.h> @@ -199,4 +201,35 @@ static unsigned int _tzc_read_peripheral_id(uintptr_t base) return id; } + +#ifdef AARCH32 +static unsigned long long _tzc_get_max_top_addr(int addr_width) +{ + /* + * Assume at least 32 bit wide address and initialize the max. + * This function doesn't use 64-bit integer arithmetic to avoid + * having to implement additional compiler library functions. + */ + unsigned long long addr_mask = 0xFFFFFFFF; + uint32_t *addr_ptr = (uint32_t *)&addr_mask; + + assert(addr_width >= 32); + + /* This logic works only on little - endian platforms */ + assert((read_sctlr() & SCTLR_EE_BIT) == 0); + + /* + * If required address width is greater than 32, populate the higher + * 32 bits of the 64 bit field with the max address. + */ + if (addr_width > 32) + *(addr_ptr + 1) = ((1 << (addr_width - 32)) - 1); + + return addr_mask; +} +#else +#define _tzc_get_max_top_addr(addr_width)\ + (UINT64_MAX >> (64 - (addr_width))) +#endif /* AARCH32 */ + #endif diff --git a/drivers/arm/tzc/tzc_dmc500.c b/drivers/arm/tzc/tzc_dmc500.c index b2f0bf67..24e587c1 100644 --- a/drivers/arm/tzc/tzc_dmc500.c +++ b/drivers/arm/tzc/tzc_dmc500.c @@ -211,7 +211,7 @@ void tzc_dmc500_configure_region(int region_no, * Do address range check based on DMC-TZ configuration. A 43bit address * is the max and expected case. */ - assert(((region_top <= (UINT64_MAX >> (64 - 43))) && + assert(((region_top <= _tzc_get_max_top_addr(43)) && (region_base < region_top))); /* region_base and (region_top + 1) must be 4KB aligned */ diff --git a/drivers/cadence/uart/aarch64/cdns_console.S b/drivers/cadence/uart/aarch64/cdns_console.S new file mode 100644 index 00000000..2c7960d8 --- /dev/null +++ b/drivers/cadence/uart/aarch64/cdns_console.S @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <arch.h> +#include <asm_macros.S> +#include <cadence/cdns_uart.h> + + .globl console_core_init + .globl console_core_putc + .globl console_core_getc + + /* ----------------------------------------------- + * int console_core_init(unsigned long base_addr, + * unsigned int uart_clk, unsigned int baud_rate) + * Function to initialize the console without a + * C Runtime to print debug information. This + * function will be accessed by console_init and + * crash reporting. + * We assume that the bootloader already set up + * the HW (baud, ...) and only enable the trans- + * mitter and receiver here. + * In: x0 - console base address + * w1 - Uart clock in Hz + * w2 - Baud rate + * Out: return 1 on success else 0 on error + * Clobber list : x1, x2, x3 + * ----------------------------------------------- + */ +func console_core_init + /* Check the input base address */ + cbz x0, core_init_fail + /* Check baud rate and uart clock for sanity */ + cbz w1, core_init_fail + cbz w2, core_init_fail + + /* RX/TX enabled & reset */ + mov w3, #(R_UART_CR_TX_EN | R_UART_CR_RX_EN | R_UART_CR_TXRST | R_UART_CR_RXRST) + str w3, [x0, #R_UART_CR] + + mov w0, #1 + ret +core_init_fail: + mov w0, wzr + ret +endfunc console_core_init + + /* -------------------------------------------------------- + * int console_core_putc(int c, unsigned long base_addr) + * Function to output a character over the console. It + * returns the character printed on success or -1 on error. + * In : w0 - character to be printed + * x1 - console base address + * Out : return -1 on error else return character. + * Clobber list : x2 + * -------------------------------------------------------- + */ +func console_core_putc + /* Check the input parameter */ + cbz x1, putc_error + /* Prepend '\r' to '\n' */ + cmp w0, #0xA + b.ne 2f +1: + /* Check if the transmit FIFO is full */ + ldr w2, [x1, #R_UART_SR] + tbnz w2, #UART_SR_INTR_TFUL_BIT, 1b + mov w2, #0xD + str w2, [x1, #R_UART_TX] +2: + /* Check if the transmit FIFO is full */ + ldr w2, [x1, #R_UART_SR] + tbnz w2, #UART_SR_INTR_TFUL_BIT, 2b + str w0, [x1, #R_UART_TX] + ret +putc_error: + mov w0, #-1 + ret +endfunc console_core_putc + + /* --------------------------------------------- + * int console_core_getc(unsigned long base_addr) + * Function to get a character from the console. + * It returns the character grabbed on success + * or -1 on error. + * In : x0 - console base address + * Clobber list : x0, x1 + * --------------------------------------------- + */ +func console_core_getc + cbz x0, getc_error +1: + /* Check if the receive FIFO is empty */ + ldr w1, [x0, #R_UART_SR] + tbnz w1, #UART_SR_INTR_REMPTY_BIT, 1b + ldr w1, [x0, #R_UART_RX] + mov w0, w1 + ret +getc_error: + mov w0, #-1 + ret +endfunc console_core_getc diff --git a/drivers/cadence/uart/cdns_console.S b/drivers/cadence/uart/cdns_console.S index 2c7960d8..f7278387 100644 --- a/drivers/cadence/uart/cdns_console.S +++ b/drivers/cadence/uart/cdns_console.S @@ -27,101 +27,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -#include <arch.h> -#include <asm_macros.S> -#include <cadence/cdns_uart.h> - .globl console_core_init - .globl console_core_putc - .globl console_core_getc - - /* ----------------------------------------------- - * int console_core_init(unsigned long base_addr, - * unsigned int uart_clk, unsigned int baud_rate) - * Function to initialize the console without a - * C Runtime to print debug information. This - * function will be accessed by console_init and - * crash reporting. - * We assume that the bootloader already set up - * the HW (baud, ...) and only enable the trans- - * mitter and receiver here. - * In: x0 - console base address - * w1 - Uart clock in Hz - * w2 - Baud rate - * Out: return 1 on success else 0 on error - * Clobber list : x1, x2, x3 - * ----------------------------------------------- - */ -func console_core_init - /* Check the input base address */ - cbz x0, core_init_fail - /* Check baud rate and uart clock for sanity */ - cbz w1, core_init_fail - cbz w2, core_init_fail - - /* RX/TX enabled & reset */ - mov w3, #(R_UART_CR_TX_EN | R_UART_CR_RX_EN | R_UART_CR_TXRST | R_UART_CR_RXRST) - str w3, [x0, #R_UART_CR] - - mov w0, #1 - ret -core_init_fail: - mov w0, wzr - ret -endfunc console_core_init - - /* -------------------------------------------------------- - * int console_core_putc(int c, unsigned long base_addr) - * Function to output a character over the console. It - * returns the character printed on success or -1 on error. - * In : w0 - character to be printed - * x1 - console base address - * Out : return -1 on error else return character. - * Clobber list : x2 - * -------------------------------------------------------- - */ -func console_core_putc - /* Check the input parameter */ - cbz x1, putc_error - /* Prepend '\r' to '\n' */ - cmp w0, #0xA - b.ne 2f -1: - /* Check if the transmit FIFO is full */ - ldr w2, [x1, #R_UART_SR] - tbnz w2, #UART_SR_INTR_TFUL_BIT, 1b - mov w2, #0xD - str w2, [x1, #R_UART_TX] -2: - /* Check if the transmit FIFO is full */ - ldr w2, [x1, #R_UART_SR] - tbnz w2, #UART_SR_INTR_TFUL_BIT, 2b - str w0, [x1, #R_UART_TX] - ret -putc_error: - mov w0, #-1 - ret -endfunc console_core_putc - - /* --------------------------------------------- - * int console_core_getc(unsigned long base_addr) - * Function to get a character from the console. - * It returns the character grabbed on success - * or -1 on error. - * In : x0 - console base address - * Clobber list : x0, x1 - * --------------------------------------------- - */ -func console_core_getc - cbz x0, getc_error -1: - /* Check if the receive FIFO is empty */ - ldr w1, [x0, #R_UART_SR] - tbnz w1, #UART_SR_INTR_REMPTY_BIT, 1b - ldr w1, [x0, #R_UART_RX] - mov w0, w1 - ret -getc_error: - mov w0, #-1 - ret -endfunc console_core_getc +#if !ERROR_DEPRECATED +#include "./aarch64/cdns_console.S" +#endif diff --git a/drivers/console/aarch32/console.S b/drivers/console/aarch32/console.S new file mode 100644 index 00000000..29933452 --- /dev/null +++ b/drivers/console/aarch32/console.S @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <asm_macros.S> + + .globl console_init + .globl console_uninit + .globl console_putc + .globl console_getc + + /* + * The console base is in the data section and not in .bss + * even though it is zero-init. In particular, this allows + * the console functions to start using this variable before + * the runtime memory is initialized for images which do not + * need to copy the .data section from ROM to RAM. + */ +.section .data.console_base ; .align 2 + console_base: .word 0x0 + + /* ----------------------------------------------- + * int console_init(uintptr_t base_addr, + * unsigned int uart_clk, unsigned int baud_rate) + * Function to initialize the console without a + * C Runtime to print debug information. It saves + * the console base to the data section. + * In: r0 - console base address + * r1 - Uart clock in Hz + * r2 - Baud rate + * out: return 1 on success else 0 on error + * Clobber list : r1 - r3 + * ----------------------------------------------- + */ +func console_init + /* Check the input base address */ + cmp r0, #0 + beq init_fail + ldr r3, =console_base + str r0, [r3] + b console_core_init +init_fail: + bx lr +endfunc console_init + + /* ----------------------------------------------- + * void console_uninit(void) + * Function to finish the use of console driver. + * It sets the console_base as NULL so that any + * further invocation of `console_putc` or + * `console_getc` APIs would return error. + * ----------------------------------------------- + */ +func console_uninit + mov r0, #0 + ldr r3, =console_base + str r0, [r3] + bx lr +endfunc console_uninit + + /* --------------------------------------------- + * int console_putc(int c) + * Function to output a character over the + * console. It returns the character printed on + * success or -1 on error. + * In : r0 - character to be printed + * Out : return -1 on error else return character. + * Clobber list : r1, r2 + * --------------------------------------------- + */ +func console_putc + ldr r2, =console_base + ldr r1, [r2] + b console_core_putc +endfunc console_putc + + /* --------------------------------------------- + * int console_getc(void) + * Function to get a character from the console. + * It returns the character grabbed on success + * or -1 on error. + * Clobber list : r0, r1 + * --------------------------------------------- + */ +func console_getc + ldr r1, =console_base + ldr r0, [r1] + b console_core_getc +endfunc console_getc diff --git a/drivers/console/aarch32/skeleton_console.S b/drivers/console/aarch32/skeleton_console.S new file mode 100644 index 00000000..383874e6 --- /dev/null +++ b/drivers/console/aarch32/skeleton_console.S @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <asm_macros.S> + + /* + * This file contains a skeleton console implementation that can + * be used as basis for a real console implementation by platforms + * that do not contain PL011 hardware. + */ + + .globl console_core_init + .globl console_core_putc + .globl console_core_getc + + /* ----------------------------------------------- + * int console_core_init(uintptr_t base_addr, + * unsigned int uart_clk, unsigned int baud_rate) + * Function to initialize the console without a + * C Runtime to print debug information. This + * function will be accessed by console_init and + * crash reporting. + * In: r0 - console base address + * r1 - Uart clock in Hz + * r2 - Baud rate + * Out: return 1 on success else 0 on error + * Clobber list : r1, r2 + * ----------------------------------------------- + */ +func console_core_init + /* Check the input base address */ + cmp r0, #0 + beq core_init_fail + /* Check baud rate and uart clock for sanity */ + cmp r1, #0 + beq core_init_fail + cmp r2, #0 + beq core_init_fail + /* Insert implementation here */ + mov r0, #1 + bx lr +core_init_fail: + mov r0, #0 + bx lr +endfunc console_core_init + + /* -------------------------------------------------------- + * int console_core_putc(int c, uintptr_t base_addr) + * Function to output a character over the console. It + * returns the character printed on success or -1 on error. + * In : r0 - character to be printed + * r1 - console base address + * Out : return -1 on error else return character. + * Clobber list : r2 + * -------------------------------------------------------- + */ +func console_core_putc + /* Check the input parameter */ + cmp r1, #0 + beq putc_error + /* Insert implementation here */ + bx lr +putc_error: + mov r0, #-1 + bx lr +endfunc console_core_putc + + /* --------------------------------------------- + * int console_core_getc(uintptr_t base_addr) + * Function to get a character from the console. + * It returns the character grabbed on success + * or -1 on error. + * In : r0 - console base address + * Clobber list : r0, r1 + * --------------------------------------------- + */ +func console_core_getc + cmp r0, #0 + beq getc_error + /* Insert implementation here */ + bx lr +getc_error: + mov r0, #-1 + bx lr +endfunc console_core_getc diff --git a/drivers/console/aarch64/console.S b/drivers/console/aarch64/console.S new file mode 100644 index 00000000..bdd5f4c3 --- /dev/null +++ b/drivers/console/aarch64/console.S @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2015-2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <asm_macros.S> + + .globl console_init + .globl console_uninit + .globl console_putc + .globl console_getc + + /* + * The console base is in the data section and not in .bss + * even though it is zero-init. In particular, this allows + * the console functions to start using this variable before + * the runtime memory is initialized for images which do not + * need to copy the .data section from ROM to RAM. + */ +.section .data.console_base ; .align 3 + console_base: .quad 0x0 + + /* ----------------------------------------------- + * int console_init(uintptr_t base_addr, + * unsigned int uart_clk, unsigned int baud_rate) + * Function to initialize the console without a + * C Runtime to print debug information. It saves + * the console base to the data section. + * In: x0 - console base address + * w1 - Uart clock in Hz + * w2 - Baud rate + * out: return 1 on success else 0 on error + * Clobber list : x1 - x4 + * ----------------------------------------------- + */ +func console_init + /* Check the input base address */ + cbz x0, init_fail + adrp x3, console_base + str x0, [x3, :lo12:console_base] + b console_core_init +init_fail: + ret +endfunc console_init + + /* ----------------------------------------------- + * void console_uninit(void) + * Function to finish the use of console driver. + * It sets the console_base as NULL so that any + * further invocation of `console_putc` or + * `console_getc` APIs would return error. + * ----------------------------------------------- + */ +func console_uninit + mov x0, #0 + adrp x3, console_base + str x0, [x3, :lo12:console_base] + ret +endfunc console_uninit + + /* --------------------------------------------- + * int console_putc(int c) + * Function to output a character over the + * console. It returns the character printed on + * success or -1 on error. + * In : x0 - character to be printed + * Out : return -1 on error else return character. + * Clobber list : x1, x2 + * --------------------------------------------- + */ +func console_putc + adrp x2, console_base + ldr x1, [x2, :lo12:console_base] + b console_core_putc +endfunc console_putc + + /* --------------------------------------------- + * int console_getc(void) + * Function to get a character from the console. + * It returns the character grabbed on success + * or -1 on error. + * Clobber list : x0, x1 + * --------------------------------------------- + */ +func console_getc + adrp x1, console_base + ldr x0, [x1, :lo12:console_base] + b console_core_getc +endfunc console_getc diff --git a/drivers/console/aarch64/skeleton_console.S b/drivers/console/aarch64/skeleton_console.S new file mode 100644 index 00000000..1583ee7d --- /dev/null +++ b/drivers/console/aarch64/skeleton_console.S @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2015-2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <asm_macros.S> + + /* + * This file contains a skeleton console implementation that can + * be used as basis for a real console implementation by platforms + * that do not contain PL011 hardware. + */ + + .globl console_core_init + .globl console_core_putc + .globl console_core_getc + + /* ----------------------------------------------- + * int console_core_init(uintptr_t base_addr, + * unsigned int uart_clk, unsigned int baud_rate) + * Function to initialize the console without a + * C Runtime to print debug information. This + * function will be accessed by console_init and + * crash reporting. + * In: x0 - console base address + * w1 - Uart clock in Hz + * w2 - Baud rate + * Out: return 1 on success else 0 on error + * Clobber list : x1, x2 + * ----------------------------------------------- + */ +func console_core_init + /* Check the input base address */ + cbz x0, core_init_fail + /* Check baud rate and uart clock for sanity */ + cbz w1, core_init_fail + cbz w2, core_init_fail + /* Insert implementation here */ + mov w0, #1 + ret +core_init_fail: + mov w0, wzr + ret +endfunc console_core_init + + /* -------------------------------------------------------- + * int console_core_putc(int c, uintptr_t base_addr) + * Function to output a character over the console. It + * returns the character printed on success or -1 on error. + * In : w0 - character to be printed + * x1 - console base address + * Out : return -1 on error else return character. + * Clobber list : x2 + * -------------------------------------------------------- + */ +func console_core_putc + /* Check the input parameter */ + cbz x1, putc_error + /* Insert implementation here */ + ret +putc_error: + mov w0, #-1 + ret +endfunc console_core_putc + + /* --------------------------------------------- + * int console_core_getc(uintptr_t base_addr) + * Function to get a character from the console. + * It returns the character grabbed on success + * or -1 on error. + * In : x0 - console base address + * Clobber list : x0, x1 + * --------------------------------------------- + */ +func console_core_getc + cbz x0, getc_error + /* Insert implementation here */ + ret +getc_error: + mov w0, #-1 + ret +endfunc console_core_getc diff --git a/drivers/console/console.S b/drivers/console/console.S index 797b5645..e8612985 100644 --- a/drivers/console/console.S +++ b/drivers/console/console.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2016, 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: @@ -27,87 +27,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -#include <asm_macros.S> - .globl console_init - .globl console_uninit - .globl console_putc - .globl console_getc - - /* - * The console base is in the data section and not in .bss - * even though it is zero-init. In particular, this allows - * the console functions to start using this variable before - * the runtime memory is initialized for images which do not - * need to copy the .data section from ROM to RAM. - */ -.section .data.console_base ; .align 3 - console_base: .quad 0x0 - - /* ----------------------------------------------- - * int console_init(uintptr_t base_addr, - * unsigned int uart_clk, unsigned int baud_rate) - * Function to initialize the console without a - * C Runtime to print debug information. It saves - * the console base to the data section. - * In: x0 - console base address - * w1 - Uart clock in Hz - * w2 - Baud rate - * out: return 1 on success else 0 on error - * Clobber list : x1 - x4 - * ----------------------------------------------- - */ -func console_init - /* Check the input base address */ - cbz x0, init_fail - adrp x3, console_base - str x0, [x3, :lo12:console_base] - b console_core_init -init_fail: - ret -endfunc console_init - - /* ----------------------------------------------- - * void console_uninit(void) - * Function to finish the use of console driver. - * It sets the console_base as NULL so that any - * further invocation of `console_putc` or - * `console_getc` APIs would return error. - * ----------------------------------------------- - */ -func console_uninit - mov x0, #0 - adrp x3, console_base - str x0, [x3, :lo12:console_base] - ret -endfunc console_uninit - - /* --------------------------------------------- - * int console_putc(int c) - * Function to output a character over the - * console. It returns the character printed on - * success or -1 on error. - * In : x0 - character to be printed - * Out : return -1 on error else return character. - * Clobber list : x1, x2 - * --------------------------------------------- - */ -func console_putc - adrp x2, console_base - ldr x1, [x2, :lo12:console_base] - b console_core_putc -endfunc console_putc - - /* --------------------------------------------- - * int console_getc(void) - * Function to get a character from the console. - * It returns the character grabbed on success - * or -1 on error. - * Clobber list : x0, x1 - * --------------------------------------------- - */ -func console_getc - adrp x1, console_base - ldr x0, [x1, :lo12:console_base] - b console_core_getc -endfunc console_getc +#if !ERROR_DEPRECATED +#include "./aarch64/console.S" +#endif diff --git a/drivers/console/skeleton_console.S b/drivers/console/skeleton_console.S index 083d3c70..ddfd8348 100644 --- a/drivers/console/skeleton_console.S +++ b/drivers/console/skeleton_console.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2016, 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: @@ -27,80 +27,7 @@ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ -#include <asm_macros.S> - /* - * This file contains a skeleton console implementation that can - * be used as basis for a real console implementation by platforms - * that do not contain PL011 hardware. - */ - - .globl console_core_init - .globl console_core_putc - .globl console_core_getc - - /* ----------------------------------------------- - * int console_core_init(uintptr_t base_addr, - * unsigned int uart_clk, unsigned int baud_rate) - * Function to initialize the console without a - * C Runtime to print debug information. This - * function will be accessed by console_init and - * crash reporting. - * In: x0 - console base address - * w1 - Uart clock in Hz - * w2 - Baud rate - * Out: return 1 on success else 0 on error - * Clobber list : x1, x2 - * ----------------------------------------------- - */ -func console_core_init - /* Check the input base address */ - cbz x0, core_init_fail - /* Check baud rate and uart clock for sanity */ - cbz w1, core_init_fail - cbz w2, core_init_fail - /* Insert implementation here */ - mov w0, #1 - ret -core_init_fail: - mov w0, wzr - ret -endfunc console_core_init - - /* -------------------------------------------------------- - * int console_core_putc(int c, uintptr_t base_addr) - * Function to output a character over the console. It - * returns the character printed on success or -1 on error. - * In : w0 - character to be printed - * x1 - console base address - * Out : return -1 on error else return character. - * Clobber list : x2 - * -------------------------------------------------------- - */ -func console_core_putc - /* Check the input parameter */ - cbz x1, putc_error - /* Insert implementation here */ - ret -putc_error: - mov w0, #-1 - ret -endfunc console_core_putc - - /* --------------------------------------------- - * int console_core_getc(uintptr_t base_addr) - * Function to get a character from the console. - * It returns the character grabbed on success - * or -1 on error. - * In : x0 - console base address - * Clobber list : x0, x1 - * --------------------------------------------- - */ -func console_core_getc - cbz x0, getc_error - /* Insert implementation here */ - ret -getc_error: - mov w0, #-1 - ret -endfunc console_core_getc +#if !ERROR_DEPRECATED +#include "./aarch64/skeleton_console.S" +#endif diff --git a/drivers/ti/uart/16550_console.S b/drivers/ti/uart/16550_console.S index ebb46151..90b12e5f 100644 --- a/drivers/ti/uart/16550_console.S +++ b/drivers/ti/uart/16550_console.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2016, 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,128 +28,6 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include <arch.h> -#include <asm_macros.S> -#include <uart_16550.h> - - .globl console_core_init - .globl console_core_putc - .globl console_core_getc - - /* ----------------------------------------------- - * int console_core_init(unsigned long base_addr, - * unsigned int uart_clk, unsigned int baud_rate) - * Function to initialize the console without a - * C Runtime to print debug information. This - * function will be accessed by console_init and - * crash reporting. - * In: x0 - console base address - * w1 - Uart clock in Hz - * w2 - Baud rate - * Out: return 1 on success - * Clobber list : x1, x2, x3 - * ----------------------------------------------- - */ -func console_core_init - /* Check the input base address */ - cbz x0, init_fail - /* Check baud rate and uart clock for sanity */ - cbz w1, init_fail - cbz w2, init_fail - - /* Program the baudrate */ - /* Divisor = Uart clock / (16 * baudrate) */ - lsl w2, w2, #4 - udiv w2, w1, w2 - and w1, w2, #0xff /* w1 = DLL */ - lsr w2, w2, #8 - and w2, w2, #0xff /* w2 = DLLM */ - ldr w3, [x0, #UARTLCR] - orr w3, w3, #UARTLCR_DLAB - str w3, [x0, #UARTLCR] /* enable DLL, DLLM programming */ - str w1, [x0, #UARTDLL] /* program DLL */ - str w2, [x0, #UARTDLLM] /* program DLLM */ - mov w2, #~UARTLCR_DLAB - and w3, w3, w2 - str w3, [x0, #UARTLCR] /* disable DLL, DLLM programming */ - - /* 8n1 */ - mov w3, #3 - str w3, [x0, #UARTLCR] - /* no interrupt */ - mov w3, #0 - str w3, [x0, #UARTIER] - /* enable fifo, DMA */ - mov w3, #(UARTFCR_FIFOEN | UARTFCR_DMAEN) - str w3, [x0, #UARTFCR] - /* DTR + RTS */ - mov w3, #3 - str w3, [x0, #UARTMCR] - mov w0, #1 -init_fail: - ret -endfunc console_core_init - - /* -------------------------------------------------------- - * int console_core_putc(int c, unsigned int base_addr) - * Function to output a character over the console. It - * returns the character printed on success or -1 on error. - * In : w0 - character to be printed - * x1 - console base address - * Out : return -1 on error else return character. - * Clobber list : x2 - * -------------------------------------------------------- - */ -func console_core_putc - /* Check the input parameter */ - cbz x1, putc_error - - /* Prepend '\r' to '\n' */ - cmp w0, #0xA - b.ne 2f - /* Check if the transmit FIFO is full */ -1: ldr w2, [x1, #UARTLSR] - and w2, w2, #(UARTLSR_TEMT | UARTLSR_THRE) - cmp w2, #(UARTLSR_TEMT | UARTLSR_THRE) - b.ne 1b - mov w2, #0xD /* '\r' */ - str w2, [x1, #UARTTX] - ldr w2, [x1, #UARTFCR] - orr w2, w2, #UARTFCR_TXCLR - str w2, [x1, #UARTFCR] - - /* Check if the transmit FIFO is full */ -2: ldr w2, [x1, #UARTLSR] - and w2, w2, #(UARTLSR_TEMT | UARTLSR_THRE) - cmp w2, #(UARTLSR_TEMT | UARTLSR_THRE) - b.ne 2b - str w0, [x1, #UARTTX] - ldr w2, [x1, #UARTFCR] - orr w2, w2, #UARTFCR_TXCLR - str w2, [x1, #UARTFCR] - ret -putc_error: - mov w0, #-1 - ret -endfunc console_core_putc - - /* --------------------------------------------- - * int console_core_getc(void) - * Function to get a character from the console. - * It returns the character grabbed on success - * or -1 on error. - * In : w0 - console base address - * Out : return -1 on error else return character. - * Clobber list : x0, x1 - * --------------------------------------------- - */ -func console_core_getc - /* Check if the receive FIFO is empty */ -1: ldr w1, [x0, #UARTLSR] - tbz w1, #UARTLSR_RDR, 1b - ldr w0, [x0, #UARTRX] - ret -getc_error: - mov w0, #-1 - ret -endfunc console_core_getc +#if !ERROR_DEPRECATED +#include "./aarch64/16550_console.S" +#endif diff --git a/drivers/ti/uart/aarch64/16550_console.S b/drivers/ti/uart/aarch64/16550_console.S new file mode 100644 index 00000000..05353814 --- /dev/null +++ b/drivers/ti/uart/aarch64/16550_console.S @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2015-2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <arch.h> +#include <asm_macros.S> +#include <uart_16550.h> + + .globl console_core_init + .globl console_core_putc + .globl console_core_getc + + /* ----------------------------------------------- + * int console_core_init(unsigned long base_addr, + * unsigned int uart_clk, unsigned int baud_rate) + * Function to initialize the console without a + * C Runtime to print debug information. This + * function will be accessed by console_init and + * crash reporting. + * In: x0 - console base address + * w1 - Uart clock in Hz + * w2 - Baud rate + * Out: return 1 on success + * Clobber list : x1, x2, x3 + * ----------------------------------------------- + */ +func console_core_init + /* Check the input base address */ + cbz x0, init_fail + /* Check baud rate and uart clock for sanity */ + cbz w1, init_fail + cbz w2, init_fail + + /* Program the baudrate */ + /* Divisor = Uart clock / (16 * baudrate) */ + lsl w2, w2, #4 + udiv w2, w1, w2 + and w1, w2, #0xff /* w1 = DLL */ + lsr w2, w2, #8 + and w2, w2, #0xff /* w2 = DLLM */ + ldr w3, [x0, #UARTLCR] + orr w3, w3, #UARTLCR_DLAB + str w3, [x0, #UARTLCR] /* enable DLL, DLLM programming */ + str w1, [x0, #UARTDLL] /* program DLL */ + str w2, [x0, #UARTDLLM] /* program DLLM */ + mov w2, #~UARTLCR_DLAB + and w3, w3, w2 + str w3, [x0, #UARTLCR] /* disable DLL, DLLM programming */ + + /* 8n1 */ + mov w3, #3 + str w3, [x0, #UARTLCR] + /* no interrupt */ + mov w3, #0 + str w3, [x0, #UARTIER] + /* enable fifo, DMA */ + mov w3, #(UARTFCR_FIFOEN | UARTFCR_DMAEN) + str w3, [x0, #UARTFCR] + /* DTR + RTS */ + mov w3, #3 + str w3, [x0, #UARTMCR] + mov w0, #1 +init_fail: + ret +endfunc console_core_init + + /* -------------------------------------------------------- + * int console_core_putc(int c, unsigned int base_addr) + * Function to output a character over the console. It + * returns the character printed on success or -1 on error. + * In : w0 - character to be printed + * x1 - console base address + * Out : return -1 on error else return character. + * Clobber list : x2 + * -------------------------------------------------------- + */ +func console_core_putc + /* Check the input parameter */ + cbz x1, putc_error + + /* Prepend '\r' to '\n' */ + cmp w0, #0xA + b.ne 2f + /* Check if the transmit FIFO is full */ +1: ldr w2, [x1, #UARTLSR] + and w2, w2, #(UARTLSR_TEMT | UARTLSR_THRE) + cmp w2, #(UARTLSR_TEMT | UARTLSR_THRE) + b.ne 1b + mov w2, #0xD /* '\r' */ + str w2, [x1, #UARTTX] + ldr w2, [x1, #UARTFCR] + orr w2, w2, #UARTFCR_TXCLR + str w2, [x1, #UARTFCR] + + /* Check if the transmit FIFO is full */ +2: ldr w2, [x1, #UARTLSR] + and w2, w2, #(UARTLSR_TEMT | UARTLSR_THRE) + cmp w2, #(UARTLSR_TEMT | UARTLSR_THRE) + b.ne 2b + str w0, [x1, #UARTTX] + ldr w2, [x1, #UARTFCR] + orr w2, w2, #UARTFCR_TXCLR + str w2, [x1, #UARTFCR] + ret +putc_error: + mov w0, #-1 + ret +endfunc console_core_putc + + /* --------------------------------------------- + * int console_core_getc(void) + * Function to get a character from the console. + * It returns the character grabbed on success + * or -1 on error. + * In : w0 - console base address + * Out : return -1 on error else return character. + * Clobber list : x0, x1 + * --------------------------------------------- + */ +func console_core_getc + /* Check if the receive FIFO is empty */ +1: ldr w1, [x0, #UARTLSR] + tbz w1, #UARTLSR_RDR, 1b + ldr w0, [x0, #UARTRX] + ret +getc_error: + mov w0, #-1 + ret +endfunc console_core_getc diff --git a/include/bl32/sp_min/platform_sp_min.h b/include/bl32/sp_min/platform_sp_min.h new file mode 100644 index 00000000..ae9dd58a --- /dev/null +++ b/include/bl32/sp_min/platform_sp_min.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __PLATFORM_SP_MIN_H__ +#define __PLATFORM_SP_MIN_H__ + +/******************************************************************************* + * Mandatory SP_MIN functions + ******************************************************************************/ +void sp_min_early_platform_setup(void); +void sp_min_plat_arch_setup(void); +void sp_min_platform_setup(void); +entry_point_info_t *sp_min_plat_get_bl33_ep_info(void); + +#endif /* __PLATFORM_SP_MIN_H__ */ diff --git a/include/common/aarch32/asm_macros.S b/include/common/aarch32/asm_macros.S new file mode 100644 index 00000000..11e45bbf --- /dev/null +++ b/include/common/aarch32/asm_macros.S @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __ASM_MACROS_S__ +#define __ASM_MACROS_S__ + +#include <arch.h> +#include <asm_macros_common.S> + +#define WORD_SIZE 4 + + /* + * Co processor register accessors + */ + .macro ldcopr reg, coproc, opc1, CRn, CRm, opc2 + mrc \coproc, \opc1, \reg, \CRn, \CRm, \opc2 + .endm + + .macro ldcopr16 reg1, reg2, coproc, opc1, CRm + mrrc \coproc, \opc1, \reg1, \reg2, \CRm + .endm + + .macro stcopr reg, coproc, opc1, CRn, CRm, opc2 + mcr \coproc, \opc1, \reg, \CRn, \CRm, \opc2 + .endm + + .macro stcopr16 reg1, reg2, coproc, opc1, CRm + mcrr \coproc, \opc1, \reg1, \reg2, \CRm + .endm + + /* Cache line size helpers */ + .macro dcache_line_size reg, tmp + ldcopr \tmp, CTR + ubfx \tmp, \tmp, #CTR_DMINLINE_SHIFT, #CTR_DMINLINE_WIDTH + mov \reg, #WORD_SIZE + lsl \reg, \reg, \tmp + .endm + + .macro icache_line_size reg, tmp + ldcopr \tmp, CTR + and \tmp, \tmp, #CTR_IMINLINE_MASK + mov \reg, #WORD_SIZE + lsl \reg, \reg, \tmp + .endm + + /* + * This macro calculates the base address of the current CPU's multi + * processor(MP) stack using the plat_my_core_pos() index, the name of + * the stack storage and the size of each stack. + * Out: r0 = physical address of stack base + * Clobber: r14, r1, r2 + */ + .macro get_my_mp_stack _name, _size + bl plat_my_core_pos + ldr r2, =(\_name + \_size) + mov r1, #\_size + mla r0, r0, r1, r2 + .endm + + /* + * This macro calculates the base address of a uniprocessor(UP) stack + * using the name of the stack storage and the size of the stack + * Out: r0 = physical address of stack base + */ + .macro get_up_stack _name, _size + ldr r0, =(\_name + \_size) + .endm + +#endif /* __ASM_MACROS_S__ */ diff --git a/include/common/aarch32/assert_macros.S b/include/common/aarch32/assert_macros.S new file mode 100644 index 00000000..f35fc6af --- /dev/null +++ b/include/common/aarch32/assert_macros.S @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __ASSERT_MACROS_S__ +#define __ASSERT_MACROS_S__ + + /* + * Assembler macro to enable asm_assert. We assume that the stack is + * initialized prior to invoking this macro. + */ +#define ASM_ASSERT(_cc) \ +.ifndef .L_assert_filename ;\ + .pushsection .rodata.str1.1, "aS" ;\ + .L_assert_filename: ;\ + .string __FILE__ ;\ + .popsection ;\ +.endif ;\ + b##_cc 300f ;\ + ldr r0, =.L_assert_filename ;\ + mov r1, #__LINE__ ;\ + b . ;\ +300: + +#endif /* __ASSERT_MACROS_S__ */ diff --git a/include/common/asm_macros_common.S b/include/common/asm_macros_common.S index ee59a939..023124b3 100644 --- a/include/common/asm_macros_common.S +++ b/include/common/asm_macros_common.S @@ -30,8 +30,6 @@ #ifndef __ASM_MACROS_COMMON_S__ #define __ASM_MACROS_COMMON_S__ -#include <arch.h> - /* * This macro is used to create a function label and place the * code into a separate text section based on the function name diff --git a/include/common/bl_common.h b/include/common/bl_common.h index 3aa08360..942843cf 100644 --- a/include/common/bl_common.h +++ b/include/common/bl_common.h @@ -50,7 +50,11 @@ * 'entry_point_info' structure at their correct offsets. ******************************************************************************/ #define ENTRY_POINT_INFO_PC_OFFSET 0x08 +#ifdef AARCH32 +#define ENTRY_POINT_INFO_ARGS_OFFSET 0x10 +#else #define ENTRY_POINT_INFO_ARGS_OFFSET 0x18 +#endif /* The following are used to set/get image attributes. */ #define PARAM_EP_SECURITY_MASK (0x1) @@ -192,6 +196,13 @@ typedef struct aapcs64_params { u_register_t arg7; } aapcs64_params_t; +typedef struct aapcs32_params { + u_register_t arg0; + u_register_t arg1; + u_register_t arg2; + u_register_t arg3; +} aapcs32_params_t; + /*************************************************************************** * This structure provides version information and the size of the * structure, attributes for the structure it represents @@ -216,7 +227,11 @@ typedef struct entry_point_info { param_header_t h; uintptr_t pc; uint32_t spsr; +#ifdef AARCH32 + aapcs32_params_t args; +#else aapcs64_params_t args; +#endif } entry_point_info_t; /***************************************************************************** diff --git a/include/common/runtime_svc.h b/include/common/runtime_svc.h index adafcee4..514f334a 100644 --- a/include/common/runtime_svc.h +++ b/include/common/runtime_svc.h @@ -43,10 +43,17 @@ * Constants to allow the assembler access a runtime service * descriptor */ +#ifdef AARCH32 +#define RT_SVC_SIZE_LOG2 4 +#define RT_SVC_DESC_INIT 8 +#define RT_SVC_DESC_HANDLE 12 +#else #define RT_SVC_SIZE_LOG2 5 -#define SIZEOF_RT_SVC_DESC (1 << RT_SVC_SIZE_LOG2) #define RT_SVC_DESC_INIT 16 #define RT_SVC_DESC_HANDLE 24 +#endif /* AARCH32 */ +#define SIZEOF_RT_SVC_DESC (1 << RT_SVC_SIZE_LOG2) + /* * The function identifier has 6 bits for the owning entity number and @@ -123,10 +130,22 @@ CASSERT(RT_SVC_DESC_HANDLE == __builtin_offsetof(rt_svc_desc_t, handle), \ ((call_type & FUNCID_TYPE_MASK) \ << FUNCID_OEN_WIDTH)) +/* + * This macro generates the unique owning entity number from the SMC Function + * ID. This unique oen is used to access an entry in the + * 'rt_svc_descs_indices' array to invoke the corresponding runtime service + * handler during SMC handling. + */ +#define get_unique_oen_from_smc_fid(fid) \ + get_unique_oen(((fid) >> FUNCID_OEN_SHIFT), \ + ((fid) >> FUNCID_TYPE_SHIFT)) + /******************************************************************************* * Function & variable prototypes ******************************************************************************/ void runtime_svc_init(void); +uintptr_t handle_runtime_svc(uint32_t smc_fid, void *cookie, void *handle, + unsigned int flags); extern uintptr_t __RT_SVC_DESCS_START__; extern uintptr_t __RT_SVC_DESCS_END__; void init_crash_reporting(void); diff --git a/include/lib/aarch32/arch.h b/include/lib/aarch32/arch.h new file mode 100644 index 00000000..6653cd14 --- /dev/null +++ b/include/lib/aarch32/arch.h @@ -0,0 +1,410 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __ARCH_H__ +#define __ARCH_H__ + +/******************************************************************************* + * MIDR bit definitions + ******************************************************************************/ +#define MIDR_IMPL_MASK 0xff +#define MIDR_IMPL_SHIFT 24 +#define MIDR_VAR_SHIFT 20 +#define MIDR_VAR_BITS 4 +#define MIDR_REV_SHIFT 0 +#define MIDR_REV_BITS 4 +#define MIDR_PN_MASK 0xfff +#define MIDR_PN_SHIFT 4 + +/******************************************************************************* + * MPIDR macros + ******************************************************************************/ +#define MPIDR_CPU_MASK MPIDR_AFFLVL_MASK +#define MPIDR_CLUSTER_MASK (MPIDR_AFFLVL_MASK << MPIDR_AFFINITY_BITS) +#define MPIDR_AFFINITY_BITS 8 +#define MPIDR_AFFLVL_MASK 0xff +#define MPIDR_AFFLVL_SHIFT 3 +#define MPIDR_AFF0_SHIFT 0 +#define MPIDR_AFF1_SHIFT 8 +#define MPIDR_AFF2_SHIFT 16 +#define MPIDR_AFFINITY_MASK 0x00ffffff +#define MPIDR_AFFLVL0 0 +#define MPIDR_AFFLVL1 1 +#define MPIDR_AFFLVL2 2 + +#define MPIDR_AFFLVL0_VAL(mpidr) \ + (((mpidr) >> MPIDR_AFF0_SHIFT) & MPIDR_AFFLVL_MASK) +#define MPIDR_AFFLVL1_VAL(mpidr) \ + (((mpidr) >> MPIDR_AFF1_SHIFT) & MPIDR_AFFLVL_MASK) +#define MPIDR_AFFLVL2_VAL(mpidr) \ + (((mpidr) >> MPIDR_AFF2_SHIFT) & MPIDR_AFFLVL_MASK) + +/* + * The MPIDR_MAX_AFFLVL count starts from 0. Take care to + * add one while using this macro to define array sizes. + */ +#define MPIDR_MAX_AFFLVL 2 + +/* Data Cache set/way op type defines */ +#define DC_OP_ISW 0x0 +#define DC_OP_CISW 0x1 +#define DC_OP_CSW 0x2 + +/******************************************************************************* + * Generic timer memory mapped registers & offsets + ******************************************************************************/ +#define CNTCR_OFF 0x000 +#define CNTFID_OFF 0x020 + +#define CNTCR_EN (1 << 0) +#define CNTCR_HDBG (1 << 1) +#define CNTCR_FCREQ(x) ((x) << 8) + +/******************************************************************************* + * System register bit definitions + ******************************************************************************/ +/* CLIDR definitions */ +#define LOUIS_SHIFT 21 +#define LOC_SHIFT 24 +#define CLIDR_FIELD_WIDTH 3 + +/* CSSELR definitions */ +#define LEVEL_SHIFT 1 + +/* ID_PFR1 definitions */ +#define ID_PFR1_VIRTEXT_SHIFT 12 +#define ID_PFR1_VIRTEXT_MASK 0xf +#define GET_VIRT_EXT(id) (((id) >> ID_PFR1_VIRTEXT_SHIFT) \ + & ID_PFR1_VIRTEXT_MASK) +#define ID_PFR1_GIC_SHIFT 28 +#define ID_PFR1_GIC_MASK 0xf + +/* SCTLR definitions */ +#define SCTLR_RES1 ((1 << 23) | (1 << 22) | (1 << 11) | (1 << 4) | \ + (1 << 3) | SCTLR_CP15BEN_BIT | SCTLR_NTWI_BIT | SCTLR_NTWE_BIT) +#define SCTLR_M_BIT (1 << 0) +#define SCTLR_A_BIT (1 << 1) +#define SCTLR_C_BIT (1 << 2) +#define SCTLR_CP15BEN_BIT (1 << 5) +#define SCTLR_ITD_BIT (1 << 7) +#define SCTLR_I_BIT (1 << 12) +#define SCTLR_V_BIT (1 << 13) +#define SCTLR_NTWI_BIT (1 << 16) +#define SCTLR_NTWE_BIT (1 << 18) +#define SCTLR_WXN_BIT (1 << 19) +#define SCTLR_UWXN_BIT (1 << 20) +#define SCTLR_EE_BIT (1 << 25) +#define SCTLR_TRE_BIT (1 << 28) +#define SCTLR_AFE_BIT (1 << 29) +#define SCTLR_TE_BIT (1 << 30) + +/* HSCTLR definitions */ +#define HSCTLR_RES1 ((1 << 29) | (1 << 28) | (1 << 23) | (1 << 22) \ + | (1 << 18) | (1 << 16) | (1 << 11) | (1 << 4) \ + | (1 << 3) | HSCTLR_CP15BEN_BIT) +#define HSCTLR_M_BIT (1 << 0) +#define HSCTLR_A_BIT (1 << 1) +#define HSCTLR_C_BIT (1 << 2) +#define HSCTLR_CP15BEN_BIT (1 << 5) +#define HSCTLR_ITD_BIT (1 << 7) +#define HSCTLR_SED_BIT (1 << 8) +#define HSCTLR_I_BIT (1 << 12) +#define HSCTLR_WXN_BIT (1 << 19) +#define HSCTLR_EE_BIT (1 << 25) +#define HSCTLR_TE_BIT (1 << 30) + +/* CPACR definitions */ +#define CPACR_FPEN(x) ((x) << 20) +#define CPACR_FP_TRAP_PL0 0x1 +#define CPACR_FP_TRAP_ALL 0x2 +#define CPACR_FP_TRAP_NONE 0x3 + +/* SCR definitions */ +#define SCR_TWE_BIT (1 << 13) +#define SCR_TWI_BIT (1 << 12) +#define SCR_SIF_BIT (1 << 9) +#define SCR_HCE_BIT (1 << 8) +#define SCR_SCD_BIT (1 << 7) +#define SCR_NET_BIT (1 << 6) +#define SCR_AW_BIT (1 << 5) +#define SCR_FW_BIT (1 << 4) +#define SCR_EA_BIT (1 << 3) +#define SCR_FIQ_BIT (1 << 2) +#define SCR_IRQ_BIT (1 << 1) +#define SCR_NS_BIT (1 << 0) +#define SCR_VALID_BIT_MASK 0x33ff + +#define GET_NS_BIT(scr) ((scr) & SCR_NS_BIT) + +/* HCR definitions */ +#define HCR_AMO_BIT (1 << 5) +#define HCR_IMO_BIT (1 << 4) +#define HCR_FMO_BIT (1 << 3) + +/* CNTHCTL definitions */ +#define EVNTEN_BIT (1 << 2) +#define PL1PCEN_BIT (1 << 1) +#define PL1PCTEN_BIT (1 << 0) + +/* CNTKCTL definitions */ +#define PL0PTEN_BIT (1 << 9) +#define PL0VTEN_BIT (1 << 8) +#define PL0PCTEN_BIT (1 << 0) +#define PL0VCTEN_BIT (1 << 1) +#define EVNTEN_BIT (1 << 2) +#define EVNTDIR_BIT (1 << 3) +#define EVNTI_SHIFT 4 +#define EVNTI_MASK 0xf + +/* HCPTR definitions */ +#define TCPAC_BIT (1 << 31) +#define TTA_BIT (1 << 20) +#define TCP11_BIT (1 << 10) +#define TCP10_BIT (1 << 10) + +/* NASCR definitions */ +#define NSASEDIS_BIT (1 << 15) +#define NASCR_CP11_BIT (1 << 11) +#define NASCR_CP10_BIT (1 << 10) + +/* CPACR definitions */ +#define ASEDIS_BIT (1 << 31) +#define TRCDIS_BIT (1 << 28) +#define CPACR_CP11_SHIFT 22 +#define CPACR_CP10_SHIFT 20 +#define CPACR_ENABLE_FP_ACCESS (0x3 << CPACR_CP11_SHIFT |\ + 0x3 << CPACR_CP10_SHIFT) + +/* FPEXC definitions */ +#define FPEXC_EN_BIT (1 << 30) + +/* SPSR/CPSR definitions */ +#define SPSR_FIQ_BIT (1 << 0) +#define SPSR_IRQ_BIT (1 << 1) +#define SPSR_ABT_BIT (1 << 2) +#define SPSR_AIF_SHIFT 6 +#define SPSR_AIF_MASK 0x7 + +#define SPSR_E_SHIFT 9 +#define SPSR_E_MASK 0x1 +#define SPSR_E_LITTLE 0 +#define SPSR_E_BIG 1 + +#define SPSR_T_SHIFT 5 +#define SPSR_T_MASK 0x1 +#define SPSR_T_ARM 0 +#define SPSR_T_THUMB 1 + +#define SPSR_MODE_SHIFT 0 +#define SPSR_MODE_MASK 0x7 + + +#define DISABLE_ALL_EXCEPTIONS \ + (SPSR_FIQ_BIT | SPSR_IRQ_BIT | SPSR_ABT_BIT) + +/* + * TTBCR definitions + */ +/* The ARM Trusted Firmware uses the long descriptor format */ +#define TTBCR_EAE_BIT (1 << 31) + +#define TTBCR_SH1_NON_SHAREABLE (0x0 << 28) +#define TTBCR_SH1_OUTER_SHAREABLE (0x2 << 28) +#define TTBCR_SH1_INNER_SHAREABLE (0x3 << 28) + +#define TTBCR_RGN1_OUTER_NC (0x0 << 26) +#define TTBCR_RGN1_OUTER_WBA (0x1 << 26) +#define TTBCR_RGN1_OUTER_WT (0x2 << 26) +#define TTBCR_RGN1_OUTER_WBNA (0x3 << 26) + +#define TTBCR_RGN1_INNER_NC (0x0 << 24) +#define TTBCR_RGN1_INNER_WBA (0x1 << 24) +#define TTBCR_RGN1_INNER_WT (0x2 << 24) +#define TTBCR_RGN1_INNER_WBNA (0x3 << 24) + +#define TTBCR_EPD1_BIT (1 << 23) +#define TTBCR_A1_BIT (1 << 22) + +#define TTBCR_T1SZ_SHIFT 16 +#define TTBCR_T1SZ_MASK (0x7) +#define TTBCR_TxSZ_MIN 0 +#define TTBCR_TxSZ_MAX 7 + +#define TTBCR_SH0_NON_SHAREABLE (0x0 << 12) +#define TTBCR_SH0_OUTER_SHAREABLE (0x2 << 12) +#define TTBCR_SH0_INNER_SHAREABLE (0x3 << 12) + +#define TTBCR_RGN0_OUTER_NC (0x0 << 10) +#define TTBCR_RGN0_OUTER_WBA (0x1 << 10) +#define TTBCR_RGN0_OUTER_WT (0x2 << 10) +#define TTBCR_RGN0_OUTER_WBNA (0x3 << 10) + +#define TTBCR_RGN0_INNER_NC (0x0 << 8) +#define TTBCR_RGN0_INNER_WBA (0x1 << 8) +#define TTBCR_RGN0_INNER_WT (0x2 << 8) +#define TTBCR_RGN0_INNER_WBNA (0x3 << 8) + +#define TTBCR_EPD0_BIT (1 << 7) +#define TTBCR_T0SZ_SHIFT 0 +#define TTBCR_T0SZ_MASK (0x7) + +#define MODE_RW_SHIFT 0x4 +#define MODE_RW_MASK 0x1 +#define MODE_RW_32 0x1 + +#define MODE32_SHIFT 0 +#define MODE32_MASK 0x1f +#define MODE32_usr 0x10 +#define MODE32_fiq 0x11 +#define MODE32_irq 0x12 +#define MODE32_svc 0x13 +#define MODE32_mon 0x16 +#define MODE32_abt 0x17 +#define MODE32_hyp 0x1a +#define MODE32_und 0x1b +#define MODE32_sys 0x1f + +#define GET_M32(mode) (((mode) >> MODE32_SHIFT) & MODE32_MASK) + +#define SPSR_MODE32(mode, isa, endian, aif) \ + (MODE_RW_32 << MODE_RW_SHIFT | \ + ((mode) & MODE32_MASK) << MODE32_SHIFT | \ + ((isa) & SPSR_T_MASK) << SPSR_T_SHIFT | \ + ((endian) & SPSR_E_MASK) << SPSR_E_SHIFT | \ + ((aif) & SPSR_AIF_MASK) << SPSR_AIF_SHIFT) + +/* + * CTR definitions + */ +#define CTR_CWG_SHIFT 24 +#define CTR_CWG_MASK 0xf +#define CTR_ERG_SHIFT 20 +#define CTR_ERG_MASK 0xf +#define CTR_DMINLINE_SHIFT 16 +#define CTR_DMINLINE_WIDTH 4 +#define CTR_DMINLINE_MASK ((1 << 4) - 1) +#define CTR_L1IP_SHIFT 14 +#define CTR_L1IP_MASK 0x3 +#define CTR_IMINLINE_SHIFT 0 +#define CTR_IMINLINE_MASK 0xf + +#define MAX_CACHE_LINE_SIZE 0x800 /* 2KB */ + +/******************************************************************************* + * Definitions of register offsets and fields in the CNTCTLBase Frame of the + * system level implementation of the Generic Timer. + ******************************************************************************/ +#define CNTNSAR 0x4 +#define CNTNSAR_NS_SHIFT(x) (x) + +#define CNTACR_BASE(x) (0x40 + ((x) << 2)) +#define CNTACR_RPCT_SHIFT 0x0 +#define CNTACR_RVCT_SHIFT 0x1 +#define CNTACR_RFRQ_SHIFT 0x2 +#define CNTACR_RVOFF_SHIFT 0x3 +#define CNTACR_RWVT_SHIFT 0x4 +#define CNTACR_RWPT_SHIFT 0x5 + +/* MAIR macros */ +#define MAIR0_ATTR_SET(attr, index) ((attr) << ((index) << 3)) +#define MAIR1_ATTR_SET(attr, index) ((attr) << (((index) - 3) << 3)) + +/* System register defines The format is: coproc, opt1, CRn, CRm, opt2 */ +#define SCR p15, 0, c1, c1, 0 +#define SCTLR p15, 0, c1, c0, 0 +#define MPIDR p15, 0, c0, c0, 5 +#define MIDR p15, 0, c0, c0, 0 +#define VBAR p15, 0, c12, c0, 0 +#define MVBAR p15, 0, c12, c0, 1 +#define NSACR p15, 0, c1, c1, 2 +#define CPACR p15, 0, c1, c0, 2 +#define DCCIMVAC p15, 0, c7, c14, 1 +#define DCCMVAC p15, 0, c7, c10, 1 +#define DCIMVAC p15, 0, c7, c6, 1 +#define DCCISW p15, 0, c7, c14, 2 +#define DCCSW p15, 0, c7, c10, 2 +#define DCISW p15, 0, c7, c6, 2 +#define CTR p15, 0, c0, c0, 1 +#define CNTFRQ p15, 0, c14, c0, 0 +#define ID_PFR1 p15, 0, c0, c1, 1 +#define MAIR0 p15, 0, c10, c2, 0 +#define MAIR1 p15, 0, c10, c2, 1 +#define TTBCR p15, 0, c2, c0, 2 +#define TTBR0 p15, 0, c2, c0, 0 +#define TTBR1 p15, 0, c2, c0, 1 +#define TLBIALL p15, 0, c8, c7, 0 +#define TLBIALLIS p15, 0, c8, c3, 0 +#define TLBIMVA p15, 0, c8, c7, 1 +#define TLBIMVAA p15, 0, c8, c7, 3 +#define HSCTLR p15, 4, c1, c0, 0 +#define HCR p15, 4, c1, c1, 0 +#define HCPTR p15, 4, c1, c1, 2 +#define CNTHCTL p15, 4, c14, c1, 0 +#define VPIDR p15, 4, c0, c0, 0 +#define VMPIDR p15, 4, c0, c0, 5 +#define ISR p15, 0, c12, c1, 0 +#define CLIDR p15, 1, c0, c0, 1 +#define CSSELR p15, 2, c0, c0, 0 +#define CCSIDR p15, 1, c0, c0, 0 + +/* GICv3 CPU Interface system register defines. The format is: coproc, opt1, CRn, CRm, opt2 */ +#define ICC_IAR1 p15, 0, c12, c12, 0 +#define ICC_IAR0 p15, 0, c12, c8, 0 +#define ICC_EOIR1 p15, 0, c12, c12, 1 +#define ICC_EOIR0 p15, 0, c12, c8, 1 +#define ICC_HPPIR1 p15, 0, c12, c12, 2 +#define ICC_HPPIR0 p15, 0, c12, c8, 2 +#define ICC_BPR1 p15, 0, c12, c12, 3 +#define ICC_BPR0 p15, 0, c12, c8, 3 +#define ICC_DIR p15, 0, c12, c11, 1 +#define ICC_PMR p15, 0, c4, c6, 0 +#define ICC_RPR p15, 0, c12, c11, 3 +#define ICC_CTLR p15, 0, c12, c12, 4 +#define ICC_MCTLR p15, 6, c12, c12, 4 +#define ICC_SRE p15, 0, c12, c12, 5 +#define ICC_HSRE p15, 4, c12, c9, 5 +#define ICC_MSRE p15, 6, c12, c12, 5 +#define ICC_IGRPEN0 p15, 0, c12, c12, 6 +#define ICC_IGRPEN1 p15, 0, c12, c12, 7 +#define ICC_MGRPEN1 p15, 6, c12, c12, 7 + +/* 64 bit system register defines The format is: coproc, opt1, CRm */ +#define TTBR0_64 p15, 0, c2 +#define TTBR1_64 p15, 1, c2 +#define CNTVOFF_64 p15, 4, c14 +#define VTTBR_64 p15, 6, c2 +#define CNTPCT_64 p15, 0, c14 + +/* 64 bit GICv3 CPU Interface system register defines. The format is: coproc, opt1, CRm */ +#define ICC_SGI1R_EL1_64 p15, 0, c12 +#define ICC_ASGI1R_EL1_64 p15, 1, c12 +#define ICC_SGI0R_EL1_64 p15, 2, c12 + +#endif /* __ARCH_H__ */ diff --git a/include/lib/aarch32/arch_helpers.h b/include/lib/aarch32/arch_helpers.h new file mode 100644 index 00000000..ddf660b1 --- /dev/null +++ b/include/lib/aarch32/arch_helpers.h @@ -0,0 +1,292 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __ARCH_HELPERS_H__ +#define __ARCH_HELPERS_H__ + +#include <arch.h> /* for additional register definitions */ +#include <stdint.h> +#include <types.h> + +/********************************************************************** + * Macros which create inline functions to read or write CPU system + * registers + *********************************************************************/ + +#define _DEFINE_COPROCR_WRITE_FUNC(_name, coproc, opc1, CRn, CRm, opc2) \ +static inline void write_## _name(u_register_t v) \ +{ \ + __asm__ volatile ("mcr "#coproc","#opc1",%0,"#CRn","#CRm","#opc2 : : "r" (v));\ +} + +#define _DEFINE_COPROCR_READ_FUNC(_name, coproc, opc1, CRn, CRm, opc2) \ +static inline u_register_t read_ ## _name(void) \ +{ \ + u_register_t v; \ + __asm__ volatile ("mrc "#coproc","#opc1",%0,"#CRn","#CRm","#opc2 : "=r" (v));\ + return v; \ +} + +/* + * The undocumented %Q and %R extended asm are used to implemented the below + * 64 bit `mrrc` and `mcrr` instructions. It works only on Little Endian + * systems for GCC versions < 4.6. Above GCC 4.6, both Little Endian and + * Big Endian systems generate the right instruction encoding. + */ +#if !(__GNUC__ > (4) || __GNUC__ == (4) && __GNUC_MINOR__ >= (6)) +#error "GCC 4.6 or above is required to build AArch32 Trusted Firmware" +#endif + +#define _DEFINE_COPROCR_WRITE_FUNC_64(_name, coproc, opc1, CRm) \ +static inline void write64_## _name(uint64_t v) \ +{ \ + __asm__ volatile ("mcrr "#coproc","#opc1", %Q0, %R0,"#CRm : : "r" (v));\ +} + +#define _DEFINE_COPROCR_READ_FUNC_64(_name, coproc, opc1, CRm) \ +static inline uint64_t read64_## _name(void) \ +{ uint64_t v; \ + __asm__ volatile ("mrrc "#coproc","#opc1", %Q0, %R0,"#CRm : "=r" (v));\ + return v; \ +} + +#define _DEFINE_SYSREG_READ_FUNC(_name, _reg_name) \ +static inline u_register_t read_ ## _name(void) \ +{ \ + u_register_t v; \ + __asm__ volatile ("mrs %0, " #_reg_name : "=r" (v)); \ + return v; \ +} + +#define _DEFINE_SYSREG_WRITE_FUNC(_name, _reg_name) \ +static inline void write_ ## _name(u_register_t v) \ +{ \ + __asm__ volatile ("msr " #_reg_name ", %0" : : "r" (v)); \ +} + +#define _DEFINE_SYSREG_WRITE_CONST_FUNC(_name, _reg_name) \ +static inline void write_ ## _name(const u_register_t v) \ +{ \ + __asm__ volatile ("msr " #_reg_name ", %0" : : "i" (v)); \ +} + +/* Define read function for coproc register */ +#define DEFINE_COPROCR_READ_FUNC(_name, ...) \ + _DEFINE_COPROCR_READ_FUNC(_name, __VA_ARGS__) + +/* Define read & write function for coproc register */ +#define DEFINE_COPROCR_RW_FUNCS(_name, ...) \ + _DEFINE_COPROCR_READ_FUNC(_name, __VA_ARGS__) \ + _DEFINE_COPROCR_WRITE_FUNC(_name, __VA_ARGS__) + +/* Define 64 bit read function for coproc register */ +#define DEFINE_COPROCR_READ_FUNC_64(_name, ...) \ + _DEFINE_COPROCR_READ_FUNC_64(_name, __VA_ARGS__) + +/* Define 64 bit read & write function for coproc register */ +#define DEFINE_COPROCR_RW_FUNCS_64(_name, ...) \ + _DEFINE_COPROCR_READ_FUNC_64(_name, __VA_ARGS__) \ + _DEFINE_COPROCR_WRITE_FUNC_64(_name, __VA_ARGS__) + +/* Define read & write function for system register */ +#define DEFINE_SYSREG_RW_FUNCS(_name) \ + _DEFINE_SYSREG_READ_FUNC(_name, _name) \ + _DEFINE_SYSREG_WRITE_FUNC(_name, _name) + +/********************************************************************** + * Macros to create inline functions for tlbi operations + *********************************************************************/ + +#define _DEFINE_TLBIOP_FUNC(_op, coproc, opc1, CRn, CRm, opc2) \ +static inline void tlbi##_op(void) \ +{ \ + u_register_t v = 0; \ + __asm__ volatile ("mcr "#coproc","#opc1",%0,"#CRn","#CRm","#opc2 : : "r" (v));\ +} + +#define _DEFINE_TLBIOP_PARAM_FUNC(_op, coproc, opc1, CRn, CRm, opc2) \ +static inline void tlbi##_op(u_register_t v) \ +{ \ + __asm__ volatile ("mcr "#coproc","#opc1",%0,"#CRn","#CRm","#opc2 : : "r" (v));\ +} + +/* Define function for simple TLBI operation */ +#define DEFINE_TLBIOP_FUNC(_op, ...) \ + _DEFINE_TLBIOP_FUNC(_op, __VA_ARGS__) + +/* Define function for TLBI operation with register parameter */ +#define DEFINE_TLBIOP_PARAM_FUNC(_op, ...) \ + _DEFINE_TLBIOP_PARAM_FUNC(_op, __VA_ARGS__) + +/********************************************************************** + * Macros to create inline functions for DC operations + *********************************************************************/ +#define _DEFINE_DCOP_PARAM_FUNC(_op, coproc, opc1, CRn, CRm, opc2) \ +static inline void dc##_op(u_register_t v) \ +{ \ + __asm__ volatile ("mcr "#coproc","#opc1",%0,"#CRn","#CRm","#opc2 : : "r" (v));\ +} + +/* Define function for DC operation with register parameter */ +#define DEFINE_DCOP_PARAM_FUNC(_op, ...) \ + _DEFINE_DCOP_PARAM_FUNC(_op, __VA_ARGS__) + +/********************************************************************** + * Macros to create inline functions for system instructions + *********************************************************************/ + /* Define function for simple system instruction */ +#define DEFINE_SYSOP_FUNC(_op) \ +static inline void _op(void) \ +{ \ + __asm__ (#_op); \ +} + + +/* Define function for system instruction with type specifier */ +#define DEFINE_SYSOP_TYPE_FUNC(_op, _type) \ +static inline void _op ## _type(void) \ +{ \ + __asm__ (#_op " " #_type); \ +} + +/* Define function for system instruction with register parameter */ +#define DEFINE_SYSOP_TYPE_PARAM_FUNC(_op, _type) \ +static inline void _op ## _type(u_register_t v) \ +{ \ + __asm__ (#_op " " #_type ", %0" : : "r" (v)); \ +} + +void flush_dcache_range(uintptr_t addr, size_t size); +void clean_dcache_range(uintptr_t addr, size_t size); +void inv_dcache_range(uintptr_t addr, size_t size); + +DEFINE_SYSOP_FUNC(wfi) +DEFINE_SYSOP_FUNC(wfe) +DEFINE_SYSOP_FUNC(sev) +DEFINE_SYSOP_TYPE_FUNC(dsb, sy) +DEFINE_SYSOP_TYPE_FUNC(dmb, sy) +DEFINE_SYSOP_TYPE_FUNC(dsb, ish) +DEFINE_SYSOP_TYPE_FUNC(dmb, ish) +DEFINE_SYSOP_FUNC(isb) + +DEFINE_SYSREG_RW_FUNCS(spsr) +DEFINE_SYSREG_RW_FUNCS(cpsr) + +/******************************************************************************* + * System register accessor prototypes + ******************************************************************************/ +DEFINE_COPROCR_READ_FUNC(mpidr, MPIDR) +DEFINE_COPROCR_READ_FUNC(midr, MIDR) +DEFINE_COPROCR_READ_FUNC(id_pfr1, ID_PFR1) +DEFINE_COPROCR_READ_FUNC(isr, ISR) +DEFINE_COPROCR_READ_FUNC(clidr, CLIDR) +DEFINE_COPROCR_READ_FUNC_64(cntpct, CNTPCT_64) + +DEFINE_COPROCR_RW_FUNCS(scr, SCR) +DEFINE_COPROCR_RW_FUNCS(ctr, CTR) +DEFINE_COPROCR_RW_FUNCS(sctlr, SCTLR) +DEFINE_COPROCR_RW_FUNCS(hsctlr, HSCTLR) +DEFINE_COPROCR_RW_FUNCS(hcr, HCR) +DEFINE_COPROCR_RW_FUNCS(hcptr, HCPTR) +DEFINE_COPROCR_RW_FUNCS(cntfrq, CNTFRQ) +DEFINE_COPROCR_RW_FUNCS(cnthctl, CNTHCTL) +DEFINE_COPROCR_RW_FUNCS(mair0, MAIR0) +DEFINE_COPROCR_RW_FUNCS(mair1, MAIR1) +DEFINE_COPROCR_RW_FUNCS(ttbcr, TTBCR) +DEFINE_COPROCR_RW_FUNCS(ttbr0, TTBR0) +DEFINE_COPROCR_RW_FUNCS_64(ttbr0, TTBR0_64) +DEFINE_COPROCR_RW_FUNCS(ttbr1, TTBR1) +DEFINE_COPROCR_RW_FUNCS(vpidr, VPIDR) +DEFINE_COPROCR_RW_FUNCS(vmpidr, VMPIDR) +DEFINE_COPROCR_RW_FUNCS_64(vttbr, VTTBR_64) +DEFINE_COPROCR_RW_FUNCS_64(ttbr1, TTBR1_64) +DEFINE_COPROCR_RW_FUNCS_64(cntvoff, CNTVOFF_64) +DEFINE_COPROCR_RW_FUNCS(csselr, CSSELR) + +DEFINE_COPROCR_RW_FUNCS(icc_sre_el1, ICC_SRE) +DEFINE_COPROCR_RW_FUNCS(icc_sre_el2, ICC_HSRE) +DEFINE_COPROCR_RW_FUNCS(icc_sre_el3, ICC_MSRE) +DEFINE_COPROCR_RW_FUNCS(icc_pmr_el1, ICC_PMR) +DEFINE_COPROCR_RW_FUNCS(icc_igrpen1_el3, ICC_MGRPEN1) +DEFINE_COPROCR_RW_FUNCS(icc_igrpen0_el1, ICC_IGRPEN0) +DEFINE_COPROCR_RW_FUNCS(icc_hppir0_el1, ICC_HPPIR0) +DEFINE_COPROCR_RW_FUNCS(icc_hppir1_el1, ICC_HPPIR1) +DEFINE_COPROCR_RW_FUNCS(icc_iar0_el1, ICC_IAR0) +DEFINE_COPROCR_RW_FUNCS(icc_iar1_el1, ICC_IAR1) +DEFINE_COPROCR_RW_FUNCS(icc_eoir0_el1, ICC_EOIR0) +DEFINE_COPROCR_RW_FUNCS(icc_eoir1_el1, ICC_EOIR1) + +/* + * TLBI operation prototypes + */ +DEFINE_TLBIOP_FUNC(all, TLBIALL) +DEFINE_TLBIOP_FUNC(allis, TLBIALLIS) +DEFINE_TLBIOP_PARAM_FUNC(mva, TLBIMVA) +DEFINE_TLBIOP_PARAM_FUNC(mvaa, TLBIMVAA) + +/* + * DC operation prototypes + */ +DEFINE_DCOP_PARAM_FUNC(civac, DCCIMVAC) +DEFINE_DCOP_PARAM_FUNC(ivac, DCIMVAC) +DEFINE_DCOP_PARAM_FUNC(cvac, DCCMVAC) + +/* Previously defined accessor functions with incomplete register names */ +#define dsb() dsbsy() + +#define IS_IN_SECURE() \ + (GET_NS_BIT(read_scr()) == 0) + + /* + * If EL3 is AArch32, then secure PL1 and monitor mode correspond to EL3 + */ +#define IS_IN_EL3() \ + ((GET_M32(read_cpsr()) == MODE32_mon) || \ + (IS_IN_SECURE() && (GET_M32(read_cpsr()) != MODE32_usr))) + +/* Macros for compatibility with AArch64 system registers */ +#define read_mpidr_el1() read_mpidr() + +#define read_scr_el3() read_scr() +#define write_scr_el3(_v) write_scr(_v) + +#define read_hcr_el2() read_hcr() +#define write_hcr_el2(_v) write_hcr(_v) + +#define read_cpacr_el1() read_cpacr() +#define write_cpacr_el1(_v) write_cpacr(_v) + +#define read_cntfrq_el0() read_cntfrq() +#define write_cntfrq_el0(_v) write_cntfrq(_v) +#define read_isr_el1() read_isr() + +#define read_cntpct_el0() read64_cntpct() + +#endif /* __ARCH_HELPERS_H__ */ diff --git a/include/lib/aarch32/smcc_helpers.h b/include/lib/aarch32/smcc_helpers.h new file mode 100644 index 00000000..5aeca223 --- /dev/null +++ b/include/lib/aarch32/smcc_helpers.h @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __SMCC_HELPERS_H__ +#define __SMCC_HELPERS_H__ + +#include <smcc.h> + +/* These are offsets to registers in smc_ctx_t */ +#define SMC_CTX_GPREG_R0 0x0 +#define SMC_CTX_GPREG_R1 0x4 +#define SMC_CTX_GPREG_R2 0x8 +#define SMC_CTX_GPREG_R3 0xC +#define SMC_CTX_GPREG_R4 0x10 +#define SMC_CTX_SP_USR 0x34 +#define SMC_CTX_SPSR_MON 0x78 +#define SMC_CTX_LR_MON 0x7C +#define SMC_CTX_SIZE 0x80 + +#ifndef __ASSEMBLY__ +#include <cassert.h> +#include <types.h> + +/* + * The generic structure to save arguments and callee saved registers during + * an SMC. Also this structure is used to store the result return values after + * the completion of SMC service. + */ +typedef struct smc_ctx { + u_register_t r0; + u_register_t r1; + u_register_t r2; + u_register_t r3; + u_register_t r4; + u_register_t r5; + u_register_t r6; + u_register_t r7; + u_register_t r8; + u_register_t r9; + u_register_t r10; + u_register_t r11; + u_register_t r12; + /* spsr_usr doesn't exist */ + u_register_t sp_usr; + u_register_t lr_usr; + u_register_t spsr_irq; + u_register_t sp_irq; + u_register_t lr_irq; + u_register_t spsr_fiq; + u_register_t sp_fiq; + u_register_t lr_fiq; + u_register_t spsr_svc; + u_register_t sp_svc; + u_register_t lr_svc; + u_register_t spsr_abt; + u_register_t sp_abt; + u_register_t lr_abt; + u_register_t spsr_und; + u_register_t sp_und; + u_register_t lr_und; + u_register_t spsr_mon; + /* No need to save 'sp_mon' because we are already in monitor mode */ + u_register_t lr_mon; +} smc_ctx_t; + +/* + * Compile time assertions related to the 'smc_context' structure to + * ensure that the assembler and the compiler view of the offsets of + * the structure members is the same. + */ +CASSERT(SMC_CTX_GPREG_R0 == __builtin_offsetof(smc_ctx_t, r0), \ + assert_smc_ctx_greg_r0_offset_mismatch); +CASSERT(SMC_CTX_GPREG_R1 == __builtin_offsetof(smc_ctx_t, r1), \ + assert_smc_ctx_greg_r1_offset_mismatch); +CASSERT(SMC_CTX_GPREG_R2 == __builtin_offsetof(smc_ctx_t, r2), \ + assert_smc_ctx_greg_r2_offset_mismatch); +CASSERT(SMC_CTX_GPREG_R3 == __builtin_offsetof(smc_ctx_t, r3), \ + assert_smc_ctx_greg_r3_offset_mismatch); +CASSERT(SMC_CTX_GPREG_R4 == __builtin_offsetof(smc_ctx_t, r4), \ + assert_smc_ctx_greg_r4_offset_mismatch); +CASSERT(SMC_CTX_SP_USR == __builtin_offsetof(smc_ctx_t, sp_usr), \ + assert_smc_ctx_sp_usr_offset_mismatch); +CASSERT(SMC_CTX_LR_MON == __builtin_offsetof(smc_ctx_t, lr_mon), \ + assert_smc_ctx_lr_mon_offset_mismatch); +CASSERT(SMC_CTX_SPSR_MON == __builtin_offsetof(smc_ctx_t, spsr_mon), \ + assert_smc_ctx_spsr_mon_offset_mismatch); + +CASSERT(SMC_CTX_SIZE == sizeof(smc_ctx_t), assert_smc_ctx_size_mismatch); + +/* Convenience macros to return from SMC handler */ +#define SMC_RET0(_h) { \ + return (uintptr_t)(_h); \ +} +#define SMC_RET1(_h, _r0) { \ + ((smc_ctx_t *)(_h))->r0 = (_r0); \ + SMC_RET0(_h); \ +} +#define SMC_RET2(_h, _r0, _r1) { \ + ((smc_ctx_t *)(_h))->r1 = (_r1); \ + SMC_RET1(_h, (_r0)); \ +} +#define SMC_RET3(_h, _r0, _r1, _r2) { \ + ((smc_ctx_t *)(_h))->r2 = (_r2); \ + SMC_RET2(_h, (_r0), (_r1)); \ +} +#define SMC_RET4(_h, _r0, _r1, _r2, _r3) { \ + ((smc_ctx_t *)(_h))->r3 = (_r3); \ + SMC_RET3(_h, (_r0), (_r1), (_r2)); \ +} + +/* Return a UUID in the SMC return registers */ +#define SMC_UUID_RET(_h, _uuid) \ + SMC_RET4(handle, ((const uint32_t *) &(_uuid))[0], \ + ((const uint32_t *) &(_uuid))[1], \ + ((const uint32_t *) &(_uuid))[2], \ + ((const uint32_t *) &(_uuid))[3]) + +/* + * Helper macro to retrieve the SMC parameters from smc_ctx_t. + */ +#define get_smc_params_from_ctx(_hdl, _r1, _r2, _r3, _r4) { \ + _r1 = ((smc_ctx_t *)_hdl)->r1; \ + _r2 = ((smc_ctx_t *)_hdl)->r2; \ + _r3 = ((smc_ctx_t *)_hdl)->r3; \ + _r4 = ((smc_ctx_t *)_hdl)->r4; \ + } + +/* ------------------------------------------------------------------------ + * Helper APIs for setting and retrieving appropriate `smc_ctx_t`. + * These functions need to implemented by the BL including this library. + * ------------------------------------------------------------------------ + */ + +/* Get the pointer to `smc_ctx_t` corresponding to the security state. */ +void *smc_get_ctx(int security_state); + +/* Set the next `smc_ctx_t` corresponding to the security state. */ +void smc_set_next_ctx(int security_state); + +/* Get the pointer to next `smc_ctx_t` already set by `smc_set_next_ctx()`. */ +void *smc_get_next_ctx(void); + +#endif /*__ASSEMBLY__*/ +#endif /* __SMCC_HELPERS_H__ */ diff --git a/include/lib/aarch32/smcc_macros.S b/include/lib/aarch32/smcc_macros.S new file mode 100644 index 00000000..c80c3e47 --- /dev/null +++ b/include/lib/aarch32/smcc_macros.S @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __SMCC_MACROS_S__ +#define __SMCC_MACROS_S__ + +#include <arch.h> + +/* + * Macro to save the General purpose registers including the banked + * registers to the SMC context on entry due a SMC call. On return, r0 + * contains the pointer to the `smc_context_t`. + */ + .macro smcc_save_gp_mode_regs + push {r0-r3, lr} + + ldcopr r0, SCR + and r0, r0, #SCR_NS_BIT + bl smc_get_ctx + + /* Save r4 - r12 in the SMC context */ + add r1, r0, #SMC_CTX_GPREG_R4 + stm r1!, {r4-r12} + + /* + * Pop r0 - r3, lr to r4 - r7, lr from stack and then save + * it to SMC context. + */ + pop {r4-r7, lr} + stm r0, {r4-r7} + + /* Save the banked registers including the current SPSR and LR */ + mrs r4, sp_usr + mrs r5, lr_usr + mrs r6, spsr_irq + mrs r7, sp_irq + mrs r8, lr_irq + mrs r9, spsr_fiq + mrs r10, sp_fiq + mrs r11, lr_fiq + mrs r12, spsr_svc + stm r1!, {r4-r12} + + mrs r4, sp_svc + mrs r5, lr_svc + mrs r6, spsr_abt + mrs r7, sp_abt + mrs r8, lr_abt + mrs r9, spsr_und + mrs r10, sp_und + mrs r11, lr_und + mrs r12, spsr + stm r1!, {r4-r12, lr} + + .endm + +/* + * Macro to restore the General purpose registers including the banked + * registers from the SMC context prior to exit from the SMC call. + * r0 must point to the `smc_context_t` to restore from. + */ + .macro smcc_restore_gp_mode_regs + + /* Restore the banked registers including the current SPSR and LR */ + add r1, r0, #SMC_CTX_SP_USR + ldm r1!, {r4-r12} + msr sp_usr, r4 + msr lr_usr, r5 + msr spsr_irq, r6 + msr sp_irq, r7 + msr lr_irq, r8 + msr spsr_fiq, r9 + msr sp_fiq, r10 + msr lr_fiq, r11 + msr spsr_svc, r12 + + ldm r1!, {r4-r12, lr} + msr sp_svc, r4 + msr lr_svc, r5 + msr spsr_abt, r6 + msr sp_abt, r7 + msr lr_abt, r8 + msr spsr_und, r9 + msr sp_und, r10 + msr lr_und, r11 + msr spsr, r12 + + /* Restore the rest of the general purpose registers */ + ldm r0, {r0-r12} + .endm + +#endif /* __SMCC_MACROS_S__ */ diff --git a/include/lib/aarch64/arch.h b/include/lib/aarch64/arch.h index 07bbd899..fb8cbc0b 100644 --- a/include/lib/aarch64/arch.h +++ b/include/lib/aarch64/arch.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2016, 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: @@ -243,6 +243,9 @@ #define TCR_EL1_IPS_SHIFT 32 #define TCR_EL3_PS_SHIFT 16 +#define TCR_TxSZ_MIN 16 +#define TCR_TxSZ_MAX 39 + /* (internal) physical address size bits in EL3/EL1 */ #define TCR_PS_BITS_4GB (0x0) #define TCR_PS_BITS_64GB (0x1) @@ -334,8 +337,6 @@ #define CTR_IMINLINE_MASK 0xf #define MAX_CACHE_LINE_SIZE 0x800 /* 2KB */ -#define SIZE_FROM_LOG2_WORDS(n) (4 << (n)) - /* Physical timer control register bit fields shifts and masks */ #define CNTP_CTL_ENABLE_SHIFT 0 diff --git a/include/lib/aarch64/smcc_helpers.h b/include/lib/aarch64/smcc_helpers.h index 617a5bce..6e633839 100644 --- a/include/lib/aarch64/smcc_helpers.h +++ b/include/lib/aarch64/smcc_helpers.h @@ -82,5 +82,17 @@ ((const uint32_t *) &(_uuid))[2], \ ((const uint32_t *) &(_uuid))[3]) +/* + * Helper macro to retrieve the SMC parameters from cpu_context_t. + */ +#define get_smc_params_from_ctx(_hdl, _x1, _x2, _x3, _x4) \ + do { \ + const gp_regs_t *regs = get_gpregs_ctx(_hdl); \ + _x1 = read_ctx_reg(regs, CTX_GPREG_X1); \ + _x2 = read_ctx_reg(regs, CTX_GPREG_X2); \ + _x3 = read_ctx_reg(regs, CTX_GPREG_X3); \ + _x4 = read_ctx_reg(regs, CTX_GPREG_X4); \ + } while (0) + #endif /*__ASSEMBLY__*/ #endif /* __SMCC_HELPERS_H__ */ diff --git a/include/lib/cpus/aarch32/aem_generic.h b/include/lib/cpus/aarch32/aem_generic.h new file mode 100644 index 00000000..9b313677 --- /dev/null +++ b/include/lib/cpus/aarch32/aem_generic.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __AEM_GENERIC_H__ +#define __AEM_GENERIC_H__ + +/* BASE AEM midr for revision 0 */ +#define BASE_AEM_MIDR 0x410FD0F0 + +#endif /* __AEM_GENERIC_H__ */ diff --git a/include/lib/cpus/aarch32/cpu_macros.S b/include/lib/cpus/aarch32/cpu_macros.S new file mode 100644 index 00000000..f58f3e94 --- /dev/null +++ b/include/lib/cpus/aarch32/cpu_macros.S @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __CPU_MACROS_S__ +#define __CPU_MACROS_S__ + +#include <arch.h> + +#define CPU_IMPL_PN_MASK (MIDR_IMPL_MASK << MIDR_IMPL_SHIFT) | \ + (MIDR_PN_MASK << MIDR_PN_SHIFT) + + /* + * Define the offsets to the fields in cpu_ops structure. + */ + .struct 0 +CPU_MIDR: /* cpu_ops midr */ + .space 4 +/* Reset fn is needed during reset */ +CPU_RESET_FUNC: /* cpu_ops reset_func */ + .space 4 +CPU_PWR_DWN_CORE: /* cpu_ops core_pwr_dwn */ + .space 4 +CPU_PWR_DWN_CLUSTER: /* cpu_ops cluster_pwr_dwn */ + .space 4 +CPU_OPS_SIZE = . + + /* + * Convenience macro to declare cpu_ops structure. + * Make sure the structure fields are as per the offsets + * defined above. + */ + .macro declare_cpu_ops _name:req, _midr:req, _noresetfunc = 0 + .section cpu_ops, "a" + .align 2 + .type cpu_ops_\_name, %object + .word \_midr + .if \_noresetfunc + .word 0 + .else + .word \_name\()_reset_func + .endif + .word \_name\()_core_pwr_dwn + .word \_name\()_cluster_pwr_dwn + .endm + +#endif /* __CPU_MACROS_S__ */ diff --git a/include/lib/el3_runtime/aarch32/context.h b/include/lib/el3_runtime/aarch32/context.h new file mode 100644 index 00000000..51081415 --- /dev/null +++ b/include/lib/el3_runtime/aarch32/context.h @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __CONTEXT_H__ +#define __CONTEXT_H__ + +/******************************************************************************* + * Constants that allow assembler code to access members of and the 'regs' + * structure at their correct offsets. + ******************************************************************************/ +#define CTX_REGS_OFFSET 0x0 +#define CTX_GPREG_R0 0x0 +#define CTX_GPREG_R1 0x4 +#define CTX_GPREG_R2 0x8 +#define CTX_GPREG_R3 0xC +#define CTX_LR 0x10 +#define CTX_SCR 0x14 +#define CTX_SPSR 0x18 +#define CTX_NS_SCTLR 0x1C +#define CTX_REGS_END 0x20 + +#ifndef __ASSEMBLY__ + +#include <cassert.h> +#include <stdint.h> + +/* + * Common constants to help define the 'cpu_context' structure and its + * members below. + */ +#define WORD_SHIFT 2 +#define DEFINE_REG_STRUCT(name, num_regs) \ + typedef struct name { \ + uint32_t _regs[num_regs]; \ + } __aligned(8) name##_t + +/* Constants to determine the size of individual context structures */ +#define CTX_REG_ALL (CTX_REGS_END >> WORD_SHIFT) + +DEFINE_REG_STRUCT(regs, CTX_REG_ALL); + +#undef CTX_REG_ALL + +#define read_ctx_reg(ctx, offset) ((ctx)->_regs[offset >> WORD_SHIFT]) +#define write_ctx_reg(ctx, offset, val) (((ctx)->_regs[offset >> WORD_SHIFT]) \ + = val) +typedef struct cpu_context { + regs_t regs_ctx; +} cpu_context_t; + +/* Macros to access members of the 'cpu_context_t' structure */ +#define get_regs_ctx(h) (&((cpu_context_t *) h)->regs_ctx) + +/* + * Compile time assertions related to the 'cpu_context' structure to + * ensure that the assembler and the compiler view of the offsets of + * the structure members is the same. + */ +CASSERT(CTX_REGS_OFFSET == __builtin_offsetof(cpu_context_t, regs_ctx), \ + assert_core_context_regs_offset_mismatch); + +#endif /* __ASSEMBLY__ */ + +#endif /* __CONTEXT_H__ */ diff --git a/include/lib/el3_runtime/context_mgmt.h b/include/lib/el3_runtime/context_mgmt.h index 672ea11e..b264fc32 100644 --- a/include/lib/el3_runtime/context_mgmt.h +++ b/include/lib/el3_runtime/context_mgmt.h @@ -42,11 +42,6 @@ struct entry_point_info; * Function & variable prototypes ******************************************************************************/ void cm_init(void); -void *cm_get_context_by_mpidr(uint64_t mpidr, - uint32_t security_state) __deprecated; -void cm_set_context_by_mpidr(uint64_t mpidr, - void *context, - uint32_t security_state) __deprecated; void *cm_get_context_by_index(unsigned int cpu_idx, unsigned int security_state); void cm_set_context_by_index(unsigned int cpu_idx, @@ -54,12 +49,12 @@ void cm_set_context_by_index(unsigned int cpu_idx, unsigned int security_state); void *cm_get_context(uint32_t security_state); void cm_set_context(void *context, uint32_t security_state); -void cm_init_context(uint64_t mpidr, - const struct entry_point_info *ep) __deprecated; void cm_init_my_context(const struct entry_point_info *ep); void cm_init_context_by_index(unsigned int cpu_idx, const struct entry_point_info *ep); void cm_prepare_el3_exit(uint32_t security_state); + +#ifndef AARCH32 void cm_el1_sysregs_context_save(uint32_t security_state); void cm_el1_sysregs_context_restore(uint32_t security_state); void cm_set_elr_el3(uint32_t security_state, uintptr_t entrypoint); @@ -71,6 +66,16 @@ void cm_write_scr_el3_bit(uint32_t security_state, void cm_set_next_eret_context(uint32_t security_state); uint32_t cm_get_scr_el3(uint32_t security_state); + +void cm_init_context(uint64_t mpidr, + const struct entry_point_info *ep) __deprecated; + +void *cm_get_context_by_mpidr(uint64_t mpidr, + uint32_t security_state) __deprecated; +void cm_set_context_by_mpidr(uint64_t mpidr, + void *context, + uint32_t security_state) __deprecated; + /* Inline definitions */ /******************************************************************************* @@ -98,4 +103,5 @@ static inline void cm_set_next_context(void *context) "msr spsel, #0\n" : : "r" (context)); } +#endif /* AARCH32 */ #endif /* __CM_H__ */ diff --git a/include/lib/el3_runtime/cpu_data.h b/include/lib/el3_runtime/cpu_data.h index 4fc801bf..910b1534 100644 --- a/include/lib/el3_runtime/cpu_data.h +++ b/include/lib/el3_runtime/cpu_data.h @@ -31,16 +31,28 @@ #ifndef __CPU_DATA_H__ #define __CPU_DATA_H__ +#ifdef AARCH32 + +#if CRASH_REPORTING +#error "Crash reporting is not supported in AArch32" +#endif +#define CPU_DATA_CPU_OPS_PTR 0x0 + +#else /* AARCH32 */ + /* Offsets for the cpu_data structure */ #define CPU_DATA_CRASH_BUF_OFFSET 0x18 +/* need enough space in crash buffer to save 8 registers */ +#define CPU_DATA_CRASH_BUF_SIZE 64 +#define CPU_DATA_CPU_OPS_PTR 0x10 + +#endif /* AARCH32 */ + #if CRASH_REPORTING #define CPU_DATA_LOG2SIZE 7 #else #define CPU_DATA_LOG2SIZE 6 #endif -/* need enough space in crash buffer to save 8 registers */ -#define CPU_DATA_CRASH_BUF_SIZE 64 -#define CPU_DATA_CPU_OPS_PTR 0x10 #ifndef __ASSEMBLY__ @@ -77,7 +89,9 @@ * used for this. ******************************************************************************/ typedef struct cpu_data { +#ifndef AARCH32 void *cpu_context[2]; +#endif uintptr_t cpu_ops_ptr; #if CRASH_REPORTING u_register_t crash_buf[CPU_DATA_CRASH_BUF_SIZE >> 3]; @@ -104,12 +118,15 @@ CASSERT(CPU_DATA_CPU_OPS_PTR == __builtin_offsetof struct cpu_data *_cpu_data_by_index(uint32_t cpu_index); +#ifndef AARCH32 /* Return the cpu_data structure for the current CPU. */ static inline struct cpu_data *_cpu_data(void) { return (cpu_data_t *)read_tpidr_el3(); } - +#else +struct cpu_data *_cpu_data(void); +#endif /************************************************************************** * APIs for initialising and accessing per-cpu data diff --git a/include/lib/pmf.h b/include/lib/pmf/pmf.h index 5f953b5a..5f953b5a 100644 --- a/include/lib/pmf.h +++ b/include/lib/pmf/pmf.h diff --git a/include/lib/pmf/pmf_asm_macros.S b/include/lib/pmf/pmf_asm_macros.S new file mode 100644 index 00000000..42dcd794 --- /dev/null +++ b/include/lib/pmf/pmf_asm_macros.S @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __PMF_ASM_MACROS_S__ +#define __PMF_ASM_MACROS_S__ + +#define PMF_TS_SIZE 8 + + /* + * This macro calculates the offset into the memory + * region where the per-cpu timestamp value is stored + * for the given service name and timestamp id. + * Clobbers: x0 - x9 + */ + .macro pmf_calc_timestamp_offset _name _tid + mov x9, x30 + bl plat_my_core_pos + mov x30, x9 + ldr x1, =__PERCPU_TIMESTAMP_SIZE__ + mov x2, #(\_tid * PMF_TS_SIZE) + madd x0, x0, x1, x2 + ldr x1, =pmf_ts_mem_\_name + add x0, x0, x1 + .endm + +#endif /* __PMF_ASM_MACROS_S__ */ diff --git a/include/lib/pmf_helpers.h b/include/lib/pmf/pmf_helpers.h index 1b8c5077..9be6050f 100644 --- a/include/lib/pmf_helpers.h +++ b/include/lib/pmf/pmf_helpers.h @@ -69,8 +69,8 @@ typedef struct pmf_svc_desc { /* * Convenience macro to allocate memory for a PMF service. */ -#define PMF_ALLOCATE_TIMESTAMP_MEMORY(_name, _total_id) \ - static unsigned long long pmf_ts_mem_ ## _name[_total_id] \ +#define PMF_ALLOCATE_TIMESTAMP_MEMORY(_name, _total_id) \ + unsigned long long pmf_ts_mem_ ## _name[_total_id] \ __section("pmf_timestamp_array") __used; /* diff --git a/include/lib/psci/psci.h b/include/lib/psci/psci.h index c3e9ef7e..a583fef7 100644 --- a/include/lib/psci/psci.h +++ b/include/lib/psci/psci.h @@ -359,6 +359,8 @@ u_register_t psci_smc_handler(uint32_t smc_fid, int psci_setup(uintptr_t mailbox_ep); void psci_warmboot_entrypoint(void); void psci_register_spd_pm_hook(const spd_pm_ops_t *pm); +void psci_prepare_next_non_secure_ctx( + struct entry_point_info *next_image_info); #endif /*__ASSEMBLY__*/ diff --git a/include/lib/stdlib/machine/_types.h b/include/lib/stdlib/machine/_types.h index 7e993c4c..fb1083b7 100644 --- a/include/lib/stdlib/machine/_types.h +++ b/include/lib/stdlib/machine/_types.h @@ -31,6 +31,10 @@ * From: @(#)types.h 8.3 (Berkeley) 1/5/94 * $FreeBSD$ */ +/* + * Portions copyright (c) 2016, ARM Limited and Contributors. + * All rights reserved. + */ #ifndef _MACHINE__TYPES_H_ #define _MACHINE__TYPES_H_ @@ -48,19 +52,56 @@ typedef short __int16_t; typedef unsigned short __uint16_t; typedef int __int32_t; typedef unsigned int __uint32_t; + + +/* + * Standard type definitions which are different in AArch64 and AArch32 + */ +#ifdef AARCH32 +typedef long long __int64_t; +typedef unsigned long long __uint64_t; +typedef __int32_t __critical_t; +typedef __int32_t __intfptr_t; +typedef __int32_t __intptr_t; +typedef __int32_t __ptrdiff_t; /* ptr1 - ptr2 */ +typedef __int32_t __register_t; +typedef __int32_t __segsz_t; /* segment size (in pages) */ +typedef __uint32_t __size_t; /* sizeof() */ +typedef __int32_t __ssize_t; /* byte count or error */ +typedef __uint32_t __uintfptr_t; +typedef __uint32_t __uintptr_t; +typedef __uint32_t __u_register_t; +typedef __uint32_t __vm_offset_t; +typedef __uint32_t __vm_paddr_t; +typedef __uint32_t __vm_size_t; +#elif defined AARCH64 typedef long __int64_t; typedef unsigned long __uint64_t; +typedef __int64_t __critical_t; +typedef __int64_t __intfptr_t; +typedef __int64_t __intptr_t; +typedef __int64_t __ptrdiff_t; /* ptr1 - ptr2 */ +typedef __int64_t __register_t; +typedef __int64_t __segsz_t; /* segment size (in pages) */ +typedef __uint64_t __size_t; /* sizeof() */ +typedef __int64_t __ssize_t; /* byte count or error */ +typedef __uint64_t __uintfptr_t; +typedef __uint64_t __uintptr_t; +typedef __uint64_t __u_register_t; +typedef __uint64_t __vm_offset_t; +typedef __uint64_t __vm_paddr_t; +typedef __uint64_t __vm_size_t; +#else +#error "Only AArch32 or AArch64 supported" +#endif /* AARCH32 */ /* * Standard type definitions. */ typedef __int32_t __clock_t; /* clock()... */ -typedef __int64_t __critical_t; typedef double __double_t; typedef float __float_t; -typedef __int64_t __intfptr_t; typedef __int64_t __intmax_t; -typedef __int64_t __intptr_t; typedef __int32_t __int_fast8_t; typedef __int32_t __int_fast16_t; typedef __int32_t __int_fast32_t; @@ -69,15 +110,8 @@ typedef __int8_t __int_least8_t; typedef __int16_t __int_least16_t; typedef __int32_t __int_least32_t; typedef __int64_t __int_least64_t; -typedef __int64_t __ptrdiff_t; /* ptr1 - ptr2 */ -typedef __int64_t __register_t; -typedef __int64_t __segsz_t; /* segment size (in pages) */ -typedef __uint64_t __size_t; /* sizeof() */ -typedef __int64_t __ssize_t; /* byte count or error */ typedef __int64_t __time_t; /* time()... */ -typedef __uint64_t __uintfptr_t; typedef __uint64_t __uintmax_t; -typedef __uint64_t __uintptr_t; typedef __uint32_t __uint_fast8_t; typedef __uint32_t __uint_fast16_t; typedef __uint32_t __uint_fast32_t; @@ -86,12 +120,8 @@ typedef __uint8_t __uint_least8_t; typedef __uint16_t __uint_least16_t; typedef __uint32_t __uint_least32_t; typedef __uint64_t __uint_least64_t; -typedef __uint64_t __u_register_t; -typedef __uint64_t __vm_offset_t; typedef __int64_t __vm_ooffset_t; -typedef __uint64_t __vm_paddr_t; typedef __uint64_t __vm_pindex_t; -typedef __uint64_t __vm_size_t; /* * Unusual type definitions. diff --git a/include/lib/utils.h b/include/lib/utils.h index 0936cbb3..a234e3c9 100644 --- a/include/lib/utils.h +++ b/include/lib/utils.h @@ -38,6 +38,8 @@ #define IS_POWER_OF_TWO(x) \ (((x) & ((x) - 1)) == 0) +#define SIZE_FROM_LOG2_WORDS(n) (4 << (n)) + /* * 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/include/lib/xlat_tables.h b/include/lib/xlat_tables.h index b51a1de5..d2ac6db6 100644 --- a/include/lib/xlat_tables.h +++ b/include/lib/xlat_tables.h @@ -32,29 +32,12 @@ #define __XLAT_TABLES_H__ /* Miscellaneous MMU related constants */ -#define NUM_2MB_IN_GB (1 << 9) -#define NUM_4K_IN_2MB (1 << 9) -#define NUM_GB_IN_4GB (1 << 2) - -#define TWO_MB_SHIFT 21 -#define ONE_GB_SHIFT 30 #define FOUR_KB_SHIFT 12 -#define ONE_GB_INDEX(x) ((x) >> ONE_GB_SHIFT) -#define TWO_MB_INDEX(x) ((x) >> TWO_MB_SHIFT) -#define FOUR_KB_INDEX(x) ((x) >> FOUR_KB_SHIFT) - #define INVALID_DESC 0x0 -#define BLOCK_DESC 0x1 -#define TABLE_DESC 0x3 - -#define FIRST_LEVEL_DESC_N ONE_GB_SHIFT -#define SECOND_LEVEL_DESC_N TWO_MB_SHIFT -#define THIRD_LEVEL_DESC_N FOUR_KB_SHIFT - -#define LEVEL1 1 -#define LEVEL2 2 -#define LEVEL3 3 +#define BLOCK_DESC 0x1 /* Table levels 0-2 */ +#define TABLE_DESC 0x3 /* Table levels 0-2 */ +#define PAGE_DESC 0x3 /* Table level 3 */ #define XN (1ull << 2) #define PXN (1ull << 1) @@ -78,6 +61,14 @@ #define XLAT_TABLE_SIZE_SHIFT PAGE_SIZE_SHIFT #define XLAT_TABLE_SIZE (1 << XLAT_TABLE_SIZE_SHIFT) +#ifdef AARCH32 +#define XLAT_TABLE_LEVEL_MIN 1 +#else +#define XLAT_TABLE_LEVEL_MIN 0 +#endif /* AARCH32 */ + +#define XLAT_TABLE_LEVEL_MAX 3 + /* Values for number of entries in each MMU translation table */ #define XLAT_TABLE_ENTRIES_SHIFT (XLAT_TABLE_SIZE_SHIFT - XLAT_ENTRY_SIZE_SHIFT) #define XLAT_TABLE_ENTRIES (1 << XLAT_TABLE_ENTRIES_SHIFT) @@ -87,6 +78,7 @@ #define L3_XLAT_ADDRESS_SHIFT PAGE_SIZE_SHIFT #define L2_XLAT_ADDRESS_SHIFT (L3_XLAT_ADDRESS_SHIFT + XLAT_TABLE_ENTRIES_SHIFT) #define L1_XLAT_ADDRESS_SHIFT (L2_XLAT_ADDRESS_SHIFT + XLAT_TABLE_ENTRIES_SHIFT) +#define L0_XLAT_ADDRESS_SHIFT (L1_XLAT_ADDRESS_SHIFT + XLAT_TABLE_ENTRIES_SHIFT) /* * AP[1] bit is ignored by hardware and is @@ -188,9 +180,14 @@ void mmap_add_region(unsigned long long base_pa, uintptr_t base_va, size_t size, unsigned int attr); void mmap_add(const mmap_region_t *mm); +#ifdef AARCH32 +/* AArch32 specific translation table API */ +void enable_mmu_secure(uint32_t flags); +#else /* AArch64 specific translation table APIs */ void enable_mmu_el1(unsigned int flags); void enable_mmu_el3(unsigned int flags); +#endif /* AARCH32 */ #endif /*__ASSEMBLY__*/ #endif /* __XLAT_TABLES_H__ */ diff --git a/include/plat/arm/common/arm_def.h b/include/plat/arm/common/arm_def.h index 0b3e66b1..4a4dfd40 100644 --- a/include/plat/arm/common/arm_def.h +++ b/include/plat/arm/common/arm_def.h @@ -321,9 +321,12 @@ # error "Unsupported ARM_TSP_RAM_LOCATION_ID value" #endif +/* BL32 is mandatory in AArch32 */ +#ifndef AARCH32 #ifdef SPD_none #undef BL32_BASE #endif /* SPD_none */ +#endif /******************************************************************************* * FWU Images: NS_BL1U, BL2U & NS_BL2U defines. diff --git a/include/plat/arm/common/plat_arm.h b/include/plat/arm/common/plat_arm.h index 0ffdb5c0..25aab249 100644 --- a/include/plat/arm/common/plat_arm.h +++ b/include/plat/arm/common/plat_arm.h @@ -167,6 +167,9 @@ void arm_bl31_plat_arch_setup(void); /* TSP utility functions */ void arm_tsp_early_platform_setup(void); +/* SP_MIN utility functions */ +void arm_sp_min_early_platform_setup(void); + /* FIP TOC validity check */ int arm_io_is_toc_valid(void); diff --git a/lib/aarch32/cache_helpers.S b/lib/aarch32/cache_helpers.S new file mode 100644 index 00000000..d0e5cd06 --- /dev/null +++ b/lib/aarch32/cache_helpers.S @@ -0,0 +1,237 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <arch.h> +#include <asm_macros.S> + + .globl flush_dcache_range + .globl clean_dcache_range + .globl inv_dcache_range + .globl dcsw_op_louis + .globl dcsw_op_all + .globl dcsw_op_level1 + .globl dcsw_op_level2 + .globl dcsw_op_level3 + +/* + * This macro can be used for implementing various data cache operations `op` + */ +.macro do_dcache_maintenance_by_mva op, coproc, opc1, CRn, CRm, opc2 + dcache_line_size r2, r3 + add r1, r0, r1 + sub r3, r2, #1 + bic r0, r0, r3 +loop_\op: + stcopr r0, \coproc, \opc1, \CRn, \CRm, \opc2 + add r0, r0, r2 + cmp r0, r1 + blo loop_\op + dsb sy + bx lr +.endm + + /* ------------------------------------------ + * Clean+Invalidate from base address till + * size. 'r0' = addr, 'r1' = size + * ------------------------------------------ + */ +func flush_dcache_range + do_dcache_maintenance_by_mva cimvac, DCCIMVAC +endfunc flush_dcache_range + + /* ------------------------------------------ + * Clean from base address till size. + * 'r0' = addr, 'r1' = size + * ------------------------------------------ + */ +func clean_dcache_range + do_dcache_maintenance_by_mva cmvac, DCCMVAC +endfunc clean_dcache_range + + /* ------------------------------------------ + * Invalidate from base address till + * size. 'r0' = addr, 'r1' = size + * ------------------------------------------ + */ +func inv_dcache_range + do_dcache_maintenance_by_mva imvac, DCIMVAC +endfunc inv_dcache_range + + /* ---------------------------------------------------------------- + * Data cache operations by set/way to the level specified + * + * The main function, do_dcsw_op requires: + * r0: The operation type (DC_OP_ISW, DC_OP_CISW, DC_OP_CSW), + * as defined in arch.h + * r1: The cache level to begin operation from + * r2: clidr_el1 + * r3: The last cache level to operate on + * and will carry out the operation on each data cache from level 0 + * to the level in r3 in sequence + * + * The dcsw_op macro sets up the r2 and r3 parameters based on + * clidr_el1 cache information before invoking the main function + * ---------------------------------------------------------------- + */ + + .macro dcsw_op shift, fw, ls + ldcopr r2, CLIDR + ubfx r3, r2, \shift, \fw + lsl r3, r3, \ls + mov r1, #0 + b do_dcsw_op + .endm + +func do_dcsw_op + push {r4-r12,lr} + adr r11, dcsw_loop_table // compute cache op based on the operation type + add r6, r11, r0, lsl #3 // cache op is 2x32-bit instructions +loop1: + add r10, r1, r1, LSR #1 // Work out 3x current cache level + mov r12, r2, LSR r10 // extract cache type bits from clidr + and r12, r12, #7 // mask the bits for current cache only + cmp r12, #2 // see what cache we have at this level + blt level_done // no cache or only instruction cache at this level + + stcopr r1, CSSELR // select current cache level in csselr + isb // isb to sych the new cssr&csidr + ldcopr r12, CCSIDR // read the new ccsidr + and r10, r12, #7 // extract the length of the cache lines + add r10, r10, #4 // add 4 (r10 = line length offset) + ubfx r4, r12, #3, #10 // r4 = maximum way number (right aligned) + clz r5, r4 // r5 = the bit position of the way size increment + mov r9, r4 // r9 working copy of the aligned max way number + +loop2: + ubfx r7, r12, #13, #15 // r7 = max set number (right aligned) + +loop3: + orr r0, r1, r9, LSL r5 // factor in the way number and cache level into r0 + orr r0, r0, r7, LSL r10 // factor in the set number + + blx r6 + subs r7, r7, #1 // decrement the set number + bge loop3 + subs r9, r9, #1 // decrement the way number + bge loop2 +level_done: + add r1, r1, #2 // increment the cache number + cmp r3, r1 + dsb sy // ensure completion of previous cache maintenance instruction + bgt loop1 + + mov r6, #0 + stcopr r6, CSSELR //select cache level 0 in csselr + dsb sy + isb + pop {r4-r12,pc} + +dcsw_loop_table: + stcopr r0, DCISW + bx lr + stcopr r0, DCCISW + bx lr + stcopr r0, DCCSW + bx lr + +endfunc do_dcsw_op + + /* --------------------------------------------------------------- + * Data cache operations by set/way till PoU. + * + * The function requires : + * r0: The operation type (DC_OP_ISW, DC_OP_CISW, DC_OP_CSW), + * as defined in arch.h + * --------------------------------------------------------------- + */ +func dcsw_op_louis + dcsw_op #LOUIS_SHIFT, #CLIDR_FIELD_WIDTH, #LEVEL_SHIFT +endfunc dcsw_op_louis + + /* --------------------------------------------------------------- + * Data cache operations by set/way till PoC. + * + * The function requires : + * r0: The operation type (DC_OP_ISW, DC_OP_CISW, DC_OP_CSW), + * as defined in arch.h + * --------------------------------------------------------------- + */ +func dcsw_op_all + dcsw_op #LOC_SHIFT, #CLIDR_FIELD_WIDTH, #LEVEL_SHIFT +endfunc dcsw_op_all + + + /* --------------------------------------------------------------- + * Helper macro for data cache operations by set/way for the + * level specified + * --------------------------------------------------------------- + */ + .macro dcsw_op_level level + ldcopr r2, CLIDR + mov r3, \level + sub r1, r3, #2 + b do_dcsw_op + .endm + + /* --------------------------------------------------------------- + * Data cache operations by set/way for level 1 cache + * + * The main function, do_dcsw_op requires: + * r0: The operation type (DC_OP_ISW, DC_OP_CISW, DC_OP_CSW), + * as defined in arch.h + * --------------------------------------------------------------- + */ +func dcsw_op_level1 + dcsw_op_level #(1 << LEVEL_SHIFT) +endfunc dcsw_op_level1 + + /* --------------------------------------------------------------- + * Data cache operations by set/way for level 2 cache + * + * The main function, do_dcsw_op requires: + * r0: The operation type (DC_OP_ISW, DC_OP_CISW, DC_OP_CSW), + * as defined in arch.h + * --------------------------------------------------------------- + */ +func dcsw_op_level2 + dcsw_op_level #(2 << LEVEL_SHIFT) +endfunc dcsw_op_level2 + + /* --------------------------------------------------------------- + * Data cache operations by set/way for level 3 cache + * + * The main function, do_dcsw_op requires: + * r0: The operation type (DC_OP_ISW, DC_OP_CISW, DC_OP_CSW), + * as defined in arch.h + * --------------------------------------------------------------- + */ +func dcsw_op_level3 + dcsw_op_level #(3 << LEVEL_SHIFT) +endfunc dcsw_op_level3 diff --git a/lib/aarch32/misc_helpers.S b/lib/aarch32/misc_helpers.S new file mode 100644 index 00000000..63ac1a7e --- /dev/null +++ b/lib/aarch32/misc_helpers.S @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <arch.h> +#include <asm_macros.S> +#include <assert_macros.S> + + .globl zeromem + +/* ----------------------------------------------------------------------- + * void zeromem(void *mem, unsigned int length); + * + * Initialise a memory region to 0. + * The memory address and length must be 4-byte aligned. + * ----------------------------------------------------------------------- + */ +func zeromem +#if ASM_ASSERTION + tst r0, #0x3 + ASM_ASSERT(eq) + tst r1, #0x3 + ASM_ASSERT(eq) +#endif + add r2, r0, r1 + mov r1, #0 +z_loop: + cmp r2, r0 + beq z_end + str r1, [r0], #4 + b z_loop +z_end: + bx lr +endfunc zeromem diff --git a/lib/cpus/aarch32/aem_generic.S b/lib/cpus/aarch32/aem_generic.S new file mode 100644 index 00000000..10ea4e47 --- /dev/null +++ b/lib/cpus/aarch32/aem_generic.S @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <aem_generic.h> +#include <arch.h> +#include <asm_macros.S> +#include <assert_macros.S> +#include <cpu_macros.S> + +func aem_generic_core_pwr_dwn + /* Assert if cache is enabled */ +#if ASM_ASSERTION + ldcopr r0, SCTLR + tst r0, #SCTLR_C_BIT + ASM_ASSERT(eq) +#endif + /* --------------------------------------------- + * Flush L1 cache to PoU. + * --------------------------------------------- + */ + mov r0, #DC_OP_CISW + b dcsw_op_louis +endfunc aem_generic_core_pwr_dwn + + +func aem_generic_cluster_pwr_dwn + /* Assert if cache is enabled */ +#if ASM_ASSERTION + ldcopr r0, SCTLR + tst r0, #SCTLR_C_BIT + ASM_ASSERT(eq) +#endif + /* --------------------------------------------- + * Flush L1 and L2 caches to PoC. + * --------------------------------------------- + */ + mov r0, #DC_OP_CISW + b dcsw_op_all +endfunc aem_generic_cluster_pwr_dwn + +/* cpu_ops for Base AEM FVP */ +declare_cpu_ops aem_generic, BASE_AEM_MIDR, 1 diff --git a/lib/cpus/aarch32/cpu_helpers.S b/lib/cpus/aarch32/cpu_helpers.S new file mode 100644 index 00000000..927a6f50 --- /dev/null +++ b/lib/cpus/aarch32/cpu_helpers.S @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <arch.h> +#include <asm_macros.S> +#include <assert_macros.S> +#include <cpu_data.h> +#include <cpu_macros.S> + + /* + * The reset handler common to all platforms. After a matching + * cpu_ops structure entry is found, the correponding reset_handler + * in the cpu_ops is invoked. The reset handler is invoked very early + * in the boot sequence and it is assumed that we can clobber r0 - r10 + * without the need to follow AAPCS. + * Clobbers: r0 - r10 + */ + .globl reset_handler +func reset_handler + mov r10, lr + + /* The plat_reset_handler can clobber r0 - r9 */ + bl plat_reset_handler + + /* Get the matching cpu_ops pointer (clobbers: r0 - r5) */ + bl get_cpu_ops_ptr + +#if ASM_ASSERTION + cmp r0, #0 + ASM_ASSERT(ne) +#endif + + /* Get the cpu_ops reset handler */ + ldr r1, [r0, #CPU_RESET_FUNC] + cmp r1, #0 + mov lr, r10 + bxne r1 + bx lr +endfunc reset_handler + + /* + * The prepare core power down function for all platforms. After + * the cpu_ops pointer is retrieved from cpu_data, the corresponding + * pwr_dwn_core in the cpu_ops is invoked. Follows AAPCS. + */ + .globl prepare_core_pwr_dwn +func prepare_core_pwr_dwn + push {lr} + bl _cpu_data + pop {lr} + + ldr r1, [r0, #CPU_DATA_CPU_OPS_PTR] +#if ASM_ASSERTION + cmp r1, #0 + ASM_ASSERT(ne) +#endif + + /* Get the cpu_ops core_pwr_dwn handler */ + ldr r0, [r1, #CPU_PWR_DWN_CORE] + bx r0 +endfunc prepare_core_pwr_dwn + + /* + * The prepare cluster power down function for all platforms. After + * the cpu_ops pointer is retrieved from cpu_data, the corresponding + * pwr_dwn_cluster in the cpu_ops is invoked. Follows AAPCS. + */ + .globl prepare_cluster_pwr_dwn +func prepare_cluster_pwr_dwn + push {lr} + bl _cpu_data + pop {lr} + + ldr r1, [r0, #CPU_DATA_CPU_OPS_PTR] +#if ASM_ASSERTION + cmp r1, #0 + ASM_ASSERT(ne) +#endif + + /* Get the cpu_ops cluster_pwr_dwn handler */ + ldr r0, [r1, #CPU_PWR_DWN_CLUSTER] + bx r0 +endfunc prepare_cluster_pwr_dwn + + /* + * Initializes the cpu_ops_ptr if not already initialized + * in cpu_data. This must only be called after the data cache + * is enabled. AAPCS is followed. + */ + .globl init_cpu_ops +func init_cpu_ops + push {r4 - r6, lr} + bl _cpu_data + mov r6, r0 + ldr r1, [r0, #CPU_DATA_CPU_OPS_PTR] + cmp r1, #0 + bne 1f + bl get_cpu_ops_ptr +#if ASM_ASSERTION + cmp r0, #0 + ASM_ASSERT(ne) +#endif + str r0, [r6, #CPU_DATA_CPU_OPS_PTR]! +1: + pop {r4 - r6, pc} +endfunc init_cpu_ops + + /* + * The below function returns the cpu_ops structure matching the + * midr of the core. It reads the MIDR and finds the matching + * entry in cpu_ops entries. Only the implementation and part number + * are used to match the entries. + * Return : + * r0 - The matching cpu_ops pointer on Success + * r0 - 0 on failure. + * Clobbers: r0 - r5 + */ + .globl get_cpu_ops_ptr +func get_cpu_ops_ptr + /* Get the cpu_ops start and end locations */ + ldr r4, =(__CPU_OPS_START__ + CPU_MIDR) + ldr r5, =(__CPU_OPS_END__ + CPU_MIDR) + + /* Initialize the return parameter */ + mov r0, #0 + + /* Read the MIDR_EL1 */ + ldcopr r2, MIDR + ldr r3, =CPU_IMPL_PN_MASK + + /* Retain only the implementation and part number using mask */ + and r2, r2, r3 +1: + /* Check if we have reached end of list */ + cmp r4, r5 + bge error_exit + + /* load the midr from the cpu_ops */ + ldr r1, [r4], #CPU_OPS_SIZE + and r1, r1, r3 + + /* Check if midr matches to midr of this core */ + cmp r1, r2 + bne 1b + + /* Subtract the increment and offset to get the cpu-ops pointer */ + sub r0, r4, #(CPU_OPS_SIZE + CPU_MIDR) +error_exit: + bx lr +endfunc get_cpu_ops_ptr diff --git a/lib/el3_runtime/aarch32/context_mgmt.c b/lib/el3_runtime/aarch32/context_mgmt.c new file mode 100644 index 00000000..6915ded7 --- /dev/null +++ b/lib/el3_runtime/aarch32/context_mgmt.c @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <arch.h> +#include <arch_helpers.h> +#include <assert.h> +#include <bl_common.h> +#include <context.h> +#include <context_mgmt.h> +#include <platform.h> +#include <platform_def.h> +#include <smcc_helpers.h> +#include <string.h> + +/******************************************************************************* + * Context management library initialisation routine. This library is used by + * runtime services to share pointers to 'cpu_context' structures for the secure + * and non-secure states. Management of the structures and their associated + * memory is not done by the context management library e.g. the PSCI service + * manages the cpu context used for entry from and exit to the non-secure state. + * The Secure payload manages the context(s) corresponding to the secure state. + * It also uses this library to get access to the non-secure + * state cpu context pointers. + ******************************************************************************/ +void cm_init(void) +{ + /* + * The context management library has only global data to initialize, but + * that will be done when the BSS is zeroed out + */ +} + +/******************************************************************************* + * The following function initializes the cpu_context 'ctx' for + * first use, and sets the initial entrypoint state as specified by the + * entry_point_info structure. + * + * The security state to initialize is determined by the SECURE attribute + * of the entry_point_info. The function returns a pointer to the initialized + * context and sets this as the next context to return to. + * + * The EE and ST attributes are used to configure the endianness and secure + * timer availability for the new execution context. + * + * To prepare the register state for entry call cm_prepare_el3_exit() and + * el3_exit(). For Secure-EL1 cm_prepare_el3_exit() is equivalent to + * cm_e1_sysreg_context_restore(). + ******************************************************************************/ +static void cm_init_context_common(cpu_context_t *ctx, const entry_point_info_t *ep) +{ + unsigned int security_state; + uint32_t scr, sctlr; + regs_t *reg_ctx; + + assert(ctx); + + security_state = GET_SECURITY_STATE(ep->h.attr); + + /* Clear any residual register values from the context */ + memset(ctx, 0, sizeof(*ctx)); + + /* + * Base the context SCR on the current value, adjust for entry point + * specific requirements + */ + scr = read_scr(); + scr &= ~(SCR_NS_BIT | SCR_HCE_BIT); + + if (security_state != SECURE) + scr |= SCR_NS_BIT; + + /* + * Set up SCTLR for the Non Secure context. + * EE bit is taken from the entrypoint attributes + * M, C and I bits must be zero (as required by PSCI specification) + * + * The target exception level is based on the spsr mode requested. + * If execution is requested to hyp mode, HVC is enabled + * via SCR.HCE. + * + * Always compute the SCTLR_EL1 value and save in the cpu_context + * - the HYP registers are set up by cm_preapre_ns_entry() as they + * are not part of the stored cpu_context + * + * TODO: In debug builds the spsr should be validated and checked + * against the CPU support, security state, endianness and pc + */ + if (security_state != SECURE) { + sctlr = EP_GET_EE(ep->h.attr) ? SCTLR_EE_BIT : 0; + sctlr |= SCTLR_RES1; + write_ctx_reg(reg_ctx, CTX_NS_SCTLR, sctlr); + } + + if (GET_M32(ep->spsr) == MODE32_hyp) + scr |= SCR_HCE_BIT; + + reg_ctx = get_regs_ctx(ctx); + + write_ctx_reg(reg_ctx, CTX_SCR, scr); + write_ctx_reg(reg_ctx, CTX_LR, ep->pc); + write_ctx_reg(reg_ctx, CTX_SPSR, ep->spsr); + + /* + * Store the r0-r3 value from the entrypoint into the context + * Use memcpy as we are in control of the layout of the structures + */ + memcpy((void *)reg_ctx, (void *)&ep->args, sizeof(aapcs32_params_t)); +} + +/******************************************************************************* + * The following function initializes the cpu_context for a CPU specified by + * its `cpu_idx` for first use, and sets the initial entrypoint state as + * specified by the entry_point_info structure. + ******************************************************************************/ +void cm_init_context_by_index(unsigned int cpu_idx, + const entry_point_info_t *ep) +{ + cpu_context_t *ctx; + ctx = cm_get_context_by_index(cpu_idx, GET_SECURITY_STATE(ep->h.attr)); + cm_init_context_common(ctx, ep); +} + +/******************************************************************************* + * The following function initializes the cpu_context for the current CPU + * for first use, and sets the initial entrypoint state as specified by the + * entry_point_info structure. + ******************************************************************************/ +void cm_init_my_context(const entry_point_info_t *ep) +{ + cpu_context_t *ctx; + ctx = cm_get_context(GET_SECURITY_STATE(ep->h.attr)); + cm_init_context_common(ctx, ep); +} + +/******************************************************************************* + * Prepare the CPU system registers for first entry into secure or normal world + * + * If execution is requested to hyp mode, HSCTLR is initialized + * If execution is requested to non-secure PL1, and the CPU supports + * HYP mode then HYP mode is disabled by configuring all necessary HYP mode + * registers. + ******************************************************************************/ +void cm_prepare_el3_exit(uint32_t security_state) +{ + uint32_t sctlr, scr, hcptr; + cpu_context_t *ctx = cm_get_context(security_state); + + assert(ctx); + + if (security_state == NON_SECURE) { + scr = read_ctx_reg(get_regs_ctx(ctx), CTX_SCR); + if (scr & SCR_HCE_BIT) { + /* Use SCTLR value to initialize HSCTLR */ + sctlr = read_ctx_reg(get_regs_ctx(ctx), + CTX_NS_SCTLR); + sctlr |= HSCTLR_RES1; + /* Temporarily set the NS bit to access HSCTLR */ + write_scr(read_scr() | SCR_NS_BIT); + /* + * Make sure the write to SCR is complete so that + * we can access HSCTLR + */ + isb(); + write_hsctlr(sctlr); + isb(); + + write_scr(read_scr() & ~SCR_NS_BIT); + isb(); + } else if (read_id_pfr1() & + (ID_PFR1_VIRTEXT_MASK << ID_PFR1_VIRTEXT_SHIFT)) { + /* Set the NS bit to access HCR, HCPTR, CNTHCTL, VPIDR, VMPIDR */ + write_scr(read_scr() | SCR_NS_BIT); + isb(); + + /* PL2 present but unused, need to disable safely */ + write_hcr(0); + + /* HSCTLR : can be ignored when bypassing */ + + /* HCPTR : disable all traps TCPAC, TTA, TCP */ + hcptr = read_hcptr(); + hcptr &= ~(TCPAC_BIT | TTA_BIT | TCP11_BIT | TCP10_BIT); + write_hcptr(hcptr); + + /* Enable EL1 access to timer */ + write_cnthctl(PL1PCEN_BIT | PL1PCTEN_BIT); + + /* Reset CNTVOFF_EL2 */ + write64_cntvoff(0); + + /* Set VPIDR, VMPIDR to match MIDR, MPIDR */ + write_vpidr(read_midr()); + write_vmpidr(read_mpidr()); + + /* + * Reset VTTBR. + * Needed because cache maintenance operations depend on + * the VMID even when non-secure EL1&0 stage 2 address + * translation are disabled. + */ + write64_vttbr(0); + isb(); + + write_scr(read_scr() & ~SCR_NS_BIT); + isb(); + } + } +} diff --git a/lib/el3_runtime/aarch32/cpu_data.S b/lib/el3_runtime/aarch32/cpu_data.S new file mode 100644 index 00000000..b97911fb --- /dev/null +++ b/lib/el3_runtime/aarch32/cpu_data.S @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <asm_macros.S> +#include <cpu_data.h> + + .globl _cpu_data + .globl _cpu_data_by_index + +/* ----------------------------------------------------------------- + * cpu_data_t *_cpu_data(void) + * + * Return the cpu_data structure for the current CPU. + * ----------------------------------------------------------------- + */ +func _cpu_data + push {lr} + bl plat_my_core_pos + pop {lr} + b _cpu_data_by_index +endfunc _cpu_data + +/* ----------------------------------------------------------------- + * cpu_data_t *_cpu_data_by_index(uint32_t cpu_index) + * + * Return the cpu_data structure for the CPU with given linear index + * + * This can be called without a valid stack. + * clobbers: r0, r1 + * ----------------------------------------------------------------- + */ +func _cpu_data_by_index + ldr r1, =percpu_data + add r0, r1, r0, LSL #CPU_DATA_LOG2SIZE + bx lr +endfunc _cpu_data_by_index diff --git a/lib/locks/exclusive/aarch32/spinlock.S b/lib/locks/exclusive/aarch32/spinlock.S new file mode 100644 index 00000000..f3a2bc36 --- /dev/null +++ b/lib/locks/exclusive/aarch32/spinlock.S @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <asm_macros.S> + + .globl spin_lock + .globl spin_unlock + + +func spin_lock + mov r2, #1 +1: + ldrex r1, [r0] + cmp r1, #0 + wfene + strexeq r1, r2, [r0] + cmpeq r1, #0 + bne 1b + dmb + bx lr +endfunc spin_lock + + +func spin_unlock + mov r1, #0 + stl r1, [r0] + bx lr +endfunc spin_unlock diff --git a/lib/locks/exclusive/aarch64/spinlock.S b/lib/locks/exclusive/aarch64/spinlock.S new file mode 100644 index 00000000..1ca59123 --- /dev/null +++ b/lib/locks/exclusive/aarch64/spinlock.S @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2013-2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <asm_macros.S> + + .globl spin_lock + .globl spin_unlock + + +func spin_lock + mov w2, #1 + sevl +l1: wfe +l2: ldaxr w1, [x0] + cbnz w1, l1 + stxr w1, w2, [x0] + cbnz w1, l2 + ret +endfunc spin_lock + + +func spin_unlock + stlr wzr, [x0] + ret +endfunc spin_unlock diff --git a/lib/locks/exclusive/spinlock.S b/lib/locks/exclusive/spinlock.S index 772f14e9..9c945f9b 100644 --- a/lib/locks/exclusive/spinlock.S +++ b/lib/locks/exclusive/spinlock.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2016, 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,25 +28,6 @@ * POSSIBILITY OF SUCH DAMAGE. */ -#include <asm_macros.S> - - .globl spin_lock - .globl spin_unlock - - -func spin_lock - mov w2, #1 - sevl -l1: wfe -l2: ldaxr w1, [x0] - cbnz w1, l1 - stxr w1, w2, [x0] - cbnz w1, l2 - ret -endfunc spin_lock - - -func spin_unlock - stlr wzr, [x0] - ret -endfunc spin_unlock +#if !ERROR_DEPRECATED +#include "./aarch64/spinlock.S" +#endif diff --git a/lib/psci/aarch32/psci_helpers.S b/lib/psci/aarch32/psci_helpers.S new file mode 100644 index 00000000..36d5d7d9 --- /dev/null +++ b/lib/psci/aarch32/psci_helpers.S @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <asm_macros.S> +#include <platform_def.h> +#include <psci.h> + + .globl psci_do_pwrdown_cache_maintenance + .globl psci_do_pwrup_cache_maintenance + .globl psci_power_down_wfi + +/* ----------------------------------------------------------------------- + * void psci_do_pwrdown_cache_maintenance(unsigned int power level); + * + * This function performs cache maintenance for the specified power + * level. The levels of cache affected are determined by the power + * level which is passed as the argument i.e. level 0 results + * in a flush of the L1 cache. Both the L1 and L2 caches are flushed + * for a higher power level. + * + * Additionally, this function also ensures that stack memory is correctly + * flushed out to avoid coherency issues due to a change in its memory + * attributes after the data cache is disabled. + * ----------------------------------------------------------------------- + */ +func psci_do_pwrdown_cache_maintenance + push {r4, lr} + + /* ---------------------------------------------- + * Turn OFF cache and do stack maintenance + * prior to cpu operations . This sequence is + * different from AArch64 because in AArch32 the + * assembler routines for cpu operations utilize + * the stack whereas in AArch64 it doesn't. + * ---------------------------------------------- + */ + mov r4, r0 + bl do_stack_maintenance + + /* --------------------------------------------- + * Determine how many levels of cache will be + * subject to cache maintenance. Power level + * 0 implies that only the cpu is being powered + * down. Only the L1 data cache needs to be + * flushed to the PoU in this case. For a higher + * power level we are assuming that a flush + * of L1 data and L2 unified cache is enough. + * This information should be provided by the + * platform. + * --------------------------------------------- + */ + cmp r4, #PSCI_CPU_PWR_LVL + pop {r4,lr} + + beq prepare_core_pwr_dwn + b prepare_cluster_pwr_dwn +endfunc psci_do_pwrdown_cache_maintenance + + +/* ----------------------------------------------------------------------- + * void psci_do_pwrup_cache_maintenance(void); + * + * This function performs cache maintenance after this cpu is powered up. + * Currently, this involves managing the used stack memory before turning + * on the data cache. + * ----------------------------------------------------------------------- + */ +func psci_do_pwrup_cache_maintenance + push {lr} + + /* --------------------------------------------- + * Ensure any inflight stack writes have made it + * to main memory. + * --------------------------------------------- + */ + dmb st + + /* --------------------------------------------- + * Calculate and store the size of the used + * stack memory in r1. Calculate and store the + * stack base address in r0. + * --------------------------------------------- + */ + bl plat_get_my_stack + mov r1, sp + sub r1, r0, r1 + mov r0, sp + bl inv_dcache_range + + /* --------------------------------------------- + * Enable the data cache. + * --------------------------------------------- + */ + ldcopr r0, SCTLR + orr r0, r0, #SCTLR_C_BIT + stcopr r0, SCTLR + isb + + pop {pc} +endfunc psci_do_pwrup_cache_maintenance + + /* --------------------------------------------- + * void do_stack_maintenance(void) + * Do stack maintenance by flushing the used + * stack to the main memory and invalidating the + * remainder. + * --------------------------------------------- + */ +func do_stack_maintenance + push {r4, lr} + bl plat_get_my_stack + + /* Turn off the D-cache */ + ldcopr r1, SCTLR + bic r1, #SCTLR_C_BIT + stcopr r1, SCTLR + isb + + /* --------------------------------------------- + * Calculate and store the size of the used + * stack memory in r1. + * --------------------------------------------- + */ + mov r4, r0 + mov r1, sp + sub r1, r0, r1 + mov r0, sp + bl flush_dcache_range + + /* --------------------------------------------- + * Calculate and store the size of the unused + * stack memory in r1. Calculate and store the + * stack base address in r0. + * --------------------------------------------- + */ + sub r0, r4, #PLATFORM_STACK_SIZE + sub r1, sp, r0 + bl inv_dcache_range + + pop {r4, pc} +endfunc do_stack_maintenance + +/* ----------------------------------------------------------------------- + * This function is called to indicate to the power controller that it + * is safe to power down this cpu. It should not exit the wfi and will + * be released from reset upon power up. + * ----------------------------------------------------------------------- + */ +func psci_power_down_wfi + dsb sy // ensure write buffer empty + wfi + bl plat_panic_handler +endfunc psci_power_down_wfi diff --git a/lib/psci/psci_common.c b/lib/psci/psci_common.c index e87e8c05..68cdd6eb 100644 --- a/lib/psci/psci_common.c +++ b/lib/psci/psci_common.c @@ -592,6 +592,53 @@ int psci_validate_mpidr(u_register_t mpidr) * This function determines the full entrypoint information for the requested * PSCI entrypoint on power on/resume and returns it. ******************************************************************************/ +#ifdef AARCH32 +static int psci_get_ns_ep_info(entry_point_info_t *ep, + uintptr_t entrypoint, + u_register_t context_id) +{ + u_register_t ep_attr; + unsigned int aif, ee, mode; + u_register_t scr = read_scr(); + u_register_t ns_sctlr, sctlr; + + /* Switch to non secure state */ + write_scr(scr | SCR_NS_BIT); + isb(); + ns_sctlr = read_sctlr(); + + sctlr = scr & SCR_HCE_BIT ? read_hsctlr() : ns_sctlr; + + /* Return to original state */ + write_scr(scr); + isb(); + ee = 0; + + ep_attr = NON_SECURE | EP_ST_DISABLE; + if (sctlr & SCTLR_EE_BIT) { + ep_attr |= EP_EE_BIG; + ee = 1; + } + SET_PARAM_HEAD(ep, PARAM_EP, VERSION_1, ep_attr); + + ep->pc = entrypoint; + memset(&ep->args, 0, sizeof(ep->args)); + ep->args.arg0 = context_id; + + mode = scr & SCR_HCE_BIT ? MODE32_hyp : MODE32_svc; + + /* + * TODO: Choose async. exception bits if HYP mode is not + * implemented according to the values of SCR.{AW, FW} bits + */ + aif = SPSR_ABT_BIT | SPSR_IRQ_BIT | SPSR_FIQ_BIT; + + ep->spsr = SPSR_MODE32(mode, entrypoint & 0x1, ee, aif); + + return PSCI_E_SUCCESS; +} + +#else static int psci_get_ns_ep_info(entry_point_info_t *ep, uintptr_t entrypoint, u_register_t context_id) @@ -646,6 +693,7 @@ static int psci_get_ns_ep_info(entry_point_info_t *ep, return PSCI_E_SUCCESS; } +#endif /******************************************************************************* * This function validates the entrypoint with the platform layer if the diff --git a/lib/psci/psci_lib.mk b/lib/psci/psci_lib.mk index 662e14a2..8daa8318 100644 --- a/lib/psci/psci_lib.mk +++ b/lib/psci/psci_lib.mk @@ -29,11 +29,10 @@ # PSCI_LIB_SOURCES := lib/el3_runtime/cpu_data_array.c \ - lib/el3_runtime/aarch64/context.S \ - lib/el3_runtime/aarch64/cpu_data.S \ - lib/el3_runtime/aarch64/context_mgmt.c \ - lib/cpus/aarch64/cpu_helpers.S \ - lib/locks/exclusive/spinlock.S \ + lib/el3_runtime/${ARCH}/cpu_data.S \ + lib/el3_runtime/${ARCH}/context_mgmt.c \ + lib/cpus/${ARCH}/cpu_helpers.S \ + lib/locks/exclusive/${ARCH}/spinlock.S \ lib/psci/psci_off.c \ lib/psci/psci_on.c \ lib/psci/psci_suspend.c \ @@ -41,7 +40,11 @@ PSCI_LIB_SOURCES := lib/el3_runtime/cpu_data_array.c \ lib/psci/psci_main.c \ lib/psci/psci_setup.c \ lib/psci/psci_system_off.c \ - lib/psci/aarch64/psci_helpers.S + lib/psci/${ARCH}/psci_helpers.S + +ifeq (${ARCH}, aarch64) +PSCI_LIB_SOURCES += lib/el3_runtime/aarch64/context.S +endif ifeq (${USE_COHERENT_MEM}, 1) PSCI_LIB_SOURCES += lib/locks/bakery/bakery_lock_coherent.c diff --git a/lib/psci/psci_setup.c b/lib/psci/psci_setup.c index d35e0001..20d06352 100644 --- a/lib/psci/psci_setup.c +++ b/lib/psci/psci_setup.c @@ -278,3 +278,15 @@ void psci_arch_setup(void) /* Initialize the cpu_ops pointer. */ init_cpu_ops(); } + +/****************************************************************************** + * PSCI Library interface to initialize the cpu context for the next non + * secure image during cold boot. The relevant registers in the cpu context + * need to be retrieved and programmed on return from this interface. + *****************************************************************************/ +void psci_prepare_next_non_secure_ctx(entry_point_info_t *next_image_info) +{ + assert(GET_SECURITY_STATE(next_image_info->h.attr) == NON_SECURE); + cm_init_my_context(next_image_info); + cm_prepare_el3_exit(NON_SECURE); +} diff --git a/lib/xlat_tables/aarch32/xlat_tables.c b/lib/xlat_tables/aarch32/xlat_tables.c new file mode 100644 index 00000000..d70a6ef5 --- /dev/null +++ b/lib/xlat_tables/aarch32/xlat_tables.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <arch.h> +#include <arch_helpers.h> +#include <assert.h> +#include <cassert.h> +#include <platform_def.h> +#include <utils.h> +#include <xlat_tables.h> +#include "../xlat_tables_private.h" + +/* + * Each platform can define the size of the virtual address space, which is + * defined in ADDR_SPACE_SIZE. TTBCR.TxSZ is calculated as 32 minus the width + * of said address space. The value of TTBCR.TxSZ must be in the range 0 to + * 7 [1], which means that the virtual address space width must be in the range + * 32 to 25 bits. + * + * Here we calculate the initial lookup level from the value of ADDR_SPACE_SIZE. + * For a 4 KB page size, level 1 supports virtual address spaces of widths 32 + * to 31 bits, and level 2 from 30 to 25. Wider or narrower address spaces are + * not supported. As a result, level 3 cannot be used as initial lookup level + * with 4 KB granularity [1]. + * + * For example, for a 31-bit address space (i.e. ADDR_SPACE_SIZE == 1 << 31), + * TTBCR.TxSZ will be programmed to (32 - 31) = 1. According to Table G4-5 in + * the ARM ARM, the initial lookup level for such an address space is 1. + * + * See the ARMv8-A Architecture Reference Manual (DDI 0487A.j) for more + * information: + * [1] Section G4.6.5 + */ + +#if ADDR_SPACE_SIZE > (1ULL << (32 - TTBCR_TxSZ_MIN)) + +# error "ADDR_SPACE_SIZE is too big." + +#elif ADDR_SPACE_SIZE > (1 << L1_XLAT_ADDRESS_SHIFT) + +# define XLAT_TABLE_LEVEL_BASE 1 +# define NUM_BASE_LEVEL_ENTRIES (ADDR_SPACE_SIZE >> L1_XLAT_ADDRESS_SHIFT) + +#elif ADDR_SPACE_SIZE >= (1 << (32 - TTBCR_TxSZ_MAX)) + +# define XLAT_TABLE_LEVEL_BASE 2 +# define NUM_BASE_LEVEL_ENTRIES (ADDR_SPACE_SIZE >> L2_XLAT_ADDRESS_SHIFT) + +#else + +# error "ADDR_SPACE_SIZE is too small." + +#endif + +static uint64_t base_xlation_table[NUM_BASE_LEVEL_ENTRIES] + __aligned(NUM_BASE_LEVEL_ENTRIES * sizeof(uint64_t)); + +void init_xlat_tables(void) +{ + unsigned long long max_pa; + uintptr_t max_va; + print_mmap(); + init_xlation_table(0, base_xlation_table, XLAT_TABLE_LEVEL_BASE, + &max_va, &max_pa); + assert(max_va < ADDR_SPACE_SIZE); +} + +/******************************************************************************* + * Function for enabling the MMU in Secure PL1, assuming that the + * page-tables have already been created. + ******************************************************************************/ +void enable_mmu_secure(unsigned int flags) +{ + unsigned int mair0, ttbcr, sctlr; + uint64_t ttbr0; + + assert(IS_IN_SECURE()); + assert((read_sctlr() & SCTLR_M_BIT) == 0); + + /* Set attributes in the right indices of the MAIR */ + mair0 = MAIR0_ATTR_SET(ATTR_DEVICE, ATTR_DEVICE_INDEX); + mair0 |= MAIR0_ATTR_SET(ATTR_IWBWA_OWBWA_NTR, + ATTR_IWBWA_OWBWA_NTR_INDEX); + mair0 |= MAIR0_ATTR_SET(ATTR_NON_CACHEABLE, + ATTR_NON_CACHEABLE_INDEX); + write_mair0(mair0); + + /* Invalidate TLBs at the current exception level */ + tlbiall(); + + /* + * Set TTBCR bits as well. Set TTBR0 table properties as Inner + * & outer WBWA & shareable. Disable TTBR1. + */ + ttbcr = TTBCR_EAE_BIT | + TTBCR_SH0_INNER_SHAREABLE | TTBCR_RGN0_OUTER_WBA | + TTBCR_RGN0_INNER_WBA | + (32 - __builtin_ctzl((uintptr_t)ADDR_SPACE_SIZE)); + ttbcr |= TTBCR_EPD1_BIT; + write_ttbcr(ttbcr); + + /* Set TTBR0 bits as well */ + ttbr0 = (uintptr_t) base_xlation_table; + write64_ttbr0(ttbr0); + write64_ttbr1(0); + + /* + * Ensure all translation table writes have drained + * into memory, the TLB invalidation is complete, + * and translation register writes are committed + * before enabling the MMU + */ + dsb(); + isb(); + + sctlr = read_sctlr(); + sctlr |= SCTLR_WXN_BIT | SCTLR_M_BIT; + + if (flags & DISABLE_DCACHE) + sctlr &= ~SCTLR_C_BIT; + else + sctlr |= SCTLR_C_BIT; + + write_sctlr(sctlr); + + /* Ensure the MMU enable takes effect immediately */ + isb(); +} diff --git a/lib/xlat_tables/aarch64/xlat_tables.c b/lib/xlat_tables/aarch64/xlat_tables.c index f4322375..5b639b7a 100644 --- a/lib/xlat_tables/aarch64/xlat_tables.c +++ b/lib/xlat_tables/aarch64/xlat_tables.c @@ -38,19 +38,55 @@ #include "../xlat_tables_private.h" /* - * The virtual address space size must be a power of two (as set in TCR.T0SZ). - * As we start the initial lookup at level 1, it must also be between 2 GB and - * 512 GB (with the virtual address size therefore 31 to 39 bits). See section - * D4.2.5 in the ARMv8-A Architecture Reference Manual (DDI 0487A.i) for more - * information. + * Each platform can define the size of the virtual address space, which is + * defined in ADDR_SPACE_SIZE. TCR.TxSZ is calculated as 64 minus the width of + * said address space. The value of TCR.TxSZ must be in the range 16 to 39 [1], + * which means that the virtual address space width must be in the range 48 to + * 25 bits. + * + * Here we calculate the initial lookup level from the value of ADDR_SPACE_SIZE. + * For a 4 KB page size, level 0 supports virtual address spaces of widths 48 to + * 40 bits, level 1 from 39 to 31, and level 2 from 30 to 25. Wider or narrower + * address spaces are not supported. As a result, level 3 cannot be used as + * initial lookup level with 4 KB granularity. [2] + * + * For example, for a 35-bit address space (i.e. ADDR_SPACE_SIZE == 1 << 35), + * TCR.TxSZ will be programmed to (64 - 35) = 29. According to Table D4-11 in + * the ARM ARM, the initial lookup level for such an address space is 1. + * + * See the ARMv8-A Architecture Reference Manual (DDI 0487A.j) for more + * information: + * [1] Page 1730: 'Input address size', 'For all translation stages'. + * [2] Section D4.2.5 */ -CASSERT(ADDR_SPACE_SIZE >= (1ull << 31) && ADDR_SPACE_SIZE <= (1ull << 39) && - IS_POWER_OF_TWO(ADDR_SPACE_SIZE), assert_valid_addr_space_size); -#define NUM_L1_ENTRIES (ADDR_SPACE_SIZE >> L1_XLAT_ADDRESS_SHIFT) +#if ADDR_SPACE_SIZE > (1ULL << (64 - TCR_TxSZ_MIN)) + +# error "ADDR_SPACE_SIZE is too big." + +#elif ADDR_SPACE_SIZE > (1ULL << L0_XLAT_ADDRESS_SHIFT) + +# define XLAT_TABLE_LEVEL_BASE 0 +# define NUM_BASE_LEVEL_ENTRIES (ADDR_SPACE_SIZE >> L0_XLAT_ADDRESS_SHIFT) + +#elif ADDR_SPACE_SIZE > (1 << L1_XLAT_ADDRESS_SHIFT) + +# define XLAT_TABLE_LEVEL_BASE 1 +# define NUM_BASE_LEVEL_ENTRIES (ADDR_SPACE_SIZE >> L1_XLAT_ADDRESS_SHIFT) + +#elif ADDR_SPACE_SIZE >= (1 << (64 - TCR_TxSZ_MAX)) + +# define XLAT_TABLE_LEVEL_BASE 2 +# define NUM_BASE_LEVEL_ENTRIES (ADDR_SPACE_SIZE >> L2_XLAT_ADDRESS_SHIFT) + +#else + +# error "ADDR_SPACE_SIZE is too small." + +#endif -static uint64_t l1_xlation_table[NUM_L1_ENTRIES] - __aligned(NUM_L1_ENTRIES * sizeof(uint64_t)); +static uint64_t base_xlation_table[NUM_BASE_LEVEL_ENTRIES] + __aligned(NUM_BASE_LEVEL_ENTRIES * sizeof(uint64_t)); static unsigned long long tcr_ps_bits; @@ -88,7 +124,8 @@ void init_xlat_tables(void) unsigned long long max_pa; uintptr_t max_va; print_mmap(); - init_xlation_table(0, l1_xlation_table, 1, &max_va, &max_pa); + init_xlation_table(0, base_xlation_table, XLAT_TABLE_LEVEL_BASE, + &max_va, &max_pa); tcr_ps_bits = calc_physical_addr_size_bits(max_pa); assert(max_va < ADDR_SPACE_SIZE); } @@ -124,7 +161,8 @@ void init_xlat_tables(void) _tlbi_fct(); \ \ /* Set TCR bits as well. */ \ - /* Inner & outer WBWA & shareable + T0SZ = 32 */ \ + /* Inner & outer WBWA & shareable. */ \ + /* Set T0SZ to (64 - width of virtual address space) */ \ tcr = TCR_SH_INNER_SHAREABLE | TCR_RGN_OUTER_WBA | \ TCR_RGN_INNER_WBA | \ (64 - __builtin_ctzl(ADDR_SPACE_SIZE)); \ @@ -132,7 +170,7 @@ void init_xlat_tables(void) write_tcr_el##_el(tcr); \ \ /* Set TTBR bits as well */ \ - ttbr = (uint64_t) l1_xlation_table; \ + ttbr = (uint64_t) base_xlation_table; \ write_ttbr0_el##_el(ttbr); \ \ /* Ensure all translation table writes have drained */ \ diff --git a/lib/xlat_tables/xlat_tables_common.c b/lib/xlat_tables/xlat_tables_common.c index 33784c2d..ebbc9161 100644 --- a/lib/xlat_tables/xlat_tables_common.c +++ b/lib/xlat_tables/xlat_tables_common.c @@ -199,7 +199,11 @@ static uint64_t mmap_desc(unsigned attr, unsigned long long addr_pa, int mem_type; desc = addr_pa; - desc |= (level == 3) ? TABLE_DESC : BLOCK_DESC; + /* + * There are different translation table descriptors for level 3 and the + * rest. + */ + desc |= (level == XLAT_TABLE_LEVEL_MAX) ? PAGE_DESC : BLOCK_DESC; desc |= (attr & MT_NS) ? LOWER_ATTRS(NS) : 0; desc |= (attr & MT_RW) ? LOWER_ATTRS(AP_RW) : LOWER_ATTRS(AP_RO); desc |= LOWER_ATTRS(ACCESS_FLAG); @@ -289,17 +293,17 @@ static int mmap_region_attr(mmap_region_t *mm, uintptr_t base_va, if (!mm->size) return attr; /* Reached end of list */ - if (mm->base_va >= base_va + size) + if (mm->base_va > base_va + size - 1) return attr; /* Next region is after area so end */ - if (mm->base_va + mm->size <= base_va) + if (mm->base_va + mm->size - 1 < base_va) continue; /* Next region has already been overtaken */ if (mm->attr == attr) continue; /* Region doesn't override attribs so skip */ if (mm->base_va > base_va || - mm->base_va + mm->size < base_va + size) + mm->base_va + mm->size - 1 < base_va + size - 1) return -1; /* Region doesn't fully cover our area */ attr = mm->attr; @@ -311,14 +315,13 @@ static mmap_region_t *init_xlation_table_inner(mmap_region_t *mm, uint64_t *table, int level) { - unsigned level_size_shift = L1_XLAT_ADDRESS_SHIFT - (level - 1) * - XLAT_TABLE_ENTRIES_SHIFT; - unsigned level_size = 1 << level_size_shift; - u_register_t level_index_mask = - (u_register_t)(((u_register_t) XLAT_TABLE_ENTRIES_MASK) - << level_size_shift); + assert(level >= XLAT_TABLE_LEVEL_MIN && level <= XLAT_TABLE_LEVEL_MAX); - assert(level > 0 && level <= 3); + unsigned int level_size_shift = + L0_XLAT_ADDRESS_SHIFT - level * XLAT_TABLE_ENTRIES_SHIFT; + u_register_t level_size = (u_register_t)1 << level_size_shift; + u_register_t level_index_mask = + ((u_register_t)XLAT_TABLE_ENTRIES_MASK) << level_size_shift; debug_print("New xlat table:\n"); @@ -328,16 +331,16 @@ static mmap_region_t *init_xlation_table_inner(mmap_region_t *mm, if (!mm->size) { /* Done mapping regions; finish zeroing the table */ desc = INVALID_DESC; - } else if (mm->base_va + mm->size <= base_va) { + } else if (mm->base_va + mm->size - 1 < base_va) { /* This area is after the region so get next region */ ++mm; continue; } - debug_print("%s VA:%p size:0x%x ", get_level_spacer(level), - (void *)base_va, level_size); + debug_print("%s VA:%p size:0x%llx ", get_level_spacer(level), + (void *)base_va, (unsigned long long)level_size); - if (mm->base_va >= base_va + level_size) { + if (mm->base_va > base_va + level_size - 1) { /* Next region is after this area. Nothing to map yet */ desc = INVALID_DESC; } else { @@ -369,7 +372,7 @@ static mmap_region_t *init_xlation_table_inner(mmap_region_t *mm, *table++ = desc; base_va += level_size; - } while ((base_va & level_index_mask) && (base_va < ADDR_SPACE_SIZE)); + } while ((base_va & level_index_mask) && (base_va - 1 < ADDR_SPACE_SIZE - 1)); return mm; } diff --git a/lib/xlat_tables/xlat_tables_private.h b/lib/xlat_tables/xlat_tables_private.h index 389bb0b5..159d071b 100644 --- a/lib/xlat_tables/xlat_tables_private.h +++ b/lib/xlat_tables/xlat_tables_private.h @@ -31,6 +31,12 @@ #ifndef __XLAT_TABLES_PRIVATE_H__ #define __XLAT_TABLES_PRIVATE_H__ +#include <cassert.h> +#include <utils.h> + +/* The virtual address space size must be a power of two. */ +CASSERT(IS_POWER_OF_TWO(ADDR_SPACE_SIZE), assert_valid_addr_space_size); + void print_mmap(void); void init_xlation_table(uintptr_t base_va, uint64_t *table, int level, uintptr_t *max_va, diff --git a/plat/arm/board/common/board_common.mk b/plat/arm/board/common/board_common.mk index a5636d5e..49136e68 100644 --- a/plat/arm/board/common/board_common.mk +++ b/plat/arm/board/common/board_common.mk @@ -31,8 +31,10 @@ PLAT_INCLUDES += -Iinclude/plat/arm/board/common/ \ -Iinclude/plat/arm/board/common/drivers -PLAT_BL_COMMON_SOURCES += drivers/arm/pl011/pl011_console.S \ - plat/arm/board/common/aarch64/board_arm_helpers.S +PLAT_BL_COMMON_SOURCES += drivers/arm/pl011/${ARCH}/pl011_console.S +ifeq (${ARCH}, aarch64) +PLAT_BL_COMMON_SOURCES += plat/arm/board/common/aarch64/board_arm_helpers.S +endif BL1_SOURCES += plat/arm/board/common/drivers/norflash/norflash.c diff --git a/plat/arm/board/fvp/aarch32/fvp_helpers.S b/plat/arm/board/fvp/aarch32/fvp_helpers.S new file mode 100644 index 00000000..373036c9 --- /dev/null +++ b/plat/arm/board/fvp/aarch32/fvp_helpers.S @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <arch.h> +#include <asm_macros.S> +#include <platform_def.h> +#include "../drivers/pwrc/fvp_pwrc.h" +#include "../fvp_def.h" + + .globl plat_get_my_entrypoint + .globl plat_is_my_cpu_primary + + /* --------------------------------------------------------------------- + * unsigned long plat_get_my_entrypoint (void); + * + * Main job of this routine is to distinguish between a cold and warm + * boot. On FVP, this information can be queried from the power + * controller. The Power Control SYS Status Register (PSYSR) indicates + * the wake-up reason for the CPU. + * + * For a cold boot, return 0. + * For a warm boot, read the mailbox and return the address it contains. + * + * TODO: PSYSR is a common register and should be + * accessed using locks. Since it is not possible + * to use locks immediately after a cold reset + * we are relying on the fact that after a cold + * reset all cpus will read the same WK field + * --------------------------------------------------------------------- + */ +func plat_get_my_entrypoint + /* --------------------------------------------------------------------- + * When bit PSYSR.WK indicates either "Wake by PPONR" or "Wake by GIC + * WakeRequest signal" then it is a warm boot. + * --------------------------------------------------------------------- + */ + ldcopr r2, MPIDR + ldr r1, =PWRC_BASE + str r2, [r1, #PSYSR_OFF] + ldr r2, [r1, #PSYSR_OFF] + ubfx r2, r2, #PSYSR_WK_SHIFT, #PSYSR_WK_WIDTH + cmp r2, #WKUP_PPONR + beq warm_reset + cmp r2, #WKUP_GICREQ + beq warm_reset + + /* Cold reset */ + mov r0, #0 + bx lr + +warm_reset: + /* --------------------------------------------------------------------- + * A mailbox is maintained in the trusted SRAM. It is flushed out of the + * caches after every update using normal memory so it is safe to read + * it here with SO attributes. + * --------------------------------------------------------------------- + */ + ldr r0, =PLAT_ARM_TRUSTED_MAILBOX_BASE + ldr r0, [r0] + cmp r0, #0 + beq _panic + bx lr + + /* --------------------------------------------------------------------- + * The power controller indicates this is a warm reset but the mailbox + * is empty. This should never happen! + * --------------------------------------------------------------------- + */ +_panic: + b _panic +endfunc plat_get_my_entrypoint + + /* ----------------------------------------------------- + * unsigned int plat_is_my_cpu_primary (void); + * + * Find out whether the current cpu is the primary + * cpu. + * ----------------------------------------------------- + */ +func plat_is_my_cpu_primary + ldcopr r0, MPIDR + ldr r1, =(MPIDR_CLUSTER_MASK | MPIDR_CPU_MASK) + and r0, r1 + cmp r0, #FVP_PRIMARY_CPU + moveq r0, #1 + movne r0, #0 + bx lr +endfunc plat_is_my_cpu_primary diff --git a/plat/arm/board/fvp/fvp_common.c b/plat/arm/board/fvp/fvp_common.c index 002cff67..fbbe34e6 100644 --- a/plat/arm/board/fvp/fvp_common.c +++ b/plat/arm/board/fvp/fvp_common.c @@ -121,6 +121,9 @@ const mmap_region_t plat_arm_mmap[] = { #endif #if IMAGE_BL32 const mmap_region_t plat_arm_mmap[] = { +#ifdef AARCH32 + ARM_MAP_SHARED_RAM, +#endif V2M_MAP_IOFPGA, MAP_DEVICE0, MAP_DEVICE1, diff --git a/plat/arm/board/fvp/fvp_err.c b/plat/arm/board/fvp/fvp_err.c index 7867e49c..f8ea6a03 100644 --- a/plat/arm/board/fvp/fvp_err.c +++ b/plat/arm/board/fvp/fvp_err.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2016, 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,6 +28,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include <arch_helpers.h> #include <board_arm_def.h> #include <debug.h> #include <errno.h> @@ -61,5 +62,5 @@ void plat_error_handler(int err) /* Loop until the watchdog resets the system */ for (;;) - ; + wfi(); } diff --git a/plat/arm/board/fvp/platform.mk b/plat/arm/board/fvp/platform.mk index 1ea98222..2865569a 100644 --- a/plat/arm/board/fvp/platform.mk +++ b/plat/arm/board/fvp/platform.mk @@ -69,6 +69,9 @@ FVP_GIC_SOURCES := drivers/arm/gic/common/gic_common.c \ plat/common/plat_gicv2.c \ plat/arm/common/arm_gicv2.c else ifeq (${FVP_USE_GIC_DRIVER}, FVP_GICV3_LEGACY) + ifeq (${ARCH}, aarch32) + $(error "GICV3 Legacy driver not supported for AArch32 build") + endif FVP_GIC_SOURCES := drivers/arm/gic/arm_gic.c \ drivers/arm/gic/gic_v2.c \ drivers/arm/gic/gic_v3.c \ @@ -98,12 +101,15 @@ PLAT_INCLUDES := -Iplat/arm/board/fvp/include PLAT_BL_COMMON_SOURCES := plat/arm/board/fvp/fvp_common.c -FVP_CPU_LIBS := lib/cpus/aarch64/aem_generic.S \ - lib/cpus/aarch64/cortex_a35.S \ +FVP_CPU_LIBS := lib/cpus/${ARCH}/aem_generic.S + +ifeq (${ARCH}, aarch64) +FVP_CPU_LIBS += lib/cpus/aarch64/cortex_a35.S \ lib/cpus/aarch64/cortex_a53.S \ lib/cpus/aarch64/cortex_a57.S \ lib/cpus/aarch64/cortex_a72.S \ lib/cpus/aarch64/cortex_a73.S +endif BL1_SOURCES += drivers/io/io_semihosting.c \ lib/semihosting/semihosting.c \ diff --git a/plat/arm/board/fvp/sp_min/fvp_sp_min_setup.c b/plat/arm/board/fvp/sp_min/fvp_sp_min_setup.c new file mode 100644 index 00000000..d3bef82f --- /dev/null +++ b/plat/arm/board/fvp/sp_min/fvp_sp_min_setup.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <plat_arm.h> +#include "../fvp_private.h" + +void sp_min_early_platform_setup(void) +{ + arm_sp_min_early_platform_setup(); + + /* Initialize the platform config for future decision making */ + fvp_config_setup(); + + /* + * Initialize the correct interconnect for this cluster during cold + * boot. No need for locks as no other CPU is active. + */ + fvp_interconnect_init(); + + /* + * Enable coherency in interconnect for the primary CPU's cluster. + * Earlier bootloader stages might already do this (e.g. Trusted + * Firmware's BL1 does it) but we can't assume so. There is no harm in + * executing this code twice anyway. + * FVP PSCI code will enable coherency for other clusters. + */ + fvp_interconnect_enable(); +} diff --git a/plat/arm/board/fvp/sp_min/sp_min-fvp.mk b/plat/arm/board/fvp/sp_min/sp_min-fvp.mk new file mode 100644 index 00000000..a7887824 --- /dev/null +++ b/plat/arm/board/fvp/sp_min/sp_min-fvp.mk @@ -0,0 +1,42 @@ +# +# Copyright (c) 2016, 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: +# +# Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# Neither the name of ARM nor the names of its contributors may be used +# to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +# SP_MIN source files specific to FVP platform +BL32_SOURCES += plat/arm/board/fvp/aarch32/fvp_helpers.S \ + plat/arm/board/fvp/drivers/pwrc/fvp_pwrc.c \ + plat/arm/board/fvp/fvp_pm.c \ + plat/arm/board/fvp/fvp_topology.c \ + plat/arm/board/fvp/sp_min/fvp_sp_min_setup.c \ + ${FVP_CPU_LIBS} \ + ${FVP_GIC_SOURCES} \ + ${FVP_INTERCONNECT_SOURCES} \ + ${FVP_SECURITY_SOURCES} + +include plat/arm/common/sp_min/arm_sp_min.mk
\ No newline at end of file diff --git a/plat/arm/board/juno/juno_err.c b/plat/arm/board/juno/juno_err.c index 497cc7fa..fa19da76 100644 --- a/plat/arm/board/juno/juno_err.c +++ b/plat/arm/board/juno/juno_err.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2016, 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,6 +28,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ +#include <arch_helpers.h> #include <errno.h> #include <v2m_def.h> @@ -45,5 +46,5 @@ void plat_error_handler(int err) /* Loop until the watchdog resets the system */ for (;;) - ; + wfi(); } diff --git a/plat/arm/common/aarch32/arm_helpers.S b/plat/arm/common/aarch32/arm_helpers.S new file mode 100644 index 00000000..08399137 --- /dev/null +++ b/plat/arm/common/aarch32/arm_helpers.S @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +#include <asm_macros.S> +#include <platform_def.h> + + .weak plat_arm_calc_core_pos + .weak plat_my_core_pos + + /* ----------------------------------------------------- + * unsigned int plat_my_core_pos(void) + * This function uses the plat_arm_calc_core_pos() + * definition to get the index of the calling CPU. + * ----------------------------------------------------- + */ +func plat_my_core_pos + ldcopr r0, MPIDR + b plat_arm_calc_core_pos +endfunc plat_my_core_pos + + /* ----------------------------------------------------- + * unsigned int plat_arm_calc_core_pos(uint64_t mpidr) + * Helper function to calculate the core position. + * With this function: CorePos = (ClusterId * 4) + + * CoreId + * ----------------------------------------------------- + */ +func plat_arm_calc_core_pos + and r1, r0, #MPIDR_CPU_MASK + and r0, r0, #MPIDR_CLUSTER_MASK + add r0, r1, r0, LSR #6 + bx lr +endfunc plat_arm_calc_core_pos diff --git a/plat/arm/common/arm_common.c b/plat/arm/common/arm_common.c index 93355fe4..c53723d5 100644 --- a/plat/arm/common/arm_common.c +++ b/plat/arm/common/arm_common.c @@ -134,6 +134,7 @@ uint32_t arm_get_spsr_for_bl32_entry(void) /******************************************************************************* * Gets SPSR for BL33 entry ******************************************************************************/ +#ifndef AARCH32 uint32_t arm_get_spsr_for_bl33_entry(void) { unsigned long el_status; @@ -154,6 +155,28 @@ uint32_t arm_get_spsr_for_bl33_entry(void) spsr = SPSR_64(mode, MODE_SP_ELX, DISABLE_ALL_EXCEPTIONS); return spsr; } +#else +/******************************************************************************* + * Gets SPSR for BL33 entry + ******************************************************************************/ +uint32_t arm_get_spsr_for_bl33_entry(void) +{ + unsigned int hyp_status, mode, spsr; + + hyp_status = GET_VIRT_EXT(read_id_pfr1()); + + mode = (hyp_status) ? MODE32_hyp : MODE32_svc; + + /* + * TODO: Consider the possibility of specifying the SPSR in + * the FIP ToC and allowing the platform to have a say as + * well. + */ + spsr = SPSR_MODE32(mode, plat_get_ns_image_entrypoint() & 0x1, + SPSR_E_LITTLE, DISABLE_ALL_EXCEPTIONS); + return spsr; +} +#endif /* AARCH32 */ /******************************************************************************* * Configures access to the system counter timer module. diff --git a/plat/arm/common/arm_common.mk b/plat/arm/common/arm_common.mk index 03b9fe47..0b961ea7 100644 --- a/plat/arm/common/arm_common.mk +++ b/plat/arm/common/arm_common.mk @@ -28,23 +28,30 @@ # POSSIBILITY OF SUCH DAMAGE. # -# On ARM standard platorms, the TSP can execute from Trusted SRAM, Trusted -# DRAM (if available) or the TZC secured area of DRAM. -# Trusted SRAM is the default. - -ARM_TSP_RAM_LOCATION := tsram -ifeq (${ARM_TSP_RAM_LOCATION}, tsram) - ARM_TSP_RAM_LOCATION_ID = ARM_TRUSTED_SRAM_ID -else ifeq (${ARM_TSP_RAM_LOCATION}, tdram) - ARM_TSP_RAM_LOCATION_ID = ARM_TRUSTED_DRAM_ID -else ifeq (${ARM_TSP_RAM_LOCATION}, dram) - ARM_TSP_RAM_LOCATION_ID = ARM_DRAM_ID -else - $(error "Unsupported ARM_TSP_RAM_LOCATION value") -endif +ifeq (${ARCH}, aarch64) + # On ARM standard platorms, the TSP can execute from Trusted SRAM, Trusted + # DRAM (if available) or the TZC secured area of DRAM. + # Trusted SRAM is the default. + + ARM_TSP_RAM_LOCATION := tsram + ifeq (${ARM_TSP_RAM_LOCATION}, tsram) + ARM_TSP_RAM_LOCATION_ID = ARM_TRUSTED_SRAM_ID + else ifeq (${ARM_TSP_RAM_LOCATION}, tdram) + ARM_TSP_RAM_LOCATION_ID = ARM_TRUSTED_DRAM_ID + else ifeq (${ARM_TSP_RAM_LOCATION}, dram) + ARM_TSP_RAM_LOCATION_ID = ARM_DRAM_ID + else + $(error "Unsupported ARM_TSP_RAM_LOCATION value") + endif -# Process flags -$(eval $(call add_define,ARM_TSP_RAM_LOCATION_ID)) + # Process flags + $(eval $(call add_define,ARM_TSP_RAM_LOCATION_ID)) + + # Process ARM_BL31_IN_DRAM flag + ARM_BL31_IN_DRAM := 0 + $(eval $(call assert_boolean,ARM_BL31_IN_DRAM)) + $(eval $(call add_define,ARM_BL31_IN_DRAM)) +endif # For the original power-state parameter format, the State-ID can be encoded # according to the recommended encoding or zero. This flag determines which @@ -83,7 +90,7 @@ $(eval $(call assert_boolean,ARM_BL31_IN_DRAM)) $(eval $(call add_define,ARM_BL31_IN_DRAM)) # Enable PSCI_STAT_COUNT/RESIDENCY APIs on ARM platforms -ENABLE_PSCI_STAT = 1 +ENABLE_PSCI_STAT := 1 # On ARM platforms, separate the code and read-only data sections to allow # mapping the former as executable and the latter as execute-never. @@ -91,15 +98,17 @@ SEPARATE_CODE_AND_RODATA := 1 PLAT_INCLUDES += -Iinclude/common/tbbr \ - -Iinclude/plat/arm/common \ - -Iinclude/plat/arm/common/aarch64 + -Iinclude/plat/arm/common +ifeq (${ARCH}, aarch64) +PLAT_INCLUDES += -Iinclude/plat/arm/common/aarch64 +endif PLAT_BL_COMMON_SOURCES += lib/xlat_tables/xlat_tables_common.c \ - lib/xlat_tables/aarch64/xlat_tables.c \ - plat/arm/common/aarch64/arm_helpers.S \ + lib/xlat_tables/${ARCH}/xlat_tables.c \ + plat/arm/common/${ARCH}/arm_helpers.S \ plat/arm/common/arm_common.c \ - plat/common/aarch64/plat_common.c + plat/common/${ARCH}/plat_common.c BL1_SOURCES += drivers/arm/sp805/sp805.c \ drivers/io/io_fip.c \ diff --git a/plat/arm/common/arm_gicv3.c b/plat/arm/common/arm_gicv3.c index a20fd56f..ac309f2b 100644 --- a/plat/arm/common/arm_gicv3.c +++ b/plat/arm/common/arm_gicv3.c @@ -77,7 +77,7 @@ void plat_arm_gic_driver_init(void) * can use GIC system registers to manage interrupts and does * not need GIC interface base addresses to be configured. */ -#if IMAGE_BL31 +#if (AARCH32 && IMAGE_BL32) || (IMAGE_BL31 && !AARCH32) gicv3_driver_init(&arm_gic_data); #endif } diff --git a/plat/arm/common/sp_min/arm_sp_min.mk b/plat/arm/common/sp_min/arm_sp_min.mk new file mode 100644 index 00000000..8a4d5984 --- /dev/null +++ b/plat/arm/common/sp_min/arm_sp_min.mk @@ -0,0 +1,37 @@ +# +# Copyright (c) 2016, 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: +# +# Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# Neither the name of ARM nor the names of its contributors may be used +# to endorse or promote products derived from this software without specific +# prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +# SP MIN source files common to ARM standard platforms +BL32_SOURCES += plat/arm/common/arm_pm.c \ + plat/arm/common/arm_topology.c \ + plat/arm/common/sp_min/arm_sp_min_setup.c \ + plat/common/aarch32/platform_mp_stack.S \ + plat/common/plat_psci_common.c + diff --git a/plat/arm/common/sp_min/arm_sp_min_setup.c b/plat/arm/common/sp_min/arm_sp_min_setup.c new file mode 100644 index 00000000..927f30f5 --- /dev/null +++ b/plat/arm/common/sp_min/arm_sp_min_setup.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <assert.h> +#include <console.h> +#include <mmio.h> +#include <plat_arm.h> +#include <platform.h> +#include <platform_def.h> +#include <platform_sp_min.h> + +#define BL32_END (uintptr_t)(&__BL32_END__) + +#if USE_COHERENT_MEM +/* + * The next 2 constants identify the extents of the coherent memory region. + * These addresses are used by the MMU setup code and therefore they must be + * page-aligned. It is the responsibility of the linker script to ensure that + * __COHERENT_RAM_START__ and __COHERENT_RAM_END__ linker symbols refer to + * page-aligned addresses. + */ +#define BL32_COHERENT_RAM_BASE (uintptr_t)(&__COHERENT_RAM_START__) +#define BL32_COHERENT_RAM_LIMIT (uintptr_t)(&__COHERENT_RAM_END__) +#endif + + +static entry_point_info_t bl33_image_ep_info; + +/* Weak definitions may be overridden in specific ARM standard platform */ +#pragma weak sp_min_early_platform_setup +#pragma weak sp_min_platform_setup +#pragma weak sp_min_plat_arch_setup + +#ifndef RESET_TO_SP_MIN +#error (" RESET_TO_SP_MIN flag is expected to be set.") +#endif + + +/******************************************************************************* + * Return a pointer to the 'entry_point_info' structure of the next image for the + * security state specified. BL33 corresponds to the non-secure image type + * while BL32 corresponds to the secure image type. A NULL pointer is returned + * if the image does not exist. + ******************************************************************************/ +entry_point_info_t *sp_min_plat_get_bl33_ep_info(void) +{ + entry_point_info_t *next_image_info; + + next_image_info = &bl33_image_ep_info; + + /* + * None of the images on the ARM development platforms can have 0x0 + * as the entrypoint + */ + if (next_image_info->pc) + return next_image_info; + else + return NULL; +} + +/******************************************************************************* + * Perform early platform setup. We expect SP_MIN is the first boot loader + * image and RESET_TO_SP_MIN build option to be set. + ******************************************************************************/ +void arm_sp_min_early_platform_setup(void) +{ + /* Initialize the console to provide early debug support */ + console_init(PLAT_ARM_BOOT_UART_BASE, PLAT_ARM_BOOT_UART_CLK_IN_HZ, + ARM_CONSOLE_BAUDRATE); + + /* Populate entry point information for BL33 */ + SET_PARAM_HEAD(&bl33_image_ep_info, + PARAM_EP, + VERSION_1, + 0); + /* + * Tell SP_MIN where the non-trusted software image + * is located and the entry state information + */ +#ifdef PRELOADED_BL33_BASE + bl33_image_ep_info.pc = PRELOADED_BL33_BASE; +#else + bl33_image_ep_info.pc = plat_get_ns_image_entrypoint(); +#endif + bl33_image_ep_info.spsr = arm_get_spsr_for_bl33_entry(); + SET_SECURITY_STATE(bl33_image_ep_info.h.attr, NON_SECURE); +} + +void sp_min_early_platform_setup(void) +{ + arm_sp_min_early_platform_setup(); + + /* + * Initialize Interconnect for this cluster during cold boot. + * No need for locks as no other CPU is active. + */ + plat_arm_interconnect_init(); + + /* + * Enable Interconnect coherency for the primary CPU's cluster. + * Earlier bootloader stages might already do this (e.g. Trusted + * Firmware's BL1 does it) but we can't assume so. There is no harm in + * executing this code twice anyway. + * Platform specific PSCI code will enable coherency for other + * clusters. + */ + plat_arm_interconnect_enter_coherency(); +} + +/******************************************************************************* + * Perform platform specific setup for SP_MIN + ******************************************************************************/ +void sp_min_platform_setup(void) +{ + /* Initialize the GIC driver, cpu and distributor interfaces */ + plat_arm_gic_driver_init(); + plat_arm_gic_init(); + + /* + * Do initial security configuration to allow DRAM/device access + * (if earlier BL has not already done so). + * TODO: If RESET_TO_SP_MIN is not set, the security setup needs + * to be skipped. + */ + plat_arm_security_setup(); + + /* Enable and initialize the System level generic timer */ + mmio_write_32(ARM_SYS_CNTCTL_BASE + CNTCR_OFF, + CNTCR_FCREQ(0) | CNTCR_EN); + + /* Allow access to the System counter timer module */ + arm_configure_sys_timer(); + + /* Initialize power controller before setting up topology */ + plat_arm_pwrc_setup(); +} + +/******************************************************************************* + * Perform the very early platform specific architectural setup here. At the + * moment this only initializes the MMU + ******************************************************************************/ +void sp_min_plat_arch_setup(void) +{ + + arm_setup_page_tables(BL32_BASE, + (BL32_END - BL32_BASE), + BL_CODE_BASE, + BL_CODE_LIMIT, + BL_RO_DATA_BASE, + BL_RO_DATA_LIMIT +#if USE_COHERENT_MEM + , BL32_COHERENT_RAM_BASE, + BL32_COHERENT_RAM_LIMIT +#endif + ); + + enable_mmu_secure(0); +} diff --git a/plat/common/aarch32/plat_common.c b/plat/common/aarch32/plat_common.c new file mode 100644 index 00000000..a5b9535c --- /dev/null +++ b/plat/common/aarch32/plat_common.c @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <platform.h> +#include <xlat_tables.h> + +/* + * The following platform setup functions are weakly defined. They + * provide typical implementations that may be re-used by multiple + * platforms but may also be overridden by a platform if required. + */ +#pragma weak bl32_plat_enable_mmu + +void bl32_plat_enable_mmu(uint32_t flags) +{ + enable_mmu_secure(flags); +} diff --git a/plat/common/aarch32/platform_helpers.S b/plat/common/aarch32/platform_helpers.S new file mode 100644 index 00000000..481dd68d --- /dev/null +++ b/plat/common/aarch32/platform_helpers.S @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <arch.h> +#include <asm_macros.S> + + .weak plat_my_core_pos + .weak plat_reset_handler + .weak platform_mem_init + .weak plat_panic_handler + + /* ----------------------------------------------------- + * int plat_my_core_pos(void); + * With this function: CorePos = (ClusterId * 4) + + * CoreId + * ----------------------------------------------------- + */ +func plat_my_core_pos + ldcopr r0, MPIDR + and r1, r0, #MPIDR_CPU_MASK + and r0, r0, #MPIDR_CLUSTER_MASK + add r0, r1, r0, LSR #6 + bx lr +endfunc plat_my_core_pos + + /* ----------------------------------------------------- + * Placeholder function which should be redefined by + * each platform. + * ----------------------------------------------------- + */ +func plat_reset_handler + bx lr +endfunc plat_reset_handler + + /* --------------------------------------------------------------------- + * Placeholder function which should be redefined by + * each platform. + * --------------------------------------------------------------------- + */ +func platform_mem_init + bx lr +endfunc platform_mem_init + + /* ----------------------------------------------------- + * void plat_panic_handler(void) __dead2; + * Endless loop by default. + * ----------------------------------------------------- + */ +func plat_panic_handler + b plat_panic_handler +endfunc plat_panic_handler diff --git a/plat/common/aarch32/platform_mp_stack.S b/plat/common/aarch32/platform_mp_stack.S new file mode 100644 index 00000000..a0154369 --- /dev/null +++ b/plat/common/aarch32/platform_mp_stack.S @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <arch.h> +#include <asm_macros.S> +#include <platform_def.h> + + .globl plat_get_my_stack + .globl plat_set_my_stack + + /* ----------------------------------------------------- + * uintptr_t plat_get_my_stack (u_register_t mpidr) + * + * For a given CPU, this function returns the stack + * pointer for a stack allocated in device memory. + * ----------------------------------------------------- + */ +func plat_get_my_stack + mov r3, lr + get_my_mp_stack platform_normal_stacks, PLATFORM_STACK_SIZE + bx r3 +endfunc plat_get_my_stack + + /* ----------------------------------------------------- + * void plat_set_my_stack () + * + * For the current CPU, this function sets the stack + * pointer to a stack allocated in normal memory. + * ----------------------------------------------------- + */ +func plat_set_my_stack + mov r3, lr + get_my_mp_stack platform_normal_stacks, PLATFORM_STACK_SIZE + mov sp, r0 + bx r3 +endfunc plat_set_my_stack + + /* ----------------------------------------------------- + * Per-cpu stacks in normal memory. Each cpu gets a + * stack of PLATFORM_STACK_SIZE bytes. + * ----------------------------------------------------- + */ +declare_stack platform_normal_stacks, tzfw_normal_stacks, \ + PLATFORM_STACK_SIZE, PLATFORM_CORE_COUNT diff --git a/plat/common/aarch64/platform_helpers.S b/plat/common/aarch64/platform_helpers.S index 08638303..a134ded0 100644 --- a/plat/common/aarch64/platform_helpers.S +++ b/plat/common/aarch64/platform_helpers.S @@ -130,6 +130,7 @@ endfunc bl1_plat_prepare_exit * ----------------------------------------------------- */ func plat_error_handler + wfi b plat_error_handler endfunc plat_error_handler @@ -139,5 +140,6 @@ endfunc plat_error_handler * ----------------------------------------------------- */ func plat_panic_handler + wfi b plat_panic_handler endfunc plat_panic_handler diff --git a/plat/common/plat_gicv3.c b/plat/common/plat_gicv3.c index 249caf8e..c961d629 100644 --- a/plat/common/plat_gicv3.c +++ b/plat/common/plat_gicv3.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2015-2016, 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: @@ -186,6 +186,11 @@ uint32_t plat_interrupt_type_to_line(uint32_t type, #pragma weak plat_ic_acknowledge_interrupt #pragma weak plat_ic_end_of_interrupt +/* In AArch32, the secure group1 interrupts are targeted to Secure PL1 */ +#ifdef AARCH32 +#define IS_IN_EL1() IS_IN_SECURE() +#endif + /* * This function returns the highest priority pending interrupt at * the Interrupt controller diff --git a/plat/compat/plat_compat.mk b/plat/compat/plat_compat.mk index d9d50f6e..a1cdd809 100644 --- a/plat/compat/plat_compat.mk +++ b/plat/compat/plat_compat.mk @@ -33,6 +33,9 @@ ifeq (${PSCI_EXTENDED_STATE_ID}, 1) PSCI_EXTENDED_STATE_ID is not set") endif +ifneq (${ARCH}, aarch64) + $(error "PSCI Compatibility mode is only supported for AArch64 platforms") +endif PLAT_BL_COMMON_SOURCES += plat/compat/aarch64/plat_helpers_compat.S diff --git a/plat/mediatek/mt8173/platform.mk b/plat/mediatek/mt8173/platform.mk index 8f482304..c815110e 100644 --- a/plat/mediatek/mt8173/platform.mk +++ b/plat/mediatek/mt8173/platform.mk @@ -50,7 +50,7 @@ BL31_SOURCES += drivers/arm/cci/cci.c \ drivers/arm/gic/arm_gic.c \ drivers/arm/gic/gic_v2.c \ drivers/arm/gic/gic_v3.c \ - drivers/console/console.S \ + drivers/console/aarch64/console.S \ drivers/delay_timer/delay_timer.c \ drivers/delay_timer/generic_delay_timer.c \ lib/cpus/aarch64/aem_generic.S \ diff --git a/plat/nvidia/tegra/common/tegra_common.mk b/plat/nvidia/tegra/common/tegra_common.mk index 03ca7732..3c07032d 100644 --- a/plat/nvidia/tegra/common/tegra_common.mk +++ b/plat/nvidia/tegra/common/tegra_common.mk @@ -48,9 +48,9 @@ COMMON_DIR := plat/nvidia/tegra/common BL31_SOURCES += drivers/arm/gic/gic_v2.c \ drivers/arm/gic/gic_v3.c \ - drivers/console/console.S \ + drivers/console/aarch64/console.S \ drivers/delay_timer/delay_timer.c \ - drivers/ti/uart/16550_console.S \ + drivers/ti/uart/aarch64/16550_console.S \ plat/common/aarch64/platform_mp_stack.S \ plat/common/plat_psci_common.c \ ${COMMON_DIR}/aarch64/tegra_helpers.S \ diff --git a/plat/qemu/platform.mk b/plat/qemu/platform.mk index 95421989..aa08bd33 100644 --- a/plat/qemu/platform.mk +++ b/plat/qemu/platform.mk @@ -37,7 +37,7 @@ PLAT_INCLUDES := -Iinclude/plat/arm/common/ \ PLAT_BL_COMMON_SOURCES := plat/qemu/qemu_common.c \ - drivers/arm/pl011/pl011_console.S \ + drivers/arm/pl011/aarch64/pl011_console.S \ lib/xlat_tables/xlat_tables_common.c \ lib/xlat_tables/aarch64/xlat_tables.c diff --git a/plat/rockchip/common/include/plat_private.h b/plat/rockchip/common/include/plat_private.h index 71998998..b9b634e7 100644 --- a/plat/rockchip/common/include/plat_private.h +++ b/plat/rockchip/common/include/plat_private.h @@ -56,6 +56,7 @@ struct rockchip_pm_ops_cb { int (*sys_pwr_dm_resume)(void); void (*sys_gbl_soft_reset)(void) __dead2; void (*system_off)(void) __dead2; + void (*sys_pwr_down_wfi)(const psci_power_state_t *state_info) __dead2; }; /****************************************************************************** diff --git a/plat/rockchip/common/plat_pm.c b/plat/rockchip/common/plat_pm.c index b6291bbf..7372fcff 100644 --- a/plat/rockchip/common/plat_pm.c +++ b/plat/rockchip/common/plat_pm.c @@ -311,6 +311,18 @@ static void __dead2 rockchip_system_poweroff(void) rockchip_ops->system_off(); } +static void +__dead2 rockchip_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state) +{ + if ((RK_CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) && + (rockchip_ops)) { + if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE && + rockchip_ops->sys_pwr_down_wfi) + rockchip_ops->sys_pwr_down_wfi(target_state); + } + psci_power_down_wfi(); +} + /******************************************************************************* * Export the platform handlers via plat_rockchip_psci_pm_ops. The rockchip * standard @@ -323,6 +335,7 @@ const plat_psci_ops_t plat_rockchip_psci_pm_ops = { .pwr_domain_suspend = rockchip_pwr_domain_suspend, .pwr_domain_on_finish = rockchip_pwr_domain_on_finish, .pwr_domain_suspend_finish = rockchip_pwr_domain_suspend_finish, + .pwr_domain_pwr_down_wfi = rockchip_pwr_domain_pwr_down_wfi, .system_reset = rockchip_system_reset, .system_off = rockchip_system_poweroff, .validate_power_state = rockchip_validate_power_state, diff --git a/plat/rockchip/rk3368/platform.mk b/plat/rockchip/rk3368/platform.mk index 1dca4c56..73a56e34 100644 --- a/plat/rockchip/rk3368/platform.mk +++ b/plat/rockchip/rk3368/platform.mk @@ -55,8 +55,8 @@ PLAT_BL_COMMON_SOURCES := lib/xlat_tables/xlat_tables_common.c \ BL31_SOURCES += ${RK_GIC_SOURCES} \ drivers/arm/cci/cci.c \ - drivers/console/console.S \ - drivers/ti/uart/16550_console.S \ + drivers/console/aarch64/console.S \ + drivers/ti/uart/aarch64/16550_console.S \ drivers/delay_timer/delay_timer.c \ drivers/delay_timer/generic_delay_timer.c \ lib/cpus/aarch64/cortex_a53.S \ diff --git a/plat/rockchip/rk3399/drivers/dram/dcf_code.inc b/plat/rockchip/rk3399/drivers/dram/dcf_code.inc new file mode 100644 index 00000000..53196a02 --- /dev/null +++ b/plat/rockchip/rk3399/drivers/dram/dcf_code.inc @@ -0,0 +1,364 @@ + 0x0 , + 0x4f8c120c , + 0x0 , + 0x4f8c1210 , + 0x100000 , + 0x1f310019 , + 0x0 , + 0xb0000001 , + 0x58 , + 0xd0000000 , + 0x1300 , + 0x1f760329 , + 0x0 , + 0xb0000001 , + 0x40 , + 0xd0000000 , + 0xc , + 0x1f760371 , + 0x0 , + 0xb0000001 , + 0x28 , + 0xd0000000 , + 0x400000 , + 0x1f900009 , + 0x0 , + 0xb0000001 , + 0x10 , + 0xd0000000 , + 0x1 , + 0x4f8c120c , + 0x100000 , + 0x1f310019 , + 0x0 , + 0xb0000001 , + 0x58 , + 0xd0000000 , + 0x2c00 , + 0x1f760329 , + 0x0 , + 0xb0000001 , + 0x40 , + 0xd0000000 , + 0xc0 , + 0x1f760371 , + 0x0 , + 0xb0000001 , + 0x28 , + 0xd0000000 , + 0x400000 , + 0x1f8f0009 , + 0x0 , + 0xb0000001 , + 0x10 , + 0xd0000000 , + 0x1 , + 0x4f8c1210 , + 0x0 , + 0x4f8c1220 , + 0x0 , + 0x4f8c121c , + 0x0 , + 0xaf8c120d , + 0x108 , + 0xd0000000 , + 0x2000 , + 0x1f900009 , + 0x0 , + 0xa0000001 , + 0x30 , + 0xd0000000 , + 0x0 , + 0x4f8c1220 , + 0x0 , + 0x4f8c121c , + 0x0 , + 0x10000001 , + 0x0 , + 0xa0000001 , + 0xb0 , + 0xd0000000 , + 0x8000 , + 0x1f900009 , + 0x0 , + 0xa0000001 , + 0x30 , + 0xd0000000 , + 0x1 , + 0x4f8c1220 , + 0x1 , + 0x4f8c121c , + 0x0 , + 0x10000001 , + 0x0 , + 0xa0000001 , + 0x70 , + 0xd0000000 , + 0x4000 , + 0x1f900009 , + 0x0 , + 0xa0000001 , + 0x30 , + 0xd0000000 , + 0x0 , + 0x4f8c1220 , + 0x1 , + 0x4f8c121c , + 0x0 , + 0x10000001 , + 0x0 , + 0xa0000001 , + 0x30 , + 0xd0000000 , + 0x1000 , + 0x1f900009 , + 0x0 , + 0xa0000001 , + 0x18 , + 0xd0000000 , + 0x0 , + 0x4f8c1220 , + 0x1 , + 0x4f8c121c , + 0x0 , + 0x10000001 , + 0x0 , + 0xa0000001 , + 0x100 , + 0xd0000000 , + 0x0 , + 0xaf8c1211 , + 0xf0 , + 0xd0000000 , + 0x2000 , + 0x1f8f0009 , + 0x0 , + 0xa0000001 , + 0x30 , + 0xd0000000 , + 0x0 , + 0x4f8c1220 , + 0x0 , + 0x4f8c121c , + 0x0 , + 0x10000001 , + 0x0 , + 0xa0000001 , + 0xb0 , + 0xd0000000 , + 0x8000 , + 0x1f8f0009 , + 0x0 , + 0xa0000001 , + 0x30 , + 0xd0000000 , + 0x1 , + 0x4f8c1220 , + 0x1 , + 0x4f8c121c , + 0x0 , + 0x10000001 , + 0x0 , + 0xa0000001 , + 0x70 , + 0xd0000000 , + 0x4000 , + 0x1f8f0009 , + 0x0 , + 0xa0000001 , + 0x30 , + 0xd0000000 , + 0x0 , + 0x4f8c1220 , + 0x1 , + 0x4f8c121c , + 0x0 , + 0x10000001 , + 0x0 , + 0xa0000001 , + 0x30 , + 0xd0000000 , + 0x1000 , + 0x1f8f0009 , + 0x0 , + 0xa0000001 , + 0x18 , + 0xd0000000 , + 0x0 , + 0x4f8c1220 , + 0x1 , + 0x4f8c121c , + 0x0 , + 0xaf8c120d , + 0x40 , + 0xd0000000 , + 0x80008000 , + 0x7f900284 , + 0x1 , + 0x0 , + 0x8000 , + 0x1f90028d , + 0x0 , + 0x60000001 , + 0x0 , + 0x10000001 , + 0x0 , + 0xa0000001 , + 0x38 , + 0xd0000000 , + 0x0 , + 0xaf8c1211 , + 0x28 , + 0xd0000000 , + 0x80008000 , + 0x7f8f0284 , + 0x1 , + 0x0 , + 0x8000 , + 0x1f8f028d , + 0x0 , + 0x60000001 , + 0xffffffff , + 0x4f77e200 , + 0xffffffff , + 0x4f77e204 , + 0xffffffff , + 0x4f77e208 , + 0xffffffff , + 0x4f77e20c , + 0x70007000 , + 0x4f77e210 , + 0x3fffffff , + 0x7f750130 , + 0x0 , + 0x2f310061 , + 0xc0000 , + 0x20000001 , + 0x0 , + 0x4f310061 , + 0xc0000 , + 0x1f310065 , + 0xc0000 , + 0xb0000001 , + 0x10 , + 0xc0000000 , + 0x0 , + 0xaf8c121d , + 0x48 , + 0xd0000000 , + 0x0 , + 0xaf8c120d , + 0x18 , + 0xd0000000 , + 0x80000000 , + 0x2f90000d , + 0x0 , + 0x4f90000d , + 0x0 , + 0xaf8c1211 , + 0x18 , + 0xd0000000 , + 0x80000000 , + 0x2f90000d , + 0x0 , + 0x4f8f000d , + 0x0 , + 0x2f8c101d , + 0x350005 , + 0x20000001 , + 0x0 , + 0x4f620001 , + 0x1 , + 0x0 , + 0x4 , + 0x1f620011 , + 0x0 , + 0x60000001 , + 0x3000000 , + 0x7f76004c , + 0x18 , + 0x0 , + 0x10001 , + 0x7f76004c , + 0x0 , + 0x2f8c1005 , + 0x0 , + 0x4f760041 , + 0x0 , + 0x2f8c1009 , + 0x0 , + 0x4f760045 , + 0x10000 , + 0x7f76004c , + 0x18 , + 0x0 , + 0x1 , + 0x0 , + 0x80000000 , + 0x1f760049 , + 0x0 , + 0x60000001 , + 0x3000100 , + 0x7f76004c , + 0x3e8 , + 0x0 , + 0x20002 , + 0x4f620000 , + 0x1 , + 0x0 , + 0x1 , + 0x1f620011 , + 0x0 , + 0x60000001 , + 0x0 , + 0xaf8c121d , + 0x48 , + 0xd0000000 , + 0x0 , + 0xaf8c120d , + 0x18 , + 0xd0000000 , + 0x7fffffff , + 0x1f90000d , + 0x0 , + 0x4f90000d , + 0x0 , + 0xaf8c1211 , + 0x18 , + 0xd0000000 , + 0x7fffffff , + 0x1f90000d , + 0x0 , + 0x4f8f000d , + 0xfff3ffff , + 0x1f310061 , + 0x0 , + 0x7f310061 , + 0xc0000 , + 0x1f310065 , + 0x0 , + 0xb0000001 , + 0x10 , + 0xc0000000 , + 0x0 , + 0x7f750130 , + 0x1 , + 0x0 , + 0x1 , + 0x0 , + 0x1 , + 0x0 , + 0x1 , + 0x0 , + 0x1 , + 0x0 , + 0x1 , + 0x0 , + 0x1 , + 0x0 , + 0x1 , + 0x0 , + 0x1 , + 0x0 , + 0x0 , + 0xe0000000 , diff --git a/plat/rockchip/rk3399/drivers/dram/dram.c b/plat/rockchip/rk3399/drivers/dram/dram.c new file mode 100644 index 00000000..ddae84d3 --- /dev/null +++ b/plat/rockchip/rk3399/drivers/dram/dram.c @@ -0,0 +1,2564 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <debug.h> +#include <mmio.h> +#include <plat_private.h> +#include "dram.h" +#include "dram_spec_timing.h" +#include "string.h" +#include "soc.h" +#include "pmu.h" + +#include <delay_timer.h> + +#define CTL_TRAINING (1) +#define PI_TRAINING (!CTL_TRAINING) + +#define EN_READ_GATE_TRAINING (1) +#define EN_CA_TRAINING (0) +#define EN_WRITE_LEVELING (0) +#define EN_READ_LEVELING (0) +#define EN_WDQ_LEVELING (0) + +#define ENPER_CS_TRAINING_FREQ (933) + +struct pll_div { + unsigned int mhz; + unsigned int refdiv; + unsigned int fbdiv; + unsigned int postdiv1; + unsigned int postdiv2; + unsigned int frac; + unsigned int freq; +}; + +static const struct pll_div dpll_rates_table[] = { + + /* _mhz, _refdiv, _fbdiv, _postdiv1, _postdiv2 */ + {.mhz = 933, .refdiv = 3, .fbdiv = 350, .postdiv1 = 3, .postdiv2 = 1}, + {.mhz = 800, .refdiv = 1, .fbdiv = 100, .postdiv1 = 3, .postdiv2 = 1}, + {.mhz = 732, .refdiv = 1, .fbdiv = 61, .postdiv1 = 2, .postdiv2 = 1}, + {.mhz = 666, .refdiv = 1, .fbdiv = 111, .postdiv1 = 4, .postdiv2 = 1}, + {.mhz = 600, .refdiv = 1, .fbdiv = 50, .postdiv1 = 2, .postdiv2 = 1}, + {.mhz = 528, .refdiv = 1, .fbdiv = 66, .postdiv1 = 3, .postdiv2 = 1}, + {.mhz = 400, .refdiv = 1, .fbdiv = 50, .postdiv1 = 3, .postdiv2 = 1}, + {.mhz = 300, .refdiv = 1, .fbdiv = 50, .postdiv1 = 4, .postdiv2 = 1}, + {.mhz = 200, .refdiv = 1, .fbdiv = 50, .postdiv1 = 3, .postdiv2 = 2}, +}; + +static struct rk3399_ddr_cic_regs *const rk3399_ddr_cic = (void *)CIC_BASE; +static struct rk3399_ddr_pctl_regs *const rk3399_ddr_pctl[2] = { + (void *)DDRC0_BASE, (void *)DDRC1_BASE +}; + +static struct rk3399_ddr_pi_regs *const rk3399_ddr_pi[2] = { + (void *)DDRC0_PI_BASE, (void *)DDRC1_PI_BASE +}; + +static struct rk3399_ddr_publ_regs *const rk3399_ddr_publ[2] = { + (void *)DDRC0_PHY_BASE, (void *)DDRC1_PHY_BASE +}; + +struct rk3399_dram_status { + uint32_t current_index; + uint32_t index_freq[2]; + uint32_t low_power_stat; + struct timing_related_config timing_config; + struct drv_odt_lp_config drv_odt_lp_cfg; +}; + +static struct rk3399_dram_status rk3399_dram_status; +static struct ddr_dts_config_timing dts_parameter = { + .available = 0 +}; + +static struct rk3399_sdram_default_config ddr3_default_config = { + .bl = 8, + .ap = 0, + .dramds = 40, + .dramodt = 120, + .burst_ref_cnt = 1, + .zqcsi = 0 +}; + +static struct drv_odt_lp_config ddr3_drv_odt_default_config = { + .ddr3_speed_bin = DDR3_DEFAULT, + .pd_idle = 0, + .sr_idle = 0, + .sr_mc_gate_idle = 0, + .srpd_lite_idle = 0, + .standby_idle = 0, + + .ddr3_dll_dis_freq = 300, + .phy_dll_dis_freq = 125, + .odt_dis_freq = 933, + + .dram_side_drv = 40, + .dram_side_dq_odt = 120, + .dram_side_ca_odt = 120, + + .phy_side_ca_drv = 40, + .phy_side_ck_cs_drv = 40, + .phy_side_dq_drv = 40, + .phy_side_odt = 240, +}; + +static struct rk3399_sdram_default_config lpddr3_default_config = { + .bl = 8, + .ap = 0, + .dramds = 34, + .dramodt = 240, + .burst_ref_cnt = 1, + .zqcsi = 0 +}; + +static struct drv_odt_lp_config lpddr3_drv_odt_default_config = { + .ddr3_speed_bin = DDR3_DEFAULT, + .pd_idle = 0, + .sr_idle = 0, + .sr_mc_gate_idle = 0, + .srpd_lite_idle = 0, + .standby_idle = 0, + + .ddr3_dll_dis_freq = 300, + .phy_dll_dis_freq = 125, + .odt_dis_freq = 666, + + .dram_side_drv = 40, + .dram_side_dq_odt = 120, + .dram_side_ca_odt = 120, + + .phy_side_ca_drv = 40, + .phy_side_ck_cs_drv = 40, + .phy_side_dq_drv = 40, + .phy_side_odt = 240, +}; + +static struct rk3399_sdram_default_config lpddr4_default_config = { + .bl = 16, + .ap = 0, + .dramds = 40, + .dramodt = 240, + .caodt = 240, + .burst_ref_cnt = 1, + .zqcsi = 0 +}; + +static struct drv_odt_lp_config lpddr4_drv_odt_default_config = { + .ddr3_speed_bin = DDR3_DEFAULT, + .pd_idle = 0, + .sr_idle = 0, + .sr_mc_gate_idle = 0, + .srpd_lite_idle = 0, + .standby_idle = 0, + + .ddr3_dll_dis_freq = 300, + .phy_dll_dis_freq = 125, + .odt_dis_freq = 933, + + .dram_side_drv = 60, + .dram_side_dq_odt = 40, + .dram_side_ca_odt = 40, + + .phy_side_ca_drv = 40, + .phy_side_ck_cs_drv = 80, + .phy_side_dq_drv = 80, + .phy_side_odt = 60, +}; + +uint32_t dcf_code[] = { +#include "dcf_code.inc" +}; + + +#define write_32(addr, value)\ + mmio_write_32((uintptr_t)(addr), (uint32_t)(value)) + +#define read_32(addr) \ + mmio_read_32((uintptr_t)(addr)) +#define clrbits_32(addr, clear)\ + mmio_clrbits_32((uintptr_t)(addr), (uint32_t)(clear)) +#define setbits_32(addr, set)\ + mmio_setbits_32((uintptr_t)(addr), (uint32_t)(set)) +#define clrsetbits_32(addr, clear, set)\ + mmio_clrsetbits_32((uintptr_t)(addr), (uint32_t)(clear),\ + (uint32_t)(set)) + +#define DCF_START_ADDR (SRAM_BASE + 0x1400) +#define DCF_PARAM_ADDR (SRAM_BASE + 0x1000) + +/* DCF_PAMET */ +#define PARAM_DRAM_FREQ (0) +#define PARAM_DPLL_CON0 (4) +#define PARAM_DPLL_CON1 (8) +#define PARAM_DPLL_CON2 (0xc) +#define PARAM_DPLL_CON3 (0x10) +#define PARAM_DPLL_CON4 (0x14) +#define PARAM_DPLL_CON5 (0x18) +/* equal to fn<<4 */ +#define PARAM_FREQ_SELECT (0x1c) + +static unsigned int get_cs_die_capability(struct rk3399_sdram_config + *psdram_config, unsigned int channel, + unsigned int cs) +{ + unsigned int die; + unsigned int cs_cap; + unsigned int row[2]; + + row[0] = psdram_config->ch[channel].cs0_row; + row[1] = psdram_config->ch[channel].cs1_row; + die = psdram_config->ch[channel].bus_width / + psdram_config->ch[channel].each_die_bus_width; + cs_cap = (1 << (row[cs] + + (psdram_config->ch[channel].bank / 4 + 1) + + psdram_config->ch[channel].col + + (psdram_config->ch[channel].bus_width / 16))); + if (psdram_config->ch[channel].each_die_6gb_or_12gb) + cs_cap = cs_cap * 3 / 4; + + return (cs_cap / die); +} + +static void sdram_config_init(struct rk3399_sdram_config *psdram_config) +{ + uint32_t os_reg2_val, i; + + os_reg2_val = read_32(PMUGRF_BASE + PMUGRF_OSREG(2)); + + for (i = 0; i < READ_CH_CNT(os_reg2_val); i++) { + psdram_config->ch[i].bank = 1 << READ_BK_INFO(os_reg2_val, i); + psdram_config->ch[i].bus_width = + 8 * (1 << READ_BW_INFO(os_reg2_val, i)); + psdram_config->ch[i].col = READ_COL_INFO(os_reg2_val, i); + psdram_config->ch[i].cs0_row = + READ_CS0_ROW_INFO(os_reg2_val, i); + psdram_config->ch[i].cs1_row = + READ_CS1_ROW_INFO(os_reg2_val, i); + psdram_config->ch[i].cs_cnt = READ_CS_INFO(os_reg2_val, i); + psdram_config->ch[i].each_die_6gb_or_12gb = + READ_CH_ROW_INFO(os_reg2_val, i); + psdram_config->ch[i].each_die_bus_width = + 8 * (1 << READ_DIE_BW_INFO(os_reg2_val, i)); + } + psdram_config->dramtype = READ_DRAMTYPE_INFO(os_reg2_val); + psdram_config->channal_num = READ_CH_CNT(os_reg2_val); +} + +static void drv_odt_lp_cfg_init(uint32_t dram_type, + struct ddr_dts_config_timing *dts_timing, + struct drv_odt_lp_config *drv_config) +{ + if ((dts_timing) && (dts_timing->available)) { + drv_config->ddr3_speed_bin = dts_timing->ddr3_speed_bin; + drv_config->pd_idle = dts_timing->pd_idle; + drv_config->sr_idle = dts_timing->sr_idle; + drv_config->sr_mc_gate_idle = dts_timing->sr_mc_gate_idle; + drv_config->srpd_lite_idle = dts_timing->srpd_lite_idle; + drv_config->standby_idle = dts_timing->standby_idle; + drv_config->ddr3_dll_dis_freq = dts_timing->ddr3_dll_dis_freq; + drv_config->phy_dll_dis_freq = dts_timing->phy_dll_dis_freq; + } + + switch (dram_type) { + case DDR3: + if ((dts_timing) && (dts_timing->available)) { + drv_config->odt_dis_freq = + dts_timing->ddr3_odt_dis_freq; + drv_config->dram_side_drv = dts_timing->ddr3_drv; + drv_config->dram_side_dq_odt = dts_timing->ddr3_odt; + drv_config->phy_side_ca_drv = + dts_timing->phy_ddr3_ca_drv; + drv_config->phy_side_ck_cs_drv = + dts_timing->phy_ddr3_ca_drv; + drv_config->phy_side_dq_drv = + dts_timing->phy_ddr3_dq_drv; + drv_config->phy_side_odt = dts_timing->phy_ddr3_odt; + } else { + memcpy(drv_config, &ddr3_drv_odt_default_config, + sizeof(struct drv_odt_lp_config)); + } + break; + case LPDDR3: + if ((dts_timing) && (dts_timing->available)) { + drv_config->odt_dis_freq = + dts_timing->lpddr3_odt_dis_freq; + drv_config->dram_side_drv = dts_timing->lpddr3_drv; + drv_config->dram_side_dq_odt = dts_timing->lpddr3_odt; + drv_config->phy_side_ca_drv = + dts_timing->phy_lpddr3_ca_drv; + drv_config->phy_side_ck_cs_drv = + dts_timing->phy_lpddr3_ca_drv; + drv_config->phy_side_dq_drv = + dts_timing->phy_lpddr3_dq_drv; + drv_config->phy_side_odt = dts_timing->phy_lpddr3_odt; + + } else { + memcpy(drv_config, &lpddr3_drv_odt_default_config, + sizeof(struct drv_odt_lp_config)); + } + break; + case LPDDR4: + default: + if ((dts_timing) && (dts_timing->available)) { + drv_config->odt_dis_freq = + dts_timing->lpddr4_odt_dis_freq; + drv_config->dram_side_drv = dts_timing->lpddr4_drv; + drv_config->dram_side_dq_odt = + dts_timing->lpddr4_dq_odt; + drv_config->dram_side_ca_odt = + dts_timing->lpddr4_ca_odt; + drv_config->phy_side_ca_drv = + dts_timing->phy_lpddr4_ca_drv; + drv_config->phy_side_ck_cs_drv = + dts_timing->phy_lpddr4_ck_cs_drv; + drv_config->phy_side_dq_drv = + dts_timing->phy_lpddr4_dq_drv; + drv_config->phy_side_odt = dts_timing->phy_lpddr4_odt; + } else { + memcpy(drv_config, &lpddr4_drv_odt_default_config, + sizeof(struct drv_odt_lp_config)); + } + break; + } + + switch (drv_config->phy_side_ca_drv) { + case 240: + drv_config->phy_side_ca_drv = PHY_DRV_ODT_240; + break; + case 120: + drv_config->phy_side_ca_drv = PHY_DRV_ODT_120; + break; + case 80: + drv_config->phy_side_ca_drv = PHY_DRV_ODT_80; + break; + case 60: + drv_config->phy_side_ca_drv = PHY_DRV_ODT_60; + break; + case 48: + drv_config->phy_side_ca_drv = PHY_DRV_ODT_48; + break; + case 40: + drv_config->phy_side_ca_drv = PHY_DRV_ODT_40; + break; + default: + drv_config->phy_side_ca_drv = PHY_DRV_ODT_34_3; + break; + }; + + switch (drv_config->phy_side_ck_cs_drv) { + case 240: + drv_config->phy_side_ck_cs_drv = PHY_DRV_ODT_240; + break; + case 120: + drv_config->phy_side_ck_cs_drv = PHY_DRV_ODT_120; + break; + case 80: + drv_config->phy_side_ck_cs_drv = PHY_DRV_ODT_80; + break; + case 60: + drv_config->phy_side_ck_cs_drv = PHY_DRV_ODT_60; + break; + case 48: + drv_config->phy_side_ck_cs_drv = PHY_DRV_ODT_48; + break; + case 40: + drv_config->phy_side_ck_cs_drv = PHY_DRV_ODT_40; + break; + default: + drv_config->phy_side_ck_cs_drv = PHY_DRV_ODT_34_3; + break; + } + + switch (drv_config->phy_side_dq_drv) { + case 240: + drv_config->phy_side_dq_drv = PHY_DRV_ODT_240; + break; + case 120: + drv_config->phy_side_dq_drv = PHY_DRV_ODT_120; + break; + case 80: + drv_config->phy_side_dq_drv = PHY_DRV_ODT_80; + break; + case 60: + drv_config->phy_side_dq_drv = PHY_DRV_ODT_60; + break; + case 48: + drv_config->phy_side_dq_drv = PHY_DRV_ODT_48; + break; + case 40: + drv_config->phy_side_dq_drv = PHY_DRV_ODT_40; + break; + default: + drv_config->phy_side_dq_drv = PHY_DRV_ODT_34_3; + break; + } + + switch (drv_config->phy_side_odt) { + case 240: + drv_config->phy_side_odt = PHY_DRV_ODT_240; + break; + case 120: + drv_config->phy_side_odt = PHY_DRV_ODT_120; + break; + case 80: + drv_config->phy_side_odt = PHY_DRV_ODT_80; + break; + case 60: + drv_config->phy_side_odt = PHY_DRV_ODT_60; + break; + case 48: + drv_config->phy_side_odt = PHY_DRV_ODT_48; + break; + case 40: + drv_config->phy_side_odt = PHY_DRV_ODT_40; + break; + default: + drv_config->phy_side_odt = PHY_DRV_ODT_34_3; + break; + } +} + +static void sdram_timing_cfg_init(struct timing_related_config *ptiming_config, + struct rk3399_sdram_config *psdram_config, + struct drv_odt_lp_config *drv_config) +{ + uint32_t i, j; + + for (i = 0; i < psdram_config->channal_num; i++) { + ptiming_config->dram_info[i].speed_rate = + drv_config->ddr3_speed_bin; + ptiming_config->dram_info[i].cs_cnt = + psdram_config->ch[i].cs_cnt; + for (j = 0; j < psdram_config->ch[i].cs_cnt; j++) { + ptiming_config->dram_info[i].per_die_capability[j] = + get_cs_die_capability(psdram_config, i, j); + } + } + ptiming_config->dram_type = psdram_config->dramtype; + ptiming_config->ch_cnt = psdram_config->channal_num; + switch (psdram_config->dramtype) { + case DDR3: + ptiming_config->bl = ddr3_default_config.bl; + ptiming_config->ap = ddr3_default_config.ap; + break; + case LPDDR3: + ptiming_config->bl = lpddr3_default_config.bl; + ptiming_config->ap = lpddr3_default_config.ap; + break; + case LPDDR4: + ptiming_config->bl = lpddr4_default_config.bl; + ptiming_config->ap = lpddr4_default_config.ap; + ptiming_config->rdbi = 0; + ptiming_config->wdbi = 0; + break; + } + ptiming_config->dramds = drv_config->dram_side_drv; + ptiming_config->dramodt = drv_config->dram_side_dq_odt; + ptiming_config->caodt = drv_config->dram_side_ca_odt; +} + +struct lat_adj_pair { + uint32_t cl; + uint32_t rdlat_adj; + uint32_t cwl; + uint32_t wrlat_adj; +}; + +const struct lat_adj_pair ddr3_lat_adj[] = { + {6, 5, 5, 4}, + {8, 7, 6, 5}, + {10, 9, 7, 6}, + {11, 9, 8, 7}, + {13, 0xb, 9, 8}, + {14, 0xb, 0xa, 9} +}; + +const struct lat_adj_pair lpddr3_lat_adj[] = { + {3, 2, 1, 0}, + {6, 5, 3, 2}, + {8, 7, 4, 3}, + {9, 8, 5, 4}, + {10, 9, 6, 5}, + {11, 9, 6, 5}, + {12, 0xa, 6, 5}, + {14, 0xc, 8, 7}, + {16, 0xd, 8, 7} +}; + +const struct lat_adj_pair lpddr4_lat_adj[] = { + {6, 5, 4, 2}, + {10, 9, 6, 4}, + {14, 0xc, 8, 6}, + {20, 0x11, 0xa, 8}, + {24, 0x15, 0xc, 0xa}, + {28, 0x18, 0xe, 0xc}, + {32, 0x1b, 0x10, 0xe}, + {36, 0x1e, 0x12, 0x10} +}; + +static uint32_t get_rdlat_adj(uint32_t dram_type, uint32_t cl) +{ + const struct lat_adj_pair *p; + uint32_t cnt; + uint32_t i; + + if (dram_type == DDR3) { + p = ddr3_lat_adj; + cnt = ARRAY_SIZE(ddr3_lat_adj); + } else if (dram_type == LPDDR3) { + p = lpddr3_lat_adj; + cnt = ARRAY_SIZE(lpddr3_lat_adj); + } else { + p = lpddr4_lat_adj; + cnt = ARRAY_SIZE(lpddr4_lat_adj); + } + + for (i = 0; i < cnt; i++) { + if (cl == p[i].cl) + return p[i].rdlat_adj; + } + /* fail */ + return 0xff; +} + +static uint32_t get_wrlat_adj(uint32_t dram_type, uint32_t cwl) +{ + const struct lat_adj_pair *p; + uint32_t cnt; + uint32_t i; + + if (dram_type == DDR3) { + p = ddr3_lat_adj; + cnt = ARRAY_SIZE(ddr3_lat_adj); + } else if (dram_type == LPDDR3) { + p = lpddr3_lat_adj; + cnt = ARRAY_SIZE(lpddr3_lat_adj); + } else { + p = lpddr4_lat_adj; + cnt = ARRAY_SIZE(lpddr4_lat_adj); + } + + for (i = 0; i < cnt; i++) { + if (cwl == p[i].cwl) + return p[i].wrlat_adj; + } + /* fail */ + return 0xff; +} + +#define PI_REGS_DIMM_SUPPORT (0) +#define PI_ADD_LATENCY (0) +#define PI_DOUBLEFREEK (1) + +#define PI_PAD_DELAY_PS_VALUE (1000) +#define PI_IE_ENABLE_VALUE (3000) +#define PI_TSEL_ENABLE_VALUE (700) + +static uint32_t get_pi_rdlat_adj(struct dram_timing_t *pdram_timing) +{ + /*[DLLSUBTYPE2] == "STD_DENALI_HS" */ + uint32_t rdlat, delay_adder, ie_enable, hs_offset, tsel_adder, + extra_adder, tsel_enable; + + ie_enable = PI_IE_ENABLE_VALUE; + tsel_enable = PI_TSEL_ENABLE_VALUE; + + rdlat = pdram_timing->cl + PI_ADD_LATENCY; + delay_adder = ie_enable / (1000000 / pdram_timing->mhz); + if ((ie_enable % (1000000 / pdram_timing->mhz)) != 0) + delay_adder++; + hs_offset = 0; + tsel_adder = 0; + extra_adder = 0; + /* rdlat = rdlat - (PREAMBLE_SUPPORT & 0x1); */ + tsel_adder = tsel_enable / (1000000 / pdram_timing->mhz); + if ((tsel_enable % (1000000 / pdram_timing->mhz)) != 0) + tsel_adder++; + delay_adder = delay_adder - 1; + if (tsel_adder > delay_adder) + extra_adder = tsel_adder - delay_adder; + else + extra_adder = 0; + if (PI_REGS_DIMM_SUPPORT && PI_DOUBLEFREEK) + hs_offset = 2; + else + hs_offset = 1; + + if (delay_adder > (rdlat - 1 - hs_offset)) { + rdlat = rdlat - tsel_adder; + } else { + if ((rdlat - delay_adder) < 2) + rdlat = 2; + else + rdlat = rdlat - delay_adder - extra_adder; + } + + return rdlat; +} + +static uint32_t get_pi_wrlat(struct dram_timing_t *pdram_timing, + struct timing_related_config *timing_config) +{ + uint32_t tmp; + + if (timing_config->dram_type == LPDDR3) { + tmp = pdram_timing->cl; + if (tmp >= 14) + tmp = 8; + else if (tmp >= 10) + tmp = 6; + else if (tmp == 9) + tmp = 5; + else if (tmp == 8) + tmp = 4; + else if (tmp == 6) + tmp = 3; + else + tmp = 1; + } else { + tmp = 1; + } + + return tmp; +} + +static uint32_t get_pi_wrlat_adj(struct dram_timing_t *pdram_timing, + struct timing_related_config *timing_config) +{ + return get_pi_wrlat(pdram_timing, timing_config) + PI_ADD_LATENCY - 1; +} + +static uint32_t get_pi_tdfi_phy_rdlat(struct dram_timing_t *pdram_timing, + struct timing_related_config *timing_config) +{ + /* [DLLSUBTYPE2] == "STD_DENALI_HS" */ + uint32_t cas_lat, delay_adder, ie_enable, hs_offset, ie_delay_adder; + uint32_t mem_delay_ps, round_trip_ps; + uint32_t phy_internal_delay, lpddr_adder, dfi_adder, rdlat_delay; + + ie_enable = PI_IE_ENABLE_VALUE; + + delay_adder = ie_enable / (1000000 / pdram_timing->mhz); + if ((ie_enable % (1000000 / pdram_timing->mhz)) != 0) + delay_adder++; + delay_adder = delay_adder - 1; + if (PI_REGS_DIMM_SUPPORT && PI_DOUBLEFREEK) + hs_offset = 2; + else + hs_offset = 1; + + cas_lat = pdram_timing->cl + PI_ADD_LATENCY; + + if (delay_adder > (cas_lat - 1 - hs_offset)) { + ie_delay_adder = 0; + } else { + ie_delay_adder = ie_enable / (1000000 / pdram_timing->mhz); + if ((ie_enable % (1000000 / pdram_timing->mhz)) != 0) + ie_delay_adder++; + } + + if (timing_config->dram_type == DDR3) { + mem_delay_ps = 0; + } else if (timing_config->dram_type == LPDDR4) { + mem_delay_ps = 3600; + } else if (timing_config->dram_type == LPDDR3) { + mem_delay_ps = 5500; + } else { + printf("get_pi_tdfi_phy_rdlat:dramtype unsupport\n"); + return 0; + } + round_trip_ps = 1100 + 500 + mem_delay_ps + 500 + 600; + delay_adder = round_trip_ps / (1000000 / pdram_timing->mhz); + if ((round_trip_ps % (1000000 / pdram_timing->mhz)) != 0) + delay_adder++; + + phy_internal_delay = 5 + 2 + 4; + lpddr_adder = mem_delay_ps / (1000000 / pdram_timing->mhz); + if ((mem_delay_ps % (1000000 / pdram_timing->mhz)) != 0) + lpddr_adder++; + dfi_adder = 0; + phy_internal_delay = phy_internal_delay + 2; + rdlat_delay = delay_adder + phy_internal_delay + + ie_delay_adder + lpddr_adder + dfi_adder; + + rdlat_delay = rdlat_delay + 2; + return rdlat_delay; +} + +static uint32_t get_pi_todtoff_min(struct dram_timing_t *pdram_timing, + struct timing_related_config *timing_config) +{ + uint32_t tmp, todtoff_min_ps; + + if (timing_config->dram_type == LPDDR3) + todtoff_min_ps = 2500; + else if (timing_config->dram_type == LPDDR4) + todtoff_min_ps = 1500; + else + todtoff_min_ps = 0; + /* todtoff_min */ + tmp = todtoff_min_ps / (1000000 / pdram_timing->mhz); + if ((todtoff_min_ps % (1000000 / pdram_timing->mhz)) != 0) + tmp++; + return tmp; +} + +static uint32_t get_pi_todtoff_max(struct dram_timing_t *pdram_timing, + struct timing_related_config *timing_config) +{ + uint32_t tmp, todtoff_max_ps; + + if ((timing_config->dram_type == LPDDR4) + || (timing_config->dram_type == LPDDR3)) + todtoff_max_ps = 3500; + else + todtoff_max_ps = 0; + + /* todtoff_max */ + tmp = todtoff_max_ps / (1000000 / pdram_timing->mhz); + if ((todtoff_max_ps % (1000000 / pdram_timing->mhz)) != 0) + tmp++; + return tmp; +} + +static void gen_rk3399_ctl_params_f0(struct timing_related_config + *timing_config, + struct dram_timing_t *pdram_timing) +{ + uint32_t i; + uint32_t tmp, tmp1; + + for (i = 0; i < timing_config->ch_cnt; i++) { + if (timing_config->dram_type == DDR3) { + tmp = ((700000 + 10) * timing_config->freq + + 999) / 1000; + tmp += pdram_timing->txsnr + (pdram_timing->tmrd * 3) + + pdram_timing->tmod + pdram_timing->tzqinit; + write_32(&rk3399_ddr_pctl[i]->denali_ctl[5], tmp); + + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[22], + 0xffff, pdram_timing->tdllk); + + write_32(&rk3399_ddr_pctl[i]->denali_ctl[32], + (pdram_timing->tmod << 8) | + pdram_timing->tmrd); + + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[59], + 0xffff << 16, + (pdram_timing->txsr - + pdram_timing->trcd) << 16); + } else if (timing_config->dram_type == LPDDR4) { + write_32(&rk3399_ddr_pctl[i]->denali_ctl[5], + pdram_timing->tinit1 + pdram_timing->tinit3); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[32], + (pdram_timing->tmrd << 8) | + pdram_timing->tmrd); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[59], + 0xffff << 16, pdram_timing->txsr << 16); + } else { + write_32(&rk3399_ddr_pctl[i]->denali_ctl[5], + pdram_timing->tinit1); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[7], + pdram_timing->tinit4); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[32], + (pdram_timing->tmrd << 8) | + pdram_timing->tmrd); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[59], + 0xffff << 16, pdram_timing->txsr << 16); + } + write_32(&rk3399_ddr_pctl[i]->denali_ctl[6], + pdram_timing->tinit3); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[8], + pdram_timing->tinit5); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[23], (0x7f << 16), + ((pdram_timing->cl * 2) << 16)); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[23], (0x1f << 24), + (pdram_timing->cwl << 24)); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[24], 0x3f, + pdram_timing->al); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[26], 0xffff << 16, + (pdram_timing->trc << 24) | + (pdram_timing->trrd << 16)); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[27], + (pdram_timing->tfaw << 24) | + (pdram_timing->trppb << 16) | + (pdram_timing->twtr << 8) | pdram_timing->tras_min); + + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[31], 0xff << 24, + max(4, pdram_timing->trtp) << 24); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[33], + (pdram_timing->tcke << 24) | pdram_timing->tras_max); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[34], 0xff, + max(1, pdram_timing->tckesr)); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[39], + (0x3f << 16) | (0xff << 8), + (pdram_timing->twr << 16) | + (pdram_timing->trcd << 8)); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[42], 0x1f << 16, + pdram_timing->tmrz << 16); + tmp = pdram_timing->tdal ? pdram_timing->tdal : + (pdram_timing->twr + pdram_timing->trp); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[44], 0xff, tmp); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[45], 0xff, + pdram_timing->trp); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[48], + ((pdram_timing->trefi - 8) << 16) | + pdram_timing->trfc); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[52], 0xffff, + pdram_timing->txp); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[53], 0xffff << 16, + pdram_timing->txpdll << 16); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[55], 0xf << 24, + pdram_timing->tcscke << 24); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[55], 0xff, + pdram_timing->tmrri); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[56], + (pdram_timing->tzqcke << 24) | + (pdram_timing->tmrwckel << 16) | + (pdram_timing->tckehcs << 8) | pdram_timing->tckelcs); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[60], 0xffff, + pdram_timing->txsnr); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[62], 0xffff << 16, + (pdram_timing->tckehcmd << 24) | + (pdram_timing->tckelcmd << 16)); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[63], + (pdram_timing->tckelpd << 24) | + (pdram_timing->tescke << 16) | + (pdram_timing->tsr << 8) | pdram_timing->tckckel); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[64], 0xfff, + (pdram_timing->tcmdcke << 8) | + pdram_timing->tcsckeh); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[92], + (0xffff << 8), + (pdram_timing->tcksrx << 16) | + (pdram_timing->tcksre << 8)); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[108], (0x1 << 24), + (timing_config->dllbp << 24)); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[122], + (0x3FF << 16), + (pdram_timing->tvrcg_enable << 16)); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[123], + (pdram_timing->tfc_long << 16) | + pdram_timing->tvrcg_disable); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[124], + (pdram_timing->tvref_long << 16) | + (pdram_timing->tckfspx << 8) | + pdram_timing->tckfspe); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[133], + (pdram_timing->mr[1] << 16) | pdram_timing->mr[0]); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[134], 0xffff, + pdram_timing->mr[2]); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[138], 0xffff, + pdram_timing->mr[3]); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[139], 0xff << 24, + pdram_timing->mr11 << 24); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[147], + (pdram_timing->mr[1] << 16) | pdram_timing->mr[0]); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[148], 0xffff, + pdram_timing->mr[2]); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[152], 0xffff, + pdram_timing->mr[3]); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[153], 0xff << 24, + pdram_timing->mr11 << 24); + if (timing_config->dram_type == LPDDR4) { + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[140], + 0xffff << 16, pdram_timing->mr12 << 16); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[142], + 0xffff << 16, pdram_timing->mr14 << 16); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[145], + 0xffff << 16, pdram_timing->mr22 << 16); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[154], + 0xffff << 16, pdram_timing->mr12 << 16); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[156], + 0xffff << 16, pdram_timing->mr14 << 16); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[159], + 0xffff << 16, pdram_timing->mr22 << 16); + } + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[179], 0xfff << 8, + pdram_timing->tzqinit << 8); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[180], + (pdram_timing->tzqcs << 16) | + (pdram_timing->tzqinit / 2)); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[181], + (pdram_timing->tzqlat << 16) | pdram_timing->tzqcal); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[212], 0xff << 8, + pdram_timing->todton << 8); + + if (timing_config->odt) { + setbits_32(&rk3399_ddr_pctl[i]->denali_ctl[213], + 1 << 16); + if (timing_config->freq < 400) + tmp = 4 << 24; + else + tmp = 8 << 24; + } else { + clrbits_32(&rk3399_ddr_pctl[i]->denali_ctl[213], + 1 << 16); + tmp = 2 << 24; + } + + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[216], + 0x1f << 24, tmp); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[221], + (0x3 << 16) | (0xf << 8), + (pdram_timing->tdqsck << 16) | + (pdram_timing->tdqsck_max << 8)); + tmp = + (get_wrlat_adj(timing_config->dram_type, pdram_timing->cwl) + << 8) | get_rdlat_adj(timing_config->dram_type, + pdram_timing->cl); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[284], 0xffff, + tmp); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[82], 0xffff << 16, + (4 * pdram_timing->trefi) << 16); + + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[83], 0xffff, + (2 * pdram_timing->trefi) & 0xffff); + + if ((timing_config->dram_type == LPDDR3) || + (timing_config->dram_type == LPDDR4)) { + tmp = get_pi_wrlat(pdram_timing, timing_config); + tmp1 = get_pi_todtoff_max(pdram_timing, timing_config); + tmp = (tmp > tmp1) ? (tmp - tmp1) : 0; + } else { + tmp = 0; + } + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[214], 0x3f << 16, + (tmp & 0x3f) << 16); + + if ((timing_config->dram_type == LPDDR3) || + (timing_config->dram_type == LPDDR4)) { + /* min_rl_preamble= cl+TDQSCK_MIN-1 */ + tmp = pdram_timing->cl + + get_pi_todtoff_min(pdram_timing, timing_config) - 1; + /* todtoff_max */ + tmp1 = get_pi_todtoff_max(pdram_timing, timing_config); + tmp = (tmp > tmp1) ? (tmp - tmp1) : 0; + } else { + tmp = pdram_timing->cl - pdram_timing->cwl; + } + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[215], 0x3f << 8, + (tmp & 0x3f) << 8); + + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[275], 0xff << 16, + (get_pi_tdfi_phy_rdlat + (pdram_timing, timing_config) + & 0xff) << 16); + + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[277], 0xffff, + (2 * pdram_timing->trefi) & 0xffff); + + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[282], 0xffff, + (2 * pdram_timing->trefi) & 0xffff); + + write_32(&rk3399_ddr_pctl[i]->denali_ctl[283], + 20 * pdram_timing->trefi); + + /* CTL_308 TDFI_CALVL_CAPTURE_F0:RW:16:10 */ + tmp1 = 20000 / (1000000 / pdram_timing->mhz) + 1; + if ((20000 % (1000000 / pdram_timing->mhz)) != 0) + tmp1++; + tmp = (tmp1 >> 1) + (tmp1 % 2) + 5; + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[308], 0x3ff << 16, + tmp << 16); + + /* CTL_308 TDFI_CALVL_CC_F0:RW:0:10 */ + tmp = tmp + 18; + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[308], 0x3ff, + tmp); + + /* CTL_314 TDFI_WRCSLAT_F0:RW:8:8 */ + tmp1 = get_pi_wrlat_adj(pdram_timing, timing_config); + if (timing_config->freq <= ENPER_CS_TRAINING_FREQ) { + if (tmp1 < 5) { + if (tmp1 == 0) + tmp = 0; + else + tmp = tmp1 - 1; + } else { + tmp = tmp1 - 5; + } + } else { + tmp = tmp1 - 2; + } + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[314], 0xff << 8, + tmp << 8); + + /* CTL_314 TDFI_RDCSLAT_F0:RW:0:8 */ + if ((timing_config->freq <= ENPER_CS_TRAINING_FREQ) && + (pdram_timing->cl >= 5)) + tmp = pdram_timing->cl - 5; + else + tmp = pdram_timing->cl - 2; + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[314], 0xff, + tmp); + } +} + +static void gen_rk3399_ctl_params_f1(struct timing_related_config + *timing_config, + struct dram_timing_t *pdram_timing) +{ + uint32_t i; + uint32_t tmp, tmp1; + + for (i = 0; i < timing_config->ch_cnt; i++) { + if (timing_config->dram_type == DDR3) { + tmp = + ((700000 + 10) * timing_config->freq + + 999) / 1000; + tmp += + pdram_timing->txsnr + (pdram_timing->tmrd * 3) + + pdram_timing->tmod + pdram_timing->tzqinit; + write_32(&rk3399_ddr_pctl[i]->denali_ctl[9], tmp); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[22], + 0xffff << 16, pdram_timing->tdllk << 16); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[34], + 0xffffff00, + (pdram_timing->tmod << 24) | + (pdram_timing->tmrd << 16) | + (pdram_timing->trtp << 8)); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[60], + 0xffff << 16, + (pdram_timing->txsr - + pdram_timing->trcd) << 16); + } else if (timing_config->dram_type == LPDDR4) { + write_32(&rk3399_ddr_pctl[i]->denali_ctl[9], + pdram_timing->tinit1 + pdram_timing->tinit3); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[34], + 0xffffff00, + (pdram_timing->tmrd << 24) | + (pdram_timing->tmrd << 16) | + (pdram_timing->trtp << 8)); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[60], + 0xffff << 16, pdram_timing->txsr << 16); + } else { + write_32(&rk3399_ddr_pctl[i]->denali_ctl[9], + pdram_timing->tinit1); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[11], + pdram_timing->tinit4); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[34], + 0xffffff00, + (pdram_timing->tmrd << 24) | + (pdram_timing->tmrd << 16) | + (pdram_timing->trtp << 8)); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[60], + 0xffff << 16, pdram_timing->txsr << 16); + } + write_32(&rk3399_ddr_pctl[i]->denali_ctl[10], + pdram_timing->tinit3); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[12], + pdram_timing->tinit5); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[24], (0x7f << 8), + ((pdram_timing->cl * 2) << 8)); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[24], (0x1f << 16), + (pdram_timing->cwl << 16)); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[24], 0x3f << 24, + pdram_timing->al << 24); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[28], 0xffffff00, + (pdram_timing->tras_min << 24) | + (pdram_timing->trc << 16) | + (pdram_timing->trrd << 8)); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[29], 0xffffff, + (pdram_timing->tfaw << 16) | + (pdram_timing->trppb << 8) | pdram_timing->twtr); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[35], + (pdram_timing->tcke << 24) | pdram_timing->tras_max); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[36], 0xff, + max(1, pdram_timing->tckesr)); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[39], + (0xff << 24), (pdram_timing->trcd << 24)); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[40], + 0x3f, pdram_timing->twr); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[42], 0x1f << 24, + pdram_timing->tmrz << 24); + tmp = pdram_timing->tdal ? pdram_timing->tdal : + (pdram_timing->twr + pdram_timing->trp); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[44], 0xff << 8, + tmp << 8); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[45], 0xff << 8, + pdram_timing->trp << 8); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[49], + ((pdram_timing->trefi - 8) << 16) | + pdram_timing->trfc); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[52], 0xffff << 16, + pdram_timing->txp << 16); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[54], 0xffff, + pdram_timing->txpdll); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[55], 0xff << 8, + pdram_timing->tmrri << 8); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[57], + (pdram_timing->tmrwckel << 24) | + (pdram_timing->tckehcs << 16) | + (pdram_timing->tckelcs << 8) | pdram_timing->tcscke); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[58], 0xf, + pdram_timing->tzqcke); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[61], 0xffff, + pdram_timing->txsnr); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[64], 0xffff << 16, + (pdram_timing->tckehcmd << 24) | + (pdram_timing->tckelcmd << 16)); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[65], + (pdram_timing->tckelpd << 24) | + (pdram_timing->tescke << 16) | + (pdram_timing->tsr << 8) | pdram_timing->tckckel); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[66], 0xfff, + (pdram_timing->tcmdcke << 8) | + pdram_timing->tcsckeh); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[92], (0xff << 24), + (pdram_timing->tcksre << 24)); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[93], 0xff, + pdram_timing->tcksrx); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[108], (0x1 << 25), + (timing_config->dllbp << 25)); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[125], + (pdram_timing->tvrcg_disable << 16) | + pdram_timing->tvrcg_enable); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[126], + (pdram_timing->tckfspx << 24) | + (pdram_timing->tckfspe << 16) | + pdram_timing->tfc_long); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[127], 0xffff, + pdram_timing->tvref_long); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[134], + 0xffff << 16, pdram_timing->mr[0] << 16); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[135], + (pdram_timing->mr[2] << 16) | pdram_timing->mr[1]); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[138], + 0xffff << 16, pdram_timing->mr[3] << 16); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[140], 0xff, + pdram_timing->mr11); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[148], + 0xffff << 16, pdram_timing->mr[0] << 16); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[149], + (pdram_timing->mr[2] << 16) | pdram_timing->mr[1]); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[152], + 0xffff << 16, pdram_timing->mr[3] << 16); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[154], 0xff, + pdram_timing->mr11); + if (timing_config->dram_type == LPDDR4) { + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[141], + 0xffff, pdram_timing->mr12); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[143], + 0xffff, pdram_timing->mr14); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[146], + 0xffff, pdram_timing->mr22); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[155], + 0xffff, pdram_timing->mr12); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[157], + 0xffff, pdram_timing->mr14); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[160], + 0xffff, pdram_timing->mr22); + } + write_32(&rk3399_ddr_pctl[i]->denali_ctl[182], + ((pdram_timing->tzqinit / 2) << 16) | + pdram_timing->tzqinit); + write_32(&rk3399_ddr_pctl[i]->denali_ctl[183], + (pdram_timing->tzqcal << 16) | pdram_timing->tzqcs); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[184], 0x3f, + pdram_timing->tzqlat); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[188], 0xfff, + pdram_timing->tzqreset); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[212], 0xff << 16, + pdram_timing->todton << 16); + + if (timing_config->odt) { + setbits_32(&rk3399_ddr_pctl[i]->denali_ctl[213], + (1 << 24)); + if (timing_config->freq < 400) + tmp = 4 << 24; + else + tmp = 8 << 24; + } else { + clrbits_32(&rk3399_ddr_pctl[i]->denali_ctl[213], + (1 << 24)); + tmp = 2 << 24; + } + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[217], 0x1f << 24, + tmp); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[221], 0xf << 24, + (pdram_timing->tdqsck_max << 24)); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[222], 0x3, + pdram_timing->tdqsck); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[291], 0xffff, + (get_wrlat_adj(timing_config->dram_type, + pdram_timing->cwl) << 8) | + get_rdlat_adj(timing_config->dram_type, + pdram_timing->cl)); + + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[84], 0xffff, + (4 * pdram_timing->trefi) & 0xffff); + + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[84], 0xffff << 16, + ((2 * pdram_timing->trefi) & 0xffff) << 16); + + if ((timing_config->dram_type == LPDDR3) || + (timing_config->dram_type == LPDDR4)) { + tmp = get_pi_wrlat(pdram_timing, timing_config); + tmp1 = get_pi_todtoff_max(pdram_timing, timing_config); + tmp = (tmp > tmp1) ? (tmp - tmp1) : 0; + } else { + tmp = 0; + } + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[214], 0x3f << 24, + (tmp & 0x3f) << 24); + + if ((timing_config->dram_type == LPDDR3) || + (timing_config->dram_type == LPDDR4)) { + /* min_rl_preamble= cl+TDQSCK_MIN-1 */ + tmp = pdram_timing->cl + + get_pi_todtoff_min(pdram_timing, timing_config) - 1; + /* todtoff_max */ + tmp1 = get_pi_todtoff_max(pdram_timing, timing_config); + tmp = (tmp > tmp1) ? (tmp - tmp1) : 0; + } else { + tmp = pdram_timing->cl - pdram_timing->cwl; + } + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[215], 0x3f << 16, + (tmp & 0x3f) << 16); + + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[275], 0xff << 24, + (get_pi_tdfi_phy_rdlat + (pdram_timing, timing_config) + & 0xff) << 24); + + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[284], + 0xffff << 16, + ((2 * pdram_timing->trefi) & 0xffff) << 16); + + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[289], 0xffff, + (2 * pdram_timing->trefi) & 0xffff); + + write_32(&rk3399_ddr_pctl[i]->denali_ctl[290], + 20 * pdram_timing->trefi); + + /* CTL_309 TDFI_CALVL_CAPTURE_F1:RW:16:10 */ + tmp1 = 20000 / (1000000 / pdram_timing->mhz) + 1; + if ((20000 % (1000000 / pdram_timing->mhz)) != 0) + tmp1++; + tmp = (tmp1 >> 1) + (tmp1 % 2) + 5; + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[309], 0x3ff << 16, + tmp << 16); + + /* CTL_309 TDFI_CALVL_CC_F1:RW:0:10 */ + tmp = tmp + 18; + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[309], 0x3ff, + tmp); + + /* CTL_314 TDFI_WRCSLAT_F1:RW:24:8 */ + tmp1 = get_pi_wrlat_adj(pdram_timing, timing_config); + if (timing_config->freq <= ENPER_CS_TRAINING_FREQ) { + if (tmp1 < 5) { + if (tmp1 == 0) + tmp = 0; + else + tmp = tmp1 - 1; + } else { + tmp = tmp1 - 5; + } + } else { + tmp = tmp1 - 2; + } + + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[314], 0xff << 24, + tmp << 24); + + /* CTL_314 TDFI_RDCSLAT_F1:RW:16:8 */ + if ((timing_config->freq <= ENPER_CS_TRAINING_FREQ) && + (pdram_timing->cl >= 5)) + tmp = pdram_timing->cl - 5; + else + tmp = pdram_timing->cl - 2; + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[314], 0xff << 16, + tmp << 16); + } +} + +static void gen_rk3399_ctl_params(struct timing_related_config *timing_config, + struct dram_timing_t *pdram_timing, + uint32_t fn) +{ + if (fn == 0) + gen_rk3399_ctl_params_f0(timing_config, pdram_timing); + else + gen_rk3399_ctl_params_f1(timing_config, pdram_timing); + +#if CTL_TRAINING + uint32_t i, tmp0, tmp1; + + tmp0 = tmp1 = 0; +#if EN_READ_GATE_TRAINING + tmp1 = 1; +#endif + +#if EN_CA_TRAINING + tmp0 |= (1 << 8); +#endif + +#if EN_WRITE_LEVELING + tmp0 |= (1 << 16); +#endif + +#if EN_READ_LEVELING + tmp0 |= (1 << 24); +#endif + for (i = 0; i < timing_config->ch_cnt; i++) { + if (tmp0 | tmp1) + setbits_32(&rk3399_ddr_pctl[i]->denali_ctl[305], + 1 << 16); + if (tmp0) + setbits_32(&rk3399_ddr_pctl[i]->denali_ctl[70], tmp0); + if (tmp1) + setbits_32(&rk3399_ddr_pctl[i]->denali_ctl[71], tmp1); + } +#endif +} + +static void gen_rk3399_pi_params_f0(struct timing_related_config *timing_config, + struct dram_timing_t *pdram_timing) +{ + uint32_t tmp, tmp1, tmp2; + uint32_t i; + + for (i = 0; i < timing_config->ch_cnt; i++) { + /* PI_02 PI_TDFI_PHYMSTR_MAX_F0:RW:0:32 */ + tmp = 4 * pdram_timing->trefi; + write_32(&rk3399_ddr_pi[i]->denali_pi[2], tmp); + /* PI_03 PI_TDFI_PHYMSTR_RESP_F0:RW:0:16 */ + tmp = 2 * pdram_timing->trefi; + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[3], 0xffff, tmp); + /* PI_07 PI_TDFI_PHYUPD_RESP_F0:RW:16:16 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[7], 0xffff << 16, + tmp << 16); + + /* PI_42 PI_TDELAY_RDWR_2_BUS_IDLE_F0:RW:0:8 */ + if (timing_config->dram_type == LPDDR4) + tmp = 2; + else + tmp = 0; + tmp = (pdram_timing->bl / 2) + 4 + + (get_pi_rdlat_adj(pdram_timing) - 2) + tmp + + get_pi_tdfi_phy_rdlat(pdram_timing, timing_config); + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[42], 0xff, tmp); + /* PI_43 PI_WRLAT_F0:RW:0:5 */ + if (timing_config->dram_type == LPDDR3) { + tmp = get_pi_wrlat(pdram_timing, timing_config); + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[43], 0x1f, + tmp); + } + /* PI_43 PI_ADDITIVE_LAT_F0:RW:8:6 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[43], 0x3f << 8, + PI_ADD_LATENCY << 8); + + /* PI_43 PI_CASLAT_LIN_F0:RW:16:7 */ + tmp = pdram_timing->cl * 2; + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[43], 0x7f << 16, + tmp << 16); + /* PI_46 PI_TREF_F0:RW:16:16 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[46], 0xffff << 16, + pdram_timing->trefi << 16); + /* PI_46 PI_TRFC_F0:RW:0:10 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[46], 0x3ff, + pdram_timing->trfc); + /* PI_66 PI_TODTL_2CMD_F0:RW:24:8 */ + if (timing_config->dram_type == LPDDR3) { + tmp = get_pi_todtoff_max(pdram_timing, timing_config); + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[66], + 0xff << 24, tmp << 24); + } + /* PI_72 PI_WR_TO_ODTH_F0:RW:16:6 */ + if ((timing_config->dram_type == LPDDR3) || + (timing_config->dram_type == LPDDR4)) { + tmp1 = get_pi_wrlat(pdram_timing, timing_config); + tmp2 = get_pi_todtoff_max(pdram_timing, timing_config); + if (tmp1 > tmp2) + tmp = tmp1 - tmp2; + else + tmp = 0; + } else if (timing_config->dram_type == DDR3) { + tmp = 0; + } + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[72], 0x3f << 16, + tmp << 16); + /* PI_73 PI_RD_TO_ODTH_F0:RW:8:6 */ + if ((timing_config->dram_type == LPDDR3) || + (timing_config->dram_type == LPDDR4)) { + /* min_rl_preamble= cl+TDQSCK_MIN-1 */ + tmp1 = pdram_timing->cl + + get_pi_todtoff_min(pdram_timing, timing_config) - 1; + /* todtoff_max */ + tmp2 = get_pi_todtoff_max(pdram_timing, timing_config); + if (tmp1 > tmp2) + tmp = tmp1 - tmp2; + else + tmp = 0; + } else if (timing_config->dram_type == DDR3) { + tmp = pdram_timing->cl - pdram_timing->cwl; + } + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[73], 0x3f << 8, + tmp << 8); + /* PI_89 PI_RDLAT_ADJ_F0:RW:16:8 */ + tmp = get_pi_rdlat_adj(pdram_timing); + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[89], 0xff << 16, + tmp << 16); + /* PI_90 PI_WRLAT_ADJ_F0:RW:16:8 */ + tmp = get_pi_wrlat_adj(pdram_timing, timing_config); + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[90], 0xff << 16, + tmp << 16); + /* PI_91 PI_TDFI_WRCSLAT_F0:RW:16:8 */ + tmp1 = tmp; + if (tmp1 < 5) { + if (tmp1 == 0) + tmp = 0; + else + tmp = tmp1 - 1; + } else { + tmp = tmp1 - 5; + } + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[91], 0xff << 16, + tmp << 16); + /* PI_95 PI_TDFI_CALVL_CAPTURE_F0:RW:16:10 */ + tmp1 = 20000 / (1000000 / pdram_timing->mhz) + 1; + if ((20000 % (1000000 / pdram_timing->mhz)) != 0) + tmp1++; + tmp = (tmp1 >> 1) + (tmp1 % 2) + 5; + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[95], 0x3ff << 16, + tmp << 16); + /* PI_95 PI_TDFI_CALVL_CC_F0:RW:0:10 */ + tmp = tmp + 18; + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[95], 0x3ff, tmp); + /* PI_102 PI_TMRZ_F0:RW:8:5 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[102], 0x1f << 8, + pdram_timing->tmrz << 8); + /* PI_111 PI_TDFI_CALVL_STROBE_F0:RW:8:4 */ + tmp1 = 2 * 1000 / (1000000 / pdram_timing->mhz); + if ((2 * 1000 % (1000000 / pdram_timing->mhz)) != 0) + tmp1++; + /* pi_tdfi_calvl_strobe=tds_train+5 */ + tmp = tmp1 + 5; + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[111], 0xf << 8, + tmp << 8); + /* PI_116 PI_TCKEHDQS_F0:RW:16:6 */ + tmp = 10000 / (1000000 / pdram_timing->mhz); + if ((10000 % (1000000 / pdram_timing->mhz)) != 0) + tmp++; + if (pdram_timing->mhz <= 100) + tmp = tmp + 1; + else + tmp = tmp + 8; + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[116], 0x3f << 16, + tmp << 16); + /* PI_125 PI_MR1_DATA_F0_0:RW+:8:16 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[125], 0xffff << 8, + pdram_timing->mr[1] << 8); + /* PI_133 PI_MR1_DATA_F0_1:RW+:0:16 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[133], 0xffff, + pdram_timing->mr[1]); + /* PI_140 PI_MR1_DATA_F0_2:RW+:16:16 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[140], 0xffff << 16, + pdram_timing->mr[1] << 16); + /* PI_148 PI_MR1_DATA_F0_3:RW+:0:16 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[148], 0xffff, + pdram_timing->mr[1]); + /* PI_126 PI_MR2_DATA_F0_0:RW+:0:16 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[126], 0xffff, + pdram_timing->mr[2]); + /* PI_133 PI_MR2_DATA_F0_1:RW+:16:16 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[133], 0xffff << 16, + pdram_timing->mr[2] << 16); + /* PI_141 PI_MR2_DATA_F0_2:RW+:0:16 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[141], 0xffff, + pdram_timing->mr[2]); + /* PI_148 PI_MR2_DATA_F0_3:RW+:16:16 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[148], 0xffff << 16, + pdram_timing->mr[2] << 16); + /* PI_156 PI_TFC_F0:RW:0:10 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[156], 0x3ff, + pdram_timing->trfc); + /* PI_158 PI_TWR_F0:RW:24:6 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[158], 0x3f << 24, + pdram_timing->twr << 24); + /* PI_158 PI_TWTR_F0:RW:16:6 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[158], 0x3f << 16, + pdram_timing->twtr << 16); + /* PI_158 PI_TRCD_F0:RW:8:8 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[158], 0xff << 8, + pdram_timing->trcd << 8); + /* PI_158 PI_TRP_F0:RW:0:8 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[158], 0xff, + pdram_timing->trp); + /* PI_157 PI_TRTP_F0:RW:24:8 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[157], 0xff << 24, + pdram_timing->trtp << 24); + /* PI_159 PI_TRAS_MIN_F0:RW:24:8 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[159], 0xff << 24, + pdram_timing->tras_min << 24); + /* PI_159 PI_TRAS_MAX_F0:RW:0:17 */ + tmp = pdram_timing->tras_max * 99 / 100; + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[159], 0x1ffff, tmp); + /* PI_160 PI_TMRD_F0:RW:16:6 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[160], 0x3f << 16, + pdram_timing->tmrd << 16); + /*PI_160 PI_TDQSCK_MAX_F0:RW:0:4 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[160], 0xf, + pdram_timing->tdqsck_max); + /* PI_187 PI_TDFI_CTRLUPD_MAX_F0:RW:8:16 */ + tmp = 2 * pdram_timing->trefi; + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[187], 0xffff << 8, + tmp << 8); + /* PI_188 PI_TDFI_CTRLUPD_INTERVAL_F0:RW:0:32 */ + tmp = 20 * pdram_timing->trefi; + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[188], 0xffffffff, + tmp); + } +} + +static void gen_rk3399_pi_params_f1(struct timing_related_config *timing_config, + struct dram_timing_t *pdram_timing) +{ + uint32_t tmp, tmp1, tmp2; + uint32_t i; + + for (i = 0; i < timing_config->ch_cnt; i++) { + /* PI_04 PI_TDFI_PHYMSTR_MAX_F1:RW:0:32 */ + tmp = 4 * pdram_timing->trefi; + write_32(&rk3399_ddr_pi[i]->denali_pi[4], tmp); + /* PI_05 PI_TDFI_PHYMSTR_RESP_F1:RW:0:16 */ + tmp = 2 * pdram_timing->trefi; + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[5], 0xffff, tmp); + /* PI_12 PI_TDFI_PHYUPD_RESP_F1:RW:0:16 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[12], 0xffff, tmp); + + /* PI_42 PI_TDELAY_RDWR_2_BUS_IDLE_F1:RW:8:8 */ + if (timing_config->dram_type == LPDDR4) + tmp = 2; + else + tmp = 0; + tmp = (pdram_timing->bl / 2) + 4 + + (get_pi_rdlat_adj(pdram_timing) - 2) + tmp + + get_pi_tdfi_phy_rdlat(pdram_timing, timing_config); + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[42], 0xff << 8, + tmp << 8); + /* PI_43 PI_WRLAT_F1:RW:24:5 */ + if (timing_config->dram_type == LPDDR3) { + tmp = get_pi_wrlat(pdram_timing, timing_config); + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[43], + 0x1f << 24, tmp << 24); + } + /* PI_44 PI_ADDITIVE_LAT_F1:RW:0:6 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[44], 0x3f, + PI_ADD_LATENCY); + /* PI_44 PI_CASLAT_LIN_F1:RW:8:7:=0x18 */ + tmp = pdram_timing->cl * 2; + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[44], 0x7f << 8, + tmp << 8); + /* PI_47 PI_TREF_F1:RW:16:16 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[47], 0xffff << 16, + pdram_timing->trefi << 16); + /* PI_47 PI_TRFC_F1:RW:0:10 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[47], 0x3ff, + pdram_timing->trfc); + /* PI_67 PI_TODTL_2CMD_F1:RW:8:8 */ + if (timing_config->dram_type == LPDDR3) { + tmp = get_pi_todtoff_max(pdram_timing, timing_config); + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[67], + 0xff << 8, tmp << 8); + } + /* PI_72 PI_WR_TO_ODTH_F1:RW:24:6 */ + if ((timing_config->dram_type == LPDDR3) + || (timing_config->dram_type == LPDDR4)) { + tmp1 = get_pi_wrlat(pdram_timing, timing_config); + tmp2 = get_pi_todtoff_max(pdram_timing, timing_config); + if (tmp1 > tmp2) + tmp = tmp1 - tmp2; + else + tmp = 0; + } else if (timing_config->dram_type == DDR3) { + tmp = 0; + } + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[72], 0x3f << 24, + tmp << 24); + /* PI_73 PI_RD_TO_ODTH_F1:RW:16:6 */ + if ((timing_config->dram_type == LPDDR3) + || (timing_config->dram_type == LPDDR4)) { + /* min_rl_preamble= cl+TDQSCK_MIN-1 */ + tmp1 = + pdram_timing->cl + get_pi_todtoff_min(pdram_timing, + timing_config) + - 1; + /* todtoff_max */ + tmp2 = get_pi_todtoff_max(pdram_timing, timing_config); + if (tmp1 > tmp2) + tmp = tmp1 - tmp2; + else + tmp = 0; + } else if (timing_config->dram_type == DDR3) { + tmp = pdram_timing->cl - pdram_timing->cwl; + } + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[73], 0x3f << 16, + tmp << 16); + /*P I_89 PI_RDLAT_ADJ_F1:RW:24:8 */ + tmp = get_pi_rdlat_adj(pdram_timing); + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[89], 0xff << 24, + tmp << 24); + /* PI_90 PI_WRLAT_ADJ_F1:RW:24:8 */ + tmp = get_pi_wrlat_adj(pdram_timing, timing_config); + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[90], 0xff << 24, + tmp << 24); + /* PI_91 PI_TDFI_WRCSLAT_F1:RW:24:8 */ + tmp1 = tmp; + if (tmp1 < 5) { + if (tmp1 == 0) + tmp = 0; + else + tmp = tmp1 - 1; + } else { + tmp = tmp1 - 5; + } + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[91], 0xff << 24, + tmp << 24); + /*PI_96 PI_TDFI_CALVL_CAPTURE_F1:RW:16:10 */ + /* tadr=20ns */ + tmp1 = 20000 / (1000000 / pdram_timing->mhz) + 1; + if ((20000 % (1000000 / pdram_timing->mhz)) != 0) + tmp1++; + tmp = (tmp1 >> 1) + (tmp1 % 2) + 5; + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[96], 0x3ff << 16, + tmp << 16); + /* PI_96 PI_TDFI_CALVL_CC_F1:RW:0:10 */ + tmp = tmp + 18; + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[96], 0x3ff, tmp); + /*PI_103 PI_TMRZ_F1:RW:0:5 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[103], 0x1f, + pdram_timing->tmrz); + /*PI_111 PI_TDFI_CALVL_STROBE_F1:RW:16:4 */ + /* tds_train=ceil(2/ns) */ + tmp1 = 2 * 1000 / (1000000 / pdram_timing->mhz); + if ((2 * 1000 % (1000000 / pdram_timing->mhz)) != 0) + tmp1++; + /* pi_tdfi_calvl_strobe=tds_train+5 */ + tmp = tmp1 + 5; + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[111], 0xf << 16, + tmp << 16); + /* PI_116 PI_TCKEHDQS_F1:RW:24:6 */ + tmp = 10000 / (1000000 / pdram_timing->mhz); + if ((10000 % (1000000 / pdram_timing->mhz)) != 0) + tmp++; + if (pdram_timing->mhz <= 100) + tmp = tmp + 1; + else + tmp = tmp + 8; + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[116], 0x3f << 24, + tmp << 24); + /* PI_128 PI_MR1_DATA_F1_0:RW+:0:16 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[128], 0xffff, + pdram_timing->mr[1]); + /* PI_135 PI_MR1_DATA_F1_1:RW+:8:16 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[135], 0xffff << 8, + pdram_timing->mr[1] << 8); + /* PI_143 PI_MR1_DATA_F1_2:RW+:0:16 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[143], 0xffff, + pdram_timing->mr[1]); + /* PI_150 PI_MR1_DATA_F1_3:RW+:8:16 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[150], 0xffff << 8, + pdram_timing->mr[1] << 8); + /* PI_128 PI_MR2_DATA_F1_0:RW+:16:16 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[128], 0xffff << 16, + pdram_timing->mr[2] << 16); + /* PI_136 PI_MR2_DATA_F1_1:RW+:0:16 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[136], 0xffff, + pdram_timing->mr[2]); + /* PI_143 PI_MR2_DATA_F1_2:RW+:16:16 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[143], 0xffff << 16, + pdram_timing->mr[2] << 16); + /* PI_151 PI_MR2_DATA_F1_3:RW+:0:16 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[151], 0xffff, + pdram_timing->mr[2]); + /* PI_156 PI_TFC_F1:RW:16:10 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[156], 0x3ff << 16, + pdram_timing->trfc << 16); + /* PI_162 PI_TWR_F1:RW:8:6 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[162], 0x3f << 8, + pdram_timing->twr << 8); + /* PI_162 PI_TWTR_F1:RW:0:6 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[162], 0x3f, + pdram_timing->twtr); + /* PI_161 PI_TRCD_F1:RW:24:8 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[161], 0xff << 24, + pdram_timing->trcd << 24); + /* PI_161 PI_TRP_F1:RW:16:8 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[161], 0xff << 16, + pdram_timing->trp << 16); + /* PI_161 PI_TRTP_F1:RW:8:8 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[161], 0xff << 8, + pdram_timing->trtp << 8); + /* PI_163 PI_TRAS_MIN_F1:RW:24:8 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[163], 0xff << 24, + pdram_timing->tras_min << 24); + /* PI_163 PI_TRAS_MAX_F1:RW:0:17 */ + tmp = pdram_timing->tras_max * 99 / 100; + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[163], 0x1ffff, tmp); + /* PI_164 PI_TMRD_F1:RW:16:6 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[164], 0x3f << 16, + pdram_timing->tmrd << 16); + /* PI_164 PI_TDQSCK_MAX_F1:RW:0:4 */ + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[164], 0xf, + pdram_timing->tdqsck_max); + /* PI_189 PI_TDFI_CTRLUPD_MAX_F1:RW:0:16 */ + tmp = 2 * pdram_timing->trefi; + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[189], 0xffff, tmp); + /* PI_190 PI_TDFI_CTRLUPD_INTERVAL_F1:RW:0:32 */ + tmp = 20 * pdram_timing->trefi; + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[190], 0xffffffff, + tmp); + } +} + +static void gen_rk3399_pi_params(struct timing_related_config *timing_config, + struct dram_timing_t *pdram_timing, + uint32_t fn) +{ + if (fn == 0) + gen_rk3399_pi_params_f0(timing_config, pdram_timing); + else + gen_rk3399_pi_params_f1(timing_config, pdram_timing); + +#if PI_TRAINING + uint32_t i; + + for (i = 0; i < timing_config->ch_cnt; i++) { +#if EN_READ_GATE_TRAINING + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[80], 3 << 24, + 2 << 24); +#endif + +#if EN_CA_TRAINING + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[100], 3 << 8, + 2 << 8); +#endif + +#if EN_WRITE_LEVELING + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[60], 3 << 8, + 2 << 8); +#endif + +#if EN_READ_LEVELING + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[80], 3 << 16, + 2 << 16); +#endif + +#if EN_WDQ_LEVELING + clrsetbits_32(&rk3399_ddr_pi[i]->denali_pi[124], 3 << 16, + 2 << 16); +#endif + } +#endif +} + +static void gen_rk3399_set_odt(uint32_t odt_en) +{ + uint32_t drv_odt_val; + uint32_t i; + + for (i = 0; i < rk3399_dram_status.timing_config.ch_cnt; i++) { + drv_odt_val = (odt_en | (0 << 1) | (0 << 2)) << 16; + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[5], + 0x7 << 16, drv_odt_val); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[133], + 0x7 << 16, drv_odt_val); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[261], + 0x7 << 16, drv_odt_val); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[389], + 0x7 << 16, drv_odt_val); + drv_odt_val = (odt_en | (0 << 1) | (0 << 2)) << 24; + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[6], + 0x7 << 24, drv_odt_val); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[134], + 0x7 << 24, drv_odt_val); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[262], + 0x7 << 24, drv_odt_val); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[390], + 0x7 << 24, drv_odt_val); + } +} + +static void gen_rk3399_set_ds_odt(struct timing_related_config *timing_config, + struct drv_odt_lp_config *drv_config) +{ + uint32_t i, drv_odt_val; + + for (i = 0; i < timing_config->ch_cnt; i++) { + if (timing_config->dram_type == LPDDR4) + drv_odt_val = drv_config->phy_side_odt | + (PHY_DRV_ODT_Hi_Z << 4) | + (drv_config->phy_side_dq_drv << 8) | + (drv_config->phy_side_dq_drv << 12); + else if (timing_config->dram_type == LPDDR3) + drv_odt_val = PHY_DRV_ODT_Hi_Z | + (drv_config->phy_side_odt << 4) | + (drv_config->phy_side_dq_drv << 8) | + (drv_config->phy_side_dq_drv << 12); + else + drv_odt_val = drv_config->phy_side_odt | + (drv_config->phy_side_odt << 4) | + (drv_config->phy_side_dq_drv << 8) | + (drv_config->phy_side_dq_drv << 12); + + /* DQ drv odt set */ + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[6], 0xffffff, + drv_odt_val); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[134], 0xffffff, + drv_odt_val); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[262], 0xffffff, + drv_odt_val); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[390], 0xffffff, + drv_odt_val); + /* DQS drv odt set */ + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[7], 0xffffff, + drv_odt_val); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[135], 0xffffff, + drv_odt_val); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[263], 0xffffff, + drv_odt_val); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[391], 0xffffff, + drv_odt_val); + + gen_rk3399_set_odt(timing_config->odt); + + /* CA drv set */ + drv_odt_val = drv_config->phy_side_ca_drv | + (drv_config->phy_side_ca_drv << 4); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[544], 0xff, + drv_odt_val); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[672], 0xff, + drv_odt_val); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[800], 0xff, + drv_odt_val); + + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[928], 0xff, + drv_odt_val); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[937], 0xff, + drv_odt_val); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[935], 0xff, + drv_odt_val); + + drv_odt_val = drv_config->phy_side_ck_cs_drv | + (drv_config->phy_side_ck_cs_drv << 4); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[929], 0xff, + drv_odt_val); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[939], 0xff, + drv_odt_val); + } +} + +static void gen_rk3399_phy_params(struct timing_related_config *timing_config, + struct drv_odt_lp_config *drv_config, + struct dram_timing_t *pdram_timing, + uint32_t fn) +{ + uint32_t tmp, i, div, j; + uint32_t mem_delay_ps, pad_delay_ps, total_delay_ps, delay_frac_ps; + uint32_t trpre_min_ps, gate_delay_ps, gate_delay_frac_ps; + uint32_t ie_enable, tsel_enable, cas_lat, rddata_en_ie_dly, tsel_adder; + uint32_t extra_adder, delta, hs_offset; + + for (i = 0; i < timing_config->ch_cnt; i++) { + + pad_delay_ps = PI_PAD_DELAY_PS_VALUE; + ie_enable = PI_IE_ENABLE_VALUE; + tsel_enable = PI_TSEL_ENABLE_VALUE; + + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[896], + (0x3 << 8) | 1, fn << 8); + + /* PHY_LOW_FREQ_SEL */ + /* DENALI_PHY_913 1bit offset_0 */ + if (timing_config->freq > 400) + clrbits_32(&rk3399_ddr_publ[i]->denali_phy[913], 1); + else + setbits_32(&rk3399_ddr_publ[i]->denali_phy[913], 1); + + /* PHY_RPTR_UPDATE_x */ + /* DENALI_PHY_87/215/343/471 4bit offset_16 */ + tmp = 2500 / (1000000 / pdram_timing->mhz) + 3; + if ((2500 % (1000000 / pdram_timing->mhz)) != 0) + tmp++; + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[87], 0xf << 16, + tmp << 16); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[215], 0xf << 16, + tmp << 16); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[343], 0xf << 16, + tmp << 16); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[471], 0xf << 16, + tmp << 16); + + /* PHY_PLL_CTRL */ + /* DENALI_PHY_911 13bits offset_0 */ + /* PHY_LP4_BOOT_PLL_CTRL */ + /* DENALI_PHY_919 13bits offset_0 */ + if (pdram_timing->mhz <= 150) + tmp = 3; + else if (pdram_timing->mhz <= 300) + tmp = 2; + else if (pdram_timing->mhz <= 600) + tmp = 1; + else + tmp = 0; + tmp = (1 << 12) | (tmp << 9) | (2 << 7) | (1 << 1); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[911], 0x1fff, + tmp); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[919], 0x1fff, + tmp); + + /* PHY_PLL_CTRL_CA */ + /* DENALI_PHY_911 13bits offset_16 */ + /* PHY_LP4_BOOT_PLL_CTRL_CA */ + /* DENALI_PHY_919 13bits offset_16 */ + if (pdram_timing->mhz <= 150) + tmp = 3; + else if (pdram_timing->mhz <= 300) + tmp = 2; + else if (pdram_timing->mhz <= 600) + tmp = 1; + else + tmp = 0; + tmp = (tmp << 9) | (2 << 7) | (1 << 5) | (1 << 1); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[911], + 0x1fff << 16, tmp << 16); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[919], + 0x1fff << 16, tmp << 16); + + /* PHY_TCKSRE_WAIT */ + /* DENALI_PHY_922 4bits offset_24 */ + if (pdram_timing->mhz <= 400) + tmp = 1; + else if (pdram_timing->mhz <= 800) + tmp = 3; + else if (pdram_timing->mhz <= 1000) + tmp = 4; + else + tmp = 5; + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[922], 0xf << 24, + tmp << 24); + /* PHY_CAL_CLK_SELECT_0:RW8:3 */ + div = pdram_timing->mhz / (2 * 20); + for (j = 2, tmp = 1; j <= 128; j <<= 1, tmp++) { + if (div < j) + break; + } + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[947], 0x7 << 8, + tmp << 8); + setbits_32(&rk3399_ddr_publ[i]->denali_phy[927], (1 << 22)); + + if (timing_config->dram_type == DDR3) { + mem_delay_ps = 0; + trpre_min_ps = 1000; + } else if (timing_config->dram_type == LPDDR4) { + mem_delay_ps = 1500; + trpre_min_ps = 900; + } else if (timing_config->dram_type == LPDDR3) { + mem_delay_ps = 2500; + trpre_min_ps = 900; + } else { + ERROR("gen_rk3399_phy_params:dramtype unsupport\n"); + return; + } + total_delay_ps = mem_delay_ps + pad_delay_ps; + delay_frac_ps = + 1000 * total_delay_ps / (1000000 / pdram_timing->mhz); + gate_delay_ps = delay_frac_ps + 1000 - (trpre_min_ps / 2); + gate_delay_frac_ps = + gate_delay_ps - gate_delay_ps / 1000 * 1000; + tmp = gate_delay_frac_ps * 0x200 / 1000; + /* PHY_RDDQS_GATE_BYPASS_SLAVE_DELAY */ + /* DENALI_PHY_2/130/258/386 10bits offset_0 */ + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[2], 0x2ff, tmp); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[130], 0x2ff, tmp); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[258], 0x2ff, tmp); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[386], 0x2ff, tmp); + /* PHY_RDDQS_GATE_SLAVE_DELAY */ + /* DENALI_PHY_77/205/333/461 10bits offset_16 */ + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[77], 0x2ff << 16, + tmp << 16); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[205], 0x2ff << 16, + tmp << 16); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[333], 0x2ff << 16, + tmp << 16); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[461], 0x2ff << 16, + tmp << 16); + + tmp = gate_delay_ps / 1000; + /* PHY_LP4_BOOT_RDDQS_LATENCY_ADJUST */ + /* DENALI_PHY_10/138/266/394 4bit offset_0 */ + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[10], 0xf, tmp); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[138], 0xf, tmp); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[266], 0xf, tmp); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[394], 0xf, tmp); + /* PHY_RDDQS_LATENCY_ADJUST */ + /* DENALI_PHY_78/206/334/462 4bits offset_0 */ + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[78], 0xf, tmp); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[206], 0xf, tmp); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[334], 0xf, tmp); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[462], 0xf, tmp); + /* PHY_GTLVL_LAT_ADJ_START */ + /* DENALI_PHY_80/208/336/464 4bits offset_16 */ + tmp = delay_frac_ps / 1000; + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[80], 0xf << 16, + tmp << 16); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[208], 0xf << 16, + tmp << 16); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[336], 0xf << 16, + tmp << 16); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[464], 0xf << 16, + tmp << 16); + + cas_lat = pdram_timing->cl + PI_ADD_LATENCY; + rddata_en_ie_dly = ie_enable / (1000000 / pdram_timing->mhz); + if ((ie_enable % (1000000 / pdram_timing->mhz)) != 0) + rddata_en_ie_dly++; + rddata_en_ie_dly = rddata_en_ie_dly - 1; + tsel_adder = tsel_enable / (1000000 / pdram_timing->mhz); + if ((tsel_enable % (1000000 / pdram_timing->mhz)) != 0) + tsel_adder++; + if (rddata_en_ie_dly > tsel_adder) + extra_adder = rddata_en_ie_dly - tsel_adder; + else + extra_adder = 0; + delta = cas_lat - rddata_en_ie_dly; + if (PI_REGS_DIMM_SUPPORT && PI_DOUBLEFREEK) + hs_offset = 2; + else + hs_offset = 1; + if (rddata_en_ie_dly > (cas_lat - 1 - hs_offset)) { + tmp = 0; + } else { + if ((delta == 2) || (delta == 1)) + tmp = rddata_en_ie_dly - 0 - extra_adder; + else + tmp = extra_adder; + } + /* PHY_LP4_BOOT_RDDATA_EN_TSEL_DLY */ + /* DENALI_PHY_9/137/265/393 4bit offset_16 */ + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[9], 0xf << 16, + tmp << 16); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[137], 0xf << 16, + tmp << 16); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[265], 0xf << 16, + tmp << 16); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[393], 0xf << 16, + tmp << 16); + /* PHY_RDDATA_EN_TSEL_DLY */ + /* DENALI_PHY_86/214/342/470 4bit offset_0 */ + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[86], 0xf, tmp); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[214], 0xf, tmp); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[342], 0xf, tmp); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[470], 0xf, tmp); + + if (tsel_adder > rddata_en_ie_dly) + extra_adder = tsel_adder - rddata_en_ie_dly; + else + extra_adder = 0; + if (rddata_en_ie_dly > (cas_lat - 1 - hs_offset)) + tmp = tsel_adder; + else + tmp = rddata_en_ie_dly - 0 + extra_adder; + /* PHY_LP4_BOOT_RDDATA_EN_DLY */ + /* DENALI_PHY_9/137/265/393 4bit offset_8 */ + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[9], 0xf << 8, + tmp << 8); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[137], 0xf << 8, + tmp << 8); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[265], 0xf << 8, + tmp << 8); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[393], 0xf << 8, + tmp << 8); + /* PHY_RDDATA_EN_DLY */ + /* DENALI_PHY_85/213/341/469 4bit offset_24 */ + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[85], 0xf << 24, + tmp << 24); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[213], 0xf << 24, + tmp << 24); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[341], 0xf << 24, + tmp << 24); + clrsetbits_32(&rk3399_ddr_publ[i]->denali_phy[469], 0xf << 24, + tmp << 24); + + if (pdram_timing->mhz <= ENPER_CS_TRAINING_FREQ) { + + /* + * Note:Per-CS Training is not compatible at speeds + * under 533 MHz. If the PHY is running at a speed + * less than 533MHz, all phy_per_cs_training_en_X + * parameters must be cleared to 0. + */ + + /*DENALI_PHY_84/212/340/468 1bit offset_16 */ + clrbits_32(&rk3399_ddr_publ[i]->denali_phy[84], + 0x1 << 16); + clrbits_32(&rk3399_ddr_publ[i]->denali_phy[212], + 0x1 << 16); + clrbits_32(&rk3399_ddr_publ[i]->denali_phy[340], + 0x1 << 16); + clrbits_32(&rk3399_ddr_publ[i]->denali_phy[468], + 0x1 << 16); + } else { + setbits_32(&rk3399_ddr_publ[i]->denali_phy[84], + 0x1 << 16); + setbits_32(&rk3399_ddr_publ[i]->denali_phy[212], + 0x1 << 16); + setbits_32(&rk3399_ddr_publ[i]->denali_phy[340], + 0x1 << 16); + setbits_32(&rk3399_ddr_publ[i]->denali_phy[468], + 0x1 << 16); + } + } +} + +static int to_get_clk_index(unsigned int mhz) +{ + int pll_cnt, i; + + pll_cnt = sizeof(dpll_rates_table) / sizeof(struct pll_div); + + /* Assumming rate_table is in descending order */ + for (i = 0; i < pll_cnt; i++) { + if (mhz >= dpll_rates_table[i].mhz) + break; + } + + return i; +} + +uint32_t rkclk_prepare_pll_timing(unsigned int mhz) +{ + unsigned int refdiv, postdiv1, fbdiv, postdiv2; + int index; + + index = to_get_clk_index(mhz); + refdiv = dpll_rates_table[index].refdiv; + fbdiv = dpll_rates_table[index].fbdiv; + postdiv1 = dpll_rates_table[index].postdiv1; + postdiv2 = dpll_rates_table[index].postdiv2; + write_32(DCF_PARAM_ADDR + PARAM_DPLL_CON0, FBDIV(fbdiv)); + write_32(DCF_PARAM_ADDR + PARAM_DPLL_CON1, POSTDIV2(postdiv2) | + POSTDIV1(postdiv1) | REFDIV(refdiv)); + return (24 * fbdiv) / refdiv / postdiv1 / postdiv2; +} + +uint64_t ddr_get_rate(void) +{ + uint32_t refdiv, postdiv1, fbdiv, postdiv2; + + refdiv = mmio_read_32(CRU_BASE + CRU_PLL_CON(DPLL_ID, 1)) & 0x3f; + fbdiv = mmio_read_32(CRU_BASE + CRU_PLL_CON(DPLL_ID, 0)) & 0xfff; + postdiv1 = + (mmio_read_32(CRU_BASE + CRU_PLL_CON(DPLL_ID, 1)) >> 8) & 0x7; + postdiv2 = + (mmio_read_32(CRU_BASE + CRU_PLL_CON(DPLL_ID, 1)) >> 12) & 0x7; + + return (24 / refdiv * fbdiv / postdiv1 / postdiv2) * 1000 * 1000; +} + +/* + * return: bit12: channel 1, external self-refresh + * bit11: channel 1, stdby_mode + * bit10: channel 1, self-refresh with controller and memory clock gate + * bit9: channel 1, self-refresh + * bit8: channel 1, power-down + * + * bit4: channel 1, external self-refresh + * bit3: channel 0, stdby_mode + * bit2: channel 0, self-refresh with controller and memory clock gate + * bit1: channel 0, self-refresh + * bit0: channel 0, power-down + */ +uint32_t exit_low_power(void) +{ + struct rk3399_ddr_pctl_regs *ddr_pctl_regs; + uint32_t low_power = 0; + uint32_t channel_mask; + uint32_t channel; + uint32_t tmp; + + channel_mask = (read_32(PMUGRF_BASE + PMUGRF_OSREG(2)) >> 28) & 0x3; + for (channel = 0; channel < 2; channel++) { + ddr_pctl_regs = rk3399_ddr_pctl[channel]; + if (!(channel_mask & (1 << channel))) + continue; + + /* exit stdby mode */ + write_32(&rk3399_ddr_cic->cic_ctrl1, + (1 << (channel + 16)) | (0 << channel)); + /* exit external self-refresh */ + tmp = channel ? 12 : 8; + low_power |= ((read_32(PMU_BASE + PMU_SFT_CON) >> tmp) & 0x1) + << (4 + 8 * channel); + clrbits_32(PMU_BASE + PMU_SFT_CON, 1 << tmp); + while (!(read_32(PMU_BASE + PMU_DDR_SREF_ST) & + (1 << channel))) + ; + /* exit auto low-power */ + clrbits_32(&ddr_pctl_regs->denali_ctl[101], 0x7); + /* lp_cmd to exit */ + if (((read_32(&ddr_pctl_regs->denali_ctl[100]) >> 24) & + 0x7f) != 0x40) { + while (read_32(&ddr_pctl_regs->denali_ctl[200]) & 0x1) + ; + clrsetbits_32(&ddr_pctl_regs->denali_ctl[93], + 0xff << 24, 0x69 << 24); + while (((read_32(&ddr_pctl_regs->denali_ctl[100]) >> + 24) & 0x7f) != 0x40) + ; + } + } + return low_power; +} + +void resume_low_power(uint32_t low_power) +{ + struct rk3399_ddr_pctl_regs *ddr_pctl_regs; + uint32_t channel_mask; + uint32_t channel; + uint32_t tmp; + uint32_t val; + + channel_mask = (read_32(PMUGRF_BASE + PMUGRF_OSREG(2)) >> 28) & 0x3; + for (channel = 0; channel < 2; channel++) { + ddr_pctl_regs = rk3399_ddr_pctl[channel]; + if (!(channel_mask & (1 << channel))) + continue; + + /* resume external self-refresh */ + tmp = channel ? 12 : 8; + val = (low_power >> (4 + 8 * channel)) & 0x1; + setbits_32(PMU_BASE + PMU_SFT_CON, val << tmp); + /* resume auto low-power */ + val = (low_power >> (8 * channel)) & 0x7; + setbits_32(&ddr_pctl_regs->denali_ctl[101], val); + /* resume stdby mode */ + val = (low_power >> (3 + 8 * channel)) & 0x1; + write_32(&rk3399_ddr_cic->cic_ctrl1, + (1 << (channel + 16)) | (val << channel)); + } +} + +static void wait_dcf_done(void) +{ + while ((read_32(DCF_BASE + DCF_DCF_ISR) & (DCF_DONE)) == 0) + continue; +} + +void clr_dcf_irq(void) +{ + /* clear dcf irq status */ + mmio_write_32(DCF_BASE + DCF_DCF_ISR, DCF_TIMEOUT | DCF_ERR | DCF_DONE); +} + +static void enable_dcf(uint32_t dcf_addr) +{ + /* config DCF start addr */ + write_32(DCF_BASE + DCF_DCF_ADDR, dcf_addr); + /* wait dcf done */ + while (read_32(DCF_BASE + DCF_DCF_CTRL) & 1) + continue; + /* clear dcf irq status */ + write_32(DCF_BASE + DCF_DCF_ISR, DCF_TIMEOUT | DCF_ERR | DCF_DONE); + /* DCF start */ + setbits_32(DCF_BASE + DCF_DCF_CTRL, DCF_START); +} + +void dcf_code_init(void) +{ + memcpy((void *)DCF_START_ADDR, (void *)dcf_code, sizeof(dcf_code)); + /* set dcf master secure */ + write_32(SGRF_BASE + 0xe01c, ((0x3 << 0) << 16) | (0 << 0)); + write_32(DCF_BASE + DCF_DCF_TOSET, 0x80000000); +} + +static void dcf_start(uint32_t freq, uint32_t index) +{ + write_32(CRU_BASE + CRU_SOFTRST_CON(10), (0x1 << (1 + 16)) | (1 << 1)); + write_32(CRU_BASE + CRU_SOFTRST_CON(11), (0x1 << (0 + 16)) | (1 << 0)); + write_32(DCF_PARAM_ADDR + PARAM_FREQ_SELECT, index << 4); + + write_32(DCF_PARAM_ADDR + PARAM_DRAM_FREQ, freq); + + rkclk_prepare_pll_timing(freq); + udelay(10); + write_32(CRU_BASE + CRU_SOFTRST_CON(10), (0x1 << (1 + 16)) | (0 << 1)); + write_32(CRU_BASE + CRU_SOFTRST_CON(11), (0x1 << (0 + 16)) | (0 << 0)); + udelay(10); + enable_dcf(DCF_START_ADDR); +} + +static void dram_low_power_config(struct drv_odt_lp_config *lp_config) +{ + uint32_t tmp, tmp1, i; + uint32_t ch_cnt = rk3399_dram_status.timing_config.ch_cnt; + uint32_t dram_type = rk3399_dram_status.timing_config.dram_type; + uint32_t *low_power = &rk3399_dram_status.low_power_stat; + + if (dram_type == LPDDR4) + tmp = (lp_config->srpd_lite_idle << 16) | + lp_config->pd_idle; + else + tmp = lp_config->pd_idle; + + if (dram_type == DDR3) + tmp1 = (2 << 16) | (0x7 << 8) | 7; + else + tmp1 = (3 << 16) | (0x7 << 8) | 7; + + *low_power = 0; + + for (i = 0; i < ch_cnt; i++) { + write_32(&rk3399_ddr_pctl[i]->denali_ctl[102], tmp); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[103], 0xffff, + (lp_config->sr_mc_gate_idle << 8) | + lp_config->sr_idle); + clrsetbits_32(&rk3399_ddr_pctl[i]->denali_ctl[101], + 0x70f0f, tmp1); + *low_power |= (7 << (8 * i)); + } + + /* standby idle */ + write_32(&rk3399_ddr_cic->cic_idle_th, lp_config->standby_idle); + write_32(&rk3399_ddr_cic->cic_cg_wait_th, 0x640008); + + if (ch_cnt == 2) { + write_32(GRF_BASE + GRF_DDRC1_CON1, + (((0x1<<4) | (0x1<<5) | (0x1<<6) | (0x1<<7)) << 16) | + ((0x1<<4) | (0x0<<5) | (0x1<<6) | (0x1<<7))); + if (lp_config->standby_idle) { + tmp = 0x002a002a; + *low_power |= (1 << 11); + } else { + tmp = 0; + } + write_32(&rk3399_ddr_cic->cic_ctrl1, tmp); + } + + write_32(GRF_BASE + GRF_DDRC0_CON1, + (((0x1<<4) | (0x1<<5) | (0x1<<6) | (0x1<<7)) << 16) | + ((0x1<<4) | (0x0<<5) | (0x1<<6) | (0x1<<7))); + if (lp_config->standby_idle) { + tmp = 0x00150015; + *low_power |= (1 << 3); + } else { + tmp = 0; + } + write_32(&rk3399_ddr_cic->cic_ctrl1, tmp); +} + + +static void dram_related_init(struct ddr_dts_config_timing *dts_timing) +{ + uint32_t trefi0, trefi1; + uint32_t i; + struct rk3399_sdram_config sdram_config; + + dcf_code_init(); + + /* get sdram config for os reg */ + sdram_config_init(&sdram_config); + drv_odt_lp_cfg_init(sdram_config.dramtype, dts_timing, + &rk3399_dram_status.drv_odt_lp_cfg); + sdram_timing_cfg_init(&rk3399_dram_status.timing_config, + &sdram_config, + &rk3399_dram_status.drv_odt_lp_cfg); + + trefi0 = ((read_32(&rk3399_ddr_pctl[0]->denali_ctl[48]) >> + 16) & 0xffff) + 8; + trefi1 = ((read_32(&rk3399_ddr_pctl[0]->denali_ctl[49]) >> + 16) & 0xffff) + 8; + + rk3399_dram_status.index_freq[0] = trefi0 * 10 / 39; + rk3399_dram_status.index_freq[1] = trefi1 * 10 / 39; + rk3399_dram_status.current_index = + (read_32(&rk3399_ddr_pctl[0]->denali_ctl[111]) + >> 16) & 0x3; + if (rk3399_dram_status.timing_config.dram_type == DDR3) { + rk3399_dram_status.index_freq[0] /= 2; + rk3399_dram_status.index_freq[1] /= 2; + } + rk3399_dram_status.index_freq[(rk3399_dram_status.current_index + 1) + & 0x1] = 0; + + /* disable all training by ctl and pi */ + for (i = 0; i < rk3399_dram_status.timing_config.ch_cnt; i++) { + clrbits_32(&rk3399_ddr_pctl[i]->denali_ctl[70], (1 << 24) | + (1 << 16) | (1 << 8) | 1); + clrbits_32(&rk3399_ddr_pctl[i]->denali_ctl[71], 1); + + clrbits_32(&rk3399_ddr_pi[i]->denali_pi[60], 0x3 << 8); + clrbits_32(&rk3399_ddr_pi[i]->denali_pi[80], (0x3 << 24) | + (0x3 << 16)); + clrbits_32(&rk3399_ddr_pi[i]->denali_pi[100], 0x3 << 8); + clrbits_32(&rk3399_ddr_pi[i]->denali_pi[124], 0x3 << 16); + } + + /* init drv odt */ + if (rk3399_dram_status.index_freq[rk3399_dram_status.current_index] < + rk3399_dram_status.drv_odt_lp_cfg.odt_dis_freq) + rk3399_dram_status.timing_config.odt = 0; + else + rk3399_dram_status.timing_config.odt = 1; + gen_rk3399_set_ds_odt(&rk3399_dram_status.timing_config, + &rk3399_dram_status.drv_odt_lp_cfg); + dram_low_power_config(&rk3399_dram_status.drv_odt_lp_cfg); +} + +static uint32_t prepare_ddr_timing(uint32_t mhz) +{ + uint32_t index; + struct dram_timing_t dram_timing; + + rk3399_dram_status.timing_config.freq = mhz; + + if (mhz < rk3399_dram_status.drv_odt_lp_cfg.ddr3_dll_dis_freq) + rk3399_dram_status.timing_config.dllbp = 1; + else + rk3399_dram_status.timing_config.dllbp = 0; + if (mhz < rk3399_dram_status.drv_odt_lp_cfg.odt_dis_freq) { + rk3399_dram_status.timing_config.odt = 0; + } else { + rk3399_dram_status.timing_config.odt = 1; + gen_rk3399_set_odt(1); + } + + index = (rk3399_dram_status.current_index + 1) & 0x1; + if (rk3399_dram_status.index_freq[index] == mhz) + goto out; + + /* + * checking if having available gate traiing timing for + * target freq. + */ + dram_get_parameter(&rk3399_dram_status.timing_config, &dram_timing); + + gen_rk3399_ctl_params(&rk3399_dram_status.timing_config, + &dram_timing, index); + gen_rk3399_pi_params(&rk3399_dram_status.timing_config, + &dram_timing, index); + gen_rk3399_phy_params(&rk3399_dram_status.timing_config, + &rk3399_dram_status.drv_odt_lp_cfg, + &dram_timing, index); + rk3399_dram_status.index_freq[index] = mhz; + + +out: + return index; +} + +void print_dram_status_info(void) +{ + uint32_t *p; + uint32_t i; + + p = (uint32_t *) &rk3399_dram_status.timing_config; + INFO("rk3399_dram_status.timing_config:\n"); + for (i = 0; i < sizeof(struct timing_related_config) / 4; i++) + tf_printf("%u\n", p[i]); + p = (uint32_t *) &rk3399_dram_status.drv_odt_lp_cfg; + INFO("rk3399_dram_status.drv_odt_lp_cfg:\n"); + for (i = 0; i < sizeof(struct drv_odt_lp_config) / 4; i++) + tf_printf("%u\n", p[i]); +} + +uint64_t ddr_set_rate(uint64_t hz) +{ + uint32_t low_power, index; + uint32_t mhz = hz / (1000 * 1000); + + if (mhz == + rk3399_dram_status.index_freq[rk3399_dram_status.current_index]) + goto out; + + low_power = exit_low_power(); + index = prepare_ddr_timing(mhz); + if (index > 1) { + /* set timing error, quit */ + mhz = 0; + goto out; + } + + dcf_start(mhz, index); + wait_dcf_done(); + if (rk3399_dram_status.timing_config.odt == 0) + gen_rk3399_set_odt(0); + + rk3399_dram_status.current_index = index; + + if (mhz < dts_parameter.auto_pd_dis_freq) + low_power |= rk3399_dram_status.low_power_stat; + + resume_low_power(low_power); +out: + return mhz; +} + +uint64_t ddr_round_rate(uint64_t hz) +{ + int index; + uint32_t mhz = hz / (1000 * 1000); + + index = to_get_clk_index(mhz); + + return dpll_rates_table[index].mhz * 1000 * 1000; +} + +uint64_t dts_timing_receive(uint64_t timing, uint64_t index) +{ + uint32_t *p = (uint32_t *) &dts_parameter; + static uint32_t receive_nums; + + if (index < (sizeof(dts_parameter) / sizeof(uint32_t) - 1)) { + p[index] = (uint32_t)timing; + receive_nums++; + } else { + dts_parameter.available = 0; + return -1; + } + + /* receive all parameter */ + if (receive_nums == (sizeof(dts_parameter) / sizeof(uint32_t) - 1)) { + dts_parameter.available = 1; + receive_nums = 0; + } + + return index; +} + +void ddr_init(void) +{ + dram_related_init(&dts_parameter); +} diff --git a/plat/rockchip/rk3399/drivers/dram/dram.h b/plat/rockchip/rk3399/drivers/dram/dram.h new file mode 100644 index 00000000..62c5170a --- /dev/null +++ b/plat/rockchip/rk3399/drivers/dram/dram.h @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __SOC_ROCKCHIP_RK3399_SDRAM_H__ +#define __SOC_ROCKCHIP_RK3399_SDRAM_H__ + +struct rk3399_ddr_cic_regs { + uint32_t cic_ctrl0; + uint32_t cic_ctrl1; + uint32_t cic_idle_th; + uint32_t cic_cg_wait_th; + uint32_t cic_status0; + uint32_t cic_status1; + uint32_t cic_ctrl2; + uint32_t cic_ctrl3; + uint32_t cic_ctrl4; +}; + +/* DENALI_CTL_00 */ +#define START (1) + +/* DENALI_CTL_68 */ +#define PWRUP_SREFRESH_EXIT (1 << 16) + +/* DENALI_CTL_274 */ +#define MEM_RST_VALID (1) + +struct rk3399_ddr_pctl_regs { + uint32_t denali_ctl[332]; +}; + +struct rk3399_ddr_publ_regs { + uint32_t denali_phy[959]; +}; + +#define PHY_DRV_ODT_Hi_Z (0x0) +#define PHY_DRV_ODT_240 (0x1) +#define PHY_DRV_ODT_120 (0x8) +#define PHY_DRV_ODT_80 (0x9) +#define PHY_DRV_ODT_60 (0xc) +#define PHY_DRV_ODT_48 (0xd) +#define PHY_DRV_ODT_40 (0xe) +#define PHY_DRV_ODT_34_3 (0xf) + +struct rk3399_ddr_pi_regs { + uint32_t denali_pi[200]; +}; +union noc_ddrtiminga0 { + uint32_t d32; + struct { + unsigned acttoact : 6; + unsigned reserved0 : 2; + unsigned rdtomiss : 6; + unsigned reserved1 : 2; + unsigned wrtomiss : 6; + unsigned reserved2 : 2; + unsigned readlatency : 8; + } b; +}; + +union noc_ddrtimingb0 { + uint32_t d32; + struct { + unsigned rdtowr : 5; + unsigned reserved0 : 3; + unsigned wrtord : 5; + unsigned reserved1 : 3; + unsigned rrd : 4; + unsigned reserved2 : 4; + unsigned faw : 6; + unsigned reserved3 : 2; + } b; +}; + +union noc_ddrtimingc0 { + uint32_t d32; + struct { + unsigned burstpenalty : 4; + unsigned reserved0 : 4; + unsigned wrtomwr : 6; + unsigned reserved1 : 18; + } b; +}; + +union noc_devtodev0 { + uint32_t d32; + struct { + unsigned busrdtord : 3; + unsigned reserved0 : 1; + unsigned busrdtowr : 3; + unsigned reserved1 : 1; + unsigned buswrtord : 3; + unsigned reserved2 : 1; + unsigned buswrtowr : 3; + unsigned reserved3 : 17; + } b; +}; + +union noc_ddrmode { + uint32_t d32; + struct { + unsigned autoprecharge : 1; + unsigned bypassfiltering : 1; + unsigned fawbank : 1; + unsigned burstsize : 2; + unsigned mwrsize : 2; + unsigned reserved2 : 1; + unsigned forceorder : 8; + unsigned forceorderstate : 8; + unsigned reserved3 : 8; + } b; +}; + +struct rk3399_msch_regs { + uint32_t coreid; + uint32_t revisionid; + uint32_t ddrconf; + uint32_t ddrsize; + union noc_ddrtiminga0 ddrtiminga0; + union noc_ddrtimingb0 ddrtimingb0; + union noc_ddrtimingc0 ddrtimingc0; + union noc_devtodev0 devtodev0; + uint32_t reserved0[(0x110-0x20)/4]; + union noc_ddrmode ddrmode; + uint32_t reserved1[(0x1000-0x114)/4]; + uint32_t agingx0; +}; + +struct rk3399_msch_timings { + union noc_ddrtiminga0 ddrtiminga0; + union noc_ddrtimingb0 ddrtimingb0; + union noc_ddrtimingc0 ddrtimingc0; + union noc_devtodev0 devtodev0; + union noc_ddrmode ddrmode; + uint32_t agingx0; +}; +#if 1 +struct rk3399_sdram_channel { + unsigned char rank; + /* col = 0, means this channel is invalid */ + unsigned char col; + /* 3:8bank, 2:4bank */ + unsigned char bk; + /* channel buswidth, 2:32bit, 1:16bit, 0:8bit */ + unsigned char bw; + /* die buswidth, 2:32bit, 1:16bit, 0:8bit */ + unsigned char dbw; + /* row_3_4 = 1: 6Gb or 12Gb die + * row_3_4 = 0: normal die, power of 2 + */ + unsigned char row_3_4; + unsigned char cs0_row; + unsigned char cs1_row; + uint32_t ddrconfig; + struct rk3399_msch_timings noc_timings; +}; + +struct rk3399_sdram_params { + struct rk3399_sdram_channel ch[2]; + uint32_t ddr_freq; + unsigned char dramtype; + unsigned char num_channels; + unsigned char stride; + unsigned char odt; + struct rk3399_ddr_pctl_regs pctl_regs; + struct rk3399_ddr_pi_regs pi_regs; + struct rk3399_ddr_publ_regs phy_regs; +}; +#endif +struct rk3399_sdram_channel_config { + uint32_t bus_width; + uint32_t cs_cnt; + uint32_t cs0_row; + uint32_t cs1_row; + uint32_t bank; + uint32_t col; + uint32_t each_die_bus_width; + uint32_t each_die_6gb_or_12gb; +}; + +struct rk3399_sdram_config { + struct rk3399_sdram_channel_config ch[2]; + uint32_t dramtype; + uint32_t channal_num; +}; + +struct rk3399_sdram_default_config { + unsigned char bl; + /* 1:auto precharge, 0:never auto precharge */ + unsigned char ap; + /* dram driver strength */ + unsigned char dramds; + /* dram ODT, if odt=0, this parameter invalid */ + unsigned char dramodt; + /* ca ODT, if odt=0, this parameter invalid + * only used by LPDDR4 + */ + unsigned char caodt; + unsigned char burst_ref_cnt; + /* zqcs period, unit(s) */ + unsigned char zqcsi; +}; + +struct ddr_dts_config_timing { + unsigned int ddr3_speed_bin; + unsigned int pd_idle; + unsigned int sr_idle; + unsigned int sr_mc_gate_idle; + unsigned int srpd_lite_idle; + unsigned int standby_idle; + unsigned int auto_pd_dis_freq; + unsigned int ddr3_dll_dis_freq; + unsigned int phy_dll_dis_freq; + unsigned int ddr3_odt_dis_freq; + unsigned int ddr3_drv; + unsigned int ddr3_odt; + unsigned int phy_ddr3_ca_drv; + unsigned int phy_ddr3_dq_drv; + unsigned int phy_ddr3_odt; + unsigned int lpddr3_odt_dis_freq; + unsigned int lpddr3_drv; + unsigned int lpddr3_odt; + unsigned int phy_lpddr3_ca_drv; + unsigned int phy_lpddr3_dq_drv; + unsigned int phy_lpddr3_odt; + unsigned int lpddr4_odt_dis_freq; + unsigned int lpddr4_drv; + unsigned int lpddr4_dq_odt; + unsigned int lpddr4_ca_odt; + unsigned int phy_lpddr4_ca_drv; + unsigned int phy_lpddr4_ck_cs_drv; + unsigned int phy_lpddr4_dq_drv; + unsigned int phy_lpddr4_odt; + uint32_t available; +}; + +struct drv_odt_lp_config { + uint32_t ddr3_speed_bin; + uint32_t pd_idle; + uint32_t sr_idle; + uint32_t sr_mc_gate_idle; + uint32_t srpd_lite_idle; + uint32_t standby_idle; + + uint32_t ddr3_dll_dis_freq;/* for ddr3 only */ + uint32_t phy_dll_dis_freq; + uint32_t odt_dis_freq; + + uint32_t dram_side_drv; + uint32_t dram_side_dq_odt; + uint32_t dram_side_ca_odt; + + uint32_t phy_side_ca_drv; + uint32_t phy_side_ck_cs_drv; + uint32_t phy_side_dq_drv; + uint32_t phy_side_odt; +}; + +#define KHz (1000) +#define MHz (1000*KHz) +#define GHz (1000*MHz) + +#define PI_CA_TRAINING (1 << 0) +#define PI_WRITE_LEVELING (1 << 1) +#define PI_READ_GATE_TRAINING (1 << 2) +#define PI_READ_LEVELING (1 << 3) +#define PI_WDQ_LEVELING (1 << 4) +#define PI_FULL_TARINING (0xff) + +#define READ_CH_CNT(val) (1+((val>>12)&0x1)) +#define READ_CH_INFO(val) ((val>>28)&0x3) +/* row_3_4:0=normal, 1=6Gb or 12Gb */ +#define READ_CH_ROW_INFO(val, ch) ((val>>(30+(ch)))&0x1) + +#define READ_DRAMTYPE_INFO(val) ((val>>13)&0x7) +#define READ_CS_INFO(val, ch) ((((val)>>(11+(ch)*16))&0x1)+1) +#define READ_BW_INFO(val, ch) (2>>(((val)>>(2+(ch)*16))&0x3)) +#define READ_COL_INFO(val, ch) (9+(((val)>>(9+(ch)*16))&0x3)) +#define READ_BK_INFO(val, ch) (3-(((val)>>(8+(ch)*16))&0x1)) +#define READ_CS0_ROW_INFO(val, ch) (13+(((val)>>(6+(ch)*16))&0x3)) +#define READ_CS1_ROW_INFO(val, ch) (13+(((val)>>(4+(ch)*16))&0x3)) +#define READ_DIE_BW_INFO(val, ch) (2>>((val>>((ch)*16))&0x3)) + +#define __sramdata __attribute__((section(".sram.data"))) +#define __sramconst __attribute__((section(".sram.rodata"))) +#define __sramlocalfunc __attribute__((section(".sram.text"))) +#define __sramfunc __attribute__((section(".sram.text"))) \ + __attribute__((noinline)) + + +#define DDR_SAVE_SP(save_sp) (save_sp = ddr_save_sp(((uint32_t)\ + (SRAM_CODE_BASE + 0x2000) & (~7)))) + +#define DDR_RESTORE_SP(save_sp) ddr_save_sp(save_sp) + +void ddr_init(void); +uint64_t ddr_set_rate(uint64_t hz); +uint64_t ddr_round_rate(uint64_t hz); +uint64_t ddr_get_rate(void); +void clr_dcf_irq(void); +uint64_t dts_timing_receive(uint64_t timing, uint64_t index); +#endif diff --git a/plat/rockchip/rk3399/drivers/dram/dram_spec_timing.c b/plat/rockchip/rk3399/drivers/dram/dram_spec_timing.c new file mode 100644 index 00000000..b015db74 --- /dev/null +++ b/plat/rockchip/rk3399/drivers/dram/dram_spec_timing.c @@ -0,0 +1,1323 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <string.h> +#include <stdint.h> +#include "dram_spec_timing.h" + +static const uint8_t ddr3_cl_cwl[][7] = { + /* + * speed 0~330 331 ~ 400 401 ~ 533 534~666 667~800 801~933 934~1066 + * tCK>3 2.5~3 1.875~2.5 1.5~1.875 1.25~1.5 1.07~1.25 0.938~1.07 + * cl<<4, cwl cl<<4, cwl cl<<4, cwl + */ + /* DDR3_800D (5-5-5) */ + {((5 << 4) | 5), ((5 << 4) | 5), 0, 0, 0, 0, 0}, + /* DDR3_800E (6-6-6) */ + {((5 << 4) | 5), ((6 << 4) | 5), 0, 0, 0, 0, 0}, + /* DDR3_1066E (6-6-6) */ + {((5 << 4) | 5), ((5 << 4) | 5), ((6 << 4) | 6), 0, 0, 0, 0}, + /* DDR3_1066F (7-7-7) */ + {((5 << 4) | 5), ((6 << 4) | 5), ((7 << 4) | 6), 0, 0, 0, 0}, + /* DDR3_1066G (8-8-8) */ + {((5 << 4) | 5), ((6 << 4) | 5), ((8 << 4) | 6), 0, 0, 0, 0}, + /* DDR3_1333F (7-7-7) */ + {((5 << 4) | 5), ((5 << 4) | 5), ((6 << 4) | 6), ((7 << 4) | 7), + 0, 0, 0}, + /* DDR3_1333G (8-8-8) */ + {((5 << 4) | 5), ((5 << 4) | 5), ((7 << 4) | 6), ((8 << 4) | 7), + 0, 0, 0}, + /* DDR3_1333H (9-9-9) */ + {((5 << 4) | 5), ((6 << 4) | 5), ((8 << 4) | 6), ((9 << 4) | 7), + 0, 0, 0}, + /* DDR3_1333J (10-10-10) */ + {((5 << 4) | 5), ((6 << 4) | 5), ((8 << 4) | 6), ((10 << 4) | 7), + 0, 0, 0}, + /* DDR3_1600G (8-8-8) */ + {((5 << 4) | 5), ((5 << 4) | 5), ((6 << 4) | 6), ((7 << 4) | 7), + ((8 << 4) | 8), 0, 0}, + /* DDR3_1600H (9-9-9) */ + {((5 << 4) | 5), ((5 << 4) | 5), ((6 << 4) | 6), ((8 << 4) | 7), + ((9 << 4) | 8), 0, 0}, + /* DDR3_1600J (10-10-10) */ + {((5 << 4) | 5), ((5 << 4) | 5), ((7 << 4) | 6), ((9 << 4) | 7), + ((10 << 4) | 8), 0, 0}, + /* DDR3_1600K (11-11-11) */ + {((5 << 4) | 5), ((6 << 4) | 5), ((8 << 4) | 6), ((10 << 4) | 7), + ((11 << 4) | 8), 0, 0}, + /* DDR3_1866J (10-10-10) */ + {((5 << 4) | 5), ((5 << 4) | 5), ((6 << 4) | 6), ((8 << 4) | 7), + ((9 << 4) | 8), ((11 << 4) | 9), 0}, + /* DDR3_1866K (11-11-11) */ + {((5 << 4) | 5), ((5 << 4) | 5), ((7 << 4) | 6), ((8 << 4) | 7), + ((10 << 4) | 8), ((11 << 4) | 9), 0}, + /* DDR3_1866L (12-12-12) */ + {((6 << 4) | 5), ((6 << 4) | 5), ((7 << 4) | 6), ((9 << 4) | 7), + ((11 << 4) | 8), ((12 << 4) | 9), 0}, + /* DDR3_1866M (13-13-13) */ + {((6 << 4) | 5), ((6 << 4) | 5), ((8 << 4) | 6), ((10 << 4) | 7), + ((11 << 4) | 8), ((13 << 4) | 9), 0}, + /* DDR3_2133K (11-11-11) */ + {((5 << 4) | 5), ((5 << 4) | 5), ((6 << 4) | 6), ((7 << 4) | 7), + ((9 << 4) | 8), ((10 << 4) | 9), ((11 << 4) | 10)}, + /* DDR3_2133L (12-12-12) */ + {((5 << 4) | 5), ((5 << 4) | 5), ((6 << 4) | 6), ((8 << 4) | 7), + ((9 << 4) | 8), ((11 << 4) | 9), ((12 << 4) | 10)}, + /* DDR3_2133M (13-13-13) */ + {((5 << 4) | 5), ((5 << 4) | 5), ((7 << 4) | 6), ((9 << 4) | 7), + ((10 << 4) | 8), ((12 << 4) | 9), ((13 << 4) | 10)}, + /* DDR3_2133N (14-14-14) */ + {((6 << 4) | 5), ((6 << 4) | 5), ((7 << 4) | 6), ((9 << 4) | 7), + ((11 << 4) | 8), ((13 << 4) | 9), ((14 << 4) | 10)}, + /* DDR3_DEFAULT */ + {((6 << 4) | 5), ((6 << 4) | 5), ((8 << 4) | 6), ((10 << 4) | 7), + ((11 << 4) | 8), ((13 << 4) | 9), ((14 << 4) | 10)} +}; + +static const uint16_t ddr3_trc_tfaw[] = { + /* tRC tFAW */ + ((50 << 8) | 50), /* DDR3_800D (5-5-5) */ + ((53 << 8) | 50), /* DDR3_800E (6-6-6) */ + + ((49 << 8) | 50), /* DDR3_1066E (6-6-6) */ + ((51 << 8) | 50), /* DDR3_1066F (7-7-7) */ + ((53 << 8) | 50), /* DDR3_1066G (8-8-8) */ + + ((47 << 8) | 45), /* DDR3_1333F (7-7-7) */ + ((48 << 8) | 45), /* DDR3_1333G (8-8-8) */ + ((50 << 8) | 45), /* DDR3_1333H (9-9-9) */ + ((51 << 8) | 45), /* DDR3_1333J (10-10-10) */ + + ((45 << 8) | 40), /* DDR3_1600G (8-8-8) */ + ((47 << 8) | 40), /* DDR3_1600H (9-9-9)*/ + ((48 << 8) | 40), /* DDR3_1600J (10-10-10) */ + ((49 << 8) | 40), /* DDR3_1600K (11-11-11) */ + + ((45 << 8) | 35), /* DDR3_1866J (10-10-10) */ + ((46 << 8) | 35), /* DDR3_1866K (11-11-11) */ + ((47 << 8) | 35), /* DDR3_1866L (12-12-12) */ + ((48 << 8) | 35), /* DDR3_1866M (13-13-13) */ + + ((44 << 8) | 35), /* DDR3_2133K (11-11-11) */ + ((45 << 8) | 35), /* DDR3_2133L (12-12-12) */ + ((46 << 8) | 35), /* DDR3_2133M (13-13-13) */ + ((47 << 8) | 35), /* DDR3_2133N (14-14-14) */ + + ((53 << 8) | 50) /* DDR3_DEFAULT */ +}; + +static uint32_t get_max_speed_rate(struct timing_related_config *timing_config) +{ + if (timing_config->ch_cnt > 1) + return max(timing_config->dram_info[0].speed_rate, + timing_config->dram_info[1].speed_rate); + else + return timing_config->dram_info[0].speed_rate; +} + +static uint32_t +get_max_die_capability(struct timing_related_config *timing_config) +{ + uint32_t die_cap = 0; + uint32_t cs, ch; + + for (ch = 0; ch < timing_config->ch_cnt; ch++) { + for (cs = 0; cs < timing_config->dram_info[ch].cs_cnt; cs++) { + die_cap = max(die_cap, + timing_config-> + dram_info[ch].per_die_capability[cs]); + } + } + return die_cap; +} + +/* tRSTL, 100ns */ +#define DDR3_TRSTL (100) +/* trsth, 500us */ +#define DDR3_TRSTH (500000) +/* trefi, 7.8us */ +#define DDR3_TREFI_7_8_US (7800) +/* tWR, 15ns */ +#define DDR3_TWR (15) +/* tRTP, max(4 tCK,7.5ns) */ +#define DDR3_TRTP (7) +/* tRRD = max(4nCK, 10ns) */ +#define DDR3_TRRD (10) +/* tCK */ +#define DDR3_TCCD (4) +/*tWTR, max(4 tCK,7.5ns)*/ +#define DDR3_TWTR (7) +/* tCK */ +#define DDR3_TRTW (0) +/* tRAS, 37.5ns(400MHz) 37.5ns(533MHz) */ +#define DDR3_TRAS (37) +/* ns */ +#define DDR3_TRFC_512MBIT (90) +/* ns */ +#define DDR3_TRFC_1GBIT (110) +/* ns */ +#define DDR3_TRFC_2GBIT (160) +/* ns */ +#define DDR3_TRFC_4GBIT (300) +/* ns */ +#define DDR3_TRFC_8GBIT (350) + +/*pd and sr*/ +#define DDR3_TXP (7) /* tXP, max(3 tCK, 7.5ns)( < 933MHz) */ +#define DDR3_TXPDLL (24) /* tXPDLL, max(10 tCK, 24ns) */ +#define DDR3_TDLLK (512) /* tXSR, tDLLK=512 tCK */ +#define DDR3_TCKE_400MHZ (7) /* tCKE, max(3 tCK,7.5ns)(400MHz) */ +#define DDR3_TCKE_533MHZ (6) /* tCKE, max(3 tCK,5.625ns)(533MHz) */ +#define DDR3_TCKSRE (10) /* tCKSRX, max(5 tCK, 10ns) */ + +/*mode register timing*/ +#define DDR3_TMOD (15) /* tMOD, max(12 tCK,15ns) */ +#define DDR3_TMRD (4) /* tMRD, 4 tCK */ + +/* ZQ */ +#define DDR3_TZQINIT (640) /* tZQinit, max(512 tCK, 640ns) */ +#define DDR3_TZQCS (80) /* tZQCS, max(64 tCK, 80ns) */ +#define DDR3_TZQOPER (320) /* tZQoper, max(256 tCK, 320ns) */ + +/* Write leveling */ +#define DDR3_TWLMRD (40) /* tCK */ +#define DDR3_TWLO (9) /* max 7.5ns */ +#define DDR3_TWLDQSEN (25) /* tCK */ + +/* + * Description: depend on input parameter "timing_config", + * and calculate all ddr3 + * spec timing to "pdram_timing" + * parameters: + * input: timing_config + * output: pdram_timing + */ +static void ddr3_get_parameter(struct timing_related_config *timing_config, + struct dram_timing_t *pdram_timing) +{ + uint32_t nmhz = timing_config->freq; + uint32_t ddr_speed_bin = get_max_speed_rate(timing_config); + uint32_t ddr_capability_per_die = get_max_die_capability(timing_config); + uint32_t tmp; + + memset((void *)pdram_timing, 0, sizeof(struct dram_timing_t)); + pdram_timing->mhz = nmhz; + pdram_timing->al = 0; + pdram_timing->bl = timing_config->bl; + if (nmhz <= 330) + tmp = 0; + else if (nmhz <= 400) + tmp = 1; + else if (nmhz <= 533) + tmp = 2; + else if (nmhz <= 666) + tmp = 3; + else if (nmhz <= 800) + tmp = 4; + else if (nmhz <= 933) + tmp = 5; + else + tmp = 6; + + /* when dll bypss cl = cwl = 6 */ + if (nmhz < 300) { + pdram_timing->cl = 6; + pdram_timing->cwl = 6; + } else { + pdram_timing->cl = (ddr3_cl_cwl[ddr_speed_bin][tmp] >> 4) & 0xf; + pdram_timing->cwl = ddr3_cl_cwl[ddr_speed_bin][tmp] & 0xf; + } + + switch (timing_config->dramds) { + case 40: + tmp = DDR3_DS_40; + break; + case 34: + default: + tmp = DDR3_DS_34; + break; + } + + switch (timing_config->dramodt) { + case 60: + pdram_timing->mr[1] = tmp | DDR3_RTT_NOM_60; + break; + case 40: + pdram_timing->mr[1] = tmp | DDR3_RTT_NOM_40; + break; + case 120: + pdram_timing->mr[1] = tmp | DDR3_RTT_NOM_120; + break; + case 0: + default: + pdram_timing->mr[1] = tmp | DDR3_RTT_NOM_DIS; + break; + } + + pdram_timing->mr[2] = DDR3_MR2_CWL(pdram_timing->cwl); + pdram_timing->mr[3] = 0; + + pdram_timing->trstl = ((DDR3_TRSTL * nmhz + 999) / 1000); + pdram_timing->trsth = ((DDR3_TRSTH * nmhz + 999) / 1000); + /* tREFI, average periodic refresh interval, 7.8us */ + pdram_timing->trefi = ((DDR3_TREFI_7_8_US * nmhz + 999) / 1000); + /* base timing */ + pdram_timing->trcd = pdram_timing->cl; + pdram_timing->trp = pdram_timing->cl; + pdram_timing->trppb = pdram_timing->cl; + tmp = ((DDR3_TWR * nmhz + 999) / 1000); + pdram_timing->twr = tmp; + pdram_timing->tdal = tmp + pdram_timing->trp; + if (tmp < 9) { + tmp = tmp - 4; + } else { + tmp += (tmp & 0x1) ? 1 : 0; + tmp = tmp >> 1; + } + if (pdram_timing->bl == 4) + pdram_timing->mr[0] = DDR3_BC4 + | DDR3_CL(pdram_timing->cl) + | DDR3_WR(tmp); + else + pdram_timing->mr[0] = DDR3_BL8 + | DDR3_CL(pdram_timing->cl) + | DDR3_WR(tmp); + tmp = ((DDR3_TRTP * nmhz + (nmhz >> 1) + 999) / 1000); + pdram_timing->trtp = max(4, tmp); + pdram_timing->trc = + (((ddr3_trc_tfaw[ddr_speed_bin] >> 8) * nmhz + 999) / 1000); + tmp = ((DDR3_TRRD * nmhz + 999) / 1000); + pdram_timing->trrd = max(4, tmp); + pdram_timing->tccd = DDR3_TCCD; + tmp = ((DDR3_TWTR * nmhz + (nmhz >> 1) + 999) / 1000); + pdram_timing->twtr = max(4, tmp); + pdram_timing->trtw = DDR3_TRTW; + pdram_timing->tras_max = 9 * pdram_timing->trefi; + pdram_timing->tras_min = ((DDR3_TRAS * nmhz + (nmhz >> 1) + 999) + / 1000); + pdram_timing->tfaw = + (((ddr3_trc_tfaw[ddr_speed_bin] & 0x0ff) * nmhz + 999) + / 1000); + /* tRFC, 90ns(512Mb),110ns(1Gb),160ns(2Gb),300ns(4Gb),350ns(8Gb) */ + if (ddr_capability_per_die <= 0x4000000) + tmp = DDR3_TRFC_512MBIT; + else if (ddr_capability_per_die <= 0x8000000) + tmp = DDR3_TRFC_1GBIT; + else if (ddr_capability_per_die <= 0x10000000) + tmp = DDR3_TRFC_2GBIT; + else if (ddr_capability_per_die <= 0x20000000) + tmp = DDR3_TRFC_4GBIT; + else + tmp = DDR3_TRFC_8GBIT; + pdram_timing->trfc = (tmp * nmhz + 999) / 1000; + pdram_timing->txsnr = max(5, (((tmp + 10) * nmhz + 999) / 1000)); + pdram_timing->tdqsck_max = 0; + /*pd and sr*/ + pdram_timing->txsr = DDR3_TDLLK; + tmp = ((DDR3_TXP * nmhz + (nmhz >> 1) + 999) / 1000); + pdram_timing->txp = max(3, tmp); + tmp = ((DDR3_TXPDLL * nmhz + 999) / 1000); + pdram_timing->txpdll = max(10, tmp); + pdram_timing->tdllk = DDR3_TDLLK; + if (nmhz >= 533) + tmp = ((DDR3_TCKE_533MHZ * nmhz + 999) / 1000); + else + tmp = ((DDR3_TCKE_400MHZ * nmhz + (nmhz >> 1) + 999) / 1000); + pdram_timing->tcke = max(3, tmp); + pdram_timing->tckesr = (pdram_timing->tcke + 1); + tmp = ((DDR3_TCKSRE * nmhz + 999) / 1000); + pdram_timing->tcksre = max(5, tmp); + pdram_timing->tcksrx = max(5, tmp); + /*mode register timing*/ + tmp = ((DDR3_TMOD * nmhz + 999) / 1000); + pdram_timing->tmod = max(12, tmp); + pdram_timing->tmrd = DDR3_TMRD; + pdram_timing->tmrr = 0; + /*ODT*/ + pdram_timing->todton = pdram_timing->cwl - 2; + /*ZQ*/ + tmp = ((DDR3_TZQINIT * nmhz + 999) / 1000); + pdram_timing->tzqinit = max(512, tmp); + tmp = ((DDR3_TZQCS * nmhz + 999) / 1000); + pdram_timing->tzqcs = max(64, tmp); + tmp = ((DDR3_TZQOPER * nmhz + 999) / 1000); + pdram_timing->tzqoper = max(256, tmp); + /* write leveling */ + pdram_timing->twlmrd = DDR3_TWLMRD; + pdram_timing->twldqsen = DDR3_TWLDQSEN; + pdram_timing->twlo = ((DDR3_TWLO * nmhz + (nmhz >> 1) + 999) / 1000); +} + +#define LPDDR2_TINIT1 (100) /* ns */ +#define LPDDR2_TINIT2 (5) /* tCK */ +#define LPDDR2_TINIT3 (200000) /* 200us */ +#define LPDDR2_TINIT4 (1000) /* 1us */ +#define LPDDR2_TINIT5 (10000) /* 10us */ +#define LPDDR2_TRSTL (0) /* tCK */ +#define LPDDR2_TRSTH (500000) /* 500us */ +#define LPDDR2_TREFI_3_9_US (3900) /* 3.9us */ +#define LPDDR2_TREFI_7_8_US (7800) /* 7.8us */ + +/* base timing */ +#define LPDDR2_TRCD (24) /* tRCD,15ns(Fast)18ns(Typ)24ns(Slow) */ +#define LPDDR2_TRP_PB (18) /* tRPpb,15ns(Fast)18ns(Typ)24ns(Slow) */ +#define LPDDR2_TRP_AB_8_BANK (21) /* tRPab,18ns(Fast)21ns(Typ)27ns(Slow) */ +#define LPDDR2_TWR (15) /* tWR, max(3tCK,15ns) */ +#define LPDDR2_TRTP (7) /* tRTP, max(2tCK, 7.5ns) */ +#define LPDDR2_TRRD (10) /* tRRD, max(2tCK,10ns) */ +#define LPDDR2_TCCD (2) /* tCK */ +#define LPDDR2_TWTR_GREAT_200MHZ (7) /* ns */ +#define LPDDR2_TWTR_LITTLE_200MHZ (10) /* ns */ +#define LPDDR2_TRTW (0) /* tCK */ +#define LPDDR2_TRAS_MAX (70000) /* 70us */ +#define LPDDR2_TRAS (42) /* tRAS, max(3tCK,42ns) */ +#define LPDDR2_TFAW_GREAT_200MHZ (50) /* max(8tCK,50ns) */ +#define LPDDR2_TFAW_LITTLE_200MHZ (60) /* max(8tCK,60ns) */ +#define LPDDR2_TRFC_8GBIT (210) /* ns */ +#define LPDDR2_TRFC_4GBIT (130) /* ns */ +#define LPDDR2_TDQSCK_MIN (2) /* tDQSCKmin, 2.5ns */ +#define LPDDR2_TDQSCK_MAX (5) /* tDQSCKmax, 5.5ns */ + +/*pd and sr*/ +#define LPDDR2_TXP (7) /* tXP, max(2tCK,7.5ns) */ +#define LPDDR2_TXPDLL (0) +#define LPDDR2_TDLLK (0) /* tCK */ +#define LPDDR2_TCKE (3) /* tCK */ +#define LPDDR2_TCKESR (15) /* tCKESR, max(3tCK,15ns) */ +#define LPDDR2_TCKSRE (1) /* tCK */ +#define LPDDR2_TCKSRX (2) /* tCK */ + +/*mode register timing*/ +#define LPDDR2_TMOD (0) +#define LPDDR2_TMRD (5) /* tMRD, (=tMRW), 5 tCK */ +#define LPDDR2_TMRR (2) /* tCK */ + +/*ZQ*/ +#define LPDDR2_TZQINIT (1000) /* ns */ +#define LPDDR2_TZQCS (90) /* tZQCS, max(6tCK,90ns) */ +#define LPDDR2_TZQCL (360) /* tZQCL, max(6tCK,360ns) */ +#define LPDDR2_TZQRESET (50) /* ZQreset, max(3tCK,50ns) */ + +/* + * Description: depend on input parameter "timing_config", + * and calculate all lpddr2 + * spec timing to "pdram_timing" + * parameters: + * input: timing_config + * output: pdram_timing + */ +static void lpddr2_get_parameter(struct timing_related_config *timing_config, + struct dram_timing_t *pdram_timing) +{ + uint32_t nmhz = timing_config->freq; + uint32_t ddr_capability_per_die = get_max_die_capability(timing_config); + uint32_t tmp, trp_tmp, trppb_tmp, tras_tmp, twr_tmp, bl_tmp; + + memset((void *)pdram_timing, 0, sizeof(struct dram_timing_t)); + pdram_timing->mhz = nmhz; + pdram_timing->al = 0; + pdram_timing->bl = timing_config->bl; + + /* 1066 933 800 667 533 400 333 + * RL, 8 7 6 5 4 3 3 + * WL, 4 4 3 2 2 1 1 + */ + if (nmhz <= 266) { + pdram_timing->cl = 4; + pdram_timing->cwl = 2; + pdram_timing->mr[2] = LPDDR2_RL4_WL2; + } else if (nmhz <= 333) { + pdram_timing->cl = 5; + pdram_timing->cwl = 2; + pdram_timing->mr[2] = LPDDR2_RL5_WL2; + } else if (nmhz <= 400) { + pdram_timing->cl = 6; + pdram_timing->cwl = 3; + pdram_timing->mr[2] = LPDDR2_RL6_WL3; + } else if (nmhz <= 466) { + pdram_timing->cl = 7; + pdram_timing->cwl = 4; + pdram_timing->mr[2] = LPDDR2_RL7_WL4; + } else { + pdram_timing->cl = 8; + pdram_timing->cwl = 4; + pdram_timing->mr[2] = LPDDR2_RL8_WL4; + } + switch (timing_config->dramds) { + case 120: + pdram_timing->mr[3] = LPDDR2_DS_120; + break; + case 80: + pdram_timing->mr[3] = LPDDR2_DS_80; + break; + case 60: + pdram_timing->mr[3] = LPDDR2_DS_60; + break; + case 48: + pdram_timing->mr[3] = LPDDR2_DS_48; + break; + case 40: + pdram_timing->mr[3] = LPDDR2_DS_40; + break; + case 34: + default: + pdram_timing->mr[3] = LPDDR2_DS_34; + break; + } + pdram_timing->mr[0] = 0; + + pdram_timing->tinit1 = (LPDDR2_TINIT1 * nmhz + 999) / 1000; + pdram_timing->tinit2 = LPDDR2_TINIT2; + pdram_timing->tinit3 = (LPDDR2_TINIT3 * nmhz + 999) / 1000; + pdram_timing->tinit4 = (LPDDR2_TINIT4 * nmhz + 999) / 1000; + pdram_timing->tinit5 = (LPDDR2_TINIT5 * nmhz + 999) / 1000; + pdram_timing->trstl = LPDDR2_TRSTL; + pdram_timing->trsth = (LPDDR2_TRSTH * nmhz + 999) / 1000; + /* + * tREFI, average periodic refresh interval, + * 15.6us(<256Mb) 7.8us(256Mb-1Gb) 3.9us(2Gb-8Gb) + */ + if (ddr_capability_per_die >= 0x10000000) + pdram_timing->trefi = (LPDDR2_TREFI_3_9_US * nmhz + 999) + / 1000; + else + pdram_timing->trefi = (LPDDR2_TREFI_7_8_US * nmhz + 999) + / 1000; + /* base timing */ + tmp = ((LPDDR2_TRCD * nmhz + 999) / 1000); + pdram_timing->trcd = max(3, tmp); + /* + * tRPpb, max(3tCK, 15ns(Fast) 18ns(Typ) 24ns(Slow), + */ + trppb_tmp = ((LPDDR2_TRP_PB * nmhz + 999) / 1000); + trppb_tmp = max(3, trppb_tmp); + pdram_timing->trppb = trppb_tmp; + /* + * tRPab, max(3tCK, 4-bank:15ns(Fast) 18ns(Typ) 24ns(Slow), + * 8-bank:18ns(Fast) 21ns(Typ) 27ns(Slow)) + */ + trp_tmp = ((LPDDR2_TRP_AB_8_BANK * nmhz + 999) / 1000); + trp_tmp = max(3, trp_tmp); + pdram_timing->trp = trp_tmp; + twr_tmp = ((LPDDR2_TWR * nmhz + 999) / 1000); + twr_tmp = max(3, twr_tmp); + pdram_timing->twr = twr_tmp; + bl_tmp = (pdram_timing->bl == 16) ? LPDDR2_BL16 : + ((pdram_timing->bl == 8) ? LPDDR2_BL8 : LPDDR2_BL4); + pdram_timing->mr[1] = bl_tmp | LPDDR2_N_WR(twr_tmp); + tmp = ((LPDDR2_TRTP * nmhz + (nmhz >> 1) + 999) / 1000); + pdram_timing->trtp = max(2, tmp); + tras_tmp = ((LPDDR2_TRAS * nmhz + 999) / 1000); + tras_tmp = max(3, tras_tmp); + pdram_timing->tras_min = tras_tmp; + pdram_timing->tras_max = ((LPDDR2_TRAS_MAX * nmhz + 999) / 1000); + pdram_timing->trc = (tras_tmp + trp_tmp); + tmp = ((LPDDR2_TRRD * nmhz + 999) / 1000); + pdram_timing->trrd = max(2, tmp); + pdram_timing->tccd = LPDDR2_TCCD; + /* tWTR, max(2tCK, 7.5ns(533-266MHz) 10ns(200-166MHz)) */ + if (nmhz > 200) + tmp = ((LPDDR2_TWTR_GREAT_200MHZ * nmhz + (nmhz >> 1) + + 999) / 1000); + else + tmp = ((LPDDR2_TWTR_LITTLE_200MHZ * nmhz + 999) / 1000); + pdram_timing->twtr = max(2, tmp); + pdram_timing->trtw = LPDDR2_TRTW; + if (nmhz <= 200) + pdram_timing->tfaw = (LPDDR2_TFAW_LITTLE_200MHZ * nmhz + 999) + / 1000; + else + pdram_timing->tfaw = (LPDDR2_TFAW_GREAT_200MHZ * nmhz + 999) + / 1000; + /* tRFC, 90ns(<=512Mb) 130ns(1Gb-4Gb) 210ns(8Gb) */ + if (ddr_capability_per_die >= 0x40000000) { + pdram_timing->trfc = + (LPDDR2_TRFC_8GBIT * nmhz + 999) / 1000; + tmp = (((LPDDR2_TRFC_8GBIT + 10) * nmhz + 999) / 1000); + } else { + pdram_timing->trfc = + (LPDDR2_TRFC_4GBIT * nmhz + 999) / 1000; + tmp = (((LPDDR2_TRFC_4GBIT + 10) * nmhz + 999) / 1000); + } + if (tmp < 2) + tmp = 2; + pdram_timing->txsr = tmp; + pdram_timing->txsnr = tmp; + /* tdqsck use rounded down */ + pdram_timing->tdqsck = ((LPDDR2_TDQSCK_MIN * nmhz + (nmhz >> 1)) + / 1000); + pdram_timing->tdqsck_max = + ((LPDDR2_TDQSCK_MAX * nmhz + (nmhz >> 1) + 999) + / 1000); + /* pd and sr */ + tmp = ((LPDDR2_TXP * nmhz + (nmhz >> 1) + 999) / 1000); + pdram_timing->txp = max(2, tmp); + pdram_timing->txpdll = LPDDR2_TXPDLL; + pdram_timing->tdllk = LPDDR2_TDLLK; + pdram_timing->tcke = LPDDR2_TCKE; + tmp = ((LPDDR2_TCKESR * nmhz + 999) / 1000); + pdram_timing->tckesr = max(3, tmp); + pdram_timing->tcksre = LPDDR2_TCKSRE; + pdram_timing->tcksrx = LPDDR2_TCKSRX; + /* mode register timing */ + pdram_timing->tmod = LPDDR2_TMOD; + pdram_timing->tmrd = LPDDR2_TMRD; + pdram_timing->tmrr = LPDDR2_TMRR; + /* ZQ */ + pdram_timing->tzqinit = (LPDDR2_TZQINIT * nmhz + 999) / 1000; + tmp = ((LPDDR2_TZQCS * nmhz + 999) / 1000); + pdram_timing->tzqcs = max(6, tmp); + tmp = ((LPDDR2_TZQCL * nmhz + 999) / 1000); + pdram_timing->tzqoper = max(6, tmp); + tmp = ((LPDDR2_TZQRESET * nmhz + 999) / 1000); + pdram_timing->tzqreset = max(3, tmp); +} + +#define LPDDR3_TINIT1 (100) /* ns */ +#define LPDDR3_TINIT2 (5) /* tCK */ +#define LPDDR3_TINIT3 (200000) /* 200us */ +#define LPDDR3_TINIT4 (1000) /* 1us */ +#define LPDDR3_TINIT5 (10000) /* 10us */ +#define LPDDR3_TRSTL (0) +#define LPDDR3_TRSTH (0) /* 500us */ +#define LPDDR3_TREFI_3_9_US (3900) /* 3.9us */ + +/* base timging */ +#define LPDDR3_TRCD (18) /* tRCD,15ns(Fast)18ns(Typ)24ns(Slow) */ +#define LPDDR3_TRP_PB (18) /* tRPpb, 15ns(Fast) 18ns(Typ) 24ns(Slow) */ +#define LPDDR3_TRP_AB (21) /* tRPab, 18ns(Fast) 21ns(Typ) 27ns(Slow) */ +#define LPDDR3_TWR (15) /* tWR, max(4tCK,15ns) */ +#define LPDDR3_TRTP (7) /* tRTP, max(4tCK, 7.5ns) */ +#define LPDDR3_TRRD (10) /* tRRD, max(2tCK,10ns) */ +#define LPDDR3_TCCD (4) /* tCK */ +#define LPDDR3_TWTR (7) /* tWTR, max(4tCK, 7.5ns) */ +#define LPDDR3_TRTW (0) /* tCK register min valid value */ +#define LPDDR3_TRAS_MAX (70000) /* 70us */ +#define LPDDR3_TRAS (42) /* tRAS, max(3tCK,42ns) */ +#define LPDDR3_TFAW (50) /* tFAW,max(8tCK, 50ns) */ +#define LPDDR3_TRFC_8GBIT (210) /* tRFC, 130ns(4Gb) 210ns(>4Gb) */ +#define LPDDR3_TRFC_4GBIT (130) /* ns */ +#define LPDDR3_TDQSCK_MIN (2) /* tDQSCKmin,2.5ns */ +#define LPDDR3_TDQSCK_MAX (5) /* tDQSCKmax,5.5ns */ + +/* pd and sr */ +#define LPDDR3_TXP (7) /* tXP, max(3tCK,7.5ns) */ +#define LPDDR3_TXPDLL (0) +#define LPDDR3_TCKE (7) /* tCKE, (max 7.5ns,3 tCK) */ +#define LPDDR3_TCKESR (15) /* tCKESR, max(3tCK,15ns) */ +#define LPDDR3_TCKSRE (2) /* tCKSRE=tCPDED, 2 tCK */ +#define LPDDR3_TCKSRX (2) /* tCKSRX, 2 tCK */ + +/* mode register timing */ +#define LPDDR3_TMOD (0) +#define LPDDR3_TMRD (14) /* tMRD, (=tMRW), max(14ns, 10 tCK) */ +#define LPDDR3_TMRR (4) /* tMRR, 4 tCK */ +#define LPDDR3_TMRRI LPDDR3_TRCD + +/* ODT */ +#define LPDDR3_TODTON (3) /* 3.5ns */ + +/* ZQ */ +#define LPDDR3_TZQINIT (1000) /* 1us */ +#define LPDDR3_TZQCS (90) /* tZQCS, 90ns */ +#define LPDDR3_TZQCL (360) /* 360ns */ +#define LPDDR3_TZQRESET (50) /* ZQreset, max(3tCK,50ns) */ +/* write leveling */ +#define LPDDR3_TWLMRD (40) /* ns */ +#define LPDDR3_TWLO (20) /* ns */ +#define LPDDR3_TWLDQSEN (25) /* ns */ +/* CA training */ +#define LPDDR3_TCACKEL (10) /* tCK */ +#define LPDDR3_TCAENT (10) /* tCK */ +#define LPDDR3_TCAMRD (20) /* tCK */ +#define LPDDR3_TCACKEH (10) /* tCK */ +#define LPDDR3_TCAEXT (10) /* tCK */ +#define LPDDR3_TADR (20) /* ns */ +#define LPDDR3_TMRZ (3) /* ns */ + +/* + * Description: depend on input parameter "timing_config", + * and calculate all lpddr3 + * spec timing to "pdram_timing" + * parameters: + * input: timing_config + * output: pdram_timing + */ +static void lpddr3_get_parameter(struct timing_related_config *timing_config, + struct dram_timing_t *pdram_timing) +{ + uint32_t nmhz = timing_config->freq; + uint32_t ddr_capability_per_die = get_max_die_capability(timing_config); + uint32_t tmp, trp_tmp, trppb_tmp, tras_tmp, twr_tmp, bl_tmp; + + memset((void *)pdram_timing, 0, sizeof(struct dram_timing_t)); + pdram_timing->mhz = nmhz; + pdram_timing->al = 0; + pdram_timing->bl = timing_config->bl; + + /* + * Only support Write Latency Set A here + * 1066 933 800 733 667 600 533 400 166 + * RL, 16 14 12 11 10 9 8 6 3 + * WL, 8 8 6 6 6 5 4 3 1 + */ + if (nmhz <= 400) { + pdram_timing->cl = 6; + pdram_timing->cwl = 3; + pdram_timing->mr[2] = LPDDR3_RL6_WL3; + } else if (nmhz <= 533) { + pdram_timing->cl = 8; + pdram_timing->cwl = 4; + pdram_timing->mr[2] = LPDDR3_RL8_WL4; + } else if (nmhz <= 600) { + pdram_timing->cl = 9; + pdram_timing->cwl = 5; + pdram_timing->mr[2] = LPDDR3_RL9_WL5; + } else if (nmhz <= 667) { + pdram_timing->cl = 10; + pdram_timing->cwl = 6; + pdram_timing->mr[2] = LPDDR3_RL10_WL6; + } else if (nmhz <= 733) { + pdram_timing->cl = 11; + pdram_timing->cwl = 6; + pdram_timing->mr[2] = LPDDR3_RL11_WL6; + } else if (nmhz <= 800) { + pdram_timing->cl = 12; + pdram_timing->cwl = 6; + pdram_timing->mr[2] = LPDDR3_RL12_WL6; + } else if (nmhz <= 933) { + pdram_timing->cl = 14; + pdram_timing->cwl = 8; + pdram_timing->mr[2] = LPDDR3_RL14_WL8; + } else { + pdram_timing->cl = 16; + pdram_timing->cwl = 8; + pdram_timing->mr[2] = LPDDR3_RL16_WL8; + } + switch (timing_config->dramds) { + case 80: + pdram_timing->mr[3] = LPDDR3_DS_80; + break; + case 60: + pdram_timing->mr[3] = LPDDR3_DS_60; + break; + case 48: + pdram_timing->mr[3] = LPDDR3_DS_48; + break; + case 40: + pdram_timing->mr[3] = LPDDR3_DS_40; + break; + case 3440: + pdram_timing->mr[3] = LPDDR3_DS_34D_40U; + break; + case 4048: + pdram_timing->mr[3] = LPDDR3_DS_40D_48U; + break; + case 3448: + pdram_timing->mr[3] = LPDDR3_DS_34D_48U; + break; + case 34: + default: + pdram_timing->mr[3] = LPDDR3_DS_34; + break; + } + pdram_timing->mr[0] = 0; + switch (timing_config->dramodt) { + case 60: + pdram_timing->mr11 = LPDDR3_ODT_60; + break; + case 120: + pdram_timing->mr11 = LPDDR3_ODT_120; + break; + case 240: + default: + pdram_timing->mr11 = LPDDR3_ODT_240; + break; + } + + pdram_timing->tinit1 = (LPDDR3_TINIT1 * nmhz + 999) / 1000; + pdram_timing->tinit2 = LPDDR3_TINIT2; + pdram_timing->tinit3 = (LPDDR3_TINIT3 * nmhz + 999) / 1000; + pdram_timing->tinit4 = (LPDDR3_TINIT4 * nmhz + 999) / 1000; + pdram_timing->tinit5 = (LPDDR3_TINIT5 * nmhz + 999) / 1000; + pdram_timing->trstl = LPDDR3_TRSTL; + pdram_timing->trsth = (LPDDR3_TRSTH * nmhz + 999) / 1000; + /* tREFI, average periodic refresh interval, 3.9us(4Gb-16Gb) */ + pdram_timing->trefi = (LPDDR3_TREFI_3_9_US * nmhz + 999) / 1000; + /* base timing */ + tmp = ((LPDDR3_TRCD * nmhz + 999) / 1000); + pdram_timing->trcd = max(3, tmp); + trppb_tmp = ((LPDDR3_TRP_PB * nmhz + 999) / 1000); + trppb_tmp = max(3, trppb_tmp); + pdram_timing->trppb = trppb_tmp; + trp_tmp = ((LPDDR3_TRP_AB * nmhz + 999) / 1000); + trp_tmp = max(3, trp_tmp); + pdram_timing->trp = trp_tmp; + twr_tmp = ((LPDDR3_TWR * nmhz + 999) / 1000); + twr_tmp = max(4, twr_tmp); + pdram_timing->twr = twr_tmp; + if (twr_tmp <= 6) + twr_tmp = 6; + else if (twr_tmp <= 8) + twr_tmp = 8; + else if (twr_tmp <= 12) + twr_tmp = twr_tmp; + else if (twr_tmp <= 14) + twr_tmp = 14; + else + twr_tmp = 16; + if (twr_tmp > 9) + pdram_timing->mr[2] |= (1 << 4); /*enable nWR > 9*/ + twr_tmp = (twr_tmp > 9) ? (twr_tmp - 10) : (twr_tmp - 2); + bl_tmp = LPDDR3_BL8; + pdram_timing->mr[1] = bl_tmp | LPDDR3_N_WR(twr_tmp); + tmp = ((LPDDR3_TRTP * nmhz + (nmhz >> 1) + 999) / 1000); + pdram_timing->trtp = max(4, tmp); + tras_tmp = ((LPDDR3_TRAS * nmhz + 999) / 1000); + tras_tmp = max(3, tras_tmp); + pdram_timing->tras_min = tras_tmp; + pdram_timing->trc = (tras_tmp + trp_tmp); + tmp = ((LPDDR3_TRRD * nmhz + 999) / 1000); + pdram_timing->trrd = max(2, tmp); + pdram_timing->tccd = LPDDR3_TCCD; + tmp = ((LPDDR3_TWTR * nmhz + (nmhz >> 1) + 999) / 1000); + pdram_timing->twtr = max(4, tmp); + pdram_timing->trtw = ((LPDDR3_TRTW * nmhz + 999) / 1000); + pdram_timing->tras_max = ((LPDDR3_TRAS_MAX * nmhz + 999) / 1000); + tmp = (LPDDR3_TFAW * nmhz + 999) / 1000; + pdram_timing->tfaw = max(8, tmp); + if (ddr_capability_per_die > 0x20000000) { + pdram_timing->trfc = + (LPDDR3_TRFC_8GBIT * nmhz + 999) / 1000; + tmp = (((LPDDR3_TRFC_8GBIT + 10) * nmhz + 999) / 1000); + } else { + pdram_timing->trfc = + (LPDDR3_TRFC_4GBIT * nmhz + 999) / 1000; + tmp = (((LPDDR3_TRFC_4GBIT + 10) * nmhz + 999) / 1000); + } + pdram_timing->txsr = max(2, tmp); + pdram_timing->txsnr = max(2, tmp); + /* tdqsck use rounded down */ + pdram_timing->tdqsck = + ((LPDDR3_TDQSCK_MIN * nmhz + (nmhz >> 1)) + / 1000); + pdram_timing->tdqsck_max = + ((LPDDR3_TDQSCK_MAX * nmhz + (nmhz >> 1) + 999) + / 1000); + /*pd and sr*/ + tmp = ((LPDDR3_TXP * nmhz + (nmhz >> 1) + 999) / 1000); + pdram_timing->txp = max(3, tmp); + pdram_timing->txpdll = LPDDR3_TXPDLL; + tmp = ((LPDDR3_TCKE * nmhz + (nmhz >> 1) + 999) / 1000); + pdram_timing->tcke = max(3, tmp); + tmp = ((LPDDR3_TCKESR * nmhz + 999) / 1000); + pdram_timing->tckesr = max(3, tmp); + pdram_timing->tcksre = LPDDR3_TCKSRE; + pdram_timing->tcksrx = LPDDR3_TCKSRX; + /*mode register timing*/ + pdram_timing->tmod = LPDDR3_TMOD; + tmp = ((LPDDR3_TMRD * nmhz + 999) / 1000); + pdram_timing->tmrd = max(10, tmp); + pdram_timing->tmrr = LPDDR3_TMRR; + tmp = ((LPDDR3_TRCD * nmhz + 999) / 1000); + pdram_timing->tmrri = max(3, tmp); + /* ODT */ + pdram_timing->todton = (LPDDR3_TODTON * nmhz + (nmhz >> 1) + 999) + / 1000; + /* ZQ */ + pdram_timing->tzqinit = (LPDDR3_TZQINIT * nmhz + 999) / 1000; + pdram_timing->tzqcs = + ((LPDDR3_TZQCS * nmhz + 999) / 1000); + pdram_timing->tzqoper = + ((LPDDR3_TZQCL * nmhz + 999) / 1000); + tmp = ((LPDDR3_TZQRESET * nmhz + 999) / 1000); + pdram_timing->tzqreset = max(3, tmp); + /* write leveling */ + pdram_timing->twlmrd = (LPDDR3_TWLMRD * nmhz + 999) / 1000; + pdram_timing->twlo = (LPDDR3_TWLO * nmhz + 999) / 1000; + pdram_timing->twldqsen = (LPDDR3_TWLDQSEN * nmhz + 999) / 1000; + /* CA training */ + pdram_timing->tcackel = LPDDR3_TCACKEL; + pdram_timing->tcaent = LPDDR3_TCAENT; + pdram_timing->tcamrd = LPDDR3_TCAMRD; + pdram_timing->tcackeh = LPDDR3_TCACKEH; + pdram_timing->tcaext = LPDDR3_TCAEXT; + pdram_timing->tadr = (LPDDR3_TADR * nmhz + 999) / 1000; + pdram_timing->tmrz = (LPDDR3_TMRZ * nmhz + 999) / 1000; + pdram_timing->tcacd = pdram_timing->tadr + 2; +} + +#define LPDDR4_TINIT1 (200000) /* 200us */ +#define LPDDR4_TINIT2 (10) /* 10ns */ +#define LPDDR4_TINIT3 (2000000) /* 2ms */ +#define LPDDR4_TINIT4 (5) /* tCK */ +#define LPDDR4_TINIT5 (2000) /* 2us */ +#define LPDDR4_TRSTL LPDDR4_TINIT1 +#define LPDDR4_TRSTH LPDDR4_TINIT3 +#define LPDDR4_TREFI_3_9_US (3900) /* 3.9us */ + +/* base timging */ +#define LPDDR4_TRCD (18) /* tRCD, max(18ns,4tCK) */ +#define LPDDR4_TRP_PB (18) /* tRPpb, max(18ns, 4tCK) */ +#define LPDDR4_TRP_AB (21) /* tRPab, max(21ns, 4tCK) */ +#define LPDDR4_TRRD (10) /* tRRD, max(4tCK,10ns) */ +#define LPDDR4_TCCD_BL16 (8) /* tCK */ +#define LPDDR4_TCCD_BL32 (16) /* tCK */ +#define LPDDR4_TWTR (10) /* tWTR, max(8tCK, 10ns) */ +#define LPDDR4_TRTW (0) /* tCK register min valid value */ +#define LPDDR4_TRAS_MAX (70000) /* 70us */ +#define LPDDR4_TRAS (42) /* tRAS, max(3tCK,42ns) */ +#define LPDDR4_TFAW (40) /* tFAW,min 40ns) */ +#define LPDDR4_TRFC_12GBIT (280) /* tRFC, 280ns(>=12Gb) */ +#define LPDDR4_TRFC_6GBIT (180) /* 6Gb/8Gb 180ns */ +#define LPDDR4_TRFC_4GBIT (130) /* 4Gb 130ns */ +#define LPDDR4_TDQSCK_MIN (1) /* tDQSCKmin,1.5ns */ +#define LPDDR4_TDQSCK_MAX (3) /* tDQSCKmax,3.5ns */ +#define LPDDR4_TPPD (4) /* tCK */ + +/* pd and sr */ +#define LPDDR4_TXP (7) /* tXP, max(5tCK,7.5ns) */ +#define LPDDR4_TCKE (7) /* tCKE, max(7.5ns,4 tCK) */ +#define LPDDR4_TESCKE (1) /* tESCKE, max(1.75ns, 3tCK) */ +#define LPDDR4_TSR (15) /* tSR, max(15ns, 3tCK) */ +#define LPDDR4_TCMDCKE (1) /* max(1.75ns, 3tCK) */ +#define LPDDR4_TCSCKE (1) /* 1.75ns */ +#define LPDDR4_TCKELCS (5) /* max(5ns, 5tCK) */ +#define LPDDR4_TCSCKEH (1) /* 1.75ns */ +#define LPDDR4_TCKEHCS (7) /* max(7.5ns, 5tCK) */ +#define LPDDR4_TMRWCKEL (14) /* max(14ns, 10tCK) */ +#define LPDDR4_TCKELCMD (7) /* max(7.5ns, 3tCK) */ +#define LPDDR4_TCKEHCMD (7) /* max(7.5ns, 3tCK) */ +#define LPDDR4_TCKELPD (7) /* max(7.5ns, 3tCK) */ +#define LPDDR4_TCKCKEL (7) /* max(7.5ns, 3tCK) */ + +/* mode register timing */ +#define LPDDR4_TMRD (14) /* tMRD, (=tMRW), max(14ns, 10 tCK) */ +#define LPDDR4_TMRR (8) /* tMRR, 8 tCK */ + +/* ODT */ +#define LPDDR4_TODTON (3) /* 3.5ns */ + +/* ZQ */ +#define LPDDR4_TZQCAL (1000) /* 1us */ +#define LPDDR4_TZQLAT (30) /* tZQLAT, max(30ns,8tCK) */ +#define LPDDR4_TZQRESET (50) /* ZQreset, max(3tCK,50ns) */ +#define LPDDR4_TZQCKE (1) /* tZQCKE, max(1.75ns, 3tCK) */ + +/* write leveling */ +#define LPDDR4_TWLMRD (40) /* tCK */ +#define LPDDR4_TWLO (20) /* ns */ +#define LPDDR4_TWLDQSEN (20) /* tCK */ + +/* CA training */ +#define LPDDR4_TCAENT (250) /* ns */ +#define LPDDR4_TADR (20) /* ns */ +#define LPDDR4_TMRZ (1) /* 1.5ns */ +#define LPDDR4_TVREF_LONG (250) /* ns */ +#define LPDDR4_TVREF_SHORT (100) /* ns */ + +/* VRCG */ +#define LPDDR4_TVRCG_ENABLE (200) /* ns */ +#define LPDDR4_TVRCG_DISABLE (100) /* ns */ + +/* FSP */ +#define LPDDR4_TFC_LONG (250) /* ns */ +#define LPDDR4_TCKFSPE (7) /* max(7.5ns, 4tCK) */ +#define LPDDR4_TCKFSPX (7) /* max(7.5ns, 4tCK) */ + +/* + * Description: depend on input parameter "timing_config", + * and calculate all lpddr4 + * spec timing to "pdram_timing" + * parameters: + * input: timing_config + * output: pdram_timing + */ +static void lpddr4_get_parameter(struct timing_related_config *timing_config, + struct dram_timing_t *pdram_timing) +{ + uint32_t nmhz = timing_config->freq; + uint32_t ddr_capability_per_die = get_max_die_capability(timing_config); + uint32_t tmp, trp_tmp, trppb_tmp, tras_tmp; + + memset((void *)pdram_timing, 0, sizeof(struct dram_timing_t)); + pdram_timing->mhz = nmhz; + pdram_timing->al = 0; + pdram_timing->bl = timing_config->bl; + + /* + * Only support Write Latency Set A here + * 2133 1866 1600 1333 1066 800 533 266 + * RL, 36 32 28 24 20 14 10 6 + * WL, 18 16 14 12 10 8 6 4 + * nWR, 40 34 30 24 20 16 10 6 + * nRTP,16 14 12 10 8 8 8 8 + */ + tmp = (timing_config->bl == 32) ? 1 : 0; + + /* + * we always use WR preamble = 2tCK + * RD preamble = Static + */ + tmp |= (1 << 2); + if (nmhz <= 266) { + pdram_timing->cl = 6; + pdram_timing->cwl = 4; + pdram_timing->twr = 6; + pdram_timing->trtp = 8; + pdram_timing->mr[2] = LPDDR4_RL6_NRTP8 | LPDDR4_A_WL4; + } else if (nmhz <= 533) { + if (timing_config->rdbi) { + pdram_timing->cl = 12; + pdram_timing->mr[2] = LPDDR4_RL12_NRTP8 | LPDDR4_A_WL6; + } else { + pdram_timing->cl = 10; + pdram_timing->mr[2] = LPDDR4_RL10_NRTP8 | LPDDR4_A_WL6; + } + pdram_timing->cwl = 6; + pdram_timing->twr = 10; + pdram_timing->trtp = 8; + tmp |= (1 << 4); + } else if (nmhz <= 800) { + if (timing_config->rdbi) { + pdram_timing->cl = 16; + pdram_timing->mr[2] = LPDDR4_RL16_NRTP8 | LPDDR4_A_WL8; + } else { + pdram_timing->cl = 14; + pdram_timing->mr[2] = LPDDR4_RL14_NRTP8 | LPDDR4_A_WL8; + } + pdram_timing->cwl = 8; + pdram_timing->twr = 16; + pdram_timing->trtp = 8; + tmp |= (2 << 4); + } else if (nmhz <= 1066) { + if (timing_config->rdbi) { + pdram_timing->cl = 22; + pdram_timing->mr[2] = LPDDR4_RL22_NRTP8 | LPDDR4_A_WL10; + } else { + pdram_timing->cl = 20; + pdram_timing->mr[2] = LPDDR4_RL20_NRTP8 | LPDDR4_A_WL10; + } + pdram_timing->cwl = 10; + pdram_timing->twr = 20; + pdram_timing->trtp = 8; + tmp |= (3 << 4); + } else if (nmhz <= 1333) { + if (timing_config->rdbi) { + pdram_timing->cl = 28; + pdram_timing->mr[2] = LPDDR4_RL28_NRTP10 | + LPDDR4_A_WL12; + } else { + pdram_timing->cl = 24; + pdram_timing->mr[2] = LPDDR4_RL24_NRTP10 | + LPDDR4_A_WL12; + } + pdram_timing->cwl = 12; + pdram_timing->twr = 24; + pdram_timing->trtp = 10; + tmp |= (4 << 4); + } else if (nmhz <= 1600) { + if (timing_config->rdbi) { + pdram_timing->cl = 32; + pdram_timing->mr[2] = LPDDR4_RL32_NRTP12 | + LPDDR4_A_WL14; + } else { + pdram_timing->cl = 28; + pdram_timing->mr[2] = LPDDR4_RL28_NRTP12 | + LPDDR4_A_WL14; + } + pdram_timing->cwl = 14; + pdram_timing->twr = 30; + pdram_timing->trtp = 12; + tmp |= (5 << 4); + } else if (nmhz <= 1866) { + if (timing_config->rdbi) { + pdram_timing->cl = 36; + pdram_timing->mr[2] = LPDDR4_RL36_NRTP14 | + LPDDR4_A_WL16; + } else { + pdram_timing->cl = 32; + pdram_timing->mr[2] = LPDDR4_RL32_NRTP14 | + LPDDR4_A_WL16; + } + pdram_timing->cwl = 16; + pdram_timing->twr = 34; + pdram_timing->trtp = 14; + tmp |= (6 << 4); + } else { + if (timing_config->rdbi) { + pdram_timing->cl = 40; + pdram_timing->mr[2] = LPDDR4_RL40_NRTP16 | + LPDDR4_A_WL18; + } else { + pdram_timing->cl = 36; + pdram_timing->mr[2] = LPDDR4_RL36_NRTP16 | + LPDDR4_A_WL18; + } + pdram_timing->cwl = 18; + pdram_timing->twr = 40; + pdram_timing->trtp = 16; + tmp |= (7 << 4); + } + pdram_timing->mr[1] = tmp; + tmp = (timing_config->rdbi ? LPDDR4_DBI_RD_EN : 0) | + (timing_config->wdbi ? LPDDR4_DBI_WR_EN : 0); + switch (timing_config->dramds) { + case 240: + pdram_timing->mr[3] = LPDDR4_PDDS_240 | tmp; + break; + case 120: + pdram_timing->mr[3] = LPDDR4_PDDS_120 | tmp; + break; + case 80: + pdram_timing->mr[3] = LPDDR4_PDDS_80 | tmp; + break; + case 60: + pdram_timing->mr[3] = LPDDR4_PDDS_60 | tmp; + break; + case 48: + pdram_timing->mr[3] = LPDDR4_PDDS_48 | tmp; + break; + case 40: + default: + pdram_timing->mr[3] = LPDDR4_PDDS_40 | tmp; + break; + } + pdram_timing->mr[0] = 0; + switch (timing_config->dramodt) { + case 240: + tmp = LPDDR4_DQODT_240; + break; + case 120: + tmp = LPDDR4_DQODT_120; + break; + case 80: + tmp = LPDDR4_DQODT_80; + break; + case 60: + tmp = LPDDR4_DQODT_60; + break; + case 48: + tmp = LPDDR4_DQODT_48; + break; + case 40: + default: + tmp = LPDDR4_DQODT_40; + break; + } + switch (timing_config->caodt) { + case 240: + pdram_timing->mr11 = LPDDR4_CAODT_240 | tmp; + break; + case 120: + pdram_timing->mr11 = LPDDR4_CAODT_120 | tmp; + break; + case 80: + pdram_timing->mr11 = LPDDR4_CAODT_80 | tmp; + break; + case 60: + pdram_timing->mr11 = LPDDR4_CAODT_60 | tmp; + break; + case 48: + pdram_timing->mr11 = LPDDR4_CAODT_48 | tmp; + break; + case 40: + default: + pdram_timing->mr11 = LPDDR4_CAODT_40 | tmp; + break; + } + + pdram_timing->tinit1 = (LPDDR4_TINIT1 * nmhz + 999) / 1000; + pdram_timing->tinit2 = (LPDDR4_TINIT2 * nmhz + 999) / 1000; + pdram_timing->tinit3 = (LPDDR4_TINIT3 * nmhz + 999) / 1000; + pdram_timing->tinit4 = (LPDDR4_TINIT4 * nmhz + 999) / 1000; + pdram_timing->tinit5 = (LPDDR4_TINIT5 * nmhz + 999) / 1000; + pdram_timing->trstl = (LPDDR4_TRSTL * nmhz + 999) / 1000; + pdram_timing->trsth = (LPDDR4_TRSTH * nmhz + 999) / 1000; + /* tREFI, average periodic refresh interval, 3.9us(4Gb-16Gb) */ + pdram_timing->trefi = (LPDDR4_TREFI_3_9_US * nmhz + 999) / 1000; + /* base timing */ + tmp = ((LPDDR4_TRCD * nmhz + 999) / 1000); + pdram_timing->trcd = max(4, tmp); + trppb_tmp = ((LPDDR4_TRP_PB * nmhz + 999) / 1000); + trppb_tmp = max(4, trppb_tmp); + pdram_timing->trppb = trppb_tmp; + trp_tmp = ((LPDDR4_TRP_AB * nmhz + 999) / 1000); + trp_tmp = max(4, trp_tmp); + pdram_timing->trp = trp_tmp; + tras_tmp = ((LPDDR4_TRAS * nmhz + 999) / 1000); + tras_tmp = max(3, tras_tmp); + pdram_timing->tras_min = tras_tmp; + pdram_timing->trc = (tras_tmp + trp_tmp); + tmp = ((LPDDR4_TRRD * nmhz + 999) / 1000); + pdram_timing->trrd = max(4, tmp); + if (timing_config->bl == 32) + pdram_timing->tccd = LPDDR4_TCCD_BL16; + else + pdram_timing->tccd = LPDDR4_TCCD_BL32; + pdram_timing->tccdmw = 4 * pdram_timing->tccd; + tmp = ((LPDDR4_TWTR * nmhz + 999) / 1000); + pdram_timing->twtr = max(8, tmp); + pdram_timing->trtw = ((LPDDR4_TRTW * nmhz + 999) / 1000); + pdram_timing->tras_max = ((LPDDR4_TRAS_MAX * nmhz + 999) / 1000); + pdram_timing->tfaw = (LPDDR4_TFAW * nmhz + 999) / 1000; + if (ddr_capability_per_die > 0x60000000) { + /* >= 12Gb */ + pdram_timing->trfc = + (LPDDR4_TRFC_12GBIT * nmhz + 999) / 1000; + tmp = (((LPDDR4_TRFC_12GBIT + 7) * nmhz + (nmhz >> 1) + + 999) / 1000); + } else if (ddr_capability_per_die > 0x30000000) { + pdram_timing->trfc = + (LPDDR4_TRFC_6GBIT * nmhz + 999) / 1000; + tmp = (((LPDDR4_TRFC_6GBIT + 7) * nmhz + (nmhz >> 1) + + 999) / 1000); + } else { + pdram_timing->trfc = + (LPDDR4_TRFC_4GBIT * nmhz + 999) / 1000; + tmp = (((LPDDR4_TRFC_4GBIT + 7) * nmhz + (nmhz >> 1) + + 999) / 1000); + } + pdram_timing->txsr = max(2, tmp); + pdram_timing->txsnr = max(2, tmp); + /* tdqsck use rounded down */ + pdram_timing->tdqsck = ((LPDDR4_TDQSCK_MIN * nmhz + + (nmhz >> 1)) / 1000); + pdram_timing->tdqsck_max = ((LPDDR4_TDQSCK_MAX * nmhz + + (nmhz >> 1) + 999) / 1000); + pdram_timing->tppd = LPDDR4_TPPD; + /* pd and sr */ + tmp = ((LPDDR4_TXP * nmhz + (nmhz >> 1) + 999) / 1000); + pdram_timing->txp = max(5, tmp); + tmp = ((LPDDR4_TCKE * nmhz + (nmhz >> 1) + 999) / 1000); + pdram_timing->tcke = max(4, tmp); + tmp = ((LPDDR4_TESCKE * nmhz + + ((nmhz * 3) / 4) + + 999) / 1000); + pdram_timing->tescke = max(3, tmp); + tmp = ((LPDDR4_TSR * nmhz + 999) / 1000); + pdram_timing->tsr = max(3, tmp); + tmp = ((LPDDR4_TCMDCKE * nmhz + + ((nmhz * 3) / 4) + + 999) / 1000); + pdram_timing->tcmdcke = max(3, tmp); + pdram_timing->tcscke = ((LPDDR4_TCSCKE * nmhz + + ((nmhz * 3) / 4) + + 999) / 1000); + tmp = ((LPDDR4_TCKELCS * nmhz + 999) / 1000); + pdram_timing->tckelcs = max(5, tmp); + pdram_timing->tcsckeh = ((LPDDR4_TCSCKEH * nmhz + + ((nmhz * 3) / 4) + + 999) / 1000); + tmp = ((LPDDR4_TCKEHCS * nmhz + + (nmhz >> 1) + 999) / 1000); + pdram_timing->tckehcs = max(5, tmp); + tmp = ((LPDDR4_TMRWCKEL * nmhz + 999) / 1000); + pdram_timing->tmrwckel = max(10, tmp); + tmp = ((LPDDR4_TCKELCMD * nmhz + (nmhz >> 1) + + 999) / 1000); + pdram_timing->tckelcmd = max(3, tmp); + tmp = ((LPDDR4_TCKEHCMD * nmhz + (nmhz >> 1) + + 999) / 1000); + pdram_timing->tckehcmd = max(3, tmp); + tmp = ((LPDDR4_TCKELPD * nmhz + (nmhz >> 1) + + 999) / 1000); + pdram_timing->tckelpd = max(3, tmp); + tmp = ((LPDDR4_TCKCKEL * nmhz + (nmhz >> 1) + + 999) / 1000); + pdram_timing->tckckel = max(3, tmp); + /* mode register timing */ + tmp = ((LPDDR4_TMRD * nmhz + 999) / 1000); + pdram_timing->tmrd = max(10, tmp); + pdram_timing->tmrr = LPDDR4_TMRR; + pdram_timing->tmrri = pdram_timing->trcd + 3; + /* ODT */ + pdram_timing->todton = (LPDDR4_TODTON * nmhz + (nmhz >> 1) + 999) + / 1000; + /* ZQ */ + pdram_timing->tzqcal = (LPDDR4_TZQCAL * nmhz + 999) / 1000; + tmp = ((LPDDR4_TZQLAT * nmhz + 999) / 1000); + pdram_timing->tzqlat = max(8, tmp); + tmp = ((LPDDR4_TZQRESET * nmhz + 999) / 1000); + pdram_timing->tzqreset = max(3, tmp); + tmp = ((LPDDR4_TZQCKE * nmhz + + ((nmhz * 3) / 4) + + 999) / 1000); + pdram_timing->tzqcke = max(3, tmp); + /* write leveling */ + pdram_timing->twlmrd = LPDDR4_TWLMRD; + pdram_timing->twlo = (LPDDR4_TWLO * nmhz + 999) / 1000; + pdram_timing->twldqsen = LPDDR4_TWLDQSEN; + /* CA training */ + pdram_timing->tcaent = (LPDDR4_TCAENT * nmhz + 999) / 1000; + pdram_timing->tadr = (LPDDR4_TADR * nmhz + 999) / 1000; + pdram_timing->tmrz = (LPDDR4_TMRZ * nmhz + (nmhz >> 1) + 999) / 1000; + pdram_timing->tvref_long = (LPDDR4_TVREF_LONG * nmhz + 999) / 1000; + pdram_timing->tvref_short = (LPDDR4_TVREF_SHORT * nmhz + 999) / 1000; + /* VRCG */ + pdram_timing->tvrcg_enable = (LPDDR4_TVRCG_ENABLE * nmhz + + 999) / 1000; + pdram_timing->tvrcg_disable = (LPDDR4_TVRCG_DISABLE * nmhz + + 999) / 1000; + /* FSP */ + pdram_timing->tfc_long = (LPDDR4_TFC_LONG * nmhz + 999) / 1000; + tmp = (LPDDR4_TCKFSPE * nmhz + (nmhz >> 1) + 999) / 1000; + pdram_timing->tckfspe = max(4, tmp); + tmp = (LPDDR4_TCKFSPX * nmhz + (nmhz >> 1) + 999) / 1000; + pdram_timing->tckfspx = max(4, tmp); +} + +/* + * Description: depend on input parameter "timing_config", + * and calculate correspond "dram_type" + * spec timing to "pdram_timing" + * parameters: + * input: timing_config + * output: pdram_timing + * NOTE: MR ODT is set, need to disable by controller + */ +void dram_get_parameter(struct timing_related_config *timing_config, + struct dram_timing_t *pdram_timing) +{ + switch (timing_config->dram_type) { + case DDR3: + ddr3_get_parameter(timing_config, pdram_timing); + break; + case LPDDR2: + lpddr2_get_parameter(timing_config, pdram_timing); + break; + case LPDDR3: + lpddr3_get_parameter(timing_config, pdram_timing); + break; + case LPDDR4: + lpddr4_get_parameter(timing_config, pdram_timing); + break; + } +} diff --git a/plat/rockchip/rk3399/drivers/dram/dram_spec_timing.h b/plat/rockchip/rk3399/drivers/dram/dram_spec_timing.h new file mode 100644 index 00000000..2008332f --- /dev/null +++ b/plat/rockchip/rk3399/drivers/dram/dram_spec_timing.h @@ -0,0 +1,538 @@ +/* + * Copyright (c) 2016, 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: + * + * Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * Neither the name of ARM nor the names of its contributors may be used + * to endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DRAM_SPEC_TIMING_HEAD_ +#define _DRAM_SPEC_TIMING_HEAD_ +#include <stdint.h> + +enum { + DDR3 = 3, + LPDDR2 = 5, + LPDDR3 = 6, + LPDDR4 = 7, + UNUSED = 0xFF +}; + +enum ddr3_speed_rate { + /* 5-5-5 */ + DDR3_800D = 0, + /* 6-6-6 */ + DDR3_800E = 1, + /* 6-6-6 */ + DDR3_1066E = 2, + /* 7-7-7 */ + DDR3_1066F = 3, + /* 8-8-8 */ + DDR3_1066G = 4, + /* 7-7-7 */ + DDR3_1333F = 5, + /* 8-8-8 */ + DDR3_1333G = 6, + /* 9-9-9 */ + DDR3_1333H = 7, + /* 10-10-10 */ + DDR3_1333J = 8, + /* 8-8-8 */ + DDR3_1600G = 9, + /* 9-9-9 */ + DDR3_1600H = 10, + /* 10-10-10 */ + DDR3_1600J = 11, + /* 11-11-11 */ + DDR3_1600K = 12, + /* 10-10-10 */ + DDR3_1866J = 13, + /* 11-11-11 */ + DDR3_1866K = 14, + /* 12-12-12 */ + DDR3_1866L = 15, + /* 13-13-13 */ + DDR3_1866M = 16, + /* 11-11-11 */ + DDR3_2133K = 17, + /* 12-12-12 */ + DDR3_2133L = 18, + /* 13-13-13 */ + DDR3_2133M = 19, + /* 14-14-14 */ + DDR3_2133N = 20, + DDR3_DEFAULT = 21, +}; + +#define max(a, b) (((a) > (b)) ? (a) : (b)) +#define range(mi, val, ma) (((ma) > (val)) ? (max(mi, val)) : (ma)) + +struct dram_timing_t { + /* unit MHz */ + uint32_t mhz; + /* some timing unit is us */ + uint32_t tinit1; + uint32_t tinit2; + uint32_t tinit3; + uint32_t tinit4; + uint32_t tinit5; + /* reset low, DDR3:200us */ + uint32_t trstl; + /* reset high to CKE high, DDR3:500us */ + uint32_t trsth; + uint32_t trefi; + /* base */ + uint32_t trcd; + /* trp per bank */ + uint32_t trppb; + /* trp all bank */ + uint32_t trp; + uint32_t twr; + uint32_t tdal; + uint32_t trtp; + uint32_t trc; + uint32_t trrd; + uint32_t tccd; + uint32_t twtr; + uint32_t trtw; + uint32_t tras_max; + uint32_t tras_min; + uint32_t tfaw; + uint32_t trfc; + uint32_t tdqsck; + uint32_t tdqsck_max; + /* pd or sr */ + uint32_t txsr; + uint32_t txsnr; + uint32_t txp; + uint32_t txpdll; + uint32_t tdllk; + uint32_t tcke; + uint32_t tckesr; + uint32_t tcksre; + uint32_t tcksrx; + uint32_t tdpd; + /* mode regiter timing */ + uint32_t tmod; + uint32_t tmrd; + uint32_t tmrr; + uint32_t tmrri; + /* ODT */ + uint32_t todton; + /* ZQ */ + uint32_t tzqinit; + uint32_t tzqcs; + uint32_t tzqoper; + uint32_t tzqreset; + /* Write Leveling */ + uint32_t twlmrd; + uint32_t twlo; + uint32_t twldqsen; + /* CA Training */ + uint32_t tcackel; + uint32_t tcaent; + uint32_t tcamrd; + uint32_t tcackeh; + uint32_t tcaext; + uint32_t tadr; + uint32_t tmrz; + uint32_t tcacd; + /* mode register */ + uint32_t mr[4]; + uint32_t mr11; + /* lpddr4 spec */ + uint32_t mr12; + uint32_t mr13; + uint32_t mr14; + uint32_t mr16; + uint32_t mr17; + uint32_t mr20; + uint32_t mr22; + uint32_t tccdmw; + uint32_t tppd; + uint32_t tescke; + uint32_t tsr; + uint32_t tcmdcke; + uint32_t tcscke; + uint32_t tckelcs; + uint32_t tcsckeh; + uint32_t tckehcs; + uint32_t tmrwckel; + uint32_t tzqcal; + uint32_t tzqlat; + uint32_t tzqcke; + uint32_t tvref_long; + uint32_t tvref_short; + uint32_t tvrcg_enable; + uint32_t tvrcg_disable; + uint32_t tfc_long; + uint32_t tckfspe; + uint32_t tckfspx; + uint32_t tckehcmd; + uint32_t tckelcmd; + uint32_t tckelpd; + uint32_t tckckel; + /* other */ + uint32_t al; + uint32_t cl; + uint32_t cwl; + uint32_t bl; +}; + +struct dram_info_t { + /* speed_rate only used when DDR3 */ + enum ddr3_speed_rate speed_rate; + /* 1: use CS0, 2: use CS0 and CS1 */ + uint32_t cs_cnt; + /* give the max per-die capability on each rank/cs */ + uint32_t per_die_capability[2]; +}; + +struct timing_related_config { + struct dram_info_t dram_info[2]; + uint32_t dram_type; + /* MHz */ + uint32_t freq; + uint32_t ch_cnt; + uint32_t bl; + /* 1:auto precharge, 0:never auto precharge */ + uint32_t ap; + /* + * 1:dll bypass, 0:dll normal + * dram and controller dll bypass at the same time + */ + uint32_t dllbp; + /* 1:odt enable, 0:odt disable */ + uint32_t odt; + /* 1:enable, 0:disabe */ + uint32_t rdbi; + uint32_t wdbi; + /* dram driver strength */ + uint32_t dramds; + /* dram ODT, if odt=0, this parameter invalid */ + uint32_t dramodt; + /* + * ca ODT, if odt=0, this parameter invalid + * it only used by LPDDR4 + */ + uint32_t caodt; +}; + +/* mr0 for ddr3 */ +#define DDR3_BL8 (0) +#define DDR3_BC4_8 (1) +#define DDR3_BC4 (2) +#define DDR3_CL(n) (((((n) - 4) & 0x7) << 4)\ + | ((((n) - 4) & 0x8) >> 1)) +#define DDR3_WR(n) (((n) & 0x7) << 9) +#define DDR3_DLL_RESET (1 << 8) +#define DDR3_DLL_DERESET (0 << 8) + +/* mr1 for ddr3 */ +#define DDR3_DLL_ENABLE (0) +#define DDR3_DLL_DISABLE (1) +#define DDR3_MR1_AL(n) (((n) & 0x3) << 3) + +#define DDR3_DS_40 (0) +#define DDR3_DS_34 (1 << 1) +#define DDR3_RTT_NOM_DIS (0) +#define DDR3_RTT_NOM_60 (1 << 2) +#define DDR3_RTT_NOM_120 (1 << 6) +#define DDR3_RTT_NOM_40 ((1 << 2) | (1 << 6)) +#define DDR3_TDQS (1 << 11) + +/* mr2 for ddr3 */ +#define DDR3_MR2_CWL(n) ((((n) - 5) & 0x7) << 3) +#define DDR3_RTT_WR_DIS (0) +#define DDR3_RTT_WR_60 (1 << 9) +#define DDR3_RTT_WR_120 (2 << 9) + +/* + * MR0 (Device Information) + * 0:DAI complete, 1:DAI still in progress + */ +#define LPDDR2_DAI (0x1) +/* 0:S2 or S4 SDRAM, 1:NVM */ +#define LPDDR2_DI (0x1 << 1) +/* 0:DNV not supported, 1:DNV supported */ +#define LPDDR2_DNVI (0x1 << 2) +#define LPDDR2_RZQI (0x3 << 3) + +/* + * 00:RZQ self test not supported, + * 01:ZQ-pin may connect to VDDCA or float + * 10:ZQ-pin may short to GND. + * 11:ZQ-pin self test completed, no error condition detected. + */ + +/* MR1 (Device Feature) */ +#define LPDDR2_BL4 (0x2) +#define LPDDR2_BL8 (0x3) +#define LPDDR2_BL16 (0x4) +#define LPDDR2_N_WR(n) (((n) - 2) << 5) + +/* MR2 (Device Feature 2) */ +#define LPDDR2_RL3_WL1 (0x1) +#define LPDDR2_RL4_WL2 (0x2) +#define LPDDR2_RL5_WL2 (0x3) +#define LPDDR2_RL6_WL3 (0x4) +#define LPDDR2_RL7_WL4 (0x5) +#define LPDDR2_RL8_WL4 (0x6) + +/* MR3 (IO Configuration 1) */ +#define LPDDR2_DS_34 (0x1) +#define LPDDR2_DS_40 (0x2) +#define LPDDR2_DS_48 (0x3) +#define LPDDR2_DS_60 (0x4) +#define LPDDR2_DS_80 (0x6) +/* optional */ +#define LPDDR2_DS_120 (0x7) + +/* MR4 (Device Temperature) */ +#define LPDDR2_TREF_MASK (0x7) +#define LPDDR2_4_TREF (0x1) +#define LPDDR2_2_TREF (0x2) +#define LPDDR2_1_TREF (0x3) +#define LPDDR2_025_TREF (0x5) +#define LPDDR2_025_TREF_DERATE (0x6) + +#define LPDDR2_TUF (0x1 << 7) + +/* MR8 (Basic configuration 4) */ +#define LPDDR2_S4 (0x0) +#define LPDDR2_S2 (0x1) +#define LPDDR2_N (0x2) +/* Unit:MB */ +#define LPDDR2_DENSITY(mr8) (8 << (((mr8) >> 2) & 0xf)) +#define LPDDR2_IO_WIDTH(mr8) (32 >> (((mr8) >> 6) & 0x3)) + +/* MR10 (Calibration) */ +#define LPDDR2_ZQINIT (0xff) +#define LPDDR2_ZQCL (0xab) +#define LPDDR2_ZQCS (0x56) +#define LPDDR2_ZQRESET (0xc3) + +/* MR16 (PASR Bank Mask), S2 SDRAM Only */ +#define LPDDR2_PASR_FULL (0x0) +#define LPDDR2_PASR_1_2 (0x1) +#define LPDDR2_PASR_1_4 (0x2) +#define LPDDR2_PASR_1_8 (0x3) + +/* + * MR0 (Device Information) + * 0:DAI complete, + * 1:DAI still in progress + */ +#define LPDDR3_DAI (0x1) +/* + * 00:RZQ self test not supported, + * 01:ZQ-pin may connect to VDDCA or float + * 10:ZQ-pin may short to GND. + * 11:ZQ-pin self test completed, no error condition detected. + */ +#define LPDDR3_RZQI (0x3 << 3) +/* + * 0:DRAM does not support WL(Set B), + * 1:DRAM support WL(Set B) + */ +#define LPDDR3_WL_SUPOT (1 << 6) +/* + * 0:DRAM does not support RL=3,nWR=3,WL=1; + * 1:DRAM supports RL=3,nWR=3,WL=1 for frequencies <=166 + */ +#define LPDDR3_RL3_SUPOT (1 << 7) + +/* MR1 (Device Feature) */ +#define LPDDR3_BL8 (0x3) +#define LPDDR3_N_WR(n) ((n) << 5) + +/* MR2 (Device Feature 2), WL Set A,default */ +/* <=166MHz,optional*/ +#define LPDDR3_RL3_WL1 (0x1) +/* <=400MHz*/ +#define LPDDR3_RL6_WL3 (0x4) +/* <=533MHz*/ +#define LPDDR3_RL8_WL4 (0x6) +/* <=600MHz*/ +#define LPDDR3_RL9_WL5 (0x7) +/* <=667MHz,default*/ +#define LPDDR3_RL10_WL6 (0x8) +/* <=733MHz*/ +#define LPDDR3_RL11_WL6 (0x9) +/* <=800MHz*/ +#define LPDDR3_RL12_WL6 (0xa) +/* <=933MHz*/ +#define LPDDR3_RL14_WL8 (0xc) +/* <=1066MHz*/ +#define LPDDR3_RL16_WL8 (0xe) + +/* WL Set B, optional */ +/* <=667MHz,default*/ +#define LPDDR3_RL10_WL8 (0x8) +/* <=733MHz*/ +#define LPDDR3_RL11_WL9 (0x9) +/* <=800MHz*/ +#define LPDDR3_RL12_WL9 (0xa) +/* <=933MHz*/ +#define LPDDR3_RL14_WL11 (0xc) +/* <=1066MHz*/ +#define LPDDR3_RL16_WL13 (0xe) + +/* 1:enable nWR programming > 9(default)*/ +#define LPDDR3_N_WRE (1 << 4) +/* 1:Select WL Set B*/ +#define LPDDR3_WL_S (1 << 6) +/* 1:enable*/ +#define LPDDR3_WR_LEVEL (1 << 7) + +/* MR3 (IO Configuration 1) */ +#define LPDDR3_DS_34 (0x1) +#define LPDDR3_DS_40 (0x2) +#define LPDDR3_DS_48 (0x3) +#define LPDDR3_DS_60 (0x4) +#define LPDDR3_DS_80 (0x6) +#define LPDDR3_DS_34D_40U (0x9) +#define LPDDR3_DS_40D_48U (0xa) +#define LPDDR3_DS_34D_48U (0xb) + +/* MR4 (Device Temperature) */ +#define LPDDR3_TREF_MASK (0x7) +/* SDRAM Low temperature operating limit exceeded */ +#define LPDDR3_LT_EXED (0x0) +#define LPDDR3_4_TREF (0x1) +#define LPDDR3_2_TREF (0x2) +#define LPDDR3_1_TREF (0x3) +#define LPDDR3_05_TREF (0x4) +#define LPDDR3_025_TREF (0x5) +#define LPDDR3_025_TREF_DERATE (0x6) +/* SDRAM High temperature operating limit exceeded */ +#define LPDDR3_HT_EXED (0x7) + +/* 1:value has changed since last read of MR4 */ +#define LPDDR3_TUF (0x1 << 7) + +/* MR8 (Basic configuration 4) */ +#define LPDDR3_S8 (0x3) +#define LPDDR3_DENSITY(mr8) (8 << (((mr8) >> 2) & 0xf)) +#define LPDDR3_IO_WIDTH(mr8) (32 >> (((mr8) >> 6) & 0x3)) + +/* MR10 (Calibration) */ +#define LPDDR3_ZQINIT (0xff) +#define LPDDR3_ZQCL (0xab) +#define LPDDR3_ZQCS (0x56) +#define LPDDR3_ZQRESET (0xc3) + +/* MR11 (ODT Control) */ +#define LPDDR3_ODT_60 (1) +#define LPDDR3_ODT_120 (2) +#define LPDDR3_ODT_240 (3) +#define LPDDR3_ODT_DIS (0) + +/* MR2 (Device Feature 2) */ +/* RL & nRTP for DBI-RD Disabled */ +#define LPDDR4_RL6_NRTP8 (0x0) +#define LPDDR4_RL10_NRTP8 (0x1) +#define LPDDR4_RL14_NRTP8 (0x2) +#define LPDDR4_RL20_NRTP8 (0x3) +#define LPDDR4_RL24_NRTP10 (0x4) +#define LPDDR4_RL28_NRTP12 (0x5) +#define LPDDR4_RL32_NRTP14 (0x6) +#define LPDDR4_RL36_NRTP16 (0x7) +/* RL & nRTP for DBI-RD Disabled */ +#define LPDDR4_RL12_NRTP8 (0x1) +#define LPDDR4_RL16_NRTP8 (0x2) +#define LPDDR4_RL22_NRTP8 (0x3) +#define LPDDR4_RL28_NRTP10 (0x4) +#define LPDDR4_RL32_NRTP12 (0x5) +#define LPDDR4_RL36_NRTP14 (0x6) +#define LPDDR4_RL40_NRTP16 (0x7) +/* WL Set A,default */ +#define LPDDR4_A_WL4 (0x0) +#define LPDDR4_A_WL6 (0x1) +#define LPDDR4_A_WL8 (0x2) +#define LPDDR4_A_WL10 (0x3) +#define LPDDR4_A_WL12 (0x4) +#define LPDDR4_A_WL14 (0x5) +#define LPDDR4_A_WL16 (0x6) +#define LPDDR4_A_WL18 (0x7) +/* WL Set B, optional */ +#define LPDDR4_B_WL4 (0x0 << 3) +#define LPDDR4_B_WL8 (0x1 << 3) +#define LPDDR4_B_WL12 (0x2 << 3) +#define LPDDR4_B_WL18 (0x3 << 3) +#define LPDDR4_B_WL22 (0x4 << 3) +#define LPDDR4_B_WL26 (0x5 << 3) +#define LPDDR4_B_WL30 (0x6 << 3) +#define LPDDR4_B_WL34 (0x7 << 3) +/* 1:Select WL Set B*/ +#define LPDDR4_WL_B (1 << 6) +/* 1:enable*/ +#define LPDDR4_WR_LEVEL (1 << 7) + +/* MR3 */ +#define LPDDR4_VDDQ_2_5 (0) +#define LPDDR4_VDDQ_3 (1) +#define LPDDR4_WRPST_0_5_TCK (0 << 1) +#define LPDDR4_WRPST_1_5_TCK (1 << 1) +#define LPDDR4_PPR_EN (1 << 2) +/* PDDS */ +#define LPDDR4_PDDS_240 (0x1 << 3) +#define LPDDR4_PDDS_120 (0x2 << 3) +#define LPDDR4_PDDS_80 (0x3 << 3) +#define LPDDR4_PDDS_60 (0x4 << 3) +#define LPDDR4_PDDS_48 (0x5 << 3) +#define LPDDR4_PDDS_40 (0x6 << 3) +#define LPDDR4_DBI_RD_EN (1 << 6) +#define LPDDR4_DBI_WR_EN (1 << 7) + +/* MR11 (ODT Control) */ +#define LPDDR4_DQODT_240 (1) +#define LPDDR4_DQODT_120 (2) +#define LPDDR4_DQODT_80 (3) +#define LPDDR4_DQODT_60 (4) +#define LPDDR4_DQODT_48 (5) +#define LPDDR4_DQODT_40 (6) +#define LPDDR4_DQODT_DIS (0) +#define LPDDR4_CAODT_240 (1 << 4) +#define LPDDR4_CAODT_120 (2 << 4) +#define LPDDR4_CAODT_80 (3 << 4) +#define LPDDR4_CAODT_60 (4 << 4) +#define LPDDR4_CAODT_48 (5 << 4) +#define LPDDR4_CAODT_40 (6 << 4) +#define LPDDR4_CAODT_DIS (0 << 4) + +/* + * Description: depend on input parameter "timing_config", + * and calculate correspond "dram_type" + * spec timing to "pdram_timing" + * parameters: + * input: timing_config + * output: pdram_timing + * NOTE: MR ODT is set, need to disable by controller + */ +void dram_get_parameter(struct timing_related_config *timing_config, + struct dram_timing_t *pdram_timing); + +#endif /* _DRAM_SPEC_TIMING_HEAD_ */ diff --git a/plat/rockchip/rk3399/drivers/pmu/pmu.c b/plat/rockchip/rk3399/drivers/pmu/pmu.c index 01f84e92..d2d1acde 100644 --- a/plat/rockchip/rk3399/drivers/pmu/pmu.c +++ b/plat/rockchip/rk3399/drivers/pmu/pmu.c @@ -47,6 +47,7 @@ #include <pmu_com.h> #include <pwm.h> #include <soc.h> +#include <bl31.h> DEFINE_BAKERY_LOCK(rockchip_pd_lock); @@ -734,6 +735,90 @@ static int hlvl_pwr_domain_resume(uint32_t lvl, plat_local_state_t lvl_state) return 0; } +/** + * init_pmu_counts - Init timing counts in the PMU register area + * + * At various points when we power up or down parts of the system we need + * a delay to wait for power / clocks to become stable. The PMU has counters + * to help software do the delay properly. Basically, it works like this: + * - Software sets up counter values + * - When software turns on something in the PMU, the counter kicks off + * - The hardware sets a bit automatically when the counter has finished and + * software knows that the initialization is done. + * + * It's software's job to setup these counters. The hardware power on default + * for these settings is conservative, setting everything to 0x5dc0 + * (750 ms in 32 kHz counts or 1 ms in 24 MHz counts). + * + * Note that some of these counters are only really used at suspend/resume + * time (for instance, that's the only time we turn off/on the oscillator) and + * others are used during normal runtime (like turning on/off a CPU or GPU) but + * it doesn't hurt to init everything at boot. + * + * Also note that these counters can run off the 32 kHz clock or the 24 MHz + * clock. While the 24 MHz clock can give us more precision, it's not always + * available (like when we turn the oscillator off at sleep time). The + * pmu_use_lf (lf: low freq) is available in power mode. Current understanding + * is that counts work like this: + * IF (pmu_use_lf == 0) || (power_mode_en == 0) + * use the 24M OSC for counts + * ELSE + * use the 32K OSC for counts + * + * Notes: + * - There is a separate bit for the PMU called PMU_24M_EN_CFG. At the moment + * we always keep that 0. This apparently choose between using the PLL as + * the source for the PMU vs. the 24M clock. If we ever set it to 1 we + * should consider how it affects these counts (if at all). + * - The power_mode_en is documented to auto-clear automatically when we leave + * "power mode". That's why most clocks are on 24M. Only timings used when + * in "power mode" are 32k. + * - In some cases the kernel may override these counts. + * + * The PMU_STABLE_CNT / PMU_OSC_CNT / PMU_PLLLOCK_CNT are important CNTs + * in power mode, we need to ensure that they are available. + */ +static void init_pmu_counts(void) +{ + /* COUNTS FOR INSIDE POWER MODE */ + + /* + * From limited testing, need PMU stable >= 2ms, but go overkill + * and choose 30 ms to match testing on past SoCs. Also let + * OSC have 30 ms for stabilization. + */ + mmio_write_32(PMU_BASE + PMU_STABLE_CNT, CYCL_32K_CNT_MS(30)); + mmio_write_32(PMU_BASE + PMU_OSC_CNT, CYCL_32K_CNT_MS(30)); + + /* Unclear what these should be; try 3 ms */ + mmio_write_32(PMU_BASE + PMU_WAKEUP_RST_CLR_CNT, CYCL_32K_CNT_MS(3)); + + /* Unclear what this should be, but set the default explicitly */ + mmio_write_32(PMU_BASE + PMU_TIMEOUT_CNT, 0x5dc0); + + /* COUNTS FOR OUTSIDE POWER MODE */ + + /* Put something sorta conservative here until we know better */ + mmio_write_32(PMU_BASE + PMU_PLLLOCK_CNT, CYCL_24M_CNT_MS(3)); + mmio_write_32(PMU_BASE + PMU_DDRIO_PWRON_CNT, CYCL_24M_CNT_MS(1)); + mmio_write_32(PMU_BASE + PMU_CENTER_PWRDN_CNT, CYCL_24M_CNT_MS(1)); + mmio_write_32(PMU_BASE + PMU_CENTER_PWRUP_CNT, CYCL_24M_CNT_MS(1)); + + /* + * Set CPU/GPU to 1 us. + * + * NOTE: Even though ATF doesn't configure the GPU we'll still setup + * counts here. After all ATF controls all these other bits and also + * chooses which clock these counters use. + */ + mmio_write_32(PMU_BASE + PMU_SCU_L_PWRDN_CNT, CYCL_24M_CNT_US(1)); + mmio_write_32(PMU_BASE + PMU_SCU_L_PWRUP_CNT, CYCL_24M_CNT_US(1)); + mmio_write_32(PMU_BASE + PMU_SCU_B_PWRDN_CNT, CYCL_24M_CNT_US(1)); + mmio_write_32(PMU_BASE + PMU_SCU_B_PWRUP_CNT, CYCL_24M_CNT_US(1)); + mmio_write_32(PMU_BASE + PMU_GPU_PWRDN_CNT, CYCL_24M_CNT_US(1)); + mmio_write_32(PMU_BASE + PMU_GPU_PWRUP_CNT, CYCL_24M_CNT_US(1)); +} + static void sys_slp_config(void) { uint32_t slp_mode_cfg = 0; @@ -772,57 +857,15 @@ static void sys_slp_config(void) BIT(PMU_OSC_DIS) | BIT(PMU_PMU_USE_LF); - mmio_setbits_32(PMU_BASE + PMU_WKUP_CFG4, BIT(PMU_CLUSTER_L_WKUP_EN)); - mmio_setbits_32(PMU_BASE + PMU_WKUP_CFG4, BIT(PMU_CLUSTER_B_WKUP_EN)); mmio_setbits_32(PMU_BASE + PMU_WKUP_CFG4, BIT(PMU_GPIO_WKUP_EN)); mmio_write_32(PMU_BASE + PMU_PWRMODE_CON, slp_mode_cfg); - /* - * About to switch PMU counters to 32K; switch all timings to 32K - * for simplicity even if we don't plan on using them. - */ - mmio_write_32(PMU_BASE + PMU_SCU_L_PWRDN_CNT, CYCL_32K_CNT_MS(3)); - mmio_write_32(PMU_BASE + PMU_SCU_L_PWRUP_CNT, CYCL_32K_CNT_MS(3)); - mmio_write_32(PMU_BASE + PMU_SCU_B_PWRDN_CNT, CYCL_32K_CNT_MS(3)); - mmio_write_32(PMU_BASE + PMU_SCU_B_PWRUP_CNT, CYCL_32K_CNT_MS(3)); - mmio_write_32(PMU_BASE + PMU_CENTER_PWRDN_CNT, CYCL_32K_CNT_MS(3)); - mmio_write_32(PMU_BASE + PMU_CENTER_PWRUP_CNT, CYCL_32K_CNT_MS(3)); - mmio_write_32(PMU_BASE + PMU_WAKEUP_RST_CLR_CNT, CYCL_32K_CNT_MS(3)); - mmio_write_32(PMU_BASE + PMU_OSC_CNT, CYCL_32K_CNT_MS(3)); - mmio_write_32(PMU_BASE + PMU_DDRIO_PWRON_CNT, CYCL_32K_CNT_MS(3)); - mmio_write_32(PMU_BASE + PMU_PLLLOCK_CNT, CYCL_32K_CNT_MS(3)); - mmio_write_32(PMU_BASE + PMU_PLLRST_CNT, CYCL_32K_CNT_MS(3)); - mmio_write_32(PMU_BASE + PMU_STABLE_CNT, CYCL_32K_CNT_MS(3)); - - mmio_clrbits_32(PMU_BASE + PMU_SFT_CON, BIT(PMU_24M_EN_CFG)); mmio_write_32(PMU_BASE + PMU_PLL_CON, PLL_PD_HW); mmio_write_32(PMUGRF_BASE + PMUGRF_SOC_CON0, EXTERNAL_32K); mmio_write_32(PMUGRF_BASE, IOMUX_CLK_32K); /* 32k iomux */ } -static void sys_slp_unconfig(void) -{ - /* - * About to switch PMU counters to 24M; switch all timings to 24M - * for simplicity even if we don't plan on using them. - */ - mmio_write_32(PMU_BASE + PMU_SCU_L_PWRDN_CNT, CYCL_24M_CNT_MS(3)); - mmio_write_32(PMU_BASE + PMU_SCU_L_PWRUP_CNT, CYCL_24M_CNT_MS(3)); - mmio_write_32(PMU_BASE + PMU_SCU_B_PWRDN_CNT, CYCL_24M_CNT_MS(3)); - mmio_write_32(PMU_BASE + PMU_SCU_B_PWRUP_CNT, CYCL_24M_CNT_MS(3)); - mmio_write_32(PMU_BASE + PMU_CENTER_PWRDN_CNT, CYCL_24M_CNT_MS(3)); - mmio_write_32(PMU_BASE + PMU_CENTER_PWRUP_CNT, CYCL_24M_CNT_MS(3)); - mmio_write_32(PMU_BASE + PMU_WAKEUP_RST_CLR_CNT, CYCL_24M_CNT_MS(3)); - mmio_write_32(PMU_BASE + PMU_OSC_CNT, CYCL_24M_CNT_MS(3)); - mmio_write_32(PMU_BASE + PMU_DDRIO_PWRON_CNT, CYCL_24M_CNT_MS(3)); - mmio_write_32(PMU_BASE + PMU_PLLLOCK_CNT, CYCL_24M_CNT_MS(3)); - mmio_write_32(PMU_BASE + PMU_PLLRST_CNT, CYCL_24M_CNT_MS(3)); - mmio_write_32(PMU_BASE + PMU_STABLE_CNT, CYCL_24M_CNT_MS(3)); - - mmio_setbits_32(PMU_BASE + PMU_SFT_CON, BIT(PMU_24M_EN_CFG)); -} - static void set_hw_idle(uint32_t hw_idle) { mmio_setbits_32(PMU_BASE + PMU_BUS_CLR, hw_idle); @@ -879,6 +922,10 @@ static int sys_pwr_domain_suspend(void) } mmio_setbits_32(PMU_BASE + PMU_PWRDN_CON, BIT(PMU_SCU_B_PWRDWN_EN)); + /* + * Disabling PLLs/PWM/DVFS is approaching WFI which is + * the last steps in suspend. + */ plls_suspend_prepare(); disable_dvfs_plls(); disable_pwms(); @@ -899,7 +946,16 @@ static int sys_pwr_domain_resume(void) enable_dvfs_plls(); plls_resume_finish(); - sys_slp_unconfig(); + /* + * The wakeup status is not cleared by itself, we need to clear it + * manually. Otherwise we will alway query some interrupt next time. + * + * NOTE: If the kernel needs to query this, we might want to stash it + * somewhere. + */ + mmio_write_32(PMU_BASE + PMU_WAKEUP_STATUS, 0xffffffff); + + mmio_write_32(PMU_BASE + PMU_WKUP_CFG4, 0x00); mmio_write_32(SGRF_BASE + SGRF_SOC_CON0_1(1), (cpu_warm_boot_addr >> CPU_BOOT_ADDR_ALIGN) | @@ -993,6 +1049,42 @@ void __dead2 soc_system_off(void) while (1) ; } +static void __dead2 sys_pwr_down_wfi(const psci_power_state_t *target_state) +{ + uint32_t wakeup_status; + + /* + * Check wakeup status and abort suspend early if we see a wakeup + * event. + * + * NOTE: technically I we're supposed to just execute a wfi here and + * we'll either execute a normal suspend/resume or the wfi will be + * treated as a no-op if a wake event was present and caused an abort + * of the suspend/resume. For some reason that's not happening and if + * we execute the wfi while a wake event is pending then the whole + * system wedges. + * + * Until the above is solved this extra check prevents system wedges in + * most cases but there is still a small race condition between checking + * PMU_WAKEUP_STATUS and executing wfi. If a wake event happens in + * there then we will die. + */ + wakeup_status = mmio_read_32(PMU_BASE + PMU_WAKEUP_STATUS); + if (wakeup_status) { + WARN("early wake, will not enter power mode.\n"); + + mmio_write_32(PMU_BASE + PMU_PWRMODE_CON, 0); + + disable_mmu_icache_el3(); + bl31_warm_entrypoint(); + + while (1) + ; + } else { + /* Enter WFI */ + psci_power_down_wfi(); + } +} static struct rockchip_pm_ops_cb pm_ops = { .cores_pwr_dm_on = cores_pwr_domain_on, @@ -1008,6 +1100,7 @@ static struct rockchip_pm_ops_cb pm_ops = { .sys_pwr_dm_resume = sys_pwr_domain_resume, .sys_gbl_soft_reset = soc_soft_reset, .system_off = soc_system_off, + .sys_pwr_down_wfi = sys_pwr_down_wfi, }; void plat_rockchip_pmu_init(void) @@ -1037,6 +1130,14 @@ void plat_rockchip_pmu_init(void) CPU_BOOT_ADDR_WMASK); mmio_write_32(PMU_BASE + PMU_NOC_AUTO_ENA, NOC_AUTO_ENABLE); + /* + * Enable Schmitt trigger for better 32 kHz input signal, which is + * important for suspend/resume reliability among other things. + */ + mmio_write_32(PMUGRF_BASE + PMUGRF_GPIO0A_SMT, GPIO0A0_SMT_ENABLE); + + init_pmu_counts(); + nonboot_cpus_off(); INFO("%s(%d): pd status %x\n", __func__, __LINE__, diff --git a/plat/rockchip/rk3399/drivers/pmu/pmu.h b/plat/rockchip/rk3399/drivers/pmu/pmu.h index c821efc0..65fe7dbe 100644 --- a/plat/rockchip/rk3399/drivers/pmu/pmu.h +++ b/plat/rockchip/rk3399/drivers/pmu/pmu.h @@ -819,6 +819,7 @@ enum pmu_core_pwr_st { #define AP_PWROFF 0x0a +#define GPIO0A0_SMT_ENABLE BITS_WITH_WMASK(1, 3, 0) #define GPIO1A6_IOMUX BITS_WITH_WMASK(0, 3, 12) #define TSADC_INT_PIN 38 @@ -876,6 +877,7 @@ enum pmu_core_pwr_st { #define GRF_SOC_CON4 0x0e210 #define GRF_GPIO4C_IOMUX 0x0e028 +#define PMUGRF_GPIO0A_SMT 0x0120 #define PMUGRF_SOC_CON0 0x0180 #define CCI_FORCE_WAKEUP WMSK_BIT(8) diff --git a/plat/rockchip/rk3399/drivers/soc/soc.h b/plat/rockchip/rk3399/drivers/soc/soc.h index 1ea6e5e1..906452a3 100644 --- a/plat/rockchip/rk3399/drivers/soc/soc.h +++ b/plat/rockchip/rk3399/drivers/soc/soc.h @@ -48,6 +48,12 @@ #define NO_PLL_BYPASS (0x00) #define NO_PLL_PWRDN (0x00) +#define FBDIV(n) ((0xfff << 16) | n) +#define POSTDIV2(n) ((0x7 << (12 + 16)) | (n << 12)) +#define POSTDIV1(n) ((0x7 << (8 + 16)) | (n << 8)) +#define REFDIV(n) ((0x3F << 16) | n) +#define PLL_LOCK(n) ((n >> 31) & 0x1) + #define PLL_SLOW_MODE BITS_WITH_WMASK(SLOW_MODE,\ PLL_MODE_MSK, PLL_MODE_SHIFT) @@ -107,6 +113,31 @@ struct deepsleep_data_s { uint32_t pmucru_gate_con[PMUCRU_GATE_COUNT]; }; +/************************************************** + * pmugrf reg, offset + **************************************************/ +#define PMUGRF_OSREG(n) (0x300 + (n) * 4) + +/************************************************** + * DCF reg, offset + **************************************************/ +#define DCF_DCF_CTRL 0x0 +#define DCF_DCF_ADDR 0x8 +#define DCF_DCF_ISR 0xc +#define DCF_DCF_TOSET 0x14 +#define DCF_DCF_TOCMD 0x18 +#define DCF_DCF_CMD_CFG 0x1c + +/* DCF_DCF_ISR */ +#define DCF_TIMEOUT (1 << 2) +#define DCF_ERR (1 << 1) +#define DCF_DONE (1 << 0) + +/* DCF_DCF_CTRL */ +#define DCF_VOP_HW_EN (1 << 2) +#define DCF_STOP (1 << 1) +#define DCF_START (1 << 0) + #define CYCL_24M_CNT_US(us) (24 * us) #define CYCL_24M_CNT_MS(ms) (ms * CYCL_24M_CNT_US(1000)) #define CYCL_32K_CNT_MS(ms) (ms * 32) @@ -256,6 +287,12 @@ struct deepsleep_data_s { #define PWM_DISABLE (0 << 0) #define PWM_ENABLE (1 << 0) +/* grf reg offset */ +#define GRF_DDRC0_CON0 0xe380 +#define GRF_DDRC0_CON1 0xe384 +#define GRF_DDRC1_CON0 0xe388 +#define GRF_DDRC1_CON1 0xe38c + /* * When system reset in running state, we want the cpus to be reboot * from maskrom (system reboot), diff --git a/plat/rockchip/rk3399/plat_sip_calls.c b/plat/rockchip/rk3399/plat_sip_calls.c index 3d2f39a7..6069be2a 100644 --- a/plat/rockchip/rk3399/plat_sip_calls.c +++ b/plat/rockchip/rk3399/plat_sip_calls.c @@ -29,6 +29,42 @@ #include <plat_sip_calls.h> #include <rockchip_sip_svc.h> #include <runtime_svc.h> +#include <dram.h> + +#define RK_SIP_DDR_CFG64 0x82000008 +#define CONFIG_DRAM_INIT 0x00 +#define CONFIG_DRAM_SET_RATE 0x01 +#define CONFIG_DRAM_ROUND_RATE 0x02 +#define CONFIG_DRAM_SET_AT_SR 0x03 +#define CONFIG_DRAM_GET_BW 0x04 +#define CONFIG_DRAM_GET_RATE 0x05 +#define CONFIG_DRAM_CLR_IRQ 0x06 +#define CONFIG_DRAM_SET_PARAM 0x07 + +uint64_t ddr_smc_handler(uint64_t arg0, uint64_t arg1, uint64_t id) +{ + switch (id) { + case CONFIG_DRAM_INIT: + ddr_init(); + break; + case CONFIG_DRAM_SET_RATE: + return ddr_set_rate(arg0); + case CONFIG_DRAM_ROUND_RATE: + return ddr_round_rate(arg0); + case CONFIG_DRAM_GET_RATE: + return ddr_get_rate(); + case CONFIG_DRAM_CLR_IRQ: + clr_dcf_irq(); + break; + case CONFIG_DRAM_SET_PARAM: + dts_timing_receive(arg0, arg1); + break; + default: + break; + } + + return 0; +} uint64_t rockchip_plat_sip_handler(uint32_t smc_fid, uint64_t x1, @@ -40,6 +76,8 @@ uint64_t rockchip_plat_sip_handler(uint32_t smc_fid, uint64_t flags) { switch (smc_fid) { + case RK_SIP_DDR_CFG64: + SMC_RET1(handle, ddr_smc_handler(x1, x2, x3)); default: ERROR("%s: unhandled SMC (0x%x)\n", __func__, smc_fid); SMC_RET1(handle, SMC_UNK); diff --git a/plat/rockchip/rk3399/platform.mk b/plat/rockchip/rk3399/platform.mk index e8d4d41d..36278573 100644 --- a/plat/rockchip/rk3399/platform.mk +++ b/plat/rockchip/rk3399/platform.mk @@ -40,6 +40,7 @@ PLAT_INCLUDES := -I${RK_PLAT_COMMON}/ \ -I${RK_PLAT_SOC}/drivers/pmu/ \ -I${RK_PLAT_SOC}/drivers/pwm/ \ -I${RK_PLAT_SOC}/drivers/soc/ \ + -I${RK_PLAT_SOC}/drivers/dram/ \ -I${RK_PLAT_SOC}/include/ \ RK_GIC_SOURCES := drivers/arm/gic/common/gic_common.c \ @@ -55,8 +56,8 @@ PLAT_BL_COMMON_SOURCES := lib/xlat_tables/xlat_tables_common.c \ BL31_SOURCES += ${RK_GIC_SOURCES} \ drivers/arm/cci/cci.c \ - drivers/console/console.S \ - drivers/ti/uart/16550_console.S \ + drivers/console/aarch64/console.S \ + drivers/ti/uart/aarch64/16550_console.S \ drivers/delay_timer/delay_timer.c \ drivers/delay_timer/generic_delay_timer.c \ drivers/gpio/gpio.c \ @@ -76,6 +77,8 @@ BL31_SOURCES += ${RK_GIC_SOURCES} ${RK_PLAT_SOC}/drivers/gpio/rk3399_gpio.c \ ${RK_PLAT_SOC}/drivers/pmu/pmu.c \ ${RK_PLAT_SOC}/drivers/pwm/pwm.c \ - ${RK_PLAT_SOC}/drivers/soc/soc.c + ${RK_PLAT_SOC}/drivers/soc/soc.c \ + ${RK_PLAT_SOC}/drivers/dram/dram.c \ + ${RK_PLAT_SOC}/drivers/dram/dram_spec_timing.c ENABLE_PLAT_COMPAT := 0 diff --git a/plat/rockchip/rk3399/rk3399_def.h b/plat/rockchip/rk3399/rk3399_def.h index 8ee71a88..fdf93fd6 100644 --- a/plat/rockchip/rk3399/rk3399_def.h +++ b/plat/rockchip/rk3399/rk3399_def.h @@ -61,6 +61,12 @@ #define PWM_BASE (MMIO_BASE + 0x1420000) #define PWM_SIZE SIZE_K(64) +#define CIC_BASE (MMIO_BASE + 0x1620000) +#define CIC_SIZE SIZE_K(4) + +#define DCF_BASE (MMIO_BASE + 0x16a0000) +#define DCF_SIZE SIZE_K(4) + #define GPIO0_BASE (MMIO_BASE + 0x1720000) #define GPIO0_SIZE SIZE_K(64) @@ -85,12 +91,21 @@ #define STIME_BASE (MMIO_BASE + 0x1860000) #define STIME_SIZE SIZE_K(64) +#define SRAM_BASE (MMIO_BASE + 0x18c0000) +#define SRAM_SIZE SIZE_K(192) + #define SERVICE_NOC_0_BASE (MMIO_BASE + 0x1a50000) #define NOC_0_SIZE SIZE_K(192) +#define DDRC0_BASE (MMIO_BASE + 0x1a80000) +#define DDRC0_SIZE SIZE_K(32) + #define SERVICE_NOC_1_BASE (MMIO_BASE + 0x1a84000) #define NOC_1_SIZE SIZE_K(16) +#define DDRC1_BASE (MMIO_BASE + 0x1a88000) +#define DDRC1_SIZE SIZE_K(32) + #define SERVICE_NOC_2_BASE (MMIO_BASE + 0x1a8c000) #define NOC_2_SIZE SIZE_K(16) @@ -100,6 +115,14 @@ #define CCI500_BASE (MMIO_BASE + 0x1b00000) #define CCI500_SIZE SIZE_M(1) +#define DDR_PI_OFFSET 0x800 +#define DDR_PHY_OFFSET 0x2000 + +#define DDRC0_PI_BASE (DDRC0_BASE + DDR_PI_OFFSET) +#define DDRC0_PHY_BASE (DDRC0_BASE + DDR_PHY_OFFSET) +#define DDRC1_PI_BASE (DDRC1_BASE + DDR_PI_OFFSET) +#define DDRC1_PHY_BASE (DDRC1_BASE + DDR_PHY_OFFSET) + /* Aggregate of all devices in the first GB */ #define RK3399_DEV_RNG0_BASE MMIO_BASE #define RK3399_DEV_RNG0_SIZE 0x1d00000 diff --git a/plat/xilinx/zynqmp/platform.mk b/plat/xilinx/zynqmp/platform.mk index fe939c75..9bde5ff6 100644 --- a/plat/xilinx/zynqmp/platform.mk +++ b/plat/xilinx/zynqmp/platform.mk @@ -67,8 +67,8 @@ PLAT_BL_COMMON_SOURCES := lib/xlat_tables/xlat_tables_common.c \ drivers/arm/gic/common/gic_common.c \ drivers/arm/gic/v2/gicv2_main.c \ drivers/arm/gic/v2/gicv2_helpers.c \ - drivers/cadence/uart/cdns_console.S \ - drivers/console/console.S \ + drivers/cadence/uart/aarch64/cdns_console.S \ + drivers/console/aarch64/console.S \ plat/arm/common/aarch64/arm_helpers.S \ plat/arm/common/arm_cci.c \ plat/arm/common/arm_common.c \ diff --git a/tools/fiptool/fiptool.c b/tools/fiptool/fiptool.c index 4a3e61a3..68ddcf5a 100644 --- a/tools/fiptool/fiptool.c +++ b/tools/fiptool/fiptool.c @@ -587,7 +587,7 @@ static void create_usage(void) { toc_entry_t *toc_entry = toc_entries; - printf("fiptfool create [--plat-toc-flags <value>] [opts] FIP_FILENAME\n"); + printf("fiptool create [--plat-toc-flags <value>] [opts] FIP_FILENAME\n"); printf(" --plat-toc-flags <value>\t16-bit platform specific flag field " "occupying bits 32-47 in 64-bit ToC header.\n"); fputc('\n', stderr); @@ -672,7 +672,7 @@ static void update_usage(void) { toc_entry_t *toc_entry = toc_entries; - printf("fiptfool update [--out FIP_FILENAME] " + printf("fiptool update [--out FIP_FILENAME] " "[--plat-toc-flags <value>] [opts] FIP_FILENAME\n"); printf(" --out FIP_FILENAME\t\tSet an alternative output FIP file.\n"); printf(" --plat-toc-flags <value>\t16-bit platform specific flag field " |