diff --git a/Makefile b/Makefile index 8908849d50..45fce862be 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ unexport MAKEFILE_LIST # Automatically delete corrupt targets (file updated but recipe exits with a # nonzero status). Useful since a few recipes use shell redirection. +# adding other comment .DELETE_ON_ERROR: include mk/macros.mk diff --git a/core/arch/arm/plat-qcom/conf.mk b/core/arch/arm/plat-qcom/conf.mk index dddecb407f..0ae6b3fa3a 100644 --- a/core/arch/arm/plat-qcom/conf.mk +++ b/core/arch/arm/plat-qcom/conf.mk @@ -16,31 +16,15 @@ $(call force,CFG_CRYPTO_WITH_CE,y) ta-targets = ta_arm64 supported-ta-targets ?= ta_arm64 -ifneq (,$(filter $(PLATFORM_FLAVOR),kodiak lemans)) -include core/arch/arm/cpu/cortex-armv8-0.mk -$(call force,CFG_TEE_CORE_NB_CORE,8) +# Architecture family mapping +HOYA_ARCH_CHIPSETS := kodiak lemans -$(call force,CFG_QCOM_RAMBLUR_PIMEM_V3,y) -CFG_QCOM_RAMBLUR_TA_WINDOW_ID ?= 2 - -$(call force,CFG_QCOM_PRNG,y) - -CFG_TZDRAM_START ?= 0x1c300000 -CFG_TEE_RAM_VA_SIZE ?= 0x200000 -CFG_TA_RAM_VA_SIZE ?= 0x1c00000 -CFG_TZDRAM_SIZE ?= (CFG_TEE_RAM_VA_SIZE + CFG_TA_RAM_VA_SIZE) -CFG_NUM_THREADS ?= 8 -endif - -ifneq (,$(filter $(PLATFORM_FLAVOR),kodiak)) -CFG_DRIVERS_CLK ?= y -CFG_QCOM_PAS_PTA ?= y -endif - -ifeq ($(CFG_QCOM_PAS_PTA),y) -# Increase late mappings to cover all PAS resources -CFG_RESERVED_VASPACE_SIZE ?= (60 * 1024 * 1024) -$(call force,CFG_DRIVERS_QCOM_CLK,y,Mandated by CFG_QCOM_PAS_PTA) -CFG_IN_TREE_EARLY_TAS += qcom_pas/cff7d191-7ca0-4784-af13-48223b9a4fbe +ifneq (,$(filter $(PLATFORM_FLAVOR),$(HOYA_ARCH_CHIPSETS))) +QCOM_ARCH_FAMILY := hoya +else +$(error Unsupported PLATFORM_FLAVOR: $(PLATFORM_FLAVOR)) endif +# Include architecture-specific configuration +include core/arch/arm/plat-qcom/$(QCOM_ARCH_FAMILY)/arch.mk +include core/arch/arm/plat-qcom/$(QCOM_ARCH_FAMILY)/$(PLATFORM_FLAVOR)/target.mk diff --git a/core/arch/arm/plat-qcom/diag_log.c b/core/arch/arm/plat-qcom/diag_log.c new file mode 100644 index 0000000000..0594adc13b --- /dev/null +++ b/core/arch/arm/plat-qcom/diag_log.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "diag_log.h" + +register_phys_mem_pgdir(MEM_AREA_IO_SEC, DIAG_BASE, DIAG_SIZE); +register_phys_mem_pgdir(MEM_AREA_IO_SEC, + (DIAG_LOG_START_INFO & ~SMALL_PAGE_MASK), + SMALL_PAGE_SIZE); +register_phys_mem_pgdir(MEM_AREA_IO_SEC, + (TCSR_BOOT_MISC_DETECT & ~SMALL_PAGE_MASK), + SMALL_PAGE_SIZE); + +#define DIAG_VERSION_V1 1 +#define DIAG_MAGIC_INIT 0x47414944 +#define DIAG_MAGIC_FAILED 0xDEADBEEF +#define DIAG_MAGIC_DLOAD 0xD15AB1ED +#define DLOAD_MAGIC_COOKIE 0x10 + +struct diag_hdr { + uint32_t version; + uint32_t magic; +}; + +struct diag_conf { + uint32_t buf_offset; + uint32_t buf_size; +}; + +struct circ_wo_buf { + uint32_t wrap; + uint32_t head; + uint8_t buf[]; +}; + +struct diag { + struct diag_hdr hdr; + struct diag_conf conf; + struct circ_wo_buf wo_cbuf; +}; + +static struct diag *global_diag; + +static struct diag *get_diag_region(void) +{ + struct diag *diag; + uint32_t *tcsr_reg; + + tcsr_reg = phys_to_virt(TCSR_BOOT_MISC_DETECT, MEM_AREA_IO_SEC, + sizeof(uint32_t)); + diag = phys_to_virt(DIAG_BASE, MEM_AREA_IO_SEC, DIAG_SIZE); + + if (!tcsr_reg || !diag) { + EMSG("DIAG: Failed to map regions"); + return NULL; + } + + if (io_read32((vaddr_t)tcsr_reg) == DLOAD_MAGIC_COOKIE) { + diag->hdr.magic = DIAG_MAGIC_DLOAD; + dsb(); + return NULL; + } + + return diag; +} + +void qcom_diag_log_init(void) +{ + struct diag *diag; + uint32_t *diag_info_addr; + + if (!IS_ENABLED(CFG_QCOM_DIAG_LOG)) { + IMSG("DIAG: Feature not available"); + return; + } + + diag = get_diag_region(); + if (!diag) + return; + + memset(diag, 0, DIAG_SIZE); + + diag->hdr.version = DIAG_VERSION_V1; + diag->hdr.magic = DIAG_MAGIC_INIT; + diag->conf.buf_offset = offsetof(struct diag, wo_cbuf.buf); + diag->conf.buf_size = ROUNDDOWN2(DIAG_SIZE - diag->conf.buf_offset, 16); + + if (diag->conf.buf_offset >= DIAG_SIZE || diag->conf.buf_size == 0) { + EMSG("DIAG: Invalid buffer configuration (offset=%u, size=%u)", + diag->conf.buf_offset, diag->conf.buf_size); + diag->hdr.magic = DIAG_MAGIC_FAILED; + return; + } + + diag_info_addr = phys_to_virt(DIAG_LOG_START_INFO, MEM_AREA_IO_SEC, + 2 * sizeof(uint32_t)); + if (diag_info_addr) { + io_write32((vaddr_t)&diag_info_addr[0], DIAG_BASE); + io_write32((vaddr_t)&diag_info_addr[1], DIAG_SIZE); + } + + dsb(); + + global_diag = diag; +} + +void qcom_diag_log_puts(const char *str) +{ + struct diag *diag = global_diag; + const char *p; + + if (!diag || !str) + return; + + for (p = str; *p; p++) { + diag->wo_cbuf.buf[diag->wo_cbuf.head++] = *p; + if (diag->wo_cbuf.head >= diag->conf.buf_size) { + diag->wo_cbuf.head = 0; + if (diag->wo_cbuf.wrap < UINT32_MAX) + diag->wo_cbuf.wrap++; + } + } +} diff --git a/core/arch/arm/plat-qcom/diag_log.h b/core/arch/arm/plat-qcom/diag_log.h new file mode 100644 index 0000000000..c92bb8ebc3 --- /dev/null +++ b/core/arch/arm/plat-qcom/diag_log.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __DIAG_LOG_H +#define __DIAG_LOG_H + +#include +#include +#include +#include + +#define IMEM_DIAG_OFFSET UL(0x720) +#define DIAG_LOG_START_INFO (IMEM_BASE + IMEM_DIAG_OFFSET) +#define TCSR_BOOT_MISC_DETECT UL(0x1FD3000) +#define DIAG_SIZE UL(0x3000) +#define DIAG_BASE (IMEM_BASE + IMEM_SIZE - DIAG_SIZE) + +void qcom_diag_log_init(void); +void qcom_diag_log_puts(const char *str); + +#endif /* __DIAG_LOG_H */ diff --git a/core/arch/arm/plat-qcom/hoya/arch.mk b/core/arch/arm/plat-qcom/hoya/arch.mk new file mode 100644 index 0000000000..9ee039dde0 --- /dev/null +++ b/core/arch/arm/plat-qcom/hoya/arch.mk @@ -0,0 +1,15 @@ +# HOYA architecture configuration + +include core/arch/arm/cpu/cortex-armv8-0.mk +$(call force,CFG_TEE_CORE_NB_CORE,8) + +$(call force,CFG_QCOM_RAMBLUR_PIMEM_V3,y) +CFG_QCOM_RAMBLUR_TA_WINDOW_ID ?= 2 + +$(call force,CFG_QCOM_PRNG,y) + +CFG_TZDRAM_START ?= 0x1c300000 +CFG_TEE_RAM_VA_SIZE ?= 0x200000 +CFG_TA_RAM_VA_SIZE ?= 0x1c00000 +CFG_TZDRAM_SIZE ?= (CFG_TEE_RAM_VA_SIZE + CFG_TA_RAM_VA_SIZE) +CFG_NUM_THREADS ?= 8 diff --git a/core/arch/arm/plat-qcom/hoya/arch_config.h b/core/arch/arm/plat-qcom/hoya/arch_config.h new file mode 100644 index 0000000000..b0459613b8 --- /dev/null +++ b/core/arch/arm/plat-qcom/hoya/arch_config.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2024, Linaro Limited + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef ARCH_CONFIG_H +#define ARCH_CONFIG_H + +#define GICD_BASE UL(0x17a00000) +#define GICR_BASE UL(0x17a60000) + +#define RAMBLUR_PIMEM_REG_BASE UL(0x610000) +#define SEC_PRNG_REG_BASE UL(0x010D1000) + +#define AOP_MSG_RAM_BASE UL(0x0C300000) +#define AOP_MSG_RAM_SIZE UL(0x00100000) + +#define RPMH_BASE_ADDR UL(0x18200000) +#define RPMH_RSC_SIZE UL(0x40000) + +#define SECURITY_CONTROL_BASE UL(0x00780000) +#define SECURITY_CONTROL_SIZE UL(0x10000) + +#define TCSR_MUTEX_BASE UL(0x01F40000) +#define TCSR_MUTEX_SIZE UL(0x40000) + +#endif /* ARCH_CONFIG_H */ diff --git a/core/arch/arm/plat-qcom/hoya/kodiak/target.mk b/core/arch/arm/plat-qcom/hoya/kodiak/target.mk new file mode 100644 index 0000000000..b91c1b0146 --- /dev/null +++ b/core/arch/arm/plat-qcom/hoya/kodiak/target.mk @@ -0,0 +1,21 @@ +CFG_DRIVERS_CLK ?= y +CFG_DRIVERS_QCOM_CLK ?= y + +CFG_QCOM_DIAG_LOG ?= $(if $(filter y,$(CFG_TEE_CORE_DEBUG)),y,n) + +CFG_QCOM_QFPROM_FUSEPROV ?= $(if $(filter y,$(CFG_INSECURE)),n,y) + +_qcom_fuseprov_deps = $(if $(filter y,$(CFG_QCOM_QFPROM_FUSEPROV)),y,n) +CFG_QCOM_CMD_DB ?= $(_qcom_fuseprov_deps) +CFG_QCOM_RPMH_CLIENT ?= $(_qcom_fuseprov_deps) +CFG_QCOM_QFPROM ?= $(_qcom_fuseprov_deps) + +CFG_QCOM_PAS_PTA ?= y +# Kodiak requires MX voltage rail workaround for QFPROM fuse blowing +$(call force,CFG_QFPROM_MX_RAIL_WA,y) + +ifeq ($(CFG_QCOM_PAS_PTA),y) +# Increase late mappings to cover all PAS resources +CFG_RESERVED_VASPACE_SIZE ?= (60 * 1024 * 1024) +CFG_IN_TREE_EARLY_TAS += qcom_pas/cff7d191-7ca0-4784-af13-48223b9a4fbe +endif diff --git a/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h b/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h new file mode 100644 index 0000000000..f498d0bc59 --- /dev/null +++ b/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2024, Linaro Limited + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef TARGET_CONFIG_H +#define TARGET_CONFIG_H + +#define GCC_BASE UL(0x100000) +#define GCC_SIZE UL(0x100000) + +#define CFG_SEC_ELF_DDR_ADDR UL(0x808FF000) +#define CFG_SEC_ELF_DDR_SIZE UL(0x1000) + +#define DRAM0_BASE UL(0x80000000) +#define DRAM0_SIZE UL(0x80000000) +#define DRAM1_BASE ULL(0x100000000) +#define DRAM1_SIZE ULL(0x100000000) + +#define RAMBLUR_PIMEM_VAULT_TA_BASE ULL(0xc1800000) +#define RAMBLUR_PIMEM_VAULT_TA_SIZE ULL(0x01c00000) + +#define GENI_UART_REG_BASE UL(0x994000) + +#define IMEM_BASE UL(0x14680000) +#define IMEM_SIZE UL(0x19000) + +#define WPSS_BASE UL(0x8a00000) +#define WPSS_SIZE UL(0x200000) +#define TURING_BASE UL(0x09800000) +#define TURING_SIZE ULL(0x00e00000) +#define LPASS_BASE UL(0x02c00000) +#define LPASS_SIZE ULL(0x01080000) +#define IRIS_BASE UL(0x0aa00000) +#define IRIS_SIZE ULL(0x00200000) + +#define PAS_ID_QDSP6 1 +#define PAS_ID_WPSS 6 +#define PAS_ID_VENUS 9 +#define PAS_ID_TURING 18 + +#endif /* TARGET_CONFIG_H */ diff --git a/core/arch/arm/plat-qcom/hoya/lemans/target.mk b/core/arch/arm/plat-qcom/hoya/lemans/target.mk new file mode 100644 index 0000000000..f645897e90 --- /dev/null +++ b/core/arch/arm/plat-qcom/hoya/lemans/target.mk @@ -0,0 +1,11 @@ +CFG_DRIVERS_CLK ?= y +CFG_DRIVERS_QCOM_CLK ?= y + +CFG_QCOM_DIAG_LOG ?= $(if $(filter y,$(CFG_TEE_CORE_DEBUG)),y,n) + +CFG_QCOM_QFPROM_FUSEPROV ?= $(if $(filter y,$(CFG_INSECURE)),n,y) + +_qcom_fuseprov_deps = $(if $(filter y,$(CFG_QCOM_QFPROM_FUSEPROV)),y,n) +CFG_QCOM_CMD_DB ?= $(_qcom_fuseprov_deps) +CFG_QCOM_RPMH_CLIENT ?= $(_qcom_fuseprov_deps) +CFG_QCOM_QFPROM ?= $(_qcom_fuseprov_deps) diff --git a/core/arch/arm/plat-qcom/hoya/lemans/target_config.h b/core/arch/arm/plat-qcom/hoya/lemans/target_config.h new file mode 100644 index 0000000000..dfc39cacf0 --- /dev/null +++ b/core/arch/arm/plat-qcom/hoya/lemans/target_config.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2024, Linaro Limited + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef TARGET_CONFIG_H +#define TARGET_CONFIG_H + +#define GCC_BASE UL(0x110000) +#define GCC_SIZE UL(0x100000) + +#define CFG_SEC_ELF_DDR_ADDR UL(0x908FF000) +#define CFG_SEC_ELF_DDR_SIZE UL(0x1000) + +#define DRAM0_BASE UL(0x80000000) +#define DRAM0_SIZE UL(0x380000000) +#define DRAM1_BASE ULL(0x800000000) +#define DRAM1_SIZE ULL(0x800000000) + +#define RAMBLUR_PIMEM_VAULT_TA_BASE ULL(0xd1900000) +#define RAMBLUR_PIMEM_VAULT_TA_SIZE ULL(0x01c00000) + +#define GENI_UART_REG_BASE UL(0xa8c000) + +#define IMEM_BASE UL(0x14680000) +#define IMEM_SIZE UL(0x32000) + +#endif /* TARGET_CONFIG_H */ diff --git a/core/arch/arm/plat-qcom/main.c b/core/arch/arm/plat-qcom/main.c index 3ed3670c22..b620c150eb 100644 --- a/core/arch/arm/plat-qcom/main.c +++ b/core/arch/arm/plat-qcom/main.c @@ -11,6 +11,8 @@ #include #include +#include "diag_log.h" + /* * Register the physical memory area for peripherals etc. Here we are * registering the UART console. @@ -28,6 +30,16 @@ register_ddr(DRAM1_BASE, DRAM1_SIZE); static struct qcom_geni_uart_data console_data; +void plat_trace_ext_puts(const char *str) +{ + qcom_diag_log_puts(str); +} + +void plat_trace_init(void) +{ + qcom_diag_log_init(); +} + void plat_console_init(void) { qcom_geni_uart_init(&console_data, GENI_UART_REG_BASE); diff --git a/core/arch/arm/plat-qcom/platform_config.h b/core/arch/arm/plat-qcom/platform_config.h index 4b10bc4706..bce35cdfdd 100644 --- a/core/arch/arm/plat-qcom/platform_config.h +++ b/core/arch/arm/plat-qcom/platform_config.h @@ -8,58 +8,13 @@ #define PLATFORM_CONFIG_H #include +#include +#include /* Make stacks aligned to data cache line length */ #define STACK_ALIGNMENT 64 -#if defined(PLATFORM_FLAVOR_kodiak) || defined(PLATFORM_FLAVOR_lemans) -/* GIC related constants */ -#define GICD_BASE UL(0x17a00000) -#define GICR_BASE UL(0x17a60000) - -#define RAMBLUR_PIMEM_REG_BASE UL(0x610000) -#define SEC_PRNG_REG_BASE UL(0x010D1000) -#endif - -#if defined(PLATFORM_FLAVOR_kodiak) -#define DRAM0_BASE UL(0x80000000) -#define DRAM0_SIZE UL(0x80000000) -#define DRAM1_BASE ULL(0x100000000) -#define DRAM1_SIZE ULL(0x100000000) - -/* DDR reserved*/ -#define RAMBLUR_PIMEM_VAULT_TA_BASE ULL(0xc1800000) -#define RAMBLUR_PIMEM_VAULT_TA_SIZE ULL(0x01c00000) - -#define GENI_UART_REG_BASE UL(0x994000) -#define GCC_BASE UL(0x100000) -#define GCC_SIZE UL(0x100000) -#define WPSS_BASE UL(0x8a00000) -#define WPSS_SIZE UL(0x200000) -#define TURING_BASE UL(0x09800000) -#define TURING_SIZE ULL(0x00e00000) -#define LPASS_BASE UL(0x02c00000) -#define LPASS_SIZE ULL(0x01080000) -#define IRIS_BASE UL(0x0aa00000) -#define IRIS_SIZE ULL(0x00200000) -#endif - -#if defined(PLATFORM_FLAVOR_lemans) -#define DRAM0_BASE UL(0x80000000) -#define DRAM0_SIZE UL(0x380000000) -#define DRAM1_BASE ULL(0x800000000) -#define DRAM1_SIZE ULL(0x800000000) - -/* DDR reserved*/ -#define RAMBLUR_PIMEM_VAULT_TA_BASE ULL(0xd1900000) -#define RAMBLUR_PIMEM_VAULT_TA_SIZE ULL(0x01c00000) - -#define GENI_UART_REG_BASE UL(0xa8c000) -#endif - -#define PAS_ID_QDSP6 1 -#define PAS_ID_WPSS 6 -#define PAS_ID_VENUS 9 -#define PAS_ID_TURING 18 +#define MAX_XLAT_TABLES (40 + (CFG_RESERVED_VASPACE_SIZE) / \ + (CORE_MMU_PGDIR_SIZE) + 5) #endif /*PLATFORM_CONFIG_H*/ diff --git a/core/arch/arm/plat-qcom/sub.mk b/core/arch/arm/plat-qcom/sub.mk index 8ddc2fd45c..c3bfe97042 100644 --- a/core/arch/arm/plat-qcom/sub.mk +++ b/core/arch/arm/plat-qcom/sub.mk @@ -1,2 +1,5 @@ global-incdirs-y += . +global-incdirs-y += $(QCOM_ARCH_FAMILY) +global-incdirs-y += $(QCOM_ARCH_FAMILY)/$(PLATFORM_FLAVOR) srcs-y += main.c +srcs-y += diag_log.c diff --git a/core/drivers/clk/qcom/clock-qcom.c b/core/drivers/clk/qcom/clock-qcom.c index 5969afcdeb..83fd63bcd1 100644 --- a/core/drivers/clk/qcom/clock-qcom.c +++ b/core/drivers/clk/qcom/clock-qcom.c @@ -34,6 +34,24 @@ TEE_Result qcom_clock_enable_cbc(vaddr_t cbcr) return TEE_SUCCESS; } +TEE_Result qcom_clock_set_rate(vaddr_t cfg_rcgr, vaddr_t cmd_rcgr, + uint32_t cfg_value) +{ + uint64_t timer = 0; + + io_write32(cfg_rcgr, cfg_value); + io_write32(cmd_rcgr, CMD_RCGR_UPDATE_BIT); + + timer = timeout_init_us(10 * 1000); + while (io_read32(cmd_rcgr) & CMD_RCGR_UPDATE_BIT) { + if (timeout_elapsed(timer)) + return TEE_ERROR_TIMEOUT; + udelay(1); + } + + return TEE_SUCCESS; +} + TEE_Result qcom_clock_enable(enum qcom_clk_group group) { switch (group) { @@ -45,6 +63,4 @@ TEE_Result qcom_clock_enable(enum qcom_clk_group group) EMSG("Unsupported clock group %d\n", group); return TEE_ERROR_BAD_PARAMETERS; } - - return TEE_SUCCESS; } diff --git a/core/drivers/clk/qcom/platform/kodiak/clock_group_qcom.h b/core/drivers/clk/qcom/platform/kodiak/clock_group_qcom.h index 7a4a930a7e..8f93ee74c8 100644 --- a/core/drivers/clk/qcom/platform/kodiak/clock_group_qcom.h +++ b/core/drivers/clk/qcom/platform/kodiak/clock_group_qcom.h @@ -5,6 +5,10 @@ #ifndef _CLOCK_GROUP_QCOM_H_ #define _CLOCK_GROUP_QCOM_H_ +#define GCC_SEC_CTRL_CFG_RCGR 0x3d030 +#define GCC_SEC_CTRL_CMD_RCGR 0x3d02c +#define QFPROM_CLOCK_DIVIDE 0x7 + #define GCC_WPSS_AHB_CLK 0x9d154 #define GCC_WPSS_AHB_BDG_MST_CLK 0x9d158 #define GCC_WPSS_RSCP_CLK 0x9d16c @@ -34,4 +38,3 @@ #define TOP_CC_AGGNOC_MPU_LS_CLK 0x7000 #endif /* _CLOCK_GROUP_QCOM_H_ */ - diff --git a/core/drivers/clk/qcom/platform/lemans/clock_group_qcom.h b/core/drivers/clk/qcom/platform/lemans/clock_group_qcom.h new file mode 100644 index 0000000000..b29f8aa723 --- /dev/null +++ b/core/drivers/clk/qcom/platform/lemans/clock_group_qcom.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ +#ifndef _CLOCK_GROUP_QCOM_H_ +#define _CLOCK_GROUP_QCOM_H_ + +#define GCC_SEC_CTRL_CFG_RCGR 0x39038 +#define GCC_SEC_CTRL_CMD_RCGR 0x39034 +#define QFPROM_CLOCK_DIVIDE 0x7 + +#endif /* _CLOCK_GROUP_QCOM_H_ */ diff --git a/core/drivers/clk/qcom/sub.mk b/core/drivers/clk/qcom/sub.mk index f7b59c9a48..5e8b39c304 100644 --- a/core/drivers/clk/qcom/sub.mk +++ b/core/drivers/clk/qcom/sub.mk @@ -1,5 +1,5 @@ global-incdirs-y += . +global-incdirs-y += platform/$(PLATFORM_FLAVOR) srcs-y += clock-qcom.c clock-qcom-pas.c incdirs-y += . -incdirs-$(CFG_QCOM_PAS_PTA) += platform/$(PLATFORM_FLAVOR) diff --git a/core/drivers/qcom/cmd_db/cmd_db.c b/core/drivers/qcom/cmd_db/cmd_db.c new file mode 100644 index 0000000000..70eac23313 --- /dev/null +++ b/core/drivers/qcom/cmd_db/cmd_db.c @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cmd_db_target_config.h" + +register_phys_mem_pgdir(MEM_AREA_IO_NSEC, AOP_MSG_RAM_BASE, + CORE_MMU_PGDIR_SIZE); + +#define CMD_DB_MAGIC_NUM 0x0c0330db +#define CMD_DB_VER 0x00000001 + +#define CMD_DB_SLV_ID_ACTUAL 3 +#define CMD_DB_SLV_ID_RESERVE 5 +#define CMD_DB_MAX_SLV_ID (CMD_DB_SLV_ID_ACTUAL + \ + CMD_DB_SLV_ID_RESERVE) + +#define CMD_DB_ADDR_SLV_ID_SHIFT 16 +#define CMD_DB_ADDR_SLV_ID_MASK 0xf + +#define CMD_DB_RES_ID_MAX_CHARS 8 +#define CMD_DB_CHAR_BITS 8 + +#define CMD_DB_PRIORITY_LOW_DRV_MAX 15 +#define CMD_DB_PRIORITY_HIGH_DRV_MAX 31 +#define CMD_DB_PRIORITY_HIGH_DRV_OFFSET 16 +#define PRIORITY_BITS_PER_DRV 2 +#define PRIORITY_MASK 0x3 +#define MAX_DRV_ID CMD_DB_PRIORITY_HIGH_DRV_MAX + +#define ADDR_SLV_ID(id) \ + ((enum cmd_db_slv_id_type)(((id) >> CMD_DB_ADDR_SLV_ID_SHIFT) & \ + CMD_DB_ADDR_SLV_ID_MASK)) + +enum cmd_db_slv_id_type { + CMD_DB_SLV_ID_INVALID = 0, + CMD_DB_SLV_ID_VALID_LOW = 3, + CMD_DB_SLV_ID_ARC = CMD_DB_SLV_ID_VALID_LOW, + CMD_DB_SLV_ID_VRM = 4, + CMD_DB_SLV_ID_BCM = 5, + CMD_DB_SLV_ID_VALID_HIGH = CMD_DB_SLV_ID_BCM, +}; + +enum cmd_db_query_type { + CMD_DB_QUERY_RES_ID = 0, + CMD_DB_QUERY_ADDRESS, + CMD_DB_QUERY_INVALID, +}; + +struct cmd_db_query_result_type { + char res_id[CMD_DB_MAX_RES_ID_LEN + 1]; + uint32_t addr; + uint32_t priority[CMD_DB_DRV_ID_PRIORITY_SZ]; + uint32_t len; + uint16_t version; +}; + +struct cmd_db_query_info { + char res_id[CMD_DB_MAX_RES_ID_LEN + 1]; + uint32_t addr; + enum cmd_db_query_type type; + enum cmd_db_slv_id_type slv_id; +}; + +struct entry_header { + uint64_t res_id; + uint32_t priority[CMD_DB_DRV_ID_PRIORITY_SZ]; + uint32_t addr; + uint16_t len; + uint16_t offset; +}; + +struct slv_id_info { + uint16_t slv_id; + uint16_t header_offset; + uint16_t data_offset; + uint16_t cnt; + uint16_t version; + uint16_t reserved[3]; +}; + +struct db_header { + uint32_t version; + uint32_t magic_num; + struct slv_id_info slv_id_info[CMD_DB_MAX_SLV_ID]; + uint32_t check_sum; + uint32_t reserved; + uint8_t data[]; +}; + +struct cmd_db_data { + struct db_header *data; + size_t size; + struct mutex lock; /* Protects CMD_DB data access */ +}; + +static struct cmd_db_data query_db = { + .lock = MUTEX_INITIALIZER, +}; + +static uint64_t conv_str_to_uint64(const char *res_id) +{ + uint64_t value = 0; + size_t i, len; + + len = strnlen(res_id, CMD_DB_RES_ID_MAX_CHARS + 1); + if (len == 0 || len > CMD_DB_RES_ID_MAX_CHARS) + return 0; + + for (i = 0; i < len; i++) + value |= ((uint64_t)res_id[i]) << (CMD_DB_CHAR_BITS * i); + + return value; +} + +static bool is_valid_slv_id(uint32_t slv_id) +{ + return (slv_id >= CMD_DB_SLV_ID_VALID_LOW && + slv_id <= CMD_DB_SLV_ID_VALID_HIGH); +} + +static bool is_valid_res_id(const char *res_id) +{ + if (!res_id || !res_id[0]) + return false; + + return (strnlen(res_id, CMD_DB_RES_ID_MAX_CHARS + 1) <= + CMD_DB_RES_ID_MAX_CHARS); +} + +static TEE_Result get_config(paddr_t *base_addr, size_t *size_out) +{ + struct cmd_db_target_config config = { }; + struct aop_cmd_db_metadata *metadata = NULL; + + config = cmd_db_get_target_config(); + + if (!config.aop_metadata_addr || !config.aop_metadata_size) + return TEE_ERROR_BAD_PARAMETERS; + + metadata = phys_to_virt(config.aop_metadata_addr, MEM_AREA_IO_NSEC, + config.aop_metadata_size); + if (!metadata) { + EMSG("Failed to get VA for AOP metadata at PA 0x%lx", + (unsigned long)config.aop_metadata_addr); + return TEE_ERROR_GENERIC; + } + + *base_addr = metadata->addr; + *size_out = metadata->size; + + return TEE_SUCCESS; +} + +static TEE_Result cmd_db_init(void) +{ + paddr_t pa = 0; + size_t size = 0; + TEE_Result res; + + res = get_config(&pa, &size); + if (res != TEE_SUCCESS) { + EMSG("CMD_DB: Failed to get configuration"); + goto err_panic; + } + + query_db.data = core_mmu_add_mapping(MEM_AREA_IO_NSEC, pa, size); + if (!query_db.data) { + EMSG("CMD_DB: Failed to map at PA 0x%lx", (unsigned long)pa); + goto err_panic; + } + + query_db.size = size; + + if (query_db.data->version != CMD_DB_VER || + query_db.data->magic_num != CMD_DB_MAGIC_NUM) { + EMSG("CMD_DB: Version/magic mismatch: 0x%x/0x%x exp 0x%x/0x%x", + query_db.data->version, query_db.data->magic_num, + CMD_DB_VER, CMD_DB_MAGIC_NUM); + goto err_panic; + } + + return TEE_SUCCESS; + +err_panic: + panic("CMD_DB driver initialization failed"); +} + +static TEE_Result validate_entry_bounds(struct slv_id_info *slv_info, + uint16_t entry_idx) +{ + size_t offset, bounds, entry_size; + + if (MUL_OVERFLOW(sizeof(struct entry_header), entry_idx, &entry_size) || + ADD_OVERFLOW(slv_info->header_offset, entry_size, &offset) || + ADD_OVERFLOW(sizeof(struct db_header), offset, &bounds) || + ADD_OVERFLOW(bounds, sizeof(struct entry_header), &bounds) || + bounds > query_db.size) + return TEE_ERROR_CORRUPT_OBJECT; + + return TEE_SUCCESS; +} + +static TEE_Result search_entry(struct cmd_db_query_info *query_info, + uint64_t res_id, uint16_t *slv_idx, + struct entry_header *entry) +{ + struct slv_id_info *slv_info = NULL; + bool valid; + uint16_t i, j; + TEE_Result res = TEE_SUCCESS; + + if (!query_db.data) + return TEE_ERROR_BAD_STATE; + + valid = is_valid_slv_id(query_info->slv_id); + + for (i = 0; i < CMD_DB_MAX_SLV_ID; i++) { + slv_info = &query_db.data->slv_id_info[i]; + + if (valid) { + if (slv_info->slv_id != query_info->slv_id) + continue; + } + + for (j = 0; j < slv_info->cnt; j++) { + res = validate_entry_bounds(slv_info, j); + if (res != TEE_SUCCESS) + return res; + + memcpy(entry, query_db.data->data + + slv_info->header_offset + + sizeof(struct entry_header) * j, sizeof(*entry)); + + if (query_info->type == CMD_DB_QUERY_RES_ID && + res_id == entry->res_id) + goto found; + + if (query_info->type == CMD_DB_QUERY_ADDRESS && + query_info->addr == entry->addr) + goto found; + } + } + + return TEE_ERROR_ITEM_NOT_FOUND; + +found: + *slv_idx = i; + return TEE_SUCCESS; +} + +static TEE_Result copy_aux_data(uint16_t slv_idx, struct entry_header *entry, + struct cmd_db_query_result_type *result, + uint8_t *data) +{ + struct slv_id_info *slv_info = &query_db.data->slv_id_info[slv_idx]; + uint32_t len = MIN(result->len, entry->len); + size_t offset, bounds; + + if (!data) + return TEE_ERROR_BAD_PARAMETERS; + + if (ADD_OVERFLOW(slv_info->data_offset, entry->offset, &offset) || + ADD_OVERFLOW(sizeof(struct db_header), offset, &bounds) || + ADD_OVERFLOW(bounds, len, &bounds) || + bounds > query_db.size) + return TEE_ERROR_CORRUPT_OBJECT; + + memcpy(data, query_db.data->data + slv_info->data_offset + + entry->offset, len); + result->len = len; + + return TEE_SUCCESS; +} + +static TEE_Result +cmd_db_get_entry_by_res_id(const char *res_id, + struct cmd_db_query_result_type *result, + uint8_t *data) +{ + struct cmd_db_query_info query_info = { }; + uint64_t res_id_val; + uint16_t slv_idx; + struct entry_header entry; + TEE_Result res; + + if (!res_id || !result || (!data && result->len > 0)) + return TEE_ERROR_BAD_PARAMETERS; + + if (!query_db.data) + return TEE_ERROR_BAD_STATE; + + if (!res_id[0]) + return TEE_ERROR_BAD_PARAMETERS; + + res_id_val = conv_str_to_uint64(res_id); + query_info.type = CMD_DB_QUERY_RES_ID; + strlcpy(query_info.res_id, res_id, sizeof(query_info.res_id)); + + res = search_entry(&query_info, res_id_val, &slv_idx, &entry); + if (res) + return res; + + strlcpy(result->res_id, res_id, sizeof(result->res_id)); + result->addr = entry.addr; + result->priority[0] = entry.priority[0]; + result->priority[1] = entry.priority[1]; + result->version = query_db.data->slv_id_info[slv_idx].version; + + if (result->len == 0 || entry.len == 0) { + if (entry.len > 0) + result->len = entry.len; + return TEE_SUCCESS; + } + + return copy_aux_data(slv_idx, &entry, result, data); +} + +static TEE_Result +cmd_db_get_entry_by_addr(uint32_t addr, struct cmd_db_query_result_type *result, + uint8_t *data) +{ + struct cmd_db_query_info query_info = { }; + uint16_t slv_idx; + struct entry_header entry; + uint32_t slv_id; + TEE_Result res; + + if (!result || (!data && result->len > 0)) + return TEE_ERROR_BAD_PARAMETERS; + + if (!query_db.data) + return TEE_ERROR_BAD_STATE; + + slv_id = ADDR_SLV_ID(addr); + if (!is_valid_slv_id(slv_id)) + return TEE_ERROR_BAD_PARAMETERS; + + query_info.type = CMD_DB_QUERY_ADDRESS; + query_info.addr = addr; + query_info.slv_id = slv_id; + + res = search_entry(&query_info, 0, &slv_idx, &entry); + if (res) + return res; + + result->addr = entry.addr; + result->priority[0] = entry.priority[0]; + result->priority[1] = entry.priority[1]; + result->version = query_db.data->slv_id_info[slv_idx].version; + + if (result->len == 0 || entry.len == 0) { + if (entry.len > 0) + result->len = entry.len; + return TEE_SUCCESS; + } + + return copy_aux_data(slv_idx, &entry, result, data); +} + +TEE_Result cmd_db_query_addr(const char *res_id, uint32_t *addr) +{ + struct cmd_db_query_result_type result = { }; + TEE_Result res; + + if (!addr || !is_valid_res_id(res_id)) + return TEE_ERROR_BAD_PARAMETERS; + + mutex_lock(&query_db.lock); + + if (!query_db.data) { + mutex_unlock(&query_db.lock); + return TEE_ERROR_BAD_STATE; + } + + res = cmd_db_get_entry_by_res_id(res_id, &result, NULL); + if (res == TEE_SUCCESS) + *addr = result.addr; + + mutex_unlock(&query_db.lock); + return res; +} + +static uint32_t extract_priority(struct cmd_db_query_result_type *result, + uint8_t drv_id) +{ + if (drv_id <= CMD_DB_PRIORITY_LOW_DRV_MAX) + return (result->priority[0] >> + (drv_id * PRIORITY_BITS_PER_DRV)) & PRIORITY_MASK; + + if (drv_id <= CMD_DB_PRIORITY_HIGH_DRV_MAX) + return (result->priority[1] >> + ((drv_id - CMD_DB_PRIORITY_HIGH_DRV_OFFSET) * + PRIORITY_BITS_PER_DRV)) & PRIORITY_MASK; + + return 0; +} + +TEE_Result cmd_db_query_priority(uint32_t addr, uint8_t drv_id, + uint32_t *priority) +{ + struct cmd_db_query_result_type result = { }; + TEE_Result res; + + if (!priority || drv_id > MAX_DRV_ID) + return TEE_ERROR_BAD_PARAMETERS; + + mutex_lock(&query_db.lock); + + if (!query_db.data) { + mutex_unlock(&query_db.lock); + return TEE_ERROR_BAD_STATE; + } + + res = cmd_db_get_entry_by_addr(addr, &result, NULL); + if (res == TEE_SUCCESS) + *priority = extract_priority(&result, drv_id); + + mutex_unlock(&query_db.lock); + return res; +} + +early_init(cmd_db_init); diff --git a/core/drivers/qcom/cmd_db/cmd_db_target_config.h b/core/drivers/qcom/cmd_db/cmd_db_target_config.h new file mode 100644 index 0000000000..65575728ed --- /dev/null +++ b/core/drivers/qcom/cmd_db/cmd_db_target_config.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __CMD_DB_TARGET_CONFIG_H__ +#define __CMD_DB_TARGET_CONFIG_H__ + +#include +#include +#include + +#define AOP_MSG_RAM_END (AOP_MSG_RAM_BASE + AOP_MSG_RAM_SIZE) + +#define CMD_DB_METADATA_OFFSET 0x10000 +#define CMD_DB_ADDR_FIELD_OFFSET 0xC + +#define AOP_METADATA_ADDR (AOP_MSG_RAM_END - CMD_DB_METADATA_OFFSET + \ + CMD_DB_ADDR_FIELD_OFFSET) + +struct cmd_db_target_config { + paddr_t aop_metadata_addr; + size_t aop_metadata_size; +}; + +struct aop_cmd_db_metadata { + uint32_t addr; + uint32_t size; +}; + +static inline struct cmd_db_target_config cmd_db_get_target_config(void) +{ + struct cmd_db_target_config cfg = { + .aop_metadata_addr = AOP_METADATA_ADDR, + .aop_metadata_size = sizeof(struct aop_cmd_db_metadata), + }; + return cfg; +} + +#endif /* __CMD_DB_TARGET_CONFIG_H__ */ diff --git a/core/drivers/qcom/cmd_db/sub.mk b/core/drivers/qcom/cmd_db/sub.mk new file mode 100644 index 0000000000..43b9065e00 --- /dev/null +++ b/core/drivers/qcom/cmd_db/sub.mk @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# + +srcs-y += cmd_db.c diff --git a/core/drivers/qcom/qfprom/fuseprov/fuseprov_boot.c b/core/drivers/qcom/qfprom/fuseprov/fuseprov_boot.c new file mode 100644 index 0000000000..c277fc5479 --- /dev/null +++ b/core/drivers/qcom/qfprom/fuseprov/fuseprov_boot.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sec_elf_v2.h" + +register_phys_mem(MEM_AREA_RAM_NSEC, CFG_SEC_ELF_DDR_ADDR, + CFG_SEC_ELF_DDR_SIZE); + +static TEE_Result discover_sec_elf(const uint8_t **data_out, size_t *size, + void **vaddr_out) +{ + const uint8_t *data; + const struct secdat_hdr *hdr; + void *vaddr; + + if (!data_out || !size || !vaddr_out) + return TEE_ERROR_BAD_PARAMETERS; + + vaddr = phys_to_virt(CFG_SEC_ELF_DDR_ADDR, MEM_AREA_RAM_NSEC, + CFG_SEC_ELF_DDR_SIZE); + if (!vaddr) { + EMSG("Failed to get VA for sec.elf at PA 0x%lx", + (unsigned long)CFG_SEC_ELF_DDR_ADDR); + return TEE_ERROR_GENERIC; + } + + data = (const uint8_t *)vaddr; + hdr = (const struct secdat_hdr *)data; + + if (hdr->magic1 != FUSEPROV_SECDAT_MAGIC1 || + hdr->magic2 != FUSEPROV_SECDAT_MAGIC2) { + EMSG("Invalid sec.elf magic: 0x%x/0x%x", + hdr->magic1, hdr->magic2); + return TEE_ERROR_BAD_FORMAT; + } + + *size = sizeof(*hdr) + hdr->size + TEE_SHA256_HASH_SIZE; + + if (*size > CFG_SEC_ELF_DDR_SIZE) { + EMSG("sec.elf size %zu exceeds limit %zu", + *size, (size_t)CFG_SEC_ELF_DDR_SIZE); + return TEE_ERROR_BAD_FORMAT; + } + + *data_out = data; + *vaddr_out = vaddr; + return TEE_SUCCESS; +} + +static TEE_Result execute_provisioning(void) +{ + const uint8_t *data = NULL; + void *vaddr = NULL; + size_t len = 0; + TEE_Result res; + + res = discover_sec_elf(&data, &len, &vaddr); + if (res != TEE_SUCCESS) + return res; + + res = fuseprov_execute(data, len); + + if (res != TEE_SUCCESS) { + EMSG("Fuse provisioning failed: 0x%x", res); + return res; + } + + IMSG("Fuse provisioning completed successfully"); + fuseprov_reset_device(); + return TEE_SUCCESS; +} + +service_init(execute_provisioning); diff --git a/core/drivers/qcom/qfprom/fuseprov/fuseprov_core.c b/core/drivers/qcom/qfprom/fuseprov/fuseprov_core.c new file mode 100644 index 0000000000..1ac975dfcc --- /dev/null +++ b/core/drivers/qcom/qfprom/fuseprov/fuseprov_core.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sec_elf_v2.h" + +static TEE_Result blow_fuse_region(enum fuseprov_region_type region, + const struct fuse_entry *entries, + uint32_t count) +{ + enum qfprom_error err; + uint32_t fuse_data[2]; + uint32_t current_data[2]; + uint32_t i; + uint8_t has_fec; + uint32_t mask_fec_msb_bits; + + for (i = 0; i < count; i++) { + const struct fuse_entry *entry = &entries[i]; + + if (entry->region != region || + entry->operation != FUSEPROV_OP_BLOW || + (entry->lsb_val == 0 && entry->msb_val == 0)) + continue; + + err = qfprom_row_has_fec_bits(entry->addr, + QFPROM_ADDR_SPACE_RAW, + &has_fec); + if (err != QFPROM_NO_ERR) { + EMSG("Failed to check FEC for addr 0x%08x: error=%d", + entry->addr, err); + return TEE_ERROR_GENERIC; + } + + if (has_fec) + mask_fec_msb_bits = 0x00FFFFFF; + else + mask_fec_msb_bits = 0xFFFFFFFF; + + err = qfprom_read_row(entry->addr, QFPROM_ADDR_SPACE_CORR, + current_data); + if (err != QFPROM_NO_ERR) { + EMSG("Failed to read fuse at 0x%08x: error=%d", + entry->addr, err); + return TEE_ERROR_GENERIC; + } + + /* Check if fuse bits are already blown */ + if (((current_data[0] & entry->lsb_val) == entry->lsb_val) && + ((current_data[1] & entry->msb_val) == + (entry->msb_val & mask_fec_msb_bits))) + continue; + + fuse_data[0] = entry->lsb_val; + fuse_data[1] = entry->msb_val; + + err = qfprom_write_row(entry->addr, fuse_data); + if (err != QFPROM_NO_ERR) { + EMSG("Failed to write fuse at 0x%08x: error=%d", + entry->addr, err); + return TEE_ERROR_GENERIC; + } + } + + return TEE_SUCCESS; +} + +TEE_Result fuseprov_execute(const uint8_t *data, size_t len) +{ + TEE_Result res = TEE_ERROR_GENERIC; + const struct secdat_hdr *hdr = NULL; + const struct segment_hdr *segments = NULL; + const uint8_t *seg_data = NULL; + uint32_t seg_size = 0; + const struct qfuse_list_hdr *qfuse_hdr = NULL; + const struct fuse_entry *entries = NULL; + uint32_t count = 0; + uint32_t perm_data[2]; + enum qfprom_error err; + + if (!data || len == 0) + return TEE_ERROR_BAD_PARAMETERS; + + res = sec_elf_parse(data, len, &hdr, &segments); + if (res != TEE_SUCCESS) + return res; + + res = sec_elf_validate_hash(data, len, hdr); + if (res != TEE_SUCCESS) + return res; + + res = qfprom_hw_init(); + if (res != TEE_SUCCESS) { + EMSG("Failed to initialize QFPROM hardware: 0x%x", res); + return res; + } + + err = qfprom_read_row(WRITE_PERMISSION_ADDR, QFPROM_ADDR_SPACE_CORR, + perm_data); + if (err != QFPROM_NO_ERR) { + EMSG("Failed to read WRITE_PERMISSION fuse: error=%d", err); + res = TEE_ERROR_GENERIC; + goto cleanup; + } + + if (perm_data[0] & OEM_SECURE_BOOT_PERM_MASK) { + res = TEE_SUCCESS; + goto cleanup; + } + + res = sec_elf_find_segment(data, len, + FUSEPROV_SECDAT_SEGMENT_TYPE_EFUSE, + &seg_data, &seg_size); + + if (res != TEE_SUCCESS) { + res = TEE_ERROR_BAD_FORMAT; + goto cleanup; + } + + if (!seg_data) { + res = TEE_ERROR_BAD_FORMAT; + goto cleanup; + } + + if (seg_size < sizeof(struct qfuse_list_hdr)) { + res = TEE_ERROR_BAD_FORMAT; + goto cleanup; + } + + qfuse_hdr = (const struct qfuse_list_hdr *)seg_data; + count = qfuse_hdr->fuse_count; + entries = (const struct fuse_entry *) + (seg_data + sizeof(struct qfuse_list_hdr)); + + res = blow_fuse_region(FUSEPROV_REGION_GENERAL, entries, count); + if (res != TEE_SUCCESS) + goto cleanup; + + res = blow_shk(entries, count); + if (res != TEE_SUCCESS) + goto cleanup; + + res = provision_oem_spare(entries, count); + if (res != TEE_SUCCESS) + goto cleanup; + + res = blow_fuse_region(FUSEPROV_REGION_OEM_CONFIG, entries, count); + if (res != TEE_SUCCESS) + goto cleanup; + + res = blow_fuse_region(FUSEPROV_REGION_SECBOOT, entries, count); + if (res != TEE_SUCCESS) + goto cleanup; + + res = blow_fuse_region(FUSEPROV_REGION_FEC_EN, entries, count); + if (res != TEE_SUCCESS) + goto cleanup; + + res = blow_fuse_region(FUSEPROV_REGION_RW_PERM, entries, count); + if (res != TEE_SUCCESS) + goto cleanup; + + res = TEE_SUCCESS; + +cleanup: + qfprom_hw_deinit(); + return res; +} + +void fuseprov_reset_device(void) +{ + IMSG("Fuse provisioning complete - manual reset required from U-Boot"); +} diff --git a/core/drivers/qcom/qfprom/fuseprov/fuseprov_regions.c b/core/drivers/qcom/qfprom/fuseprov/fuseprov_regions.c new file mode 100644 index 0000000000..932e46927c --- /dev/null +++ b/core/drivers/qcom/qfprom/fuseprov/fuseprov_regions.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include + +#include "sec_elf_v2.h" + +#define SHK_SIZE_BYTES 40 +#define SHK_NUM_ROWS 5 + +TEE_Result blow_shk(const struct fuse_entry *entries, uint32_t count) +{ + uint8_t shk[SHK_SIZE_BYTES]; + uint32_t data[2]; + enum qfprom_error err; + uint32_t i; + uint32_t shk_row_idx = 0; + TEE_Result res; + uint8_t has_fec; + + for (i = 0; i < count; i++) { + if (entries[i].region != FUSEPROV_REGION_SHK || + entries[i].operation != FUSEPROV_OP_BLOW) + continue; + + err = qfprom_read_row(entries[i].addr, QFPROM_ADDR_SPACE_CORR, + data); + if (err != QFPROM_NO_ERR) + return TEE_ERROR_GENERIC; + + if (data[0] != 0 || data[1] != 0) { + IMSG("SHK fuse already blown, skipping"); + return TEE_SUCCESS; + } + } + + res = crypto_rng_read(shk, sizeof(shk)); + if (res != TEE_SUCCESS) + return res; + + shk_row_idx = 0; + for (i = 0; i < count; i++) { + uint32_t offset; + + if (entries[i].region != FUSEPROV_REGION_SHK || + entries[i].operation != FUSEPROV_OP_BLOW) + continue; + + if (shk_row_idx >= SHK_NUM_ROWS) { + EMSG("Too many SHK entries in SEC-ELF (max %d)", + SHK_NUM_ROWS); + res = TEE_ERROR_GENERIC; + goto cleanup; + } + + err = qfprom_row_has_fec_bits(entries[i].addr, + QFPROM_ADDR_SPACE_RAW, &has_fec); + if (err != QFPROM_NO_ERR) { + res = TEE_ERROR_GENERIC; + goto cleanup; + } + + offset = shk_row_idx * 8; + memcpy(&data[0], &shk[offset], 4); + memcpy(&data[1], &shk[offset + 4], 4); + + data[0] &= 0xFFFFFFFF; + data[1] &= 0x00FFFFFF; + + if (has_fec) + data[1] = qfprom_fec_63_56_bit(data[0], data[1]); + + err = qfprom_write_row(entries[i].addr, data); + if (err != QFPROM_NO_ERR) { + res = TEE_ERROR_GENERIC; + goto cleanup; + } + + shk_row_idx++; + } + + res = TEE_SUCCESS; + +cleanup: + memset(shk, 0, sizeof(shk)); + memset(data, 0, sizeof(data)); + dsb(); + + return res; +} + +TEE_Result provision_oem_spare(const struct fuse_entry *entries, + uint32_t count) +{ + TEE_Result res = TEE_SUCCESS; + enum qfprom_error err; + uint32_t data[2]; + uint32_t i; + uint8_t rnd[8]; + uint8_t has_fec; + + for (i = 0; i < count; i++) { + const struct fuse_entry *entry = &entries[i]; + + if (entry->region != FUSEPROV_REGION_OEM_SPARE || + (entry->operation != FUSEPROV_OP_BLOW && + entry->operation != FUSEPROV_OP_BLOW_RANDOM)) + continue; + + err = qfprom_read_row(entry->addr, QFPROM_ADDR_SPACE_CORR, + data); + if (err != QFPROM_NO_ERR) { + EMSG("Failed to read OEM spare fuse at 0x%08x: err=%d", + entry->addr, err); + res = TEE_ERROR_GENERIC; + goto cleanup; + } + + if (data[0] != 0 || data[1] != 0) { + IMSG("OEM spare fuse at 0x%08x already blown, skipping", + entry->addr); + continue; + } + + has_fec = 0; + + err = qfprom_row_has_fec_bits(entry->addr, + QFPROM_ADDR_SPACE_RAW, + &has_fec); + if (err != QFPROM_NO_ERR) { + EMSG("Failed to check FEC for addr 0x%08x: error=%d", + entry->addr, err); + res = TEE_ERROR_GENERIC; + goto cleanup; + } + + if (entry->operation == FUSEPROV_OP_BLOW) { + data[0] = entry->lsb_val; + data[1] = entry->msb_val; + } else { + res = crypto_rng_read(rnd, sizeof(rnd)); + if (res != TEE_SUCCESS) { + EMSG("Failed to generate random data: 0x%x", + res); + goto cleanup; + } + + memcpy(&data[0], &rnd[0], 4); + memcpy(&data[1], &rnd[4], 4); + } + + if (has_fec) + data[1] = qfprom_fec_63_56_bit(data[0], data[1]); + + err = qfprom_write_row(entry->addr, data); + if (err != QFPROM_NO_ERR) { + EMSG("Failed to write OEM spare fuse at 0x%08x: err=%d", + entry->addr, err); + res = TEE_ERROR_GENERIC; + goto cleanup; + } + } + +cleanup: + memset(rnd, 0, sizeof(rnd)); + memset(data, 0, sizeof(data)); + dsb(); + + return res; +} diff --git a/core/drivers/qcom/qfprom/fuseprov/sec_elf_v2.c b/core/drivers/qcom/qfprom/fuseprov/sec_elf_v2.c new file mode 100644 index 0000000000..566bb47d1d --- /dev/null +++ b/core/drivers/qcom/qfprom/fuseprov/sec_elf_v2.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include + +#include "sec_elf_v2.h" + +TEE_Result sec_elf_parse(const uint8_t *data, size_t size, + const struct secdat_hdr **hdr, + const struct segment_hdr **segments) +{ + const struct secdat_hdr *header; + + if (!data || !hdr || !segments) + return TEE_ERROR_BAD_PARAMETERS; + + if (size < sizeof(struct secdat_hdr)) { + EMSG("sec.elf too small: %zu bytes", size); + return TEE_ERROR_BAD_FORMAT; + } + + header = (const struct secdat_hdr *)data; + + if (header->magic1 != FUSEPROV_SECDAT_MAGIC1) { + EMSG("Invalid magic1: 0x%08x", header->magic1); + return TEE_ERROR_BAD_FORMAT; + } + + if (header->magic2 != FUSEPROV_SECDAT_MAGIC2) { + EMSG("Invalid magic2: 0x%08x", header->magic2); + return TEE_ERROR_BAD_FORMAT; + } + + if (header->revision != FUSEPROV_SECDAT_VERSION_2) { + EMSG("Unsupported version: 0x%08x", header->revision); + return TEE_ERROR_BAD_FORMAT; + } + + if (header->size + sizeof(*header) + sizeof(struct secdat_footer) != + size) { + EMSG("Size mismatch: header=%u, file=%zu", header->size, size); + return TEE_ERROR_BAD_FORMAT; + } + + if (header->seg_num > FUSEPROV_SECDAT_MAX_SUPPORTED_SEGMENT) { + EMSG("Too many segments: %u", header->seg_num); + return TEE_ERROR_BAD_FORMAT; + } + + *hdr = header; + *segments = (const struct segment_hdr *)(data + sizeof(*header)); + + return TEE_SUCCESS; +} + +TEE_Result sec_elf_validate_header(const uint8_t *data, size_t size, + const struct secdat_hdr **hdr) +{ + const struct secdat_hdr *header; + + if (!data || !hdr) + return TEE_ERROR_BAD_PARAMETERS; + + if (size < sizeof(struct secdat_hdr)) { + EMSG("sec.elf too small: %zu bytes", size); + return TEE_ERROR_BAD_FORMAT; + } + + header = (const struct secdat_hdr *)data; + + if (header->magic1 != FUSEPROV_SECDAT_MAGIC1 || + header->magic2 != FUSEPROV_SECDAT_MAGIC2) { + EMSG("Invalid magic numbers"); + return TEE_ERROR_BAD_FORMAT; + } + + if (header->revision != FUSEPROV_SECDAT_VERSION_2) { + EMSG("Unsupported version: 0x%08x", header->revision); + return TEE_ERROR_BAD_FORMAT; + } + + if (header->size + sizeof(*header) + sizeof(struct secdat_footer) != + size) { + EMSG("Size mismatch: header=%u, file=%zu", header->size, size); + return TEE_ERROR_BAD_FORMAT; + } + + if (header->seg_num > FUSEPROV_SECDAT_MAX_SUPPORTED_SEGMENT) { + EMSG("Too many segments: %u", header->seg_num); + return TEE_ERROR_BAD_FORMAT; + } + + *hdr = header; + return TEE_SUCCESS; +} + +TEE_Result sec_elf_validate_hash(const uint8_t *data, size_t size, + const struct secdat_hdr *hdr) +{ + TEE_Result res; + uint8_t calc_hash[TEE_SHA256_HASH_SIZE]; + const uint8_t *stored_hash; + void *ctx = NULL; + size_t hash_bytes; + + if (!data || !hdr) + return TEE_ERROR_BAD_PARAMETERS; + + /* + * Calculate hash over header, segments, and data payload. + * The footer contains the stored hash for verification. + */ + hash_bytes = sizeof(struct secdat_hdr) + + (hdr->seg_num * sizeof(struct segment_hdr)) + + (hdr->size - sizeof(struct secdat_footer)); + + if (hash_bytes >= size) { + EMSG("Invalid hash size calculation: %zu >= %zu", + hash_bytes, size); + return TEE_ERROR_BAD_FORMAT; + } + + stored_hash = data + hash_bytes; + + res = crypto_hash_alloc_ctx(&ctx, TEE_ALG_SHA256); + if (res != TEE_SUCCESS) { + EMSG("Failed to allocate hash context: 0x%x", res); + goto out; + } + + res = crypto_hash_init(ctx); + if (res != TEE_SUCCESS) { + EMSG("Failed to initialize hash: 0x%x", res); + goto out; + } + + res = crypto_hash_update(ctx, data, hash_bytes); + if (res != TEE_SUCCESS) { + EMSG("Failed to update hash: 0x%x", res); + goto out; + } + + res = crypto_hash_final(ctx, calc_hash, TEE_SHA256_HASH_SIZE); + if (res != TEE_SUCCESS) { + EMSG("Failed to finalize hash: 0x%x", res); + goto out; + } + + if (consttime_memcmp(calc_hash, stored_hash, + TEE_SHA256_HASH_SIZE) != 0) { + EMSG("Hash verification failed"); + res = TEE_ERROR_SECURITY; + goto out; + } + + DMSG("Hash validation successful"); + res = TEE_SUCCESS; + +out: + crypto_hash_free_ctx(ctx); + return res; +} + +TEE_Result sec_elf_find_segment(const uint8_t *data, size_t size, + uint32_t seg_type, + const uint8_t **seg_data, + uint32_t *seg_size) +{ + const struct secdat_hdr *hdr; + const struct segment_hdr *segments; + uint32_t i; + TEE_Result res; + + if (!data || !seg_data || !seg_size) + return TEE_ERROR_BAD_PARAMETERS; + + res = sec_elf_validate_header(data, size, &hdr); + if (res != TEE_SUCCESS) + return res; + + segments = (const struct segment_hdr *)(data + sizeof(*hdr)); + + for (i = 0; i < hdr->seg_num; i++) { + if (segments[i].type == seg_type) { + if (segments[i].offset >= size) { + EMSG("Invalid segment offset: %u", + segments[i].offset); + return TEE_ERROR_BAD_FORMAT; + } + + *seg_data = data + segments[i].offset; + + /* + * Calculate segment size: + * - If not the last segment, size is the difference + * between this segment's offset and the next + * - If last segment, size extends to the footer + */ + if ((i + 1) < hdr->seg_num) { + /* Not the last segment */ + *seg_size = segments[i + 1].offset - + segments[i].offset; + } else { + /* Last segment */ + *seg_size = size - + sizeof(struct secdat_footer) - + segments[i].offset; + } + + return TEE_SUCCESS; + } + } + + *seg_data = NULL; + *seg_size = 0; + return TEE_ERROR_ITEM_NOT_FOUND; +} diff --git a/core/drivers/qcom/qfprom/fuseprov/sec_elf_v2.h b/core/drivers/qcom/qfprom/fuseprov/sec_elf_v2.h new file mode 100644 index 0000000000..fcdcf45549 --- /dev/null +++ b/core/drivers/qcom/qfprom/fuseprov/sec_elf_v2.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __SEC_ELF_V2_H__ +#define __SEC_ELF_V2_H__ + +#include +#include +#include +#include + +#define FUSEPROV_SECDAT_MAGIC1 0x3B7251CA +#define FUSEPROV_SECDAT_MAGIC2 0x2A126F29 +#define FUSEPROV_SECDAT_VERSION_2 0x00000002 + +#define FUSEPROV_SECDAT_MAX_SUPPORTED_SEGMENT 32 + +#define FUSEPROV_SECDAT_SEGMENT_TYPE_EFUSE 0x00 +#define FUSEPROV_SECDAT_SEGMENT_TYPE_ENCKEY 0x01 + +enum fuseprov_region_type { + FUSEPROV_REGION_SECBOOT = 0, + FUSEPROV_REGION_OEM_PK_HASH = 1, + FUSEPROV_REGION_SHK = 2, + FUSEPROV_REGION_OEM_CONFIG = 3, + FUSEPROV_REGION_RW_PERM = 4, + FUSEPROV_REGION_SPARE_REG19 = 5, + FUSEPROV_REGION_GENERAL = 6, + FUSEPROV_REGION_FEC_EN = 7, + FUSEPROV_REGION_ANTI_ROLLBACK_2 = 8, + FUSEPROV_REGION_ANTI_ROLLBACK_3 = 9, + FUSEPROV_REGION_PK_HASH1 = 10, + FUSEPROV_REGION_IMAGE_ENCR_KEY1 = 11, + FUSEPROV_REGION_OEM_SECURE = 12, + FUSEPROV_REGION_MRC_2_0 = 13, + FUSEPROV_REGION_OEM_SPARE = 14, + FUSEPROV_REGION_MAX +}; + +enum fuseprov_operation_type { + FUSEPROV_OP_BLOW = 0x00, + FUSEPROV_OP_VERIFYMASK0 = 0x01, + FUSEPROV_OP_BLOW_RANDOM = 0x02, +}; + +struct secdat_hdr { + uint32_t magic1; + uint32_t magic2; + uint32_t revision; + uint32_t size; + uint8_t info[16]; + uint32_t seg_num; + uint32_t reserved[3]; +} __packed; + +struct segment_hdr { + uint32_t offset; + uint16_t type; + uint16_t attribute; +} __packed; + +struct fuse_entry { + uint32_t region; + uint32_t addr; + uint32_t lsb_val; + uint32_t msb_val; + uint32_t operation; +} __packed; + +struct qfuse_list_hdr { + uint32_t revision; + uint32_t size; + uint32_t fuse_count; + uint32_t reserved[4]; +} __packed; + +struct secdat_footer { + uint8_t hash[TEE_SHA256_HASH_SIZE]; +} __packed; + +TEE_Result sec_elf_parse(const uint8_t *data, size_t size, + const struct secdat_hdr **hdr, + const struct segment_hdr **segments); + +TEE_Result sec_elf_validate_header(const uint8_t *data, size_t size, + const struct secdat_hdr **hdr); + +TEE_Result sec_elf_validate_hash(const uint8_t *data, size_t size, + const struct secdat_hdr *hdr); + +TEE_Result sec_elf_find_segment(const uint8_t *data, size_t size, + uint32_t seg_type, + const uint8_t **seg_data, + uint32_t *seg_size); + +TEE_Result provision_oem_spare(const struct fuse_entry *entries, + uint32_t count); + +TEE_Result blow_shk(const struct fuse_entry *entries, uint32_t count); + +TEE_Result fuseprov_execute(const uint8_t *data, size_t len); + +void fuseprov_reset_device(void); + +#endif /* __SEC_ELF_V2_H__ */ diff --git a/core/drivers/qcom/qfprom/fuseprov/sub.mk b/core/drivers/qcom/qfprom/fuseprov/sub.mk new file mode 100644 index 0000000000..5a11913e38 --- /dev/null +++ b/core/drivers/qcom/qfprom/fuseprov/sub.mk @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# + +srcs-y += sec_elf_v2.c +srcs-y += fuseprov_core.c +srcs-y += fuseprov_regions.c +srcs-y += fuseprov_boot.c diff --git a/core/drivers/qcom/qfprom/kodiak/qfprom_fuse_region.c b/core/drivers/qcom/qfprom/kodiak/qfprom_fuse_region.c new file mode 100644 index 0000000000..b223f5f285 --- /dev/null +++ b/core/drivers/qcom/qfprom/kodiak/qfprom_fuse_region.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include + +#include "qfprom_priv.h" + +#define QFPROM_REGION_ENTRY(name, sz, fec, readable, idx) \ + { \ + .region_name = QFPROM_##name##_REGION, \ + .size = (sz), \ + .fec_type = (fec), \ + .raw_base_addr = name##_ADDR, \ + .corr_base_addr = QFPROM_RAW_TO_CORR(name##_ADDR), \ + .read_perm_mask = name##_PERM_MASK, \ + .write_perm_mask = name##_PERM_MASK, \ + .perm_reg_type = QFPROM_ROW_LSB, \ + .read_allowed = (readable), \ + .region_index = (idx), \ + } + +#define QFPROM_STD_REGION(name, sz, idx) \ + QFPROM_REGION_ENTRY(name, sz, QFPROM_FEC_NONE, true, idx) + +#define QFPROM_FEC_REGION(name, sz, idx) \ + QFPROM_REGION_ENTRY(name, sz, QFPROM_FEC_63_56, true, idx) + +#define QFPROM_PRIVATE_REGION(name, sz, idx) \ + QFPROM_REGION_ENTRY(name, sz, QFPROM_FEC_NONE, false, idx) + +const struct qfprom_region_info region_data[] = { + QFPROM_STD_REGION(LCM, 1, 1), + QFPROM_PRIVATE_REGION(PRI_KEY_DERIVATION_KEY, 4, 2), + QFPROM_STD_REGION(MRC_2_0, 2, 4), + QFPROM_STD_REGION(PTE, 5, 5), + QFPROM_STD_REGION(READ_PERMISSION, 1, 6), + QFPROM_STD_REGION(WRITE_PERMISSION, 1, 7), + QFPROM_STD_REGION(FEC_ENABLES, 1, 8), + QFPROM_STD_REGION(OEM_CONFIG, 3, 9), + QFPROM_STD_REGION(FEATURE_CONFIG_M, 5, 10), + QFPROM_STD_REGION(FEATURE_CONFIG_NM, 4, 11), + QFPROM_STD_REGION(ANTI_ROLLBACK_1, 1, 12), + QFPROM_STD_REGION(ANTI_ROLLBACK_2, 1, 13), + QFPROM_STD_REGION(ANTI_ROLLBACK_3, 1, 14), + QFPROM_STD_REGION(ANTI_ROLLBACK_4, 1, 15), + QFPROM_STD_REGION(ANTI_ROLLBACK_5, 1, 16), + QFPROM_STD_REGION(PK_HASH_0, 6, 17), + QFPROM_STD_REGION(CALIBRATION, 25, 18), + QFPROM_STD_REGION(MEMORY_CONFIGURATION, 120, 19), + QFPROM_FEC_REGION(QC_SPARE_20, 1, 20), + QFPROM_FEC_REGION(QC_SPARE_21, 1, 21), + QFPROM_FEC_REGION(OEM_IMAGE_ENCRYPTION_KEY, 3, 22), + QFPROM_FEC_REGION(OEM_SECURE_BOOT, 2, 23), + QFPROM_FEC_REGION(SEC_KEY_DERIVATION_KEY, 5, 24), + QFPROM_FEC_REGION(IMAGE_ENCRYPTION_KEY_1, 3, 26), + QFPROM_FEC_REGION(USER_KEY_DERIVATION_KEY, 5, 27), + QFPROM_STD_REGION(OEM_SPARE_28, 2, 28), + QFPROM_STD_REGION(OEM_SPARE_29, 2, 29), + QFPROM_STD_REGION(OEM_SPARE_30, 2, 30), + QFPROM_STD_REGION(OEM_SPARE_31, 2, 31), + { + QFPROM_LAST_REGION_DUMMY, + 0, + QFPROM_FEC_NONE, + 0, + 0, + 0, + 0, + QFPROM_ROW_LSB + } +}; + +const size_t region_count = ARRAY_SIZE(region_data); + +const struct qfprom_platform_config plat_config = { + .name = "Kodiak", + .qfprom_raw_base = QFPROM_RAW_BASE, + .qfprom_corr_base = QFPROM_CORR_BASE, + .qfprom_size = QFPROM_SIZE, + .region_data = region_data, + .num_regions = ARRAY_SIZE(region_data), + .read_perm_lsb_offset = QFPROM_READ_PERM_LSB_OFFSET, + .read_perm_msb_offset = QFPROM_READ_PERM_MSB_OFFSET, + .write_perm_lsb_offset = QFPROM_WRITE_PERM_LSB_OFFSET, + .write_perm_msb_offset = QFPROM_WRITE_PERM_MSB_OFFSET, +}; + +const struct qfprom_platform_config *qfprom_get_platform_config(void) +{ + return &plat_config; +} diff --git a/core/drivers/qcom/qfprom/kodiak/qfprom_target.h b/core/drivers/qcom/qfprom/kodiak/qfprom_target.h new file mode 100644 index 0000000000..48f02520a1 --- /dev/null +++ b/core/drivers/qcom/qfprom/kodiak/qfprom_target.h @@ -0,0 +1,207 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __QFPROM_TARGET_H__ +#define __QFPROM_TARGET_H__ + +#include +#include +#include +#include +#include +#include + +#define QFPROM_RAW_BASE 0x00780000 +#define QFPROM_CORR_BASE 0x00784000 +#define QFPROM_SIZE 0x4000 + +#define QFPROM_BLOW_TIMER_OFFSET 0x203c +#define QFPROM_ACCEL_OFFSET 0x2044 + +#define QFPROM_BLOW_STATUS_OFFSET 0x2048 +#define QFPROM_BLOW_STATUS_RMSK 0x3 +#define QFPROM_BLOW_STATUS_BUSY_VAL 0x1 +#define QFPROM_BLOW_STATUS_ERROR_VAL 0x2 +#define QFPROM_BLOW_STATUS_READY_VAL 0x0 + +#define QFPROM_FEC_ESR_OFFSET 0x2060 +#define QFPROM_FEC_EAR_OFFSET 0x2064 +#define QFPROM_FEC_ESR_ERR_SEEN_BMSK BIT(0) +#define QFPROM_FEC_EAR_ERR_ADDR_BMSK 0xFFFF + +#define QFPROM_BLOW_TIMER_CLK_FREQ_MHZ_X10 48 +#define QFPROM_FUSE_BLOW_TIME_IN_US 5 + +#define QFPROM_GATELAST_VAL 0x1 +#define QFPROM_TRIPPT_SEL_VAL 0x5 +#define QFPROM_ACCEL_VAL 0x8 + +#define QFPROM_GATELAST_SHFT 11 +#define QFPROM_TRIPPT_SEL_SHFT 8 +#define QFPROM_ACCEL_SHFT 0 + +#define QFPROM_ACCEL_VALUE \ + ((QFPROM_GATELAST_VAL << QFPROM_GATELAST_SHFT) | \ + (QFPROM_TRIPPT_SEL_VAL << QFPROM_TRIPPT_SEL_SHFT) | \ + (QFPROM_ACCEL_VAL << QFPROM_ACCEL_SHFT)) + +#define QFPROM_ACCEL_RESET_VALUE \ + (0x1 << QFPROM_GATELAST_SHFT) + +#define MX_QFPROM_ENABLE_VAL 15 +#define MX_QFPROM_DISABLE_VAL 9 + +#define PM_QFPROM_VREG_A "mx.lvl" +#define QFPROM_VOLTAGE_ON 15 +#define QFPROM_VOLTAGE_OFF 9 + +#define QFPROM_RAW_TO_CORR(raw_addr) \ + ((raw_addr) + (QFPROM_CORR_BASE - QFPROM_RAW_BASE)) + +#define LCM_ADDR 0x00780120 +#define PRI_KEY_DERIVATION_KEY_ADDR 0x00780128 +#define MRC_2_0_ADDR 0x00780170 +#define PTE_ADDR 0x00780180 +#define READ_PERMISSION_ADDR 0x007801A8 +#define WRITE_PERMISSION_ADDR 0x007801B0 +#define FEC_ENABLES_ADDR 0x007801B8 +#define OEM_CONFIG_ADDR 0x007801C0 +#define FEATURE_CONFIG_M_ADDR 0x007801D8 +#define FEATURE_CONFIG_NM_ADDR 0x00780200 +#define ANTI_ROLLBACK_1_ADDR 0x00780220 +#define ANTI_ROLLBACK_2_ADDR 0x00780228 +#define ANTI_ROLLBACK_3_ADDR 0x00780230 +#define ANTI_ROLLBACK_4_ADDR 0x00780238 +#define ANTI_ROLLBACK_5_ADDR 0x00780240 +#define PK_HASH_0_ADDR 0x00780248 +#define CALIBRATION_ADDR 0x00780278 +#define MEMORY_CONFIGURATION_ADDR 0x00780340 +#define QC_SPARE_20_ADDR 0x00780700 +#define QC_SPARE_21_ADDR 0x00780708 +#define OEM_IMAGE_ENCRYPTION_KEY_ADDR 0x00780710 +#define OEM_SECURE_BOOT_ADDR 0x00780728 +#define SEC_KEY_DERIVATION_KEY_ADDR 0x00780738 +#define IMAGE_ENCRYPTION_KEY_1_ADDR 0x007809A0 +#define USER_KEY_DERIVATION_KEY_ADDR 0x007809B8 +#define OEM_SPARE_28_ADDR 0x007809E0 +#define OEM_SPARE_29_ADDR 0x007809F0 +#define OEM_SPARE_30_ADDR 0x00780A00 +#define OEM_SPARE_31_ADDR 0x00780A10 + +#define QFPROM_READ_PERM_LSB_OFFSET \ + (READ_PERMISSION_ADDR - QFPROM_RAW_BASE) +#define QFPROM_READ_PERM_MSB_OFFSET \ + (QFPROM_READ_PERM_LSB_OFFSET + 4) + +#define QFPROM_WRITE_PERM_LSB_OFFSET \ + (WRITE_PERMISSION_ADDR - QFPROM_RAW_BASE) +#define QFPROM_WRITE_PERM_MSB_OFFSET \ + (QFPROM_WRITE_PERM_LSB_OFFSET + 4) + +#define QFPROM_HW_MUTEX_ID 8 +#define QFPROM_HW_MUTEX_PID 1 +#define QFPROM_HW_MUTEX_TIMEOUT_US 10000 +#define QFPROM_MUTEX_REG_ADDR \ + (TCSR_MUTEX_BASE + (0x1000 * QFPROM_HW_MUTEX_ID)) + +enum qfprom_region_name { + QFPROM_CRI_CM_PRIVATE_REGION = 0, + QFPROM_LCM_REGION, + QFPROM_PRI_KEY_DERIVATION_KEY_REGION, + QFPROM_CM_FEATURE_CONFIG_REGION, + QFPROM_MRC_2_0_REGION, + QFPROM_PTE_REGION, + QFPROM_READ_PERMISSION_REGION, + QFPROM_WRITE_PERMISSION_REGION, + QFPROM_FEC_ENABLES_REGION, + QFPROM_OEM_CONFIG_REGION, + QFPROM_FEATURE_CONFIG_M_REGION, + QFPROM_FEATURE_CONFIG_NM_REGION, + QFPROM_ANTI_ROLLBACK_1_REGION, + QFPROM_ANTI_ROLLBACK_2_REGION, + QFPROM_ANTI_ROLLBACK_3_REGION, + QFPROM_ANTI_ROLLBACK_4_REGION, + QFPROM_ANTI_ROLLBACK_5_REGION, + QFPROM_PK_HASH_0_REGION, + QFPROM_CALIBRATION_REGION, + QFPROM_MEMORY_CONFIGURATION_REGION, + QFPROM_QC_SPARE_20_REGION, + QFPROM_QC_SPARE_21_REGION, + QFPROM_OEM_IMAGE_ENCRYPTION_KEY_REGION, + QFPROM_OEM_SECURE_BOOT_REGION, + QFPROM_SEC_KEY_DERIVATION_KEY_REGION, + QFPROM_BOOT_ROM_PATCH_REGION, + QFPROM_IMAGE_ENCRYPTION_KEY_1_REGION, + QFPROM_USER_KEY_DERIVATION_KEY_REGION, + QFPROM_OEM_SPARE_28_REGION, + QFPROM_OEM_SPARE_29_REGION, + QFPROM_OEM_SPARE_30_REGION, + QFPROM_OEM_SPARE_31_REGION, + QFPROM_LAST_REGION_DUMMY, +}; + +enum qfprom_perm_bit_pos { + LCM = 1, + PRI_KEY_DERIVATION_KEY = 2, + MRC_2_0 = 4, + PTE = 5, + READ_PERMISSION = 6, + WRITE_PERMISSION = 7, + FEC_ENABLES = 8, + OEM_CONFIG = 9, + FEATURE_CONFIG_M = 10, + FEATURE_CONFIG_NM = 11, + ANTI_ROLLBACK_1 = 12, + ANTI_ROLLBACK_2 = 13, + ANTI_ROLLBACK_3 = 14, + ANTI_ROLLBACK_4 = 15, + ANTI_ROLLBACK_5 = 16, + PK_HASH_0 = 17, + CALIBRATION = 18, + MEMORY_CONFIGURATION = 19, + QC_SPARE_20 = 20, + QC_SPARE_21 = 21, + OEM_IMAGE_ENCRYPTION_KEY = 22, + OEM_SECURE_BOOT = 23, + SEC_KEY_DERIVATION_KEY = 24, + IMAGE_ENCRYPTION_KEY_1 = 26, + USER_KEY_DERIVATION_KEY = 27, + OEM_SPARE_28 = 28, + OEM_SPARE_29 = 29, + OEM_SPARE_30 = 30, + OEM_SPARE_31 = 31, +}; + +#define LCM_PERM_MASK BIT(LCM) +#define PRI_KEY_DERIVATION_KEY_PERM_MASK BIT(PRI_KEY_DERIVATION_KEY) +#define MRC_2_0_PERM_MASK BIT(MRC_2_0) +#define PTE_PERM_MASK BIT(PTE) +#define READ_PERMISSION_PERM_MASK BIT(READ_PERMISSION) +#define WRITE_PERMISSION_PERM_MASK BIT(WRITE_PERMISSION) +#define FEC_ENABLES_PERM_MASK BIT(FEC_ENABLES) +#define OEM_CONFIG_PERM_MASK BIT(OEM_CONFIG) +#define FEATURE_CONFIG_M_PERM_MASK BIT(FEATURE_CONFIG_M) +#define FEATURE_CONFIG_NM_PERM_MASK BIT(FEATURE_CONFIG_NM) +#define ANTI_ROLLBACK_1_PERM_MASK BIT(ANTI_ROLLBACK_1) +#define ANTI_ROLLBACK_2_PERM_MASK BIT(ANTI_ROLLBACK_2) +#define ANTI_ROLLBACK_3_PERM_MASK BIT(ANTI_ROLLBACK_3) +#define ANTI_ROLLBACK_4_PERM_MASK BIT(ANTI_ROLLBACK_4) +#define ANTI_ROLLBACK_5_PERM_MASK BIT(ANTI_ROLLBACK_5) +#define PK_HASH_0_PERM_MASK BIT(PK_HASH_0) +#define CALIBRATION_PERM_MASK BIT(CALIBRATION) +#define MEMORY_CONFIGURATION_PERM_MASK BIT(MEMORY_CONFIGURATION) +#define QC_SPARE_20_PERM_MASK BIT(QC_SPARE_20) +#define QC_SPARE_21_PERM_MASK BIT(QC_SPARE_21) +#define OEM_IMAGE_ENCRYPTION_KEY_PERM_MASK BIT(OEM_IMAGE_ENCRYPTION_KEY) +#define OEM_SECURE_BOOT_PERM_MASK BIT(OEM_SECURE_BOOT) +#define SEC_KEY_DERIVATION_KEY_PERM_MASK BIT(SEC_KEY_DERIVATION_KEY) +#define IMAGE_ENCRYPTION_KEY_1_PERM_MASK BIT(IMAGE_ENCRYPTION_KEY_1) +#define USER_KEY_DERIVATION_KEY_PERM_MASK BIT(USER_KEY_DERIVATION_KEY) +#define OEM_SPARE_28_PERM_MASK BIT(OEM_SPARE_28) +#define OEM_SPARE_29_PERM_MASK BIT(OEM_SPARE_29) +#define OEM_SPARE_30_PERM_MASK BIT(OEM_SPARE_30) +#define OEM_SPARE_31_PERM_MASK BIT(OEM_SPARE_31) + +#endif /* __QFPROM_TARGET_H__ */ diff --git a/core/drivers/qcom/qfprom/lemans/qfprom_fuse_region.c b/core/drivers/qcom/qfprom/lemans/qfprom_fuse_region.c new file mode 100644 index 0000000000..31fd4291fc --- /dev/null +++ b/core/drivers/qcom/qfprom/lemans/qfprom_fuse_region.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include + +#include "qfprom_priv.h" + +#define QFPROM_REGION_ENTRY(name, sz, fec, readable, idx) \ + { \ + .region_name = QFPROM_##name##_REGION, \ + .size = (sz), \ + .fec_type = (fec), \ + .raw_base_addr = name##_ADDR, \ + .corr_base_addr = QFPROM_RAW_TO_CORR(name##_ADDR), \ + .read_perm_mask = name##_PERM_MASK, \ + .write_perm_mask = name##_PERM_MASK, \ + .perm_reg_type = QFPROM_ROW_LSB, \ + .read_allowed = (readable), \ + .region_index = (idx), \ + } + +#define QFPROM_REGION_ENTRY_MSB(name, sz, fec, readable, idx) \ + { \ + .region_name = QFPROM_##name##_REGION, \ + .size = (sz), \ + .fec_type = (fec), \ + .raw_base_addr = name##_ADDR, \ + .corr_base_addr = QFPROM_RAW_TO_CORR(name##_ADDR), \ + .read_perm_mask = name##_PERM_MASK, \ + .write_perm_mask = name##_PERM_MASK, \ + .perm_reg_type = QFPROM_ROW_MSB, \ + .read_allowed = (readable), \ + .region_index = (idx), \ + } + +#define QFPROM_STD_REGION(name, sz, idx) \ + QFPROM_REGION_ENTRY(name, sz, QFPROM_FEC_NONE, true, idx) + +#define QFPROM_FEC_REGION(name, sz, idx) \ + QFPROM_REGION_ENTRY(name, sz, QFPROM_FEC_63_56, true, idx) + +#define QFPROM_FEC_REGION_MSB(name, sz, idx) \ + QFPROM_REGION_ENTRY_MSB(name, sz, QFPROM_FEC_63_56, true, idx) + +#define QFPROM_PRIVATE_REGION(name, sz, idx) \ + QFPROM_REGION_ENTRY(name, sz, QFPROM_FEC_NONE, false, idx) + +#define QFPROM_STD_REGION_MSB(name, sz, idx) \ + QFPROM_REGION_ENTRY_MSB(name, sz, QFPROM_FEC_NONE, true, idx) + +const struct qfprom_region_info region_data[] = { + QFPROM_STD_REGION(LCM, 1, 1), + QFPROM_PRIVATE_REGION(PRI_KEY_DERIVATION_KEY, 4, 2), + QFPROM_STD_REGION(MRC_2_0, 2, 3), + QFPROM_STD_REGION(PTE, 7, 4), + QFPROM_STD_REGION(READ_PERMISSION, 1, 5), + QFPROM_STD_REGION(WRITE_PERMISSION, 1, 6), + QFPROM_STD_REGION(FEC_ENABLES, 1, 7), + QFPROM_STD_REGION(OEM_CONFIG, 5, 8), + QFPROM_STD_REGION(FEATURE_CONFIG_M, 8, 9), + QFPROM_STD_REGION(FEATURE_CONFIG_NM, 5, 10), + QFPROM_STD_REGION(ANTI_ROLLBACK_1, 1, 11), + QFPROM_STD_REGION(ANTI_ROLLBACK_2, 1, 12), + QFPROM_STD_REGION(ANTI_ROLLBACK_3, 1, 13), + QFPROM_STD_REGION(ANTI_ROLLBACK_4, 1, 14), + QFPROM_STD_REGION(ANTI_ROLLBACK_5, 1, 15), + QFPROM_STD_REGION(ANTI_ROLLBACK_6, 1, 16), + QFPROM_STD_REGION(ANTI_ROLLBACK_7, 1, 17), + QFPROM_STD_REGION(ANTI_ROLLBACK_8, 1, 18), + QFPROM_STD_REGION(ANTI_ROLLBACK_9, 1, 19), + QFPROM_STD_REGION(ANTI_ROLLBACK_10, 1, 20), + QFPROM_STD_REGION(ANTI_ROLLBACK_11, 1, 21), + QFPROM_STD_REGION(ANTI_ROLLBACK_12, 1, 22), + QFPROM_STD_REGION(ANTI_ROLLBACK_13, 1, 23), + QFPROM_STD_REGION(PK_HASH_0, 8, 24), + QFPROM_STD_REGION(CALIBRATION, 54, 25), + QFPROM_STD_REGION(MEMORY_CONFIGURATION, 280, 26), + QFPROM_FEC_REGION(QC_SPARE_20, 1, 27), + QFPROM_FEC_REGION(QC_SPARE_21, 1, 28), + QFPROM_FEC_REGION(OEM_IMAGE_ENCRYPTION_KEY, 3, 29), + QFPROM_FEC_REGION(OEM_SECURE_BOOT, 2, 30), + QFPROM_FEC_REGION_MSB(SEC_KEY_DERIVATION_KEY, 5, 31), + QFPROM_FEC_REGION_MSB(IMAGE_ENCRYPTION_KEY_1, 3, 33), + QFPROM_FEC_REGION_MSB(USER_KEY_DERIVATION_KEY, 5, 34), + QFPROM_STD_REGION_MSB(OEM_SPARE_28, 2, 35), + QFPROM_STD_REGION_MSB(OEM_SPARE_29, 2, 36), + QFPROM_STD_REGION_MSB(OEM_SPARE_30, 2, 37), + QFPROM_STD_REGION_MSB(OEM_SPARE_31, 2, 38), + { + QFPROM_LAST_REGION_DUMMY, + 0, + QFPROM_FEC_NONE, + 0, + 0, + 0, + 0, + QFPROM_ROW_LSB + } +}; + +const size_t region_count = ARRAY_SIZE(region_data); + +const struct qfprom_platform_config plat_config = { + .name = "Lemans", + .qfprom_raw_base = QFPROM_RAW_BASE, + .qfprom_corr_base = QFPROM_CORR_BASE, + .qfprom_size = QFPROM_SIZE, + .region_data = region_data, + .num_regions = ARRAY_SIZE(region_data), + .read_perm_lsb_offset = QFPROM_READ_PERM_LSB_OFFSET, + .read_perm_msb_offset = QFPROM_READ_PERM_MSB_OFFSET, + .write_perm_lsb_offset = QFPROM_WRITE_PERM_LSB_OFFSET, + .write_perm_msb_offset = QFPROM_WRITE_PERM_MSB_OFFSET, +}; + +const struct qfprom_platform_config *qfprom_get_platform_config(void) +{ + return &plat_config; +} diff --git a/core/drivers/qcom/qfprom/lemans/qfprom_target.h b/core/drivers/qcom/qfprom/lemans/qfprom_target.h new file mode 100644 index 0000000000..8207330b4a --- /dev/null +++ b/core/drivers/qcom/qfprom/lemans/qfprom_target.h @@ -0,0 +1,231 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __QFPROM_TARGET_H__ +#define __QFPROM_TARGET_H__ + +#include +#include +#include +#include +#include +#include + +#define QFPROM_RAW_BASE 0x00780000 +#define QFPROM_CORR_BASE 0x00784000 +#define QFPROM_SIZE 0x4000 + +#define QFPROM_BLOW_TIMER_OFFSET 0x2030 +#define QFPROM_ACCEL_OFFSET 0x2038 + +#define QFPROM_BLOW_STATUS_OFFSET 0x203c +#define QFPROM_BLOW_STATUS_RMSK 0x3 +#define QFPROM_BLOW_STATUS_BUSY_VAL 0x1 +#define QFPROM_BLOW_STATUS_ERROR_VAL 0x2 +#define QFPROM_BLOW_STATUS_READY_VAL 0x0 + +#define QFPROM_FEC_ESR_OFFSET 0x2058 +#define QFPROM_FEC_EAR_OFFSET 0x205c +#define QFPROM_FEC_ESR_ERR_SEEN_BMSK BIT(0) +#define QFPROM_FEC_EAR_ERR_ADDR_BMSK 0xFFFF + +#define QFPROM_BLOW_TIMER_CLK_FREQ_MHZ_X10 48 +#define QFPROM_FUSE_BLOW_TIME_IN_US 5 + +#define QFPROM_GATELAST_VAL 0x1 +#define QFPROM_TRIPPT_SEL_VAL 0x4 +#define QFPROM_ACCEL_VAL 0x10 + +#define QFPROM_GATELAST_SHFT 11 +#define QFPROM_TRIPPT_SEL_SHFT 8 +#define QFPROM_ACCEL_SHFT 0 + +#define QFPROM_ACCEL_VALUE \ + ((QFPROM_GATELAST_VAL << QFPROM_GATELAST_SHFT) | \ + (QFPROM_TRIPPT_SEL_VAL << QFPROM_TRIPPT_SEL_SHFT) | \ + (QFPROM_ACCEL_VAL << QFPROM_ACCEL_SHFT)) + +#define QFPROM_ACCEL_RESET_VALUE \ + (0x1 << QFPROM_GATELAST_SHFT) + +#define QFPROM_RAW_TO_CORR(raw_addr) \ + ((raw_addr) + (QFPROM_CORR_BASE - QFPROM_RAW_BASE)) + +#define LCM_ADDR 0x00780120 +#define PRI_KEY_DERIVATION_KEY_ADDR 0x00780128 +#define MRC_2_0_ADDR 0x00780148 +#define PTE_ADDR 0x00780158 +#define READ_PERMISSION_ADDR 0x00780190 +#define WRITE_PERMISSION_ADDR 0x00780198 +#define FEC_ENABLES_ADDR 0x007801A0 +#define OEM_CONFIG_ADDR 0x007801A8 +#define FEATURE_CONFIG_M_ADDR 0x007801D0 +#define FEATURE_CONFIG_NM_ADDR 0x00780210 +#define ANTI_ROLLBACK_1_ADDR 0x00780238 +#define ANTI_ROLLBACK_2_ADDR 0x00780240 +#define ANTI_ROLLBACK_3_ADDR 0x00780248 +#define ANTI_ROLLBACK_4_ADDR 0x00780250 +#define ANTI_ROLLBACK_5_ADDR 0x00780258 +#define ANTI_ROLLBACK_6_ADDR 0x00780260 +#define ANTI_ROLLBACK_7_ADDR 0x00780268 +#define ANTI_ROLLBACK_8_ADDR 0x00780270 +#define ANTI_ROLLBACK_9_ADDR 0x00780278 +#define ANTI_ROLLBACK_10_ADDR 0x00780280 +#define ANTI_ROLLBACK_11_ADDR 0x00780288 +#define ANTI_ROLLBACK_12_ADDR 0x00780290 +#define ANTI_ROLLBACK_13_ADDR 0x00780298 +#define PK_HASH_0_ADDR 0x007802A0 +#define CALIBRATION_ADDR 0x007802E0 +#define MEMORY_CONFIGURATION_ADDR 0x00780490 +#define QC_SPARE_20_ADDR 0x00780D50 +#define QC_SPARE_21_ADDR 0x00780D58 +#define OEM_IMAGE_ENCRYPTION_KEY_ADDR 0x00780D60 +#define OEM_SECURE_BOOT_ADDR 0x00780D78 +#define SEC_KEY_DERIVATION_KEY_ADDR 0x00780D88 +#define IMAGE_ENCRYPTION_KEY_1_ADDR 0x00781150 +#define USER_KEY_DERIVATION_KEY_ADDR 0x00781168 +#define OEM_SPARE_28_ADDR 0x00781190 +#define OEM_SPARE_29_ADDR 0x007811A0 +#define OEM_SPARE_30_ADDR 0x007811B0 +#define OEM_SPARE_31_ADDR 0x007811C0 + +#define QFPROM_READ_PERM_LSB_OFFSET \ + (READ_PERMISSION_ADDR - QFPROM_RAW_BASE) +#define QFPROM_READ_PERM_MSB_OFFSET \ + (QFPROM_READ_PERM_LSB_OFFSET + 4) + +#define QFPROM_WRITE_PERM_LSB_OFFSET \ + (WRITE_PERMISSION_ADDR - QFPROM_RAW_BASE) +#define QFPROM_WRITE_PERM_MSB_OFFSET \ + (QFPROM_WRITE_PERM_LSB_OFFSET + 4) + +#define QFPROM_HW_MUTEX_ID 8 +#define QFPROM_HW_MUTEX_PID 1 +#define QFPROM_HW_MUTEX_TIMEOUT_US 10000 +#define QFPROM_MUTEX_REG_ADDR \ + (TCSR_MUTEX_BASE + (0x1000 * QFPROM_HW_MUTEX_ID)) + +enum qfprom_region_name { + QFPROM_CRI_CM_PRIVATE_REGION = 0, + QFPROM_LCM_REGION, + QFPROM_PRI_KEY_DERIVATION_KEY_REGION, + QFPROM_MRC_2_0_REGION, + QFPROM_PTE_REGION, + QFPROM_READ_PERMISSION_REGION, + QFPROM_WRITE_PERMISSION_REGION, + QFPROM_FEC_ENABLES_REGION, + QFPROM_OEM_CONFIG_REGION, + QFPROM_FEATURE_CONFIG_M_REGION, + QFPROM_FEATURE_CONFIG_NM_REGION, + QFPROM_ANTI_ROLLBACK_1_REGION, + QFPROM_ANTI_ROLLBACK_2_REGION, + QFPROM_ANTI_ROLLBACK_3_REGION, + QFPROM_ANTI_ROLLBACK_4_REGION, + QFPROM_ANTI_ROLLBACK_5_REGION, + QFPROM_ANTI_ROLLBACK_6_REGION, + QFPROM_ANTI_ROLLBACK_7_REGION, + QFPROM_ANTI_ROLLBACK_8_REGION, + QFPROM_ANTI_ROLLBACK_9_REGION, + QFPROM_ANTI_ROLLBACK_10_REGION, + QFPROM_ANTI_ROLLBACK_11_REGION, + QFPROM_ANTI_ROLLBACK_12_REGION, + QFPROM_ANTI_ROLLBACK_13_REGION, + QFPROM_PK_HASH_0_REGION, + QFPROM_CALIBRATION_REGION, + QFPROM_MEMORY_CONFIGURATION_REGION, + QFPROM_QC_SPARE_20_REGION, + QFPROM_QC_SPARE_21_REGION, + QFPROM_OEM_IMAGE_ENCRYPTION_KEY_REGION, + QFPROM_OEM_SECURE_BOOT_REGION, + QFPROM_SEC_KEY_DERIVATION_KEY_REGION, + QFPROM_BOOT_ROM_PATCH_REGION, + QFPROM_IMAGE_ENCRYPTION_KEY_1_REGION, + QFPROM_USER_KEY_DERIVATION_KEY_REGION, + QFPROM_OEM_SPARE_28_REGION, + QFPROM_OEM_SPARE_29_REGION, + QFPROM_OEM_SPARE_30_REGION, + QFPROM_OEM_SPARE_31_REGION, + QFPROM_LAST_REGION_DUMMY, +}; + +enum qfprom_perm_bit_pos { + LCM = 1, + PRI_KEY_DERIVATION_KEY = 2, + MRC_2_0 = 3, + PTE = 4, + READ_PERMISSION = 5, + WRITE_PERMISSION = 6, + FEC_ENABLES = 7, + OEM_CONFIG = 8, + FEATURE_CONFIG_M = 9, + FEATURE_CONFIG_NM = 10, + ANTI_ROLLBACK_1 = 11, + ANTI_ROLLBACK_2 = 12, + ANTI_ROLLBACK_3 = 13, + ANTI_ROLLBACK_4 = 14, + ANTI_ROLLBACK_5 = 15, + ANTI_ROLLBACK_6 = 16, + ANTI_ROLLBACK_7 = 17, + ANTI_ROLLBACK_8 = 18, + ANTI_ROLLBACK_9 = 19, + ANTI_ROLLBACK_10 = 20, + ANTI_ROLLBACK_11 = 21, + ANTI_ROLLBACK_12 = 22, + ANTI_ROLLBACK_13 = 23, + PK_HASH_0 = 24, + CALIBRATION = 25, + MEMORY_CONFIGURATION = 26, + QC_SPARE_20 = 27, + QC_SPARE_21 = 28, + OEM_IMAGE_ENCRYPTION_KEY = 29, + OEM_SECURE_BOOT = 30, + SEC_KEY_DERIVATION_KEY = 31, + IMAGE_ENCRYPTION_KEY_1 = 1, /* MSB bit 1 */ + USER_KEY_DERIVATION_KEY = 2, /* MSB bit 2 */ + OEM_SPARE_28 = 3, /* MSB bit 3 */ + OEM_SPARE_29 = 4, /* MSB bit 4 */ + OEM_SPARE_30 = 5, /* MSB bit 5 */ + OEM_SPARE_31 = 6, /* MSB bit 6 */ +}; + +#define LCM_PERM_MASK BIT(LCM) +#define PRI_KEY_DERIVATION_KEY_PERM_MASK BIT(PRI_KEY_DERIVATION_KEY) +#define MRC_2_0_PERM_MASK BIT(MRC_2_0) +#define PTE_PERM_MASK BIT(PTE) +#define READ_PERMISSION_PERM_MASK BIT(READ_PERMISSION) +#define WRITE_PERMISSION_PERM_MASK BIT(WRITE_PERMISSION) +#define FEC_ENABLES_PERM_MASK BIT(FEC_ENABLES) +#define OEM_CONFIG_PERM_MASK BIT(OEM_CONFIG) +#define FEATURE_CONFIG_M_PERM_MASK BIT(FEATURE_CONFIG_M) +#define FEATURE_CONFIG_NM_PERM_MASK BIT(FEATURE_CONFIG_NM) +#define ANTI_ROLLBACK_1_PERM_MASK BIT(ANTI_ROLLBACK_1) +#define ANTI_ROLLBACK_2_PERM_MASK BIT(ANTI_ROLLBACK_2) +#define ANTI_ROLLBACK_3_PERM_MASK BIT(ANTI_ROLLBACK_3) +#define ANTI_ROLLBACK_4_PERM_MASK BIT(ANTI_ROLLBACK_4) +#define ANTI_ROLLBACK_5_PERM_MASK BIT(ANTI_ROLLBACK_5) +#define ANTI_ROLLBACK_6_PERM_MASK BIT(ANTI_ROLLBACK_6) +#define ANTI_ROLLBACK_7_PERM_MASK BIT(ANTI_ROLLBACK_7) +#define ANTI_ROLLBACK_8_PERM_MASK BIT(ANTI_ROLLBACK_8) +#define ANTI_ROLLBACK_9_PERM_MASK BIT(ANTI_ROLLBACK_9) +#define ANTI_ROLLBACK_10_PERM_MASK BIT(ANTI_ROLLBACK_10) +#define ANTI_ROLLBACK_11_PERM_MASK BIT(ANTI_ROLLBACK_11) +#define ANTI_ROLLBACK_12_PERM_MASK BIT(ANTI_ROLLBACK_12) +#define ANTI_ROLLBACK_13_PERM_MASK BIT(ANTI_ROLLBACK_13) +#define PK_HASH_0_PERM_MASK BIT(PK_HASH_0) +#define CALIBRATION_PERM_MASK BIT(CALIBRATION) +#define MEMORY_CONFIGURATION_PERM_MASK BIT(MEMORY_CONFIGURATION) +#define QC_SPARE_20_PERM_MASK BIT(QC_SPARE_20) +#define QC_SPARE_21_PERM_MASK BIT(QC_SPARE_21) +#define OEM_IMAGE_ENCRYPTION_KEY_PERM_MASK BIT(OEM_IMAGE_ENCRYPTION_KEY) +#define OEM_SECURE_BOOT_PERM_MASK BIT(OEM_SECURE_BOOT) +#define SEC_KEY_DERIVATION_KEY_PERM_MASK BIT(SEC_KEY_DERIVATION_KEY) +#define IMAGE_ENCRYPTION_KEY_1_PERM_MASK BIT(IMAGE_ENCRYPTION_KEY_1) +#define USER_KEY_DERIVATION_KEY_PERM_MASK BIT(USER_KEY_DERIVATION_KEY) +#define OEM_SPARE_28_PERM_MASK BIT(OEM_SPARE_28) +#define OEM_SPARE_29_PERM_MASK BIT(OEM_SPARE_29) +#define OEM_SPARE_30_PERM_MASK BIT(OEM_SPARE_30) +#define OEM_SPARE_31_PERM_MASK BIT(OEM_SPARE_31) + +#endif /* __QFPROM_TARGET_H__ */ diff --git a/core/drivers/qcom/qfprom/qfprom_core.c b/core/drivers/qcom/qfprom/qfprom_core.c new file mode 100644 index 0000000000..54deabd6f8 --- /dev/null +++ b/core/drivers/qcom/qfprom/qfprom_core.c @@ -0,0 +1,560 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qfprom_hal.h" +#include "qfprom_priv.h" +#include "qfprom_target.h" + +register_phys_mem_pgdir(MEM_AREA_IO_SEC, TCSR_MUTEX_BASE, CORE_MMU_PGDIR_SIZE); +register_phys_mem_pgdir(MEM_AREA_IO_SEC, SECURITY_CONTROL_BASE, + CORE_MMU_PGDIR_SIZE); + +static struct qfprom_context ctx; + +struct qfprom_context *qfprom_get_context(void) +{ + return &ctx; +} + +static bool is_address_valid(uint32_t addr, + enum qfprom_addr_space type) +{ + struct qfprom_context *qfprom_ctx = qfprom_get_context(); + paddr_t base, end; + uint32_t lookup_addr; + + if (!qfprom_ctx || !qfprom_ctx->config || + !IS_ALIGNED_WITH_TYPE(addr, uint64_t)) + return false; + + lookup_addr = addr; + if (type == QFPROM_ADDR_SPACE_CORR) + lookup_addr = QFPROM_RAW_TO_CORR(addr); + + if (type == QFPROM_ADDR_SPACE_RAW) + base = qfprom_ctx->config->qfprom_raw_base; + else + base = qfprom_ctx->config->qfprom_corr_base; + + end = base + qfprom_ctx->config->qfprom_size; + if (lookup_addr < base || lookup_addr >= end) + return false; + + return true; +} + +static enum qfprom_error get_region_name(uint32_t addr, + enum qfprom_addr_space addr_type, + enum qfprom_region_name *region_type) +{ + struct qfprom_context *drv = qfprom_get_context(); + uint32_t region_start, region_end; + uint32_t lookup_addr; + size_t i; + + if (!region_type) + return QFPROM_DATA_PTR_NULL_ERR; + + if (!drv->config || !drv->config->region_data) + return QFPROM_ERR_UNKNOWN; + + if (!is_address_valid(addr, addr_type)) + return QFPROM_ADDRESS_INVALID_ERR; + + lookup_addr = (addr_type == QFPROM_ADDR_SPACE_CORR) ? + QFPROM_RAW_TO_CORR(addr) : addr; + + for (i = 0; i < drv->config->num_regions; i++) { + const struct qfprom_region_info *region = + &drv->config->region_data[i]; + uint32_t offset; + + if (addr_type == QFPROM_ADDR_SPACE_RAW) + region_start = region->raw_base_addr; + else if (addr_type == QFPROM_ADDR_SPACE_CORR) + region_start = region->corr_base_addr; + else + return QFPROM_ERR_UNKNOWN; + + region_end = region_start + (region->size * 8); + + if (lookup_addr < region_start) + continue; + + if (lookup_addr >= region_end) + continue; + + offset = lookup_addr - region_start; + if (offset % 8) + return QFPROM_ADDRESS_INVALID_ERR; + + if (offset / 8 >= region->size) + return QFPROM_ADDRESS_INVALID_ERR; + + *region_type = region->region_name; + return QFPROM_NO_ERR; + } + + return QFPROM_REGION_NOT_SUPPORTED_ERR; +} + +static int raw_read(uint32_t addr, + enum qfprom_addr_space type, + uint32_t *data) +{ + uint32_t ret; + + if (!data) + return QFPROM_DATA_PTR_NULL_ERR; + + if (type == QFPROM_ADDR_SPACE_RAW) { + ret = hal_qfprom_read_raw_address_row(addr, data); + } else { + addr = QFPROM_RAW_TO_CORR(addr); + ret = hal_qfprom_read_corrected_address_row(addr, data); + } + + return ret; +} + +static enum qfprom_error is_fec_enabled(enum qfprom_region_name region_type, + bool *fec_status) +{ + struct qfprom_context *drv = qfprom_get_context(); + const struct qfprom_region_info *info = NULL; + enum qfprom_error err; + paddr_t reg_addr; + uint32_t val, bit; + size_t i; + + if (!fec_status) + return QFPROM_DATA_PTR_NULL_ERR; + + if (!drv->config || !drv->config->region_data) + return QFPROM_ERR_UNKNOWN; + + for (i = 0; i < drv->config->num_regions; i++) { + if (drv->config->region_data[i].region_name == region_type) { + info = &drv->config->region_data[i]; + break; + } + } + + if (!info) + return QFPROM_REGION_NOT_SUPPORTED_ERR; + + if (info->fec_type == QFPROM_FEC_NONE) { + *fec_status = false; + return QFPROM_NO_ERR; + } + + if (info->fec_type != QFPROM_FEC_63_56 || + info->region_index >= QFPROM_FEC_REGION_MSB_MAX) + return QFPROM_ERR_UNKNOWN; + + reg_addr = QFPROM_RAW_TO_CORR(FEC_ENABLES_ADDR); + if (info->region_index >= QFPROM_FEC_REGION_LSB_MAX) { + reg_addr += 4; + bit = info->region_index - QFPROM_FEC_REGION_LSB_MAX; + } else { + bit = info->region_index; + } + + err = hal_qfprom_read_corrected_address(reg_addr, &val); + if (err != QFPROM_NO_ERR) { + EMSG("Failed to read FEC enable register at 0x%lx, error: %d", + reg_addr, err); + return err; + } + + *fec_status = (val & (1U << bit)) != 0; + + return QFPROM_NO_ERR; +} + +static bool check_region_access(enum qfprom_region_name region, + uint32_t perm_flags) +{ + struct qfprom_context *drv = qfprom_get_context(); + const struct qfprom_region_info *info = NULL; + paddr_t perm_addr; + uint32_t perm; + uint32_t offset; + size_t i; + + if (!drv->config || !drv->config->region_data) + return false; + + for (i = 0; i < drv->config->num_regions; i++) { + if (drv->config->region_data[i].region_name == region) { + info = &drv->config->region_data[i]; + break; + } + } + + if (!info) + return false; + + /* Check read permission */ + if (perm_flags & REGION_PERM_READ) { + offset = (info->perm_reg_type == QFPROM_ROW_LSB) ? + drv->config->read_perm_lsb_offset : + drv->config->read_perm_msb_offset; + + if (!offset) { + if (!info->read_allowed) + return false; + } else { + perm_addr = drv->config->qfprom_corr_base + offset; + if (hal_qfprom_read_corrected_address(perm_addr, + &perm) != + QFPROM_NO_ERR) + return false; + + if (perm & info->read_perm_mask) + return false; + } + } + + /* Check write permission */ + if (perm_flags & REGION_PERM_WRITE) { + offset = (info->perm_reg_type == QFPROM_ROW_LSB) ? + drv->config->write_perm_lsb_offset : + drv->config->write_perm_msb_offset; + + if (!offset) + return false; + + perm_addr = drv->config->qfprom_corr_base + offset; + if (hal_qfprom_read_corrected_address(perm_addr, + &perm) != + QFPROM_NO_ERR) + return false; + + if (perm & info->write_perm_mask) + return false; + } + + return true; +} + +static enum qfprom_error wait_blow_status_ready(void) +{ + uint64_t timer = timeout_init_us(QFPROM_BLOW_TIMEOUT_US); + uint32_t status; + enum qfprom_error err; + + while (true) { + err = hal_qfprom_read_blow_status(&status); + if (err != QFPROM_NO_ERR) + return err; + + if (status != QFPROM_BLOW_STATUS_BUSY_VAL) + break; + + if (timeout_elapsed(timer)) { + EMSG("QFPROM blow operation timed out"); + return QFPROM_ERROR_TIMEOUT; + } + + udelay(10); + } + + if (status != QFPROM_BLOW_STATUS_READY_VAL) + return QFPROM_WRITE_ERR; + + return QFPROM_NO_ERR; +} + +static enum qfprom_error raw_write(uint32_t addr, + const uint32_t *data) +{ + enum qfprom_error err; + enum qfprom_region_name region; + bool fec_enabled = false; + uint32_t verify[2]; + + if (!data) + return QFPROM_DATA_PTR_NULL_ERR; + + err = get_region_name(addr, QFPROM_ADDR_SPACE_RAW, ®ion); + if (err != QFPROM_NO_ERR) + return err; + + if (!check_region_access(region, + REGION_PERM_READ | REGION_PERM_WRITE)) + return QFPROM_REGION_NOT_WRITABLE_ERR; + + if (is_fec_enabled(region, &fec_enabled) == QFPROM_NO_ERR && + fec_enabled) + return QFPROM_REGION_NOT_WRITABLE_ERR; + + err = wait_blow_status_ready(); + if (err != QFPROM_NO_ERR) + return err; + + err = hal_qfprom_write_raw_address(addr, data[0]); + if (err != QFPROM_NO_ERR) + return err; + + err = wait_blow_status_ready(); + if (err != QFPROM_NO_ERR) + return err; + + err = hal_qfprom_write_raw_address(addr + 4, data[1]); + if (err != QFPROM_NO_ERR) + return err; + + err = wait_blow_status_ready(); + if (err != QFPROM_NO_ERR) + return err; + + if (!check_region_access(region, REGION_PERM_READ)) + return QFPROM_NO_ERR; + + err = raw_read(addr, QFPROM_ADDR_SPACE_RAW, verify); + if (err != QFPROM_NO_ERR) + return QFPROM_NO_ERR; + + if ((verify[0] & data[0]) != data[0] || + (verify[1] & data[1]) != data[1]) + return QFPROM_WRITE_ERR; + + return QFPROM_NO_ERR; +} + +TEE_Result qfprom_read_row(uint32_t addr, + enum qfprom_addr_space type, + uint32_t *data) +{ + enum qfprom_region_name region; + enum qfprom_error err; + + if (!data) + return TEE_ERROR_BAD_PARAMETERS; + + err = get_region_name(addr, type, ®ion); + if (err == QFPROM_ADDRESS_INVALID_ERR) + return TEE_ERROR_BAD_PARAMETERS; + else if (err != QFPROM_NO_ERR) + return TEE_ERROR_GENERIC; + + if (!check_region_access(region, REGION_PERM_READ)) + return TEE_ERROR_ACCESS_DENIED; + + err = raw_read(addr, type, data); + if (err != QFPROM_NO_ERR) { + EMSG("QFPROM read failed for address 0x%08x, error: %d", + addr, err); + return TEE_ERROR_GENERIC; + } + + if (type == QFPROM_ADDR_SPACE_CORR) { + bool fec_enabled = false; + + err = is_fec_enabled(region, &fec_enabled); + if (err == QFPROM_NO_ERR && fec_enabled && + hal_qfprom_is_fec_error_seen()) { + uint16_t err_addr = 0; + + hal_qfprom_read_error_address(&err_addr); + EMSG("FEC error: 0x%04x req 0x%08x", err_addr, addr); + hal_qfprom_clear_fec_error_status(); + return TEE_ERROR_CORRUPT_OBJECT; + } + } + + return TEE_SUCCESS; +} + +TEE_Result qfprom_hw_init(void) +{ + struct qfprom_context *drv = qfprom_get_context(); + TEE_Result res; + + res = qfprom_acquire_hw_mutex(); + if (res != TEE_SUCCESS) + return res; + + mutex_lock(&drv->lock); + + res = qfprom_enable_voltage(); + if (res != TEE_SUCCESS) + goto err_unlock; + + res = qfprom_write_set_clock_settings(); + if (res != TEE_SUCCESS) + goto err_disable_voltage; + + drv->write_op_allowed = true; + return TEE_SUCCESS; + +err_disable_voltage: + if (qfprom_disable_voltage() != TEE_SUCCESS) + EMSG("Failed to disable voltage"); +err_unlock: + mutex_unlock(&drv->lock); + qfprom_release_hw_mutex(); + return res; +} + +void qfprom_hw_deinit(void) +{ + struct qfprom_context *drv = qfprom_get_context(); + + drv->write_op_allowed = false; + qfprom_write_reset_clock_settings(); + if (qfprom_disable_voltage() != TEE_SUCCESS) + EMSG("Failed to disable voltage"); + mutex_unlock(&drv->lock); + qfprom_release_hw_mutex(); +} + +TEE_Result qfprom_write_row(uint32_t addr, uint32_t *data) +{ + enum qfprom_error err; + uint32_t write_data[2]; + + if (!data) + return TEE_ERROR_BAD_PARAMETERS; + + if (!IS_ENABLED(CFG_QFPROM_PROGRAMMING)) + return TEE_ERROR_NOT_SUPPORTED; + + write_data[0] = data[0]; + write_data[1] = data[1]; + + err = raw_write(addr, write_data); + if (err != QFPROM_NO_ERR) { + EMSG("QFPROM write failed for address 0x%08x, error: %d", + addr, err); + return TEE_ERROR_GENERIC; + } + + return TEE_SUCCESS; +} + +TEE_Result qfprom_row_has_fec_bits(uint32_t addr, + enum qfprom_addr_space type, + uint8_t *has_fec) +{ + struct qfprom_context *drv = qfprom_get_context(); + enum qfprom_region_name region_type; + enum qfprom_error err; + size_t i; + + if (!has_fec) + return TEE_ERROR_BAD_PARAMETERS; + + err = get_region_name(addr, type, ®ion_type); + if (err == QFPROM_ADDRESS_INVALID_ERR) + return TEE_ERROR_BAD_PARAMETERS; + else if (err != QFPROM_NO_ERR) + return TEE_ERROR_GENERIC; + + if (!drv->config || !drv->config->region_data) + return TEE_ERROR_GENERIC; + + for (i = 0; i < drv->config->num_regions; i++) { + const struct qfprom_region_info *region = + &drv->config->region_data[i]; + + if (region->region_name == region_type) { + if (check_region_access(region_type, REGION_PERM_READ)) + *has_fec = (region->fec_type != + QFPROM_FEC_NONE) ? 1 : 0; + else + *has_fec = 0; + + return TEE_SUCCESS; + } + } + + return TEE_ERROR_ITEM_NOT_FOUND; +} + +uint32_t qfprom_fec_63_56_bit(uint32_t lsb_data, uint32_t msb_data) +{ + uint8_t lfsr[7] = {0}; + int i = 0; + uint32_t temp = 0; + uint32_t fec_val = 0; + uint64_t data_loc = 0; + + data_loc = ((uint64_t)msb_data << 32) | lsb_data; + + for (i = 0; i < 56; i++) { + temp = lfsr[0] ^ ((data_loc >> i) & 0x1); + + lfsr[0] = lfsr[1] ^ temp; + lfsr[1] = lfsr[2]; + lfsr[2] = lfsr[3]; + lfsr[3] = lfsr[4]; + lfsr[4] = lfsr[5] ^ temp; + lfsr[5] = lfsr[6]; + lfsr[6] = temp; + } + + for (i = 6; i >= 0; i--) { + temp = (lfsr[i] << i); + fec_val = (fec_val | temp); + } + + return ((fec_val << 24) | msb_data); +} + +static TEE_Result qfprom_init(void) +{ + struct qfprom_context *drv = qfprom_get_context(); + const struct qfprom_platform_config *config; + + config = qfprom_get_platform_config(); + if (!config) { + EMSG("Failed to get platform configuration"); + goto err_panic; + } + + drv->config = config; + mutex_init(&drv->lock); + + drv->raw_base_va = (vaddr_t)phys_to_virt(SECURITY_CONTROL_BASE, + MEM_AREA_IO_SEC, + SECURITY_CONTROL_SIZE); + if (!drv->raw_base_va) { + EMSG("Failed to get VA for security control at PA 0x%lx", + (unsigned long)SECURITY_CONTROL_BASE); + goto err_panic; + } + + drv->mutex_reg_va = (vaddr_t)phys_to_virt(QFPROM_MUTEX_REG_ADDR, + MEM_AREA_IO_SEC, + sizeof(uint32_t)); + if (!drv->mutex_reg_va) { + EMSG("Failed to get VA for mutex register at PA 0x%lx", + (unsigned long)QFPROM_MUTEX_REG_ADDR); + goto err_panic; + } + + drv->corr_base_va = QFPROM_RAW_TO_CORR(drv->raw_base_va); + + return TEE_SUCCESS; + +err_panic: + panic("QFPROM driver initialization failed"); +} + +early_init_late(qfprom_init); diff --git a/core/drivers/qcom/qfprom/qfprom_hal.c b/core/drivers/qcom/qfprom/qfprom_hal.c new file mode 100644 index 0000000000..eccd213b2b --- /dev/null +++ b/core/drivers/qcom/qfprom/qfprom_hal.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include + +#include "qfprom_hal.h" +#include "qfprom_priv.h" +#include "qfprom_target.h" + +static void qfprom_write_reg(uint32_t offset, uint32_t value) +{ + struct qfprom_context *ctx = qfprom_get_context(); + vaddr_t reg = ctx->raw_base_va + offset; + + io_write32(reg, value); +} + +static uint32_t qfprom_read_reg(uint32_t offset) +{ + struct qfprom_context *ctx = qfprom_get_context(); + vaddr_t reg = ctx->raw_base_va + offset; + + return io_read32(reg); +} + +static enum qfprom_error hal_qfprom_read_address_generic(uint32_t addr, + uint32_t *value) +{ + vaddr_t vaddr; + + if (!value) + return QFPROM_DATA_PTR_NULL_ERR; + + if (!IS_ALIGNED_WITH_TYPE(addr, uint32_t)) + return QFPROM_ADDRESS_INVALID_ERR; + + vaddr = (vaddr_t)phys_to_virt(addr, MEM_AREA_IO_SEC, sizeof(uint32_t)); + if (!vaddr) + return QFPROM_ADDRESS_INVALID_ERR; + + *value = io_read32(vaddr); + + return QFPROM_NO_ERR; +} + +static enum qfprom_error hal_qfprom_read_row_generic(uint32_t addr, + uint32_t *value) +{ + enum qfprom_error ret; + + if (!value) + return QFPROM_DATA_PTR_NULL_ERR; + + ret = hal_qfprom_read_address_generic(addr, &value[0]); + if (ret != QFPROM_NO_ERR) + return ret; + + return hal_qfprom_read_address_generic(addr + 4, &value[1]); +} + +void hal_qfprom_set_blow_timer(uint32_t value) +{ + qfprom_write_reg(QFPROM_BLOW_TIMER_OFFSET, value); +} + +void hal_qfprom_set_accel(uint32_t value) +{ + qfprom_write_reg(QFPROM_ACCEL_OFFSET, value); +} + +enum qfprom_error hal_qfprom_read_raw_address(uint32_t addr, uint32_t *value) +{ + return hal_qfprom_read_address_generic(addr, value); +} + +enum qfprom_error hal_qfprom_read_raw_address_row(uint32_t addr, + uint32_t *value) +{ + return hal_qfprom_read_row_generic(addr, value); +} + +enum qfprom_error hal_qfprom_read_corrected_address(uint32_t addr, + uint32_t *value) +{ + return hal_qfprom_read_address_generic(addr, value); +} + +enum qfprom_error hal_qfprom_read_corrected_address_row(uint32_t addr, + uint32_t *value) +{ + return hal_qfprom_read_row_generic(addr, value); +} + +enum qfprom_error hal_qfprom_write_raw_address(uint32_t addr, uint32_t value) +{ + vaddr_t vaddr; + + if (!IS_ENABLED(CFG_QFPROM_PROGRAMMING)) + return QFPROM_OPERATION_NOT_ALLOWED_ERR; + + if (!IS_ALIGNED_WITH_TYPE(addr, uint32_t)) + return QFPROM_ADDRESS_INVALID_ERR; + + vaddr = (vaddr_t)phys_to_virt(addr, MEM_AREA_IO_SEC, sizeof(uint32_t)); + if (!vaddr) + return QFPROM_ADDRESS_INVALID_ERR; + + io_write32(vaddr, value); + return QFPROM_NO_ERR; +} + +enum qfprom_error hal_qfprom_read_blow_status(uint32_t *value) +{ + if (!value) + return QFPROM_DATA_PTR_NULL_ERR; + + *value = qfprom_read_reg(QFPROM_BLOW_STATUS_OFFSET) & + QFPROM_BLOW_STATUS_RMSK; + + return QFPROM_NO_ERR; +} + +void hal_qfprom_clear_fec_error_status(void) +{ + qfprom_write_reg(QFPROM_FEC_ESR_OFFSET, QFPROM_FEC_ESR_ERR_SEEN_BMSK); +} + +bool hal_qfprom_is_fec_error_seen(void) +{ + uint32_t val = qfprom_read_reg(QFPROM_FEC_ESR_OFFSET); + + return (val & QFPROM_FEC_ESR_ERR_SEEN_BMSK) != 0; +} + +void hal_qfprom_read_error_address(uint16_t *value) +{ + uint32_t val; + + if (!value) + return; + + val = qfprom_read_reg(QFPROM_FEC_EAR_OFFSET); + *value = (uint16_t)(val & QFPROM_FEC_EAR_ERR_ADDR_BMSK); +} diff --git a/core/drivers/qcom/qfprom/qfprom_hal.h b/core/drivers/qcom/qfprom/qfprom_hal.h new file mode 100644 index 0000000000..9224abbd14 --- /dev/null +++ b/core/drivers/qcom/qfprom/qfprom_hal.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __QFPROM_HAL_H__ +#define __QFPROM_HAL_H__ + +#include +#include +#include + +void hal_qfprom_set_blow_timer(uint32_t value); +void hal_qfprom_set_accel(uint32_t value); +enum qfprom_error hal_qfprom_read_raw_address(uint32_t addr, uint32_t *value); +enum qfprom_error hal_qfprom_read_raw_address_row(uint32_t addr, + uint32_t *value); +enum qfprom_error hal_qfprom_write_raw_address(uint32_t addr, uint32_t value); +enum qfprom_error hal_qfprom_read_corrected_address(uint32_t addr, + uint32_t *value); +enum qfprom_error hal_qfprom_read_corrected_address_row(uint32_t addr, + uint32_t *value); +enum qfprom_error hal_qfprom_read_blow_status(uint32_t *value); +void hal_qfprom_clear_fec_error_status(void); +bool hal_qfprom_is_fec_error_seen(void); +void hal_qfprom_read_error_address(uint16_t *value); + +#endif /* __QFPROM_HAL_H__ */ diff --git a/core/drivers/qcom/qfprom/qfprom_priv.h b/core/drivers/qcom/qfprom/qfprom_priv.h new file mode 100644 index 0000000000..0fabbdf7e9 --- /dev/null +++ b/core/drivers/qcom/qfprom/qfprom_priv.h @@ -0,0 +1,84 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __QFPROM_PRIV_H__ +#define __QFPROM_PRIV_H__ + +#include +#include +#include +#include + +#define QFPROM_BLOW_TIMEOUT_US 1000 +#define QFPROM_FEC_REGION_LSB_MAX 32 +#define QFPROM_FEC_REGION_MSB_MAX 64 + +enum qfprom_fec_scheme { + QFPROM_FEC_NONE = 0, + QFPROM_FEC_15_10, + QFPROM_FEC_63_56, + QFPROM_MAX_FEC +}; + +enum qfprom_row_type { + QFPROM_ROW_LSB = 0, + QFPROM_ROW_MSB, +}; + +struct qfprom_region_info { + enum qfprom_region_name region_name; + uint32_t size; + enum qfprom_fec_scheme fec_type; + uint32_t raw_base_addr; + uint32_t corr_base_addr; + uint32_t read_perm_mask; + uint32_t write_perm_mask; + enum qfprom_row_type perm_reg_type; + bool read_allowed; + uint32_t region_index; +}; + +struct qfprom_platform_config { + const char *name; + paddr_t qfprom_raw_base; + paddr_t qfprom_corr_base; + size_t qfprom_size; + uint32_t default_bus_clk_khz; + const struct qfprom_region_info *region_data; + size_t num_regions; + uint32_t read_perm_lsb_offset; + uint32_t write_perm_lsb_offset; + uint32_t read_perm_msb_offset; + uint32_t write_perm_msb_offset; +}; + +struct qfprom_context { + const struct qfprom_platform_config *config; + struct mutex lock; /* Protects QFPROM operations and state */ + vaddr_t raw_base_va; + vaddr_t corr_base_va; + vaddr_t mutex_reg_va; + bool write_op_allowed; +}; + +enum region_permission { + REGION_PERM_READ = BIT(0), + REGION_PERM_WRITE = BIT(1), +}; + +struct qfprom_context *qfprom_get_context(void); + +TEE_Result qfprom_write_set_clock_settings(void); +TEE_Result qfprom_write_reset_clock_settings(void); + +TEE_Result qfprom_enable_voltage(void); +TEE_Result qfprom_disable_voltage(void); + +TEE_Result qfprom_acquire_hw_mutex(void); +TEE_Result qfprom_release_hw_mutex(void); + +const struct qfprom_platform_config *qfprom_get_platform_config(void); + +#endif /* __QFPROM_PRIV_H__ */ diff --git a/core/drivers/qcom/qfprom/qfprom_target.c b/core/drivers/qcom/qfprom/qfprom_target.c new file mode 100644 index 0000000000..f89246bcc4 --- /dev/null +++ b/core/drivers/qcom/qfprom/qfprom_target.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qfprom_hal.h" +#include "qfprom_priv.h" + +TEE_Result qfprom_write_set_clock_settings(void) +{ + struct io_pa_va base = { .pa = GCC_BASE }; + vaddr_t gcc_base = io_pa_or_va(&base, GCC_SIZE); + vaddr_t cfg_rcgr = gcc_base + GCC_SEC_CTRL_CFG_RCGR; + vaddr_t cmd_rcgr = gcc_base + GCC_SEC_CTRL_CMD_RCGR; + uint32_t blow_timer_value; + TEE_Result res; + + res = qcom_clock_set_rate(cfg_rcgr, cmd_rcgr, QFPROM_CLOCK_DIVIDE); + if (res != TEE_SUCCESS) + return res; + + blow_timer_value = (QFPROM_BLOW_TIMER_CLK_FREQ_MHZ_X10 * + QFPROM_FUSE_BLOW_TIME_IN_US + 5) / 10; + + hal_qfprom_set_blow_timer(blow_timer_value & 0xFFF); + hal_qfprom_set_accel(QFPROM_ACCEL_VALUE); + + return TEE_SUCCESS; +} + +TEE_Result qfprom_write_reset_clock_settings(void) +{ + struct io_pa_va base = { .pa = GCC_BASE }; + vaddr_t gcc_base = io_pa_or_va(&base, GCC_SIZE); + vaddr_t cfg_rcgr = gcc_base + GCC_SEC_CTRL_CFG_RCGR; + vaddr_t cmd_rcgr = gcc_base + GCC_SEC_CTRL_CMD_RCGR; + TEE_Result res; + + res = qcom_clock_set_rate(cfg_rcgr, cmd_rcgr, 0); + if (res != TEE_SUCCESS) + return res; + + hal_qfprom_set_blow_timer(0); + + hal_qfprom_set_accel(QFPROM_ACCEL_RESET_VALUE); + + return TEE_SUCCESS; +} + +#ifdef CFG_QFPROM_MX_RAIL_WA +static struct rpmh_client *rpmh_handle; + +TEE_Result qfprom_enable_voltage(void) +{ + uint32_t vrm_addr; + uint32_t req_id; + + if (!rpmh_handle) { + rpmh_handle = rpmh_create_handle(RSC_DRV_SECURE, "qfprom"); + if (!rpmh_handle) { + EMSG("RPMH client creation failed"); + return TEE_ERROR_GENERIC; + } + } + + /* Enable MX voltage rail for QFPROM operations */ + if (cmd_db_query_addr(PM_QFPROM_VREG_A, &vrm_addr) != TEE_SUCCESS) { + EMSG("QFPROM voltage rail '%s' not found in CMD_DB", + PM_QFPROM_VREG_A); + return TEE_ERROR_GENERIC; + } + + if (rpmh_issue_command(rpmh_handle, RPMH_SET_ACTIVE, true, + vrm_addr, QFPROM_VOLTAGE_ON, &req_id) != + TEE_SUCCESS) { + EMSG("RPMH enable MX failed: addr 0x%x req_id %u", + vrm_addr, req_id); + return TEE_ERROR_GENERIC; + } + + rpmh_barrier_single(rpmh_handle, req_id); + + return TEE_SUCCESS; +} + +TEE_Result qfprom_disable_voltage(void) +{ + uint32_t vrm_addr; + uint32_t req_id; + + if (!rpmh_handle) { + EMSG("RPMH not initialized"); + return TEE_ERROR_GENERIC; + } + + /* Disable MX voltage rail after QFPROM operations */ + if (cmd_db_query_addr(PM_QFPROM_VREG_A, &vrm_addr) != TEE_SUCCESS) { + EMSG("QFPROM voltage rail '%s' not found in CMD_DB", + PM_QFPROM_VREG_A); + return TEE_ERROR_GENERIC; + } + + if (rpmh_issue_command(rpmh_handle, RPMH_SET_ACTIVE, true, + vrm_addr, QFPROM_VOLTAGE_OFF, &req_id) != + TEE_SUCCESS) { + EMSG("RPMH disable MX failed: addr 0x%x req_id %u", + vrm_addr, req_id); + return TEE_ERROR_GENERIC; + } + + rpmh_barrier_single(rpmh_handle, req_id); + + return TEE_SUCCESS; +} +#else +TEE_Result qfprom_enable_voltage(void) +{ + return TEE_SUCCESS; +} + +TEE_Result qfprom_disable_voltage(void) +{ + return TEE_SUCCESS; +} +#endif /* CFG_QFPROM_MX_RAIL_WA */ + +TEE_Result qfprom_acquire_hw_mutex(void) +{ + struct qfprom_context *qfprom_ctx = qfprom_get_context(); + uint64_t timer = timeout_init_us(QFPROM_HW_MUTEX_TIMEOUT_US); + uint32_t read_val; + + while (true) { + io_write32(qfprom_ctx->mutex_reg_va, QFPROM_HW_MUTEX_PID); + dsb(); + + read_val = io_read32(qfprom_ctx->mutex_reg_va); + if (read_val == QFPROM_HW_MUTEX_PID) + return TEE_SUCCESS; + + if (timeout_elapsed(timer)) { + EMSG("QFPROM HW mutex acquisition timeout after %u us", + QFPROM_HW_MUTEX_TIMEOUT_US); + return TEE_ERROR_BUSY; + } + + udelay(1); + } +} + +TEE_Result qfprom_release_hw_mutex(void) +{ + struct qfprom_context *qfprom_ctx = qfprom_get_context(); + + io_write32(qfprom_ctx->mutex_reg_va, 0); + dsb(); + + return TEE_SUCCESS; +} diff --git a/core/drivers/qcom/qfprom/sub.mk b/core/drivers/qcom/qfprom/sub.mk new file mode 100644 index 0000000000..77d0d6a79e --- /dev/null +++ b/core/drivers/qcom/qfprom/sub.mk @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# + +srcs-y += qfprom_core.c +srcs-y += qfprom_hal.c +srcs-y += qfprom_target.c + +$(eval $(call cfg-depends-all,CFG_QCOM_QFPROM_FUSEPROV,CFG_QCOM_QFPROM)) +subdirs-$(CFG_QCOM_QFPROM_FUSEPROV) += fuseprov + +srcs-y += $(PLATFORM_FLAVOR)/qfprom_fuse_region.c + +global-incdirs-y += . +global-incdirs-y += $(PLATFORM_FLAVOR) diff --git a/core/drivers/qcom/rpmh/rpmh_client.c b/core/drivers/qcom/rpmh/rpmh_client.c new file mode 100644 index 0000000000..e2d3f62bdb --- /dev/null +++ b/core/drivers/qcom/rpmh/rpmh_client.c @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rpmh_hal.h" +#include "rpmh_resource_commands.h" +#include "rpmh_tcs.h" + +register_phys_mem_pgdir(MEM_AREA_IO_NSEC, AOP_MSG_RAM_BASE, + CORE_MMU_PGDIR_SIZE); +register_phys_mem_pgdir(MEM_AREA_IO_SEC, RPMH_BASE_ADDR, + CORE_MMU_PGDIR_SIZE); + +struct rpmh_driver_data { + vaddr_t rsc_base; + struct client_queue *queue; + struct mutex lock; /* Protects driver state and client operations */ +}; + +struct client { + enum rscsw_drv_mapping drv_id; + const char *name; + uint32_t req_id; + uint32_t oldest_req_id; + uint32_t num_reqs_in_progress; + struct explicit_cmd_set *cmds; + SLIST_ENTRY(client) link; +}; + +struct explicit_cmd_set { + uint32_t in_use; + uint32_t total; + struct cmd_set_internal *sets; +}; + +struct cmd_set_internal { + enum rpmh_set_enum set; + uint32_t count; + uint32_t dependency_bmsk; + struct rpmh_command commands[RPMH_MAX_TCS_SIZE]; +}; + +SLIST_HEAD(client_list, client); + +struct client_queue { + struct client_list handles; +}; + +#define AOP_BOOT_COOKIE 0xA0C00C1E +#define MSG_RAM_SECTION_SIZE 0x10000 + +struct aop_msg_ram_dict { + uint32_t boot_cookie_offset; + uint32_t sleep_stats_offset; + uint32_t reserved_addrs[14]; +}; + +static struct rpmh_driver_data driver_state = { + .lock = MUTEX_INITIALIZER, +}; + +static bool tcs_is_idle(enum rscsw_drv_mapping drv_id, uint32_t timeout_us) +{ + uint64_t timer = timeout_init_us(timeout_us); + + while (!rpmh_tcs_is_amc_free(drv_id)) { + if (timeout_elapsed(timer)) + return false; + udelay(1); + } + + return true; +} + +static uint32_t issue_cmd_set_internal(struct client *client, + struct cmd_set_internal *set) +{ + struct rpmh_resource_command temp_rc; + struct rpmh_resource_command *rc; + uint32_t i, req_id, enable_mask; + uint32_t addr, data; + bool completion; + bool dirty = false; + + for (i = 0; i < set->count; i++) { + addr = set->commands[i].address; + data = set->commands[i].data; + completion = set->commands[i].completion; + + rc = rpmh_find_resource_command(addr); + if (!rc) { + rpmh_resource_command_init(&temp_rc, addr); + rc = &temp_rc; + } + + if (rpmh_resource_command_update(rc, set->set, data, + completion, client->drv_id, + false)) + dirty = true; + } + + if (!dirty || set->set != RPMH_SET_ACTIVE) + return 0; + + if (!tcs_is_idle(client->drv_id, 1000)) { + EMSG("TCS idle timeout for drv %u", client->drv_id); + return 0; + } + + req_id = ++client->req_id; + client->num_reqs_in_progress++; + + for (i = 0; i < set->count; i++) { + if (hal_rpmh_write_cmd(client->drv_id, 0, i, + set->commands[i].address, + set->commands[i].data, + set->commands[i].completion) != + HAL_STATUS_SUCCESS) { + EMSG("Failed to write cmd %u addr 0x%x", i, + set->commands[i].address); + return 0; + } + } + + enable_mask = GENMASK_32(set->count - 1, 0); + if (hal_rpmh_send_tcs(client->drv_id, 0, enable_mask) != + HAL_STATUS_SUCCESS) { + EMSG("Failed to send TCS for drv %u", client->drv_id); + return 0; + } + + return req_id; +} + +static struct client *find_client(enum rscsw_drv_mapping drv_id, + const char *name) +{ + struct client *client; + + SLIST_FOREACH(client, &driver_state.queue->handles, link) { + if (client->drv_id == drv_id && !strcmp(client->name, name)) + return client; + } + + return NULL; +} + +static struct client *create_client(enum rscsw_drv_mapping drv_id, + const char *name, bool explicit) +{ + struct client *client = find_client(drv_id, name); + + if (client) + return client; + + client = calloc(1, sizeof(struct client)); + if (!client) + return NULL; + + client->drv_id = drv_id; + client->name = name; + client->req_id = 0; + client->oldest_req_id = 0; + client->num_reqs_in_progress = 0; + + if (explicit) { + client->cmds = calloc(1, sizeof(struct explicit_cmd_set)); + if (!client->cmds) { + free(client); + return NULL; + } + } + + SLIST_INSERT_HEAD(&driver_state.queue->handles, client, link); + return client; +} + +static bool wait_for_cmd(struct client *client, uint32_t req_id, bool all) +{ + uint64_t timer = timeout_init_us(10000); + bool timed_out = false; + + if (req_id == 0) + return true; + + while (true) { + if (all ? client->num_reqs_in_progress == 0 : + (hal_rpmh_is_tcs_idle(client->drv_id, 0, &(bool){false}) == + HAL_STATUS_SUCCESS)) + break; + + if (timeout_elapsed(timer)) { + timed_out = true; + break; + } + + udelay(1); + } + + if (!timed_out && client->num_reqs_in_progress > 0) + client->num_reqs_in_progress--; + + return !timed_out; +} + +static TEE_Result check_aop_init(bool *ready) +{ + vaddr_t base, dict_addr, cookie_addr; + struct aop_msg_ram_dict *dict; + uint64_t timer = timeout_init_us(100000); + uint32_t cookie = 0; + + if (!ready) + return TEE_ERROR_BAD_PARAMETERS; + + *ready = false; + base = (vaddr_t)phys_to_virt(AOP_MSG_RAM_BASE, MEM_AREA_IO_NSEC, + AOP_MSG_RAM_SIZE); + if (!base) { + EMSG("Failed to get VA for AOP message RAM at PA 0x%lx", + (unsigned long)AOP_MSG_RAM_BASE); + return TEE_ERROR_GENERIC; + } + + dict_addr = base + AOP_MSG_RAM_SIZE - MSG_RAM_SECTION_SIZE; + dict = (struct aop_msg_ram_dict *)dict_addr; + cookie_addr = base + dict->boot_cookie_offset; + + while (cookie != AOP_BOOT_COOKIE) { + cookie = io_read32(cookie_addr); + if (cookie == AOP_BOOT_COOKIE) + break; + + if (timeout_elapsed(timer)) { + EMSG("AOP boot timeout after 100ms"); + return TEE_ERROR_BUSY; + } + + udelay(1); + } + + *ready = true; + return TEE_SUCCESS; +} + +static TEE_Result rpmh_client_init(void) +{ + TEE_Result res; + bool ready = false; + + res = check_aop_init(&ready); + if (res != TEE_SUCCESS) { + EMSG("AOP initialization check failed"); + goto err_panic; + } + + driver_state.rsc_base = (vaddr_t)phys_to_virt(RPMH_BASE_ADDR, + MEM_AREA_IO_SEC, + RPMH_RSC_SIZE); + if (!driver_state.rsc_base) { + EMSG("Failed to get VA for RSC base at PA 0x%lx", + (unsigned long)RPMH_BASE_ADDR); + goto err_panic; + } + + driver_state.queue = calloc(1, sizeof(struct client_queue)); + if (!driver_state.queue) { + EMSG("Failed to allocate client queue"); + goto err_panic; + } + + SLIST_INIT(&driver_state.queue->handles); + + if (hal_rpmh_init(driver_state.rsc_base) != HAL_STATUS_SUCCESS || + rpmh_tcs_init() != TEE_SUCCESS) { + EMSG("Failed to initialize HAL/TCS"); + goto err_panic; + } + + return TEE_SUCCESS; + +err_panic: + panic("RPMH driver initialization failed"); +} + +struct rpmh_client *rpmh_create_handle(enum rscsw_drv_mapping drv_id, + const char *name) +{ + struct rpmh_client *handle; + + if (!name || !rpmh_is_drv_id_valid(drv_id)) + return NULL; + + mutex_lock(&driver_state.lock); + handle = (struct rpmh_client *)create_client(drv_id, name, false); + mutex_unlock(&driver_state.lock); + + return handle; +} + +TEE_Result rpmh_issue_command(struct rpmh_client *handle, + enum rpmh_set_enum set, bool completion, + uint32_t address, uint32_t data, + uint32_t *req_id) +{ + struct client *client = (struct client *)handle; + struct cmd_set_internal cmd_set = { }; + uint32_t id; + TEE_Result res = TEE_SUCCESS; + + if (!client || !req_id) + return TEE_ERROR_BAD_PARAMETERS; + + *req_id = 0; + mutex_lock(&driver_state.lock); + + cmd_set.commands[0].address = address; + cmd_set.commands[0].data = data; + cmd_set.commands[0].completion = completion; + cmd_set.set = set; + cmd_set.count = 1; + cmd_set.dependency_bmsk = 0; + + id = issue_cmd_set_internal(client, &cmd_set); + + if (id == 0 || !wait_for_cmd(client, id, false)) { + res = (id == 0) ? TEE_ERROR_GENERIC : TEE_ERROR_BUSY; + goto out; + } + + *req_id = id; + +out: + mutex_unlock(&driver_state.lock); + return res; +} + +void rpmh_barrier_single(struct rpmh_client *handle, uint32_t req_id) +{ + struct client *client = (struct client *)handle; + + if (!client) + return; + + wait_for_cmd(client, req_id, false); +} + +bool rpmh_is_drv_id_valid(enum rscsw_drv_mapping drv_id) +{ + return (drv_id == RSC_DRV_SECURE); +} + +early_init(rpmh_client_init); diff --git a/core/drivers/qcom/rpmh/rpmh_drv_config.c b/core/drivers/qcom/rpmh/rpmh_drv_config.c new file mode 100644 index 0000000000..0b1f7076a5 --- /dev/null +++ b/core/drivers/qcom/rpmh/rpmh_drv_config.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include + +#include "rpmh_drv_config.h" +#include "rpmh_target_config.h" + +static const struct tcs_config tcs_config_tz = { + .amcs = RPMH_TCS_ACTIVE, + .sleep_start = RPMH_TCS_ACTIVE, + .wake_start = RPMH_TCS_ACTIVE + RPMH_TCS_SLEEP, +}; + +static const struct drv_config_data optee_config_data = { + .drvs_count = 1, + .init_clks_count = 0, + .init_clks = NULL, + .sleep_clks_count = 0, + .sleep_clks = NULL, + + .drvs = (struct drv_config[]) { + { + .drv_id = RSC_DRV_SECURE, + .hw_drv = RSC_DRV_SECURE, + .wake_set_latency = 0x7080, + .tcs_offset = 0, + .tcs = RPMH_TCS_ACTIVE + RPMH_TCS_SLEEP + RPMH_TCS_WAKE, + .cmds = RPMH_MAX_CMDS_PER_TCS, + .modes_count = 1, + .modes = (const struct tcs_config *[]) { + &tcs_config_tz, + } + } + } +}; + +const struct drv_config_data *const g_drv_config_data = &optee_config_data; diff --git a/core/drivers/qcom/rpmh/rpmh_drv_config.h b/core/drivers/qcom/rpmh/rpmh_drv_config.h new file mode 100644 index 0000000000..f01714e956 --- /dev/null +++ b/core/drivers/qcom/rpmh/rpmh_drv_config.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __RPMH_DRV_CONFIG_H__ +#define __RPMH_DRV_CONFIG_H__ + +#include +#include + +/* RPMH DRV Configuration */ + +struct tcs_config { + uint32_t amcs; /* Number of AMCs (active TCS) */ + uint32_t sleep_start; /* Index where sleep TCS start */ + uint32_t wake_start; /* Index where wake TCS start */ +}; + +struct drv_config { + enum rscsw_drv_mapping drv_id; + enum rscsw_drv_mapping hw_drv; + uint32_t wake_set_latency; + uint32_t tcs_offset; + uint32_t tcs; + uint32_t cmds; + uint32_t modes_count; + const struct tcs_config **modes; +}; + +struct drv_config_data { + uint32_t drvs_count; + uint32_t init_clks_count; + char **init_clks; + uint32_t sleep_clks_count; + char **sleep_clks; + struct drv_config *drvs; +}; + +extern const struct drv_config_data *const g_drv_config_data; + +#endif /* __RPMH_DRV_CONFIG_H__ */ diff --git a/core/drivers/qcom/rpmh/rpmh_hal.c b/core/drivers/qcom/rpmh/rpmh_hal.c new file mode 100644 index 0000000000..e3d31aed7f --- /dev/null +++ b/core/drivers/qcom/rpmh/rpmh_hal.c @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include + +#include "rpmh_hal.h" + +static struct { + vaddr_t rsc_base; + vaddr_t drv_base[RSC_DRV_MAX]; +} rsc_base_addrs; + +static inline vaddr_t get_drv_base(enum rscsw_drv_mapping drv_id) +{ + return (drv_id >= RSC_DRV_MAX) ? 0 : rsc_base_addrs.drv_base[drv_id]; +} + +static inline vaddr_t get_tcs_base(vaddr_t base, uint32_t tcs_id) +{ + return base + TCS_BASE_OFFSET + (tcs_id * TCS_STRIDE); +} + +static inline uint32_t hal_read32(vaddr_t addr) +{ + return io_read32(addr); +} + +static inline void hal_write32(vaddr_t addr, uint32_t val) +{ + io_write32(addr, val); +} + +enum hal_status hal_rpmh_init(vaddr_t rsc_base) +{ + if (!rsc_base) + return HAL_STATUS_INVALID_PARAM; + + rsc_base_addrs.rsc_base = rsc_base; + + return HAL_STATUS_SUCCESS; +} + +enum hal_status hal_rpmh_register_drv(enum rscsw_drv_mapping drv_id) +{ + if (drv_id >= RSC_DRV_MAX) + return HAL_STATUS_INVALID_PARAM; + + rsc_base_addrs.drv_base[drv_id] = rsc_base_addrs.rsc_base + + (drv_id * DRV_STRIDE); + + return HAL_STATUS_SUCCESS; +} + +enum hal_status hal_rpmh_read_config(enum rscsw_drv_mapping drv_id, + uint32_t *tcs, uint32_t *cmds) +{ + vaddr_t base; + uint32_t config; + + if (!tcs || !cmds) + return HAL_STATUS_INVALID_PARAM; + + base = get_drv_base(drv_id); + if (!base) + return HAL_STATUS_INVALID_PARAM; + + config = hal_read32(base + RSC_DRV_TCS_CONFIG); + *tcs = (config >> 0) & 0xFF; + *cmds = (config >> 8) & 0xFF; + + return HAL_STATUS_SUCCESS; +} + +enum hal_status hal_rpmh_convert_to_amc(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id) +{ + vaddr_t base, tcs_base; + uint32_t control; + + base = get_drv_base(drv_id); + if (!base) + return HAL_STATUS_INVALID_PARAM; + + tcs_base = get_tcs_base(base, tcs_id); + control = hal_read32(tcs_base + TCS_CONTROL_OFFSET); + control |= TCS_CONTROL_AMC_MODE_EN; + hal_write32(tcs_base + TCS_CONTROL_OFFSET, control); + + return HAL_STATUS_SUCCESS; +} + +enum hal_status hal_rpmh_convert_to_tcs(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id) +{ + vaddr_t base, tcs_base; + uint32_t control; + + base = get_drv_base(drv_id); + if (!base) + return HAL_STATUS_INVALID_PARAM; + + tcs_base = get_tcs_base(base, tcs_id); + control = hal_read32(tcs_base + TCS_CONTROL_OFFSET); + control &= ~TCS_CONTROL_AMC_MODE_EN; + hal_write32(tcs_base + TCS_CONTROL_OFFSET, control); + + return HAL_STATUS_SUCCESS; +} + +enum hal_status hal_rpmh_enable_amc_status(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id) +{ + vaddr_t base; + uint32_t enable; + + base = get_drv_base(drv_id); + if (!base) + return HAL_STATUS_INVALID_PARAM; + + enable = hal_read32(base + RSC_DRV_IRQ_ENABLE); + enable |= BIT(tcs_id); + hal_write32(base + RSC_DRV_IRQ_ENABLE, enable); + + return HAL_STATUS_SUCCESS; +} + +enum hal_status hal_rpmh_clear_amc_status(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id) +{ + vaddr_t base; + + base = get_drv_base(drv_id); + if (!base) + return HAL_STATUS_INVALID_PARAM; + + hal_write32(base + RSC_DRV_IRQ_CLEAR, BIT(tcs_id)); + + return HAL_STATUS_SUCCESS; +} + +enum hal_status hal_rpmh_is_tcs_idle(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id, bool *idle) +{ + vaddr_t base, tcs_base; + uint32_t status; + + if (!idle) + return HAL_STATUS_INVALID_PARAM; + + base = get_drv_base(drv_id); + if (!base) + return HAL_STATUS_INVALID_PARAM; + + tcs_base = get_tcs_base(base, tcs_id); + status = hal_read32(tcs_base + TCS_STATUS_OFFSET); + *idle = (status & TCS_STATUS_CONTROLLER_IDLE) != 0; + + return HAL_STATUS_SUCCESS; +} + +enum hal_status hal_rpmh_get_amc_status(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id, + bool *finished) +{ + vaddr_t base; + uint32_t status; + + if (!finished) + return HAL_STATUS_INVALID_PARAM; + + base = get_drv_base(drv_id); + if (!base) + return HAL_STATUS_INVALID_PARAM; + + status = hal_read32(base + RSC_DRV_IRQ_STATUS); + *finished = (status & BIT(tcs_id)) != 0; + + return HAL_STATUS_SUCCESS; +} + +enum hal_status hal_rpmh_send_tcs(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id, + uint32_t enable_mask) +{ + vaddr_t base, tcs_base; + uint32_t control; + + base = get_drv_base(drv_id); + if (!base) + return HAL_STATUS_INVALID_PARAM; + + tcs_base = get_tcs_base(base, tcs_id); + hal_write32(tcs_base + TCS_CMD_ENABLE_OFFSET, enable_mask); + control = hal_read32(tcs_base + TCS_CONTROL_OFFSET); + control |= TCS_CONTROL_AMC_MODE_EN; + control &= ~TCS_CONTROL_AMC_MODE_TRIGGER; + hal_write32(tcs_base + TCS_CONTROL_OFFSET, control); + control |= TCS_CONTROL_AMC_MODE_TRIGGER; + hal_write32(tcs_base + TCS_CONTROL_OFFSET, control); + + return HAL_STATUS_SUCCESS; +} + +enum hal_status hal_rpmh_write_cmd(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id, uint32_t cmd_idx, + uint32_t addr, uint32_t data, + bool completion) +{ + vaddr_t base, tcs_base, cmd_base; + uint32_t msgid, addr_reg; + uint32_t slave_id, offset; + + base = get_drv_base(drv_id); + if (!base) + return HAL_STATUS_INVALID_PARAM; + + tcs_base = get_tcs_base(base, tcs_id); + cmd_base = tcs_base + TCS_CMD_BASE_OFFSET + (cmd_idx * TCS_CMD_STRIDE); + + msgid = 0; + msgid |= (0 << MSGID_READ_OR_WRITE_SHIFT); + msgid |= (completion ? 1 : 0) << MSGID_RES_REQ_SHIFT; + msgid |= (1 << MSGID_MSG_LENGTH_SHIFT); + + slave_id = (addr >> 16) & 0x7; + offset = addr & 0xFFFF; + addr_reg = (slave_id << ADDR_SLV_ID_SHIFT) | + (offset << ADDR_OFFSET_SHIFT); + + hal_write32(cmd_base + TCS_CMDn_MSGID_OFFSET, msgid); + hal_write32(cmd_base + TCS_CMDn_ADDR_OFFSET, addr_reg); + hal_write32(cmd_base + TCS_CMDn_DATA_OFFSET, data); + + return HAL_STATUS_SUCCESS; +} + +enum hal_status hal_rpmh_update_epcb_timeout(enum rscsw_drv_mapping drv_id, + uint32_t threshold) +{ + vaddr_t base; + uint32_t val; + + if (drv_id != RSC_DRV_SECURE) + return HAL_STATUS_INVALID_PARAM; + + base = get_drv_base(drv_id); + if (!base) + return HAL_STATUS_INVALID_PARAM; + + val = hal_read32(base + RSC_DRV_ERROR_IRQ_ENABLE); + val &= ~EPCB_TIMEOUT_THRESHOLD_MASK; + val |= (threshold & EPCB_TIMEOUT_THRESHOLD_MASK) << + EPCB_TIMEOUT_THRESHOLD_SHIFT; + hal_write32(base + RSC_DRV_ERROR_IRQ_ENABLE, val); + + return HAL_STATUS_SUCCESS; +} + +enum hal_status hal_rpmh_toggle_epcb_timeout(enum rscsw_drv_mapping drv_id, + bool enable) +{ + vaddr_t base; + uint32_t val; + + if (drv_id != RSC_DRV_SECURE) + return HAL_STATUS_INVALID_PARAM; + + base = get_drv_base(drv_id); + if (!base) + return HAL_STATUS_INVALID_PARAM; + + val = hal_read32(base + RSC_DRV_ERROR_IRQ_ENABLE); + if (enable) + val |= EPCB_TIMEOUT_IRQ_EN_MASK; + else + val &= ~EPCB_TIMEOUT_IRQ_EN_MASK; + hal_write32(base + RSC_DRV_ERROR_IRQ_ENABLE, val); + + return HAL_STATUS_SUCCESS; +} diff --git a/core/drivers/qcom/rpmh/rpmh_hal.h b/core/drivers/qcom/rpmh/rpmh_hal.h new file mode 100644 index 0000000000..7acbad2ce8 --- /dev/null +++ b/core/drivers/qcom/rpmh/rpmh_hal.h @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __RPMH_HAL_H__ +#define __RPMH_HAL_H__ + +#include +#include +#include +#include + +enum hal_status { + HAL_STATUS_SUCCESS = 0, + HAL_STATUS_ERROR = 1, + HAL_STATUS_INVALID_PARAM = 2, +}; + +#define DRV_STRIDE 0x10000 +#define RSC_DRV_IRQ_ENABLE 0x0d00 +#define RSC_DRV_IRQ_STATUS 0x0d04 +#define RSC_DRV_IRQ_CLEAR 0x0d08 + +#define RSC_DRV_TCS_CONFIG 0x0C +#define TCS_BASE_OFFSET 0x0D10 /* CMD_WAIT_FOR_CMPL base */ +#define TCS_STRIDE 0x2A0 + +#define TCS_CONTROL_OFFSET 0x04 +#define TCS_STATUS_OFFSET 0x08 +#define TCS_CMD_ENABLE_OFFSET 0x0C + +#define TCS_CMD_BASE_OFFSET 0x20 +#define TCS_CMDn_MSGID_OFFSET 0x00 +#define TCS_CMDn_ADDR_OFFSET 0x04 +#define TCS_CMDn_DATA_OFFSET 0x08 +#define TCS_CMD_STRIDE 0x14 + +#define TCS_CONTROL_AMC_MODE_TRIGGER BIT(24) +#define TCS_CONTROL_AMC_MODE_EN BIT(16) + +#define TCS_STATUS_CONTROLLER_IDLE BIT(0) + +#define RSC_DRV_ERROR_IRQ_STATUS 0xD0 +#define RSC_DRV_ERROR_IRQ_ENABLE 0xD8 +#define RSC_DRV_ERROR_IRQ_CLEAR 0xD4 + +#define EPCB_TIMEOUT_IRQ_EN_MASK BIT(20) +#define EPCB_TIMEOUT_THRESHOLD_SHIFT 0x0 +#define EPCB_TIMEOUT_THRESHOLD_MASK 0xFFFF + +#define MSGID_READ_OR_WRITE_SHIFT 0x10 +#define MSGID_RES_REQ_SHIFT 0x8 +#define MSGID_MSG_LENGTH_SHIFT 0x0 + +#define ADDR_SLV_ID_SHIFT 0x10 +#define ADDR_OFFSET_SHIFT 0x0 + +enum hal_status hal_rpmh_init(vaddr_t rsc_base); +enum hal_status hal_rpmh_register_drv(enum rscsw_drv_mapping drv_id); +enum hal_status hal_rpmh_read_config(enum rscsw_drv_mapping drv_id, + uint32_t *tcs, uint32_t *cmds); +enum hal_status hal_rpmh_convert_to_amc(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id); +enum hal_status hal_rpmh_convert_to_tcs(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id); +enum hal_status hal_rpmh_enable_amc_status(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id); +enum hal_status hal_rpmh_clear_amc_status(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id); +enum hal_status hal_rpmh_is_tcs_idle(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id, bool *idle); +enum hal_status hal_rpmh_get_amc_status(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id, + bool *finished); +enum hal_status hal_rpmh_send_tcs(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id, + uint32_t enable_mask); +enum hal_status hal_rpmh_write_cmd(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id, uint32_t cmd_idx, + uint32_t addr, uint32_t data, + bool completion); +enum hal_status hal_rpmh_update_epcb_timeout(enum rscsw_drv_mapping drv_id, + uint32_t threshold); +enum hal_status hal_rpmh_toggle_epcb_timeout(enum rscsw_drv_mapping drv_id, + bool enable); + +#endif /* __RPMH_HAL_H__ */ diff --git a/core/drivers/qcom/rpmh/rpmh_resource_commands.c b/core/drivers/qcom/rpmh/rpmh_resource_commands.c new file mode 100644 index 0000000000..dafc39dc88 --- /dev/null +++ b/core/drivers/qcom/rpmh/rpmh_resource_commands.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include + +#include "rpmh_drv_config.h" +#include "rpmh_resource_commands.h" +#include "rpmh_tcs.h" + +static struct rpmh_resource_command *resources; +static uint32_t resources_count; + +static void +reconcile_vote_state(struct rpmh_resource_command __maybe_unused *rc, + struct drv_votes *drv, bool completion) +{ + struct rpmh_vote *sleep; + struct rpmh_vote *active; + struct rpmh_vote *wake; + + if (!drv) + return; + + sleep = &drv->local_votes[RPMH_SET_SLEEP]; + active = &drv->local_votes[RPMH_SET_ACTIVE]; + wake = &drv->local_votes[RPMH_SET_WAKE]; + + if (active->valid && + (!drv->vote_at_rpmh.valid || + active->data != drv->vote_at_rpmh.data || + (completion && !active->completion))) { + active->dirty = RPMH_RC_DIRTY; + } else { + active->dirty = RPMH_RC_CLEAN; + } + + if (sleep->dirty != RPMH_RC_CLEAN_EXPLICIT_VOTE) { + if (sleep->valid && + (!active->valid || + (active->valid && active->data != sleep->data))) { + sleep->dirty = RPMH_RC_DIRTY; + } else { + sleep->dirty = RPMH_RC_CLEAN; + } + } + + if (wake->dirty != RPMH_RC_CLEAN_EXPLICIT_VOTE) { + if (wake->valid && + ((active->valid && wake->data != active->data) || + (sleep->valid && wake->data != sleep->data))) { + wake->dirty = RPMH_RC_DIRTY; + } else if (!wake->valid && + (active->valid && sleep->valid) && + active->data != sleep->data && + sleep->dirty != RPMH_RC_CLEAN_EXPLICIT_VOTE) { + wake->dirty = RPMH_RC_DIRTY_USE_ACTIVE; + } else { + wake->dirty = RPMH_RC_CLEAN; + } + } +} + +void rpmh_resource_command_init(struct rpmh_resource_command *rc, + uint32_t address) +{ + if (!rc) + return; + + memset(rc, 0, sizeof(*rc)); + rc->address = address; +} + +static struct drv_votes * +rpmh_resource_command_get_voter(struct rpmh_resource_command *rc, + enum rscsw_drv_mapping drv_id) +{ + struct drv_votes *drv = rc->drv_votes; + struct drv_votes *prev = NULL; + + if (!rc) + return NULL; + + while (drv) { + if (drv->drv_id == drv_id) + break; + prev = drv; + drv = drv->next; + } + + if (!drv) { + uint32_t drv_index; + const struct drv_config *config; + uint32_t priority = 0; + TEE_Result res; + + drv = calloc(1, sizeof(struct drv_votes)); + if (!drv) + return NULL; + + drv->drv_id = drv_id; + + if (rpmh_tcs_find_drv_index(drv_id, &drv_index) != + TEE_SUCCESS) { + free(drv); + return NULL; + } + + config = &g_drv_config_data->drvs[drv_index]; + + res = cmd_db_query_priority(rc->address, + (uint8_t)config->hw_drv, + &priority); + if (res != TEE_SUCCESS) { + EMSG("Failed to query priority for addr 0x%x", + rc->address); + free(drv); + return NULL; + } + + drv->priority = (enum rpmh_rc_priority)priority; + + if (!rc->drv_votes) + rc->drv_votes = drv; + else if (prev) + prev->next = drv; + else + return NULL; + } + + return drv; +} + +struct rpmh_resource_command *rpmh_find_resource_command(uint32_t address) +{ + uint32_t low = 0, high = resources_count - 1; + + if (resources_count == 0) + return NULL; + + while (high >= low) { + uint32_t mid = (low + high) / 2; + uint32_t addr = resources[mid].address; + + if (high >= resources_count) + break; + + if (addr < address) + low = mid + 1; + else if (addr > address) + high = mid - 1; + else + return &resources[mid]; + } + + return NULL; +} + +bool rpmh_resource_command_update(struct rpmh_resource_command *rc, + enum rpmh_set_enum set, uint32_t data, + bool completion, + enum rscsw_drv_mapping drv_id, + bool explicit_cmd) +{ + struct drv_votes *drv = rpmh_resource_command_get_voter(rc, drv_id); + + drv->local_votes[set].data = data; + drv->local_votes[set].valid = true; + + if (explicit_cmd && set != RPMH_SET_ACTIVE) + drv->local_votes[set].dirty = RPMH_RC_CLEAN_EXPLICIT_VOTE; + + reconcile_vote_state(rc, drv, + completion && set == RPMH_SET_ACTIVE); + + drv->local_votes[set].completion = completion; + + return (drv->local_votes[set].dirty > RPMH_RC_LAST_CLEAN); +} diff --git a/core/drivers/qcom/rpmh/rpmh_resource_commands.h b/core/drivers/qcom/rpmh/rpmh_resource_commands.h new file mode 100644 index 0000000000..c808befdc8 --- /dev/null +++ b/core/drivers/qcom/rpmh/rpmh_resource_commands.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __RPMH_RESOURCE_COMMANDS_H__ +#define __RPMH_RESOURCE_COMMANDS_H__ + +#include +#include +#include +#include + +enum rpmh_rc_state { + RPMH_RC_STATE_IDLE = 0, + RPMH_RC_STATE_IN_PROGRESS, + RPMH_RC_STATE_MAX +}; + +enum rpmh_rc_dirty { + RPMH_RC_CLEAN = 0, + RPMH_RC_CLEAN_EXPLICIT_VOTE = 1, + RPMH_RC_LAST_CLEAN = 1, + RPMH_RC_DIRTY = 2, + RPMH_RC_DIRTY_MOL = 3, + RPMH_RC_DIRTY_USE_ACTIVE = 4, + RPMH_RC_DIRTY_MAX +}; + +struct rpmh_vote { + uint32_t data; + bool valid; + bool completion; + enum rpmh_rc_dirty dirty; +}; + +enum rpmh_rc_priority { + RPMH_RC_PRIORITY_USE_CASE = 0, + RPMH_RC_PRIORITY_SUBSYSTEM, + RPMH_RC_PRIORITY_MAX +}; + +struct drv_votes { + enum rscsw_drv_mapping drv_id; + enum rpmh_rc_state state; + enum rpmh_rc_priority priority; + struct rpmh_vote vote_at_rpmh; + struct rpmh_vote local_votes[RPMH_NUM_SETS]; + struct drv_votes *next; +}; + +struct rpmh_resource_command { + uint32_t address; + struct drv_votes *drv_votes; +}; + +/* Initialize resource command */ +void rpmh_resource_command_init(struct rpmh_resource_command *rc, + uint32_t address); + +/* Update resource command vote, returns true if set is dirty */ +bool rpmh_resource_command_update(struct rpmh_resource_command *rc, + enum rpmh_set_enum set, + uint32_t data, bool completion, + enum rscsw_drv_mapping drv_id, + bool explicit_cmd); + +/* Find resource command by address */ +struct rpmh_resource_command *rpmh_find_resource_command(uint32_t address); + +#endif /* __RPMH_RESOURCE_COMMANDS_H__ */ diff --git a/core/drivers/qcom/rpmh/rpmh_target_config.h b/core/drivers/qcom/rpmh/rpmh_target_config.h new file mode 100644 index 0000000000..bc9d97a731 --- /dev/null +++ b/core/drivers/qcom/rpmh/rpmh_target_config.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __RPMH_TARGET_CONFIG_H__ +#define __RPMH_TARGET_CONFIG_H__ + +#include + +#define RPMH_TCS_ACTIVE 2 +#define RPMH_TCS_SLEEP 1 +#define RPMH_TCS_WAKE 1 + +#define RPMH_MAX_CMDS_PER_TCS 16 + +#endif /* __RPMH_TARGET_CONFIG_H__ */ diff --git a/core/drivers/qcom/rpmh/rpmh_tcs.c b/core/drivers/qcom/rpmh/rpmh_tcs.c new file mode 100644 index 0000000000..3cff44c8f8 --- /dev/null +++ b/core/drivers/qcom/rpmh/rpmh_tcs.c @@ -0,0 +1,440 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include + +#include "rpmh_drv_config.h" +#include "rpmh_hal.h" +#include "rpmh_tcs.h" + +struct tcs_driver_state { + struct tcs **tcs; + uint32_t *mode; +}; + +static struct tcs_driver_state tcs_driver_state; + +static uint64_t get_timestamp(void) +{ + TEE_Time time; + + tee_time_get_sys_time(&time); + return (uint64_t)time.seconds * 1000000 + time.millis * 1000; +} + +bool rpmh_tcs_drv_valid(enum rscsw_drv_mapping drv_id) +{ + uint32_t i; + + for (i = 0; i < g_drv_config_data->drvs_count; i++) { + if (g_drv_config_data->drvs[i].drv_id == drv_id) + return true; + } + + return false; +} + +TEE_Result rpmh_tcs_find_drv_index(enum rscsw_drv_mapping drv_id, + uint32_t *drv_index) +{ + uint32_t i; + + if (!drv_index) + return TEE_ERROR_BAD_PARAMETERS; + + for (i = 0; i < g_drv_config_data->drvs_count; i++) { + if (g_drv_config_data->drvs[i].drv_id == drv_id) { + *drv_index = i; + return TEE_SUCCESS; + } + } + + return TEE_ERROR_ITEM_NOT_FOUND; +} + +TEE_Result rpmh_tcs_size(enum rscsw_drv_mapping drv_id, uint32_t *size) +{ + uint32_t drv_index; + TEE_Result res; + const struct drv_config *drv; + + if (!size) + return TEE_ERROR_BAD_PARAMETERS; + + res = rpmh_tcs_find_drv_index(drv_id, &drv_index); + if (res) + return res; + + drv = &g_drv_config_data->drvs[drv_index]; + *size = drv->cmds; + + return TEE_SUCCESS; +} + +static void update_mode(uint32_t drv_index, uint32_t mode) +{ + const struct drv_config *drv = &g_drv_config_data->drvs[drv_index]; + uint32_t i; + + if (mode >= drv->modes_count) + mode = 0; + + tcs_driver_state.mode[drv_index] = mode; + + for (i = 0; i < drv->tcs; i++) { + struct tcs *tcs = &tcs_driver_state.tcs[drv_index][i]; + + tcs->id = i + drv->tcs_offset; + tcs->time_last_sent = 0; + hal_rpmh_clear_amc_status(drv->hw_drv, tcs->id); + + if (i < drv->modes[mode]->amcs) { + tcs->state = TCS_AMC_IDLE; + hal_rpmh_convert_to_amc(drv->hw_drv, tcs->id); + } else { + tcs->state = TCS_NON_AMC; + hal_rpmh_convert_to_tcs(drv->hw_drv, tcs->id); + } + } +} + +TEE_Result rpmh_tcs_init(void) +{ + uint32_t i, j, tcs, cmds; + enum hal_status status; + TEE_Result res = TEE_ERROR_OUT_OF_MEMORY; + + tcs_driver_state.tcs = calloc(g_drv_config_data->drvs_count, + sizeof(struct tcs *)); + if (!tcs_driver_state.tcs) + return TEE_ERROR_OUT_OF_MEMORY; + + tcs_driver_state.mode = calloc(g_drv_config_data->drvs_count, + sizeof(uint32_t)); + if (!tcs_driver_state.mode) + goto error_free_tcs; + + for (i = 0; i < g_drv_config_data->drvs_count; i++) { + const struct drv_config *drv = &g_drv_config_data->drvs[i]; + + tcs_driver_state.tcs[i] = calloc(drv->tcs, sizeof(struct tcs)); + if (!tcs_driver_state.tcs[i]) + goto error_free_tcs_array; + + tcs_driver_state.mode[i] = 0; + + if (drv->drv_id >= RSC_DRV_VIRTUAL_DRVS) { + update_mode(i, 0); + continue; + } + + status = hal_rpmh_register_drv(drv->drv_id); + if (status != HAL_STATUS_SUCCESS) + goto error_free_tcs_array_i; + + status = hal_rpmh_read_config(drv->drv_id, &tcs, &cmds); + if (status != HAL_STATUS_SUCCESS) + goto error_free_tcs_array_i; + + hal_rpmh_update_epcb_timeout(drv->drv_id, 0xFFFF); + hal_rpmh_toggle_epcb_timeout(drv->drv_id, true); + update_mode(i, 0); + } + + return TEE_SUCCESS; + +error_free_tcs_array_i: + res = TEE_ERROR_GENERIC; + free(tcs_driver_state.tcs[i]); +error_free_tcs_array: + for (j = 0; j < i; j++) + free(tcs_driver_state.tcs[j]); + free(tcs_driver_state.mode); +error_free_tcs: + free(tcs_driver_state.tcs); + return res; +} + +bool rpmh_tcs_is_amc_free(enum rscsw_drv_mapping drv_id) +{ + uint32_t drv_index; + const struct drv_config *drv; + uint32_t amcs, i; + + if (rpmh_tcs_find_drv_index(drv_id, &drv_index)) + return false; + + drv = &g_drv_config_data->drvs[drv_index]; + amcs = drv->modes[tcs_driver_state.mode[drv_index]]->amcs; + + for (i = 0; i < amcs; i++) { + if (tcs_driver_state.tcs[drv_index][i].state == TCS_AMC_IDLE) + return true; + } + + return false; +} + +bool rpmh_tcs_is_amc_finished(enum rscsw_drv_mapping drv_id) +{ + uint32_t drv_index; + const struct drv_config *drv; + uint32_t amcs, i; + bool finished; + enum hal_status status; + + if (rpmh_tcs_find_drv_index(drv_id, &drv_index)) + return false; + + drv = &g_drv_config_data->drvs[drv_index]; + amcs = drv->modes[tcs_driver_state.mode[drv_index]]->amcs; + + for (i = 0; i < amcs; i++) { + struct tcs *tcs = &tcs_driver_state.tcs[drv_index][i]; + + if (tcs->state != TCS_AMC_WAIT_FOR_DONE) + continue; + + status = hal_rpmh_get_amc_status(drv->hw_drv, + tcs->id, + &finished); + if (status == HAL_STATUS_SUCCESS && finished) + return true; + } + + return false; +} + +TEE_Result rpmh_tcs_send(struct rpmh_cmd *cmd, enum rscsw_drv_mapping drv_id) +{ + uint32_t drv_index; + const struct drv_config *drv; + uint32_t amcs, i, j; + struct tcs *tcs = NULL; + uint32_t enable_mask = 0; + enum hal_status status; + TEE_Result res; + + if (!cmd) + return TEE_ERROR_BAD_PARAMETERS; + + res = rpmh_tcs_find_drv_index(drv_id, &drv_index); + if (res) + return res; + + drv = &g_drv_config_data->drvs[drv_index]; + amcs = drv->modes[tcs_driver_state.mode[drv_index]]->amcs; + + for (i = 0; i < amcs; i++) { + tcs = &tcs_driver_state.tcs[drv_index][i]; + if (tcs->state == TCS_AMC_IDLE) + break; + } + + if (i == amcs) + return TEE_ERROR_BUSY; + + for (j = 0; j < cmd->num_rcs; j++) { + status = hal_rpmh_write_cmd(drv->hw_drv, tcs->id, j, + cmd->details[j].address, + cmd->details[j].data, + cmd->details[j].completion); + if (status != HAL_STATUS_SUCCESS) + return TEE_ERROR_GENERIC; + enable_mask |= BIT(j); + } + + hal_rpmh_clear_amc_status(drv->hw_drv, tcs->id); + status = hal_rpmh_enable_amc_status(drv->hw_drv, tcs->id); + if (status != HAL_STATUS_SUCCESS) + return TEE_ERROR_GENERIC; + + tcs->state = TCS_AMC_WAIT_FOR_DONE; + tcs->time_last_sent = get_timestamp(); + tcs->cmds = cmd; + + status = hal_rpmh_send_tcs(drv->hw_drv, tcs->id, enable_mask); + if (status != HAL_STATUS_SUCCESS) { + tcs->state = TCS_AMC_IDLE; + return TEE_ERROR_GENERIC; + } + + return TEE_SUCCESS; +} + +TEE_Result rpmh_tcs_finish_active_amc(struct rpmh_client **client, + struct rpmh_cmdq *cmdq, + enum rscsw_drv_mapping drv_id, + uint32_t *req_id) +{ + uint32_t drv_index; + const struct drv_config *drv; + uint32_t amcs; + uint32_t i; + bool finished; + enum hal_status status; + TEE_Result res; + + if (!req_id) + return TEE_ERROR_BAD_PARAMETERS; + + *req_id = 0; + + res = rpmh_tcs_find_drv_index(drv_id, &drv_index); + if (res) + return res; + + drv = &g_drv_config_data->drvs[drv_index]; + amcs = drv->modes[tcs_driver_state.mode[drv_index]]->amcs; + + for (i = 0; i < amcs; i++) { + struct tcs *tcs = &tcs_driver_state.tcs[drv_index][i]; + + if (tcs->state != TCS_AMC_WAIT_FOR_DONE) + continue; + + status = hal_rpmh_get_amc_status(drv->hw_drv, + tcs->id, + &finished); + if (status != HAL_STATUS_SUCCESS || !finished) + continue; + + hal_rpmh_clear_amc_status(drv->hw_drv, tcs->id); + + if (tcs->cmds) + *req_id = tcs->cmds->req_id; + + if (client && cmdq && tcs->cmds) + *client = NULL; + + tcs->state = TCS_AMC_IDLE; + tcs->cmds = NULL; + + return TEE_SUCCESS; + } + + return TEE_ERROR_ITEM_NOT_FOUND; +} + +TEE_Result rpmh_tcs_slots_available(enum rscsw_drv_mapping drv_id, + enum rpmh_set_enum set, + uint32_t slots, + uint32_t *tcs_index) +{ + uint32_t drv_index; + const struct drv_config *drv; + const struct tcs_config *mode; + uint32_t start, end, i; + struct tcs *tcs; + TEE_Result res; + + if (!tcs_index) + return TEE_ERROR_BAD_PARAMETERS; + + res = rpmh_tcs_find_drv_index(drv_id, &drv_index); + if (res) + return res; + + drv = &g_drv_config_data->drvs[drv_index]; + mode = drv->modes[tcs_driver_state.mode[drv_index]]; + + if (slots > drv->cmds) + return TEE_ERROR_BAD_PARAMETERS; + + switch (set) { + case RPMH_SET_SLEEP: + start = mode->sleep_start; + end = mode->wake_start; + break; + case RPMH_SET_WAKE: + start = mode->wake_start; + end = drv->tcs; + break; + default: + return TEE_ERROR_BAD_PARAMETERS; + } + + for (i = start; i < end; i++) { + tcs = &tcs_driver_state.tcs[drv_index][i]; + + if (!tcs->cmds) { + *tcs_index = i; + return TEE_SUCCESS; + } + + if (slots <= (drv->cmds - tcs->cmds->num_rcs)) { + *tcs_index = i; + return TEE_SUCCESS; + } + } + + return TEE_ERROR_ITEM_NOT_FOUND; +} + +bool rpmh_tcs_is_stuck(enum rscsw_drv_mapping drv_id) +{ + uint32_t drv_index; + const struct drv_config *drv; + uint32_t amcs, i; + uint64_t current_time; + bool is_idle; + enum hal_status status; + + if (rpmh_tcs_find_drv_index(drv_id, &drv_index)) + return false; + + drv = &g_drv_config_data->drvs[drv_index]; + amcs = drv->modes[tcs_driver_state.mode[drv_index]]->amcs; + current_time = get_timestamp(); + + for (i = 0; i < amcs; i++) { + struct tcs *tcs = &tcs_driver_state.tcs[drv_index][i]; + + status = hal_rpmh_is_tcs_idle(drv->hw_drv, tcs->id, &is_idle); + if (status != HAL_STATUS_SUCCESS) + continue; + + if (is_idle || !tcs->time_last_sent) + continue; + + if (current_time > (tcs->time_last_sent + + TCS_TIMEOUT_THRESHOLD)) + return true; + } + + return false; +} + +TEE_Result rpmh_tcs_get_finished_drv(enum rscsw_drv_mapping hw_drv, + enum rscsw_drv_mapping *finished_drv) +{ + const struct drv_config *drv = g_drv_config_data->drvs; + uint32_t i; + + if (!finished_drv) + return TEE_ERROR_BAD_PARAMETERS; + + if (hw_drv < RSC_DRV_VIRTUAL_DRVS) { + *finished_drv = hw_drv; + return TEE_SUCCESS; + } + + for (i = 0; i < g_drv_config_data->drvs_count; i++, drv++) { + if (drv->hw_drv != hw_drv) + continue; + + if (rpmh_tcs_is_amc_finished(drv->drv_id)) { + *finished_drv = drv->drv_id; + return TEE_SUCCESS; + } + } + + *finished_drv = RSC_DRV_VIRTUAL_MAX; + + return TEE_ERROR_ITEM_NOT_FOUND; +} diff --git a/core/drivers/qcom/rpmh/rpmh_tcs.h b/core/drivers/qcom/rpmh/rpmh_tcs.h new file mode 100644 index 0000000000..a19fd67968 --- /dev/null +++ b/core/drivers/qcom/rpmh/rpmh_tcs.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __RPMH_TCS_H__ +#define __RPMH_TCS_H__ + +#include +#include +#include + +#define TCS_TIMEOUT_THRESHOLD 0x2EE00 + +#define RPMH_SLAVE_ID(address) ((address) >> 16) +#define RPMH_OFFSET(address) ((address) & 0xFFFF) + +struct rpmh_cmd_details { + uint32_t address; + uint32_t data; + bool completion; +}; + +struct rpmh_cmd { + struct rpmh_cmd_details *details; + uint32_t num_rcs; + uint32_t dependency_bmsk; + struct rpmh_client *client; + uint32_t req_id; + enum rpmh_set_enum set; + bool amc; + struct rpmh_cmd *next; +}; + +enum tcs_state { + TCS_AMC_IDLE = 0, + TCS_AMC_WAIT_FOR_DONE = 1, + TCS_NON_AMC = 2, + TCS_NUM_STATES = 3 +}; + +struct tcs { + uint8_t id; + enum tcs_state state; + uint64_t time_last_sent; + struct rpmh_cmd *cmds; +}; + +struct rpmh_client; +struct rpmh_cmdq; + +/* Initialize TCS management */ +TEE_Result rpmh_tcs_init(void); + +/* Check if driver ID is valid */ +bool rpmh_tcs_drv_valid(enum rscsw_drv_mapping drv_id); + +/* Get driver configuration index */ +TEE_Result rpmh_tcs_find_drv_index(enum rscsw_drv_mapping drv_id, + uint32_t *drv_index); + +/* Get number of commands per TCS */ +TEE_Result rpmh_tcs_size(enum rscsw_drv_mapping drv_id, uint32_t *size); + +/* Check if AMC is available */ +bool rpmh_tcs_is_amc_free(enum rscsw_drv_mapping drv_id); + +/* Send command via AMC */ +TEE_Result rpmh_tcs_send(struct rpmh_cmd *cmd, enum rscsw_drv_mapping drv_id); + +/* Check if AMC finished */ +bool rpmh_tcs_is_amc_finished(enum rscsw_drv_mapping drv_id); + +/* Clean up finished AMC */ +TEE_Result rpmh_tcs_finish_active_amc(struct rpmh_client **client, + struct rpmh_cmdq *cmdq, + enum rscsw_drv_mapping drv_id, + uint32_t *req_id); + +/* Check TCS slot availability */ +TEE_Result rpmh_tcs_slots_available(enum rscsw_drv_mapping drv_id, + enum rpmh_set_enum set, + uint32_t slots, + uint32_t *tcs_index); + +/* Check if TCS is stuck */ +bool rpmh_tcs_is_stuck(enum rscsw_drv_mapping drv_id); + +/* Get finished driver ID */ +TEE_Result rpmh_tcs_get_finished_drv(enum rscsw_drv_mapping hw_drv, + enum rscsw_drv_mapping *finished_drv); + +#endif /* __RPMH_TCS_H__ */ diff --git a/core/drivers/qcom/rpmh/sub.mk b/core/drivers/qcom/rpmh/sub.mk new file mode 100644 index 0000000000..5152d9c0ca --- /dev/null +++ b/core/drivers/qcom/rpmh/sub.mk @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# + +# RPMH (Resource Power Manager Hardened) Driver + +# Core RPMH client API +srcs-y += rpmh_client.c + +# RPMH hardware abstraction layer +srcs-y += rpmh_hal.c + +# TCS (Task Control Structure) management +srcs-y += rpmh_tcs.c + +# Resource command tracking and management +srcs-y += rpmh_resource_commands.c + +# DRV configuration +srcs-y += rpmh_drv_config.c diff --git a/core/drivers/qcom/sub.mk b/core/drivers/qcom/sub.mk index 369b68d58b..b7a931564c 100644 --- a/core/drivers/qcom/sub.mk +++ b/core/drivers/qcom/sub.mk @@ -6,3 +6,9 @@ srcs-$(CFG_QCOM_RAMBLUR_PIMEM_V3) += ramblur/ramblur_pimem_v3.c srcs-$(CFG_QCOM_PRNG) += prng/prng.c + +$(eval $(call cfg-depends-all,CFG_QCOM_QFPROM,CFG_QCOM_CMD_DB CFG_QCOM_RPMH_CLIENT)) +$(eval $(call cfg-depends-all,CFG_QCOM_RPMH_CLIENT,CFG_QCOM_CMD_DB)) +subdirs-$(CFG_QCOM_CMD_DB) += cmd_db +subdirs-$(CFG_QCOM_RPMH_CLIENT) += rpmh +subdirs-$(CFG_QCOM_QFPROM) += qfprom diff --git a/core/include/drivers/clk_qcom.h b/core/include/drivers/clk_qcom.h index 334541e5d1..9baac78cb2 100644 --- a/core/include/drivers/clk_qcom.h +++ b/core/include/drivers/clk_qcom.h @@ -9,6 +9,7 @@ #include #include +#include #define REG_POLL_TIMEOUT(_addr, _timeout_us, _delay_us, _retp, _match) \ do { \ @@ -28,8 +29,15 @@ enum qcom_clk_group { QCOM_CLKS_MAX, }; +#define CBCR_BRANCH_ENABLE_BIT BIT(0) +#define CBCR_HW_CTL_ENABLE_BIT BIT(1) +#define CBCR_BRANCH_OFF_BIT BIT(31) +#define CMD_RCGR_UPDATE_BIT BIT(0) + TEE_Result qcom_clock_enable(enum qcom_clk_group group); TEE_Result qcom_clock_enable_cbc(vaddr_t cbcr); +TEE_Result qcom_clock_set_rate(vaddr_t cfg_rcgr, vaddr_t cmd_rcgr, + uint32_t cfg_value); TEE_Result qcom_clock_enable_pas(enum qcom_clk_group group); #endif /* _CLK_QCOM_H_ */ diff --git a/core/include/drivers/qcom/cmd_db/cmd_db.h b/core/include/drivers/qcom/cmd_db/cmd_db.h new file mode 100644 index 0000000000..e9bb1d4264 --- /dev/null +++ b/core/include/drivers/qcom/cmd_db/cmd_db.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __CMD_DB_H__ +#define __CMD_DB_H__ + +#include +#include + +#define CMD_DB_MAX_RES_ID_LEN 8 +#define CMD_DB_DRV_ID_PRIORITY_SZ 2 + +/* Query resource address by resource ID */ +TEE_Result cmd_db_query_addr(const char *res_id, uint32_t *addr); + +/* Query resource priority for driver ID */ +TEE_Result cmd_db_query_priority(uint32_t addr, uint8_t drv_id, + uint32_t *priority); + +#endif /* __CMD_DB_H__ */ diff --git a/core/include/drivers/qcom/qfprom/qfprom.h b/core/include/drivers/qcom/qfprom/qfprom.h new file mode 100644 index 0000000000..0068d32f58 --- /dev/null +++ b/core/include/drivers/qcom/qfprom/qfprom.h @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __QFPROM_H__ +#define __QFPROM_H__ + +#include +#include +#include + +enum qfprom_addr_space { + QFPROM_ADDR_SPACE_RAW = 0, + QFPROM_ADDR_SPACE_CORR = 1, +}; + +enum qfprom_error { + QFPROM_NO_ERR = 0x0, + QFPROM_ERR_UNKNOWN = 0x1, + QFPROM_DATA_PTR_NULL_ERR = 0x2, + QFPROM_ADDRESS_INVALID_ERR = 0x3, + QFPROM_WRITE_ERR = 0x4, + QFPROM_REGION_NOT_SUPPORTED_ERR = 0x5, + QFPROM_REGION_NOT_READABLE_ERR = 0x6, + QFPROM_REGION_NOT_WRITABLE_ERR = 0x7, + QFPROM_FEC_ERR = 0x8, + QFPROM_OPERATION_NOT_ALLOWED_ERR = 0x9, + QFPROM_FAILED_TO_CHANGE_VOLTAGE_ERR = 0xA, + QFPROM_ERROR_CLOCK_FAILED = 0x10, + QFPROM_ERROR_TIMEOUT = 0x11, +}; + +/* Read QFPROM row data */ +TEE_Result qfprom_read_row(uint32_t addr, + enum qfprom_addr_space type, + uint32_t *data); + +/* Write QFPROM row data */ +TEE_Result qfprom_write_row(uint32_t addr, uint32_t *data); + +/* Check if row has FEC protection */ +TEE_Result qfprom_row_has_fec_bits(uint32_t addr, + enum qfprom_addr_space type, + uint8_t *has_fec); + +/* Calculate FEC bits for 56-bit data */ +uint32_t qfprom_fec_63_56_bit(uint32_t lsb_data, uint32_t msb_data); + +/* Hardware init/deinit for batch fuse operations */ +TEE_Result qfprom_hw_init(void); +void qfprom_hw_deinit(void); + +#endif /* __QFPROM_H__ */ diff --git a/core/include/drivers/qcom/rpmh/rpmh_client.h b/core/include/drivers/qcom/rpmh/rpmh_client.h new file mode 100644 index 0000000000..a275dab1c1 --- /dev/null +++ b/core/include/drivers/qcom/rpmh/rpmh_client.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __RPMH_CLIENT_H__ +#define __RPMH_CLIENT_H__ + +#include +#include +#include + +#define RPMH_MAX_TCS_SIZE 16 + +enum rpmh_set_enum { + RPMH_SET_ACTIVE = 0, + RPMH_SET_SLEEP = 1, + RPMH_SET_WAKE = 2, + RPMH_NUM_SETS = 3, +}; + +enum rscsw_drv_mapping { + RSC_DRV_SECURE = 0, + RSC_DRV_CPUCP = 1, + RSC_DRV_L3 = RSC_DRV_CPUCP, + RSC_DRV_HLOS = 2, + RSC_DRV_HYP = 3, + RSC_DRV_MAX, + + /* Virtual DRVs */ + RSC_DRV_VIRTUAL_DRVS = 0x3FFFFF00, + RSC_DRV_VIRTUAL_SENSORS, + RSC_DRV_VIRTUAL_MAX = 0x3FFFFFFF, +}; + +struct rpmh_command { + uint32_t address; + uint32_t data; + bool completion; +}; + +struct rpmh_client; + +/* Create RPMH client handle */ +struct rpmh_client *rpmh_create_handle(enum rscsw_drv_mapping drv_id, + const char *name); + +/* Issue command to RPMH resource */ +TEE_Result rpmh_issue_command(struct rpmh_client *handle, + enum rpmh_set_enum set, + bool completion, uint32_t address, + uint32_t data, uint32_t *req_id); + +/* Wait for command completion */ +void rpmh_barrier_single(struct rpmh_client *handle, uint32_t req_id); + +/* Check if driver ID is valid */ +bool rpmh_is_drv_id_valid(enum rscsw_drv_mapping drv_id); + +#endif /* __RPMH_CLIENT_H__ */