From 360cbe30be5de0fff2d69c5bd013a67ad1b821e9 Mon Sep 17 00:00:00 2001 From: Nicolas Royer Date: Sun, 27 Sep 2020 16:44:13 +0200 Subject: [PATCH 01/19] arch: add armv8-a support Signed-off-by: Tsutomu Muroya Signed-off-by: Nicolas Royer --- arch/arm/armv8-a/arch.mk | 17 + arch/arm/armv8-a/include/arch.h | 852 ++++++++++++++++++ arch/arm/armv8-a/include/arch_gic.h | 135 +++ arch/arm/armv8-a/include/arch_helpers.h | 534 +++++++++++ arch/arm/armv8-a/include/arch_system.h | 26 + arch/arm/armv8-a/include/asm_macros.S | 194 ++++ arch/arm/armv8-a/include/assert_macros.S | 30 + .../include/common/asm_macros_common.S | 108 +++ arch/arm/armv8-a/include/common/debug.h | 32 + arch/arm/armv8-a/include/lib/libc/assert.h | 44 + arch/arm/armv8-a/include/lib/libc/stdint.h | 123 +++ arch/arm/armv8-a/include/lib/libc/string.h | 19 + arch/arm/armv8-a/include/lib/mmio.h | 77 ++ arch/arm/armv8-a/include/lib/utils_def.h | 174 ++++ arch/arm/armv8-a/src/arch.ld.S | 218 +++++ arch/arm/armv8-a/src/arch_cache_helpers.S | 204 +++++ arch/arm/armv8-a/src/arch_crt0.S | 186 ++++ arch/arm/armv8-a/src/arch_exceptions.S | 127 +++ arch/arm/armv8-a/src/arch_gic.c | 567 ++++++++++++ arch/arm/armv8-a/src/arch_libc.c | 258 ++++++ arch/arm/armv8-a/src/arch_main.c | 44 + arch/arm/armv8-a/src/arch_misc_helpers.S | 511 +++++++++++ arch/arm/armv8-a/src/arch_mm.c | 65 ++ tools/build_system/cpu.mk | 10 + tools/build_system/rules.mk | 26 +- 25 files changed, 4576 insertions(+), 5 deletions(-) create mode 100644 arch/arm/armv8-a/arch.mk create mode 100644 arch/arm/armv8-a/include/arch.h create mode 100644 arch/arm/armv8-a/include/arch_gic.h create mode 100644 arch/arm/armv8-a/include/arch_helpers.h create mode 100644 arch/arm/armv8-a/include/arch_system.h create mode 100644 arch/arm/armv8-a/include/asm_macros.S create mode 100644 arch/arm/armv8-a/include/assert_macros.S create mode 100644 arch/arm/armv8-a/include/common/asm_macros_common.S create mode 100644 arch/arm/armv8-a/include/common/debug.h create mode 100644 arch/arm/armv8-a/include/lib/libc/assert.h create mode 100644 arch/arm/armv8-a/include/lib/libc/stdint.h create mode 100644 arch/arm/armv8-a/include/lib/libc/string.h create mode 100644 arch/arm/armv8-a/include/lib/mmio.h create mode 100644 arch/arm/armv8-a/include/lib/utils_def.h create mode 100644 arch/arm/armv8-a/src/arch.ld.S create mode 100644 arch/arm/armv8-a/src/arch_cache_helpers.S create mode 100644 arch/arm/armv8-a/src/arch_crt0.S create mode 100644 arch/arm/armv8-a/src/arch_exceptions.S create mode 100644 arch/arm/armv8-a/src/arch_gic.c create mode 100644 arch/arm/armv8-a/src/arch_libc.c create mode 100644 arch/arm/armv8-a/src/arch_main.c create mode 100644 arch/arm/armv8-a/src/arch_misc_helpers.S create mode 100644 arch/arm/armv8-a/src/arch_mm.c diff --git a/arch/arm/armv8-a/arch.mk b/arch/arm/armv8-a/arch.mk new file mode 100644 index 000000000..156883dfd --- /dev/null +++ b/arch/arm/armv8-a/arch.mk @@ -0,0 +1,17 @@ +# +# Arm SCP/MCP Software +# Copyright (c) 2018-2020, Arm Limited and Contributors. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +BS_LIB_SOURCES_$(BS_ARCH_ARCH) += arch_cache_helpers.S +BS_LIB_SOURCES_$(BS_ARCH_ARCH) += arch_crt0.S +BS_LIB_SOURCES_$(BS_ARCH_ARCH) += arch_exceptions.c +BS_LIB_SOURCES_$(BS_ARCH_ARCH) += arch_gic.c +BS_LIB_SOURCES_$(BS_ARCH_ARCH) += arch_libc.c +BS_LIB_SOURCES_$(BS_ARCH_ARCH) += arch_main.c +BS_LIB_SOURCES_$(BS_ARCH_ARCH) += arch_misc_helpers.S +BS_LIB_SOURCES_$(BS_ARCH_ARCH) += arch_mm.c + +BS_LIB_SOURCES_$(BS_ARCH_ARCH) := $(addprefix $(ARCH_DIR)/$(BS_ARCH_VENDOR)/$(BS_ARCH_ARCH)/src/,$(BS_LIB_SOURCES_$(BS_ARCH_ARCH))) diff --git a/arch/arm/armv8-a/include/arch.h b/arch/arm/armv8-a/include/arch.h new file mode 100644 index 000000000..b9cb0a594 --- /dev/null +++ b/arch/arm/armv8-a/include/arch.h @@ -0,0 +1,852 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2013-2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef ARCH_H +#define ARCH_H + +#include + +/******************************************************************************* + * MIDR bit definitions + ******************************************************************************/ +#define MIDR_IMPL_MASK U(0xff) +#define MIDR_IMPL_SHIFT U(0x18) +#define MIDR_VAR_SHIFT U(20) +#define MIDR_VAR_BITS U(4) +#define MIDR_VAR_MASK U(0xf) +#define MIDR_REV_SHIFT U(0) +#define MIDR_REV_BITS U(4) +#define MIDR_REV_MASK U(0xf) +#define MIDR_PN_MASK U(0xfff) +#define MIDR_PN_SHIFT U(0x4) + +/******************************************************************************* + * MPIDR macros + ******************************************************************************/ +#define MPIDR_MT_MASK (ULL(1) << 24) +#define MPIDR_CPU_MASK MPIDR_AFFLVL_MASK +#define MPIDR_CLUSTER_MASK (MPIDR_AFFLVL_MASK << MPIDR_AFFINITY_BITS) +#define MPIDR_AFFINITY_BITS U(8) +#define MPIDR_AFFLVL_MASK ULL(0xff) +#define MPIDR_AFF0_SHIFT U(0) +#define MPIDR_AFF1_SHIFT U(8) +#define MPIDR_AFF2_SHIFT U(16) +#define MPIDR_AFF3_SHIFT U(32) +#define MPIDR_AFF_SHIFT(_n) MPIDR_AFF##_n##_SHIFT +#define MPIDR_AFFINITY_MASK ULL(0xff00ffffff) +#define MPIDR_AFFLVL_SHIFT U(3) +#define MPIDR_AFFLVL0 ULL(0x0) +#define MPIDR_AFFLVL1 ULL(0x1) +#define MPIDR_AFFLVL2 ULL(0x2) +#define MPIDR_AFFLVL3 ULL(0x3) +#define MPIDR_AFFLVL(_n) MPIDR_AFFLVL##_n +#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) +#define MPIDR_AFFLVL3_VAL(mpidr) \ + (((mpidr) >> MPIDR_AFF3_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. + * TODO: Support only the first 3 affinity levels for now. + */ +#define MPIDR_MAX_AFFLVL U(2) + +#define MPID_MASK (MPIDR_MT_MASK | \ + (MPIDR_AFFLVL_MASK << MPIDR_AFF3_SHIFT) | \ + (MPIDR_AFFLVL_MASK << MPIDR_AFF2_SHIFT) | \ + (MPIDR_AFFLVL_MASK << MPIDR_AFF1_SHIFT) | \ + (MPIDR_AFFLVL_MASK << MPIDR_AFF0_SHIFT)) + +#define MPIDR_AFF_ID(mpid, n) \ + (((mpid) >> MPIDR_AFF_SHIFT(n)) & MPIDR_AFFLVL_MASK) + +/* + * An invalid MPID. This value can be used by functions that return an MPID to + * indicate an error. + */ +#define INVALID_MPID U(0xFFFFFFFF) + +/******************************************************************************* + * Definitions for CPU system register interface to GICv3 + ******************************************************************************/ +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_SGI1R S3_0_C12_C11_5 +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 +#define ICC_CTLR_EL1 S3_0_C12_C12_4 +#define ICC_CTLR_EL3 S3_6_C12_C12_4 +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_RPR_EL1 S3_0_C12_C11_3 +#define ICC_IGRPEN1_EL3 S3_6_c12_c12_7 +#define ICC_IGRPEN0_EL1 S3_0_c12_c12_6 +#define ICC_HPPIR0_EL1 S3_0_c12_c8_2 +#define ICC_HPPIR1_EL1 S3_0_c12_c12_2 +#define ICC_IAR0_EL1 S3_0_c12_c8_0 +#define ICC_IAR1_EL1 S3_0_c12_c12_0 +#define ICC_EOIR0_EL1 S3_0_c12_c8_1 +#define ICC_EOIR1_EL1 S3_0_c12_c12_1 +#define ICC_SGI0R_EL1 S3_0_c12_c11_7 + +/******************************************************************************* + * Generic timer memory mapped registers & offsets + ******************************************************************************/ +#define CNTCR_OFF U(0x000) +#define CNTFID_OFF U(0x020) + +#define CNTCR_EN (U(1) << 0) +#define CNTCR_HDBG (U(1) << 1) +#define CNTCR_FCREQ(x) ((x) << 8) + +/******************************************************************************* + * System register bit definitions + ******************************************************************************/ +/* CLIDR definitions */ +#define LOUIS_SHIFT U(21) +#define LOC_SHIFT U(24) +#define CLIDR_FIELD_WIDTH U(3) + +/* CSSELR definitions */ +#define LEVEL_SHIFT U(1) + +/* Data cache set/way op type defines */ +#define DCISW U(0x0) +#define DCCISW U(0x1) +#if ERRATA_A53_827319 +#define DCCSW DCCISW +#else +#define DCCSW U(0x2) +#endif + +/* ID_AA64PFR0_EL1 definitions */ +#define ID_AA64PFR0_EL0_SHIFT U(0) +#define ID_AA64PFR0_EL1_SHIFT U(4) +#define ID_AA64PFR0_EL2_SHIFT U(8) +#define ID_AA64PFR0_EL3_SHIFT U(12) +#define ID_AA64PFR0_AMU_SHIFT U(44) +#define ID_AA64PFR0_AMU_LENGTH U(4) +#define ID_AA64PFR0_AMU_MASK ULL(0xf) +#define ID_AA64PFR0_ELX_MASK ULL(0xf) +#define ID_AA64PFR0_SVE_SHIFT U(32) +#define ID_AA64PFR0_SVE_MASK ULL(0xf) +#define ID_AA64PFR0_SVE_LENGTH U(4) +#define ID_AA64PFR0_MPAM_SHIFT U(40) +#define ID_AA64PFR0_MPAM_MASK ULL(0xf) +#define ID_AA64PFR0_DIT_SHIFT U(48) +#define ID_AA64PFR0_DIT_MASK ULL(0xf) +#define ID_AA64PFR0_DIT_LENGTH U(4) +#define ID_AA64PFR0_DIT_SUPPORTED U(1) +#define ID_AA64PFR0_CSV2_SHIFT U(56) +#define ID_AA64PFR0_CSV2_MASK ULL(0xf) +#define ID_AA64PFR0_CSV2_LENGTH U(4) + +/* ID_AA64DFR0_EL1.PMS definitions (for ARMv8.2+) */ +#define ID_AA64DFR0_PMS_SHIFT U(32) +#define ID_AA64DFR0_PMS_LENGTH U(4) +#define ID_AA64DFR0_PMS_MASK ULL(0xf) + +#define EL_IMPL_NONE ULL(0) +#define EL_IMPL_A64ONLY ULL(1) +#define EL_IMPL_A64_A32 ULL(2) + +#define ID_AA64PFR0_GIC_SHIFT U(24) +#define ID_AA64PFR0_GIC_WIDTH U(4) +#define ID_AA64PFR0_GIC_MASK ULL(0xf) + +/* ID_AA64ISAR1_EL1 definitions */ +#define ID_AA64ISAR1_EL1 S3_0_C0_C6_1 +#define ID_AA64ISAR1_GPI_SHIFT U(28) +#define ID_AA64ISAR1_GPI_WIDTH U(4) +#define ID_AA64ISAR1_GPI_MASK ULL(0xf) +#define ID_AA64ISAR1_GPA_SHIFT U(24) +#define ID_AA64ISAR1_GPA_WIDTH U(4) +#define ID_AA64ISAR1_GPA_MASK ULL(0xf) +#define ID_AA64ISAR1_API_SHIFT U(8) +#define ID_AA64ISAR1_API_WIDTH U(4) +#define ID_AA64ISAR1_API_MASK ULL(0xf) +#define ID_AA64ISAR1_APA_SHIFT U(4) +#define ID_AA64ISAR1_APA_WIDTH U(4) +#define ID_AA64ISAR1_APA_MASK ULL(0xf) + +/* ID_AA64MMFR0_EL1 definitions */ +#define ID_AA64MMFR0_EL1_PARANGE_SHIFT U(0) +#define ID_AA64MMFR0_EL1_PARANGE_MASK ULL(0xf) + +#define PARANGE_0000 U(32) +#define PARANGE_0001 U(36) +#define PARANGE_0010 U(40) +#define PARANGE_0011 U(42) +#define PARANGE_0100 U(44) +#define PARANGE_0101 U(48) +#define PARANGE_0110 U(52) + +#define ID_AA64MMFR0_EL1_TGRAN4_SHIFT U(28) +#define ID_AA64MMFR0_EL1_TGRAN4_MASK ULL(0xf) +#define ID_AA64MMFR0_EL1_TGRAN4_SUPPORTED ULL(0x0) +#define ID_AA64MMFR0_EL1_TGRAN4_NOT_SUPPORTED ULL(0xf) + +#define ID_AA64MMFR0_EL1_TGRAN64_SHIFT U(24) +#define ID_AA64MMFR0_EL1_TGRAN64_MASK ULL(0xf) +#define ID_AA64MMFR0_EL1_TGRAN64_SUPPORTED ULL(0x0) +#define ID_AA64MMFR0_EL1_TGRAN64_NOT_SUPPORTED ULL(0xf) + +#define ID_AA64MMFR0_EL1_TGRAN16_SHIFT U(20) +#define ID_AA64MMFR0_EL1_TGRAN16_MASK ULL(0xf) +#define ID_AA64MMFR0_EL1_TGRAN16_SUPPORTED ULL(0x1) +#define ID_AA64MMFR0_EL1_TGRAN16_NOT_SUPPORTED ULL(0x0) + +/* ID_AA64MMFR2_EL1 definitions */ +#define ID_AA64MMFR2_EL1 S3_0_C0_C7_2 + +#define ID_AA64MMFR2_EL1_ST_SHIFT U(28) +#define ID_AA64MMFR2_EL1_ST_MASK ULL(0xf) + +#define ID_AA64MMFR2_EL1_CNP_SHIFT U(0) +#define ID_AA64MMFR2_EL1_CNP_MASK ULL(0xf) + +/* ID_AA64PFR1_EL1 definitions */ +#define ID_AA64PFR1_EL1_SSBS_SHIFT U(4) +#define ID_AA64PFR1_EL1_SSBS_MASK ULL(0xf) + +#define SSBS_UNAVAILABLE ULL(0) /* No architectural SSBS support */ + +/* ID_PFR1_EL1 definitions */ +#define ID_PFR1_VIRTEXT_SHIFT U(12) +#define ID_PFR1_VIRTEXT_MASK U(0xf) +#define GET_VIRT_EXT(id) (((id) >> ID_PFR1_VIRTEXT_SHIFT) \ + & ID_PFR1_VIRTEXT_MASK) + +/* SCTLR definitions */ +#define SCTLR_EL2_RES1 ((U(1) << 29) | (U(1) << 28) | (U(1) << 23) | \ + (U(1) << 22) | (U(1) << 18) | (U(1) << 16) | \ + (U(1) << 11) | (U(1) << 5) | (U(1) << 4)) + +#define SCTLR_EL1_RES1 ((U(1) << 29) | (U(1) << 28) | (U(1) << 23) | \ + (U(1) << 22) | (U(1) << 20) | (U(1) << 11)) +#define SCTLR_AARCH32_EL1_RES1 \ + ((U(1) << 23) | (U(1) << 22) | (U(1) << 11) | \ + (U(1) << 4) | (U(1) << 3)) + +#define SCTLR_EL3_RES1 ((U(1) << 29) | (U(1) << 28) | (U(1) << 23) | \ + (U(1) << 22) | (U(1) << 18) | (U(1) << 16) | \ + (U(1) << 11) | (U(1) << 5) | (U(1) << 4)) + +#define SCTLR_M_BIT (ULL(1) << 0) +#define SCTLR_A_BIT (ULL(1) << 1) +#define SCTLR_C_BIT (ULL(1) << 2) +#define SCTLR_SA_BIT (ULL(1) << 3) +#define SCTLR_SA0_BIT (ULL(1) << 4) +#define SCTLR_CP15BEN_BIT (ULL(1) << 5) +#define SCTLR_ITD_BIT (ULL(1) << 7) +#define SCTLR_SED_BIT (ULL(1) << 8) +#define SCTLR_UMA_BIT (ULL(1) << 9) +#define SCTLR_I_BIT (ULL(1) << 12) +#define SCTLR_V_BIT (ULL(1) << 13) +#define SCTLR_DZE_BIT (ULL(1) << 14) +#define SCTLR_UCT_BIT (ULL(1) << 15) +#define SCTLR_NTWI_BIT (ULL(1) << 16) +#define SCTLR_NTWE_BIT (ULL(1) << 18) +#define SCTLR_WXN_BIT (ULL(1) << 19) +#define SCTLR_UWXN_BIT (ULL(1) << 20) +#define SCTLR_IESB_BIT (ULL(1) << 21) +#define SCTLR_E0E_BIT (ULL(1) << 24) +#define SCTLR_EE_BIT (ULL(1) << 25) +#define SCTLR_UCI_BIT (ULL(1) << 26) +#define SCTLR_EnIA_BIT (ULL(1) << 31) +#define SCTLR_DSSBS_BIT (ULL(1) << 44) +#define SCTLR_RESET_VAL SCTLR_EL3_RES1 + +/* CPACR_El1 definitions */ +#define CPACR_EL1_FPEN(x) ((x) << 20) +#define CPACR_EL1_FP_TRAP_EL0 U(0x1) +#define CPACR_EL1_FP_TRAP_ALL U(0x2) +#define CPACR_EL1_FP_TRAP_NONE U(0x3) + +/* SCR definitions */ +#define SCR_RES1_BITS ((U(1) << 4) | (U(1) << 5)) +#define SCR_FIEN_BIT (U(1) << 21) +#define SCR_API_BIT (U(1) << 17) +#define SCR_APK_BIT (U(1) << 16) +#define SCR_TWE_BIT (U(1) << 13) +#define SCR_TWI_BIT (U(1) << 12) +#define SCR_ST_BIT (U(1) << 11) +#define SCR_RW_BIT (U(1) << 10) +#define SCR_SIF_BIT (U(1) << 9) +#define SCR_HCE_BIT (U(1) << 8) +#define SCR_SMD_BIT (U(1) << 7) +#define SCR_EA_BIT (U(1) << 3) +#define SCR_FIQ_BIT (U(1) << 2) +#define SCR_IRQ_BIT (U(1) << 1) +#define SCR_NS_BIT (U(1) << 0) +#define SCR_VALID_BIT_MASK U(0x2f8f) +#define SCR_RESET_VAL SCR_RES1_BITS + +/* MDCR_EL3 definitions */ +#define MDCR_SPD32(x) ((x) << 14) +#define MDCR_SPD32_LEGACY ULL(0x0) +#define MDCR_SPD32_DISABLE ULL(0x2) +#define MDCR_SPD32_ENABLE ULL(0x3) +#define MDCR_SDD_BIT (ULL(1) << 16) +#define MDCR_NSPB(x) ((x) << 12) +#define MDCR_NSPB_EL1 ULL(0x3) +#define MDCR_TDOSA_BIT (ULL(1) << 10) +#define MDCR_TDA_BIT (ULL(1) << 9) +#define MDCR_TPM_BIT (ULL(1) << 6) +#define MDCR_SCCD_BIT (ULL(1) << 23) +#define MDCR_EL3_RESET_VAL ULL(0x0) + +/* MDCR_EL2 definitions */ +#define MDCR_EL2_TPMS (U(1) << 14) +#define MDCR_EL2_E2PB(x) ((x) << 12) +#define MDCR_EL2_E2PB_EL1 U(0x3) +#define MDCR_EL2_TDRA_BIT (U(1) << 11) +#define MDCR_EL2_TDOSA_BIT (U(1) << 10) +#define MDCR_EL2_TDA_BIT (U(1) << 9) +#define MDCR_EL2_TDE_BIT (U(1) << 8) +#define MDCR_EL2_HPME_BIT (U(1) << 7) +#define MDCR_EL2_TPM_BIT (U(1) << 6) +#define MDCR_EL2_TPMCR_BIT (U(1) << 5) +#define MDCR_EL2_RESET_VAL U(0x0) + +/* HSTR_EL2 definitions */ +#define HSTR_EL2_RESET_VAL U(0x0) +#define HSTR_EL2_T_MASK U(0xff) + +/* CNTHP_CTL_EL2 definitions */ +#define CNTHP_CTL_ENABLE_BIT (U(1) << 0) +#define CNTHP_CTL_RESET_VAL U(0x0) + +/* VTTBR_EL2 definitions */ +#define VTTBR_RESET_VAL ULL(0x0) +#define VTTBR_VMID_MASK ULL(0xff) +#define VTTBR_VMID_SHIFT U(48) +#define VTTBR_BADDR_MASK ULL(0xffffffffffff) +#define VTTBR_BADDR_SHIFT U(0) + +/* HCR definitions */ +#define HCR_API_BIT (ULL(1) << 41) +#define HCR_APK_BIT (ULL(1) << 40) +#define HCR_TGE_BIT (ULL(1) << 27) +#define HCR_RW_SHIFT U(31) +#define HCR_RW_BIT (ULL(1) << HCR_RW_SHIFT) +#define HCR_AMO_BIT (ULL(1) << 5) +#define HCR_IMO_BIT (ULL(1) << 4) +#define HCR_FMO_BIT (ULL(1) << 3) + +/* ISR definitions */ +#define ISR_A_SHIFT U(8) +#define ISR_I_SHIFT U(7) +#define ISR_F_SHIFT U(6) + +/* CNTHCTL_EL2 definitions */ +#define CNTHCTL_RESET_VAL U(0x0) +#define EVNTEN_BIT (U(1) << 2) +#define EL1PCEN_BIT (U(1) << 1) +#define EL1PCTEN_BIT (U(1) << 0) + +/* CNTKCTL_EL1 definitions */ +#define EL0PTEN_BIT (U(1) << 9) +#define EL0VTEN_BIT (U(1) << 8) +#define EL0PCTEN_BIT (U(1) << 0) +#define EL0VCTEN_BIT (U(1) << 1) +#define EVNTEN_BIT (U(1) << 2) +#define EVNTDIR_BIT (U(1) << 3) +#define EVNTI_SHIFT U(4) +#define EVNTI_MASK U(0xf) + +/* CPTR_EL3 definitions */ +#define TCPAC_BIT (U(1) << 31) +#define TAM_BIT (U(1) << 30) +#define TTA_BIT (U(1) << 20) +#define TFP_BIT (U(1) << 10) +#define CPTR_EZ_BIT (U(1) << 8) +#define CPTR_EL3_RESET_VAL U(0x0) + +/* CPTR_EL2 definitions */ +#define CPTR_EL2_RES1 ((U(1) << 13) | (U(1) << 12) | (U(0x3ff))) +#define CPTR_EL2_TCPAC_BIT (U(1) << 31) +#define CPTR_EL2_TAM_BIT (U(1) << 30) +#define CPTR_EL2_TTA_BIT (U(1) << 20) +#define CPTR_EL2_TFP_BIT (U(1) << 10) +#define CPTR_EL2_TZ_BIT (U(1) << 8) +#define CPTR_EL2_RESET_VAL CPTR_EL2_RES1 + +/* CPSR/SPSR definitions */ +#define DAIF_FIQ_BIT (U(1) << 0) +#define DAIF_IRQ_BIT (U(1) << 1) +#define DAIF_ABT_BIT (U(1) << 2) +#define DAIF_DBG_BIT (U(1) << 3) +#define SPSR_DAIF_SHIFT U(6) +#define SPSR_DAIF_MASK U(0xf) + +#define SPSR_AIF_SHIFT U(6) +#define SPSR_AIF_MASK U(0x7) + +#define SPSR_E_SHIFT U(9) +#define SPSR_E_MASK U(0x1) +#define SPSR_E_LITTLE U(0x0) +#define SPSR_E_BIG U(0x1) + +#define SPSR_T_SHIFT U(5) +#define SPSR_T_MASK U(0x1) +#define SPSR_T_ARM U(0x0) +#define SPSR_T_THUMB U(0x1) + +#define SPSR_M_SHIFT U(4) +#define SPSR_M_MASK U(0x1) +#define SPSR_M_AARCH64 U(0x0) +#define SPSR_M_AARCH32 U(0x1) + +#define DISABLE_ALL_EXCEPTIONS \ + (DAIF_FIQ_BIT | DAIF_IRQ_BIT | DAIF_ABT_BIT | DAIF_DBG_BIT) + +#define DISABLE_INTERRUPTS (DAIF_FIQ_BIT | DAIF_IRQ_BIT) + +/* + * RMR_EL3 definitions + */ +#define RMR_EL3_RR_BIT (U(1) << 1) +#define RMR_EL3_AA64_BIT (U(1) << 0) + +/* + * HI-VECTOR address for AArch32 state + */ +#define HI_VECTOR_BASE U(0xFFFF0000) + +/* + * TCR defintions + */ +#define TCR_EL3_RES1 ((ULL(1) << 31) | (ULL(1) << 23)) +#define TCR_EL2_RES1 ((ULL(1) << 31) | (ULL(1) << 23)) +#define TCR_EL1_IPS_SHIFT U(32) +#define TCR_EL2_PS_SHIFT U(16) +#define TCR_EL3_PS_SHIFT U(16) + +#define TCR_TxSZ_MIN ULL(16) +#define TCR_TxSZ_MAX ULL(39) +#define TCR_TxSZ_MAX_TTST ULL(48) + +/* (internal) physical address size bits in EL3/EL1 */ +#define TCR_PS_BITS_4GB ULL(0x0) +#define TCR_PS_BITS_64GB ULL(0x1) +#define TCR_PS_BITS_1TB ULL(0x2) +#define TCR_PS_BITS_4TB ULL(0x3) +#define TCR_PS_BITS_16TB ULL(0x4) +#define TCR_PS_BITS_256TB ULL(0x5) + +#define ADDR_MASK_48_TO_63 ULL(0xFFFF000000000000) +#define ADDR_MASK_44_TO_47 ULL(0x0000F00000000000) +#define ADDR_MASK_42_TO_43 ULL(0x00000C0000000000) +#define ADDR_MASK_40_TO_41 ULL(0x0000030000000000) +#define ADDR_MASK_36_TO_39 ULL(0x000000F000000000) +#define ADDR_MASK_32_TO_35 ULL(0x0000000F00000000) + +#define TCR_RGN_INNER_NC (ULL(0x0) << 8) +#define TCR_RGN_INNER_WBA (ULL(0x1) << 8) +#define TCR_RGN_INNER_WT (ULL(0x2) << 8) +#define TCR_RGN_INNER_WBNA (ULL(0x3) << 8) + +#define TCR_RGN_OUTER_NC (ULL(0x0) << 10) +#define TCR_RGN_OUTER_WBA (ULL(0x1) << 10) +#define TCR_RGN_OUTER_WT (ULL(0x2) << 10) +#define TCR_RGN_OUTER_WBNA (ULL(0x3) << 10) + +#define TCR_SH_NON_SHAREABLE (ULL(0x0) << 12) +#define TCR_SH_OUTER_SHAREABLE (ULL(0x2) << 12) +#define TCR_SH_INNER_SHAREABLE (ULL(0x3) << 12) + +#define TCR_TG0_SHIFT U(14) +#define TCR_TG0_MASK ULL(3) +#define TCR_TG0_4K (ULL(0) << TCR_TG0_SHIFT) +#define TCR_TG0_64K (ULL(1) << TCR_TG0_SHIFT) +#define TCR_TG0_16K (ULL(2) << TCR_TG0_SHIFT) + +#define TCR_EPD0_BIT (ULL(1) << 7) +#define TCR_EPD1_BIT (ULL(1) << 23) + +#define MODE_SP_SHIFT U(0x0) +#define MODE_SP_MASK U(0x1) +#define MODE_SP_EL0 U(0x0) +#define MODE_SP_ELX U(0x1) + +#define MODE_RW_SHIFT U(0x4) +#define MODE_RW_MASK U(0x1) +#define MODE_RW_64 U(0x0) +#define MODE_RW_32 U(0x1) + +#define MODE_EL_SHIFT U(0x2) +#define MODE_EL_MASK U(0x3) +#define MODE_EL3 U(0x3) +#define MODE_EL2 U(0x2) +#define MODE_EL1 U(0x1) +#define MODE_EL0 U(0x0) + +#define MODE32_SHIFT U(0) +#define MODE32_MASK U(0xf) +#define MODE32_usr U(0x0) +#define MODE32_fiq U(0x1) +#define MODE32_irq U(0x2) +#define MODE32_svc U(0x3) +#define MODE32_mon U(0x6) +#define MODE32_abt U(0x7) +#define MODE32_hyp U(0xa) +#define MODE32_und U(0xb) +#define MODE32_sys U(0xf) + +#define GET_RW(mode) (((mode) >> MODE_RW_SHIFT) & MODE_RW_MASK) +#define GET_EL(mode) (((mode) >> MODE_EL_SHIFT) & MODE_EL_MASK) +#define GET_SP(mode) (((mode) >> MODE_SP_SHIFT) & MODE_SP_MASK) +#define GET_M32(mode) (((mode) >> MODE32_SHIFT) & MODE32_MASK) + +#define SPSR_64(el, sp, daif) \ + ((MODE_RW_64 << MODE_RW_SHIFT) | \ + (((el) & MODE_EL_MASK) << MODE_EL_SHIFT) | \ + (((sp) & MODE_SP_MASK) << MODE_SP_SHIFT) | \ + (((daif) & SPSR_DAIF_MASK) << SPSR_DAIF_SHIFT)) + +#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)) + +/* + * TTBR Definitions + */ +#define TTBR_CNP_BIT ULL(0x1) + +/* + * CTR_EL0 definitions + */ +#define CTR_CWG_SHIFT U(24) +#define CTR_CWG_MASK U(0xf) +#define CTR_ERG_SHIFT U(20) +#define CTR_ERG_MASK U(0xf) +#define CTR_DMINLINE_SHIFT U(16) +#define CTR_DMINLINE_MASK U(0xf) +#define CTR_L1IP_SHIFT U(14) +#define CTR_L1IP_MASK U(0x3) +#define CTR_IMINLINE_SHIFT U(0) +#define CTR_IMINLINE_MASK U(0xf) + +#define MAX_CACHE_LINE_SIZE U(0x800) /* 2KB */ + +/* Physical timer control register bit fields shifts and masks */ +#define CNTP_CTL_ENABLE_SHIFT U(0) +#define CNTP_CTL_IMASK_SHIFT U(1) +#define CNTP_CTL_ISTATUS_SHIFT U(2) + +#define CNTP_CTL_ENABLE_MASK U(1) +#define CNTP_CTL_IMASK_MASK U(1) +#define CNTP_CTL_ISTATUS_MASK U(1) + +/* Exception Syndrome register bits and bobs */ +#define ESR_EC_SHIFT U(26) +#define ESR_EC_MASK U(0x3f) +#define ESR_EC_LENGTH U(6) +#define EC_UNKNOWN U(0x0) +#define EC_WFE_WFI U(0x1) +#define EC_AARCH32_CP15_MRC_MCR U(0x3) +#define EC_AARCH32_CP15_MRRC_MCRR U(0x4) +#define EC_AARCH32_CP14_MRC_MCR U(0x5) +#define EC_AARCH32_CP14_LDC_STC U(0x6) +#define EC_FP_SIMD U(0x7) +#define EC_AARCH32_CP10_MRC U(0x8) +#define EC_AARCH32_CP14_MRRC_MCRR U(0xc) +#define EC_ILLEGAL U(0xe) +#define EC_AARCH32_SVC U(0x11) +#define EC_AARCH32_HVC U(0x12) +#define EC_AARCH32_SMC U(0x13) +#define EC_AARCH64_SVC U(0x15) +#define EC_AARCH64_HVC U(0x16) +#define EC_AARCH64_SMC U(0x17) +#define EC_AARCH64_SYS U(0x18) +#define EC_IABORT_LOWER_EL U(0x20) +#define EC_IABORT_CUR_EL U(0x21) +#define EC_PC_ALIGN U(0x22) +#define EC_DABORT_LOWER_EL U(0x24) +#define EC_DABORT_CUR_EL U(0x25) +#define EC_SP_ALIGN U(0x26) +#define EC_AARCH32_FP U(0x28) +#define EC_AARCH64_FP U(0x2c) +#define EC_SERROR U(0x2f) + +/* + * External Abort bit in Instruction and Data Aborts synchronous exception + * syndromes. + */ +#define ESR_ISS_EABORT_EA_BIT U(9) + +#define EC_BITS(x) (((x) >> ESR_EC_SHIFT) & ESR_EC_MASK) + +/* Reset bit inside the Reset management register for EL3 (RMR_EL3) */ +#define RMR_RESET_REQUEST_SHIFT U(0x1) +#define RMR_WARM_RESET_CPU (U(1) << RMR_RESET_REQUEST_SHIFT) + +/******************************************************************************* + * Definitions of register offsets, fields and macros for CPU system + * instructions. + ******************************************************************************/ + +#define TLBI_ADDR_SHIFT U(12) +#define TLBI_ADDR_MASK ULL(0x00000FFFFFFFFFFF) +#define TLBI_ADDR(x) (((x) >> TLBI_ADDR_SHIFT) & TLBI_ADDR_MASK) + +/******************************************************************************* + * Definitions of register offsets and fields in the CNTCTLBase Frame of the + * system level implementation of the Generic Timer. + ******************************************************************************/ +#define CNTCTLBASE_CNTFRQ U(0x0) +#define CNTNSAR U(0x4) +#define CNTNSAR_NS_SHIFT(x) (x) + +#define CNTACR_BASE(x) (U(0x40) + ((x) << 2)) +#define CNTACR_RPCT_SHIFT U(0x0) +#define CNTACR_RVCT_SHIFT U(0x1) +#define CNTACR_RFRQ_SHIFT U(0x2) +#define CNTACR_RVOFF_SHIFT U(0x3) +#define CNTACR_RWVT_SHIFT U(0x4) +#define CNTACR_RWPT_SHIFT U(0x5) + +/******************************************************************************* + * Definitions of register offsets and fields in the CNTBaseN Frame of the + * system level implementation of the Generic Timer. + ******************************************************************************/ +/* Physical Count register. */ +#define CNTPCT_LO U(0x0) +/* Counter Frequency register. */ +#define CNTBASEN_CNTFRQ U(0x10) +/* Physical Timer CompareValue register. */ +#define CNTP_CVAL_LO U(0x20) +/* Physical Timer Control register. */ +#define CNTP_CTL U(0x2c) + +/* PMCR_EL0 definitions */ +#define PMCR_EL0_RESET_VAL U(0x0) +#define PMCR_EL0_N_SHIFT U(11) +#define PMCR_EL0_N_MASK U(0x1f) +#define PMCR_EL0_N_BITS (PMCR_EL0_N_MASK << PMCR_EL0_N_SHIFT) +#define PMCR_EL0_LC_BIT (U(1) << 6) +#define PMCR_EL0_DP_BIT (U(1) << 5) +#define PMCR_EL0_X_BIT (U(1) << 4) +#define PMCR_EL0_D_BIT (U(1) << 3) + +/******************************************************************************* + * Definitions for system register interface to SVE + ******************************************************************************/ +#define ZCR_EL3 S3_6_C1_C2_0 +#define ZCR_EL2 S3_4_C1_C2_0 + +/* ZCR_EL3 definitions */ +#define ZCR_EL3_LEN_MASK U(0xf) + +/* ZCR_EL2 definitions */ +#define ZCR_EL2_LEN_MASK U(0xf) + +/******************************************************************************* + * Definitions of MAIR encodings for device and normal memory + ******************************************************************************/ +/* + * MAIR encodings for device memory attributes. + */ +#define MAIR_DEV_nGnRnE ULL(0x0) +#define MAIR_DEV_nGnRE ULL(0x4) +#define MAIR_DEV_nGRE ULL(0x8) +#define MAIR_DEV_GRE ULL(0xc) + +/* + * MAIR encodings for normal memory attributes. + * + * Cache Policy + * WT: Write Through + * WB: Write Back + * NC: Non-Cacheable + * + * Transient Hint + * NTR: Non-Transient + * TR: Transient + * + * Allocation Policy + * RA: Read Allocate + * WA: Write Allocate + * RWA: Read and Write Allocate + * NA: No Allocation + */ +#define MAIR_NORM_WT_TR_WA ULL(0x1) +#define MAIR_NORM_WT_TR_RA ULL(0x2) +#define MAIR_NORM_WT_TR_RWA ULL(0x3) +#define MAIR_NORM_NC ULL(0x4) +#define MAIR_NORM_WB_TR_WA ULL(0x5) +#define MAIR_NORM_WB_TR_RA ULL(0x6) +#define MAIR_NORM_WB_TR_RWA ULL(0x7) +#define MAIR_NORM_WT_NTR_NA ULL(0x8) +#define MAIR_NORM_WT_NTR_WA ULL(0x9) +#define MAIR_NORM_WT_NTR_RA ULL(0xa) +#define MAIR_NORM_WT_NTR_RWA ULL(0xb) +#define MAIR_NORM_WB_NTR_NA ULL(0xc) +#define MAIR_NORM_WB_NTR_WA ULL(0xd) +#define MAIR_NORM_WB_NTR_RA ULL(0xe) +#define MAIR_NORM_WB_NTR_RWA ULL(0xf) + +#define MAIR_NORM_OUTER_SHIFT U(4) + +#define MAKE_MAIR_NORMAL_MEMORY(inner, outer) \ + ((inner) | ((outer) << MAIR_NORM_OUTER_SHIFT)) + +/* PAR_EL1 fields */ +#define PAR_F_SHIFT U(0) +#define PAR_F_MASK ULL(0x1) +#define PAR_ADDR_SHIFT U(12) +#define PAR_ADDR_MASK (BIT(40) - ULL(1)) /* 40-bits-wide page address */ + +/******************************************************************************* + * Definitions for system register interface to SPE + ******************************************************************************/ +#define PMBLIMITR_EL1 S3_0_C9_C10_0 + +/******************************************************************************* + * Definitions for system register interface to MPAM + ******************************************************************************/ +#define MPAMIDR_EL1 S3_0_C10_C4_4 +#define MPAM2_EL2 S3_4_C10_C5_0 +#define MPAMHCR_EL2 S3_4_C10_C4_0 +#define MPAM3_EL3 S3_6_C10_C5_0 + +/******************************************************************************* + * Definitions for system register interface to AMU for ARMv8.4 onwards + ******************************************************************************/ +#define AMCR_EL0 S3_3_C13_C2_0 +#define AMCFGR_EL0 S3_3_C13_C2_1 +#define AMCGCR_EL0 S3_3_C13_C2_2 +#define AMUSERENR_EL0 S3_3_C13_C2_3 +#define AMCNTENCLR0_EL0 S3_3_C13_C2_4 +#define AMCNTENSET0_EL0 S3_3_C13_C2_5 +#define AMCNTENCLR1_EL0 S3_3_C13_C3_0 +#define AMCNTENSET1_EL0 S3_3_C13_C3_1 + +/* Activity Monitor Group 0 Event Counter Registers */ +#define AMEVCNTR00_EL0 S3_3_C13_C4_0 +#define AMEVCNTR01_EL0 S3_3_C13_C4_1 +#define AMEVCNTR02_EL0 S3_3_C13_C4_2 +#define AMEVCNTR03_EL0 S3_3_C13_C4_3 + +/* Activity Monitor Group 0 Event Type Registers */ +#define AMEVTYPER00_EL0 S3_3_C13_C6_0 +#define AMEVTYPER01_EL0 S3_3_C13_C6_1 +#define AMEVTYPER02_EL0 S3_3_C13_C6_2 +#define AMEVTYPER03_EL0 S3_3_C13_C6_3 + +/* Activity Monitor Group 1 Event Counter Registers */ +#define AMEVCNTR10_EL0 S3_3_C13_C12_0 +#define AMEVCNTR11_EL0 S3_3_C13_C12_1 +#define AMEVCNTR12_EL0 S3_3_C13_C12_2 +#define AMEVCNTR13_EL0 S3_3_C13_C12_3 +#define AMEVCNTR14_EL0 S3_3_C13_C12_4 +#define AMEVCNTR15_EL0 S3_3_C13_C12_5 +#define AMEVCNTR16_EL0 S3_3_C13_C12_6 +#define AMEVCNTR17_EL0 S3_3_C13_C12_7 +#define AMEVCNTR18_EL0 S3_3_C13_C13_0 +#define AMEVCNTR19_EL0 S3_3_C13_C13_1 +#define AMEVCNTR1A_EL0 S3_3_C13_C13_2 +#define AMEVCNTR1B_EL0 S3_3_C13_C13_3 +#define AMEVCNTR1C_EL0 S3_3_C13_C13_4 +#define AMEVCNTR1D_EL0 S3_3_C13_C13_5 +#define AMEVCNTR1E_EL0 S3_3_C13_C13_6 +#define AMEVCNTR1F_EL0 S3_3_C13_C13_7 + +/* Activity Monitor Group 1 Event Type Registers */ +#define AMEVTYPER10_EL0 S3_3_C13_C14_0 +#define AMEVTYPER11_EL0 S3_3_C13_C14_1 +#define AMEVTYPER12_EL0 S3_3_C13_C14_2 +#define AMEVTYPER13_EL0 S3_3_C13_C14_3 +#define AMEVTYPER14_EL0 S3_3_C13_C14_4 +#define AMEVTYPER15_EL0 S3_3_C13_C14_5 +#define AMEVTYPER16_EL0 S3_3_C13_C14_6 +#define AMEVTYPER17_EL0 S3_3_C13_C14_7 +#define AMEVTYPER18_EL0 S3_3_C13_C15_0 +#define AMEVTYPER19_EL0 S3_3_C13_C15_1 +#define AMEVTYPER1A_EL0 S3_3_C13_C15_2 +#define AMEVTYPER1B_EL0 S3_3_C13_C15_3 +#define AMEVTYPER1C_EL0 S3_3_C13_C15_4 +#define AMEVTYPER1D_EL0 S3_3_C13_C15_5 +#define AMEVTYPER1E_EL0 S3_3_C13_C15_6 +#define AMEVTYPER1F_EL0 S3_3_C13_C15_7 + +/* AMCGCR_EL0 definitions */ +#define AMCGCR_EL0_CG1NC_SHIFT U(8) +#define AMCGCR_EL0_CG1NC_LENGTH U(8) +#define AMCGCR_EL0_CG1NC_MASK U(0xff) + +/* MPAM register definitions */ +#define MPAM3_EL3_MPAMEN_BIT (ULL(1) << 63) +#define MPAMHCR_EL2_TRAP_MPAMIDR_EL1 (ULL(1) << 31) + +#define MPAM2_EL2_TRAPMPAM0EL1 (ULL(1) << 49) +#define MPAM2_EL2_TRAPMPAM1EL1 (ULL(1) << 48) + +#define MPAMIDR_HAS_HCR_BIT (ULL(1) << 17) + +/******************************************************************************* + * RAS system registers + ******************************************************************************/ +#define DISR_EL1 S3_0_C12_C1_1 +#define DISR_A_BIT U(31) + +#define ERRIDR_EL1 S3_0_C5_C3_0 +#define ERRIDR_MASK U(0xffff) + +#define ERRSELR_EL1 S3_0_C5_C3_1 + +/* System register access to Standard Error Record registers */ +#define ERXFR_EL1 S3_0_C5_C4_0 +#define ERXCTLR_EL1 S3_0_C5_C4_1 +#define ERXSTATUS_EL1 S3_0_C5_C4_2 +#define ERXADDR_EL1 S3_0_C5_C4_3 +#define ERXPFGF_EL1 S3_0_C5_C4_4 +#define ERXPFGCTL_EL1 S3_0_C5_C4_5 +#define ERXPFGCDN_EL1 S3_0_C5_C4_6 +#define ERXMISC0_EL1 S3_0_C5_C5_0 +#define ERXMISC1_EL1 S3_0_C5_C5_1 + +#define ERXCTLR_ED_BIT (U(1) << 0) +#define ERXCTLR_UE_BIT (U(1) << 4) + +#define ERXPFGCTL_UC_BIT (U(1) << 1) +#define ERXPFGCTL_UEU_BIT (U(1) << 2) +#define ERXPFGCTL_CDEN_BIT (U(1) << 31) + +/******************************************************************************* + * Armv8.3 Pointer Authentication Registers + ******************************************************************************/ +#define APIAKeyLo_EL1 S3_0_C2_C1_0 +#define APIAKeyHi_EL1 S3_0_C2_C1_1 +#define APIBKeyLo_EL1 S3_0_C2_C1_2 +#define APIBKeyHi_EL1 S3_0_C2_C1_3 +#define APDAKeyLo_EL1 S3_0_C2_C2_0 +#define APDAKeyHi_EL1 S3_0_C2_C2_1 +#define APDBKeyLo_EL1 S3_0_C2_C2_2 +#define APDBKeyHi_EL1 S3_0_C2_C2_3 +#define APGAKeyLo_EL1 S3_0_C2_C3_0 +#define APGAKeyHi_EL1 S3_0_C2_C3_1 + +/******************************************************************************* + * Armv8.4 Data Independent Timing Registers + ******************************************************************************/ +#define DIT S3_3_C4_C2_5 +#define DIT_BIT BIT(24) + +/******************************************************************************* + * Armv8.5 - new MSR encoding to directly access PSTATE.SSBS field + ******************************************************************************/ +#define SSBS S3_3_C4_C2_6 + +#endif /* ARCH_H */ diff --git a/arch/arm/armv8-a/include/arch_gic.h b/arch/arm/armv8-a/include/arch_gic.h new file mode 100644 index 000000000..b3da68012 --- /dev/null +++ b/arch/arm/armv8-a/include/arch_gic.h @@ -0,0 +1,135 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef ARMV8A_GIC_H +#define ARMV8A_GIC_H + +#include +#include + +/* Constants to categorise priorities */ +#define GIC_HIGHEST_SEC_PRIORITY 0x0 +#define GIC_LOWEST_SEC_PRIORITY 0x7f +#define GIC_HIGHEST_NS_PRIORITY 0x80 +#define GIC_LOWEST_NS_PRIORITY 0xfe /* 0xff would disable all interrupts */ + + +/******************************************************************************* + * GIC Distributor interface general definitions + ******************************************************************************/ +/* Constants to categorise interrupts */ +#define MIN_SGI_ID U(0) +#define MIN_SEC_SGI_ID U(8) +#define MIN_PPI_ID U(16) +#define MIN_SPI_ID U(32) +#define MAX_SPI_ID U(1019) + +/* Mask for the priority field common to all GIC interfaces */ +#define GIC_PRI_MASK U(0xff) + +/* Mask for the configuration field common to all GIC interfaces */ +#define GIC_CFG_MASK U(0x3) + +/******************************************************************************* + * GIC Distributor interface register offsets that are common to GICv2 + ******************************************************************************/ +#define GICD_CTLR U(0x0) +#define GICD_TYPER U(0x4) +#define GICD_IIDR U(0x8) +#define GICD_IGROUPR U(0x80) +#define GICD_ISENABLER U(0x100) +#define GICD_ICENABLER U(0x180) +#define GICD_ISPENDR U(0x200) +#define GICD_ICPENDR U(0x280) +#define GICD_ISACTIVER U(0x300) +#define GICD_ICACTIVER U(0x380) +#define GICD_IPRIORITYR U(0x400) +#define GICD_ITARGETSR U(0x800) +#define GICD_ICFGR U(0xc00) +#define GICD_NSACR U(0xe00) + +/* GICD_CTLR bit definitions */ +#define CTLR_ENABLE_G0_SHIFT 0 +#define CTLR_ENABLE_G0_MASK U(0x1) +#define CTLR_ENABLE_G0_BIT BIT_32(CTLR_ENABLE_G0_SHIFT) +#define CTLR_ENABLE_G1_SHIFT 1 +#define CTLR_ENABLE_G1_MASK U(0x1) +#define CTLR_ENABLE_G1_BIT BIT_32(CTLR_ENABLE_G1_SHIFT) + +/******************************************************************************* + * GICv2 specific CPU interface register offsets and constants. + ******************************************************************************/ +/* Physical CPU Interface registers */ +#define GICC_CTLR U(0x0) +#define GICC_PMR U(0x4) +#define GICC_BPR U(0x8) +#define GICC_IAR U(0xC) +#define GICC_EOIR U(0x10) +#define GICC_RPR U(0x14) +#define GICC_HPPIR U(0x18) +#define GICC_AHPPIR U(0x28) +#define GICC_IIDR U(0xFC) +#define GICC_DIR U(0x1000) +#define GICC_PRIODROP GICC_EOIR + +/* Common CPU Interface definitions */ +#define INT_ID_MASK U(0x3ff) +#define INT_ID(n) (n & INT_ID_MASK) + +/* GICC_CTLR bit definitions */ +#define EOI_MODE_NS (U(1) << 10) +#define EOI_MODE_S (U(1) << 9) +#define IRQ_BYP_DIS_GRP1 (U(1) << 8) +#define FIQ_BYP_DIS_GRP1 (U(1) << 7) +#define IRQ_BYP_DIS_GRP0 (U(1) << 6) +#define FIQ_BYP_DIS_GRP0 (U(1) << 5) +#define CBPR (U(1) << 4) +#define FIQ_EN (U(1) << 3) +#define ACK_CTL (U(1) << 2) +#define ENABLE_G1 (U(1) << 1) +#define ENABLE_G0 (U(1) << 0) +#define FIQ_EN_SHIFT 3 +#define FIQ_EN_BIT BIT_32(FIQ_EN_SHIFT) + +/******************************************************************************* + * GIC Distributor interface register constants that are common to GICv3 & GICv2 + ******************************************************************************/ +#define PIDR2_ARCH_REV_SHIFT 4 +#define PIDR2_ARCH_REV_MASK U(0xf) + +/* GICv3 revision as reported by the PIDR2 register */ +#define ARCH_REV_GICV3 U(0x3) +/* GICv2 revision as reported by the PIDR2 register */ +#define ARCH_REV_GICV2 U(0x2) +/* GICv1 revision as reported by the PIDR2 register */ +#define ARCH_REV_GICV1 U(0x1) + +#define IGROUPR_SHIFT 5 +#define ISENABLER_SHIFT 5 +#define ICENABLER_SHIFT ISENABLER_SHIFT +#define ISPENDR_SHIFT 5 +#define ICPENDR_SHIFT ISPENDR_SHIFT +#define ISACTIVER_SHIFT 5 +#define ICACTIVER_SHIFT ISACTIVER_SHIFT +#define IPRIORITYR_SHIFT 2 +#define ITARGETSR_SHIFT 2 +#define ICFGR_SHIFT 4 +#define NSACR_SHIFT 4 + + +/* GIC */ +#define RCAR_GICD_BASE U(0xF1010000) +#define RCAR_GICR_BASE U(0xF1010000) +#define RCAR_GICC_BASE U(0xF1020000) +#define RCAR_GICH_BASE U(0xF1040000) +#define RCAR_GICV_BASE U(0xF1060000) + +void gic_init(void); +int arm_gic_init(const struct fwk_arch_interrupt_driver **driver); +void irq_global(uint32_t iid); + +#endif /* ARMV8A_GIC_H */ diff --git a/arch/arm/armv8-a/include/arch_helpers.h b/arch/arm/armv8-a/include/arch_helpers.h new file mode 100644 index 000000000..1e3673c89 --- /dev/null +++ b/arch/arm/armv8-a/include/arch_helpers.h @@ -0,0 +1,534 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2013-2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef ARCH_HELPERS_H +#define ARCH_HELPERS_H + +#include +#include +#include +#include +#include + +/********************************************************************** + * Macros which create inline functions to read or write CPU system + * registers + *********************************************************************/ + +#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 SYSREG_WRITE_CONST(reg_name, v) \ + __asm__ volatile ("msr " #reg_name ", %0" : : "i" (v)) + +/* Define read function for system register */ +#define DEFINE_SYSREG_READ_FUNC(_name) \ + _DEFINE_SYSREG_READ_FUNC(_name, _name) + +/* 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) + +/* Define read & write function for renamed system register */ +#define DEFINE_RENAME_SYSREG_RW_FUNCS(_name, _reg_name) \ + _DEFINE_SYSREG_READ_FUNC(_name, _reg_name) \ + _DEFINE_SYSREG_WRITE_FUNC(_name, _reg_name) + +/* Define read function for renamed system register */ +#define DEFINE_RENAME_SYSREG_READ_FUNC(_name, _reg_name) \ + _DEFINE_SYSREG_READ_FUNC(_name, _reg_name) + +/* Define write function for renamed system register */ +#define DEFINE_RENAME_SYSREG_WRITE_FUNC(_name, _reg_name) \ + _DEFINE_SYSREG_WRITE_FUNC(_name, _reg_name) + +/********************************************************************** + * 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(uint64_t v) \ +{ \ + __asm__ (#_op " " #_type ", %0" : : "r" (v)); \ +} + +/******************************************************************************* + * TLB maintenance accessor prototypes + ******************************************************************************/ + +#if ERRATA_A57_813419 +/* + * Define function for TLBI instruction with type specifier that implements + * the workaround for errata 813419 of Cortex-A57. + */ +#define DEFINE_TLBIOP_ERRATA_A57_813419_TYPE_FUNC(_type)\ +static inline void tlbi ## _type(void) \ +{ \ + __asm__("tlbi " #_type "\n" \ + "dsb ish\n" \ + "tlbi " #_type); \ +} + +/* + * Define function for TLBI instruction with register parameter that implements + * the workaround for errata 813419 of Cortex-A57. + */ +#define DEFINE_TLBIOP_ERRATA_A57_813419_TYPE_PARAM_FUNC(_type) \ +static inline void tlbi ## _type(uint64_t v) \ +{ \ + __asm__("tlbi " #_type ", %0\n" \ + "dsb ish\n" \ + "tlbi " #_type ", %0" : : "r" (v)); \ +} +#endif /* ERRATA_A57_813419 */ + +#if ERRATA_A53_819472 || ERRATA_A53_824069 || ERRATA_A53_827319 +/* + * Define function for DC instruction with register parameter that enables + * the workaround for errata 819472, 824069 and 827319 of Cortex-A53. + */ +#define DEFINE_DCOP_ERRATA_A53_TYPE_PARAM_FUNC(_name, _type) \ +static inline void dc ## _name(uint64_t v) \ +{ \ + __asm__("dc " #_type ", %0" : : "r" (v)); \ +} +#endif /* ERRATA_A53_819472 || ERRATA_A53_824069 || ERRATA_A53_827319 */ + +DEFINE_SYSOP_TYPE_FUNC(tlbi, alle1) +DEFINE_SYSOP_TYPE_FUNC(tlbi, alle1is) +DEFINE_SYSOP_TYPE_FUNC(tlbi, alle2) +DEFINE_SYSOP_TYPE_FUNC(tlbi, alle2is) +#if ERRATA_A57_813419 +DEFINE_TLBIOP_ERRATA_A57_813419_TYPE_FUNC(alle3) +DEFINE_TLBIOP_ERRATA_A57_813419_TYPE_FUNC(alle3is) +#else +DEFINE_SYSOP_TYPE_FUNC(tlbi, alle3) +DEFINE_SYSOP_TYPE_FUNC(tlbi, alle3is) +#endif +DEFINE_SYSOP_TYPE_FUNC(tlbi, vmalle1) + +DEFINE_SYSOP_TYPE_PARAM_FUNC(tlbi, vaae1is) +DEFINE_SYSOP_TYPE_PARAM_FUNC(tlbi, vaale1is) +DEFINE_SYSOP_TYPE_PARAM_FUNC(tlbi, vae2is) +DEFINE_SYSOP_TYPE_PARAM_FUNC(tlbi, vale2is) +#if ERRATA_A57_813419 +DEFINE_TLBIOP_ERRATA_A57_813419_TYPE_PARAM_FUNC(vae3is) +DEFINE_TLBIOP_ERRATA_A57_813419_TYPE_PARAM_FUNC(vale3is) +#else +DEFINE_SYSOP_TYPE_PARAM_FUNC(tlbi, vae3is) +DEFINE_SYSOP_TYPE_PARAM_FUNC(tlbi, vale3is) +#endif + +/******************************************************************************* + * Cache maintenance accessor prototypes + ******************************************************************************/ +DEFINE_SYSOP_TYPE_PARAM_FUNC(dc, isw) +DEFINE_SYSOP_TYPE_PARAM_FUNC(dc, cisw) +#if ERRATA_A53_827319 +DEFINE_DCOP_ERRATA_A53_TYPE_PARAM_FUNC(csw, cisw) +#else +DEFINE_SYSOP_TYPE_PARAM_FUNC(dc, csw) +#endif +#if ERRATA_A53_819472 || ERRATA_A53_824069 || ERRATA_A53_827319 +DEFINE_DCOP_ERRATA_A53_TYPE_PARAM_FUNC(cvac, civac) +#else +DEFINE_SYSOP_TYPE_PARAM_FUNC(dc, cvac) +#endif +DEFINE_SYSOP_TYPE_PARAM_FUNC(dc, ivac) +DEFINE_SYSOP_TYPE_PARAM_FUNC(dc, civac) +#if ERRATA_A53_819472 || ERRATA_A53_824069 || ERRATA_A53_827319 +DEFINE_DCOP_ERRATA_A53_TYPE_PARAM_FUNC(cvau, civac) +#else +DEFINE_SYSOP_TYPE_PARAM_FUNC(dc, cvau) +#endif +DEFINE_SYSOP_TYPE_PARAM_FUNC(dc, zva) + +/******************************************************************************* + * Address translation accessor prototypes + ******************************************************************************/ +DEFINE_SYSOP_TYPE_PARAM_FUNC(at, s12e1r) +DEFINE_SYSOP_TYPE_PARAM_FUNC(at, s12e1w) +DEFINE_SYSOP_TYPE_PARAM_FUNC(at, s12e0r) +DEFINE_SYSOP_TYPE_PARAM_FUNC(at, s12e0w) +DEFINE_SYSOP_TYPE_PARAM_FUNC(at, s1e1r) +DEFINE_SYSOP_TYPE_PARAM_FUNC(at, s1e2r) +DEFINE_SYSOP_TYPE_PARAM_FUNC(at, s1e3r) + +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); + +void dcsw_op_louis(u_register_t op_type); +void dcsw_op_all(u_register_t op_type); + +void disable_mmu_el1(void); +void disable_mmu_el3(void); +void disable_mmu_icache_el1(void); +void disable_mmu_icache_el3(void); + +/******************************************************************************* + * Misc. accessor prototypes + ******************************************************************************/ + +#define write_daifclr(val) SYSREG_WRITE_CONST(daifclr, val) +#define write_daifset(val) SYSREG_WRITE_CONST(daifset, val) + +DEFINE_SYSREG_RW_FUNCS(par_el1) +DEFINE_SYSREG_READ_FUNC(id_pfr1_el1) +DEFINE_SYSREG_READ_FUNC(id_aa64isar1_el1) +DEFINE_SYSREG_READ_FUNC(id_aa64pfr0_el1) +DEFINE_SYSREG_READ_FUNC(id_aa64pfr1_el1) +DEFINE_SYSREG_READ_FUNC(id_aa64dfr0_el1) +DEFINE_SYSREG_READ_FUNC(id_afr0_el1) +DEFINE_SYSREG_READ_FUNC(CurrentEl) +DEFINE_SYSREG_READ_FUNC(ctr_el0) +DEFINE_SYSREG_RW_FUNCS(daif) +DEFINE_SYSREG_RW_FUNCS(spsr_el1) +DEFINE_SYSREG_RW_FUNCS(spsr_el2) +DEFINE_SYSREG_RW_FUNCS(spsr_el3) +DEFINE_SYSREG_RW_FUNCS(elr_el1) +DEFINE_SYSREG_RW_FUNCS(elr_el2) +DEFINE_SYSREG_RW_FUNCS(elr_el3) + +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(dmb, st) +DEFINE_SYSOP_TYPE_FUNC(dmb, ld) +DEFINE_SYSOP_TYPE_FUNC(dsb, ish) +DEFINE_SYSOP_TYPE_FUNC(dsb, nsh) +DEFINE_SYSOP_TYPE_FUNC(dsb, ishst) +DEFINE_SYSOP_TYPE_FUNC(dmb, oshld) +DEFINE_SYSOP_TYPE_FUNC(dmb, oshst) +DEFINE_SYSOP_TYPE_FUNC(dmb, osh) +DEFINE_SYSOP_TYPE_FUNC(dmb, nshld) +DEFINE_SYSOP_TYPE_FUNC(dmb, nshst) +DEFINE_SYSOP_TYPE_FUNC(dmb, nsh) +DEFINE_SYSOP_TYPE_FUNC(dmb, ishld) +DEFINE_SYSOP_TYPE_FUNC(dmb, ishst) +DEFINE_SYSOP_TYPE_FUNC(dmb, ish) +DEFINE_SYSOP_FUNC(isb) + +static inline void enable_irq(void) +{ + /* + * The compiler memory barrier will prevent the compiler from + * scheduling non-volatile memory access after the write to the + * register. + * + * This could happen if some initialization code issues non-volatile + * accesses to an area used by an interrupt handler, in the assumption + * that it is safe as the interrupts are disabled at the time it does + * that (according to program order). However, non-volatile accesses + * are not necessarily in program order relatively with volatile inline + * assembly statements (and volatile accesses). + */ + COMPILER_BARRIER(); + write_daifclr(DAIF_IRQ_BIT); + isb(); +} + +static inline void enable_fiq(void) +{ + COMPILER_BARRIER(); + write_daifclr(DAIF_FIQ_BIT); + isb(); +} + +static inline void enable_serror(void) +{ + COMPILER_BARRIER(); + write_daifclr(DAIF_ABT_BIT); + isb(); +} + +static inline void enable_debug_exceptions(void) +{ + COMPILER_BARRIER(); + write_daifclr(DAIF_DBG_BIT); + isb(); +} + +static inline void disable_irq(void) +{ + COMPILER_BARRIER(); + write_daifset(DAIF_IRQ_BIT); + isb(); +} + +static inline void disable_fiq(void) +{ + COMPILER_BARRIER(); + write_daifset(DAIF_FIQ_BIT); + isb(); +} + +static inline void disable_serror(void) +{ + COMPILER_BARRIER(); + write_daifset(DAIF_ABT_BIT); + isb(); +} + +static inline void disable_debug_exceptions(void) +{ + COMPILER_BARRIER(); + write_daifset(DAIF_DBG_BIT); + isb(); +} + +#if !ERROR_DEPRECATED +uint32_t get_afflvl_shift(uint32_t); +uint32_t mpidr_mask_lower_afflvls(uint64_t, uint32_t); + +noreturn void eret(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, + uint64_t x4, uint64_t x5, uint64_t x6, uint64_t x7); +#endif +noreturn void smc(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, + uint64_t x4, uint64_t x5, uint64_t x6, uint64_t x7); + +/******************************************************************************* + * System register accessor prototypes + ******************************************************************************/ +DEFINE_SYSREG_READ_FUNC(midr_el1) +DEFINE_SYSREG_READ_FUNC(mpidr_el1) +DEFINE_SYSREG_READ_FUNC(id_aa64mmfr0_el1) + +DEFINE_SYSREG_RW_FUNCS(scr_el3) +DEFINE_SYSREG_RW_FUNCS(hcr_el2) + +DEFINE_SYSREG_RW_FUNCS(vbar_el1) +DEFINE_SYSREG_RW_FUNCS(vbar_el2) +DEFINE_SYSREG_RW_FUNCS(vbar_el3) + +DEFINE_SYSREG_RW_FUNCS(sctlr_el1) +DEFINE_SYSREG_RW_FUNCS(sctlr_el2) +DEFINE_SYSREG_RW_FUNCS(sctlr_el3) + +DEFINE_SYSREG_RW_FUNCS(actlr_el1) +DEFINE_SYSREG_RW_FUNCS(actlr_el2) +DEFINE_SYSREG_RW_FUNCS(actlr_el3) + +DEFINE_SYSREG_RW_FUNCS(esr_el1) +DEFINE_SYSREG_RW_FUNCS(esr_el2) +DEFINE_SYSREG_RW_FUNCS(esr_el3) + +DEFINE_SYSREG_RW_FUNCS(afsr0_el1) +DEFINE_SYSREG_RW_FUNCS(afsr0_el2) +DEFINE_SYSREG_RW_FUNCS(afsr0_el3) + +DEFINE_SYSREG_RW_FUNCS(afsr1_el1) +DEFINE_SYSREG_RW_FUNCS(afsr1_el2) +DEFINE_SYSREG_RW_FUNCS(afsr1_el3) + +DEFINE_SYSREG_RW_FUNCS(far_el1) +DEFINE_SYSREG_RW_FUNCS(far_el2) +DEFINE_SYSREG_RW_FUNCS(far_el3) + +DEFINE_SYSREG_RW_FUNCS(mair_el1) +DEFINE_SYSREG_RW_FUNCS(mair_el2) +DEFINE_SYSREG_RW_FUNCS(mair_el3) + +DEFINE_SYSREG_RW_FUNCS(amair_el1) +DEFINE_SYSREG_RW_FUNCS(amair_el2) +DEFINE_SYSREG_RW_FUNCS(amair_el3) + +DEFINE_SYSREG_READ_FUNC(rvbar_el1) +DEFINE_SYSREG_READ_FUNC(rvbar_el2) +DEFINE_SYSREG_READ_FUNC(rvbar_el3) + +DEFINE_SYSREG_RW_FUNCS(rmr_el1) +DEFINE_SYSREG_RW_FUNCS(rmr_el2) +DEFINE_SYSREG_RW_FUNCS(rmr_el3) + +DEFINE_SYSREG_RW_FUNCS(tcr_el1) +DEFINE_SYSREG_RW_FUNCS(tcr_el2) +DEFINE_SYSREG_RW_FUNCS(tcr_el3) + +DEFINE_SYSREG_RW_FUNCS(ttbr0_el1) +DEFINE_SYSREG_RW_FUNCS(ttbr0_el2) +DEFINE_SYSREG_RW_FUNCS(ttbr0_el3) + +DEFINE_SYSREG_RW_FUNCS(ttbr1_el1) + +DEFINE_SYSREG_RW_FUNCS(vttbr_el2) + +DEFINE_SYSREG_RW_FUNCS(cptr_el2) +DEFINE_SYSREG_RW_FUNCS(cptr_el3) + +DEFINE_SYSREG_RW_FUNCS(cpacr_el1) +DEFINE_SYSREG_RW_FUNCS(cntfrq_el0) +DEFINE_SYSREG_RW_FUNCS(cnthp_ctl_el2) +DEFINE_SYSREG_RW_FUNCS(cnthp_tval_el2) +DEFINE_SYSREG_RW_FUNCS(cnthp_cval_el2) +DEFINE_SYSREG_RW_FUNCS(cntps_ctl_el1) +DEFINE_SYSREG_RW_FUNCS(cntps_tval_el1) +DEFINE_SYSREG_RW_FUNCS(cntps_cval_el1) +DEFINE_SYSREG_RW_FUNCS(cntp_ctl_el0) +DEFINE_SYSREG_RW_FUNCS(cntp_tval_el0) +DEFINE_SYSREG_RW_FUNCS(cntp_cval_el0) +DEFINE_SYSREG_READ_FUNC(cntpct_el0) +DEFINE_SYSREG_RW_FUNCS(cnthctl_el2) + +#define get_cntp_ctl_enable(x) (((x) >> CNTP_CTL_ENABLE_SHIFT) & \ + CNTP_CTL_ENABLE_MASK) +#define get_cntp_ctl_imask(x) (((x) >> CNTP_CTL_IMASK_SHIFT) & \ + CNTP_CTL_IMASK_MASK) +#define get_cntp_ctl_istatus(x) (((x) >> CNTP_CTL_ISTATUS_SHIFT) & \ + CNTP_CTL_ISTATUS_MASK) + +#define set_cntp_ctl_enable(x) ((x) |= (U(1) << CNTP_CTL_ENABLE_SHIFT)) +#define set_cntp_ctl_imask(x) ((x) |= (U(1) << CNTP_CTL_IMASK_SHIFT)) + +#define clr_cntp_ctl_enable(x) ((x) &= ~(U(1) << CNTP_CTL_ENABLE_SHIFT)) +#define clr_cntp_ctl_imask(x) ((x) &= ~(U(1) << CNTP_CTL_IMASK_SHIFT)) + +DEFINE_SYSREG_RW_FUNCS(tpidr_el3) + +DEFINE_SYSREG_RW_FUNCS(cntvoff_el2) + +DEFINE_SYSREG_RW_FUNCS(vpidr_el2) +DEFINE_SYSREG_RW_FUNCS(vmpidr_el2) + +DEFINE_SYSREG_READ_FUNC(isr_el1) + +DEFINE_SYSREG_RW_FUNCS(mdcr_el2) +DEFINE_SYSREG_RW_FUNCS(mdcr_el3) +DEFINE_SYSREG_RW_FUNCS(hstr_el2) +DEFINE_SYSREG_RW_FUNCS(pmcr_el0) + +/* GICv3 System Registers */ + +DEFINE_RENAME_SYSREG_RW_FUNCS(icc_sre_el1, ICC_SRE_EL1) +DEFINE_RENAME_SYSREG_RW_FUNCS(icc_sre_el2, ICC_SRE_EL2) +DEFINE_RENAME_SYSREG_RW_FUNCS(icc_sre_el3, ICC_SRE_EL3) +DEFINE_RENAME_SYSREG_RW_FUNCS(icc_pmr_el1, ICC_PMR_EL1) +DEFINE_RENAME_SYSREG_READ_FUNC(icc_rpr_el1, ICC_RPR_EL1) +DEFINE_RENAME_SYSREG_RW_FUNCS(icc_igrpen1_el3, ICC_IGRPEN1_EL3) +DEFINE_RENAME_SYSREG_RW_FUNCS(icc_igrpen1_el1, ICC_IGRPEN1_EL1) +DEFINE_RENAME_SYSREG_RW_FUNCS(icc_igrpen0_el1, ICC_IGRPEN0_EL1) +DEFINE_RENAME_SYSREG_READ_FUNC(icc_hppir0_el1, ICC_HPPIR0_EL1) +DEFINE_RENAME_SYSREG_READ_FUNC(icc_hppir1_el1, ICC_HPPIR1_EL1) +DEFINE_RENAME_SYSREG_READ_FUNC(icc_iar0_el1, ICC_IAR0_EL1) +DEFINE_RENAME_SYSREG_READ_FUNC(icc_iar1_el1, ICC_IAR1_EL1) +DEFINE_RENAME_SYSREG_WRITE_FUNC(icc_eoir0_el1, ICC_EOIR0_EL1) +DEFINE_RENAME_SYSREG_WRITE_FUNC(icc_eoir1_el1, ICC_EOIR1_EL1) +DEFINE_RENAME_SYSREG_WRITE_FUNC(icc_sgi0r_el1, ICC_SGI0R_EL1) +DEFINE_RENAME_SYSREG_RW_FUNCS(icc_sgi1r, ICC_SGI1R) + +DEFINE_RENAME_SYSREG_RW_FUNCS(amcgcr_el0, AMCGCR_EL0) +DEFINE_RENAME_SYSREG_RW_FUNCS(amcntenclr0_el0, AMCNTENCLR0_EL0) +DEFINE_RENAME_SYSREG_RW_FUNCS(amcntenset0_el0, AMCNTENSET0_EL0) +DEFINE_RENAME_SYSREG_RW_FUNCS(amcntenclr1_el0, AMCNTENCLR1_EL0) +DEFINE_RENAME_SYSREG_RW_FUNCS(amcntenset1_el0, AMCNTENSET1_EL0) + +DEFINE_RENAME_SYSREG_READ_FUNC(mpamidr_el1, MPAMIDR_EL1) +DEFINE_RENAME_SYSREG_RW_FUNCS(mpam3_el3, MPAM3_EL3) +DEFINE_RENAME_SYSREG_RW_FUNCS(mpam2_el2, MPAM2_EL2) +DEFINE_RENAME_SYSREG_RW_FUNCS(mpamhcr_el2, MPAMHCR_EL2) + +DEFINE_RENAME_SYSREG_RW_FUNCS(pmblimitr_el1, PMBLIMITR_EL1) + +DEFINE_RENAME_SYSREG_WRITE_FUNC(zcr_el3, ZCR_EL3) +DEFINE_RENAME_SYSREG_WRITE_FUNC(zcr_el2, ZCR_EL2) + +DEFINE_RENAME_SYSREG_READ_FUNC(erridr_el1, ERRIDR_EL1) +DEFINE_RENAME_SYSREG_WRITE_FUNC(errselr_el1, ERRSELR_EL1) + +DEFINE_RENAME_SYSREG_READ_FUNC(erxfr_el1, ERXFR_EL1) +DEFINE_RENAME_SYSREG_RW_FUNCS(erxctlr_el1, ERXCTLR_EL1) +DEFINE_RENAME_SYSREG_RW_FUNCS(erxstatus_el1, ERXSTATUS_EL1) +DEFINE_RENAME_SYSREG_READ_FUNC(erxaddr_el1, ERXADDR_EL1) +DEFINE_RENAME_SYSREG_READ_FUNC(erxmisc0_el1, ERXMISC0_EL1) +DEFINE_RENAME_SYSREG_READ_FUNC(erxmisc1_el1, ERXMISC1_EL1) + +/* Armv8.2 Registers */ +DEFINE_RENAME_SYSREG_READ_FUNC(id_aa64mmfr2_el1, ID_AA64MMFR2_EL1) + +/* Armv8.3 Pointer Authentication Registers */ +DEFINE_RENAME_SYSREG_RW_FUNCS(apiakeyhi_el1, APIAKeyHi_EL1) +DEFINE_RENAME_SYSREG_RW_FUNCS(apiakeylo_el1, APIAKeyLo_EL1) + +#define IS_IN_EL(x) \ + (GET_EL(read_CurrentEl()) == MODE_EL##x) + +#define IS_IN_EL1() IS_IN_EL(1) +#define IS_IN_EL2() IS_IN_EL(2) +#define IS_IN_EL3() IS_IN_EL(3) + +static inline unsigned int get_current_el(void) +{ + return GET_EL(read_CurrentEl()); +} + +/* + * Check if an EL is implemented from AA64PFR0 register fields. + */ +static inline uint64_t el_implemented(unsigned int el) +{ + if (el > 3U) { + return EL_IMPL_NONE; + } else { + unsigned int shift = ID_AA64PFR0_EL1_SHIFT * el; + + return (read_id_aa64pfr0_el1() >> shift) & ID_AA64PFR0_ELX_MASK; + } +} + +#if !ERROR_DEPRECATED +#define EL_IMPLEMENTED(_el) el_implemented(_el) +#endif + +/* Previously defined accesor functions with incomplete register names */ + +#define read_current_el() read_CurrentEl() + +#define dsb() dsbsy() + +#define read_midr() read_midr_el1() + +#define read_mpidr() read_mpidr_el1() + +#define read_scr() read_scr_el3() +#define write_scr(_v) write_scr_el3(_v) + +#define read_hcr() read_hcr_el2() +#define write_hcr(_v) write_hcr_el2(_v) + +#define read_cpacr() read_cpacr_el1() +#define write_cpacr(_v) write_cpacr_el1(_v) + +#endif /* ARCH_HELPERS_H */ diff --git a/arch/arm/armv8-a/include/arch_system.h b/arch/arm/armv8-a/include/arch_system.h new file mode 100644 index 000000000..72692f1ad --- /dev/null +++ b/arch/arm/armv8-a/include/arch_system.h @@ -0,0 +1,26 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef ARMV8A_SYSTEM_H +#define ARMV8A_SYSTEM_H + +#define R_WARMBOOT (0xAA55AA55) +#define R_SUSPEND (0x55AA55AA) +#define R_RESET (0x5555AAAA) +#define R_OFF (0xAAAA5555) +#define R_CLEAR (0) + +#ifdef __ASSEMBLY__ +.extern _boot_flag +.extern _shutdown_request +#else +extern volatile uint32_t _boot_flag; +extern volatile uint32_t _shutdown_request; + +#endif /* __ASSEMBLY__ */ + +#endif /* ARMV8A_SYSTEM_H */ diff --git a/arch/arm/armv8-a/include/asm_macros.S b/arch/arm/armv8-a/include/asm_macros.S new file mode 100644 index 000000000..bd961c44e --- /dev/null +++ b/arch/arm/armv8-a/include/asm_macros.S @@ -0,0 +1,194 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2013-2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef ASM_MACROS_S +#define ASM_MACROS_S + +#include + +/* + * TLBI instruction with type specifier that implements the workaround for + * errata 813419 of Cortex-A57. + */ +#if ERRATA_A57_813419 +#define TLB_INVALIDATE(_type) \ + tlbi _type; \ + dsb ish; \ + tlbi _type +#else +#define TLB_INVALIDATE(_type) \ + tlbi _type +#endif + + + .macro func_prologue + stp x29, x30, [sp, #-0x10]! + mov x29,sp + .endm + + .macro func_epilogue + ldp x29, x30, [sp], #0x10 + .endm + + + .macro dcache_line_size reg, tmp + mrs \tmp, ctr_el0 + ubfx \tmp, \tmp, #16, #4 + mov \reg, #4 + lsl \reg, \reg, \tmp + .endm + + + .macro icache_line_size reg, tmp + mrs \tmp, ctr_el0 + and \tmp, \tmp, #0xf + mov \reg, #4 + lsl \reg, \reg, \tmp + .endm + + + .macro smc_check label + mrs x0, esr_el3 + ubfx x0, x0, #ESR_EC_SHIFT, #ESR_EC_LENGTH + cmp x0, #EC_AARCH64_SMC + b.ne $label + .endm + + /* + * Declare the exception vector table, enforcing it is aligned on a + * 2KB boundary, as required by the ARMv8 architecture. + * Use zero bytes as the fill value to be stored in the padding bytes + * so that it inserts illegal AArch64 instructions. This increases + * security, robustness and potentially facilitates debugging. + */ + .macro vector_base label, section_name=.vectors + .section \section_name, "ax" + .align 11, 0 + \label: + .endm + + /* + * Create an entry in the exception vector table, enforcing it is + * aligned on a 128-byte boundary, as required by the ARMv8 architecture. + * Use zero bytes as the fill value to be stored in the padding bytes + * so that it inserts illegal AArch64 instructions. This increases + * security, robustness and potentially facilitates debugging. + */ + .macro vector_entry label, section_name=.vectors + .cfi_sections .debug_frame + .section \section_name, "ax" + .align 7, 0 + .type \label, %function + .cfi_startproc + \label: + .endm + + /* + * Add the bytes until fill the full exception vector, whose size is always + * 32 instructions. If there are more than 32 instructions in the + * exception vector then an error is emitted. + */ + .macro end_vector_entry label + .cfi_endproc + .fill \label + (32 * 4) - . + .endm + + /* + * This macro calculates the base address of the current CPU's MP stack + * using the plat_my_core_pos() index, the name of the stack storage + * and the size of each stack + * Out: X0 = physical address of stack base + * Clobber: X30, X1, X2 + */ + .macro get_my_mp_stack _name, _size + bl plat_my_core_pos + adrp x2, (\_name + \_size) + add x2, x2, :lo12:(\_name + \_size) + mov x1, #\_size + madd x0, x0, x1, x2 + .endm + + /* + * This macro calculates the base address of a UP stack using the + * name of the stack storage and the size of the stack + * Out: X0 = physical address of stack base + */ + .macro get_up_stack _name, _size + adrp x0, (\_name + \_size) + add x0, x0, :lo12:(\_name + \_size) + .endm + + /* + * Helper macro to generate the best mov/movk combinations according + * the value to be moved. The 16 bits from '_shift' are tested and + * if not zero, they are moved into '_reg' without affecting + * other bits. + */ + .macro _mov_imm16 _reg, _val, _shift + .if (\_val >> \_shift) & 0xffff + .if (\_val & (1 << \_shift - 1)) + movk \_reg, (\_val >> \_shift) & 0xffff, LSL \_shift + .else + mov \_reg, \_val & (0xffff << \_shift) + .endif + .endif + .endm + + /* + * Helper macro to load arbitrary values into 32 or 64-bit registers + * which generates the best mov/movk combinations. Many base addresses + * are 64KB aligned the macro will eliminate updating bits 15:0 in + * that case + */ + .macro mov_imm _reg, _val + .if (\_val) == 0 + mov \_reg, #0 + .else + _mov_imm16 \_reg, (\_val), 0 + _mov_imm16 \_reg, (\_val), 16 + _mov_imm16 \_reg, (\_val), 32 + _mov_imm16 \_reg, (\_val), 48 + .endif + .endm + + /* + * Macro to mark instances where we're jumping to a function and don't + * expect a return. To provide the function being jumped to with + * additional information, we use 'bl' instruction to jump rather than + * 'b'. + * + * Debuggers infer the location of a call from where LR points to, which + * is usually the instruction after 'bl'. If this macro expansion + * happens to be the last location in a function, that'll cause the LR + * to point a location beyond the function, thereby misleading debugger + * back trace. We therefore insert a 'nop' after the function call for + * debug builds, unless 'skip_nop' parameter is non-zero. + */ + .macro no_ret _func:req, skip_nop=0 + bl \_func +#if DEBUG + .ifeq \skip_nop + nop + .endif +#endif + .endm + + /* + * Reserve space for a spin lock in assembly file. + */ + .macro define_asm_spinlock _name:req + .align SPINLOCK_ASM_ALIGN + \_name: + .space SPINLOCK_ASM_SIZE + .endm + +#if RAS_EXTENSION + .macro esb + .inst 0xd503221f + .endm +#endif + +#endif /* ASM_MACROS_S */ diff --git a/arch/arm/armv8-a/include/assert_macros.S b/arch/arm/armv8-a/include/assert_macros.S new file mode 100644 index 000000000..800f26746 --- /dev/null +++ b/arch/arm/armv8-a/include/assert_macros.S @@ -0,0 +1,30 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2014-2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef ASSERT_MACROS_S +#define ASSERT_MACROS_S + + /* + * Assembler macro to enable asm_assert. Use this macro wherever + * assert is required in assembly. Please note that the macro makes + * use of label '300' to provide the logic and the caller + * should make sure that this label is not used to branch prior + * to calling 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 ;\ + adr x0, .L_assert_filename ;\ + mov x1, __LINE__ ;\ + b asm_assert ;\ +300: + +#endif /* ASSERT_MACROS_S */ diff --git a/arch/arm/armv8-a/include/common/asm_macros_common.S b/arch/arm/armv8-a/include/common/asm_macros_common.S new file mode 100644 index 000000000..ed2b71589 --- /dev/null +++ b/arch/arm/armv8-a/include/common/asm_macros_common.S @@ -0,0 +1,108 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2013-2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#ifndef ASM_MACROS_COMMON_S +#define ASM_MACROS_COMMON_S + + /* + * This macro is used to create a function label and place the + * code into a separate text section based on the function name + * to enable elimination of unused code during linking. It also adds + * basic debug information to enable call stack printing most of the + * time. The optional _align parameter can be used to force a + * non-standard alignment (indicated in powers of 2). The default is + * _align=2 because both Aarch32 and Aarch64 instructions must be + * word aligned. Do *not* try to use a raw .align directive. Since func + * switches to a new section, this would not have the desired effect. + */ + .macro func _name, _align=2 + /* + * Add Call Frame Information entry in the .debug_frame section for + * debugger consumption. This enables callstack printing in debuggers. + * This does not use any space in the final loaded binary, only in the + * ELF file. + * Note that a function manipulating the CFA pointer location (i.e. the + * x29 frame pointer on AArch64) should declare it using the + * appropriate .cfi* directives, or be prepared to have a degraded + * debugging experience. + */ + .cfi_sections .debug_frame + .section .text.asm.\_name, "ax" + .type \_name, %function + /* + * .cfi_startproc and .cfi_endproc are needed to output entries in + * .debug_frame + */ + .cfi_startproc + .align \_align + \_name: + .endm + + /* + * This macro is used to mark the end of a function. + */ + .macro endfunc _name + .cfi_endproc + .size \_name, . - \_name + .endm + + /* + * Theses macros are used to create function labels for deprecated + * APIs. If ERROR_DEPRECATED is non zero, the callers of these APIs + * will fail to link and cause build failure. + */ +#if ERROR_DEPRECATED + .macro func_deprecated _name + func deprecated\_name + .endm + + .macro endfunc_deprecated _name + endfunc deprecated\_name + .endm +#else + .macro func_deprecated _name + func \_name + .endm + + .macro endfunc_deprecated _name + endfunc \_name + .endm +#endif + + /* + * Helper assembler macro to count trailing zeros. The output is + * populated in the `TZ_COUNT` symbol. + */ + .macro count_tz _value, _tz_count + .if \_value + count_tz "(\_value >> 1)", "(\_tz_count + 1)" + .else + .equ TZ_COUNT, (\_tz_count - 1) + .endif + .endm + + /* + * This macro declares an array of 1 or more stacks, properly + * aligned and in the requested section + */ +#define DEFAULT_STACK_ALIGN (1 << 6) /* In case the caller doesnt provide alignment */ + + .macro declare_stack _name, _section, _size, _count, _align=DEFAULT_STACK_ALIGN + count_tz \_align, 0 + .if (\_align - (1 << TZ_COUNT)) + .error "Incorrect stack alignment specified (Must be a power of 2)." + .endif + .if ((\_size & ((1 << TZ_COUNT) - 1)) <> 0) + .error "Stack size not correctly aligned" + .endif + .section \_section, "aw", %nobits + .align TZ_COUNT + \_name: + .space ((\_count) * (\_size)), 0 + .endm + + +#endif /* ASM_MACROS_COMMON_S */ diff --git a/arch/arm/armv8-a/include/common/debug.h b/arch/arm/armv8-a/include/common/debug.h new file mode 100644 index 000000000..4649cb8b9 --- /dev/null +++ b/arch/arm/armv8-a/include/common/debug.h @@ -0,0 +1,32 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2013-2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef DEBUG_H +#define DEBUG_H + +#include + +/* + * The log output macros print output to the console. These macros produce + * compiled log output only if the LOG_LEVEL defined in the makefile (or the + * make command line) is greater or equal than the level required for that + * type of log output. + * + * The format expected is the same as for printf(). For example: + * INFO("Info %s.\n", "message") -> INFO: Info message. + * WARN("Warning %s.\n", "message") -> WARNING: Warning message. + */ + +#define LOG_LEVEL_NONE U(0) +#define LOG_LEVEL_ERROR U(10) +#define LOG_LEVEL_NOTICE U(20) +#define LOG_LEVEL_WARNING U(30) +#define LOG_LEVEL_INFO U(40) +#define LOG_LEVEL_VERBOSE U(50) + + +#endif /* DEBUG_H */ diff --git a/arch/arm/armv8-a/include/lib/libc/assert.h b/arch/arm/armv8-a/include/lib/libc/assert.h new file mode 100644 index 000000000..c232a2f02 --- /dev/null +++ b/arch/arm/armv8-a/include/lib/libc/assert.h @@ -0,0 +1,44 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2018-2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef ASSERT_H +#define ASSERT_H + +#include +#include + +#ifndef static_assert +# define static_assert _Static_assert +#endif + + +#ifndef PLAT_LOG_LEVEL_ASSERT +#define PLAT_LOG_LEVEL_ASSERT LOG_LEVEL +#endif + +#if ENABLE_ASSERTIONS +# if PLAT_LOG_LEVEL_ASSERT >= LOG_LEVEL_VERBOSE +# define assert(e) ((e) ? (void)0 : __assert(__FILE__, __LINE__, #e)) +# elif PLAT_LOG_LEVEL_ASSERT >= LOG_LEVEL_INFO +# define assert(e) ((e) ? (void)0 : __assert(__FILE__, __LINE__)) +# else +# define assert(e) ((e) ? (void)0 : __assert()) +# endif +#else +#define assert(e) ((void)0) +#endif /* ENABLE_ASSERTIONS */ + +#if PLAT_LOG_LEVEL_ASSERT >= LOG_LEVEL_VERBOSE +noreturn void __assert(const char *file, unsigned int line, + const char *assertion); +#elif PLAT_LOG_LEVEL_ASSERT >= LOG_LEVEL_INFO +noreturn void __assert(const char *file, unsigned int line); +#else +noreturn void __assert(void); +#endif + +#endif /* ASSERT_H */ diff --git a/arch/arm/armv8-a/include/lib/libc/stdint.h b/arch/arm/armv8-a/include/lib/libc/stdint.h new file mode 100644 index 000000000..dc5b76af7 --- /dev/null +++ b/arch/arm/armv8-a/include/lib/libc/stdint.h @@ -0,0 +1,123 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2016-2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STDINT_H +#define STDINT_H + +#define INT8_MAX 0x7F +#define INT8_MIN (-INT8_MAX - 1) +#define UINT8_MAX 0xFFU + +#define INT16_MAX 0x7FFF +#define INT16_MIN (-INT16_MAX - 1) +#define UINT16_MAX 0xFFFFU + +#define INT32_MAX 0x7FFFFFFF +#define INT32_MIN (-INT32_MAX - 1) +#define UINT32_MAX 0xFFFFFFFFU + +#define INT64_MAX 0x7FFFFFFFFFFFFFFFLL +#define INT64_MIN (-INT64_MAX - 1LL) +#define UINT64_MAX 0xFFFFFFFFFFFFFFFFULL + +#define INT_LEAST8_MIN INT8_MIN +#define INT_LEAST8_MAX INT8_MAX +#define UINT_LEAST8_MAX UINT8_MAX + +#define INT_LEAST16_MIN INT16_MIN +#define INT_LEAST16_MAX INT16_MAX +#define UINT_LEAST16_MAX UINT16_MAX + +#define INT_LEAST32_MIN INT32_MIN +#define INT_LEAST32_MAX INT32_MAX +#define UINT_LEAST32_MAX UINT32_MAX + +#define INT_LEAST64_MIN INT64_MIN +#define INT_LEAST64_MAX INT64_MAX +#define UINT_LEAST64_MAX UINT64_MAX + +#define INT_FAST8_MIN INT32_MIN +#define INT_FAST8_MAX INT32_MAX +#define UINT_FAST8_MAX UINT32_MAX + +#define INT_FAST16_MIN INT32_MIN +#define INT_FAST16_MAX INT32_MAX +#define UINT_FAST16_MAX UINT32_MAX + +#define INT_FAST32_MIN INT32_MIN +#define INT_FAST32_MAX INT32_MAX +#define UINT_FAST32_MAX UINT32_MAX + +#define INT_FAST64_MIN INT64_MIN +#define INT_FAST64_MAX INT64_MAX +#define UINT_FAST64_MAX UINT64_MAX + +#define INTPTR_MIN INT64_MIN +#define INTPTR_MAX INT64_MAX +#define UINTPTR_MAX UINT64_MAX + +#define INTMAX_MIN INT64_MIN +#define INTMAX_MAX INT64_MAX +#define UINTMAX_MAX UINT64_MAX + +#define PTRDIFF_MIN INT64_MIN +#define PTRDIFF_MAX INT64_MAX + +#define SIZE_MAX UINT64_MAX + +#define INT8_C(x) x +#define INT16_C(x) x +#define INT32_C(x) x +#define INT64_C(x) x ## LL + +#define UINT8_C(x) x +#define UINT16_C(x) x +#define UINT32_C(x) x ## U +#define UINT64_C(x) x ## ULL + +#define INTMAX_C(x) x ## L +#define UINTMAX_C(x) x ## ULL + +typedef signed char int8_t; +typedef short int16_t; +typedef int int32_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +typedef signed char int8_least_t; +typedef short int16_least_t; +typedef int int32_least_t; +typedef long long int64_least_t; + +typedef unsigned char uint8_least_t; +typedef unsigned short uint16_least_t; +typedef unsigned int uint32_least_t; +typedef unsigned long long uint64_least_t; + +typedef int int8_fast_t; +typedef int int16_fast_t; +typedef int int32_fast_t; +typedef long long int64_fast_t; + +typedef unsigned int uint8_fast_t; +typedef unsigned int uint16_fast_t; +typedef unsigned int uint32_fast_t; +typedef unsigned long long uint64_fast_t; + +typedef long intptr_t; +typedef unsigned long uintptr_t; + +typedef long intmax_t; +typedef unsigned long uintmax_t; + +typedef long register_t; +typedef unsigned long u_register_t; + +#endif /* STDINT_H */ diff --git a/arch/arm/armv8-a/include/lib/libc/string.h b/arch/arm/armv8-a/include/lib/libc/string.h new file mode 100644 index 000000000..69773462d --- /dev/null +++ b/arch/arm/armv8-a/include/lib/libc/string.h @@ -0,0 +1,19 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef STRING_H +#define STRING_H + +typedef unsigned long size_t; + +void *memset(void *s, int c, size_t count); +void *memcpy(void *dst, const void *src, size_t n); +char *strncpy(char *dest, const char *src, size_t n); +char *strchr(const char *str, int c); +size_t strlen(const char *str); + +#endif /* STRING_H */ diff --git a/arch/arm/armv8-a/include/lib/mmio.h b/arch/arm/armv8-a/include/lib/mmio.h new file mode 100644 index 000000000..ae5cde8f1 --- /dev/null +++ b/arch/arm/armv8-a/include/lib/mmio.h @@ -0,0 +1,77 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2013-2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MMIO_H +#define MMIO_H + +#include + +static inline void mmio_write_8(uintptr_t addr, uint8_t value) +{ + *(volatile uint8_t*)addr = value; +} + +static inline uint8_t mmio_read_8(uintptr_t addr) +{ + return *(volatile uint8_t*)addr; +} + +static inline void mmio_write_16(uintptr_t addr, uint16_t value) +{ + *(volatile uint16_t*)addr = value; +} + +static inline uint16_t mmio_read_16(uintptr_t addr) +{ + return *(volatile uint16_t*)addr; +} + +static inline void mmio_clrsetbits_16(uintptr_t addr, + uint16_t clear, + uint16_t set) +{ + mmio_write_16(addr, (mmio_read_16(addr) & ~clear) | set); +} + +static inline void mmio_write_32(uintptr_t addr, uint32_t value) +{ + *(volatile uint32_t*)addr = value; +} + +static inline uint32_t mmio_read_32(uintptr_t addr) +{ + return *(volatile uint32_t*)addr; +} + +static inline void mmio_write_64(uintptr_t addr, uint64_t value) +{ + *(volatile uint64_t*)addr = value; +} + +static inline uint64_t mmio_read_64(uintptr_t addr) +{ + return *(volatile uint64_t*)addr; +} + +static inline void mmio_clrbits_32(uintptr_t addr, uint32_t clear) +{ + mmio_write_32(addr, mmio_read_32(addr) & ~clear); +} + +static inline void mmio_setbits_32(uintptr_t addr, uint32_t set) +{ + mmio_write_32(addr, mmio_read_32(addr) | set); +} + +static inline void mmio_clrsetbits_32(uintptr_t addr, + uint32_t clear, + uint32_t set) +{ + mmio_write_32(addr, (mmio_read_32(addr) & ~clear) | set); +} + +#endif /* MMIO_H */ diff --git a/arch/arm/armv8-a/include/lib/utils_def.h b/arch/arm/armv8-a/include/lib/utils_def.h new file mode 100644 index 000000000..6928a50c4 --- /dev/null +++ b/arch/arm/armv8-a/include/lib/utils_def.h @@ -0,0 +1,174 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2016-2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef UTILS_DEF_H +#define UTILS_DEF_H + +/* Compute the number of elements in the given array */ +#define ARRAY_SIZE(a) \ + (sizeof(a) / sizeof((a)[0])) + +#define IS_POWER_OF_TWO(x) \ + (((x) & ((x) - 1)) == 0) + +#define SIZE_FROM_LOG2_WORDS(n) (4 << (n)) + +#define BIT_32(nr) (U(1) << (nr)) +#define BIT_64(nr) (ULL(1) << (nr)) + +#ifdef AARCH32 +#define BIT BIT_32 +#else +#define BIT BIT_64 +#endif + +/* + * Create a contiguous bitmask starting at bit position @l and ending at + * position @h. For example + * GENMASK_64(39, 21) gives us the 64bit vector 0x000000ffffe00000. + */ +#if defined(__LINKER__) || defined(__ASSEMBLY__) +#define GENMASK_32(h, l) \ + (((0xFFFFFFFF) << (l)) & (0xFFFFFFFF >> (32 - 1 - (h)))) + +#define GENMASK_64(h, l) \ + ((~0 << (l)) & (~0 >> (64 - 1 - (h)))) +#else +#define GENMASK_32(h, l) \ + (((~UINT32_C(0)) << (l)) & (~UINT32_C(0) >> (32 - 1 - (h)))) + +#define GENMASK_64(h, l) \ + (((~UINT64_C(0)) << (l)) & (~UINT64_C(0) >> (64 - 1 - (h)))) +#endif + +#ifdef AARCH32 +#define GENMASK GENMASK_32 +#else +#define GENMASK GENMASK_64 +#endif + +/* + * This variant of div_round_up can be used in macro definition but should not + * be used in C code as the `div` parameter is evaluated twice. + */ +#define DIV_ROUND_UP_2EVAL(n, d) (((n) + (d) - 1) / (d)) + +#define div_round_up(val, div) __extension__ ({ \ + __typeof__(div) _div = (div); \ + ((val) + _div - (__typeof__(div)) 1) / _div; \ +}) + +#define MIN(x, y) __extension__ ({ \ + __typeof__(x) _x = (x); \ + __typeof__(y) _y = (y); \ + (void)(&_x == &_y); \ + _x < _y ? _x : _y; \ +}) + +#define MAX(x, y) __extension__ ({ \ + __typeof__(x) _x = (x); \ + __typeof__(y) _y = (y); \ + (void)(&_x == &_y); \ + _x > _y ? _x : _y; \ +}) + +/* + * 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. + * In other words, it computes the smallest multiple of boundary which is + * greater than or equal to value. + * + * round_down() is similar but rounds the value down instead. + */ +#define round_boundary(value, boundary) \ + ((__typeof__(value))((boundary) - 1)) + +#define round_up(value, boundary) \ + ((((value) - 1) | round_boundary(value, boundary)) + 1) + +#define round_down(value, boundary) \ + ((value) & ~round_boundary(value, boundary)) + +/* + * Evaluates to 1 if (ptr + inc) overflows, 0 otherwise. + * Both arguments must be unsigned pointer values (i.e. uintptr_t). + */ +#define check_uptr_overflow(_ptr, _inc) \ + ((_ptr) > (UINTPTR_MAX - (_inc))) + +/* + * Evaluates to 1 if (u32 + inc) overflows, 0 otherwise. + * Both arguments must be 32-bit unsigned integers (i.e. effectively uint32_t). + */ +#define check_u32_overflow(_u32, _inc) \ + ((_u32) > (UINT32_MAX - (_inc))) + +/* + * For those constants to be shared between C and other sources, apply a 'U', + * 'UL', 'ULL', 'L' or 'LL' suffix to the argument only in C, to avoid + * undefined or unintended behaviour. + * + * The GNU assembler and linker do not support these suffixes (it causes the + * build process to fail) therefore the suffix is omitted when used in linker + * scripts and assembler files. +*/ +#if defined(__LINKER__) || defined(__ASSEMBLY__) +# define U(_x) (_x) +# define UL(_x) (_x) +# define ULL(_x) (_x) +# define L(_x) (_x) +# define LL(_x) (_x) +#else +# define U(_x) (_x##U) +# define UL(_x) (_x##UL) +# define ULL(_x) (_x##ULL) +# define L(_x) (_x##L) +# define LL(_x) (_x##LL) +#endif + +/* Register size of the current architecture. */ +#ifdef AARCH32 +#define REGSZ U(4) +#else +#define REGSZ U(8) +#endif + +/* + * Test for the current architecture version to be at least the version + * expected. + */ +#define ARM_ARCH_AT_LEAST(_maj, _min) \ + ((ARM_ARCH_MAJOR > (_maj)) || \ + ((ARM_ARCH_MAJOR == (_maj)) && (ARM_ARCH_MINOR >= (_min)))) + +/* + * Import an assembly or linker symbol as a C expression with the specified + * type + */ +#define IMPORT_SYM(type, sym, name) \ + extern char sym[];\ + static const __attribute__((unused)) type name = (type) sym; + +/* + * When the symbol is used to hold a pointer, its alignment can be asserted + * with this macro. For example, if there is a linker symbol that is going to + * be used as a 64-bit pointer, the value of the linker symbol must also be + * aligned to 64 bit. This macro makes sure this is the case. + */ +#define ASSERT_SYM_PTR_ALIGN(sym) \ + assert(((size_t)(sym) % __alignof__(*(sym))) == 0) + +#define COMPILER_BARRIER() __asm__ volatile ("" ::: "memory") + +/* Compiler builtin of GCC >= 9 and planned in llvm */ +#ifdef __HAVE_SPECULATION_SAFE_VALUE +# define SPECULATION_SAFE_VALUE(var) __builtin_speculation_safe_value(var) +#else +# define SPECULATION_SAFE_VALUE(var) var +#endif + +#endif /* UTILS_DEF_H */ diff --git a/arch/arm/armv8-a/src/arch.ld.S b/arch/arm/armv8-a/src/arch.ld.S new file mode 100644 index 000000000..56d172397 --- /dev/null +++ b/arch/arm/armv8-a/src/arch.ld.S @@ -0,0 +1,218 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2015-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2019-2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * There are three supported memory layouts for the ARMv7-M architecture: + * + * Layout 1 - Single region: + * - All sections are placed in one contiguous region. + * - This layout uses only the mem0 memory region. + * - The memory is considered RXW by the linker, but the sections can be + * configured later on with different attributes using the MPU. + * - The main stack is placed at the end of mem0. + * - This layout is mainly used by second-stage firmware that is loaded directly + * into a single RAM. + * + * Layout 2 - Dual region with relocation: + * - One region is used for .text and .data (storage). + * - A second region is used for the remaining sections. + * - This layout uses memory regions mem0 and mem1 as the first and second + * regions, respectively. + * - The main stack is placed at the end of mem1. + * - This layout is mainly used by ROM firmware which uses part of the RAM for + * the data sections. + * + * Layout 3 - Dual region without relocation + * - One region is used only for the .text section. + * - A second region is used for all data sections. + * - This layout uses memory regions mem0 and mem1 as the first and second + * regions, respectively. + * - The main stack is placed at the end of mem1. + * - The main difference from layout 2 is that there is no relocation of the + * .data section. + * - This layout is mainly used by second-stage firmware loaded directly into + * two RAM regions. One of the RAM regions is attached to the instruction bus, + * which improves the performance as data and instruction accesses are + * independent. + * + */ + +#define ARCH_MEM_MODE_SINGLE_REGION 0 +#define ARCH_MEM_MODE_DUAL_REGION_RELOCATION 1 +#define ARCH_MEM_MODE_DUAL_REGION_NO_RELOCATION 2 + +#include + +#define STACK_ALIGNMENT 8 + +/* + * Input validation + */ + +#ifndef FMW_MEM_MODE + #error "FMW_MEM_MODE has not been configured" +#endif + +#ifndef FMW_STACK_SIZE + #error "FMW_STACK_SIZE has not been configured" +#endif + +#ifndef FMW_MEM0_BASE + #error "FMW_MEM0_BASE has not been configured" +#endif + +#ifndef FMW_MEM0_SIZE + #error "FMW_MEM0_SIZE has not been configured" +#endif + +#if ((FMW_MEM_MODE != ARCH_MEM_MODE_SINGLE_REGION) && \ + (FMW_MEM_MODE != ARCH_MEM_MODE_DUAL_REGION_RELOCATION) && \ + (FMW_MEM_MODE != ARCH_MEM_MODE_DUAL_REGION_NO_RELOCATION)) + #error "FMW_MEM_MODE has been configured improperly" +#endif + +#if FMW_MEM_MODE != ARCH_MEM_MODE_SINGLE_REGION + #ifndef FIRMWARE_MEM1_BASE + #error "FIRMWARE_MEM1_BASE has not been configured" + #endif + + #ifndef FIRMWARE_MEM1_SIZE + #error "FIRMWARE_MEM1_SIZE has not been configured" + #endif +#endif + +/* + * Calculate stack region in the data memory. + */ + +#if FMW_MEM_MODE == ARCH_MEM_MODE_SINGLE_REGION + ASSERT(FMW_STACK_SIZE < FMW_MEM0_SIZE, + "FMW_STACK_SIZE does not fit in MEM0") + #define UNALIGNED_STACK_BASE \ + (FMW_MEM0_BASE + FMW_MEM0_SIZE - FMW_STACK_SIZE) +#else + ASSERT(FMW_STACK_SIZE < FIRMWARE_MEM1_SIZE, + "FMW_STACK_SIZE does not fit in MEM1") + #define UNALIGNED_STACK_BASE \ + (FIRMWARE_MEM1_BASE + FIRMWARE_MEM1_SIZE - FMW_STACK_SIZE) +#endif + +#define STACK_BASE \ + ( \ + ((UNALIGNED_STACK_BASE + STACK_ALIGNMENT - 1) / STACK_ALIGNMENT) \ + * STACK_ALIGNMENT \ + ) + +#define STACK_SIZE \ + (( \ + ((STACK_BASE + FMW_STACK_SIZE) / STACK_ALIGNMENT) \ + * STACK_ALIGNMENT \ + ) - STACK_BASE) + +ASSERT(STACK_SIZE > 0, "FMW_STACK_SIZE is too small") + +ENTRY(_entrypoint) + +MEMORY { +#if FMW_MEM_MODE == ARCH_MEM_MODE_SINGLE_REGION + /* Only one memory region with read, execute and write attributes */ + mem0 (rxw): ORIGIN = FMW_MEM0_BASE, LENGTH = FMW_MEM0_SIZE - \ + FMW_STACK_SIZE +#else + mem0 (rx): ORIGIN = FMW_MEM0_BASE, LENGTH = FMW_MEM0_SIZE + mem1 (rxw): ORIGIN = FIRMWARE_MEM1_BASE, LENGTH = FIRMWARE_MEM1_SIZE - \ + FMW_STACK_SIZE +#endif + stack (rw): ORIGIN = STACK_BASE, LENGTH = STACK_SIZE + sram (rxw): ORIGIN = ((0xE6302000)), LENGTH = (0x00001000) +} + +SECTIONS { + .text : { + *(.vectors) + *(.entrypoint) + *(.text*) + *(.rodata*) + *(.note.gnu.build-id) + } > mem0 + + __text__ = .; + + __sram_copy_start__ = .; + .system_ram : { + __system_ram_start__ = .; + *(.system_ram*) + *iic_dvfs.o(.rodata) + __system_ram_end__ = .; + } > sram AT> mem0 + + . = __text__ + SIZEOF(.system_ram); + + /* + * Define a linker symbol to mark start of the RW memory area for this + * image. + */ + __RW_START__ = . ; + + .data : { + . = ALIGN(4); + *(.data*) + . = ALIGN(4); +#if FMW_MEM_MODE == ARCH_MEM_MODE_SINGLE_REGION + } > mem0 /* .data follows .text in mem0 */ +#elif FMW_MEM_MODE == ARCH_MEM_MODE_DUAL_REGION_NO_RELOCATION + } > mem1 /* .data is the first section in mem1 */ +#elif FMW_MEM_MODE == ARCH_MEM_MODE_DUAL_REGION_RELOCATION + } > mem1 AT>mem0 /* Run-time image is at mem1, but loaded from mem0 */ +#else + ASSERT(0, "Unrecognized FMW_MEM_MODE") +#endif + + .bss : { + . = ALIGN(4); + *(.bss*) + . = ALIGN(4); +#if FMW_MEM_MODE == ARCH_MEM_MODE_SINGLE_REGION + } > mem0 /* Run-time image is at mem1, but loaded from mem0 */ +#else + } > mem1 /* .bss follows .data in mem1 */ +#endif + + .stack : { + . = . + STACK_SIZE; + } > stack + __RW_END__ = .; + + /* + * Define a linker symbol to mark end of the RW memory area for this + * image. + */ + + __TEXT_START__ = LOADADDR(.text); + __TEXT_SIZE__ = SIZEOF(.text); + __TEXT_END__ = __TEXT_START__ + __TEXT_SIZE__; + + __STACK_START__ = LOADADDR(.stack); + __STACK_SIZE__ = SIZEOF(.stack); + __STACK_END__ = __STACK_START__ + __STACK_SIZE__; + __STACK_TOP__ = __STACK_END__; + __STACK_SP3_SIZE__ = 0x800; + __STACK_SP0_TOP__ = __STACK_END__ - __STACK_SP3_SIZE__; + + __DATA_LMA_START__ = LOADADDR(.data); + __DATA_START__ = ADDR(.data); + __DATA_SIZE__ = SIZEOF(.data); + + __BSS_START__ = ADDR(.bss); + __BSS_SIZE__ = SIZEOF(.bss); + __BSS_END__ = __BSS_START__ + __BSS_SIZE__; + + __HEAP_START__ = __BSS_START__ + __BSS_SIZE__; + __HEAP_END__ = __STACK_START__; + __HEAP_SIZE__ = __HEAP_END__ - __HEAP_START__; + + +} diff --git a/arch/arm/armv8-a/src/arch_cache_helpers.S b/arch/arm/armv8-a/src/arch_cache_helpers.S new file mode 100644 index 000000000..2c40cb23c --- /dev/null +++ b/arch/arm/armv8-a/src/arch_cache_helpers.S @@ -0,0 +1,204 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2013-2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + + .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 + /* Exit early if size is zero */ + cbz x1, exit_loop_\op + dcache_line_size x2, x3 + add x1, x0, x1 + sub x3, x2, #1 + bic x0, x0, x3 +loop_\op: + dc \op, x0 + add x0, x0, x2 + cmp x0, x1 + b.lo loop_\op + dsb sy +exit_loop_\op: + ret +.endm + /* ------------------------------------------ + * Clean+Invalidate from base address till + * size. 'x0' = addr, 'x1' = size + * ------------------------------------------ + */ +func flush_dcache_range + do_dcache_maintenance_by_mva civac +endfunc flush_dcache_range + + /* ------------------------------------------ + * Clean from base address till size. + * 'x0' = addr, 'x1' = size + * ------------------------------------------ + */ +func clean_dcache_range + do_dcache_maintenance_by_mva cvac +endfunc clean_dcache_range + + /* ------------------------------------------ + * Invalidate from base address till + * size. 'x0' = addr, 'x1' = size + * ------------------------------------------ + */ +func inv_dcache_range + do_dcache_maintenance_by_mva ivac +endfunc inv_dcache_range + + + /* --------------------------------------------------------------- + * Data cache operations by set/way to the level specified + * + * The main function, do_dcsw_op requires: + * x0: The operation type (0-2), as defined in arch.h + * x3: The last cache level to operate on + * x9: clidr_el1 + * x10: The cache level to begin operation from + * and will carry out the operation on each data cache from level 0 + * to the level in x3 in sequence + * + * The dcsw_op macro sets up the x3 and x9 parameters based on + * clidr_el1 cache information before invoking the main function + * --------------------------------------------------------------- + */ + + .macro dcsw_op shift, fw, ls + mrs x9, clidr_el1 + ubfx x3, x9, \shift, \fw + lsl x3, x3, \ls + mov x10, xzr + b do_dcsw_op + .endm + +func do_dcsw_op + cbz x3, exit + adr x14, dcsw_loop_table // compute inner loop address + add x14, x14, x0, lsl #5 // inner loop is 8x32-bit instructions + mov x0, x9 + mov w8, #1 +loop1: + add x2, x10, x10, lsr #1 // work out 3x current cache level + lsr x1, x0, x2 // extract cache type bits from clidr + and x1, x1, #7 // mask the bits for current cache only + cmp x1, #2 // see what cache we have at this level + b.lo level_done // nothing to do if no cache or icache + + msr csselr_el1, x10 // select current cache level in csselr + isb // isb to sych the new cssr&csidr + mrs x1, ccsidr_el1 // read the new ccsidr + and x2, x1, #7 // extract the length of the cache lines + add x2, x2, #4 // add 4 (line length offset) + ubfx x4, x1, #3, #10 // maximum way number + clz w5, w4 // bit position of way size increment + lsl w9, w4, w5 // w9 = aligned max way number + lsl w16, w8, w5 // w16 = way number loop decrement + orr w9, w10, w9 // w9 = combine way and cache number + ubfx w6, w1, #13, #15 // w6 = max set number + lsl w17, w8, w2 // w17 = set number loop decrement + dsb sy // barrier before we start this level + br x14 // jump to DC operation specific loop + + .macro dcsw_loop _op +loop2_\_op: + lsl w7, w6, w2 // w7 = aligned max set number + +loop3_\_op: + orr w11, w9, w7 // combine cache, way and set number + dc \_op, x11 + subs w7, w7, w17 // decrement set number + b.hs loop3_\_op + + subs x9, x9, x16 // decrement way number + b.hs loop2_\_op + + b level_done + .endm + +level_done: + add x10, x10, #2 // increment cache number + cmp x3, x10 + b.hi loop1 + msr csselr_el1, xzr // select cache level 0 in csselr + dsb sy // barrier to complete final cache operation + isb +exit: + ret +endfunc do_dcsw_op + +dcsw_loop_table: + dcsw_loop isw + dcsw_loop cisw + dcsw_loop csw + + +func dcsw_op_louis + dcsw_op #LOUIS_SHIFT, #CLIDR_FIELD_WIDTH, #LEVEL_SHIFT +endfunc dcsw_op_louis + + +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 + mrs x9, clidr_el1 + mov x3, \level + sub x10, x3, #2 + b do_dcsw_op + .endm + + /* --------------------------------------------------------------- + * Data cache operations by set/way for level 1 cache + * + * The main function, do_dcsw_op requires: + * x0: The operation type (0-2), 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: + * x0: The operation type (0-2), 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: + * x0: The operation type (0-2), as defined in arch.h + * --------------------------------------------------------------- + */ +func dcsw_op_level3 + dcsw_op_level #(3 << LEVEL_SHIFT) +endfunc dcsw_op_level3 diff --git a/arch/arm/armv8-a/src/arch_crt0.S b/arch/arm/armv8-a/src/arch_crt0.S new file mode 100644 index 000000000..8cc238b9c --- /dev/null +++ b/arch/arm/armv8-a/src/arch_crt0.S @@ -0,0 +1,186 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + + .section .entrypoint + + .globl _entrypoint + .globl _restore_system + .globl _save_system + .globl _boot_flag + .globl _shutdown_request + + .extern _vector_table + .extern arm_main + .extern rcar_pwrc_set_suspend_to_ram +#ifdef BUILD_HAS_NOTIFICATION + .extern __fwk_notification_reset +#endif + +func _entrypoint + ldr w0, _boot_flag + ldr w1, =R_WARMBOOT + cmp w1, w0 + beq _restore_system + + ldr x0, =__STACK_TOP__ + mov sp, x0 /* SP_EL3 */ + + adr x0, _vector_table + msr vbar_el3, x0 + isb + + msr spsel, #0 + ldr x0, =__STACK_SP0_TOP__ + mov sp, x0 /* SP_EL0 */ + stp x29, x30, [sp, #-32]! + + adr x0, __RW_START__ + adr x1, __RW_END__ + sub x1, x1, x0 + bl inv_dcache_range + + ldr x0, =__BSS_START__ + ldr x1, =__BSS_SIZE__ + bl zeromem + +#if USE_COHERENT_MEM + ldr x0, =__COHERENT_RAM_START__ + ldr x1, =__COHERENT_RAM_UNALIGNED_SIZE__ + bl zeromem +#endif + + mrs x0, scr_el3 + /* RW[10]=1, HCE[8]=0, SMD[7]=0, EA[3]=1, FIQ[2]=1, IRQ[1]=1, NS[0]=0 */ + mov x0, #(1<<10 | 0<<8 | 0<<7 | 1<<3 | 1<<2 | 1<<1 | 0<<0) + msr scr_el3, x0 + + /* -------------------------------------------------- + * Initialize platform and jump to our c-entry point + * for this type of reset. + * -------------------------------------------------- + */ +#ifdef BUILD_HAS_NOTIFICATION + bl __fwk_notification_reset +#endif + bl arm_main + + mov x0, 1 + ldp x29, x30, [sp], #32 + + ret + +endfunc _entrypoint + +func _save_system /* EL3t */ + stp x2, x3, [sp, #-0x10]! + ldr x2, =_save_area_top + stp x0, x1, [x2, #-0x10]! + mov x0, x2 + ldp x2, x3, [sp], #0x10 + stp x2, x3, [x0, #-0x10]! + stp x4, x5, [x0, #-0x10]! + stp x6, x7, [x0, #-0x10]! + stp x8, x9, [x0, #-0x10]! + stp x10, x11, [x0, #-0x10]! + stp x12, x13, [x0, #-0x10]! + stp x14, x15, [x0, #-0x10]! + stp x16, x17, [x0, #-0x10]! + stp x18, x19, [x0, #-0x10]! + stp x20, x21, [x0, #-0x10]! + stp x22, x23, [x0, #-0x10]! + stp x24, x25, [x0, #-0x10]! + stp x26, x27, [x0, #-0x10]! + stp x28, x29, [x0, #-0x10]! + stp x30, xzr, [x0, #-0x10]! + + mov x2, sp + msr spsel, #1 + mov x3, sp + msr spsel, #0 + stp x2, x3, [x0, #-0x10]! /* Save SP_EL0, SP_EL3 */ + + mrs x3, scr_el3 + mov x2, x30 + stp x2, x3, [x0, #-0x10]! /* Save elr_el3(lr), scr_el3 */ + + mrs x2, nzcv + mrs x3, daif + orr x2, x2, x3 + mrs x3, CurrentEL + orr x2, x2, x3 + mrs x3, SPSel + orr x2, x2, x3 + mrs x3, vbar_el3 + stp x2, x3, [x0, #-0x10]! /* Save spsr_el3(psr), vbar_el3 */ + + bl rcar_pwrc_set_suspend_to_ram +1: + wfi + b 1b +endfunc _save_system + +func _restore_system /* EL3h */ + ldr x0, =_save_area_bottom + + ldp x2, x3, [x0], #0x10 /* Restore spsr_el3(psr), vbar_el3 */ + msr spsr_el3, x2 + msr vbar_el3, x3 + + ldp x2, x3, [x0], #0x10 /* Restore elr_el3(lr), scr_el3 */ + msr elr_el3, x2 + msr scr_el3, x3 + + ldp x2, x3, [x0], #0x10 /* Restore SP_EL0, SP_EL3 */ + mov sp, x3 + msr spsel, #0 + mov sp, x2 + + ldp x30, xzr, [x0], #0x10 + ldp x28, x29, [x0], #0x10 + ldp x26, x27, [x0], #0x10 + ldp x24, x25, [x0], #0x10 + ldp x22, x23, [x0], #0x10 + ldp x20, x21, [x0], #0x10 + ldp x18, x19, [x0], #0x10 + ldp x16, x17, [x0], #0x10 + ldp x14, x15, [x0], #0x10 + ldp x12, x13, [x0], #0x10 + ldp x10, x11, [x0], #0x10 + ldp x8, x9, [x0], #0x10 + ldp x6, x7, [x0], #0x10 + ldp x4, x5, [x0], #0x10 + ldp x2, x3, [x0], #0x10 + stp x2, x3, [sp, #-0x10]! + mov x2, x0 + ldp x0, x1, [x2], #0x10 + ldp x2, x3, [sp], #0x10 + + msr spsel, #1 + eret +endfunc _restore_system + + .section .data.context + .align 4 +_boot_flag: + .long 0 + + .align 4 +_shutdown_request: + .long 0 + + .align 4 +_save_area_bottom: + .rept 38 + .long 0, 0 + .endr +_save_area_top: + + .end diff --git a/arch/arm/armv8-a/src/arch_exceptions.S b/arch/arm/armv8-a/src/arch_exceptions.S new file mode 100644 index 000000000..dfdfc37d6 --- /dev/null +++ b/arch/arm/armv8-a/src/arch_exceptions.S @@ -0,0 +1,127 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + + .globl _vector_table + .globl _freertos_vector_table + .extern _entrypoint + +.section .vectors +.balign 2048 +_vector_table: + +.set VBAR, _vector_table + /* ----------------------------------------------------- + * Current EL with SP0 : 0x0 - 0x200 (at EL3) + * ----------------------------------------------------- + */ +.org VBAR + b _entrypoint +.org (VBAR + 0x80) + b . +.org (VBAR + 0x100) + b . +.org (VBAR + 0x180) + b . + + /* ----------------------------------------------------- + * Current EL with SPx: 0x200 - 0x400 (at EL3) + * ----------------------------------------------------- + */ +.org (VBAR + 0x200) + b . +.org (VBAR + 0x280) + b . +.org (VBAR + 0x300) + b . +.org (VBAR + 0x380) + b . + + /* ----------------------------------------------------- + * Lower EL using AArch64 : 0x400 - 0x600 (at EL3) + * ----------------------------------------------------- + */ +.org (VBAR + 0x400) + b . +.org (VBAR + 0x480) + b . +.org (VBAR + 0x500) + b . +.org (VBAR + 0x580) + b . + + /* ----------------------------------------------------- + * Lower EL using AArch32 : 0x600 - 0x800 (at EL3) + * ----------------------------------------------------- + */ +.org (VBAR + 0x600) + b . +.org (VBAR + 0x680) + b . +.org (VBAR + 0x700) + b . +.org (VBAR + 0x780) + b . + +/****************************************************************************** + * Vector table to use when FreeRTOS is running. + *****************************************************************************/ +.set FREERTOS_VBAR, (VBAR+0x0800) + + /* ----------------------------------------------------- + * Current EL with SP0 : 0x0 - 0x200 (at EL3) + * ----------------------------------------------------- + */ +.org(FREERTOS_VBAR) +_freertos_vector_table: + b FreeRTOS_SWI_Handler +.org (FREERTOS_VBAR + 0x80) + b FreeRTOS_IRQ_Handler +.org (FREERTOS_VBAR + 0x100) + b FreeRTOS_IRQ_Handler +.org (FREERTOS_VBAR + 0x180) + b . + + /* ----------------------------------------------------- + * Current EL with SPx: 0x200 - 0x400 (at EL3) + * ----------------------------------------------------- + */ +.org (FREERTOS_VBAR + 0x200) + b FreeRTOS_SWI_Handler +.org (FREERTOS_VBAR + 0x280) + b FreeRTOS_IRQ_Handler +.org (FREERTOS_VBAR + 0x300) + b FreeRTOS_IRQ_Handler +.org (FREERTOS_VBAR + 0x380) + b . + + /* ----------------------------------------------------- + * Lower EL using AArch64 : 0x400 - 0x600 (at EL3) + * ----------------------------------------------------- + */ +.org (FREERTOS_VBAR + 0x400) + b . +.org (FREERTOS_VBAR + 0x480) + b . +.org (FREERTOS_VBAR + 0x500) + b . +.org (FREERTOS_VBAR + 0x580) + b . + + /* ----------------------------------------------------- + * Lower EL using AArch32 : 0x600 - 0x800 (at EL3) + * ----------------------------------------------------- + */ +.org (FREERTOS_VBAR + 0x600) + b . +.org (FREERTOS_VBAR + 0x680) + b . +.org (FREERTOS_VBAR + 0x700) + b . +.org (FREERTOS_VBAR + 0x780) + b . + + .end diff --git a/arch/arm/armv8-a/src/arch_gic.c b/arch/arm/armv8-a/src/arch_gic.c new file mode 100644 index 000000000..818f7ae50 --- /dev/null +++ b/arch/arm/armv8-a/src/arch_gic.c @@ -0,0 +1,567 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + +#define C_INT_ID (INT_ID(c_interrupt)) +#ifndef RCAR_SCMI_LIB +#define RCAR_MFIS_MIN U(256) +#define RCAR_MFIS_NO U(8) +#define RCAR_MFIS_MAX (RCAR_MFIS_MIN + RCAR_MFIS_NO) +#define IS_SUPPORT_INT(n) ((n >= RCAR_MFIS_MIN) && (n <= RCAR_MFIS_MAX)) +#define EFECTIVE_NO(n) (n - RCAR_MFIS_MIN) +#else +#define IS_SUPPORT_INT(n) ((n >= SMCMH_IRQ_START) && (n < SMCMH_IRQ_END)) +#define EFECTIVE_NO(n) (n & 0xff) +#endif /* RCAR_SCMI_LIB */ +#define CHECK_BIT(d, b) ((d >> b) & 1) +#define IID_LEN (10) + +/* + * For interrupts with parameters, their entry in the vector table points to a + * global handler that calls a registered function in the callback table with a + * corresponding parameter. Entries in the vector table for interrupts without + * parameters point directly to the handler functions. + */ +struct callback { + union { + void (*func)(uintptr_t param); + void (*funcn)(void); + }; + uintptr_t param; +}; + +struct r_node { + int valid; + struct r_node *left; + struct r_node *right; + struct callback *entry; +}; + +struct r_tree { + struct r_node *root; + int _allocated; +}; + +static unsigned int c_interrupt; +static struct r_tree *radix; + +struct r_tree *init_entry(struct r_tree *rt) +{ + if (NULL == rt) { + rt = fwk_mm_calloc(1, sizeof(struct r_tree)); + if (NULL == rt) { + return NULL; + } + rt->_allocated = 1; + } else { + rt->_allocated = 0; + } + rt->root = NULL; + + return rt; +} + +static void *_lookup(struct r_node *cur, + struct r_node *cand, uint32_t iid, int bitno) +{ + if (NULL == cur) { + return NULL != cand ? cand->entry : NULL; + } + + if (cur->valid) { + cand = cur; + } + + if (CHECK_BIT(iid, bitno)) { + return _lookup(cur->right, cand, iid, bitno + 1); + } else { + return _lookup(cur->left, cand, iid, bitno + 1); + } +} + +static void *lookup_entry(struct r_tree *rt, uint32_t iid) +{ + return _lookup(rt->root, NULL, iid, 0); +} + +static int _add(struct r_node **cur, + uint32_t iid, void *entry, int bitsize, int bitno) +{ + struct r_node *new; + + if (NULL == *cur) { + new = fwk_mm_calloc(1, sizeof(struct r_node)); + if (NULL == new) { + return -1; + } + memset(new, 0, sizeof(struct r_node)); + *cur = new; + } + + if (bitsize == bitno) { + if ((*cur)->valid) { + return -1; + } + (*cur)->valid = 1; + (*cur)->entry = entry; + return 0; + } else { + if (CHECK_BIT(iid, bitno)) { + return _add(&(*cur)->right, iid, entry, bitsize, bitno + 1); + } else { + return _add(&(*cur)->left, iid, entry, bitsize, bitno + 1); + } + } +} + +static int add_entry(struct r_tree *rt, uint32_t iid, void *entry, int len) +{ + return _add(&rt->root, iid, entry, len, 0); +} + +void irq_global(uint32_t iid) +{ + struct callback *entry; + + c_interrupt = iid; + + entry = (struct callback *)lookup_entry(radix, iid); + if (entry != NULL) { + if (entry->func) { + /* Available callback Function */ + if (entry->param) { + entry->func(entry->param); + } else { + entry->funcn(); + } + } + } else { + /* No interrupt entry */ + } + c_interrupt = 0; +} + +#ifndef RCAR_SCMI_LIB + +/******************************************************************************* + * GIC Distributor interface accessors for reading entire registers + ******************************************************************************/ +/* + * Accessor to read the GIC Distributor ISENABLER corresponding to the + * interrupt `id`, 32 interrupt ids at a time. + */ +static unsigned int gicd_read_isenabler(uintptr_t base, unsigned int id) +{ + unsigned int n = id >> ISENABLER_SHIFT; + + return mmio_read_32(base + GICD_ISENABLER + (n << 2)); +} + +/* + * Accessor to read the GIC Distributor ISPENDR corresponding to the + * interrupt `id`, 32 interrupt IDs at a time. + */ +static unsigned int gicd_read_ispendr(uintptr_t base, unsigned int id) +{ + unsigned int n = id >> ISPENDR_SHIFT; + + return mmio_read_32(base + GICD_ISPENDR + (n << 2)); +} + +/******************************************************************************* + * GIC Distributor interface accessors for writing entire registers + ******************************************************************************/ +/* + * Accessor to write the GIC Distributor ISENABLER corresponding to the + * interrupt `id`, 32 interrupt IDs at a time. + */ +static void gicd_write_isenabler( + uintptr_t base, unsigned int id, unsigned int val) +{ + unsigned int n = id >> ISENABLER_SHIFT; + + mmio_write_32(base + GICD_ISENABLER + (n << 2), val); +} + +/* + * Accessor to write the GIC Distributor ICENABLER corresponding to the + * interrupt `id`, 32 interrupt IDs at a time. + */ +static void gicd_write_icenabler( + uintptr_t base, unsigned int id, unsigned int val) +{ + unsigned int n = id >> ICENABLER_SHIFT; + + mmio_write_32(base + GICD_ICENABLER + (n << 2), val); +} + +/* + * Accessor to write the GIC Distributor ISPENDR corresponding to the + * interrupt `id`, 32 interrupt IDs at a time. + */ +static void gicd_write_ispendr( + uintptr_t base, unsigned int id, unsigned int val) +{ + unsigned int n = id >> ISPENDR_SHIFT; + + mmio_write_32(base + GICD_ISPENDR + (n << 2), val); +} + +/* + * Accessor to write the GIC Distributor ICPENDR corresponding to the + * interrupt `id`, 32 interrupt IDs at a time. + */ +static void gicd_write_icpendr( + uintptr_t base, unsigned int id, unsigned int val) +{ + unsigned int n = id >> ICPENDR_SHIFT; + + mmio_write_32(base + GICD_ICPENDR + (n << 2), val); +} + +/******************************************************************************* + * GIC Distributor functions for accessing the GIC registers + * corresponding to a single interrupt ID. These functions use bitwise + * operations or appropriate register accesses to modify or return + * the bit-field corresponding the single interrupt ID. + ******************************************************************************/ +static void gicd_set_isenabler(uintptr_t base, unsigned int id) +{ + unsigned int bit_num = id & ((1U << ISENABLER_SHIFT) - 1U); + + gicd_write_isenabler(base, id, (1U << bit_num)); +} + +static void gicd_set_icenabler(uintptr_t base, unsigned int id) +{ + unsigned int bit_num = id & ((1U << ICENABLER_SHIFT) - 1U); + + gicd_write_icenabler(base, id, (1U << bit_num)); +} + +static void gicd_set_ipriorityr( + uintptr_t base, unsigned int id, unsigned int pri) +{ + uint8_t val = pri & GIC_PRI_MASK; + + mmio_write_8(base + GICD_IPRIORITYR + id, val); +} + +static unsigned int gicd_get_isenabler(uintptr_t base, unsigned int id) +{ + unsigned int bit_num = id & ((1U << ISENABLER_SHIFT) - 1U); + + return ((gicd_read_isenabler(base, id) >> bit_num) & 1U); +} + +/******************************************************************************* + * GIC CPU interface accessors for writing entire registers + ******************************************************************************/ +static inline unsigned int gicc_read_ctlr(uintptr_t base) +{ + return mmio_read_32(base + GICC_CTLR); +} + +static void gicc_write_ctlr(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICC_CTLR, val); +} + +static void gicc_write_pmr(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICC_PMR, val); +} + +/******************************************************************************* + * Enable secure interrupts and use FIQs to route them. Disable legacy bypass + * and set the priority mask register to allow all interrupts to trickle in. + ******************************************************************************/ +void gic_cpuif_enable(void) +{ + unsigned int val; + + /* + * Enable the Group 0 interrupts, FIQEn and disable Group 0/1 + * bypass. + */ + val = CTLR_ENABLE_G0_BIT | FIQ_EN_BIT | FIQ_BYP_DIS_GRP0; + val |= IRQ_BYP_DIS_GRP0 | FIQ_BYP_DIS_GRP1 | IRQ_BYP_DIS_GRP1; + + /* Program the idle priority in the PMR */ + gicc_write_pmr(RCAR_GICC_BASE, GIC_PRI_MASK); + gicc_write_ctlr(RCAR_GICC_BASE, val); +} + +/******************************************************************************* + * Place the cpu interface in a state where it can never make a cpu exit wfi as + * as result of an asserted interrupt. This is critical for powering down a cpu + ******************************************************************************/ +void gic_cpuif_disable(void) +{ + unsigned int val; + + /* Disable secure, non-secure interrupts and disable their bypass */ + val = gicc_read_ctlr(RCAR_GICC_BASE); + val &= ~(CTLR_ENABLE_G0_BIT | CTLR_ENABLE_G1_BIT); + val |= FIQ_BYP_DIS_GRP1 | FIQ_BYP_DIS_GRP0; + val |= IRQ_BYP_DIS_GRP0 | IRQ_BYP_DIS_GRP1; + gicc_write_ctlr(RCAR_GICC_BASE, val); +} + +void gic_init(void) +{ + gicd_set_ipriorityr( + RCAR_GICD_BASE, VIRTUAL_TIMER_IRQ,GIC_HIGHEST_SEC_PRIORITY); + gicd_set_isenabler(RCAR_GICD_BASE, VIRTUAL_TIMER_IRQ); + gic_cpuif_enable(); +} + +/* --------------------------------------------------- */ + +static int global_enable(void) +{ + __asm__ volatile ("msr DAIFClr, #1"); /* FIQ */ + return FWK_SUCCESS; +} + +static int global_disable(void) +{ + __asm__ volatile ("msr DAIFSet, #1"); /* FIQ */ + return FWK_SUCCESS; +} + +static int is_enabled(unsigned int interrupt, bool *enabled) +{ + if (!IS_SUPPORT_INT(interrupt)) + return FWK_E_PARAM; + + *enabled = (bool)gicd_get_isenabler(RCAR_GICD_BASE, interrupt); + + return FWK_SUCCESS; +} + +static int enable(unsigned int interrupt) +{ + if (!IS_SUPPORT_INT(interrupt)) + return FWK_E_PARAM; + + gicd_set_isenabler(RCAR_GICD_BASE, interrupt); + + return FWK_SUCCESS; +} + +static int disable(unsigned int interrupt) +{ + if (!IS_SUPPORT_INT(interrupt)) + return FWK_E_PARAM; + + gicd_set_icenabler(RCAR_GICD_BASE, interrupt); + + return FWK_SUCCESS; +} + +static int is_pending(unsigned int interrupt, bool *pending) +{ + unsigned int bit; + + if (!IS_SUPPORT_INT(interrupt)) + return FWK_E_PARAM; + + bit = interrupt % 32; + *pending = + (gicd_read_ispendr(RCAR_GICD_BASE, interrupt) & (1 << bit))? 1:0; + + return FWK_SUCCESS; +} + +static int set_pending(unsigned int interrupt) +{ + unsigned int bit; + + if (!IS_SUPPORT_INT(interrupt)) + return FWK_E_PARAM; + + bit = interrupt % 32; + gicd_write_ispendr(RCAR_GICD_BASE, interrupt, 1 << bit); + + return FWK_SUCCESS; +} + +static int clear_pending(unsigned int interrupt) +{ + unsigned int bit; + + if (!IS_SUPPORT_INT(interrupt)) + return FWK_E_PARAM; + + bit = interrupt % 32; + gicd_write_icpendr(RCAR_GICD_BASE, interrupt, 1 << bit); + + return FWK_SUCCESS; +} + +#else + +static int global_enable(void) +{ + return FWK_SUCCESS; +} + +static int global_disable(void) +{ + return FWK_SUCCESS; +} + +static int is_enabled(unsigned int interrupt, bool *enabled) +{ + return FWK_SUCCESS; +} + +static int enable(unsigned int interrupt) +{ + return FWK_SUCCESS; +} + +static int disable(unsigned int interrupt) +{ + return FWK_SUCCESS; +} + +static int is_pending(unsigned int interrupt, bool *pending) +{ + return FWK_SUCCESS; +} + +static int set_pending(unsigned int interrupt) +{ + return FWK_SUCCESS; +} + +static int clear_pending(unsigned int interrupt) +{ + return FWK_SUCCESS; +} + +#endif /* RCAR_SCMI_LIB */ + +static int set_isr_irq(unsigned int interrupt, void (*isr)(void)) +{ + struct callback *entry; + int ret; + + if ((MIN_IRQ > interrupt) || (MAX_IRQ <= interrupt)) + return FWK_E_PARAM; + + entry = fwk_mm_calloc(1, sizeof(struct callback)); + if (NULL == entry) + return FWK_E_PANIC; + + entry->funcn = isr; + entry->param = (uintptr_t)NULL; + ret = add_entry(radix, interrupt, (void *)entry, IID_LEN); + if (ret) + return FWK_E_PANIC; + + return FWK_SUCCESS; +} + +static int set_isr_irq_param(unsigned int interrupt, + void (*isr)(uintptr_t param), + uintptr_t parameter) +{ + struct callback *entry; + int ret; + + + if ((MIN_IRQ > interrupt) || (MAX_IRQ <= interrupt)) + return FWK_E_PANIC; + + entry = fwk_mm_calloc(1, sizeof(struct callback)); + if (NULL == entry) + return FWK_E_PANIC; + + entry->func = isr; + entry->param = parameter; + ret = add_entry(radix, interrupt, (void *)entry, IID_LEN); + if (ret) + return FWK_E_PARAM; + + return FWK_SUCCESS; +} + +static int set_isr_dummy(void (*isr)(void)) +{ + return FWK_SUCCESS; +} + +static int set_isr_dummy_param( + void (*isr)(uintptr_t param), uintptr_t parameter) +{ + return FWK_SUCCESS; +} + +static int get_current(unsigned int *interrupt) +{ + *interrupt = c_interrupt; + + /* Not an interrupt */ + if (0 == *interrupt) + return FWK_E_STATE; + + return FWK_SUCCESS; +} + +static const struct fwk_arch_interrupt_driver arm_gic_driver = { + .global_enable = global_enable, + .global_disable = global_disable, + .is_enabled = is_enabled, + .enable = enable, + .disable = disable, + .is_pending = is_pending, + .set_pending = set_pending, + .clear_pending = clear_pending, + .set_isr_irq = set_isr_irq, + .set_isr_irq_param = set_isr_irq_param, + .set_isr_nmi = set_isr_dummy, + .set_isr_nmi_param = set_isr_dummy_param, + .set_isr_fault = set_isr_dummy, + .get_current = get_current, +}; + +int arm_gic_init(const struct fwk_arch_interrupt_driver **driver) +{ + /* + * Allocate and initialize a table for the callback functions and their + * corresponding parameters. + */ + radix = init_entry(NULL); + if (radix == NULL) + return FWK_E_NOMEM; + + gic_init(); + + /* + * Initialize all exception entries to point to the arm_exception_invalid() + * handler. + * + * Note: Initialization starts from entry 1 since entry 0 is not an + * exception pointer but the default stack pointer. + */ + + *driver = &arm_gic_driver; + + return FWK_SUCCESS; +} diff --git a/arch/arm/armv8-a/src/arch_libc.c b/arch/arm/armv8-a/src/arch_libc.c new file mode 100644 index 000000000..222631159 --- /dev/null +++ b/arch/arm/armv8-a/src/arch_libc.c @@ -0,0 +1,258 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include + +void *memset(void *s, int c, size_t count) +{ + char *xs = s; + while (count--) + *xs++ = c; + return s; +} + +void *memcpy(void *dst, const void *src, size_t n) +{ + /* copy per 1 byte */ + const char *p = src; + char *q = dst; + + while (n--) { + *q++ = *p++; + } + + return dst; +} + +char *strncpy(char *dest, const char *src, size_t n) +{ + size_t i; + + for (i = 0 ; i < n && src[i] != 0 ; i++) + dest[i] = src[i]; + for ( ; i < n ; i++) + dest[i] = 0; + + return dest; +} + +char *strchr(const char *str, int c) +{ + do { + if (*str == (char) c) + return (char *) str; + str++; + } while (*str); + + return NULL; +} + +size_t strlen(const char *str) +{ + char *tmp = (char *) str; + size_t counter = 0; + while (*tmp++) ++counter; + return counter; +} + +static void uint_to_str(unsigned int i, char *buf, int base) +{ + char const digit_10[] = "0123456789"; + char const digit_16[] = "0123456789abcdef"; + unsigned int shifter = i; + char const *digit; + + if (base == 10) + digit = digit_10; + else + digit = digit_16; + + do { + ++buf; + shifter = shifter/base; + } while (shifter); + + *buf = '\0'; + + do { + *--buf = digit[i%base]; + i = i/base; + } while (i); +} + +static void int_to_str(int i, char *buf, int base) +{ + int sign = i; + + if (i < 0) { + i = -i; + buf++; + } + + uint_to_str((unsigned int) i, buf, base); + + if (sign < 0) + *--buf = '-'; +} + +static int isdigit(char c) +{ + return (c >= '0' && c <= '9'); +} + +static int handle_num(char type, char *buf, va_list *args) +{ + int int_num; + unsigned int uint_num; + + switch (type) { + case 'u': + uint_num = va_arg(*args, unsigned int); + uint_to_str(uint_num, buf, 10); + break; + case 'd': + int_num = va_arg(*args, int); + int_to_str(int_num, buf, 10); + break; + case 'x': + uint_num = va_arg(*args, unsigned int); + uint_to_str(uint_num, buf, 16); + break; + default: + return 1; + break; + } + + return 0; +} + +int vsnprintf(char *str, size_t n, const char *format, va_list args) +{ + char *pos; + char *s; + char *tmp = str; + size_t length = 0; + int num_length, min_length; + char num_buf[12]; + int not_implemented; + + for (pos = (char *) format; *pos != '\0'; pos++) { + while ((*pos != '%') && (*pos != '\0') && (length < n)) { + *tmp++ = *pos++; + length++; + } + + if (length == n) + break; + + if (*pos == '\0') { + *tmp = '\0'; + break; + } + + pos++; + + not_implemented = 0; + + switch (*pos) { + case 's': + s = va_arg(args,char *); + strncpy(tmp, s, n - length); + break; + case '0': + if (isdigit(*(pos + 1)) && (*(pos + 1) > '0')) { + pos++; + } else { + not_implemented = 1; + break; + } + case '1' ... '9': + min_length = (unsigned int) (*pos - '0'); + + if (handle_num(*(pos + 1), num_buf, &args)) { + if (*(pos - 1) == '0') + pos--; + + not_implemented = 1; + break; + } + + num_length = strlen(num_buf); + + if (num_length < min_length) { + while (num_length >= 0) + num_buf[min_length--] = num_buf[num_length--]; + + if (*(pos - 1) == '0') { + if (num_buf[0] == '-') { + min_length++; + while (min_length > 0) + num_buf[min_length--] = '0'; + } else { + while (min_length >= 0) + num_buf[min_length--] = '0'; + } + } else { + while (min_length >= 0) + num_buf[min_length--] = ' '; + } + } + strncpy(tmp, num_buf, n - length); + pos++; + break; + default: + if (handle_num(*pos, num_buf, &args)) + not_implemented = 1; + else + strncpy(tmp, num_buf, n - length); + break; + } + + if (not_implemented) { + va_arg(args, unsigned int); + *tmp++ = '%'; + length++; + pos--; + } else { + while ((*tmp != '\0') && (length < n)) { + tmp++; + length++; + } + } + } + + if (tmp == str) { + *tmp = '\0'; + } else if (length == n) { + tmp--; + if (*tmp != '\0') + *tmp = '\0'; + else + length--; + } else if (*(tmp - 1) != '\0') { + *tmp = '\0'; + } else { + length--; + } + + return length; +} + +int snprintf(char *str, size_t size, const char *format, ...) +{ + int counter; + va_list args; + va_start(args, format); + counter = vsnprintf(str, size, format, args); + va_end(args); + return counter; +} diff --git a/arch/arm/armv8-a/src/arch_main.c b/arch/arm/armv8-a/src/arch_main.c new file mode 100644 index 000000000..7131653f2 --- /dev/null +++ b/arch/arm/armv8-a/src/arch_main.c @@ -0,0 +1,44 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include + +#include +#include + +/* + * Error handler for failures that occur during early initialization. + */ +void panic(void) +{ + while (true) + wfi(); +} + +static struct fwk_arch_init_driver arch_init_driver = { + .interrupt = arm_gic_init, +}; + +void arm_main(void) +{ + int status; + + rcar_system_code_copy_to_system_ram(); + +#ifdef BUILD_MODE_DEBUG + uint32_t cntv_ctl = 0; + __asm__ volatile ("msr cntv_ctl_el0, %0" :: "r" (cntv_ctl)); +#endif /* BUILD_MODE_DEBUG */ + + status = fwk_arch_init(&arch_init_driver); + if (status != FWK_SUCCESS) + panic(); +} diff --git a/arch/arm/armv8-a/src/arch_misc_helpers.S b/arch/arm/armv8-a/src/arch_misc_helpers.S new file mode 100644 index 000000000..cde1a0397 --- /dev/null +++ b/arch/arm/armv8-a/src/arch_misc_helpers.S @@ -0,0 +1,511 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2013-2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + + .globl get_afflvl_shift + .globl mpidr_mask_lower_afflvls + .globl eret + .globl smc + + .globl zero_normalmem + .globl zeromem + .globl zeromem16 + .globl memcpy16 + + .globl disable_mmu_el1 + .globl disable_mmu_el3 + .globl disable_mmu_icache_el1 + .globl disable_mmu_icache_el3 + +#if SUPPORT_VFP + .globl enable_vfp +#endif + +func get_afflvl_shift + cmp x0, #3 + cinc x0, x0, eq + mov x1, #MPIDR_AFFLVL_SHIFT + lsl x0, x0, x1 + ret +endfunc get_afflvl_shift + +func mpidr_mask_lower_afflvls + cmp x1, #3 + cinc x1, x1, eq + mov x2, #MPIDR_AFFLVL_SHIFT + lsl x2, x1, x2 + lsr x0, x0, x2 + lsl x0, x0, x2 + ret +endfunc mpidr_mask_lower_afflvls + + +func eret + eret +endfunc eret + + +func smc + smc #0 +endfunc smc + +/* ----------------------------------------------------------------------- + * void zeromem16(void *mem, unsigned int length); + * + * Initialise a memory region to 0. + * The memory address must be 16-byte aligned. + * NOTE: This function is deprecated and zeromem should be used instead. + * ----------------------------------------------------------------------- + */ +.equ zeromem16, zeromem + +/* ----------------------------------------------------------------------- + * void zero_normalmem(void *mem, unsigned int length); + * + * Initialise a region in normal memory to 0. This functions complies with the + * AAPCS and can be called from C code. + * + * NOTE: MMU must be enabled when using this function as it can only operate on + * normal memory. It is intended to be mainly used from C code when MMU + * is usually enabled. + * ----------------------------------------------------------------------- + */ +.equ zero_normalmem, zeromem_dczva + +/* ----------------------------------------------------------------------- + * void zeromem(void *mem, unsigned int length); + * + * Initialise a region of device memory to 0. This functions complies with the + * AAPCS and can be called from C code. + * + * NOTE: When data caches and MMU are enabled, zero_normalmem can usually be + * used instead for faster zeroing. + * + * ----------------------------------------------------------------------- + */ +func zeromem + /* x2 is the address past the last zeroed address */ + add x2, x0, x1 + /* + * Uses the fallback path that does not use DC ZVA instruction and + * therefore does not need enabled MMU + */ + b .Lzeromem_dczva_fallback_entry +endfunc zeromem + +/* ----------------------------------------------------------------------- + * void zeromem_dczva(void *mem, unsigned int length); + * + * Fill a region of normal memory of size "length" in bytes with null bytes. + * MMU must be enabled and the memory be of + * normal type. This is because this function internally uses the DC ZVA + * instruction, which generates an Alignment fault if used on any type of + * Device memory (see section D3.4.9 of the ARMv8 ARM, issue k). When the MMU + * is disabled, all memory behaves like Device-nGnRnE memory (see section + * D4.2.8), hence the requirement on the MMU being enabled. + * NOTE: The code assumes that the block size as defined in DCZID_EL0 + * register is at least 16 bytes. + * + * ----------------------------------------------------------------------- + */ +func zeromem_dczva + + /* + * The function consists of a series of loops that zero memory one byte + * at a time, 16 bytes at a time or using the DC ZVA instruction to + * zero aligned block of bytes, which is assumed to be more than 16. + * In the case where the DC ZVA instruction cannot be used or if the + * first 16 bytes loop would overflow, there is fallback path that does + * not use DC ZVA. + * Note: The fallback path is also used by the zeromem function that + * branches to it directly. + * + * +---------+ zeromem_dczva + * | entry | + * +----+----+ + * | + * v + * +---------+ + * | checks |>o-------+ (If any check fails, fallback) + * +----+----+ | + * | |---------------+ + * v | Fallback path | + * +------+------+ |---------------+ + * | 1 byte loop | | + * +------+------+ .Lzeromem_dczva_initial_1byte_aligned_end + * | | + * v | + * +-------+-------+ | + * | 16 bytes loop | | + * +-------+-------+ | + * | | + * v | + * +------+------+ .Lzeromem_dczva_blocksize_aligned + * | DC ZVA loop | | + * +------+------+ | + * +--------+ | | + * | | | | + * | v v | + * | +-------+-------+ .Lzeromem_dczva_final_16bytes_aligned + * | | 16 bytes loop | | + * | +-------+-------+ | + * | | | + * | v | + * | +------+------+ .Lzeromem_dczva_final_1byte_aligned + * | | 1 byte loop | | + * | +-------------+ | + * | | | + * | v | + * | +---+--+ | + * | | exit | | + * | +------+ | + * | | + * | +--------------+ +------------------+ zeromem + * | | +----------------| zeromem function | + * | | | +------------------+ + * | v v + * | +-------------+ .Lzeromem_dczva_fallback_entry + * | | 1 byte loop | + * | +------+------+ + * | | + * +-----------+ + */ + + /* + * Readable names for registers + * + * Registers x0, x1 and x2 are also set by zeromem which + * branches into the fallback path directly, so cursor, length and + * stop_address should not be retargeted to other registers. + */ + cursor .req x0 /* Start address and then current address */ + length .req x1 /* Length in bytes of the region to zero out */ + /* Reusing x1 as length is never used after block_mask is set */ + block_mask .req x1 /* Bitmask of the block size read in DCZID_EL0 */ + stop_address .req x2 /* Address past the last zeroed byte */ + block_size .req x3 /* Size of a block in bytes as read in DCZID_EL0 */ + tmp1 .req x4 + tmp2 .req x5 + +#if ENABLE_ASSERTIONS + /* + * Check for M bit (MMU enabled) of the current SCTLR_EL(1|3) + * register value and panic if the MMU is disabled. + */ +#if defined(IMAGE_BL1) || defined(IMAGE_BL31) || \ + (defined(IMAGE_BL2) && BL2_AT_EL3) + mrs tmp1, sctlr_el3 +#else + mrs tmp1, sctlr_el1 +#endif + + tst tmp1, #SCTLR_M_BIT + ASM_ASSERT(ne) +#endif /* ENABLE_ASSERTIONS */ + + /* stop_address is the address past the last to zero */ + add stop_address, cursor, length + + /* + * Get block_size = (log2() >> 2) (see encoding of + * dczid_el0 reg) + */ + mrs block_size, dczid_el0 + + /* + * Select the 4 lowest bits and convert the extracted log2() to + */ + ubfx block_size, block_size, #0, #4 + mov tmp2, #(1 << 2) + lsl block_size, tmp2, block_size + +#if ENABLE_ASSERTIONS + /* + * Assumes block size is at least 16 bytes to avoid manual realignment + * of the cursor at the end of the DCZVA loop. + */ + cmp block_size, #16 + ASM_ASSERT(hs) +#endif + /* + * Not worth doing all the setup for a region less than a block and + * protects against zeroing a whole block when the area to zero is + * smaller than that. Also, as it is assumed that the block size is at + * least 16 bytes, this also protects the initial aligning loops from + * trying to zero 16 bytes when length is less than 16. + */ + cmp length, block_size + b.lo .Lzeromem_dczva_fallback_entry + + /* + * Calculate the bitmask of the block alignment. It will never + * underflow as the block size is between 4 bytes and 2kB. + * block_mask = block_size - 1 + */ + sub block_mask, block_size, #1 + + /* + * length alias should not be used after this point unless it is + * defined as a register other than block_mask's. + */ + .unreq length + + /* + * If the start address is already aligned to zero block size, go + * straight to the cache zeroing loop. This is safe because at this + * point, the length cannot be smaller than a block size. + */ + tst cursor, block_mask + b.eq .Lzeromem_dczva_blocksize_aligned + + /* + * Calculate the first block-size-aligned address. It is assumed that + * the zero block size is at least 16 bytes. This address is the last + * address of this initial loop. + */ + orr tmp1, cursor, block_mask + add tmp1, tmp1, #1 + + /* + * If the addition overflows, skip the cache zeroing loops. This is + * quite unlikely however. + */ + cbz tmp1, .Lzeromem_dczva_fallback_entry + + /* + * If the first block-size-aligned address is past the last address, + * fallback to the simpler code. + */ + cmp tmp1, stop_address + b.hi .Lzeromem_dczva_fallback_entry + + /* + * If the start address is already aligned to 16 bytes, skip this loop. + * It is safe to do this because tmp1 (the stop address of the initial + * 16 bytes loop) will never be greater than the final stop address. + */ + tst cursor, #0xf + b.eq .Lzeromem_dczva_initial_1byte_aligned_end + + /* Calculate the next address aligned to 16 bytes */ + orr tmp2, cursor, #0xf + add tmp2, tmp2, #1 + /* If it overflows, fallback to the simple path (unlikely) */ + cbz tmp2, .Lzeromem_dczva_fallback_entry + /* + * Next aligned address cannot be after the stop address because the + * length cannot be smaller than 16 at this point. + */ + + /* First loop: zero byte per byte */ +1: + strb wzr, [cursor], #1 + cmp cursor, tmp2 + b.ne 1b +.Lzeromem_dczva_initial_1byte_aligned_end: + + /* + * Second loop: we need to zero 16 bytes at a time from cursor to tmp1 + * before being able to use the code that deals with block-size-aligned + * addresses. + */ + cmp cursor, tmp1 + b.hs 2f +1: + stp xzr, xzr, [cursor], #16 + cmp cursor, tmp1 + b.lo 1b +2: + + /* + * Third loop: zero a block at a time using DC ZVA cache block zeroing + * instruction. + */ +.Lzeromem_dczva_blocksize_aligned: + /* + * Calculate the last block-size-aligned address. If the result equals + * to the start address, the loop will exit immediately. + */ + bic tmp1, stop_address, block_mask + + cmp cursor, tmp1 + b.hs 2f +1: + /* Zero the block containing the cursor */ + dc zva, cursor + /* Increment the cursor by the size of a block */ + add cursor, cursor, block_size + cmp cursor, tmp1 + b.lo 1b +2: + + /* + * Fourth loop: zero 16 bytes at a time and then byte per byte the + * remaining area + */ +.Lzeromem_dczva_final_16bytes_aligned: + /* + * Calculate the last 16 bytes aligned address. It is assumed that the + * block size will never be smaller than 16 bytes so that the current + * cursor is aligned to at least 16 bytes boundary. + */ + bic tmp1, stop_address, #15 + + cmp cursor, tmp1 + b.hs 2f +1: + stp xzr, xzr, [cursor], #16 + cmp cursor, tmp1 + b.lo 1b +2: + + /* Fifth and final loop: zero byte per byte */ +.Lzeromem_dczva_final_1byte_aligned: + cmp cursor, stop_address + b.eq 2f +1: + strb wzr, [cursor], #1 + cmp cursor, stop_address + b.ne 1b +2: + ret + + /* Fallback for unaligned start addresses */ +.Lzeromem_dczva_fallback_entry: + /* + * If the start address is already aligned to 16 bytes, skip this loop. + */ + tst cursor, #0xf + b.eq .Lzeromem_dczva_final_16bytes_aligned + + /* Calculate the next address aligned to 16 bytes */ + orr tmp1, cursor, #15 + add tmp1, tmp1, #1 + /* If it overflows, fallback to byte per byte zeroing */ + cbz tmp1, .Lzeromem_dczva_final_1byte_aligned + /* If the next aligned address is after the stop address, fall back */ + cmp tmp1, stop_address + b.hs .Lzeromem_dczva_final_1byte_aligned + + /* Fallback entry loop: zero byte per byte */ +1: + strb wzr, [cursor], #1 + cmp cursor, tmp1 + b.ne 1b + + b .Lzeromem_dczva_final_16bytes_aligned + + .unreq cursor + /* + * length is already unreq'ed to reuse the register for another + * variable. + */ + .unreq stop_address + .unreq block_size + .unreq block_mask + .unreq tmp1 + .unreq tmp2 +endfunc zeromem_dczva + +/* -------------------------------------------------------------------------- + * void memcpy16(void *dest, const void *src, unsigned int length) + * + * Copy length bytes from memory area src to memory area dest. + * The memory areas should not overlap. + * Destination and source addresses must be 16-byte aligned. + * -------------------------------------------------------------------------- + */ +func memcpy16 +#if ENABLE_ASSERTIONS + orr x3, x0, x1 + tst x3, #0xf + ASM_ASSERT(eq) +#endif +/* copy 16 bytes at a time */ +m_loop16: + cmp x2, #16 + b.lo m_loop1 + ldp x3, x4, [x1], #16 + stp x3, x4, [x0], #16 + sub x2, x2, #16 + b m_loop16 +/* copy byte per byte */ +m_loop1: + cbz x2, m_end + ldrb w3, [x1], #1 + strb w3, [x0], #1 + subs x2, x2, #1 + b.ne m_loop1 +m_end: + ret +endfunc memcpy16 + +/* --------------------------------------------------------------------------- + * Disable the MMU at EL3 + * --------------------------------------------------------------------------- + */ + +func disable_mmu_el3 + mov x1, #(SCTLR_M_BIT | SCTLR_C_BIT) +do_disable_mmu_el3: + mrs x0, sctlr_el3 + bic x0, x0, x1 + msr sctlr_el3, x0 + isb /* ensure MMU is off */ + dsb sy + ret +endfunc disable_mmu_el3 + + +func disable_mmu_icache_el3 + mov x1, #(SCTLR_M_BIT | SCTLR_C_BIT | SCTLR_I_BIT) + b do_disable_mmu_el3 +endfunc disable_mmu_icache_el3 + +/* --------------------------------------------------------------------------- + * Disable the MMU at EL1 + * --------------------------------------------------------------------------- + */ + +func disable_mmu_el1 + mov x1, #(SCTLR_M_BIT | SCTLR_C_BIT) +do_disable_mmu_el1: + mrs x0, sctlr_el1 + bic x0, x0, x1 + msr sctlr_el1, x0 + isb /* ensure MMU is off */ + dsb sy + ret +endfunc disable_mmu_el1 + + +func disable_mmu_icache_el1 + mov x1, #(SCTLR_M_BIT | SCTLR_C_BIT | SCTLR_I_BIT) + b do_disable_mmu_el1 +endfunc disable_mmu_icache_el1 + +/* --------------------------------------------------------------------------- + * Enable the use of VFP at EL3 + * --------------------------------------------------------------------------- + */ +#if SUPPORT_VFP +func enable_vfp + mrs x0, cpacr_el1 + orr x0, x0, #CPACR_VFP_BITS + msr cpacr_el1, x0 + mrs x0, cptr_el3 + mov x1, #AARCH64_CPTR_TFP + bic x0, x0, x1 + msr cptr_el3, x0 + isb + ret +endfunc enable_vfp +#endif diff --git a/arch/arm/armv8-a/src/arch_mm.c b/arch/arm/armv8-a/src/arch_mm.c new file mode 100644 index 000000000..c01f71f2b --- /dev/null +++ b/arch/arm/armv8-a/src/arch_mm.c @@ -0,0 +1,65 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include + +extern char __HEAP_START__; +extern char __HEAP_END__; + +/*! + * \brief Architecture memory manager context. + */ +static struct arch_mm_ctx { + uintptr_t heap_break; + uintptr_t heap_end; +} arch_mm_ctx = { + .heap_break = (uintptr_t)(&__HEAP_START__), + .heap_end = (uintptr_t)(&__HEAP_END__), +}; + +void *_sbrk(intptr_t increment) +{ + if (increment == 0) { + return (void *)arch_mm_ctx.heap_break; + } else { + uintptr_t heap_old = FWK_ALIGN_NEXT(arch_mm_ctx.heap_break, 16); + uintptr_t heap_new = heap_old + increment; + + if (heap_new > arch_mm_ctx.heap_end) { + return (void *)-1; + } else { + arch_mm_ctx.heap_break = heap_new; + + return (void *)heap_old; + } + } +} + +void *malloc(size_t size) +{ + void *mem = _sbrk(size); + + if (mem == ((void *)-1)) + mem = NULL; + + return mem; +} + +void *calloc(size_t nmemb, size_t size) +{ + void *mem = malloc(nmemb * size); + + if (mem) + memset(mem, 0, nmemb * size); + + return mem; +} diff --git a/tools/build_system/cpu.mk b/tools/build_system/cpu.mk index 518102939..3ad53e138 100644 --- a/tools/build_system/cpu.mk +++ b/tools/build_system/cpu.mk @@ -15,6 +15,9 @@ BS_ARCH_CPU := $(BS_FIRMWARE_CPU) # Supported ARMv7-M CPUs ARMV7M_CPUS := cortex-m3 cortex-m7 +# Supported ARMv8-A CPUs +ARMV8A_CPUS := cortex-a53 cortex-a57 cortex-a57.cortex-a53 + ifneq ($(findstring $(BS_FIRMWARE_CPU),$(ARMV7M_CPUS)),) BS_ARCH_VENDOR := arm BS_ARCH_ARCH := armv7-m @@ -25,6 +28,13 @@ ifneq ($(findstring $(BS_FIRMWARE_CPU),$(ARMV7M_CPUS)),) CFLAGS_CLANG += --target=arm-arm-none-eabi CFLAGS += -mfloat-abi=soft # No hardware floating point support +else ifneq ($(findstring $(BS_FIRMWARE_CPU),$(ARMV8A_CPUS)),) + BS_ARCH_VENDOR := arm + BS_ARCH_ARCH := armv8-a + + CFLAGS += -fno-builtin -mstrict-align + DEP_CFLAGS_GCC += -DAARCH64 + DEP_ASFLAGS_GCC += -D__ASSEMBLY__ else ifeq ($(BS_FIRMWARE_CPU),host) BS_ARCH_VENDOR := none BS_ARCH_ARCH := host diff --git a/tools/build_system/rules.mk b/tools/build_system/rules.mk index c8354a68d..c3bbcacd8 100644 --- a/tools/build_system/rules.mk +++ b/tools/build_system/rules.mk @@ -13,7 +13,9 @@ include $(BS_DIR)/cpu.mk ifeq ($(BUILD_HAS_MULTITHREADING),yes) # Add the OS directory to the main INCLUDES list - INCLUDES += $(OS_DIR)/Include + ifeq ($(findstring $(BS_FIRMWARE_CPU),$(ARMV8A_CPUS)),) + INCLUDES += $(OS_DIR)/Include + endif DEFINES += BUILD_HAS_MULTITHREADING endif @@ -148,7 +150,11 @@ LDFLAGS_GCC += -Wl,--cref LDFLAGS_GCC += -Wl,--undefined=arch_exceptions LDFLAGS_ARM += -Wl,--undefined=arch_exceptions -BUILTIN_LIBS_GCC := -lc -lgcc +ifneq ($(BS_ARCH_ARCH),armv8-a) + BUILTIN_LIBS_GCC := -lc -lgcc +else + BUILTIN_LIBS_GCC := -nostdlib +endif ifeq ($(MODE),release) O ?= $(DEFAULT_OPT_GCC_RELEASE) @@ -177,15 +183,24 @@ INCLUDES += $(ARCH_DIR)/include INCLUDES += $(ARCH_DIR)/$(BS_ARCH_VENDOR)/include INCLUDES += $(ARCH_DIR)/$(BS_ARCH_VENDOR)/$(BS_ARCH_ARCH)/include +ifeq ($(BS_ARCH_ARCH),armv8-a) + INCLUDES += $(ARCH_DIR)/$(BS_ARCH_VENDOR)/$(BS_ARCH_ARCH)/include/common + INCLUDES += $(ARCH_DIR)/$(BS_ARCH_VENDOR)/$(BS_ARCH_ARCH)/include/lib + INCLUDES += $(ARCH_DIR)/$(BS_ARCH_VENDOR)/$(BS_ARCH_ARCH)/include/lib/libc + INCLUDES += $(ARCH_DIR)/$(BS_ARCH_VENDOR)/$(BS_ARCH_ARCH)/include/lib/libc/aarch64 +endif + # # Always include the framework library # INCLUDES += $(FWK_DIR)/include # -# Always include CMSIS +# CMSIS library # -INCLUDES += $(CMSIS_DIR)/Include +ifneq ($(BS_ARCH_ARCH),armv8-a) + INCLUDES += $(CMSIS_DIR)/Include +endif # # Toolchain-independent flags @@ -201,6 +216,7 @@ ASFLAGS += $(ASFLAGS_GCC) ARFLAGS = $(ARFLAGS_GCC) LDFLAGS += $(LDFLAGS_$(BS_LINKER)) DEP_CFLAGS = $(DEP_CFLAGS_GCC) +DEP_ASFLAGS = $(DEP_ASFLAGS_GCC) BUILTIN_LIBS = $(BUILTIN_LIBS_$(BS_LINKER)) CFLAGS += $(CFLAGS_$(BS_COMPILER)) @@ -255,7 +271,7 @@ $(OBJ_DIR)/%.o: %.s | $$(@D)/ $(OBJ_DIR)/%.o: %.S | $$(@D)/ $(call show-action,AS,$<) - $(AS) -c $(CFLAGS) $(DEP_CFLAGS) $< -o $@ + $(AS) -c $(CFLAGS) $(DEP_CFLAGS) $(DEP_ASFLAGS) $< -o $@ $(BUILD_PATH)%/: $(call show-action,MD,$@) -- GitLab From 365cc91edecf87bc3d50ec68c518293fe3d683d1 Mon Sep 17 00:00:00 2001 From: Nicolas Royer Date: Sun, 27 Sep 2020 17:06:50 +0200 Subject: [PATCH 02/19] rcar: add rcar platform This patch adds support for the rcar platform running on FreeRTOS. FreeRTOS source code is temporarily included into rcar product directory until armv8-a support is added to CMSIS-FreeRTOS repository. Signed-off-by: Tsutomu Muroya Signed-off-by: Nicolas Royer --- product/rcar/include/rcar_common.h | 17 + product/rcar/include/rcar_core.h | 15 + product/rcar/include/rcar_def.h | 97 + product/rcar/include/rcar_irq.h | 31 + product/rcar/include/rcar_mfismh.h | 17 + product/rcar/include/rcar_mmap.h | 36 + product/rcar/include/rcar_mmap_scp.h | 14 + product/rcar/include/rcar_scmi.h | 25 + product/rcar/include/rcar_scmi_id.h | 46 + product/rcar/include/software_mmap.h | 129 + product/rcar/include/system_clock.h | 28 + product/rcar/include/system_mmap.h | 21 + product/rcar/include/system_mmap_scp.h | 16 + product/rcar/product.mk | 9 + product/rcar/scp_ramfw/FreeRTOSConfig.h | 49 + product/rcar/scp_ramfw/clock_devices.h | 208 + product/rcar/scp_ramfw/clock_mstp_devices.h | 169 + product/rcar/scp_ramfw/clock_sd_devices.h | 75 + product/rcar/scp_ramfw/config_clock.c | 1762 ++++++ product/rcar/scp_ramfw/config_scmi.c | 95 + product/rcar/scp_ramfw/config_scmi_apcore.c | 29 + product/rcar/scp_ramfw/config_scmi_clock.c | 978 +++ .../rcar/scp_ramfw/config_scmi_power_domain.c | 11 + product/rcar/scp_ramfw/config_sensor.c | 112 + product/rcar/scp_ramfw/config_smt.c | 65 + product/rcar/scp_ramfw/firmware.mk | 92 + product/rcar/scp_ramfw/fmw_memory.h | 23 + .../FreeRTOS/Source/cmsis_os2_tiny4scp.c | 535 ++ .../CMSIS-FreeRTOS/Source/include/FreeRTOS.h | 1295 ++++ .../Source/include/deprecated_definitions.h | 279 + .../src/CMSIS-FreeRTOS/Source/include/list.h | 412 ++ .../Source/include/mpu_wrappers.h | 189 + .../CMSIS-FreeRTOS/Source/include/portable.h | 199 + .../CMSIS-FreeRTOS/Source/include/projdefs.h | 124 + .../src/CMSIS-FreeRTOS/Source/include/queue.h | 1655 +++++ .../Source/include/stack_macros.h | 129 + .../src/CMSIS-FreeRTOS/Source/include/task.h | 2543 ++++++++ .../CMSIS-FreeRTOS/Source/include/timers.h | 1309 ++++ product/rcar/src/CMSIS-FreeRTOS/Source/list.c | 198 + .../portable/GCC/ARM_CA53_64_Rcar/port.c | 301 + .../portable/GCC/ARM_CA53_64_Rcar/portASM.S | 352 ++ .../portable/GCC/ARM_CA53_64_Rcar/portmacro.h | 117 + .../Source/portable/MemMang/heap_1.c | 146 + .../rcar/src/CMSIS-FreeRTOS/Source/queue.c | 2945 +++++++++ .../rcar/src/CMSIS-FreeRTOS/Source/tasks.c | 5310 +++++++++++++++++ .../rcar/src/CMSIS-FreeRTOS/Source/timers.c | 1127 ++++ product/rcar/src/rcar_core.c | 23 + tools/build_system/firmware.mk | 19 +- 48 files changed, 23372 insertions(+), 4 deletions(-) create mode 100644 product/rcar/include/rcar_common.h create mode 100644 product/rcar/include/rcar_core.h create mode 100644 product/rcar/include/rcar_def.h create mode 100644 product/rcar/include/rcar_irq.h create mode 100644 product/rcar/include/rcar_mfismh.h create mode 100644 product/rcar/include/rcar_mmap.h create mode 100644 product/rcar/include/rcar_mmap_scp.h create mode 100644 product/rcar/include/rcar_scmi.h create mode 100644 product/rcar/include/rcar_scmi_id.h create mode 100644 product/rcar/include/software_mmap.h create mode 100644 product/rcar/include/system_clock.h create mode 100644 product/rcar/include/system_mmap.h create mode 100644 product/rcar/include/system_mmap_scp.h create mode 100644 product/rcar/product.mk create mode 100644 product/rcar/scp_ramfw/FreeRTOSConfig.h create mode 100644 product/rcar/scp_ramfw/clock_devices.h create mode 100644 product/rcar/scp_ramfw/clock_mstp_devices.h create mode 100644 product/rcar/scp_ramfw/clock_sd_devices.h create mode 100644 product/rcar/scp_ramfw/config_clock.c create mode 100644 product/rcar/scp_ramfw/config_scmi.c create mode 100644 product/rcar/scp_ramfw/config_scmi_apcore.c create mode 100644 product/rcar/scp_ramfw/config_scmi_clock.c create mode 100644 product/rcar/scp_ramfw/config_scmi_power_domain.c create mode 100644 product/rcar/scp_ramfw/config_sensor.c create mode 100644 product/rcar/scp_ramfw/config_smt.c create mode 100644 product/rcar/scp_ramfw/firmware.mk create mode 100644 product/rcar/scp_ramfw/fmw_memory.h create mode 100644 product/rcar/src/CMSIS-FreeRTOS/CMSIS/RTOS2/FreeRTOS/Source/cmsis_os2_tiny4scp.c create mode 100644 product/rcar/src/CMSIS-FreeRTOS/Source/include/FreeRTOS.h create mode 100644 product/rcar/src/CMSIS-FreeRTOS/Source/include/deprecated_definitions.h create mode 100644 product/rcar/src/CMSIS-FreeRTOS/Source/include/list.h create mode 100644 product/rcar/src/CMSIS-FreeRTOS/Source/include/mpu_wrappers.h create mode 100644 product/rcar/src/CMSIS-FreeRTOS/Source/include/portable.h create mode 100644 product/rcar/src/CMSIS-FreeRTOS/Source/include/projdefs.h create mode 100644 product/rcar/src/CMSIS-FreeRTOS/Source/include/queue.h create mode 100644 product/rcar/src/CMSIS-FreeRTOS/Source/include/stack_macros.h create mode 100644 product/rcar/src/CMSIS-FreeRTOS/Source/include/task.h create mode 100644 product/rcar/src/CMSIS-FreeRTOS/Source/include/timers.h create mode 100644 product/rcar/src/CMSIS-FreeRTOS/Source/list.c create mode 100644 product/rcar/src/CMSIS-FreeRTOS/Source/portable/GCC/ARM_CA53_64_Rcar/port.c create mode 100644 product/rcar/src/CMSIS-FreeRTOS/Source/portable/GCC/ARM_CA53_64_Rcar/portASM.S create mode 100644 product/rcar/src/CMSIS-FreeRTOS/Source/portable/GCC/ARM_CA53_64_Rcar/portmacro.h create mode 100644 product/rcar/src/CMSIS-FreeRTOS/Source/portable/MemMang/heap_1.c create mode 100644 product/rcar/src/CMSIS-FreeRTOS/Source/queue.c create mode 100644 product/rcar/src/CMSIS-FreeRTOS/Source/tasks.c create mode 100644 product/rcar/src/CMSIS-FreeRTOS/Source/timers.c create mode 100644 product/rcar/src/rcar_core.c diff --git a/product/rcar/include/rcar_common.h b/product/rcar/include/rcar_common.h new file mode 100644 index 000000000..8bca19e63 --- /dev/null +++ b/product/rcar/include/rcar_common.h @@ -0,0 +1,17 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RCAR_COMMON_H +#define RCAR_COMMON_H + +void mstpcr_write(uint32_t mstpcr, uint32_t mstpsr, uint32_t target_bit); +void cpg_write(uintptr_t regadr, uint32_t regval); + +void udelay(unsigned long usec); +void mdelay(unsigned long msecs); + +#endif /* RCAR_COMMON_H */ diff --git a/product/rcar/include/rcar_core.h b/product/rcar/include/rcar_core.h new file mode 100644 index 000000000..0c5e0eb0e --- /dev/null +++ b/product/rcar/include/rcar_core.h @@ -0,0 +1,15 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RCAR_CORE_H +#define RCAR_CORE_H + +#define RCAR_CORE_PER_CLUSTER_MAX 8 + +unsigned int rcar_core_get_count(void); + +#endif /* RCAR_CORE_H */ diff --git a/product/rcar/include/rcar_def.h b/product/rcar/include/rcar_def.h new file mode 100644 index 000000000..6b225ad23 --- /dev/null +++ b/product/rcar/include/rcar_def.h @@ -0,0 +1,97 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RCAR_DEF_H +#define RCAR_DEF_H + +/* Reset */ +#define RCAR_CPGWPR UINT32_C(0xE6150900) /* CPG write protect */ +#define RCAR_MODEMR UINT32_C(0xE6160060) /* Mode pin */ +#define RCAR_CA57RESCNT UINT32_C(0xE6160040) /* Reset control A57 */ +#define RCAR_CA53RESCNT UINT32_C(0xE6160044) /* Reset control A53 */ +#define RCAR_SRESCR UINT32_C(0xE6160110) /* Soft Power On Reset */ +#define RCAR_CA53WUPCR UINT32_C(0xE6151010) /* Wake-up control A53 */ +#define RCAR_CA57WUPCR UINT32_C(0xE6152010) /* Wake-up control A57 */ +#define RCAR_CA53PSTR UINT32_C(0xE6151040) /* Power status A53 */ +#define RCAR_CA57PSTR UINT32_C(0xE6152040) /* Power status A57 */ +#define RCAR_CA53CPU0CR UINT32_C(0xE6151100) /* CPU control A53 */ +#define RCAR_CA57CPU0CR UINT32_C(0xE6152100) /* CPU control A57 */ +#define RCAR_CA53CPUCMCR UINT32_C(0xE6151184) /* Common power A53 */ +#define RCAR_CA57CPUCMCR UINT32_C(0xE6152184) /* Common power A57 */ +#define RCAR_WUPMSKCA57 UINT32_C(0xE6180014) /* Wake-up mask A57 */ +#define RCAR_WUPMSKCA53 UINT32_C(0xE6180018) /* Wake-up mask A53 */ + +/* SYSC */ +#define RCAR_PWRSR3 UINT32_C(0xE6180140) /* Power stat A53-SCU */ +#define RCAR_PWRSR5 UINT32_C(0xE61801C0) /* Power stat A57-SCU */ +#define RCAR_SYSCIER UINT32_C(0xE618000C) /* Interrupt enable */ +#define RCAR_SYSCIMR UINT32_C(0xE6180010) /* Interrupt mask */ +#define RCAR_SYSCSR UINT32_C(0xE6180000) /* SYSC status */ +#define RCAR_PWRONCR3 UINT32_C(0xE618014C) /* Power resume A53-SCU */ +#define RCAR_PWRONCR5 UINT32_C(0xE61801CC) /* Power resume A57-SCU */ +#define RCAR_PWROFFCR3 UINT32_C(0xE6180144) /* Power shutof A53-SCU */ +#define RCAR_PWROFFCR5 UINT32_C(0xE61801C4) /* Power shutof A57-SCU */ +#define RCAR_PWRER3 UINT32_C(0xE6180154) /* shutoff/resume error */ +#define RCAR_PWRER5 UINT32_C(0xE61801D4) /* shutoff/resume error */ +#define RCAR_SYSCISR UINT32_C(0xE6180004) /* Interrupt status */ +#define RCAR_SYSCISCR UINT32_C(0xE6180008) /* Interrupt stat clear */ + +/* Product register */ +#define RCAR_PRR UINT32_C(0xFFF00044) +#define RCAR_PRODUCT_MASK UINT32_C(0x00007F00) +#define RCAR_CUT_MASK UINT32_C(0x000000FF) +#define RCAR_PRODUCT_H3 UINT32_C(0x00004F00) +#define RCAR_PRODUCT_M3 UINT32_C(0x00005200) +#define RCAR_PRODUCT_M3N UINT32_C(0x00005500) +#define RCAR_PRODUCT_E3 UINT32_C(0x00005700) +#define RCAR_CUT_VER10 UINT32_C(0x00000000) +#define RCAR_CUT_VER11 UINT32_C(0x00000001) /* H3/M3N/E3 Ver.1.1 */ +#define RCAR_M3_CUT_VER11 UINT32_C(0x00000010) /* M3 Ver.1.1/Ver.1.2 */ +#define RCAR_CUT_VER20 UINT32_C(0x00000010) +#define RCAR_CUT_VER30 UINT32_C(0x00000020) +#define RCAR_MAJOR_MASK UINT32_C(0x000000F0) +#define RCAR_MINOR_MASK UINT32_C(0x0000000F) +#define RCAR_PRODUCT_SHIFT UINT32_C(8) +#define RCAR_MAJOR_SHIFT UINT32_C(4) +#define RCAR_MINOR_SHIFT UINT32_C(0) +#define RCAR_MAJOR_OFFSET UINT32_C(1) +#define RCAR_M3_MINOR_OFFSET UINT32_C(2) +#define RCAR_PRODUCT_H3_CUT10 (RCAR_PRODUCT_H3 | UINT32_C(0x00)) /* 1.0 */ +#define RCAR_PRODUCT_H3_CUT11 (RCAR_PRODUCT_H3 | UINT32_C(0x01)) /* 1.1 */ +#define RCAR_PRODUCT_H3_CUT20 (RCAR_PRODUCT_H3 | UINT32_C(0x10)) /* 2.0 */ +#define RCAR_PRODUCT_M3_CUT10 (RCAR_PRODUCT_M3 | UINT32_C(0x00)) /* 1.0 */ +#define RCAR_PRODUCT_M3_CUT11 (RCAR_PRODUCT_M3 | UINT32_C(0x10)) +#define RCAR_CPU_MASK_CA57 UINT32_C(0x80000000) +#define RCAR_CPU_MASK_CA53 UINT32_C(0x04000000) +#define RCAR_CPU_HAVE_CA57 UINT32_C(0x00000000) +#define RCAR_CPU_HAVE_CA53 UINT32_C(0x00000000) +#define RCAR_SSCG_MASK UINT32_C(0x1000) /* MD12 */ +#define RCAR_SSCG_ENABLE UINT32_C(0x1000) + +/* MD pin information */ +#define MODEMR_BOOT_PLL_MASK UINT32_C(0x00006000) +#define MODEMR_BOOT_PLL_SHIFT UINT32_C(13) +/* MODEMR PLL masks and bitfield values */ +#define CHECK_MD13_MD14 UINT32_C(0x6000) +#define MD14_MD13_TYPE_0 UINT32_C(0x0000) /* MD14=0 MD13=0 */ +#define MD14_MD13_TYPE_1 UINT32_C(0x2000) /* MD14=0 MD13=1 */ +#define MD14_MD13_TYPE_2 UINT32_C(0x4000) /* MD14=1 MD13=0 */ +#define MD14_MD13_TYPE_3 UINT32_C(0x6000) /* MD14=1 MD13=1 */ +/* Frequency of EXTAL(Hz) */ +#define EXTAL_MD14_MD13_TYPE_0 UINT32_C(8333300) /* MD14=0 MD13=0 */ +#define EXTAL_MD14_MD13_TYPE_1 UINT32_C(10000000) /* MD14=0 MD13=1 */ +#define EXTAL_MD14_MD13_TYPE_2 UINT32_C(12500000) /* MD14=1 MD13=0 */ +#define EXTAL_MD14_MD13_TYPE_3 UINT32_C(16666600) /* MD14=1 MD13=1 */ +#define EXTAL_SALVATOR_XS UINT32_C(8320000) /* Salvator-XS */ +#define EXTAL_EBISU UINT32_C(24000000) /* Ebisu */ + +/* CPG Secure Module Stop Control Register 9 */ +#define SCMSTPCR9 (CPG_BASE + 0x0B44U) +/* CPG stop status 9 */ +#define CPG_MSTPSR9 (CPG_BASE + 0x09A4U) + +#endif /* RCAR_DEF_H */ diff --git a/product/rcar/include/rcar_irq.h b/product/rcar/include/rcar_irq.h new file mode 100644 index 000000000..e591271a2 --- /dev/null +++ b/product/rcar/include/rcar_irq.h @@ -0,0 +1,31 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RCAR_IRQ_H +#define RCAR_IRQ_H + +#include + +enum rcar_irq { + /**/ + MIN_IRQ = 16, + VIRTUAL_TIMER_IRQ = 27, + /**/ + MFIS_AREICR1_IRQ = 257, + MFIS_AREICR2_IRQ = 258, + MFIS_AREICR3_IRQ = 259, + /**//* The following numbers are virtual IID */ + SMCMH_IRQ_START = 1024, + SMCMH_SECURE_IRQ = SMCMH_IRQ_START, /* SMC Secure */ + SMCMH_LOW_PRIO_IRQ, /* SMC Low Priority */ + SMCMH_HIGH_PRIO_IRQ, /* SMC High Priority */ + SMCMH_IRQ_END, + MAX_IRQ = SMCMH_IRQ_END, + /**/ +}; + +#endif /* RCAR_IRQ_H */ diff --git a/product/rcar/include/rcar_mfismh.h b/product/rcar/include/rcar_mfismh.h new file mode 100644 index 000000000..b0397d2a1 --- /dev/null +++ b/product/rcar/include/rcar_mfismh.h @@ -0,0 +1,17 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RCAR_MFISMH_H +#define RCAR_MFISMH_H + +enum rcar_smcmh_device_idx { + RCAR_MFISMH_DEVICE_IDX_S, + RCAR_MFISMH_DEVICE_IDX_NS_L, + RCAR_MFISMH_DEVICE_IDX_COUNT, +}; + +#endif /* RCAR_MFISMH_H */ diff --git a/product/rcar/include/rcar_mmap.h b/product/rcar/include/rcar_mmap.h new file mode 100644 index 000000000..29b06a2bb --- /dev/null +++ b/product/rcar/include/rcar_mmap.h @@ -0,0 +1,36 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RCAR_MMAP_H +#define RCAR_MMAP_H + +#include +#include + +/* + * Top-level base addresses + */ +#define PERIPHERAL_BASE UINT64_C(0xE6000000) + +/* + * Peripherals + */ +#define CPG_BASE (PERIPHERAL_BASE + 0x00150000) +#define MFIS_BASE (PERIPHERAL_BASE + 0x00260000) + +#define MFISAREICR_BASE (MFIS_BASE + 0x0400) + +/* Memory */ +#define TRUSTED_RAM_BASE UINT64_C(0x040000000) +#define NONTRUSTED_RAM_BASE UINT64_C(0x040002000) + +#define SCP_SRAM_BASE (0xE6302000U) +#define SCP_SRAM_SIZE (0x00001000U) +#define SCP_SRAM_STACK_BASE (SCP_SRAM_BASE + SCP_SRAM_SIZE) +#define SCP_SRAM_STACK_SIZE (0x00001000U) + +#endif /* RCAR_MMAP_H */ diff --git a/product/rcar/include/rcar_mmap_scp.h b/product/rcar/include/rcar_mmap_scp.h new file mode 100644 index 000000000..178ff3db1 --- /dev/null +++ b/product/rcar/include/rcar_mmap_scp.h @@ -0,0 +1,14 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RCAR_MMAP_SCP_H +#define RCAR_MMAP_SCP_H + +#define SCP_ROM_BASE 0x00000000 +#define SCP_RAM_BASE 0x44200000 + +#endif /* RCAR_MMAP_SCP_H */ diff --git a/product/rcar/include/rcar_scmi.h b/product/rcar/include/rcar_scmi.h new file mode 100644 index 000000000..cf4773dd1 --- /dev/null +++ b/product/rcar/include/rcar_scmi.h @@ -0,0 +1,25 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RCAR_SCMI_H +#define RCAR_SCMI_H + +/* SCMI agent identifiers */ +enum rcar_scmi_agent_id { + /* 0 is reserved for the platform */ + SCMI_AGENT_ID_OSPM = 1, + SCMI_AGENT_ID_PSCI, + SCMI_AGENT_ID_COUNT, +}; + +/* SCMI service indexes */ +enum rcar_scmi_service_idx { + RCAR_SCMI_SERVICE_IDX_PSCI, + RCAR_SCMI_SERVICE_IDX_OSPM_0, + RCAR_SCMI_SERVICE_IDX_COUNT, +}; +#endif /* RCAR_SCMI_H */ diff --git a/product/rcar/include/rcar_scmi_id.h b/product/rcar/include/rcar_scmi_id.h new file mode 100644 index 000000000..39784001d --- /dev/null +++ b/product/rcar/include/rcar_scmi_id.h @@ -0,0 +1,46 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RCAR_SCMI_ID_H +#define RCAR_SCMI_ID_H + +/* SCMI PowerDomain Domain indexes */ +enum rcar_scmi_pd_domain_id { + PD_RCAR_CLUS0CORE0 = 0, + PD_RCAR_CLUS0CORE1, + PD_RCAR_CLUS0CORE2, + PD_RCAR_CLUS0CORE3, + PD_RCAR_CLUS1CORE0, + PD_RCAR_CLUS1CORE1, + PD_RCAR_CLUS1CORE2, + PD_RCAR_CLUS1CORE3, + PD_RCAR_CLUSTER0, + PD_RCAR_CLUSTER1, + PD_RCAR_A3IR, + PD_RCAR_3DGE, + PD_RCAR_3DGD, + PD_RCAR_3DGC, + PD_RCAR_3DGB, + PD_RCAR_3DGA, + PD_RCAR_A2VC1, + PD_RCAR_A3VC, + PD_RCAR_CR7, + PD_RCAR_A3VP, + PD_RCAR_COUNT, +}; + +/* SCMI Clock indexes */ +enum rcar_scmi_clock_id { + CLOCK_RCAR_COUNT, +}; + +/* SCMI Sensor indexes */ +enum rcar_scmi_sensor_id { + SENSOR_RCAR_COUNT, +}; + +#endif /* RCAR_SCMI_ID_H */ diff --git a/product/rcar/include/software_mmap.h b/product/rcar/include/software_mmap.h new file mode 100644 index 000000000..b8e7c0c3a --- /dev/null +++ b/product/rcar/include/software_mmap.h @@ -0,0 +1,129 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SOFTWARE_MMAP_H +#define SOFTWARE_MMAP_H + +#include +#include + +/* + * The 4KiB AP/SCP Shared memory at the base of Trusted SRAM is used for several + * purposes. These are: the Shared Data Storage (SDS) Memory Region, the SCMI + * secure payload areas, and the context area for Application Processor + * firmware. + * + * Shared Data Storage (SDS) Memory Region: Used for structured storage of data + * that is shared between SCP Firmware and Application Processor firmware. The + * SDS Memory Region occupies the area between the context region base and + * the SCMI Secure Payload base. + * + * SCMI Secure Payload Areas: Storage for SCMI message contents in both the + * Agent->Platform and Platform->Agent directions. + * + * Application Processor Context Area: The usage of this area is defined by the + * firmware running on the Application Processors. The SCP Firmware must zero + * this memory before releasing any Application Processors. This area must + * always be located in the top 64 bytes of the 4KiB reserved region. + * + * +-----------------------+ 4096 + * | | + * 64B | AP Context Area | + * | | + * +-----------------------+ + * | | + * 256B | Unused | + * | | + * +-----------------------+ + * | | + * | SCMI Sec. Payload | + * 128B | Platform to Agent | + * | | + * +-----------------------+ + * | | + * 128B | SCMI Sec. Payload | + * | Agent to Platform | + * | | + * +-----------------------+ + * | | + * 3520B | SDS Memory Region | + * | | + * +-----------------------+ 0 + */ + +/* Secure shared memory at the base of Trusted SRAM */ +#define SHARED_SECURE_BASE (TRUSTED_RAM_BASE) +#define SHARED_SECURE_SIZE (4 * FWK_KIB) + +/* SDS Memory Region */ +#define SDS_MEM_BASE (SHARED_SECURE_BASE) +#if 0 /* Tentative */ +#define SDS_MEM_SIZE (3520) +#else +#define SDS_MEM_SIZE (0) +#endif + +/* AP Context Area */ +#define AP_CONTEXT_BASE (SHARED_SECURE_BASE + SHARED_SECURE_SIZE - \ + AP_CONTEXT_SIZE) +#define AP_CONTEXT_SIZE (64) + +/* SCMI Secure Payload Areas */ +#define SCMI_PAYLOAD_SIZE (128) +#define SCMI_PAYLOAD_S_A2P_BASE (SDS_MEM_BASE + SDS_MEM_SIZE) +#define SCMI_PAYLOAD_S_P2A_BASE (SCMI_PAYLOAD_S_A2P_BASE + SCMI_PAYLOAD_SIZE) + +/* + * The 4KiB AP/SCP Shared memory at the base of Non-trusted SRAM is used for the + * SCMI non-secure payload areas. + * + * Two SCMI non-Secure Payload Areas: Storage for SCMI message contents in both + * the Agent->Platform and Platform->Agent directions. + * + * +-----------------------+ 4096 + * 3584B | Unused | + * +-----------------------+ + * | | + * | Non-Sec. Channel 1 | + * | SCMI non-Sec. Payload | + * 128B | Platform to Agent | + * | | + * +-----------------------+ + * | | + * | Non-Sec. Channel 1 | + * 128B | SCMI non-Sec. Payload | + * | Agent to Platform | + * | | + * +-----------------------+ + * | | + * | Non-Sec. Channel 0 | + * | SCMI non-Sec. Payload | + * 128B | Platform to Agent | + * | | + * +-----------------------+ + * | | + * | Non-Sec. Channel 0 | + * 128B | SCMI non-Sec. Payload | + * | Agent to Platform | + * | | + * +-----------------------+ 0 + */ + +/* Non-secure shared memory at the base of Non-trusted SRAM */ +#define SHARED_NONSECURE_BASE (NONTRUSTED_RAM_BASE) +#define SHARED_NONSECURE_SIZE (4 * FWK_KIB) + +/* SCMI Non-Secure Payload Areas */ +#define SCMI_PAYLOAD0_NS_A2P_BASE (SHARED_NONSECURE_BASE) +#define SCMI_PAYLOAD0_NS_P2A_BASE (SCMI_PAYLOAD0_NS_A2P_BASE + \ + SCMI_PAYLOAD_SIZE) +#define SCMI_PAYLOAD1_NS_A2P_BASE (SCMI_PAYLOAD0_NS_P2A_BASE + \ + SCMI_PAYLOAD_SIZE) +#define SCMI_PAYLOAD1_NS_P2A_BASE (SCMI_PAYLOAD1_NS_A2P_BASE + \ + SCMI_PAYLOAD_SIZE) + +#endif /* SOFTWARE_MMAP_H */ diff --git a/product/rcar/include/system_clock.h b/product/rcar/include/system_clock.h new file mode 100644 index 000000000..2dc3ddda1 --- /dev/null +++ b/product/rcar/include/system_clock.h @@ -0,0 +1,28 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SYSTEM_CLOCK_H +#define SYSTEM_CLOCK_H + +#include + +/*! + * \brief Calculates the necessary divider for obtaining a target frequency + * from a given clock. + * + * \param CLOCK_RATE The tick rate of the clock to be divided. + * + * \param TARGET_FREQ The target frequency to be obtained by the division. + * + * \return The divider needed to obtain TARGET_FREQ from CLOCK_RATE. + */ +#define DIV_FROM_CLOCK(CLOCK_RATE, TARGET_FREQ) ((CLOCK_RATE) / (TARGET_FREQ)) + +#define CLOCK_RATE_REFCLK (50UL * FWK_MHZ) +#define CLOCK_RATE_SYSPLLCLK (2000UL * FWK_MHZ) + +#endif /* SYSTEM_CLOCK_H */ diff --git a/product/rcar/include/system_mmap.h b/product/rcar/include/system_mmap.h new file mode 100644 index 000000000..117988b66 --- /dev/null +++ b/product/rcar/include/system_mmap.h @@ -0,0 +1,21 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SYSTEM_MMAP_H +#define SYSTEM_MMAP_H + +#include + +#define BOARD_UART1_BASE (PERIPHERAL_BASE + 0xe68000) +#define BOARD_UART2_BASE (PERIPHERAL_BASE + 0xe88000) + +#define SENSOR_SOC_TEMP1 (PERIPHERAL_BASE + 0x198000) +#define SENSOR_SOC_TEMP2 (PERIPHERAL_BASE + 0x1a0000) +#define SENSOR_SOC_TEMP3 (PERIPHERAL_BASE + 0x1a8000) +#define SENSOR_SOC_TEMP (SENSOR_SOC_TEMP1) + +#endif /* SYSTEM_MMAP_H */ diff --git a/product/rcar/include/system_mmap_scp.h b/product/rcar/include/system_mmap_scp.h new file mode 100644 index 000000000..4a9a94aa2 --- /dev/null +++ b/product/rcar/include/system_mmap_scp.h @@ -0,0 +1,16 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SYSTEM_MMAP_SCP_H +#define SYSTEM_MMAP_SCP_H + +#include + +#define SCP_ROM_SIZE (64 * 1024) /* for SCP romfw */ +#define SCP_RAM_SIZE (256 * 1024) /* for SCP ramfw */ + +#endif /* SYSTEM_MMAP_SCP_H */ diff --git a/product/rcar/product.mk b/product/rcar/product.mk new file mode 100644 index 000000000..b5f86297e --- /dev/null +++ b/product/rcar/product.mk @@ -0,0 +1,9 @@ +# +# Renesas SCP/MCP Software +# Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +BS_PRODUCT_NAME := rcar +BS_FIRMWARE_LIST := scp_ramfw diff --git a/product/rcar/scp_ramfw/FreeRTOSConfig.h b/product/rcar/scp_ramfw/FreeRTOSConfig.h new file mode 100644 index 000000000..464b762d6 --- /dev/null +++ b/product/rcar/scp_ramfw/FreeRTOSConfig.h @@ -0,0 +1,49 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + +#define configUSE_IDLE_HOOK 1 +#define configUSE_TICK_HOOK 0 +#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 200 ) +#define configTOTAL_HEAP_SIZE ( (size_t) ( 20 * 1024) ) + +/* Software timer definitions. */ +#define configUSE_TIMERS 1 +#define configTIMER_TASK_PRIORITY ( configMAX_PRIORITIES - 1 ) +#define configTIMER_QUEUE_LENGTH 5 +#define configTIMER_TASK_STACK_DEPTH ( configMINIMAL_STACK_SIZE * 2 ) + +/* Set the following definitions to 1 to include the API function, or zero +to exclude the API function. */ +#define INCLUDE_vTaskDelay 1 + +#define INCLUDE_xSemaphoreGetMutexHolder 1 + +void vConfigureTickInterrupt( void ); +#define configSETUP_TICK_INTERRUPT() vConfigureTickInterrupt() +void vClearTickInterrupt( void ); +#define configCLEAR_TICK_INTERRUPT() vClearTickInterrupt() + +/* Defines needed by FreeRTOS to implement CMSIS RTOS2 API. Do not change! */ +#define configTICK_RATE_HZ ( ( TickType_t ) 1000/* 1ms */ ) +#define configSUPPORT_STATIC_ALLOCATION 1 +#define configSUPPORT_DYNAMIC_ALLOCATION 1 +#define configUSE_PREEMPTION 0 +#define configUSE_TIMERS 1 +#define configUSE_MUTEXES 1 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configUSE_TASK_NOTIFICATIONS 1 +#define configUSE_TRACE_FACILITY 1 +#define configUSE_16_BIT_TICKS 0 +#define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 +#define configMAX_PRIORITIES 56 +#define configUSE_TASK_NOTIFICATIONS 1 +#define configUSE_OS2_THREAD_FLAGS 1 +#endif /* FREERTOS_CONFIG_H */ diff --git a/product/rcar/scp_ramfw/clock_devices.h b/product/rcar/scp_ramfw/clock_devices.h new file mode 100644 index 000000000..77c040386 --- /dev/null +++ b/product/rcar/scp_ramfw/clock_devices.h @@ -0,0 +1,208 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef CLOCK_DEVICES_H +#define CLOCK_DEVICES_H + +/*! + * \brief Clock device indexes. + */ +enum clock_dev_idx { + CLOCK_DEV_IDX_BIG, + CLOCK_DEV_IDX_LITTLE, + CLOCK_DEV_IDX_ZTR, + CLOCK_DEV_IDX_ZTRD2, + CLOCK_DEV_IDX_ZT, + CLOCK_DEV_IDX_ZX, + CLOCK_DEV_IDX_S0D1, + CLOCK_DEV_IDX_S0D2, + CLOCK_DEV_IDX_S0D3, + CLOCK_DEV_IDX_S0D4, + CLOCK_DEV_IDX_S0D6, + CLOCK_DEV_IDX_S0D8, + CLOCK_DEV_IDX_S0D12, + CLOCK_DEV_IDX_S1D1, + CLOCK_DEV_IDX_S1D2, + CLOCK_DEV_IDX_S1D4, + CLOCK_DEV_IDX_S2D1, + CLOCK_DEV_IDX_S2D2, + CLOCK_DEV_IDX_S2D4, + CLOCK_DEV_IDX_S3D1, + CLOCK_DEV_IDX_S3D2, + CLOCK_DEV_IDX_S3D4, + CLOCK_DEV_IDX_SD0, + CLOCK_DEV_IDX_SD1, + CLOCK_DEV_IDX_SD2, + CLOCK_DEV_IDX_SD3, + CLOCK_DEV_IDX_CL, + CLOCK_DEV_IDX_CR, + CLOCK_DEV_IDX_CP, + CLOCK_DEV_IDX_CPEX, + CLOCK_DEV_IDX_CANFD, + CLOCK_DEV_IDX_CSI0, + CLOCK_DEV_IDX_MSO, + CLOCK_DEV_IDX_HDMI, + CLOCK_DEV_IDX_OSC, + CLOCK_DEV_IDX_R, + CLOCK_DEV_IDX_S0, + CLOCK_DEV_IDX_S1, + CLOCK_DEV_IDX_S2, + CLOCK_DEV_IDX_S3, + CLOCK_DEV_IDX_SDSRC, + CLOCK_DEV_IDX_RINT, + CLOCK_DEV_IDX_FDP1_1, + CLOCK_DEV_IDX_FDP1_0, + CLOCK_DEV_IDX_SCIF5, + CLOCK_DEV_IDX_SCIF4, + CLOCK_DEV_IDX_SCIF3, + CLOCK_DEV_IDX_SCIF1, + CLOCK_DEV_IDX_SCIF0, + CLOCK_DEV_IDX_MSIOF3, + CLOCK_DEV_IDX_MSIOF2, + CLOCK_DEV_IDX_MSIOF1, + CLOCK_DEV_IDX_MSIOF0, + CLOCK_DEV_IDX_SYS_DMAC2, + CLOCK_DEV_IDX_SYS_DMAC1, + CLOCK_DEV_IDX_SYS_DMAC0, + CLOCK_DEV_IDX_SCEG_PUB, + CLOCK_DEV_IDX_CMT3, + CLOCK_DEV_IDX_CMT2, + CLOCK_DEV_IDX_CMT1, + CLOCK_DEV_IDX_CMT0, + CLOCK_DEV_IDX_TPU0, + CLOCK_DEV_IDX_SCIF2, + CLOCK_DEV_IDX_SDIF3, + CLOCK_DEV_IDX_SDIF2, + CLOCK_DEV_IDX_SDIF1, + CLOCK_DEV_IDX_SDIF0, + CLOCK_DEV_IDX_PCIE1, + CLOCK_DEV_IDX_PCIE0, + CLOCK_DEV_IDX_USB_DMAC30, + CLOCK_DEV_IDX_USB3_IF0, + CLOCK_DEV_IDX_USB_DMAC31, + CLOCK_DEV_IDX_USB_DMAC0, + CLOCK_DEV_IDX_USB_DMAC1, + CLOCK_DEV_IDX_RWDT, + CLOCK_DEV_IDX_INTC_EX, + CLOCK_DEV_IDX_INTC_AP, + CLOCK_DEV_IDX_AUDMAC1, + CLOCK_DEV_IDX_AUDMAC0, + CLOCK_DEV_IDX_DRIF31, + CLOCK_DEV_IDX_DRIF30, + CLOCK_DEV_IDX_DRIF21, + CLOCK_DEV_IDX_DRIF20, + CLOCK_DEV_IDX_DRIF11, + CLOCK_DEV_IDX_DRIF10, + CLOCK_DEV_IDX_DRIF01, + CLOCK_DEV_IDX_DRIF00, + CLOCK_DEV_IDX_HSCIF4, + CLOCK_DEV_IDX_HSCIF3, + CLOCK_DEV_IDX_HSCIF2, + CLOCK_DEV_IDX_HSCIF1, + CLOCK_DEV_IDX_HSCIF0, + CLOCK_DEV_IDX_THERMAL, + CLOCK_DEV_IDX_PWM, + CLOCK_DEV_IDX_FCPVD2, + CLOCK_DEV_IDX_FCPVD1, + CLOCK_DEV_IDX_FCPVD0, + CLOCK_DEV_IDX_FCPVB1, + CLOCK_DEV_IDX_FCPVB0, + CLOCK_DEV_IDX_FCPVI1, + CLOCK_DEV_IDX_FCPVI0, + CLOCK_DEV_IDX_FCPF1, + CLOCK_DEV_IDX_FCPF0, + CLOCK_DEV_IDX_FCPCS, + CLOCK_DEV_IDX_VSPD2, + CLOCK_DEV_IDX_VSPD1, + CLOCK_DEV_IDX_VSPD0, + CLOCK_DEV_IDX_VSPBC, + CLOCK_DEV_IDX_VSPBD, + CLOCK_DEV_IDX_VSPI1, + CLOCK_DEV_IDX_VSPI0, + CLOCK_DEV_IDX_EHCI3, + CLOCK_DEV_IDX_EHCI2, + CLOCK_DEV_IDX_EHCI1, + CLOCK_DEV_IDX_EHCI0, + CLOCK_DEV_IDX_HSUSB, + CLOCK_DEV_IDX_HSUSB3, + CLOCK_DEV_IDX_CMM3, + CLOCK_DEV_IDX_CMM2, + CLOCK_DEV_IDX_CMM1, + CLOCK_DEV_IDX_CMM0, + CLOCK_DEV_IDX_CSI20, + CLOCK_DEV_IDX_CSI41, + CLOCK_DEV_IDX_CSI40, + CLOCK_DEV_IDX_DU3, + CLOCK_DEV_IDX_DU2, + CLOCK_DEV_IDX_DU1, + CLOCK_DEV_IDX_DU0, + CLOCK_DEV_IDX_LVDS, + CLOCK_DEV_IDX_HDMI1, + CLOCK_DEV_IDX_HDMI0, + CLOCK_DEV_IDX_VIN7, + CLOCK_DEV_IDX_VIN6, + CLOCK_DEV_IDX_VIN5, + CLOCK_DEV_IDX_VIN4, + CLOCK_DEV_IDX_VIN3, + CLOCK_DEV_IDX_VIN2, + CLOCK_DEV_IDX_VIN1, + CLOCK_DEV_IDX_VIN0, + CLOCK_DEV_IDX_ETHERAVB, + CLOCK_DEV_IDX_SATA0, + CLOCK_DEV_IDX_IMR3, + CLOCK_DEV_IDX_IMR2, + CLOCK_DEV_IDX_IMR1, + CLOCK_DEV_IDX_IMR0, + CLOCK_DEV_IDX_GPIO7, + CLOCK_DEV_IDX_GPIO6, + CLOCK_DEV_IDX_GPIO5, + CLOCK_DEV_IDX_GPIO4, + CLOCK_DEV_IDX_GPIO3, + CLOCK_DEV_IDX_GPIO2, + CLOCK_DEV_IDX_GPIO1, + CLOCK_DEV_IDX_GPIO0, + CLOCK_DEV_IDX_CAN_FD, + CLOCK_DEV_IDX_CAN_IF1, + CLOCK_DEV_IDX_CAN_IF0, + CLOCK_DEV_IDX_I2C6, + CLOCK_DEV_IDX_I2C5, + CLOCK_DEV_IDX_I2C_DVFS, + CLOCK_DEV_IDX_I2C4, + CLOCK_DEV_IDX_I2C3, + CLOCK_DEV_IDX_I2C2, + CLOCK_DEV_IDX_I2C1, + CLOCK_DEV_IDX_I2C0, + CLOCK_DEV_IDX_SSI_ALL, + CLOCK_DEV_IDX_SSI9, + CLOCK_DEV_IDX_SSI8, + CLOCK_DEV_IDX_SSI7, + CLOCK_DEV_IDX_SSI6, + CLOCK_DEV_IDX_SSI5, + CLOCK_DEV_IDX_SSI4, + CLOCK_DEV_IDX_SSI3, + CLOCK_DEV_IDX_SSI2, + CLOCK_DEV_IDX_SSI1, + CLOCK_DEV_IDX_SSI0, + CLOCK_DEV_IDX_SCU_ALL, + CLOCK_DEV_IDX_SCU_DVC1, + CLOCK_DEV_IDX_SCU_DVC0, + CLOCK_DEV_IDX_SCU_CTU0_MIX1, + CLOCK_DEV_IDX_SCU_CTU0_MIX0, + CLOCK_DEV_IDX_SCU_SRC9, + CLOCK_DEV_IDX_SCU_SRC8, + CLOCK_DEV_IDX_SCU_SRC7, + CLOCK_DEV_IDX_SCU_SRC6, + CLOCK_DEV_IDX_SCU_SRC5, + CLOCK_DEV_IDX_SCU_SRC4, + CLOCK_DEV_IDX_SCU_SRC3, + CLOCK_DEV_IDX_SCU_SRC2, + CLOCK_DEV_IDX_SCU_SRC1, + CLOCK_DEV_IDX_SCU_SRC0, + CLOCK_DEV_IDX_COUNT +}; + +#endif /* CLOCK_DEVICES_H */ diff --git a/product/rcar/scp_ramfw/clock_mstp_devices.h b/product/rcar/scp_ramfw/clock_mstp_devices.h new file mode 100644 index 000000000..4acd13cbb --- /dev/null +++ b/product/rcar/scp_ramfw/clock_mstp_devices.h @@ -0,0 +1,169 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef CLOCK_MSTP_DEVICES_H +#define CLOCK_MSTP_DEVICES_H + +/*! + * \brief Clock device indexes. + */ +enum clock_mstp_dev_idx { + CLOCK_MSTP_DEV_IDX_FDP1_1, + CLOCK_MSTP_DEV_IDX_FDP1_0, + CLOCK_MSTP_DEV_IDX_SCIF5, + CLOCK_MSTP_DEV_IDX_SCIF4, + CLOCK_MSTP_DEV_IDX_SCIF3, + CLOCK_MSTP_DEV_IDX_SCIF1, + CLOCK_MSTP_DEV_IDX_SCIF0, + CLOCK_MSTP_DEV_IDX_MSIOF3, + CLOCK_MSTP_DEV_IDX_MSIOF2, + CLOCK_MSTP_DEV_IDX_MSIOF1, + CLOCK_MSTP_DEV_IDX_MSIOF0, + CLOCK_MSTP_DEV_IDX_SYS_DMAC2, + CLOCK_MSTP_DEV_IDX_SYS_DMAC1, + CLOCK_MSTP_DEV_IDX_SYS_DMAC0, + CLOCK_MSTP_DEV_IDX_SCEG_PUB, + CLOCK_MSTP_DEV_IDX_CMT3, + CLOCK_MSTP_DEV_IDX_CMT2, + CLOCK_MSTP_DEV_IDX_CMT1, + CLOCK_MSTP_DEV_IDX_CMT0, + CLOCK_MSTP_DEV_IDX_TPU0, + CLOCK_MSTP_DEV_IDX_SCIF2, + CLOCK_MSTP_DEV_IDX_SDIF3, + CLOCK_MSTP_DEV_IDX_SDIF2, + CLOCK_MSTP_DEV_IDX_SDIF1, + CLOCK_MSTP_DEV_IDX_SDIF0, + CLOCK_MSTP_DEV_IDX_PCIE1, + CLOCK_MSTP_DEV_IDX_PCIE0, + CLOCK_MSTP_DEV_IDX_USB_DMAC30, + CLOCK_MSTP_DEV_IDX_USB3_IF0, + CLOCK_MSTP_DEV_IDX_USB_DMAC31, + CLOCK_MSTP_DEV_IDX_USB_DMAC0, + CLOCK_MSTP_DEV_IDX_USB_DMAC1, + CLOCK_MSTP_DEV_IDX_RWDT, + CLOCK_MSTP_DEV_IDX_INTC_EX, + CLOCK_MSTP_DEV_IDX_INTC_AP, + CLOCK_MSTP_DEV_IDX_AUDMAC1, + CLOCK_MSTP_DEV_IDX_AUDMAC0, + CLOCK_MSTP_DEV_IDX_DRIF31, + CLOCK_MSTP_DEV_IDX_DRIF30, + CLOCK_MSTP_DEV_IDX_DRIF21, + CLOCK_MSTP_DEV_IDX_DRIF20, + CLOCK_MSTP_DEV_IDX_DRIF11, + CLOCK_MSTP_DEV_IDX_DRIF10, + CLOCK_MSTP_DEV_IDX_DRIF01, + CLOCK_MSTP_DEV_IDX_DRIF00, + CLOCK_MSTP_DEV_IDX_HSCIF4, + CLOCK_MSTP_DEV_IDX_HSCIF3, + CLOCK_MSTP_DEV_IDX_HSCIF2, + CLOCK_MSTP_DEV_IDX_HSCIF1, + CLOCK_MSTP_DEV_IDX_HSCIF0, + CLOCK_MSTP_DEV_IDX_THERMAL, + CLOCK_MSTP_DEV_IDX_PWM, + CLOCK_MSTP_DEV_IDX_FCPVD2, + CLOCK_MSTP_DEV_IDX_FCPVD1, + CLOCK_MSTP_DEV_IDX_FCPVD0, + CLOCK_MSTP_DEV_IDX_FCPVB1, + CLOCK_MSTP_DEV_IDX_FCPVB0, + CLOCK_MSTP_DEV_IDX_FCPVI1, + CLOCK_MSTP_DEV_IDX_FCPVI0, + CLOCK_MSTP_DEV_IDX_FCPF1, + CLOCK_MSTP_DEV_IDX_FCPF0, + CLOCK_MSTP_DEV_IDX_FCPCS, + CLOCK_MSTP_DEV_IDX_VSPD2, + CLOCK_MSTP_DEV_IDX_VSPD1, + CLOCK_MSTP_DEV_IDX_VSPD0, + CLOCK_MSTP_DEV_IDX_VSPBC, + CLOCK_MSTP_DEV_IDX_VSPBD, + CLOCK_MSTP_DEV_IDX_VSPI1, + CLOCK_MSTP_DEV_IDX_VSPI0, + CLOCK_MSTP_DEV_IDX_EHCI3, + CLOCK_MSTP_DEV_IDX_EHCI2, + CLOCK_MSTP_DEV_IDX_EHCI1, + CLOCK_MSTP_DEV_IDX_EHCI0, + CLOCK_MSTP_DEV_IDX_HSUSB, + CLOCK_MSTP_DEV_IDX_HSUSB3, + CLOCK_MSTP_DEV_IDX_CMM3, + CLOCK_MSTP_DEV_IDX_CMM2, + CLOCK_MSTP_DEV_IDX_CMM1, + CLOCK_MSTP_DEV_IDX_CMM0, + CLOCK_MSTP_DEV_IDX_CSI20, + CLOCK_MSTP_DEV_IDX_CSI41, + CLOCK_MSTP_DEV_IDX_CSI40, + CLOCK_MSTP_DEV_IDX_DU3, + CLOCK_MSTP_DEV_IDX_DU2, + CLOCK_MSTP_DEV_IDX_DU1, + CLOCK_MSTP_DEV_IDX_DU0, + CLOCK_MSTP_DEV_IDX_LVDS, + CLOCK_MSTP_DEV_IDX_HDMI1, + CLOCK_MSTP_DEV_IDX_HDMI0, + CLOCK_MSTP_DEV_IDX_VIN7, + CLOCK_MSTP_DEV_IDX_VIN6, + CLOCK_MSTP_DEV_IDX_VIN5, + CLOCK_MSTP_DEV_IDX_VIN4, + CLOCK_MSTP_DEV_IDX_VIN3, + CLOCK_MSTP_DEV_IDX_VIN2, + CLOCK_MSTP_DEV_IDX_VIN1, + CLOCK_MSTP_DEV_IDX_VIN0, + CLOCK_MSTP_DEV_IDX_ETHERAVB, + CLOCK_MSTP_DEV_IDX_SATA0, + CLOCK_MSTP_DEV_IDX_IMR3, + CLOCK_MSTP_DEV_IDX_IMR2, + CLOCK_MSTP_DEV_IDX_IMR1, + CLOCK_MSTP_DEV_IDX_IMR0, + CLOCK_MSTP_DEV_IDX_GPIO7, + CLOCK_MSTP_DEV_IDX_GPIO6, + CLOCK_MSTP_DEV_IDX_GPIO5, + CLOCK_MSTP_DEV_IDX_GPIO4, + CLOCK_MSTP_DEV_IDX_GPIO3, + CLOCK_MSTP_DEV_IDX_GPIO2, + CLOCK_MSTP_DEV_IDX_GPIO1, + CLOCK_MSTP_DEV_IDX_GPIO0, + CLOCK_MSTP_DEV_IDX_CAN_FD, + CLOCK_MSTP_DEV_IDX_CAN_IF1, + CLOCK_MSTP_DEV_IDX_CAN_IF0, + CLOCK_MSTP_DEV_IDX_I2C6, + CLOCK_MSTP_DEV_IDX_I2C5, + CLOCK_MSTP_DEV_IDX_I2C_DVFS, + CLOCK_MSTP_DEV_IDX_I2C4, + CLOCK_MSTP_DEV_IDX_I2C3, + CLOCK_MSTP_DEV_IDX_I2C2, + CLOCK_MSTP_DEV_IDX_I2C1, + CLOCK_MSTP_DEV_IDX_I2C0, + CLOCK_MSTP_DEV_IDX_SSI_ALL, + CLOCK_MSTP_DEV_IDX_SSI9, + CLOCK_MSTP_DEV_IDX_SSI8, + CLOCK_MSTP_DEV_IDX_SSI7, + CLOCK_MSTP_DEV_IDX_SSI6, + CLOCK_MSTP_DEV_IDX_SSI5, + CLOCK_MSTP_DEV_IDX_SSI4, + CLOCK_MSTP_DEV_IDX_SSI3, + CLOCK_MSTP_DEV_IDX_SSI2, + CLOCK_MSTP_DEV_IDX_SSI1, + CLOCK_MSTP_DEV_IDX_SSI0, + CLOCK_MSTP_DEV_IDX_SCU_ALL, + CLOCK_MSTP_DEV_IDX_SCU_DVC1, + CLOCK_MSTP_DEV_IDX_SCU_DVC0, + CLOCK_MSTP_DEV_IDX_SCU_CTU0_MIX1, + CLOCK_MSTP_DEV_IDX_SCU_CTU0_MIX0, + CLOCK_MSTP_DEV_IDX_SCU_SRC9, + CLOCK_MSTP_DEV_IDX_SCU_SRC8, + CLOCK_MSTP_DEV_IDX_SCU_SRC7, + CLOCK_MSTP_DEV_IDX_SCU_SRC6, + CLOCK_MSTP_DEV_IDX_SCU_SRC5, + CLOCK_MSTP_DEV_IDX_SCU_SRC4, + CLOCK_MSTP_DEV_IDX_SCU_SRC3, + CLOCK_MSTP_DEV_IDX_SCU_SRC2, + CLOCK_MSTP_DEV_IDX_SCU_SRC1, + CLOCK_MSTP_DEV_IDX_SCU_SRC0, + CLOCK_MSTP_DEV_IDX_COUNT +}; + +#define CLK_ID_MSTP_START CLOCK_MSTP_DEV_IDX_FDP1_1 +#define CLK_ID_MSTP_END CLOCK_MSTP_DEV_IDX_COUNT + +#endif /* CLOCK_MSTP_DEVICES_H */ diff --git a/product/rcar/scp_ramfw/clock_sd_devices.h b/product/rcar/scp_ramfw/clock_sd_devices.h new file mode 100644 index 000000000..3105b298b --- /dev/null +++ b/product/rcar/scp_ramfw/clock_sd_devices.h @@ -0,0 +1,75 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef CLOCK_SD_DEVICES_H +#define CLOCK_SD_DEVICES_H + +/*! + * \brief Clock device indexes. + */ +enum clock_sd_parent_idx { + CLK_EXTAL, + CLK_OSC_EXTAL, + CLK_PLL1, + CLK_PLL1_DIV2, + CLK_PLL1_DIV4, + CLK_S0, + CLK_S1, + CLK_S2, + CLK_S3, + CLK_SDSRC, + CLOCK_PARENT_IDX_COUNT +}; + +enum clock_sd_dev_idx { + CLOCK_SD_DEV_IDX_ZTR, + CLOCK_SD_DEV_IDX_ZTRD2, + CLOCK_SD_DEV_IDX_ZT, + CLOCK_SD_DEV_IDX_ZX, + CLOCK_SD_DEV_IDX_S0D1, + CLOCK_SD_DEV_IDX_S0D2, + CLOCK_SD_DEV_IDX_S0D3, + CLOCK_SD_DEV_IDX_S0D4, + CLOCK_SD_DEV_IDX_S0D6, + CLOCK_SD_DEV_IDX_S0D8, + CLOCK_SD_DEV_IDX_S0D12, + CLOCK_SD_DEV_IDX_S1D1, + CLOCK_SD_DEV_IDX_S1D2, + CLOCK_SD_DEV_IDX_S1D4, + CLOCK_SD_DEV_IDX_S2D1, + CLOCK_SD_DEV_IDX_S2D2, + CLOCK_SD_DEV_IDX_S2D4, + CLOCK_SD_DEV_IDX_S3D1, + CLOCK_SD_DEV_IDX_S3D2, + CLOCK_SD_DEV_IDX_S3D4, + CLOCK_SD_DEV_IDX_SD0, + CLOCK_SD_DEV_IDX_SD1, + CLOCK_SD_DEV_IDX_SD2, + CLOCK_SD_DEV_IDX_SD3, + CLOCK_SD_DEV_IDX_CL, + CLOCK_SD_DEV_IDX_CR, + CLOCK_SD_DEV_IDX_CP, + CLOCK_SD_DEV_IDX_CPEX, + CLOCK_SD_DEV_IDX_CANFD, + CLOCK_SD_DEV_IDX_CSI0, + CLOCK_SD_DEV_IDX_MSO, + CLOCK_SD_DEV_IDX_HDMI, + CLOCK_SD_DEV_IDX_OSC, + CLOCK_SD_DEV_IDX_R, + CLOCK_SD_DEV_IDX_S0, + CLOCK_SD_DEV_IDX_S1, + CLOCK_SD_DEV_IDX_S2, + CLOCK_SD_DEV_IDX_S3, + CLOCK_SD_DEV_IDX_SDSRC, + CLOCK_SD_DEV_IDX_RINT, + CLOCK_SD_DEV_IDX_COUNT +}; + +#define CLK_ID_SD_START CLOCK_SD_DEV_IDX_ZTR +#define CLK_ID_SD_END CLOCK_SD_DEV_IDX_COUNT + +#endif /* CLOCK_SD_DEVICES_H */ diff --git a/product/rcar/scp_ramfw/config_clock.c b/product/rcar/scp_ramfw/config_clock.c new file mode 100644 index 000000000..fccec11b2 --- /dev/null +++ b/product/rcar/scp_ramfw/config_clock.c @@ -0,0 +1,1762 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct fwk_element clock_dev_desc_table[] = { + [CLOCK_DEV_IDX_BIG] = { + .name = "CPU_GROUP_BIG", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_CLOCK, 0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_LITTLE] = { + .name = "CPU_GROUP_LITTLE", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_CLOCK, 1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_ZTR] = { + .name = "ztr", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_ZTR), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_ZTRD2] = { + .name = "ztrd2", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_ZTRD2), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_ZT] = { + .name = "zt", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_ZT), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_ZX] = { + .name = "zx", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_ZX), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_S0D1] = { + .name = "s0d1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_S0D1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_S0D2] = { + .name = "s0d2", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_S0D2), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_S0D3] = { + .name = "s0d3", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_S0D3), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_S0D4] = { + .name = "s0d4", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_S0D4), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_S0D6] = { + .name = "s0d6", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_S0D6), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_S0D8] = { + .name = "s0d8", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_S0D8), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_S0D12] = { + .name = "s0d12", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_S0D12), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_S1D1] = { + .name = "s1d1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_S1D1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_S1D2] = { + .name = "s1d2", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_S1D2), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_S1D4] = { + .name = "s1d4", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_S1D4), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_S2D1] = { + .name = "s2d1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_S2D1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_S2D2] = { + .name = "s2d2", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_S2D2), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_S2D4] = { + .name = "s2d4", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_S2D4), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_S3D1] = { + .name = "s3d1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_S3D1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_S3D2] = { + .name = "s3d2", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_S3D2), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_S3D4] = { + .name = "s3d4", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_S3D4), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SD0] = { + .name = "sd0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_SD0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SD1] = { + .name = "sd1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_SD1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SD2] = { + .name = "sd2", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_SD2), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SD3] = { + .name = "sd3", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_SD3), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_CL] = { + .name = "cl", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_CL), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_CR] = { + .name = "cr", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_CR), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_CP] = { + .name = "cp", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_CP), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_CPEX] = { + .name = "cpex", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_CPEX), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_CANFD] = { + .name = "canfd", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_CANFD), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_CSI0] = { + .name = "csi0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_CSI0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_MSO] = { + .name = "mso", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_MSO), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_HDMI] = { + .name = "hdmi", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_HDMI), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_OSC] = { + .name = "osc", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_OSC), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_R] = { + .name = "r", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_R), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_S0] = { + .name = "s0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_S0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_S1] = { + .name = "s1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_S1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_S2] = { + .name = "s2", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_S2), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_S3] = { + .name = "s3", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_S3), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SDSRC] = { + .name = "sdsrc", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_SDSRC), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_RINT] = { + .name = "rint", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + CLOCK_SD_DEV_IDX_RINT), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_FDP1_1] = { + .name = "fdp1-1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_FDP1_1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_FDP1_0] = { + .name = "fdp1-0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_FDP1_0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SCIF5] = { + .name = "scif5", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SCIF5), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SCIF4] = { + .name = "scif4", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SCIF4), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SCIF3] = { + .name = "scif3", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SCIF3), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SCIF1] = { + .name = "scif1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SCIF1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SCIF0] = { + .name = "scif0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SCIF0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_MSIOF3] = { + .name = "msiof3", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_MSIOF3), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_MSIOF2] = { + .name = "msiof2", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_MSIOF2), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_MSIOF1] = { + .name = "msiof1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_MSIOF1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_MSIOF0] = { + .name = "msiof0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_MSIOF0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SYS_DMAC2] = { + .name = "sys-dmac2", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SYS_DMAC2), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SYS_DMAC1] = { + .name = "sys-dmac1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SYS_DMAC1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SYS_DMAC0] = { + .name = "sys-dmac0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SYS_DMAC0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SCEG_PUB] = { + .name = "sceg-pub", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SCEG_PUB), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_CMT3] = { + .name = "cmt3", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_CMT3), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_CMT2] = { + .name = "cmt2", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_CMT2), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_CMT1] = { + .name = "cmt1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_CMT1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_CMT0] = { + .name = "cmt0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_CMT0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_TPU0] = { + .name = "tpu0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_TPU0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SCIF2] = { + .name = "scif2", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SCIF2), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SDIF3] = { + .name = "sdif3", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SDIF3), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SDIF2] = { + .name = "sdif2", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SDIF2), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SDIF1] = { + .name = "sdif1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SDIF1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SDIF0] = { + .name = "sdif0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SDIF0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_PCIE1] = { + .name = "pcie1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_PCIE1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_PCIE0] = { + .name = "pcie0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_PCIE0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_USB_DMAC30] = { + .name = "usb-dmac30", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_USB_DMAC30), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_USB3_IF0] = { + .name = "usb3-if0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_USB3_IF0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_USB_DMAC31] = { + .name = "usb-dmac31", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_USB_DMAC31), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_USB_DMAC0] = { + .name = "usb-dmac0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_USB_DMAC0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_USB_DMAC1] = { + .name = "usb-dmac1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_USB_DMAC1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_RWDT] = { + .name = "rwdt", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_RWDT), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_INTC_EX] = { + .name = "intc-ex", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_INTC_EX), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_INTC_AP] = { + .name = "intc-ap", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_INTC_AP), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_AUDMAC1] = { + .name = "audmac1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_AUDMAC1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_AUDMAC0] = { + .name = "audmac0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_AUDMAC0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_DRIF31] = { + .name = "drif31", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_DRIF31), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_DRIF30] = { + .name = "drif30", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_DRIF30), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_DRIF21] = { + .name = "drif21", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_DRIF21), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_DRIF20] = { + .name = "drif20", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_DRIF20), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_DRIF11] = { + .name = "drif11", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_DRIF11), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_DRIF10] = { + .name = "drif10", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_DRIF10), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_DRIF01] = { + .name = "drif01", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_DRIF01), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_DRIF00] = { + .name = "drif00", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_DRIF00), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_HSCIF4] = { + .name = "hscif4", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_HSCIF4), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_HSCIF3] = { + .name = "hscif3", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_HSCIF3), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_HSCIF2] = { + .name = "hscif2", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_HSCIF2), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_HSCIF1] = { + .name = "hscif1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_HSCIF1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_HSCIF0] = { + .name = "hscif0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_HSCIF0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_THERMAL] = { + .name = "thermal", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_THERMAL), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_PWM] = { + .name = "pwm", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_PWM), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_FCPVD2] = { + .name = "fcpvd2", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_FCPVD2), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_FCPVD1] = { + .name = "fcpvd1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_FCPVD1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_FCPVD0] = { + .name = "fcpvd0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_FCPVD0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_FCPVB1] = { + .name = "fcpvb1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_FCPVB1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_FCPVB0] = { + .name = "fcpvb0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_FCPVB0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_FCPVI1] = { + .name = "fcpvi1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_FCPVI1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_FCPVI0] = { + .name = "fcpvi0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_FCPVI0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_FCPF1] = { + .name = "fcpf1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_FCPF1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_FCPF0] = { + .name = "fcpf0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_FCPF0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_FCPCS] = { + .name = "fcpcs", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_FCPCS), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_VSPD2] = { + .name = "vspd2", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_VSPD2), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_VSPD1] = { + .name = "vspd1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_VSPD1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_VSPD0] = { + .name = "vspd0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_VSPD0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_VSPBC] = { + .name = "vspbc", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_VSPBC), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_VSPBD] = { + .name = "vspbd", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_VSPBD), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_VSPI1] = { + .name = "vspi1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_VSPI1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_VSPI0] = { + .name = "vspi0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_VSPI0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_EHCI3] = { + .name = "ehci3", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_EHCI3), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_EHCI2] = { + .name = "ehci2", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_EHCI2), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_EHCI1] = { + .name = "ehci1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_EHCI1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_EHCI0] = { + .name = "ehci0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_EHCI0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_HSUSB] = { + .name = "hsusb", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_HSUSB), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_HSUSB3] = { + .name = "hsusb3", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_HSUSB3), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_CMM3] = { + .name = "cmm3", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_CMM3), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_CMM2] = { + .name = "cmm2", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_CMM2), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_CMM1] = { + .name = "cmm1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_CMM1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_CMM0] = { + .name = "cmm0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_CMM0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_CSI20] = { + .name = "csi20", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_CSI20), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_CSI41] = { + .name = "csi41", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_CSI41), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_CSI40] = { + .name = "csi40", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_CSI40), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_DU3] = { + .name = "du3", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_DU3), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_DU2] = { + .name = "du2", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_DU2), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_DU1] = { + .name = "du1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_DU1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_DU0] = { + .name = "du0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_DU0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_LVDS] = { + .name = "lvds", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_LVDS), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_HDMI1] = { + .name = "hdmi1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_HDMI1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_HDMI0] = { + .name = "hdmi0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_HDMI0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_VIN7] = { + .name = "vin7", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_VIN7), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_VIN6] = { + .name = "vin6", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_VIN6), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_VIN5] = { + .name = "vin5", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_VIN5), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_VIN4] = { + .name = "vin4", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_VIN4), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_VIN3] = { + .name = "vin3", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_VIN3), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_VIN2] = { + .name = "vin2", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_VIN2), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_VIN1] = { + .name = "vin1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_VIN1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_VIN0] = { + .name = "vin0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_VIN0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_ETHERAVB] = { + .name = "etheravb", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_ETHERAVB), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SATA0] = { + .name = "sata0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SATA0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_IMR3] = { + .name = "imr3", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_IMR3), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_IMR2] = { + .name = "imr2", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_IMR2), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_IMR1] = { + .name = "imr1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_IMR1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_IMR0] = { + .name = "imr0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_IMR0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_GPIO7] = { + .name = "gpio7", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_GPIO7), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_GPIO6] = { + .name = "gpio6", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_GPIO6), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_GPIO5] = { + .name = "gpio5", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_GPIO5), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_GPIO4] = { + .name = "gpio4", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_GPIO4), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_GPIO3] = { + .name = "gpio3", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_GPIO3), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_GPIO2] = { + .name = "gpio2", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_GPIO2), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_GPIO1] = { + .name = "gpio1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_GPIO1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_GPIO0] = { + .name = "gpio0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_GPIO0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_CAN_FD] = { + .name = "can-fd", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_CAN_FD), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_CAN_IF1] = { + .name = "can-if1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_CAN_IF1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_CAN_IF0] = { + .name = "can-if0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_CAN_IF0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_I2C6] = { + .name = "i2c6", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_I2C6), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_I2C5] = { + .name = "i2c5", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_I2C5), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_I2C_DVFS] = { + .name = "i2c-dvfs", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_I2C_DVFS), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_I2C4] = { + .name = "i2c4", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_I2C4), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_I2C3] = { + .name = "i2c3", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_I2C3), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_I2C2] = { + .name = "i2c2", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_I2C2), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_I2C1] = { + .name = "i2c1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_I2C1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_I2C0] = { + .name = "i2c0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_I2C0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SSI_ALL] = { + .name = "ssi-all", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SSI_ALL), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SSI9] = { + .name = "ssi9", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SSI9), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SSI8] = { + .name = "ssi8", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SSI8), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SSI7] = { + .name = "ssi7", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SSI7), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SSI6] = { + .name = "ssi6", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SSI6), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SSI5] = { + .name = "ssi5", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SSI5), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SSI4] = { + .name = "ssi4", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SSI4), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SSI3] = { + .name = "ssi3", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SSI3), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SSI2] = { + .name = "ssi2", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SSI2), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SSI1] = { + .name = "ssi1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SSI1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SSI0] = { + .name = "ssi0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SSI0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SCU_ALL] = { + .name = "scu-all", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SCU_ALL), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SCU_DVC1] = { + .name = "scu-dvc1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SCU_DVC1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SCU_DVC0] = { + .name = "scu-dvc0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SCU_DVC0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SCU_CTU0_MIX1] = { + .name = "scu-ctu1-mix1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SCU_CTU0_MIX1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SCU_CTU0_MIX0] = { + .name = "scu-ctu0-mix0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SCU_CTU0_MIX0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SCU_SRC9] = { + .name = "scu-src9", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SCU_SRC9), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SCU_SRC8] = { + .name = "scu-src8", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SCU_SRC8), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SCU_SRC7] = { + .name = "scu-src7", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SCU_SRC7), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SCU_SRC6] = { + .name = "scu-src6", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SCU_SRC6), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SCU_SRC5] = { + .name = "scu-src5", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SCU_SRC5), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SCU_SRC4] = { + .name = "scu-src4", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SCU_SRC4), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SCU_SRC3] = { + .name = "scu-src3", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SCU_SRC3), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SCU_SRC2] = { + .name = "scu-src2", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SCU_SRC2), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SCU_SRC1] = { + .name = "scu-src1", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SCU_SRC1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_SCU_SRC0] = { + .name = "scu-src0", + .data = &((struct mod_clock_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + CLOCK_MSTP_DEV_IDX_SCU_SRC0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + [CLOCK_DEV_IDX_COUNT] = { 0 }, /* Termination description. */ +}; + +static const struct fwk_element *clock_get_dev_desc_table(fwk_id_t module_id) +{ + unsigned int i; + unsigned int core_count; + struct mod_clock_dev_config *dev_config; + + core_count = rcar_core_get_count(); + + /* Configure all clocks to respond to changes in SYSTOP power state */ + for (i = 0; i < CLOCK_DEV_IDX_COUNT; i++) { + dev_config = + (struct mod_clock_dev_config *)clock_dev_desc_table[i].data; + dev_config->pd_source_id = FWK_ID_ELEMENT( + FWK_MODULE_IDX_RCAR_POWER_DOMAIN, + CONFIG_POWER_DOMAIN_CHILD_COUNT + core_count); + } + return clock_dev_desc_table; +} + +struct fwk_module_config config_clock = { + .elements = FWK_MODULE_DYNAMIC_ELEMENTS(clock_get_dev_desc_table), + .data = &((struct mod_clock_config) { + .pd_transition_notification_id = FWK_ID_NOTIFICATION_INIT( + FWK_MODULE_IDX_RCAR_POWER_DOMAIN, + MOD_PD_NOTIFICATION_IDX_POWER_STATE_TRANSITION), + .pd_pre_transition_notification_id = FWK_ID_NOTIFICATION_INIT( + FWK_MODULE_IDX_RCAR_POWER_DOMAIN, + MOD_PD_NOTIFICATION_IDX_POWER_STATE_PRE_TRANSITION), + }), +}; diff --git a/product/rcar/scp_ramfw/config_scmi.c b/product/rcar/scp_ramfw/config_scmi.c new file mode 100644 index 000000000..abb95a13b --- /dev/null +++ b/product/rcar/scp_ramfw/config_scmi.c @@ -0,0 +1,95 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct fwk_element element_table[] = { + [RCAR_SCMI_SERVICE_IDX_PSCI] = { + .name = "PSCI", + .data = &(struct mod_scmi_service_config) { + .transport_id = FWK_ID_ELEMENT_INIT( + FWK_MODULE_IDX_SMT, + RCAR_SCMI_SERVICE_IDX_PSCI), + .transport_api_id = FWK_ID_API_INIT( + FWK_MODULE_IDX_SMT, + MOD_SMT_API_IDX_SCMI_TRANSPORT), + .transport_notification_init_id = + FWK_ID_NOTIFICATION_INIT(FWK_MODULE_IDX_SMT, + MOD_SMT_NOTIFICATION_IDX_INITIALIZED), + .scmi_agent_id = SCMI_AGENT_ID_PSCI, + }, + }, + + [RCAR_SCMI_SERVICE_IDX_OSPM_0] = { + .name = "OSPM 0", + .data = &(struct mod_scmi_service_config) { + .transport_id = FWK_ID_ELEMENT_INIT( + FWK_MODULE_IDX_SMT, + RCAR_SCMI_SERVICE_IDX_OSPM_0), + .transport_api_id = FWK_ID_API_INIT( + FWK_MODULE_IDX_SMT, + MOD_SMT_API_IDX_SCMI_TRANSPORT), + .transport_notification_init_id = + FWK_ID_NOTIFICATION_INIT(FWK_MODULE_IDX_SMT, + MOD_SMT_NOTIFICATION_IDX_INITIALIZED), + .scmi_agent_id = SCMI_AGENT_ID_OSPM, + }, + }, +/* + [RCAR_SCMI_SERVICE_IDX_OSPM_1] = { + .name = "OSPM 1", + .data = &(struct mod_scmi_service_config) { + .transport_id = FWK_ID_ELEMENT_INIT( + FWK_MODULE_IDX_SMT, + RCAR_SCMI_SERVICE_IDX_OSPM_1), + .transport_api_id = FWK_ID_API_INIT( + FWK_MODULE_IDX_SMT, + MOD_SMT_API_IDX_SCMI_TRANSPORT), + .transport_notification_init_id = + FWK_ID_NOTIFICATION_INIT(FWK_MODULE_IDX_SMT, + MOD_SMT_NOTIFICATION_IDX_INITIALIZED), + .scmi_agent_id = SCMI_AGENT_ID_OSPM, + }, + }, +*/ + [RCAR_SCMI_SERVICE_IDX_COUNT] = { 0 }, +}; + +static const struct fwk_element *get_element_table(fwk_id_t module_id) +{ + return element_table; +} + +static const struct mod_scmi_agent agent_table[] = { + [SCMI_AGENT_ID_OSPM] = { + .type = SCMI_AGENT_TYPE_OSPM, + .name = "OSPM", + }, + [SCMI_AGENT_ID_PSCI] = { + .type = SCMI_AGENT_TYPE_PSCI, + .name = "PSCI", + }, +}; + +struct fwk_module_config config_scmi = { + .elements = FWK_MODULE_DYNAMIC_ELEMENTS(get_element_table), + .data = &((struct mod_scmi_config) { + .protocol_count_max = 9, + .agent_count = FWK_ARRAY_SIZE(agent_table) - 1, + .agent_table = agent_table, + .vendor_identifier = "arm", + .sub_vendor_identifier = "arm", + }), +}; diff --git a/product/rcar/scp_ramfw/config_scmi_apcore.c b/product/rcar/scp_ramfw/config_scmi_apcore.c new file mode 100644 index 000000000..6a7c769f4 --- /dev/null +++ b/product/rcar/scp_ramfw/config_scmi_apcore.c @@ -0,0 +1,29 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include + +static const struct mod_scmi_apcore_reset_register_group + reset_reg_group_table[] = { + { + .base_register = 0, + .register_count = RCAR_CORE_PER_CLUSTER_MAX, + }, + }; + +const struct fwk_module_config config_scmi_apcore = { + .data = &((struct mod_scmi_apcore_config){ + .reset_register_width = MOD_SCMI_APCORE_REG_WIDTH_64, + .reset_register_group_count = + FWK_ARRAY_SIZE(reset_reg_group_table), + .reset_register_group_table = &reset_reg_group_table[0], + }), +}; diff --git a/product/rcar/scp_ramfw/config_scmi_clock.c b/product/rcar/scp_ramfw/config_scmi_clock.c new file mode 100644 index 000000000..5cc596f1b --- /dev/null +++ b/product/rcar/scp_ramfw/config_scmi_clock.c @@ -0,0 +1,978 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include + +static const struct mod_scmi_clock_device agent_device_table_ospm[] = { + /* Core Clock */ + { + /* ztr */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_ZTR), + }, + { + /* ztrd2 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_ZTRD2), + }, + { + /* zt */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_ZT), + }, + { + /* zx */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_ZX), + }, + { + /* s0d1 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_S0D1), + }, + { + /* s0d2 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_S0D2), + }, + { + /* s0d3 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_S0D3), + }, + { + /* s0d4 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_S0D4), + }, + { + /* s0d6 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_S0D6), + }, + { + /* s0d8 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_S0D8), + }, + { + /* s0d12 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_S0D12), + }, + { + /* s1d1 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_S1D1), + }, + { + /* s1d2 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_S1D2), + }, + { + /* s1d4 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_S1D4), + }, + { + /* s2d1 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_S2D1), + }, + { + /* s2d2 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_S2D2), + }, + { + /* s2d4 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_S2D4), + }, + { + /* s3d1 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_S3D1), + }, + { + /* s3d2 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_S3D2), + }, + { + /* s3d4 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_S3D4), + }, + { + /* sd0 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SD0), + }, + { + /* sd1 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SD1), + }, + { + /* sd2 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SD2), + }, + { + /* sd3 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SD3), + }, + { + /* cl */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_CL), + }, + { + /* cr */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_CR), + }, + { + /* cp */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_CP), + }, + { + /* cpex */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_CPEX), + }, + { + /* canfd */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_CANFD), + }, + { + /* csi0 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_CSI0), + + }, + { + /* mso */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_MSO), + }, + { + /* hdmi */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_HDMI), + }, + { + /* osc */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_OSC), + }, + { + /* r */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_R), + }, + { + /* s0 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_S0), + }, + { + /* s1 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_S1), + }, + { + /* s2 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_S2), + }, + { + /* s3 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_S3), + }, + { + /* sdsrc */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SDSRC), + }, + { + /* rint */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_RINT), + }, + + /* mstp Clock */ + { + /* fdp1-1 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_FDP1_1), + }, + { + /* fdp1-0 */ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_FDP1_0), + }, + { + /* scif5*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SCIF5), + }, + { + /* scif4*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SCIF4), + }, + { + /* scif3*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SCIF3), + }, + { + /* scif1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SCIF1), + }, + { + /* scif0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SCIF0), + }, + { + /* msiof3*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_MSIOF3), + }, + { + /* msiof2*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_MSIOF2), + }, + { + /* msiof1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_MSIOF1), + }, + { + /* msiof0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_MSIOF0), + }, + { + /* sys-dmac2*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SYS_DMAC2), + }, + { + /* sys-dmac1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SYS_DMAC1), + }, + { + /* sys-dmac0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SYS_DMAC0), + }, + { + /* sceg-pub*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SCEG_PUB), + }, + { + /* cmt3*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_CMT3), + }, + { + /* cmt2*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_CMT2), + }, + { + /* cmt1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_CMT1), + }, + { + /* cmt0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_CMT0), + }, + { + /* tpu0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_TPU0), + }, + { + /* scif2*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SCIF2), + }, + { + /* sdif3*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SDIF3), + }, + { + /* sdif2*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SDIF2), + }, + { + /* sdif1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SDIF1), + }, + { + /* sdif0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SDIF0), + }, + { + /* pcie1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_PCIE1), + }, + { + /* pcie0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_PCIE0), + }, + { + /* usb-dmac30*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_USB_DMAC30), + }, + { + /* usb3-if0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_USB3_IF0), + }, + { + /* usb-dmac31*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_USB_DMAC31), + }, + { + /* usb-dmac0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_USB_DMAC0), + }, + { + /* usb-dmac1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_USB_DMAC1), + }, + { + /* rwdt*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_RWDT), + }, + { + /* intc-ex*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_INTC_EX), + }, + { + /* intc-ap*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_INTC_AP), + }, + { + /* audmac1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_AUDMAC1), + }, + { + /* audmac0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_AUDMAC0), + }, + { + /* drif31*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_DRIF31), + }, + { + /* drif30*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_DRIF30), + }, + { + /* drif21*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_DRIF21), + }, + { + /* drif20*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_DRIF20), + }, + { + /* drif11*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_DRIF11), + }, + { + /* drif10*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_DRIF10), + }, + { + /* drif01*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_DRIF01), + }, + { + /* drif00*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_DRIF00), + }, + { + /* hscif4*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_HSCIF4), + }, + { + /* hscif3*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_HSCIF3), + }, + { + /* hscif2*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_HSCIF2), + }, + { + /* hscif1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_HSCIF1), + }, + { + /* hscif0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_HSCIF0), + }, + { + /* thermal*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_THERMAL), + }, + { + /* pwm*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_PWM), + }, + { + /* fcpvd2*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_FCPVD2), + }, + { + /* fcpvd1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_FCPVD1), + }, + { + /* fcpvd0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_FCPVD0), + }, + { + /* fcpvb1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_FCPVB1), + }, + { + /* fcpvb0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_FCPVB0), + }, + { + /* fcpvi1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_FCPVI1), + }, + { + /* fcpvi0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_FCPVI0), + }, + { + /* fcpf1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_FCPF1), + }, + { + /* fcpf0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_FCPF0), + }, + { + /* fcpcs*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_FCPCS), + }, + { + /* vspd2*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_VSPD2), + }, + { + /* vspd1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_VSPD1), + }, + { + /* vspd0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_VSPD0), + }, + { + /* vspbc*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_VSPBC), + }, + { + /* vspbd*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_VSPBD), + }, + { + /* vspi1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_VSPI1), + }, + { + /* vspi0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_VSPI0), + }, + { + /* ehci3*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_EHCI3), + }, + { + /* ehci2*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_EHCI2), + }, + { + /* ehci1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_EHCI1), + }, + { + /* ehci0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_EHCI0), + }, + { + /* hsusb*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_HSUSB), + }, + { + /* hsusb3*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_HSUSB3), + }, + { + /* cmm3*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_CMM3), + }, + { + /* cmm2*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_CMM2), + }, + { + /* cmm1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_CMM1), + }, + { + /* cmm0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_CMM0), + }, + { + /* csi20*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_CSI20), + }, + { + /* csi41*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_CSI41), + }, + { + /* csi40*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_CSI40), + }, + { + /* du3*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_DU3), + }, + { + /* du2*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_DU2), + }, + { + /* du1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_DU1), + }, + { + /* du0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_DU0), + }, + { + /* lvds*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_LVDS), + }, + { + /* hdmi1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_HDMI1), + }, + { + /* hdmi0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_HDMI0), + }, + { + /* vin7*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_VIN7), + }, + { + /* vin6*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_VIN6), + }, + { + /* vin5*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_VIN5), + }, + { + /* vin4*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_VIN4), + }, + { + /* vin3*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_VIN3), + }, + { + /* vin2*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_VIN2), + }, + { + /* vin1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_VIN1), + }, + { + /* vin0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_VIN0), + }, + { + /* etheravb*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_ETHERAVB), + }, + { + /* sata0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SATA0), + }, + { + /* imr3*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_IMR3), + }, + { + /* imr2*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_IMR2), + }, + { + /* imr1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_IMR1), + }, + { + /* imr0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_IMR0), + }, + { + /* gpio7*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_GPIO7), + }, + { + /* gpio6*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_GPIO6), + }, + { + /* gpio5*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_GPIO5), + }, + { + /* gpio4*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_GPIO4), + }, + { + /* gpio3*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_GPIO3), + }, + { + /* gpio2*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_GPIO2), + }, + { + /* gpio1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_GPIO1), + }, + { + /* gpio0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_GPIO0), + }, + { + /* can-fd*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_CAN_FD), + }, + { + /* can-if1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_CAN_IF1), + }, + { + /* can-if0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_CAN_IF0), + }, + { + /* i2c6*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_I2C6), + }, + { + /* i2c5*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_I2C5), + }, + { + /* i2c-dvfs*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_I2C_DVFS), + }, + { + /* i2c4*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_I2C4), + }, + { + /* i2c3*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_I2C3), + }, + { + /* i2c2*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_I2C2), + }, + { + /* i2c1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_I2C1), + }, + { + /* i2c0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_I2C0), + }, + { + /* ssi-all*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SSI_ALL), + }, + { + /* ssi9*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SSI9), + }, + { + /* ssi8*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SSI8), + }, + { + /* ssi7*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SSI7), + }, + { + /* ssi6*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SSI6), + }, + { + /* ssi5*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SSI5), + }, + { + /* ssi4*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SSI4), + }, + { + /* ssi3*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SSI3), + }, + { + /* ssi2*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SSI2), + }, + { + /* ssi1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SSI1), + }, + { + /* ssi0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SSI0), + }, + { + /* scu-all*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SCU_ALL), + }, + { + /* scu-dvc1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SCU_DVC1), + }, + { + /* scu-dvc0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SCU_DVC0), + }, + { + /* scu-ctu1-mix1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, + CLOCK_DEV_IDX_SCU_CTU0_MIX1), + }, + { + /* scu-ctu0-mix0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, + CLOCK_DEV_IDX_SCU_CTU0_MIX0), + }, + { + /* scu-src9*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SCU_SRC9), + }, + { + /* scu-src8*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SCU_SRC8), + }, + { + /* scu-src7*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SCU_SRC7), + }, + { + /* scu-src6*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SCU_SRC6), + }, + { + /* scu-src5*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SCU_SRC5), + }, + { + /* scu-src4*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SCU_SRC4), + }, + { + /* scu-src3*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SCU_SRC3), + }, + { + /* scu-src2*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SCU_SRC2), + }, + { + /* scu-src1*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SCU_SRC1), + }, + { + /* scu-src0*/ + .element_id = + FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLOCK_DEV_IDX_SCU_SRC0), + }, +}; + +static const struct mod_scmi_clock_agent agent_table[] = { + [SCMI_AGENT_ID_PSCI] = { 0 /* No access */ }, + [SCMI_AGENT_ID_OSPM] = { + .device_table = agent_device_table_ospm, + .device_count = FWK_ARRAY_SIZE(agent_device_table_ospm), + }, +}; + +struct fwk_module_config config_scmi_clock = { + .data = &((struct mod_scmi_clock_config) { + .max_pending_transactions = 0, + .agent_table = agent_table, + .agent_count = FWK_ARRAY_SIZE(agent_table), + }), +}; diff --git a/product/rcar/scp_ramfw/config_scmi_power_domain.c b/product/rcar/scp_ramfw/config_scmi_power_domain.c new file mode 100644 index 000000000..c0c4ec5f2 --- /dev/null +++ b/product/rcar/scp_ramfw/config_scmi_power_domain.c @@ -0,0 +1,11 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +/* No elements, no module configuration data */ +struct fwk_module_config config_scmi_power_domain = { 0 }; diff --git a/product/rcar/scp_ramfw/config_sensor.c b/product/rcar/scp_ramfw/config_sensor.c new file mode 100644 index 000000000..12510a595 --- /dev/null +++ b/product/rcar/scp_ramfw/config_sensor.c @@ -0,0 +1,112 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +enum REG_SENSOR_DEVICES { + REG_SENSOR_DEV_SOC_TEMP1, + REG_SENSOR_DEV_SOC_TEMP2, + REG_SENSOR_DEV_SOC_TEMP3, + REG_SENSOR_DEV_COUNT, +}; +enum SENSOR_DEVICES { + R8A7795_SNSR_THERMAL1, + R8A7795_SNSR_THERMAL2, + R8A7795_SNSR_THERMAL3, + R8A7795_SNSR_COUNT, +}; + +/* + * Register Sensor driver config + */ +static struct mod_sensor_info info_soc_temperature = { + .type = MOD_SENSOR_TYPE_DEGREES_C, + .update_interval = 0, + .update_interval_multiplier = 0, + .unit_multiplier = -3, +}; + +static const struct fwk_element reg_sensor_element_table[] = { + [REG_SENSOR_DEV_SOC_TEMP1] = { + .name = "thermal1", + .data = &((struct mod_reg_sensor_dev_config) { + .reg = (uintptr_t)(SENSOR_SOC_TEMP1), + .info = &info_soc_temperature, + }), + }, + [REG_SENSOR_DEV_SOC_TEMP2] = { + .name = "thermal2", + .data = &((struct mod_reg_sensor_dev_config) { + .reg = (uintptr_t)(SENSOR_SOC_TEMP2), + .info = &info_soc_temperature, + }), + }, + [REG_SENSOR_DEV_SOC_TEMP3] = { + .name = "thermal3", + .data = &((struct mod_reg_sensor_dev_config) { + .reg = (uintptr_t)(SENSOR_SOC_TEMP3), + .info = &info_soc_temperature, + }), + }, + [REG_SENSOR_DEV_COUNT] = { 0 }, +}; + +static const struct fwk_element *get_reg_sensor_element_table(fwk_id_t id) +{ + return reg_sensor_element_table; +} + +struct fwk_module_config config_rcar_reg_sensor = { + .elements = FWK_MODULE_DYNAMIC_ELEMENTS(get_reg_sensor_element_table), +}; + +/* + * Sensor module config + */ +static const struct fwk_element sensor_element_table[] = { + [R8A7795_SNSR_THERMAL1] = { + .name = "thermal1", + .data = &((const struct mod_sensor_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_REG_SENSOR, + REG_SENSOR_DEV_SOC_TEMP1), + .driver_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_REG_SENSOR, 0), + }), + }, + [R8A7795_SNSR_THERMAL2] = { + .name = "thermal2", + .data = &((const struct mod_sensor_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_REG_SENSOR, + REG_SENSOR_DEV_SOC_TEMP2), + .driver_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_REG_SENSOR, 0), + }), + }, + [R8A7795_SNSR_THERMAL3] = { + .name = "thermal3", + .data = &((const struct mod_sensor_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_REG_SENSOR, + REG_SENSOR_DEV_SOC_TEMP3), + .driver_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_REG_SENSOR, 0), + }), + }, + [R8A7795_SNSR_COUNT] = { 0 }, +}; + +static const struct fwk_element *get_sensor_element_table(fwk_id_t module_id) +{ + return sensor_element_table; +} + +struct fwk_module_config config_sensor = { + .elements = FWK_MODULE_DYNAMIC_ELEMENTS(get_sensor_element_table), + .data = NULL, +}; diff --git a/product/rcar/scp_ramfw/config_smt.c b/product/rcar/scp_ramfw/config_smt.c new file mode 100644 index 000000000..aeb83559e --- /dev/null +++ b/product/rcar/scp_ramfw/config_smt.c @@ -0,0 +1,65 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct fwk_element smt_element_table[] = { + [RCAR_SCMI_SERVICE_IDX_PSCI] = { + .name = "PSCI", + .data = &((struct mod_smt_channel_config) { + .type = MOD_SMT_CHANNEL_TYPE_SLAVE, + .policies = MOD_SMT_POLICY_INIT_MAILBOX | MOD_SMT_POLICY_SECURE, + .mailbox_address = (uintptr_t)SCMI_PAYLOAD_S_A2P_BASE, + .mailbox_size = SCMI_PAYLOAD_SIZE, + .driver_id = FWK_ID_SUB_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MFISMH, + RCAR_MFISMH_DEVICE_IDX_S, 0), + .driver_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MFISMH, 0), + }) + }, + [RCAR_SCMI_SERVICE_IDX_OSPM_0] = { + .name = "OSPM0", + .data = &((struct mod_smt_channel_config) { + .type = MOD_SMT_CHANNEL_TYPE_SLAVE, + .policies = MOD_SMT_POLICY_INIT_MAILBOX, + .mailbox_address = (uintptr_t)SCMI_PAYLOAD0_NS_A2P_BASE, + .mailbox_size = SCMI_PAYLOAD_SIZE, + .driver_id = FWK_ID_SUB_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MFISMH, + RCAR_MFISMH_DEVICE_IDX_NS_L, 0), + .driver_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MFISMH, 0), + }) + }, + [RCAR_SCMI_SERVICE_IDX_COUNT] = { 0 }, +}; + +static const struct fwk_element *smt_get_element_table(fwk_id_t module_id) +{ + unsigned int idx; + struct mod_smt_channel_config *config; + + for (idx = 0; idx < RCAR_SCMI_SERVICE_IDX_COUNT; idx++) { + config = (struct mod_smt_channel_config *)(smt_element_table[idx].data); + config->pd_source_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_RCAR_POWER_DOMAIN, + CONFIG_POWER_DOMAIN_CHILD_COUNT + rcar_core_get_count()); + } + + return smt_element_table; +} + +struct fwk_module_config config_smt = { + .elements = FWK_MODULE_DYNAMIC_ELEMENTS(smt_get_element_table), +}; diff --git a/product/rcar/scp_ramfw/firmware.mk b/product/rcar/scp_ramfw/firmware.mk new file mode 100644 index 000000000..707138c88 --- /dev/null +++ b/product/rcar/scp_ramfw/firmware.mk @@ -0,0 +1,92 @@ +# +# Renesas SCP/MCP Software +# Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +BS_FIRMWARE_CPU := cortex-a57.cortex-a53 +BS_FIRMWARE_HAS_MULTITHREADING := yes +BS_FIRMWARE_HAS_NOTIFICATION := yes +BS_FIRMWARE_HAS_RESOURCE_PERMISSIONS := no + +BS_FIRMWARE_MODULES := \ + rcar_scif \ + rcar_system \ + scmi \ + smt \ + clock \ + rcar_clock \ + rcar_sd_clock \ + rcar_mstp_clock \ + rcar_system_power \ + rcar_dvfs \ + rcar_pmic \ + rcar_mock_pmic \ + rcar_mfismh \ + rcar_power_domain \ + rcar_pd_sysc \ + rcar_pd_core \ + rcar_reg_sensor \ + sensor \ + scmi_power_domain \ + scmi_clock \ + scmi_sensor \ + scmi_apcore + +ifeq ($(BS_FIRMWARE_HAS_RESOURCE_PERMISSIONS),yes) + BS_FIRMWARE_MODULES += resource_perms +endif + +BS_FIRMWARE_SOURCES := \ + rcar_core.c \ + config_rcar_scif.c \ + config_rcar_power_domain.c \ + config_rcar_pd_sysc.c \ + config_rcar_pd_core.c \ + config_sensor.c \ + config_clock.c \ + config_rcar_clock.c \ + config_rcar_sd_clock.c \ + config_rcar_mstp_clock.c \ + config_rcar_dvfs.c \ + config_rcar_pmic.c \ + config_rcar_mock_pmic.c \ + config_rcar_mfismh.c \ + config_smt.c \ + config_scmi.c \ + config_scmi_clock.c \ + config_scmi_apcore.c \ + config_scmi_power_domain.c \ + config_rcar_system_power.c \ + config_rcar_system.c + +ifeq ($(BS_FIRMWARE_HAS_RESOURCE_PERMISSIONS),yes) + BS_FIRMWARE_SOURCES += config_resource_perms.c +endif + +# +# Temporary source code until CMSIS-FreeRTOS is updated +# +BS_FIRMWARE_SOURCES += \ + portASM.S \ + port.c \ + list.c \ + queue.c \ + tasks.c \ + timers.c \ + heap_1.c \ + cmsis_os2_tiny4scp.c + +vpath %.c $(PRODUCT_DIR)/src/CMSIS-FreeRTOS/Source/portable/GCC/ARM_CA53_64_Rcar +vpath %.S $(PRODUCT_DIR)/src/CMSIS-FreeRTOS/Source/portable/GCC/ARM_CA53_64_Rcar +vpath %.c $(PRODUCT_DIR)/src/CMSIS-FreeRTOS/Source +vpath %.c $(PRODUCT_DIR)/src/CMSIS-FreeRTOS/Source/portable/MemMang +vpath %.c $(PRODUCT_DIR)/src/CMSIS-FreeRTOS/CMSIS/RTOS2/FreeRTOS/Source + +# +# Temporary directory until CMSIS-FreeRTOS is updated +# +FREERTOS_DIR := $(PRODUCT_DIR)/src/CMSIS-FreeRTOS/CMSIS/RTOS2 + +include $(BS_DIR)/firmware.mk diff --git a/product/rcar/scp_ramfw/fmw_memory.h b/product/rcar/scp_ramfw/fmw_memory.h new file mode 100644 index 000000000..7ebc1d695 --- /dev/null +++ b/product/rcar/scp_ramfw/fmw_memory.h @@ -0,0 +1,23 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef FMW_MEMORY_H +#define FMW_MEMORY_H + +#include + +#define FMW_MEM_MODE ARCH_MEM_MODE_SINGLE_REGION + +/* + * RAM memory + */ +#define FMW_MEM0_SIZE SCP_RAM_SIZE +#define FMW_MEM0_BASE SCP_RAM_BASE + +#define FMW_STACK_SIZE (4 * 1024) + +#endif /* FMW_MEMORY_H */ diff --git a/product/rcar/src/CMSIS-FreeRTOS/CMSIS/RTOS2/FreeRTOS/Source/cmsis_os2_tiny4scp.c b/product/rcar/src/CMSIS-FreeRTOS/CMSIS/RTOS2/FreeRTOS/Source/cmsis_os2_tiny4scp.c new file mode 100644 index 000000000..957559e41 --- /dev/null +++ b/product/rcar/src/CMSIS-FreeRTOS/CMSIS/RTOS2/FreeRTOS/Source/cmsis_os2_tiny4scp.c @@ -0,0 +1,535 @@ +/* -------------------------------------------------------------------------- + * Copyright (c) 2013-2019 Arm Limited. All rights reserved. + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the License); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an AS IS BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Name: cmsis_os2_tyny4scp.c + * Purpose: CMSIS RTOS2 wrapper for FreeRTOS + * + *---------------------------------------------------------------------------*/ + +#include + +#include "cmsis_os2.h" // ::CMSIS:RTOS2 + +#include "FreeRTOS.h" // ARM.FreeRTOS::RTOS:Core +#include "task.h" // ARM.FreeRTOS::RTOS:Core + +#define __ARM_ARCH_8A__ (1) + +#define IS_IRQ() is_irq() + +/* Kernel version and identification string definition + (major.minor.rev: mmnnnrrrr dec) */ +#define KERNEL_VERSION (((uint32_t)tskKERNEL_VERSION_MAJOR * 10000000UL) | \ + ((uint32_t)tskKERNEL_VERSION_MINOR * 10000UL) | \ + ((uint32_t)tskKERNEL_VERSION_BUILD * 1UL)) + +#define KERNEL_ID ("FreeRTOS " tskKERNEL_VERSION_NUMBER) + +#define portYIELD_FROM_ISR(n) +#define __STATIC_INLINE static inline +/* Limits */ +#define MAX_BITS_TASK_NOTIFY 31U +#define MAX_BITS_EVENT_GROUPS 24U + +#define THREAD_FLAGS_INVALID_BITS (~((1UL << MAX_BITS_TASK_NOTIFY) - 1U)) +#define EVENT_FLAGS_INVALID_BITS (~((1UL << MAX_BITS_EVENT_GROUPS) - 1U)) + +static inline uint32_t is_irq(void) +{ + uint32_t val; + __asm__ volatile ("mrs %0, spsel" : "=r" (val)); + return val & 0x01; +} + +/* + Setup SVC to reset value. +*/ +__STATIC_INLINE void SVC_Setup (void) { +#if ((__ARM_ARCH_7A__ == 0U) && (__ARM_ARCH_8A__ == 0U)) + /* Service Call interrupt might be configured before kernel start */ + /* and when its priority is lower or equal to BASEPRI, svc intruction */ + /* causes a Hard Fault. */ + NVIC_SetPriority (SVCall_IRQn, 0U); +#endif +} + +/* Kernel initialization state */ +static osKernelState_t KernelState = osKernelInactive; + +osStatus_t osKernelInitialize(void) +{ + osStatus_t stat; + + if (IS_IRQ()) { + stat = osErrorISR; + } + else { + if (KernelState == osKernelInactive) { + #if defined(RTE_Compiler_EventRecorder) + EvrFreeRTOSSetup(0U); + #endif + #if defined(RTE_RTOS_FreeRTOS_HEAP_5) && (HEAP_5_REGION_SETUP == 1) + vPortDefineHeapRegions (configHEAP_5_REGIONS); + #endif + KernelState = osKernelReady; + stat = osOK; + } else { + stat = osError; + } + } + + return (stat); +} + +osStatus_t osKernelGetInfo(osVersion_t *version, char *id_buf, uint32_t id_size) +{ + + if (version != NULL) { + /* Version encoding is major.minor.rev: mmnnnrrrr dec */ + version->api = KERNEL_VERSION; + version->kernel = KERNEL_VERSION; + } + + if ((id_buf != NULL) && (id_size != 0U)) { + if (id_size > sizeof(KERNEL_ID)) { + id_size = sizeof(KERNEL_ID); + } + memcpy(id_buf, KERNEL_ID, id_size); + } + + return (osOK); +} + +osKernelState_t osKernelGetState (void) { + osKernelState_t state; + + switch (xTaskGetSchedulerState()) { + case taskSCHEDULER_RUNNING: + state = osKernelRunning; + break; + + case taskSCHEDULER_SUSPENDED: + state = osKernelLocked; + break; + + case taskSCHEDULER_NOT_STARTED: + default: + if (KernelState == osKernelReady) { + state = osKernelReady; + } else { + state = osKernelInactive; + } + break; + } + + return (state); +} + +osStatus_t osKernelStart(void) +{ + osStatus_t stat; + + if (IS_IRQ()) { + stat = osErrorISR; + } + else { + if (KernelState == osKernelReady) { + /* Ensure SVC priority is at the reset value */ + SVC_Setup(); + /* Change state to enable IRQ masking check */ + KernelState = osKernelRunning; + /* Start the kernel scheduler */ + vTaskStartScheduler(); + stat = osOK; + } else { + stat = osError; + } + } + + return (stat); +} + +int32_t osKernelLock(void) +{ + int32_t lock; + + if (IS_IRQ()) { + lock = (int32_t)osErrorISR; + } + else { + switch (xTaskGetSchedulerState()) { + case taskSCHEDULER_SUSPENDED: + lock = 1; + break; + + case taskSCHEDULER_RUNNING: + vTaskSuspendAll(); + lock = 0; + break; + + case taskSCHEDULER_NOT_STARTED: + default: + lock = (int32_t)osError; + break; + } + } + + return (lock); +} + +int32_t osKernelUnlock(void) +{ + int32_t lock; + + if (IS_IRQ()) { + lock = (int32_t)osErrorISR; + } + else { + switch (xTaskGetSchedulerState()) { + case taskSCHEDULER_SUSPENDED: + lock = 1; + + if (xTaskResumeAll() != pdTRUE) { + if (xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED) { + lock = (int32_t)osError; + } + } + break; + + case taskSCHEDULER_RUNNING: + lock = 0; + break; + + case taskSCHEDULER_NOT_STARTED: + default: + lock = (int32_t)osError; + break; + } + } + + return (lock); +} + +int32_t osKernelRestoreLock(int32_t lock) +{ + + if (IS_IRQ()) { + lock = (int32_t)osErrorISR; + } + else { + switch (xTaskGetSchedulerState()) { + case taskSCHEDULER_SUSPENDED: + case taskSCHEDULER_RUNNING: + if (lock == 1) { + vTaskSuspendAll(); + } + else { + if (lock != 0) { + lock = (int32_t)osError; + } + else { + if (xTaskResumeAll() != pdTRUE) { + if (xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) { + lock = (int32_t)osError; + } + } + } + } + break; + + case taskSCHEDULER_NOT_STARTED: + default: + lock = (int32_t)osError; + break; + } + } + + return (lock); +} + +uint32_t osKernelGetTickCount(void) +{ + TickType_t ticks; + + if (IS_IRQ()) { + ticks = xTaskGetTickCountFromISR(); + } else { + ticks = xTaskGetTickCount(); + } + + return (ticks); +} + +uint32_t osKernelGetTickFreq(void) +{ + return (configTICK_RATE_HZ); +} + +/*---------------------------------------------------------------------------*/ + +osThreadId_t osThreadNew(osThreadFunc_t func,void *argument, + const osThreadAttr_t *attr) +{ + const char *name; + uint32_t stack; + TaskHandle_t hTask; + UBaseType_t prio; + int32_t mem; + + hTask = NULL; + + if (!IS_IRQ() && (func != NULL)) { + stack = configMINIMAL_STACK_SIZE; + prio = (UBaseType_t)osPriorityNormal; + + name = NULL; + mem = -1; + + if (attr != NULL) { + if (attr->name != NULL) { + name = attr->name; + } + if (attr->priority != osPriorityNone) { + prio = (UBaseType_t)attr->priority; + } + + if ((prio < osPriorityIdle) || (prio > osPriorityISR) || + ((attr->attr_bits & osThreadJoinable) == osThreadJoinable)) { + return (NULL); + } + + if (attr->stack_size > 0U) { + /* In FreeRTOS stack is not in bytes, */ + /* but in sizeof(StackType_t) which is 4 on ARM ports. */ + /* Stack size should be therefore 4 byte aligned in order to */ + /* avoid division caused side effects */ + stack = attr->stack_size / sizeof(StackType_t); + } + + if ((attr->cb_mem != NULL) && + (attr->cb_size >= sizeof(StaticTask_t)) && + (attr->stack_mem != NULL) && (attr->stack_size > 0U)) { + mem = 1; + } + else { + if ((attr->cb_mem == NULL) && (attr->cb_size == 0U) && + (attr->stack_mem == NULL)) { + mem = 0; + } + } + } + else { + mem = 0; + } + + if (mem == 1) { + hTask = xTaskCreateStatic((TaskFunction_t)func, name, stack, argument, + prio, (StackType_t *)attr->stack_mem, (StaticTask_t *)attr->cb_mem); + } + else { + if (mem == 0) { + if (xTaskCreate ((TaskFunction_t)func, name, (uint16_t)stack, argument, + prio, &hTask) != pdPASS) { + hTask = NULL; + } + } + } + } + + return ((osThreadId_t)hTask); +} + + +uint32_t osThreadFlagsSet(osThreadId_t thread_id, uint32_t flags) +{ + TaskHandle_t hTask = (TaskHandle_t)thread_id; + uint32_t rflags; + BaseType_t yield; + + if ((hTask == NULL) || ((flags & THREAD_FLAGS_INVALID_BITS) != 0U)) { + rflags = (uint32_t)osErrorParameter; + } + else { + rflags = (uint32_t)osError; + + if (IS_IRQ()) { + yield = pdFALSE; + + (void)xTaskNotifyFromISR (hTask, flags, eSetBits, &yield); + (void)xTaskNotifyAndQueryFromISR (hTask, 0, eNoAction, &rflags, NULL); + + portYIELD_FROM_ISR (yield); + } + else { + (void)xTaskNotify (hTask, flags, eSetBits); + (void)xTaskNotifyAndQuery (hTask, 0, eNoAction, &rflags); + } + } + /* Return flags after setting */ + return (rflags); +} + +uint32_t osThreadFlagsClear (uint32_t flags) { + TaskHandle_t hTask; + uint32_t rflags, cflags; + + if (IS_IRQ()) { + rflags = (uint32_t)osErrorISR; + } + else if ((flags & THREAD_FLAGS_INVALID_BITS) != 0U) { + rflags = (uint32_t)osErrorParameter; + } + else { + hTask = xTaskGetCurrentTaskHandle(); + + if (xTaskNotifyAndQuery (hTask, 0, eNoAction, &cflags) == pdPASS) { + rflags = cflags; + cflags &= ~flags; + + if (xTaskNotify (hTask, cflags, eSetValueWithOverwrite) != pdPASS) { + rflags = (uint32_t)osError; + } + } + else { + rflags = (uint32_t)osError; + } + } + + /* Return flags before clearing */ + return (rflags); +} + +uint32_t osThreadFlagsWait(uint32_t flags, uint32_t options, uint32_t timeout) +{ + uint32_t rflags, nval; + uint32_t clear; + TickType_t t0, td, tout; + BaseType_t rval; + + if (IS_IRQ()) { + rflags = (uint32_t)osErrorISR; + } + else if ((flags & THREAD_FLAGS_INVALID_BITS) != 0U) { + rflags = (uint32_t)osErrorParameter; + } + else { + if ((options & osFlagsNoClear) == osFlagsNoClear) { + clear = 0U; + } else { + clear = flags; + } + + rflags = 0U; + tout = timeout; + + t0 = xTaskGetTickCount(); + do { + rval = xTaskNotifyWait(0, clear, &nval, tout); + + if (rval == pdPASS) { + rflags &= flags; + rflags |= nval; + + if ((options & osFlagsWaitAll) == osFlagsWaitAll) { + if ((flags & rflags) == flags) { + break; + } else { + if (timeout == 0U) { + rflags = (uint32_t)osErrorResource; + break; + } + } + } + else { + if ((flags & rflags) != 0) { + break; + } else { + if (timeout == 0U) { + rflags = (uint32_t)osErrorResource; + break; + } + } + } + + /* Update timeout */ + td = xTaskGetTickCount() - t0; + + if (td > tout) { + tout = 0; + } else { + tout -= td; + } + } + else { + if (timeout == 0) { + rflags = (uint32_t)osErrorResource; + } else { + rflags = (uint32_t)osErrorTimeout; + } + } + } + while (rval != pdFAIL); + } + + /* Return flags before clearing */ + return (rflags); +} + +/*---------------------------------------------------------------------------*/ + +/* External Idle and Timer task static memory allocation functions */ + +/* External Idle and Timer task static memory allocation functions */ +extern void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, + StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize); +extern void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, + StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize); + +/* Idle task control block and stack */ +static StaticTask_t Idle_TCB; +static StackType_t Idle_Stack[configMINIMAL_STACK_SIZE]; + +/* Timer task control block and stack */ +static StaticTask_t Timer_TCB; +static StackType_t Timer_Stack[configTIMER_TASK_STACK_DEPTH]; + +/* + vApplicationGetIdleTaskMemory gets called when + configSUPPORT_STATIC_ALLOCATION + equals to 1 and is required for static memory allocation support. +*/ +void vApplicationGetIdleTaskMemory(StaticTask_t **ppxIdleTaskTCBBuffer, + StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize) +{ + *ppxIdleTaskTCBBuffer = &Idle_TCB; + *ppxIdleTaskStackBuffer = &Idle_Stack[0]; + *pulIdleTaskStackSize = (uint32_t)configMINIMAL_STACK_SIZE; +} + +/* + vApplicationGetTimerTaskMemory gets called when + configSUPPORT_STATIC_ALLOCATION + equals to 1 and is required for static memory allocation support. +*/ +void vApplicationGetTimerTaskMemory(StaticTask_t **ppxTimerTaskTCBBuffer, + StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize) +{ + *ppxTimerTaskTCBBuffer = &Timer_TCB; + *ppxTimerTaskStackBuffer = &Timer_Stack[0]; + *pulTimerTaskStackSize = (uint32_t)configTIMER_TASK_STACK_DEPTH; +} diff --git a/product/rcar/src/CMSIS-FreeRTOS/Source/include/FreeRTOS.h b/product/rcar/src/CMSIS-FreeRTOS/Source/include/FreeRTOS.h new file mode 100644 index 000000000..ceb469a72 --- /dev/null +++ b/product/rcar/src/CMSIS-FreeRTOS/Source/include/FreeRTOS.h @@ -0,0 +1,1295 @@ +/* + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +#ifndef INC_FREERTOS_H +#define INC_FREERTOS_H + +/* + * Include the generic headers required for the FreeRTOS port being used. + */ +#include + +/* + * If stdint.h cannot be located then: + * + If using GCC ensure the -nostdint options is *not* being used. + * + Ensure the project's include path includes the directory in which your + * compiler stores stdint.h. + * + Set any compiler options necessary for it to support C99, as technically + * stdint.h is only mandatory with C99 (FreeRTOS does not require C99 in any + * other way). + * + The FreeRTOS download includes a simple stdint.h definition that can be + * used in cases where none is provided by the compiler. The files only + * contains the typedefs required to build FreeRTOS. Read the instructions + * in FreeRTOS/source/stdint.readme for more information. + */ +#include /* READ COMMENT ABOVE. */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Application specific configuration options. */ +#include "FreeRTOSConfig.h" + +/* Basic FreeRTOS definitions. */ +#include "projdefs.h" + +/* Definitions specific to the port being used. */ +#include "portable.h" + +/* Must be defaulted before configUSE_NEWLIB_REENTRANT is used below. */ +#ifndef configUSE_NEWLIB_REENTRANT + #define configUSE_NEWLIB_REENTRANT 0 +#endif + +/* Required if struct _reent is used. */ +#if ( configUSE_NEWLIB_REENTRANT == 1 ) + #include +#endif +/* + * Check all the required application specific macros have been defined. + * These macros are application specific and (as downloaded) are defined + * within FreeRTOSConfig.h. + */ + +#ifndef configMINIMAL_STACK_SIZE + #error Missing definition: configMINIMAL_STACK_SIZE must be defined in FreeRTOSConfig.h. configMINIMAL_STACK_SIZE defines the size (in words) of the stack allocated to the idle task. Refer to the demo project provided for your port for a suitable value. +#endif + +#ifndef configMAX_PRIORITIES + #error Missing definition: configMAX_PRIORITIES must be defined in FreeRTOSConfig.h. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#if configMAX_PRIORITIES < 1 + #error configMAX_PRIORITIES must be defined to be greater than or equal to 1. +#endif + +#ifndef configUSE_PREEMPTION + #error Missing definition: configUSE_PREEMPTION must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef configUSE_IDLE_HOOK + #error Missing definition: configUSE_IDLE_HOOK must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef configUSE_TICK_HOOK + #error Missing definition: configUSE_TICK_HOOK must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef configUSE_16_BIT_TICKS + #error Missing definition: configUSE_16_BIT_TICKS must be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef configUSE_CO_ROUTINES + #define configUSE_CO_ROUTINES 0 +#endif + +#ifndef INCLUDE_vTaskPrioritySet + #define INCLUDE_vTaskPrioritySet 0 +#endif + +#ifndef INCLUDE_uxTaskPriorityGet + #define INCLUDE_uxTaskPriorityGet 0 +#endif + +#ifndef INCLUDE_vTaskDelete + #define INCLUDE_vTaskDelete 0 +#endif + +#ifndef INCLUDE_vTaskSuspend + #define INCLUDE_vTaskSuspend 0 +#endif + +#ifndef INCLUDE_vTaskDelayUntil + #define INCLUDE_vTaskDelayUntil 0 +#endif + +#ifndef INCLUDE_vTaskDelay + #define INCLUDE_vTaskDelay 0 +#endif + +#ifndef INCLUDE_xTaskGetIdleTaskHandle + #define INCLUDE_xTaskGetIdleTaskHandle 0 +#endif + +#ifndef INCLUDE_xTaskAbortDelay + #define INCLUDE_xTaskAbortDelay 0 +#endif + +#ifndef INCLUDE_xQueueGetMutexHolder + #define INCLUDE_xQueueGetMutexHolder 0 +#endif + +#ifndef INCLUDE_xSemaphoreGetMutexHolder + #define INCLUDE_xSemaphoreGetMutexHolder INCLUDE_xQueueGetMutexHolder +#endif + +#ifndef INCLUDE_xTaskGetHandle + #define INCLUDE_xTaskGetHandle 0 +#endif + +#ifndef INCLUDE_uxTaskGetStackHighWaterMark + #define INCLUDE_uxTaskGetStackHighWaterMark 0 +#endif + +#ifndef INCLUDE_uxTaskGetStackHighWaterMark2 + #define INCLUDE_uxTaskGetStackHighWaterMark2 0 +#endif + +#ifndef INCLUDE_eTaskGetState + #define INCLUDE_eTaskGetState 0 +#endif + +#ifndef INCLUDE_xTaskResumeFromISR + #define INCLUDE_xTaskResumeFromISR 1 +#endif + +#ifndef INCLUDE_xTimerPendFunctionCall + #define INCLUDE_xTimerPendFunctionCall 0 +#endif + +#ifndef INCLUDE_xTaskGetSchedulerState + #define INCLUDE_xTaskGetSchedulerState 0 +#endif + +#ifndef INCLUDE_xTaskGetCurrentTaskHandle + #define INCLUDE_xTaskGetCurrentTaskHandle 0 +#endif + +#if configUSE_CO_ROUTINES != 0 + #ifndef configMAX_CO_ROUTINE_PRIORITIES + #error configMAX_CO_ROUTINE_PRIORITIES must be greater than or equal to 1. + #endif +#endif + +#ifndef configUSE_DAEMON_TASK_STARTUP_HOOK + #define configUSE_DAEMON_TASK_STARTUP_HOOK 0 +#endif + +#ifndef configUSE_APPLICATION_TASK_TAG + #define configUSE_APPLICATION_TASK_TAG 0 +#endif + +#ifndef configNUM_THREAD_LOCAL_STORAGE_POINTERS + #define configNUM_THREAD_LOCAL_STORAGE_POINTERS 0 +#endif + +#ifndef configUSE_RECURSIVE_MUTEXES + #define configUSE_RECURSIVE_MUTEXES 0 +#endif + +#ifndef configUSE_MUTEXES + #define configUSE_MUTEXES 0 +#endif + +#ifndef configUSE_TIMERS + #define configUSE_TIMERS 0 +#endif + +#ifndef configUSE_COUNTING_SEMAPHORES + #define configUSE_COUNTING_SEMAPHORES 0 +#endif + +#ifndef configUSE_ALTERNATIVE_API + #define configUSE_ALTERNATIVE_API 0 +#endif + +#ifndef portCRITICAL_NESTING_IN_TCB + #define portCRITICAL_NESTING_IN_TCB 0 +#endif + +#ifndef configMAX_TASK_NAME_LEN + #define configMAX_TASK_NAME_LEN 16 +#endif + +#ifndef configIDLE_SHOULD_YIELD + #define configIDLE_SHOULD_YIELD 1 +#endif + +#if configMAX_TASK_NAME_LEN < 1 + #error configMAX_TASK_NAME_LEN must be set to a minimum of 1 in FreeRTOSConfig.h +#endif + +#ifndef configASSERT + #define configASSERT( x ) + #define configASSERT_DEFINED 0 +#else + #define configASSERT_DEFINED 1 +#endif + +/* configPRECONDITION should be defined as configASSERT. +The CBMC proofs need a way to track assumptions and assertions. +A configPRECONDITION statement should express an implicit invariant or +assumption made. A configASSERT statement should express an invariant that must +hold explicit before calling the code. */ +#ifndef configPRECONDITION + #define configPRECONDITION( X ) configASSERT(X) + #define configPRECONDITION_DEFINED 0 +#else + #define configPRECONDITION_DEFINED 1 +#endif + +#ifndef portMEMORY_BARRIER + #define portMEMORY_BARRIER() +#endif + +#ifndef portSOFTWARE_BARRIER + #define portSOFTWARE_BARRIER() +#endif + +/* The timers module relies on xTaskGetSchedulerState(). */ +#if configUSE_TIMERS == 1 + + #ifndef configTIMER_TASK_PRIORITY + #error If configUSE_TIMERS is set to 1 then configTIMER_TASK_PRIORITY must also be defined. + #endif /* configTIMER_TASK_PRIORITY */ + + #ifndef configTIMER_QUEUE_LENGTH + #error If configUSE_TIMERS is set to 1 then configTIMER_QUEUE_LENGTH must also be defined. + #endif /* configTIMER_QUEUE_LENGTH */ + + #ifndef configTIMER_TASK_STACK_DEPTH + #error If configUSE_TIMERS is set to 1 then configTIMER_TASK_STACK_DEPTH must also be defined. + #endif /* configTIMER_TASK_STACK_DEPTH */ + +#endif /* configUSE_TIMERS */ + +#ifndef portSET_INTERRUPT_MASK_FROM_ISR + #define portSET_INTERRUPT_MASK_FROM_ISR() 0 +#endif + +#ifndef portCLEAR_INTERRUPT_MASK_FROM_ISR + #define portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedStatusValue ) ( void ) uxSavedStatusValue +#endif + +#ifndef portCLEAN_UP_TCB + #define portCLEAN_UP_TCB( pxTCB ) ( void ) pxTCB +#endif + +#ifndef portPRE_TASK_DELETE_HOOK + #define portPRE_TASK_DELETE_HOOK( pvTaskToDelete, pxYieldPending ) +#endif + +#ifndef portSETUP_TCB + #define portSETUP_TCB( pxTCB ) ( void ) pxTCB +#endif + +#ifndef configQUEUE_REGISTRY_SIZE + #define configQUEUE_REGISTRY_SIZE 0U +#endif + +#if ( configQUEUE_REGISTRY_SIZE < 1 ) + #define vQueueAddToRegistry( xQueue, pcName ) + #define vQueueUnregisterQueue( xQueue ) + #define pcQueueGetName( xQueue ) +#endif + +#ifndef portPOINTER_SIZE_TYPE + #define portPOINTER_SIZE_TYPE uint32_t +#endif + +/* Remove any unused trace macros. */ +#ifndef traceSTART + /* Used to perform any necessary initialisation - for example, open a file + into which trace is to be written. */ + #define traceSTART() +#endif + +#ifndef traceEND + /* Use to close a trace, for example close a file into which trace has been + written. */ + #define traceEND() +#endif + +#ifndef traceTASK_SWITCHED_IN + /* Called after a task has been selected to run. pxCurrentTCB holds a pointer + to the task control block of the selected task. */ + #define traceTASK_SWITCHED_IN() +#endif + +#ifndef traceINCREASE_TICK_COUNT + /* Called before stepping the tick count after waking from tickless idle + sleep. */ + #define traceINCREASE_TICK_COUNT( x ) +#endif + +#ifndef traceLOW_POWER_IDLE_BEGIN + /* Called immediately before entering tickless idle. */ + #define traceLOW_POWER_IDLE_BEGIN() +#endif + +#ifndef traceLOW_POWER_IDLE_END + /* Called when returning to the Idle task after a tickless idle. */ + #define traceLOW_POWER_IDLE_END() +#endif + +#ifndef traceTASK_SWITCHED_OUT + /* Called before a task has been selected to run. pxCurrentTCB holds a pointer + to the task control block of the task being switched out. */ + #define traceTASK_SWITCHED_OUT() +#endif + +#ifndef traceTASK_PRIORITY_INHERIT + /* Called when a task attempts to take a mutex that is already held by a + lower priority task. pxTCBOfMutexHolder is a pointer to the TCB of the task + that holds the mutex. uxInheritedPriority is the priority the mutex holder + will inherit (the priority of the task that is attempting to obtain the + muted. */ + #define traceTASK_PRIORITY_INHERIT( pxTCBOfMutexHolder, uxInheritedPriority ) +#endif + +#ifndef traceTASK_PRIORITY_DISINHERIT + /* Called when a task releases a mutex, the holding of which had resulted in + the task inheriting the priority of a higher priority task. + pxTCBOfMutexHolder is a pointer to the TCB of the task that is releasing the + mutex. uxOriginalPriority is the task's configured (base) priority. */ + #define traceTASK_PRIORITY_DISINHERIT( pxTCBOfMutexHolder, uxOriginalPriority ) +#endif + +#ifndef traceBLOCKING_ON_QUEUE_RECEIVE + /* Task is about to block because it cannot read from a + queue/mutex/semaphore. pxQueue is a pointer to the queue/mutex/semaphore + upon which the read was attempted. pxCurrentTCB points to the TCB of the + task that attempted the read. */ + #define traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ) +#endif + +#ifndef traceBLOCKING_ON_QUEUE_PEEK + /* Task is about to block because it cannot read from a + queue/mutex/semaphore. pxQueue is a pointer to the queue/mutex/semaphore + upon which the read was attempted. pxCurrentTCB points to the TCB of the + task that attempted the read. */ + #define traceBLOCKING_ON_QUEUE_PEEK( pxQueue ) +#endif + +#ifndef traceBLOCKING_ON_QUEUE_SEND + /* Task is about to block because it cannot write to a + queue/mutex/semaphore. pxQueue is a pointer to the queue/mutex/semaphore + upon which the write was attempted. pxCurrentTCB points to the TCB of the + task that attempted the write. */ + #define traceBLOCKING_ON_QUEUE_SEND( pxQueue ) +#endif + +#ifndef configCHECK_FOR_STACK_OVERFLOW + #define configCHECK_FOR_STACK_OVERFLOW 0 +#endif + +#ifndef configRECORD_STACK_HIGH_ADDRESS + #define configRECORD_STACK_HIGH_ADDRESS 0 +#endif + +#ifndef configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H + #define configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H 0 +#endif + +/* The following event macros are embedded in the kernel API calls. */ + +#ifndef traceMOVED_TASK_TO_READY_STATE + #define traceMOVED_TASK_TO_READY_STATE( pxTCB ) +#endif + +#ifndef tracePOST_MOVED_TASK_TO_READY_STATE + #define tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB ) +#endif + +#ifndef traceQUEUE_CREATE + #define traceQUEUE_CREATE( pxNewQueue ) +#endif + +#ifndef traceQUEUE_CREATE_FAILED + #define traceQUEUE_CREATE_FAILED( ucQueueType ) +#endif + +#ifndef traceCREATE_MUTEX + #define traceCREATE_MUTEX( pxNewQueue ) +#endif + +#ifndef traceCREATE_MUTEX_FAILED + #define traceCREATE_MUTEX_FAILED() +#endif + +#ifndef traceGIVE_MUTEX_RECURSIVE + #define traceGIVE_MUTEX_RECURSIVE( pxMutex ) +#endif + +#ifndef traceGIVE_MUTEX_RECURSIVE_FAILED + #define traceGIVE_MUTEX_RECURSIVE_FAILED( pxMutex ) +#endif + +#ifndef traceTAKE_MUTEX_RECURSIVE + #define traceTAKE_MUTEX_RECURSIVE( pxMutex ) +#endif + +#ifndef traceTAKE_MUTEX_RECURSIVE_FAILED + #define traceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex ) +#endif + +#ifndef traceCREATE_COUNTING_SEMAPHORE + #define traceCREATE_COUNTING_SEMAPHORE() +#endif + +#ifndef traceCREATE_COUNTING_SEMAPHORE_FAILED + #define traceCREATE_COUNTING_SEMAPHORE_FAILED() +#endif + +#ifndef traceQUEUE_SEND + #define traceQUEUE_SEND( pxQueue ) +#endif + +#ifndef traceQUEUE_SEND_FAILED + #define traceQUEUE_SEND_FAILED( pxQueue ) +#endif + +#ifndef traceQUEUE_RECEIVE + #define traceQUEUE_RECEIVE( pxQueue ) +#endif + +#ifndef traceQUEUE_PEEK + #define traceQUEUE_PEEK( pxQueue ) +#endif + +#ifndef traceQUEUE_PEEK_FAILED + #define traceQUEUE_PEEK_FAILED( pxQueue ) +#endif + +#ifndef traceQUEUE_PEEK_FROM_ISR + #define traceQUEUE_PEEK_FROM_ISR( pxQueue ) +#endif + +#ifndef traceQUEUE_RECEIVE_FAILED + #define traceQUEUE_RECEIVE_FAILED( pxQueue ) +#endif + +#ifndef traceQUEUE_SEND_FROM_ISR + #define traceQUEUE_SEND_FROM_ISR( pxQueue ) +#endif + +#ifndef traceQUEUE_SEND_FROM_ISR_FAILED + #define traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue ) +#endif + +#ifndef traceQUEUE_RECEIVE_FROM_ISR + #define traceQUEUE_RECEIVE_FROM_ISR( pxQueue ) +#endif + +#ifndef traceQUEUE_RECEIVE_FROM_ISR_FAILED + #define traceQUEUE_RECEIVE_FROM_ISR_FAILED( pxQueue ) +#endif + +#ifndef traceQUEUE_PEEK_FROM_ISR_FAILED + #define traceQUEUE_PEEK_FROM_ISR_FAILED( pxQueue ) +#endif + +#ifndef traceQUEUE_DELETE + #define traceQUEUE_DELETE( pxQueue ) +#endif + +#ifndef traceTASK_CREATE + #define traceTASK_CREATE( pxNewTCB ) +#endif + +#ifndef traceTASK_CREATE_FAILED + #define traceTASK_CREATE_FAILED() +#endif + +#ifndef traceTASK_DELETE + #define traceTASK_DELETE( pxTaskToDelete ) +#endif + +#ifndef traceTASK_DELAY_UNTIL + #define traceTASK_DELAY_UNTIL( x ) +#endif + +#ifndef traceTASK_DELAY + #define traceTASK_DELAY() +#endif + +#ifndef traceTASK_PRIORITY_SET + #define traceTASK_PRIORITY_SET( pxTask, uxNewPriority ) +#endif + +#ifndef traceTASK_SUSPEND + #define traceTASK_SUSPEND( pxTaskToSuspend ) +#endif + +#ifndef traceTASK_RESUME + #define traceTASK_RESUME( pxTaskToResume ) +#endif + +#ifndef traceTASK_RESUME_FROM_ISR + #define traceTASK_RESUME_FROM_ISR( pxTaskToResume ) +#endif + +#ifndef traceTASK_INCREMENT_TICK + #define traceTASK_INCREMENT_TICK( xTickCount ) +#endif + +#ifndef traceTIMER_CREATE + #define traceTIMER_CREATE( pxNewTimer ) +#endif + +#ifndef traceTIMER_CREATE_FAILED + #define traceTIMER_CREATE_FAILED() +#endif + +#ifndef traceTIMER_COMMAND_SEND + #define traceTIMER_COMMAND_SEND( xTimer, xMessageID, xMessageValueValue, xReturn ) +#endif + +#ifndef traceTIMER_EXPIRED + #define traceTIMER_EXPIRED( pxTimer ) +#endif + +#ifndef traceTIMER_COMMAND_RECEIVED + #define traceTIMER_COMMAND_RECEIVED( pxTimer, xMessageID, xMessageValue ) +#endif + +#ifndef traceMALLOC + #define traceMALLOC( pvAddress, uiSize ) +#endif + +#ifndef traceFREE + #define traceFREE( pvAddress, uiSize ) +#endif + +#ifndef traceEVENT_GROUP_CREATE + #define traceEVENT_GROUP_CREATE( xEventGroup ) +#endif + +#ifndef traceEVENT_GROUP_CREATE_FAILED + #define traceEVENT_GROUP_CREATE_FAILED() +#endif + +#ifndef traceEVENT_GROUP_SYNC_BLOCK + #define traceEVENT_GROUP_SYNC_BLOCK( xEventGroup, uxBitsToSet, uxBitsToWaitFor ) +#endif + +#ifndef traceEVENT_GROUP_SYNC_END + #define traceEVENT_GROUP_SYNC_END( xEventGroup, uxBitsToSet, uxBitsToWaitFor, xTimeoutOccurred ) ( void ) xTimeoutOccurred +#endif + +#ifndef traceEVENT_GROUP_WAIT_BITS_BLOCK + #define traceEVENT_GROUP_WAIT_BITS_BLOCK( xEventGroup, uxBitsToWaitFor ) +#endif + +#ifndef traceEVENT_GROUP_WAIT_BITS_END + #define traceEVENT_GROUP_WAIT_BITS_END( xEventGroup, uxBitsToWaitFor, xTimeoutOccurred ) ( void ) xTimeoutOccurred +#endif + +#ifndef traceEVENT_GROUP_CLEAR_BITS + #define traceEVENT_GROUP_CLEAR_BITS( xEventGroup, uxBitsToClear ) +#endif + +#ifndef traceEVENT_GROUP_CLEAR_BITS_FROM_ISR + #define traceEVENT_GROUP_CLEAR_BITS_FROM_ISR( xEventGroup, uxBitsToClear ) +#endif + +#ifndef traceEVENT_GROUP_SET_BITS + #define traceEVENT_GROUP_SET_BITS( xEventGroup, uxBitsToSet ) +#endif + +#ifndef traceEVENT_GROUP_SET_BITS_FROM_ISR + #define traceEVENT_GROUP_SET_BITS_FROM_ISR( xEventGroup, uxBitsToSet ) +#endif + +#ifndef traceEVENT_GROUP_DELETE + #define traceEVENT_GROUP_DELETE( xEventGroup ) +#endif + +#ifndef tracePEND_FUNC_CALL + #define tracePEND_FUNC_CALL(xFunctionToPend, pvParameter1, ulParameter2, ret) +#endif + +#ifndef tracePEND_FUNC_CALL_FROM_ISR + #define tracePEND_FUNC_CALL_FROM_ISR(xFunctionToPend, pvParameter1, ulParameter2, ret) +#endif + +#ifndef traceQUEUE_REGISTRY_ADD + #define traceQUEUE_REGISTRY_ADD(xQueue, pcQueueName) +#endif + +#ifndef traceTASK_NOTIFY_TAKE_BLOCK + #define traceTASK_NOTIFY_TAKE_BLOCK() +#endif + +#ifndef traceTASK_NOTIFY_TAKE + #define traceTASK_NOTIFY_TAKE() +#endif + +#ifndef traceTASK_NOTIFY_WAIT_BLOCK + #define traceTASK_NOTIFY_WAIT_BLOCK() +#endif + +#ifndef traceTASK_NOTIFY_WAIT + #define traceTASK_NOTIFY_WAIT() +#endif + +#ifndef traceTASK_NOTIFY + #define traceTASK_NOTIFY() +#endif + +#ifndef traceTASK_NOTIFY_FROM_ISR + #define traceTASK_NOTIFY_FROM_ISR() +#endif + +#ifndef traceTASK_NOTIFY_GIVE_FROM_ISR + #define traceTASK_NOTIFY_GIVE_FROM_ISR() +#endif + +#ifndef traceSTREAM_BUFFER_CREATE_FAILED + #define traceSTREAM_BUFFER_CREATE_FAILED( xIsMessageBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_CREATE_STATIC_FAILED + #define traceSTREAM_BUFFER_CREATE_STATIC_FAILED( xReturn, xIsMessageBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_CREATE + #define traceSTREAM_BUFFER_CREATE( pxStreamBuffer, xIsMessageBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_DELETE + #define traceSTREAM_BUFFER_DELETE( xStreamBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_RESET + #define traceSTREAM_BUFFER_RESET( xStreamBuffer ) +#endif + +#ifndef traceBLOCKING_ON_STREAM_BUFFER_SEND + #define traceBLOCKING_ON_STREAM_BUFFER_SEND( xStreamBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_SEND + #define traceSTREAM_BUFFER_SEND( xStreamBuffer, xBytesSent ) +#endif + +#ifndef traceSTREAM_BUFFER_SEND_FAILED + #define traceSTREAM_BUFFER_SEND_FAILED( xStreamBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_SEND_FROM_ISR + #define traceSTREAM_BUFFER_SEND_FROM_ISR( xStreamBuffer, xBytesSent ) +#endif + +#ifndef traceBLOCKING_ON_STREAM_BUFFER_RECEIVE + #define traceBLOCKING_ON_STREAM_BUFFER_RECEIVE( xStreamBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_RECEIVE + #define traceSTREAM_BUFFER_RECEIVE( xStreamBuffer, xReceivedLength ) +#endif + +#ifndef traceSTREAM_BUFFER_RECEIVE_FAILED + #define traceSTREAM_BUFFER_RECEIVE_FAILED( xStreamBuffer ) +#endif + +#ifndef traceSTREAM_BUFFER_RECEIVE_FROM_ISR + #define traceSTREAM_BUFFER_RECEIVE_FROM_ISR( xStreamBuffer, xReceivedLength ) +#endif + +#ifndef configGENERATE_RUN_TIME_STATS + #define configGENERATE_RUN_TIME_STATS 0 +#endif + +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + + #ifndef portCONFIGURE_TIMER_FOR_RUN_TIME_STATS + #error If configGENERATE_RUN_TIME_STATS is defined then portCONFIGURE_TIMER_FOR_RUN_TIME_STATS must also be defined. portCONFIGURE_TIMER_FOR_RUN_TIME_STATS should call a port layer function to setup a peripheral timer/counter that can then be used as the run time counter time base. + #endif /* portCONFIGURE_TIMER_FOR_RUN_TIME_STATS */ + + #ifndef portGET_RUN_TIME_COUNTER_VALUE + #ifndef portALT_GET_RUN_TIME_COUNTER_VALUE + #error If configGENERATE_RUN_TIME_STATS is defined then either portGET_RUN_TIME_COUNTER_VALUE or portALT_GET_RUN_TIME_COUNTER_VALUE must also be defined. See the examples provided and the FreeRTOS web site for more information. + #endif /* portALT_GET_RUN_TIME_COUNTER_VALUE */ + #endif /* portGET_RUN_TIME_COUNTER_VALUE */ + +#endif /* configGENERATE_RUN_TIME_STATS */ + +#ifndef portCONFIGURE_TIMER_FOR_RUN_TIME_STATS + #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() +#endif + +#ifndef configUSE_MALLOC_FAILED_HOOK + #define configUSE_MALLOC_FAILED_HOOK 0 +#endif + +#ifndef portPRIVILEGE_BIT + #define portPRIVILEGE_BIT ( ( UBaseType_t ) 0x00 ) +#endif + +#ifndef portYIELD_WITHIN_API + #define portYIELD_WITHIN_API portYIELD +#endif + +#ifndef portSUPPRESS_TICKS_AND_SLEEP + #define portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ) +#endif + +#ifndef configEXPECTED_IDLE_TIME_BEFORE_SLEEP + #define configEXPECTED_IDLE_TIME_BEFORE_SLEEP 2 +#endif + +#if configEXPECTED_IDLE_TIME_BEFORE_SLEEP < 2 + #error configEXPECTED_IDLE_TIME_BEFORE_SLEEP must not be less than 2 +#endif + +#ifndef configUSE_TICKLESS_IDLE + #define configUSE_TICKLESS_IDLE 0 +#endif + +#ifndef configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING + #define configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING( x ) +#endif + +#ifndef configPRE_SLEEP_PROCESSING + #define configPRE_SLEEP_PROCESSING( x ) +#endif + +#ifndef configPOST_SLEEP_PROCESSING + #define configPOST_SLEEP_PROCESSING( x ) +#endif + +#ifndef configUSE_QUEUE_SETS + #define configUSE_QUEUE_SETS 0 +#endif + +#ifndef portTASK_USES_FLOATING_POINT + #define portTASK_USES_FLOATING_POINT() +#endif + +#ifndef portALLOCATE_SECURE_CONTEXT + #define portALLOCATE_SECURE_CONTEXT( ulSecureStackSize ) +#endif + +#ifndef portDONT_DISCARD + #define portDONT_DISCARD +#endif + +#ifndef configUSE_TIME_SLICING + #define configUSE_TIME_SLICING 1 +#endif + +#ifndef configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS + #define configINCLUDE_APPLICATION_DEFINED_PRIVILEGED_FUNCTIONS 0 +#endif + +#ifndef configUSE_STATS_FORMATTING_FUNCTIONS + #define configUSE_STATS_FORMATTING_FUNCTIONS 0 +#endif + +#ifndef portASSERT_IF_INTERRUPT_PRIORITY_INVALID + #define portASSERT_IF_INTERRUPT_PRIORITY_INVALID() +#endif + +#ifndef configUSE_TRACE_FACILITY + #define configUSE_TRACE_FACILITY 0 +#endif + +#ifndef mtCOVERAGE_TEST_MARKER + #define mtCOVERAGE_TEST_MARKER() +#endif + +#ifndef mtCOVERAGE_TEST_DELAY + #define mtCOVERAGE_TEST_DELAY() +#endif + +#ifndef portASSERT_IF_IN_ISR + #define portASSERT_IF_IN_ISR() +#endif + +#ifndef configUSE_PORT_OPTIMISED_TASK_SELECTION + #define configUSE_PORT_OPTIMISED_TASK_SELECTION 0 +#endif + +#ifndef configAPPLICATION_ALLOCATED_HEAP + #define configAPPLICATION_ALLOCATED_HEAP 0 +#endif + +#ifndef configUSE_TASK_NOTIFICATIONS + #define configUSE_TASK_NOTIFICATIONS 1 +#endif + +#ifndef configUSE_POSIX_ERRNO + #define configUSE_POSIX_ERRNO 0 +#endif + +#ifndef portTICK_TYPE_IS_ATOMIC + #define portTICK_TYPE_IS_ATOMIC 0 +#endif + +#ifndef configSUPPORT_STATIC_ALLOCATION + /* Defaults to 0 for backward compatibility. */ + #define configSUPPORT_STATIC_ALLOCATION 0 +#endif + +#ifndef configSUPPORT_DYNAMIC_ALLOCATION + /* Defaults to 1 for backward compatibility. */ + #define configSUPPORT_DYNAMIC_ALLOCATION 1 +#endif + +#ifndef configSTACK_DEPTH_TYPE + /* Defaults to uint16_t for backward compatibility, but can be overridden + in FreeRTOSConfig.h if uint16_t is too restrictive. */ + #define configSTACK_DEPTH_TYPE uint16_t +#endif + +#ifndef configMESSAGE_BUFFER_LENGTH_TYPE + /* Defaults to size_t for backward compatibility, but can be overridden + in FreeRTOSConfig.h if lengths will always be less than the number of bytes + in a size_t. */ + #define configMESSAGE_BUFFER_LENGTH_TYPE size_t +#endif + +/* Sanity check the configuration. */ +#if( configUSE_TICKLESS_IDLE != 0 ) + #if( INCLUDE_vTaskSuspend != 1 ) + #error INCLUDE_vTaskSuspend must be set to 1 if configUSE_TICKLESS_IDLE is not set to 0 + #endif /* INCLUDE_vTaskSuspend */ +#endif /* configUSE_TICKLESS_IDLE */ + +#if( ( configSUPPORT_STATIC_ALLOCATION == 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 0 ) ) + #error configSUPPORT_STATIC_ALLOCATION and configSUPPORT_DYNAMIC_ALLOCATION cannot both be 0, but can both be 1. +#endif + +#if( ( configUSE_RECURSIVE_MUTEXES == 1 ) && ( configUSE_MUTEXES != 1 ) ) + #error configUSE_MUTEXES must be set to 1 to use recursive mutexes +#endif + +#ifndef configINITIAL_TICK_COUNT + #define configINITIAL_TICK_COUNT 0 +#endif + +#if( portTICK_TYPE_IS_ATOMIC == 0 ) + /* Either variables of tick type cannot be read atomically, or + portTICK_TYPE_IS_ATOMIC was not set - map the critical sections used when + the tick count is returned to the standard critical section macros. */ + #define portTICK_TYPE_ENTER_CRITICAL() portENTER_CRITICAL() + #define portTICK_TYPE_EXIT_CRITICAL() portEXIT_CRITICAL() + #define portTICK_TYPE_SET_INTERRUPT_MASK_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR() + #define portTICK_TYPE_CLEAR_INTERRUPT_MASK_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( ( x ) ) +#else + /* The tick type can be read atomically, so critical sections used when the + tick count is returned can be defined away. */ + #define portTICK_TYPE_ENTER_CRITICAL() + #define portTICK_TYPE_EXIT_CRITICAL() + #define portTICK_TYPE_SET_INTERRUPT_MASK_FROM_ISR() 0 + #define portTICK_TYPE_CLEAR_INTERRUPT_MASK_FROM_ISR( x ) ( void ) x +#endif + +/* Definitions to allow backward compatibility with FreeRTOS versions prior to +V8 if desired. */ +#ifndef configENABLE_BACKWARD_COMPATIBILITY + #define configENABLE_BACKWARD_COMPATIBILITY 1 +#endif + +#ifndef configPRINTF + /* configPRINTF() was not defined, so define it away to nothing. To use + configPRINTF() then define it as follows (where MyPrintFunction() is + provided by the application writer): + + void MyPrintFunction(const char *pcFormat, ... ); + #define configPRINTF( X ) MyPrintFunction X + + Then call like a standard printf() function, but placing brackets around + all parameters so they are passed as a single parameter. For example: + configPRINTF( ("Value = %d", MyVariable) ); */ + #define configPRINTF( X ) +#endif + +#ifndef configMAX + /* The application writer has not provided their own MAX macro, so define + the following generic implementation. */ + #define configMAX( a, b ) ( ( ( a ) > ( b ) ) ? ( a ) : ( b ) ) +#endif + +#ifndef configMIN + /* The application writer has not provided their own MAX macro, so define + the following generic implementation. */ + #define configMIN( a, b ) ( ( ( a ) < ( b ) ) ? ( a ) : ( b ) ) +#endif + +#if configENABLE_BACKWARD_COMPATIBILITY == 1 + #define eTaskStateGet eTaskGetState + #define portTickType TickType_t + #define xTaskHandle TaskHandle_t + #define xQueueHandle QueueHandle_t + #define xSemaphoreHandle SemaphoreHandle_t + #define xQueueSetHandle QueueSetHandle_t + #define xQueueSetMemberHandle QueueSetMemberHandle_t + #define xTimeOutType TimeOut_t + #define xMemoryRegion MemoryRegion_t + #define xTaskParameters TaskParameters_t + #define xTaskStatusType TaskStatus_t + #define xTimerHandle TimerHandle_t + #define xCoRoutineHandle CoRoutineHandle_t + #define pdTASK_HOOK_CODE TaskHookFunction_t + #define portTICK_RATE_MS portTICK_PERIOD_MS + #define pcTaskGetTaskName pcTaskGetName + #define pcTimerGetTimerName pcTimerGetName + #define pcQueueGetQueueName pcQueueGetName + #define vTaskGetTaskInfo vTaskGetInfo + #define xTaskGetIdleRunTimeCounter ulTaskGetIdleRunTimeCounter + + /* Backward compatibility within the scheduler code only - these definitions + are not really required but are included for completeness. */ + #define tmrTIMER_CALLBACK TimerCallbackFunction_t + #define pdTASK_CODE TaskFunction_t + #define xListItem ListItem_t + #define xList List_t + + /* For libraries that break the list data hiding, and access list structure + members directly (which is not supposed to be done). */ + #define pxContainer pvContainer +#endif /* configENABLE_BACKWARD_COMPATIBILITY */ + +#if( configUSE_ALTERNATIVE_API != 0 ) + #error The alternative API was deprecated some time ago, and was removed in FreeRTOS V9.0 0 +#endif + +/* Set configUSE_TASK_FPU_SUPPORT to 0 to omit floating point support even +if floating point hardware is otherwise supported by the FreeRTOS port in use. +This constant is not supported by all FreeRTOS ports that include floating +point support. */ +#ifndef configUSE_TASK_FPU_SUPPORT + #define configUSE_TASK_FPU_SUPPORT 1 +#endif + +/* Set configENABLE_MPU to 1 to enable MPU support and 0 to disable it. This is +currently used in ARMv8M ports. */ +#ifndef configENABLE_MPU + #define configENABLE_MPU 0 +#endif + +/* Set configENABLE_FPU to 1 to enable FPU support and 0 to disable it. This is +currently used in ARMv8M ports. */ +#ifndef configENABLE_FPU + #define configENABLE_FPU 1 +#endif + +/* Set configENABLE_TRUSTZONE to 1 enable TrustZone support and 0 to disable it. +This is currently used in ARMv8M ports. */ +#ifndef configENABLE_TRUSTZONE + #define configENABLE_TRUSTZONE 1 +#endif + +/* Set configRUN_FREERTOS_SECURE_ONLY to 1 to run the FreeRTOS ARMv8M port on +the Secure Side only. */ +#ifndef configRUN_FREERTOS_SECURE_ONLY + #define configRUN_FREERTOS_SECURE_ONLY 0 +#endif + +/* Sometimes the FreeRTOSConfig.h settings only allow a task to be created using + * dynamically allocated RAM, in which case when any task is deleted it is known + * that both the task's stack and TCB need to be freed. Sometimes the + * FreeRTOSConfig.h settings only allow a task to be created using statically + * allocated RAM, in which case when any task is deleted it is known that neither + * the task's stack or TCB should be freed. Sometimes the FreeRTOSConfig.h + * settings allow a task to be created using either statically or dynamically + * allocated RAM, in which case a member of the TCB is used to record whether the + * stack and/or TCB were allocated statically or dynamically, so when a task is + * deleted the RAM that was allocated dynamically is freed again and no attempt is + * made to free the RAM that was allocated statically. + * tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE is only true if it is possible for a + * task to be created using either statically or dynamically allocated RAM. Note + * that if portUSING_MPU_WRAPPERS is 1 then a protected task can be created with + * a statically allocated stack and a dynamically allocated TCB. + * + * The following table lists various combinations of portUSING_MPU_WRAPPERS, + * configSUPPORT_DYNAMIC_ALLOCATION and configSUPPORT_STATIC_ALLOCATION and + * when it is possible to have both static and dynamic allocation: + * +-----+---------+--------+-----------------------------+-----------------------------------+------------------+-----------+ + * | MPU | Dynamic | Static | Available Functions | Possible Allocations | Both Dynamic and | Need Free | + * | | | | | | Static Possible | | + * +-----+---------+--------+-----------------------------+-----------------------------------+------------------+-----------+ + * | 0 | 0 | 1 | xTaskCreateStatic | TCB - Static, Stack - Static | No | No | + * +-----|---------|--------|-----------------------------|-----------------------------------|------------------|-----------| + * | 0 | 1 | 0 | xTaskCreate | TCB - Dynamic, Stack - Dynamic | No | Yes | + * +-----|---------|--------|-----------------------------|-----------------------------------|------------------|-----------| + * | 0 | 1 | 1 | xTaskCreate, | 1. TCB - Dynamic, Stack - Dynamic | Yes | Yes | + * | | | | xTaskCreateStatic | 2. TCB - Static, Stack - Static | | | + * +-----|---------|--------|-----------------------------|-----------------------------------|------------------|-----------| + * | 1 | 0 | 1 | xTaskCreateStatic, | TCB - Static, Stack - Static | No | No | + * | | | | xTaskCreateRestrictedStatic | | | | + * +-----|---------|--------|-----------------------------|-----------------------------------|------------------|-----------| + * | 1 | 1 | 0 | xTaskCreate, | 1. TCB - Dynamic, Stack - Dynamic | Yes | Yes | + * | | | | xTaskCreateRestricted | 2. TCB - Dynamic, Stack - Static | | | + * +-----|---------|--------|-----------------------------|-----------------------------------|------------------|-----------| + * | 1 | 1 | 1 | xTaskCreate, | 1. TCB - Dynamic, Stack - Dynamic | Yes | Yes | + * | | | | xTaskCreateStatic, | 2. TCB - Dynamic, Stack - Static | | | + * | | | | xTaskCreateRestricted, | 3. TCB - Static, Stack - Static | | | + * | | | | xTaskCreateRestrictedStatic | | | | + * +-----+---------+--------+-----------------------------+-----------------------------------+------------------+-----------+ + */ +#define tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE ( ( ( portUSING_MPU_WRAPPERS == 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) || \ + ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) ) + +/* + * In line with software engineering best practice, FreeRTOS implements a strict + * data hiding policy, so the real structures used by FreeRTOS to maintain the + * state of tasks, queues, semaphores, etc. are not accessible to the application + * code. However, if the application writer wants to statically allocate such + * an object then the size of the object needs to be know. Dummy structures + * that are guaranteed to have the same size and alignment requirements of the + * real objects are used for this purpose. The dummy list and list item + * structures below are used for inclusion in such a dummy structure. + */ +struct xSTATIC_LIST_ITEM +{ + #if( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + TickType_t xDummy1; + #endif + TickType_t xDummy2; + void *pvDummy3[ 4 ]; + #if( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + TickType_t xDummy4; + #endif +}; +typedef struct xSTATIC_LIST_ITEM StaticListItem_t; + +/* See the comments above the struct xSTATIC_LIST_ITEM definition. */ +struct xSTATIC_MINI_LIST_ITEM +{ + #if( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + TickType_t xDummy1; + #endif + TickType_t xDummy2; + void *pvDummy3[ 2 ]; +}; +typedef struct xSTATIC_MINI_LIST_ITEM StaticMiniListItem_t; + +/* See the comments above the struct xSTATIC_LIST_ITEM definition. */ +typedef struct xSTATIC_LIST +{ + #if( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + TickType_t xDummy1; + #endif + UBaseType_t uxDummy2; + void *pvDummy3; + StaticMiniListItem_t xDummy4; + #if( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 1 ) + TickType_t xDummy5; + #endif +} StaticList_t; + +/* + * In line with software engineering best practice, especially when supplying a + * library that is likely to change in future versions, FreeRTOS implements a + * strict data hiding policy. This means the Task structure used internally by + * FreeRTOS is not accessible to application code. However, if the application + * writer wants to statically allocate the memory required to create a task then + * the size of the task object needs to be know. The StaticTask_t structure + * below is provided for this purpose. Its sizes and alignment requirements are + * guaranteed to match those of the genuine structure, no matter which + * architecture is being used, and no matter how the values in FreeRTOSConfig.h + * are set. Its contents are somewhat obfuscated in the hope users will + * recognise that it would be unwise to make direct use of the structure members. + */ +typedef struct xSTATIC_TCB +{ + void *pxDummy1; + #if ( portUSING_MPU_WRAPPERS == 1 ) + xMPU_SETTINGS xDummy2; + #endif + StaticListItem_t xDummy3[ 2 ]; + UBaseType_t uxDummy5; + void *pxDummy6; + uint8_t ucDummy7[ configMAX_TASK_NAME_LEN ]; + #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) ) + void *pxDummy8; + #endif + #if ( portCRITICAL_NESTING_IN_TCB == 1 ) + UBaseType_t uxDummy9; + #endif + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxDummy10[ 2 ]; + #endif + #if ( configUSE_MUTEXES == 1 ) + UBaseType_t uxDummy12[ 2 ]; + #endif + #if ( configUSE_APPLICATION_TASK_TAG == 1 ) + void *pxDummy14; + #endif + #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) + void *pvDummy15[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ]; + #endif + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + uint32_t ulDummy16; + #endif + #if ( configUSE_NEWLIB_REENTRANT == 1 ) + struct _reent xDummy17; + #endif + #if ( configUSE_TASK_NOTIFICATIONS == 1 ) + uint32_t ulDummy18; + uint8_t ucDummy19; + #endif + #if ( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + uint8_t uxDummy20; + #endif + + #if( INCLUDE_xTaskAbortDelay == 1 ) + uint8_t ucDummy21; + #endif + #if ( configUSE_POSIX_ERRNO == 1 ) + int iDummy22; + #endif +} StaticTask_t; + +/* + * In line with software engineering best practice, especially when supplying a + * library that is likely to change in future versions, FreeRTOS implements a + * strict data hiding policy. This means the Queue structure used internally by + * FreeRTOS is not accessible to application code. However, if the application + * writer wants to statically allocate the memory required to create a queue + * then the size of the queue object needs to be know. The StaticQueue_t + * structure below is provided for this purpose. Its sizes and alignment + * requirements are guaranteed to match those of the genuine structure, no + * matter which architecture is being used, and no matter how the values in + * FreeRTOSConfig.h are set. Its contents are somewhat obfuscated in the hope + * users will recognise that it would be unwise to make direct use of the + * structure members. + */ +typedef struct xSTATIC_QUEUE +{ + void *pvDummy1[ 3 ]; + + union + { + void *pvDummy2; + UBaseType_t uxDummy2; + } u; + + StaticList_t xDummy3[ 2 ]; + UBaseType_t uxDummy4[ 3 ]; + uint8_t ucDummy5[ 2 ]; + + #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + uint8_t ucDummy6; + #endif + + #if ( configUSE_QUEUE_SETS == 1 ) + void *pvDummy7; + #endif + + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxDummy8; + uint8_t ucDummy9; + #endif + +} StaticQueue_t; +typedef StaticQueue_t StaticSemaphore_t; + +/* + * In line with software engineering best practice, especially when supplying a + * library that is likely to change in future versions, FreeRTOS implements a + * strict data hiding policy. This means the event group structure used + * internally by FreeRTOS is not accessible to application code. However, if + * the application writer wants to statically allocate the memory required to + * create an event group then the size of the event group object needs to be + * know. The StaticEventGroup_t structure below is provided for this purpose. + * Its sizes and alignment requirements are guaranteed to match those of the + * genuine structure, no matter which architecture is being used, and no matter + * how the values in FreeRTOSConfig.h are set. Its contents are somewhat + * obfuscated in the hope users will recognise that it would be unwise to make + * direct use of the structure members. + */ +typedef struct xSTATIC_EVENT_GROUP +{ + TickType_t xDummy1; + StaticList_t xDummy2; + + #if( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxDummy3; + #endif + + #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + uint8_t ucDummy4; + #endif + +} StaticEventGroup_t; + +/* + * In line with software engineering best practice, especially when supplying a + * library that is likely to change in future versions, FreeRTOS implements a + * strict data hiding policy. This means the software timer structure used + * internally by FreeRTOS is not accessible to application code. However, if + * the application writer wants to statically allocate the memory required to + * create a software timer then the size of the queue object needs to be know. + * The StaticTimer_t structure below is provided for this purpose. Its sizes + * and alignment requirements are guaranteed to match those of the genuine + * structure, no matter which architecture is being used, and no matter how the + * values in FreeRTOSConfig.h are set. Its contents are somewhat obfuscated in + * the hope users will recognise that it would be unwise to make direct use of + * the structure members. + */ +typedef struct xSTATIC_TIMER +{ + void *pvDummy1; + StaticListItem_t xDummy2; + TickType_t xDummy3; + void *pvDummy5; + TaskFunction_t pvDummy6; + #if( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxDummy7; + #endif + uint8_t ucDummy8; + +} StaticTimer_t; + +/* +* In line with software engineering best practice, especially when supplying a +* library that is likely to change in future versions, FreeRTOS implements a +* strict data hiding policy. This means the stream buffer structure used +* internally by FreeRTOS is not accessible to application code. However, if +* the application writer wants to statically allocate the memory required to +* create a stream buffer then the size of the stream buffer object needs to be +* know. The StaticStreamBuffer_t structure below is provided for this purpose. +* Its size and alignment requirements are guaranteed to match those of the +* genuine structure, no matter which architecture is being used, and no matter +* how the values in FreeRTOSConfig.h are set. Its contents are somewhat +* obfuscated in the hope users will recognise that it would be unwise to make +* direct use of the structure members. +*/ +typedef struct xSTATIC_STREAM_BUFFER +{ + size_t uxDummy1[ 4 ]; + void * pvDummy2[ 3 ]; + uint8_t ucDummy3; + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxDummy4; + #endif +} StaticStreamBuffer_t; + +/* Message buffers are built on stream buffers. */ +typedef StaticStreamBuffer_t StaticMessageBuffer_t; + +#ifdef __cplusplus +} +#endif + +#endif /* INC_FREERTOS_H */ + diff --git a/product/rcar/src/CMSIS-FreeRTOS/Source/include/deprecated_definitions.h b/product/rcar/src/CMSIS-FreeRTOS/Source/include/deprecated_definitions.h new file mode 100644 index 000000000..21657b9df --- /dev/null +++ b/product/rcar/src/CMSIS-FreeRTOS/Source/include/deprecated_definitions.h @@ -0,0 +1,279 @@ +/* + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +#ifndef DEPRECATED_DEFINITIONS_H +#define DEPRECATED_DEFINITIONS_H + + +/* Each FreeRTOS port has a unique portmacro.h header file. Originally a +pre-processor definition was used to ensure the pre-processor found the correct +portmacro.h file for the port being used. That scheme was deprecated in favour +of setting the compiler's include path such that it found the correct +portmacro.h file - removing the need for the constant and allowing the +portmacro.h file to be located anywhere in relation to the port being used. The +definitions below remain in the code for backward compatibility only. New +projects should not use them. */ + +#ifdef OPEN_WATCOM_INDUSTRIAL_PC_PORT + #include "..\..\Source\portable\owatcom\16bitdos\pc\portmacro.h" + typedef void ( __interrupt __far *pxISR )(); +#endif + +#ifdef OPEN_WATCOM_FLASH_LITE_186_PORT + #include "..\..\Source\portable\owatcom\16bitdos\flsh186\portmacro.h" + typedef void ( __interrupt __far *pxISR )(); +#endif + +#ifdef GCC_MEGA_AVR + #include "../portable/GCC/ATMega323/portmacro.h" +#endif + +#ifdef IAR_MEGA_AVR + #include "../portable/IAR/ATMega323/portmacro.h" +#endif + +#ifdef MPLAB_PIC24_PORT + #include "../../Source/portable/MPLAB/PIC24_dsPIC/portmacro.h" +#endif + +#ifdef MPLAB_DSPIC_PORT + #include "../../Source/portable/MPLAB/PIC24_dsPIC/portmacro.h" +#endif + +#ifdef MPLAB_PIC18F_PORT + #include "../../Source/portable/MPLAB/PIC18F/portmacro.h" +#endif + +#ifdef MPLAB_PIC32MX_PORT + #include "../../Source/portable/MPLAB/PIC32MX/portmacro.h" +#endif + +#ifdef _FEDPICC + #include "libFreeRTOS/Include/portmacro.h" +#endif + +#ifdef SDCC_CYGNAL + #include "../../Source/portable/SDCC/Cygnal/portmacro.h" +#endif + +#ifdef GCC_ARM7 + #include "../../Source/portable/GCC/ARM7_LPC2000/portmacro.h" +#endif + +#ifdef GCC_ARM7_ECLIPSE + #include "portmacro.h" +#endif + +#ifdef ROWLEY_LPC23xx + #include "../../Source/portable/GCC/ARM7_LPC23xx/portmacro.h" +#endif + +#ifdef IAR_MSP430 + #include "..\..\Source\portable\IAR\MSP430\portmacro.h" +#endif + +#ifdef GCC_MSP430 + #include "../../Source/portable/GCC/MSP430F449/portmacro.h" +#endif + +#ifdef ROWLEY_MSP430 + #include "../../Source/portable/Rowley/MSP430F449/portmacro.h" +#endif + +#ifdef ARM7_LPC21xx_KEIL_RVDS + #include "..\..\Source\portable\RVDS\ARM7_LPC21xx\portmacro.h" +#endif + +#ifdef SAM7_GCC + #include "../../Source/portable/GCC/ARM7_AT91SAM7S/portmacro.h" +#endif + +#ifdef SAM7_IAR + #include "..\..\Source\portable\IAR\AtmelSAM7S64\portmacro.h" +#endif + +#ifdef SAM9XE_IAR + #include "..\..\Source\portable\IAR\AtmelSAM9XE\portmacro.h" +#endif + +#ifdef LPC2000_IAR + #include "..\..\Source\portable\IAR\LPC2000\portmacro.h" +#endif + +#ifdef STR71X_IAR + #include "..\..\Source\portable\IAR\STR71x\portmacro.h" +#endif + +#ifdef STR75X_IAR + #include "..\..\Source\portable\IAR\STR75x\portmacro.h" +#endif + +#ifdef STR75X_GCC + #include "..\..\Source\portable\GCC\STR75x\portmacro.h" +#endif + +#ifdef STR91X_IAR + #include "..\..\Source\portable\IAR\STR91x\portmacro.h" +#endif + +#ifdef GCC_H8S + #include "../../Source/portable/GCC/H8S2329/portmacro.h" +#endif + +#ifdef GCC_AT91FR40008 + #include "../../Source/portable/GCC/ARM7_AT91FR40008/portmacro.h" +#endif + +#ifdef RVDS_ARMCM3_LM3S102 + #include "../../Source/portable/RVDS/ARM_CM3/portmacro.h" +#endif + +#ifdef GCC_ARMCM3_LM3S102 + #include "../../Source/portable/GCC/ARM_CM3/portmacro.h" +#endif + +#ifdef GCC_ARMCM3 + #include "../../Source/portable/GCC/ARM_CM3/portmacro.h" +#endif + +#ifdef IAR_ARM_CM3 + #include "../../Source/portable/IAR/ARM_CM3/portmacro.h" +#endif + +#ifdef IAR_ARMCM3_LM + #include "../../Source/portable/IAR/ARM_CM3/portmacro.h" +#endif + +#ifdef HCS12_CODE_WARRIOR + #include "../../Source/portable/CodeWarrior/HCS12/portmacro.h" +#endif + +#ifdef MICROBLAZE_GCC + #include "../../Source/portable/GCC/MicroBlaze/portmacro.h" +#endif + +#ifdef TERN_EE + #include "..\..\Source\portable\Paradigm\Tern_EE\small\portmacro.h" +#endif + +#ifdef GCC_HCS12 + #include "../../Source/portable/GCC/HCS12/portmacro.h" +#endif + +#ifdef GCC_MCF5235 + #include "../../Source/portable/GCC/MCF5235/portmacro.h" +#endif + +#ifdef COLDFIRE_V2_GCC + #include "../../../Source/portable/GCC/ColdFire_V2/portmacro.h" +#endif + +#ifdef COLDFIRE_V2_CODEWARRIOR + #include "../../Source/portable/CodeWarrior/ColdFire_V2/portmacro.h" +#endif + +#ifdef GCC_PPC405 + #include "../../Source/portable/GCC/PPC405_Xilinx/portmacro.h" +#endif + +#ifdef GCC_PPC440 + #include "../../Source/portable/GCC/PPC440_Xilinx/portmacro.h" +#endif + +#ifdef _16FX_SOFTUNE + #include "..\..\Source\portable\Softune\MB96340\portmacro.h" +#endif + +#ifdef BCC_INDUSTRIAL_PC_PORT + /* A short file name has to be used in place of the normal + FreeRTOSConfig.h when using the Borland compiler. */ + #include "frconfig.h" + #include "..\portable\BCC\16BitDOS\PC\prtmacro.h" + typedef void ( __interrupt __far *pxISR )(); +#endif + +#ifdef BCC_FLASH_LITE_186_PORT + /* A short file name has to be used in place of the normal + FreeRTOSConfig.h when using the Borland compiler. */ + #include "frconfig.h" + #include "..\portable\BCC\16BitDOS\flsh186\prtmacro.h" + typedef void ( __interrupt __far *pxISR )(); +#endif + +#ifdef __GNUC__ + #ifdef __AVR32_AVR32A__ + #include "portmacro.h" + #endif +#endif + +#ifdef __ICCAVR32__ + #ifdef __CORE__ + #if __CORE__ == __AVR32A__ + #include "portmacro.h" + #endif + #endif +#endif + +#ifdef __91467D + #include "portmacro.h" +#endif + +#ifdef __96340 + #include "portmacro.h" +#endif + + +#ifdef __IAR_V850ES_Fx3__ + #include "../../Source/portable/IAR/V850ES/portmacro.h" +#endif + +#ifdef __IAR_V850ES_Jx3__ + #include "../../Source/portable/IAR/V850ES/portmacro.h" +#endif + +#ifdef __IAR_V850ES_Jx3_L__ + #include "../../Source/portable/IAR/V850ES/portmacro.h" +#endif + +#ifdef __IAR_V850ES_Jx2__ + #include "../../Source/portable/IAR/V850ES/portmacro.h" +#endif + +#ifdef __IAR_V850ES_Hx2__ + #include "../../Source/portable/IAR/V850ES/portmacro.h" +#endif + +#ifdef __IAR_78K0R_Kx3__ + #include "../../Source/portable/IAR/78K0R/portmacro.h" +#endif + +#ifdef __IAR_78K0R_Kx3L__ + #include "../../Source/portable/IAR/78K0R/portmacro.h" +#endif + +#endif /* DEPRECATED_DEFINITIONS_H */ + diff --git a/product/rcar/src/CMSIS-FreeRTOS/Source/include/list.h b/product/rcar/src/CMSIS-FreeRTOS/Source/include/list.h new file mode 100644 index 000000000..a3e302492 --- /dev/null +++ b/product/rcar/src/CMSIS-FreeRTOS/Source/include/list.h @@ -0,0 +1,412 @@ +/* + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/* + * This is the list implementation used by the scheduler. While it is tailored + * heavily for the schedulers needs, it is also available for use by + * application code. + * + * list_ts can only store pointers to list_item_ts. Each ListItem_t contains a + * numeric value (xItemValue). Most of the time the lists are sorted in + * descending item value order. + * + * Lists are created already containing one list item. The value of this + * item is the maximum possible that can be stored, it is therefore always at + * the end of the list and acts as a marker. The list member pxHead always + * points to this marker - even though it is at the tail of the list. This + * is because the tail contains a wrap back pointer to the true head of + * the list. + * + * In addition to it's value, each list item contains a pointer to the next + * item in the list (pxNext), a pointer to the list it is in (pxContainer) + * and a pointer to back to the object that contains it. These later two + * pointers are included for efficiency of list manipulation. There is + * effectively a two way link between the object containing the list item and + * the list item itself. + * + * + * \page ListIntroduction List Implementation + * \ingroup FreeRTOSIntro + */ + +#ifndef INC_FREERTOS_H + #error FreeRTOS.h must be included before list.h +#endif + +#ifndef LIST_H +#define LIST_H + +/* + * The list structure members are modified from within interrupts, and therefore + * by rights should be declared volatile. However, they are only modified in a + * functionally atomic way (within critical sections of with the scheduler + * suspended) and are either passed by reference into a function or indexed via + * a volatile variable. Therefore, in all use cases tested so far, the volatile + * qualifier can be omitted in order to provide a moderate performance + * improvement without adversely affecting functional behaviour. The assembly + * instructions generated by the IAR, ARM and GCC compilers when the respective + * compiler's options were set for maximum optimisation has been inspected and + * deemed to be as intended. That said, as compiler technology advances, and + * especially if aggressive cross module optimisation is used (a use case that + * has not been exercised to any great extend) then it is feasible that the + * volatile qualifier will be needed for correct optimisation. It is expected + * that a compiler removing essential code because, without the volatile + * qualifier on the list structure members and with aggressive cross module + * optimisation, the compiler deemed the code unnecessary will result in + * complete and obvious failure of the scheduler. If this is ever experienced + * then the volatile qualifier can be inserted in the relevant places within the + * list structures by simply defining configLIST_VOLATILE to volatile in + * FreeRTOSConfig.h (as per the example at the bottom of this comment block). + * If configLIST_VOLATILE is not defined then the preprocessor directives below + * will simply #define configLIST_VOLATILE away completely. + * + * To use volatile list structure members then add the following line to + * FreeRTOSConfig.h (without the quotes): + * "#define configLIST_VOLATILE volatile" + */ +#ifndef configLIST_VOLATILE + #define configLIST_VOLATILE +#endif /* configSUPPORT_CROSS_MODULE_OPTIMISATION */ + +#ifdef __cplusplus +extern "C" { +#endif + +/* Macros that can be used to place known values within the list structures, +then check that the known values do not get corrupted during the execution of +the application. These may catch the list data structures being overwritten in +memory. They will not catch data errors caused by incorrect configuration or +use of FreeRTOS.*/ +#if( configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES == 0 ) + /* Define the macros to do nothing. */ + #define listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE + #define listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE + #define listFIRST_LIST_INTEGRITY_CHECK_VALUE + #define listSECOND_LIST_INTEGRITY_CHECK_VALUE + #define listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ) + #define listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ) + #define listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList ) + #define listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList ) + #define listTEST_LIST_ITEM_INTEGRITY( pxItem ) + #define listTEST_LIST_INTEGRITY( pxList ) +#else + /* Define macros that add new members into the list structures. */ + #define listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE TickType_t xListItemIntegrityValue1; + #define listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE TickType_t xListItemIntegrityValue2; + #define listFIRST_LIST_INTEGRITY_CHECK_VALUE TickType_t xListIntegrityValue1; + #define listSECOND_LIST_INTEGRITY_CHECK_VALUE TickType_t xListIntegrityValue2; + + /* Define macros that set the new structure members to known values. */ + #define listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ) ( pxItem )->xListItemIntegrityValue1 = pdINTEGRITY_CHECK_VALUE + #define listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ) ( pxItem )->xListItemIntegrityValue2 = pdINTEGRITY_CHECK_VALUE + #define listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList ) ( pxList )->xListIntegrityValue1 = pdINTEGRITY_CHECK_VALUE + #define listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList ) ( pxList )->xListIntegrityValue2 = pdINTEGRITY_CHECK_VALUE + + /* Define macros that will assert if one of the structure members does not + contain its expected value. */ + #define listTEST_LIST_ITEM_INTEGRITY( pxItem ) configASSERT( ( ( pxItem )->xListItemIntegrityValue1 == pdINTEGRITY_CHECK_VALUE ) && ( ( pxItem )->xListItemIntegrityValue2 == pdINTEGRITY_CHECK_VALUE ) ) + #define listTEST_LIST_INTEGRITY( pxList ) configASSERT( ( ( pxList )->xListIntegrityValue1 == pdINTEGRITY_CHECK_VALUE ) && ( ( pxList )->xListIntegrityValue2 == pdINTEGRITY_CHECK_VALUE ) ) +#endif /* configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES */ + + +/* + * Definition of the only type of object that a list can contain. + */ +struct xLIST; +struct xLIST_ITEM +{ + listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ + configLIST_VOLATILE TickType_t xItemValue; /*< The value being listed. In most cases this is used to sort the list in descending order. */ + struct xLIST_ITEM * configLIST_VOLATILE pxNext; /*< Pointer to the next ListItem_t in the list. */ + struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; /*< Pointer to the previous ListItem_t in the list. */ + void * pvOwner; /*< Pointer to the object (normally a TCB) that contains the list item. There is therefore a two way link between the object containing the list item and the list item itself. */ + struct xLIST * configLIST_VOLATILE pxContainer; /*< Pointer to the list in which this list item is placed (if any). */ + listSECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ +}; +typedef struct xLIST_ITEM ListItem_t; /* For some reason lint wants this as two separate definitions. */ + +struct xMINI_LIST_ITEM +{ + listFIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ + configLIST_VOLATILE TickType_t xItemValue; + struct xLIST_ITEM * configLIST_VOLATILE pxNext; + struct xLIST_ITEM * configLIST_VOLATILE pxPrevious; +}; +typedef struct xMINI_LIST_ITEM MiniListItem_t; + +/* + * Definition of the type of queue used by the scheduler. + */ +typedef struct xLIST +{ + listFIRST_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ + volatile UBaseType_t uxNumberOfItems; + ListItem_t * configLIST_VOLATILE pxIndex; /*< Used to walk through the list. Points to the last item returned by a call to listGET_OWNER_OF_NEXT_ENTRY (). */ + MiniListItem_t xListEnd; /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */ + listSECOND_LIST_INTEGRITY_CHECK_VALUE /*< Set to a known value if configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ +} List_t; + +/* + * Access macro to set the owner of a list item. The owner of a list item + * is the object (usually a TCB) that contains the list item. + * + * \page listSET_LIST_ITEM_OWNER listSET_LIST_ITEM_OWNER + * \ingroup LinkedList + */ +#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner ) ( ( pxListItem )->pvOwner = ( void * ) ( pxOwner ) ) + +/* + * Access macro to get the owner of a list item. The owner of a list item + * is the object (usually a TCB) that contains the list item. + * + * \page listGET_LIST_ITEM_OWNER listSET_LIST_ITEM_OWNER + * \ingroup LinkedList + */ +#define listGET_LIST_ITEM_OWNER( pxListItem ) ( ( pxListItem )->pvOwner ) + +/* + * Access macro to set the value of the list item. In most cases the value is + * used to sort the list in descending order. + * + * \page listSET_LIST_ITEM_VALUE listSET_LIST_ITEM_VALUE + * \ingroup LinkedList + */ +#define listSET_LIST_ITEM_VALUE( pxListItem, xValue ) ( ( pxListItem )->xItemValue = ( xValue ) ) + +/* + * Access macro to retrieve the value of the list item. The value can + * represent anything - for example the priority of a task, or the time at + * which a task should be unblocked. + * + * \page listGET_LIST_ITEM_VALUE listGET_LIST_ITEM_VALUE + * \ingroup LinkedList + */ +#define listGET_LIST_ITEM_VALUE( pxListItem ) ( ( pxListItem )->xItemValue ) + +/* + * Access macro to retrieve the value of the list item at the head of a given + * list. + * + * \page listGET_LIST_ITEM_VALUE listGET_LIST_ITEM_VALUE + * \ingroup LinkedList + */ +#define listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext->xItemValue ) + +/* + * Return the list item at the head of the list. + * + * \page listGET_HEAD_ENTRY listGET_HEAD_ENTRY + * \ingroup LinkedList + */ +#define listGET_HEAD_ENTRY( pxList ) ( ( ( pxList )->xListEnd ).pxNext ) + +/* + * Return the next list item. + * + * \page listGET_NEXT listGET_NEXT + * \ingroup LinkedList + */ +#define listGET_NEXT( pxListItem ) ( ( pxListItem )->pxNext ) + +/* + * Return the list item that marks the end of the list + * + * \page listGET_END_MARKER listGET_END_MARKER + * \ingroup LinkedList + */ +#define listGET_END_MARKER( pxList ) ( ( ListItem_t const * ) ( &( ( pxList )->xListEnd ) ) ) + +/* + * Access macro to determine if a list contains any items. The macro will + * only have the value true if the list is empty. + * + * \page listLIST_IS_EMPTY listLIST_IS_EMPTY + * \ingroup LinkedList + */ +#define listLIST_IS_EMPTY( pxList ) ( ( ( pxList )->uxNumberOfItems == ( UBaseType_t ) 0 ) ? pdTRUE : pdFALSE ) + +/* + * Access macro to return the number of items in the list. + */ +#define listCURRENT_LIST_LENGTH( pxList ) ( ( pxList )->uxNumberOfItems ) + +/* + * Access function to obtain the owner of the next entry in a list. + * + * The list member pxIndex is used to walk through a list. Calling + * listGET_OWNER_OF_NEXT_ENTRY increments pxIndex to the next item in the list + * and returns that entry's pxOwner parameter. Using multiple calls to this + * function it is therefore possible to move through every item contained in + * a list. + * + * The pxOwner parameter of a list item is a pointer to the object that owns + * the list item. In the scheduler this is normally a task control block. + * The pxOwner parameter effectively creates a two way link between the list + * item and its owner. + * + * @param pxTCB pxTCB is set to the address of the owner of the next list item. + * @param pxList The list from which the next item owner is to be returned. + * + * \page listGET_OWNER_OF_NEXT_ENTRY listGET_OWNER_OF_NEXT_ENTRY + * \ingroup LinkedList + */ +#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \ +{ \ +List_t * const pxConstList = ( pxList ); \ + /* Increment the index to the next item and return the item, ensuring */ \ + /* we don't return the marker used at the end of the list. */ \ + ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \ + if( ( void * ) ( pxConstList )->pxIndex == ( void * ) &( ( pxConstList )->xListEnd ) ) \ + { \ + ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \ + } \ + ( pxTCB ) = ( pxConstList )->pxIndex->pvOwner; \ +} + + +/* + * Access function to obtain the owner of the first entry in a list. Lists + * are normally sorted in ascending item value order. + * + * This function returns the pxOwner member of the first item in the list. + * The pxOwner parameter of a list item is a pointer to the object that owns + * the list item. In the scheduler this is normally a task control block. + * The pxOwner parameter effectively creates a two way link between the list + * item and its owner. + * + * @param pxList The list from which the owner of the head item is to be + * returned. + * + * \page listGET_OWNER_OF_HEAD_ENTRY listGET_OWNER_OF_HEAD_ENTRY + * \ingroup LinkedList + */ +#define listGET_OWNER_OF_HEAD_ENTRY( pxList ) ( (&( ( pxList )->xListEnd ))->pxNext->pvOwner ) + +/* + * Check to see if a list item is within a list. The list item maintains a + * "container" pointer that points to the list it is in. All this macro does + * is check to see if the container and the list match. + * + * @param pxList The list we want to know if the list item is within. + * @param pxListItem The list item we want to know if is in the list. + * @return pdTRUE if the list item is in the list, otherwise pdFALSE. + */ +#define listIS_CONTAINED_WITHIN( pxList, pxListItem ) ( ( ( pxListItem )->pxContainer == ( pxList ) ) ? ( pdTRUE ) : ( pdFALSE ) ) + +/* + * Return the list a list item is contained within (referenced from). + * + * @param pxListItem The list item being queried. + * @return A pointer to the List_t object that references the pxListItem + */ +#define listLIST_ITEM_CONTAINER( pxListItem ) ( ( pxListItem )->pxContainer ) + +/* + * This provides a crude means of knowing if a list has been initialised, as + * pxList->xListEnd.xItemValue is set to portMAX_DELAY by the vListInitialise() + * function. + */ +#define listLIST_IS_INITIALISED( pxList ) ( ( pxList )->xListEnd.xItemValue == portMAX_DELAY ) + +/* + * Must be called before a list is used! This initialises all the members + * of the list structure and inserts the xListEnd item into the list as a + * marker to the back of the list. + * + * @param pxList Pointer to the list being initialised. + * + * \page vListInitialise vListInitialise + * \ingroup LinkedList + */ +void vListInitialise( List_t * const pxList ) PRIVILEGED_FUNCTION; + +/* + * Must be called before a list item is used. This sets the list container to + * null so the item does not think that it is already contained in a list. + * + * @param pxItem Pointer to the list item being initialised. + * + * \page vListInitialiseItem vListInitialiseItem + * \ingroup LinkedList + */ +void vListInitialiseItem( ListItem_t * const pxItem ) PRIVILEGED_FUNCTION; + +/* + * Insert a list item into a list. The item will be inserted into the list in + * a position determined by its item value (descending item value order). + * + * @param pxList The list into which the item is to be inserted. + * + * @param pxNewListItem The item that is to be placed in the list. + * + * \page vListInsert vListInsert + * \ingroup LinkedList + */ +void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem ) PRIVILEGED_FUNCTION; + +/* + * Insert a list item into a list. The item will be inserted in a position + * such that it will be the last item within the list returned by multiple + * calls to listGET_OWNER_OF_NEXT_ENTRY. + * + * The list member pxIndex is used to walk through a list. Calling + * listGET_OWNER_OF_NEXT_ENTRY increments pxIndex to the next item in the list. + * Placing an item in a list using vListInsertEnd effectively places the item + * in the list position pointed to by pxIndex. This means that every other + * item within the list will be returned by listGET_OWNER_OF_NEXT_ENTRY before + * the pxIndex parameter again points to the item being inserted. + * + * @param pxList The list into which the item is to be inserted. + * + * @param pxNewListItem The list item to be inserted into the list. + * + * \page vListInsertEnd vListInsertEnd + * \ingroup LinkedList + */ +void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem ) PRIVILEGED_FUNCTION; + +/* + * Remove an item from a list. The list item has a pointer to the list that + * it is in, so only the list item need be passed into the function. + * + * @param uxListRemove The item to be removed. The item will remove itself from + * the list pointed to by it's pxContainer parameter. + * + * @return The number of items that remain in the list after the list item has + * been removed. + * + * \page uxListRemove uxListRemove + * \ingroup LinkedList + */ +UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove ) PRIVILEGED_FUNCTION; + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/product/rcar/src/CMSIS-FreeRTOS/Source/include/mpu_wrappers.h b/product/rcar/src/CMSIS-FreeRTOS/Source/include/mpu_wrappers.h new file mode 100644 index 000000000..5f63d4f2d --- /dev/null +++ b/product/rcar/src/CMSIS-FreeRTOS/Source/include/mpu_wrappers.h @@ -0,0 +1,189 @@ +/* + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +#ifndef MPU_WRAPPERS_H +#define MPU_WRAPPERS_H + +/* This file redefines API functions to be called through a wrapper macro, but +only for ports that are using the MPU. */ +#ifdef portUSING_MPU_WRAPPERS + + /* MPU_WRAPPERS_INCLUDED_FROM_API_FILE will be defined when this file is + included from queue.c or task.c to prevent it from having an effect within + those files. */ + #ifndef MPU_WRAPPERS_INCLUDED_FROM_API_FILE + + /* + * Map standard (non MPU) API functions to equivalents that start + * "MPU_". This will cause the application code to call the MPU_ + * version, which wraps the non-MPU version with privilege promoting + * then demoting code, so the kernel code always runs will full + * privileges. + */ + + /* Map standard tasks.h API functions to the MPU equivalents. */ + #define xTaskCreate MPU_xTaskCreate + #define xTaskCreateStatic MPU_xTaskCreateStatic + #define xTaskCreateRestricted MPU_xTaskCreateRestricted + #define vTaskAllocateMPURegions MPU_vTaskAllocateMPURegions + #define vTaskDelete MPU_vTaskDelete + #define vTaskDelay MPU_vTaskDelay + #define vTaskDelayUntil MPU_vTaskDelayUntil + #define xTaskAbortDelay MPU_xTaskAbortDelay + #define uxTaskPriorityGet MPU_uxTaskPriorityGet + #define eTaskGetState MPU_eTaskGetState + #define vTaskGetInfo MPU_vTaskGetInfo + #define vTaskPrioritySet MPU_vTaskPrioritySet + #define vTaskSuspend MPU_vTaskSuspend + #define vTaskResume MPU_vTaskResume + #define vTaskSuspendAll MPU_vTaskSuspendAll + #define xTaskResumeAll MPU_xTaskResumeAll + #define xTaskGetTickCount MPU_xTaskGetTickCount + #define uxTaskGetNumberOfTasks MPU_uxTaskGetNumberOfTasks + #define pcTaskGetName MPU_pcTaskGetName + #define xTaskGetHandle MPU_xTaskGetHandle + #define uxTaskGetStackHighWaterMark MPU_uxTaskGetStackHighWaterMark + #define uxTaskGetStackHighWaterMark2 MPU_uxTaskGetStackHighWaterMark2 + #define vTaskSetApplicationTaskTag MPU_vTaskSetApplicationTaskTag + #define xTaskGetApplicationTaskTag MPU_xTaskGetApplicationTaskTag + #define vTaskSetThreadLocalStoragePointer MPU_vTaskSetThreadLocalStoragePointer + #define pvTaskGetThreadLocalStoragePointer MPU_pvTaskGetThreadLocalStoragePointer + #define xTaskCallApplicationTaskHook MPU_xTaskCallApplicationTaskHook + #define xTaskGetIdleTaskHandle MPU_xTaskGetIdleTaskHandle + #define uxTaskGetSystemState MPU_uxTaskGetSystemState + #define vTaskList MPU_vTaskList + #define vTaskGetRunTimeStats MPU_vTaskGetRunTimeStats + #define ulTaskGetIdleRunTimeCounter MPU_ulTaskGetIdleRunTimeCounter + #define xTaskGenericNotify MPU_xTaskGenericNotify + #define xTaskNotifyWait MPU_xTaskNotifyWait + #define ulTaskNotifyTake MPU_ulTaskNotifyTake + #define xTaskNotifyStateClear MPU_xTaskNotifyStateClear + #define ulTaskNotifyValueClear MPU_ulTaskNotifyValueClear + #define xTaskCatchUpTicks MPU_xTaskCatchUpTicks + + #define xTaskGetCurrentTaskHandle MPU_xTaskGetCurrentTaskHandle + #define vTaskSetTimeOutState MPU_vTaskSetTimeOutState + #define xTaskCheckForTimeOut MPU_xTaskCheckForTimeOut + #define xTaskGetSchedulerState MPU_xTaskGetSchedulerState + + /* Map standard queue.h API functions to the MPU equivalents. */ + #define xQueueGenericSend MPU_xQueueGenericSend + #define xQueueReceive MPU_xQueueReceive + #define xQueuePeek MPU_xQueuePeek + #define xQueueSemaphoreTake MPU_xQueueSemaphoreTake + #define uxQueueMessagesWaiting MPU_uxQueueMessagesWaiting + #define uxQueueSpacesAvailable MPU_uxQueueSpacesAvailable + #define vQueueDelete MPU_vQueueDelete + #define xQueueCreateMutex MPU_xQueueCreateMutex + #define xQueueCreateMutexStatic MPU_xQueueCreateMutexStatic + #define xQueueCreateCountingSemaphore MPU_xQueueCreateCountingSemaphore + #define xQueueCreateCountingSemaphoreStatic MPU_xQueueCreateCountingSemaphoreStatic + #define xQueueGetMutexHolder MPU_xQueueGetMutexHolder + #define xQueueTakeMutexRecursive MPU_xQueueTakeMutexRecursive + #define xQueueGiveMutexRecursive MPU_xQueueGiveMutexRecursive + #define xQueueGenericCreate MPU_xQueueGenericCreate + #define xQueueGenericCreateStatic MPU_xQueueGenericCreateStatic + #define xQueueCreateSet MPU_xQueueCreateSet + #define xQueueAddToSet MPU_xQueueAddToSet + #define xQueueRemoveFromSet MPU_xQueueRemoveFromSet + #define xQueueSelectFromSet MPU_xQueueSelectFromSet + #define xQueueGenericReset MPU_xQueueGenericReset + + #if( configQUEUE_REGISTRY_SIZE > 0 ) + #define vQueueAddToRegistry MPU_vQueueAddToRegistry + #define vQueueUnregisterQueue MPU_vQueueUnregisterQueue + #define pcQueueGetName MPU_pcQueueGetName + #endif + + /* Map standard timer.h API functions to the MPU equivalents. */ + #define xTimerCreate MPU_xTimerCreate + #define xTimerCreateStatic MPU_xTimerCreateStatic + #define pvTimerGetTimerID MPU_pvTimerGetTimerID + #define vTimerSetTimerID MPU_vTimerSetTimerID + #define xTimerIsTimerActive MPU_xTimerIsTimerActive + #define xTimerGetTimerDaemonTaskHandle MPU_xTimerGetTimerDaemonTaskHandle + #define xTimerPendFunctionCall MPU_xTimerPendFunctionCall + #define pcTimerGetName MPU_pcTimerGetName + #define vTimerSetReloadMode MPU_vTimerSetReloadMode + #define uxTimerGetReloadMode MPU_uxTimerGetReloadMode + #define xTimerGetPeriod MPU_xTimerGetPeriod + #define xTimerGetExpiryTime MPU_xTimerGetExpiryTime + #define xTimerGenericCommand MPU_xTimerGenericCommand + + /* Map standard event_group.h API functions to the MPU equivalents. */ + #define xEventGroupCreate MPU_xEventGroupCreate + #define xEventGroupCreateStatic MPU_xEventGroupCreateStatic + #define xEventGroupWaitBits MPU_xEventGroupWaitBits + #define xEventGroupClearBits MPU_xEventGroupClearBits + #define xEventGroupSetBits MPU_xEventGroupSetBits + #define xEventGroupSync MPU_xEventGroupSync + #define vEventGroupDelete MPU_vEventGroupDelete + + /* Map standard message/stream_buffer.h API functions to the MPU + equivalents. */ + #define xStreamBufferSend MPU_xStreamBufferSend + #define xStreamBufferReceive MPU_xStreamBufferReceive + #define xStreamBufferNextMessageLengthBytes MPU_xStreamBufferNextMessageLengthBytes + #define vStreamBufferDelete MPU_vStreamBufferDelete + #define xStreamBufferIsFull MPU_xStreamBufferIsFull + #define xStreamBufferIsEmpty MPU_xStreamBufferIsEmpty + #define xStreamBufferReset MPU_xStreamBufferReset + #define xStreamBufferSpacesAvailable MPU_xStreamBufferSpacesAvailable + #define xStreamBufferBytesAvailable MPU_xStreamBufferBytesAvailable + #define xStreamBufferSetTriggerLevel MPU_xStreamBufferSetTriggerLevel + #define xStreamBufferGenericCreate MPU_xStreamBufferGenericCreate + #define xStreamBufferGenericCreateStatic MPU_xStreamBufferGenericCreateStatic + + + /* Remove the privileged function macro, but keep the PRIVILEGED_DATA + macro so applications can place data in privileged access sections + (useful when using statically allocated objects). */ + #define PRIVILEGED_FUNCTION + #define PRIVILEGED_DATA __attribute__((section("privileged_data"))) + #define FREERTOS_SYSTEM_CALL + + #else /* MPU_WRAPPERS_INCLUDED_FROM_API_FILE */ + + /* Ensure API functions go in the privileged execution section. */ + #define PRIVILEGED_FUNCTION __attribute__((section("privileged_functions"))) + #define PRIVILEGED_DATA __attribute__((section("privileged_data"))) + #define FREERTOS_SYSTEM_CALL __attribute__((section( "freertos_system_calls"))) + + #endif /* MPU_WRAPPERS_INCLUDED_FROM_API_FILE */ + +#else /* portUSING_MPU_WRAPPERS */ + + #define PRIVILEGED_FUNCTION + #define PRIVILEGED_DATA + #define FREERTOS_SYSTEM_CALL + #define portUSING_MPU_WRAPPERS 0 + +#endif /* portUSING_MPU_WRAPPERS */ + + +#endif /* MPU_WRAPPERS_H */ + diff --git a/product/rcar/src/CMSIS-FreeRTOS/Source/include/portable.h b/product/rcar/src/CMSIS-FreeRTOS/Source/include/portable.h new file mode 100644 index 000000000..a2099c33d --- /dev/null +++ b/product/rcar/src/CMSIS-FreeRTOS/Source/include/portable.h @@ -0,0 +1,199 @@ +/* + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/*----------------------------------------------------------- + * Portable layer API. Each function must be defined for each port. + *----------------------------------------------------------*/ + +#ifndef PORTABLE_H +#define PORTABLE_H + +/* Each FreeRTOS port has a unique portmacro.h header file. Originally a +pre-processor definition was used to ensure the pre-processor found the correct +portmacro.h file for the port being used. That scheme was deprecated in favour +of setting the compiler's include path such that it found the correct +portmacro.h file - removing the need for the constant and allowing the +portmacro.h file to be located anywhere in relation to the port being used. +Purely for reasons of backward compatibility the old method is still valid, but +to make it clear that new projects should not use it, support for the port +specific constants has been moved into the deprecated_definitions.h header +file. */ +#include "deprecated_definitions.h" + +/* If portENTER_CRITICAL is not defined then including deprecated_definitions.h +did not result in a portmacro.h header file being included - and it should be +included here. In this case the path to the correct portmacro.h header file +must be set in the compiler's include path. */ +#ifndef portENTER_CRITICAL + #include "portmacro.h" +#endif + +#if portBYTE_ALIGNMENT == 32 + #define portBYTE_ALIGNMENT_MASK ( 0x001f ) +#endif + +#if portBYTE_ALIGNMENT == 16 + #define portBYTE_ALIGNMENT_MASK ( 0x000f ) +#endif + +#if portBYTE_ALIGNMENT == 8 + #define portBYTE_ALIGNMENT_MASK ( 0x0007 ) +#endif + +#if portBYTE_ALIGNMENT == 4 + #define portBYTE_ALIGNMENT_MASK ( 0x0003 ) +#endif + +#if portBYTE_ALIGNMENT == 2 + #define portBYTE_ALIGNMENT_MASK ( 0x0001 ) +#endif + +#if portBYTE_ALIGNMENT == 1 + #define portBYTE_ALIGNMENT_MASK ( 0x0000 ) +#endif + +#ifndef portBYTE_ALIGNMENT_MASK + #error "Invalid portBYTE_ALIGNMENT definition" +#endif + +#ifndef portNUM_CONFIGURABLE_REGIONS + #define portNUM_CONFIGURABLE_REGIONS 1 +#endif + +#ifndef portHAS_STACK_OVERFLOW_CHECKING + #define portHAS_STACK_OVERFLOW_CHECKING 0 +#endif + +#ifndef portARCH_NAME + #define portARCH_NAME NULL +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mpu_wrappers.h" + +/* + * Setup the stack of a new task so it is ready to be placed under the + * scheduler control. The registers have to be placed on the stack in + * the order that the port expects to find them. + * + */ +#if( portUSING_MPU_WRAPPERS == 1 ) + #if( portHAS_STACK_OVERFLOW_CHECKING == 1 ) + StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, StackType_t *pxEndOfStack, TaskFunction_t pxCode, void *pvParameters, BaseType_t xRunPrivileged ) PRIVILEGED_FUNCTION; + #else + StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters, BaseType_t xRunPrivileged ) PRIVILEGED_FUNCTION; + #endif +#else + #if( portHAS_STACK_OVERFLOW_CHECKING == 1 ) + StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, StackType_t *pxEndOfStack, TaskFunction_t pxCode, void *pvParameters ) PRIVILEGED_FUNCTION; + #else + StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters ) PRIVILEGED_FUNCTION; + #endif +#endif + +/* Used by heap_5.c to define the start address and size of each memory region +that together comprise the total FreeRTOS heap space. */ +typedef struct HeapRegion +{ + uint8_t *pucStartAddress; + size_t xSizeInBytes; +} HeapRegion_t; + +/* Used to pass information about the heap out of vPortGetHeapStats(). */ +typedef struct xHeapStats +{ + size_t xAvailableHeapSpaceInBytes; /* The total heap size currently available - this is the sum of all the free blocks, not the largest block that can be allocated. */ + size_t xSizeOfLargestFreeBlockInBytes; /* The maximum size, in bytes, of all the free blocks within the heap at the time vPortGetHeapStats() is called. */ + size_t xSizeOfSmallestFreeBlockInBytes; /* The minimum size, in bytes, of all the free blocks within the heap at the time vPortGetHeapStats() is called. */ + size_t xNumberOfFreeBlocks; /* The number of free memory blocks within the heap at the time vPortGetHeapStats() is called. */ + size_t xMinimumEverFreeBytesRemaining; /* The minimum amount of total free memory (sum of all free blocks) there has been in the heap since the system booted. */ + size_t xNumberOfSuccessfulAllocations; /* The number of calls to pvPortMalloc() that have returned a valid memory block. */ + size_t xNumberOfSuccessfulFrees; /* The number of calls to vPortFree() that has successfully freed a block of memory. */ +} HeapStats_t; + +/* + * Used to define multiple heap regions for use by heap_5.c. This function + * must be called before any calls to pvPortMalloc() - not creating a task, + * queue, semaphore, mutex, software timer, event group, etc. will result in + * pvPortMalloc being called. + * + * pxHeapRegions passes in an array of HeapRegion_t structures - each of which + * defines a region of memory that can be used as the heap. The array is + * terminated by a HeapRegions_t structure that has a size of 0. The region + * with the lowest start address must appear first in the array. + */ +void vPortDefineHeapRegions( const HeapRegion_t * const pxHeapRegions ) PRIVILEGED_FUNCTION; + +/* + * Returns a HeapStats_t structure filled with information about the current + * heap state. + */ +void vPortGetHeapStats( HeapStats_t *pxHeapStats ); + +/* + * Map to the memory management routines required for the port. + */ +void *pvPortMalloc( size_t xSize ) PRIVILEGED_FUNCTION; +void vPortFree( void *pv ) PRIVILEGED_FUNCTION; +void vPortInitialiseBlocks( void ) PRIVILEGED_FUNCTION; +size_t xPortGetFreeHeapSize( void ) PRIVILEGED_FUNCTION; +size_t xPortGetMinimumEverFreeHeapSize( void ) PRIVILEGED_FUNCTION; + +/* + * Setup the hardware ready for the scheduler to take control. This generally + * sets up a tick interrupt and sets timers for the correct tick frequency. + */ +BaseType_t xPortStartScheduler( void ) PRIVILEGED_FUNCTION; + +/* + * Undo any hardware/ISR setup that was performed by xPortStartScheduler() so + * the hardware is left in its original condition after the scheduler stops + * executing. + */ +void vPortEndScheduler( void ) PRIVILEGED_FUNCTION; + +/* + * The structures and methods of manipulating the MPU are contained within the + * port layer. + * + * Fills the xMPUSettings structure with the memory region information + * contained in xRegions. + */ +#if( portUSING_MPU_WRAPPERS == 1 ) + struct xMEMORY_REGION; + void vPortStoreTaskMPUSettings( xMPU_SETTINGS *xMPUSettings, const struct xMEMORY_REGION * const xRegions, StackType_t *pxBottomOfStack, uint32_t ulStackDepth ) PRIVILEGED_FUNCTION; +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* PORTABLE_H */ + diff --git a/product/rcar/src/CMSIS-FreeRTOS/Source/include/projdefs.h b/product/rcar/src/CMSIS-FreeRTOS/Source/include/projdefs.h new file mode 100644 index 000000000..0d95130b6 --- /dev/null +++ b/product/rcar/src/CMSIS-FreeRTOS/Source/include/projdefs.h @@ -0,0 +1,124 @@ +/* + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +#ifndef PROJDEFS_H +#define PROJDEFS_H + +/* + * Defines the prototype to which task functions must conform. Defined in this + * file to ensure the type is known before portable.h is included. + */ +typedef void (*TaskFunction_t)( void * ); + +/* Converts a time in milliseconds to a time in ticks. This macro can be +overridden by a macro of the same name defined in FreeRTOSConfig.h in case the +definition here is not suitable for your application. */ +#ifndef pdMS_TO_TICKS + #define pdMS_TO_TICKS( xTimeInMs ) ( ( TickType_t ) ( ( ( TickType_t ) ( xTimeInMs ) * ( TickType_t ) configTICK_RATE_HZ ) / ( TickType_t ) 1000 ) ) +#endif + +#define pdFALSE ( ( BaseType_t ) 0 ) +#define pdTRUE ( ( BaseType_t ) 1 ) + +#define pdPASS ( pdTRUE ) +#define pdFAIL ( pdFALSE ) +#define errQUEUE_EMPTY ( ( BaseType_t ) 0 ) +#define errQUEUE_FULL ( ( BaseType_t ) 0 ) + +/* FreeRTOS error definitions. */ +#define errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ( -1 ) +#define errQUEUE_BLOCKED ( -4 ) +#define errQUEUE_YIELD ( -5 ) + +/* Macros used for basic data corruption checks. */ +#ifndef configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES + #define configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES 0 +#endif + +#if( configUSE_16_BIT_TICKS == 1 ) + #define pdINTEGRITY_CHECK_VALUE 0x5a5a +#else + #define pdINTEGRITY_CHECK_VALUE 0x5a5a5a5aUL +#endif + +/* The following errno values are used by FreeRTOS+ components, not FreeRTOS +itself. */ +#define pdFREERTOS_ERRNO_NONE 0 /* No errors */ +#define pdFREERTOS_ERRNO_ENOENT 2 /* No such file or directory */ +#define pdFREERTOS_ERRNO_EINTR 4 /* Interrupted system call */ +#define pdFREERTOS_ERRNO_EIO 5 /* I/O error */ +#define pdFREERTOS_ERRNO_ENXIO 6 /* No such device or address */ +#define pdFREERTOS_ERRNO_EBADF 9 /* Bad file number */ +#define pdFREERTOS_ERRNO_EAGAIN 11 /* No more processes */ +#define pdFREERTOS_ERRNO_EWOULDBLOCK 11 /* Operation would block */ +#define pdFREERTOS_ERRNO_ENOMEM 12 /* Not enough memory */ +#define pdFREERTOS_ERRNO_EACCES 13 /* Permission denied */ +#define pdFREERTOS_ERRNO_EFAULT 14 /* Bad address */ +#define pdFREERTOS_ERRNO_EBUSY 16 /* Mount device busy */ +#define pdFREERTOS_ERRNO_EEXIST 17 /* File exists */ +#define pdFREERTOS_ERRNO_EXDEV 18 /* Cross-device link */ +#define pdFREERTOS_ERRNO_ENODEV 19 /* No such device */ +#define pdFREERTOS_ERRNO_ENOTDIR 20 /* Not a directory */ +#define pdFREERTOS_ERRNO_EISDIR 21 /* Is a directory */ +#define pdFREERTOS_ERRNO_EINVAL 22 /* Invalid argument */ +#define pdFREERTOS_ERRNO_ENOSPC 28 /* No space left on device */ +#define pdFREERTOS_ERRNO_ESPIPE 29 /* Illegal seek */ +#define pdFREERTOS_ERRNO_EROFS 30 /* Read only file system */ +#define pdFREERTOS_ERRNO_EUNATCH 42 /* Protocol driver not attached */ +#define pdFREERTOS_ERRNO_EBADE 50 /* Invalid exchange */ +#define pdFREERTOS_ERRNO_EFTYPE 79 /* Inappropriate file type or format */ +#define pdFREERTOS_ERRNO_ENMFILE 89 /* No more files */ +#define pdFREERTOS_ERRNO_ENOTEMPTY 90 /* Directory not empty */ +#define pdFREERTOS_ERRNO_ENAMETOOLONG 91 /* File or path name too long */ +#define pdFREERTOS_ERRNO_EOPNOTSUPP 95 /* Operation not supported on transport endpoint */ +#define pdFREERTOS_ERRNO_ENOBUFS 105 /* No buffer space available */ +#define pdFREERTOS_ERRNO_ENOPROTOOPT 109 /* Protocol not available */ +#define pdFREERTOS_ERRNO_EADDRINUSE 112 /* Address already in use */ +#define pdFREERTOS_ERRNO_ETIMEDOUT 116 /* Connection timed out */ +#define pdFREERTOS_ERRNO_EINPROGRESS 119 /* Connection already in progress */ +#define pdFREERTOS_ERRNO_EALREADY 120 /* Socket already connected */ +#define pdFREERTOS_ERRNO_EADDRNOTAVAIL 125 /* Address not available */ +#define pdFREERTOS_ERRNO_EISCONN 127 /* Socket is already connected */ +#define pdFREERTOS_ERRNO_ENOTCONN 128 /* Socket is not connected */ +#define pdFREERTOS_ERRNO_ENOMEDIUM 135 /* No medium inserted */ +#define pdFREERTOS_ERRNO_EILSEQ 138 /* An invalid UTF-16 sequence was encountered. */ +#define pdFREERTOS_ERRNO_ECANCELED 140 /* Operation canceled. */ + +/* The following endian values are used by FreeRTOS+ components, not FreeRTOS +itself. */ +#define pdFREERTOS_LITTLE_ENDIAN 0 +#define pdFREERTOS_BIG_ENDIAN 1 + +/* Re-defining endian values for generic naming. */ +#define pdLITTLE_ENDIAN pdFREERTOS_LITTLE_ENDIAN +#define pdBIG_ENDIAN pdFREERTOS_BIG_ENDIAN + + +#endif /* PROJDEFS_H */ + + + diff --git a/product/rcar/src/CMSIS-FreeRTOS/Source/include/queue.h b/product/rcar/src/CMSIS-FreeRTOS/Source/include/queue.h new file mode 100644 index 000000000..52ccca551 --- /dev/null +++ b/product/rcar/src/CMSIS-FreeRTOS/Source/include/queue.h @@ -0,0 +1,1655 @@ +/* + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + + +#ifndef QUEUE_H +#define QUEUE_H + +#ifndef INC_FREERTOS_H + #error "include FreeRTOS.h" must appear in source files before "include queue.h" +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include "task.h" + +/** + * Type by which queues are referenced. For example, a call to xQueueCreate() + * returns an QueueHandle_t variable that can then be used as a parameter to + * xQueueSend(), xQueueReceive(), etc. + */ +struct QueueDefinition; /* Using old naming convention so as not to break kernel aware debuggers. */ +typedef struct QueueDefinition * QueueHandle_t; + +/** + * Type by which queue sets are referenced. For example, a call to + * xQueueCreateSet() returns an xQueueSet variable that can then be used as a + * parameter to xQueueSelectFromSet(), xQueueAddToSet(), etc. + */ +typedef struct QueueDefinition * QueueSetHandle_t; + +/** + * Queue sets can contain both queues and semaphores, so the + * QueueSetMemberHandle_t is defined as a type to be used where a parameter or + * return value can be either an QueueHandle_t or an SemaphoreHandle_t. + */ +typedef struct QueueDefinition * QueueSetMemberHandle_t; + +/* For internal use only. */ +#define queueSEND_TO_BACK ( ( BaseType_t ) 0 ) +#define queueSEND_TO_FRONT ( ( BaseType_t ) 1 ) +#define queueOVERWRITE ( ( BaseType_t ) 2 ) + +/* For internal use only. These definitions *must* match those in queue.c. */ +#define queueQUEUE_TYPE_BASE ( ( uint8_t ) 0U ) +#define queueQUEUE_TYPE_SET ( ( uint8_t ) 0U ) +#define queueQUEUE_TYPE_MUTEX ( ( uint8_t ) 1U ) +#define queueQUEUE_TYPE_COUNTING_SEMAPHORE ( ( uint8_t ) 2U ) +#define queueQUEUE_TYPE_BINARY_SEMAPHORE ( ( uint8_t ) 3U ) +#define queueQUEUE_TYPE_RECURSIVE_MUTEX ( ( uint8_t ) 4U ) + +/** + * queue. h + *
+ QueueHandle_t xQueueCreate(
+							  UBaseType_t uxQueueLength,
+							  UBaseType_t uxItemSize
+						  );
+ * 
+ * + * Creates a new queue instance, and returns a handle by which the new queue + * can be referenced. + * + * Internally, within the FreeRTOS implementation, queues use two blocks of + * memory. The first block is used to hold the queue's data structures. The + * second block is used to hold items placed into the queue. If a queue is + * created using xQueueCreate() then both blocks of memory are automatically + * dynamically allocated inside the xQueueCreate() function. (see + * http://www.freertos.org/a00111.html). If a queue is created using + * xQueueCreateStatic() then the application writer must provide the memory that + * will get used by the queue. xQueueCreateStatic() therefore allows a queue to + * be created without using any dynamic memory allocation. + * + * http://www.FreeRTOS.org/Embedded-RTOS-Queues.html + * + * @param uxQueueLength The maximum number of items that the queue can contain. + * + * @param uxItemSize The number of bytes each item in the queue will require. + * Items are queued by copy, not by reference, so this is the number of bytes + * that will be copied for each posted item. Each item on the queue must be + * the same size. + * + * @return If the queue is successfully create then a handle to the newly + * created queue is returned. If the queue cannot be created then 0 is + * returned. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ };
+
+ void vATask( void *pvParameters )
+ {
+ QueueHandle_t xQueue1, xQueue2;
+
+	// Create a queue capable of containing 10 uint32_t values.
+	xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
+	if( xQueue1 == 0 )
+	{
+		// Queue was not created and must not be used.
+	}
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+	if( xQueue2 == 0 )
+	{
+		// Queue was not created and must not be used.
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueCreate xQueueCreate + * \ingroup QueueManagement + */ +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + #define xQueueCreate( uxQueueLength, uxItemSize ) xQueueGenericCreate( ( uxQueueLength ), ( uxItemSize ), ( queueQUEUE_TYPE_BASE ) ) +#endif + +/** + * queue. h + *
+ QueueHandle_t xQueueCreateStatic(
+							  UBaseType_t uxQueueLength,
+							  UBaseType_t uxItemSize,
+							  uint8_t *pucQueueStorageBuffer,
+							  StaticQueue_t *pxQueueBuffer
+						  );
+ * 
+ * + * Creates a new queue instance, and returns a handle by which the new queue + * can be referenced. + * + * Internally, within the FreeRTOS implementation, queues use two blocks of + * memory. The first block is used to hold the queue's data structures. The + * second block is used to hold items placed into the queue. If a queue is + * created using xQueueCreate() then both blocks of memory are automatically + * dynamically allocated inside the xQueueCreate() function. (see + * http://www.freertos.org/a00111.html). If a queue is created using + * xQueueCreateStatic() then the application writer must provide the memory that + * will get used by the queue. xQueueCreateStatic() therefore allows a queue to + * be created without using any dynamic memory allocation. + * + * http://www.FreeRTOS.org/Embedded-RTOS-Queues.html + * + * @param uxQueueLength The maximum number of items that the queue can contain. + * + * @param uxItemSize The number of bytes each item in the queue will require. + * Items are queued by copy, not by reference, so this is the number of bytes + * that will be copied for each posted item. Each item on the queue must be + * the same size. + * + * @param pucQueueStorageBuffer If uxItemSize is not zero then + * pucQueueStorageBuffer must point to a uint8_t array that is at least large + * enough to hold the maximum number of items that can be in the queue at any + * one time - which is ( uxQueueLength * uxItemsSize ) bytes. If uxItemSize is + * zero then pucQueueStorageBuffer can be NULL. + * + * @param pxQueueBuffer Must point to a variable of type StaticQueue_t, which + * will be used to hold the queue's data structure. + * + * @return If the queue is created then a handle to the created queue is + * returned. If pxQueueBuffer is NULL then NULL is returned. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ };
+
+ #define QUEUE_LENGTH 10
+ #define ITEM_SIZE sizeof( uint32_t )
+
+ // xQueueBuffer will hold the queue structure.
+ StaticQueue_t xQueueBuffer;
+
+ // ucQueueStorage will hold the items posted to the queue.  Must be at least
+ // [(queue length) * ( queue item size)] bytes long.
+ uint8_t ucQueueStorage[ QUEUE_LENGTH * ITEM_SIZE ];
+
+ void vATask( void *pvParameters )
+ {
+ QueueHandle_t xQueue1;
+
+	// Create a queue capable of containing 10 uint32_t values.
+	xQueue1 = xQueueCreate( QUEUE_LENGTH, // The number of items the queue can hold.
+							ITEM_SIZE	  // The size of each item in the queue
+							&( ucQueueStorage[ 0 ] ), // The buffer that will hold the items in the queue.
+							&xQueueBuffer ); // The buffer that will hold the queue structure.
+
+	// The queue is guaranteed to be created successfully as no dynamic memory
+	// allocation is used.  Therefore xQueue1 is now a handle to a valid queue.
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueCreateStatic xQueueCreateStatic + * \ingroup QueueManagement + */ +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + #define xQueueCreateStatic( uxQueueLength, uxItemSize, pucQueueStorage, pxQueueBuffer ) xQueueGenericCreateStatic( ( uxQueueLength ), ( uxItemSize ), ( pucQueueStorage ), ( pxQueueBuffer ), ( queueQUEUE_TYPE_BASE ) ) +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +/** + * queue. h + *
+ BaseType_t xQueueSendToToFront(
+								   QueueHandle_t	xQueue,
+								   const void		*pvItemToQueue,
+								   TickType_t		xTicksToWait
+							   );
+ * 
+ * + * Post an item to the front of a queue. The item is queued by copy, not by + * reference. This function must not be called from an interrupt service + * routine. See xQueueSendFromISR () for an alternative which may be used + * in an ISR. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for space to become available on the queue, should it already + * be full. The call will return immediately if this is set to 0 and the + * queue is full. The time is defined in tick periods so the constant + * portTICK_PERIOD_MS should be used to convert to real time if this is required. + * + * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ uint32_t ulVar = 10UL;
+
+ void vATask( void *pvParameters )
+ {
+ QueueHandle_t xQueue1, xQueue2;
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 uint32_t values.
+	xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+
+	// ...
+
+	if( xQueue1 != 0 )
+	{
+		// Send an uint32_t.  Wait for 10 ticks for space to become
+		// available if necessary.
+		if( xQueueSendToFront( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS )
+		{
+			// Failed to post the message, even after 10 ticks.
+		}
+	}
+
+	if( xQueue2 != 0 )
+	{
+		// Send a pointer to a struct AMessage object.  Don't block if the
+		// queue is already full.
+		pxMessage = & xMessage;
+		xQueueSendToFront( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 );
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueSend xQueueSend + * \ingroup QueueManagement + */ +#define xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_FRONT ) + +/** + * queue. h + *
+ BaseType_t xQueueSendToBack(
+								   QueueHandle_t	xQueue,
+								   const void		*pvItemToQueue,
+								   TickType_t		xTicksToWait
+							   );
+ * 
+ * + * This is a macro that calls xQueueGenericSend(). + * + * Post an item to the back of a queue. The item is queued by copy, not by + * reference. This function must not be called from an interrupt service + * routine. See xQueueSendFromISR () for an alternative which may be used + * in an ISR. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for space to become available on the queue, should it already + * be full. The call will return immediately if this is set to 0 and the queue + * is full. The time is defined in tick periods so the constant + * portTICK_PERIOD_MS should be used to convert to real time if this is required. + * + * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ uint32_t ulVar = 10UL;
+
+ void vATask( void *pvParameters )
+ {
+ QueueHandle_t xQueue1, xQueue2;
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 uint32_t values.
+	xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+
+	// ...
+
+	if( xQueue1 != 0 )
+	{
+		// Send an uint32_t.  Wait for 10 ticks for space to become
+		// available if necessary.
+		if( xQueueSendToBack( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS )
+		{
+			// Failed to post the message, even after 10 ticks.
+		}
+	}
+
+	if( xQueue2 != 0 )
+	{
+		// Send a pointer to a struct AMessage object.  Don't block if the
+		// queue is already full.
+		pxMessage = & xMessage;
+		xQueueSendToBack( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 );
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueSend xQueueSend + * \ingroup QueueManagement + */ +#define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK ) + +/** + * queue. h + *
+ BaseType_t xQueueSend(
+							  QueueHandle_t xQueue,
+							  const void * pvItemToQueue,
+							  TickType_t xTicksToWait
+						 );
+ * 
+ * + * This is a macro that calls xQueueGenericSend(). It is included for + * backward compatibility with versions of FreeRTOS.org that did not + * include the xQueueSendToFront() and xQueueSendToBack() macros. It is + * equivalent to xQueueSendToBack(). + * + * Post an item on a queue. The item is queued by copy, not by reference. + * This function must not be called from an interrupt service routine. + * See xQueueSendFromISR () for an alternative which may be used in an ISR. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for space to become available on the queue, should it already + * be full. The call will return immediately if this is set to 0 and the + * queue is full. The time is defined in tick periods so the constant + * portTICK_PERIOD_MS should be used to convert to real time if this is required. + * + * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ uint32_t ulVar = 10UL;
+
+ void vATask( void *pvParameters )
+ {
+ QueueHandle_t xQueue1, xQueue2;
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 uint32_t values.
+	xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+
+	// ...
+
+	if( xQueue1 != 0 )
+	{
+		// Send an uint32_t.  Wait for 10 ticks for space to become
+		// available if necessary.
+		if( xQueueSend( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10 ) != pdPASS )
+		{
+			// Failed to post the message, even after 10 ticks.
+		}
+	}
+
+	if( xQueue2 != 0 )
+	{
+		// Send a pointer to a struct AMessage object.  Don't block if the
+		// queue is already full.
+		pxMessage = & xMessage;
+		xQueueSend( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0 );
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueSend xQueueSend + * \ingroup QueueManagement + */ +#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), ( xTicksToWait ), queueSEND_TO_BACK ) + +/** + * queue. h + *
+ BaseType_t xQueueOverwrite(
+							  QueueHandle_t xQueue,
+							  const void * pvItemToQueue
+						 );
+ * 
+ * + * Only for use with queues that have a length of one - so the queue is either + * empty or full. + * + * Post an item on a queue. If the queue is already full then overwrite the + * value held in the queue. The item is queued by copy, not by reference. + * + * This function must not be called from an interrupt service routine. + * See xQueueOverwriteFromISR () for an alternative which may be used in an ISR. + * + * @param xQueue The handle of the queue to which the data is being sent. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @return xQueueOverwrite() is a macro that calls xQueueGenericSend(), and + * therefore has the same return values as xQueueSendToFront(). However, pdPASS + * is the only value that can be returned because xQueueOverwrite() will write + * to the queue even when the queue is already full. + * + * Example usage: +
+
+ void vFunction( void *pvParameters )
+ {
+ QueueHandle_t xQueue;
+ uint32_t ulVarToSend, ulValReceived;
+
+	// Create a queue to hold one uint32_t value.  It is strongly
+	// recommended *not* to use xQueueOverwrite() on queues that can
+	// contain more than one value, and doing so will trigger an assertion
+	// if configASSERT() is defined.
+	xQueue = xQueueCreate( 1, sizeof( uint32_t ) );
+
+	// Write the value 10 to the queue using xQueueOverwrite().
+	ulVarToSend = 10;
+	xQueueOverwrite( xQueue, &ulVarToSend );
+
+	// Peeking the queue should now return 10, but leave the value 10 in
+	// the queue.  A block time of zero is used as it is known that the
+	// queue holds a value.
+	ulValReceived = 0;
+	xQueuePeek( xQueue, &ulValReceived, 0 );
+
+	if( ulValReceived != 10 )
+	{
+		// Error unless the item was removed by a different task.
+	}
+
+	// The queue is still full.  Use xQueueOverwrite() to overwrite the
+	// value held in the queue with 100.
+	ulVarToSend = 100;
+	xQueueOverwrite( xQueue, &ulVarToSend );
+
+	// This time read from the queue, leaving the queue empty once more.
+	// A block time of 0 is used again.
+	xQueueReceive( xQueue, &ulValReceived, 0 );
+
+	// The value read should be the last value written, even though the
+	// queue was already full when the value was written.
+	if( ulValReceived != 100 )
+	{
+		// Error!
+	}
+
+	// ...
+}
+ 
+ * \defgroup xQueueOverwrite xQueueOverwrite + * \ingroup QueueManagement + */ +#define xQueueOverwrite( xQueue, pvItemToQueue ) xQueueGenericSend( ( xQueue ), ( pvItemToQueue ), 0, queueOVERWRITE ) + + +/** + * queue. h + *
+ BaseType_t xQueueGenericSend(
+									QueueHandle_t xQueue,
+									const void * pvItemToQueue,
+									TickType_t xTicksToWait
+									BaseType_t xCopyPosition
+								);
+ * 
+ * + * It is preferred that the macros xQueueSend(), xQueueSendToFront() and + * xQueueSendToBack() are used in place of calling this function directly. + * + * Post an item on a queue. The item is queued by copy, not by reference. + * This function must not be called from an interrupt service routine. + * See xQueueSendFromISR () for an alternative which may be used in an ISR. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for space to become available on the queue, should it already + * be full. The call will return immediately if this is set to 0 and the + * queue is full. The time is defined in tick periods so the constant + * portTICK_PERIOD_MS should be used to convert to real time if this is required. + * + * @param xCopyPosition Can take the value queueSEND_TO_BACK to place the + * item at the back of the queue, or queueSEND_TO_FRONT to place the item + * at the front of the queue (for high priority messages). + * + * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ uint32_t ulVar = 10UL;
+
+ void vATask( void *pvParameters )
+ {
+ QueueHandle_t xQueue1, xQueue2;
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 uint32_t values.
+	xQueue1 = xQueueCreate( 10, sizeof( uint32_t ) );
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+
+	// ...
+
+	if( xQueue1 != 0 )
+	{
+		// Send an uint32_t.  Wait for 10 ticks for space to become
+		// available if necessary.
+		if( xQueueGenericSend( xQueue1, ( void * ) &ulVar, ( TickType_t ) 10, queueSEND_TO_BACK ) != pdPASS )
+		{
+			// Failed to post the message, even after 10 ticks.
+		}
+	}
+
+	if( xQueue2 != 0 )
+	{
+		// Send a pointer to a struct AMessage object.  Don't block if the
+		// queue is already full.
+		pxMessage = & xMessage;
+		xQueueGenericSend( xQueue2, ( void * ) &pxMessage, ( TickType_t ) 0, queueSEND_TO_BACK );
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueSend xQueueSend + * \ingroup QueueManagement + */ +BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition ) PRIVILEGED_FUNCTION; + +/** + * queue. h + *
+ BaseType_t xQueuePeek(
+							 QueueHandle_t xQueue,
+							 void * const pvBuffer,
+							 TickType_t xTicksToWait
+						 );
+ * + * Receive an item from a queue without removing the item from the queue. + * The item is received by copy so a buffer of adequate size must be + * provided. The number of bytes copied into the buffer was defined when + * the queue was created. + * + * Successfully received items remain on the queue so will be returned again + * by the next call, or a call to xQueueReceive(). + * + * This macro must not be used in an interrupt service routine. See + * xQueuePeekFromISR() for an alternative that can be called from an interrupt + * service routine. + * + * @param xQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for an item to receive should the queue be empty at the time + * of the call. The time is defined in tick periods so the constant + * portTICK_PERIOD_MS should be used to convert to real time if this is required. + * xQueuePeek() will return immediately if xTicksToWait is 0 and the queue + * is empty. + * + * @return pdTRUE if an item was successfully received from the queue, + * otherwise pdFALSE. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ QueueHandle_t xQueue;
+
+ // Task to create a queue and post a value.
+ void vATask( void *pvParameters )
+ {
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
+	if( xQueue == 0 )
+	{
+		// Failed to create the queue.
+	}
+
+	// ...
+
+	// Send a pointer to a struct AMessage object.  Don't block if the
+	// queue is already full.
+	pxMessage = & xMessage;
+	xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 );
+
+	// ... Rest of task code.
+ }
+
+ // Task to peek the data from the queue.
+ void vADifferentTask( void *pvParameters )
+ {
+ struct AMessage *pxRxedMessage;
+
+	if( xQueue != 0 )
+	{
+		// Peek a message on the created queue.  Block for 10 ticks if a
+		// message is not immediately available.
+		if( xQueuePeek( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) )
+		{
+			// pcRxedMessage now points to the struct AMessage variable posted
+			// by vATask, but the item still remains on the queue.
+		}
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueuePeek xQueuePeek + * \ingroup QueueManagement + */ +BaseType_t xQueuePeek( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/** + * queue. h + *
+ BaseType_t xQueuePeekFromISR(
+									QueueHandle_t xQueue,
+									void *pvBuffer,
+								);
+ * + * A version of xQueuePeek() that can be called from an interrupt service + * routine (ISR). + * + * Receive an item from a queue without removing the item from the queue. + * The item is received by copy so a buffer of adequate size must be + * provided. The number of bytes copied into the buffer was defined when + * the queue was created. + * + * Successfully received items remain on the queue so will be returned again + * by the next call, or a call to xQueueReceive(). + * + * @param xQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @return pdTRUE if an item was successfully received from the queue, + * otherwise pdFALSE. + * + * \defgroup xQueuePeekFromISR xQueuePeekFromISR + * \ingroup QueueManagement + */ +BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * const pvBuffer ) PRIVILEGED_FUNCTION; + +/** + * queue. h + *
+ BaseType_t xQueueReceive(
+								 QueueHandle_t xQueue,
+								 void *pvBuffer,
+								 TickType_t xTicksToWait
+							);
+ * + * Receive an item from a queue. The item is received by copy so a buffer of + * adequate size must be provided. The number of bytes copied into the buffer + * was defined when the queue was created. + * + * Successfully received items are removed from the queue. + * + * This function must not be used in an interrupt service routine. See + * xQueueReceiveFromISR for an alternative that can. + * + * @param xQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for an item to receive should the queue be empty at the time + * of the call. xQueueReceive() will return immediately if xTicksToWait + * is zero and the queue is empty. The time is defined in tick periods so the + * constant portTICK_PERIOD_MS should be used to convert to real time if this is + * required. + * + * @return pdTRUE if an item was successfully received from the queue, + * otherwise pdFALSE. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ QueueHandle_t xQueue;
+
+ // Task to create a queue and post a value.
+ void vATask( void *pvParameters )
+ {
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
+	if( xQueue == 0 )
+	{
+		// Failed to create the queue.
+	}
+
+	// ...
+
+	// Send a pointer to a struct AMessage object.  Don't block if the
+	// queue is already full.
+	pxMessage = & xMessage;
+	xQueueSend( xQueue, ( void * ) &pxMessage, ( TickType_t ) 0 );
+
+	// ... Rest of task code.
+ }
+
+ // Task to receive from the queue.
+ void vADifferentTask( void *pvParameters )
+ {
+ struct AMessage *pxRxedMessage;
+
+	if( xQueue != 0 )
+	{
+		// Receive a message on the created queue.  Block for 10 ticks if a
+		// message is not immediately available.
+		if( xQueueReceive( xQueue, &( pxRxedMessage ), ( TickType_t ) 10 ) )
+		{
+			// pcRxedMessage now points to the struct AMessage variable posted
+			// by vATask.
+		}
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueReceive xQueueReceive + * \ingroup QueueManagement + */ +BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/** + * queue. h + *
UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue );
+ * + * Return the number of messages stored in a queue. + * + * @param xQueue A handle to the queue being queried. + * + * @return The number of messages available in the queue. + * + * \defgroup uxQueueMessagesWaiting uxQueueMessagesWaiting + * \ingroup QueueManagement + */ +UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; + +/** + * queue. h + *
UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue );
+ * + * Return the number of free spaces available in a queue. This is equal to the + * number of items that can be sent to the queue before the queue becomes full + * if no items are removed. + * + * @param xQueue A handle to the queue being queried. + * + * @return The number of spaces available in the queue. + * + * \defgroup uxQueueMessagesWaiting uxQueueMessagesWaiting + * \ingroup QueueManagement + */ +UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; + +/** + * queue. h + *
void vQueueDelete( QueueHandle_t xQueue );
+ * + * Delete a queue - freeing all the memory allocated for storing of items + * placed on the queue. + * + * @param xQueue A handle to the queue to be deleted. + * + * \defgroup vQueueDelete vQueueDelete + * \ingroup QueueManagement + */ +void vQueueDelete( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; + +/** + * queue. h + *
+ BaseType_t xQueueSendToFrontFromISR(
+										 QueueHandle_t xQueue,
+										 const void *pvItemToQueue,
+										 BaseType_t *pxHigherPriorityTaskWoken
+									  );
+ 
+ * + * This is a macro that calls xQueueGenericSendFromISR(). + * + * Post an item to the front of a queue. It is safe to use this macro from + * within an interrupt service routine. + * + * Items are queued by copy not reference so it is preferable to only + * queue small items, especially when called from an ISR. In most cases + * it would be preferable to store a pointer to the item being queued. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param pxHigherPriorityTaskWoken xQueueSendToFrontFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueSendToFromFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return pdTRUE if the data was successfully sent to the queue, otherwise + * errQUEUE_FULL. + * + * Example usage for buffered IO (where the ISR can obtain more than one value + * per call): +
+ void vBufferISR( void )
+ {
+ char cIn;
+ BaseType_t xHigherPrioritTaskWoken;
+
+	// We have not woken a task at the start of the ISR.
+	xHigherPriorityTaskWoken = pdFALSE;
+
+	// Loop until the buffer is empty.
+	do
+	{
+		// Obtain a byte from the buffer.
+		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
+
+		// Post the byte.
+		xQueueSendToFrontFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
+
+	} while( portINPUT_BYTE( BUFFER_COUNT ) );
+
+	// Now the buffer is empty we can switch context if necessary.
+	if( xHigherPriorityTaskWoken )
+	{
+		taskYIELD ();
+	}
+ }
+ 
+ * + * \defgroup xQueueSendFromISR xQueueSendFromISR + * \ingroup QueueManagement + */ +#define xQueueSendToFrontFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_FRONT ) + + +/** + * queue. h + *
+ BaseType_t xQueueSendToBackFromISR(
+										 QueueHandle_t xQueue,
+										 const void *pvItemToQueue,
+										 BaseType_t *pxHigherPriorityTaskWoken
+									  );
+ 
+ * + * This is a macro that calls xQueueGenericSendFromISR(). + * + * Post an item to the back of a queue. It is safe to use this macro from + * within an interrupt service routine. + * + * Items are queued by copy not reference so it is preferable to only + * queue small items, especially when called from an ISR. In most cases + * it would be preferable to store a pointer to the item being queued. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param pxHigherPriorityTaskWoken xQueueSendToBackFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueSendToBackFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return pdTRUE if the data was successfully sent to the queue, otherwise + * errQUEUE_FULL. + * + * Example usage for buffered IO (where the ISR can obtain more than one value + * per call): +
+ void vBufferISR( void )
+ {
+ char cIn;
+ BaseType_t xHigherPriorityTaskWoken;
+
+	// We have not woken a task at the start of the ISR.
+	xHigherPriorityTaskWoken = pdFALSE;
+
+	// Loop until the buffer is empty.
+	do
+	{
+		// Obtain a byte from the buffer.
+		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
+
+		// Post the byte.
+		xQueueSendToBackFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
+
+	} while( portINPUT_BYTE( BUFFER_COUNT ) );
+
+	// Now the buffer is empty we can switch context if necessary.
+	if( xHigherPriorityTaskWoken )
+	{
+		taskYIELD ();
+	}
+ }
+ 
+ * + * \defgroup xQueueSendFromISR xQueueSendFromISR + * \ingroup QueueManagement + */ +#define xQueueSendToBackFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK ) + +/** + * queue. h + *
+ BaseType_t xQueueOverwriteFromISR(
+							  QueueHandle_t xQueue,
+							  const void * pvItemToQueue,
+							  BaseType_t *pxHigherPriorityTaskWoken
+						 );
+ * 
+ * + * A version of xQueueOverwrite() that can be used in an interrupt service + * routine (ISR). + * + * Only for use with queues that can hold a single item - so the queue is either + * empty or full. + * + * Post an item on a queue. If the queue is already full then overwrite the + * value held in the queue. The item is queued by copy, not by reference. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param pxHigherPriorityTaskWoken xQueueOverwriteFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueOverwriteFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return xQueueOverwriteFromISR() is a macro that calls + * xQueueGenericSendFromISR(), and therefore has the same return values as + * xQueueSendToFrontFromISR(). However, pdPASS is the only value that can be + * returned because xQueueOverwriteFromISR() will write to the queue even when + * the queue is already full. + * + * Example usage: +
+
+ QueueHandle_t xQueue;
+
+ void vFunction( void *pvParameters )
+ {
+ 	// Create a queue to hold one uint32_t value.  It is strongly
+	// recommended *not* to use xQueueOverwriteFromISR() on queues that can
+	// contain more than one value, and doing so will trigger an assertion
+	// if configASSERT() is defined.
+	xQueue = xQueueCreate( 1, sizeof( uint32_t ) );
+}
+
+void vAnInterruptHandler( void )
+{
+// xHigherPriorityTaskWoken must be set to pdFALSE before it is used.
+BaseType_t xHigherPriorityTaskWoken = pdFALSE;
+uint32_t ulVarToSend, ulValReceived;
+
+	// Write the value 10 to the queue using xQueueOverwriteFromISR().
+	ulVarToSend = 10;
+	xQueueOverwriteFromISR( xQueue, &ulVarToSend, &xHigherPriorityTaskWoken );
+
+	// The queue is full, but calling xQueueOverwriteFromISR() again will still
+	// pass because the value held in the queue will be overwritten with the
+	// new value.
+	ulVarToSend = 100;
+	xQueueOverwriteFromISR( xQueue, &ulVarToSend, &xHigherPriorityTaskWoken );
+
+	// Reading from the queue will now return 100.
+
+	// ...
+
+	if( xHigherPrioritytaskWoken == pdTRUE )
+	{
+		// Writing to the queue caused a task to unblock and the unblocked task
+		// has a priority higher than or equal to the priority of the currently
+		// executing task (the task this interrupt interrupted).  Perform a context
+		// switch so this interrupt returns directly to the unblocked task.
+		portYIELD_FROM_ISR(); // or portEND_SWITCHING_ISR() depending on the port.
+	}
+}
+ 
+ * \defgroup xQueueOverwriteFromISR xQueueOverwriteFromISR + * \ingroup QueueManagement + */ +#define xQueueOverwriteFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueOVERWRITE ) + +/** + * queue. h + *
+ BaseType_t xQueueSendFromISR(
+									 QueueHandle_t xQueue,
+									 const void *pvItemToQueue,
+									 BaseType_t *pxHigherPriorityTaskWoken
+								);
+ 
+ * + * This is a macro that calls xQueueGenericSendFromISR(). It is included + * for backward compatibility with versions of FreeRTOS.org that did not + * include the xQueueSendToBackFromISR() and xQueueSendToFrontFromISR() + * macros. + * + * Post an item to the back of a queue. It is safe to use this function from + * within an interrupt service routine. + * + * Items are queued by copy not reference so it is preferable to only + * queue small items, especially when called from an ISR. In most cases + * it would be preferable to store a pointer to the item being queued. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param pxHigherPriorityTaskWoken xQueueSendFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueSendFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return pdTRUE if the data was successfully sent to the queue, otherwise + * errQUEUE_FULL. + * + * Example usage for buffered IO (where the ISR can obtain more than one value + * per call): +
+ void vBufferISR( void )
+ {
+ char cIn;
+ BaseType_t xHigherPriorityTaskWoken;
+
+	// We have not woken a task at the start of the ISR.
+	xHigherPriorityTaskWoken = pdFALSE;
+
+	// Loop until the buffer is empty.
+	do
+	{
+		// Obtain a byte from the buffer.
+		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
+
+		// Post the byte.
+		xQueueSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
+
+	} while( portINPUT_BYTE( BUFFER_COUNT ) );
+
+	// Now the buffer is empty we can switch context if necessary.
+	if( xHigherPriorityTaskWoken )
+	{
+		// Actual macro used here is port specific.
+		portYIELD_FROM_ISR ();
+	}
+ }
+ 
+ * + * \defgroup xQueueSendFromISR xQueueSendFromISR + * \ingroup QueueManagement + */ +#define xQueueSendFromISR( xQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueue ), ( pvItemToQueue ), ( pxHigherPriorityTaskWoken ), queueSEND_TO_BACK ) + +/** + * queue. h + *
+ BaseType_t xQueueGenericSendFromISR(
+										   QueueHandle_t		xQueue,
+										   const	void	*pvItemToQueue,
+										   BaseType_t	*pxHigherPriorityTaskWoken,
+										   BaseType_t	xCopyPosition
+									   );
+ 
+ * + * It is preferred that the macros xQueueSendFromISR(), + * xQueueSendToFrontFromISR() and xQueueSendToBackFromISR() be used in place + * of calling this function directly. xQueueGiveFromISR() is an + * equivalent for use by semaphores that don't actually copy any data. + * + * Post an item on a queue. It is safe to use this function from within an + * interrupt service routine. + * + * Items are queued by copy not reference so it is preferable to only + * queue small items, especially when called from an ISR. In most cases + * it would be preferable to store a pointer to the item being queued. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param pxHigherPriorityTaskWoken xQueueGenericSendFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueGenericSendFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @param xCopyPosition Can take the value queueSEND_TO_BACK to place the + * item at the back of the queue, or queueSEND_TO_FRONT to place the item + * at the front of the queue (for high priority messages). + * + * @return pdTRUE if the data was successfully sent to the queue, otherwise + * errQUEUE_FULL. + * + * Example usage for buffered IO (where the ISR can obtain more than one value + * per call): +
+ void vBufferISR( void )
+ {
+ char cIn;
+ BaseType_t xHigherPriorityTaskWokenByPost;
+
+	// We have not woken a task at the start of the ISR.
+	xHigherPriorityTaskWokenByPost = pdFALSE;
+
+	// Loop until the buffer is empty.
+	do
+	{
+		// Obtain a byte from the buffer.
+		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
+
+		// Post each byte.
+		xQueueGenericSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWokenByPost, queueSEND_TO_BACK );
+
+	} while( portINPUT_BYTE( BUFFER_COUNT ) );
+
+	// Now the buffer is empty we can switch context if necessary.  Note that the
+	// name of the yield function required is port specific.
+	if( xHigherPriorityTaskWokenByPost )
+	{
+		portYIELD_FROM_ISR();
+	}
+ }
+ 
+ * + * \defgroup xQueueSendFromISR xQueueSendFromISR + * \ingroup QueueManagement + */ +BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, const void * const pvItemToQueue, BaseType_t * const pxHigherPriorityTaskWoken, const BaseType_t xCopyPosition ) PRIVILEGED_FUNCTION; +BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue, BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; + +/** + * queue. h + *
+ BaseType_t xQueueReceiveFromISR(
+									   QueueHandle_t	xQueue,
+									   void	*pvBuffer,
+									   BaseType_t *pxTaskWoken
+								   );
+ * 
+ * + * Receive an item from a queue. It is safe to use this function from within an + * interrupt service routine. + * + * @param xQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @param pxTaskWoken A task may be blocked waiting for space to become + * available on the queue. If xQueueReceiveFromISR causes such a task to + * unblock *pxTaskWoken will get set to pdTRUE, otherwise *pxTaskWoken will + * remain unchanged. + * + * @return pdTRUE if an item was successfully received from the queue, + * otherwise pdFALSE. + * + * Example usage: +
+
+ QueueHandle_t xQueue;
+
+ // Function to create a queue and post some values.
+ void vAFunction( void *pvParameters )
+ {
+ char cValueToPost;
+ const TickType_t xTicksToWait = ( TickType_t )0xff;
+
+	// Create a queue capable of containing 10 characters.
+	xQueue = xQueueCreate( 10, sizeof( char ) );
+	if( xQueue == 0 )
+	{
+		// Failed to create the queue.
+	}
+
+	// ...
+
+	// Post some characters that will be used within an ISR.  If the queue
+	// is full then this task will block for xTicksToWait ticks.
+	cValueToPost = 'a';
+	xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
+	cValueToPost = 'b';
+	xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
+
+	// ... keep posting characters ... this task may block when the queue
+	// becomes full.
+
+	cValueToPost = 'c';
+	xQueueSend( xQueue, ( void * ) &cValueToPost, xTicksToWait );
+ }
+
+ // ISR that outputs all the characters received on the queue.
+ void vISR_Routine( void )
+ {
+ BaseType_t xTaskWokenByReceive = pdFALSE;
+ char cRxedChar;
+
+	while( xQueueReceiveFromISR( xQueue, ( void * ) &cRxedChar, &xTaskWokenByReceive) )
+	{
+		// A character was received.  Output the character now.
+		vOutputCharacter( cRxedChar );
+
+		// If removing the character from the queue woke the task that was
+		// posting onto the queue cTaskWokenByReceive will have been set to
+		// pdTRUE.  No matter how many times this loop iterates only one
+		// task will be woken.
+	}
+
+	if( cTaskWokenByPost != ( char ) pdFALSE;
+	{
+		taskYIELD ();
+	}
+ }
+ 
+ * \defgroup xQueueReceiveFromISR xQueueReceiveFromISR + * \ingroup QueueManagement + */ +BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void * const pvBuffer, BaseType_t * const pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; + +/* + * Utilities to query queues that are safe to use from an ISR. These utilities + * should be used only from witin an ISR, or within a critical section. + */ +BaseType_t xQueueIsQueueEmptyFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +BaseType_t xQueueIsQueueFullFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +UBaseType_t uxQueueMessagesWaitingFromISR( const QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; + +/* + * The functions defined above are for passing data to and from tasks. The + * functions below are the equivalents for passing data to and from + * co-routines. + * + * These functions are called from the co-routine macro implementation and + * should not be called directly from application code. Instead use the macro + * wrappers defined within croutine.h. + */ +BaseType_t xQueueCRSendFromISR( QueueHandle_t xQueue, const void *pvItemToQueue, BaseType_t xCoRoutinePreviouslyWoken ); +BaseType_t xQueueCRReceiveFromISR( QueueHandle_t xQueue, void *pvBuffer, BaseType_t *pxTaskWoken ); +BaseType_t xQueueCRSend( QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait ); +BaseType_t xQueueCRReceive( QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait ); + +/* + * For internal use only. Use xSemaphoreCreateMutex(), + * xSemaphoreCreateCounting() or xSemaphoreGetMutexHolder() instead of calling + * these functions directly. + */ +QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; +QueueHandle_t xQueueCreateMutexStatic( const uint8_t ucQueueType, StaticQueue_t *pxStaticQueue ) PRIVILEGED_FUNCTION; +QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount ) PRIVILEGED_FUNCTION; +QueueHandle_t xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount, StaticQueue_t *pxStaticQueue ) PRIVILEGED_FUNCTION; +BaseType_t xQueueSemaphoreTake( QueueHandle_t xQueue, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +TaskHandle_t xQueueGetMutexHolder( QueueHandle_t xSemaphore ) PRIVILEGED_FUNCTION; +TaskHandle_t xQueueGetMutexHolderFromISR( QueueHandle_t xSemaphore ) PRIVILEGED_FUNCTION; + +/* + * For internal use only. Use xSemaphoreTakeMutexRecursive() or + * xSemaphoreGiveMutexRecursive() instead of calling these functions directly. + */ +BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex ) PRIVILEGED_FUNCTION; + +/* + * Reset a queue back to its original empty state. The return value is now + * obsolete and is always set to pdPASS. + */ +#define xQueueReset( xQueue ) xQueueGenericReset( xQueue, pdFALSE ) + +/* + * The registry is provided as a means for kernel aware debuggers to + * locate queues, semaphores and mutexes. Call vQueueAddToRegistry() add + * a queue, semaphore or mutex handle to the registry if you want the handle + * to be available to a kernel aware debugger. If you are not using a kernel + * aware debugger then this function can be ignored. + * + * configQUEUE_REGISTRY_SIZE defines the maximum number of handles the + * registry can hold. configQUEUE_REGISTRY_SIZE must be greater than 0 + * within FreeRTOSConfig.h for the registry to be available. Its value + * does not effect the number of queues, semaphores and mutexes that can be + * created - just the number that the registry can hold. + * + * @param xQueue The handle of the queue being added to the registry. This + * is the handle returned by a call to xQueueCreate(). Semaphore and mutex + * handles can also be passed in here. + * + * @param pcName The name to be associated with the handle. This is the + * name that the kernel aware debugger will display. The queue registry only + * stores a pointer to the string - so the string must be persistent (global or + * preferably in ROM/Flash), not on the stack. + */ +#if( configQUEUE_REGISTRY_SIZE > 0 ) + void vQueueAddToRegistry( QueueHandle_t xQueue, const char *pcQueueName ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +#endif + +/* + * The registry is provided as a means for kernel aware debuggers to + * locate queues, semaphores and mutexes. Call vQueueAddToRegistry() add + * a queue, semaphore or mutex handle to the registry if you want the handle + * to be available to a kernel aware debugger, and vQueueUnregisterQueue() to + * remove the queue, semaphore or mutex from the register. If you are not using + * a kernel aware debugger then this function can be ignored. + * + * @param xQueue The handle of the queue being removed from the registry. + */ +#if( configQUEUE_REGISTRY_SIZE > 0 ) + void vQueueUnregisterQueue( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +#endif + +/* + * The queue registry is provided as a means for kernel aware debuggers to + * locate queues, semaphores and mutexes. Call pcQueueGetName() to look + * up and return the name of a queue in the queue registry from the queue's + * handle. + * + * @param xQueue The handle of the queue the name of which will be returned. + * @return If the queue is in the registry then a pointer to the name of the + * queue is returned. If the queue is not in the registry then NULL is + * returned. + */ +#if( configQUEUE_REGISTRY_SIZE > 0 ) + const char *pcQueueGetName( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +#endif + +/* + * Generic version of the function used to creaet a queue using dynamic memory + * allocation. This is called by other functions and macros that create other + * RTOS objects that use the queue structure as their base. + */ +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; +#endif + +/* + * Generic version of the function used to creaet a queue using dynamic memory + * allocation. This is called by other functions and macros that create other + * RTOS objects that use the queue structure as their base. + */ +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + QueueHandle_t xQueueGenericCreateStatic( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, StaticQueue_t *pxStaticQueue, const uint8_t ucQueueType ) PRIVILEGED_FUNCTION; +#endif + +/* + * Queue sets provide a mechanism to allow a task to block (pend) on a read + * operation from multiple queues or semaphores simultaneously. + * + * See FreeRTOS/Source/Demo/Common/Minimal/QueueSet.c for an example using this + * function. + * + * A queue set must be explicitly created using a call to xQueueCreateSet() + * before it can be used. Once created, standard FreeRTOS queues and semaphores + * can be added to the set using calls to xQueueAddToSet(). + * xQueueSelectFromSet() is then used to determine which, if any, of the queues + * or semaphores contained in the set is in a state where a queue read or + * semaphore take operation would be successful. + * + * Note 1: See the documentation on http://wwwFreeRTOS.org/RTOS-queue-sets.html + * for reasons why queue sets are very rarely needed in practice as there are + * simpler methods of blocking on multiple objects. + * + * Note 2: Blocking on a queue set that contains a mutex will not cause the + * mutex holder to inherit the priority of the blocked task. + * + * Note 3: An additional 4 bytes of RAM is required for each space in a every + * queue added to a queue set. Therefore counting semaphores that have a high + * maximum count value should not be added to a queue set. + * + * Note 4: A receive (in the case of a queue) or take (in the case of a + * semaphore) operation must not be performed on a member of a queue set unless + * a call to xQueueSelectFromSet() has first returned a handle to that set member. + * + * @param uxEventQueueLength Queue sets store events that occur on + * the queues and semaphores contained in the set. uxEventQueueLength specifies + * the maximum number of events that can be queued at once. To be absolutely + * certain that events are not lost uxEventQueueLength should be set to the + * total sum of the length of the queues added to the set, where binary + * semaphores and mutexes have a length of 1, and counting semaphores have a + * length set by their maximum count value. Examples: + * + If a queue set is to hold a queue of length 5, another queue of length 12, + * and a binary semaphore, then uxEventQueueLength should be set to + * (5 + 12 + 1), or 18. + * + If a queue set is to hold three binary semaphores then uxEventQueueLength + * should be set to (1 + 1 + 1 ), or 3. + * + If a queue set is to hold a counting semaphore that has a maximum count of + * 5, and a counting semaphore that has a maximum count of 3, then + * uxEventQueueLength should be set to (5 + 3), or 8. + * + * @return If the queue set is created successfully then a handle to the created + * queue set is returned. Otherwise NULL is returned. + */ +QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength ) PRIVILEGED_FUNCTION; + +/* + * Adds a queue or semaphore to a queue set that was previously created by a + * call to xQueueCreateSet(). + * + * See FreeRTOS/Source/Demo/Common/Minimal/QueueSet.c for an example using this + * function. + * + * Note 1: A receive (in the case of a queue) or take (in the case of a + * semaphore) operation must not be performed on a member of a queue set unless + * a call to xQueueSelectFromSet() has first returned a handle to that set member. + * + * @param xQueueOrSemaphore The handle of the queue or semaphore being added to + * the queue set (cast to an QueueSetMemberHandle_t type). + * + * @param xQueueSet The handle of the queue set to which the queue or semaphore + * is being added. + * + * @return If the queue or semaphore was successfully added to the queue set + * then pdPASS is returned. If the queue could not be successfully added to the + * queue set because it is already a member of a different queue set then pdFAIL + * is returned. + */ +BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; + +/* + * Removes a queue or semaphore from a queue set. A queue or semaphore can only + * be removed from a set if the queue or semaphore is empty. + * + * See FreeRTOS/Source/Demo/Common/Minimal/QueueSet.c for an example using this + * function. + * + * @param xQueueOrSemaphore The handle of the queue or semaphore being removed + * from the queue set (cast to an QueueSetMemberHandle_t type). + * + * @param xQueueSet The handle of the queue set in which the queue or semaphore + * is included. + * + * @return If the queue or semaphore was successfully removed from the queue set + * then pdPASS is returned. If the queue was not in the queue set, or the + * queue (or semaphore) was not empty, then pdFAIL is returned. + */ +BaseType_t xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; + +/* + * xQueueSelectFromSet() selects from the members of a queue set a queue or + * semaphore that either contains data (in the case of a queue) or is available + * to take (in the case of a semaphore). xQueueSelectFromSet() effectively + * allows a task to block (pend) on a read operation on all the queues and + * semaphores in a queue set simultaneously. + * + * See FreeRTOS/Source/Demo/Common/Minimal/QueueSet.c for an example using this + * function. + * + * Note 1: See the documentation on http://wwwFreeRTOS.org/RTOS-queue-sets.html + * for reasons why queue sets are very rarely needed in practice as there are + * simpler methods of blocking on multiple objects. + * + * Note 2: Blocking on a queue set that contains a mutex will not cause the + * mutex holder to inherit the priority of the blocked task. + * + * Note 3: A receive (in the case of a queue) or take (in the case of a + * semaphore) operation must not be performed on a member of a queue set unless + * a call to xQueueSelectFromSet() has first returned a handle to that set member. + * + * @param xQueueSet The queue set on which the task will (potentially) block. + * + * @param xTicksToWait The maximum time, in ticks, that the calling task will + * remain in the Blocked state (with other tasks executing) to wait for a member + * of the queue set to be ready for a successful queue read or semaphore take + * operation. + * + * @return xQueueSelectFromSet() will return the handle of a queue (cast to + * a QueueSetMemberHandle_t type) contained in the queue set that contains data, + * or the handle of a semaphore (cast to a QueueSetMemberHandle_t type) contained + * in the queue set that is available, or NULL if no such queue or semaphore + * exists before before the specified block time expires. + */ +QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/* + * A version of xQueueSelectFromSet() that can be used from an ISR. + */ +QueueSetMemberHandle_t xQueueSelectFromSetFromISR( QueueSetHandle_t xQueueSet ) PRIVILEGED_FUNCTION; + +/* Not public API functions. */ +void vQueueWaitForMessageRestricted( QueueHandle_t xQueue, TickType_t xTicksToWait, const BaseType_t xWaitIndefinitely ) PRIVILEGED_FUNCTION; +BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue ) PRIVILEGED_FUNCTION; +void vQueueSetQueueNumber( QueueHandle_t xQueue, UBaseType_t uxQueueNumber ) PRIVILEGED_FUNCTION; +UBaseType_t uxQueueGetQueueNumber( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; +uint8_t ucQueueGetQueueType( QueueHandle_t xQueue ) PRIVILEGED_FUNCTION; + + +#ifdef __cplusplus +} +#endif + +#endif /* QUEUE_H */ + diff --git a/product/rcar/src/CMSIS-FreeRTOS/Source/include/stack_macros.h b/product/rcar/src/CMSIS-FreeRTOS/Source/include/stack_macros.h new file mode 100644 index 000000000..b5bac0836 --- /dev/null +++ b/product/rcar/src/CMSIS-FreeRTOS/Source/include/stack_macros.h @@ -0,0 +1,129 @@ +/* + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +#ifndef STACK_MACROS_H +#define STACK_MACROS_H + +/* + * Call the stack overflow hook function if the stack of the task being swapped + * out is currently overflowed, or looks like it might have overflowed in the + * past. + * + * Setting configCHECK_FOR_STACK_OVERFLOW to 1 will cause the macro to check + * the current stack state only - comparing the current top of stack value to + * the stack limit. Setting configCHECK_FOR_STACK_OVERFLOW to greater than 1 + * will also cause the last few stack bytes to be checked to ensure the value + * to which the bytes were set when the task was created have not been + * overwritten. Note this second test does not guarantee that an overflowed + * stack will always be recognised. + */ + +/*-----------------------------------------------------------*/ + +#if( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) && ( portSTACK_GROWTH < 0 ) ) + + /* Only the current stack state is to be checked. */ + #define taskCHECK_FOR_STACK_OVERFLOW() \ + { \ + /* Is the currently saved stack pointer within the stack limit? */ \ + if( pxCurrentTCB->pxTopOfStack <= pxCurrentTCB->pxStack ) \ + { \ + vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ + } \ + } + +#endif /* configCHECK_FOR_STACK_OVERFLOW == 1 */ +/*-----------------------------------------------------------*/ + +#if( ( configCHECK_FOR_STACK_OVERFLOW == 1 ) && ( portSTACK_GROWTH > 0 ) ) + + /* Only the current stack state is to be checked. */ + #define taskCHECK_FOR_STACK_OVERFLOW() \ + { \ + \ + /* Is the currently saved stack pointer within the stack limit? */ \ + if( pxCurrentTCB->pxTopOfStack >= pxCurrentTCB->pxEndOfStack ) \ + { \ + vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ + } \ + } + +#endif /* configCHECK_FOR_STACK_OVERFLOW == 1 */ +/*-----------------------------------------------------------*/ + +#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH < 0 ) ) + + #define taskCHECK_FOR_STACK_OVERFLOW() \ + { \ + const uint32_t * const pulStack = ( uint32_t * ) pxCurrentTCB->pxStack; \ + const uint32_t ulCheckValue = ( uint32_t ) 0xa5a5a5a5; \ + \ + if( ( pulStack[ 0 ] != ulCheckValue ) || \ + ( pulStack[ 1 ] != ulCheckValue ) || \ + ( pulStack[ 2 ] != ulCheckValue ) || \ + ( pulStack[ 3 ] != ulCheckValue ) ) \ + { \ + vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ + } \ + } + +#endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */ +/*-----------------------------------------------------------*/ + +#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH > 0 ) ) + + #define taskCHECK_FOR_STACK_OVERFLOW() \ + { \ + int8_t *pcEndOfStack = ( int8_t * ) pxCurrentTCB->pxEndOfStack; \ + static const uint8_t ucExpectedStackBytes[] = { tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE }; \ + \ + \ + pcEndOfStack -= sizeof( ucExpectedStackBytes ); \ + \ + /* Has the extremity of the task stack ever been written over? */ \ + if( memcmp( ( void * ) pcEndOfStack, ( void * ) ucExpectedStackBytes, sizeof( ucExpectedStackBytes ) ) != 0 ) \ + { \ + vApplicationStackOverflowHook( ( TaskHandle_t ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ + } \ + } + +#endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */ +/*-----------------------------------------------------------*/ + +/* Remove stack overflow macro if not being used. */ +#ifndef taskCHECK_FOR_STACK_OVERFLOW + #define taskCHECK_FOR_STACK_OVERFLOW() +#endif + + + +#endif /* STACK_MACROS_H */ + diff --git a/product/rcar/src/CMSIS-FreeRTOS/Source/include/task.h b/product/rcar/src/CMSIS-FreeRTOS/Source/include/task.h new file mode 100644 index 000000000..b0cc60b6e --- /dev/null +++ b/product/rcar/src/CMSIS-FreeRTOS/Source/include/task.h @@ -0,0 +1,2543 @@ +/* + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + + +#ifndef INC_TASK_H +#define INC_TASK_H + +#ifndef INC_FREERTOS_H + #error "include FreeRTOS.h must appear in source files before include task.h" +#endif + +#include "list.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*----------------------------------------------------------- + * MACROS AND DEFINITIONS + *----------------------------------------------------------*/ + +#define tskKERNEL_VERSION_NUMBER "V10.3.1" +#define tskKERNEL_VERSION_MAJOR 10 +#define tskKERNEL_VERSION_MINOR 3 +#define tskKERNEL_VERSION_BUILD 1 + +/* MPU region parameters passed in ulParameters + * of MemoryRegion_t struct. */ +#define tskMPU_REGION_READ_ONLY ( 1UL << 0UL ) +#define tskMPU_REGION_READ_WRITE ( 1UL << 1UL ) +#define tskMPU_REGION_EXECUTE_NEVER ( 1UL << 2UL ) +#define tskMPU_REGION_NORMAL_MEMORY ( 1UL << 3UL ) +#define tskMPU_REGION_DEVICE_MEMORY ( 1UL << 4UL ) + +/** + * task. h + * + * Type by which tasks are referenced. For example, a call to xTaskCreate + * returns (via a pointer parameter) an TaskHandle_t variable that can then + * be used as a parameter to vTaskDelete to delete the task. + * + * \defgroup TaskHandle_t TaskHandle_t + * \ingroup Tasks + */ +struct tskTaskControlBlock; /* The old naming convention is used to prevent breaking kernel aware debuggers. */ +typedef struct tskTaskControlBlock* TaskHandle_t; + +/* + * Defines the prototype to which the application task hook function must + * conform. + */ +typedef BaseType_t (*TaskHookFunction_t)( void * ); + +/* Task states returned by eTaskGetState. */ +typedef enum +{ + eRunning = 0, /* A task is querying the state of itself, so must be running. */ + eReady, /* The task being queried is in a read or pending ready list. */ + eBlocked, /* The task being queried is in the Blocked state. */ + eSuspended, /* The task being queried is in the Suspended state, or is in the Blocked state with an infinite time out. */ + eDeleted, /* The task being queried has been deleted, but its TCB has not yet been freed. */ + eInvalid /* Used as an 'invalid state' value. */ +} eTaskState; + +/* Actions that can be performed when vTaskNotify() is called. */ +typedef enum +{ + eNoAction = 0, /* Notify the task without updating its notify value. */ + eSetBits, /* Set bits in the task's notification value. */ + eIncrement, /* Increment the task's notification value. */ + eSetValueWithOverwrite, /* Set the task's notification value to a specific value even if the previous value has not yet been read by the task. */ + eSetValueWithoutOverwrite /* Set the task's notification value if the previous value has been read by the task. */ +} eNotifyAction; + +/* + * Used internally only. + */ +typedef struct xTIME_OUT +{ + BaseType_t xOverflowCount; + TickType_t xTimeOnEntering; +} TimeOut_t; + +/* + * Defines the memory ranges allocated to the task when an MPU is used. + */ +typedef struct xMEMORY_REGION +{ + void *pvBaseAddress; + uint32_t ulLengthInBytes; + uint32_t ulParameters; +} MemoryRegion_t; + +/* + * Parameters required to create an MPU protected task. + */ +typedef struct xTASK_PARAMETERS +{ + TaskFunction_t pvTaskCode; + const char * const pcName; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + configSTACK_DEPTH_TYPE usStackDepth; + void *pvParameters; + UBaseType_t uxPriority; + StackType_t *puxStackBuffer; + MemoryRegion_t xRegions[ portNUM_CONFIGURABLE_REGIONS ]; + #if ( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + StaticTask_t * const pxTaskBuffer; + #endif +} TaskParameters_t; + +/* Used with the uxTaskGetSystemState() function to return the state of each task +in the system. */ +typedef struct xTASK_STATUS +{ + TaskHandle_t xHandle; /* The handle of the task to which the rest of the information in the structure relates. */ + const char *pcTaskName; /* A pointer to the task's name. This value will be invalid if the task was deleted since the structure was populated! */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + UBaseType_t xTaskNumber; /* A number unique to the task. */ + eTaskState eCurrentState; /* The state in which the task existed when the structure was populated. */ + UBaseType_t uxCurrentPriority; /* The priority at which the task was running (may be inherited) when the structure was populated. */ + UBaseType_t uxBasePriority; /* The priority to which the task will return if the task's current priority has been inherited to avoid unbounded priority inversion when obtaining a mutex. Only valid if configUSE_MUTEXES is defined as 1 in FreeRTOSConfig.h. */ + uint32_t ulRunTimeCounter; /* The total run time allocated to the task so far, as defined by the run time stats clock. See http://www.freertos.org/rtos-run-time-stats.html. Only valid when configGENERATE_RUN_TIME_STATS is defined as 1 in FreeRTOSConfig.h. */ + StackType_t *pxStackBase; /* Points to the lowest address of the task's stack area. */ + configSTACK_DEPTH_TYPE usStackHighWaterMark; /* The minimum amount of stack space that has remained for the task since the task was created. The closer this value is to zero the closer the task has come to overflowing its stack. */ +} TaskStatus_t; + +/* Possible return values for eTaskConfirmSleepModeStatus(). */ +typedef enum +{ + eAbortSleep = 0, /* A task has been made ready or a context switch pended since portSUPPORESS_TICKS_AND_SLEEP() was called - abort entering a sleep mode. */ + eStandardSleep, /* Enter a sleep mode that will not last any longer than the expected idle time. */ + eNoTasksWaitingTimeout /* No tasks are waiting for a timeout so it is safe to enter a sleep mode that can only be exited by an external interrupt. */ +} eSleepModeStatus; + +/** + * Defines the priority used by the idle task. This must not be modified. + * + * \ingroup TaskUtils + */ +#define tskIDLE_PRIORITY ( ( UBaseType_t ) 0U ) + +/** + * task. h + * + * Macro for forcing a context switch. + * + * \defgroup taskYIELD taskYIELD + * \ingroup SchedulerControl + */ +#define taskYIELD() portYIELD() + +/** + * task. h + * + * Macro to mark the start of a critical code region. Preemptive context + * switches cannot occur when in a critical region. + * + * NOTE: This may alter the stack (depending on the portable implementation) + * so must be used with care! + * + * \defgroup taskENTER_CRITICAL taskENTER_CRITICAL + * \ingroup SchedulerControl + */ +#define taskENTER_CRITICAL() portENTER_CRITICAL() +#define taskENTER_CRITICAL_FROM_ISR() portSET_INTERRUPT_MASK_FROM_ISR() + +/** + * task. h + * + * Macro to mark the end of a critical code region. Preemptive context + * switches cannot occur when in a critical region. + * + * NOTE: This may alter the stack (depending on the portable implementation) + * so must be used with care! + * + * \defgroup taskEXIT_CRITICAL taskEXIT_CRITICAL + * \ingroup SchedulerControl + */ +#define taskEXIT_CRITICAL() portEXIT_CRITICAL() +#define taskEXIT_CRITICAL_FROM_ISR( x ) portCLEAR_INTERRUPT_MASK_FROM_ISR( x ) +/** + * task. h + * + * Macro to disable all maskable interrupts. + * + * \defgroup taskDISABLE_INTERRUPTS taskDISABLE_INTERRUPTS + * \ingroup SchedulerControl + */ +#define taskDISABLE_INTERRUPTS() portDISABLE_INTERRUPTS() + +/** + * task. h + * + * Macro to enable microcontroller interrupts. + * + * \defgroup taskENABLE_INTERRUPTS taskENABLE_INTERRUPTS + * \ingroup SchedulerControl + */ +#define taskENABLE_INTERRUPTS() portENABLE_INTERRUPTS() + +/* Definitions returned by xTaskGetSchedulerState(). taskSCHEDULER_SUSPENDED is +0 to generate more optimal code when configASSERT() is defined as the constant +is used in assert() statements. */ +#define taskSCHEDULER_SUSPENDED ( ( BaseType_t ) 0 ) +#define taskSCHEDULER_NOT_STARTED ( ( BaseType_t ) 1 ) +#define taskSCHEDULER_RUNNING ( ( BaseType_t ) 2 ) + + +/*----------------------------------------------------------- + * TASK CREATION API + *----------------------------------------------------------*/ + +/** + * task. h + *
+ BaseType_t xTaskCreate(
+							  TaskFunction_t pvTaskCode,
+							  const char * const pcName,
+							  configSTACK_DEPTH_TYPE usStackDepth,
+							  void *pvParameters,
+							  UBaseType_t uxPriority,
+							  TaskHandle_t *pvCreatedTask
+						  );
+ * + * Create a new task and add it to the list of tasks that are ready to run. + * + * Internally, within the FreeRTOS implementation, tasks use two blocks of + * memory. The first block is used to hold the task's data structures. The + * second block is used by the task as its stack. If a task is created using + * xTaskCreate() then both blocks of memory are automatically dynamically + * allocated inside the xTaskCreate() function. (see + * http://www.freertos.org/a00111.html). If a task is created using + * xTaskCreateStatic() then the application writer must provide the required + * memory. xTaskCreateStatic() therefore allows a task to be created without + * using any dynamic memory allocation. + * + * See xTaskCreateStatic() for a version that does not use any dynamic memory + * allocation. + * + * xTaskCreate() can only be used to create a task that has unrestricted + * access to the entire microcontroller memory map. Systems that include MPU + * support can alternatively create an MPU constrained task using + * xTaskCreateRestricted(). + * + * @param pvTaskCode Pointer to the task entry function. Tasks + * must be implemented to never return (i.e. continuous loop). + * + * @param pcName A descriptive name for the task. This is mainly used to + * facilitate debugging. Max length defined by configMAX_TASK_NAME_LEN - default + * is 16. + * + * @param usStackDepth The size of the task stack specified as the number of + * variables the stack can hold - not the number of bytes. For example, if + * the stack is 16 bits wide and usStackDepth is defined as 100, 200 bytes + * will be allocated for stack storage. + * + * @param pvParameters Pointer that will be used as the parameter for the task + * being created. + * + * @param uxPriority The priority at which the task should run. Systems that + * include MPU support can optionally create tasks in a privileged (system) + * mode by setting bit portPRIVILEGE_BIT of the priority parameter. For + * example, to create a privileged task at priority 2 the uxPriority parameter + * should be set to ( 2 | portPRIVILEGE_BIT ). + * + * @param pvCreatedTask Used to pass back a handle by which the created task + * can be referenced. + * + * @return pdPASS if the task was successfully created and added to a ready + * list, otherwise an error code defined in the file projdefs.h + * + * Example usage: +
+ // Task to be created.
+ void vTaskCode( void * pvParameters )
+ {
+	 for( ;; )
+	 {
+		 // Task code goes here.
+	 }
+ }
+
+ // Function that creates a task.
+ void vOtherFunction( void )
+ {
+ static uint8_t ucParameterToPass;
+ TaskHandle_t xHandle = NULL;
+
+	 // Create the task, storing the handle.  Note that the passed parameter ucParameterToPass
+	 // must exist for the lifetime of the task, so in this case is declared static.  If it was just an
+	 // an automatic stack variable it might no longer exist, or at least have been corrupted, by the time
+	 // the new task attempts to access it.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, &ucParameterToPass, tskIDLE_PRIORITY, &xHandle );
+	 configASSERT( xHandle );
+
+	 // Use the handle to delete the task.
+	 if( xHandle != NULL )
+	 {
+	 	vTaskDelete( xHandle );
+	 }
+ }
+   
+ * \defgroup xTaskCreate xTaskCreate + * \ingroup Tasks + */ +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const configSTACK_DEPTH_TYPE usStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + *
+ TaskHandle_t xTaskCreateStatic( TaskFunction_t pvTaskCode,
+								 const char * const pcName,
+								 uint32_t ulStackDepth,
+								 void *pvParameters,
+								 UBaseType_t uxPriority,
+								 StackType_t *pxStackBuffer,
+								 StaticTask_t *pxTaskBuffer );
+ * + * Create a new task and add it to the list of tasks that are ready to run. + * + * Internally, within the FreeRTOS implementation, tasks use two blocks of + * memory. The first block is used to hold the task's data structures. The + * second block is used by the task as its stack. If a task is created using + * xTaskCreate() then both blocks of memory are automatically dynamically + * allocated inside the xTaskCreate() function. (see + * http://www.freertos.org/a00111.html). If a task is created using + * xTaskCreateStatic() then the application writer must provide the required + * memory. xTaskCreateStatic() therefore allows a task to be created without + * using any dynamic memory allocation. + * + * @param pvTaskCode Pointer to the task entry function. Tasks + * must be implemented to never return (i.e. continuous loop). + * + * @param pcName A descriptive name for the task. This is mainly used to + * facilitate debugging. The maximum length of the string is defined by + * configMAX_TASK_NAME_LEN in FreeRTOSConfig.h. + * + * @param ulStackDepth The size of the task stack specified as the number of + * variables the stack can hold - not the number of bytes. For example, if + * the stack is 32-bits wide and ulStackDepth is defined as 100 then 400 bytes + * will be allocated for stack storage. + * + * @param pvParameters Pointer that will be used as the parameter for the task + * being created. + * + * @param uxPriority The priority at which the task will run. + * + * @param pxStackBuffer Must point to a StackType_t array that has at least + * ulStackDepth indexes - the array will then be used as the task's stack, + * removing the need for the stack to be allocated dynamically. + * + * @param pxTaskBuffer Must point to a variable of type StaticTask_t, which will + * then be used to hold the task's data structures, removing the need for the + * memory to be allocated dynamically. + * + * @return If neither pxStackBuffer or pxTaskBuffer are NULL, then the task will + * be created and a handle to the created task is returned. If either + * pxStackBuffer or pxTaskBuffer are NULL then the task will not be created and + * NULL is returned. + * + * Example usage: +
+
+    // Dimensions the buffer that the task being created will use as its stack.
+    // NOTE:  This is the number of words the stack will hold, not the number of
+    // bytes.  For example, if each stack item is 32-bits, and this is set to 100,
+    // then 400 bytes (100 * 32-bits) will be allocated.
+    #define STACK_SIZE 200
+
+    // Structure that will hold the TCB of the task being created.
+    StaticTask_t xTaskBuffer;
+
+    // Buffer that the task being created will use as its stack.  Note this is
+    // an array of StackType_t variables.  The size of StackType_t is dependent on
+    // the RTOS port.
+    StackType_t xStack[ STACK_SIZE ];
+
+    // Function that implements the task being created.
+    void vTaskCode( void * pvParameters )
+    {
+        // The parameter value is expected to be 1 as 1 is passed in the
+        // pvParameters value in the call to xTaskCreateStatic().
+        configASSERT( ( uint32_t ) pvParameters == 1UL );
+
+        for( ;; )
+        {
+            // Task code goes here.
+        }
+    }
+
+    // Function that creates a task.
+    void vOtherFunction( void )
+    {
+        TaskHandle_t xHandle = NULL;
+
+        // Create the task without using any dynamic memory allocation.
+        xHandle = xTaskCreateStatic(
+                      vTaskCode,       // Function that implements the task.
+                      "NAME",          // Text name for the task.
+                      STACK_SIZE,      // Stack size in words, not bytes.
+                      ( void * ) 1,    // Parameter passed into the task.
+                      tskIDLE_PRIORITY,// Priority at which the task is created.
+                      xStack,          // Array to use as the task's stack.
+                      &xTaskBuffer );  // Variable to hold the task's data structure.
+
+        // puxStackBuffer and pxTaskBuffer were not NULL, so the task will have
+        // been created, and xHandle will be the task's handle.  Use the handle
+        // to suspend the task.
+        vTaskSuspend( xHandle );
+    }
+   
+ * \defgroup xTaskCreateStatic xTaskCreateStatic + * \ingroup Tasks + */ +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const uint32_t ulStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + StackType_t * const puxStackBuffer, + StaticTask_t * const pxTaskBuffer ) PRIVILEGED_FUNCTION; +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +/** + * task. h + *
+ BaseType_t xTaskCreateRestricted( TaskParameters_t *pxTaskDefinition, TaskHandle_t *pxCreatedTask );
+ * + * Only available when configSUPPORT_DYNAMIC_ALLOCATION is set to 1. + * + * xTaskCreateRestricted() should only be used in systems that include an MPU + * implementation. + * + * Create a new task and add it to the list of tasks that are ready to run. + * The function parameters define the memory regions and associated access + * permissions allocated to the task. + * + * See xTaskCreateRestrictedStatic() for a version that does not use any + * dynamic memory allocation. + * + * @param pxTaskDefinition Pointer to a structure that contains a member + * for each of the normal xTaskCreate() parameters (see the xTaskCreate() API + * documentation) plus an optional stack buffer and the memory region + * definitions. + * + * @param pxCreatedTask Used to pass back a handle by which the created task + * can be referenced. + * + * @return pdPASS if the task was successfully created and added to a ready + * list, otherwise an error code defined in the file projdefs.h + * + * Example usage: +
+// Create an TaskParameters_t structure that defines the task to be created.
+static const TaskParameters_t xCheckTaskParameters =
+{
+	vATask,		// pvTaskCode - the function that implements the task.
+	"ATask",	// pcName - just a text name for the task to assist debugging.
+	100,		// usStackDepth	- the stack size DEFINED IN WORDS.
+	NULL,		// pvParameters - passed into the task function as the function parameters.
+	( 1UL | portPRIVILEGE_BIT ),// uxPriority - task priority, set the portPRIVILEGE_BIT if the task should run in a privileged state.
+	cStackBuffer,// puxStackBuffer - the buffer to be used as the task stack.
+
+	// xRegions - Allocate up to three separate memory regions for access by
+	// the task, with appropriate access permissions.  Different processors have
+	// different memory alignment requirements - refer to the FreeRTOS documentation
+	// for full information.
+	{
+		// Base address					Length	Parameters
+		{ cReadWriteArray,				32,		portMPU_REGION_READ_WRITE },
+		{ cReadOnlyArray,				32,		portMPU_REGION_READ_ONLY },
+		{ cPrivilegedOnlyAccessArray,	128,	portMPU_REGION_PRIVILEGED_READ_WRITE }
+	}
+};
+
+int main( void )
+{
+TaskHandle_t xHandle;
+
+	// Create a task from the const structure defined above.  The task handle
+	// is requested (the second parameter is not NULL) but in this case just for
+	// demonstration purposes as its not actually used.
+	xTaskCreateRestricted( &xRegTest1Parameters, &xHandle );
+
+	// Start the scheduler.
+	vTaskStartScheduler();
+
+	// Will only get here if there was insufficient memory to create the idle
+	// and/or timer task.
+	for( ;; );
+}
+   
+ * \defgroup xTaskCreateRestricted xTaskCreateRestricted + * \ingroup Tasks + */ +#if( portUSING_MPU_WRAPPERS == 1 ) + BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + *
+ BaseType_t xTaskCreateRestrictedStatic( TaskParameters_t *pxTaskDefinition, TaskHandle_t *pxCreatedTask );
+ * + * Only available when configSUPPORT_STATIC_ALLOCATION is set to 1. + * + * xTaskCreateRestrictedStatic() should only be used in systems that include an + * MPU implementation. + * + * Internally, within the FreeRTOS implementation, tasks use two blocks of + * memory. The first block is used to hold the task's data structures. The + * second block is used by the task as its stack. If a task is created using + * xTaskCreateRestricted() then the stack is provided by the application writer, + * and the memory used to hold the task's data structure is automatically + * dynamically allocated inside the xTaskCreateRestricted() function. If a task + * is created using xTaskCreateRestrictedStatic() then the application writer + * must provide the memory used to hold the task's data structures too. + * xTaskCreateRestrictedStatic() therefore allows a memory protected task to be + * created without using any dynamic memory allocation. + * + * @param pxTaskDefinition Pointer to a structure that contains a member + * for each of the normal xTaskCreate() parameters (see the xTaskCreate() API + * documentation) plus an optional stack buffer and the memory region + * definitions. If configSUPPORT_STATIC_ALLOCATION is set to 1 the structure + * contains an additional member, which is used to point to a variable of type + * StaticTask_t - which is then used to hold the task's data structure. + * + * @param pxCreatedTask Used to pass back a handle by which the created task + * can be referenced. + * + * @return pdPASS if the task was successfully created and added to a ready + * list, otherwise an error code defined in the file projdefs.h + * + * Example usage: +
+// Create an TaskParameters_t structure that defines the task to be created.
+// The StaticTask_t variable is only included in the structure when
+// configSUPPORT_STATIC_ALLOCATION is set to 1.  The PRIVILEGED_DATA macro can
+// be used to force the variable into the RTOS kernel's privileged data area.
+static PRIVILEGED_DATA StaticTask_t xTaskBuffer;
+static const TaskParameters_t xCheckTaskParameters =
+{
+	vATask,		// pvTaskCode - the function that implements the task.
+	"ATask",	// pcName - just a text name for the task to assist debugging.
+	100,		// usStackDepth	- the stack size DEFINED IN WORDS.
+	NULL,		// pvParameters - passed into the task function as the function parameters.
+	( 1UL | portPRIVILEGE_BIT ),// uxPriority - task priority, set the portPRIVILEGE_BIT if the task should run in a privileged state.
+	cStackBuffer,// puxStackBuffer - the buffer to be used as the task stack.
+
+	// xRegions - Allocate up to three separate memory regions for access by
+	// the task, with appropriate access permissions.  Different processors have
+	// different memory alignment requirements - refer to the FreeRTOS documentation
+	// for full information.
+	{
+		// Base address					Length	Parameters
+		{ cReadWriteArray,				32,		portMPU_REGION_READ_WRITE },
+		{ cReadOnlyArray,				32,		portMPU_REGION_READ_ONLY },
+		{ cPrivilegedOnlyAccessArray,	128,	portMPU_REGION_PRIVILEGED_READ_WRITE }
+	}
+
+	&xTaskBuffer; // Holds the task's data structure.
+};
+
+int main( void )
+{
+TaskHandle_t xHandle;
+
+	// Create a task from the const structure defined above.  The task handle
+	// is requested (the second parameter is not NULL) but in this case just for
+	// demonstration purposes as its not actually used.
+	xTaskCreateRestricted( &xRegTest1Parameters, &xHandle );
+
+	// Start the scheduler.
+	vTaskStartScheduler();
+
+	// Will only get here if there was insufficient memory to create the idle
+	// and/or timer task.
+	for( ;; );
+}
+   
+ * \defgroup xTaskCreateRestrictedStatic xTaskCreateRestrictedStatic + * \ingroup Tasks + */ +#if( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + BaseType_t xTaskCreateRestrictedStatic( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) PRIVILEGED_FUNCTION; +#endif + +/** + * task. h + *
+ void vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * const pxRegions );
+ * + * Memory regions are assigned to a restricted task when the task is created by + * a call to xTaskCreateRestricted(). These regions can be redefined using + * vTaskAllocateMPURegions(). + * + * @param xTask The handle of the task being updated. + * + * @param xRegions A pointer to an MemoryRegion_t structure that contains the + * new memory region definitions. + * + * Example usage: +
+// Define an array of MemoryRegion_t structures that configures an MPU region
+// allowing read/write access for 1024 bytes starting at the beginning of the
+// ucOneKByte array.  The other two of the maximum 3 definable regions are
+// unused so set to zero.
+static const MemoryRegion_t xAltRegions[ portNUM_CONFIGURABLE_REGIONS ] =
+{
+	// Base address		Length		Parameters
+	{ ucOneKByte,		1024,		portMPU_REGION_READ_WRITE },
+	{ 0,				0,			0 },
+	{ 0,				0,			0 }
+};
+
+void vATask( void *pvParameters )
+{
+	// This task was created such that it has access to certain regions of
+	// memory as defined by the MPU configuration.  At some point it is
+	// desired that these MPU regions are replaced with that defined in the
+	// xAltRegions const struct above.  Use a call to vTaskAllocateMPURegions()
+	// for this purpose.  NULL is used as the task handle to indicate that this
+	// function should modify the MPU regions of the calling task.
+	vTaskAllocateMPURegions( NULL, xAltRegions );
+
+	// Now the task can continue its function, but from this point on can only
+	// access its stack and the ucOneKByte array (unless any other statically
+	// defined or shared regions have been declared elsewhere).
+}
+   
+ * \defgroup xTaskCreateRestricted xTaskCreateRestricted + * \ingroup Tasks + */ +void vTaskAllocateMPURegions( TaskHandle_t xTask, const MemoryRegion_t * const pxRegions ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskDelete( TaskHandle_t xTask );
+ * + * INCLUDE_vTaskDelete must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Remove a task from the RTOS real time kernel's management. The task being + * deleted will be removed from all ready, blocked, suspended and event lists. + * + * NOTE: The idle task is responsible for freeing the kernel allocated + * memory from tasks that have been deleted. It is therefore important that + * the idle task is not starved of microcontroller processing time if your + * application makes any calls to vTaskDelete (). Memory allocated by the + * task code is not automatically freed, and should be freed before the task + * is deleted. + * + * See the demo application file death.c for sample code that utilises + * vTaskDelete (). + * + * @param xTask The handle of the task to be deleted. Passing NULL will + * cause the calling task to be deleted. + * + * Example usage: +
+ void vOtherFunction( void )
+ {
+ TaskHandle_t xHandle;
+
+	 // Create the task, storing the handle.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+
+	 // Use the handle to delete the task.
+	 vTaskDelete( xHandle );
+ }
+   
+ * \defgroup vTaskDelete vTaskDelete + * \ingroup Tasks + */ +void vTaskDelete( TaskHandle_t xTaskToDelete ) PRIVILEGED_FUNCTION; + +/*----------------------------------------------------------- + * TASK CONTROL API + *----------------------------------------------------------*/ + +/** + * task. h + *
void vTaskDelay( const TickType_t xTicksToDelay );
+ * + * Delay a task for a given number of ticks. The actual time that the + * task remains blocked depends on the tick rate. The constant + * portTICK_PERIOD_MS can be used to calculate real time from the tick + * rate - with the resolution of one tick period. + * + * INCLUDE_vTaskDelay must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * + * vTaskDelay() specifies a time at which the task wishes to unblock relative to + * the time at which vTaskDelay() is called. For example, specifying a block + * period of 100 ticks will cause the task to unblock 100 ticks after + * vTaskDelay() is called. vTaskDelay() does not therefore provide a good method + * of controlling the frequency of a periodic task as the path taken through the + * code, as well as other task and interrupt activity, will effect the frequency + * at which vTaskDelay() gets called and therefore the time at which the task + * next executes. See vTaskDelayUntil() for an alternative API function designed + * to facilitate fixed frequency execution. It does this by specifying an + * absolute time (rather than a relative time) at which the calling task should + * unblock. + * + * @param xTicksToDelay The amount of time, in tick periods, that + * the calling task should block. + * + * Example usage: + + void vTaskFunction( void * pvParameters ) + { + // Block for 500ms. + const TickType_t xDelay = 500 / portTICK_PERIOD_MS; + + for( ;; ) + { + // Simply toggle the LED every 500ms, blocking between each toggle. + vToggleLED(); + vTaskDelay( xDelay ); + } + } + + * \defgroup vTaskDelay vTaskDelay + * \ingroup TaskCtrl + */ +void vTaskDelay( const TickType_t xTicksToDelay ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskDelayUntil( TickType_t *pxPreviousWakeTime, const TickType_t xTimeIncrement );
+ * + * INCLUDE_vTaskDelayUntil must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Delay a task until a specified time. This function can be used by periodic + * tasks to ensure a constant execution frequency. + * + * This function differs from vTaskDelay () in one important aspect: vTaskDelay () will + * cause a task to block for the specified number of ticks from the time vTaskDelay () is + * called. It is therefore difficult to use vTaskDelay () by itself to generate a fixed + * execution frequency as the time between a task starting to execute and that task + * calling vTaskDelay () may not be fixed [the task may take a different path though the + * code between calls, or may get interrupted or preempted a different number of times + * each time it executes]. + * + * Whereas vTaskDelay () specifies a wake time relative to the time at which the function + * is called, vTaskDelayUntil () specifies the absolute (exact) time at which it wishes to + * unblock. + * + * The constant portTICK_PERIOD_MS can be used to calculate real time from the tick + * rate - with the resolution of one tick period. + * + * @param pxPreviousWakeTime Pointer to a variable that holds the time at which the + * task was last unblocked. The variable must be initialised with the current time + * prior to its first use (see the example below). Following this the variable is + * automatically updated within vTaskDelayUntil (). + * + * @param xTimeIncrement The cycle time period. The task will be unblocked at + * time *pxPreviousWakeTime + xTimeIncrement. Calling vTaskDelayUntil with the + * same xTimeIncrement parameter value will cause the task to execute with + * a fixed interface period. + * + * Example usage: +
+ // Perform an action every 10 ticks.
+ void vTaskFunction( void * pvParameters )
+ {
+ TickType_t xLastWakeTime;
+ const TickType_t xFrequency = 10;
+
+	 // Initialise the xLastWakeTime variable with the current time.
+	 xLastWakeTime = xTaskGetTickCount ();
+	 for( ;; )
+	 {
+		 // Wait for the next cycle.
+		 vTaskDelayUntil( &xLastWakeTime, xFrequency );
+
+		 // Perform action here.
+	 }
+ }
+   
+ * \defgroup vTaskDelayUntil vTaskDelayUntil + * \ingroup TaskCtrl + */ +void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
BaseType_t xTaskAbortDelay( TaskHandle_t xTask );
+ * + * INCLUDE_xTaskAbortDelay must be defined as 1 in FreeRTOSConfig.h for this + * function to be available. + * + * A task will enter the Blocked state when it is waiting for an event. The + * event it is waiting for can be a temporal event (waiting for a time), such + * as when vTaskDelay() is called, or an event on an object, such as when + * xQueueReceive() or ulTaskNotifyTake() is called. If the handle of a task + * that is in the Blocked state is used in a call to xTaskAbortDelay() then the + * task will leave the Blocked state, and return from whichever function call + * placed the task into the Blocked state. + * + * There is no 'FromISR' version of this function as an interrupt would need to + * know which object a task was blocked on in order to know which actions to + * take. For example, if the task was blocked on a queue the interrupt handler + * would then need to know if the queue was locked. + * + * @param xTask The handle of the task to remove from the Blocked state. + * + * @return If the task referenced by xTask was not in the Blocked state then + * pdFAIL is returned. Otherwise pdPASS is returned. + * + * \defgroup xTaskAbortDelay xTaskAbortDelay + * \ingroup TaskCtrl + */ +BaseType_t xTaskAbortDelay( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask );
+ * + * INCLUDE_uxTaskPriorityGet must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Obtain the priority of any task. + * + * @param xTask Handle of the task to be queried. Passing a NULL + * handle results in the priority of the calling task being returned. + * + * @return The priority of xTask. + * + * Example usage: +
+ void vAFunction( void )
+ {
+ TaskHandle_t xHandle;
+
+	 // Create a task, storing the handle.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+
+	 // ...
+
+	 // Use the handle to obtain the priority of the created task.
+	 // It was created with tskIDLE_PRIORITY, but may have changed
+	 // it itself.
+	 if( uxTaskPriorityGet( xHandle ) != tskIDLE_PRIORITY )
+	 {
+		 // The task has changed it's priority.
+	 }
+
+	 // ...
+
+	 // Is our priority higher than the created task?
+	 if( uxTaskPriorityGet( xHandle ) < uxTaskPriorityGet( NULL ) )
+	 {
+		 // Our priority (obtained using NULL handle) is higher.
+	 }
+ }
+   
+ * \defgroup uxTaskPriorityGet uxTaskPriorityGet + * \ingroup TaskCtrl + */ +UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
UBaseType_t uxTaskPriorityGetFromISR( const TaskHandle_t xTask );
+ * + * A version of uxTaskPriorityGet() that can be used from an ISR. + */ +UBaseType_t uxTaskPriorityGetFromISR( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
eTaskState eTaskGetState( TaskHandle_t xTask );
+ * + * INCLUDE_eTaskGetState must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Obtain the state of any task. States are encoded by the eTaskState + * enumerated type. + * + * @param xTask Handle of the task to be queried. + * + * @return The state of xTask at the time the function was called. Note the + * state of the task might change between the function being called, and the + * functions return value being tested by the calling task. + */ +eTaskState eTaskGetState( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskGetInfo( TaskHandle_t xTask, TaskStatus_t *pxTaskStatus, BaseType_t xGetFreeStackSpace, eTaskState eState );
+ * + * configUSE_TRACE_FACILITY must be defined as 1 for this function to be + * available. See the configuration section for more information. + * + * Populates a TaskStatus_t structure with information about a task. + * + * @param xTask Handle of the task being queried. If xTask is NULL then + * information will be returned about the calling task. + * + * @param pxTaskStatus A pointer to the TaskStatus_t structure that will be + * filled with information about the task referenced by the handle passed using + * the xTask parameter. + * + * @xGetFreeStackSpace The TaskStatus_t structure contains a member to report + * the stack high water mark of the task being queried. Calculating the stack + * high water mark takes a relatively long time, and can make the system + * temporarily unresponsive - so the xGetFreeStackSpace parameter is provided to + * allow the high water mark checking to be skipped. The high watermark value + * will only be written to the TaskStatus_t structure if xGetFreeStackSpace is + * not set to pdFALSE; + * + * @param eState The TaskStatus_t structure contains a member to report the + * state of the task being queried. Obtaining the task state is not as fast as + * a simple assignment - so the eState parameter is provided to allow the state + * information to be omitted from the TaskStatus_t structure. To obtain state + * information then set eState to eInvalid - otherwise the value passed in + * eState will be reported as the task state in the TaskStatus_t structure. + * + * Example usage: +
+ void vAFunction( void )
+ {
+ TaskHandle_t xHandle;
+ TaskStatus_t xTaskDetails;
+
+    // Obtain the handle of a task from its name.
+    xHandle = xTaskGetHandle( "Task_Name" );
+
+    // Check the handle is not NULL.
+    configASSERT( xHandle );
+
+    // Use the handle to obtain further information about the task.
+    vTaskGetInfo( xHandle,
+                  &xTaskDetails,
+                  pdTRUE, // Include the high water mark in xTaskDetails.
+                  eInvalid ); // Include the task state in xTaskDetails.
+ }
+   
+ * \defgroup vTaskGetInfo vTaskGetInfo + * \ingroup TaskCtrl + */ +void vTaskGetInfo( TaskHandle_t xTask, TaskStatus_t *pxTaskStatus, BaseType_t xGetFreeStackSpace, eTaskState eState ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority );
+ * + * INCLUDE_vTaskPrioritySet must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Set the priority of any task. + * + * A context switch will occur before the function returns if the priority + * being set is higher than the currently executing task. + * + * @param xTask Handle to the task for which the priority is being set. + * Passing a NULL handle results in the priority of the calling task being set. + * + * @param uxNewPriority The priority to which the task will be set. + * + * Example usage: +
+ void vAFunction( void )
+ {
+ TaskHandle_t xHandle;
+
+	 // Create a task, storing the handle.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+
+	 // ...
+
+	 // Use the handle to raise the priority of the created task.
+	 vTaskPrioritySet( xHandle, tskIDLE_PRIORITY + 1 );
+
+	 // ...
+
+	 // Use a NULL handle to raise our priority to the same value.
+	 vTaskPrioritySet( NULL, tskIDLE_PRIORITY + 1 );
+ }
+   
+ * \defgroup vTaskPrioritySet vTaskPrioritySet + * \ingroup TaskCtrl + */ +void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskSuspend( TaskHandle_t xTaskToSuspend );
+ * + * INCLUDE_vTaskSuspend must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Suspend any task. When suspended a task will never get any microcontroller + * processing time, no matter what its priority. + * + * Calls to vTaskSuspend are not accumulative - + * i.e. calling vTaskSuspend () twice on the same task still only requires one + * call to vTaskResume () to ready the suspended task. + * + * @param xTaskToSuspend Handle to the task being suspended. Passing a NULL + * handle will cause the calling task to be suspended. + * + * Example usage: +
+ void vAFunction( void )
+ {
+ TaskHandle_t xHandle;
+
+	 // Create a task, storing the handle.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+
+	 // ...
+
+	 // Use the handle to suspend the created task.
+	 vTaskSuspend( xHandle );
+
+	 // ...
+
+	 // The created task will not run during this period, unless
+	 // another task calls vTaskResume( xHandle ).
+
+	 //...
+
+
+	 // Suspend ourselves.
+	 vTaskSuspend( NULL );
+
+	 // We cannot get here unless another task calls vTaskResume
+	 // with our handle as the parameter.
+ }
+   
+ * \defgroup vTaskSuspend vTaskSuspend + * \ingroup TaskCtrl + */ +void vTaskSuspend( TaskHandle_t xTaskToSuspend ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskResume( TaskHandle_t xTaskToResume );
+ * + * INCLUDE_vTaskSuspend must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Resumes a suspended task. + * + * A task that has been suspended by one or more calls to vTaskSuspend () + * will be made available for running again by a single call to + * vTaskResume (). + * + * @param xTaskToResume Handle to the task being readied. + * + * Example usage: +
+ void vAFunction( void )
+ {
+ TaskHandle_t xHandle;
+
+	 // Create a task, storing the handle.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+
+	 // ...
+
+	 // Use the handle to suspend the created task.
+	 vTaskSuspend( xHandle );
+
+	 // ...
+
+	 // The created task will not run during this period, unless
+	 // another task calls vTaskResume( xHandle ).
+
+	 //...
+
+
+	 // Resume the suspended task ourselves.
+	 vTaskResume( xHandle );
+
+	 // The created task will once again get microcontroller processing
+	 // time in accordance with its priority within the system.
+ }
+   
+ * \defgroup vTaskResume vTaskResume + * \ingroup TaskCtrl + */ +void vTaskResume( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void xTaskResumeFromISR( TaskHandle_t xTaskToResume );
+ * + * INCLUDE_xTaskResumeFromISR must be defined as 1 for this function to be + * available. See the configuration section for more information. + * + * An implementation of vTaskResume() that can be called from within an ISR. + * + * A task that has been suspended by one or more calls to vTaskSuspend () + * will be made available for running again by a single call to + * xTaskResumeFromISR (). + * + * xTaskResumeFromISR() should not be used to synchronise a task with an + * interrupt if there is a chance that the interrupt could arrive prior to the + * task being suspended - as this can lead to interrupts being missed. Use of a + * semaphore as a synchronisation mechanism would avoid this eventuality. + * + * @param xTaskToResume Handle to the task being readied. + * + * @return pdTRUE if resuming the task should result in a context switch, + * otherwise pdFALSE. This is used by the ISR to determine if a context switch + * may be required following the ISR. + * + * \defgroup vTaskResumeFromISR vTaskResumeFromISR + * \ingroup TaskCtrl + */ +BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) PRIVILEGED_FUNCTION; + +/*----------------------------------------------------------- + * SCHEDULER CONTROL + *----------------------------------------------------------*/ + +/** + * task. h + *
void vTaskStartScheduler( void );
+ * + * Starts the real time kernel tick processing. After calling the kernel + * has control over which tasks are executed and when. + * + * See the demo application file main.c for an example of creating + * tasks and starting the kernel. + * + * Example usage: +
+ void vAFunction( void )
+ {
+	 // Create at least one task before starting the kernel.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
+
+	 // Start the real time kernel with preemption.
+	 vTaskStartScheduler ();
+
+	 // Will not get here unless a task calls vTaskEndScheduler ()
+ }
+   
+ * + * \defgroup vTaskStartScheduler vTaskStartScheduler + * \ingroup SchedulerControl + */ +void vTaskStartScheduler( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskEndScheduler( void );
+ * + * NOTE: At the time of writing only the x86 real mode port, which runs on a PC + * in place of DOS, implements this function. + * + * Stops the real time kernel tick. All created tasks will be automatically + * deleted and multitasking (either preemptive or cooperative) will + * stop. Execution then resumes from the point where vTaskStartScheduler () + * was called, as if vTaskStartScheduler () had just returned. + * + * See the demo application file main. c in the demo/PC directory for an + * example that uses vTaskEndScheduler (). + * + * vTaskEndScheduler () requires an exit function to be defined within the + * portable layer (see vPortEndScheduler () in port. c for the PC port). This + * performs hardware specific operations such as stopping the kernel tick. + * + * vTaskEndScheduler () will cause all of the resources allocated by the + * kernel to be freed - but will not free resources allocated by application + * tasks. + * + * Example usage: +
+ void vTaskCode( void * pvParameters )
+ {
+	 for( ;; )
+	 {
+		 // Task code goes here.
+
+		 // At some point we want to end the real time kernel processing
+		 // so call ...
+		 vTaskEndScheduler ();
+	 }
+ }
+
+ void vAFunction( void )
+ {
+	 // Create at least one task before starting the kernel.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
+
+	 // Start the real time kernel with preemption.
+	 vTaskStartScheduler ();
+
+	 // Will only get here when the vTaskCode () task has called
+	 // vTaskEndScheduler ().  When we get here we are back to single task
+	 // execution.
+ }
+   
+ * + * \defgroup vTaskEndScheduler vTaskEndScheduler + * \ingroup SchedulerControl + */ +void vTaskEndScheduler( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskSuspendAll( void );
+ * + * Suspends the scheduler without disabling interrupts. Context switches will + * not occur while the scheduler is suspended. + * + * After calling vTaskSuspendAll () the calling task will continue to execute + * without risk of being swapped out until a call to xTaskResumeAll () has been + * made. + * + * API functions that have the potential to cause a context switch (for example, + * vTaskDelayUntil(), xQueueSend(), etc.) must not be called while the scheduler + * is suspended. + * + * Example usage: +
+ void vTask1( void * pvParameters )
+ {
+	 for( ;; )
+	 {
+		 // Task code goes here.
+
+		 // ...
+
+		 // At some point the task wants to perform a long operation during
+		 // which it does not want to get swapped out.  It cannot use
+		 // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the
+		 // operation may cause interrupts to be missed - including the
+		 // ticks.
+
+		 // Prevent the real time kernel swapping out the task.
+		 vTaskSuspendAll ();
+
+		 // Perform the operation here.  There is no need to use critical
+		 // sections as we have all the microcontroller processing time.
+		 // During this time interrupts will still operate and the kernel
+		 // tick count will be maintained.
+
+		 // ...
+
+		 // The operation is complete.  Restart the kernel.
+		 xTaskResumeAll ();
+	 }
+ }
+   
+ * \defgroup vTaskSuspendAll vTaskSuspendAll + * \ingroup SchedulerControl + */ +void vTaskSuspendAll( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
BaseType_t xTaskResumeAll( void );
+ * + * Resumes scheduler activity after it was suspended by a call to + * vTaskSuspendAll(). + * + * xTaskResumeAll() only resumes the scheduler. It does not unsuspend tasks + * that were previously suspended by a call to vTaskSuspend(). + * + * @return If resuming the scheduler caused a context switch then pdTRUE is + * returned, otherwise pdFALSE is returned. + * + * Example usage: +
+ void vTask1( void * pvParameters )
+ {
+	 for( ;; )
+	 {
+		 // Task code goes here.
+
+		 // ...
+
+		 // At some point the task wants to perform a long operation during
+		 // which it does not want to get swapped out.  It cannot use
+		 // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the
+		 // operation may cause interrupts to be missed - including the
+		 // ticks.
+
+		 // Prevent the real time kernel swapping out the task.
+		 vTaskSuspendAll ();
+
+		 // Perform the operation here.  There is no need to use critical
+		 // sections as we have all the microcontroller processing time.
+		 // During this time interrupts will still operate and the real
+		 // time kernel tick count will be maintained.
+
+		 // ...
+
+		 // The operation is complete.  Restart the kernel.  We want to force
+		 // a context switch - but there is no point if resuming the scheduler
+		 // caused a context switch already.
+		 if( !xTaskResumeAll () )
+		 {
+			  taskYIELD ();
+		 }
+	 }
+ }
+   
+ * \defgroup xTaskResumeAll xTaskResumeAll + * \ingroup SchedulerControl + */ +BaseType_t xTaskResumeAll( void ) PRIVILEGED_FUNCTION; + +/*----------------------------------------------------------- + * TASK UTILITIES + *----------------------------------------------------------*/ + +/** + * task. h + *
TickType_t xTaskGetTickCount( void );
+ * + * @return The count of ticks since vTaskStartScheduler was called. + * + * \defgroup xTaskGetTickCount xTaskGetTickCount + * \ingroup TaskUtils + */ +TickType_t xTaskGetTickCount( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
TickType_t xTaskGetTickCountFromISR( void );
+ * + * @return The count of ticks since vTaskStartScheduler was called. + * + * This is a version of xTaskGetTickCount() that is safe to be called from an + * ISR - provided that TickType_t is the natural word size of the + * microcontroller being used or interrupt nesting is either not supported or + * not being used. + * + * \defgroup xTaskGetTickCountFromISR xTaskGetTickCountFromISR + * \ingroup TaskUtils + */ +TickType_t xTaskGetTickCountFromISR( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
uint16_t uxTaskGetNumberOfTasks( void );
+ * + * @return The number of tasks that the real time kernel is currently managing. + * This includes all ready, blocked and suspended tasks. A task that + * has been deleted but not yet freed by the idle task will also be + * included in the count. + * + * \defgroup uxTaskGetNumberOfTasks uxTaskGetNumberOfTasks + * \ingroup TaskUtils + */ +UBaseType_t uxTaskGetNumberOfTasks( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
char *pcTaskGetName( TaskHandle_t xTaskToQuery );
+ * + * @return The text (human readable) name of the task referenced by the handle + * xTaskToQuery. A task can query its own name by either passing in its own + * handle, or by setting xTaskToQuery to NULL. + * + * \defgroup pcTaskGetName pcTaskGetName + * \ingroup TaskUtils + */ +char *pcTaskGetName( TaskHandle_t xTaskToQuery ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + +/** + * task. h + *
TaskHandle_t xTaskGetHandle( const char *pcNameToQuery );
+ * + * NOTE: This function takes a relatively long time to complete and should be + * used sparingly. + * + * @return The handle of the task that has the human readable name pcNameToQuery. + * NULL is returned if no matching name is found. INCLUDE_xTaskGetHandle + * must be set to 1 in FreeRTOSConfig.h for pcTaskGetHandle() to be available. + * + * \defgroup pcTaskGetHandle pcTaskGetHandle + * \ingroup TaskUtils + */ +TaskHandle_t xTaskGetHandle( const char *pcNameToQuery ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + +/** + * task.h + *
UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask );
+ * + * INCLUDE_uxTaskGetStackHighWaterMark must be set to 1 in FreeRTOSConfig.h for + * this function to be available. + * + * Returns the high water mark of the stack associated with xTask. That is, + * the minimum free stack space there has been (in words, so on a 32 bit machine + * a value of 1 means 4 bytes) since the task started. The smaller the returned + * number the closer the task has come to overflowing its stack. + * + * uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are the + * same except for their return type. Using configSTACK_DEPTH_TYPE allows the + * user to determine the return type. It gets around the problem of the value + * overflowing on 8-bit types without breaking backward compatibility for + * applications that expect an 8-bit return type. + * + * @param xTask Handle of the task associated with the stack to be checked. + * Set xTask to NULL to check the stack of the calling task. + * + * @return The smallest amount of free stack space there has been (in words, so + * actual spaces on the stack rather than bytes) since the task referenced by + * xTask was created. + */ +UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/** + * task.h + *
configSTACK_DEPTH_TYPE uxTaskGetStackHighWaterMark2( TaskHandle_t xTask );
+ * + * INCLUDE_uxTaskGetStackHighWaterMark2 must be set to 1 in FreeRTOSConfig.h for + * this function to be available. + * + * Returns the high water mark of the stack associated with xTask. That is, + * the minimum free stack space there has been (in words, so on a 32 bit machine + * a value of 1 means 4 bytes) since the task started. The smaller the returned + * number the closer the task has come to overflowing its stack. + * + * uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are the + * same except for their return type. Using configSTACK_DEPTH_TYPE allows the + * user to determine the return type. It gets around the problem of the value + * overflowing on 8-bit types without breaking backward compatibility for + * applications that expect an 8-bit return type. + * + * @param xTask Handle of the task associated with the stack to be checked. + * Set xTask to NULL to check the stack of the calling task. + * + * @return The smallest amount of free stack space there has been (in words, so + * actual spaces on the stack rather than bytes) since the task referenced by + * xTask was created. + */ +configSTACK_DEPTH_TYPE uxTaskGetStackHighWaterMark2( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/* When using trace macros it is sometimes necessary to include task.h before +FreeRTOS.h. When this is done TaskHookFunction_t will not yet have been defined, +so the following two prototypes will cause a compilation error. This can be +fixed by simply guarding against the inclusion of these two prototypes unless +they are explicitly required by the configUSE_APPLICATION_TASK_TAG configuration +constant. */ +#ifdef configUSE_APPLICATION_TASK_TAG + #if configUSE_APPLICATION_TASK_TAG == 1 + /** + * task.h + *
void vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxHookFunction );
+ * + * Sets pxHookFunction to be the task hook function used by the task xTask. + * Passing xTask as NULL has the effect of setting the calling tasks hook + * function. + */ + void vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxHookFunction ) PRIVILEGED_FUNCTION; + + /** + * task.h + *
void xTaskGetApplicationTaskTag( TaskHandle_t xTask );
+ * + * Returns the pxHookFunction value assigned to the task xTask. Do not + * call from an interrupt service routine - call + * xTaskGetApplicationTaskTagFromISR() instead. + */ + TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + + /** + * task.h + *
void xTaskGetApplicationTaskTagFromISR( TaskHandle_t xTask );
+ * + * Returns the pxHookFunction value assigned to the task xTask. Can + * be called from an interrupt service routine. + */ + TaskHookFunction_t xTaskGetApplicationTaskTagFromISR( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + #endif /* configUSE_APPLICATION_TASK_TAG ==1 */ +#endif /* ifdef configUSE_APPLICATION_TASK_TAG */ + +#if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) + + /* Each task contains an array of pointers that is dimensioned by the + configNUM_THREAD_LOCAL_STORAGE_POINTERS setting in FreeRTOSConfig.h. The + kernel does not use the pointers itself, so the application writer can use + the pointers for any purpose they wish. The following two functions are + used to set and query a pointer respectively. */ + void vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet, BaseType_t xIndex, void *pvValue ) PRIVILEGED_FUNCTION; + void *pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery, BaseType_t xIndex ) PRIVILEGED_FUNCTION; + +#endif + +/** + * task.h + *
BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter );
+ * + * Calls the hook function associated with xTask. Passing xTask as NULL has + * the effect of calling the Running tasks (the calling task) hook function. + * + * pvParameter is passed to the hook function for the task to interpret as it + * wants. The return value is the value returned by the task hook function + * registered by the user. + */ +BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter ) PRIVILEGED_FUNCTION; + +/** + * xTaskGetIdleTaskHandle() is only available if + * INCLUDE_xTaskGetIdleTaskHandle is set to 1 in FreeRTOSConfig.h. + * + * Simply returns the handle of the idle task. It is not valid to call + * xTaskGetIdleTaskHandle() before the scheduler has been started. + */ +TaskHandle_t xTaskGetIdleTaskHandle( void ) PRIVILEGED_FUNCTION; + +/** + * configUSE_TRACE_FACILITY must be defined as 1 in FreeRTOSConfig.h for + * uxTaskGetSystemState() to be available. + * + * uxTaskGetSystemState() populates an TaskStatus_t structure for each task in + * the system. TaskStatus_t structures contain, among other things, members + * for the task handle, task name, task priority, task state, and total amount + * of run time consumed by the task. See the TaskStatus_t structure + * definition in this file for the full member list. + * + * NOTE: This function is intended for debugging use only as its use results in + * the scheduler remaining suspended for an extended period. + * + * @param pxTaskStatusArray A pointer to an array of TaskStatus_t structures. + * The array must contain at least one TaskStatus_t structure for each task + * that is under the control of the RTOS. The number of tasks under the control + * of the RTOS can be determined using the uxTaskGetNumberOfTasks() API function. + * + * @param uxArraySize The size of the array pointed to by the pxTaskStatusArray + * parameter. The size is specified as the number of indexes in the array, or + * the number of TaskStatus_t structures contained in the array, not by the + * number of bytes in the array. + * + * @param pulTotalRunTime If configGENERATE_RUN_TIME_STATS is set to 1 in + * FreeRTOSConfig.h then *pulTotalRunTime is set by uxTaskGetSystemState() to the + * total run time (as defined by the run time stats clock, see + * http://www.freertos.org/rtos-run-time-stats.html) since the target booted. + * pulTotalRunTime can be set to NULL to omit the total run time information. + * + * @return The number of TaskStatus_t structures that were populated by + * uxTaskGetSystemState(). This should equal the number returned by the + * uxTaskGetNumberOfTasks() API function, but will be zero if the value passed + * in the uxArraySize parameter was too small. + * + * Example usage: +
+    // This example demonstrates how a human readable table of run time stats
+	// information is generated from raw data provided by uxTaskGetSystemState().
+	// The human readable table is written to pcWriteBuffer
+	void vTaskGetRunTimeStats( char *pcWriteBuffer )
+	{
+	TaskStatus_t *pxTaskStatusArray;
+	volatile UBaseType_t uxArraySize, x;
+	uint32_t ulTotalRunTime, ulStatsAsPercentage;
+
+		// Make sure the write buffer does not contain a string.
+		*pcWriteBuffer = 0x00;
+
+		// Take a snapshot of the number of tasks in case it changes while this
+		// function is executing.
+		uxArraySize = uxTaskGetNumberOfTasks();
+
+		// Allocate a TaskStatus_t structure for each task.  An array could be
+		// allocated statically at compile time.
+		pxTaskStatusArray = pvPortMalloc( uxArraySize * sizeof( TaskStatus_t ) );
+
+		if( pxTaskStatusArray != NULL )
+		{
+			// Generate raw status information about each task.
+			uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, &ulTotalRunTime );
+
+			// For percentage calculations.
+			ulTotalRunTime /= 100UL;
+
+			// Avoid divide by zero errors.
+			if( ulTotalRunTime > 0 )
+			{
+				// For each populated position in the pxTaskStatusArray array,
+				// format the raw data as human readable ASCII data
+				for( x = 0; x < uxArraySize; x++ )
+				{
+					// What percentage of the total run time has the task used?
+					// This will always be rounded down to the nearest integer.
+					// ulTotalRunTimeDiv100 has already been divided by 100.
+					ulStatsAsPercentage = pxTaskStatusArray[ x ].ulRunTimeCounter / ulTotalRunTime;
+
+					if( ulStatsAsPercentage > 0UL )
+					{
+						sprintf( pcWriteBuffer, "%s\t\t%lu\t\t%lu%%\r\n", pxTaskStatusArray[ x ].pcTaskName, pxTaskStatusArray[ x ].ulRunTimeCounter, ulStatsAsPercentage );
+					}
+					else
+					{
+						// If the percentage is zero here then the task has
+						// consumed less than 1% of the total run time.
+						sprintf( pcWriteBuffer, "%s\t\t%lu\t\t<1%%\r\n", pxTaskStatusArray[ x ].pcTaskName, pxTaskStatusArray[ x ].ulRunTimeCounter );
+					}
+
+					pcWriteBuffer += strlen( ( char * ) pcWriteBuffer );
+				}
+			}
+
+			// The array is no longer needed, free the memory it consumes.
+			vPortFree( pxTaskStatusArray );
+		}
+	}
+	
+ */ +UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskList( char *pcWriteBuffer );
+ * + * configUSE_TRACE_FACILITY and configUSE_STATS_FORMATTING_FUNCTIONS must + * both be defined as 1 for this function to be available. See the + * configuration section of the FreeRTOS.org website for more information. + * + * NOTE 1: This function will disable interrupts for its duration. It is + * not intended for normal application runtime use but as a debug aid. + * + * Lists all the current tasks, along with their current state and stack + * usage high water mark. + * + * Tasks are reported as blocked ('B'), ready ('R'), deleted ('D') or + * suspended ('S'). + * + * PLEASE NOTE: + * + * This function is provided for convenience only, and is used by many of the + * demo applications. Do not consider it to be part of the scheduler. + * + * vTaskList() calls uxTaskGetSystemState(), then formats part of the + * uxTaskGetSystemState() output into a human readable table that displays task + * names, states and stack usage. + * + * vTaskList() has a dependency on the sprintf() C library function that might + * bloat the code size, use a lot of stack, and provide different results on + * different platforms. An alternative, tiny, third party, and limited + * functionality implementation of sprintf() is provided in many of the + * FreeRTOS/Demo sub-directories in a file called printf-stdarg.c (note + * printf-stdarg.c does not provide a full snprintf() implementation!). + * + * It is recommended that production systems call uxTaskGetSystemState() + * directly to get access to raw stats data, rather than indirectly through a + * call to vTaskList(). + * + * @param pcWriteBuffer A buffer into which the above mentioned details + * will be written, in ASCII form. This buffer is assumed to be large + * enough to contain the generated report. Approximately 40 bytes per + * task should be sufficient. + * + * \defgroup vTaskList vTaskList + * \ingroup TaskUtils + */ +void vTaskList( char * pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + +/** + * task. h + *
void vTaskGetRunTimeStats( char *pcWriteBuffer );
+ * + * configGENERATE_RUN_TIME_STATS and configUSE_STATS_FORMATTING_FUNCTIONS + * must both be defined as 1 for this function to be available. The application + * must also then provide definitions for + * portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and portGET_RUN_TIME_COUNTER_VALUE() + * to configure a peripheral timer/counter and return the timers current count + * value respectively. The counter should be at least 10 times the frequency of + * the tick count. + * + * NOTE 1: This function will disable interrupts for its duration. It is + * not intended for normal application runtime use but as a debug aid. + * + * Setting configGENERATE_RUN_TIME_STATS to 1 will result in a total + * accumulated execution time being stored for each task. The resolution + * of the accumulated time value depends on the frequency of the timer + * configured by the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() macro. + * Calling vTaskGetRunTimeStats() writes the total execution time of each + * task into a buffer, both as an absolute count value and as a percentage + * of the total system execution time. + * + * NOTE 2: + * + * This function is provided for convenience only, and is used by many of the + * demo applications. Do not consider it to be part of the scheduler. + * + * vTaskGetRunTimeStats() calls uxTaskGetSystemState(), then formats part of the + * uxTaskGetSystemState() output into a human readable table that displays the + * amount of time each task has spent in the Running state in both absolute and + * percentage terms. + * + * vTaskGetRunTimeStats() has a dependency on the sprintf() C library function + * that might bloat the code size, use a lot of stack, and provide different + * results on different platforms. An alternative, tiny, third party, and + * limited functionality implementation of sprintf() is provided in many of the + * FreeRTOS/Demo sub-directories in a file called printf-stdarg.c (note + * printf-stdarg.c does not provide a full snprintf() implementation!). + * + * It is recommended that production systems call uxTaskGetSystemState() directly + * to get access to raw stats data, rather than indirectly through a call to + * vTaskGetRunTimeStats(). + * + * @param pcWriteBuffer A buffer into which the execution times will be + * written, in ASCII form. This buffer is assumed to be large enough to + * contain the generated report. Approximately 40 bytes per task should + * be sufficient. + * + * \defgroup vTaskGetRunTimeStats vTaskGetRunTimeStats + * \ingroup TaskUtils + */ +void vTaskGetRunTimeStats( char *pcWriteBuffer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + +/** +* task. h +*
uint32_t ulTaskGetIdleRunTimeCounter( void );
+* +* configGENERATE_RUN_TIME_STATS and configUSE_STATS_FORMATTING_FUNCTIONS +* must both be defined as 1 for this function to be available. The application +* must also then provide definitions for +* portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and portGET_RUN_TIME_COUNTER_VALUE() +* to configure a peripheral timer/counter and return the timers current count +* value respectively. The counter should be at least 10 times the frequency of +* the tick count. +* +* Setting configGENERATE_RUN_TIME_STATS to 1 will result in a total +* accumulated execution time being stored for each task. The resolution +* of the accumulated time value depends on the frequency of the timer +* configured by the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() macro. +* While uxTaskGetSystemState() and vTaskGetRunTimeStats() writes the total +* execution time of each task into a buffer, ulTaskGetIdleRunTimeCounter() +* returns the total execution time of just the idle task. +* +* @return The total run time of the idle task. This is the amount of time the +* idle task has actually been executing. The unit of time is dependent on the +* frequency configured using the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and +* portGET_RUN_TIME_COUNTER_VALUE() macros. +* +* \defgroup ulTaskGetIdleRunTimeCounter ulTaskGetIdleRunTimeCounter +* \ingroup TaskUtils +*/ +uint32_t ulTaskGetIdleRunTimeCounter( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
BaseType_t xTaskNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction );
+ * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this + * function to be available. + * + * When configUSE_TASK_NOTIFICATIONS is set to one each task has its own private + * "notification value", which is a 32-bit unsigned integer (uint32_t). + * + * Events can be sent to a task using an intermediary object. Examples of such + * objects are queues, semaphores, mutexes and event groups. Task notifications + * are a method of sending an event directly to a task without the need for such + * an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment the task's notification value. In that way + * task notifications can be used to send data to a task, or be used as light + * weight and fast binary or counting semaphores. + * + * A notification sent to a task will remain pending until it is cleared by the + * task calling xTaskNotifyWait() or ulTaskNotifyTake(). If the task was + * already in the Blocked state to wait for a notification when the notification + * arrives then the task will automatically be removed from the Blocked state + * (unblocked) and the notification cleared. + * + * A task can use xTaskNotifyWait() to [optionally] block to wait for a + * notification to be pending, or ulTaskNotifyTake() to [optionally] block + * to wait for its notification value to have a non-zero value. The task does + * not consume any CPU time while it is in the Blocked state. + * + * See http://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * @param xTaskToNotify The handle of the task being notified. The handle to a + * task can be returned from the xTaskCreate() API function used to create the + * task, and the handle of the currently running task can be obtained by calling + * xTaskGetCurrentTaskHandle(). + * + * @param ulValue Data that can be sent with the notification. How the data is + * used depends on the value of the eAction parameter. + * + * @param eAction Specifies how the notification updates the task's notification + * value, if at all. Valid values for eAction are as follows: + * + * eSetBits - + * The task's notification value is bitwise ORed with ulValue. xTaskNofify() + * always returns pdPASS in this case. + * + * eIncrement - + * The task's notification value is incremented. ulValue is not used and + * xTaskNotify() always returns pdPASS in this case. + * + * eSetValueWithOverwrite - + * The task's notification value is set to the value of ulValue, even if the + * task being notified had not yet processed the previous notification (the + * task already had a notification pending). xTaskNotify() always returns + * pdPASS in this case. + * + * eSetValueWithoutOverwrite - + * If the task being notified did not already have a notification pending then + * the task's notification value is set to ulValue and xTaskNotify() will + * return pdPASS. If the task being notified already had a notification + * pending then no action is performed and pdFAIL is returned. + * + * eNoAction - + * The task receives a notification without its notification value being + * updated. ulValue is not used and xTaskNotify() always returns pdPASS in + * this case. + * + * pulPreviousNotificationValue - + * Can be used to pass out the subject task's notification value before any + * bits are modified by the notify function. + * + * @return Dependent on the value of eAction. See the description of the + * eAction parameter. + * + * \defgroup xTaskNotify xTaskNotify + * \ingroup TaskNotifications + */ +BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue ) PRIVILEGED_FUNCTION; +#define xTaskNotify( xTaskToNotify, ulValue, eAction ) xTaskGenericNotify( ( xTaskToNotify ), ( ulValue ), ( eAction ), NULL ) +#define xTaskNotifyAndQuery( xTaskToNotify, ulValue, eAction, pulPreviousNotifyValue ) xTaskGenericNotify( ( xTaskToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotifyValue ) ) + +/** + * task. h + *
BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, BaseType_t *pxHigherPriorityTaskWoken );
+ * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this + * function to be available. + * + * When configUSE_TASK_NOTIFICATIONS is set to one each task has its own private + * "notification value", which is a 32-bit unsigned integer (uint32_t). + * + * A version of xTaskNotify() that can be used from an interrupt service routine + * (ISR). + * + * Events can be sent to a task using an intermediary object. Examples of such + * objects are queues, semaphores, mutexes and event groups. Task notifications + * are a method of sending an event directly to a task without the need for such + * an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment the task's notification value. In that way + * task notifications can be used to send data to a task, or be used as light + * weight and fast binary or counting semaphores. + * + * A notification sent to a task will remain pending until it is cleared by the + * task calling xTaskNotifyWait() or ulTaskNotifyTake(). If the task was + * already in the Blocked state to wait for a notification when the notification + * arrives then the task will automatically be removed from the Blocked state + * (unblocked) and the notification cleared. + * + * A task can use xTaskNotifyWait() to [optionally] block to wait for a + * notification to be pending, or ulTaskNotifyTake() to [optionally] block + * to wait for its notification value to have a non-zero value. The task does + * not consume any CPU time while it is in the Blocked state. + * + * See http://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * @param xTaskToNotify The handle of the task being notified. The handle to a + * task can be returned from the xTaskCreate() API function used to create the + * task, and the handle of the currently running task can be obtained by calling + * xTaskGetCurrentTaskHandle(). + * + * @param ulValue Data that can be sent with the notification. How the data is + * used depends on the value of the eAction parameter. + * + * @param eAction Specifies how the notification updates the task's notification + * value, if at all. Valid values for eAction are as follows: + * + * eSetBits - + * The task's notification value is bitwise ORed with ulValue. xTaskNofify() + * always returns pdPASS in this case. + * + * eIncrement - + * The task's notification value is incremented. ulValue is not used and + * xTaskNotify() always returns pdPASS in this case. + * + * eSetValueWithOverwrite - + * The task's notification value is set to the value of ulValue, even if the + * task being notified had not yet processed the previous notification (the + * task already had a notification pending). xTaskNotify() always returns + * pdPASS in this case. + * + * eSetValueWithoutOverwrite - + * If the task being notified did not already have a notification pending then + * the task's notification value is set to ulValue and xTaskNotify() will + * return pdPASS. If the task being notified already had a notification + * pending then no action is performed and pdFAIL is returned. + * + * eNoAction - + * The task receives a notification without its notification value being + * updated. ulValue is not used and xTaskNotify() always returns pdPASS in + * this case. + * + * @param pxHigherPriorityTaskWoken xTaskNotifyFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending the notification caused the + * task to which the notification was sent to leave the Blocked state, and the + * unblocked task has a priority higher than the currently running task. If + * xTaskNotifyFromISR() sets this value to pdTRUE then a context switch should + * be requested before the interrupt is exited. How a context switch is + * requested from an ISR is dependent on the port - see the documentation page + * for the port in use. + * + * @return Dependent on the value of eAction. See the description of the + * eAction parameter. + * + * \defgroup xTaskNotify xTaskNotify + * \ingroup TaskNotifications + */ +BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue, BaseType_t *pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; +#define xTaskNotifyFromISR( xTaskToNotify, ulValue, eAction, pxHigherPriorityTaskWoken ) xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( ulValue ), ( eAction ), NULL, ( pxHigherPriorityTaskWoken ) ) +#define xTaskNotifyAndQueryFromISR( xTaskToNotify, ulValue, eAction, pulPreviousNotificationValue, pxHigherPriorityTaskWoken ) xTaskGenericNotifyFromISR( ( xTaskToNotify ), ( ulValue ), ( eAction ), ( pulPreviousNotificationValue ), ( pxHigherPriorityTaskWoken ) ) + +/** + * task. h + *
BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait );
+ * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this + * function to be available. + * + * When configUSE_TASK_NOTIFICATIONS is set to one each task has its own private + * "notification value", which is a 32-bit unsigned integer (uint32_t). + * + * Events can be sent to a task using an intermediary object. Examples of such + * objects are queues, semaphores, mutexes and event groups. Task notifications + * are a method of sending an event directly to a task without the need for such + * an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment the task's notification value. In that way + * task notifications can be used to send data to a task, or be used as light + * weight and fast binary or counting semaphores. + * + * A notification sent to a task will remain pending until it is cleared by the + * task calling xTaskNotifyWait() or ulTaskNotifyTake(). If the task was + * already in the Blocked state to wait for a notification when the notification + * arrives then the task will automatically be removed from the Blocked state + * (unblocked) and the notification cleared. + * + * A task can use xTaskNotifyWait() to [optionally] block to wait for a + * notification to be pending, or ulTaskNotifyTake() to [optionally] block + * to wait for its notification value to have a non-zero value. The task does + * not consume any CPU time while it is in the Blocked state. + * + * See http://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * @param ulBitsToClearOnEntry Bits that are set in ulBitsToClearOnEntry value + * will be cleared in the calling task's notification value before the task + * checks to see if any notifications are pending, and optionally blocks if no + * notifications are pending. Setting ulBitsToClearOnEntry to ULONG_MAX (if + * limits.h is included) or 0xffffffffUL (if limits.h is not included) will have + * the effect of resetting the task's notification value to 0. Setting + * ulBitsToClearOnEntry to 0 will leave the task's notification value unchanged. + * + * @param ulBitsToClearOnExit If a notification is pending or received before + * the calling task exits the xTaskNotifyWait() function then the task's + * notification value (see the xTaskNotify() API function) is passed out using + * the pulNotificationValue parameter. Then any bits that are set in + * ulBitsToClearOnExit will be cleared in the task's notification value (note + * *pulNotificationValue is set before any bits are cleared). Setting + * ulBitsToClearOnExit to ULONG_MAX (if limits.h is included) or 0xffffffffUL + * (if limits.h is not included) will have the effect of resetting the task's + * notification value to 0 before the function exits. Setting + * ulBitsToClearOnExit to 0 will leave the task's notification value unchanged + * when the function exits (in which case the value passed out in + * pulNotificationValue will match the task's notification value). + * + * @param pulNotificationValue Used to pass the task's notification value out + * of the function. Note the value passed out will not be effected by the + * clearing of any bits caused by ulBitsToClearOnExit being non-zero. + * + * @param xTicksToWait The maximum amount of time that the task should wait in + * the Blocked state for a notification to be received, should a notification + * not already be pending when xTaskNotifyWait() was called. The task + * will not consume any processing time while it is in the Blocked state. This + * is specified in kernel ticks, the macro pdMS_TO_TICSK( value_in_ms ) can be + * used to convert a time specified in milliseconds to a time specified in + * ticks. + * + * @return If a notification was received (including notifications that were + * already pending when xTaskNotifyWait was called) then pdPASS is + * returned. Otherwise pdFAIL is returned. + * + * \defgroup xTaskNotifyWait xTaskNotifyWait + * \ingroup TaskNotifications + */ +BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
BaseType_t xTaskNotifyGive( TaskHandle_t xTaskToNotify );
+ * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this macro + * to be available. + * + * When configUSE_TASK_NOTIFICATIONS is set to one each task has its own private + * "notification value", which is a 32-bit unsigned integer (uint32_t). + * + * Events can be sent to a task using an intermediary object. Examples of such + * objects are queues, semaphores, mutexes and event groups. Task notifications + * are a method of sending an event directly to a task without the need for such + * an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment the task's notification value. In that way + * task notifications can be used to send data to a task, or be used as light + * weight and fast binary or counting semaphores. + * + * xTaskNotifyGive() is a helper macro intended for use when task notifications + * are used as light weight and faster binary or counting semaphore equivalents. + * Actual FreeRTOS semaphores are given using the xSemaphoreGive() API function, + * the equivalent action that instead uses a task notification is + * xTaskNotifyGive(). + * + * When task notifications are being used as a binary or counting semaphore + * equivalent then the task being notified should wait for the notification + * using the ulTaskNotificationTake() API function rather than the + * xTaskNotifyWait() API function. + * + * See http://www.FreeRTOS.org/RTOS-task-notifications.html for more details. + * + * @param xTaskToNotify The handle of the task being notified. The handle to a + * task can be returned from the xTaskCreate() API function used to create the + * task, and the handle of the currently running task can be obtained by calling + * xTaskGetCurrentTaskHandle(). + * + * @return xTaskNotifyGive() is a macro that calls xTaskNotify() with the + * eAction parameter set to eIncrement - so pdPASS is always returned. + * + * \defgroup xTaskNotifyGive xTaskNotifyGive + * \ingroup TaskNotifications + */ +#define xTaskNotifyGive( xTaskToNotify ) xTaskGenericNotify( ( xTaskToNotify ), ( 0 ), eIncrement, NULL ) + +/** + * task. h + *
void vTaskNotifyGiveFromISR( TaskHandle_t xTaskHandle, BaseType_t *pxHigherPriorityTaskWoken );
+ *
+ * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this macro
+ * to be available.
+ *
+ * When configUSE_TASK_NOTIFICATIONS is set to one each task has its own private
+ * "notification value", which is a 32-bit unsigned integer (uint32_t).
+ *
+ * A version of xTaskNotifyGive() that can be called from an interrupt service
+ * routine (ISR).
+ *
+ * Events can be sent to a task using an intermediary object.  Examples of such
+ * objects are queues, semaphores, mutexes and event groups.  Task notifications
+ * are a method of sending an event directly to a task without the need for such
+ * an intermediary object.
+ *
+ * A notification sent to a task can optionally perform an action, such as
+ * update, overwrite or increment the task's notification value.  In that way
+ * task notifications can be used to send data to a task, or be used as light
+ * weight and fast binary or counting semaphores.
+ *
+ * vTaskNotifyGiveFromISR() is intended for use when task notifications are
+ * used as light weight and faster binary or counting semaphore equivalents.
+ * Actual FreeRTOS semaphores are given from an ISR using the
+ * xSemaphoreGiveFromISR() API function, the equivalent action that instead uses
+ * a task notification is vTaskNotifyGiveFromISR().
+ *
+ * When task notifications are being used as a binary or counting semaphore
+ * equivalent then the task being notified should wait for the notification
+ * using the ulTaskNotificationTake() API function rather than the
+ * xTaskNotifyWait() API function.
+ *
+ * See http://www.FreeRTOS.org/RTOS-task-notifications.html for more details.
+ *
+ * @param xTaskToNotify The handle of the task being notified.  The handle to a
+ * task can be returned from the xTaskCreate() API function used to create the
+ * task, and the handle of the currently running task can be obtained by calling
+ * xTaskGetCurrentTaskHandle().
+ *
+ * @param pxHigherPriorityTaskWoken  vTaskNotifyGiveFromISR() will set
+ * *pxHigherPriorityTaskWoken to pdTRUE if sending the notification caused the
+ * task to which the notification was sent to leave the Blocked state, and the
+ * unblocked task has a priority higher than the currently running task.  If
+ * vTaskNotifyGiveFromISR() sets this value to pdTRUE then a context switch
+ * should be requested before the interrupt is exited.  How a context switch is
+ * requested from an ISR is dependent on the port - see the documentation page
+ * for the port in use.
+ *
+ * \defgroup xTaskNotifyWait xTaskNotifyWait
+ * \ingroup TaskNotifications
+ */
+void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify, BaseType_t *pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION;
+
+/**
+ * task. h
+ * 
uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );
+ * + * configUSE_TASK_NOTIFICATIONS must be undefined or defined as 1 for this + * function to be available. + * + * When configUSE_TASK_NOTIFICATIONS is set to one each task has its own private + * "notification value", which is a 32-bit unsigned integer (uint32_t). + * + * Events can be sent to a task using an intermediary object. Examples of such + * objects are queues, semaphores, mutexes and event groups. Task notifications + * are a method of sending an event directly to a task without the need for such + * an intermediary object. + * + * A notification sent to a task can optionally perform an action, such as + * update, overwrite or increment the task's notification value. In that way + * task notifications can be used to send data to a task, or be used as light + * weight and fast binary or counting semaphores. + * + * ulTaskNotifyTake() is intended for use when a task notification is used as a + * faster and lighter weight binary or counting semaphore alternative. Actual + * FreeRTOS semaphores are taken using the xSemaphoreTake() API function, the + * equivalent action that instead uses a task notification is + * ulTaskNotifyTake(). + * + * When a task is using its notification value as a binary or counting semaphore + * other tasks should send notifications to it using the xTaskNotifyGive() + * macro, or xTaskNotify() function with the eAction parameter set to + * eIncrement. + * + * ulTaskNotifyTake() can either clear the task's notification value to + * zero on exit, in which case the notification value acts like a binary + * semaphore, or decrement the task's notification value on exit, in which case + * the notification value acts like a counting semaphore. + * + * A task can use ulTaskNotifyTake() to [optionally] block to wait for a + * the task's notification value to be non-zero. The task does not consume any + * CPU time while it is in the Blocked state. + * + * Where as xTaskNotifyWait() will return when a notification is pending, + * ulTaskNotifyTake() will return when the task's notification value is + * not zero. + * + * See http://www.FreeRTOS.org/RTOS-task-notifications.html for details. + * + * @param xClearCountOnExit if xClearCountOnExit is pdFALSE then the task's + * notification value is decremented when the function exits. In this way the + * notification value acts like a counting semaphore. If xClearCountOnExit is + * not pdFALSE then the task's notification value is cleared to zero when the + * function exits. In this way the notification value acts like a binary + * semaphore. + * + * @param xTicksToWait The maximum amount of time that the task should wait in + * the Blocked state for the task's notification value to be greater than zero, + * should the count not already be greater than zero when + * ulTaskNotifyTake() was called. The task will not consume any processing + * time while it is in the Blocked state. This is specified in kernel ticks, + * the macro pdMS_TO_TICSK( value_in_ms ) can be used to convert a time + * specified in milliseconds to a time specified in ticks. + * + * @return The task's notification count before it is either cleared to zero or + * decremented (see the xClearCountOnExit parameter). + * + * \defgroup ulTaskNotifyTake ulTaskNotifyTake + * \ingroup TaskNotifications + */ +uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
BaseType_t xTaskNotifyStateClear( TaskHandle_t xTask );
+ * + * If the notification state of the task referenced by the handle xTask is + * eNotified, then set the task's notification state to eNotWaitingNotification. + * The task's notification value is not altered. Set xTask to NULL to clear the + * notification state of the calling task. + * + * @return pdTRUE if the task's notification state was set to + * eNotWaitingNotification, otherwise pdFALSE. + * \defgroup xTaskNotifyStateClear xTaskNotifyStateClear + * \ingroup TaskNotifications + */ +BaseType_t xTaskNotifyStateClear( TaskHandle_t xTask ); + +/** +* task. h +*
uint32_t ulTaskNotifyValueClear( TaskHandle_t xTask, uint32_t ulBitsToClear );
+* +* Clears the bits specified by the ulBitsToClear bit mask in the notification +* value of the task referenced by xTask. +* +* Set ulBitsToClear to 0xffffffff (UINT_MAX on 32-bit architectures) to clear +* the notification value to 0. Set ulBitsToClear to 0 to query the task's +* notification value without clearing any bits. +* +* @return The value of the target task's notification value before the bits +* specified by ulBitsToClear were cleared. +* \defgroup ulTaskNotifyValueClear ulTaskNotifyValueClear +* \ingroup TaskNotifications +*/ +uint32_t ulTaskNotifyValueClear( TaskHandle_t xTask, uint32_t ulBitsToClear ) PRIVILEGED_FUNCTION; + +/** + * task.h + *
void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut )
+ * + * Capture the current time for future use with xTaskCheckForTimeOut(). + * + * @param pxTimeOut Pointer to a timeout object into which the current time + * is to be captured. The captured time includes the tick count and the number + * of times the tick count has overflowed since the system first booted. + * \defgroup vTaskSetTimeOutState vTaskSetTimeOutState + * \ingroup TaskCtrl + */ +void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) PRIVILEGED_FUNCTION; + +/** + * task.h + *
BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait );
+ * + * Determines if pxTicksToWait ticks has passed since a time was captured + * using a call to vTaskSetTimeOutState(). The captured time includes the tick + * count and the number of times the tick count has overflowed. + * + * @param pxTimeOut The time status as captured previously using + * vTaskSetTimeOutState. If the timeout has not yet occurred, it is updated + * to reflect the current time status. + * @param pxTicksToWait The number of ticks to check for timeout i.e. if + * pxTicksToWait ticks have passed since pxTimeOut was last updated (either by + * vTaskSetTimeOutState() or xTaskCheckForTimeOut()), the timeout has occurred. + * If the timeout has not occurred, pxTIcksToWait is updated to reflect the + * number of remaining ticks. + * + * @return If timeout has occurred, pdTRUE is returned. Otherwise pdFALSE is + * returned and pxTicksToWait is updated to reflect the number of remaining + * ticks. + * + * @see https://www.freertos.org/xTaskCheckForTimeOut.html + * + * Example Usage: + *
+	// Driver library function used to receive uxWantedBytes from an Rx buffer
+	// that is filled by a UART interrupt. If there are not enough bytes in the
+	// Rx buffer then the task enters the Blocked state until it is notified that
+	// more data has been placed into the buffer. If there is still not enough
+	// data then the task re-enters the Blocked state, and xTaskCheckForTimeOut()
+	// is used to re-calculate the Block time to ensure the total amount of time
+	// spent in the Blocked state does not exceed MAX_TIME_TO_WAIT. This
+	// continues until either the buffer contains at least uxWantedBytes bytes,
+	// or the total amount of time spent in the Blocked state reaches
+	// MAX_TIME_TO_WAIT – at which point the task reads however many bytes are
+	// available up to a maximum of uxWantedBytes.
+
+	size_t xUART_Receive( uint8_t *pucBuffer, size_t uxWantedBytes )
+	{
+	size_t uxReceived = 0;
+	TickType_t xTicksToWait = MAX_TIME_TO_WAIT;
+	TimeOut_t xTimeOut;
+
+		// Initialize xTimeOut.  This records the time at which this function
+		// was entered.
+		vTaskSetTimeOutState( &xTimeOut );
+
+		// Loop until the buffer contains the wanted number of bytes, or a
+		// timeout occurs.
+		while( UART_bytes_in_rx_buffer( pxUARTInstance ) < uxWantedBytes )
+		{
+			// The buffer didn't contain enough data so this task is going to
+			// enter the Blocked state. Adjusting xTicksToWait to account for
+			// any time that has been spent in the Blocked state within this
+			// function so far to ensure the total amount of time spent in the
+			// Blocked state does not exceed MAX_TIME_TO_WAIT.
+			if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) != pdFALSE )
+			{
+				//Timed out before the wanted number of bytes were available,
+				// exit the loop.
+				break;
+			}
+
+			// Wait for a maximum of xTicksToWait ticks to be notified that the
+			// receive interrupt has placed more data into the buffer.
+			ulTaskNotifyTake( pdTRUE, xTicksToWait );
+		}
+
+		// Attempt to read uxWantedBytes from the receive buffer into pucBuffer.
+		// The actual number of bytes read (which might be less than
+		// uxWantedBytes) is returned.
+		uxReceived = UART_read_from_receive_buffer( pxUARTInstance,
+													pucBuffer,
+													uxWantedBytes );
+
+		return uxReceived;
+	}
+ 
+ * \defgroup xTaskCheckForTimeOut xTaskCheckForTimeOut + * \ingroup TaskCtrl + */ +BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait ) PRIVILEGED_FUNCTION; + +/*----------------------------------------------------------- + * SCHEDULER INTERNALS AVAILABLE FOR PORTING PURPOSES + *----------------------------------------------------------*/ + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS ONLY + * INTENDED FOR USE WHEN IMPLEMENTING A PORT OF THE SCHEDULER AND IS + * AN INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * Called from the real time kernel tick (either preemptive or cooperative), + * this increments the tick count and checks if any tasks that are blocked + * for a finite period required removing from a blocked list and placing on + * a ready list. If a non-zero value is returned then a context switch is + * required because either: + * + A task was removed from a blocked list because its timeout had expired, + * or + * + Time slicing is in use and there is a task of equal priority to the + * currently running task. + */ +BaseType_t xTaskIncrementTick( void ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED. + * + * Removes the calling task from the ready list and places it both + * on the list of tasks waiting for a particular event, and the + * list of delayed tasks. The task will be removed from both lists + * and replaced on the ready list should either the event occur (and + * there be no higher priority tasks waiting on the same event) or + * the delay period expires. + * + * The 'unordered' version replaces the event list item value with the + * xItemValue value, and inserts the list item at the end of the list. + * + * The 'ordered' version uses the existing event list item value (which is the + * owning tasks priority) to insert the list item into the event list is task + * priority order. + * + * @param pxEventList The list containing tasks that are blocked waiting + * for the event to occur. + * + * @param xItemValue The item value to use for the event list item when the + * event list is not ordered by task priority. + * + * @param xTicksToWait The maximum amount of time that the task should wait + * for the event to occur. This is specified in kernel ticks,the constant + * portTICK_PERIOD_MS can be used to convert kernel ticks into a real time + * period. + */ +void vTaskPlaceOnEventList( List_t * const pxEventList, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; +void vTaskPlaceOnUnorderedEventList( List_t * pxEventList, const TickType_t xItemValue, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED. + * + * This function performs nearly the same function as vTaskPlaceOnEventList(). + * The difference being that this function does not permit tasks to block + * indefinitely, whereas vTaskPlaceOnEventList() does. + * + */ +void vTaskPlaceOnEventListRestricted( List_t * const pxEventList, TickType_t xTicksToWait, const BaseType_t xWaitIndefinitely ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED. + * + * Removes a task from both the specified event list and the list of blocked + * tasks, and places it on a ready queue. + * + * xTaskRemoveFromEventList()/vTaskRemoveFromUnorderedEventList() will be called + * if either an event occurs to unblock a task, or the block timeout period + * expires. + * + * xTaskRemoveFromEventList() is used when the event list is in task priority + * order. It removes the list item from the head of the event list as that will + * have the highest priority owning task of all the tasks on the event list. + * vTaskRemoveFromUnorderedEventList() is used when the event list is not + * ordered and the event list items hold something other than the owning tasks + * priority. In this case the event list item value is updated to the value + * passed in the xItemValue parameter. + * + * @return pdTRUE if the task being removed has a higher priority than the task + * making the call, otherwise pdFALSE. + */ +BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList ) PRIVILEGED_FUNCTION; +void vTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, const TickType_t xItemValue ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS ONLY + * INTENDED FOR USE WHEN IMPLEMENTING A PORT OF THE SCHEDULER AND IS + * AN INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * Sets the pointer to the current TCB to the TCB of the highest priority task + * that is ready to run. + */ +portDONT_DISCARD void vTaskSwitchContext( void ) PRIVILEGED_FUNCTION; + +/* + * THESE FUNCTIONS MUST NOT BE USED FROM APPLICATION CODE. THEY ARE USED BY + * THE EVENT BITS MODULE. + */ +TickType_t uxTaskResetEventItemValue( void ) PRIVILEGED_FUNCTION; + +/* + * Return the handle of the calling task. + */ +TaskHandle_t xTaskGetCurrentTaskHandle( void ) PRIVILEGED_FUNCTION; + +/* + * Shortcut used by the queue implementation to prevent unnecessary call to + * taskYIELD(); + */ +void vTaskMissedYield( void ) PRIVILEGED_FUNCTION; + +/* + * Returns the scheduler state as taskSCHEDULER_RUNNING, + * taskSCHEDULER_NOT_STARTED or taskSCHEDULER_SUSPENDED. + */ +BaseType_t xTaskGetSchedulerState( void ) PRIVILEGED_FUNCTION; + +/* + * Raises the priority of the mutex holder to that of the calling task should + * the mutex holder have a priority less than the calling task. + */ +BaseType_t xTaskPriorityInherit( TaskHandle_t const pxMutexHolder ) PRIVILEGED_FUNCTION; + +/* + * Set the priority of a task back to its proper priority in the case that it + * inherited a higher priority while it was holding a semaphore. + */ +BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder ) PRIVILEGED_FUNCTION; + +/* + * If a higher priority task attempting to obtain a mutex caused a lower + * priority task to inherit the higher priority task's priority - but the higher + * priority task then timed out without obtaining the mutex, then the lower + * priority task will disinherit the priority again - but only down as far as + * the highest priority task that is still waiting for the mutex (if there were + * more than one task waiting for the mutex). + */ +void vTaskPriorityDisinheritAfterTimeout( TaskHandle_t const pxMutexHolder, UBaseType_t uxHighestPriorityWaitingTask ) PRIVILEGED_FUNCTION; + +/* + * Get the uxTCBNumber assigned to the task referenced by the xTask parameter. + */ +UBaseType_t uxTaskGetTaskNumber( TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +/* + * Set the uxTaskNumber of the task referenced by the xTask parameter to + * uxHandle. + */ +void vTaskSetTaskNumber( TaskHandle_t xTask, const UBaseType_t uxHandle ) PRIVILEGED_FUNCTION; + +/* + * Only available when configUSE_TICKLESS_IDLE is set to 1. + * If tickless mode is being used, or a low power mode is implemented, then + * the tick interrupt will not execute during idle periods. When this is the + * case, the tick count value maintained by the scheduler needs to be kept up + * to date with the actual execution time by being skipped forward by a time + * equal to the idle period. + */ +void vTaskStepTick( const TickType_t xTicksToJump ) PRIVILEGED_FUNCTION; + +/* Correct the tick count value after the application code has held +interrupts disabled for an extended period. xTicksToCatchUp is the number +of tick interrupts that have been missed due to interrupts being disabled. +Its value is not computed automatically, so must be computed by the +application writer. + +This function is similar to vTaskStepTick(), however, unlike +vTaskStepTick(), xTaskCatchUpTicks() may move the tick count forward past a +time at which a task should be removed from the blocked state. That means +tasks may have to be removed from the blocked state as the tick count is +moved. */ +BaseType_t xTaskCatchUpTicks( TickType_t xTicksToCatchUp ) PRIVILEGED_FUNCTION; + +/* + * Only available when configUSE_TICKLESS_IDLE is set to 1. + * Provided for use within portSUPPRESS_TICKS_AND_SLEEP() to allow the port + * specific sleep function to determine if it is ok to proceed with the sleep, + * and if it is ok to proceed, if it is ok to sleep indefinitely. + * + * This function is necessary because portSUPPRESS_TICKS_AND_SLEEP() is only + * called with the scheduler suspended, not from within a critical section. It + * is therefore possible for an interrupt to request a context switch between + * portSUPPRESS_TICKS_AND_SLEEP() and the low power mode actually being + * entered. eTaskConfirmSleepModeStatus() should be called from a short + * critical section between the timer being stopped and the sleep mode being + * entered to ensure it is ok to proceed into the sleep mode. + */ +eSleepModeStatus eTaskConfirmSleepModeStatus( void ) PRIVILEGED_FUNCTION; + +/* + * For internal use only. Increment the mutex held count when a mutex is + * taken and return the handle of the task that has taken the mutex. + */ +TaskHandle_t pvTaskIncrementMutexHeldCount( void ) PRIVILEGED_FUNCTION; + +/* + * For internal use only. Same as vTaskSetTimeOutState(), but without a critial + * section. + */ +void vTaskInternalSetTimeOutState( TimeOut_t * const pxTimeOut ) PRIVILEGED_FUNCTION; + + +#ifdef __cplusplus +} +#endif +#endif /* INC_TASK_H */ + + + diff --git a/product/rcar/src/CMSIS-FreeRTOS/Source/include/timers.h b/product/rcar/src/CMSIS-FreeRTOS/Source/include/timers.h new file mode 100644 index 000000000..1b6d7f970 --- /dev/null +++ b/product/rcar/src/CMSIS-FreeRTOS/Source/include/timers.h @@ -0,0 +1,1309 @@ +/* + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + + +#ifndef TIMERS_H +#define TIMERS_H + +#ifndef INC_FREERTOS_H + #error "include FreeRTOS.h must appear in source files before include timers.h" +#endif + +/*lint -save -e537 This headers are only multiply included if the application code +happens to also be including task.h. */ +#include "task.h" +/*lint -restore */ + +#ifdef __cplusplus +extern "C" { +#endif + +/*----------------------------------------------------------- + * MACROS AND DEFINITIONS + *----------------------------------------------------------*/ + +/* IDs for commands that can be sent/received on the timer queue. These are to +be used solely through the macros that make up the public software timer API, +as defined below. The commands that are sent from interrupts must use the +highest numbers as tmrFIRST_FROM_ISR_COMMAND is used to determine if the task +or interrupt version of the queue send function should be used. */ +#define tmrCOMMAND_EXECUTE_CALLBACK_FROM_ISR ( ( BaseType_t ) -2 ) +#define tmrCOMMAND_EXECUTE_CALLBACK ( ( BaseType_t ) -1 ) +#define tmrCOMMAND_START_DONT_TRACE ( ( BaseType_t ) 0 ) +#define tmrCOMMAND_START ( ( BaseType_t ) 1 ) +#define tmrCOMMAND_RESET ( ( BaseType_t ) 2 ) +#define tmrCOMMAND_STOP ( ( BaseType_t ) 3 ) +#define tmrCOMMAND_CHANGE_PERIOD ( ( BaseType_t ) 4 ) +#define tmrCOMMAND_DELETE ( ( BaseType_t ) 5 ) + +#define tmrFIRST_FROM_ISR_COMMAND ( ( BaseType_t ) 6 ) +#define tmrCOMMAND_START_FROM_ISR ( ( BaseType_t ) 6 ) +#define tmrCOMMAND_RESET_FROM_ISR ( ( BaseType_t ) 7 ) +#define tmrCOMMAND_STOP_FROM_ISR ( ( BaseType_t ) 8 ) +#define tmrCOMMAND_CHANGE_PERIOD_FROM_ISR ( ( BaseType_t ) 9 ) + + +/** + * Type by which software timers are referenced. For example, a call to + * xTimerCreate() returns an TimerHandle_t variable that can then be used to + * reference the subject timer in calls to other software timer API functions + * (for example, xTimerStart(), xTimerReset(), etc.). + */ +struct tmrTimerControl; /* The old naming convention is used to prevent breaking kernel aware debuggers. */ +typedef struct tmrTimerControl * TimerHandle_t; + +/* + * Defines the prototype to which timer callback functions must conform. + */ +typedef void (*TimerCallbackFunction_t)( TimerHandle_t xTimer ); + +/* + * Defines the prototype to which functions used with the + * xTimerPendFunctionCallFromISR() function must conform. + */ +typedef void (*PendedFunction_t)( void *, uint32_t ); + +/** + * TimerHandle_t xTimerCreate( const char * const pcTimerName, + * TickType_t xTimerPeriodInTicks, + * UBaseType_t uxAutoReload, + * void * pvTimerID, + * TimerCallbackFunction_t pxCallbackFunction ); + * + * Creates a new software timer instance, and returns a handle by which the + * created software timer can be referenced. + * + * Internally, within the FreeRTOS implementation, software timers use a block + * of memory, in which the timer data structure is stored. If a software timer + * is created using xTimerCreate() then the required memory is automatically + * dynamically allocated inside the xTimerCreate() function. (see + * http://www.freertos.org/a00111.html). If a software timer is created using + * xTimerCreateStatic() then the application writer must provide the memory that + * will get used by the software timer. xTimerCreateStatic() therefore allows a + * software timer to be created without using any dynamic memory allocation. + * + * Timers are created in the dormant state. The xTimerStart(), xTimerReset(), + * xTimerStartFromISR(), xTimerResetFromISR(), xTimerChangePeriod() and + * xTimerChangePeriodFromISR() API functions can all be used to transition a + * timer into the active state. + * + * @param pcTimerName A text name that is assigned to the timer. This is done + * purely to assist debugging. The kernel itself only ever references a timer + * by its handle, and never by its name. + * + * @param xTimerPeriodInTicks The timer period. The time is defined in tick + * periods so the constant portTICK_PERIOD_MS can be used to convert a time that + * has been specified in milliseconds. For example, if the timer must expire + * after 100 ticks, then xTimerPeriodInTicks should be set to 100. + * Alternatively, if the timer must expire after 500ms, then xPeriod can be set + * to ( 500 / portTICK_PERIOD_MS ) provided configTICK_RATE_HZ is less than or + * equal to 1000. + * + * @param uxAutoReload If uxAutoReload is set to pdTRUE then the timer will + * expire repeatedly with a frequency set by the xTimerPeriodInTicks parameter. + * If uxAutoReload is set to pdFALSE then the timer will be a one-shot timer and + * enter the dormant state after it expires. + * + * @param pvTimerID An identifier that is assigned to the timer being created. + * Typically this would be used in the timer callback function to identify which + * timer expired when the same callback function is assigned to more than one + * timer. + * + * @param pxCallbackFunction The function to call when the timer expires. + * Callback functions must have the prototype defined by TimerCallbackFunction_t, + * which is "void vCallbackFunction( TimerHandle_t xTimer );". + * + * @return If the timer is successfully created then a handle to the newly + * created timer is returned. If the timer cannot be created (because either + * there is insufficient FreeRTOS heap remaining to allocate the timer + * structures, or the timer period was set to 0) then NULL is returned. + * + * Example usage: + * @verbatim + * #define NUM_TIMERS 5 + * + * // An array to hold handles to the created timers. + * TimerHandle_t xTimers[ NUM_TIMERS ]; + * + * // An array to hold a count of the number of times each timer expires. + * int32_t lExpireCounters[ NUM_TIMERS ] = { 0 }; + * + * // Define a callback function that will be used by multiple timer instances. + * // The callback function does nothing but count the number of times the + * // associated timer expires, and stop the timer once the timer has expired + * // 10 times. + * void vTimerCallback( TimerHandle_t pxTimer ) + * { + * int32_t lArrayIndex; + * const int32_t xMaxExpiryCountBeforeStopping = 10; + * + * // Optionally do something if the pxTimer parameter is NULL. + * configASSERT( pxTimer ); + * + * // Which timer expired? + * lArrayIndex = ( int32_t ) pvTimerGetTimerID( pxTimer ); + * + * // Increment the number of times that pxTimer has expired. + * lExpireCounters[ lArrayIndex ] += 1; + * + * // If the timer has expired 10 times then stop it from running. + * if( lExpireCounters[ lArrayIndex ] == xMaxExpiryCountBeforeStopping ) + * { + * // Do not use a block time if calling a timer API function from a + * // timer callback function, as doing so could cause a deadlock! + * xTimerStop( pxTimer, 0 ); + * } + * } + * + * void main( void ) + * { + * int32_t x; + * + * // Create then start some timers. Starting the timers before the scheduler + * // has been started means the timers will start running immediately that + * // the scheduler starts. + * for( x = 0; x < NUM_TIMERS; x++ ) + * { + * xTimers[ x ] = xTimerCreate( "Timer", // Just a text name, not used by the kernel. + * ( 100 * x ), // The timer period in ticks. + * pdTRUE, // The timers will auto-reload themselves when they expire. + * ( void * ) x, // Assign each timer a unique id equal to its array index. + * vTimerCallback // Each timer calls the same callback when it expires. + * ); + * + * if( xTimers[ x ] == NULL ) + * { + * // The timer was not created. + * } + * else + * { + * // Start the timer. No block time is specified, and even if one was + * // it would be ignored because the scheduler has not yet been + * // started. + * if( xTimerStart( xTimers[ x ], 0 ) != pdPASS ) + * { + * // The timer could not be set into the Active state. + * } + * } + * } + * + * // ... + * // Create tasks here. + * // ... + * + * // Starting the scheduler will start the timers running as they have already + * // been set into the active state. + * vTaskStartScheduler(); + * + * // Should not reach here. + * for( ;; ); + * } + * @endverbatim + */ +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + TimerHandle_t xTimerCreate( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const TickType_t xTimerPeriodInTicks, + const UBaseType_t uxAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction ) PRIVILEGED_FUNCTION; +#endif + +/** + * TimerHandle_t xTimerCreateStatic(const char * const pcTimerName, + * TickType_t xTimerPeriodInTicks, + * UBaseType_t uxAutoReload, + * void * pvTimerID, + * TimerCallbackFunction_t pxCallbackFunction, + * StaticTimer_t *pxTimerBuffer ); + * + * Creates a new software timer instance, and returns a handle by which the + * created software timer can be referenced. + * + * Internally, within the FreeRTOS implementation, software timers use a block + * of memory, in which the timer data structure is stored. If a software timer + * is created using xTimerCreate() then the required memory is automatically + * dynamically allocated inside the xTimerCreate() function. (see + * http://www.freertos.org/a00111.html). If a software timer is created using + * xTimerCreateStatic() then the application writer must provide the memory that + * will get used by the software timer. xTimerCreateStatic() therefore allows a + * software timer to be created without using any dynamic memory allocation. + * + * Timers are created in the dormant state. The xTimerStart(), xTimerReset(), + * xTimerStartFromISR(), xTimerResetFromISR(), xTimerChangePeriod() and + * xTimerChangePeriodFromISR() API functions can all be used to transition a + * timer into the active state. + * + * @param pcTimerName A text name that is assigned to the timer. This is done + * purely to assist debugging. The kernel itself only ever references a timer + * by its handle, and never by its name. + * + * @param xTimerPeriodInTicks The timer period. The time is defined in tick + * periods so the constant portTICK_PERIOD_MS can be used to convert a time that + * has been specified in milliseconds. For example, if the timer must expire + * after 100 ticks, then xTimerPeriodInTicks should be set to 100. + * Alternatively, if the timer must expire after 500ms, then xPeriod can be set + * to ( 500 / portTICK_PERIOD_MS ) provided configTICK_RATE_HZ is less than or + * equal to 1000. + * + * @param uxAutoReload If uxAutoReload is set to pdTRUE then the timer will + * expire repeatedly with a frequency set by the xTimerPeriodInTicks parameter. + * If uxAutoReload is set to pdFALSE then the timer will be a one-shot timer and + * enter the dormant state after it expires. + * + * @param pvTimerID An identifier that is assigned to the timer being created. + * Typically this would be used in the timer callback function to identify which + * timer expired when the same callback function is assigned to more than one + * timer. + * + * @param pxCallbackFunction The function to call when the timer expires. + * Callback functions must have the prototype defined by TimerCallbackFunction_t, + * which is "void vCallbackFunction( TimerHandle_t xTimer );". + * + * @param pxTimerBuffer Must point to a variable of type StaticTimer_t, which + * will be then be used to hold the software timer's data structures, removing + * the need for the memory to be allocated dynamically. + * + * @return If the timer is created then a handle to the created timer is + * returned. If pxTimerBuffer was NULL then NULL is returned. + * + * Example usage: + * @verbatim + * + * // The buffer used to hold the software timer's data structure. + * static StaticTimer_t xTimerBuffer; + * + * // A variable that will be incremented by the software timer's callback + * // function. + * UBaseType_t uxVariableToIncrement = 0; + * + * // A software timer callback function that increments a variable passed to + * // it when the software timer was created. After the 5th increment the + * // callback function stops the software timer. + * static void prvTimerCallback( TimerHandle_t xExpiredTimer ) + * { + * UBaseType_t *puxVariableToIncrement; + * BaseType_t xReturned; + * + * // Obtain the address of the variable to increment from the timer ID. + * puxVariableToIncrement = ( UBaseType_t * ) pvTimerGetTimerID( xExpiredTimer ); + * + * // Increment the variable to show the timer callback has executed. + * ( *puxVariableToIncrement )++; + * + * // If this callback has executed the required number of times, stop the + * // timer. + * if( *puxVariableToIncrement == 5 ) + * { + * // This is called from a timer callback so must not block. + * xTimerStop( xExpiredTimer, staticDONT_BLOCK ); + * } + * } + * + * + * void main( void ) + * { + * // Create the software time. xTimerCreateStatic() has an extra parameter + * // than the normal xTimerCreate() API function. The parameter is a pointer + * // to the StaticTimer_t structure that will hold the software timer + * // structure. If the parameter is passed as NULL then the structure will be + * // allocated dynamically, just as if xTimerCreate() had been called. + * xTimer = xTimerCreateStatic( "T1", // Text name for the task. Helps debugging only. Not used by FreeRTOS. + * xTimerPeriod, // The period of the timer in ticks. + * pdTRUE, // This is an auto-reload timer. + * ( void * ) &uxVariableToIncrement, // A variable incremented by the software timer's callback function + * prvTimerCallback, // The function to execute when the timer expires. + * &xTimerBuffer ); // The buffer that will hold the software timer structure. + * + * // The scheduler has not started yet so a block time is not used. + * xReturned = xTimerStart( xTimer, 0 ); + * + * // ... + * // Create tasks here. + * // ... + * + * // Starting the scheduler will start the timers running as they have already + * // been set into the active state. + * vTaskStartScheduler(); + * + * // Should not reach here. + * for( ;; ); + * } + * @endverbatim + */ +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + TimerHandle_t xTimerCreateStatic( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const TickType_t xTimerPeriodInTicks, + const UBaseType_t uxAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction, + StaticTimer_t *pxTimerBuffer ) PRIVILEGED_FUNCTION; +#endif /* configSUPPORT_STATIC_ALLOCATION */ + +/** + * void *pvTimerGetTimerID( TimerHandle_t xTimer ); + * + * Returns the ID assigned to the timer. + * + * IDs are assigned to timers using the pvTimerID parameter of the call to + * xTimerCreated() that was used to create the timer, and by calling the + * vTimerSetTimerID() API function. + * + * If the same callback function is assigned to multiple timers then the timer + * ID can be used as time specific (timer local) storage. + * + * @param xTimer The timer being queried. + * + * @return The ID assigned to the timer being queried. + * + * Example usage: + * + * See the xTimerCreate() API function example usage scenario. + */ +void *pvTimerGetTimerID( const TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; + +/** + * void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ); + * + * Sets the ID assigned to the timer. + * + * IDs are assigned to timers using the pvTimerID parameter of the call to + * xTimerCreated() that was used to create the timer. + * + * If the same callback function is assigned to multiple timers then the timer + * ID can be used as time specific (timer local) storage. + * + * @param xTimer The timer being updated. + * + * @param pvNewID The ID to assign to the timer. + * + * Example usage: + * + * See the xTimerCreate() API function example usage scenario. + */ +void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) PRIVILEGED_FUNCTION; + +/** + * BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ); + * + * Queries a timer to see if it is active or dormant. + * + * A timer will be dormant if: + * 1) It has been created but not started, or + * 2) It is an expired one-shot timer that has not been restarted. + * + * Timers are created in the dormant state. The xTimerStart(), xTimerReset(), + * xTimerStartFromISR(), xTimerResetFromISR(), xTimerChangePeriod() and + * xTimerChangePeriodFromISR() API functions can all be used to transition a timer into the + * active state. + * + * @param xTimer The timer being queried. + * + * @return pdFALSE will be returned if the timer is dormant. A value other than + * pdFALSE will be returned if the timer is active. + * + * Example usage: + * @verbatim + * // This function assumes xTimer has already been created. + * void vAFunction( TimerHandle_t xTimer ) + * { + * if( xTimerIsTimerActive( xTimer ) != pdFALSE ) // or more simply and equivalently "if( xTimerIsTimerActive( xTimer ) )" + * { + * // xTimer is active, do something. + * } + * else + * { + * // xTimer is not active, do something else. + * } + * } + * @endverbatim + */ +BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; + +/** + * TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ); + * + * Simply returns the handle of the timer service/daemon task. It it not valid + * to call xTimerGetTimerDaemonTaskHandle() before the scheduler has been started. + */ +TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ) PRIVILEGED_FUNCTION; + +/** + * BaseType_t xTimerStart( TimerHandle_t xTimer, TickType_t xTicksToWait ); + * + * Timer functionality is provided by a timer service/daemon task. Many of the + * public FreeRTOS timer API functions send commands to the timer service task + * through a queue called the timer command queue. The timer command queue is + * private to the kernel itself and is not directly accessible to application + * code. The length of the timer command queue is set by the + * configTIMER_QUEUE_LENGTH configuration constant. + * + * xTimerStart() starts a timer that was previously created using the + * xTimerCreate() API function. If the timer had already been started and was + * already in the active state, then xTimerStart() has equivalent functionality + * to the xTimerReset() API function. + * + * Starting a timer ensures the timer is in the active state. If the timer + * is not stopped, deleted, or reset in the mean time, the callback function + * associated with the timer will get called 'n' ticks after xTimerStart() was + * called, where 'n' is the timers defined period. + * + * It is valid to call xTimerStart() before the scheduler has been started, but + * when this is done the timer will not actually start until the scheduler is + * started, and the timers expiry time will be relative to when the scheduler is + * started, not relative to when xTimerStart() was called. + * + * The configUSE_TIMERS configuration constant must be set to 1 for xTimerStart() + * to be available. + * + * @param xTimer The handle of the timer being started/restarted. + * + * @param xTicksToWait Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the start command to be successfully + * sent to the timer command queue, should the queue already be full when + * xTimerStart() was called. xTicksToWait is ignored if xTimerStart() is called + * before the scheduler is started. + * + * @return pdFAIL will be returned if the start command could not be sent to + * the timer command queue even after xTicksToWait ticks had passed. pdPASS will + * be returned if the command was successfully sent to the timer command queue. + * When the command is actually processed will depend on the priority of the + * timer service/daemon task relative to other tasks in the system, although the + * timers expiry time is relative to when xTimerStart() is actually called. The + * timer service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * + * Example usage: + * + * See the xTimerCreate() API function example usage scenario. + * + */ +#define xTimerStart( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) ) + +/** + * BaseType_t xTimerStop( TimerHandle_t xTimer, TickType_t xTicksToWait ); + * + * Timer functionality is provided by a timer service/daemon task. Many of the + * public FreeRTOS timer API functions send commands to the timer service task + * through a queue called the timer command queue. The timer command queue is + * private to the kernel itself and is not directly accessible to application + * code. The length of the timer command queue is set by the + * configTIMER_QUEUE_LENGTH configuration constant. + * + * xTimerStop() stops a timer that was previously started using either of the + * The xTimerStart(), xTimerReset(), xTimerStartFromISR(), xTimerResetFromISR(), + * xTimerChangePeriod() or xTimerChangePeriodFromISR() API functions. + * + * Stopping a timer ensures the timer is not in the active state. + * + * The configUSE_TIMERS configuration constant must be set to 1 for xTimerStop() + * to be available. + * + * @param xTimer The handle of the timer being stopped. + * + * @param xTicksToWait Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the stop command to be successfully + * sent to the timer command queue, should the queue already be full when + * xTimerStop() was called. xTicksToWait is ignored if xTimerStop() is called + * before the scheduler is started. + * + * @return pdFAIL will be returned if the stop command could not be sent to + * the timer command queue even after xTicksToWait ticks had passed. pdPASS will + * be returned if the command was successfully sent to the timer command queue. + * When the command is actually processed will depend on the priority of the + * timer service/daemon task relative to other tasks in the system. The timer + * service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * + * Example usage: + * + * See the xTimerCreate() API function example usage scenario. + * + */ +#define xTimerStop( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP, 0U, NULL, ( xTicksToWait ) ) + +/** + * BaseType_t xTimerChangePeriod( TimerHandle_t xTimer, + * TickType_t xNewPeriod, + * TickType_t xTicksToWait ); + * + * Timer functionality is provided by a timer service/daemon task. Many of the + * public FreeRTOS timer API functions send commands to the timer service task + * through a queue called the timer command queue. The timer command queue is + * private to the kernel itself and is not directly accessible to application + * code. The length of the timer command queue is set by the + * configTIMER_QUEUE_LENGTH configuration constant. + * + * xTimerChangePeriod() changes the period of a timer that was previously + * created using the xTimerCreate() API function. + * + * xTimerChangePeriod() can be called to change the period of an active or + * dormant state timer. + * + * The configUSE_TIMERS configuration constant must be set to 1 for + * xTimerChangePeriod() to be available. + * + * @param xTimer The handle of the timer that is having its period changed. + * + * @param xNewPeriod The new period for xTimer. Timer periods are specified in + * tick periods, so the constant portTICK_PERIOD_MS can be used to convert a time + * that has been specified in milliseconds. For example, if the timer must + * expire after 100 ticks, then xNewPeriod should be set to 100. Alternatively, + * if the timer must expire after 500ms, then xNewPeriod can be set to + * ( 500 / portTICK_PERIOD_MS ) provided configTICK_RATE_HZ is less than + * or equal to 1000. + * + * @param xTicksToWait Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the change period command to be + * successfully sent to the timer command queue, should the queue already be + * full when xTimerChangePeriod() was called. xTicksToWait is ignored if + * xTimerChangePeriod() is called before the scheduler is started. + * + * @return pdFAIL will be returned if the change period command could not be + * sent to the timer command queue even after xTicksToWait ticks had passed. + * pdPASS will be returned if the command was successfully sent to the timer + * command queue. When the command is actually processed will depend on the + * priority of the timer service/daemon task relative to other tasks in the + * system. The timer service/daemon task priority is set by the + * configTIMER_TASK_PRIORITY configuration constant. + * + * Example usage: + * @verbatim + * // This function assumes xTimer has already been created. If the timer + * // referenced by xTimer is already active when it is called, then the timer + * // is deleted. If the timer referenced by xTimer is not active when it is + * // called, then the period of the timer is set to 500ms and the timer is + * // started. + * void vAFunction( TimerHandle_t xTimer ) + * { + * if( xTimerIsTimerActive( xTimer ) != pdFALSE ) // or more simply and equivalently "if( xTimerIsTimerActive( xTimer ) )" + * { + * // xTimer is already active - delete it. + * xTimerDelete( xTimer ); + * } + * else + * { + * // xTimer is not active, change its period to 500ms. This will also + * // cause the timer to start. Block for a maximum of 100 ticks if the + * // change period command cannot immediately be sent to the timer + * // command queue. + * if( xTimerChangePeriod( xTimer, 500 / portTICK_PERIOD_MS, 100 ) == pdPASS ) + * { + * // The command was successfully sent. + * } + * else + * { + * // The command could not be sent, even after waiting for 100 ticks + * // to pass. Take appropriate action here. + * } + * } + * } + * @endverbatim + */ + #define xTimerChangePeriod( xTimer, xNewPeriod, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_CHANGE_PERIOD, ( xNewPeriod ), NULL, ( xTicksToWait ) ) + +/** + * BaseType_t xTimerDelete( TimerHandle_t xTimer, TickType_t xTicksToWait ); + * + * Timer functionality is provided by a timer service/daemon task. Many of the + * public FreeRTOS timer API functions send commands to the timer service task + * through a queue called the timer command queue. The timer command queue is + * private to the kernel itself and is not directly accessible to application + * code. The length of the timer command queue is set by the + * configTIMER_QUEUE_LENGTH configuration constant. + * + * xTimerDelete() deletes a timer that was previously created using the + * xTimerCreate() API function. + * + * The configUSE_TIMERS configuration constant must be set to 1 for + * xTimerDelete() to be available. + * + * @param xTimer The handle of the timer being deleted. + * + * @param xTicksToWait Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the delete command to be + * successfully sent to the timer command queue, should the queue already be + * full when xTimerDelete() was called. xTicksToWait is ignored if xTimerDelete() + * is called before the scheduler is started. + * + * @return pdFAIL will be returned if the delete command could not be sent to + * the timer command queue even after xTicksToWait ticks had passed. pdPASS will + * be returned if the command was successfully sent to the timer command queue. + * When the command is actually processed will depend on the priority of the + * timer service/daemon task relative to other tasks in the system. The timer + * service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * + * Example usage: + * + * See the xTimerChangePeriod() API function example usage scenario. + */ +#define xTimerDelete( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_DELETE, 0U, NULL, ( xTicksToWait ) ) + +/** + * BaseType_t xTimerReset( TimerHandle_t xTimer, TickType_t xTicksToWait ); + * + * Timer functionality is provided by a timer service/daemon task. Many of the + * public FreeRTOS timer API functions send commands to the timer service task + * through a queue called the timer command queue. The timer command queue is + * private to the kernel itself and is not directly accessible to application + * code. The length of the timer command queue is set by the + * configTIMER_QUEUE_LENGTH configuration constant. + * + * xTimerReset() re-starts a timer that was previously created using the + * xTimerCreate() API function. If the timer had already been started and was + * already in the active state, then xTimerReset() will cause the timer to + * re-evaluate its expiry time so that it is relative to when xTimerReset() was + * called. If the timer was in the dormant state then xTimerReset() has + * equivalent functionality to the xTimerStart() API function. + * + * Resetting a timer ensures the timer is in the active state. If the timer + * is not stopped, deleted, or reset in the mean time, the callback function + * associated with the timer will get called 'n' ticks after xTimerReset() was + * called, where 'n' is the timers defined period. + * + * It is valid to call xTimerReset() before the scheduler has been started, but + * when this is done the timer will not actually start until the scheduler is + * started, and the timers expiry time will be relative to when the scheduler is + * started, not relative to when xTimerReset() was called. + * + * The configUSE_TIMERS configuration constant must be set to 1 for xTimerReset() + * to be available. + * + * @param xTimer The handle of the timer being reset/started/restarted. + * + * @param xTicksToWait Specifies the time, in ticks, that the calling task should + * be held in the Blocked state to wait for the reset command to be successfully + * sent to the timer command queue, should the queue already be full when + * xTimerReset() was called. xTicksToWait is ignored if xTimerReset() is called + * before the scheduler is started. + * + * @return pdFAIL will be returned if the reset command could not be sent to + * the timer command queue even after xTicksToWait ticks had passed. pdPASS will + * be returned if the command was successfully sent to the timer command queue. + * When the command is actually processed will depend on the priority of the + * timer service/daemon task relative to other tasks in the system, although the + * timers expiry time is relative to when xTimerStart() is actually called. The + * timer service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * + * Example usage: + * @verbatim + * // When a key is pressed, an LCD back-light is switched on. If 5 seconds pass + * // without a key being pressed, then the LCD back-light is switched off. In + * // this case, the timer is a one-shot timer. + * + * TimerHandle_t xBacklightTimer = NULL; + * + * // The callback function assigned to the one-shot timer. In this case the + * // parameter is not used. + * void vBacklightTimerCallback( TimerHandle_t pxTimer ) + * { + * // The timer expired, therefore 5 seconds must have passed since a key + * // was pressed. Switch off the LCD back-light. + * vSetBacklightState( BACKLIGHT_OFF ); + * } + * + * // The key press event handler. + * void vKeyPressEventHandler( char cKey ) + * { + * // Ensure the LCD back-light is on, then reset the timer that is + * // responsible for turning the back-light off after 5 seconds of + * // key inactivity. Wait 10 ticks for the command to be successfully sent + * // if it cannot be sent immediately. + * vSetBacklightState( BACKLIGHT_ON ); + * if( xTimerReset( xBacklightTimer, 100 ) != pdPASS ) + * { + * // The reset command was not executed successfully. Take appropriate + * // action here. + * } + * + * // Perform the rest of the key processing here. + * } + * + * void main( void ) + * { + * int32_t x; + * + * // Create then start the one-shot timer that is responsible for turning + * // the back-light off if no keys are pressed within a 5 second period. + * xBacklightTimer = xTimerCreate( "BacklightTimer", // Just a text name, not used by the kernel. + * ( 5000 / portTICK_PERIOD_MS), // The timer period in ticks. + * pdFALSE, // The timer is a one-shot timer. + * 0, // The id is not used by the callback so can take any value. + * vBacklightTimerCallback // The callback function that switches the LCD back-light off. + * ); + * + * if( xBacklightTimer == NULL ) + * { + * // The timer was not created. + * } + * else + * { + * // Start the timer. No block time is specified, and even if one was + * // it would be ignored because the scheduler has not yet been + * // started. + * if( xTimerStart( xBacklightTimer, 0 ) != pdPASS ) + * { + * // The timer could not be set into the Active state. + * } + * } + * + * // ... + * // Create tasks here. + * // ... + * + * // Starting the scheduler will start the timer running as it has already + * // been set into the active state. + * vTaskStartScheduler(); + * + * // Should not reach here. + * for( ;; ); + * } + * @endverbatim + */ +#define xTimerReset( xTimer, xTicksToWait ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET, ( xTaskGetTickCount() ), NULL, ( xTicksToWait ) ) + +/** + * BaseType_t xTimerStartFromISR( TimerHandle_t xTimer, + * BaseType_t *pxHigherPriorityTaskWoken ); + * + * A version of xTimerStart() that can be called from an interrupt service + * routine. + * + * @param xTimer The handle of the timer being started/restarted. + * + * @param pxHigherPriorityTaskWoken The timer service/daemon task spends most + * of its time in the Blocked state, waiting for messages to arrive on the timer + * command queue. Calling xTimerStartFromISR() writes a message to the timer + * command queue, so has the potential to transition the timer service/daemon + * task out of the Blocked state. If calling xTimerStartFromISR() causes the + * timer service/daemon task to leave the Blocked state, and the timer service/ + * daemon task has a priority equal to or greater than the currently executing + * task (the task that was interrupted), then *pxHigherPriorityTaskWoken will + * get set to pdTRUE internally within the xTimerStartFromISR() function. If + * xTimerStartFromISR() sets this value to pdTRUE then a context switch should + * be performed before the interrupt exits. + * + * @return pdFAIL will be returned if the start command could not be sent to + * the timer command queue. pdPASS will be returned if the command was + * successfully sent to the timer command queue. When the command is actually + * processed will depend on the priority of the timer service/daemon task + * relative to other tasks in the system, although the timers expiry time is + * relative to when xTimerStartFromISR() is actually called. The timer + * service/daemon task priority is set by the configTIMER_TASK_PRIORITY + * configuration constant. + * + * Example usage: + * @verbatim + * // This scenario assumes xBacklightTimer has already been created. When a + * // key is pressed, an LCD back-light is switched on. If 5 seconds pass + * // without a key being pressed, then the LCD back-light is switched off. In + * // this case, the timer is a one-shot timer, and unlike the example given for + * // the xTimerReset() function, the key press event handler is an interrupt + * // service routine. + * + * // The callback function assigned to the one-shot timer. In this case the + * // parameter is not used. + * void vBacklightTimerCallback( TimerHandle_t pxTimer ) + * { + * // The timer expired, therefore 5 seconds must have passed since a key + * // was pressed. Switch off the LCD back-light. + * vSetBacklightState( BACKLIGHT_OFF ); + * } + * + * // The key press interrupt service routine. + * void vKeyPressEventInterruptHandler( void ) + * { + * BaseType_t xHigherPriorityTaskWoken = pdFALSE; + * + * // Ensure the LCD back-light is on, then restart the timer that is + * // responsible for turning the back-light off after 5 seconds of + * // key inactivity. This is an interrupt service routine so can only + * // call FreeRTOS API functions that end in "FromISR". + * vSetBacklightState( BACKLIGHT_ON ); + * + * // xTimerStartFromISR() or xTimerResetFromISR() could be called here + * // as both cause the timer to re-calculate its expiry time. + * // xHigherPriorityTaskWoken was initialised to pdFALSE when it was + * // declared (in this function). + * if( xTimerStartFromISR( xBacklightTimer, &xHigherPriorityTaskWoken ) != pdPASS ) + * { + * // The start command was not executed successfully. Take appropriate + * // action here. + * } + * + * // Perform the rest of the key processing here. + * + * // If xHigherPriorityTaskWoken equals pdTRUE, then a context switch + * // should be performed. The syntax required to perform a context switch + * // from inside an ISR varies from port to port, and from compiler to + * // compiler. Inspect the demos for the port you are using to find the + * // actual syntax required. + * if( xHigherPriorityTaskWoken != pdFALSE ) + * { + * // Call the interrupt safe yield function here (actual function + * // depends on the FreeRTOS port being used). + * } + * } + * @endverbatim + */ +#define xTimerStartFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_START_FROM_ISR, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken ), 0U ) + +/** + * BaseType_t xTimerStopFromISR( TimerHandle_t xTimer, + * BaseType_t *pxHigherPriorityTaskWoken ); + * + * A version of xTimerStop() that can be called from an interrupt service + * routine. + * + * @param xTimer The handle of the timer being stopped. + * + * @param pxHigherPriorityTaskWoken The timer service/daemon task spends most + * of its time in the Blocked state, waiting for messages to arrive on the timer + * command queue. Calling xTimerStopFromISR() writes a message to the timer + * command queue, so has the potential to transition the timer service/daemon + * task out of the Blocked state. If calling xTimerStopFromISR() causes the + * timer service/daemon task to leave the Blocked state, and the timer service/ + * daemon task has a priority equal to or greater than the currently executing + * task (the task that was interrupted), then *pxHigherPriorityTaskWoken will + * get set to pdTRUE internally within the xTimerStopFromISR() function. If + * xTimerStopFromISR() sets this value to pdTRUE then a context switch should + * be performed before the interrupt exits. + * + * @return pdFAIL will be returned if the stop command could not be sent to + * the timer command queue. pdPASS will be returned if the command was + * successfully sent to the timer command queue. When the command is actually + * processed will depend on the priority of the timer service/daemon task + * relative to other tasks in the system. The timer service/daemon task + * priority is set by the configTIMER_TASK_PRIORITY configuration constant. + * + * Example usage: + * @verbatim + * // This scenario assumes xTimer has already been created and started. When + * // an interrupt occurs, the timer should be simply stopped. + * + * // The interrupt service routine that stops the timer. + * void vAnExampleInterruptServiceRoutine( void ) + * { + * BaseType_t xHigherPriorityTaskWoken = pdFALSE; + * + * // The interrupt has occurred - simply stop the timer. + * // xHigherPriorityTaskWoken was set to pdFALSE where it was defined + * // (within this function). As this is an interrupt service routine, only + * // FreeRTOS API functions that end in "FromISR" can be used. + * if( xTimerStopFromISR( xTimer, &xHigherPriorityTaskWoken ) != pdPASS ) + * { + * // The stop command was not executed successfully. Take appropriate + * // action here. + * } + * + * // If xHigherPriorityTaskWoken equals pdTRUE, then a context switch + * // should be performed. The syntax required to perform a context switch + * // from inside an ISR varies from port to port, and from compiler to + * // compiler. Inspect the demos for the port you are using to find the + * // actual syntax required. + * if( xHigherPriorityTaskWoken != pdFALSE ) + * { + * // Call the interrupt safe yield function here (actual function + * // depends on the FreeRTOS port being used). + * } + * } + * @endverbatim + */ +#define xTimerStopFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_STOP_FROM_ISR, 0, ( pxHigherPriorityTaskWoken ), 0U ) + +/** + * BaseType_t xTimerChangePeriodFromISR( TimerHandle_t xTimer, + * TickType_t xNewPeriod, + * BaseType_t *pxHigherPriorityTaskWoken ); + * + * A version of xTimerChangePeriod() that can be called from an interrupt + * service routine. + * + * @param xTimer The handle of the timer that is having its period changed. + * + * @param xNewPeriod The new period for xTimer. Timer periods are specified in + * tick periods, so the constant portTICK_PERIOD_MS can be used to convert a time + * that has been specified in milliseconds. For example, if the timer must + * expire after 100 ticks, then xNewPeriod should be set to 100. Alternatively, + * if the timer must expire after 500ms, then xNewPeriod can be set to + * ( 500 / portTICK_PERIOD_MS ) provided configTICK_RATE_HZ is less than + * or equal to 1000. + * + * @param pxHigherPriorityTaskWoken The timer service/daemon task spends most + * of its time in the Blocked state, waiting for messages to arrive on the timer + * command queue. Calling xTimerChangePeriodFromISR() writes a message to the + * timer command queue, so has the potential to transition the timer service/ + * daemon task out of the Blocked state. If calling xTimerChangePeriodFromISR() + * causes the timer service/daemon task to leave the Blocked state, and the + * timer service/daemon task has a priority equal to or greater than the + * currently executing task (the task that was interrupted), then + * *pxHigherPriorityTaskWoken will get set to pdTRUE internally within the + * xTimerChangePeriodFromISR() function. If xTimerChangePeriodFromISR() sets + * this value to pdTRUE then a context switch should be performed before the + * interrupt exits. + * + * @return pdFAIL will be returned if the command to change the timers period + * could not be sent to the timer command queue. pdPASS will be returned if the + * command was successfully sent to the timer command queue. When the command + * is actually processed will depend on the priority of the timer service/daemon + * task relative to other tasks in the system. The timer service/daemon task + * priority is set by the configTIMER_TASK_PRIORITY configuration constant. + * + * Example usage: + * @verbatim + * // This scenario assumes xTimer has already been created and started. When + * // an interrupt occurs, the period of xTimer should be changed to 500ms. + * + * // The interrupt service routine that changes the period of xTimer. + * void vAnExampleInterruptServiceRoutine( void ) + * { + * BaseType_t xHigherPriorityTaskWoken = pdFALSE; + * + * // The interrupt has occurred - change the period of xTimer to 500ms. + * // xHigherPriorityTaskWoken was set to pdFALSE where it was defined + * // (within this function). As this is an interrupt service routine, only + * // FreeRTOS API functions that end in "FromISR" can be used. + * if( xTimerChangePeriodFromISR( xTimer, &xHigherPriorityTaskWoken ) != pdPASS ) + * { + * // The command to change the timers period was not executed + * // successfully. Take appropriate action here. + * } + * + * // If xHigherPriorityTaskWoken equals pdTRUE, then a context switch + * // should be performed. The syntax required to perform a context switch + * // from inside an ISR varies from port to port, and from compiler to + * // compiler. Inspect the demos for the port you are using to find the + * // actual syntax required. + * if( xHigherPriorityTaskWoken != pdFALSE ) + * { + * // Call the interrupt safe yield function here (actual function + * // depends on the FreeRTOS port being used). + * } + * } + * @endverbatim + */ +#define xTimerChangePeriodFromISR( xTimer, xNewPeriod, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_CHANGE_PERIOD_FROM_ISR, ( xNewPeriod ), ( pxHigherPriorityTaskWoken ), 0U ) + +/** + * BaseType_t xTimerResetFromISR( TimerHandle_t xTimer, + * BaseType_t *pxHigherPriorityTaskWoken ); + * + * A version of xTimerReset() that can be called from an interrupt service + * routine. + * + * @param xTimer The handle of the timer that is to be started, reset, or + * restarted. + * + * @param pxHigherPriorityTaskWoken The timer service/daemon task spends most + * of its time in the Blocked state, waiting for messages to arrive on the timer + * command queue. Calling xTimerResetFromISR() writes a message to the timer + * command queue, so has the potential to transition the timer service/daemon + * task out of the Blocked state. If calling xTimerResetFromISR() causes the + * timer service/daemon task to leave the Blocked state, and the timer service/ + * daemon task has a priority equal to or greater than the currently executing + * task (the task that was interrupted), then *pxHigherPriorityTaskWoken will + * get set to pdTRUE internally within the xTimerResetFromISR() function. If + * xTimerResetFromISR() sets this value to pdTRUE then a context switch should + * be performed before the interrupt exits. + * + * @return pdFAIL will be returned if the reset command could not be sent to + * the timer command queue. pdPASS will be returned if the command was + * successfully sent to the timer command queue. When the command is actually + * processed will depend on the priority of the timer service/daemon task + * relative to other tasks in the system, although the timers expiry time is + * relative to when xTimerResetFromISR() is actually called. The timer service/daemon + * task priority is set by the configTIMER_TASK_PRIORITY configuration constant. + * + * Example usage: + * @verbatim + * // This scenario assumes xBacklightTimer has already been created. When a + * // key is pressed, an LCD back-light is switched on. If 5 seconds pass + * // without a key being pressed, then the LCD back-light is switched off. In + * // this case, the timer is a one-shot timer, and unlike the example given for + * // the xTimerReset() function, the key press event handler is an interrupt + * // service routine. + * + * // The callback function assigned to the one-shot timer. In this case the + * // parameter is not used. + * void vBacklightTimerCallback( TimerHandle_t pxTimer ) + * { + * // The timer expired, therefore 5 seconds must have passed since a key + * // was pressed. Switch off the LCD back-light. + * vSetBacklightState( BACKLIGHT_OFF ); + * } + * + * // The key press interrupt service routine. + * void vKeyPressEventInterruptHandler( void ) + * { + * BaseType_t xHigherPriorityTaskWoken = pdFALSE; + * + * // Ensure the LCD back-light is on, then reset the timer that is + * // responsible for turning the back-light off after 5 seconds of + * // key inactivity. This is an interrupt service routine so can only + * // call FreeRTOS API functions that end in "FromISR". + * vSetBacklightState( BACKLIGHT_ON ); + * + * // xTimerStartFromISR() or xTimerResetFromISR() could be called here + * // as both cause the timer to re-calculate its expiry time. + * // xHigherPriorityTaskWoken was initialised to pdFALSE when it was + * // declared (in this function). + * if( xTimerResetFromISR( xBacklightTimer, &xHigherPriorityTaskWoken ) != pdPASS ) + * { + * // The reset command was not executed successfully. Take appropriate + * // action here. + * } + * + * // Perform the rest of the key processing here. + * + * // If xHigherPriorityTaskWoken equals pdTRUE, then a context switch + * // should be performed. The syntax required to perform a context switch + * // from inside an ISR varies from port to port, and from compiler to + * // compiler. Inspect the demos for the port you are using to find the + * // actual syntax required. + * if( xHigherPriorityTaskWoken != pdFALSE ) + * { + * // Call the interrupt safe yield function here (actual function + * // depends on the FreeRTOS port being used). + * } + * } + * @endverbatim + */ +#define xTimerResetFromISR( xTimer, pxHigherPriorityTaskWoken ) xTimerGenericCommand( ( xTimer ), tmrCOMMAND_RESET_FROM_ISR, ( xTaskGetTickCountFromISR() ), ( pxHigherPriorityTaskWoken ), 0U ) + + +/** + * BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, + * void *pvParameter1, + * uint32_t ulParameter2, + * BaseType_t *pxHigherPriorityTaskWoken ); + * + * + * Used from application interrupt service routines to defer the execution of a + * function to the RTOS daemon task (the timer service task, hence this function + * is implemented in timers.c and is prefixed with 'Timer'). + * + * Ideally an interrupt service routine (ISR) is kept as short as possible, but + * sometimes an ISR either has a lot of processing to do, or needs to perform + * processing that is not deterministic. In these cases + * xTimerPendFunctionCallFromISR() can be used to defer processing of a function + * to the RTOS daemon task. + * + * A mechanism is provided that allows the interrupt to return directly to the + * task that will subsequently execute the pended callback function. This + * allows the callback function to execute contiguously in time with the + * interrupt - just as if the callback had executed in the interrupt itself. + * + * @param xFunctionToPend The function to execute from the timer service/ + * daemon task. The function must conform to the PendedFunction_t + * prototype. + * + * @param pvParameter1 The value of the callback function's first parameter. + * The parameter has a void * type to allow it to be used to pass any type. + * For example, unsigned longs can be cast to a void *, or the void * can be + * used to point to a structure. + * + * @param ulParameter2 The value of the callback function's second parameter. + * + * @param pxHigherPriorityTaskWoken As mentioned above, calling this function + * will result in a message being sent to the timer daemon task. If the + * priority of the timer daemon task (which is set using + * configTIMER_TASK_PRIORITY in FreeRTOSConfig.h) is higher than the priority of + * the currently running task (the task the interrupt interrupted) then + * *pxHigherPriorityTaskWoken will be set to pdTRUE within + * xTimerPendFunctionCallFromISR(), indicating that a context switch should be + * requested before the interrupt exits. For that reason + * *pxHigherPriorityTaskWoken must be initialised to pdFALSE. See the + * example code below. + * + * @return pdPASS is returned if the message was successfully sent to the + * timer daemon task, otherwise pdFALSE is returned. + * + * Example usage: + * @verbatim + * + * // The callback function that will execute in the context of the daemon task. + * // Note callback functions must all use this same prototype. + * void vProcessInterface( void *pvParameter1, uint32_t ulParameter2 ) + * { + * BaseType_t xInterfaceToService; + * + * // The interface that requires servicing is passed in the second + * // parameter. The first parameter is not used in this case. + * xInterfaceToService = ( BaseType_t ) ulParameter2; + * + * // ...Perform the processing here... + * } + * + * // An ISR that receives data packets from multiple interfaces + * void vAnISR( void ) + * { + * BaseType_t xInterfaceToService, xHigherPriorityTaskWoken; + * + * // Query the hardware to determine which interface needs processing. + * xInterfaceToService = prvCheckInterfaces(); + * + * // The actual processing is to be deferred to a task. Request the + * // vProcessInterface() callback function is executed, passing in the + * // number of the interface that needs processing. The interface to + * // service is passed in the second parameter. The first parameter is + * // not used in this case. + * xHigherPriorityTaskWoken = pdFALSE; + * xTimerPendFunctionCallFromISR( vProcessInterface, NULL, ( uint32_t ) xInterfaceToService, &xHigherPriorityTaskWoken ); + * + * // If xHigherPriorityTaskWoken is now set to pdTRUE then a context + * // switch should be requested. The macro used is port specific and will + * // be either portYIELD_FROM_ISR() or portEND_SWITCHING_ISR() - refer to + * // the documentation page for the port being used. + * portYIELD_FROM_ISR( xHigherPriorityTaskWoken ); + * + * } + * @endverbatim + */ +BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, BaseType_t *pxHigherPriorityTaskWoken ) PRIVILEGED_FUNCTION; + + /** + * BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, + * void *pvParameter1, + * uint32_t ulParameter2, + * TickType_t xTicksToWait ); + * + * + * Used to defer the execution of a function to the RTOS daemon task (the timer + * service task, hence this function is implemented in timers.c and is prefixed + * with 'Timer'). + * + * @param xFunctionToPend The function to execute from the timer service/ + * daemon task. The function must conform to the PendedFunction_t + * prototype. + * + * @param pvParameter1 The value of the callback function's first parameter. + * The parameter has a void * type to allow it to be used to pass any type. + * For example, unsigned longs can be cast to a void *, or the void * can be + * used to point to a structure. + * + * @param ulParameter2 The value of the callback function's second parameter. + * + * @param xTicksToWait Calling this function will result in a message being + * sent to the timer daemon task on a queue. xTicksToWait is the amount of + * time the calling task should remain in the Blocked state (so not using any + * processing time) for space to become available on the timer queue if the + * queue is found to be full. + * + * @return pdPASS is returned if the message was successfully sent to the + * timer daemon task, otherwise pdFALSE is returned. + * + */ +BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +/** + * const char * const pcTimerGetName( TimerHandle_t xTimer ); + * + * Returns the name that was assigned to a timer when the timer was created. + * + * @param xTimer The handle of the timer being queried. + * + * @return The name assigned to the timer specified by the xTimer parameter. + */ +const char * pcTimerGetName( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + +/** + * void vTimerSetReloadMode( TimerHandle_t xTimer, const UBaseType_t uxAutoReload ); + * + * Updates a timer to be either an auto-reload timer, in which case the timer + * automatically resets itself each time it expires, or a one-shot timer, in + * which case the timer will only expire once unless it is manually restarted. + * + * @param xTimer The handle of the timer being updated. + * + * @param uxAutoReload If uxAutoReload is set to pdTRUE then the timer will + * expire repeatedly with a frequency set by the timer's period (see the + * xTimerPeriodInTicks parameter of the xTimerCreate() API function). If + * uxAutoReload is set to pdFALSE then the timer will be a one-shot timer and + * enter the dormant state after it expires. + */ +void vTimerSetReloadMode( TimerHandle_t xTimer, const UBaseType_t uxAutoReload ) PRIVILEGED_FUNCTION; + +/** +* UBaseType_t uxTimerGetReloadMode( TimerHandle_t xTimer ); +* +* Queries a timer to determine if it is an auto-reload timer, in which case the timer +* automatically resets itself each time it expires, or a one-shot timer, in +* which case the timer will only expire once unless it is manually restarted. +* +* @param xTimer The handle of the timer being queried. +* +* @return If the timer is an auto-reload timer then pdTRUE is returned, otherwise +* pdFALSE is returned. +*/ +UBaseType_t uxTimerGetReloadMode( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; + +/** + * TickType_t xTimerGetPeriod( TimerHandle_t xTimer ); + * + * Returns the period of a timer. + * + * @param xTimer The handle of the timer being queried. + * + * @return The period of the timer in ticks. + */ +TickType_t xTimerGetPeriod( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; + +/** +* TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ); +* +* Returns the time in ticks at which the timer will expire. If this is less +* than the current tick count then the expiry time has overflowed from the +* current time. +* +* @param xTimer The handle of the timer being queried. +* +* @return If the timer is running then the time in ticks at which the timer +* will next expire is returned. If the timer is not running then the return +* value is undefined. +*/ +TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; + +/* + * Functions beyond this part are not part of the public API and are intended + * for use by the kernel only. + */ +BaseType_t xTimerCreateTimerTask( void ) PRIVILEGED_FUNCTION; +BaseType_t xTimerGenericCommand( TimerHandle_t xTimer, const BaseType_t xCommandID, const TickType_t xOptionalValue, BaseType_t * const pxHigherPriorityTaskWoken, const TickType_t xTicksToWait ) PRIVILEGED_FUNCTION; + +#if( configUSE_TRACE_FACILITY == 1 ) + void vTimerSetTimerNumber( TimerHandle_t xTimer, UBaseType_t uxTimerNumber ) PRIVILEGED_FUNCTION; + UBaseType_t uxTimerGetTimerNumber( TimerHandle_t xTimer ) PRIVILEGED_FUNCTION; +#endif + +#ifdef __cplusplus +} +#endif +#endif /* TIMERS_H */ + + + diff --git a/product/rcar/src/CMSIS-FreeRTOS/Source/list.c b/product/rcar/src/CMSIS-FreeRTOS/Source/list.c new file mode 100644 index 000000000..7618ee8b8 --- /dev/null +++ b/product/rcar/src/CMSIS-FreeRTOS/Source/list.c @@ -0,0 +1,198 @@ +/* + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + + +#include +#include "FreeRTOS.h" +#include "list.h" + +/*----------------------------------------------------------- + * PUBLIC LIST API documented in list.h + *----------------------------------------------------------*/ + +void vListInitialise( List_t * const pxList ) +{ + /* The list structure contains a list item which is used to mark the + end of the list. To initialise the list the list end is inserted + as the only list entry. */ + pxList->pxIndex = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */ + + /* The list end value is the highest possible value in the list to + ensure it remains at the end of the list. */ + pxList->xListEnd.xItemValue = portMAX_DELAY; + + /* The list end next and previous pointers point to itself so we know + when the list is empty. */ + pxList->xListEnd.pxNext = ( ListItem_t * ) &( pxList->xListEnd ); /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */ + pxList->xListEnd.pxPrevious = ( ListItem_t * ) &( pxList->xListEnd );/*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. */ + + pxList->uxNumberOfItems = ( UBaseType_t ) 0U; + + /* Write known values into the list if + configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ + listSET_LIST_INTEGRITY_CHECK_1_VALUE( pxList ); + listSET_LIST_INTEGRITY_CHECK_2_VALUE( pxList ); +} +/*-----------------------------------------------------------*/ + +void vListInitialiseItem( ListItem_t * const pxItem ) +{ + /* Make sure the list item is not recorded as being on a list. */ + pxItem->pxContainer = NULL; + + /* Write known values into the list item if + configUSE_LIST_DATA_INTEGRITY_CHECK_BYTES is set to 1. */ + listSET_FIRST_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ); + listSET_SECOND_LIST_ITEM_INTEGRITY_CHECK_VALUE( pxItem ); +} +/*-----------------------------------------------------------*/ + +void vListInsertEnd( List_t * const pxList, ListItem_t * const pxNewListItem ) +{ +ListItem_t * const pxIndex = pxList->pxIndex; + + /* Only effective when configASSERT() is also defined, these tests may catch + the list data structures being overwritten in memory. They will not catch + data errors caused by incorrect configuration or use of FreeRTOS. */ + listTEST_LIST_INTEGRITY( pxList ); + listTEST_LIST_ITEM_INTEGRITY( pxNewListItem ); + + /* Insert a new list item into pxList, but rather than sort the list, + makes the new list item the last item to be removed by a call to + listGET_OWNER_OF_NEXT_ENTRY(). */ + pxNewListItem->pxNext = pxIndex; + pxNewListItem->pxPrevious = pxIndex->pxPrevious; + + /* Only used during decision coverage testing. */ + mtCOVERAGE_TEST_DELAY(); + + pxIndex->pxPrevious->pxNext = pxNewListItem; + pxIndex->pxPrevious = pxNewListItem; + + /* Remember which list the item is in. */ + pxNewListItem->pxContainer = pxList; + + ( pxList->uxNumberOfItems )++; +} +/*-----------------------------------------------------------*/ + +void vListInsert( List_t * const pxList, ListItem_t * const pxNewListItem ) +{ +ListItem_t *pxIterator; +const TickType_t xValueOfInsertion = pxNewListItem->xItemValue; + + /* Only effective when configASSERT() is also defined, these tests may catch + the list data structures being overwritten in memory. They will not catch + data errors caused by incorrect configuration or use of FreeRTOS. */ + listTEST_LIST_INTEGRITY( pxList ); + listTEST_LIST_ITEM_INTEGRITY( pxNewListItem ); + + /* Insert the new list item into the list, sorted in xItemValue order. + + If the list already contains a list item with the same item value then the + new list item should be placed after it. This ensures that TCBs which are + stored in ready lists (all of which have the same xItemValue value) get a + share of the CPU. However, if the xItemValue is the same as the back marker + the iteration loop below will not end. Therefore the value is checked + first, and the algorithm slightly modified if necessary. */ + if( xValueOfInsertion == portMAX_DELAY ) + { + pxIterator = pxList->xListEnd.pxPrevious; + } + else + { + /* *** NOTE *********************************************************** + If you find your application is crashing here then likely causes are + listed below. In addition see https://www.freertos.org/FAQHelp.html for + more tips, and ensure configASSERT() is defined! + https://www.freertos.org/a00110.html#configASSERT + + 1) Stack overflow - + see https://www.freertos.org/Stacks-and-stack-overflow-checking.html + 2) Incorrect interrupt priority assignment, especially on Cortex-M + parts where numerically high priority values denote low actual + interrupt priorities, which can seem counter intuitive. See + https://www.freertos.org/RTOS-Cortex-M3-M4.html and the definition + of configMAX_SYSCALL_INTERRUPT_PRIORITY on + https://www.freertos.org/a00110.html + 3) Calling an API function from within a critical section or when + the scheduler is suspended, or calling an API function that does + not end in "FromISR" from an interrupt. + 4) Using a queue or semaphore before it has been initialised or + before the scheduler has been started (are interrupts firing + before vTaskStartScheduler() has been called?). + **********************************************************************/ + + for( pxIterator = ( ListItem_t * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) /*lint !e826 !e740 !e9087 The mini list structure is used as the list end to save RAM. This is checked and valid. *//*lint !e440 The iterator moves to a different value, not xValueOfInsertion. */ + { + /* There is nothing to do here, just iterating to the wanted + insertion position. */ + } + } + + pxNewListItem->pxNext = pxIterator->pxNext; + pxNewListItem->pxNext->pxPrevious = pxNewListItem; + pxNewListItem->pxPrevious = pxIterator; + pxIterator->pxNext = pxNewListItem; + + /* Remember which list the item is in. This allows fast removal of the + item later. */ + pxNewListItem->pxContainer = pxList; + + ( pxList->uxNumberOfItems )++; +} +/*-----------------------------------------------------------*/ + +UBaseType_t uxListRemove( ListItem_t * const pxItemToRemove ) +{ +/* The list item knows which list it is in. Obtain the list from the list +item. */ +List_t * const pxList = pxItemToRemove->pxContainer; + + pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious; + pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext; + + /* Only used during decision coverage testing. */ + mtCOVERAGE_TEST_DELAY(); + + /* Make sure the index is left pointing to a valid item. */ + if( pxList->pxIndex == pxItemToRemove ) + { + pxList->pxIndex = pxItemToRemove->pxPrevious; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + pxItemToRemove->pxContainer = NULL; + ( pxList->uxNumberOfItems )--; + + return pxList->uxNumberOfItems; +} +/*-----------------------------------------------------------*/ + diff --git a/product/rcar/src/CMSIS-FreeRTOS/Source/portable/GCC/ARM_CA53_64_Rcar/port.c b/product/rcar/src/CMSIS-FreeRTOS/Source/portable/GCC/ARM_CA53_64_Rcar/port.c new file mode 100644 index 000000000..429775835 --- /dev/null +++ b/product/rcar/src/CMSIS-FreeRTOS/Source/portable/GCC/ARM_CA53_64_Rcar/port.c @@ -0,0 +1,301 @@ +/* + * FreeRTOS Kernel V10.2.0 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/* Standard includes. */ +#include + +/* Scheduler includes. */ +#include "FreeRTOS.h" +#include "task.h" + +#ifndef configSETUP_TICK_INTERRUPT + #error configSETUP_TICK_INTERRUPT() must be defined. See http://www.freertos.org/Using-FreeRTOS-on-Cortex-A-Embedded-Processors.html +#endif /* configSETUP_TICK_INTERRUPT */ + +/* Some vendor specific files default configCLEAR_TICK_INTERRUPT() in +portmacro.h. */ +#ifndef configCLEAR_TICK_INTERRUPT + #define configCLEAR_TICK_INTERRUPT() +#endif + +/* A critical section is exited when the critical section nesting count reaches +this value. */ +#define portNO_CRITICAL_NESTING ( ( size_t ) 0 ) + +/* Tasks are not created with a floating point context, but can be given a +floating point context after they have been created. A variable is stored as +part of the tasks context that holds portNO_FLOATING_POINT_CONTEXT if the task +does not have an FPU context, or any other value if the task does have an FPU +context. */ +#define portNO_FLOATING_POINT_CONTEXT ( ( StackType_t ) 0 ) + +/* Constants required to setup the initial task context. */ +#define portSP_ELx ( ( StackType_t ) 0x01 ) +#define portSP_EL0 ( ( StackType_t ) 0x00 ) + +#define portEL1 ( ( StackType_t ) 0x04 ) +#define portEL3 ( ( StackType_t ) 0x0C ) +#if 0 +#define portINITIAL_PSTATE ( portEL1 | portSP_EL0 ) +#else +#define portINITIAL_PSTATE ( portEL3 | portSP_EL0 ) +#endif + +/* Masks all bits in the APSR other than the mode bits. */ +#define portAPSR_MODE_BITS_MASK ( 0x0C ) + +/* Used in the ASM code. */ +__attribute__(( used )) const uint64_t ulCORE0_INT_SRC = 0x40000060; + +/*-----------------------------------------------------------*/ + +/* + * Starts the first task executing. This function is necessarily written in + * assembly code so is implemented in portASM.s. + */ +extern void vPortRestoreTaskContext( void ); + +/*-----------------------------------------------------------*/ + +/* A variable is used to keep track of the critical section nesting. This +variable has to be stored as part of the task context and must be initialised to +a non zero value to ensure interrupts don't inadvertently become unmasked before +the scheduler starts. As it is stored as part of the task context it will +automatically be set to 0 when the first task is started. */ +volatile uint64_t ullCriticalNesting = 9999ULL; + +/* Saved as part of the task context. If ullPortTaskHasFPUContext is non-zero +then floating point context must be saved and restored for the task. */ +uint64_t ullPortTaskHasFPUContext = pdFALSE; + +/* Set to 1 to pend a context switch from an ISR. */ +uint64_t ullPortYieldRequired = pdFALSE; + +/* Counts the interrupt nesting depth. A context switch is only performed if +if the nesting depth is 0. */ +uint64_t ullPortInterruptNesting = 0; + +/*-----------------------------------------------------------*/ +/* + * See header file for description. + */ +StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, + TaskFunction_t pxCode, void *pvParameters ) +{ + /* Setup the initial stack of the task. The stack is set exactly as + expected by the portRESTORE_CONTEXT() macro. */ + + /* First all the general purpose registers. */ + pxTopOfStack--; + *pxTopOfStack = 0x0101010101010101ULL; /* R1 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) pvParameters; /* R0 */ + pxTopOfStack--; + *pxTopOfStack = 0x0303030303030303ULL; /* R3 */ + pxTopOfStack--; + *pxTopOfStack = 0x0202020202020202ULL; /* R2 */ + pxTopOfStack--; + *pxTopOfStack = 0x0505050505050505ULL; /* R5 */ + pxTopOfStack--; + *pxTopOfStack = 0x0404040404040404ULL; /* R4 */ + pxTopOfStack--; + *pxTopOfStack = 0x0707070707070707ULL; /* R7 */ + pxTopOfStack--; + *pxTopOfStack = 0x0606060606060606ULL; /* R6 */ + pxTopOfStack--; + *pxTopOfStack = 0x0909090909090909ULL; /* R9 */ + pxTopOfStack--; + *pxTopOfStack = 0x0808080808080808ULL; /* R8 */ + pxTopOfStack--; + *pxTopOfStack = 0x1111111111111111ULL; /* R11 */ + pxTopOfStack--; + *pxTopOfStack = 0x1010101010101010ULL; /* R10 */ + pxTopOfStack--; + *pxTopOfStack = 0x1313131313131313ULL; /* R13 */ + pxTopOfStack--; + *pxTopOfStack = 0x1212121212121212ULL; /* R12 */ + pxTopOfStack--; + *pxTopOfStack = 0x1515151515151515ULL; /* R15 */ + pxTopOfStack--; + *pxTopOfStack = 0x1414141414141414ULL; /* R14 */ + pxTopOfStack--; + *pxTopOfStack = 0x1717171717171717ULL; /* R17 */ + pxTopOfStack--; + *pxTopOfStack = 0x1616161616161616ULL; /* R16 */ + pxTopOfStack--; + *pxTopOfStack = 0x1919191919191919ULL; /* R19 */ + pxTopOfStack--; + *pxTopOfStack = 0x1818181818181818ULL; /* R18 */ + pxTopOfStack--; + *pxTopOfStack = 0x2121212121212121ULL; /* R21 */ + pxTopOfStack--; + *pxTopOfStack = 0x2020202020202020ULL; /* R20 */ + pxTopOfStack--; + *pxTopOfStack = 0x2323232323232323ULL; /* R23 */ + pxTopOfStack--; + *pxTopOfStack = 0x2222222222222222ULL; /* R22 */ + pxTopOfStack--; + *pxTopOfStack = 0x2525252525252525ULL; /* R25 */ + pxTopOfStack--; + *pxTopOfStack = 0x2424242424242424ULL; /* R24 */ + pxTopOfStack--; + *pxTopOfStack = 0x2727272727272727ULL; /* R27 */ + pxTopOfStack--; + *pxTopOfStack = 0x2626262626262626ULL; /* R26 */ + pxTopOfStack--; + *pxTopOfStack = 0x2929292929292929ULL; /* R29 */ + pxTopOfStack--; + *pxTopOfStack = 0x2828282828282828ULL; /* R28 */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x00; /* XZR */ + /* - has no effect, used so there are an even number of registers. */ + pxTopOfStack--; + *pxTopOfStack = ( StackType_t ) 0x00; /* R30 */ + /* - procedure call link register. */ + pxTopOfStack--; + + *pxTopOfStack = portINITIAL_PSTATE; + pxTopOfStack--; + + *pxTopOfStack = ( StackType_t ) pxCode; /* Exception return address. */ + pxTopOfStack--; + + /* The task will start with a critical nesting count of 0 as interrupts are + enabled. */ + *pxTopOfStack = portNO_CRITICAL_NESTING; + pxTopOfStack--; + + /* The task will start without a floating point context. A task that uses + the floating point hardware must call vPortTaskUsesFPU() before executing + any floating point instructions. */ + *pxTopOfStack = portNO_FLOATING_POINT_CONTEXT; + + return pxTopOfStack; +} +/*-----------------------------------------------------------*/ + +BaseType_t xPortStartScheduler( void ) +{ + uint32_t ulAPSR; + + /* At the time of writing, the BSP only supports EL3. */ + __asm volatile ( "MRS %0, CurrentEL" : "=r" ( ulAPSR ) ); + ulAPSR &= portAPSR_MODE_BITS_MASK; + + configASSERT( ulAPSR == portEL3 ); + if( ulAPSR == portEL3 ) + { + { + /* Interrupts are turned off in the CPU itself to ensure a tick does + not execute while the scheduler is being started. Interrupts are + automatically turned back on in the CPU when the first task starts + executing. */ + portDISABLE_INTERRUPTS(); + + /* Start the timer that generates the tick ISR. */ + configSETUP_TICK_INTERRUPT(); + + /* Start the first task executing. */ + vPortRestoreTaskContext(); + } + } + + return 0; +} +/*-----------------------------------------------------------*/ + +void vPortEndScheduler( void ) +{ + /* Not implemented in ports where there is nothing to return to. + Artificially force an assert. */ + configASSERT( ullCriticalNesting == 1000ULL ); +} +/*-----------------------------------------------------------*/ + +void vPortEnterCritical( void ) +{ + portDISABLE_INTERRUPTS(); + + /* Now interrupts are disabled ullCriticalNesting can be accessed + directly. Increment ullCriticalNesting to keep a count of how many times + portENTER_CRITICAL() has been called. */ + ullCriticalNesting++; + + /* This is not the interrupt safe version of the enter critical function so + assert() if it is being called from an interrupt context. Only API + functions that end in "FromISR" can be used in an interrupt. Only assert if + the critical nesting count is 1 to protect against recursive calls if the + assert function also uses a critical section. */ + if( ullCriticalNesting == 1ULL ) + { + configASSERT( ullPortInterruptNesting == 0 ); + } +} +/*-----------------------------------------------------------*/ + +void vPortExitCritical( void ) +{ + if( ullCriticalNesting > portNO_CRITICAL_NESTING ) + { + /* Decrement the nesting count as the critical section is being + exited. */ + ullCriticalNesting--; + + /* If the nesting level has reached zero then all interrupt + priorities must be re-enabled. */ + if( ullCriticalNesting == portNO_CRITICAL_NESTING ) + { + /* Critical nesting has reached zero so interrupts + should be enabled. */ + portENABLE_INTERRUPTS(); + } + } +} +/*-----------------------------------------------------------*/ + +void FreeRTOS_Tick_Handler( void ) +{ + /* Interrupts should not be enabled before this point. */ + #if( configASSERT_DEFINED == 1 ) + { + uint32_t ulMaskBits; + + __asm volatile( "mrs %0, daif" : "=r"( ulMaskBits ) :: "memory" ); + configASSERT( ( ulMaskBits & portDAIF_I ) != 0 ); + } + #endif /* configASSERT_DEFINED */ + + /* Ok to enable interrupts after the interrupt source has been cleared. */ + configCLEAR_TICK_INTERRUPT(); + portENABLE_INTERRUPTS(); + + /* Increment the RTOS tick. */ + if( xTaskIncrementTick() != pdFALSE ) + { + ullPortYieldRequired = pdTRUE; + } +} diff --git a/product/rcar/src/CMSIS-FreeRTOS/Source/portable/GCC/ARM_CA53_64_Rcar/portASM.S b/product/rcar/src/CMSIS-FreeRTOS/Source/portable/GCC/ARM_CA53_64_Rcar/portASM.S new file mode 100644 index 000000000..602e8c288 --- /dev/null +++ b/product/rcar/src/CMSIS-FreeRTOS/Source/portable/GCC/ARM_CA53_64_Rcar/portASM.S @@ -0,0 +1,352 @@ +/* + * FreeRTOS Kernel V10.2.0 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + + .text + + /* Variables and functions. */ + .extern ullMaxAPIPriorityMask + .extern pxCurrentTCB + .extern _freertos_vector_table + + .globl FreeRTOS_SWI_Handler + .globl FreeRTOS_IRQ_Handler + .globl vPortRestoreTaskContext + +; /**********************************************************************/ + +.macro portSAVE_CONTEXT + + /* Switch to use the EL0 stack pointer. */ + MSR SPSEL, #0 + + /* Save the entire context. */ + STP X0, X1, [SP, #-0x10]! + STP X2, X3, [SP, #-0x10]! + STP X4, X5, [SP, #-0x10]! + STP X6, X7, [SP, #-0x10]! + STP X8, X9, [SP, #-0x10]! + STP X10, X11, [SP, #-0x10]! + STP X12, X13, [SP, #-0x10]! + STP X14, X15, [SP, #-0x10]! + STP X16, X17, [SP, #-0x10]! + STP X18, X19, [SP, #-0x10]! + STP X20, X21, [SP, #-0x10]! + STP X22, X23, [SP, #-0x10]! + STP X24, X25, [SP, #-0x10]! + STP X26, X27, [SP, #-0x10]! + STP X28, X29, [SP, #-0x10]! + STP X30, XZR, [SP, #-0x10]! + + /* Save the SPSR. */ + MRS X3, SPSR_EL3 + MRS X2, ELR_EL3 + + STP X2, X3, [SP, #-0x10]! + + /* Save the critical section nesting depth. */ + LDR X0, ullCriticalNestingConst + LDR X3, [X0] + + /* Save the FPU context indicator. */ + LDR X0, ullPortTaskHasFPUContextConst + LDR X2, [X0] + + /* Save the FPU context, if any (32 128-bit registers). */ + CMP X2, #0 + B.EQ 1f + STP Q0, Q1, [SP,#-0x20]! + STP Q2, Q3, [SP,#-0x20]! + STP Q4, Q5, [SP,#-0x20]! + STP Q6, Q7, [SP,#-0x20]! + STP Q8, Q9, [SP,#-0x20]! + STP Q10, Q11, [SP,#-0x20]! + STP Q12, Q13, [SP,#-0x20]! + STP Q14, Q15, [SP,#-0x20]! + STP Q16, Q17, [SP,#-0x20]! + STP Q18, Q19, [SP,#-0x20]! + STP Q20, Q21, [SP,#-0x20]! + STP Q22, Q23, [SP,#-0x20]! + STP Q24, Q25, [SP,#-0x20]! + STP Q26, Q27, [SP,#-0x20]! + STP Q28, Q29, [SP,#-0x20]! + STP Q30, Q31, [SP,#-0x20]! + +1: + /* Store the critical nesting count and FPU context indicator. */ + STP X2, X3, [SP, #-0x10]! + + LDR X0, pxCurrentTCBConst + LDR X1, [X0] + MOV X0, SP /* Move SP into X0 for saving. */ + STR X0, [X1] + + /* Switch to use the ELx stack pointer. */ + MSR SPSEL, #1 + + .endm + + +.macro portRESTORE_CONTEXT + + /* Switch to use the EL0 stack pointer. */ + MSR SPSEL, #0 + + /* Set the SP to point to the stack of the task being restored. */ + LDR X0, pxCurrentTCBConst + LDR X1, [X0] + LDR X0, [X1] + MOV SP, X0 + + LDP X2, X3, [SP], #0x10 /* Critical nesting and FPU context. */ + + /* Set the PMR register to be correct for the current critical nesting + depth. */ + /* X0 holds the address of ullCriticalNesting. */ + LDR X0, ullCriticalNestingConst + /* Restore the task's critical nesting count. */ + STR X3, [X0] + + /* Restore the FPU context indicator. */ + LDR X0, ullPortTaskHasFPUContextConst + STR X2, [X0] + + /* Restore the FPU context, if any. */ + CMP X2, #0 + B.EQ 1f + LDP Q30, Q31, [SP], #0x20 + LDP Q28, Q29, [SP], #0x20 + LDP Q26, Q27, [SP], #0x20 + LDP Q24, Q25, [SP], #0x20 + LDP Q22, Q23, [SP], #0x20 + LDP Q20, Q21, [SP], #0x20 + LDP Q18, Q19, [SP], #0x20 + LDP Q16, Q17, [SP], #0x20 + LDP Q14, Q15, [SP], #0x20 + LDP Q12, Q13, [SP], #0x20 + LDP Q10, Q11, [SP], #0x20 + LDP Q8, Q9, [SP], #0x20 + LDP Q6, Q7, [SP], #0x20 + LDP Q4, Q5, [SP], #0x20 + LDP Q2, Q3, [SP], #0x20 + LDP Q0, Q1, [SP], #0x20 +1: + LDP X2, X3, [SP], #0x10 /* SPSR and ELR. */ + + /* Restore the SPSR. */ + MSR SPSR_EL3, X3 + /* Restore the ELR. */ + MSR ELR_EL3, X2 + + LDP X30, XZR, [SP], #0x10 + LDP X28, X29, [SP], #0x10 + LDP X26, X27, [SP], #0x10 + LDP X24, X25, [SP], #0x10 + LDP X22, X23, [SP], #0x10 + LDP X20, X21, [SP], #0x10 + LDP X18, X19, [SP], #0x10 + LDP X16, X17, [SP], #0x10 + LDP X14, X15, [SP], #0x10 + LDP X12, X13, [SP], #0x10 + LDP X10, X11, [SP], #0x10 + LDP X8, X9, [SP], #0x10 + LDP X6, X7, [SP], #0x10 + LDP X4, X5, [SP], #0x10 + LDP X2, X3, [SP], #0x10 + LDP X0, X1, [SP], #0x10 + + /* Switch to use the ELx stack pointer. _RB_ Might not be required. */ + MSR SPSEL, #1 + + ERET + + .endm + +/****************************************************************************** + * FreeRTOS_SWI_Handler handler is used to perform a context switch. + *****************************************************************************/ +.align 8 +.type FreeRTOS_SWI_Handler, %function +FreeRTOS_SWI_Handler: + /* Save the context of the current task and select a new task to run. */ + portSAVE_CONTEXT + MRS X0, ESR_EL3 + + LSR X1, X0, #26 + + CMP X1, #0x15 /* 0x15 = SVC instruction. */ + + B.NE FreeRTOS_Abort + BL vTaskSwitchContext + + portRESTORE_CONTEXT + +FreeRTOS_Abort: + /* Full ESR is in X0, exception class code is in X1. */ + B . + +/****************************************************************************** + * vPortRestoreTaskContext is used to start the scheduler. + *****************************************************************************/ +.align 8 +.type vPortRestoreTaskContext, %function +vPortRestoreTaskContext: +.set freertos_vector_base, _freertos_vector_table + + /* Install the FreeRTOS interrupt handlers. */ + LDR X1, =freertos_vector_base + MSR VBAR_EL3, X1 + DSB SY + ISB SY + + /* Start the first task. */ + portRESTORE_CONTEXT + +/****************************************************************************** + * FreeRTOS_IRQ_Handler handles IRQ entry and exit. + *****************************************************************************/ +.align 8 +.type FreeRTOS_IRQ_Handler, %function +FreeRTOS_IRQ_Handler: + /* Save volatile registers. */ + STP X0, X1, [SP, #-0x10]! + STP X2, X3, [SP, #-0x10]! + STP X4, X5, [SP, #-0x10]! + STP X6, X7, [SP, #-0x10]! + STP X8, X9, [SP, #-0x10]! + STP X10, X11, [SP, #-0x10]! + STP X12, X13, [SP, #-0x10]! + STP X14, X15, [SP, #-0x10]! + STP X16, X17, [SP, #-0x10]! + STP X18, X19, [SP, #-0x10]! + STP X29, X30, [SP, #-0x10]! + + /* Save the SPSR and ELR. */ + MRS X3, SPSR_EL3 + MRS X2, ELR_EL3 + STP X2, X3, [SP, #-0x10]! + + /* Increment the interrupt nesting counter. */ + LDR X5, ullPortInterruptNestingConst + LDR X1, [X5] /* Old nesting count in X1. */ + ADD X6, X1, #1 + STR X6, [X5] /* Address of nesting count variable in X5. */ + + /* Maintain the interrupt nesting information across the function call. */ + STP X1, X5, [SP, #-0x10]! + +#if 0 + /* Read Cor0 interrupt Source */ + ldr x2, ulCORE0_INT_SRCConst + ldr x3, [x2] + ldr w0, [x3] /* set parametor for handler */ +#endif + + /* Call the C handler. */ + BL vApplicationIRQHandler + + /* Disable interrupts. */ + MSR DAIFSET, #1 /* IRQ -> FIQ */ + DSB SY + ISB SY + + /* Restore the critical nesting count. */ + LDP X1, X5, [SP], #0x10 + STR X1, [X5] + + /* Has interrupt nesting unwound? */ + CMP X1, #0 + B.NE Exit_IRQ_No_Context_Switch + + /* Is a context switch required? */ + LDR X0, ullPortYieldRequiredConst + LDR X1, [X0] + CMP X1, #0 + B.EQ Exit_IRQ_No_Context_Switch + + /* Reset ullPortYieldRequired to 0. */ + MOV X2, #0 + STR X2, [X0] + + /* Restore volatile registers. */ + LDP X4, X5, [SP], #0x10 /* SPSR and ELR. */ + MSR SPSR_EL3, X5 + MSR ELR_EL3, X4 + DSB SY + ISB SY + + LDP X29, X30, [SP], #0x10 + LDP X18, X19, [SP], #0x10 + LDP X16, X17, [SP], #0x10 + LDP X14, X15, [SP], #0x10 + LDP X12, X13, [SP], #0x10 + LDP X10, X11, [SP], #0x10 + LDP X8, X9, [SP], #0x10 + LDP X6, X7, [SP], #0x10 + LDP X4, X5, [SP], #0x10 + LDP X2, X3, [SP], #0x10 + LDP X0, X1, [SP], #0x10 + + /* Save the context of the current task and select a new task to run. */ + portSAVE_CONTEXT + BL vTaskSwitchContext + portRESTORE_CONTEXT + +Exit_IRQ_No_Context_Switch: + /* Restore volatile registers. */ + LDP X4, X5, [SP], #0x10 /* SPSR and ELR. */ + MSR SPSR_EL3, X5 + MSR ELR_EL3, X4 + DSB SY + ISB SY + + LDP X29, X30, [SP], #0x10 + LDP X18, X19, [SP], #0x10 + LDP X16, X17, [SP], #0x10 + LDP X14, X15, [SP], #0x10 + LDP X12, X13, [SP], #0x10 + LDP X10, X11, [SP], #0x10 + LDP X8, X9, [SP], #0x10 + LDP X6, X7, [SP], #0x10 + LDP X4, X5, [SP], #0x10 + LDP X2, X3, [SP], #0x10 + LDP X0, X1, [SP], #0x10 + + ERET + + +.align 8 +pxCurrentTCBConst: .dword pxCurrentTCB +ullCriticalNestingConst: .dword ullCriticalNesting +ullPortTaskHasFPUContextConst: .dword ullPortTaskHasFPUContext + +vApplicationIRQHandlerConst: .word vApplicationIRQHandler + .word 0 +ullPortInterruptNestingConst: .dword ullPortInterruptNesting +ullPortYieldRequiredConst: .dword ullPortYieldRequired + +ulCORE0_INT_SRCConst: .dword ulCORE0_INT_SRC +.end diff --git a/product/rcar/src/CMSIS-FreeRTOS/Source/portable/GCC/ARM_CA53_64_Rcar/portmacro.h b/product/rcar/src/CMSIS-FreeRTOS/Source/portable/GCC/ARM_CA53_64_Rcar/portmacro.h new file mode 100644 index 000000000..492f5b6fa --- /dev/null +++ b/product/rcar/src/CMSIS-FreeRTOS/Source/portable/GCC/ARM_CA53_64_Rcar/portmacro.h @@ -0,0 +1,117 @@ +/* + * FreeRTOS Kernel V10.2.0 + * Copyright (C) 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +#ifndef PORTMACRO_H +#define PORTMACRO_H + +#ifdef __cplusplus + extern "C" { +#endif + +/*----------------------------------------------------------- + * Port specific definitions. + * + * The settings in this file configure FreeRTOS correctly for the given hardware + * and compiler. + * + * These settings should not be altered. + *----------------------------------------------------------- + */ + +/* Type definitions. */ +#define portCHAR char +#define portFLOAT float +#define portDOUBLE double +#define portLONG long +#define portSHORT short +#define portSTACK_TYPE size_t +#define portBASE_TYPE long + +typedef portSTACK_TYPE StackType_t; +typedef portBASE_TYPE BaseType_t; +typedef uint64_t UBaseType_t; + +typedef uint64_t TickType_t; +#define portMAX_DELAY ( ( TickType_t ) 0xffffffffffffffff ) + +/*-----------------------------------------------------------*/ + +/* Task utilities. */ + +#define portYIELD() __asm volatile ( "SVC 0" ::: "memory" ) + +/*----------------------------------------------------------- + * Critical section control + *----------------------------------------------------------*/ + +extern void vPortEnterCritical( void ); +extern void vPortExitCritical( void ); + +#define portDISABLE_INTERRUPTS() \ + __asm volatile ( "MSR DAIFSET, #1" ::: "memory" ); \ + __asm volatile ( "DSB SY" ); \ + __asm volatile ( "ISB SY" ); + +#define portENABLE_INTERRUPTS() \ + __asm volatile ( "MSR DAIFCLR, #1" ::: "memory" ); \ + __asm volatile ( "DSB SY" ); \ + __asm volatile ( "ISB SY" ); + + + +/* These macros do not globally disable/enable interrupts. They do mask off +interrupts that have a priority below configMAX_API_CALL_INTERRUPT_PRIORITY. */ +#define portENTER_CRITICAL() vPortEnterCritical(); +#define portEXIT_CRITICAL() vPortExitCritical(); + +/*-----------------------------------------------------------*/ + +/* Task function macros as described on the FreeRTOS.org WEB site. These are +not required for this port but included in case common demo code that uses these +macros is used. */ +#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) \ + void vFunction( void *pvParameters ) +#define portTASK_FUNCTION( vFunction, pvParameters ) \ + void vFunction( void *pvParameters ) + +/* Prototype of the FreeRTOS tick handler. This must be installed as the +handler for whichever peripheral is used to generate the RTOS tick. */ +void FreeRTOS_Tick_Handler( void ); + +/*-----------------------------------------------------------*/ + +/* Hardware specifics. */ +#define portSTACK_GROWTH ( -1 ) +#define portTICK_PERIOD_MS ( ( TickType_t ) 1000 / configTICK_RATE_HZ ) +#define portBYTE_ALIGNMENT 16 +#define portPOINTER_SIZE_TYPE uint64_t + +#ifdef __cplusplus + } /* extern C */ +#endif + +#endif /* PORTMACRO_H */ diff --git a/product/rcar/src/CMSIS-FreeRTOS/Source/portable/MemMang/heap_1.c b/product/rcar/src/CMSIS-FreeRTOS/Source/portable/MemMang/heap_1.c new file mode 100644 index 000000000..0bd40cd40 --- /dev/null +++ b/product/rcar/src/CMSIS-FreeRTOS/Source/portable/MemMang/heap_1.c @@ -0,0 +1,146 @@ +/* + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + + +/* + * The simplest possible implementation of pvPortMalloc(). Note that this + * implementation does NOT allow allocated memory to be freed again. + * + * See heap_2.c, heap_3.c and heap_4.c for alternative implementations, and the + * memory management pages of http://www.FreeRTOS.org for more information. + */ +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +#include "FreeRTOS.h" +#include "task.h" + +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +#if( configSUPPORT_DYNAMIC_ALLOCATION == 0 ) + #error This file must not be used if configSUPPORT_DYNAMIC_ALLOCATION is 0 +#endif + +/* A few bytes might be lost to byte aligning the heap start address. */ +#define configADJUSTED_HEAP_SIZE ( configTOTAL_HEAP_SIZE - portBYTE_ALIGNMENT ) + +/* Allocate the memory for the heap. */ +#if( configAPPLICATION_ALLOCATED_HEAP == 1 ) + /* The application writer has already defined the array used for the RTOS + heap - probably so it can be placed in a special segment or address. */ + extern uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; +#else + static uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; +#endif /* configAPPLICATION_ALLOCATED_HEAP */ + +/* Index into the ucHeap array. */ +static size_t xNextFreeByte = ( size_t ) 0; + +/*-----------------------------------------------------------*/ + +void *pvPortMalloc( size_t xWantedSize ) +{ +void *pvReturn = NULL; +static uint8_t *pucAlignedHeap = NULL; + + /* Ensure that blocks are always aligned to the required number of bytes. */ + #if( portBYTE_ALIGNMENT != 1 ) + { + if( xWantedSize & portBYTE_ALIGNMENT_MASK ) + { + /* Byte alignment required. */ + xWantedSize += ( portBYTE_ALIGNMENT - ( xWantedSize & portBYTE_ALIGNMENT_MASK ) ); + } + } + #endif + + vTaskSuspendAll(); + { + if( pucAlignedHeap == NULL ) + { + /* Ensure the heap starts on a correctly aligned boundary. */ + pucAlignedHeap = ( uint8_t * ) ( ( ( portPOINTER_SIZE_TYPE ) &ucHeap[ portBYTE_ALIGNMENT ] ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); + } + + /* Check there is enough room left for the allocation. */ + if( ( ( xNextFreeByte + xWantedSize ) < configADJUSTED_HEAP_SIZE ) && + ( ( xNextFreeByte + xWantedSize ) > xNextFreeByte ) )/* Check for overflow. */ + { + /* Return the next free byte then increment the index past this + block. */ + pvReturn = pucAlignedHeap + xNextFreeByte; + xNextFreeByte += xWantedSize; + } + + traceMALLOC( pvReturn, xWantedSize ); + } + ( void ) xTaskResumeAll(); + + #if( configUSE_MALLOC_FAILED_HOOK == 1 ) + { + if( pvReturn == NULL ) + { + extern void vApplicationMallocFailedHook( void ); + vApplicationMallocFailedHook(); + } + } + #endif + + return pvReturn; +} +/*-----------------------------------------------------------*/ + +void vPortFree( void *pv ) +{ + /* Memory cannot be freed using this scheme. See heap_2.c, heap_3.c and + heap_4.c for alternative implementations, and the memory management pages of + http://www.FreeRTOS.org for more information. */ + ( void ) pv; + + /* Force an assert as it is invalid to call this function. */ + configASSERT( pv == NULL ); +} +/*-----------------------------------------------------------*/ + +void vPortInitialiseBlocks( void ) +{ + /* Only required when static memory is not cleared. */ + xNextFreeByte = ( size_t ) 0; +} +/*-----------------------------------------------------------*/ + +size_t xPortGetFreeHeapSize( void ) +{ + return ( configADJUSTED_HEAP_SIZE - xNextFreeByte ); +} + + + diff --git a/product/rcar/src/CMSIS-FreeRTOS/Source/queue.c b/product/rcar/src/CMSIS-FreeRTOS/Source/queue.c new file mode 100644 index 000000000..b3203b806 --- /dev/null +++ b/product/rcar/src/CMSIS-FreeRTOS/Source/queue.c @@ -0,0 +1,2945 @@ +/* + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +#include +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" + +#if ( configUSE_CO_ROUTINES == 1 ) + #include "croutine.h" +#endif + +/* Lint e9021, e961 and e750 are suppressed as a MISRA exception justified +because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined +for the header files above, but not in this file, in order to generate the +correct privileged Vs unprivileged linkage and placement. */ +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750 !e9021. */ + + +/* Constants used with the cRxLock and cTxLock structure members. */ +#define queueUNLOCKED ( ( int8_t ) -1 ) +#define queueLOCKED_UNMODIFIED ( ( int8_t ) 0 ) + +/* When the Queue_t structure is used to represent a base queue its pcHead and +pcTail members are used as pointers into the queue storage area. When the +Queue_t structure is used to represent a mutex pcHead and pcTail pointers are +not necessary, and the pcHead pointer is set to NULL to indicate that the +structure instead holds a pointer to the mutex holder (if any). Map alternative +names to the pcHead and structure member to ensure the readability of the code +is maintained. The QueuePointers_t and SemaphoreData_t types are used to form +a union as their usage is mutually exclusive dependent on what the queue is +being used for. */ +#define uxQueueType pcHead +#define queueQUEUE_IS_MUTEX NULL + +typedef struct QueuePointers +{ + int8_t *pcTail; /*< Points to the byte at the end of the queue storage area. Once more byte is allocated than necessary to store the queue items, this is used as a marker. */ + int8_t *pcReadFrom; /*< Points to the last place that a queued item was read from when the structure is used as a queue. */ +} QueuePointers_t; + +typedef struct SemaphoreData +{ + TaskHandle_t xMutexHolder; /*< The handle of the task that holds the mutex. */ + UBaseType_t uxRecursiveCallCount;/*< Maintains a count of the number of times a recursive mutex has been recursively 'taken' when the structure is used as a mutex. */ +} SemaphoreData_t; + +/* Semaphores do not actually store or copy data, so have an item size of +zero. */ +#define queueSEMAPHORE_QUEUE_ITEM_LENGTH ( ( UBaseType_t ) 0 ) +#define queueMUTEX_GIVE_BLOCK_TIME ( ( TickType_t ) 0U ) + +#if( configUSE_PREEMPTION == 0 ) + /* If the cooperative scheduler is being used then a yield should not be + performed just because a higher priority task has been woken. */ + #define queueYIELD_IF_USING_PREEMPTION() +#else + #define queueYIELD_IF_USING_PREEMPTION() portYIELD_WITHIN_API() +#endif + +/* + * Definition of the queue used by the scheduler. + * Items are queued by copy, not reference. See the following link for the + * rationale: https://www.freertos.org/Embedded-RTOS-Queues.html + */ +typedef struct QueueDefinition /* The old naming convention is used to prevent breaking kernel aware debuggers. */ +{ + int8_t *pcHead; /*< Points to the beginning of the queue storage area. */ + int8_t *pcWriteTo; /*< Points to the free next place in the storage area. */ + + union + { + QueuePointers_t xQueue; /*< Data required exclusively when this structure is used as a queue. */ + SemaphoreData_t xSemaphore; /*< Data required exclusively when this structure is used as a semaphore. */ + } u; + + List_t xTasksWaitingToSend; /*< List of tasks that are blocked waiting to post onto this queue. Stored in priority order. */ + List_t xTasksWaitingToReceive; /*< List of tasks that are blocked waiting to read from this queue. Stored in priority order. */ + + volatile UBaseType_t uxMessagesWaiting;/*< The number of items currently in the queue. */ + UBaseType_t uxLength; /*< The length of the queue defined as the number of items it will hold, not the number of bytes. */ + UBaseType_t uxItemSize; /*< The size of each items that the queue will hold. */ + + volatile int8_t cRxLock; /*< Stores the number of items received from the queue (removed from the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */ + volatile int8_t cTxLock; /*< Stores the number of items transmitted to the queue (added to the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */ + + #if( ( configSUPPORT_STATIC_ALLOCATION == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the memory used by the queue was statically allocated to ensure no attempt is made to free the memory. */ + #endif + + #if ( configUSE_QUEUE_SETS == 1 ) + struct QueueDefinition *pxQueueSetContainer; + #endif + + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxQueueNumber; + uint8_t ucQueueType; + #endif + +} xQUEUE; + +/* The old xQUEUE name is maintained above then typedefed to the new Queue_t +name below to enable the use of older kernel aware debuggers. */ +typedef xQUEUE Queue_t; + +/*-----------------------------------------------------------*/ + +/* + * The queue registry is just a means for kernel aware debuggers to locate + * queue structures. It has no other purpose so is an optional component. + */ +#if ( configQUEUE_REGISTRY_SIZE > 0 ) + + /* The type stored within the queue registry array. This allows a name + to be assigned to each queue making kernel aware debugging a little + more user friendly. */ + typedef struct QUEUE_REGISTRY_ITEM + { + const char *pcQueueName; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + QueueHandle_t xHandle; + } xQueueRegistryItem; + + /* The old xQueueRegistryItem name is maintained above then typedefed to the + new xQueueRegistryItem name below to enable the use of older kernel aware + debuggers. */ + typedef xQueueRegistryItem QueueRegistryItem_t; + + /* The queue registry is simply an array of QueueRegistryItem_t structures. + The pcQueueName member of a structure being NULL is indicative of the + array position being vacant. */ + PRIVILEGED_DATA QueueRegistryItem_t xQueueRegistry[ configQUEUE_REGISTRY_SIZE ]; + +#endif /* configQUEUE_REGISTRY_SIZE */ + +/* + * Unlocks a queue locked by a call to prvLockQueue. Locking a queue does not + * prevent an ISR from adding or removing items to the queue, but does prevent + * an ISR from removing tasks from the queue event lists. If an ISR finds a + * queue is locked it will instead increment the appropriate queue lock count + * to indicate that a task may require unblocking. When the queue in unlocked + * these lock counts are inspected, and the appropriate action taken. + */ +static void prvUnlockQueue( Queue_t * const pxQueue ) PRIVILEGED_FUNCTION; + +/* + * Uses a critical section to determine if there is any data in a queue. + * + * @return pdTRUE if the queue contains no items, otherwise pdFALSE. + */ +static BaseType_t prvIsQueueEmpty( const Queue_t *pxQueue ) PRIVILEGED_FUNCTION; + +/* + * Uses a critical section to determine if there is any space in a queue. + * + * @return pdTRUE if there is no space, otherwise pdFALSE; + */ +static BaseType_t prvIsQueueFull( const Queue_t *pxQueue ) PRIVILEGED_FUNCTION; + +/* + * Copies an item into the queue, either at the front of the queue or the + * back of the queue. + */ +static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue, const void *pvItemToQueue, const BaseType_t xPosition ) PRIVILEGED_FUNCTION; + +/* + * Copies an item out of a queue. + */ +static void prvCopyDataFromQueue( Queue_t * const pxQueue, void * const pvBuffer ) PRIVILEGED_FUNCTION; + +#if ( configUSE_QUEUE_SETS == 1 ) + /* + * Checks to see if a queue is a member of a queue set, and if so, notifies + * the queue set that the queue contains data. + */ + static BaseType_t prvNotifyQueueSetContainer( const Queue_t * const pxQueue ) PRIVILEGED_FUNCTION; +#endif + +/* + * Called after a Queue_t structure has been allocated either statically or + * dynamically to fill in the structure's members. + */ +static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, const uint8_t ucQueueType, Queue_t *pxNewQueue ) PRIVILEGED_FUNCTION; + +/* + * Mutexes are a special type of queue. When a mutex is created, first the + * queue is created, then prvInitialiseMutex() is called to configure the queue + * as a mutex. + */ +#if( configUSE_MUTEXES == 1 ) + static void prvInitialiseMutex( Queue_t *pxNewQueue ) PRIVILEGED_FUNCTION; +#endif + +#if( configUSE_MUTEXES == 1 ) + /* + * If a task waiting for a mutex causes the mutex holder to inherit a + * priority, but the waiting task times out, then the holder should + * disinherit the priority - but only down to the highest priority of any + * other tasks that are waiting for the same mutex. This function returns + * that priority. + */ + static UBaseType_t prvGetDisinheritPriorityAfterTimeout( const Queue_t * const pxQueue ) PRIVILEGED_FUNCTION; +#endif +/*-----------------------------------------------------------*/ + +/* + * Macro to mark a queue as locked. Locking a queue prevents an ISR from + * accessing the queue event lists. + */ +#define prvLockQueue( pxQueue ) \ + taskENTER_CRITICAL(); \ + { \ + if( ( pxQueue )->cRxLock == queueUNLOCKED ) \ + { \ + ( pxQueue )->cRxLock = queueLOCKED_UNMODIFIED; \ + } \ + if( ( pxQueue )->cTxLock == queueUNLOCKED ) \ + { \ + ( pxQueue )->cTxLock = queueLOCKED_UNMODIFIED; \ + } \ + } \ + taskEXIT_CRITICAL() +/*-----------------------------------------------------------*/ + +BaseType_t xQueueGenericReset( QueueHandle_t xQueue, BaseType_t xNewQueue ) +{ +Queue_t * const pxQueue = xQueue; + + configASSERT( pxQueue ); + + taskENTER_CRITICAL(); + { + pxQueue->u.xQueue.pcTail = pxQueue->pcHead + ( pxQueue->uxLength * pxQueue->uxItemSize ); /*lint !e9016 Pointer arithmetic allowed on char types, especially when it assists conveying intent. */ + pxQueue->uxMessagesWaiting = ( UBaseType_t ) 0U; + pxQueue->pcWriteTo = pxQueue->pcHead; + pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead + ( ( pxQueue->uxLength - 1U ) * pxQueue->uxItemSize ); /*lint !e9016 Pointer arithmetic allowed on char types, especially when it assists conveying intent. */ + pxQueue->cRxLock = queueUNLOCKED; + pxQueue->cTxLock = queueUNLOCKED; + + if( xNewQueue == pdFALSE ) + { + /* If there are tasks blocked waiting to read from the queue, then + the tasks will remain blocked as after this function exits the queue + will still be empty. If there are tasks blocked waiting to write to + the queue, then one should be unblocked as after this function exits + it will be possible to write to it. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* Ensure the event queues start in the correct state. */ + vListInitialise( &( pxQueue->xTasksWaitingToSend ) ); + vListInitialise( &( pxQueue->xTasksWaitingToReceive ) ); + } + } + taskEXIT_CRITICAL(); + + /* A value is returned for calling semantic consistency with previous + versions. */ + return pdPASS; +} +/*-----------------------------------------------------------*/ + +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + + QueueHandle_t xQueueGenericCreateStatic( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, StaticQueue_t *pxStaticQueue, const uint8_t ucQueueType ) + { + Queue_t *pxNewQueue; + + configASSERT( uxQueueLength > ( UBaseType_t ) 0 ); + + /* The StaticQueue_t structure and the queue storage area must be + supplied. */ + configASSERT( pxStaticQueue != NULL ); + + /* A queue storage area should be provided if the item size is not 0, and + should not be provided if the item size is 0. */ + configASSERT( !( ( pucQueueStorage != NULL ) && ( uxItemSize == 0 ) ) ); + configASSERT( !( ( pucQueueStorage == NULL ) && ( uxItemSize != 0 ) ) ); + + #if( configASSERT_DEFINED == 1 ) + { + /* Sanity check that the size of the structure used to declare a + variable of type StaticQueue_t or StaticSemaphore_t equals the size of + the real queue and semaphore structures. */ + volatile size_t xSize = sizeof( StaticQueue_t ); + configASSERT( xSize == sizeof( Queue_t ) ); + ( void ) xSize; /* Keeps lint quiet when configASSERT() is not defined. */ + } + #endif /* configASSERT_DEFINED */ + + /* The address of a statically allocated queue was passed in, use it. + The address of a statically allocated storage area was also passed in + but is already set. */ + pxNewQueue = ( Queue_t * ) pxStaticQueue; /*lint !e740 !e9087 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */ + + if( pxNewQueue != NULL ) + { + #if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + { + /* Queues can be allocated wither statically or dynamically, so + note this queue was allocated statically in case the queue is + later deleted. */ + pxNewQueue->ucStaticallyAllocated = pdTRUE; + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + + prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue ); + } + else + { + traceQUEUE_CREATE_FAILED( ucQueueType ); + mtCOVERAGE_TEST_MARKER(); + } + + return pxNewQueue; + } + +#endif /* configSUPPORT_STATIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + + QueueHandle_t xQueueGenericCreate( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, const uint8_t ucQueueType ) + { + Queue_t *pxNewQueue; + size_t xQueueSizeInBytes; + uint8_t *pucQueueStorage; + + configASSERT( uxQueueLength > ( UBaseType_t ) 0 ); + + /* Allocate enough space to hold the maximum number of items that + can be in the queue at any time. It is valid for uxItemSize to be + zero in the case the queue is used as a semaphore. */ + xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + + /* Allocate the queue and storage area. Justification for MISRA + deviation as follows: pvPortMalloc() always ensures returned memory + blocks are aligned per the requirements of the MCU stack. In this case + pvPortMalloc() must return a pointer that is guaranteed to meet the + alignment requirements of the Queue_t structure - which in this case + is an int8_t *. Therefore, whenever the stack alignment requirements + are greater than or equal to the pointer to char requirements the cast + is safe. In other cases alignment requirements are not strict (one or + two bytes). */ + pxNewQueue = ( Queue_t * ) pvPortMalloc( sizeof( Queue_t ) + xQueueSizeInBytes ); /*lint !e9087 !e9079 see comment above. */ + + if( pxNewQueue != NULL ) + { + /* Jump past the queue structure to find the location of the queue + storage area. */ + pucQueueStorage = ( uint8_t * ) pxNewQueue; + pucQueueStorage += sizeof( Queue_t ); /*lint !e9016 Pointer arithmetic allowed on char types, especially when it assists conveying intent. */ + + #if( configSUPPORT_STATIC_ALLOCATION == 1 ) + { + /* Queues can be created either statically or dynamically, so + note this task was created dynamically in case it is later + deleted. */ + pxNewQueue->ucStaticallyAllocated = pdFALSE; + } + #endif /* configSUPPORT_STATIC_ALLOCATION */ + + prvInitialiseNewQueue( uxQueueLength, uxItemSize, pucQueueStorage, ucQueueType, pxNewQueue ); + } + else + { + traceQUEUE_CREATE_FAILED( ucQueueType ); + mtCOVERAGE_TEST_MARKER(); + } + + return pxNewQueue; + } + +#endif /* configSUPPORT_STATIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +static void prvInitialiseNewQueue( const UBaseType_t uxQueueLength, const UBaseType_t uxItemSize, uint8_t *pucQueueStorage, const uint8_t ucQueueType, Queue_t *pxNewQueue ) +{ + /* Remove compiler warnings about unused parameters should + configUSE_TRACE_FACILITY not be set to 1. */ + ( void ) ucQueueType; + + if( uxItemSize == ( UBaseType_t ) 0 ) + { + /* No RAM was allocated for the queue storage area, but PC head cannot + be set to NULL because NULL is used as a key to say the queue is used as + a mutex. Therefore just set pcHead to point to the queue as a benign + value that is known to be within the memory map. */ + pxNewQueue->pcHead = ( int8_t * ) pxNewQueue; + } + else + { + /* Set the head to the start of the queue storage area. */ + pxNewQueue->pcHead = ( int8_t * ) pucQueueStorage; + } + + /* Initialise the queue members as described where the queue type is + defined. */ + pxNewQueue->uxLength = uxQueueLength; + pxNewQueue->uxItemSize = uxItemSize; + ( void ) xQueueGenericReset( pxNewQueue, pdTRUE ); + + #if ( configUSE_TRACE_FACILITY == 1 ) + { + pxNewQueue->ucQueueType = ucQueueType; + } + #endif /* configUSE_TRACE_FACILITY */ + + #if( configUSE_QUEUE_SETS == 1 ) + { + pxNewQueue->pxQueueSetContainer = NULL; + } + #endif /* configUSE_QUEUE_SETS */ + + traceQUEUE_CREATE( pxNewQueue ); +} +/*-----------------------------------------------------------*/ + +#if( configUSE_MUTEXES == 1 ) + + static void prvInitialiseMutex( Queue_t *pxNewQueue ) + { + if( pxNewQueue != NULL ) + { + /* The queue create function will set all the queue structure members + correctly for a generic queue, but this function is creating a + mutex. Overwrite those members that need to be set differently - + in particular the information required for priority inheritance. */ + pxNewQueue->u.xSemaphore.xMutexHolder = NULL; + pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX; + + /* In case this is a recursive mutex. */ + pxNewQueue->u.xSemaphore.uxRecursiveCallCount = 0; + + traceCREATE_MUTEX( pxNewQueue ); + + /* Start with the semaphore in the expected state. */ + ( void ) xQueueGenericSend( pxNewQueue, NULL, ( TickType_t ) 0U, queueSEND_TO_BACK ); + } + else + { + traceCREATE_MUTEX_FAILED(); + } + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if( ( configUSE_MUTEXES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + + QueueHandle_t xQueueCreateMutex( const uint8_t ucQueueType ) + { + QueueHandle_t xNewQueue; + const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0; + + xNewQueue = xQueueGenericCreate( uxMutexLength, uxMutexSize, ucQueueType ); + prvInitialiseMutex( ( Queue_t * ) xNewQueue ); + + return xNewQueue; + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if( ( configUSE_MUTEXES == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + + QueueHandle_t xQueueCreateMutexStatic( const uint8_t ucQueueType, StaticQueue_t *pxStaticQueue ) + { + QueueHandle_t xNewQueue; + const UBaseType_t uxMutexLength = ( UBaseType_t ) 1, uxMutexSize = ( UBaseType_t ) 0; + + /* Prevent compiler warnings about unused parameters if + configUSE_TRACE_FACILITY does not equal 1. */ + ( void ) ucQueueType; + + xNewQueue = xQueueGenericCreateStatic( uxMutexLength, uxMutexSize, NULL, pxStaticQueue, ucQueueType ); + prvInitialiseMutex( ( Queue_t * ) xNewQueue ); + + return xNewQueue; + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if ( ( configUSE_MUTEXES == 1 ) && ( INCLUDE_xSemaphoreGetMutexHolder == 1 ) ) + + TaskHandle_t xQueueGetMutexHolder( QueueHandle_t xSemaphore ) + { + TaskHandle_t pxReturn; + Queue_t * const pxSemaphore = ( Queue_t * ) xSemaphore; + + /* This function is called by xSemaphoreGetMutexHolder(), and should not + be called directly. Note: This is a good way of determining if the + calling task is the mutex holder, but not a good way of determining the + identity of the mutex holder, as the holder may change between the + following critical section exiting and the function returning. */ + taskENTER_CRITICAL(); + { + if( pxSemaphore->uxQueueType == queueQUEUE_IS_MUTEX ) + { + pxReturn = pxSemaphore->u.xSemaphore.xMutexHolder; + } + else + { + pxReturn = NULL; + } + } + taskEXIT_CRITICAL(); + + return pxReturn; + } /*lint !e818 xSemaphore cannot be a pointer to const because it is a typedef. */ + +#endif +/*-----------------------------------------------------------*/ + +#if ( ( configUSE_MUTEXES == 1 ) && ( INCLUDE_xSemaphoreGetMutexHolder == 1 ) ) + + TaskHandle_t xQueueGetMutexHolderFromISR( QueueHandle_t xSemaphore ) + { + TaskHandle_t pxReturn; + + configASSERT( xSemaphore ); + + /* Mutexes cannot be used in interrupt service routines, so the mutex + holder should not change in an ISR, and therefore a critical section is + not required here. */ + if( ( ( Queue_t * ) xSemaphore )->uxQueueType == queueQUEUE_IS_MUTEX ) + { + pxReturn = ( ( Queue_t * ) xSemaphore )->u.xSemaphore.xMutexHolder; + } + else + { + pxReturn = NULL; + } + + return pxReturn; + } /*lint !e818 xSemaphore cannot be a pointer to const because it is a typedef. */ + +#endif +/*-----------------------------------------------------------*/ + +#if ( configUSE_RECURSIVE_MUTEXES == 1 ) + + BaseType_t xQueueGiveMutexRecursive( QueueHandle_t xMutex ) + { + BaseType_t xReturn; + Queue_t * const pxMutex = ( Queue_t * ) xMutex; + + configASSERT( pxMutex ); + + /* If this is the task that holds the mutex then xMutexHolder will not + change outside of this task. If this task does not hold the mutex then + pxMutexHolder can never coincidentally equal the tasks handle, and as + this is the only condition we are interested in it does not matter if + pxMutexHolder is accessed simultaneously by another task. Therefore no + mutual exclusion is required to test the pxMutexHolder variable. */ + if( pxMutex->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle() ) + { + traceGIVE_MUTEX_RECURSIVE( pxMutex ); + + /* uxRecursiveCallCount cannot be zero if xMutexHolder is equal to + the task handle, therefore no underflow check is required. Also, + uxRecursiveCallCount is only modified by the mutex holder, and as + there can only be one, no mutual exclusion is required to modify the + uxRecursiveCallCount member. */ + ( pxMutex->u.xSemaphore.uxRecursiveCallCount )--; + + /* Has the recursive call count unwound to 0? */ + if( pxMutex->u.xSemaphore.uxRecursiveCallCount == ( UBaseType_t ) 0 ) + { + /* Return the mutex. This will automatically unblock any other + task that might be waiting to access the mutex. */ + ( void ) xQueueGenericSend( pxMutex, NULL, queueMUTEX_GIVE_BLOCK_TIME, queueSEND_TO_BACK ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + xReturn = pdPASS; + } + else + { + /* The mutex cannot be given because the calling task is not the + holder. */ + xReturn = pdFAIL; + + traceGIVE_MUTEX_RECURSIVE_FAILED( pxMutex ); + } + + return xReturn; + } + +#endif /* configUSE_RECURSIVE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_RECURSIVE_MUTEXES == 1 ) + + BaseType_t xQueueTakeMutexRecursive( QueueHandle_t xMutex, TickType_t xTicksToWait ) + { + BaseType_t xReturn; + Queue_t * const pxMutex = ( Queue_t * ) xMutex; + + configASSERT( pxMutex ); + + /* Comments regarding mutual exclusion as per those within + xQueueGiveMutexRecursive(). */ + + traceTAKE_MUTEX_RECURSIVE( pxMutex ); + + if( pxMutex->u.xSemaphore.xMutexHolder == xTaskGetCurrentTaskHandle() ) + { + ( pxMutex->u.xSemaphore.uxRecursiveCallCount )++; + xReturn = pdPASS; + } + else + { + xReturn = xQueueSemaphoreTake( pxMutex, xTicksToWait ); + + /* pdPASS will only be returned if the mutex was successfully + obtained. The calling task may have entered the Blocked state + before reaching here. */ + if( xReturn != pdFAIL ) + { + ( pxMutex->u.xSemaphore.uxRecursiveCallCount )++; + } + else + { + traceTAKE_MUTEX_RECURSIVE_FAILED( pxMutex ); + } + } + + return xReturn; + } + +#endif /* configUSE_RECURSIVE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if( ( configUSE_COUNTING_SEMAPHORES == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + + QueueHandle_t xQueueCreateCountingSemaphoreStatic( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount, StaticQueue_t *pxStaticQueue ) + { + QueueHandle_t xHandle; + + configASSERT( uxMaxCount != 0 ); + configASSERT( uxInitialCount <= uxMaxCount ); + + xHandle = xQueueGenericCreateStatic( uxMaxCount, queueSEMAPHORE_QUEUE_ITEM_LENGTH, NULL, pxStaticQueue, queueQUEUE_TYPE_COUNTING_SEMAPHORE ); + + if( xHandle != NULL ) + { + ( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount; + + traceCREATE_COUNTING_SEMAPHORE(); + } + else + { + traceCREATE_COUNTING_SEMAPHORE_FAILED(); + } + + return xHandle; + } + +#endif /* ( ( configUSE_COUNTING_SEMAPHORES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if( ( configUSE_COUNTING_SEMAPHORES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + + QueueHandle_t xQueueCreateCountingSemaphore( const UBaseType_t uxMaxCount, const UBaseType_t uxInitialCount ) + { + QueueHandle_t xHandle; + + configASSERT( uxMaxCount != 0 ); + configASSERT( uxInitialCount <= uxMaxCount ); + + xHandle = xQueueGenericCreate( uxMaxCount, queueSEMAPHORE_QUEUE_ITEM_LENGTH, queueQUEUE_TYPE_COUNTING_SEMAPHORE ); + + if( xHandle != NULL ) + { + ( ( Queue_t * ) xHandle )->uxMessagesWaiting = uxInitialCount; + + traceCREATE_COUNTING_SEMAPHORE(); + } + else + { + traceCREATE_COUNTING_SEMAPHORE_FAILED(); + } + + return xHandle; + } + +#endif /* ( ( configUSE_COUNTING_SEMAPHORES == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) */ +/*-----------------------------------------------------------*/ + +BaseType_t xQueueGenericSend( QueueHandle_t xQueue, const void * const pvItemToQueue, TickType_t xTicksToWait, const BaseType_t xCopyPosition ) +{ +BaseType_t xEntryTimeSet = pdFALSE, xYieldRequired; +TimeOut_t xTimeOut; +Queue_t * const pxQueue = xQueue; + + configASSERT( pxQueue ); + configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); + configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) ); + #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + { + configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); + } + #endif + + + /*lint -save -e904 This function relaxes the coding standard somewhat to + allow return statements within the function itself. This is done in the + interest of execution time efficiency. */ + for( ;; ) + { + taskENTER_CRITICAL(); + { + /* Is there room on the queue now? The running task must be the + highest priority task wanting to access the queue. If the head item + in the queue is to be overwritten then it does not matter if the + queue is full. */ + if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) ) + { + traceQUEUE_SEND( pxQueue ); + + #if ( configUSE_QUEUE_SETS == 1 ) + { + const UBaseType_t uxPreviousMessagesWaiting = pxQueue->uxMessagesWaiting; + + xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); + + if( pxQueue->pxQueueSetContainer != NULL ) + { + if( ( xCopyPosition == queueOVERWRITE ) && ( uxPreviousMessagesWaiting != ( UBaseType_t ) 0 ) ) + { + /* Do not notify the queue set as an existing item + was overwritten in the queue so the number of items + in the queue has not changed. */ + mtCOVERAGE_TEST_MARKER(); + } + else if( prvNotifyQueueSetContainer( pxQueue ) != pdFALSE ) + { + /* The queue is a member of a queue set, and posting + to the queue set caused a higher priority task to + unblock. A context switch is required. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* If there was a task waiting for data to arrive on the + queue then unblock it now. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The unblocked task has a priority higher than + our own so yield immediately. Yes it is ok to + do this from within the critical section - the + kernel takes care of that. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else if( xYieldRequired != pdFALSE ) + { + /* This path is a special case that will only get + executed if the task was holding multiple mutexes + and the mutexes were given back in an order that is + different to that in which they were taken. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + #else /* configUSE_QUEUE_SETS */ + { + xYieldRequired = prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); + + /* If there was a task waiting for data to arrive on the + queue then unblock it now. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The unblocked task has a priority higher than + our own so yield immediately. Yes it is ok to do + this from within the critical section - the kernel + takes care of that. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else if( xYieldRequired != pdFALSE ) + { + /* This path is a special case that will only get + executed if the task was holding multiple mutexes and + the mutexes were given back in an order that is + different to that in which they were taken. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_QUEUE_SETS */ + + taskEXIT_CRITICAL(); + return pdPASS; + } + else + { + if( xTicksToWait == ( TickType_t ) 0 ) + { + /* The queue was full and no block time is specified (or + the block time has expired) so leave now. */ + taskEXIT_CRITICAL(); + + /* Return to the original privilege level before exiting + the function. */ + traceQUEUE_SEND_FAILED( pxQueue ); + return errQUEUE_FULL; + } + else if( xEntryTimeSet == pdFALSE ) + { + /* The queue was full and a block time was specified so + configure the timeout structure. */ + vTaskInternalSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + else + { + /* Entry time was already set. */ + mtCOVERAGE_TEST_MARKER(); + } + } + } + taskEXIT_CRITICAL(); + + /* Interrupts and other tasks can send to and receive from the queue + now the critical section has been exited. */ + + vTaskSuspendAll(); + prvLockQueue( pxQueue ); + + /* Update the timeout state to see if it has expired yet. */ + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + if( prvIsQueueFull( pxQueue ) != pdFALSE ) + { + traceBLOCKING_ON_QUEUE_SEND( pxQueue ); + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait ); + + /* Unlocking the queue means queue events can effect the + event list. It is possible that interrupts occurring now + remove this task from the event list again - but as the + scheduler is suspended the task will go onto the pending + ready last instead of the actual ready list. */ + prvUnlockQueue( pxQueue ); + + /* Resuming the scheduler will move tasks from the pending + ready list into the ready list - so it is feasible that this + task is already in a ready list before it yields - in which + case the yield will not cause a context switch unless there + is also a higher priority task in the pending ready list. */ + if( xTaskResumeAll() == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + } + else + { + /* Try again. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + } + } + else + { + /* The timeout has expired. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + + traceQUEUE_SEND_FAILED( pxQueue ); + return errQUEUE_FULL; + } + } /*lint -restore */ +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueueGenericSendFromISR( QueueHandle_t xQueue, const void * const pvItemToQueue, BaseType_t * const pxHigherPriorityTaskWoken, const BaseType_t xCopyPosition ) +{ +BaseType_t xReturn; +UBaseType_t uxSavedInterruptStatus; +Queue_t * const pxQueue = xQueue; + + configASSERT( pxQueue ); + configASSERT( !( ( pvItemToQueue == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); + configASSERT( !( ( xCopyPosition == queueOVERWRITE ) && ( pxQueue->uxLength != 1 ) ) ); + + /* RTOS ports that support interrupt nesting have the concept of a maximum + system call (or maximum API call) interrupt priority. Interrupts that are + above the maximum system call priority are kept permanently enabled, even + when the RTOS kernel is in a critical section, but cannot make any calls to + FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h + then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + failure if a FreeRTOS API function is called from an interrupt that has been + assigned a priority above the configured maximum system call priority. + Only FreeRTOS functions that end in FromISR can be called from interrupts + that have been assigned a priority at or (logically) below the maximum + system call interrupt priority. FreeRTOS maintains a separate interrupt + safe API to ensure interrupt entry is as fast and as simple as possible. + More information (albeit Cortex-M specific) is provided on the following + link: http://www.freertos.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + /* Similar to xQueueGenericSend, except without blocking if there is no room + in the queue. Also don't directly wake a task that was blocked on a queue + read, instead return a flag to say whether a context switch is required or + not (i.e. has a task with a higher priority than us been woken by this + post). */ + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + if( ( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) || ( xCopyPosition == queueOVERWRITE ) ) + { + const int8_t cTxLock = pxQueue->cTxLock; + const UBaseType_t uxPreviousMessagesWaiting = pxQueue->uxMessagesWaiting; + + traceQUEUE_SEND_FROM_ISR( pxQueue ); + + /* Semaphores use xQueueGiveFromISR(), so pxQueue will not be a + semaphore or mutex. That means prvCopyDataToQueue() cannot result + in a task disinheriting a priority and prvCopyDataToQueue() can be + called here even though the disinherit function does not check if + the scheduler is suspended before accessing the ready lists. */ + ( void ) prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); + + /* The event list is not altered if the queue is locked. This will + be done when the queue is unlocked later. */ + if( cTxLock == queueUNLOCKED ) + { + #if ( configUSE_QUEUE_SETS == 1 ) + { + if( pxQueue->pxQueueSetContainer != NULL ) + { + if( ( xCopyPosition == queueOVERWRITE ) && ( uxPreviousMessagesWaiting != ( UBaseType_t ) 0 ) ) + { + /* Do not notify the queue set as an existing item + was overwritten in the queue so the number of items + in the queue has not changed. */ + mtCOVERAGE_TEST_MARKER(); + } + else if( prvNotifyQueueSetContainer( pxQueue ) != pdFALSE ) + { + /* The queue is a member of a queue set, and posting + to the queue set caused a higher priority task to + unblock. A context switch is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so + record that a context switch is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + #else /* configUSE_QUEUE_SETS */ + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so record that a + context switch is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Not used in this path. */ + ( void ) uxPreviousMessagesWaiting; + } + #endif /* configUSE_QUEUE_SETS */ + } + else + { + /* Increment the lock count so the task that unlocks the queue + knows that data was posted while it was locked. */ + pxQueue->cTxLock = ( int8_t ) ( cTxLock + 1 ); + } + + xReturn = pdPASS; + } + else + { + traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue ); + xReturn = errQUEUE_FULL; + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueueGiveFromISR( QueueHandle_t xQueue, BaseType_t * const pxHigherPriorityTaskWoken ) +{ +BaseType_t xReturn; +UBaseType_t uxSavedInterruptStatus; +Queue_t * const pxQueue = xQueue; + + /* Similar to xQueueGenericSendFromISR() but used with semaphores where the + item size is 0. Don't directly wake a task that was blocked on a queue + read, instead return a flag to say whether a context switch is required or + not (i.e. has a task with a higher priority than us been woken by this + post). */ + + configASSERT( pxQueue ); + + /* xQueueGenericSendFromISR() should be used instead of xQueueGiveFromISR() + if the item size is not 0. */ + configASSERT( pxQueue->uxItemSize == 0 ); + + /* Normally a mutex would not be given from an interrupt, especially if + there is a mutex holder, as priority inheritance makes no sense for an + interrupts, only tasks. */ + configASSERT( !( ( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) && ( pxQueue->u.xSemaphore.xMutexHolder != NULL ) ) ); + + /* RTOS ports that support interrupt nesting have the concept of a maximum + system call (or maximum API call) interrupt priority. Interrupts that are + above the maximum system call priority are kept permanently enabled, even + when the RTOS kernel is in a critical section, but cannot make any calls to + FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h + then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + failure if a FreeRTOS API function is called from an interrupt that has been + assigned a priority above the configured maximum system call priority. + Only FreeRTOS functions that end in FromISR can be called from interrupts + that have been assigned a priority at or (logically) below the maximum + system call interrupt priority. FreeRTOS maintains a separate interrupt + safe API to ensure interrupt entry is as fast and as simple as possible. + More information (albeit Cortex-M specific) is provided on the following + link: http://www.freertos.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting; + + /* When the queue is used to implement a semaphore no data is ever + moved through the queue but it is still valid to see if the queue 'has + space'. */ + if( uxMessagesWaiting < pxQueue->uxLength ) + { + const int8_t cTxLock = pxQueue->cTxLock; + + traceQUEUE_SEND_FROM_ISR( pxQueue ); + + /* A task can only have an inherited priority if it is a mutex + holder - and if there is a mutex holder then the mutex cannot be + given from an ISR. As this is the ISR version of the function it + can be assumed there is no mutex holder and no need to determine if + priority disinheritance is needed. Simply increase the count of + messages (semaphores) available. */ + pxQueue->uxMessagesWaiting = uxMessagesWaiting + ( UBaseType_t ) 1; + + /* The event list is not altered if the queue is locked. This will + be done when the queue is unlocked later. */ + if( cTxLock == queueUNLOCKED ) + { + #if ( configUSE_QUEUE_SETS == 1 ) + { + if( pxQueue->pxQueueSetContainer != NULL ) + { + if( prvNotifyQueueSetContainer( pxQueue ) != pdFALSE ) + { + /* The semaphore is a member of a queue set, and + posting to the queue set caused a higher priority + task to unblock. A context switch is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so + record that a context switch is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + #else /* configUSE_QUEUE_SETS */ + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so record that a + context switch is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_QUEUE_SETS */ + } + else + { + /* Increment the lock count so the task that unlocks the queue + knows that data was posted while it was locked. */ + pxQueue->cTxLock = ( int8_t ) ( cTxLock + 1 ); + } + + xReturn = pdPASS; + } + else + { + traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue ); + xReturn = errQUEUE_FULL; + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ) +{ +BaseType_t xEntryTimeSet = pdFALSE; +TimeOut_t xTimeOut; +Queue_t * const pxQueue = xQueue; + + /* Check the pointer is not NULL. */ + configASSERT( ( pxQueue ) ); + + /* The buffer into which data is received can only be NULL if the data size + is zero (so no data is copied into the buffer. */ + configASSERT( !( ( ( pvBuffer ) == NULL ) && ( ( pxQueue )->uxItemSize != ( UBaseType_t ) 0U ) ) ); + + /* Cannot block if the scheduler is suspended. */ + #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + { + configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); + } + #endif + + + /*lint -save -e904 This function relaxes the coding standard somewhat to + allow return statements within the function itself. This is done in the + interest of execution time efficiency. */ + for( ;; ) + { + taskENTER_CRITICAL(); + { + const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting; + + /* Is there data in the queue now? To be running the calling task + must be the highest priority task wanting to access the queue. */ + if( uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + /* Data available, remove one item. */ + prvCopyDataFromQueue( pxQueue, pvBuffer ); + traceQUEUE_RECEIVE( pxQueue ); + pxQueue->uxMessagesWaiting = uxMessagesWaiting - ( UBaseType_t ) 1; + + /* There is now space in the queue, were any tasks waiting to + post to the queue? If so, unblock the highest priority waiting + task. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + taskEXIT_CRITICAL(); + return pdPASS; + } + else + { + if( xTicksToWait == ( TickType_t ) 0 ) + { + /* The queue was empty and no block time is specified (or + the block time has expired) so leave now. */ + taskEXIT_CRITICAL(); + traceQUEUE_RECEIVE_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else if( xEntryTimeSet == pdFALSE ) + { + /* The queue was empty and a block time was specified so + configure the timeout structure. */ + vTaskInternalSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + else + { + /* Entry time was already set. */ + mtCOVERAGE_TEST_MARKER(); + } + } + } + taskEXIT_CRITICAL(); + + /* Interrupts and other tasks can send to and receive from the queue + now the critical section has been exited. */ + + vTaskSuspendAll(); + prvLockQueue( pxQueue ); + + /* Update the timeout state to see if it has expired yet. */ + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + /* The timeout has not expired. If the queue is still empty place + the task on the list of tasks waiting to receive from the queue. */ + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ); + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); + prvUnlockQueue( pxQueue ); + if( xTaskResumeAll() == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* The queue contains data again. Loop back to try and read the + data. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + } + } + else + { + /* Timed out. If there is no data in the queue exit, otherwise loop + back and attempt to read the data. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceQUEUE_RECEIVE_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } /*lint -restore */ +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueueSemaphoreTake( QueueHandle_t xQueue, TickType_t xTicksToWait ) +{ +BaseType_t xEntryTimeSet = pdFALSE; +TimeOut_t xTimeOut; +Queue_t * const pxQueue = xQueue; + +#if( configUSE_MUTEXES == 1 ) + BaseType_t xInheritanceOccurred = pdFALSE; +#endif + + /* Check the queue pointer is not NULL. */ + configASSERT( ( pxQueue ) ); + + /* Check this really is a semaphore, in which case the item size will be + 0. */ + configASSERT( pxQueue->uxItemSize == 0 ); + + /* Cannot block if the scheduler is suspended. */ + #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + { + configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); + } + #endif + + + /*lint -save -e904 This function relaxes the coding standard somewhat to allow return + statements within the function itself. This is done in the interest + of execution time efficiency. */ + for( ;; ) + { + taskENTER_CRITICAL(); + { + /* Semaphores are queues with an item size of 0, and where the + number of messages in the queue is the semaphore's count value. */ + const UBaseType_t uxSemaphoreCount = pxQueue->uxMessagesWaiting; + + /* Is there data in the queue now? To be running the calling task + must be the highest priority task wanting to access the queue. */ + if( uxSemaphoreCount > ( UBaseType_t ) 0 ) + { + traceQUEUE_RECEIVE( pxQueue ); + + /* Semaphores are queues with a data size of zero and where the + messages waiting is the semaphore's count. Reduce the count. */ + pxQueue->uxMessagesWaiting = uxSemaphoreCount - ( UBaseType_t ) 1; + + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) + { + /* Record the information required to implement + priority inheritance should it become necessary. */ + pxQueue->u.xSemaphore.xMutexHolder = pvTaskIncrementMutexHeldCount(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_MUTEXES */ + + /* Check to see if other tasks are blocked waiting to give the + semaphore, and if so, unblock the highest priority such task. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + taskEXIT_CRITICAL(); + return pdPASS; + } + else + { + if( xTicksToWait == ( TickType_t ) 0 ) + { + /* For inheritance to have occurred there must have been an + initial timeout, and an adjusted timeout cannot become 0, as + if it were 0 the function would have exited. */ + #if( configUSE_MUTEXES == 1 ) + { + configASSERT( xInheritanceOccurred == pdFALSE ); + } + #endif /* configUSE_MUTEXES */ + + /* The semaphore count was 0 and no block time is specified + (or the block time has expired) so exit now. */ + taskEXIT_CRITICAL(); + traceQUEUE_RECEIVE_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else if( xEntryTimeSet == pdFALSE ) + { + /* The semaphore count was 0 and a block time was specified + so configure the timeout structure ready to block. */ + vTaskInternalSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + else + { + /* Entry time was already set. */ + mtCOVERAGE_TEST_MARKER(); + } + } + } + taskEXIT_CRITICAL(); + + /* Interrupts and other tasks can give to and take from the semaphore + now the critical section has been exited. */ + + vTaskSuspendAll(); + prvLockQueue( pxQueue ); + + /* Update the timeout state to see if it has expired yet. */ + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + /* A block time is specified and not expired. If the semaphore + count is 0 then enter the Blocked state to wait for a semaphore to + become available. As semaphores are implemented with queues the + queue being empty is equivalent to the semaphore count being 0. */ + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ); + + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) + { + taskENTER_CRITICAL(); + { + xInheritanceOccurred = xTaskPriorityInherit( pxQueue->u.xSemaphore.xMutexHolder ); + } + taskEXIT_CRITICAL(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif + + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); + prvUnlockQueue( pxQueue ); + if( xTaskResumeAll() == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* There was no timeout and the semaphore count was not 0, so + attempt to take the semaphore again. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + } + } + else + { + /* Timed out. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + + /* If the semaphore count is 0 exit now as the timeout has + expired. Otherwise return to attempt to take the semaphore that is + known to be available. As semaphores are implemented by queues the + queue being empty is equivalent to the semaphore count being 0. */ + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + #if ( configUSE_MUTEXES == 1 ) + { + /* xInheritanceOccurred could only have be set if + pxQueue->uxQueueType == queueQUEUE_IS_MUTEX so no need to + test the mutex type again to check it is actually a mutex. */ + if( xInheritanceOccurred != pdFALSE ) + { + taskENTER_CRITICAL(); + { + UBaseType_t uxHighestWaitingPriority; + + /* This task blocking on the mutex caused another + task to inherit this task's priority. Now this task + has timed out the priority should be disinherited + again, but only as low as the next highest priority + task that is waiting for the same mutex. */ + uxHighestWaitingPriority = prvGetDisinheritPriorityAfterTimeout( pxQueue ); + vTaskPriorityDisinheritAfterTimeout( pxQueue->u.xSemaphore.xMutexHolder, uxHighestWaitingPriority ); + } + taskEXIT_CRITICAL(); + } + } + #endif /* configUSE_MUTEXES */ + + traceQUEUE_RECEIVE_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } /*lint -restore */ +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueuePeek( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait ) +{ +BaseType_t xEntryTimeSet = pdFALSE; +TimeOut_t xTimeOut; +int8_t *pcOriginalReadPosition; +Queue_t * const pxQueue = xQueue; + + /* Check the pointer is not NULL. */ + configASSERT( ( pxQueue ) ); + + /* The buffer into which data is received can only be NULL if the data size + is zero (so no data is copied into the buffer. */ + configASSERT( !( ( ( pvBuffer ) == NULL ) && ( ( pxQueue )->uxItemSize != ( UBaseType_t ) 0U ) ) ); + + /* Cannot block if the scheduler is suspended. */ + #if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + { + configASSERT( !( ( xTaskGetSchedulerState() == taskSCHEDULER_SUSPENDED ) && ( xTicksToWait != 0 ) ) ); + } + #endif + + + /*lint -save -e904 This function relaxes the coding standard somewhat to + allow return statements within the function itself. This is done in the + interest of execution time efficiency. */ + for( ;; ) + { + taskENTER_CRITICAL(); + { + const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting; + + /* Is there data in the queue now? To be running the calling task + must be the highest priority task wanting to access the queue. */ + if( uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + /* Remember the read position so it can be reset after the data + is read from the queue as this function is only peeking the + data, not removing it. */ + pcOriginalReadPosition = pxQueue->u.xQueue.pcReadFrom; + + prvCopyDataFromQueue( pxQueue, pvBuffer ); + traceQUEUE_PEEK( pxQueue ); + + /* The data is not being removed, so reset the read pointer. */ + pxQueue->u.xQueue.pcReadFrom = pcOriginalReadPosition; + + /* The data is being left in the queue, so see if there are + any other tasks waiting for the data. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority than this task. */ + queueYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + taskEXIT_CRITICAL(); + return pdPASS; + } + else + { + if( xTicksToWait == ( TickType_t ) 0 ) + { + /* The queue was empty and no block time is specified (or + the block time has expired) so leave now. */ + taskEXIT_CRITICAL(); + traceQUEUE_PEEK_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else if( xEntryTimeSet == pdFALSE ) + { + /* The queue was empty and a block time was specified so + configure the timeout structure ready to enter the blocked + state. */ + vTaskInternalSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + else + { + /* Entry time was already set. */ + mtCOVERAGE_TEST_MARKER(); + } + } + } + taskEXIT_CRITICAL(); + + /* Interrupts and other tasks can send to and receive from the queue + now the critical section has been exited. */ + + vTaskSuspendAll(); + prvLockQueue( pxQueue ); + + /* Update the timeout state to see if it has expired yet. */ + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + /* Timeout has not expired yet, check to see if there is data in the + queue now, and if not enter the Blocked state to wait for data. */ + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceBLOCKING_ON_QUEUE_PEEK( pxQueue ); + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); + prvUnlockQueue( pxQueue ); + if( xTaskResumeAll() == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* There is data in the queue now, so don't enter the blocked + state, instead return to try and obtain the data. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + } + } + else + { + /* The timeout has expired. If there is still no data in the queue + exit, otherwise go back and try to read the data again. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + + if( prvIsQueueEmpty( pxQueue ) != pdFALSE ) + { + traceQUEUE_PEEK_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } /*lint -restore */ +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueueReceiveFromISR( QueueHandle_t xQueue, void * const pvBuffer, BaseType_t * const pxHigherPriorityTaskWoken ) +{ +BaseType_t xReturn; +UBaseType_t uxSavedInterruptStatus; +Queue_t * const pxQueue = xQueue; + + configASSERT( pxQueue ); + configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); + + /* RTOS ports that support interrupt nesting have the concept of a maximum + system call (or maximum API call) interrupt priority. Interrupts that are + above the maximum system call priority are kept permanently enabled, even + when the RTOS kernel is in a critical section, but cannot make any calls to + FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h + then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + failure if a FreeRTOS API function is called from an interrupt that has been + assigned a priority above the configured maximum system call priority. + Only FreeRTOS functions that end in FromISR can be called from interrupts + that have been assigned a priority at or (logically) below the maximum + system call interrupt priority. FreeRTOS maintains a separate interrupt + safe API to ensure interrupt entry is as fast and as simple as possible. + More information (albeit Cortex-M specific) is provided on the following + link: http://www.freertos.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + const UBaseType_t uxMessagesWaiting = pxQueue->uxMessagesWaiting; + + /* Cannot block in an ISR, so check there is data available. */ + if( uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + const int8_t cRxLock = pxQueue->cRxLock; + + traceQUEUE_RECEIVE_FROM_ISR( pxQueue ); + + prvCopyDataFromQueue( pxQueue, pvBuffer ); + pxQueue->uxMessagesWaiting = uxMessagesWaiting - ( UBaseType_t ) 1; + + /* If the queue is locked the event list will not be modified. + Instead update the lock count so the task that unlocks the queue + will know that an ISR has removed data while the queue was + locked. */ + if( cRxLock == queueUNLOCKED ) + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + /* The task waiting has a higher priority than us so + force a context switch. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* Increment the lock count so the task that unlocks the queue + knows that data was removed while it was locked. */ + pxQueue->cRxLock = ( int8_t ) ( cRxLock + 1 ); + } + + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + traceQUEUE_RECEIVE_FROM_ISR_FAILED( pxQueue ); + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueuePeekFromISR( QueueHandle_t xQueue, void * const pvBuffer ) +{ +BaseType_t xReturn; +UBaseType_t uxSavedInterruptStatus; +int8_t *pcOriginalReadPosition; +Queue_t * const pxQueue = xQueue; + + configASSERT( pxQueue ); + configASSERT( !( ( pvBuffer == NULL ) && ( pxQueue->uxItemSize != ( UBaseType_t ) 0U ) ) ); + configASSERT( pxQueue->uxItemSize != 0 ); /* Can't peek a semaphore. */ + + /* RTOS ports that support interrupt nesting have the concept of a maximum + system call (or maximum API call) interrupt priority. Interrupts that are + above the maximum system call priority are kept permanently enabled, even + when the RTOS kernel is in a critical section, but cannot make any calls to + FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h + then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + failure if a FreeRTOS API function is called from an interrupt that has been + assigned a priority above the configured maximum system call priority. + Only FreeRTOS functions that end in FromISR can be called from interrupts + that have been assigned a priority at or (logically) below the maximum + system call interrupt priority. FreeRTOS maintains a separate interrupt + safe API to ensure interrupt entry is as fast and as simple as possible. + More information (albeit Cortex-M specific) is provided on the following + link: http://www.freertos.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + /* Cannot block in an ISR, so check there is data available. */ + if( pxQueue->uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + traceQUEUE_PEEK_FROM_ISR( pxQueue ); + + /* Remember the read position so it can be reset as nothing is + actually being removed from the queue. */ + pcOriginalReadPosition = pxQueue->u.xQueue.pcReadFrom; + prvCopyDataFromQueue( pxQueue, pvBuffer ); + pxQueue->u.xQueue.pcReadFrom = pcOriginalReadPosition; + + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + traceQUEUE_PEEK_FROM_ISR_FAILED( pxQueue ); + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +UBaseType_t uxQueueMessagesWaiting( const QueueHandle_t xQueue ) +{ +UBaseType_t uxReturn; + + configASSERT( xQueue ); + + taskENTER_CRITICAL(); + { + uxReturn = ( ( Queue_t * ) xQueue )->uxMessagesWaiting; + } + taskEXIT_CRITICAL(); + + return uxReturn; +} /*lint !e818 Pointer cannot be declared const as xQueue is a typedef not pointer. */ +/*-----------------------------------------------------------*/ + +UBaseType_t uxQueueSpacesAvailable( const QueueHandle_t xQueue ) +{ +UBaseType_t uxReturn; +Queue_t * const pxQueue = xQueue; + + configASSERT( pxQueue ); + + taskENTER_CRITICAL(); + { + uxReturn = pxQueue->uxLength - pxQueue->uxMessagesWaiting; + } + taskEXIT_CRITICAL(); + + return uxReturn; +} /*lint !e818 Pointer cannot be declared const as xQueue is a typedef not pointer. */ +/*-----------------------------------------------------------*/ + +UBaseType_t uxQueueMessagesWaitingFromISR( const QueueHandle_t xQueue ) +{ +UBaseType_t uxReturn; +Queue_t * const pxQueue = xQueue; + + configASSERT( pxQueue ); + uxReturn = pxQueue->uxMessagesWaiting; + + return uxReturn; +} /*lint !e818 Pointer cannot be declared const as xQueue is a typedef not pointer. */ +/*-----------------------------------------------------------*/ + +void vQueueDelete( QueueHandle_t xQueue ) +{ +Queue_t * const pxQueue = xQueue; + + configASSERT( pxQueue ); + traceQUEUE_DELETE( pxQueue ); + + #if ( configQUEUE_REGISTRY_SIZE > 0 ) + { + vQueueUnregisterQueue( pxQueue ); + } + #endif + + #if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) ) + { + /* The queue can only have been allocated dynamically - free it + again. */ + vPortFree( pxQueue ); + } + #elif( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + { + /* The queue could have been allocated statically or dynamically, so + check before attempting to free the memory. */ + if( pxQueue->ucStaticallyAllocated == ( uint8_t ) pdFALSE ) + { + vPortFree( pxQueue ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #else + { + /* The queue must have been statically allocated, so is not going to be + deleted. Avoid compiler warnings about the unused parameter. */ + ( void ) pxQueue; + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + UBaseType_t uxQueueGetQueueNumber( QueueHandle_t xQueue ) + { + return ( ( Queue_t * ) xQueue )->uxQueueNumber; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + void vQueueSetQueueNumber( QueueHandle_t xQueue, UBaseType_t uxQueueNumber ) + { + ( ( Queue_t * ) xQueue )->uxQueueNumber = uxQueueNumber; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + uint8_t ucQueueGetQueueType( QueueHandle_t xQueue ) + { + return ( ( Queue_t * ) xQueue )->ucQueueType; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if( configUSE_MUTEXES == 1 ) + + static UBaseType_t prvGetDisinheritPriorityAfterTimeout( const Queue_t * const pxQueue ) + { + UBaseType_t uxHighestPriorityOfWaitingTasks; + + /* If a task waiting for a mutex causes the mutex holder to inherit a + priority, but the waiting task times out, then the holder should + disinherit the priority - but only down to the highest priority of any + other tasks that are waiting for the same mutex. For this purpose, + return the priority of the highest priority task that is waiting for the + mutex. */ + if( listCURRENT_LIST_LENGTH( &( pxQueue->xTasksWaitingToReceive ) ) > 0U ) + { + uxHighestPriorityOfWaitingTasks = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) listGET_ITEM_VALUE_OF_HEAD_ENTRY( &( pxQueue->xTasksWaitingToReceive ) ); + } + else + { + uxHighestPriorityOfWaitingTasks = tskIDLE_PRIORITY; + } + + return uxHighestPriorityOfWaitingTasks; + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +static BaseType_t prvCopyDataToQueue( Queue_t * const pxQueue, const void *pvItemToQueue, const BaseType_t xPosition ) +{ +BaseType_t xReturn = pdFALSE; +UBaseType_t uxMessagesWaiting; + + /* This function is called from a critical section. */ + + uxMessagesWaiting = pxQueue->uxMessagesWaiting; + + if( pxQueue->uxItemSize == ( UBaseType_t ) 0 ) + { + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) + { + /* The mutex is no longer being held. */ + xReturn = xTaskPriorityDisinherit( pxQueue->u.xSemaphore.xMutexHolder ); + pxQueue->u.xSemaphore.xMutexHolder = NULL; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_MUTEXES */ + } + else if( xPosition == queueSEND_TO_BACK ) + { + ( void ) memcpy( ( void * ) pxQueue->pcWriteTo, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 !e418 !e9087 MISRA exception as the casts are only redundant for some ports, plus previous logic ensures a null pointer can only be passed to memcpy() if the copy size is 0. Cast to void required by function signature and safe as no alignment requirement and copy length specified in bytes. */ + pxQueue->pcWriteTo += pxQueue->uxItemSize; /*lint !e9016 Pointer arithmetic on char types ok, especially in this use case where it is the clearest way of conveying intent. */ + if( pxQueue->pcWriteTo >= pxQueue->u.xQueue.pcTail ) /*lint !e946 MISRA exception justified as comparison of pointers is the cleanest solution. */ + { + pxQueue->pcWriteTo = pxQueue->pcHead; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + ( void ) memcpy( ( void * ) pxQueue->u.xQueue.pcReadFrom, pvItemToQueue, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 !e9087 !e418 MISRA exception as the casts are only redundant for some ports. Cast to void required by function signature and safe as no alignment requirement and copy length specified in bytes. Assert checks null pointer only used when length is 0. */ + pxQueue->u.xQueue.pcReadFrom -= pxQueue->uxItemSize; + if( pxQueue->u.xQueue.pcReadFrom < pxQueue->pcHead ) /*lint !e946 MISRA exception justified as comparison of pointers is the cleanest solution. */ + { + pxQueue->u.xQueue.pcReadFrom = ( pxQueue->u.xQueue.pcTail - pxQueue->uxItemSize ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( xPosition == queueOVERWRITE ) + { + if( uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + /* An item is not being added but overwritten, so subtract + one from the recorded number of items in the queue so when + one is added again below the number of recorded items remains + correct. */ + --uxMessagesWaiting; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + pxQueue->uxMessagesWaiting = uxMessagesWaiting + ( UBaseType_t ) 1; + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static void prvCopyDataFromQueue( Queue_t * const pxQueue, void * const pvBuffer ) +{ + if( pxQueue->uxItemSize != ( UBaseType_t ) 0 ) + { + pxQueue->u.xQueue.pcReadFrom += pxQueue->uxItemSize; /*lint !e9016 Pointer arithmetic on char types ok, especially in this use case where it is the clearest way of conveying intent. */ + if( pxQueue->u.xQueue.pcReadFrom >= pxQueue->u.xQueue.pcTail ) /*lint !e946 MISRA exception justified as use of the relational operator is the cleanest solutions. */ + { + pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.xQueue.pcReadFrom, ( size_t ) pxQueue->uxItemSize ); /*lint !e961 !e418 !e9087 MISRA exception as the casts are only redundant for some ports. Also previous logic ensures a null pointer can only be passed to memcpy() when the count is 0. Cast to void required by function signature and safe as no alignment requirement and copy length specified in bytes. */ + } +} +/*-----------------------------------------------------------*/ + +static void prvUnlockQueue( Queue_t * const pxQueue ) +{ + /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. */ + + /* The lock counts contains the number of extra data items placed or + removed from the queue while the queue was locked. When a queue is + locked items can be added or removed, but the event lists cannot be + updated. */ + taskENTER_CRITICAL(); + { + int8_t cTxLock = pxQueue->cTxLock; + + /* See if data was added to the queue while it was locked. */ + while( cTxLock > queueLOCKED_UNMODIFIED ) + { + /* Data was posted while the queue was locked. Are any tasks + blocked waiting for data to become available? */ + #if ( configUSE_QUEUE_SETS == 1 ) + { + if( pxQueue->pxQueueSetContainer != NULL ) + { + if( prvNotifyQueueSetContainer( pxQueue ) != pdFALSE ) + { + /* The queue is a member of a queue set, and posting to + the queue set caused a higher priority task to unblock. + A context switch is required. */ + vTaskMissedYield(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* Tasks that are removed from the event list will get + added to the pending ready list as the scheduler is still + suspended. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so record that a + context switch is required. */ + vTaskMissedYield(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + break; + } + } + } + #else /* configUSE_QUEUE_SETS */ + { + /* Tasks that are removed from the event list will get added to + the pending ready list as the scheduler is still suspended. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so record that + a context switch is required. */ + vTaskMissedYield(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + break; + } + } + #endif /* configUSE_QUEUE_SETS */ + + --cTxLock; + } + + pxQueue->cTxLock = queueUNLOCKED; + } + taskEXIT_CRITICAL(); + + /* Do the same for the Rx lock. */ + taskENTER_CRITICAL(); + { + int8_t cRxLock = pxQueue->cRxLock; + + while( cRxLock > queueLOCKED_UNMODIFIED ) + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + vTaskMissedYield(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + --cRxLock; + } + else + { + break; + } + } + + pxQueue->cRxLock = queueUNLOCKED; + } + taskEXIT_CRITICAL(); +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvIsQueueEmpty( const Queue_t *pxQueue ) +{ +BaseType_t xReturn; + + taskENTER_CRITICAL(); + { + if( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0 ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + } + taskEXIT_CRITICAL(); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueueIsQueueEmptyFromISR( const QueueHandle_t xQueue ) +{ +BaseType_t xReturn; +Queue_t * const pxQueue = xQueue; + + configASSERT( pxQueue ); + if( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0 ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} /*lint !e818 xQueue could not be pointer to const because it is a typedef. */ +/*-----------------------------------------------------------*/ + +static BaseType_t prvIsQueueFull( const Queue_t *pxQueue ) +{ +BaseType_t xReturn; + + taskENTER_CRITICAL(); + { + if( pxQueue->uxMessagesWaiting == pxQueue->uxLength ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + } + taskEXIT_CRITICAL(); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +BaseType_t xQueueIsQueueFullFromISR( const QueueHandle_t xQueue ) +{ +BaseType_t xReturn; +Queue_t * const pxQueue = xQueue; + + configASSERT( pxQueue ); + if( pxQueue->uxMessagesWaiting == pxQueue->uxLength ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} /*lint !e818 xQueue could not be pointer to const because it is a typedef. */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_CO_ROUTINES == 1 ) + + BaseType_t xQueueCRSend( QueueHandle_t xQueue, const void *pvItemToQueue, TickType_t xTicksToWait ) + { + BaseType_t xReturn; + Queue_t * const pxQueue = xQueue; + + /* If the queue is already full we may have to block. A critical section + is required to prevent an interrupt removing something from the queue + between the check to see if the queue is full and blocking on the queue. */ + portDISABLE_INTERRUPTS(); + { + if( prvIsQueueFull( pxQueue ) != pdFALSE ) + { + /* The queue is full - do we want to block or just leave without + posting? */ + if( xTicksToWait > ( TickType_t ) 0 ) + { + /* As this is called from a coroutine we cannot block directly, but + return indicating that we need to block. */ + vCoRoutineAddToDelayedList( xTicksToWait, &( pxQueue->xTasksWaitingToSend ) ); + portENABLE_INTERRUPTS(); + return errQUEUE_BLOCKED; + } + else + { + portENABLE_INTERRUPTS(); + return errQUEUE_FULL; + } + } + } + portENABLE_INTERRUPTS(); + + portDISABLE_INTERRUPTS(); + { + if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) + { + /* There is room in the queue, copy the data into the queue. */ + prvCopyDataToQueue( pxQueue, pvItemToQueue, queueSEND_TO_BACK ); + xReturn = pdPASS; + + /* Were any co-routines waiting for data to become available? */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + /* In this instance the co-routine could be placed directly + into the ready list as we are within a critical section. + Instead the same pending ready list mechanism is used as if + the event were caused from within an interrupt. */ + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The co-routine waiting has a higher priority so record + that a yield might be appropriate. */ + xReturn = errQUEUE_YIELD; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + xReturn = errQUEUE_FULL; + } + } + portENABLE_INTERRUPTS(); + + return xReturn; + } + +#endif /* configUSE_CO_ROUTINES */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_CO_ROUTINES == 1 ) + + BaseType_t xQueueCRReceive( QueueHandle_t xQueue, void *pvBuffer, TickType_t xTicksToWait ) + { + BaseType_t xReturn; + Queue_t * const pxQueue = xQueue; + + /* If the queue is already empty we may have to block. A critical section + is required to prevent an interrupt adding something to the queue + between the check to see if the queue is empty and blocking on the queue. */ + portDISABLE_INTERRUPTS(); + { + if( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0 ) + { + /* There are no messages in the queue, do we want to block or just + leave with nothing? */ + if( xTicksToWait > ( TickType_t ) 0 ) + { + /* As this is a co-routine we cannot block directly, but return + indicating that we need to block. */ + vCoRoutineAddToDelayedList( xTicksToWait, &( pxQueue->xTasksWaitingToReceive ) ); + portENABLE_INTERRUPTS(); + return errQUEUE_BLOCKED; + } + else + { + portENABLE_INTERRUPTS(); + return errQUEUE_FULL; + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + portENABLE_INTERRUPTS(); + + portDISABLE_INTERRUPTS(); + { + if( pxQueue->uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + /* Data is available from the queue. */ + pxQueue->u.xQueue.pcReadFrom += pxQueue->uxItemSize; + if( pxQueue->u.xQueue.pcReadFrom >= pxQueue->u.xQueue.pcTail ) + { + pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + --( pxQueue->uxMessagesWaiting ); + ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.xQueue.pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); + + xReturn = pdPASS; + + /* Were any co-routines waiting for space to become available? */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + /* In this instance the co-routine could be placed directly + into the ready list as we are within a critical section. + Instead the same pending ready list mechanism is used as if + the event were caused from within an interrupt. */ + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + xReturn = errQUEUE_YIELD; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + xReturn = pdFAIL; + } + } + portENABLE_INTERRUPTS(); + + return xReturn; + } + +#endif /* configUSE_CO_ROUTINES */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_CO_ROUTINES == 1 ) + + BaseType_t xQueueCRSendFromISR( QueueHandle_t xQueue, const void *pvItemToQueue, BaseType_t xCoRoutinePreviouslyWoken ) + { + Queue_t * const pxQueue = xQueue; + + /* Cannot block within an ISR so if there is no space on the queue then + exit without doing anything. */ + if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) + { + prvCopyDataToQueue( pxQueue, pvItemToQueue, queueSEND_TO_BACK ); + + /* We only want to wake one co-routine per ISR, so check that a + co-routine has not already been woken. */ + if( xCoRoutinePreviouslyWoken == pdFALSE ) + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + return pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xCoRoutinePreviouslyWoken; + } + +#endif /* configUSE_CO_ROUTINES */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_CO_ROUTINES == 1 ) + + BaseType_t xQueueCRReceiveFromISR( QueueHandle_t xQueue, void *pvBuffer, BaseType_t *pxCoRoutineWoken ) + { + BaseType_t xReturn; + Queue_t * const pxQueue = xQueue; + + /* We cannot block from an ISR, so check there is data available. If + not then just leave without doing anything. */ + if( pxQueue->uxMessagesWaiting > ( UBaseType_t ) 0 ) + { + /* Copy the data from the queue. */ + pxQueue->u.xQueue.pcReadFrom += pxQueue->uxItemSize; + if( pxQueue->u.xQueue.pcReadFrom >= pxQueue->u.xQueue.pcTail ) + { + pxQueue->u.xQueue.pcReadFrom = pxQueue->pcHead; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + --( pxQueue->uxMessagesWaiting ); + ( void ) memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->u.xQueue.pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); + + if( ( *pxCoRoutineWoken ) == pdFALSE ) + { + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + *pxCoRoutineWoken = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + } + + return xReturn; + } + +#endif /* configUSE_CO_ROUTINES */ +/*-----------------------------------------------------------*/ + +#if ( configQUEUE_REGISTRY_SIZE > 0 ) + + void vQueueAddToRegistry( QueueHandle_t xQueue, const char *pcQueueName ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + { + UBaseType_t ux; + + /* See if there is an empty space in the registry. A NULL name denotes + a free slot. */ + for( ux = ( UBaseType_t ) 0U; ux < ( UBaseType_t ) configQUEUE_REGISTRY_SIZE; ux++ ) + { + if( xQueueRegistry[ ux ].pcQueueName == NULL ) + { + /* Store the information on this queue. */ + xQueueRegistry[ ux ].pcQueueName = pcQueueName; + xQueueRegistry[ ux ].xHandle = xQueue; + + traceQUEUE_REGISTRY_ADD( xQueue, pcQueueName ); + break; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + +#endif /* configQUEUE_REGISTRY_SIZE */ +/*-----------------------------------------------------------*/ + +#if ( configQUEUE_REGISTRY_SIZE > 0 ) + + const char *pcQueueGetName( QueueHandle_t xQueue ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + { + UBaseType_t ux; + const char *pcReturn = NULL; /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + + /* Note there is nothing here to protect against another task adding or + removing entries from the registry while it is being searched. */ + for( ux = ( UBaseType_t ) 0U; ux < ( UBaseType_t ) configQUEUE_REGISTRY_SIZE; ux++ ) + { + if( xQueueRegistry[ ux ].xHandle == xQueue ) + { + pcReturn = xQueueRegistry[ ux ].pcQueueName; + break; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + return pcReturn; + } /*lint !e818 xQueue cannot be a pointer to const because it is a typedef. */ + +#endif /* configQUEUE_REGISTRY_SIZE */ +/*-----------------------------------------------------------*/ + +#if ( configQUEUE_REGISTRY_SIZE > 0 ) + + void vQueueUnregisterQueue( QueueHandle_t xQueue ) + { + UBaseType_t ux; + + /* See if the handle of the queue being unregistered in actually in the + registry. */ + for( ux = ( UBaseType_t ) 0U; ux < ( UBaseType_t ) configQUEUE_REGISTRY_SIZE; ux++ ) + { + if( xQueueRegistry[ ux ].xHandle == xQueue ) + { + /* Set the name to NULL to show that this slot if free again. */ + xQueueRegistry[ ux ].pcQueueName = NULL; + + /* Set the handle to NULL to ensure the same queue handle cannot + appear in the registry twice if it is added, removed, then + added again. */ + xQueueRegistry[ ux ].xHandle = ( QueueHandle_t ) 0; + break; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + } /*lint !e818 xQueue could not be pointer to const because it is a typedef. */ + +#endif /* configQUEUE_REGISTRY_SIZE */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TIMERS == 1 ) + + void vQueueWaitForMessageRestricted( QueueHandle_t xQueue, TickType_t xTicksToWait, const BaseType_t xWaitIndefinitely ) + { + Queue_t * const pxQueue = xQueue; + + /* This function should not be called by application code hence the + 'Restricted' in its name. It is not part of the public API. It is + designed for use by kernel code, and has special calling requirements. + It can result in vListInsert() being called on a list that can only + possibly ever have one item in it, so the list will be fast, but even + so it should be called with the scheduler locked and not from a critical + section. */ + + /* Only do anything if there are no messages in the queue. This function + will not actually cause the task to block, just place it on a blocked + list. It will not block until the scheduler is unlocked - at which + time a yield will be performed. If an item is added to the queue while + the queue is locked, and the calling task blocks on the queue, then the + calling task will be immediately unblocked when the queue is unlocked. */ + prvLockQueue( pxQueue ); + if( pxQueue->uxMessagesWaiting == ( UBaseType_t ) 0U ) + { + /* There is nothing in the queue, block for the specified period. */ + vTaskPlaceOnEventListRestricted( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait, xWaitIndefinitely ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + prvUnlockQueue( pxQueue ); + } + +#endif /* configUSE_TIMERS */ +/*-----------------------------------------------------------*/ + +#if( ( configUSE_QUEUE_SETS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + + QueueSetHandle_t xQueueCreateSet( const UBaseType_t uxEventQueueLength ) + { + QueueSetHandle_t pxQueue; + + pxQueue = xQueueGenericCreate( uxEventQueueLength, ( UBaseType_t ) sizeof( Queue_t * ), queueQUEUE_TYPE_SET ); + + return pxQueue; + } + +#endif /* configUSE_QUEUE_SETS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_QUEUE_SETS == 1 ) + + BaseType_t xQueueAddToSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) + { + BaseType_t xReturn; + + taskENTER_CRITICAL(); + { + if( ( ( Queue_t * ) xQueueOrSemaphore )->pxQueueSetContainer != NULL ) + { + /* Cannot add a queue/semaphore to more than one queue set. */ + xReturn = pdFAIL; + } + else if( ( ( Queue_t * ) xQueueOrSemaphore )->uxMessagesWaiting != ( UBaseType_t ) 0 ) + { + /* Cannot add a queue/semaphore to a queue set if there are already + items in the queue/semaphore. */ + xReturn = pdFAIL; + } + else + { + ( ( Queue_t * ) xQueueOrSemaphore )->pxQueueSetContainer = xQueueSet; + xReturn = pdPASS; + } + } + taskEXIT_CRITICAL(); + + return xReturn; + } + +#endif /* configUSE_QUEUE_SETS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_QUEUE_SETS == 1 ) + + BaseType_t xQueueRemoveFromSet( QueueSetMemberHandle_t xQueueOrSemaphore, QueueSetHandle_t xQueueSet ) + { + BaseType_t xReturn; + Queue_t * const pxQueueOrSemaphore = ( Queue_t * ) xQueueOrSemaphore; + + if( pxQueueOrSemaphore->pxQueueSetContainer != xQueueSet ) + { + /* The queue was not a member of the set. */ + xReturn = pdFAIL; + } + else if( pxQueueOrSemaphore->uxMessagesWaiting != ( UBaseType_t ) 0 ) + { + /* It is dangerous to remove a queue from a set when the queue is + not empty because the queue set will still hold pending events for + the queue. */ + xReturn = pdFAIL; + } + else + { + taskENTER_CRITICAL(); + { + /* The queue is no longer contained in the set. */ + pxQueueOrSemaphore->pxQueueSetContainer = NULL; + } + taskEXIT_CRITICAL(); + xReturn = pdPASS; + } + + return xReturn; + } /*lint !e818 xQueueSet could not be declared as pointing to const as it is a typedef. */ + +#endif /* configUSE_QUEUE_SETS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_QUEUE_SETS == 1 ) + + QueueSetMemberHandle_t xQueueSelectFromSet( QueueSetHandle_t xQueueSet, TickType_t const xTicksToWait ) + { + QueueSetMemberHandle_t xReturn = NULL; + + ( void ) xQueueReceive( ( QueueHandle_t ) xQueueSet, &xReturn, xTicksToWait ); /*lint !e961 Casting from one typedef to another is not redundant. */ + return xReturn; + } + +#endif /* configUSE_QUEUE_SETS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_QUEUE_SETS == 1 ) + + QueueSetMemberHandle_t xQueueSelectFromSetFromISR( QueueSetHandle_t xQueueSet ) + { + QueueSetMemberHandle_t xReturn = NULL; + + ( void ) xQueueReceiveFromISR( ( QueueHandle_t ) xQueueSet, &xReturn, NULL ); /*lint !e961 Casting from one typedef to another is not redundant. */ + return xReturn; + } + +#endif /* configUSE_QUEUE_SETS */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_QUEUE_SETS == 1 ) + + static BaseType_t prvNotifyQueueSetContainer( const Queue_t * const pxQueue ) + { + Queue_t *pxQueueSetContainer = pxQueue->pxQueueSetContainer; + BaseType_t xReturn = pdFALSE; + + /* This function must be called form a critical section. */ + + configASSERT( pxQueueSetContainer ); + configASSERT( pxQueueSetContainer->uxMessagesWaiting < pxQueueSetContainer->uxLength ); + + if( pxQueueSetContainer->uxMessagesWaiting < pxQueueSetContainer->uxLength ) + { + const int8_t cTxLock = pxQueueSetContainer->cTxLock; + + traceQUEUE_SEND( pxQueueSetContainer ); + + /* The data copied is the handle of the queue that contains data. */ + xReturn = prvCopyDataToQueue( pxQueueSetContainer, &pxQueue, queueSEND_TO_BACK ); + + if( cTxLock == queueUNLOCKED ) + { + if( listLIST_IS_EMPTY( &( pxQueueSetContainer->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueueSetContainer->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority. */ + xReturn = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + pxQueueSetContainer->cTxLock = ( int8_t ) ( cTxLock + 1 ); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xReturn; + } + +#endif /* configUSE_QUEUE_SETS */ + + + + + + + + + + + + diff --git a/product/rcar/src/CMSIS-FreeRTOS/Source/tasks.c b/product/rcar/src/CMSIS-FreeRTOS/Source/tasks.c new file mode 100644 index 000000000..f6a6a9b42 --- /dev/null +++ b/product/rcar/src/CMSIS-FreeRTOS/Source/tasks.c @@ -0,0 +1,5310 @@ +/* + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/* Standard includes. */ +#include +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +/* FreeRTOS includes. */ +#include "FreeRTOS.h" +#include "task.h" +#include "timers.h" +#include "stack_macros.h" + +/* Lint e9021, e961 and e750 are suppressed as a MISRA exception justified +because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined +for the header files above, but not in this file, in order to generate the +correct privileged Vs unprivileged linkage and placement. */ +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e961 !e750 !e9021. */ + +/* Set configUSE_STATS_FORMATTING_FUNCTIONS to 2 to include the stats formatting +functions but without including stdio.h here. */ +#if ( configUSE_STATS_FORMATTING_FUNCTIONS == 1 ) + /* At the bottom of this file are two optional functions that can be used + to generate human readable text from the raw data generated by the + uxTaskGetSystemState() function. Note the formatting functions are provided + for convenience only, and are NOT considered part of the kernel. */ + #include +#endif /* configUSE_STATS_FORMATTING_FUNCTIONS == 1 ) */ + +#if( configUSE_PREEMPTION == 0 ) + /* If the cooperative scheduler is being used then a yield should not be + performed just because a higher priority task has been woken. */ + #define taskYIELD_IF_USING_PREEMPTION() +#else + #define taskYIELD_IF_USING_PREEMPTION() portYIELD_WITHIN_API() +#endif + +/* Values that can be assigned to the ucNotifyState member of the TCB. */ +#define taskNOT_WAITING_NOTIFICATION ( ( uint8_t ) 0 ) +#define taskWAITING_NOTIFICATION ( ( uint8_t ) 1 ) +#define taskNOTIFICATION_RECEIVED ( ( uint8_t ) 2 ) + +/* + * The value used to fill the stack of a task when the task is created. This + * is used purely for checking the high water mark for tasks. + */ +#define tskSTACK_FILL_BYTE ( 0xa5U ) + +/* Bits used to recored how a task's stack and TCB were allocated. */ +#define tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB ( ( uint8_t ) 0 ) +#define tskSTATICALLY_ALLOCATED_STACK_ONLY ( ( uint8_t ) 1 ) +#define tskSTATICALLY_ALLOCATED_STACK_AND_TCB ( ( uint8_t ) 2 ) + +/* If any of the following are set then task stacks are filled with a known +value so the high water mark can be determined. If none of the following are +set then don't fill the stack so there is no unnecessary dependency on memset. */ +#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) + #define tskSET_NEW_STACKS_TO_KNOWN_VALUE 1 +#else + #define tskSET_NEW_STACKS_TO_KNOWN_VALUE 0 +#endif + +/* + * Macros used by vListTask to indicate which state a task is in. + */ +#define tskRUNNING_CHAR ( 'X' ) +#define tskBLOCKED_CHAR ( 'B' ) +#define tskREADY_CHAR ( 'R' ) +#define tskDELETED_CHAR ( 'D' ) +#define tskSUSPENDED_CHAR ( 'S' ) + +/* + * Some kernel aware debuggers require the data the debugger needs access to be + * global, rather than file scope. + */ +#ifdef portREMOVE_STATIC_QUALIFIER + #define static +#endif + +/* The name allocated to the Idle task. This can be overridden by defining +configIDLE_TASK_NAME in FreeRTOSConfig.h. */ +#ifndef configIDLE_TASK_NAME + #define configIDLE_TASK_NAME "IDLE" +#endif + +#if ( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 ) + + /* If configUSE_PORT_OPTIMISED_TASK_SELECTION is 0 then task selection is + performed in a generic way that is not optimised to any particular + microcontroller architecture. */ + + /* uxTopReadyPriority holds the priority of the highest priority ready + state task. */ + #define taskRECORD_READY_PRIORITY( uxPriority ) \ + { \ + if( ( uxPriority ) > uxTopReadyPriority ) \ + { \ + uxTopReadyPriority = ( uxPriority ); \ + } \ + } /* taskRECORD_READY_PRIORITY */ + + /*-----------------------------------------------------------*/ + + #define taskSELECT_HIGHEST_PRIORITY_TASK() \ + { \ + UBaseType_t uxTopPriority = uxTopReadyPriority; \ + \ + /* Find the highest priority queue that contains ready tasks. */ \ + while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopPriority ] ) ) ) \ + { \ + configASSERT( uxTopPriority ); \ + --uxTopPriority; \ + } \ + \ + /* listGET_OWNER_OF_NEXT_ENTRY indexes through the list, so the tasks of \ + the same priority get an equal share of the processor time. */ \ + listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \ + uxTopReadyPriority = uxTopPriority; \ + } /* taskSELECT_HIGHEST_PRIORITY_TASK */ + + /*-----------------------------------------------------------*/ + + /* Define away taskRESET_READY_PRIORITY() and portRESET_READY_PRIORITY() as + they are only required when a port optimised method of task selection is + being used. */ + #define taskRESET_READY_PRIORITY( uxPriority ) + #define portRESET_READY_PRIORITY( uxPriority, uxTopReadyPriority ) + +#else /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ + + /* If configUSE_PORT_OPTIMISED_TASK_SELECTION is 1 then task selection is + performed in a way that is tailored to the particular microcontroller + architecture being used. */ + + /* A port optimised version is provided. Call the port defined macros. */ + #define taskRECORD_READY_PRIORITY( uxPriority ) portRECORD_READY_PRIORITY( uxPriority, uxTopReadyPriority ) + + /*-----------------------------------------------------------*/ + + #define taskSELECT_HIGHEST_PRIORITY_TASK() \ + { \ + UBaseType_t uxTopPriority; \ + \ + /* Find the highest priority list that contains ready tasks. */ \ + portGET_HIGHEST_PRIORITY( uxTopPriority, uxTopReadyPriority ); \ + configASSERT( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ uxTopPriority ] ) ) > 0 ); \ + listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopPriority ] ) ); \ + } /* taskSELECT_HIGHEST_PRIORITY_TASK() */ + + /*-----------------------------------------------------------*/ + + /* A port optimised version is provided, call it only if the TCB being reset + is being referenced from a ready list. If it is referenced from a delayed + or suspended list then it won't be in a ready list. */ + #define taskRESET_READY_PRIORITY( uxPriority ) \ + { \ + if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ ( uxPriority ) ] ) ) == ( UBaseType_t ) 0 ) \ + { \ + portRESET_READY_PRIORITY( ( uxPriority ), ( uxTopReadyPriority ) ); \ + } \ + } + +#endif /* configUSE_PORT_OPTIMISED_TASK_SELECTION */ + +/*-----------------------------------------------------------*/ + +/* pxDelayedTaskList and pxOverflowDelayedTaskList are switched when the tick +count overflows. */ +#define taskSWITCH_DELAYED_LISTS() \ +{ \ + List_t *pxTemp; \ + \ + /* The delayed tasks list should be empty when the lists are switched. */ \ + configASSERT( ( listLIST_IS_EMPTY( pxDelayedTaskList ) ) ); \ + \ + pxTemp = pxDelayedTaskList; \ + pxDelayedTaskList = pxOverflowDelayedTaskList; \ + pxOverflowDelayedTaskList = pxTemp; \ + xNumOfOverflows++; \ + prvResetNextTaskUnblockTime(); \ +} + +/*-----------------------------------------------------------*/ + +/* + * Place the task represented by pxTCB into the appropriate ready list for + * the task. It is inserted at the end of the list. + */ +#define prvAddTaskToReadyList( pxTCB ) \ + traceMOVED_TASK_TO_READY_STATE( pxTCB ); \ + taskRECORD_READY_PRIORITY( ( pxTCB )->uxPriority ); \ + vListInsertEnd( &( pxReadyTasksLists[ ( pxTCB )->uxPriority ] ), &( ( pxTCB )->xStateListItem ) ); \ + tracePOST_MOVED_TASK_TO_READY_STATE( pxTCB ) +/*-----------------------------------------------------------*/ + +/* + * Several functions take an TaskHandle_t parameter that can optionally be NULL, + * where NULL is used to indicate that the handle of the currently executing + * task should be used in place of the parameter. This macro simply checks to + * see if the parameter is NULL and returns a pointer to the appropriate TCB. + */ +#define prvGetTCBFromHandle( pxHandle ) ( ( ( pxHandle ) == NULL ) ? pxCurrentTCB : ( pxHandle ) ) + +/* The item value of the event list item is normally used to hold the priority +of the task to which it belongs (coded to allow it to be held in reverse +priority order). However, it is occasionally borrowed for other purposes. It +is important its value is not updated due to a task priority change while it is +being used for another purpose. The following bit definition is used to inform +the scheduler that the value should not be changed - in which case it is the +responsibility of whichever module is using the value to ensure it gets set back +to its original value when it is released. */ +#if( configUSE_16_BIT_TICKS == 1 ) + #define taskEVENT_LIST_ITEM_VALUE_IN_USE 0x8000U +#else + #define taskEVENT_LIST_ITEM_VALUE_IN_USE 0x80000000UL +#endif + +/* + * Task control block. A task control block (TCB) is allocated for each task, + * and stores task state information, including a pointer to the task's context + * (the task's run time environment, including register values) + */ +typedef struct tskTaskControlBlock /* The old naming convention is used to prevent breaking kernel aware debuggers. */ +{ + volatile StackType_t *pxTopOfStack; /*< Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE TCB STRUCT. */ + + #if ( portUSING_MPU_WRAPPERS == 1 ) + xMPU_SETTINGS xMPUSettings; /*< The MPU settings are defined as part of the port layer. THIS MUST BE THE SECOND MEMBER OF THE TCB STRUCT. */ + #endif + + ListItem_t xStateListItem; /*< The list that the state list item of a task is reference from denotes the state of that task (Ready, Blocked, Suspended ). */ + ListItem_t xEventListItem; /*< Used to reference a task from an event list. */ + UBaseType_t uxPriority; /*< The priority of the task. 0 is the lowest priority. */ + StackType_t *pxStack; /*< Points to the start of the stack. */ + char pcTaskName[ configMAX_TASK_NAME_LEN ];/*< Descriptive name given to the task when created. Facilitates debugging only. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + + #if ( ( portSTACK_GROWTH > 0 ) || ( configRECORD_STACK_HIGH_ADDRESS == 1 ) ) + StackType_t *pxEndOfStack; /*< Points to the highest valid address for the stack. */ + #endif + + #if ( portCRITICAL_NESTING_IN_TCB == 1 ) + UBaseType_t uxCriticalNesting; /*< Holds the critical section nesting depth for ports that do not maintain their own count in the port layer. */ + #endif + + #if ( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxTCBNumber; /*< Stores a number that increments each time a TCB is created. It allows debuggers to determine when a task has been deleted and then recreated. */ + UBaseType_t uxTaskNumber; /*< Stores a number specifically for use by third party trace code. */ + #endif + + #if ( configUSE_MUTEXES == 1 ) + UBaseType_t uxBasePriority; /*< The priority last assigned to the task - used by the priority inheritance mechanism. */ + UBaseType_t uxMutexesHeld; + #endif + + #if ( configUSE_APPLICATION_TASK_TAG == 1 ) + TaskHookFunction_t pxTaskTag; + #endif + + #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS > 0 ) + void *pvThreadLocalStoragePointers[ configNUM_THREAD_LOCAL_STORAGE_POINTERS ]; + #endif + + #if( configGENERATE_RUN_TIME_STATS == 1 ) + uint32_t ulRunTimeCounter; /*< Stores the amount of time the task has spent in the Running state. */ + #endif + + #if ( configUSE_NEWLIB_REENTRANT == 1 ) + /* Allocate a Newlib reent structure that is specific to this task. + Note Newlib support has been included by popular demand, but is not + used by the FreeRTOS maintainers themselves. FreeRTOS is not + responsible for resulting newlib operation. User must be familiar with + newlib and must provide system-wide implementations of the necessary + stubs. Be warned that (at the time of writing) the current newlib design + implements a system-wide malloc() that must be provided with locks. + + See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html + for additional information. */ + struct _reent xNewLib_reent; + #endif + + #if( configUSE_TASK_NOTIFICATIONS == 1 ) + volatile uint32_t ulNotifiedValue; + volatile uint8_t ucNotifyState; + #endif + + /* See the comments in FreeRTOS.h with the definition of + tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE. */ + #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */ + uint8_t ucStaticallyAllocated; /*< Set to pdTRUE if the task is a statically allocated to ensure no attempt is made to free the memory. */ + #endif + + #if( INCLUDE_xTaskAbortDelay == 1 ) + uint8_t ucDelayAborted; + #endif + + #if( configUSE_POSIX_ERRNO == 1 ) + int iTaskErrno; + #endif + +} tskTCB; + +/* The old tskTCB name is maintained above then typedefed to the new TCB_t name +below to enable the use of older kernel aware debuggers. */ +typedef tskTCB TCB_t; + +/*lint -save -e956 A manual analysis and inspection has been used to determine +which static variables must be declared volatile. */ +PRIVILEGED_DATA TCB_t * volatile pxCurrentTCB = NULL; + +/* Lists for ready and blocked tasks. -------------------- +xDelayedTaskList1 and xDelayedTaskList2 could be move to function scople but +doing so breaks some kernel aware debuggers and debuggers that rely on removing +the static qualifier. */ +PRIVILEGED_DATA static List_t pxReadyTasksLists[ configMAX_PRIORITIES ];/*< Prioritised ready tasks. */ +PRIVILEGED_DATA static List_t xDelayedTaskList1; /*< Delayed tasks. */ +PRIVILEGED_DATA static List_t xDelayedTaskList2; /*< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count. */ +PRIVILEGED_DATA static List_t * volatile pxDelayedTaskList; /*< Points to the delayed task list currently being used. */ +PRIVILEGED_DATA static List_t * volatile pxOverflowDelayedTaskList; /*< Points to the delayed task list currently being used to hold tasks that have overflowed the current tick count. */ +PRIVILEGED_DATA static List_t xPendingReadyList; /*< Tasks that have been readied while the scheduler was suspended. They will be moved to the ready list when the scheduler is resumed. */ + +#if( INCLUDE_vTaskDelete == 1 ) + + PRIVILEGED_DATA static List_t xTasksWaitingTermination; /*< Tasks that have been deleted - but their memory not yet freed. */ + PRIVILEGED_DATA static volatile UBaseType_t uxDeletedTasksWaitingCleanUp = ( UBaseType_t ) 0U; + +#endif + +#if ( INCLUDE_vTaskSuspend == 1 ) + + PRIVILEGED_DATA static List_t xSuspendedTaskList; /*< Tasks that are currently suspended. */ + +#endif + +/* Global POSIX errno. Its value is changed upon context switching to match +the errno of the currently running task. */ +#if ( configUSE_POSIX_ERRNO == 1 ) + int FreeRTOS_errno = 0; +#endif + +/* Other file private variables. --------------------------------*/ +PRIVILEGED_DATA static volatile UBaseType_t uxCurrentNumberOfTasks = ( UBaseType_t ) 0U; +PRIVILEGED_DATA static volatile TickType_t xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT; +PRIVILEGED_DATA static volatile UBaseType_t uxTopReadyPriority = tskIDLE_PRIORITY; +PRIVILEGED_DATA static volatile BaseType_t xSchedulerRunning = pdFALSE; +PRIVILEGED_DATA static volatile TickType_t xPendedTicks = ( TickType_t ) 0U; +PRIVILEGED_DATA static volatile BaseType_t xYieldPending = pdFALSE; +PRIVILEGED_DATA static volatile BaseType_t xNumOfOverflows = ( BaseType_t ) 0; +PRIVILEGED_DATA static UBaseType_t uxTaskNumber = ( UBaseType_t ) 0U; +PRIVILEGED_DATA static volatile TickType_t xNextTaskUnblockTime = ( TickType_t ) 0U; /* Initialised to portMAX_DELAY before the scheduler starts. */ +PRIVILEGED_DATA static TaskHandle_t xIdleTaskHandle = NULL; /*< Holds the handle of the idle task. The idle task is created automatically when the scheduler is started. */ + +/* Context switches are held pending while the scheduler is suspended. Also, +interrupts must not manipulate the xStateListItem of a TCB, or any of the +lists the xStateListItem can be referenced from, if the scheduler is suspended. +If an interrupt needs to unblock a task while the scheduler is suspended then it +moves the task's event list item into the xPendingReadyList, ready for the +kernel to move the task from the pending ready list into the real ready list +when the scheduler is unsuspended. The pending ready list itself can only be +accessed from a critical section. */ +PRIVILEGED_DATA static volatile UBaseType_t uxSchedulerSuspended = ( UBaseType_t ) pdFALSE; + +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + + /* Do not move these variables to function scope as doing so prevents the + code working with debuggers that need to remove the static qualifier. */ + PRIVILEGED_DATA static uint32_t ulTaskSwitchedInTime = 0UL; /*< Holds the value of a timer/counter the last time a task was switched in. */ + PRIVILEGED_DATA static uint32_t ulTotalRunTime = 0UL; /*< Holds the total amount of execution time as defined by the run time counter clock. */ + +#endif + +/*lint -restore */ + +/*-----------------------------------------------------------*/ + +/* Callback function prototypes. --------------------------*/ +#if( configCHECK_FOR_STACK_OVERFLOW > 0 ) + + extern void vApplicationStackOverflowHook( TaskHandle_t xTask, char *pcTaskName ); + +#endif + +#if( configUSE_TICK_HOOK > 0 ) + + extern void vApplicationTickHook( void ); /*lint !e526 Symbol not defined as it is an application callback. */ + +#endif + +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + + extern void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize ); /*lint !e526 Symbol not defined as it is an application callback. */ + +#endif + +/* File private functions. --------------------------------*/ + +/** + * Utility task that simply returns pdTRUE if the task referenced by xTask is + * currently in the Suspended state, or pdFALSE if the task referenced by xTask + * is in any other state. + */ +#if ( INCLUDE_vTaskSuspend == 1 ) + + static BaseType_t prvTaskIsTaskSuspended( const TaskHandle_t xTask ) PRIVILEGED_FUNCTION; + +#endif /* INCLUDE_vTaskSuspend */ + +/* + * Utility to ready all the lists used by the scheduler. This is called + * automatically upon the creation of the first task. + */ +static void prvInitialiseTaskLists( void ) PRIVILEGED_FUNCTION; + +/* + * The idle task, which as all tasks is implemented as a never ending loop. + * The idle task is automatically created and added to the ready lists upon + * creation of the first user task. + * + * The portTASK_FUNCTION_PROTO() macro is used to allow port/compiler specific + * language extensions. The equivalent prototype for this function is: + * + * void prvIdleTask( void *pvParameters ); + * + */ +static portTASK_FUNCTION_PROTO( prvIdleTask, pvParameters ); + +/* + * Utility to free all memory allocated by the scheduler to hold a TCB, + * including the stack pointed to by the TCB. + * + * This does not free memory allocated by the task itself (i.e. memory + * allocated by calls to pvPortMalloc from within the tasks application code). + */ +#if ( INCLUDE_vTaskDelete == 1 ) + + static void prvDeleteTCB( TCB_t *pxTCB ) PRIVILEGED_FUNCTION; + +#endif + +/* + * Used only by the idle task. This checks to see if anything has been placed + * in the list of tasks waiting to be deleted. If so the task is cleaned up + * and its TCB deleted. + */ +static void prvCheckTasksWaitingTermination( void ) PRIVILEGED_FUNCTION; + +/* + * The currently executing task is entering the Blocked state. Add the task to + * either the current or the overflow delayed task list. + */ +static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely ) PRIVILEGED_FUNCTION; + +/* + * Fills an TaskStatus_t structure with information on each task that is + * referenced from the pxList list (which may be a ready list, a delayed list, + * a suspended list, etc.). + * + * THIS FUNCTION IS INTENDED FOR DEBUGGING ONLY, AND SHOULD NOT BE CALLED FROM + * NORMAL APPLICATION CODE. + */ +#if ( configUSE_TRACE_FACILITY == 1 ) + + static UBaseType_t prvListTasksWithinSingleList( TaskStatus_t *pxTaskStatusArray, List_t *pxList, eTaskState eState ) PRIVILEGED_FUNCTION; + +#endif + +/* + * Searches pxList for a task with name pcNameToQuery - returning a handle to + * the task if it is found, or NULL if the task is not found. + */ +#if ( INCLUDE_xTaskGetHandle == 1 ) + + static TCB_t *prvSearchForNameWithinSingleList( List_t *pxList, const char pcNameToQuery[] ) PRIVILEGED_FUNCTION; + +#endif + +/* + * When a task is created, the stack of the task is filled with a known value. + * This function determines the 'high water mark' of the task stack by + * determining how much of the stack remains at the original preset value. + */ +#if ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) + + static configSTACK_DEPTH_TYPE prvTaskCheckFreeStackSpace( const uint8_t * pucStackByte ) PRIVILEGED_FUNCTION; + +#endif + +/* + * Return the amount of time, in ticks, that will pass before the kernel will + * next move a task from the Blocked state to the Running state. + * + * This conditional compilation should use inequality to 0, not equality to 1. + * This is to ensure portSUPPRESS_TICKS_AND_SLEEP() can be called when user + * defined low power mode implementations require configUSE_TICKLESS_IDLE to be + * set to a value other than 1. + */ +#if ( configUSE_TICKLESS_IDLE != 0 ) + + static TickType_t prvGetExpectedIdleTime( void ) PRIVILEGED_FUNCTION; + +#endif + +/* + * Set xNextTaskUnblockTime to the time at which the next Blocked state task + * will exit the Blocked state. + */ +static void prvResetNextTaskUnblockTime( void ); + +#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) + + /* + * Helper function used to pad task names with spaces when printing out + * human readable tables of task information. + */ + static char *prvWriteNameToBuffer( char *pcBuffer, const char *pcTaskName ) PRIVILEGED_FUNCTION; + +#endif + +/* + * Called after a Task_t structure has been allocated either statically or + * dynamically to fill in the structure's members. + */ +static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const uint32_t ulStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask, + TCB_t *pxNewTCB, + const MemoryRegion_t * const xRegions ) PRIVILEGED_FUNCTION; + +/* + * Called after a new task has been created and initialised to place the task + * under the control of the scheduler. + */ +static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) PRIVILEGED_FUNCTION; + +/* + * freertos_tasks_c_additions_init() should only be called if the user definable + * macro FREERTOS_TASKS_C_ADDITIONS_INIT() is defined, as that is the only macro + * called by the function. + */ +#ifdef FREERTOS_TASKS_C_ADDITIONS_INIT + + static void freertos_tasks_c_additions_init( void ) PRIVILEGED_FUNCTION; + +#endif + +/*-----------------------------------------------------------*/ + +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + + TaskHandle_t xTaskCreateStatic( TaskFunction_t pxTaskCode, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const uint32_t ulStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + StackType_t * const puxStackBuffer, + StaticTask_t * const pxTaskBuffer ) + { + TCB_t *pxNewTCB; + TaskHandle_t xReturn; + + configASSERT( puxStackBuffer != NULL ); + configASSERT( pxTaskBuffer != NULL ); + + #if( configASSERT_DEFINED == 1 ) + { + /* Sanity check that the size of the structure used to declare a + variable of type StaticTask_t equals the size of the real task + structure. */ + volatile size_t xSize = sizeof( StaticTask_t ); + configASSERT( xSize == sizeof( TCB_t ) ); + ( void ) xSize; /* Prevent lint warning when configASSERT() is not used. */ + } + #endif /* configASSERT_DEFINED */ + + + if( ( pxTaskBuffer != NULL ) && ( puxStackBuffer != NULL ) ) + { + /* The memory used for the task's TCB and stack are passed into this + function - use them. */ + pxNewTCB = ( TCB_t * ) pxTaskBuffer; /*lint !e740 !e9087 Unusual cast is ok as the structures are designed to have the same alignment, and the size is checked by an assert. */ + pxNewTCB->pxStack = ( StackType_t * ) puxStackBuffer; + + #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */ + { + /* Tasks can be created statically or dynamically, so note this + task was created statically in case the task is later deleted. */ + pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB; + } + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ + + prvInitialiseNewTask( pxTaskCode, pcName, ulStackDepth, pvParameters, uxPriority, &xReturn, pxNewTCB, NULL ); + prvAddNewTaskToReadyList( pxNewTCB ); + } + else + { + xReturn = NULL; + } + + return xReturn; + } + +#endif /* SUPPORT_STATIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +#if( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) + + BaseType_t xTaskCreateRestrictedStatic( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) + { + TCB_t *pxNewTCB; + BaseType_t xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + + configASSERT( pxTaskDefinition->puxStackBuffer != NULL ); + configASSERT( pxTaskDefinition->pxTaskBuffer != NULL ); + + if( ( pxTaskDefinition->puxStackBuffer != NULL ) && ( pxTaskDefinition->pxTaskBuffer != NULL ) ) + { + /* Allocate space for the TCB. Where the memory comes from depends + on the implementation of the port malloc function and whether or + not static allocation is being used. */ + pxNewTCB = ( TCB_t * ) pxTaskDefinition->pxTaskBuffer; + + /* Store the stack location in the TCB. */ + pxNewTCB->pxStack = pxTaskDefinition->puxStackBuffer; + + #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + { + /* Tasks can be created statically or dynamically, so note this + task was created statically in case the task is later deleted. */ + pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_AND_TCB; + } + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ + + prvInitialiseNewTask( pxTaskDefinition->pvTaskCode, + pxTaskDefinition->pcName, + ( uint32_t ) pxTaskDefinition->usStackDepth, + pxTaskDefinition->pvParameters, + pxTaskDefinition->uxPriority, + pxCreatedTask, pxNewTCB, + pxTaskDefinition->xRegions ); + + prvAddNewTaskToReadyList( pxNewTCB ); + xReturn = pdPASS; + } + + return xReturn; + } + +#endif /* ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) */ +/*-----------------------------------------------------------*/ + +#if( ( portUSING_MPU_WRAPPERS == 1 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + + BaseType_t xTaskCreateRestricted( const TaskParameters_t * const pxTaskDefinition, TaskHandle_t *pxCreatedTask ) + { + TCB_t *pxNewTCB; + BaseType_t xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + + configASSERT( pxTaskDefinition->puxStackBuffer ); + + if( pxTaskDefinition->puxStackBuffer != NULL ) + { + /* Allocate space for the TCB. Where the memory comes from depends + on the implementation of the port malloc function and whether or + not static allocation is being used. */ + pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); + + if( pxNewTCB != NULL ) + { + /* Store the stack location in the TCB. */ + pxNewTCB->pxStack = pxTaskDefinition->puxStackBuffer; + + #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) + { + /* Tasks can be created statically or dynamically, so note + this task had a statically allocated stack in case it is + later deleted. The TCB was allocated dynamically. */ + pxNewTCB->ucStaticallyAllocated = tskSTATICALLY_ALLOCATED_STACK_ONLY; + } + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ + + prvInitialiseNewTask( pxTaskDefinition->pvTaskCode, + pxTaskDefinition->pcName, + ( uint32_t ) pxTaskDefinition->usStackDepth, + pxTaskDefinition->pvParameters, + pxTaskDefinition->uxPriority, + pxCreatedTask, pxNewTCB, + pxTaskDefinition->xRegions ); + + prvAddNewTaskToReadyList( pxNewTCB ); + xReturn = pdPASS; + } + } + + return xReturn; + } + +#endif /* portUSING_MPU_WRAPPERS */ +/*-----------------------------------------------------------*/ + +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + + BaseType_t xTaskCreate( TaskFunction_t pxTaskCode, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const configSTACK_DEPTH_TYPE usStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask ) + { + TCB_t *pxNewTCB; + BaseType_t xReturn; + + /* If the stack grows down then allocate the stack then the TCB so the stack + does not grow into the TCB. Likewise if the stack grows up then allocate + the TCB then the stack. */ + #if( portSTACK_GROWTH > 0 ) + { + /* Allocate space for the TCB. Where the memory comes from depends on + the implementation of the port malloc function and whether or not static + allocation is being used. */ + pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); + + if( pxNewTCB != NULL ) + { + /* Allocate space for the stack used by the task being created. + The base of the stack memory stored in the TCB so the task can + be deleted later if required. */ + pxNewTCB->pxStack = ( StackType_t * ) pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + + if( pxNewTCB->pxStack == NULL ) + { + /* Could not allocate the stack. Delete the allocated TCB. */ + vPortFree( pxNewTCB ); + pxNewTCB = NULL; + } + } + } + #else /* portSTACK_GROWTH */ + { + StackType_t *pxStack; + + /* Allocate space for the stack used by the task being created. */ + pxStack = pvPortMalloc( ( ( ( size_t ) usStackDepth ) * sizeof( StackType_t ) ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation is the stack. */ + + if( pxStack != NULL ) + { + /* Allocate space for the TCB. */ + pxNewTCB = ( TCB_t * ) pvPortMalloc( sizeof( TCB_t ) ); /*lint !e9087 !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack, and the first member of TCB_t is always a pointer to the task's stack. */ + + if( pxNewTCB != NULL ) + { + /* Store the stack location in the TCB. */ + pxNewTCB->pxStack = pxStack; + } + else + { + /* The stack cannot be used as the TCB was not created. Free + it again. */ + vPortFree( pxStack ); + } + } + else + { + pxNewTCB = NULL; + } + } + #endif /* portSTACK_GROWTH */ + + if( pxNewTCB != NULL ) + { + #if( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e9029 !e731 Macro has been consolidated for readability reasons. */ + { + /* Tasks can be created statically or dynamically, so note this + task was created dynamically in case it is later deleted. */ + pxNewTCB->ucStaticallyAllocated = tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB; + } + #endif /* tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE */ + + prvInitialiseNewTask( pxTaskCode, pcName, ( uint32_t ) usStackDepth, pvParameters, uxPriority, pxCreatedTask, pxNewTCB, NULL ); + prvAddNewTaskToReadyList( pxNewTCB ); + xReturn = pdPASS; + } + else + { + xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + } + + return xReturn; + } + +#endif /* configSUPPORT_DYNAMIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +static void prvInitialiseNewTask( TaskFunction_t pxTaskCode, + const char * const pcName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const uint32_t ulStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pxCreatedTask, + TCB_t *pxNewTCB, + const MemoryRegion_t * const xRegions ) +{ +StackType_t *pxTopOfStack; +UBaseType_t x; + + #if( portUSING_MPU_WRAPPERS == 1 ) + /* Should the task be created in privileged mode? */ + BaseType_t xRunPrivileged; + if( ( uxPriority & portPRIVILEGE_BIT ) != 0U ) + { + xRunPrivileged = pdTRUE; + } + else + { + xRunPrivileged = pdFALSE; + } + uxPriority &= ~portPRIVILEGE_BIT; + #endif /* portUSING_MPU_WRAPPERS == 1 */ + + /* Avoid dependency on memset() if it is not required. */ + #if( tskSET_NEW_STACKS_TO_KNOWN_VALUE == 1 ) + { + /* Fill the stack with a known value to assist debugging. */ + ( void ) memset( pxNewTCB->pxStack, ( int ) tskSTACK_FILL_BYTE, ( size_t ) ulStackDepth * sizeof( StackType_t ) ); + } + #endif /* tskSET_NEW_STACKS_TO_KNOWN_VALUE */ + + /* Calculate the top of stack address. This depends on whether the stack + grows from high memory to low (as per the 80x86) or vice versa. + portSTACK_GROWTH is used to make the result positive or negative as required + by the port. */ + #if( portSTACK_GROWTH < 0 ) + { + pxTopOfStack = &( pxNewTCB->pxStack[ ulStackDepth - ( uint32_t ) 1 ] ); + pxTopOfStack = ( StackType_t * ) ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack ) & ( ~( ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) ) ); /*lint !e923 !e9033 !e9078 MISRA exception. Avoiding casts between pointers and integers is not practical. Size differences accounted for using portPOINTER_SIZE_TYPE type. Checked by assert(). */ + + /* Check the alignment of the calculated top of stack is correct. */ + configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxTopOfStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); + + #if( configRECORD_STACK_HIGH_ADDRESS == 1 ) + { + /* Also record the stack's high address, which may assist + debugging. */ + pxNewTCB->pxEndOfStack = pxTopOfStack; + } + #endif /* configRECORD_STACK_HIGH_ADDRESS */ + } + #else /* portSTACK_GROWTH */ + { + pxTopOfStack = pxNewTCB->pxStack; + + /* Check the alignment of the stack buffer is correct. */ + configASSERT( ( ( ( portPOINTER_SIZE_TYPE ) pxNewTCB->pxStack & ( portPOINTER_SIZE_TYPE ) portBYTE_ALIGNMENT_MASK ) == 0UL ) ); + + /* The other extreme of the stack space is required if stack checking is + performed. */ + pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( ulStackDepth - ( uint32_t ) 1 ); + } + #endif /* portSTACK_GROWTH */ + + /* Store the task name in the TCB. */ + if( pcName != NULL ) + { + for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) + { + pxNewTCB->pcTaskName[ x ] = pcName[ x ]; + + /* Don't copy all configMAX_TASK_NAME_LEN if the string is shorter than + configMAX_TASK_NAME_LEN characters just in case the memory after the + string is not accessible (extremely unlikely). */ + if( pcName[ x ] == ( char ) 0x00 ) + { + break; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + /* Ensure the name string is terminated in the case that the string length + was greater or equal to configMAX_TASK_NAME_LEN. */ + pxNewTCB->pcTaskName[ configMAX_TASK_NAME_LEN - 1 ] = '\0'; + } + else + { + /* The task has not been given a name, so just ensure there is a NULL + terminator when it is read out. */ + pxNewTCB->pcTaskName[ 0 ] = 0x00; + } + + /* This is used as an array index so must ensure it's not too large. First + remove the privilege bit if one is present. */ + if( uxPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) + { + uxPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + pxNewTCB->uxPriority = uxPriority; + #if ( configUSE_MUTEXES == 1 ) + { + pxNewTCB->uxBasePriority = uxPriority; + pxNewTCB->uxMutexesHeld = 0; + } + #endif /* configUSE_MUTEXES */ + + vListInitialiseItem( &( pxNewTCB->xStateListItem ) ); + vListInitialiseItem( &( pxNewTCB->xEventListItem ) ); + + /* Set the pxNewTCB as a link back from the ListItem_t. This is so we can get + back to the containing TCB from a generic item in a list. */ + listSET_LIST_ITEM_OWNER( &( pxNewTCB->xStateListItem ), pxNewTCB ); + + /* Event lists are always in priority order. */ + listSET_LIST_ITEM_VALUE( &( pxNewTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + listSET_LIST_ITEM_OWNER( &( pxNewTCB->xEventListItem ), pxNewTCB ); + + #if ( portCRITICAL_NESTING_IN_TCB == 1 ) + { + pxNewTCB->uxCriticalNesting = ( UBaseType_t ) 0U; + } + #endif /* portCRITICAL_NESTING_IN_TCB */ + + #if ( configUSE_APPLICATION_TASK_TAG == 1 ) + { + pxNewTCB->pxTaskTag = NULL; + } + #endif /* configUSE_APPLICATION_TASK_TAG */ + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + { + pxNewTCB->ulRunTimeCounter = 0UL; + } + #endif /* configGENERATE_RUN_TIME_STATS */ + + #if ( portUSING_MPU_WRAPPERS == 1 ) + { + vPortStoreTaskMPUSettings( &( pxNewTCB->xMPUSettings ), xRegions, pxNewTCB->pxStack, ulStackDepth ); + } + #else + { + /* Avoid compiler warning about unreferenced parameter. */ + ( void ) xRegions; + } + #endif + + #if( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 ) + { + for( x = 0; x < ( UBaseType_t ) configNUM_THREAD_LOCAL_STORAGE_POINTERS; x++ ) + { + pxNewTCB->pvThreadLocalStoragePointers[ x ] = NULL; + } + } + #endif + + #if ( configUSE_TASK_NOTIFICATIONS == 1 ) + { + pxNewTCB->ulNotifiedValue = 0; + pxNewTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION; + } + #endif + + #if ( configUSE_NEWLIB_REENTRANT == 1 ) + { + /* Initialise this task's Newlib reent structure. + See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html + for additional information. */ + _REENT_INIT_PTR( ( &( pxNewTCB->xNewLib_reent ) ) ); + } + #endif + + #if( INCLUDE_xTaskAbortDelay == 1 ) + { + pxNewTCB->ucDelayAborted = pdFALSE; + } + #endif + + /* Initialize the TCB stack to look as if the task was already running, + but had been interrupted by the scheduler. The return address is set + to the start of the task function. Once the stack has been initialised + the top of stack variable is updated. */ + #if( portUSING_MPU_WRAPPERS == 1 ) + { + /* If the port has capability to detect stack overflow, + pass the stack end address to the stack initialization + function as well. */ + #if( portHAS_STACK_OVERFLOW_CHECKING == 1 ) + { + #if( portSTACK_GROWTH < 0 ) + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters, xRunPrivileged ); + } + #else /* portSTACK_GROWTH */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters, xRunPrivileged ); + } + #endif /* portSTACK_GROWTH */ + } + #else /* portHAS_STACK_OVERFLOW_CHECKING */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged ); + } + #endif /* portHAS_STACK_OVERFLOW_CHECKING */ + } + #else /* portUSING_MPU_WRAPPERS */ + { + /* If the port has capability to detect stack overflow, + pass the stack end address to the stack initialization + function as well. */ + #if( portHAS_STACK_OVERFLOW_CHECKING == 1 ) + { + #if( portSTACK_GROWTH < 0 ) + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxStack, pxTaskCode, pvParameters ); + } + #else /* portSTACK_GROWTH */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxNewTCB->pxEndOfStack, pxTaskCode, pvParameters ); + } + #endif /* portSTACK_GROWTH */ + } + #else /* portHAS_STACK_OVERFLOW_CHECKING */ + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); + } + #endif /* portHAS_STACK_OVERFLOW_CHECKING */ + } + #endif /* portUSING_MPU_WRAPPERS */ + + if( pxCreatedTask != NULL ) + { + /* Pass the handle out in an anonymous way. The handle can be used to + change the created task's priority, delete the created task, etc.*/ + *pxCreatedTask = ( TaskHandle_t ) pxNewTCB; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } +} +/*-----------------------------------------------------------*/ + +static void prvAddNewTaskToReadyList( TCB_t *pxNewTCB ) +{ + /* Ensure interrupts don't access the task lists while the lists are being + updated. */ + taskENTER_CRITICAL(); + { + uxCurrentNumberOfTasks++; + if( pxCurrentTCB == NULL ) + { + /* There are no other tasks, or all the other tasks are in + the suspended state - make this the current task. */ + pxCurrentTCB = pxNewTCB; + + if( uxCurrentNumberOfTasks == ( UBaseType_t ) 1 ) + { + /* This is the first task to be created so do the preliminary + initialisation required. We will not recover if this call + fails, but we will report the failure. */ + prvInitialiseTaskLists(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* If the scheduler is not already running, make this task the + current task if it is the highest priority task to be created + so far. */ + if( xSchedulerRunning == pdFALSE ) + { + if( pxCurrentTCB->uxPriority <= pxNewTCB->uxPriority ) + { + pxCurrentTCB = pxNewTCB; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + uxTaskNumber++; + + #if ( configUSE_TRACE_FACILITY == 1 ) + { + /* Add a counter into the TCB for tracing only. */ + pxNewTCB->uxTCBNumber = uxTaskNumber; + } + #endif /* configUSE_TRACE_FACILITY */ + traceTASK_CREATE( pxNewTCB ); + + prvAddTaskToReadyList( pxNewTCB ); + + portSETUP_TCB( pxNewTCB ); + } + taskEXIT_CRITICAL(); + + if( xSchedulerRunning != pdFALSE ) + { + /* If the created task is of a higher priority than the current task + then it should run now. */ + if( pxCurrentTCB->uxPriority < pxNewTCB->uxPriority ) + { + taskYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } +} +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelete == 1 ) + + void vTaskDelete( TaskHandle_t xTaskToDelete ) + { + TCB_t *pxTCB; + + taskENTER_CRITICAL(); + { + /* If null is passed in here then it is the calling task that is + being deleted. */ + pxTCB = prvGetTCBFromHandle( xTaskToDelete ); + + /* Remove task from the ready/delayed list. */ + if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + taskRESET_READY_PRIORITY( pxTCB->uxPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Is the task waiting on an event also? */ + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) + { + ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Increment the uxTaskNumber also so kernel aware debuggers can + detect that the task lists need re-generating. This is done before + portPRE_TASK_DELETE_HOOK() as in the Windows port that macro will + not return. */ + uxTaskNumber++; + + if( pxTCB == pxCurrentTCB ) + { + /* A task is deleting itself. This cannot complete within the + task itself, as a context switch to another task is required. + Place the task in the termination list. The idle task will + check the termination list and free up any memory allocated by + the scheduler for the TCB and stack of the deleted task. */ + vListInsertEnd( &xTasksWaitingTermination, &( pxTCB->xStateListItem ) ); + + /* Increment the ucTasksDeleted variable so the idle task knows + there is a task that has been deleted and that it should therefore + check the xTasksWaitingTermination list. */ + ++uxDeletedTasksWaitingCleanUp; + + /* Call the delete hook before portPRE_TASK_DELETE_HOOK() as + portPRE_TASK_DELETE_HOOK() does not return in the Win32 port. */ + traceTASK_DELETE( pxTCB ); + + /* The pre-delete hook is primarily for the Windows simulator, + in which Windows specific clean up operations are performed, + after which it is not possible to yield away from this task - + hence xYieldPending is used to latch that a context switch is + required. */ + portPRE_TASK_DELETE_HOOK( pxTCB, &xYieldPending ); + } + else + { + --uxCurrentNumberOfTasks; + traceTASK_DELETE( pxTCB ); + prvDeleteTCB( pxTCB ); + + /* Reset the next expected unblock time in case it referred to + the task that has just been deleted. */ + prvResetNextTaskUnblockTime(); + } + } + taskEXIT_CRITICAL(); + + /* Force a reschedule if it is the currently running task that has just + been deleted. */ + if( xSchedulerRunning != pdFALSE ) + { + if( pxTCB == pxCurrentTCB ) + { + configASSERT( uxSchedulerSuspended == 0 ); + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + +#endif /* INCLUDE_vTaskDelete */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelayUntil == 1 ) + + void vTaskDelayUntil( TickType_t * const pxPreviousWakeTime, const TickType_t xTimeIncrement ) + { + TickType_t xTimeToWake; + BaseType_t xAlreadyYielded, xShouldDelay = pdFALSE; + + configASSERT( pxPreviousWakeTime ); + configASSERT( ( xTimeIncrement > 0U ) ); + configASSERT( uxSchedulerSuspended == 0 ); + + vTaskSuspendAll(); + { + /* Minor optimisation. The tick count cannot change in this + block. */ + const TickType_t xConstTickCount = xTickCount; + + /* Generate the tick time at which the task wants to wake. */ + xTimeToWake = *pxPreviousWakeTime + xTimeIncrement; + + if( xConstTickCount < *pxPreviousWakeTime ) + { + /* The tick count has overflowed since this function was + lasted called. In this case the only time we should ever + actually delay is if the wake time has also overflowed, + and the wake time is greater than the tick time. When this + is the case it is as if neither time had overflowed. */ + if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake > xConstTickCount ) ) + { + xShouldDelay = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* The tick time has not overflowed. In this case we will + delay if either the wake time has overflowed, and/or the + tick time is less than the wake time. */ + if( ( xTimeToWake < *pxPreviousWakeTime ) || ( xTimeToWake > xConstTickCount ) ) + { + xShouldDelay = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + /* Update the wake time ready for the next call. */ + *pxPreviousWakeTime = xTimeToWake; + + if( xShouldDelay != pdFALSE ) + { + traceTASK_DELAY_UNTIL( xTimeToWake ); + + /* prvAddCurrentTaskToDelayedList() needs the block time, not + the time to wake, so subtract the current tick count. */ + prvAddCurrentTaskToDelayedList( xTimeToWake - xConstTickCount, pdFALSE ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + xAlreadyYielded = xTaskResumeAll(); + + /* Force a reschedule if xTaskResumeAll has not already done so, we may + have put ourselves to sleep. */ + if( xAlreadyYielded == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* INCLUDE_vTaskDelayUntil */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelay == 1 ) + + void vTaskDelay( const TickType_t xTicksToDelay ) + { + BaseType_t xAlreadyYielded = pdFALSE; + + /* A delay time of zero just forces a reschedule. */ + if( xTicksToDelay > ( TickType_t ) 0U ) + { + configASSERT( uxSchedulerSuspended == 0 ); + vTaskSuspendAll(); + { + traceTASK_DELAY(); + + /* A task that is removed from the event list while the + scheduler is suspended will not get placed in the ready + list or removed from the blocked list until the scheduler + is resumed. + + This task cannot be in an event list as it is the currently + executing task. */ + prvAddCurrentTaskToDelayedList( xTicksToDelay, pdFALSE ); + } + xAlreadyYielded = xTaskResumeAll(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Force a reschedule if xTaskResumeAll has not already done so, we may + have put ourselves to sleep. */ + if( xAlreadyYielded == pdFALSE ) + { + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* INCLUDE_vTaskDelay */ +/*-----------------------------------------------------------*/ + +#if( ( INCLUDE_eTaskGetState == 1 ) || ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_xTaskAbortDelay == 1 ) ) + + eTaskState eTaskGetState( TaskHandle_t xTask ) + { + eTaskState eReturn; + List_t const * pxStateList, *pxDelayedList, *pxOverflowedDelayedList; + const TCB_t * const pxTCB = xTask; + + configASSERT( pxTCB ); + + if( pxTCB == pxCurrentTCB ) + { + /* The task calling this function is querying its own state. */ + eReturn = eRunning; + } + else + { + taskENTER_CRITICAL(); + { + pxStateList = listLIST_ITEM_CONTAINER( &( pxTCB->xStateListItem ) ); + pxDelayedList = pxDelayedTaskList; + pxOverflowedDelayedList = pxOverflowDelayedTaskList; + } + taskEXIT_CRITICAL(); + + if( ( pxStateList == pxDelayedList ) || ( pxStateList == pxOverflowedDelayedList ) ) + { + /* The task being queried is referenced from one of the Blocked + lists. */ + eReturn = eBlocked; + } + + #if ( INCLUDE_vTaskSuspend == 1 ) + else if( pxStateList == &xSuspendedTaskList ) + { + /* The task being queried is referenced from the suspended + list. Is it genuinely suspended or is it blocked + indefinitely? */ + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ) + { + #if( configUSE_TASK_NOTIFICATIONS == 1 ) + { + /* The task does not appear on the event list item of + and of the RTOS objects, but could still be in the + blocked state if it is waiting on its notification + rather than waiting on an object. */ + if( pxTCB->ucNotifyState == taskWAITING_NOTIFICATION ) + { + eReturn = eBlocked; + } + else + { + eReturn = eSuspended; + } + } + #else + { + eReturn = eSuspended; + } + #endif + } + else + { + eReturn = eBlocked; + } + } + #endif + + #if ( INCLUDE_vTaskDelete == 1 ) + else if( ( pxStateList == &xTasksWaitingTermination ) || ( pxStateList == NULL ) ) + { + /* The task being queried is referenced from the deleted + tasks list, or it is not referenced from any lists at + all. */ + eReturn = eDeleted; + } + #endif + + else /*lint !e525 Negative indentation is intended to make use of pre-processor clearer. */ + { + /* If the task is not in any other state, it must be in the + Ready (including pending ready) state. */ + eReturn = eReady; + } + } + + return eReturn; + } /*lint !e818 xTask cannot be a pointer to const because it is a typedef. */ + +#endif /* INCLUDE_eTaskGetState */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_uxTaskPriorityGet == 1 ) + + UBaseType_t uxTaskPriorityGet( const TaskHandle_t xTask ) + { + TCB_t const *pxTCB; + UBaseType_t uxReturn; + + taskENTER_CRITICAL(); + { + /* If null is passed in here then it is the priority of the task + that called uxTaskPriorityGet() that is being queried. */ + pxTCB = prvGetTCBFromHandle( xTask ); + uxReturn = pxTCB->uxPriority; + } + taskEXIT_CRITICAL(); + + return uxReturn; + } + +#endif /* INCLUDE_uxTaskPriorityGet */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_uxTaskPriorityGet == 1 ) + + UBaseType_t uxTaskPriorityGetFromISR( const TaskHandle_t xTask ) + { + TCB_t const *pxTCB; + UBaseType_t uxReturn, uxSavedInterruptState; + + /* RTOS ports that support interrupt nesting have the concept of a + maximum system call (or maximum API call) interrupt priority. + Interrupts that are above the maximum system call priority are keep + permanently enabled, even when the RTOS kernel is in a critical section, + but cannot make any calls to FreeRTOS API functions. If configASSERT() + is defined in FreeRTOSConfig.h then + portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + failure if a FreeRTOS API function is called from an interrupt that has + been assigned a priority above the configured maximum system call + priority. Only FreeRTOS functions that end in FromISR can be called + from interrupts that have been assigned a priority at or (logically) + below the maximum system call interrupt priority. FreeRTOS maintains a + separate interrupt safe API to ensure interrupt entry is as fast and as + simple as possible. More information (albeit Cortex-M specific) is + provided on the following link: + https://www.freertos.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + uxSavedInterruptState = portSET_INTERRUPT_MASK_FROM_ISR(); + { + /* If null is passed in here then it is the priority of the calling + task that is being queried. */ + pxTCB = prvGetTCBFromHandle( xTask ); + uxReturn = pxTCB->uxPriority; + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptState ); + + return uxReturn; + } + +#endif /* INCLUDE_uxTaskPriorityGet */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskPrioritySet == 1 ) + + void vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority ) + { + TCB_t *pxTCB; + UBaseType_t uxCurrentBasePriority, uxPriorityUsedOnEntry; + BaseType_t xYieldRequired = pdFALSE; + + configASSERT( ( uxNewPriority < configMAX_PRIORITIES ) ); + + /* Ensure the new priority is valid. */ + if( uxNewPriority >= ( UBaseType_t ) configMAX_PRIORITIES ) + { + uxNewPriority = ( UBaseType_t ) configMAX_PRIORITIES - ( UBaseType_t ) 1U; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + taskENTER_CRITICAL(); + { + /* If null is passed in here then it is the priority of the calling + task that is being changed. */ + pxTCB = prvGetTCBFromHandle( xTask ); + + traceTASK_PRIORITY_SET( pxTCB, uxNewPriority ); + + #if ( configUSE_MUTEXES == 1 ) + { + uxCurrentBasePriority = pxTCB->uxBasePriority; + } + #else + { + uxCurrentBasePriority = pxTCB->uxPriority; + } + #endif + + if( uxCurrentBasePriority != uxNewPriority ) + { + /* The priority change may have readied a task of higher + priority than the calling task. */ + if( uxNewPriority > uxCurrentBasePriority ) + { + if( pxTCB != pxCurrentTCB ) + { + /* The priority of a task other than the currently + running task is being raised. Is the priority being + raised above that of the running task? */ + if( uxNewPriority >= pxCurrentTCB->uxPriority ) + { + xYieldRequired = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + /* The priority of the running task is being raised, + but the running task must already be the highest + priority task able to run so no yield is required. */ + } + } + else if( pxTCB == pxCurrentTCB ) + { + /* Setting the priority of the running task down means + there may now be another task of higher priority that + is ready to execute. */ + xYieldRequired = pdTRUE; + } + else + { + /* Setting the priority of any other task down does not + require a yield as the running task must be above the + new priority of the task being modified. */ + } + + /* Remember the ready list the task might be referenced from + before its uxPriority member is changed so the + taskRESET_READY_PRIORITY() macro can function correctly. */ + uxPriorityUsedOnEntry = pxTCB->uxPriority; + + #if ( configUSE_MUTEXES == 1 ) + { + /* Only change the priority being used if the task is not + currently using an inherited priority. */ + if( pxTCB->uxBasePriority == pxTCB->uxPriority ) + { + pxTCB->uxPriority = uxNewPriority; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* The base priority gets set whatever. */ + pxTCB->uxBasePriority = uxNewPriority; + } + #else + { + pxTCB->uxPriority = uxNewPriority; + } + #endif + + /* Only reset the event list item value if the value is not + being used for anything else. */ + if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) + { + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxNewPriority ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* If the task is in the blocked or suspended list we need do + nothing more than change its priority variable. However, if + the task is in a ready list it needs to be removed and placed + in the list appropriate to its new priority. */ + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ) + { + /* The task is currently in its ready list - remove before + adding it to it's new ready list. As we are in a critical + section we can do this even if the scheduler is suspended. */ + if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + /* It is known that the task is in its ready list so + there is no need to check again and the port level + reset macro can be called directly. */ + portRESET_READY_PRIORITY( uxPriorityUsedOnEntry, uxTopReadyPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + prvAddTaskToReadyList( pxTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( xYieldRequired != pdFALSE ) + { + taskYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Remove compiler warning about unused variables when the port + optimised task selection is not being used. */ + ( void ) uxPriorityUsedOnEntry; + } + } + taskEXIT_CRITICAL(); + } + +#endif /* INCLUDE_vTaskPrioritySet */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskSuspend == 1 ) + + void vTaskSuspend( TaskHandle_t xTaskToSuspend ) + { + TCB_t *pxTCB; + + taskENTER_CRITICAL(); + { + /* If null is passed in here then it is the running task that is + being suspended. */ + pxTCB = prvGetTCBFromHandle( xTaskToSuspend ); + + traceTASK_SUSPEND( pxTCB ); + + /* Remove task from the ready/delayed list and place in the + suspended list. */ + if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + taskRESET_READY_PRIORITY( pxTCB->uxPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Is the task waiting on an event also? */ + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) + { + ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + vListInsertEnd( &xSuspendedTaskList, &( pxTCB->xStateListItem ) ); + + #if( configUSE_TASK_NOTIFICATIONS == 1 ) + { + if( pxTCB->ucNotifyState == taskWAITING_NOTIFICATION ) + { + /* The task was blocked to wait for a notification, but is + now suspended, so no notification was received. */ + pxTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION; + } + } + #endif + } + taskEXIT_CRITICAL(); + + if( xSchedulerRunning != pdFALSE ) + { + /* Reset the next expected unblock time in case it referred to the + task that is now in the Suspended state. */ + taskENTER_CRITICAL(); + { + prvResetNextTaskUnblockTime(); + } + taskEXIT_CRITICAL(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( pxTCB == pxCurrentTCB ) + { + if( xSchedulerRunning != pdFALSE ) + { + /* The current task has just been suspended. */ + configASSERT( uxSchedulerSuspended == 0 ); + portYIELD_WITHIN_API(); + } + else + { + /* The scheduler is not running, but the task that was pointed + to by pxCurrentTCB has just been suspended and pxCurrentTCB + must be adjusted to point to a different task. */ + if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == uxCurrentNumberOfTasks ) /*lint !e931 Right has no side effect, just volatile. */ + { + /* No other tasks are ready, so set pxCurrentTCB back to + NULL so when the next task is created pxCurrentTCB will + be set to point to it no matter what its relative priority + is. */ + pxCurrentTCB = NULL; + } + else + { + vTaskSwitchContext(); + } + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* INCLUDE_vTaskSuspend */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskSuspend == 1 ) + + static BaseType_t prvTaskIsTaskSuspended( const TaskHandle_t xTask ) + { + BaseType_t xReturn = pdFALSE; + const TCB_t * const pxTCB = xTask; + + /* Accesses xPendingReadyList so must be called from a critical + section. */ + + /* It does not make sense to check if the calling task is suspended. */ + configASSERT( xTask ); + + /* Is the task being resumed actually in the suspended list? */ + if( listIS_CONTAINED_WITHIN( &xSuspendedTaskList, &( pxTCB->xStateListItem ) ) != pdFALSE ) + { + /* Has the task already been resumed from within an ISR? */ + if( listIS_CONTAINED_WITHIN( &xPendingReadyList, &( pxTCB->xEventListItem ) ) == pdFALSE ) + { + /* Is it in the suspended list because it is in the Suspended + state, or because is is blocked with no timeout? */ + if( listIS_CONTAINED_WITHIN( NULL, &( pxTCB->xEventListItem ) ) != pdFALSE ) /*lint !e961. The cast is only redundant when NULL is used. */ + { + xReturn = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xReturn; + } /*lint !e818 xTask cannot be a pointer to const because it is a typedef. */ + +#endif /* INCLUDE_vTaskSuspend */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskSuspend == 1 ) + + void vTaskResume( TaskHandle_t xTaskToResume ) + { + TCB_t * const pxTCB = xTaskToResume; + + /* It does not make sense to resume the calling task. */ + configASSERT( xTaskToResume ); + + /* The parameter cannot be NULL as it is impossible to resume the + currently executing task. */ + if( ( pxTCB != pxCurrentTCB ) && ( pxTCB != NULL ) ) + { + taskENTER_CRITICAL(); + { + if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE ) + { + traceTASK_RESUME( pxTCB ); + + /* The ready list can be accessed even if the scheduler is + suspended because this is inside a critical section. */ + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxTCB ); + + /* A higher priority task may have just been resumed. */ + if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) + { + /* This yield may not cause the task just resumed to run, + but will leave the lists in the correct state for the + next yield. */ + taskYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* INCLUDE_vTaskSuspend */ + +/*-----------------------------------------------------------*/ + +#if ( ( INCLUDE_xTaskResumeFromISR == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) ) + + BaseType_t xTaskResumeFromISR( TaskHandle_t xTaskToResume ) + { + BaseType_t xYieldRequired = pdFALSE; + TCB_t * const pxTCB = xTaskToResume; + UBaseType_t uxSavedInterruptStatus; + + configASSERT( xTaskToResume ); + + /* RTOS ports that support interrupt nesting have the concept of a + maximum system call (or maximum API call) interrupt priority. + Interrupts that are above the maximum system call priority are keep + permanently enabled, even when the RTOS kernel is in a critical section, + but cannot make any calls to FreeRTOS API functions. If configASSERT() + is defined in FreeRTOSConfig.h then + portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + failure if a FreeRTOS API function is called from an interrupt that has + been assigned a priority above the configured maximum system call + priority. Only FreeRTOS functions that end in FromISR can be called + from interrupts that have been assigned a priority at or (logically) + below the maximum system call interrupt priority. FreeRTOS maintains a + separate interrupt safe API to ensure interrupt entry is as fast and as + simple as possible. More information (albeit Cortex-M specific) is + provided on the following link: + https://www.freertos.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + if( prvTaskIsTaskSuspended( pxTCB ) != pdFALSE ) + { + traceTASK_RESUME_FROM_ISR( pxTCB ); + + /* Check the ready lists can be accessed. */ + if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) + { + /* Ready lists can be accessed so move the task from the + suspended list to the ready list directly. */ + if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) + { + xYieldRequired = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxTCB ); + } + else + { + /* The delayed or ready lists cannot be accessed so the task + is held in the pending ready list until the scheduler is + unsuspended. */ + vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xYieldRequired; + } + +#endif /* ( ( INCLUDE_xTaskResumeFromISR == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) ) */ +/*-----------------------------------------------------------*/ + +void vTaskStartScheduler( void ) +{ +BaseType_t xReturn; + + /* Add the idle task at the lowest priority. */ + #if( configSUPPORT_STATIC_ALLOCATION == 1 ) + { + StaticTask_t *pxIdleTaskTCBBuffer = NULL; + StackType_t *pxIdleTaskStackBuffer = NULL; + uint32_t ulIdleTaskStackSize; + + /* The Idle task is created using user provided RAM - obtain the + address of the RAM then create the idle task. */ + vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize ); + xIdleTaskHandle = xTaskCreateStatic( prvIdleTask, + configIDLE_TASK_NAME, + ulIdleTaskStackSize, + ( void * ) NULL, /*lint !e961. The cast is not redundant for all compilers. */ + portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */ + pxIdleTaskStackBuffer, + pxIdleTaskTCBBuffer ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */ + + if( xIdleTaskHandle != NULL ) + { + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + } + } + #else + { + /* The Idle task is being created using dynamically allocated RAM. */ + xReturn = xTaskCreate( prvIdleTask, + configIDLE_TASK_NAME, + configMINIMAL_STACK_SIZE, + ( void * ) NULL, + portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */ + &xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */ + } + #endif /* configSUPPORT_STATIC_ALLOCATION */ + + #if ( configUSE_TIMERS == 1 ) + { + if( xReturn == pdPASS ) + { + xReturn = xTimerCreateTimerTask(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_TIMERS */ + + if( xReturn == pdPASS ) + { + /* freertos_tasks_c_additions_init() should only be called if the user + definable macro FREERTOS_TASKS_C_ADDITIONS_INIT() is defined, as that is + the only macro called by the function. */ + #ifdef FREERTOS_TASKS_C_ADDITIONS_INIT + { + freertos_tasks_c_additions_init(); + } + #endif + + /* Interrupts are turned off here, to ensure a tick does not occur + before or during the call to xPortStartScheduler(). The stacks of + the created tasks contain a status word with interrupts switched on + so interrupts will automatically get re-enabled when the first task + starts to run. */ + portDISABLE_INTERRUPTS(); + + #if ( configUSE_NEWLIB_REENTRANT == 1 ) + { + /* Switch Newlib's _impure_ptr variable to point to the _reent + structure specific to the task that will run first. + See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html + for additional information. */ + _impure_ptr = &( pxCurrentTCB->xNewLib_reent ); + } + #endif /* configUSE_NEWLIB_REENTRANT */ + + xNextTaskUnblockTime = portMAX_DELAY; + xSchedulerRunning = pdTRUE; + xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT; + + /* If configGENERATE_RUN_TIME_STATS is defined then the following + macro must be defined to configure the timer/counter used to generate + the run time counter time base. NOTE: If configGENERATE_RUN_TIME_STATS + is set to 0 and the following line fails to build then ensure you do not + have portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() defined in your + FreeRTOSConfig.h file. */ + portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(); + + traceTASK_SWITCHED_IN(); + + /* Setting up the timer tick is hardware specific and thus in the + portable interface. */ + if( xPortStartScheduler() != pdFALSE ) + { + /* Should not reach here as if the scheduler is running the + function will not return. */ + } + else + { + /* Should only reach here if a task calls xTaskEndScheduler(). */ + } + } + else + { + /* This line will only be reached if the kernel could not be started, + because there was not enough FreeRTOS heap to create the idle task + or the timer task. */ + configASSERT( xReturn != errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ); + } + + /* Prevent compiler warnings if INCLUDE_xTaskGetIdleTaskHandle is set to 0, + meaning xIdleTaskHandle is not used anywhere else. */ + ( void ) xIdleTaskHandle; +} +/*-----------------------------------------------------------*/ + +void vTaskEndScheduler( void ) +{ + /* Stop the scheduler interrupts and call the portable scheduler end + routine so the original ISRs can be restored if necessary. The port + layer must ensure interrupts enable bit is left in the correct state. */ + portDISABLE_INTERRUPTS(); + xSchedulerRunning = pdFALSE; + vPortEndScheduler(); +} +/*----------------------------------------------------------*/ + +void vTaskSuspendAll( void ) +{ + /* A critical section is not required as the variable is of type + BaseType_t. Please read Richard Barry's reply in the following link to a + post in the FreeRTOS support forum before reporting this as a bug! - + http://goo.gl/wu4acr */ + + /* portSOFRWARE_BARRIER() is only implemented for emulated/simulated ports that + do not otherwise exhibit real time behaviour. */ + portSOFTWARE_BARRIER(); + + /* The scheduler is suspended if uxSchedulerSuspended is non-zero. An increment + is used to allow calls to vTaskSuspendAll() to nest. */ + ++uxSchedulerSuspended; + + /* Enforces ordering for ports and optimised compilers that may otherwise place + the above increment elsewhere. */ + portMEMORY_BARRIER(); +} +/*----------------------------------------------------------*/ + +#if ( configUSE_TICKLESS_IDLE != 0 ) + + static TickType_t prvGetExpectedIdleTime( void ) + { + TickType_t xReturn; + UBaseType_t uxHigherPriorityReadyTasks = pdFALSE; + + /* uxHigherPriorityReadyTasks takes care of the case where + configUSE_PREEMPTION is 0, so there may be tasks above the idle priority + task that are in the Ready state, even though the idle task is + running. */ + #if( configUSE_PORT_OPTIMISED_TASK_SELECTION == 0 ) + { + if( uxTopReadyPriority > tskIDLE_PRIORITY ) + { + uxHigherPriorityReadyTasks = pdTRUE; + } + } + #else + { + const UBaseType_t uxLeastSignificantBit = ( UBaseType_t ) 0x01; + + /* When port optimised task selection is used the uxTopReadyPriority + variable is used as a bit map. If bits other than the least + significant bit are set then there are tasks that have a priority + above the idle priority that are in the Ready state. This takes + care of the case where the co-operative scheduler is in use. */ + if( uxTopReadyPriority > uxLeastSignificantBit ) + { + uxHigherPriorityReadyTasks = pdTRUE; + } + } + #endif + + if( pxCurrentTCB->uxPriority > tskIDLE_PRIORITY ) + { + xReturn = 0; + } + else if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > 1 ) + { + /* There are other idle priority tasks in the ready state. If + time slicing is used then the very next tick interrupt must be + processed. */ + xReturn = 0; + } + else if( uxHigherPriorityReadyTasks != pdFALSE ) + { + /* There are tasks in the Ready state that have a priority above the + idle priority. This path can only be reached if + configUSE_PREEMPTION is 0. */ + xReturn = 0; + } + else + { + xReturn = xNextTaskUnblockTime - xTickCount; + } + + return xReturn; + } + +#endif /* configUSE_TICKLESS_IDLE */ +/*----------------------------------------------------------*/ + +BaseType_t xTaskResumeAll( void ) +{ +TCB_t *pxTCB = NULL; +BaseType_t xAlreadyYielded = pdFALSE; + + /* If uxSchedulerSuspended is zero then this function does not match a + previous call to vTaskSuspendAll(). */ + configASSERT( uxSchedulerSuspended ); + + /* It is possible that an ISR caused a task to be removed from an event + list while the scheduler was suspended. If this was the case then the + removed task will have been added to the xPendingReadyList. Once the + scheduler has been resumed it is safe to move all the pending ready + tasks from this list into their appropriate ready list. */ + taskENTER_CRITICAL(); + { + --uxSchedulerSuspended; + + if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) + { + if( uxCurrentNumberOfTasks > ( UBaseType_t ) 0U ) + { + /* Move any readied tasks from the pending list into the + appropriate ready list. */ + while( listLIST_IS_EMPTY( &xPendingReadyList ) == pdFALSE ) + { + pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xPendingReadyList ) ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxTCB ); + + /* If the moved task has a priority higher than the current + task then a yield must be performed. */ + if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) + { + xYieldPending = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + if( pxTCB != NULL ) + { + /* A task was unblocked while the scheduler was suspended, + which may have prevented the next unblock time from being + re-calculated, in which case re-calculate it now. Mainly + important for low power tickless implementations, where + this can prevent an unnecessary exit from low power + state. */ + prvResetNextTaskUnblockTime(); + } + + /* If any ticks occurred while the scheduler was suspended then + they should be processed now. This ensures the tick count does + not slip, and that any delayed tasks are resumed at the correct + time. */ + { + TickType_t xPendedCounts = xPendedTicks; /* Non-volatile copy. */ + + if( xPendedCounts > ( TickType_t ) 0U ) + { + do + { + if( xTaskIncrementTick() != pdFALSE ) + { + xYieldPending = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + --xPendedCounts; + } while( xPendedCounts > ( TickType_t ) 0U ); + + xPendedTicks = 0; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + if( xYieldPending != pdFALSE ) + { + #if( configUSE_PREEMPTION != 0 ) + { + xAlreadyYielded = pdTRUE; + } + #endif + taskYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + return xAlreadyYielded; +} +/*-----------------------------------------------------------*/ + +TickType_t xTaskGetTickCount( void ) +{ +TickType_t xTicks; + + /* Critical section required if running on a 16 bit processor. */ + portTICK_TYPE_ENTER_CRITICAL(); + { + xTicks = xTickCount; + } + portTICK_TYPE_EXIT_CRITICAL(); + + return xTicks; +} +/*-----------------------------------------------------------*/ + +TickType_t xTaskGetTickCountFromISR( void ) +{ +TickType_t xReturn; +UBaseType_t uxSavedInterruptStatus; + + /* RTOS ports that support interrupt nesting have the concept of a maximum + system call (or maximum API call) interrupt priority. Interrupts that are + above the maximum system call priority are kept permanently enabled, even + when the RTOS kernel is in a critical section, but cannot make any calls to + FreeRTOS API functions. If configASSERT() is defined in FreeRTOSConfig.h + then portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + failure if a FreeRTOS API function is called from an interrupt that has been + assigned a priority above the configured maximum system call priority. + Only FreeRTOS functions that end in FromISR can be called from interrupts + that have been assigned a priority at or (logically) below the maximum + system call interrupt priority. FreeRTOS maintains a separate interrupt + safe API to ensure interrupt entry is as fast and as simple as possible. + More information (albeit Cortex-M specific) is provided on the following + link: https://www.freertos.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + uxSavedInterruptStatus = portTICK_TYPE_SET_INTERRUPT_MASK_FROM_ISR(); + { + xReturn = xTickCount; + } + portTICK_TYPE_CLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +UBaseType_t uxTaskGetNumberOfTasks( void ) +{ + /* A critical section is not required because the variables are of type + BaseType_t. */ + return uxCurrentNumberOfTasks; +} +/*-----------------------------------------------------------*/ + +char *pcTaskGetName( TaskHandle_t xTaskToQuery ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +{ +TCB_t *pxTCB; + + /* If null is passed in here then the name of the calling task is being + queried. */ + pxTCB = prvGetTCBFromHandle( xTaskToQuery ); + configASSERT( pxTCB ); + return &( pxTCB->pcTaskName[ 0 ] ); +} +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_xTaskGetHandle == 1 ) + + static TCB_t *prvSearchForNameWithinSingleList( List_t *pxList, const char pcNameToQuery[] ) + { + TCB_t *pxNextTCB, *pxFirstTCB, *pxReturn = NULL; + UBaseType_t x; + char cNextChar; + BaseType_t xBreakLoop; + + /* This function is called with the scheduler suspended. */ + + if( listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0 ) + { + listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + + do + { + listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + + /* Check each character in the name looking for a match or + mismatch. */ + xBreakLoop = pdFALSE; + for( x = ( UBaseType_t ) 0; x < ( UBaseType_t ) configMAX_TASK_NAME_LEN; x++ ) + { + cNextChar = pxNextTCB->pcTaskName[ x ]; + + if( cNextChar != pcNameToQuery[ x ] ) + { + /* Characters didn't match. */ + xBreakLoop = pdTRUE; + } + else if( cNextChar == ( char ) 0x00 ) + { + /* Both strings terminated, a match must have been + found. */ + pxReturn = pxNextTCB; + xBreakLoop = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + if( xBreakLoop != pdFALSE ) + { + break; + } + } + + if( pxReturn != NULL ) + { + /* The handle has been found. */ + break; + } + + } while( pxNextTCB != pxFirstTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return pxReturn; + } + +#endif /* INCLUDE_xTaskGetHandle */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_xTaskGetHandle == 1 ) + + TaskHandle_t xTaskGetHandle( const char *pcNameToQuery ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + { + UBaseType_t uxQueue = configMAX_PRIORITIES; + TCB_t* pxTCB; + + /* Task names will be truncated to configMAX_TASK_NAME_LEN - 1 bytes. */ + configASSERT( strlen( pcNameToQuery ) < configMAX_TASK_NAME_LEN ); + + vTaskSuspendAll(); + { + /* Search the ready lists. */ + do + { + uxQueue--; + pxTCB = prvSearchForNameWithinSingleList( ( List_t * ) &( pxReadyTasksLists[ uxQueue ] ), pcNameToQuery ); + + if( pxTCB != NULL ) + { + /* Found the handle. */ + break; + } + + } while( uxQueue > ( UBaseType_t ) tskIDLE_PRIORITY ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + + /* Search the delayed lists. */ + if( pxTCB == NULL ) + { + pxTCB = prvSearchForNameWithinSingleList( ( List_t * ) pxDelayedTaskList, pcNameToQuery ); + } + + if( pxTCB == NULL ) + { + pxTCB = prvSearchForNameWithinSingleList( ( List_t * ) pxOverflowDelayedTaskList, pcNameToQuery ); + } + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + if( pxTCB == NULL ) + { + /* Search the suspended list. */ + pxTCB = prvSearchForNameWithinSingleList( &xSuspendedTaskList, pcNameToQuery ); + } + } + #endif + + #if( INCLUDE_vTaskDelete == 1 ) + { + if( pxTCB == NULL ) + { + /* Search the deleted list. */ + pxTCB = prvSearchForNameWithinSingleList( &xTasksWaitingTermination, pcNameToQuery ); + } + } + #endif + } + ( void ) xTaskResumeAll(); + + return pxTCB; + } + +#endif /* INCLUDE_xTaskGetHandle */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + UBaseType_t uxTaskGetSystemState( TaskStatus_t * const pxTaskStatusArray, const UBaseType_t uxArraySize, uint32_t * const pulTotalRunTime ) + { + UBaseType_t uxTask = 0, uxQueue = configMAX_PRIORITIES; + + vTaskSuspendAll(); + { + /* Is there a space in the array for each task in the system? */ + if( uxArraySize >= uxCurrentNumberOfTasks ) + { + /* Fill in an TaskStatus_t structure with information on each + task in the Ready state. */ + do + { + uxQueue--; + uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &( pxReadyTasksLists[ uxQueue ] ), eReady ); + + } while( uxQueue > ( UBaseType_t ) tskIDLE_PRIORITY ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + + /* Fill in an TaskStatus_t structure with information on each + task in the Blocked state. */ + uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), ( List_t * ) pxDelayedTaskList, eBlocked ); + uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), ( List_t * ) pxOverflowDelayedTaskList, eBlocked ); + + #if( INCLUDE_vTaskDelete == 1 ) + { + /* Fill in an TaskStatus_t structure with information on + each task that has been deleted but not yet cleaned up. */ + uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &xTasksWaitingTermination, eDeleted ); + } + #endif + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + /* Fill in an TaskStatus_t structure with information on + each task in the Suspended state. */ + uxTask += prvListTasksWithinSingleList( &( pxTaskStatusArray[ uxTask ] ), &xSuspendedTaskList, eSuspended ); + } + #endif + + #if ( configGENERATE_RUN_TIME_STATS == 1) + { + if( pulTotalRunTime != NULL ) + { + #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE + portALT_GET_RUN_TIME_COUNTER_VALUE( ( *pulTotalRunTime ) ); + #else + *pulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE(); + #endif + } + } + #else + { + if( pulTotalRunTime != NULL ) + { + *pulTotalRunTime = 0; + } + } + #endif + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + ( void ) xTaskResumeAll(); + + return uxTask; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*----------------------------------------------------------*/ + +#if ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) + + TaskHandle_t xTaskGetIdleTaskHandle( void ) + { + /* If xTaskGetIdleTaskHandle() is called before the scheduler has been + started, then xIdleTaskHandle will be NULL. */ + configASSERT( ( xIdleTaskHandle != NULL ) ); + return xIdleTaskHandle; + } + +#endif /* INCLUDE_xTaskGetIdleTaskHandle */ +/*----------------------------------------------------------*/ + +/* This conditional compilation should use inequality to 0, not equality to 1. +This is to ensure vTaskStepTick() is available when user defined low power mode +implementations require configUSE_TICKLESS_IDLE to be set to a value other than +1. */ +#if ( configUSE_TICKLESS_IDLE != 0 ) + + void vTaskStepTick( const TickType_t xTicksToJump ) + { + /* Correct the tick count value after a period during which the tick + was suppressed. Note this does *not* call the tick hook function for + each stepped tick. */ + configASSERT( ( xTickCount + xTicksToJump ) <= xNextTaskUnblockTime ); + xTickCount += xTicksToJump; + traceINCREASE_TICK_COUNT( xTicksToJump ); + } + +#endif /* configUSE_TICKLESS_IDLE */ +/*----------------------------------------------------------*/ + +BaseType_t xTaskCatchUpTicks( TickType_t xTicksToCatchUp ) +{ +BaseType_t xYieldRequired = pdFALSE; + + /* Must not be called with the scheduler suspended as the implementation + relies on xPendedTicks being wound down to 0 in xTaskResumeAll(). */ + configASSERT( uxSchedulerSuspended == 0 ); + + /* Use xPendedTicks to mimic xTicksToCatchUp number of ticks occurring when + the scheduler is suspended so the ticks are executed in xTaskResumeAll(). */ + vTaskSuspendAll(); + xPendedTicks += xTicksToCatchUp; + xYieldRequired = xTaskResumeAll(); + + return xYieldRequired; +} +/*----------------------------------------------------------*/ + +#if ( INCLUDE_xTaskAbortDelay == 1 ) + + BaseType_t xTaskAbortDelay( TaskHandle_t xTask ) + { + TCB_t *pxTCB = xTask; + BaseType_t xReturn; + + configASSERT( pxTCB ); + + vTaskSuspendAll(); + { + /* A task can only be prematurely removed from the Blocked state if + it is actually in the Blocked state. */ + if( eTaskGetState( xTask ) == eBlocked ) + { + xReturn = pdPASS; + + /* Remove the reference to the task from the blocked list. An + interrupt won't touch the xStateListItem because the + scheduler is suspended. */ + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + + /* Is the task waiting on an event also? If so remove it from + the event list too. Interrupts can touch the event list item, + even though the scheduler is suspended, so a critical section + is used. */ + taskENTER_CRITICAL(); + { + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) + { + ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); + + /* This lets the task know it was forcibly removed from the + blocked state so it should not re-evaluate its block time and + then block again. */ + pxTCB->ucDelayAborted = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + /* Place the unblocked task into the appropriate ready list. */ + prvAddTaskToReadyList( pxTCB ); + + /* A task being unblocked cannot cause an immediate context + switch if preemption is turned off. */ + #if ( configUSE_PREEMPTION == 1 ) + { + /* Preemption is on, but a context switch should only be + performed if the unblocked task has a priority that is + equal to or higher than the currently executing task. */ + if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* Pend the yield to be performed when the scheduler + is unsuspended. */ + xYieldPending = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_PREEMPTION */ + } + else + { + xReturn = pdFAIL; + } + } + ( void ) xTaskResumeAll(); + + return xReturn; + } + +#endif /* INCLUDE_xTaskAbortDelay */ +/*----------------------------------------------------------*/ + +BaseType_t xTaskIncrementTick( void ) +{ +TCB_t * pxTCB; +TickType_t xItemValue; +BaseType_t xSwitchRequired = pdFALSE; + + /* Called by the portable layer each time a tick interrupt occurs. + Increments the tick then checks to see if the new tick value will cause any + tasks to be unblocked. */ + traceTASK_INCREMENT_TICK( xTickCount ); + if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) + { + /* Minor optimisation. The tick count cannot change in this + block. */ + const TickType_t xConstTickCount = xTickCount + ( TickType_t ) 1; + + /* Increment the RTOS tick, switching the delayed and overflowed + delayed lists if it wraps to 0. */ + xTickCount = xConstTickCount; + + if( xConstTickCount == ( TickType_t ) 0U ) /*lint !e774 'if' does not always evaluate to false as it is looking for an overflow. */ + { + taskSWITCH_DELAYED_LISTS(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* See if this tick has made a timeout expire. Tasks are stored in + the queue in the order of their wake time - meaning once one task + has been found whose block time has not expired there is no need to + look any further down the list. */ + if( xConstTickCount >= xNextTaskUnblockTime ) + { + for( ;; ) + { + if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ) + { + /* The delayed list is empty. Set xNextTaskUnblockTime + to the maximum possible value so it is extremely + unlikely that the + if( xTickCount >= xNextTaskUnblockTime ) test will pass + next time through. */ + xNextTaskUnblockTime = portMAX_DELAY; /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + break; + } + else + { + /* The delayed list is not empty, get the value of the + item at the head of the delayed list. This is the time + at which the task at the head of the delayed list must + be removed from the Blocked state. */ + pxTCB = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + xItemValue = listGET_LIST_ITEM_VALUE( &( pxTCB->xStateListItem ) ); + + if( xConstTickCount < xItemValue ) + { + /* It is not time to unblock this item yet, but the + item value is the time at which the task at the head + of the blocked list must be removed from the Blocked + state - so record the item value in + xNextTaskUnblockTime. */ + xNextTaskUnblockTime = xItemValue; + break; /*lint !e9011 Code structure here is deedmed easier to understand with multiple breaks. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* It is time to remove the item from the Blocked state. */ + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + + /* Is the task waiting on an event also? If so remove + it from the event list. */ + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) + { + ( void ) uxListRemove( &( pxTCB->xEventListItem ) ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Place the unblocked task into the appropriate ready + list. */ + prvAddTaskToReadyList( pxTCB ); + + /* A task being unblocked cannot cause an immediate + context switch if preemption is turned off. */ + #if ( configUSE_PREEMPTION == 1 ) + { + /* Preemption is on, but a context switch should + only be performed if the unblocked task has a + priority that is equal to or higher than the + currently executing task. */ + if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) + { + xSwitchRequired = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_PREEMPTION */ + } + } + } + + /* Tasks of equal priority to the currently running task will share + processing time (time slice) if preemption is on, and the application + writer has not explicitly turned time slicing off. */ + #if ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) + { + if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ pxCurrentTCB->uxPriority ] ) ) > ( UBaseType_t ) 1 ) + { + xSwitchRequired = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configUSE_TIME_SLICING == 1 ) ) */ + + #if ( configUSE_TICK_HOOK == 1 ) + { + /* Guard against the tick hook being called when the pended tick + count is being unwound (when the scheduler is being unlocked). */ + if( xPendedTicks == ( TickType_t ) 0 ) + { + vApplicationTickHook(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_TICK_HOOK */ + + #if ( configUSE_PREEMPTION == 1 ) + { + if( xYieldPending != pdFALSE ) + { + xSwitchRequired = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_PREEMPTION */ + } + else + { + ++xPendedTicks; + + /* The tick hook gets called at regular intervals, even if the + scheduler is locked. */ + #if ( configUSE_TICK_HOOK == 1 ) + { + vApplicationTickHook(); + } + #endif + } + + return xSwitchRequired; +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + + void vTaskSetApplicationTaskTag( TaskHandle_t xTask, TaskHookFunction_t pxHookFunction ) + { + TCB_t *xTCB; + + /* If xTask is NULL then it is the task hook of the calling task that is + getting set. */ + if( xTask == NULL ) + { + xTCB = ( TCB_t * ) pxCurrentTCB; + } + else + { + xTCB = xTask; + } + + /* Save the hook function in the TCB. A critical section is required as + the value can be accessed from an interrupt. */ + taskENTER_CRITICAL(); + { + xTCB->pxTaskTag = pxHookFunction; + } + taskEXIT_CRITICAL(); + } + +#endif /* configUSE_APPLICATION_TASK_TAG */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + + TaskHookFunction_t xTaskGetApplicationTaskTag( TaskHandle_t xTask ) + { + TCB_t *pxTCB; + TaskHookFunction_t xReturn; + + /* If xTask is NULL then set the calling task's hook. */ + pxTCB = prvGetTCBFromHandle( xTask ); + + /* Save the hook function in the TCB. A critical section is required as + the value can be accessed from an interrupt. */ + taskENTER_CRITICAL(); + { + xReturn = pxTCB->pxTaskTag; + } + taskEXIT_CRITICAL(); + + return xReturn; + } + +#endif /* configUSE_APPLICATION_TASK_TAG */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + + TaskHookFunction_t xTaskGetApplicationTaskTagFromISR( TaskHandle_t xTask ) + { + TCB_t *pxTCB; + TaskHookFunction_t xReturn; + UBaseType_t uxSavedInterruptStatus; + + /* If xTask is NULL then set the calling task's hook. */ + pxTCB = prvGetTCBFromHandle( xTask ); + + /* Save the hook function in the TCB. A critical section is required as + the value can be accessed from an interrupt. */ + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + xReturn = pxTCB->pxTaskTag; + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; + } + +#endif /* configUSE_APPLICATION_TASK_TAG */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + + BaseType_t xTaskCallApplicationTaskHook( TaskHandle_t xTask, void *pvParameter ) + { + TCB_t *xTCB; + BaseType_t xReturn; + + /* If xTask is NULL then we are calling our own task hook. */ + if( xTask == NULL ) + { + xTCB = pxCurrentTCB; + } + else + { + xTCB = xTask; + } + + if( xTCB->pxTaskTag != NULL ) + { + xReturn = xTCB->pxTaskTag( pvParameter ); + } + else + { + xReturn = pdFAIL; + } + + return xReturn; + } + +#endif /* configUSE_APPLICATION_TASK_TAG */ +/*-----------------------------------------------------------*/ + +void vTaskSwitchContext( void ) +{ + if( uxSchedulerSuspended != ( UBaseType_t ) pdFALSE ) + { + /* The scheduler is currently suspended - do not allow a context + switch. */ + xYieldPending = pdTRUE; + } + else + { + xYieldPending = pdFALSE; + traceTASK_SWITCHED_OUT(); + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + { + #ifdef portALT_GET_RUN_TIME_COUNTER_VALUE + portALT_GET_RUN_TIME_COUNTER_VALUE( ulTotalRunTime ); + #else + ulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE(); + #endif + + /* Add the amount of time the task has been running to the + accumulated time so far. The time the task started running was + stored in ulTaskSwitchedInTime. Note that there is no overflow + protection here so count values are only valid until the timer + overflows. The guard against negative values is to protect + against suspect run time stat counter implementations - which + are provided by the application, not the kernel. */ + if( ulTotalRunTime > ulTaskSwitchedInTime ) + { + pxCurrentTCB->ulRunTimeCounter += ( ulTotalRunTime - ulTaskSwitchedInTime ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + ulTaskSwitchedInTime = ulTotalRunTime; + } + #endif /* configGENERATE_RUN_TIME_STATS */ + + /* Check for stack overflow, if configured. */ + taskCHECK_FOR_STACK_OVERFLOW(); + + /* Before the currently running task is switched out, save its errno. */ + #if( configUSE_POSIX_ERRNO == 1 ) + { + pxCurrentTCB->iTaskErrno = FreeRTOS_errno; + } + #endif + + /* Select a new task to run using either the generic C or port + optimised asm code. */ + taskSELECT_HIGHEST_PRIORITY_TASK(); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + traceTASK_SWITCHED_IN(); + + /* After the new task is switched in, update the global errno. */ + #if( configUSE_POSIX_ERRNO == 1 ) + { + FreeRTOS_errno = pxCurrentTCB->iTaskErrno; + } + #endif + + #if ( configUSE_NEWLIB_REENTRANT == 1 ) + { + /* Switch Newlib's _impure_ptr variable to point to the _reent + structure specific to this task. + See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html + for additional information. */ + _impure_ptr = &( pxCurrentTCB->xNewLib_reent ); + } + #endif /* configUSE_NEWLIB_REENTRANT */ + } +} +/*-----------------------------------------------------------*/ + +void vTaskPlaceOnEventList( List_t * const pxEventList, const TickType_t xTicksToWait ) +{ + configASSERT( pxEventList ); + + /* THIS FUNCTION MUST BE CALLED WITH EITHER INTERRUPTS DISABLED OR THE + SCHEDULER SUSPENDED AND THE QUEUE BEING ACCESSED LOCKED. */ + + /* Place the event list item of the TCB in the appropriate event list. + This is placed in the list in priority order so the highest priority task + is the first to be woken by the event. The queue that contains the event + list is locked, preventing simultaneous access from interrupts. */ + vListInsert( pxEventList, &( pxCurrentTCB->xEventListItem ) ); + + prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); +} +/*-----------------------------------------------------------*/ + +void vTaskPlaceOnUnorderedEventList( List_t * pxEventList, const TickType_t xItemValue, const TickType_t xTicksToWait ) +{ + configASSERT( pxEventList ); + + /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. It is used by + the event groups implementation. */ + configASSERT( uxSchedulerSuspended != 0 ); + + /* Store the item value in the event list item. It is safe to access the + event list item here as interrupts won't access the event list item of a + task that is not in the Blocked state. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xEventListItem ), xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE ); + + /* Place the event list item of the TCB at the end of the appropriate event + list. It is safe to access the event list here because it is part of an + event group implementation - and interrupts don't access event groups + directly (instead they access them indirectly by pending function calls to + the task level). */ + vListInsertEnd( pxEventList, &( pxCurrentTCB->xEventListItem ) ); + + prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); +} +/*-----------------------------------------------------------*/ + +#if( configUSE_TIMERS == 1 ) + + void vTaskPlaceOnEventListRestricted( List_t * const pxEventList, TickType_t xTicksToWait, const BaseType_t xWaitIndefinitely ) + { + configASSERT( pxEventList ); + + /* This function should not be called by application code hence the + 'Restricted' in its name. It is not part of the public API. It is + designed for use by kernel code, and has special calling requirements - + it should be called with the scheduler suspended. */ + + + /* Place the event list item of the TCB in the appropriate event list. + In this case it is assume that this is the only task that is going to + be waiting on this event list, so the faster vListInsertEnd() function + can be used in place of vListInsert. */ + vListInsertEnd( pxEventList, &( pxCurrentTCB->xEventListItem ) ); + + /* If the task should block indefinitely then set the block time to a + value that will be recognised as an indefinite delay inside the + prvAddCurrentTaskToDelayedList() function. */ + if( xWaitIndefinitely != pdFALSE ) + { + xTicksToWait = portMAX_DELAY; + } + + traceTASK_DELAY_UNTIL( ( xTickCount + xTicksToWait ) ); + prvAddCurrentTaskToDelayedList( xTicksToWait, xWaitIndefinitely ); + } + +#endif /* configUSE_TIMERS */ +/*-----------------------------------------------------------*/ + +BaseType_t xTaskRemoveFromEventList( const List_t * const pxEventList ) +{ +TCB_t *pxUnblockedTCB; +BaseType_t xReturn; + + /* THIS FUNCTION MUST BE CALLED FROM A CRITICAL SECTION. It can also be + called from a critical section within an ISR. */ + + /* The event list is sorted in priority order, so the first in the list can + be removed as it is known to be the highest priority. Remove the TCB from + the delayed list, and add it to the ready list. + + If an event is for a queue that is locked then this function will never + get called - the lock count on the queue will get modified instead. This + means exclusive access to the event list is guaranteed here. + + This function assumes that a check has already been made to ensure that + pxEventList is not empty. */ + pxUnblockedTCB = listGET_OWNER_OF_HEAD_ENTRY( pxEventList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + configASSERT( pxUnblockedTCB ); + ( void ) uxListRemove( &( pxUnblockedTCB->xEventListItem ) ); + + if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) + { + ( void ) uxListRemove( &( pxUnblockedTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxUnblockedTCB ); + + #if( configUSE_TICKLESS_IDLE != 0 ) + { + /* If a task is blocked on a kernel object then xNextTaskUnblockTime + might be set to the blocked task's time out time. If the task is + unblocked for a reason other than a timeout xNextTaskUnblockTime is + normally left unchanged, because it is automatically reset to a new + value when the tick count equals xNextTaskUnblockTime. However if + tickless idling is used it might be more important to enter sleep mode + at the earliest possible time - so reset xNextTaskUnblockTime here to + ensure it is updated at the earliest possible time. */ + prvResetNextTaskUnblockTime(); + } + #endif + } + else + { + /* The delayed and ready lists cannot be accessed, so hold this task + pending until the scheduler is resumed. */ + vListInsertEnd( &( xPendingReadyList ), &( pxUnblockedTCB->xEventListItem ) ); + } + + if( pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* Return true if the task removed from the event list has a higher + priority than the calling task. This allows the calling task to know if + it should force a context switch now. */ + xReturn = pdTRUE; + + /* Mark that a yield is pending in case the user is not using the + "xHigherPriorityTaskWoken" parameter to an ISR safe FreeRTOS function. */ + xYieldPending = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vTaskRemoveFromUnorderedEventList( ListItem_t * pxEventListItem, const TickType_t xItemValue ) +{ +TCB_t *pxUnblockedTCB; + + /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. It is used by + the event flags implementation. */ + configASSERT( uxSchedulerSuspended != pdFALSE ); + + /* Store the new item value in the event list. */ + listSET_LIST_ITEM_VALUE( pxEventListItem, xItemValue | taskEVENT_LIST_ITEM_VALUE_IN_USE ); + + /* Remove the event list form the event flag. Interrupts do not access + event flags. */ + pxUnblockedTCB = listGET_LIST_ITEM_OWNER( pxEventListItem ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + configASSERT( pxUnblockedTCB ); + ( void ) uxListRemove( pxEventListItem ); + + #if( configUSE_TICKLESS_IDLE != 0 ) + { + /* If a task is blocked on a kernel object then xNextTaskUnblockTime + might be set to the blocked task's time out time. If the task is + unblocked for a reason other than a timeout xNextTaskUnblockTime is + normally left unchanged, because it is automatically reset to a new + value when the tick count equals xNextTaskUnblockTime. However if + tickless idling is used it might be more important to enter sleep mode + at the earliest possible time - so reset xNextTaskUnblockTime here to + ensure it is updated at the earliest possible time. */ + prvResetNextTaskUnblockTime(); + } + #endif + + /* Remove the task from the delayed list and add it to the ready list. The + scheduler is suspended so interrupts will not be accessing the ready + lists. */ + ( void ) uxListRemove( &( pxUnblockedTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxUnblockedTCB ); + + if( pxUnblockedTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* The unblocked task has a priority above that of the calling task, so + a context switch is required. This function is called with the + scheduler suspended so xYieldPending is set so the context switch + occurs immediately that the scheduler is resumed (unsuspended). */ + xYieldPending = pdTRUE; + } +} +/*-----------------------------------------------------------*/ + +void vTaskSetTimeOutState( TimeOut_t * const pxTimeOut ) +{ + configASSERT( pxTimeOut ); + taskENTER_CRITICAL(); + { + pxTimeOut->xOverflowCount = xNumOfOverflows; + pxTimeOut->xTimeOnEntering = xTickCount; + } + taskEXIT_CRITICAL(); +} +/*-----------------------------------------------------------*/ + +void vTaskInternalSetTimeOutState( TimeOut_t * const pxTimeOut ) +{ + /* For internal use only as it does not use a critical section. */ + pxTimeOut->xOverflowCount = xNumOfOverflows; + pxTimeOut->xTimeOnEntering = xTickCount; +} +/*-----------------------------------------------------------*/ + +BaseType_t xTaskCheckForTimeOut( TimeOut_t * const pxTimeOut, TickType_t * const pxTicksToWait ) +{ +BaseType_t xReturn; + + configASSERT( pxTimeOut ); + configASSERT( pxTicksToWait ); + + taskENTER_CRITICAL(); + { + /* Minor optimisation. The tick count cannot change in this block. */ + const TickType_t xConstTickCount = xTickCount; + const TickType_t xElapsedTime = xConstTickCount - pxTimeOut->xTimeOnEntering; + + #if( INCLUDE_xTaskAbortDelay == 1 ) + if( pxCurrentTCB->ucDelayAborted != ( uint8_t ) pdFALSE ) + { + /* The delay was aborted, which is not the same as a time out, + but has the same result. */ + pxCurrentTCB->ucDelayAborted = pdFALSE; + xReturn = pdTRUE; + } + else + #endif + + #if ( INCLUDE_vTaskSuspend == 1 ) + if( *pxTicksToWait == portMAX_DELAY ) + { + /* If INCLUDE_vTaskSuspend is set to 1 and the block time + specified is the maximum block time then the task should block + indefinitely, and therefore never time out. */ + xReturn = pdFALSE; + } + else + #endif + + if( ( xNumOfOverflows != pxTimeOut->xOverflowCount ) && ( xConstTickCount >= pxTimeOut->xTimeOnEntering ) ) /*lint !e525 Indentation preferred as is to make code within pre-processor directives clearer. */ + { + /* The tick count is greater than the time at which + vTaskSetTimeout() was called, but has also overflowed since + vTaskSetTimeOut() was called. It must have wrapped all the way + around and gone past again. This passed since vTaskSetTimeout() + was called. */ + xReturn = pdTRUE; + } + else if( xElapsedTime < *pxTicksToWait ) /*lint !e961 Explicit casting is only redundant with some compilers, whereas others require it to prevent integer conversion errors. */ + { + /* Not a genuine timeout. Adjust parameters for time remaining. */ + *pxTicksToWait -= xElapsedTime; + vTaskInternalSetTimeOutState( pxTimeOut ); + xReturn = pdFALSE; + } + else + { + *pxTicksToWait = 0; + xReturn = pdTRUE; + } + } + taskEXIT_CRITICAL(); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vTaskMissedYield( void ) +{ + xYieldPending = pdTRUE; +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + UBaseType_t uxTaskGetTaskNumber( TaskHandle_t xTask ) + { + UBaseType_t uxReturn; + TCB_t const *pxTCB; + + if( xTask != NULL ) + { + pxTCB = xTask; + uxReturn = pxTCB->uxTaskNumber; + } + else + { + uxReturn = 0U; + } + + return uxReturn; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + void vTaskSetTaskNumber( TaskHandle_t xTask, const UBaseType_t uxHandle ) + { + TCB_t * pxTCB; + + if( xTask != NULL ) + { + pxTCB = xTask; + pxTCB->uxTaskNumber = uxHandle; + } + } + +#endif /* configUSE_TRACE_FACILITY */ + +/* + * ----------------------------------------------------------- + * The Idle task. + * ---------------------------------------------------------- + * + * The portTASK_FUNCTION() macro is used to allow port/compiler specific + * language extensions. The equivalent prototype for this function is: + * + * void prvIdleTask( void *pvParameters ); + * + */ +static portTASK_FUNCTION( prvIdleTask, pvParameters ) +{ + /* Stop warnings. */ + ( void ) pvParameters; + + /** THIS IS THE RTOS IDLE TASK - WHICH IS CREATED AUTOMATICALLY WHEN THE + SCHEDULER IS STARTED. **/ + + /* In case a task that has a secure context deletes itself, in which case + the idle task is responsible for deleting the task's secure context, if + any. */ + portALLOCATE_SECURE_CONTEXT( configMINIMAL_SECURE_STACK_SIZE ); + + for( ;; ) + { + /* See if any tasks have deleted themselves - if so then the idle task + is responsible for freeing the deleted task's TCB and stack. */ + prvCheckTasksWaitingTermination(); + + #if ( configUSE_PREEMPTION == 0 ) + { + /* If we are not using preemption we keep forcing a task switch to + see if any other task has become available. If we are using + preemption we don't need to do this as any task becoming available + will automatically get the processor anyway. */ + taskYIELD(); + } + #endif /* configUSE_PREEMPTION */ + + #if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) + { + /* When using preemption tasks of equal priority will be + timesliced. If a task that is sharing the idle priority is ready + to run then the idle task should yield before the end of the + timeslice. + + A critical region is not required here as we are just reading from + the list, and an occasional incorrect value will not matter. If + the ready list at the idle priority contains more than one task + then a task other than the idle task is ready to execute. */ + if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( UBaseType_t ) 1 ) + { + taskYIELD(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) */ + + #if ( configUSE_IDLE_HOOK == 1 ) + { + extern void vApplicationIdleHook( void ); + + /* Call the user defined function from within the idle task. This + allows the application designer to add background functionality + without the overhead of a separate task. + NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES, + CALL A FUNCTION THAT MIGHT BLOCK. */ + vApplicationIdleHook(); + } + #endif /* configUSE_IDLE_HOOK */ + + /* This conditional compilation should use inequality to 0, not equality + to 1. This is to ensure portSUPPRESS_TICKS_AND_SLEEP() is called when + user defined low power mode implementations require + configUSE_TICKLESS_IDLE to be set to a value other than 1. */ + #if ( configUSE_TICKLESS_IDLE != 0 ) + { + TickType_t xExpectedIdleTime; + + /* It is not desirable to suspend then resume the scheduler on + each iteration of the idle task. Therefore, a preliminary + test of the expected idle time is performed without the + scheduler suspended. The result here is not necessarily + valid. */ + xExpectedIdleTime = prvGetExpectedIdleTime(); + + if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) + { + vTaskSuspendAll(); + { + /* Now the scheduler is suspended, the expected idle + time can be sampled again, and this time its value can + be used. */ + configASSERT( xNextTaskUnblockTime >= xTickCount ); + xExpectedIdleTime = prvGetExpectedIdleTime(); + + /* Define the following macro to set xExpectedIdleTime to 0 + if the application does not want + portSUPPRESS_TICKS_AND_SLEEP() to be called. */ + configPRE_SUPPRESS_TICKS_AND_SLEEP_PROCESSING( xExpectedIdleTime ); + + if( xExpectedIdleTime >= configEXPECTED_IDLE_TIME_BEFORE_SLEEP ) + { + traceLOW_POWER_IDLE_BEGIN(); + portSUPPRESS_TICKS_AND_SLEEP( xExpectedIdleTime ); + traceLOW_POWER_IDLE_END(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + ( void ) xTaskResumeAll(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configUSE_TICKLESS_IDLE */ + } +} +/*-----------------------------------------------------------*/ + +#if( configUSE_TICKLESS_IDLE != 0 ) + + eSleepModeStatus eTaskConfirmSleepModeStatus( void ) + { + /* The idle task exists in addition to the application tasks. */ + const UBaseType_t uxNonApplicationTasks = 1; + eSleepModeStatus eReturn = eStandardSleep; + + /* This function must be called from a critical section. */ + + if( listCURRENT_LIST_LENGTH( &xPendingReadyList ) != 0 ) + { + /* A task was made ready while the scheduler was suspended. */ + eReturn = eAbortSleep; + } + else if( xYieldPending != pdFALSE ) + { + /* A yield was pended while the scheduler was suspended. */ + eReturn = eAbortSleep; + } + else + { + /* If all the tasks are in the suspended list (which might mean they + have an infinite block time rather than actually being suspended) + then it is safe to turn all clocks off and just wait for external + interrupts. */ + if( listCURRENT_LIST_LENGTH( &xSuspendedTaskList ) == ( uxCurrentNumberOfTasks - uxNonApplicationTasks ) ) + { + eReturn = eNoTasksWaitingTimeout; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + return eReturn; + } + +#endif /* configUSE_TICKLESS_IDLE */ +/*-----------------------------------------------------------*/ + +#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 ) + + void vTaskSetThreadLocalStoragePointer( TaskHandle_t xTaskToSet, BaseType_t xIndex, void *pvValue ) + { + TCB_t *pxTCB; + + if( xIndex < configNUM_THREAD_LOCAL_STORAGE_POINTERS ) + { + pxTCB = prvGetTCBFromHandle( xTaskToSet ); + configASSERT( pxTCB != NULL ); + pxTCB->pvThreadLocalStoragePointers[ xIndex ] = pvValue; + } + } + +#endif /* configNUM_THREAD_LOCAL_STORAGE_POINTERS */ +/*-----------------------------------------------------------*/ + +#if ( configNUM_THREAD_LOCAL_STORAGE_POINTERS != 0 ) + + void *pvTaskGetThreadLocalStoragePointer( TaskHandle_t xTaskToQuery, BaseType_t xIndex ) + { + void *pvReturn = NULL; + TCB_t *pxTCB; + + if( xIndex < configNUM_THREAD_LOCAL_STORAGE_POINTERS ) + { + pxTCB = prvGetTCBFromHandle( xTaskToQuery ); + pvReturn = pxTCB->pvThreadLocalStoragePointers[ xIndex ]; + } + else + { + pvReturn = NULL; + } + + return pvReturn; + } + +#endif /* configNUM_THREAD_LOCAL_STORAGE_POINTERS */ +/*-----------------------------------------------------------*/ + +#if ( portUSING_MPU_WRAPPERS == 1 ) + + void vTaskAllocateMPURegions( TaskHandle_t xTaskToModify, const MemoryRegion_t * const xRegions ) + { + TCB_t *pxTCB; + + /* If null is passed in here then we are modifying the MPU settings of + the calling task. */ + pxTCB = prvGetTCBFromHandle( xTaskToModify ); + + vPortStoreTaskMPUSettings( &( pxTCB->xMPUSettings ), xRegions, NULL, 0 ); + } + +#endif /* portUSING_MPU_WRAPPERS */ +/*-----------------------------------------------------------*/ + +static void prvInitialiseTaskLists( void ) +{ +UBaseType_t uxPriority; + + for( uxPriority = ( UBaseType_t ) 0U; uxPriority < ( UBaseType_t ) configMAX_PRIORITIES; uxPriority++ ) + { + vListInitialise( &( pxReadyTasksLists[ uxPriority ] ) ); + } + + vListInitialise( &xDelayedTaskList1 ); + vListInitialise( &xDelayedTaskList2 ); + vListInitialise( &xPendingReadyList ); + + #if ( INCLUDE_vTaskDelete == 1 ) + { + vListInitialise( &xTasksWaitingTermination ); + } + #endif /* INCLUDE_vTaskDelete */ + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + vListInitialise( &xSuspendedTaskList ); + } + #endif /* INCLUDE_vTaskSuspend */ + + /* Start with pxDelayedTaskList using list1 and the pxOverflowDelayedTaskList + using list2. */ + pxDelayedTaskList = &xDelayedTaskList1; + pxOverflowDelayedTaskList = &xDelayedTaskList2; +} +/*-----------------------------------------------------------*/ + +static void prvCheckTasksWaitingTermination( void ) +{ + + /** THIS FUNCTION IS CALLED FROM THE RTOS IDLE TASK **/ + + #if ( INCLUDE_vTaskDelete == 1 ) + { + TCB_t *pxTCB; + + /* uxDeletedTasksWaitingCleanUp is used to prevent taskENTER_CRITICAL() + being called too often in the idle task. */ + while( uxDeletedTasksWaitingCleanUp > ( UBaseType_t ) 0U ) + { + taskENTER_CRITICAL(); + { + pxTCB = listGET_OWNER_OF_HEAD_ENTRY( ( &xTasksWaitingTermination ) ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + --uxCurrentNumberOfTasks; + --uxDeletedTasksWaitingCleanUp; + } + taskEXIT_CRITICAL(); + + prvDeleteTCB( pxTCB ); + } + } + #endif /* INCLUDE_vTaskDelete */ +} +/*-----------------------------------------------------------*/ + +#if( configUSE_TRACE_FACILITY == 1 ) + + void vTaskGetInfo( TaskHandle_t xTask, TaskStatus_t *pxTaskStatus, BaseType_t xGetFreeStackSpace, eTaskState eState ) + { + TCB_t *pxTCB; + + /* xTask is NULL then get the state of the calling task. */ + pxTCB = prvGetTCBFromHandle( xTask ); + + pxTaskStatus->xHandle = ( TaskHandle_t ) pxTCB; + pxTaskStatus->pcTaskName = ( const char * ) &( pxTCB->pcTaskName [ 0 ] ); + pxTaskStatus->uxCurrentPriority = pxTCB->uxPriority; + pxTaskStatus->pxStackBase = pxTCB->pxStack; + pxTaskStatus->xTaskNumber = pxTCB->uxTCBNumber; + + #if ( configUSE_MUTEXES == 1 ) + { + pxTaskStatus->uxBasePriority = pxTCB->uxBasePriority; + } + #else + { + pxTaskStatus->uxBasePriority = 0; + } + #endif + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + { + pxTaskStatus->ulRunTimeCounter = pxTCB->ulRunTimeCounter; + } + #else + { + pxTaskStatus->ulRunTimeCounter = 0; + } + #endif + + /* Obtaining the task state is a little fiddly, so is only done if the + value of eState passed into this function is eInvalid - otherwise the + state is just set to whatever is passed in. */ + if( eState != eInvalid ) + { + if( pxTCB == pxCurrentTCB ) + { + pxTaskStatus->eCurrentState = eRunning; + } + else + { + pxTaskStatus->eCurrentState = eState; + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + /* If the task is in the suspended list then there is a + chance it is actually just blocked indefinitely - so really + it should be reported as being in the Blocked state. */ + if( eState == eSuspended ) + { + vTaskSuspendAll(); + { + if( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) != NULL ) + { + pxTaskStatus->eCurrentState = eBlocked; + } + } + ( void ) xTaskResumeAll(); + } + } + #endif /* INCLUDE_vTaskSuspend */ + } + } + else + { + pxTaskStatus->eCurrentState = eTaskGetState( pxTCB ); + } + + /* Obtaining the stack space takes some time, so the xGetFreeStackSpace + parameter is provided to allow it to be skipped. */ + if( xGetFreeStackSpace != pdFALSE ) + { + #if ( portSTACK_GROWTH > 0 ) + { + pxTaskStatus->usStackHighWaterMark = prvTaskCheckFreeStackSpace( ( uint8_t * ) pxTCB->pxEndOfStack ); + } + #else + { + pxTaskStatus->usStackHighWaterMark = prvTaskCheckFreeStackSpace( ( uint8_t * ) pxTCB->pxStack ); + } + #endif + } + else + { + pxTaskStatus->usStackHighWaterMark = 0; + } + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + static UBaseType_t prvListTasksWithinSingleList( TaskStatus_t *pxTaskStatusArray, List_t *pxList, eTaskState eState ) + { + configLIST_VOLATILE TCB_t *pxNextTCB, *pxFirstTCB; + UBaseType_t uxTask = 0; + + if( listCURRENT_LIST_LENGTH( pxList ) > ( UBaseType_t ) 0 ) + { + listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + + /* Populate an TaskStatus_t structure within the + pxTaskStatusArray array for each task that is referenced from + pxList. See the definition of TaskStatus_t in task.h for the + meaning of each TaskStatus_t structure member. */ + do + { + listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + vTaskGetInfo( ( TaskHandle_t ) pxNextTCB, &( pxTaskStatusArray[ uxTask ] ), pdTRUE, eState ); + uxTask++; + } while( pxNextTCB != pxFirstTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return uxTask; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) + + static configSTACK_DEPTH_TYPE prvTaskCheckFreeStackSpace( const uint8_t * pucStackByte ) + { + uint32_t ulCount = 0U; + + while( *pucStackByte == ( uint8_t ) tskSTACK_FILL_BYTE ) + { + pucStackByte -= portSTACK_GROWTH; + ulCount++; + } + + ulCount /= ( uint32_t ) sizeof( StackType_t ); /*lint !e961 Casting is not redundant on smaller architectures. */ + + return ( configSTACK_DEPTH_TYPE ) ulCount; + } + +#endif /* ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_uxTaskGetStackHighWaterMark2 == 1 ) + + /* uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are the + same except for their return type. Using configSTACK_DEPTH_TYPE allows the + user to determine the return type. It gets around the problem of the value + overflowing on 8-bit types without breaking backward compatibility for + applications that expect an 8-bit return type. */ + configSTACK_DEPTH_TYPE uxTaskGetStackHighWaterMark2( TaskHandle_t xTask ) + { + TCB_t *pxTCB; + uint8_t *pucEndOfStack; + configSTACK_DEPTH_TYPE uxReturn; + + /* uxTaskGetStackHighWaterMark() and uxTaskGetStackHighWaterMark2() are + the same except for their return type. Using configSTACK_DEPTH_TYPE + allows the user to determine the return type. It gets around the + problem of the value overflowing on 8-bit types without breaking + backward compatibility for applications that expect an 8-bit return + type. */ + + pxTCB = prvGetTCBFromHandle( xTask ); + + #if portSTACK_GROWTH < 0 + { + pucEndOfStack = ( uint8_t * ) pxTCB->pxStack; + } + #else + { + pucEndOfStack = ( uint8_t * ) pxTCB->pxEndOfStack; + } + #endif + + uxReturn = prvTaskCheckFreeStackSpace( pucEndOfStack ); + + return uxReturn; + } + +#endif /* INCLUDE_uxTaskGetStackHighWaterMark2 */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) + + UBaseType_t uxTaskGetStackHighWaterMark( TaskHandle_t xTask ) + { + TCB_t *pxTCB; + uint8_t *pucEndOfStack; + UBaseType_t uxReturn; + + pxTCB = prvGetTCBFromHandle( xTask ); + + #if portSTACK_GROWTH < 0 + { + pucEndOfStack = ( uint8_t * ) pxTCB->pxStack; + } + #else + { + pucEndOfStack = ( uint8_t * ) pxTCB->pxEndOfStack; + } + #endif + + uxReturn = ( UBaseType_t ) prvTaskCheckFreeStackSpace( pucEndOfStack ); + + return uxReturn; + } + +#endif /* INCLUDE_uxTaskGetStackHighWaterMark */ +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelete == 1 ) + + static void prvDeleteTCB( TCB_t *pxTCB ) + { + /* This call is required specifically for the TriCore port. It must be + above the vPortFree() calls. The call is also used by ports/demos that + want to allocate and clean RAM statically. */ + portCLEAN_UP_TCB( pxTCB ); + + /* Free up the memory allocated by the scheduler for the task. It is up + to the task to free any memory allocated at the application level. + See the third party link http://www.nadler.com/embedded/newlibAndFreeRTOS.html + for additional information. */ + #if ( configUSE_NEWLIB_REENTRANT == 1 ) + { + _reclaim_reent( &( pxTCB->xNewLib_reent ) ); + } + #endif /* configUSE_NEWLIB_REENTRANT */ + + #if( ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) && ( configSUPPORT_STATIC_ALLOCATION == 0 ) && ( portUSING_MPU_WRAPPERS == 0 ) ) + { + /* The task can only have been allocated dynamically - free both + the stack and TCB. */ + vPortFree( pxTCB->pxStack ); + vPortFree( pxTCB ); + } + #elif( tskSTATIC_AND_DYNAMIC_ALLOCATION_POSSIBLE != 0 ) /*lint !e731 !e9029 Macro has been consolidated for readability reasons. */ + { + /* The task could have been allocated statically or dynamically, so + check what was statically allocated before trying to free the + memory. */ + if( pxTCB->ucStaticallyAllocated == tskDYNAMICALLY_ALLOCATED_STACK_AND_TCB ) + { + /* Both the stack and TCB were allocated dynamically, so both + must be freed. */ + vPortFree( pxTCB->pxStack ); + vPortFree( pxTCB ); + } + else if( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_ONLY ) + { + /* Only the stack was statically allocated, so the TCB is the + only memory that must be freed. */ + vPortFree( pxTCB ); + } + else + { + /* Neither the stack nor the TCB were allocated dynamically, so + nothing needs to be freed. */ + configASSERT( pxTCB->ucStaticallyAllocated == tskSTATICALLY_ALLOCATED_STACK_AND_TCB ); + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + } + +#endif /* INCLUDE_vTaskDelete */ +/*-----------------------------------------------------------*/ + +static void prvResetNextTaskUnblockTime( void ) +{ +TCB_t *pxTCB; + + if( listLIST_IS_EMPTY( pxDelayedTaskList ) != pdFALSE ) + { + /* The new current delayed list is empty. Set xNextTaskUnblockTime to + the maximum possible value so it is extremely unlikely that the + if( xTickCount >= xNextTaskUnblockTime ) test will pass until + there is an item in the delayed list. */ + xNextTaskUnblockTime = portMAX_DELAY; + } + else + { + /* The new current delayed list is not empty, get the value of + the item at the head of the delayed list. This is the time at + which the task at the head of the delayed list should be removed + from the Blocked state. */ + ( pxTCB ) = listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ); /*lint !e9079 void * is used as this macro is used with timers and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + xNextTaskUnblockTime = listGET_LIST_ITEM_VALUE( &( ( pxTCB )->xStateListItem ) ); + } +} +/*-----------------------------------------------------------*/ + +#if ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) ) + + TaskHandle_t xTaskGetCurrentTaskHandle( void ) + { + TaskHandle_t xReturn; + + /* A critical section is not required as this is not called from + an interrupt and the current TCB will always be the same for any + individual execution thread. */ + xReturn = pxCurrentTCB; + + return xReturn; + } + +#endif /* ( ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) || ( configUSE_MUTEXES == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) + + BaseType_t xTaskGetSchedulerState( void ) + { + BaseType_t xReturn; + + if( xSchedulerRunning == pdFALSE ) + { + xReturn = taskSCHEDULER_NOT_STARTED; + } + else + { + if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) + { + xReturn = taskSCHEDULER_RUNNING; + } + else + { + xReturn = taskSCHEDULER_SUSPENDED; + } + } + + return xReturn; + } + +#endif /* ( ( INCLUDE_xTaskGetSchedulerState == 1 ) || ( configUSE_TIMERS == 1 ) ) */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + + BaseType_t xTaskPriorityInherit( TaskHandle_t const pxMutexHolder ) + { + TCB_t * const pxMutexHolderTCB = pxMutexHolder; + BaseType_t xReturn = pdFALSE; + + /* If the mutex was given back by an interrupt while the queue was + locked then the mutex holder might now be NULL. _RB_ Is this still + needed as interrupts can no longer use mutexes? */ + if( pxMutexHolder != NULL ) + { + /* If the holder of the mutex has a priority below the priority of + the task attempting to obtain the mutex then it will temporarily + inherit the priority of the task attempting to obtain the mutex. */ + if( pxMutexHolderTCB->uxPriority < pxCurrentTCB->uxPriority ) + { + /* Adjust the mutex holder state to account for its new + priority. Only reset the event list item value if the value is + not being used for anything else. */ + if( ( listGET_LIST_ITEM_VALUE( &( pxMutexHolderTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) + { + listSET_LIST_ITEM_VALUE( &( pxMutexHolderTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* If the task being modified is in the ready state it will need + to be moved into a new list. */ + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxMutexHolderTCB->uxPriority ] ), &( pxMutexHolderTCB->xStateListItem ) ) != pdFALSE ) + { + if( uxListRemove( &( pxMutexHolderTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + /* It is known that the task is in its ready list so + there is no need to check again and the port level + reset macro can be called directly. */ + portRESET_READY_PRIORITY( pxMutexHolderTCB->uxPriority, uxTopReadyPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Inherit the priority before being moved into the new list. */ + pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority; + prvAddTaskToReadyList( pxMutexHolderTCB ); + } + else + { + /* Just inherit the priority. */ + pxMutexHolderTCB->uxPriority = pxCurrentTCB->uxPriority; + } + + traceTASK_PRIORITY_INHERIT( pxMutexHolderTCB, pxCurrentTCB->uxPriority ); + + /* Inheritance occurred. */ + xReturn = pdTRUE; + } + else + { + if( pxMutexHolderTCB->uxBasePriority < pxCurrentTCB->uxPriority ) + { + /* The base priority of the mutex holder is lower than the + priority of the task attempting to take the mutex, but the + current priority of the mutex holder is not lower than the + priority of the task attempting to take the mutex. + Therefore the mutex holder must have already inherited a + priority, but inheritance would have occurred if that had + not been the case. */ + xReturn = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xReturn; + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + + BaseType_t xTaskPriorityDisinherit( TaskHandle_t const pxMutexHolder ) + { + TCB_t * const pxTCB = pxMutexHolder; + BaseType_t xReturn = pdFALSE; + + if( pxMutexHolder != NULL ) + { + /* A task can only have an inherited priority if it holds the mutex. + If the mutex is held by a task then it cannot be given from an + interrupt, and if a mutex is given by the holding task then it must + be the running state task. */ + configASSERT( pxTCB == pxCurrentTCB ); + configASSERT( pxTCB->uxMutexesHeld ); + ( pxTCB->uxMutexesHeld )--; + + /* Has the holder of the mutex inherited the priority of another + task? */ + if( pxTCB->uxPriority != pxTCB->uxBasePriority ) + { + /* Only disinherit if no other mutexes are held. */ + if( pxTCB->uxMutexesHeld == ( UBaseType_t ) 0 ) + { + /* A task can only have an inherited priority if it holds + the mutex. If the mutex is held by a task then it cannot be + given from an interrupt, and if a mutex is given by the + holding task then it must be the running state task. Remove + the holding task from the ready/delayed list. */ + if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + taskRESET_READY_PRIORITY( pxTCB->uxPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Disinherit the priority before adding the task into the + new ready list. */ + traceTASK_PRIORITY_DISINHERIT( pxTCB, pxTCB->uxBasePriority ); + pxTCB->uxPriority = pxTCB->uxBasePriority; + + /* Reset the event list item value. It cannot be in use for + any other purpose if this task is running, and it must be + running to give back the mutex. */ + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxTCB->uxPriority ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + prvAddTaskToReadyList( pxTCB ); + + /* Return true to indicate that a context switch is required. + This is only actually required in the corner case whereby + multiple mutexes were held and the mutexes were given back + in an order different to that in which they were taken. + If a context switch did not occur when the first mutex was + returned, even if a task was waiting on it, then a context + switch should occur when the last mutex is returned whether + a task is waiting on it or not. */ + xReturn = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xReturn; + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + + void vTaskPriorityDisinheritAfterTimeout( TaskHandle_t const pxMutexHolder, UBaseType_t uxHighestPriorityWaitingTask ) + { + TCB_t * const pxTCB = pxMutexHolder; + UBaseType_t uxPriorityUsedOnEntry, uxPriorityToUse; + const UBaseType_t uxOnlyOneMutexHeld = ( UBaseType_t ) 1; + + if( pxMutexHolder != NULL ) + { + /* If pxMutexHolder is not NULL then the holder must hold at least + one mutex. */ + configASSERT( pxTCB->uxMutexesHeld ); + + /* Determine the priority to which the priority of the task that + holds the mutex should be set. This will be the greater of the + holding task's base priority and the priority of the highest + priority task that is waiting to obtain the mutex. */ + if( pxTCB->uxBasePriority < uxHighestPriorityWaitingTask ) + { + uxPriorityToUse = uxHighestPriorityWaitingTask; + } + else + { + uxPriorityToUse = pxTCB->uxBasePriority; + } + + /* Does the priority need to change? */ + if( pxTCB->uxPriority != uxPriorityToUse ) + { + /* Only disinherit if no other mutexes are held. This is a + simplification in the priority inheritance implementation. If + the task that holds the mutex is also holding other mutexes then + the other mutexes may have caused the priority inheritance. */ + if( pxTCB->uxMutexesHeld == uxOnlyOneMutexHeld ) + { + /* If a task has timed out because it already holds the + mutex it was trying to obtain then it cannot of inherited + its own priority. */ + configASSERT( pxTCB != pxCurrentTCB ); + + /* Disinherit the priority, remembering the previous + priority to facilitate determining the subject task's + state. */ + traceTASK_PRIORITY_DISINHERIT( pxTCB, pxTCB->uxBasePriority ); + uxPriorityUsedOnEntry = pxTCB->uxPriority; + pxTCB->uxPriority = uxPriorityToUse; + + /* Only reset the event list item value if the value is not + being used for anything else. */ + if( ( listGET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ) ) & taskEVENT_LIST_ITEM_VALUE_IN_USE ) == 0UL ) + { + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) uxPriorityToUse ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* If the running task is not the task that holds the mutex + then the task that holds the mutex could be in either the + Ready, Blocked or Suspended states. Only remove the task + from its current state list if it is in the Ready state as + the task's priority is going to change and there is one + Ready list per priority. */ + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxPriorityUsedOnEntry ] ), &( pxTCB->xStateListItem ) ) != pdFALSE ) + { + if( uxListRemove( &( pxTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + /* It is known that the task is in its ready list so + there is no need to check again and the port level + reset macro can be called directly. */ + portRESET_READY_PRIORITY( pxTCB->uxPriority, uxTopReadyPriority ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + prvAddTaskToReadyList( pxTCB ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if ( portCRITICAL_NESTING_IN_TCB == 1 ) + + void vTaskEnterCritical( void ) + { + portDISABLE_INTERRUPTS(); + + if( xSchedulerRunning != pdFALSE ) + { + ( pxCurrentTCB->uxCriticalNesting )++; + + /* This is not the interrupt safe version of the enter critical + function so assert() if it is being called from an interrupt + context. Only API functions that end in "FromISR" can be used in an + interrupt. Only assert if the critical nesting count is 1 to + protect against recursive calls if the assert function also uses a + critical section. */ + if( pxCurrentTCB->uxCriticalNesting == 1 ) + { + portASSERT_IF_IN_ISR(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* portCRITICAL_NESTING_IN_TCB */ +/*-----------------------------------------------------------*/ + +#if ( portCRITICAL_NESTING_IN_TCB == 1 ) + + void vTaskExitCritical( void ) + { + if( xSchedulerRunning != pdFALSE ) + { + if( pxCurrentTCB->uxCriticalNesting > 0U ) + { + ( pxCurrentTCB->uxCriticalNesting )--; + + if( pxCurrentTCB->uxCriticalNesting == 0U ) + { + portENABLE_INTERRUPTS(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* portCRITICAL_NESTING_IN_TCB */ +/*-----------------------------------------------------------*/ + +#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) ) + + static char *prvWriteNameToBuffer( char *pcBuffer, const char *pcTaskName ) + { + size_t x; + + /* Start by copying the entire string. */ + strcpy( pcBuffer, pcTaskName ); + + /* Pad the end of the string with spaces to ensure columns line up when + printed out. */ + for( x = strlen( pcBuffer ); x < ( size_t ) ( configMAX_TASK_NAME_LEN - 1 ); x++ ) + { + pcBuffer[ x ] = ' '; + } + + /* Terminate. */ + pcBuffer[ x ] = ( char ) 0x00; + + /* Return the new end of string. */ + return &( pcBuffer[ x ] ); + } + +#endif /* ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) */ +/*-----------------------------------------------------------*/ + +#if ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + + void vTaskList( char * pcWriteBuffer ) + { + TaskStatus_t *pxTaskStatusArray; + UBaseType_t uxArraySize, x; + char cStatus; + + /* + * PLEASE NOTE: + * + * This function is provided for convenience only, and is used by many + * of the demo applications. Do not consider it to be part of the + * scheduler. + * + * vTaskList() calls uxTaskGetSystemState(), then formats part of the + * uxTaskGetSystemState() output into a human readable table that + * displays task names, states and stack usage. + * + * vTaskList() has a dependency on the sprintf() C library function that + * might bloat the code size, use a lot of stack, and provide different + * results on different platforms. An alternative, tiny, third party, + * and limited functionality implementation of sprintf() is provided in + * many of the FreeRTOS/Demo sub-directories in a file called + * printf-stdarg.c (note printf-stdarg.c does not provide a full + * snprintf() implementation!). + * + * It is recommended that production systems call uxTaskGetSystemState() + * directly to get access to raw stats data, rather than indirectly + * through a call to vTaskList(). + */ + + + /* Make sure the write buffer does not contain a string. */ + *pcWriteBuffer = ( char ) 0x00; + + /* Take a snapshot of the number of tasks in case it changes while this + function is executing. */ + uxArraySize = uxCurrentNumberOfTasks; + + /* Allocate an array index for each task. NOTE! if + configSUPPORT_DYNAMIC_ALLOCATION is set to 0 then pvPortMalloc() will + equate to NULL. */ + pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation allocates a struct that has the alignment requirements of a pointer. */ + + if( pxTaskStatusArray != NULL ) + { + /* Generate the (binary) data. */ + uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, NULL ); + + /* Create a human readable table from the binary data. */ + for( x = 0; x < uxArraySize; x++ ) + { + switch( pxTaskStatusArray[ x ].eCurrentState ) + { + case eRunning: cStatus = tskRUNNING_CHAR; + break; + + case eReady: cStatus = tskREADY_CHAR; + break; + + case eBlocked: cStatus = tskBLOCKED_CHAR; + break; + + case eSuspended: cStatus = tskSUSPENDED_CHAR; + break; + + case eDeleted: cStatus = tskDELETED_CHAR; + break; + + case eInvalid: /* Fall through. */ + default: /* Should not get here, but it is included + to prevent static checking errors. */ + cStatus = ( char ) 0x00; + break; + } + + /* Write the task name to the string, padding with spaces so it + can be printed in tabular form more easily. */ + pcWriteBuffer = prvWriteNameToBuffer( pcWriteBuffer, pxTaskStatusArray[ x ].pcTaskName ); + + /* Write the rest of the string. */ + sprintf( pcWriteBuffer, "\t%c\t%u\t%u\t%u\r\n", cStatus, ( unsigned int ) pxTaskStatusArray[ x ].uxCurrentPriority, ( unsigned int ) pxTaskStatusArray[ x ].usStackHighWaterMark, ( unsigned int ) pxTaskStatusArray[ x ].xTaskNumber ); /*lint !e586 sprintf() allowed as this is compiled with many compilers and this is a utility function only - not part of the core kernel implementation. */ + pcWriteBuffer += strlen( pcWriteBuffer ); /*lint !e9016 Pointer arithmetic ok on char pointers especially as in this case where it best denotes the intent of the code. */ + } + + /* Free the array again. NOTE! If configSUPPORT_DYNAMIC_ALLOCATION + is 0 then vPortFree() will be #defined to nothing. */ + vPortFree( pxTaskStatusArray ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* ( ( configUSE_TRACE_FACILITY == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) */ +/*----------------------------------------------------------*/ + +#if ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) ) + + void vTaskGetRunTimeStats( char *pcWriteBuffer ) + { + TaskStatus_t *pxTaskStatusArray; + UBaseType_t uxArraySize, x; + uint32_t ulTotalTime, ulStatsAsPercentage; + + #if( configUSE_TRACE_FACILITY != 1 ) + { + #error configUSE_TRACE_FACILITY must also be set to 1 in FreeRTOSConfig.h to use vTaskGetRunTimeStats(). + } + #endif + + /* + * PLEASE NOTE: + * + * This function is provided for convenience only, and is used by many + * of the demo applications. Do not consider it to be part of the + * scheduler. + * + * vTaskGetRunTimeStats() calls uxTaskGetSystemState(), then formats part + * of the uxTaskGetSystemState() output into a human readable table that + * displays the amount of time each task has spent in the Running state + * in both absolute and percentage terms. + * + * vTaskGetRunTimeStats() has a dependency on the sprintf() C library + * function that might bloat the code size, use a lot of stack, and + * provide different results on different platforms. An alternative, + * tiny, third party, and limited functionality implementation of + * sprintf() is provided in many of the FreeRTOS/Demo sub-directories in + * a file called printf-stdarg.c (note printf-stdarg.c does not provide + * a full snprintf() implementation!). + * + * It is recommended that production systems call uxTaskGetSystemState() + * directly to get access to raw stats data, rather than indirectly + * through a call to vTaskGetRunTimeStats(). + */ + + /* Make sure the write buffer does not contain a string. */ + *pcWriteBuffer = ( char ) 0x00; + + /* Take a snapshot of the number of tasks in case it changes while this + function is executing. */ + uxArraySize = uxCurrentNumberOfTasks; + + /* Allocate an array index for each task. NOTE! If + configSUPPORT_DYNAMIC_ALLOCATION is set to 0 then pvPortMalloc() will + equate to NULL. */ + pxTaskStatusArray = pvPortMalloc( uxCurrentNumberOfTasks * sizeof( TaskStatus_t ) ); /*lint !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack and this allocation allocates a struct that has the alignment requirements of a pointer. */ + + if( pxTaskStatusArray != NULL ) + { + /* Generate the (binary) data. */ + uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, &ulTotalTime ); + + /* For percentage calculations. */ + ulTotalTime /= 100UL; + + /* Avoid divide by zero errors. */ + if( ulTotalTime > 0UL ) + { + /* Create a human readable table from the binary data. */ + for( x = 0; x < uxArraySize; x++ ) + { + /* What percentage of the total run time has the task used? + This will always be rounded down to the nearest integer. + ulTotalRunTimeDiv100 has already been divided by 100. */ + ulStatsAsPercentage = pxTaskStatusArray[ x ].ulRunTimeCounter / ulTotalTime; + + /* Write the task name to the string, padding with + spaces so it can be printed in tabular form more + easily. */ + pcWriteBuffer = prvWriteNameToBuffer( pcWriteBuffer, pxTaskStatusArray[ x ].pcTaskName ); + + if( ulStatsAsPercentage > 0UL ) + { + #ifdef portLU_PRINTF_SPECIFIER_REQUIRED + { + sprintf( pcWriteBuffer, "\t%lu\t\t%lu%%\r\n", pxTaskStatusArray[ x ].ulRunTimeCounter, ulStatsAsPercentage ); + } + #else + { + /* sizeof( int ) == sizeof( long ) so a smaller + printf() library can be used. */ + sprintf( pcWriteBuffer, "\t%u\t\t%u%%\r\n", ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter, ( unsigned int ) ulStatsAsPercentage ); /*lint !e586 sprintf() allowed as this is compiled with many compilers and this is a utility function only - not part of the core kernel implementation. */ + } + #endif + } + else + { + /* If the percentage is zero here then the task has + consumed less than 1% of the total run time. */ + #ifdef portLU_PRINTF_SPECIFIER_REQUIRED + { + sprintf( pcWriteBuffer, "\t%lu\t\t<1%%\r\n", pxTaskStatusArray[ x ].ulRunTimeCounter ); + } + #else + { + /* sizeof( int ) == sizeof( long ) so a smaller + printf() library can be used. */ + sprintf( pcWriteBuffer, "\t%u\t\t<1%%\r\n", ( unsigned int ) pxTaskStatusArray[ x ].ulRunTimeCounter ); /*lint !e586 sprintf() allowed as this is compiled with many compilers and this is a utility function only - not part of the core kernel implementation. */ + } + #endif + } + + pcWriteBuffer += strlen( pcWriteBuffer ); /*lint !e9016 Pointer arithmetic ok on char pointers especially as in this case where it best denotes the intent of the code. */ + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + /* Free the array again. NOTE! If configSUPPORT_DYNAMIC_ALLOCATION + is 0 then vPortFree() will be #defined to nothing. */ + vPortFree( pxTaskStatusArray ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + +#endif /* ( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( configUSE_STATS_FORMATTING_FUNCTIONS > 0 ) && ( configSUPPORT_STATIC_ALLOCATION == 1 ) ) */ +/*-----------------------------------------------------------*/ + +TickType_t uxTaskResetEventItemValue( void ) +{ +TickType_t uxReturn; + + uxReturn = listGET_LIST_ITEM_VALUE( &( pxCurrentTCB->xEventListItem ) ); + + /* Reset the event list item to its normal value - so it can be used with + queues and semaphores. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xEventListItem ), ( ( TickType_t ) configMAX_PRIORITIES - ( TickType_t ) pxCurrentTCB->uxPriority ) ); /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + + return uxReturn; +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + + TaskHandle_t pvTaskIncrementMutexHeldCount( void ) + { + /* If xSemaphoreCreateMutex() is called before any tasks have been created + then pxCurrentTCB will be NULL. */ + if( pxCurrentTCB != NULL ) + { + ( pxCurrentTCB->uxMutexesHeld )++; + } + + return pxCurrentTCB; + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if( configUSE_TASK_NOTIFICATIONS == 1 ) + + uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait ) + { + uint32_t ulReturn; + + taskENTER_CRITICAL(); + { + /* Only block if the notification count is not already non-zero. */ + if( pxCurrentTCB->ulNotifiedValue == 0UL ) + { + /* Mark this task as waiting for a notification. */ + pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION; + + if( xTicksToWait > ( TickType_t ) 0 ) + { + prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); + traceTASK_NOTIFY_TAKE_BLOCK(); + + /* All ports are written to allow a yield in a critical + section (some will yield immediately, others wait until the + critical section exits) - but it is not something that + application code should ever do. */ + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + taskENTER_CRITICAL(); + { + traceTASK_NOTIFY_TAKE(); + ulReturn = pxCurrentTCB->ulNotifiedValue; + + if( ulReturn != 0UL ) + { + if( xClearCountOnExit != pdFALSE ) + { + pxCurrentTCB->ulNotifiedValue = 0UL; + } + else + { + pxCurrentTCB->ulNotifiedValue = ulReturn - ( uint32_t ) 1; + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION; + } + taskEXIT_CRITICAL(); + + return ulReturn; + } + +#endif /* configUSE_TASK_NOTIFICATIONS */ +/*-----------------------------------------------------------*/ + +#if( configUSE_TASK_NOTIFICATIONS == 1 ) + + BaseType_t xTaskNotifyWait( uint32_t ulBitsToClearOnEntry, uint32_t ulBitsToClearOnExit, uint32_t *pulNotificationValue, TickType_t xTicksToWait ) + { + BaseType_t xReturn; + + taskENTER_CRITICAL(); + { + /* Only block if a notification is not already pending. */ + if( pxCurrentTCB->ucNotifyState != taskNOTIFICATION_RECEIVED ) + { + /* Clear bits in the task's notification value as bits may get + set by the notifying task or interrupt. This can be used to + clear the value to zero. */ + pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnEntry; + + /* Mark this task as waiting for a notification. */ + pxCurrentTCB->ucNotifyState = taskWAITING_NOTIFICATION; + + if( xTicksToWait > ( TickType_t ) 0 ) + { + prvAddCurrentTaskToDelayedList( xTicksToWait, pdTRUE ); + traceTASK_NOTIFY_WAIT_BLOCK(); + + /* All ports are written to allow a yield in a critical + section (some will yield immediately, others wait until the + critical section exits) - but it is not something that + application code should ever do. */ + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + taskENTER_CRITICAL(); + { + traceTASK_NOTIFY_WAIT(); + + if( pulNotificationValue != NULL ) + { + /* Output the current notification value, which may or may not + have changed. */ + *pulNotificationValue = pxCurrentTCB->ulNotifiedValue; + } + + /* If ucNotifyValue is set then either the task never entered the + blocked state (because a notification was already pending) or the + task unblocked because of a notification. Otherwise the task + unblocked because of a timeout. */ + if( pxCurrentTCB->ucNotifyState != taskNOTIFICATION_RECEIVED ) + { + /* A notification was not received. */ + xReturn = pdFALSE; + } + else + { + /* A notification was already pending or a notification was + received while the task was waiting. */ + pxCurrentTCB->ulNotifiedValue &= ~ulBitsToClearOnExit; + xReturn = pdTRUE; + } + + pxCurrentTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION; + } + taskEXIT_CRITICAL(); + + return xReturn; + } + +#endif /* configUSE_TASK_NOTIFICATIONS */ +/*-----------------------------------------------------------*/ + +#if( configUSE_TASK_NOTIFICATIONS == 1 ) + + BaseType_t xTaskGenericNotify( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue ) + { + TCB_t * pxTCB; + BaseType_t xReturn = pdPASS; + uint8_t ucOriginalNotifyState; + + configASSERT( xTaskToNotify ); + pxTCB = xTaskToNotify; + + taskENTER_CRITICAL(); + { + if( pulPreviousNotificationValue != NULL ) + { + *pulPreviousNotificationValue = pxTCB->ulNotifiedValue; + } + + ucOriginalNotifyState = pxTCB->ucNotifyState; + + pxTCB->ucNotifyState = taskNOTIFICATION_RECEIVED; + + switch( eAction ) + { + case eSetBits : + pxTCB->ulNotifiedValue |= ulValue; + break; + + case eIncrement : + ( pxTCB->ulNotifiedValue )++; + break; + + case eSetValueWithOverwrite : + pxTCB->ulNotifiedValue = ulValue; + break; + + case eSetValueWithoutOverwrite : + if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED ) + { + pxTCB->ulNotifiedValue = ulValue; + } + else + { + /* The value could not be written to the task. */ + xReturn = pdFAIL; + } + break; + + case eNoAction: + /* The task is being notified without its notify value being + updated. */ + break; + + default: + /* Should not get here if all enums are handled. + Artificially force an assert by testing a value the + compiler can't assume is const. */ + configASSERT( pxTCB->ulNotifiedValue == ~0UL ); + + break; + } + + traceTASK_NOTIFY(); + + /* If the task is in the blocked state specifically to wait for a + notification then unblock it now. */ + if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) + { + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxTCB ); + + /* The task should not have been on an event list. */ + configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ); + + #if( configUSE_TICKLESS_IDLE != 0 ) + { + /* If a task is blocked waiting for a notification then + xNextTaskUnblockTime might be set to the blocked task's time + out time. If the task is unblocked for a reason other than + a timeout xNextTaskUnblockTime is normally left unchanged, + because it will automatically get reset to a new value when + the tick count equals xNextTaskUnblockTime. However if + tickless idling is used it might be more important to enter + sleep mode at the earliest possible time - so reset + xNextTaskUnblockTime here to ensure it is updated at the + earliest possible time. */ + prvResetNextTaskUnblockTime(); + } + #endif + + if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* The notified task has a priority above the currently + executing task so a yield is required. */ + taskYIELD_IF_USING_PREEMPTION(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); + + return xReturn; + } + +#endif /* configUSE_TASK_NOTIFICATIONS */ +/*-----------------------------------------------------------*/ + +#if( configUSE_TASK_NOTIFICATIONS == 1 ) + + BaseType_t xTaskGenericNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, uint32_t *pulPreviousNotificationValue, BaseType_t *pxHigherPriorityTaskWoken ) + { + TCB_t * pxTCB; + uint8_t ucOriginalNotifyState; + BaseType_t xReturn = pdPASS; + UBaseType_t uxSavedInterruptStatus; + + configASSERT( xTaskToNotify ); + + /* RTOS ports that support interrupt nesting have the concept of a + maximum system call (or maximum API call) interrupt priority. + Interrupts that are above the maximum system call priority are keep + permanently enabled, even when the RTOS kernel is in a critical section, + but cannot make any calls to FreeRTOS API functions. If configASSERT() + is defined in FreeRTOSConfig.h then + portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + failure if a FreeRTOS API function is called from an interrupt that has + been assigned a priority above the configured maximum system call + priority. Only FreeRTOS functions that end in FromISR can be called + from interrupts that have been assigned a priority at or (logically) + below the maximum system call interrupt priority. FreeRTOS maintains a + separate interrupt safe API to ensure interrupt entry is as fast and as + simple as possible. More information (albeit Cortex-M specific) is + provided on the following link: + http://www.freertos.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + pxTCB = xTaskToNotify; + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + if( pulPreviousNotificationValue != NULL ) + { + *pulPreviousNotificationValue = pxTCB->ulNotifiedValue; + } + + ucOriginalNotifyState = pxTCB->ucNotifyState; + pxTCB->ucNotifyState = taskNOTIFICATION_RECEIVED; + + switch( eAction ) + { + case eSetBits : + pxTCB->ulNotifiedValue |= ulValue; + break; + + case eIncrement : + ( pxTCB->ulNotifiedValue )++; + break; + + case eSetValueWithOverwrite : + pxTCB->ulNotifiedValue = ulValue; + break; + + case eSetValueWithoutOverwrite : + if( ucOriginalNotifyState != taskNOTIFICATION_RECEIVED ) + { + pxTCB->ulNotifiedValue = ulValue; + } + else + { + /* The value could not be written to the task. */ + xReturn = pdFAIL; + } + break; + + case eNoAction : + /* The task is being notified without its notify value being + updated. */ + break; + + default: + /* Should not get here if all enums are handled. + Artificially force an assert by testing a value the + compiler can't assume is const. */ + configASSERT( pxTCB->ulNotifiedValue == ~0UL ); + break; + } + + traceTASK_NOTIFY_FROM_ISR(); + + /* If the task is in the blocked state specifically to wait for a + notification then unblock it now. */ + if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) + { + /* The task should not have been on an event list. */ + configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ); + + if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) + { + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxTCB ); + } + else + { + /* The delayed and ready lists cannot be accessed, so hold + this task pending until the scheduler is resumed. */ + vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); + } + + if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* The notified task has a priority above the currently + executing task so a yield is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + + /* Mark that a yield is pending in case the user is not + using the "xHigherPriorityTaskWoken" parameter to an ISR + safe FreeRTOS function. */ + xYieldPending = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; + } + +#endif /* configUSE_TASK_NOTIFICATIONS */ +/*-----------------------------------------------------------*/ + +#if( configUSE_TASK_NOTIFICATIONS == 1 ) + + void vTaskNotifyGiveFromISR( TaskHandle_t xTaskToNotify, BaseType_t *pxHigherPriorityTaskWoken ) + { + TCB_t * pxTCB; + uint8_t ucOriginalNotifyState; + UBaseType_t uxSavedInterruptStatus; + + configASSERT( xTaskToNotify ); + + /* RTOS ports that support interrupt nesting have the concept of a + maximum system call (or maximum API call) interrupt priority. + Interrupts that are above the maximum system call priority are keep + permanently enabled, even when the RTOS kernel is in a critical section, + but cannot make any calls to FreeRTOS API functions. If configASSERT() + is defined in FreeRTOSConfig.h then + portASSERT_IF_INTERRUPT_PRIORITY_INVALID() will result in an assertion + failure if a FreeRTOS API function is called from an interrupt that has + been assigned a priority above the configured maximum system call + priority. Only FreeRTOS functions that end in FromISR can be called + from interrupts that have been assigned a priority at or (logically) + below the maximum system call interrupt priority. FreeRTOS maintains a + separate interrupt safe API to ensure interrupt entry is as fast and as + simple as possible. More information (albeit Cortex-M specific) is + provided on the following link: + http://www.freertos.org/RTOS-Cortex-M3-M4.html */ + portASSERT_IF_INTERRUPT_PRIORITY_INVALID(); + + pxTCB = xTaskToNotify; + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + ucOriginalNotifyState = pxTCB->ucNotifyState; + pxTCB->ucNotifyState = taskNOTIFICATION_RECEIVED; + + /* 'Giving' is equivalent to incrementing a count in a counting + semaphore. */ + ( pxTCB->ulNotifiedValue )++; + + traceTASK_NOTIFY_GIVE_FROM_ISR(); + + /* If the task is in the blocked state specifically to wait for a + notification then unblock it now. */ + if( ucOriginalNotifyState == taskWAITING_NOTIFICATION ) + { + /* The task should not have been on an event list. */ + configASSERT( listLIST_ITEM_CONTAINER( &( pxTCB->xEventListItem ) ) == NULL ); + + if( uxSchedulerSuspended == ( UBaseType_t ) pdFALSE ) + { + ( void ) uxListRemove( &( pxTCB->xStateListItem ) ); + prvAddTaskToReadyList( pxTCB ); + } + else + { + /* The delayed and ready lists cannot be accessed, so hold + this task pending until the scheduler is resumed. */ + vListInsertEnd( &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); + } + + if( pxTCB->uxPriority > pxCurrentTCB->uxPriority ) + { + /* The notified task has a priority above the currently + executing task so a yield is required. */ + if( pxHigherPriorityTaskWoken != NULL ) + { + *pxHigherPriorityTaskWoken = pdTRUE; + } + + /* Mark that a yield is pending in case the user is not + using the "xHigherPriorityTaskWoken" parameter in an ISR + safe FreeRTOS function. */ + xYieldPending = pdTRUE; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + } + +#endif /* configUSE_TASK_NOTIFICATIONS */ +/*-----------------------------------------------------------*/ + +#if( configUSE_TASK_NOTIFICATIONS == 1 ) + + BaseType_t xTaskNotifyStateClear( TaskHandle_t xTask ) + { + TCB_t *pxTCB; + BaseType_t xReturn; + + /* If null is passed in here then it is the calling task that is having + its notification state cleared. */ + pxTCB = prvGetTCBFromHandle( xTask ); + + taskENTER_CRITICAL(); + { + if( pxTCB->ucNotifyState == taskNOTIFICATION_RECEIVED ) + { + pxTCB->ucNotifyState = taskNOT_WAITING_NOTIFICATION; + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + } + } + taskEXIT_CRITICAL(); + + return xReturn; + } + +#endif /* configUSE_TASK_NOTIFICATIONS */ +/*-----------------------------------------------------------*/ + +#if( configUSE_TASK_NOTIFICATIONS == 1 ) + + uint32_t ulTaskNotifyValueClear( TaskHandle_t xTask, uint32_t ulBitsToClear ) + { + TCB_t *pxTCB; + uint32_t ulReturn; + + /* If null is passed in here then it is the calling task that is having + its notification state cleared. */ + pxTCB = prvGetTCBFromHandle( xTask ); + + taskENTER_CRITICAL(); + { + /* Return the notification as it was before the bits were cleared, + then clear the bit mask. */ + ulReturn = pxCurrentTCB->ulNotifiedValue; + pxTCB->ulNotifiedValue &= ~ulBitsToClear; + } + taskEXIT_CRITICAL(); + + return ulReturn; + } + +#endif /* configUSE_TASK_NOTIFICATIONS */ +/*-----------------------------------------------------------*/ + +#if( ( configGENERATE_RUN_TIME_STATS == 1 ) && ( INCLUDE_xTaskGetIdleTaskHandle == 1 ) ) + + uint32_t ulTaskGetIdleRunTimeCounter( void ) + { + return xIdleTaskHandle->ulRunTimeCounter; + } + +#endif +/*-----------------------------------------------------------*/ + +static void prvAddCurrentTaskToDelayedList( TickType_t xTicksToWait, const BaseType_t xCanBlockIndefinitely ) +{ +TickType_t xTimeToWake; +const TickType_t xConstTickCount = xTickCount; + + #if( INCLUDE_xTaskAbortDelay == 1 ) + { + /* About to enter a delayed list, so ensure the ucDelayAborted flag is + reset to pdFALSE so it can be detected as having been set to pdTRUE + when the task leaves the Blocked state. */ + pxCurrentTCB->ucDelayAborted = pdFALSE; + } + #endif + + /* Remove the task from the ready list before adding it to the blocked list + as the same list item is used for both lists. */ + if( uxListRemove( &( pxCurrentTCB->xStateListItem ) ) == ( UBaseType_t ) 0 ) + { + /* The current task must be in a ready list, so there is no need to + check, and the port reset macro can be called directly. */ + portRESET_READY_PRIORITY( pxCurrentTCB->uxPriority, uxTopReadyPriority ); /*lint !e931 pxCurrentTCB cannot change as it is the calling task. pxCurrentTCB->uxPriority and uxTopReadyPriority cannot change as called with scheduler suspended or in a critical section. */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + if( ( xTicksToWait == portMAX_DELAY ) && ( xCanBlockIndefinitely != pdFALSE ) ) + { + /* Add the task to the suspended task list instead of a delayed task + list to ensure it is not woken by a timing event. It will block + indefinitely. */ + vListInsertEnd( &xSuspendedTaskList, &( pxCurrentTCB->xStateListItem ) ); + } + else + { + /* Calculate the time at which the task should be woken if the event + does not occur. This may overflow but this doesn't matter, the + kernel will manage it correctly. */ + xTimeToWake = xConstTickCount + xTicksToWait; + + /* The list item will be inserted in wake time order. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake ); + + if( xTimeToWake < xConstTickCount ) + { + /* Wake time has overflowed. Place this item in the overflow + list. */ + vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) ); + } + else + { + /* The wake time has not overflowed, so the current block list + is used. */ + vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) ); + + /* If the task entering the blocked state was placed at the + head of the list of blocked tasks then xNextTaskUnblockTime + needs to be updated too. */ + if( xTimeToWake < xNextTaskUnblockTime ) + { + xNextTaskUnblockTime = xTimeToWake; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + } + #else /* INCLUDE_vTaskSuspend */ + { + /* Calculate the time at which the task should be woken if the event + does not occur. This may overflow but this doesn't matter, the kernel + will manage it correctly. */ + xTimeToWake = xConstTickCount + xTicksToWait; + + /* The list item will be inserted in wake time order. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xStateListItem ), xTimeToWake ); + + if( xTimeToWake < xConstTickCount ) + { + /* Wake time has overflowed. Place this item in the overflow list. */ + vListInsert( pxOverflowDelayedTaskList, &( pxCurrentTCB->xStateListItem ) ); + } + else + { + /* The wake time has not overflowed, so the current block list is used. */ + vListInsert( pxDelayedTaskList, &( pxCurrentTCB->xStateListItem ) ); + + /* If the task entering the blocked state was placed at the head of the + list of blocked tasks then xNextTaskUnblockTime needs to be updated + too. */ + if( xTimeToWake < xNextTaskUnblockTime ) + { + xNextTaskUnblockTime = xTimeToWake; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + /* Avoid compiler warning when INCLUDE_vTaskSuspend is not 1. */ + ( void ) xCanBlockIndefinitely; + } + #endif /* INCLUDE_vTaskSuspend */ +} + +/* Code below here allows additional code to be inserted into this source file, +especially where access to file scope functions and data is needed (for example +when performing module tests). */ + +#ifdef FREERTOS_MODULE_TEST + #include "tasks_test_access_functions.h" +#endif + + +#if( configINCLUDE_FREERTOS_TASK_C_ADDITIONS_H == 1 ) + + #include "freertos_tasks_c_additions.h" + + #ifdef FREERTOS_TASKS_C_ADDITIONS_INIT + static void freertos_tasks_c_additions_init( void ) + { + FREERTOS_TASKS_C_ADDITIONS_INIT(); + } + #endif + +#endif + + diff --git a/product/rcar/src/CMSIS-FreeRTOS/Source/timers.c b/product/rcar/src/CMSIS-FreeRTOS/Source/timers.c new file mode 100644 index 000000000..00200b8f6 --- /dev/null +++ b/product/rcar/src/CMSIS-FreeRTOS/Source/timers.c @@ -0,0 +1,1127 @@ +/* + * FreeRTOS Kernel V10.3.1 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +/* Standard includes. */ +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "timers.h" + +#if ( INCLUDE_xTimerPendFunctionCall == 1 ) && ( configUSE_TIMERS == 0 ) + #error configUSE_TIMERS must be set to 1 to make the xTimerPendFunctionCall() function available. +#endif + +/* Lint e9021, e961 and e750 are suppressed as a MISRA exception justified +because the MPU ports require MPU_WRAPPERS_INCLUDED_FROM_API_FILE to be defined +for the header files above, but not in this file, in order to generate the +correct privileged Vs unprivileged linkage and placement. */ +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE /*lint !e9021 !e961 !e750. */ + + +/* This entire source file will be skipped if the application is not configured +to include software timer functionality. This #if is closed at the very bottom +of this file. If you want to include software timer functionality then ensure +configUSE_TIMERS is set to 1 in FreeRTOSConfig.h. */ +#if ( configUSE_TIMERS == 1 ) + +/* Misc definitions. */ +#define tmrNO_DELAY ( TickType_t ) 0U + +/* The name assigned to the timer service task. This can be overridden by +defining trmTIMER_SERVICE_TASK_NAME in FreeRTOSConfig.h. */ +#ifndef configTIMER_SERVICE_TASK_NAME + #define configTIMER_SERVICE_TASK_NAME "Tmr Svc" +#endif + +/* Bit definitions used in the ucStatus member of a timer structure. */ +#define tmrSTATUS_IS_ACTIVE ( ( uint8_t ) 0x01 ) +#define tmrSTATUS_IS_STATICALLY_ALLOCATED ( ( uint8_t ) 0x02 ) +#define tmrSTATUS_IS_AUTORELOAD ( ( uint8_t ) 0x04 ) + +/* The definition of the timers themselves. */ +typedef struct tmrTimerControl /* The old naming convention is used to prevent breaking kernel aware debuggers. */ +{ + const char *pcTimerName; /*<< Text name. This is not used by the kernel, it is included simply to make debugging easier. */ /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + ListItem_t xTimerListItem; /*<< Standard linked list item as used by all kernel features for event management. */ + TickType_t xTimerPeriodInTicks;/*<< How quickly and often the timer expires. */ + void *pvTimerID; /*<< An ID to identify the timer. This allows the timer to be identified when the same callback is used for multiple timers. */ + TimerCallbackFunction_t pxCallbackFunction; /*<< The function that will be called when the timer expires. */ + #if( configUSE_TRACE_FACILITY == 1 ) + UBaseType_t uxTimerNumber; /*<< An ID assigned by trace tools such as FreeRTOS+Trace */ + #endif + uint8_t ucStatus; /*<< Holds bits to say if the timer was statically allocated or not, and if it is active or not. */ +} xTIMER; + +/* The old xTIMER name is maintained above then typedefed to the new Timer_t +name below to enable the use of older kernel aware debuggers. */ +typedef xTIMER Timer_t; + +/* The definition of messages that can be sent and received on the timer queue. +Two types of message can be queued - messages that manipulate a software timer, +and messages that request the execution of a non-timer related callback. The +two message types are defined in two separate structures, xTimerParametersType +and xCallbackParametersType respectively. */ +typedef struct tmrTimerParameters +{ + TickType_t xMessageValue; /*<< An optional value used by a subset of commands, for example, when changing the period of a timer. */ + Timer_t * pxTimer; /*<< The timer to which the command will be applied. */ +} TimerParameter_t; + + +typedef struct tmrCallbackParameters +{ + PendedFunction_t pxCallbackFunction; /* << The callback function to execute. */ + void *pvParameter1; /* << The value that will be used as the callback functions first parameter. */ + uint32_t ulParameter2; /* << The value that will be used as the callback functions second parameter. */ +} CallbackParameters_t; + +/* The structure that contains the two message types, along with an identifier +that is used to determine which message type is valid. */ +typedef struct tmrTimerQueueMessage +{ + BaseType_t xMessageID; /*<< The command being sent to the timer service task. */ + union + { + TimerParameter_t xTimerParameters; + + /* Don't include xCallbackParameters if it is not going to be used as + it makes the structure (and therefore the timer queue) larger. */ + #if ( INCLUDE_xTimerPendFunctionCall == 1 ) + CallbackParameters_t xCallbackParameters; + #endif /* INCLUDE_xTimerPendFunctionCall */ + } u; +} DaemonTaskMessage_t; + +/*lint -save -e956 A manual analysis and inspection has been used to determine +which static variables must be declared volatile. */ + +/* The list in which active timers are stored. Timers are referenced in expire +time order, with the nearest expiry time at the front of the list. Only the +timer service task is allowed to access these lists. +xActiveTimerList1 and xActiveTimerList2 could be at function scope but that +breaks some kernel aware debuggers, and debuggers that reply on removing the +static qualifier. */ +PRIVILEGED_DATA static List_t xActiveTimerList1; +PRIVILEGED_DATA static List_t xActiveTimerList2; +PRIVILEGED_DATA static List_t *pxCurrentTimerList; +PRIVILEGED_DATA static List_t *pxOverflowTimerList; + +/* A queue that is used to send commands to the timer service task. */ +PRIVILEGED_DATA static QueueHandle_t xTimerQueue = NULL; +PRIVILEGED_DATA static TaskHandle_t xTimerTaskHandle = NULL; + +/*lint -restore */ + +/*-----------------------------------------------------------*/ + +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + + /* If static allocation is supported then the application must provide the + following callback function - which enables the application to optionally + provide the memory that will be used by the timer task as the task's stack + and TCB. */ + extern void vApplicationGetTimerTaskMemory( StaticTask_t **ppxTimerTaskTCBBuffer, StackType_t **ppxTimerTaskStackBuffer, uint32_t *pulTimerTaskStackSize ); + +#endif + +/* + * Initialise the infrastructure used by the timer service task if it has not + * been initialised already. + */ +static void prvCheckForValidListAndQueue( void ) PRIVILEGED_FUNCTION; + +/* + * The timer service task (daemon). Timer functionality is controlled by this + * task. Other tasks communicate with the timer service task using the + * xTimerQueue queue. + */ +static portTASK_FUNCTION_PROTO( prvTimerTask, pvParameters ) PRIVILEGED_FUNCTION; + +/* + * Called by the timer service task to interpret and process a command it + * received on the timer queue. + */ +static void prvProcessReceivedCommands( void ) PRIVILEGED_FUNCTION; + +/* + * Insert the timer into either xActiveTimerList1, or xActiveTimerList2, + * depending on if the expire time causes a timer counter overflow. + */ +static BaseType_t prvInsertTimerInActiveList( Timer_t * const pxTimer, const TickType_t xNextExpiryTime, const TickType_t xTimeNow, const TickType_t xCommandTime ) PRIVILEGED_FUNCTION; + +/* + * An active timer has reached its expire time. Reload the timer if it is an + * auto-reload timer, then call its callback. + */ +static void prvProcessExpiredTimer( const TickType_t xNextExpireTime, const TickType_t xTimeNow ) PRIVILEGED_FUNCTION; + +/* + * The tick count has overflowed. Switch the timer lists after ensuring the + * current timer list does not still reference some timers. + */ +static void prvSwitchTimerLists( void ) PRIVILEGED_FUNCTION; + +/* + * Obtain the current tick count, setting *pxTimerListsWereSwitched to pdTRUE + * if a tick count overflow occurred since prvSampleTimeNow() was last called. + */ +static TickType_t prvSampleTimeNow( BaseType_t * const pxTimerListsWereSwitched ) PRIVILEGED_FUNCTION; + +/* + * If the timer list contains any active timers then return the expire time of + * the timer that will expire first and set *pxListWasEmpty to false. If the + * timer list does not contain any timers then return 0 and set *pxListWasEmpty + * to pdTRUE. + */ +static TickType_t prvGetNextExpireTime( BaseType_t * const pxListWasEmpty ) PRIVILEGED_FUNCTION; + +/* + * If a timer has expired, process it. Otherwise, block the timer service task + * until either a timer does expire or a command is received. + */ +static void prvProcessTimerOrBlockTask( const TickType_t xNextExpireTime, BaseType_t xListWasEmpty ) PRIVILEGED_FUNCTION; + +/* + * Called after a Timer_t structure has been allocated either statically or + * dynamically to fill in the structure's members. + */ +static void prvInitialiseNewTimer( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const TickType_t xTimerPeriodInTicks, + const UBaseType_t uxAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction, + Timer_t *pxNewTimer ) PRIVILEGED_FUNCTION; +/*-----------------------------------------------------------*/ + +BaseType_t xTimerCreateTimerTask( void ) +{ +BaseType_t xReturn = pdFAIL; + + /* This function is called when the scheduler is started if + configUSE_TIMERS is set to 1. Check that the infrastructure used by the + timer service task has been created/initialised. If timers have already + been created then the initialisation will already have been performed. */ + prvCheckForValidListAndQueue(); + + if( xTimerQueue != NULL ) + { + #if( configSUPPORT_STATIC_ALLOCATION == 1 ) + { + StaticTask_t *pxTimerTaskTCBBuffer = NULL; + StackType_t *pxTimerTaskStackBuffer = NULL; + uint32_t ulTimerTaskStackSize; + + vApplicationGetTimerTaskMemory( &pxTimerTaskTCBBuffer, &pxTimerTaskStackBuffer, &ulTimerTaskStackSize ); + xTimerTaskHandle = xTaskCreateStatic( prvTimerTask, + configTIMER_SERVICE_TASK_NAME, + ulTimerTaskStackSize, + NULL, + ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT, + pxTimerTaskStackBuffer, + pxTimerTaskTCBBuffer ); + + if( xTimerTaskHandle != NULL ) + { + xReturn = pdPASS; + } + } + #else + { + xReturn = xTaskCreate( prvTimerTask, + configTIMER_SERVICE_TASK_NAME, + configTIMER_TASK_STACK_DEPTH, + NULL, + ( ( UBaseType_t ) configTIMER_TASK_PRIORITY ) | portPRIVILEGE_BIT, + &xTimerTaskHandle ); + } + #endif /* configSUPPORT_STATIC_ALLOCATION */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + configASSERT( xReturn ); + return xReturn; +} +/*-----------------------------------------------------------*/ + +#if( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + + TimerHandle_t xTimerCreate( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const TickType_t xTimerPeriodInTicks, + const UBaseType_t uxAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction ) + { + Timer_t *pxNewTimer; + + pxNewTimer = ( Timer_t * ) pvPortMalloc( sizeof( Timer_t ) ); /*lint !e9087 !e9079 All values returned by pvPortMalloc() have at least the alignment required by the MCU's stack, and the first member of Timer_t is always a pointer to the timer's mame. */ + + if( pxNewTimer != NULL ) + { + /* Status is thus far zero as the timer is not created statically + and has not been started. The auto-reload bit may get set in + prvInitialiseNewTimer. */ + pxNewTimer->ucStatus = 0x00; + prvInitialiseNewTimer( pcTimerName, xTimerPeriodInTicks, uxAutoReload, pvTimerID, pxCallbackFunction, pxNewTimer ); + } + + return pxNewTimer; + } + +#endif /* configSUPPORT_DYNAMIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +#if( configSUPPORT_STATIC_ALLOCATION == 1 ) + + TimerHandle_t xTimerCreateStatic( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const TickType_t xTimerPeriodInTicks, + const UBaseType_t uxAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction, + StaticTimer_t *pxTimerBuffer ) + { + Timer_t *pxNewTimer; + + #if( configASSERT_DEFINED == 1 ) + { + /* Sanity check that the size of the structure used to declare a + variable of type StaticTimer_t equals the size of the real timer + structure. */ + volatile size_t xSize = sizeof( StaticTimer_t ); + configASSERT( xSize == sizeof( Timer_t ) ); + ( void ) xSize; /* Keeps lint quiet when configASSERT() is not defined. */ + } + #endif /* configASSERT_DEFINED */ + + /* A pointer to a StaticTimer_t structure MUST be provided, use it. */ + configASSERT( pxTimerBuffer ); + pxNewTimer = ( Timer_t * ) pxTimerBuffer; /*lint !e740 !e9087 StaticTimer_t is a pointer to a Timer_t, so guaranteed to be aligned and sized correctly (checked by an assert()), so this is safe. */ + + if( pxNewTimer != NULL ) + { + /* Timers can be created statically or dynamically so note this + timer was created statically in case it is later deleted. The + auto-reload bit may get set in prvInitialiseNewTimer(). */ + pxNewTimer->ucStatus = tmrSTATUS_IS_STATICALLY_ALLOCATED; + + prvInitialiseNewTimer( pcTimerName, xTimerPeriodInTicks, uxAutoReload, pvTimerID, pxCallbackFunction, pxNewTimer ); + } + + return pxNewTimer; + } + +#endif /* configSUPPORT_STATIC_ALLOCATION */ +/*-----------------------------------------------------------*/ + +static void prvInitialiseNewTimer( const char * const pcTimerName, /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ + const TickType_t xTimerPeriodInTicks, + const UBaseType_t uxAutoReload, + void * const pvTimerID, + TimerCallbackFunction_t pxCallbackFunction, + Timer_t *pxNewTimer ) +{ + /* 0 is not a valid value for xTimerPeriodInTicks. */ + configASSERT( ( xTimerPeriodInTicks > 0 ) ); + + if( pxNewTimer != NULL ) + { + /* Ensure the infrastructure used by the timer service task has been + created/initialised. */ + prvCheckForValidListAndQueue(); + + /* Initialise the timer structure members using the function + parameters. */ + pxNewTimer->pcTimerName = pcTimerName; + pxNewTimer->xTimerPeriodInTicks = xTimerPeriodInTicks; + pxNewTimer->pvTimerID = pvTimerID; + pxNewTimer->pxCallbackFunction = pxCallbackFunction; + vListInitialiseItem( &( pxNewTimer->xTimerListItem ) ); + if( uxAutoReload != pdFALSE ) + { + pxNewTimer->ucStatus |= tmrSTATUS_IS_AUTORELOAD; + } + traceTIMER_CREATE( pxNewTimer ); + } +} +/*-----------------------------------------------------------*/ + +BaseType_t xTimerGenericCommand( TimerHandle_t xTimer, const BaseType_t xCommandID, const TickType_t xOptionalValue, BaseType_t * const pxHigherPriorityTaskWoken, const TickType_t xTicksToWait ) +{ +BaseType_t xReturn = pdFAIL; +DaemonTaskMessage_t xMessage; + + configASSERT( xTimer ); + + /* Send a message to the timer service task to perform a particular action + on a particular timer definition. */ + if( xTimerQueue != NULL ) + { + /* Send a command to the timer service task to start the xTimer timer. */ + xMessage.xMessageID = xCommandID; + xMessage.u.xTimerParameters.xMessageValue = xOptionalValue; + xMessage.u.xTimerParameters.pxTimer = xTimer; + + if( xCommandID < tmrFIRST_FROM_ISR_COMMAND ) + { + if( xTaskGetSchedulerState() == taskSCHEDULER_RUNNING ) + { + xReturn = xQueueSendToBack( xTimerQueue, &xMessage, xTicksToWait ); + } + else + { + xReturn = xQueueSendToBack( xTimerQueue, &xMessage, tmrNO_DELAY ); + } + } + else + { + xReturn = xQueueSendToBackFromISR( xTimerQueue, &xMessage, pxHigherPriorityTaskWoken ); + } + + traceTIMER_COMMAND_SEND( xTimer, xCommandID, xOptionalValue, xReturn ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +TaskHandle_t xTimerGetTimerDaemonTaskHandle( void ) +{ + /* If xTimerGetTimerDaemonTaskHandle() is called before the scheduler has been + started, then xTimerTaskHandle will be NULL. */ + configASSERT( ( xTimerTaskHandle != NULL ) ); + return xTimerTaskHandle; +} +/*-----------------------------------------------------------*/ + +TickType_t xTimerGetPeriod( TimerHandle_t xTimer ) +{ +Timer_t *pxTimer = xTimer; + + configASSERT( xTimer ); + return pxTimer->xTimerPeriodInTicks; +} +/*-----------------------------------------------------------*/ + +void vTimerSetReloadMode( TimerHandle_t xTimer, const UBaseType_t uxAutoReload ) +{ +Timer_t * pxTimer = xTimer; + + configASSERT( xTimer ); + taskENTER_CRITICAL(); + { + if( uxAutoReload != pdFALSE ) + { + pxTimer->ucStatus |= tmrSTATUS_IS_AUTORELOAD; + } + else + { + pxTimer->ucStatus &= ~tmrSTATUS_IS_AUTORELOAD; + } + } + taskEXIT_CRITICAL(); +} +/*-----------------------------------------------------------*/ + +UBaseType_t uxTimerGetReloadMode( TimerHandle_t xTimer ) +{ +Timer_t * pxTimer = xTimer; +UBaseType_t uxReturn; + + configASSERT( xTimer ); + taskENTER_CRITICAL(); + { + if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) == 0 ) + { + /* Not an auto-reload timer. */ + uxReturn = ( UBaseType_t ) pdFALSE; + } + else + { + /* Is an auto-reload timer. */ + uxReturn = ( UBaseType_t ) pdTRUE; + } + } + taskEXIT_CRITICAL(); + + return uxReturn; +} +/*-----------------------------------------------------------*/ + +TickType_t xTimerGetExpiryTime( TimerHandle_t xTimer ) +{ +Timer_t * pxTimer = xTimer; +TickType_t xReturn; + + configASSERT( xTimer ); + xReturn = listGET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ) ); + return xReturn; +} +/*-----------------------------------------------------------*/ + +const char * pcTimerGetName( TimerHandle_t xTimer ) /*lint !e971 Unqualified char types are allowed for strings and single characters only. */ +{ +Timer_t *pxTimer = xTimer; + + configASSERT( xTimer ); + return pxTimer->pcTimerName; +} +/*-----------------------------------------------------------*/ + +static void prvProcessExpiredTimer( const TickType_t xNextExpireTime, const TickType_t xTimeNow ) +{ +BaseType_t xResult; +Timer_t * const pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); /*lint !e9087 !e9079 void * is used as this macro is used with tasks and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + + /* Remove the timer from the list of active timers. A check has already + been performed to ensure the list is not empty. */ + ( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); + traceTIMER_EXPIRED( pxTimer ); + + /* If the timer is an auto-reload timer then calculate the next + expiry time and re-insert the timer in the list of active timers. */ + if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0 ) + { + /* The timer is inserted into a list using a time relative to anything + other than the current time. It will therefore be inserted into the + correct list relative to the time this task thinks it is now. */ + if( prvInsertTimerInActiveList( pxTimer, ( xNextExpireTime + pxTimer->xTimerPeriodInTicks ), xTimeNow, xNextExpireTime ) != pdFALSE ) + { + /* The timer expired before it was added to the active timer + list. Reload it now. */ + xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xNextExpireTime, NULL, tmrNO_DELAY ); + configASSERT( xResult ); + ( void ) xResult; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + pxTimer->ucStatus &= ~tmrSTATUS_IS_ACTIVE; + mtCOVERAGE_TEST_MARKER(); + } + + /* Call the timer callback. */ + pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer ); +} +/*-----------------------------------------------------------*/ + +static portTASK_FUNCTION( prvTimerTask, pvParameters ) +{ +TickType_t xNextExpireTime; +BaseType_t xListWasEmpty; + + /* Just to avoid compiler warnings. */ + ( void ) pvParameters; + + #if( configUSE_DAEMON_TASK_STARTUP_HOOK == 1 ) + { + extern void vApplicationDaemonTaskStartupHook( void ); + + /* Allow the application writer to execute some code in the context of + this task at the point the task starts executing. This is useful if the + application includes initialisation code that would benefit from + executing after the scheduler has been started. */ + vApplicationDaemonTaskStartupHook(); + } + #endif /* configUSE_DAEMON_TASK_STARTUP_HOOK */ + + for( ;; ) + { + /* Query the timers list to see if it contains any timers, and if so, + obtain the time at which the next timer will expire. */ + xNextExpireTime = prvGetNextExpireTime( &xListWasEmpty ); + + /* If a timer has expired, process it. Otherwise, block this task + until either a timer does expire, or a command is received. */ + prvProcessTimerOrBlockTask( xNextExpireTime, xListWasEmpty ); + + /* Empty the command queue. */ + prvProcessReceivedCommands(); + } +} +/*-----------------------------------------------------------*/ + +static void prvProcessTimerOrBlockTask( const TickType_t xNextExpireTime, BaseType_t xListWasEmpty ) +{ +TickType_t xTimeNow; +BaseType_t xTimerListsWereSwitched; + + vTaskSuspendAll(); + { + /* Obtain the time now to make an assessment as to whether the timer + has expired or not. If obtaining the time causes the lists to switch + then don't process this timer as any timers that remained in the list + when the lists were switched will have been processed within the + prvSampleTimeNow() function. */ + xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched ); + if( xTimerListsWereSwitched == pdFALSE ) + { + /* The tick count has not overflowed, has the timer expired? */ + if( ( xListWasEmpty == pdFALSE ) && ( xNextExpireTime <= xTimeNow ) ) + { + ( void ) xTaskResumeAll(); + prvProcessExpiredTimer( xNextExpireTime, xTimeNow ); + } + else + { + /* The tick count has not overflowed, and the next expire + time has not been reached yet. This task should therefore + block to wait for the next expire time or a command to be + received - whichever comes first. The following line cannot + be reached unless xNextExpireTime > xTimeNow, except in the + case when the current timer list is empty. */ + if( xListWasEmpty != pdFALSE ) + { + /* The current timer list is empty - is the overflow list + also empty? */ + xListWasEmpty = listLIST_IS_EMPTY( pxOverflowTimerList ); + } + + vQueueWaitForMessageRestricted( xTimerQueue, ( xNextExpireTime - xTimeNow ), xListWasEmpty ); + + if( xTaskResumeAll() == pdFALSE ) + { + /* Yield to wait for either a command to arrive, or the + block time to expire. If a command arrived between the + critical section being exited and this yield then the yield + will not cause the task to block. */ + portYIELD_WITHIN_API(); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + } + else + { + ( void ) xTaskResumeAll(); + } + } +} +/*-----------------------------------------------------------*/ + +static TickType_t prvGetNextExpireTime( BaseType_t * const pxListWasEmpty ) +{ +TickType_t xNextExpireTime; + + /* Timers are listed in expiry time order, with the head of the list + referencing the task that will expire first. Obtain the time at which + the timer with the nearest expiry time will expire. If there are no + active timers then just set the next expire time to 0. That will cause + this task to unblock when the tick count overflows, at which point the + timer lists will be switched and the next expiry time can be + re-assessed. */ + *pxListWasEmpty = listLIST_IS_EMPTY( pxCurrentTimerList ); + if( *pxListWasEmpty == pdFALSE ) + { + xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList ); + } + else + { + /* Ensure the task unblocks when the tick count rolls over. */ + xNextExpireTime = ( TickType_t ) 0U; + } + + return xNextExpireTime; +} +/*-----------------------------------------------------------*/ + +static TickType_t prvSampleTimeNow( BaseType_t * const pxTimerListsWereSwitched ) +{ +TickType_t xTimeNow; +PRIVILEGED_DATA static TickType_t xLastTime = ( TickType_t ) 0U; /*lint !e956 Variable is only accessible to one task. */ + + xTimeNow = xTaskGetTickCount(); + + if( xTimeNow < xLastTime ) + { + prvSwitchTimerLists(); + *pxTimerListsWereSwitched = pdTRUE; + } + else + { + *pxTimerListsWereSwitched = pdFALSE; + } + + xLastTime = xTimeNow; + + return xTimeNow; +} +/*-----------------------------------------------------------*/ + +static BaseType_t prvInsertTimerInActiveList( Timer_t * const pxTimer, const TickType_t xNextExpiryTime, const TickType_t xTimeNow, const TickType_t xCommandTime ) +{ +BaseType_t xProcessTimerNow = pdFALSE; + + listSET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ), xNextExpiryTime ); + listSET_LIST_ITEM_OWNER( &( pxTimer->xTimerListItem ), pxTimer ); + + if( xNextExpiryTime <= xTimeNow ) + { + /* Has the expiry time elapsed between the command to start/reset a + timer was issued, and the time the command was processed? */ + if( ( ( TickType_t ) ( xTimeNow - xCommandTime ) ) >= pxTimer->xTimerPeriodInTicks ) /*lint !e961 MISRA exception as the casts are only redundant for some ports. */ + { + /* The time between a command being issued and the command being + processed actually exceeds the timers period. */ + xProcessTimerNow = pdTRUE; + } + else + { + vListInsert( pxOverflowTimerList, &( pxTimer->xTimerListItem ) ); + } + } + else + { + if( ( xTimeNow < xCommandTime ) && ( xNextExpiryTime >= xCommandTime ) ) + { + /* If, since the command was issued, the tick count has overflowed + but the expiry time has not, then the timer must have already passed + its expiry time and should be processed immediately. */ + xProcessTimerNow = pdTRUE; + } + else + { + vListInsert( pxCurrentTimerList, &( pxTimer->xTimerListItem ) ); + } + } + + return xProcessTimerNow; +} +/*-----------------------------------------------------------*/ + +static void prvProcessReceivedCommands( void ) +{ +DaemonTaskMessage_t xMessage; +Timer_t *pxTimer; +BaseType_t xTimerListsWereSwitched, xResult; +TickType_t xTimeNow; + + while( xQueueReceive( xTimerQueue, &xMessage, tmrNO_DELAY ) != pdFAIL ) /*lint !e603 xMessage does not have to be initialised as it is passed out, not in, and it is not used unless xQueueReceive() returns pdTRUE. */ + { + #if ( INCLUDE_xTimerPendFunctionCall == 1 ) + { + /* Negative commands are pended function calls rather than timer + commands. */ + if( xMessage.xMessageID < ( BaseType_t ) 0 ) + { + const CallbackParameters_t * const pxCallback = &( xMessage.u.xCallbackParameters ); + + /* The timer uses the xCallbackParameters member to request a + callback be executed. Check the callback is not NULL. */ + configASSERT( pxCallback ); + + /* Call the function. */ + pxCallback->pxCallbackFunction( pxCallback->pvParameter1, pxCallback->ulParameter2 ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* INCLUDE_xTimerPendFunctionCall */ + + /* Commands that are positive are timer commands rather than pended + function calls. */ + if( xMessage.xMessageID >= ( BaseType_t ) 0 ) + { + /* The messages uses the xTimerParameters member to work on a + software timer. */ + pxTimer = xMessage.u.xTimerParameters.pxTimer; + + if( listIS_CONTAINED_WITHIN( NULL, &( pxTimer->xTimerListItem ) ) == pdFALSE ) /*lint !e961. The cast is only redundant when NULL is passed into the macro. */ + { + /* The timer is in a list, remove it. */ + ( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + + traceTIMER_COMMAND_RECEIVED( pxTimer, xMessage.xMessageID, xMessage.u.xTimerParameters.xMessageValue ); + + /* In this case the xTimerListsWereSwitched parameter is not used, but + it must be present in the function call. prvSampleTimeNow() must be + called after the message is received from xTimerQueue so there is no + possibility of a higher priority task adding a message to the message + queue with a time that is ahead of the timer daemon task (because it + pre-empted the timer daemon task after the xTimeNow value was set). */ + xTimeNow = prvSampleTimeNow( &xTimerListsWereSwitched ); + + switch( xMessage.xMessageID ) + { + case tmrCOMMAND_START : + case tmrCOMMAND_START_FROM_ISR : + case tmrCOMMAND_RESET : + case tmrCOMMAND_RESET_FROM_ISR : + case tmrCOMMAND_START_DONT_TRACE : + /* Start or restart a timer. */ + pxTimer->ucStatus |= tmrSTATUS_IS_ACTIVE; + if( prvInsertTimerInActiveList( pxTimer, xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, xTimeNow, xMessage.u.xTimerParameters.xMessageValue ) != pdFALSE ) + { + /* The timer expired before it was added to the active + timer list. Process it now. */ + pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer ); + traceTIMER_EXPIRED( pxTimer ); + + if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0 ) + { + xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xMessage.u.xTimerParameters.xMessageValue + pxTimer->xTimerPeriodInTicks, NULL, tmrNO_DELAY ); + configASSERT( xResult ); + ( void ) xResult; + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + break; + + case tmrCOMMAND_STOP : + case tmrCOMMAND_STOP_FROM_ISR : + /* The timer has already been removed from the active list. */ + pxTimer->ucStatus &= ~tmrSTATUS_IS_ACTIVE; + break; + + case tmrCOMMAND_CHANGE_PERIOD : + case tmrCOMMAND_CHANGE_PERIOD_FROM_ISR : + pxTimer->ucStatus |= tmrSTATUS_IS_ACTIVE; + pxTimer->xTimerPeriodInTicks = xMessage.u.xTimerParameters.xMessageValue; + configASSERT( ( pxTimer->xTimerPeriodInTicks > 0 ) ); + + /* The new period does not really have a reference, and can + be longer or shorter than the old one. The command time is + therefore set to the current time, and as the period cannot + be zero the next expiry time can only be in the future, + meaning (unlike for the xTimerStart() case above) there is + no fail case that needs to be handled here. */ + ( void ) prvInsertTimerInActiveList( pxTimer, ( xTimeNow + pxTimer->xTimerPeriodInTicks ), xTimeNow, xTimeNow ); + break; + + case tmrCOMMAND_DELETE : + #if ( configSUPPORT_DYNAMIC_ALLOCATION == 1 ) + { + /* The timer has already been removed from the active list, + just free up the memory if the memory was dynamically + allocated. */ + if( ( pxTimer->ucStatus & tmrSTATUS_IS_STATICALLY_ALLOCATED ) == ( uint8_t ) 0 ) + { + vPortFree( pxTimer ); + } + else + { + pxTimer->ucStatus &= ~tmrSTATUS_IS_ACTIVE; + } + } + #else + { + /* If dynamic allocation is not enabled, the memory + could not have been dynamically allocated. So there is + no need to free the memory - just mark the timer as + "not active". */ + pxTimer->ucStatus &= ~tmrSTATUS_IS_ACTIVE; + } + #endif /* configSUPPORT_DYNAMIC_ALLOCATION */ + break; + + default : + /* Don't expect to get here. */ + break; + } + } + } +} +/*-----------------------------------------------------------*/ + +static void prvSwitchTimerLists( void ) +{ +TickType_t xNextExpireTime, xReloadTime; +List_t *pxTemp; +Timer_t *pxTimer; +BaseType_t xResult; + + /* The tick count has overflowed. The timer lists must be switched. + If there are any timers still referenced from the current timer list + then they must have expired and should be processed before the lists + are switched. */ + while( listLIST_IS_EMPTY( pxCurrentTimerList ) == pdFALSE ) + { + xNextExpireTime = listGET_ITEM_VALUE_OF_HEAD_ENTRY( pxCurrentTimerList ); + + /* Remove the timer from the list. */ + pxTimer = ( Timer_t * ) listGET_OWNER_OF_HEAD_ENTRY( pxCurrentTimerList ); /*lint !e9087 !e9079 void * is used as this macro is used with tasks and co-routines too. Alignment is known to be fine as the type of the pointer stored and retrieved is the same. */ + ( void ) uxListRemove( &( pxTimer->xTimerListItem ) ); + traceTIMER_EXPIRED( pxTimer ); + + /* Execute its callback, then send a command to restart the timer if + it is an auto-reload timer. It cannot be restarted here as the lists + have not yet been switched. */ + pxTimer->pxCallbackFunction( ( TimerHandle_t ) pxTimer ); + + if( ( pxTimer->ucStatus & tmrSTATUS_IS_AUTORELOAD ) != 0 ) + { + /* Calculate the reload value, and if the reload value results in + the timer going into the same timer list then it has already expired + and the timer should be re-inserted into the current list so it is + processed again within this loop. Otherwise a command should be sent + to restart the timer to ensure it is only inserted into a list after + the lists have been swapped. */ + xReloadTime = ( xNextExpireTime + pxTimer->xTimerPeriodInTicks ); + if( xReloadTime > xNextExpireTime ) + { + listSET_LIST_ITEM_VALUE( &( pxTimer->xTimerListItem ), xReloadTime ); + listSET_LIST_ITEM_OWNER( &( pxTimer->xTimerListItem ), pxTimer ); + vListInsert( pxCurrentTimerList, &( pxTimer->xTimerListItem ) ); + } + else + { + xResult = xTimerGenericCommand( pxTimer, tmrCOMMAND_START_DONT_TRACE, xNextExpireTime, NULL, tmrNO_DELAY ); + configASSERT( xResult ); + ( void ) xResult; + } + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + + pxTemp = pxCurrentTimerList; + pxCurrentTimerList = pxOverflowTimerList; + pxOverflowTimerList = pxTemp; +} +/*-----------------------------------------------------------*/ + +static void prvCheckForValidListAndQueue( void ) +{ + /* Check that the list from which active timers are referenced, and the + queue used to communicate with the timer service, have been + initialised. */ + taskENTER_CRITICAL(); + { + if( xTimerQueue == NULL ) + { + vListInitialise( &xActiveTimerList1 ); + vListInitialise( &xActiveTimerList2 ); + pxCurrentTimerList = &xActiveTimerList1; + pxOverflowTimerList = &xActiveTimerList2; + + #if( configSUPPORT_STATIC_ALLOCATION == 1 ) + { + /* The timer queue is allocated statically in case + configSUPPORT_DYNAMIC_ALLOCATION is 0. */ + static StaticQueue_t xStaticTimerQueue; /*lint !e956 Ok to declare in this manner to prevent additional conditional compilation guards in other locations. */ + static uint8_t ucStaticTimerQueueStorage[ ( size_t ) configTIMER_QUEUE_LENGTH * sizeof( DaemonTaskMessage_t ) ]; /*lint !e956 Ok to declare in this manner to prevent additional conditional compilation guards in other locations. */ + + xTimerQueue = xQueueCreateStatic( ( UBaseType_t ) configTIMER_QUEUE_LENGTH, ( UBaseType_t ) sizeof( DaemonTaskMessage_t ), &( ucStaticTimerQueueStorage[ 0 ] ), &xStaticTimerQueue ); + } + #else + { + xTimerQueue = xQueueCreate( ( UBaseType_t ) configTIMER_QUEUE_LENGTH, sizeof( DaemonTaskMessage_t ) ); + } + #endif + + #if ( configQUEUE_REGISTRY_SIZE > 0 ) + { + if( xTimerQueue != NULL ) + { + vQueueAddToRegistry( xTimerQueue, "TmrQ" ); + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + #endif /* configQUEUE_REGISTRY_SIZE */ + } + else + { + mtCOVERAGE_TEST_MARKER(); + } + } + taskEXIT_CRITICAL(); +} +/*-----------------------------------------------------------*/ + +BaseType_t xTimerIsTimerActive( TimerHandle_t xTimer ) +{ +BaseType_t xReturn; +Timer_t *pxTimer = xTimer; + + configASSERT( xTimer ); + + /* Is the timer in the list of active timers? */ + taskENTER_CRITICAL(); + { + if( ( pxTimer->ucStatus & tmrSTATUS_IS_ACTIVE ) == 0 ) + { + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } + } + taskEXIT_CRITICAL(); + + return xReturn; +} /*lint !e818 Can't be pointer to const due to the typedef. */ +/*-----------------------------------------------------------*/ + +void *pvTimerGetTimerID( const TimerHandle_t xTimer ) +{ +Timer_t * const pxTimer = xTimer; +void *pvReturn; + + configASSERT( xTimer ); + + taskENTER_CRITICAL(); + { + pvReturn = pxTimer->pvTimerID; + } + taskEXIT_CRITICAL(); + + return pvReturn; +} +/*-----------------------------------------------------------*/ + +void vTimerSetTimerID( TimerHandle_t xTimer, void *pvNewID ) +{ +Timer_t * const pxTimer = xTimer; + + configASSERT( xTimer ); + + taskENTER_CRITICAL(); + { + pxTimer->pvTimerID = pvNewID; + } + taskEXIT_CRITICAL(); +} +/*-----------------------------------------------------------*/ + +#if( INCLUDE_xTimerPendFunctionCall == 1 ) + + BaseType_t xTimerPendFunctionCallFromISR( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, BaseType_t *pxHigherPriorityTaskWoken ) + { + DaemonTaskMessage_t xMessage; + BaseType_t xReturn; + + /* Complete the message with the function parameters and post it to the + daemon task. */ + xMessage.xMessageID = tmrCOMMAND_EXECUTE_CALLBACK_FROM_ISR; + xMessage.u.xCallbackParameters.pxCallbackFunction = xFunctionToPend; + xMessage.u.xCallbackParameters.pvParameter1 = pvParameter1; + xMessage.u.xCallbackParameters.ulParameter2 = ulParameter2; + + xReturn = xQueueSendFromISR( xTimerQueue, &xMessage, pxHigherPriorityTaskWoken ); + + tracePEND_FUNC_CALL_FROM_ISR( xFunctionToPend, pvParameter1, ulParameter2, xReturn ); + + return xReturn; + } + +#endif /* INCLUDE_xTimerPendFunctionCall */ +/*-----------------------------------------------------------*/ + +#if( INCLUDE_xTimerPendFunctionCall == 1 ) + + BaseType_t xTimerPendFunctionCall( PendedFunction_t xFunctionToPend, void *pvParameter1, uint32_t ulParameter2, TickType_t xTicksToWait ) + { + DaemonTaskMessage_t xMessage; + BaseType_t xReturn; + + /* This function can only be called after a timer has been created or + after the scheduler has been started because, until then, the timer + queue does not exist. */ + configASSERT( xTimerQueue ); + + /* Complete the message with the function parameters and post it to the + daemon task. */ + xMessage.xMessageID = tmrCOMMAND_EXECUTE_CALLBACK; + xMessage.u.xCallbackParameters.pxCallbackFunction = xFunctionToPend; + xMessage.u.xCallbackParameters.pvParameter1 = pvParameter1; + xMessage.u.xCallbackParameters.ulParameter2 = ulParameter2; + + xReturn = xQueueSendToBack( xTimerQueue, &xMessage, xTicksToWait ); + + tracePEND_FUNC_CALL( xFunctionToPend, pvParameter1, ulParameter2, xReturn ); + + return xReturn; + } + +#endif /* INCLUDE_xTimerPendFunctionCall */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + UBaseType_t uxTimerGetTimerNumber( TimerHandle_t xTimer ) + { + return ( ( Timer_t * ) xTimer )->uxTimerNumber; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + void vTimerSetTimerNumber( TimerHandle_t xTimer, UBaseType_t uxTimerNumber ) + { + ( ( Timer_t * ) xTimer )->uxTimerNumber = uxTimerNumber; + } + +#endif /* configUSE_TRACE_FACILITY */ +/*-----------------------------------------------------------*/ + +/* This entire source file will be skipped if the application is not configured +to include software timer functionality. If you want to include software timer +functionality then ensure configUSE_TIMERS is set to 1 in FreeRTOSConfig.h. */ +#endif /* configUSE_TIMERS == 1 */ + + + diff --git a/product/rcar/src/rcar_core.c b/product/rcar/src/rcar_core.c new file mode 100644 index 000000000..4adc9b169 --- /dev/null +++ b/product/rcar/src/rcar_core.c @@ -0,0 +1,23 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +#define PRR (0xFFF00044U) +#define PRR_57EN_OFS (27) +#define PRR_57EN_MASK (0x0f) +#define PRR_53EN_OFS (22) +#define PRR_53EN_MASK (0x0f) +#define PRR_CAEN_MASK ((PRR_57EN_MASK << PRR_57EN_OFS) \ + + (PRR_53EN_MASK << PRR_53EN_OFS)) + +unsigned int rcar_core_get_count(void) +{ + return (__builtin_popcount((mmio_read_32(PRR) & PRR_CAEN_MASK) \ + ^ PRR_CAEN_MASK)); +} diff --git a/tools/build_system/firmware.mk b/tools/build_system/firmware.mk index 0b41fd72c..17fe2094b 100644 --- a/tools/build_system/firmware.mk +++ b/tools/build_system/firmware.mk @@ -83,13 +83,14 @@ FIRMWARE_DIR := $(PRODUCT_DIR)/$(FIRMWARE) TARGET := $(BIN_DIR)/$(FIRMWARE) TARGET_BIN := $(TARGET).bin TARGET_ELF := $(TARGET).elf +TARGET_SREC := $(TARGET).srec vpath %.c $(FIRMWARE_DIR) vpath %.S $(FIRMWARE_DIR) vpath %.c $(PRODUCT_DIR)/src vpath %.S $(PRODUCT_DIR)/src -goal: $(TARGET_BIN) +goal: $(TARGET_SREC) ifneq ($(BS_ARCH_CPU),host) ifeq ($(BS_LINKER),ARM) @@ -180,9 +181,15 @@ ifeq ($(BS_FIRMWARE_HAS_MULTITHREADING),yes) BUILD_SUFFIX := $(MULTHREADING_SUFFIX) BUILD_HAS_MULTITHREADING := yes - INCLUDES += $(OS_DIR)/RTX/Source - INCLUDES += $(OS_DIR)/RTX/Include - INCLUDES += $(OS_DIR)/../Core/Include + ifneq ($(findstring $(BS_FIRMWARE_CPU),$(ARMV8A_CPUS)),) + INCLUDES += $(OS_DIR)/Include + INCLUDES += $(FREERTOS_DIR)/../../Source/include + INCLUDES += $(FREERTOS_DIR)/../../Source/portable/GCC/ARM_CA53_64_Rcar + else + INCLUDES += $(OS_DIR)/RTX/Source + INCLUDES += $(OS_DIR)/RTX/Include + INCLUDES += $(OS_DIR)/../Core/Include + endif else BUILD_HAS_MULTITHREADING := no endif @@ -335,4 +342,8 @@ $(TARGET_BIN): $(TARGET_ELF) | $$(@D)/ $(call show-action,BIN,$@) $(OBJCOPY) $< $(OCFLAGS) $@ cp $@ $(BIN_DIR)/firmware.bin + +$(TARGET_SREC): $(TARGET_BIN) + $(call show-action,SREC,$@) + $(OBJCOPY) -O srec $(TARGET_ELF) $(basename $@).srec endif -- GitLab From d982abe08b1b7f38d6ad4b689fdd98caeebee580 Mon Sep 17 00:00:00 2001 From: Nicolas Royer Date: Sun, 27 Sep 2020 17:36:43 +0200 Subject: [PATCH 03/19] rcar/module: add rcar scif module and config data Signed-off-by: Tsutomu Muroya Signed-off-by: Nicolas Royer --- .../module/rcar_scif/include/mod_rcar_scif.h | 57 ++++ product/rcar/module/rcar_scif/src/Makefile | 11 + .../rcar/module/rcar_scif/src/mod_rcar_scif.c | 280 ++++++++++++++++++ product/rcar/module/rcar_scif/src/scif.h | 110 +++++++ product/rcar/scp_ramfw/config_rcar_scif.c | 30 ++ product/rcar/scp_ramfw/fmw_io.h | 17 ++ 6 files changed, 505 insertions(+) create mode 100644 product/rcar/module/rcar_scif/include/mod_rcar_scif.h create mode 100644 product/rcar/module/rcar_scif/src/Makefile create mode 100644 product/rcar/module/rcar_scif/src/mod_rcar_scif.c create mode 100644 product/rcar/module/rcar_scif/src/scif.h create mode 100644 product/rcar/scp_ramfw/config_rcar_scif.c create mode 100644 product/rcar/scp_ramfw/fmw_io.h diff --git a/product/rcar/module/rcar_scif/include/mod_rcar_scif.h b/product/rcar/module/rcar_scif/include/mod_rcar_scif.h new file mode 100644 index 000000000..792f864a8 --- /dev/null +++ b/product/rcar/module/rcar_scif/include/mod_rcar_scif.h @@ -0,0 +1,57 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_RCAR_SCIF_H +#define MOD_RCAR_SCIF_H + +#include +#include + +/*! + * \addtogroup GroupRCARModule RCAR Product Modules + * \{ + */ + +/*! + * \defgroup GroupRCARSCIF SCIF Driver + * + * \brief Device driver module for the Primecell® SCIF UART. + * + * \{ + */ + +/*! + * \brief SCIF element configuration data. + */ +struct mod_rcar_scif_element_cfg { + /*! + * \brief Base address of the device registers. + */ + uintptr_t reg_base; + + /*! + * \brief Baud rate in bits per second. + */ + unsigned int baud_rate_bps; + + /*! + * \brief Reference clock in Hertz. + */ + uint64_t clock_rate_hz; +}; + +/*! + * \} + */ + +/*! + * \} + */ + +int mod_rcar_scif_resume(); + +#endif /* MOD_RCAR_SCIF_H */ diff --git a/product/rcar/module/rcar_scif/src/Makefile b/product/rcar/module/rcar_scif/src/Makefile new file mode 100644 index 000000000..38cdd0e4d --- /dev/null +++ b/product/rcar/module/rcar_scif/src/Makefile @@ -0,0 +1,11 @@ +# +# Renesas SCP/MCP Software +# Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +BS_LIB_NAME := "SCIF" +BS_LIB_SOURCES = mod_rcar_scif.c + +include $(BS_DIR)/lib.mk diff --git a/product/rcar/module/rcar_scif/src/mod_rcar_scif.c b/product/rcar/module/rcar_scif/src/mod_rcar_scif.c new file mode 100644 index 000000000..4e394d81a --- /dev/null +++ b/product/rcar/module/rcar_scif/src/mod_rcar_scif.c @@ -0,0 +1,280 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "scif.h" + +struct mod_rcar_scif_element_ctx { + /* Whether the device has an open file stream */ + bool open; +}; + +static struct mod_rcar_scif_ctx { + bool initialized; /* Whether the context has been initialized */ + + struct mod_rcar_scif_element_ctx *elements; /* Element context table */ +} mod_rcar_scif_ctx = { + .initialized = false, +}; + +static const struct mod_rcar_scif_element_cfg *current_cfg; + +static int mod_rcar_scif_init_ctx(struct mod_rcar_scif_ctx *ctx) +{ + size_t element_count; + + fwk_assert(!mod_rcar_scif_ctx.initialized); + + element_count = fwk_module_get_element_count(fwk_module_id_rcar_scif); + if (element_count == 0) + return FWK_SUCCESS; + + ctx->elements = fwk_mm_alloc(element_count, sizeof(ctx->elements[0])); + if (!fwk_expect(ctx->elements != NULL)) + return FWK_E_NOMEM; + + for (size_t i = 0; i < element_count; i++) { + ctx->elements[i] = (struct mod_rcar_scif_element_ctx){ + .open = false, + }; + } + + mod_rcar_scif_ctx.initialized = true; + + return FWK_SUCCESS; +} + +/* + * For details on the constants and equations used to calculate the baud rate + * settings, please consult the SCIF TRM. + * However, this version does not support it. + */ +static int mod_rcar_scif_set_baud_rate( + const struct mod_rcar_scif_element_cfg *cfg) +{ + struct scif_reg *reg = (void *)cfg->reg_base; + uint32_t status; + int i; + + assert(reg); + current_cfg = cfg; + + if (reg == (void *)BOARD_UART1_BASE) { + /* SCSI-1 */ + status = mmio_read_32(CPG_SMSTPCR2); + status = (status & ~MSTP26); + mmio_write_32(CPG_CPGWPR, ~status); + mmio_write_32(CPG_SMSTPCR2, status); + + while (mmio_read_32(CPG_MSTPSR2) & MSTP26) + continue; + } else if (reg == (void *)BOARD_UART2_BASE) { + /* SCSI-2 */ + status = mmio_read_32(CPG_SMSTPCR3); + status = (status & ~MSTP310); + mmio_write_32(CPG_CPGWPR, ~status); + mmio_write_32(CPG_SMSTPCR3, status); + + while (mmio_read_32(CPG_MSTPSR3) & MSTP310) + continue; + } else { + return FWK_E_PARAM; + } + + /* Clear bits TE and RE in SCSCR to 0 */ + reg->SCSCR = (SCSCR_TE_DIS | SCSCR_RE_DIS); + /* Set bits TFRST and RFRST in SCFCR to 1 */ + reg->SCFCR |= (SCFCR_TFRST_EN | SCFCR_RFRS_EN); + /* Read flags of ER, DR, BRK, */ + reg->SCFSR = SCFSR_INIT_DATA; + reg->SCLSR = 0; + /* Set bits CKE[1:0] in SCSCR */ + reg->SCSCR = (reg->SCSCR & ~SCSCR_CKE_MASK) | SCSCR_CKE_INT_CLK; + /* Set data transfer format in SCSMR */ + reg->SCSMR = SCSMR_INIT_DATA; + /* Set value in SCBRR */ +#if SCIF_CLK == SCIF_INTERNAL_CLK + if ((mmio_read_32(PRR) & (PRR_PRODUCT_MASK | PRR_CUT_MASK)) == + PRR_PRODUCT_H3_VER_10) { + /* H3 Ver.1.0 sets clock to doubling */ + reg->SCBRR = SCBRR_230400BPS; + } else { + reg->SCBRR = SCBRR_115200BPS; + } +#else + reg->DL = DL_INIT_DATA; + reg->CKS = CKS_INIT_DATA; +#endif /* SCIF_CLK == SCIF_INTERNAL_CLK */ + for (i = 100; i; i--); + reg->SCFCR = SCFCR_INIT_DATA; + reg->SCSCR = (reg->SCSCR | (SCSCR_TE_EN | SCSCR_RE_EN)); + return FWK_SUCCESS; +} + +int mod_rcar_scif_resume() { + return mod_rcar_scif_set_baud_rate(current_cfg); +} + +static void mod_rcar_scif_putch(fwk_id_t id, char c) +{ + const struct mod_rcar_scif_element_cfg *cfg = fwk_module_get_data(id); + struct scif_reg *reg = (void *)cfg->reg_base; + + /* Check if the transmit FIFO is full */ + while (GET_SCFDR_T(reg) >= FIFO_FULL) + continue; + + reg->SCFTDR = c; +} + +static bool mod_rcar_scif_getch(fwk_id_t id, char *ch) +{ + return false; /* Not implemented yet */ +} + +static void mod_rcar_scif_flush(fwk_id_t id) +{ + const struct mod_rcar_scif_element_cfg *cfg = fwk_module_get_data(id); + struct scif_reg *reg = (void *)cfg->reg_base; + + /* Check if the transmit data is available */ + while (GET_SCFDR_T(reg)) + continue; +} + +/* + * Framework module handlers + */ +static int mod_rcar_scif_init( + fwk_id_t module_id, + unsigned int element_count, + const void *data) +{ + if (mod_rcar_scif_ctx.initialized) + return FWK_SUCCESS; + + return mod_rcar_scif_init_ctx(&mod_rcar_scif_ctx); +} + +static int mod_rcar_scif_element_init( + fwk_id_t element_id, + unsigned int unused, + const void *data) +{ + return FWK_SUCCESS; +} + +static int mod_rcar_scif_start(fwk_id_t id) +{ + return FWK_SUCCESS; +} + +/* + * Framework io handlers + */ +static int mod_rcar_scif_io_open(const struct fwk_io_stream *stream) +{ + int status; + struct mod_rcar_scif_element_ctx *ctx; + const struct mod_rcar_scif_element_cfg *cfg; + + if (!fwk_id_is_type(stream->id, FWK_ID_TYPE_ELEMENT)) + return FWK_E_SUPPORT; + + if (!mod_rcar_scif_ctx.initialized) { + status = mod_rcar_scif_init_ctx(&mod_rcar_scif_ctx); + if (status != FWK_SUCCESS) + return FWK_E_STATE; + } + + ctx = &mod_rcar_scif_ctx.elements[fwk_id_get_element_idx(stream->id)]; + if (ctx->open) /* Refuse to open the same device twice */ + return FWK_E_BUSY; + + ctx->open = true; + + /* Enable the device if possible */ + cfg = fwk_module_get_data(stream->id); + status = mod_rcar_scif_set_baud_rate(cfg); + + return status; +} + +static int mod_rcar_scif_io_getch( + const struct fwk_io_stream *restrict stream, + char *restrict ch) +{ + const struct mod_rcar_scif_element_ctx *ctx = + &mod_rcar_scif_ctx.elements[fwk_id_get_element_idx(stream->id)]; + + bool ok = true; + + fwk_assert(ctx->open); + + ok = mod_rcar_scif_getch(stream->id, ch); + if (!ok) + return FWK_PENDING; + + return FWK_SUCCESS; +} + +static int mod_rcar_scif_io_putch(const struct fwk_io_stream *stream, char ch) +{ + const struct mod_rcar_scif_element_ctx *ctx = + &mod_rcar_scif_ctx.elements[fwk_id_get_element_idx(stream->id)]; + + fwk_assert(ctx->open); + + if ((ch == '\n') && !(stream->mode & FWK_IO_MODE_BINARY)) + mod_rcar_scif_putch(stream->id, '\r'); /* Prepend carriage return */ + + mod_rcar_scif_putch(stream->id, ch); + + return FWK_SUCCESS; +} + +static int mod_rcar_scif_io_close(const struct fwk_io_stream *stream) +{ + struct mod_rcar_scif_element_ctx *ctx; + + fwk_assert(stream != NULL); /* Validated by the framework */ + fwk_assert(fwk_module_is_valid_element_id(stream->id)); + + mod_rcar_scif_flush(stream->id); + + ctx = &mod_rcar_scif_ctx.elements[fwk_id_get_element_idx(stream->id)]; + fwk_assert(ctx->open); + + ctx->open = false; + + return FWK_SUCCESS; +} + +const struct fwk_module module_rcar_scif = { + .name = "RCAR SCIF", + .type = FWK_MODULE_TYPE_DRIVER, + + .init = mod_rcar_scif_init, + .element_init = mod_rcar_scif_element_init, + .start = mod_rcar_scif_start, + + .adapter = + (struct fwk_io_adapter){ + .open = mod_rcar_scif_io_open, + .getch = mod_rcar_scif_io_getch, + .putch = mod_rcar_scif_io_putch, + .close = mod_rcar_scif_io_close, + }, +}; diff --git a/product/rcar/module/rcar_scif/src/scif.h b/product/rcar/module/rcar_scif/src/scif.h new file mode 100644 index 000000000..59c44bd3c --- /dev/null +++ b/product/rcar/module/rcar_scif/src/scif.h @@ -0,0 +1,110 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef SCIF_H +#define SCIF_H + +#include +#include + +#define SCIF_INTERNAL_CLK 0 /* Internal clock(S3D4:66.66MHz) */ +#define SCIF_EXTARNAL_CLK 1 /* External clock(SCK2:14.7456MHz) */ +#define SCIF_CLK SCIF_INTERNAL_CLK + +struct scif_reg { + FWK_RW uint16_t SCSMR; /* H'00 */ + uint16_t RESERVED1; /* H'02 */ + FWK_RW uint8_t SCBRR; /* H'04 */ + uint8_t RESERVED2; /* H'05 */ + uint16_t RESERVED3; /* H'06 */ + FWK_RW uint16_t SCSCR; /* H'08 */ + uint16_t RESERVED4; /* H'0A */ + FWK_W uint8_t SCFTDR; /* H'0C */ + uint8_t RESERVED5; /* H'0D */ + uint16_t RESERVED6; /* H'0E */ + FWK_RW uint16_t SCFSR; /* H'10 */ + uint16_t RESERVED7; /* H'12 */ + FWK_R uint8_t SCFRDR; /* H'14 */ + uint8_t RESERVED8; /* H'15 */ + uint16_t RESERVED9; /* H'16 */ + FWK_RW uint16_t SCFCR; /* H'18 */ + uint16_t RESERVED10;/* H'1A */ + FWK_R uint16_t SCFDR; /* H'1C */ + uint16_t RESERVED11;/* H'1E */ + FWK_RW uint16_t SCSPTR; /* H'20 */ + uint16_t RESERVED12;/* H'22 */ + FWK_RW uint16_t SCLSR; /* H'24 */ + uint16_t RESERVED13;/* H'26 */ + uint32_t RESERVED14;/* H'28 */ + uint32_t RESERVED15;/* H'2C */ + FWK_RW uint16_t DL; /* H'30 */ + uint16_t RESERVED16;/* H'32 */ + FWK_RW uint16_t CKS; /* H'34 */ +}; + +#define CPG_SMSTPCR2 (CPG_BASE + 0x0138) +#define CPG_SMSTPCR3 (CPG_BASE + 0x013C) +#define CPG_MSTPSR2 (CPG_BASE + 0x0040) +#define CPG_MSTPSR3 (CPG_BASE + 0x0048) +#define CPG_CPGWPR (CPG_BASE + 0x0900) + +#define PRR (0xFFF00044) +#define PRR_PRODUCT_MASK (0x00007F00) +#define PRR_CUT_MASK (0x000000FF) +#define PRR_PRODUCT_H3_VER_10 (0x00004F00) /* R-Car H3 Ver.1.0 */ + +#define SCSMR_CA_MASK (1 << 7) +#define SCSMR_CA_ASYNC (0x0000) +#define SCSMR_CHR_MASK (1 << 6) +#define SCSMR_CHR_8 (0x0000) +#define SCSMR_PE_MASK (1 << 5) +#define SCSMR_PE_DIS (0x0000) +#define SCSMR_STOP_MASK (1 << 3) +#define SCSMR_STOP_1 (0x0000) +#define SCSMR_CKS_MASK (3 << 0) +#define SCSMR_CKS_DIV1 (0x0000) +#define SCSMR_INIT_DATA (SCSMR_CA_ASYNC + SCSMR_CHR_8 + \ + SCSMR_PE_DIS + SCSMR_STOP_1 + \ + SCSMR_CKS_DIV1) + +#define MSTP310 (1 << 10) +#define MSTP26 (1 << 6) + +#define SCSCR_TE_MASK (1 << 5) +#define SCSCR_TE_DIS (0x0000) +#define SCSCR_TE_EN (0x0020) +#define SCSCR_RE_MASK (1 << 4) +#define SCSCR_RE_DIS (0x0000) +#define SCSCR_RE_EN (0x0010) +#define SCSCR_CKE_MASK (3 << 0) +#define SCSCR_CKE_INT (0x0000) +#define SCSCR_CKE_BRG (0x0002) +#if SCIF_CLK == SCIF_EXTARNAL_CLK +#define SCSCR_CKE_INT_CLK (SCSCR_CKE_BRG) +#else +#define SCSCR_CKE_INT_CLK (SCSCR_CKE_INT) +#endif + +#define SCFSR_INIT_DATA (0x0000) + +#define SCFCR_TFRST_EN (0x0004) +#define SCFCR_RFRS_EN (0x0002) + +#define FIFO_SIZE_BIT (5) +#define FIFO_MASK ((1 << FIFO_SIZE_BIT) -1) +#define FIFO_R_SHIFT (0) +#define FIFO_T_SHIFT (8) +#define FIFO_FULL (16) +#define GET_SCFDR_T(reg) ((reg->SCFDR >> FIFO_T_SHIFT) & FIFO_MASK) + +#define SCFCR_TTRG_8 (0x0000) +#define SCFCR_INIT_DATA (SCFCR_TTRG_8) + +#define SCBRR_115200BPS (17) /* 115200bps@66MHz */ +#define SCBRR_230400BPS (8) /* 230400bps@66MHz */ + +#endif /* SCIF_H */ diff --git a/product/rcar/scp_ramfw/config_rcar_scif.c b/product/rcar/scp_ramfw/config_rcar_scif.c new file mode 100644 index 000000000..3b05c4a60 --- /dev/null +++ b/product/rcar/scp_ramfw/config_rcar_scif.c @@ -0,0 +1,30 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +#include + +#include +#include +#include +#include +#include + +struct fwk_module_config config_rcar_scif = { + .elements = FWK_MODULE_STATIC_ELEMENTS({ + [0] = { + .name = "board-uart1", + .data = + &(struct mod_rcar_scif_element_cfg){ + .reg_base = BOARD_UART1_BASE, + }, + }, + + [1] = { 0 }, + }), +}; diff --git a/product/rcar/scp_ramfw/fmw_io.h b/product/rcar/scp_ramfw/fmw_io.h new file mode 100644 index 000000000..40ba92457 --- /dev/null +++ b/product/rcar/scp_ramfw/fmw_io.h @@ -0,0 +1,17 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef FMW_IO_H +#define FMW_IO_H + +#include +#include + +#define FMW_IO_STDIN_ID FWK_ID_ELEMENT(FWK_MODULE_IDX_RCAR_SCIF, 0) +#define FMW_IO_STDOUT_ID FWK_ID_ELEMENT(FWK_MODULE_IDX_RCAR_SCIF, 0) + +#endif /* FMW_IO_H */ -- GitLab From e07b2d0fe5f9c05d642ea5bb4007741db84022e1 Mon Sep 17 00:00:00 2001 From: Nicolas Royer Date: Sun, 27 Sep 2020 17:37:25 +0200 Subject: [PATCH 04/19] rcar/module: add rcar clock module and config data Signed-off-by: Tsutomu Muroya Signed-off-by: Nicolas Royer --- .../rcar_clock/include/mod_rcar_clock.h | 404 ++++++++ product/rcar/module/rcar_clock/src/Makefile | 11 + .../module/rcar_clock/src/mod_rcar_clock.c | 869 ++++++++++++++++++ product/rcar/scp_ramfw/config_rcar_clock.c | 150 +++ 4 files changed, 1434 insertions(+) create mode 100644 product/rcar/module/rcar_clock/include/mod_rcar_clock.h create mode 100644 product/rcar/module/rcar_clock/src/Makefile create mode 100644 product/rcar/module/rcar_clock/src/mod_rcar_clock.c create mode 100644 product/rcar/scp_ramfw/config_rcar_clock.c diff --git a/product/rcar/module/rcar_clock/include/mod_rcar_clock.h b/product/rcar/module/rcar_clock/include/mod_rcar_clock.h new file mode 100644 index 000000000..3c09a1696 --- /dev/null +++ b/product/rcar/module/rcar_clock/include/mod_rcar_clock.h @@ -0,0 +1,404 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_RCAR_CLOCK_H +#define MOD_RCAR_CLOCK_H + +#include +#include +#include +#include + +/*! + * \addtogroup GroupRCARModule RCAR Product Modules + * @{ + */ + +/*! + * \defgroup GroupRCARClock Clock + * @{ + */ + +/*! + * \brief Clock driver interface. + */ +struct mod_rcar_clock_drv_api { + /*! Name of the driver */ + const char *name; + + /*! + * \brief Set a new clock rate by providing a frequency in Hertz (Hz). + * + * \param clock_id Clock device identifier. + * + * \param rate The desired frequency in Hertz. + * + * \param round_mode The type of rounding to perform, if required, to + * achieve the given rate. + * + * \retval FWK_SUCCESS The operation succeeded. + * \return One of the standard framework error codes. + */ + int (*set_rate)(fwk_id_t clock_id, uint64_t rate, + enum mod_clock_round_mode round_mode); + + /*! + * \brief Get the current rate of a clock in Hertz (Hz). + * + * \param clock_id Clock device identifier. + * + * \param [out] rate The current clock rate in Hertz. + * + * \retval FWK_SUCCESS The operation succeeded. + * \return One of the standard framework error codes. + */ + int (*get_rate)(fwk_id_t clock_id, uint64_t *rate); + + /*! + * \brief Get a clock rate in Hertz from an index into the clock's range. + * + * \param clock_id Clock device identifier. + * + * \param rate_index The index into the clock's range to get the rate of. + * + * \param[out] rate The rate, in Hertz, corresponding to the index. + * + * \retval FWK_SUCCESS The operation succeeded. + * \return One of the standard framework error codes. + */ + int (*get_rate_from_index)(fwk_id_t clock_id, unsigned int rate_index, + uint64_t *rate); + + /*! + * \brief Set the running state of a clock. + * + * \param clock_id Clock device identifier. + * + * \param state One of the valid clock states. + * + * \retval FWK_SUCCESS The operation succeeded. + * \return One of the standard framework error codes. + */ + int (*set_state)(fwk_id_t clock_id, enum mod_clock_state state); + + /*! + * \brief Get the running state of a clock. + * + * \param clock_id Clock device identifier. + * + * \param[out] state The current clock state. + * + * \retval FWK_SUCCESS The operation succeeded. + * \return One of the standard framework error codes. + */ + int (*get_state)(fwk_id_t clock_id, enum mod_clock_state *state); + + /*! + * \brief Get the range of rates that the clock supports. + * + * \param clock_id Clock device identifier. + * + * \param[out] range The clock range structure. + * + * \retval FWK_SUCCESS The operation succeeded. + * \return One of the standard framework error codes. + */ + int (*get_range)(fwk_id_t clock_id, struct mod_clock_range *range); + + /*! + * \brief Handle the condition where the state of a clock's power domain is + * about to change. + * + * \details This function will be called prior to the change in power + * state occurring so that the clock driver implementing this API is + * able to perform any required preparatory work beforehand. + * + * \note This function is optional. If the driver does not control any + * clocks that require power state awareness then the pointer may be set + * to NULL. + * + * \param clock_id Clock device identifier. + * + * \param current_state The current power state that the clock's power + * domain will transition away from. + * + * \param new_state The power state that the clock's power domain will + * transition to. + * + * \retval FWK_SUCCESS The operation succeeded. + * \return One of the standard framework error codes. + */ + int (*process_pending_power_transition)( + fwk_id_t clock_id, + unsigned int current_state, + unsigned int new_state); + + /*! + * \brief Handle the condition where the state of a clock's power domain + * has changed. + * + * \details This function will be called after the change in power state + * has occurred. The driver can take any appropriate actions that are + * required to accommodate the new state. The transition can be to a + * deeper power state (e.g. ON->OFF) or to a shallower power state + * (e.g. OFF->ON). + * + * \note This function is optional. If the driver does not control any + * clocks that require power state awareness then the pointer may be set + * to NULL. + * + * \param clock_id Clock device identifier. + * + * \param state The power state that the clock's power domain transitioned + * to. + * + * \retval FWK_SUCCESS The operation succeeded. + * \return One of the standard framework error codes. + */ + int (*process_power_transition)(fwk_id_t clock_id, unsigned int state); + + /*! + * \brief Handle the resume of a clock's power domain. + * + * \retval FWK_SUCCESS The operation succeeded. + * \return One of the standard framework error codes. + */ + int (*resume)(void); +}; + +/*! + * \brief APIs provided by the driver. + */ +enum mod_rcar_clock_api_type { + /*! An implementation of the Clock HAL module's clock driver API */ + MOD_RCAR_CLOCK_API_TYPE_CLOCK, + /*! A low-level API for direct control of CSS clocks */ + MOD_RCAR_CLOCK_API_TYPE_CSS, + MOD_RCAR_CLOCK_API_COUNT, +}; + +/*! + * \brief Sub-types of rcar clock. + */ +enum mod_rcar_clock_type { + /*! A clock with a fixed source. Only its divider can be changed. */ + MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + /*! A clock with multiple, selectable sources and at least one divider. */ + MOD_RCAR_CLOCK_TYPE_MULTI_SOURCE, + /*! + * A clock with multiple, selectable sources, at least one divider, and + * support for modulation. + */ + MOD_RCAR_CLOCK_TYPE_CLUSTER, +}; + +/*! + * \brief Divider register types. + */ +enum mod_rcar_clock_msclock_divider { + /*! Divider affecting the A57 PLL clock source. */ + MOD_RCAR_CLOCK_A57_DIVIDER_DIV_EXT = 1, + /*! Divider affecting the A53 PLL clock source. */ + MOD_RCAR_CLOCK_A53_DIVIDER_DIV_EXT = 2, +}; + +/*! + * \brief Selectable clock sources for V8.2 cluster clocks. + */ +enum mod_clusclock_source { + /*! The clock source is set to a private cluster PLL */ + MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL0 = 0, + /*! The clock source is set to a private cluster PLL */ + MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL1 = 1, + /*! The clock source is set to a private cluster PLL */ + MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL2 = 2, + /*! The clock source is set to a private cluster PLL */ + MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL3 = 3, + /*! The clock source is set to a private cluster PLL */ + MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL4 = 4, + /*! The clock source is set to a private cluster PLL */ + MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL5 = 5, + /*! The clock source is set to a private cluster PLL */ + MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL6 = 6, + /*! The clock source is set to a private cluster PLL */ + MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL7 = 7, + /*! Number of valid clock sources */ + MOD_RCAR_CLOCK_CLUSCLK_SOURCE_MAX +}; + +/*! + * \brief Rate lookup entry. + */ +struct mod_rcar_clock_rate { + /*! Rate in Hertz. */ + uint64_t rate; + /*! Clock source used to obtain the rate (multi-source clocks only). */ + uint8_t source; + /*! The divider register to use (multi-source clocks only). */ + enum mod_rcar_clock_msclock_divider divider_reg; + /*! Divider used to obtain the rate. */ + uint8_t divider; +}; + +/*! + * \brief Ext clock rate lookup entry. + */ +struct mod_ext_clock_rate { + /*! Rate in Hertz. */ + uint32_t ext_clk_rate; +}; + +/*! + * \brief Subsystem clock device configuration. + */ +struct mod_rcar_clock_dev_config { + /*! The type of the clock device. */ + enum mod_rcar_clock_type type; + + /*! + * \brief Indicates whether the clock is part of a CSS clock group (\c true) + * or operating as an independent clock (\c false). + */ + bool is_group_member; + + /*! Pointer to the clock's control register. */ + volatile uint32_t * const control_reg; + + /*! Pointer to the clock's modulator register, if any. */ + volatile uint32_t * const modulator_reg; + + /*! Pointer to the clock's DIV_SYS divider register, if any. */ + volatile uint32_t * const divsys_reg; + + /*! Pointer to the clock's DIV_EXT divider register, if any. */ + volatile uint32_t * const divext_reg; + + /*! Pointer to the clock's rate lookup table. */ + const struct mod_rcar_clock_rate *rate_table; + + /*! The number of rates in the rate lookup table. */ + uint32_t rate_count; + + /*! The rate, in Hz, to set during module initialization. */ + uint64_t initial_rate; + + /*! + * If \c true, the driver will not attempt to set a default frequency, or to + * otherwise configure the PLL during the pre-runtime phase. The PLL is + * expected to be initialized later in response to a notification or other + * event. + */ + const bool defer_initialization; +}; + +/*! + * @cond + */ + +/* Device context */ +struct rcar_clock_dev_ctx { + bool initialized; + uint64_t current_rate; + enum mod_clock_state current_state; + const struct mod_rcar_clock_dev_config *config; +}; + +/* Module context */ +struct rcar_clock_ctx { + struct rcar_clock_dev_ctx *dev_ctx_table; + unsigned int dev_count; + uint32_t extal_clk; +}; + +struct op_points +{ + unsigned long freq; /* Hz */ + unsigned long volt; /* uV */ +}; + +#define PLL_BASE_CLOCK (16640000UL) +#define CPG_FRQCRB (CPG_BASE + 0x0004) +#define CPG_FRQCRB_KICK 0x80000000 +#define CPG_FRQCRC (CPG_BASE + 0x00e0) +#define CPG_FRQCRC_ZFC_A57_MASK 0x00001F00 +#define CPG_FRQCRC_ZFC_A57_SHIFT 8 +#define CPG_FRQCRC_ZFC_A53_MASK 0x0000001F +#define CPG_FRQCRC_ZFC_A53_SHIFT 0 +#ifndef CPG_PLL0CR +#define CPG_PLL0CR (CPG_BASE + 0x00d8) +#endif +#define CPG_PLL0CR_ZFC_MASK 0x7F000000 +#define CPG_PLL0CR_ZFC_SHIFT 24 +#ifndef CPG_PLL2CR +#define CPG_PLL2CR (CPG_BASE + 0x002c) +#endif +#define CPG_PLL2CR_ZFC_MASK 0x7F000000 +#define CPG_PLL2CR_ZFC_SHIFT 24 + +#define min(x,y) ({ \ + __typeof__(x) _x = (x); \ + __typeof__(y) _y = (y); \ + _x < _y ? _x : _y; }) + +#define max(x,y) ({ \ + __typeof__(x) _x = (x); \ + __typeof__(y) _y = (y); \ + _x > _y ? _x : _y; }) + +#define clamp(val, lo, hi) min((__typeof__(val))max(val, lo), hi) + +#define A57_DOMAIN 0 +#define A53_DOMAIN 1 +#define NR_H3_A57_OPP 5 +#define NR_M3_A57_OPP 6 +#define NR_H3_A53_OPP 3 +#define NR_M3_A53_OPP 4 +#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) +#define DIV_ROUND(n, d) (((n) + (d) / 2) / (d)) + +/* CPG base address */ +#define CPG_PLLECR 0x00D0 + +/* Implementation for customized clocks (Z-clk, Z2-clk, PLL0-clk) for CPUFreq */ +#define CPG_PLLECR_PLL0ST BIT(8) +#define CPG_PLLECR_PLL2ST BIT(10) + +/* Define for PLL0 clk driver */ +#define CPG_PLLCR_STC_MASK 0x7f000000 +#define CPG_PLLCR_STC_SHIFT 24 + +/* Modify for Z-clock and Z2-clock + * + * Traits of this clock: + * prepare - clk_prepare only ensures that parents are prepared + * enable - clk_enable only ensures that parents are enabled + * rate - rate is adjustable. clk->rate = parent->rate * mult / 32 + * parent - fixed parent. No clk_set_parent support + */ +#define CPG_FRQCRC_ZFC_MASK (0x1f << 8) +#define CPG_FRQCRC_ZFC_SHIFT 8 +#define CPG_FRQCRC_Z2FC_MASK 0x1f +#define Z_CLK_MAX_THRESHOLD 1500000000U +#define Z2_CLK_MAX_THRESHOLD 1200000000U + + +extern int32_t rcar_iic_dvfs_receive(uint8_t slave, uint8_t reg, uint8_t *data); +extern int32_t rcar_iic_dvfs_send(uint8_t slave, uint8_t regr, uint8_t data); + +/*! + * @endcond + */ + +/*! + * @} + */ + +/*! + * @} + */ + +#endif /* MOD_RCAR_CLOCK_H */ diff --git a/product/rcar/module/rcar_clock/src/Makefile b/product/rcar/module/rcar_clock/src/Makefile new file mode 100644 index 000000000..b773cf6b1 --- /dev/null +++ b/product/rcar/module/rcar_clock/src/Makefile @@ -0,0 +1,11 @@ +# +# Renesas SCP/MCP Software +# Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +BS_LIB_NAME := Clock HAL +BS_LIB_SOURCES := mod_rcar_clock.c + +include $(BS_DIR)/lib.mk diff --git a/product/rcar/module/rcar_clock/src/mod_rcar_clock.c b/product/rcar/module/rcar_clock/src/mod_rcar_clock.c new file mode 100644 index 000000000..74f45bac8 --- /dev/null +++ b/product/rcar/module/rcar_clock/src/mod_rcar_clock.c @@ -0,0 +1,869 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct rcar_clock_ctx module_ctx; +static int current_a57_opp_limit = 0; +static const struct op_points *current_a57_opp_table; +static int current_a53_opp_limit = 0; +static const struct op_points *current_a53_opp_table; +static int dvfs_inited = 0; + +/* TODO These should be taken from avs_driver.c */ +#define EFUSE_AVS0 (0U) +#define EFUSE_AVS_NUM (8U) +static uint32_t efuse_avs = EFUSE_AVS0; + +/* Describe OPPs exactly how they are described in the device-tree */ +static const + struct op_points rcar_h3_a57_op_points[EFUSE_AVS_NUM][NR_H3_A57_OPP] = { + { + { 500000000, 830000, }, + { 1000000000, 830000, }, + { 1500000000, 830000, }, + { 1600000000, 900000, }, + { 1700000000, 960000, }, + }, + { + { 500000000, 820000, }, + { 1000000000, 820000, }, + { 1500000000, 820000, }, + { 1600000000, 890000, }, + { 1700000000, 950000, }, + }, + { + { 500000000, 810000, }, + { 1000000000, 810000, }, + { 1500000000, 810000, }, + { 1600000000, 880000, }, + { 1700000000, 930000, }, + }, + { + { 500000000, 800000, }, + { 1000000000, 800000, }, + { 1500000000, 800000, }, + { 1600000000, 870000, }, + { 1700000000, 910000, }, + }, + { + { 500000000, 790000, }, + { 1000000000, 790000, }, + { 1500000000, 790000, }, + { 1600000000, 860000, }, + { 1700000000, 890000, }, + }, + { + { 500000000, 780000, }, + { 1000000000, 780000, }, + { 1500000000, 780000, }, + { 1600000000, 850000, }, + { 1700000000, 880000, }, + }, + { + { 500000000, 770000, }, + { 1000000000, 770000, }, + { 1500000000, 770000, }, + { 1600000000, 840000, }, + { 1700000000, 870000, }, + }, + { + { 500000000, 760000, }, + { 1000000000, 760000, }, + { 1500000000, 760000, }, + { 1600000000, 830000, }, + { 1700000000, 860000, }, + }, +}; + +static const + struct op_points rcar_h3_a53_op_points[NR_H3_A53_OPP] = { + { 800000000, 820000, }, + { 1000000000, 820000, }, + { 1200000000, 820000, }, +}; + +static const + struct op_points rcar_m3_a57_op_points[EFUSE_AVS_NUM][NR_M3_A57_OPP] = { + { + { 500000000, 830000, }, + { 1000000000, 830000, }, + { 1500000000, 830000, }, + { 1600000000, 900000, }, + { 1700000000, 900000, }, + { 1800000000, 960000, }, + }, + { + { 500000000, 820000, }, + { 1000000000, 820000, }, + { 1500000000, 820000, }, + { 1600000000, 890000, }, + { 1700000000, 890000, }, + { 1800000000, 950000, }, + }, + { + { 500000000, 810000, }, + { 1000000000, 810000, }, + { 1500000000, 810000, }, + { 1600000000, 880000, }, + { 1700000000, 880000, }, + { 1800000000, 930000, }, + }, + { + { 500000000, 800000, }, + { 1000000000, 800000, }, + { 1500000000, 800000, }, + { 1600000000, 870000, }, + { 1700000000, 870000, }, + { 1800000000, 910000, }, + }, + + { + { 500000000, 790000, }, + { 1000000000, 790000, }, + { 1500000000, 790000, }, + { 1600000000, 860000, }, + { 1700000000, 860000, }, + { 1800000000, 890000, }, + }, + { + { 500000000, 780000, }, + { 1000000000, 780000, }, + { 1500000000, 780000, }, + { 1600000000, 850000, }, + { 1700000000, 850000, }, + { 1800000000, 880000, }, + }, + { + { 500000000, 770000, }, + { 1000000000, 770000, }, + { 1500000000, 770000, }, + { 1600000000, 840000, }, + { 1700000000, 840000, }, + { 1800000000, 870000, }, + }, + { + { 500000000, 760000, }, + { 1000000000, 760000, }, + { 1500000000, 760000, }, + { 1600000000, 830000, }, + { 1700000000, 830000, }, + { 1800000000, 860000, }, + }, +}; + +static const + struct op_points rcar_m3_a53_op_points[NR_M3_A53_OPP] = { + { 800000000, 820000, }, + { 1000000000, 820000, }, + { 1200000000, 820000, }, + { 1300000000, 820000, }, +}; + +uint32_t rcar_dvfs_get_opp_voltage(int domain, int oppnr) +{ + if (domain == A57_DOMAIN) { + if (oppnr < 0 || oppnr >= current_a57_opp_limit) + return ~0; + + /* Protocol requires voltage to be in mV */ + return current_a57_opp_table[oppnr].volt / 1000; + } else if (domain == A53_DOMAIN) { + if (oppnr < 0 || oppnr >= current_a53_opp_limit) + return ~0; + + /* Protocol requires voltage to be in mV */ + return current_a53_opp_table[oppnr].volt / 1000; + } + + return ~0; +} + +uint32_t rcar_dvfs_get_opp_frequency(int domain, int oppnr) +{ + if (domain == A57_DOMAIN) { + if (oppnr < 0 || oppnr >= current_a57_opp_limit) + return ~0; + + /* Protocol requires frequency to be in Hz */ + return current_a57_opp_table[oppnr].freq; + } else if (domain == A53_DOMAIN) { + if (oppnr < 0 || oppnr >= current_a53_opp_limit) + return ~0; + + /* Protocol requires frequency to be in Hz */ + return current_a53_opp_table[oppnr].freq; + } + + return ~0; +} + +static unsigned long pll_clk_parent_rate(void) +{ + static const unsigned long extal_freq[] = { + 16660000U, /* MD14_MD13_TYPE_0 */ + 20000000U, /* MD14_MD13_TYPE_1 */ + 25000000U, /* MD14_MD13_TYPE_2 */ + 33330000U, /* MD14_MD13_TYPE_3 */ + }; + unsigned long rate; + int idx; + + idx = (mmio_read_32(RCAR_MODEMR) & MODEMR_BOOT_PLL_MASK) + >> MODEMR_BOOT_PLL_SHIFT; + + rate = extal_freq[idx]; + /* Divider setting of EXTAL input is 1/2 when MD14=1 MD13=1 */ + if (idx == MD14_MD13_TYPE_3) + rate = DIV_ROUND(rate, 2); + + return rate; +} + +static unsigned long pll0_clk_round_rate(unsigned long rate) +{ + unsigned long parent_rate = pll_clk_parent_rate(); + unsigned int mult; + + if (rate < Z_CLK_MAX_THRESHOLD) + rate = Z_CLK_MAX_THRESHOLD; /* Set lowest value: 1.5GHz */ + + mult = DIV_ROUND(rate, parent_rate); + mult = max(mult, 90U); /* Lowest value is 1.5GHz (stc == 90) */ + mult = min(mult, 108U); + + rate = parent_rate * mult; + /* Round to closest value at 100MHz unit */ + rate = 100000000 * DIV_ROUND(rate, 100000000); + + return rate; +} + +static unsigned long pll0_clk_recalc_rate(void) +{ + unsigned long parent_rate = pll_clk_parent_rate(); + unsigned int val; + unsigned long rate; + + val = (mmio_read_32(CPG_PLL0CR) & CPG_PLLCR_STC_MASK) + >> CPG_PLLCR_STC_SHIFT; + + rate = parent_rate * (val + 1); + /* Round to closest value at 100MHz unit */ + rate = 100000000 * DIV_ROUND(rate, 100000000); + + return rate; +} + +static int pll0_clk_set_rate(unsigned long rate) +{ + unsigned long parent_rate = pll_clk_parent_rate(); + unsigned int stc_val; + uint32_t val; + + stc_val = DIV_ROUND(rate, parent_rate); + stc_val = max(stc_val, 90U); /* Lowest value is 1.5GHz (stc == 90) */ + stc_val = min(stc_val, 108U); + + stc_val -= 1; + val = mmio_read_32(CPG_PLL0CR); + val &= ~CPG_PLLCR_STC_MASK; + val |= stc_val << CPG_PLLCR_STC_SHIFT; + mmio_write_32(CPG_PLL0CR, val); + + while (!(mmio_read_32(CPG_BASE + CPG_PLLECR) & CPG_PLLECR_PLL0ST)) + continue; + + return 0; +} + +static unsigned long pll2_clk_round_rate(unsigned long rate) +{ + unsigned long parent_rate = pll_clk_parent_rate(); + unsigned int mult; + + if (rate < Z2_CLK_MAX_THRESHOLD) + rate = Z2_CLK_MAX_THRESHOLD; /* Set lowest value: 1.2GHz */ + + mult = DIV_ROUND(rate, parent_rate); + mult = max(mult, 72U); /* Lowest value is 1.2GHz (stc == 72) */ + mult = min(mult, 78U); + + rate = parent_rate * mult; + /* Round to closest value at 100MHz unit */ + rate = 100000000 * DIV_ROUND(rate, 100000000); + + return rate; +} + +static unsigned long pll2_clk_recalc_rate(void) +{ + unsigned long parent_rate = pll_clk_parent_rate(); + unsigned int val; + unsigned long rate; + + val = (mmio_read_32(CPG_PLL2CR) & CPG_PLLCR_STC_MASK) + >> CPG_PLLCR_STC_SHIFT; + + rate = parent_rate * (val + 1); + /* Round to closest value at 100MHz unit */ + rate = 100000000 * DIV_ROUND(rate, 100000000); + + return rate; +} + +static int pll2_clk_set_rate(unsigned long rate) +{ + unsigned long parent_rate = pll_clk_parent_rate(); + unsigned int stc_val; + uint32_t val; + + stc_val = DIV_ROUND(rate, parent_rate); + stc_val = max(stc_val, 72U); /* Lowest value is 1.2GHz (stc == 72) */ + stc_val = min(stc_val, 78U); + + stc_val -= 1; + val = mmio_read_32(CPG_PLL2CR); + val &= ~CPG_PLLCR_STC_MASK; + val |= stc_val << CPG_PLLCR_STC_SHIFT; + mmio_write_32(CPG_PLL2CR, val); + + while (!(mmio_read_32(CPG_BASE + CPG_PLLECR) & CPG_PLLECR_PLL2ST)) + continue; + + return 0; +} + +static unsigned long z_clk_round_rate( + unsigned long rate, unsigned long *parent_rate) +{ + unsigned long prate = *parent_rate; + unsigned int mult; + + if (!prate) + prate = 1; + + if (rate <= Z_CLK_MAX_THRESHOLD) { /* Focus on changing z-clock */ + prate = Z_CLK_MAX_THRESHOLD; /* Set parent to: 1.5GHz */ + mult = DIV_ROUND(rate * 32, prate); + } else { + /* Focus on changing parent. Fix z-clock divider is 32/32 */ + mult = 32; + } + mult = max(mult, 1U); + mult = min(mult, 32U); + + /* Re-calculate the parent_rate to propagate new rate for it */ + prate = DIV_ROUND(rate * 32, mult); + prate = 100000000 * DIV_ROUND(prate, 100000000); + rate = 100000000 * DIV_ROUND(prate / 32 * mult, 100000000); + *parent_rate = prate; + + return rate; +} + +static unsigned long z_clk_recalc_rate(unsigned long parent_rate) +{ + unsigned int mult; + unsigned int val; + unsigned long rate; + + val = (mmio_read_32(CPG_FRQCRC) & CPG_FRQCRC_ZFC_MASK) + >> CPG_FRQCRC_ZFC_SHIFT; + mult = 32 - val; + + rate = DIV_ROUND(parent_rate * mult, 32); + /* Round to closest value at 100MHz unit */ + rate = 100000000 * DIV_ROUND(rate, 100000000); + + return rate; +} + +static int z_clk_set_rate(unsigned long rate, unsigned long parent_rate) +{ + unsigned int mult; + uint32_t val, kick; + unsigned int i; + + if (rate <= Z_CLK_MAX_THRESHOLD) { /* Focus on changing z-clock */ + parent_rate = Z_CLK_MAX_THRESHOLD; /* Set parent to: 1.5GHz */ + mult = DIV_ROUND(rate * 32, parent_rate); + } else { + mult = 32; + } + mult = max(mult, 1U); + mult = min(mult, 32U); + + if (mmio_read_32(CPG_FRQCRB) & CPG_FRQCRB_KICK) + return -1; + + val = mmio_read_32(CPG_FRQCRC); + val &= ~CPG_FRQCRC_ZFC_MASK; + val |= (32 - mult) << CPG_FRQCRC_ZFC_SHIFT; + mmio_write_32(CPG_FRQCRC, val); + + /* + * Set KICK bit in FRQCRB to update hardware setting and wait for + * clock change completion. + */ + kick = mmio_read_32(CPG_FRQCRB); + kick |= CPG_FRQCRB_KICK; + mmio_write_32(CPG_FRQCRB, kick); + + /* + * Note: There is no HW information about the worst case latency. + * + * Using experimental measurements, it seems that no more than + * ~10 iterations are needed, independently of the CPU rate. + * Since this value might be dependent of external xtal rate, pll1 + * rate or even the other emulation clocks rate, use 1000 as a + * "super" safe value. + */ + for (i = 1000; i; i--) { + if (!(mmio_read_32(CPG_FRQCRB) & CPG_FRQCRB_KICK)){ + return 0; + } + + /*cpu_relax();*/ + } + + return -1; +} + +static unsigned long z2_clk_round_rate( + unsigned long rate, unsigned long *parent_rate) +{ + unsigned long prate = *parent_rate; + unsigned int mult; + + if (!prate) + prate = 1; + + if (rate <= Z2_CLK_MAX_THRESHOLD) { /* Focus on changing z2-clock */ + prate = Z2_CLK_MAX_THRESHOLD; /* Set parent to: 1.2GHz */ + mult = DIV_ROUND(rate * 32, prate); + } else { + /* Focus on changing parent. Fix z2-clock divider is 32/32 */ + mult = 32; + } + mult = max(mult, 1U); + mult = min(mult, 32U); + + /* Re-calculate the parent_rate to propagate new rate for it */ + prate = DIV_ROUND(rate * 32, mult); + prate = 100000000 * DIV_ROUND(prate, 100000000); + rate = 100000000 * DIV_ROUND(prate / 32 * mult, 100000000); + *parent_rate = prate; + + return rate; +} + +static unsigned long z2_clk_recalc_rate(unsigned long parent_rate) +{ + unsigned int mult; + unsigned int val; + unsigned long rate; + + val = mmio_read_32(CPG_FRQCRC) & CPG_FRQCRC_Z2FC_MASK; + mult = 32 - val; + + rate = DIV_ROUND(parent_rate * mult, 32); + /* Round to closest value at 100MHz unit */ + rate = 100000000 * DIV_ROUND(rate, 100000000); + + return rate; +} + +static int z2_clk_set_rate(unsigned long rate, unsigned long parent_rate) +{ + unsigned int mult; + uint32_t val, kick; + unsigned int i; + + if (rate <= Z2_CLK_MAX_THRESHOLD) { /* Focus on changing z2-clock */ + parent_rate = Z2_CLK_MAX_THRESHOLD; /* Set parent to: 1.2GHz */ + mult = DIV_ROUND(rate * 32, parent_rate); + } else { + mult = 32; + } + mult = max(mult, 1U); + mult = min(mult, 32U); + + if (mmio_read_32(CPG_FRQCRB) & CPG_FRQCRB_KICK) + return -1; + + val = mmio_read_32(CPG_FRQCRC); + val &= ~CPG_FRQCRC_Z2FC_MASK; + val |= 32 - mult; + mmio_write_32(CPG_FRQCRC, val); + + /* + * Set KICK bit in FRQCRB to update hardware setting and wait for + * clock change completion. + */ + kick = mmio_read_32(CPG_FRQCRB); + kick |= CPG_FRQCRB_KICK; + mmio_write_32(CPG_FRQCRB, kick); + + /* + * Note: There is no HW information about the worst case latency. + * + * Using experimental measurements, it seems that no more than + * ~10 iterations are needed, independently of the CPU rate. + * Since this value might be dependent of external xtal rate, pll1 + * rate or even the other emulation clocks rate, use 1000 as a + * "super" safe value. + */ + for (i = 1000; i; i--) { + if (!(mmio_read_32(CPG_FRQCRB) & CPG_FRQCRB_KICK)){ + return 0; + } + + /*cpu_relax();*/ + } + + return -1; +} + +#if 0 +static int set_voltage(unsigned long volt) +{ +/* TODO : Not supported because it conflicts with I2C control from the kernel */ + uint8_t val; + int ret; + + if (volt < BD9571MWV_MIN_MV * 1000 || volt > BD9571MWV_MAX_MV * 1000) + return -1; + + val = DIV_ROUND(volt, BD9571MWV_STEP_MV * 1000); + val &= REG_DATA_DVFS_SetVID_MASK; + + ret = rcar_iic_dvfs_send(SLAVE_ADDR_PMIC, REG_ADDR_DVFS_SetVID, val); + if (ret) { + return ret; + } + return 0; +} +#endif + +#if 0 +static unsigned long get_voltage(void) +{ + uint8_t val; + unsigned long volt; + int ret; + + ret = rcar_iic_dvfs_receive(SLAVE_ADDR_PMIC, REG_ADDR_DVFS_SetVID, &val); + if (ret) { + return ret; + } + + val &= REG_DATA_DVFS_SetVID_MASK; + volt = (unsigned long)val * BD9571MWV_STEP_MV * 1000; + + return volt; +} +#endif + +static const struct op_points *find_opp(int domain, unsigned long freq) +{ + int i; + + if (domain == A57_DOMAIN) { + for (i = 0; i < current_a57_opp_limit; i++) { + if (current_a57_opp_table[i].freq == freq) + return ¤t_a57_opp_table[i]; + } + } else if (domain == A53_DOMAIN) { + for (i = 0; i < current_a53_opp_limit; i++) { + if (current_a53_opp_table[i].freq == freq) + return ¤t_a53_opp_table[i]; + } + } + + return NULL; +} + + +static int set_a57_opp(unsigned long target_freq) +{ + unsigned long freq, old_freq, prate, old_prate; + const struct op_points *opp; + int ret; + + prate = 0; + freq = z_clk_round_rate(target_freq, &prate); + + old_prate = pll0_clk_recalc_rate(); + old_freq = z_clk_recalc_rate(old_prate); + + /* Return early if nothing to do */ + if (old_freq == freq) { + return 0; + } + + opp = find_opp(A57_DOMAIN, freq); + if (!opp) { + return -1; + } + + /* Scaling up? Scale voltage before frequency */ + prate = pll0_clk_round_rate(prate); + if (old_prate != prate) + pll0_clk_set_rate(prate); + + ret = z_clk_set_rate(freq, prate); + if (ret) { + /* Restore voltage */ + if (old_prate != prate) + pll0_clk_set_rate(old_prate); + } + + return ret; +} + +static int set_a53_opp(unsigned long target_freq) +{ + unsigned long freq, old_freq, prate, old_prate; + const struct op_points *opp; + int ret=0; + + prate = 0; + freq = z2_clk_round_rate(target_freq, &prate); + + old_prate = pll2_clk_recalc_rate(); + old_freq = z2_clk_recalc_rate(old_prate); + + /* Return early if nothing to do */ + if (old_freq == freq) { + return 0; + } + + + opp = find_opp(A53_DOMAIN, freq); + if (!opp) { + return -1; + } + + prate = pll2_clk_round_rate(prate); + if (old_prate != prate) + pll2_clk_set_rate(prate); + + ret = z2_clk_set_rate(freq, prate); + + return ret; +} + +int rcar_dvfs_get_nr_opp(int domain) +{ + if (domain == A57_DOMAIN) + return current_a57_opp_limit; + else if (domain == A53_DOMAIN) + return current_a53_opp_limit; + + return 0; +} + +int rcar_dvfs_opp_init(void) +{ + uint32_t product; + + if (dvfs_inited) + return 0; + + product = mmio_read_32(RCAR_PRR) & RCAR_PRODUCT_MASK; + + if (product == RCAR_PRODUCT_H3) { + current_a57_opp_limit = ARRAY_SIZE(rcar_h3_a57_op_points[efuse_avs]); + current_a57_opp_table = &rcar_h3_a57_op_points[efuse_avs][0]; + + current_a53_opp_limit = ARRAY_SIZE(rcar_h3_a53_op_points); + current_a53_opp_table = &rcar_h3_a53_op_points[0]; + } else if (product == RCAR_PRODUCT_M3) { + current_a57_opp_limit = ARRAY_SIZE(rcar_m3_a57_op_points[efuse_avs]); + current_a57_opp_table = &rcar_m3_a57_op_points[efuse_avs][0]; + + current_a53_opp_limit = ARRAY_SIZE(rcar_m3_a53_op_points); + current_a53_opp_table = &rcar_m3_a53_op_points[0]; + } else + return -1; + + dvfs_inited = 1; + + return 0; +} + +/* + * Static helper functions + */ + +static int do_rcar_clock_set_rate(fwk_id_t dev_id, uint64_t rate, + enum mod_clock_round_mode round_mode) +{ + struct rcar_clock_dev_ctx *ctx; + int ret=0; + + ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id); + + /* base clock */ + switch (ctx->config->rate_table->divider_reg) { + case MOD_RCAR_CLOCK_A57_DIVIDER_DIV_EXT: + ret = set_a57_opp(rate); + break; + case MOD_RCAR_CLOCK_A53_DIVIDER_DIV_EXT: + ret = set_a53_opp(rate); + break; + default: + return FWK_E_SUPPORT; + } + ctx->current_rate = rate; + return ret; +} + +/* + * Clock driver API functions + */ + +static int rcar_clock_set_rate(fwk_id_t dev_id, uint64_t rate, + enum mod_clock_round_mode round_mode) +{ + struct rcar_clock_dev_ctx *ctx; + + ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id); + + if (!ctx->initialized) + return FWK_E_INIT; + + if (ctx->current_state == MOD_CLOCK_STATE_STOPPED) + return FWK_E_PWRSTATE; + + return do_rcar_clock_set_rate(dev_id, rate, round_mode); +} + +static int rcar_clock_get_rate(fwk_id_t dev_id, uint64_t *rate) +{ + struct rcar_clock_dev_ctx *ctx; + + if (rate == NULL) + return FWK_E_PARAM; + + ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id); + *rate = ctx->current_rate; + + return FWK_SUCCESS; +} + +static const struct mod_rcar_clock_drv_api api_clock = { + .set_rate = rcar_clock_set_rate, + .get_rate = rcar_clock_get_rate, +}; + +/* + * Framework handler functions + */ + +static int rcar_clock_init(fwk_id_t module_id, unsigned int element_count, + const void *data) +{ + struct mod_ext_clock_rate *ext; + module_ctx.dev_count = element_count; + + if (element_count == 0) + return FWK_SUCCESS; + + module_ctx.dev_ctx_table = fwk_mm_calloc(element_count, + sizeof(struct rcar_clock_dev_ctx)); + if (module_ctx.dev_ctx_table == NULL) + return FWK_E_NOMEM; + + ext = (struct mod_ext_clock_rate *)data; + module_ctx.extal_clk = ext->ext_clk_rate; + + return FWK_SUCCESS; +} + +static int rcar_clock_element_init(fwk_id_t element_id, + unsigned int sub_element_count, + const void *data) +{ + unsigned int i = 0; + uint64_t current_rate; + uint64_t last_rate = 0; + struct rcar_clock_dev_ctx *ctx; + const struct mod_rcar_clock_dev_config *dev_config = data; + + if (!fwk_module_is_valid_element_id(element_id)) + return FWK_E_PARAM; + + ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(element_id); + + /* Verify that the rate entries in the device's lookup table are ordered */ + while (i < dev_config->rate_count) { + current_rate = dev_config->rate_table[i].rate; + + /* The rate entries must be in ascending order */ + if (current_rate < last_rate) + return FWK_E_DATA; + + last_rate = current_rate; + i++; + } + + ctx->config = dev_config; + + if (ctx->config->defer_initialization) + return FWK_SUCCESS; + + ctx->current_state = MOD_CLOCK_STATE_RUNNING; + ctx->initialized = true; + + /* + * Clock devices that are members of a clock group must skip initialization + * at this time since they will be set to a specific rate by the CSS Clock + * driver during the start stage or in response to a notification. + */ + if (ctx->config->is_group_member) + return FWK_SUCCESS; + + return do_rcar_clock_set_rate( + element_id, dev_config->initial_rate, MOD_CLOCK_ROUND_MODE_NONE); +} + +static int rcar_clock_process_bind_request(fwk_id_t source_id, + fwk_id_t target_id, fwk_id_t api_id, + const void **api) +{ + *api = &api_clock; + return FWK_SUCCESS; +} +static int rcar_clock_start(fwk_id_t id) +{ + rcar_dvfs_opp_init(); + return FWK_SUCCESS; +} + +const struct fwk_module module_rcar_clock = { + .name = "RCAR Clock Driver", + .type = FWK_MODULE_TYPE_DRIVER, + .api_count = MOD_RCAR_CLOCK_API_COUNT, + .event_count = 0, + .init = rcar_clock_init, + .element_init = rcar_clock_element_init, + .process_bind_request = rcar_clock_process_bind_request, + .start = rcar_clock_start, +}; diff --git a/product/rcar/scp_ramfw/config_rcar_clock.c b/product/rcar/scp_ramfw/config_rcar_clock.c new file mode 100644 index 000000000..47dd9815a --- /dev/null +++ b/product/rcar/scp_ramfw/config_rcar_clock.c @@ -0,0 +1,150 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include + +/* + * Rate lookup tables + */ + +static struct mod_rcar_clock_rate rate_table_cpu_a53[] = { + { + .rate = 800 * FWK_MHZ, + .source = MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL2, + .divider_reg = MOD_RCAR_CLOCK_A53_DIVIDER_DIV_EXT, + .divider = 1, /* Rate adjusted via CPU PLL */ + }, + { + .rate = 1000 * FWK_MHZ, + .source = MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL2, + .divider_reg = MOD_RCAR_CLOCK_A53_DIVIDER_DIV_EXT, + .divider = 1, /* Rate adjusted via CPU PLL */ + }, + { + .rate = 1200 * FWK_MHZ, + .source = MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL2, + .divider_reg = MOD_RCAR_CLOCK_A53_DIVIDER_DIV_EXT, + .divider = 1, /* Rate adjusted via CPU PLL */ + }, +}; + +static struct mod_rcar_clock_rate rate_table_cpu_a57[] = { + { + .rate = 500 * FWK_MHZ, + .source = MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL0, + .divider_reg = MOD_RCAR_CLOCK_A57_DIVIDER_DIV_EXT, + .divider = 1, /* Rate adjusted via CPU PLL */ + }, + { + .rate = 1000 * FWK_MHZ, + .source = MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL0, + .divider_reg = MOD_RCAR_CLOCK_A57_DIVIDER_DIV_EXT, + .divider = 1, /* Rate adjusted via CPU PLL */ + }, + { + .rate = 1500 * FWK_MHZ, + .source = MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL0, + .divider_reg = MOD_RCAR_CLOCK_A57_DIVIDER_DIV_EXT, + .divider = 1, /* Rate adjusted via CPU PLL */ + }, + { + .rate = 1600 * FWK_MHZ, + .source = MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL0, + .divider_reg = MOD_RCAR_CLOCK_A57_DIVIDER_DIV_EXT, + .divider = 1, /* Rate adjusted via CPU PLL */ + }, + { + .rate = 1700 * FWK_MHZ, + .source = MOD_RCAR_CLOCK_CLUSCLK_SOURCE_PLL0, + .divider_reg = MOD_RCAR_CLOCK_A57_DIVIDER_DIV_EXT, + .divider = 1, /* Rate adjusted via CPU PLL */ + }, +}; + +static const struct fwk_element rcar_clock_element_table[] = { + /* + * A53 CPUS + */ + { + .name = "CLUS0_CPU0", + .data = &((struct mod_rcar_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_CLUSTER, + .is_group_member = false, + .rate_table = rate_table_cpu_a53, + .rate_count = FWK_ARRAY_SIZE(rate_table_cpu_a53), + .initial_rate = 1200 * FWK_MHZ, + .defer_initialization = false, + }), + }, + /* + * A57 CPUS + */ + { + .name = "CLUS0_CPU4", + .data = &((struct mod_rcar_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_CLUSTER, + .is_group_member = false, + .rate_table = rate_table_cpu_a57, + .rate_count = FWK_ARRAY_SIZE(rate_table_cpu_a57), + .initial_rate = 1500 * FWK_MHZ, + .defer_initialization = false, + }), + }, + /* + * VPU + */ + { + .name = "VPU", + .data = &((struct mod_rcar_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_MULTI_SOURCE, + .is_group_member = true, + .initial_rate = 600 * FWK_MHZ, + .defer_initialization = false, + }), + }, + /* + * DPU + */ + { + .name = "ACLKDP", + .data = &((struct mod_rcar_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_MULTI_SOURCE, + .is_group_member = true, + .initial_rate = (CLOCK_RATE_SYSPLLCLK / 3), + .defer_initialization = false, + }), + }, + { + .name = "DPU_M0", + .data = &((struct mod_rcar_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_MULTI_SOURCE, + .is_group_member = true, + .initial_rate = 260 * FWK_MHZ, + .defer_initialization = false, + }), + }, + { }, /* Termination description. */ +}; + +static const struct fwk_element *rcar_clock_get_element_table + (fwk_id_t module_id) +{ + return rcar_clock_element_table; +} + + +struct fwk_module_config config_rcar_clock = { + .elements = FWK_MODULE_DYNAMIC_ELEMENTS(rcar_clock_get_element_table), + .data = &((struct mod_ext_clock_rate) { + .ext_clk_rate = PLL_BASE_CLOCK, + }), +}; -- GitLab From 515da73e309744a5b51b3d6396afbca00ab6dc5f Mon Sep 17 00:00:00 2001 From: Nicolas Royer Date: Sun, 27 Sep 2020 17:37:49 +0200 Subject: [PATCH 05/19] rcar/module: add rcar dvfs module and config data Signed-off-by: Tsutomu Muroya Signed-off-by: Nicolas Royer --- .../rcar/module/rcar_dvfs/include/mod_dvfs.h | 17 ++ .../module/rcar_dvfs/include/mod_rcar_dvfs.h | 272 ++++++++++++++++++ product/rcar/module/rcar_dvfs/src/Makefile | 15 + .../rcar_dvfs/src/mod_dvfs_domain_api.c | 265 +++++++++++++++++ .../src/mod_dvfs_domain_api_private.h | 16 ++ .../module/rcar_dvfs/src/mod_dvfs_event.c | 47 +++ .../rcar_dvfs/src/mod_dvfs_event_private.h | 18 ++ .../module/rcar_dvfs/src/mod_dvfs_module.c | 238 +++++++++++++++ .../rcar_dvfs/src/mod_dvfs_module_private.h | 41 +++ .../module/rcar_dvfs/src/mod_dvfs_private.h | 16 ++ .../rcar/module/rcar_dvfs/src/mod_dvfs_util.c | 71 +++++ .../rcar_dvfs/src/mod_dvfs_util_private.h | 22 ++ product/rcar/scp_ramfw/config_rcar_dvfs.c | 89 ++++++ product/rcar/scp_ramfw/config_rcar_dvfs.h | 17 ++ 14 files changed, 1144 insertions(+) create mode 100644 product/rcar/module/rcar_dvfs/include/mod_dvfs.h create mode 100644 product/rcar/module/rcar_dvfs/include/mod_rcar_dvfs.h create mode 100644 product/rcar/module/rcar_dvfs/src/Makefile create mode 100644 product/rcar/module/rcar_dvfs/src/mod_dvfs_domain_api.c create mode 100644 product/rcar/module/rcar_dvfs/src/mod_dvfs_domain_api_private.h create mode 100644 product/rcar/module/rcar_dvfs/src/mod_dvfs_event.c create mode 100644 product/rcar/module/rcar_dvfs/src/mod_dvfs_event_private.h create mode 100644 product/rcar/module/rcar_dvfs/src/mod_dvfs_module.c create mode 100644 product/rcar/module/rcar_dvfs/src/mod_dvfs_module_private.h create mode 100644 product/rcar/module/rcar_dvfs/src/mod_dvfs_private.h create mode 100644 product/rcar/module/rcar_dvfs/src/mod_dvfs_util.c create mode 100644 product/rcar/module/rcar_dvfs/src/mod_dvfs_util_private.h create mode 100644 product/rcar/scp_ramfw/config_rcar_dvfs.c create mode 100644 product/rcar/scp_ramfw/config_rcar_dvfs.h diff --git a/product/rcar/module/rcar_dvfs/include/mod_dvfs.h b/product/rcar/module/rcar_dvfs/include/mod_dvfs.h new file mode 100644 index 000000000..ffb0a421a --- /dev/null +++ b/product/rcar/module/rcar_dvfs/include/mod_dvfs.h @@ -0,0 +1,17 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_DVFS_H +#define MOD_DVFS_H + +#define FWK_MODULE_IDX_DVFS FWK_MODULE_IDX_RCAR_DVFS +#define fwk_module_id_dvfs fwk_module_id_rcar_dvfs + +#include "mod_rcar_dvfs.h" + + +#endif /* MOD_DVFS_H */ diff --git a/product/rcar/module/rcar_dvfs/include/mod_rcar_dvfs.h b/product/rcar/module/rcar_dvfs/include/mod_rcar_dvfs.h new file mode 100644 index 000000000..34509c177 --- /dev/null +++ b/product/rcar/module/rcar_dvfs/include/mod_rcar_dvfs.h @@ -0,0 +1,272 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * Copyright (c) 2017-2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_RCAR_DVFS_H +#define MOD_RCAR_DVFS_H + +#include +#include +#include +#include + +/*! + * \ingroup GroupRCARModule RCAR Product Modules + * \defgroup GroupRCARDvfs Dynamic Voltage and Frequency Scaling (DVFS) + * \{ + */ + +/*! + * \defgroup GroupRCARDvfsTypes Types + * \{ + */ + +/*! + * \brief Frequency limits. + */ +struct mod_dvfs_frequency_limits { + uint64_t minimum; /*!< Minimum permitted rate */ + uint64_t maximum; /*!< Maximum permitted rate */ +}; + +/*! + * \brief Operating Performance Point (OPP). + */ +struct mod_dvfs_opp { + uint64_t voltage; /*!< Power supply voltage in millivolts (mV) */ + uint64_t frequency; /*!< Clock rate in Hertz (Hz) */ +}; + +/*! + * \} + */ + +/*! + * \defgroup GroupRCARDvfsConfig Configuration + * \{ + */ + +/*! + * \brief Domain configuration. + */ +struct mod_dvfs_domain_config { + /*! + * \brief Power supply identifier. + * + * \warning This identifier must refer to an element of the \c psu module. + */ + fwk_id_t psu_id; + + /*! + * \brief Clock identifier. + * + * \warning This identifier must refer to an element of the \c clock module. + */ + fwk_id_t clock_id; + + /*! Worst-case transition latency in microseconds */ + uint16_t latency; + + /*! Sustained operating point index */ + size_t sustained_idx; + + /*! + * \brief Operating points. + * + * \note The frequencies of these operating points must be in ascending + * order. + */ + struct mod_dvfs_opp *opps; +}; + +/*! + * \} + */ + +/*! + * \defgroup GroupRCARDvfsApis APIs + * \{ + */ + +/*! + * \brief Domain API. + */ +struct mod_dvfs_domain_api { + /*! + * \brief Get the current operating point of a domain. + * + * \param domain_id Element identifier of the domain. + * \param [out] opp Current operating point. + */ + int (*get_current_opp)(fwk_id_t domain_id, struct mod_dvfs_opp *opp); + + /*! + * \brief Get the sustained operating point of a domain. + * + * \param domain_id Element identifier of the domain. + * \param [out] opp Sustained operating point. + */ + int (*get_sustained_opp)( + fwk_id_t domain_id, + struct mod_dvfs_opp *opp); + + /*! + * \brief Get an operating point from its index. + * + * \param domain_id Element identifier of the domain. + * \param n Index of the operating point to retrieve. + * \param [out] opp Requested operating point. + */ + int (*get_nth_opp)( + fwk_id_t domain_id, + size_t n, + struct mod_dvfs_opp *opp); + + /*! + * \brief Get the number of operating points of a domain. + * + * \param domain_id Element identifier of the domain. + * \param [out] opp_count Number of operating points. + */ + int (*get_opp_count)(fwk_id_t domain_id, size_t *opp_count); + + /*! + * \brief Get the worst-case transition latency of a domain. + * + * \param domain_id Element identifier of the domain. + * \param [out] latency Worst-case transition latency. + */ + int (*get_latency)(fwk_id_t domain_id, uint16_t *latency); + + /*! + * \brief Set the frequency of a domain. + * + * \param domain_id Element identifier of the domain. + * \param idx Index of the operating point to transition to. + */ + int (*set_frequency)(fwk_id_t domain_id, uint64_t frequency); + + /*! + * \brief Set the frequency of a domain. + * + * \note This function is asynchronous. + * + * \param domain_id Element identifier of the domain. + * \param idx Index of the operating point to transition to. + */ + int (*set_frequency_async)(fwk_id_t domain_id, uint64_t frequency); + + /*! + * \brief Get the frequency of a domain. + * + * \param domain_id Element identifier of the domain. + * \param [out] limits Current frequency limits. + */ + int (*get_frequency_limits)( + fwk_id_t domain_id, + struct mod_dvfs_frequency_limits *limits); + + /*! + * \brief Set the frequency of a domain. + * + * \param domain_id Element identifier of the domain. + * \param limits Pointer to the new limits. + */ + int (*set_frequency_limits)( + fwk_id_t domain_id, + const struct mod_dvfs_frequency_limits *limits); + + /*! + * \brief Set the frequency of a domain. + * + * \note This function is asynchronous. + * + * \param domain_id Element identifier of the domain. + * \param limits Pointer to the new limits. + */ + int (*set_frequency_limits_async)( + fwk_id_t domain_id, + const struct mod_dvfs_frequency_limits *limits); +}; + +/*! + * \} + */ + +/*! + * \defgroup GroupRCARDvfsEvents Events + * \{ + */ + +/*! + * \brief Set operating point event response parameters. + */ +struct mod_dvfs_event_params_set_frequency_response { + int status; /*!< Status of the request */ +}; + +/*! + * \brief Set limits event response parameters. + */ +struct mod_dvfs_event_params_set_frequency_limits_response { + int status; /*!< Status of the request */ +}; + +/*! + * \} + */ + +/*! + * \defgroup GroupRCARDvfsIds Identifiers + * \{ + */ + +/*! + * \brief API indices. + */ +enum mod_dvfs_api_idx { + MOD_DVFS_API_IDX_DVFS, /*!< API index for mod_dvfs_api_id_dvfs() */ + MOD_DVFS_API_IDX_COUNT /*!< Number of defined APIs */ +}; + +/*! Module API identifier */ +static const fwk_id_t mod_dvfs_api_id_dvfs = + FWK_ID_API_INIT(FWK_MODULE_IDX_DVFS, MOD_DVFS_API_IDX_DVFS); + +/*! + * \brief Event indices. + */ +enum mod_dvfs_event_idx { + /*! Event index for mod_dvfs_event_id_set_frequency() */ + MOD_DVFS_EVENT_IDX_SET_FREQUENCY, + + /*! Event index for mod_dvfs_event_id_set_frequency_limits() */ + MOD_DVFS_EVENT_IDX_SET_FREQUENCY_LIMITS, + + /*! Number of defined events */ + MOD_DVFS_EVENT_IDX_COUNT +}; + +/*! Set operating point event identifier */ +static const fwk_id_t mod_dvfs_event_id_set_frequency = + FWK_ID_EVENT_INIT(FWK_MODULE_IDX_DVFS, MOD_DVFS_EVENT_IDX_SET_FREQUENCY); + +/*! Set frequency limits event identifier */ +static const fwk_id_t mod_dvfs_event_id_set_frequency_limits = + FWK_ID_EVENT_INIT( + FWK_MODULE_IDX_DVFS, + MOD_DVFS_EVENT_IDX_SET_FREQUENCY_LIMITS); + +/*! + * \} + */ + +/*! + * \} + */ + +#endif /* MOD_RCAR_DVFS_H */ diff --git a/product/rcar/module/rcar_dvfs/src/Makefile b/product/rcar/module/rcar_dvfs/src/Makefile new file mode 100644 index 000000000..f85b56757 --- /dev/null +++ b/product/rcar/module/rcar_dvfs/src/Makefile @@ -0,0 +1,15 @@ +# +# Renesas SCP/MCP Software +# Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +BS_LIB_NAME := rcar_dvfs +BS_LIB_SOURCES := \ + mod_dvfs_domain_api.c \ + mod_dvfs_event.c \ + mod_dvfs_module.c \ + mod_dvfs_util.c \ + +include $(BS_DIR)/lib.mk diff --git a/product/rcar/module/rcar_dvfs/src/mod_dvfs_domain_api.c b/product/rcar/module/rcar_dvfs/src/mod_dvfs_domain_api.c new file mode 100644 index 000000000..c1107f3d7 --- /dev/null +++ b/product/rcar/module/rcar_dvfs/src/mod_dvfs_domain_api.c @@ -0,0 +1,265 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const struct mod_dvfs_opp *get_opp_for_values( + const struct mod_dvfs_domain_ctx *ctx, + uint64_t frequency, + uint64_t voltage) +{ + size_t opp_idx; + const struct mod_dvfs_opp *opp; + + /* A value of zero indicates the parameter should be ignored */ + assert((frequency != 0) || (voltage != 0)); + + for (opp_idx = 0; opp_idx < ctx->opp_count; opp_idx++) { + opp = &ctx->config->opps[opp_idx]; + + /* Only check the frequency if requested */ + if ((frequency != 0) && (opp->frequency != frequency)) + continue; + + /* Only check the voltage if requested */ + if ((voltage != 0) && (opp->voltage != voltage)) + continue; + + return opp; + } + + return NULL; +} + +static bool is_opp_within_limits( + const struct mod_dvfs_opp *opp, + const struct mod_dvfs_frequency_limits *limits) +{ + return (opp->frequency >= limits->minimum) && + (opp->frequency <= limits->maximum); +} + +static bool are_limits_valid( + const struct mod_dvfs_domain_ctx *ctx, + const struct mod_dvfs_frequency_limits *limits) +{ + if (limits->minimum > limits->maximum) + return false; + + if (get_opp_for_values(ctx, limits->minimum, 0) == NULL) + return false; + + if (get_opp_for_values(ctx, limits->maximum, 0) == NULL) + return false; + + return true; +} + +static const struct mod_dvfs_opp *adjust_opp_for_new_limits( + const struct mod_dvfs_domain_ctx *ctx, + const struct mod_dvfs_opp *opp, + const struct mod_dvfs_frequency_limits *limits) +{ + uint64_t needle; + + if (opp->frequency < limits->minimum) + needle = limits->minimum; + else if (opp->frequency > limits->maximum) + needle = limits->maximum; + else { + /* No transition necessary */ + return opp; + } + + return get_opp_for_values(ctx, needle, 0); +} + +static int api_get_current_opp(fwk_id_t domain_id, struct mod_dvfs_opp *opp) +{ + int status; + const struct mod_dvfs_domain_ctx *ctx; + + assert(opp != NULL); + + ctx = __mod_dvfs_get_valid_domain_ctx(domain_id); + if (ctx == NULL) + return FWK_E_PARAM; + + status = __mod_dvfs_get_current_opp(ctx, opp); + if (status != FWK_SUCCESS) + return status; + + return FWK_SUCCESS; +} + +int api_get_sustained_opp(fwk_id_t domain_id, struct mod_dvfs_opp *opp) +{ + const struct mod_dvfs_domain_ctx *ctx; + + assert(opp != NULL); + + ctx = __mod_dvfs_get_valid_domain_ctx(domain_id); + if (ctx == NULL) + return FWK_E_PARAM; + + *opp = ctx->config->opps[ctx->config->sustained_idx]; + + return FWK_SUCCESS; +} + +int api_get_nth_opp(fwk_id_t domain_id, + size_t n, + struct mod_dvfs_opp *opp) +{ + const struct mod_dvfs_domain_ctx *ctx; + + assert(opp != NULL); + + ctx = __mod_dvfs_get_valid_domain_ctx(domain_id); + if (ctx == NULL) + return FWK_E_PARAM; + + if (n >= ctx->opp_count) + return FWK_E_PARAM; + + *opp = ctx->config->opps[n]; + + return FWK_SUCCESS; +} + +static int api_get_opp_count(fwk_id_t domain_id, size_t *opp_count) +{ + const struct mod_dvfs_domain_ctx *ctx; + + assert(opp_count != NULL); + + ctx = __mod_dvfs_get_valid_domain_ctx(domain_id); + if (ctx == NULL) + return FWK_E_PARAM; + + *opp_count = ctx->opp_count; + + return FWK_SUCCESS; +} + +int api_get_latency(fwk_id_t domain_id, uint16_t *latency) +{ + const struct mod_dvfs_domain_ctx *ctx; + + assert(latency != NULL); + + ctx = __mod_dvfs_get_valid_domain_ctx(domain_id); + if (ctx == NULL) + return FWK_E_PARAM; + + *latency = ctx->config->latency; + + return FWK_SUCCESS; +} + +static int api_set_frequency(fwk_id_t domain_id, uint64_t frequency) +{ + int status; + const struct mod_dvfs_domain_ctx *ctx; + const struct mod_dvfs_opp *new_opp; + + ctx = __mod_dvfs_get_valid_domain_ctx(domain_id); + if (ctx == NULL) + return FWK_E_PARAM; + + /* Only accept frequencies that exist in the operating point table */ + new_opp = get_opp_for_values(ctx, frequency, 0); + if (new_opp == NULL) + return FWK_E_RANGE; + + if (!is_opp_within_limits(new_opp, &ctx->frequency_limits)) + return FWK_E_RANGE; + + status = __mod_dvfs_set_opp(ctx, new_opp); + if (status != FWK_SUCCESS) + return status; + + return FWK_SUCCESS; +} + +static int api_set_frequency_async(fwk_id_t domain_id, uint64_t frequency) +{ + return FWK_E_SUPPORT; +} + +int api_get_frequency_limits( + fwk_id_t domain_id, + struct mod_dvfs_frequency_limits *limits) +{ + const struct mod_dvfs_domain_ctx *ctx; + + ctx = __mod_dvfs_get_valid_domain_ctx(domain_id); + if (ctx == NULL) + return FWK_E_PARAM; + + *limits = ctx->frequency_limits; + + return FWK_SUCCESS; +} + +static int api_set_frequency_limits( + fwk_id_t domain_id, + const struct mod_dvfs_frequency_limits *limits) +{ + int status; + struct mod_dvfs_domain_ctx *ctx; + struct mod_dvfs_opp current_opp; + const struct mod_dvfs_opp *new_opp; + + ctx = __mod_dvfs_get_valid_domain_ctx(domain_id); + if (ctx == NULL) + return FWK_E_PARAM; + + if (!are_limits_valid(ctx, limits)) + return FWK_E_PARAM; + + status = __mod_dvfs_get_current_opp(ctx, ¤t_opp); + if (status != FWK_SUCCESS) + return status; + + new_opp = adjust_opp_for_new_limits(ctx, ¤t_opp, limits); + status = __mod_dvfs_set_opp(ctx, new_opp); + if (status != FWK_SUCCESS) + return status; + + ctx->frequency_limits = *limits; + + return FWK_SUCCESS; +} + +static int api_set_frequency_limits_async( + fwk_id_t domain_id, + const struct mod_dvfs_frequency_limits *limits) +{ + return FWK_E_SUPPORT; +} + +const struct mod_dvfs_domain_api __mod_dvfs_domain_api = { + .get_current_opp = api_get_current_opp, + .get_sustained_opp = api_get_sustained_opp, + .get_nth_opp = api_get_nth_opp, + .get_opp_count = api_get_opp_count, + .get_latency = api_get_latency, + .set_frequency = api_set_frequency, + .set_frequency_async = api_set_frequency_async, + .get_frequency_limits = api_get_frequency_limits, + .set_frequency_limits = api_set_frequency_limits, + .set_frequency_limits_async = api_set_frequency_limits_async, +}; diff --git a/product/rcar/module/rcar_dvfs/src/mod_dvfs_domain_api_private.h b/product/rcar/module/rcar_dvfs/src/mod_dvfs_domain_api_private.h new file mode 100644 index 000000000..5026bc29d --- /dev/null +++ b/product/rcar/module/rcar_dvfs/src/mod_dvfs_domain_api_private.h @@ -0,0 +1,16 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_DVFS_DOMAIN_API_PRIVATE_H +#define MOD_DVFS_DOMAIN_API_PRIVATE_H + +#include + +/* Module API implementation */ +extern const struct mod_dvfs_domain_api __mod_dvfs_domain_api; + +#endif /* MOD_DVFS_DOMAIN_API_PRIVATE_H */ diff --git a/product/rcar/module/rcar_dvfs/src/mod_dvfs_event.c b/product/rcar/module/rcar_dvfs/src/mod_dvfs_event.c new file mode 100644 index 000000000..78894cce1 --- /dev/null +++ b/product/rcar/module/rcar_dvfs/src/mod_dvfs_event.c @@ -0,0 +1,47 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +static int event_set_opp( + const struct fwk_event *event, + struct fwk_event *response) +{ + return FWK_E_SUPPORT; +} + +static int event_set_frequency_limits( + const struct fwk_event *event, + struct fwk_event *response) +{ + return FWK_E_SUPPORT; +} + +int __mod_dvfs_process_event( + const struct fwk_event *event, + struct fwk_event *response) +{ + typedef int (*handler_t)( + const struct fwk_event *event, + struct fwk_event *response); + + static const handler_t handlers[] = { + [MOD_DVFS_EVENT_IDX_SET_FREQUENCY] = event_set_opp, + [MOD_DVFS_EVENT_IDX_SET_FREQUENCY_LIMITS] = event_set_frequency_limits, + }; + + handler_t handler; + + /* Ensure we have a handler implemented for this event */ + handler = handlers[fwk_id_get_event_idx(event->id)]; + if (handler == NULL) + return FWK_E_PARAM; + + /* Delegate event handling to the relevant handler */ + return handler(event, response); +} diff --git a/product/rcar/module/rcar_dvfs/src/mod_dvfs_event_private.h b/product/rcar/module/rcar_dvfs/src/mod_dvfs_event_private.h new file mode 100644 index 000000000..c8e0b3242 --- /dev/null +++ b/product/rcar/module/rcar_dvfs/src/mod_dvfs_event_private.h @@ -0,0 +1,18 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_DVFS_EVENT_PRIVATE_H +#define MOD_DVFS_EVENT_PRIVATE_H + +#include + +/* Event handler */ +int __mod_dvfs_process_event( + const struct fwk_event *event, + struct fwk_event *response); + +#endif /* MOD_DVFS_EVENT_PRIVATE_H */ diff --git a/product/rcar/module/rcar_dvfs/src/mod_dvfs_module.c b/product/rcar/module/rcar_dvfs/src/mod_dvfs_module.c new file mode 100644 index 000000000..30f13e947 --- /dev/null +++ b/product/rcar/module/rcar_dvfs/src/mod_dvfs_module.c @@ -0,0 +1,238 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2017-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct mod_dvfs_domain_ctx (*domain_ctx)[]; + +static int count_opps(const struct mod_dvfs_opp *opps) +{ + const struct mod_dvfs_opp *opp = &opps[0]; + + while ((opp->voltage != 0) && (opp->frequency != 0)) + opp++; + + return opp - &opps[0]; +} + +static struct mod_dvfs_domain_ctx *get_domain_ctx(fwk_id_t domain_id) +{ + unsigned int element_idx = fwk_id_get_element_idx(domain_id); + + return &(*domain_ctx)[element_idx]; +} + +static int dvfs_init( + fwk_id_t module_id, + unsigned int element_count, + const void *data) +{ + domain_ctx = fwk_mm_calloc( + element_count, + sizeof((*domain_ctx)[0])); + if (domain_ctx == NULL) + return FWK_E_NOMEM; + + return FWK_SUCCESS; +} + +static int dvfs_element_init( + fwk_id_t domain_id, + unsigned int sub_element_count, + const void *data) +{ + struct mod_dvfs_domain_ctx *ctx = get_domain_ctx(domain_id); + + assert(sub_element_count == 0); + + /* Initialize the configuration */ + ctx->config = data; + assert(ctx->config->opps != NULL); + + /* Initialize the context */ + ctx->opp_count = count_opps(ctx->config->opps); + assert(ctx->opp_count > 0); + + /* Frequency limits default to the minimum and maximum available */ + ctx->frequency_limits = (struct mod_dvfs_frequency_limits) { + .minimum = ctx->config->opps[0].frequency, + .maximum = ctx->config->opps[ctx->opp_count - 1].frequency, + }; + + ctx->suspended_opp = ctx->config->opps[ctx->config->sustained_idx]; + + return FWK_SUCCESS; +} + +static int dvfs_bind_element(fwk_id_t domain_id, unsigned int round) +{ + int status; + const struct mod_dvfs_domain_ctx *ctx = + get_domain_ctx(domain_id); + + /* Only handle the first round */ + if (round > 0) + return FWK_SUCCESS; + + /* Bind to the power supply module */ + status = fwk_module_bind( + ctx->config->psu_id, + mod_rcar_pmic_api_id_device, + &ctx->apis.psu); + if (status != FWK_SUCCESS) + return FWK_E_PANIC; + + /* Bind to the clock module */ + status = fwk_module_bind( + ctx->config->clock_id, + FWK_ID_API(FWK_MODULE_IDX_CLOCK, 0), + &ctx->apis.clock); + if (status != FWK_SUCCESS) + return FWK_E_PANIC; + + return FWK_SUCCESS; +} + +static int dvfs_bind(fwk_id_t id, unsigned int round) +{ + /* We only need to handle binding our elements */ + if (fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT)) + return dvfs_bind_element(id, round); + + return FWK_SUCCESS; +} + +static int dvfs_process_bind_request_module( + fwk_id_t source_id, + fwk_id_t api_id, + const void **api) +{ + /* Only expose the module API */ + if (!fwk_id_is_equal(api_id, mod_dvfs_api_id_dvfs)) + return FWK_E_PARAM; + + /* We don't do any permissions management */ + *api = &__mod_dvfs_domain_api; + + return FWK_SUCCESS; +} + +static int dvfs_process_bind_request( + fwk_id_t source_id, + fwk_id_t target_id, + fwk_id_t api_id, + const void **api) +{ + /* Only allow binding to the module */ + if (!fwk_id_is_equal(target_id, fwk_module_id_dvfs)) + return FWK_E_PARAM; + + return dvfs_process_bind_request_module(source_id, api_id, api); +} + +static int dvfs_start(fwk_id_t id) +{ + int status; + const struct mod_dvfs_domain_ctx *ctx; + + if (!fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT)) + return FWK_SUCCESS; + + ctx = get_domain_ctx(id); + + /* Register for clock state notifications */ + status = fwk_notification_subscribe( + mod_clock_notification_id_state_changed, + ctx->config->clock_id, + id); + if (status != FWK_SUCCESS) + return status; + + return fwk_notification_subscribe( + mod_clock_notification_id_state_change_pending, + ctx->config->clock_id, + id); +} + +static int dvfs_notify_system_state_transition_suspend(fwk_id_t domain_id) +{ + struct mod_dvfs_domain_ctx *ctx = + get_domain_ctx(domain_id); + + return __mod_dvfs_get_current_opp(ctx, &ctx->suspended_opp); +} + +static int dvfs_notify_system_state_transition_resume(fwk_id_t domain_id) +{ + const struct mod_dvfs_domain_ctx *ctx = + get_domain_ctx(domain_id); + + return __mod_dvfs_set_opp(ctx, &ctx->suspended_opp); +} + +static int dvfs_process_notification( + const struct fwk_event *event, + struct fwk_event *resp_event) +{ + struct clock_notification_params *params; + struct clock_state_change_pending_resp_params *resp_params; + + assert( + fwk_id_is_equal( + event->id, + mod_clock_notification_id_state_changed) || + fwk_id_is_equal( + event->id, + mod_clock_notification_id_state_change_pending)); + assert(fwk_id_is_type(event->target_id, FWK_ID_TYPE_ELEMENT)); + + params = (struct clock_notification_params *)event->params; + + if (fwk_id_is_equal(event->id, mod_clock_notification_id_state_changed)) { + if (params->new_state == MOD_CLOCK_STATE_RUNNING) + return dvfs_notify_system_state_transition_resume(event->target_id); + } else if (params->new_state == MOD_CLOCK_STATE_STOPPED) { + /* DVFS has received the pending change notification */ + resp_params = + (struct clock_state_change_pending_resp_params *)resp_event->params; + resp_params->status = FWK_SUCCESS; + + return dvfs_notify_system_state_transition_suspend(event->target_id); + } + + return FWK_SUCCESS; +} + +struct mod_dvfs_domain_ctx *__mod_dvfs_get_valid_domain_ctx(fwk_id_t domain_id) +{ + return get_domain_ctx(domain_id); +} + +/* Module description */ +const struct fwk_module module_rcar_dvfs = { + .name = "DVFS", + .type = FWK_MODULE_TYPE_HAL, + .init = dvfs_init, + .element_init = dvfs_element_init, + .bind = dvfs_bind, + .process_bind_request = dvfs_process_bind_request, + .process_event = __mod_dvfs_process_event, + .start = dvfs_start, + .process_notification = dvfs_process_notification, + .api_count = MOD_DVFS_API_IDX_COUNT, + .event_count = MOD_DVFS_EVENT_IDX_COUNT, +}; diff --git a/product/rcar/module/rcar_dvfs/src/mod_dvfs_module_private.h b/product/rcar/module/rcar_dvfs/src/mod_dvfs_module_private.h new file mode 100644 index 000000000..78aa502d1 --- /dev/null +++ b/product/rcar/module/rcar_dvfs/src/mod_dvfs_module_private.h @@ -0,0 +1,41 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2017-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_DVFS_MODULE_PRIVATE_H +#define MOD_DVFS_MODULE_PRIVATE_H + +#include +#include +#include + +/* Domain context */ +struct mod_dvfs_domain_ctx { + /* Domain configuration */ + const struct mod_dvfs_domain_config *config; + + struct { + /* Power supply API */ + const struct mod_rcar_pmic_device_api *psu; + + /* Clock API */ + const struct mod_clock_api *clock; + } apis; + + /* Number of operating points */ + size_t opp_count; + + /* Operating point prior to domain suspension */ + struct mod_dvfs_opp suspended_opp; + + /* Current operating point limits */ + struct mod_dvfs_frequency_limits frequency_limits; +}; + +struct mod_dvfs_domain_ctx *__mod_dvfs_get_valid_domain_ctx(fwk_id_t domain_id); + +#endif /* MOD_DVFS_MODULE_PRIVATE_H */ diff --git a/product/rcar/module/rcar_dvfs/src/mod_dvfs_private.h b/product/rcar/module/rcar_dvfs/src/mod_dvfs_private.h new file mode 100644 index 000000000..4b7810ae7 --- /dev/null +++ b/product/rcar/module/rcar_dvfs/src/mod_dvfs_private.h @@ -0,0 +1,16 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_DVFS_PRIVATE_H +#define MOD_DVFS_PRIVATE_H + +#include +#include +#include +#include + +#endif /* MOD_DVFS_PRIVATE_H */ diff --git a/product/rcar/module/rcar_dvfs/src/mod_dvfs_util.c b/product/rcar/module/rcar_dvfs/src/mod_dvfs_util.c new file mode 100644 index 000000000..130fce07f --- /dev/null +++ b/product/rcar/module/rcar_dvfs/src/mod_dvfs_util.c @@ -0,0 +1,71 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include + +int __mod_dvfs_set_opp( + const struct mod_dvfs_domain_ctx *ctx, + const struct mod_dvfs_opp *new_opp) +{ + int status; + struct mod_dvfs_opp current_opp; + + status = __mod_dvfs_get_current_opp(ctx, ¤t_opp); + if (status != FWK_SUCCESS) + return status; + + if (new_opp->voltage > current_opp.voltage) { + /* Raise the voltage before raising the frequency */ + status = ctx->apis.psu->set_voltage( + ctx->config->psu_id, + new_opp->voltage); + if (status != FWK_SUCCESS) + return FWK_E_DEVICE; + } + + if (new_opp->frequency != current_opp.frequency) { + status = ctx->apis.clock->set_rate( + ctx->config->clock_id, + new_opp->frequency, + MOD_CLOCK_ROUND_MODE_NONE); + if (status != FWK_SUCCESS) + return FWK_E_DEVICE; + } + + if (new_opp->voltage < current_opp.voltage) { + /* Lower the voltage after lowering the frequency */ + status = ctx->apis.psu->set_voltage( + ctx->config->psu_id, + new_opp->voltage); + if (status != FWK_SUCCESS) + return FWK_E_DEVICE; + } + + return FWK_SUCCESS; +} + +int __mod_dvfs_get_current_opp( + const struct mod_dvfs_domain_ctx *ctx, + struct mod_dvfs_opp *opp) +{ + int status; + + status = ctx->apis.clock->get_rate( + ctx->config->clock_id, + &opp->frequency); + if (status != FWK_SUCCESS) + return FWK_E_DEVICE; + + status = ctx->apis.psu->get_voltage( + ctx->config->psu_id, + &opp->voltage); + if (status != FWK_SUCCESS) + return FWK_E_DEVICE; + + return FWK_SUCCESS; +} diff --git a/product/rcar/module/rcar_dvfs/src/mod_dvfs_util_private.h b/product/rcar/module/rcar_dvfs/src/mod_dvfs_util_private.h new file mode 100644 index 000000000..4897afc37 --- /dev/null +++ b/product/rcar/module/rcar_dvfs/src/mod_dvfs_util_private.h @@ -0,0 +1,22 @@ +/* + * Arm SCP/MCP Software + * Copyright (c) 2017-2020, Arm Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_DVFS_UTIL_PRIVATE_H +#define MOD_DVFS_UTIL_PRIVATE_H + +#include +#include + +int __mod_dvfs_set_opp( + const struct mod_dvfs_domain_ctx *ctx, + const struct mod_dvfs_opp *new_opp); + +int __mod_dvfs_get_current_opp( + const struct mod_dvfs_domain_ctx *ctx, + struct mod_dvfs_opp *opp); + +#endif /* MOD_DVFS_PRIVATE_H */ diff --git a/product/rcar/scp_ramfw/config_rcar_dvfs.c b/product/rcar/scp_ramfw/config_rcar_dvfs.c new file mode 100644 index 000000000..ea05897f3 --- /dev/null +++ b/product/rcar/scp_ramfw/config_rcar_dvfs.c @@ -0,0 +1,89 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include + +static const struct mod_dvfs_domain_config cpu_group_little = { + .psu_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_PMIC, 0), + .clock_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, 0), + .latency = 1200, + .sustained_idx = 2, + .opps = (struct mod_dvfs_opp[]) { + { + .frequency = 800 * FWK_MHZ, + .voltage = 820000, + }, + { + .frequency = 1000 * FWK_MHZ, + .voltage = 820000, + }, + { + .frequency = 1200 * FWK_MHZ, + .voltage = 820000, + }, + { 0 } + } +}; + +static const struct mod_dvfs_domain_config cpu_group_big = { + .psu_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_PMIC, 1), + .clock_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, 1), + .latency = 1200, + .sustained_idx = 2, + .opps = (struct mod_dvfs_opp[]) { + { + .frequency = 500 * FWK_MHZ, + .voltage = 830000, + }, + { + .frequency = 1000 * FWK_MHZ, + .voltage = 830000, + }, + { + .frequency = 1500 * FWK_MHZ, + .voltage = 830000, + }, +#if 0 /* The prototype does not support boost mode. */ + { + .frequency = 1600 * FWK_MHZ, + .voltage = 900000, + }, + { + .frequency = 1700 * FWK_MHZ, + .voltage = 960000, + }, +#endif + { 0 } + } +}; + +static const struct fwk_element element_table[] = { + [DVFS_ELEMENT_IDX_LITTLE] = { + .name = "CPU_GROUP_LITTLE", + .data = &cpu_group_little, + }, + [DVFS_ELEMENT_IDX_BIG] = { + .name = "CPU_GROUP_BIG", + .data = &cpu_group_big, + }, + { 0 } +}; + +static const struct fwk_element *dvfs_get_element_table(fwk_id_t module_id) +{ + return element_table; +} + +struct fwk_module_config config_rcar_dvfs = { + .elements = FWK_MODULE_DYNAMIC_ELEMENTS(dvfs_get_element_table), + .data = NULL, +}; diff --git a/product/rcar/scp_ramfw/config_rcar_dvfs.h b/product/rcar/scp_ramfw/config_rcar_dvfs.h new file mode 100644 index 000000000..e97ab608a --- /dev/null +++ b/product/rcar/scp_ramfw/config_rcar_dvfs.h @@ -0,0 +1,17 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef CONFIG_RCAR_DVFS_H +#define CONFIG_RCAR_DVFS_H + +enum dvfs_element_idx { + DVFS_ELEMENT_IDX_LITTLE, + DVFS_ELEMENT_IDX_BIG, + DVFS_ELEMENT_IDX_COUNT +}; + +#endif /* CONFIG_RCAR_DVFS_H */ -- GitLab From f092662656b9f1fc7b5c5039e8e49ea4e0390d72 Mon Sep 17 00:00:00 2001 From: Nicolas Royer Date: Sun, 27 Sep 2020 17:38:23 +0200 Subject: [PATCH 06/19] rcar/module: add rcar mfismh module and config data Signed-off-by: Tsutomu Muroya Signed-off-by: Nicolas Royer --- .../rcar_mfismh/include/mod_rcar_mfismh.h | 51 ++++ product/rcar/module/rcar_mfismh/src/Makefile | 11 + .../module/rcar_mfismh/src/mod_rcar_mfismh.c | 245 ++++++++++++++++++ .../rcar/module/rcar_mfismh/src/rcar_mfismh.h | 39 +++ product/rcar/scp_ramfw/config_rcar_mfismh.c | 41 +++ 5 files changed, 387 insertions(+) create mode 100644 product/rcar/module/rcar_mfismh/include/mod_rcar_mfismh.h create mode 100644 product/rcar/module/rcar_mfismh/src/Makefile create mode 100644 product/rcar/module/rcar_mfismh/src/mod_rcar_mfismh.c create mode 100644 product/rcar/module/rcar_mfismh/src/rcar_mfismh.h create mode 100644 product/rcar/scp_ramfw/config_rcar_mfismh.c diff --git a/product/rcar/module/rcar_mfismh/include/mod_rcar_mfismh.h b/product/rcar/module/rcar_mfismh/include/mod_rcar_mfismh.h new file mode 100644 index 000000000..aceae6c97 --- /dev/null +++ b/product/rcar/module/rcar_mfismh/include/mod_rcar_mfismh.h @@ -0,0 +1,51 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_RCAR_MFISMH_H +#define MOD_RCAR_MFISMH_H + +#include +#include + +/*! + * \addtogroup GroupRCARModule RCAR Product Modules + * @{ + */ + +/*! + * \defgroup GroupRCARMHU Message Handling Unit (MHU) Driver + * @{ + */ + +/*! + * \brief MFISMB device + * + * \details Abstract representation of a bidirectional + * MFISMB device that consists + * of a single receive interrupt line and a pair of register sets, one for + * each direction of communication. + */ +struct mod_rcar_mfismh_device_config { + /*! IRQ number of the receive interrupt line */ + unsigned int irq; + + /*! Base address of the registers of the incoming MFISMHU */ + uintptr_t in; + + /*! Base address of the registers of the outgoing MFISMHU */ + uintptr_t out; +}; + +/*! + * @} + */ + +/*! + * @} + */ + +#endif /* MOD_RCAR_MFISMH_H */ diff --git a/product/rcar/module/rcar_mfismh/src/Makefile b/product/rcar/module/rcar_mfismh/src/Makefile new file mode 100644 index 000000000..5d769b93f --- /dev/null +++ b/product/rcar/module/rcar_mfismh/src/Makefile @@ -0,0 +1,11 @@ +# +# Renesas SCP/MCP Software +# Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +BS_LIB_NAME := RCAR MFISMH +BS_LIB_SOURCES := mod_rcar_mfismh.c + +include $(BS_DIR)/lib.mk diff --git a/product/rcar/module/rcar_mfismh/src/mod_rcar_mfismh.c b/product/rcar/module/rcar_mfismh/src/mod_rcar_mfismh.c new file mode 100644 index 000000000..e421b7913 --- /dev/null +++ b/product/rcar/module/rcar_mfismh/src/mod_rcar_mfismh.c @@ -0,0 +1,245 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include "rcar_mfismh.h" +#include +#include + +/* + * Maximum number of slots per MFISMH device. The maximum number of slots is 31 + * and not 32 because bit [31] in the MFISMH STAT register is reserved in secure + * MFISMHs for indicating that a non-secure access attempt occurred. + * This reservation also applies to non-secure MFISMHs for consistency, + * though the bit is unused. + */ +#define MFISMH_SLOT_COUNT_MAX 31 + +struct mfismh_reg *mfis_regs; + +struct mfismh_smt_channel { + fwk_id_t id; + struct mod_smt_driver_input_api *api; +}; + +/* MFISMH device context */ +struct mfismh_device_ctx { + /* Pointer to the device configuration */ + const struct mod_rcar_mfismh_device_config *config; + + /* Number of slots (represented by sub-elements) */ + unsigned int slot_count; + + /* Mask of slots that are bound to an SMT channel */ + uint32_t bound_slots; + + /* Table of SMT channels bound to the device */ + struct mfismh_smt_channel *smt_channel_table; +}; + +/* MFISMH context */ +struct mfismh_ctx { + /* Table of device contexts */ + struct mfismh_device_ctx *device_ctx_table; + + /* Number of devices in the device context table*/ + unsigned int device_count; +}; + +static struct mfismh_ctx mfismh_ctx; + +static void mfismh_isr(void) +{ + int status; + unsigned int interrupt; + unsigned int device_idx; + struct mfismh_device_ctx *device_ctx; + unsigned int slot; + struct mfismh_reg *reg; + struct mfismh_smt_channel *smt_channel; + + status = fwk_interrupt_get_current(&interrupt); + if (status != FWK_SUCCESS) + return; + + if (!IS_MFIS_IRQ(interrupt)) + return; + + for (device_idx = 0; device_idx < mfismh_ctx.device_count; device_idx++) { + device_ctx = &mfismh_ctx.device_ctx_table[device_idx]; + if (device_ctx->config->irq == interrupt) + break; + } + + if (device_idx >= mfismh_ctx.device_count) + return; + + reg = (struct mfismh_reg *)&mfis_regs[MFIS_IRQ2NO(interrupt)]; + + /* Acknowledge the interrupt */ + reg->CCR.eir = 0; + + /* Loop over all the slots *//* For prototyping, slot number is 0 only. */ + { + slot = 0; + + /* + * If the slot is bound to an SMT channel, signal the message to the + * SMT channel. + */ + if (device_ctx->bound_slots & (1 << slot)) { + smt_channel = &device_ctx->smt_channel_table[slot]; + smt_channel->api->signal_message(smt_channel->id); + } + } +} + +/* + * SMT module driver API + */ +static int raise_interrupt(fwk_id_t slot_id) +{ + /* This function is unsupported. */ + return FWK_SUCCESS; +} + +const struct mod_smt_driver_api mfismh_mod_smt_driver_api = { + .raise_interrupt = raise_interrupt, +}; + +/* + * Framework handlers + */ +static int mfismh_init(fwk_id_t module_id, unsigned int device_count, + const void *unused) +{ + if (device_count == 0) + return FWK_E_PARAM; + + mfismh_ctx.device_ctx_table = fwk_mm_calloc(device_count, + sizeof(mfismh_ctx.device_ctx_table[0])); + if (mfismh_ctx.device_ctx_table == NULL) + return FWK_E_NOMEM; + + mfismh_ctx.device_count = device_count; + + return FWK_SUCCESS; +} + +static int mfismh_device_init(fwk_id_t device_id, unsigned int slot_count, + const void *data) +{ + struct mod_rcar_mfismh_device_config *config = + (struct mod_rcar_mfismh_device_config *)data; + struct mfismh_device_ctx *device_ctx; + + device_ctx = + &mfismh_ctx.device_ctx_table[fwk_id_get_element_idx(device_id)]; + + device_ctx->smt_channel_table = fwk_mm_calloc(slot_count, + sizeof(device_ctx->smt_channel_table[0])); + if (device_ctx->smt_channel_table == NULL) + return FWK_E_NOMEM; + + device_ctx->config = config; + device_ctx->slot_count = slot_count; + + mfis_regs = (struct mfismh_reg *)MFISAREICR_BASE; + + return FWK_SUCCESS; +} + +static int mfismh_bind(fwk_id_t id, unsigned int round) +{ + int status; + struct mfismh_device_ctx *device_ctx; + unsigned int slot; + struct mfismh_smt_channel *smt_channel; + + if ((round == 1) && fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT)) { + device_ctx = &mfismh_ctx.device_ctx_table[fwk_id_get_element_idx(id)]; + + for (slot = 0; slot < MFISMH_SLOT_COUNT_MAX; slot++) { + if (!(device_ctx->bound_slots & (1 << slot))) + continue; + + smt_channel = &device_ctx->smt_channel_table[slot]; + + status = fwk_module_bind(smt_channel->id, + FWK_ID_API(FWK_MODULE_IDX_SMT, MOD_SMT_API_IDX_DRIVER_INPUT), + &smt_channel->api); + if (status != FWK_SUCCESS) + return status; + } + } + + return FWK_SUCCESS; +} + +static int mfismh_process_bind_request(fwk_id_t source_id, fwk_id_t target_id, + fwk_id_t api_id, const void **api) +{ + struct mfismh_device_ctx *device_ctx; + unsigned int slot; + + if (!fwk_id_is_type(target_id, FWK_ID_TYPE_SUB_ELEMENT)) + return FWK_E_ACCESS; + + device_ctx = + &mfismh_ctx.device_ctx_table[fwk_id_get_element_idx(target_id)]; + slot = fwk_id_get_sub_element_idx(target_id); + + if (device_ctx->bound_slots & (1 << slot)) + return FWK_E_ACCESS; + + device_ctx->smt_channel_table[slot].id = source_id; + device_ctx->bound_slots |= 1 << slot; + + *api = &mfismh_mod_smt_driver_api; + + return FWK_SUCCESS; +} + +static int mfismh_start(fwk_id_t id) +{ + int status; + struct mfismh_device_ctx *device_ctx; + + if (fwk_id_get_type(id) == FWK_ID_TYPE_MODULE) + return FWK_SUCCESS; + + device_ctx = &mfismh_ctx.device_ctx_table[fwk_id_get_element_idx(id)]; + + if (device_ctx->bound_slots != 0) { + status = fwk_interrupt_set_isr(device_ctx->config->irq, &mfismh_isr); + if (status != FWK_SUCCESS) + return status; + status = fwk_interrupt_enable(device_ctx->config->irq); + if (status != FWK_SUCCESS) + return status; + } + + return FWK_SUCCESS; +} + +/* MFISMH module definition */ +const struct fwk_module module_rcar_mfismh = { + .name = "RCAR MFISMH", + .type = FWK_MODULE_TYPE_DRIVER, + .api_count = 1, + .init = mfismh_init, + .element_init = mfismh_device_init, + .bind = mfismh_bind, + .start = mfismh_start, + .process_bind_request = mfismh_process_bind_request, +}; diff --git a/product/rcar/module/rcar_mfismh/src/rcar_mfismh.h b/product/rcar/module/rcar_mfismh/src/rcar_mfismh.h new file mode 100644 index 000000000..fec2f9c0c --- /dev/null +++ b/product/rcar/module/rcar_mfismh/src/rcar_mfismh.h @@ -0,0 +1,39 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RCAR_MFISMH_H +#define RCAR_MFISMH_H + +#include +#include +#include + +#define MFIS_IRQ_MIN (256U) +#define MFIS_IRQ_MAX (MFIS_IRQ_MIN + 8U) +#define MFIS_IRQ2NO(irn) (irn - MFIS_IRQ_MIN) +#define IS_MFIS_IRQ(irn) (((irn < MFIS_IRQ_MIN) || \ + (irn > MFIS_IRQ_MAX))? 0:1) + +/*! + * \brief SMCMB Register Definitions + */ +struct mfismh_reg { + /*! Communication Control Register(->R) */ + FWK_R uint32_t reserve; + /*! Communication Control Register(->CA) */ + FWK_W union { + struct { + uint32_t eir : 1; + uint32_t eic : 15; + uint32_t reserve : 16; + } CCR; + uint32_t CCR2CA; + }; +}; + + +#endif /* RCAR_MFISMH_H */ diff --git a/product/rcar/scp_ramfw/config_rcar_mfismh.c b/product/rcar/scp_ramfw/config_rcar_mfismh.c new file mode 100644 index 000000000..1ce05bfc8 --- /dev/null +++ b/product/rcar/scp_ramfw/config_rcar_mfismh.c @@ -0,0 +1,41 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +static const struct fwk_element mfismh_element_table[] = { + [RCAR_MFISMH_DEVICE_IDX_S] = { + .name = "MFISMH_S", + .sub_element_count = 1, + .data = &((struct mod_rcar_mfismh_device_config) { + .irq = MFIS_AREICR2_IRQ, + }) + }, + [RCAR_MFISMH_DEVICE_IDX_NS_L] = { + .name = "MSIFMH_NS_L", + .sub_element_count = 1, + .data = &((struct mod_rcar_mfismh_device_config) { + .irq = MFIS_AREICR1_IRQ, + }) + }, + [RCAR_MFISMH_DEVICE_IDX_COUNT] = {}, +}; + +static const struct fwk_element *mfismh_get_element_table(fwk_id_t module_id) +{ + return mfismh_element_table; +} + +struct fwk_module_config config_rcar_mfismh = { + .elements = FWK_MODULE_DYNAMIC_ELEMENTS(mfismh_get_element_table), +}; -- GitLab From c15c84bda1ed5126fa7330795ec78ab854aa97a6 Mon Sep 17 00:00:00 2001 From: Nicolas Royer Date: Sun, 27 Sep 2020 17:38:46 +0200 Subject: [PATCH 07/19] rcar/module: add rcar mock_pmic module and config data Signed-off-by: Tsutomu Muroya Signed-off-by: Nicolas Royer --- .../include/mod_rcar_mock_pmic.h | 70 +++++++++++++++ .../rcar/module/rcar_mock_pmic/src/Makefile | 13 +++ .../src/mod_rcar_mock_pmic_driver_api.c | 87 +++++++++++++++++++ .../mod_rcar_mock_pmic_driver_api_private.h | 28 ++++++ .../src/mod_rcar_mock_pmic_module.c | 84 ++++++++++++++++++ .../src/mod_rcar_mock_pmic_module_private.h | 22 +++++ .../src/mod_rcar_mock_pmic_private.h | 17 ++++ .../rcar/scp_ramfw/config_rcar_mock_pmic.c | 52 +++++++++++ 8 files changed, 373 insertions(+) create mode 100644 product/rcar/module/rcar_mock_pmic/include/mod_rcar_mock_pmic.h create mode 100644 product/rcar/module/rcar_mock_pmic/src/Makefile create mode 100644 product/rcar/module/rcar_mock_pmic/src/mod_rcar_mock_pmic_driver_api.c create mode 100644 product/rcar/module/rcar_mock_pmic/src/mod_rcar_mock_pmic_driver_api_private.h create mode 100644 product/rcar/module/rcar_mock_pmic/src/mod_rcar_mock_pmic_module.c create mode 100644 product/rcar/module/rcar_mock_pmic/src/mod_rcar_mock_pmic_module_private.h create mode 100644 product/rcar/module/rcar_mock_pmic/src/mod_rcar_mock_pmic_private.h create mode 100644 product/rcar/scp_ramfw/config_rcar_mock_pmic.c diff --git a/product/rcar/module/rcar_mock_pmic/include/mod_rcar_mock_pmic.h b/product/rcar/module/rcar_mock_pmic/include/mod_rcar_mock_pmic.h new file mode 100644 index 000000000..66c33fb14 --- /dev/null +++ b/product/rcar/module/rcar_mock_pmic/include/mod_rcar_mock_pmic.h @@ -0,0 +1,70 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_RCAR_MOCK_PMIC_H +#define MOD_RCAR_MOCK_PMIC_H + +#include +#include +#include + +/*! + * \ingroup GroupRCARModule + * \defgroup GroupRCARMockPmic Mock Power Supply Driver + * \{ + */ + +/*! + * \defgroup GroupRCARMockPmicConfig Configuration + * \{ + */ + +/*! + * \brief Element configuration. + */ +struct mod_rcar_mock_pmic_device_config { + /*! Default state of the mock device's supply (enabled or disabled) */ + bool default_enabled; + + /*! Default voltage, in millivolts (mV), of the device's supply */ + uint64_t default_voltage; +}; + +/*! + * \} + */ + +/*! + * \defgroup GroupRCARMockPmicIds Identifiers + * \{ + */ + +/*! + * \brief API indices. + */ +enum mod_rcar_mock_pmic_api_idx { + /*! API index for PSU driver API */ + MOD_RCAR_MOCK_PMIC_API_IDX_PSU_DRIVER, + + /*! Number of defined APIs */ + MOD_RCAR_MOCK_PMIC_API_COUNT +}; + +/*! Driver API identifier */ +static const fwk_id_t mod_rcar_mock_pmic_api_id_psu_driver = + FWK_ID_API_INIT( + FWK_MODULE_IDX_RCAR_MOCK_PMIC, MOD_RCAR_MOCK_PMIC_API_IDX_PSU_DRIVER); + +/*! + * \} + */ + +/*! + * \} + */ + +#endif /* MOD_RCAR_MOCK_PMIC_H */ diff --git a/product/rcar/module/rcar_mock_pmic/src/Makefile b/product/rcar/module/rcar_mock_pmic/src/Makefile new file mode 100644 index 000000000..36e5b9ff2 --- /dev/null +++ b/product/rcar/module/rcar_mock_pmic/src/Makefile @@ -0,0 +1,13 @@ +# +# Renesas SCP/MCP Software +# Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +BS_LIB_NAME := rcar_mock_pmic +BS_LIB_SOURCES := \ + mod_rcar_mock_pmic_module.c \ + mod_rcar_mock_pmic_driver_api.c + +include $(BS_DIR)/lib.mk diff --git a/product/rcar/module/rcar_mock_pmic/src/mod_rcar_mock_pmic_driver_api.c b/product/rcar/module/rcar_mock_pmic/src/mod_rcar_mock_pmic_driver_api.c new file mode 100644 index 000000000..8edf11a30 --- /dev/null +++ b/product/rcar/module/rcar_mock_pmic/src/mod_rcar_mock_pmic_driver_api.c @@ -0,0 +1,87 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include + +static int api_set_enabled(fwk_id_t device_id, bool enable) +{ + struct mod_rcar_mock_pmic_device_ctx *ctx; + + ctx = __mod_rcar_mock_pmic_get_valid_device_ctx(device_id); + if (ctx == NULL) + return FWK_E_PARAM; + + ctx->enabled = enable; + + return FWK_SUCCESS; +} + +static int api_get_enabled(fwk_id_t device_id, bool *enabled) +{ + struct mod_rcar_mock_pmic_device_ctx *ctx; + + ctx = __mod_rcar_mock_pmic_get_valid_device_ctx(device_id); + if (ctx == NULL) + return FWK_E_PARAM; + + *enabled = ctx->enabled; + + return FWK_SUCCESS; +} + +static int api_set_voltage(fwk_id_t device_id, uint64_t voltage) +{ + struct mod_rcar_mock_pmic_device_ctx *ctx; + uint8_t val; + + ctx = __mod_rcar_mock_pmic_get_valid_device_ctx(device_id); + if (ctx == NULL){ + return FWK_E_PARAM; + } + + if (voltage < BD9571MWV_MIN_MV * 1000 || voltage > BD9571MWV_MAX_MV * 1000) + return -1; + + val = DIV_ROUND(voltage, BD9571MWV_STEP_MV * 1000); + val &= REG_DATA_DVFS_SetVID_MASK; + +#if 0 + int ret; + /* Not supported because I2C used in kernel */ + ret = rcar_iic_dvfs_send(SLAVE_ADDR_PMIC, REG_ADDR_DVFS_SetVID, val); + if (ret) { + return ret; + } +#endif + + ctx->voltage = voltage; + return FWK_SUCCESS; +} + +static int api_get_voltage(fwk_id_t device_id, uint64_t *voltage) +{ + struct mod_rcar_mock_pmic_device_ctx *ctx; + + ctx = __mod_rcar_mock_pmic_get_valid_device_ctx(device_id); + if (ctx == NULL) + return FWK_E_PARAM; + + *voltage = ctx->voltage; + + return FWK_SUCCESS; +} + +const struct mod_rcar_pmic_driver_api __mod_rcar_mock_pmic_driver_api = { + .set_enabled = api_set_enabled, + .get_enabled = api_get_enabled, + .set_voltage = api_set_voltage, + .get_voltage = api_get_voltage, +}; diff --git a/product/rcar/module/rcar_mock_pmic/src/mod_rcar_mock_pmic_driver_api_private.h b/product/rcar/module/rcar_mock_pmic/src/mod_rcar_mock_pmic_driver_api_private.h new file mode 100644 index 000000000..8dfd6f19c --- /dev/null +++ b/product/rcar/module/rcar_mock_pmic/src/mod_rcar_mock_pmic_driver_api_private.h @@ -0,0 +1,28 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_RCAR_MOCK_PMIC_DRIVER_API_PRIVATE_H +#define MOD_RCAR_MOCK_PMIC_DRIVER_API_PRIVATE_H + +#include + +extern const struct mod_rcar_pmic_driver_api __mod_rcar_mock_pmic_driver_api; + +/* Default limits measured in millivolts and milliamps */ +#define BD9571MWV_MIN_MV 750 +#define BD9571MWV_MAX_MV 1030 +#define BD9571MWV_STEP_MV 10 + +/* Define Register */ +#define REG_DATA_DVFS_SetVID_MASK (0x7EU) +#define REG_ADDR_DVFS_SetVID (0x54U) +#define SLAVE_ADDR_PMIC (0x30U) +#define DIV_ROUND(n, d) (((n) + (d) / 2) / (d)) + +extern int32_t rcar_iic_dvfs_send(uint8_t slave, uint8_t regr, uint8_t data); + +#endif /* MOD_RCAR_MOCK_PMIC_DRIVER_API_PRIVATE_H */ diff --git a/product/rcar/module/rcar_mock_pmic/src/mod_rcar_mock_pmic_module.c b/product/rcar/module/rcar_mock_pmic/src/mod_rcar_mock_pmic_module.c new file mode 100644 index 000000000..d14637c90 --- /dev/null +++ b/product/rcar/module/rcar_mock_pmic/src/mod_rcar_mock_pmic_module.c @@ -0,0 +1,84 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +static struct mod_rcar_mock_pmic_device_ctx (*device_ctx)[]; + +static struct mod_rcar_mock_pmic_device_ctx *get_device_ctx( + fwk_id_t device_id) +{ + return &(*device_ctx)[fwk_id_get_element_idx(device_id)]; +} + +static int mock_psu_init( + fwk_id_t module_id, + unsigned int element_count, + const void *data) +{ + device_ctx = fwk_mm_calloc( + element_count, + sizeof((*device_ctx)[0])); + if (device_ctx == NULL) + return FWK_E_NOMEM; + + return FWK_SUCCESS; +} + +static int mock_psu_element_init( + fwk_id_t device_id, + unsigned int sub_element_count, + const void *data) +{ + struct mod_rcar_mock_pmic_device_ctx *ctx; + const struct mod_rcar_mock_pmic_device_config *config = data; + + assert(sub_element_count == 0); + + ctx = get_device_ctx(device_id); + ctx->enabled = config->default_enabled; + ctx->voltage = config->default_voltage; + + return FWK_SUCCESS; +} + +static int mock_psu_process_bind_request( + fwk_id_t source_id, + fwk_id_t target_id, + fwk_id_t api_id, + const void **api) +{ + /* Only accept binds to the elements */ + if (!fwk_id_is_type(target_id, FWK_ID_TYPE_ELEMENT)) + return FWK_E_PARAM; + + *api = &__mod_rcar_mock_pmic_driver_api; + + return FWK_SUCCESS; +} + +struct mod_rcar_mock_pmic_device_ctx *__mod_rcar_mock_pmic_get_valid_device_ctx( + fwk_id_t device_id) +{ + return get_device_ctx(device_id); +} + +/* Module description */ +const struct fwk_module module_rcar_mock_pmic = { + .name = "MOCK_PSU", + .type = FWK_MODULE_TYPE_DRIVER, + .api_count = MOD_RCAR_MOCK_PMIC_API_COUNT, + .init = mock_psu_init, + .element_init = mock_psu_element_init, + .process_bind_request = mock_psu_process_bind_request, +}; diff --git a/product/rcar/module/rcar_mock_pmic/src/mod_rcar_mock_pmic_module_private.h b/product/rcar/module/rcar_mock_pmic/src/mod_rcar_mock_pmic_module_private.h new file mode 100644 index 000000000..3c36e4708 --- /dev/null +++ b/product/rcar/module/rcar_mock_pmic/src/mod_rcar_mock_pmic_module_private.h @@ -0,0 +1,22 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_RCAR_MOCK_PMIC_MODULE_PRIVATE_H +#define MOD_RCAR_MOCK_PMIC_MODULE_PRIVATE_H + +#include +#include + +struct mod_rcar_mock_pmic_device_ctx { + bool enabled; /* Current enabled state */ + uint64_t voltage; /* Current voltage (in mV) */ +}; + +struct mod_rcar_mock_pmic_device_ctx *__mod_rcar_mock_pmic_get_valid_device_ctx( + fwk_id_t device_id); + +#endif /* MOD_RCAR_MOCK_PMIC_MODULE_PRIVATE_H */ diff --git a/product/rcar/module/rcar_mock_pmic/src/mod_rcar_mock_pmic_private.h b/product/rcar/module/rcar_mock_pmic/src/mod_rcar_mock_pmic_private.h new file mode 100644 index 000000000..d9667c510 --- /dev/null +++ b/product/rcar/module/rcar_mock_pmic/src/mod_rcar_mock_pmic_private.h @@ -0,0 +1,17 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_RCAR_MOCK_PMIC_PRIVATE_H +#define MOD_RCAR_MOCK_PMIC_PRIVATE_H + +#include +#include +#include +#include +#include + +#endif /* MOD_RCAR_MOCK_PMIC_PRIVATE_H */ diff --git a/product/rcar/scp_ramfw/config_rcar_mock_pmic.c b/product/rcar/scp_ramfw/config_rcar_mock_pmic.c new file mode 100644 index 000000000..9a1fc8297 --- /dev/null +++ b/product/rcar/scp_ramfw/config_rcar_mock_pmic.c @@ -0,0 +1,52 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include + +static const struct fwk_element element_table[] = { + { + .name = "CPU_GROUP_LITTLE", + .data = &(const struct mod_rcar_mock_pmic_device_config) { + .default_enabled = true, + .default_voltage = 820000, + }, + }, + { + .name = "CPU_GROUP_BIG", + .data = &(const struct mod_rcar_mock_pmic_device_config) { + .default_enabled = true, + .default_voltage = 830000, + }, + }, + { + .name = "GPU", + .data = &(const struct mod_rcar_mock_pmic_device_config) { + .default_enabled = true, + .default_voltage = 100, + }, + }, + { + .name = "VPU", + .data = &(const struct mod_rcar_mock_pmic_device_config) { + .default_enabled = true, + .default_voltage = 100, + }, + }, + { 0 } +}; + +static const struct fwk_element *get_element_table(fwk_id_t module_id) +{ + return element_table; +} + +struct fwk_module_config config_rcar_mock_pmic = { + .elements = FWK_MODULE_DYNAMIC_ELEMENTS(get_element_table), + .data = NULL, +}; -- GitLab From 1d47affaf25b0773d01928e2fb4de2fe78a775a5 Mon Sep 17 00:00:00 2001 From: Nicolas Royer Date: Sun, 27 Sep 2020 17:39:40 +0200 Subject: [PATCH 08/19] rcar/module: add rcar mstp_clock module and config data Signed-off-by: Tsutomu Muroya Signed-off-by: Nicolas Royer --- .../include/mod_rcar_mstp_clock.h | 101 ++ .../rcar/module/rcar_mstp_clock/src/Makefile | 11 + .../rcar_mstp_clock/src/mod_rcar_mstp_clock.c | 199 +++ .../rcar/scp_ramfw/config_rcar_mstp_clock.c | 1082 +++++++++++++++++ 4 files changed, 1393 insertions(+) create mode 100644 product/rcar/module/rcar_mstp_clock/include/mod_rcar_mstp_clock.h create mode 100644 product/rcar/module/rcar_mstp_clock/src/Makefile create mode 100755 product/rcar/module/rcar_mstp_clock/src/mod_rcar_mstp_clock.c create mode 100644 product/rcar/scp_ramfw/config_rcar_mstp_clock.c diff --git a/product/rcar/module/rcar_mstp_clock/include/mod_rcar_mstp_clock.h b/product/rcar/module/rcar_mstp_clock/include/mod_rcar_mstp_clock.h new file mode 100644 index 000000000..fb62bb712 --- /dev/null +++ b/product/rcar/module/rcar_mstp_clock/include/mod_rcar_mstp_clock.h @@ -0,0 +1,101 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_RCAR_MSTP_CLOCK_H +#define MOD_RCAR_MSTP_CLOCK_H + +#include +#include +#include +#include +#include + +/*! + * \addtogroup GroupRCARModule RCAR Product Modules + * @{ + */ + +/*! + * \defgroup GroupRCARMstpClock MSTP Clock + * @{ + */ + +/*! + * \brief Subsystem clock device configuration. + */ +struct mod_rcar_mstp_clock_dev_config { + /*! Pointer to the clock's control register. */ + volatile uint32_t const control_reg; + + /*! enable / disable bit position. */ + volatile uint32_t const bit; + + /*! If true, the driver will provide a default clock supply. */ + const bool defer_initialization; +}; + +/*! + * @cond + */ + +/* Device context */ +struct rcar_mstp_clock_dev_ctx { + bool initialized; + uint64_t current_rate; + enum mod_clock_state current_state; + const struct mod_rcar_mstp_clock_dev_config *config; + struct mod_rcar_clock_drv_api *api; +}; + +struct mod_rcar_mstp_clock_init { + volatile uint32_t const smstpcr_init[12]; +}; + +/* Module context */ +struct rcar_mstp_clock_ctx { + struct rcar_mstp_clock_dev_ctx *dev_ctx_table; + unsigned int dev_count; + const struct mod_rcar_mstp_clock_init *mstp_init; +}; + +/* Module Stop Status Register offsets */ +static const uint16_t mstpsr[] = { + 0x030, 0x038, 0x040, 0x048, 0x04C, 0x03C, 0x1C0, 0x1C4, + 0x9A0, 0x9A4, 0x9A8, 0x9AC, +}; + +/* System Module Stop Control Register offsets */ +static const uint16_t smstpcr[] = { + 0x130, 0x134, 0x138, 0x13C, 0x140, 0x144, 0x148, 0x14C, + 0x990, 0x994, 0x998, 0x99C, +}; + +/* System Module Stop Control Register Number */ +#define CPG_SMSTPCR1 1 +#define CPG_SMSTPCR2 2 +#define CPG_SMSTPCR3 3 +#define CPG_SMSTPCR4 4 +#define CPG_SMSTPCR5 5 +#define CPG_SMSTPCR6 6 +#define CPG_SMSTPCR7 7 +#define CPG_SMSTPCR8 8 +#define CPG_SMSTPCR9 9 +#define CPG_SMSTPCR10 10 + +/*! + * @endcond + */ + +/*! + * @} + */ + +/*! + * @} + */ + +#endif /* MOD_RCAR_MSTP_CLOCK_H */ diff --git a/product/rcar/module/rcar_mstp_clock/src/Makefile b/product/rcar/module/rcar_mstp_clock/src/Makefile new file mode 100644 index 000000000..fe4acb109 --- /dev/null +++ b/product/rcar/module/rcar_mstp_clock/src/Makefile @@ -0,0 +1,11 @@ +# +# Renesas SCP/MCP Software +# Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +BS_LIB_NAME := RCAR SD Clock Driver +BS_LIB_SOURCES := mod_rcar_mstp_clock.c + +include $(BS_DIR)/lib.mk diff --git a/product/rcar/module/rcar_mstp_clock/src/mod_rcar_mstp_clock.c b/product/rcar/module/rcar_mstp_clock/src/mod_rcar_mstp_clock.c new file mode 100755 index 000000000..d2de9427d --- /dev/null +++ b/product/rcar/module/rcar_mstp_clock/src/mod_rcar_mstp_clock.c @@ -0,0 +1,199 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct rcar_mstp_clock_ctx module_ctx; + +/* + * Static helper functions + */ +static int mstp_clock_set_state( + fwk_id_t dev_id, + enum mod_clock_state target_state) +{ + struct rcar_mstp_clock_dev_ctx *ctx; + uint32_t value; + int i; + + ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id); + + value = mmio_read_32(CPG_BASE + smstpcr[ctx->config->control_reg]); + if (MOD_CLOCK_STATE_RUNNING == target_state) + value &= ~(BIT(ctx->config->bit)); + else + value |= BIT(ctx->config->bit); + + mmio_write_32((CPG_BASE + smstpcr[ctx->config->control_reg]), value); + + if (MOD_CLOCK_STATE_RUNNING == target_state) { + for (i = 1000; i > 0; --i) { + if (!(mmio_read_32(CPG_BASE + mstpsr[ctx->config->control_reg]) + & BIT(ctx->config->bit))) + break; + } + + if (!i) + return FWK_E_TIMEOUT; + } + + ctx->current_state = target_state; + return FWK_SUCCESS; +} + +static int mstp_clock_get_state(fwk_id_t dev_id, enum mod_clock_state *state) +{ + struct rcar_mstp_clock_dev_ctx *ctx; + + ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id); + *state = ctx->current_state; + return FWK_SUCCESS; +} + +static void mstp_clock_hw_initial_set_state(fwk_id_t element_id, + struct rcar_mstp_clock_dev_ctx *ctx) +{ + /* Maintain clock supply at startup. */ + if (module_ctx.mstp_init->smstpcr_init[ctx->config->control_reg] + & BIT(ctx->config->bit)) + ctx->current_state = MOD_CLOCK_STATE_STOPPED; + else + ctx->current_state = MOD_CLOCK_STATE_RUNNING; + + /* If true, the driver will provide a default clock supply. */ + if (ctx->config->defer_initialization) + mstp_clock_set_state(element_id, MOD_CLOCK_STATE_RUNNING); + +} + +static int mstp_clock_resume(void) +{ + fwk_id_t element_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, + CLK_ID_MSTP_START); + uint32_t mstp_id; + struct rcar_mstp_clock_dev_ctx *ctx; + + for (mstp_id = CLK_ID_MSTP_START; mstp_id < CLK_ID_MSTP_END; mstp_id++) { + element_id.element.element_idx = mstp_id; + ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(element_id); + mstp_clock_hw_initial_set_state(element_id, ctx); + } + return FWK_SUCCESS; +} + +static int mstp_clock_set_rate(fwk_id_t dev_id, uint64_t rate, + enum mod_clock_round_mode round_mode) +{ + return FWK_E_PARAM; +} + +static int mstp_clock_get_rate(fwk_id_t dev_id, uint64_t *rate) +{ + return FWK_E_PARAM; +} + +static int mstp_clock_get_rate_from_index(fwk_id_t dev_id, + unsigned int rate_index, + uint64_t *rate) +{ + return FWK_E_PARAM; +} + +static int mstp_clock_get_range(fwk_id_t dev_id, struct mod_clock_range *range) +{ + return FWK_E_PARAM; +} + +static const struct mod_rcar_clock_drv_api api_clock = { + .set_state = mstp_clock_set_state, + .get_state = mstp_clock_get_state, + .resume = mstp_clock_resume, + .set_rate = mstp_clock_set_rate, + .get_rate = mstp_clock_get_rate, + .get_rate_from_index = mstp_clock_get_rate_from_index, + .get_range = mstp_clock_get_range, +}; + +/* + * Framework handler functions + */ + +static int mstp_clock_init(fwk_id_t module_id, unsigned int element_count, + const void *data) +{ + const struct mod_rcar_mstp_clock_init *mstp_init = data; + + module_ctx.dev_count = element_count; + + if (element_count == 0) + return FWK_SUCCESS; + + if (mstp_init == NULL) + return FWK_E_PARAM; + + module_ctx.mstp_init = mstp_init; + module_ctx.dev_ctx_table = fwk_mm_calloc(element_count, + sizeof(struct rcar_mstp_clock_dev_ctx)); + if (module_ctx.dev_ctx_table == NULL) + return FWK_E_NOMEM; + + return FWK_SUCCESS; +} + +static int mstp_clock_element_init(fwk_id_t element_id, + unsigned int sub_element_count, + const void *data) +{ + struct rcar_mstp_clock_dev_ctx *ctx; + const struct mod_rcar_mstp_clock_dev_config *dev_config = data; + + if (!fwk_module_is_valid_element_id(element_id)) + return FWK_E_PARAM; + + ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(element_id); + ctx->config = dev_config; + ctx->initialized = true; + + return FWK_SUCCESS; +} + +static int mstp_clock_process_bind_request(fwk_id_t source_id, + fwk_id_t target_id, fwk_id_t api_id, + const void **api) +{ + *api = &api_clock; + return FWK_SUCCESS; +} + +static int mstp_clock_start(fwk_id_t id) +{ + int ret = FWK_SUCCESS; + ret = mstp_clock_resume(); + return ret; +} + +const struct fwk_module module_rcar_mstp_clock = { + .name = "MSTP Clock Driver", + .type = FWK_MODULE_TYPE_DRIVER, + .api_count = MOD_RCAR_CLOCK_API_COUNT, + .event_count = 0, + .init = mstp_clock_init, + .element_init = mstp_clock_element_init, + .process_bind_request = mstp_clock_process_bind_request, + .start = mstp_clock_start, +}; diff --git a/product/rcar/scp_ramfw/config_rcar_mstp_clock.c b/product/rcar/scp_ramfw/config_rcar_mstp_clock.c new file mode 100644 index 000000000..fa74d3992 --- /dev/null +++ b/product/rcar/scp_ramfw/config_rcar_mstp_clock.c @@ -0,0 +1,1082 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +static const struct fwk_element pik_clock_element_table[] = { + { + .name = "fdp1-1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR1, + .bit = 18, + }), + }, + { + .name = "fdp1-0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR1, + .bit = 19, + }), + }, + { + .name = "scif5", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR2, + .bit = 2, + }), + }, + { + .name = "scif4", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR2, + .bit = 3, + }), + }, + { + .name = "scif3", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR2, + .bit = 4, + }), + }, + { + .name = "scif1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR2, + .bit = 6, + }), + }, + { + .name = "scif0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR2, + .bit = 7, + }), + }, + { + .name = "msiof3", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR2, + .bit = 8, + }), + }, + { + .name = "msiof2", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR2, + .bit = 9, + }), + }, + { + .name = "msiof1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR2, + .bit = 10, + }), + }, + { + .name = "msiof0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR2, + .bit = 11, + }), + }, + { + .name = "sys-dmac2", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR2, + .bit = 17, + }), + }, + { + .name = "sys-dmac1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR2, + .bit = 18, + }), + }, + { + .name = "sys-dmac0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR2, + .bit = 19, + }), + }, + { + .name = "sceg-pub", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR2, + .bit = 29, + }), + }, + { + .name = "cmt3", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR3, + .bit = 0, + }), + }, + { + .name = "cmt2", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR3, + .bit = 1, + }), + }, + { + .name = "cmt1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR3, + .bit = 2, + }), + }, + { + .name = "cmt0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR3, + .bit = 3, + }), + }, + { + .name = "tpu0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR3, + .bit = 4, + }), + }, + { + .name = "scif2", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR3, + .bit = 10, + }), + }, + { + .name = "sdif3", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR3, + .bit = 11, + }), + }, + { + .name = "sdif2", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR3, + .bit = 12, + }), + }, + { + .name = "sdif1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR3, + .bit = 13, + }), + }, + { + .name = "sdif0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR3, + .bit = 14, + }), + }, + { + .name = "pcie1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR3, + .bit = 18, + }), + }, + { + .name = "pcie0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR3, + .bit = 19, + }), + }, + { + .name = "usb-dmac30", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR3, + .bit = 26, + }), + }, + { + .name = "usb3-if0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR3, + .bit = 28, + }), + }, + { + .name = "usb-dmac31", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR3, + .bit = 29, + }), + }, + { + .name = "usb-dmac0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR3, + .bit = 30, + }), + }, + { + .name = "usb-dmac1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR3, + .bit = 31, + }), + }, + { + .name = "rwdt", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR4, + .bit = 2, + }), + }, + { + .name = "intc-ex", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR4, + .bit = 7, + }), + }, + { + .name = "intc-ap", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR4, + .bit = 8, + }), + }, + { + .name = "audmac1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR5, + .bit = 1, + }), + }, + { + .name = "audmac0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR5, + .bit = 2, + }), + }, + { + .name = "drif31", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR5, + .bit = 8, + }), + }, + { + .name = "drif30", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR5, + .bit = 9, + }), + }, + { + .name = "drif21", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR5, + .bit = 10, + }), + }, + { + .name = "drif20", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR5, + .bit = 11, + }), + }, + { + .name = "drif11", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR5, + .bit = 12, + }), + }, + { + .name = "drif10", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR5, + .bit = 13, + }), + }, + { + .name = "drif01", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR5, + .bit = 14, + }), + }, + { + .name = "drif00", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR5, + .bit = 15, + }), + }, + { + .name = "hscif4", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR5, + .bit = 16, + }), + }, + { + .name = "hscif3", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR5, + .bit = 17, + }), + }, + { + .name = "hscif2", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR5, + .bit = 18, + }), + }, + { + .name = "hscif1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR5, + .bit = 19, + }), + }, + { + .name = "hscif0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR5, + .bit = 20, + }), + }, + { + .name = "thermal", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR5, + .bit = 22, + .defer_initialization = true, + }), + }, + { + .name = "pwm", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR5, + .bit = 23, + }), + }, + { + .name = "fcpvd2", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR6, + .bit = 1, + }), + }, + { + .name = "fcpvd1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR6, + .bit = 2, + }), + }, + { + .name = "fcpvd0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR6, + .bit = 3, + }), + }, + { + .name = "fcpvb1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR6, + .bit = 6, + }), + }, + { + .name = "fcpvb0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR6, + .bit = 7, + }), + }, + { + .name = "fcpvi1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR6, + .bit = 10, + }), + }, + { + .name = "fcpvi0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR6, + .bit = 11, + }), + }, + { + .name = "fcpf1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR6, + .bit = 14, + }), + }, + { + .name = "fcpf0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR6, + .bit = 15, + }), + }, + { + .name = "fcpcs", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR6, + .bit = 19, + }), + }, + { + .name = "vspd2", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR6, + .bit = 21, + }), + }, + { + .name = "vspd1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR6, + .bit = 22, + }), + }, + { + .name = "vspd0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR6, + .bit = 23, + }), + }, + { + .name = "vspbc", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR6, + .bit = 24, + }), + }, + { + .name = "vspbd", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR6, + .bit = 26, + }), + }, + { + .name = "vspi1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR6, + .bit = 30, + }), + }, + { + .name = "vspi0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR6, + .bit = 31, + }), + }, + { + .name = "ehci3", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR7, + .bit = 0, + }), + }, + { + .name = "ehci2", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR7, + .bit = 1, + }), + }, + { + .name = "ehci1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR7, + .bit = 2, + }), + }, + { + .name = "ehci0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR7, + .bit = 3, + }), + }, + { + .name = "hsusb", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR7, + .bit = 4, + }), + }, + { + .name = "hsusb3", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR7, + .bit = 5, + }), + }, + { + .name = "cmm3", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR7, + .bit = 8, + }), + }, + { + .name = "cmm2", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR7, + .bit = 9, + }), + }, + { + .name = "cmm1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR7, + .bit = 10, + }), + }, + { + .name = "cmm0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR7, + .bit = 11, + }), + }, + { + .name = "csi20", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR7, + .bit = 14, + }), + }, + { + .name = "csi41", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR7, + .bit = 15, + }), + }, + { + .name = "csi40", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR7, + .bit = 16, + }), + }, + { + .name = "du3", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR7, + .bit = 21, + }), + }, + { + .name = "du2", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR7, + .bit = 22, + }), + }, + { + .name = "du1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR7, + .bit = 23, + }), + }, + { + .name = "du0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR7, + .bit = 24, + }), + }, + { + .name = "lvds", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR7, + .bit = 27, + }), + }, + { + .name = "hdmi1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR7, + .bit = 28, + }), + }, + { + .name = "hdmi0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR7, + .bit = 29, + }), + }, + { + .name = "vin7", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR8, + .bit = 4, + }), + }, + { + .name = "vin6", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR8, + .bit = 5, + }), + }, + { + .name = "vin5", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR8, + .bit = 6, + }), + }, + { + .name = "vin4", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR8, + .bit = 7, + }), + }, + { + .name = "vin3", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR8, + .bit = 8, + }), + }, + { + .name = "vin2", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR8, + .bit = 9, + }), + }, + { + .name = "vin1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR8, + .bit = 10, + }), + }, + { + .name = "vin0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR8, + .bit = 11, + }), + }, + { + .name = "etheravb", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR8, + .bit = 12, + }), + }, + { + .name = "sata0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR8, + .bit = 15, + }), + }, + { + .name = "imr3", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR8, + .bit = 20, + }), + }, + { + .name = "imr2", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR8, + .bit = 21, + }), + }, + { + .name = "imr1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR8, + .bit = 22, + }), + }, + { + .name = "imr0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR8, + .bit = 23, + }), + }, + { + .name = "gpio7", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR9, + .bit = 5, + }), + }, + { + .name = "gpio6", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR9, + .bit = 6, + }), + }, + { + .name = "gpio5", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR9, + .bit = 7, + }), + }, + { + .name = "gpio4", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR9, + .bit = 8, + }), + }, + { + .name = "gpio3", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR9, + .bit = 9, + }), + }, + { + .name = "gpio2", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR9, + .bit = 10, + }), + }, + { + .name = "gpio1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR9, + .bit = 11, + }), + }, + { + .name = "gpio0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR9, + .bit = 12, + }), + }, + { + .name = "can-fd", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR9, + .bit = 14, + }), + }, + { + .name = "can-if1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR9, + .bit = 15, + }), + }, + { + .name = "can-if0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR9, + .bit = 16, + }), + }, + { + .name = "i2c6", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR9, + .bit = 18, + }), + }, + { + .name = "i2c5", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR9, + .bit = 19, + }), + }, + { + .name = "i2c-dvfs", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR9, + .bit = 26, + }), + }, + { + .name = "i2c4", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR9, + .bit = 27, + }), + }, + { + .name = "i2c3", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR9, + .bit = 28, + }), + }, + { + .name = "i2c2", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR9, + .bit = 29, + }), + }, + { + .name = "i2c1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR9, + .bit = 30, + }), + }, + { + .name = "i2c0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR9, + .bit = 31, + }), + }, + { + .name = "ssi-all", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 5, + }), + }, + { + .name = "ssi9", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 6, + }), + }, + { + .name = "ssi8", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 7, + }), + }, + { + .name = "ssi7", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 8, + }), + }, + { + .name = "ssi6", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 9, + }), + }, + { + .name = "ssi5", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 10, + }), + }, + { + .name = "ssi4", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 11, + }), + }, + { + .name = "ssi3", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 12, + }), + }, + { + .name = "ssi2", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 13, + }), + }, + { + .name = "ssi1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 14, + }), + }, + { + .name = "ssi0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 15, + }), + }, + { + .name = "scu-all", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 17, + }), + }, + { + .name = "scu-dvc1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 18, + }), + }, + { + .name = "scu-dvc0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 19, + }), + }, + { + .name = "scu-ctu1-mix1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 20, + }), + }, + { + .name = "scu-ctu0-mix0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 21, + }), + }, + { + .name = "scu-src9", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 22, + }), + }, + { + .name = "scu-src8", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 23, + }), + }, + { + .name = "scu-src7", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 24, + }), + }, + { + .name = "scu-src6", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 25, + }), + }, + { + .name = "scu-src5", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 26, + }), + }, + { + .name = "scu-src4", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 27, + }), + }, + { + .name = "scu-src3", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 28, + }), + }, + { + .name = "scu-src2", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 29, + }), + }, + { + .name = "scu-src1", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 30, + }), + }, + { + .name = "scu-src0", + .data = &((struct mod_rcar_mstp_clock_dev_config) { + .control_reg = CPG_SMSTPCR10, + .bit = 31, + }), + }, + { 0 }, /* Termination description. */ +}; + +static const struct fwk_element *pik_clock_get_element_table + (fwk_id_t module_id) +{ + return pik_clock_element_table; +} + +/* TFA mstp clock default value. */ +struct fwk_module_config config_rcar_mstp_clock = { + .elements = FWK_MODULE_DYNAMIC_ELEMENTS(pik_clock_get_element_table), + .data = &((struct mod_rcar_mstp_clock_init) { + .smstpcr_init = { + 0x00210000, + 0xFFFFFFFF, + 0x040E2FDC, + 0xFFFFFBDF, + 0x80000004, + 0xC3FFFFFF, + 0xFFFFFFFF, + 0xFFFFFFFF, + 0x01F1FFF5, + 0xFFFFFFFF, + 0xFFFEFFE0, + 0x000000B7 + }, + }), +}; -- GitLab From 7acba4b53ceda58b0039c0f7f9f093986303aa24 Mon Sep 17 00:00:00 2001 From: Nicolas Royer Date: Sun, 27 Sep 2020 17:40:12 +0200 Subject: [PATCH 09/19] rcar/module: add rcar pd_core module and config data Signed-off-by: Tsutomu Muroya Signed-off-by: Nicolas Royer --- .../rcar_pd_core/include/mod_rcar_pd_core.h | 191 +++++++++ product/rcar/module/rcar_pd_core/src/Makefile | 11 + .../rcar_pd_core/src/mod_rcar_pd_core.c | 396 ++++++++++++++++++ .../module/rcar_pd_core/src/rcar_pd_core.c | 128 ++++++ .../module/rcar_pd_core/src/rcar_pd_core.h | 39 ++ product/rcar/scp_ramfw/config_rcar_pd_core.c | 150 +++++++ product/rcar/scp_ramfw/config_rcar_pd_core.h | 27 ++ 7 files changed, 942 insertions(+) create mode 100644 product/rcar/module/rcar_pd_core/include/mod_rcar_pd_core.h create mode 100644 product/rcar/module/rcar_pd_core/src/Makefile create mode 100644 product/rcar/module/rcar_pd_core/src/mod_rcar_pd_core.c create mode 100644 product/rcar/module/rcar_pd_core/src/rcar_pd_core.c create mode 100644 product/rcar/module/rcar_pd_core/src/rcar_pd_core.h create mode 100644 product/rcar/scp_ramfw/config_rcar_pd_core.c create mode 100644 product/rcar/scp_ramfw/config_rcar_pd_core.h diff --git a/product/rcar/module/rcar_pd_core/include/mod_rcar_pd_core.h b/product/rcar/module/rcar_pd_core/include/mod_rcar_pd_core.h new file mode 100644 index 000000000..d0773b84c --- /dev/null +++ b/product/rcar/module/rcar_pd_core/include/mod_rcar_pd_core.h @@ -0,0 +1,191 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_RCAR_PD_CORE_H +#define MOD_RCAR_PD_CORE_H + +#include +#include +#include +#include +#include + +#define CORE_PER_CLUSTER_COUNT_MAX 8 + +/*! + * \addtogroup GroupRCARModule RCAR Product Modules + * @{ + */ + +/*! + * \defgroup GroupRCARPdCore PD Core + * @{ + */ + +/*! + * \brief Indexes of the interfaces exposed by the module. + */ +enum mod_rcar_ps_sysc_api_idx { + /*! Power domain driver API */ + MOD_RCAR_PD_SYSC_API_IDX_POWER_DOMAIN_DRIVER, + /*! interrupt Service Routine driver API */ + MOD_RCAR_PD_SYSC_API_IDX_ISR, + /*! System boot API */ + MOD_RCAR_PD_SYSC_API_IDX_BOOT, + /*! Number of exposed interfaces */ + MOD_RCAR_PD_SYSC_API_IDX_COUNT, +}; + +/*! + * \brief Power domain descriptor. + */ +struct mod_rcar_pd_sysc { + /*! Base address of the registers */ + uintptr_t reg_base; + +}; + +/*! + * \brief core module configuration + */ +struct mod_rcar_pd_core_config { + /*! Identifier of the power domain notification to register elements for */ + const fwk_id_t pd_notification_id; + + /*! + * Identifier of the source module or element that is expected to send power + * domain notifications. + */ + fwk_id_t pd_source_id; +}; + +/*! + * \brief Configuration data of a power domain of the sysc driver module. + */ +struct mod_rcar_pd_core_pd_config { + /*! Power domain type */ + enum mod_pd_type pd_type; + + /*! descriptor */ + struct mod_rcar_pd_sysc ppu; + + /*! + * In the case of a core power domain, identifier of the cluster power + * domain it belongs to. If the power domain is not a core power domain, + * the value of this field is undefined. + */ + fwk_id_t cluster_id; + + /*! + * Flag indicating if this domain should be powered on during element + * init. This flag is only supported for device and system PPUs and should + * not be set for any other type. + */ + bool default_power_on; + + /*! + * \brief Identifier of an entity wishing to be notified when the + * transitions out of the OFF state. + * + * \note This field may be set to \ref FWK_ID_NONE, in which case no + * observer will be set. + */ + fwk_id_t observer_id; + + /*! + * \brief Identifier of the power state observer API implemented by + * \ref observer_id. + */ + fwk_id_t observer_api; + +}; + +/*! + * \brief SYSC Power State Observer API. + * + * \details This API should be implemented by any modules that should be + * notified when a changes state. + */ +struct mod_rcar_pd_sysc_power_state_observer_api { + /*! + * \brief Called after a has turned on. + * + * \param param Generic configurable parameter. + */ + void (*post_ppu_on)(void *param); +}; + +/*! + * @cond + */ + +/* Power domain context */ +struct rcar_pd_sysc_pd_ctx { + /* Power domain configuration data */ + const struct mod_rcar_pd_core_pd_config *config; + + /* Identifier of the entity bound to the power domain driver API */ + fwk_id_t bound_id; + + /* Power module driver input API */ + struct mod_pd_driver_input_api *pd_driver_input_api; + + /* Context of the parent power domain (used only for core power domains) */ + struct rcar_pd_sysc_pd_ctx *parent_pd_ctx; + + /* Pointer to the power state observer API */ + const struct mod_rcar_pd_sysc_power_state_observer_api *observer_api; + + /* Context data specific to the type of power domain */ + void *data; + /* Power Domain current state*/ + unsigned int current_state; +}; + +/* Cluster power domain specific context */ +struct rcar_pd_sysc_cluster_pd_ctx { + /* + * Table of pointers to the contexts of the cores being part of the + * cluster. + */ + struct rcar_pd_sysc_pd_ctx *core_pd_ctx_table[CORE_PER_CLUSTER_COUNT_MAX]; + + /* Number of cores */ + unsigned int core_count; +}; + +/* Module context */ +struct rcar_pd_sysc_ctx { + /* Table of the power domain contexts */ + struct rcar_pd_sysc_pd_ctx *pd_ctx_table; + + /* Number of power domains */ + size_t pd_ctx_table_size; + + /* Log API */ + struct mod_log_api *log_api; +}; + +/* + * Internal variables + */ + +#define MODE_UNSUPPORTED ~0U + +/*! + * @endcond + */ + +/*! + * @} + */ + +/*! + * @} + */ + +#endif /* MOD_RCAR_PD_CORE_H */ diff --git a/product/rcar/module/rcar_pd_core/src/Makefile b/product/rcar/module/rcar_pd_core/src/Makefile new file mode 100644 index 000000000..a05114123 --- /dev/null +++ b/product/rcar/module/rcar_pd_core/src/Makefile @@ -0,0 +1,11 @@ +# +# Renesas SCP/MCP Software +# Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +BS_LIB_NAME := RCAR_PD_CORE +BS_LIB_SOURCES = rcar_pd_core.c mod_rcar_pd_core.c + +include $(BS_DIR)/lib.mk diff --git a/product/rcar/module/rcar_pd_core/src/mod_rcar_pd_core.c b/product/rcar/module/rcar_pd_core/src/mod_rcar_pd_core.c new file mode 100644 index 000000000..db9dca756 --- /dev/null +++ b/product/rcar/module/rcar_pd_core/src/mod_rcar_pd_core.c @@ -0,0 +1,396 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static struct rcar_pd_sysc_ctx rcar_pd_sysc_ctx; + +/* + * Functions not specific to any type of power domain + */ + +static int rcar_pd_set_state(fwk_id_t pd_id, unsigned int state) +{ + struct rcar_pd_sysc_pd_ctx *pd_ctx; + unsigned int core; + + pd_ctx = rcar_pd_sysc_ctx.pd_ctx_table + fwk_id_get_element_idx(pd_id); + core = fwk_id_get_element_idx(pd_id); + + switch (state) { + case MOD_PD_STATE_ON: + rcar_pwrc_cpuon(core); + pd_ctx->current_state = state; + break; + + case MOD_PD_STATE_OFF: + rcar_pwrc_cpuoff(core); + pd_ctx->current_state = state; + break; + + default: + FWK_LOG_ERR("[PD] Requested power state (%i) is not supported.", state); + return FWK_E_PARAM; + } + + return FWK_SUCCESS; +} + +static int rcar_pd_get_state(fwk_id_t pd_id, unsigned int *state) +{ + struct rcar_pd_sysc_pd_ctx *pd_ctx; + + pd_ctx = rcar_pd_sysc_ctx.pd_ctx_table + fwk_id_get_element_idx(pd_id); + + *state = pd_ctx->current_state; + return FWK_SUCCESS; +} + +static int rcar_pd_reset(fwk_id_t pd_id) +{ + return FWK_SUCCESS; +} + +static const struct mod_pd_driver_api pd_driver = { + .set_state = rcar_pd_set_state, + .get_state = rcar_pd_get_state, + .reset = rcar_pd_reset, +}; + +/* + * Functions specific to core power domains + */ +static int rcar_core_pd_init(struct rcar_pd_sysc_pd_ctx *pd_ctx) +{ + return FWK_SUCCESS; +} + +static int rcar_core_pd_set_state(fwk_id_t core_pd_id, unsigned int state) +{ + struct rcar_pd_sysc_pd_ctx *pd_ctx; + unsigned int core; + + pd_ctx = rcar_pd_sysc_ctx.pd_ctx_table + fwk_id_get_element_idx(core_pd_id); + core = fwk_id_get_element_idx(core_pd_id); + + switch (state) { + case MOD_PD_STATE_OFF: + rcar_pwrc_cpuoff(core); + pd_ctx->current_state = state; + break; + + case MOD_PD_STATE_ON: + rcar_pwrc_cpuon(core); + pd_ctx->current_state = state; + break; + + case MOD_PD_STATE_SLEEP: + pd_ctx->pd_driver_input_api->report_power_state_transition( + pd_ctx->bound_id, MOD_PD_STATE_ON); + break; + default: + FWK_LOG_ERR("[PD] Requested power state (%i) is not supported.", state); + return FWK_E_PARAM; + } + + return FWK_SUCCESS; +} + +static int rcar_core_pd_reset(fwk_id_t core_pd_id) +{ + return FWK_SUCCESS; +} + +static int rcar_core_pd_prepare_for_system_suspend(fwk_id_t core_pd_id) +{ + mmio_write_32(RCAR_CPGWPR, ~CPU_PWR_OFF); + mmio_write_32(RCAR_CA57CPU0CR, CPU_PWR_OFF); + + mmio_write_32(RCAR_CA57CPUCMCR, MODE_L2_DOWN); + _shutdown_request = R_SUSPEND; + + return FWK_SUCCESS; +} + +static const struct mod_pd_driver_api core_pd_driver = { + .set_state = rcar_core_pd_set_state, + .get_state = rcar_pd_get_state, + .reset = rcar_core_pd_reset, + .prepare_core_for_system_suspend = rcar_core_pd_prepare_for_system_suspend +}; + +/* + * Functions specific to cluster power domains + */ + +static int rcar_cluster_pd_init(struct rcar_pd_sysc_pd_ctx *pd_ctx) +{ + return FWK_SUCCESS; +} + +static int rcar_cluster_pd_set_state(fwk_id_t cluster_pd_id, + unsigned int state) +{ + return FWK_SUCCESS; +} + +static const struct mod_pd_driver_api cluster_pd_driver = { + .set_state = rcar_cluster_pd_set_state, + .get_state = rcar_pd_get_state, + .reset = rcar_pd_reset, +}; + +/* + * Framework handlers + */ + +static int rcar_mod_init(fwk_id_t module_id, unsigned int pd_count, + const void *unused) +{ + rcar_pd_sysc_ctx.pd_ctx_table = fwk_mm_calloc(pd_count, + sizeof(struct rcar_pd_sysc_pd_ctx)); + if (rcar_pd_sysc_ctx.pd_ctx_table == NULL) + return FWK_E_NOMEM; + + rcar_pd_sysc_ctx.pd_ctx_table_size = pd_count; + + return FWK_SUCCESS; +} + +static int rcar_pd_init(fwk_id_t pd_id, unsigned int unused, const void *data) +{ + const struct mod_rcar_pd_core_pd_config *config = data; + struct rcar_pd_sysc_pd_ctx *pd_ctx; + + if (config->pd_type >= MOD_PD_TYPE_COUNT) + return FWK_E_DATA; + + pd_ctx = rcar_pd_sysc_ctx.pd_ctx_table + fwk_id_get_element_idx(pd_id); + pd_ctx->config = config; + pd_ctx->bound_id = FWK_ID_NONE; + + if (config->pd_type == MOD_PD_TYPE_CLUSTER) { + pd_ctx->data = + fwk_mm_calloc(1, sizeof(struct rcar_pd_sysc_cluster_pd_ctx)); + if (pd_ctx->data == NULL) + return FWK_E_NOMEM; + } + + if (config->default_power_on) { + switch (config->pd_type) { + case MOD_PD_TYPE_DEVICE: + /* Fall through */ + case MOD_PD_TYPE_DEVICE_DEBUG: + /* Fall through */ + case MOD_PD_TYPE_SYSTEM: + break;//To Do. add (Default Power ON?) + + default: + assert(false); + return FWK_E_SUPPORT; + } + } + + return FWK_SUCCESS; +} + +static int rcar_post_init(fwk_id_t module_id) +{ + return FWK_SUCCESS; +} + +static int rcar_core_bind(fwk_id_t id, unsigned int round) +{ + int status = FWK_SUCCESS; + struct rcar_pd_sysc_pd_ctx *pd_ctx; + + /* Nothing to do during the first round of calls where the power module + will bind to the power domains of this module. */ + if (round == 0) + return FWK_SUCCESS; + +#if 0 + /* In the case of the module, bind to the log component */ + if (fwk_id_is_type(id, FWK_ID_TYPE_MODULE)) { + status = fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_LOG), + FWK_ID_API(FWK_MODULE_IDX_LOG, 0), + &rcar_pd_sysc_ctx.log_api); + return status; + } +#endif + + pd_ctx = rcar_pd_sysc_ctx.pd_ctx_table + fwk_id_get_element_idx(id); + + if (!fwk_id_is_equal(pd_ctx->config->observer_id, FWK_ID_NONE)) { + if (pd_ctx->config->pd_type != MOD_PD_TYPE_CLUSTER) { + /* State observation only supported for clusters */ + assert(false); + return FWK_E_SUPPORT; + } + + status = fwk_module_bind(pd_ctx->config->observer_id, + pd_ctx->config->observer_api, + &pd_ctx->observer_api); + if (status != FWK_SUCCESS) + return status; + } + + if (fwk_id_is_equal(pd_ctx->bound_id, FWK_ID_NONE)) + return FWK_SUCCESS; + + switch (fwk_id_get_module_idx(pd_ctx->bound_id)) { + case FWK_MODULE_IDX_RCAR_POWER_DOMAIN: + return fwk_module_bind(pd_ctx->bound_id, + mod_pd_api_id_driver_input, + &pd_ctx->pd_driver_input_api); + break; + + case FWK_MODULE_IDX_SYSTEM_POWER: + return fwk_module_bind(pd_ctx->bound_id, + mod_system_power_api_id_pd_driver_input, + &pd_ctx->pd_driver_input_api); + break; + + default: + assert(false); + return FWK_E_SUPPORT; + } +} + +static int rcar_core_process_bind_request(fwk_id_t source_id, + fwk_id_t target_id, fwk_id_t api_id, + const void **api) +{ + struct rcar_pd_sysc_pd_ctx *pd_ctx; + unsigned int api_idx; + bool is_power_domain_module = false; + bool is_system_power_module = false; + + api_idx = fwk_id_get_api_idx(api_id); + + if (api_idx != MOD_RCAR_PD_SYSC_API_IDX_POWER_DOMAIN_DRIVER) + return FWK_E_SUPPORT; + + if (!fwk_module_is_valid_element_id(target_id)) + return FWK_E_PARAM; + + pd_ctx = rcar_pd_sysc_ctx.pd_ctx_table + fwk_id_get_element_idx(target_id); + + /* Allow multiple binding only for device power domain for now */ + if ((pd_ctx->config->pd_type != MOD_PD_TYPE_DEVICE) && + (!fwk_id_is_equal(pd_ctx->bound_id, FWK_ID_NONE))) { + assert(false); + return FWK_E_ACCESS; + } + + is_power_domain_module = (fwk_id_get_module_idx(source_id) == + FWK_MODULE_IDX_RCAR_POWER_DOMAIN); + is_system_power_module = (fwk_id_get_module_idx(source_id) == + FWK_MODULE_IDX_SYSTEM_POWER); + + switch (pd_ctx->config->pd_type) { + case MOD_PD_TYPE_CORE: + if (is_power_domain_module) { + *api = &core_pd_driver; + pd_ctx->bound_id = source_id; + return FWK_SUCCESS; + } + break; + + case MOD_PD_TYPE_CLUSTER: + if (is_power_domain_module) { + *api = &cluster_pd_driver; + pd_ctx->bound_id = source_id; + return FWK_SUCCESS; + } + break; + + case MOD_PD_TYPE_SYSTEM: + if (is_power_domain_module || is_system_power_module) { + *api = &pd_driver; + pd_ctx->bound_id = source_id; + return FWK_SUCCESS; + } + break; + + default: + if (is_power_domain_module) + pd_ctx->bound_id = source_id; + *api = &pd_driver; + return FWK_SUCCESS; + } + + pd_ctx->bound_id = FWK_ID_NONE; + return FWK_E_ACCESS; +} + +static int rcar_core_process_notification( + const struct fwk_event *event, + struct fwk_event *resp_event) +{ + const struct mod_rcar_pd_core_config *module_config; + struct rcar_pd_sysc_pd_ctx *pd_ctx; + struct mod_pd_power_state_transition_notification_params *params; + + assert(fwk_id_is_type(event->target_id, FWK_ID_TYPE_ELEMENT)); + module_config = + fwk_module_get_data(fwk_id_build_module_id(event->target_id)); + assert( + fwk_id_is_equal( + event->id, + module_config->pd_notification_id)); + (void)module_config; + + params = (struct mod_pd_power_state_transition_notification_params *) + event->params; + + if (params->state != MOD_PD_STATE_ON) + return FWK_SUCCESS; + + pd_ctx = rcar_pd_sysc_ctx.pd_ctx_table + + fwk_id_get_element_idx(event->target_id); + + switch (pd_ctx->config->pd_type) { + case MOD_PD_TYPE_CORE: + return rcar_core_pd_init(pd_ctx); + + case MOD_PD_TYPE_CLUSTER: + return rcar_cluster_pd_init(pd_ctx); + + default: + return FWK_SUCCESS; + } +} + +const struct fwk_module module_rcar_pd_core = { + .name = "RCAR_PD_CORE", + .type = FWK_MODULE_TYPE_DRIVER, + .api_count = MOD_RCAR_PD_SYSC_API_IDX_COUNT, + .init = rcar_mod_init, + .element_init = rcar_pd_init, + .post_init = rcar_post_init, + .bind = rcar_core_bind, + .process_bind_request = rcar_core_process_bind_request, + .process_notification = rcar_core_process_notification, +}; diff --git a/product/rcar/module/rcar_pd_core/src/rcar_pd_core.c b/product/rcar/module/rcar_pd_core/src/rcar_pd_core.c new file mode 100644 index 000000000..ac7efe556 --- /dev/null +++ b/product/rcar/module/rcar_pd_core/src/rcar_pd_core.c @@ -0,0 +1,128 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +void rcar_pwrc_cpuoff(unsigned int core) +{ + uintptr_t off_reg; + uint32_t cpu_no; + + mdelay(300); + + if (core & CLUSTER_CPU_MASK) { + /* A53 side */ + off_reg = (uintptr_t)RCAR_CA53CPU0CR; + cpu_no = (core & (~CLUSTER_CPU_MASK)); + } else { + /* A57 side */ + off_reg = (uintptr_t)RCAR_CA57CPU0CR; + cpu_no = core; + } + mmio_write_32(RCAR_CPGWPR, ~((uint32_t)CPU_PWR_OFF)); + mmio_write_32(off_reg + (cpu_no * 0x0010U), (uint32_t)CPU_PWR_OFF); +} + +void rcar_pwrc_cpuon(uint32_t core) +{ + uintptr_t res_reg; + uint32_t res_data; + uintptr_t on_reg; + uint32_t upper_value; + uint32_t wup_data; + uint32_t cpu_no; + + if (core & CLUSTER_CPU_MASK) { + /* A53 side */ + res_reg = (uintptr_t)RCAR_CA53RESCNT; + on_reg = (uintptr_t)RCAR_CA53WUPCR; + upper_value = 0x5A5A0000U; + cpu_no = (core & (~CLUSTER_CPU_MASK)); +; + } else { + /* A57 side */ + res_reg = (uintptr_t)RCAR_CA57RESCNT; + on_reg = (uintptr_t)RCAR_CA57WUPCR; + upper_value = 0xA5A50000U; + cpu_no = core; + } + + res_data = mmio_read_32(res_reg) | upper_value; + SCU_power_up(core); + wup_data = (uint32_t)((uint32_t)1U << cpu_no); + mmio_write_32(RCAR_CPGWPR, ~wup_data); + mmio_write_32(on_reg, wup_data); + /* Dessert to CPU reset */ + mmio_write_32(res_reg, (res_data & (~((uint32_t)1U << (3U - cpu_no))))); +} + +void SCU_power_up(uint32_t core) +{ + uint32_t reg_SYSC_bit; + uintptr_t reg_PWRONCR; + volatile uintptr_t reg_PWRER; + uintptr_t reg_PWRSR; + uintptr_t reg_CPUCMCR; + uintptr_t reg_SYSCIER = (uintptr_t)RCAR_SYSCIER; + uintptr_t reg_SYSCIMR = (uintptr_t)RCAR_SYSCIMR; + volatile uintptr_t reg_SYSCSR = (volatile uintptr_t)RCAR_SYSCSR; + volatile uintptr_t reg_SYSCISR = (volatile uintptr_t)RCAR_SYSCISR; + volatile uintptr_t reg_SYSCISCR = (volatile uintptr_t)RCAR_SYSCISCR; + + if (core < 4) { + /* CA57-SCU */ + reg_SYSC_bit = (uint32_t)BIT_CA57_SCU; + reg_PWRONCR = (uintptr_t)RCAR_PWRONCR5; + reg_PWRER = (volatile uintptr_t)RCAR_PWRER5; + reg_PWRSR = (uintptr_t)RCAR_PWRSR5; + reg_CPUCMCR = (uintptr_t)RCAR_CA57CPUCMCR; + } else { + /* CA53-SCU */ + reg_SYSC_bit = (uint32_t)BIT_CA53_SCU; + reg_PWRONCR = (uintptr_t)RCAR_PWRONCR3; + reg_PWRER = (volatile uintptr_t)RCAR_PWRER3; + reg_PWRSR = (uintptr_t)RCAR_PWRSR3; + reg_CPUCMCR = (uintptr_t)RCAR_CA53CPUCMCR; + } + if ((mmio_read_32(reg_PWRSR) & (uint32_t)STATUS_PWRDOWN) != 0x0000U) { + if (mmio_read_32(reg_CPUCMCR) != 0U) { + mmio_write_32(reg_CPUCMCR, (uint32_t)0x00000000U); + } + /* set SYSCIER and SYSCIMR */ + mmio_write_32(reg_SYSCIER, + (mmio_read_32(reg_SYSCIER) | reg_SYSC_bit)); + mmio_write_32(reg_SYSCIMR, + (mmio_read_32(reg_SYSCIMR) | reg_SYSC_bit)); + do { + /* SYSCSR[1]=1? */ + while ((mmio_read_32(reg_SYSCSR) & (uint32_t)REQ_RESUME) == 0U) + continue; + + /* If SYSCSR[1]=1 then set bit in PWRONCRn to 1 */ + mmio_write_32(reg_PWRONCR, 0x0001U); + } while ((mmio_read_32(reg_PWRER) & 0x0001U) != 0U); + + /* bit in SYSCISR=1 ? */ + while ((mmio_read_32(reg_SYSCISR) & reg_SYSC_bit) == 0U) + continue; + + /* clear bit in SYSCISR */ + mmio_write_32(reg_SYSCISCR, reg_SYSC_bit); + + /* Check the SCU power-up */ + while ((mmio_read_32(reg_PWRSR) & (uint32_t)STATUS_PWRUP) == 0x0000U) + continue; + + } +} diff --git a/product/rcar/module/rcar_pd_core/src/rcar_pd_core.h b/product/rcar/module/rcar_pd_core/src/rcar_pd_core.h new file mode 100644 index 000000000..177e9071d --- /dev/null +++ b/product/rcar/module/rcar_pd_core/src/rcar_pd_core.h @@ -0,0 +1,39 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RCAR_PD_CORE_H +#define RCAR_PD_CORE_H + +/*! + * \cond + * @{ + */ + +#include +#include +#include + +#define CLUSTER_CPU_MASK 0x4 +#define REQ_RESUME ((uint32_t)1U << 1U) +#define BIT_CA53_SCU ((uint32_t)1U << 21U) +#define BIT_CA57_SCU ((uint32_t)1U << 12U) +#define STATUS_PWRDOWN ((uint32_t)1U << 0U) +#define STATUS_PWRUP ((uint32_t)1U << 4U) + +/* + * Interface + */ +void SCU_power_up(uint32_t mpidr); +void rcar_pwrc_cpuoff(uint32_t mpidr); +void rcar_pwrc_cpuon(uint32_t mpidr); + +/*! + * \endcond + * @} + */ + +#endif /* RCAR_PD_CORE_H */ diff --git a/product/rcar/scp_ramfw/config_rcar_pd_core.c b/product/rcar/scp_ramfw/config_rcar_pd_core.c new file mode 100644 index 000000000..4ccc8c8e3 --- /dev/null +++ b/product/rcar/scp_ramfw/config_rcar_pd_core.c @@ -0,0 +1,150 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Maximum core name size including the null terminator */ +#define CORE_NAME_SIZE 12 + +/* Maximum cluster name size including the null terminator */ +#define CLUS_NAME_SIZE 6 + +static const char *core_pd_name_table[RCAR_CORE_PER_CLUSTER_MAX] = { + "CLUS0CORE0", "CLUS0CORE1", "CLUS0CORE2", "CLUS0CORE3", + "CLUS1CORE0", "CLUS1CORE1", "CLUS1CORE2", "CLUS1CORE3", +}; + +static const char *cluster_pd_name_table[2] = { + "CLUS0", "CLUS1", +}; + +/* Module configuration data */ +static struct mod_rcar_pd_core_config rcar_pd_core_config_data = { + .pd_notification_id = FWK_ID_NOTIFICATION_INIT( + FWK_MODULE_IDX_RCAR_POWER_DOMAIN, + MOD_PD_NOTIFICATION_IDX_POWER_STATE_TRANSITION), +}; + +static struct fwk_element rcar_pd_core_system_element_table[] = { + [0] = { + .name = "SYS0", + .data = &((struct mod_rcar_pd_core_pd_config) { + .pd_type = MOD_PD_TYPE_SYSTEM, + .observer_id = FWK_ID_NONE_INIT, + }), + }, + [1] = { + .name = "SYS1", + .data = &((struct mod_rcar_pd_core_pd_config) { + .pd_type = MOD_PD_TYPE_SYSTEM, + .observer_id = FWK_ID_NONE_INIT, + }), + }, +}; + +static const struct fwk_element *rcar_pd_core_get_element_table(fwk_id_t mod) +{ + struct fwk_element *element_table, *element; + struct mod_rcar_pd_core_pd_config *pd_config_table, *pd_config; + unsigned int core_idx; + unsigned int cluster_idx=0; + unsigned int core_count; + unsigned int cluster_count; + unsigned int core_element_count = 0; + + core_count = CPU_CORE_MAX; + cluster_count = CPU_CLUSTER_MAX; + + /* + * Allocate element descriptors based on: + * Number of cores + * + Number of cluster descriptors + * + Number of system power domain descriptors + * + 1 terminator descriptor + */ + element_table = fwk_mm_calloc(core_count + cluster_count + + FWK_ARRAY_SIZE(rcar_pd_core_system_element_table) + 1, + sizeof(struct fwk_element)); + if (element_table == NULL) + return NULL; + + pd_config_table = fwk_mm_calloc(core_count + cluster_count, + sizeof(struct mod_rcar_pd_core_pd_config)); + if (pd_config_table == NULL) + return NULL; + + for (core_idx = 0; core_idx < core_count; core_idx++) { + element = &element_table[core_element_count]; + pd_config = &pd_config_table[core_element_count]; + + element->name = fwk_mm_alloc(CORE_NAME_SIZE, 1); + if (element->name == NULL) + return NULL; + + element->name = core_pd_name_table[core_idx]; + element->data = pd_config; + + pd_config->pd_type = MOD_PD_TYPE_CORE; + pd_config->cluster_id = + FWK_ID_ELEMENT(FWK_MODULE_IDX_RCAR_PD_CORE, + cluster_idx); + pd_config->observer_id = FWK_ID_NONE; + core_element_count++; + } + + for (cluster_idx = 0; cluster_idx < cluster_count; cluster_idx++) { + element = &element_table[core_element_count]; + pd_config = &pd_config_table[core_element_count]; + + element->name = fwk_mm_alloc(CLUS_NAME_SIZE, 1); + if (element->name == NULL) + return NULL; + + + element->name = cluster_pd_name_table[cluster_idx]; + element->data = pd_config; + pd_config->pd_type = MOD_PD_TYPE_CLUSTER; + pd_config->observer_id = FWK_ID_NONE; + pd_config->observer_api = FWK_ID_NONE; + core_element_count++; + } + + memcpy(&element_table[core_count + cluster_count], + rcar_pd_core_system_element_table, + sizeof(rcar_pd_core_system_element_table)); + + /* + * Configure pd_source_id with the SYSTOP identifier from the power domain + * module which is dynamically defined based on the number of cores. + */ + rcar_pd_core_config_data.pd_source_id = fwk_id_build_element_id( + fwk_module_id_rcar_power_domain, + core_count + CONFIG_POWER_DOMAIN_CHILD_CLUSTER0); + + return element_table; +} + +/* + * Power module configuration data + */ +const struct fwk_module_config config_rcar_pd_core = { + .elements = FWK_MODULE_DYNAMIC_ELEMENTS(rcar_pd_core_get_element_table), + .data = &rcar_pd_core_config_data, +}; diff --git a/product/rcar/scp_ramfw/config_rcar_pd_core.h b/product/rcar/scp_ramfw/config_rcar_pd_core.h new file mode 100644 index 000000000..612008ef9 --- /dev/null +++ b/product/rcar/scp_ramfw/config_rcar_pd_core.h @@ -0,0 +1,27 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef CONFIG_RCAR_PD_CORE_H +#define CONFIG_RCAR_PD_CORE_H + +enum rcar_pd_core_element_idx { + RCAR_PD_CORE_ELEMENT_IDX_CPU0, + RCAR_PD_CORE_ELEMENT_IDX_CPU1, + RCAR_PD_CORE_ELEMENT_IDX_CPU2, + RCAR_PD_CORE_ELEMENT_IDX_CPU3, + RCAR_PD_CORE_ELEMENT_IDX_CPU4, + RCAR_PD_CORE_ELEMENT_IDX_CPU5, + RCAR_PD_CORE_ELEMENT_IDX_CPU6, + RCAR_PD_CORE_ELEMENT_IDX_CPU7, + RCAR_PD_CORE_ELEMENT_IDX_CLU0, + RCAR_PD_CORE_ELEMENT_IDX_CLU1 +}; + +#define CPU_CORE_MAX 8 +#define CPU_CLUSTER_MAX 2 + +#endif /* CONFIG_RCAR_PD_CORE_H */ -- GitLab From 29c79d3a3f3af7bd05e466db37e7ff3985ea5414 Mon Sep 17 00:00:00 2001 From: Nicolas Royer Date: Sun, 27 Sep 2020 17:40:31 +0200 Subject: [PATCH 10/19] rcar/module: add rcar pd_sysc module and config data Signed-off-by: Tsutomu Muroya Signed-off-by: Nicolas Royer --- .../rcar_pd_sysc/include/mod_rcar_pd_sysc.h | 83 +++++++ product/rcar/module/rcar_pd_sysc/src/Makefile | 11 + .../rcar_pd_sysc/src/mod_rcar_pd_sysc.c | 203 ++++++++++++++++++ .../module/rcar_pd_sysc/src/rcar_pd_sysc.c | 101 +++++++++ .../module/rcar_pd_sysc/src/rcar_pd_sysc.h | 68 ++++++ product/rcar/scp_ramfw/config_rcar_pd_sysc.c | 118 ++++++++++ product/rcar/scp_ramfw/config_rcar_pd_sysc.h | 47 ++++ 7 files changed, 631 insertions(+) create mode 100644 product/rcar/module/rcar_pd_sysc/include/mod_rcar_pd_sysc.h create mode 100644 product/rcar/module/rcar_pd_sysc/src/Makefile create mode 100644 product/rcar/module/rcar_pd_sysc/src/mod_rcar_pd_sysc.c create mode 100644 product/rcar/module/rcar_pd_sysc/src/rcar_pd_sysc.c create mode 100644 product/rcar/module/rcar_pd_sysc/src/rcar_pd_sysc.h create mode 100644 product/rcar/scp_ramfw/config_rcar_pd_sysc.c create mode 100644 product/rcar/scp_ramfw/config_rcar_pd_sysc.h diff --git a/product/rcar/module/rcar_pd_sysc/include/mod_rcar_pd_sysc.h b/product/rcar/module/rcar_pd_sysc/include/mod_rcar_pd_sysc.h new file mode 100644 index 000000000..c4090a569 --- /dev/null +++ b/product/rcar/module/rcar_pd_sysc/include/mod_rcar_pd_sysc.h @@ -0,0 +1,83 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_RCAR_PD_SYSC_H +#define MOD_RCAR_PD_SYSC_H + +#include +#include +#include + +/*! + * \addtogroup GroupRCARModule RCAR Product Modules + * @{ + */ + +/*! + * \defgroup GroupRCARPdSysc SYSC Driver + * @{ + */ + +/*! + * @cond + */ + +/* Power domain context */ +struct rcar_sysc_pd_ctx { + /* Power domain configuration data */ + const struct mod_rcar_pd_sysc_config *config; + + /* Identifier of the entity bound to the power domain driver API */ + fwk_id_t bound_id; + + /* Power module driver input API */ + struct mod_pd_driver_input_api *pd_driver_input_api; + /* Power Domain current state*/ + unsigned int current_state; +}; + +/* Module context */ +struct rcar_sysc_ctx { + /* Table of the power domain contexts */ + struct rcar_sysc_pd_ctx *pd_ctx_table; + + /* Log API */ + struct mod_log_api *log_api; +}; + +/*! + * @endcond + */ + +/*! + * \brief Configuration data of a power domain of the SYSC module. + */ +struct mod_rcar_pd_sysc_config { + /*! Power domain type */ + enum mod_pd_type pd_type; + /*! Offset of PWRSR register for this area */ + unsigned int chan_offs; + /*! Bit in PWR* (except for PWRUP in PWRSR) */ + unsigned char chan_bit; + /*! Bit in SYSCI*R */ + unsigned char isr_bit; + + /*! + * Flag indicating if this domain should be powered on during element init. + */ + bool default_power_on; +}; + +/*! + * @} + */ + +/*! + * @} + */ + +#endif /* MOD_RCAR_PD_SYSC_H */ diff --git a/product/rcar/module/rcar_pd_sysc/src/Makefile b/product/rcar/module/rcar_pd_sysc/src/Makefile new file mode 100644 index 000000000..41d2edb7b --- /dev/null +++ b/product/rcar/module/rcar_pd_sysc/src/Makefile @@ -0,0 +1,11 @@ +# +# Renesas SCP/MCP Software +# Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +BS_LIB_NAME := RCAR_PS_SYSC +BS_LIB_SOURCES = rcar_pd_sysc.c mod_rcar_pd_sysc.c + +include $(BS_DIR)/lib.mk diff --git a/product/rcar/module/rcar_pd_sysc/src/mod_rcar_pd_sysc.c b/product/rcar/module/rcar_pd_sysc/src/mod_rcar_pd_sysc.c new file mode 100644 index 000000000..2559b579a --- /dev/null +++ b/product/rcar/module/rcar_pd_sysc/src/mod_rcar_pd_sysc.c @@ -0,0 +1,203 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Internal variables + */ +static struct rcar_sysc_ctx rcar_sysc_ctx; + +/* + * Power domain driver interface + */ +static int pd_set_state(fwk_id_t pd_id, unsigned int state) +{ + int status = FWK_SUCCESS; + struct rcar_sysc_pd_ctx *pd_ctx; + + pd_ctx = rcar_sysc_ctx.pd_ctx_table + fwk_id_get_element_idx(pd_id); + + switch (state) { + case MOD_PD_STATE_ON: + status = rcar_sysc_power(pd_ctx, true); + pd_ctx->current_state = state; + break; + + case MOD_PD_STATE_OFF: + status = rcar_sysc_power(pd_ctx, false); + pd_ctx->current_state = state; + break; + + default: + FWK_LOG_ERR("[PD] Requested power state (%i) is not supported.", state); + return FWK_E_PARAM; + } + + return status; +} + +static int pd_get_state(fwk_id_t pd_id, unsigned int *state) +{ + struct rcar_sysc_pd_ctx *pd_ctx; + + pd_ctx = rcar_sysc_ctx.pd_ctx_table + fwk_id_get_element_idx(pd_id); + + *state = pd_ctx->current_state; + return FWK_SUCCESS; +} + +static int pd_reset(fwk_id_t pd_id) +{ + return FWK_SUCCESS; +} + +static const struct mod_pd_driver_api pd_driver = { + .set_state = pd_set_state, + .get_state = pd_get_state, + .reset = pd_reset, +}; + +/* + * Framework handlers + */ + +static int rcar_sysc_mod_init(fwk_id_t module_id, unsigned int pd_count, + const void *unused) +{ + rcar_sysc_ctx.pd_ctx_table = fwk_mm_calloc(pd_count, + sizeof(struct rcar_sysc_pd_ctx)); + if (rcar_sysc_ctx.pd_ctx_table == NULL) + return FWK_E_NOMEM; + + return FWK_SUCCESS; +} + +static int rcar_sysc_pd_init( + fwk_id_t pd_id, unsigned int unused, const void *data) +{ + const struct mod_rcar_pd_sysc_config *config = data; + struct rcar_sysc_pd_ctx *pd_ctx; + + if (config->pd_type >= MOD_PD_TYPE_COUNT) + return FWK_E_DATA; + + pd_ctx = rcar_sysc_ctx.pd_ctx_table + fwk_id_get_element_idx(pd_id); + pd_ctx->config = config; + pd_ctx->bound_id = FWK_ID_NONE; + + switch (config->pd_type) { + case MOD_PD_TYPE_DEVICE: + case MOD_PD_TYPE_DEVICE_DEBUG: + case MOD_PD_TYPE_SYSTEM: + return FWK_SUCCESS; + default: + return FWK_E_SUPPORT; + } +} + +static int rcar_sysc_bind(fwk_id_t id, unsigned int round) +{ + struct rcar_sysc_pd_ctx *pd_ctx; + + /* Nothing to do during the first round of calls where the power module + will bind to the power domains of this module. */ + if (round == 0) + return FWK_SUCCESS; + +#if 0 + /* In the case of the module, bind to the log component */ + if (fwk_module_is_valid_module_id(id)) { + return fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_LOG), + FWK_ID_API(FWK_MODULE_IDX_LOG, 0), + &rcar_sysc_ctx.log_api); + } +#endif + + pd_ctx = rcar_sysc_ctx.pd_ctx_table + fwk_id_get_element_idx(id); + + if (fwk_id_is_equal(pd_ctx->bound_id, FWK_ID_NONE)) + return FWK_SUCCESS; + + switch (fwk_id_get_module_idx(pd_ctx->bound_id)) { + case FWK_MODULE_IDX_RCAR_POWER_DOMAIN: + return fwk_module_bind(pd_ctx->bound_id, + mod_pd_api_id_driver_input, + &pd_ctx->pd_driver_input_api); + break; + + case FWK_MODULE_IDX_SYSTEM_POWER: + return fwk_module_bind(pd_ctx->bound_id, + mod_system_power_api_id_pd_driver_input, + &pd_ctx->pd_driver_input_api); + break; + + default: + assert(false); + return FWK_E_SUPPORT; + } +} + +static int rcar_sysc_process_bind_request(fwk_id_t source_id, + fwk_id_t target_id, fwk_id_t not_used, + const void **api) +{ + struct rcar_sysc_pd_ctx *pd_ctx; + + pd_ctx = rcar_sysc_ctx.pd_ctx_table + fwk_id_get_element_idx(target_id); + + switch (pd_ctx->config->pd_type) { + case MOD_PD_TYPE_SYSTEM: + if (!fwk_id_is_equal(pd_ctx->bound_id, FWK_ID_NONE)) { + assert(false); + return FWK_E_ACCESS; + } + /* Fallthrough */ + + case MOD_PD_TYPE_DEVICE: + case MOD_PD_TYPE_DEVICE_DEBUG: + if (fwk_id_get_module_idx(source_id) == + FWK_MODULE_IDX_RCAR_POWER_DOMAIN) { + pd_ctx->bound_id = source_id; + *api = &pd_driver; + break; + } + if (fwk_id_get_module_idx(source_id) == FWK_MODULE_IDX_SYSTEM_POWER) { + *api = &pd_driver; + break; + } + assert(false); + return FWK_E_ACCESS; + + default: + (void)pd_driver; + return FWK_E_SUPPORT; + } + + return FWK_SUCCESS; +} + +const struct fwk_module module_rcar_pd_sysc = { + .name = "RCAR_PD_SYSC", + .type = FWK_MODULE_TYPE_DRIVER, + .api_count = 1, + .init = rcar_sysc_mod_init, + .element_init = rcar_sysc_pd_init, + .bind = rcar_sysc_bind, + .process_bind_request = rcar_sysc_process_bind_request, +}; diff --git a/product/rcar/module/rcar_pd_sysc/src/rcar_pd_sysc.c b/product/rcar/module/rcar_pd_sysc/src/rcar_pd_sysc.c new file mode 100644 index 000000000..90538baa7 --- /dev/null +++ b/product/rcar/module/rcar_pd_sysc/src/rcar_pd_sysc.c @@ -0,0 +1,101 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include + +/***************************************************************************** + * LOCAL FUNCTIONS + *****************************************************************************/ +static int rcar_sysc_pwr_on_off(struct rcar_sysc_pd_ctx *pd_ctx, bool on) +{ + unsigned int sr_bit, reg_offs; + int k; + + if (on) { + sr_bit = SYSCSR_PONENB; + reg_offs = PWRONCR_OFFS; + } else { + sr_bit = SYSCSR_POFFENB; + reg_offs = PWROFFCR_OFFS; + } + + /* Wait until SYSC is ready to accept a power request */ + for (k = 0; k < SYSCSR_RETRIES; k++) { + if (mmio_read_32(SYSC_BASE_ADDR + SYSCSR) & BIT_SHIFT(sr_bit)) + break; + udelay(SYSCSR_DELAY_US); + } + + if (k == SYSCSR_RETRIES) { + return FWK_E_TIMEOUT; + } + + /* Submit power shutoff or power resume request */ + udelay(SYSCSR_DELAY_US); + + mmio_write_32((SYSC_BASE_ADDR + pd_ctx->config->chan_offs + reg_offs), + BIT_SHIFT(pd_ctx->config->chan_bit)); + return 0; +} + +int rcar_sysc_power(struct rcar_sysc_pd_ctx *pd_ctx, bool on) +{ + unsigned int isr_mask = BIT_SHIFT(pd_ctx->config->isr_bit); + unsigned int chan_mask = BIT_SHIFT(pd_ctx->config->chan_bit); + unsigned int status; + int ret = 0; + int k; + uint32_t syscier, syscimr; + + syscimr = mmio_read_32(SYSC_BASE_ADDR + SYSCIMR); + syscier = mmio_read_32(SYSC_BASE_ADDR + SYSCIER); + mmio_write_32((SYSC_BASE_ADDR + SYSCIMR), (syscimr | isr_mask)); + mmio_write_32((SYSC_BASE_ADDR + SYSCIER), (syscier | isr_mask)); + mmio_write_32((SYSC_BASE_ADDR + SYSCISCR), isr_mask); + + /* Submit power shutoff or resume request until it was accepted */ + for (k = 0; k < PWRER_RETRIES; k++) { + ret = rcar_sysc_pwr_on_off(pd_ctx, on); + if (ret) + return ret; + + status = mmio_read_32(SYSC_BASE_ADDR + + pd_ctx->config->chan_offs + PWRER_OFFS); + if (!(status & chan_mask)) { + break; + } + + udelay(PWRER_DELAY_US); + } + + if (k == PWRER_RETRIES) + return FWK_E_BUSY; + + /* Wait until the power shutoff or resume request has completed * */ + for (k = 0; k < SYSCISR_RETRIES; k++) { + if (mmio_read_32(SYSC_BASE_ADDR + SYSCISR) & isr_mask) + break; + udelay(SYSCISR_DELAY_US); + } + + if (k == SYSCISR_RETRIES){ + ret = FWK_E_BUSY; + } + + mmio_write_32((SYSC_BASE_ADDR + SYSCISCR), isr_mask); + + return ret; +} + +int rcar_sysc_power_get(struct rcar_sysc_pd_ctx *pd_ctx, unsigned int *statee) +{ + return FWK_SUCCESS; +} diff --git a/product/rcar/module/rcar_pd_sysc/src/rcar_pd_sysc.h b/product/rcar/module/rcar_pd_sysc/src/rcar_pd_sysc.h new file mode 100644 index 000000000..e68d91ad5 --- /dev/null +++ b/product/rcar/module/rcar_pd_sysc/src/rcar_pd_sysc.h @@ -0,0 +1,68 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RCAR_PD_SYSC_H +#define RCAR_PD_SYSC_H + +/*! + * \cond + * @{ + */ + +#include +#include +#include +#include + +#define BIT_SHIFT(nr) (1UL << (nr)) + +/* SYSC Common */ +#define SYSC_BASE_ADDR (0xE6180000U) /* SYSC Base Address*/ +#define SYSCSR 0x00 /* SYSC Status Register */ +#define SYSCISR 0x04 /* Interrupt Status Register */ +#define SYSCISCR 0x08 /* Interrupt Status Clear Register */ +#define SYSCIER 0x0c /* Interrupt Enable Register */ +#define SYSCIMR 0x10 /* Interrupt Mask Register */ + +/* SYSC Status Register */ +#define SYSCSR_PONENB 1 /* Ready for power resume requests */ +#define SYSCSR_POFFENB 0 /* Ready for power shutoff requests */ + +/* + * Power Control Register Offsets inside the register block for each domain + * Note: The "CR" registers for ARM cores exist on H1 only + * Use WFI to power off, CPG/APMU to resume ARM cores on R-Car Gen2 + * Use PSCI on R-Car Gen3 + */ +#define PWRSR_OFFS 0x00 /* Power Status Register */ +#define PWROFFCR_OFFS 0x04 /* Power Shutoff Control Register */ +#define PWROFFSR_OFFS 0x08 /* Power Shutoff Status Register */ +#define PWRONCR_OFFS 0x0c /* Power Resume Control Register */ +#define PWRONSR_OFFS 0x10 /* Power Resume Status Register */ +#define PWRER_OFFS 0x14 /* Power Shutoff/Resume Error */ + +#define SYSCSR_RETRIES 1000 +#define SYSCSR_DELAY_US 1 + +#define PWRER_RETRIES 1000 +#define PWRER_DELAY_US 1 + +#define SYSCISR_RETRIES 1000 +#define SYSCISR_DELAY_US 1 + +/* + * Interface + */ +int rcar_sysc_power(struct rcar_sysc_pd_ctx *pd_ctx, bool on); +int rcar_sysc_power_get(struct rcar_sysc_pd_ctx *pd_ctx, unsigned int *statee); + +/*! + * \endcond + * @} + */ + +#endif /* RCAR_PD_SYSC_H */ diff --git a/product/rcar/scp_ramfw/config_rcar_pd_sysc.c b/product/rcar/scp_ramfw/config_rcar_pd_sysc.c new file mode 100644 index 000000000..2471f2ae2 --- /dev/null +++ b/product/rcar/scp_ramfw/config_rcar_pd_sysc.c @@ -0,0 +1,118 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include + +static struct fwk_element rcar_pd_sysc_element_table[] = { + [RCAR_PD_SYSC_ELEMENT_IDX_A3IR] = { + .name = "a3ir", + .data = &((struct mod_rcar_pd_sysc_config) { + .pd_type = MOD_PD_TYPE_DEVICE, + .chan_offs = 0x180, + .chan_bit = 0, + .isr_bit = R8A7795_PD_A3IR, + }), + }, + [RCAR_PD_SYSC_ELEMENT_IDX_3DGE] = { + .name = "3dg-e", + .data = &((struct mod_rcar_pd_sysc_config) { + .pd_type = MOD_PD_TYPE_DEVICE, + .chan_offs = 0x100, + .chan_bit = 4, + .isr_bit = R8A7795_PD_3DG_E, + }), + }, + [RCAR_PD_SYSC_ELEMENT_IDX_3DGD] = { + .name = "3dg-d", + .data = &((struct mod_rcar_pd_sysc_config) { + .pd_type = MOD_PD_TYPE_DEVICE, + .chan_offs = 0x100, + .chan_bit = 3, + .isr_bit = R8A7795_PD_3DG_D, + }), + }, + [RCAR_PD_SYSC_ELEMENT_IDX_3DGC] = { + .name = "3dg-c", + .data = &((struct mod_rcar_pd_sysc_config) { + .pd_type = MOD_PD_TYPE_DEVICE, + .chan_offs = 0x100, + .chan_bit = 2, + .isr_bit = R8A7795_PD_3DG_C, + }), + }, + [RCAR_PD_SYSC_ELEMENT_IDX_3DGB] = { + .name = "3dg-b", + .data = &((struct mod_rcar_pd_sysc_config) { + .pd_type = MOD_PD_TYPE_DEVICE, + .chan_offs = 0x100, + .chan_bit = 1, + .isr_bit = R8A7795_PD_3DG_B, + }), + }, + [RCAR_PD_SYSC_ELEMENT_IDX_3DGA] = { + .name = "3dg-a", + .data = &((struct mod_rcar_pd_sysc_config) { + .pd_type = MOD_PD_TYPE_DEVICE, + .chan_offs = 0x100, + .chan_bit = 0, + .isr_bit = R8A7795_PD_3DG_A, + }), + }, + [RCAR_PD_SYSC_ELEMENT_IDX_A2VC1] = { + .name = "a2vc1", + .data = &((struct mod_rcar_pd_sysc_config) { + .pd_type = MOD_PD_TYPE_DEVICE, + .chan_offs = 0x3c0, + .chan_bit = 1, + .isr_bit = R8A7795_PD_A2VC1, + }), + }, + [RCAR_PD_SYSC_ELEMENT_IDX_A3VC] = { + .name = "a3vc", + .data = &((struct mod_rcar_pd_sysc_config) { + .pd_type = MOD_PD_TYPE_DEVICE, + .chan_offs = 0x380, + .chan_bit = 0, + .isr_bit = R8A7795_PD_A3VC, + }), + }, + [RCAR_PD_SYSC_ELEMENT_IDX_CR7] = { + .name = "cr7", + .data = &((struct mod_rcar_pd_sysc_config) { + .pd_type = MOD_PD_TYPE_DEVICE, + .chan_offs = 0x240, + .chan_bit = 0, + .isr_bit = R8A7795_PD_CR7, + }), + }, + [RCAR_PD_SYSC_ELEMENT_IDX_A3VP] = { + .name = "a3vp", + .data = &((struct mod_rcar_pd_sysc_config) { + .pd_type = MOD_PD_TYPE_DEVICE, + .chan_offs = 0x340, + .chan_bit = 0, + .isr_bit = R8A7795_PD_A3VP, + }), + }, + [RCAR_PD_SYSC_ELEMENT_IDX_COUNT] = { 0 }, /* Termination entry */ +}; + + +static const struct fwk_element *rcar_pd_sysc_get_element_table(fwk_id_t mod) +{ + return rcar_pd_sysc_element_table; +} + +/* + * Power module configuration data + */ +const struct fwk_module_config config_rcar_pd_sysc = { + .elements = FWK_MODULE_DYNAMIC_ELEMENTS(rcar_pd_sysc_get_element_table), +}; diff --git a/product/rcar/scp_ramfw/config_rcar_pd_sysc.h b/product/rcar/scp_ramfw/config_rcar_pd_sysc.h new file mode 100644 index 000000000..5e1ca9eef --- /dev/null +++ b/product/rcar/scp_ramfw/config_rcar_pd_sysc.h @@ -0,0 +1,47 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef CONFIG_RCAR_PD_SYSC_H +#define CONFIG_RCAR_PD_SYSC_H + +enum rcar_pd_sysc_element_idx { + RCAR_PD_SYSC_ELEMENT_IDX_A3IR, + RCAR_PD_SYSC_ELEMENT_IDX_3DGE, + RCAR_PD_SYSC_ELEMENT_IDX_3DGD, + RCAR_PD_SYSC_ELEMENT_IDX_3DGC, + RCAR_PD_SYSC_ELEMENT_IDX_3DGB, + RCAR_PD_SYSC_ELEMENT_IDX_3DGA, + RCAR_PD_SYSC_ELEMENT_IDX_A2VC1, + RCAR_PD_SYSC_ELEMENT_IDX_A3VC, + RCAR_PD_SYSC_ELEMENT_IDX_CR7, + RCAR_PD_SYSC_ELEMENT_IDX_A3VP, + RCAR_PD_SYSC_ELEMENT_IDX_COUNT +}; + +#define R8A7795_PD_CA57_CPU0 0 +#define R8A7795_PD_CA57_CPU1 1 +#define R8A7795_PD_CA57_CPU2 2 +#define R8A7795_PD_CA57_CPU3 3 +#define R8A7795_PD_CA53_CPU0 5 +#define R8A7795_PD_CA53_CPU1 6 +#define R8A7795_PD_CA53_CPU2 7 +#define R8A7795_PD_CA53_CPU3 8 +#define R8A7795_PD_A3VP 9 +#define R8A7795_PD_CA57_SCU 12 +#define R8A7795_PD_CR7 13 +#define R8A7795_PD_A3VC 14 +#define R8A7795_PD_3DG_A 17 +#define R8A7795_PD_3DG_B 18 +#define R8A7795_PD_3DG_C 19 +#define R8A7795_PD_3DG_D 20 +#define R8A7795_PD_CA53_SCU 21 +#define R8A7795_PD_3DG_E 22 +#define R8A7795_PD_A3IR 24 +#define R8A7795_PD_A2VC0 25 /* ES1.x only */ +#define R8A7795_PD_A2VC1 26 + +#endif /* CONFIG_RCAR_PD_SYSC_H */ -- GitLab From 493c7b4de008337202e8434369d50e345626f86a Mon Sep 17 00:00:00 2001 From: Nicolas Royer Date: Sun, 27 Sep 2020 17:40:53 +0200 Subject: [PATCH 11/19] rcar/module: add rcar pmic module and config data Signed-off-by: Tsutomu Muroya Signed-off-by: Nicolas Royer --- .../module/rcar_pmic/include/mod_rcar_pmic.h | 259 ++++++++++++++++++ product/rcar/module/rcar_pmic/src/Makefile | 14 + .../rcar_pmic/src/mod_rcar_pmic_device_api.c | 193 +++++++++++++ .../src/mod_rcar_pmic_device_api_private.h | 16 ++ .../rcar_pmic/src/mod_rcar_pmic_event.c | 98 +++++++ .../src/mod_rcar_pmic_event_private.h | 29 ++ .../rcar_pmic/src/mod_rcar_pmic_module.c | 127 +++++++++ .../src/mod_rcar_pmic_module_private.h | 30 ++ .../rcar_pmic/src/mod_rcar_pmic_private.h | 16 ++ product/rcar/scp_ramfw/config_rcar_pmic.c | 58 ++++ 10 files changed, 840 insertions(+) create mode 100644 product/rcar/module/rcar_pmic/include/mod_rcar_pmic.h create mode 100644 product/rcar/module/rcar_pmic/src/Makefile create mode 100644 product/rcar/module/rcar_pmic/src/mod_rcar_pmic_device_api.c create mode 100644 product/rcar/module/rcar_pmic/src/mod_rcar_pmic_device_api_private.h create mode 100644 product/rcar/module/rcar_pmic/src/mod_rcar_pmic_event.c create mode 100644 product/rcar/module/rcar_pmic/src/mod_rcar_pmic_event_private.h create mode 100644 product/rcar/module/rcar_pmic/src/mod_rcar_pmic_module.c create mode 100644 product/rcar/module/rcar_pmic/src/mod_rcar_pmic_module_private.h create mode 100644 product/rcar/module/rcar_pmic/src/mod_rcar_pmic_private.h create mode 100644 product/rcar/scp_ramfw/config_rcar_pmic.c diff --git a/product/rcar/module/rcar_pmic/include/mod_rcar_pmic.h b/product/rcar/module/rcar_pmic/include/mod_rcar_pmic.h new file mode 100644 index 000000000..9a5f61832 --- /dev/null +++ b/product/rcar/module/rcar_pmic/include/mod_rcar_pmic.h @@ -0,0 +1,259 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_RCAR_PMIC_H +#define MOD_RCAR_PMIC_H + +#include +#include +#include +#include + +/*! + * \ingroup GroupRCARModule + * \defgroup GroupRCARPmic PMIC + * \{ + */ + +/*! + * \defgroup GroupRCARPmicApis APIs + * \{ + */ + +/*! + * \brief Device API. + */ +struct mod_rcar_pmic_device_api { + /*! + * \brief Get the enabled state of a device. + * + * \param device_id Identifier of the device to get the state of. + * \param [out] enabled \c true if the device is enabled, or \c if it is + * disabled. + * + * \retval FWK_SUCCESS The operation succeeded. + * \retval FWK_E_PARAM One or more parameters were invalid. + * \retval FWK_E_STATE The element cannot accept the request. + * \retval FWK_E_HANDLER An error occurred in the device driver. + */ + int (*get_enabled)(fwk_id_t device_id, bool *enabled); + + /*! + * \brief Set the enabled state of a device. + * + * \param device_id Identifier of the device to set the state of. + * \param enable \c true to enable the device, or \c false to disable it. + * + * \retval FWK_SUCCESS The operation succeeded. + * \retval FWK_E_PARAM One or more parameters were invalid. + * \retval FWK_E_STATE The element cannot accept the request. + * \retval FWK_E_HANDLER An error occurred in the device driver. + */ + int (*set_enabled)(fwk_id_t device_id, bool enable); + + /*! + * \brief Set the enabled state of a device. + * + * \param device_id Identifier of the device to set the state of. + * \param enable \c true to enable the device, or \c false to disable it. + * + * \retval FWK_SUCCESS The operation succeeded. + * \retval FWK_E_PARAM One or more parameters were invalid. + * \retval FWK_E_STATE The element cannot accept the request. + * \retval FWK_E_NOMEM The event queue is full. + * \retval FWK_E_PANIC An error in the framework occurred. + */ + int (*set_enabled_async)(fwk_id_t device_id, bool enable); + + /*! + * \brief Get the voltage of a device. + * + * \param device_id Identifier of the device to get the voltage of. + * \param [out] voltage Voltage in mV. + * + * \retval FWK_SUCCESS The operation succeeded. + * \retval FWK_E_PARAM One or more parameters were invalid. + * \retval FWK_E_STATE The element cannot accept the request. + * \retval FWK_E_HANDLER An error occurred in the device driver. + */ + int (*get_voltage)(fwk_id_t device_id, uint64_t *voltage); + + /*! + * \brief Set the voltage of a device. + * + * \param device_id Identifier of the device to set the voltage of. + * \param voltage New voltage in mV. + * + * \retval FWK_SUCCESS The operation succeeded. + * \retval FWK_E_PARAM One or more parameters were invalid. + * \retval FWK_E_STATE The element cannot accept the request. + * \retval FWK_E_HANDLER An error occurred in the device driver. + */ + int (*set_voltage)(fwk_id_t device_id, uint64_t voltage); + + /*! + * \brief Set the voltage of a device. + * + * \param device_id Identifier of the device to set the voltage of. + * \param voltage New voltage in mV. + * + * \retval FWK_SUCCESS The operation succeeded. + * \retval FWK_E_PARAM One or more parameters were invalid. + * \retval FWK_E_STATE The element cannot accept the request. + * \retval FWK_E_NOMEM The event queue is full. + * \retval FWK_E_PANIC An error in the framework occurred. + */ + int (*set_voltage_async)(fwk_id_t device_id, uint64_t voltage); +}; + +/*! + * \brief Driver API. + */ +struct mod_rcar_pmic_driver_api { + /*! + * \brief Set the enabled state of a device. + * + * \param id Identifier of the device to set the state of. + * \param enable \c true to enable the device, or \c false to disable it. + * + * \retval FWK_SUCCESS The operation succeeded. + * \return One of the other driver-defined error codes. + */ + int (*set_enabled)(fwk_id_t id, bool enable); + + /*! + * \brief Get the enabled state of a device. + * + * \param id Identifier of the device to get the state of. + * \param [out] enabled \c true if the device is enabled, or \c if it is + * disabled. + * + * \retval FWK_SUCCESS The operation succeeded. + * \return One of the other driver-defined error codes. + */ + int (*get_enabled)(fwk_id_t id, bool *enabled); + + /*! + * \brief Set the voltage of a device. + * + * \param id Identifier of the device to set the voltage of. + * \param voltage New voltage in millivolts (mV). + * + * \retval FWK_SUCCESS The operation succeeded. + * \return One of the other driver-defined error codes. + */ + int (*set_voltage)(fwk_id_t id, uint64_t voltage); + + /*! + * \brief Get the voltage of a device. + * + * \param id Identifier of the device to get the voltage of. + * \param [out] voltage Voltage in millivolts (mV). + * + * \retval FWK_SUCCESS The operation succeeded. + * \return One of the other driver-defined error codes. + */ + int (*get_voltage)(fwk_id_t id, uint64_t *voltage); +}; + +/*! + * \} + */ + +/*! + * \defgroup GroupRCARPmicConfig Configuration + * \{ + */ + +/*! + * \brief Device configuration. + */ +struct mod_rcar_pmic_device_config { + fwk_id_t driver_id; /*!< Driver identifier */ + fwk_id_t driver_api_id; /*!< Driver API identifier */ +}; + +/*! + * \} + */ + +/*! + * \defgroup GroupRCARPmicEvents Events + * \{ + */ + +/*! + * \brief Set enabled event response parameters. + */ +struct mod_rcar_pmic_event_params_set_enabled_response { + int status; /*!< Status of the request */ +}; + +/*! + * \brief Set voltage event response parameters. + */ +struct mod_rcar_pmic_event_params_set_voltage_response { + int status; /*!< Status of the request */ +}; + +/*! + * \} + */ + +/*! + * \defgroup GroupRCARPmicIds Identifiers + * \{ + */ + +/*! + * \brief API indices. + */ +enum mod_rcar_pmic_api_idx { + /*! API index for mod_rcar_pmic_api_id_device */ + MOD_RCAR_PMIC_API_IDX_DEVICE, + + /*! Number of defined APIs */ + MOD_RCAR_PMIC_API_IDX_COUNT +}; + +/*! Device API identifier */ +static const fwk_id_t mod_rcar_pmic_api_id_device = + FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_PMIC, MOD_RCAR_PMIC_API_IDX_DEVICE); + +/*! + * \brief Event indices. + */ +enum mod_rcar_pmic_event_idx { + /*! Event index for mod_rcar_pmic_event_id_set_enabled */ + MOD_RCAR_PMIC_EVENT_IDX_SET_ENABLED, + + /*! Event index for mod_psu_event_id_set_voltage */ + MOD_RCAR_PMIC_EVENT_IDX_SET_VOLTAGE, + + /*! Number of defined events */ + MOD_RCAR_PMIC_EVENT_IDX_COUNT +}; + +/*! Set enabled event identifier */ +static const fwk_id_t mod_rcar_pmic_event_id_set_enabled = + FWK_ID_EVENT_INIT( + FWK_MODULE_IDX_RCAR_PMIC, MOD_RCAR_PMIC_EVENT_IDX_SET_ENABLED); + +/*! Set voltage event identifier */ +static const fwk_id_t mod_psu_event_id_set_voltage = + FWK_ID_EVENT_INIT( + FWK_MODULE_IDX_RCAR_PMIC, MOD_RCAR_PMIC_EVENT_IDX_SET_VOLTAGE); + +/*! + * \} + */ + +/*! + * \} + */ + +#endif /* MOD_RCAR_PMIC_H */ diff --git a/product/rcar/module/rcar_pmic/src/Makefile b/product/rcar/module/rcar_pmic/src/Makefile new file mode 100644 index 000000000..31569fe1b --- /dev/null +++ b/product/rcar/module/rcar_pmic/src/Makefile @@ -0,0 +1,14 @@ +# +# Renesas SCP/MCP Software +# Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +BS_LIB_NAME := rcar_pmic +BS_LIB_SOURCES := \ + mod_rcar_pmic_device_api.c \ + mod_rcar_pmic_event.c \ + mod_rcar_pmic_module.c + +include $(BS_DIR)/lib.mk diff --git a/product/rcar/module/rcar_pmic/src/mod_rcar_pmic_device_api.c b/product/rcar/module/rcar_pmic/src/mod_rcar_pmic_device_api.c new file mode 100644 index 000000000..ae55d829b --- /dev/null +++ b/product/rcar/module/rcar_pmic/src/mod_rcar_pmic_device_api.c @@ -0,0 +1,193 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include + +static int api_get_enabled(fwk_id_t device_id, bool *enabled) +{ + int status; + const struct mod_rcar_pmic_device_ctx *ctx; + + /* This API call cannot target another module */ + if (fwk_id_get_module_idx(device_id) != FWK_MODULE_IDX_RCAR_PMIC) + return FWK_E_PARAM; + + /* Ensure the identifier refers to a valid element */ + if (!fwk_module_is_valid_element_id(device_id)) + return FWK_E_PARAM; + + ctx = __mod_rcar_pmic_get_valid_device_ctx(device_id); + if (ctx == NULL) + return FWK_E_PARAM; + + /* Get the enabled state from the driver */ + status = ctx->apis.driver->get_enabled(ctx->config->driver_id, enabled); + if (status != FWK_SUCCESS) + return FWK_E_HANDLER; + + return FWK_SUCCESS; +} + +static int api_set_enabled(fwk_id_t device_id, bool enable) +{ + int status; + const struct mod_rcar_pmic_device_ctx *ctx; + + /* This API call cannot target another module */ + if (fwk_id_get_module_idx(device_id) != FWK_MODULE_IDX_RCAR_PMIC) + return FWK_E_PARAM; + + /* Ensure the identifier refers to a valid element */ + if (!fwk_module_is_valid_element_id(device_id)) + return FWK_E_PARAM; + + ctx = __mod_rcar_pmic_get_valid_device_ctx(device_id); + if (ctx == NULL) + return FWK_E_PARAM; + + /* Set the enabled state through the driver */ + status = ctx->apis.driver->set_enabled(ctx->config->driver_id, enable); + if (status != FWK_SUCCESS) + return FWK_E_HANDLER; + + return FWK_SUCCESS; +} + +static int api_set_enabled_async(fwk_id_t device_id, bool enable) +{ + int status; + struct fwk_event event; + struct mod_rcar_pmic_event_params_set_enabled *params; + + /* This API call cannot target another module */ + if (fwk_id_get_module_idx(device_id) != FWK_MODULE_IDX_RCAR_PMIC) + return FWK_E_PARAM; + + /* Ensure the identifier refers to an existing element */ + if (fwk_module_is_valid_element_id(device_id)) + return FWK_E_PARAM; + + /* Build and submit the event */ + event = (struct fwk_event) { + .id = mod_rcar_pmic_event_id_set_enabled, + .target_id = device_id, + .response_requested = true, + }; + + params = (void *)&event.params; + *params = (struct mod_rcar_pmic_event_params_set_enabled) { + .enable = enable, + }; + + /* Submit the event for processing */ + status = fwk_thread_put_event(&event); + if (status == FWK_E_NOMEM) + return FWK_E_NOMEM; + else if (status != FWK_SUCCESS) + return FWK_E_PANIC; + + return FWK_SUCCESS; +} + +static int api_get_voltage(fwk_id_t device_id, uint64_t *voltage) +{ + int status; + const struct mod_rcar_pmic_device_ctx *ctx; + + /* This API call cannot target another module */ + if (fwk_id_get_module_idx(device_id) != FWK_MODULE_IDX_RCAR_PMIC) + return FWK_E_PARAM; + + /* Ensure the identifier refers to a valid element */ + if (!fwk_module_is_valid_element_id(device_id)) + return FWK_E_PARAM; + + ctx = __mod_rcar_pmic_get_valid_device_ctx(device_id); + if (ctx == NULL) + return FWK_E_PARAM; + + /* Get the voltage from the driver */ + status = ctx->apis.driver->get_voltage(ctx->config->driver_id, voltage); + if (status != FWK_SUCCESS) + return FWK_E_HANDLER; + + return FWK_SUCCESS; +} + +static int api_set_voltage(fwk_id_t device_id, uint64_t voltage) +{ + int status; + const struct mod_rcar_pmic_device_ctx *ctx; + + /* This API call cannot target another module */ + if (fwk_id_get_module_idx(device_id) != FWK_MODULE_IDX_RCAR_PMIC) + return FWK_E_PARAM; + + /* Ensure the identifier refers to a valid element */ + if (!fwk_module_is_valid_element_id(device_id)) + return FWK_E_PARAM; + + ctx = __mod_rcar_pmic_get_valid_device_ctx(device_id); + if (ctx == NULL) + return FWK_E_PARAM; + + /* Set the voltage state through the driver */ + status = ctx->apis.driver->set_voltage(ctx->config->driver_id, voltage); + if (status != FWK_SUCCESS) + return FWK_E_HANDLER; + + return FWK_SUCCESS; +} + +static int api_set_voltage_async(fwk_id_t device_id, uint64_t voltage) +{ + int status; + struct fwk_event event; + struct mod_rcar_pmic_event_params_set_voltage *params; + + /* This API call cannot target another module */ + if (fwk_id_get_module_idx(device_id) != FWK_MODULE_IDX_RCAR_PMIC) + return FWK_E_PARAM; + + /* Ensure the identifier refers to an existing element */ + if (fwk_module_is_valid_element_id(device_id)) + return FWK_E_PARAM; + + /* Build and submit the event */ + event = (struct fwk_event) { + .id = mod_rcar_pmic_event_id_set_enabled, + .target_id = device_id, + .response_requested = true, + }; + + params = (void *)&event.params; + *params = (struct mod_rcar_pmic_event_params_set_voltage) { + .voltage = voltage, + }; + + status = fwk_thread_put_event(&event); + if (status == FWK_E_NOMEM) + return FWK_E_NOMEM; + else if (status != FWK_SUCCESS) + return FWK_E_PANIC; + + return FWK_SUCCESS; +} + +/* Module API implementation */ +const struct mod_rcar_pmic_device_api __mod_rcar_pmic_device_api = { + .get_enabled = api_get_enabled, + .set_enabled = api_set_enabled, + .set_enabled_async = api_set_enabled_async, + .get_voltage = api_get_voltage, + .set_voltage = api_set_voltage, + .set_voltage_async = api_set_voltage_async, +}; diff --git a/product/rcar/module/rcar_pmic/src/mod_rcar_pmic_device_api_private.h b/product/rcar/module/rcar_pmic/src/mod_rcar_pmic_device_api_private.h new file mode 100644 index 000000000..a5ed64111 --- /dev/null +++ b/product/rcar/module/rcar_pmic/src/mod_rcar_pmic_device_api_private.h @@ -0,0 +1,16 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_RCAR_PMIC_DEVICE_API_PRIVATE_H +#define MOD_RCAR_PMIC_DEVICE_API_PRIVATE_H + +#include + +/* Module API implementation */ +extern const struct mod_rcar_pmic_device_api __mod_rcar_pmic_device_api; + +#endif /* MOD_RCAR_PMIC_DEVICE_API_PRIVATE_H */ diff --git a/product/rcar/module/rcar_pmic/src/mod_rcar_pmic_event.c b/product/rcar/module/rcar_pmic/src/mod_rcar_pmic_event.c new file mode 100644 index 000000000..e5c0fac24 --- /dev/null +++ b/product/rcar/module/rcar_pmic/src/mod_rcar_pmic_event.c @@ -0,0 +1,98 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include + +int mod_rcar_pmic_event_set_enabled( + const struct fwk_event *event, + struct fwk_event *response) +{ + const struct mod_rcar_pmic_device_ctx *ctx; + const struct mod_rcar_pmic_event_params_set_enabled *params; + struct mod_rcar_pmic_event_params_set_enabled_response *response_params; + + /* These conditions were checked when we submitted the event */ + assert(fwk_id_get_module_idx(event->target_id) == FWK_MODULE_IDX_RCAR_PMIC); + assert(fwk_module_is_valid_element_id(event->target_id)); + + /* Explicitly cast to our parameter types */ + params = (void *)&event->params; + response_params = (void *)&response->params; + + ctx = __mod_rcar_pmic_get_device_ctx(event->target_id); + + /* Set the enabled state through the driver */ + response_params->status = ctx->apis.driver->set_enabled( + ctx->config->driver_id, + params->enable); + + return FWK_SUCCESS; +} + +int mod_rcar_pmic_event_set_voltage( + const struct fwk_event *event, + struct fwk_event *response) +{ + const struct mod_rcar_pmic_device_ctx *ctx; + const struct mod_rcar_pmic_event_params_set_voltage *params; + struct mod_rcar_pmic_event_params_set_voltage_response *response_params; + + /* These conditions were checked when we submitted the event */ + assert(fwk_id_get_module_idx(event->target_id) == FWK_MODULE_IDX_RCAR_PMIC); + assert(fwk_module_is_valid_element_id(event->target_id)); + + /* Explicitly cast to our parameter types */ + params = (void *)&event->params; + response_params = (void *)&response->params; + + ctx = __mod_rcar_pmic_get_device_ctx(event->target_id); + + /* Set the voltage through the driver */ + response_params->status = ctx->apis.driver->set_voltage( + ctx->config->driver_id, + params->voltage); + + return FWK_SUCCESS; +} + +int __mod_rcar_pmic_process_event( + const struct fwk_event *event, + struct fwk_event *response) +{ + typedef int (*handler_t)( + const struct fwk_event *event, + struct fwk_event *response); + + static const handler_t handlers[] = { + [MOD_RCAR_PMIC_EVENT_IDX_SET_ENABLED] = mod_rcar_pmic_event_set_enabled, + [MOD_RCAR_PMIC_EVENT_IDX_SET_VOLTAGE] = mod_rcar_pmic_event_set_voltage, + }; + + unsigned int event_idx; + handler_t handler; + + /* We only handle the events defined by us */ + if (fwk_id_get_module_idx(event->id) != FWK_MODULE_IDX_RCAR_PMIC) + return FWK_E_PARAM; + + /* Ensure the event index is within bounds we can handle */ + event_idx = fwk_id_get_event_idx(event->id); + if (event_idx >= FWK_ARRAY_SIZE(handlers)) + return FWK_E_PARAM; + + /* Ensure we have an implemented handler for this event */ + handler = handlers[event_idx]; + if (handler == NULL) + return FWK_E_PARAM; + + /* Delegate event handling to the relevant handler */ + return handler(event, response); +} diff --git a/product/rcar/module/rcar_pmic/src/mod_rcar_pmic_event_private.h b/product/rcar/module/rcar_pmic/src/mod_rcar_pmic_event_private.h new file mode 100644 index 000000000..a59631a80 --- /dev/null +++ b/product/rcar/module/rcar_pmic/src/mod_rcar_pmic_event_private.h @@ -0,0 +1,29 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_RCAR_PMIC_EVENT_PRIVATE_H +#define MOD_RCAR_PMIC_EVENT_PRIVATE_H + +#include +#include + +/* "Set enabled" event */ +struct mod_rcar_pmic_event_params_set_enabled { + bool enable; +}; + +/* "Set voltage" event */ +struct mod_rcar_pmic_event_params_set_voltage { + uint64_t voltage; +}; + +/* Event handler */ +int __mod_rcar_pmic_process_event( + const struct fwk_event *event, + struct fwk_event *response); + +#endif /* MOD_RCAR_PMIC_EVENT_PRIVATE_H */ diff --git a/product/rcar/module/rcar_pmic/src/mod_rcar_pmic_module.c b/product/rcar/module/rcar_pmic/src/mod_rcar_pmic_module.c new file mode 100644 index 000000000..f3c7d508a --- /dev/null +++ b/product/rcar/module/rcar_pmic/src/mod_rcar_pmic_module.c @@ -0,0 +1,127 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include + +/* Device context table */ +static struct mod_rcar_pmic_device_ctx (*device_ctx)[]; + +static int rcar_pmic_init( + fwk_id_t module_id, + unsigned int element_count, + const void *data) +{ + device_ctx = fwk_mm_calloc( + element_count, + sizeof((*device_ctx)[0])); + if (device_ctx == NULL) + return FWK_E_NOMEM; + + return FWK_SUCCESS; +} + +static int rcar_pmic_element_init( + fwk_id_t device_id, + unsigned int sub_element_count, + const void *data) +{ + assert(sub_element_count == 0); + + __mod_rcar_pmic_get_device_ctx(device_id)->config = data; + + return FWK_SUCCESS; +} + +static int rcar_pmic_bind_element(fwk_id_t device_id, unsigned int round) +{ + int status; + const struct mod_rcar_pmic_device_ctx *ctx; + + /* Only handle the first round */ + if (round > 0) + return FWK_SUCCESS; + + ctx = __mod_rcar_pmic_get_device_ctx(device_id); + + /* Bind to the driver */ + status = fwk_module_bind( + ctx->config->driver_id, + ctx->config->driver_api_id, + &ctx->apis.driver); + if (status != FWK_SUCCESS) { + assert(false); + + return FWK_E_PANIC; + } + + assert(ctx->apis.driver->set_enabled != NULL); + assert(ctx->apis.driver->get_enabled != NULL); + assert(ctx->apis.driver->set_voltage != NULL); + assert(ctx->apis.driver->get_voltage != NULL); + + return FWK_SUCCESS; +} + +static int rcar_pmic_bind(fwk_id_t id, unsigned int round) +{ + /* We only need to handle element binding */ + if (fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT)) + return rcar_pmic_bind_element(id, round); + + return FWK_SUCCESS; +} + +static int rcar_pmic_process_bind_request( + fwk_id_t source_id, + fwk_id_t target_id, + fwk_id_t api_id, + const void **api) +{ + /* Only accept binds to the elements */ + if (!fwk_id_is_type(target_id, FWK_ID_TYPE_ELEMENT)) + return FWK_E_PARAM; + + /* Only expose the device API */ + if (!fwk_id_is_equal(api_id, mod_rcar_pmic_api_id_device)) + return FWK_E_PARAM; + + *api = &__mod_rcar_pmic_device_api; + + return FWK_SUCCESS; +} + +struct mod_rcar_pmic_device_ctx + *__mod_rcar_pmic_get_device_ctx(fwk_id_t device_id) +{ + unsigned int element_idx = fwk_id_get_element_idx(device_id); + + return &(*device_ctx)[element_idx]; +} + +struct mod_rcar_pmic_device_ctx + *__mod_rcar_pmic_get_valid_device_ctx(fwk_id_t device_id) +{ + return __mod_rcar_pmic_get_device_ctx(device_id); +} + +/* Module description */ +const struct fwk_module module_rcar_pmic = { + .name = "RCAR_PMIC", + .type = FWK_MODULE_TYPE_HAL, + .init = rcar_pmic_init, + .element_init = rcar_pmic_element_init, + .bind = rcar_pmic_bind, + .process_bind_request = rcar_pmic_process_bind_request, + .process_event = __mod_rcar_pmic_process_event, + .api_count = MOD_RCAR_PMIC_API_IDX_COUNT, + .event_count = MOD_RCAR_PMIC_EVENT_IDX_COUNT, +}; diff --git a/product/rcar/module/rcar_pmic/src/mod_rcar_pmic_module_private.h b/product/rcar/module/rcar_pmic/src/mod_rcar_pmic_module_private.h new file mode 100644 index 000000000..d7b7290a5 --- /dev/null +++ b/product/rcar/module/rcar_pmic/src/mod_rcar_pmic_module_private.h @@ -0,0 +1,30 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_RCAR_PMIC_DEVICE_CTX_PRIVATE_H +#define MOD_RCAR_PMIC_DEVICE_CTX_PRIVATE_H + +#include +#include + +/* Device context */ +struct mod_rcar_pmic_device_ctx { + /* Device configuration */ + const struct mod_rcar_pmic_device_config *config; + + struct { + /* Driver API */ + const struct mod_rcar_pmic_driver_api *driver; + } apis; +}; + +struct mod_rcar_pmic_device_ctx + *__mod_rcar_pmic_get_device_ctx(fwk_id_t device_id); +struct mod_rcar_pmic_device_ctx + *__mod_rcar_pmic_get_valid_device_ctx(fwk_id_t device_id); + +#endif /* MOD_RCAR_PMIC_DEVICE_CTX_PRIVATE_H */ diff --git a/product/rcar/module/rcar_pmic/src/mod_rcar_pmic_private.h b/product/rcar/module/rcar_pmic/src/mod_rcar_pmic_private.h new file mode 100644 index 000000000..7cb371576 --- /dev/null +++ b/product/rcar/module/rcar_pmic/src/mod_rcar_pmic_private.h @@ -0,0 +1,16 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_RCAR_PMIC_PRIVATE_H +#define MOD_RCAR_PMIC_PRIVATE_H + +#include +#include +#include +#include + +#endif /* MOD_RCAR_PMIC_PRIVATE_H */ diff --git a/product/rcar/scp_ramfw/config_rcar_pmic.c b/product/rcar/scp_ramfw/config_rcar_pmic.c new file mode 100644 index 000000000..76bd20948 --- /dev/null +++ b/product/rcar/scp_ramfw/config_rcar_pmic.c @@ -0,0 +1,58 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include + +static const struct fwk_element element_table[] = { + { + .name = "CPU_GROUP_LITTLE", + .data = &(const struct mod_rcar_pmic_device_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MOCK_PMIC, 0), + .driver_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MOCK_PMIC, + MOD_RCAR_MOCK_PMIC_API_IDX_PSU_DRIVER) + }, + }, + { + .name = "CPU_GROUP_BIG", + .data = &(const struct mod_rcar_pmic_device_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MOCK_PMIC, 1), + .driver_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MOCK_PMIC, + MOD_RCAR_MOCK_PMIC_API_IDX_PSU_DRIVER) + }, + }, + { + .name = "GPU", + .data = &(const struct mod_rcar_pmic_device_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MOCK_PMIC, 2), + .driver_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MOCK_PMIC, + MOD_RCAR_MOCK_PMIC_API_IDX_PSU_DRIVER) + }, + }, + { + .name = "VPU", + .data = &(const struct mod_rcar_pmic_device_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MOCK_PMIC, 3), + .driver_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MOCK_PMIC, + MOD_RCAR_MOCK_PMIC_API_IDX_PSU_DRIVER) + }, + }, + { 0 } +}; + +static const struct fwk_element *rcar_pmic_get_element_table(fwk_id_t module_id) +{ + return element_table; +} + +struct fwk_module_config config_rcar_pmic = { + .elements = FWK_MODULE_DYNAMIC_ELEMENTS(rcar_pmic_get_element_table), + .data = NULL, +}; -- GitLab From 8d2f503472fabb5c2dcaf4f673b55e75b80e6cb5 Mon Sep 17 00:00:00 2001 From: Nicolas Royer Date: Sun, 27 Sep 2020 17:41:19 +0200 Subject: [PATCH 12/19] rcar/module: add rcar power_domain module and config data Signed-off-by: Tsutomu Muroya Signed-off-by: Nicolas Royer --- .../include/mod_power_domain.h | 16 + .../include/mod_rcar_power_domain.h | 1147 ++++++++++++ .../module/rcar_power_domain/src/Makefile | 11 + .../src/mod_rcar_power_domain.c | 1534 +++++++++++++++++ .../rcar/scp_ramfw/config_rcar_power_domain.c | 362 ++++ .../rcar/scp_ramfw/config_rcar_power_domain.h | 38 + 6 files changed, 3108 insertions(+) create mode 100644 product/rcar/module/rcar_power_domain/include/mod_power_domain.h create mode 100644 product/rcar/module/rcar_power_domain/include/mod_rcar_power_domain.h create mode 100644 product/rcar/module/rcar_power_domain/src/Makefile create mode 100644 product/rcar/module/rcar_power_domain/src/mod_rcar_power_domain.c create mode 100644 product/rcar/scp_ramfw/config_rcar_power_domain.c create mode 100644 product/rcar/scp_ramfw/config_rcar_power_domain.h diff --git a/product/rcar/module/rcar_power_domain/include/mod_power_domain.h b/product/rcar/module/rcar_power_domain/include/mod_power_domain.h new file mode 100644 index 000000000..9f518a574 --- /dev/null +++ b/product/rcar/module/rcar_power_domain/include/mod_power_domain.h @@ -0,0 +1,16 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_POWER_DOMAIN_H +#define MOD_POWER_DOMAIN_H + +#define FWK_MODULE_IDX_POWER_DOMAIN FWK_MODULE_IDX_RCAR_POWER_DOMAIN +#define fwk_module_id_power_domain fwk_module_id_rcar_power_domain + +#include "mod_rcar_power_domain.h" + +#endif /* MOD_POWER_DOMAIN_H */ diff --git a/product/rcar/module/rcar_power_domain/include/mod_rcar_power_domain.h b/product/rcar/module/rcar_power_domain/include/mod_rcar_power_domain.h new file mode 100644 index 000000000..f1dd2f5ba --- /dev/null +++ b/product/rcar/module/rcar_power_domain/include/mod_rcar_power_domain.h @@ -0,0 +1,1147 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2015-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_RCAR_POWER_DOMAIN_H +#define MOD_RCAR_POWER_DOMAIN_H + +#include +#include +#include +#include +#include +#include + +/*! + * \addtogroup GroupRCARModule RCAR Product Modules + * @{ + */ + +/*! + * \defgroup GroupRCARPowerDomain Power Domains + * + * \details Support for querying and setting the power state of power domains + * such as CPU, cluster and GPU power domains. + * + * @{ + */ + +/*! + * Maximum number of states for a power domain. + */ +#define MOD_PD_STATE_COUNT_MAX 16 + +/*! + * \brief Types of power domain. + */ +enum mod_pd_type { + /*! Processor. */ + MOD_PD_TYPE_CORE, + /*! Processor cluster. */ + MOD_PD_TYPE_CLUSTER, + /*! Generic device. */ + MOD_PD_TYPE_DEVICE, + /*! Debug device. */ + MOD_PD_TYPE_DEVICE_DEBUG, + /*! System. */ + MOD_PD_TYPE_SYSTEM, + /*! Number of power domain types. */ + MOD_PD_TYPE_COUNT +}; + +/*! + * \brief Bit width of power domain type within attributes. + */ +#define MOD_PD_TYPE_BITS_WIDTH ((sizeof(unsigned int) * CHAR_BIT) - \ + __builtin_clz(MOD_PD_TYPE_COUNT - 1)) + +/*! + * \brief Attributes of a power domain. + */ +struct mod_pd_attributes { + /*! Type of the power domain. */ + enum mod_pd_type pd_type : MOD_PD_TYPE_BITS_WIDTH; + + /*! \internal */ + unsigned int not_used : ((sizeof(unsigned int) * CHAR_BIT) - + MOD_PD_TYPE_BITS_WIDTH); +}; + +/*! + * \brief Identifiers for the power levels. + */ +enum mod_pd_level { + /*! Level 0. */ + MOD_PD_LEVEL_0, + /*! Level 1. */ + MOD_PD_LEVEL_1, + /*! Level 2. */ + MOD_PD_LEVEL_2, + /*! Level 3. */ + MOD_PD_LEVEL_3, + /*! Number of power domain levels. */ + MOD_PD_LEVEL_COUNT +}; + +/*! + * \brief Number of bits used for each level within a power domain tree + * position. + */ +#define MOD_PD_TREE_POS_BITS_PER_LEVEL 8 + +/*! + * \brief Shifts for the power level fields within a power domain tree position. + */ +enum { + /*! Number of bits to shift for the power level 0 field. */ + MOD_PD_TREE_POS_LEVEL_0_SHIFT = + MOD_PD_LEVEL_0 * MOD_PD_TREE_POS_BITS_PER_LEVEL, + /*! Number of bits to shift for the power level 1 field. */ + MOD_PD_TREE_POS_LEVEL_1_SHIFT = + MOD_PD_LEVEL_1 * MOD_PD_TREE_POS_BITS_PER_LEVEL, + /*! Number of bits to shift for the power level 2 field. */ + MOD_PD_TREE_POS_LEVEL_2_SHIFT = + MOD_PD_LEVEL_2 * MOD_PD_TREE_POS_BITS_PER_LEVEL, + /*! Number of bits to shift for the power level 3 field. */ + MOD_PD_TREE_POS_LEVEL_3_SHIFT = + MOD_PD_LEVEL_3 * MOD_PD_TREE_POS_BITS_PER_LEVEL, + MOD_PD_TREE_POS_LEVEL_SHIFT = + MOD_PD_LEVEL_COUNT * MOD_PD_TREE_POS_BITS_PER_LEVEL, +}; + +/*! + * \brief Mask for the power level fields within a power domain tree position. + */ +#define MOD_PD_TREE_POS_LEVEL_MASK UINT64_C(0xFF) + +/*! + * \brief Build the power domain tree position of a power domain. + */ +#define MOD_PD_TREE_POS(LEVEL, LEVEL_3, LEVEL_2, LEVEL_1, LEVEL_0) \ + ((((uint64_t)((LEVEL) & MOD_PD_TREE_POS_LEVEL_MASK)) << \ + MOD_PD_TREE_POS_LEVEL_SHIFT) | \ + (((uint64_t)((LEVEL_3) & MOD_PD_TREE_POS_LEVEL_MASK)) << \ + MOD_PD_TREE_POS_LEVEL_3_SHIFT) | \ + (((uint64_t)((LEVEL_2) & MOD_PD_TREE_POS_LEVEL_MASK)) << \ + MOD_PD_TREE_POS_LEVEL_2_SHIFT) | \ + (((uint64_t)((LEVEL_1) & MOD_PD_TREE_POS_LEVEL_MASK)) << \ + MOD_PD_TREE_POS_LEVEL_1_SHIFT) | \ + (((uint64_t)((LEVEL_0) & MOD_PD_TREE_POS_LEVEL_MASK)) << \ + MOD_PD_TREE_POS_LEVEL_0_SHIFT)) + +/*! + * \brief Representation of the invalid tree position. Used when checking that + * power domains are declared in increasing order of their tree position. + */ +#define MOD_PD_INVALID_TREE_POS MOD_PD_TREE_POS(MOD_PD_LEVEL_COUNT, 0, 0, 0, 0) + +/*! + * \brief Power domain module configuration. + */ +struct mod_power_domain_config { + /*! + * Identifiers of the modules and elements which are granted access to the + * restricted interface of the module. + */ + fwk_id_t *authorized_id_table; + + /*! Number of identifiers in the "authorized_id_table" table. */ + size_t authorized_id_table_size; +}; + +/*! + * \brief Configuration data for a power domain. + * + * \details The configuration data of a power domain describes the power + * domain and its relationship with other power domains in the system. + * Assumptions about the organisation of the power domains, underlying the + * way in which power domains are described here, are: + * - Each power domain can be represented as a node in a power domain + * topology tree. + * - Sibling power domains are mutually exclusive. + * - Parent power domains are shared by the children. + * + * For more information, refer to the "Arm Power State Coordination + * Interface Platform Design Document", available here: + * http://infocenter.arm.com/help/topic/com.arm.doc.den0022d/Power_State_ + * Coordination_Interface_PDD_v1_1_DEN0022D.pdf + */ +struct mod_power_domain_element_config { + /*! + * \brief Defines the position of the power domain within the power domain + * tree. + * + * \details Each child of a power domain is assigned a number ranging from 0 + * to 255. Compute the position of a power domain from the position of + * its parent 'parent_pos' (the number assigned to the power domain as + * a child of its parent is 'child_pos') as follows: + * + * tree_pos = (parent_pos - (1 << MOD_PD_TREE_POS_LEVEL_SHIFT)) + + * (child_pos << (8*pd_level)) + * + * The position of the top-level domain is defined as: + * (level of the top-level domain) << MOD_PD_TREE_POS_LEVEL_SHIFT + * + * If the power domain hierarchy maps to the core hierarchy (based on + * MPIDR levels of affinity), derive the position of the core power + * domains from the Aff0, Aff1, Aff2 and Aff3 fields of the MPIDR + * registers of the cores as follows: + * + * core power domain position = (Aff3 << MOD_PD_TREE_LEVEL_3_SHIFT) + + * (Aff2 << MOD_PD_TREE_LEVEL_2_SHIFT) + + * (Aff1 << MOD_PD_TREE_LEVEL_1_SHIFT) + + * (Aff0 << MOD_PD_TREE_LEVEL_0_SHIFT) + * + * In the module configuration data, the power domains have to be in + * increasing order of their power domain position. Thus, the power + * domains with the lowest power level have to be first and the system + * power domain has to be last. This table must contain at least one + * element, the system power domain. + */ + uint64_t tree_pos; + + /*! + * Identifier of the module or element providing the driver for the power + * domain. + */ + fwk_id_t driver_id; + + /*! Identifier of the driver API. */ + fwk_id_t api_id; + + /*! Attributes of the power domain. */ + struct mod_pd_attributes attributes; + + /*! + * Mask of the allowed power states for the power domain, for each possible + * power state of its parent. Table of allowed state masks. The bit 'i' of + * the entry 'j' is equal to one if and only if the state 'i' for the power + * domain is allowed when its parent is in state 'j'. The number of bits of + * each entry of this table has to be greater or equal than + * MOD_PD_STATE_COUNT_MAX. + */ + const uint32_t *allowed_state_mask_table; + + /*! + * Power state names. The power state names are used only for debug + * purposes and this table is optional. If not provided then the default + * power state names (OFF, ON, SLEEP, 3, 4, ...) are used for the power + * domain states. + */ + const char **state_name_table; + + /*! Size of the table of allowed state masks */ + size_t allowed_state_mask_table_size; + + /*! Size of the table of allowed state masks */ + size_t state_name_table_size; +}; + +/*! + * \brief Types of system shutdown + */ +enum mod_pd_system_shutdown { + /*! System shutdown */ + MOD_PD_SYSTEM_SHUTDOWN, + + /*! System cold reset */ + MOD_PD_SYSTEM_COLD_RESET, + + /*! System warm reset */ + MOD_PD_SYSTEM_WARM_RESET, + + /*! Sub-system reset */ + MOD_PD_SYSTEM_SUB_SYSTEM_RESET, + + /*! Forced system shutdown */ + MOD_PD_SYSTEM_FORCED_SHUTDOWN, + + /*! Number of shutdown types */ + MOD_PD_SYSTEM_COUNT, +}; + +/*! + * \brief Identifiers of the power domain states. The other states are defined + * by the platform code for more flexibility. The power states defined by + * the platform must be ordered from the shallowest to the deepest state. + */ +enum mod_pd_state { + /*! \c OFF power state */ + MOD_PD_STATE_OFF, + + /*! \c ON power state */ + MOD_PD_STATE_ON, + + /*! \c SLEEP power state */ + MOD_PD_STATE_SLEEP, + + /*! Number of power states */ + MOD_PD_STATE_COUNT +}; + +/*! + * \brief Masks for the power domain states. + */ +enum mod_pd_state_mask { + MOD_PD_STATE_OFF_MASK = 1 << MOD_PD_STATE_OFF, + MOD_PD_STATE_ON_MASK = 1 << MOD_PD_STATE_ON, + MOD_PD_STATE_SLEEP_MASK = 1 << MOD_PD_STATE_SLEEP, +}; + +/*! + * \brief Number of bits for each level state in a composite power state. + */ +#define MOD_PD_CS_STATE_BITS_PER_LEVEL 4 + +/*! + * \brief Mask for a composite power state. + */ +#define MOD_PD_CS_STATE_MASK ((1 << MOD_PD_CS_STATE_BITS_PER_LEVEL) - 1) + +/*! + * \brief Shifts for the states and child policies fields in a composite + * power state. + */ +enum { + MOD_PD_CS_LEVEL_0_STATE_SHIFT = + MOD_PD_LEVEL_0 * MOD_PD_CS_STATE_BITS_PER_LEVEL, + MOD_PD_CS_LEVEL_1_STATE_SHIFT = + MOD_PD_LEVEL_1 * MOD_PD_CS_STATE_BITS_PER_LEVEL, + MOD_PD_CS_LEVEL_2_STATE_SHIFT = + MOD_PD_LEVEL_2 * MOD_PD_CS_STATE_BITS_PER_LEVEL, + MOD_PD_CS_LEVEL_3_STATE_SHIFT = + MOD_PD_LEVEL_3 * MOD_PD_CS_STATE_BITS_PER_LEVEL, + MOD_PD_CS_LEVEL_SHIFT = + MOD_PD_LEVEL_COUNT * MOD_PD_CS_STATE_BITS_PER_LEVEL, + + MOD_PD_CS_VALID_BITS = (1 << + (MOD_PD_CS_LEVEL_SHIFT + MOD_PD_CS_STATE_BITS_PER_LEVEL)) - 1 +}; + +/*! + * \brief Compute a composite power domain state. + */ +#define MOD_PD_COMPOSITE_STATE(HIGHEST_LEVEL, LEVEL_3_STATE, LEVEL_2_STATE, \ + LEVEL_1_STATE, LEVEL_0_STATE) \ + (((HIGHEST_LEVEL) << MOD_PD_CS_LEVEL_SHIFT) | \ + ((LEVEL_3_STATE) << MOD_PD_CS_LEVEL_3_STATE_SHIFT) | \ + ((LEVEL_2_STATE) << MOD_PD_CS_LEVEL_2_STATE_SHIFT) | \ + ((LEVEL_1_STATE) << MOD_PD_CS_LEVEL_1_STATE_SHIFT) | \ + ((LEVEL_0_STATE) << MOD_PD_CS_LEVEL_0_STATE_SHIFT)) + +/*! + * \brief Power domain driver interface. + * + * \details The interface the power domain module relies on to perform + * actions on a power domain. Examples include powering a domain on or off, + * resetting the domain and preparing for/reacting to a system shutdown or + * reset. + */ +struct mod_pd_driver_api { + /*! + * \brief Set the power domain identified by \p dev_id to the \p state power + * state. + * + * \details The power domain module calls this function when processing a + * set state request. The function is called once the module + * has checked that the power domain's children and parent are in a + * proper state for the power domain to be transitioned to the \p state + * power state. This function must not return until the transition has + * completed. + * + * \warning In the case of a shutdown request, if provided, \ref + * shutdown will be called instead. + * + * \param dev_id Driver identifier of the power domain. + * \param state Power state the power domain has to be put into. + * + * \retval FWK_SUCCESS The power state has been successfully set. + * \retval FWK_E_ACCESS Invalid access, the framework has rejected the + * call to the API. + * \return One of the other specific error codes described by the driver + * module. + */ + int (*set_state)(fwk_id_t dev_id, unsigned int state); + + /*! + * \brief Get the current power state of the power domain identified by + * \p dev_id. + * + * \details The power domain module calls this function when processing a + * get state or get composite state request. + * + * \param dev_id Driver identifier of the power domain. + * \param state Pointer to storage for the power state of the power domain. + * + * \retval FWK_SUCCESS The power state was successfully returned. + * \retval FWK_E_ACCESS Invalid access, the framework has rejected the + * call to the API. + */ + int (*get_state)(fwk_id_t dev_id, unsigned int *state); + + /*! + * \brief Reset the power domain identified by \p dev_id. + * + * \details The power domain module calls this function when processing a + * reset request. The module will ensure that any children of + * the power domain are powered off before attempting a reset. + * + * \param dev_id Driver identifier of the power domain. + * + * \retval FWK_SUCCESS The power domain was successfully reset. + * \retval FWK_E_ACCESS Invalid access, the framework has rejected the + * call to the API. + * \return One of the other specific error codes described by the module. + */ + int (*reset)(fwk_id_t dev_id); + + /*! + * \brief Check whether a state transition request should be denied. + * + * \note This function is optional (may be \c NULL) and it is expected that + * most drivers will not implement it. + * + * \details The power domain module calls this function when it wishes to + * know whether it should permit or deny a state transition request. + * It is intended to provide a device a denial mechanism that can make + * judgements based on information unavailable to the power domain + * module, such as dependencies between power domains beyond the power + * domain tree model. + * + * \param dev_id Driver identifier of the power domain. + * \param state Power state the power domain is intended to be put into. + * + * \retval true The transition should be denied. + * \retval false The power domain can transition to the power state \p + * state. + */ + bool (*deny)(fwk_id_t dev_id, unsigned int state); + + /*! + * \brief Prepare the last standing core for a system suspend. + * + * \details The function prepares the last standing core for entering the + * \ref MOD_PD_STATE_OFF state (powered off, no wake-up interrupt) when + * it will execute WFI. The function should also ensure that when the + * core is powered off a state transition report is sent by means of + * the \ref mod_pd_driver_input_api::report_power_state_transition + * driver input interface function indicating that the core power + * domain state should be updated. This function is mandatory for core + * power domains, but is otherwise unused. + * + * \param dev_id Driver identifier of the power domain. + * + * \retval FWK_SUCCESS The core has been successfully prepared. + * \retval FWK_E_ACCESS Invalid access, the framework has rejected the + * call to the API. + */ + int (*prepare_core_for_system_suspend)(fwk_id_t dev_id); + + /*! + * \brief Shutdown a power domain as part of a system shutdown. + * + * \note This function is optional (may be \c NULL). + * + * \details The power domain module calls this function when processing a + * shutdown request. The power domain must be in the + * \ref MOD_PD_STATE_OFF state when it returns. + * + * \param dev_id Driver identifier of the power domain. + * \param state State the system will be suspended to. The definition + * of those states is platform specific. + * + * \retval FWK_SUCCESS The core has been successfully shutdown. + * \retval FWK_E_ACCESS Invalid access, the framework has rejected the + * call to the API. + */ + int (*shutdown)(fwk_id_t dev_id, + enum mod_pd_system_shutdown system_shutdown); +}; + +/*! + * \brief Power domain module public interface. + * + * \details The interface the power domain module exposes to the other modules + * and their elements without any restriction. + */ + +struct mod_pd_public_api { + /*! + * \brief Get the type of a power domain. + * + * \param pd_id Identifier of the power domain that the type is being + * queried for. + * \param[out] type Type of the power domain. + * + * \retval FWK_SUCCESS The type of the power domain was returned. + * \retval FWK_E_INIT The module has not been initialized. + * \retval FWK_E_STATE The power domain module is in an invalid state. + * \retval FWK_E_PARAM The power domain identifier is unknown. + * \retval FWK_E_PARAM The pointer 'type' is equal to NULL. + */ + int (*get_domain_type)(fwk_id_t pd_id, enum mod_pd_type *type); + + /*! + * \brief Get the identifier of the parent of a power domain. + * + * \note The function returns \ref FWK_ID_NONE in the case of the root power + * domain which does not have any parent. + * + * \param pd_id Identifier of the power domain. + * \param[out] parent_pd_id Identifier of the parent power domain. + * + * \retval FWK_SUCCESS The identifier of the parent power domain was + * returned. + * \retval FWK_E_INIT The module has not been initialized. + * \retval FWK_E_STATE The power domain module is in an invalid state. + * \retval FWK_E_PARAM The power domain identifier is unknown. + * \retval FWK_E_PARAM The pointer 'parent_pd_id' is equal to NULL. + */ + int (*get_domain_parent_id)(fwk_id_t pd_id, fwk_id_t *parent_pd_id); +}; + +/*! + * \brief Power domain module restricted interface. + * + * \details The interface the power domain module exposes to a restricted set + * of modules and/or elements. The set of modules and/or elements that are + * allowed to access this interface is defined by the module configuration + * data. + */ +struct mod_pd_restricted_api { + /*! + * \brief Get the type of a power domain. + * + * \param pd_id Identifier of the power domain that the type is being + * queried for. + * \param[out] type Type of the power domain. + * + * \retval FWK_SUCCESS The type of the power domain was returned. + * \retval FWK_E_INIT The module has not been initialized. + * \retval FWK_E_STATE The power domain module is in an invalid state. + * \retval FWK_E_PARAM The power domain identifier is unknown. + * \retval FWK_E_PARAM The pointer 'type' is equal to NULL. + */ + int (*get_domain_type)(fwk_id_t pd_id, enum mod_pd_type *type); + + /*! + * \brief Get the identifier of the parent of a power domain. + * + * \note The function returns \ref FWK_ID_NONE in the case of the root power + * domain which does not have any parent. + * + * \param pd_id Identifier of the power domain. + * \param[out] parent_pd_id The identifier of the parent power domain. + * + * \retval FWK_SUCCESS The identifier of the parent power domain was + * returned. + * \retval FWK_E_INIT The module has not been initialized. + * \retval FWK_E_STATE The power domain module is in an invalid state. + * \retval FWK_E_PARAM The power domain identifier is unknown. + * \retval FWK_E_PARAM The pointer 'parent_pd_id' is equal to NULL. + */ + int (*get_domain_parent_id)(fwk_id_t pd_id, fwk_id_t *parent_pd_id); + + /*! + * \brief Set the state of a power domain and possibly of one or several of + * its ancestors. + * + * \note The function sets the state of the power domain identified by + * 'pd_id' and possibly of one or several of its ancestors. When the + * function returns the state transition is completed. + * + * \param pd_id Identifier of the power domain whose state has to be set. + * + * \param state State the power domain has to be put into and + * possibly the state(s) its ancestor(s) has(have) to be put into. The + * module will ensure that, for each power state transition, the parent + * and the children of the power domain involved are in a state where + * the transition can be completed. + * + * \retval FWK_SUCCESS The power state transition was completed. + * \retval FWK_E_ACCESS Invalid access, the framework has rejected the + * call to the API. + * \retval FWK_E_HANDLER The function is not called from a thread. + * \retval FWK_E_PARAM One or more parameters were invalid. + */ + int (*set_state)(fwk_id_t pd_id, uint32_t state); + + /*! + * \brief Request an asynchronous power state transition. + * + * \warning Successful completion of this function does not indicate + * completion of a transition, but instead that a request has been + * submitted. + * + * \param pd_id Identifier of the power domain whose state has to be set. + * + * \param resp_requested True if the caller wants to be notified with an + * event response at the end of the request processing. + * + * \param composite State the power domain has to be put into and + * possibly the state(s) its ancestor(s) has(have) to be put into. The + * module will ensure that, for each power state transition, the parent + * and the children of the power domain involved are in a state where + * the transition can be completed. + * + * \retval FWK_SUCCESS The power state transition was submitted. + * \retval FWK_E_ACCESS Invalid access, the framework has rejected the + * call to the API. + * \retval FWK_E_PARAM One or more parameters were invalid. + */ + int (*set_state_async)(fwk_id_t pd_id, bool resp_requested, uint32_t state); + + /*! + * \brief Get the state of a given power domain. + * + * \param pd_id Identifier of the power domain whose state has to be + * retrieved. + * \param[out] state The power domain state. + * + * \retval FWK_SUCCESS The power state was returned. + * \retval FWK_E_ACCESS Invalid access, the framework has rejected the + * call to the API. + * \retval FWK_E_HANDLER The function is not called from a thread. + * \retval FWK_E_PARAM The power domain identifier is unknown. + * \retval FWK_E_PARAM The pointer 'state' is equal to NULL. + */ + int (*get_state)(fwk_id_t pd_id, unsigned int *state); + + /*! + * \brief Reset of a power domain. + * + * \note The function resets the power domain identified by 'pd_id'. When + * the function returns the power domain reset is completed. + * + * \param pd_id Identifier of the power domain to reset. + * + * \retval FWK_SUCCESS Power state retrieving request transmitted. + * \retval FWK_E_ACCESS Invalid access, the framework has rejected the + * call to the API. + * \retval FWK_E_HANDLER The function is not called from a thread. + * \retval FWK_E_NOMEM Failed to allocate a request descriptor. + * \retval FWK_E_PARAM The power domain identifier is unknown. + */ + int (*reset)(fwk_id_t pd_id); + + /*! + * \brief Suspend the system. + * + * \note The function initiates the suspension of the system. On call, all + * but one core power domain must be in the MOD_PD_STATE_OFF state. + * When the function returns the power down of the last standing core + * is programmed to occur as soon as the core executes WFI. + * + * \param state State the system has to be suspended to. The definition + * of those states is platform specific. + * + * \retval FWK_SUCCESS The system suspension has been initiated + * successfully. + * \retval FWK_E_ACCESS Invalid access, the framework has rejected the + * call to the API. + * \retval FWK_E_HANDLER The function is not called from a thread. + * \retval FWK_E_NOMEM Failed to allocate a request descriptor. + * \retval FWK_E_PARAM Invalid state. + * \retval FWK_E_STATE The system is not in the proper state to be + * suspended. + */ + int (*system_suspend)(unsigned int state); + + /*! + * \brief Shutdown the system. + * + * \note The function shutdowns the system whatever its current state. If + * the shutdown is successful, the function does not return. + * + * \retval FWK_E_ACCESS Invalid access, the framework has rejected the + * call to the API. + * \retval FWK_PENDING Request aknowledged. A response event will not be + * sent to the caller. + * \retval FWK_E_HANDLER The function is not called from a thread. + * \retval FWK_E_NOMEM Failed to allocate a request descriptor. + */ + int (*system_shutdown)(enum mod_pd_system_shutdown system_shutdown); +}; + +/*! + * \brief Power domain module driver input API. + * + * \details The interface the power domain module exposes to its module drivers + * to be able to ask for power state transitions or report power state + * transitions following the occurrence of interrupts. + */ +struct mod_pd_driver_input_api { + /*! + * \brief Request an asynchronous composite power state transition. + * + * \warning Successful completion of this function does not indicate + * completion of a transition, but instead that a request has been + * submitted. + * + * \param pd_id Identifier of the power domain whose state has to be set. + * + * \param resp_requested True if the caller wants to be notified with an + * event response at the end of the request processing. + * + * \param composite_state State the power domain has to be put into and + * possibly the state(s) its ancestor(s) has(have) to be put into. The + * module will ensure that, for each power state transition, the parent + * and the children of the power domain involved are in a state where + * the transition can be completed. + * + * \retval FWK_SUCCESS The composite power state transition was submitted. + * \retval FWK_E_ACCESS Invalid access, the framework has rejected the + * call to the API. + * \retval FWK_E_PARAM One or more parameters were invalid. + */ + int (*set_state_async)( + fwk_id_t pd_id, + bool resp_requested, + uint32_t composite_state); + + /*! + * \brief Request for a power domain to be reset. + * + * \note The function queues a reset request. When the function returns the + * power domain has not been reset, the reset has just been requested + * to the power domain module. + * + * \param pd_id Identifier of the power domain to reset. + * + * \param resp_requested True if the caller wants to be notified with an + * event response at the end of the request processing. + * + * \retval FWK_SUCCESS Reset request transmitted. + * \retval FWK_E_ACCESS Invalid access, the framework has rejected the + * call to the API. + * \retval FWK_E_HANDLER The function is not called from a thread. + * \retval FWK_E_NOMEM Failed to allocate a request descriptor. + * \retval FWK_E_PARAM The power domain identifier is unknown. + */ + int (*reset_async)(fwk_id_t pd_id, bool resp_requested); + + /*! + * \brief Report a power domain state transition. + * + * \warning Failure to call this function on a power state transition may + * leave the power domain module with an outdated power state for the + * power domain. + * + * \param pd_id Identifier of the power domain, a power state transition + * report is is sent to. + * + * \retval FWK_SUCCESS Report transmitted. + * \retval FWK_E_ACCESS Invalid access, the framework has rejected the call + * to the API. + * \retval FWK_E_NOMEM Failed to allocate a report event. + * \retval FWK_E_PARAM The power domain identifier is unknown. + */ + int (*report_power_state_transition)(fwk_id_t pd_id, unsigned int state); + + /*! + * \brief Get the power domain identifier of the last core online before or + * during system suspend. + * + * \details When an agent calls for system suspend, the power domain module + * ensures that all cores are turned off except for one, which is the + * calling entity. Because any core can call system suspend, it may be + * necessary for a driver to know which core called system suspend. + * With this information the driver can, for example, safely resume the + * system from the same core which was turned off lastly. + * + * \note The power domain module does not perform any checks to ensure that + * this function is called during a system suspend sequence. + * + * \param[out] last_core_pd_id Identifier of the last core. + * + * \retval FWK_E_PARAM The pointer to the identifier is not valid. + * \retval FWK_SUCCESS The request was successful. + * \return One of the standard framework error codes. + */ + int (*get_last_core_pd_id)(fwk_id_t *last_core_pd_id); +}; + +/*! + * \brief Parameters of a power state pre-transition notification. + */ +struct mod_pd_power_state_pre_transition_notification_params { + /*! Current power state */ + unsigned int current_state; + + /*! Target power state */ + unsigned int target_state; +}; + +/*! + * \brief Parameters of the response to a power state pre-transition + * notification. + */ +struct mod_pd_power_state_pre_transition_notification_resp_params { + /*! + * Status of the transition for the entity being notified. + */ + int status; +}; + +/*! + * \brief Parameters of a power domain transition notification. + */ +struct mod_pd_power_state_transition_notification_params { + /*! Power state the power domain has transitioned to */ + unsigned int state; +}; + +/*! + * \defgroup GroupPowerDomainIds Identifiers + * \{ + */ + +/*! + * \brief API indices + */ +enum mod_pd_api_idx { + MOD_PD_API_IDX_PUBLIC, + MOD_PD_API_IDX_RESTRICTED, + MOD_PD_API_IDX_DRIVER_INPUT, + MOD_PD_API_IDX_COUNT, +}; + +/*! Public API identifier */ +static const fwk_id_t mod_pd_api_id_public = + FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_POWER_DOMAIN, + MOD_PD_API_IDX_PUBLIC); + +/*! Restricted API identifier */ +static const fwk_id_t mod_pd_api_id_restricted = + FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_POWER_DOMAIN, + MOD_PD_API_IDX_RESTRICTED); + +/*! Driver input API identifier */ +static const fwk_id_t mod_pd_api_id_driver_input = + FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_POWER_DOMAIN, + MOD_PD_API_IDX_DRIVER_INPUT); + +/*! + * \brief Notification indices. + */ +enum mod_pd_notification_idx { + /*! Power state transition */ + MOD_PD_NOTIFICATION_IDX_POWER_STATE_TRANSITION, + + /*! Power state pre-transition */ + MOD_PD_NOTIFICATION_IDX_POWER_STATE_PRE_TRANSITION, + + /*! Number of notifications defined by the power domain module */ + MOD_PD_NOTIFICATION_COUNT, +}; + +/*! + * \brief Notification identifiers. + */ +/*! Identifier of the power state transition notification */ +static const fwk_id_t mod_pd_notification_id_power_state_transition = + FWK_ID_NOTIFICATION_INIT(FWK_MODULE_IDX_RCAR_POWER_DOMAIN, + MOD_PD_NOTIFICATION_IDX_POWER_STATE_TRANSITION); +/*! Identifier of the power state pre-transition notification */ +static const fwk_id_t mod_pd_notification_id_power_state_pre_transition = + FWK_ID_NOTIFICATION_INIT(FWK_MODULE_IDX_RCAR_POWER_DOMAIN, + MOD_PD_NOTIFICATION_IDX_POWER_STATE_PRE_TRANSITION); + +/*! + * @cond + */ + +#define CPU_PWR_OFF U(0x00000003) +#define MODE_L2_DOWN (0x00000002U) + +/* + * Module and power domain contexts + */ + +struct response_ctx { + /* Pending response flag. */ + bool pending; + + /* Cookie of the event to respond to. */ + uint32_t cookie; +}; + +/* Context for the power state transition notification */ +struct power_state_transition_notification_ctx { + /* Number of pending notification responses */ + unsigned int pending_responses; + + /* + * Power state the power domain has transitioned to. + */ + unsigned int state; +}; + +/* Context for the power state pre-transition notification */ +struct power_state_pre_transition_notification_ctx { + /* Number of pending notification responses */ + unsigned int pending_responses; + + /* Target power state */ + unsigned int state; + + /* + * Status of the responses received so far. Either FWK_SUCCESS if all the + * responses received so far have indicated success, or FWK_E_DEVICE + * otherwise. + */ + int response_status; + + /* + * Validity flag. Set to true when a notification is sent and reset to + * false when the requested state for the power domain is changed. + */ + bool valid; +}; + +struct pd_ctx { + /* Identifier of the power domain */ + fwk_id_t id; + + /* Driver interface */ + struct mod_pd_driver_api *driver_api; + + /* Driver's identifier of the power domain */ + fwk_id_t driver_id; + + /* Power domain configuration data */ + const struct mod_power_domain_element_config *config; + + /* + * Mask of the valid states. Bit \c n in \ref valid_states_mask is equal + * to one if and only if the state \c n is a valid state for the power + * domain. The number of bits of this field has to be greater or equal than + * MOD_PD_STATE_COUNT_MAX. + */ + uint32_t valid_state_mask; + + /* + * Table of allowed state masks. Bit \c n of the entry \c m is equal to + * one if and only if the state \c n for the power domain is allowed when + * its parent is in state \c m. The number of bits of each entry of this + * table has to be greater or equal than MOD_PD_STATE_COUNT_MAX. + */ + const uint32_t *allowed_state_mask_table; + + /* Size of the table of allowed state masks */ + size_t allowed_state_mask_table_size; + + /* Pointer to the power domain's parent context */ + struct pd_ctx *parent; + + /* + * Pointer to the context of the power domain's first child. This + * field is equal to NULL if the power domain does not have any children. + */ + struct pd_ctx *first_child; + + /* + * Pointer to the context of the power domain sibling in the chain of the + * children power domains of their parent. + */ + struct pd_ctx *sibling; + + /* Requested power state for the power domain */ + unsigned int requested_state; + + /* Last power state requested to the driver for the power domain */ + unsigned int state_requested_to_driver; + + /* + * Current state of the power domain. When a transition is in progress, the + * current state is different from the last requested state. + */ + unsigned int current_state; + + /* Pending response context */ + struct response_ctx response; + + /* Context for the power state transition notification */ + struct power_state_transition_notification_ctx + power_state_transition_notification_ctx; + + /* Context for the power state pre-transition notification */ + struct power_state_pre_transition_notification_ctx + power_state_pre_transition_notification_ctx; +}; + +struct system_suspend_ctx { + /* Flag indicating if a system suspend is ongoing (true) or not (false) */ + bool ongoing; + + /* Last standing core context */ + struct pd_ctx *last_core_pd; + + /* Suspend state for the system power domain */ + unsigned int state; +}; + +struct mod_pd_ctx { + /* Module configuration data */ + struct mod_power_domain_config *config; + + /* Table of power domain contexts */ + struct pd_ctx *pd_ctx_table; + + /* Number of power domains */ + unsigned int pd_count; + + /* Log module API */ + const struct mod_log_api *log_api; + + /* Context of the system power domain */ + struct pd_ctx *system_pd_ctx; + + /* System suspend context */ + struct system_suspend_ctx system_suspend; +}; + +/* + * Power domain module events + */ + +/* Power module event indexes */ +enum pd_event_idx { + PD_EVENT_IDX_SET_STATE, + PD_EVENT_IDX_GET_STATE, + PD_EVENT_IDX_RESET, + PD_EVENT_IDX_REPORT_POWER_STATE_TRANSITION, + PD_EVENT_IDX_SYSTEM_SUSPEND, + PD_EVENT_IDX_SYSTEM_SHUTDOWN, + PD_EVENT_COUNT +}; + +/* Standard parameter of a response event */ +struct pd_response { + /* Status of the event processing */ + int status; +}; + +/* + * PD_EVENT_IDX_SET_STATE + * Parameters of the set state request event + */ +struct pd_set_state_request { + /* + * The composite state that defines the power state that the power domain, + * target of the request, has to be put into and possibly the power states + * the ancestors of the power domain have to be put into. + */ + uint32_t composite_state; + uint32_t domain_id; +}; + +/* Parameters of the set state response event */ +struct pd_set_state_response { + /* Status of the set state request event processing */ + int status; + + /* + * The composite state that defines the power state that the power domain, + * target of the request, had to be put into and possibly the power states + * the ancestors of the power domain had to be put into. + */ + uint32_t composite_state; +}; + +/* + * PD_EVENT_IDX_GET_STATE + * Parameters of the get state request event + */ +struct pd_get_state_request { + /* + * Flag indicating if the composite state of the power domain and its + * ancestors has to be returned (composite=true) or just the power domain + * state (composite=false). + */ + bool composite; +}; + +/* Parameters of the get state response event */ +struct pd_get_state_response { + /* Status of the get state request event processing */ + int status; + + /* Copy of the "composite" request parameter */ + bool composite; + + /* + * The power state of the power domain target of the request or the + * composite state of the power domain and its ancestors depending on the + * value of the "composite" request parameter. + */ + uint32_t state; +}; + +/* + * PD_EVENT_IDX_REPORT_POWER_STATE_TRANSITION + * Parameters of the power state transition report event + */ +struct pd_power_state_transition_report { + /* The new power state of the power domain */ + uint32_t state; +}; + +/* + * PD_EVENT_IDX_SYSTEM_SUSPEND + * Parameters of the system suspend request event + */ +struct pd_system_suspend_request { + /* System suspend state, platform specific */ + unsigned int state; +}; + +/* + * PD_EVENT_IDX_SYSTEM_SHUTDOWN + * Parameters of the system shutdown request event + */ +struct pd_system_shutdown_request { + /* System shutdown type */ + enum mod_pd_system_shutdown system_shutdown; +}; + +/* + * For each power level, shift in a composite state of the state for the power + * level. + */ +static const unsigned int mod_pd_cs_level_state_shift[MOD_PD_LEVEL_COUNT] = { + MOD_PD_CS_LEVEL_0_STATE_SHIFT, + MOD_PD_CS_LEVEL_1_STATE_SHIFT, + MOD_PD_CS_LEVEL_2_STATE_SHIFT, + MOD_PD_CS_LEVEL_3_STATE_SHIFT +}; + +/* + * Internal variables + */ +static const char * const default_state_name_table[] = { + "OFF", "ON", "SLEEP", "3", "4", "5", "6", "7", + "8", "9", "10", "11", "12", "13", "14", "15" +}; + +/*! + * @endcond + */ + +/*! + * \} + */ + +/*! + * @} + */ + +/*! + * @} + */ + +#endif /* MOD_RCAR_POWER_DOMAIN_H */ diff --git a/product/rcar/module/rcar_power_domain/src/Makefile b/product/rcar/module/rcar_power_domain/src/Makefile new file mode 100644 index 000000000..18ec7212d --- /dev/null +++ b/product/rcar/module/rcar_power_domain/src/Makefile @@ -0,0 +1,11 @@ +# +# Renesas SCP/MCP Software +# Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +BS_LIB_NAME := Power domain +BS_LIB_SOURCES := mod_rcar_power_domain.c + +include $(BS_DIR)/lib.mk diff --git a/product/rcar/module/rcar_power_domain/src/mod_rcar_power_domain.c b/product/rcar/module/rcar_power_domain/src/mod_rcar_power_domain.c new file mode 100644 index 000000000..823a3e2b1 --- /dev/null +++ b/product/rcar/module/rcar_power_domain/src/mod_rcar_power_domain.c @@ -0,0 +1,1534 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2015-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Internal variables + */ +static struct mod_pd_ctx mod_pd_ctx; + +/* + * Utility functions + */ + +/* Functions related to power domain positions in the power domain tree */ +static bool is_valid_tree_pos(uint64_t tree_pos) +{ + return (tree_pos < MOD_PD_TREE_POS(MOD_PD_LEVEL_COUNT, 0, 0, 0, 0)); +} + +static enum mod_pd_level get_level_from_tree_pos(uint64_t tree_pos) +{ + return (enum mod_pd_level)((tree_pos >> MOD_PD_TREE_POS_LEVEL_SHIFT) & + MOD_PD_TREE_POS_LEVEL_MASK); +} + +/* State related utility functions */ +static bool is_valid_state(const struct pd_ctx *pd, unsigned int state) +{ + return (state < MOD_PD_STATE_COUNT_MAX) && + ((pd->valid_state_mask & (1 << state)) != 0); +} + +static unsigned int normalize_state(unsigned int state) +{ + switch (state) { + case MOD_PD_STATE_OFF: + return (MOD_PD_STATE_COUNT_MAX + 1); + + case MOD_PD_STATE_SLEEP: + return MOD_PD_STATE_COUNT_MAX; + + default: + return state; + } +} + +static bool is_deeper_state(unsigned int state, + unsigned int state_to_compare_to) +{ + return normalize_state(state) > normalize_state(state_to_compare_to); +} + +static bool is_shallower_state(unsigned int state, + unsigned int state_to_compare_to) +{ + return normalize_state(state) < normalize_state(state_to_compare_to); +} + +static bool is_allowed_by_child(const struct pd_ctx *child, + unsigned int parent_state, unsigned int child_state) +{ + if (parent_state >= child->allowed_state_mask_table_size) + return false; + + return ((child->allowed_state_mask_table[parent_state] + & (1 << child_state)) != 0); +} + +static const char *get_state_name(const struct pd_ctx *pd, unsigned int state) +{ + static char const unknown_name[] = "Unknown"; + + if (state < pd->config->state_name_table_size) + return pd->config->state_name_table[state]; + else if (state < FWK_ARRAY_SIZE(default_state_name_table)) + return default_state_name_table[state]; + else + return unknown_name; +} + +/* Functions related to a composite state */ +static unsigned int get_level_state_from_composite_state( + uint32_t composite_state, enum mod_pd_level level) +{ + return (composite_state >> mod_pd_cs_level_state_shift[level]) + & MOD_PD_CS_STATE_MASK; +} + +static enum mod_pd_level get_highest_level_from_composite_state( + uint32_t composite_state) +{ + return (enum mod_pd_level)((composite_state >> MOD_PD_CS_LEVEL_SHIFT) & + MOD_PD_CS_STATE_MASK); +} + +static bool is_valid_composite_state(struct pd_ctx *target_pd, + uint32_t composite_state) +{ + enum mod_pd_level level; + enum mod_pd_level highest_level; + unsigned int state, child_state = MOD_PD_STATE_OFF; + struct pd_ctx *pd = target_pd; + struct pd_ctx *child = NULL; + + assert(target_pd != NULL); + + if (composite_state & (~MOD_PD_CS_VALID_BITS)) + goto error; + + level = get_level_from_tree_pos(pd->config->tree_pos); + highest_level = get_highest_level_from_composite_state(composite_state); + + if ((highest_level < level) || + (highest_level >= MOD_PD_LEVEL_COUNT)) + goto error; + + for (; level <= highest_level; level++) { + if (pd == NULL) + goto error; + + state = get_level_state_from_composite_state(composite_state, level); + if (!is_valid_state(pd, state)) + goto error; + + if ((child != NULL) && !is_allowed_by_child(child, state, child_state)) + goto error; + + child = pd; + child_state = state; + pd = pd->parent; + } + + return true; + +error: + FWK_LOG_ERR("[PD] ERROR: Invalid composite state for %s: 0x%08x.", + fwk_module_get_name(target_pd->id), composite_state); + + return false; +} + +/* + * Check whether a transition to a given power state for a power domain is + * possible given the current state of its parent and children (if any). + * + * \param pd Description of the power domain to check the power state transition + * for. + * \param state Power state. + */ +static bool is_allowed_by_parent_and_children(struct pd_ctx *pd, + unsigned int state) +{ + struct pd_ctx *parent, *child; + + parent = pd->parent; + if (parent != NULL) { + if (!is_allowed_by_child(pd, parent->current_state, state)) + return false; + } + + child = pd->first_child; + while (child != NULL) { + if (!is_allowed_by_child(child, state, child->current_state)) + return false; + child = child->sibling; + } + + return true; +} + +/* + * Check whether a power state pre-transition notification must be sent. + * + * \param pd Description of the power domain + * \param state Power state the power domain has to transit to + * + * \retval true A power state pre-transition notification must be sent. + * \retval false A power state pre-transition notification doesn't have to be + * sent. + */ +static bool check_power_state_pre_transition_notification(struct pd_ctx *pd, + unsigned int state) +{ + if (!is_deeper_state(state, pd->state_requested_to_driver)) + return false; + + if ((state == pd->power_state_pre_transition_notification_ctx.state) && + pd->power_state_pre_transition_notification_ctx.valid) { + return (pd->power_state_pre_transition_notification_ctx.response_status + != FWK_SUCCESS); + } + + return true; +} + +/* + * Initiate a power state pre-transition notification if necessary. + * + * \param pd Description of the power domain to initiate the notification + * for. + * + * \retval true Waiting for notification responses. + * \retval false Not waiting for any notification response. + */ +static bool initiate_power_state_pre_transition_notification(struct pd_ctx *pd) +{ + unsigned int state; + struct fwk_event notification_event = { + .id = mod_pd_notification_id_power_state_pre_transition, + .response_requested = true + }; + struct mod_pd_power_state_pre_transition_notification_params *params; + + state = pd->requested_state; + if (!check_power_state_pre_transition_notification(pd, state)) + return false; + + /* + * If still waiting for some responses on the previous power state + * pre-transition notification, wait for them before to issue the next one. + */ + if (pd->power_state_pre_transition_notification_ctx.pending_responses != 0) + return true; + + params = (struct mod_pd_power_state_pre_transition_notification_params *) + notification_event.params; + params->current_state = pd->current_state; + params->target_state = state; + fwk_notification_notify(¬ification_event, + &pd->power_state_pre_transition_notification_ctx.pending_responses); + + pd->power_state_pre_transition_notification_ctx.state = state; + pd->power_state_pre_transition_notification_ctx.response_status = + FWK_SUCCESS; + pd->power_state_pre_transition_notification_ctx.valid = true; + + return (pd->power_state_pre_transition_notification_ctx.pending_responses + != 0); +} + +/* + * Initiate the transition to a power state for a power domain. + * + * \param pd Description of the power domain to initiate the state transition + * for. + * + * \retval FWK_SUCCESS The power state transition was initiated. + * \retval FWK_E_DEVICE The power state transition was denied by the driver. + * \return One of the other driver-defined error codes. + */ +static int initiate_power_state_transition(struct pd_ctx *pd) +{ + int status; + unsigned int state = pd->requested_state; + + status = pd->driver_api->set_state(pd->driver_id, state); + pd->current_state = state; + + FWK_LOG_TRACE("[PD] %s: %s->%s, %d.", fwk_module_get_name(pd->id), + get_state_name(pd, pd->state_requested_to_driver), + get_state_name(pd, state), status); + + pd->state_requested_to_driver = state; + + return status; +} + +/* + * Respond to a request. + * + * \param pd Description of the power domain in charge of the response + * \param status Response status + */ +static void respond(struct pd_ctx *pd, int status) +{ + struct fwk_event resp_event; + const struct pd_set_state_request *req_params = + (struct pd_set_state_request *)(&resp_event.params); + struct pd_set_state_response *resp_params = + (struct pd_set_state_response *)(&resp_event.params); + + if (!pd->response.pending) + return; + + status = fwk_thread_get_delayed_response( + pd->id, pd->response.cookie, &resp_event); + pd->response.pending = false; + + if (status != FWK_SUCCESS) + return; + + resp_params->composite_state = req_params->composite_state; + resp_params->status = status; + + fwk_thread_put_event(&resp_event); +} + +/* + * Determine whether a composite state requires that the transition begins + * with the highest or lowest level. + * + * \param lowest_pd Target of the composite state transition request. + * \param uint32_t composite_state Target composite state. + * \retval true The power state transition must propagate upwards. + * \retval false The power state transition must propagate downwards. + */ +static bool is_upwards_transition_propagation(const struct pd_ctx *lowest_pd, + uint32_t composite_state) +{ + enum mod_pd_level lowest_level, highest_level, level; + const struct pd_ctx *pd; + unsigned int state; + + lowest_level = get_level_from_tree_pos(lowest_pd->config->tree_pos); + highest_level = get_highest_level_from_composite_state(composite_state); + + for (level = lowest_level, pd = lowest_pd; level <= highest_level; + level++, pd = pd->parent) { + + state = get_level_state_from_composite_state(composite_state, level); + if (state == pd->requested_state) + continue; + + return is_deeper_state(state, pd->requested_state); + } + + return false; +} +static bool is_allowed_by_children(const struct pd_ctx *pd, unsigned int state) +{ + const struct pd_ctx *child; + + for (child = pd->first_child; child != NULL; child = child->sibling) { + if (!is_allowed_by_child(child, state, child->requested_state)) + return false; + } + + return true; +} + +static void process_set_state_request(struct pd_ctx *lowest_pd, + struct pd_set_state_request *req_params, struct fwk_event *resp_event) +{ + int status; + struct pd_set_state_response *resp_params = + (struct pd_set_state_response *)resp_event->params; + uint32_t composite_state; + bool up; + enum mod_pd_level lowest_level, highest_level, level; + unsigned int nb_pds; + struct pd_ctx *pd; + const struct pd_ctx *parent; + unsigned int pd_index; + unsigned int state; + bool first_power_state_transition_initiated = false; + + /* A set state request cancels any pending system suspend. */ + mod_pd_ctx.system_suspend.ongoing = false; + + composite_state = req_params->composite_state; + up = is_upwards_transition_propagation(lowest_pd, composite_state); + + /* + * It has already been tested as part of the composite state validation that + * 'highest_level >= lowest_level' and 'highest_level' is lower + * than the highest power level. + */ + lowest_level = get_level_from_tree_pos(lowest_pd->config->tree_pos); + highest_level = get_highest_level_from_composite_state(composite_state); + nb_pds = highest_level - lowest_level + 1; + + + status = FWK_SUCCESS; + pd = lowest_pd; + for (pd_index = 0; pd_index < nb_pds; pd_index++, pd = pd->parent) { + if (up) + level = lowest_level + pd_index; + else { + /* + * When walking down the power domain tree, get the context of the + * next power domain to process as well as its level. + */ + pd = lowest_pd; + for (level = lowest_level; + level < (highest_level - pd_index); level++) + pd = pd->parent; + } + + state = get_level_state_from_composite_state(composite_state, level); + if (state == pd->requested_state){ + continue; + } + + /* + * Check that the requested power state is compatible with the states + * currently requested for the parent and children of the power domain. + */ + parent = pd->parent; + if ((parent != NULL) && + (!is_allowed_by_child(pd, parent->requested_state, state))) { + status = FWK_E_PWRSTATE; + break; + } + + if (!is_allowed_by_children(pd, state)) + continue; + + /* + * A new valid power state is requested for the power domain. Send any + * pending response concerning the previous requested power state. + */ + pd->requested_state = state; + pd->power_state_pre_transition_notification_ctx.valid = false; + respond(pd, FWK_E_OVERWRITTEN); + + if (pd->state_requested_to_driver == state) + continue; + + /* + * If a power state transition has already been initiated for an + * ancestor or descendant, we don't initiate the power state transition + * now. It will be initiated on completion of the transition of one + * of its ancestor or descendant. + */ + if (first_power_state_transition_initiated) + continue; + + /* + * If the parent or a child is not currently in a power state + * compatible with the new requested state for the power domain, do not + * initiate the transition now as well. It will be initiated when the + * parent and the children are in a proper state. + */ + if (!is_allowed_by_parent_and_children(pd, state)) + continue; + + /* + * Defer the power state transition if power state pre-transition + * notification responses need to be waited for. + */ + if (initiate_power_state_pre_transition_notification(pd)) + continue; + + status = initiate_power_state_transition(pd); + first_power_state_transition_initiated = (status == FWK_SUCCESS); + } + + if (!resp_event->response_requested) + return; + + resp_params->status = status; + resp_params->composite_state = composite_state; +} + +/* + * Complete a system suspend + * + * Following the shutdown of the last standing core put all of its ancestors + * in the MOD_PD_STATE_OFF state but the system power domain which is put + * into the state that has been asked for. + * + * target_pd Description of the power domain target of the 'set composite state' + * request to suspend the system in the desired state. + */ +static int complete_system_suspend(struct pd_ctx *target_pd) +{ + enum mod_pd_level level; + unsigned int composite_state = 0; + struct pd_ctx *pd = target_pd; + struct fwk_event resp_event; + struct pd_set_state_response *resp_params = + (struct pd_set_state_response *)(&resp_event.params); + + /* + * Traverse the PD tree bottom-up from current power domain to the top + * to build the composite state with MOD_PD_STATE_OFF power state for all + * levels but the last one. + */ + level = get_level_from_tree_pos(target_pd->config->tree_pos); + do { + composite_state |= ((pd->parent != NULL) ? MOD_PD_STATE_OFF : + mod_pd_ctx.system_suspend.state) + << mod_pd_cs_level_state_shift[level++]; + pd = pd->parent; + } while (pd != NULL); + + /* + * Finally, we need to update the highest valid level in the composite + * state. + */ + composite_state |= (--level) << MOD_PD_CS_LEVEL_SHIFT; + + resp_event = (struct fwk_event) { 0 }; + + process_set_state_request(target_pd, + &((struct pd_set_state_request){ + .composite_state = composite_state, + }), &resp_event); + + return resp_params->status; +} + +/* + * Process a 'get composite state' request. + * + * pd Description of the target of the 'get state' request + * req_params Parameters of the 'get state' request + * resp_params Parameters of the 'get state' request response to be filled in + */ +static void process_get_state_request(struct pd_ctx *pd, + const struct pd_get_state_request *req_params, + struct pd_get_state_response *resp_params) +{ + enum mod_pd_level level = get_level_from_tree_pos(pd->config->tree_pos); + unsigned int composite_state = 0; + if (!req_params->composite) { + resp_params->state = pd->current_state; + } else { + /* + * Traverse the PD tree bottom-up from current power domain to the top, + * collecting node's states and placing them in the correct position in + * the composite state. + */ + do { + composite_state |= pd->current_state << + mod_pd_cs_level_state_shift[level++]; + pd = pd->parent; + } while (pd != NULL); + + /* + * Finally, we need to update the highest valid level in + * the composite state. + */ + composite_state |= (--level) << MOD_PD_CS_LEVEL_SHIFT; + + resp_params->state = composite_state; + } + + resp_params->status = FWK_SUCCESS; +} + +/* + * Process a 'reset' request. + * + * pd Description of the target of the 'reset' request + * resp_params Parameters of the 'reset' request response to be filled in + */ +static void process_reset_request(struct pd_ctx *pd, + struct pd_response *resp_params) +{ + struct pd_ctx *child; + + if (pd->requested_state == MOD_PD_STATE_OFF) { + resp_params->status = FWK_E_PWRSTATE; + return; + } + + child = pd->first_child; + while (child != NULL) { + if ((child->requested_state != MOD_PD_STATE_OFF) || + (child->current_state != MOD_PD_STATE_OFF)) { + resp_params->status = FWK_E_PWRSTATE; + return; + } + child = child->sibling; + } + + resp_params->status = pd->driver_api->reset(pd->driver_id); +} + +/* + * Process a power state transition report describing a transition to a deeper + * state. + * + * \param pd Target power domain context + */ +static void process_power_state_transition_report_deeper_state( + struct pd_ctx *pd) +{ + struct pd_ctx *parent = pd->parent; + unsigned int requested_state = parent->requested_state; + + if (parent == NULL) + return; + + if (parent->state_requested_to_driver == requested_state) + return; + + if (!is_allowed_by_parent_and_children(parent, requested_state)) + return; + + if (!initiate_power_state_pre_transition_notification(parent)) + initiate_power_state_transition(parent); +} + +/* + * Process a power state transition report describing a transition to a + * shallower state. + * + * \param pd Target power domain context + */ +static void process_power_state_transition_report_shallower_state( + struct pd_ctx *pd) +{ + struct pd_ctx *child; + unsigned int requested_state; + + for (child = pd->first_child; child != NULL; child = child->sibling) { + requested_state = child->requested_state; + if (child->state_requested_to_driver == requested_state) + continue; + + if (!is_allowed_by_parent_and_children(child, requested_state)) + return; + + if (!initiate_power_state_pre_transition_notification(child)) + initiate_power_state_transition(child); + } +} + +/* + * Process a power state transition report + * + * \param pd Description of the target of the power state transition report + * \param report_params Parameters of the power state transition report + */ +static void process_power_state_transition_report(struct pd_ctx *pd, + const struct pd_power_state_transition_report *report_params) +{ + unsigned int new_state = report_params->state; + unsigned int previous_state; + struct fwk_event notification_event = { + .id = mod_pd_notification_id_power_state_transition, + .response_requested = true + }; + struct mod_pd_power_state_transition_notification_params *params; + + if (new_state == pd->requested_state) + respond(pd, FWK_SUCCESS); + + previous_state = pd->current_state; + pd->current_state = new_state; + + if (pd->power_state_transition_notification_ctx.pending_responses == 0) { + params = (struct mod_pd_power_state_transition_notification_params *) + notification_event.params; + params->state = new_state; + pd->power_state_transition_notification_ctx.state = new_state; + fwk_notification_notify(¬ification_event, + &pd->power_state_transition_notification_ctx.pending_responses); + } + + if ((mod_pd_ctx.system_suspend.ongoing) && + (pd == mod_pd_ctx.system_suspend.last_core_pd)) { + mod_pd_ctx.system_suspend.ongoing = false; + complete_system_suspend(pd); + } + + if (is_deeper_state(new_state, previous_state)) + process_power_state_transition_report_deeper_state(pd); + else if (is_shallower_state(new_state, previous_state)) + process_power_state_transition_report_shallower_state(pd); +} + +/* + * Process a 'system suspend' request + * + * req_params Parameters of the 'system suspend' request + * resp_params Parameters of the 'system suspend' request response to be filled + * in + */ +static void process_system_suspend_request( + const struct pd_system_suspend_request *req_params, + struct pd_response *resp_params) +{ + int status; + unsigned int pd_idx; + struct pd_ctx *pd; + struct pd_ctx *last_core_pd = NULL; + struct pd_ctx *last_cluster_pd = NULL; + + /* + * All core related power domains have to be in the MOD_PD_STATE_OFF state + * but one core and its ancestors. + */ + for (pd_idx = 0; pd_idx < mod_pd_ctx.pd_count; pd_idx++) { + pd = &mod_pd_ctx.pd_ctx_table[pd_idx]; + if ((pd->requested_state == MOD_PD_STATE_OFF) && + (pd->current_state == MOD_PD_STATE_OFF)) + continue; + + if (pd->config->attributes.pd_type == MOD_PD_TYPE_CORE) { + if (last_core_pd != NULL) { + resp_params->status = FWK_E_STATE; + return; + } + last_core_pd = pd; + } else if (pd->config->attributes.pd_type == MOD_PD_TYPE_CLUSTER) { + if (last_cluster_pd != NULL) { + if (PD_RCAR_CLUSTER1 != pd_idx) { + resp_params->status = FWK_E_STATE; + return; + } + } + last_cluster_pd = pd; + } + } + + if (last_core_pd == NULL) { + status = complete_system_suspend( + (last_cluster_pd != NULL) ? last_cluster_pd : + mod_pd_ctx.system_pd_ctx); + } else { + status = last_core_pd->driver_api->prepare_core_for_system_suspend( + last_core_pd->driver_id); + if (status == FWK_SUCCESS) { + mod_pd_ctx.system_suspend.ongoing = true; + mod_pd_ctx.system_suspend.last_core_pd = last_core_pd; + mod_pd_ctx.system_suspend.state = req_params->state; + last_core_pd->requested_state = + last_core_pd->state_requested_to_driver = MOD_PD_STATE_OFF; + } + } + + resp_params->status = status; +} + +/* + * Process a 'system shutdown' request + * + * req_params Parameters of the 'system shutdown' request + * resp_params Parameters of the 'system shutdown' request response to be filled + * in + */ +static void process_system_shutdown_request( + const struct pd_system_shutdown_request *req_params, + struct pd_response *resp_params) +{ + int status; + unsigned int pd_idx; + struct pd_ctx *pd; + fwk_id_t pd_id; + + for (pd_idx = 0; pd_idx < mod_pd_ctx.pd_count; pd_idx++) { + pd = &mod_pd_ctx.pd_ctx_table[pd_idx]; + pd_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_RCAR_POWER_DOMAIN, pd_idx); + + FWK_LOG_TRACE("[PD] Shutting down %s.", fwk_module_get_name(pd_id)); + + if (pd->driver_api->shutdown != NULL) { + status = pd->driver_api->shutdown(pd->driver_id, + req_params->system_shutdown); + } else + status = pd->driver_api->set_state(pd->driver_id, MOD_PD_STATE_OFF); + + if (status != FWK_SUCCESS) + FWK_LOG_ERR("[PD] ERROR: Shutdown of %s returned %d.", + fwk_module_get_name(pd_id), status); + else + FWK_LOG_TRACE("[PD] %s shutdown.", fwk_module_get_name(pd_id)); + + pd->requested_state = + pd->state_requested_to_driver = + pd->current_state = MOD_PD_STATE_OFF; + } + + resp_params->status = FWK_E_PANIC; +} + +/* + * API functions + */ + +/* Functions common to the public and restricted API */ +static int pd_get_domain_type(fwk_id_t pd_id, enum mod_pd_type *type) +{ + struct pd_ctx *pd; + + if (type == NULL) + return FWK_E_PARAM; + + if (!fwk_module_is_valid_element_id(pd_id)) + return FWK_E_PARAM; + + pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)]; + + *type = pd->config->attributes.pd_type; + + return FWK_SUCCESS; +} + +static int pd_get_domain_parent_id(fwk_id_t pd_id, fwk_id_t *parent_pd_id) +{ + const struct pd_ctx *pd; + + if (parent_pd_id == NULL) + return FWK_E_PARAM; + + if (!fwk_module_is_valid_element_id(pd_id)) + return FWK_E_PARAM; + + pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)]; + + *parent_pd_id = (pd->parent != NULL) ? pd->parent->id : FWK_ID_NONE; + + return FWK_SUCCESS; +} + +/* Functions specific to the restricted API */ + +static int pd_set_state(fwk_id_t pd_id, unsigned int state) +{ + int status; + struct pd_ctx *pd; + enum mod_pd_level level; + struct fwk_event req; + struct fwk_event resp; + struct pd_set_state_request *req_params = + (struct pd_set_state_request *)(&req.params); + struct pd_set_state_response *resp_params = + (struct pd_set_state_response *)(&resp.params); + + pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)]; + + if (!is_valid_state(pd, state)) + return FWK_E_PARAM; + + level = get_level_from_tree_pos(pd->config->tree_pos); + + req = (struct fwk_event) { + .id = FWK_ID_EVENT(FWK_MODULE_IDX_RCAR_POWER_DOMAIN, + PD_EVENT_IDX_SET_STATE), + .target_id = pd_id, + }; + + req_params->composite_state = (level << MOD_PD_CS_LEVEL_SHIFT) | + (state << mod_pd_cs_level_state_shift[level]); + + status = fwk_thread_put_event_and_wait(&req, &resp); + if (status != FWK_SUCCESS) { + return status; + } + + return resp_params->status; +} + +#if 0 +static int pd_set_state_async(fwk_id_t pd_id, + bool response_requested, unsigned int state) +{ + return FWK_SUCCESS; +} +#endif + +static int pd_set_composite_state(fwk_id_t pd_id, uint32_t composite_state) +{ + int status; + struct pd_ctx *pd; + struct fwk_event req; + struct fwk_event resp; + struct pd_set_state_request *req_params = + (struct pd_set_state_request *)(&req.params); + struct pd_set_state_response *resp_params = + (struct pd_set_state_response *)(&resp.params); + + pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)]; + + if (!is_valid_composite_state(pd, composite_state)) + return FWK_E_PARAM; + + req = (struct fwk_event) { + .id = FWK_ID_EVENT(FWK_MODULE_IDX_RCAR_POWER_DOMAIN, + PD_EVENT_IDX_SET_STATE), + .source_id = pd->driver_id, + .target_id = pd_id, + }; + + req_params->composite_state = composite_state; + + status = fwk_thread_put_event_and_wait(&req, &resp); + if (status != FWK_SUCCESS) + return status; + + return resp_params->status; +} + +static int pd_set_composite_state_async(fwk_id_t pd_id, + bool response_requested, + uint32_t composite_state) +{ + return pd_set_composite_state(pd_id, composite_state); +} + +static int pd_get_state(fwk_id_t pd_id, unsigned int *state) +{ + int status; + struct fwk_event req; + struct fwk_event resp; + struct pd_get_state_request *req_params = + (struct pd_get_state_request *)(&req.params); + struct pd_get_state_response *resp_params = + (struct pd_get_state_response *)(&resp.params); + + if (state == NULL) + return FWK_E_PARAM; + + req = (struct fwk_event) { + .id = FWK_ID_EVENT(FWK_MODULE_IDX_RCAR_POWER_DOMAIN, + PD_EVENT_IDX_GET_STATE), + .target_id = pd_id, + }; + + req_params->composite = false; + + status = fwk_thread_put_event_and_wait(&req, &resp); + if (status != FWK_SUCCESS) + return status; + + if (resp_params->status != FWK_SUCCESS) + return resp_params->status; + + *state = resp_params->state; + + return FWK_SUCCESS; +} + +#if 0 +static int pd_get_composite_state(fwk_id_t pd_id, unsigned int *composite_state) +{ + int status; + struct fwk_event req; + struct fwk_event resp; + struct pd_get_state_request *req_params = + (struct pd_get_state_request *)(&req.params); + struct pd_get_state_response *resp_params = + (struct pd_get_state_response *)(&resp.params); + + if (composite_state == NULL) + return FWK_E_PARAM; + + req = (struct fwk_event) { + .id = FWK_ID_EVENT(FWK_MODULE_IDX_RCAR_POWER_DOMAIN, + PD_EVENT_IDX_GET_STATE), + .target_id = pd_id, + }; + + req_params->composite = true; + + status = fwk_thread_put_event_and_wait(&req, &resp); + if (status != FWK_SUCCESS) + return status; + + if (resp_params->status != FWK_SUCCESS) + return resp_params->status; + + *composite_state = resp_params->state; + + return FWK_SUCCESS; +} +#endif + +static int pd_reset(fwk_id_t pd_id) +{ + int status; + struct fwk_event req; + struct fwk_event resp; + struct pd_response *resp_params = (struct pd_response *)(&resp.params); + + req = (struct fwk_event) { + .id = FWK_ID_EVENT(FWK_MODULE_IDX_RCAR_POWER_DOMAIN, + PD_EVENT_IDX_RESET), + .target_id = pd_id, + }; + + status = fwk_thread_put_event_and_wait(&req, &resp); + if (status != FWK_SUCCESS) + return status; + + return resp_params->status; + +} + +static int pd_system_suspend(unsigned int state) +{ + int status; + struct fwk_event req; + struct fwk_event resp; + struct pd_system_suspend_request *req_params = + (struct pd_system_suspend_request *)(&req.params); + struct pd_response *resp_params = (struct pd_response *)(&resp.params); + + req = (struct fwk_event) { + .id = FWK_ID_EVENT(FWK_MODULE_IDX_RCAR_POWER_DOMAIN, + PD_EVENT_IDX_SYSTEM_SUSPEND), + .target_id = fwk_module_id_rcar_power_domain, + }; + + req_params->state = state; + + status = fwk_thread_put_event_and_wait(&req, &resp); + if (status != FWK_SUCCESS) + return status; + + return resp_params->status; +} + +static int pd_system_shutdown(enum mod_pd_system_shutdown system_shutdown) +{ + int status; + struct fwk_event req; + struct fwk_event resp; + struct pd_system_shutdown_request *req_params = + (struct pd_system_shutdown_request *)(&req.params); + struct pd_response *resp_params = (struct pd_response *)(&resp.params); + + req = (struct fwk_event) { + .id = FWK_ID_EVENT(FWK_MODULE_IDX_RCAR_POWER_DOMAIN, + PD_EVENT_IDX_SYSTEM_SHUTDOWN), + .target_id = fwk_module_id_rcar_power_domain, + }; + + req_params->system_shutdown = system_shutdown; + + status = fwk_thread_put_event_and_wait(&req, &resp); + if (status != FWK_SUCCESS) + return status; + + return resp_params->status; +} + +/* Functions specific to the driver input API */ + +static int pd_reset_async(fwk_id_t pd_id, bool response_requested) +{ + struct fwk_event req; + struct pd_ctx *pd; + + pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)]; + + req = (struct fwk_event) { + .id = FWK_ID_EVENT(FWK_MODULE_IDX_RCAR_POWER_DOMAIN, + PD_EVENT_IDX_RESET), + .source_id = pd->driver_id, + .target_id = pd_id, + .response_requested = response_requested, + }; + + return fwk_thread_put_event(&req); +} + +static int report_power_state_transition(const struct pd_ctx *pd, + unsigned int state) +{ + struct fwk_event report; + struct pd_power_state_transition_report *report_params = + (struct pd_power_state_transition_report *)(&report.params); + + report = (struct fwk_event){ + .source_id = pd->driver_id, + .target_id = pd->id, + .id = FWK_ID_EVENT(FWK_MODULE_IDX_RCAR_POWER_DOMAIN, + PD_EVENT_IDX_REPORT_POWER_STATE_TRANSITION) + }; + report_params->state = state; + + return fwk_thread_put_event(&report); +} + +static int pd_report_power_state_transition(fwk_id_t pd_id, unsigned int state) +{ + const struct pd_ctx *pd; + + pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)]; + + return report_power_state_transition(pd, state); +} + +static int pd_get_last_core_pd_id(fwk_id_t *last_core_pd_id) +{ + if (last_core_pd_id == NULL) + return FWK_E_PARAM; + + *last_core_pd_id = mod_pd_ctx.system_suspend.last_core_pd->id; + + return FWK_SUCCESS; +} + +/* Module APIs */ + +static const struct mod_pd_public_api pd_public_api = { + .get_domain_type = pd_get_domain_type, + .get_domain_parent_id = pd_get_domain_parent_id, +}; + +static const struct mod_pd_restricted_api pd_restricted_api = { + .get_domain_type = pd_get_domain_type, + .get_domain_parent_id = pd_get_domain_parent_id, + + .set_state = pd_set_state, + .set_state_async = pd_set_composite_state_async, + .get_state = pd_get_state, + .reset = pd_reset, + .system_suspend = pd_system_suspend, + .system_shutdown = pd_system_shutdown +}; + +static const struct mod_pd_driver_input_api pd_driver_input_api = { + .set_state_async = pd_set_composite_state_async, + .reset_async = pd_reset_async, + .report_power_state_transition = pd_report_power_state_transition, + .get_last_core_pd_id = pd_get_last_core_pd_id, +}; + +/* + * Framework handlers + */ +static int pd_init(fwk_id_t module_id, unsigned int dev_count, + const void *data) +{ + if ((data == NULL) || (dev_count == 0)) + return FWK_E_PARAM; + + mod_pd_ctx.config = (struct mod_power_domain_config *)data; + + if ((mod_pd_ctx.config->authorized_id_table == NULL) && + (mod_pd_ctx.config->authorized_id_table_size != 0)) + return FWK_E_PARAM; + + mod_pd_ctx.pd_ctx_table = fwk_mm_calloc(dev_count, sizeof(struct pd_ctx)); + if (mod_pd_ctx.pd_ctx_table == NULL) + return FWK_E_NOMEM; + + mod_pd_ctx.pd_count = dev_count; + mod_pd_ctx.system_pd_ctx = &mod_pd_ctx.pd_ctx_table[dev_count - 1]; + + return FWK_SUCCESS; +} + +static int pd_power_domain_init(fwk_id_t pd_id, unsigned int unused, + const void *config) +{ + static uint64_t max_tree_pos = MOD_PD_INVALID_TREE_POS; + + const struct mod_power_domain_element_config *pd_config = + (const struct mod_power_domain_element_config *)config; + struct pd_ctx *pd; + unsigned int state; + + pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(pd_id)]; + + if (!is_valid_tree_pos(pd_config->tree_pos)) + return FWK_E_PARAM; + + if (pd_config->attributes.pd_type >= + MOD_PD_TYPE_COUNT) + return FWK_E_PARAM; + + /* + * Check that the power domains are declared by increasing order of their + * tree position. + */ + if ((max_tree_pos != MOD_PD_INVALID_TREE_POS) && + (pd_config->tree_pos <= max_tree_pos)) + return FWK_E_PARAM; + max_tree_pos = pd_config->tree_pos; + + if ((pd_config->allowed_state_mask_table == NULL) || + (pd_config->allowed_state_mask_table_size == 0)) + return FWK_E_PARAM; + + pd->allowed_state_mask_table = pd_config->allowed_state_mask_table; + pd->allowed_state_mask_table_size = + pd_config->allowed_state_mask_table_size; + + for (state = 0; state < pd->allowed_state_mask_table_size; state++) + pd->valid_state_mask |= pd->allowed_state_mask_table[state]; + + pd->id = pd_id; + pd->config = pd_config; + + return FWK_SUCCESS; +} + +static int pd_post_init(fwk_id_t module_id) +{ + return FWK_SUCCESS; +} + +static int pd_bind(fwk_id_t id, unsigned int round) +{ + int status; + struct pd_ctx *pd; + const struct mod_power_domain_element_config *config; + struct mod_pd_driver_api *driver_api = NULL; + + /* Nothing to do but during the first round of calls */ + if (round != 0){ + return FWK_SUCCESS; + } + +#if 0 + if (fwk_id_is_type(id, FWK_ID_TYPE_MODULE)) { + return fwk_module_bind(FWK_ID_MODULE(FWK_MODULE_IDX_LOG), + FWK_ID_API(FWK_MODULE_IDX_LOG, 0), &mod_pd_ctx.log_api); + } +#endif + + pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(id)]; + config = pd->config; + + + status = fwk_module_bind(config->driver_id, config->api_id, &driver_api); + if (status != FWK_SUCCESS){ + return status; + } + + pd->driver_id = config->driver_id; + if ((driver_api->set_state == NULL) || + (driver_api->get_state == NULL) || + (driver_api->reset == NULL) || + ((config->attributes.pd_type == MOD_PD_TYPE_CORE) && + (driver_api->prepare_core_for_system_suspend == NULL))){ + return FWK_E_PARAM; + } + + pd->driver_api = driver_api; + + return FWK_SUCCESS; +} + +static int pd_start(fwk_id_t id) +{ + struct pd_ctx *pd; + int index; + struct fwk_event notification_event = { + .source_id = FWK_ID_ELEMENT( + FWK_MODULE_IDX_RCAR_POWER_DOMAIN, + CONFIG_POWER_DOMAIN_CHILD_COUNT + rcar_core_get_count()), + .id = mod_pd_notification_id_power_state_transition, + .response_requested = true, + }; + struct mod_pd_power_state_transition_notification_params *params; + unsigned int notification_count; + + /* Nothing to do for elements */ + if (fwk_module_is_valid_element_id(id)) + return FWK_SUCCESS; + + for (index = mod_pd_ctx.pd_count - 1; index >= 0; index--) { + pd = &mod_pd_ctx.pd_ctx_table[index]; + pd->requested_state = MOD_PD_STATE_OFF; + pd->state_requested_to_driver = MOD_PD_STATE_OFF; + pd->current_state = MOD_PD_STATE_OFF; + } + + /* CPU0 is always on. */ + pd = &mod_pd_ctx.pd_ctx_table[PD_RCAR_CLUS0CORE0]; + pd->current_state = MOD_PD_STATE_ON; + /* CLUSTER0 is always on. */ + pd = &mod_pd_ctx.pd_ctx_table[PD_RCAR_CLUSTER0]; + pd->current_state = MOD_PD_STATE_ON; + /* CLUSTER1 is always on. */ + pd = &mod_pd_ctx.pd_ctx_table[PD_RCAR_CLUSTER1]; + pd->current_state = MOD_PD_STATE_ON; + + params = (struct mod_pd_power_state_transition_notification_params *) + notification_event.params; + params->state = MOD_PD_STATE_ON; + fwk_notification_notify(¬ification_event, ¬ification_count); + + return FWK_SUCCESS; +} + +static int pd_process_bind_request(fwk_id_t source_id, fwk_id_t target_id, + fwk_id_t api_id, const void **api) +{ + struct pd_ctx *pd; + unsigned int id_idx; + + switch (fwk_id_get_api_idx(api_id)) { + case MOD_PD_API_IDX_PUBLIC: + if (!fwk_id_is_type(target_id, FWK_ID_TYPE_MODULE)) + return FWK_E_ACCESS; + *api = &pd_public_api; + break; + + case MOD_PD_API_IDX_RESTRICTED: + if (!fwk_id_is_type(target_id, FWK_ID_TYPE_MODULE)) + return FWK_E_ACCESS; + if (mod_pd_ctx.config->authorized_id_table_size == 0) { + *api = &pd_restricted_api; + return FWK_SUCCESS; + } + for (id_idx = 0; + id_idx < mod_pd_ctx.config->authorized_id_table_size; + id_idx++) { + + if (fwk_id_is_equal(source_id, + mod_pd_ctx.config->authorized_id_table[id_idx])) { + *api = &pd_restricted_api; + return FWK_SUCCESS; + } + } + return FWK_E_ACCESS; + + case MOD_PD_API_IDX_DRIVER_INPUT: + if (!fwk_id_is_type(target_id, FWK_ID_TYPE_ELEMENT)) + return FWK_E_ACCESS; + pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(target_id)]; + if (!fwk_id_is_equal(source_id, pd->driver_id)) + return FWK_E_ACCESS; + *api = &pd_driver_input_api; + break; + + default: + return FWK_E_PARAM; + } + + return FWK_SUCCESS; +} + +static int pd_process_event(const struct fwk_event *event, + struct fwk_event *resp) +{ + struct pd_ctx *pd = NULL; + + if (fwk_id_is_type(event->target_id, FWK_ID_TYPE_ELEMENT)) + pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(event->target_id)]; + + switch (fwk_id_get_event_idx(event->id)) { + case PD_EVENT_IDX_SET_STATE: + assert(pd != NULL); + + process_set_state_request(pd, + (struct pd_set_state_request *)event->params, resp); + + return FWK_SUCCESS; + + case PD_EVENT_IDX_GET_STATE: + assert(pd != NULL); + + process_get_state_request(pd, + (struct pd_get_state_request *)event->params, + (struct pd_get_state_response *)resp->params); + + return FWK_SUCCESS; + + case PD_EVENT_IDX_RESET: + assert(pd != NULL); + + process_reset_request(pd, (struct pd_response *)resp->params); + + return FWK_SUCCESS; + + case PD_EVENT_IDX_REPORT_POWER_STATE_TRANSITION: + assert(pd != NULL); + + process_power_state_transition_report(pd, + (struct pd_power_state_transition_report *)event->params); + + return FWK_SUCCESS; + + case PD_EVENT_IDX_SYSTEM_SUSPEND: + process_system_suspend_request( + (struct pd_system_suspend_request *)event->params, + (struct pd_response *)resp->params); + + return FWK_SUCCESS; + + case PD_EVENT_IDX_SYSTEM_SHUTDOWN: + process_system_shutdown_request( + (struct pd_system_shutdown_request *)event->params, + (struct pd_response *)resp->params); + + return FWK_SUCCESS; + + default: + FWK_LOG_ERR("[PD] ERROR: Invalid power state request: <%d>.", + event->id.value); + + return FWK_E_PARAM; + } +} + +static int process_power_state_pre_transition_notification_response( + struct pd_ctx *pd, + struct mod_pd_power_state_pre_transition_notification_resp_params *params) +{ + if (pd->power_state_pre_transition_notification_ctx.pending_responses + == 0) { + assert(false); + return FWK_E_PANIC; + } + + if (params->status != FWK_SUCCESS) { + pd->power_state_pre_transition_notification_ctx.response_status = + FWK_E_DEVICE; + } + + pd->power_state_pre_transition_notification_ctx.pending_responses--; + if (pd->power_state_pre_transition_notification_ctx.pending_responses != 0) + return FWK_SUCCESS; + + if (pd->power_state_pre_transition_notification_ctx.valid == true) { + /* + * All the notification responses have been received, the requested + * state for the power domain has not changed in the + * meantime and all the notified entities agreed on the power state + * transition, proceed with it. + */ + if (pd->power_state_pre_transition_notification_ctx.response_status == + FWK_SUCCESS) + initiate_power_state_transition(pd); + } else { + /* + * All the notification responses have been received but the + * requested state for the power domain has changed, start the + * processings for the new requested state. + */ + if ((pd->requested_state == pd->state_requested_to_driver) || + (!is_allowed_by_parent_and_children(pd, pd->requested_state))) + return FWK_SUCCESS; + + if (!initiate_power_state_pre_transition_notification(pd)) + initiate_power_state_transition(pd); + } + + return FWK_SUCCESS; +} + +static int process_power_state_transition_notification_response( + struct pd_ctx *pd) +{ + struct fwk_event notification_event; + struct mod_pd_power_state_transition_notification_params *params; + + if (pd->power_state_transition_notification_ctx.pending_responses == 0) { + assert(false); + return FWK_E_PANIC; + } + + pd->power_state_transition_notification_ctx.pending_responses--; + if (pd->power_state_transition_notification_ctx.pending_responses != 0) + return FWK_SUCCESS; + + if (pd->power_state_transition_notification_ctx.state == pd->current_state) + return FWK_SUCCESS; + + /* + * While receiving the responses, the power state of the power domain + * has changed. Send a notification for the current power state. + */ + notification_event.id = mod_pd_notification_id_power_state_transition; + notification_event.response_requested = true; + params = (struct mod_pd_power_state_transition_notification_params *) + notification_event.params; + params->state = pd->current_state; + + pd->power_state_transition_notification_ctx.state = pd->current_state; + fwk_notification_notify(¬ification_event, + &pd->power_state_transition_notification_ctx.pending_responses); + + return FWK_SUCCESS; +} + +static int pd_process_notification(const struct fwk_event *event, + struct fwk_event *resp) +{ + struct pd_ctx *pd; + + /* Only responses are expected. */ + if (!event->is_response) { + assert(false); + return FWK_E_SUPPORT; + } + + if (!fwk_module_is_valid_element_id(event->target_id)) { + assert(false); + return FWK_E_PARAM; + } + + pd = &mod_pd_ctx.pd_ctx_table[fwk_id_get_element_idx(event->target_id)]; + + if (fwk_id_is_equal(event->id, + mod_pd_notification_id_power_state_transition)) + return process_power_state_transition_notification_response(pd); + + return process_power_state_pre_transition_notification_response(pd, + (struct mod_pd_power_state_pre_transition_notification_resp_params *) + event->params); +} + +/* Module definition */ +const struct fwk_module module_rcar_power_domain = { + .name = "POWER DOMAIN", + .type = FWK_MODULE_TYPE_HAL, + .api_count = MOD_PD_API_IDX_COUNT, + .event_count = PD_EVENT_COUNT, + .notification_count = MOD_PD_NOTIFICATION_COUNT, + .init = pd_init, + .element_init = pd_power_domain_init, + .post_init = pd_post_init, + .bind = pd_bind, + .start = pd_start, + .process_bind_request = pd_process_bind_request, + .process_event = pd_process_event, + .process_notification = pd_process_notification +}; diff --git a/product/rcar/scp_ramfw/config_rcar_power_domain.c b/product/rcar/scp_ramfw/config_rcar_power_domain.c new file mode 100644 index 000000000..0bda17287 --- /dev/null +++ b/product/rcar/scp_ramfw/config_rcar_power_domain.c @@ -0,0 +1,362 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static const char *core_pd_name_table[RCAR_CORE_PER_CLUSTER_MAX] = { + "CLUS0CORE0", "CLUS0CORE1", "CLUS0CORE2", "CLUS0CORE3", + "CLUS1CORE0", "CLUS1CORE1", "CLUS1CORE2", "CLUS1CORE3", +}; + +/* Mask of the allowed states for the systop power domain */ +static const uint32_t systop_allowed_state_mask_table[] = { + [0] = MOD_PD_STATE_OFF_MASK | MOD_PD_STATE_ON_MASK | + (1 << MOD_SYSTEM_POWER_POWER_STATE_SLEEP0) | + (1 << MOD_SYSTEM_POWER_POWER_STATE_SLEEP1) +}; +/* + * Mask of the allowed states for the top level power domains + * (but the cluster power domains) depending on the system states. + */ +static const uint32_t toplevel_allowed_state_mask_table[] = { + [MOD_PD_STATE_OFF] = MOD_PD_STATE_OFF_MASK, + [MOD_PD_STATE_ON] = MOD_PD_STATE_OFF_MASK | MOD_PD_STATE_ON_MASK, + [MOD_SYSTEM_POWER_POWER_STATE_SLEEP0] = MOD_PD_STATE_OFF_MASK, + [MOD_SYSTEM_POWER_POWER_STATE_SLEEP1] = MOD_PD_STATE_OFF_MASK +}; +/* + * Mask of the allowed states for the cluster power domain depending on the + * system states. + */ +static const uint32_t cluster_pd_allowed_state_mask_table[] = { + [MOD_PD_STATE_OFF] = MOD_PD_STATE_OFF_MASK, + [MOD_PD_STATE_ON] = MOD_PD_STATE_OFF_MASK | MOD_PD_STATE_ON_MASK | + MOD_PD_STATE_SLEEP_MASK, + [MOD_SYSTEM_POWER_POWER_STATE_SLEEP0] = MOD_PD_STATE_OFF_MASK, + [MOD_SYSTEM_POWER_POWER_STATE_SLEEP1] = MOD_PD_STATE_OFF_MASK +}; + +/* Mask of the allowed states for a core depending on the cluster states. */ +static const uint32_t core_pd_allowed_state_mask_table[] = { + [MOD_PD_STATE_OFF] = MOD_PD_STATE_OFF_MASK, + [MOD_PD_STATE_ON] = MOD_PD_STATE_OFF_MASK | MOD_PD_STATE_ON_MASK | + MOD_PD_STATE_SLEEP_MASK, + [MOD_PD_STATE_SLEEP] = MOD_PD_STATE_OFF_MASK | MOD_PD_STATE_SLEEP_MASK, +}; + +/* Power module specific configuration data (none) */ +static const struct mod_power_domain_config rcar_power_domain_config = { }; + +static struct fwk_element rcar_power_domain_static_element_table[] = { + [CONFIG_POWER_DOMAIN_CHILD_CLUSTER0] = { + .name = "CLUS0", + .data = &((struct mod_power_domain_element_config) { + .attributes.pd_type = MOD_PD_TYPE_CLUSTER, + .tree_pos = MOD_PD_TREE_POS( + MOD_PD_LEVEL_2, + 0, + 0, + CONFIG_POWER_DOMAIN_CHILD_CLUSTER0, + 0), + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_PD_CORE, + RCAR_PD_CORE_ELEMENT_IDX_CLU0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_PD_CORE, + MOD_RCAR_PD_SYSC_API_IDX_POWER_DOMAIN_DRIVER), + .allowed_state_mask_table = cluster_pd_allowed_state_mask_table, + .allowed_state_mask_table_size = + FWK_ARRAY_SIZE(cluster_pd_allowed_state_mask_table) + }), + }, + [CONFIG_POWER_DOMAIN_CHILD_CLUSTER1] = { + .name = "CLUS1", + .data = &((struct mod_power_domain_element_config) { + .attributes.pd_type = MOD_PD_TYPE_CLUSTER, + .tree_pos = MOD_PD_TREE_POS( + MOD_PD_LEVEL_2, + 0, + 0, + CONFIG_POWER_DOMAIN_CHILD_CLUSTER1, + 0), + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_PD_CORE, + RCAR_PD_CORE_ELEMENT_IDX_CLU1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_PD_CORE, + MOD_RCAR_PD_SYSC_API_IDX_POWER_DOMAIN_DRIVER), + .allowed_state_mask_table = cluster_pd_allowed_state_mask_table, + .allowed_state_mask_table_size = + FWK_ARRAY_SIZE(cluster_pd_allowed_state_mask_table) + }), + }, + [CONFIG_POWER_DOMAIN_CHILD_A3IR] = { + .name = "a3ir", + .data = &((struct mod_power_domain_element_config) { + .attributes.pd_type = MOD_PD_TYPE_DEVICE, + .tree_pos = MOD_PD_TREE_POS( + MOD_PD_LEVEL_2, + 0, + 0, + CONFIG_POWER_DOMAIN_CHILD_A3IR, + 0), + .driver_id = FWK_ID_ELEMENT_INIT( + FWK_MODULE_IDX_RCAR_PD_SYSC, RCAR_PD_SYSC_ELEMENT_IDX_A3IR), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_PD_SYSC, 0), + .allowed_state_mask_table = toplevel_allowed_state_mask_table, + .allowed_state_mask_table_size = + FWK_ARRAY_SIZE(toplevel_allowed_state_mask_table) + }), + }, + [CONFIG_POWER_DOMAIN_CHILD_3DGE] = { + .name = "3dg-e", + .data = &((struct mod_power_domain_element_config) { + .attributes.pd_type = MOD_PD_TYPE_DEVICE, + .tree_pos = MOD_PD_TREE_POS( + MOD_PD_LEVEL_2, + 0, + 0, + CONFIG_POWER_DOMAIN_CHILD_3DGE, + 0), + .driver_id = FWK_ID_ELEMENT_INIT( + FWK_MODULE_IDX_RCAR_PD_SYSC, RCAR_PD_SYSC_ELEMENT_IDX_3DGE), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_PD_SYSC, 0), + .allowed_state_mask_table = toplevel_allowed_state_mask_table, + .allowed_state_mask_table_size = + FWK_ARRAY_SIZE(toplevel_allowed_state_mask_table) + }), + }, + [CONFIG_POWER_DOMAIN_CHILD_3DGD] = { + .name = "3dg-d", + .data = &((struct mod_power_domain_element_config) { + .attributes.pd_type = MOD_PD_TYPE_DEVICE, + .tree_pos = MOD_PD_TREE_POS( + MOD_PD_LEVEL_2, + 0, + 0, + CONFIG_POWER_DOMAIN_CHILD_3DGD, + 0), + .driver_id = FWK_ID_ELEMENT_INIT( + FWK_MODULE_IDX_RCAR_PD_SYSC, RCAR_PD_SYSC_ELEMENT_IDX_3DGD), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_PD_SYSC, 0), + .allowed_state_mask_table = toplevel_allowed_state_mask_table, + .allowed_state_mask_table_size = + FWK_ARRAY_SIZE(toplevel_allowed_state_mask_table) + }), + }, + [CONFIG_POWER_DOMAIN_CHILD_3DGC] = { + .name = "3dg-c", + .data = &((struct mod_power_domain_element_config) { + .attributes.pd_type = MOD_PD_TYPE_DEVICE, + .tree_pos = MOD_PD_TREE_POS( + MOD_PD_LEVEL_2, + 0, + 0, + CONFIG_POWER_DOMAIN_CHILD_3DGC, + 0), + .driver_id = FWK_ID_ELEMENT_INIT( + FWK_MODULE_IDX_RCAR_PD_SYSC, RCAR_PD_SYSC_ELEMENT_IDX_3DGC), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_PD_SYSC, 0), + .allowed_state_mask_table = toplevel_allowed_state_mask_table, + .allowed_state_mask_table_size = + FWK_ARRAY_SIZE(toplevel_allowed_state_mask_table) + }), + }, + [CONFIG_POWER_DOMAIN_CHILD_3DGB] = { + .name = "3dg-b", + .data = &((struct mod_power_domain_element_config) { + .attributes.pd_type = MOD_PD_TYPE_DEVICE, + .tree_pos = MOD_PD_TREE_POS( + MOD_PD_LEVEL_2, + 0, + 0, + CONFIG_POWER_DOMAIN_CHILD_3DGB, + 0), + .driver_id = FWK_ID_ELEMENT_INIT( + FWK_MODULE_IDX_RCAR_PD_SYSC, RCAR_PD_SYSC_ELEMENT_IDX_3DGB), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_PD_SYSC, 0), + .allowed_state_mask_table = toplevel_allowed_state_mask_table, + .allowed_state_mask_table_size = + FWK_ARRAY_SIZE(toplevel_allowed_state_mask_table) + }), + }, + [CONFIG_POWER_DOMAIN_CHILD_3DGA] = { + .name = "3dg-a", + .data = &((struct mod_power_domain_element_config) { + .attributes.pd_type = MOD_PD_TYPE_DEVICE, + .tree_pos = MOD_PD_TREE_POS( + MOD_PD_LEVEL_2, + 0, + 0, + CONFIG_POWER_DOMAIN_CHILD_3DGA, + 0), + .driver_id = FWK_ID_ELEMENT_INIT( + FWK_MODULE_IDX_RCAR_PD_SYSC, RCAR_PD_SYSC_ELEMENT_IDX_3DGA), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_PD_SYSC, 0), + .allowed_state_mask_table = toplevel_allowed_state_mask_table, + .allowed_state_mask_table_size = + FWK_ARRAY_SIZE(toplevel_allowed_state_mask_table) + }), + }, + [CONFIG_POWER_DOMAIN_CHILD_A2VC1] = { + .name = "a2vc1", + .data = &((struct mod_power_domain_element_config) { + .attributes.pd_type = MOD_PD_TYPE_DEVICE, + .tree_pos = MOD_PD_TREE_POS( + MOD_PD_LEVEL_2, + 0, + 0, + CONFIG_POWER_DOMAIN_CHILD_A2VC1, + 0), + .driver_id = FWK_ID_ELEMENT_INIT( + FWK_MODULE_IDX_RCAR_PD_SYSC, RCAR_PD_SYSC_ELEMENT_IDX_A2VC1), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_PD_SYSC, 0), + .allowed_state_mask_table = toplevel_allowed_state_mask_table, + .allowed_state_mask_table_size = + FWK_ARRAY_SIZE(toplevel_allowed_state_mask_table) + }), + }, + [CONFIG_POWER_DOMAIN_CHILD_A3VC] = { + .name = "a3vc", + .data = &((struct mod_power_domain_element_config) { + .attributes.pd_type = MOD_PD_TYPE_DEVICE, + .tree_pos = MOD_PD_TREE_POS( + MOD_PD_LEVEL_2, + 0, + 0, + CONFIG_POWER_DOMAIN_CHILD_A3VC, + 0), + .driver_id = FWK_ID_ELEMENT_INIT( + FWK_MODULE_IDX_RCAR_PD_SYSC, RCAR_PD_SYSC_ELEMENT_IDX_A3VC), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_PD_SYSC, 0), + .allowed_state_mask_table = toplevel_allowed_state_mask_table, + .allowed_state_mask_table_size = + FWK_ARRAY_SIZE(toplevel_allowed_state_mask_table) + }), + }, + [CONFIG_POWER_DOMAIN_CHILD_CR7] = { + .name = "cr7", + .data = &((struct mod_power_domain_element_config) { + .attributes.pd_type = MOD_PD_TYPE_DEVICE, + .tree_pos = MOD_PD_TREE_POS( + MOD_PD_LEVEL_2, + 0, + 0, + CONFIG_POWER_DOMAIN_CHILD_CR7, + 0), + .driver_id = FWK_ID_ELEMENT_INIT( + FWK_MODULE_IDX_RCAR_PD_SYSC, RCAR_PD_SYSC_ELEMENT_IDX_CR7), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_PD_SYSC, 0), + .allowed_state_mask_table = toplevel_allowed_state_mask_table, + .allowed_state_mask_table_size = + FWK_ARRAY_SIZE(toplevel_allowed_state_mask_table) + }), + }, + [CONFIG_POWER_DOMAIN_CHILD_A3VP] = { + .name = "a3vp", + .data = &((struct mod_power_domain_element_config) { + .attributes.pd_type = MOD_PD_TYPE_DEVICE, + .tree_pos = MOD_PD_TREE_POS( + MOD_PD_LEVEL_2, + 0, + 0, + CONFIG_POWER_DOMAIN_CHILD_A3VP, + 0), + .driver_id = FWK_ID_ELEMENT_INIT( + FWK_MODULE_IDX_RCAR_PD_SYSC, RCAR_PD_SYSC_ELEMENT_IDX_A3VP), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_PD_SYSC, 0), + .allowed_state_mask_table = toplevel_allowed_state_mask_table, + .allowed_state_mask_table_size = + FWK_ARRAY_SIZE(toplevel_allowed_state_mask_table) + }), + }, + [CONFIG_POWER_DOMAIN_CHILD_COUNT] = { + .name = "SYSTOP", + .data = &((struct mod_power_domain_element_config) { + .attributes.pd_type = MOD_PD_TYPE_SYSTEM, + .tree_pos = MOD_PD_TREE_POS( + MOD_PD_LEVEL_2, 0, 0, 0, 0), + .driver_id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_SYSTEM_POWER), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_SYSTEM_POWER, + MOD_SYSTEM_POWER_API_IDX_PD_DRIVER), + .allowed_state_mask_table = systop_allowed_state_mask_table, + .allowed_state_mask_table_size = + FWK_ARRAY_SIZE(systop_allowed_state_mask_table) + }), + }, +}; + +/* + * Function definitions with internal linkage + */ +static const struct fwk_element *rcar_power_domain_get_element_table + (fwk_id_t module_id) +{ + struct fwk_element *element_table, *element; + struct mod_power_domain_element_config *pd_config_table, *pd_config; + unsigned int core_idx; + + element_table = fwk_mm_calloc( + rcar_core_get_count() + + FWK_ARRAY_SIZE(rcar_power_domain_static_element_table) + + 1, /* Terminator */ + sizeof(struct fwk_element)); + if (element_table == NULL) + return NULL; + + pd_config_table = fwk_mm_calloc(rcar_core_get_count(), + sizeof(struct mod_power_domain_element_config)); + if (pd_config_table == NULL) + return NULL; + + for (core_idx = 0; core_idx < rcar_core_get_count(); core_idx++) { + element = &element_table[core_idx]; + pd_config = &pd_config_table[core_idx]; + + element->name = core_pd_name_table[core_idx]; + element->data = pd_config; + + pd_config->attributes.pd_type = MOD_PD_TYPE_CORE, + pd_config->tree_pos = MOD_PD_TREE_POS( + MOD_PD_LEVEL_2, + 0, + 0, + CONFIG_POWER_DOMAIN_CHILD_CLUSTER0, + core_idx), + pd_config->driver_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_RCAR_PD_CORE, + core_idx), + pd_config->api_id = FWK_ID_API( + FWK_MODULE_IDX_RCAR_PD_CORE, + MOD_RCAR_PD_SYSC_API_IDX_POWER_DOMAIN_DRIVER), + pd_config->allowed_state_mask_table = core_pd_allowed_state_mask_table, + pd_config->allowed_state_mask_table_size = + FWK_ARRAY_SIZE(core_pd_allowed_state_mask_table); + } + + memcpy(element_table + rcar_core_get_count(), + rcar_power_domain_static_element_table, + sizeof(rcar_power_domain_static_element_table)); + + return element_table; +} + +/* + * Power module configuration data + */ +struct fwk_module_config config_rcar_power_domain = { + .elements = FWK_MODULE_DYNAMIC_ELEMENTS(rcar_power_domain_get_element_table), + .data = &rcar_power_domain_config, +}; diff --git a/product/rcar/scp_ramfw/config_rcar_power_domain.h b/product/rcar/scp_ramfw/config_rcar_power_domain.h new file mode 100644 index 000000000..6e7b56013 --- /dev/null +++ b/product/rcar/scp_ramfw/config_rcar_power_domain.h @@ -0,0 +1,38 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef CONFIG_RCAR_POWER_DOMAIN_H +#define CONFIG_RCAR_POWER_DOMAIN_H + +#include + +#define CONFIG_POWER_DOMAIN_CORE_CLUS0CORE0 PD_RCAR_CLUS0CORE0 +#define CONFIG_POWER_DOMAIN_CORE_CLUS0CORE1 PD_RCAR_CLUS0CORE1 +#define CONFIG_POWER_DOMAIN_CORE_CLUS0CORE2 PD_RCAR_CLUS0CORE2 +#define CONFIG_POWER_DOMAIN_CORE_CLUS0CORE3 PD_RCAR_CLUS0CORE3 +#define CONFIG_POWER_DOMAIN_CORE_CLUS1CORE0 PD_RCAR_CLUS1CORE0 +#define CONFIG_POWER_DOMAIN_CORE_CLUS1CORE1 PD_RCAR_CLUS1CORE1 +#define CONFIG_POWER_DOMAIN_CORE_CLUS1CORE2 PD_RCAR_CLUS1CORE2 +#define CONFIG_POWER_DOMAIN_CORE_CLUS1CORE3 PD_RCAR_CLUS1CORE3 + +enum rcar_powerdomain_child_index { + CONFIG_POWER_DOMAIN_CHILD_CLUSTER0, + CONFIG_POWER_DOMAIN_CHILD_CLUSTER1, + CONFIG_POWER_DOMAIN_CHILD_A3IR, + CONFIG_POWER_DOMAIN_CHILD_3DGE, + CONFIG_POWER_DOMAIN_CHILD_3DGD, + CONFIG_POWER_DOMAIN_CHILD_3DGC, + CONFIG_POWER_DOMAIN_CHILD_3DGB, + CONFIG_POWER_DOMAIN_CHILD_3DGA, + CONFIG_POWER_DOMAIN_CHILD_A2VC1, + CONFIG_POWER_DOMAIN_CHILD_A3VC, + CONFIG_POWER_DOMAIN_CHILD_CR7, + CONFIG_POWER_DOMAIN_CHILD_A3VP, + CONFIG_POWER_DOMAIN_CHILD_COUNT +}; + +#endif /* CONFIG_RCAR_POWER_DOMAIN_H */ -- GitLab From fcaa19ba7ab88104ce9daef3b4405529692b98f1 Mon Sep 17 00:00:00 2001 From: Nicolas Royer Date: Sun, 27 Sep 2020 17:46:43 +0200 Subject: [PATCH 13/19] rcar/module: add rcar reg_sensor module Signed-off-by: Tsutomu Muroya Signed-off-by: Chikara Asou Signed-off-by: Nicolas Royer --- .../include/mod_rcar_reg_sensor.h | 162 ++++++++++++ .../rcar_reg_sensor/include/mod_reg_sensor.h | 21 ++ .../rcar/module/rcar_reg_sensor/src/Makefile | 11 + .../rcar_reg_sensor/src/mod_rcar_reg_sensor.c | 233 ++++++++++++++++++ 4 files changed, 427 insertions(+) create mode 100644 product/rcar/module/rcar_reg_sensor/include/mod_rcar_reg_sensor.h create mode 100644 product/rcar/module/rcar_reg_sensor/include/mod_reg_sensor.h create mode 100644 product/rcar/module/rcar_reg_sensor/src/Makefile create mode 100644 product/rcar/module/rcar_reg_sensor/src/mod_rcar_reg_sensor.c diff --git a/product/rcar/module/rcar_reg_sensor/include/mod_rcar_reg_sensor.h b/product/rcar/module/rcar_reg_sensor/include/mod_rcar_reg_sensor.h new file mode 100644 index 000000000..40acb75cd --- /dev/null +++ b/product/rcar/module/rcar_reg_sensor/include/mod_rcar_reg_sensor.h @@ -0,0 +1,162 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_RCAR_REG_SENSOR_H +#define MOD_RCAR_REG_SENSOR_H + +#include +#include + +/*! + * \addtogroup GroupRCARModule RCAR Product Modules + * @{ + */ + +/*! + * \defgroup GroupRCARRegSensor Register Sensor Driver + * + * \brief Driver for simple, register-based sensors. + * @{ + */ + +/*! \brief Element configuration */ +struct mod_reg_sensor_dev_config { + /*! Address of the sensor register */ + uintptr_t reg; + + /*! Auxiliary sensor information */ + struct mod_sensor_info *info; +}; + +/*! + * @cond + */ + +/* Register base */ +#define GEN3_THERMAL_BASE (PERIPHERAL_BASE + 0x198000) +#define GEN3_THERMAL_OFFSET (0x8000) +/* Register offsets */ +#define REG_GEN3_IRQSTR (0x04) +#define REG_GEN3_IRQMSK (0x08) +#define REG_GEN3_IRQCTL (0x0C) +#define REG_GEN3_IRQEN (0x10) +#define REG_GEN3_IRQTEMP1 (0x14) +#define REG_GEN3_IRQTEMP2 (0x18) +#define REG_GEN3_IRQTEMP3 (0x1C) +#define REG_GEN3_CTSR (0x20) +#define REG_GEN3_THCTR (0x20) +#define REG_GEN3_TEMP (0x28) +#define REG_GEN3_THCODE1 (0x50) +#define REG_GEN3_THCODE2 (0x54) +#define REG_GEN3_THCODE3 (0x58) +#define REG_GEN3_PTAT1 (0x5C) +#define REG_GEN3_PTAT2 (0x60) +#define REG_GEN3_PTAT3 (0x64) +#define REG_GEN3_THSCP (0x68) +#define NEXT_REG_OFFSET (4) + +/* THCTR bits */ +#define THCTR_PONM BIT(6) +#define THCTR_THSST BIT(0) + +#define CTEMP_MASK (0xFFF) +#define MCELSIUS(temp) ((temp) * 1000) + +#define AVAILABLE_HARDWARE_PARAM ( \ + mmio_read_32(GEN3_THERMAL_BASE + REG_GEN3_THSCP) \ + == (0x03 << 14) ) + +/* Structure for thermal temperature calculation */ +struct equation_coefs { + int a1; + int b1; + int a2; + int b2; +}; + +struct rcar_gen3_thermal_tsc { + uintptr_t base; + struct thermal_zone_device *zone; + struct equation_coefs coef; + int low; + int high; + int tj_t; + int id; /* thermal channel id */ +}; + +/* + * Linear approximation for temperature + * + * [reg] = [temp] * a + b => [temp] = ([reg] - b) / a + * + * The constants a and b are calculated using two triplets of int values PTAT + * and THCODE. PTAT and THCODE can either be read from hardware or use hard + * coded values from driver. The formula to calculate a and b are taken from + * BSP and sparsely documented and understood. + * + * Examining the linear formula and the formula used to calculate constants a + * and b while knowing that the span for PTAT and THCODE values are between + * 0x000 and 0xfff the largest integer possible is 0xfff * 0xfff == 0xffe001. + * Integer also needs to be signed so that leaves 7 bits for binary + * fixed point scaling. + */ +#define DIV_ROUND_CLOSEST(x, divisor)( \ +{ \ + __typeof(x) __x = x; \ + __typeof(divisor) __d = divisor; \ + (((__typeof(x))-1) > 0 || \ + ((__typeof(divisor))-1) > 0 || (__x) > 0) ? \ + (((__x) + ((__d) / 2)) / (__d)) : \ + (((__x) - ((__d) / 2)) / (__d)); \ +} \ +) +#define FIXPT_SHIFT (7) +#define FIXPT_INT(_x) ((_x) * (1 << FIXPT_SHIFT)) +#define INT_FIXPT(_x) ((_x) >> FIXPT_SHIFT) +#define FIXPT_DIV(_a, _b) DIV_ROUND_CLOSEST(((_a) * (1 << FIXPT_SHIFT)), (_b)) +#define FIXPT_TO_MCELSIUS(_x) ((_x) * 1000 >> FIXPT_SHIFT) + +#define RCAR3_THERMAL_GRAN (500) /* mili Celsius */ + +#define TSC_MAX_NUM (3) +#define TSC_PARAM_NUM (3) +#define TEMP_UPPER_LIMIT (125) +#define TEMP_LOWER_LIMIT (-40) + +/* no idea where these constants come from */ +#define TJ_1 (116) +#define TJ_3 (-41) + +#define SENSOR_ADR_BASE (\ + SENSOR_SOC_TEMP1 &\ + SENSOR_SOC_TEMP2 &\ + SENSOR_SOC_TEMP3 ) +#define SENSOR_ADR_MASK (\ + (\ + SENSOR_SOC_TEMP1 |\ + SENSOR_SOC_TEMP2 |\ + SENSOR_SOC_TEMP3 )\ + - SENSOR_ADR_BASE) +#define IS_SENSOR_ADR(adr) ((adr & ~SENSOR_ADR_MASK) == SENSOR_ADR_BASE) +#define CV_ADR2INDEX(adr) (int)(((adr & SENSOR_ADR_MASK) >> 15) - 3) +#define ADR2INDEX(adr) (IS_SENSOR_ADR(adr)? CV_ADR2INDEX(adr):(-1)) + +void reg_sensor_resume(); + +/*! + * @endcond + */ + +/*! + * @} + */ + +/*! + * @} + */ + +#endif /* MOD_RCAR_REG_SENSOR_H */ diff --git a/product/rcar/module/rcar_reg_sensor/include/mod_reg_sensor.h b/product/rcar/module/rcar_reg_sensor/include/mod_reg_sensor.h new file mode 100644 index 000000000..00903817b --- /dev/null +++ b/product/rcar/module/rcar_reg_sensor/include/mod_reg_sensor.h @@ -0,0 +1,21 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_REG_SENSOR_H +#define MOD_REG_SENSOR_H + +#include +#include + + +#define FWK_MODULE_IDX_REG_SENSOR FWK_MODULE_IDX_RCAR_REG_SENSOR + +#include "mod_rcar_reg_sensor.h" + + + +#endif /* MOD_REG_SENSOR_H */ diff --git a/product/rcar/module/rcar_reg_sensor/src/Makefile b/product/rcar/module/rcar_reg_sensor/src/Makefile new file mode 100644 index 000000000..9a7056e22 --- /dev/null +++ b/product/rcar/module/rcar_reg_sensor/src/Makefile @@ -0,0 +1,11 @@ +# +# Renesas SCP/MCP Software +# Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +BS_LIB_NAME := rcar_reg_sensor +BS_LIB_SOURCES := mod_rcar_reg_sensor.c + +include $(BS_DIR)/lib.mk diff --git a/product/rcar/module/rcar_reg_sensor/src/mod_rcar_reg_sensor.c b/product/rcar/module/rcar_reg_sensor/src/mod_rcar_reg_sensor.c new file mode 100644 index 000000000..c103bb0ad --- /dev/null +++ b/product/rcar/module/rcar_reg_sensor/src/mod_rcar_reg_sensor.c @@ -0,0 +1,233 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* default values if FUSEs are missing */ +/* TODO: Read values from hardware on supported platforms */ +int ptat[3] = { 2631, 1509, 435 }; +int thcode[TSC_MAX_NUM][3] = { + { 3397, 2800, 2221 }, + { 3393, 2795, 2216 }, + { 3389, 2805, 2237 }, +}; +struct rcar_gen3_thermal_tsc *tscs[TSC_MAX_NUM]; + +static struct mod_reg_sensor_dev_config **config_table; +static const int rcar_gen3_ths_tj_1 = 126; + +static inline uint32_t rcar_gen3_thermal_read( + struct rcar_gen3_thermal_tsc *tsc, uint32_t reg) +{ + return mmio_read_32(tsc->base + reg); +} + +static inline void rcar_gen3_thermal_write(struct rcar_gen3_thermal_tsc *tsc, + uint32_t reg, uint32_t data) +{ + mmio_write_32(tsc->base + reg, data); +} + +static void rcar_gen3_thermal_calc_coefs(struct rcar_gen3_thermal_tsc *tsc, + int *ptat, const int *thcode, + int ths_tj_1) +{ + /* TODO: Find documentation and document constant calculation formula */ + + /* + * Division is not scaled in BSP and if scaled it might overflow + * the dividend (4095 * 4095 << 14 > INT_MAX) so keep it unscaled + */ + tsc->tj_t = (FIXPT_INT((ptat[1] - ptat[2]) * 157) + / (ptat[0] - ptat[2])) + FIXPT_INT(TJ_3); + + tsc->coef.a1 = FIXPT_DIV(FIXPT_INT(thcode[1] - thcode[2]), + tsc->tj_t - FIXPT_INT(TJ_3)); + tsc->coef.b1 = FIXPT_INT(thcode[2]) - tsc->coef.a1 * TJ_3; + + tsc->coef.a2 = FIXPT_DIV(FIXPT_INT(thcode[1] - thcode[0]), + tsc->tj_t - FIXPT_INT(ths_tj_1)); + tsc->coef.b2 = FIXPT_INT(thcode[0]) - tsc->coef.a2 * ths_tj_1; +} + +static int rcar_gen3_thermal_round(int temp) +{ + int result, round_offs; + + round_offs = temp >= 0 ? RCAR3_THERMAL_GRAN / 2 : + -RCAR3_THERMAL_GRAN / 2; + result = (temp + round_offs) / RCAR3_THERMAL_GRAN; + return result * RCAR3_THERMAL_GRAN; +} + +static int rcar_gen3_thermal_get_temp(void *devdata, int *temp) +{ + struct rcar_gen3_thermal_tsc *tsc = devdata; + int mcelsius, val; + int reg; + + /* Read register and convert to mili Celsius */ + reg = rcar_gen3_thermal_read(tsc, REG_GEN3_TEMP) & CTEMP_MASK; + + if (reg <= thcode[tsc->id][1]) + val = FIXPT_DIV(FIXPT_INT(reg) - tsc->coef.b1, + tsc->coef.a1); + else + val = FIXPT_DIV(FIXPT_INT(reg) - tsc->coef.b2, + tsc->coef.a2); + mcelsius = FIXPT_TO_MCELSIUS(val); + + /* Make sure we are inside specifications */ + if ((mcelsius < MCELSIUS(TEMP_LOWER_LIMIT)) + || (mcelsius > MCELSIUS(TEMP_UPPER_LIMIT))) + return -1/*-EIO*/; + + /* Round value to device granularity setting */ + *temp = rcar_gen3_thermal_round(mcelsius); + + return 0; +} + +/* + * Module API + */ +static int get_value(fwk_id_t id, uint64_t *value) +{ + int tmp; + int64_t *ivalue; + + if (value == NULL) { + assert(false); + return FWK_E_PARAM; + } + + if (rcar_gen3_thermal_get_temp(tscs[fwk_id_get_element_idx(id)], &tmp)) + return FWK_E_DATA; + + ivalue = (void *)value; + *ivalue = (int64_t)tmp; + + return FWK_SUCCESS; +} + +static int get_info(fwk_id_t id, struct mod_sensor_info *info) +{ + struct mod_reg_sensor_dev_config *config; + + config = config_table[fwk_id_get_element_idx(id)]; + fwk_assert(config != NULL); + + if (info == NULL) + return FWK_E_PARAM; + + *info = *(config->info); + + return FWK_SUCCESS; +} + +static const struct mod_sensor_driver_api reg_sensor_api = { + .get_value = get_value, + .get_info = get_info, +}; + +/* + * Framework handlers + */ +static int reg_sensor_start(fwk_id_t id) +{ + int eid, pid; + struct rcar_gen3_thermal_tsc *tsc; + struct mod_reg_sensor_dev_config *config; + + /* for Module */ + if (fwk_id_is_type(id, FWK_ID_TYPE_MODULE)) + return FWK_SUCCESS; + + /* for Elements */ + eid = fwk_id_get_element_idx(id); + tsc = fwk_mm_alloc(1, sizeof(*tsc)); + if (!tsc) + return FWK_E_NOMEM; + + config = config_table[eid]; + pid = ADR2INDEX(config->reg); + if (pid < 0) + return FWK_E_DATA; + tsc->base = config->reg; + rcar_gen3_thermal_calc_coefs(tsc, ptat, thcode[pid], + rcar_gen3_ths_tj_1); + tsc->id = pid; + tscs[eid] = tsc; + rcar_gen3_thermal_write(tsc, REG_GEN3_THCTR, THCTR_THSST); + + return FWK_SUCCESS; +} + +static int reg_sensor_init(fwk_id_t module_id, + unsigned int element_count, + const void *unused) +{ + config_table = fwk_mm_alloc(element_count, sizeof(*config_table)); + if (config_table == NULL) + return FWK_E_NOMEM; + + return FWK_SUCCESS; +} + +static int reg_sensor_element_init(fwk_id_t element_id, + unsigned int sub_element_count, + const void *data) +{ + struct mod_reg_sensor_dev_config *config = + (struct mod_reg_sensor_dev_config *)data; + + if (config->reg == 0) + return FWK_E_DATA; + + config_table[fwk_id_get_element_idx(element_id)] = config; + + return FWK_SUCCESS; +} + +void reg_sensor_resume() +{ + int i; + + for (i = 0; i < TSC_MAX_NUM; i++) { + if (tscs[i]) { + rcar_gen3_thermal_write(tscs[i], REG_GEN3_THCTR, THCTR_THSST); + } + } +} + +static int reg_sensor_process_bind_request(fwk_id_t source_id, + fwk_id_t target_id, + fwk_id_t api_type, + const void **api) +{ + *api = ®_sensor_api; + return FWK_SUCCESS; +} + +const struct fwk_module module_rcar_reg_sensor = { + .name = "Rcar Thermal Sensor", + .api_count = 1, + .type = FWK_MODULE_TYPE_DRIVER, + .init = reg_sensor_init, + .start = reg_sensor_start, + .element_init = reg_sensor_element_init, + .process_bind_request = reg_sensor_process_bind_request, +}; -- GitLab From 200fbdab331d747e7f8e38ed03cf55b7b9f073d9 Mon Sep 17 00:00:00 2001 From: Nicolas Royer Date: Sun, 27 Sep 2020 17:47:17 +0200 Subject: [PATCH 14/19] rcar/module: add rcar sd_clock module and config data Signed-off-by: Tsutomu Muroya Signed-off-by: Nicolas Royer --- .../rcar_sd_clock/include/mod_rcar_sd_clock.h | 185 +++++ .../rcar/module/rcar_sd_clock/src/Makefile | 11 + .../rcar_sd_clock/src/mod_rcar_sd_clock.c | 409 +++++++++++ product/rcar/scp_ramfw/config_rcar_sd_clock.c | 634 ++++++++++++++++++ 4 files changed, 1239 insertions(+) create mode 100644 product/rcar/module/rcar_sd_clock/include/mod_rcar_sd_clock.h create mode 100644 product/rcar/module/rcar_sd_clock/src/Makefile create mode 100755 product/rcar/module/rcar_sd_clock/src/mod_rcar_sd_clock.c create mode 100644 product/rcar/scp_ramfw/config_rcar_sd_clock.c diff --git a/product/rcar/module/rcar_sd_clock/include/mod_rcar_sd_clock.h b/product/rcar/module/rcar_sd_clock/include/mod_rcar_sd_clock.h new file mode 100644 index 000000000..a9175d7cc --- /dev/null +++ b/product/rcar/module/rcar_sd_clock/include/mod_rcar_sd_clock.h @@ -0,0 +1,185 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_RCAR_SD_CLOCK_H +#define MOD_RCAR_SD_CLOCK_H + +#include +#include +#include +#include +#include +#include + +/*! + * \addtogroup GroupRCARModule RCAR Product Modules + * @{ + */ + +/*! + * \defgroup GroupRCARSdClock SD clock + * @{ + */ + +/*! + * \brief Rate lookup entry. + */ +struct mod_rcar_sd_clock_rate { + /*! Divider used to obtain the rate value. */ + uint32_t divider_num; + /*! Value to be set for the divider. */ + uint32_t divider; + /*! Divider maskbit value. */ + uint32_t divider_mask; +}; + +/*! + * \brief Subsystem clock device configuration. + */ +struct mod_rcar_sd_clock_dev_config { + /*! The type of the clock device. */ + enum mod_rcar_clock_type type; + + /*! Pointer to the clock's control register. */ + volatile uint32_t const control_reg; + + /*! enable / disable bit position. */ + volatile bool stop_clk; + + /*! enable / disable bit position. */ + volatile uint32_t const stop_clk_bit; + + /*! Parent clock id. */ + uint32_t parent; + + /*! Required initialization Clock divider. */ + bool need_hardware_init; + + /*! Fixed Clock divider. */ + uint32_t div; + + /*! Fixed Clock divider. */ + uint32_t multi; + + /*! The type of rates the clock provides (discrete or continuous) */ + enum mod_clock_rate_type rate_type; + + /*! Pointer to the clock's rate lookup table. */ + const struct mod_rcar_sd_clock_rate *rate_table; + + /*! The number of rates in the rate lookup table. */ + uint32_t rate_count; +}; + +/*! + * @cond + */ + +/* Device context */ +struct rcar_sd_clock_dev_ctx { + bool initialized; + uint64_t current_rate; + uint64_t *rate_table; + enum mod_clock_state current_state; + const struct mod_rcar_sd_clock_dev_config *config; +}; + +/* Module context */ +struct rcar_sd_clock_ctx { + struct rcar_sd_clock_dev_ctx *dev_ctx_table; + unsigned int dev_count; + uint32_t parent_clk[CLOCK_PARENT_IDX_COUNT]; +}; + +struct rcar_gen3_cpg_pll_config { + char extal_div; + char pll1_mult; + char pll1_div; + char pll3_mult; + char pll3_div; + char osc_prediv; +}; + +/* control register */ +#define CPG_SD0CKCR (CPG_BASE + 0x0074) +#define CPG_SD1CKCR (CPG_BASE + 0x0078) +#define CPG_SD2CKCR (CPG_BASE + 0x0268) +#define CPG_SD3CKCR (CPG_BASE + 0x026C) +#define CPG_SDNCKCR_MASK 0x1F +#define CPG_SDNCKCR_SD_64 0x11 +#define CPG_SDNCKCR_SD_32 0xD +#define CPG_SDNCKCR_SD_16 0x9 +#define CPG_SDNCKCR_SD_8 0x5 +#define CPG_SDNCKCR_SD_4 0x1 +#define CPG_SDNCKCR_SD_2 0x0 + +#define CPG_CANFDCKCR (CPG_BASE + 0x0244) +#define CPG_CSI0CKCR (CPG_BASE + 0x000C) +#define CPG_MSOCKCR (CPG_BASE + 0x0014) +#define CPG_HDMICKCR (CPG_BASE + 0x0250) +#define CPG_CON_MASK 0x3F +#define CPG_CON_MAX 64 + +#define CPG_FRQCRB_ZTRFC_MASK 0xF00000 +#define CPG_FRQCRB_ZTRFC_24 0x800000 +#define CPG_FRQCRB_ZTRFC_18 0x700000 +#define CPG_FRQCRB_ZTRFC_16 0x600000 +#define CPG_FRQCRB_ZTRFC_12 0x500000 +#define CPG_FRQCRB_ZTRFC_8 0x400000 +#define CPG_FRQCRB_ZTRFC_6 0x300000 +#define CPG_FRQCRB_ZTFC_MASK 0xF0000 +#define CPG_FRQCRB_ZTFC_24 0x80000 +#define CPG_FRQCRB_ZTFC_18 0x70000 +#define CPG_FRQCRB_ZTFC_16 0x60000 +#define CPG_FRQCRB_ZTFC_12 0x50000 +#define CPG_FRQCRB_ZTFC_8 0x40000 +#define CPG_FRQCRB_ZTFC_6 0x30000 +#define CPG_FRQCRB_ZTFC_4 0x20000 +#define CPG_FRQCRB_ZTRD2FC_MASK 0xF +#define CPG_FRQCRB_ZTRD2FC_24 0x8 +#define CPG_FRQCRB_ZTRD2FC_18 0x7 +#define CPG_FRQCRB_ZTRD2FC_16 0x6 +#define CPG_FRQCRB_ZTRD2FC_12 0x5 + +#define CPG_PLL_CONFIG_INDEX(md) ((((md) & BIT(14)) >> 11) | \ + (((md) & BIT(13)) >> 11) | \ + (((md) & BIT(19)) >> 18) | \ + (((md) & BIT(17)) >> 17)) + +static const struct rcar_gen3_cpg_pll_config cpg_pll_configs[16] = { + /* EXTAL div PLL1 mult/div PLL3 mult/div OSC prediv */ + { 1, 192, 1, 192, 1, 16,}, + { 1, 192, 1, 128, 1, 16,}, + { 0, /* Prohibited setting */ }, + { 1, 192, 1, 192, 1, 16,}, + { 1, 160, 1, 160, 1, 19,}, + { 1, 160, 1, 106, 1, 19,}, + { 0, /* Prohibited setting */ }, + { 1, 160, 1, 160, 1, 19,}, + { 1, 128, 1, 128, 1, 24,}, + { 1, 128, 1, 84, 1, 24,}, + { 0, /* Prohibited setting */ }, + { 1, 128, 1, 128, 1, 24,}, + { 2, 192, 1, 192, 1, 32,}, + { 2, 192, 1, 128, 1, 32,}, + { 0, /* Prohibited setting */ }, + { 2, 192, 1, 192, 1, 32,}, +}; + +/*! + * @endcond + */ + +/*! + * @} + */ + +/*! + * @} + */ + +#endif /* MOD_RCAR_SD_CLOCK_H */ diff --git a/product/rcar/module/rcar_sd_clock/src/Makefile b/product/rcar/module/rcar_sd_clock/src/Makefile new file mode 100644 index 000000000..998cc5902 --- /dev/null +++ b/product/rcar/module/rcar_sd_clock/src/Makefile @@ -0,0 +1,11 @@ +# +# Renesas SCP/MCP Software +# Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +BS_LIB_NAME := RCAR SD Clock Driver +BS_LIB_SOURCES := mod_rcar_sd_clock.c + +include $(BS_DIR)/lib.mk diff --git a/product/rcar/module/rcar_sd_clock/src/mod_rcar_sd_clock.c b/product/rcar/module/rcar_sd_clock/src/mod_rcar_sd_clock.c new file mode 100755 index 000000000..7c76a89d7 --- /dev/null +++ b/product/rcar/module/rcar_sd_clock/src/mod_rcar_sd_clock.c @@ -0,0 +1,409 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct rcar_sd_clock_ctx module_ctx; + +/* + * Static helper functions + */ +static int get_rate_entry(struct rcar_sd_clock_dev_ctx *ctx, + uint64_t target_rate, + struct mod_rcar_sd_clock_rate **entry) +{ + struct mod_rcar_sd_clock_rate *rate_entry; + uint32_t cnt; + if (ctx == NULL) + return FWK_E_PARAM; + if (entry == NULL) + return FWK_E_PARAM; + + /* Perform a binary search to find the entry matching the requested rate */ + if (ctx->config->rate_type == MOD_CLOCK_RATE_TYPE_CONTINUOUS) { + if ((ctx->rate_table[0] > target_rate) && + (ctx->rate_table[1] < target_rate)) + return FWK_E_PARAM; + rate_entry = (struct mod_rcar_sd_clock_rate *)ctx->config->rate_table; + *entry = rate_entry; + } else { + for (cnt = 0; cnt < ctx->config->rate_count; cnt++) { + if (ctx->rate_table[cnt] == target_rate) + break; + } + + if (cnt >= ctx->config->rate_count) + return FWK_E_PARAM; + *entry = (struct mod_rcar_sd_clock_rate *)&ctx->config->rate_table[cnt]; + } + return FWK_SUCCESS; +} + +static int do_sd_clock_set_rate(fwk_id_t dev_id, uint64_t rate) +{ + int status; + struct rcar_sd_clock_dev_ctx *ctx; + struct mod_rcar_sd_clock_rate *rate_entry; + uint32_t value; + + ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id); + + status = get_rate_entry(ctx, rate, &rate_entry); + if (status != FWK_SUCCESS) + return status; + + switch (ctx->config->type) { + case MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE: + return FWK_E_SUPPORT; + case MOD_RCAR_CLOCK_TYPE_MULTI_SOURCE: + if (ctx->config->rate_type == MOD_CLOCK_RATE_TYPE_DISCRETE) { + value = mmio_read_32(ctx->config->control_reg); + value &= (~rate_entry->divider_mask); + value |= rate_entry->divider; + mmio_write_32(ctx->config->control_reg, value); + } else { + value = mmio_read_32(ctx->config->control_reg); + value &= (~CPG_CON_MASK); + value |= (CPG_CON_MAX - (rate / ctx->rate_table[2])) & CPG_CON_MASK; + mmio_write_32(ctx->config->control_reg, value); + } + break; + default: + return FWK_E_SUPPORT; + } + + ctx->current_rate = rate; + + return FWK_SUCCESS; +} + +/* + * Clock driver API functions + */ +static int sd_clock_set_rate(fwk_id_t dev_id, uint64_t rate, + enum mod_clock_round_mode round_mode) +{ + struct rcar_sd_clock_dev_ctx *ctx; + + ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id); + + if (!ctx->initialized) + return FWK_E_INIT; + + if (ctx->current_state == MOD_CLOCK_STATE_STOPPED) + return FWK_E_PWRSTATE; + + return do_sd_clock_set_rate(dev_id, rate); +} + +static int sd_clock_get_rate(fwk_id_t dev_id, uint64_t *rate) +{ + struct rcar_sd_clock_dev_ctx *ctx; + + if (rate == NULL) + return FWK_E_PARAM; + + ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id); + + *rate = ctx->current_rate; + + return FWK_SUCCESS; +} + +static int sd_clock_get_rate_from_index(fwk_id_t dev_id, + unsigned int rate_index, + uint64_t *rate) +{ + struct rcar_sd_clock_dev_ctx *ctx; + + if (rate == NULL) + return FWK_E_PARAM; + + ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id); + + if (rate_index >= ctx->config->rate_count) + return FWK_E_PARAM; + + *rate = ctx->rate_table[rate_index]; + return FWK_SUCCESS; +} + +static int sd_clock_set_state( + fwk_id_t dev_id, + enum mod_clock_state target_state) +{ + struct rcar_sd_clock_dev_ctx *ctx; + uint32_t value; + + ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id); + value = mmio_read_32(ctx->config->control_reg); + if (MOD_CLOCK_STATE_RUNNING == target_state) + value &= ~(BIT(ctx->config->stop_clk_bit)); + else + value |= BIT(ctx->config->stop_clk_bit); + + mmio_write_32(ctx->config->control_reg, value); + + ctx->current_state = target_state; + + return FWK_SUCCESS; +} + +static int sd_clock_get_state(fwk_id_t dev_id, enum mod_clock_state *state) +{ + struct rcar_sd_clock_dev_ctx *ctx; + + ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id); + *state = ctx->current_state; + return FWK_SUCCESS; +} +static int sd_clock_get_range(fwk_id_t dev_id, struct mod_clock_range *range) +{ + struct rcar_sd_clock_dev_ctx *ctx; + + if (range == NULL) + return FWK_E_PARAM; + + ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(dev_id); + + range->rate_type = ctx->config->rate_type; + if (ctx->config->rate_count > 0) { + if (MOD_CLOCK_RATE_TYPE_CONTINUOUS == range->rate_type) { + /* SCMI_CLOCK_RATE_FORMAT_RANGE */ + range->min = ctx->rate_table[0]; + range->max = ctx->rate_table[1]; + range->step = ctx->rate_table[2]; + } else { + /* SCMI_CLOCK_RATE_FORMAT_LIST */ + range->min = ctx->rate_table[0]; + range->max = ctx->rate_table[ctx->config->rate_count - 1]; + } + } + range->rate_count = ctx->config->rate_count; + + return FWK_SUCCESS; +} + +static int sd_clock_hw_initial_set_state( + fwk_id_t element_id, + struct rcar_sd_clock_dev_ctx *ctx) +{ + uint32_t i = 0; + uint32_t value = 0; + uint32_t div_value = 0; + uint64_t rate = 0; + + switch (ctx->config->type) { + case MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE: + ctx->current_state = MOD_CLOCK_STATE_RUNNING; + div_value = ctx->config->div; + break; + case MOD_RCAR_CLOCK_TYPE_MULTI_SOURCE: + /* Maintain clock supply at startup. */ + ctx->current_state = MOD_CLOCK_STATE_RUNNING; + if (ctx->config->stop_clk) { + if (mmio_read_32(ctx->config->control_reg) + & BIT(ctx->config->stop_clk_bit)) + ctx->current_state = MOD_CLOCK_STATE_STOPPED; + } + /* Holds clock frequency at startup. */ + if (ctx->config->rate_type == MOD_CLOCK_RATE_TYPE_DISCRETE) { + value = mmio_read_32(ctx->config->control_reg); + value &= ctx->config->rate_table[0].divider_mask; + for (i = 0; i < ctx->config->rate_count; i++) { + if (value == ctx->config->rate_table[i].divider) { + div_value = ctx->config->rate_table[i].divider_num; + break; + } + } + } else { + value = mmio_read_32(ctx->config->control_reg); + value &= (CPG_CON_MASK); + div_value = (value + 1); + } + break; + default: + return FWK_E_SUPPORT; + } + ctx->current_rate = module_ctx.parent_clk[ctx->config->parent] / div_value; + + if (ctx->config->need_hardware_init) { + rate = module_ctx.parent_clk[ctx->config->parent] / ctx->config->div; + do_sd_clock_set_rate(element_id, rate); + } + + return FWK_SUCCESS; +} + +static int sd_clock_resume(void) +{ + fwk_id_t element_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, CLK_ID_SD_START); + struct rcar_sd_clock_dev_ctx *ctx; + uint32_t sd_id = 0; + int ret = FWK_SUCCESS; + + for (sd_id = CLK_ID_SD_START; sd_id < CLK_ID_SD_END; sd_id++) { + element_id.element.element_idx = sd_id; + ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(element_id); + + ret = sd_clock_hw_initial_set_state(element_id, ctx); + if (ret != FWK_SUCCESS) + break; + } + return ret; +} + +static const struct mod_rcar_clock_drv_api api_clock = { + .set_rate = sd_clock_set_rate, + .get_rate = sd_clock_get_rate, + .get_rate_from_index = sd_clock_get_rate_from_index, + .set_state = sd_clock_set_state, + .get_state = sd_clock_get_state, + .get_range = sd_clock_get_range, + .resume = sd_clock_resume, +}; + +/* + * Framework handler functions + */ + +static int r8a7795_cpg_pll1_init(uint32_t ext_rate) +{ + const struct rcar_gen3_cpg_pll_config *cpg_pll_config; + uint32_t saved_mode; + saved_mode = mmio_read_32(RCAR_MODEMR); + cpg_pll_config = &cpg_pll_configs[CPG_PLL_CONFIG_INDEX(saved_mode)]; + return (ext_rate * cpg_pll_config->pll1_mult / cpg_pll_config->pll1_div); +} + +static int r8a7795_cpg_init(uint32_t ext_rate) +{ + const struct rcar_gen3_cpg_pll_config *cpg_pll_config; + uint32_t saved_mode; + saved_mode = mmio_read_32(RCAR_MODEMR); + cpg_pll_config = &cpg_pll_configs[CPG_PLL_CONFIG_INDEX(saved_mode)]; + return (ext_rate / cpg_pll_config->osc_prediv); +} + +static int sd_clock_init(fwk_id_t module_id, unsigned int element_count, + const void *data) +{ + struct mod_ext_clock_rate *ext; + module_ctx.dev_count = element_count; + + if (element_count == 0) + return FWK_SUCCESS; + + module_ctx.dev_ctx_table = fwk_mm_calloc(element_count, + sizeof(struct rcar_sd_clock_dev_ctx)); + if (module_ctx.dev_ctx_table == NULL) + return FWK_E_NOMEM; + + /* Parent clock calculation. */ + ext = (struct mod_ext_clock_rate *)data; + module_ctx.parent_clk[CLK_EXTAL] = ext->ext_clk_rate; + module_ctx.parent_clk[CLK_OSC_EXTAL] = r8a7795_cpg_init(ext->ext_clk_rate); + module_ctx.parent_clk[CLK_PLL1] = r8a7795_cpg_pll1_init(ext->ext_clk_rate); + module_ctx.parent_clk[CLK_PLL1_DIV2] = module_ctx.parent_clk[CLK_PLL1] / 2; + module_ctx.parent_clk[CLK_PLL1_DIV4] = module_ctx.parent_clk[CLK_PLL1] / 4;; + module_ctx.parent_clk[CLK_S0] = module_ctx.parent_clk[CLK_PLL1_DIV2] / 2; + module_ctx.parent_clk[CLK_S1] = module_ctx.parent_clk[CLK_PLL1_DIV2] / 3; + module_ctx.parent_clk[CLK_S2] = module_ctx.parent_clk[CLK_PLL1_DIV2] / 4; + module_ctx.parent_clk[CLK_S3] = module_ctx.parent_clk[CLK_PLL1_DIV2] / 6; + module_ctx.parent_clk[CLK_SDSRC] = module_ctx.parent_clk[CLK_PLL1_DIV2] / 2; + + return FWK_SUCCESS; +} + +static int sd_clock_element_init(fwk_id_t element_id, + unsigned int sub_element_count, + const void *data) +{ + struct rcar_sd_clock_dev_ctx *ctx; + const struct mod_rcar_sd_clock_dev_config *dev_config = data; + unsigned int i = 0; + uint64_t current_rate; + uint64_t last_rate = 0; + + if (!fwk_module_is_valid_element_id(element_id)) + return FWK_E_PARAM; + + ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(element_id); + ctx->config = dev_config; + + if (ctx->config->rate_count > 0) { + ctx->rate_table = fwk_mm_calloc(ctx->config->rate_count, + sizeof(uint64_t)); + if (ctx->rate_table == NULL) + return FWK_E_NOMEM; + + for (i = 0; i < ctx->config->rate_count; i++) + ctx->rate_table[i] = module_ctx.parent_clk[ctx->config->parent] + / ctx->config->rate_table[i].divider_num; + } + + /* Verify that the rate entries in the device's lookup table are ordered */ + if (dev_config->rate_type == MOD_CLOCK_RATE_TYPE_CONTINUOUS) { + if (ctx->rate_table[0] > ctx->rate_table[1]) + return FWK_E_DATA; + } else { + i = 0; + while (i < dev_config->rate_count) { + current_rate = ctx->rate_table[i]; + + /* The rate entries must be in ascending order */ + if (current_rate < last_rate) + return FWK_E_DATA; + + last_rate = current_rate; + i++; + } + } + + + ctx->initialized = true; + + return FWK_SUCCESS; +} + +static int sd_clock_process_bind_request(fwk_id_t source_id, + fwk_id_t target_id, fwk_id_t api_id, + const void **api) +{ + *api = &api_clock; + return FWK_SUCCESS; +} + +static int sd_clock_start(fwk_id_t id) +{ + int ret = FWK_SUCCESS; + ret = sd_clock_resume(); + return ret; +} + +const struct fwk_module module_rcar_sd_clock = { + .name = "SD Clock Driver", + .type = FWK_MODULE_TYPE_DRIVER, + .api_count = MOD_RCAR_CLOCK_API_COUNT, + .event_count = 0, + .init = sd_clock_init, + .element_init = sd_clock_element_init, + .process_bind_request = sd_clock_process_bind_request, + .start = sd_clock_start, +}; diff --git a/product/rcar/scp_ramfw/config_rcar_sd_clock.c b/product/rcar/scp_ramfw/config_rcar_sd_clock.c new file mode 100644 index 000000000..4e9f412e1 --- /dev/null +++ b/product/rcar/scp_ramfw/config_rcar_sd_clock.c @@ -0,0 +1,634 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Rate lookup tables + */ +static const struct mod_rcar_sd_clock_rate rate_table_sys_ztr[] = { + {/* 1/24 */ + .divider_num = 24, + .divider = CPG_FRQCRB_ZTRFC_24, + .divider_mask = CPG_FRQCRB_ZTRFC_MASK, + }, + {/* 1/18 */ + .divider_num = 18, + .divider = CPG_FRQCRB_ZTRFC_18, + .divider_mask = CPG_FRQCRB_ZTRFC_MASK, + }, + {/* 1/16 */ + .divider_num = 16, + .divider = CPG_FRQCRB_ZTRFC_16, + .divider_mask = CPG_FRQCRB_ZTRFC_MASK, + }, + {/* 1/12 */ + .divider_num = 12, + .divider = CPG_FRQCRB_ZTRFC_12, + .divider_mask = CPG_FRQCRB_ZTRFC_MASK, + }, + {/* 1/8 */ + .divider_num = 8, + .divider = CPG_FRQCRB_ZTRFC_8, + .divider_mask = CPG_FRQCRB_ZTRFC_MASK, + }, + {/* 1/6 */ + .divider_num = 6, + .divider = CPG_FRQCRB_ZTRFC_6, + .divider_mask = CPG_FRQCRB_ZTRFC_MASK, + }, +}; + +static const struct mod_rcar_sd_clock_rate rate_table_sys_ztrd2[] = { + {/* 1/24 */ + .divider_num = 24, + .divider = CPG_FRQCRB_ZTRD2FC_24, + .divider_mask = CPG_FRQCRB_ZTRD2FC_MASK, + }, + {/* 1/18 */ + .divider_num = 18, + .divider = CPG_FRQCRB_ZTRD2FC_18, + .divider_mask = CPG_FRQCRB_ZTRD2FC_MASK, + }, + {/* 1/16 */ + .divider_num = 16, + .divider = CPG_FRQCRB_ZTRD2FC_16, + .divider_mask = CPG_FRQCRB_ZTRD2FC_MASK, + }, + {/* 1/12 */ + .divider_num = 12, + .divider = CPG_FRQCRB_ZTRD2FC_12, + .divider_mask = CPG_FRQCRB_ZTRD2FC_MASK, + }, +}; + +static const struct mod_rcar_sd_clock_rate rate_table_sys_zt[] = { + {/* 1/24 */ + .divider_num = 24, + .divider = CPG_FRQCRB_ZTFC_24, + .divider_mask = CPG_FRQCRB_ZTFC_MASK, + }, + {/* 1/18 */ + .divider_num = 18, + .divider = CPG_FRQCRB_ZTFC_18, + .divider_mask = CPG_FRQCRB_ZTFC_MASK, + }, + {/* 1/16 */ + .divider_num = 16, + .divider = CPG_FRQCRB_ZTFC_16, + .divider_mask = CPG_FRQCRB_ZTFC_MASK, + }, + {/* 1/12 */ + .divider_num = 12, + .divider = CPG_FRQCRB_ZTFC_12, + .divider_mask = CPG_FRQCRB_ZTFC_MASK, + }, + {/* 1/8 */ + .divider_num = 8, + .divider = CPG_FRQCRB_ZTFC_8, + .divider_mask = CPG_FRQCRB_ZTFC_MASK, + }, + {/* 1/6 */ + .divider_num = 6, + .divider = CPG_FRQCRB_ZTFC_6, + .divider_mask = CPG_FRQCRB_ZTFC_MASK, + }, + {/* 1/4 */ + .divider_num = 4, + .divider = CPG_FRQCRB_ZTFC_4, + .divider_mask = CPG_FRQCRB_ZTFC_MASK, + }, +}; + +static const struct mod_rcar_sd_clock_rate rate_table_sys_sd[] = { + {/* 1/64 */ + .divider_num = 64, + .divider = CPG_SDNCKCR_SD_64, + .divider_mask = CPG_SDNCKCR_MASK, + }, + {/* 1/32 */ + .divider_num = 32, + .divider = CPG_SDNCKCR_SD_32, + .divider_mask = CPG_SDNCKCR_MASK, + }, + {/* 1/16 */ + .divider_num = 16, + .divider = CPG_SDNCKCR_SD_16, + .divider_mask = CPG_SDNCKCR_MASK, + }, + {/* 1/8 */ + .divider_num = 8, + .divider = CPG_SDNCKCR_SD_8, + .divider_mask = CPG_SDNCKCR_MASK, + }, + {/* 1/4 */ + .divider_num = 4, + .divider = CPG_SDNCKCR_SD_4, + .divider_mask = CPG_SDNCKCR_MASK, + }, + {/* 1/2 */ + .divider_num = 2, + .divider = CPG_SDNCKCR_SD_2, + .divider_mask = CPG_SDNCKCR_MASK, + }, +}; + +static const struct mod_rcar_sd_clock_rate rate_table_sys_ckcr[] = { + /* min rate*/ + { + .divider_num = 64, + }, + /* max rate*/ + { + .divider_num = 1, + }, + /* step rate*/ + { + .divider_num = 64, + }, +}; + +static const struct fwk_element pik_clock_element_table[] = { + { + .name = "ztr", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_MULTI_SOURCE, + .control_reg = CPG_FRQCRB, + .stop_clk = false, + .parent = CLK_PLL1_DIV2, + .div = 6, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + .rate_table = rate_table_sys_ztr, + .rate_count = FWK_ARRAY_SIZE(rate_table_sys_ztr), + }), + }, + { + .name = "ztrd2", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_MULTI_SOURCE, + .control_reg = CPG_FRQCRB, + .stop_clk = false, + .parent = CLK_PLL1_DIV2, + .div = 12, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + .rate_table = rate_table_sys_ztrd2, + .rate_count = FWK_ARRAY_SIZE(rate_table_sys_ztrd2), + }), + }, + { + .name = "zt", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_MULTI_SOURCE, + .control_reg = CPG_FRQCRB, + .stop_clk = false, + .parent = CLK_PLL1_DIV2, + .div = 4, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + .rate_table = rate_table_sys_zt, + .rate_count = FWK_ARRAY_SIZE(rate_table_sys_zt), + }), + }, + { + .name = "zx", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .control_reg = CPG_FRQCRB, + .parent = CLK_PLL1_DIV2, + .div = 2, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "s0d1", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_S0, + .div = 1, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "s0d2", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_S0, + .div = 2, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "s0d3", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_S0, + .div = 3, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "s0d4", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_S0, + .div = 4, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "s0d6", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_S0, + .div = 6, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "s0d8", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_S0, + .div = 8, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "s0d12", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_S0, + .div = 12, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "s1d1", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_S1, + .div = 1, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "s1d2", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_S1, + .div = 2, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "s1d4", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_S1, + .div = 4, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "s2d1", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_S2, + .div = 1, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "s2d2", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_S2, + .div = 2, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "s2d4", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_S2, + .div = 4, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "s3d1", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_S3, + .div = 1, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "s3d2", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_S3, + .div = 2, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "s3d4", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_S3, + .div = 4, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "sd0", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_MULTI_SOURCE, + .control_reg = CPG_SD0CKCR, + .stop_clk = true, + .stop_clk_bit = 8, + .parent = CLK_SDSRC, + .need_hardware_init = true, + .div = 4, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + .rate_table = rate_table_sys_sd, + .rate_count = FWK_ARRAY_SIZE(rate_table_sys_sd), + }), + }, + { + .name = "sd1", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_MULTI_SOURCE, + .control_reg = CPG_SD1CKCR, + .stop_clk = true, + .stop_clk_bit = 8, + .parent = CLK_SDSRC, + .need_hardware_init = true, + .div = 4, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + .rate_table = rate_table_sys_sd, + .rate_count = FWK_ARRAY_SIZE(rate_table_sys_sd), + }), + }, + { + .name = "sd2", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_MULTI_SOURCE, + .control_reg = CPG_SD2CKCR, + .stop_clk = true, + .stop_clk_bit = 8, + .parent = CLK_SDSRC, + .need_hardware_init = true, + .div = 4, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + .rate_table = rate_table_sys_sd, + .rate_count = FWK_ARRAY_SIZE(rate_table_sys_sd), + }), + }, + { + .name = "sd3", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_MULTI_SOURCE, + .control_reg = CPG_SD3CKCR, + .stop_clk = true, + .stop_clk_bit = 8, + .parent = CLK_SDSRC, + .need_hardware_init = true, + .div = 4, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + .rate_table = rate_table_sys_sd, + .rate_count = FWK_ARRAY_SIZE(rate_table_sys_sd), + }), + }, + { + .name = "cl", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_PLL1_DIV2, + .div = 48, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "cr", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_PLL1_DIV4, + .div = 2, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "cp", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_EXTAL, + .div = 2, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "cpex", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_EXTAL, + .div = 2, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "canfd", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_MULTI_SOURCE, + .control_reg = CPG_CANFDCKCR, + .stop_clk = true, + .stop_clk_bit = 8, + .parent = CLK_PLL1_DIV4, + .div = 64, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_CONTINUOUS, + .rate_table = rate_table_sys_ckcr, + .rate_count = FWK_ARRAY_SIZE(rate_table_sys_ckcr), + }), + }, + { + .name = "csi0", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_MULTI_SOURCE, + .control_reg = CPG_CSI0CKCR, + .stop_clk = true, + .stop_clk_bit = 8, + .parent = CLK_PLL1_DIV4, + .div = 64, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_CONTINUOUS, + .rate_table = rate_table_sys_ckcr, + .rate_count = FWK_ARRAY_SIZE(rate_table_sys_ckcr), + }), + }, + { + .name = "mso", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_MULTI_SOURCE, + .control_reg = CPG_MSOCKCR, + .stop_clk = true, + .stop_clk_bit = 8, + .parent = CLK_PLL1_DIV4, + .div = 63, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_CONTINUOUS, + .rate_table = rate_table_sys_ckcr, + .rate_count = FWK_ARRAY_SIZE(rate_table_sys_ckcr), + }), + }, + { + .name = "hdmi", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_MULTI_SOURCE, + .control_reg = CPG_HDMICKCR, + .stop_clk = true, + .stop_clk_bit = 8, + .parent = CLK_PLL1_DIV4, + .div = 63, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_CONTINUOUS, + .rate_table = rate_table_sys_ckcr, + .rate_count = FWK_ARRAY_SIZE(rate_table_sys_ckcr), + }), + }, + { + .name = "osc", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_OSC_EXTAL, + .div = 8, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "r", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_OSC_EXTAL, + .div = 32, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "s0", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_PLL1_DIV2, + .div = 2, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "s1", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_PLL1_DIV2, + .div = 3, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "s2", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_PLL1_DIV2, + .div = 4, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "s3", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_PLL1_DIV2, + .div = 6, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "sdsrc", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_PLL1_DIV2, + .div = 2, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { + .name = "rint", + .data = &((struct mod_rcar_sd_clock_dev_config) { + .type = MOD_RCAR_CLOCK_TYPE_SINGLE_SOURCE, + .parent = CLK_OSC_EXTAL, + .div = 32, + .multi = 1, + .rate_type = MOD_CLOCK_RATE_TYPE_DISCRETE, + }), + }, + { 0 }, /* Termination description. */ +}; + +static const struct fwk_element *pik_clock_get_element_table + (fwk_id_t module_id) +{ + return pik_clock_element_table; +} + +struct fwk_module_config config_rcar_sd_clock = { + .elements = FWK_MODULE_DYNAMIC_ELEMENTS(pik_clock_get_element_table), + .data = &((struct mod_ext_clock_rate) { + .ext_clk_rate = PLL_BASE_CLOCK, + }), +}; -- GitLab From c5a5686d61e4fb0d4bfea67f8fac9b07f9f5040b Mon Sep 17 00:00:00 2001 From: Nicolas Royer Date: Sun, 27 Sep 2020 17:47:45 +0200 Subject: [PATCH 15/19] rcar/module: add rcar system module and config data Signed-off-by: Tsutomu Muroya Signed-off-by: Nicolas Royer --- .../rcar_system/include/mod_rcar_system.h | 67 +++ .../rcar_system/src/FreeRTOS_tick_config.c | 160 +++++ product/rcar/module/rcar_system/src/Makefile | 16 + .../module/rcar_system/src/mod_rcar_system.c | 281 +++++++++ .../module/rcar_system/src/rcar_call_sram.S | 48 ++ .../rcar/module/rcar_system/src/rcar_common.c | 73 +++ .../module/rcar_system/src/rcar_iic_dvfs.c | 558 ++++++++++++++++++ .../module/rcar_system/src/rcar_iic_dvfs.h | 24 + .../rcar/module/rcar_system/src/rcar_pwc.c | 215 +++++++ .../rcar/module/rcar_system/src/rcar_pwc.h | 16 + product/rcar/scp_ramfw/config_rcar_system.c | 51 ++ 11 files changed, 1509 insertions(+) create mode 100644 product/rcar/module/rcar_system/include/mod_rcar_system.h create mode 100644 product/rcar/module/rcar_system/src/FreeRTOS_tick_config.c create mode 100644 product/rcar/module/rcar_system/src/Makefile create mode 100644 product/rcar/module/rcar_system/src/mod_rcar_system.c create mode 100644 product/rcar/module/rcar_system/src/rcar_call_sram.S create mode 100644 product/rcar/module/rcar_system/src/rcar_common.c create mode 100644 product/rcar/module/rcar_system/src/rcar_iic_dvfs.c create mode 100644 product/rcar/module/rcar_system/src/rcar_iic_dvfs.h create mode 100644 product/rcar/module/rcar_system/src/rcar_pwc.c create mode 100644 product/rcar/module/rcar_system/src/rcar_pwc.h create mode 100644 product/rcar/scp_ramfw/config_rcar_system.c diff --git a/product/rcar/module/rcar_system/include/mod_rcar_system.h b/product/rcar/module/rcar_system/include/mod_rcar_system.h new file mode 100644 index 000000000..1a4a43f3c --- /dev/null +++ b/product/rcar/module/rcar_system/include/mod_rcar_system.h @@ -0,0 +1,67 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_RCAR_SYSTEM_H +#define MOD_RCAR_SYSTEM_H + +#include + +/*! + * \addtogroup GroupRCARModule RCAR Product Modules + * @{ + */ + +/*! + * \defgroup GroupRCARSystem RCAR System Support + * + * @{ + */ + +/*! + * \brief System device configuration. + */ +struct mod_rcar_system_dev_config { + /*! Reference to the device element within the associated driver module */ + const fwk_id_t driver_id; + + /*! Reference to the API provided by the device driver module */ + const fwk_id_t api_id; + +}; + +/*! + * \brief API indices. + */ +enum mod_rcar_system_api_idx { + /*! API index for the driver interface of the SYSTEM POWER module */ + MOD_RCAR_SYSTEM_API_IDX_SYSTEM_POWER_DRIVER, + + /*! Number of defined APIs */ + MOD_RCAR_SYSTEM_API_COUNT +}; + +/*! + * @cond + */ + +void rcar_system_code_copy_to_system_ram(void); +extern void vConfigureTickInterrupt(void); +extern void _save_system(void); + +/*! + * @endcond + */ + +/*! + * @} + */ + +/*! + * @} + */ + +#endif /* MOD_RCAR_SYSTEM_H */ diff --git a/product/rcar/module/rcar_system/src/FreeRTOS_tick_config.c b/product/rcar/module/rcar_system/src/FreeRTOS_tick_config.c new file mode 100644 index 000000000..795f6e91c --- /dev/null +++ b/product/rcar/module/rcar_system/src/FreeRTOS_tick_config.c @@ -0,0 +1,160 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include "FreeRTOS.h" +#include "task.h" + +#include "arch_gic.h" +#include "mmio.h" +#include "rcar_irq.h" +#include "rcar_mmap.h" + +uint32_t c_interrupt; + +/* ARM Generic Timer */ +#define CORE0_TIMER_IRQCNTL ((volatile uint32_t *)(0x40000040)) +static uint32_t timer_cntfrq = 0; +static uint32_t timer_tick = 0; + +static void init_generic_timer(void) +{ + uint32_t reg; + uint32_t reg_cntfid; + uint32_t modemr; + uint32_t modemr_pll; + + uint32_t pll_table[] = { + EXTAL_MD14_MD13_TYPE_0, /* MD14/MD13 : 0b00 */ + EXTAL_MD14_MD13_TYPE_1, /* MD14/MD13 : 0b01 */ + EXTAL_MD14_MD13_TYPE_2, /* MD14/MD13 : 0b10 */ + EXTAL_MD14_MD13_TYPE_3 /* MD14/MD13 : 0b11 */ + }; + + modemr = mmio_read_32(RCAR_MODEMR); + modemr_pll = (modemr & MODEMR_BOOT_PLL_MASK); + + /* Set frequency data in CNTFID0 */ + reg_cntfid = pll_table[modemr_pll >> MODEMR_BOOT_PLL_SHIFT]; + reg = mmio_read_32(RCAR_PRR) & (RCAR_PRODUCT_MASK | RCAR_CUT_MASK); + switch (modemr_pll) { + case MD14_MD13_TYPE_0: +#ifdef SALVATORE_XS + reg_cntfid = EXTAL_SALVATOR_XS; +#endif + break; + case MD14_MD13_TYPE_3: + if (RCAR_PRODUCT_H3_CUT10 == reg) { + reg_cntfid = reg_cntfid >> 1U; + } + break; + default: + /* none */ + break; + } + /* Update memory mapped and register based freqency */ + __asm__ volatile ("msr cntfrq_el0, %0" :: "r" (reg_cntfid)); +} + + +void disable_cntv(void) +{ + uint32_t cntv_ctl; + cntv_ctl = 0; + __asm__ volatile ("msr cntv_ctl_el0, %0" :: "r" (cntv_ctl)); +} +/*-----------------------------------------------------------*/ + +void enable_cntv(void) +{ + uint32_t cntv_ctl; + cntv_ctl = 1; + __asm__ volatile ("msr cntv_ctl_el0, %0" :: "r" (cntv_ctl)); +} +/*-----------------------------------------------------------*/ + +void write_cntv_tval(uint32_t val) +{ + __asm__ volatile ("msr cntv_tval_el0, %0" :: "r" (val)); + return; +} +/*-----------------------------------------------------------*/ + +uint32_t read_cntfrq(void) +{ + uint32_t val; + __asm__ volatile ("mrs %0, cntfrq_el0" : "=r" (val)); + return val; +} +/*-----------------------------------------------------------*/ + +uint32_t read_cntv_tval(void) +{ + uint32_t val; + __asm__ volatile ("mrs %0, cntvct_el0" : "=r" (val)); + return val; +} +/*-----------------------------------------------------------*/ + +void init_timer(void) +{ + timer_cntfrq = timer_tick = read_cntfrq(); + /* clear cntv interrupt and set next 1 sec timer. */ + write_cntv_tval(timer_cntfrq); + return; +} +/*-----------------------------------------------------------*/ + +void timer_set_tick_rate_hz(uint32_t rate) +{ + timer_tick = timer_cntfrq / rate ; + write_cntv_tval(timer_tick); +} +/*-----------------------------------------------------------*/ + +void vConfigureTickInterrupt( void ) +{ + /* disable timer */ + disable_cntv(); + + /* init timer device. */ + init_generic_timer(); + init_timer(); + + /* set tick rate. */ + timer_set_tick_rate_hz(configTICK_RATE_HZ); + + /* start & enable interrupts in the timer. */ + enable_cntv(); +} +/*-----------------------------------------------------------*/ + +void vClearTickInterrupt( void ) +{ + /* clear cntv interrupt and set next timer. */ + write_cntv_tval(timer_tick); + return; +} +/*-----------------------------------------------------------*/ + +void vApplicationIRQHandler( void ) +{ + uint32_t ulInterruptID; + + c_interrupt = mmio_read_32(RCAR_GICC_BASE + GICC_IAR); + mmio_write_32(RCAR_GICC_BASE + GICC_EOIR, c_interrupt); + ulInterruptID = c_interrupt & 0x00000FFFUL; + + /* call handler function */ + if (ulInterruptID == VIRTUAL_TIMER_IRQ) { + /* Generic Timer */ + FreeRTOS_Tick_Handler(); + } else { + /* Peripherals */ + irq_global(ulInterruptID); + } + c_interrupt = 0; +} diff --git a/product/rcar/module/rcar_system/src/Makefile b/product/rcar/module/rcar_system/src/Makefile new file mode 100644 index 000000000..9337ca36e --- /dev/null +++ b/product/rcar/module/rcar_system/src/Makefile @@ -0,0 +1,16 @@ +# +# Renesas SCP/MCP Software +# Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +BS_LIB_NAME := RCAR SYSTEM +BS_LIB_SOURCES = mod_rcar_system.c \ + rcar_common.c \ + rcar_iic_dvfs.c \ + rcar_pwc.c \ + rcar_call_sram.S \ + FreeRTOS_tick_config.c + +include $(BS_DIR)/lib.mk diff --git a/product/rcar/module/rcar_system/src/mod_rcar_system.c b/product/rcar/module/rcar_system/src/mod_rcar_system.c new file mode 100644 index 000000000..38ccc4bea --- /dev/null +++ b/product/rcar/module/rcar_system/src/mod_rcar_system.c @@ -0,0 +1,281 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Device context */ +struct rcar_system_dev_ctx { + const struct mod_rcar_system_dev_config *config; + struct mod_rcar_clock_drv_api *api; +}; + +/* Module context */ +struct rcar_system_ctx { + struct rcar_system_dev_ctx *dev_ctx_table; + unsigned int dev_count; +}; + +static struct rcar_system_ctx module_ctx; + +/*-----------------------------------------------------------*/ +#define P_STATUS (_shutdown_request) + +/* SCMI services required to enable the messaging stack */ +static unsigned int scmi_notification_table[] = { + RCAR_SCMI_SERVICE_IDX_PSCI, + RCAR_SCMI_SERVICE_IDX_OSPM_0, +}; + +IMPORT_SYM(unsigned long, __system_ram_start__, SYSTEM_RAM_START); +IMPORT_SYM(unsigned long, __system_ram_end__, SYSTEM_RAM_END); +IMPORT_SYM(unsigned long, __sram_copy_start__, SRAM_COPY_START); + +/* + * Static helpers + */ + +static int messaging_stack_ready(void) +{ + return 0; +} + +bool is_available_shutdown_req(uint32_t req) +{ + bool ret; + + switch (req) { + case R_WARMBOOT: + case R_SUSPEND: + case R_RESET: + case R_OFF: + ret = true; + break; + default: + ret = false; + break; + } + return ret; +} + +void rcar_system_code_copy_to_system_ram(void) +{ + memcpy((void *)SCP_SRAM_BASE, (void *)SRAM_COPY_START, + (SYSTEM_RAM_END - SYSTEM_RAM_START)); +} + +void vApplicationIdleHook(void) +{ + uint32_t req; + struct rcar_system_dev_ctx *ctx; + fwk_id_t element_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_CLOCK, 0); + uint32_t i; + + if (is_available_shutdown_req(P_STATUS)) { + fwk_interrupt_global_disable(); + req = P_STATUS; + P_STATUS = R_CLEAR; + switch (req) { + case R_SUSPEND: + _boot_flag = R_WARMBOOT; + while (!(mmio_read_32(RCAR_CA57PSTR) & 0x0f)) + continue; + + _save_system(); + + rcar_system_code_copy_to_system_ram(); + + for (i = 0; i < module_ctx.dev_count; i++) { + element_id.element.element_idx = i; + ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(element_id); + if (ctx->api->resume) + ctx->api->resume(); + } + + reg_sensor_resume(); + mod_rcar_scif_resume(); + gic_init(); + vConfigureTickInterrupt(); + fwk_interrupt_global_enable(); + break; + case R_RESET: + rcar_system_reset(); + break; + case R_OFF: + rcar_system_off(); + break; + default: + break; + } + } +} + +/* + * Functions fulfilling the framework's module interface + */ + +static int rcar_system_shutdown(enum mod_pd_system_shutdown system_shutdown) +{ + switch (system_shutdown) { + case MOD_PD_SYSTEM_SHUTDOWN: + _shutdown_request = R_OFF; + break; + case MOD_PD_SYSTEM_COLD_RESET: + _shutdown_request = R_RESET; + break; + default: + break; + } + + return FWK_SUCCESS; +} + +static const struct mod_system_power_driver_api + rcar_system_system_power_driver_api = { + .system_shutdown = rcar_system_shutdown +}; + +/* + * Functions fulfilling the framework's module interface + */ + +static int rcar_system_init(fwk_id_t module_id, unsigned int element_count, + const void *data) +{ + module_ctx.dev_count = element_count; + + if (element_count == 0) + return FWK_SUCCESS; + + module_ctx.dev_ctx_table = fwk_mm_calloc(element_count, + sizeof(struct rcar_system_dev_ctx)); + if (module_ctx.dev_ctx_table == NULL) + return FWK_E_NOMEM; + + return FWK_SUCCESS; +} +static int rcar_system_element(fwk_id_t element_id, unsigned int sub_element_count, + const void *data) +{ + struct rcar_system_dev_ctx *ctx; + const struct mod_rcar_system_dev_config *dev_config = data; + + ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(element_id); + ctx->config = dev_config; + + return FWK_SUCCESS; +} + +static int rcar_system_bind(fwk_id_t id, unsigned int round) +{ + struct rcar_system_dev_ctx *ctx; + + if (round == 1) + return FWK_SUCCESS; + + if (!fwk_id_is_type(id, FWK_ID_TYPE_ELEMENT)) + /* Only element binding is supported */ + return FWK_SUCCESS; + + ctx = module_ctx.dev_ctx_table + fwk_id_get_element_idx(id); + + return fwk_module_bind(ctx->config->driver_id, + ctx->config->api_id, + &ctx->api); + +} + +static int rcar_system_process_bind_request(fwk_id_t source_id, + fwk_id_t target_id, fwk_id_t api_id, const void **api) +{ + *api = &rcar_system_system_power_driver_api; + + return FWK_SUCCESS; +} + +static int rcar_system_start(fwk_id_t id) +{ + int status; + unsigned int i; + + /* + * Subscribe to these SCMI channels in order to know when they have all + * initialized. + * At that point we can consider the SCMI stack to be initialized from + * the point of view of the PSCI agent. + */ + if (fwk_id_get_type(id) != FWK_ID_TYPE_MODULE) + return FWK_SUCCESS; + + for (i = 0; i < FWK_ARRAY_SIZE(scmi_notification_table); i++) { + status = fwk_notification_subscribe( + mod_scmi_notification_id_initialized, + fwk_id_build_element_id(fwk_module_id_scmi, + scmi_notification_table[i]), + id); + if (status != FWK_SUCCESS) + return status; + } + + return FWK_SUCCESS; +} + +static int rcar_system_process_notification(const struct fwk_event *event, + struct fwk_event *resp_event) +{ + static unsigned int scmi_notification_count = 0; + + if (!fwk_expect(fwk_id_is_type(event->target_id, FWK_ID_TYPE_MODULE))) + return FWK_E_PARAM; + + if (fwk_id_is_equal(event->id, mod_scmi_notification_id_initialized)) + scmi_notification_count++; + + if (scmi_notification_count == FWK_ARRAY_SIZE(scmi_notification_table)) { + messaging_stack_ready(); + + scmi_notification_count = 0; + } + + return FWK_SUCCESS; +} + +const struct fwk_module module_rcar_system = { + .name = "RCAR_SYSTEM", + .api_count = MOD_RCAR_SYSTEM_API_COUNT, + .type = FWK_MODULE_TYPE_DRIVER, + .init = rcar_system_init, + .element_init = rcar_system_element, + .bind = rcar_system_bind, + .start = rcar_system_start, + .process_bind_request = rcar_system_process_bind_request, + .process_notification = rcar_system_process_notification, +}; diff --git a/product/rcar/module/rcar_system/src/rcar_call_sram.S b/product/rcar/module/rcar_system/src/rcar_call_sram.S new file mode 100644 index 000000000..3519222bb --- /dev/null +++ b/product/rcar/module/rcar_system/src/rcar_call_sram.S @@ -0,0 +1,48 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include + +.global rcar_pwrc_switch_stack + +/* + * x0 : jump address, + * x1 : stack address, + * x2 : arg, + * x3 : stack address (temporary) + */ +func rcar_pwrc_switch_stack + + /* lr to stack */ + stp x29, x30, [sp,#-16] + + /* change stack pointer */ + mov x3, sp + mov sp, x1 + + /* save stack pointer */ + sub sp, sp, #16 + stp x0, x3, [sp] + + /* data synchronization barrier */ + dsb sy + + /* jump to code */ + mov x1, x0 + mov x0, x2 + blr x1 + + /* load stack pointer */ + ldp x0, x2, [sp,#0] + + /* change stack pointer */ + mov sp, x2 + + /* return */ + ldp x29, x30, [sp,#-16] + ret +endfunc rcar_pwrc_switch_stack diff --git a/product/rcar/module/rcar_system/src/rcar_common.c b/product/rcar/module/rcar_system/src/rcar_common.c new file mode 100644 index 000000000..0a406288d --- /dev/null +++ b/product/rcar/module/rcar_system/src/rcar_common.c @@ -0,0 +1,73 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include + +void FWK_SECTION(".system_ram") + cpg_write(uintptr_t regadr, uint32_t regval) +{ + uint32_t value = (regval); + mmio_write_32((uintptr_t) RCAR_CPGWPR, ~value); + mmio_write_32(regadr, value); +} + +void FWK_SECTION(".system_ram") + mstpcr_write(uint32_t mstpcr, uint32_t mstpsr, uint32_t target_bit) +{ + uint32_t reg; + reg = mmio_read_32(mstpcr); + reg &= ~target_bit; + cpg_write(mstpcr, reg); + while ((mmio_read_32(mstpsr) & target_bit) != 0U) + continue; +} + +static inline void cpu_relax(void) +{ + __asm__ volatile("yield" ::: "memory"); +} + +static uint64_t FWK_SECTION(".system_ram") + get_cntfrq(void) +{ + uint64_t val; + __asm__ volatile ("mrs %0, cntfrq_el0" : "=r" (val)); + return val; +} + +static uint64_t FWK_SECTION(".system_ram") + get_cntvct(void) +{ + uint64_t val; + __asm__ volatile ("mrs %0, cntvct_el0" : "=r" (val)); + return val; +} + +static void FWK_SECTION(".system_ram") + delay(uint64_t cycles) +{ + uint64_t start = get_cntvct(); + + while ((get_cntvct() - start) < cycles) + cpu_relax(); +} + +void FWK_SECTION(".system_ram") + udelay(unsigned long usec) +{ + delay((uint64_t)usec * get_cntfrq() / 1000000); +} + +void FWK_SECTION(".system_ram") + mdelay(unsigned long msecs) +{ + while (msecs--) + udelay(1000); +} diff --git a/product/rcar/module/rcar_system/src/rcar_iic_dvfs.c b/product/rcar/module/rcar_system/src/rcar_iic_dvfs.c new file mode 100644 index 000000000..875820c2e --- /dev/null +++ b/product/rcar/module/rcar_system/src/rcar_iic_dvfs.c @@ -0,0 +1,558 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include "rcar_mmap.h" +#include "rcar_iic_dvfs.h" +#include "rcar_common.h" + +#define DVFS_RETRY_MAX (2U) + +#define IIC_DVFS_SET_ICCL_EXTAL_TYPE_0 (0x07) +#define IIC_DVFS_SET_ICCL_EXTAL_TYPE_1 (0x09) +#define IIC_DVFS_SET_ICCL_EXTAL_TYPE_2 (0x0B) +#define IIC_DVFS_SET_ICCL_EXTAL_TYPE_3 (0x0E) +#define IIC_DVFS_SET_ICCL_EXTAL_TYPE_E (0x15) + +#define IIC_DVFS_SET_ICCH_EXTAL_TYPE_0 (0x01) +#define IIC_DVFS_SET_ICCH_EXTAL_TYPE_1 (0x02) +#define IIC_DVFS_SET_ICCH_EXTAL_TYPE_2 (0x03) +#define IIC_DVFS_SET_ICCH_EXTAL_TYPE_3 (0x05) +#define IIC_DVFS_SET_ICCH_EXTAL_TYPE_E (0x07) + +#define CPG_BIT_SMSTPCR9_DVFS (0x04000000) + +#define IIC_DVFS_REG_BASE (0xE60B0000) +#define IIC_DVFS_REG_ICDR (IIC_DVFS_REG_BASE + 0x0000) +#define IIC_DVFS_REG_ICCR (IIC_DVFS_REG_BASE + 0x0004) +#define IIC_DVFS_REG_ICSR (IIC_DVFS_REG_BASE + 0x0008) +#define IIC_DVFS_REG_ICIC (IIC_DVFS_REG_BASE + 0x000C) +#define IIC_DVFS_REG_ICCL (IIC_DVFS_REG_BASE + 0x0010) +#define IIC_DVFS_REG_ICCH (IIC_DVFS_REG_BASE + 0x0014) + +#define IIC_DVFS_BIT_ICSR_BUSY (0x10) +#define IIC_DVFS_BIT_ICSR_AL (0x08) +#define IIC_DVFS_BIT_ICSR_TACK (0x04) +#define IIC_DVFS_BIT_ICSR_WAIT (0x02) +#define IIC_DVFS_BIT_ICSR_DTE (0x01) + +#define IIC_DVFS_BIT_ICCR_ENABLE (0x80) +#define IIC_DVFS_SET_ICCR_START (0x94) +#define IIC_DVFS_SET_ICCR_STOP (0x90) +#define IIC_DVFS_SET_ICCR_RETRANSMISSION (0x94) +#define IIC_DVFS_SET_ICCR_CHANGE (0x81) +#define IIC_DVFS_SET_ICCR_STOP_READ (0xC0) + +#define IIC_DVFS_BIT_ICIC_TACKE (0x04) +#define IIC_DVFS_BIT_ICIC_WAITE (0x02) +#define IIC_DVFS_BIT_ICIC_DTEE (0x01) + +#define DVFS_READ_MODE (0x01) +#define DVFS_WRITE_MODE (0x00) + +#define IIC_DVFS_SET_DUMMY (0x52) +#define IIC_DVFS_SET_BUSY_LOOP (500000000U) + +typedef enum { + DVFS_START = 0, + DVFS_STOP, + DVFS_RETRANSMIT, + DVFS_READ, + DVFS_STOP_READ, + DVFS_SET_SLAVE_READ, + DVFS_SET_SLAVE, + DVFS_WRITE_ADDR, + DVFS_WRITE_DATA, + DVFS_CHANGE_SEND_TO_RECIEVE, + DVFS_DONE, +} DVFS_STATE_T; + +#define DVFS_PROCESS (1) +#define DVFS_COMPLETE (0) +#define DVFS_ERROR (-1) + +#define IIC_DVFS_FUNC(__name, ...) \ +static int32_t FWK_SECTION(".system_ram") \ +dvfs_ ##__name(__VA_ARGS__) + +#define RCAR_DVFS_API(__name, ...) \ +int32_t FWK_SECTION(".system_ram") \ +rcar_iic_dvfs_ ##__name(__VA_ARGS__) + +extern void panic(void); + +IIC_DVFS_FUNC(check_error, DVFS_STATE_T *state, uint32_t *err, uint8_t mode) +{ + uint8_t icsr_al = 0, icsr_tack = 0; + uint8_t reg, stop; + uint32_t i = 0; + + stop = mode == DVFS_READ_MODE ? IIC_DVFS_SET_ICCR_STOP_READ : + IIC_DVFS_SET_ICCR_STOP; + + reg = mmio_read_8(IIC_DVFS_REG_ICSR); + icsr_al = (reg & IIC_DVFS_BIT_ICSR_AL) == IIC_DVFS_BIT_ICSR_AL; + icsr_tack = (reg & IIC_DVFS_BIT_ICSR_TACK) == IIC_DVFS_BIT_ICSR_TACK; + + if (icsr_al == 0 && icsr_tack == 0) + return DVFS_PROCESS; + + if (icsr_al) { + reg = mmio_read_8(IIC_DVFS_REG_ICSR) & ~IIC_DVFS_BIT_ICSR_AL; + mmio_write_8(IIC_DVFS_REG_ICSR, reg); + + if (*state == DVFS_SET_SLAVE) + mmio_write_8(IIC_DVFS_REG_ICDR, IIC_DVFS_SET_DUMMY); + + do { + reg = mmio_read_8(IIC_DVFS_REG_ICSR) & + IIC_DVFS_BIT_ICSR_WAIT; + } while (reg == 0); + + mmio_write_8(IIC_DVFS_REG_ICCR, stop); + + reg = mmio_read_8(IIC_DVFS_REG_ICSR) & ~IIC_DVFS_BIT_ICSR_WAIT; + mmio_write_8(IIC_DVFS_REG_ICSR, reg); + + i = 0; + do { + reg = mmio_read_8(IIC_DVFS_REG_ICSR) & + IIC_DVFS_BIT_ICSR_BUSY; + if (reg == 0) + break; + + if (i++ > IIC_DVFS_SET_BUSY_LOOP) + panic(); + + } while (1); + + mmio_write_8(IIC_DVFS_REG_ICCR, 0x00U); + + (*err)++; + if (*err > DVFS_RETRY_MAX) + return DVFS_ERROR; + + *state = DVFS_START; + + return DVFS_PROCESS; + + } + + /* icsr_tack */ + mmio_write_8(IIC_DVFS_REG_ICCR, stop); + + reg = mmio_read_8(IIC_DVFS_REG_ICIC); + reg &= ~(IIC_DVFS_BIT_ICIC_WAITE | IIC_DVFS_BIT_ICIC_DTEE); + mmio_write_8(IIC_DVFS_REG_ICIC, reg); + + reg = mmio_read_8(IIC_DVFS_REG_ICSR) & ~IIC_DVFS_BIT_ICSR_TACK; + mmio_write_8(IIC_DVFS_REG_ICSR, reg); + + i = 0; + while ((mmio_read_8(IIC_DVFS_REG_ICSR) & IIC_DVFS_BIT_ICSR_BUSY) != 0) { + if (i++ > IIC_DVFS_SET_BUSY_LOOP) + panic(); + } + + mmio_write_8(IIC_DVFS_REG_ICCR, 0); + (*err)++; + + if (*err > DVFS_RETRY_MAX) + return DVFS_ERROR; + + *state = DVFS_START; + + return DVFS_PROCESS; +} + +IIC_DVFS_FUNC(start, DVFS_STATE_T * state) +{ + uint8_t iccl = IIC_DVFS_SET_ICCL_EXTAL_TYPE_E; + uint8_t icch = IIC_DVFS_SET_ICCH_EXTAL_TYPE_E; + int32_t result = DVFS_PROCESS; + uint32_t reg, lsi_product; + uint8_t mode; + + mode = mmio_read_8(IIC_DVFS_REG_ICCR) | IIC_DVFS_BIT_ICCR_ENABLE; + mmio_write_8(IIC_DVFS_REG_ICCR, mode); + + lsi_product = mmio_read_32(RCAR_PRR) & RCAR_PRODUCT_MASK; + if (lsi_product != RCAR_PRODUCT_E3) { + reg = mmio_read_32(RCAR_MODEMR) & CHECK_MD13_MD14; + switch (reg) { + case MD14_MD13_TYPE_0: + iccl = IIC_DVFS_SET_ICCL_EXTAL_TYPE_0; + icch = IIC_DVFS_SET_ICCH_EXTAL_TYPE_0; + break; + case MD14_MD13_TYPE_1: + iccl = IIC_DVFS_SET_ICCL_EXTAL_TYPE_1; + icch = IIC_DVFS_SET_ICCH_EXTAL_TYPE_1; + break; + case MD14_MD13_TYPE_2: + iccl = IIC_DVFS_SET_ICCL_EXTAL_TYPE_2; + icch = IIC_DVFS_SET_ICCH_EXTAL_TYPE_2; + break; + default: + iccl = IIC_DVFS_SET_ICCL_EXTAL_TYPE_3; + icch = IIC_DVFS_SET_ICCH_EXTAL_TYPE_3; + break; + } + } + + mmio_write_8(IIC_DVFS_REG_ICCL, iccl); + mmio_write_8(IIC_DVFS_REG_ICCH, icch); + + mode = mmio_read_8(IIC_DVFS_REG_ICIC) + | IIC_DVFS_BIT_ICIC_TACKE + | IIC_DVFS_BIT_ICIC_WAITE | IIC_DVFS_BIT_ICIC_DTEE; + + mmio_write_8(IIC_DVFS_REG_ICIC, mode); + mmio_write_8(IIC_DVFS_REG_ICCR, IIC_DVFS_SET_ICCR_START); + + *state = DVFS_SET_SLAVE; + + return result; +} + +IIC_DVFS_FUNC(set_slave, DVFS_STATE_T * state, uint32_t *err, uint8_t slave) +{ + uint8_t mode; + int32_t result; + uint8_t address; + + result = dvfs_check_error(state, err, DVFS_WRITE_MODE); + if (result == DVFS_ERROR) + return result; + + mode = mmio_read_8(IIC_DVFS_REG_ICSR) & IIC_DVFS_BIT_ICSR_DTE; + if (mode != IIC_DVFS_BIT_ICSR_DTE) + return result; + + mode = mmio_read_8(IIC_DVFS_REG_ICIC) & ~IIC_DVFS_BIT_ICIC_DTEE; + mmio_write_8(IIC_DVFS_REG_ICIC, mode); + + address = slave << 1; + mmio_write_8(IIC_DVFS_REG_ICDR, address); + + *state = DVFS_WRITE_ADDR; + + return result; +} + +IIC_DVFS_FUNC(write_addr, DVFS_STATE_T *state, uint32_t *err, uint8_t reg_addr) +{ + uint8_t mode; + int32_t result; + + result = dvfs_check_error(state, err, DVFS_WRITE_MODE); + if (result == DVFS_ERROR) + return result; + + mode = mmio_read_8(IIC_DVFS_REG_ICSR) & IIC_DVFS_BIT_ICSR_WAIT; + if (mode != IIC_DVFS_BIT_ICSR_WAIT) + return result; + + mmio_write_8(IIC_DVFS_REG_ICDR, reg_addr); + + mode = mmio_read_8(IIC_DVFS_REG_ICSR) & ~IIC_DVFS_BIT_ICSR_WAIT; + mmio_write_8(IIC_DVFS_REG_ICSR, mode); + + *state = DVFS_WRITE_DATA; + + return result; +} + +IIC_DVFS_FUNC(write_data, DVFS_STATE_T *state, uint32_t *err, uint8_t reg_data) +{ + int32_t result; + uint8_t mode; + + result = dvfs_check_error(state, err, DVFS_WRITE_MODE); + if (result == DVFS_ERROR) + return result; + + mode = mmio_read_8(IIC_DVFS_REG_ICSR) & IIC_DVFS_BIT_ICSR_WAIT; + if (mode != IIC_DVFS_BIT_ICSR_WAIT) + return result; + + mmio_write_8(IIC_DVFS_REG_ICDR, reg_data); + + mode = mmio_read_8(IIC_DVFS_REG_ICSR) & ~IIC_DVFS_BIT_ICSR_WAIT; + mmio_write_8(IIC_DVFS_REG_ICSR, mode); + + *state = DVFS_STOP; + + return result; +} + +IIC_DVFS_FUNC(stop, DVFS_STATE_T *state, uint32_t *err) +{ + int32_t result; + uint8_t mode; + + result = dvfs_check_error(state, err, DVFS_WRITE_MODE); + if (result == DVFS_ERROR) + return result; + + mode = mmio_read_8(IIC_DVFS_REG_ICSR) & IIC_DVFS_BIT_ICSR_WAIT; + if (mode != IIC_DVFS_BIT_ICSR_WAIT) + return result; + + mmio_write_8(IIC_DVFS_REG_ICCR, IIC_DVFS_SET_ICCR_STOP); + + mode = mmio_read_8(IIC_DVFS_REG_ICSR) & ~IIC_DVFS_BIT_ICSR_WAIT; + mmio_write_8(IIC_DVFS_REG_ICSR, mode); + + *state = DVFS_DONE; + + return result; +} + +IIC_DVFS_FUNC(done, void) +{ + uint32_t i; + + for (i = 0; i < IIC_DVFS_SET_BUSY_LOOP; i++) { + if (mmio_read_8(IIC_DVFS_REG_ICSR) & IIC_DVFS_BIT_ICSR_BUSY) + continue; + goto done; + } + + panic(); +done: + mmio_write_8(IIC_DVFS_REG_ICCR, 0); + + return DVFS_COMPLETE; +} + +IIC_DVFS_FUNC(write_reg_addr_read, DVFS_STATE_T *state, uint32_t *err, + uint8_t reg_addr) +{ + int32_t result; + uint8_t mode; + + result = dvfs_check_error(state, err, DVFS_WRITE_MODE); + if (result == DVFS_ERROR) + return result; + + mode = mmio_read_8(IIC_DVFS_REG_ICSR) & IIC_DVFS_BIT_ICSR_WAIT; + if (mode != IIC_DVFS_BIT_ICSR_WAIT) + return result; + + mmio_write_8(IIC_DVFS_REG_ICDR, reg_addr); + + mode = mmio_read_8(IIC_DVFS_REG_ICSR) & ~IIC_DVFS_BIT_ICSR_WAIT; + mmio_write_8(IIC_DVFS_REG_ICSR, mode); + + *state = DVFS_RETRANSMIT; + + return result; +} + +IIC_DVFS_FUNC(retransmit, DVFS_STATE_T *state, uint32_t *err) +{ + int32_t result; + uint8_t mode; + + result = dvfs_check_error(state, err, DVFS_WRITE_MODE); + if (result == DVFS_ERROR) + return result; + + mode = mmio_read_8(IIC_DVFS_REG_ICSR) & IIC_DVFS_BIT_ICSR_WAIT; + if (mode != IIC_DVFS_BIT_ICSR_WAIT) + return result; + + mmio_write_8(IIC_DVFS_REG_ICCR, IIC_DVFS_SET_ICCR_RETRANSMISSION); + + mode = mmio_read_8(IIC_DVFS_REG_ICSR) & ~IIC_DVFS_BIT_ICSR_WAIT; + mmio_write_8(IIC_DVFS_REG_ICSR, mode); + + mode = mmio_read_8(IIC_DVFS_REG_ICIC) | IIC_DVFS_BIT_ICIC_DTEE; + mmio_write_8(IIC_DVFS_REG_ICIC, mode); + + *state = DVFS_SET_SLAVE_READ; + + return result; +} + +IIC_DVFS_FUNC(set_slave_read, DVFS_STATE_T *state, uint32_t *err, + uint8_t slave) +{ + uint8_t address; + int32_t result; + uint8_t mode; + + result = dvfs_check_error(state, err, DVFS_WRITE_MODE); + if (result == DVFS_ERROR) + return result; + + mode = mmio_read_8(IIC_DVFS_REG_ICSR) & IIC_DVFS_BIT_ICSR_DTE; + if (mode != IIC_DVFS_BIT_ICSR_DTE) + return result; + + mode = mmio_read_8(IIC_DVFS_REG_ICIC) & ~IIC_DVFS_BIT_ICIC_DTEE; + mmio_write_8(IIC_DVFS_REG_ICIC, mode); + + address = ((uint8_t) (slave << 1) + DVFS_READ_MODE); + mmio_write_8(IIC_DVFS_REG_ICDR, address); + + *state = DVFS_CHANGE_SEND_TO_RECIEVE; + + return result; +} + +IIC_DVFS_FUNC(change_send_to_recieve, DVFS_STATE_T *state, uint32_t *err) +{ + int32_t result; + uint8_t mode; + + result = dvfs_check_error(state, err, DVFS_WRITE_MODE); + if (result == DVFS_ERROR) + return result; + + mode = mmio_read_8(IIC_DVFS_REG_ICSR) & IIC_DVFS_BIT_ICSR_WAIT; + if (mode != IIC_DVFS_BIT_ICSR_WAIT) + return result; + + mmio_write_8(IIC_DVFS_REG_ICCR, IIC_DVFS_SET_ICCR_CHANGE); + + mode = mmio_read_8(IIC_DVFS_REG_ICSR) & ~IIC_DVFS_BIT_ICSR_WAIT; + mmio_write_8(IIC_DVFS_REG_ICSR, mode); + + *state = DVFS_STOP_READ; + + return result; +} + +IIC_DVFS_FUNC(stop_read, DVFS_STATE_T *state, uint32_t *err) +{ + int32_t result; + uint8_t mode; + + result = dvfs_check_error(state, err, DVFS_READ_MODE); + if (result == DVFS_ERROR) + return result; + + mode = mmio_read_8(IIC_DVFS_REG_ICSR) & IIC_DVFS_BIT_ICSR_WAIT; + if (mode != IIC_DVFS_BIT_ICSR_WAIT) + return result; + + mmio_write_8(IIC_DVFS_REG_ICCR, IIC_DVFS_SET_ICCR_STOP_READ); + + mode = mmio_read_8(IIC_DVFS_REG_ICSR) & ~IIC_DVFS_BIT_ICSR_WAIT; + mmio_write_8(IIC_DVFS_REG_ICSR, mode); + + mode = mmio_read_8(IIC_DVFS_REG_ICIC) | IIC_DVFS_BIT_ICIC_DTEE; + mmio_write_8(IIC_DVFS_REG_ICIC, mode); + + *state = DVFS_READ; + + return result; +} + +IIC_DVFS_FUNC(read, DVFS_STATE_T *state, uint8_t *reg_data) +{ + uint8_t mode; + + mode = mmio_read_8(IIC_DVFS_REG_ICSR) & IIC_DVFS_BIT_ICSR_DTE; + if (mode != IIC_DVFS_BIT_ICSR_DTE) + return DVFS_PROCESS; + + mode = mmio_read_8(IIC_DVFS_REG_ICIC) & ~IIC_DVFS_BIT_ICIC_DTEE; + mmio_write_8(IIC_DVFS_REG_ICIC, mode); + + *reg_data = mmio_read_8(IIC_DVFS_REG_ICDR); + *state = DVFS_DONE; + + return DVFS_PROCESS; +} + +RCAR_DVFS_API(send, uint8_t slave, uint8_t reg_addr, uint8_t reg_data) +{ + DVFS_STATE_T state = DVFS_START; + int32_t result = DVFS_PROCESS; + uint32_t err = 0; + + mstpcr_write(SCMSTPCR9, CPG_MSTPSR9, CPG_BIT_SMSTPCR9_DVFS); + mmio_write_8(IIC_DVFS_REG_ICCR, 0); + + do { + switch (state) { + case DVFS_START: + result = dvfs_start(&state); + break; + case DVFS_SET_SLAVE: + result = dvfs_set_slave(&state, &err, slave); + break; + case DVFS_WRITE_ADDR: + result = dvfs_write_addr(&state, &err, reg_addr); + break; + case DVFS_WRITE_DATA: + result = dvfs_write_data(&state, &err, reg_data); + break; + case DVFS_STOP: + result = dvfs_stop(&state, &err); + break; + case DVFS_DONE: + result = dvfs_done(); + break; + default: + panic(); + break; + } + } while (result == DVFS_PROCESS); + + return result; +} + +RCAR_DVFS_API(receive, uint8_t slave, uint8_t reg, uint8_t *data) +{ + DVFS_STATE_T state = DVFS_START; + int32_t result = DVFS_PROCESS; + uint32_t err = 0; + + mstpcr_write(SCMSTPCR9, CPG_MSTPSR9, CPG_BIT_SMSTPCR9_DVFS); + mmio_write_8(IIC_DVFS_REG_ICCR, 0); + + do { + switch (state) { + case DVFS_START: + result = dvfs_start(&state); + break; + case DVFS_SET_SLAVE: + result = dvfs_set_slave(&state, &err, slave); + break; + case DVFS_WRITE_ADDR: + result = dvfs_write_reg_addr_read(&state, &err, reg); + break; + case DVFS_RETRANSMIT: + result = dvfs_retransmit(&state, &err); + break; + case DVFS_SET_SLAVE_READ: + result = dvfs_set_slave_read(&state, &err, slave); + break; + case DVFS_CHANGE_SEND_TO_RECIEVE: + result = dvfs_change_send_to_recieve(&state, &err); + break; + case DVFS_STOP_READ: + result = dvfs_stop_read(&state, &err); + break; + case DVFS_READ: + result = dvfs_read(&state, data); + break; + case DVFS_DONE: + result = dvfs_done(); + break; + default: + panic(); + break; + } + } while (result == DVFS_PROCESS); + + return result; +} diff --git a/product/rcar/module/rcar_system/src/rcar_iic_dvfs.h b/product/rcar/module/rcar_system/src/rcar_iic_dvfs.h new file mode 100644 index 000000000..c2b2102a9 --- /dev/null +++ b/product/rcar/module/rcar_system/src/rcar_iic_dvfs.h @@ -0,0 +1,24 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RCAR_IIC_DVFS_H +#define RCAR_IIC_DVFS_H + +/* PMIC slave */ +#define PMIC (0x30) +#define BKUP_MODE_CNT (0x20) +#define DVFS_SET_VID (0x54) +#define REG_KEEP10 (0x79) + +/* EEPROM slave */ +#define EEPROM (0x50) +#define BOARD_ID (0x70) + +int32_t rcar_iic_dvfs_receive(uint8_t slave, uint8_t reg, uint8_t *data); +int32_t rcar_iic_dvfs_send(uint8_t slave, uint8_t regr, uint8_t data); + +#endif /* RCAR_IIC_DVFS_H */ diff --git a/product/rcar/module/rcar_system/src/rcar_pwc.c b/product/rcar/module/rcar_system/src/rcar_pwc.c new file mode 100644 index 000000000..37314d220 --- /dev/null +++ b/product/rcar/module/rcar_system/src/rcar_pwc.c @@ -0,0 +1,215 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define PMIC_ROHM_BD9571 1 + +/* Suspend to ram */ +#define DBSC4_REG_BASE (0xE6790000U) +#define DBSC4_REG_DBSYSCNT0 (DBSC4_REG_BASE + 0x0100U) +#define DBSC4_REG_DBACEN (DBSC4_REG_BASE + 0x0200U) +#define DBSC4_REG_DBCMD (DBSC4_REG_BASE + 0x0208U) +#define DBSC4_REG_DBWAIT (DBSC4_REG_BASE + 0x0210U) +#define DBSC4_REG_DBRFEN (DBSC4_REG_BASE + 0x0204U) + +#define DBSC4_REG_DBCALCNF (DBSC4_REG_BASE + 0x0424U) +#define DBSC4_REG_DBDFIPMSTRCNF (DBSC4_REG_BASE + 0x0520U) + +#define DBSC4_REG_DBCAM0CTRL0 (DBSC4_REG_BASE + 0x0940U) +#define DBSC4_REG_DBCAM0STAT0 (DBSC4_REG_BASE + 0x0980U) +#define DBSC4_REG_DBCAM1STAT0 (DBSC4_REG_BASE + 0x0990U) +#define DBSC4_REG_DBCAM2STAT0 (DBSC4_REG_BASE + 0x09A0U) +#define DBSC4_REG_DBCAM3STAT0 (DBSC4_REG_BASE + 0x09B0U) + +#define DBSC4_BIT_DBCAMxSTAT0 (0x00000001U) +#define DBSC4_BIT_DBDFIPMSTRCNF_PMSTREN (0x00000001U) +#define DBSC4_SET_DBCMD_OPC_PRE (0x04000000U) +#define DBSC4_SET_DBCMD_OPC_SR (0x0A000000U) +#define DBSC4_SET_DBCMD_OPC_PD (0x08000000U) +#define DBSC4_SET_DBCMD_OPC_MRW (0x0E000000U) +#define DBSC4_SET_DBCMD_ARG_ENTER (0x00000000U) +#define DBSC4_SET_DBCMD_ARG_MRW_ODTC (0x00000B00U) +#define DBSC4_SET_DBCMD_CH_ALL (0x00800000U) +#define DBSC4_SET_DBCMD_RANK_ALL (0x00040000U) +#define DBSC4_SET_DBCMD_ARG_ALL (0x00000010U) +#define DBSC4_SET_DBSYSCNT0_WRITE_ENABLE (0x00001234U) +#define DBSC4_SET_DBSYSCNT0_WRITE_DISABLE (0x00000000U) + +#if PMIC_ROHM_BD9571 +#define BIT_BKUP_CTRL_OUT ((uint8_t)(1U << 4)) +#define PMIC_RETRY_MAX (100U) +#define PMIC_BKUP_MODE_CNT (0x20U) +#define PMIC_QLLM_CNT (0x27U) +#define DVFS_SET_VID_0V (0x00) +#define P_ALL_OFF (0x80) +#endif /* PMIC_ROHM_BD9571 */ + +#define SCTLR_EL3_M_BIT ((uint32_t)1U << 0) +#define RCAR_CONV_MICROSEC (1000000U) + +#define DBCAM_FLUSH(__bit) \ +do { \ + ; \ +} while (!(mmio_read_32(DBSC4_REG_DBCAM##__bit##STAT0) & DBSC4_BIT_DBCAMxSTAT0)) + +extern uint32_t rcar_pwrc_switch_stack(uintptr_t jump, uintptr_t stack, + void *arg); + +extern void panic(void); + +static void FWK_SECTION(".system_ram") + rcar_pwrc_set_self_refresh(void) +{ + uint32_t reg = mmio_read_32(RCAR_PRR); + uint32_t cut, product; + + product = reg & RCAR_PRODUCT_MASK; + cut = reg & RCAR_CUT_MASK; + + if (!((product == RCAR_PRODUCT_M3 && cut < RCAR_CUT_VER30) || + (product == RCAR_PRODUCT_H3 && cut < RCAR_CUT_VER20))) + mmio_write_32(DBSC4_REG_DBSYSCNT0, DBSC4_SET_DBSYSCNT0_WRITE_ENABLE); + + /* DFI_PHYMSTR_ACK setting */ + mmio_write_32(DBSC4_REG_DBDFIPMSTRCNF, + mmio_read_32(DBSC4_REG_DBDFIPMSTRCNF) & + (~DBSC4_BIT_DBDFIPMSTRCNF_PMSTREN)); + + /* Set the Self-Refresh mode */ + mmio_write_32(DBSC4_REG_DBACEN, 0); + + if (product == RCAR_PRODUCT_H3 && cut < RCAR_CUT_VER20) + udelay(100); + else if (product == RCAR_PRODUCT_H3) { + mmio_write_32(DBSC4_REG_DBCAM0CTRL0, 1); + DBCAM_FLUSH(0); + DBCAM_FLUSH(1); + DBCAM_FLUSH(2); + DBCAM_FLUSH(3); + mmio_write_32(DBSC4_REG_DBCAM0CTRL0, 0); + } else if (product == RCAR_PRODUCT_M3) { + mmio_write_32(DBSC4_REG_DBCAM0CTRL0, 1); + DBCAM_FLUSH(0); + DBCAM_FLUSH(1); + mmio_write_32(DBSC4_REG_DBCAM0CTRL0, 0); + } else { + mmio_write_32(DBSC4_REG_DBCAM0CTRL0, 1); + DBCAM_FLUSH(0); + mmio_write_32(DBSC4_REG_DBCAM0CTRL0, 0); + } + + /* Set the SDRAM calibration configuration register */ + mmio_write_32(DBSC4_REG_DBCALCNF, 0); + + reg = DBSC4_SET_DBCMD_OPC_PRE | DBSC4_SET_DBCMD_CH_ALL | + DBSC4_SET_DBCMD_RANK_ALL | DBSC4_SET_DBCMD_ARG_ALL; + mmio_write_32(DBSC4_REG_DBCMD, reg); + while (mmio_read_32(DBSC4_REG_DBWAIT)) + continue; + + /* Self-Refresh entry command */ + reg = DBSC4_SET_DBCMD_OPC_SR | DBSC4_SET_DBCMD_CH_ALL | + DBSC4_SET_DBCMD_RANK_ALL | DBSC4_SET_DBCMD_ARG_ENTER; + mmio_write_32(DBSC4_REG_DBCMD, reg); + while (mmio_read_32(DBSC4_REG_DBWAIT)) + continue; + + /* Mode Register Write command. (ODT disabled) */ + reg = DBSC4_SET_DBCMD_OPC_MRW | DBSC4_SET_DBCMD_CH_ALL | + DBSC4_SET_DBCMD_RANK_ALL | DBSC4_SET_DBCMD_ARG_MRW_ODTC; + mmio_write_32(DBSC4_REG_DBCMD, reg); + while (mmio_read_32(DBSC4_REG_DBWAIT)) + continue; + + /* Power Down entry command */ + reg = DBSC4_SET_DBCMD_OPC_PD | DBSC4_SET_DBCMD_CH_ALL | + DBSC4_SET_DBCMD_RANK_ALL | DBSC4_SET_DBCMD_ARG_ENTER; + mmio_write_32(DBSC4_REG_DBCMD, reg); + while (mmio_read_32(DBSC4_REG_DBWAIT)) + continue; + + /* Set the auto-refresh enable register */ + mmio_write_32(DBSC4_REG_DBRFEN, 0U); + udelay(1); + + if (product == RCAR_PRODUCT_M3 && cut < RCAR_CUT_VER30) + return; + + if (product == RCAR_PRODUCT_H3 && cut < RCAR_CUT_VER20) + return; + + mmio_write_32(DBSC4_REG_DBSYSCNT0, DBSC4_SET_DBSYSCNT0_WRITE_DISABLE); +} + +void FWK_SECTION(".system_ram") FWK_NOINLINE + rcar_pwrc_go_suspend_to_ram(void) +{ +#if PMIC_ROHM_BD9571 + int32_t rc = -1, qllm = -1; + uint8_t mode; + uint32_t i; +#endif /* PMIC_ROHM_BD9571 */ + + rcar_pwrc_set_self_refresh(); + +#if PMIC_ROHM_BD9571 + /* Set QLLM Cnt Disable */ + for (i = 0; (i < PMIC_RETRY_MAX) && (qllm != 0); i++) + qllm = rcar_iic_dvfs_send(PMIC, PMIC_QLLM_CNT, 0); + + /* Set trigger of power down to PMIV */ + for (i = 0; (i < PMIC_RETRY_MAX) && (rc != 0) && (qllm == 0); i++) { + rc = rcar_iic_dvfs_receive(PMIC, PMIC_BKUP_MODE_CNT, &mode); + if (rc == 0) { + mode |= BIT_BKUP_CTRL_OUT; + rc = rcar_iic_dvfs_send(PMIC, PMIC_BKUP_MODE_CNT, mode); + } + } +#endif /* PMIC_ROHM_BD9571 */ + + wfi(); + + while (1) + continue; +} + +void rcar_pwrc_set_suspend_to_ram(void) +{ + uintptr_t jump = (uintptr_t) &rcar_pwrc_go_suspend_to_ram; + uintptr_t stack = (uintptr_t) (SCP_SRAM_STACK_BASE + + SCP_SRAM_STACK_SIZE); + uint32_t sctlr; + + /* disable MMU */ + sctlr = (uint32_t) read_sctlr_el3(); + sctlr &= (uint32_t) ~SCTLR_EL3_M_BIT; + write_sctlr_el3((uint64_t) sctlr); + + rcar_pwrc_switch_stack(jump, stack, NULL); +} + +void rcar_system_off(void) +{ + if (rcar_iic_dvfs_send(PMIC, DVFS_SET_VID, DVFS_SET_VID_0V)) + panic(); +} + +void rcar_system_reset(void) +{ + if (rcar_iic_dvfs_send(PMIC, BKUP_MODE_CNT, P_ALL_OFF)) + panic(); +} diff --git a/product/rcar/module/rcar_system/src/rcar_pwc.h b/product/rcar/module/rcar_system/src/rcar_pwc.h new file mode 100644 index 000000000..f2c1b54bc --- /dev/null +++ b/product/rcar/module/rcar_system/src/rcar_pwc.h @@ -0,0 +1,16 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef RCAR_PWC_H +#define RCAR_PWC_H + +void rcar_pwrc_go_suspend_to_ram(void); +void rcar_pwrc_set_suspend_to_ram(void); +void rcar_system_off(void); +void rcar_system_reset(void); + +#endif /* RCAR_PWC_H */ diff --git a/product/rcar/scp_ramfw/config_rcar_system.c b/product/rcar/scp_ramfw/config_rcar_system.c new file mode 100644 index 000000000..64fcf2790 --- /dev/null +++ b/product/rcar/scp_ramfw/config_rcar_system.c @@ -0,0 +1,51 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +static struct fwk_element rcar_system_element_table[] = { + { + .name = "sd_closk", + .data = &((struct mod_rcar_system_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, 0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SD_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + { + .name = "mstp_clock", + .data = &((struct mod_rcar_system_dev_config) { + .driver_id = FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, 0), + .api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_MSTP_CLOCK, + MOD_RCAR_CLOCK_API_TYPE_CLOCK), + }), + }, + { 0 }, /* Termination description. */ +}; + +static const struct fwk_element *get_element_table(fwk_id_t module_id) +{ + return rcar_system_element_table; +} + +struct fwk_module_config config_rcar_system = { + .elements = FWK_MODULE_DYNAMIC_ELEMENTS(get_element_table), + .data = NULL, +}; -- GitLab From 305ed8d985dfd15d69febc8ab07538f0961b8f85 Mon Sep 17 00:00:00 2001 From: Nicolas Royer Date: Sun, 27 Sep 2020 17:48:36 +0200 Subject: [PATCH 16/19] rcar/module: add rcar system_power module and config data Signed-off-by: Tsutomu Muroya Signed-off-by: Nicolas Royer --- .../include/mod_rcar_system_power.h | 124 ++++++++++ .../include/mod_system_power.h | 15 ++ .../module/rcar_system_power/src/Makefile | 11 + .../src/mod_rcar_system_power.c | 231 ++++++++++++++++++ .../rcar/scp_ramfw/config_rcar_system_power.c | 23 ++ 5 files changed, 404 insertions(+) create mode 100644 product/rcar/module/rcar_system_power/include/mod_rcar_system_power.h create mode 100644 product/rcar/module/rcar_system_power/include/mod_system_power.h create mode 100644 product/rcar/module/rcar_system_power/src/Makefile create mode 100644 product/rcar/module/rcar_system_power/src/mod_rcar_system_power.c create mode 100644 product/rcar/scp_ramfw/config_rcar_system_power.c diff --git a/product/rcar/module/rcar_system_power/include/mod_rcar_system_power.h b/product/rcar/module/rcar_system_power/include/mod_rcar_system_power.h new file mode 100644 index 000000000..f0ba7fd40 --- /dev/null +++ b/product/rcar/module/rcar_system_power/include/mod_rcar_system_power.h @@ -0,0 +1,124 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2015-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_RCAR_SYSTEM_POWER_H +#define MOD_RCAR_SYSTEM_POWER_H + +#include +#include + +/*! + * \ingroup GroupRCARModule RCAR Product Modules + * \defgroup GroupRCARSystemPower System Power Support + * + * @{ + */ + +/*! Additional system_power power states */ +enum mod_system_power_power_states { + MOD_SYSTEM_POWER_POWER_STATE_SLEEP0 = MOD_PD_STATE_COUNT, + MOD_SYSTEM_POWER_POWER_STATE_SLEEP1, + MOD_SYSTEM_POWER_POWER_STATE_COUNT +}; + +/*! Extended PPU configuration */ +struct mod_system_power_ext_ppu_config { + /*! PPU identifier */ + fwk_id_t ppu_id; + + /*! API identifier */ + fwk_id_t api_id; +}; + +/*! Module configuration */ +struct mod_system_power_config { + /*! SoC wakeup IRQ number */ + unsigned int soc_wakeup_irq; + + /*! System 0 PPU element ID */ + fwk_id_t ppu_sys0_id; + + /*! System 1 PPU element ID */ + fwk_id_t ppu_sys1_id; + + /*! System PPUs API ID */ + fwk_id_t ppu_sys_api_id; + + /*! Number of extended PPUs */ + size_t ext_ppus_count; + + /*! + * \brief Pointer to array of extended PPU configurations. + * + * \details These PPUs will be powered on automatically with the rest of the + * system. + */ + const struct mod_system_power_ext_ppu_config *ext_ppus; + + /*! System shutdown driver identifier */ + fwk_id_t driver_id; + + /*! System shutdown driver API identifier */ + fwk_id_t driver_api_id; +}; + +/*! + * \brief Driver interface. + */ +struct mod_system_power_driver_api { + /*! + * \brief Pointer to the system shutdown function. + * + * \note This function is \b mandatory. In case of a successful call the + * function does not return. + * + * \param system_shutdown Type of system shutdown. + * + * \retval One of the driver-defined error code. + */ + int (*system_shutdown)(enum mod_pd_system_shutdown system_shutdown); +}; + +/*! + * \defgroup GroupSystemPowerIds Identifiers + * \{ + */ + +/*! + * \brief API indices. + */ +enum mod_system_power_api_idx { + /*! API index for the power domain driver API */ + MOD_SYSTEM_POWER_API_IDX_PD_DRIVER, + + /*! API index for the power domain driver input API */ + MOD_SYSTEM_POWER_API_IDX_PD_DRIVER_INPUT, + + /*! Number of exposed APIs */ + MOD_SYSTEM_POWER_API_COUNT +}; + +/*! Identifier of the power domain driver API */ +static const fwk_id_t mod_system_power_api_id_pd_driver = + FWK_ID_API_INIT(FWK_MODULE_IDX_SYSTEM_POWER, + MOD_SYSTEM_POWER_API_IDX_PD_DRIVER); + +/*! Identifier of the power domain driver input API */ +static const fwk_id_t mod_system_power_api_id_pd_driver_input = + FWK_ID_API_INIT(FWK_MODULE_IDX_SYSTEM_POWER, + MOD_SYSTEM_POWER_API_IDX_PD_DRIVER_INPUT); + +/*! + * \} + */ + +/*! + * @} + */ + +#endif /* MOD_RCAR_SYSTEM_POWER_H */ diff --git a/product/rcar/module/rcar_system_power/include/mod_system_power.h b/product/rcar/module/rcar_system_power/include/mod_system_power.h new file mode 100644 index 000000000..314af9d6f --- /dev/null +++ b/product/rcar/module/rcar_system_power/include/mod_system_power.h @@ -0,0 +1,15 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef MOD_SYSTEM_POWER_H +#define MOD_SYSTEM_POWER_H + +#define FWK_MODULE_IDX_SYSTEM_POWER FWK_MODULE_IDX_RCAR_SYSTEM_POWER + +#include "mod_rcar_system_power.h" + +#endif /* MOD_SYSTEM_POWER_H */ diff --git a/product/rcar/module/rcar_system_power/src/Makefile b/product/rcar/module/rcar_system_power/src/Makefile new file mode 100644 index 000000000..38f074629 --- /dev/null +++ b/product/rcar/module/rcar_system_power/src/Makefile @@ -0,0 +1,11 @@ +# +# Renesas SCP/MCP Software +# Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# + +BS_LIB_NAME := RCAR SYSTEM POWER +BS_LIB_SOURCES = mod_rcar_system_power.c + +include $(BS_DIR)/lib.mk diff --git a/product/rcar/module/rcar_system_power/src/mod_rcar_system_power.c b/product/rcar/module/rcar_system_power/src/mod_rcar_system_power.c new file mode 100644 index 000000000..aedf90623 --- /dev/null +++ b/product/rcar/module/rcar_system_power/src/mod_rcar_system_power.c @@ -0,0 +1,231 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2015-2020, Arm Limited and Contributors. All rights reserved. + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Module context */ +struct system_power_ctx { + /* Log API pointer */ + const struct mod_log_api *log_api; + + /* SYS0 power domain driver API pointer */ + const struct mod_pd_driver_api *sys0_api; + + /* SYS1 power domain driver API pointer*/ + const struct mod_pd_driver_api *sys1_api; + + /* Pointer to array of extended PPU power domain driver APIs */ + const struct mod_pd_driver_api *ext_ppu_apis; + + /* Power domain module restricted API pointer */ + const struct mod_pd_restricted_api *mod_pd_restricted_api; + + /* Power domain module driver input API pointer */ + const struct mod_pd_driver_input_api *mod_pd_driver_input_api; + + /* Driver API pointer */ + const struct mod_system_power_driver_api *driver_api; + + /* Power domain module identifier of the system power domain */ + fwk_id_t mod_pd_system_id; + + /* Current system-level power state */ + unsigned int state; + + /* Pointer to module config */ + const struct mod_system_power_config *config; +}; + +struct system_power_isr { + unsigned int interrupt; + void (*handler)(void); +}; + +static struct system_power_ctx system_power_ctx; + +/* + * Functions fulfilling the Power Domain module's driver API + */ + +static int system_power_set_state(fwk_id_t pd_id, unsigned int state) +{ + return FWK_SUCCESS; +} + +static int system_power_get_state(fwk_id_t pd_id, unsigned int *state) +{ + *state = system_power_ctx.state; + + return FWK_SUCCESS; +} + +static int system_power_reset(fwk_id_t pd_id) +{ + return FWK_E_SUPPORT; +} + +static int system_power_shutdown(fwk_id_t pd_id, + enum mod_pd_system_shutdown system_shutdown) +{ + int status; + + status = system_power_set_state(pd_id, MOD_PD_STATE_OFF); + if (status != FWK_SUCCESS) + return status; + + return system_power_ctx.driver_api->system_shutdown(system_shutdown); +} + +static void soc_wakeup_handler(void) +{ + int status; + fwk_id_t pd_id = FWK_ID_ELEMENT(FWK_MODULE_IDX_POWER_DOMAIN, 0); + uint32_t state = MOD_PD_COMPOSITE_STATE(MOD_PD_LEVEL_2, + 0, + MOD_PD_STATE_ON, + MOD_PD_STATE_ON, + MOD_PD_STATE_ON); + + status = + system_power_ctx.mod_pd_restricted_api->set_state_async( + pd_id, false, state); + assert(status == FWK_SUCCESS); + (void)status; +} + +static const struct mod_pd_driver_api system_power_power_domain_driver_api = { + .set_state = system_power_set_state, + .get_state = system_power_get_state, + .reset = system_power_reset, + .shutdown = system_power_shutdown +}; + +/* + * Functions fulfilling the Power Domain module's driver input API + */ + +static int system_power_report_power_state_transition(fwk_id_t module_id, + unsigned int state) +{ + int status; + unsigned int sys0_state, sys1_state; + + system_power_ctx.sys0_api->get_state(system_power_ctx.config->ppu_sys0_id, + &sys0_state); + system_power_ctx.sys1_api->get_state(system_power_ctx.config->ppu_sys1_id, + &sys1_state); + + if ((sys0_state == MOD_PD_STATE_ON) && (sys1_state == MOD_PD_STATE_ON)) + system_power_ctx.state = MOD_PD_STATE_ON; + else if ((sys0_state == MOD_PD_STATE_OFF) && + (sys1_state == MOD_PD_STATE_ON)) + system_power_ctx.state = MOD_SYSTEM_POWER_POWER_STATE_SLEEP0; + else + system_power_ctx.state = MOD_PD_STATE_OFF; + + status = + system_power_ctx.mod_pd_driver_input_api->report_power_state_transition( + system_power_ctx.mod_pd_system_id, system_power_ctx.state); + assert(status == FWK_SUCCESS); + (void)status; + + return FWK_SUCCESS; +} + +static const struct mod_pd_driver_input_api + system_power_power_domain_driver_input_api = { + .report_power_state_transition = system_power_report_power_state_transition +}; + +/* + * Functions fulfilling the framework's module interface + */ + +static int system_power_mod_init(fwk_id_t module_id, + unsigned int unused, + const void *data) +{ + assert(data != NULL); + + system_power_ctx.config = data; + system_power_ctx.mod_pd_system_id = FWK_ID_NONE; + + if (system_power_ctx.config->ext_ppus_count > 0) { + system_power_ctx.ext_ppu_apis = fwk_mm_calloc( + system_power_ctx.config->ext_ppus_count, + sizeof(system_power_ctx.ext_ppu_apis[0])); + if (system_power_ctx.ext_ppu_apis == NULL) + return FWK_E_NOMEM; + } + + if (system_power_ctx.config->soc_wakeup_irq != FWK_INTERRUPT_NONE) { + return fwk_interrupt_set_isr(system_power_ctx.config->soc_wakeup_irq, + soc_wakeup_handler); + } else + return FWK_SUCCESS; +} + +static int system_power_bind(fwk_id_t id, unsigned int round) +{ + int status; + + status = fwk_module_bind(system_power_ctx.config->driver_id, + system_power_ctx.config->driver_api_id, &system_power_ctx.driver_api); + if (status != FWK_SUCCESS) + return status; + + status = fwk_module_bind(fwk_module_id_power_domain, + mod_pd_api_id_restricted, + &system_power_ctx.mod_pd_restricted_api); + if (status != FWK_SUCCESS) + return status; + + return FWK_SUCCESS; +} + +static int system_power_process_bind_request(fwk_id_t requester_id, + fwk_id_t pd_id, fwk_id_t api_id, + const void **api) +{ + if (fwk_id_is_equal(api_id, mod_system_power_api_id_pd_driver)) { + + if (!fwk_id_is_equal(fwk_id_build_module_id(requester_id), + fwk_module_id_power_domain)) + return FWK_E_ACCESS; + + *api = &system_power_power_domain_driver_api; + system_power_ctx.mod_pd_system_id = requester_id; + } else { + if (!fwk_id_is_equal(requester_id, + system_power_ctx.config->ppu_sys0_id) && + !fwk_id_is_equal(requester_id, + system_power_ctx.config->ppu_sys1_id)) + return FWK_E_ACCESS; + *api = &system_power_power_domain_driver_input_api; + } + + return FWK_SUCCESS; +} + +const struct fwk_module module_rcar_system_power = { + .name = "SYSTEM_POWER", + .type = FWK_MODULE_TYPE_DRIVER, + .api_count = MOD_SYSTEM_POWER_API_COUNT, + .init = system_power_mod_init, + .bind = system_power_bind, + .process_bind_request = system_power_process_bind_request, +}; diff --git a/product/rcar/scp_ramfw/config_rcar_system_power.c b/product/rcar/scp_ramfw/config_rcar_system_power.c new file mode 100644 index 000000000..1ae5fffd2 --- /dev/null +++ b/product/rcar/scp_ramfw/config_rcar_system_power.c @@ -0,0 +1,23 @@ +/* + * Renesas SCP/MCP Software + * Copyright (c) 2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include +#include +#include +#include + +const struct fwk_module_config config_rcar_system_power = { + .data = &((struct mod_system_power_config) { + .soc_wakeup_irq = FWK_INTERRUPT_NONE, + .driver_id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_RCAR_SYSTEM), + .driver_api_id = FWK_ID_API_INIT(FWK_MODULE_IDX_RCAR_SYSTEM, + MOD_RCAR_SYSTEM_API_IDX_SYSTEM_POWER_DRIVER) + }) +}; -- GitLab From 524c91bb6f74ec73472644860276258efc18d586 Mon Sep 17 00:00:00 2001 From: jimqui01 <54316584+jimqui01@users.noreply.github.com> Date: Mon, 28 Sep 2020 15:26:39 +0100 Subject: [PATCH 17/19] arm: Arm toolchain does not support S-Record format The armclang toolchain does not support S-Records. --- tools/build_system/firmware.mk | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tools/build_system/firmware.mk b/tools/build_system/firmware.mk index 17fe2094b..427c1b392 100644 --- a/tools/build_system/firmware.mk +++ b/tools/build_system/firmware.mk @@ -84,13 +84,18 @@ TARGET := $(BIN_DIR)/$(FIRMWARE) TARGET_BIN := $(TARGET).bin TARGET_ELF := $(TARGET).elf TARGET_SREC := $(TARGET).srec +ifeq ($(BS_LINKER),ARM) +TARGET_GOAL := $(TARGET_BIN) +else +TARGET_GOAL := $(TARGET_SREC) +endif vpath %.c $(FIRMWARE_DIR) vpath %.S $(FIRMWARE_DIR) vpath %.c $(PRODUCT_DIR)/src vpath %.S $(PRODUCT_DIR)/src -goal: $(TARGET_SREC) +goal: $(TARGET_GOAL) ifneq ($(BS_ARCH_CPU),host) ifeq ($(BS_LINKER),ARM) -- GitLab From 93bc503e4aeeb27ccd1b4df5e401dd9cacd70588 Mon Sep 17 00:00:00 2001 From: Nicolas Royer Date: Mon, 5 Oct 2020 01:32:17 +0200 Subject: [PATCH 18/19] tools: add rcar platform support Signed-off-by: Nicolas Royer --- tools/check_api.py | 1 + tools/check_copyright.py | 12 ++++++++---- tools/check_spacing.py | 1 + tools/check_tabs.py | 1 + 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/tools/check_api.py b/tools/check_api.py index fc64f2ed7..ad29549f9 100755 --- a/tools/check_api.py +++ b/tools/check_api.py @@ -30,6 +30,7 @@ EXCLUDE_DIRECTORIES = [ 'build', 'tools', 'cmsis', + 'product/rcar/src/CMSIS-FreeRTOS', ] # diff --git a/tools/check_copyright.py b/tools/check_copyright.py index bba57264c..52dd94fa1 100755 --- a/tools/check_copyright.py +++ b/tools/check_copyright.py @@ -27,6 +27,7 @@ EXCLUDE_DIRECTORIES = [ '.git', 'build', 'cmsis', + 'product/rcar/src/CMSIS-FreeRTOS', ] # @@ -47,17 +48,20 @@ FILE_TYPES = [ # Supported comment styles (Python regex) # COMMENT_PATTERN = '^(( \\*)|(;)|(\\#))' +COMPANY_PATTERN = '(Arm|Renesas)' +COMPANY_FULL_NAME_PATTERN = \ + '(Arm Limited and Contributors|Renesas Electronics Corporation)' # # License pattern to match # LICENSE_PATTERN = \ - '{0} Arm SCP/MCP Software$\n'\ - '{0} Copyright \\(c\\) (?P[0-9]{{4}}(-[0-9]{{4}})?), Arm Limited '\ - 'and Contributors. All rights reserved.$\n'\ + '{0} {1} SCP/MCP Software$\n'\ + '({0} Copyright \\(c\\) (?P[0-9]{{4}}(-[0-9]{{4}})?), {2}.'\ + ' All rights reserved.$\n)+'\ '{0}$\n'\ '{0} SPDX-License-Identifier: BSD-3-Clause$\n'\ - .format(COMMENT_PATTERN) + .format(COMMENT_PATTERN, COMPANY_PATTERN, COMPANY_FULL_NAME_PATTERN) # # The number of lines from the beginning of the file to search for the diff --git a/tools/check_spacing.py b/tools/check_spacing.py index 21d228005..e5910d668 100755 --- a/tools/check_spacing.py +++ b/tools/check_spacing.py @@ -25,6 +25,7 @@ EXCLUDE_DIRECTORIES = [ 'build', 'tools', 'cmsis', + 'product/rcar/src/CMSIS-FreeRTOS', ] # diff --git a/tools/check_tabs.py b/tools/check_tabs.py index 70f21f1a7..9e327ee34 100755 --- a/tools/check_tabs.py +++ b/tools/check_tabs.py @@ -24,6 +24,7 @@ EXCLUDE_DIRECTORIES = [ '.git', 'build', 'cmsis', + 'product/rcar/src/CMSIS-FreeRTOS', ] # -- GitLab From 0f5e2c542ac9ac77b330471b4e4fa0c54ecf1dd4 Mon Sep 17 00:00:00 2001 From: Nicolas Royer Date: Mon, 5 Oct 2020 01:55:48 +0200 Subject: [PATCH 19/19] misc: Add maintainer for rcar platform Signed-off-by: Nicolas Royer --- .github/CODEOWNERS | 1 + maintainers.md | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 7bbcfc6ba..f354af39f 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -12,3 +12,4 @@ /product/rddaniel*/ thomas.abraham@arm.com /product/synquacer/ masahisa.kojima@linaro.org /product/tc0/ tushar.khandelwal@arm.com +/product/rcar/ nroyer@baylibre.com diff --git a/maintainers.md b/maintainers.md index 3243f7da6..b1565ca37 100644 --- a/maintainers.md +++ b/maintainers.md @@ -55,3 +55,7 @@ maintainers. The platforms listed here represent the exceptions to this rule. #### SynQuacer (synquacer) - [Masahisa Kojima](https://github.com/masahisak) (masahisa.kojima@linaro.org) + +#### R-Car (rcar) + +- [Nicolas Royer](https://github.com/n-royer) (nroyer@baylibre.com) -- GitLab