From 86bd02c8e91b52a3f34436e3c5b22154b3721b88 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Mon, 25 May 2026 19:09:22 +0200 Subject: [PATCH 01/30] clk: qcom: move PAS clock driver to platform-specific directory Organizes PAS clock support under platform/$(PLATFORM_FLAVOR)/ so future platforms can provide their own PAS clock implementation. Signed-off-by: Jorge Ramirez-Ortiz Reviewed-by: Sumit Garg --- .../clk/qcom/{ => platform/kodiak}/clock-qcom-pas.c | 9 --------- core/drivers/clk/qcom/sub.mk | 5 ++++- core/include/drivers/clk_qcom.h | 8 ++++++++ 3 files changed, 12 insertions(+), 10 deletions(-) rename core/drivers/clk/qcom/{ => platform/kodiak}/clock-qcom-pas.c (96%) diff --git a/core/drivers/clk/qcom/clock-qcom-pas.c b/core/drivers/clk/qcom/platform/kodiak/clock-qcom-pas.c similarity index 96% rename from core/drivers/clk/qcom/clock-qcom-pas.c rename to core/drivers/clk/qcom/platform/kodiak/clock-qcom-pas.c index d664beef3..0d4621ef4 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 @@ -167,10 +165,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/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/include/drivers/clk_qcom.h b/core/include/drivers/clk_qcom.h index 9baac78cb..65756a9a4 100644 --- a/core/include/drivers/clk_qcom.h +++ b/core/include/drivers/clk_qcom.h @@ -38,6 +38,14 @@ 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); +#ifdef CFG_QCOM_PAS_PTA TEE_Result qcom_clock_enable_pas(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; +} +#endif #endif /* _CLK_QCOM_H_ */ From 0155ec3c5bcd24847e16be2713c04d93ccd5f569 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Thu, 21 May 2026 18:03:22 +0200 Subject: [PATCH 02/30] pta: qcom: pas: refactor into a descriptor/ops abstraction Move all Kodiak-specific logic and the PTA command handlers into platform/kodiak/, and model each subsystem with a descriptor/ops abstraction: every platform exposes a table via qcom_pas_platform_subsys() that the generic pas_core.c drives. Pure structural refactor. Signed-off-by: Jorge Ramirez-Ortiz Reviewed-by: Sumit Garg --- .../arm/plat-qcom/hoya/kodiak/target_config.h | 5 + core/pta/qcom/pas/compute.c | 22 --- core/pta/qcom/pas/lpass.c | 30 --- core/pta/qcom/pas/pas.c | 67 ------- core/pta/qcom/pas/pas.h | 95 --------- core/pta/qcom/pas/pas_core.c | 149 ++++++++++++++ core/pta/qcom/pas/platform/kodiak/cdsp.c | 97 +++++++++ core/pta/qcom/pas/platform/kodiak/cdsp.h | 13 ++ core/pta/qcom/pas/platform/kodiak/dsp_boot.h | 80 -------- .../pas/{dsp.c => platform/kodiak/dsp_fw.c} | 6 +- .../pas/{dsp.h => platform/kodiak/dsp_fw.h} | 16 +- core/pta/qcom/pas/platform/kodiak/lpass.c | 94 +++++++++ core/pta/qcom/pas/platform/kodiak/lpass.h | 13 ++ .../qcom/pas/platform/kodiak/pas_resources.h | 129 ------------ core/pta/qcom/pas/platform/kodiak/sub.mk | 3 + core/pta/qcom/pas/platform/kodiak/subsys.c | 64 ++++++ .../qcom/pas/{ => platform/kodiak}/venus.c | 23 ++- .../pas/platform/kodiak/{video.h => venus.h} | 10 +- core/pta/qcom/pas/platform/kodiak/wpss.c | 67 +++++++ core/pta/qcom/pas/platform/kodiak/wpss.h | 13 ++ core/pta/qcom/pas/platform/pas_data.h | 23 +++ core/pta/qcom/pas/platform/pas_subsys.h | 80 ++++++++ core/pta/qcom/pas/platform/platform_pas.h | 23 +++ core/pta/qcom/pas/platform/resource_table.h | 185 ++++++++++++++++++ core/pta/qcom/pas/pta_qcom_pas.c | 183 ++--------------- core/pta/qcom/pas/q6dsp.c | 24 --- core/pta/qcom/pas/sub.mk | 6 +- 27 files changed, 884 insertions(+), 636 deletions(-) delete mode 100644 core/pta/qcom/pas/compute.c delete mode 100644 core/pta/qcom/pas/lpass.c delete mode 100644 core/pta/qcom/pas/pas.c delete mode 100644 core/pta/qcom/pas/pas.h create mode 100644 core/pta/qcom/pas/pas_core.c create mode 100644 core/pta/qcom/pas/platform/kodiak/cdsp.c create mode 100644 core/pta/qcom/pas/platform/kodiak/cdsp.h delete mode 100644 core/pta/qcom/pas/platform/kodiak/dsp_boot.h rename core/pta/qcom/pas/{dsp.c => platform/kodiak/dsp_fw.c} (92%) rename core/pta/qcom/pas/{dsp.h => platform/kodiak/dsp_fw.h} (62%) create mode 100644 core/pta/qcom/pas/platform/kodiak/lpass.c create mode 100644 core/pta/qcom/pas/platform/kodiak/lpass.h delete mode 100644 core/pta/qcom/pas/platform/kodiak/pas_resources.h create mode 100644 core/pta/qcom/pas/platform/kodiak/sub.mk create mode 100644 core/pta/qcom/pas/platform/kodiak/subsys.c rename core/pta/qcom/pas/{ => platform/kodiak}/venus.c (73%) rename core/pta/qcom/pas/platform/kodiak/{video.h => venus.h} (63%) create mode 100644 core/pta/qcom/pas/platform/kodiak/wpss.c create mode 100644 core/pta/qcom/pas/platform/kodiak/wpss.h create mode 100644 core/pta/qcom/pas/platform/pas_data.h create mode 100644 core/pta/qcom/pas/platform/pas_subsys.h create mode 100644 core/pta/qcom/pas/platform/platform_pas.h create mode 100644 core/pta/qcom/pas/platform/resource_table.h delete mode 100644 core/pta/qcom/pas/q6dsp.c diff --git a/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h b/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h index 77354f49e..2243878ec 100644 --- a/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h +++ b/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h @@ -39,4 +39,9 @@ #define IRIS_BASE UL(0x0aa00000) #define IRIS_SIZE ULL(0x00200000) +#define PAS_ID_QDSP6 1 +#define PAS_ID_WPSS 6 +#define PAS_ID_VENUS 9 +#define PAS_ID_TURING 18 + #endif /* TARGET_CONFIG_H */ diff --git a/core/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_core.c b/core/pta/qcom/pas/pas_core.c new file mode 100644 index 000000000..403fb9eba --- /dev/null +++ b/core/pta/qcom/pas/pas_core.c @@ -0,0 +1,149 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include + +#include "pas_subsys.h" + +static 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); + struct qcom_pas_data *data = NULL; + TEE_Result res = TEE_ERROR_GENERIC; + + 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); + + if (!subsys || !subsys->ops->fw_shutdown) + return TEE_ERROR_NOT_SUPPORTED; + + return subsys->ops->fw_shutdown(&subsys->data); +} 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/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..540fc1c24 --- /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 venus.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..b14e80b92 --- /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 "lpass.h" +#include "pas_subsys.h" +#include "venus.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_VENUS, + .base.pa = IRIS_BASE, + .size = IRIS_SIZE, + }, + .ops = &venus_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/venus.c b/core/pta/qcom/pas/platform/kodiak/venus.c similarity index 73% rename from core/pta/qcom/pas/venus.c rename to core/pta/qcom/pas/platform/kodiak/venus.c index deea980a5..d429123ff 100644 --- a/core/pta/qcom/pas/venus.c +++ b/core/pta/qcom/pas/platform/kodiak/venus.c @@ -4,12 +4,10 @@ */ #include -#include #include #include -#include "pas.h" -#include "video.h" +#include "venus.h" #define WRAPPER_IRIS_VERSION 0x0 #define WRAPPER_CPA_START_ADDR 0x1020 @@ -23,8 +21,9 @@ static TEE_Result venus_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 venus_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,12 +58,12 @@ 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 venus_fw_start(struct qcom_pas_data *data) { return venus_reset_cpu(data); } -TEE_Result venus_fw_set_state(struct qcom_pas_data *data, bool power_on) +static TEE_Result venus_fw_set_state(struct qcom_pas_data *data, bool power_on) { if (power_on) return venus_reset_cpu(data); @@ -71,3 +71,8 @@ TEE_Result venus_fw_set_state(struct qcom_pas_data *data, bool power_on) return venus_fw_shutdown(data); } +const struct qcom_pas_ops venus_ops = { + .fw_start = venus_fw_start, + .fw_shutdown = venus_fw_shutdown, + .fw_set_state = venus_fw_set_state, +}; diff --git a/core/pta/qcom/pas/platform/kodiak/video.h b/core/pta/qcom/pas/platform/kodiak/venus.h similarity index 63% rename from core/pta/qcom/pas/platform/kodiak/video.h rename to core/pta/qcom/pas/platform/kodiak/venus.h index da31db4b0..8886f394c 100644 --- a/core/pta/qcom/pas/platform/kodiak/video.h +++ b/core/pta/qcom/pas/platform/kodiak/venus.h @@ -3,10 +3,14 @@ * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. */ -#ifndef _VIDEO_H_ -#define _VIDEO_H_ +#ifndef _VENUS_H_ +#define _VENUS_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 venus_ops; + +#endif /* _VENUS_H_ */ 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/pas_data.h b/core/pta/qcom/pas/platform/pas_data.h new file mode 100644 index 000000000..cbd229211 --- /dev/null +++ b/core/pta/qcom/pas/platform/pas_data.h @@ -0,0 +1,23 @@ +/* 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 + +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..dd526f84d --- /dev/null +++ b/core/pta/qcom/pas/platform/pas_subsys.h @@ -0,0 +1,80 @@ +/* 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); + +#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..7bb596be7 --- /dev/null +++ b/core/pta/qcom/pas/platform/platform_pas.h @@ -0,0 +1,23 @@ +/* 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 + +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); + +#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..13d5a10d6 100644 --- a/core/pta/qcom/pas/pta_qcom_pas.c +++ b/core/pta/qcom/pas/pta_qcom_pas.c @@ -7,43 +7,15 @@ #include #include #include +#include #include #include -#include "pas.h" - #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 +26,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 +41,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[1].value.a); } static TEE_Result qcom_pas_init_image(uint32_t pt, @@ -94,16 +58,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 +70,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,17 +88,11 @@ 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 @@ -187,13 +106,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,55 +119,12 @@ 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 @@ -265,23 +138,9 @@ 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 TEE_ERROR_NOT_IMPLEMENTED; + return pas_platform_shutdown(params[0].value.a); } static TEE_Result pta_qcom_pas_invoke_command(void *session __unused, @@ -320,8 +179,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 +192,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..06bdd894c 100644 --- a/core/pta/qcom/pas/sub.mk +++ b/core/pta/qcom/pas/sub.mk @@ -1,3 +1,5 @@ -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 incdirs-y += . -incdirs-y += platform/$(PLATFORM_FLAVOR) +incdirs-y += platform/ +subdirs-y += platform/$(PLATFORM_FLAVOR) From 6d4a8e830a5c86a74ec47f749b188b5dc9c28e88 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Mon, 8 Jun 2026 19:34:25 +0200 Subject: [PATCH 03/30] clk: qcom: add reusable Lucid-EVO PLL helper Add qcom_clock_lucidevo_pll_enable(), a self-contained helper that configures, locks and enables the main output of a Lucid-EVO PLL given its register block base and a struct qcom_lucidevo_pll_config. No caller yet; this provides the building block for per-processor PLL bring-up. Signed-off-by: Jorge Ramirez-Ortiz Reviewed-by: Sumit Garg --- core/drivers/clk/qcom/clock-qcom.c | 97 ++++++++++++++++++++++++++++++ core/include/drivers/clk_qcom.h | 21 +++++++ 2 files changed, 118 insertions(+) diff --git a/core/drivers/clk/qcom/clock-qcom.c b/core/drivers/clk/qcom/clock-qcom.c index b1e9c6fe2..a70d4d56f 100644 --- a/core/drivers/clk/qcom/clock-qcom.c +++ b/core/drivers/clk/qcom/clock-qcom.c @@ -15,6 +15,41 @@ 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 + +/* 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 + +/* 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); @@ -34,6 +69,68 @@ 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_clock_set_rate(vaddr_t cfg_rcgr, vaddr_t cmd_rcgr, uint32_t cfg_value) { diff --git a/core/include/drivers/clk_qcom.h b/core/include/drivers/clk_qcom.h index 65756a9a4..d4f6f15b8 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 @@ -34,10 +35,30 @@ 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) */ +}; + 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); #ifdef CFG_QCOM_PAS_PTA TEE_Result qcom_clock_enable_pas(enum qcom_clk_group group); #else From b6ccd16f66c7540b47cccf40f9f513eac6929832 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Wed, 10 Jun 2026 18:15:35 +0200 Subject: [PATCH 04/30] qcom: pas: lemans: enable CDSP0/1 PAS support Bring up the Compute DSP (CDSP0/1) via the PAS peripheral authentication path on the Lemans platform. Signed-off-by: Jorge Ramirez-Ortiz Reviewed-by: Sumit Garg --- core/arch/arm/plat-qcom/hoya/lemans/target.mk | 6 + .../arm/plat-qcom/hoya/lemans/target_config.h | 29 ++ core/drivers/clk/qcom/clock-qcom.c | 9 + .../clk/qcom/platform/kodiak/clock-qcom-pas.c | 12 + .../clk/qcom/platform/lemans/clock-qcom-pas.c | 316 ++++++++++++++++++ .../qcom/platform/lemans/clock_group_qcom.h | 98 ++++++ core/include/drivers/clk_qcom.h | 15 + core/pta/qcom/pas/platform/lemans/cdsp0.c | 292 ++++++++++++++++ core/pta/qcom/pas/platform/lemans/cdsp0.h | 13 + core/pta/qcom/pas/platform/lemans/cdsp1.c | 250 ++++++++++++++ core/pta/qcom/pas/platform/lemans/cdsp1.h | 13 + core/pta/qcom/pas/platform/lemans/sub.mk | 3 + core/pta/qcom/pas/platform/lemans/subsys.c | 43 +++ 13 files changed, 1099 insertions(+) create mode 100644 core/drivers/clk/qcom/platform/lemans/clock-qcom-pas.c create mode 100644 core/pta/qcom/pas/platform/lemans/cdsp0.c create mode 100644 core/pta/qcom/pas/platform/lemans/cdsp0.h create mode 100644 core/pta/qcom/pas/platform/lemans/cdsp1.c create mode 100644 core/pta/qcom/pas/platform/lemans/cdsp1.h create mode 100644 core/pta/qcom/pas/platform/lemans/sub.mk create mode 100644 core/pta/qcom/pas/platform/lemans/subsys.c diff --git a/core/arch/arm/plat-qcom/hoya/lemans/target.mk b/core/arch/arm/plat-qcom/hoya/lemans/target.mk index c339ac0e5..bce7dea6d 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target.mk +++ b/core/arch/arm/plat-qcom/hoya/lemans/target.mk @@ -12,3 +12,9 @@ $(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_IN_TREE_EARLY_TAS += qcom_pas/cff7d191-7ca0-4784-af13-48223b9a4fbe +endif 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..fe54062bf 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,33 @@ #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) + +#define PAS_ID_TURING 18 +#define PAS_ID_TURING1 30 + +/* 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) + #endif /* TARGET_CONFIG_H */ diff --git a/core/drivers/clk/qcom/clock-qcom.c b/core/drivers/clk/qcom/clock-qcom.c index a70d4d56f..bae21986c 100644 --- a/core/drivers/clk/qcom/clock-qcom.c +++ b/core/drivers/clk/qcom/clock-qcom.c @@ -61,6 +61,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) @@ -150,6 +158,7 @@ TEE_Result qcom_clock_enable(enum qcom_clk_group group) { switch (group) { case QCOM_CLKS_TURING: + case QCOM_CLKS_TURING1: case QCOM_CLKS_LPASS: case QCOM_CLKS_WPSS: return qcom_clock_enable_pas(group); diff --git a/core/drivers/clk/qcom/platform/kodiak/clock-qcom-pas.c b/core/drivers/clk/qcom/platform/kodiak/clock-qcom-pas.c index 0d4621ef4..2aa66ca28 100644 --- a/core/drivers/clk/qcom/platform/kodiak/clock-qcom-pas.c +++ b/core/drivers/clk/qcom/platform/kodiak/clock-qcom-pas.c @@ -120,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 }; 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..ee5ca4f8f --- /dev/null +++ b/core/drivers/clk/qcom/platform/lemans/clock-qcom-pas.c @@ -0,0 +1,316 @@ +// 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, 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; +} + +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); + 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; +} + +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); + 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; + 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..0acd7c7e5 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,102 @@ #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 + #endif /* _CLOCK_GROUP_QCOM_H_ */ diff --git a/core/include/drivers/clk_qcom.h b/core/include/drivers/clk_qcom.h index d4f6f15b8..fb6312be7 100644 --- a/core/include/drivers/clk_qcom.h +++ b/core/include/drivers/clk_qcom.h @@ -26,6 +26,7 @@ enum qcom_clk_group { QCOM_CLKS_WPSS, QCOM_CLKS_TURING, + QCOM_CLKS_TURING1, QCOM_CLKS_LPASS, QCOM_CLKS_MAX, }; @@ -61,12 +62,26 @@ TEE_Result qcom_lucidevo_pll_enable(vaddr_t pll_base, const struct qcom_lucidevo_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/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/sub.mk b/core/pta/qcom/pas/platform/lemans/sub.mk new file mode 100644 index 000000000..8ca9b5dbf --- /dev/null +++ b/core/pta/qcom/pas/platform/lemans/sub.mk @@ -0,0 +1,3 @@ +srcs-y += subsys.c cdsp0.c cdsp1.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..985d49dd4 --- /dev/null +++ b/core/pta/qcom/pas/platform/lemans/subsys.c @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include + +#include "cdsp0.h" +#include "cdsp1.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, + }, +}; + +struct qcom_pas_subsys *qcom_pas_platform_subsys(size_t *count) +{ + *count = ARRAY_SIZE(subsystems); + + return subsystems; +} From 757e4d92bdabfd12b7bed4c3d6fd26e0872f602f Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Mon, 15 Jun 2026 07:56:52 +0200 Subject: [PATCH 05/30] plat-qcom: lemans: reserve VA space for PAS subsystem mappings Each PAS subsystem maps its controller window at runtime via core_mmu_add_mapping(); these late mappings come from CFG_RESERVED_VASPACE_SIZE and are never released. The six DSP windows total ~146.5 MB but the previous 60 MB default fits only one, so reserve 256 MB to cover them with headroom. Signed-off-by: Jorge Ramirez-Ortiz Reviewed-by: Sumit Garg --- core/arch/arm/plat-qcom/hoya/lemans/target.mk | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/arch/arm/plat-qcom/hoya/lemans/target.mk b/core/arch/arm/plat-qcom/hoya/lemans/target.mk index bce7dea6d..930e33ef0 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target.mk +++ b/core/arch/arm/plat-qcom/hoya/lemans/target.mk @@ -16,5 +16,9 @@ endif CFG_QCOM_PAS_PTA ?= y ifeq ($(CFG_QCOM_PAS_PTA),y) +# PAS subsystems map their controller windows at runtime from the reserved VA +# pool (never released). The six DSP windows total ~146.5 MB; the 60 MB default +# fits only one, so reserve 256 MB with headroom. +CFG_RESERVED_VASPACE_SIZE ?= (256 * 1024 * 1024) CFG_IN_TREE_EARLY_TAS += qcom_pas/cff7d191-7ca0-4784-af13-48223b9a4fbe endif From 531523c303eacd182b1a8dafd61990ddf006ef55 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Thu, 11 Jun 2026 10:17:25 +0200 Subject: [PATCH 06/30] pta: qcom: pas: lemans: add LPASS (ADSP) bring-up Add LPASS / ADSP (QDSP6 v68/v69) PAS bring-up for the Lemans platform (IQ-9075-EVK), following the existing Lemans CDSP0/1 PAS + clock-driver pattern and the Kodiak LPASS PTA layout. Signed-off-by: Jorge Ramirez-Ortiz Reviewed-by: Sumit Garg --- .../arm/plat-qcom/hoya/lemans/target_config.h | 8 + .../clk/qcom/platform/lemans/clock-qcom-pas.c | 81 +++++ .../qcom/platform/lemans/clock_group_qcom.h | 49 +++ core/pta/qcom/pas/platform/lemans/lpass.c | 290 ++++++++++++++++++ core/pta/qcom/pas/platform/lemans/lpass.h | 13 + core/pta/qcom/pas/platform/lemans/sub.mk | 2 +- core/pta/qcom/pas/platform/lemans/subsys.c | 11 + 7 files changed, 453 insertions(+), 1 deletion(-) create mode 100644 core/pta/qcom/pas/platform/lemans/lpass.c create mode 100644 core/pta/qcom/pas/platform/lemans/lpass.h 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 fe54062bf..7ad5c62ba 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target_config.h +++ b/core/arch/arm/plat-qcom/hoya/lemans/target_config.h @@ -36,8 +36,16 @@ #define TURING_1_BASE UL(0x28000000) #define TURING_1_SIZE UL(0x03000000) +/* + * 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) + #define PAS_ID_TURING 18 #define PAS_ID_TURING1 30 +#define PAS_ID_QDSP6 1 /* CDSP0 content-protection shared channel (secure DDR); no CDSP1 equivalent. */ #define CDSP_SECCHANNEL_BASE UL(0xdb1dc000) diff --git a/core/drivers/clk/qcom/platform/lemans/clock-qcom-pas.c b/core/drivers/clk/qcom/platform/lemans/clock-qcom-pas.c index ee5ca4f8f..ba41a147f 100644 --- a/core/drivers/clk/qcom/platform/lemans/clock-qcom-pas.c +++ b/core/drivers/clk/qcom/platform/lemans/clock-qcom-pas.c @@ -112,6 +112,71 @@ static TEE_Result cdsp_enable_processor(paddr_t turing_base) 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); +} + TEE_Result qcom_clock_enable_pas_processor(enum qcom_clk_group group) { switch (group) { @@ -119,6 +184,13 @@ TEE_Result qcom_clock_enable_pas_processor(enum qcom_clk_group group) 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; default: return TEE_ERROR_NOT_SUPPORTED; } @@ -271,6 +343,13 @@ TEE_Result qcom_clock_pas_reset(enum qcom_clk_group group) return cdsp_reset_processor(&cdsp0_reset_regs); case QCOM_CLKS_TURING1: return cdsp_reset_processor(&cdsp1_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; } @@ -305,6 +384,8 @@ TEE_Result qcom_clock_enable_pas(enum qcom_clk_group group) if (res != TEE_SUCCESS) goto timeout; break; + case QCOM_CLKS_LPASS: + return lpass_setup(); default: return TEE_ERROR_NOT_SUPPORTED; } 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 0acd7c7e5..3cedb8207 100644 --- a/core/drivers/clk/qcom/platform/lemans/clock_group_qcom.h +++ b/core/drivers/clk/qcom/platform/lemans/clock_group_qcom.h @@ -107,4 +107,53 @@ #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 + #endif /* _CLOCK_GROUP_QCOM_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 index 8ca9b5dbf..22f3cc5ca 100644 --- a/core/pta/qcom/pas/platform/lemans/sub.mk +++ b/core/pta/qcom/pas/platform/lemans/sub.mk @@ -1,3 +1,3 @@ -srcs-y += subsys.c cdsp0.c cdsp1.c +srcs-y += subsys.c cdsp0.c cdsp1.c lpass.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 index 985d49dd4..3d121fb2d 100644 --- a/core/pta/qcom/pas/platform/lemans/subsys.c +++ b/core/pta/qcom/pas/platform/lemans/subsys.c @@ -10,6 +10,7 @@ #include "cdsp0.h" #include "cdsp1.h" +#include "lpass.h" #include "pas_subsys.h" static struct qcom_pas_subsys subsystems[] = { @@ -33,6 +34,16 @@ static struct qcom_pas_subsys subsystems[] = { .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, + }, }; struct qcom_pas_subsys *qcom_pas_platform_subsys(size_t *count) From 5629674e6125c922f180ec99664a47654a88406a Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Fri, 12 Jun 2026 09:30:42 +0200 Subject: [PATCH 07/30] core: mm: grow memory map in core_mmu_add_mapping() when full After boot the static memory map is frozen at count + 5 entries, so core_mmu_add_mapping() failed once those spare slots were exhausted. Grow the map through the same realloc hook as every other add path, re-resolving RES_VASPACE afterwards. Signed-off-by: Jorge Ramirez-Ortiz Reviewed-by: Sumit Garg --- core/mm/core_mmu.c | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) 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, From 3da7935dcf995c5ac9fe888ed0f2d2cb085b7671 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Fri, 12 Jun 2026 08:03:50 +0200 Subject: [PATCH 08/30] pta: qcom: pas: lemans: add IRIS video-codec bring-up Add the IRIS video-codec PAS driver for lemans, mirroring the kodiak venus driver (which already targets IRIS hardware). The lemans IRIS register layout is identical: WRAPPER_TZ at IRIS+0xc0000 with the same XTSS_SW_RESET / FW / CPA / NONPIX offsets. Signed-off-by: Jorge Ramirez-Ortiz Reviewed-by: Sumit Garg --- .../arm/plat-qcom/hoya/lemans/target_config.h | 9 +++ core/pta/qcom/pas/platform/lemans/iris.c | 78 +++++++++++++++++++ core/pta/qcom/pas/platform/lemans/iris.h | 16 ++++ core/pta/qcom/pas/platform/lemans/sub.mk | 2 +- core/pta/qcom/pas/platform/lemans/subsys.c | 10 +++ 5 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 core/pta/qcom/pas/platform/lemans/iris.c create mode 100644 core/pta/qcom/pas/platform/lemans/iris.h 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 7ad5c62ba..af2746084 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target_config.h +++ b/core/arch/arm/plat-qcom/hoya/lemans/target_config.h @@ -43,9 +43,18 @@ #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) + #define PAS_ID_TURING 18 #define PAS_ID_TURING1 30 #define PAS_ID_QDSP6 1 +#define PAS_ID_IRIS 9 /* CDSP0 content-protection shared channel (secure DDR); no CDSP1 equivalent. */ #define CDSP_SECCHANNEL_BASE UL(0xdb1dc000) 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/sub.mk b/core/pta/qcom/pas/platform/lemans/sub.mk index 22f3cc5ca..0e8cab070 100644 --- a/core/pta/qcom/pas/platform/lemans/sub.mk +++ b/core/pta/qcom/pas/platform/lemans/sub.mk @@ -1,3 +1,3 @@ -srcs-y += subsys.c cdsp0.c cdsp1.c lpass.c +srcs-y += subsys.c cdsp0.c cdsp1.c lpass.c iris.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 index 3d121fb2d..7501dd8df 100644 --- a/core/pta/qcom/pas/platform/lemans/subsys.c +++ b/core/pta/qcom/pas/platform/lemans/subsys.c @@ -10,6 +10,7 @@ #include "cdsp0.h" #include "cdsp1.h" +#include "iris.h" #include "lpass.h" #include "pas_subsys.h" @@ -44,6 +45,15 @@ static struct qcom_pas_subsys subsystems[] = { .ops = &lpass_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, + }, }; struct qcom_pas_subsys *qcom_pas_platform_subsys(size_t *count) From ac82789235954ec978c8e35f8201236153d793d8 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Sun, 14 Jun 2026 18:49:39 +0200 Subject: [PATCH 09/30] pta: qcom: pas: lemans: add GP-DSP0/1 bring-up Add PAS bring-up for the two general-purpose Hexagon DSPs on Lemans (SA8775P): GP-DSP0 (TURINGGDSP, image id 39) and GP-DSP1 (TURINGGDSP1, image id 40). This follows the same architecture as the existing CDSP0/1, LPASS and IRIS subsystems. Signed-off-by: Jorge Ramirez-Ortiz Reviewed-by: Sumit Garg --- .../arm/plat-qcom/hoya/lemans/target_config.h | 23 ++ core/drivers/clk/qcom/clock-qcom.c | 2 + .../clk/qcom/platform/lemans/clock-qcom-pas.c | 208 +++++++++++++ .../qcom/platform/lemans/clock_group_qcom.h | 68 ++++ core/include/drivers/clk_qcom.h | 2 + core/pta/qcom/pas/platform/lemans/gpdsp0.c | 283 +++++++++++++++++ core/pta/qcom/pas/platform/lemans/gpdsp0.h | 13 + core/pta/qcom/pas/platform/lemans/gpdsp1.c | 291 ++++++++++++++++++ core/pta/qcom/pas/platform/lemans/gpdsp1.h | 13 + core/pta/qcom/pas/platform/lemans/sub.mk | 2 +- core/pta/qcom/pas/platform/lemans/subsys.c | 22 ++ 11 files changed, 926 insertions(+), 1 deletion(-) create mode 100644 core/pta/qcom/pas/platform/lemans/gpdsp0.c create mode 100644 core/pta/qcom/pas/platform/lemans/gpdsp0.h create mode 100644 core/pta/qcom/pas/platform/lemans/gpdsp1.c create mode 100644 core/pta/qcom/pas/platform/lemans/gpdsp1.h 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 af2746084..6510709fb 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target_config.h +++ b/core/arch/arm/plat-qcom/hoya/lemans/target_config.h @@ -36,6 +36,17 @@ #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). @@ -55,6 +66,8 @@ #define PAS_ID_TURING1 30 #define PAS_ID_QDSP6 1 #define PAS_ID_IRIS 9 +#define PAS_ID_GPDSP0 39 +#define PAS_ID_GPDSP1 40 /* CDSP0 content-protection shared channel (secure DDR); no CDSP1 equivalent. */ #define CDSP_SECCHANNEL_BASE UL(0xdb1dc000) @@ -76,4 +89,14 @@ #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) + #endif /* TARGET_CONFIG_H */ diff --git a/core/drivers/clk/qcom/clock-qcom.c b/core/drivers/clk/qcom/clock-qcom.c index bae21986c..a84964842 100644 --- a/core/drivers/clk/qcom/clock-qcom.c +++ b/core/drivers/clk/qcom/clock-qcom.c @@ -161,6 +161,8 @@ TEE_Result qcom_clock_enable(enum qcom_clk_group group) case QCOM_CLKS_TURING1: case QCOM_CLKS_LPASS: case QCOM_CLKS_WPSS: + case QCOM_CLKS_GPDSP0: + case QCOM_CLKS_GPDSP1: return qcom_clock_enable_pas(group); default: EMSG("Unsupported clock group %d\n", group); diff --git a/core/drivers/clk/qcom/platform/lemans/clock-qcom-pas.c b/core/drivers/clk/qcom/platform/lemans/clock-qcom-pas.c index ba41a147f..244750925 100644 --- a/core/drivers/clk/qcom/platform/lemans/clock-qcom-pas.c +++ b/core/drivers/clk/qcom/platform/lemans/clock-qcom-pas.c @@ -24,6 +24,8 @@ 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) @@ -177,6 +179,67 @@ static TEE_Result lpass_setup(void) 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) { @@ -191,6 +254,14 @@ TEE_Result qcom_clock_enable_pas_processor(enum qcom_clk_group group) * 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; } @@ -336,6 +407,133 @@ static TEE_Result cdsp_reset_processor(const struct cdsp_reset_regs *r) 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) { @@ -343,6 +541,10 @@ TEE_Result qcom_clock_pas_reset(enum qcom_clk_group group) 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; @@ -386,6 +588,12 @@ TEE_Result qcom_clock_enable_pas(enum qcom_clk_group group) 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; } 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 3cedb8207..8dd0bcd14 100644 --- a/core/drivers/clk/qcom/platform/lemans/clock_group_qcom.h +++ b/core/drivers/clk/qcom/platform/lemans/clock_group_qcom.h @@ -156,4 +156,72 @@ #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/include/drivers/clk_qcom.h b/core/include/drivers/clk_qcom.h index fb6312be7..b4c5d3579 100644 --- a/core/include/drivers/clk_qcom.h +++ b/core/include/drivers/clk_qcom.h @@ -28,6 +28,8 @@ enum qcom_clk_group { QCOM_CLKS_TURING, QCOM_CLKS_TURING1, QCOM_CLKS_LPASS, + QCOM_CLKS_GPDSP0, + QCOM_CLKS_GPDSP1, QCOM_CLKS_MAX, }; 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/sub.mk b/core/pta/qcom/pas/platform/lemans/sub.mk index 0e8cab070..f770bf1be 100644 --- a/core/pta/qcom/pas/platform/lemans/sub.mk +++ b/core/pta/qcom/pas/platform/lemans/sub.mk @@ -1,3 +1,3 @@ -srcs-y += subsys.c cdsp0.c cdsp1.c lpass.c iris.c +srcs-y += subsys.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 index 7501dd8df..7be6784a7 100644 --- a/core/pta/qcom/pas/platform/lemans/subsys.c +++ b/core/pta/qcom/pas/platform/lemans/subsys.c @@ -10,6 +10,8 @@ #include "cdsp0.h" #include "cdsp1.h" +#include "gpdsp0.h" +#include "gpdsp1.h" #include "iris.h" #include "lpass.h" #include "pas_subsys.h" @@ -45,6 +47,26 @@ static struct qcom_pas_subsys subsystems[] = { .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, From 597d83b6ed4f5f75d7621e99dfdd8e26fd2e5b77 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Wed, 15 Jul 2026 15:47:56 +0200 Subject: [PATCH 10/30] pta: qcom: pas: kodiak: rename venus to iris The subsystem manages the Iris video core, so name the file and its symbols accordingly to match the hardware it drives. Signed-off-by: Jorge Ramirez-Ortiz Reviewed-by: Sumit Garg --- .../arm/plat-qcom/hoya/kodiak/target_config.h | 2 +- .../pas/platform/kodiak/{venus.c => iris.c} | 24 +++++++++---------- .../pas/platform/kodiak/{venus.h => iris.h} | 8 +++---- core/pta/qcom/pas/platform/kodiak/sub.mk | 2 +- core/pta/qcom/pas/platform/kodiak/subsys.c | 6 ++--- 5 files changed, 21 insertions(+), 21 deletions(-) rename core/pta/qcom/pas/platform/kodiak/{venus.c => iris.c} (73%) rename core/pta/qcom/pas/platform/kodiak/{venus.h => iris.h} (70%) diff --git a/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h b/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h index 2243878ec..21031a272 100644 --- a/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h +++ b/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h @@ -41,7 +41,7 @@ #define PAS_ID_QDSP6 1 #define PAS_ID_WPSS 6 -#define PAS_ID_VENUS 9 +#define PAS_ID_IRIS 9 #define PAS_ID_TURING 18 #endif /* TARGET_CONFIG_H */ diff --git a/core/pta/qcom/pas/platform/kodiak/venus.c b/core/pta/qcom/pas/platform/kodiak/iris.c similarity index 73% rename from core/pta/qcom/pas/platform/kodiak/venus.c rename to core/pta/qcom/pas/platform/kodiak/iris.c index d429123ff..54f60a162 100644 --- a/core/pta/qcom/pas/platform/kodiak/venus.c +++ b/core/pta/qcom/pas/platform/kodiak/iris.c @@ -7,7 +7,7 @@ #include #include -#include "venus.h" +#include "iris.h" #define WRAPPER_IRIS_VERSION 0x0 #define WRAPPER_CPA_START_ADDR 0x1020 @@ -19,7 +19,7 @@ #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 = 0; @@ -40,7 +40,7 @@ static TEE_Result venus_reset_cpu(struct qcom_pas_data *data) return TEE_SUCCESS; } -static TEE_Result venus_fw_shutdown(struct qcom_pas_data *data) +static TEE_Result iris_fw_shutdown(struct qcom_pas_data *data) { vaddr_t base = 0; uint32_t reg = 0; @@ -58,21 +58,21 @@ static TEE_Result venus_fw_shutdown(struct qcom_pas_data *data) return TEE_SUCCESS; } -static 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); } -static 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 venus_ops = { - .fw_start = venus_fw_start, - .fw_shutdown = venus_fw_shutdown, - .fw_set_state = venus_fw_set_state, +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/venus.h b/core/pta/qcom/pas/platform/kodiak/iris.h similarity index 70% rename from core/pta/qcom/pas/platform/kodiak/venus.h rename to core/pta/qcom/pas/platform/kodiak/iris.h index 8886f394c..b283c3cf0 100644 --- a/core/pta/qcom/pas/platform/kodiak/venus.h +++ b/core/pta/qcom/pas/platform/kodiak/iris.h @@ -3,14 +3,14 @@ * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. */ -#ifndef _VENUS_H_ -#define _VENUS_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 -extern const struct qcom_pas_ops venus_ops; +extern const struct qcom_pas_ops iris_ops; -#endif /* _VENUS_H_ */ +#endif /* _IRIS_H_ */ diff --git a/core/pta/qcom/pas/platform/kodiak/sub.mk b/core/pta/qcom/pas/platform/kodiak/sub.mk index 540fc1c24..4be1630e1 100644 --- a/core/pta/qcom/pas/platform/kodiak/sub.mk +++ b/core/pta/qcom/pas/platform/kodiak/sub.mk @@ -1,3 +1,3 @@ -srcs-y += subsys.c cdsp.c dsp_fw.c lpass.c wpss.c venus.c +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 index b14e80b92..10cd7fbe4 100644 --- a/core/pta/qcom/pas/platform/kodiak/subsys.c +++ b/core/pta/qcom/pas/platform/kodiak/subsys.c @@ -9,9 +9,9 @@ #include #include "cdsp.h" +#include "iris.h" #include "lpass.h" #include "pas_subsys.h" -#include "venus.h" #include "wpss.h" static struct qcom_pas_subsys subsystems[] = { @@ -47,11 +47,11 @@ static struct qcom_pas_subsys subsystems[] = { }, { .data = { - .pas_id = PAS_ID_VENUS, + .pas_id = PAS_ID_IRIS, .base.pa = IRIS_BASE, .size = IRIS_SIZE, }, - .ops = &venus_ops, + .ops = &iris_ops, .reset_seq = QCOM_PAS_RESET_NONE, }, }; From a168b571609fe141176fd73b1b1cadf9535392e7 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Sat, 18 Jul 2026 08:47:43 +0200 Subject: [PATCH 11/30] pta: qcom: pas: centralize PAS_ID definitions Move the PAS_ID_* definitions out of the per-platform target_config.h files into the PTA's pas_data.h so they live in one place. These IDs are really part of the PTA contract with the client rather than a platform definition; centralizing them in the PTA is a first step towards that abstraction. Signed-off-by: Jorge Ramirez-Ortiz Reviewed-by: Sumit Garg --- core/arch/arm/plat-qcom/hoya/kodiak/target_config.h | 5 ----- core/arch/arm/plat-qcom/hoya/lemans/target_config.h | 7 ------- core/pta/qcom/pas/platform/pas_data.h | 8 ++++++++ 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h b/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h index 21031a272..77354f49e 100644 --- a/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h +++ b/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h @@ -39,9 +39,4 @@ #define IRIS_BASE UL(0x0aa00000) #define IRIS_SIZE ULL(0x00200000) -#define PAS_ID_QDSP6 1 -#define PAS_ID_WPSS 6 -#define PAS_ID_IRIS 9 -#define PAS_ID_TURING 18 - #endif /* TARGET_CONFIG_H */ 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 6510709fb..06eac0b76 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target_config.h +++ b/core/arch/arm/plat-qcom/hoya/lemans/target_config.h @@ -62,13 +62,6 @@ #define IRIS_BASE UL(0x0aa00000) #define IRIS_SIZE ULL(0x00200000) -#define PAS_ID_TURING 18 -#define PAS_ID_TURING1 30 -#define PAS_ID_QDSP6 1 -#define PAS_ID_IRIS 9 -#define PAS_ID_GPDSP0 39 -#define PAS_ID_GPDSP1 40 - /* CDSP0 content-protection shared channel (secure DDR); no CDSP1 equivalent. */ #define CDSP_SECCHANNEL_BASE UL(0xdb1dc000) #define CDSP_SECCHANNEL_SIZE UL(0x2000) diff --git a/core/pta/qcom/pas/platform/pas_data.h b/core/pta/qcom/pas/platform/pas_data.h index cbd229211..0b8978b6b 100644 --- a/core/pta/qcom/pas/platform/pas_data.h +++ b/core/pta/qcom/pas/platform/pas_data.h @@ -11,6 +11,14 @@ #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_GPDSP0 39 +#define PAS_ID_GPDSP1 40 + struct qcom_pas_data { uint32_t pas_id; struct io_pa_va base; From f04d396ae3f45640ae6568ee3f130b14ba81f00b Mon Sep 17 00:00:00 2001 From: Amirreza Zarrabi Date: Thu, 16 Jul 2026 21:30:16 -0700 Subject: [PATCH 12/30] core: io: add base+offset and register field accessor helpers Add io_read32_off()/io_write32_off() for reading/writing a 32-bit MMIO register at a base address plus byte offset, and io_read32_off_field()/io_write32_off_field() for getting/setting a masked, shifted field within such a register. Signed-off-by: Amirreza Zarrabi --- core/include/io.h | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) 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)); From 28ae92fdda6fbf6237e1736639a29df71553e042 Mon Sep 17 00:00:00 2001 From: Amirreza Zarrabi Date: Mon, 22 Jun 2026 20:28:33 -0700 Subject: [PATCH 13/30] drivers: crypto: add Qualcomm Hardware Key Manager (HWKM) driver Add a driver for the Qualcomm Hardware Key Manager (HWKM), a hardware IP block present on Qualcomm SoCs that manages cryptographic key slots in a tamper-resistant key table. Keys stored in HWKM slots are never exposed in plaintext to software above the security level they were provisioned at; the hardware enforces per-slot access-control and usage policies. The driver exposes the following functionality to OP-TEE: - Hardware Unique Key (HUK): implements tee_otp_get_hw_unique_key() by performing a three-level key derivation using the SYSTEM_KDF command. A stable SKDK L3 mixing key is first derived from TZ_SKDK_L2 into the dedicated mixing key slot; the UKDK L3 KDK and the final L4 HUK are then derived with the mixing key folded in via BSVE.MKS_EN. Two Kconfig options control this behaviour: CFG_HWKM_HUK_MIX_SKDK (default y) enables the SKDK mixing step, and CFG_HWKM_HUK_FUSE_REGION_DIGEST (default 0x0) selects fuse regions whose SHA256 digest is bound into the KDF input. - Full command set: NIST_KEYGEN, SYSTEM_KDF, KEY_WRAP_EXPORT, KEY_UNWRAP_IMPORT, KEY_SLOT_CLEAR, KEY_SLOT_RDWR, and SET_TPKEY are all implemented and exposed through a transaction queue API. Signed-off-by: Amirreza Zarrabi Acked-by: Sumit Garg Reviewed-by: Jorge Ramirez-Ortiz --- core/drivers/crypto/qcom/hwkm.c | 267 ++++++ core/drivers/crypto/qcom/hwkm_huk.c | 317 +++++++ core/drivers/crypto/qcom/hwkm_transaction.c | 858 ++++++++++++++++++ core/drivers/crypto/qcom/include/hwkm.h | 377 ++++++++ core/drivers/crypto/qcom/include/hwkm_errno.h | 143 +++ core/drivers/crypto/qcom/include/hwkm_regs.h | 161 ++++ core/drivers/crypto/qcom/sub.mk | 16 + core/drivers/crypto/sub.mk | 2 + 8 files changed, 2141 insertions(+) create mode 100644 core/drivers/crypto/qcom/hwkm.c create mode 100644 core/drivers/crypto/qcom/hwkm_huk.c create mode 100644 core/drivers/crypto/qcom/hwkm_transaction.c create mode 100644 core/drivers/crypto/qcom/include/hwkm.h create mode 100644 core/drivers/crypto/qcom/include/hwkm_errno.h create mode 100644 core/drivers/crypto/qcom/include/hwkm_regs.h create mode 100644 core/drivers/crypto/qcom/sub.mk 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 From ccbc91061f345d092620e1a74ced2501ac9259dd Mon Sep 17 00:00:00 2001 From: Amirreza Zarrabi Date: Thu, 25 Jun 2026 23:13:33 -0700 Subject: [PATCH 14/30] MAINTAINERS: add entry for Qualcomm HWKM driver Add a MAINTAINERS entry for the new Qualcomm Hardware Key Manager (HWKM) driver. Signed-off-by: Amirreza Zarrabi Acked-by: Sumit Garg Reviewed-by: Jorge Ramirez-Ortiz --- MAINTAINERS | 5 +++++ 1 file changed, 5 insertions(+) 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] From 858b8fe0cdbab420e5a3ce075b366f1bcb7e8531 Mon Sep 17 00:00:00 2001 From: Amirreza Zarrabi Date: Thu, 25 Jun 2026 23:14:09 -0700 Subject: [PATCH 15/30] plat-qcom: lemans: enable HWKM driver Add the HWKM master register region base and size to the shared Hoya architecture config so all Hoya-family targets can reference them, and enable CFG_QCOM_HWKM by default for the lemans target. Signed-off-by: Amirreza Zarrabi Acked-by: Sumit Garg Reviewed-by: Jorge Ramirez-Ortiz --- core/arch/arm/plat-qcom/hoya/arch_config.h | 3 +++ core/arch/arm/plat-qcom/hoya/lemans/target.mk | 1 + 2 files changed, 4 insertions(+) diff --git a/core/arch/arm/plat-qcom/hoya/arch_config.h b/core/arch/arm/plat-qcom/hoya/arch_config.h index 8848825cb..d097dec74 100644 --- a/core/arch/arm/plat-qcom/hoya/arch_config.h +++ b/core/arch/arm/plat-qcom/hoya/arch_config.h @@ -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 930e33ef0..d82eecb50 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target.mk +++ b/core/arch/arm/plat-qcom/hoya/lemans/target.mk @@ -22,3 +22,4 @@ 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 endif +CFG_QCOM_HWKM ?= y From d254508e387dcb1353144c485639bc5b71caf5e0 Mon Sep 17 00:00:00 2001 From: Harikrishna Date: Fri, 19 Jun 2026 16:30:04 +0530 Subject: [PATCH 16/30] drivers: qcom: rng: Consolidate PRNG into CSRNG driver Standardize RNG driver naming and configuration across QCOM platforms.Rename prng.c to qcom-csrng.c and consolidate driver inclusion in the parent qcom/sub.mk with the unified CFG_QCOM_CSRNG flag.This change simplifies the driver structure and aligns with platform-agnostic naming conventions. The QCOM RNG IPs are confirmed by the hardware team to be cryptographically secure (CSRNG), so the driver is named qcom-csrng.c and enabled via CFG_QCOM_CSRNG to explicitly reflect that the source is safe for key generation. Update hoya chipset configurations to use the new CFG_QCOM_CSRNG flag and configure QCOM_RNG_REG_BASE for PRNG variant support, ensuring backward compatibility while establishing consistent naming standards across the codebase. Force enable CFG_QCOM_CSRNG to use the hardware QRNG driver, and disable CFG_WITH_SOFTWARE_PRNG whenever CFG_QCOM_CSRNG is enabled to prevent fallback to the software PRNG. Signed-off-by: Harikrishna Reviewed-by: Selvam Sathappan Periakaruppan Reviewed-by: Sumit Garg --- core/arch/arm/plat-qcom/hoya/arch_config.h | 2 +- core/arch/arm/plat-qcom/hoya/qcom-arch.mk | 3 +- .../qcom/{prng/prng.c => rng/qcom-csrng.c} | 34 +++++++++---------- core/drivers/qcom/sub.mk | 2 +- 4 files changed, 21 insertions(+), 20 deletions(-) rename core/drivers/qcom/{prng/prng.c => rng/qcom-csrng.c} (58%) diff --git a/core/arch/arm/plat-qcom/hoya/arch_config.h b/core/arch/arm/plat-qcom/hoya/arch_config.h index d097dec74..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) 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/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..cf80024ee 100644 --- a/core/drivers/qcom/sub.mk +++ b/core/drivers/qcom/sub.mk @@ -5,7 +5,7 @@ # 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 subdirs-$(CFG_QCOM_CMD_DB) += cmd_db subdirs-$(CFG_QCOM_RPMH_CLIENT) += rpmh From 7a2b392ee3b68e623201b4d17d27cc14ef400681 Mon Sep 17 00:00:00 2001 From: Harikrishna Date: Fri, 19 Jun 2026 16:33:00 +0530 Subject: [PATCH 17/30] plat-qcom: Enable RNG driver for Bobcat platforms Configure QCOM_RNG_REG_BASE and enable the consolidated RNG driver for the Bobcat family (ipq52xx). Force enable CFG_QCOM_CSRNG to use the hardware QRNG driver, and disable CFG_WITH_SOFTWARE_PRNG whenever CFG_QCOM_CSRNG is enabled to prevent fallback to the software PRNG. When HWRNG_PTA is enabled: - Configure HWRNG quality to 1024 bits entropy - Set HWRNG rate to 0 (unlimited) Signed-off-by: Harikrishna Reviewed-by: Selvam Sathappan Periakaruppan Reviewed-by: Sumit Garg --- core/arch/arm/plat-qcom/bobcat/ipq52xx/target.mk | 7 +++++++ core/arch/arm/plat-qcom/bobcat/ipq52xx/target_config.h | 2 ++ 2 files changed, 9 insertions(+) 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..3692accd1 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,6 @@ #define IMEM_BASE UL(0x8600000) #define IMEM_SIZE UL(0x18000) +#define QCOM_RNG_REG_BASE UL(0x4C5000) + #endif /* TARGET_CONFIG_H */ From 50dd4dbf65f82fa853435267902964abbaee8537 Mon Sep 17 00:00:00 2001 From: Harikrishna Date: Tue, 23 Jun 2026 15:58:05 +0530 Subject: [PATCH 18/30] drivers: qcom: wdog: Add secure watchdog driver for Bobcat family MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QCOM platforms manage secure watchdog via driver_init() without framework registration. This is sufficient as QCOM currently does not require HLOS control over the secure watchdog. The implementation maintains separation between secure and non-secure world watchdog management. The implementation maps the watchdog base (QCOM_WDT_TMR_BASE) into secure I/O memory, configures bark and bite timeouts using a 32 KHz clock, registers a bark interrupt handler, and services the watchdog by writing to the reset register. Signed-off-by: Harikrishna Reviewed-by: Selvam Sathappan Periakaruppan Reviewed-by: Sumit Garg --- core/drivers/qcom/sub.mk | 1 + core/drivers/qcom/wdt/qcom-wdt.c | 115 +++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 core/drivers/qcom/wdt/qcom-wdt.c diff --git a/core/drivers/qcom/sub.mk b/core/drivers/qcom/sub.mk index cf80024ee..ea4c6c2bd 100644 --- a/core/drivers/qcom/sub.mk +++ b/core/drivers/qcom/sub.mk @@ -6,6 +6,7 @@ srcs-$(CFG_QCOM_RAMBLUR_PIMEM_V3) += ramblur/ramblur_pimem_v3.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 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); + From a4d253dff47a515445b2f97b136380770b73d1a1 Mon Sep 17 00:00:00 2001 From: Harikrishna Date: Fri, 19 Jun 2026 16:48:54 +0530 Subject: [PATCH 19/30] plat-qcom: Enable sec wdog config for Bobcat family CFG_QCOM_SEC_WDOG is enabled in bobcat/arch.mk for all Bobcat targets, with platform-specific watchdog base addresses, interrupt IDs, and reset offsets defined in the respective target_config.h files (e.g., ipq96xx/ipq54xx and ipq52xx variants). Signed-off-by: Harikrishna Reviewed-by: Selvam Sathappan Periakaruppan Reviewed-by: Sumit Garg --- core/arch/arm/plat-qcom/bobcat/ipq52xx/target_config.h | 3 +++ core/arch/arm/plat-qcom/bobcat/ipq96xx/target_config.h | 3 +++ core/arch/arm/plat-qcom/bobcat/qcom-arch.mk | 4 ++++ 3 files changed, 10 insertions(+) create mode 100644 core/arch/arm/plat-qcom/bobcat/qcom-arch.mk 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 3692accd1..7269d7d85 100644 --- a/core/arch/arm/plat-qcom/bobcat/ipq52xx/target_config.h +++ b/core/arch/arm/plat-qcom/bobcat/ipq52xx/target_config.h @@ -22,4 +22,7 @@ #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..e3b71bcb2 --- /dev/null +++ b/core/arch/arm/plat-qcom/bobcat/qcom-arch.mk @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: BSD-2-Clause +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + +CFG_QCOM_SEC_WDOG ?= y From 0b477498a08c9cfe5c9797ce9a159c33e2829e1c Mon Sep 17 00:00:00 2001 From: Ignatius Michael Jihan Date: Mon, 13 Jul 2026 15:52:37 +0530 Subject: [PATCH 20/30] pta: qcom: pas: lemans: add Camera ICP bring-up The Camera ICP (Imaging Control Processor, PAS ID 33) firmware must be loaded and authenticated by OP-TEE before CAMX can use the camera subsystem on SA8775P / Lemans EVK. Without this support the kernel camera driver fails to bring up the ICP and camera preview is unavailable. Add PAS reset ops for the ICP, register the subsystem in the PAS table. Tested: camera preview use case exercised on Lemans EVK; ICP firmware loads, authenticates and executes correctly with camera preview confirmed functional end-to-end. Signed-off-by: Ignatius Michael Jihan --- .../arm/plat-qcom/hoya/lemans/target_config.h | 2 + core/pta/qcom/pas/platform/lemans/camera.c | 105 ++++++++++++++++++ core/pta/qcom/pas/platform/lemans/camera.h | 13 +++ core/pta/qcom/pas/platform/lemans/sub.mk | 2 +- core/pta/qcom/pas/platform/lemans/subsys.c | 10 ++ core/pta/qcom/pas/platform/pas_data.h | 1 + 6 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 core/pta/qcom/pas/platform/lemans/camera.c create mode 100644 core/pta/qcom/pas/platform/lemans/camera.h 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 06eac0b76..757c67c61 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target_config.h +++ b/core/arch/arm/plat-qcom/hoya/lemans/target_config.h @@ -92,4 +92,6 @@ #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/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/sub.mk b/core/pta/qcom/pas/platform/lemans/sub.mk index f770bf1be..c40fd65fc 100644 --- a/core/pta/qcom/pas/platform/lemans/sub.mk +++ b/core/pta/qcom/pas/platform/lemans/sub.mk @@ -1,3 +1,3 @@ -srcs-y += subsys.c cdsp0.c cdsp1.c lpass.c iris.c gpdsp0.c gpdsp1.c +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 index 7be6784a7..039e6b98c 100644 --- a/core/pta/qcom/pas/platform/lemans/subsys.c +++ b/core/pta/qcom/pas/platform/lemans/subsys.c @@ -8,6 +8,7 @@ #include #include +#include "camera.h" #include "cdsp0.h" #include "cdsp1.h" #include "gpdsp0.h" @@ -76,6 +77,15 @@ static struct qcom_pas_subsys subsystems[] = { .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) diff --git a/core/pta/qcom/pas/platform/pas_data.h b/core/pta/qcom/pas/platform/pas_data.h index 0b8978b6b..1cf99887b 100644 --- a/core/pta/qcom/pas/platform/pas_data.h +++ b/core/pta/qcom/pas/platform/pas_data.h @@ -16,6 +16,7 @@ #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 From 1f0f8bba61d5f79bd969d7b1bb50f8b42e0ba76f Mon Sep 17 00:00:00 2001 From: Harikrishna Date: Tue, 4 Aug 2026 12:13:56 +0530 Subject: [PATCH 21/30] drivers: qcom: add OP-TEE-owned XPU protection for TZDRAM and DIAG log TZDRAM and the DIAG log are currently protected by TF-A's static XPU policy, duplicating values OP-TEE already owns. Move ownership to OP-TEE for a single source of truth. Add an XPU4 driver: xpu_protect_region() takes a region and access policy, and resolves the XPU instance and a free resource group itself. It has no external callers, so it stays static. Both regions are protected from one service_init() call. DIAG log protection is skipped when CFG_QCOM_DIAG_LOG is disabled, so an unused buffer doesn't consume a resource group. Compiles only when CFG_QCOM_XPUV4 is enabled. Testing: XPU resource-group registers matched the expected TZDRAM/DIAG log ranges and permissions. Non-secure accesses raised XPU violations. Tested-on: Hermosa (IPQ52xx) Tested-on: Juhu (IPQ96xx) Signed-off-by: Harikrishna --- core/drivers/qcom/sub.mk | 1 + core/drivers/qcom/xpu/ipq52xx/xpu_target.h | 30 +++ core/drivers/qcom/xpu/ipq96xx/xpu_target.h | 34 +++ core/drivers/qcom/xpu/sub.mk | 8 + core/drivers/qcom/xpu/xpuv4.c | 255 +++++++++++++++++++++ core/include/drivers/qcom/xpuv4.h | 34 +++ 6 files changed, 362 insertions(+) create mode 100644 core/drivers/qcom/xpu/ipq52xx/xpu_target.h create mode 100644 core/drivers/qcom/xpu/ipq96xx/xpu_target.h create mode 100644 core/drivers/qcom/xpu/sub.mk create mode 100644 core/drivers/qcom/xpu/xpuv4.c create mode 100644 core/include/drivers/qcom/xpuv4.h diff --git a/core/drivers/qcom/sub.mk b/core/drivers/qcom/sub.mk index ea4c6c2bd..56e7d0625 100644 --- a/core/drivers/qcom/sub.mk +++ b/core/drivers/qcom/sub.mk @@ -11,3 +11,4 @@ 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/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/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 */ From 3b724f46f3ff591572df8532e777c5dd1628f14b Mon Sep 17 00:00:00 2001 From: Harikrishna Date: Tue, 4 Aug 2026 12:14:34 +0530 Subject: [PATCH 22/30] plat-qcom: Enable XPU protection for Bobcat platforms Enable OP-TEE-owned XPU protection for TZDRAM and the DIAG log on the Bobcat family (IPQ52xx, IPQ96xx), replacing TF-A's static policy for these regions. Tested-on: Hermosa (IPQ52xx) Tested-on: Juhu (IPQ96xx) Signed-off-by: Harikrishna --- core/arch/arm/plat-qcom/bobcat/qcom-arch.mk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/arch/arm/plat-qcom/bobcat/qcom-arch.mk b/core/arch/arm/plat-qcom/bobcat/qcom-arch.mk index e3b71bcb2..a0544ac11 100644 --- a/core/arch/arm/plat-qcom/bobcat/qcom-arch.mk +++ b/core/arch/arm/plat-qcom/bobcat/qcom-arch.mk @@ -2,3 +2,6 @@ # 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 From d3a0500c42195d38b80f25eb0b5916b82694dfab Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Tue, 11 Aug 2026 20:21:08 +0000 Subject: [PATCH 23/30] pta: qcom: pas: preparatory fixes and cleanup A stale carveout could be reused across peripheral loads: shutdown did not clear the cached MEM_SETUP coordinates, so a subsystem that was stopped and reloaded without a fresh MEM_SETUP call would pass the resulting VERIFY_IMAGE cross-check against physical memory it no longer owns. qcom_pas_capabilities() also passed the wrong parameter to pas_platform_capabilities(), reading the output flags field instead of the caller-supplied pas_id. Fix both ahead of the authentication work that builds on this code, along with unrelated include and logging cleanup, so the feature commits that follow stay focused on the feature. pas_lookup() is exported for the same reason: later commits need it directly. Signed-off-by: Selvam Sathappan Periakaruppan Assisted-by: Claude:sonnet-5 Reviewed-by: Jorge Ramirez-Ortiz --- core/pta/qcom/pas/pas_core.c | 14 ++++++++++---- core/pta/qcom/pas/platform/pas_subsys.h | 2 ++ core/pta/qcom/pas/pta_qcom_pas.c | 7 +++---- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/core/pta/qcom/pas/pas_core.c b/core/pta/qcom/pas/pas_core.c index 403fb9eba..43073d824 100644 --- a/core/pta/qcom/pas/pas_core.c +++ b/core/pta/qcom/pas/pas_core.c @@ -4,7 +4,6 @@ */ #include -#include #include #include #include @@ -12,7 +11,7 @@ #include "pas_subsys.h" -static struct qcom_pas_subsys *pas_lookup(uint32_t pas_id) +struct qcom_pas_subsys *pas_lookup(uint32_t pas_id) { struct qcom_pas_subsys *subsys = NULL; size_t count = 0; @@ -98,8 +97,8 @@ 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) { struct qcom_pas_subsys *subsys = pas_lookup(pas_id); - struct qcom_pas_data *data = NULL; TEE_Result res = TEE_ERROR_GENERIC; + struct qcom_pas_data *data = NULL; if (!subsys) return TEE_ERROR_NOT_SUPPORTED; @@ -141,9 +140,16 @@ TEE_Result pas_platform_auth_and_reset(uint32_t pas_id) 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; - return subsys->ops->fw_shutdown(&subsys->data); + 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/pas_subsys.h b/core/pta/qcom/pas/platform/pas_subsys.h index dd526f84d..128572c32 100644 --- a/core/pta/qcom/pas/platform/pas_subsys.h +++ b/core/pta/qcom/pas/platform/pas_subsys.h @@ -77,4 +77,6 @@ struct qcom_pas_subsys { */ 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/pta_qcom_pas.c b/core/pta/qcom/pas/pta_qcom_pas.c index 13d5a10d6..b3d1943b8 100644 --- a/core/pta/qcom/pas/pta_qcom_pas.c +++ b/core/pta/qcom/pas/pta_qcom_pas.c @@ -3,13 +3,12 @@ * Copyright (c) 2026, Qualcomm Technologies, Inc. and/or its subsidiaries. */ -#include #include -#include -#include +#include #include #include #include +#include #define PTA_NAME "pta.qcom.pas" @@ -45,7 +44,7 @@ static TEE_Result qcom_pas_capabilities(uint32_t pt, /* Capabilities flags reserved for future use */ params[1].value.a = 0; - return pas_platform_capabilities(params[1].value.a); + return pas_platform_capabilities(params[0].value.a); } static TEE_Result qcom_pas_init_image(uint32_t pt, From c297c66d1e22f76d74396dbee098b4f3dc433310 Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Tue, 11 Aug 2026 20:22:08 +0000 Subject: [PATCH 24/30] ta: qcom_pas: add Multiboot Binary (MBN) hash-segment parser PIL firmware images carry a Qualcomm MBN hash segment holding the per-segment digest table, signature and certificate material the PAS TA needs to authenticate an image before releasing the peripheral from reset. Introduce a parser for this segment so the authentication phases that follow can consume a single, structured view of it rather than each phase re-parsing the raw metadata buffer independently and risking disagreement about region boundaries. Signed-off-by: Selvam Sathappan Periakaruppan Assisted-by: Claude:sonnet-5 Reviewed-by: Jorge Ramirez-Ortiz --- ta/qcom_pas/include/pas_mbn_parser.h | 60 +++++ ta/qcom_pas/include/pas_mbn_parser_priv.h | 35 +++ ta/qcom_pas/src/pas_mbn_parser.c | 266 ++++++++++++++++++++++ ta/qcom_pas/src/sub.mk | 2 + ta/qcom_pas/user_ta.mk | 7 +- 5 files changed, 369 insertions(+), 1 deletion(-) create mode 100644 ta/qcom_pas/include/pas_mbn_parser.h create mode 100644 ta/qcom_pas/include/pas_mbn_parser_priv.h create mode 100644 ta/qcom_pas/src/pas_mbn_parser.c 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/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/sub.mk b/ta/qcom_pas/src/sub.mk index f572b0463..ff412bce7 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_mbn_parser.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 From 1c89e2f176510c6d4a583375318b797fca58c196 Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Tue, 11 Aug 2026 20:23:08 +0000 Subject: [PATCH 25/30] pta: qcom: pas: add firmware segment hash verification A compromised REE could substitute firmware in the peripheral's carveout between the point image metadata is accepted and the point the peripheral is released from reset. Add a verification step that re-hashes each loaded segment against the image's own digest table and fails closed on any mismatch, so the peripheral only runs code whose bytes match what was authenticated. Provide it as a standalone capability so hash verification can be enabled and reviewed independently of signature authentication. Signed-off-by: Selvam Sathappan Periakaruppan Assisted-by: Claude:sonnet-5 Reviewed-by: Jorge Ramirez-Ortiz --- core/pta/qcom/pas/pas_auth_core.c | 403 ++++++++++++++++++++++ core/pta/qcom/pas/pas_auth_core.h | 29 ++ core/pta/qcom/pas/platform/platform_pas.h | 34 ++ core/pta/qcom/pas/pta_qcom_pas.c | 43 ++- core/pta/qcom/pas/sub.mk | 1 + lib/libutee/include/pta_qcom_pas.h | 21 +- 6 files changed, 526 insertions(+), 5 deletions(-) create mode 100644 core/pta/qcom/pas/pas_auth_core.c create mode 100644 core/pta/qcom/pas/pas_auth_core.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/platform/platform_pas.h b/core/pta/qcom/pas/platform/platform_pas.h index 7bb596be7..457498ef5 100644 --- a/core/pta/qcom/pas/platform/platform_pas.h +++ b/core/pta/qcom/pas/platform/platform_pas.h @@ -7,6 +7,8 @@ #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); @@ -20,4 +22,36 @@ 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/pta_qcom_pas.c b/core/pta/qcom/pas/pta_qcom_pas.c index b3d1943b8..595e978d4 100644 --- a/core/pta/qcom/pas/pta_qcom_pas.c +++ b/core/pta/qcom/pas/pta_qcom_pas.c @@ -95,8 +95,7 @@ static TEE_Result qcom_pas_get_resource_table(uint32_t pt, } 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, @@ -127,8 +126,7 @@ static TEE_Result qcom_pas_auth_and_reset(uint32_t pt, } 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, @@ -142,6 +140,41 @@ qcom_pas_shutdown(uint32_t pt, return pas_platform_shutdown(params[0].value.a); } +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, uint32_t cmd_id, uint32_t param_types, @@ -164,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; } diff --git a/core/pta/qcom/pas/sub.mk b/core/pta/qcom/pas/sub.mk index 06bdd894c..b52cc740b 100644 --- a/core/pta/qcom/pas/sub.mk +++ b/core/pta/qcom/pas/sub.mk @@ -1,5 +1,6 @@ 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/ subdirs-y += platform/$(PLATFORM_FLAVOR) 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 */ From d74ee5501ad944472c7c0f984286691912e125c6 Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Tue, 11 Aug 2026 20:24:08 +0000 Subject: [PATCH 26/30] ta: qcom_pas: verify loaded firmware image integrity Segment-hash verification lives in the PTA but nothing invokes it today, so a peripheral can be released from reset without any TEE-side check that the firmware in its carveout is what was authenticated. Bridge that gap: the TA captures a TEE-private copy of the REE-supplied metadata at image-init time and drives the verifier with it at reset time, so the REE cannot mutate the metadata the check runs against between the two events. Per-peripheral state is keyed by ID so concurrent bring-ups on the same session do not clobber each other. Signature authentication is left as a placeholder so segment-hash verification can land and be reviewed without waiting on it. Release the captured metadata when a peripheral is shut down, so it does not remain allocated for the life of the session after the peripheral it authenticated is torn down. The per-ID slot itself is retained because slots are provisioned one per peripheral; reclaiming it would break the capacity model when the same peripheral is reloaded later. Signed-off-by: Selvam Sathappan Periakaruppan Assisted-by: Claude:sonnet-5 Reviewed-by: Jorge Ramirez-Ortiz --- core/pta/qcom/pas/sub.mk | 3 + ta/qcom_pas/include/pas_auth.h | 58 +++++++++ ta/qcom_pas/include/pas_meta.h | 21 ++++ ta/qcom_pas/include/qcom_pas_priv.h | 29 +++++ ta/qcom_pas/src/pas_auth.c | 178 ++++++++++++++++++++++++++++ ta/qcom_pas/src/pas_meta.c | 54 +++++++++ ta/qcom_pas/src/qcom_pas.c | 108 +++++++++++++---- ta/qcom_pas/src/sub.mk | 2 +- 8 files changed, 429 insertions(+), 24 deletions(-) create mode 100644 ta/qcom_pas/include/pas_auth.h create mode 100644 ta/qcom_pas/include/pas_meta.h create mode 100644 ta/qcom_pas/include/qcom_pas_priv.h create mode 100644 ta/qcom_pas/src/pas_auth.c create mode 100644 ta/qcom_pas/src/pas_meta.c diff --git a/core/pta/qcom/pas/sub.mk b/core/pta/qcom/pas/sub.mk index b52cc740b..4f2225a2a 100644 --- a/core/pta/qcom/pas/sub.mk +++ b/core/pta/qcom/pas/sub.mk @@ -4,3 +4,6 @@ srcs-$(CFG_QCOM_PAS_AUTH) += pas_auth_core.c incdirs-y += . incdirs-y += platform/ subdirs-y += platform/$(PLATFORM_FLAVOR) + +# PIL PAS authentication metadata slots, defaults to 1. +CFG_PAS_MD_SLOTS ?= 1 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_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_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 ff412bce7..c183ff77a 100644 --- a/ta/qcom_pas/src/sub.mk +++ b/ta/qcom_pas/src/sub.mk @@ -1,3 +1,3 @@ global-incdirs-y += ../include srcs-y += qcom_pas.c -srcs-$(CFG_QCOM_PAS_AUTH) += pas_mbn_parser.c +srcs-$(CFG_QCOM_PAS_AUTH) += pas_auth.c pas_mbn_parser.c pas_meta.c From d06baaeaa92bbc75a20b9b21882049204ef9f8b3 Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Tue, 11 Aug 2026 20:25:08 +0000 Subject: [PATCH 27/30] plat-qcom: hoya: lemans: enable PAS firmware authentication Enable CFG_QCOM_PAS_AUTH on Lemans so PIL images are authenticated before the peripheral leaves reset. Segment-hash verification takes effect immediately; signature authentication is a runtime step that only engages once secure-boot fuses are blown, and is filled in later in this series. Signed-off-by: Selvam Sathappan Periakaruppan Assisted-by: Claude:sonnet-5 Reviewed-by: Jorge Ramirez-Ortiz --- core/arch/arm/plat-qcom/hoya/lemans/target.mk | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/core/arch/arm/plat-qcom/hoya/lemans/target.mk b/core/arch/arm/plat-qcom/hoya/lemans/target.mk index d82eecb50..95c04859f 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target.mk +++ b/core/arch/arm/plat-qcom/hoya/lemans/target.mk @@ -16,10 +16,11 @@ endif CFG_QCOM_PAS_PTA ?= y ifeq ($(CFG_QCOM_PAS_PTA),y) -# PAS subsystems map their controller windows at runtime from the reserved VA -# pool (never released). The six DSP windows total ~146.5 MB; the 60 MB default -# fits only one, so reserve 256 MB with headroom. 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 From f2509693ca9f023a7ac069be7c557f11a94019fa Mon Sep 17 00:00:00 2001 From: Pawan Rai Date: Wed, 12 Aug 2026 16:05:05 +0530 Subject: [PATCH 28/30] plat-qcom: nord: add PIL bring-up for HPASS, SOCCP and Turing DSPs Add the clock and PAS bring-up support required to load the additional DSP images on the Nord platform: - clock-qcom: add a Lucid-OLE PLL enable helper. Lucid-OLE reuses the Lucid-EVO register layout but packs PLL_L_VAL as an 8-bit L value plus separate 8-bit process-cal and ring-osc-cal fields, and requires the PLL_TEST_CTL* trim registers to be programmed, so it gets its own config struct and enable path. - clk_qcom: add QCOM_CLKS_{SOCCP,HPASS0,HPASS1,HPASS2,TURING2,TURING3} clock groups and route them through the PAS enable path. - pas_data: add PAS IDs for TURING2/3, HPASS0-2 and SOCCP. - nord: add the per-platform clock-qcom-pas.c and clock_group_qcom.h describing the PLL and clock-group configuration for these subsystems. - arch/target config: add the register base/size definitions needed by the new bring-up code. Change-Id: I30e107d24ddf9f289a4178116430359ab128716b Signed-off-by: Taniya Das --- core/arch/arm/plat-qcom/wildcat/arch_config.h | 15 + .../plat-qcom/wildcat/nord/target_config.h | 62 +- core/drivers/clk/qcom/clock-qcom.c | 82 ++ .../clk/qcom/platform/nord/clock-qcom-pas.c | 806 ++++++++++++++++++ .../clk/qcom/platform/nord/clock_group_qcom.h | 287 +++++++ core/include/drivers/clk_qcom.h | 30 + core/pta/qcom/pas/platform/pas_data.h | 6 + 7 files changed, 1287 insertions(+), 1 deletion(-) create mode 100644 core/drivers/clk/qcom/platform/nord/clock-qcom-pas.c create mode 100644 core/drivers/clk/qcom/platform/nord/clock_group_qcom.h 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_config.h b/core/arch/arm/plat-qcom/wildcat/nord/target_config.h index 5e0c0fad2..c2f1d0687 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,71 @@ #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) + #endif /* TARGET_CONFIG_H */ diff --git a/core/drivers/clk/qcom/clock-qcom.c b/core/drivers/clk/qcom/clock-qcom.c index a84964842..fac4ea1ee 100644 --- a/core/drivers/clk/qcom/clock-qcom.c +++ b/core/drivers/clk/qcom/clock-qcom.c @@ -25,6 +25,10 @@ register_phys_mem(MEM_AREA_IO_NSEC, GCC_BASE, GCC_SIZE); #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) @@ -39,6 +43,17 @@ register_phys_mem(MEM_AREA_IO_NSEC, GCC_BASE, GCC_SIZE); #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 @@ -139,6 +154,67 @@ TEE_Result qcom_lucidevo_pll_enable(vaddr_t pll_base, 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) { @@ -159,10 +235,16 @@ 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/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/include/drivers/clk_qcom.h b/core/include/drivers/clk_qcom.h index b4c5d3579..dea2bd030 100644 --- a/core/include/drivers/clk_qcom.h +++ b/core/include/drivers/clk_qcom.h @@ -30,6 +30,12 @@ enum qcom_clk_group { 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, }; @@ -52,6 +58,23 @@ struct qcom_lucidevo_pll_config { 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, @@ -62,6 +85,13 @@ TEE_Result qcom_clock_set_rate(vaddr_t cfg_rcgr, vaddr_t cmd_rcgr, */ 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); diff --git a/core/pta/qcom/pas/platform/pas_data.h b/core/pta/qcom/pas/platform/pas_data.h index 1cf99887b..2bf918d1e 100644 --- a/core/pta/qcom/pas/platform/pas_data.h +++ b/core/pta/qcom/pas/platform/pas_data.h @@ -19,6 +19,12 @@ #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; From f04d52db552cebdaa81fc19fa93302b0f18061a3 Mon Sep 17 00:00:00 2001 From: Pawan Rai Date: Tue, 18 Aug 2026 17:05:12 +0530 Subject: [PATCH 29/30] plat-qcom: wildcat/nord: enable PAS PTA and qcom_pas user TA Enable the full PAS (Peripheral Authentication Service) framework for the Nord platform to support PIL firmware authentication: - CFG_DRIVERS_CLK / CFG_DRIVERS_QCOM_CLK: enable the clock driver framework needed for PAS peripheral bring-up sequences - CFG_QCOM_QFPROM_FUSEPROV / CMD_DB / RPMH_CLIENT / QFPROM: enable fuse provisioning support (gated on !CFG_INSECURE) for future secure-boot fuse checks during signature authentication - CFG_QCOM_PAS_PTA: enables the PAS Pseudo-TA (S-EL1) which owns carveout mapping, segment hash verification, and clock/reset sequencing - CFG_QCOM_PAS_AUTH: enables per-segment SHA-256/384 hash verification at AUTH_AND_RESET time; a tampered firmware segment triggers TEE_ERROR_SECURITY and the DSP is never released from reset - CFG_PAS_MD_SLOTS=8: allows up to 8 concurrent per-peripheral metadata slots within one shared TEE session - CFG_IN_TREE_EARLY_TAS: registers the qcom_pas user TA (UUID cff7d191-7ca0-4784-af13-48223b9a4fbe) so Linux remoteproc can open a TEE session for PIL bring-up Change-Id: I1181df76124b120dee1ec4705d0333400e07e76f Signed-off-by: Pawan Rai --- .../arch/arm/plat-qcom/wildcat/nord/target.mk | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) 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 From 83488a6affc2819bbca926843ee4ca237804eea1 Mon Sep 17 00:00:00 2001 From: Pawan Rai Date: Mon, 17 Aug 2026 17:06:51 +0530 Subject: [PATCH 30/30] plat-qcom: wildcat/nord: add IRIS video codec PAS support Add the IRIS video-codec PAS platform for the Nord (SA8797P/Oryon) chipset. This includes: - platform/nord/iris.c: firmware start/shutdown/set_state ops with SID table programming, memory region protection, and SCIBCMDARG3 trigger ported from TZ nord/video_arch.c - platform/nord/subsys.c: single IRIS subsystem (PAS_ID 9), QCOM_PAS_RESET_NONE as clocks are owned by HLOS - wildcat/nord/target_config.h: IRIS_BASE/SIZE register window - wildcat/nord/target.mk: enable CFG_QCOM_PAS_PTA and register qcom_pas as an early TA Change-Id: I86f2f3ae2b54b727fb9a03e20444169b12c2fe05 Signed-off-by: Priyanka Gujjula --- .../plat-qcom/wildcat/nord/target_config.h | 6 + core/pta/qcom/pas/platform/nord/iris.c | 276 ++++++++++++++++++ core/pta/qcom/pas/platform/nord/iris.h | 20 ++ core/pta/qcom/pas/platform/nord/sub.mk | 3 + core/pta/qcom/pas/platform/nord/subsys.c | 31 ++ 5 files changed, 336 insertions(+) create mode 100644 core/pta/qcom/pas/platform/nord/iris.c create mode 100644 core/pta/qcom/pas/platform/nord/iris.h create mode 100644 core/pta/qcom/pas/platform/nord/sub.mk create mode 100644 core/pta/qcom/pas/platform/nord/subsys.c 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 c2f1d0687..0b6aa1bce 100644 --- a/core/arch/arm/plat-qcom/wildcat/nord/target_config.h +++ b/core/arch/arm/plat-qcom/wildcat/nord/target_config.h @@ -73,4 +73,10 @@ #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/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; +}