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