From c6989b07e1bd7272d5d4dd2e54bc4aa387eef854 Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Fri, 20 Feb 2026 11:58:15 +0530 Subject: [PATCH 01/14] drivers: qcom: cmd_db: add CMD_DB driver Add Command Database (CMD_DB) driver for Qualcomm platforms. CMD_DB provides resource address and priority lookup from a shared memory database populated by the Always-On Processor (AOP). This database contains mappings between resource identifiers (e.g., "mx.lvl" for MX voltage rail) and their hardware addresses, which are required by RPMH for power management operations. The driver supports: - Resource address queries by resource ID - Priority level queries for different driver IDs - Validation of database integrity (magic number, version) This is a prerequisite for RPMH client driver functionality. Signed-off-by: Selvam Sathappan Periakaruppan --- core/drivers/qcom/cmd_db/cmd_db.c | 436 ++++++++++++++++++ .../qcom/cmd_db/cmd_db_target_config.h | 40 ++ core/drivers/qcom/cmd_db/sub.mk | 6 + core/drivers/qcom/sub.mk | 1 + core/include/drivers/qcom/cmd_db/cmd_db.h | 22 + 5 files changed, 505 insertions(+) create mode 100644 core/drivers/qcom/cmd_db/cmd_db.c create mode 100644 core/drivers/qcom/cmd_db/cmd_db_target_config.h create mode 100644 core/drivers/qcom/cmd_db/sub.mk create mode 100644 core/include/drivers/qcom/cmd_db/cmd_db.h diff --git a/core/drivers/qcom/cmd_db/cmd_db.c b/core/drivers/qcom/cmd_db/cmd_db.c new file mode 100644 index 0000000000..70eac23313 --- /dev/null +++ b/core/drivers/qcom/cmd_db/cmd_db.c @@ -0,0 +1,436 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cmd_db_target_config.h" + +register_phys_mem_pgdir(MEM_AREA_IO_NSEC, AOP_MSG_RAM_BASE, + CORE_MMU_PGDIR_SIZE); + +#define CMD_DB_MAGIC_NUM 0x0c0330db +#define CMD_DB_VER 0x00000001 + +#define CMD_DB_SLV_ID_ACTUAL 3 +#define CMD_DB_SLV_ID_RESERVE 5 +#define CMD_DB_MAX_SLV_ID (CMD_DB_SLV_ID_ACTUAL + \ + CMD_DB_SLV_ID_RESERVE) + +#define CMD_DB_ADDR_SLV_ID_SHIFT 16 +#define CMD_DB_ADDR_SLV_ID_MASK 0xf + +#define CMD_DB_RES_ID_MAX_CHARS 8 +#define CMD_DB_CHAR_BITS 8 + +#define CMD_DB_PRIORITY_LOW_DRV_MAX 15 +#define CMD_DB_PRIORITY_HIGH_DRV_MAX 31 +#define CMD_DB_PRIORITY_HIGH_DRV_OFFSET 16 +#define PRIORITY_BITS_PER_DRV 2 +#define PRIORITY_MASK 0x3 +#define MAX_DRV_ID CMD_DB_PRIORITY_HIGH_DRV_MAX + +#define ADDR_SLV_ID(id) \ + ((enum cmd_db_slv_id_type)(((id) >> CMD_DB_ADDR_SLV_ID_SHIFT) & \ + CMD_DB_ADDR_SLV_ID_MASK)) + +enum cmd_db_slv_id_type { + CMD_DB_SLV_ID_INVALID = 0, + CMD_DB_SLV_ID_VALID_LOW = 3, + CMD_DB_SLV_ID_ARC = CMD_DB_SLV_ID_VALID_LOW, + CMD_DB_SLV_ID_VRM = 4, + CMD_DB_SLV_ID_BCM = 5, + CMD_DB_SLV_ID_VALID_HIGH = CMD_DB_SLV_ID_BCM, +}; + +enum cmd_db_query_type { + CMD_DB_QUERY_RES_ID = 0, + CMD_DB_QUERY_ADDRESS, + CMD_DB_QUERY_INVALID, +}; + +struct cmd_db_query_result_type { + char res_id[CMD_DB_MAX_RES_ID_LEN + 1]; + uint32_t addr; + uint32_t priority[CMD_DB_DRV_ID_PRIORITY_SZ]; + uint32_t len; + uint16_t version; +}; + +struct cmd_db_query_info { + char res_id[CMD_DB_MAX_RES_ID_LEN + 1]; + uint32_t addr; + enum cmd_db_query_type type; + enum cmd_db_slv_id_type slv_id; +}; + +struct entry_header { + uint64_t res_id; + uint32_t priority[CMD_DB_DRV_ID_PRIORITY_SZ]; + uint32_t addr; + uint16_t len; + uint16_t offset; +}; + +struct slv_id_info { + uint16_t slv_id; + uint16_t header_offset; + uint16_t data_offset; + uint16_t cnt; + uint16_t version; + uint16_t reserved[3]; +}; + +struct db_header { + uint32_t version; + uint32_t magic_num; + struct slv_id_info slv_id_info[CMD_DB_MAX_SLV_ID]; + uint32_t check_sum; + uint32_t reserved; + uint8_t data[]; +}; + +struct cmd_db_data { + struct db_header *data; + size_t size; + struct mutex lock; /* Protects CMD_DB data access */ +}; + +static struct cmd_db_data query_db = { + .lock = MUTEX_INITIALIZER, +}; + +static uint64_t conv_str_to_uint64(const char *res_id) +{ + uint64_t value = 0; + size_t i, len; + + len = strnlen(res_id, CMD_DB_RES_ID_MAX_CHARS + 1); + if (len == 0 || len > CMD_DB_RES_ID_MAX_CHARS) + return 0; + + for (i = 0; i < len; i++) + value |= ((uint64_t)res_id[i]) << (CMD_DB_CHAR_BITS * i); + + return value; +} + +static bool is_valid_slv_id(uint32_t slv_id) +{ + return (slv_id >= CMD_DB_SLV_ID_VALID_LOW && + slv_id <= CMD_DB_SLV_ID_VALID_HIGH); +} + +static bool is_valid_res_id(const char *res_id) +{ + if (!res_id || !res_id[0]) + return false; + + return (strnlen(res_id, CMD_DB_RES_ID_MAX_CHARS + 1) <= + CMD_DB_RES_ID_MAX_CHARS); +} + +static TEE_Result get_config(paddr_t *base_addr, size_t *size_out) +{ + struct cmd_db_target_config config = { }; + struct aop_cmd_db_metadata *metadata = NULL; + + config = cmd_db_get_target_config(); + + if (!config.aop_metadata_addr || !config.aop_metadata_size) + return TEE_ERROR_BAD_PARAMETERS; + + metadata = phys_to_virt(config.aop_metadata_addr, MEM_AREA_IO_NSEC, + config.aop_metadata_size); + if (!metadata) { + EMSG("Failed to get VA for AOP metadata at PA 0x%lx", + (unsigned long)config.aop_metadata_addr); + return TEE_ERROR_GENERIC; + } + + *base_addr = metadata->addr; + *size_out = metadata->size; + + return TEE_SUCCESS; +} + +static TEE_Result cmd_db_init(void) +{ + paddr_t pa = 0; + size_t size = 0; + TEE_Result res; + + res = get_config(&pa, &size); + if (res != TEE_SUCCESS) { + EMSG("CMD_DB: Failed to get configuration"); + goto err_panic; + } + + query_db.data = core_mmu_add_mapping(MEM_AREA_IO_NSEC, pa, size); + if (!query_db.data) { + EMSG("CMD_DB: Failed to map at PA 0x%lx", (unsigned long)pa); + goto err_panic; + } + + query_db.size = size; + + if (query_db.data->version != CMD_DB_VER || + query_db.data->magic_num != CMD_DB_MAGIC_NUM) { + EMSG("CMD_DB: Version/magic mismatch: 0x%x/0x%x exp 0x%x/0x%x", + query_db.data->version, query_db.data->magic_num, + CMD_DB_VER, CMD_DB_MAGIC_NUM); + goto err_panic; + } + + return TEE_SUCCESS; + +err_panic: + panic("CMD_DB driver initialization failed"); +} + +static TEE_Result validate_entry_bounds(struct slv_id_info *slv_info, + uint16_t entry_idx) +{ + size_t offset, bounds, entry_size; + + if (MUL_OVERFLOW(sizeof(struct entry_header), entry_idx, &entry_size) || + ADD_OVERFLOW(slv_info->header_offset, entry_size, &offset) || + ADD_OVERFLOW(sizeof(struct db_header), offset, &bounds) || + ADD_OVERFLOW(bounds, sizeof(struct entry_header), &bounds) || + bounds > query_db.size) + return TEE_ERROR_CORRUPT_OBJECT; + + return TEE_SUCCESS; +} + +static TEE_Result search_entry(struct cmd_db_query_info *query_info, + uint64_t res_id, uint16_t *slv_idx, + struct entry_header *entry) +{ + struct slv_id_info *slv_info = NULL; + bool valid; + uint16_t i, j; + TEE_Result res = TEE_SUCCESS; + + if (!query_db.data) + return TEE_ERROR_BAD_STATE; + + valid = is_valid_slv_id(query_info->slv_id); + + for (i = 0; i < CMD_DB_MAX_SLV_ID; i++) { + slv_info = &query_db.data->slv_id_info[i]; + + if (valid) { + if (slv_info->slv_id != query_info->slv_id) + continue; + } + + for (j = 0; j < slv_info->cnt; j++) { + res = validate_entry_bounds(slv_info, j); + if (res != TEE_SUCCESS) + return res; + + memcpy(entry, query_db.data->data + + slv_info->header_offset + + sizeof(struct entry_header) * j, sizeof(*entry)); + + if (query_info->type == CMD_DB_QUERY_RES_ID && + res_id == entry->res_id) + goto found; + + if (query_info->type == CMD_DB_QUERY_ADDRESS && + query_info->addr == entry->addr) + goto found; + } + } + + return TEE_ERROR_ITEM_NOT_FOUND; + +found: + *slv_idx = i; + return TEE_SUCCESS; +} + +static TEE_Result copy_aux_data(uint16_t slv_idx, struct entry_header *entry, + struct cmd_db_query_result_type *result, + uint8_t *data) +{ + struct slv_id_info *slv_info = &query_db.data->slv_id_info[slv_idx]; + uint32_t len = MIN(result->len, entry->len); + size_t offset, bounds; + + if (!data) + return TEE_ERROR_BAD_PARAMETERS; + + if (ADD_OVERFLOW(slv_info->data_offset, entry->offset, &offset) || + ADD_OVERFLOW(sizeof(struct db_header), offset, &bounds) || + ADD_OVERFLOW(bounds, len, &bounds) || + bounds > query_db.size) + return TEE_ERROR_CORRUPT_OBJECT; + + memcpy(data, query_db.data->data + slv_info->data_offset + + entry->offset, len); + result->len = len; + + return TEE_SUCCESS; +} + +static TEE_Result +cmd_db_get_entry_by_res_id(const char *res_id, + struct cmd_db_query_result_type *result, + uint8_t *data) +{ + struct cmd_db_query_info query_info = { }; + uint64_t res_id_val; + uint16_t slv_idx; + struct entry_header entry; + TEE_Result res; + + if (!res_id || !result || (!data && result->len > 0)) + return TEE_ERROR_BAD_PARAMETERS; + + if (!query_db.data) + return TEE_ERROR_BAD_STATE; + + if (!res_id[0]) + return TEE_ERROR_BAD_PARAMETERS; + + res_id_val = conv_str_to_uint64(res_id); + query_info.type = CMD_DB_QUERY_RES_ID; + strlcpy(query_info.res_id, res_id, sizeof(query_info.res_id)); + + res = search_entry(&query_info, res_id_val, &slv_idx, &entry); + if (res) + return res; + + strlcpy(result->res_id, res_id, sizeof(result->res_id)); + result->addr = entry.addr; + result->priority[0] = entry.priority[0]; + result->priority[1] = entry.priority[1]; + result->version = query_db.data->slv_id_info[slv_idx].version; + + if (result->len == 0 || entry.len == 0) { + if (entry.len > 0) + result->len = entry.len; + return TEE_SUCCESS; + } + + return copy_aux_data(slv_idx, &entry, result, data); +} + +static TEE_Result +cmd_db_get_entry_by_addr(uint32_t addr, struct cmd_db_query_result_type *result, + uint8_t *data) +{ + struct cmd_db_query_info query_info = { }; + uint16_t slv_idx; + struct entry_header entry; + uint32_t slv_id; + TEE_Result res; + + if (!result || (!data && result->len > 0)) + return TEE_ERROR_BAD_PARAMETERS; + + if (!query_db.data) + return TEE_ERROR_BAD_STATE; + + slv_id = ADDR_SLV_ID(addr); + if (!is_valid_slv_id(slv_id)) + return TEE_ERROR_BAD_PARAMETERS; + + query_info.type = CMD_DB_QUERY_ADDRESS; + query_info.addr = addr; + query_info.slv_id = slv_id; + + res = search_entry(&query_info, 0, &slv_idx, &entry); + if (res) + return res; + + result->addr = entry.addr; + result->priority[0] = entry.priority[0]; + result->priority[1] = entry.priority[1]; + result->version = query_db.data->slv_id_info[slv_idx].version; + + if (result->len == 0 || entry.len == 0) { + if (entry.len > 0) + result->len = entry.len; + return TEE_SUCCESS; + } + + return copy_aux_data(slv_idx, &entry, result, data); +} + +TEE_Result cmd_db_query_addr(const char *res_id, uint32_t *addr) +{ + struct cmd_db_query_result_type result = { }; + TEE_Result res; + + if (!addr || !is_valid_res_id(res_id)) + return TEE_ERROR_BAD_PARAMETERS; + + mutex_lock(&query_db.lock); + + if (!query_db.data) { + mutex_unlock(&query_db.lock); + return TEE_ERROR_BAD_STATE; + } + + res = cmd_db_get_entry_by_res_id(res_id, &result, NULL); + if (res == TEE_SUCCESS) + *addr = result.addr; + + mutex_unlock(&query_db.lock); + return res; +} + +static uint32_t extract_priority(struct cmd_db_query_result_type *result, + uint8_t drv_id) +{ + if (drv_id <= CMD_DB_PRIORITY_LOW_DRV_MAX) + return (result->priority[0] >> + (drv_id * PRIORITY_BITS_PER_DRV)) & PRIORITY_MASK; + + if (drv_id <= CMD_DB_PRIORITY_HIGH_DRV_MAX) + return (result->priority[1] >> + ((drv_id - CMD_DB_PRIORITY_HIGH_DRV_OFFSET) * + PRIORITY_BITS_PER_DRV)) & PRIORITY_MASK; + + return 0; +} + +TEE_Result cmd_db_query_priority(uint32_t addr, uint8_t drv_id, + uint32_t *priority) +{ + struct cmd_db_query_result_type result = { }; + TEE_Result res; + + if (!priority || drv_id > MAX_DRV_ID) + return TEE_ERROR_BAD_PARAMETERS; + + mutex_lock(&query_db.lock); + + if (!query_db.data) { + mutex_unlock(&query_db.lock); + return TEE_ERROR_BAD_STATE; + } + + res = cmd_db_get_entry_by_addr(addr, &result, NULL); + if (res == TEE_SUCCESS) + *priority = extract_priority(&result, drv_id); + + mutex_unlock(&query_db.lock); + return res; +} + +early_init(cmd_db_init); diff --git a/core/drivers/qcom/cmd_db/cmd_db_target_config.h b/core/drivers/qcom/cmd_db/cmd_db_target_config.h new file mode 100644 index 0000000000..65575728ed --- /dev/null +++ b/core/drivers/qcom/cmd_db/cmd_db_target_config.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __CMD_DB_TARGET_CONFIG_H__ +#define __CMD_DB_TARGET_CONFIG_H__ + +#include +#include +#include + +#define AOP_MSG_RAM_END (AOP_MSG_RAM_BASE + AOP_MSG_RAM_SIZE) + +#define CMD_DB_METADATA_OFFSET 0x10000 +#define CMD_DB_ADDR_FIELD_OFFSET 0xC + +#define AOP_METADATA_ADDR (AOP_MSG_RAM_END - CMD_DB_METADATA_OFFSET + \ + CMD_DB_ADDR_FIELD_OFFSET) + +struct cmd_db_target_config { + paddr_t aop_metadata_addr; + size_t aop_metadata_size; +}; + +struct aop_cmd_db_metadata { + uint32_t addr; + uint32_t size; +}; + +static inline struct cmd_db_target_config cmd_db_get_target_config(void) +{ + struct cmd_db_target_config cfg = { + .aop_metadata_addr = AOP_METADATA_ADDR, + .aop_metadata_size = sizeof(struct aop_cmd_db_metadata), + }; + return cfg; +} + +#endif /* __CMD_DB_TARGET_CONFIG_H__ */ diff --git a/core/drivers/qcom/cmd_db/sub.mk b/core/drivers/qcom/cmd_db/sub.mk new file mode 100644 index 0000000000..43b9065e00 --- /dev/null +++ b/core/drivers/qcom/cmd_db/sub.mk @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# + +srcs-y += cmd_db.c diff --git a/core/drivers/qcom/sub.mk b/core/drivers/qcom/sub.mk index 369b68d58b..e9717e3145 100644 --- a/core/drivers/qcom/sub.mk +++ b/core/drivers/qcom/sub.mk @@ -6,3 +6,4 @@ srcs-$(CFG_QCOM_RAMBLUR_PIMEM_V3) += ramblur/ramblur_pimem_v3.c srcs-$(CFG_QCOM_PRNG) += prng/prng.c +subdirs-$(CFG_QCOM_CMD_DB) += cmd_db diff --git a/core/include/drivers/qcom/cmd_db/cmd_db.h b/core/include/drivers/qcom/cmd_db/cmd_db.h new file mode 100644 index 0000000000..e9bb1d4264 --- /dev/null +++ b/core/include/drivers/qcom/cmd_db/cmd_db.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __CMD_DB_H__ +#define __CMD_DB_H__ + +#include +#include + +#define CMD_DB_MAX_RES_ID_LEN 8 +#define CMD_DB_DRV_ID_PRIORITY_SZ 2 + +/* Query resource address by resource ID */ +TEE_Result cmd_db_query_addr(const char *res_id, uint32_t *addr); + +/* Query resource priority for driver ID */ +TEE_Result cmd_db_query_priority(uint32_t addr, uint8_t drv_id, + uint32_t *priority); + +#endif /* __CMD_DB_H__ */ From 7f1c17299ddae8d81d0f738e8e845dc663242095 Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Fri, 20 Feb 2026 12:52:15 +0530 Subject: [PATCH 02/14] drivers: qcom: rpmh: add RPMH client driver Add Resource Power Manager Hardened (RPMH) client driver for Qualcomm platforms. RPMH provides a communication interface to the Always-On Processor for power management operations. It uses Trigger Command Sets (TCS) to send resource state requests (e.g., voltage levels, clock states) to the AOP, which then programs the hardware accordingly. The driver implements: - Client handle management for Secure driver ID - Active set command submission (sleep/wake not needed for Secure DRV) - TCS hardware sequencing and completion barriers - Resource command tracking and caching - Hardware abstraction layer for register access This driver is essential for controlling voltage rails during QFPROM fuse provisioning, which requires precise voltage levels (0.95V on MX rail) to safely program one-time-programmable fuses. Signed-off-by: Selvam Sathappan Periakaruppan --- core/drivers/qcom/rpmh/rpmh_client.c | 371 +++++++++++++++ core/drivers/qcom/rpmh/rpmh_drv_config.c | 40 ++ core/drivers/qcom/rpmh/rpmh_drv_config.h | 42 ++ core/drivers/qcom/rpmh/rpmh_hal.c | 284 +++++++++++ core/drivers/qcom/rpmh/rpmh_hal.h | 88 ++++ .../qcom/rpmh/rpmh_resource_commands.c | 183 ++++++++ .../qcom/rpmh/rpmh_resource_commands.h | 71 +++ core/drivers/qcom/rpmh/rpmh_target_config.h | 17 + core/drivers/qcom/rpmh/rpmh_tcs.c | 440 ++++++++++++++++++ core/drivers/qcom/rpmh/rpmh_tcs.h | 93 ++++ core/drivers/qcom/rpmh/sub.mk | 21 + core/drivers/qcom/sub.mk | 3 + core/include/drivers/qcom/rpmh/rpmh_client.h | 60 +++ 13 files changed, 1713 insertions(+) create mode 100644 core/drivers/qcom/rpmh/rpmh_client.c create mode 100644 core/drivers/qcom/rpmh/rpmh_drv_config.c create mode 100644 core/drivers/qcom/rpmh/rpmh_drv_config.h create mode 100644 core/drivers/qcom/rpmh/rpmh_hal.c create mode 100644 core/drivers/qcom/rpmh/rpmh_hal.h create mode 100644 core/drivers/qcom/rpmh/rpmh_resource_commands.c create mode 100644 core/drivers/qcom/rpmh/rpmh_resource_commands.h create mode 100644 core/drivers/qcom/rpmh/rpmh_target_config.h create mode 100644 core/drivers/qcom/rpmh/rpmh_tcs.c create mode 100644 core/drivers/qcom/rpmh/rpmh_tcs.h create mode 100644 core/drivers/qcom/rpmh/sub.mk create mode 100644 core/include/drivers/qcom/rpmh/rpmh_client.h diff --git a/core/drivers/qcom/rpmh/rpmh_client.c b/core/drivers/qcom/rpmh/rpmh_client.c new file mode 100644 index 0000000000..e2d3f62bdb --- /dev/null +++ b/core/drivers/qcom/rpmh/rpmh_client.c @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rpmh_hal.h" +#include "rpmh_resource_commands.h" +#include "rpmh_tcs.h" + +register_phys_mem_pgdir(MEM_AREA_IO_NSEC, AOP_MSG_RAM_BASE, + CORE_MMU_PGDIR_SIZE); +register_phys_mem_pgdir(MEM_AREA_IO_SEC, RPMH_BASE_ADDR, + CORE_MMU_PGDIR_SIZE); + +struct rpmh_driver_data { + vaddr_t rsc_base; + struct client_queue *queue; + struct mutex lock; /* Protects driver state and client operations */ +}; + +struct client { + enum rscsw_drv_mapping drv_id; + const char *name; + uint32_t req_id; + uint32_t oldest_req_id; + uint32_t num_reqs_in_progress; + struct explicit_cmd_set *cmds; + SLIST_ENTRY(client) link; +}; + +struct explicit_cmd_set { + uint32_t in_use; + uint32_t total; + struct cmd_set_internal *sets; +}; + +struct cmd_set_internal { + enum rpmh_set_enum set; + uint32_t count; + uint32_t dependency_bmsk; + struct rpmh_command commands[RPMH_MAX_TCS_SIZE]; +}; + +SLIST_HEAD(client_list, client); + +struct client_queue { + struct client_list handles; +}; + +#define AOP_BOOT_COOKIE 0xA0C00C1E +#define MSG_RAM_SECTION_SIZE 0x10000 + +struct aop_msg_ram_dict { + uint32_t boot_cookie_offset; + uint32_t sleep_stats_offset; + uint32_t reserved_addrs[14]; +}; + +static struct rpmh_driver_data driver_state = { + .lock = MUTEX_INITIALIZER, +}; + +static bool tcs_is_idle(enum rscsw_drv_mapping drv_id, uint32_t timeout_us) +{ + uint64_t timer = timeout_init_us(timeout_us); + + while (!rpmh_tcs_is_amc_free(drv_id)) { + if (timeout_elapsed(timer)) + return false; + udelay(1); + } + + return true; +} + +static uint32_t issue_cmd_set_internal(struct client *client, + struct cmd_set_internal *set) +{ + struct rpmh_resource_command temp_rc; + struct rpmh_resource_command *rc; + uint32_t i, req_id, enable_mask; + uint32_t addr, data; + bool completion; + bool dirty = false; + + for (i = 0; i < set->count; i++) { + addr = set->commands[i].address; + data = set->commands[i].data; + completion = set->commands[i].completion; + + rc = rpmh_find_resource_command(addr); + if (!rc) { + rpmh_resource_command_init(&temp_rc, addr); + rc = &temp_rc; + } + + if (rpmh_resource_command_update(rc, set->set, data, + completion, client->drv_id, + false)) + dirty = true; + } + + if (!dirty || set->set != RPMH_SET_ACTIVE) + return 0; + + if (!tcs_is_idle(client->drv_id, 1000)) { + EMSG("TCS idle timeout for drv %u", client->drv_id); + return 0; + } + + req_id = ++client->req_id; + client->num_reqs_in_progress++; + + for (i = 0; i < set->count; i++) { + if (hal_rpmh_write_cmd(client->drv_id, 0, i, + set->commands[i].address, + set->commands[i].data, + set->commands[i].completion) != + HAL_STATUS_SUCCESS) { + EMSG("Failed to write cmd %u addr 0x%x", i, + set->commands[i].address); + return 0; + } + } + + enable_mask = GENMASK_32(set->count - 1, 0); + if (hal_rpmh_send_tcs(client->drv_id, 0, enable_mask) != + HAL_STATUS_SUCCESS) { + EMSG("Failed to send TCS for drv %u", client->drv_id); + return 0; + } + + return req_id; +} + +static struct client *find_client(enum rscsw_drv_mapping drv_id, + const char *name) +{ + struct client *client; + + SLIST_FOREACH(client, &driver_state.queue->handles, link) { + if (client->drv_id == drv_id && !strcmp(client->name, name)) + return client; + } + + return NULL; +} + +static struct client *create_client(enum rscsw_drv_mapping drv_id, + const char *name, bool explicit) +{ + struct client *client = find_client(drv_id, name); + + if (client) + return client; + + client = calloc(1, sizeof(struct client)); + if (!client) + return NULL; + + client->drv_id = drv_id; + client->name = name; + client->req_id = 0; + client->oldest_req_id = 0; + client->num_reqs_in_progress = 0; + + if (explicit) { + client->cmds = calloc(1, sizeof(struct explicit_cmd_set)); + if (!client->cmds) { + free(client); + return NULL; + } + } + + SLIST_INSERT_HEAD(&driver_state.queue->handles, client, link); + return client; +} + +static bool wait_for_cmd(struct client *client, uint32_t req_id, bool all) +{ + uint64_t timer = timeout_init_us(10000); + bool timed_out = false; + + if (req_id == 0) + return true; + + while (true) { + if (all ? client->num_reqs_in_progress == 0 : + (hal_rpmh_is_tcs_idle(client->drv_id, 0, &(bool){false}) == + HAL_STATUS_SUCCESS)) + break; + + if (timeout_elapsed(timer)) { + timed_out = true; + break; + } + + udelay(1); + } + + if (!timed_out && client->num_reqs_in_progress > 0) + client->num_reqs_in_progress--; + + return !timed_out; +} + +static TEE_Result check_aop_init(bool *ready) +{ + vaddr_t base, dict_addr, cookie_addr; + struct aop_msg_ram_dict *dict; + uint64_t timer = timeout_init_us(100000); + uint32_t cookie = 0; + + if (!ready) + return TEE_ERROR_BAD_PARAMETERS; + + *ready = false; + base = (vaddr_t)phys_to_virt(AOP_MSG_RAM_BASE, MEM_AREA_IO_NSEC, + AOP_MSG_RAM_SIZE); + if (!base) { + EMSG("Failed to get VA for AOP message RAM at PA 0x%lx", + (unsigned long)AOP_MSG_RAM_BASE); + return TEE_ERROR_GENERIC; + } + + dict_addr = base + AOP_MSG_RAM_SIZE - MSG_RAM_SECTION_SIZE; + dict = (struct aop_msg_ram_dict *)dict_addr; + cookie_addr = base + dict->boot_cookie_offset; + + while (cookie != AOP_BOOT_COOKIE) { + cookie = io_read32(cookie_addr); + if (cookie == AOP_BOOT_COOKIE) + break; + + if (timeout_elapsed(timer)) { + EMSG("AOP boot timeout after 100ms"); + return TEE_ERROR_BUSY; + } + + udelay(1); + } + + *ready = true; + return TEE_SUCCESS; +} + +static TEE_Result rpmh_client_init(void) +{ + TEE_Result res; + bool ready = false; + + res = check_aop_init(&ready); + if (res != TEE_SUCCESS) { + EMSG("AOP initialization check failed"); + goto err_panic; + } + + driver_state.rsc_base = (vaddr_t)phys_to_virt(RPMH_BASE_ADDR, + MEM_AREA_IO_SEC, + RPMH_RSC_SIZE); + if (!driver_state.rsc_base) { + EMSG("Failed to get VA for RSC base at PA 0x%lx", + (unsigned long)RPMH_BASE_ADDR); + goto err_panic; + } + + driver_state.queue = calloc(1, sizeof(struct client_queue)); + if (!driver_state.queue) { + EMSG("Failed to allocate client queue"); + goto err_panic; + } + + SLIST_INIT(&driver_state.queue->handles); + + if (hal_rpmh_init(driver_state.rsc_base) != HAL_STATUS_SUCCESS || + rpmh_tcs_init() != TEE_SUCCESS) { + EMSG("Failed to initialize HAL/TCS"); + goto err_panic; + } + + return TEE_SUCCESS; + +err_panic: + panic("RPMH driver initialization failed"); +} + +struct rpmh_client *rpmh_create_handle(enum rscsw_drv_mapping drv_id, + const char *name) +{ + struct rpmh_client *handle; + + if (!name || !rpmh_is_drv_id_valid(drv_id)) + return NULL; + + mutex_lock(&driver_state.lock); + handle = (struct rpmh_client *)create_client(drv_id, name, false); + mutex_unlock(&driver_state.lock); + + return handle; +} + +TEE_Result rpmh_issue_command(struct rpmh_client *handle, + enum rpmh_set_enum set, bool completion, + uint32_t address, uint32_t data, + uint32_t *req_id) +{ + struct client *client = (struct client *)handle; + struct cmd_set_internal cmd_set = { }; + uint32_t id; + TEE_Result res = TEE_SUCCESS; + + if (!client || !req_id) + return TEE_ERROR_BAD_PARAMETERS; + + *req_id = 0; + mutex_lock(&driver_state.lock); + + cmd_set.commands[0].address = address; + cmd_set.commands[0].data = data; + cmd_set.commands[0].completion = completion; + cmd_set.set = set; + cmd_set.count = 1; + cmd_set.dependency_bmsk = 0; + + id = issue_cmd_set_internal(client, &cmd_set); + + if (id == 0 || !wait_for_cmd(client, id, false)) { + res = (id == 0) ? TEE_ERROR_GENERIC : TEE_ERROR_BUSY; + goto out; + } + + *req_id = id; + +out: + mutex_unlock(&driver_state.lock); + return res; +} + +void rpmh_barrier_single(struct rpmh_client *handle, uint32_t req_id) +{ + struct client *client = (struct client *)handle; + + if (!client) + return; + + wait_for_cmd(client, req_id, false); +} + +bool rpmh_is_drv_id_valid(enum rscsw_drv_mapping drv_id) +{ + return (drv_id == RSC_DRV_SECURE); +} + +early_init(rpmh_client_init); diff --git a/core/drivers/qcom/rpmh/rpmh_drv_config.c b/core/drivers/qcom/rpmh/rpmh_drv_config.c new file mode 100644 index 0000000000..0b1f7076a5 --- /dev/null +++ b/core/drivers/qcom/rpmh/rpmh_drv_config.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include + +#include "rpmh_drv_config.h" +#include "rpmh_target_config.h" + +static const struct tcs_config tcs_config_tz = { + .amcs = RPMH_TCS_ACTIVE, + .sleep_start = RPMH_TCS_ACTIVE, + .wake_start = RPMH_TCS_ACTIVE + RPMH_TCS_SLEEP, +}; + +static const struct drv_config_data optee_config_data = { + .drvs_count = 1, + .init_clks_count = 0, + .init_clks = NULL, + .sleep_clks_count = 0, + .sleep_clks = NULL, + + .drvs = (struct drv_config[]) { + { + .drv_id = RSC_DRV_SECURE, + .hw_drv = RSC_DRV_SECURE, + .wake_set_latency = 0x7080, + .tcs_offset = 0, + .tcs = RPMH_TCS_ACTIVE + RPMH_TCS_SLEEP + RPMH_TCS_WAKE, + .cmds = RPMH_MAX_CMDS_PER_TCS, + .modes_count = 1, + .modes = (const struct tcs_config *[]) { + &tcs_config_tz, + } + } + } +}; + +const struct drv_config_data *const g_drv_config_data = &optee_config_data; diff --git a/core/drivers/qcom/rpmh/rpmh_drv_config.h b/core/drivers/qcom/rpmh/rpmh_drv_config.h new file mode 100644 index 0000000000..f01714e956 --- /dev/null +++ b/core/drivers/qcom/rpmh/rpmh_drv_config.h @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __RPMH_DRV_CONFIG_H__ +#define __RPMH_DRV_CONFIG_H__ + +#include +#include + +/* RPMH DRV Configuration */ + +struct tcs_config { + uint32_t amcs; /* Number of AMCs (active TCS) */ + uint32_t sleep_start; /* Index where sleep TCS start */ + uint32_t wake_start; /* Index where wake TCS start */ +}; + +struct drv_config { + enum rscsw_drv_mapping drv_id; + enum rscsw_drv_mapping hw_drv; + uint32_t wake_set_latency; + uint32_t tcs_offset; + uint32_t tcs; + uint32_t cmds; + uint32_t modes_count; + const struct tcs_config **modes; +}; + +struct drv_config_data { + uint32_t drvs_count; + uint32_t init_clks_count; + char **init_clks; + uint32_t sleep_clks_count; + char **sleep_clks; + struct drv_config *drvs; +}; + +extern const struct drv_config_data *const g_drv_config_data; + +#endif /* __RPMH_DRV_CONFIG_H__ */ diff --git a/core/drivers/qcom/rpmh/rpmh_hal.c b/core/drivers/qcom/rpmh/rpmh_hal.c new file mode 100644 index 0000000000..e3d31aed7f --- /dev/null +++ b/core/drivers/qcom/rpmh/rpmh_hal.c @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include + +#include "rpmh_hal.h" + +static struct { + vaddr_t rsc_base; + vaddr_t drv_base[RSC_DRV_MAX]; +} rsc_base_addrs; + +static inline vaddr_t get_drv_base(enum rscsw_drv_mapping drv_id) +{ + return (drv_id >= RSC_DRV_MAX) ? 0 : rsc_base_addrs.drv_base[drv_id]; +} + +static inline vaddr_t get_tcs_base(vaddr_t base, uint32_t tcs_id) +{ + return base + TCS_BASE_OFFSET + (tcs_id * TCS_STRIDE); +} + +static inline uint32_t hal_read32(vaddr_t addr) +{ + return io_read32(addr); +} + +static inline void hal_write32(vaddr_t addr, uint32_t val) +{ + io_write32(addr, val); +} + +enum hal_status hal_rpmh_init(vaddr_t rsc_base) +{ + if (!rsc_base) + return HAL_STATUS_INVALID_PARAM; + + rsc_base_addrs.rsc_base = rsc_base; + + return HAL_STATUS_SUCCESS; +} + +enum hal_status hal_rpmh_register_drv(enum rscsw_drv_mapping drv_id) +{ + if (drv_id >= RSC_DRV_MAX) + return HAL_STATUS_INVALID_PARAM; + + rsc_base_addrs.drv_base[drv_id] = rsc_base_addrs.rsc_base + + (drv_id * DRV_STRIDE); + + return HAL_STATUS_SUCCESS; +} + +enum hal_status hal_rpmh_read_config(enum rscsw_drv_mapping drv_id, + uint32_t *tcs, uint32_t *cmds) +{ + vaddr_t base; + uint32_t config; + + if (!tcs || !cmds) + return HAL_STATUS_INVALID_PARAM; + + base = get_drv_base(drv_id); + if (!base) + return HAL_STATUS_INVALID_PARAM; + + config = hal_read32(base + RSC_DRV_TCS_CONFIG); + *tcs = (config >> 0) & 0xFF; + *cmds = (config >> 8) & 0xFF; + + return HAL_STATUS_SUCCESS; +} + +enum hal_status hal_rpmh_convert_to_amc(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id) +{ + vaddr_t base, tcs_base; + uint32_t control; + + base = get_drv_base(drv_id); + if (!base) + return HAL_STATUS_INVALID_PARAM; + + tcs_base = get_tcs_base(base, tcs_id); + control = hal_read32(tcs_base + TCS_CONTROL_OFFSET); + control |= TCS_CONTROL_AMC_MODE_EN; + hal_write32(tcs_base + TCS_CONTROL_OFFSET, control); + + return HAL_STATUS_SUCCESS; +} + +enum hal_status hal_rpmh_convert_to_tcs(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id) +{ + vaddr_t base, tcs_base; + uint32_t control; + + base = get_drv_base(drv_id); + if (!base) + return HAL_STATUS_INVALID_PARAM; + + tcs_base = get_tcs_base(base, tcs_id); + control = hal_read32(tcs_base + TCS_CONTROL_OFFSET); + control &= ~TCS_CONTROL_AMC_MODE_EN; + hal_write32(tcs_base + TCS_CONTROL_OFFSET, control); + + return HAL_STATUS_SUCCESS; +} + +enum hal_status hal_rpmh_enable_amc_status(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id) +{ + vaddr_t base; + uint32_t enable; + + base = get_drv_base(drv_id); + if (!base) + return HAL_STATUS_INVALID_PARAM; + + enable = hal_read32(base + RSC_DRV_IRQ_ENABLE); + enable |= BIT(tcs_id); + hal_write32(base + RSC_DRV_IRQ_ENABLE, enable); + + return HAL_STATUS_SUCCESS; +} + +enum hal_status hal_rpmh_clear_amc_status(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id) +{ + vaddr_t base; + + base = get_drv_base(drv_id); + if (!base) + return HAL_STATUS_INVALID_PARAM; + + hal_write32(base + RSC_DRV_IRQ_CLEAR, BIT(tcs_id)); + + return HAL_STATUS_SUCCESS; +} + +enum hal_status hal_rpmh_is_tcs_idle(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id, bool *idle) +{ + vaddr_t base, tcs_base; + uint32_t status; + + if (!idle) + return HAL_STATUS_INVALID_PARAM; + + base = get_drv_base(drv_id); + if (!base) + return HAL_STATUS_INVALID_PARAM; + + tcs_base = get_tcs_base(base, tcs_id); + status = hal_read32(tcs_base + TCS_STATUS_OFFSET); + *idle = (status & TCS_STATUS_CONTROLLER_IDLE) != 0; + + return HAL_STATUS_SUCCESS; +} + +enum hal_status hal_rpmh_get_amc_status(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id, + bool *finished) +{ + vaddr_t base; + uint32_t status; + + if (!finished) + return HAL_STATUS_INVALID_PARAM; + + base = get_drv_base(drv_id); + if (!base) + return HAL_STATUS_INVALID_PARAM; + + status = hal_read32(base + RSC_DRV_IRQ_STATUS); + *finished = (status & BIT(tcs_id)) != 0; + + return HAL_STATUS_SUCCESS; +} + +enum hal_status hal_rpmh_send_tcs(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id, + uint32_t enable_mask) +{ + vaddr_t base, tcs_base; + uint32_t control; + + base = get_drv_base(drv_id); + if (!base) + return HAL_STATUS_INVALID_PARAM; + + tcs_base = get_tcs_base(base, tcs_id); + hal_write32(tcs_base + TCS_CMD_ENABLE_OFFSET, enable_mask); + control = hal_read32(tcs_base + TCS_CONTROL_OFFSET); + control |= TCS_CONTROL_AMC_MODE_EN; + control &= ~TCS_CONTROL_AMC_MODE_TRIGGER; + hal_write32(tcs_base + TCS_CONTROL_OFFSET, control); + control |= TCS_CONTROL_AMC_MODE_TRIGGER; + hal_write32(tcs_base + TCS_CONTROL_OFFSET, control); + + return HAL_STATUS_SUCCESS; +} + +enum hal_status hal_rpmh_write_cmd(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id, uint32_t cmd_idx, + uint32_t addr, uint32_t data, + bool completion) +{ + vaddr_t base, tcs_base, cmd_base; + uint32_t msgid, addr_reg; + uint32_t slave_id, offset; + + base = get_drv_base(drv_id); + if (!base) + return HAL_STATUS_INVALID_PARAM; + + tcs_base = get_tcs_base(base, tcs_id); + cmd_base = tcs_base + TCS_CMD_BASE_OFFSET + (cmd_idx * TCS_CMD_STRIDE); + + msgid = 0; + msgid |= (0 << MSGID_READ_OR_WRITE_SHIFT); + msgid |= (completion ? 1 : 0) << MSGID_RES_REQ_SHIFT; + msgid |= (1 << MSGID_MSG_LENGTH_SHIFT); + + slave_id = (addr >> 16) & 0x7; + offset = addr & 0xFFFF; + addr_reg = (slave_id << ADDR_SLV_ID_SHIFT) | + (offset << ADDR_OFFSET_SHIFT); + + hal_write32(cmd_base + TCS_CMDn_MSGID_OFFSET, msgid); + hal_write32(cmd_base + TCS_CMDn_ADDR_OFFSET, addr_reg); + hal_write32(cmd_base + TCS_CMDn_DATA_OFFSET, data); + + return HAL_STATUS_SUCCESS; +} + +enum hal_status hal_rpmh_update_epcb_timeout(enum rscsw_drv_mapping drv_id, + uint32_t threshold) +{ + vaddr_t base; + uint32_t val; + + if (drv_id != RSC_DRV_SECURE) + return HAL_STATUS_INVALID_PARAM; + + base = get_drv_base(drv_id); + if (!base) + return HAL_STATUS_INVALID_PARAM; + + val = hal_read32(base + RSC_DRV_ERROR_IRQ_ENABLE); + val &= ~EPCB_TIMEOUT_THRESHOLD_MASK; + val |= (threshold & EPCB_TIMEOUT_THRESHOLD_MASK) << + EPCB_TIMEOUT_THRESHOLD_SHIFT; + hal_write32(base + RSC_DRV_ERROR_IRQ_ENABLE, val); + + return HAL_STATUS_SUCCESS; +} + +enum hal_status hal_rpmh_toggle_epcb_timeout(enum rscsw_drv_mapping drv_id, + bool enable) +{ + vaddr_t base; + uint32_t val; + + if (drv_id != RSC_DRV_SECURE) + return HAL_STATUS_INVALID_PARAM; + + base = get_drv_base(drv_id); + if (!base) + return HAL_STATUS_INVALID_PARAM; + + val = hal_read32(base + RSC_DRV_ERROR_IRQ_ENABLE); + if (enable) + val |= EPCB_TIMEOUT_IRQ_EN_MASK; + else + val &= ~EPCB_TIMEOUT_IRQ_EN_MASK; + hal_write32(base + RSC_DRV_ERROR_IRQ_ENABLE, val); + + return HAL_STATUS_SUCCESS; +} diff --git a/core/drivers/qcom/rpmh/rpmh_hal.h b/core/drivers/qcom/rpmh/rpmh_hal.h new file mode 100644 index 0000000000..7acbad2ce8 --- /dev/null +++ b/core/drivers/qcom/rpmh/rpmh_hal.h @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __RPMH_HAL_H__ +#define __RPMH_HAL_H__ + +#include +#include +#include +#include + +enum hal_status { + HAL_STATUS_SUCCESS = 0, + HAL_STATUS_ERROR = 1, + HAL_STATUS_INVALID_PARAM = 2, +}; + +#define DRV_STRIDE 0x10000 +#define RSC_DRV_IRQ_ENABLE 0x0d00 +#define RSC_DRV_IRQ_STATUS 0x0d04 +#define RSC_DRV_IRQ_CLEAR 0x0d08 + +#define RSC_DRV_TCS_CONFIG 0x0C +#define TCS_BASE_OFFSET 0x0D10 /* CMD_WAIT_FOR_CMPL base */ +#define TCS_STRIDE 0x2A0 + +#define TCS_CONTROL_OFFSET 0x04 +#define TCS_STATUS_OFFSET 0x08 +#define TCS_CMD_ENABLE_OFFSET 0x0C + +#define TCS_CMD_BASE_OFFSET 0x20 +#define TCS_CMDn_MSGID_OFFSET 0x00 +#define TCS_CMDn_ADDR_OFFSET 0x04 +#define TCS_CMDn_DATA_OFFSET 0x08 +#define TCS_CMD_STRIDE 0x14 + +#define TCS_CONTROL_AMC_MODE_TRIGGER BIT(24) +#define TCS_CONTROL_AMC_MODE_EN BIT(16) + +#define TCS_STATUS_CONTROLLER_IDLE BIT(0) + +#define RSC_DRV_ERROR_IRQ_STATUS 0xD0 +#define RSC_DRV_ERROR_IRQ_ENABLE 0xD8 +#define RSC_DRV_ERROR_IRQ_CLEAR 0xD4 + +#define EPCB_TIMEOUT_IRQ_EN_MASK BIT(20) +#define EPCB_TIMEOUT_THRESHOLD_SHIFT 0x0 +#define EPCB_TIMEOUT_THRESHOLD_MASK 0xFFFF + +#define MSGID_READ_OR_WRITE_SHIFT 0x10 +#define MSGID_RES_REQ_SHIFT 0x8 +#define MSGID_MSG_LENGTH_SHIFT 0x0 + +#define ADDR_SLV_ID_SHIFT 0x10 +#define ADDR_OFFSET_SHIFT 0x0 + +enum hal_status hal_rpmh_init(vaddr_t rsc_base); +enum hal_status hal_rpmh_register_drv(enum rscsw_drv_mapping drv_id); +enum hal_status hal_rpmh_read_config(enum rscsw_drv_mapping drv_id, + uint32_t *tcs, uint32_t *cmds); +enum hal_status hal_rpmh_convert_to_amc(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id); +enum hal_status hal_rpmh_convert_to_tcs(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id); +enum hal_status hal_rpmh_enable_amc_status(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id); +enum hal_status hal_rpmh_clear_amc_status(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id); +enum hal_status hal_rpmh_is_tcs_idle(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id, bool *idle); +enum hal_status hal_rpmh_get_amc_status(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id, + bool *finished); +enum hal_status hal_rpmh_send_tcs(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id, + uint32_t enable_mask); +enum hal_status hal_rpmh_write_cmd(enum rscsw_drv_mapping drv_id, + uint32_t tcs_id, uint32_t cmd_idx, + uint32_t addr, uint32_t data, + bool completion); +enum hal_status hal_rpmh_update_epcb_timeout(enum rscsw_drv_mapping drv_id, + uint32_t threshold); +enum hal_status hal_rpmh_toggle_epcb_timeout(enum rscsw_drv_mapping drv_id, + bool enable); + +#endif /* __RPMH_HAL_H__ */ diff --git a/core/drivers/qcom/rpmh/rpmh_resource_commands.c b/core/drivers/qcom/rpmh/rpmh_resource_commands.c new file mode 100644 index 0000000000..dafc39dc88 --- /dev/null +++ b/core/drivers/qcom/rpmh/rpmh_resource_commands.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include + +#include "rpmh_drv_config.h" +#include "rpmh_resource_commands.h" +#include "rpmh_tcs.h" + +static struct rpmh_resource_command *resources; +static uint32_t resources_count; + +static void +reconcile_vote_state(struct rpmh_resource_command __maybe_unused *rc, + struct drv_votes *drv, bool completion) +{ + struct rpmh_vote *sleep; + struct rpmh_vote *active; + struct rpmh_vote *wake; + + if (!drv) + return; + + sleep = &drv->local_votes[RPMH_SET_SLEEP]; + active = &drv->local_votes[RPMH_SET_ACTIVE]; + wake = &drv->local_votes[RPMH_SET_WAKE]; + + if (active->valid && + (!drv->vote_at_rpmh.valid || + active->data != drv->vote_at_rpmh.data || + (completion && !active->completion))) { + active->dirty = RPMH_RC_DIRTY; + } else { + active->dirty = RPMH_RC_CLEAN; + } + + if (sleep->dirty != RPMH_RC_CLEAN_EXPLICIT_VOTE) { + if (sleep->valid && + (!active->valid || + (active->valid && active->data != sleep->data))) { + sleep->dirty = RPMH_RC_DIRTY; + } else { + sleep->dirty = RPMH_RC_CLEAN; + } + } + + if (wake->dirty != RPMH_RC_CLEAN_EXPLICIT_VOTE) { + if (wake->valid && + ((active->valid && wake->data != active->data) || + (sleep->valid && wake->data != sleep->data))) { + wake->dirty = RPMH_RC_DIRTY; + } else if (!wake->valid && + (active->valid && sleep->valid) && + active->data != sleep->data && + sleep->dirty != RPMH_RC_CLEAN_EXPLICIT_VOTE) { + wake->dirty = RPMH_RC_DIRTY_USE_ACTIVE; + } else { + wake->dirty = RPMH_RC_CLEAN; + } + } +} + +void rpmh_resource_command_init(struct rpmh_resource_command *rc, + uint32_t address) +{ + if (!rc) + return; + + memset(rc, 0, sizeof(*rc)); + rc->address = address; +} + +static struct drv_votes * +rpmh_resource_command_get_voter(struct rpmh_resource_command *rc, + enum rscsw_drv_mapping drv_id) +{ + struct drv_votes *drv = rc->drv_votes; + struct drv_votes *prev = NULL; + + if (!rc) + return NULL; + + while (drv) { + if (drv->drv_id == drv_id) + break; + prev = drv; + drv = drv->next; + } + + if (!drv) { + uint32_t drv_index; + const struct drv_config *config; + uint32_t priority = 0; + TEE_Result res; + + drv = calloc(1, sizeof(struct drv_votes)); + if (!drv) + return NULL; + + drv->drv_id = drv_id; + + if (rpmh_tcs_find_drv_index(drv_id, &drv_index) != + TEE_SUCCESS) { + free(drv); + return NULL; + } + + config = &g_drv_config_data->drvs[drv_index]; + + res = cmd_db_query_priority(rc->address, + (uint8_t)config->hw_drv, + &priority); + if (res != TEE_SUCCESS) { + EMSG("Failed to query priority for addr 0x%x", + rc->address); + free(drv); + return NULL; + } + + drv->priority = (enum rpmh_rc_priority)priority; + + if (!rc->drv_votes) + rc->drv_votes = drv; + else if (prev) + prev->next = drv; + else + return NULL; + } + + return drv; +} + +struct rpmh_resource_command *rpmh_find_resource_command(uint32_t address) +{ + uint32_t low = 0, high = resources_count - 1; + + if (resources_count == 0) + return NULL; + + while (high >= low) { + uint32_t mid = (low + high) / 2; + uint32_t addr = resources[mid].address; + + if (high >= resources_count) + break; + + if (addr < address) + low = mid + 1; + else if (addr > address) + high = mid - 1; + else + return &resources[mid]; + } + + return NULL; +} + +bool rpmh_resource_command_update(struct rpmh_resource_command *rc, + enum rpmh_set_enum set, uint32_t data, + bool completion, + enum rscsw_drv_mapping drv_id, + bool explicit_cmd) +{ + struct drv_votes *drv = rpmh_resource_command_get_voter(rc, drv_id); + + drv->local_votes[set].data = data; + drv->local_votes[set].valid = true; + + if (explicit_cmd && set != RPMH_SET_ACTIVE) + drv->local_votes[set].dirty = RPMH_RC_CLEAN_EXPLICIT_VOTE; + + reconcile_vote_state(rc, drv, + completion && set == RPMH_SET_ACTIVE); + + drv->local_votes[set].completion = completion; + + return (drv->local_votes[set].dirty > RPMH_RC_LAST_CLEAN); +} diff --git a/core/drivers/qcom/rpmh/rpmh_resource_commands.h b/core/drivers/qcom/rpmh/rpmh_resource_commands.h new file mode 100644 index 0000000000..c808befdc8 --- /dev/null +++ b/core/drivers/qcom/rpmh/rpmh_resource_commands.h @@ -0,0 +1,71 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __RPMH_RESOURCE_COMMANDS_H__ +#define __RPMH_RESOURCE_COMMANDS_H__ + +#include +#include +#include +#include + +enum rpmh_rc_state { + RPMH_RC_STATE_IDLE = 0, + RPMH_RC_STATE_IN_PROGRESS, + RPMH_RC_STATE_MAX +}; + +enum rpmh_rc_dirty { + RPMH_RC_CLEAN = 0, + RPMH_RC_CLEAN_EXPLICIT_VOTE = 1, + RPMH_RC_LAST_CLEAN = 1, + RPMH_RC_DIRTY = 2, + RPMH_RC_DIRTY_MOL = 3, + RPMH_RC_DIRTY_USE_ACTIVE = 4, + RPMH_RC_DIRTY_MAX +}; + +struct rpmh_vote { + uint32_t data; + bool valid; + bool completion; + enum rpmh_rc_dirty dirty; +}; + +enum rpmh_rc_priority { + RPMH_RC_PRIORITY_USE_CASE = 0, + RPMH_RC_PRIORITY_SUBSYSTEM, + RPMH_RC_PRIORITY_MAX +}; + +struct drv_votes { + enum rscsw_drv_mapping drv_id; + enum rpmh_rc_state state; + enum rpmh_rc_priority priority; + struct rpmh_vote vote_at_rpmh; + struct rpmh_vote local_votes[RPMH_NUM_SETS]; + struct drv_votes *next; +}; + +struct rpmh_resource_command { + uint32_t address; + struct drv_votes *drv_votes; +}; + +/* Initialize resource command */ +void rpmh_resource_command_init(struct rpmh_resource_command *rc, + uint32_t address); + +/* Update resource command vote, returns true if set is dirty */ +bool rpmh_resource_command_update(struct rpmh_resource_command *rc, + enum rpmh_set_enum set, + uint32_t data, bool completion, + enum rscsw_drv_mapping drv_id, + bool explicit_cmd); + +/* Find resource command by address */ +struct rpmh_resource_command *rpmh_find_resource_command(uint32_t address); + +#endif /* __RPMH_RESOURCE_COMMANDS_H__ */ diff --git a/core/drivers/qcom/rpmh/rpmh_target_config.h b/core/drivers/qcom/rpmh/rpmh_target_config.h new file mode 100644 index 0000000000..bc9d97a731 --- /dev/null +++ b/core/drivers/qcom/rpmh/rpmh_target_config.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __RPMH_TARGET_CONFIG_H__ +#define __RPMH_TARGET_CONFIG_H__ + +#include + +#define RPMH_TCS_ACTIVE 2 +#define RPMH_TCS_SLEEP 1 +#define RPMH_TCS_WAKE 1 + +#define RPMH_MAX_CMDS_PER_TCS 16 + +#endif /* __RPMH_TARGET_CONFIG_H__ */ diff --git a/core/drivers/qcom/rpmh/rpmh_tcs.c b/core/drivers/qcom/rpmh/rpmh_tcs.c new file mode 100644 index 0000000000..3cff44c8f8 --- /dev/null +++ b/core/drivers/qcom/rpmh/rpmh_tcs.c @@ -0,0 +1,440 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include + +#include "rpmh_drv_config.h" +#include "rpmh_hal.h" +#include "rpmh_tcs.h" + +struct tcs_driver_state { + struct tcs **tcs; + uint32_t *mode; +}; + +static struct tcs_driver_state tcs_driver_state; + +static uint64_t get_timestamp(void) +{ + TEE_Time time; + + tee_time_get_sys_time(&time); + return (uint64_t)time.seconds * 1000000 + time.millis * 1000; +} + +bool rpmh_tcs_drv_valid(enum rscsw_drv_mapping drv_id) +{ + uint32_t i; + + for (i = 0; i < g_drv_config_data->drvs_count; i++) { + if (g_drv_config_data->drvs[i].drv_id == drv_id) + return true; + } + + return false; +} + +TEE_Result rpmh_tcs_find_drv_index(enum rscsw_drv_mapping drv_id, + uint32_t *drv_index) +{ + uint32_t i; + + if (!drv_index) + return TEE_ERROR_BAD_PARAMETERS; + + for (i = 0; i < g_drv_config_data->drvs_count; i++) { + if (g_drv_config_data->drvs[i].drv_id == drv_id) { + *drv_index = i; + return TEE_SUCCESS; + } + } + + return TEE_ERROR_ITEM_NOT_FOUND; +} + +TEE_Result rpmh_tcs_size(enum rscsw_drv_mapping drv_id, uint32_t *size) +{ + uint32_t drv_index; + TEE_Result res; + const struct drv_config *drv; + + if (!size) + return TEE_ERROR_BAD_PARAMETERS; + + res = rpmh_tcs_find_drv_index(drv_id, &drv_index); + if (res) + return res; + + drv = &g_drv_config_data->drvs[drv_index]; + *size = drv->cmds; + + return TEE_SUCCESS; +} + +static void update_mode(uint32_t drv_index, uint32_t mode) +{ + const struct drv_config *drv = &g_drv_config_data->drvs[drv_index]; + uint32_t i; + + if (mode >= drv->modes_count) + mode = 0; + + tcs_driver_state.mode[drv_index] = mode; + + for (i = 0; i < drv->tcs; i++) { + struct tcs *tcs = &tcs_driver_state.tcs[drv_index][i]; + + tcs->id = i + drv->tcs_offset; + tcs->time_last_sent = 0; + hal_rpmh_clear_amc_status(drv->hw_drv, tcs->id); + + if (i < drv->modes[mode]->amcs) { + tcs->state = TCS_AMC_IDLE; + hal_rpmh_convert_to_amc(drv->hw_drv, tcs->id); + } else { + tcs->state = TCS_NON_AMC; + hal_rpmh_convert_to_tcs(drv->hw_drv, tcs->id); + } + } +} + +TEE_Result rpmh_tcs_init(void) +{ + uint32_t i, j, tcs, cmds; + enum hal_status status; + TEE_Result res = TEE_ERROR_OUT_OF_MEMORY; + + tcs_driver_state.tcs = calloc(g_drv_config_data->drvs_count, + sizeof(struct tcs *)); + if (!tcs_driver_state.tcs) + return TEE_ERROR_OUT_OF_MEMORY; + + tcs_driver_state.mode = calloc(g_drv_config_data->drvs_count, + sizeof(uint32_t)); + if (!tcs_driver_state.mode) + goto error_free_tcs; + + for (i = 0; i < g_drv_config_data->drvs_count; i++) { + const struct drv_config *drv = &g_drv_config_data->drvs[i]; + + tcs_driver_state.tcs[i] = calloc(drv->tcs, sizeof(struct tcs)); + if (!tcs_driver_state.tcs[i]) + goto error_free_tcs_array; + + tcs_driver_state.mode[i] = 0; + + if (drv->drv_id >= RSC_DRV_VIRTUAL_DRVS) { + update_mode(i, 0); + continue; + } + + status = hal_rpmh_register_drv(drv->drv_id); + if (status != HAL_STATUS_SUCCESS) + goto error_free_tcs_array_i; + + status = hal_rpmh_read_config(drv->drv_id, &tcs, &cmds); + if (status != HAL_STATUS_SUCCESS) + goto error_free_tcs_array_i; + + hal_rpmh_update_epcb_timeout(drv->drv_id, 0xFFFF); + hal_rpmh_toggle_epcb_timeout(drv->drv_id, true); + update_mode(i, 0); + } + + return TEE_SUCCESS; + +error_free_tcs_array_i: + res = TEE_ERROR_GENERIC; + free(tcs_driver_state.tcs[i]); +error_free_tcs_array: + for (j = 0; j < i; j++) + free(tcs_driver_state.tcs[j]); + free(tcs_driver_state.mode); +error_free_tcs: + free(tcs_driver_state.tcs); + return res; +} + +bool rpmh_tcs_is_amc_free(enum rscsw_drv_mapping drv_id) +{ + uint32_t drv_index; + const struct drv_config *drv; + uint32_t amcs, i; + + if (rpmh_tcs_find_drv_index(drv_id, &drv_index)) + return false; + + drv = &g_drv_config_data->drvs[drv_index]; + amcs = drv->modes[tcs_driver_state.mode[drv_index]]->amcs; + + for (i = 0; i < amcs; i++) { + if (tcs_driver_state.tcs[drv_index][i].state == TCS_AMC_IDLE) + return true; + } + + return false; +} + +bool rpmh_tcs_is_amc_finished(enum rscsw_drv_mapping drv_id) +{ + uint32_t drv_index; + const struct drv_config *drv; + uint32_t amcs, i; + bool finished; + enum hal_status status; + + if (rpmh_tcs_find_drv_index(drv_id, &drv_index)) + return false; + + drv = &g_drv_config_data->drvs[drv_index]; + amcs = drv->modes[tcs_driver_state.mode[drv_index]]->amcs; + + for (i = 0; i < amcs; i++) { + struct tcs *tcs = &tcs_driver_state.tcs[drv_index][i]; + + if (tcs->state != TCS_AMC_WAIT_FOR_DONE) + continue; + + status = hal_rpmh_get_amc_status(drv->hw_drv, + tcs->id, + &finished); + if (status == HAL_STATUS_SUCCESS && finished) + return true; + } + + return false; +} + +TEE_Result rpmh_tcs_send(struct rpmh_cmd *cmd, enum rscsw_drv_mapping drv_id) +{ + uint32_t drv_index; + const struct drv_config *drv; + uint32_t amcs, i, j; + struct tcs *tcs = NULL; + uint32_t enable_mask = 0; + enum hal_status status; + TEE_Result res; + + if (!cmd) + return TEE_ERROR_BAD_PARAMETERS; + + res = rpmh_tcs_find_drv_index(drv_id, &drv_index); + if (res) + return res; + + drv = &g_drv_config_data->drvs[drv_index]; + amcs = drv->modes[tcs_driver_state.mode[drv_index]]->amcs; + + for (i = 0; i < amcs; i++) { + tcs = &tcs_driver_state.tcs[drv_index][i]; + if (tcs->state == TCS_AMC_IDLE) + break; + } + + if (i == amcs) + return TEE_ERROR_BUSY; + + for (j = 0; j < cmd->num_rcs; j++) { + status = hal_rpmh_write_cmd(drv->hw_drv, tcs->id, j, + cmd->details[j].address, + cmd->details[j].data, + cmd->details[j].completion); + if (status != HAL_STATUS_SUCCESS) + return TEE_ERROR_GENERIC; + enable_mask |= BIT(j); + } + + hal_rpmh_clear_amc_status(drv->hw_drv, tcs->id); + status = hal_rpmh_enable_amc_status(drv->hw_drv, tcs->id); + if (status != HAL_STATUS_SUCCESS) + return TEE_ERROR_GENERIC; + + tcs->state = TCS_AMC_WAIT_FOR_DONE; + tcs->time_last_sent = get_timestamp(); + tcs->cmds = cmd; + + status = hal_rpmh_send_tcs(drv->hw_drv, tcs->id, enable_mask); + if (status != HAL_STATUS_SUCCESS) { + tcs->state = TCS_AMC_IDLE; + return TEE_ERROR_GENERIC; + } + + return TEE_SUCCESS; +} + +TEE_Result rpmh_tcs_finish_active_amc(struct rpmh_client **client, + struct rpmh_cmdq *cmdq, + enum rscsw_drv_mapping drv_id, + uint32_t *req_id) +{ + uint32_t drv_index; + const struct drv_config *drv; + uint32_t amcs; + uint32_t i; + bool finished; + enum hal_status status; + TEE_Result res; + + if (!req_id) + return TEE_ERROR_BAD_PARAMETERS; + + *req_id = 0; + + res = rpmh_tcs_find_drv_index(drv_id, &drv_index); + if (res) + return res; + + drv = &g_drv_config_data->drvs[drv_index]; + amcs = drv->modes[tcs_driver_state.mode[drv_index]]->amcs; + + for (i = 0; i < amcs; i++) { + struct tcs *tcs = &tcs_driver_state.tcs[drv_index][i]; + + if (tcs->state != TCS_AMC_WAIT_FOR_DONE) + continue; + + status = hal_rpmh_get_amc_status(drv->hw_drv, + tcs->id, + &finished); + if (status != HAL_STATUS_SUCCESS || !finished) + continue; + + hal_rpmh_clear_amc_status(drv->hw_drv, tcs->id); + + if (tcs->cmds) + *req_id = tcs->cmds->req_id; + + if (client && cmdq && tcs->cmds) + *client = NULL; + + tcs->state = TCS_AMC_IDLE; + tcs->cmds = NULL; + + return TEE_SUCCESS; + } + + return TEE_ERROR_ITEM_NOT_FOUND; +} + +TEE_Result rpmh_tcs_slots_available(enum rscsw_drv_mapping drv_id, + enum rpmh_set_enum set, + uint32_t slots, + uint32_t *tcs_index) +{ + uint32_t drv_index; + const struct drv_config *drv; + const struct tcs_config *mode; + uint32_t start, end, i; + struct tcs *tcs; + TEE_Result res; + + if (!tcs_index) + return TEE_ERROR_BAD_PARAMETERS; + + res = rpmh_tcs_find_drv_index(drv_id, &drv_index); + if (res) + return res; + + drv = &g_drv_config_data->drvs[drv_index]; + mode = drv->modes[tcs_driver_state.mode[drv_index]]; + + if (slots > drv->cmds) + return TEE_ERROR_BAD_PARAMETERS; + + switch (set) { + case RPMH_SET_SLEEP: + start = mode->sleep_start; + end = mode->wake_start; + break; + case RPMH_SET_WAKE: + start = mode->wake_start; + end = drv->tcs; + break; + default: + return TEE_ERROR_BAD_PARAMETERS; + } + + for (i = start; i < end; i++) { + tcs = &tcs_driver_state.tcs[drv_index][i]; + + if (!tcs->cmds) { + *tcs_index = i; + return TEE_SUCCESS; + } + + if (slots <= (drv->cmds - tcs->cmds->num_rcs)) { + *tcs_index = i; + return TEE_SUCCESS; + } + } + + return TEE_ERROR_ITEM_NOT_FOUND; +} + +bool rpmh_tcs_is_stuck(enum rscsw_drv_mapping drv_id) +{ + uint32_t drv_index; + const struct drv_config *drv; + uint32_t amcs, i; + uint64_t current_time; + bool is_idle; + enum hal_status status; + + if (rpmh_tcs_find_drv_index(drv_id, &drv_index)) + return false; + + drv = &g_drv_config_data->drvs[drv_index]; + amcs = drv->modes[tcs_driver_state.mode[drv_index]]->amcs; + current_time = get_timestamp(); + + for (i = 0; i < amcs; i++) { + struct tcs *tcs = &tcs_driver_state.tcs[drv_index][i]; + + status = hal_rpmh_is_tcs_idle(drv->hw_drv, tcs->id, &is_idle); + if (status != HAL_STATUS_SUCCESS) + continue; + + if (is_idle || !tcs->time_last_sent) + continue; + + if (current_time > (tcs->time_last_sent + + TCS_TIMEOUT_THRESHOLD)) + return true; + } + + return false; +} + +TEE_Result rpmh_tcs_get_finished_drv(enum rscsw_drv_mapping hw_drv, + enum rscsw_drv_mapping *finished_drv) +{ + const struct drv_config *drv = g_drv_config_data->drvs; + uint32_t i; + + if (!finished_drv) + return TEE_ERROR_BAD_PARAMETERS; + + if (hw_drv < RSC_DRV_VIRTUAL_DRVS) { + *finished_drv = hw_drv; + return TEE_SUCCESS; + } + + for (i = 0; i < g_drv_config_data->drvs_count; i++, drv++) { + if (drv->hw_drv != hw_drv) + continue; + + if (rpmh_tcs_is_amc_finished(drv->drv_id)) { + *finished_drv = drv->drv_id; + return TEE_SUCCESS; + } + } + + *finished_drv = RSC_DRV_VIRTUAL_MAX; + + return TEE_ERROR_ITEM_NOT_FOUND; +} diff --git a/core/drivers/qcom/rpmh/rpmh_tcs.h b/core/drivers/qcom/rpmh/rpmh_tcs.h new file mode 100644 index 0000000000..a19fd67968 --- /dev/null +++ b/core/drivers/qcom/rpmh/rpmh_tcs.h @@ -0,0 +1,93 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __RPMH_TCS_H__ +#define __RPMH_TCS_H__ + +#include +#include +#include + +#define TCS_TIMEOUT_THRESHOLD 0x2EE00 + +#define RPMH_SLAVE_ID(address) ((address) >> 16) +#define RPMH_OFFSET(address) ((address) & 0xFFFF) + +struct rpmh_cmd_details { + uint32_t address; + uint32_t data; + bool completion; +}; + +struct rpmh_cmd { + struct rpmh_cmd_details *details; + uint32_t num_rcs; + uint32_t dependency_bmsk; + struct rpmh_client *client; + uint32_t req_id; + enum rpmh_set_enum set; + bool amc; + struct rpmh_cmd *next; +}; + +enum tcs_state { + TCS_AMC_IDLE = 0, + TCS_AMC_WAIT_FOR_DONE = 1, + TCS_NON_AMC = 2, + TCS_NUM_STATES = 3 +}; + +struct tcs { + uint8_t id; + enum tcs_state state; + uint64_t time_last_sent; + struct rpmh_cmd *cmds; +}; + +struct rpmh_client; +struct rpmh_cmdq; + +/* Initialize TCS management */ +TEE_Result rpmh_tcs_init(void); + +/* Check if driver ID is valid */ +bool rpmh_tcs_drv_valid(enum rscsw_drv_mapping drv_id); + +/* Get driver configuration index */ +TEE_Result rpmh_tcs_find_drv_index(enum rscsw_drv_mapping drv_id, + uint32_t *drv_index); + +/* Get number of commands per TCS */ +TEE_Result rpmh_tcs_size(enum rscsw_drv_mapping drv_id, uint32_t *size); + +/* Check if AMC is available */ +bool rpmh_tcs_is_amc_free(enum rscsw_drv_mapping drv_id); + +/* Send command via AMC */ +TEE_Result rpmh_tcs_send(struct rpmh_cmd *cmd, enum rscsw_drv_mapping drv_id); + +/* Check if AMC finished */ +bool rpmh_tcs_is_amc_finished(enum rscsw_drv_mapping drv_id); + +/* Clean up finished AMC */ +TEE_Result rpmh_tcs_finish_active_amc(struct rpmh_client **client, + struct rpmh_cmdq *cmdq, + enum rscsw_drv_mapping drv_id, + uint32_t *req_id); + +/* Check TCS slot availability */ +TEE_Result rpmh_tcs_slots_available(enum rscsw_drv_mapping drv_id, + enum rpmh_set_enum set, + uint32_t slots, + uint32_t *tcs_index); + +/* Check if TCS is stuck */ +bool rpmh_tcs_is_stuck(enum rscsw_drv_mapping drv_id); + +/* Get finished driver ID */ +TEE_Result rpmh_tcs_get_finished_drv(enum rscsw_drv_mapping hw_drv, + enum rscsw_drv_mapping *finished_drv); + +#endif /* __RPMH_TCS_H__ */ diff --git a/core/drivers/qcom/rpmh/sub.mk b/core/drivers/qcom/rpmh/sub.mk new file mode 100644 index 0000000000..5152d9c0ca --- /dev/null +++ b/core/drivers/qcom/rpmh/sub.mk @@ -0,0 +1,21 @@ +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# + +# RPMH (Resource Power Manager Hardened) Driver + +# Core RPMH client API +srcs-y += rpmh_client.c + +# RPMH hardware abstraction layer +srcs-y += rpmh_hal.c + +# TCS (Task Control Structure) management +srcs-y += rpmh_tcs.c + +# Resource command tracking and management +srcs-y += rpmh_resource_commands.c + +# DRV configuration +srcs-y += rpmh_drv_config.c diff --git a/core/drivers/qcom/sub.mk b/core/drivers/qcom/sub.mk index e9717e3145..87a7676888 100644 --- a/core/drivers/qcom/sub.mk +++ b/core/drivers/qcom/sub.mk @@ -6,4 +6,7 @@ srcs-$(CFG_QCOM_RAMBLUR_PIMEM_V3) += ramblur/ramblur_pimem_v3.c srcs-$(CFG_QCOM_PRNG) += prng/prng.c + +$(eval $(call cfg-depends-all,CFG_QCOM_RPMH_CLIENT,CFG_QCOM_CMD_DB)) subdirs-$(CFG_QCOM_CMD_DB) += cmd_db +subdirs-$(CFG_QCOM_RPMH_CLIENT) += rpmh diff --git a/core/include/drivers/qcom/rpmh/rpmh_client.h b/core/include/drivers/qcom/rpmh/rpmh_client.h new file mode 100644 index 0000000000..a275dab1c1 --- /dev/null +++ b/core/include/drivers/qcom/rpmh/rpmh_client.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __RPMH_CLIENT_H__ +#define __RPMH_CLIENT_H__ + +#include +#include +#include + +#define RPMH_MAX_TCS_SIZE 16 + +enum rpmh_set_enum { + RPMH_SET_ACTIVE = 0, + RPMH_SET_SLEEP = 1, + RPMH_SET_WAKE = 2, + RPMH_NUM_SETS = 3, +}; + +enum rscsw_drv_mapping { + RSC_DRV_SECURE = 0, + RSC_DRV_CPUCP = 1, + RSC_DRV_L3 = RSC_DRV_CPUCP, + RSC_DRV_HLOS = 2, + RSC_DRV_HYP = 3, + RSC_DRV_MAX, + + /* Virtual DRVs */ + RSC_DRV_VIRTUAL_DRVS = 0x3FFFFF00, + RSC_DRV_VIRTUAL_SENSORS, + RSC_DRV_VIRTUAL_MAX = 0x3FFFFFFF, +}; + +struct rpmh_command { + uint32_t address; + uint32_t data; + bool completion; +}; + +struct rpmh_client; + +/* Create RPMH client handle */ +struct rpmh_client *rpmh_create_handle(enum rscsw_drv_mapping drv_id, + const char *name); + +/* Issue command to RPMH resource */ +TEE_Result rpmh_issue_command(struct rpmh_client *handle, + enum rpmh_set_enum set, + bool completion, uint32_t address, + uint32_t data, uint32_t *req_id); + +/* Wait for command completion */ +void rpmh_barrier_single(struct rpmh_client *handle, uint32_t req_id); + +/* Check if driver ID is valid */ +bool rpmh_is_drv_id_valid(enum rscsw_drv_mapping drv_id); + +#endif /* __RPMH_CLIENT_H__ */ From fe2ed34eef387561f1c81e54f62c1efc582fbf53 Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Tue, 28 Apr 2026 00:03:32 +0530 Subject: [PATCH 03/14] drivers: qcom: qfprom: add Qualcomm QFPROM OTP driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for Qualcomm QFPROM (Fuse Programmable Read-Only Memory), a one-time programmable memory used for storing security credentials and device configuration. QFPROM stores critical security data including: - Root of trust public key hashes for secure boot - Hardware key derivation keys (primary and secondary) - Anti-rollback version counters - OEM configuration and feature flags - Device-specific encryption keys The driver provides: - Read operations from corrected (FEC) and raw address spaces - Write operations with hardware sequencing and timing control - FEC (Forward Error Correction) calculation for protected regions - Hardware mutex synchronization for multi-core safety - Voltage and clock control via RPMH for safe fuse programming - Region-based access control and permission checking Fuse programming requires precise timing (5µs hold time) and voltage (0.95V) to ensure reliable one-time programming without hardware damage. This implementation also introduces a generic clock rate setting API (qcom_clock_set_rate) that allows drivers like QFPROM to directly manage their clocks without going through the clock group abstraction. The QFPROM driver uses this API directly for its clock configuration needs. Signed-off-by: Selvam Sathappan Periakaruppan --- core/drivers/clk/qcom/clock-qcom.c | 20 +- .../qcom/platform/kodiak/clock_group_qcom.h | 5 +- core/drivers/clk/qcom/sub.mk | 2 +- core/drivers/qcom/qfprom/qfprom_core.c | 558 ++++++++++++++++++ core/drivers/qcom/qfprom/qfprom_hal.c | 151 +++++ core/drivers/qcom/qfprom/qfprom_hal.h | 28 + core/drivers/qcom/qfprom/qfprom_priv.h | 83 +++ core/drivers/qcom/qfprom/qfprom_target.c | 153 +++++ core/drivers/qcom/qfprom/sub.mk | 8 + core/drivers/qcom/sub.mk | 2 + core/include/drivers/clk_qcom.h | 8 + core/include/drivers/qcom/qfprom/qfprom.h | 91 +++ 12 files changed, 1105 insertions(+), 4 deletions(-) create mode 100644 core/drivers/qcom/qfprom/qfprom_core.c create mode 100644 core/drivers/qcom/qfprom/qfprom_hal.c create mode 100644 core/drivers/qcom/qfprom/qfprom_hal.h create mode 100644 core/drivers/qcom/qfprom/qfprom_priv.h create mode 100644 core/drivers/qcom/qfprom/qfprom_target.c create mode 100644 core/drivers/qcom/qfprom/sub.mk create mode 100644 core/include/drivers/qcom/qfprom/qfprom.h diff --git a/core/drivers/clk/qcom/clock-qcom.c b/core/drivers/clk/qcom/clock-qcom.c index 5969afcdeb..83fd63bcd1 100644 --- a/core/drivers/clk/qcom/clock-qcom.c +++ b/core/drivers/clk/qcom/clock-qcom.c @@ -34,6 +34,24 @@ TEE_Result qcom_clock_enable_cbc(vaddr_t cbcr) return TEE_SUCCESS; } +TEE_Result qcom_clock_set_rate(vaddr_t cfg_rcgr, vaddr_t cmd_rcgr, + uint32_t cfg_value) +{ + uint64_t timer = 0; + + io_write32(cfg_rcgr, cfg_value); + io_write32(cmd_rcgr, CMD_RCGR_UPDATE_BIT); + + timer = timeout_init_us(10 * 1000); + while (io_read32(cmd_rcgr) & CMD_RCGR_UPDATE_BIT) { + if (timeout_elapsed(timer)) + return TEE_ERROR_TIMEOUT; + udelay(1); + } + + return TEE_SUCCESS; +} + TEE_Result qcom_clock_enable(enum qcom_clk_group group) { switch (group) { @@ -45,6 +63,4 @@ TEE_Result qcom_clock_enable(enum qcom_clk_group group) EMSG("Unsupported clock group %d\n", group); return TEE_ERROR_BAD_PARAMETERS; } - - return TEE_SUCCESS; } diff --git a/core/drivers/clk/qcom/platform/kodiak/clock_group_qcom.h b/core/drivers/clk/qcom/platform/kodiak/clock_group_qcom.h index 7a4a930a7e..8f93ee74c8 100644 --- a/core/drivers/clk/qcom/platform/kodiak/clock_group_qcom.h +++ b/core/drivers/clk/qcom/platform/kodiak/clock_group_qcom.h @@ -5,6 +5,10 @@ #ifndef _CLOCK_GROUP_QCOM_H_ #define _CLOCK_GROUP_QCOM_H_ +#define GCC_SEC_CTRL_CFG_RCGR 0x3d030 +#define GCC_SEC_CTRL_CMD_RCGR 0x3d02c +#define QFPROM_CLOCK_DIVIDE 0x7 + #define GCC_WPSS_AHB_CLK 0x9d154 #define GCC_WPSS_AHB_BDG_MST_CLK 0x9d158 #define GCC_WPSS_RSCP_CLK 0x9d16c @@ -34,4 +38,3 @@ #define TOP_CC_AGGNOC_MPU_LS_CLK 0x7000 #endif /* _CLOCK_GROUP_QCOM_H_ */ - diff --git a/core/drivers/clk/qcom/sub.mk b/core/drivers/clk/qcom/sub.mk index f7b59c9a48..5e8b39c304 100644 --- a/core/drivers/clk/qcom/sub.mk +++ b/core/drivers/clk/qcom/sub.mk @@ -1,5 +1,5 @@ global-incdirs-y += . +global-incdirs-y += platform/$(PLATFORM_FLAVOR) srcs-y += clock-qcom.c clock-qcom-pas.c incdirs-y += . -incdirs-$(CFG_QCOM_PAS_PTA) += platform/$(PLATFORM_FLAVOR) diff --git a/core/drivers/qcom/qfprom/qfprom_core.c b/core/drivers/qcom/qfprom/qfprom_core.c new file mode 100644 index 0000000000..4d26a209bd --- /dev/null +++ b/core/drivers/qcom/qfprom/qfprom_core.c @@ -0,0 +1,558 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qfprom_hal.h" +#include "qfprom_priv.h" +#include "qfprom_target.h" + +register_phys_mem_pgdir(MEM_AREA_IO_SEC, TCSR_MUTEX_BASE, CORE_MMU_PGDIR_SIZE); +register_phys_mem_pgdir(MEM_AREA_IO_SEC, SECURITY_CONTROL_BASE, + CORE_MMU_PGDIR_SIZE); + +static struct qfprom_context ctx; + +struct qfprom_context *qfprom_get_context(void) +{ + return &ctx; +} + +static bool is_address_valid(uint32_t addr, + enum qfprom_addr_space type) +{ + struct qfprom_context *qfprom_ctx = qfprom_get_context(); + paddr_t base, end; + uint32_t lookup_addr; + + if (!qfprom_ctx || !qfprom_ctx->config || + !IS_ALIGNED_WITH_TYPE(addr, uint64_t)) + return false; + + lookup_addr = addr; + if (type == QFPROM_ADDR_SPACE_CORR) + lookup_addr = QFPROM_RAW_TO_CORR(addr); + + if (type == QFPROM_ADDR_SPACE_RAW) + base = qfprom_ctx->config->qfprom_raw_base; + else + base = qfprom_ctx->config->qfprom_corr_base; + + end = base + qfprom_ctx->config->qfprom_size; + if (lookup_addr < base || lookup_addr >= end) + return false; + + return true; +} + +static enum qfprom_error get_region_name(uint32_t addr, + enum qfprom_addr_space addr_type, + enum qfprom_region_name *region_type) +{ + struct qfprom_context *drv = qfprom_get_context(); + uint32_t region_start, region_end; + uint32_t lookup_addr; + size_t i; + + if (!region_type) + return QFPROM_DATA_PTR_NULL_ERR; + + if (!drv->config || !drv->config->region_data) + return QFPROM_ERR_UNKNOWN; + + if (!is_address_valid(addr, addr_type)) + return QFPROM_ADDRESS_INVALID_ERR; + + lookup_addr = (addr_type == QFPROM_ADDR_SPACE_CORR) ? + QFPROM_RAW_TO_CORR(addr) : addr; + + for (i = 0; i < drv->config->num_regions; i++) { + const struct qfprom_region_info *region = + &drv->config->region_data[i]; + uint32_t offset; + + if (addr_type == QFPROM_ADDR_SPACE_RAW) + region_start = region->raw_base_addr; + else if (addr_type == QFPROM_ADDR_SPACE_CORR) + region_start = region->corr_base_addr; + else + return QFPROM_ERR_UNKNOWN; + + region_end = region_start + (region->size * 8); + + if (lookup_addr < region_start) + continue; + + if (lookup_addr >= region_end) + continue; + + offset = lookup_addr - region_start; + if (offset % 8) + return QFPROM_ADDRESS_INVALID_ERR; + + if (offset / 8 >= region->size) + return QFPROM_ADDRESS_INVALID_ERR; + + *region_type = region->region_name; + return QFPROM_NO_ERR; + } + + return QFPROM_REGION_NOT_SUPPORTED_ERR; +} + +static int raw_read(uint32_t addr, + enum qfprom_addr_space type, + uint32_t *data) +{ + uint32_t ret; + + if (!data) + return QFPROM_DATA_PTR_NULL_ERR; + + if (type == QFPROM_ADDR_SPACE_RAW) { + ret = hal_qfprom_read_raw_address_row(addr, data); + } else { + addr = QFPROM_RAW_TO_CORR(addr); + ret = hal_qfprom_read_corrected_address_row(addr, data); + } + + return ret; +} + +static enum qfprom_error is_fec_enabled(enum qfprom_region_name region_type, + bool *fec_status) +{ + struct qfprom_context *drv = qfprom_get_context(); + const struct qfprom_region_info *info = NULL; + enum qfprom_error err; + paddr_t reg_addr; + uint32_t val, bit; + size_t i; + + if (!fec_status) + return QFPROM_DATA_PTR_NULL_ERR; + + if (!drv->config || !drv->config->region_data) + return QFPROM_ERR_UNKNOWN; + + for (i = 0; i < drv->config->num_regions; i++) { + if (drv->config->region_data[i].region_name == region_type) { + info = &drv->config->region_data[i]; + break; + } + } + + if (!info) + return QFPROM_REGION_NOT_SUPPORTED_ERR; + + if (info->fec_type == QFPROM_FEC_NONE) { + *fec_status = false; + return QFPROM_NO_ERR; + } + + if (info->fec_type != QFPROM_FEC_63_56 || + info->region_index >= QFPROM_FEC_REGION_MSB_MAX) + return QFPROM_ERR_UNKNOWN; + + reg_addr = QFPROM_RAW_TO_CORR(FEC_ENABLES_ADDR); + if (info->region_index >= QFPROM_FEC_REGION_LSB_MAX) { + reg_addr += 4; + bit = info->region_index - QFPROM_FEC_REGION_LSB_MAX; + } else { + bit = info->region_index; + } + + err = hal_qfprom_read_corrected_address(reg_addr, &val); + if (err != QFPROM_NO_ERR) { + EMSG("Failed to read FEC enable register at 0x%lx, error: %d", + reg_addr, err); + return err; + } + + *fec_status = (val & (1U << bit)) != 0; + + return QFPROM_NO_ERR; +} + +static bool check_region_access(enum qfprom_region_name region, + uint32_t perm_flags) +{ + struct qfprom_context *drv = qfprom_get_context(); + const struct qfprom_region_info *info = NULL; + paddr_t perm_addr; + uint32_t perm; + uint32_t offset; + size_t i; + + if (!drv->config || !drv->config->region_data) + return false; + + for (i = 0; i < drv->config->num_regions; i++) { + if (drv->config->region_data[i].region_name == region) { + info = &drv->config->region_data[i]; + break; + } + } + + if (!info) + return false; + + /* Check read permission */ + if (perm_flags & REGION_PERM_READ) { + offset = (info->perm_reg_type == QFPROM_ROW_LSB) ? + drv->config->read_perm_lsb_offset : + drv->config->read_perm_msb_offset; + + if (!offset) { + if (!info->read_allowed) + return false; + } else { + perm_addr = drv->config->qfprom_corr_base + offset; + if (hal_qfprom_read_corrected_address(perm_addr, + &perm) != + QFPROM_NO_ERR) + return false; + + if (perm & info->read_perm_mask) + return false; + } + } + + /* Check write permission */ + if (perm_flags & REGION_PERM_WRITE) { + offset = (info->perm_reg_type == QFPROM_ROW_LSB) ? + drv->config->write_perm_lsb_offset : + drv->config->write_perm_msb_offset; + + if (!offset) + return false; + + perm_addr = drv->config->qfprom_corr_base + offset; + if (hal_qfprom_read_corrected_address(perm_addr, + &perm) != + QFPROM_NO_ERR) + return false; + + if (perm & info->write_perm_mask) + return false; + } + + return true; +} + +static enum qfprom_error wait_blow_status_ready(void) +{ + uint64_t timer = timeout_init_us(QFPROM_BLOW_TIMEOUT_US); + uint32_t status; + enum qfprom_error err; + + while (true) { + err = hal_qfprom_read_blow_status(&status); + if (err != QFPROM_NO_ERR) + return err; + + if (status != QFPROM_BLOW_STATUS_BUSY_VAL) + break; + + if (timeout_elapsed(timer)) { + EMSG("QFPROM blow operation timed out"); + return QFPROM_ERROR_TIMEOUT; + } + + udelay(10); + } + + if (status != QFPROM_BLOW_STATUS_READY_VAL) + return QFPROM_WRITE_ERR; + + return QFPROM_NO_ERR; +} + +static enum qfprom_error raw_write(uint32_t addr, + const uint32_t *data) +{ + enum qfprom_error err; + enum qfprom_region_name region; + bool fec_enabled = false; + uint32_t verify[2]; + + if (!data) + return QFPROM_DATA_PTR_NULL_ERR; + + err = get_region_name(addr, QFPROM_ADDR_SPACE_RAW, ®ion); + if (err != QFPROM_NO_ERR) + return err; + + if (!check_region_access(region, + REGION_PERM_READ | REGION_PERM_WRITE)) + return QFPROM_REGION_NOT_WRITABLE_ERR; + + if (is_fec_enabled(region, &fec_enabled) == QFPROM_NO_ERR && + fec_enabled) + return QFPROM_REGION_NOT_WRITABLE_ERR; + + err = wait_blow_status_ready(); + if (err != QFPROM_NO_ERR) + return err; + + err = hal_qfprom_write_raw_address(addr, data[0]); + if (err != QFPROM_NO_ERR) + return err; + + err = wait_blow_status_ready(); + if (err != QFPROM_NO_ERR) + return err; + + err = hal_qfprom_write_raw_address(addr + 4, data[1]); + if (err != QFPROM_NO_ERR) + return err; + + err = wait_blow_status_ready(); + if (err != QFPROM_NO_ERR) + return err; + + if (!check_region_access(region, REGION_PERM_READ)) + return QFPROM_NO_ERR; + + err = raw_read(addr, QFPROM_ADDR_SPACE_RAW, verify); + if (err != QFPROM_NO_ERR) + return QFPROM_NO_ERR; + + if ((verify[0] & data[0]) != data[0] || + (verify[1] & data[1]) != data[1]) + return QFPROM_WRITE_ERR; + + return QFPROM_NO_ERR; +} + +TEE_Result qfprom_read_row(uint32_t addr, + enum qfprom_addr_space type, + uint32_t *data) +{ + enum qfprom_region_name region; + enum qfprom_error err; + + if (!data) + return TEE_ERROR_BAD_PARAMETERS; + + err = get_region_name(addr, type, ®ion); + if (err == QFPROM_ADDRESS_INVALID_ERR) + return TEE_ERROR_BAD_PARAMETERS; + else if (err != QFPROM_NO_ERR) + return TEE_ERROR_GENERIC; + + if (!check_region_access(region, REGION_PERM_READ)) + return TEE_ERROR_ACCESS_DENIED; + + err = raw_read(addr, type, data); + if (err != QFPROM_NO_ERR) { + EMSG("QFPROM read failed for address 0x%08x, error: %d", + addr, err); + return TEE_ERROR_GENERIC; + } + + if (type == QFPROM_ADDR_SPACE_CORR) { + bool fec_enabled = false; + + err = is_fec_enabled(region, &fec_enabled); + if (err == QFPROM_NO_ERR && fec_enabled && + hal_qfprom_is_fec_error_seen()) { + uint16_t err_addr = 0; + + hal_qfprom_read_error_address(&err_addr); + EMSG("FEC error: 0x%04x req 0x%08x", err_addr, addr); + hal_qfprom_clear_fec_error_status(); + return TEE_ERROR_CORRUPT_OBJECT; + } + } + + return TEE_SUCCESS; +} + +TEE_Result qfprom_hw_init(void) +{ + struct qfprom_context *drv = qfprom_get_context(); + TEE_Result res; + + res = qfprom_acquire_hw_mutex(); + if (res != TEE_SUCCESS) + return res; + + mutex_lock(&drv->lock); + + res = qfprom_enable_voltage(); + if (res != TEE_SUCCESS) + goto err_unlock; + + res = qfprom_write_set_clock_settings(); + if (res != TEE_SUCCESS) + goto err_disable_voltage; + + drv->write_op_allowed = true; + return TEE_SUCCESS; + +err_disable_voltage: + qfprom_disable_voltage(); +err_unlock: + mutex_unlock(&drv->lock); + qfprom_release_hw_mutex(); + return res; +} + +void qfprom_hw_deinit(void) +{ + struct qfprom_context *drv = qfprom_get_context(); + + drv->write_op_allowed = false; + qfprom_write_reset_clock_settings(); + qfprom_disable_voltage(); + mutex_unlock(&drv->lock); + qfprom_release_hw_mutex(); +} + +TEE_Result qfprom_write_row(uint32_t addr, uint32_t *data) +{ + enum qfprom_error err; + uint32_t write_data[2]; + + if (!data) + return TEE_ERROR_BAD_PARAMETERS; + + if (!IS_ENABLED(CFG_QFPROM_PROGRAMMING)) + return TEE_ERROR_NOT_SUPPORTED; + + write_data[0] = data[0]; + write_data[1] = data[1]; + + err = raw_write(addr, write_data); + if (err != QFPROM_NO_ERR) { + EMSG("QFPROM write failed for address 0x%08x, error: %d", + addr, err); + return TEE_ERROR_GENERIC; + } + + return TEE_SUCCESS; +} + +TEE_Result qfprom_row_has_fec_bits(uint32_t addr, + enum qfprom_addr_space type, + uint8_t *has_fec) +{ + struct qfprom_context *drv = qfprom_get_context(); + enum qfprom_region_name region_type; + enum qfprom_error err; + size_t i; + + if (!has_fec) + return TEE_ERROR_BAD_PARAMETERS; + + err = get_region_name(addr, type, ®ion_type); + if (err == QFPROM_ADDRESS_INVALID_ERR) + return TEE_ERROR_BAD_PARAMETERS; + else if (err != QFPROM_NO_ERR) + return TEE_ERROR_GENERIC; + + if (!drv->config || !drv->config->region_data) + return TEE_ERROR_GENERIC; + + for (i = 0; i < drv->config->num_regions; i++) { + const struct qfprom_region_info *region = + &drv->config->region_data[i]; + + if (region->region_name == region_type) { + if (check_region_access(region_type, REGION_PERM_READ)) + *has_fec = (region->fec_type != + QFPROM_FEC_NONE) ? 1 : 0; + else + *has_fec = 0; + + return TEE_SUCCESS; + } + } + + return TEE_ERROR_ITEM_NOT_FOUND; +} + +uint32_t qfprom_fec_63_56_bit(uint32_t lsb_data, uint32_t msb_data) +{ + uint8_t lfsr[7] = {0}; + int i = 0; + uint32_t temp = 0; + uint32_t fec_val = 0; + uint64_t data_loc = 0; + + data_loc = ((uint64_t)msb_data << 32) | lsb_data; + + for (i = 0; i < 56; i++) { + temp = lfsr[0] ^ ((data_loc >> i) & 0x1); + + lfsr[0] = lfsr[1] ^ temp; + lfsr[1] = lfsr[2]; + lfsr[2] = lfsr[3]; + lfsr[3] = lfsr[4]; + lfsr[4] = lfsr[5] ^ temp; + lfsr[5] = lfsr[6]; + lfsr[6] = temp; + } + + for (i = 6; i >= 0; i--) { + temp = (lfsr[i] << i); + fec_val = (fec_val | temp); + } + + return ((fec_val << 24) | msb_data); +} + +static TEE_Result qfprom_init(void) +{ + struct qfprom_context *drv = qfprom_get_context(); + const struct qfprom_platform_config *config; + + config = qfprom_get_platform_config(); + if (!config) { + EMSG("Failed to get platform configuration"); + goto err_panic; + } + + drv->config = config; + mutex_init(&drv->lock); + + drv->raw_base_va = (vaddr_t)phys_to_virt(SECURITY_CONTROL_BASE, + MEM_AREA_IO_SEC, + SECURITY_CONTROL_SIZE); + if (!drv->raw_base_va) { + EMSG("Failed to get VA for security control at PA 0x%lx", + (unsigned long)SECURITY_CONTROL_BASE); + goto err_panic; + } + + drv->mutex_reg_va = (vaddr_t)phys_to_virt(QFPROM_MUTEX_REG_ADDR, + MEM_AREA_IO_SEC, + sizeof(uint32_t)); + if (!drv->mutex_reg_va) { + EMSG("Failed to get VA for mutex register at PA 0x%lx", + (unsigned long)QFPROM_MUTEX_REG_ADDR); + goto err_panic; + } + + drv->corr_base_va = QFPROM_RAW_TO_CORR(drv->raw_base_va); + + return TEE_SUCCESS; + +err_panic: + panic("QFPROM driver initialization failed"); +} + +early_init_late(qfprom_init); diff --git a/core/drivers/qcom/qfprom/qfprom_hal.c b/core/drivers/qcom/qfprom/qfprom_hal.c new file mode 100644 index 0000000000..eccd213b2b --- /dev/null +++ b/core/drivers/qcom/qfprom/qfprom_hal.c @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include + +#include "qfprom_hal.h" +#include "qfprom_priv.h" +#include "qfprom_target.h" + +static void qfprom_write_reg(uint32_t offset, uint32_t value) +{ + struct qfprom_context *ctx = qfprom_get_context(); + vaddr_t reg = ctx->raw_base_va + offset; + + io_write32(reg, value); +} + +static uint32_t qfprom_read_reg(uint32_t offset) +{ + struct qfprom_context *ctx = qfprom_get_context(); + vaddr_t reg = ctx->raw_base_va + offset; + + return io_read32(reg); +} + +static enum qfprom_error hal_qfprom_read_address_generic(uint32_t addr, + uint32_t *value) +{ + vaddr_t vaddr; + + if (!value) + return QFPROM_DATA_PTR_NULL_ERR; + + if (!IS_ALIGNED_WITH_TYPE(addr, uint32_t)) + return QFPROM_ADDRESS_INVALID_ERR; + + vaddr = (vaddr_t)phys_to_virt(addr, MEM_AREA_IO_SEC, sizeof(uint32_t)); + if (!vaddr) + return QFPROM_ADDRESS_INVALID_ERR; + + *value = io_read32(vaddr); + + return QFPROM_NO_ERR; +} + +static enum qfprom_error hal_qfprom_read_row_generic(uint32_t addr, + uint32_t *value) +{ + enum qfprom_error ret; + + if (!value) + return QFPROM_DATA_PTR_NULL_ERR; + + ret = hal_qfprom_read_address_generic(addr, &value[0]); + if (ret != QFPROM_NO_ERR) + return ret; + + return hal_qfprom_read_address_generic(addr + 4, &value[1]); +} + +void hal_qfprom_set_blow_timer(uint32_t value) +{ + qfprom_write_reg(QFPROM_BLOW_TIMER_OFFSET, value); +} + +void hal_qfprom_set_accel(uint32_t value) +{ + qfprom_write_reg(QFPROM_ACCEL_OFFSET, value); +} + +enum qfprom_error hal_qfprom_read_raw_address(uint32_t addr, uint32_t *value) +{ + return hal_qfprom_read_address_generic(addr, value); +} + +enum qfprom_error hal_qfprom_read_raw_address_row(uint32_t addr, + uint32_t *value) +{ + return hal_qfprom_read_row_generic(addr, value); +} + +enum qfprom_error hal_qfprom_read_corrected_address(uint32_t addr, + uint32_t *value) +{ + return hal_qfprom_read_address_generic(addr, value); +} + +enum qfprom_error hal_qfprom_read_corrected_address_row(uint32_t addr, + uint32_t *value) +{ + return hal_qfprom_read_row_generic(addr, value); +} + +enum qfprom_error hal_qfprom_write_raw_address(uint32_t addr, uint32_t value) +{ + vaddr_t vaddr; + + if (!IS_ENABLED(CFG_QFPROM_PROGRAMMING)) + return QFPROM_OPERATION_NOT_ALLOWED_ERR; + + if (!IS_ALIGNED_WITH_TYPE(addr, uint32_t)) + return QFPROM_ADDRESS_INVALID_ERR; + + vaddr = (vaddr_t)phys_to_virt(addr, MEM_AREA_IO_SEC, sizeof(uint32_t)); + if (!vaddr) + return QFPROM_ADDRESS_INVALID_ERR; + + io_write32(vaddr, value); + return QFPROM_NO_ERR; +} + +enum qfprom_error hal_qfprom_read_blow_status(uint32_t *value) +{ + if (!value) + return QFPROM_DATA_PTR_NULL_ERR; + + *value = qfprom_read_reg(QFPROM_BLOW_STATUS_OFFSET) & + QFPROM_BLOW_STATUS_RMSK; + + return QFPROM_NO_ERR; +} + +void hal_qfprom_clear_fec_error_status(void) +{ + qfprom_write_reg(QFPROM_FEC_ESR_OFFSET, QFPROM_FEC_ESR_ERR_SEEN_BMSK); +} + +bool hal_qfprom_is_fec_error_seen(void) +{ + uint32_t val = qfprom_read_reg(QFPROM_FEC_ESR_OFFSET); + + return (val & QFPROM_FEC_ESR_ERR_SEEN_BMSK) != 0; +} + +void hal_qfprom_read_error_address(uint16_t *value) +{ + uint32_t val; + + if (!value) + return; + + val = qfprom_read_reg(QFPROM_FEC_EAR_OFFSET); + *value = (uint16_t)(val & QFPROM_FEC_EAR_ERR_ADDR_BMSK); +} diff --git a/core/drivers/qcom/qfprom/qfprom_hal.h b/core/drivers/qcom/qfprom/qfprom_hal.h new file mode 100644 index 0000000000..9224abbd14 --- /dev/null +++ b/core/drivers/qcom/qfprom/qfprom_hal.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __QFPROM_HAL_H__ +#define __QFPROM_HAL_H__ + +#include +#include +#include + +void hal_qfprom_set_blow_timer(uint32_t value); +void hal_qfprom_set_accel(uint32_t value); +enum qfprom_error hal_qfprom_read_raw_address(uint32_t addr, uint32_t *value); +enum qfprom_error hal_qfprom_read_raw_address_row(uint32_t addr, + uint32_t *value); +enum qfprom_error hal_qfprom_write_raw_address(uint32_t addr, uint32_t value); +enum qfprom_error hal_qfprom_read_corrected_address(uint32_t addr, + uint32_t *value); +enum qfprom_error hal_qfprom_read_corrected_address_row(uint32_t addr, + uint32_t *value); +enum qfprom_error hal_qfprom_read_blow_status(uint32_t *value); +void hal_qfprom_clear_fec_error_status(void); +bool hal_qfprom_is_fec_error_seen(void); +void hal_qfprom_read_error_address(uint16_t *value); + +#endif /* __QFPROM_HAL_H__ */ diff --git a/core/drivers/qcom/qfprom/qfprom_priv.h b/core/drivers/qcom/qfprom/qfprom_priv.h new file mode 100644 index 0000000000..143988f791 --- /dev/null +++ b/core/drivers/qcom/qfprom/qfprom_priv.h @@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __QFPROM_PRIV_H__ +#define __QFPROM_PRIV_H__ + +#include +#include +#include + +#define QFPROM_BLOW_TIMEOUT_US 1000 +#define QFPROM_FEC_REGION_LSB_MAX 32 +#define QFPROM_FEC_REGION_MSB_MAX 64 + +enum qfprom_fec_scheme { + QFPROM_FEC_NONE = 0, + QFPROM_FEC_15_10, + QFPROM_FEC_63_56, + QFPROM_MAX_FEC +}; + +enum qfprom_row_type { + QFPROM_ROW_LSB = 0, + QFPROM_ROW_MSB, +}; + +struct qfprom_region_info { + enum qfprom_region_name region_name; + uint32_t size; + enum qfprom_fec_scheme fec_type; + uint32_t raw_base_addr; + uint32_t corr_base_addr; + uint32_t read_perm_mask; + uint32_t write_perm_mask; + enum qfprom_row_type perm_reg_type; + bool read_allowed; + uint32_t region_index; +}; + +struct qfprom_platform_config { + const char *name; + paddr_t qfprom_raw_base; + paddr_t qfprom_corr_base; + size_t qfprom_size; + uint32_t default_bus_clk_khz; + const struct qfprom_region_info *region_data; + size_t num_regions; + uint32_t read_perm_lsb_offset; + uint32_t write_perm_lsb_offset; + uint32_t read_perm_msb_offset; + uint32_t write_perm_msb_offset; +}; + +struct qfprom_context { + const struct qfprom_platform_config *config; + struct mutex lock; /* Protects QFPROM operations and state */ + vaddr_t raw_base_va; + vaddr_t corr_base_va; + vaddr_t mutex_reg_va; + bool write_op_allowed; +}; + +enum region_permission { + REGION_PERM_READ = BIT(0), + REGION_PERM_WRITE = BIT(1), +}; + +struct qfprom_context *qfprom_get_context(void); + +TEE_Result qfprom_write_set_clock_settings(void); +TEE_Result qfprom_write_reset_clock_settings(void); + +TEE_Result qfprom_enable_voltage(void); +TEE_Result qfprom_disable_voltage(void); + +TEE_Result qfprom_acquire_hw_mutex(void); +TEE_Result qfprom_release_hw_mutex(void); + +const struct qfprom_platform_config *qfprom_get_platform_config(void); + +#endif /* __QFPROM_PRIV_H__ */ diff --git a/core/drivers/qcom/qfprom/qfprom_target.c b/core/drivers/qcom/qfprom/qfprom_target.c new file mode 100644 index 0000000000..23e69bde57 --- /dev/null +++ b/core/drivers/qcom/qfprom/qfprom_target.c @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qfprom_hal.h" +#include "qfprom_priv.h" + +static struct rpmh_client *rpmh_handle; + +TEE_Result qfprom_write_set_clock_settings(void) +{ + uint32_t blow_timer_value; + TEE_Result res; + + res = qcom_clock_enable(QCOM_CLKS_QFPROM); + if (res != TEE_SUCCESS) + return res; + + blow_timer_value = (QFPROM_BLOW_TIMER_CLK_FREQ_MHZ_X10 * + QFPROM_FUSE_BLOW_TIME_IN_US + 5) / 10; + + hal_qfprom_set_blow_timer(blow_timer_value & 0xFFF); + hal_qfprom_set_accel(QFPROM_ACCEL_VALUE); + + return TEE_SUCCESS; +} + +TEE_Result qfprom_write_reset_clock_settings(void) +{ + TEE_Result res; + + res = qcom_clock_disable(QCOM_CLKS_QFPROM); + if (res != TEE_SUCCESS) + return res; + + hal_qfprom_set_blow_timer(0); + + hal_qfprom_set_accel(QFPROM_ACCEL_RESET_VALUE); + + return TEE_SUCCESS; +} + +TEE_Result qfprom_enable_voltage(void) +{ + uint32_t vrm_addr; + uint32_t req_id; + + if (!rpmh_handle) { + rpmh_handle = rpmh_create_handle(RSC_DRV_SECURE, "qfprom"); + if (!rpmh_handle) { + EMSG("RPMH client creation failed"); + return TEE_ERROR_GENERIC; + } + } + + /* Enable MX voltage rail for QFPROM operations */ + if (cmd_db_query_addr(PM_QFPROM_VREG_A, &vrm_addr) != TEE_SUCCESS) { + EMSG("QFPROM voltage rail '%s' not found in CMD_DB", + PM_QFPROM_VREG_A); + return TEE_ERROR_GENERIC; + } + + if (rpmh_issue_command(rpmh_handle, RPMH_SET_ACTIVE, true, + vrm_addr, QFPROM_VOLTAGE_ON, &req_id) != + TEE_SUCCESS) { + EMSG("RPMH enable MX failed: addr 0x%x req_id %u", + vrm_addr, req_id); + return TEE_ERROR_GENERIC; + } + + rpmh_barrier_single(rpmh_handle, req_id); + + return TEE_SUCCESS; +} + +TEE_Result qfprom_disable_voltage(void) +{ + uint32_t vrm_addr; + uint32_t req_id; + + if (!rpmh_handle) { + EMSG("RPMH not initialized"); + return TEE_ERROR_GENERIC; + } + + /* Disable MX voltage rail after QFPROM operations */ + if (cmd_db_query_addr(PM_QFPROM_VREG_A, &vrm_addr) != TEE_SUCCESS) { + EMSG("QFPROM voltage rail '%s' not found in CMD_DB", + PM_QFPROM_VREG_A); + return TEE_ERROR_GENERIC; + } + + if (rpmh_issue_command(rpmh_handle, RPMH_SET_ACTIVE, true, + vrm_addr, QFPROM_VOLTAGE_OFF, &req_id) != + TEE_SUCCESS) { + EMSG("RPMH disable MX failed: addr 0x%x req_id %u", + vrm_addr, req_id); + return TEE_ERROR_GENERIC; + } + + rpmh_barrier_single(rpmh_handle, req_id); + + return TEE_SUCCESS; +} + +TEE_Result qfprom_acquire_hw_mutex(void) +{ + struct qfprom_context *qfprom_ctx = qfprom_get_context(); + uint64_t timer = timeout_init_us(QFPROM_HW_MUTEX_TIMEOUT_US); + uint32_t read_val; + + while (true) { + io_write32(qfprom_ctx->mutex_reg_va, QFPROM_HW_MUTEX_PID); + dsb(); + + read_val = io_read32(qfprom_ctx->mutex_reg_va); + if (read_val == QFPROM_HW_MUTEX_PID) + return TEE_SUCCESS; + + if (timeout_elapsed(timer)) { + EMSG("QFPROM HW mutex acquisition timeout after %u us", + QFPROM_HW_MUTEX_TIMEOUT_US); + return TEE_ERROR_BUSY; + } + + udelay(1); + } +} + +TEE_Result qfprom_release_hw_mutex(void) +{ + struct qfprom_context *qfprom_ctx = qfprom_get_context(); + + io_write32(qfprom_ctx->mutex_reg_va, 0); + dsb(); + + return TEE_SUCCESS; +} diff --git a/core/drivers/qcom/qfprom/sub.mk b/core/drivers/qcom/qfprom/sub.mk new file mode 100644 index 0000000000..2153e3859f --- /dev/null +++ b/core/drivers/qcom/qfprom/sub.mk @@ -0,0 +1,8 @@ +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# + +srcs-y += qfprom_core.c +srcs-y += qfprom_hal.c +srcs-y += qfprom_target.c diff --git a/core/drivers/qcom/sub.mk b/core/drivers/qcom/sub.mk index 87a7676888..b7a931564c 100644 --- a/core/drivers/qcom/sub.mk +++ b/core/drivers/qcom/sub.mk @@ -7,6 +7,8 @@ srcs-$(CFG_QCOM_RAMBLUR_PIMEM_V3) += ramblur/ramblur_pimem_v3.c srcs-$(CFG_QCOM_PRNG) += prng/prng.c +$(eval $(call cfg-depends-all,CFG_QCOM_QFPROM,CFG_QCOM_CMD_DB CFG_QCOM_RPMH_CLIENT)) $(eval $(call cfg-depends-all,CFG_QCOM_RPMH_CLIENT,CFG_QCOM_CMD_DB)) subdirs-$(CFG_QCOM_CMD_DB) += cmd_db subdirs-$(CFG_QCOM_RPMH_CLIENT) += rpmh +subdirs-$(CFG_QCOM_QFPROM) += qfprom diff --git a/core/include/drivers/clk_qcom.h b/core/include/drivers/clk_qcom.h index 334541e5d1..9baac78cb2 100644 --- a/core/include/drivers/clk_qcom.h +++ b/core/include/drivers/clk_qcom.h @@ -9,6 +9,7 @@ #include #include +#include #define REG_POLL_TIMEOUT(_addr, _timeout_us, _delay_us, _retp, _match) \ do { \ @@ -28,8 +29,15 @@ enum qcom_clk_group { QCOM_CLKS_MAX, }; +#define CBCR_BRANCH_ENABLE_BIT BIT(0) +#define CBCR_HW_CTL_ENABLE_BIT BIT(1) +#define CBCR_BRANCH_OFF_BIT BIT(31) +#define CMD_RCGR_UPDATE_BIT BIT(0) + TEE_Result qcom_clock_enable(enum qcom_clk_group group); TEE_Result qcom_clock_enable_cbc(vaddr_t cbcr); +TEE_Result qcom_clock_set_rate(vaddr_t cfg_rcgr, vaddr_t cmd_rcgr, + uint32_t cfg_value); TEE_Result qcom_clock_enable_pas(enum qcom_clk_group group); #endif /* _CLK_QCOM_H_ */ diff --git a/core/include/drivers/qcom/qfprom/qfprom.h b/core/include/drivers/qcom/qfprom/qfprom.h new file mode 100644 index 0000000000..68be58f35e --- /dev/null +++ b/core/include/drivers/qcom/qfprom/qfprom.h @@ -0,0 +1,91 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __QFPROM_H__ +#define __QFPROM_H__ + +#include +#include +#include + +enum qfprom_addr_space { + QFPROM_ADDR_SPACE_RAW = 0, + QFPROM_ADDR_SPACE_CORR = 1, +}; + +enum qfprom_error { + QFPROM_NO_ERR = 0x0, + QFPROM_ERR_UNKNOWN = 0x1, + QFPROM_DATA_PTR_NULL_ERR = 0x2, + QFPROM_ADDRESS_INVALID_ERR = 0x3, + QFPROM_WRITE_ERR = 0x4, + QFPROM_REGION_NOT_SUPPORTED_ERR = 0x5, + QFPROM_REGION_NOT_READABLE_ERR = 0x6, + QFPROM_REGION_NOT_WRITABLE_ERR = 0x7, + QFPROM_FEC_ERR = 0x8, + QFPROM_OPERATION_NOT_ALLOWED_ERR = 0x9, + QFPROM_FAILED_TO_CHANGE_VOLTAGE_ERR = 0xA, + QFPROM_ERROR_CLOCK_FAILED = 0x10, + QFPROM_ERROR_TIMEOUT = 0x11, +}; + +enum qfprom_region_name { + QFPROM_CRI_CM_PRIVATE_REGION = 0, + QFPROM_LCM_REGION, + QFPROM_PRI_KEY_DERIVATION_KEY_REGION, + QFPROM_CM_FEATURE_CONFIG_REGION, + QFPROM_MRC_2_0_REGION, + QFPROM_PTE_REGION, + QFPROM_READ_PERMISSION_REGION, + QFPROM_WRITE_PERMISSION_REGION, + QFPROM_FEC_ENABLES_REGION, + QFPROM_OEM_CONFIG_REGION, + QFPROM_FEATURE_CONFIG_M_REGION, + QFPROM_FEATURE_CONFIG_NM_REGION, + QFPROM_ANTI_ROLLBACK_1_REGION, + QFPROM_ANTI_ROLLBACK_2_REGION, + QFPROM_ANTI_ROLLBACK_3_REGION, + QFPROM_ANTI_ROLLBACK_4_REGION, + QFPROM_ANTI_ROLLBACK_5_REGION, + QFPROM_PK_HASH_0_REGION, + QFPROM_CALIBRATION_REGION, + QFPROM_MEMORY_CONFIGURATION_REGION, + QFPROM_QC_SPARE_20_REGION, + QFPROM_QC_SPARE_21_REGION, + QFPROM_OEM_IMAGE_ENCRYPTION_KEY_REGION, + QFPROM_OEM_SECURE_BOOT_REGION, + QFPROM_SEC_KEY_DERIVATION_KEY_REGION, + QFPROM_BOOT_ROM_PATCH_REGION, + QFPROM_IMAGE_ENCRYPTION_KEY_1_REGION, + QFPROM_USER_KEY_DERIVATION_KEY_REGION, + QFPROM_OEM_SPARE_28_REGION, + QFPROM_OEM_SPARE_29_REGION, + QFPROM_OEM_SPARE_30_REGION, + QFPROM_OEM_SPARE_31_REGION, + QFPROM_LAST_REGION_DUMMY, + QFPROM_MAX_REGION_ENUM = 0x7FFF +}; + +/* Read QFPROM row data */ +TEE_Result qfprom_read_row(uint32_t addr, + enum qfprom_addr_space type, + uint32_t *data); + +/* Write QFPROM row data */ +TEE_Result qfprom_write_row(uint32_t addr, uint32_t *data); + +/* Check if row has FEC protection */ +TEE_Result qfprom_row_has_fec_bits(uint32_t addr, + enum qfprom_addr_space type, + uint8_t *has_fec); + +/* Calculate FEC bits for 56-bit data */ +uint32_t qfprom_fec_63_56_bit(uint32_t lsb_data, uint32_t msb_data); + +/* Hardware init/deinit for batch fuse operations */ +TEE_Result qfprom_hw_init(void); +void qfprom_hw_deinit(void); + +#endif /* __QFPROM_H__ */ From 910b47f209a5c9c3f4d54ff55c336a5119dea578 Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Fri, 20 Feb 2026 14:15:13 +0530 Subject: [PATCH 04/14] drivers: qcom: qfprom: add Fuse Provisioning support Add support for secure fuse provisioning using sec.elf V2 format. Fuse provisioning is performed once during device manufacturing to program security credentials and configuration that cannot be changed after provisioning. This includes: - Secure boot public key hashes - Hardware-backed encryption keys (SHK, OEM keys) - OEM configuration and feature enables - Access permission controls (read/write disable bits) The implementation: - Parses and validates sec.elf format with SHA256 verification - Supports multiple fuse regions (general, secure boot, OEM, etc.) - Handles special operations (random key generation for SHK) - Implements FEC encoding for error-protected regions - Provides OEM spare region provisioning with random data option Provisioning is triggered at boot time if sec.elf is present in DDR, allowing factory programming without requiring runtime APIs. Once complete, a device reset is required for fuses to take effect. Signed-off-by: Selvam Sathappan Periakaruppan --- .../qcom/qfprom/fuseprov/fuseprov_boot.c | 86 +++++++ .../qcom/qfprom/fuseprov/fuseprov_core.c | 183 ++++++++++++++ .../qcom/qfprom/fuseprov/fuseprov_regions.c | 177 ++++++++++++++ .../drivers/qcom/qfprom/fuseprov/sec_elf_v2.c | 223 ++++++++++++++++++ .../drivers/qcom/qfprom/fuseprov/sec_elf_v2.h | 107 +++++++++ core/drivers/qcom/qfprom/fuseprov/sub.mk | 9 + core/drivers/qcom/qfprom/sub.mk | 3 + 7 files changed, 788 insertions(+) create mode 100644 core/drivers/qcom/qfprom/fuseprov/fuseprov_boot.c create mode 100644 core/drivers/qcom/qfprom/fuseprov/fuseprov_core.c create mode 100644 core/drivers/qcom/qfprom/fuseprov/fuseprov_regions.c create mode 100644 core/drivers/qcom/qfprom/fuseprov/sec_elf_v2.c create mode 100644 core/drivers/qcom/qfprom/fuseprov/sec_elf_v2.h create mode 100644 core/drivers/qcom/qfprom/fuseprov/sub.mk diff --git a/core/drivers/qcom/qfprom/fuseprov/fuseprov_boot.c b/core/drivers/qcom/qfprom/fuseprov/fuseprov_boot.c new file mode 100644 index 0000000000..c277fc5479 --- /dev/null +++ b/core/drivers/qcom/qfprom/fuseprov/fuseprov_boot.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sec_elf_v2.h" + +register_phys_mem(MEM_AREA_RAM_NSEC, CFG_SEC_ELF_DDR_ADDR, + CFG_SEC_ELF_DDR_SIZE); + +static TEE_Result discover_sec_elf(const uint8_t **data_out, size_t *size, + void **vaddr_out) +{ + const uint8_t *data; + const struct secdat_hdr *hdr; + void *vaddr; + + if (!data_out || !size || !vaddr_out) + return TEE_ERROR_BAD_PARAMETERS; + + vaddr = phys_to_virt(CFG_SEC_ELF_DDR_ADDR, MEM_AREA_RAM_NSEC, + CFG_SEC_ELF_DDR_SIZE); + if (!vaddr) { + EMSG("Failed to get VA for sec.elf at PA 0x%lx", + (unsigned long)CFG_SEC_ELF_DDR_ADDR); + return TEE_ERROR_GENERIC; + } + + data = (const uint8_t *)vaddr; + hdr = (const struct secdat_hdr *)data; + + if (hdr->magic1 != FUSEPROV_SECDAT_MAGIC1 || + hdr->magic2 != FUSEPROV_SECDAT_MAGIC2) { + EMSG("Invalid sec.elf magic: 0x%x/0x%x", + hdr->magic1, hdr->magic2); + return TEE_ERROR_BAD_FORMAT; + } + + *size = sizeof(*hdr) + hdr->size + TEE_SHA256_HASH_SIZE; + + if (*size > CFG_SEC_ELF_DDR_SIZE) { + EMSG("sec.elf size %zu exceeds limit %zu", + *size, (size_t)CFG_SEC_ELF_DDR_SIZE); + return TEE_ERROR_BAD_FORMAT; + } + + *data_out = data; + *vaddr_out = vaddr; + return TEE_SUCCESS; +} + +static TEE_Result execute_provisioning(void) +{ + const uint8_t *data = NULL; + void *vaddr = NULL; + size_t len = 0; + TEE_Result res; + + res = discover_sec_elf(&data, &len, &vaddr); + if (res != TEE_SUCCESS) + return res; + + res = fuseprov_execute(data, len); + + if (res != TEE_SUCCESS) { + EMSG("Fuse provisioning failed: 0x%x", res); + return res; + } + + IMSG("Fuse provisioning completed successfully"); + fuseprov_reset_device(); + return TEE_SUCCESS; +} + +service_init(execute_provisioning); diff --git a/core/drivers/qcom/qfprom/fuseprov/fuseprov_core.c b/core/drivers/qcom/qfprom/fuseprov/fuseprov_core.c new file mode 100644 index 0000000000..1ac975dfcc --- /dev/null +++ b/core/drivers/qcom/qfprom/fuseprov/fuseprov_core.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sec_elf_v2.h" + +static TEE_Result blow_fuse_region(enum fuseprov_region_type region, + const struct fuse_entry *entries, + uint32_t count) +{ + enum qfprom_error err; + uint32_t fuse_data[2]; + uint32_t current_data[2]; + uint32_t i; + uint8_t has_fec; + uint32_t mask_fec_msb_bits; + + for (i = 0; i < count; i++) { + const struct fuse_entry *entry = &entries[i]; + + if (entry->region != region || + entry->operation != FUSEPROV_OP_BLOW || + (entry->lsb_val == 0 && entry->msb_val == 0)) + continue; + + err = qfprom_row_has_fec_bits(entry->addr, + QFPROM_ADDR_SPACE_RAW, + &has_fec); + if (err != QFPROM_NO_ERR) { + EMSG("Failed to check FEC for addr 0x%08x: error=%d", + entry->addr, err); + return TEE_ERROR_GENERIC; + } + + if (has_fec) + mask_fec_msb_bits = 0x00FFFFFF; + else + mask_fec_msb_bits = 0xFFFFFFFF; + + err = qfprom_read_row(entry->addr, QFPROM_ADDR_SPACE_CORR, + current_data); + if (err != QFPROM_NO_ERR) { + EMSG("Failed to read fuse at 0x%08x: error=%d", + entry->addr, err); + return TEE_ERROR_GENERIC; + } + + /* Check if fuse bits are already blown */ + if (((current_data[0] & entry->lsb_val) == entry->lsb_val) && + ((current_data[1] & entry->msb_val) == + (entry->msb_val & mask_fec_msb_bits))) + continue; + + fuse_data[0] = entry->lsb_val; + fuse_data[1] = entry->msb_val; + + err = qfprom_write_row(entry->addr, fuse_data); + if (err != QFPROM_NO_ERR) { + EMSG("Failed to write fuse at 0x%08x: error=%d", + entry->addr, err); + return TEE_ERROR_GENERIC; + } + } + + return TEE_SUCCESS; +} + +TEE_Result fuseprov_execute(const uint8_t *data, size_t len) +{ + TEE_Result res = TEE_ERROR_GENERIC; + const struct secdat_hdr *hdr = NULL; + const struct segment_hdr *segments = NULL; + const uint8_t *seg_data = NULL; + uint32_t seg_size = 0; + const struct qfuse_list_hdr *qfuse_hdr = NULL; + const struct fuse_entry *entries = NULL; + uint32_t count = 0; + uint32_t perm_data[2]; + enum qfprom_error err; + + if (!data || len == 0) + return TEE_ERROR_BAD_PARAMETERS; + + res = sec_elf_parse(data, len, &hdr, &segments); + if (res != TEE_SUCCESS) + return res; + + res = sec_elf_validate_hash(data, len, hdr); + if (res != TEE_SUCCESS) + return res; + + res = qfprom_hw_init(); + if (res != TEE_SUCCESS) { + EMSG("Failed to initialize QFPROM hardware: 0x%x", res); + return res; + } + + err = qfprom_read_row(WRITE_PERMISSION_ADDR, QFPROM_ADDR_SPACE_CORR, + perm_data); + if (err != QFPROM_NO_ERR) { + EMSG("Failed to read WRITE_PERMISSION fuse: error=%d", err); + res = TEE_ERROR_GENERIC; + goto cleanup; + } + + if (perm_data[0] & OEM_SECURE_BOOT_PERM_MASK) { + res = TEE_SUCCESS; + goto cleanup; + } + + res = sec_elf_find_segment(data, len, + FUSEPROV_SECDAT_SEGMENT_TYPE_EFUSE, + &seg_data, &seg_size); + + if (res != TEE_SUCCESS) { + res = TEE_ERROR_BAD_FORMAT; + goto cleanup; + } + + if (!seg_data) { + res = TEE_ERROR_BAD_FORMAT; + goto cleanup; + } + + if (seg_size < sizeof(struct qfuse_list_hdr)) { + res = TEE_ERROR_BAD_FORMAT; + goto cleanup; + } + + qfuse_hdr = (const struct qfuse_list_hdr *)seg_data; + count = qfuse_hdr->fuse_count; + entries = (const struct fuse_entry *) + (seg_data + sizeof(struct qfuse_list_hdr)); + + res = blow_fuse_region(FUSEPROV_REGION_GENERAL, entries, count); + if (res != TEE_SUCCESS) + goto cleanup; + + res = blow_shk(entries, count); + if (res != TEE_SUCCESS) + goto cleanup; + + res = provision_oem_spare(entries, count); + if (res != TEE_SUCCESS) + goto cleanup; + + res = blow_fuse_region(FUSEPROV_REGION_OEM_CONFIG, entries, count); + if (res != TEE_SUCCESS) + goto cleanup; + + res = blow_fuse_region(FUSEPROV_REGION_SECBOOT, entries, count); + if (res != TEE_SUCCESS) + goto cleanup; + + res = blow_fuse_region(FUSEPROV_REGION_FEC_EN, entries, count); + if (res != TEE_SUCCESS) + goto cleanup; + + res = blow_fuse_region(FUSEPROV_REGION_RW_PERM, entries, count); + if (res != TEE_SUCCESS) + goto cleanup; + + res = TEE_SUCCESS; + +cleanup: + qfprom_hw_deinit(); + return res; +} + +void fuseprov_reset_device(void) +{ + IMSG("Fuse provisioning complete - manual reset required from U-Boot"); +} diff --git a/core/drivers/qcom/qfprom/fuseprov/fuseprov_regions.c b/core/drivers/qcom/qfprom/fuseprov/fuseprov_regions.c new file mode 100644 index 0000000000..932e46927c --- /dev/null +++ b/core/drivers/qcom/qfprom/fuseprov/fuseprov_regions.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include + +#include "sec_elf_v2.h" + +#define SHK_SIZE_BYTES 40 +#define SHK_NUM_ROWS 5 + +TEE_Result blow_shk(const struct fuse_entry *entries, uint32_t count) +{ + uint8_t shk[SHK_SIZE_BYTES]; + uint32_t data[2]; + enum qfprom_error err; + uint32_t i; + uint32_t shk_row_idx = 0; + TEE_Result res; + uint8_t has_fec; + + for (i = 0; i < count; i++) { + if (entries[i].region != FUSEPROV_REGION_SHK || + entries[i].operation != FUSEPROV_OP_BLOW) + continue; + + err = qfprom_read_row(entries[i].addr, QFPROM_ADDR_SPACE_CORR, + data); + if (err != QFPROM_NO_ERR) + return TEE_ERROR_GENERIC; + + if (data[0] != 0 || data[1] != 0) { + IMSG("SHK fuse already blown, skipping"); + return TEE_SUCCESS; + } + } + + res = crypto_rng_read(shk, sizeof(shk)); + if (res != TEE_SUCCESS) + return res; + + shk_row_idx = 0; + for (i = 0; i < count; i++) { + uint32_t offset; + + if (entries[i].region != FUSEPROV_REGION_SHK || + entries[i].operation != FUSEPROV_OP_BLOW) + continue; + + if (shk_row_idx >= SHK_NUM_ROWS) { + EMSG("Too many SHK entries in SEC-ELF (max %d)", + SHK_NUM_ROWS); + res = TEE_ERROR_GENERIC; + goto cleanup; + } + + err = qfprom_row_has_fec_bits(entries[i].addr, + QFPROM_ADDR_SPACE_RAW, &has_fec); + if (err != QFPROM_NO_ERR) { + res = TEE_ERROR_GENERIC; + goto cleanup; + } + + offset = shk_row_idx * 8; + memcpy(&data[0], &shk[offset], 4); + memcpy(&data[1], &shk[offset + 4], 4); + + data[0] &= 0xFFFFFFFF; + data[1] &= 0x00FFFFFF; + + if (has_fec) + data[1] = qfprom_fec_63_56_bit(data[0], data[1]); + + err = qfprom_write_row(entries[i].addr, data); + if (err != QFPROM_NO_ERR) { + res = TEE_ERROR_GENERIC; + goto cleanup; + } + + shk_row_idx++; + } + + res = TEE_SUCCESS; + +cleanup: + memset(shk, 0, sizeof(shk)); + memset(data, 0, sizeof(data)); + dsb(); + + return res; +} + +TEE_Result provision_oem_spare(const struct fuse_entry *entries, + uint32_t count) +{ + TEE_Result res = TEE_SUCCESS; + enum qfprom_error err; + uint32_t data[2]; + uint32_t i; + uint8_t rnd[8]; + uint8_t has_fec; + + for (i = 0; i < count; i++) { + const struct fuse_entry *entry = &entries[i]; + + if (entry->region != FUSEPROV_REGION_OEM_SPARE || + (entry->operation != FUSEPROV_OP_BLOW && + entry->operation != FUSEPROV_OP_BLOW_RANDOM)) + continue; + + err = qfprom_read_row(entry->addr, QFPROM_ADDR_SPACE_CORR, + data); + if (err != QFPROM_NO_ERR) { + EMSG("Failed to read OEM spare fuse at 0x%08x: err=%d", + entry->addr, err); + res = TEE_ERROR_GENERIC; + goto cleanup; + } + + if (data[0] != 0 || data[1] != 0) { + IMSG("OEM spare fuse at 0x%08x already blown, skipping", + entry->addr); + continue; + } + + has_fec = 0; + + err = qfprom_row_has_fec_bits(entry->addr, + QFPROM_ADDR_SPACE_RAW, + &has_fec); + if (err != QFPROM_NO_ERR) { + EMSG("Failed to check FEC for addr 0x%08x: error=%d", + entry->addr, err); + res = TEE_ERROR_GENERIC; + goto cleanup; + } + + if (entry->operation == FUSEPROV_OP_BLOW) { + data[0] = entry->lsb_val; + data[1] = entry->msb_val; + } else { + res = crypto_rng_read(rnd, sizeof(rnd)); + if (res != TEE_SUCCESS) { + EMSG("Failed to generate random data: 0x%x", + res); + goto cleanup; + } + + memcpy(&data[0], &rnd[0], 4); + memcpy(&data[1], &rnd[4], 4); + } + + if (has_fec) + data[1] = qfprom_fec_63_56_bit(data[0], data[1]); + + err = qfprom_write_row(entry->addr, data); + if (err != QFPROM_NO_ERR) { + EMSG("Failed to write OEM spare fuse at 0x%08x: err=%d", + entry->addr, err); + res = TEE_ERROR_GENERIC; + goto cleanup; + } + } + +cleanup: + memset(rnd, 0, sizeof(rnd)); + memset(data, 0, sizeof(data)); + dsb(); + + return res; +} diff --git a/core/drivers/qcom/qfprom/fuseprov/sec_elf_v2.c b/core/drivers/qcom/qfprom/fuseprov/sec_elf_v2.c new file mode 100644 index 0000000000..566bb47d1d --- /dev/null +++ b/core/drivers/qcom/qfprom/fuseprov/sec_elf_v2.c @@ -0,0 +1,223 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include + +#include "sec_elf_v2.h" + +TEE_Result sec_elf_parse(const uint8_t *data, size_t size, + const struct secdat_hdr **hdr, + const struct segment_hdr **segments) +{ + const struct secdat_hdr *header; + + if (!data || !hdr || !segments) + return TEE_ERROR_BAD_PARAMETERS; + + if (size < sizeof(struct secdat_hdr)) { + EMSG("sec.elf too small: %zu bytes", size); + return TEE_ERROR_BAD_FORMAT; + } + + header = (const struct secdat_hdr *)data; + + if (header->magic1 != FUSEPROV_SECDAT_MAGIC1) { + EMSG("Invalid magic1: 0x%08x", header->magic1); + return TEE_ERROR_BAD_FORMAT; + } + + if (header->magic2 != FUSEPROV_SECDAT_MAGIC2) { + EMSG("Invalid magic2: 0x%08x", header->magic2); + return TEE_ERROR_BAD_FORMAT; + } + + if (header->revision != FUSEPROV_SECDAT_VERSION_2) { + EMSG("Unsupported version: 0x%08x", header->revision); + return TEE_ERROR_BAD_FORMAT; + } + + if (header->size + sizeof(*header) + sizeof(struct secdat_footer) != + size) { + EMSG("Size mismatch: header=%u, file=%zu", header->size, size); + return TEE_ERROR_BAD_FORMAT; + } + + if (header->seg_num > FUSEPROV_SECDAT_MAX_SUPPORTED_SEGMENT) { + EMSG("Too many segments: %u", header->seg_num); + return TEE_ERROR_BAD_FORMAT; + } + + *hdr = header; + *segments = (const struct segment_hdr *)(data + sizeof(*header)); + + return TEE_SUCCESS; +} + +TEE_Result sec_elf_validate_header(const uint8_t *data, size_t size, + const struct secdat_hdr **hdr) +{ + const struct secdat_hdr *header; + + if (!data || !hdr) + return TEE_ERROR_BAD_PARAMETERS; + + if (size < sizeof(struct secdat_hdr)) { + EMSG("sec.elf too small: %zu bytes", size); + return TEE_ERROR_BAD_FORMAT; + } + + header = (const struct secdat_hdr *)data; + + if (header->magic1 != FUSEPROV_SECDAT_MAGIC1 || + header->magic2 != FUSEPROV_SECDAT_MAGIC2) { + EMSG("Invalid magic numbers"); + return TEE_ERROR_BAD_FORMAT; + } + + if (header->revision != FUSEPROV_SECDAT_VERSION_2) { + EMSG("Unsupported version: 0x%08x", header->revision); + return TEE_ERROR_BAD_FORMAT; + } + + if (header->size + sizeof(*header) + sizeof(struct secdat_footer) != + size) { + EMSG("Size mismatch: header=%u, file=%zu", header->size, size); + return TEE_ERROR_BAD_FORMAT; + } + + if (header->seg_num > FUSEPROV_SECDAT_MAX_SUPPORTED_SEGMENT) { + EMSG("Too many segments: %u", header->seg_num); + return TEE_ERROR_BAD_FORMAT; + } + + *hdr = header; + return TEE_SUCCESS; +} + +TEE_Result sec_elf_validate_hash(const uint8_t *data, size_t size, + const struct secdat_hdr *hdr) +{ + TEE_Result res; + uint8_t calc_hash[TEE_SHA256_HASH_SIZE]; + const uint8_t *stored_hash; + void *ctx = NULL; + size_t hash_bytes; + + if (!data || !hdr) + return TEE_ERROR_BAD_PARAMETERS; + + /* + * Calculate hash over header, segments, and data payload. + * The footer contains the stored hash for verification. + */ + hash_bytes = sizeof(struct secdat_hdr) + + (hdr->seg_num * sizeof(struct segment_hdr)) + + (hdr->size - sizeof(struct secdat_footer)); + + if (hash_bytes >= size) { + EMSG("Invalid hash size calculation: %zu >= %zu", + hash_bytes, size); + return TEE_ERROR_BAD_FORMAT; + } + + stored_hash = data + hash_bytes; + + res = crypto_hash_alloc_ctx(&ctx, TEE_ALG_SHA256); + if (res != TEE_SUCCESS) { + EMSG("Failed to allocate hash context: 0x%x", res); + goto out; + } + + res = crypto_hash_init(ctx); + if (res != TEE_SUCCESS) { + EMSG("Failed to initialize hash: 0x%x", res); + goto out; + } + + res = crypto_hash_update(ctx, data, hash_bytes); + if (res != TEE_SUCCESS) { + EMSG("Failed to update hash: 0x%x", res); + goto out; + } + + res = crypto_hash_final(ctx, calc_hash, TEE_SHA256_HASH_SIZE); + if (res != TEE_SUCCESS) { + EMSG("Failed to finalize hash: 0x%x", res); + goto out; + } + + if (consttime_memcmp(calc_hash, stored_hash, + TEE_SHA256_HASH_SIZE) != 0) { + EMSG("Hash verification failed"); + res = TEE_ERROR_SECURITY; + goto out; + } + + DMSG("Hash validation successful"); + res = TEE_SUCCESS; + +out: + crypto_hash_free_ctx(ctx); + return res; +} + +TEE_Result sec_elf_find_segment(const uint8_t *data, size_t size, + uint32_t seg_type, + const uint8_t **seg_data, + uint32_t *seg_size) +{ + const struct secdat_hdr *hdr; + const struct segment_hdr *segments; + uint32_t i; + TEE_Result res; + + if (!data || !seg_data || !seg_size) + return TEE_ERROR_BAD_PARAMETERS; + + res = sec_elf_validate_header(data, size, &hdr); + if (res != TEE_SUCCESS) + return res; + + segments = (const struct segment_hdr *)(data + sizeof(*hdr)); + + for (i = 0; i < hdr->seg_num; i++) { + if (segments[i].type == seg_type) { + if (segments[i].offset >= size) { + EMSG("Invalid segment offset: %u", + segments[i].offset); + return TEE_ERROR_BAD_FORMAT; + } + + *seg_data = data + segments[i].offset; + + /* + * Calculate segment size: + * - If not the last segment, size is the difference + * between this segment's offset and the next + * - If last segment, size extends to the footer + */ + if ((i + 1) < hdr->seg_num) { + /* Not the last segment */ + *seg_size = segments[i + 1].offset - + segments[i].offset; + } else { + /* Last segment */ + *seg_size = size - + sizeof(struct secdat_footer) - + segments[i].offset; + } + + return TEE_SUCCESS; + } + } + + *seg_data = NULL; + *seg_size = 0; + return TEE_ERROR_ITEM_NOT_FOUND; +} diff --git a/core/drivers/qcom/qfprom/fuseprov/sec_elf_v2.h b/core/drivers/qcom/qfprom/fuseprov/sec_elf_v2.h new file mode 100644 index 0000000000..fcdcf45549 --- /dev/null +++ b/core/drivers/qcom/qfprom/fuseprov/sec_elf_v2.h @@ -0,0 +1,107 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __SEC_ELF_V2_H__ +#define __SEC_ELF_V2_H__ + +#include +#include +#include +#include + +#define FUSEPROV_SECDAT_MAGIC1 0x3B7251CA +#define FUSEPROV_SECDAT_MAGIC2 0x2A126F29 +#define FUSEPROV_SECDAT_VERSION_2 0x00000002 + +#define FUSEPROV_SECDAT_MAX_SUPPORTED_SEGMENT 32 + +#define FUSEPROV_SECDAT_SEGMENT_TYPE_EFUSE 0x00 +#define FUSEPROV_SECDAT_SEGMENT_TYPE_ENCKEY 0x01 + +enum fuseprov_region_type { + FUSEPROV_REGION_SECBOOT = 0, + FUSEPROV_REGION_OEM_PK_HASH = 1, + FUSEPROV_REGION_SHK = 2, + FUSEPROV_REGION_OEM_CONFIG = 3, + FUSEPROV_REGION_RW_PERM = 4, + FUSEPROV_REGION_SPARE_REG19 = 5, + FUSEPROV_REGION_GENERAL = 6, + FUSEPROV_REGION_FEC_EN = 7, + FUSEPROV_REGION_ANTI_ROLLBACK_2 = 8, + FUSEPROV_REGION_ANTI_ROLLBACK_3 = 9, + FUSEPROV_REGION_PK_HASH1 = 10, + FUSEPROV_REGION_IMAGE_ENCR_KEY1 = 11, + FUSEPROV_REGION_OEM_SECURE = 12, + FUSEPROV_REGION_MRC_2_0 = 13, + FUSEPROV_REGION_OEM_SPARE = 14, + FUSEPROV_REGION_MAX +}; + +enum fuseprov_operation_type { + FUSEPROV_OP_BLOW = 0x00, + FUSEPROV_OP_VERIFYMASK0 = 0x01, + FUSEPROV_OP_BLOW_RANDOM = 0x02, +}; + +struct secdat_hdr { + uint32_t magic1; + uint32_t magic2; + uint32_t revision; + uint32_t size; + uint8_t info[16]; + uint32_t seg_num; + uint32_t reserved[3]; +} __packed; + +struct segment_hdr { + uint32_t offset; + uint16_t type; + uint16_t attribute; +} __packed; + +struct fuse_entry { + uint32_t region; + uint32_t addr; + uint32_t lsb_val; + uint32_t msb_val; + uint32_t operation; +} __packed; + +struct qfuse_list_hdr { + uint32_t revision; + uint32_t size; + uint32_t fuse_count; + uint32_t reserved[4]; +} __packed; + +struct secdat_footer { + uint8_t hash[TEE_SHA256_HASH_SIZE]; +} __packed; + +TEE_Result sec_elf_parse(const uint8_t *data, size_t size, + const struct secdat_hdr **hdr, + const struct segment_hdr **segments); + +TEE_Result sec_elf_validate_header(const uint8_t *data, size_t size, + const struct secdat_hdr **hdr); + +TEE_Result sec_elf_validate_hash(const uint8_t *data, size_t size, + const struct secdat_hdr *hdr); + +TEE_Result sec_elf_find_segment(const uint8_t *data, size_t size, + uint32_t seg_type, + const uint8_t **seg_data, + uint32_t *seg_size); + +TEE_Result provision_oem_spare(const struct fuse_entry *entries, + uint32_t count); + +TEE_Result blow_shk(const struct fuse_entry *entries, uint32_t count); + +TEE_Result fuseprov_execute(const uint8_t *data, size_t len); + +void fuseprov_reset_device(void); + +#endif /* __SEC_ELF_V2_H__ */ diff --git a/core/drivers/qcom/qfprom/fuseprov/sub.mk b/core/drivers/qcom/qfprom/fuseprov/sub.mk new file mode 100644 index 0000000000..5a11913e38 --- /dev/null +++ b/core/drivers/qcom/qfprom/fuseprov/sub.mk @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: BSD-2-Clause +# +# Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. +# + +srcs-y += sec_elf_v2.c +srcs-y += fuseprov_core.c +srcs-y += fuseprov_regions.c +srcs-y += fuseprov_boot.c diff --git a/core/drivers/qcom/qfprom/sub.mk b/core/drivers/qcom/qfprom/sub.mk index 2153e3859f..303cc60d5d 100644 --- a/core/drivers/qcom/qfprom/sub.mk +++ b/core/drivers/qcom/qfprom/sub.mk @@ -6,3 +6,6 @@ srcs-y += qfprom_core.c srcs-y += qfprom_hal.c srcs-y += qfprom_target.c + +$(eval $(call cfg-depends-all,CFG_QCOM_QFPROM_FUSEPROV,CFG_QCOM_QFPROM)) +subdirs-$(CFG_QCOM_QFPROM_FUSEPROV) += fuseprov From d1efde3113bbce25fdb3e9abadce36434b57afb9 Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Fri, 20 Feb 2026 13:34:38 +0530 Subject: [PATCH 05/14] drivers: qcom: qfprom: add Kodiak platform configuration Add Kodiak-specific QFPROM configuration with 31 fuse region definitions including base addresses, sizes, FEC settings, and permission masks. Regions include security keys, anti-rollback counters, OEM configuration, and calibration data. Signed-off-by: Selvam Sathappan Periakaruppan --- .../qcom/qfprom/kodiak/qfprom_fuse_region.c | 93 ++++++++++ .../qcom/qfprom/kodiak/qfprom_target.h | 171 ++++++++++++++++++ core/drivers/qcom/qfprom/sub.mk | 5 + 3 files changed, 269 insertions(+) create mode 100644 core/drivers/qcom/qfprom/kodiak/qfprom_fuse_region.c create mode 100644 core/drivers/qcom/qfprom/kodiak/qfprom_target.h diff --git a/core/drivers/qcom/qfprom/kodiak/qfprom_fuse_region.c b/core/drivers/qcom/qfprom/kodiak/qfprom_fuse_region.c new file mode 100644 index 0000000000..b223f5f285 --- /dev/null +++ b/core/drivers/qcom/qfprom/kodiak/qfprom_fuse_region.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include + +#include "qfprom_priv.h" + +#define QFPROM_REGION_ENTRY(name, sz, fec, readable, idx) \ + { \ + .region_name = QFPROM_##name##_REGION, \ + .size = (sz), \ + .fec_type = (fec), \ + .raw_base_addr = name##_ADDR, \ + .corr_base_addr = QFPROM_RAW_TO_CORR(name##_ADDR), \ + .read_perm_mask = name##_PERM_MASK, \ + .write_perm_mask = name##_PERM_MASK, \ + .perm_reg_type = QFPROM_ROW_LSB, \ + .read_allowed = (readable), \ + .region_index = (idx), \ + } + +#define QFPROM_STD_REGION(name, sz, idx) \ + QFPROM_REGION_ENTRY(name, sz, QFPROM_FEC_NONE, true, idx) + +#define QFPROM_FEC_REGION(name, sz, idx) \ + QFPROM_REGION_ENTRY(name, sz, QFPROM_FEC_63_56, true, idx) + +#define QFPROM_PRIVATE_REGION(name, sz, idx) \ + QFPROM_REGION_ENTRY(name, sz, QFPROM_FEC_NONE, false, idx) + +const struct qfprom_region_info region_data[] = { + QFPROM_STD_REGION(LCM, 1, 1), + QFPROM_PRIVATE_REGION(PRI_KEY_DERIVATION_KEY, 4, 2), + QFPROM_STD_REGION(MRC_2_0, 2, 4), + QFPROM_STD_REGION(PTE, 5, 5), + QFPROM_STD_REGION(READ_PERMISSION, 1, 6), + QFPROM_STD_REGION(WRITE_PERMISSION, 1, 7), + QFPROM_STD_REGION(FEC_ENABLES, 1, 8), + QFPROM_STD_REGION(OEM_CONFIG, 3, 9), + QFPROM_STD_REGION(FEATURE_CONFIG_M, 5, 10), + QFPROM_STD_REGION(FEATURE_CONFIG_NM, 4, 11), + QFPROM_STD_REGION(ANTI_ROLLBACK_1, 1, 12), + QFPROM_STD_REGION(ANTI_ROLLBACK_2, 1, 13), + QFPROM_STD_REGION(ANTI_ROLLBACK_3, 1, 14), + QFPROM_STD_REGION(ANTI_ROLLBACK_4, 1, 15), + QFPROM_STD_REGION(ANTI_ROLLBACK_5, 1, 16), + QFPROM_STD_REGION(PK_HASH_0, 6, 17), + QFPROM_STD_REGION(CALIBRATION, 25, 18), + QFPROM_STD_REGION(MEMORY_CONFIGURATION, 120, 19), + QFPROM_FEC_REGION(QC_SPARE_20, 1, 20), + QFPROM_FEC_REGION(QC_SPARE_21, 1, 21), + QFPROM_FEC_REGION(OEM_IMAGE_ENCRYPTION_KEY, 3, 22), + QFPROM_FEC_REGION(OEM_SECURE_BOOT, 2, 23), + QFPROM_FEC_REGION(SEC_KEY_DERIVATION_KEY, 5, 24), + QFPROM_FEC_REGION(IMAGE_ENCRYPTION_KEY_1, 3, 26), + QFPROM_FEC_REGION(USER_KEY_DERIVATION_KEY, 5, 27), + QFPROM_STD_REGION(OEM_SPARE_28, 2, 28), + QFPROM_STD_REGION(OEM_SPARE_29, 2, 29), + QFPROM_STD_REGION(OEM_SPARE_30, 2, 30), + QFPROM_STD_REGION(OEM_SPARE_31, 2, 31), + { + QFPROM_LAST_REGION_DUMMY, + 0, + QFPROM_FEC_NONE, + 0, + 0, + 0, + 0, + QFPROM_ROW_LSB + } +}; + +const size_t region_count = ARRAY_SIZE(region_data); + +const struct qfprom_platform_config plat_config = { + .name = "Kodiak", + .qfprom_raw_base = QFPROM_RAW_BASE, + .qfprom_corr_base = QFPROM_CORR_BASE, + .qfprom_size = QFPROM_SIZE, + .region_data = region_data, + .num_regions = ARRAY_SIZE(region_data), + .read_perm_lsb_offset = QFPROM_READ_PERM_LSB_OFFSET, + .read_perm_msb_offset = QFPROM_READ_PERM_MSB_OFFSET, + .write_perm_lsb_offset = QFPROM_WRITE_PERM_LSB_OFFSET, + .write_perm_msb_offset = QFPROM_WRITE_PERM_MSB_OFFSET, +}; + +const struct qfprom_platform_config *qfprom_get_platform_config(void) +{ + return &plat_config; +} diff --git a/core/drivers/qcom/qfprom/kodiak/qfprom_target.h b/core/drivers/qcom/qfprom/kodiak/qfprom_target.h new file mode 100644 index 0000000000..473e422464 --- /dev/null +++ b/core/drivers/qcom/qfprom/kodiak/qfprom_target.h @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __QFPROM_TARGET_H__ +#define __QFPROM_TARGET_H__ + +#include +#include +#include +#include +#include + +#define QFPROM_RAW_BASE 0x00780000 +#define QFPROM_CORR_BASE 0x00784000 +#define QFPROM_SIZE 0x4000 + +#define QFPROM_BLOW_TIMER_OFFSET 0x203c +#define QFPROM_ACCEL_OFFSET 0x2044 + +#define QFPROM_BLOW_STATUS_OFFSET 0x2048 +#define QFPROM_BLOW_STATUS_RMSK 0x3 +#define QFPROM_BLOW_STATUS_BUSY_VAL 0x1 +#define QFPROM_BLOW_STATUS_ERROR_VAL 0x2 +#define QFPROM_BLOW_STATUS_READY_VAL 0x0 + +#define QFPROM_FEC_ESR_OFFSET 0x2060 +#define QFPROM_FEC_EAR_OFFSET 0x2064 +#define QFPROM_FEC_ESR_ERR_SEEN_BMSK BIT(0) +#define QFPROM_FEC_EAR_ERR_ADDR_BMSK 0xFFFF + +#define QFPROM_BLOW_TIMER_CLK_FREQ_MHZ_X10 48 +#define QFPROM_FUSE_BLOW_TIME_IN_US 5 +#define QFPROM_CLOCK_DIVIDE 0x7 + +#define QFPROM_GATELAST_VAL 0x1 +#define QFPROM_TRIPPT_SEL_VAL 0x5 +#define QFPROM_ACCEL_VAL 0x8 + +#define QFPROM_GATELAST_SHFT 11 +#define QFPROM_TRIPPT_SEL_SHFT 8 +#define QFPROM_ACCEL_SHFT 0 + +#define QFPROM_ACCEL_VALUE \ + ((QFPROM_GATELAST_VAL << QFPROM_GATELAST_SHFT) | \ + (QFPROM_TRIPPT_SEL_VAL << QFPROM_TRIPPT_SEL_SHFT) | \ + (QFPROM_ACCEL_VAL << QFPROM_ACCEL_SHFT)) + +#define QFPROM_ACCEL_RESET_VALUE \ + (0x1 << QFPROM_GATELAST_SHFT) + +#define MX_QFPROM_ENABLE_VAL 15 +#define MX_QFPROM_DISABLE_VAL 9 + +#define PM_QFPROM_VREG_A "mx.lvl" +#define QFPROM_VOLTAGE_ON 15 +#define QFPROM_VOLTAGE_OFF 9 + +#define QFPROM_RAW_TO_CORR(raw_addr) \ + ((raw_addr) + (QFPROM_CORR_BASE - QFPROM_RAW_BASE)) + +#define LCM_ADDR 0x00780120 +#define PRI_KEY_DERIVATION_KEY_ADDR 0x00780128 +#define MRC_2_0_ADDR 0x00780170 +#define PTE_ADDR 0x00780180 +#define READ_PERMISSION_ADDR 0x007801A8 +#define WRITE_PERMISSION_ADDR 0x007801B0 +#define FEC_ENABLES_ADDR 0x007801B8 +#define OEM_CONFIG_ADDR 0x007801C0 +#define FEATURE_CONFIG_M_ADDR 0x007801D8 +#define FEATURE_CONFIG_NM_ADDR 0x00780200 +#define ANTI_ROLLBACK_1_ADDR 0x00780220 +#define ANTI_ROLLBACK_2_ADDR 0x00780228 +#define ANTI_ROLLBACK_3_ADDR 0x00780230 +#define ANTI_ROLLBACK_4_ADDR 0x00780238 +#define ANTI_ROLLBACK_5_ADDR 0x00780240 +#define PK_HASH_0_ADDR 0x00780248 +#define CALIBRATION_ADDR 0x00780278 +#define MEMORY_CONFIGURATION_ADDR 0x00780340 +#define QC_SPARE_20_ADDR 0x00780700 +#define QC_SPARE_21_ADDR 0x00780708 +#define OEM_IMAGE_ENCRYPTION_KEY_ADDR 0x00780710 +#define OEM_SECURE_BOOT_ADDR 0x00780728 +#define SEC_KEY_DERIVATION_KEY_ADDR 0x00780738 +#define IMAGE_ENCRYPTION_KEY_1_ADDR 0x007809A0 +#define USER_KEY_DERIVATION_KEY_ADDR 0x007809B8 +#define OEM_SPARE_28_ADDR 0x007809E0 +#define OEM_SPARE_29_ADDR 0x007809F0 +#define OEM_SPARE_30_ADDR 0x00780A00 +#define OEM_SPARE_31_ADDR 0x00780A10 + +#define QFPROM_READ_PERM_LSB_OFFSET \ + (READ_PERMISSION_ADDR - QFPROM_RAW_BASE) +#define QFPROM_READ_PERM_MSB_OFFSET \ + (QFPROM_READ_PERM_LSB_OFFSET + 4) + +#define QFPROM_WRITE_PERM_LSB_OFFSET \ + (WRITE_PERMISSION_ADDR - QFPROM_RAW_BASE) +#define QFPROM_WRITE_PERM_MSB_OFFSET \ + (QFPROM_WRITE_PERM_LSB_OFFSET + 4) + +#define QFPROM_HW_MUTEX_ID 8 +#define QFPROM_HW_MUTEX_PID 1 +#define QFPROM_HW_MUTEX_TIMEOUT_US 10000 +#define QFPROM_MUTEX_REG_ADDR \ + (TCSR_MUTEX_BASE + (0x1000 * QFPROM_HW_MUTEX_ID)) + +enum qfprom_perm_bit_pos { + LCM = 1, + PRI_KEY_DERIVATION_KEY = 2, + MRC_2_0 = 4, + PTE = 5, + READ_PERMISSION = 6, + WRITE_PERMISSION = 7, + FEC_ENABLES = 8, + OEM_CONFIG = 9, + FEATURE_CONFIG_M = 10, + FEATURE_CONFIG_NM = 11, + ANTI_ROLLBACK_1 = 12, + ANTI_ROLLBACK_2 = 13, + ANTI_ROLLBACK_3 = 14, + ANTI_ROLLBACK_4 = 15, + ANTI_ROLLBACK_5 = 16, + PK_HASH_0 = 17, + CALIBRATION = 18, + MEMORY_CONFIGURATION = 19, + QC_SPARE_20 = 20, + QC_SPARE_21 = 21, + OEM_IMAGE_ENCRYPTION_KEY = 22, + OEM_SECURE_BOOT = 23, + SEC_KEY_DERIVATION_KEY = 24, + IMAGE_ENCRYPTION_KEY_1 = 26, + USER_KEY_DERIVATION_KEY = 27, + OEM_SPARE_28 = 28, + OEM_SPARE_29 = 29, + OEM_SPARE_30 = 30, + OEM_SPARE_31 = 31, +}; + +#define LCM_PERM_MASK BIT(LCM) +#define PRI_KEY_DERIVATION_KEY_PERM_MASK BIT(PRI_KEY_DERIVATION_KEY) +#define MRC_2_0_PERM_MASK BIT(MRC_2_0) +#define PTE_PERM_MASK BIT(PTE) +#define READ_PERMISSION_PERM_MASK BIT(READ_PERMISSION) +#define WRITE_PERMISSION_PERM_MASK BIT(WRITE_PERMISSION) +#define FEC_ENABLES_PERM_MASK BIT(FEC_ENABLES) +#define OEM_CONFIG_PERM_MASK BIT(OEM_CONFIG) +#define FEATURE_CONFIG_M_PERM_MASK BIT(FEATURE_CONFIG_M) +#define FEATURE_CONFIG_NM_PERM_MASK BIT(FEATURE_CONFIG_NM) +#define ANTI_ROLLBACK_1_PERM_MASK BIT(ANTI_ROLLBACK_1) +#define ANTI_ROLLBACK_2_PERM_MASK BIT(ANTI_ROLLBACK_2) +#define ANTI_ROLLBACK_3_PERM_MASK BIT(ANTI_ROLLBACK_3) +#define ANTI_ROLLBACK_4_PERM_MASK BIT(ANTI_ROLLBACK_4) +#define ANTI_ROLLBACK_5_PERM_MASK BIT(ANTI_ROLLBACK_5) +#define PK_HASH_0_PERM_MASK BIT(PK_HASH_0) +#define CALIBRATION_PERM_MASK BIT(CALIBRATION) +#define MEMORY_CONFIGURATION_PERM_MASK BIT(MEMORY_CONFIGURATION) +#define QC_SPARE_20_PERM_MASK BIT(QC_SPARE_20) +#define QC_SPARE_21_PERM_MASK BIT(QC_SPARE_21) +#define OEM_IMAGE_ENCRYPTION_KEY_PERM_MASK BIT(OEM_IMAGE_ENCRYPTION_KEY) +#define OEM_SECURE_BOOT_PERM_MASK BIT(OEM_SECURE_BOOT) +#define SEC_KEY_DERIVATION_KEY_PERM_MASK BIT(SEC_KEY_DERIVATION_KEY) +#define IMAGE_ENCRYPTION_KEY_1_PERM_MASK BIT(IMAGE_ENCRYPTION_KEY_1) +#define USER_KEY_DERIVATION_KEY_PERM_MASK BIT(USER_KEY_DERIVATION_KEY) +#define OEM_SPARE_28_PERM_MASK BIT(OEM_SPARE_28) +#define OEM_SPARE_29_PERM_MASK BIT(OEM_SPARE_29) +#define OEM_SPARE_30_PERM_MASK BIT(OEM_SPARE_30) +#define OEM_SPARE_31_PERM_MASK BIT(OEM_SPARE_31) + +#endif /* __QFPROM_TARGET_H__ */ diff --git a/core/drivers/qcom/qfprom/sub.mk b/core/drivers/qcom/qfprom/sub.mk index 303cc60d5d..77d0d6a79e 100644 --- a/core/drivers/qcom/qfprom/sub.mk +++ b/core/drivers/qcom/qfprom/sub.mk @@ -9,3 +9,8 @@ srcs-y += qfprom_target.c $(eval $(call cfg-depends-all,CFG_QCOM_QFPROM_FUSEPROV,CFG_QCOM_QFPROM)) subdirs-$(CFG_QCOM_QFPROM_FUSEPROV) += fuseprov + +srcs-y += $(PLATFORM_FLAVOR)/qfprom_fuse_region.c + +global-incdirs-y += . +global-incdirs-y += $(PLATFORM_FLAVOR) From b22c14c5294e59eedc7b1ae8cf46366b93de6152 Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Sat, 18 Apr 2026 09:08:59 +0530 Subject: [PATCH 06/14] drivers: qcom: qfprom: add Lemans platform configuration Add QFPROM driver support for Lemans platform with 37 fuse regions including security keys, anti-rollback counters, OEM configuration, and calibration data. Add Lemans-specific clock driver header with QFPROM clock register definitions (GCC_SEC_CTRL_CFG_RCGR, GCC_SEC_CTRL_CMD_RCGR). Move platform-specific fuse region enums from common header to target-specific headers to allow per-platform customization. Implement conditional MX voltage rail workaround using CFG_QFPROM_MX_RAIL_WA configuration flag. This workaround is required for Kodiak platform but not needed for Lemans. Signed-off-by: Selvam Sathappan Periakaruppan --- .../qcom/platform/lemans/clock_group_qcom.h | 12 + .../qcom/qfprom/kodiak/qfprom_target.h | 38 ++- .../qcom/qfprom/lemans/qfprom_fuse_region.c | 121 +++++++++ .../qcom/qfprom/lemans/qfprom_target.h | 231 ++++++++++++++++++ core/drivers/qcom/qfprom/qfprom_core.c | 6 +- core/drivers/qcom/qfprom/qfprom_priv.h | 1 + core/drivers/qcom/qfprom/qfprom_target.c | 28 ++- core/include/drivers/qcom/qfprom/qfprom.h | 37 --- 8 files changed, 430 insertions(+), 44 deletions(-) create mode 100644 core/drivers/clk/qcom/platform/lemans/clock_group_qcom.h create mode 100644 core/drivers/qcom/qfprom/lemans/qfprom_fuse_region.c create mode 100644 core/drivers/qcom/qfprom/lemans/qfprom_target.h diff --git a/core/drivers/clk/qcom/platform/lemans/clock_group_qcom.h b/core/drivers/clk/qcom/platform/lemans/clock_group_qcom.h new file mode 100644 index 0000000000..b29f8aa723 --- /dev/null +++ b/core/drivers/clk/qcom/platform/lemans/clock_group_qcom.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ +#ifndef _CLOCK_GROUP_QCOM_H_ +#define _CLOCK_GROUP_QCOM_H_ + +#define GCC_SEC_CTRL_CFG_RCGR 0x39038 +#define GCC_SEC_CTRL_CMD_RCGR 0x39034 +#define QFPROM_CLOCK_DIVIDE 0x7 + +#endif /* _CLOCK_GROUP_QCOM_H_ */ diff --git a/core/drivers/qcom/qfprom/kodiak/qfprom_target.h b/core/drivers/qcom/qfprom/kodiak/qfprom_target.h index 473e422464..48f02520a1 100644 --- a/core/drivers/qcom/qfprom/kodiak/qfprom_target.h +++ b/core/drivers/qcom/qfprom/kodiak/qfprom_target.h @@ -6,6 +6,7 @@ #ifndef __QFPROM_TARGET_H__ #define __QFPROM_TARGET_H__ +#include #include #include #include @@ -32,7 +33,6 @@ #define QFPROM_BLOW_TIMER_CLK_FREQ_MHZ_X10 48 #define QFPROM_FUSE_BLOW_TIME_IN_US 5 -#define QFPROM_CLOCK_DIVIDE 0x7 #define QFPROM_GATELAST_VAL 0x1 #define QFPROM_TRIPPT_SEL_VAL 0x5 @@ -106,6 +106,42 @@ #define QFPROM_MUTEX_REG_ADDR \ (TCSR_MUTEX_BASE + (0x1000 * QFPROM_HW_MUTEX_ID)) +enum qfprom_region_name { + QFPROM_CRI_CM_PRIVATE_REGION = 0, + QFPROM_LCM_REGION, + QFPROM_PRI_KEY_DERIVATION_KEY_REGION, + QFPROM_CM_FEATURE_CONFIG_REGION, + QFPROM_MRC_2_0_REGION, + QFPROM_PTE_REGION, + QFPROM_READ_PERMISSION_REGION, + QFPROM_WRITE_PERMISSION_REGION, + QFPROM_FEC_ENABLES_REGION, + QFPROM_OEM_CONFIG_REGION, + QFPROM_FEATURE_CONFIG_M_REGION, + QFPROM_FEATURE_CONFIG_NM_REGION, + QFPROM_ANTI_ROLLBACK_1_REGION, + QFPROM_ANTI_ROLLBACK_2_REGION, + QFPROM_ANTI_ROLLBACK_3_REGION, + QFPROM_ANTI_ROLLBACK_4_REGION, + QFPROM_ANTI_ROLLBACK_5_REGION, + QFPROM_PK_HASH_0_REGION, + QFPROM_CALIBRATION_REGION, + QFPROM_MEMORY_CONFIGURATION_REGION, + QFPROM_QC_SPARE_20_REGION, + QFPROM_QC_SPARE_21_REGION, + QFPROM_OEM_IMAGE_ENCRYPTION_KEY_REGION, + QFPROM_OEM_SECURE_BOOT_REGION, + QFPROM_SEC_KEY_DERIVATION_KEY_REGION, + QFPROM_BOOT_ROM_PATCH_REGION, + QFPROM_IMAGE_ENCRYPTION_KEY_1_REGION, + QFPROM_USER_KEY_DERIVATION_KEY_REGION, + QFPROM_OEM_SPARE_28_REGION, + QFPROM_OEM_SPARE_29_REGION, + QFPROM_OEM_SPARE_30_REGION, + QFPROM_OEM_SPARE_31_REGION, + QFPROM_LAST_REGION_DUMMY, +}; + enum qfprom_perm_bit_pos { LCM = 1, PRI_KEY_DERIVATION_KEY = 2, diff --git a/core/drivers/qcom/qfprom/lemans/qfprom_fuse_region.c b/core/drivers/qcom/qfprom/lemans/qfprom_fuse_region.c new file mode 100644 index 0000000000..31fd4291fc --- /dev/null +++ b/core/drivers/qcom/qfprom/lemans/qfprom_fuse_region.c @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include + +#include "qfprom_priv.h" + +#define QFPROM_REGION_ENTRY(name, sz, fec, readable, idx) \ + { \ + .region_name = QFPROM_##name##_REGION, \ + .size = (sz), \ + .fec_type = (fec), \ + .raw_base_addr = name##_ADDR, \ + .corr_base_addr = QFPROM_RAW_TO_CORR(name##_ADDR), \ + .read_perm_mask = name##_PERM_MASK, \ + .write_perm_mask = name##_PERM_MASK, \ + .perm_reg_type = QFPROM_ROW_LSB, \ + .read_allowed = (readable), \ + .region_index = (idx), \ + } + +#define QFPROM_REGION_ENTRY_MSB(name, sz, fec, readable, idx) \ + { \ + .region_name = QFPROM_##name##_REGION, \ + .size = (sz), \ + .fec_type = (fec), \ + .raw_base_addr = name##_ADDR, \ + .corr_base_addr = QFPROM_RAW_TO_CORR(name##_ADDR), \ + .read_perm_mask = name##_PERM_MASK, \ + .write_perm_mask = name##_PERM_MASK, \ + .perm_reg_type = QFPROM_ROW_MSB, \ + .read_allowed = (readable), \ + .region_index = (idx), \ + } + +#define QFPROM_STD_REGION(name, sz, idx) \ + QFPROM_REGION_ENTRY(name, sz, QFPROM_FEC_NONE, true, idx) + +#define QFPROM_FEC_REGION(name, sz, idx) \ + QFPROM_REGION_ENTRY(name, sz, QFPROM_FEC_63_56, true, idx) + +#define QFPROM_FEC_REGION_MSB(name, sz, idx) \ + QFPROM_REGION_ENTRY_MSB(name, sz, QFPROM_FEC_63_56, true, idx) + +#define QFPROM_PRIVATE_REGION(name, sz, idx) \ + QFPROM_REGION_ENTRY(name, sz, QFPROM_FEC_NONE, false, idx) + +#define QFPROM_STD_REGION_MSB(name, sz, idx) \ + QFPROM_REGION_ENTRY_MSB(name, sz, QFPROM_FEC_NONE, true, idx) + +const struct qfprom_region_info region_data[] = { + QFPROM_STD_REGION(LCM, 1, 1), + QFPROM_PRIVATE_REGION(PRI_KEY_DERIVATION_KEY, 4, 2), + QFPROM_STD_REGION(MRC_2_0, 2, 3), + QFPROM_STD_REGION(PTE, 7, 4), + QFPROM_STD_REGION(READ_PERMISSION, 1, 5), + QFPROM_STD_REGION(WRITE_PERMISSION, 1, 6), + QFPROM_STD_REGION(FEC_ENABLES, 1, 7), + QFPROM_STD_REGION(OEM_CONFIG, 5, 8), + QFPROM_STD_REGION(FEATURE_CONFIG_M, 8, 9), + QFPROM_STD_REGION(FEATURE_CONFIG_NM, 5, 10), + QFPROM_STD_REGION(ANTI_ROLLBACK_1, 1, 11), + QFPROM_STD_REGION(ANTI_ROLLBACK_2, 1, 12), + QFPROM_STD_REGION(ANTI_ROLLBACK_3, 1, 13), + QFPROM_STD_REGION(ANTI_ROLLBACK_4, 1, 14), + QFPROM_STD_REGION(ANTI_ROLLBACK_5, 1, 15), + QFPROM_STD_REGION(ANTI_ROLLBACK_6, 1, 16), + QFPROM_STD_REGION(ANTI_ROLLBACK_7, 1, 17), + QFPROM_STD_REGION(ANTI_ROLLBACK_8, 1, 18), + QFPROM_STD_REGION(ANTI_ROLLBACK_9, 1, 19), + QFPROM_STD_REGION(ANTI_ROLLBACK_10, 1, 20), + QFPROM_STD_REGION(ANTI_ROLLBACK_11, 1, 21), + QFPROM_STD_REGION(ANTI_ROLLBACK_12, 1, 22), + QFPROM_STD_REGION(ANTI_ROLLBACK_13, 1, 23), + QFPROM_STD_REGION(PK_HASH_0, 8, 24), + QFPROM_STD_REGION(CALIBRATION, 54, 25), + QFPROM_STD_REGION(MEMORY_CONFIGURATION, 280, 26), + QFPROM_FEC_REGION(QC_SPARE_20, 1, 27), + QFPROM_FEC_REGION(QC_SPARE_21, 1, 28), + QFPROM_FEC_REGION(OEM_IMAGE_ENCRYPTION_KEY, 3, 29), + QFPROM_FEC_REGION(OEM_SECURE_BOOT, 2, 30), + QFPROM_FEC_REGION_MSB(SEC_KEY_DERIVATION_KEY, 5, 31), + QFPROM_FEC_REGION_MSB(IMAGE_ENCRYPTION_KEY_1, 3, 33), + QFPROM_FEC_REGION_MSB(USER_KEY_DERIVATION_KEY, 5, 34), + QFPROM_STD_REGION_MSB(OEM_SPARE_28, 2, 35), + QFPROM_STD_REGION_MSB(OEM_SPARE_29, 2, 36), + QFPROM_STD_REGION_MSB(OEM_SPARE_30, 2, 37), + QFPROM_STD_REGION_MSB(OEM_SPARE_31, 2, 38), + { + QFPROM_LAST_REGION_DUMMY, + 0, + QFPROM_FEC_NONE, + 0, + 0, + 0, + 0, + QFPROM_ROW_LSB + } +}; + +const size_t region_count = ARRAY_SIZE(region_data); + +const struct qfprom_platform_config plat_config = { + .name = "Lemans", + .qfprom_raw_base = QFPROM_RAW_BASE, + .qfprom_corr_base = QFPROM_CORR_BASE, + .qfprom_size = QFPROM_SIZE, + .region_data = region_data, + .num_regions = ARRAY_SIZE(region_data), + .read_perm_lsb_offset = QFPROM_READ_PERM_LSB_OFFSET, + .read_perm_msb_offset = QFPROM_READ_PERM_MSB_OFFSET, + .write_perm_lsb_offset = QFPROM_WRITE_PERM_LSB_OFFSET, + .write_perm_msb_offset = QFPROM_WRITE_PERM_MSB_OFFSET, +}; + +const struct qfprom_platform_config *qfprom_get_platform_config(void) +{ + return &plat_config; +} diff --git a/core/drivers/qcom/qfprom/lemans/qfprom_target.h b/core/drivers/qcom/qfprom/lemans/qfprom_target.h new file mode 100644 index 0000000000..8207330b4a --- /dev/null +++ b/core/drivers/qcom/qfprom/lemans/qfprom_target.h @@ -0,0 +1,231 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __QFPROM_TARGET_H__ +#define __QFPROM_TARGET_H__ + +#include +#include +#include +#include +#include +#include + +#define QFPROM_RAW_BASE 0x00780000 +#define QFPROM_CORR_BASE 0x00784000 +#define QFPROM_SIZE 0x4000 + +#define QFPROM_BLOW_TIMER_OFFSET 0x2030 +#define QFPROM_ACCEL_OFFSET 0x2038 + +#define QFPROM_BLOW_STATUS_OFFSET 0x203c +#define QFPROM_BLOW_STATUS_RMSK 0x3 +#define QFPROM_BLOW_STATUS_BUSY_VAL 0x1 +#define QFPROM_BLOW_STATUS_ERROR_VAL 0x2 +#define QFPROM_BLOW_STATUS_READY_VAL 0x0 + +#define QFPROM_FEC_ESR_OFFSET 0x2058 +#define QFPROM_FEC_EAR_OFFSET 0x205c +#define QFPROM_FEC_ESR_ERR_SEEN_BMSK BIT(0) +#define QFPROM_FEC_EAR_ERR_ADDR_BMSK 0xFFFF + +#define QFPROM_BLOW_TIMER_CLK_FREQ_MHZ_X10 48 +#define QFPROM_FUSE_BLOW_TIME_IN_US 5 + +#define QFPROM_GATELAST_VAL 0x1 +#define QFPROM_TRIPPT_SEL_VAL 0x4 +#define QFPROM_ACCEL_VAL 0x10 + +#define QFPROM_GATELAST_SHFT 11 +#define QFPROM_TRIPPT_SEL_SHFT 8 +#define QFPROM_ACCEL_SHFT 0 + +#define QFPROM_ACCEL_VALUE \ + ((QFPROM_GATELAST_VAL << QFPROM_GATELAST_SHFT) | \ + (QFPROM_TRIPPT_SEL_VAL << QFPROM_TRIPPT_SEL_SHFT) | \ + (QFPROM_ACCEL_VAL << QFPROM_ACCEL_SHFT)) + +#define QFPROM_ACCEL_RESET_VALUE \ + (0x1 << QFPROM_GATELAST_SHFT) + +#define QFPROM_RAW_TO_CORR(raw_addr) \ + ((raw_addr) + (QFPROM_CORR_BASE - QFPROM_RAW_BASE)) + +#define LCM_ADDR 0x00780120 +#define PRI_KEY_DERIVATION_KEY_ADDR 0x00780128 +#define MRC_2_0_ADDR 0x00780148 +#define PTE_ADDR 0x00780158 +#define READ_PERMISSION_ADDR 0x00780190 +#define WRITE_PERMISSION_ADDR 0x00780198 +#define FEC_ENABLES_ADDR 0x007801A0 +#define OEM_CONFIG_ADDR 0x007801A8 +#define FEATURE_CONFIG_M_ADDR 0x007801D0 +#define FEATURE_CONFIG_NM_ADDR 0x00780210 +#define ANTI_ROLLBACK_1_ADDR 0x00780238 +#define ANTI_ROLLBACK_2_ADDR 0x00780240 +#define ANTI_ROLLBACK_3_ADDR 0x00780248 +#define ANTI_ROLLBACK_4_ADDR 0x00780250 +#define ANTI_ROLLBACK_5_ADDR 0x00780258 +#define ANTI_ROLLBACK_6_ADDR 0x00780260 +#define ANTI_ROLLBACK_7_ADDR 0x00780268 +#define ANTI_ROLLBACK_8_ADDR 0x00780270 +#define ANTI_ROLLBACK_9_ADDR 0x00780278 +#define ANTI_ROLLBACK_10_ADDR 0x00780280 +#define ANTI_ROLLBACK_11_ADDR 0x00780288 +#define ANTI_ROLLBACK_12_ADDR 0x00780290 +#define ANTI_ROLLBACK_13_ADDR 0x00780298 +#define PK_HASH_0_ADDR 0x007802A0 +#define CALIBRATION_ADDR 0x007802E0 +#define MEMORY_CONFIGURATION_ADDR 0x00780490 +#define QC_SPARE_20_ADDR 0x00780D50 +#define QC_SPARE_21_ADDR 0x00780D58 +#define OEM_IMAGE_ENCRYPTION_KEY_ADDR 0x00780D60 +#define OEM_SECURE_BOOT_ADDR 0x00780D78 +#define SEC_KEY_DERIVATION_KEY_ADDR 0x00780D88 +#define IMAGE_ENCRYPTION_KEY_1_ADDR 0x00781150 +#define USER_KEY_DERIVATION_KEY_ADDR 0x00781168 +#define OEM_SPARE_28_ADDR 0x00781190 +#define OEM_SPARE_29_ADDR 0x007811A0 +#define OEM_SPARE_30_ADDR 0x007811B0 +#define OEM_SPARE_31_ADDR 0x007811C0 + +#define QFPROM_READ_PERM_LSB_OFFSET \ + (READ_PERMISSION_ADDR - QFPROM_RAW_BASE) +#define QFPROM_READ_PERM_MSB_OFFSET \ + (QFPROM_READ_PERM_LSB_OFFSET + 4) + +#define QFPROM_WRITE_PERM_LSB_OFFSET \ + (WRITE_PERMISSION_ADDR - QFPROM_RAW_BASE) +#define QFPROM_WRITE_PERM_MSB_OFFSET \ + (QFPROM_WRITE_PERM_LSB_OFFSET + 4) + +#define QFPROM_HW_MUTEX_ID 8 +#define QFPROM_HW_MUTEX_PID 1 +#define QFPROM_HW_MUTEX_TIMEOUT_US 10000 +#define QFPROM_MUTEX_REG_ADDR \ + (TCSR_MUTEX_BASE + (0x1000 * QFPROM_HW_MUTEX_ID)) + +enum qfprom_region_name { + QFPROM_CRI_CM_PRIVATE_REGION = 0, + QFPROM_LCM_REGION, + QFPROM_PRI_KEY_DERIVATION_KEY_REGION, + QFPROM_MRC_2_0_REGION, + QFPROM_PTE_REGION, + QFPROM_READ_PERMISSION_REGION, + QFPROM_WRITE_PERMISSION_REGION, + QFPROM_FEC_ENABLES_REGION, + QFPROM_OEM_CONFIG_REGION, + QFPROM_FEATURE_CONFIG_M_REGION, + QFPROM_FEATURE_CONFIG_NM_REGION, + QFPROM_ANTI_ROLLBACK_1_REGION, + QFPROM_ANTI_ROLLBACK_2_REGION, + QFPROM_ANTI_ROLLBACK_3_REGION, + QFPROM_ANTI_ROLLBACK_4_REGION, + QFPROM_ANTI_ROLLBACK_5_REGION, + QFPROM_ANTI_ROLLBACK_6_REGION, + QFPROM_ANTI_ROLLBACK_7_REGION, + QFPROM_ANTI_ROLLBACK_8_REGION, + QFPROM_ANTI_ROLLBACK_9_REGION, + QFPROM_ANTI_ROLLBACK_10_REGION, + QFPROM_ANTI_ROLLBACK_11_REGION, + QFPROM_ANTI_ROLLBACK_12_REGION, + QFPROM_ANTI_ROLLBACK_13_REGION, + QFPROM_PK_HASH_0_REGION, + QFPROM_CALIBRATION_REGION, + QFPROM_MEMORY_CONFIGURATION_REGION, + QFPROM_QC_SPARE_20_REGION, + QFPROM_QC_SPARE_21_REGION, + QFPROM_OEM_IMAGE_ENCRYPTION_KEY_REGION, + QFPROM_OEM_SECURE_BOOT_REGION, + QFPROM_SEC_KEY_DERIVATION_KEY_REGION, + QFPROM_BOOT_ROM_PATCH_REGION, + QFPROM_IMAGE_ENCRYPTION_KEY_1_REGION, + QFPROM_USER_KEY_DERIVATION_KEY_REGION, + QFPROM_OEM_SPARE_28_REGION, + QFPROM_OEM_SPARE_29_REGION, + QFPROM_OEM_SPARE_30_REGION, + QFPROM_OEM_SPARE_31_REGION, + QFPROM_LAST_REGION_DUMMY, +}; + +enum qfprom_perm_bit_pos { + LCM = 1, + PRI_KEY_DERIVATION_KEY = 2, + MRC_2_0 = 3, + PTE = 4, + READ_PERMISSION = 5, + WRITE_PERMISSION = 6, + FEC_ENABLES = 7, + OEM_CONFIG = 8, + FEATURE_CONFIG_M = 9, + FEATURE_CONFIG_NM = 10, + ANTI_ROLLBACK_1 = 11, + ANTI_ROLLBACK_2 = 12, + ANTI_ROLLBACK_3 = 13, + ANTI_ROLLBACK_4 = 14, + ANTI_ROLLBACK_5 = 15, + ANTI_ROLLBACK_6 = 16, + ANTI_ROLLBACK_7 = 17, + ANTI_ROLLBACK_8 = 18, + ANTI_ROLLBACK_9 = 19, + ANTI_ROLLBACK_10 = 20, + ANTI_ROLLBACK_11 = 21, + ANTI_ROLLBACK_12 = 22, + ANTI_ROLLBACK_13 = 23, + PK_HASH_0 = 24, + CALIBRATION = 25, + MEMORY_CONFIGURATION = 26, + QC_SPARE_20 = 27, + QC_SPARE_21 = 28, + OEM_IMAGE_ENCRYPTION_KEY = 29, + OEM_SECURE_BOOT = 30, + SEC_KEY_DERIVATION_KEY = 31, + IMAGE_ENCRYPTION_KEY_1 = 1, /* MSB bit 1 */ + USER_KEY_DERIVATION_KEY = 2, /* MSB bit 2 */ + OEM_SPARE_28 = 3, /* MSB bit 3 */ + OEM_SPARE_29 = 4, /* MSB bit 4 */ + OEM_SPARE_30 = 5, /* MSB bit 5 */ + OEM_SPARE_31 = 6, /* MSB bit 6 */ +}; + +#define LCM_PERM_MASK BIT(LCM) +#define PRI_KEY_DERIVATION_KEY_PERM_MASK BIT(PRI_KEY_DERIVATION_KEY) +#define MRC_2_0_PERM_MASK BIT(MRC_2_0) +#define PTE_PERM_MASK BIT(PTE) +#define READ_PERMISSION_PERM_MASK BIT(READ_PERMISSION) +#define WRITE_PERMISSION_PERM_MASK BIT(WRITE_PERMISSION) +#define FEC_ENABLES_PERM_MASK BIT(FEC_ENABLES) +#define OEM_CONFIG_PERM_MASK BIT(OEM_CONFIG) +#define FEATURE_CONFIG_M_PERM_MASK BIT(FEATURE_CONFIG_M) +#define FEATURE_CONFIG_NM_PERM_MASK BIT(FEATURE_CONFIG_NM) +#define ANTI_ROLLBACK_1_PERM_MASK BIT(ANTI_ROLLBACK_1) +#define ANTI_ROLLBACK_2_PERM_MASK BIT(ANTI_ROLLBACK_2) +#define ANTI_ROLLBACK_3_PERM_MASK BIT(ANTI_ROLLBACK_3) +#define ANTI_ROLLBACK_4_PERM_MASK BIT(ANTI_ROLLBACK_4) +#define ANTI_ROLLBACK_5_PERM_MASK BIT(ANTI_ROLLBACK_5) +#define ANTI_ROLLBACK_6_PERM_MASK BIT(ANTI_ROLLBACK_6) +#define ANTI_ROLLBACK_7_PERM_MASK BIT(ANTI_ROLLBACK_7) +#define ANTI_ROLLBACK_8_PERM_MASK BIT(ANTI_ROLLBACK_8) +#define ANTI_ROLLBACK_9_PERM_MASK BIT(ANTI_ROLLBACK_9) +#define ANTI_ROLLBACK_10_PERM_MASK BIT(ANTI_ROLLBACK_10) +#define ANTI_ROLLBACK_11_PERM_MASK BIT(ANTI_ROLLBACK_11) +#define ANTI_ROLLBACK_12_PERM_MASK BIT(ANTI_ROLLBACK_12) +#define ANTI_ROLLBACK_13_PERM_MASK BIT(ANTI_ROLLBACK_13) +#define PK_HASH_0_PERM_MASK BIT(PK_HASH_0) +#define CALIBRATION_PERM_MASK BIT(CALIBRATION) +#define MEMORY_CONFIGURATION_PERM_MASK BIT(MEMORY_CONFIGURATION) +#define QC_SPARE_20_PERM_MASK BIT(QC_SPARE_20) +#define QC_SPARE_21_PERM_MASK BIT(QC_SPARE_21) +#define OEM_IMAGE_ENCRYPTION_KEY_PERM_MASK BIT(OEM_IMAGE_ENCRYPTION_KEY) +#define OEM_SECURE_BOOT_PERM_MASK BIT(OEM_SECURE_BOOT) +#define SEC_KEY_DERIVATION_KEY_PERM_MASK BIT(SEC_KEY_DERIVATION_KEY) +#define IMAGE_ENCRYPTION_KEY_1_PERM_MASK BIT(IMAGE_ENCRYPTION_KEY_1) +#define USER_KEY_DERIVATION_KEY_PERM_MASK BIT(USER_KEY_DERIVATION_KEY) +#define OEM_SPARE_28_PERM_MASK BIT(OEM_SPARE_28) +#define OEM_SPARE_29_PERM_MASK BIT(OEM_SPARE_29) +#define OEM_SPARE_30_PERM_MASK BIT(OEM_SPARE_30) +#define OEM_SPARE_31_PERM_MASK BIT(OEM_SPARE_31) + +#endif /* __QFPROM_TARGET_H__ */ diff --git a/core/drivers/qcom/qfprom/qfprom_core.c b/core/drivers/qcom/qfprom/qfprom_core.c index 4d26a209bd..54deabd6f8 100644 --- a/core/drivers/qcom/qfprom/qfprom_core.c +++ b/core/drivers/qcom/qfprom/qfprom_core.c @@ -404,7 +404,8 @@ TEE_Result qfprom_hw_init(void) return TEE_SUCCESS; err_disable_voltage: - qfprom_disable_voltage(); + if (qfprom_disable_voltage() != TEE_SUCCESS) + EMSG("Failed to disable voltage"); err_unlock: mutex_unlock(&drv->lock); qfprom_release_hw_mutex(); @@ -417,7 +418,8 @@ void qfprom_hw_deinit(void) drv->write_op_allowed = false; qfprom_write_reset_clock_settings(); - qfprom_disable_voltage(); + if (qfprom_disable_voltage() != TEE_SUCCESS) + EMSG("Failed to disable voltage"); mutex_unlock(&drv->lock); qfprom_release_hw_mutex(); } diff --git a/core/drivers/qcom/qfprom/qfprom_priv.h b/core/drivers/qcom/qfprom/qfprom_priv.h index 143988f791..0fabbdf7e9 100644 --- a/core/drivers/qcom/qfprom/qfprom_priv.h +++ b/core/drivers/qcom/qfprom/qfprom_priv.h @@ -7,6 +7,7 @@ #define __QFPROM_PRIV_H__ #include +#include #include #include diff --git a/core/drivers/qcom/qfprom/qfprom_target.c b/core/drivers/qcom/qfprom/qfprom_target.c index 23e69bde57..f89246bcc4 100644 --- a/core/drivers/qcom/qfprom/qfprom_target.c +++ b/core/drivers/qcom/qfprom/qfprom_target.c @@ -20,14 +20,16 @@ #include "qfprom_hal.h" #include "qfprom_priv.h" -static struct rpmh_client *rpmh_handle; - TEE_Result qfprom_write_set_clock_settings(void) { + struct io_pa_va base = { .pa = GCC_BASE }; + vaddr_t gcc_base = io_pa_or_va(&base, GCC_SIZE); + vaddr_t cfg_rcgr = gcc_base + GCC_SEC_CTRL_CFG_RCGR; + vaddr_t cmd_rcgr = gcc_base + GCC_SEC_CTRL_CMD_RCGR; uint32_t blow_timer_value; TEE_Result res; - res = qcom_clock_enable(QCOM_CLKS_QFPROM); + res = qcom_clock_set_rate(cfg_rcgr, cmd_rcgr, QFPROM_CLOCK_DIVIDE); if (res != TEE_SUCCESS) return res; @@ -42,9 +44,13 @@ TEE_Result qfprom_write_set_clock_settings(void) TEE_Result qfprom_write_reset_clock_settings(void) { + struct io_pa_va base = { .pa = GCC_BASE }; + vaddr_t gcc_base = io_pa_or_va(&base, GCC_SIZE); + vaddr_t cfg_rcgr = gcc_base + GCC_SEC_CTRL_CFG_RCGR; + vaddr_t cmd_rcgr = gcc_base + GCC_SEC_CTRL_CMD_RCGR; TEE_Result res; - res = qcom_clock_disable(QCOM_CLKS_QFPROM); + res = qcom_clock_set_rate(cfg_rcgr, cmd_rcgr, 0); if (res != TEE_SUCCESS) return res; @@ -55,6 +61,9 @@ TEE_Result qfprom_write_reset_clock_settings(void) return TEE_SUCCESS; } +#ifdef CFG_QFPROM_MX_RAIL_WA +static struct rpmh_client *rpmh_handle; + TEE_Result qfprom_enable_voltage(void) { uint32_t vrm_addr; @@ -117,6 +126,17 @@ TEE_Result qfprom_disable_voltage(void) return TEE_SUCCESS; } +#else +TEE_Result qfprom_enable_voltage(void) +{ + return TEE_SUCCESS; +} + +TEE_Result qfprom_disable_voltage(void) +{ + return TEE_SUCCESS; +} +#endif /* CFG_QFPROM_MX_RAIL_WA */ TEE_Result qfprom_acquire_hw_mutex(void) { diff --git a/core/include/drivers/qcom/qfprom/qfprom.h b/core/include/drivers/qcom/qfprom/qfprom.h index 68be58f35e..0068d32f58 100644 --- a/core/include/drivers/qcom/qfprom/qfprom.h +++ b/core/include/drivers/qcom/qfprom/qfprom.h @@ -31,43 +31,6 @@ enum qfprom_error { QFPROM_ERROR_TIMEOUT = 0x11, }; -enum qfprom_region_name { - QFPROM_CRI_CM_PRIVATE_REGION = 0, - QFPROM_LCM_REGION, - QFPROM_PRI_KEY_DERIVATION_KEY_REGION, - QFPROM_CM_FEATURE_CONFIG_REGION, - QFPROM_MRC_2_0_REGION, - QFPROM_PTE_REGION, - QFPROM_READ_PERMISSION_REGION, - QFPROM_WRITE_PERMISSION_REGION, - QFPROM_FEC_ENABLES_REGION, - QFPROM_OEM_CONFIG_REGION, - QFPROM_FEATURE_CONFIG_M_REGION, - QFPROM_FEATURE_CONFIG_NM_REGION, - QFPROM_ANTI_ROLLBACK_1_REGION, - QFPROM_ANTI_ROLLBACK_2_REGION, - QFPROM_ANTI_ROLLBACK_3_REGION, - QFPROM_ANTI_ROLLBACK_4_REGION, - QFPROM_ANTI_ROLLBACK_5_REGION, - QFPROM_PK_HASH_0_REGION, - QFPROM_CALIBRATION_REGION, - QFPROM_MEMORY_CONFIGURATION_REGION, - QFPROM_QC_SPARE_20_REGION, - QFPROM_QC_SPARE_21_REGION, - QFPROM_OEM_IMAGE_ENCRYPTION_KEY_REGION, - QFPROM_OEM_SECURE_BOOT_REGION, - QFPROM_SEC_KEY_DERIVATION_KEY_REGION, - QFPROM_BOOT_ROM_PATCH_REGION, - QFPROM_IMAGE_ENCRYPTION_KEY_1_REGION, - QFPROM_USER_KEY_DERIVATION_KEY_REGION, - QFPROM_OEM_SPARE_28_REGION, - QFPROM_OEM_SPARE_29_REGION, - QFPROM_OEM_SPARE_30_REGION, - QFPROM_OEM_SPARE_31_REGION, - QFPROM_LAST_REGION_DUMMY, - QFPROM_MAX_REGION_ENUM = 0x7FFF -}; - /* Read QFPROM row data */ TEE_Result qfprom_read_row(uint32_t addr, enum qfprom_addr_space type, From 0e1beb191e19829868fae4bcc6ca0acfaedf9b6a Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Tue, 24 Mar 2026 16:42:23 +0530 Subject: [PATCH 07/14] plat-qcom: kodiak: enable QFPROM Fuse Provisioning Enable QFPROM Fuse Provisioning support for Kodiak platform along with its required dependencies (CMD_DB, RPMH_CLIENT, QFPROM drivers). The drivers are conditionally enabled based on CFG_QCOM_QFPROM_FUSEPROV, which defaults to 'y' for secure builds and 'n' for insecure builds. This ensures the fuse provisioning infrastructure is only included when needed, reducing the attack surface in production builds. Note: Actual fuse programming operations require CFG_QFPROM_PROGRAMMING to be explicitly enabled. This config is disabled by default as a safety measure and must be enabled during provisioning to allow write operations to the OTP fuses. Platform configuration additions: - AOP MSG RAM base and size for CMD_DB metadata - RPMH RSC base address for power management - Security Control base for QFPROM access - TCSR Hardware Mutex for synchronization - Secure ELF DDR region for fuse provisioning data Signed-off-by: Selvam Sathappan Periakaruppan --- core/arch/arm/plat-qcom/conf.mk | 10 +++++++++- core/arch/arm/plat-qcom/platform_config.h | 23 +++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/core/arch/arm/plat-qcom/conf.mk b/core/arch/arm/plat-qcom/conf.mk index 7e9bec1baa..47ae065c8b 100644 --- a/core/arch/arm/plat-qcom/conf.mk +++ b/core/arch/arm/plat-qcom/conf.mk @@ -41,6 +41,15 @@ endif ifneq (,$(filter $(PLATFORM_FLAVOR),kodiak)) CFG_DRIVERS_CLK ?= y CFG_QCOM_PAS_PTA ?= y + +# QFPROM Fuse Provisioning: disabled in insecure mode, enabled otherwise +CFG_QCOM_QFPROM_FUSEPROV ?= $(if $(filter y,$(CFG_INSECURE)),n,y) + +# QFPROM dependencies: all enabled/disabled together based on FUSEPROV +_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) endif ifeq ($(CFG_QCOM_PAS_PTA),y) @@ -49,4 +58,3 @@ CFG_RESERVED_VASPACE_SIZE ?= (60 * 1024 * 1024) $(call force,CFG_DRIVERS_QCOM_CLK,y,Mandated by CFG_QCOM_PAS_PTA) CFG_IN_TREE_EARLY_TAS += qcom_pas/cff7d191-7ca0-4784-af13-48223b9a4fbe endif - diff --git a/core/arch/arm/plat-qcom/platform_config.h b/core/arch/arm/plat-qcom/platform_config.h index 4b10bc4706..1f80d55e39 100644 --- a/core/arch/arm/plat-qcom/platform_config.h +++ b/core/arch/arm/plat-qcom/platform_config.h @@ -12,6 +12,9 @@ /* Make stacks aligned to data cache line length */ #define STACK_ALIGNMENT 64 +#define MAX_XLAT_TABLES (40 + (CFG_RESERVED_VASPACE_SIZE) / \ + (CORE_MMU_PGDIR_SIZE) + 5) + #if defined(PLATFORM_FLAVOR_kodiak) || defined(PLATFORM_FLAVOR_lemans) /* GIC related constants */ #define GICD_BASE UL(0x17a00000) @@ -42,6 +45,26 @@ #define LPASS_SIZE ULL(0x01080000) #define IRIS_BASE UL(0x0aa00000) #define IRIS_SIZE ULL(0x00200000) + +/* AOP MSG RAM */ +#define AOP_MSG_RAM_BASE UL(0x0C300000) +#define AOP_MSG_RAM_SIZE UL(0x00100000) + +/* RPMH RSC base address */ +#define RPMH_BASE_ADDR UL(0x18200000) +#define RPMH_RSC_SIZE UL(0x40000) + +/* QFPROM and Security Control */ +#define SECURITY_CONTROL_BASE UL(0x00780000) +#define SECURITY_CONTROL_SIZE UL(0x10000) + +/* TCSR Hardware Mutex */ +#define TCSR_MUTEX_BASE UL(0x01F40000) +#define TCSR_MUTEX_SIZE UL(0x40000) + +/* QFPROM Fuse Provisioning */ +#define CFG_SEC_ELF_DDR_ADDR UL(0x808FF000) +#define CFG_SEC_ELF_DDR_SIZE UL(0x1000) #endif #if defined(PLATFORM_FLAVOR_lemans) From a242357c4f5e36e2677fbe534e993ae0fa6ac3f0 Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Sat, 18 Apr 2026 09:20:04 +0530 Subject: [PATCH 08/14] plat-qcom: lemans: enable QFPROM Fuse Provisioning Enable QFPROM Fuse Provisioning support for Lemans platform with required driver dependencies (CMD_DB, RPMH, QFPROM, CLK). Move common platform definitions to shared section and organize platform-specific definitions (GCC_BASE) into respective platform sections. Signed-off-by: Selvam Sathappan Periakaruppan --- core/arch/arm/plat-qcom/conf.mk | 12 ++++-- core/arch/arm/plat-qcom/platform_config.h | 51 ++++++++++++++--------- 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/core/arch/arm/plat-qcom/conf.mk b/core/arch/arm/plat-qcom/conf.mk index 47ae065c8b..76d3baab03 100644 --- a/core/arch/arm/plat-qcom/conf.mk +++ b/core/arch/arm/plat-qcom/conf.mk @@ -36,11 +36,10 @@ CFG_TEE_RAM_VA_SIZE ?= 0x200000 CFG_TA_RAM_VA_SIZE ?= 0x1c00000 CFG_TZDRAM_SIZE ?= (CFG_TEE_RAM_VA_SIZE + CFG_TA_RAM_VA_SIZE) CFG_NUM_THREADS ?= 8 -endif -ifneq (,$(filter $(PLATFORM_FLAVOR),kodiak)) +# Clock driver CFG_DRIVERS_CLK ?= y -CFG_QCOM_PAS_PTA ?= y +CFG_DRIVERS_QCOM_CLK ?= y # QFPROM Fuse Provisioning: disabled in insecure mode, enabled otherwise CFG_QCOM_QFPROM_FUSEPROV ?= $(if $(filter y,$(CFG_INSECURE)),n,y) @@ -52,9 +51,14 @@ CFG_QCOM_RPMH_CLIENT ?= $(_qcom_fuseprov_deps) CFG_QCOM_QFPROM ?= $(_qcom_fuseprov_deps) endif +ifneq (,$(filter $(PLATFORM_FLAVOR),kodiak)) +CFG_QCOM_PAS_PTA ?= y +# Kodiak requires MX voltage rail workaround for QFPROM fuse blowing +$(call force,CFG_QFPROM_MX_RAIL_WA,y) +endif + ifeq ($(CFG_QCOM_PAS_PTA),y) # Increase late mappings to cover all PAS resources CFG_RESERVED_VASPACE_SIZE ?= (60 * 1024 * 1024) -$(call force,CFG_DRIVERS_QCOM_CLK,y,Mandated by CFG_QCOM_PAS_PTA) CFG_IN_TREE_EARLY_TAS += qcom_pas/cff7d191-7ca0-4784-af13-48223b9a4fbe endif diff --git a/core/arch/arm/plat-qcom/platform_config.h b/core/arch/arm/plat-qcom/platform_config.h index 1f80d55e39..76e16b9316 100644 --- a/core/arch/arm/plat-qcom/platform_config.h +++ b/core/arch/arm/plat-qcom/platform_config.h @@ -22,9 +22,33 @@ #define RAMBLUR_PIMEM_REG_BASE UL(0x610000) #define SEC_PRNG_REG_BASE UL(0x010D1000) + +/* AOP MSG RAM */ +#define AOP_MSG_RAM_BASE UL(0x0C300000) +#define AOP_MSG_RAM_SIZE UL(0x00100000) + +/* RPMH RSC base address */ +#define RPMH_BASE_ADDR UL(0x18200000) +#define RPMH_RSC_SIZE UL(0x40000) + +/* QFPROM and Security Control */ +#define SECURITY_CONTROL_BASE UL(0x00780000) +#define SECURITY_CONTROL_SIZE UL(0x10000) + +/* TCSR Hardware Mutex */ +#define TCSR_MUTEX_BASE UL(0x01F40000) +#define TCSR_MUTEX_SIZE UL(0x40000) #endif #if defined(PLATFORM_FLAVOR_kodiak) +/* Clock controller */ +#define GCC_BASE UL(0x100000) +#define GCC_SIZE UL(0x100000) + +/* QFPROM Fuse Provisioning */ +#define CFG_SEC_ELF_DDR_ADDR UL(0x808FF000) +#define CFG_SEC_ELF_DDR_SIZE UL(0x1000) + #define DRAM0_BASE UL(0x80000000) #define DRAM0_SIZE UL(0x80000000) #define DRAM1_BASE ULL(0x100000000) @@ -35,8 +59,7 @@ #define RAMBLUR_PIMEM_VAULT_TA_SIZE ULL(0x01c00000) #define GENI_UART_REG_BASE UL(0x994000) -#define GCC_BASE UL(0x100000) -#define GCC_SIZE UL(0x100000) + #define WPSS_BASE UL(0x8a00000) #define WPSS_SIZE UL(0x200000) #define TURING_BASE UL(0x09800000) @@ -45,29 +68,17 @@ #define LPASS_SIZE ULL(0x01080000) #define IRIS_BASE UL(0x0aa00000) #define IRIS_SIZE ULL(0x00200000) +#endif -/* AOP MSG RAM */ -#define AOP_MSG_RAM_BASE UL(0x0C300000) -#define AOP_MSG_RAM_SIZE UL(0x00100000) - -/* RPMH RSC base address */ -#define RPMH_BASE_ADDR UL(0x18200000) -#define RPMH_RSC_SIZE UL(0x40000) - -/* QFPROM and Security Control */ -#define SECURITY_CONTROL_BASE UL(0x00780000) -#define SECURITY_CONTROL_SIZE UL(0x10000) - -/* TCSR Hardware Mutex */ -#define TCSR_MUTEX_BASE UL(0x01F40000) -#define TCSR_MUTEX_SIZE UL(0x40000) +#if defined(PLATFORM_FLAVOR_lemans) +/* Clock controller */ +#define GCC_BASE UL(0x110000) +#define GCC_SIZE UL(0x100000) /* QFPROM Fuse Provisioning */ -#define CFG_SEC_ELF_DDR_ADDR UL(0x808FF000) +#define CFG_SEC_ELF_DDR_ADDR UL(0x908FF000) #define CFG_SEC_ELF_DDR_SIZE UL(0x1000) -#endif -#if defined(PLATFORM_FLAVOR_lemans) #define DRAM0_BASE UL(0x80000000) #define DRAM0_SIZE UL(0x380000000) #define DRAM1_BASE ULL(0x800000000) From 18983664851ce48c9105e9e7c819deaea397ff75 Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Tue, 14 Apr 2026 23:31:51 +0530 Subject: [PATCH 09/14] plat-qcom: Add support for diagnostic ring buffer Implement a diagnostic ring buffer driver that preserves crash logs in IMEM across reboots for post-mortem analysis. The driver provides a write-only circular buffer that captures console output during normal operation and crash scenarios. Key features: - Write-only circular buffer in IMEM for crash log preservation - DLOAD mode detection to disable logging during download mode - Automatic buffer wraparound with overflow tracking Signed-off-by: Selvam Sathappan Periakaruppan Signed-off-by: Jorge Ramirez-Ortiz --- core/arch/arm/plat-qcom/diag_log.c | 133 +++++++++++++++++++++++++++++ core/arch/arm/plat-qcom/diag_log.h | 23 +++++ core/arch/arm/plat-qcom/sub.mk | 1 + 3 files changed, 157 insertions(+) create mode 100644 core/arch/arm/plat-qcom/diag_log.c create mode 100644 core/arch/arm/plat-qcom/diag_log.h diff --git a/core/arch/arm/plat-qcom/diag_log.c b/core/arch/arm/plat-qcom/diag_log.c new file mode 100644 index 0000000000..0594adc13b --- /dev/null +++ b/core/arch/arm/plat-qcom/diag_log.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: BSD-2-Clause +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "diag_log.h" + +register_phys_mem_pgdir(MEM_AREA_IO_SEC, DIAG_BASE, DIAG_SIZE); +register_phys_mem_pgdir(MEM_AREA_IO_SEC, + (DIAG_LOG_START_INFO & ~SMALL_PAGE_MASK), + SMALL_PAGE_SIZE); +register_phys_mem_pgdir(MEM_AREA_IO_SEC, + (TCSR_BOOT_MISC_DETECT & ~SMALL_PAGE_MASK), + SMALL_PAGE_SIZE); + +#define DIAG_VERSION_V1 1 +#define DIAG_MAGIC_INIT 0x47414944 +#define DIAG_MAGIC_FAILED 0xDEADBEEF +#define DIAG_MAGIC_DLOAD 0xD15AB1ED +#define DLOAD_MAGIC_COOKIE 0x10 + +struct diag_hdr { + uint32_t version; + uint32_t magic; +}; + +struct diag_conf { + uint32_t buf_offset; + uint32_t buf_size; +}; + +struct circ_wo_buf { + uint32_t wrap; + uint32_t head; + uint8_t buf[]; +}; + +struct diag { + struct diag_hdr hdr; + struct diag_conf conf; + struct circ_wo_buf wo_cbuf; +}; + +static struct diag *global_diag; + +static struct diag *get_diag_region(void) +{ + struct diag *diag; + uint32_t *tcsr_reg; + + tcsr_reg = phys_to_virt(TCSR_BOOT_MISC_DETECT, MEM_AREA_IO_SEC, + sizeof(uint32_t)); + diag = phys_to_virt(DIAG_BASE, MEM_AREA_IO_SEC, DIAG_SIZE); + + if (!tcsr_reg || !diag) { + EMSG("DIAG: Failed to map regions"); + return NULL; + } + + if (io_read32((vaddr_t)tcsr_reg) == DLOAD_MAGIC_COOKIE) { + diag->hdr.magic = DIAG_MAGIC_DLOAD; + dsb(); + return NULL; + } + + return diag; +} + +void qcom_diag_log_init(void) +{ + struct diag *diag; + uint32_t *diag_info_addr; + + if (!IS_ENABLED(CFG_QCOM_DIAG_LOG)) { + IMSG("DIAG: Feature not available"); + return; + } + + diag = get_diag_region(); + if (!diag) + return; + + memset(diag, 0, DIAG_SIZE); + + diag->hdr.version = DIAG_VERSION_V1; + diag->hdr.magic = DIAG_MAGIC_INIT; + diag->conf.buf_offset = offsetof(struct diag, wo_cbuf.buf); + diag->conf.buf_size = ROUNDDOWN2(DIAG_SIZE - diag->conf.buf_offset, 16); + + if (diag->conf.buf_offset >= DIAG_SIZE || diag->conf.buf_size == 0) { + EMSG("DIAG: Invalid buffer configuration (offset=%u, size=%u)", + diag->conf.buf_offset, diag->conf.buf_size); + diag->hdr.magic = DIAG_MAGIC_FAILED; + return; + } + + diag_info_addr = phys_to_virt(DIAG_LOG_START_INFO, MEM_AREA_IO_SEC, + 2 * sizeof(uint32_t)); + if (diag_info_addr) { + io_write32((vaddr_t)&diag_info_addr[0], DIAG_BASE); + io_write32((vaddr_t)&diag_info_addr[1], DIAG_SIZE); + } + + dsb(); + + global_diag = diag; +} + +void qcom_diag_log_puts(const char *str) +{ + struct diag *diag = global_diag; + const char *p; + + if (!diag || !str) + return; + + for (p = str; *p; p++) { + diag->wo_cbuf.buf[diag->wo_cbuf.head++] = *p; + if (diag->wo_cbuf.head >= diag->conf.buf_size) { + diag->wo_cbuf.head = 0; + if (diag->wo_cbuf.wrap < UINT32_MAX) + diag->wo_cbuf.wrap++; + } + } +} diff --git a/core/arch/arm/plat-qcom/diag_log.h b/core/arch/arm/plat-qcom/diag_log.h new file mode 100644 index 0000000000..c92bb8ebc3 --- /dev/null +++ b/core/arch/arm/plat-qcom/diag_log.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef __DIAG_LOG_H +#define __DIAG_LOG_H + +#include +#include +#include +#include + +#define IMEM_DIAG_OFFSET UL(0x720) +#define DIAG_LOG_START_INFO (IMEM_BASE + IMEM_DIAG_OFFSET) +#define TCSR_BOOT_MISC_DETECT UL(0x1FD3000) +#define DIAG_SIZE UL(0x3000) +#define DIAG_BASE (IMEM_BASE + IMEM_SIZE - DIAG_SIZE) + +void qcom_diag_log_init(void); +void qcom_diag_log_puts(const char *str); + +#endif /* __DIAG_LOG_H */ diff --git a/core/arch/arm/plat-qcom/sub.mk b/core/arch/arm/plat-qcom/sub.mk index 8ddc2fd45c..4409e3606e 100644 --- a/core/arch/arm/plat-qcom/sub.mk +++ b/core/arch/arm/plat-qcom/sub.mk @@ -1,2 +1,3 @@ global-incdirs-y += . srcs-y += main.c +srcs-y += diag_log.c From 0c5896938622cfc1a5aa1aaf2ca10363191258d4 Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Tue, 14 Apr 2026 23:42:01 +0530 Subject: [PATCH 10/14] plat-qcom: Integrate diagnostic ring buffer Integrate the diagnostic ring buffer driver into the platform: - Add IMEM memory region definitions (platform_config.h) * Kodiak: 0x14680000, size 0x19000 * Lemans: 0x14680000, size 0x32000 - Enable CFG_QCOM_DIAG_LOG in debug builds (conf.mk) * Automatically enabled when CFG_TEE_CORE_DEBUG=y - Hook into trace infrastructure * Initialize DIAG during plat_trace_init() * Mirror console output via plat_trace_ext_puts() This enables crash log preservation for debugging production issues when debug builds are deployed. Signed-off-by: Selvam Sathappan Periakaruppan --- core/arch/arm/plat-qcom/conf.mk | 2 ++ core/arch/arm/plat-qcom/main.c | 12 ++++++++++++ core/arch/arm/plat-qcom/platform_config.h | 8 ++++++++ 3 files changed, 22 insertions(+) diff --git a/core/arch/arm/plat-qcom/conf.mk b/core/arch/arm/plat-qcom/conf.mk index 76d3baab03..fa81ae69a0 100644 --- a/core/arch/arm/plat-qcom/conf.mk +++ b/core/arch/arm/plat-qcom/conf.mk @@ -31,6 +31,8 @@ CFG_QCOM_RAMBLUR_TA_WINDOW_ID ?= 2 $(call force,CFG_QCOM_PRNG,y) +CFG_QCOM_DIAG_LOG ?= $(if $(filter y,$(CFG_TEE_CORE_DEBUG)),y,n) + CFG_TZDRAM_START ?= 0x1c300000 CFG_TEE_RAM_VA_SIZE ?= 0x200000 CFG_TA_RAM_VA_SIZE ?= 0x1c00000 diff --git a/core/arch/arm/plat-qcom/main.c b/core/arch/arm/plat-qcom/main.c index 3ed3670c22..b620c150eb 100644 --- a/core/arch/arm/plat-qcom/main.c +++ b/core/arch/arm/plat-qcom/main.c @@ -11,6 +11,8 @@ #include #include +#include "diag_log.h" + /* * Register the physical memory area for peripherals etc. Here we are * registering the UART console. @@ -28,6 +30,16 @@ register_ddr(DRAM1_BASE, DRAM1_SIZE); static struct qcom_geni_uart_data console_data; +void plat_trace_ext_puts(const char *str) +{ + qcom_diag_log_puts(str); +} + +void plat_trace_init(void) +{ + qcom_diag_log_init(); +} + void plat_console_init(void) { qcom_geni_uart_init(&console_data, GENI_UART_REG_BASE); diff --git a/core/arch/arm/plat-qcom/platform_config.h b/core/arch/arm/plat-qcom/platform_config.h index 76e16b9316..44ef1dd56e 100644 --- a/core/arch/arm/plat-qcom/platform_config.h +++ b/core/arch/arm/plat-qcom/platform_config.h @@ -60,6 +60,10 @@ #define GENI_UART_REG_BASE UL(0x994000) +/* IMEM and Diagnostic buffer */ +#define IMEM_BASE UL(0x14680000) +#define IMEM_SIZE UL(0x19000) + #define WPSS_BASE UL(0x8a00000) #define WPSS_SIZE UL(0x200000) #define TURING_BASE UL(0x09800000) @@ -89,6 +93,10 @@ #define RAMBLUR_PIMEM_VAULT_TA_SIZE ULL(0x01c00000) #define GENI_UART_REG_BASE UL(0xa8c000) + +/* IMEM and Diagnostic buffer */ +#define IMEM_BASE UL(0x14680000) +#define IMEM_SIZE UL(0x32000) #endif #define PAS_ID_QDSP6 1 From f7a90334527d1c5e9a4356de98b761c5da02b2d7 Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Sun, 26 Apr 2026 22:40:45 +0530 Subject: [PATCH 11/14] plat-qcom: Refactor configuration into architecture-based hierarchy Introduce an architecture-centric structure for Qualcomm platform configuration to improve scalability and maintainability. Chipsets are now grouped by architecture family (e.g., Kodiak and Lemans under HOYA). Configuration is split into three levels: 1. Platform-wide (conf.mk): Maps chipsets to architecture families 2. Architecture-level (hoya/arch.mk, hoya/arch_config.h): Base platform requirements common across all chipsets in the family (CPU, cores, TZDRAM, RAMBLUR, PRNG) 3. Chipset-specific (hoya/kodiak/target.mk, hoya/lemans/target.mk): Advanced features like clock drivers, diagnostic logging, and fuse provisioning The conf.mk uses variable-based includes to automatically load the correct architecture and chipset configurations, eliminating conditional logic from architecture files. This structure allows new chipsets to start with minimal base platform support and incrementally add advanced features. It also simplifies adding new architecture families in the future. No functional changes - pure refactoring with identical configuration values and hardware addresses. Signed-off-by: Selvam Sathappan Periakaruppan --- core/arch/arm/plat-qcom/conf.mk | 48 ++-------- core/arch/arm/plat-qcom/hoya/arch.mk | 15 +++ core/arch/arm/plat-qcom/hoya/arch_config.h | 28 ++++++ core/arch/arm/plat-qcom/hoya/kodiak/target.mk | 21 +++++ .../arm/plat-qcom/hoya/kodiak/target_config.h | 43 +++++++++ core/arch/arm/plat-qcom/hoya/lemans/target.mk | 11 +++ .../arm/plat-qcom/hoya/lemans/target_config.h | 29 ++++++ core/arch/arm/plat-qcom/platform_config.h | 91 +------------------ core/arch/arm/plat-qcom/sub.mk | 2 + 9 files changed, 160 insertions(+), 128 deletions(-) create mode 100644 core/arch/arm/plat-qcom/hoya/arch.mk create mode 100644 core/arch/arm/plat-qcom/hoya/arch_config.h create mode 100644 core/arch/arm/plat-qcom/hoya/kodiak/target.mk create mode 100644 core/arch/arm/plat-qcom/hoya/kodiak/target_config.h create mode 100644 core/arch/arm/plat-qcom/hoya/lemans/target.mk create mode 100644 core/arch/arm/plat-qcom/hoya/lemans/target_config.h diff --git a/core/arch/arm/plat-qcom/conf.mk b/core/arch/arm/plat-qcom/conf.mk index fa81ae69a0..ec21b872a5 100644 --- a/core/arch/arm/plat-qcom/conf.mk +++ b/core/arch/arm/plat-qcom/conf.mk @@ -22,45 +22,15 @@ CFG_QCOM_GENI_UART_RDY_WAIT_USEC ?= 1000 ta-targets = ta_arm64 supported-ta-targets ?= ta_arm64 -ifneq (,$(filter $(PLATFORM_FLAVOR),kodiak lemans)) -include core/arch/arm/cpu/cortex-armv8-0.mk -$(call force,CFG_TEE_CORE_NB_CORE,8) +# Architecture family mapping +HOYA_ARCH_CHIPSETS := kodiak lemans -$(call force,CFG_QCOM_RAMBLUR_PIMEM_V3,y) -CFG_QCOM_RAMBLUR_TA_WINDOW_ID ?= 2 - -$(call force,CFG_QCOM_PRNG,y) - -CFG_QCOM_DIAG_LOG ?= $(if $(filter y,$(CFG_TEE_CORE_DEBUG)),y,n) - -CFG_TZDRAM_START ?= 0x1c300000 -CFG_TEE_RAM_VA_SIZE ?= 0x200000 -CFG_TA_RAM_VA_SIZE ?= 0x1c00000 -CFG_TZDRAM_SIZE ?= (CFG_TEE_RAM_VA_SIZE + CFG_TA_RAM_VA_SIZE) -CFG_NUM_THREADS ?= 8 - -# Clock driver -CFG_DRIVERS_CLK ?= y -CFG_DRIVERS_QCOM_CLK ?= y - -# QFPROM Fuse Provisioning: disabled in insecure mode, enabled otherwise -CFG_QCOM_QFPROM_FUSEPROV ?= $(if $(filter y,$(CFG_INSECURE)),n,y) - -# QFPROM dependencies: all enabled/disabled together based on FUSEPROV -_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) -endif - -ifneq (,$(filter $(PLATFORM_FLAVOR),kodiak)) -CFG_QCOM_PAS_PTA ?= y -# Kodiak requires MX voltage rail workaround for QFPROM fuse blowing -$(call force,CFG_QFPROM_MX_RAIL_WA,y) +ifneq (,$(filter $(PLATFORM_FLAVOR),$(HOYA_ARCH_CHIPSETS))) +QCOM_ARCH_FAMILY := hoya +else +$(error Unsupported PLATFORM_FLAVOR: $(PLATFORM_FLAVOR)) endif -ifeq ($(CFG_QCOM_PAS_PTA),y) -# Increase late mappings to cover all PAS resources -CFG_RESERVED_VASPACE_SIZE ?= (60 * 1024 * 1024) -CFG_IN_TREE_EARLY_TAS += qcom_pas/cff7d191-7ca0-4784-af13-48223b9a4fbe -endif +# Include architecture-specific configuration +include core/arch/arm/plat-qcom/$(QCOM_ARCH_FAMILY)/arch.mk +include core/arch/arm/plat-qcom/$(QCOM_ARCH_FAMILY)/$(PLATFORM_FLAVOR)/target.mk diff --git a/core/arch/arm/plat-qcom/hoya/arch.mk b/core/arch/arm/plat-qcom/hoya/arch.mk new file mode 100644 index 0000000000..9ee039dde0 --- /dev/null +++ b/core/arch/arm/plat-qcom/hoya/arch.mk @@ -0,0 +1,15 @@ +# HOYA architecture configuration + +include core/arch/arm/cpu/cortex-armv8-0.mk +$(call force,CFG_TEE_CORE_NB_CORE,8) + +$(call force,CFG_QCOM_RAMBLUR_PIMEM_V3,y) +CFG_QCOM_RAMBLUR_TA_WINDOW_ID ?= 2 + +$(call force,CFG_QCOM_PRNG,y) + +CFG_TZDRAM_START ?= 0x1c300000 +CFG_TEE_RAM_VA_SIZE ?= 0x200000 +CFG_TA_RAM_VA_SIZE ?= 0x1c00000 +CFG_TZDRAM_SIZE ?= (CFG_TEE_RAM_VA_SIZE + CFG_TA_RAM_VA_SIZE) +CFG_NUM_THREADS ?= 8 diff --git a/core/arch/arm/plat-qcom/hoya/arch_config.h b/core/arch/arm/plat-qcom/hoya/arch_config.h new file mode 100644 index 0000000000..b0459613b8 --- /dev/null +++ b/core/arch/arm/plat-qcom/hoya/arch_config.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2024, Linaro Limited + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef ARCH_CONFIG_H +#define ARCH_CONFIG_H + +#define GICD_BASE UL(0x17a00000) +#define GICR_BASE UL(0x17a60000) + +#define RAMBLUR_PIMEM_REG_BASE UL(0x610000) +#define SEC_PRNG_REG_BASE UL(0x010D1000) + +#define AOP_MSG_RAM_BASE UL(0x0C300000) +#define AOP_MSG_RAM_SIZE UL(0x00100000) + +#define RPMH_BASE_ADDR UL(0x18200000) +#define RPMH_RSC_SIZE UL(0x40000) + +#define SECURITY_CONTROL_BASE UL(0x00780000) +#define SECURITY_CONTROL_SIZE UL(0x10000) + +#define TCSR_MUTEX_BASE UL(0x01F40000) +#define TCSR_MUTEX_SIZE UL(0x40000) + +#endif /* ARCH_CONFIG_H */ diff --git a/core/arch/arm/plat-qcom/hoya/kodiak/target.mk b/core/arch/arm/plat-qcom/hoya/kodiak/target.mk new file mode 100644 index 0000000000..b91c1b0146 --- /dev/null +++ b/core/arch/arm/plat-qcom/hoya/kodiak/target.mk @@ -0,0 +1,21 @@ +CFG_DRIVERS_CLK ?= y +CFG_DRIVERS_QCOM_CLK ?= y + +CFG_QCOM_DIAG_LOG ?= $(if $(filter y,$(CFG_TEE_CORE_DEBUG)),y,n) + +CFG_QCOM_QFPROM_FUSEPROV ?= $(if $(filter y,$(CFG_INSECURE)),n,y) + +_qcom_fuseprov_deps = $(if $(filter y,$(CFG_QCOM_QFPROM_FUSEPROV)),y,n) +CFG_QCOM_CMD_DB ?= $(_qcom_fuseprov_deps) +CFG_QCOM_RPMH_CLIENT ?= $(_qcom_fuseprov_deps) +CFG_QCOM_QFPROM ?= $(_qcom_fuseprov_deps) + +CFG_QCOM_PAS_PTA ?= y +# Kodiak requires MX voltage rail workaround for QFPROM fuse blowing +$(call force,CFG_QFPROM_MX_RAIL_WA,y) + +ifeq ($(CFG_QCOM_PAS_PTA),y) +# Increase late mappings to cover all PAS resources +CFG_RESERVED_VASPACE_SIZE ?= (60 * 1024 * 1024) +CFG_IN_TREE_EARLY_TAS += qcom_pas/cff7d191-7ca0-4784-af13-48223b9a4fbe +endif diff --git a/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h b/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h new file mode 100644 index 0000000000..f498d0bc59 --- /dev/null +++ b/core/arch/arm/plat-qcom/hoya/kodiak/target_config.h @@ -0,0 +1,43 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2024, Linaro Limited + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef TARGET_CONFIG_H +#define TARGET_CONFIG_H + +#define GCC_BASE UL(0x100000) +#define GCC_SIZE UL(0x100000) + +#define CFG_SEC_ELF_DDR_ADDR UL(0x808FF000) +#define CFG_SEC_ELF_DDR_SIZE UL(0x1000) + +#define DRAM0_BASE UL(0x80000000) +#define DRAM0_SIZE UL(0x80000000) +#define DRAM1_BASE ULL(0x100000000) +#define DRAM1_SIZE ULL(0x100000000) + +#define RAMBLUR_PIMEM_VAULT_TA_BASE ULL(0xc1800000) +#define RAMBLUR_PIMEM_VAULT_TA_SIZE ULL(0x01c00000) + +#define GENI_UART_REG_BASE UL(0x994000) + +#define IMEM_BASE UL(0x14680000) +#define IMEM_SIZE UL(0x19000) + +#define WPSS_BASE UL(0x8a00000) +#define WPSS_SIZE UL(0x200000) +#define TURING_BASE UL(0x09800000) +#define TURING_SIZE ULL(0x00e00000) +#define LPASS_BASE UL(0x02c00000) +#define LPASS_SIZE ULL(0x01080000) +#define IRIS_BASE UL(0x0aa00000) +#define IRIS_SIZE ULL(0x00200000) + +#define PAS_ID_QDSP6 1 +#define PAS_ID_WPSS 6 +#define PAS_ID_VENUS 9 +#define PAS_ID_TURING 18 + +#endif /* TARGET_CONFIG_H */ diff --git a/core/arch/arm/plat-qcom/hoya/lemans/target.mk b/core/arch/arm/plat-qcom/hoya/lemans/target.mk new file mode 100644 index 0000000000..f645897e90 --- /dev/null +++ b/core/arch/arm/plat-qcom/hoya/lemans/target.mk @@ -0,0 +1,11 @@ +CFG_DRIVERS_CLK ?= y +CFG_DRIVERS_QCOM_CLK ?= y + +CFG_QCOM_DIAG_LOG ?= $(if $(filter y,$(CFG_TEE_CORE_DEBUG)),y,n) + +CFG_QCOM_QFPROM_FUSEPROV ?= $(if $(filter y,$(CFG_INSECURE)),n,y) + +_qcom_fuseprov_deps = $(if $(filter y,$(CFG_QCOM_QFPROM_FUSEPROV)),y,n) +CFG_QCOM_CMD_DB ?= $(_qcom_fuseprov_deps) +CFG_QCOM_RPMH_CLIENT ?= $(_qcom_fuseprov_deps) +CFG_QCOM_QFPROM ?= $(_qcom_fuseprov_deps) diff --git a/core/arch/arm/plat-qcom/hoya/lemans/target_config.h b/core/arch/arm/plat-qcom/hoya/lemans/target_config.h new file mode 100644 index 0000000000..dfc39cacf0 --- /dev/null +++ b/core/arch/arm/plat-qcom/hoya/lemans/target_config.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) 2024, Linaro Limited + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef TARGET_CONFIG_H +#define TARGET_CONFIG_H + +#define GCC_BASE UL(0x110000) +#define GCC_SIZE UL(0x100000) + +#define CFG_SEC_ELF_DDR_ADDR UL(0x908FF000) +#define CFG_SEC_ELF_DDR_SIZE UL(0x1000) + +#define DRAM0_BASE UL(0x80000000) +#define DRAM0_SIZE UL(0x380000000) +#define DRAM1_BASE ULL(0x800000000) +#define DRAM1_SIZE ULL(0x800000000) + +#define RAMBLUR_PIMEM_VAULT_TA_BASE ULL(0xd1900000) +#define RAMBLUR_PIMEM_VAULT_TA_SIZE ULL(0x01c00000) + +#define GENI_UART_REG_BASE UL(0xa8c000) + +#define IMEM_BASE UL(0x14680000) +#define IMEM_SIZE UL(0x32000) + +#endif /* TARGET_CONFIG_H */ diff --git a/core/arch/arm/plat-qcom/platform_config.h b/core/arch/arm/plat-qcom/platform_config.h index 44ef1dd56e..bce35cdfdd 100644 --- a/core/arch/arm/plat-qcom/platform_config.h +++ b/core/arch/arm/plat-qcom/platform_config.h @@ -8,6 +8,8 @@ #define PLATFORM_CONFIG_H #include +#include +#include /* Make stacks aligned to data cache line length */ #define STACK_ALIGNMENT 64 @@ -15,93 +17,4 @@ #define MAX_XLAT_TABLES (40 + (CFG_RESERVED_VASPACE_SIZE) / \ (CORE_MMU_PGDIR_SIZE) + 5) -#if defined(PLATFORM_FLAVOR_kodiak) || defined(PLATFORM_FLAVOR_lemans) -/* GIC related constants */ -#define GICD_BASE UL(0x17a00000) -#define GICR_BASE UL(0x17a60000) - -#define RAMBLUR_PIMEM_REG_BASE UL(0x610000) -#define SEC_PRNG_REG_BASE UL(0x010D1000) - -/* AOP MSG RAM */ -#define AOP_MSG_RAM_BASE UL(0x0C300000) -#define AOP_MSG_RAM_SIZE UL(0x00100000) - -/* RPMH RSC base address */ -#define RPMH_BASE_ADDR UL(0x18200000) -#define RPMH_RSC_SIZE UL(0x40000) - -/* QFPROM and Security Control */ -#define SECURITY_CONTROL_BASE UL(0x00780000) -#define SECURITY_CONTROL_SIZE UL(0x10000) - -/* TCSR Hardware Mutex */ -#define TCSR_MUTEX_BASE UL(0x01F40000) -#define TCSR_MUTEX_SIZE UL(0x40000) -#endif - -#if defined(PLATFORM_FLAVOR_kodiak) -/* Clock controller */ -#define GCC_BASE UL(0x100000) -#define GCC_SIZE UL(0x100000) - -/* QFPROM Fuse Provisioning */ -#define CFG_SEC_ELF_DDR_ADDR UL(0x808FF000) -#define CFG_SEC_ELF_DDR_SIZE UL(0x1000) - -#define DRAM0_BASE UL(0x80000000) -#define DRAM0_SIZE UL(0x80000000) -#define DRAM1_BASE ULL(0x100000000) -#define DRAM1_SIZE ULL(0x100000000) - -/* DDR reserved*/ -#define RAMBLUR_PIMEM_VAULT_TA_BASE ULL(0xc1800000) -#define RAMBLUR_PIMEM_VAULT_TA_SIZE ULL(0x01c00000) - -#define GENI_UART_REG_BASE UL(0x994000) - -/* IMEM and Diagnostic buffer */ -#define IMEM_BASE UL(0x14680000) -#define IMEM_SIZE UL(0x19000) - -#define WPSS_BASE UL(0x8a00000) -#define WPSS_SIZE UL(0x200000) -#define TURING_BASE UL(0x09800000) -#define TURING_SIZE ULL(0x00e00000) -#define LPASS_BASE UL(0x02c00000) -#define LPASS_SIZE ULL(0x01080000) -#define IRIS_BASE UL(0x0aa00000) -#define IRIS_SIZE ULL(0x00200000) -#endif - -#if defined(PLATFORM_FLAVOR_lemans) -/* Clock controller */ -#define GCC_BASE UL(0x110000) -#define GCC_SIZE UL(0x100000) - -/* QFPROM Fuse Provisioning */ -#define CFG_SEC_ELF_DDR_ADDR UL(0x908FF000) -#define CFG_SEC_ELF_DDR_SIZE UL(0x1000) - -#define DRAM0_BASE UL(0x80000000) -#define DRAM0_SIZE UL(0x380000000) -#define DRAM1_BASE ULL(0x800000000) -#define DRAM1_SIZE ULL(0x800000000) - -/* DDR reserved*/ -#define RAMBLUR_PIMEM_VAULT_TA_BASE ULL(0xd1900000) -#define RAMBLUR_PIMEM_VAULT_TA_SIZE ULL(0x01c00000) - -#define GENI_UART_REG_BASE UL(0xa8c000) - -/* IMEM and Diagnostic buffer */ -#define IMEM_BASE UL(0x14680000) -#define IMEM_SIZE UL(0x32000) -#endif - -#define PAS_ID_QDSP6 1 -#define PAS_ID_WPSS 6 -#define PAS_ID_VENUS 9 -#define PAS_ID_TURING 18 - #endif /*PLATFORM_CONFIG_H*/ diff --git a/core/arch/arm/plat-qcom/sub.mk b/core/arch/arm/plat-qcom/sub.mk index 4409e3606e..c3bfe97042 100644 --- a/core/arch/arm/plat-qcom/sub.mk +++ b/core/arch/arm/plat-qcom/sub.mk @@ -1,3 +1,5 @@ global-incdirs-y += . +global-incdirs-y += $(QCOM_ARCH_FAMILY) +global-incdirs-y += $(QCOM_ARCH_FAMILY)/$(PLATFORM_FLAVOR) srcs-y += main.c srcs-y += diag_log.c From 4456b9b8c7c852d23eb807c51f89021f8778287a Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Mon, 18 May 2026 13:16:06 +0530 Subject: [PATCH 12/14] plat-qcom: Add BOBCAT architecture with IPQ96xx platform support Introduce support for the BOBCAT chipset family, starting with the IPQ96xx platform. This extends the architecture-based hierarchy previously established for HOYA to accommodate the new family. BOBCAT Architecture Framework: The conf.mk is updated to detect BOBCAT chipsets alongside HOYA and load the appropriate architecture and chipset configurations using the same variable-based include mechanism. Architecture-level configuration common to all BOBCAT chipsets is placed in bobcat/arch_config.h, while per-chipset differences are captured in chipset-specific target.mk and target_config.h files. IPQ96xx Platform Configuration: IPQ96xx is a 5-core SoC (4x Cortex-A55 + 1x Cortex-A78) with the following configuration: - 5 CPU cores with GICv3 interrupt controller - TZDRAM at 0x8A680000 (2.5MB) - Dual DRAM regions (2GB + 14GB) - GENI UART at 0x01A98000 - IMEM at 0x8600000 (128KB) - DIAG logging support (debug builds only) DIAG Logging Migration: As part of this change, DIAG logging configuration macros are moved from the common diag_log.h to architecture-specific arch_config.h files. This enables each chipset family to define their own DIAG memory layout and register addresses: - HOYA: IMEM_DIAG_OFFSET=0x720, DIAG_SIZE=0x3000 - BOBCAT: IMEM_DIAG_OFFSET=0x730, DIAG_SIZE=0x6000 Signed-off-by: Harikrishna Signed-off-by: Selvam Sathappan Periakaruppan --- core/arch/arm/plat-qcom/bobcat/arch_config.h | 15 +++++++++++++ .../arm/plat-qcom/bobcat/ipq96xx/target.mk | 11 ++++++++++ .../plat-qcom/bobcat/ipq96xx/target_config.h | 22 +++++++++++++++++++ core/arch/arm/plat-qcom/conf.mk | 10 +++++---- core/arch/arm/plat-qcom/diag_log.h | 6 ----- core/arch/arm/plat-qcom/hoya/arch.mk | 1 + core/arch/arm/plat-qcom/hoya/arch_config.h | 6 +++++ 7 files changed, 61 insertions(+), 10 deletions(-) create mode 100644 core/arch/arm/plat-qcom/bobcat/arch_config.h create mode 100644 core/arch/arm/plat-qcom/bobcat/ipq96xx/target.mk create mode 100644 core/arch/arm/plat-qcom/bobcat/ipq96xx/target_config.h diff --git a/core/arch/arm/plat-qcom/bobcat/arch_config.h b/core/arch/arm/plat-qcom/bobcat/arch_config.h new file mode 100644 index 0000000000..937d7ae5a9 --- /dev/null +++ b/core/arch/arm/plat-qcom/bobcat/arch_config.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef ARCH_CONFIG_H +#define ARCH_CONFIG_H + +#define IMEM_DIAG_OFFSET UL(0x730) +#define DIAG_SIZE UL(0x6000) +#define DIAG_BASE UL(0x8608000) +#define DIAG_LOG_START_INFO (IMEM_BASE + IMEM_DIAG_OFFSET) +#define TCSR_BOOT_MISC_DETECT UL(0x195C100) + +#endif /* ARCH_CONFIG_H */ diff --git a/core/arch/arm/plat-qcom/bobcat/ipq96xx/target.mk b/core/arch/arm/plat-qcom/bobcat/ipq96xx/target.mk new file mode 100644 index 0000000000..610f549d56 --- /dev/null +++ b/core/arch/arm/plat-qcom/bobcat/ipq96xx/target.mk @@ -0,0 +1,11 @@ +$(call force,CFG_TEE_CORE_NB_CORE,5) +$(call force,CFG_CORE_CLUSTER_SHIFT,3) +CFG_NUM_THREADS ?= 5 + +$(call force,CFG_ARM_GICV3,y) + +CFG_TZDRAM_START ?= 0x8A680000 +CFG_TZDRAM_SIZE ?= 0x280000 +CFG_TEE_RAM_VA_SIZE ?= 0x280000 + +CFG_QCOM_DIAG_LOG ?= $(if $(filter y,$(CFG_TEE_CORE_DEBUG)),y,n) diff --git a/core/arch/arm/plat-qcom/bobcat/ipq96xx/target_config.h b/core/arch/arm/plat-qcom/bobcat/ipq96xx/target_config.h new file mode 100644 index 0000000000..b3eb10732b --- /dev/null +++ b/core/arch/arm/plat-qcom/bobcat/ipq96xx/target_config.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef TARGET_CONFIG_H +#define TARGET_CONFIG_H + +#define GENI_UART_REG_BASE UL(0x1A98000) + +#define DRAM0_BASE UL(0x80000000) +#define DRAM0_SIZE UL(0x80000000) +#define DRAM1_BASE ULL(0x800000000) +#define DRAM1_SIZE ULL(0x380000000) + +#define GICD_BASE UL(0xF200000) +#define GICR_BASE UL(0xF240000) + +#define IMEM_BASE UL(0x8600000) +#define IMEM_SIZE UL(0x20000) + +#endif /* TARGET_CONFIG_H */ diff --git a/core/arch/arm/plat-qcom/conf.mk b/core/arch/arm/plat-qcom/conf.mk index ec21b872a5..c5561a7267 100644 --- a/core/arch/arm/plat-qcom/conf.mk +++ b/core/arch/arm/plat-qcom/conf.mk @@ -3,7 +3,6 @@ PLATFORM_FLAVOR ?= kodiak $(call force,CFG_GIC,y) -$(call force,CFG_ARM_GICV3,y) $(call force,CFG_SECURE_TIME_SOURCE_CNTPCT,y) $(call force,CFG_ARM64_core,y) $(call force,CFG_WITH_ARM_TRUSTED_FW,y) @@ -24,13 +23,16 @@ supported-ta-targets ?= ta_arm64 # Architecture family mapping HOYA_ARCH_CHIPSETS := kodiak lemans +BOBCAT_ARCH_CHIPSETS := ipq96xx ifneq (,$(filter $(PLATFORM_FLAVOR),$(HOYA_ARCH_CHIPSETS))) QCOM_ARCH_FAMILY := hoya +else ifneq (,$(filter $(PLATFORM_FLAVOR),$(BOBCAT_ARCH_CHIPSETS))) +QCOM_ARCH_FAMILY := bobcat else $(error Unsupported PLATFORM_FLAVOR: $(PLATFORM_FLAVOR)) endif -# Include architecture-specific configuration -include core/arch/arm/plat-qcom/$(QCOM_ARCH_FAMILY)/arch.mk -include core/arch/arm/plat-qcom/$(QCOM_ARCH_FAMILY)/$(PLATFORM_FLAVOR)/target.mk +# Include architecture-specific configurations +-include core/arch/arm/plat-qcom/$(QCOM_ARCH_FAMILY)/arch.mk +-include core/arch/arm/plat-qcom/$(QCOM_ARCH_FAMILY)/$(PLATFORM_FLAVOR)/target.mk diff --git a/core/arch/arm/plat-qcom/diag_log.h b/core/arch/arm/plat-qcom/diag_log.h index c92bb8ebc3..609f0977e7 100644 --- a/core/arch/arm/plat-qcom/diag_log.h +++ b/core/arch/arm/plat-qcom/diag_log.h @@ -11,12 +11,6 @@ #include #include -#define IMEM_DIAG_OFFSET UL(0x720) -#define DIAG_LOG_START_INFO (IMEM_BASE + IMEM_DIAG_OFFSET) -#define TCSR_BOOT_MISC_DETECT UL(0x1FD3000) -#define DIAG_SIZE UL(0x3000) -#define DIAG_BASE (IMEM_BASE + IMEM_SIZE - DIAG_SIZE) - void qcom_diag_log_init(void); void qcom_diag_log_puts(const char *str); diff --git a/core/arch/arm/plat-qcom/hoya/arch.mk b/core/arch/arm/plat-qcom/hoya/arch.mk index 9ee039dde0..59e372983b 100644 --- a/core/arch/arm/plat-qcom/hoya/arch.mk +++ b/core/arch/arm/plat-qcom/hoya/arch.mk @@ -7,6 +7,7 @@ $(call force,CFG_QCOM_RAMBLUR_PIMEM_V3,y) CFG_QCOM_RAMBLUR_TA_WINDOW_ID ?= 2 $(call force,CFG_QCOM_PRNG,y) +$(call force,CFG_ARM_GICV3,y) CFG_TZDRAM_START ?= 0x1c300000 CFG_TEE_RAM_VA_SIZE ?= 0x200000 diff --git a/core/arch/arm/plat-qcom/hoya/arch_config.h b/core/arch/arm/plat-qcom/hoya/arch_config.h index b0459613b8..8848825cb4 100644 --- a/core/arch/arm/plat-qcom/hoya/arch_config.h +++ b/core/arch/arm/plat-qcom/hoya/arch_config.h @@ -25,4 +25,10 @@ #define TCSR_MUTEX_BASE UL(0x01F40000) #define TCSR_MUTEX_SIZE UL(0x40000) +#define IMEM_DIAG_OFFSET UL(0x720) +#define DIAG_SIZE UL(0x3000) +#define DIAG_BASE (IMEM_BASE + IMEM_SIZE - DIAG_SIZE) +#define DIAG_LOG_START_INFO (IMEM_BASE + IMEM_DIAG_OFFSET) +#define TCSR_BOOT_MISC_DETECT UL(0x1FD3000) + #endif /* ARCH_CONFIG_H */ From 8dfa4c3d1788658478e6d3b1ea285275f5176666 Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Thu, 28 May 2026 23:58:30 +0530 Subject: [PATCH 13/14] drivers: gic: allow overriding PIDR2_GICV2 address Certain Qualcomm SoCs seems to have a custom implementation of the GICv2 specification. It's mostly compliant but unfortunately it looks like a mistake was made with the GICD_PIDR registers. PIDR2 is defined to be at offset 0xFE8, but the Qualcomm implementation has it at 0xFD8. It looks like the entire PIDR0-3/4-7 block is swapped compared to the ARM implementation: PIDR0 starts at 0xFD0 (instead of 0xFE0) and PIDR4 starts at 0xFE0 (instead of 0xFD0). Allow overriding it in platform headers as workaround. Similar change is already available in TF-A for reference: https://review.trustedfirmware.org/c/TF-A/trusted-firmware-a/+/12889 Signed-off-by: Harikrishna Signed-off-by: Selvam Sathappan Periakaruppan --- core/drivers/gic.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/drivers/gic.c b/core/drivers/gic.c index af7783d326..f2eeefd293 100644 --- a/core/drivers/gic.c +++ b/core/drivers/gic.c @@ -53,9 +53,16 @@ #ifdef _CFG_ARM_V3_OR_V4 #define GICD_PIDR2 (0xFFE8) #else +/* + * Some GICv2 implementations violate the specification and have this + * register at a different address. Allow overriding it in platform + * headers as workaround. + */ +#ifndef GICD_PIDR2 /* Called ICPIDR2 in GICv2 specification */ #define GICD_PIDR2 (0xFE8) #endif +#endif #define GICD_CTLR_ENABLEGRP0 BIT32(0) #define GICD_CTLR_ENABLEGRP1NS BIT32(1) From c2c2ee442ce7f5243062418855be69cda4bc60bd Mon Sep 17 00:00:00 2001 From: Selvam Sathappan Periakaruppan Date: Mon, 18 May 2026 13:36:38 +0530 Subject: [PATCH 14/14] plat-qcom: Add IPQ52xx platform support Add IPQ52xx chipset support to the BOBCAT architecture family. IPQ52xx is a 4-core Cortex-A53 SoC using GICv2 for interrupt control, unlike other BOBCAT chipsets which use GICv3. The platform configuration includes: - 4 CPU cores (Cortex-A53) with GICv2 interrupt controller - TZDRAM at 0x87D80000 (2.5MB) - Dual DRAM regions (2GB + 2GB) - GENI UART at 0x01A84000 - IMEM at 0x8600000 (96KB) - DIAG logging support (debug builds only) - GICv2 support added to main.c with conditional compilation This commit extends the platform initialization code to support both GICv2 and GICv3 interrupt controllers based on the CFG_ARM_GICV3 configuration flag, enabling compatibility across the BOBCAT chipset family. Signed-off-by: Harikrishna Signed-off-by: Selvam Sathappan Periakaruppan --- .../arm/plat-qcom/bobcat/ipq52xx/target.mk | 8 +++++++ .../plat-qcom/bobcat/ipq52xx/target_config.h | 23 +++++++++++++++++++ core/arch/arm/plat-qcom/conf.mk | 2 +- core/arch/arm/plat-qcom/main.c | 10 +++++++- 4 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 core/arch/arm/plat-qcom/bobcat/ipq52xx/target.mk create mode 100644 core/arch/arm/plat-qcom/bobcat/ipq52xx/target_config.h diff --git a/core/arch/arm/plat-qcom/bobcat/ipq52xx/target.mk b/core/arch/arm/plat-qcom/bobcat/ipq52xx/target.mk new file mode 100644 index 0000000000..7b45b113cc --- /dev/null +++ b/core/arch/arm/plat-qcom/bobcat/ipq52xx/target.mk @@ -0,0 +1,8 @@ +$(call force,CFG_TEE_CORE_NB_CORE,4) +CFG_NUM_THREADS ?= 4 + +CFG_TZDRAM_START ?= 0x87D80000 +CFG_TZDRAM_SIZE ?= 0x280000 +CFG_TEE_RAM_VA_SIZE ?= 0x280000 + +CFG_QCOM_DIAG_LOG ?= $(if $(filter y,$(CFG_TEE_CORE_DEBUG)),y,n) diff --git a/core/arch/arm/plat-qcom/bobcat/ipq52xx/target_config.h b/core/arch/arm/plat-qcom/bobcat/ipq52xx/target_config.h new file mode 100644 index 0000000000..17008a0a0d --- /dev/null +++ b/core/arch/arm/plat-qcom/bobcat/ipq52xx/target_config.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#ifndef TARGET_CONFIG_H +#define TARGET_CONFIG_H + +#define GENI_UART_REG_BASE UL(0x1A84000) + +#define DRAM0_BASE UL(0x80000000) +#define DRAM0_SIZE UL(0x80000000) +#define DRAM1_BASE ULL(0x800000000) +#define DRAM1_SIZE UL(0x80000000) + +#define GICD_BASE UL(0xB000000) +#define GICC_BASE UL(0xB002000) +#define GICD_PIDR2 UL(0xFD8) + +#define IMEM_BASE UL(0x8600000) +#define IMEM_SIZE UL(0x18000) + +#endif /* TARGET_CONFIG_H */ diff --git a/core/arch/arm/plat-qcom/conf.mk b/core/arch/arm/plat-qcom/conf.mk index c5561a7267..252d52873b 100644 --- a/core/arch/arm/plat-qcom/conf.mk +++ b/core/arch/arm/plat-qcom/conf.mk @@ -23,7 +23,7 @@ supported-ta-targets ?= ta_arm64 # Architecture family mapping HOYA_ARCH_CHIPSETS := kodiak lemans -BOBCAT_ARCH_CHIPSETS := ipq96xx +BOBCAT_ARCH_CHIPSETS := ipq96xx ipq52xx ifneq (,$(filter $(PLATFORM_FLAVOR),$(HOYA_ARCH_CHIPSETS))) QCOM_ARCH_FAMILY := hoya diff --git a/core/arch/arm/plat-qcom/main.c b/core/arch/arm/plat-qcom/main.c index b620c150eb..30e35df7ee 100644 --- a/core/arch/arm/plat-qcom/main.c +++ b/core/arch/arm/plat-qcom/main.c @@ -21,7 +21,11 @@ register_phys_mem_pgdir(MEM_AREA_IO_NSEC, GENI_UART_REG_BASE, GENI_UART_REG_SIZE); register_phys_mem_pgdir(MEM_AREA_IO_SEC, GICD_BASE, GIC_DIST_REG_SIZE); -register_phys_mem_pgdir(MEM_AREA_IO_SEC, GICR_BASE, GIC_DIST_REG_SIZE); +#ifdef CFG_ARM_GICV3 +register_phys_mem_pgdir(MEM_AREA_IO_SEC, GICR_BASE, GIC_REDIST_REG_SIZE); +#else +register_phys_mem_pgdir(MEM_AREA_IO_SEC, GICC_BASE, GIC_CPU_REG_SIZE); +#endif register_ddr(DRAM0_BASE, DRAM0_SIZE); #ifdef DRAM1_BASE @@ -57,7 +61,11 @@ boot_final(platform_banner); void boot_primary_init_intc(void) { +#ifdef CFG_ARM_GICV3 gic_init_v3(0, GICD_BASE, GICR_BASE); +#else + gic_init(GICC_BASE, GICD_BASE); +#endif } void boot_secondary_init_intc(void)