1ec9aa1a4SGatien Chevallier // SPDX-License-Identifier: BSD-2-Clause 2ec9aa1a4SGatien Chevallier /* 3ec9aa1a4SGatien Chevallier * Copyright (c) 2022-2024, STMicroelectronics 4ec9aa1a4SGatien Chevallier */ 5ec9aa1a4SGatien Chevallier 6ec9aa1a4SGatien Chevallier #include <arm.h> 7ec9aa1a4SGatien Chevallier #include <config.h> 8ec9aa1a4SGatien Chevallier #include <drivers/clk.h> 9ec9aa1a4SGatien Chevallier #include <drivers/clk_dt.h> 10ec9aa1a4SGatien Chevallier #include <drivers/stm32_rif.h> 11ec9aa1a4SGatien Chevallier #include <io.h> 12ec9aa1a4SGatien Chevallier #include <kernel/boot.h> 13ec9aa1a4SGatien Chevallier #include <kernel/delay.h> 14ec9aa1a4SGatien Chevallier #include <kernel/dt.h> 15ec9aa1a4SGatien Chevallier #include <kernel/dt_driver.h> 16ec9aa1a4SGatien Chevallier #include <kernel/panic.h> 17ec9aa1a4SGatien Chevallier #include <libfdt.h> 18ec9aa1a4SGatien Chevallier #include <mm/core_memprot.h> 19ec9aa1a4SGatien Chevallier #include <stdbool.h> 20ec9aa1a4SGatien Chevallier #include <stdlib.h> 21ec9aa1a4SGatien Chevallier #include <stm32_util.h> 22ec9aa1a4SGatien Chevallier #include <trace.h> 23ec9aa1a4SGatien Chevallier 24ec9aa1a4SGatien Chevallier #define HSEM_SECCFGR U(0x200) 25ec9aa1a4SGatien Chevallier #define HSEM_PRIVCFGR U(0x210) 26ec9aa1a4SGatien Chevallier #define HSEM_CnCIDCFGR(x) (U(0x220) + U(0x004) * ((x) - 1)) 27ec9aa1a4SGatien Chevallier #define HSEM_GpCIDCFGR(x) (U(0x240) + U(0x004) * (x)) 28ec9aa1a4SGatien Chevallier 29ec9aa1a4SGatien Chevallier /* 30ec9aa1a4SGatien Chevallier * CnCIDCFGR register bitfields 31ec9aa1a4SGatien Chevallier */ 32ec9aa1a4SGatien Chevallier #define HSEM_CnCIDCFGR_CONF_MASK (_CIDCFGR_CFEN | \ 33ec9aa1a4SGatien Chevallier HSEM_CnCIDCFGR_SCID_MASK) 34ec9aa1a4SGatien Chevallier #define HSEM_CnCIDCFGR_SCID_MASK GENMASK_32(6, 4) 35ec9aa1a4SGatien Chevallier 36ec9aa1a4SGatien Chevallier /* 37ec9aa1a4SGatien Chevallier * GpCIDCFGR register bitfields 38ec9aa1a4SGatien Chevallier */ 39ec9aa1a4SGatien Chevallier #define HSEM_GpCIDCFGR_SEM_WLIST_C_MASK GENMASK_32(18, 16) 40ec9aa1a4SGatien Chevallier #define HSEM_GpCIDCFGR_SEM_WLIST_SHIFT U(16) 41ec9aa1a4SGatien Chevallier #define HSEM_GpCIDCFGR_CONF_MASK (_CIDCFGR_CFEN | \ 42ec9aa1a4SGatien Chevallier HSEM_GpCIDCFGR_SEM_WLIST_C_MASK) 43ec9aa1a4SGatien Chevallier 44ec9aa1a4SGatien Chevallier /* 45ec9aa1a4SGatien Chevallier * PRIVCFGR register bitfields 46ec9aa1a4SGatien Chevallier */ 47ec9aa1a4SGatien Chevallier #define HSEM_PRIVCFGR_MASK GENMASK_32(15, 0) 48ec9aa1a4SGatien Chevallier 49ec9aa1a4SGatien Chevallier /* 50ec9aa1a4SGatien Chevallier * SECCFGR register bitfields 51ec9aa1a4SGatien Chevallier */ 52ec9aa1a4SGatien Chevallier #define HSEM_SECCFGR_MASK GENMASK_32(15, 0) 53ec9aa1a4SGatien Chevallier 54ec9aa1a4SGatien Chevallier /* 55ec9aa1a4SGatien Chevallier * Miscellaneous 56ec9aa1a4SGatien Chevallier */ 57ec9aa1a4SGatien Chevallier #define HSEM_NB_PROC U(3) 58ec9aa1a4SGatien Chevallier #define HSEM_NB_SEM_GROUPS U(4) 59ec9aa1a4SGatien Chevallier #define HSEM_NB_SEM_PER_GROUP U(4) 60ec9aa1a4SGatien Chevallier 61ec9aa1a4SGatien Chevallier #define HSEM_NB_MAX_CID_SUPPORTED U(7) 62ec9aa1a4SGatien Chevallier #define HSEM_RIF_RESOURCES U(16) 63ec9aa1a4SGatien Chevallier 64ec9aa1a4SGatien Chevallier struct hsem_pdata { 65ec9aa1a4SGatien Chevallier struct clk *hsem_clock; 66ec9aa1a4SGatien Chevallier struct rif_conf_data conf_data; 67ec9aa1a4SGatien Chevallier unsigned int nb_channels; 68ec9aa1a4SGatien Chevallier vaddr_t base; 69ec9aa1a4SGatien Chevallier uint32_t *rif_proc_conf; 70ec9aa1a4SGatien Chevallier }; 71ec9aa1a4SGatien Chevallier 72ec9aa1a4SGatien Chevallier static struct hsem_pdata *hsem_d; 73ec9aa1a4SGatien Chevallier 74ec9aa1a4SGatien Chevallier static void apply_rif_config(bool is_tdcid) 75ec9aa1a4SGatien Chevallier { 76ec9aa1a4SGatien Chevallier unsigned int i = 0; 77ec9aa1a4SGatien Chevallier unsigned int j = 0; 78ec9aa1a4SGatien Chevallier uint32_t prev_cid_value = 0; 79ec9aa1a4SGatien Chevallier 80ec9aa1a4SGatien Chevallier /* 81ec9aa1a4SGatien Chevallier * When TDCID, OP-TEE should be the one to set the CID filtering 82ec9aa1a4SGatien Chevallier * configuration. Clearing previous configuration prevents 83ec9aa1a4SGatien Chevallier * undesired events during the only legitimate configuration. 84ec9aa1a4SGatien Chevallier */ 85ec9aa1a4SGatien Chevallier if (is_tdcid) { 86ec9aa1a4SGatien Chevallier for (i = 0; i < HSEM_NB_PROC; i++) 87ec9aa1a4SGatien Chevallier io_clrbits32(hsem_d->base + HSEM_CnCIDCFGR(i + 1), 88ec9aa1a4SGatien Chevallier HSEM_CnCIDCFGR_CONF_MASK); 89ec9aa1a4SGatien Chevallier 90ec9aa1a4SGatien Chevallier /* Clean HSEM groups configuration registers */ 91ec9aa1a4SGatien Chevallier for (i = 0; i < HSEM_NB_SEM_GROUPS; i++) 92ec9aa1a4SGatien Chevallier io_clrbits32(hsem_d->base + HSEM_GpCIDCFGR(i), 93ec9aa1a4SGatien Chevallier HSEM_GpCIDCFGR_CONF_MASK); 94ec9aa1a4SGatien Chevallier } 95ec9aa1a4SGatien Chevallier 96ec9aa1a4SGatien Chevallier /* Security and privilege RIF configuration */ 97ec9aa1a4SGatien Chevallier io_clrsetbits32(hsem_d->base + HSEM_SECCFGR, 98ec9aa1a4SGatien Chevallier HSEM_SECCFGR_MASK & hsem_d->conf_data.access_mask[0], 99ec9aa1a4SGatien Chevallier hsem_d->conf_data.sec_conf[0]); 100ec9aa1a4SGatien Chevallier io_clrsetbits32(hsem_d->base + HSEM_PRIVCFGR, 101ec9aa1a4SGatien Chevallier HSEM_PRIVCFGR_MASK & hsem_d->conf_data.access_mask[0], 102ec9aa1a4SGatien Chevallier hsem_d->conf_data.priv_conf[0]); 103ec9aa1a4SGatien Chevallier 104ec9aa1a4SGatien Chevallier if (!is_tdcid) 105ec9aa1a4SGatien Chevallier return; 106ec9aa1a4SGatien Chevallier 107ec9aa1a4SGatien Chevallier /* Configure HSEM processors configuration registers */ 108ec9aa1a4SGatien Chevallier for (i = 0; i < HSEM_NB_PROC; i++) { 109ec9aa1a4SGatien Chevallier /* 110ec9aa1a4SGatien Chevallier * If a processor CID configuration is present, enable it. 111ec9aa1a4SGatien Chevallier * Else, nothing to do. 112ec9aa1a4SGatien Chevallier */ 113ec9aa1a4SGatien Chevallier if (!hsem_d->rif_proc_conf[i]) 114ec9aa1a4SGatien Chevallier continue; 115ec9aa1a4SGatien Chevallier 116ec9aa1a4SGatien Chevallier io_clrsetbits32(hsem_d->base + HSEM_CnCIDCFGR(i + 1), 117ec9aa1a4SGatien Chevallier HSEM_CnCIDCFGR_CONF_MASK, 118ec9aa1a4SGatien Chevallier _CIDCFGR_CFEN | hsem_d->rif_proc_conf[i]); 119ec9aa1a4SGatien Chevallier } 120ec9aa1a4SGatien Chevallier 121ec9aa1a4SGatien Chevallier /* 122ec9aa1a4SGatien Chevallier * Configure HSEM groups configuration registers 123ec9aa1a4SGatien Chevallier * If one semaphore is configured, all semaphores of its group 124ec9aa1a4SGatien Chevallier * must be configured too and MUST have the same RIF 125ec9aa1a4SGatien Chevallier * configuration. 126ec9aa1a4SGatien Chevallier */ 127ec9aa1a4SGatien Chevallier for (i = 0; i < HSEM_NB_SEM_GROUPS; i++) { 128ec9aa1a4SGatien Chevallier unsigned int hsem_idx = i * HSEM_NB_SEM_PER_GROUP; 129ec9aa1a4SGatien Chevallier unsigned int hsem_cid = 0; 130ec9aa1a4SGatien Chevallier unsigned int known_cid_idx = 0; 131ec9aa1a4SGatien Chevallier 132ec9aa1a4SGatien Chevallier prev_cid_value = hsem_d->conf_data.cid_confs[hsem_idx]; 133ec9aa1a4SGatien Chevallier 134ec9aa1a4SGatien Chevallier /* If CID filtering is disabled, do nothing */ 135ec9aa1a4SGatien Chevallier if (!(prev_cid_value & _CIDCFGR_CFEN)) 136ec9aa1a4SGatien Chevallier continue; 137ec9aa1a4SGatien Chevallier 138ec9aa1a4SGatien Chevallier /* 139ec9aa1a4SGatien Chevallier * Check if configured CID corresponds to a processor's 140ec9aa1a4SGatien Chevallier * CID in HSEM_CnCIDCFGR. 141ec9aa1a4SGatien Chevallier */ 142ec9aa1a4SGatien Chevallier for (j = 0; j < HSEM_NB_PROC; j++) { 143ec9aa1a4SGatien Chevallier uint32_t proc_cid = hsem_d->rif_proc_conf[j]; 144ec9aa1a4SGatien Chevallier 145ec9aa1a4SGatien Chevallier hsem_cid = (prev_cid_value & HSEM_CnCIDCFGR_SCID_MASK); 146ec9aa1a4SGatien Chevallier DMSG("hsem_cid %u", hsem_cid); 147ec9aa1a4SGatien Chevallier DMSG("proc_cid %u", proc_cid); 148ec9aa1a4SGatien Chevallier if (proc_cid == hsem_cid) { 149ec9aa1a4SGatien Chevallier known_cid_idx = BIT(j + 150ec9aa1a4SGatien Chevallier HSEM_GpCIDCFGR_SEM_WLIST_SHIFT); 151ec9aa1a4SGatien Chevallier break; 152ec9aa1a4SGatien Chevallier } 153ec9aa1a4SGatien Chevallier } 154ec9aa1a4SGatien Chevallier if (!known_cid_idx) 155ec9aa1a4SGatien Chevallier panic("Unknown HSEM processor CID"); 156ec9aa1a4SGatien Chevallier 157ec9aa1a4SGatien Chevallier /* 158ec9aa1a4SGatien Chevallier * HSEM resources in the same group must have the same CID 159ec9aa1a4SGatien Chevallier * filtering configuration. Else it is inconsistent. 160ec9aa1a4SGatien Chevallier */ 161ec9aa1a4SGatien Chevallier for (j = 0; j < HSEM_NB_SEM_PER_GROUP; j++) 162ec9aa1a4SGatien Chevallier if (hsem_d->conf_data.cid_confs[j + hsem_idx] != 163ec9aa1a4SGatien Chevallier prev_cid_value) 164ec9aa1a4SGatien Chevallier panic("Inconsistent HSEM RIF group config"); 165ec9aa1a4SGatien Chevallier 166ec9aa1a4SGatien Chevallier io_clrsetbits32(hsem_d->base + HSEM_GpCIDCFGR(i), 167ec9aa1a4SGatien Chevallier HSEM_GpCIDCFGR_CONF_MASK, 168ec9aa1a4SGatien Chevallier _CIDCFGR_CFEN | known_cid_idx); 169ec9aa1a4SGatien Chevallier } 170ec9aa1a4SGatien Chevallier } 171ec9aa1a4SGatien Chevallier 172ec9aa1a4SGatien Chevallier static TEE_Result parse_dt(const void *fdt, int node) 173ec9aa1a4SGatien Chevallier { 174ec9aa1a4SGatien Chevallier TEE_Result res = TEE_ERROR_GENERIC; 175ec9aa1a4SGatien Chevallier unsigned int i = 0; 176ec9aa1a4SGatien Chevallier int lenp = 0; 177ec9aa1a4SGatien Chevallier const fdt32_t *cuint = NULL; 178ec9aa1a4SGatien Chevallier struct dt_node_info info = { }; 179ec9aa1a4SGatien Chevallier struct io_pa_va addr = { }; 180ec9aa1a4SGatien Chevallier 181ec9aa1a4SGatien Chevallier fdt_fill_device_info(fdt, &info, node); 182ec9aa1a4SGatien Chevallier assert(info.reg != DT_INFO_INVALID_REG && 183ec9aa1a4SGatien Chevallier info.reg_size != DT_INFO_INVALID_REG_SIZE); 184ec9aa1a4SGatien Chevallier 185ec9aa1a4SGatien Chevallier addr.pa = info.reg; 186ec9aa1a4SGatien Chevallier hsem_d->base = io_pa_or_va_secure(&addr, info.reg_size); 187ec9aa1a4SGatien Chevallier 188ec9aa1a4SGatien Chevallier /* Gate the IP */ 189ec9aa1a4SGatien Chevallier res = clk_dt_get_by_index(fdt, node, 0, &hsem_d->hsem_clock); 190ec9aa1a4SGatien Chevallier if (res) 191ec9aa1a4SGatien Chevallier return res; 192ec9aa1a4SGatien Chevallier 193ec9aa1a4SGatien Chevallier cuint = fdt_getprop(fdt, node, "st,protreg", &lenp); 194ec9aa1a4SGatien Chevallier if (!cuint) 195ec9aa1a4SGatien Chevallier panic("No RIF configuration available"); 196ec9aa1a4SGatien Chevallier 197ec9aa1a4SGatien Chevallier hsem_d->nb_channels = (unsigned int)(lenp / sizeof(uint32_t)); 198ec9aa1a4SGatien Chevallier assert(hsem_d->nb_channels <= HSEM_RIF_RESOURCES); 199ec9aa1a4SGatien Chevallier 200ec9aa1a4SGatien Chevallier hsem_d->rif_proc_conf = calloc(HSEM_NB_PROC, sizeof(uint32_t)); 201ec9aa1a4SGatien Chevallier assert(hsem_d->rif_proc_conf); 202ec9aa1a4SGatien Chevallier hsem_d->conf_data.cid_confs = calloc(HSEM_RIF_RESOURCES, 203ec9aa1a4SGatien Chevallier sizeof(uint32_t)); 204ec9aa1a4SGatien Chevallier hsem_d->conf_data.sec_conf = calloc(1, sizeof(uint32_t)); 205ec9aa1a4SGatien Chevallier hsem_d->conf_data.priv_conf = calloc(1, sizeof(uint32_t)); 206ec9aa1a4SGatien Chevallier hsem_d->conf_data.access_mask = calloc(1, sizeof(uint32_t)); 207ec9aa1a4SGatien Chevallier assert(hsem_d->conf_data.cid_confs && hsem_d->conf_data.sec_conf && 208ec9aa1a4SGatien Chevallier hsem_d->conf_data.priv_conf && hsem_d->conf_data.access_mask); 209ec9aa1a4SGatien Chevallier 210ec9aa1a4SGatien Chevallier for (i = 0; i < hsem_d->nb_channels; i++) 211ec9aa1a4SGatien Chevallier stm32_rif_parse_cfg(fdt32_to_cpu(cuint[i]), &hsem_d->conf_data, 212ec9aa1a4SGatien Chevallier HSEM_RIF_RESOURCES); 213ec9aa1a4SGatien Chevallier 214ec9aa1a4SGatien Chevallier cuint = fdt_getprop(fdt, node, "st,proccid", &lenp); 215ec9aa1a4SGatien Chevallier if (!cuint) 216ec9aa1a4SGatien Chevallier panic("No RIF proc configuration available"); 217ec9aa1a4SGatien Chevallier 218ec9aa1a4SGatien Chevallier lenp = (unsigned int)(lenp / sizeof(uint32_t)); 219ec9aa1a4SGatien Chevallier /* 220ec9aa1a4SGatien Chevallier * There should be maximum (HSEM_NB_PROC * 2) property argument. 221ec9aa1a4SGatien Chevallier * First argument for a processor is its number, the second is its CID. 222ec9aa1a4SGatien Chevallier */ 223ec9aa1a4SGatien Chevallier assert((unsigned int)lenp <= (HSEM_NB_PROC * 2)); 224ec9aa1a4SGatien Chevallier 225ec9aa1a4SGatien Chevallier for (i = 0; i < (unsigned int)lenp / 2; i++) { 226ec9aa1a4SGatien Chevallier unsigned int pos = fdt32_to_cpu(cuint[i * 2]) - 1; 227ec9aa1a4SGatien Chevallier unsigned int cid_value = fdt32_to_cpu(cuint[(i * 2) + 1]); 228ec9aa1a4SGatien Chevallier 229646ad62bSGatien Chevallier hsem_d->rif_proc_conf[pos] = SHIFT_U32(cid_value, 230646ad62bSGatien Chevallier _CIDCFGR_SCID_SHIFT); 231ec9aa1a4SGatien Chevallier } 232ec9aa1a4SGatien Chevallier 233ec9aa1a4SGatien Chevallier return TEE_SUCCESS; 234ec9aa1a4SGatien Chevallier } 235ec9aa1a4SGatien Chevallier 236ec9aa1a4SGatien Chevallier static TEE_Result stm32_hsem_probe(const void *fdt, int node, 237ec9aa1a4SGatien Chevallier const void *compat_data __unused) 238ec9aa1a4SGatien Chevallier { 239ec9aa1a4SGatien Chevallier TEE_Result res = TEE_ERROR_GENERIC; 240ec9aa1a4SGatien Chevallier bool is_tdcid = false; 241ec9aa1a4SGatien Chevallier 242ec9aa1a4SGatien Chevallier res = stm32_rifsc_check_tdcid(&is_tdcid); 243ec9aa1a4SGatien Chevallier if (res) 244ec9aa1a4SGatien Chevallier return res; 245ec9aa1a4SGatien Chevallier 246ec9aa1a4SGatien Chevallier hsem_d = calloc(1, sizeof(*hsem_d)); 247ec9aa1a4SGatien Chevallier if (!hsem_d) 248ec9aa1a4SGatien Chevallier return TEE_ERROR_OUT_OF_MEMORY; 249ec9aa1a4SGatien Chevallier 250ec9aa1a4SGatien Chevallier res = parse_dt(fdt, node); 251*19c6bd04SGatien Chevallier if (res) { 252*19c6bd04SGatien Chevallier free(hsem_d); 253ec9aa1a4SGatien Chevallier return res; 254*19c6bd04SGatien Chevallier } 255ec9aa1a4SGatien Chevallier 256ec9aa1a4SGatien Chevallier res = clk_enable(hsem_d->hsem_clock); 257ec9aa1a4SGatien Chevallier if (res) 258ec9aa1a4SGatien Chevallier panic("Cannot access HSEM clock"); 259ec9aa1a4SGatien Chevallier 260ec9aa1a4SGatien Chevallier apply_rif_config(is_tdcid); 261ec9aa1a4SGatien Chevallier 262ec9aa1a4SGatien Chevallier clk_disable(hsem_d->hsem_clock); 263ec9aa1a4SGatien Chevallier 264ec9aa1a4SGatien Chevallier return TEE_SUCCESS; 265ec9aa1a4SGatien Chevallier } 266ec9aa1a4SGatien Chevallier 267ec9aa1a4SGatien Chevallier static const struct dt_device_match stm32_hsem_match_table[] = { 268ec9aa1a4SGatien Chevallier { .compatible = "st,stm32mp25-hsem" }, 269ec9aa1a4SGatien Chevallier { } 270ec9aa1a4SGatien Chevallier }; 271ec9aa1a4SGatien Chevallier 272ec9aa1a4SGatien Chevallier DEFINE_DT_DRIVER(stm32_hsem_dt_driver) = { 273ec9aa1a4SGatien Chevallier .name = "st,stm32-hsem", 274ec9aa1a4SGatien Chevallier .match_table = stm32_hsem_match_table, 275ec9aa1a4SGatien Chevallier .probe = stm32_hsem_probe, 276ec9aa1a4SGatien Chevallier }; 277