1 /*
2 * Copyright (c) 2023-2024, Arm Limited. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
8 #include "discovery/ni_tower_discovery_drv.h"
9 #include "ni_tower_apu_drv.h"
10 #include "ni_tower_apu_reg.h"
11 #include "util/ni_tower_util.h"
12
13 #include <stddef.h>
14
15 #define NI_TOWER_APU_ADDRESS_GRAN (1ULL << 6)
16 #define NI_TOWER_APU_ADDRESS_MASK (~(NI_TOWER_APU_ADDRESS_GRAN - 1))
17 #define NI_TOWER_APU_ADDRESS_H(addr) ((addr) >> 32)
18 #define NI_TOWER_APU_ADDRESS_L(addr) ((addr) & NI_TOWER_APU_ADDRESS_MASK)
19
20 #define NI_TOWER_APU_GET64_BASE_ADDRESS(addr, high, low) \
21 addr = (((uint64_t)(high) << 32) | (low)) & NI_TOWER_APU_ADDRESS_MASK
22
23 #define NI_TOWER_APU_GET64_END_ADDRESS(addr, high, low) \
24 addr = (((uint64_t)(high) << 32) | (low)) | (NI_TOWER_APU_ADDRESS_GRAN - 1)
25
ni_tower_apu_set_addr_range(const struct ni_tower_apu_dev * dev,const uint32_t region,uint64_t base_addr,uint64_t end_addr)26 static enum ni_tower_err ni_tower_apu_set_addr_range(
27 const struct ni_tower_apu_dev *dev,
28 const uint32_t region,
29 uint64_t base_addr,
30 uint64_t end_addr)
31 {
32 struct ni_tower_apu_reg_map* reg;
33
34 if (dev == NULL || dev->base == (uintptr_t)NULL) {
35 return NI_TOWER_ERR_INVALID_ARG;
36 }
37
38 reg = (struct ni_tower_apu_reg_map*)dev->base;
39
40 base_addr += dev->region_mapping_offset;
41 end_addr += dev->region_mapping_offset;
42
43 /* Check alignment of base and end addresses */
44 if (((base_addr & (NI_TOWER_APU_ADDRESS_GRAN - 1)) != 0) ||
45 ((~end_addr & (NI_TOWER_APU_ADDRESS_GRAN - 1)) != 0)) {
46 return NI_TOWER_ERR_INVALID_ARG;
47 }
48
49 /* Set base address */
50 reg->region[region].prbar_high = NI_TOWER_APU_ADDRESS_H(base_addr);
51 reg->region[region].prbar_low = NI_TOWER_APU_ADDRESS_L(base_addr);
52 /* Set end address */
53 reg->region[region].prlar_high = NI_TOWER_APU_ADDRESS_H(end_addr);
54 reg->region[region].prlar_low = NI_TOWER_APU_ADDRESS_L(end_addr);
55
56 return NI_TOWER_SUCCESS;
57 }
58
ni_tower_apu_set_access_perms(const struct ni_tower_apu_dev * dev,uint32_t region,const enum ni_tower_apu_access_perm_type permission,const enum ni_tower_apu_entity_type id_select)59 static enum ni_tower_err ni_tower_apu_set_access_perms(
60 const struct ni_tower_apu_dev *dev, uint32_t region,
61 const enum ni_tower_apu_access_perm_type permission,
62 const enum ni_tower_apu_entity_type id_select)
63 {
64 struct ni_tower_apu_reg_map* reg;
65
66 if (dev == NULL || dev->base == (uintptr_t)NULL) {
67 return NI_TOWER_ERR_INVALID_ARG;
68 }
69
70 reg = (struct ni_tower_apu_reg_map*)dev->base;
71
72 switch (id_select) {
73 case NI_T_ID_0_SELECT:
74 /* Clear permission */
75 reg->region[region].prid_low &= ~NI_TOWER_APU_PERM_0_MSK;
76 /* Set permission */
77 reg->region[region].prid_low |=
78 (permission << NI_TOWER_APU_PERM_0_POS) & NI_TOWER_APU_PERM_0_MSK;
79 break;
80 case NI_T_ID_1_SELECT:
81 /* Clear permission */
82 reg->region[region].prid_low &= ~NI_TOWER_APU_PERM_1_MSK;
83 /* Set permission */
84 reg->region[region].prid_low |=
85 (permission << NI_TOWER_APU_PERM_1_POS) & NI_TOWER_APU_PERM_1_MSK;
86 break;
87 case NI_T_ID_2_SELECT:
88 /* Clear permission */
89 reg->region[region].prid_high &= ~NI_TOWER_APU_PERM_2_MSK;
90 /* Set permission */
91 reg->region[region].prid_high |=
92 (permission << NI_TOWER_APU_PERM_2_POS) & NI_TOWER_APU_PERM_2_MSK;
93 break;
94 case NI_T_ID_3_SELECT:
95 /* Clear permission */
96 reg->region[region].prid_high &= ~NI_TOWER_APU_PERM_3_MSK;
97 /* Set permission */
98 reg->region[region].prid_high |=
99 (permission << NI_TOWER_APU_PERM_3_POS) & NI_TOWER_APU_PERM_3_MSK;
100 break;
101 default:
102 return NI_TOWER_ERR_INVALID_ARG;
103 }
104
105 return NI_TOWER_SUCCESS;
106 }
107
ni_tower_apu_set_entity_id(const struct ni_tower_apu_dev * dev,const uint32_t region,const uint32_t id_value,const enum ni_tower_apu_entity_type id_select)108 static enum ni_tower_err ni_tower_apu_set_entity_id(
109 const struct ni_tower_apu_dev *dev,
110 const uint32_t region,
111 const uint32_t id_value,
112 const enum ni_tower_apu_entity_type id_select)
113 {
114 struct ni_tower_apu_reg_map* reg;
115
116 if (dev == NULL || dev->base == (uintptr_t)NULL) {
117 return NI_TOWER_ERR_INVALID_ARG;
118 }
119
120 reg = (struct ni_tower_apu_reg_map*)dev->base;
121
122 switch (id_select) {
123 case NI_T_ID_0_SELECT:
124 /* Clear apu id */
125 reg->region[region].prid_low &= ~NI_TOWER_APU_ID_0_MSK;
126 /* Set apu id */
127 reg->region[region].prid_low |=
128 (id_value << NI_TOWER_APU_ID_0_POS) & NI_TOWER_APU_ID_0_MSK;
129 break;
130 case NI_T_ID_1_SELECT:
131 /* Clear apu id */
132 reg->region[region].prid_low &= ~NI_TOWER_APU_ID_1_MSK;
133 /* Set apu id */
134 reg->region[region].prid_low |=
135 (id_value << NI_TOWER_APU_ID_1_POS) & NI_TOWER_APU_ID_1_MSK;
136 break;
137 case NI_T_ID_2_SELECT:
138 /* Clear apu id */
139 reg->region[region].prid_high &= ~NI_TOWER_APU_ID_2_MSK;
140 /* Set apu id */
141 reg->region[region].prid_high |=
142 (id_value << NI_TOWER_APU_ID_2_POS) & NI_TOWER_APU_ID_2_MSK;
143 break;
144 case NI_T_ID_3_SELECT:
145 /* Clear apu id */
146 reg->region[region].prid_high &= ~NI_TOWER_APU_ID_3_MSK;
147 /* Set apu id */
148 reg->region[region].prid_high |=
149 (id_value << NI_TOWER_APU_ID_3_POS) & NI_TOWER_APU_ID_3_MSK;
150 break;
151 default:
152 return NI_TOWER_ERR_INVALID_ARG;
153 }
154
155 return NI_TOWER_SUCCESS;
156 }
157
ni_tower_apu_set_lock(const struct ni_tower_apu_dev * dev,const uint32_t region,const enum ni_tower_apu_lock_type lock)158 static enum ni_tower_err ni_tower_apu_set_lock(
159 const struct ni_tower_apu_dev *dev,
160 const uint32_t region,
161 const enum ni_tower_apu_lock_type lock)
162 {
163 struct ni_tower_apu_reg_map* reg;
164
165 if (dev == NULL || dev->base == (uintptr_t)NULL) {
166 return NI_TOWER_ERR_INVALID_ARG;
167 }
168
169 reg = (struct ni_tower_apu_reg_map*)dev->base;
170
171 /* Once locked, the region cannot be unlocked unless APU is reset again. */
172 reg->region[region].prbar_low |= (lock << NI_TOWER_APU_LOCK_POS) &
173 NI_TOWER_APU_LOCK_MSK;
174
175 return NI_TOWER_SUCCESS;
176 }
177
ni_tower_apu_set_br(const struct ni_tower_apu_dev * dev,const uint32_t region,const enum ni_tower_apu_br_type background)178 static enum ni_tower_err ni_tower_apu_set_br(
179 const struct ni_tower_apu_dev *dev,
180 const uint32_t region,
181 const enum ni_tower_apu_br_type background)
182 {
183 struct ni_tower_apu_reg_map* reg;
184
185 if (dev == NULL || dev->base == (uintptr_t)NULL) {
186 return NI_TOWER_ERR_INVALID_ARG;
187 }
188
189 reg = (struct ni_tower_apu_reg_map*)dev->base;
190
191 /* Clear background bit */
192 reg->region[region].prbar_low &= ~NI_TOWER_APU_BR_MSK;
193 /* Set background bit */
194 reg->region[region].prbar_low |= (background << NI_TOWER_APU_BR_POS) &
195 NI_TOWER_APU_BR_MSK;
196
197 return NI_TOWER_SUCCESS;
198 }
199
ni_tower_apu_set_region_enable(const struct ni_tower_apu_dev * dev,const uint32_t region)200 static enum ni_tower_err ni_tower_apu_set_region_enable(
201 const struct ni_tower_apu_dev *dev,
202 const uint32_t region)
203 {
204 enum ni_tower_apu_br_type temp_br_type, curr_br_type;
205 struct ni_tower_apu_reg_map* reg;
206 uint64_t temp_base_addr, temp_end_addr, curr_base_addr, curr_end_addr;
207 uint32_t r_idx;
208
209 if (dev == NULL || dev->base == (uintptr_t)NULL) {
210 return NI_TOWER_ERR_INVALID_ARG;
211 }
212
213 reg = (struct ni_tower_apu_reg_map*)dev->base;
214
215 /* Clear apu region enable bit */
216 reg->region[region].prbar_low &= ~NI_TOWER_APU_REGION_ENABLE_MSK;
217
218 /*
219 * Check whether two foreground or two background region overlaps.
220 * Foreground region can overlap two different background region, APU
221 * prioritises the foreground access permissions.
222 */
223 curr_br_type = reg->region[region].prbar_low & NI_TOWER_APU_BR_MSK ?
224 NI_T_BACKGROUND : NI_T_FOREGROUND;
225
226 NI_TOWER_APU_GET64_BASE_ADDRESS(curr_base_addr,
227 reg->region[region].prbar_high,
228 reg->region[region].prbar_low);
229
230 NI_TOWER_APU_GET64_END_ADDRESS(curr_end_addr,
231 reg->region[region].prlar_high,
232 reg->region[region].prlar_low);
233
234 for (r_idx = 0; r_idx < NI_TOWER_MAX_APU_REGIONS; ++r_idx) {
235 temp_br_type = reg->region[r_idx].prbar_low & NI_TOWER_APU_BR_MSK ?
236 NI_T_BACKGROUND : NI_T_FOREGROUND;
237 if ((reg->region[r_idx].prbar_low & NI_TOWER_APU_REGION_ENABLE) &&
238 (temp_br_type == curr_br_type))
239 {
240 NI_TOWER_APU_GET64_BASE_ADDRESS(temp_base_addr,
241 reg->region[r_idx].prbar_high,
242 reg->region[r_idx].prbar_low);
243
244 NI_TOWER_APU_GET64_END_ADDRESS(temp_end_addr,
245 reg->region[r_idx].prlar_high,
246 reg->region[r_idx].prlar_low);
247
248 if (ni_tower_check_region_overlaps(curr_base_addr, curr_end_addr,
249 temp_base_addr, temp_end_addr) !=
250 NI_TOWER_SUCCESS) {
251 return NI_TOWER_ERR_REGION_OVERLAPS;
252 }
253 }
254 }
255
256 /* Set apu region enable bit */
257 reg->region[region].prbar_low |= NI_TOWER_APU_REGION_ENABLE;
258
259 return NI_TOWER_SUCCESS;
260 }
261
ni_tower_apu_set_id_valid(const struct ni_tower_apu_dev * dev,const uint32_t region,const enum ni_tower_apu_entity_valid_type valid)262 static enum ni_tower_err ni_tower_apu_set_id_valid(
263 const struct ni_tower_apu_dev *dev,
264 const uint32_t region,
265 const enum ni_tower_apu_entity_valid_type valid)
266 {
267 struct ni_tower_apu_reg_map* reg;
268
269 if (dev == NULL || dev->base == (uintptr_t)NULL) {
270 return NI_TOWER_ERR_INVALID_ARG;
271 }
272
273 reg = (struct ni_tower_apu_reg_map*)dev->base;
274
275 /* Clear id valid */
276 reg->region[region].prbar_low &= ~NI_TOWER_APU_ID_VALID_MSK;
277 /* Set id valid */
278 reg->region[region].prlar_low |= (valid << NI_TOWER_APU_ID_VALID_POS) &
279 NI_TOWER_APU_ID_VALID_MSK;
280
281 return NI_TOWER_SUCCESS;
282 }
283
ni_tower_apu_enable(const struct ni_tower_apu_dev * dev)284 enum ni_tower_err ni_tower_apu_enable(const struct ni_tower_apu_dev *dev)
285 {
286 struct ni_tower_apu_reg_map* reg;
287
288 if (dev == NULL || dev->base == (uintptr_t)NULL) {
289 return NI_TOWER_ERR_INVALID_ARG;
290 }
291
292 reg = (struct ni_tower_apu_reg_map*)dev->base;
293
294 /*
295 * Cannot disable this field once enabled. This can only cleared by
296 * APU reset.
297 */
298 reg->apu_ctlr |= NI_TOWER_APU_CTLR_APU_ENABLE;
299
300 return NI_TOWER_SUCCESS;
301 }
302
ni_tower_apu_sync_err_enable(const struct ni_tower_apu_dev * dev)303 enum ni_tower_err ni_tower_apu_sync_err_enable(
304 const struct ni_tower_apu_dev *dev)
305 {
306 struct ni_tower_apu_reg_map* reg;
307
308 if (dev == NULL || dev->base == (uintptr_t)NULL) {
309 return NI_TOWER_ERR_INVALID_ARG;
310 }
311
312 reg = (struct ni_tower_apu_reg_map*)dev->base;
313
314 /* Clear sync_err_en */
315 reg->apu_ctlr &= ~NI_TOWER_APU_CTLR_SYNC_ERROR_EN_MSK;
316 /* Set sync_err_en */
317 reg->apu_ctlr |= NI_TOWER_APU_CTLR_SYNC_ERROR_EN;
318
319 return NI_TOWER_SUCCESS;
320 }
321
ni_tower_apu_dev_init(const struct ni_tower_dev * ni_tower_dev,const struct ni_tower_component_node * component,const uint64_t region_mapping_offset,struct ni_tower_apu_dev * dev)322 enum ni_tower_err ni_tower_apu_dev_init(
323 const struct ni_tower_dev *ni_tower_dev,
324 const struct ni_tower_component_node *component,
325 const uint64_t region_mapping_offset,
326 struct ni_tower_apu_dev *dev)
327 {
328 enum ni_tower_err err;
329 uint32_t off_addr;
330 struct ni_tower_discovery_node root = {
331 .node_type = NI_TOWER_CFGNI,
332 .node_id = 0,
333 .node_off_addr = 0x0
334 };
335
336 if (ni_tower_dev == NULL || ni_tower_dev->periphbase == (uintptr_t)NULL) {
337 return NI_TOWER_ERR_INVALID_ARG;
338 }
339
340 if (component == NULL || dev == NULL) {
341 return NI_TOWER_ERR_INVALID_ARG;
342 }
343
344 /* Discover offset address for the APU */
345 err = ni_tower_discover_offset(
346 ni_tower_dev, &root,
347 component->type,
348 component->id,
349 NI_TOWER_APU, &off_addr);
350 if (err != NI_TOWER_SUCCESS) {
351 return err;
352 }
353
354 dev->base = ni_tower_dev->periphbase + off_addr;
355 dev->region_mapping_offset = region_mapping_offset;
356
357 return NI_TOWER_SUCCESS;
358 }
359
ni_tower_apu_configure_region(const struct ni_tower_apu_dev * dev,const struct ni_tower_apu_reg_cfg_info * cfg_info,const uint32_t region)360 enum ni_tower_err ni_tower_apu_configure_region(
361 const struct ni_tower_apu_dev *dev,
362 const struct ni_tower_apu_reg_cfg_info *cfg_info,
363 const uint32_t region)
364 {
365 struct ni_tower_apu_reg_map *reg;
366 enum ni_tower_err err;
367 uint32_t id_idx;
368
369 if (dev == NULL || dev->base == (uintptr_t)NULL) {
370 return NI_TOWER_ERR_INVALID_ARG;
371 }
372
373 reg = (struct ni_tower_apu_reg_map *)dev->base;
374
375 /*
376 * If this region is locked, disallow re-configuration of this APU region
377 */
378 if (reg->region[region].prbar_low & NI_TOWER_APU_LOCK) {
379 return NI_TOWER_ERR_NOT_PERMITTED;
380 }
381
382 err = ni_tower_apu_set_addr_range(dev, region,
383 cfg_info->base_addr,
384 cfg_info->end_addr);
385 if (err != NI_TOWER_SUCCESS) {
386 return err;
387 }
388
389 err = ni_tower_apu_set_br(dev, region, cfg_info->background);
390 if (err != NI_TOWER_SUCCESS) {
391 return err;
392 }
393
394 err = ni_tower_apu_set_id_valid(dev, region, cfg_info->id_valid);
395 if (err != NI_TOWER_SUCCESS) {
396 return err;
397 }
398
399 for (id_idx = 0; id_idx < NI_TOWER_APU_NUM_ENTITIES; ++id_idx) {
400 err = ni_tower_apu_set_access_perms(dev, region,
401 cfg_info->permissions[id_idx],
402 (1 << id_idx));
403 if (err != NI_TOWER_SUCCESS) {
404 return err;
405 }
406
407 err = ni_tower_apu_set_entity_id(dev, region,
408 cfg_info->entity_ids[id_idx],
409 (1 << id_idx));
410 if (err != NI_TOWER_SUCCESS) {
411 return err;
412 }
413 }
414
415 if (cfg_info->region_enable == NI_T_REGION_ENABLE) {
416 err = ni_tower_apu_set_region_enable(dev, region);
417 if (err != NI_TOWER_SUCCESS) {
418 return err;
419 }
420 }
421
422 err = ni_tower_apu_set_lock(dev, region, cfg_info->lock);
423 if (err != NI_TOWER_SUCCESS) {
424 return err;
425 }
426
427 return NI_TOWER_SUCCESS;
428 }
429
get_next_available_region(const struct ni_tower_apu_dev * dev,uint32_t * region)430 static enum ni_tower_err get_next_available_region(
431 const struct ni_tower_apu_dev *dev,
432 uint32_t *region)
433 {
434 struct ni_tower_apu_reg_map* reg;
435 uint32_t r_idx;
436
437 if (dev == NULL || dev->base == (uintptr_t)NULL) {
438 return NI_TOWER_ERR_INVALID_ARG;
439 }
440
441 reg = (struct ni_tower_apu_reg_map*)dev->base;
442
443 for (r_idx = 0; r_idx < NI_TOWER_MAX_APU_REGIONS; ++r_idx) {
444 if (!(reg->region[r_idx].prbar_low & NI_TOWER_APU_REGION_ENABLE)) {
445 *region = r_idx;
446 return NI_TOWER_SUCCESS;
447 }
448 }
449
450 return NI_TOWER_ERR;
451 }
452
ni_tower_apu_configure_next_available_region(const struct ni_tower_apu_dev * dev,const struct ni_tower_apu_reg_cfg_info * cfg_info)453 enum ni_tower_err ni_tower_apu_configure_next_available_region(
454 const struct ni_tower_apu_dev *dev,
455 const struct ni_tower_apu_reg_cfg_info *cfg_info)
456 {
457 enum ni_tower_err err;
458 uint32_t next_available_region;
459
460 err = get_next_available_region(dev, &next_available_region);
461 if (err != NI_TOWER_SUCCESS) {
462 return err;
463 }
464
465 return ni_tower_apu_configure_region(dev, cfg_info, next_available_region);
466 }
467