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
optee_scmi_server_init_resets(const void * fdt,int node,struct scpfw_agent_config * agent_cfg,struct scpfw_channel_config * channel_cfg)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