From 23e75bcfd3c24bb13b0cd3bc30e20c9fc7af527b Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Mon, 25 May 2026 19:09:22 +0200 Subject: [PATCH 01/10] 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 --- .../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 91d01566b54f7b2094292421f0e87be6a59db438 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Thu, 21 May 2026 18:03:22 +0200 Subject: [PATCH 02/10] 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 --- .../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 4696716ed..1a83a1662 100644 --- a/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h +++ b/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h @@ -35,4 +35,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 c62018c1ed22476aa440aec99bf28689c4406c03 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Mon, 8 Jun 2026 19:34:25 +0200 Subject: [PATCH 03/10] 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 --- 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 4ae6699d5979b21b71f2e48fe28371404140a15b Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Wed, 10 Jun 2026 18:15:35 +0200 Subject: [PATCH 04/10] 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 --- 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 fba5dc6c0..ee2ac60e5 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target.mk +++ b/core/arch/arm/plat-qcom/hoya/lemans/target.mk @@ -7,3 +7,9 @@ _qcom_fuseprov_deps = $(if $(filter y,$(CFG_QCOM_QFPROM_FUSEPROV)),y,n) CFG_QCOM_CMD_DB ?= $(_qcom_fuseprov_deps) CFG_QCOM_RPMH_CLIENT ?= $(_qcom_fuseprov_deps) CFG_QCOM_QFPROM ?= $(_qcom_fuseprov_deps) + +CFG_QCOM_PAS_PTA ?= y + +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 6431bfa21..d09341b14 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target_config.h +++ b/core/arch/arm/plat-qcom/hoya/lemans/target_config.h @@ -26,4 +26,33 @@ #define GENI_UART_REG_BASE UL(0xa8c000) +#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 d72d5420ae209f5e3fd59326be6a83f0df1926f0 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Mon, 15 Jun 2026 07:56:52 +0200 Subject: [PATCH 05/10] 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 --- 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 ee2ac60e5..678ccb7f0 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target.mk +++ b/core/arch/arm/plat-qcom/hoya/lemans/target.mk @@ -11,5 +11,9 @@ CFG_QCOM_QFPROM ?= $(_qcom_fuseprov_deps) 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 03d403856e3678fdb34a4fc7aa000800430fa1a1 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Thu, 11 Jun 2026 10:17:25 +0200 Subject: [PATCH 06/10] 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 --- .../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 d09341b14..69eb3519d 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target_config.h +++ b/core/arch/arm/plat-qcom/hoya/lemans/target_config.h @@ -32,8 +32,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 1bc7a4e427aedbaa0a45be1ced5e503d0b90dba3 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Fri, 12 Jun 2026 09:30:42 +0200 Subject: [PATCH 07/10] 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 --- 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 b11ad3ae1..2285c664b 100644 --- a/core/mm/core_mmu.c +++ b/core/mm/core_mmu.c @@ -2453,8 +2453,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 30a18b93724992bf50599f56d8a6124ebbc6da44 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Fri, 12 Jun 2026 08:03:50 +0200 Subject: [PATCH 08/10] 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 --- .../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 69eb3519d..f58cce109 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target_config.h +++ b/core/arch/arm/plat-qcom/hoya/lemans/target_config.h @@ -39,9 +39,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 38ff615297b71c21e2dd68fdf45a6e235ad03956 Mon Sep 17 00:00:00 2001 From: Jorge Ramirez-Ortiz Date: Sun, 14 Jun 2026 18:49:39 +0200 Subject: [PATCH 09/10] 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 --- .../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 f58cce109..89d6ebd00 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target_config.h +++ b/core/arch/arm/plat-qcom/hoya/lemans/target_config.h @@ -32,6 +32,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). @@ -51,6 +62,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) @@ -72,4 +85,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 cb723c264565b0d5ec9244bfc641186120bb0d68 Mon Sep 17 00:00:00 2001 From: Siva Rama Krishna Potu Date: Mon, 29 Jun 2026 08:57:55 +0530 Subject: [PATCH 10/10] drivers: qcom: tlmm: gpio and pinctrl driver Signed-off-by: Siva Rama Krishna Potu --- core/arch/arm/plat-qcom/hoya/lemans/target.mk | 2 + .../arm/plat-qcom/hoya/lemans/target_config.h | 11 + .../arm/plat-qcom/hoya/lemans/tlmm_soc_data.c | 31 + core/arch/arm/plat-qcom/sub.mk | 1 + core/drivers/qcom/sub.mk | 1 + core/drivers/qcom/tlmm/gpio.c | 283 +++++++++ core/drivers/qcom/tlmm/pinctrl.c | 325 ++++++++++ core/drivers/qcom/tlmm/sub.mk | 6 + core/drivers/qcom/tlmm/test.c | 581 ++++++++++++++++++ core/drivers/qcom/tlmm/tlmm_private.h | 135 ++++ core/include/drivers/qcom/tlmm/tlmm.h | 159 +++++ 11 files changed, 1535 insertions(+) create mode 100644 core/arch/arm/plat-qcom/hoya/lemans/tlmm_soc_data.c create mode 100644 core/drivers/qcom/tlmm/gpio.c create mode 100644 core/drivers/qcom/tlmm/pinctrl.c create mode 100644 core/drivers/qcom/tlmm/sub.mk create mode 100644 core/drivers/qcom/tlmm/test.c create mode 100644 core/drivers/qcom/tlmm/tlmm_private.h create mode 100644 core/include/drivers/qcom/tlmm/tlmm.h diff --git a/core/arch/arm/plat-qcom/hoya/lemans/target.mk b/core/arch/arm/plat-qcom/hoya/lemans/target.mk index 678ccb7f0..9d039ff0a 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target.mk +++ b/core/arch/arm/plat-qcom/hoya/lemans/target.mk @@ -1,6 +1,8 @@ CFG_DRIVERS_CLK ?= y CFG_DRIVERS_QCOM_CLK ?= y +CFG_QCOM_TLMM ?= y + CFG_QCOM_QFPROM_FUSEPROV ?= $(if $(filter y,$(CFG_INSECURE)),n,y) _qcom_fuseprov_deps = $(if $(filter y,$(CFG_QCOM_QFPROM_FUSEPROV)),y,n) 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 89d6ebd00..94cffc03b 100644 --- a/core/arch/arm/plat-qcom/hoya/lemans/target_config.h +++ b/core/arch/arm/plat-qcom/hoya/lemans/target_config.h @@ -26,6 +26,17 @@ #define GENI_UART_REG_BASE UL(0xa8c000) +/* TLMM — from msmhwiobase.h and GPIOLemans.c */ +#define TLMM_BASE UL(0x0f000000) +#define TLMM_BASE_SIZE UL(0x01000000) + +#ifdef CFG_QCOM_TLMM +#ifndef __ASSEMBLER__ +#include +extern const struct tlmm_desc tlmm_soc_desc; +#endif +#endif + #define TURING_0_BASE UL(0x24000000) #define TURING_0_SIZE UL(0x03000000) diff --git a/core/arch/arm/plat-qcom/hoya/lemans/tlmm_soc_data.c b/core/arch/arm/plat-qcom/hoya/lemans/tlmm_soc_data.c new file mode 100644 index 000000000..f4a7af18b --- /dev/null +++ b/core/arch/arm/plat-qcom/hoya/lemans/tlmm_soc_data.c @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * LeMans TLMM hardware descriptor. + * + * Values sourced from GPIOLemans.c (HALgpioBlockDescType): + * nBaseAddr = 0x0f000000 (TLMM_BASE) + * nPinRegWidth = 0x1000 + * nNumTiles = 1 + * aTiles[0] = 0x100000 + * nMaxPinNumber = 148 (num_gpios = 149) + * has_egpio = true + * has_strong_pull = false + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include + +const struct tlmm_desc tlmm_soc_desc = { + .base = TLMM_BASE, + .size = TLMM_BASE_SIZE, + .pin_reg_width = 0x1000, + .num_tiles = 1, + .tile_offsets = { 0x100000, 0, 0, 0, 0 }, + .num_gpios = 149, + .has_egpio = true, + .has_strong_pull = false, + .lp_cfg_mask = GENMASK_32(12, 0), +}; diff --git a/core/arch/arm/plat-qcom/sub.mk b/core/arch/arm/plat-qcom/sub.mk index 81c0195fd..3ad671e8a 100644 --- a/core/arch/arm/plat-qcom/sub.mk +++ b/core/arch/arm/plat-qcom/sub.mk @@ -2,6 +2,7 @@ global-incdirs-y += . global-incdirs-y += $(QCOM_ARCH_FAMILY) global-incdirs-y += $(QCOM_ARCH_FAMILY)/$(PLATFORM_FLAVOR) srcs-y += main.c +srcs-$(CFG_QCOM_TLMM) += $(QCOM_ARCH_FAMILY)/$(PLATFORM_FLAVOR)/tlmm_soc_data.c $(eval $(call cfg-depends-all,CFG_QCOM_QFPROM_FUSEPROV,CFG_QCOM_QFPROM)) subdirs-$(CFG_QCOM_QFPROM_FUSEPROV) += provision diff --git a/core/drivers/qcom/sub.mk b/core/drivers/qcom/sub.mk index b7a931564..464ea6e2c 100644 --- a/core/drivers/qcom/sub.mk +++ b/core/drivers/qcom/sub.mk @@ -12,3 +12,4 @@ $(eval $(call cfg-depends-all,CFG_QCOM_RPMH_CLIENT,CFG_QCOM_CMD_DB)) subdirs-$(CFG_QCOM_CMD_DB) += cmd_db subdirs-$(CFG_QCOM_RPMH_CLIENT) += rpmh subdirs-$(CFG_QCOM_QFPROM) += qfprom +subdirs-$(CFG_QCOM_TLMM) += tlmm diff --git a/core/drivers/qcom/tlmm/gpio.c b/core/drivers/qcom/tlmm/gpio.c new file mode 100644 index 000000000..4b8d39a2e --- /dev/null +++ b/core/drivers/qcom/tlmm/gpio.c @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Qualcomm TLMM GPIO driver — non-DT implementation. + * + * Implements gpio_ops for Qcom TLMM hardware using driver_init. + * Also provides shared hardware helpers used by pinctrl.c: + * tlmm_tile_offset(), tlmm_pin_reg(), tlmm_write_cfg(). + * + * Consumers call tlmm_get_chip() to obtain the gpio_chip pointer, + * then use standard gpio_* wrappers from . + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tlmm_private.h" + +/* + * Register the TLMM physical region with the MMU at file scope. + * TLMM_BASE and TLMM_BASE_SIZE are defined in target_config.h. + */ +register_phys_mem_pgdir(MEM_AREA_IO_SEC, TLMM_BASE, TLMM_BASE_SIZE); + +/* Single global instance — one TLMM controller per SoC */ +struct tlmm_chip tlmm = { }; + +/* ------------------------------------------------------------------------- + * Shared hardware helpers — declared in tlmm_private.h, defined here. + * Used by both tlmm.c (gpio ops) and tlmm_pinctrl.c (pinctrl ops). + * ---------------------------------------------------------------------- + */ + +uint32_t tlmm_tile_offset(const struct tlmm_chip *chip, unsigned int pin) +{ + const struct tlmm_desc *d = chip->desc; + unsigned int t = 0; + vaddr_t id_reg = 0; + + if (d->num_tiles <= 1) + return d->tile_offsets[0]; + + for (t = 0; t < d->num_tiles; t++) { + id_reg = chip->base + + d->tile_offsets[t] + + (vaddr_t)d->pin_reg_width * pin + + TLMM_REG_ID_STATUS; + if (io_read32(id_reg) & TLMM_ID_STATUS_PRESENT) + return d->tile_offsets[t]; + } + + EMSG("TLMM: pin %u not found in any tile, defaulting to tile 0", pin); + return d->tile_offsets[0]; +} + +vaddr_t tlmm_pin_reg(const struct tlmm_chip *chip, unsigned int pin, + uint32_t reg_off) +{ + return chip->base + + tlmm_tile_offset(chip, pin) + + (vaddr_t)chip->desc->pin_reg_width * pin + + reg_off; +} + +/* + * tlmm_write_cfg() - RMW GPIO_CFG for a single pin. + * Preserves HIHYS (bit 10). Auto-sets EGPIO_ENABLE (bit 12) when needed. + */ +void tlmm_write_cfg(struct tlmm_chip *chip, unsigned int pin, + uint32_t clear_mask, uint32_t set_mask) +{ + vaddr_t reg = TLMM_GPIO_CFG(chip, pin); + uint32_t val = io_read32(reg); + + clear_mask &= ~TLMM_CFG_PRESERVE_MASK; + + if (chip->desc->has_egpio && (val & TLMM_CFG_EGPIO_PRESENT)) + set_mask |= TLMM_CFG_EGPIO_ENABLE; + + val = (val & ~clear_mask) | set_mask; + io_write32(reg, val); +} + +/* ------------------------------------------------------------------------- + * Internal helpers + * ---------------------------------------------------------------------- + */ + +static inline struct tlmm_chip *chip_to_tlmm(struct gpio_chip *chip) +{ + return container_of(chip, struct tlmm_chip, gpio_chip); +} + +static inline void tlmm_assert_pin(const struct tlmm_chip *tc, unsigned int pin) +{ + assert(pin < tc->desc->num_gpios); +} + +static inline bool tlmm_pin_is_owned(unsigned int pin) +{ + if (pin >= TLMM_MAX_GPIOS || pin >= tlmm.desc->num_gpios) + return false; + + return !!(tlmm.pin_owners[pin / 32] & BIT(pin % 32)); +} + +/* ------------------------------------------------------------------------- + * Pin ownership API + * + * Each pin must be claimed with tlmm_request_pin() before use and released + * with tlmm_release_pin() when done. A second request on an already-owned + * pin returns TEE_ERROR_BUSY. For pinctrl consumers, conf_apply() handles + * claiming and conf_free() handles releasing — no explicit request needed. + * ---------------------------------------------------------------------- + */ + +/* + * tlmm_restore_lp() - Restore GPIO_CFG from LP_CFG on pin release. + * + * XBL programs LP_CFG for every pin before handing off to OP-TEE. + * desc->lp_cfg_mask selects which bits to copy — bits [12:0] are + * common; bit 13 varies per SoC (GPIO_VALID on Lemans, I2C_PULL + * on SoCs where GPIO_CFG also has that field). + */ +void tlmm_restore_lp(struct tlmm_chip *chip, unsigned int pin) +{ + uint32_t lp_val = io_read32(TLMM_GPIO_LP_CFG(chip, pin)); + + io_write32(TLMM_GPIO_CFG(chip, pin), + lp_val & chip->desc->lp_cfg_mask); +} + +TEE_Result tlmm_request_pin(unsigned int pin) +{ + if (pin >= TLMM_MAX_GPIOS || pin >= tlmm.desc->num_gpios) + return TEE_ERROR_BAD_PARAMETERS; + + mutex_lock(&tlmm.lock); + if (tlmm.pin_owners[pin / 32] & BIT(pin % 32)) { + mutex_unlock(&tlmm.lock); + EMSG("TLMM: pin %u already owned", pin); + return TEE_ERROR_BUSY; + } + tlmm.pin_owners[pin / 32] |= BIT(pin % 32); + mutex_unlock(&tlmm.lock); + + return TEE_SUCCESS; +} + +void tlmm_release_pin(unsigned int pin) +{ + if (pin >= TLMM_MAX_GPIOS || pin >= tlmm.desc->num_gpios) + return; + + tlmm_restore_lp(&tlmm, pin); + + mutex_lock(&tlmm.lock); + tlmm.pin_owners[pin / 32] &= ~BIT(pin % 32); + mutex_unlock(&tlmm.lock); +} + +/* ------------------------------------------------------------------------- + * gpio_ops callbacks + * ---------------------------------------------------------------------- + */ + +static enum gpio_dir tlmm_get_direction(struct gpio_chip *chip, + unsigned int pin) +{ + struct tlmm_chip *tc = chip_to_tlmm(chip); + + tlmm_assert_pin(tc, pin); + return (io_read32(TLMM_GPIO_CFG(tc, pin)) & TLMM_CFG_OE) ? + GPIO_DIR_OUT : GPIO_DIR_IN; +} + +static void tlmm_set_direction(struct gpio_chip *chip, unsigned int pin, + enum gpio_dir dir) +{ + struct tlmm_chip *tc = chip_to_tlmm(chip); + + tlmm_assert_pin(tc, pin); + if (!tlmm_pin_is_owned(pin)) { + EMSG("TLMM: pin %u not owned, set_direction rejected", pin); + return; + } + if (dir == GPIO_DIR_OUT) + tlmm_write_cfg(tc, pin, 0, TLMM_CFG_OE); + else + tlmm_write_cfg(tc, pin, TLMM_CFG_OE, 0); +} + +static enum gpio_level tlmm_get_value(struct gpio_chip *chip, unsigned int pin) +{ + struct tlmm_chip *tc = chip_to_tlmm(chip); + + tlmm_assert_pin(tc, pin); + return (io_read32(TLMM_GPIO_IN_OUT(tc, pin)) & TLMM_IN_OUT_IN_BIT) ? + GPIO_LEVEL_HIGH : GPIO_LEVEL_LOW; +} + +static void tlmm_set_value(struct gpio_chip *chip, unsigned int pin, + enum gpio_level level) +{ + struct tlmm_chip *tc = chip_to_tlmm(chip); + + tlmm_assert_pin(tc, pin); + if (!tlmm_pin_is_owned(pin)) { + EMSG("TLMM: pin %u not owned, set_value rejected", pin); + return; + } + io_write32(TLMM_GPIO_IN_OUT(tc, pin), + (level == GPIO_LEVEL_HIGH) ? TLMM_IN_OUT_OUT_BIT : U(0)); +} + +static const struct gpio_ops tlmm_gpio_ops = { + .get_direction = tlmm_get_direction, + .set_direction = tlmm_set_direction, + .get_value = tlmm_get_value, + .set_value = tlmm_set_value, +}; +DECLARE_KEEP_PAGER(tlmm_gpio_ops); + +/* ------------------------------------------------------------------------- + * Public API — non-DT consumer entry point + * ---------------------------------------------------------------------- + */ + +/* + * tlmm_get_chip() - Return the gpio_chip for the main TLMM controller. + * + * Consumer usage: + * struct gpio g = { .chip = tlmm_get_chip(), .pin = 42, .dt_flags = 0 }; + * tlmm_request_pin(42); + * gpio_set_direction(&g, GPIO_DIR_OUT); + * gpio_set_value(&g, GPIO_LEVEL_HIGH); + * tlmm_release_pin(42); + */ +struct gpio_chip *tlmm_get_chip(void) +{ + assert(tlmm.gpio_chip.ops); + return &tlmm.gpio_chip; +} + +/* ------------------------------------------------------------------------- + * Driver init + * ---------------------------------------------------------------------- + */ + +static TEE_Result tlmm_init(void) +{ + const struct tlmm_desc *desc = &tlmm_soc_desc; + + tlmm.base = (vaddr_t)phys_to_virt(desc->base, MEM_AREA_IO_SEC, + desc->size); + if (!tlmm.base) { + EMSG("TLMM: failed to map base 0x%"PRIxPA, desc->base); + return TEE_ERROR_GENERIC; + } + + tlmm.desc = desc; + tlmm.gpio_chip.ops = &tlmm_gpio_ops; + mutex_init(&tlmm.lock); + + IMSG("TLMM: %u GPIOs, %u tile(s), egpio=%s, base=0x%"PRIxPA, + desc->num_gpios, desc->num_tiles, + desc->has_egpio ? "yes" : "no", + desc->base); + + return TEE_SUCCESS; +} + +driver_init(tlmm_init); diff --git a/core/drivers/qcom/tlmm/pinctrl.c b/core/drivers/qcom/tlmm/pinctrl.c new file mode 100644 index 000000000..1ae86869b --- /dev/null +++ b/core/drivers/qcom/tlmm/pinctrl.c @@ -0,0 +1,325 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Qualcomm TLMM pinctrl driver — non-DT implementation. + * + * Implements the OP-TEE pinctrl_ops interface for Qcom TLMM hardware. + * Works alongside gpio.c (gpio_ops). + * + * What pinctrl writes to GPIO_CFG: + * FUNC_SEL [5:2] — which peripheral function is muxed on this pad + * GPIO_PULL [1:0] — pull resistor configuration + * DRV_STRENGTH [8:6] — output drive strength + * STRONG_PULL [13] — strong I2C pull-up (conditional on has_strong_pull) + * + * What gpio_ops (gpio.c) writes: + * GPIO_OE [9] — direction (input/output) + * GPIO_IN_OUT +0x04 — output value / input read + * + * Consumer usage (non-DT): + * const struct tlmm_pin_group groups[] = { + * { pins, N, func, pull, drive_ma, false }, + * }; + * struct pinctrl_state *state = NULL; + * + * tlmm_make_pin_state(groups, ARRAY_SIZE(groups), &state); + * tlmm_apply_pin_state(state); + * tlmm_free_pin_state(state); + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "tlmm_private.h" + +/* + * struct tlmm_pin_conf - Private data embedded in a struct pinconf. + * + * Single contiguous allocation: + * [struct pinconf][struct tlmm_pin_conf][unsigned int pins[pin_count]] + * + * conf->priv points to tlmm_pin_conf immediately after conf. + * conf_free() calls free(conf) to release the entire block. + * + * @applied: set to true by conf_apply() on success; conf_free() only + * releases pin ownership if this is true, so a failed apply + * cannot accidentally free pins owned by another conf. + */ +struct tlmm_pin_conf { + struct tlmm_chip *chip; + uint32_t func; + uint32_t pull; + uint32_t drive_hw; + bool strong_pull; + bool applied; + unsigned int pin_count; + unsigned int pins[]; +}; + +/* ------------------------------------------------------------------------- + * pinctrl_ops callbacks + * ---------------------------------------------------------------------- + */ + +static TEE_Result tlmm_conf_apply(struct pinconf *conf) +{ + struct tlmm_pin_conf *pc = conf->priv; + struct tlmm_chip *chip = pc->chip; + unsigned int i = 0; + uint32_t clear = 0; + uint32_t set = 0; + + /* + * Atomically verify none of the pins in this group are already owned, + * then claim them all. A partial failure releases nothing — no pin + * was claimed yet since we check first, then claim. + */ + mutex_lock(&chip->lock); + for (i = 0; i < pc->pin_count; i++) { + unsigned int p = pc->pins[i]; + + if (chip->pin_owners[p / 32] & BIT(p % 32)) { + EMSG("TLMM: pin %u already owned, conf_apply rejected", + p); + mutex_unlock(&chip->lock); + return TEE_ERROR_BUSY; + } + } + for (i = 0; i < pc->pin_count; i++) { + unsigned int p = pc->pins[i]; + + chip->pin_owners[p / 32] |= BIT(p % 32); + } + mutex_unlock(&chip->lock); + + pc->applied = true; + + clear = TLMM_CFG_PULL_MASK | + TLMM_CFG_FUNC_MASK | + TLMM_CFG_DRIVE_MASK | + TLMM_CFG_STRONG_PULL; + + set = (pc->pull & TLMM_CFG_PULL_MASK) | + ((pc->func << TLMM_CFG_FUNC_SHIFT) & TLMM_CFG_FUNC_MASK) | + ((pc->drive_hw << TLMM_CFG_DRIVE_SHIFT) & TLMM_CFG_DRIVE_MASK); + + if (chip->desc->has_strong_pull && + pc->strong_pull && + pc->pull == TLMM_PULL_UP) + set |= TLMM_CFG_STRONG_PULL; + + for (i = 0; i < pc->pin_count; i++) { + DMSG("TLMM pinctrl: pin %u func=%u pull=%u drive_hw=%u", + pc->pins[i], pc->func, pc->pull, pc->drive_hw); + tlmm_write_cfg(chip, pc->pins[i], clear, set); + } + + return TEE_SUCCESS; +} + +static void tlmm_conf_free(struct pinconf *conf) +{ + struct tlmm_pin_conf *pc = conf->priv; + struct tlmm_chip *chip = pc->chip; + unsigned int i = 0; + + if (pc->applied) { + mutex_lock(&chip->lock); + for (i = 0; i < pc->pin_count; i++) { + unsigned int p = pc->pins[i]; + + tlmm_restore_lp(chip, p); + chip->pin_owners[p / 32] &= ~BIT(p % 32); + } + mutex_unlock(&chip->lock); + } + + /* + * free(conf) releases the entire contiguous block — tlmm_pin_conf + * and pins[] are embedded in the same allocation, not separate. + */ + free(conf); +} + +static const struct pinctrl_ops tlmm_pinctrl_ops = { + .conf_apply = tlmm_conf_apply, + .conf_free = tlmm_conf_free, +}; +DECLARE_KEEP_PAGER(tlmm_pinctrl_ops); + +/* ------------------------------------------------------------------------- + * Public API + * ---------------------------------------------------------------------- + */ + +/* 2mA steps: 0=2mA, 1=4mA ... 7=16mA; values above 16mA clamped to 7 */ +static uint32_t tlmm_drive_to_hw(unsigned int drive_ma) +{ + if (drive_ma <= 2) + return 0; + if (drive_ma <= 4) + return 1; + if (drive_ma <= 6) + return 2; + if (drive_ma <= 8) + return 3; + if (drive_ma <= 10) + return 4; + if (drive_ma <= 12) + return 5; + if (drive_ma <= 14) + return 6; + return 7; +} + +/* Build a pinctrl configuration blob for @pin_count pins. + * + * Allocates one contiguous block for all three objects: + * + * +----------------+-------------------+----------------------+ + * | struct pinconf | tlmm_pin_conf | unsigned int pins[N] | + * | (conf) | (pc = conf + 1) | (pc->pins, FAM) | + * +----------------+-------------------+----------------------+ + * ^ + * returned as struct pinconf * — free(conf) releases all three. + * + * conf->priv is set to pc (immediately after conf in memory). + * pc->pins is a flexible array member — no separate pointer needed. + */ +static TEE_Result tlmm_make_pinconf(const unsigned int *pins, + unsigned int pin_count, + uint32_t func, uint32_t pull, + unsigned int drive_ma, bool strong_pull, + struct pinconf **out_conf) +{ + struct pinconf *conf = NULL; + struct tlmm_pin_conf *pc = NULL; + size_t alloc_size = 0; + + if (!pins || !pin_count || !out_conf) + return TEE_ERROR_BAD_PARAMETERS; + + if (pin_count > TLMM_MAX_PINS_PER_GROUP) { + EMSG("TLMM: pin_count %u exceeds max %u", + pin_count, TLMM_MAX_PINS_PER_GROUP); + return TEE_ERROR_BAD_PARAMETERS; + } + + alloc_size = sizeof(*conf) + sizeof(*pc) + + pin_count * sizeof(pc->pins[0]); + + conf = calloc(1, alloc_size); + if (!conf) + return TEE_ERROR_OUT_OF_MEMORY; + + /* pc sits immediately after conf in the same allocation block */ + pc = (struct tlmm_pin_conf *)(conf + 1); + + pc->chip = &tlmm; + pc->func = func; + pc->pull = pull; + pc->drive_hw = tlmm_drive_to_hw(drive_ma); + pc->strong_pull = strong_pull; + pc->pin_count = pin_count; + memcpy(pc->pins, pins, pin_count * sizeof(pc->pins[0])); + + conf->ops = &tlmm_pinctrl_ops; + conf->priv = pc; + + *out_conf = conf; + return TEE_SUCCESS; +} + +/* + * tlmm_make_pin_state() - Build a pinctrl_state from multiple pin groups. + * + * Allocates a struct pinctrl_state containing one pinconf per group. On any + * allocation failure the already-built confs are freed and the state is not + * returned. No pins are owned at this point; claiming happens in conf_apply. + */ +TEE_Result tlmm_make_pin_state(const struct tlmm_pin_group *groups, + unsigned int group_count, + struct pinctrl_state **out_state) +{ + struct pinctrl_state *state = NULL; + unsigned int i = 0; + TEE_Result res = TEE_SUCCESS; + + if (!groups || !group_count || !out_state) + return TEE_ERROR_BAD_PARAMETERS; + + state = calloc(1, sizeof(*state) + + group_count * sizeof(*state->confs)); + if (!state) + return TEE_ERROR_OUT_OF_MEMORY; + + for (i = 0; i < group_count; i++) { + res = tlmm_make_pinconf(groups[i].pins, + groups[i].pin_count, + groups[i].func, + groups[i].pull, + groups[i].drive_ma, + groups[i].strong_pull, + &state->confs[i]); + if (res) { + /* + * Free confs[0..i-1]. Use `i > 0` rather than + * `while (i--)` to avoid unsigned wrap when i == 0. + */ + for (; i > 0; i--) { + struct pinconf *c = state->confs[i - 1]; + + c->ops->conf_free(c); + } + free(state); + return res; + } + } + + state->conf_count = group_count; + *out_state = state; + return TEE_SUCCESS; +} + +/* + * tlmm_apply_pin_state() / tlmm_free_pin_state() - Apply and release a + * multi-group pin state built by tlmm_make_pin_state(). + * + * These provide the same semantics as the framework's pinctrl_apply_state() + * and pinctrl_free_state() without requiring CFG_DRIVERS_PINCTRL, which + * pulls in DT infrastructure not available in non-DT builds. The underlying + * struct pinctrl_state type is identical, so callers can switch to the + * framework functions transparently if DT support is added later. + */ +TEE_Result tlmm_apply_pin_state(struct pinctrl_state *state) +{ + unsigned int i = 0; + TEE_Result res = TEE_SUCCESS; + + for (i = 0; i < state->conf_count; i++) { + res = state->confs[i]->ops->conf_apply(state->confs[i]); + if (res) { + EMSG("TLMM: conf_apply failed for group %u: %#"PRIx32, + i, res); + return res; + } + } + + return TEE_SUCCESS; +} + +void tlmm_free_pin_state(struct pinctrl_state *state) +{ + unsigned int i = 0; + + for (i = 0; i < state->conf_count; i++) + state->confs[i]->ops->conf_free(state->confs[i]); + + free(state); +} diff --git a/core/drivers/qcom/tlmm/sub.mk b/core/drivers/qcom/tlmm/sub.mk new file mode 100644 index 000000000..456054801 --- /dev/null +++ b/core/drivers/qcom/tlmm/sub.mk @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: BSD-2-Clause +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + +srcs-$(CFG_QCOM_TLMM) += gpio.c +srcs-$(CFG_QCOM_TLMM) += pinctrl.c +srcs-$(CFG_QCOM_TLMM_TEST) += test.c diff --git a/core/drivers/qcom/tlmm/test.c b/core/drivers/qcom/tlmm/test.c new file mode 100644 index 000000000..9ec630753 --- /dev/null +++ b/core/drivers/qcom/tlmm/test.c @@ -0,0 +1,581 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Consumer usage examples and tests for the Qcom TLMM GPIO and pinctrl drivers. + * + * Compiled only when CFG_QCOM_TLMM_TEST=y. + * + * GPIO consumer use cases covered: + * 1. Output pin — request, drive high/low, release + * 2. Input pin — request, read level, release + * 3. Active-low — logical polarity inversion via dt_flags + * + * Pinctrl consumer use cases covered: + * 4. Single pin — one pin, one group, one config + * 5. Single group, same config — multiple pins share one func/pull/drive + * 6. Multiple groups, different config — each group has its own settings + * + * Ownership and protection use cases covered: + * 7. GPIO pin ownership — exclusive claim, double-request returns BUSY + * 8. Pinctrl pin ownership — second apply on owned pins returns BUSY + * 9. Overlap detection — pin in two groups: second group rejected + * 10. Re-apply after free — ownership released, pin can be reclaimed + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include + +#define TEST_GPIO_OUTPUT U(100) +#define TEST_GPIO_INPUT U(101) +#define TEST_PINCTRL_PINS { U(100), U(101) } + +/* ========================================================================= + * Use cases 1-3: GPIO consumers + * + * Pattern: + * tlmm_request_pin(pin) -- claim exclusive ownership + * gpio_set_direction / value -- drive or read the pin + * tlmm_release_pin(pin) -- release ownership when done + * ======================================================================= + */ + +/* + * Use case 1: output pin — drive a pad high or low. + * + * Request ownership first; gpio_set_direction/value reject operations on + * unowned pins to prevent silent conflicts between callers. + */ +static void test_gpio_output(struct gpio_chip *chip) +{ + struct gpio g = { + .chip = chip, + .pin = TEST_GPIO_OUTPUT, + .dt_flags = 0, + }; + enum gpio_dir dir = GPIO_DIR_IN; + TEE_Result res = TEE_SUCCESS; + + DMSG("TLMM test: output GPIO %u", g.pin); + + res = tlmm_request_pin(TEST_GPIO_OUTPUT); + if (res) { + EMSG("TLMM test FAIL: request pin %u returned %#x", + TEST_GPIO_OUTPUT, res); + return; + } + + gpio_set_direction(&g, GPIO_DIR_OUT); + + dir = gpio_get_direction(&g); + if (dir != GPIO_DIR_OUT) + EMSG("TLMM test FAIL: pin %u expected OUT got %d", + g.pin, (int)dir); + + gpio_set_value(&g, GPIO_LEVEL_HIGH); + DMSG("TLMM test: pin %u driven HIGH", g.pin); + + gpio_set_value(&g, GPIO_LEVEL_LOW); + DMSG("TLMM test: pin %u driven LOW", g.pin); + + tlmm_release_pin(TEST_GPIO_OUTPUT); +} + +/* + * Use case 2: input pin — read the current pad level. + */ +static void test_gpio_input(struct gpio_chip *chip) +{ + struct gpio g = { + .chip = chip, + .pin = TEST_GPIO_INPUT, + .dt_flags = 0, + }; + enum gpio_level level = GPIO_LEVEL_LOW; + TEE_Result res = TEE_SUCCESS; + + DMSG("TLMM test: input GPIO %u", g.pin); + + res = tlmm_request_pin(TEST_GPIO_INPUT); + if (res) { + EMSG("TLMM test FAIL: request pin %u returned %#x", + TEST_GPIO_INPUT, res); + return; + } + + gpio_set_direction(&g, GPIO_DIR_IN); + level = gpio_get_value(&g); + + DMSG("TLMM test: pin %u level = %s", + g.pin, level == GPIO_LEVEL_HIGH ? "HIGH" : "LOW"); + + tlmm_release_pin(TEST_GPIO_INPUT); +} + +/* + * Use case 3: active-low pin — logical value is inverted by the driver. + * + * Setting GPIO_ACTIVE_LOW in dt_flags causes gpio_set_value to invert the + * logical level before writing the hardware register. The caller always + * works in logical (not hardware) polarity. + */ +static void test_gpio_active_low(struct gpio_chip *chip) +{ + struct gpio g = { + .chip = chip, + .pin = TEST_GPIO_OUTPUT, + .dt_flags = GPIO_ACTIVE_LOW, + }; + TEE_Result res = TEE_SUCCESS; + + res = tlmm_request_pin(TEST_GPIO_OUTPUT); + if (res) { + EMSG("TLMM test FAIL: request pin %u returned %#x", + TEST_GPIO_OUTPUT, res); + return; + } + + gpio_set_direction(&g, GPIO_DIR_OUT); + + gpio_set_value(&g, GPIO_LEVEL_HIGH); + DMSG("TLMM test: ACTIVE_LOW pin %u: logical HIGH → hw LOW", g.pin); + + gpio_set_value(&g, GPIO_LEVEL_LOW); + DMSG("TLMM test: ACTIVE_LOW pin %u: logical LOW → hw HIGH", g.pin); + + tlmm_release_pin(TEST_GPIO_OUTPUT); +} + +/* ========================================================================= + * Use cases 4-6: pinctrl consumers + * + * All pinctrl scenarios use the same three-step pattern regardless of how + * many pins or groups are involved: + * + * tlmm_make_pin_state(groups, N, &state) -- build config descriptor + * tlmm_apply_pin_state(state) -- claim pins, write GPIO_CFG + * tlmm_free_pin_state(state) -- release ownership, free memory + * + * Ownership is handled automatically by apply/free — no explicit + * tlmm_request_pin() call is needed for pinctrl consumers. + * + * Equivalent Qcom internal HAL: HAL_gpio_ConfigPin(pin, func, dir, pull, drive) + * Our pinctrl writes FUNC_SEL, GPIO_PULL, DRV_STRENGTH to GPIO_CFG. + * Direction (GPIO_OE) is set separately via gpio_ops when needed. + * ======================================================================= + */ + +/* + * Use case 4: single pin — one pin, one group, one configuration. + * + * The simplest pinctrl usage: configure a single pad's mux, pull and drive. + * The groups array has one element with pin_count=1. + */ +static void test_pinctrl_single_pin(void) +{ + const unsigned int pin = TEST_GPIO_OUTPUT; + const struct tlmm_pin_group groups[] = { + { &pin, 1, 0, TLMM_PULL_NONE, 2, false }, + }; + struct pinctrl_state *state = NULL; + TEE_Result res = TEE_SUCCESS; + + DMSG("TLMM test: pinctrl — single pin %u, GPIO mode, no pull, 2mA", + pin); + + res = tlmm_make_pin_state(groups, ARRAY_SIZE(groups), &state); + if (!res) + res = tlmm_apply_pin_state(state); + if (res) + EMSG("TLMM test FAIL: tlmm_apply_pin_state returned %#x", res); + else + DMSG("TLMM test: single pin %u config applied", pin); + + if (state) + tlmm_free_pin_state(state); +} + +/* + * Use case 5: single group — multiple pins sharing the same configuration. + * + * Both pins get func=1 (UART), no pull, 6 mA drive. One group array element + * covers all pins that need identical settings. + * + * Equivalent internal HAL: + * HAL_gpio_ConfigPin(100, 1, GPIO_INPUT, GPIO_NO_PULL, GPIO_6MA); + * HAL_gpio_ConfigPin(101, 1, GPIO_INPUT, GPIO_NO_PULL, GPIO_6MA); + */ +static void test_pinctrl_uart(void) +{ + unsigned int pins[] = TEST_PINCTRL_PINS; + const struct tlmm_pin_group groups[] = { + { pins, 2, 1, TLMM_PULL_NONE, 6, false }, + }; + struct pinctrl_state *state = NULL; + TEE_Result res = TEE_SUCCESS; + + DMSG("TLMM test: pinctrl — UART function on pins %u, %u", + pins[0], pins[1]); + + res = tlmm_make_pin_state(groups, ARRAY_SIZE(groups), &state); + if (!res) + res = tlmm_apply_pin_state(state); + if (res) + EMSG("TLMM test FAIL: tlmm_apply_pin_state returned %#x", res); + else + DMSG("TLMM test: pinctrl UART config applied"); + + if (state) + tlmm_free_pin_state(state); +} + +/* + * Use case 5 (variant): single group, multiple pins, different pull/drive + * from use case 5. + * + * Same single-group pattern, but with pull-up and 2 mA instead of no-pull + * and 6 mA. Both pins still share one config — the group has pin_count=2. + * Demonstrates that pull and drive are independently configurable per group. + * + * Equivalent internal HAL: + * HAL_gpio_ConfigPin(100, 0, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA); + * HAL_gpio_ConfigPin(101, 0, GPIO_INPUT, GPIO_PULL_UP, GPIO_2MA); + */ +static void test_pinctrl_gpio_pull(void) +{ + unsigned int pins[] = TEST_PINCTRL_PINS; + const struct tlmm_pin_group groups[] = { + { pins, 2, 0, TLMM_PULL_UP, 2, false }, + }; + struct pinctrl_state *state = NULL; + TEE_Result res = TEE_SUCCESS; + + DMSG("TLMM test: pinctrl — GPIO mode, pull-up, 2mA on pins %u, %u", + pins[0], pins[1]); + + res = tlmm_make_pin_state(groups, ARRAY_SIZE(groups), &state); + if (!res) + res = tlmm_apply_pin_state(state); + if (res) + EMSG("TLMM test FAIL: tlmm_apply_pin_state returned %#x", res); + else + DMSG("TLMM test: pinctrl GPIO+pull-up config applied"); + + if (state) + tlmm_free_pin_state(state); +} + +/* ========================================================================= + * Use cases 7-8: ownership and protection + * ======================================================================= + */ + +/* + * Use case 7: GPIO pin ownership — exclusive access enforcement. + * + * A pin can only be owned by one caller at a time. A second + * tlmm_request_pin() on an already-owned pin returns TEE_ERROR_BUSY. + * Once released, the pin can be claimed again. + */ +static void test_pin_ownership(void) +{ + TEE_Result res = TEE_SUCCESS; + + DMSG("TLMM test: pin ownership — start"); + + /* First request: succeeds */ + res = tlmm_request_pin(TEST_GPIO_OUTPUT); + if (res) + EMSG("TLMM test FAIL: first request returned %#x", res); + else + DMSG("TLMM test: request pin %u OK", TEST_GPIO_OUTPUT); + + /* Second request on same pin: must return BUSY */ + res = tlmm_request_pin(TEST_GPIO_OUTPUT); + if (res == TEE_ERROR_BUSY) + DMSG("TLMM test: double-request correctly rejected (BUSY)"); + else + EMSG("TLMM test FAIL: double-req returned %#x (expected BUSY)", + res); + + tlmm_release_pin(TEST_GPIO_OUTPUT); + + /* After release: can be claimed again */ + res = tlmm_request_pin(TEST_GPIO_OUTPUT); + if (res) + EMSG("TLMM test FAIL: post-release request returned %#x", res); + else + DMSG("TLMM test: post-release request OK"); + + tlmm_release_pin(TEST_GPIO_OUTPUT); + + DMSG("TLMM test: pin ownership — done"); +} + +/* + * Use case 8: pinctrl ownership — applying a different config without + * releasing the first is rejected. + * + * tlmm_apply_pin_state() claims pins atomically. A second apply on the same + * pins before free returns TEE_ERROR_BUSY — preventing a caller from silently + * overwriting another consumer's pin configuration. tlmm_free_pin_state() + * releases ownership; after that, a new apply succeeds. + */ +static void test_pinctrl_ownership(void) +{ + unsigned int pins[] = TEST_PINCTRL_PINS; + const struct tlmm_pin_group groups1[] = { + { pins, 2, 0, TLMM_PULL_NONE, 2, false }, + }; + const struct tlmm_pin_group groups2[] = { + { pins, 2, 1, TLMM_PULL_UP, 4, false }, + }; + struct pinctrl_state *state1 = NULL; + struct pinctrl_state *state2 = NULL; + TEE_Result res = TEE_SUCCESS; + + DMSG("TLMM test: pinctrl ownership — start"); + + /* First apply: pins 100,101 claimed */ + res = tlmm_make_pin_state(groups1, 1, &state1); + if (!res) + res = tlmm_apply_pin_state(state1); + if (res) { + EMSG("TLMM test FAIL: first apply returned %#x", res); + if (state1) + tlmm_free_pin_state(state1); + return; + } + DMSG("TLMM test: first apply on pins %u,%u OK", pins[0], pins[1]); + + /* Second apply on same pins without free: must return BUSY */ + res = tlmm_make_pin_state(groups2, 1, &state2); + if (!res) + res = tlmm_apply_pin_state(state2); + if (res == TEE_ERROR_BUSY) + DMSG("TLMM test: second apply without free correctly rejected"); + else + EMSG("TLMM test FAIL: second apply returned %#x (exp BUSY)", + res); + + /* state2 not applied — free releases no ownership, only memory */ + if (state2) + tlmm_free_pin_state(state2); + /* state1 was applied — free releases pins 100,101 */ + tlmm_free_pin_state(state1); + + /* After free: same pins can be configured again */ + state1 = NULL; + res = tlmm_make_pin_state(groups1, 1, &state1); + if (!res) + res = tlmm_apply_pin_state(state1); + if (res) + EMSG("TLMM test FAIL: post-free apply returned %#x", res); + else + DMSG("TLMM test: post-free apply OK"); + if (state1) + tlmm_free_pin_state(state1); + + DMSG("TLMM test: pinctrl ownership — done"); +} + +/* ========================================================================= + * Use cases 6, 9, 10: multi-group pin state + * ======================================================================= + */ + +/* + * Use case 6: multiple groups, multiple pins, DIFFERENT configs per group. + * + * When a peripheral's pins need different electrical settings, each + * requirement becomes one group. Here two UART TX pins share one config + * (no pull, 6 mA) and two UART RX pins share another (pull-up, 4 mA). + * A single tlmm_make_pin_state() + tlmm_apply_pin_state() configures all + * four pins atomically in one call. + * + * Equivalent internal HAL: + * HAL_gpio_ConfigPin(100, 1, GPIO_INPUT, GPIO_NO_PULL, GPIO_6MA); TX0 + * HAL_gpio_ConfigPin(102, 1, GPIO_INPUT, GPIO_NO_PULL, GPIO_6MA); TX1 + * HAL_gpio_ConfigPin(101, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_4MA); RX0 + * HAL_gpio_ConfigPin(103, 1, GPIO_INPUT, GPIO_PULL_UP, GPIO_4MA); RX1 + */ +static void test_pin_state_basic(void) +{ + const unsigned int tx_pins[] = { TEST_GPIO_OUTPUT, U(102) }; + const unsigned int rx_pins[] = { TEST_GPIO_INPUT, U(103) }; + const struct tlmm_pin_group groups[] = { + { tx_pins, 2, 1, TLMM_PULL_NONE, 6, false }, /* TX: no pull */ + { rx_pins, 2, 1, TLMM_PULL_UP, 4, false }, /* RX: pull-up */ + }; + struct pinctrl_state *state = NULL; + TEE_Result res = TEE_SUCCESS; + + DMSG("TLMM test: pin state basic — start"); + + res = tlmm_make_pin_state(groups, ARRAY_SIZE(groups), &state); + if (res) { + EMSG("TLMM test FAIL: make_pin_state returned %#x", res); + return; + } + + res = tlmm_apply_pin_state(state); + if (res) + EMSG("TLMM test FAIL: apply_state returned %#x", res); + else + DMSG("TLMM test: TX{%u,%u} no-pull/6mA RX{%u,%u} pull-up/4mA", + tx_pins[0], tx_pins[1], rx_pins[0], rx_pins[1]); + + tlmm_free_pin_state(state); + + DMSG("TLMM test: pin state basic — done"); +} + +/* + * Use case 9: overlap detection — a pin appearing in two groups of the same + * state is rejected. + * + * tlmm_apply_pin_state() claims groups in order. If group 1 contains a pin + * already claimed by group 0, it returns TEE_ERROR_BUSY without touching + * group 1's hardware. tlmm_free_pin_state() then releases group 0's pins + * correctly (its applied flag is set) while simply freeing group 1's memory + * (its applied flag is clear). + */ +static void test_pin_state_overlap(void) +{ + const unsigned int g0_pins[] = { TEST_GPIO_OUTPUT, TEST_GPIO_INPUT }; + /* pin 101 overlaps with g0_pins */ + const unsigned int g1_pins[] = { TEST_GPIO_INPUT, U(102) }; + const struct tlmm_pin_group groups[] = { + { g0_pins, 2, 0, TLMM_PULL_NONE, 2, false }, + { g1_pins, 2, 0, TLMM_PULL_UP, 2, false }, + }; + struct pinctrl_state *state = NULL; + TEE_Result res = TEE_SUCCESS; + + DMSG("TLMM test: pin state overlap — start"); + + res = tlmm_make_pin_state(groups, ARRAY_SIZE(groups), &state); + if (res) { + EMSG("TLMM test FAIL: make_pin_state returned %#x", res); + return; + } + + res = tlmm_apply_pin_state(state); + if (res == TEE_ERROR_BUSY) + DMSG("TLMM test: overlapping pin correctly rejected (BUSY)"); + else + EMSG("TLMM test FAIL: overlap apply returned %#x (exp BUSY)", + res); + + /* free_state: group 0 releases {100,101}; group 1 frees memory only */ + tlmm_free_pin_state(state); + + /* Confirm pins 100 and 101 are no longer owned */ + res = tlmm_request_pin(TEST_GPIO_OUTPUT); + if (res) + EMSG("TLMM test FAIL: pin %u still owned after free (%#x)", + TEST_GPIO_OUTPUT, res); + else + DMSG("TLMM test: pin %u correctly released", TEST_GPIO_OUTPUT); + tlmm_release_pin(TEST_GPIO_OUTPUT); + + res = tlmm_request_pin(TEST_GPIO_INPUT); + if (res) + EMSG("TLMM test FAIL: pin %u still owned after free (%#x)", + TEST_GPIO_INPUT, res); + else + DMSG("TLMM test: pin %u correctly released", TEST_GPIO_INPUT); + tlmm_release_pin(TEST_GPIO_INPUT); + + DMSG("TLMM test: pin state overlap — done"); +} + +/* + * Use case 10: re-apply after free — ownership is fully released by + * tlmm_free_pin_state(), allowing the same pins to be configured again. + * + * Demonstrates the complete apply → use → free → re-apply lifecycle that + * a real driver would follow across PM state transitions (active/sleep). + */ +static void test_pin_state_post_free(void) +{ + const unsigned int pins[] = { TEST_GPIO_OUTPUT, TEST_GPIO_INPUT }; + const struct tlmm_pin_group groups[] = { + { pins, 2, 0, TLMM_PULL_NONE, 2, false }, + }; + struct pinctrl_state *state = NULL; + TEE_Result res = TEE_SUCCESS; + + DMSG("TLMM test: pin state post-free — start"); + + res = tlmm_make_pin_state(groups, ARRAY_SIZE(groups), &state); + if (!res) + res = tlmm_apply_pin_state(state); + if (res) { + EMSG("TLMM test FAIL: first apply returned %#x", res); + if (state) + tlmm_free_pin_state(state); + return; + } + DMSG("TLMM test: first state applied OK"); + tlmm_free_pin_state(state); + state = NULL; + + /* Re-apply: must succeed because free released ownership */ + res = tlmm_make_pin_state(groups, ARRAY_SIZE(groups), &state); + if (!res) + res = tlmm_apply_pin_state(state); + if (res) + EMSG("TLMM test FAIL: post-free re-apply returned %#x", res); + else + DMSG("TLMM test: post-free re-apply OK"); + if (state) + tlmm_free_pin_state(state); + + DMSG("TLMM test: pin state post-free — done"); +} + +/* ========================================================================= + * Entry points + * ======================================================================= + */ + +static void tlmm_gpio_test(void) +{ + struct gpio_chip *chip = tlmm_get_chip(); + + IMSG("TLMM GPIO test: start"); + test_pin_ownership(); /* use case 7 */ + test_gpio_output(chip); /* use case 1 */ + test_gpio_input(chip); /* use case 2 */ + test_gpio_active_low(chip); /* use case 3 */ + IMSG("TLMM GPIO test: done"); +} + +static void tlmm_pinctrl_test(void) +{ + IMSG("TLMM pinctrl test: start"); + test_pinctrl_ownership(); /* use case 8 */ + test_pinctrl_single_pin(); /* use case 4 */ + test_pinctrl_uart(); /* use case 5 */ + test_pinctrl_gpio_pull(); /* use case 5 (variant) */ + test_pin_state_basic(); /* use case 6 */ + test_pin_state_overlap(); /* use case 9 */ + test_pin_state_post_free(); /* use case 10 */ + IMSG("TLMM pinctrl test: done"); +} + +/* + * Auto-invoked at driver_init_late — after tlmm_init() has run at + * driver_init phase, ensuring the hardware mapping and gpio_ops are ready. + */ +static TEE_Result tlmm_run_tests(void) +{ + tlmm_gpio_test(); + tlmm_pinctrl_test(); + return TEE_SUCCESS; +} +driver_init_late(tlmm_run_tests); diff --git a/core/drivers/qcom/tlmm/tlmm_private.h b/core/drivers/qcom/tlmm/tlmm_private.h new file mode 100644 index 000000000..c7126218a --- /dev/null +++ b/core/drivers/qcom/tlmm/tlmm_private.h @@ -0,0 +1,135 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * TLMM GPIO driver — internal register definitions and data structures. + * + * Not for inclusion outside the tlmm driver directory. + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __QCOM_TLMM_PRIVATE_H +#define __QCOM_TLMM_PRIVATE_H + +#include +#include +#include +#include +#include +#include + +/* + * GPIO_CFG register — bit field layout. + * Address: TLMM_BASE + tile_offset + pin * pin_reg_width + 0x00 + * + * Talos — bits [10:0] (no eGPIO, no STRONG_PULL) + * LeMans — bits [12:0] (adds EGPIO_PRESENT[11], EGPIO_ENABLE[12]) + * Newer — bits [13:0] (adds STRONG_PULL[13]) + * + * [1:0] GPIO_PULL — 0=no-pull, 1=pull-down, 2=keeper, 3=pull-up + * [5:2] FUNC_SEL — function select (0 = GPIO mode) + * [8:6] DRV_STRENGTH — 0=2mA ... 7=16mA in 2mA steps + * [9] GPIO_OE — output enable (1=output, 0=input) + * [10] GPIO_HIHYS_EN — high-hysteresis; HW-managed, never clear + * [11] EGPIO_PRESENT — R-only; LPASS/SSC can own this GPIO + * [12] EGPIO_ENABLE — RW; must be 1 when EGPIO_PRESENT=1 + * [13] STRONG_PULL — RW; strong pull-up (I2C); newer SoCs only + */ +#define TLMM_CFG_PULL_MASK GENMASK_32(1, 0) +#define TLMM_CFG_FUNC_SHIFT U(2) +#define TLMM_CFG_FUNC_MASK GENMASK_32(5, 2) +#define TLMM_CFG_DRIVE_SHIFT U(6) +#define TLMM_CFG_DRIVE_MASK GENMASK_32(8, 6) +#define TLMM_CFG_OE BIT(9) +#define TLMM_CFG_HIHYS BIT(10) +#define TLMM_CFG_EGPIO_PRESENT BIT(11) +#define TLMM_CFG_EGPIO_ENABLE BIT(12) +#define TLMM_CFG_STRONG_PULL BIT(13) + +/* Bits preserved unconditionally on every GPIO_CFG RMW */ +#define TLMM_CFG_PRESERVE_MASK TLMM_CFG_HIHYS + +/* + * GPIO_IN_OUT register — bit field layout. + * Address: TLMM_BASE + tile_offset + pin * pin_reg_width + 0x04 + * + * [0] GPIO_IN R — current pad level + * [1] GPIO_OUT RW — output latch (direct write, no RMW needed) + */ +#define TLMM_IN_OUT_IN_BIT BIT(0) +#define TLMM_IN_OUT_OUT_BIT BIT(1) + +/* + * GPIO_ID_STATUS register. + * [0] R — 1 if this pin exists in this tile. Probed on-demand for + * multi-tile SoCs. Never bulk-probed (HLOS pins are XPU-protected). + */ +#define TLMM_ID_STATUS_PRESENT BIT(0) + +/* Compile-time maximum GPIOs supported — sized for the ownership bitmap */ +#define TLMM_MAX_GPIOS U(256) + +/* Register offsets within each pin's register set */ +#define TLMM_REG_CFG U(0x00) +#define TLMM_REG_IN_OUT U(0x04) +#define TLMM_REG_ID_STATUS U(0x10) +#define TLMM_REG_LP_CFG U(0x14) + +/* + * LP_CFG register — XBL programs this for every pin before handing off + * to OP-TEE. Bits [12:0] share the same layout as GPIO_CFG. Bit 13 + * meaning differs per SoC (GPIO_VALID on Lemans, I2C_PULL on others) — + * tlmm_desc.lp_cfg_mask controls which bits are copied to GPIO_CFG on + * pin release. + */ + +/* + * struct tlmm_chip - Runtime state for one TLMM controller instance. + * + * @gpio_chip: Embedded gpio_chip — MUST be first member so container_of + * in chip_to_tlmm() resolves to a zero offset. + * @base: Virtual base address after MMU mapping. + * @desc: SoC-specific hardware descriptor. + * @lock: Serialises access to pin_owners. + * @pin_owners: Bitmap tracking which pins are currently claimed. Each bit + * is set by tlmm_request_pin() and cleared by + * tlmm_release_pin(). conf_apply() sets bits atomically for + * the whole pin group; conf_free() clears them. + */ +struct tlmm_chip { + struct gpio_chip gpio_chip; + vaddr_t base; + const struct tlmm_desc *desc; + struct mutex lock; /* serialises pin_owners access */ + uint32_t pin_owners[TLMM_MAX_GPIOS / 32]; +}; + +/* + * Function declarations — defined in tlmm.c, shared with tlmm_pinctrl.c. + * Headers declare, .c files define — no function bodies in headers. + */ + +/* Return the tile byte-offset that owns @pin */ +uint32_t tlmm_tile_offset(const struct tlmm_chip *chip, unsigned int pin); + +/* Compute virtual address: base + tile_offset + stride*pin + reg_off */ +vaddr_t tlmm_pin_reg(const struct tlmm_chip *chip, unsigned int pin, + uint32_t reg_off); + +/* RMW GPIO_CFG — preserves HIHYS, auto-sets EGPIO_ENABLE when needed */ +void tlmm_write_cfg(struct tlmm_chip *chip, unsigned int pin, + uint32_t clear_mask, uint32_t set_mask); + +/* Restore GPIO_CFG from LP_CFG on pin release */ +void tlmm_restore_lp(struct tlmm_chip *chip, unsigned int pin); + +/* Convenience address macros */ +#define TLMM_GPIO_CFG(c, p) tlmm_pin_reg((c), (p), TLMM_REG_CFG) +#define TLMM_GPIO_IN_OUT(c, p) tlmm_pin_reg((c), (p), TLMM_REG_IN_OUT) +#define TLMM_GPIO_ID_STATUS(c, p) \ + tlmm_pin_reg((c), (p), TLMM_REG_ID_STATUS) +#define TLMM_GPIO_LP_CFG(c, p) tlmm_pin_reg((c), (p), TLMM_REG_LP_CFG) + +/* The single TLMM controller instance — defined in tlmm.c */ +extern struct tlmm_chip tlmm; + +#endif /* __QCOM_TLMM_PRIVATE_H */ diff --git a/core/include/drivers/qcom/tlmm/tlmm.h b/core/include/drivers/qcom/tlmm/tlmm.h new file mode 100644 index 000000000..43cf4aea8 --- /dev/null +++ b/core/include/drivers/qcom/tlmm/tlmm.h @@ -0,0 +1,159 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Qualcomm TLMM GPIO and pinctrl driver — public API. + * + * GPIO consumers: + * chip = tlmm_get_chip(); + * struct gpio g = { .chip = chip, .pin = 42, .dt_flags = 0 }; + * tlmm_request_pin(42); + * gpio_set_direction(&g, GPIO_DIR_OUT); + * gpio_set_value(&g, GPIO_LEVEL_HIGH); + * tlmm_release_pin(42); + * + * Pinctrl consumers — all cases use the same state-based flow: + * + * // Single pin, or multiple pins with the same config (one group): + * const struct tlmm_pin_group groups[] = { + * { pins, N, func, pull, drive_ma, false }, + * }; + * + * // Multiple pins with different configs (multiple groups): + * const struct tlmm_pin_group groups[] = { + * { tx_pins, 1, 1, TLMM_PULL_NONE, 6, false }, + * { rx_pins, 1, 1, TLMM_PULL_UP, 4, false }, + * }; + * + * struct pinctrl_state *state = NULL; + * tlmm_make_pin_state(groups, ARRAY_SIZE(groups), &state); + * tlmm_apply_pin_state(state); + * tlmm_free_pin_state(state); + * + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __DRIVERS_QCOM_TLMM_TLMM_H +#define __DRIVERS_QCOM_TLMM_TLMM_H + +#include +#include +#include +#include +#include +#include + +/* Maximum number of tiles a single TLMM controller can have */ +#define TLMM_MAX_TILES U(5) + +/* + * struct tlmm_desc - SoC-specific TLMM hardware descriptor. + * Defined in tlmm_soc_data.c per SoC. All driver code is parameterized + * through this struct — nothing is hardcoded in gpio.c or pinctrl.c. + * + * @base: Physical base address (TLMM_BASE). + * @size: Physical region size (TLMM_BASE_SIZE). + * @pin_reg_width: Byte stride between pin register sets (0x1000). + * @num_tiles: Number of tiles (1 for LeMans, 3 for Talos). + * @tile_offsets: Byte offsets from base to each tile's register window. + * @num_gpios: Total GPIO count. + * @has_egpio: true if GPIO_CFG has EGPIO_PRESENT[11]/EGPIO_ENABLE[12]. + * @has_strong_pull: true if GPIO_CFG has STRONG_PULL[13]. + * @lp_cfg_mask: Bitmask of LP_CFG bits to copy into GPIO_CFG on pin + * release. Bits [12:0] are common to all SoCs. Bit 13 + * is GPIO_VALID in LP_CFG (Lemans) — exclude it since + * GPIO_CFG has no such field — or I2C_PULL (newer SoCs) + * — include it when GPIO_CFG bit 13 exists. + */ +struct tlmm_desc { + paddr_t base; + size_t size; + uint32_t pin_reg_width; + uint32_t num_tiles; + uint32_t tile_offsets[TLMM_MAX_TILES]; + unsigned int num_gpios; + bool has_egpio; + bool has_strong_pull; + uint32_t lp_cfg_mask; +}; + +/* Pull configuration values — match Qcom HAL GPIOPullType */ +#define TLMM_PULL_NONE U(0) +#define TLMM_PULL_DOWN U(1) +#define TLMM_PULL_KEEPER U(2) +#define TLMM_PULL_UP U(3) + +/* Maximum pins in a single tlmm_pin_group */ +#define TLMM_MAX_PINS_PER_GROUP U(64) + +/* GPIO API */ +struct gpio_chip *tlmm_get_chip(void); + +/* + * Pin ownership API — must be called before using gpio_ops on a pin. + * + * tlmm_request_pin(): claim exclusive ownership of @pin. Returns + * TEE_ERROR_BUSY if another caller already owns it, TEE_ERROR_BAD_PARAMETERS + * if @pin is out of range. + * tlmm_release_pin(): release ownership. Safe to call on an unowned pin. + */ +TEE_Result tlmm_request_pin(unsigned int pin); +void tlmm_release_pin(unsigned int pin); + +/* Pinctrl API *//* + * struct tlmm_pin_group - One group of pins sharing the same configuration. + * + * Used as an element of the array passed to tlmm_make_pin_state() to express + * a set of groups where each group may have a different func/pull/drive. + * + * @pins: Array of global pin numbers in this group. + * @pin_count: Number of entries in @pins. Max TLMM_MAX_PINS_PER_GROUP. + * @func: FUNC_SEL value (0 = GPIO mode). + * @pull: TLMM_PULL_NONE / DOWN / KEEPER / UP. + * @drive_ma: Drive strength in mA (2–16). + * @strong_pull: true = I2C strong pull (only if has_strong_pull && PULL_UP). + */ +struct tlmm_pin_group { + const unsigned int *pins; + unsigned int pin_count; + uint32_t func; + uint32_t pull; + unsigned int drive_ma; + bool strong_pull; +}; + +/* + * tlmm_make_pin_state() - Build a pinctrl_state from multiple pin groups. + * + * Each group may carry a distinct configuration (func/pull/drive), allowing + * a caller to configure many differently-typed pins in one shot: + * + * tlmm_apply_pin_state(state); -- claims all pins, writes all configs + * tlmm_free_pin_state(state); -- releases all ownership, frees memory + * + * On failure during construction every successfully allocated group config is + * freed and the state struct is not returned. Ownership is never touched + * here; claiming happens inside conf_apply. + * + * tlmm_apply_pin_state() does not roll back on partial failure. If it + * returns an error, call tlmm_free_pin_state() to release whatever was + * applied. + * + * Returns TEE_ERROR_BAD_PARAMETERS if groups/group_count/out_state are NULL + * or zero, or TEE_ERROR_OUT_OF_MEMORY on allocation failure. + */ +TEE_Result tlmm_make_pin_state(const struct tlmm_pin_group *groups, + unsigned int group_count, + struct pinctrl_state **out_state); + +/* + * tlmm_apply_pin_state() / tlmm_free_pin_state() - Apply and release a + * multi-group pin state built by tlmm_make_pin_state(). + * + * Non-DT equivalents of pinctrl_apply_state() / pinctrl_free_state(). + * tlmm_apply_pin_state() returns TEE_ERROR_BUSY if any group's pins are + * already owned; on partial failure call tlmm_free_pin_state() to release + * whatever was applied. + */ +TEE_Result tlmm_apply_pin_state(struct pinctrl_state *state); +void tlmm_free_pin_state(struct pinctrl_state *state); + +#endif /* __DRIVERS_QCOM_TLMM_TLMM_H */