1 /*
2 * Copyright (c) 2024, Arm Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7 
8 #include <stdbool.h>
9 #include <stddef.h>
10 #include <stdint.h>
11 
12 #include "device_definition.h"
13 #include "host_base_address.h"
14 #include "host_system.h"
15 #include "tfm_hal_device_header.h"
16 #include "tfm_plat_otp.h"
17 #ifdef RD_SYSCTRL_NI_TOWER
18 #include "ni_tower_lib.h"
19 
20 #define NI_TOWER_SYSCTRL_SYSTOP_PARENT_TYPE      NI_TOWER_VD
21 #define NI_TOWER_SYSCTRL_SYSTOP_PARENT_ID        (0)
22 #define NI_TOWER_SYSCTRL_SYSTOP_IDX              (1)
23 #endif
24 
25 static struct host_system_t host_system_data = {0};
26 
27 #if defined(RD_SYSCTRL_NI_TOWER) || defined(RD_PERIPH_NI_TOWER)
28 /*
29  * Initializes the ATU region before configuring the NI-Tower. This function
30  * maps the physical base address of the NI-Tower instance received as the
31  * parameter to a logical address HOST_NI_TOWER_BASE.
32  */
ni_tower_pre_init(uint64_t ni_tower_phys_address)33 static int ni_tower_pre_init(uint64_t ni_tower_phys_address)
34 {
35     enum atu_error_t atu_err;
36     enum atu_roba_t roba_value;
37 
38     atu_err = atu_initialize_region(
39                 &ATU_DEV_S,
40                 HOST_NI_TOWER_ATU_ID,
41                 HOST_NI_TOWER_BASE,
42                 ni_tower_phys_address,
43                 HOST_NI_TOWER_SIZE);
44     if (atu_err != ATU_ERR_NONE) {
45         return -1;
46     }
47 
48     roba_value = ATU_ROBA_SET_1;
49     atu_err = set_axnsc(&ATU_DEV_S, roba_value, HOST_NI_TOWER_ATU_ID);
50     if (atu_err != ATU_ERR_NONE) {
51         return -1;
52     }
53 
54     roba_value = ATU_ROBA_SET_0;
55     atu_err = set_axprot1(&ATU_DEV_S, roba_value, HOST_NI_TOWER_ATU_ID);
56     if (atu_err != ATU_ERR_NONE) {
57         return -1;
58     }
59 
60     return 0;
61 }
62 
63 /* Un-initializes the ATU region after configuring the NI-Tower */
ni_tower_post_init(void)64 static int ni_tower_post_init(void)
65 {
66     enum atu_error_t atu_err;
67 
68     atu_err = atu_uninitialize_region(&ATU_DEV_S, HOST_NI_TOWER_ATU_ID);
69     if (atu_err != ATU_ERR_NONE) {
70         return -1;
71     }
72 
73     return 0;
74 }
75 #endif
76 
77 #ifdef RD_SYSCTRL_NI_TOWER
78 /* Voltage domain - 0 is the parent node of SYSTOP Power domain */
79 const struct ni_tower_component_node systop_parent_node = {
80     .type = NI_TOWER_SYSCTRL_SYSTOP_PARENT_TYPE,
81     .id = NI_TOWER_SYSCTRL_SYSTOP_PARENT_ID,
82 };
83 
84 /* List of node data to be skipped during AON discovery */
85 const struct ni_tower_skip_component_discovery_node_data
86     skip_aon_discovery_data[] = {
87     /*
88      * Skip discovery of SYSTOP power domain node since the node is
89      * undiscoverable during AON initialisation.
90      * CFG_NODE - 0
91      *    |
92      *    +--> VD - 0
93      *            |
94      *            +--> PD - 0 (AON) ...
95      *            |
96      *            +--> PD - 1 (SYSTOP) ...
97      *
98      */
99     {
100         .parent_node = &systop_parent_node,
101         .node_idx = NI_TOWER_SYSCTRL_SYSTOP_IDX,
102     },
103 };
104 
105 /*
106  * Programs the System control NI-Tower for nodes under Always-On (AON) domain.
107  */
ni_tower_sysctrl_aon_init(void)108 static int ni_tower_sysctrl_aon_init(void)
109 {
110     int err;
111 
112     err = ni_tower_pre_init(host_system_data.info.chip_ap_phys_base +
113                             HOST_SYSCTRL_NI_TOWER_PHYS_BASE);
114     if (err != 0) {
115         return err;
116     }
117 
118     SYSCTRL_NI_TOWER_DEV.skip_discovery_list =
119         &(struct ni_tower_skip_component_discovery_list ){
120             .skip_node_data = skip_aon_discovery_data,
121             .skip_node_count = ARRAY_SIZE(skip_aon_discovery_data),
122         };
123 
124     SYSCTRL_NI_TOWER_DEV.chip_addr_offset =
125             host_system_data.info.chip_ap_phys_base;
126     err = program_sysctrl_ni_tower_aon(host_system_data.info.chip_id);
127     if (err != 0) {
128         return err;
129     }
130 
131     SYSCTRL_NI_TOWER_DEV.skip_discovery_list = NULL;
132 
133     err = ni_tower_post_init();
134     if (err != 0) {
135         return err;
136     }
137 
138     return 0;
139 }
140 
141 /*
142  * Programs the System control NI-Tower for nodes under SYSTOP domain.
143  */
ni_tower_sysctrl_systop_init(void)144 static int ni_tower_sysctrl_systop_init(void)
145 {
146     int err;
147 
148     err = ni_tower_pre_init(host_system_data.info.chip_ap_phys_base +
149                             HOST_SYSCTRL_NI_TOWER_PHYS_BASE);
150     if (err != 0) {
151         return err;
152     }
153 
154     SYSCTRL_NI_TOWER_DEV.chip_addr_offset =
155             host_system_data.info.chip_ap_phys_base;
156     err = program_sysctrl_ni_tower_systop();
157     if (err != 0) {
158         return err;
159     }
160 
161     err = ni_tower_post_init();
162     if (err != 0) {
163         return err;
164     }
165 
166     return 0;
167 }
168 #endif
169 
170 #ifdef RD_PERIPH_NI_TOWER
171 /*
172  * Programs the Peripheral NI-Tower.
173  */
ni_tower_periph_init(void)174 static int ni_tower_periph_init(void)
175 {
176     int err;
177 
178     err = ni_tower_pre_init(host_system_data.info.chip_ap_phys_base +
179                             HOST_PERIPH_NI_TOWER_PHYS_BASE);
180     if (err != 0) {
181         return err;
182     }
183 
184     PERIPH_NI_TOWER_DEV.chip_addr_offset =
185         host_system_data.info.chip_ap_phys_base;
186     err = program_periph_ni_tower();
187     if (err != 0) {
188         return err;
189     }
190 
191     err = ni_tower_post_init();
192     if (err != 0) {
193         return err;
194     }
195 
196     return 0;
197 }
198 
199 /*
200  * Programs the Peripheral NI-Tower for ram_axim AP_BL1_RO region.
201  */
ni_tower_periph_init_ap_bl1_post_load(void)202 static int32_t ni_tower_periph_init_ap_bl1_post_load(void)
203 {
204     int32_t err;
205 
206     err = ni_tower_pre_init(host_system_data.info.chip_ap_phys_base +
207                             HOST_PERIPH_NI_TOWER_PHYS_BASE);
208     if (err != 0) {
209         return err;
210     }
211 
212     PERIPH_NI_TOWER_DEV.chip_addr_offset =
213         host_system_data.info.chip_ap_phys_base;
214     err = program_periph_ni_tower_post_ap_bl1_load();
215     if (err != 0) {
216         return err;
217     }
218 
219     err = ni_tower_post_init();
220     if (err != 0) {
221         return err;
222     }
223 
224     return 0;
225 }
226 #endif
227 
228 #ifdef HOST_SMMU
229 /*
230  * Initialize and bypass Granule Protection Check (GPC) to allow RSE and SCP
231  * to access HOST AP peripheral regions.
232  */
sysctrl_smmu_init(void)233 static int32_t sysctrl_smmu_init(void)
234 {
235     enum atu_roba_t roba_value;
236     enum atu_error_t atu_err;
237     enum smmu_error_t smmu_err;
238 
239     atu_err = atu_initialize_region(&ATU_DEV_S,
240                                     HOST_SYSCTRL_SMMU_ATU_ID,
241                                     HOST_SYSCTRL_SMMU_BASE,
242                                     (host_system_data.info.chip_ap_phys_base +
243                                         HOST_SYSCTRL_SMMU_PHYS_BASE),
244                                     HOST_SYSCTRL_SMMU_SIZE);
245     if (atu_err != ATU_ERR_NONE) {
246         return -1;
247     }
248 
249     roba_value = ATU_ROBA_SET_1;
250     atu_err = set_axnsc(&ATU_DEV_S, roba_value, HOST_SYSCTRL_SMMU_ATU_ID);
251     if (atu_err != ATU_ERR_NONE) {
252         return -1;
253     }
254 
255     roba_value = ATU_ROBA_SET_0;
256     atu_err = set_axprot1(&ATU_DEV_S, roba_value, HOST_SYSCTRL_SMMU_ATU_ID);
257     if (atu_err != ATU_ERR_NONE) {
258         return -1;
259     }
260 
261     /* Disable GPC */
262     smmu_err = smmu_gpc_disable(&HOST_SYSCTRL_SMMU_DEV);
263     if (smmu_err != SMMU_ERR_NONE){
264         return -1;
265     }
266 
267     /* Allow Access via SMMU */
268     smmu_err = smmu_access_enable(&HOST_SYSCTRL_SMMU_DEV);
269     if (smmu_err != SMMU_ERR_NONE){
270         return -1;
271     }
272 
273     atu_err = atu_uninitialize_region(&ATU_DEV_S, HOST_SYSCTRL_SMMU_ATU_ID);
274     if (atu_err != ATU_ERR_NONE) {
275         return -1;
276     }
277     return 0;
278 }
279 #endif
280 
281 /* Read Chip ID from OTP */
read_chip_id(uint32_t * chip_id)282 static int read_chip_id(uint32_t *chip_id)
283 {
284     int err;
285     uint32_t otp_chip_id;
286 
287     err = tfm_plat_otp_read(PLAT_OTP_ID_RSE_ID,
288                             sizeof(otp_chip_id),
289                             (uint8_t*)&otp_chip_id);
290     if (err != 0)
291         return err;
292 
293     *chip_id = otp_chip_id;
294     return 0;
295 }
296 
297 /* Initialize host system by collecting fixed data about the host system */
host_system_init(void)298 int host_system_init(void)
299 {
300     int res;
301 
302     res = read_chip_id(&host_system_data.info.chip_id);
303     if (res != 0) {
304         host_system_data.info.chip_id = 0;
305         return res;
306     }
307     host_system_data.info.chip_ap_phys_base =
308                     HOST_AP_CHIP_N_PHYS_BASE(host_system_data.info.chip_id);
309 
310     host_system_data.initialized = true;
311     return 0;
312 }
313 
314 /* Get info struct containing fixed data about the host system */
host_system_get_info(struct host_system_info_t ** info)315 int host_system_get_info(struct host_system_info_t **info)
316 {
317     if (info == NULL) {
318         return -1;
319     }
320 
321     if (host_system_data.initialized == false) {
322         return -1;
323     }
324 
325     *info = &host_system_data.info;
326     return 0;
327 }
328 
host_system_prepare_mscp_access(void)329 int host_system_prepare_mscp_access(void)
330 {
331 #ifdef RD_SYSCTRL_NI_TOWER
332     int res;
333 
334     /* Configure System Control NI-Tower for nodes under AON power domain */
335     res = ni_tower_sysctrl_aon_init();
336     if (res != 0) {
337         return res;
338     }
339 #endif
340     return 0;
341 }
342 
host_system_prepare_ap_access(void)343 int host_system_prepare_ap_access(void)
344 {
345     int res;
346 
347     (void)res;
348 
349     /*
350      * AP cannot be accessed until SCP setup is complete so wait for signal
351      * from SCP.
352      */
353     while (host_system_data.status.scp_systop_ready == false) {
354         __WFE();
355     }
356 
357 #ifdef RD_SYSCTRL_NI_TOWER
358     /* Configure System Control NI-Tower for nodes under SYSTOP power domain */
359     res = ni_tower_sysctrl_systop_init();
360     if (res != 0) {
361         return 1;
362     }
363 #endif
364 
365 #ifdef HOST_SMMU
366     /* Initialize the SYSCTRL SMMU for boot time */
367     res = sysctrl_smmu_init();
368     if (res != 0) {
369         return 1;
370     }
371 #endif
372 
373 #ifdef RD_PERIPH_NI_TOWER
374     /* Configure Peripheral NI-Tower */
375     res = ni_tower_periph_init();
376     if (res != 0) {
377         return 1;
378     }
379 #endif
380 
381     return 0;
382 }
383 
host_system_scp_signal_ap_ready(void)384 void host_system_scp_signal_ap_ready(void)
385 {
386     host_system_data.status.scp_systop_ready = true;
387 }
388 
host_system_finish(void)389 int host_system_finish(void)
390 {
391     int res;
392 
393     (void)res;
394 
395 #ifdef RD_PERIPH_NI_TOWER
396     /* Limit AP BL1 load region to read-only and lock the APU region */
397     res = ni_tower_periph_init_ap_bl1_post_load();
398     if (res != 0) {
399         return 1;
400     }
401 #endif
402 
403     return 0;
404 }
405