1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright (c) 2024, STMicroelectronics 4 */ 5 6 #include <assert.h> 7 #include <drivers/rstctrl.h> 8 #include <kernel/dt.h> 9 #include <kernel/panic.h> 10 #include <libfdt.h> 11 #include <malloc.h> 12 #include <scmi_agent_configuration.h> 13 #include <scmi_reset_consumer.h> 14 #include <tee_api_defines_extensions.h> 15 #include <trace.h> 16 17 /* 18 * struct scmi_server_reset: data for a SCMI reset in DT 19 * 20 * @domain_id: SCMI domain identifier 21 * @domain_name: SCMI domain name 22 * @reset: reset to control through SCMI protocol 23 */ 24 struct scmi_server_reset { 25 uint32_t domain_id; 26 const char *domain_name; 27 struct rstctrl *reset; 28 }; 29 30 TEE_Result optee_scmi_server_init_resets(const void *fdt, int node, 31 struct scpfw_agent_config *agent_cfg, 32 struct scpfw_channel_config 33 *channel_cfg) 34 { 35 TEE_Result res = TEE_ERROR_GENERIC; 36 struct scmi_server_reset *s_resets = NULL; 37 size_t s_resets_count = 0; 38 int item_node = 0; 39 int subnode = 0; 40 bool have_subnodes = false; 41 size_t n = 0; 42 43 item_node = fdt_subnode_offset(fdt, node, "resets"); 44 if (item_node < 0) 45 return TEE_SUCCESS; 46 47 /* Compute the number of domains to allocate */ 48 fdt_for_each_subnode(subnode, fdt, item_node) { 49 paddr_t reg = fdt_reg_base_address(fdt, subnode); 50 51 assert(reg != DT_INFO_INVALID_REG); 52 if (reg > s_resets_count) 53 s_resets_count = reg; 54 55 have_subnodes = true; 56 } 57 58 if (!have_subnodes) 59 return TEE_SUCCESS; 60 61 /* Number of SCMI reset domains is the max domain ID + 1 */ 62 s_resets_count += 1; 63 s_resets = calloc(s_resets_count, sizeof(*s_resets)); 64 if (!s_resets) 65 return TEE_ERROR_OUT_OF_MEMORY; 66 67 fdt_for_each_subnode(subnode, fdt, item_node) { 68 struct scmi_server_reset *s_reset = NULL; 69 struct rstctrl *reset = NULL; 70 const fdt32_t *cuint = NULL; 71 uint32_t domain_id = 0; 72 73 res = rstctrl_dt_get_by_index(fdt, subnode, 0, &reset); 74 if (res == TEE_ERROR_DEFER_DRIVER_INIT) { 75 panic("Unexpected init deferral"); 76 } else if (res) { 77 EMSG("Can't get reset %s (%#"PRIx32"), skipped", 78 fdt_get_name(fdt, subnode, NULL), res); 79 continue; 80 } 81 82 domain_id = fdt_reg_base_address(fdt, subnode); 83 s_reset = s_resets + domain_id; 84 s_reset->domain_id = domain_id; 85 86 cuint = fdt_getprop(fdt, subnode, "domain-name", NULL); 87 if (cuint) 88 s_reset->domain_name = (char *)cuint; 89 else 90 s_reset->domain_name = fdt_get_name(fdt, subnode, NULL); 91 92 /* Check that the domain_id is not already used */ 93 if (s_reset->reset) { 94 EMSG("Domain ID %"PRIu32" already used", domain_id); 95 panic(); 96 } 97 s_reset->reset = reset; 98 99 DMSG("scmi reset shares %s on domain ID %"PRIu32, 100 s_reset->domain_name, domain_id); 101 } 102 103 for (n = 0; n < s_resets_count; n++) { 104 /* 105 * Assign domain IDs to un-exposed reset as SCMI specification 106 * requires the resource is defined even if not accessible. 107 */ 108 if (!s_resets[n].reset) { 109 s_resets[n].domain_id = n; 110 s_resets[n].domain_name = ""; 111 } 112 113 s_resets[n].domain_name = strdup(s_resets[n].domain_name); 114 if (!s_resets[n].domain_name) 115 panic(); 116 } 117 118 if (channel_cfg->reset) { 119 EMSG("Reset already loaded: agent %u, channel %u", 120 agent_cfg->agent_id, channel_cfg->channel_id); 121 panic(); 122 } 123 124 channel_cfg->reset_count = s_resets_count; 125 channel_cfg->reset = calloc(channel_cfg->reset_count, 126 sizeof(*channel_cfg->reset)); 127 if (!channel_cfg->reset) 128 panic(); 129 130 for (n = 0; n < s_resets_count; n++) { 131 unsigned int domain_id = s_resets[n].domain_id; 132 133 channel_cfg->reset[domain_id] = (struct scmi_reset){ 134 .name = s_resets[n].domain_name, 135 .rstctrl = s_resets[n].reset, 136 }; 137 } 138 139 return TEE_SUCCESS; 140 } 141