1 /*
2  * Arm SCP/MCP Software
3  * Copyright (c) 2020-2024, Arm Limited and Contributors. All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * Description:
8  *     Power domain utilities.
9  */
10 
11 #include <mod_power_domain.h>
12 #include <power_domain_utils.h>
13 
14 #include <fwk_element.h>
15 #include <fwk_id.h>
16 #include <fwk_mm.h>
17 #include <fwk_status.h>
18 #include <fwk_string.h>
19 
20 #include <stdio.h>
21 
22 /* Maximum power domain name size including the null terminator */
23 #define PD_NAME_SIZE 25
24 /* Maximum number of cores */
25 #define MAX_CORES_COUNT 64
26 /* Maximum number of static table elements */
27 #define MAX_STATIC_ELEMENTS_COUNT 32
28 
create_core_cluster_pd_element_table(struct fwk_element * element_table,unsigned int core_count,unsigned int cluster_count,unsigned int driver_idx,unsigned int api_idx,const uint32_t * core_state_table,size_t core_state_table_size,const uint32_t * cluster_state_table,size_t cluster_state_table_size)29 static int create_core_cluster_pd_element_table(
30     struct fwk_element *element_table,
31     unsigned int core_count,
32     unsigned int cluster_count,
33     unsigned int driver_idx,
34     unsigned int api_idx,
35     const uint32_t *core_state_table,
36     size_t core_state_table_size,
37     const uint32_t *cluster_state_table,
38     size_t cluster_state_table_size)
39 {
40     struct fwk_element *element;
41     struct mod_power_domain_element_config *pd_config, *pd_config_table = NULL;
42     unsigned int core_element_counter = 0;
43     unsigned int core_idx;
44     unsigned int cluster_idx;
45     unsigned int cores_per_clusters = core_count / cluster_count;
46     int status;
47 
48     pd_config_table = fwk_mm_calloc(
49         core_count + cluster_count,
50         sizeof(struct mod_power_domain_element_config));
51     if (pd_config_table == NULL) {
52         return FWK_E_NOMEM;
53     }
54 
55     for (cluster_idx = 0; cluster_idx < cluster_count; cluster_idx++) {
56         for (core_idx = 0; core_idx < cores_per_clusters; core_idx++) {
57             element = &element_table[core_element_counter];
58             pd_config = &pd_config_table[core_element_counter];
59             element->name = fwk_mm_alloc(PD_NAME_SIZE, 1);
60             status = snprintf(
61                 (char *)element->name,
62                 PD_NAME_SIZE,
63                 "CLUS%uCORE%u",
64                 cluster_idx,
65                 core_idx);
66             if (status < 0) {
67                 return FWK_E_PANIC;
68             }
69             element->data = pd_config;
70             pd_config->attributes.pd_type = MOD_PD_TYPE_CORE;
71             pd_config->parent_idx = cluster_idx + core_count;
72             pd_config->driver_id =
73                 FWK_ID_ELEMENT(driver_idx, core_element_counter);
74             pd_config->api_id = FWK_ID_API(driver_idx, api_idx);
75             pd_config->allowed_state_mask_table = core_state_table;
76             pd_config->allowed_state_mask_table_size = core_state_table_size;
77             core_element_counter++;
78         }
79         /* Define the cluster configuration */
80         element = &element_table[cluster_idx + core_count];
81         pd_config = &pd_config_table[cluster_idx + core_count];
82         element->name = fwk_mm_alloc(PD_NAME_SIZE, 1);
83         status = snprintf(
84             (char *)element->name, PD_NAME_SIZE, "CLUS%u", cluster_idx);
85         if (status < 0) {
86             return FWK_E_PANIC;
87         }
88         element->data = pd_config;
89         pd_config->attributes.pd_type = MOD_PD_TYPE_CLUSTER;
90         pd_config->driver_id =
91             FWK_ID_ELEMENT(driver_idx, cluster_idx + core_count);
92         pd_config->api_id = FWK_ID_API(driver_idx, api_idx);
93         pd_config->allowed_state_mask_table = cluster_state_table;
94         pd_config->allowed_state_mask_table_size = cluster_state_table_size;
95     }
96 
97     return FWK_SUCCESS;
98 }
99 
create_power_domain_element_table(unsigned int core_count,unsigned int cluster_count,unsigned int driver_idx,unsigned int api_idx,const uint32_t * core_state_table,size_t core_state_table_size,const uint32_t * cluster_state_table,size_t cluster_state_table_size,struct fwk_element * static_table,size_t static_table_size)100 const struct fwk_element *create_power_domain_element_table(
101     unsigned int core_count,
102     unsigned int cluster_count,
103     unsigned int driver_idx,
104     unsigned int api_idx,
105     const uint32_t *core_state_table,
106     size_t core_state_table_size,
107     const uint32_t *cluster_state_table,
108     size_t cluster_state_table_size,
109     struct fwk_element *static_table,
110     size_t static_table_size)
111 {
112     struct fwk_element *element_table = NULL;
113     struct mod_power_domain_element_config *pd_config;
114     unsigned int systop_idx, element_idx;
115     int status;
116 
117     if ((core_count % cluster_count != 0) || (core_count > MAX_CORES_COUNT) ||
118         (static_table_size > MAX_STATIC_ELEMENTS_COUNT)) {
119         return NULL;
120     }
121 
122     element_table = fwk_mm_calloc(
123         core_count + cluster_count + static_table_size + 1, /* Terminator */
124         sizeof(struct fwk_element));
125 
126     if (element_table == NULL) {
127         return element_table;
128     }
129 
130     status = create_core_cluster_pd_element_table(
131         element_table,
132         core_count,
133         cluster_count,
134         driver_idx,
135         api_idx,
136         core_state_table,
137         core_state_table_size,
138         cluster_state_table,
139         cluster_state_table_size);
140 
141     if (status != FWK_SUCCESS) {
142         return NULL;
143     }
144 
145     fwk_str_memcpy(
146         element_table + (core_count + cluster_count),
147         static_table,
148         static_table_size * sizeof(struct fwk_element));
149 
150     /* Calculate SYSTOP index */
151     systop_idx =
152         (unsigned int)(core_count + cluster_count + static_table_size - 1);
153 
154     /* Set Power Domain Parent for all SYSTOP children */
155     for (element_idx = core_count; element_idx < systop_idx; element_idx++) {
156         pd_config =
157             (struct mod_power_domain_element_config *)element_table[element_idx]
158                 .data;
159         pd_config->parent_idx = systop_idx;
160     }
161 
162     return element_table;
163 }
164