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