diff --git a/MAINTAINERS b/MAINTAINERS index 3c01ffde3..b2ddf43a1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -146,6 +146,11 @@ S: Maintained F: core/drivers/qcom/cmd_db/ F: core/include/drivers/qcom/cmd_db/ +Core Drivers QCOM HWKM +R: Amirreza Zarrabi [@qc-azarrabi] +S: Maintained +F: core/drivers/crypto/qcom/ + Core Drivers QCOM QFPROM R: Selvam Sathappan Periakaruppan [@zelvam95] R: Kishore Batta [@kishorebatta-ossqcom] diff --git a/core/arch/arm/plat-qcom/bobcat/ipq52xx/target.mk b/core/arch/arm/plat-qcom/bobcat/ipq52xx/target.mk index 4a226d28b..0ec62ba96 100644 --- a/core/arch/arm/plat-qcom/bobcat/ipq52xx/target.mk +++ b/core/arch/arm/plat-qcom/bobcat/ipq52xx/target.mk @@ -6,3 +6,10 @@ CFG_QCOM_DIAG_LOG ?= $(CFG_TEE_CORE_DEBUG) CFG_TZDRAM_START ?= 0x87D80000 CFG_TZDRAM_SIZE ?= 0x280000 CFG_TEE_RAM_VA_SIZE ?= 0x280000 + +$(call force,CFG_QCOM_CSRNG,y) +$(call force,CFG_WITH_SOFTWARE_PRNG,n) + +CFG_HWRNG_PTA ?= y +CFG_HWRNG_QUALITY ?= 1024 +CFG_HWRNG_RATE ?= 0 diff --git a/core/arch/arm/plat-qcom/bobcat/ipq52xx/target_config.h b/core/arch/arm/plat-qcom/bobcat/ipq52xx/target_config.h index 17008a0a0..7269d7d85 100644 --- a/core/arch/arm/plat-qcom/bobcat/ipq52xx/target_config.h +++ b/core/arch/arm/plat-qcom/bobcat/ipq52xx/target_config.h @@ -20,4 +20,9 @@ #define IMEM_BASE UL(0x8600000) #define IMEM_SIZE UL(0x18000) +#define QCOM_RNG_REG_BASE UL(0x4C5000) + +#define QCOM_WDT_TMR_BASE UL(0x0B117000) +#define QCOM_WDT_BARK_INT_ID UL(0x23) + #endif /* TARGET_CONFIG_H */ diff --git a/core/arch/arm/plat-qcom/bobcat/ipq96xx/target_config.h b/core/arch/arm/plat-qcom/bobcat/ipq96xx/target_config.h index b3eb10732..50697cfd8 100644 --- a/core/arch/arm/plat-qcom/bobcat/ipq96xx/target_config.h +++ b/core/arch/arm/plat-qcom/bobcat/ipq96xx/target_config.h @@ -19,4 +19,7 @@ #define IMEM_BASE UL(0x8600000) #define IMEM_SIZE UL(0x20000) +#define QCOM_WDT_TMR_BASE UL(0x0F411000) +#define QCOM_WDT_BARK_INT_ID UL(0x36) + #endif /* TARGET_CONFIG_H */ diff --git a/core/arch/arm/plat-qcom/bobcat/qcom-arch.mk b/core/arch/arm/plat-qcom/bobcat/qcom-arch.mk new file mode 100644 index 000000000..a0544ac11 --- /dev/null +++ b/core/arch/arm/plat-qcom/bobcat/qcom-arch.mk @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: BSD-2-Clause +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + +CFG_QCOM_SEC_WDOG ?= y + +# XPU protection for TZDRAM and the OP-TEE DIAG log buffer +CFG_QCOM_XPUV4 ?= y diff --git a/core/arch/arm/plat-qcom/hoya/arch_config.h b/core/arch/arm/plat-qcom/hoya/arch_config.h index 8848825cb..11fc5ab44 100644 --- a/core/arch/arm/plat-qcom/hoya/arch_config.h +++ b/core/arch/arm/plat-qcom/hoya/arch_config.h @@ -11,7 +11,7 @@ #define GICR_BASE UL(0x17a60000) #define RAMBLUR_PIMEM_REG_BASE UL(0x610000) -#define SEC_PRNG_REG_BASE UL(0x010D1000) +#define QCOM_RNG_REG_BASE UL(0x010D1000) #define AOP_MSG_RAM_BASE UL(0x0C300000) #define AOP_MSG_RAM_SIZE UL(0x00100000) @@ -25,6 +25,9 @@ #define TCSR_MUTEX_BASE UL(0x01F40000) #define TCSR_MUTEX_SIZE UL(0x40000) +#define HWKM_MASTER_BASE UL(0x010c0000) +#define HWKM_MASTER_SIZE UL(0x00020000) + #define IMEM_DIAG_OFFSET UL(0x720) #define DIAG_SIZE UL(0x3000) #define DIAG_BASE (IMEM_BASE + IMEM_SIZE - DIAG_SIZE) diff --git a/core/arch/arm/plat-qcom/hoya/lemans/target.mk b/core/arch/arm/plat-qcom/hoya/lemans/target.mk index c339ac0e5..95c04859f 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target.mk +++ b/core/arch/arm/plat-qcom/hoya/lemans/target.mk @@ -12,3 +12,15 @@ $(call force,CFG_QCOM_CMD_DB,y) $(call force,CFG_QCOM_RPMH_CLIENT,y) $(call force,CFG_QCOM_QFPROM,y) endif + +CFG_QCOM_PAS_PTA ?= y + +ifeq ($(CFG_QCOM_PAS_PTA),y) +CFG_RESERVED_VASPACE_SIZE ?= (256 * 1024 * 1024) +CFG_IN_TREE_EARLY_TAS += qcom_pas/cff7d191-7ca0-4784-af13-48223b9a4fbe + +CFG_QCOM_PAS_AUTH ?= y + +CFG_PAS_MD_SLOTS ?= 8 +endif +CFG_QCOM_HWKM ?= y diff --git a/core/arch/arm/plat-qcom/hoya/lemans/target_config.h b/core/arch/arm/plat-qcom/hoya/lemans/target_config.h index fda237478..757c67c61 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target_config.h +++ b/core/arch/arm/plat-qcom/hoya/lemans/target_config.h @@ -30,4 +30,68 @@ #define IMEM_BASE UL(0x14680000) #define IMEM_SIZE UL(0x32000) +#define TURING_0_BASE UL(0x24000000) +#define TURING_0_SIZE UL(0x03000000) + +#define TURING_1_BASE UL(0x28000000) +#define TURING_1_SIZE UL(0x03000000) + +/* + * GP-DSP0 / GP-DSP1 (TURINGGDSP / TURINGGDSP1) subsystems. The window covers + * every GP-DSP sub-block the PTA and clock driver touch (GDSP_CC 0x808000, PUB + * 0xc00000, PLL 0xc40000, CORE_CC 0xc48000). + */ +#define TURING_GDSP_0_BASE UL(0x20000000) +#define TURING_GDSP_0_SIZE UL(0x01000000) + +#define TURING_GDSP_1_BASE UL(0x21000000) +#define TURING_GDSP_1_SIZE UL(0x01000000) + +/* + * LPASS / ADSP (QDSP6 v68/v69) subsystem window, covering every sub-block the + * PTA and clock driver touch (PUB, PLL, CORE_CC, AON_CC, MCC, TOP_CC). + */ +#define LPASS_BASE UL(0x02c00000) +#define LPASS_SIZE ULL(0x01080000) + +/* + * IRIS video-codec subsystem. The window covers the VCODEC_IRIS_WRAPPER_TOP + * (IRIS+0xb0000) and WRAPPER_TZ (IRIS+0xc0000) blocks the bring-up path + * touches. + */ +#define IRIS_BASE UL(0x0aa00000) +#define IRIS_SIZE ULL(0x00200000) + +/* CDSP0 content-protection shared channel (secure DDR); no CDSP1 equivalent. */ +#define CDSP_SECCHANNEL_BASE UL(0xdb1dc000) +#define CDSP_SECCHANNEL_SIZE UL(0x2000) + +/* + * Global register blocks for the Turing/NSP reset sequence, under AOSS_BASE + * (0x0b000000). TCSR_MUTEX_BASE/SIZE is already defined in arch_config.h. + */ +#define AOSS_CC_BASE UL(0x0c2a8000) +#define AOSS_CC_SIZE UL(0x00050000) + +#define RPMH_PDC_GLOBAL_BASE UL(0x0b5e0000) +#define RPMH_PDC_GLOBAL_SIZE UL(0x00002000) + +#define RPMH_PDC_COMPUTE_BASE UL(0x0b2c0000) +#define RPMH_PDC_COMPUTE_SIZE UL(0x00002000) + +#define RPMH_PDC_NSP_BASE UL(0x0b2f0000) +#define RPMH_PDC_NSP_SIZE UL(0x00002000) + +/* + * RPMH PDC blocks for the GP-DSP subsystem reset sequence. These live under + * AOSS_BASE (0x0b000000), at offsets 0x240000 / 0x2d0000. + */ +#define RPMH_PDC_GPDSP0_BASE UL(0x0b240000) +#define RPMH_PDC_GPDSP0_SIZE UL(0x00002000) + +#define RPMH_PDC_GPDSP1_BASE UL(0x0b2d0000) +#define RPMH_PDC_GPDSP1_SIZE UL(0x00002000) + +#define TITAN_SS_BASE UL(0x0ac00000) +#define TITAN_SS_SIZE UL(0x00200000) #endif /* TARGET_CONFIG_H */ diff --git a/core/arch/arm/plat-qcom/hoya/qcom-arch.mk b/core/arch/arm/plat-qcom/hoya/qcom-arch.mk index 59e372983..091471168 100644 --- a/core/arch/arm/plat-qcom/hoya/qcom-arch.mk +++ b/core/arch/arm/plat-qcom/hoya/qcom-arch.mk @@ -6,7 +6,8 @@ $(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) +$(call force,CFG_QCOM_CSRNG,y) +$(call force,CFG_WITH_SOFTWARE_PRNG,n) $(call force,CFG_ARM_GICV3,y) CFG_TZDRAM_START ?= 0x1c300000 diff --git a/core/arch/arm/plat-qcom/wildcat/arch_config.h b/core/arch/arm/plat-qcom/wildcat/arch_config.h index 1ee2c9ae2..638772cc4 100644 --- a/core/arch/arm/plat-qcom/wildcat/arch_config.h +++ b/core/arch/arm/plat-qcom/wildcat/arch_config.h @@ -9,4 +9,19 @@ #define GICD_BASE UL(0x17000000) #define GICR_BASE UL(0x17080000) +#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(0xC0000) + #endif /* ARCH_CONFIG_H */ diff --git a/core/arch/arm/plat-qcom/wildcat/nord/target.mk b/core/arch/arm/plat-qcom/wildcat/nord/target.mk index d3a3f2329..85c9e5711 100644 --- a/core/arch/arm/plat-qcom/wildcat/nord/target.mk +++ b/core/arch/arm/plat-qcom/wildcat/nord/target.mk @@ -3,3 +3,25 @@ # Threads are expensive in OP-TEE, so they don't have # to be same as number of cores. $(call force,CFG_TEE_CORE_NB_CORE,18) + +CFG_DRIVERS_CLK ?= y +CFG_DRIVERS_QCOM_CLK ?= y + +ifneq ($(CFG_INSECURE),y) +CFG_QCOM_QFPROM_FUSEPROV ?= y +endif + +ifeq ($(CFG_QCOM_QFPROM_FUSEPROV),y) +$(call force,CFG_QCOM_CMD_DB,y) +$(call force,CFG_QCOM_RPMH_CLIENT,y) +$(call force,CFG_QCOM_QFPROM,y) +endif + +CFG_QCOM_PAS_PTA ?= y + +ifeq ($(CFG_QCOM_PAS_PTA),y) +CFG_QCOM_PAS_AUTH ?= y +CFG_PAS_MD_SLOTS ?= 8 +CFG_RESERVED_VASPACE_SIZE ?= (64 * 1024 * 1024) +CFG_IN_TREE_EARLY_TAS += qcom_pas/cff7d191-7ca0-4784-af13-48223b9a4fbe +endif diff --git a/core/arch/arm/plat-qcom/wildcat/nord/target_config.h b/core/arch/arm/plat-qcom/wildcat/nord/target_config.h index 5e0c0fad2..0b6aa1bce 100644 --- a/core/arch/arm/plat-qcom/wildcat/nord/target_config.h +++ b/core/arch/arm/plat-qcom/wildcat/nord/target_config.h @@ -6,11 +6,77 @@ #ifndef TARGET_CONFIG_H #define TARGET_CONFIG_H -#define GENI_UART_REG_BASE UL(0x884000) +#define GCC_BASE UL(0x110000) +#define GCC_SIZE UL(0x100000) + +#define AOP_CMD_DB_BASE UL(0x90860000) +#define AOP_CMD_DB_SIZE UL(0x00020000) + +#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 GENI_UART_REG_BASE UL(0x884000) + +#define IMEM_BASE UL(0x14680000) +#define IMEM_SIZE UL(0x32000) + +#define CDSP_0_BASE UL(0x1f0c8000) +#define CDSP_0_SIZE UL(0x00280000) + +#define CDSP_1_BASE UL(0x1f4c8000) +#define CDSP_1_SIZE UL(0x00280000) + +#define CDSP_2_BASE UL(0x1f8c8000) +#define CDSP_2_SIZE UL(0x00280000) + +#define CDSP_3_BASE UL(0x1fcc8000) +#define CDSP_3_SIZE UL(0x00280000) + +#define HPASS_0_BASE UL(0x04c00000) +#define HPASS_0_SIZE UL(0x00100000) + +#define HPASS_1_BASE UL(0x06000000) +#define HPASS_1_SIZE UL(0x00100000) + +#define HPASS_2_BASE UL(0x06100000) +#define HPASS_2_SIZE UL(0x00100000) + +#define HPASS_CC_BASE UL(0x05508000) +#define HPASS_CC_SIZE UL(0x00020000) + +#define HPASS_TCM_BASE UL(0x05700000) +#define HPASS_TCM_SIZE UL(0x00001000) + +#define HPASS_TCSR_BASE UL(0x05580000) +#define HPASS_TCSR_SIZE UL(0x00020000) + +#define SE_GCC_BASE UL(0x08a10000) +#define SE_GCC_SIZE UL(0x00013000) + +#define SOCCP_CSR_BASE UL(0x00d40000) +#define SOCCP_CSR_SIZE UL(0x00040000) + +#define QDSS_SOCCP_DMI_BASE UL(0x11508000) +#define QDSS_SOCCP_DMI_SIZE UL(0x00001000) + +#define AOSS_CC_BASE UL(0x0c2f0000) +#define AOSS_CC_SIZE UL(0x00010000) + +#define RPMH_PDC_GLOBAL_BASE UL(0x0b5e6000) +#define RPMH_PDC_GLOBAL_SIZE UL(0x0000a000) + +#define FUSE_CONTROLLER_SW_RANGE4_BASE UL(0x360d4000) +#define FUSE_CONTROLLER_SW_RANGE4_SIZE UL(0x00001000) + +/* + * IRIS video-codec subsystem. + */ +#define IRIS_BASE UL(0x0aa00000) +#define IRIS_SIZE ULL(0x00200000) + #endif /* TARGET_CONFIG_H */ diff --git a/core/drivers/clk/qcom/clock-qcom.c b/core/drivers/clk/qcom/clock-qcom.c index b1e9c6fe2..fac4ea1ee 100644 --- a/core/drivers/clk/qcom/clock-qcom.c +++ b/core/drivers/clk/qcom/clock-qcom.c @@ -15,6 +15,56 @@ register_phys_mem(MEM_AREA_IO_NSEC, GCC_BASE, GCC_SIZE); #define CBCR_HW_CTL_ENABLE_BIT BIT(1) #define CBCR_BRANCH_OFF_BIT BIT(31) +/* Lucid-EVO PLL register offsets, relative to the PLL register block base. */ +#define PLL_MODE 0x0 +#define PLL_OPMODE 0x4 +#define PLL_L_VAL 0x10 +#define PLL_ALPHA_VAL 0x14 +#define PLL_USER_CTL 0x18 +#define PLL_USER_CTL_U 0x1c +#define PLL_CONFIG_CTL 0x20 +#define PLL_CONFIG_CTL_U 0x24 +#define PLL_CONFIG_CTL_U1 0x28 +#define PLL_TEST_CTL 0x2c +#define PLL_TEST_CTL_U 0x30 +#define PLL_TEST_CTL_U1 0x34 +#define PLL_TEST_CTL_U2 0x38 + +/* PLL_MODE fields */ +#define PLL_MODE_OUTCTRL BIT(0) +#define PLL_MODE_RESET_N BIT(2) +#define PLL_MODE_LOCK_DET BIT(31) + +/* PLL_OPMODE values */ +#define PLL_OPMODE_RUN 0x1 + +/* PLL_L_VAL fields */ +#define PLL_L_VAL_L_MASK 0x0000ffff +#define PLL_L_VAL_CAL_L_SHIFT 16 +#define PLL_L_VAL_CAL_L_MASK 0xffff0000 + +/* + * Lucid-OLE PLL_L_VAL fields, packed differently from Lucid-EVO: an 8-bit + * L value (bits 0-7) and an 8-bit process-cal value (bits 16-23), at the + * same register offset as EVO's 16-bit L (bits 0-15) + 16-bit cal (bits + * 16-31) fields. Bits 24-31 (ring-osc cal) are not written by + * HAL_clk_LucidolePLLConfigPLL/SetCalConfig and are left alone here too. + */ +#define PLL_OLE_L_VAL_L_MASK 0x000000ff +#define PLL_OLE_L_VAL_PROCESS_CAL_SHIFT 16 +#define PLL_OLE_L_VAL_PROCESS_CAL_MASK 0x00ff0000 + +/* PLL_USER_CTL fields */ +#define PLL_USER_CTL_PLLOUT_MAIN_EN BIT(0) +#define PLL_USER_CTL_PRE_DIV_SHIFT 22 +#define PLL_USER_CTL_PRE_DIV_MASK 0x01c00000 +#define PLL_USER_CTL_POST_DIV_ODD_MASK 0x0003c000 +#define PLL_USER_CTL_POST_DIV_EVEN_MASK 0x00003c00 +#define PLL_USER_CTL_FRAC_FORMAT_SEL BIT(28) + +/* PLL_USER_CTL_U fields */ +#define PLL_USER_CTL_U_FINE_LOCK_DET BIT(0) + static inline bool cbcr_branch_on(uint32_t val) { return !(val & CBCR_BRANCH_OFF_BIT); @@ -26,6 +76,14 @@ TEE_Result qcom_clock_enable_cbc(vaddr_t cbcr) io_setbits32(cbcr, CBCR_BRANCH_ENABLE_BIT); + /* + * In hardware clock-control mode (HW_CTL set) CLK_OFF is driven by HW, + * not the software CLK_ENABLE write, so skip the poll to avoid + * spinning. + */ + if (io_read32(cbcr) & CBCR_HW_CTL_ENABLE_BIT) + return TEE_SUCCESS; + REG_POLL_TIMEOUT(cbcr, 10 * 1000, 10, &ret, cbcr_branch_on); if (ret < 0) @@ -34,6 +92,129 @@ TEE_Result qcom_clock_enable_cbc(vaddr_t cbcr) return TEE_SUCCESS; } +static inline bool pll_locked(uint32_t val) +{ + return val & PLL_MODE_LOCK_DET; +} + +TEE_Result qcom_lucidevo_pll_enable(vaddr_t pll_base, + const struct qcom_lucidevo_pll_config *cfg) +{ + uint32_t user_val = 0; + int ret = 0; + + /* Reg settings: program the static PLL trim/config registers. */ + io_write32(pll_base + PLL_CONFIG_CTL, cfg->config_ctl); + io_write32(pll_base + PLL_CONFIG_CTL_U, cfg->config_ctl_u); + io_write32(pll_base + PLL_CONFIG_CTL_U1, cfg->config_ctl_u1); + io_write32(pll_base + PLL_USER_CTL, cfg->user_ctl); + io_write32(pll_base + PLL_USER_CTL_U, cfg->user_ctl_u); + + /* ConfigPLL: program L value and fractional value. */ + io_mask32(pll_base + PLL_L_VAL, cfg->l_val, PLL_L_VAL_L_MASK); + io_write32(pll_base + PLL_ALPHA_VAL, cfg->alpha_val); + + /* Select fractional format and program the pre-/post-div ratios. */ + user_val = io_read32(pll_base + PLL_USER_CTL); + if (cfg->frac_mode_mn) + user_val |= PLL_USER_CTL_FRAC_FORMAT_SEL; + else + user_val &= ~PLL_USER_CTL_FRAC_FORMAT_SEL; + + user_val &= ~(PLL_USER_CTL_PRE_DIV_MASK | + PLL_USER_CTL_POST_DIV_ODD_MASK | + PLL_USER_CTL_POST_DIV_EVEN_MASK); + if (cfg->pre_div >= 1 && cfg->pre_div <= 8) + user_val |= SHIFT_U32(cfg->pre_div - 1, + PLL_USER_CTL_PRE_DIV_SHIFT) & + PLL_USER_CTL_PRE_DIV_MASK; + io_write32(pll_base + PLL_USER_CTL, user_val); + + /* Always use fine-grained lock detection. */ + io_setbits32(pll_base + PLL_USER_CTL_U, PLL_USER_CTL_U_FINE_LOCK_DET); + + /* SetCalConfig: program the calibration L value. */ + io_mask32(pll_base + PLL_L_VAL, + SHIFT_U32(cfg->cal_l_val, PLL_L_VAL_CAL_L_SHIFT), + PLL_L_VAL_CAL_L_MASK); + + /* Enable: select RUN opmode and take the PLL out of reset. */ + io_write32(pll_base + PLL_OPMODE, PLL_OPMODE_RUN); + io_setbits32(pll_base + PLL_MODE, PLL_MODE_RESET_N); + + /* Wait for the PLL to lock. */ + REG_POLL_TIMEOUT(pll_base + PLL_MODE, 10 * 1000, 10, &ret, pll_locked); + if (ret < 0) + return TEE_ERROR_TIMEOUT; + + /* Enable PLL outputs and the main output. */ + io_setbits32(pll_base + PLL_MODE, PLL_MODE_OUTCTRL); + io_setbits32(pll_base + PLL_USER_CTL, PLL_USER_CTL_PLLOUT_MAIN_EN); + + return TEE_SUCCESS; +} + +TEE_Result qcom_lucidole_pll_enable(vaddr_t pll_base, + const struct qcom_lucidole_pll_config *cfg) +{ + uint32_t user_val = 0; + int ret = 0; + + /* Reg settings: program the static PLL trim/config registers. */ + io_write32(pll_base + PLL_CONFIG_CTL, cfg->config_ctl); + io_write32(pll_base + PLL_CONFIG_CTL_U, cfg->config_ctl_u); + io_write32(pll_base + PLL_CONFIG_CTL_U1, cfg->config_ctl_u1); + io_write32(pll_base + PLL_TEST_CTL, cfg->test_ctl); + io_write32(pll_base + PLL_TEST_CTL_U, cfg->test_ctl_u); + io_write32(pll_base + PLL_TEST_CTL_U1, cfg->test_ctl_u1); + io_write32(pll_base + PLL_TEST_CTL_U2, cfg->test_ctl_u2); + io_write32(pll_base + PLL_USER_CTL, cfg->user_ctl); + io_write32(pll_base + PLL_USER_CTL_U, cfg->user_ctl_u); + + /* ConfigPLL: program L value and fractional value. */ + io_mask32(pll_base + PLL_L_VAL, cfg->l_val, PLL_OLE_L_VAL_L_MASK); + io_write32(pll_base + PLL_ALPHA_VAL, cfg->alpha_val); + + /* Select fractional format and program the pre-/post-div ratios. */ + user_val = io_read32(pll_base + PLL_USER_CTL); + if (cfg->frac_mode_mn) + user_val |= PLL_USER_CTL_FRAC_FORMAT_SEL; + else + user_val &= ~PLL_USER_CTL_FRAC_FORMAT_SEL; + + user_val &= ~(PLL_USER_CTL_PRE_DIV_MASK | + PLL_USER_CTL_POST_DIV_ODD_MASK | + PLL_USER_CTL_POST_DIV_EVEN_MASK); + if (cfg->pre_div >= 1 && cfg->pre_div <= 8) + user_val |= SHIFT_U32(cfg->pre_div - 1, + PLL_USER_CTL_PRE_DIV_SHIFT) & + PLL_USER_CTL_PRE_DIV_MASK; + io_write32(pll_base + PLL_USER_CTL, user_val); + + /* Always use fine-grained lock detection. */ + io_setbits32(pll_base + PLL_USER_CTL_U, PLL_USER_CTL_U_FINE_LOCK_DET); + + /* SetCalConfig: program the process-cal L value. */ + io_mask32(pll_base + PLL_L_VAL, + SHIFT_U32(cfg->cal_l_val, PLL_OLE_L_VAL_PROCESS_CAL_SHIFT), + PLL_OLE_L_VAL_PROCESS_CAL_MASK); + + /* Enable: select RUN opmode and take the PLL out of reset. */ + io_write32(pll_base + PLL_OPMODE, PLL_OPMODE_RUN); + io_setbits32(pll_base + PLL_MODE, PLL_MODE_RESET_N); + + /* Wait for the PLL to lock. */ + REG_POLL_TIMEOUT(pll_base + PLL_MODE, 10 * 1000, 10, &ret, pll_locked); + if (ret < 0) + return TEE_ERROR_TIMEOUT; + + /* Enable PLL outputs and the main output. */ + io_setbits32(pll_base + PLL_MODE, PLL_MODE_OUTCTRL); + io_setbits32(pll_base + PLL_USER_CTL, PLL_USER_CTL_PLLOUT_MAIN_EN); + + return TEE_SUCCESS; +} + TEE_Result qcom_clock_set_rate(vaddr_t cfg_rcgr, vaddr_t cmd_rcgr, uint32_t cfg_value) { @@ -53,8 +234,17 @@ TEE_Result qcom_clock_enable(enum qcom_clk_group group) { switch (group) { case QCOM_CLKS_TURING: + case QCOM_CLKS_TURING1: + case QCOM_CLKS_TURING2: + case QCOM_CLKS_TURING3: case QCOM_CLKS_LPASS: case QCOM_CLKS_WPSS: + case QCOM_CLKS_GPDSP0: + case QCOM_CLKS_GPDSP1: + case QCOM_CLKS_SOCCP: + case QCOM_CLKS_HPASS0: + case QCOM_CLKS_HPASS1: + case QCOM_CLKS_HPASS2: return qcom_clock_enable_pas(group); default: EMSG("Unsupported clock group %d\n", group); diff --git a/core/drivers/clk/qcom/clock-qcom-pas.c b/core/drivers/clk/qcom/platform/kodiak/clock-qcom-pas.c similarity index 92% rename from core/drivers/clk/qcom/clock-qcom-pas.c rename to core/drivers/clk/qcom/platform/kodiak/clock-qcom-pas.c index d664beef3..2aa66ca28 100644 --- a/core/drivers/clk/qcom/clock-qcom-pas.c +++ b/core/drivers/clk/qcom/platform/kodiak/clock-qcom-pas.c @@ -6,8 +6,6 @@ #include #include - -#ifdef CFG_QCOM_PAS_PTA #include #include #include @@ -122,6 +120,18 @@ static int lpass_gdsc_enable(void) return qcom_clock_enable_cbc(gdsc_base + TOP_CC_LPI_Q6_AXIM_HS_CLK); } +TEE_Result qcom_clock_enable_pas_processor(enum qcom_clk_group group __unused) +{ + /* The DSP core is released entirely within the PTA fw_start path. */ + return TEE_SUCCESS; +} + +TEE_Result qcom_clock_pas_reset(enum qcom_clk_group group __unused) +{ + /* The PTA fw_start path brings the DSP up from a known state. */ + return TEE_SUCCESS; +} + TEE_Result qcom_clock_enable_pas(enum qcom_clk_group group) { struct io_pa_va base = { .pa = GCC_BASE }; @@ -167,10 +177,3 @@ TEE_Result qcom_clock_enable_pas(enum qcom_clk_group group) EMSG("Timeout trying to enable clock group %d\n", group); return TEE_ERROR_TIMEOUT; } - -#else -TEE_Result qcom_clock_enable_pas(enum qcom_clk_group group __unused) -{ - return TEE_ERROR_NOT_SUPPORTED; -} -#endif /* ! CFG_QCOM_PAS_PTA */ diff --git a/core/drivers/clk/qcom/platform/lemans/clock-qcom-pas.c b/core/drivers/clk/qcom/platform/lemans/clock-qcom-pas.c new file mode 100644 index 000000000..244750925 --- /dev/null +++ b/core/drivers/clk/qcom/platform/lemans/clock-qcom-pas.c @@ -0,0 +1,605 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clock_group_qcom.h" + +#define CBCR_BRANCH_ENABLE_BIT BIT(0) +#define CBCR_HW_CTL_ENABLE_BIT BIT(1) +#define CBCR_BRANCH_OFF_BIT BIT(31) + +register_phys_mem(MEM_AREA_IO_NSEC, AOSS_CC_BASE, AOSS_CC_SIZE); +register_phys_mem(MEM_AREA_IO_NSEC, RPMH_PDC_GLOBAL_BASE, RPMH_PDC_GLOBAL_SIZE); +register_phys_mem(MEM_AREA_IO_NSEC, RPMH_PDC_COMPUTE_BASE, + RPMH_PDC_COMPUTE_SIZE); +register_phys_mem(MEM_AREA_IO_NSEC, RPMH_PDC_NSP_BASE, RPMH_PDC_NSP_SIZE); +register_phys_mem(MEM_AREA_IO_NSEC, RPMH_PDC_GPDSP0_BASE, RPMH_PDC_GPDSP0_SIZE); +register_phys_mem(MEM_AREA_IO_NSEC, RPMH_PDC_GPDSP1_BASE, RPMH_PDC_GPDSP1_SIZE); +register_phys_mem(MEM_AREA_IO_NSEC, TCSR_MUTEX_BASE, TCSR_MUTEX_SIZE); + +static TEE_Result cdsp_enable(paddr_t turing_base) +{ + struct io_pa_va turing_cc_io = { + .pa = turing_base + TURINGNSP_CC_OFFSET + }; + vaddr_t cc_base = io_pa_or_va(&turing_cc_io, 0x50000); + uint64_t timeout = timeout_init_us(10000); + TEE_Result res = TEE_SUCCESS; + + res = qcom_clock_enable_cbc(cc_base + TURINGNSP_Q6SS_AHBS_AON); + if (res != TEE_SUCCESS) + return res; + + res = qcom_clock_enable_cbc(cc_base + TURINGNSP_Q6SS_ALT_RESET_AON); + if (res != TEE_SUCCESS) + return res; + + io_clrbits32(cc_base + TURINGNSP_Q6SS_ALT_RESET_CTL, + CBCR_BRANCH_ENABLE_BIT); + io_clrbits32(cc_base + TURINGNSP_Q6SS_ALT_RESET_AON, + CBCR_BRANCH_ENABLE_BIT); + + io_setbits32(cc_base + TURINGNSP_NSPNOC, + CBCR_BRANCH_ENABLE_BIT); + + /* Retention flop */ + io_clrbits32(cc_base + TURINGNSP_VAPSS_GDSCR, 0x1); + while (!timeout_elapsed(timeout)) { + if (io_read32(cc_base + TURINGNSP_VAPSS_GDSCR) & 0x80000000) + goto out; + + udelay(10); + } + + return TEE_ERROR_TIMEOUT; +out: + io_setbits32(cc_base + TURINGNSP_VAPSS_GDSCR, 0x801); + + return TEE_SUCCESS; +} + +static const struct qcom_lucidevo_pll_config q6_pll_cfg = { + .l_val = TURINGNSP_Q6_PLL_L_VAL, + .cal_l_val = TURINGNSP_Q6_PLL_CAL_L_VAL, + .pre_div = 1, + .config_ctl = TURINGNSP_Q6_PLL_CONFIG_CTL, + .config_ctl_u = TURINGNSP_Q6_PLL_CONFIG_CTL_U, + .config_ctl_u1 = TURINGNSP_Q6_PLL_CONFIG_CTL_U1, + .user_ctl = TURINGNSP_Q6_PLL_USER_CTL, + .user_ctl_u = TURINGNSP_Q6_PLL_USER_CTL_U, + /* alpha (default) fractional mode */ +}; + +/* + * Bring the QDSP6 out of reset after the boot FSM completes: lock the Q6 PLL, + * switch the core RCG onto it, then release the core. Runs after fw_start() + * since the Q6 PLL registers must not be touched until the FSM is done. + */ +static TEE_Result cdsp_enable_processor(paddr_t turing_base) +{ + struct io_pa_va proc_io = { + .pa = turing_base + TURINGNSP_BOOT_OFFSET + }; + vaddr_t boot_base = io_pa_or_va(&proc_io, TURINGNSP_PROC_WINDOW_SIZE); + vaddr_t pll_base = boot_base - TURINGNSP_BOOT_OFFSET + + TURINGNSP_Q6_PLL_OFFSET; + vaddr_t core_cc = boot_base - TURINGNSP_BOOT_OFFSET + + TURINGNSP_CORE_CC_OFFSET; + TEE_Result res = TEE_SUCCESS; + + res = qcom_lucidevo_pll_enable(pll_base, &q6_pll_cfg); + if (res != TEE_SUCCESS) + return res; + + res = qcom_clock_set_rate(core_cc + QDSP6SS_CORE_CFG_RCGR, + core_cc + QDSP6SS_CORE_CMD_RCGR, + Q6RCG_CFG_VALUE); + if (res != TEE_SUCCESS) + return res; + + /* Release the core only after the PLL has been initialised. */ + io_setbits32(boot_base + QDSP6SS_BOOT_CORE_START, BIT(0)); + + return TEE_SUCCESS; +} + +static const struct qcom_lucidevo_pll_config lpass_q6_pll_cfg = { + .l_val = LPASS_Q6_PLL_L_VAL, + .cal_l_val = LPASS_Q6_PLL_CAL_L_VAL, + .pre_div = 1, + .config_ctl = LPASS_Q6_PLL_CONFIG_CTL, + .config_ctl_u = LPASS_Q6_PLL_CONFIG_CTL_U, + .config_ctl_u1 = LPASS_Q6_PLL_CONFIG_CTL_U1, + .user_ctl = LPASS_Q6_PLL_USER_CTL, + .user_ctl_u = LPASS_Q6_PLL_USER_CTL_U, + /* alpha (default) fractional mode */ +}; + +/* + * Set up clocks for the LPASS / ADSP QDSP6. Unlike the Turing NSP, the Q6 PLL + * and core RCG are configured here, before the boot FSM in lpass_fw_start. + */ +static TEE_Result lpass_setup(void) +{ + struct io_pa_va gcc_io = { .pa = GCC_BASE }; + vaddr_t gcc_base = io_pa_or_va(&gcc_io, GCC_SIZE); + struct io_pa_va lpass_io = { .pa = LPASS_BASE }; + vaddr_t lpass_base = io_pa_or_va(&lpass_io, LPASS_SIZE); + vaddr_t aon_cc = lpass_base + LPASS_AON_CC_OFFSET; + vaddr_t top_cc = lpass_base + LPASS_TOP_CC_OFFSET; + vaddr_t core_cc = lpass_base + LPASS_CORE_CC_OFFSET; + vaddr_t pll_base = lpass_base + LPASS_PLL_OFFSET; + TEE_Result res = TEE_SUCCESS; + + if (!gcc_base || !lpass_base) + return TEE_ERROR_GENERIC; + + /* 4. Enable LPASS access from the config NoC. */ + res = qcom_clock_enable_cbc(gcc_base + GCC_CFG_NOC_LPASS_CBCR); + if (res != TEE_SUCCESS) + return res; + + /* AHB master/slave clocks, required to reach the QDSP6SS registers. */ + res = qcom_clock_enable_cbc(aon_cc + LPASS_AON_CC_Q6_AHBM_CBCR); + if (res != TEE_SUCCESS) + return res; + + res = qcom_clock_enable_cbc(aon_cc + LPASS_AON_CC_Q6_AHBS_CBCR); + if (res != TEE_SUCCESS) + return res; + + /* LPASS interface clock. */ + res = qcom_clock_enable_cbc(top_cc + LPASS_TOP_CC_LPI_Q6_AXIM_HS_CBCR); + if (res != TEE_SUCCESS) + return res; + + /* Enable the QDSP6 core clock branch. */ + res = qcom_clock_enable_cbc(core_cc + LPASS_QDSP6SS_CORE_CBCR); + if (res != TEE_SUCCESS) + return res; + + /* Configure and lock the Q6 PLL, then switch the core RCG onto it. */ + res = qcom_lucidevo_pll_enable(pll_base, &lpass_q6_pll_cfg); + if (res != TEE_SUCCESS) + return res; + + return qcom_clock_set_rate(core_cc + LPASS_QDSP6SS_CORE_CFG_RCGR, + core_cc + LPASS_QDSP6SS_CORE_CMD_RCGR, + Q6RCG_CFG_VALUE); +} + +static const struct qcom_lucidevo_pll_config gpdsp_q6_pll_cfg = { + .l_val = GPDSP_Q6_PLL_L_VAL, + .cal_l_val = GPDSP_Q6_PLL_CAL_L_VAL, + .pre_div = 1, + .config_ctl = GPDSP_Q6_PLL_CONFIG_CTL, + .config_ctl_u = GPDSP_Q6_PLL_CONFIG_CTL_U, + .config_ctl_u1 = GPDSP_Q6_PLL_CONFIG_CTL_U1, + .user_ctl = GPDSP_Q6_PLL_USER_CTL, + .user_ctl_u = GPDSP_Q6_PLL_USER_CTL_U, + /* alpha (default) fractional mode */ +}; + +/* + * Set up clocks for a GP-DSP (TURINGGDSP) QDSP6. Like LPASS, the Q6 PLL and + * core RCG are configured here, before the boot FSM in gpdspN_fw_start. + */ +static TEE_Result gpdsp_setup(paddr_t gdsp_base, uint32_t gcc_cfg_ahb_cbcr, + uint32_t gcc_aggre_axi_cbcr) +{ + struct io_pa_va gcc_io = { .pa = GCC_BASE }; + vaddr_t gcc_base = io_pa_or_va(&gcc_io, GCC_SIZE); + struct io_pa_va gdsp_io = { .pa = gdsp_base }; + vaddr_t base = io_pa_or_va(&gdsp_io, TURING_GDSP_0_SIZE); + vaddr_t gdsp_cc = base + TURINGGDSP_GDSP_CC_OFFSET; + vaddr_t core_cc = base + TURINGGDSP_CORE_CC_OFFSET; + vaddr_t pll_base = base + TURINGGDSP_PLL_OFFSET; + vaddr_t pub = base + TURINGGDSP_PUB_OFFSET; + TEE_Result res = TEE_SUCCESS; + + if (!gcc_base || !base) + return TEE_ERROR_GENERIC; + + /* Turing slave-way bus clock branch, also on NIU socket. */ + res = qcom_clock_enable_cbc(gcc_base + gcc_cfg_ahb_cbcr); + if (res != TEE_SUCCESS) + return res; + + res = qcom_clock_enable_cbc(gcc_base + gcc_aggre_axi_cbcr); + if (res != TEE_SUCCESS) + return res; + + /* Q6SS slave clock, required to reach the QDSP6SS registers. */ + res = qcom_clock_enable_cbc(gdsp_cc + TURINGGDSP_Q6SS_AHBS_AON_CBCR); + if (res != TEE_SUCCESS) + return res; + + /* Program the debug config register, then enable core clock. */ + io_write32(pub + TURINGGDSP_QDSP6SS_DBG_CFG, 0); + io_setbits32(core_cc + TURINGGDSP_QDSP6SS_CORE_CBCR, + CBCR_BRANCH_ENABLE_BIT); + + /* Configure and lock the Q6 PLL, then switch the core RCG onto it. */ + res = qcom_lucidevo_pll_enable(pll_base, &gpdsp_q6_pll_cfg); + if (res != TEE_SUCCESS) + return res; + + return qcom_clock_set_rate(core_cc + TURINGGDSP_QDSP6SS_CORE_CFG_RCGR, + core_cc + TURINGGDSP_QDSP6SS_CORE_CMD_RCGR, + Q6RCG_CFG_VALUE); +} + +TEE_Result qcom_clock_enable_pas_processor(enum qcom_clk_group group) +{ + switch (group) { + case QCOM_CLKS_TURING: + return cdsp_enable_processor(TURING_0_BASE); + case QCOM_CLKS_TURING1: + return cdsp_enable_processor(TURING_1_BASE); + case QCOM_CLKS_LPASS: + /* + * LPASS configures its Q6 PLL and core RCG in lpass_setup + * (before the boot FSM) and releases the core in + * lpass_fw_start, so there is no post-boot step here. + */ + return TEE_SUCCESS; + case QCOM_CLKS_GPDSP0: + case QCOM_CLKS_GPDSP1: + /* + * Like LPASS, the GP-DSP Q6 PLL and core RCG are configured in + * gpdsp_setup and the core released in gpdspN_fw_start, so + * there is no post-boot step here. + */ + return TEE_SUCCESS; + default: + return TEE_ERROR_NOT_SUPPORTED; + } +} + +/* + * Per-instance register selection for the Turing/NSP reset sequence: CDSP0 and + * CDSP1 share the Turing-CC offsets but differ in the GCC/TCSR/PDC/AOSS fields. + */ +struct cdsp_reset_regs { + paddr_t turing_base; + uint32_t gcc_cfg_ahb_cbcr; /* offset within GCC window */ + uint32_t tcsr_haltreq; /* offsets within TCSR mutex window */ + uint32_t tcsr_haltack; + uint32_t tcsr_master_idle; + uint32_t tcsr_pwr_on; + paddr_t pdc_status_base; /* RPMH PDC block for the busy check */ + uint32_t pdc_status_size; + uint32_t pdc_sync_reset_bit; /* field within RPMH_PDC_SYNC_RESET */ + uint32_t computess_restart_bit; /* bit in AOSS_CC_COMPUTESS_RESTART */ + uint32_t ret_cfg_settle_us; /* settle us after RET_CFG, CDSP0 */ +}; + +static const struct cdsp_reset_regs cdsp0_reset_regs = { + .turing_base = TURING_0_BASE, + .gcc_cfg_ahb_cbcr = GCC_TURING_0_CFG_AHB_CBCR, + .tcsr_haltreq = TCSR_TURING_HALTREQ, + .tcsr_haltack = TCSR_TURING_HALTACK, + .tcsr_master_idle = TCSR_TURING_MASTER_IDLE, + .tcsr_pwr_on = TCSR_TURING_PWR_ON, + .pdc_status_base = RPMH_PDC_COMPUTE_BASE, + .pdc_status_size = RPMH_PDC_COMPUTE_SIZE, + .pdc_sync_reset_bit = PDC_SYNC_RESET_COMPUTE_BIT, + .computess_restart_bit = COMPUTESS_RESTART_SS_0_BIT, + .ret_cfg_settle_us = 2000, +}; + +static const struct cdsp_reset_regs cdsp1_reset_regs = { + .turing_base = TURING_1_BASE, + .gcc_cfg_ahb_cbcr = GCC_TURING_1_CFG_AHB_CBCR, + .tcsr_haltreq = TCSR_TURING1_HALT_REQ, + .tcsr_haltack = TCSR_TURING1_HALT_ACK, + .tcsr_master_idle = TCSR_TURING1_MASTER_IDLE, + .tcsr_pwr_on = TCSR_TURING1_PWR_ON, + .pdc_status_base = RPMH_PDC_NSP_BASE, + .pdc_status_size = RPMH_PDC_NSP_SIZE, + .pdc_sync_reset_bit = PDC_SYNC_RESET_NSP_BIT, + .computess_restart_bit = COMPUTESS_RESTART_SS_1_BIT, + .ret_cfg_settle_us = 0, +}; + +/* HALT_ACK polls for up to 1s (200000 * 5us), matching the reference. */ +#define CDSP_HALT_ACK_TIMEOUT_US (200000 * 5) + +/* + * Put the QDSP6/NSP through a full subsystem reset (AOSS_CC_COMPUTESS_RESTART + * and PDC sync reset) before bring-up, so the Q6 does not come out of reset + * from a stale state left by an earlier boot stage. + */ +static TEE_Result cdsp_reset_processor(const struct cdsp_reset_regs *r) +{ + struct io_pa_va turing_cc_io = { .pa = r->turing_base + + TURINGNSP_CC_OFFSET }; + vaddr_t cc_base = io_pa_or_va(&turing_cc_io, + TURINGNSP_PROC_WINDOW_SIZE); + struct io_pa_va pub_io = { .pa = r->turing_base + + TURINGNSP_BOOT_OFFSET }; + vaddr_t pub_base = io_pa_or_va(&pub_io, TURINGNSP_PROC_WINDOW_SIZE); + struct io_pa_va gcc_io = { .pa = GCC_BASE }; + vaddr_t gcc_base = io_pa_or_va(&gcc_io, GCC_SIZE); + struct io_pa_va aoss_io = { .pa = AOSS_CC_BASE }; + vaddr_t aoss_cc = io_pa_or_va(&aoss_io, AOSS_CC_SIZE); + struct io_pa_va pdc_g_io = { .pa = RPMH_PDC_GLOBAL_BASE }; + vaddr_t pdc_global = io_pa_or_va(&pdc_g_io, RPMH_PDC_GLOBAL_SIZE); + struct io_pa_va pdc_s_io = { .pa = r->pdc_status_base }; + vaddr_t pdc_status = io_pa_or_va(&pdc_s_io, r->pdc_status_size); + struct io_pa_va tcsr_io = { .pa = TCSR_MUTEX_BASE }; + vaddr_t tcsr = io_pa_or_va(&tcsr_io, TCSR_MUTEX_SIZE); + uint64_t timeout = 0; + TEE_Result res = TEE_SUCCESS; + + /* Bail if the PDC sequencer is mid-transition. */ + if (io_read32(pdc_status + RPMH_PDC_MODE_STATUS_DRV0) & + PDC_MODE_STATUS_SEQ_BUSY_BIT) + return TEE_ERROR_BUSY; + + /* Activate a full QDSP6 reset before the SSR. */ + res = qcom_clock_enable_cbc(cc_base + TURINGNSP_Q6SS_ALT_RESET_AON); + if (res != TEE_SUCCESS) + return res; + io_setbits32(cc_base + TURINGNSP_Q6SS_ALT_RESET_CTL, + Q6SS_ALT_RESET_CTL_ALT_ARES_BYPASS_BIT); + + /* + * Reset the retention logic; CDSP0 waits for the write to settle + * (CDSP1 omits this), captured by ret_cfg_settle_us. + */ + io_setbits32(pub_base + TURINGNSP_QDSP6SS_RET_CFG, + QDSP6SS_RET_CFG_RET_ARES_ENA_BIT); + if (r->ret_cfg_settle_us) { + dsb(); + udelay(r->ret_cfg_settle_us); + } + + /* Retain the NSP_AUX registers across the reset. */ + io_setbits32(cc_base + TURINGNSP_NSPAUX_XO_CBCR, CBCR_CLK_ENABLE_BIT); + io_setbits32(cc_base + TURINGNSP_NSPAUX_GDSCR, + NSPAUX_GDSCR_RETAIN_FF_ENABLE_BIT); + + /* Disconnect the SWAY NIU socket to halt config-NoC traffic. */ + io_clrbits32(gcc_base + r->gcc_cfg_ahb_cbcr, CBCR_CLK_ENABLE_BIT); + + /* If powered on and the master port is busy, halt mem-NoC traffic. */ + if ((io_read32(tcsr + r->tcsr_pwr_on) & TCSR_TURING_BIT) && + !(io_read32(tcsr + r->tcsr_master_idle) & TCSR_TURING_BIT)) { + io_setbits32(tcsr + r->tcsr_haltreq, TCSR_TURING_BIT); + + timeout = timeout_init_us(CDSP_HALT_ACK_TIMEOUT_US); + while (!(io_read32(tcsr + r->tcsr_haltack) & + TCSR_TURING_BIT)) { + if (timeout_elapsed(timeout)) + break; + udelay(5); + } + } + + /* Assert the PDC reset, then pulse the subsystem restart. */ + io_setbits32(pdc_global + RPMH_PDC_SYNC_RESET, r->pdc_sync_reset_bit); + + io_setbits32(aoss_cc + AOSS_CC_COMPUTESS_RESTART, + r->computess_restart_bit); + udelay(200); + io_clrbits32(aoss_cc + AOSS_CC_COMPUTESS_RESTART, + r->computess_restart_bit); + dsb(); + udelay(200); + + /* De-assert the PDC reset and clear the halt request. */ + io_clrbits32(pdc_global + RPMH_PDC_SYNC_RESET, r->pdc_sync_reset_bit); + io_clrbits32(tcsr + r->tcsr_haltreq, TCSR_TURING_BIT); + udelay(100); + + return TEE_SUCCESS; +} + +/* + * Per-instance register selection for the GP-DSP reset sequence: GDSP0 and + * GDSP1 share the QDSP6 PUB offsets but differ in the GCC/TCSR/PDC/AOSS fields. + */ +struct gpdsp_reset_regs { + paddr_t gdsp_base; + uint32_t gcc_cfg_ahb_cbcr; /* offset within GCC window */ + uint32_t tcsr_haltreq; /* offsets within TCSR mutex window */ + uint32_t tcsr_haltack; + uint32_t tcsr_il0_master_idle; + uint32_t tcsr_il1_master_idle; + uint32_t tcsr_pwr_on; + paddr_t pdc_status_base; /* RPMH PDC block for the busy check */ + uint32_t pdc_status_size; + uint32_t pdc_sync_reset_bit; /* field within RPMH_PDC_SYNC_RESET */ + uint32_t gpdsp_restart_bit; /* field within AOSS_CC_GPDSP_RESTART */ +}; + +static const struct gpdsp_reset_regs gpdsp0_reset_regs = { + .gdsp_base = TURING_GDSP_0_BASE, + .gcc_cfg_ahb_cbcr = GCC_GPDSP_0_CFG_AHB_CBCR, + .tcsr_haltreq = TCSR_GPDSP0_HALT_REQ, + .tcsr_haltack = TCSR_GPDSP0_HALT_ACK, + .tcsr_il0_master_idle = TCSR_GPDSP0_IL0_MASTER_IDLE, + .tcsr_il1_master_idle = TCSR_GPDSP0_IL1_MASTER_IDLE, + .tcsr_pwr_on = TCSR_GPDSP0_PWR_ON, + .pdc_status_base = RPMH_PDC_GPDSP0_BASE, + .pdc_status_size = RPMH_PDC_GPDSP0_SIZE, + .pdc_sync_reset_bit = PDC_SYNC_RESET_GPDSP0_BIT, + .gpdsp_restart_bit = GPDSP_RESTART_SS_0_BIT, +}; + +static const struct gpdsp_reset_regs gpdsp1_reset_regs = { + .gdsp_base = TURING_GDSP_1_BASE, + .gcc_cfg_ahb_cbcr = GCC_GPDSP_1_CFG_AHB_CBCR, + .tcsr_haltreq = TCSR_GPDSP1_HALT_REQ, + .tcsr_haltack = TCSR_GPDSP1_HALT_ACK, + .tcsr_il0_master_idle = TCSR_GPDSP1_IL0_MASTER_IDLE, + .tcsr_il1_master_idle = TCSR_GPDSP1_IL1_MASTER_IDLE, + .tcsr_pwr_on = TCSR_GPDSP1_PWR_ON, + .pdc_status_base = RPMH_PDC_GPDSP1_BASE, + .pdc_status_size = RPMH_PDC_GPDSP1_SIZE, + .pdc_sync_reset_bit = PDC_SYNC_RESET_GPDSP1_BIT, + .gpdsp_restart_bit = GPDSP_RESTART_SS_1_BIT, +}; + +/* HALT_ACK polls for up to 1s (200000 * 5us), matching the reference. */ +#define GPDSP_HALT_ACK_TIMEOUT_US (200000 * 5) + +/* + * Put a GP-DSP QDSP6 through a full subsystem reset (AOSS_CC_GPDSP_RESTART and + * PDC sync reset) before bring-up. Unlike the Turing NSP there is no + * NSPAUX/ALT_RESET retention handling and the idle check covers two ports. + */ +static TEE_Result gpdsp_reset_processor(const struct gpdsp_reset_regs *r) +{ + struct io_pa_va gdsp_io = { .pa = r->gdsp_base }; + vaddr_t base = io_pa_or_va(&gdsp_io, TURING_GDSP_0_SIZE); + vaddr_t pub = base + TURINGGDSP_PUB_OFFSET; + struct io_pa_va gcc_io = { .pa = GCC_BASE }; + vaddr_t gcc_base = io_pa_or_va(&gcc_io, GCC_SIZE); + struct io_pa_va aoss_io = { .pa = AOSS_CC_BASE }; + vaddr_t aoss_cc = io_pa_or_va(&aoss_io, AOSS_CC_SIZE); + struct io_pa_va pdc_g_io = { .pa = RPMH_PDC_GLOBAL_BASE }; + vaddr_t pdc_global = io_pa_or_va(&pdc_g_io, RPMH_PDC_GLOBAL_SIZE); + struct io_pa_va pdc_s_io = { .pa = r->pdc_status_base }; + vaddr_t pdc_status = io_pa_or_va(&pdc_s_io, r->pdc_status_size); + struct io_pa_va tcsr_io = { .pa = TCSR_MUTEX_BASE }; + vaddr_t tcsr = io_pa_or_va(&tcsr_io, TCSR_MUTEX_SIZE); + uint64_t timeout = 0; + + if (!base || !gcc_base || !aoss_cc || !pdc_global || !pdc_status || + !tcsr) + return TEE_ERROR_GENERIC; + + /* Bail if the PDC sequencer is mid-transition. */ + if (io_read32(pdc_status + RPMH_PDC_MODE_STATUS_DRV0) & + PDC_MODE_STATUS_SEQ_BUSY_BIT) + return TEE_ERROR_BUSY; + + /* Reset the retention logic and let the write settle. */ + io_setbits32(pub + TURINGGDSP_QDSP6SS_RET_CFG, + QDSP6SS_RET_CFG_RET_ARES_ENA_BIT); + dsb(); + udelay(2000); + + /* Disconnect the SWAY NIU socket to halt config-NoC traffic. */ + io_clrbits32(gcc_base + r->gcc_cfg_ahb_cbcr, CBCR_CLK_ENABLE_BIT); + + /* + * If powered on and either master port (IL0 or IL1) is busy, halt + * mem-NoC traffic and wait for the acknowledge. + */ + if (io_read32(tcsr + r->tcsr_pwr_on) & TCSR_TURING_BIT) { + if (!(io_read32(tcsr + r->tcsr_il0_master_idle) & + TCSR_TURING_BIT) || + !(io_read32(tcsr + r->tcsr_il1_master_idle) & + TCSR_TURING_BIT)) { + io_setbits32(tcsr + r->tcsr_haltreq, TCSR_TURING_BIT); + + timeout = timeout_init_us(GPDSP_HALT_ACK_TIMEOUT_US); + while (!(io_read32(tcsr + r->tcsr_haltack) & + TCSR_TURING_BIT)) { + if (timeout_elapsed(timeout)) + break; + udelay(5); + } + } + } + + /* Assert the PDC reset, then pulse the subsystem restart. */ + io_setbits32(pdc_global + RPMH_PDC_SYNC_RESET, r->pdc_sync_reset_bit); + + io_setbits32(aoss_cc + AOSS_CC_GPDSP_RESTART, r->gpdsp_restart_bit); + udelay(200); + io_clrbits32(aoss_cc + AOSS_CC_GPDSP_RESTART, r->gpdsp_restart_bit); + dsb(); + udelay(200); + + /* De-assert the PDC reset and clear the halt request. */ + io_clrbits32(pdc_global + RPMH_PDC_SYNC_RESET, r->pdc_sync_reset_bit); + io_clrbits32(tcsr + r->tcsr_haltreq, TCSR_TURING_BIT); + udelay(100); + + return TEE_SUCCESS; +} + +TEE_Result qcom_clock_pas_reset(enum qcom_clk_group group) +{ + switch (group) { + case QCOM_CLKS_TURING: + return cdsp_reset_processor(&cdsp0_reset_regs); + case QCOM_CLKS_TURING1: + return cdsp_reset_processor(&cdsp1_reset_regs); + case QCOM_CLKS_GPDSP0: + return gpdsp_reset_processor(&gpdsp0_reset_regs); + case QCOM_CLKS_GPDSP1: + return gpdsp_reset_processor(&gpdsp1_reset_regs); + case QCOM_CLKS_LPASS: + /* + * The LPASS subsystem reset is the SSR / tear-down path; + * cold bring-up starts from a known state with the LPASS + * core domain already powered. + */ + return TEE_SUCCESS; + default: + return TEE_ERROR_NOT_SUPPORTED; + } +} + +TEE_Result qcom_clock_enable_pas(enum qcom_clk_group group) +{ + struct io_pa_va base = { .pa = GCC_BASE }; + vaddr_t gcc_base = io_pa_or_va(&base, GCC_SIZE); + TEE_Result res = 0; + + switch (group) { + case QCOM_CLKS_TURING: + /* Turing bus clock branch connected to the NIU socket */ + res = qcom_clock_enable_cbc(gcc_base + + GCC_TURING_0_CFG_AHB_CLK); + if (res) + goto timeout; + + res = cdsp_enable(TURING_0_BASE); + if (res != TEE_SUCCESS) + goto timeout; + break; + case QCOM_CLKS_TURING1: + /* Turing bus clock branch connected to the NIU socket */ + res = qcom_clock_enable_cbc(gcc_base + + GCC_TURING_1_CFG_AHB_CLK); + if (res) + goto timeout; + + res = cdsp_enable(TURING_1_BASE); + if (res != TEE_SUCCESS) + goto timeout; + break; + case QCOM_CLKS_LPASS: + return lpass_setup(); + case QCOM_CLKS_GPDSP0: + return gpdsp_setup(TURING_GDSP_0_BASE, GCC_GPDSP_0_CFG_AHB_CBCR, + GCC_AGGRE_NOC_GPDSP_0_AXI_CBCR); + case QCOM_CLKS_GPDSP1: + return gpdsp_setup(TURING_GDSP_1_BASE, GCC_GPDSP_1_CFG_AHB_CBCR, + GCC_AGGRE_NOC_GPDSP_1_AXI_CBCR); + default: + return TEE_ERROR_NOT_SUPPORTED; + } + + return TEE_SUCCESS; +timeout: + EMSG("Timeout trying to enable clock group %d\n", group); + return TEE_ERROR_TIMEOUT; +} diff --git a/core/drivers/clk/qcom/platform/lemans/clock_group_qcom.h b/core/drivers/clk/qcom/platform/lemans/clock_group_qcom.h index b29f8aa72..8dd0bcd14 100644 --- a/core/drivers/clk/qcom/platform/lemans/clock_group_qcom.h +++ b/core/drivers/clk/qcom/platform/lemans/clock_group_qcom.h @@ -9,4 +9,219 @@ #define GCC_SEC_CTRL_CMD_RCGR 0x39034 #define QFPROM_CLOCK_DIVIDE 0x7 +#define GCC_TURING_0_CFG_AHB_CLK 0x41028 +#define GCC_TURING_1_CFG_AHB_CLK 0x12028 + +/* + * Turing/NSP clock-controller register offsets, identical for both NSP + * instances; only the subsystem base differs (TURING_0_BASE / TURING_1_BASE). + */ +#define TURINGNSP_CC_OFFSET 0x02008000 +#define TURINGNSP_Q6SS_ALT_RESET_AON 0x418 +#define TURINGNSP_Q6SS_AHBS_AON 0x414 +#define TURINGNSP_Q6SS_ALT_RESET_CTL 0x10034 +#define TURINGNSP_NSPNOC 0x22c +#define TURINGNSP_VAPSS_GDSCR 0x80 + +/* + * Additional Turing CC / QDSP6 PUB register offsets used by the reset sequence + * (within the Turing CC and QDSP6 PUB blocks respectively). + */ +#define TURINGNSP_NSPAUX_XO_CBCR 0x40 +#define TURINGNSP_NSPAUX_GDSCR 0x200 +#define TURINGNSP_QDSP6SS_RET_CFG 0x1c + +#define NSPAUX_GDSCR_RETAIN_FF_ENABLE_BIT BIT(11) +#define Q6SS_ALT_RESET_CTL_ALT_ARES_BYPASS_BIT BIT(0) +#define QDSP6SS_RET_CFG_RET_ARES_ENA_BIT BIT(0) + +/* + * Global blocks involved in the Turing reset (offsets within each window); + * the GCC config-NoC AHB CBCRs gate the SWAY NIU socket. + */ +#define GCC_TURING_0_CFG_AHB_CBCR 0x41028 +#define GCC_TURING_1_CFG_AHB_CBCR 0x12028 +#define CBCR_CLK_ENABLE_BIT BIT(0) + +/* AOSS_CC compute-subsystem restart (within AOSS_CC window). */ +#define AOSS_CC_COMPUTESS_RESTART 0x4f030 +#define COMPUTESS_RESTART_SS_0_BIT BIT(0) +#define COMPUTESS_RESTART_SS_1_BIT BIT(1) + +/* RPMH PDC global sync-reset (within RPMH_PDC_GLOBAL window). */ +#define RPMH_PDC_SYNC_RESET 0x1000 +#define PDC_SYNC_RESET_COMPUTE_BIT BIT(9) +#define PDC_SYNC_RESET_NSP_BIT BIT(12) + +/* RPMH PDC per-block mode status, drv0 (within each PDC window). */ +#define RPMH_PDC_MODE_STATUS_DRV0 0x1030 +#define PDC_MODE_STATUS_SEQ_BUSY_BIT BIT(0) + +/* + * TCSR Turing master-halt / power registers (offsets within the TCSR mutex + * window); the CDSP1 block sits 0x18 above CDSP0. + */ +#define TCSR_TURING_HALTREQ 0x34000 +#define TCSR_TURING_HALTACK 0x34004 +#define TCSR_TURING_MASTER_IDLE 0x34008 +#define TCSR_TURING_PWR_ON 0x3400c +#define TCSR_TURING1_HALT_REQ 0x34018 +#define TCSR_TURING1_HALT_ACK 0x3401c +#define TCSR_TURING1_MASTER_IDLE 0x34020 +#define TCSR_TURING1_PWR_ON 0x34028 +#define TCSR_TURING_BIT BIT(0) + +/* + * QDSP6 boot / PLL / core CC blocks relative to the subsystem base; the + * enable-processor window at TURINGNSP_BOOT_OFFSET (0x50000) covers all three. + */ +#define TURINGNSP_BOOT_OFFSET 0x02300000 +#define TURINGNSP_PROC_WINDOW_SIZE 0x50000 +#define TURINGNSP_Q6_PLL_OFFSET 0x02340000 +#define TURINGNSP_CORE_CC_OFFSET 0x02348000 + +/* Offsets within the boot block (TURINGNSP_BOOT_OFFSET). */ +#define QDSP6SS_BOOT_CORE_START 0x400 + +/* Offsets within the core clock-controller block (TURINGNSP_CORE_CC_OFFSET). */ +#define QDSP6SS_CORE_CMD_RCGR 0x20 +#define QDSP6SS_CORE_CFG_RCGR 0x24 + +/* + * Q6 core RCG: source select = Q6 PLL, divider = 1. CFG_RCGR holds SRC_SEL at + * bits [10:8] and SRC_DIV at bits [4:0]. + */ +#define Q6RCG_SRC_SEL 0x2 +#define Q6RCG_SRC_SEL_SHIFT 8 +#define Q6RCG_SRC_DIV 0x1 +#define Q6RCG_CFG_VALUE (((Q6RCG_SRC_SEL) << \ + (Q6RCG_SRC_SEL_SHIFT)) | \ + (Q6RCG_SRC_DIV)) + +/* Q6 Lucid-EVO PLL settings, identical for both NSP instances. */ +#define TURINGNSP_Q6_PLL_L_VAL 0x32 +#define TURINGNSP_Q6_PLL_CAL_L_VAL 0x44 +#define TURINGNSP_Q6_PLL_CONFIG_CTL 0x20485699 +#define TURINGNSP_Q6_PLL_CONFIG_CTL_U 0x00182261 +#define TURINGNSP_Q6_PLL_CONFIG_CTL_U1 0x32AA299C +#define TURINGNSP_Q6_PLL_USER_CTL 0x00000000 +#define TURINGNSP_Q6_PLL_USER_CTL_U 0x00400805 + +/* + * LPASS / ADSP (QDSP6 v68/v69). All offsets are relative to LPASS_BASE; the + * subsystem window is mapped once by the PAS PTA (pas_platform_mem_setup), so + * the clock driver only derives sub-block bases as LPASS_BASE + offset. + */ +#define LPASS_PUB_OFFSET 0x00400000 +#define LPASS_PLL_OFFSET 0x00440000 +#define LPASS_CORE_CC_OFFSET 0x00448000 +#define LPASS_AON_CC_OFFSET 0x00808000 +#define LPASS_MCC_OFFSET 0x008d0000 +#define LPASS_TOP_CC_OFFSET 0x01000000 + +/* Offsets within the QDSP6 PUB block (LPASS_PUB_OFFSET). */ +#define LPASS_QDSP6SS_RST_EVB 0x10 +#define LPASS_QDSP6SS_BOOT_CORE_START 0x400 +#define LPASS_QDSP6SS_BOOT_CMD 0x404 +#define LPASS_QDSP6SS_BOOT_STATUS 0x408 + +/* Offset within the MCC block (LPASS_MCC_OFFSET). */ +#define LPASS_EFUSE_Q6SS_EVB_SEL 0xb000 + +/* Offsets within the core clock-controller block (LPASS_CORE_CC_OFFSET). */ +#define LPASS_QDSP6SS_CORE_CMD_RCGR 0x0 +#define LPASS_QDSP6SS_CORE_CFG_RCGR 0x4 +#define LPASS_QDSP6SS_CORE_CBCR 0x20 + +/* Offsets within the always-on clock-controller block (LPASS_AON_CC_OFFSET). */ +#define LPASS_AON_CC_Q6_AHBM_CBCR 0x101c +#define LPASS_AON_CC_Q6_AHBS_CBCR 0x1020 + +/* Offset within the LPASS top clock-controller block (LPASS_TOP_CC_OFFSET). */ +#define LPASS_TOP_CC_LPI_Q6_AXIM_HS_CBCR 0x4000 + +/* GCC config-NoC LPASS access branch (offset within the GCC window). */ +#define GCC_CFG_NOC_LPASS_CBCR 0x43024 + +/* + * LPASS Q6 Lucid-EVO PLL settings, taken from the reference clock driver + * HALclkPLLSettings.h (HAL_CLK_LPASS_AON_CC_LPASS_QDSP6SS_PLL_*). Identical to + * the Turing Q6 PLL except for the L value. + */ +#define LPASS_Q6_PLL_L_VAL 0x2C +#define LPASS_Q6_PLL_CAL_L_VAL 0x44 +#define LPASS_Q6_PLL_CONFIG_CTL 0x20485699 +#define LPASS_Q6_PLL_CONFIG_CTL_U 0x00182261 +#define LPASS_Q6_PLL_CONFIG_CTL_U1 0x32AA299C +#define LPASS_Q6_PLL_USER_CTL 0x00000000 +#define LPASS_Q6_PLL_USER_CTL_U 0x00400805 + +/* + * GP-DSP0 / GP-DSP1 (TURINGGDSP / TURINGGDSP1, QDSP6 v68/v69) sub-block offsets + * relative to TURING_GDSP_{0,1}_BASE; identical for both instances. + */ +#define TURINGGDSP_GDSP_CC_OFFSET 0x00808000 +#define TURINGGDSP_PUB_OFFSET 0x00c00000 +#define TURINGGDSP_PLL_OFFSET 0x00c40000 +#define TURINGGDSP_CORE_CC_OFFSET 0x00c48000 + +/* Offsets within the GDSP_CC block (TURINGGDSP_GDSP_CC_OFFSET). */ +#define TURINGGDSP_Q6SS_AHBS_AON_CBCR 0x10 + +/* Offsets within the QDSP6 PUB block (TURINGGDSP_PUB_OFFSET). */ +#define TURINGGDSP_QDSP6SS_RST_EVB 0x10 +#define TURINGGDSP_QDSP6SS_DBG_CFG 0x18 +#define TURINGGDSP_QDSP6SS_RET_CFG 0x1c +#define TURINGGDSP_QDSP6SS_BOOT_CORE_START 0x400 +#define TURINGGDSP_QDSP6SS_BOOT_CMD 0x404 +#define TURINGGDSP_QDSP6SS_BOOT_STATUS 0x408 + +/* Offsets in the core clock-controller block (TURINGGDSP_CORE_CC_OFFSET). */ +#define TURINGGDSP_QDSP6SS_CORE_CMD_RCGR 0x0 +#define TURINGGDSP_QDSP6SS_CORE_CFG_RCGR 0x4 +#define TURINGGDSP_QDSP6SS_CORE_CBCR 0x20 + +/* GCC config-NoC AHB / aggre-NoC AXI branches (within GCC window). */ +#define GCC_GPDSP_0_CFG_AHB_CBCR 0x16008 +#define GCC_AGGRE_NOC_GPDSP_0_AXI_CBCR 0x16004 +#define GCC_GPDSP_1_CFG_AHB_CBCR 0x15008 +#define GCC_AGGRE_NOC_GPDSP_1_AXI_CBCR 0x15004 + +/* AOSS_CC GP-DSP subsystem restart (within AOSS_CC window). */ +#define AOSS_CC_GPDSP_RESTART 0x4f034 +#define GPDSP_RESTART_SS_0_BIT BIT(0) +#define GPDSP_RESTART_SS_1_BIT BIT(1) + +/* RPMH PDC global sync-reset bits for GP-DSP (in RPMH_PDC_GLOBAL window). */ +#define PDC_SYNC_RESET_GPDSP0_BIT BIT(1) +#define PDC_SYNC_RESET_GPDSP1_BIT BIT(10) + +/* + * TCSR GP-DSP master-halt / power registers (offsets within the TCSR mutex + * window). The GDSP1 block sits 0x14 above the GDSP0 block. Unlike the Turing + * NSP, the reset sequence checks two master-idle bits (IL0 and IL1). + */ +#define TCSR_GPDSP0_HALT_REQ 0x32000 +#define TCSR_GPDSP0_HALT_ACK 0x32004 +#define TCSR_GPDSP0_IL0_MASTER_IDLE 0x32008 +#define TCSR_GPDSP0_IL1_MASTER_IDLE 0x3200c +#define TCSR_GPDSP0_PWR_ON 0x32010 +#define TCSR_GPDSP1_HALT_REQ 0x32014 +#define TCSR_GPDSP1_HALT_ACK 0x32018 +#define TCSR_GPDSP1_IL0_MASTER_IDLE 0x3201c +#define TCSR_GPDSP1_IL1_MASTER_IDLE 0x32020 +#define TCSR_GPDSP1_PWR_ON 0x32024 + +/* + * GP-DSP Q6 Lucid-EVO PLL settings, identical for both instances; differs from + * the Turing NSP Q6 PLL only in the L value. + */ +#define GPDSP_Q6_PLL_L_VAL 0x3A +#define GPDSP_Q6_PLL_CAL_L_VAL 0x44 +#define GPDSP_Q6_PLL_CONFIG_CTL 0x20485699 +#define GPDSP_Q6_PLL_CONFIG_CTL_U 0x00182261 +#define GPDSP_Q6_PLL_CONFIG_CTL_U1 0x32AA299C +#define GPDSP_Q6_PLL_USER_CTL 0x00000000 +#define GPDSP_Q6_PLL_USER_CTL_U 0x00400805 + #endif /* _CLOCK_GROUP_QCOM_H_ */ diff --git a/core/drivers/clk/qcom/platform/nord/clock-qcom-pas.c b/core/drivers/clk/qcom/platform/nord/clock-qcom-pas.c new file mode 100644 index 000000000..086a6f6f4 --- /dev/null +++ b/core/drivers/clk/qcom/platform/nord/clock-qcom-pas.c @@ -0,0 +1,806 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "clock_group_qcom.h" + +#define CBCR_BRANCH_ENABLE_BIT BIT(0) + +register_phys_mem(MEM_AREA_IO_NSEC, GCC_BASE, GCC_SIZE); +register_phys_mem(MEM_AREA_IO_NSEC, CDSP_0_BASE, CDSP_0_SIZE); +register_phys_mem(MEM_AREA_IO_NSEC, CDSP_1_BASE, CDSP_1_SIZE); +register_phys_mem(MEM_AREA_IO_NSEC, CDSP_2_BASE, CDSP_2_SIZE); +register_phys_mem(MEM_AREA_IO_NSEC, CDSP_3_BASE, CDSP_3_SIZE); +register_phys_mem(MEM_AREA_IO_NSEC, HPASS_0_BASE, HPASS_0_SIZE); +register_phys_mem(MEM_AREA_IO_NSEC, HPASS_1_BASE, HPASS_1_SIZE); +register_phys_mem(MEM_AREA_IO_NSEC, HPASS_2_BASE, HPASS_2_SIZE); +register_phys_mem(MEM_AREA_IO_NSEC, HPASS_CC_BASE, HPASS_CC_SIZE); +register_phys_mem(MEM_AREA_IO_NSEC, HPASS_TCM_BASE, HPASS_TCM_SIZE); +register_phys_mem(MEM_AREA_IO_NSEC, HPASS_TCSR_BASE, HPASS_TCSR_SIZE); +register_phys_mem(MEM_AREA_IO_NSEC, SOCCP_CSR_BASE, SOCCP_CSR_SIZE); +register_phys_mem(MEM_AREA_IO_NSEC, QDSS_SOCCP_DMI_BASE, QDSS_SOCCP_DMI_SIZE); +register_phys_mem(MEM_AREA_IO_NSEC, AOSS_CC_BASE, AOSS_CC_SIZE); +register_phys_mem(MEM_AREA_IO_NSEC, RPMH_PDC_GLOBAL_BASE, RPMH_PDC_GLOBAL_SIZE); +register_phys_mem(MEM_AREA_IO_NSEC, FUSE_CONTROLLER_SW_RANGE4_BASE, + FUSE_CONTROLLER_SW_RANGE4_SIZE); +register_phys_mem(MEM_AREA_IO_NSEC, TCSR_MUTEX_BASE, TCSR_MUTEX_SIZE); + +struct cdsp_regs { + paddr_t base; + uint32_t tcsr_haltreq; + uint32_t tcsr_haltack; + uint32_t tcsr_master_idle; + uint32_t tcsr_il1_master_idle; + uint32_t tcsr_pwr_on; + uint32_t tbf_core_cfg1; + uint32_t computess_restart_bit; + uint32_t pdc_sync_reset_off; +}; + +static const struct cdsp_regs cdsp0_regs = { + .base = CDSP_0_BASE, + .tcsr_haltreq = TCSR_CDSP_0_HALTREQ, + .tcsr_haltack = TCSR_CDSP_0_HALTACK, + .tcsr_master_idle = TCSR_CDSP_0_MASTER_IDLE, + .tcsr_il1_master_idle = TCSR_CDSP_0_IL1_MASTER_IDLE, + .tcsr_pwr_on = TCSR_CDSP_0_PWR_ON, + .computess_restart_bit = COMPUTESS_RESTART_SS_0_BIT, + .pdc_sync_reset_off = RPMH_PDC_COMPUTE_SYNC_RESET, +}; + +static const struct cdsp_regs cdsp1_regs = { + .base = CDSP_1_BASE, + .tcsr_haltreq = TCSR_CDSP_1_HALTREQ, + .tcsr_haltack = TCSR_CDSP_1_HALTACK, + .tcsr_master_idle = TCSR_CDSP_1_MASTER_IDLE, + .tcsr_il1_master_idle = TCSR_CDSP_1_IL1_MASTER_IDLE, + .tcsr_pwr_on = TCSR_CDSP_1_PWR_ON, + .computess_restart_bit = COMPUTESS_RESTART_SS_1_BIT, + .pdc_sync_reset_off = RPMH_PDC_COMPUTE1_SYNC_RESET, +}; + +static const struct cdsp_regs cdsp2_regs = { + .base = CDSP_2_BASE, + .tcsr_haltreq = TCSR_CDSP_2_HALTREQ, + .tcsr_haltack = TCSR_CDSP_2_HALTACK, + .tcsr_master_idle = TCSR_CDSP_2_MASTER_IDLE, + .tcsr_il1_master_idle = TCSR_CDSP_2_IL1_MASTER_IDLE, + .tcsr_pwr_on = TCSR_CDSP_2_PWR_ON, + .computess_restart_bit = COMPUTESS_RESTART_SS_2_BIT, + .pdc_sync_reset_off = RPMH_PDC_COMPUTE2_SYNC_RESET, +}; + +static const struct cdsp_regs cdsp3_regs = { + .base = CDSP_3_BASE, + .tcsr_haltreq = TCSR_CDSP_3_HALTREQ, + .tcsr_haltack = TCSR_CDSP_3_HALTACK, + .tcsr_master_idle = TCSR_CDSP_3_MASTER_IDLE, + .tcsr_il1_master_idle = TCSR_CDSP_3_IL1_MASTER_IDLE, + .tcsr_pwr_on = TCSR_CDSP_3_PWR_ON, + .computess_restart_bit = COMPUTESS_RESTART_SS_3_BIT, + .pdc_sync_reset_off = RPMH_PDC_COMPUTE3_SYNC_RESET, +}; + +static const struct qcom_lucidole_pll_config cdsp_q6_pll_cfg = { + .l_val = CDSP_Q6_PLL_L_VAL_SVS_L1_V2, + .cal_l_val = CDSP_Q6_PLL_CAL_L_VAL, + .pre_div = 1, + .config_ctl = CDSP_Q6_PLL_CONFIG_CTL_VAL, + .config_ctl_u = CDSP_Q6_PLL_CONFIG_CTL_U_VAL, + .config_ctl_u1 = CDSP_Q6_PLL_CONFIG_CTL_U1_VAL, + .test_ctl = CDSP_Q6_PLL_TEST_CTL_VAL, + .test_ctl_u = CDSP_Q6_PLL_TEST_CTL_U_VAL, + .test_ctl_u1 = CDSP_Q6_PLL_TEST_CTL_U1_VAL, + .test_ctl_u2 = CDSP_Q6_PLL_TEST_CTL_U2_VAL, + .user_ctl = CDSP_Q6_PLL_USER_CTL_VAL, + .user_ctl_u = CDSP_Q6_PLL_USER_CTL_U_VAL, + /* alpha (default) fractional mode */ +}; + +static bool boot_imem_reset_done; + +static const struct qcom_lucidole_pll_config hpass0_q6_pll_cfg = { + .l_val = HPASS_Q6_PLL_L_VAL_SVS_L1, + .cal_l_val = HPASS_Q6_PLL_CAL_L_VAL, + .pre_div = 1, + .config_ctl = HPASS_Q6_PLL_CONFIG_CTL_VAL, + .config_ctl_u = HPASS_Q6_PLL_CONFIG_CTL_U_VAL, + .config_ctl_u1 = HPASS_Q6_PLL_CONFIG_CTL_U1_VAL, + .test_ctl = HPASS_Q6_PLL_TEST_CTL_VAL, + .test_ctl_u = HPASS_Q6_PLL_TEST_CTL_U_VAL, + .test_ctl_u1 = HPASS_Q6_PLL_TEST_CTL_U1_VAL, + .test_ctl_u2 = HPASS_Q6_PLL_TEST_CTL_U2_VAL, + .user_ctl = HPASS_Q6_PLL_USER_CTL_VAL, + .user_ctl_u = HPASS_Q6_PLL_USER_CTL_U_VAL, + /* alpha (default) fractional mode */ +}; + +static uint32_t cdsp_efuse_bootimem_ecc_disable_bit(paddr_t base) +{ + switch (base) { + case CDSP_0_BASE: + return FEATURE_CONFIG0_CDSP0_BOOTIMEM_ECC_DISABLE_BIT; + case CDSP_1_BASE: + return FEATURE_CONFIG0_CDSP1_BOOTIMEM_ECC_DISABLE_BIT; + case CDSP_2_BASE: + return FEATURE_CONFIG0_CDSP2_BOOTIMEM_ECC_DISABLE_BIT; + default: + return FEATURE_CONFIG0_CDSP3_BOOTIMEM_ECC_DISABLE_BIT; + } +} + +static TEE_Result cdsp_enable_processor(paddr_t base) +{ + struct io_pa_va io = { .pa = base }; + vaddr_t v = io_pa_or_va(&io, CDSP_0_SIZE); + vaddr_t pub = v + CDSP_QDSP6SS_PUB_OFFSET; + vaddr_t core_cc = v + CDSP_QDSP6SS_CORE_CC_OFFSET; + struct io_pa_va fuse_io = { .pa = FUSE_CONTROLLER_SW_RANGE4_BASE }; + vaddr_t fuse = io_pa_or_va(&fuse_io, FUSE_CONTROLLER_SW_RANGE4_SIZE); + uint64_t timeout = 0; + TEE_Result res = TEE_SUCCESS; + + /* 1a. Clear RET_CFG; de-assert the ALT ares bypass. */ + io_write32(pub + CDSP_QDSP6SS_RET_CFG, 0); + io_clrbits32(v + CDSP_CC_ALT_RESET_CTL, + CDSP_CC_ALT_RESET_CTL_ALT_ARES_BYPASS_BIT); + + /* 2b. De-assert QDSP6 stop-core. */ + io_setbits32(pub + CDSP_QDSP6SS_BOOT_CORE_START, + QDSP6SS_BOOT_CORE_START_START_BIT); + + /* 3a. Efuse-gated VTCM ECC disable. */ + if (io_read32(fuse + FEATURE_CONFIG0) & + cdsp_efuse_bootimem_ecc_disable_bit(base)) { + io_clrbits32(pub + CDSP_QDSP6SS_MEMECC_CFG, + QDSP6SS_MEMECC_CFG_VTCM_EXT_CTRL_EN_BIT); + udelay(25); + } + + /* 4/5. Enable boot FSM auto-break + SW resume, then start the FSM. */ + io_write32(pub + CDSP_QDSP6SS_BOOT_AUTO_BREAK_EN, 1); + io_write32(pub + CDSP_QDSP6SS_BOOT_CMD, 1); + + /* 6. Wait for the FSM to auto-break. */ + while (!(io_read32(pub + CDSP_QDSP6SS_BOOT_STATUS) & + QDSP6SS_BOOT_STATUS_AUTO_BREAK_BIT)) + udelay(5); + + /* Invalidate the ACMU freq word before configuring the Q6 PLL. */ + io_setbits32(core_cc + CDSP_QDSP6SS_CORE_PLL_ACMU, + CORE_PLL_ACMU_CMON_FREQ_INVALID_BIT); + + /* 7. Program the Q6 PLL (Lucid-OLE). */ + res = qcom_lucidole_pll_enable(v + CDSP_QDSP6SS_PLL_OFFSET, + &cdsp_q6_pll_cfg); + if (res != TEE_SUCCESS) + return res; + + io_mask32(core_cc + CDSP_QDSP6SS_CORE_PLL_ACMU, + CDSP_Q6_PLL_L_VAL_SVS_L1_V2 * 10, + CORE_PLL_ACMU_CMON_FREQ_VAL_MASK); + io_clrbits32(core_cc + CDSP_QDSP6SS_CORE_PLL_ACMU, + CORE_PLL_ACMU_CMON_FREQ_INVALID_BIT); + + /* Program the Q6 core RCG. */ + res = qcom_clock_set_rate(core_cc + CDSP_QDSP6SS_CORE_CFG_RCGR, + core_cc + CDSP_QDSP6SS_CORE_CMD_RCGR, + CDSP_QDSP0RCG_CFG_VAL); + if (res != TEE_SUCCESS) + return res; + + /* 8. Program the Q6 core CBC. */ + io_setbits32(core_cc + CDSP_QDSP6SS_CORE_CBCR, + QDSP6SS_CORE_CBCR_CLK_ENABLE_BIT); + + /* 9. Resume the boot FSM. */ + io_write32(pub + CDSP_QDSP6SS_BOOT_RESUME_CMD, 1); + + /* 10. Poll STATUS for boot-FSM completion. */ + timeout = timeout_init_us(200000 * 5); + while (!timeout_elapsed(timeout)) { + if (io_read32(pub + CDSP_QDSP6SS_BOOT_STATUS) & + QDSP6SS_BOOT_STATUS_STATUS_BIT) + return TEE_SUCCESS; + udelay(5); + } + + return TEE_ERROR_TIMEOUT; +} + +static TEE_Result cdsp_reset_processor(const struct cdsp_regs *r) +{ + struct io_pa_va cdsp_io = { .pa = r->base }; + vaddr_t v = io_pa_or_va(&cdsp_io, CDSP_0_SIZE); + struct io_pa_va aoss_io = { .pa = AOSS_CC_BASE }; + vaddr_t aoss_cc = io_pa_or_va(&aoss_io, AOSS_CC_SIZE); + struct io_pa_va pdc_io = { .pa = RPMH_PDC_GLOBAL_BASE }; + vaddr_t pdc_global = io_pa_or_va(&pdc_io, RPMH_PDC_GLOBAL_SIZE); + struct io_pa_va tcsr_io = { .pa = TCSR_MUTEX_BASE }; + vaddr_t tcsr = io_pa_or_va(&tcsr_io, TCSR_MUTEX_SIZE); + uint64_t timeout = 0; + + /* Reset the retention logic; assert the ALT ares bypass. */ + io_write32(v + CDSP_QDSP6SS_PUB_OFFSET + CDSP_QDSP6SS_RET_CFG, 0x3); + io_setbits32(v + CDSP_CC_ALT_RESET_CTL, + CDSP_CC_ALT_RESET_CTL_ALT_ARES_BYPASS_BIT); + + if ((io_read32(tcsr + r->tcsr_pwr_on) & TCSR_CDSP_BIT) && + ((io_read32(tcsr + r->tcsr_master_idle) & TCSR_CDSP_BIT) == 0 || + (io_read32(tcsr + r->tcsr_il1_master_idle) & + TCSR_CDSP_BIT) == 0)) { + io_setbits32(tcsr + r->tcsr_haltreq, TCSR_CDSP_BIT); + + timeout = timeout_init_us(200000 * 5); + while (!(io_read32(tcsr + r->tcsr_haltack) & TCSR_CDSP_BIT)) { + if (timeout_elapsed(timeout)) + break; + udelay(5); + } + } + + /* Force the TBF core clock halt. */ + io_setbits32(v + CDSP_TBF_CFG_OFFSET + CDSP_TBF_CORE_CFG1, + TBF_CORE_FORCE_CLK_HALT_BIT); + + /* Assert the Turing PDC reset. */ + io_setbits32(pdc_global + r->pdc_sync_reset_off, PDC_SYNC_RESET_BIT); + + /* 4.2. Trigger the subsystem restart. */ + io_setbits32(aoss_cc + AOSS_CC_COMPUTESS_RESTART, + r->computess_restart_bit); + udelay(5); + io_setbits32(aoss_cc + AOSS_CC_COMPUTESS_CONFIG_RESTART, + r->computess_restart_bit); + udelay(200); + + /* Clear the halt request; wait 10ms. */ + io_clrbits32(tcsr + r->tcsr_haltreq, TCSR_CDSP_BIT); + udelay(10000); + + /* De-assert the PDC reset. */ + io_clrbits32(pdc_global + r->pdc_sync_reset_off, PDC_SYNC_RESET_BIT); + + /* 5. De-assert the subsystem restart. */ + io_clrbits32(aoss_cc + AOSS_CC_COMPUTESS_RESTART, + r->computess_restart_bit); + udelay(5); + io_clrbits32(aoss_cc + AOSS_CC_COMPUTESS_CONFIG_RESTART, + r->computess_restart_bit); + dsb(); + udelay(200); + + return TEE_SUCCESS; +} + +static TEE_Result cdsp_setup(paddr_t base) +{ + struct io_pa_va io = { .pa = base }; + vaddr_t v = io_pa_or_va(&io, CDSP_0_SIZE); + + if (base == CDSP_0_BASE && !boot_imem_reset_done) { + struct io_pa_va aoss_io = { .pa = AOSS_CC_BASE }; + vaddr_t aoss_cc = io_pa_or_va(&aoss_io, AOSS_CC_SIZE); + uint32_t dbg_cfg_val = io_read32(v + CDSP_QDSP6SS_PUB_OFFSET + + CDSP_QDSP6SS_DBG_CFG); + + io_setbits32(v + CDSP_CC_ALT_RESET_CTL, + CDSP_CC_ALT_RESET_CTL_ALT_ARES_BYPASS_BIT); + udelay(50); + + io_setbits32(aoss_cc + AOSS_CC_COMPUTESS_RESTART, + COMPUTESS_RESTART_SS_0_BIT); + udelay(5); + io_setbits32(aoss_cc + AOSS_CC_COMPUTESS_CONFIG_RESTART, + COMPUTESS_RESTART_SS_0_BIT); + udelay(500); + + io_clrbits32(aoss_cc + AOSS_CC_COMPUTESS_RESTART, + COMPUTESS_RESTART_SS_0_BIT); + udelay(5); + io_clrbits32(aoss_cc + AOSS_CC_COMPUTESS_CONFIG_RESTART, + COMPUTESS_RESTART_SS_0_BIT); + udelay(500); + + io_clrbits32(v + CDSP_CC_ALT_RESET_CTL, + CDSP_CC_ALT_RESET_CTL_ALT_ARES_BYPASS_BIT); + udelay(50); + + io_write32(v + CDSP_QDSP6SS_PUB_OFFSET + CDSP_QDSP6SS_DBG_CFG, + dbg_cfg_val); + + boot_imem_reset_done = true; + } + + io_setbits32(v + CDSP_CC_NSPAUX_GDSCR, NSPAUX_GDSCR_HW_CONTROL_BIT); + + if (qcom_clock_enable_cbc(v + CDSP_CC_Q6SS_Q6_AXIM_CBCR) != TEE_SUCCESS) + return TEE_ERROR_TIMEOUT; + if (qcom_clock_enable_cbc(v + CDSP_CC_Q6SS_AHBM_AON_CBCR) != + TEE_SUCCESS) + return TEE_ERROR_TIMEOUT; + + io_setbits32(v + CDSP_CC_Q6SS_AXIS_CBCR, CDSP_CC_CBCR_CLK_ENABLE_BIT); + io_setbits32(v + CDSP_CC_NSPAUX_XO_CBCR, CDSP_CC_CBCR_CLK_ENABLE_BIT); + io_setbits32(v + CDSP_CC_CENG_NSP_CBCR, CDSP_CC_CBCR_CLK_ENABLE_BIT); + io_setbits32(v + CDSP_CC_CENG_PROC_CBCR, CDSP_CC_CBCR_CLK_ENABLE_BIT); + io_setbits32(v + CDSP_CC_CENG_AHBS_CBCR, CDSP_CC_CBCR_CLK_ENABLE_BIT); + io_setbits32(v + CDSP_CC_NSPNOC_CFG_AHBS_CBCR, + CDSP_CC_CBCR_CLK_ENABLE_BIT); + io_setbits32(v + CDSP_CC_CENG_NOC_CBCR, CDSP_CC_CBCR_CLK_ENABLE_BIT); + io_setbits32(v + CDSP_CC_CENG_NSP_AON_CBCR, + CDSP_CC_CBCR_CLK_ENABLE_BIT); + io_setbits32(v + CDSP_CC_NSPNOC_CBCR, CDSP_CC_CBCR_CLK_ENABLE_BIT); + + /* Q6SS slave clock (ENABLE_CBCR_AND_SPIN in the reference). */ + if (qcom_clock_enable_cbc(v + CDSP_CC_Q6SS_AHBS_AON_CBCR) != + TEE_SUCCESS) + return TEE_ERROR_TIMEOUT; + + io_setbits32(v + CDSP_CC_MCSW_CBCR, CDSP_CC_CBCR_CLK_ENABLE_BIT); + io_setbits32(v + CDSP_CC_MCMW_CBCR, CDSP_CC_CBCR_CLK_ENABLE_BIT); + io_setbits32(v + CDSP_CC_MCSW_AHBS_CBCR, CDSP_CC_CBCR_CLK_ENABLE_BIT); + io_setbits32(v + CDSP_CC_MCMW_AHBS_CBCR, CDSP_CC_CBCR_CLK_ENABLE_BIT); + + /* Program the debug config register, then a DSB. */ + io_write32(v + CDSP_QDSP6SS_PUB_OFFSET + CDSP_QDSP6SS_DBG_CFG, 0); + dsb(); + + return TEE_SUCCESS; +} + +static TEE_Result hpass_enable_processor_0(void) +{ + struct io_pa_va io = { .pa = HPASS_0_BASE }; + vaddr_t v = io_pa_or_va(&io, HPASS_0_SIZE); + uint64_t timeout = timeout_init_us(200000 * 5); + + io_write32(v + HPASS_QDSP6SS_BOOT_RESUME_CMD, 1); + + while (!timeout_elapsed(timeout)) { + if (io_read32(v + HPASS_QDSP6SS_BOOT_STATUS) & + QDSP6SS_BOOT_STATUS_STATUS_BIT) + return TEE_SUCCESS; + udelay(5); + } + + return TEE_ERROR_TIMEOUT; +} + +static TEE_Result hpass_enable_processor_1(void) +{ + struct io_pa_va io = { .pa = HPASS_1_BASE }; + vaddr_t v = io_pa_or_va(&io, HPASS_1_SIZE); + + io_setbits32(v + HPASS_QDSP6SS_BOOT_CORE_START, + QDSP6SS_BOOT_CORE_START_START_BIT); + io_write32(v + HPASS_QDSP6SS_BOOT_CMD, 1); + + return TEE_SUCCESS; +} + +static TEE_Result hpass_enable_processor_2(void) +{ + struct io_pa_va io = { .pa = HPASS_2_BASE }; + vaddr_t v = io_pa_or_va(&io, HPASS_2_SIZE); + + io_setbits32(v + HPASS_QDSP6SS_BOOT_CORE_START, + QDSP6SS_BOOT_CORE_START_START_BIT); + io_write32(v + HPASS_QDSP6SS_BOOT_CMD, 1); + + return TEE_SUCCESS; +} + +static TEE_Result hpass_noc_quiesce(vaddr_t tcsr, uint32_t qreqn_bit, + uint32_t qaccept_bit, uint32_t qdeny_bit) +{ + uint64_t timeout = 0; + + io_clrbits32(tcsr + TCSR_HPASS_AG_NOC_QCHANNEL_QREQN, qreqn_bit); + timeout = timeout_init_us(500); + while (io_read32(tcsr + TCSR_HPASS_AG_NOC_QCHANNEL_QACCEPTN) & + qaccept_bit) { + if (timeout_elapsed(timeout)) + goto retry; + udelay(5); + } + return TEE_SUCCESS; + +retry: + if (!(io_read32(tcsr + TCSR_HPASS_AG_NOC_QCHANNEL_QACCEPTN) & + qdeny_bit)) + return TEE_SUCCESS; + + io_setbits32(tcsr + TCSR_HPASS_AG_NOC_QCHANNEL_QREQN, qreqn_bit); + udelay(10); + io_clrbits32(tcsr + TCSR_HPASS_AG_NOC_QCHANNEL_QREQN, qreqn_bit); + + timeout = timeout_init_us(500); + while (io_read32(tcsr + TCSR_HPASS_AG_NOC_QCHANNEL_QACCEPTN) & + qaccept_bit) { + if (timeout_elapsed(timeout)) + return TEE_ERROR_BUSY; + udelay(5); + } + + return TEE_SUCCESS; +} + +static TEE_Result hpass_reset_processor(void) +{ + struct io_pa_va cc_io = { .pa = HPASS_CC_BASE }; + vaddr_t cc = io_pa_or_va(&cc_io, HPASS_CC_SIZE); + struct io_pa_va tcsr_io = { .pa = TCSR_MUTEX_BASE }; + vaddr_t tcsr = io_pa_or_va(&tcsr_io, TCSR_MUTEX_SIZE); + struct io_pa_va hpass_tcsr_io = { .pa = HPASS_TCSR_BASE }; + vaddr_t hpass_tcsr = io_pa_or_va(&hpass_tcsr_io, HPASS_TCSR_SIZE); + struct io_pa_va h0_io = { .pa = HPASS_0_BASE }; + struct io_pa_va h1_io = { .pa = HPASS_1_BASE }; + struct io_pa_va h2_io = { .pa = HPASS_2_BASE }; + vaddr_t h0 = io_pa_or_va(&h0_io, HPASS_0_SIZE); + vaddr_t h1 = io_pa_or_va(&h1_io, HPASS_1_SIZE); + vaddr_t h2 = io_pa_or_va(&h2_io, HPASS_2_SIZE); + struct io_pa_va pdc_io = { .pa = RPMH_PDC_GLOBAL_BASE }; + vaddr_t pdc_global = io_pa_or_va(&pdc_io, RPMH_PDC_GLOBAL_SIZE); + struct io_pa_va aoss_io = { .pa = AOSS_CC_BASE }; + vaddr_t aoss_cc = io_pa_or_va(&aoss_io, AOSS_CC_SIZE); + uint64_t timeout = 0; + uint32_t i = 0; + TEE_Result res = TEE_SUCCESS; + + io_clrbits32(cc + HPASS_CC_Q6DSPSS0_XO_CBCR, + Q6DSPSS_XO_CBCR_IGNORE_ALL_CLK_DIS_BIT); + io_clrbits32(cc + HPASS_CC_Q6DSPSS1_XO_CBCR, + Q6DSPSS_XO_CBCR_IGNORE_ALL_CLK_DIS_BIT); + io_clrbits32(cc + HPASS_CC_Q6DSPSS2_XO_CBCR, + Q6DSPSS_XO_CBCR_IGNORE_ALL_CLK_DIS_BIT); + + io_write32(h0 + HPASS_QDSP6SS_RET_CFG, 0x3); + io_write32(h1 + HPASS_QDSP6SS_RET_CFG, 0x3); + io_write32(h2 + HPASS_QDSP6SS_RET_CFG, 0x3); + + for (i = 0; i <= HPASS_ALT_RESET_Q6SS_n_MAXN; i++) + io_setbits32(hpass_tcsr + HPASS_ALT_RESET_Q6SS_n(i), + HPASS_ALT_ARES_ENABLE_BIT); + + /* Isolate each Q6 master bus interface and wait for its halt ACK. */ + io_setbits32(tcsr + TCSR_HPASS_HALTREQ, + TCSR_HPASS_Q6SS_0_BUSM_HALTREQ_BIT); + timeout = timeout_init_us(200000 * 5); + while (!(io_read32(tcsr + TCSR_HPASS_HALTACK) & + TCSR_HPASS_Q6SS_0_BUSM_HALTACK_BIT)) { + if (timeout_elapsed(timeout)) + return TEE_ERROR_TIMEOUT; + udelay(5); + } + + io_setbits32(tcsr + TCSR_HPASS_HALTREQ, + TCSR_HPASS_Q6SS_1_BUSM_HALTREQ_BIT); + timeout = timeout_init_us(200000 * 5); + while (!(io_read32(tcsr + TCSR_HPASS_HALTACK) & + TCSR_HPASS_Q6SS_1_BUSM_HALTACK_BIT)) { + if (timeout_elapsed(timeout)) + return TEE_ERROR_TIMEOUT; + udelay(5); + } + + io_setbits32(tcsr + TCSR_HPASS_HALTREQ, + TCSR_HPASS_Q6SS_2_BUSM_HALTREQ_BIT); + timeout = timeout_init_us(200000 * 5); + while (!(io_read32(tcsr + TCSR_HPASS_HALTACK) & + TCSR_HPASS_Q6SS_2_BUSM_HALTACK_BIT)) { + if (timeout_elapsed(timeout)) + return TEE_ERROR_TIMEOUT; + udelay(5); + } + + /* Clear the HPASS TCSR mutex registers before proceeding. */ + for (i = 0; i <= HPASS_MUTEX_REG_n_MAXN; i++) + io_write32(hpass_tcsr + HPASS_MUTEX_REG_n(i), 0); + + /* Halt all remaining audio traffic initiators. */ + io_setbits32(tcsr + TCSR_HPASS_HALTREQ, TCSR_HALTREQ_VAL); + timeout = timeout_init_us(500); + while ((io_read32(tcsr + TCSR_HPASS_HALTACK) & TCSR_HALTREQ_VAL) != + TCSR_HALTREQ_VAL) { + if (timeout_elapsed(timeout)) + break; + udelay(5); + } + + /* + * Halt the Audio NoC channel (Perforce SSR step "6. Halt + * AUDIO_NOC"). + */ + res = hpass_noc_quiesce(tcsr, TCSR_HPASS_AUDIO_NOC_QREQN_BIT, + TCSR_HPASS_AUDIO_NOC_QACCEPTN_BIT, + TCSR_HPASS_AUDIO_NOC_QDENY_BIT); + if (res != TEE_SUCCESS) + return res; + + /* Halt the ENPU NoC channel (Perforce SSR step "6. Halt ENPU_NOC"). */ + res = hpass_noc_quiesce(tcsr, TCSR_HPASS_ENPU_NOC_QREQN_BIT, + TCSR_HPASS_ENPU_NOC_QACCEPTN_BIT, + TCSR_HPASS_ENPU_NOC_QDENY_BIT); + if (res != TEE_SUCCESS) + return res; + + /* Halt the AG NoC channel (Perforce SSR step "6. Halt AG_NOC"). */ + res = hpass_noc_quiesce(tcsr, TCSR_HPASS_AG_NOC_QREQN_BIT, + TCSR_HPASS_AG_NOC_QACCEPTN_BIT, + TCSR_HPASS_AG_NOC_QDENY_BIT); + if (res != TEE_SUCCESS) + return res; + + /* Assert the RPMH PDC audio sync reset and pulse the HPASS restart. */ + io_setbits32(pdc_global + RPMH_PDC_AUDIO_SYNC_RESET, + PDC_SYNC_RESET_BIT); + udelay(10000); + io_setbits32(aoss_cc + AOSS_CC_HPASS_RESTART, + HPASS_RESTART_SS_RESTART_BIT); + udelay(5); + io_setbits32(aoss_cc + AOSS_CC_HPASS_CONFIG_RESTART, + HPASS_RESTART_SS_RESTART_BIT); + udelay(200); + + io_clrbits32(pdc_global + RPMH_PDC_AUDIO_SYNC_RESET, + PDC_SYNC_RESET_BIT); + + /* Clear all the halt requests, re-open the three NoC channels. */ + io_write32(tcsr + TCSR_HPASS_HALTREQ, 0); + io_setbits32(tcsr + TCSR_HPASS_AG_NOC_QCHANNEL_QREQN, + TCSR_HPASS_AG_NOC_QREQN_BIT | + TCSR_HPASS_AUDIO_NOC_QREQN_BIT | + TCSR_HPASS_ENPU_NOC_QREQN_BIT); + + io_clrbits32(aoss_cc + AOSS_CC_HPASS_RESTART, + HPASS_RESTART_SS_RESTART_BIT); + udelay(5); + io_clrbits32(aoss_cc + AOSS_CC_HPASS_CONFIG_RESTART, + HPASS_RESTART_SS_RESTART_BIT); + udelay(100); + + io_write32(h0 + HPASS_QDSP6SS_RET_CFG, 0); + io_write32(h1 + HPASS_QDSP6SS_RET_CFG, 0); + io_write32(h2 + HPASS_QDSP6SS_RET_CFG, 0); + + return TEE_SUCCESS; +} + +static TEE_Result hpass_setup_0(void) +{ + struct io_pa_va io = { .pa = HPASS_0_BASE }; + vaddr_t v = io_pa_or_va(&io, HPASS_0_SIZE); + vaddr_t core_cc = v + HPASS_QDSP6SS_CORE_CC_OFFSET; + struct io_pa_va tcm_io = { .pa = HPASS_TCM_BASE }; + vaddr_t tcm = io_pa_or_va(&tcm_io, HPASS_TCM_SIZE); + uint64_t timeout = timeout_init_us(200000 * 5); + uint32_t i = 0; + TEE_Result res = TEE_SUCCESS; + + io_write32(v + HPASS_QDSP6SS_RET_CFG, 0); + io_write32(v + HPASS_QDSP6SS_DBG_CFG, 0); + + io_setbits32(v + HPASS_QDSP6SS_BOOT_CORE_START, + QDSP6SS_BOOT_CORE_START_START_BIT); + io_write32(v + HPASS_QDSP6SS_BOOT_AUTO_BREAK_EN, 1); + io_write32(v + HPASS_QDSP6SS_BOOT_CMD, 1); + + while (!timeout_elapsed(timeout)) { + if (io_read32(v + HPASS_QDSP6SS_BOOT_STATUS) & + QDSP6SS_BOOT_STATUS_AUTO_BREAK_BIT) + break; + udelay(5); + } + + io_setbits32(core_cc + HPASS_QDSP6SS_CORE_CBCR, + QDSP6SS_CORE_CBCR_CLK_ENABLE_BIT); + + io_setbits32(core_cc + HPASS_QDSP6SS_CORE_PLL_ACMU, + CORE_PLL_ACMU_CMON_FREQ_INVALID_BIT); + + res = qcom_lucidole_pll_enable(v + HPASS_QDSP6SS_PLL_OFFSET, + &hpass0_q6_pll_cfg); + if (res != TEE_SUCCESS) + return res; + + io_mask32(core_cc + HPASS_QDSP6SS_CORE_PLL_ACMU, + HPASS_Q6_PLL_L_VAL_SVS_L1 * 10, + CORE_PLL_ACMU_CMON_FREQ_VAL_MASK); + io_clrbits32(core_cc + HPASS_QDSP6SS_CORE_PLL_ACMU, + CORE_PLL_ACMU_CMON_FREQ_INVALID_BIT); + + res = qcom_clock_set_rate(core_cc + HPASS_QDSP6SS_CORE_CFG_RCGR, + core_cc + HPASS_QDSP6SS_CORE_CMD_RCGR, + CDSP_QDSP0RCG_CFG_VAL); + if (res != TEE_SUCCESS) + return res; + + /* Retain the VTCM (HPASS_TCM) memories across CHIP_CX collapse. */ + io_setbits32(tcm + HPASS_TCM_AXIS_HS_CBCR, CBCR_BRANCH_ENABLE_BIT); + for (i = 0; i <= HPASS_TCM_256KB_BLOCKn_CBCR_MAXN; i++) { + io_setbits32(tcm + HPASS_TCM_256KB_BLOCKn_CBCR(i), + TCM_BLOCK_FORCE_MEM_CORE_ON_BIT); + io_setbits32(tcm + HPASS_TCM_256KB_BLOCKn_CBCR(i), + TCM_BLOCK_CLK_ENABLE_BIT); + } + + return TEE_SUCCESS; +} + +static TEE_Result hpass_setup_1(void) +{ + struct io_pa_va io = { .pa = HPASS_1_BASE }; + vaddr_t v = io_pa_or_va(&io, HPASS_1_SIZE); + + io_write32(v + HPASS_QDSP6SS_RET_CFG, 0); + io_setbits32(v + HPASS_QDSP6SS_CORE_CC_OFFSET + HPASS_QDSP6SS_CORE_CBCR, + QDSP6SS_CORE_CBCR_CLK_ENABLE_BIT); + io_write32(v + HPASS_QDSP6SS_DBG_CFG, 0); + + return TEE_SUCCESS; +} + +static TEE_Result hpass_setup_2(void) +{ + struct io_pa_va io = { .pa = HPASS_2_BASE }; + vaddr_t v = io_pa_or_va(&io, HPASS_2_SIZE); + + io_write32(v + HPASS_QDSP6SS_RET_CFG, 0); + io_setbits32(v + HPASS_QDSP6SS_CORE_CC_OFFSET + HPASS_QDSP6SS_CORE_CBCR, + QDSP6SS_CORE_CBCR_CLK_ENABLE_BIT); + io_write32(v + HPASS_QDSP6SS_DBG_CFG, 0); + + return TEE_SUCCESS; +} + +static TEE_Result soccp_enable_processor(void) +{ + struct io_pa_va io = { .pa = SOCCP_CSR_BASE }; + vaddr_t v = io_pa_or_va(&io, SOCCP_CSR_SIZE); + + io_write32(v + SOCCP_SOCCP_RVSSMP_BOOT_SUPPRESS, 0); + + return TEE_SUCCESS; +} + +static TEE_Result soccp_reset_processor(void) +{ + struct io_pa_va csr_io = { .pa = SOCCP_CSR_BASE }; + vaddr_t csr = io_pa_or_va(&csr_io, SOCCP_CSR_SIZE); + struct io_pa_va aoss_io = { .pa = AOSS_CC_BASE }; + vaddr_t aoss_cc = io_pa_or_va(&aoss_io, AOSS_CC_SIZE); + + io_write32(csr + SOCCP_SOCCP_RVSSMP_BOOT_SUPPRESS, 1); + + io_setbits32(aoss_cc + AOSS_CC_SOCCP_RESTART, + SOCCP_RESTART_SS_RESTART_BIT); + io_setbits32(aoss_cc + AOSS_CC_SOCCP_CONFIG_RESTART, + SOCCP_RESTART_SS_RESTART_BIT); + + udelay(300); + + io_clrbits32(aoss_cc + AOSS_CC_SOCCP_CONFIG_RESTART, + SOCCP_RESTART_SS_RESTART_BIT); + io_clrbits32(aoss_cc + AOSS_CC_SOCCP_RESTART, + SOCCP_RESTART_SS_RESTART_BIT); + + return TEE_SUCCESS; +} + +static TEE_Result soccp_setup(void) +{ + struct io_pa_va gcc_io = { .pa = GCC_BASE }; + vaddr_t gcc = io_pa_or_va(&gcc_io, GCC_SIZE); + uint64_t timeout = timeout_init_us(10 * 1000); + + io_setbits32(gcc + GCC_SOCCP_ANOC_AXI_CBCR, SOCCP_CBCR_CLK_ENABLE_BIT); + io_setbits32(gcc + GCC_SOCCP_CNOC_M_AHB_CBCR, + SOCCP_CBCR_CLK_ENABLE_BIT); + io_setbits32(gcc + GCC_SOCCP_CNOC_S_AHB_CBCR, + SOCCP_CBCR_CLK_ENABLE_BIT); + io_setbits32(gcc + GCC_SOCCP_F_CBCR, SOCCP_CBCR_CLK_ENABLE_BIT); + io_setbits32(gcc + GCC_SOCCP_SS_H_CBCR, SOCCP_CBCR_CLK_ENABLE_BIT); + io_setbits32(gcc + GCC_SOCCP_TMR_CBCR, SOCCP_CBCR_CLK_ENABLE_BIT); + + io_mask32(gcc + GCC_SOCCP_CFG_RCGR, + SHIFT_U32(SOCCP_RCG_SRC_SEL_VAL, + GCC_SOCCP_RCGR_SRC_SEL_SHIFT), + GCC_SOCCP_RCGR_SRC_SEL_MASK); + io_mask32(gcc + GCC_SOCCP_CFG_RCGR, SOCCP_RCG_SRC_DIV_VAL, + GCC_SOCCP_RCGR_SRC_DIV_MASK); + io_setbits32(gcc + GCC_SOCCP_CMD_RCGR, CMD_RCGR_UPDATE_BIT); + + while (io_read32(gcc + GCC_SOCCP_CMD_RCGR) & CMD_RCGR_UPDATE_BIT) { + if (timeout_elapsed(timeout)) + return TEE_ERROR_TIMEOUT; + udelay(1); + } + + return TEE_SUCCESS; +} + +TEE_Result qcom_clock_enable_pas(enum qcom_clk_group group) +{ + switch (group) { + case QCOM_CLKS_TURING: + return cdsp_setup(CDSP_0_BASE); + case QCOM_CLKS_TURING1: + return cdsp_setup(CDSP_1_BASE); + case QCOM_CLKS_TURING2: + return cdsp_setup(CDSP_2_BASE); + case QCOM_CLKS_TURING3: + return cdsp_setup(CDSP_3_BASE); + case QCOM_CLKS_HPASS0: + return hpass_setup_0(); + case QCOM_CLKS_HPASS1: + return hpass_setup_1(); + case QCOM_CLKS_HPASS2: + return hpass_setup_2(); + case QCOM_CLKS_SOCCP: + return soccp_setup(); + default: + return TEE_ERROR_NOT_SUPPORTED; + } +} + +TEE_Result qcom_clock_enable_pas_processor(enum qcom_clk_group group) +{ + switch (group) { + case QCOM_CLKS_TURING: + return cdsp_enable_processor(CDSP_0_BASE); + case QCOM_CLKS_TURING1: + return cdsp_enable_processor(CDSP_1_BASE); + case QCOM_CLKS_TURING2: + return cdsp_enable_processor(CDSP_2_BASE); + case QCOM_CLKS_TURING3: + return cdsp_enable_processor(CDSP_3_BASE); + case QCOM_CLKS_HPASS0: + return hpass_enable_processor_0(); + case QCOM_CLKS_HPASS1: + return hpass_enable_processor_1(); + case QCOM_CLKS_HPASS2: + return hpass_enable_processor_2(); + case QCOM_CLKS_SOCCP: + return soccp_enable_processor(); + default: + return TEE_ERROR_NOT_SUPPORTED; + } +} + +TEE_Result qcom_clock_pas_reset(enum qcom_clk_group group) +{ + switch (group) { + case QCOM_CLKS_TURING: + return cdsp_reset_processor(&cdsp0_regs); + case QCOM_CLKS_TURING1: + return cdsp_reset_processor(&cdsp1_regs); + case QCOM_CLKS_TURING2: + return cdsp_reset_processor(&cdsp2_regs); + case QCOM_CLKS_TURING3: + return cdsp_reset_processor(&cdsp3_regs); + case QCOM_CLKS_HPASS0: + case QCOM_CLKS_HPASS1: + case QCOM_CLKS_HPASS2: + /* + * HPASS reset is a single combined sequence covering all 3 + * DSPs (see Reset_HPASSProcessor), not per-instance, so any of + * the group values triggers the full reset. + */ + return hpass_reset_processor(); + case QCOM_CLKS_SOCCP: + return soccp_reset_processor(); + default: + return TEE_ERROR_NOT_SUPPORTED; + } +} diff --git a/core/drivers/clk/qcom/platform/nord/clock_group_qcom.h b/core/drivers/clk/qcom/platform/nord/clock_group_qcom.h new file mode 100644 index 000000000..806d76fed --- /dev/null +++ b/core/drivers/clk/qcom/platform/nord/clock_group_qcom.h @@ -0,0 +1,287 @@ +/* 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 SE_GCC_HSCNOC_NSP_0_CBCR 0x8 +#define SE_GCC_NSP_0_CFG_CBCR 0xf024 +#define SE_GCC_NSP_0_HSCNOC_CBCR 0xf01c +#define SE_GCC_NSP_0_COMP_NOC_CBCR 0xf014 +#define SE_GCC_NSP_0_QTB_CBCR 0xb008 +#define SE_GCC_TCU_NSP_0_QTB_CBCR 0x4030 + +#define SE_GCC_HSCNOC_NSP_1_CBCR 0x10 +#define SE_GCC_NSP_1_CFG_CBCR 0x10024 +#define SE_GCC_NSP_1_HSCNOC_CBCR 0x1001c +#define SE_GCC_NSP_1_COMP_NOC_CBCR 0x10014 +#define SE_GCC_NSP_1_QTB_CBCR 0xc008 +#define SE_GCC_TCU_NSP_1_QTB_CBCR 0x4038 + +#define SE_GCC_HSCNOC_NSP_2_CBCR 0x18 +#define SE_GCC_NSP_2_CFG_CBCR 0x11024 +#define SE_GCC_NSP_2_HSCNOC_CBCR 0x1101c +#define SE_GCC_NSP_2_COMP_NOC_CBCR 0x11014 +#define SE_GCC_NSP_2_QTB_CBCR 0xd008 +#define SE_GCC_TCU_NSP_2_QTB_CBCR 0x4040 + +#define SE_GCC_HSCNOC_NSP_3_CBCR 0x20 +#define SE_GCC_NSP_3_CFG_CBCR 0x12024 +#define SE_GCC_NSP_3_HSCNOC_CBCR 0x1201c +#define SE_GCC_NSP_3_COMP_NOC_CBCR 0x12014 +#define SE_GCC_NSP_3_QTB_CBCR 0xe008 +#define SE_GCC_TCU_NSP_3_QTB_CBCR 0x4048 + +#define SE_GCC_CBCR_CLK_ENABLE_BIT BIT(0) +#define SE_GCC_CBCR_CLK_OFF_BIT BIT(31) +#define SE_GCC_CBCR_HW_CTL_ENABLE_BIT BIT(1) + +#define CDSP_NSP_SS_CC_OFFSET 0x00000000 +#define CDSP_TBF_CFG_OFFSET 0x0021c000 +#define CDSP_QDSP6SS_PUB_OFFSET 0x00238000 +#define CDSP_QDSP6SS_PLL_OFFSET 0x00278000 +#define CDSP_QDSP6SS_CORE_CC_OFFSET 0x0027c000 + +/* Offsets within the TBF_CFG block (CDSP_TBF_CFG_OFFSET). */ +#define CDSP_TBF_CORE_CFG1 0x8 +#define TBF_CORE_FORCE_CLK_HALT_BIT BIT(1) + +/* Offsets within the QDSP6 PUB block (CDSP_QDSP6SS_PUB_OFFSET). */ +#define CDSP_QDSP6SS_DBG_CFG 0x18 +#define CDSP_QDSP6SS_RET_CFG 0x1c +#define CDSP_QDSP6SS_BOOT_CORE_START 0x400 +#define CDSP_QDSP6SS_BOOT_CMD 0x404 +#define CDSP_QDSP6SS_BOOT_STATUS 0x408 +#define CDSP_QDSP6SS_BOOT_AUTO_BREAK_EN 0x410 +#define CDSP_QDSP6SS_BOOT_RESUME_CMD 0x414 +#define CDSP_QDSP6SS_MEMECC_CFG 0x440 + +#define QDSP6SS_BOOT_CORE_START_START_BIT BIT(0) +#define QDSP6SS_BOOT_STATUS_STATUS_BIT BIT(0) +#define QDSP6SS_BOOT_STATUS_AUTO_BREAK_BIT BIT(31) +#define QDSP6SS_MEMECC_CFG_VTCM_EXT_CTRL_EN_BIT BIT(0) + +#define CDSP_CC_ALT_RESET_CTL 0x1044 +#define CDSP_CC_NSPAUX_GDSCR 0x204 +#define CDSP_CC_CENG_AHBS_CBCR 0x240 +#define CDSP_CC_NSPNOC_CFG_AHBS_CBCR 0x244 +#define CDSP_CC_MCMW_AHBS_CBCR 0x248 +#define CDSP_CC_MCSW_AHBS_CBCR 0x24c +#define CDSP_CC_NSPNOC_CBCR 0x268 +#define CDSP_CC_CENG_NOC_CBCR 0x278 +#define CDSP_CC_CENG_NSP_CBCR 0x298 +#define CDSP_CC_MCSW_CBCR 0x29c +#define CDSP_CC_CENG_NSP_AON_CBCR 0x2b0 +#define CDSP_CC_MCMW_CBCR 0x2b4 +#define CDSP_CC_CENG_PROC_CBCR 0x2d0 +#define CDSP_CC_Q6SS_Q6_AXIM_CBCR 0x404 +#define CDSP_CC_Q6SS_AHBM_AON_CBCR 0x40c +#define CDSP_CC_Q6SS_AHBS_AON_CBCR 0x410 +#define CDSP_CC_NSPAUX_XO_CBCR 0x54 +#define CDSP_CC_Q6SS_AXIS_CBCR 0x408 + +#define CDSP_CC_ALT_RESET_CTL_ALT_ARES_BYPASS_BIT BIT(0) +#define NSPAUX_GDSCR_HW_CONTROL_BIT BIT(1) +#define CDSP_CC_CBCR_CLK_ENABLE_BIT BIT(0) + +#define CDSP_QDSP0RCG_CFG_VAL 0x100201 + +/* + * Offsets within the core clock-controller block + * (CDSP_QDSP6SS_CORE_CC_OFFSET). + */ + +#define CDSP_QDSP6SS_CORE_CMD_RCGR 0x20 +#define CDSP_QDSP6SS_CORE_CFG_RCGR 0x24 +#define CDSP_QDSP6SS_CORE_CBCR 0x40 +#define CDSP_QDSP6SS_CORE_PLL_ACMU 0x2d8 + +#define QDSP6SS_CORE_CBCR_CLK_ENABLE_BIT BIT(0) +#define CORE_PLL_ACMU_CMON_FREQ_INVALID_BIT BIT(31) +#define CORE_PLL_ACMU_CMON_FREQ_VAL_MASK 0x3fff + +#define CDSP_Q6_PLL_L_VAL_SVS_L1_V2 0x30 +#define CDSP_Q6_PLL_CAL_L_VAL 0x44 +#define CDSP_Q6_PLL_CONFIG_CTL_VAL 0x20485699 +#define CDSP_Q6_PLL_CONFIG_CTL_U_VAL 0x00182261 +#define CDSP_Q6_PLL_CONFIG_CTL_U1_VAL 0x82AA299C +#define CDSP_Q6_PLL_TEST_CTL_VAL 0x00000000 +#define CDSP_Q6_PLL_TEST_CTL_U_VAL 0x00000003 +#define CDSP_Q6_PLL_TEST_CTL_U1_VAL 0x00009000 +#define CDSP_Q6_PLL_TEST_CTL_U2_VAL 0x00000034 +#define CDSP_Q6_PLL_USER_CTL_VAL 0x00000000 +#define CDSP_Q6_PLL_USER_CTL_U_VAL 0x00400005 + +#define TCSR_CDSP_0_HALTREQ 0x76004 +#define TCSR_CDSP_0_HALTACK 0x76008 +#define TCSR_CDSP_0_MASTER_IDLE 0x7600c +#define TCSR_CDSP_0_PWR_ON 0x76010 +#define TCSR_CDSP_0_IL1_MASTER_IDLE 0x76018 +#define TCSR_CDSP_1_HALTREQ 0x76044 +#define TCSR_CDSP_1_HALTACK 0x76048 +#define TCSR_CDSP_1_MASTER_IDLE 0x7604c +#define TCSR_CDSP_1_PWR_ON 0x76050 +#define TCSR_CDSP_1_IL1_MASTER_IDLE 0x76058 +#define TCSR_CDSP_2_HALTREQ 0x7607c +#define TCSR_CDSP_2_HALTACK 0x76080 +#define TCSR_CDSP_2_MASTER_IDLE 0x76084 +#define TCSR_CDSP_2_PWR_ON 0x76088 +#define TCSR_CDSP_2_IL1_MASTER_IDLE 0x76090 +#define TCSR_CDSP_3_HALTREQ 0x760b4 +#define TCSR_CDSP_3_HALTACK 0x760b8 +#define TCSR_CDSP_3_MASTER_IDLE 0x760bc +#define TCSR_CDSP_3_PWR_ON 0x760c0 +#define TCSR_CDSP_3_IL1_MASTER_IDLE 0x760c8 +#define TCSR_CDSP_BIT BIT(0) +#define TCSR_BOOT_IMEM_DISABLE 0x1300 +#define TCSR_BOOT_IMEM_DISABLE_BIT BIT(0) + +/* AOSS_CC compute-subsystem restart (within AOSS_CC window, AOSS_CC_BASE). */ +#define AOSS_CC_COMPUTESS_RESTART 0x7020 +#define AOSS_CC_COMPUTESS_CONFIG_RESTART 0x704c +#define COMPUTESS_RESTART_SS_0_BIT BIT(0) +#define COMPUTESS_RESTART_SS_1_BIT BIT(1) +#define COMPUTESS_RESTART_SS_2_BIT BIT(2) +#define COMPUTESS_RESTART_SS_3_BIT BIT(3) + +#define RPMH_PDC_AUDIO_SYNC_RESET 0x0 +#define RPMH_PDC_COMPUTE_SYNC_RESET 0x6000 +#define RPMH_PDC_COMPUTE1_SYNC_RESET 0x7000 +#define RPMH_PDC_COMPUTE2_SYNC_RESET 0x8000 +#define RPMH_PDC_COMPUTE3_SYNC_RESET 0x9000 +#define PDC_SYNC_RESET_BIT BIT(0) + +#define FEATURE_CONFIG0 0x100 +#define FEATURE_CONFIG0_CDSP0_BOOTIMEM_ECC_DISABLE_BIT BIT(24) +#define FEATURE_CONFIG0_CDSP1_BOOTIMEM_ECC_DISABLE_BIT BIT(26) +#define FEATURE_CONFIG0_CDSP2_BOOTIMEM_ECC_DISABLE_BIT BIT(28) +#define FEATURE_CONFIG0_CDSP3_BOOTIMEM_ECC_DISABLE_BIT BIT(30) + +/* Offsets within the QDSP6 PUB block (HPASS_n_BASE + 0). */ +#define HPASS_QDSP6SS_DBG_CFG 0x18 +#define HPASS_QDSP6SS_RET_CFG 0x1c +#define HPASS_QDSP6SS_BOOT_CORE_START 0x400 +#define HPASS_QDSP6SS_BOOT_CMD 0x404 +#define HPASS_QDSP6SS_BOOT_STATUS 0x408 +#define HPASS_QDSP6SS_BOOT_AUTO_BREAK_EN 0x410 +#define HPASS_QDSP6SS_BOOT_RESUME_CMD 0x414 + +#define HPASS_QDSP6SS_PLL_OFFSET 0x00040000 + +/* Offsets within the core clock-controller block (HPASS_n_BASE + 0x48000). */ +#define HPASS_QDSP6SS_CORE_CC_OFFSET 0x00048000 +#define HPASS_QDSP6SS_CORE_CMD_RCGR 0x20 +#define HPASS_QDSP6SS_CORE_CFG_RCGR 0x24 +#define HPASS_QDSP6SS_CORE_CBCR 0x40 +#define HPASS_QDSP6SS_CORE_PLL_ACMU 0x3000 + +#define HPASS_Q6_PLL_L_VAL_SVS_L1 0x32 +#define HPASS_Q6_PLL_CAL_L_VAL 0x44 +#define HPASS_Q6_PLL_CONFIG_CTL_VAL 0x20485699 +#define HPASS_Q6_PLL_CONFIG_CTL_U_VAL 0x00182261 +#define HPASS_Q6_PLL_CONFIG_CTL_U1_VAL 0x82AA299C +#define HPASS_Q6_PLL_TEST_CTL_VAL 0x00000000 +#define HPASS_Q6_PLL_TEST_CTL_U_VAL 0x00000003 +#define HPASS_Q6_PLL_TEST_CTL_U1_VAL 0x00009000 +#define HPASS_Q6_PLL_TEST_CTL_U2_VAL 0x00000034 +#define HPASS_Q6_PLL_USER_CTL_VAL 0x00000000 +#define HPASS_Q6_PLL_USER_CTL_U_VAL 0x00400005 + +#define HPASS_CC_Q6DSPSS0_XO_CBCR 0xc034 +#define HPASS_CC_Q6DSPSS1_XO_CBCR 0xd034 +#define HPASS_CC_Q6DSPSS2_XO_CBCR 0xe034 +#define HPASS_CC_PCLKDBG_CBCR 0x10004 +#define HPASS_CC_ATB_CBCR 0x10008 +#define HPASS_CC_TRACE_NOC_TRIG_CBCR 0x10010 +#define Q6DSPSS_XO_CBCR_IGNORE_ALL_CLK_DIS_BIT BIT(23) + +#define HPASS_TCM_AXIS_HS_CBCR 0x400 +#define HPASS_TCM_256KB_BLOCKn_CBCR(n) ((n) * 0x4) +#define HPASS_TCM_256KB_BLOCKn_CBCR_MAXN 1 +#define TCM_BLOCK_FORCE_MEM_CORE_ON_BIT BIT(14) +#define TCM_BLOCK_CLK_ENABLE_BIT BIT(0) + +#define TCSR_HPASS_HALTACK 0x73008 +#define TCSR_HPASS_HALTREQ 0x7300c +#define TCSR_HPASS_AG_NOC_QCHANNEL_QREQN 0x73014 +#define TCSR_HPASS_AG_NOC_QCHANNEL_QACCEPTN 0x73018 +#define TCSR_HPASS_AUDIO_RESET_CTL 0x7301c + +/* Offsets within HPASS_TCSR_BASE (HPASS_HPASS_TCSR_REGS_REG_BASE window). */ +#define HPASS_Q6SS_0_HW_BOOT_CTL 0x10030 +#define HPASS_Q6SS_1_HW_BOOT_CTL 0x11030 +#define HPASS_Q6SS_2_HW_BOOT_CTL 0x12030 +#define HPASS_ALT_RESET_Q6SS_n(n) (0x10050 + (n) * 0x1000) +#define HPASS_ALT_RESET_Q6SS_n_MAXN 2 +#define HPASS_MUTEX_REG_n(n) (0x18000 + (n) * 0x1000) +#define HPASS_MUTEX_REG_n_MAXN 7 + +#define TCSR_HPASS_Q6SS_0_BUSM_HALTREQ_BIT BIT(0) +#define TCSR_HPASS_Q6SS_1_BUSM_HALTREQ_BIT BIT(1) +#define TCSR_HPASS_Q6SS_2_BUSM_HALTREQ_BIT BIT(2) +#define TCSR_HPASS_Q6SS_0_BUSM_HALTACK_BIT BIT(0) +#define TCSR_HPASS_Q6SS_1_BUSM_HALTACK_BIT BIT(1) +#define TCSR_HPASS_Q6SS_2_BUSM_HALTACK_BIT BIT(2) +#define TCSR_HALTREQ_VAL 0xfffffff8 +#define TCSR_HPASS_AG_NOC_QREQN_BIT BIT(0) +#define TCSR_HPASS_AG_NOC_QACCEPTN_BIT BIT(0) +#define TCSR_HPASS_AUDIO_NOC_QREQN_BIT BIT(1) +#define TCSR_HPASS_ENPU_NOC_QREQN_BIT BIT(2) +#define TCSR_HPASS_AUDIO_NOC_QACCEPTN_BIT BIT(2) +#define TCSR_HPASS_ENPU_NOC_QACCEPTN_BIT BIT(4) + +#define TCSR_HPASS_AG_NOC_QDENY_BIT BIT(1) +#define TCSR_HPASS_ENPU_NOC_QDENY_BIT BIT(5) +#define TCSR_HPASS_AUDIO_NOC_QDENY_BIT TCSR_HPASS_AG_NOC_QDENY_BIT +#define HPASS_DSP0_CLK_DISABLE_BIT BIT(18) +#define HPASS_DSP0_RESET_BIT BIT(19) +#define HPASS_AUDIO_CLK_DISABLE_BIT BIT(16) +#define HPASS_AUDIO_RESET_BIT BIT(17) +#define HPASS_DSP1_CLK_DISABLE_BIT BIT(20) +#define HPASS_DSP1_RESET_BIT BIT(21) +#define HPASS_DSP2_CLK_DISABLE_BIT BIT(22) +#define HPASS_DSP2_RESET_BIT BIT(23) +#define HPASS_Q6SS_HW_RESUME_BIT BIT(1) +#define HPASS_MUTEX_MASK 0xf +#define HPASS_ALT_ARES_ENABLE_BIT BIT(0) + +/* AOSS_CC HPASS restart (within AOSS_CC window, AOSS_CC_BASE). */ +#define AOSS_CC_HPASS_RESTART 0x7014 +#define AOSS_CC_HPASS_CONFIG_RESTART 0x7040 +#define HPASS_RESTART_SS_RESTART_BIT BIT(0) + +#define GCC_SOCCP_CNOC_M_AHB_CBCR 0x2e004 +#define GCC_SOCCP_CNOC_S_AHB_CBCR 0x2e008 +#define GCC_SOCCP_ANOC_AXI_CBCR 0x2e00c +#define GCC_SOCCP_SS_H_CBCR 0x2e010 +#define GCC_SOCCP_PROC_H_CBCR 0x2e024 +#define GCC_SOCCP_F_CBCR 0x2e038 +#define GCC_SOCCP_DBG_CBCR 0x2e040 +#define GCC_SOCCP_TMR_CBCR 0x2e048 +#define GCC_SOCCP_CMD_RCGR 0x2e04c +#define GCC_SOCCP_CFG_RCGR 0x2e050 +#define GCC_SOCCP_RCGR_SRC_SEL_SHIFT 8 +#define GCC_SOCCP_RCGR_SRC_SEL_MASK 0x700 +#define GCC_SOCCP_RCGR_SRC_DIV_MASK 0x1f +#define SOCCP_CBCR_CLK_ENABLE_BIT BIT(0) +/* SOCCP RCG: source select = 0x6, source divider = 1 (runs at 300MHz). */ +#define SOCCP_RCG_SRC_SEL_VAL 0x6 +#define SOCCP_RCG_SRC_DIV_VAL 0x1 + +/* SOCCP_CSR_CNOC window (SOCCP_CSR_BASE, target_config.h). */ +#define SOCCP_SOCCP_RVSSMP_BOOT_SUPPRESS 0x30010 + +/* QDSS_SOCCP_SOCCP_DMI window (QDSS_SOCCP_DMI_BASE, target_config.h). */ +#define QDSS_SOCCP_SOCCP_DMCONTROL 0x40 +#define QDSS_SOCCP_SOCCP_DMI_DMSTATUS 0x44 +#define SOCCP_DMCONTROL_DMACTIVE_BIT BIT(0) +#define SOCCP_DMCONTROL_SETRESETHALTREQ_BIT BIT(3) +#define SOCCP_DMI_DMSTATUS_ANYHAVERESET_BIT BIT(18) + +/* AOSS_CC SOCCP restart (within AOSS_CC window, AOSS_CC_BASE). */ +#define AOSS_CC_SOCCP_RESTART 0x7024 +#define AOSS_CC_SOCCP_CONFIG_RESTART 0x7050 +#define SOCCP_RESTART_SS_RESTART_BIT BIT(0) + +#endif /* _CLOCK_GROUP_QCOM_H_ */ diff --git a/core/drivers/clk/qcom/sub.mk b/core/drivers/clk/qcom/sub.mk index 5e8b39c30..f47622b46 100644 --- a/core/drivers/clk/qcom/sub.mk +++ b/core/drivers/clk/qcom/sub.mk @@ -1,5 +1,8 @@ global-incdirs-y += . global-incdirs-y += platform/$(PLATFORM_FLAVOR) -srcs-y += clock-qcom.c clock-qcom-pas.c +srcs-y += clock-qcom.c +srcs-$(CFG_QCOM_PAS_PTA) += platform/$(PLATFORM_FLAVOR)/clock-qcom-pas.c + incdirs-y += . +incdirs-$(CFG_QCOM_PAS_PTA) += platform/$(PLATFORM_FLAVOR) diff --git a/core/drivers/crypto/qcom/hwkm.c b/core/drivers/crypto/qcom/hwkm.c new file mode 100644 index 000000000..6d3d7e01c --- /dev/null +++ b/core/drivers/crypto/qcom/hwkm.c @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +register_phys_mem_pgdir(MEM_AREA_IO_SEC, HWKM_MASTER_BASE, HWKM_MASTER_SIZE); + +static_assert(HW_UNIQUE_KEY_LENGTH <= HWKM_MAX_KEY_SIZE); + +#define HWKM_BBAC_BIT(slot) BIT((slot) % 32) + +#define HWKM_BANK0_BBAC_0 \ + (HWKM_BBAC_BIT(HWKM_SLOT_TZ_NKDK_L2) | \ + HWKM_BBAC_BIT(HWKM_SLOT_TZ_PKDK_L2) | \ + HWKM_BBAC_BIT(HWKM_SLOT_TZ_SKDK_L2) | \ + HWKM_BBAC_BIT(HWKM_SLOT_TZ_UKDK_L2) | \ + HWKM_BBAC_BIT(HWKM_SLOT_TPKEY_SLOT) | \ + HWKM_BBAC_BIT(HWKM_SLOT_TPKEY_ODD_SLOT) | \ + HWKM_BBAC_BIT(HWKM_SLOT_TZ_SWAP_KEY_SLOT) | \ + HWKM_BBAC_BIT(HWKM_SLOT_TZ_SWAP_KEY_ODD_SLOT) | \ + HWKM_BBAC_BIT(HWKM_SLOT_TZ_WRAP_KEY_SLOT) | \ + HWKM_BBAC_BIT(HWKM_SLOT_TZ_WRAP_KEY_ODD_SLOT) | \ + HWKM_BBAC_BIT(HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT1) | \ + HWKM_BBAC_BIT(HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT2) | \ + HWKM_BBAC_BIT(HWKM_SLOT_PERSISTENT_SHARED_SLOT_PAIR1) | \ + HWKM_BBAC_BIT(HWKM_SLOT_PERSISTENT_SHARED_SLOT_PAIR1_ODD) | \ + HWKM_BBAC_BIT(HWKM_SLOT_PERSISTENT_SHARED_SLOT_PAIR2) | \ + HWKM_BBAC_BIT(HWKM_SLOT_PERSISTENT_SHARED_SLOT_PAIR2_ODD) | \ + HWKM_BBAC_BIT(HWKM_SLOT_TZ_MIXING_KEY_SLOT)) + +static struct hwkm_drv_ctx hwkm_ctx = { + .hwkm_lock = MUTEX_INITIALIZER, +}; + +struct hwkm_drv_ctx *hwkm_get_context(void) +{ + return hwkm_ctx.initialized ? &hwkm_ctx : NULL; +} + +struct hwkm_transaction *hwkm_transaction_alloc(void) +{ + return calloc(1, sizeof(struct hwkm_transaction)); +} + +void hwkm_transaction_free(struct hwkm_transaction *t) +{ + free_wipe(t); +} + +#define HWKM_ERR_ENTRY(m) { .status = (m), .name = #m } + +static const struct { uint32_t status; const char *name; } hwkm_err_table[] = { + /* Common errors. */ + HWKM_ERR_ENTRY(HWKM_ERR_RSP_FIFO_UNDERFLOW), + HWKM_ERR_ENTRY(HWKM_ERR_CMD_FIFO_OVERFLOW), + HWKM_ERR_ENTRY(HWKM_ERR_OPCODE_UNSUPPORTED), + HWKM_ERR_ENTRY(HWKM_ERR_INVALID_OPCODE), + HWKM_ERR_ENTRY(HWKM_ERR_INCORRECT_CMD_LENGTH), + HWKM_ERR_ENTRY(HWKM_ERR_CRC_CHECK), + HWKM_ERR_ENTRY(HWKM_ERR_BBAC_ACCESS), + /* NIST_KEYGEN errors. */ + HWKM_ERR_ENTRY(HWKM_NIST_KEYGEN_ERR_KEYSLOT_ID_OUT_OF_RANGE), + HWKM_ERR_ENTRY(HWKM_NIST_KEYGEN_ERR_UNSUPPORTED_KEY_SIZE), + HWKM_ERR_ENTRY(HWKM_NIST_KEYGEN_ERR_KT_PARITY), + HWKM_ERR_ENTRY(HWKM_NIST_KEYGEN_ERR_INVALID_KEY_TYPE), + HWKM_ERR_ENTRY(HWKM_NIST_KEYGEN_ERR_INVALID_ALG_ALLOWED), + HWKM_ERR_ENTRY(HWKM_NIST_KEYGEN_ERR_INVALID_KEY_ATTRIB), + HWKM_ERR_ENTRY(HWKM_NIST_KEYGEN_ERR_INVALID_TPKEY_ATTRIB), + HWKM_ERR_ENTRY(HWKM_NIST_KEYGEN_ERR_INVALID_KSK_ATTRIB), + HWKM_ERR_ENTRY(HWKM_NIST_KEYGEN_ERR_INVALID_KWK_ATTRIB), + HWKM_ERR_ENTRY(HWKM_NIST_KEYGEN_ERR_INVALID_KDF_DEPTH), + HWKM_ERR_ENTRY(HWKM_NIST_KEYGEN_ERR_INVALID_SECURITY_LEVEL), + HWKM_ERR_ENTRY(HWKM_NIST_KEYGEN_ERR_DKS_SLOT_NOT_EMPTY), + HWKM_ERR_ENTRY(HWKM_NIST_KEYGEN_ERR_ACCESS_VIOLATION), + /* KEY_SLOT_RDWR errors. */ + HWKM_ERR_ENTRY(HWKM_KEY_SLOT_RDWR_ERR_KEYSLOT_ID_OUT_OF_RANGE), + HWKM_ERR_ENTRY(HWKM_KEY_SLOT_RDWR_ERR_UNSUPPORTED_KEY_SIZE), + HWKM_ERR_ENTRY(HWKM_KEY_SLOT_RDWR_ERR_KT_PARITY), + HWKM_ERR_ENTRY(HWKM_KEY_SLOT_RDWR_ERR_INVALID_SECURITY_LEVEL), + HWKM_ERR_ENTRY(HWKM_KEY_SLOT_RDWR_ERR_INVALID_KEY_TYPE), + HWKM_ERR_ENTRY(HWKM_KEY_SLOT_RDWR_ERR_INVALID_ALG_ALLOWED), + HWKM_ERR_ENTRY(HWKM_KEY_SLOT_RDWR_ERR_INVALID_KEY_ATTRIB), + HWKM_ERR_ENTRY(HWKM_KEY_SLOT_RDWR_ERR_INVALID_TPKEY_ATTRIB), + HWKM_ERR_ENTRY(HWKM_KEY_SLOT_RDWR_ERR_INVALID_KSK_ATTRIB), + HWKM_ERR_ENTRY(HWKM_KEY_SLOT_RDWR_ERR_INVALID_KWK_ATTRIB), + HWKM_ERR_ENTRY(HWKM_KEY_SLOT_RDWR_ERR_INVALID_KDF_DEPTH), + HWKM_ERR_ENTRY(HWKM_KEY_SLOT_RDWR_ERR_SECURITY_LEVEL_VIOLATION), + HWKM_ERR_ENTRY(HWKM_KEY_SLOT_RDWR_ERR_SKS_SLOT_EMPTY), + HWKM_ERR_ENTRY(HWKM_KEY_SLOT_RDWR_ERR_DKS_SLOT_NOT_EMPTY), + HWKM_ERR_ENTRY(HWKM_KEY_SLOT_RDWR_ERR_ACCESS_VIOLATION), + /* SYSTEM_KDF errors. */ + HWKM_ERR_ENTRY(HWKM_SYSTEM_KDF_ERR_FR_RD_ERR_ON_SLAVE), + HWKM_ERR_ENTRY(HWKM_SYSTEM_KDF_ERR_MKS_L1L2_NOT_ALLOWED), + HWKM_ERR_ENTRY(HWKM_SYSTEM_KDF_ERR_SEC_CTRL_ACCESS), + HWKM_ERR_ENTRY(HWKM_SYSTEM_KDF_ERR_KT_PARITY), + HWKM_ERR_ENTRY(HWKM_SYSTEM_KDF_ERR_BSVE_BIT_NOT_SUPPORTED), + HWKM_ERR_ENTRY(HWKM_SYSTEM_KDF_ERR_INVALID_SECURITY_LEVEL), + HWKM_ERR_ENTRY(HWKM_SYSTEM_KDF_ERR_INVALID_KEY_TYPE), + HWKM_ERR_ENTRY(HWKM_SYSTEM_KDF_ERR_INVALID_ALG_ALLOWED), + HWKM_ERR_ENTRY(HWKM_SYSTEM_KDF_ERR_SWAP_EXPORT_NOT_ALLOWED), + HWKM_ERR_ENTRY(HWKM_SYSTEM_KDF_ERR_KEYPOLICY_CONSISTENCY), + HWKM_ERR_ENTRY(HWKM_SYSTEM_KDF_ERR_ENCRYPT_NOT_ALLOWED), + HWKM_ERR_ENTRY(HWKM_SYSTEM_KDF_ERR_UNEXPECTED_ALG_ALLOWED), + HWKM_ERR_ENTRY(HWKM_SYSTEM_KDF_ERR_UNEXPECTED_KEY_TYPE), + HWKM_ERR_ENTRY(HWKM_SYSTEM_KDF_ERR_KDF_DEPTH_ERROR), + HWKM_ERR_ENTRY(HWKM_SYSTEM_KDF_ERR_KDK_TYPE), + HWKM_ERR_ENTRY(HWKM_SYSTEM_KDF_ERR_KEY_SIZE), + HWKM_ERR_ENTRY(HWKM_SYSTEM_KDF_ERR_KEYSLOT_SLOT), + HWKM_ERR_ENTRY(HWKM_SYSTEM_KDF_ERR_KEYSLOT_OUT_OF_RANGE), + HWKM_ERR_ENTRY(HWKM_SYSTEM_KDF_ERR_DOUBLE_KEY_SLOT_NOT_EVEN), + HWKM_ERR_ENTRY(HWKM_SYSTEM_KDF_ERR_ACCESS_VIOLATION), + HWKM_ERR_ENTRY(HWKM_SYSTEM_KDF_ERR_SECURITY_LEVEL_VIOLATION), + /* KEY_WRAP_EXPORT errors. */ + HWKM_ERR_ENTRY(HWKM_EXPORT_ERR_KWK_DOUBLE_KEY_ODD_SLOT), + HWKM_ERR_ENTRY(HWKM_EXPORT_ERR_ENCRYPT_NOT_ALLOWED), + HWKM_ERR_ENTRY(HWKM_EXPORT_ERR_SKS_SIZE_ERROR), + HWKM_ERR_ENTRY(HWKM_EXPORT_ERR_SKS_SLOT_OUT_OF_RANGE), + HWKM_ERR_ENTRY(HWKM_EXPORT_ERR_KWK_SIZE_ERROR), + HWKM_ERR_ENTRY(HWKM_EXPORT_ERR_KWK_SLOT_OUT_OF_RANGE), + HWKM_ERR_ENTRY(HWKM_EXPORT_ERR_KT_PARITY), + HWKM_ERR_ENTRY(HWKM_EXPORT_ERR_WRAP_WITH_TPKEY_NOT_ALLOWED), + HWKM_ERR_ENTRY(HWKM_EXPORT_ERR_SWAP_EXPORT_NOT_ALLOWED), + HWKM_ERR_ENTRY(HWKM_EXPORT_ERR_WRAP_EXPORT_NOT_ALLOWED), + HWKM_ERR_ENTRY(HWKM_EXPORT_ERR_UNEXPECTED_ALG_ALLOWED), + HWKM_ERR_ENTRY(HWKM_EXPORT_ERR_SECURITY_LEVEL_ERROR), + HWKM_ERR_ENTRY(HWKM_EXPORT_ERR_UNEXPECTED_KEY_TYPE), + HWKM_ERR_ENTRY(HWKM_EXPORT_ERR_KWK_SLOT_EMPTY), + HWKM_ERR_ENTRY(HWKM_EXPORT_ERR_SKS_SLOT_EMPTY), + HWKM_ERR_ENTRY(HWKM_EXPORT_ERR_SKS_ACCESS_VIOLATION), + HWKM_ERR_ENTRY(HWKM_EXPORT_ERR_KWK_ACCESS_VIOLATION), + /* KEY_UNWRAP_IMPORT errors. */ + HWKM_ERR_ENTRY(HWKM_IMPORT_ERR_UNWRAP_FAILED), + HWKM_ERR_ENTRY(HWKM_IMPORT_ERR_KWK_DOUBLE_KEY_ODD_SLOT), + HWKM_ERR_ENTRY(HWKM_IMPORT_ERR_KWK_SIZE_ERROR), + HWKM_ERR_ENTRY(HWKM_IMPORT_ERR_DKS_SIZE_ERROR), + HWKM_ERR_ENTRY(HWKM_IMPORT_ERR_DKS_OUT_OF_RANGE), + HWKM_ERR_ENTRY(HWKM_IMPORT_ERR_KWK_SLOT_OUT_OF_RANGE), + HWKM_ERR_ENTRY(HWKM_IMPORT_ERR_KT_PARITY), + HWKM_ERR_ENTRY(HWKM_IMPORT_ERR_SECURITY_LEVEL_ERROR), + HWKM_ERR_ENTRY(HWKM_IMPORT_ERR_INVALID_SECURITY_LEVEL), + HWKM_ERR_ENTRY(HWKM_IMPORT_ERR_INVALID_KEY_TYPE), + HWKM_ERR_ENTRY(HWKM_IMPORT_ERR_INVALID_ALG_ALLOWED), + HWKM_ERR_ENTRY(HWKM_IMPORT_ERR_CONSISTENCY_CHECK_FAILED), + HWKM_ERR_ENTRY(HWKM_IMPORT_ERR_INVALID_TPKEY_ATTRIB), + HWKM_ERR_ENTRY(HWKM_IMPORT_ERR_INVALID_KSK_ATTRIB), + HWKM_ERR_ENTRY(HWKM_IMPORT_ERR_INVALID_KWK_ATTRIB), + HWKM_ERR_ENTRY(HWKM_IMPORT_ERR_INVALID_KDF_DEPTH), + HWKM_ERR_ENTRY(HWKM_IMPORT_ERR_DECRYPT_NOT_ALLOWED), + HWKM_ERR_ENTRY(HWKM_IMPORT_ERR_UNEXPECTED_ALG_ALLOWED), + HWKM_ERR_ENTRY(HWKM_IMPORT_ERR_UNEXPECTED_KEY_TYPE), + HWKM_ERR_ENTRY(HWKM_IMPORT_ERR_KWK_SLOT_EMPTY), + HWKM_ERR_ENTRY(HWKM_IMPORT_ERR_DKS_SLOT_NOT_EMPTY), + HWKM_ERR_ENTRY(HWKM_IMPORT_ERR_DKS_ACCESS_VIOLATION), + HWKM_ERR_ENTRY(HWKM_IMPORT_ERR_KWK_ACCESS_VIOLATION), + /* KEY_SLOT_CLEAR errors. */ + HWKM_ERR_ENTRY(HWKM_CLEAR_ERR_KEYSLOT_ID_NOT_ACCESSIBLE), + HWKM_ERR_ENTRY(HWKM_CLEAR_ERR_KEYSLOT_ID_OUT_OF_RANGE), + HWKM_ERR_ENTRY(HWKM_CLEAR_ERR_KEY_SIZE), + HWKM_ERR_ENTRY(HWKM_CLEAR_ERR_DOUBLE_KEY_DKS_SLOT_NOT_EVEN), + HWKM_ERR_ENTRY(HWKM_CLEAR_ERR_DKS_SLOT_EMPTY), + HWKM_ERR_ENTRY(HWKM_CLEAR_ERR_ACCESS_VIOLATION), + /* SET_TPKEY errors. */ + HWKM_ERR_ENTRY(HWKM_TPKEY_ERR_KT_PARITY), + HWKM_ERR_ENTRY(HWKM_TPKEY_ERR_DOUBLE_KEY_SLOT_NOT_EVEN), + HWKM_ERR_ENTRY(HWKM_TPKEY_ERR_WRONG_KEY_SIZE), + HWKM_ERR_ENTRY(HWKM_TPKEY_ERR_KEYSLOT_ID_OUT_OF_RANGE), + HWKM_ERR_ENTRY(HWKM_TPKEY_ERR_UNEXPECTED_KEY_TYPE), + HWKM_ERR_ENTRY(HWKM_TPKEY_ERR_SKS_SLOT_EMPTY), + HWKM_ERR_ENTRY(HWKM_TPKEY_ERR_ACCESS_VIOLATION), +}; + +const char *hwkm_err2str(uint32_t status) +{ + size_t i = 0; + + for (i = 0; i < ARRAY_SIZE(hwkm_err_table); i++) + if (hwkm_err_table[i].status == status) + return hwkm_err_table[i].name; + + return "Unknown"; +} + +static TEE_Result hwkm_init(void) +{ + uint32_t status = 0; + vaddr_t base = 0; + + base = (vaddr_t)phys_to_virt(HWKM_MASTER_BASE, MEM_AREA_IO_SEC, + HWKM_MASTER_SIZE); + if (!base) + return TEE_ERROR_GENERIC; + + /* Check the hardware self-test status. */ + status = io_read32_off(base, HWKM_TZ_KM_STATUS); + if (status & (HWKM_TZ_KM_STATUS_BIST_ERROR | + HWKM_TZ_KM_STATUS_CRYPTO_LIB_BIST_ERROR)) { + EMSG("hwkm: BIST failed, status=0x%08"PRIx32, status); + return TEE_ERROR_GENERIC; + } + + /* Disable CRC checking on command packets. */ + io_write32_off_field(base, HWKM_TZ_KM_CTL, + HWKM_TZ_KM_CTL_CRC_CHECK_EN, 0); + + io_write32_off(base, HWKM_BANK0_AC + HWKM_BANKn_AC_BBAC_0, + HWKM_BANK0_BBAC_0); + io_write32_off(base, HWKM_BANK0_AC + HWKM_BANKn_AC_BBAC_1, 0); + io_write32_off(base, HWKM_BANK0_AC + HWKM_BANKn_AC_BBAC_2, 0); + io_write32_off(base, HWKM_BANK0_AC + HWKM_BANKn_AC_BBAC_3, 0); + io_write32_off(base, HWKM_BANK0_AC + HWKM_BANKn_AC_BBAC_4, 0); + + /* + * Clear the spurious RSP_FIFO_FULL sticky bit. + * HW errata QCTDD06252768: RSP_FIFO_FULL may be set after reset even + * though the FIFO is empty. Write 1 to clear it unconditionally so + * it does not interfere with CMD_DONE polling in + * master_run_transaction(). + */ + io_write32_off(base, HWKM_BANK0_KM_IRQ_STATUS, + HWKM_BANK0_KM_IRQ_STATUS_RSP_FIFO_FULL); + + hwkm_ctx.base = base; + hwkm_ctx.initialized = true; + + return TEE_SUCCESS; +} + +static TEE_Result hwkm_driver_init(void) +{ + struct tee_hw_unique_key huk = { }; + TEE_Result res = TEE_ERROR_GENERIC; + + res = hwkm_init(); + if (res) { + EMSG("hwkm: init failed: 0x%08"PRIx32, res); + return res; + } + + res = tee_otp_get_hw_unique_key(&huk); + if (res) { + EMSG("hwkm: HUK derivation failed: 0x%08"PRIx32, res); + panic("HWKM HUK derivation failure"); + } + + memzero_explicit(&huk, sizeof(huk)); + + return TEE_SUCCESS; +} + +service_init(hwkm_driver_init); diff --git a/core/drivers/crypto/qcom/hwkm_huk.c b/core/drivers/crypto/qcom/hwkm_huk.c new file mode 100644 index 000000000..81d71e642 --- /dev/null +++ b/core/drivers/crypto/qcom/hwkm_huk.c @@ -0,0 +1,317 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. + */ + +#include +#include +#include +#include +#include + +#include +#include + +#define HWKM_HUK_MKS_CTX "OPTEE_HUK_HWKM_V1_MKS" +#define HWKM_HUK_L3_CTX "OPTEE_HUK_HWKM_V1_L3" +#define HWKM_HUK_L4_CTX "OPTEE_HUK_HWKM_V1_L4" + +#define HWKM_SCRATCH_SLOT_MKS HWKM_SLOT_TZ_MIXING_KEY_SLOT +#define HWKM_SCRATCH_SLOT_A HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT1 +#define HWKM_SCRATCH_SLOT_B HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT2 + +static void hwkm_setup_kdf(struct hwkm_transaction *t, + uint8_t dks, uint8_t kdk, uint8_t mks, + const struct hwkm_key_policy *policy, + const struct hwkm_bsve *bsve, + const char *ctx, size_t ctx_len) +{ + t->cmd.op = HWKM_OP_SYSTEM_KDF; + t->cmd.kdf.dks = dks; + t->cmd.kdf.kdk = kdk; + t->cmd.kdf.mks = mks; + t->cmd.kdf.policy = *policy; + t->cmd.kdf.bsve = *bsve; + t->cmd.kdf.ctx_len = ctx_len; + memcpy(t->cmd.kdf.ctx, ctx, ctx_len); +} + +/* + * hwkm_huk_derive_mks() - Derive the SKDK L3 mixing key into the MKS slot. + * + * TZ_SKDK_L2 is an L2 static key and cannot be used directly as the MKS + * argument in SYSTEM_KDF (hardware rejects L1/L2 slots with + * HWKM_SYSTEM_KDF_ERR_MKS_L1L2_NOT_ALLOWED). This function produces a stable + * L3 generic key from TZ_SKDK_L2 and loads it into HWKM_SCRATCH_SLOT_MKS. + * + * Return: TEE_SUCCESS on success, or a TEE_ERROR_* code on failure. + */ +static TEE_Result hwkm_huk_derive_mks(void) +{ + /* SKDK L3 mixing key: kdf_depth = 0 so it cannot be used as a KDK. */ + const struct hwkm_key_policy mks_policy = { + .km_by_tz_allowed = true, + .alg_allowed = HWKM_ALGO_AES256_CMAC, + .enc_allowed = true, + .dec_allowed = true, + .key_type = HWKM_KEY_TYPE_GENERIC_KEY, + .security_lvl = HWKM_KEY_SECURITY_LVL_SW_KEY, + .hw_destination = HWKM_KEY_DEST_KM_MASTER, + }; + const struct hwkm_bsve mks_bsve = { + .enabled = true, + .km_swc_en = true, + .km_apps_secure_en = true, + .km_fuse_region_sha_digest_en = + UINT64_C(CFG_HWKM_HUK_FUSE_REGION_DIGEST), + }; + struct hwkm_transaction *t_clear = NULL; + struct hwkm_transaction *t_kdf = NULL; + TEE_Result res = TEE_SUCCESS; + int rc = 0; + + t_clear = hwkm_transaction_alloc(); + t_kdf = hwkm_transaction_alloc(); + if (!t_clear || !t_kdf) { + res = TEE_ERROR_OUT_OF_MEMORY; + goto out; + } + + t_clear->cmd.op = HWKM_OP_KEY_SLOT_CLEAR; + t_clear->cmd.clear.dks = HWKM_SCRATCH_SLOT_MKS; + t_clear->cmd.clear.is_double_key = false; + hwkm_setup_kdf(t_kdf, HWKM_SCRATCH_SLOT_MKS, HWKM_SLOT_TZ_SKDK_L2, + 0, &mks_policy, &mks_bsve, + HWKM_HUK_MKS_CTX, sizeof(HWKM_HUK_MKS_CTX) - 1); + + rc = hwkm_run_transactions(HWKM_KEY_DEST_KM_MASTER, 2, + (struct hwkm_transaction *const[]){ + t_clear, t_kdf }); + if (rc) { + res = hwkm_to_optee(rc); + goto out_clear; + } + + if (t_clear->rsp.status != HWKM_RSP_ERR_SUCCESS && + t_clear->rsp.status != HWKM_CLEAR_ERR_DKS_SLOT_EMPTY) { + res = TEE_ERROR_GENERIC; + goto out_clear; + } + + if (t_kdf->rsp.status != HWKM_RSP_ERR_SUCCESS) { + EMSG("hwkm: SYSTEM_KDF MKS failed: %s", + hwkm_err2str(t_kdf->rsp.status)); + res = TEE_ERROR_GENERIC; + goto out_clear; + } + + goto out; + +out_clear: + /* Ensure the MKS slot is clean before surfacing the error. */ + t_clear->hdl = NULL; + hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, t_clear); + +out: + hwkm_transaction_free(t_clear); + hwkm_transaction_free(t_kdf); + return res; +} + +/* + * hwkm_huk_derive_keys() - Run the two-level UKDK KDF and read out the HUK. + * @huk: Output buffer receiving the derived hardware unique key. + * + * Derives the UKDK L3 KDK into HWKM_SCRATCH_SLOT_A with the SKDK L3 mixing + * key in HWKM_SCRATCH_SLOT_MKS bound via BSVE.MKS_EN, then derives the final + * L4 HUK from slot A into slot B, reads it out, and clears all three scratch + * slots. + * + * Return: TEE_SUCCESS on success, or a TEE_ERROR_* code on failure. + * All three scratch slots are cleared on any failure before returning. + */ +static TEE_Result hwkm_huk_derive_keys(uint8_t huk[HW_UNIQUE_KEY_LENGTH]) +{ + /* L3: intermediate KDK derived from TZ_UKDK_L2. */ + const struct hwkm_key_policy l3_policy = { + .km_by_tz_allowed = true, + .alg_allowed = HWKM_ALGO_AES256_CMAC, + .enc_allowed = true, + .dec_allowed = true, + .key_type = HWKM_KEY_TYPE_KDK, + .kdf_depth = 1, + .security_lvl = HWKM_KEY_SECURITY_LVL_HW_KEY, + .hw_destination = HWKM_KEY_DEST_KM_MASTER, + }; + /* L4: final SW-readable HUK derived from the L3 KDK. */ + const struct hwkm_key_policy l4_policy = { + .km_by_tz_allowed = true, + .alg_allowed = HWKM_ALGO_AES256_CMAC, + .enc_allowed = true, + .dec_allowed = true, + .key_type = HWKM_KEY_TYPE_GENERIC_KEY, + .security_lvl = HWKM_KEY_SECURITY_LVL_SW_KEY, + .hw_destination = HWKM_KEY_DEST_KM_MASTER, + }; + const struct hwkm_bsve huk_bsve = { + .enabled = true, + .km_swc_en = true, + .km_apps_secure_en = true, + .km_mks_en = IS_ENABLED(CFG_HWKM_HUK_MIX_SKDK), + .km_fuse_region_sha_digest_en = + UINT64_C(CFG_HWKM_HUK_FUSE_REGION_DIGEST), + }; + struct hwkm_transaction *t_clear_a = NULL; + struct hwkm_transaction *t_clear_b = NULL; + struct hwkm_transaction *t_clear_mks = NULL; + struct hwkm_transaction *t_kdf_l3 = NULL; + struct hwkm_transaction *t_kdf_l4 = NULL; + struct hwkm_transaction *t_read = NULL; + TEE_Result res = TEE_SUCCESS; + int rc = 0; + + t_clear_a = hwkm_transaction_alloc(); + t_clear_b = hwkm_transaction_alloc(); + t_clear_mks = hwkm_transaction_alloc(); + t_kdf_l3 = hwkm_transaction_alloc(); + t_kdf_l4 = hwkm_transaction_alloc(); + t_read = hwkm_transaction_alloc(); + if (!t_clear_a || !t_clear_b || !t_clear_mks || + !t_kdf_l3 || !t_kdf_l4 || !t_read) { + res = TEE_ERROR_OUT_OF_MEMORY; + goto out; + } + + t_clear_a->cmd.op = HWKM_OP_KEY_SLOT_CLEAR; + t_clear_a->cmd.clear.dks = HWKM_SCRATCH_SLOT_A; + t_clear_a->cmd.clear.is_double_key = false; + t_clear_b->cmd.op = HWKM_OP_KEY_SLOT_CLEAR; + t_clear_b->cmd.clear.dks = HWKM_SCRATCH_SLOT_B; + t_clear_b->cmd.clear.is_double_key = false; + t_clear_mks->cmd.op = HWKM_OP_KEY_SLOT_CLEAR; + t_clear_mks->cmd.clear.dks = HWKM_SCRATCH_SLOT_MKS; + t_clear_mks->cmd.clear.is_double_key = false; + hwkm_setup_kdf(t_kdf_l3, HWKM_SCRATCH_SLOT_A, HWKM_SLOT_TZ_UKDK_L2, + HWKM_SCRATCH_SLOT_MKS, &l3_policy, &huk_bsve, + HWKM_HUK_L3_CTX, sizeof(HWKM_HUK_L3_CTX) - 1); + hwkm_setup_kdf(t_kdf_l4, HWKM_SCRATCH_SLOT_B, HWKM_SCRATCH_SLOT_A, + HWKM_SCRATCH_SLOT_MKS, &l4_policy, &huk_bsve, + HWKM_HUK_L4_CTX, sizeof(HWKM_HUK_L4_CTX) - 1); + + /* Clear A and B, derive UKDK L3 into A, derive L4 HUK into B. */ + rc = hwkm_run_transactions(HWKM_KEY_DEST_KM_MASTER, 4, + (struct hwkm_transaction *const[]){ + t_clear_a, t_clear_b, + t_kdf_l3, t_kdf_l4 }); + if (rc) { + res = hwkm_to_optee(rc); + goto out_clear; + } + + if ((t_clear_a->rsp.status != HWKM_RSP_ERR_SUCCESS && + t_clear_a->rsp.status != HWKM_CLEAR_ERR_DKS_SLOT_EMPTY) || + (t_clear_b->rsp.status != HWKM_RSP_ERR_SUCCESS && + t_clear_b->rsp.status != HWKM_CLEAR_ERR_DKS_SLOT_EMPTY)) { + res = TEE_ERROR_GENERIC; + goto out_clear; + } + + if (t_kdf_l3->rsp.status != HWKM_RSP_ERR_SUCCESS) { + EMSG("hwkm: SYSTEM_KDF L3 failed: %s", + hwkm_err2str(t_kdf_l3->rsp.status)); + res = TEE_ERROR_GENERIC; + goto out_clear; + } + + if (t_kdf_l4->rsp.status != HWKM_RSP_ERR_SUCCESS) { + EMSG("hwkm: SYSTEM_KDF L4 failed: %s", + hwkm_err2str(t_kdf_l4->rsp.status)); + res = TEE_ERROR_GENERIC; + goto out_clear; + } + + /* Read L4 HUK from slot B, then clear A, B and MKS. */ + t_read->cmd.op = HWKM_OP_KEY_SLOT_RDWR; + t_read->cmd.rdwr.slot = HWKM_SCRATCH_SLOT_B; + t_read->cmd.rdwr.is_write = false; + + rc = hwkm_run_transactions(HWKM_KEY_DEST_KM_MASTER, 4, + (struct hwkm_transaction *const[]){ + t_read, t_clear_a, + t_clear_b, t_clear_mks }); + if (rc) { + res = hwkm_to_optee(rc); + goto out_clear; + } + + if (t_read->rsp.status != HWKM_RSP_ERR_SUCCESS) { + EMSG("hwkm: KEY_SLOT_RDWR read failed: %s", + hwkm_err2str(t_read->rsp.status)); + res = TEE_ERROR_GENERIC; + goto out_clear; + } + + if (t_clear_a->rsp.status != HWKM_RSP_ERR_SUCCESS || + t_clear_b->rsp.status != HWKM_RSP_ERR_SUCCESS || + t_clear_mks->rsp.status != HWKM_RSP_ERR_SUCCESS) { + res = TEE_ERROR_GENERIC; + goto out; + } + + memcpy(huk, t_read->rsp.rdwr.key, HW_UNIQUE_KEY_LENGTH); + goto out; + +out_clear: + t_clear_a->hdl = NULL; + t_clear_b->hdl = NULL; + t_clear_mks->hdl = NULL; + hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, t_clear_a); + hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, t_clear_b); + hwkm_run_transaction(HWKM_KEY_DEST_KM_MASTER, t_clear_mks); + +out: + hwkm_transaction_free(t_clear_a); + hwkm_transaction_free(t_clear_b); + hwkm_transaction_free(t_clear_mks); + hwkm_transaction_free(t_kdf_l3); + hwkm_transaction_free(t_kdf_l4); + hwkm_transaction_free(t_read); + return res; +} + +TEE_Result tee_otp_get_hw_unique_key(struct tee_hw_unique_key *hwkey) +{ + struct hwkm_drv_ctx *drv = NULL; + TEE_Result res = TEE_SUCCESS; + + if (!hwkey) + return TEE_ERROR_BAD_PARAMETERS; + + drv = hwkm_get_context(); + if (!drv) + return TEE_ERROR_NOT_SUPPORTED; + + mutex_lock(&drv->hwkm_lock); + + if (drv->hwkm_huk_ready) { + memcpy(hwkey->data, drv->hwkm_huk, HW_UNIQUE_KEY_LENGTH); + goto out; + } + + if (IS_ENABLED(CFG_HWKM_HUK_MIX_SKDK)) { + res = hwkm_huk_derive_mks(); + if (res) + goto out; + } + + res = hwkm_huk_derive_keys(drv->hwkm_huk); + if (res) + goto out; + + drv->hwkm_huk_ready = true; + memcpy(hwkey->data, drv->hwkm_huk, HW_UNIQUE_KEY_LENGTH); + +out: + mutex_unlock(&drv->hwkm_lock); + return res; +} diff --git a/core/drivers/crypto/qcom/hwkm_transaction.c b/core/drivers/crypto/qcom/hwkm_transaction.c new file mode 100644 index 000000000..37ca1ca93 --- /dev/null +++ b/core/drivers/crypto/qcom/hwkm_transaction.c @@ -0,0 +1,858 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Command packet format: + * Each command starts with a CMD[0] operation-info word that encodes the + * opcode, slot indices, flags, and total packet length. Subsequent words + * carry the policy, BSVE, software context, and a CRC word (always 0 while + * CRC checking is disabled). + */ + +static_assert(HWKM_OP_NIST_KEYGEN == 0x0); +static_assert(HWKM_OP_SYSTEM_KDF == 0x1); +static_assert(HWKM_OP_KEY_WRAP_EXPORT == 0x3); +static_assert(HWKM_OP_KEY_UNWRAP_IMPORT == 0x4); +static_assert(HWKM_OP_KEY_SLOT_CLEAR == 0x5); +static_assert(HWKM_OP_KEY_SLOT_RDWR == 0x6); +static_assert(HWKM_OP_SET_TPKEY == 0x7); + +#define HWKM_OPERATION_INFO_WORDS 1 + +/* Command and response formats. */ + +/* Shared macros: */ + +#define HWKM_KEY_POLICY_WORDS 2 +#define HWKM_BSVE_WORDS 3 +#define HWKM_KEY_BLOB_WORDS 17 +#define HWKM_SOFTWARE_CONTEXT_WORDS 16 +#define HWKM_KEY_RDWR_WORDS 8 + +static_assert(HWKM_KEY_BLOB_WORDS * sizeof(uint32_t) == HWKM_MAX_BLOB_SIZE); +static_assert(HWKM_SOFTWARE_CONTEXT_WORDS * sizeof(uint32_t) == + HWKM_MAX_CTX_SIZE); +static_assert(HWKM_KEY_RDWR_WORDS * sizeof(uint32_t) == HWKM_MAX_KEY_SIZE); + +/* + * HWKM_OP_NIST_KEYGEN - Generate a fresh key into a slot via hardware PRNG. + * + * CMD[0] = Operation info. + * CMD[1:2] = Policy. + * CMD[3] = CRC (disabled). + * + * RSP[0] = Unused. + * RSP[HWKM_OP_NIST_KEYGEN_RSP_ERR_IDX] = Error status. + */ +#define HWKM_OP_NIST_KEYGEN_CMD_WORDS 4 /* CMD[0] + policy + CRC. */ +#define HWKM_OP_NIST_KEYGEN_RSP_WORDS 2 /* RSP[0:1]. */ +#define HWKM_OP_NIST_KEYGEN_RSP_ERR_IDX 1 + +/* + * HWKM_OP_SYSTEM_KDF - Derive a child key from a KDK slot via hardware KDF. + * + * CMD[0] = Operation info. + * CMD[1:2] = Policy. + * CMD[3] = BSVE[0] if bsve is enabled, 0 otherwise. + * CMD[4:5] = BSVE[1:2] only if bsve is enabled. + * CMD[n:n + m] = Software Context (SC), where n = 4 or 6 depending on BSVE, + * and m is SC length. + * CMD[n + m + 1] = CRC (disabled). + * + * RSP[0] = Unused. + * RSP[HWKM_OP_SYSTEM_KDF_RSP_ERR_IDX] = Error status. + */ +/* CMD[0] + policy + BSVE[0] + CRC (minimum, excluding context). */ +#define HWKM_OP_SYSTEM_KDF_CMD_WORDS 4 +#define HWKM_OP_SYSTEM_KDF_RSP_WORDS 2 /* RSP[0:1]. */ +#define HWKM_OP_SYSTEM_KDF_RSP_ERR_IDX 1 + +/* + * HWKM_OP_KEY_WRAP_EXPORT - Encrypt a slot under a KWK/KSK and return the blob. + * + * CMD[0] = Operation info. + * CMD[1:3] = BSVE. + * CMD[4] = CRC (disabled). + * + * RSP[0] = Unused. + * RSP[1] = Error status. + * RSP[HWKM_OP_KEY_WRAP_EXPORT_RSP_BLOB_IDX: + * HWKM_OP_KEY_WRAP_EXPORT_RSP_BLOB_IDX + HWKM_KEY_BLOB_WORDS - 1] + * = Wrapped Key Blob. + */ +/* CMD[0] + BSVE + CRC. */ +#define HWKM_OP_KEY_WRAP_EXPORT_CMD_WORDS 5 +#define HWKM_OP_KEY_WRAP_EXPORT_RSP_WORDS 19 /* RSP[0:1] + blob. */ +#define HWKM_OP_KEY_WRAP_EXPORT_RSP_ERR_IDX 1 +#define HWKM_OP_KEY_WRAP_EXPORT_RSP_BLOB_IDX 2 + +/* + * HWKM_OP_KEY_UNWRAP_IMPORT - Decrypt a wrapped blob and write the key + * into a slot. + * + * CMD[0] = Operation info. + * CMD[1:17] = Wrapped Key Blob. + * CMD[18] = CRC (disabled). + * + * RSP[0] = Unused. + * RSP[HWKM_OP_KEY_UNWRAP_IMPORT_RSP_ERR_IDX] = Error status. + */ +/* CMD[0] + blob + CRC. */ +#define HWKM_OP_KEY_UNWRAP_IMPORT_CMD_WORDS 19 +#define HWKM_OP_KEY_UNWRAP_IMPORT_RSP_WORDS 2 /* RSP[0:1]. */ +#define HWKM_OP_KEY_UNWRAP_IMPORT_RSP_ERR_IDX 1 + +/* + * HWKM_OP_KEY_SLOT_CLEAR - Zeroize a slot and invalidate its policy word. + * + * CMD[0] = Operation info. + * CMD[1] = CRC (disabled). + * + * RSP[0] = Unused. + * RSP[HWKM_OP_KEY_SLOT_CLEAR_RSP_ERR_IDX] = Error status + */ +#define HWKM_OP_KEY_SLOT_CLEAR_CMD_WORDS 2 /* CMD[0] + CRC. */ +#define HWKM_OP_KEY_SLOT_CLEAR_RSP_WORDS 2 /* RSP[0:1]. */ +#define HWKM_OP_KEY_SLOT_CLEAR_RSP_ERR_IDX 1 + +/* + * HWKM_OP_KEY_SLOT_RDWR - Read or write raw key material for a SW_KEY slot. + * + * CMD[0] = Operation info. + * CMD[1:2] = Written policy (0 if read). + * CMD[3:10] = Written key value (0 if read). + * CMD[11] = CRC (disabled). + * + * RSP[0] = Unused. + * RSP[HWKM_OP_KEY_SLOT_RDWR_RSP_ERR_IDX] = Error status. + * RSP[HWKM_OP_KEY_SLOT_RDWR_RSP_POLICY_IDX: + * HWKM_OP_KEY_SLOT_RDWR_RSP_POLICY_IDX + HWKM_KEY_POLICY_WORDS - 1] + * = Read policy (0 if write). + * RSP[HWKM_OP_KEY_SLOT_RDWR_RSP_READ_KEY_IDX: + * HWKM_OP_KEY_SLOT_RDWR_RSP_READ_KEY_IDX + HWKM_KEY_RDWR_WORDS -1] + * = Read key value (0 if write). + */ +/* CMD[0] + policy + key + CRC. */ +#define HWKM_OP_KEY_SLOT_RDWR_CMD_WORDS 12 +/* RSP[0:1] + policy + key. */ +#define HWKM_OP_KEY_SLOT_RDWR_RSP_WORDS 12 +#define HWKM_OP_KEY_SLOT_RDWR_RSP_ERR_IDX 1 +#define HWKM_OP_KEY_SLOT_RDWR_RSP_POLICY_IDX 2 +#define HWKM_OP_KEY_SLOT_RDWR_RSP_READ_KEY_IDX 4 + +/* + * HWKM_OP_SET_TPKEY - Install a slot as the active transport protection key. + * + * CMD[0] = Operation info. + * CMD[1] = CRC (disabled). + * + * RSP[0] = Unused. + * RSP[HWKM_OP_SET_TPKEY_RSP_ERR_IDX] = Error status. + */ +#define HWKM_OP_SET_TPKEY_CMD_WORDS 2 /* CMD[0] + CRC. */ +#define HWKM_OP_SET_TPKEY_RSP_WORDS 2 /* RSP[0:1]. */ +#define HWKM_OP_SET_TPKEY_RSP_ERR_IDX 1 + +/* PACK and UNPACK. */ + +/* Set one field of @reg to @val, positioned by @mask's lowest set bit. */ +#define hwkm_pack(reg, val, mask) \ + ((reg) = set_field_u32((reg), (mask), (val))) +/* Store the field of @reg selected by @mask, right-justified, into @dst. */ +#define hwkm_unpack(dst, reg, mask) \ + ((dst) = (typeof(dst))get_field_u32((reg), (mask))) + +/* Operation info - HWKM_OPERATION_INFO_WORDS word. */ +#define HWKM_OP_INFO_OP_MASK GENMASK_32(3, 0) +#define HWKM_OP_INFO_SLOT1_DESC_MASK GENMASK_32(12, 5) +#define HWKM_OP_INFO_SLOT2_DESC_MASK GENMASK_32(20, 13) +#define HWKM_OP_INFO_OP_FLAG BIT(21) +#define HWKM_OP_INFO_CONTEXT_LEN_MASK GENMASK_32(26, 22) +#define HWKM_OP_INFO_LEN_MASK GENMASK_32(31, 27) + +/* Key policy - HWKM_KEY_POLICY_WORDS words. */ +#define HWKM_KEY_POL_WRAP_WITH_TPKEY BIT(2) +#define HWKM_KEY_POL_HW_DEST_MASK GENMASK_32(6, 3) +#define HWKM_KEY_POL_SEC_LVL_MASK GENMASK_32(10, 9) +#define HWKM_KEY_POL_SWAP_EXPORT BIT(11) +#define HWKM_KEY_POL_WRAP_EXPORT BIT(12) +#define HWKM_KEY_POL_KEY_TYPE_MASK GENMASK_32(15, 13) +#define HWKM_KEY_POL_KDF_DEPTH_MASK GENMASK_32(23, 16) +#define HWKM_KEY_POL_DECRYPT BIT(24) +#define HWKM_KEY_POL_ENCRYPT BIT(25) +#define HWKM_KEY_POL_ALG_MASK GENMASK_32(31, 26) +#define HWKM_KEY_POL_KM_BY_TZ BIT(0) +#define HWKM_KEY_POL_KM_BY_NSEC BIT(1) +#define HWKM_KEY_POL_KM_BY_MODEM BIT(2) +#define HWKM_KEY_POL_KM_BY_SPU BIT(3) + +/* Wrapping BSVE - HWKM_BSVE_WORDS words, word 0 only (words 1:2 reserved). */ +#define HWKM_WRAP_BSVE_KEY_POL_VER BIT(0) +#define HWKM_WRAP_BSVE_APPS_SEC BIT(1) +#define HWKM_WRAP_BSVE_MSA_SEC BIT(2) +#define HWKM_WRAP_BSVE_LCM_FUSE_ROW BIT(3) +#define HWKM_WRAP_BSVE_BOOT_STAGE_OTP BIT(4) + +/* KDF BSVE - HWKM_BSVE_WORDS words. */ +#define HWKM_KDF_BSVE_MKS GENMASK_32(7, 0) +#define HWKM_KDF_BSVE_KEY_POL_VER BIT(8) +#define HWKM_KDF_BSVE_APPS_SEC BIT(9) +#define HWKM_KDF_BSVE_MSA_SEC BIT(10) +#define HWKM_KDF_BSVE_LCM_FUSE_ROW BIT(11) +#define HWKM_KDF_BSVE_BOOT_STAGE_OTP BIT(12) +#define HWKM_KDF_BSVE_SWC BIT(13) +#define HWKM_KDF_BSVE_CHILD_KEY_POL BIT(14) +#define HWKM_KDF_BSVE_MKS_EN BIT(15) + +/* + * hwkm_pack_op_info() - Pack the CMD[0] operation-info word. + * @dst: Destination word. + * @op: Opcode [3:0]. + * @slot1_desc: DKS or SKS depending on opcode. + * @slot2_desc: KDK, KWK, or 0 depending on opcode. + * @op_flag: Flag whose meaning depends on opcode. + * @context_len: Software context length in words, KDF only. + * @len: Total command length in words. + */ +static void hwkm_pack_op_info(uint32_t *dst, uint32_t op, uint8_t slot1_desc, + uint8_t slot2_desc, bool op_flag, + uint32_t context_len, uint32_t len) +{ + *dst = op & HWKM_OP_INFO_OP_MASK; + + hwkm_pack(*dst, slot1_desc, HWKM_OP_INFO_SLOT1_DESC_MASK); + hwkm_pack(*dst, slot2_desc, HWKM_OP_INFO_SLOT2_DESC_MASK); + hwkm_pack(*dst, op_flag, HWKM_OP_INFO_OP_FLAG); + hwkm_pack(*dst, context_len, HWKM_OP_INFO_CONTEXT_LEN_MASK); + hwkm_pack(*dst, len, HWKM_OP_INFO_LEN_MASK); +} + +static void hwkm_pack_key_policy(uint32_t dst[HWKM_KEY_POLICY_WORDS], + const struct hwkm_key_policy *src) +{ + memset(dst, 0, HWKM_KEY_POLICY_WORDS * sizeof(uint32_t)); + /* Word 0: */ + hwkm_pack(dst[0], src->wrap_with_tpkey_allowed, + HWKM_KEY_POL_WRAP_WITH_TPKEY); + hwkm_pack(dst[0], src->hw_destination, HWKM_KEY_POL_HW_DEST_MASK); + hwkm_pack(dst[0], src->security_lvl, HWKM_KEY_POL_SEC_LVL_MASK); + hwkm_pack(dst[0], src->swap_export_allowed, HWKM_KEY_POL_SWAP_EXPORT); + hwkm_pack(dst[0], src->wrap_export_allowed, HWKM_KEY_POL_WRAP_EXPORT); + hwkm_pack(dst[0], src->key_type, HWKM_KEY_POL_KEY_TYPE_MASK); + hwkm_pack(dst[0], src->kdf_depth, HWKM_KEY_POL_KDF_DEPTH_MASK); + hwkm_pack(dst[0], src->dec_allowed, HWKM_KEY_POL_DECRYPT); + hwkm_pack(dst[0], src->enc_allowed, HWKM_KEY_POL_ENCRYPT); + hwkm_pack(dst[0], src->alg_allowed, HWKM_KEY_POL_ALG_MASK); + /* Word 1: */ + hwkm_pack(dst[1], src->km_by_tz_allowed, HWKM_KEY_POL_KM_BY_TZ); + hwkm_pack(dst[1], src->km_by_nsec_allowed, HWKM_KEY_POL_KM_BY_NSEC); + hwkm_pack(dst[1], src->km_by_modem_allowed, HWKM_KEY_POL_KM_BY_MODEM); + hwkm_pack(dst[1], src->km_by_spu_allowed, HWKM_KEY_POL_KM_BY_SPU); +} + +static void hwkm_unpack_key_policy(struct hwkm_key_policy *dst, + const uint32_t src[HWKM_KEY_POLICY_WORDS]) +{ + memset(dst, 0, sizeof(*dst)); + /* Word 0: */ + hwkm_unpack(dst->wrap_with_tpkey_allowed, src[0], + HWKM_KEY_POL_WRAP_WITH_TPKEY); + hwkm_unpack(dst->hw_destination, src[0], HWKM_KEY_POL_HW_DEST_MASK); + hwkm_unpack(dst->security_lvl, src[0], HWKM_KEY_POL_SEC_LVL_MASK); + hwkm_unpack(dst->swap_export_allowed, src[0], HWKM_KEY_POL_SWAP_EXPORT); + hwkm_unpack(dst->wrap_export_allowed, src[0], HWKM_KEY_POL_WRAP_EXPORT); + hwkm_unpack(dst->key_type, src[0], HWKM_KEY_POL_KEY_TYPE_MASK); + hwkm_unpack(dst->kdf_depth, src[0], HWKM_KEY_POL_KDF_DEPTH_MASK); + hwkm_unpack(dst->dec_allowed, src[0], HWKM_KEY_POL_DECRYPT); + hwkm_unpack(dst->enc_allowed, src[0], HWKM_KEY_POL_ENCRYPT); + hwkm_unpack(dst->alg_allowed, src[0], HWKM_KEY_POL_ALG_MASK); + /* Word 1: */ + hwkm_unpack(dst->km_by_tz_allowed, src[1], HWKM_KEY_POL_KM_BY_TZ); + hwkm_unpack(dst->km_by_nsec_allowed, src[1], HWKM_KEY_POL_KM_BY_NSEC); + hwkm_unpack(dst->km_by_modem_allowed, src[1], HWKM_KEY_POL_KM_BY_MODEM); + hwkm_unpack(dst->km_by_spu_allowed, src[1], HWKM_KEY_POL_KM_BY_SPU); +} + +/* Leaves @dst zeroed (BSVE disabled) if @src->enabled is false. */ +static void hwkm_pack_wrap_bsve(uint32_t dst[HWKM_BSVE_WORDS], + const struct hwkm_bsve *src) +{ + memset(dst, 0, HWKM_BSVE_WORDS * sizeof(uint32_t)); + + if (!src->enabled) + return; + + hwkm_pack(dst[0], src->km_key_policy_ver_en, + HWKM_WRAP_BSVE_KEY_POL_VER); + hwkm_pack(dst[0], src->km_apps_secure_en, HWKM_WRAP_BSVE_APPS_SEC); + hwkm_pack(dst[0], src->km_msa_secure_en, HWKM_WRAP_BSVE_MSA_SEC); + hwkm_pack(dst[0], src->km_lcm_fuse_en, HWKM_WRAP_BSVE_LCM_FUSE_ROW); + hwkm_pack(dst[0], src->km_boot_stage_otp_en, + HWKM_WRAP_BSVE_BOOT_STAGE_OTP); +} + +/* Leaves @dst zeroed (BSVE disabled) if @src->enabled is false. */ +static void hwkm_pack_kdf_bsve(uint32_t dst[HWKM_BSVE_WORDS], + const struct hwkm_bsve *src, uint8_t mks) +{ + uint64_t digest = 0; + + memset(dst, 0, HWKM_BSVE_WORDS * sizeof(uint32_t)); + + if (!src->enabled) + return; + + digest = src->km_fuse_region_sha_digest_en; + /* Word 0: */ + hwkm_pack(dst[0], mks, HWKM_KDF_BSVE_MKS); + hwkm_pack(dst[0], src->km_key_policy_ver_en, HWKM_KDF_BSVE_KEY_POL_VER); + hwkm_pack(dst[0], src->km_apps_secure_en, HWKM_KDF_BSVE_APPS_SEC); + hwkm_pack(dst[0], src->km_msa_secure_en, HWKM_KDF_BSVE_MSA_SEC); + hwkm_pack(dst[0], src->km_lcm_fuse_en, HWKM_KDF_BSVE_LCM_FUSE_ROW); + hwkm_pack(dst[0], src->km_boot_stage_otp_en, + HWKM_KDF_BSVE_BOOT_STAGE_OTP); + hwkm_pack(dst[0], src->km_swc_en, HWKM_KDF_BSVE_SWC); + /* Digest bits [17:0] -> word 0 [31:14]. */ + dst[0] |= (uint32_t)(digest & GENMASK_64(17, 0)) << 14; + /* Word 1: */ + /* Digest bits [49:18] -> word 1 [31:0]. */ + dst[1] = (uint32_t)((digest >> 18) & GENMASK_64(31, 0)); + /* Word 2: */ + /* Digest bits [63:50] -> word 2 [13:0]. */ + dst[2] |= (uint32_t)((digest >> 50) & GENMASK_64(13, 0)); + hwkm_pack(dst[2], src->km_child_key_policy_en, + HWKM_KDF_BSVE_CHILD_KEY_POL); + hwkm_pack(dst[2], src->km_mks_en, HWKM_KDF_BSVE_MKS_EN); +} + +/* + * hwkm_reverse_bytes() - Reverse @len bytes of @buf in-place. + * + * The HWKM hardware stores and returns key material in the opposite byte order + * to the canonical software representation. Apply this function before writing + * a key into a command packet and after reading a key from a response packet. + */ +static void hwkm_reverse_bytes(uint8_t *buf, size_t len) +{ + size_t left = 0; + size_t right = len - 1; + + assert(len > 0); + + while (left < right) { + uint8_t tmp = buf[left]; + + buf[left++] = buf[right]; + buf[right--] = tmp; + } +} + +/* + * hwkm_reorder_kdf_ctx() - Reverse the context bytes in 8-byte chunks. + * + * To satisfy NIST SP800-108 KAT compliance the hardware expects the software + * context to have its byte order swapped on each 8-byte (64-bit) boundary + * before being written to the command packet. + * + * Example: 00 01 02 03 04 05 06 07 -> 07 06 05 04 03 02 01 00 + */ +static void hwkm_reorder_kdf_ctx(uint8_t *buf, size_t len) +{ + size_t i = 0; + + for (i = 0; i < len; i += sizeof(uint64_t)) + hwkm_reverse_bytes(buf + i, MIN(sizeof(uint64_t), len - i)); +} + +/* RUN TRANSACTION. */ + +#define HWKM_MASTER_MAX_RETRIES 100000U + +static int hwkm_fifo_wait(vaddr_t base, uint32_t reg, uint32_t mask) +{ + uint32_t retries = 0; + + while (!io_read32_off_field(base, reg, mask)) { + if (++retries > HWKM_MASTER_MAX_RETRIES) + return HWKM_ERR_FIFO_TIMEOUT; + udelay(10); + } + + return HWKM_SUCCESS; +} + +/* + * master_run_transaction() - Submit one command packet to the master HWKM. + * @cmd: Command packet words to write into the command FIFO. + * @cmd_words: Number of 32-bit words in @cmd. + * @rsp: Response buffer filled from the response FIFO. + * @rsp_words: Number of 32-bit words expected in @rsp. + * + * Return: HWKM_SUCCESS on success, or a HWKM_ERR_* code on failure. + */ +static int master_run_transaction(const uint32_t *cmd, size_t cmd_words, + uint32_t *rsp, size_t rsp_words) +{ + struct hwkm_drv_ctx *ctx = NULL; + int rc = HWKM_ERR_GENERIC; + vaddr_t base = 0; + size_t i = 0; + + if (!cmd || !cmd_words || !rsp || !rsp_words) + return HWKM_ERR_INVALID_ARG; + + ctx = hwkm_get_context(); + if (!ctx) + return HWKM_ERR_INVALID_ARG; + + base = ctx->base; + + /* Flush any stale command FIFO contents. */ + io_write32_off_field(base, HWKM_BANK0_KM_CTL, + HWKM_BANK0_KM_CTL_CMD_FIFO_CLEAR, 1); + io_write32_off_field(base, HWKM_BANK0_KM_CTL, + HWKM_BANK0_KM_CTL_CMD_FIFO_CLEAR, 0); + + /* Clear stale error state from the previous transaction. */ + io_write32_off(base, HWKM_BANK0_KM_ESR, + io_read32_off(base, HWKM_BANK0_KM_ESR)); + + /* Enable command processing. */ + io_write32_off_field(base, HWKM_BANK0_KM_CTL, + HWKM_BANK0_KM_CTL_CMD_ENABLE, 1); + + /* Confirm the FIFO clear bit has deasserted. */ + if (io_read32_off_field(base, HWKM_BANK0_KM_CTL, + HWKM_BANK0_KM_CTL_CMD_FIFO_CLEAR)) + return HWKM_ERR_FIFO_NOT_EMPTY; + + /* Push the command packet one word at a time. */ + for (i = 0; i < cmd_words; i++) { + rc = hwkm_fifo_wait(base, HWKM_BANK0_KM_STATUS, + HWKM_BANK0_KM_STATUS_CMD_AVAIL_SPACE); + if (rc) + return rc; + + io_write32_off(base, HWKM_BANK0_KM_CMD_FIFO, cmd[i]); + } + + /* Pull the response packet one word at a time. */ + for (i = 0; i < rsp_words; i++) { + rc = hwkm_fifo_wait(base, HWKM_BANK0_KM_STATUS, + HWKM_BANK0_KM_STATUS_RSP_AVAIL_DATA); + if (rc) + return rc; + + rsp[i] = io_read32_off(base, HWKM_BANK0_KM_RSP_FIFO); + } + + /* The hardware must report completion after the response is read. */ + if (!io_read32_off_field(base, HWKM_BANK0_KM_IRQ_STATUS, + HWKM_BANK0_KM_IRQ_STATUS_CMD_DONE)) + return HWKM_ERR_RSP_OVERFLOW; + + /* Acknowledge completion. */ + io_write32_off(base, HWKM_BANK0_KM_IRQ_STATUS, + HWKM_BANK0_KM_IRQ_STATUS_CMD_DONE); + + return HWKM_SUCCESS; +} + +static int run_transaction(const struct hwkm_transaction *t, + const uint32_t *cmd, size_t cmd_words, + uint32_t *rsp, size_t rsp_words) +{ + switch (t->hdl->dest) { + case HWKM_KEY_DEST_KM_MASTER: + return master_run_transaction(cmd, cmd_words, rsp, rsp_words); + default: + return HWKM_ERR_INVALID_DEST; + } +} + +/* Command handlers. */ + +static int hwkm_nist_keygen_handle(struct hwkm_transaction *t) +{ + uint32_t cmd[HWKM_OP_NIST_KEYGEN_CMD_WORDS] = { }; + uint32_t rsp[HWKM_OP_NIST_KEYGEN_RSP_WORDS] = { }; + int rc = HWKM_ERR_GENERIC; + + if (!t || !t->hdl) + return HWKM_ERR_INVALID_ARG; + + /* CMD[0]: */ + hwkm_pack_op_info(&cmd[0], HWKM_OP_NIST_KEYGEN, t->cmd.keygen.dks, + 0, false, 0, ARRAY_SIZE(cmd)); + /* CMD[1:2]: */ + hwkm_pack_key_policy(&cmd[HWKM_OPERATION_INFO_WORDS], + &t->cmd.keygen.policy); + + rc = run_transaction(t, cmd, ARRAY_SIZE(cmd), rsp, ARRAY_SIZE(rsp)); + /* On success, RSP[1]: */ + if (rc == HWKM_SUCCESS) + t->rsp.status = rsp[HWKM_OP_NIST_KEYGEN_RSP_ERR_IDX]; + + memzero_explicit(cmd, sizeof(cmd)); + memzero_explicit(rsp, sizeof(rsp)); + + return rc; +} + +static int hwkm_system_kdf_handle(struct hwkm_transaction *t) +{ + uint32_t cmd[HWKM_OP_SYSTEM_KDF_CMD_WORDS + + HWKM_BSVE_WORDS + HWKM_SOFTWARE_CONTEXT_WORDS] = { }; + uint32_t rsp[HWKM_OP_SYSTEM_KDF_RSP_WORDS] = { }; + int rc = HWKM_ERR_GENERIC; + size_t ctx_words = 0; + size_t cmd_words = 0; + size_t ctx_idx = 0; + + if (!t || !t->hdl) + return HWKM_ERR_INVALID_ARG; + + if (t->cmd.kdf.ctx_len > sizeof(t->cmd.kdf.ctx)) + return HWKM_ERR_INVALID_ARG; + + ctx_words = ROUNDUP_DIV(t->cmd.kdf.ctx_len, sizeof(uint32_t)); + ctx_idx = HWKM_OPERATION_INFO_WORDS + HWKM_KEY_POLICY_WORDS + + (t->cmd.kdf.bsve.enabled ? HWKM_BSVE_WORDS : 1); + + /* Total command length. */ + cmd_words = ctx_idx + ctx_words + 1 /* CRC */; + if (cmd_words > ARRAY_SIZE(cmd)) + return HWKM_ERR_INVALID_ARG; + + /* CMD[0]: */ + hwkm_pack_op_info(&cmd[0], HWKM_OP_SYSTEM_KDF, t->cmd.kdf.dks, + t->cmd.kdf.kdk, t->cmd.kdf.bsve.enabled, ctx_words, + /* incl. CRC, excl. context. */ + ctx_idx + 1); + /* CMD[1:2]: */ + hwkm_pack_key_policy(&cmd[HWKM_OPERATION_INFO_WORDS], + &t->cmd.kdf.policy); + + if (t->cmd.kdf.bsve.enabled) { + /* CMD[3:5]: */ + hwkm_pack_kdf_bsve(&cmd[HWKM_OPERATION_INFO_WORDS + + HWKM_KEY_POLICY_WORDS], + &t->cmd.kdf.bsve, t->cmd.kdf.mks); + } + + if (t->cmd.kdf.ctx_len) { + /* CMD[n:], if software context exists: */ + memcpy(&cmd[ctx_idx], t->cmd.kdf.ctx, t->cmd.kdf.ctx_len); + hwkm_reorder_kdf_ctx((uint8_t *)&cmd[ctx_idx], + t->cmd.kdf.ctx_len); + } + + rc = run_transaction(t, cmd, cmd_words, rsp, ARRAY_SIZE(rsp)); + /* On success, RSP[1]: */ + if (rc == HWKM_SUCCESS) + t->rsp.status = rsp[HWKM_OP_SYSTEM_KDF_RSP_ERR_IDX]; + + memzero_explicit(cmd, sizeof(cmd)); + memzero_explicit(rsp, sizeof(rsp)); + + return rc; +} + +static int hwkm_key_wrap_export_handle(struct hwkm_transaction *t) +{ + uint32_t cmd[HWKM_OP_KEY_WRAP_EXPORT_CMD_WORDS] = { }; + uint32_t rsp[HWKM_OP_KEY_WRAP_EXPORT_RSP_WORDS] = { }; + int rc = HWKM_ERR_GENERIC; + + if (!t || !t->hdl) + return HWKM_ERR_INVALID_ARG; + + /* CMD[0]: */ + hwkm_pack_op_info(&cmd[0], HWKM_OP_KEY_WRAP_EXPORT, t->cmd.wrap.sks, + t->cmd.wrap.kwk, false, 0, ARRAY_SIZE(cmd)); + /* CMD[1:3]: */ + hwkm_pack_wrap_bsve(&cmd[HWKM_OPERATION_INFO_WORDS], + &t->cmd.wrap.bsve); + + rc = run_transaction(t, cmd, ARRAY_SIZE(cmd), rsp, ARRAY_SIZE(rsp)); + /* On success, RSP[1]: */ + if (rc == HWKM_SUCCESS) { + t->rsp.status = rsp[HWKM_OP_KEY_WRAP_EXPORT_RSP_ERR_IDX]; + /* On operation success, RSP[2:18]: */ + if (!t->rsp.status) { + memcpy(t->rsp.wrap.wkb, + &rsp[HWKM_OP_KEY_WRAP_EXPORT_RSP_BLOB_IDX], + HWKM_MAX_BLOB_SIZE); + } + } + + memzero_explicit(cmd, sizeof(cmd)); + memzero_explicit(rsp, sizeof(rsp)); + + return rc; +} + +static int hwkm_key_unwrap_import_handle(struct hwkm_transaction *t) +{ + uint32_t cmd[HWKM_OP_KEY_UNWRAP_IMPORT_CMD_WORDS] = { }; + uint32_t rsp[HWKM_OP_KEY_UNWRAP_IMPORT_RSP_WORDS] = { }; + int rc = HWKM_ERR_GENERIC; + + if (!t || !t->hdl) + return HWKM_ERR_INVALID_ARG; + + /* CMD[0]: */ + hwkm_pack_op_info(&cmd[0], HWKM_OP_KEY_UNWRAP_IMPORT, + t->cmd.unwrap.dks, t->cmd.unwrap.kwk, false, 0, + ARRAY_SIZE(cmd)); + /* CMD[1:17]: */ + memcpy(&cmd[HWKM_OPERATION_INFO_WORDS], t->cmd.unwrap.wkb, + HWKM_MAX_BLOB_SIZE); + + rc = run_transaction(t, cmd, ARRAY_SIZE(cmd), rsp, ARRAY_SIZE(rsp)); + /* On success, RSP[1]: */ + if (rc == HWKM_SUCCESS) + t->rsp.status = rsp[HWKM_OP_KEY_UNWRAP_IMPORT_RSP_ERR_IDX]; + + memzero_explicit(cmd, sizeof(cmd)); + memzero_explicit(rsp, sizeof(rsp)); + + return rc; +} + +static int hwkm_key_slot_clear_handle(struct hwkm_transaction *t) +{ + uint32_t cmd[HWKM_OP_KEY_SLOT_CLEAR_CMD_WORDS] = { }; + uint32_t rsp[HWKM_OP_KEY_SLOT_CLEAR_RSP_WORDS] = { }; + int rc = HWKM_ERR_GENERIC; + + if (!t || !t->hdl) + return HWKM_ERR_INVALID_ARG; + + /* CMD[0]: */ + hwkm_pack_op_info(&cmd[0], HWKM_OP_KEY_SLOT_CLEAR, t->cmd.clear.dks, + 0, t->cmd.clear.is_double_key, 0, ARRAY_SIZE(cmd)); + + rc = run_transaction(t, cmd, ARRAY_SIZE(cmd), rsp, ARRAY_SIZE(rsp)); + /* On success, RSP[1]: */ + if (rc == HWKM_SUCCESS) + t->rsp.status = rsp[HWKM_OP_KEY_SLOT_CLEAR_RSP_ERR_IDX]; + + memzero_explicit(cmd, sizeof(cmd)); + memzero_explicit(rsp, sizeof(rsp)); + + return rc; +} + +static int hwkm_key_slot_rdwr_handle(struct hwkm_transaction *t) +{ + uint32_t cmd[HWKM_OP_KEY_SLOT_RDWR_CMD_WORDS] = { }; + uint32_t rsp[HWKM_OP_KEY_SLOT_RDWR_RSP_WORDS] = { }; + int rc = HWKM_ERR_GENERIC; + + if (!t || !t->hdl) + return HWKM_ERR_INVALID_ARG; + + /* CMD[0]: */ + hwkm_pack_op_info(&cmd[0], HWKM_OP_KEY_SLOT_RDWR, t->cmd.rdwr.slot, + 0, t->cmd.rdwr.is_write, 0, ARRAY_SIZE(cmd)); + + if (t->cmd.rdwr.is_write) { + /* CMD[1:2]: */ + hwkm_pack_key_policy(&cmd[HWKM_OPERATION_INFO_WORDS], + &t->cmd.rdwr.policy); + /* CMD[3:10]: */ + memcpy(&cmd[HWKM_OPERATION_INFO_WORDS + HWKM_KEY_POLICY_WORDS], + t->cmd.rdwr.key, HWKM_MAX_KEY_SIZE); + hwkm_reverse_bytes((uint8_t *)&cmd[HWKM_OPERATION_INFO_WORDS + + HWKM_KEY_POLICY_WORDS], + HWKM_MAX_KEY_SIZE); + } + + rc = run_transaction(t, cmd, ARRAY_SIZE(cmd), rsp, ARRAY_SIZE(rsp)); + /* On success, RSP[1]: */ + if (rc == HWKM_SUCCESS) { + t->rsp.status = rsp[HWKM_OP_KEY_SLOT_RDWR_RSP_ERR_IDX]; + if (!t->cmd.rdwr.is_write && !t->rsp.status) { + uint32_t *rsp_policy = + &rsp[HWKM_OP_KEY_SLOT_RDWR_RSP_POLICY_IDX]; + + /* RSP[2:3]: */ + hwkm_unpack_key_policy(&t->rsp.rdwr.policy, rsp_policy); + /* RSP[4:11]: */ + memcpy(t->rsp.rdwr.key, + &rsp[HWKM_OP_KEY_SLOT_RDWR_RSP_READ_KEY_IDX], + HWKM_MAX_KEY_SIZE); + hwkm_reverse_bytes(t->rsp.rdwr.key, HWKM_MAX_KEY_SIZE); + } + } + + memzero_explicit(cmd, sizeof(cmd)); + memzero_explicit(rsp, sizeof(rsp)); + + return rc; +} + +static int hwkm_set_tpkey_handle(struct hwkm_transaction *t) +{ + uint32_t cmd[HWKM_OP_SET_TPKEY_CMD_WORDS] = { }; + uint32_t rsp[HWKM_OP_SET_TPKEY_RSP_WORDS] = { }; + int rc = HWKM_ERR_GENERIC; + + if (!t || !t->hdl) + return HWKM_ERR_INVALID_ARG; + + if (t->hdl->dest != HWKM_KEY_DEST_KM_MASTER) + return HWKM_ERR_INVALID_DEST; + + /* CMD[0]: */ + hwkm_pack_op_info(&cmd[0], HWKM_OP_SET_TPKEY, t->cmd.set_tpkey.sks, + 0, false, 0, ARRAY_SIZE(cmd)); + + rc = run_transaction(t, cmd, ARRAY_SIZE(cmd), rsp, ARRAY_SIZE(rsp)); + /* On success, RSP[1]: */ + if (rc == HWKM_SUCCESS) + t->rsp.status = rsp[HWKM_OP_SET_TPKEY_RSP_ERR_IDX]; + + memzero_explicit(cmd, sizeof(cmd)); + memzero_explicit(rsp, sizeof(rsp)); + + return rc; +} + +static int hwkm_transaction_dispatch(struct hwkm_transaction *t) +{ + switch (t->cmd.op) { + case HWKM_OP_NIST_KEYGEN: + return hwkm_nist_keygen_handle(t); + case HWKM_OP_SYSTEM_KDF: + return hwkm_system_kdf_handle(t); + case HWKM_OP_KEY_WRAP_EXPORT: + return hwkm_key_wrap_export_handle(t); + case HWKM_OP_KEY_UNWRAP_IMPORT: + return hwkm_key_unwrap_import_handle(t); + case HWKM_OP_KEY_SLOT_CLEAR: + return hwkm_key_slot_clear_handle(t); + case HWKM_OP_KEY_SLOT_RDWR: + return hwkm_key_slot_rdwr_handle(t); + case HWKM_OP_SET_TPKEY: + return hwkm_set_tpkey_handle(t); + default: + return HWKM_ERR_NOT_SUPPORTED; + } +} + +/* Command queue. */ + +int hwkm_handle_init(struct hwkm_handle *hdl, enum hwkm_key_destination dest) +{ + switch (dest) { + case HWKM_KEY_DEST_KM_MASTER: + break; + default: + return HWKM_ERR_INVALID_DEST; + } + + hdl->dest = dest; + STAILQ_INIT(&hdl->queue); + + return HWKM_SUCCESS; +} + +int hwkm_enqueue(struct hwkm_handle *hdl, struct hwkm_transaction *t) +{ + if (!t) + return HWKM_ERR_INVALID_ARG; + + if (t->hdl) + return HWKM_ERR_INVALID_ARG; + + t->hdl = hdl; + STAILQ_INSERT_TAIL(&hdl->queue, t, link); + + return HWKM_SUCCESS; +} + +/* + * hwkm_enqueue_many() - Enqueue multiple transactions on one handle. + * @hdl: Handle that owns the queue. + * @num_t: Number of transactions in @trans. + * @trans: Array of transaction pointers to enqueue in FIFO order. + * + * Enqueues all transactions in @trans onto @hdl using hwkm_enqueue(). + * If enqueueing any transaction fails, all transactions already queued by + * this call are removed again and their ownership is cleared, so the caller + * sees all-or-nothing behavior. + * + * Return: HWKM_SUCCESS on success, or a HWKM_ERR_* code on failure. + */ +int hwkm_enqueue_many(struct hwkm_handle *hdl, size_t num_t, + struct hwkm_transaction *const trans[]) +{ + int rc = HWKM_ERR_GENERIC; + size_t i = 0; + + if (!hdl || (num_t && !trans)) + return HWKM_ERR_INVALID_ARG; + + for (i = 0; i < num_t; i++) { + rc = hwkm_enqueue(hdl, trans[i]); + if (rc) + goto rollback; + } + + return HWKM_SUCCESS; + +rollback: + while (i > 0) { + struct hwkm_transaction *t = trans[--i]; + + /* Release the ownership. */ + STAILQ_REMOVE(&hdl->queue, t, hwkm_transaction, link); + t->hdl = NULL; + } + + return rc; +} + +/* + * hwkm_run_cmd_queue() - Execute queued transactions. + * @hdl: Handle containing queued transactions + * + * Executes all transactions queued on @hdl in FIFO order by calling + * hwkm_transaction_dispatch() for each transaction. Execution stops at the + * first error and that error is returned to the caller. + * + * On return, transactions that were processed are removed from the queue and + * their ownership is cleared (to be reused). + * + * Return: HWKM_SUCCESS on success, or a HWKM_ERR_* code on failure. + */ +int hwkm_run_cmd_queue(struct hwkm_handle *hdl) +{ + struct hwkm_transaction *t = NULL; + int rc = HWKM_SUCCESS; + + while (!STAILQ_EMPTY(&hdl->queue)) { + t = STAILQ_FIRST(&hdl->queue); + rc = hwkm_transaction_dispatch(t); + /* Release the ownership. */ + STAILQ_REMOVE_HEAD(&hdl->queue, link); + t->hdl = NULL; + + if (rc != HWKM_SUCCESS) + break; + } + + return rc; +} diff --git a/core/drivers/crypto/qcom/include/hwkm.h b/core/drivers/crypto/qcom/include/hwkm.h new file mode 100644 index 000000000..ff4c84be1 --- /dev/null +++ b/core/drivers/crypto/qcom/include/hwkm.h @@ -0,0 +1,377 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. + */ + +#ifndef __HWKM_H__ +#define __HWKM_H__ + +#include +#include +#include +#include + +#define HWKM_MAX_KEY_SIZE 32 /* bytes */ +#define HWKM_MAX_CTX_SIZE 64 /* bytes */ +#define HWKM_MAX_BLOB_SIZE 68 /* bytes */ + +enum hwkm_key_destination { + HWKM_KEY_DEST_KM_MASTER = 0, +}; + +enum hwkm_key_security_lvl { + HWKM_KEY_SECURITY_LVL_SW_KEY = 0, + HWKM_KEY_SECURITY_LVL_MANAGED_KEY, + HWKM_KEY_SECURITY_LVL_HW_KEY, +}; + +enum hwkm_key_type { + HWKM_KEY_TYPE_KDK = 0, /* Key derivation key. */ + HWKM_KEY_TYPE_KWK, /* Key wrapping key. */ + HWKM_KEY_TYPE_KSK, /* Key swap key. */ + HWKM_KEY_TYPE_TPKEY, /* Transport protection key. */ + HWKM_KEY_TYPE_GENERIC_KEY, +}; + +/* Master key slot assignments. */ +enum hwkm_master_key_slots { + /* L1 KDKs - HW-only: */ + HWKM_SLOT_NKDK_L1 = 0, + HWKM_SLOT_PKDK_L1 = 1, + HWKM_SLOT_SKDK_L1 = 2, + HWKM_SLOT_UKDK_L1 = 3, + /* L2 KDKs. */ + HWKM_SLOT_TZ_NKDK_L2 = 4, + HWKM_SLOT_TZ_PKDK_L2 = 5, + HWKM_SLOT_TZ_SKDK_L2 = 6, + HWKM_SLOT_MODEM_PKDK_L2 = 7, + HWKM_SLOT_MODEM_SKDK_L2 = 8, + HWKM_SLOT_TZ_UKDK_L2 = 9, + /* TPKEY pair. */ + HWKM_SLOT_TPKEY_SLOT = 10, + HWKM_SLOT_TPKEY_ODD_SLOT = 11, + /* Swap key pair. */ + HWKM_SLOT_TZ_SWAP_KEY_SLOT = 12, + HWKM_SLOT_TZ_SWAP_KEY_ODD_SLOT = 13, + /* Wrap key pair. */ + HWKM_SLOT_TZ_WRAP_KEY_SLOT = 14, + HWKM_SLOT_TZ_WRAP_KEY_ODD_SLOT = 15, + /* General purpose scratch. */ + HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT1 = 16, + HWKM_SLOT_TZ_GENERAL_PURPOSE_SLOT2 = 17, + /* Persistent shared pairs. */ + HWKM_SLOT_PERSISTENT_SHARED_SLOT_PAIR1 = 18, + HWKM_SLOT_PERSISTENT_SHARED_SLOT_PAIR1_ODD = 19, + HWKM_SLOT_PERSISTENT_SHARED_SLOT_PAIR2 = 20, + HWKM_SLOT_PERSISTENT_SHARED_SLOT_PAIR2_ODD = 21, + /* Mixing key. */ + HWKM_SLOT_TZ_MIXING_KEY_SLOT = 22, +}; + +enum hwkm_algo { + HWKM_ALGO_AES128_ECB = 0, + HWKM_ALGO_AES256_ECB = 1, + HWKM_ALGO_AES128_CBC = 4, + HWKM_ALGO_AES256_CBC = 5, + HWKM_ALGO_AES256_SIV = 12, + HWKM_ALGO_AES128_CTR = 13, + HWKM_ALGO_AES256_CTR = 14, + HWKM_ALGO_AES128_XTS = 15, + HWKM_ALGO_AES256_XTS = 16, + HWKM_ALGO_AES128_CMAC = 19, + HWKM_ALGO_AES256_CMAC = 20, +}; + +struct hwkm_key_policy { + /* TZ may issue commands against this slot. */ + bool km_by_tz_allowed; + /* Non-secure world may use this slot. */ + bool km_by_nsec_allowed; + /* Modem may use this slot. */ + bool km_by_modem_allowed; + /* SPU may use this slot. */ + bool km_by_spu_allowed; + /* Only algorithm permitted for this key. */ + enum hwkm_algo alg_allowed; + /* Key may be used for encryption. */ + bool enc_allowed; + /* Key may be used for decryption. */ + bool dec_allowed; + /* Functional role. */ + enum hwkm_key_type key_type; + /* Remaining KDF derivation hops. */ + uint8_t kdf_depth; + /* Key may be exported via KEY_WRAP_EXPORT. */ + bool wrap_export_allowed; + /* Key may be delivered via key-swap. */ + bool swap_export_allowed; + /* SW_KEY, MANAGED_KEY, or HW_KEY. */ + enum hwkm_key_security_lvl security_lvl; + /* Hardware destination. */ + enum hwkm_key_destination hw_destination; + /* Allow wrapping under TPKEY. */ + bool wrap_with_tpkey_allowed; +}; + +struct hwkm_bsve { + /* Master enable for all BSVE checks. */ + bool enabled; + /* Enforce key policy version field. */ + bool km_key_policy_ver_en; + /* Require apps processor to be in secure state. */ + bool km_apps_secure_en; + /* Require modem to be in secure state. */ + bool km_msa_secure_en; + /* Bind derivation to life-cycle fuse state. */ + bool km_lcm_fuse_en; + /* Bind derivation to boot-stage OTP value. */ + bool km_boot_stage_otp_en; + /* Enforce software component version check. */ + bool km_swc_en; + /* Child key policy must be subset of parent. */ + bool km_child_key_policy_en; + /* Include the mixing-key slot value in the KDF. */ + bool km_mks_en; + /* Fuse regions hashed into KDF. */ + uint64_t km_fuse_region_sha_digest_en; +}; + +/* Command and response structs. */ + +enum hwkm_op { + /* Generate a key via HW PRNG. */ + HWKM_OP_NIST_KEYGEN = 0, + /* Derive a child key from a KDK slot. */ + HWKM_OP_SYSTEM_KDF = 1, + /* Opcode 2 is reserved. */ + /* Wrap a slot under a KWK/KSK. */ + HWKM_OP_KEY_WRAP_EXPORT = 3, + /* Unwrap a blob into a destination slot. */ + HWKM_OP_KEY_UNWRAP_IMPORT = 4, + /* Clear a slot and invalidate its policy. */ + HWKM_OP_KEY_SLOT_CLEAR = 5, + /* Read or write raw key material. */ + HWKM_OP_KEY_SLOT_RDWR = 6, + /* Install a slot as the active TPKEY. */ + HWKM_OP_SET_TPKEY = 7, +}; + +struct hwkm_keygen_cmd { + uint8_t dks; /* Destination key slot. */ + struct hwkm_key_policy policy; +}; + +struct hwkm_rdwr_cmd { + uint8_t slot; + bool is_write; + struct hwkm_key_policy policy; + uint8_t key[HWKM_MAX_KEY_SIZE]; +}; + +struct hwkm_kdf_cmd { + uint8_t dks; /* Destination key slot. */ + uint8_t kdk; /* Parent KDK slot. */ + uint8_t mks; /* Mixing-key slot selector. */ + struct hwkm_key_policy policy; + struct hwkm_bsve bsve; + uint8_t ctx[HWKM_MAX_CTX_SIZE]; + size_t ctx_len; +}; + +struct hwkm_set_tpkey_cmd { + uint8_t sks; /* Source key slot. */ +}; + +struct hwkm_unwrap_cmd { + uint8_t dks; /* Destination key slot. */ + uint8_t kwk; /* Wrapping key slot. */ + uint8_t wkb[HWKM_MAX_BLOB_SIZE]; +}; + +struct hwkm_wrap_cmd { + uint8_t sks; /* Source key slot. */ + uint8_t kwk; /* Wrapping key slot. */ + struct hwkm_bsve bsve; +}; + +struct hwkm_clear_cmd { + uint8_t dks; /* Destination key slot. */ + bool is_double_key; +}; + +struct hwkm_cmd { + enum hwkm_op op; + union { + struct hwkm_keygen_cmd keygen; + struct hwkm_rdwr_cmd rdwr; + struct hwkm_kdf_cmd kdf; + struct hwkm_set_tpkey_cmd set_tpkey; + struct hwkm_unwrap_cmd unwrap; + struct hwkm_wrap_cmd wrap; + struct hwkm_clear_cmd clear; + }; +}; + +struct hwkm_rdwr_rsp { + struct hwkm_key_policy policy; + uint8_t key[HWKM_MAX_KEY_SIZE]; +}; + +struct hwkm_wrap_rsp { + uint8_t wkb[HWKM_MAX_BLOB_SIZE]; +}; + +struct hwkm_rsp { + uint32_t status; + union { + struct hwkm_rdwr_rsp rdwr; + struct hwkm_wrap_rsp wrap; + }; +}; + +/* PUBLIC API. */ + +#define HWKM_SUCCESS 0 +#define HWKM_ERR_GENERIC 1 +#define HWKM_ERR_INVALID_ARG 2 +#define HWKM_ERR_INVALID_DEST 3 +#define HWKM_ERR_FIFO_NOT_EMPTY 4 +#define HWKM_ERR_FIFO_TIMEOUT 5 +#define HWKM_ERR_RSP_OVERFLOW 6 +#define HWKM_ERR_NOT_SUPPORTED 7 + +static inline TEE_Result hwkm_to_optee(int rc) +{ + switch (rc) { + case HWKM_SUCCESS: + return TEE_SUCCESS; + case HWKM_ERR_INVALID_ARG: + case HWKM_ERR_INVALID_DEST: + return TEE_ERROR_BAD_PARAMETERS; + case HWKM_ERR_NOT_SUPPORTED: + return TEE_ERROR_NOT_SUPPORTED; + default: + return TEE_ERROR_GENERIC; + } +} + +struct hwkm_handle; + +struct hwkm_drv_ctx { + vaddr_t base; + struct mutex hwkm_lock; /* Serializes all HWKM operations. */ + bool hwkm_huk_ready; + uint8_t hwkm_huk[HWKM_MAX_KEY_SIZE]; + bool initialized; +}; + +struct hwkm_drv_ctx *hwkm_get_context(void); + +/* + * struct hwkm_transaction - queued HWKM command and response. + * @cmd: Command to submit. + * @rsp: Response filled by the driver. + * @hdl: Owning handle when the transaction is queued. + * @link: Queue link. + * + * A transaction contains one HWKM command and its corresponding response. + * Transactions are queued on a handle and executed in FIFO order by + * hwkm_run_cmd_queue(). + */ +struct hwkm_transaction { + struct hwkm_cmd cmd; + struct hwkm_rsp rsp; + struct hwkm_handle *hdl; + + STAILQ_ENTRY(hwkm_transaction) link; +}; + +STAILQ_HEAD(hwkm_transaction_queue, hwkm_transaction); + +/* + * struct hwkm_handle - HWKM command queue handle. + * @dest: Target hardware engine. + * @queue: Queued transactions. + * + * A handle groups one or more HWKM commands targeting the same destination. + * Initialize it with hwkm_handle_init(), enqueue transactions with + * hwkm_enqueue() / hwkm_enqueue_many(), then execute them with + * hwkm_run_cmd_queue(). + * + * Example: + * + * struct hwkm_transaction t_kdf = { + * .cmd = { + * .op = HWKM_OP_SYSTEM_KDF, + * }, + * }; + * + * struct hwkm_transaction t_clear = { + * .cmd = { + * .op = HWKM_OP_KEY_SLOT_CLEAR, + * }, + * }; + * + * struct hwkm_handle h = { }; + * + * hwkm_handle_init(&h, HWKM_KEY_DEST_KM_MASTER); + * hwkm_enqueue(&h, &t_kdf); + * hwkm_enqueue(&h, &t_clear); + * hwkm_run_cmd_queue(&h); + * + * The responses are returned in t_kdf.rsp and t_clear.rsp. + */ +struct hwkm_handle { + enum hwkm_key_destination dest; + struct hwkm_transaction_queue queue; +}; + +struct hwkm_transaction *hwkm_transaction_alloc(void); +void hwkm_transaction_free(struct hwkm_transaction *t); + +int hwkm_handle_init(struct hwkm_handle *hdl, enum hwkm_key_destination dest); +int hwkm_enqueue(struct hwkm_handle *hdl, struct hwkm_transaction *t); +int hwkm_enqueue_many(struct hwkm_handle *hdl, size_t num_t, + struct hwkm_transaction *const trans[]); +int hwkm_run_cmd_queue(struct hwkm_handle *hdl); + +/* + * hwkm_run_transactions() - Init a handle, enqueue @num_t transactions, and + * execute them in one call. + * @dest: Target hardware engine. + * @num_t: Number of entries in @trans. + * @trans: Array of transaction pointers executed in FIFO order. + * + * Convenience wrapper around hwkm_handle_init() + hwkm_enqueue_many() + + * hwkm_run_cmd_queue(). Use when all transactions target the same destination + * and no handle needs to survive the call. + * + * Return: HWKM_SUCCESS on success, or a HWKM_ERR_* code on failure. + */ +static inline int hwkm_run_transactions(enum hwkm_key_destination dest, + size_t num_t, + struct hwkm_transaction *const trans[]) +{ + struct hwkm_handle hdl = { }; + int rc = HWKM_ERR_GENERIC; + + rc = hwkm_handle_init(&hdl, dest); + if (rc) + return rc; + + rc = hwkm_enqueue_many(&hdl, num_t, trans); + if (rc) + return rc; + + return hwkm_run_cmd_queue(&hdl); +} + +/* Execute a single transaction. */ +static inline int hwkm_run_transaction(enum hwkm_key_destination dest, + struct hwkm_transaction *t) +{ + return hwkm_run_transactions(dest, 1, + (struct hwkm_transaction *const[]){ t }); +} + +#endif /* __HWKM_H__ */ diff --git a/core/drivers/crypto/qcom/include/hwkm_errno.h b/core/drivers/crypto/qcom/include/hwkm_errno.h new file mode 100644 index 000000000..f8f3998a2 --- /dev/null +++ b/core/drivers/crypto/qcom/include/hwkm_errno.h @@ -0,0 +1,143 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. + * + * Hardware Key Manager (HWKM) response-error bit definitions. + */ + +#ifndef __HWKM_ERRNO_H__ +#define __HWKM_ERRNO_H__ + +#include + +/* Error status bitmap at RSP[*_RSP_ERR_IDX]. */ + +#define HWKM_RSP_ERR_SUCCESS 0U +/* Common errors (bits 0 - 7, present in every opcode response). */ +#define HWKM_ERR_RSP_FIFO_UNDERFLOW BIT(0) +#define HWKM_ERR_CMD_FIFO_OVERFLOW BIT(1) +#define HWKM_ERR_OPCODE_UNSUPPORTED BIT(2) +#define HWKM_ERR_INVALID_OPCODE BIT(3) +#define HWKM_ERR_INCORRECT_CMD_LENGTH BIT(4) +#define HWKM_ERR_CRC_CHECK BIT(5) +#define HWKM_ERR_BBAC_ACCESS BIT(6) + +/* NIST_KEYGEN specific errors (bits 8+). */ +#define HWKM_NIST_KEYGEN_ERR_KEYSLOT_ID_OUT_OF_RANGE BIT(8) +#define HWKM_NIST_KEYGEN_ERR_UNSUPPORTED_KEY_SIZE BIT(9) +#define HWKM_NIST_KEYGEN_ERR_KT_PARITY BIT(10) +#define HWKM_NIST_KEYGEN_ERR_INVALID_KEY_TYPE BIT(11) +#define HWKM_NIST_KEYGEN_ERR_INVALID_ALG_ALLOWED BIT(12) +#define HWKM_NIST_KEYGEN_ERR_INVALID_KEY_ATTRIB BIT(13) +#define HWKM_NIST_KEYGEN_ERR_INVALID_TPKEY_ATTRIB BIT(14) +#define HWKM_NIST_KEYGEN_ERR_INVALID_KSK_ATTRIB BIT(15) +#define HWKM_NIST_KEYGEN_ERR_INVALID_KWK_ATTRIB BIT(16) +#define HWKM_NIST_KEYGEN_ERR_INVALID_KDF_DEPTH BIT(17) +#define HWKM_NIST_KEYGEN_ERR_INVALID_SECURITY_LEVEL BIT(18) +#define HWKM_NIST_KEYGEN_ERR_DKS_SLOT_NOT_EMPTY BIT(19) +#define HWKM_NIST_KEYGEN_ERR_ACCESS_VIOLATION BIT(20) + +/* KEY_SLOT_RDWR specific errors (bits 8+). */ +#define HWKM_KEY_SLOT_RDWR_ERR_KEYSLOT_ID_OUT_OF_RANGE BIT(8) +#define HWKM_KEY_SLOT_RDWR_ERR_UNSUPPORTED_KEY_SIZE BIT(9) +#define HWKM_KEY_SLOT_RDWR_ERR_KT_PARITY BIT(10) +#define HWKM_KEY_SLOT_RDWR_ERR_INVALID_SECURITY_LEVEL BIT(11) +#define HWKM_KEY_SLOT_RDWR_ERR_INVALID_KEY_TYPE BIT(12) +#define HWKM_KEY_SLOT_RDWR_ERR_INVALID_ALG_ALLOWED BIT(13) +#define HWKM_KEY_SLOT_RDWR_ERR_INVALID_KEY_ATTRIB BIT(14) +#define HWKM_KEY_SLOT_RDWR_ERR_INVALID_TPKEY_ATTRIB BIT(15) +#define HWKM_KEY_SLOT_RDWR_ERR_INVALID_KSK_ATTRIB BIT(16) +#define HWKM_KEY_SLOT_RDWR_ERR_INVALID_KWK_ATTRIB BIT(17) +#define HWKM_KEY_SLOT_RDWR_ERR_INVALID_KDF_DEPTH BIT(18) +#define HWKM_KEY_SLOT_RDWR_ERR_SECURITY_LEVEL_VIOLATION BIT(19) +#define HWKM_KEY_SLOT_RDWR_ERR_SKS_SLOT_EMPTY BIT(20) +#define HWKM_KEY_SLOT_RDWR_ERR_DKS_SLOT_NOT_EMPTY BIT(21) +#define HWKM_KEY_SLOT_RDWR_ERR_ACCESS_VIOLATION BIT(22) + +/* SYSTEM_KDF specific errors (bits 8+). */ +#define HWKM_SYSTEM_KDF_ERR_FR_RD_ERR_ON_SLAVE BIT(8) +#define HWKM_SYSTEM_KDF_ERR_MKS_L1L2_NOT_ALLOWED BIT(9) +#define HWKM_SYSTEM_KDF_ERR_SEC_CTRL_ACCESS BIT(10) +#define HWKM_SYSTEM_KDF_ERR_KT_PARITY BIT(11) +#define HWKM_SYSTEM_KDF_ERR_BSVE_BIT_NOT_SUPPORTED BIT(12) +#define HWKM_SYSTEM_KDF_ERR_INVALID_SECURITY_LEVEL BIT(13) +#define HWKM_SYSTEM_KDF_ERR_INVALID_KEY_TYPE BIT(14) +#define HWKM_SYSTEM_KDF_ERR_INVALID_ALG_ALLOWED BIT(15) +#define HWKM_SYSTEM_KDF_ERR_SWAP_EXPORT_NOT_ALLOWED BIT(16) +#define HWKM_SYSTEM_KDF_ERR_KEYPOLICY_CONSISTENCY BIT(17) +#define HWKM_SYSTEM_KDF_ERR_ENCRYPT_NOT_ALLOWED BIT(18) +#define HWKM_SYSTEM_KDF_ERR_UNEXPECTED_ALG_ALLOWED BIT(19) +#define HWKM_SYSTEM_KDF_ERR_UNEXPECTED_KEY_TYPE BIT(20) +#define HWKM_SYSTEM_KDF_ERR_KDF_DEPTH_ERROR BIT(21) +#define HWKM_SYSTEM_KDF_ERR_KDK_TYPE BIT(22) +#define HWKM_SYSTEM_KDF_ERR_KEY_SIZE BIT(23) +#define HWKM_SYSTEM_KDF_ERR_KEYSLOT_SLOT BIT(24) +#define HWKM_SYSTEM_KDF_ERR_KEYSLOT_OUT_OF_RANGE BIT(25) +#define HWKM_SYSTEM_KDF_ERR_DOUBLE_KEY_SLOT_NOT_EVEN BIT(26) +#define HWKM_SYSTEM_KDF_ERR_ACCESS_VIOLATION BIT(27) +#define HWKM_SYSTEM_KDF_ERR_SECURITY_LEVEL_VIOLATION BIT(28) + +/* KEY_WRAP_EXPORT specific errors (bits 8+). */ +#define HWKM_EXPORT_ERR_KWK_DOUBLE_KEY_ODD_SLOT BIT(8) +#define HWKM_EXPORT_ERR_ENCRYPT_NOT_ALLOWED BIT(9) +#define HWKM_EXPORT_ERR_SKS_SIZE_ERROR BIT(10) +#define HWKM_EXPORT_ERR_SKS_SLOT_OUT_OF_RANGE BIT(11) +#define HWKM_EXPORT_ERR_KWK_SIZE_ERROR BIT(12) +#define HWKM_EXPORT_ERR_KWK_SLOT_OUT_OF_RANGE BIT(13) +#define HWKM_EXPORT_ERR_KT_PARITY BIT(14) +#define HWKM_EXPORT_ERR_WRAP_WITH_TPKEY_NOT_ALLOWED BIT(15) +#define HWKM_EXPORT_ERR_SWAP_EXPORT_NOT_ALLOWED BIT(16) +#define HWKM_EXPORT_ERR_WRAP_EXPORT_NOT_ALLOWED BIT(17) +#define HWKM_EXPORT_ERR_UNEXPECTED_ALG_ALLOWED BIT(18) +#define HWKM_EXPORT_ERR_SECURITY_LEVEL_ERROR BIT(19) +#define HWKM_EXPORT_ERR_UNEXPECTED_KEY_TYPE BIT(20) +#define HWKM_EXPORT_ERR_KWK_SLOT_EMPTY BIT(21) +#define HWKM_EXPORT_ERR_SKS_SLOT_EMPTY BIT(22) +#define HWKM_EXPORT_ERR_SKS_ACCESS_VIOLATION BIT(23) +#define HWKM_EXPORT_ERR_KWK_ACCESS_VIOLATION BIT(24) + +/* KEY_UNWRAP_IMPORT specific errors (bits 8+). */ +#define HWKM_IMPORT_ERR_UNWRAP_FAILED BIT(8) +#define HWKM_IMPORT_ERR_KWK_DOUBLE_KEY_ODD_SLOT BIT(9) +#define HWKM_IMPORT_ERR_KWK_SIZE_ERROR BIT(10) +#define HWKM_IMPORT_ERR_DKS_SIZE_ERROR BIT(11) +#define HWKM_IMPORT_ERR_DKS_OUT_OF_RANGE BIT(12) +#define HWKM_IMPORT_ERR_KWK_SLOT_OUT_OF_RANGE BIT(13) +#define HWKM_IMPORT_ERR_KT_PARITY BIT(14) +#define HWKM_IMPORT_ERR_SECURITY_LEVEL_ERROR BIT(15) +#define HWKM_IMPORT_ERR_INVALID_SECURITY_LEVEL BIT(16) +#define HWKM_IMPORT_ERR_INVALID_KEY_TYPE BIT(17) +#define HWKM_IMPORT_ERR_INVALID_ALG_ALLOWED BIT(18) +#define HWKM_IMPORT_ERR_CONSISTENCY_CHECK_FAILED BIT(19) +#define HWKM_IMPORT_ERR_INVALID_TPKEY_ATTRIB BIT(20) +#define HWKM_IMPORT_ERR_INVALID_KSK_ATTRIB BIT(21) +#define HWKM_IMPORT_ERR_INVALID_KWK_ATTRIB BIT(22) +#define HWKM_IMPORT_ERR_INVALID_KDF_DEPTH BIT(23) +#define HWKM_IMPORT_ERR_DECRYPT_NOT_ALLOWED BIT(24) +#define HWKM_IMPORT_ERR_UNEXPECTED_ALG_ALLOWED BIT(25) +#define HWKM_IMPORT_ERR_UNEXPECTED_KEY_TYPE BIT(26) +#define HWKM_IMPORT_ERR_KWK_SLOT_EMPTY BIT(27) +#define HWKM_IMPORT_ERR_DKS_SLOT_NOT_EMPTY BIT(28) +#define HWKM_IMPORT_ERR_DKS_ACCESS_VIOLATION BIT(29) +#define HWKM_IMPORT_ERR_KWK_ACCESS_VIOLATION BIT(30) + +/* KEY_SLOT_CLEAR specific errors (bits 8+). */ +#define HWKM_CLEAR_ERR_KEYSLOT_ID_NOT_ACCESSIBLE BIT(8) +#define HWKM_CLEAR_ERR_KEYSLOT_ID_OUT_OF_RANGE BIT(9) +#define HWKM_CLEAR_ERR_KEY_SIZE BIT(10) +#define HWKM_CLEAR_ERR_DOUBLE_KEY_DKS_SLOT_NOT_EVEN BIT(11) +#define HWKM_CLEAR_ERR_DKS_SLOT_EMPTY BIT(12) +#define HWKM_CLEAR_ERR_ACCESS_VIOLATION BIT(13) + +/* SET_TPKEY specific errors (bits 8+). */ +#define HWKM_TPKEY_ERR_KT_PARITY BIT(8) +#define HWKM_TPKEY_ERR_DOUBLE_KEY_SLOT_NOT_EVEN BIT(9) +#define HWKM_TPKEY_ERR_WRONG_KEY_SIZE BIT(10) +#define HWKM_TPKEY_ERR_KEYSLOT_ID_OUT_OF_RANGE BIT(11) +#define HWKM_TPKEY_ERR_UNEXPECTED_KEY_TYPE BIT(12) +#define HWKM_TPKEY_ERR_SKS_SLOT_EMPTY BIT(13) +#define HWKM_TPKEY_ERR_ACCESS_VIOLATION BIT(14) + +const char *hwkm_err2str(uint32_t status); + +#endif /* __HWKM_ERRNO_H__ */ diff --git a/core/drivers/crypto/qcom/include/hwkm_regs.h b/core/drivers/crypto/qcom/include/hwkm_regs.h new file mode 100644 index 000000000..297da7786 --- /dev/null +++ b/core/drivers/crypto/qcom/include/hwkm_regs.h @@ -0,0 +1,161 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. + * + * HWKM register offsets and field masks. + */ + +#ifndef __HWKM_REGS_H__ +#define __HWKM_REGS_H__ + +#include /* BIT() and GENMASK_32(). */ + +/* SHARED_IPCAT_VERSION (HWKM_REGS_BASE + 0x0000U). */ +/* IP Catalog version: [31:24] major, [23:16] minor, [15:0] step. */ +#define HWKM_SHARED_IPCAT_VERSION 0x0000U + +/* SHARED_KEY_POLICY_VERSION (HWKM_REGS_BASE + 0x0004U). */ +/* Key-policy format version supported by this instance. */ +#define HWKM_SHARED_KEY_POLICY_VERSION 0x0004U + +/* SHARED_KEYTABLE_SIZE (HWKM_REGS_BASE + 0x000cU). */ +/* Number of key slots in this instance's key table. */ +#define HWKM_SHARED_KEYTABLE_SIZE 0x000cU + +/* TZ_KM_CTL (HWKM_REGS_BASE + 0x1000U). */ +#define HWKM_TZ_KM_CTL 0x1000U +/* Bit 0 - enable CRC validation on every command packet. */ +#define HWKM_TZ_KM_CTL_CRC_CHECK_EN_SHIFT 0U +#define HWKM_TZ_KM_CTL_CRC_CHECK_EN \ + BIT(HWKM_TZ_KM_CTL_CRC_CHECK_EN_SHIFT) + +/* TZ_KM_STATUS (HWKM_REGS_BASE + 0x1004U). */ +#define HWKM_TZ_KM_STATUS 0x1004U +/* + * Hardware BIST detected a fault in the key table or crypto logic. + * If set, the instance must not be used; the driver treats this as + * a fatal error. + */ +#define HWKM_TZ_KM_STATUS_BIST_ERROR_SHIFT 0xfU +#define HWKM_TZ_KM_STATUS_BIST_ERROR \ + BIT(HWKM_TZ_KM_STATUS_BIST_ERROR_SHIFT) +/* + * Internal crypto-library self-test failed. Treated identically to + * BIST_ERROR by the driver; either bit causes bist_failed to be set. + */ +#define HWKM_TZ_KM_STATUS_CRYPTO_LIB_BIST_ERROR_SHIFT 0xdU +#define HWKM_TZ_KM_STATUS_CRYPTO_LIB_BIST_ERROR \ + BIT(HWKM_TZ_KM_STATUS_CRYPTO_LIB_BIST_ERROR_SHIFT) + +/* TPKEY_RECEIVE_CTL (HWKM_REGS_BASE + 0x101cU). */ +#define HWKM_TZ_TPKEY_RECEIVE_CTL 0x101cU +/* Arm (1) or disarm (0) the slave for TPKEY reception. */ +#define HWKM_TZ_TPKEY_RECEIVE_CTL_EN_SHIFT 0x8U +#define HWKM_TZ_TPKEY_RECEIVE_CTL_EN \ + BIT(HWKM_TZ_TPKEY_RECEIVE_CTL_EN_SHIFT) +/* + * Destination key slot that will receive the incoming TPKEY [7:0]. + * Write CRYPTO_DEFAULT_TPKEY here before asserting EN. + */ +#define HWKM_TZ_TPKEY_RECEIVE_CTL_TPKEY_DKS GENMASK_32(7, 0) + +/* TZ_TPKEY_RECEIVE_STATUS (HWKM_REGS_BASE + 0x1020U). */ +#define HWKM_TZ_TPKEY_RECEIVE_STATUS 0x1020U +/* + * Set by hardware when the TPKEY has been written into TPKEY_DKS. + * Poll this after SET_TPKEY, before disarming the slave. + */ +#define HWKM_TZ_TPKEY_RECEIVE_STATUS_DONE_SHIFT 0x8U +#define HWKM_TZ_TPKEY_RECEIVE_STATUS_DONE \ + BIT(HWKM_TZ_TPKEY_RECEIVE_STATUS_DONE_SHIFT) +/* Slot index where the TPKEY was stored (readback of TPKEY_DKS) [7:0]. */ +#define HWKM_TZ_TPKEY_RECEIVE_STATUS_TPKEY_DKS GENMASK_32(7, 0) + +/* BANK0_KM_CTL (HWKM_REGS_BASE + 0x2000U). */ +#define HWKM_BANK0_KM_CTL 0x2000U +/* + * Enable the command FIFO for a new packet. Write 1 after clearing the + * FIFO and ESR, before writing the first command word. + */ +#define HWKM_BANK0_KM_CTL_CMD_ENABLE_SHIFT 0U +#define HWKM_BANK0_KM_CTL_CMD_ENABLE \ + BIT(HWKM_BANK0_KM_CTL_CMD_ENABLE_SHIFT) +/* + * Flush the command FIFO. Write 1 then 0 to reset. Read back: if still 1 + * after deassertion, the FIFO did not drain. + */ +#define HWKM_BANK0_KM_CTL_CMD_FIFO_CLEAR_SHIFT 0x1U +#define HWKM_BANK0_KM_CTL_CMD_FIFO_CLEAR \ + BIT(HWKM_BANK0_KM_CTL_CMD_FIFO_CLEAR_SHIFT) + +/* BANK0_KM_STATUS (HWKM_REGS_BASE + 0x2004U). */ +#define HWKM_BANK0_KM_STATUS 0x2004U +/* + * Words of data available in the response FIFO [13:9]. + * Poll > 0 before reading each response word. + */ +#define HWKM_BANK0_KM_STATUS_RSP_AVAIL_DATA GENMASK_32(13, 9) +/* + * Words of free space in the command FIFO [18:14]. + * Poll > 0 before writing each command word. + */ +#define HWKM_BANK0_KM_STATUS_CMD_AVAIL_SPACE GENMASK_32(18, 14) + +/* KM_IRQ_STATUS (HWKM_REGS_BASE + 0x2008U). */ +#define HWKM_BANK0_KM_IRQ_STATUS 0x2008U +/* + * Set by hardware when the full command has been processed and the complete + * response is in the RSP FIFO. If 0 after reading all expected response + * words, more data remains -> HWKM_ERR_RSP_OVERFLOW. + * Clear by writing 1 at the end of every successful transaction. + */ +#define HWKM_BANK0_KM_IRQ_STATUS_CMD_DONE_SHIFT 0x1U +#define HWKM_BANK0_KM_IRQ_STATUS_CMD_DONE \ + BIT(HWKM_BANK0_KM_IRQ_STATUS_CMD_DONE_SHIFT) +/* + * Response FIFO full. May be spuriously set after reset (HW errata + * QCTDD06252768). Clear unconditionally during init by writing 1. + */ +#define HWKM_BANK0_KM_IRQ_STATUS_RSP_FIFO_FULL_SHIFT 0x3U +#define HWKM_BANK0_KM_IRQ_STATUS_RSP_FIFO_FULL \ + BIT(HWKM_BANK0_KM_IRQ_STATUS_RSP_FIFO_FULL_SHIFT) + +/* + * BANK0_KM_ESR - Error status register (HWKM_REGS_BASE + 0x2010U). + * + * Each bit represents one error condition from the last command. + * Clear pattern: + * io_write32_off(HWKM_REGS_BASE, HWKM_BANK0_KM_ESR, + * io_read32_off(HWKM_REGS_BASE, HWKM_BANK0_KM_ESR)) + * Do this at the start of every transaction to acknowledge stale errors. + */ +#define HWKM_BANK0_KM_ESR 0x2010U + +/* + * BANK0_KM_CMD_FIFO - Command FIFO write port (HWKM_REGS_BASE + 0x201cU). + * + * Write one 32-bit command word per store after polling CMD_FIFO_AVAIL_SPACE. + */ +#define HWKM_BANK0_KM_CMD_FIFO 0x201cU + +/* + * BANK0_KM_RSP_FIFO - Response FIFO read port (HWKM_REGS_BASE + 0x205cU). + * + * Read one 32-bit response word per load after polling RSP_FIFO_AVAIL_DATA. + */ +#define HWKM_BANK0_KM_RSP_FIFO 0x205cU + +/* Bank-Based Access Control (BBAC) bitmaps. */ + +#define HWKM_BANK0_AC 0x6000U +#define HWKM_BANK1_AC 0x7000U +#define HWKM_BANK2_AC 0x8000U +#define HWKM_BANK3_AC 0x9000U + +#define HWKM_BANKn_AC_BBAC_0 0x0000U /* slots 0-31. */ +#define HWKM_BANKn_AC_BBAC_1 0x0004U /* slots 32-63. */ +#define HWKM_BANKn_AC_BBAC_2 0x0008U /* slots 64-95. */ +#define HWKM_BANKn_AC_BBAC_3 0x000cU /* slots 96-127. */ +#define HWKM_BANKn_AC_BBAC_4 0x0010U /* slots 128-159. */ + +#endif /* __HWKM_REGS_H__ */ diff --git a/core/drivers/crypto/qcom/sub.mk b/core/drivers/crypto/qcom/sub.mk new file mode 100644 index 000000000..741e77845 --- /dev/null +++ b/core/drivers/crypto/qcom/sub.mk @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: BSD-2-Clause +# Copyright (c) 2026, Qualcomm Technologies, Inc. + +incdirs-y += include +srcs-y += hwkm.c hwkm_huk.c hwkm_transaction.c + +# Bitmap of fuse regions whose SHA256 digest is folded into the HUK KDF input. +# Each bit corresponds to a fuse region index; set a bit to bind the HUK to +# that region's content. Defaults to 0 (no fuse regions included). +CFG_HWKM_HUK_FUSE_REGION_DIGEST ?= 0x0 + +# Mix TZ_SKDK_L2 into the HUK KDF via the MKS field. When enabled, an SKDK L3 +# key is derived from TZ_SKDK_L2 and passed as the mixing key for the UKDK L3 +# and L4 derivation steps, binding the HUK to the SKDK lineage in addition to +# the UKDK. Defaults to y. +CFG_HWKM_HUK_MIX_SKDK ?= y diff --git a/core/drivers/crypto/sub.mk b/core/drivers/crypto/sub.mk index 3457f43cc..31f1d7cc2 100644 --- a/core/drivers/crypto/sub.mk +++ b/core/drivers/crypto/sub.mk @@ -17,3 +17,5 @@ subdirs-$(CFG_AMD_ASU_SUPPORT) += asu_driver subdirs-$(CFG_HISILICON_CRYPTO_DRIVER) += hisilicon subdirs-$(CFG_IMX_ELE) += ele + +subdirs-$(CFG_QCOM_HWKM) += qcom diff --git a/core/drivers/qcom/prng/prng.c b/core/drivers/qcom/rng/qcom-csrng.c similarity index 58% rename from core/drivers/qcom/prng/prng.c rename to core/drivers/qcom/rng/qcom-csrng.c index 8f50f56b7..1b7c6a15b 100644 --- a/core/drivers/qcom/prng/prng.c +++ b/core/drivers/qcom/rng/qcom-csrng.c @@ -10,19 +10,19 @@ #include #include -#define SEC_PRNG_REG_SIZE 0x1000 +#define QCOM_RNG_REG_SIZE 0x1000 -#define SEC_PRNG_DATA_OUT 0x0 -#define SEC_PRNG_STATUS 0x4 -#define SEC_PRNG_STATUS_DATA_AVAIL_BMSK 0x1 +#define QCOM_RNG_DATA_OUT 0x0 +#define QCOM_RNG_STATUS 0x4 +#define QCOM_RNG_STATUS_DATA_AVAIL_BMSK 0x1 -#define PRNG_TIMEOUT_US 1000000 +#define QCOM_RNG_TIMEOUT_US 1000000 static struct { paddr_t pa; vaddr_t va; -} prng = { - .pa = SEC_PRNG_REG_BASE, +} rng = { + .pa = QCOM_RNG_REG_BASE, }; TEE_Result hw_get_random_bytes(void *buf, size_t len) @@ -31,22 +31,22 @@ TEE_Result hw_get_random_bytes(void *buf, size_t len) uint32_t val = 0; uint64_t to = 0; - if (!prng.va) + if (!rng.va) return TEE_ERROR_NOT_SUPPORTED; if (!out || !len) return TEE_ERROR_BAD_PARAMETERS; - to = timeout_init_us(PRNG_TIMEOUT_US); + to = timeout_init_us(QCOM_RNG_TIMEOUT_US); while (len) { - if (!(io_read32(prng.va + SEC_PRNG_STATUS) & - SEC_PRNG_STATUS_DATA_AVAIL_BMSK)) { + if (!(io_read32(rng.va + QCOM_RNG_STATUS) & + QCOM_RNG_STATUS_DATA_AVAIL_BMSK)) { if (timeout_elapsed(to)) return TEE_ERROR_BUSY; continue; } - while ((val = io_read32(prng.va + SEC_PRNG_DATA_OUT)) == 0) { + while ((val = io_read32(rng.va + QCOM_RNG_DATA_OUT)) == 0) { if (timeout_elapsed(to)) return TEE_ERROR_BUSY; } @@ -60,14 +60,14 @@ TEE_Result hw_get_random_bytes(void *buf, size_t len) return TEE_SUCCESS; } -static TEE_Result qcom_prng_init(void) +static TEE_Result qcom_csrng_init(void) { - prng.va = (vaddr_t)core_mmu_add_mapping(MEM_AREA_IO_SEC, prng.pa, - SEC_PRNG_REG_SIZE); - if (!prng.va) + rng.va = (vaddr_t)core_mmu_add_mapping(MEM_AREA_IO_SEC, rng.pa, + QCOM_RNG_REG_SIZE); + if (!rng.va) return TEE_ERROR_GENERIC; return TEE_SUCCESS; } -early_init(qcom_prng_init); +early_init(qcom_csrng_init); diff --git a/core/drivers/qcom/sub.mk b/core/drivers/qcom/sub.mk index 41d17c06c..56e7d0625 100644 --- a/core/drivers/qcom/sub.mk +++ b/core/drivers/qcom/sub.mk @@ -5,8 +5,10 @@ # srcs-$(CFG_QCOM_RAMBLUR_PIMEM_V3) += ramblur/ramblur_pimem_v3.c -srcs-$(CFG_QCOM_PRNG) += prng/prng.c +srcs-$(CFG_QCOM_CSRNG) += rng/qcom-csrng.c +srcs-$(CFG_QCOM_SEC_WDOG) += wdt/qcom-wdt.c subdirs-$(CFG_QCOM_CMD_DB) += cmd_db subdirs-$(CFG_QCOM_RPMH_CLIENT) += rpmh subdirs-$(CFG_QCOM_QFPROM) += qfprom +subdirs-$(CFG_QCOM_XPUV4) += xpu diff --git a/core/drivers/qcom/wdt/qcom-wdt.c b/core/drivers/qcom/wdt/qcom-wdt.c new file mode 100644 index 000000000..ecdc4de67 --- /dev/null +++ b/core/drivers/qcom/wdt/qcom-wdt.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define QCOM_WDT_CTL_OFFSET 0x08 +#define QCOM_WDT_BARK_TIME_OFFSET 0x10 +#define QCOM_WDT_BITE_TIME_OFFSET 0x14 +#define QCOM_WDT_RESET_REG_OFFSET 0x4 + +#define QCOM_WDT_CTL_INT_ENABLE BIT32(0) +#define QCOM_WDT_CTL_UNMASKED_INT_ENABLE BIT32(1) + +#define QCOM_WDT_RESET_REG_RESET UINT32_C(1) + +#define QCOM_WDT_BARK_TIME_RMSK 0xFFFFFU +#define QCOM_WDT_BITE_TIME_RMSK 0xFFFFFU + +#define QCOM_WDT_CLK_HZ 32000U + +#define QCOM_WDT_MS_TO_TICKS(ms) \ + ((uint32_t)(((uint64_t)(ms) * QCOM_WDT_CLK_HZ) / 1000U)) + +#define QCOM_WDT_BARK_TIME_MS 6000U +#define QCOM_WDT_BITE_TIME_MS 22000U + +register_phys_mem_pgdir(MEM_AREA_IO_SEC, QCOM_WDT_TMR_BASE, + CORE_MMU_PGDIR_SIZE); + +static vaddr_t qcom_wdt_base; + +static void qcom_wdt_enable(bool enable) +{ + uint32_t val = QCOM_WDT_CTL_UNMASKED_INT_ENABLE; + + if (enable) + val |= QCOM_WDT_CTL_INT_ENABLE; + + io_write32(qcom_wdt_base + QCOM_WDT_CTL_OFFSET, val); +} + +static void qcom_wdt_reset(void) +{ + io_write32(qcom_wdt_base + QCOM_WDT_RESET_REG_OFFSET, + QCOM_WDT_RESET_REG_RESET); +} + +static void qcom_wdt_start(void) +{ + uint32_t bark_timeout = QCOM_WDT_MS_TO_TICKS(QCOM_WDT_BARK_TIME_MS); + uint32_t bite_timeout = QCOM_WDT_MS_TO_TICKS(QCOM_WDT_BITE_TIME_MS); + + /* Timeouts have a ceiling value */ + bark_timeout = MIN(bark_timeout, (uint32_t)(QCOM_WDT_BARK_TIME_RMSK)); + bite_timeout = MIN(bite_timeout, (uint32_t)(QCOM_WDT_BITE_TIME_RMSK)); + + io_write32(qcom_wdt_base + QCOM_WDT_BARK_TIME_OFFSET, bark_timeout); + io_write32(qcom_wdt_base + QCOM_WDT_BITE_TIME_OFFSET, bite_timeout); + + qcom_wdt_enable(true); + + qcom_wdt_reset(); +} + +static enum itr_return +qcom_wdt_bark_handler(struct itr_handler *h __unused) +{ + io_write32(qcom_wdt_base + QCOM_WDT_RESET_REG_OFFSET, + QCOM_WDT_RESET_REG_RESET); + + return ITRR_HANDLED; +} + +static TEE_Result qcom_wdt_init(void) +{ + TEE_Result res = TEE_SUCCESS; + struct itr_handler *handler = NULL; + + qcom_wdt_base = (vaddr_t)phys_to_virt(QCOM_WDT_TMR_BASE, + MEM_AREA_IO_SEC, + CORE_MMU_PGDIR_SIZE); + if (!qcom_wdt_base) { + EMSG("wdt: Failed to map watchdog registers"); + return TEE_ERROR_GENERIC; + } + + qcom_wdt_enable(false); + + res = interrupt_create_handler(interrupt_get_main_chip(), + QCOM_WDT_BARK_INT_ID, + qcom_wdt_bark_handler, + 0u, 0u, &handler); + if (res != TEE_SUCCESS) { + EMSG("wdt: Failed to register bark handler (err=0x%x)", + res); + return res; + } + + qcom_wdt_start(); + + return TEE_SUCCESS; +} + +driver_init(qcom_wdt_init); + diff --git a/core/drivers/qcom/xpu/ipq52xx/xpu_target.h b/core/drivers/qcom/xpu/ipq52xx/xpu_target.h new file mode 100644 index 000000000..74c974ecb --- /dev/null +++ b/core/drivers/qcom/xpu/ipq52xx/xpu_target.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef XPU_TARGET_H +#define XPU_TARGET_H + +/* MEMNOC_SCH_MPU: resource groups 10..15 are free for runtime allocation */ +#define DDR_MPU_BASE UL(0xAC0000) +#define DDR_MPU_ADDR_OFFSET UL(0x80000000) +#define DDR_XPU_RG_START UL(10) +#define DDR_XPU_RG_COUNT UL(6) + +/* + * OCIMEM_MPU register window. The base is well below CORE_MMU_PGDIR_SIZE, + * so a pgdir-granularity mapping would round it down to address 0 and a + * small explicit range is mapped instead. Resource groups 9..10 are free + * for runtime allocation. + */ +#define IMEM_MPU_BASE UL(0x54000) +#define IMEM_MPU_MAP_SIZE (2 * SMALL_PAGE_SIZE) +#define IMEM_XPU_RG_START UL(9) +#define IMEM_XPU_RG_COUNT UL(2) + +/* AP (secure + non-secure), TME ROM, and debug may read the DIAG log */ +#define DIAG_XPU_READ_QAD \ + (QAD_ENV_APPS | QAD_TME_ROM | QAD_DEBUG) + +#endif /* XPU_TARGET_H */ diff --git a/core/drivers/qcom/xpu/ipq96xx/xpu_target.h b/core/drivers/qcom/xpu/ipq96xx/xpu_target.h new file mode 100644 index 000000000..298931165 --- /dev/null +++ b/core/drivers/qcom/xpu/ipq96xx/xpu_target.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef XPU_TARGET_H +#define XPU_TARGET_H + +/* MACHX_LLCC_MPU: resource groups 30..39 are free for runtime allocation */ +#define DDR_MPU_BASE UL(0x4060000) +#define DDR_MPU_ADDR_OFFSET UL(0x0) +#define DDR_XPU_RG_START UL(30) +#define DDR_XPU_RG_COUNT UL(10) + +/* + * OCIMEM_MPU register window. The base is well below CORE_MMU_PGDIR_SIZE, + * so a pgdir-granularity mapping would round it down to address 0 and a + * small explicit range is mapped instead. Resource groups 6..9 are free + * for runtime allocation. + */ +#define IMEM_MPU_BASE UL(0x54000) +#define IMEM_MPU_MAP_SIZE (2 * SMALL_PAGE_SIZE) +#define IMEM_XPU_RG_START UL(6) +#define IMEM_XPU_RG_COUNT UL(4) + +/* + * AP (secure + non-secure), TME ROM, debug, AP boot loader, modem, and + * PRIME may read the DIAG log. + */ +#define DIAG_XPU_READ_QAD \ + (QAD_ENV_APPS | QAD_TME_ROM | QAD_DEBUG | QAD_AP_QC_BL | QAD_MSA | \ + QAD_PRIME) + +#endif /* XPU_TARGET_H */ diff --git a/core/drivers/qcom/xpu/sub.mk b/core/drivers/qcom/xpu/sub.mk new file mode 100644 index 000000000..fd8be7f20 --- /dev/null +++ b/core/drivers/qcom/xpu/sub.mk @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# + +srcs-$(CFG_QCOM_XPUV4) += xpuv4.c + +global-incdirs-y += $(PLATFORM_FLAVOR) diff --git a/core/drivers/qcom/xpu/xpuv4.c b/core/drivers/qcom/xpu/xpuv4.c new file mode 100644 index 000000000..c43301785 --- /dev/null +++ b/core/drivers/qcom/xpu/xpuv4.c @@ -0,0 +1,255 @@ +// 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 + +/* + * XPU4 register offsets. + * IDR0 reports the highest resource group (RG) index in NRG. Each RG + * occupies 0x40 bytes starting at base + 0x1000. + * IDR0 +0x0000 NRG = bits 25:16 + * RGCR1n +0x1004 bit 0 = RGE (enable) + * RGCSAR1n +0x1008 start address [63:32] + * RGCSAR0n +0x100C start address [31:0] + * RGCEAR1n +0x1010 end address [63:32] + * RGCEAR0n +0x1014 end address [31:0] + * RGRDRn +0x1018 read QAD vector + * RGWRRn +0x101C write QAD vector + * QADRGLn +0x1030 QAD lock vector (bit 31 = secure lock RGL_S) + */ +#define XPU4_IDR0(b) ((b) + 0x0u) +#define XPU4_RGCR1n(b, n) ((b) + 0x1004u + 0x40u * (n)) +#define XPU4_RGCSAR1n(b, n) ((b) + 0x1008u + 0x40u * (n)) +#define XPU4_RGCSAR0n(b, n) ((b) + 0x100Cu + 0x40u * (n)) +#define XPU4_RGCEAR1n(b, n) ((b) + 0x1010u + 0x40u * (n)) +#define XPU4_RGCEAR0n(b, n) ((b) + 0x1014u + 0x40u * (n)) +#define XPU4_RGRDRn(b, n) ((b) + 0x1018u + 0x40u * (n)) +#define XPU4_RGWRRn(b, n) ((b) + 0x101Cu + 0x40u * (n)) +#define XPU4_QADRGLn(b, n) ((b) + 0x1030u + 0x40u * (n)) + +#define XPU4_IDR0_NRG_MASK SHIFT_U32(0x3FF, 16) +#define XPU4_IDR0_NRG_SHIFT 16 + +#define XPU4_RGCR1n_RGE BIT32(0) + +/* + * struct xpu_instance - An XPU4 instance and the memory it guards + * @base: XPU4 register base (SoC physical address) + * @map_size: Size of the register window to map at @base + * @addr_off: Base the instance's address registers are relative to + * @guard_start: Start of the memory range this instance guards + * @guard_size: Size of the memory range this instance guards + * @rg_start: First resource group available for dynamic allocation + * @rg_count: Number of resource groups available from @rg_start + * + * Resource groups outside [@rg_start, @rg_start + @rg_count) belong to + * other owners such as earlier boot stages and are never allocated here. + */ +struct xpu_instance { + paddr_t base; + size_t map_size; + paddr_t addr_off; + paddr_t guard_start; + size_t guard_size; + unsigned int rg_start; + unsigned int rg_count; +}; + +static const struct xpu_instance xpu_instances[] = { + { + .base = DDR_MPU_BASE, + .map_size = CORE_MMU_PGDIR_SIZE, + .addr_off = DDR_MPU_ADDR_OFFSET, + .guard_start = DRAM0_BASE, + .guard_size = DRAM0_SIZE, + .rg_start = DDR_XPU_RG_START, + .rg_count = DDR_XPU_RG_COUNT, + }, + { + .base = IMEM_MPU_BASE, + .map_size = IMEM_MPU_MAP_SIZE, + .addr_off = IMEM_BASE, + .guard_start = IMEM_BASE, + .guard_size = IMEM_SIZE, + .rg_start = IMEM_XPU_RG_START, + .rg_count = IMEM_XPU_RG_COUNT, + }, +}; + +register_phys_mem_pgdir(MEM_AREA_IO_SEC, DDR_MPU_BASE, CORE_MMU_PGDIR_SIZE); +register_phys_mem(MEM_AREA_IO_SEC, IMEM_MPU_BASE, IMEM_MPU_MAP_SIZE); + +static const struct xpu_instance *xpu_find_instance(paddr_t start, size_t size) +{ + const struct xpu_instance *inst = NULL; + size_t i = 0; + + for (i = 0; i < ARRAY_SIZE(xpu_instances); i++) { + inst = &xpu_instances[i]; + + if (core_is_buffer_inside(start, size, inst->guard_start, + inst->guard_size)) + return inst; + } + + return NULL; +} + +static TEE_Result xpu_find_free_rg(vaddr_t base, + const struct xpu_instance *inst, + unsigned int *rg) +{ + unsigned int rg_end = inst->rg_start + inst->rg_count; + unsigned int nrg = 0; + unsigned int i = 0; + + /* IDR0 reports the highest RG index, so the count is one more */ + nrg = ((io_read32(XPU4_IDR0(base)) & XPU4_IDR0_NRG_MASK) >> + XPU4_IDR0_NRG_SHIFT) + 1; + if (rg_end > nrg) { + EMSG("XPU: RG window %u..%u exceeds %u RGs implemented", + inst->rg_start, rg_end - 1, nrg); + return TEE_ERROR_BAD_STATE; + } + + for (i = inst->rg_start; i < rg_end; i++) { + if (io_read32(XPU4_RGCR1n(base, i)) & XPU4_RGCR1n_RGE) + continue; + + *rg = i; + return TEE_SUCCESS; + } + + EMSG("XPU: no free RG in window %u..%u", inst->rg_start, rg_end - 1); + return TEE_ERROR_OUT_OF_MEMORY; +} + +static void xpu4_program_rg(vaddr_t base, unsigned int rg, paddr_t start, + paddr_t end, uint32_t read_qad, uint32_t write_qad, + uint32_t lock_qad) +{ + io_write32(XPU4_RGCSAR1n(base, rg), (uint32_t)(start >> 32)); + io_write32(XPU4_RGCSAR0n(base, rg), (uint32_t)start); + io_write32(XPU4_RGCEAR1n(base, rg), (uint32_t)(end >> 32)); + io_write32(XPU4_RGCEAR0n(base, rg), (uint32_t)end); + io_write32(XPU4_RGRDRn(base, rg), read_qad); + io_write32(XPU4_RGWRRn(base, rg), write_qad); + io_write32(XPU4_RGCR1n(base, rg), XPU4_RGCR1n_RGE); + io_write32(XPU4_QADRGLn(base, rg), lock_qad); +} + +/* + * xpu_protect_region() - Restrict access to a memory region + * @start: Region start address (must be 4KB aligned) + * @size: Region size in bytes (must be 4KB aligned, non-zero) + * @read_perm: Read QAD permission vector + * @write_perm: Write QAD permission vector + * + * Restricts [@start, @start + @size) to @read_perm/@write_perm and locks + * the configuration. The XPU instance guarding the region and a free + * resource group within it are determined from @start, so callers only + * describe the region and the access policy they need. + * + * The lock is held by whichever AP domains (secure/non-secure APPS) are + * present in @read_perm/@write_perm. + */ +static TEE_Result xpu_protect_region(paddr_t start, size_t size, + uint32_t read_perm, uint32_t write_perm) +{ + const struct xpu_instance *inst = NULL; + uint32_t lock_perm = 0; + unsigned int rg = 0; + paddr_t xpu_start = 0; + paddr_t xpu_end = 0; + vaddr_t base = 0; + TEE_Result res = TEE_ERROR_GENERIC; + + /* XPU4 requires 4KB-aligned, non-zero start and end addresses */ + if (!size || !IS_ALIGNED(start, SIZE_4K) || + !IS_ALIGNED(size, SIZE_4K)) { + EMSG("XPU: region %#" PRIxPA "+%#zx not 4KB aligned", + start, size); + return TEE_ERROR_BAD_PARAMETERS; + } + + inst = xpu_find_instance(start, size); + if (!inst) { + EMSG("XPU: no instance guards region %#" PRIxPA "+%#zx", + start, size); + return TEE_ERROR_ITEM_NOT_FOUND; + } + + /* The XPU stores addresses relative to its memory-region base */ + if (SUB_OVERFLOW(start, inst->addr_off, &xpu_start) || + ADD_OVERFLOW(xpu_start, size, &xpu_end)) { + EMSG("XPU: region %#" PRIxPA "+%#zx invalid for offset %#" + PRIxPA, start, size, inst->addr_off); + return TEE_ERROR_BAD_PARAMETERS; + } + + lock_perm = (read_perm | write_perm) & QAD_ENV_APPS; + if (!lock_perm) { + EMSG("XPU: no AP domain in region perms, refusing to program"); + return TEE_ERROR_BAD_PARAMETERS; + } + + base = io_pa_or_va_secure(&(struct io_pa_va){ .pa = inst->base }, + inst->map_size); + + res = xpu_find_free_rg(base, inst, &rg); + if (res) + return res; + + xpu4_program_rg(base, rg, xpu_start, xpu_end, + read_perm, write_perm, lock_perm); + + return TEE_SUCCESS; +} + +static TEE_Result xpu_init(void) +{ + TEE_Result diag_res = TEE_SUCCESS; + TEE_Result ddr_res = TEE_SUCCESS; + + static_assert(IS_ALIGNED(CFG_TZDRAM_START, SIZE_4K)); + static_assert(IS_ALIGNED(CFG_TZDRAM_SIZE, SIZE_4K)); + + ddr_res = xpu_protect_region(CFG_TZDRAM_START, CFG_TZDRAM_SIZE, + QAD_APPS_SEC, QAD_APPS_SEC); + if (ddr_res) + EMSG("XPU: TZDRAM protection failed: %#" PRIx32, ddr_res); + + /* + * The DIAG log buffer only holds data when the log itself is built + * in, so protecting it otherwise would lock a resource group for a + * region nothing uses. + */ + if (IS_ENABLED(CFG_QCOM_DIAG_LOG)) { + diag_res = xpu_protect_region(DIAG_BASE, DIAG_SIZE, + DIAG_XPU_READ_QAD, QAD_APPS_SEC); + if (diag_res) + EMSG("XPU: DIAG log protection failed: %#" PRIx32, + diag_res); + } + + /* Protect what is left even if one region could not be programmed */ + if (ddr_res) + return ddr_res; + + return diag_res; +} +service_init(xpu_init); diff --git a/core/include/drivers/clk_qcom.h b/core/include/drivers/clk_qcom.h index 9baac78cb..dea2bd030 100644 --- a/core/include/drivers/clk_qcom.h +++ b/core/include/drivers/clk_qcom.h @@ -7,6 +7,7 @@ #ifndef _CLK_QCOM_H_ #define _CLK_QCOM_H_ +#include #include #include #include @@ -25,7 +26,16 @@ enum qcom_clk_group { QCOM_CLKS_WPSS, QCOM_CLKS_TURING, + QCOM_CLKS_TURING1, QCOM_CLKS_LPASS, + QCOM_CLKS_GPDSP0, + QCOM_CLKS_GPDSP1, + QCOM_CLKS_SOCCP, + QCOM_CLKS_HPASS0, + QCOM_CLKS_HPASS1, + QCOM_CLKS_HPASS2, + QCOM_CLKS_TURING2, + QCOM_CLKS_TURING3, QCOM_CLKS_MAX, }; @@ -34,10 +44,76 @@ enum qcom_clk_group { #define CBCR_BRANCH_OFF_BIT BIT(31) #define CMD_RCGR_UPDATE_BIT BIT(0) +/* Register configuration for a Lucid-EVO PLL. */ +struct qcom_lucidevo_pll_config { + uint32_t l_val; + uint32_t cal_l_val; + uint32_t alpha_val; + uint32_t pre_div; /* div-1..div-8; 0 = div-1 */ + uint32_t config_ctl; + uint32_t config_ctl_u; + uint32_t config_ctl_u1; + uint32_t user_ctl; + uint32_t user_ctl_u; + bool frac_mode_mn; /* false = alpha (default for Q6) */ +}; + +struct qcom_lucidole_pll_config { + uint32_t l_val; + uint32_t cal_l_val; + uint32_t alpha_val; + uint32_t pre_div; /* div-1..div-8; 0 = div-1 */ + uint32_t config_ctl; + uint32_t config_ctl_u; + uint32_t config_ctl_u1; + uint32_t test_ctl; + uint32_t test_ctl_u; + uint32_t test_ctl_u1; + uint32_t test_ctl_u2; + uint32_t user_ctl; + uint32_t user_ctl_u; + bool frac_mode_mn; /* false = alpha (default for Q6) */ +}; + 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); + +/* Configure, lock and enable a Lucid-EVO PLL at @pll_base; returns + * TEE_ERROR_TIMEOUT if it fails to lock. + */ +TEE_Result qcom_lucidevo_pll_enable(vaddr_t pll_base, + const struct qcom_lucidevo_pll_config *cfg); + +/* Configure, lock and enable a Lucid-OLE PLL at @pll_base; returns + * TEE_ERROR_TIMEOUT if it fails to lock. + */ +TEE_Result qcom_lucidole_pll_enable(vaddr_t pll_base, + const struct qcom_lucidole_pll_config *cfg); + +#ifdef CFG_QCOM_PAS_PTA TEE_Result qcom_clock_enable_pas(enum qcom_clk_group group); +TEE_Result qcom_clock_enable_pas_processor(enum qcom_clk_group group); +TEE_Result qcom_clock_pas_reset(enum qcom_clk_group group); +#else +static inline TEE_Result qcom_clock_enable_pas(enum qcom_clk_group group + __unused) +{ + return TEE_ERROR_NOT_SUPPORTED; +} + +static inline TEE_Result +qcom_clock_enable_pas_processor(enum qcom_clk_group group __unused) +{ + return TEE_ERROR_NOT_SUPPORTED; +} + +static inline TEE_Result qcom_clock_pas_reset(enum qcom_clk_group group + __unused) +{ + return TEE_ERROR_NOT_SUPPORTED; +} +#endif #endif /* _CLK_QCOM_H_ */ diff --git a/core/include/drivers/qcom/xpuv4.h b/core/include/drivers/qcom/xpuv4.h new file mode 100644 index 000000000..7c2a34b24 --- /dev/null +++ b/core/include/drivers/qcom/xpuv4.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef XPUV4_H +#define XPUV4_H + +#include + +/* + * QAD vector for the secure APPS domain: bit 0 = QAD_APPS identifier, + * bit 31 = secure qualifier. + */ +#define QAD_APPS_SEC (BIT32(0) | BIT32(31)) + +/* + * AP execution-environment QAD vector (secure + non-secure APPS). + */ +#define QAD_ENV_APPS (BIT32(0) | BIT32(31) | BIT32(30)) + +/* + * Per-master QAD identifiers, so read/write policy vectors in platform + * config spell out which masters have access instead of an opaque hex + * literal. Identifiers and their meaning come from the XPU4 static QAD + * assignment; not every identifier is wired to a real master on every SoC. + */ +#define QAD_TME_ROM BIT32(1) +#define QAD_DEBUG BIT32(3) +#define QAD_AP_QC_BL BIT32(4) +#define QAD_MSA BIT32(5) +#define QAD_PRIME BIT32(6) + +#endif /* XPUV4_H */ diff --git a/core/include/io.h b/core/include/io.h index 2b4717661..b3d5fa10d 100644 --- a/core/include/io.h +++ b/core/include/io.h @@ -62,6 +62,42 @@ static inline uint64_t io_read64(vaddr_t addr) return *(volatile uint64_t *)addr; } +static inline void io_write32_off(vaddr_t base, size_t offset, uint32_t val) +{ + io_write32(base + offset, val); +} + +static inline uint32_t io_read32_off(vaddr_t base, size_t offset) +{ + return io_read32(base + offset); +} + +/* + * Get and set a field within a 32-bit register at @base + @offset. + * + * @mask selects the field's bits in the register. The shift is derived from + * @mask's lowest set bit, so callers only need to supply the mask. + * + * io_read32_off_field() returns the field's current value, right-justified. + * io_write32_off_field() updates only the bits covered by @mask, leaving the + * rest of the register unchanged. + */ +static inline uint32_t io_read32_off_field(vaddr_t base, size_t offset, + uint32_t mask) +{ + return (io_read32_off(base, offset) & mask) / (mask & ~(mask - 1)); +} + +static inline void io_write32_off_field(vaddr_t base, size_t offset, + uint32_t mask, uint32_t val) +{ + uint32_t regval = io_read32_off(base, offset); + + regval &= ~mask; + regval |= (val * (mask & ~(mask - 1))) & mask; + io_write32_off(base, offset, regval); +} + static inline void io_mask8(vaddr_t addr, uint8_t val, uint8_t mask) { io_write8(addr, (io_read8(addr) & ~mask) | (val & mask)); diff --git a/core/mm/core_mmu.c b/core/mm/core_mmu.c index 7be812709..f24f8778a 100644 --- a/core/mm/core_mmu.c +++ b/core/mm/core_mmu.c @@ -2458,8 +2458,24 @@ void *core_mmu_add_mapping(enum teecore_memtypes type, paddr_t addr, size_t len) if (map->size < l) return NULL; - if (static_memory_map.count >= static_memory_map.alloc_count) - return NULL; + if (static_memory_map.count >= static_memory_map.alloc_count) { + /* + * Out of pre-allocated entries (the map is frozen at count + 5 + * after boot), so grow it through the registered realloc hook, + * as grow_mem_map() does for every other add path. + */ + if (!memory_map_realloc_func) + return NULL; + memory_map_realloc_func(mem_map); + + /* + * The realloc may have moved the map array, so the RES_VASPACE + * entry resolved above is now stale - look it up again. + */ + map = find_map_by_type(MEM_AREA_RES_VASPACE); + if (!map) + return NULL; + } mem_map->map[mem_map->count] = (struct tee_mmap_region){ .va = map->va, diff --git a/core/pta/qcom/pas/compute.c b/core/pta/qcom/pas/compute.c deleted file mode 100644 index 608cee5a7..000000000 --- a/core/pta/qcom/pas/compute.c +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: BSD-2-Clause -/* - * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. - */ - -#include -#include -#include -#include - -#include "dsp_boot.h" -#include "pas.h" - -TEE_Result compute_fw_start(struct qcom_pas_data *data) -{ - return dsp_fw_start(data, dsp_fw_get_boot_regs(data->pas_id)); -} - -TEE_Result compute_fw_shutdown(struct qcom_pas_data *data __unused) -{ - return TEE_ERROR_NOT_IMPLEMENTED; -} diff --git a/core/pta/qcom/pas/lpass.c b/core/pta/qcom/pas/lpass.c deleted file mode 100644 index c66a6e1ce..000000000 --- a/core/pta/qcom/pas/lpass.c +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: BSD-2-Clause -/* - * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. - */ - -#include -#include -#include -#include - -#include "dsp_boot.h" -#include "pas.h" - -TEE_Result lpass_fw_start(struct qcom_pas_data *data) -{ - const struct dsp_fw_boot_regs *reg = dsp_fw_get_boot_regs(data->pas_id); - vaddr_t base = io_pa_or_va(&data->base, data->size); - - if (!reg || !base) - return TEE_ERROR_GENERIC; - - io_write32(base + reg->lpass.efuse_evb_sel, 0); - - return dsp_fw_start(data, reg); -} - -TEE_Result lpass_fw_shutdown(struct qcom_pas_data *data __unused) -{ - return TEE_ERROR_NOT_IMPLEMENTED; -} diff --git a/core/pta/qcom/pas/pas.c b/core/pta/qcom/pas/pas.c deleted file mode 100644 index 4c9af308d..000000000 --- a/core/pta/qcom/pas/pas.c +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-License-Identifier: BSD-2-Clause -/* - * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. - */ - -#include -#include - -#include "pas.h" -#include "pas_resources.h" - -static TEE_Result get_mem_rsc(struct resource_table *rt, size_t *rt_size, - struct resource_table *table, - const struct fw_rsc_hdr *mem_hdr, - const struct fw_rsc_devmem *mem_res, - size_t table_header_size, size_t table_size) -{ - uint8_t *p = (uint8_t *)rt; - uint32_t offset = 0; - - if (rt_size && *rt_size < table_size) { - *rt_size = table_size; - - return TEE_SUCCESS; - } - - if (!rt) - return TEE_ERROR_BAD_PARAMETERS; - - offset = table_header_size; - - for (size_t i = 0; i < table->num; i++, mem_res++) { - table->offset[i] = offset; - memcpy(p + offset, mem_hdr, sizeof(*mem_hdr)); - offset += sizeof(*mem_hdr); - memcpy(p + offset, mem_res, sizeof(*mem_res)); - offset += sizeof(*mem_res); - } - - memcpy(p, table, table_header_size); - - return TEE_SUCCESS; -} - -TEE_Result pas_get_resource_table(uint32_t pas_id, struct resource_table *rt, - size_t *rt_size) -{ - switch (pas_id) { - case PAS_ID_WPSS: - return get_mem_rsc(rt, rt_size, &wpss_rt, &wpss_mem_hdr, - wpss_mem_res, - WPSS_RESOURCE_TABLE_HEADER_SIZE, - WPSS_RESOURCE_TABLE_SIZE); - case PAS_ID_TURING: - return get_mem_rsc(rt, rt_size, &turing_rt, &turing_mem_hdr, - turing_mem_res, - TURING_RESOURCE_TABLE_HEADER_SIZE, - TURING_RESOURCE_TABLE_SIZE); - case PAS_ID_QDSP6: - return get_mem_rsc(rt, rt_size, &lpass_rt, &lpass_mem_hdr, - lpass_mem_res, - LPASS_RESOURCE_TABLE_HEADER_SIZE, - LPASS_RESOURCE_TABLE_SIZE); - default: - return TEE_ERROR_NOT_SUPPORTED; - } -} diff --git a/core/pta/qcom/pas/pas.h b/core/pta/qcom/pas/pas.h deleted file mode 100644 index 5cd07d310..000000000 --- a/core/pta/qcom/pas/pas.h +++ /dev/null @@ -1,95 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (c) 2025, Linaro Limited - * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. - */ - -#ifndef _PAS_H_ -#define _PAS_H_ - -#include -#include -#include -#include - -#define PAS_ID_QDSP6 1 -#define PAS_ID_WPSS 6 -#define PAS_ID_VENUS 9 -#define PAS_ID_TURING 18 - -#define DEFINE_RESOURCE_TABLE(prefix, num_res) \ - enum { \ - prefix##_NUM_MEM_RESOURCES = (num_res), \ - prefix##_SIZE_MEM_RES = \ - (sizeof(struct fw_rsc_hdr) + \ - sizeof(struct fw_rsc_devmem)), \ - prefix##_RESOURCE_TABLE_HEADER_SIZE = \ - (sizeof(struct resource_table) + \ - (prefix##_NUM_MEM_RESOURCES * \ - sizeof(uint32_t))), \ - prefix##_RESOURCE_TABLE_SIZE = \ - (prefix##_RESOURCE_TABLE_HEADER_SIZE + \ - (prefix##_NUM_MEM_RESOURCES * \ - prefix##_SIZE_MEM_RES)), \ - } - -struct resource_table { - uint32_t ver; - uint32_t num; - uint32_t reserved[2]; - uint32_t offset[]; -} __packed; - -struct fw_rsc_hdr { - uint32_t type; - uint8_t data[]; -} __packed; - -enum fw_resource_type { - RSC_CARVEOUT = 0, - RSC_DEVMEM = 1, - RSC_TRACE = 2, - RSC_VDEV = 3, - RSC_LAST = 4, - RSC_VENDOR_START = 128, - RSC_VENDOR_END = 512, -}; - -#define IOMMU_READ BIT(0) -#define IOMMU_WRITE BIT(1) - -struct fw_rsc_devmem { - uint32_t da; - uint32_t pa; - uint32_t len; - uint32_t flags; - uint32_t reserved; - uint8_t name[32]; -} __packed; - -struct qcom_pas_data { - uint32_t pas_id; - struct io_pa_va base; - size_t size; - paddr_t fw_base; - size_t fw_size; - enum qcom_clk_group clk_group; -}; - -TEE_Result pas_get_resource_table(uint32_t pas_id, struct resource_table *rt, - size_t *rt_size); - -TEE_Result wpss_fw_start(struct qcom_pas_data *data); -TEE_Result wpss_fw_shutdown(struct qcom_pas_data *data); - -TEE_Result compute_fw_start(struct qcom_pas_data *data); -TEE_Result compute_fw_shutdown(struct qcom_pas_data *data); - -TEE_Result lpass_fw_start(struct qcom_pas_data *data); -TEE_Result lpass_fw_shutdown(struct qcom_pas_data *data); - -TEE_Result venus_fw_start(struct qcom_pas_data *data); -TEE_Result venus_fw_shutdown(struct qcom_pas_data *data); -TEE_Result venus_fw_set_state(struct qcom_pas_data *data, bool power_on); - -#endif /* _PAS_H_ */ diff --git a/core/pta/qcom/pas/pas_auth_core.c b/core/pta/qcom/pas/pas_auth_core.c new file mode 100644 index 000000000..02c3ac1db --- /dev/null +++ b/core/pta/qcom/pas/pas_auth_core.c @@ -0,0 +1,403 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pas_subsys.h" + +#define MI_PBT_PAGE_MODE_MASK 0x00100000 +#define MI_PBT_PAGE_MODE_SHIFT 20 +#define MI_PBT_ACCESS_TYPE_MASK 0x00E00000 +#define MI_PBT_ACCESS_TYPE_SHIFT 21 +#define MI_PBT_SEGMENT_TYPE_MASK 0x07000000 +#define MI_PBT_SEGMENT_TYPE_SHIFT 24 + +#define MI_PBT_NON_PAGED_SEGMENT 0x0 +#define MI_PBT_HASH_SEGMENT 0x2 +#define MI_PBT_NOTUSED_SEGMENT 0x3 +#define MI_PBT_SHARED_SEGMENT 0x4 + +#define MI_PBT_PAGE_MODE(x) \ + (((x) & MI_PBT_PAGE_MODE_MASK) >> MI_PBT_PAGE_MODE_SHIFT) +#define MI_PBT_ACCESS_TYPE(x) \ + (((x) & MI_PBT_ACCESS_TYPE_MASK) >> MI_PBT_ACCESS_TYPE_SHIFT) +#define MI_PBT_SEGMENT_TYPE(x) \ + (((x) & MI_PBT_SEGMENT_TYPE_MASK) >> MI_PBT_SEGMENT_TYPE_SHIFT) + +struct elf_info { + size_t elf_hdr_len; + size_t phdr_offset; + size_t phdr_entry_len; + size_t phdr_count; + uint64_t entry; + bool is_64; +}; + +struct phdr_info { + uint64_t paddr; + size_t file_len; + size_t mem_len; + uint32_t type; + uint32_t flags; +}; + +#define PARSE_EHDR(e_info, ehdr, _is_64) do { \ + (e_info)->is_64 = (_is_64); \ + (e_info)->elf_hdr_len = (ehdr)->e_ehsize; \ + (e_info)->phdr_offset = (ehdr)->e_phoff; \ + (e_info)->phdr_entry_len = (ehdr)->e_phentsize; \ + (e_info)->phdr_count = (ehdr)->e_phnum; \ + (e_info)->entry = (ehdr)->e_entry; \ +} while (0) + +#define PARSE_PHDR(p_info, phdr) do { \ + (p_info)->paddr = (phdr)->p_paddr; \ + (p_info)->file_len = (phdr)->p_filesz; \ + (p_info)->mem_len = (phdr)->p_memsz; \ + (p_info)->type = (phdr)->p_type; \ + (p_info)->flags = (phdr)->p_flags; \ +} while (0) + +static bool check_range(uint64_t off, uint64_t len, uint64_t total) +{ + uint64_t end = 0; + + if (ADD_OVERFLOW(off, len, &end)) + return false; + + return end <= total; +} + +static bool is_hashed(uint32_t p_type, uint32_t p_flags) +{ + if (p_type != PT_LOAD) + return false; + + return MI_PBT_PAGE_MODE(p_flags) == MI_PBT_NON_PAGED_SEGMENT && + MI_PBT_SEGMENT_TYPE(p_flags) != MI_PBT_HASH_SEGMENT && + MI_PBT_ACCESS_TYPE(p_flags) != MI_PBT_NOTUSED_SEGMENT && + MI_PBT_ACCESS_TYPE(p_flags) != MI_PBT_SHARED_SEGMENT; +} + +static TEE_Result parse_elf(const uint8_t *fw, size_t fw_size, + struct elf_info *e_info) +{ + const unsigned char *ident = fw; + + if (fw_size < EI_NIDENT) + return TEE_ERROR_BAD_FORMAT; + + if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 || + ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3) + return TEE_ERROR_BAD_FORMAT; + + switch (ident[EI_CLASS]) { + case ELFCLASS64: { + const Elf64_Ehdr *ehdr = (const void *)fw; + + if (fw_size < sizeof(*ehdr)) + return TEE_ERROR_BAD_FORMAT; + + PARSE_EHDR(e_info, ehdr, true); + if (e_info->elf_hdr_len < sizeof(*ehdr) || + e_info->phdr_entry_len < sizeof(Elf64_Phdr)) + return TEE_ERROR_BAD_FORMAT; + break; + } + case ELFCLASS32: { + const Elf32_Ehdr *ehdr = (const void *)fw; + + if (fw_size < sizeof(*ehdr)) + return TEE_ERROR_BAD_FORMAT; + + PARSE_EHDR(e_info, ehdr, false); + if (e_info->elf_hdr_len < sizeof(*ehdr) || + e_info->phdr_entry_len < sizeof(Elf32_Phdr)) + return TEE_ERROR_BAD_FORMAT; + break; + } + default: + return TEE_ERROR_BAD_FORMAT; + } + + return TEE_SUCCESS; +} + +static void get_phdr(const uint8_t *fw, const struct elf_info *e_info, + size_t idx, struct phdr_info *p_info) +{ + const uint8_t *p = fw + e_info->phdr_offset + + idx * e_info->phdr_entry_len; + + if (e_info->is_64) { + const Elf64_Phdr *phdr = (const void *)p; + + PARSE_PHDR(p_info, phdr); + } else { + const Elf32_Phdr *phdr = (const void *)p; + + PARSE_PHDR(p_info, phdr); + } +} + +static TEE_Result image_range(const uint8_t *fw, const struct elf_info *e_info, + uint64_t *base, uint64_t *end) +{ + uint64_t min_paddr = UINT64_MAX; + struct phdr_info p_info = { }; + uint64_t seg_end = 0; + uint64_t max_end = 0; + size_t i = 0; + + for (i = 0; i < e_info->phdr_count; i++) { + get_phdr(fw, e_info, i, &p_info); + + if (!is_hashed(p_info.type, p_info.flags)) + continue; + + if (ADD_OVERFLOW(p_info.paddr, p_info.mem_len, &seg_end)) + return TEE_ERROR_BAD_FORMAT; + + if (p_info.paddr < min_paddr) + min_paddr = p_info.paddr; + if (seg_end > max_end) + max_end = seg_end; + } + + if (min_paddr == UINT64_MAX) + return TEE_ERROR_BAD_FORMAT; + + *base = ROUNDDOWN(min_paddr, SMALL_PAGE_SIZE); + if (ROUNDUP_OVERFLOW(max_end, SMALL_PAGE_SIZE, end)) + return TEE_ERROR_BAD_FORMAT; + + return TEE_SUCCESS; +} + +static TEE_Result hash_verify(uint32_t algo, const uint8_t *data, + size_t data_len, const uint8_t *expected, + size_t hash_len) +{ + uint8_t digest[PAS_AUTH_CORE_MAX_HASH_SIZE] = { }; + TEE_Result res = TEE_ERROR_GENERIC; + + if (hash_len > sizeof(digest)) + return TEE_ERROR_BAD_PARAMETERS; + + res = tee_hash_createdigest(algo, data, data_len, digest, hash_len); + if (res) + return res; + + if (consttime_memcmp(digest, expected, hash_len)) + res = TEE_ERROR_SECURITY; + else + res = TEE_SUCCESS; + + memzero_explicit(digest, sizeof(digest)); + + return res; +} + +static TEE_Result verify_elf_header(const struct pas_auth_core_ctx *ctx, + const struct elf_info *e_info) +{ + size_t hdr_len = 0; + + if (MUL_OVERFLOW(e_info->phdr_entry_len, e_info->phdr_count, &hdr_len)) + return TEE_ERROR_BAD_FORMAT; + + if (ADD_OVERFLOW(hdr_len, e_info->elf_hdr_len, &hdr_len)) + return TEE_ERROR_BAD_FORMAT; + + if (hdr_len > ctx->metadata_len) + return TEE_ERROR_BAD_FORMAT; + + return hash_verify(ctx->hash_algo, ctx->metadata, hdr_len, + ctx->hash_table, ctx->hash_len); +} + +TEE_Result pas_auth_core_verify_segments(const struct pas_auth_core_ctx *ctx) +{ + TEE_Result res = TEE_ERROR_GENERIC; + const uint8_t *expected = NULL; + struct phdr_info p_info = { }; + struct elf_info e_info = { }; + size_t phdr_table_len = 0; + uint64_t image_end = 0; + uint64_t offset = 0; + size_t verified = 0; + uint64_t base = 0; + size_t i = 0; + + if (!ctx || !ctx->hash_table || !ctx->fw || !ctx->metadata) + return TEE_ERROR_BAD_PARAMETERS; + + if (!ctx->hash_len || ctx->hash_len > PAS_AUTH_CORE_MAX_HASH_SIZE) + return TEE_ERROR_BAD_PARAMETERS; + + res = parse_elf(ctx->metadata, ctx->metadata_len, &e_info); + if (res) + return res; + + if (MUL_OVERFLOW(e_info.phdr_entry_len, e_info.phdr_count, + &phdr_table_len) || + !check_range(e_info.phdr_offset, phdr_table_len, ctx->metadata_len)) + return TEE_ERROR_BAD_FORMAT; + + if (ctx->num_entries != e_info.phdr_count) + return TEE_ERROR_SECURITY; + + res = verify_elf_header(ctx, &e_info); + if (res) { + EMSG("PAS auth: ELF header hash mismatch"); + return res; + } + + res = image_range(ctx->metadata, &e_info, &base, &image_end); + if (res) + return res; + + if (e_info.entry < base || e_info.entry > image_end) { + EMSG("PAS auth: entry point outside image"); + return TEE_ERROR_BAD_FORMAT; + } + + if (image_end - base > ctx->fw_size) { + EMSG("PAS auth: image larger than carveout"); + return TEE_ERROR_BAD_FORMAT; + } + + for (i = 0; i < e_info.phdr_count; i++) { + get_phdr(ctx->metadata, &e_info, i, &p_info); + + if (!is_hashed(p_info.type, p_info.flags) || !p_info.file_len) + continue; + + if (p_info.paddr < base) + return TEE_ERROR_BAD_FORMAT; + offset = p_info.paddr - base; + + if (!check_range(offset, p_info.file_len, ctx->fw_size)) + return TEE_ERROR_BAD_FORMAT; + + if (p_info.mem_len < p_info.file_len) + return TEE_ERROR_BAD_FORMAT; + if (!check_range(offset, p_info.mem_len, ctx->fw_size)) + return TEE_ERROR_BAD_FORMAT; + if (p_info.mem_len > p_info.file_len) { + void *zi = ctx->fw + offset + p_info.file_len; + size_t zi_len = p_info.mem_len - p_info.file_len; + + memset(zi, 0, zi_len); + + /* + * The REE maps this carveout uncached and the DSP + * fetches it outside the CPU's coherency domain, so the + * zeros must reach DDR before reset release. + */ + dcache_cleaninv_range(zi, zi_len); + } + + expected = ctx->hash_table + i * ctx->hash_len; + + res = hash_verify(ctx->hash_algo, ctx->fw + offset, + p_info.file_len, expected, ctx->hash_len); + if (res) { + EMSG("PAS auth: segment %zu hash mismatch", i); + return res; + } + + verified++; + } + + if (!verified) { + EMSG("PAS auth: no loadable segments were hashed"); + return TEE_ERROR_SECURITY; + } + + return TEE_SUCCESS; +} + +TEE_Result pas_platform_verify_image(uint32_t pas_id, + const struct pas_fw_region *fw, + const struct pas_metadata *metadata, + const struct pas_hash_table *hash) +{ + struct qcom_pas_subsys *subsys = pas_lookup(pas_id); + struct pas_auth_core_ctx ctx = { }; + TEE_Result res = TEE_ERROR_GENERIC; + struct qcom_pas_data *data = NULL; + uint32_t fw_size = 0; + void *fw_va = NULL; + + if (!subsys) + return TEE_ERROR_NOT_SUPPORTED; + + if (!metadata->data || !metadata->size || !hash->table || + !hash->len || !fw->size) + return TEE_ERROR_BAD_PARAMETERS; + + if (!hash->entry_size || hash->len % hash->entry_size) + return TEE_ERROR_BAD_PARAMETERS; + + data = &subsys->data; + + if (!data->fw_base || !data->fw_size) { + EMSG("PAS auth: no MEM_SETUP for pas_id=%#"PRIx32, pas_id); + return TEE_ERROR_BAD_STATE; + } + + if (fw->base != data->fw_base) { + EMSG("PAS auth: base %#"PRIxPA" != MEM_SETUP %#"PRIxPA, + fw->base, data->fw_base); + return TEE_ERROR_SECURITY; + } + fw_size = data->fw_size; + + fw_va = core_mmu_add_mapping(MEM_AREA_RAM_NSEC, fw->base, fw_size); + if (!fw_va) { + EMSG("PAS auth: can't map carveout %#"PRIxPA"/%#"PRIx32, + fw->base, fw_size); + return TEE_ERROR_GENERIC; + } + + switch (hash->entry_size) { + case TEE_SHA256_HASH_SIZE: + ctx.hash_algo = TEE_ALG_SHA256; + break; + case TEE_SHA384_HASH_SIZE: + ctx.hash_algo = TEE_ALG_SHA384; + break; + default: + res = TEE_ERROR_NOT_SUPPORTED; + goto out; + } + + ctx.hash_len = hash->entry_size; + ctx.hash_table = hash->table; + ctx.num_entries = hash->len / hash->entry_size; + ctx.metadata = metadata->data; + ctx.metadata_len = metadata->size; + ctx.fw = fw_va; + ctx.fw_size = fw_size; + + res = pas_auth_core_verify_segments(&ctx); +out: + if (core_mmu_remove_mapping(MEM_AREA_RAM_NSEC, fw_va, fw_size)) + EMSG("PAS auth: failed to unmap carveout"); + + return res; +} diff --git a/core/pta/qcom/pas/pas_auth_core.h b/core/pta/qcom/pas/pas_auth_core.h new file mode 100644 index 000000000..6f8440d38 --- /dev/null +++ b/core/pta/qcom/pas/pas_auth_core.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __PAS_AUTH_CORE_H +#define __PAS_AUTH_CORE_H + +#include +#include +#include +#include + +#define PAS_AUTH_CORE_MAX_HASH_SIZE 48U + +struct pas_auth_core_ctx { + uint32_t hash_algo; + size_t hash_len; + const uint8_t *hash_table; + uint32_t num_entries; + const uint8_t *metadata; + size_t metadata_len; + uint8_t *fw; + size_t fw_size; +}; + +TEE_Result pas_auth_core_verify_segments(const struct pas_auth_core_ctx *ctx); + +#endif /* __PAS_AUTH_CORE_H */ diff --git a/core/pta/qcom/pas/pas_core.c b/core/pta/qcom/pas/pas_core.c new file mode 100644 index 000000000..43073d824 --- /dev/null +++ b/core/pta/qcom/pas/pas_core.c @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include + +#include "pas_subsys.h" + +struct qcom_pas_subsys *pas_lookup(uint32_t pas_id) +{ + struct qcom_pas_subsys *subsys = NULL; + size_t count = 0; + + subsys = qcom_pas_platform_subsys(&count); + for (size_t i = 0; i < count; i++) { + if (subsys[i].data.pas_id == pas_id) + return &subsys[i]; + } + + return NULL; +} + +TEE_Result pas_platform_is_supported(uint32_t pas_id) +{ + if (!pas_lookup(pas_id)) + return TEE_ERROR_NOT_SUPPORTED; + + return TEE_SUCCESS; +} + +TEE_Result pas_platform_capabilities(uint32_t pas_id __unused) +{ + return TEE_SUCCESS; +} + +TEE_Result pas_platform_init_image(uint32_t pas_id) +{ + if (!pas_lookup(pas_id)) + return TEE_ERROR_NOT_SUPPORTED; + + return TEE_SUCCESS; +} + +TEE_Result pas_platform_mem_setup(uint32_t pas_id, uint32_t fw_size, + uint32_t fw_base_low, uint32_t fw_base_high) +{ + struct qcom_pas_subsys *subsys = pas_lookup(pas_id); + struct qcom_pas_data *data = NULL; + + if (!subsys) + return TEE_ERROR_NOT_SUPPORTED; + + data = &subsys->data; + data->fw_size = fw_size; + data->fw_base = fw_base_low; + data->fw_base |= SHIFT_U64(fw_base_high, 32); + + /* Map the controller */ + if (!data->base.va) { + data->base.va = (vaddr_t)core_mmu_add_mapping(MEM_AREA_IO_NSEC, + data->base.pa, + data->size); + if (!data->base.va) + return TEE_ERROR_GENERIC; + } + + return TEE_SUCCESS; +} + +TEE_Result pas_platform_get_resource_table(uint32_t pas_id, + struct resource_table *rt, + size_t *size) +{ + struct qcom_pas_subsys *subsys = pas_lookup(pas_id); + + if (!subsys || !subsys->ops->get_resource_table) + return TEE_ERROR_NOT_SUPPORTED; + + return subsys->ops->get_resource_table(rt, size); +} + +TEE_Result pas_platform_set_remote_state(uint32_t pas_id, uint32_t state) +{ + struct qcom_pas_subsys *subsys = pas_lookup(pas_id); + + if (!subsys || !subsys->ops->fw_set_state) + return TEE_ERROR_NOT_IMPLEMENTED; + + return subsys->ops->fw_set_state(&subsys->data, state); +} + +TEE_Result pas_platform_auth_and_reset(uint32_t pas_id) +{ + struct qcom_pas_subsys *subsys = pas_lookup(pas_id); + TEE_Result res = TEE_ERROR_GENERIC; + struct qcom_pas_data *data = NULL; + + if (!subsys) + return TEE_ERROR_NOT_SUPPORTED; + + data = &subsys->data; + if (!data->fw_base) + return TEE_ERROR_NO_DATA; + + switch (subsys->reset_seq) { + case QCOM_PAS_RESET_CLK_FULL: + res = qcom_clock_pas_reset(data->clk_group); + if (res != TEE_SUCCESS) + return res; + + res = qcom_clock_enable(data->clk_group); + if (res != TEE_SUCCESS) + return res; + + res = subsys->ops->fw_start(data); + if (res != TEE_SUCCESS) + return res; + + return qcom_clock_enable_pas_processor(data->clk_group); + case QCOM_PAS_RESET_CLK_ENABLE: + res = qcom_clock_enable(data->clk_group); + if (res != TEE_SUCCESS) { + EMSG("Failed to enable clocks: %d", res); + return res; + } + + return subsys->ops->fw_start(data); + case QCOM_PAS_RESET_NONE: + return subsys->ops->fw_start(data); + default: + return TEE_ERROR_NOT_SUPPORTED; + } +} + +TEE_Result pas_platform_shutdown(uint32_t pas_id) +{ + struct qcom_pas_subsys *subsys = pas_lookup(pas_id); + TEE_Result res = TEE_ERROR_GENERIC; + + if (!subsys || !subsys->ops->fw_shutdown) + return TEE_ERROR_NOT_SUPPORTED; + + res = subsys->ops->fw_shutdown(&subsys->data); + if (!res) { + subsys->data.fw_base = 0; + subsys->data.fw_size = 0; + } + + return res; +} diff --git a/core/pta/qcom/pas/platform/kodiak/cdsp.c b/core/pta/qcom/pas/platform/kodiak/cdsp.c new file mode 100644 index 000000000..a8cad6548 --- /dev/null +++ b/core/pta/qcom/pas/platform/kodiak/cdsp.c @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include + +#include "cdsp.h" +#include "dsp_fw.h" + +#define TURING_QDSP6V68SS_PUB_REG 0x00b00000 +#define TURING_QDSP6V68SS_CC_REG 0x00b18000 +#define TURING_QDSP6SS_Q6_CC_REG 0x00b40000 + +static const struct fw_rsc_devmem turing_mem_res[] = { + { .name = "tcsr_2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x01fc0000, .pa = 0x01fc0000, .len = 0x00030000, }, + { .name = "tcsr_mutex", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x01f40000, .pa = 0x01f40000, .len = 0x00020000, }, + { .name = "efuse", .flags = IOMMU_READ, + .da = 0x00780000, .pa = 0x00780000, .len = 0x0000A000, }, + { .name = "mailbox", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x406000, .pa = 0x406000, .len = 0x00001000, }, + { .name = "smem", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x80900000, .pa = 0x80900000, .len = 0x00200000, }, + { .name = "cmd_db", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x80860000, .pa = 0x80860000, .len = 0x00020000, }, + { .name = "aoss_msg_ram", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x0c300000, .pa = 0x0c300000, .len = 0x00100000, }, + { .name = "clk_ctl", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x00100000, .pa = 0x00100000, .len = 0x001f0000, }, + { .name = "rpmh_pdc", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x0b2b0000, .pa = 0x0b2b0000, .len = 0x00010000, }, + { .name = "rpmh_seqmem", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x0b4b0000, .pa = 0x0b4b0000, .len = 0x00010000, }, + { .name = "rpmh_bcm", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x0bbf0000, .pa = 0x0bbf0000, .len = 0x0002000, }, + { .name = "ddr_reg", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x09080000, .pa = 0x09080000, .len = 0x00001000, }, + { .name = "llcc_bdcast", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x09600000, .pa = 0x09600000, .len = 0x00058000, }, + { .name = "llcc0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x09200000, .pa = 0x09200000, .len = 0x00058000, }, + { .name = "llcc1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x09280000, .pa = 0x09280000, .len = 0x00058000, }, + { .name = "rdpm", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x00634000, .pa = 0x00634000, .len = 0x00004000, }, +}; + +DEFINE_RESOURCE_TABLE(TURING, ARRAY_SIZE(turing_mem_res)); + +static TEE_Result cdsp_fw_start(struct qcom_pas_data *data) +{ + const struct dsp_fw_start_regs regs = { + .boot_status = TURING_QDSP6V68SS_PUB_REG + 0x408, + .core_start = TURING_QDSP6V68SS_PUB_REG + 0x400, + .core_cbcr = TURING_QDSP6SS_Q6_CC_REG + 0x1040, + .sleep_cbcr = TURING_QDSP6V68SS_CC_REG + 0x58, + .boot_cmd = TURING_QDSP6V68SS_PUB_REG + 0x404, + .rst_evb = TURING_QDSP6V68SS_PUB_REG + 0x10, + .xo_cbcr = TURING_QDSP6V68SS_CC_REG + 0x54, + }; + + return dsp_fw_start(data, ®s); +} + +static TEE_Result cdsp_fw_shutdown(struct qcom_pas_data *data __unused) +{ + return TEE_ERROR_NOT_IMPLEMENTED; +} + +static TEE_Result cdsp_get_resource_table(struct resource_table *rt, + size_t *rt_size) +{ + const struct fw_rsc_hdr header = { + .type = RSC_DEVMEM, + }; + static struct resource_table table = { + .ver = 1, + .num = TURING_NUM_MEM_RESOURCES, + .offset[TURING_NUM_MEM_RESOURCES - 1] = 0, + }; + + return get_mem_rsc(rt, rt_size, &table, &header, + turing_mem_res, + TURING_RESOURCE_TABLE_HEADER_SIZE, + TURING_RESOURCE_TABLE_SIZE); +} + +const struct qcom_pas_ops cdsp_ops = { + .fw_start = cdsp_fw_start, + .fw_shutdown = cdsp_fw_shutdown, + .get_resource_table = cdsp_get_resource_table, +}; diff --git a/core/pta/qcom/pas/platform/kodiak/cdsp.h b/core/pta/qcom/pas/platform/kodiak/cdsp.h new file mode 100644 index 000000000..4f8713fad --- /dev/null +++ b/core/pta/qcom/pas/platform/kodiak/cdsp.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _CDSP_H_ +#define _CDSP_H_ + +#include "pas_subsys.h" + +extern const struct qcom_pas_ops cdsp_ops; + +#endif /* _CDSP_H_ */ diff --git a/core/pta/qcom/pas/platform/kodiak/dsp_boot.h b/core/pta/qcom/pas/platform/kodiak/dsp_boot.h deleted file mode 100644 index 11800fc40..000000000 --- a/core/pta/qcom/pas/platform/kodiak/dsp_boot.h +++ /dev/null @@ -1,80 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. - */ - -#ifndef _DSP_BOOT_H_ -#define _DSP_BOOT_H_ - -#include -#include -#include -#include - -#include "dsp.h" -#include "pas.h" - -/* - * Compute - */ -#define TURING_QDSP6V68SS_PUB_REG 0x00b00000 -#define TURING_QDSP6V68SS_CC_REG 0x00b18000 -#define TURING_QDSP6SS_Q6_CC_REG 0x00b40000 - -static const struct dsp_fw_boot_regs compute_fw_boot_regs = { - .xo_cbcr = TURING_QDSP6V68SS_CC_REG + 0x54, - .sleep_cbcr = TURING_QDSP6V68SS_CC_REG + 0x58, - .core_cbcr = TURING_QDSP6SS_Q6_CC_REG + 0x1040, - .rst_evb = TURING_QDSP6V68SS_PUB_REG + 0x10, - .core_start = TURING_QDSP6V68SS_PUB_REG + 0x400, - .boot_cmd = TURING_QDSP6V68SS_PUB_REG + 0x404, - .boot_status = TURING_QDSP6V68SS_PUB_REG + 0x408, -}; - -/* - * LPASS - */ -#define LPASS_QDSP6V67SS_PUB_REG 0x00400000 -#define LPASS_MCC_REG 0x00950000 - -static const struct dsp_fw_boot_regs lpass_fw_boot_regs = { - .xo_cbcr = LPASS_QDSP6V67SS_PUB_REG + 0x38, - .sleep_cbcr = LPASS_QDSP6V67SS_PUB_REG + 0x3c, - .core_cbcr = LPASS_QDSP6V67SS_PUB_REG + 0x20, - .rst_evb = LPASS_QDSP6V67SS_PUB_REG + 0x10, - .core_start = LPASS_QDSP6V67SS_PUB_REG + 0x400, - .boot_cmd = LPASS_QDSP6V67SS_PUB_REG + 0x404, - .boot_status = LPASS_QDSP6V67SS_PUB_REG + 0x408, - .lpass.efuse_evb_sel = LPASS_MCC_REG + 0xb000, -}; - -/* - * WPSS - */ -#define WPSS_QDSP6V67SS_PUB_REG 0x00000000 - -static const struct dsp_fw_boot_regs wpss_fw_boot_regs = { - .xo_cbcr = WPSS_QDSP6V67SS_PUB_REG + 0x38, - .sleep_cbcr = WPSS_QDSP6V67SS_PUB_REG + 0x3c, - .core_cbcr = WPSS_QDSP6V67SS_PUB_REG + 0x20, - .rst_evb = WPSS_QDSP6V67SS_PUB_REG + 0x10, - .core_start = WPSS_QDSP6V67SS_PUB_REG + 0x400, - .boot_cmd = WPSS_QDSP6V67SS_PUB_REG + 0x404, - .boot_status = WPSS_QDSP6V67SS_PUB_REG + 0x408, -}; - -static inline const struct dsp_fw_boot_regs *dsp_fw_get_boot_regs(uint32_t id) -{ - switch (id) { - case PAS_ID_WPSS: - return &wpss_fw_boot_regs; - case PAS_ID_TURING: - return &compute_fw_boot_regs; - case PAS_ID_QDSP6: - return &lpass_fw_boot_regs; - default: - return NULL; - } -} - -#endif /* _DSP_BOOT_H_ */ diff --git a/core/pta/qcom/pas/dsp.c b/core/pta/qcom/pas/platform/kodiak/dsp_fw.c similarity index 92% rename from core/pta/qcom/pas/dsp.c rename to core/pta/qcom/pas/platform/kodiak/dsp_fw.c index d81c96fcd..df970e1b3 100644 --- a/core/pta/qcom/pas/dsp.c +++ b/core/pta/qcom/pas/platform/kodiak/dsp_fw.c @@ -4,12 +4,10 @@ */ #include -#include #include #include -#include "dsp.h" -#include "pas.h" +#include "dsp_fw.h" #define CBCR_BRANCH_ENABLE_BIT BIT(0) #define CBCR_HW_CTL_ENABLE_BIT BIT(1) @@ -19,7 +17,7 @@ #define BOOT_FSM_TIMEOUT 10000 TEE_Result dsp_fw_start(struct qcom_pas_data *data, - const struct dsp_fw_boot_regs *regs) + const struct dsp_fw_start_regs *regs) { vaddr_t base = io_pa_or_va(&data->base, data->size); uint64_t timeout; diff --git a/core/pta/qcom/pas/dsp.h b/core/pta/qcom/pas/platform/kodiak/dsp_fw.h similarity index 62% rename from core/pta/qcom/pas/dsp.h rename to core/pta/qcom/pas/platform/kodiak/dsp_fw.h index 8b190b5a4..e2b33f3fb 100644 --- a/core/pta/qcom/pas/dsp.h +++ b/core/pta/qcom/pas/platform/kodiak/dsp_fw.h @@ -3,16 +3,16 @@ * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. */ -#ifndef _DSP_H_ -#define _DSP_H_ +#ifndef _DSP_FW_H_ +#define _DSP_FW_H_ -#include "pas.h" +#include "pas_data.h" -struct dsp_fw_boot_lpass_regs { +struct dsp_fw_start_lpass_regs { uint32_t efuse_evb_sel; }; -struct dsp_fw_boot_regs { +struct dsp_fw_start_regs { uint32_t xo_cbcr; uint32_t sleep_cbcr; uint32_t core_cbcr; @@ -21,10 +21,10 @@ struct dsp_fw_boot_regs { uint32_t boot_cmd; uint32_t boot_status; /* lpass */ - struct dsp_fw_boot_lpass_regs lpass; + struct dsp_fw_start_lpass_regs lpass; }; TEE_Result dsp_fw_start(struct qcom_pas_data *data, - const struct dsp_fw_boot_regs *regs); + const struct dsp_fw_start_regs *regs); -#endif /* _DSP_H_ */ +#endif /* _DSP_FW_H_ */ diff --git a/core/pta/qcom/pas/venus.c b/core/pta/qcom/pas/platform/kodiak/iris.c similarity index 66% rename from core/pta/qcom/pas/venus.c rename to core/pta/qcom/pas/platform/kodiak/iris.c index deea980a5..54f60a162 100644 --- a/core/pta/qcom/pas/venus.c +++ b/core/pta/qcom/pas/platform/kodiak/iris.c @@ -4,12 +4,10 @@ */ #include -#include #include #include -#include "pas.h" -#include "video.h" +#include "iris.h" #define WRAPPER_IRIS_VERSION 0x0 #define WRAPPER_CPA_START_ADDR 0x1020 @@ -21,10 +19,11 @@ #define WRAPPER_TZ_XTSS_SW_RESET 0x1000 #define WRAPPER_XTSS_SW_RESET_BIT BIT(0) -static TEE_Result venus_reset_cpu(struct qcom_pas_data *data) +static TEE_Result iris_reset_cpu(struct qcom_pas_data *data) { - vaddr_t base = io_pa_or_va(&data->base, data->size); + vaddr_t base = 0; + base = io_pa_or_va(&data->base, data->size); if (!base) return TEE_ERROR_GENERIC; @@ -41,11 +40,12 @@ static TEE_Result venus_reset_cpu(struct qcom_pas_data *data) return TEE_SUCCESS; } -TEE_Result venus_fw_shutdown(struct qcom_pas_data *data) +static TEE_Result iris_fw_shutdown(struct qcom_pas_data *data) { - vaddr_t base = io_pa_or_va(&data->base, data->size); - uint32_t reg; + vaddr_t base = 0; + uint32_t reg = 0; + base = io_pa_or_va(&data->base, data->size); if (!base) return TEE_ERROR_GENERIC; @@ -58,16 +58,21 @@ TEE_Result venus_fw_shutdown(struct qcom_pas_data *data) return TEE_SUCCESS; } -TEE_Result venus_fw_start(struct qcom_pas_data *data) +static TEE_Result iris_fw_start(struct qcom_pas_data *data) { - return venus_reset_cpu(data); + return iris_reset_cpu(data); } -TEE_Result venus_fw_set_state(struct qcom_pas_data *data, bool power_on) +static TEE_Result iris_fw_set_state(struct qcom_pas_data *data, bool power_on) { if (power_on) - return venus_reset_cpu(data); + return iris_reset_cpu(data); - return venus_fw_shutdown(data); + return iris_fw_shutdown(data); } +const struct qcom_pas_ops iris_ops = { + .fw_start = iris_fw_start, + .fw_shutdown = iris_fw_shutdown, + .fw_set_state = iris_fw_set_state, +}; diff --git a/core/pta/qcom/pas/platform/kodiak/video.h b/core/pta/qcom/pas/platform/kodiak/iris.h similarity index 64% rename from core/pta/qcom/pas/platform/kodiak/video.h rename to core/pta/qcom/pas/platform/kodiak/iris.h index da31db4b0..b283c3cf0 100644 --- a/core/pta/qcom/pas/platform/kodiak/video.h +++ b/core/pta/qcom/pas/platform/kodiak/iris.h @@ -3,10 +3,14 @@ * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. */ -#ifndef _VIDEO_H_ -#define _VIDEO_H_ +#ifndef _IRIS_H_ +#define _IRIS_H_ + +#include "pas_subsys.h" #define IRIS_WRAPPER_TOP_TZ_REG_BASE 0x000c0000 #define IRIS_WRAPPER_TOP_REG_BASE 0x000b0000 -#endif +extern const struct qcom_pas_ops iris_ops; + +#endif /* _IRIS_H_ */ diff --git a/core/pta/qcom/pas/platform/kodiak/lpass.c b/core/pta/qcom/pas/platform/kodiak/lpass.c new file mode 100644 index 000000000..d12f43661 --- /dev/null +++ b/core/pta/qcom/pas/platform/kodiak/lpass.c @@ -0,0 +1,94 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include + +#include "dsp_fw.h" +#include "lpass.h" + +#define LPASS_QDSP6V67SS_PUB_REG 0x00400000 +#define LPASS_MCC_REG 0x00950000 + +static const struct fw_rsc_devmem lpass_mem_res[] = { + { .name = "efuse", .flags = IOMMU_READ, + .da = 0x00786000, .pa = 0x00786000, .len = 0x00020000, }, + { .name = "rng", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x010d4000, .pa = 0x010d4000, .len = 0x00001000, }, + { .name = "mailbox", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x00403000, .pa = 0x00403000, .len = 0x00001000, }, + { .name = "rpmh", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x0a000000, .pa = 0x0a000000, .len = 0x05000000, }, + { .name = "tcsr_2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x01fc0000, .pa = 0x01fc0000, .len = 0x00030000, }, + { .name = "tcsr_mutex", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x01f40000, .pa = 0x01f40000, .len = 0x00020000, }, + { .name = "smem", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x80900000, .pa = 0x80900000, .len = 0x00200000, }, + { .name = "gcc", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x00100000, .pa = 0x00100000, .len = 0x001F0000, }, + { .name = "cmd_db", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x80860000, .pa = 0x80860000, .len = 0x00020000, }, + { .name = "i2c", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x00a80000, .pa = 0x00a80000, .len = 0x00004000, }, + { .name = "pinctrl", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x0f100000, .pa = 0x0f100000, .len = 0x00300000, }, +}; + +DEFINE_RESOURCE_TABLE(LPASS, ARRAY_SIZE(lpass_mem_res)); + +static TEE_Result lpass_fw_start(struct qcom_pas_data *data) +{ + const struct dsp_fw_start_regs regs = { + .boot_status = LPASS_QDSP6V67SS_PUB_REG + 0x408, + .core_start = LPASS_QDSP6V67SS_PUB_REG + 0x400, + .sleep_cbcr = LPASS_QDSP6V67SS_PUB_REG + 0x3c, + .lpass.efuse_evb_sel = LPASS_MCC_REG + 0xb000, + .core_cbcr = LPASS_QDSP6V67SS_PUB_REG + 0x20, + .boot_cmd = LPASS_QDSP6V67SS_PUB_REG + 0x404, + .xo_cbcr = LPASS_QDSP6V67SS_PUB_REG + 0x38, + .rst_evb = LPASS_QDSP6V67SS_PUB_REG + 0x10, + }; + vaddr_t base = 0; + + base = io_pa_or_va(&data->base, data->size); + if (!base) + return TEE_ERROR_GENERIC; + + io_write32(base + regs.lpass.efuse_evb_sel, 0); + + return dsp_fw_start(data, ®s); +} + +static TEE_Result lpass_fw_shutdown(struct qcom_pas_data *data __unused) +{ + return TEE_ERROR_NOT_IMPLEMENTED; +} + +static TEE_Result lpass_get_resource_table(struct resource_table *rt, + size_t *rt_size) +{ + const struct fw_rsc_hdr header = { + .type = RSC_DEVMEM, + }; + static struct resource_table table = { + .ver = 1, + .num = LPASS_NUM_MEM_RESOURCES, + .offset[LPASS_NUM_MEM_RESOURCES - 1] = 0, + }; + + return get_mem_rsc(rt, rt_size, &table, &header, + lpass_mem_res, + LPASS_RESOURCE_TABLE_HEADER_SIZE, + LPASS_RESOURCE_TABLE_SIZE); +} + +const struct qcom_pas_ops lpass_ops = { + .fw_start = lpass_fw_start, + .fw_shutdown = lpass_fw_shutdown, + .get_resource_table = lpass_get_resource_table, +}; diff --git a/core/pta/qcom/pas/platform/kodiak/lpass.h b/core/pta/qcom/pas/platform/kodiak/lpass.h new file mode 100644 index 000000000..f8cf519fc --- /dev/null +++ b/core/pta/qcom/pas/platform/kodiak/lpass.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _LPASS_H_ +#define _LPASS_H_ + +#include "pas_subsys.h" + +extern const struct qcom_pas_ops lpass_ops; + +#endif /* _LPASS_H_ */ diff --git a/core/pta/qcom/pas/platform/kodiak/pas_resources.h b/core/pta/qcom/pas/platform/kodiak/pas_resources.h deleted file mode 100644 index b1b7886e5..000000000 --- a/core/pta/qcom/pas/platform/kodiak/pas_resources.h +++ /dev/null @@ -1,129 +0,0 @@ -/* SPDX-License-Identifier: BSD-2-Clause */ -/* - * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. - */ - -#ifndef _PAS_RESOURCES_H_ -#define _PAS_RESOURCES_H_ - -#include -#include -#include -#include - -#include "pas.h" - -/* - * WPSS - */ -static const struct fw_rsc_devmem wpss_mem_res[] = { - {.name = "wlan_fw_mem", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x80c00000, .pa = 0x80c00000, .len = 0xc00000, }, - {.name = "wlan_ce_mem", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x004cd000, .pa = 0x004cd000, .len = 0x1000, }, -}; - -static const struct fw_rsc_hdr wpss_mem_hdr = { - .type = RSC_DEVMEM, -}; - -DEFINE_RESOURCE_TABLE(WPSS, ARRAY_SIZE(wpss_mem_res)); - -static struct resource_table wpss_rt = { - .ver = 1, - .num = WPSS_NUM_MEM_RESOURCES, - .offset[WPSS_NUM_MEM_RESOURCES - 1] = 0, -}; - -/* - * Compute - */ -static const struct fw_rsc_devmem turing_mem_res[] = { - { .name = "tcsr_2", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x01fc0000, .pa = 0x01fc0000, .len = 0x00030000, }, - { .name = "tcsr_mutex", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x01f40000, .pa = 0x01f40000, .len = 0x00020000, }, - { .name = "efuse", .flags = IOMMU_READ, - .da = 0x00780000, .pa = 0x00780000, .len = 0x0000A000, }, - { .name = "mailbox", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x406000, .pa = 0x406000, .len = 0x00001000, }, - { .name = "smem", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x80900000, .pa = 0x80900000, .len = 0x00200000, }, - { .name = "cmd_db", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x80860000, .pa = 0x80860000, .len = 0x00020000, }, - { .name = "aoss_msg_ram", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x0c300000, .pa = 0x0c300000, .len = 0x00100000, }, - { .name = "clk_ctl", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x00100000, .pa = 0x00100000, .len = 0x001f0000, }, - { .name = "rpmh_pdc", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x0b2b0000, .pa = 0x0b2b0000, .len = 0x00010000, }, - { .name = "rpmh_seqmem", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x0b4b0000, .pa = 0x0b4b0000, .len = 0x00010000, }, - { .name = "rpmh_bcm", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x0bbf0000, .pa = 0x0bbf0000, .len = 0x0002000, }, - { .name = "ddr_reg", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x09080000, .pa = 0x09080000, .len = 0x00001000, }, - { .name = "llcc_bdcast", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x09600000, .pa = 0x09600000, .len = 0x00058000, }, - { .name = "llcc0", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x09200000, .pa = 0x09200000, .len = 0x00058000, }, - { .name = "llcc1", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x09280000, .pa = 0x09280000, .len = 0x00058000, }, - { .name = "rdpm", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x00634000, .pa = 0x00634000, .len = 0x00004000, }, -}; - -static const struct fw_rsc_hdr turing_mem_hdr = { - .type = RSC_DEVMEM, -}; - -DEFINE_RESOURCE_TABLE(TURING, ARRAY_SIZE(turing_mem_res)); - -static struct resource_table turing_rt = { - .ver = 1, - .num = TURING_NUM_MEM_RESOURCES, - .offset[TURING_NUM_MEM_RESOURCES - 1] = 0, -}; - -/* - * LPASS - */ -static const struct fw_rsc_devmem lpass_mem_res[] = { - { .name = "efuse", .flags = IOMMU_READ, - .da = 0x00786000, .pa = 0x00786000, .len = 0x00020000, }, - { .name = "rng", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x010d4000, .pa = 0x010d4000, .len = 0x00001000, }, - { .name = "mailbox", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x00403000, .pa = 0x00403000, .len = 0x00001000, }, - { .name = "rpmh", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x0a000000, .pa = 0x0a000000, .len = 0x05000000, }, - { .name = "tcsr_2", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x01fc0000, .pa = 0x01fc0000, .len = 0x00030000, }, - { .name = "tcsr_mutex", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x01f40000, .pa = 0x01f40000, .len = 0x00020000, }, - { .name = "smem", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x80900000, .pa = 0x80900000, .len = 0x00200000, }, - { .name = "gcc", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x00100000, .pa = 0x00100000, .len = 0x001F0000, }, - { .name = "cmd_db", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x80860000, .pa = 0x80860000, .len = 0x00020000, }, - { .name = "i2c", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x00a80000, .pa = 0x00a80000, .len = 0x00004000, }, - { .name = "pinctrl", .flags = IOMMU_READ | IOMMU_WRITE, - .da = 0x0f100000, .pa = 0x0f100000, .len = 0x00300000, }, -}; - -static const struct fw_rsc_hdr lpass_mem_hdr = { - .type = RSC_DEVMEM, -}; - -DEFINE_RESOURCE_TABLE(LPASS, ARRAY_SIZE(lpass_mem_res)); - -static struct resource_table lpass_rt = { - .ver = 1, - .num = LPASS_NUM_MEM_RESOURCES, - .offset[LPASS_NUM_MEM_RESOURCES - 1] = 0, -}; - -#endif /* _PAS_RESOURCES_H_ */ - diff --git a/core/pta/qcom/pas/platform/kodiak/sub.mk b/core/pta/qcom/pas/platform/kodiak/sub.mk new file mode 100644 index 000000000..4be1630e1 --- /dev/null +++ b/core/pta/qcom/pas/platform/kodiak/sub.mk @@ -0,0 +1,3 @@ +srcs-y += subsys.c cdsp.c dsp_fw.c lpass.c wpss.c iris.c +incdirs-y += . +incdirs-y += ../ diff --git a/core/pta/qcom/pas/platform/kodiak/subsys.c b/core/pta/qcom/pas/platform/kodiak/subsys.c new file mode 100644 index 000000000..10cd7fbe4 --- /dev/null +++ b/core/pta/qcom/pas/platform/kodiak/subsys.c @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include + +#include "cdsp.h" +#include "iris.h" +#include "lpass.h" +#include "pas_subsys.h" +#include "wpss.h" + +static struct qcom_pas_subsys subsystems[] = { + { + .data = { + .pas_id = PAS_ID_WPSS, + .base.pa = WPSS_BASE, + .size = WPSS_SIZE, + .clk_group = QCOM_CLKS_WPSS, + }, + .ops = &wpss_ops, + .reset_seq = QCOM_PAS_RESET_CLK_ENABLE, + }, + { + .data = { + .pas_id = PAS_ID_TURING, + .base.pa = TURING_BASE, + .size = TURING_SIZE, + .clk_group = QCOM_CLKS_TURING, + }, + .ops = &cdsp_ops, + .reset_seq = QCOM_PAS_RESET_CLK_ENABLE, + }, + { + .data = { + .pas_id = PAS_ID_QDSP6, + .base.pa = LPASS_BASE, + .size = LPASS_SIZE, + .clk_group = QCOM_CLKS_LPASS, + }, + .ops = &lpass_ops, + .reset_seq = QCOM_PAS_RESET_CLK_ENABLE, + }, + { + .data = { + .pas_id = PAS_ID_IRIS, + .base.pa = IRIS_BASE, + .size = IRIS_SIZE, + }, + .ops = &iris_ops, + .reset_seq = QCOM_PAS_RESET_NONE, + }, +}; + +struct qcom_pas_subsys *qcom_pas_platform_subsys(size_t *count) +{ + *count = ARRAY_SIZE(subsystems); + + return subsystems; +} diff --git a/core/pta/qcom/pas/platform/kodiak/wpss.c b/core/pta/qcom/pas/platform/kodiak/wpss.c new file mode 100644 index 000000000..ce7fa88b4 --- /dev/null +++ b/core/pta/qcom/pas/platform/kodiak/wpss.c @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include + +#include "dsp_fw.h" +#include "wpss.h" + +#define WPSS_QDSP6V67SS_PUB_REG 0x00000000 + +static const struct fw_rsc_devmem wpss_mem_res[] = { + { .name = "wlan_fw_mem", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x80c00000, .pa = 0x80c00000, .len = 0xc00000, }, + { .name = "wlan_ce_mem", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x004cd000, .pa = 0x004cd000, .len = 0x1000, }, +}; + +DEFINE_RESOURCE_TABLE(WPSS, ARRAY_SIZE(wpss_mem_res)); + +static TEE_Result wpss_fw_start(struct qcom_pas_data *data) +{ + static const struct dsp_fw_start_regs regs = { + .boot_status = WPSS_QDSP6V67SS_PUB_REG + 0x408, + .core_start = WPSS_QDSP6V67SS_PUB_REG + 0x400, + .sleep_cbcr = WPSS_QDSP6V67SS_PUB_REG + 0x3c, + .core_cbcr = WPSS_QDSP6V67SS_PUB_REG + 0x20, + .boot_cmd = WPSS_QDSP6V67SS_PUB_REG + 0x404, + .rst_evb = WPSS_QDSP6V67SS_PUB_REG + 0x10, + .xo_cbcr = WPSS_QDSP6V67SS_PUB_REG + 0x38, + }; + + return dsp_fw_start(data, ®s); +} + +static TEE_Result wpss_fw_shutdown(struct qcom_pas_data *data __unused) +{ + return TEE_ERROR_NOT_IMPLEMENTED; +} + +static TEE_Result wpss_get_resource_table(struct resource_table *rt, + size_t *rt_size) +{ + const struct fw_rsc_hdr header = { + .type = RSC_DEVMEM, + }; + static struct resource_table table = { + .ver = 1, + .num = WPSS_NUM_MEM_RESOURCES, + .offset[WPSS_NUM_MEM_RESOURCES - 1] = 0, + }; + + return get_mem_rsc(rt, rt_size, &table, &header, + wpss_mem_res, + WPSS_RESOURCE_TABLE_HEADER_SIZE, + WPSS_RESOURCE_TABLE_SIZE); +} + +const struct qcom_pas_ops wpss_ops = { + .fw_start = wpss_fw_start, + .fw_shutdown = wpss_fw_shutdown, + .get_resource_table = wpss_get_resource_table, +}; diff --git a/core/pta/qcom/pas/platform/kodiak/wpss.h b/core/pta/qcom/pas/platform/kodiak/wpss.h new file mode 100644 index 000000000..3b60634df --- /dev/null +++ b/core/pta/qcom/pas/platform/kodiak/wpss.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _WPSS_H_ +#define _WPSS_H_ + +#include "pas_subsys.h" + +extern const struct qcom_pas_ops wpss_ops; + +#endif /* _WPSS_H_ */ diff --git a/core/pta/qcom/pas/platform/lemans/camera.c b/core/pta/qcom/pas/platform/lemans/camera.c new file mode 100644 index 000000000..6c77b1adf --- /dev/null +++ b/core/pta/qcom/pas/platform/lemans/camera.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include + +#include "camera.h" + +#define ICP_SYS_RESET 0x0000 +#define ICP_SYS_RESET_FUNC_RESET BIT(4) + +#define ICP_SYS_CONTROL 0x0004 +#define ICP_SYS_CONTROL_CPU_EN BIT(9) + +#define ICP_SYS_STATUS 0x000c +#define ICP_SYS_STATUS_LX7_STANDBYWFI BIT(7) + +#define ICP_SYS_ACCESS 0x0010 +#define ICP_SYS_ACCESS_QOS_PRIORITY 0x0A0A + +#define ICP_SYS_SID1_START_ADDR 0x0048 +#define ICP_SYS_SID1_START_ADDR_EN BIT(0) +#define ICP_SYS_SID_REGION_MASK 0xFFFFFFF0U + +#define ICP_SYS_SID1_END_ADDR 0x004c + +#define LX7_WFI_TIMEOUT_US 300000 + +static TEE_Result camera_fw_start(struct qcom_pas_data *data) +{ + vaddr_t base = io_pa_or_va(&data->base, data->size); + uint32_t sid_end = 0; + uint32_t reg = 0; + + if (!base) + return TEE_ERROR_GENERIC; + + io_write32(base + ICP_SYS_RESET, ICP_SYS_RESET_FUNC_RESET); + + sid_end = (uint32_t)(data->fw_size) & ICP_SYS_SID_REGION_MASK; + + DMSG("camera: SID1 start=0x%08x end=0x%08x", 0, sid_end); + + io_write32(base + ICP_SYS_SID1_START_ADDR, 0); + io_write32(base + ICP_SYS_SID1_END_ADDR, sid_end); + + io_write32(base + ICP_SYS_ACCESS, ICP_SYS_ACCESS_QOS_PRIORITY); + + reg = io_read32(base + ICP_SYS_SID1_START_ADDR); + io_write32(base + ICP_SYS_SID1_START_ADDR, + reg | ICP_SYS_SID1_START_ADDR_EN); + + io_write32(base + ICP_SYS_CONTROL, ICP_SYS_CONTROL_CPU_EN); + + return TEE_SUCCESS; +} + +static TEE_Result camera_fw_shutdown(struct qcom_pas_data *data) +{ + vaddr_t base = io_pa_or_va(&data->base, data->size); + + if (!base) + return TEE_ERROR_GENERIC; + + io_write32(base + ICP_SYS_CONTROL, 0); + + return TEE_SUCCESS; +} + +static TEE_Result camera_fw_set_state(struct qcom_pas_data *data, bool power_on) +{ + uint64_t timeout = timeout_init_us(LX7_WFI_TIMEOUT_US); + vaddr_t base = io_pa_or_va(&data->base, data->size); + + if (!base) + return TEE_ERROR_GENERIC; + + if (power_on) { + if (io_read32(base + ICP_SYS_CONTROL) & ICP_SYS_CONTROL_CPU_EN) + return TEE_ERROR_BAD_STATE; + return camera_fw_start(data); + } + + while (!timeout_elapsed(timeout)) { + if (io_read32(base + ICP_SYS_STATUS) & + ICP_SYS_STATUS_LX7_STANDBYWFI) + return camera_fw_shutdown(data); + udelay(10); + } + + DMSG("camera: WFI timeout, forcing halt"); + camera_fw_shutdown(data); + return TEE_ERROR_TIMEOUT; +} + +const struct qcom_pas_ops camera_ops = { + .fw_start = camera_fw_start, + .fw_shutdown = camera_fw_shutdown, + .fw_set_state = camera_fw_set_state, +}; diff --git a/core/pta/qcom/pas/platform/lemans/camera.h b/core/pta/qcom/pas/platform/lemans/camera.h new file mode 100644 index 000000000..befe8f518 --- /dev/null +++ b/core/pta/qcom/pas/platform/lemans/camera.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _CAMERA_H_ +#define _CAMERA_H_ + +#include "pas_subsys.h" + +extern const struct qcom_pas_ops camera_ops; + +#endif /* _CAMERA_H_ */ diff --git a/core/pta/qcom/pas/platform/lemans/cdsp0.c b/core/pta/qcom/pas/platform/lemans/cdsp0.c new file mode 100644 index 000000000..63c91c2e7 --- /dev/null +++ b/core/pta/qcom/pas/platform/lemans/cdsp0.c @@ -0,0 +1,292 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cdsp0.h" + +#define TURING_NSPAUX_XO_CBCR 0x02008040 +#define TURING_Q6SS_Q6_AXIM_CBCR 0x02008404 +#define TURING_Q6SS_AXIS2_CBCR 0x0200840c + +#define TURING_QDSP6SS_CORE_CBCR 0x02348040 + +#define TURING_QDSP6SS_RST_EVB 0x02300010 +#define TURING_QDSP6SS_BOOT_CMD 0x02300404 +#define TURING_QDSP6SS_BOOT_STATUS 0x02300408 + +static const struct fw_rsc_devmem turing_mem_res[] = { + { .name = "gcc_gpll0_3", .flags = IOMMU_READ, + .da = 0x110000, .pa = 0x110000, .len = 0x4000, }, + { .name = "gcc_turing1_cfg", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x114000, .pa = 0x114000, .len = 0xf000, }, + { .name = "gcc_gpll6", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x123000, .pa = 0x123000, .len = 0x1000, }, + { .name = "gcc_qupv3_wrap1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x124000, .pa = 0x124000, .len = 0x3000, }, + { .name = "gcc_qupv3_wrap0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x127000, .pa = 0x127000, .len = 0x3000, }, + { .name = "gcc_gpll7_9", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x12a000, .pa = 0x12a000, .len = 0x3000, }, + { .name = "gcc_qupv3_wrap2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x12e000, .pa = 0x12e000, .len = 0x1000, }, + { .name = "gcc_gpll10", .flags = IOMMU_READ, + .da = 0x12f000, .pa = 0x12f000, .len = 0x1000, }, + { .name = "gcc_boot_rom", .flags = IOMMU_READ, + .da = 0x144000, .pa = 0x144000, .len = 0x1000, }, + { .name = "gcc_boot_rom_ahb", .flags = IOMMU_READ, + .da = 0x148000, .pa = 0x148000, .len = 0x1000, }, + { .name = "gcc_turing0_cfg", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x149000, .pa = 0x149000, .len = 0xc000, }, + { .name = "gcc_turing0_gdscr", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x155000, .pa = 0x155000, .len = 0x1000, }, + { .name = "gcc_turing_dsp_vote", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x16b000, .pa = 0x16b000, .len = 0x1000, }, + { .name = "gcc_clk_frq_measure", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x172000, .pa = 0x172000, .len = 0x1000, }, + { .name = "gcc_turing0_pll_vote", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x173000, .pa = 0x173000, .len = 0x11000, }, + { .name = "gcc_gpll5", .flags = IOMMU_READ, + .da = 0x184000, .pa = 0x184000, .len = 0x1000, }, + { .name = "gcc_gpll4", .flags = IOMMU_READ, + .da = 0x186000, .pa = 0x186000, .len = 0x1000, }, + { .name = "gcc_turing1_pll_vote", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x187000, .pa = 0x187000, .len = 0x45000, }, + { .name = "gcc_turing1_gdscr", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1cc000, .pa = 0x1cc000, .len = 0x1000, }, + { .name = "mproc_ipc_cfg", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x406000, .pa = 0x406000, .len = 0x1000, }, + { .name = "mproc_ipc", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x412000, .pa = 0x412000, .len = 0x1000, }, + { .name = "computel0_ipc_cfg", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x446000, .pa = 0x446000, .len = 0x1000, }, + { .name = "computel0_ipc", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x452000, .pa = 0x452000, .len = 0x1000, }, + { .name = "computel1_ipc_cfg", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x486000, .pa = 0x486000, .len = 0x1000, }, + { .name = "computel1_ipc", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x492000, .pa = 0x492000, .len = 0x1000, }, + { .name = "periph_ipc0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4c6000, .pa = 0x4c6000, .len = 0x1000, }, + { .name = "periph_ipc1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4d2000, .pa = 0x4d2000, .len = 0x1000, }, + { .name = "ipc_mode", .flags = IOMMU_READ, + .da = 0x4fc000, .pa = 0x4fc000, .len = 0x1000, }, + { .name = "nspcx0_cpr3", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x63a000, .pa = 0x63a000, .len = 0x4000, }, + { .name = "nsp_cprf_cprc", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x63e000, .pa = 0x63e000, .len = 0x4000, }, + { .name = "oem_fuses", .flags = IOMMU_READ, + .da = 0x780000, .pa = 0x780000, .len = 0x10000, }, + { .name = "hwkm_prng_cm", .flags = IOMMU_READ, + .da = 0x10d0000, .pa = 0x10d0000, .len = 0x1000, }, + { .name = "hwkm_prng_ee10", .flags = IOMMU_READ, + .da = 0x10da000, .pa = 0x10da000, .len = 0x1000, }, + { .name = "qos_usb_ufs_pcie", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x16c2000, .pa = 0x16c2000, .len = 0x9000, }, + { .name = "qos_ufs_ipa_crypto", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1703000, .pa = 0x1703000, .len = 0x5000, }, + { .name = "hw_mutex", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1f40000, .pa = 0x1f40000, .len = 0x40000, }, + { .name = "tcsr_ro", .flags = IOMMU_READ, + .da = 0x1fc0000, .pa = 0x1fc0000, .len = 0x2b000, }, + { .name = "tcsr_timeout_intr", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1feb000, .pa = 0x1feb000, .len = 0x1000, }, + { .name = "tcsr_ro2", .flags = IOMMU_READ, + .da = 0x1fec000, .pa = 0x1fec000, .len = 0x4000, }, + { .name = "tcsr_spare_rg63", .flags = IOMMU_READ, + .da = 0x1fff000, .pa = 0x1fff000, .len = 0x1000, }, + { .name = "ddr_llc_info", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9080000, .pa = 0x9080000, .len = 0x1000, }, + { .name = "bwmon0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x90b4000, .pa = 0x90b4000, .len = 0x1000, }, + { .name = "bwmon1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x90b8000, .pa = 0x90b8000, .len = 0x1000, }, + { .name = "llcc_trp0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9200000, .pa = 0x9200000, .len = 0x28000, }, + { .name = "llcc_trp1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9300000, .pa = 0x9300000, .len = 0x28000, }, + { .name = "llcc_trp2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9400000, .pa = 0x9400000, .len = 0x28000, }, + { .name = "llcc_trp3", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9500000, .pa = 0x9500000, .len = 0x28000, }, + { .name = "llcc_trp4", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9600000, .pa = 0x9600000, .len = 0x28000, }, + { .name = "llcc_trp5", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9700000, .pa = 0x9700000, .len = 0x28000, }, + { .name = "pdc_seq_mem0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xb2c0000, .pa = 0xb2c0000, .len = 0x10000, }, + { .name = "pdc_rsc0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xb2f0000, .pa = 0xb2f0000, .len = 0x10000, }, + { .name = "pdc_seq_mem1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xb4c0000, .pa = 0xb4c0000, .len = 0x10000, }, + { .name = "pdc_rsc1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xb4f0000, .pa = 0xb4f0000, .len = 0x10000, }, + { .name = "rpmh_bcm", .flags = IOMMU_READ, + .da = 0xbbf0000, .pa = 0xbbf0000, .len = 0x2000, }, + { .name = "rpmh_cprf_nsp9", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xc20a000, .pa = 0xc20a000, .len = 0x1000, }, + { .name = "rpmh_cprf_nsp10", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xc20b000, .pa = 0xc20b000, .len = 0x1000, }, + { .name = "mpm", .flags = IOMMU_READ, + .da = 0xc210000, .pa = 0xc210000, .len = 0x80000, }, + { .name = "rpmh_drv_cdsp", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xc330000, .pa = 0xc330000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv0", .flags = IOMMU_READ, + .da = 0xc370000, .pa = 0xc370000, .len = 0x1000, }, + { .name = "rpmh_drv8_aop", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xc380000, .pa = 0xc380000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv1", .flags = IOMMU_READ, + .da = 0xc390000, .pa = 0xc390000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv2", .flags = IOMMU_READ, + .da = 0xc3a0000, .pa = 0xc3a0000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv3", .flags = IOMMU_READ, + .da = 0xc3b0000, .pa = 0xc3b0000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv4", .flags = IOMMU_READ, + .da = 0xc3c0000, .pa = 0xc3c0000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv5", .flags = IOMMU_READ, + .da = 0xc3d0000, .pa = 0xc3d0000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv6", .flags = IOMMU_READ, + .da = 0xc3e0000, .pa = 0xc3e0000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv7", .flags = IOMMU_READ, + .da = 0xc3f0000, .pa = 0xc3f0000, .len = 0x1000, }, + { .name = "pmic_arb", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xc440000, .pa = 0xc440000, .len = 0x10000, }, + { .name = "pmic_arb_mgpi", .flags = IOMMU_READ, + .da = 0xc460000, .pa = 0xc460000, .len = 0x81000, }, + { .name = "pmic_obs", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xe600000, .pa = 0xe600000, .len = 0x100000, }, + { .name = "spmi_pic", .flags = IOMMU_READ, + .da = 0xe700000, .pa = 0xe700000, .len = 0xa0000, }, + { .name = "tlmm", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xf000000, .pa = 0xf000000, .len = 0x1000000, }, + { .name = "sys_imem", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x146d8000, .pa = 0x146d8000, .len = 0x1000, }, + { .name = "stm_trace", .flags = IOMMU_WRITE, + .da = 0x16000000, .pa = 0x16000000, .len = 0x1000000, }, + { .name = "emac1_ptp", .flags = IOMMU_READ, + .da = 0x23007000, .pa = 0x23007000, .len = 0x1000, }, + { .name = "emac0_ptp", .flags = IOMMU_READ, + .da = 0x23047000, .pa = 0x23047000, .len = 0x1000, }, + { .name = "emac_sgmii", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x2304e000, .pa = 0x2304e000, .len = 0x2000, }, + { .name = "nsp_7806", .flags = IOMMU_READ, + .da = 0x90860000, .pa = 0x90860000, .len = 0x20000, }, + /* + * SMEM holds the GLINK FIFOs the DSP uses to talk to the apps + * processor; without this mapping the DSP faults on its first + * SMEM access. + */ + { .name = "smem", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x90900000, .pa = 0x90900000, .len = 0x200000, }, +}; + +DEFINE_RESOURCE_TABLE(TURING, ARRAY_SIZE(turing_mem_res)); + +#define BOOT_FSM_TIMEOUT 10000 + +/* + * Clear the CDSP0 content-protection shared channel before starting the DSP; + * stale contents block the host from talking to CDSP0. No CDSP1 equivalent. + */ +static TEE_Result cdsp0_reset_shared_channel(void) +{ + void *va = core_mmu_add_mapping(MEM_AREA_RAM_SEC, + CDSP_SECCHANNEL_BASE, + CDSP_SECCHANNEL_SIZE); + + if (!va) { + EMSG("Failed to map CDSP shared channel"); + return TEE_ERROR_GENERIC; + } + + memset(va, 0, CDSP_SECCHANNEL_SIZE); + + /* + * The channel is write-back cacheable, so flush the zeros to DDR before + * unmapping; otherwise the DSP/fastrpc may read stale cached data. + */ + dcache_clean_range(va, CDSP_SECCHANNEL_SIZE); + + if (core_mmu_remove_mapping(MEM_AREA_RAM_SEC, va, + CDSP_SECCHANNEL_SIZE)) { + EMSG("Failed to unmap CDSP shared channel"); + return TEE_ERROR_GENERIC; + } + + return TEE_SUCCESS; +} + +static TEE_Result cdsp0_fw_start(struct qcom_pas_data *data) +{ + vaddr_t base = io_pa_or_va(&data->base, data->size); + uint64_t timeout = timeout_init_us(BOOT_FSM_TIMEOUT); + TEE_Result res = TEE_SUCCESS; + + res = cdsp0_reset_shared_channel(); + if (res != TEE_SUCCESS) + return res; + + /* QDSPV73SS out of reset sequence */ + io_setbits32(base + TURING_QDSP6SS_CORE_CBCR, CBCR_BRANCH_ENABLE_BIT); + io_setbits32(base + TURING_Q6SS_Q6_AXIM_CBCR, CBCR_HW_CTL_ENABLE_BIT); + io_setbits32(base + TURING_Q6SS_AXIS2_CBCR, CBCR_HW_CTL_ENABLE_BIT); + io_setbits32(base + TURING_NSPAUX_XO_CBCR, CBCR_BRANCH_ENABLE_BIT); + + /* Program firmware */ + io_write32(base + TURING_QDSP6SS_RST_EVB, data->fw_base >> 4); + dsb(); + + /* Trigger the boot FSM. The core itself is released later, after the + * Q6 PLL is configured, by qcom_clock_enable_pas_processor(). + */ + io_write32(base + TURING_QDSP6SS_BOOT_CMD, 0x1); + + while (!timeout_elapsed(timeout)) { + if (io_read32(base + TURING_QDSP6SS_BOOT_STATUS) & BIT(0)) + return TEE_SUCCESS; + + udelay(10); + } + + return TEE_ERROR_TIMEOUT; +} + +static TEE_Result cdsp0_fw_shutdown(struct qcom_pas_data *data) +{ + return qcom_clock_pas_reset(data->clk_group); +} + +static TEE_Result cdsp0_get_resource_table(struct resource_table *rt, + size_t *rt_size) +{ + const struct fw_rsc_hdr header = { + .type = RSC_DEVMEM, + }; + static struct resource_table table = { + .ver = 1, + .num = TURING_NUM_MEM_RESOURCES, + .offset[TURING_NUM_MEM_RESOURCES - 1] = 0, + }; + + return get_mem_rsc(rt, rt_size, &table, &header, + turing_mem_res, + TURING_RESOURCE_TABLE_HEADER_SIZE, + TURING_RESOURCE_TABLE_SIZE); +} + +const struct qcom_pas_ops cdsp0_ops = { + .fw_start = cdsp0_fw_start, + .fw_shutdown = cdsp0_fw_shutdown, + .get_resource_table = cdsp0_get_resource_table, +}; diff --git a/core/pta/qcom/pas/platform/lemans/cdsp0.h b/core/pta/qcom/pas/platform/lemans/cdsp0.h new file mode 100644 index 000000000..2a0f117a8 --- /dev/null +++ b/core/pta/qcom/pas/platform/lemans/cdsp0.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _CDSP0_H_ +#define _CDSP0_H_ + +#include "pas_subsys.h" + +extern const struct qcom_pas_ops cdsp0_ops; + +#endif /* _CDSP0_H_ */ diff --git a/core/pta/qcom/pas/platform/lemans/cdsp1.c b/core/pta/qcom/pas/platform/lemans/cdsp1.c new file mode 100644 index 000000000..60c47abc7 --- /dev/null +++ b/core/pta/qcom/pas/platform/lemans/cdsp1.c @@ -0,0 +1,250 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include + +#include "cdsp1.h" + +#define TURING_NSPAUX_XO_CBCR 0x02008040 +#define TURING_Q6SS_Q6_AXIM_CBCR 0x02008404 +#define TURING_Q6SS_AXIS2_CBCR 0x0200840c + +#define TURING_QDSP6SS_CORE_CBCR 0x02348040 + +#define TURING_QDSP6SS_RST_EVB 0x02300010 +#define TURING_QDSP6SS_BOOT_CMD 0x02300404 +#define TURING_QDSP6SS_BOOT_STATUS 0x02300408 + +static const struct fw_rsc_devmem turing_mem_res[] = { + { .name = "gcc_gpll0_3", .flags = IOMMU_READ, + .da = 0x110000, .pa = 0x110000, .len = 0x4000, }, + { .name = "gcc_turing1_cfg", .flags = IOMMU_READ, + .da = 0x114000, .pa = 0x114000, .len = 0xf000, }, + { .name = "gcc_gpll6", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x123000, .pa = 0x123000, .len = 0x1000, }, + { .name = "gcc_qupv3_wrap1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x124000, .pa = 0x124000, .len = 0x3000, }, + { .name = "gcc_qupv3_wrap0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x127000, .pa = 0x127000, .len = 0x3000, }, + { .name = "gcc_gpll7_9", .flags = IOMMU_READ, + .da = 0x12a000, .pa = 0x12a000, .len = 0x3000, }, + { .name = "gcc_qupv3_wrap2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x12e000, .pa = 0x12e000, .len = 0x1000, }, + { .name = "gcc_gpll10", .flags = IOMMU_READ, + .da = 0x12f000, .pa = 0x12f000, .len = 0x1000, }, + { .name = "gcc_boot_rom", .flags = IOMMU_READ, + .da = 0x144000, .pa = 0x144000, .len = 0x1000, }, + { .name = "gcc_boot_rom_ahb", .flags = IOMMU_READ, + .da = 0x148000, .pa = 0x148000, .len = 0x1000, }, + { .name = "gcc_turing0_cfg", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x149000, .pa = 0x149000, .len = 0xc000, }, + { .name = "gcc_turing0_gdscr", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x155000, .pa = 0x155000, .len = 0x1000, }, + { .name = "gcc_turing_dsp_vote", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x16b000, .pa = 0x16b000, .len = 0x1000, }, + { .name = "gcc_clk_frq_measure", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x172000, .pa = 0x172000, .len = 0x1000, }, + { .name = "gcc_turing0_pll_vote", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x173000, .pa = 0x173000, .len = 0x11000, }, + { .name = "gcc_gpll5", .flags = IOMMU_READ, + .da = 0x184000, .pa = 0x184000, .len = 0x1000, }, + { .name = "gcc_gpll4", .flags = IOMMU_READ, + .da = 0x186000, .pa = 0x186000, .len = 0x1000, }, + { .name = "gcc_turing1_pll_vote", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x187000, .pa = 0x187000, .len = 0x45000, }, + { .name = "gcc_turing1_gdscr", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1cc000, .pa = 0x1cc000, .len = 0x1000, }, + { .name = "mproc_ipc_cfg", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x406000, .pa = 0x406000, .len = 0x1000, }, + { .name = "mproc_ipc", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x412000, .pa = 0x412000, .len = 0x1000, }, + { .name = "computel0_ipc_cfg", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x446000, .pa = 0x446000, .len = 0x1000, }, + { .name = "computel0_ipc", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x452000, .pa = 0x452000, .len = 0x1000, }, + { .name = "computel1_ipc_cfg", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x486000, .pa = 0x486000, .len = 0x1000, }, + { .name = "computel1_ipc", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x492000, .pa = 0x492000, .len = 0x1000, }, + { .name = "periph_ipc0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4c6000, .pa = 0x4c6000, .len = 0x1000, }, + { .name = "periph_ipc1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4d2000, .pa = 0x4d2000, .len = 0x1000, }, + { .name = "ipc_mode", .flags = IOMMU_READ, + .da = 0x4fc000, .pa = 0x4fc000, .len = 0x1000, }, + { .name = "nsp_cprf_cprc", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x63e000, .pa = 0x63e000, .len = 0x4000, }, + { .name = "oem_fuses", .flags = IOMMU_READ, + .da = 0x780000, .pa = 0x780000, .len = 0x10000, }, + { .name = "hwkm_prng_cm", .flags = IOMMU_READ, + .da = 0x10d0000, .pa = 0x10d0000, .len = 0x1000, }, + { .name = "hwkm_prng_ee10", .flags = IOMMU_READ, + .da = 0x10da000, .pa = 0x10da000, .len = 0x1000, }, + { .name = "qos_usb_ufs_pcie", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x16c2000, .pa = 0x16c2000, .len = 0x9000, }, + { .name = "qos_ufs_ipa_crypto", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1703000, .pa = 0x1703000, .len = 0x5000, }, + { .name = "hw_mutex", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1f40000, .pa = 0x1f40000, .len = 0x40000, }, + { .name = "tcsr_ro", .flags = IOMMU_READ, + .da = 0x1fc0000, .pa = 0x1fc0000, .len = 0x2b000, }, + { .name = "tcsr_timeout_intr", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1feb000, .pa = 0x1feb000, .len = 0x1000, }, + { .name = "tcsr_ro2", .flags = IOMMU_READ, + .da = 0x1fec000, .pa = 0x1fec000, .len = 0x4000, }, + { .name = "tcsr_spare_rg63", .flags = IOMMU_READ, + .da = 0x1fff000, .pa = 0x1fff000, .len = 0x1000, }, + { .name = "ddr_llc_info", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9080000, .pa = 0x9080000, .len = 0x1000, }, + { .name = "bwmon0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x90b4000, .pa = 0x90b4000, .len = 0x1000, }, + { .name = "llcc_trp0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9200000, .pa = 0x9200000, .len = 0x28000, }, + { .name = "llcc_trp1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9300000, .pa = 0x9300000, .len = 0x28000, }, + { .name = "llcc_trp2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9400000, .pa = 0x9400000, .len = 0x28000, }, + { .name = "llcc_trp3", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9500000, .pa = 0x9500000, .len = 0x28000, }, + { .name = "llcc_trp4", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9600000, .pa = 0x9600000, .len = 0x28000, }, + { .name = "llcc_trp5", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9700000, .pa = 0x9700000, .len = 0x28000, }, + { .name = "pdc_seq_mem0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xb2c0000, .pa = 0xb2c0000, .len = 0x10000, }, + { .name = "pdc_rsc0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xb2f0000, .pa = 0xb2f0000, .len = 0x10000, }, + { .name = "pdc_seq_mem1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xb4c0000, .pa = 0xb4c0000, .len = 0x10000, }, + { .name = "pdc_rsc1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xb4f0000, .pa = 0xb4f0000, .len = 0x10000, }, + { .name = "rpmh_bcm", .flags = IOMMU_READ, + .da = 0xbbf0000, .pa = 0xbbf0000, .len = 0x2000, }, + { .name = "rpmh_cprf_nsp9", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xc20a000, .pa = 0xc20a000, .len = 0x1000, }, + { .name = "rpmh_cprf_nsp10", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xc20b000, .pa = 0xc20b000, .len = 0x1000, }, + { .name = "mpm", .flags = IOMMU_READ, + .da = 0xc210000, .pa = 0xc210000, .len = 0x80000, }, + { .name = "rpmh_drv_cdsp", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xc330000, .pa = 0xc330000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv0", .flags = IOMMU_READ, + .da = 0xc370000, .pa = 0xc370000, .len = 0x1000, }, + { .name = "rpmh_drv8_aop", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xc380000, .pa = 0xc380000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv1", .flags = IOMMU_READ, + .da = 0xc390000, .pa = 0xc390000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv2", .flags = IOMMU_READ, + .da = 0xc3a0000, .pa = 0xc3a0000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv3", .flags = IOMMU_READ, + .da = 0xc3b0000, .pa = 0xc3b0000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv4", .flags = IOMMU_READ, + .da = 0xc3c0000, .pa = 0xc3c0000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv5", .flags = IOMMU_READ, + .da = 0xc3d0000, .pa = 0xc3d0000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv6", .flags = IOMMU_READ, + .da = 0xc3e0000, .pa = 0xc3e0000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv7", .flags = IOMMU_READ, + .da = 0xc3f0000, .pa = 0xc3f0000, .len = 0x1000, }, + { .name = "pmic_arb_master", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xc400000, .pa = 0xc400000, .len = 0x30000, }, + { .name = "pmic_arb", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xc440000, .pa = 0xc440000, .len = 0x10000, }, + { .name = "pmic_arb_mgpi", .flags = IOMMU_READ, + .da = 0xc460000, .pa = 0xc460000, .len = 0x81000, }, + { .name = "pmic_obs", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xe600000, .pa = 0xe600000, .len = 0x100000, }, + { .name = "spmi_pic", .flags = IOMMU_READ, + .da = 0xe700000, .pa = 0xe700000, .len = 0xa0000, }, + { .name = "tlmm", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xf000000, .pa = 0xf000000, .len = 0x1000000, }, + { .name = "shared_imem", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x146bf000, .pa = 0x146bf000, .len = 0x1000, }, + { .name = "sys_imem", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x146d8000, .pa = 0x146d8000, .len = 0x1000, }, + { .name = "stm_trace", .flags = IOMMU_WRITE, + .da = 0x16000000, .pa = 0x16000000, .len = 0x1000000, }, + { .name = "emac1_ptp", .flags = IOMMU_READ, + .da = 0x23007000, .pa = 0x23007000, .len = 0x1000, }, + { .name = "emac0_ptp", .flags = IOMMU_READ, + .da = 0x23047000, .pa = 0x23047000, .len = 0x1000, }, + { .name = "emac_sgmii", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x2304e000, .pa = 0x2304e000, .len = 0x1000, }, + { .name = "nsp_7806", .flags = IOMMU_READ, + .da = 0x90860000, .pa = 0x90860000, .len = 0x20000, }, + /* + * SMEM holds the GLINK FIFOs the DSP uses to talk to the apps + * processor; without this mapping the DSP faults on its first + * SMEM access. + */ + { .name = "smem", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x90900000, .pa = 0x90900000, .len = 0x200000, }, +}; + +DEFINE_RESOURCE_TABLE(TURING1, ARRAY_SIZE(turing_mem_res)); + +#define BOOT_FSM_TIMEOUT 10000 + +static TEE_Result cdsp1_fw_start(struct qcom_pas_data *data) +{ + vaddr_t base = io_pa_or_va(&data->base, data->size); + uint64_t timeout = timeout_init_us(BOOT_FSM_TIMEOUT); + + /* QDSPV73SS out of reset sequence */ + io_setbits32(base + TURING_QDSP6SS_CORE_CBCR, CBCR_BRANCH_ENABLE_BIT); + io_setbits32(base + TURING_Q6SS_Q6_AXIM_CBCR, CBCR_HW_CTL_ENABLE_BIT); + io_setbits32(base + TURING_Q6SS_AXIS2_CBCR, CBCR_HW_CTL_ENABLE_BIT); + io_setbits32(base + TURING_NSPAUX_XO_CBCR, CBCR_BRANCH_ENABLE_BIT); + + /* Program firmware */ + io_write32(base + TURING_QDSP6SS_RST_EVB, data->fw_base >> 4); + dsb(); + + /* Trigger the boot FSM. The core itself is released later, after the + * Q6 PLL is configured, by qcom_clock_enable_pas_processor(). + */ + io_write32(base + TURING_QDSP6SS_BOOT_CMD, 0x1); + + while (!timeout_elapsed(timeout)) { + if (io_read32(base + TURING_QDSP6SS_BOOT_STATUS) & BIT(0)) + return TEE_SUCCESS; + + udelay(10); + } + + return TEE_ERROR_TIMEOUT; +} + +static TEE_Result cdsp1_fw_shutdown(struct qcom_pas_data *data) +{ + return qcom_clock_pas_reset(data->clk_group); +} + +static TEE_Result cdsp1_get_resource_table(struct resource_table *rt, + size_t *rt_size) +{ + const struct fw_rsc_hdr header = { + .type = RSC_DEVMEM, + }; + static struct resource_table table = { + .ver = 1, + .num = TURING1_NUM_MEM_RESOURCES, + .offset[TURING1_NUM_MEM_RESOURCES - 1] = 0, + }; + + return get_mem_rsc(rt, rt_size, &table, &header, + turing_mem_res, + TURING1_RESOURCE_TABLE_HEADER_SIZE, + TURING1_RESOURCE_TABLE_SIZE); +} + +const struct qcom_pas_ops cdsp1_ops = { + .fw_start = cdsp1_fw_start, + .fw_shutdown = cdsp1_fw_shutdown, + .get_resource_table = cdsp1_get_resource_table, +}; diff --git a/core/pta/qcom/pas/platform/lemans/cdsp1.h b/core/pta/qcom/pas/platform/lemans/cdsp1.h new file mode 100644 index 000000000..18988bb20 --- /dev/null +++ b/core/pta/qcom/pas/platform/lemans/cdsp1.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _CDSP1_H_ +#define _CDSP1_H_ + +#include "pas_subsys.h" + +extern const struct qcom_pas_ops cdsp1_ops; + +#endif /* _CDSP1_H_ */ diff --git a/core/pta/qcom/pas/platform/lemans/gpdsp0.c b/core/pta/qcom/pas/platform/lemans/gpdsp0.c new file mode 100644 index 000000000..c4a636e6b --- /dev/null +++ b/core/pta/qcom/pas/platform/lemans/gpdsp0.c @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include + +#include "gpdsp0.h" + +#define GPDSP_QDSP6SS_RST_EVB 0x00c00010 +#define GPDSP_QDSP6SS_BOOT_CORE_START 0x00c00400 +#define GPDSP_QDSP6SS_BOOT_CMD 0x00c00404 +#define GPDSP_QDSP6SS_BOOT_STATUS 0x00c00408 + +static const struct fw_rsc_devmem turing_mem_res[] = { + { .name = "gcc_gpll0_3", .flags = IOMMU_READ, + .da = 0x110000, .pa = 0x110000, .len = 0x4000, }, + { .name = "gcc_gpll6", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x123000, .pa = 0x123000, .len = 0x1000, }, + { .name = "gcc_qupv3_wrap1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x124000, .pa = 0x124000, .len = 0x3000, }, + { .name = "gcc_qupv3_wrap0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x127000, .pa = 0x127000, .len = 0x3000, }, + { .name = "gcc_gpll7_9", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x12a000, .pa = 0x12a000, .len = 0x3000, }, + { .name = "gcc_qupv3_wrap2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x12e000, .pa = 0x12e000, .len = 0x1000, }, + { .name = "gcc_gpll10", .flags = IOMMU_READ, + .da = 0x12f000, .pa = 0x12f000, .len = 0x1000, }, + { .name = "gcc_boot_rom", .flags = IOMMU_READ, + .da = 0x144000, .pa = 0x144000, .len = 0x1000, }, + { .name = "gcc_boot_rom_ahb", .flags = IOMMU_READ, + .da = 0x148000, .pa = 0x148000, .len = 0x1000, }, + { .name = "gcc_gpdsp0_gdscr", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x155000, .pa = 0x155000, .len = 0x1000, }, + { .name = "gcc_gpdsp0_pll_vote", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x156000, .pa = 0x156000, .len = 0x15000, }, + { .name = "gcc_gpdsp_dsp_vote", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x16b000, .pa = 0x16b000, .len = 0x1000, }, + { .name = "gcc_clk_frq_measure", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x172000, .pa = 0x172000, .len = 0x1000, }, + { .name = "gcc_gpll5", .flags = IOMMU_READ, + .da = 0x184000, .pa = 0x184000, .len = 0x1000, }, + { .name = "gcc_gpll4", .flags = IOMMU_READ, + .da = 0x186000, .pa = 0x186000, .len = 0x1000, }, + { .name = "gcc_gpdsp0_cfg", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1c4000, .pa = 0x1c4000, .len = 0x1000, }, + { .name = "gcc_gpdsp1_gdscr", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1cc000, .pa = 0x1cc000, .len = 0x1000, }, + { .name = "mproc_ipc_cfg", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x406000, .pa = 0x406000, .len = 0x1000, }, + { .name = "mproc_ipc", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x412000, .pa = 0x412000, .len = 0x1000, }, + { .name = "mproc_ipc1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x41f000, .pa = 0x41f000, .len = 0x1000, }, + { .name = "computel0_ipc_cfg", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x446000, .pa = 0x446000, .len = 0x1000, }, + { .name = "computel0_ipc", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x452000, .pa = 0x452000, .len = 0x1000, }, + { .name = "computel0_ipc1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x45f000, .pa = 0x45f000, .len = 0x1000, }, + { .name = "computel1_ipc_cfg", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x486000, .pa = 0x486000, .len = 0x1000, }, + { .name = "computel1_ipc", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x492000, .pa = 0x492000, .len = 0x1000, }, + { .name = "computel1_ipc1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x49f000, .pa = 0x49f000, .len = 0x1000, }, + { .name = "periph_ipc1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4d2000, .pa = 0x4d2000, .len = 0x1000, }, + { .name = "periph_ipc2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4df000, .pa = 0x4df000, .len = 0x1000, }, + { .name = "ipc_mode", .flags = IOMMU_READ, + .da = 0x4fc000, .pa = 0x4fc000, .len = 0x1000, }, + { .name = "oem_fuses", .flags = IOMMU_READ, + .da = 0x780000, .pa = 0x780000, .len = 0x10000, }, + { .name = "hwkm_prng_cm", .flags = IOMMU_READ, + .da = 0x10d0000, .pa = 0x10d0000, .len = 0x1000, }, + { .name = "hwkm_prng_ee7", .flags = IOMMU_READ, + .da = 0x10d7000, .pa = 0x10d7000, .len = 0x1000, }, + { .name = "qos_usb_ufs_pcie", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x16c2000, .pa = 0x16c2000, .len = 0x9000, }, + { .name = "qos_ufs_ipa_crypto", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1703000, .pa = 0x1703000, .len = 0x5000, }, + { .name = "qos_misc", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x178d000, .pa = 0x178d000, .len = 0x1000, }, + { .name = "hw_mutex", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1f40000, .pa = 0x1f40000, .len = 0x40000, }, + { .name = "tcsr_rw", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1fc0000, .pa = 0x1fc0000, .len = 0x9000, }, + { .name = "tcsr_ro", .flags = IOMMU_READ, + .da = 0x1fc9000, .pa = 0x1fc9000, .len = 0x22000, }, + { .name = "tcsr_timeout_intr", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1feb000, .pa = 0x1feb000, .len = 0x1000, }, + { .name = "tcsr_ro2", .flags = IOMMU_READ, + .da = 0x1fec000, .pa = 0x1fec000, .len = 0x4000, }, + { .name = "tcsr_spare", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1ffb000, .pa = 0x1ffb000, .len = 0x1000, }, + { .name = "lpass_lpm0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x3140000, .pa = 0x3140000, .len = 0x29000, }, + { .name = "lpass_lpm1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x3180000, .pa = 0x3180000, .len = 0x29000, }, + { .name = "lpass_lpm2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x3260000, .pa = 0x3260000, .len = 0x10000, }, + { .name = "lpass_lpm3", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x3280000, .pa = 0x3280000, .len = 0x29000, }, + { .name = "lpass_lpm4", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x32c0000, .pa = 0x32c0000, .len = 0x29000, }, + { .name = "lpass_lpm5", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x33c0000, .pa = 0x33c0000, .len = 0x29000, }, + { .name = "lpass_lpm6", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x33ec000, .pa = 0x33ec000, .len = 0x3000, }, + { .name = "lpass_lpm7", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x3a10000, .pa = 0x3a10000, .len = 0x8000, }, + { .name = "lpass_lpm8", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x3b00000, .pa = 0x3b00000, .len = 0x29000, }, + { .name = "lpass_lpm9", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x3b40000, .pa = 0x3b40000, .len = 0x29000, }, + { .name = "lpass_core0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4001000, .pa = 0x4001000, .len = 0x1000, }, + { .name = "lpass_core1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4002000, .pa = 0x4002000, .len = 0x1000, }, + { .name = "lpass_core2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4041000, .pa = 0x4041000, .len = 0x1000, }, + { .name = "lpass_core3", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4042000, .pa = 0x4042000, .len = 0x1000, }, + { .name = "lpass_core4", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4043000, .pa = 0x4043000, .len = 0x1000, }, + { .name = "lpass_core5", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4045000, .pa = 0x4045000, .len = 0x1000, }, + { .name = "lpass_core6", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4046000, .pa = 0x4046000, .len = 0x1000, }, + { .name = "lpass_core7", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4048000, .pa = 0x4048000, .len = 0x1000, }, + { .name = "ddr_llc_info", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9080000, .pa = 0x9080000, .len = 0x1000, }, + { .name = "llcc_trp0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9200000, .pa = 0x9200000, .len = 0x28000, }, + { .name = "llcc_trp1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9300000, .pa = 0x9300000, .len = 0x28000, }, + { .name = "llcc_trp2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9400000, .pa = 0x9400000, .len = 0x28000, }, + { .name = "llcc_trp3", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9500000, .pa = 0x9500000, .len = 0x28000, }, + { .name = "llcc_trp4", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9600000, .pa = 0x9600000, .len = 0x28000, }, + { .name = "llcc_trp5", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9700000, .pa = 0x9700000, .len = 0x28000, }, + { .name = "llcc_trp6", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9a00000, .pa = 0x9a00000, .len = 0x28000, }, + { .name = "pdc_seq_mem_a", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xb240000, .pa = 0xb240000, .len = 0x10000, }, + { .name = "pdc_seq_mem0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xb2c0000, .pa = 0xb2c0000, .len = 0x10000, }, + { .name = "pdc_rsc0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xb2f0000, .pa = 0xb2f0000, .len = 0x10000, }, + { .name = "pdc_seq_mem1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xb440000, .pa = 0xb440000, .len = 0x10000, }, + { .name = "pdc_seq_mem2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xb4c0000, .pa = 0xb4c0000, .len = 0x10000, }, + { .name = "pdc_rsc1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xb4f0000, .pa = 0xb4f0000, .len = 0x10000, }, + { .name = "rpmh_bcm", .flags = IOMMU_READ, + .da = 0xbbf0000, .pa = 0xbbf0000, .len = 0x2000, }, + { .name = "mpm", .flags = IOMMU_READ, + .da = 0xc210000, .pa = 0xc210000, .len = 0x80000, }, + { .name = "rpmh_drv_gpdsp", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xc330000, .pa = 0xc330000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xc350000, .pa = 0xc350000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv1", .flags = IOMMU_READ, + .da = 0xc370000, .pa = 0xc370000, .len = 0x1000, }, + { .name = "rpmh_drv8_aop", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xc380000, .pa = 0xc380000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv2", .flags = IOMMU_READ, + .da = 0xc390000, .pa = 0xc390000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv3", .flags = IOMMU_READ, + .da = 0xc3a0000, .pa = 0xc3a0000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv4", .flags = IOMMU_READ, + .da = 0xc3b0000, .pa = 0xc3b0000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv5", .flags = IOMMU_READ, + .da = 0xc3c0000, .pa = 0xc3c0000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv6", .flags = IOMMU_READ, + .da = 0xc3d0000, .pa = 0xc3d0000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv7", .flags = IOMMU_READ, + .da = 0xc3e0000, .pa = 0xc3e0000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv8", .flags = IOMMU_READ, + .da = 0xc3f0000, .pa = 0xc3f0000, .len = 0x1000, }, + { .name = "pmic_arb_master", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xc400000, .pa = 0xc400000, .len = 0x30000, }, + { .name = "pmic_arb", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xc440000, .pa = 0xc440000, .len = 0x10000, }, + { .name = "pmic_arb_mgpi", .flags = IOMMU_READ, + .da = 0xc460000, .pa = 0xc460000, .len = 0x81000, }, + { .name = "pmic_obs", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xe600000, .pa = 0xe600000, .len = 0x100000, }, + { .name = "spmi_pic", .flags = IOMMU_READ, + .da = 0xe700000, .pa = 0xe700000, .len = 0xa0000, }, + { .name = "tlmm", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xf000000, .pa = 0xf000000, .len = 0x1000000, }, + { .name = "sys_imem", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x146d8000, .pa = 0x146d8000, .len = 0x1000, }, + { .name = "stm_trace", .flags = IOMMU_WRITE, + .da = 0x16000000, .pa = 0x16000000, .len = 0x1000000, }, + /* GP-DSP0 exclusive DDR (RW to GPDSP0 only) */ + { .name = "gpdsp0_ddr", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x20000000, .pa = 0x20000000, .len = 0x1000000, }, + { .name = "emac0", .flags = IOMMU_READ, + .da = 0x23000000, .pa = 0x23000000, .len = 0x30000, }, + { .name = "emac1", .flags = IOMMU_READ, + .da = 0x23040000, .pa = 0x23040000, .len = 0x30000, }, + { .name = "nsp_7806", .flags = IOMMU_READ, + .da = 0x90860000, .pa = 0x90860000, .len = 0x20000, }, + /* SMEM (smem@90900000, 2 MB) holds the GLINK FIFOs the DSP must + * read/write to talk to the apps processor. Without this mapping the + * DSP takes a translation fault on its first SMEM access (e.g. iova + * 0x90aff330) -> mmufault/watchdog and never reaches running. + */ + { .name = "smem", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x90900000, .pa = 0x90900000, .len = 0x200000, }, +}; + +DEFINE_RESOURCE_TABLE(GPDSP0, ARRAY_SIZE(turing_mem_res)); + +#define BOOT_FSM_TIMEOUT 10000 + +static TEE_Result gpdsp0_fw_start(struct qcom_pas_data *data) +{ + vaddr_t base = io_pa_or_va(&data->base, data->size); + uint64_t timeout = timeout_init_us(BOOT_FSM_TIMEOUT); + + /* Program firmware entry; clocks/PLL are set up by the clock driver. */ + io_write32(base + GPDSP_QDSP6SS_RST_EVB, data->fw_base >> 4); + dsb(); + + /* + * De-assert stop-core, then trigger the boot FSM. The Q6 PLL and core + * RCG were already configured by gpdsp_setup() in the clock driver. + */ + io_setbits32(base + GPDSP_QDSP6SS_BOOT_CORE_START, BIT(0)); + udelay(5); + io_write32(base + GPDSP_QDSP6SS_BOOT_CMD, 0x1); + + while (!timeout_elapsed(timeout)) { + if (io_read32(base + GPDSP_QDSP6SS_BOOT_STATUS) & BIT(0)) + return TEE_SUCCESS; + + udelay(10); + } + + return TEE_ERROR_TIMEOUT; +} + +static TEE_Result gpdsp0_fw_shutdown(struct qcom_pas_data *data) +{ + return qcom_clock_pas_reset(data->clk_group); +} + +static TEE_Result gpdsp0_get_resource_table(struct resource_table *rt, + size_t *rt_size) +{ + const struct fw_rsc_hdr header = { + .type = RSC_DEVMEM, + }; + static struct resource_table table = { + .ver = 1, + .num = GPDSP0_NUM_MEM_RESOURCES, + .offset[GPDSP0_NUM_MEM_RESOURCES - 1] = 0, + }; + + return get_mem_rsc(rt, rt_size, &table, &header, + turing_mem_res, + GPDSP0_RESOURCE_TABLE_HEADER_SIZE, + GPDSP0_RESOURCE_TABLE_SIZE); +} + +const struct qcom_pas_ops gpdsp0_ops = { + .fw_start = gpdsp0_fw_start, + .fw_shutdown = gpdsp0_fw_shutdown, + .get_resource_table = gpdsp0_get_resource_table, +}; diff --git a/core/pta/qcom/pas/platform/lemans/gpdsp0.h b/core/pta/qcom/pas/platform/lemans/gpdsp0.h new file mode 100644 index 000000000..cbcceab92 --- /dev/null +++ b/core/pta/qcom/pas/platform/lemans/gpdsp0.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _GPDSP0_H_ +#define _GPDSP0_H_ + +#include "pas_subsys.h" + +extern const struct qcom_pas_ops gpdsp0_ops; + +#endif /* _GPDSP0_H_ */ diff --git a/core/pta/qcom/pas/platform/lemans/gpdsp1.c b/core/pta/qcom/pas/platform/lemans/gpdsp1.c new file mode 100644 index 000000000..c2d4303ca --- /dev/null +++ b/core/pta/qcom/pas/platform/lemans/gpdsp1.c @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include + +#include "gpdsp1.h" + +#define GPDSP_QDSP6SS_RST_EVB 0x00c00010 +#define GPDSP_QDSP6SS_BOOT_CORE_START 0x00c00400 +#define GPDSP_QDSP6SS_BOOT_CMD 0x00c00404 +#define GPDSP_QDSP6SS_BOOT_STATUS 0x00c00408 + +static const struct fw_rsc_devmem turing_mem_res[] = { + { .name = "gcc_gpll0_3", .flags = IOMMU_READ, + .da = 0x110000, .pa = 0x110000, .len = 0x4000, }, + { .name = "gcc_gpll6", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x123000, .pa = 0x123000, .len = 0x1000, }, + { .name = "gcc_qupv3_wrap1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x124000, .pa = 0x124000, .len = 0x3000, }, + { .name = "gcc_qupv3_wrap0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x127000, .pa = 0x127000, .len = 0x3000, }, + { .name = "gcc_gpll7_9", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x12a000, .pa = 0x12a000, .len = 0x3000, }, + { .name = "gcc_qupv3_wrap2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x12e000, .pa = 0x12e000, .len = 0x1000, }, + { .name = "gcc_gpll10", .flags = IOMMU_READ, + .da = 0x12f000, .pa = 0x12f000, .len = 0x1000, }, + { .name = "gcc_boot_rom", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x144000, .pa = 0x144000, .len = 0x1000, }, + { .name = "gcc_boot_rom_ahb", .flags = IOMMU_READ, + .da = 0x148000, .pa = 0x148000, .len = 0x1000, }, + { .name = "gcc_gpdsp0_gdscr", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x155000, .pa = 0x155000, .len = 0x1000, }, + { .name = "gcc_gpdsp_dsp_vote", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x16b000, .pa = 0x16b000, .len = 0x1000, }, + { .name = "gcc_clk_frq_measure", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x172000, .pa = 0x172000, .len = 0x1000, }, + { .name = "gcc_gpdsp1_pll_vote", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x173000, .pa = 0x173000, .len = 0x11000, }, + { .name = "gcc_gpll5", .flags = IOMMU_READ, + .da = 0x184000, .pa = 0x184000, .len = 0x1000, }, + { .name = "gcc_gpll4", .flags = IOMMU_READ, + .da = 0x186000, .pa = 0x186000, .len = 0x1000, }, + { .name = "gcc_gpdsp1_cfg", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1c4000, .pa = 0x1c4000, .len = 0x1000, }, + { .name = "gcc_gpdsp1_gdscr", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1cc000, .pa = 0x1cc000, .len = 0x1000, }, + { .name = "mproc_ipc_cfg", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x406000, .pa = 0x406000, .len = 0x1000, }, + { .name = "mproc_ipc", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x412000, .pa = 0x412000, .len = 0x1000, }, + { .name = "mproc_ipc2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x41f000, .pa = 0x41f000, .len = 0x1000, }, + { .name = "mproc_ipc3", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x420000, .pa = 0x420000, .len = 0x1000, }, + { .name = "computel0_ipc_cfg", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x446000, .pa = 0x446000, .len = 0x1000, }, + { .name = "computel0_ipc", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x452000, .pa = 0x452000, .len = 0x1000, }, + { .name = "computel0_ipc2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x45f000, .pa = 0x45f000, .len = 0x1000, }, + { .name = "computel0_ipc3", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x460000, .pa = 0x460000, .len = 0x26000, }, + { .name = "computel1_ipc_cfg", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x486000, .pa = 0x486000, .len = 0x1000, }, + { .name = "computel1_ipc", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x492000, .pa = 0x492000, .len = 0x1000, }, + { .name = "computel1_ipc2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x49f000, .pa = 0x49f000, .len = 0x1000, }, + { .name = "computel1_ipc3", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4a0000, .pa = 0x4a0000, .len = 0x32000, }, + { .name = "periph_ipc1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4d2000, .pa = 0x4d2000, .len = 0x1000, }, + { .name = "periph_ipc2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4df000, .pa = 0x4df000, .len = 0x1000, }, + { .name = "periph_ipc3", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4e0000, .pa = 0x4e0000, .len = 0x1c000, }, + { .name = "ipc_mode", .flags = IOMMU_READ, + .da = 0x4fc000, .pa = 0x4fc000, .len = 0x1000, }, + { .name = "oem_fuses", .flags = IOMMU_READ, + .da = 0x780000, .pa = 0x780000, .len = 0x10000, }, + { .name = "hwkm_prng_cm", .flags = IOMMU_READ, + .da = 0x10d0000, .pa = 0x10d0000, .len = 0x1000, }, + { .name = "hwkm_prng_ee", .flags = IOMMU_READ, + .da = 0x10d9000, .pa = 0x10d9000, .len = 0x1000, }, + { .name = "qos_usb_ufs_pcie", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x16c2000, .pa = 0x16c2000, .len = 0x9000, }, + { .name = "qos_ufs_ipa_crypto", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1703000, .pa = 0x1703000, .len = 0x5000, }, + { .name = "qos_misc", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x178e000, .pa = 0x178e000, .len = 0x1000, }, + { .name = "hw_mutex", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1f40000, .pa = 0x1f40000, .len = 0x40000, }, + { .name = "tcsr_regs", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1fc0000, .pa = 0x1fc0000, .len = 0x9000, }, + { .name = "tcsr_ro", .flags = IOMMU_READ, + .da = 0x1fc9000, .pa = 0x1fc9000, .len = 0x22000, }, + { .name = "tcsr_timeout_intr", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1feb000, .pa = 0x1feb000, .len = 0x1000, }, + { .name = "tcsr_ro2", .flags = IOMMU_READ, + .da = 0x1fec000, .pa = 0x1fec000, .len = 0x4000, }, + { .name = "tcsr_spare", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1ffb000, .pa = 0x1ffb000, .len = 0x1000, }, + { .name = "lpass_lpm0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x3140000, .pa = 0x3140000, .len = 0x29000, }, + { .name = "lpass_lpm1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x3180000, .pa = 0x3180000, .len = 0x29000, }, + { .name = "lpass_lpm2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x3260000, .pa = 0x3260000, .len = 0x10000, }, + { .name = "lpass_lpm3", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x3280000, .pa = 0x3280000, .len = 0x29000, }, + { .name = "lpass_lpm4", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x32c0000, .pa = 0x32c0000, .len = 0x29000, }, + { .name = "lpass_lpm5", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x33c0000, .pa = 0x33c0000, .len = 0x29000, }, + { .name = "lpass_lpm6", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x33ec000, .pa = 0x33ec000, .len = 0x3000, }, + { .name = "lpass_lpm7", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x3a10000, .pa = 0x3a10000, .len = 0x8000, }, + { .name = "lpass_lpm8", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x3b00000, .pa = 0x3b00000, .len = 0x29000, }, + { .name = "lpass_lpm9", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x3b40000, .pa = 0x3b40000, .len = 0x29000, }, + { .name = "lpass_core0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4001000, .pa = 0x4001000, .len = 0x1000, }, + { .name = "lpass_core1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4002000, .pa = 0x4002000, .len = 0x1000, }, + { .name = "lpass_core2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4041000, .pa = 0x4041000, .len = 0x1000, }, + { .name = "lpass_core3", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4042000, .pa = 0x4042000, .len = 0x1000, }, + { .name = "lpass_core4", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4043000, .pa = 0x4043000, .len = 0x1000, }, + { .name = "lpass_core5", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4045000, .pa = 0x4045000, .len = 0x1000, }, + { .name = "lpass_core6", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4046000, .pa = 0x4046000, .len = 0x1000, }, + { .name = "lpass_core7", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4048000, .pa = 0x4048000, .len = 0x1000, }, + { .name = "ddr_llc_info", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9080000, .pa = 0x9080000, .len = 0x1000, }, + { .name = "llcc_trp0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9200000, .pa = 0x9200000, .len = 0x28000, }, + { .name = "llcc_trp1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9300000, .pa = 0x9300000, .len = 0x28000, }, + { .name = "llcc_trp2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9400000, .pa = 0x9400000, .len = 0x28000, }, + { .name = "llcc_trp3", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9500000, .pa = 0x9500000, .len = 0x28000, }, + { .name = "llcc_trp4", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9600000, .pa = 0x9600000, .len = 0x28000, }, + { .name = "llcc_trp5", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9700000, .pa = 0x9700000, .len = 0x28000, }, + { .name = "llcc_trp6", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9a00000, .pa = 0x9a00000, .len = 0x28000, }, + { .name = "pdc_seq_mem0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xb2c0000, .pa = 0xb2c0000, .len = 0x10000, }, + { .name = "pdc_gpdsp1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xb2d0000, .pa = 0xb2d0000, .len = 0x10000, }, + { .name = "pdc_rsc0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xb2f0000, .pa = 0xb2f0000, .len = 0x10000, }, + { .name = "pdc_seq_mem1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xb4c0000, .pa = 0xb4c0000, .len = 0x10000, }, + { .name = "pdc_gpdsp1_b", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xb4d0000, .pa = 0xb4d0000, .len = 0x10000, }, + { .name = "pdc_rsc1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xb4f0000, .pa = 0xb4f0000, .len = 0x10000, }, + { .name = "rpmh_bcm", .flags = IOMMU_READ, + .da = 0xbbf0000, .pa = 0xbbf0000, .len = 0x2000, }, + { .name = "mpm", .flags = IOMMU_READ, + .da = 0xc210000, .pa = 0xc210000, .len = 0x80000, }, + { .name = "rpmh_drv_gpdsp", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xc310000, .pa = 0xc310000, .len = 0x1000, }, + { .name = "rpmh_drv_cdsp", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xc330000, .pa = 0xc330000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv0", .flags = IOMMU_READ, + .da = 0xc370000, .pa = 0xc370000, .len = 0x1000, }, + { .name = "rpmh_drv8_aop", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xc380000, .pa = 0xc380000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv1", .flags = IOMMU_READ, + .da = 0xc390000, .pa = 0xc390000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv2", .flags = IOMMU_READ, + .da = 0xc3a0000, .pa = 0xc3a0000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv3", .flags = IOMMU_READ, + .da = 0xc3b0000, .pa = 0xc3b0000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv4", .flags = IOMMU_READ, + .da = 0xc3c0000, .pa = 0xc3c0000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv5", .flags = IOMMU_READ, + .da = 0xc3d0000, .pa = 0xc3d0000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv6", .flags = IOMMU_READ, + .da = 0xc3e0000, .pa = 0xc3e0000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv7", .flags = IOMMU_READ, + .da = 0xc3f0000, .pa = 0xc3f0000, .len = 0x1000, }, + { .name = "pmic_arb_master", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xc400000, .pa = 0xc400000, .len = 0x30000, }, + { .name = "pmic_arb", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xc440000, .pa = 0xc440000, .len = 0x10000, }, + { .name = "pmic_arb_mgpi", .flags = IOMMU_READ, + .da = 0xc460000, .pa = 0xc460000, .len = 0x81000, }, + { .name = "pmic_obs", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xe600000, .pa = 0xe600000, .len = 0x100000, }, + { .name = "spmi_pic", .flags = IOMMU_READ, + .da = 0xe700000, .pa = 0xe700000, .len = 0xa0000, }, + { .name = "tlmm", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xf000000, .pa = 0xf000000, .len = 0x1000000, }, + { .name = "sys_imem", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x146d8000, .pa = 0x146d8000, .len = 0x1000, }, + { .name = "stm_trace", .flags = IOMMU_WRITE, + .da = 0x16000000, .pa = 0x16000000, .len = 0x1000000, }, + /* GP-DSP1 exclusive DDR (RW to GPDSP1 only) */ + { .name = "gpdsp1_ddr", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x21000000, .pa = 0x21000000, .len = 0x1000000, }, + { .name = "emac1", .flags = IOMMU_READ, + .da = 0x23000000, .pa = 0x23000000, .len = 0x30000, }, + { .name = "emac0", .flags = IOMMU_READ, + .da = 0x23040000, .pa = 0x23040000, .len = 0x30000, }, + { .name = "nsp_7806", .flags = IOMMU_READ, + .da = 0x90860000, .pa = 0x90860000, .len = 0x20000, }, + /* SMEM (smem@90900000, 2 MB) holds the GLINK FIFOs the DSP must + * read/write to talk to the apps processor. Without this mapping the + * DSP takes a translation fault on its first SMEM access (e.g. iova + * 0x90aff330) -> mmufault/watchdog and never reaches running. + */ + { .name = "smem", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x90900000, .pa = 0x90900000, .len = 0x200000, }, +}; + +DEFINE_RESOURCE_TABLE(GPDSP1, ARRAY_SIZE(turing_mem_res)); + +#define BOOT_FSM_TIMEOUT 10000 + +static TEE_Result gpdsp1_fw_start(struct qcom_pas_data *data) +{ + vaddr_t base = io_pa_or_va(&data->base, data->size); + uint64_t timeout = timeout_init_us(BOOT_FSM_TIMEOUT); + + /* Program firmware entry; clocks/PLL are set up by the clock driver. */ + io_write32(base + GPDSP_QDSP6SS_RST_EVB, data->fw_base >> 4); + dsb(); + + /* + * De-assert stop-core, then trigger the boot FSM. The Q6 PLL and core + * RCG were already configured by gpdsp_setup() in the clock driver. + */ + io_setbits32(base + GPDSP_QDSP6SS_BOOT_CORE_START, BIT(0)); + udelay(5); + io_write32(base + GPDSP_QDSP6SS_BOOT_CMD, 0x1); + + while (!timeout_elapsed(timeout)) { + if (io_read32(base + GPDSP_QDSP6SS_BOOT_STATUS) & BIT(0)) + return TEE_SUCCESS; + + udelay(10); + } + + return TEE_ERROR_TIMEOUT; +} + +static TEE_Result gpdsp1_fw_shutdown(struct qcom_pas_data *data) +{ + return qcom_clock_pas_reset(data->clk_group); +} + +static TEE_Result gpdsp1_get_resource_table(struct resource_table *rt, + size_t *rt_size) +{ + const struct fw_rsc_hdr header = { + .type = RSC_DEVMEM, + }; + static struct resource_table table = { + .ver = 1, + .num = GPDSP1_NUM_MEM_RESOURCES, + .offset[GPDSP1_NUM_MEM_RESOURCES - 1] = 0, + }; + + return get_mem_rsc(rt, rt_size, &table, &header, + turing_mem_res, + GPDSP1_RESOURCE_TABLE_HEADER_SIZE, + GPDSP1_RESOURCE_TABLE_SIZE); +} + +const struct qcom_pas_ops gpdsp1_ops = { + .fw_start = gpdsp1_fw_start, + .fw_shutdown = gpdsp1_fw_shutdown, + .get_resource_table = gpdsp1_get_resource_table, +}; diff --git a/core/pta/qcom/pas/platform/lemans/gpdsp1.h b/core/pta/qcom/pas/platform/lemans/gpdsp1.h new file mode 100644 index 000000000..60f82121e --- /dev/null +++ b/core/pta/qcom/pas/platform/lemans/gpdsp1.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _GPDSP1_H_ +#define _GPDSP1_H_ + +#include "pas_subsys.h" + +extern const struct qcom_pas_ops gpdsp1_ops; + +#endif /* _GPDSP1_H_ */ diff --git a/core/pta/qcom/pas/platform/lemans/iris.c b/core/pta/qcom/pas/platform/lemans/iris.c new file mode 100644 index 000000000..54f60a162 --- /dev/null +++ b/core/pta/qcom/pas/platform/lemans/iris.c @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include + +#include "iris.h" + +#define WRAPPER_IRIS_VERSION 0x0 +#define WRAPPER_CPA_START_ADDR 0x1020 +#define WRAPPER_CPA_END_ADDR 0x1024 +#define WRAPPER_FW_START_ADDR 0x1028 +#define WRAPPER_FW_END_ADDR 0x102C +#define WRAPPER_NONPIX_START_ADDR 0x1030 +#define WRAPPER_NONPIX_END_ADDR 0x1034 +#define WRAPPER_TZ_XTSS_SW_RESET 0x1000 +#define WRAPPER_XTSS_SW_RESET_BIT BIT(0) + +static TEE_Result iris_reset_cpu(struct qcom_pas_data *data) +{ + vaddr_t base = 0; + + base = io_pa_or_va(&data->base, data->size); + if (!base) + return TEE_ERROR_GENERIC; + + base += IRIS_WRAPPER_TOP_TZ_REG_BASE; + + io_write32(base + WRAPPER_FW_START_ADDR, 0); + io_write32(base + WRAPPER_FW_END_ADDR, data->fw_size); + io_write32(base + WRAPPER_CPA_START_ADDR, 0); + io_write32(base + WRAPPER_CPA_END_ADDR, data->fw_size); + io_write32(base + WRAPPER_NONPIX_START_ADDR, data->fw_size); + io_write32(base + WRAPPER_NONPIX_END_ADDR, data->fw_size); + io_write32(base + WRAPPER_TZ_XTSS_SW_RESET, 0); + + return TEE_SUCCESS; +} + +static TEE_Result iris_fw_shutdown(struct qcom_pas_data *data) +{ + vaddr_t base = 0; + uint32_t reg = 0; + + base = io_pa_or_va(&data->base, data->size); + if (!base) + return TEE_ERROR_GENERIC; + + base += IRIS_WRAPPER_TOP_TZ_REG_BASE; + + reg = io_read32(base + WRAPPER_TZ_XTSS_SW_RESET); + reg |= WRAPPER_XTSS_SW_RESET_BIT; + io_write32(base + WRAPPER_TZ_XTSS_SW_RESET, reg); + + return TEE_SUCCESS; +} + +static TEE_Result iris_fw_start(struct qcom_pas_data *data) +{ + return iris_reset_cpu(data); +} + +static TEE_Result iris_fw_set_state(struct qcom_pas_data *data, bool power_on) +{ + if (power_on) + return iris_reset_cpu(data); + + return iris_fw_shutdown(data); +} + +const struct qcom_pas_ops iris_ops = { + .fw_start = iris_fw_start, + .fw_shutdown = iris_fw_shutdown, + .fw_set_state = iris_fw_set_state, +}; diff --git a/core/pta/qcom/pas/platform/lemans/iris.h b/core/pta/qcom/pas/platform/lemans/iris.h new file mode 100644 index 000000000..b283c3cf0 --- /dev/null +++ b/core/pta/qcom/pas/platform/lemans/iris.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _IRIS_H_ +#define _IRIS_H_ + +#include "pas_subsys.h" + +#define IRIS_WRAPPER_TOP_TZ_REG_BASE 0x000c0000 +#define IRIS_WRAPPER_TOP_REG_BASE 0x000b0000 + +extern const struct qcom_pas_ops iris_ops; + +#endif /* _IRIS_H_ */ diff --git a/core/pta/qcom/pas/platform/lemans/lpass.c b/core/pta/qcom/pas/platform/lemans/lpass.c new file mode 100644 index 000000000..84bf42049 --- /dev/null +++ b/core/pta/qcom/pas/platform/lemans/lpass.c @@ -0,0 +1,290 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include + +#include "lpass.h" + +/* + * QDSP6 boot registers, relative to the LPASS subsystem base. The PUB block + * holds the reset/boot FSM registers; the MCC block holds the EVB select. The + * Q6 PLL and core RCG are configured earlier by the clock driver (lpass_setup). + */ +#define LPASS_PUB_OFFSET 0x00400000 +#define LPASS_MCC_OFFSET 0x008d0000 + +#define LPASS_QDSP6SS_RST_EVB (LPASS_PUB_OFFSET + 0x10) +#define LPASS_QDSP6SS_BOOT_CORE_START (LPASS_PUB_OFFSET + 0x400) +#define LPASS_QDSP6SS_BOOT_CMD (LPASS_PUB_OFFSET + 0x404) +#define LPASS_QDSP6SS_BOOT_STATUS (LPASS_PUB_OFFSET + 0x408) + +#define LPASS_EFUSE_Q6SS_EVB_SEL (LPASS_MCC_OFFSET + 0xb000) + +#define BOOT_FSM_TIMEOUT 10000 + +/* + * LPASS / ADSP (QDSP6) IOMMU device-memory resource table. Regions are + * identity-mapped (da == pa); flags follow the AC_PERM_R / AC_PERM_W bits. + */ +static const struct fw_rsc_devmem lpass_mem_res[] = { + { .name = "gcc_gpll0_3", .flags = IOMMU_READ, + .da = 0x110000, .pa = 0x110000, .len = 0x4000, }, + { .name = "gcc_gpll6", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x123000, .pa = 0x123000, .len = 0x1000, }, + { .name = "gcc_qupv3_wrap1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x124000, .pa = 0x124000, .len = 0x3000, }, + { .name = "gcc_qupv3_wrap0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x127000, .pa = 0x127000, .len = 0x2000, }, + { .name = "gcc_gpll7_9", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x12a000, .pa = 0x12a000, .len = 0x3000, }, + { .name = "gcc_qupv3_wrap2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x12e000, .pa = 0x12e000, .len = 0x1000, }, + { .name = "gcc_gpll10", .flags = IOMMU_READ, + .da = 0x12f000, .pa = 0x12f000, .len = 0x1000, }, + { .name = "gcc_boot_rom_ahb", .flags = IOMMU_READ, + .da = 0x144000, .pa = 0x144000, .len = 0x1000, }, + { .name = "gcc_boot_rom", .flags = IOMMU_READ, + .da = 0x148000, .pa = 0x148000, .len = 0x1000, }, + { .name = "gcc_lpass_cfg0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x149000, .pa = 0x149000, .len = 0xe000, }, + { .name = "gcc_lpass_cbcr", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x157000, .pa = 0x157000, .len = 0x1000, }, + { .name = "gcc_lpass_cfg1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x158000, .pa = 0x158000, .len = 0xd000, }, + { .name = "gcc_lpass_dsp_vote", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x165000, .pa = 0x165000, .len = 0x1000, }, + { .name = "gcc_clk_frq_measure", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x172000, .pa = 0x172000, .len = 0x1000, }, + { .name = "gcc_pll_vote", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x173000, .pa = 0x173000, .len = 0x8000, }, + { .name = "gcc_pcie4", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x17b000, .pa = 0x17b000, .len = 0x2000, }, + { .name = "gcc_pcie_phy", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x17f000, .pa = 0x17f000, .len = 0x1000, }, + { .name = "gcc_gpll5", .flags = IOMMU_READ, + .da = 0x184000, .pa = 0x184000, .len = 0x1000, }, + { .name = "gcc_gpll4", .flags = IOMMU_READ, + .da = 0x186000, .pa = 0x186000, .len = 0x1000, }, + { .name = "gcc_pcie1_tunnel", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x19c000, .pa = 0x19c000, .len = 0x3000, }, + { .name = "gcc_pcie1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x19f000, .pa = 0x19f000, .len = 0xe000, }, + { .name = "gcc_pcie2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1ad000, .pa = 0x1ad000, .len = 0x2000, }, + { .name = "gcc_pcie3a", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1b0000, .pa = 0x1b0000, .len = 0x1000, }, + { .name = "gcc_pcie3b", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1b2000, .pa = 0x1b2000, .len = 0x1000, }, + { .name = "gcc_pcie0_tunnel", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1b4000, .pa = 0x1b4000, .len = 0x1000, }, + { .name = "gcc_pcie_throttle", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1b6000, .pa = 0x1b6000, .len = 0x1000, }, + { .name = "gcc_pcie_rscc", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1be000, .pa = 0x1be000, .len = 0x1000, }, + { .name = "gcc_pcie_rscc_seq", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1bf000, .pa = 0x1bf000, .len = 0x10000, }, + { .name = "gcc_aggre_pcie", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1cf000, .pa = 0x1cf000, .len = 0x1000, }, + { .name = "lpass_ipc_mproc", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x403000, .pa = 0x403000, .len = 0x1000, }, + { .name = "lpass_ipc_computel0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x443000, .pa = 0x443000, .len = 0x1000, }, + { .name = "lpass_ipc_computel1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x483000, .pa = 0x483000, .len = 0x1000, }, + { .name = "lpass_ipc_periph", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4c3000, .pa = 0x4c3000, .len = 0x1000, }, + { .name = "ipc_mode", .flags = IOMMU_READ, + .da = 0x4fc000, .pa = 0x4fc000, .len = 0x1000, }, + { .name = "oem_fuses", .flags = IOMMU_READ, + .da = 0x780000, .pa = 0x780000, .len = 0x7000, }, + { .name = "hwkm_prng_cm", .flags = IOMMU_READ, + .da = 0x10d0000, .pa = 0x10d0000, .len = 0x1000, }, + { .name = "rng", .flags = IOMMU_READ, + .da = 0x10d4000, .pa = 0x10d4000, .len = 0x1000, }, + { .name = "tcsr_dbg0", .flags = IOMMU_READ, + .da = 0x1dc4000, .pa = 0x1dc4000, .len = 0x1000, }, + { .name = "tcsr_dbg1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1dc5000, .pa = 0x1dc5000, .len = 0x1000, }, + { .name = "tcsr_dbg2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1dc8000, .pa = 0x1dc8000, .len = 0x1000, }, + { .name = "tcsr_dbg3", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1dfa000, .pa = 0x1dfa000, .len = 0x1000, }, + { .name = "tcsr_dbg4", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1dfd000, .pa = 0x1dfd000, .len = 0x1000, }, + { .name = "hw_mutex", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1f40000, .pa = 0x1f40000, .len = 0x40000, }, + { .name = "tcsr_ro", .flags = IOMMU_READ, + .da = 0x1fc0000, .pa = 0x1fc0000, .len = 0x29000, }, + { .name = "tcsr_timeout_intr", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x1fe9000, .pa = 0x1fe9000, .len = 0x1000, }, + { .name = "tcsr_ro1", .flags = IOMMU_READ, + .da = 0x1fea000, .pa = 0x1fea000, .len = 0x1000, }, + { .name = "tcsr_ro2", .flags = IOMMU_READ, + .da = 0x1feb000, .pa = 0x1feb000, .len = 0x1000, }, + { .name = "tcsr_ro3", .flags = IOMMU_READ, + .da = 0x1fec000, .pa = 0x1fec000, .len = 0x4000, }, + { .name = "lpass_ag_noc0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x3c41000, .pa = 0x3c41000, .len = 0x3000, }, + { .name = "lpass_ag_noc1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x3c4f000, .pa = 0x3c4f000, .len = 0x1000, }, + { .name = "lpass_core0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4001000, .pa = 0x4001000, .len = 0x1000, }, + { .name = "lpass_core1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4002000, .pa = 0x4002000, .len = 0x1000, }, + { .name = "lpass_core2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4041000, .pa = 0x4041000, .len = 0x1000, }, + { .name = "lpass_core3", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4042000, .pa = 0x4042000, .len = 0x1000, }, + { .name = "lpass_core4", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4043000, .pa = 0x4043000, .len = 0x1000, }, + { .name = "lpass_core5", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4045000, .pa = 0x4045000, .len = 0x1000, }, + { .name = "lpass_core6", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4046000, .pa = 0x4046000, .len = 0x1000, }, + { .name = "lpass_core7", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x404e000, .pa = 0x404e000, .len = 0x1000, }, + { .name = "lpass_core8", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x404f000, .pa = 0x404f000, .len = 0x1000, }, + { .name = "lpass_qdss0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4b04000, .pa = 0x4b04000, .len = 0x1000, }, + { .name = "lpass_qdss1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4b05000, .pa = 0x4b05000, .len = 0x1000, }, + { .name = "lpass_qdss2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4b11000, .pa = 0x4b11000, .len = 0x1000, }, + { .name = "lpass_island_dbg", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x4b40000, .pa = 0x4b40000, .len = 0x20000, }, + { .name = "ddr_llc_info", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9080000, .pa = 0x9080000, .len = 0x1000, }, + { .name = "llcc_trp0", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9200000, .pa = 0x9200000, .len = 0x28000, }, + { .name = "llcc_trp1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9300000, .pa = 0x9300000, .len = 0x28000, }, + { .name = "llcc_trp2", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9400000, .pa = 0x9400000, .len = 0x28000, }, + { .name = "llcc_trp3", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9500000, .pa = 0x9500000, .len = 0x28000, }, + { .name = "llcc_trp4", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9600000, .pa = 0x9600000, .len = 0x28000, }, + { .name = "llcc_trp5", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x9700000, .pa = 0x9700000, .len = 0x28000, }, + { .name = "pdc_audio", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xb250000, .pa = 0xb250000, .len = 0x10000, }, + { .name = "pdc_audio1", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xb450000, .pa = 0xb450000, .len = 0x10000, }, + { .name = "rpmh_bcm", .flags = IOMMU_READ, + .da = 0xbbf0000, .pa = 0xbbf0000, .len = 0x2000, }, + { .name = "mpm", .flags = IOMMU_READ, + .da = 0xc210000, .pa = 0xc210000, .len = 0x80000, }, + { .name = "rpmh_drv_lpass", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xc340000, .pa = 0xc340000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv0", .flags = IOMMU_READ, + .da = 0xc370000, .pa = 0xc370000, .len = 0x1000, }, + { .name = "rpmh_drv8_aop", .flags = IOMMU_READ, + .da = 0xc380000, .pa = 0xc380000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv1", .flags = IOMMU_READ, + .da = 0xc390000, .pa = 0xc390000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv2", .flags = IOMMU_READ, + .da = 0xc3a0000, .pa = 0xc3a0000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv3", .flags = IOMMU_READ, + .da = 0xc3b0000, .pa = 0xc3b0000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv4", .flags = IOMMU_READ, + .da = 0xc3c0000, .pa = 0xc3c0000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv5", .flags = IOMMU_READ, + .da = 0xc3d0000, .pa = 0xc3d0000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv6", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xc3e0000, .pa = 0xc3e0000, .len = 0x1000, }, + { .name = "rpmh_drv_rsv7", .flags = IOMMU_READ, + .da = 0xc3f0000, .pa = 0xc3f0000, .len = 0x1000, }, + { .name = "pmic_arb_master", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xc400000, .pa = 0xc400000, .len = 0x30000, }, + { .name = "pmic_arb", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xc440000, .pa = 0xc440000, .len = 0x10000, }, + { .name = "pmic_arb_mgpi", .flags = IOMMU_READ, + .da = 0xc460000, .pa = 0xc460000, .len = 0x81000, }, + { .name = "pmic_obs", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xe600000, .pa = 0xe600000, .len = 0x100000, }, + { .name = "spmi_pic", .flags = IOMMU_READ, + .da = 0xe700000, .pa = 0xe700000, .len = 0xa0000, }, + { .name = "tlmm", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0xf000000, .pa = 0xf000000, .len = 0x1000000, }, + { .name = "sys_imem", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x146d8000, .pa = 0x146d8000, .len = 0x1000, }, + { .name = "stm_trace", .flags = IOMMU_WRITE, + .da = 0x16000000, .pa = 0x16000000, .len = 0x1000000, }, + { .name = "aop_cmd_db", .flags = IOMMU_READ, + .da = 0x90860000, .pa = 0x90860000, .len = 0x20000, }, + /* SMEM (smem@90900000, 2 MB) holds the GLINK FIFOs the DSP must + * read/write to talk to the apps processor. Without this mapping the + * DSP takes a translation fault on its first SMEM access (e.g. iova + * 0x90aff330) -> mmufault/watchdog and never reaches running. + */ + { .name = "smem", .flags = IOMMU_READ | IOMMU_WRITE, + .da = 0x90900000, .pa = 0x90900000, .len = 0x200000, }, +}; + +DEFINE_RESOURCE_TABLE(LPASS, ARRAY_SIZE(lpass_mem_res)); + +static TEE_Result lpass_fw_start(struct qcom_pas_data *data) +{ + vaddr_t base = io_pa_or_va(&data->base, data->size); + uint64_t timeout = timeout_init_us(BOOT_FSM_TIMEOUT); + + if (!base) + return TEE_ERROR_GENERIC; + + /* + * Program the firmware entry address and select the programmed EVB; + * the Q6 PLL and core RCG are already configured by lpass_setup(). + */ + io_write32(base + LPASS_QDSP6SS_RST_EVB, data->fw_base >> 4); + io_write32(base + LPASS_EFUSE_Q6SS_EVB_SEL, 0); + dsb(); + + /* De-assert stop-core, then trigger the boot FSM. */ + io_setbits32(base + LPASS_QDSP6SS_BOOT_CORE_START, BIT(0)); + io_write32(base + LPASS_QDSP6SS_BOOT_CMD, 0x1); + + while (!timeout_elapsed(timeout)) { + if (io_read32(base + LPASS_QDSP6SS_BOOT_STATUS) & BIT(0)) + return TEE_SUCCESS; + + udelay(10); + } + + return TEE_ERROR_TIMEOUT; +} + +static TEE_Result lpass_fw_shutdown(struct qcom_pas_data *data __unused) +{ + return TEE_ERROR_NOT_IMPLEMENTED; +} + +static TEE_Result lpass_get_resource_table(struct resource_table *rt, + size_t *rt_size) +{ + const struct fw_rsc_hdr header = { + .type = RSC_DEVMEM, + }; + static struct resource_table table = { + .ver = 1, + .num = LPASS_NUM_MEM_RESOURCES, + .offset[LPASS_NUM_MEM_RESOURCES - 1] = 0, + }; + + return get_mem_rsc(rt, rt_size, &table, &header, + lpass_mem_res, + LPASS_RESOURCE_TABLE_HEADER_SIZE, + LPASS_RESOURCE_TABLE_SIZE); +} + +const struct qcom_pas_ops lpass_ops = { + .fw_start = lpass_fw_start, + .fw_shutdown = lpass_fw_shutdown, + .get_resource_table = lpass_get_resource_table, +}; diff --git a/core/pta/qcom/pas/platform/lemans/lpass.h b/core/pta/qcom/pas/platform/lemans/lpass.h new file mode 100644 index 000000000..f8cf519fc --- /dev/null +++ b/core/pta/qcom/pas/platform/lemans/lpass.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _LPASS_H_ +#define _LPASS_H_ + +#include "pas_subsys.h" + +extern const struct qcom_pas_ops lpass_ops; + +#endif /* _LPASS_H_ */ diff --git a/core/pta/qcom/pas/platform/lemans/sub.mk b/core/pta/qcom/pas/platform/lemans/sub.mk new file mode 100644 index 000000000..c40fd65fc --- /dev/null +++ b/core/pta/qcom/pas/platform/lemans/sub.mk @@ -0,0 +1,3 @@ +srcs-y += subsys.c camera.c cdsp0.c cdsp1.c lpass.c iris.c gpdsp0.c gpdsp1.c +incdirs-y += . +incdirs-y += ../ diff --git a/core/pta/qcom/pas/platform/lemans/subsys.c b/core/pta/qcom/pas/platform/lemans/subsys.c new file mode 100644 index 000000000..039e6b98c --- /dev/null +++ b/core/pta/qcom/pas/platform/lemans/subsys.c @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include + +#include "camera.h" +#include "cdsp0.h" +#include "cdsp1.h" +#include "gpdsp0.h" +#include "gpdsp1.h" +#include "iris.h" +#include "lpass.h" +#include "pas_subsys.h" + +static struct qcom_pas_subsys subsystems[] = { + { + .data = { + .pas_id = PAS_ID_TURING, + .base.pa = TURING_0_BASE, + .size = TURING_0_SIZE, + .clk_group = QCOM_CLKS_TURING, + }, + .ops = &cdsp0_ops, + .reset_seq = QCOM_PAS_RESET_CLK_FULL, + }, + { + .data = { + .pas_id = PAS_ID_TURING1, + .base.pa = TURING_1_BASE, + .size = TURING_1_SIZE, + .clk_group = QCOM_CLKS_TURING1, + }, + .ops = &cdsp1_ops, + .reset_seq = QCOM_PAS_RESET_CLK_FULL, + }, + { + .data = { + .pas_id = PAS_ID_QDSP6, + .base.pa = LPASS_BASE, + .size = LPASS_SIZE, + .clk_group = QCOM_CLKS_LPASS, + }, + .ops = &lpass_ops, + .reset_seq = QCOM_PAS_RESET_CLK_FULL, + }, + { + .data = { + .pas_id = PAS_ID_GPDSP0, + .base.pa = TURING_GDSP_0_BASE, + .size = TURING_GDSP_0_SIZE, + .clk_group = QCOM_CLKS_GPDSP0, + }, + .ops = &gpdsp0_ops, + .reset_seq = QCOM_PAS_RESET_CLK_FULL, + }, + { + .data = { + .pas_id = PAS_ID_GPDSP1, + .base.pa = TURING_GDSP_1_BASE, + .size = TURING_GDSP_1_SIZE, + .clk_group = QCOM_CLKS_GPDSP1, + }, + .ops = &gpdsp1_ops, + .reset_seq = QCOM_PAS_RESET_CLK_FULL, + }, + { + .data = { + .pas_id = PAS_ID_IRIS, + .base.pa = IRIS_BASE, + .size = IRIS_SIZE, + }, + .ops = &iris_ops, + .reset_seq = QCOM_PAS_RESET_NONE, + }, + { + .data = { + .pas_id = PAS_ID_CAMERA, + .base.pa = TITAN_SS_BASE, + .size = TITAN_SS_SIZE, + }, + .ops = &camera_ops, + .reset_seq = QCOM_PAS_RESET_NONE, + }, +}; + +struct qcom_pas_subsys *qcom_pas_platform_subsys(size_t *count) +{ + *count = ARRAY_SIZE(subsystems); + + return subsystems; +} diff --git a/core/pta/qcom/pas/platform/nord/iris.c b/core/pta/qcom/pas/platform/nord/iris.c new file mode 100644 index 000000000..244629d62 --- /dev/null +++ b/core/pta/qcom/pas/platform/nord/iris.c @@ -0,0 +1,276 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2024, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include + +#include "iris.h" + +#define WRAPPER_TZ_XTSS_SW_RESET 0x1000 +#define WRAPPER_XTSS_SW_RESET_BIT BIT(0) + +#define WRAPPER_TZ_SEC_0_SID 0x10c0 +#define WRAPPER_TZ_SEC_1_SID 0x10c4 +#define WRAPPER_TZ_SEC_2_SID 0x10c8 +#define WRAPPER_TZ_SEC_DEFAULT_SID 0x10cc + +#define WRAPPER_TZ_SEC_0_START_ADDR 0x1040 +#define WRAPPER_TZ_SEC_0_END_ADDR 0x1044 +#define WRAPPER_TZ_SEC_1_START_ADDR 0x1048 +#define WRAPPER_TZ_SEC_1_END_ADDR 0x104c +#define WRAPPER_TZ_SEC_2_START_ADDR 0x1050 +#define WRAPPER_TZ_SEC_2_END_ADDR 0x1054 + +#define WRAPPER_SEC_CSR1 0x1084 +#define WRAPPER_SEC_CSR1_TUNNEL_BIT BIT(0) +#define WRAPPER_SEC_CSR7 0x109c + +#define IRIS_CORE_TZ_OFFSET 0x1000 +#define IRIS_NUMBER_OF_CORES 2 + +#define CORE_TZ_SEC_THRESHOLD_HEVC 0x4 +#define CORE_TZ_SEC_THRESHOLD_H264 0x8 +#define CORE_TZ_SEC_THRESHOLD_MP2 0xc +#define CORE_TZ_SEC_THRESHOLD_NON_VCL_HEVC 0x18 +#define CORE_TZ_SEC_THRESHOLD_NON_VCL_H264 0x1c +#define CORE_TZ_SEC_THRESHOLD_NON_VCL_MP2 0x20 +#define CORE_TZ_SEC_DS_THRESHOLD 0x24 +#define CORE_TZ_SEC_THRESHOLD_AV1 0x2c + +#define CORE_TZ_SEC_SID_VPP_BASE 0x110 +#define CORE_TZ_SEC_SID_BSE_BASE 0x150 +#define CORE_TZ_SEC_SID_CCE_BASE 0x190 + +#define CORE_TZ_SID_VPP_WORDS 2 +#define CORE_TZ_SID_BSE_WORDS 2 +#define CORE_TZ_SID_CCE_WORDS 3 +#define CORE_TZ_SID_NUM_PARTITIONS 8 + +#define IRIS_SCIBCMDARG3_OFFSET 0x000a006c + +static const uint32_t iris_cce_sid_table[CORE_TZ_SID_NUM_PARTITIONS] + [CORE_TZ_SID_CCE_WORDS] = { + { 0x04201084, 0x04010082, 0x00000000 }, + { 0x042098c6, 0x041108c2, 0x00008421 }, + { 0x14a4318c, 0x1485218a, 0x00042108 }, + { 0x14a4b9ce, 0x149529ca, 0x0004a529 }, + { 0x25285294, 0x25094292, 0x00084210 }, + { 0x2528dad6, 0x25194ad2, 0x0008c631 }, + { 0x35ac739c, 0x358d639a, 0x000c6318 }, + { 0x35acfbde, 0x359d6bda, 0x000ce739 }, +}; + +static const uint32_t iris_bse_sid_table[CORE_TZ_SID_NUM_PARTITIONS] + [CORE_TZ_SID_BSE_WORDS] = { + { 0x00021084, 0x00020000 }, + { 0x021318c6, 0x00008421 }, + { 0x1086318c, 0x00062108 }, + { 0x129739ce, 0x0004a529 }, + { 0x210a5294, 0x000a4210 }, + { 0x231b5ad6, 0x0008c631 }, + { 0x318e739c, 0x000e6318 }, + { 0x339f7bde, 0x000ce739 }, +}; + +static const uint32_t iris_vpp_sid_table[CORE_TZ_SID_NUM_PARTITIONS] + [CORE_TZ_SID_VPP_WORDS] = { + { 0x06000084, 0x00000063 }, + { 0x0a1084c6, 0x000084a5 }, + { 0x1684218c, 0x0004216b }, + { 0x1a94a5ce, 0x0004a5ad }, + { 0x27084294, 0x00084273 }, + { 0x2b18c6d6, 0x0008c6b5 }, + { 0x378c639c, 0x000c637b }, + { 0x3b9ce7de, 0x000ce7bd }, +}; + +struct iris_mem_region { + uint32_t start_reg; + uint32_t end_reg; + uint32_t start; + uint32_t size; +}; + +static const struct iris_mem_region iris_mem_regions[] = { + { WRAPPER_TZ_SEC_0_START_ADDR, WRAPPER_TZ_SEC_0_END_ADDR, + 0x00000000, 0x01000000 }, + { WRAPPER_TZ_SEC_1_START_ADDR, WRAPPER_TZ_SEC_1_END_ADDR, + 0x01000000, 0x24800000 }, + { WRAPPER_TZ_SEC_2_START_ADDR, WRAPPER_TZ_SEC_2_END_ADDR, + 0x25800000, 0xda600000 }, +}; + +static void iris_program_core_sid_tables(vaddr_t core_base) +{ + unsigned int part = 0; + unsigned int word = 0; + vaddr_t addr = 0; + + io_write32(core_base + CORE_TZ_SEC_DS_THRESHOLD, 3); + io_write32(core_base + CORE_TZ_SEC_THRESHOLD_HEVC, 1344); + io_write32(core_base + CORE_TZ_SEC_THRESHOLD_H264, 270); + io_write32(core_base + CORE_TZ_SEC_THRESHOLD_MP2, 46); + io_write32(core_base + CORE_TZ_SEC_THRESHOLD_AV1, 1280); + io_write32(core_base + CORE_TZ_SEC_THRESHOLD_NON_VCL_HEVC, 3800); + io_write32(core_base + CORE_TZ_SEC_THRESHOLD_NON_VCL_H264, 12600); + io_write32(core_base + CORE_TZ_SEC_THRESHOLD_NON_VCL_MP2, 1600); + + addr = core_base + CORE_TZ_SEC_SID_CCE_BASE; + for (part = 0; part < CORE_TZ_SID_NUM_PARTITIONS; part++) { + for (word = 0; word < CORE_TZ_SID_CCE_WORDS; word++) { + io_write32(addr, iris_cce_sid_table[part][word]); + addr += sizeof(uint32_t); + } + } + + addr = core_base + CORE_TZ_SEC_SID_BSE_BASE; + for (part = 0; part < CORE_TZ_SID_NUM_PARTITIONS; part++) { + for (word = 0; word < CORE_TZ_SID_BSE_WORDS; word++) { + io_write32(addr, iris_bse_sid_table[part][word]); + addr += sizeof(uint32_t); + } + } + + addr = core_base + CORE_TZ_SEC_SID_VPP_BASE; + for (part = 0; part < CORE_TZ_SID_NUM_PARTITIONS; part++) { + for (word = 0; word < CORE_TZ_SID_VPP_WORDS; word++) { + io_write32(addr, iris_vpp_sid_table[part][word]); + addr += sizeof(uint32_t); + } + } +} + +static TEE_Result iris_program_sec_sid_registers(vaddr_t iris_base) +{ + vaddr_t top_base = iris_base + IRIS_WRAPPER_TOP_REG_BASE; + vaddr_t tz_base = iris_base + IRIS_WRAPPER_TZ_REG_BASE; + vaddr_t core0_base = iris_base + IRIS_CORE0_TZ_REG_BASE; + unsigned int core = 0; + + for (core = 0; core < IRIS_NUMBER_OF_CORES; core++) + iris_program_core_sid_tables(core0_base + + core * IRIS_CORE_TZ_OFFSET); + + io_write32(iris_base + IRIS_SCIBCMDARG3_OFFSET, 0xbeef0001); + + io_write32(tz_base + WRAPPER_TZ_SEC_0_SID, 0x0198a422); + io_write32(tz_base + WRAPPER_TZ_SEC_1_SID, 0x00006208); + io_write32(tz_base + WRAPPER_TZ_SEC_2_SID, 0x00000000); + io_write32(tz_base + WRAPPER_TZ_SEC_DEFAULT_SID, 0x00000000); + + io_write32(top_base + WRAPPER_SEC_CSR1, WRAPPER_SEC_CSR1_TUNNEL_BIT); + io_write32(top_base + WRAPPER_SEC_CSR7, 0x1); + + return TEE_SUCCESS; +} + +static void iris_program_mem_regions(vaddr_t tz_base) +{ + size_t i = 0; + + for (i = 0; i < ARRAY_SIZE(iris_mem_regions); i++) { + const struct iris_mem_region *r = &iris_mem_regions[i]; + + io_write32(tz_base + r->start_reg, r->start); + io_write32(tz_base + r->end_reg, r->start + r->size); + } +} + +static bool iris_resumed; + +static TEE_Result iris_fw_start(struct qcom_pas_data *data) +{ + vaddr_t iris_base = 0; + vaddr_t tz_base = 0; + TEE_Result res = TEE_SUCCESS; + + iris_base = io_pa_or_va(&data->base, data->size); + if (!iris_base) + return TEE_ERROR_GENERIC; + + tz_base = iris_base + IRIS_WRAPPER_TZ_REG_BASE; + + if (!data->fw_base) { + EMSG("iris: FW not loaded"); + return TEE_ERROR_NO_DATA; + } + + udelay(IRIS_CLK_SETTLE_US); + + if (!(io_read32(tz_base + WRAPPER_TZ_XTSS_SW_RESET) & + WRAPPER_XTSS_SW_RESET_BIT)) { + DMSG("iris: subsystem already running, skipping bring-up"); + return TEE_SUCCESS; + } + + iris_program_mem_regions(tz_base); + + res = iris_program_sec_sid_registers(iris_base); + if (res) + return res; + + io_write32(tz_base + WRAPPER_TZ_XTSS_SW_RESET, 0); + + iris_resumed = true; + + return TEE_SUCCESS; +} + +static TEE_Result iris_fw_shutdown(struct qcom_pas_data *data) +{ + vaddr_t iris_base = 0; + vaddr_t tz_base = 0; + uint32_t reg = 0; + + iris_base = io_pa_or_va(&data->base, data->size); + if (!iris_base) + return TEE_ERROR_GENERIC; + + tz_base = iris_base + IRIS_WRAPPER_TZ_REG_BASE; + + if (iris_resumed) { + reg = io_read32(tz_base + WRAPPER_TZ_XTSS_SW_RESET); + reg |= WRAPPER_XTSS_SW_RESET_BIT; + io_write32(tz_base + WRAPPER_TZ_XTSS_SW_RESET, reg); + } else { + EMSG("iris: shutdown not in resumed state"); + } + + iris_resumed = false; + + return TEE_SUCCESS; +} + +static TEE_Result iris_fw_set_state(struct qcom_pas_data *data, bool power_on) +{ + vaddr_t iris_base = 0; + vaddr_t tz_base = 0; + uint32_t reg = 0; + + if (!power_on) { + if (iris_resumed) { + iris_base = io_pa_or_va(&data->base, data->size); + if (!iris_base) + return TEE_ERROR_GENERIC; + tz_base = iris_base + IRIS_WRAPPER_TZ_REG_BASE; + reg = io_read32(tz_base + WRAPPER_TZ_XTSS_SW_RESET); + reg |= WRAPPER_XTSS_SW_RESET_BIT; + io_write32(tz_base + WRAPPER_TZ_XTSS_SW_RESET, reg); + iris_resumed = false; + } else { + EMSG("iris: cannot SUSPEND, not in resumed state"); + } + return TEE_SUCCESS; + } + + return iris_fw_start(data); +} + +const struct qcom_pas_ops iris_ops = { + .fw_start = iris_fw_start, + .fw_shutdown = iris_fw_shutdown, + .fw_set_state = iris_fw_set_state, +}; diff --git a/core/pta/qcom/pas/platform/nord/iris.h b/core/pta/qcom/pas/platform/nord/iris.h new file mode 100644 index 000000000..875f56e36 --- /dev/null +++ b/core/pta/qcom/pas/platform/nord/iris.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2024, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _IRIS_H_ +#define _IRIS_H_ + +#include "pas_subsys.h" + +#define IRIS_WRAPPER_TOP_REG_BASE 0x000b0000 +#define IRIS_WRAPPER_TZ_REG_BASE 0x000c0000 +#define IRIS_CORE0_TZ_REG_BASE 0x000c2000 +#define IRIS_CORE1_TZ_REG_BASE 0x000c3000 + +#define IRIS_CLK_SETTLE_US 1U + +extern const struct qcom_pas_ops iris_ops; + +#endif /* _IRIS_H_ */ diff --git a/core/pta/qcom/pas/platform/nord/sub.mk b/core/pta/qcom/pas/platform/nord/sub.mk new file mode 100644 index 000000000..0cff40476 --- /dev/null +++ b/core/pta/qcom/pas/platform/nord/sub.mk @@ -0,0 +1,3 @@ +srcs-y += subsys.c iris.c +incdirs-y += . +incdirs-y += ../ diff --git a/core/pta/qcom/pas/platform/nord/subsys.c b/core/pta/qcom/pas/platform/nord/subsys.c new file mode 100644 index 000000000..3311157f1 --- /dev/null +++ b/core/pta/qcom/pas/platform/nord/subsys.c @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2024, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include + +#include "iris.h" +#include "pas_subsys.h" + +static struct qcom_pas_subsys subsystems[] = { + { + .data = { + .pas_id = PAS_ID_IRIS, + .base.pa = IRIS_BASE, + .size = IRIS_SIZE, + }, + .ops = &iris_ops, + .reset_seq = QCOM_PAS_RESET_NONE, + }, +}; + +struct qcom_pas_subsys *qcom_pas_platform_subsys(size_t *count) +{ + *count = ARRAY_SIZE(subsystems); + + return subsystems; +} diff --git a/core/pta/qcom/pas/platform/pas_data.h b/core/pta/qcom/pas/platform/pas_data.h new file mode 100644 index 000000000..2bf918d1e --- /dev/null +++ b/core/pta/qcom/pas/platform/pas_data.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2025, Linaro Limited + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef _PAS_DATA_H_ +#define _PAS_DATA_H_ + +#include +#include +#include + +#define PAS_ID_QDSP6 1 +#define PAS_ID_WPSS 6 +#define PAS_ID_IRIS 9 +#define PAS_ID_TURING 18 +#define PAS_ID_TURING1 30 +#define PAS_ID_CAMERA 33 +#define PAS_ID_GPDSP0 39 +#define PAS_ID_GPDSP1 40 +#define PAS_ID_TURING2 57 +#define PAS_ID_TURING3 58 +#define PAS_ID_HPASS0 PAS_ID_QDSP6 +#define PAS_ID_HPASS1 53 +#define PAS_ID_HPASS2 54 +#define PAS_ID_SOCCP 51 + +struct qcom_pas_data { + uint32_t pas_id; + struct io_pa_va base; + size_t size; + paddr_t fw_base; + size_t fw_size; + enum qcom_clk_group clk_group; +}; + +#endif /* _PAS_DATA_H_ */ diff --git a/core/pta/qcom/pas/platform/pas_subsys.h b/core/pta/qcom/pas/platform/pas_subsys.h new file mode 100644 index 000000000..128572c32 --- /dev/null +++ b/core/pta/qcom/pas/platform/pas_subsys.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef PAS_SUBSYS_H +#define PAS_SUBSYS_H + +#include +#include +#include + +#include "pas_data.h" +#include "resource_table.h" + +/* + * struct qcom_pas_ops : per-subsystem firmware operations. + * + * Each remoteproc subsystem a platform supports provides one of these. Any + * member may be NULL when the subsystem does not implement that operation; + * the generic core (pas_core.c) returns the appropriate error in that case. + * + * @fw_start: Program the firmware entry and run the boot FSM. + * @fw_shutdown: Put the processor back into reset. + * @fw_set_state: Power the processor on (@on == true) or off. + * @get_resource_table: Serialise the firmware resource table (see + * resource_table.h). NULL for subsystems with no table. + */ +struct qcom_pas_ops { + TEE_Result (*fw_start)(struct qcom_pas_data *data); + TEE_Result (*fw_shutdown)(struct qcom_pas_data *data); + TEE_Result (*fw_set_state)(struct qcom_pas_data *data, bool on); + TEE_Result (*get_resource_table)(struct resource_table *rt, + size_t *size); +}; + +/* + * enum qcom_pas_reset_seq : clock/reset sequence auth_and_reset runs around + * the subsystem's fw_start op. + * + * @QCOM_PAS_RESET_NONE: fw_start only; no clock management here. + * @QCOM_PAS_RESET_CLK_ENABLE: qcom_clock_enable() then fw_start(). + * @QCOM_PAS_RESET_CLK_FULL: qcom_clock_pas_reset(), qcom_clock_enable(), + * fw_start(), then + * qcom_clock_enable_pas_processor(). + */ +enum qcom_pas_reset_seq { + QCOM_PAS_RESET_NONE = 0, + QCOM_PAS_RESET_CLK_ENABLE, + QCOM_PAS_RESET_CLK_FULL, +}; + +/* + * struct qcom_pas_subsys : one remoteproc subsystem on a platform. + * + * @data: Runtime state, seeded with the static pas_id / base / size / + * clk_group and updated during mem_setup and bring-up. + * @ops: Firmware operations for this subsystem. + * @reset_seq: Clock/reset sequence auth_and_reset must run. + * + * Platform descriptor tables are writable because @data is mutated at + * runtime. + */ +struct qcom_pas_subsys { + struct qcom_pas_data data; + const struct qcom_pas_ops *ops; + enum qcom_pas_reset_seq reset_seq; +}; + +/* + * qcom_pas_platform_subsys() : return this platform's subsystem table. + * + * Implemented once per platform flavor in platform//subsys.c. + * + * @count: out, number of entries in the returned table. + * Returns the (writable) descriptor array. + */ +struct qcom_pas_subsys *qcom_pas_platform_subsys(size_t *count); + +struct qcom_pas_subsys *pas_lookup(uint32_t pas_id); + +#endif /* PAS_SUBSYS_H */ diff --git a/core/pta/qcom/pas/platform/platform_pas.h b/core/pta/qcom/pas/platform/platform_pas.h new file mode 100644 index 000000000..457498ef5 --- /dev/null +++ b/core/pta/qcom/pas/platform/platform_pas.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef PLATFORM_PAS_H +#define PLATFORM_PAS_H + +#include +#include +#include + +TEE_Result pas_platform_mem_setup(uint32_t pas_id, uint32_t fw_size, + uint32_t fw_base_low, uint32_t fw_base_high); +TEE_Result pas_platform_get_resource_table(uint32_t pas_id, + struct resource_table *rt, + size_t *size); +TEE_Result pas_platform_set_remote_state(uint32_t pas_id, uint32_t state); +TEE_Result pas_platform_auth_and_reset(uint32_t pas_id); +TEE_Result pas_platform_is_supported(uint32_t pas_id); +TEE_Result pas_platform_capabilities(uint32_t pas_id); +TEE_Result pas_platform_init_image(uint32_t pas_id); +TEE_Result pas_platform_shutdown(uint32_t pas_id); + +struct pas_fw_region { + paddr_t base; + uint32_t size; +}; + +struct pas_metadata { + const uint8_t *data; + size_t size; +}; + +struct pas_hash_table { + const uint8_t *table; + size_t len; + uint32_t entry_size; +}; + +#ifdef CFG_QCOM_PAS_AUTH +TEE_Result pas_platform_verify_image(uint32_t pas_id, + const struct pas_fw_region *fw, + const struct pas_metadata *metadata, + const struct pas_hash_table *hash); +#else +static inline TEE_Result +pas_platform_verify_image(uint32_t pas_id __unused, + const struct pas_fw_region *fw __unused, + const struct pas_metadata *metadata __unused, + const struct pas_hash_table *hash __unused) +{ + return TEE_SUCCESS; +} +#endif /* CFG_QCOM_PAS_AUTH */ + +#endif diff --git a/core/pta/qcom/pas/platform/resource_table.h b/core/pta/qcom/pas/platform/resource_table.h new file mode 100644 index 000000000..5239e38a4 --- /dev/null +++ b/core/pta/qcom/pas/platform/resource_table.h @@ -0,0 +1,185 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +/* + * Firmware resource table helpers for Qualcomm remoteproc subsystems. + * + * A resource table is a data structure embedded in (or alongside) a processor + * firmware image. It advertises to the host the set of memory regions the + * processor needs mapped into its IOMMU before it can boot. The layout + * expected by the remoteproc / PAS loader is: + * + * [ struct resource_table header + offset[] array ] + * [ struct fw_rsc_hdr + struct fw_rsc_devmem ] * N <- one per region + * + * Each element in offset[] holds the byte offset from the start of the table + * to the corresponding fw_rsc_hdr/fw_rsc_devmem pair. + * + * Callers define a table with DEFINE_RESOURCE_TABLE(), declare a static + * struct resource_table and a static array of struct fw_rsc_devmem, then + * call get_mem_rsc() to serialise everything into a caller-supplied buffer. + */ + +#ifndef RESOURCE_TABLE_H +#define RESOURCE_TABLE_H + +#include +#include +#include +#include +#include + +/* + * DEFINE_RESOURCE_TABLE(prefix, num_res) : emit compile-time size constants + * for a resource table that holds @num_res device-memory entries. + * + * Generates an anonymous enum with four members, all prefixed by @prefix: + * + * PREFIX_NUM_MEM_RESOURCES : number of fw_rsc_devmem entries + * PREFIX_SIZE_MEM_RES : byte size of one (hdr + devmem) entry + * PREFIX_RESOURCE_TABLE_HEADER_SIZE : byte size of the table header plus + * the offset[] array + * PREFIX_RESOURCE_TABLE_SIZE : total byte size of the serialised table + * + * Use these constants to size the struct resource_table initialiser and to + * pass table_header_size / table_size to get_mem_rsc(). + */ +#define DEFINE_RESOURCE_TABLE(prefix, num_res) \ + enum prefix##_rt_sizes { \ + prefix##_NUM_MEM_RESOURCES = (num_res), \ + prefix##_SIZE_MEM_RES = \ + (sizeof(struct fw_rsc_hdr) + \ + sizeof(struct fw_rsc_devmem)), \ + prefix##_RESOURCE_TABLE_HEADER_SIZE = \ + (sizeof(struct resource_table) + \ + (prefix##_NUM_MEM_RESOURCES * \ + sizeof(uint32_t))), \ + prefix##_RESOURCE_TABLE_SIZE = \ + (prefix##_RESOURCE_TABLE_HEADER_SIZE + \ + (prefix##_NUM_MEM_RESOURCES * \ + prefix##_SIZE_MEM_RES)), \ + } + +/* + * resource_table : top-level header of the firmware resource table. + * + * @ver: Table version; must be 1. + * @num: Number of resource entries (length of the offset[] array). + * @reserved: Must be zero. + * @offset: Array of byte offsets from the start of the table to each + * fw_rsc_hdr entry. Populated by get_mem_rsc(). + */ +struct resource_table { + uint32_t ver; + uint32_t num; + uint32_t reserved[2]; + uint32_t offset[]; +} __packed; + +/* + * fw_rsc_hdr : per-resource entry header. + * + * @type: Resource type (see enum fw_resource_type). + * @data: Type-specific payload; for RSC_DEVMEM this is struct fw_rsc_devmem. + */ +struct fw_rsc_hdr { + uint32_t type; + uint8_t data[]; +} __packed; + +/* Resource entry types defined by the remoteproc ABI. */ +enum fw_resource_type { + RSC_CARVEOUT = 0, /* contiguous memory carve-out */ + RSC_DEVMEM = 1, /* device memory IOMMU mapping */ + RSC_TRACE = 2, /* trace buffer */ + RSC_VDEV = 3, /* virtio device */ + RSC_LAST = 4, + RSC_VENDOR_START = 128, + RSC_VENDOR_END = 512, +}; + +/* IOMMU permission flags for fw_rsc_devmem.flags. */ +#define IOMMU_READ BIT(0) +#define IOMMU_WRITE BIT(1) + +/* + * fw_rsc_devmem : describes a single IOMMU mapping the processor requires. + * + * @da: Device virtual address of the mapping. + * @pa: Physical address; on Kodiak these are identity-mapped (da == pa). + * @len: Size of the region in bytes. + * @flags: IOMMU permission flags (IOMMU_READ, IOMMU_WRITE). + * @reserved: Must be zero. + * @name: Human-readable label for debugging (NUL-terminated, 32 bytes). + */ +struct fw_rsc_devmem { + uint32_t da; + uint32_t pa; + uint32_t len; + uint32_t flags; + uint32_t reserved; + uint8_t name[32]; +} __packed; + +/* + * get_mem_rsc() : serialise a processor resource table into a caller buffer. + * + * The function has two modes: + * + * Size query: pass rt == NULL (or *rt_size < table_size). The required + * buffer size is written to *rt_size and TEE_SUCCESS is returned so the + * caller can allocate an appropriately-sized buffer. + * + * Serialise: pass a buffer of at least table_size bytes in rt. The + * function fills in table->offset[], copies each (mem_hdr + mem_res[i]) + * pair at the computed offset, then copies the updated header to the start + * of the buffer. + * + * @rt: Output buffer (may be NULL for size query). + * @rt_size: In/out: caller capacity on entry; required size on + * exit when the buffer is too small. + * @table: Partially initialised resource_table (ver, num set by + * the caller via DEFINE_RESOURCE_TABLE constants). + * @mem_hdr: fw_rsc_hdr with type = RSC_DEVMEM, shared by all + * entries. + * @mem_res: Array of table->num fw_rsc_devmem descriptors. + * @table_header_size: PREFIX_RESOURCE_TABLE_HEADER_SIZE from the macro. + * @table_size: PREFIX_RESOURCE_TABLE_SIZE from the macro. + */ +static inline TEE_Result get_mem_rsc(struct resource_table *rt, size_t *rt_size, + struct resource_table *table, + const struct fw_rsc_hdr *mem_hdr, + const struct fw_rsc_devmem *mem_res, + size_t table_header_size, + size_t table_size) +{ + uint8_t *p = (uint8_t *)rt; + uint32_t offset = 0; + + if (rt_size && *rt_size < table_size) { + *rt_size = table_size; + + return TEE_SUCCESS; + } + + if (!rt) + return TEE_ERROR_BAD_PARAMETERS; + + offset = table_header_size; + + for (size_t i = 0; i < table->num; i++, mem_res++) { + table->offset[i] = offset; + memcpy(p + offset, mem_hdr, sizeof(*mem_hdr)); + offset += sizeof(*mem_hdr); + memcpy(p + offset, mem_res, sizeof(*mem_res)); + offset += sizeof(*mem_res); + } + + memcpy(p, table, table_header_size); + + return TEE_SUCCESS; +} + +#endif diff --git a/core/pta/qcom/pas/pta_qcom_pas.c b/core/pta/qcom/pas/pta_qcom_pas.c index b8bef6f04..595e978d4 100644 --- a/core/pta/qcom/pas/pta_qcom_pas.c +++ b/core/pta/qcom/pas/pta_qcom_pas.c @@ -3,47 +3,18 @@ * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. */ -#include #include -#include -#include +#include +#include #include #include - -#include "pas.h" +#include #define PTA_NAME "pta.qcom.pas" #define TA_PAS_UUID { 0xcff7d191, 0x7ca0, 0x4784, \ { 0xaf, 0x13, 0x48, 0x22, 0x3b, 0x9a, 0x4f, 0xbe} } -static struct qcom_pas_data wpss_dsp_data = { - .pas_id = PAS_ID_WPSS, - .base.pa = WPSS_BASE, - .size = WPSS_SIZE, - .clk_group = QCOM_CLKS_WPSS, -}; - -static struct qcom_pas_data turing_dsp_data = { - .pas_id = PAS_ID_TURING, - .base.pa = TURING_BASE, - .size = TURING_SIZE, - .clk_group = QCOM_CLKS_TURING, -}; - -static struct qcom_pas_data lpass_dsp_data = { - .pas_id = PAS_ID_QDSP6, - .base.pa = LPASS_BASE, - .size = LPASS_SIZE, - .clk_group = QCOM_CLKS_LPASS, -}; - -static struct qcom_pas_data venus_fw_data = { - .pas_id = PAS_ID_VENUS, - .base.pa = IRIS_BASE, - .size = IRIS_SIZE, -}; - static TEE_Result qcom_pas_is_supported(uint32_t pt, TEE_Param params[TEE_NUM_PARAMS]) { @@ -54,16 +25,9 @@ static TEE_Result qcom_pas_is_supported(uint32_t pt, if (pt != exp_pt) return TEE_ERROR_BAD_PARAMETERS; - DMSG("invoked with pas_id: %d", params[0].value.a); - if (params[0].value.a != PAS_ID_WPSS && - params[0].value.a != PAS_ID_QDSP6 && - params[0].value.a != PAS_ID_VENUS && - params[0].value.a != PAS_ID_TURING) - return TEE_ERROR_NOT_SUPPORTED; - - return TEE_SUCCESS; + return pas_platform_is_supported(params[0].value.a); } static TEE_Result qcom_pas_capabilities(uint32_t pt, @@ -76,12 +40,11 @@ static TEE_Result qcom_pas_capabilities(uint32_t pt, if (pt != exp_pt) return TEE_ERROR_BAD_PARAMETERS; - DMSG("invoked with pas_id: %d", params[0].value.a); + /* Capabilities flags reserved for future use */ params[1].value.a = 0; - - return TEE_SUCCESS; + return pas_platform_capabilities(params[0].value.a); } static TEE_Result qcom_pas_init_image(uint32_t pt, @@ -94,16 +57,9 @@ static TEE_Result qcom_pas_init_image(uint32_t pt, if (pt != exp_pt) return TEE_ERROR_BAD_PARAMETERS; - DMSG("invoked with pas_id: %d", params[0].value.a); - if (params[0].value.a != PAS_ID_WPSS && - params[0].value.a != PAS_ID_QDSP6 && - params[0].value.a != PAS_ID_VENUS && - params[0].value.a != PAS_ID_TURING) - return TEE_ERROR_NOT_SUPPORTED; - - return TEE_SUCCESS; + return pas_platform_init_image(params[0].value.a); } static TEE_Result qcom_pas_mem_setup(uint32_t pt, @@ -113,44 +69,12 @@ static TEE_Result qcom_pas_mem_setup(uint32_t pt, TEE_PARAM_TYPE_VALUE_INPUT, TEE_PARAM_TYPE_NONE, TEE_PARAM_TYPE_NONE); - struct qcom_pas_data *data = NULL; - if (pt != exp_pt) return TEE_ERROR_BAD_PARAMETERS; - DMSG("invoked with pas_id: %d", params[0].value.a); - switch (params[0].value.a) { - case PAS_ID_WPSS: - data = &wpss_dsp_data; - break; - case PAS_ID_TURING: - data = &turing_dsp_data; - break; - case PAS_ID_QDSP6: - data = &lpass_dsp_data; - break; - case PAS_ID_VENUS: - data = &venus_fw_data; - break; - default: - return TEE_ERROR_NOT_SUPPORTED; - } - - data->fw_size = params[0].value.b; - data->fw_base = params[1].value.a; - data->fw_base |= SHIFT_U64(params[1].value.b, 32); - - /* Map the controller */ - if (!data->base.va) { - data->base.va = (vaddr_t)core_mmu_add_mapping(MEM_AREA_IO_NSEC, - data->base.pa, - data->size); - if (!data->base.va) - return TEE_ERROR_GENERIC; - } - - return TEE_SUCCESS; + return pas_platform_mem_setup(params[0].value.a, params[0].value.b, + params[1].value.a, params[1].value.b); } static TEE_Result qcom_pas_get_resource_table(uint32_t pt, @@ -163,22 +87,15 @@ static TEE_Result qcom_pas_get_resource_table(uint32_t pt, if (pt != exp_pt) return TEE_ERROR_BAD_PARAMETERS; - DMSG("invoked with pas_id: %d", params[0].value.a); - if (params[0].value.a != PAS_ID_WPSS && - params[0].value.a != PAS_ID_TURING && - params[0].value.a != PAS_ID_QDSP6) - return TEE_ERROR_NOT_SUPPORTED; - - return pas_get_resource_table(params[0].value.a, - params[1].memref.buffer, - ¶ms[1].memref.size); + return pas_platform_get_resource_table(params[0].value.a, + params[1].memref.buffer, + ¶ms[1].memref.size); } static TEE_Result -qcom_pas_set_remote_state(uint32_t pt, - TEE_Param params[TEE_NUM_PARAMS]__maybe_unused) +qcom_pas_set_remote_state(uint32_t pt, TEE_Param params[TEE_NUM_PARAMS]) { const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, TEE_PARAM_TYPE_NONE, @@ -187,13 +104,10 @@ qcom_pas_set_remote_state(uint32_t pt, if (pt != exp_pt) return TEE_ERROR_BAD_PARAMETERS; - DMSG("invoked with pas_id: %d", params[0].value.a); - if (params[0].value.a == PAS_ID_VENUS) - return venus_fw_set_state(&venus_fw_data, params[0].value.b); - - return TEE_ERROR_NOT_IMPLEMENTED; + return pas_platform_set_remote_state(params[0].value.a, + params[0].value.b); } static TEE_Result qcom_pas_auth_and_reset(uint32_t pt, @@ -203,60 +117,16 @@ static TEE_Result qcom_pas_auth_and_reset(uint32_t pt, TEE_PARAM_TYPE_VALUE_INPUT, TEE_PARAM_TYPE_MEMREF_INPUT, TEE_PARAM_TYPE_NONE); - TEE_Result res = TEE_SUCCESS; if (pt != exp_pt) return TEE_ERROR_BAD_PARAMETERS; - DMSG("invoked with pas_id: %d", params[0].value.a); - switch (params[0].value.a) { - case PAS_ID_WPSS: - if (!wpss_dsp_data.fw_base) - return TEE_ERROR_NO_DATA; - - res = qcom_clock_enable(wpss_dsp_data.clk_group); - if (res != TEE_SUCCESS) { - EMSG("Failed to enable clocks: %d", res); - return res; - } - - return wpss_fw_start(&wpss_dsp_data); - case PAS_ID_TURING: - if (!turing_dsp_data.fw_base) - return TEE_ERROR_NO_DATA; - - res = qcom_clock_enable(turing_dsp_data.clk_group); - if (res != TEE_SUCCESS) { - EMSG("Failed to enable clocks: %d", res); - return res; - } - - return compute_fw_start(&turing_dsp_data); - case PAS_ID_QDSP6: - if (!lpass_dsp_data.fw_base) - return TEE_ERROR_NO_DATA; - - res = qcom_clock_enable(lpass_dsp_data.clk_group); - if (res != TEE_SUCCESS) { - EMSG("Failed to enable clocks: %d", res); - return res; - } - - return lpass_fw_start(&lpass_dsp_data); - case PAS_ID_VENUS: - if (!venus_fw_data.fw_base) - return TEE_ERROR_NO_DATA; - - return venus_fw_start(&venus_fw_data); - default: - return TEE_ERROR_NOT_SUPPORTED; - } + return pas_platform_auth_and_reset(params[0].value.a); } static TEE_Result -qcom_pas_shutdown(uint32_t pt, - TEE_Param params[TEE_NUM_PARAMS] __maybe_unused) +qcom_pas_shutdown(uint32_t pt, TEE_Param params[TEE_NUM_PARAMS]) { const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, TEE_PARAM_TYPE_NONE, @@ -265,23 +135,44 @@ qcom_pas_shutdown(uint32_t pt, if (pt != exp_pt) return TEE_ERROR_BAD_PARAMETERS; - DMSG("invoked with pas_id: %d", params[0].value.a); - switch (params[0].value.a) { - case PAS_ID_WPSS: - return wpss_fw_shutdown(&wpss_dsp_data); - case PAS_ID_TURING: - return compute_fw_shutdown(&turing_dsp_data); - case PAS_ID_QDSP6: - return lpass_fw_shutdown(&lpass_dsp_data); - case PAS_ID_VENUS: - return venus_fw_shutdown(&venus_fw_data); - default: - return TEE_ERROR_NOT_SUPPORTED; - } + return pas_platform_shutdown(params[0].value.a); +} - return TEE_ERROR_NOT_IMPLEMENTED; +static TEE_Result +qcom_pas_verify_image(uint32_t pt, TEE_Param params[TEE_NUM_PARAMS]) +{ + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_MEMREF_INPUT, + TEE_PARAM_TYPE_VALUE_INPUT); + struct pas_metadata metadata = { }; + struct pas_hash_table hash = { }; + struct pas_fw_region fw = { }; + const uint8_t *buf = NULL; + + if (pt != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + if (!params[2].memref.buffer || !params[2].memref.size) + return TEE_ERROR_BAD_PARAMETERS; + + metadata.size = params[3].value.b; + if (!metadata.size || metadata.size >= params[2].memref.size) + return TEE_ERROR_BAD_PARAMETERS; + + buf = params[2].memref.buffer; + metadata.data = buf; + hash.table = buf + metadata.size; + hash.len = params[2].memref.size - metadata.size; + hash.entry_size = params[3].value.a; + + fw.base = reg_pair_to_64(params[1].value.b, params[1].value.a); + fw.size = params[0].value.b; + + return pas_platform_verify_image(params[0].value.a, &fw, &metadata, + &hash); } static TEE_Result pta_qcom_pas_invoke_command(void *session __unused, @@ -306,6 +197,8 @@ static TEE_Result pta_qcom_pas_invoke_command(void *session __unused, return qcom_pas_set_remote_state(param_types, params); case PTA_QCOM_PAS_SHUTDOWN: return qcom_pas_shutdown(param_types, params); + case PTA_QCOM_PAS_VERIFY_IMAGE: + return qcom_pas_verify_image(param_types, params); default: return TEE_ERROR_NOT_IMPLEMENTED; } @@ -320,8 +213,8 @@ pta_qcom_pas_open_session(uint32_t pt __unused, void **sess_ctx __unused) { struct ts_session *s = ts_get_calling_session(); - struct ts_ctx *ctx = NULL; TEE_UUID ta_uuid = TA_PAS_UUID; + struct ts_ctx *ctx = NULL; if (!s) return TEE_ERROR_ACCESS_DENIED; @@ -333,11 +226,9 @@ pta_qcom_pas_open_session(uint32_t pt __unused, return TEE_SUCCESS; } -/* - * TA_FLAG_CONCURRENT disabled: - * concurrent operation must be supported by the client. - */ -pseudo_ta_register(.uuid = PTA_QCOM_PAS_UUID, .name = PTA_NAME, +/* TA_FLAG_CONCURRENT disabled: concurrency must be handled by the client. */ +pseudo_ta_register(.invoke_command_entry_point = pta_qcom_pas_invoke_command, + .open_session_entry_point = pta_qcom_pas_open_session, .flags = PTA_DEFAULT_FLAGS, - .invoke_command_entry_point = pta_qcom_pas_invoke_command, - .open_session_entry_point = pta_qcom_pas_open_session); + .uuid = PTA_QCOM_PAS_UUID, + .name = PTA_NAME); diff --git a/core/pta/qcom/pas/q6dsp.c b/core/pta/qcom/pas/q6dsp.c deleted file mode 100644 index ebc6df834..000000000 --- a/core/pta/qcom/pas/q6dsp.c +++ /dev/null @@ -1,24 +0,0 @@ -// SPDX-License-Identifier: BSD-2-Clause -/* - * Copyright (c) 2025, Linaro Limited - * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. - */ - -#include -#include -#include -#include -#include - -#include "dsp_boot.h" -#include "pas.h" - -TEE_Result wpss_fw_start(struct qcom_pas_data *data) -{ - return dsp_fw_start(data, dsp_fw_get_boot_regs(data->pas_id)); -} - -TEE_Result wpss_fw_shutdown(struct qcom_pas_data *data __unused) -{ - return TEE_ERROR_NOT_IMPLEMENTED; -} diff --git a/core/pta/qcom/pas/sub.mk b/core/pta/qcom/pas/sub.mk index 7c56b2915..4f2225a2a 100644 --- a/core/pta/qcom/pas/sub.mk +++ b/core/pta/qcom/pas/sub.mk @@ -1,3 +1,9 @@ -srcs-y += pta_qcom_pas.c dsp.c pas.c q6dsp.c compute.c lpass.c venus.c +srcs-y += pta_qcom_pas.c +srcs-y += pas_core.c +srcs-$(CFG_QCOM_PAS_AUTH) += pas_auth_core.c incdirs-y += . -incdirs-y += platform/$(PLATFORM_FLAVOR) +incdirs-y += platform/ +subdirs-y += platform/$(PLATFORM_FLAVOR) + +# PIL PAS authentication metadata slots, defaults to 1. +CFG_PAS_MD_SLOTS ?= 1 diff --git a/lib/libutee/include/pta_qcom_pas.h b/lib/libutee/include/pta_qcom_pas.h index 79adb415d..4a1bd6c5f 100644 --- a/lib/libutee/include/pta_qcom_pas.h +++ b/lib/libutee/include/pta_qcom_pas.h @@ -84,4 +84,23 @@ */ #define PTA_QCOM_PAS_SHUTDOWN 8 -#endif /* __PTA_QCOM_REMOTEPROC_H */ +/* + * PAS firmware image integrity verification. + * + * The caller is responsible for ensuring the hash table is trusted. + * + * [in] params[0].value.a: Unique 32bit remote processor identifier + * [in] params[0].value.b: Firmware size + * [in] params[1].value.a: 32bit LSB firmware memory address + * [in] params[1].value.b: 32bit MSB firmware memory address + * [in] params[2].memref: Packed [metadata | hash_table] buffer: the + * INIT_IMAGE metadata blob (ELF header + phdrs) + * followed immediately by the per-segment + * hash table (one digest per phdr) + * [in] params[3].value.a: Digest size in bytes (32=SHA-256, 48=SHA-384) + * [in] params[3].value.b: Metadata size, i.e. offset of hash_table + * within params[2].memref + */ +#define PTA_QCOM_PAS_VERIFY_IMAGE 9 + +#endif /* __PTA_QCOM_PAS_H */ diff --git a/ta/qcom_pas/include/pas_auth.h b/ta/qcom_pas/include/pas_auth.h new file mode 100644 index 000000000..6db48a5bd --- /dev/null +++ b/ta/qcom_pas/include/pas_auth.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __PAS_AUTH_H +#define __PAS_AUTH_H + +#include +#include + +#ifdef CFG_QCOM_PAS_AUTH +TEE_Result pas_auth_save_metadata(struct qcom_pas_session *s, uint32_t pt, + TEE_Param params[TEE_NUM_PARAMS]); + +TEE_Result pas_auth_authenticate(struct qcom_pas_session *s, uint32_t pas_id); + +TEE_Result pas_auth_verify_reset(struct qcom_pas_session *s, + TEE_TASessionHandle pta_session, + uint32_t pas_id, + TEE_Param params[TEE_NUM_PARAMS]); + +TEE_Result pas_auth_release_metadata(struct qcom_pas_session *s, + uint32_t pas_id); +#else +static inline TEE_Result +pas_auth_save_metadata(struct qcom_pas_session *s __unused, + uint32_t pt __unused, + TEE_Param params[TEE_NUM_PARAMS] __unused) +{ + return TEE_SUCCESS; +} + +static inline TEE_Result +pas_auth_authenticate(struct qcom_pas_session *s __unused, + uint32_t pas_id __unused) +{ + return TEE_SUCCESS; +} + +static inline TEE_Result +pas_auth_verify_reset(struct qcom_pas_session *s __unused, + TEE_TASessionHandle pta_session __unused, + uint32_t pas_id __unused, + TEE_Param params[TEE_NUM_PARAMS] __unused) +{ + return TEE_SUCCESS; +} + +static inline TEE_Result +pas_auth_release_metadata(struct qcom_pas_session *s __unused, + uint32_t pas_id __unused) +{ + return TEE_SUCCESS; +} +#endif /* CFG_QCOM_PAS_AUTH */ + +#endif /* __PAS_AUTH_H */ diff --git a/ta/qcom_pas/include/pas_mbn_parser.h b/ta/qcom_pas/include/pas_mbn_parser.h new file mode 100644 index 000000000..c32ada688 --- /dev/null +++ b/ta/qcom_pas/include/pas_mbn_parser.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __PAS_MBN_PARSER_H +#define __PAS_MBN_PARSER_H + +#include +#include +#include +#include + +/* + * On-wire layout of the INIT_IMAGE metadata blob: + * [ phdrs[0].p_filesz bytes ] ELF header + program-header table + * [ hash-segment bytes ] verbatim MBN hash-segment phdr content + * + * MBN hash segment, v6 (48-byte header): + * [header][qti meta][oem meta][hash table] + * [qti sig][qti certs][oem sig][oem certs] + * + * Hash table: one digest per ELF program header; entry 0 = digest of the ELF + * header plus program-header table, entry i = digest of the segment at phdr i. + */ + +#define PAS_MBN_VERSION_6 6 + +struct pas_mbn { + uint32_t version; + + const uint8_t *hash_table; + size_t hash_table_size; + uint32_t num_entries; + uint32_t hash_len; + + const uint8_t *signed_region; + size_t signed_region_size; + + const uint8_t *oem_meta; + size_t oem_meta_size; + const uint8_t *oem_sig; + size_t oem_sig_size; + const uint8_t *oem_certs; + size_t oem_certs_size; + + const uint8_t *qti_meta; + size_t qti_meta_size; + const uint8_t *qti_sig; + size_t qti_sig_size; + const uint8_t *qti_certs; + size_t qti_certs_size; + + bool uie_encrypted; +}; + +TEE_Result pas_mbn_parse(const uint8_t *md, size_t md_size, + uint32_t hash_len, struct pas_mbn *out); + +#endif /* __PAS_MBN_PARSER_H */ diff --git a/ta/qcom_pas/include/pas_mbn_parser_priv.h b/ta/qcom_pas/include/pas_mbn_parser_priv.h new file mode 100644 index 000000000..d01d6f4bb --- /dev/null +++ b/ta/qcom_pas/include/pas_mbn_parser_priv.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __PAS_MBN_PARSER_PRIV_H +#define __PAS_MBN_PARSER_PRIV_H + +#include +#include +#include + +/* MBN header field offsets (bytes from hash-segment start) */ +#define MBN_OFF_VERSION 0x04 +#define MBN_OFF_QC_SIG_SIZE 0x08 +#define MBN_OFF_QC_CERT_SIZE 0x0c +#define MBN_OFF_CODE_SIZE 0x14 +#define MBN_OFF_OEM_SIG_SIZE 0x1c +#define MBN_OFF_OEM_CERT_SIZE 0x24 +#define MBN_OFF_QC_META_SIZE 0x28 +#define MBN_OFF_OEM_META_SIZE 0x2c + +#define MBN_HDR_SIZE_V6 0x30 + +/* Read a little-endian uint32_t from @p. */ +uint32_t pas_mbn_read_u32(const uint8_t *p); + +TEE_Result pas_mbn_locate(const uint8_t *md, size_t md_size, + const uint8_t **seg, size_t *seg_size); + +TEE_Result pas_mbn_reserve_region(const uint8_t *segment, size_t segment_size, + size_t *offset, size_t len, + const uint8_t **region, size_t *region_len); + +#endif /* __PAS_MBN_PARSER_PRIV_H */ diff --git a/ta/qcom_pas/include/pas_meta.h b/ta/qcom_pas/include/pas_meta.h new file mode 100644 index 000000000..8d4740f2b --- /dev/null +++ b/ta/qcom_pas/include/pas_meta.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __PAS_META_H +#define __PAS_META_H + +#include +#include +#include +#include + +TEE_Result pas_meta_get_version(const uint8_t *meta_data, + size_t meta_data_size, uint32_t *version); + +TEE_Result pas_meta_segment_hash_len(const uint8_t *meta_data, + size_t meta_data_size, + uint32_t *hash_len); + +#endif /* __PAS_META_H */ diff --git a/ta/qcom_pas/include/qcom_pas_priv.h b/ta/qcom_pas/include/qcom_pas_priv.h new file mode 100644 index 000000000..4c0e51a57 --- /dev/null +++ b/ta/qcom_pas/include/qcom_pas_priv.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __QCOM_PAS_PRIV_H +#define __QCOM_PAS_PRIV_H + +#include +#include +#include +#include + +#define PAS_MD_SLOTS CFG_PAS_MD_SLOTS + +struct pas_md_slot { + void *meta_data; + size_t meta_data_size; + uint32_t pas_id; + bool in_use; + struct pas_mbn mbn; + bool ready; +}; + +struct qcom_pas_session { + struct pas_md_slot slots[PAS_MD_SLOTS]; +}; + +#endif /* __QCOM_PAS_PRIV_H */ diff --git a/ta/qcom_pas/src/pas_auth.c b/ta/qcom_pas/src/pas_auth.c new file mode 100644 index 000000000..6475d89ef --- /dev/null +++ b/ta/qcom_pas/src/pas_auth.c @@ -0,0 +1,178 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +static struct pas_md_slot *get_meta_data_slot(struct qcom_pas_session *s, + uint32_t pas_id) +{ + size_t i = 0; + + for (i = 0; i < PAS_MD_SLOTS; i++) { + if (s->slots[i].in_use && s->slots[i].pas_id == pas_id) + return &s->slots[i]; + } + + return NULL; +} + +TEE_Result pas_auth_save_metadata(struct qcom_pas_session *s, uint32_t pt, + TEE_Param params[TEE_NUM_PARAMS]) +{ + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_MEMREF_INPUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + struct pas_md_slot *slot = NULL; + void *meta_data_copy = NULL; + uint32_t pas_id = 0; + size_t size = 0; + size_t i = 0; + + if (pt != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + size = params[1].memref.size; + if (size && !params[1].memref.buffer) + return TEE_ERROR_BAD_PARAMETERS; + + pas_id = params[0].value.a; + + slot = get_meta_data_slot(s, pas_id); + if (!slot) { + for (i = 0; i < PAS_MD_SLOTS; i++) { + if (!s->slots[i].in_use) { + slot = &s->slots[i]; + goto found; + } + } + EMSG("PAS auth: no free md slot (pas_id=%#"PRIx32")", pas_id); + return TEE_ERROR_OUT_OF_MEMORY; + } + +found: + if (size) { + meta_data_copy = TEE_Malloc(size, TEE_MALLOC_FILL_ZERO); + if (!meta_data_copy) + return TEE_ERROR_OUT_OF_MEMORY; + memcpy(meta_data_copy, params[1].memref.buffer, size); + } + + TEE_Free(slot->meta_data); + slot->meta_data = meta_data_copy; + slot->meta_data_size = size; + slot->pas_id = pas_id; + slot->in_use = true; + slot->ready = false; + memset(&slot->mbn, 0, sizeof(slot->mbn)); + + return TEE_SUCCESS; +} + +TEE_Result pas_auth_authenticate(struct qcom_pas_session *s, uint32_t pas_id) +{ + struct pas_md_slot *slot = get_meta_data_slot(s, pas_id); + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t hash_len = 0; + + if (!slot) { + EMSG("PAS auth: no metadata for pas_id=%#"PRIx32 + " (call INIT_IMAGE first)", pas_id); + return TEE_ERROR_BAD_STATE; + } + + res = pas_meta_segment_hash_len(slot->meta_data, slot->meta_data_size, + &hash_len); + if (res) { + EMSG("PAS auth: cannot pick hash size: %#"PRIx32, res); + return res; + } + + res = pas_mbn_parse(slot->meta_data, slot->meta_data_size, hash_len, + &slot->mbn); + if (res) { + EMSG("PAS auth: MBN parse failed: %#"PRIx32, res); + return res; + } + + slot->ready = true; + + return TEE_SUCCESS; +} + +TEE_Result pas_auth_verify_reset(struct qcom_pas_session *s, + TEE_TASessionHandle pta_session, + uint32_t pas_id, + TEE_Param params[TEE_NUM_PARAMS]) +{ + struct pas_md_slot *slot = get_meta_data_slot(s, pas_id); + TEE_Param vp[TEE_NUM_PARAMS] = { }; + TEE_Result res = TEE_ERROR_GENERIC; + size_t buffer_size = 0; + uint8_t *buffer = NULL; + uint32_t pt = 0; + + if (!slot || !slot->ready) { + EMSG("PAS auth: pas_id=%#"PRIx32 + " is not ready (call INIT_IMAGE first)", pas_id); + return TEE_ERROR_BAD_STATE; + } + + if (ADD_OVERFLOW(slot->meta_data_size, slot->mbn.hash_table_size, + &buffer_size)) + return TEE_ERROR_OVERFLOW; + + buffer = TEE_Malloc(buffer_size, TEE_MALLOC_FILL_ZERO); + if (!buffer) + return TEE_ERROR_OUT_OF_MEMORY; + + TEE_MemMove(buffer, slot->meta_data, slot->meta_data_size); + TEE_MemMove(buffer + slot->meta_data_size, slot->mbn.hash_table, + slot->mbn.hash_table_size); + + pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_MEMREF_INPUT, + TEE_PARAM_TYPE_VALUE_INPUT); + + vp[0].value.a = params[0].value.a; + vp[0].value.b = params[0].value.b; + vp[1].value.a = params[1].value.a; + vp[1].value.b = params[1].value.b; + vp[2].memref.buffer = buffer; + vp[2].memref.size = buffer_size; + vp[3].value.a = slot->mbn.hash_len; + vp[3].value.b = slot->meta_data_size; + + res = TEE_InvokeTACommand(pta_session, TEE_TIMEOUT_INFINITE, + PTA_QCOM_PAS_VERIFY_IMAGE, pt, vp, NULL); + + TEE_Free(buffer); + return res; +} + +TEE_Result pas_auth_release_metadata(struct qcom_pas_session *s, + uint32_t pas_id) +{ + struct pas_md_slot *slot = get_meta_data_slot(s, pas_id); + + if (!slot) + return TEE_SUCCESS; + + TEE_Free(slot->meta_data); + slot->meta_data = NULL; + slot->meta_data_size = 0; + slot->ready = false; + memset(&slot->mbn, 0, sizeof(slot->mbn)); + + return TEE_SUCCESS; +} diff --git a/ta/qcom_pas/src/pas_mbn_parser.c b/ta/qcom_pas/src/pas_mbn_parser.c new file mode 100644 index 000000000..dede8e04f --- /dev/null +++ b/ta/qcom_pas/src/pas_mbn_parser.c @@ -0,0 +1,266 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* Header magic of the optional UIE encryption block in the hash segment. */ +#define UIE_ENC_PARAM_MAGIC 0x514D5349 /* "ISMQ" */ + +/* Segment type lives in bits 24:26 of p_flags; 0x2 marks the hash segment. */ +#define MBN_PT_FLAG_TYPE_MASK 0x07000000U +#define MBN_PT_FLAG_HASH_TYPE_MASK 0x02000000U + +uint32_t pas_mbn_read_u32(const uint8_t *p) +{ + uint32_t v = 0; + + memcpy(&v, p, sizeof(v)); + + return v; +} + +TEE_Result pas_mbn_locate(const uint8_t *md, size_t md_size, + const uint8_t **seg, size_t *seg_size) +{ + const unsigned char *ident = md; + size_t phdr_entry_len = 0; + size_t phdr_entry_min = 0; + size_t phdr_table_end = 0; + size_t phdr_offset = 0; + size_t phdr_count = 0; + uint32_t hash_flags = 0; + size_t hash_seg_len = 0; + size_t elf_hdr_len = 0; + size_t hash_seg_offset = 0; + size_t hash_seg_end = 0; + bool is_64 = false; + bool found = false; + size_t i = 0; + + if (md_size < EI_NIDENT) + return TEE_ERROR_BAD_FORMAT; + + if (ident[EI_MAG0] != ELFMAG0 || ident[EI_MAG1] != ELFMAG1 || + ident[EI_MAG2] != ELFMAG2 || ident[EI_MAG3] != ELFMAG3) + return TEE_ERROR_BAD_FORMAT; + + switch (ident[EI_CLASS]) { + case ELFCLASS64: + is_64 = true; + elf_hdr_len = sizeof(Elf64_Ehdr); + phdr_entry_min = sizeof(Elf64_Phdr); + break; + case ELFCLASS32: + is_64 = false; + elf_hdr_len = sizeof(Elf32_Ehdr); + phdr_entry_min = sizeof(Elf32_Phdr); + break; + default: + return TEE_ERROR_BAD_FORMAT; + } + + if (md_size < elf_hdr_len) + return TEE_ERROR_BAD_FORMAT; + + if (is_64) { + const Elf64_Ehdr *ehdr = (const void *)md; + + phdr_offset = ehdr->e_phoff; + phdr_entry_len = ehdr->e_phentsize; + phdr_count = ehdr->e_phnum; + } else { + const Elf32_Ehdr *ehdr = (const void *)md; + + phdr_offset = ehdr->e_phoff; + phdr_entry_len = ehdr->e_phentsize; + phdr_count = ehdr->e_phnum; + } + + if (phdr_count < 2 || phdr_offset != elf_hdr_len || + phdr_entry_len != phdr_entry_min) + return TEE_ERROR_BAD_FORMAT; + + if (MUL_OVERFLOW(phdr_entry_len, phdr_count, &phdr_table_end) || + ADD_OVERFLOW(phdr_table_end, phdr_offset, &phdr_table_end) || + phdr_table_end > md_size) + return TEE_ERROR_BAD_FORMAT; + + for (i = 0; i < phdr_count; i++) { + const uint8_t *p = md + phdr_offset + i * phdr_entry_len; + + if (is_64) { + const Elf64_Phdr *phdr = (const void *)p; + + hash_flags = phdr->p_flags; + hash_seg_len = phdr->p_filesz; + } else { + const Elf32_Phdr *phdr = (const void *)p; + + hash_flags = phdr->p_flags; + hash_seg_len = phdr->p_filesz; + } + + if ((hash_flags & MBN_PT_FLAG_TYPE_MASK) == + MBN_PT_FLAG_HASH_TYPE_MASK) { + found = true; + break; + } + } + if (!found) + return TEE_ERROR_BAD_FORMAT; + + /* + * The hash segment is packed immediately after the ELF header and + * program-header table, so its offset is the ELF header length plus + * the phdr table length. The phdr p_offset refers to the original + * firmware file and must not be used here. + */ + if (MUL_OVERFLOW(phdr_entry_len, phdr_count, &hash_seg_offset) || + ADD_OVERFLOW(hash_seg_offset, elf_hdr_len, &hash_seg_offset)) + return TEE_ERROR_BAD_FORMAT; + + if (ADD_OVERFLOW(hash_seg_offset, hash_seg_len, &hash_seg_end) || + hash_seg_end > md_size || !hash_seg_len) + return TEE_ERROR_BAD_FORMAT; + + *seg = md + hash_seg_offset; + *seg_size = hash_seg_len; + + return TEE_SUCCESS; +} + +TEE_Result pas_mbn_reserve_region(const uint8_t *segment, size_t segment_size, + size_t *offset, size_t len, + const uint8_t **region, size_t *region_len) +{ + if (!len) { + *region = NULL; + *region_len = 0; + return TEE_SUCCESS; + } + + if (len > segment_size || *offset > segment_size - len) + return TEE_ERROR_BAD_FORMAT; + + *region = segment + *offset; + *region_len = len; + *offset += len; + + return TEE_SUCCESS; +} + +TEE_Result pas_mbn_parse(const uint8_t *md, size_t md_size, + uint32_t hash_len, struct pas_mbn *out) +{ + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t oem_cert_size = 0; + uint32_t oem_meta_size = 0; + const uint8_t *seg = NULL; + uint32_t oem_sig_size = 0; + uint32_t qc_cert_size = 0; + uint32_t qc_meta_size = 0; + uint32_t qc_sig_size = 0; + size_t signed_size = 0; + uint32_t code_size = 0; + uint32_t version = 0; + size_t hdr_size = 0; + size_t seg_size = 0; + size_t cursor = 0; + + if (!md || !md_size || !out || !hash_len) + return TEE_ERROR_BAD_PARAMETERS; + + memset(out, 0, sizeof(*out)); + + res = pas_mbn_locate(md, md_size, &seg, &seg_size); + if (res) + return res; + + if (seg_size < MBN_HDR_SIZE_V6) + return TEE_ERROR_BAD_FORMAT; + + version = pas_mbn_read_u32(seg + MBN_OFF_VERSION); + if (version != PAS_MBN_VERSION_6) { + EMSG("PAS auth: unsupported MBN version %#"PRIx32, version); + return TEE_ERROR_BAD_FORMAT; + } + hdr_size = MBN_HDR_SIZE_V6; + + /* "code_size" is the hash-table length in bytes, not a code length. */ + code_size = pas_mbn_read_u32(seg + MBN_OFF_CODE_SIZE); + qc_sig_size = pas_mbn_read_u32(seg + MBN_OFF_QC_SIG_SIZE); + qc_cert_size = pas_mbn_read_u32(seg + MBN_OFF_QC_CERT_SIZE); + oem_sig_size = pas_mbn_read_u32(seg + MBN_OFF_OEM_SIG_SIZE); + oem_cert_size = pas_mbn_read_u32(seg + MBN_OFF_OEM_CERT_SIZE); + qc_meta_size = pas_mbn_read_u32(seg + MBN_OFF_QC_META_SIZE); + oem_meta_size = pas_mbn_read_u32(seg + MBN_OFF_OEM_META_SIZE); + + if (!code_size || code_size % hash_len) + return TEE_ERROR_BAD_FORMAT; + + /* + * Payload after the header: + * [qc_meta][oem_meta][hash table][qc_sig][qc_cert][oem_sig][oem_cert] + * The signed region spans the header plus + * [qc_meta || oem_meta || hash table]. + */ + cursor = hdr_size; + out->signed_region = seg; + + if (ADD_OVERFLOW(qc_meta_size, oem_meta_size, &signed_size) || + ADD_OVERFLOW(signed_size, code_size, &signed_size) || + ADD_OVERFLOW(signed_size, hdr_size, &signed_size)) + return TEE_ERROR_BAD_FORMAT; + + if (signed_size > seg_size) + return TEE_ERROR_BAD_FORMAT; + out->signed_region_size = signed_size; + + res = pas_mbn_reserve_region(seg, seg_size, &cursor, qc_meta_size, + &out->qti_meta, &out->qti_meta_size); + if (res) + return res; + res = pas_mbn_reserve_region(seg, seg_size, &cursor, oem_meta_size, + &out->oem_meta, &out->oem_meta_size); + if (res) + return res; + + out->hash_table = seg + cursor; + out->hash_table_size = code_size; + out->hash_len = hash_len; + out->num_entries = code_size / hash_len; + cursor += code_size; + + res = pas_mbn_reserve_region(seg, seg_size, &cursor, qc_sig_size, + &out->qti_sig, &out->qti_sig_size); + if (res) + return res; + res = pas_mbn_reserve_region(seg, seg_size, &cursor, qc_cert_size, + &out->qti_certs, &out->qti_certs_size); + if (res) + return res; + res = pas_mbn_reserve_region(seg, seg_size, &cursor, oem_sig_size, + &out->oem_sig, &out->oem_sig_size); + if (res) + return res; + res = pas_mbn_reserve_region(seg, seg_size, &cursor, oem_cert_size, + &out->oem_certs, &out->oem_certs_size); + if (res) + return res; + + out->version = version; + + if (cursor + sizeof(uint32_t) <= seg_size && + pas_mbn_read_u32(seg + cursor) == UIE_ENC_PARAM_MAGIC) + out->uie_encrypted = true; + + return TEE_SUCCESS; +} diff --git a/ta/qcom_pas/src/pas_meta.c b/ta/qcom_pas/src/pas_meta.c new file mode 100644 index 000000000..ae4f6c8b2 --- /dev/null +++ b/ta/qcom_pas/src/pas_meta.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include + +TEE_Result pas_meta_get_version(const uint8_t *meta_data, + size_t meta_data_size, uint32_t *version) +{ + TEE_Result res = TEE_ERROR_GENERIC; + const uint8_t *seg = NULL; + size_t seg_size = 0; + + if (!meta_data || !meta_data_size || !version) + return TEE_ERROR_BAD_PARAMETERS; + + res = pas_mbn_locate(meta_data, meta_data_size, &seg, &seg_size); + if (res) + return res; + + if (seg_size < MBN_OFF_VERSION + sizeof(uint32_t)) + return TEE_ERROR_BAD_FORMAT; + + *version = pas_mbn_read_u32(seg + MBN_OFF_VERSION); + + return TEE_SUCCESS; +} + +TEE_Result pas_meta_segment_hash_len(const uint8_t *meta_data, + size_t meta_data_size, uint32_t *hash_len) +{ + TEE_Result res = TEE_ERROR_GENERIC; + uint32_t version = 0; + + if (!hash_len) + return TEE_ERROR_BAD_PARAMETERS; + + res = pas_meta_get_version(meta_data, meta_data_size, &version); + if (res) + return res; + + switch (version) { + case PAS_MBN_VERSION_6: + *hash_len = TEE_SHA384_HASH_SIZE; + return TEE_SUCCESS; + default: + EMSG("PAS auth: unsupported MBN version %#"PRIx32, version); + return TEE_ERROR_NOT_SUPPORTED; + } +} diff --git a/ta/qcom_pas/src/qcom_pas.c b/ta/qcom_pas/src/qcom_pas.c index b0661d33c..0eb2185b2 100644 --- a/ta/qcom_pas/src/qcom_pas.c +++ b/ta/qcom_pas/src/qcom_pas.c @@ -1,9 +1,11 @@ // SPDX-License-Identifier: BSD-2-Clause /* - * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ +#include #include +#include #include #include #include @@ -13,23 +15,65 @@ static size_t session_refcount; static TEE_TASessionHandle pta_session; -static TEE_Result qcom_pas_auth_and_reset(uint32_t pt, +static TEE_Result qcom_pas_init_image(struct qcom_pas_session *s, uint32_t pt, + TEE_Param params[TEE_NUM_PARAMS]) +{ + TEE_Result res = TEE_ERROR_GENERIC; + + res = TEE_InvokeTACommand(pta_session, TEE_TIMEOUT_INFINITE, + PTA_QCOM_PAS_INIT_IMAGE, pt, params, NULL); + if (res) + return res; + + res = pas_auth_save_metadata(s, pt, params); + if (res) + return res; + + return pas_auth_authenticate(s, params[0].value.a); +} + +static TEE_Result qcom_pas_auth_and_reset(struct qcom_pas_session *s, + uint32_t pt, TEE_Param params[TEE_NUM_PARAMS]) { const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, TEE_PARAM_TYPE_VALUE_INPUT, TEE_PARAM_TYPE_MEMREF_INPUT, TEE_PARAM_TYPE_NONE); + TEE_Result res = TEE_ERROR_GENERIC; + if (pt != exp_pt) return TEE_ERROR_BAD_PARAMETERS; - /* Firmware authentication - TODO */ + res = pas_auth_verify_reset(s, pta_session, params[0].value.a, params); + if (res) + return res; return TEE_InvokeTACommand(pta_session, TEE_TIMEOUT_INFINITE, PTA_QCOM_PAS_AUTH_AND_RESET, pt, params, NULL); } +static TEE_Result qcom_pas_shutdown(struct qcom_pas_session *s, uint32_t pt, + TEE_Param params[TEE_NUM_PARAMS]) +{ + const uint32_t exp_pt = TEE_PARAM_TYPES(TEE_PARAM_TYPE_VALUE_INPUT, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE, + TEE_PARAM_TYPE_NONE); + TEE_Result res = TEE_ERROR_GENERIC; + + if (pt != exp_pt) + return TEE_ERROR_BAD_PARAMETERS; + + res = TEE_InvokeTACommand(pta_session, TEE_TIMEOUT_INFINITE, + PTA_QCOM_PAS_SHUTDOWN, pt, params, NULL); + + pas_auth_release_metadata(s, params[0].value.a); + + return res; +} + TEE_Result TA_CreateEntryPoint(void) { return TEE_SUCCESS; @@ -41,56 +85,78 @@ void TA_DestroyEntryPoint(void) TEE_Result TA_OpenSessionEntryPoint(uint32_t pt, TEE_Param params[TEE_NUM_PARAMS], - void **sess __unused) + void **sess_ctx) { static const TEE_UUID uuid = PTA_QCOM_PAS_UUID; - TEE_Result res = TEE_ERROR_GENERIC; TEE_PropSetHandle h = TEE_HANDLE_NULL; + TEE_Result res = TEE_ERROR_GENERIC; + struct qcom_pas_session *s = NULL; TEE_Identity id = { }; res = TEE_AllocatePropertyEnumerator(&h); - if (res != TEE_SUCCESS) - goto error; + if (res) + goto out; TEE_StartPropertyEnumerator(h, TEE_PROPSET_CURRENT_CLIENT); res = TEE_GetPropertyAsIdentity(h, NULL, &id); - if (res != TEE_SUCCESS) - goto error; + if (res) + goto out; if (id.login != TEE_LOGIN_REE_KERNEL) { res = TEE_ERROR_ACCESS_DENIED; - goto error; + goto out; + } + + s = TEE_Malloc(sizeof(*s), TEE_MALLOC_FILL_ZERO); + if (!s) { + res = TEE_ERROR_OUT_OF_MEMORY; + goto out; } if (!session_refcount) { res = TEE_OpenTASession(&uuid, TEE_TIMEOUT_INFINITE, pt, params, &pta_session, NULL); - if (res != TEE_SUCCESS) - goto error; + if (res) { + TEE_Free(s); + goto out; + } } session_refcount++; - res = TEE_SUCCESS; -error: + *sess_ctx = s; + +out: if (h) TEE_FreePropertyEnumerator(h); return res; } -void TA_CloseSessionEntryPoint(void *sess __unused) +void TA_CloseSessionEntryPoint(void *sess_ctx) { + struct qcom_pas_session *s = sess_ctx; + + if (s) { + size_t i = 0; + + for (i = 0; i < PAS_MD_SLOTS; i++) + TEE_Free(s->slots[i].meta_data); + TEE_Free(s); + } + session_refcount--; if (!session_refcount) TEE_CloseTASession(pta_session); } -TEE_Result TA_InvokeCommandEntryPoint(void *sess __unused, uint32_t cmd_id, +TEE_Result TA_InvokeCommandEntryPoint(void *sess_ctx, uint32_t cmd_id, uint32_t pt, TEE_Param params[TEE_NUM_PARAMS]) { + struct qcom_pas_session *s = sess_ctx; + switch (cmd_id) { case TA_QCOM_PAS_IS_SUPPORTED: return TEE_InvokeTACommand(pta_session, TEE_TIMEOUT_INFINITE, @@ -101,9 +167,7 @@ TEE_Result TA_InvokeCommandEntryPoint(void *sess __unused, uint32_t cmd_id, PTA_QCOM_PAS_CAPABILITIES, pt, params, NULL); case TA_QCOM_PAS_INIT_IMAGE: - return TEE_InvokeTACommand(pta_session, TEE_TIMEOUT_INFINITE, - PTA_QCOM_PAS_INIT_IMAGE, - pt, params, NULL); + return qcom_pas_init_image(s, pt, params); case TA_QCOM_PAS_MEM_SETUP: return TEE_InvokeTACommand(pta_session, TEE_TIMEOUT_INFINITE, PTA_QCOM_PAS_MEM_SETUP, @@ -113,15 +177,13 @@ TEE_Result TA_InvokeCommandEntryPoint(void *sess __unused, uint32_t cmd_id, PTA_QCOM_PAS_GET_RESOURCE_TABLE, pt, params, NULL); case TA_QCOM_PAS_AUTH_AND_RESET: - return qcom_pas_auth_and_reset(pt, params); + return qcom_pas_auth_and_reset(s, pt, params); case TA_QCOM_PAS_SET_REMOTE_STATE: return TEE_InvokeTACommand(pta_session, TEE_TIMEOUT_INFINITE, PTA_QCOM_PAS_SET_REMOTE_STATE, pt, params, NULL); case TA_QCOM_PAS_SHUTDOWN: - return TEE_InvokeTACommand(pta_session, TEE_TIMEOUT_INFINITE, - PTA_QCOM_PAS_SHUTDOWN, - pt, params, NULL); + return qcom_pas_shutdown(s, pt, params); default: return TEE_ERROR_NOT_IMPLEMENTED; } diff --git a/ta/qcom_pas/src/sub.mk b/ta/qcom_pas/src/sub.mk index f572b0463..c183ff77a 100644 --- a/ta/qcom_pas/src/sub.mk +++ b/ta/qcom_pas/src/sub.mk @@ -1 +1,3 @@ +global-incdirs-y += ../include srcs-y += qcom_pas.c +srcs-$(CFG_QCOM_PAS_AUTH) += pas_auth.c pas_mbn_parser.c pas_meta.c diff --git a/ta/qcom_pas/user_ta.mk b/ta/qcom_pas/user_ta.mk index 61a059b98..a13d7c7a1 100644 --- a/ta/qcom_pas/user_ta.mk +++ b/ta/qcom_pas/user_ta.mk @@ -1,4 +1,9 @@ user-ta-uuid := cff7d191-7ca0-4784-af13-48223b9a4fbe -# PAS TA heap size can be customized if 4kB is not enough +ifeq ($(CFG_QCOM_PAS_AUTH),y) +CFG_PAS_TA_HEAP_SIZE ?= (512 * 1024) +else CFG_PAS_TA_HEAP_SIZE ?= (4 * 1024) +endif + +CFG_QCOM_PAS_AUTH ?= n