1 /*
2  * Arm SCP/MCP Software
3  * Copyright (c) 2020-2023, Arm Limited and Contributors. All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include <cmn700.h>
9 
10 #include <fwk_assert.h>
11 #include <fwk_log.h>
12 #include <fwk_math.h>
13 
14 #include <inttypes.h>
15 
16 #define MOD_NAME "[CMN700] "
17 
18 /*
19  * Encoding bits size of the X and Y position in the Node info value.
20  * If X and Y dimension are less than 4, encoding bits size will be 2.
21  * If X and Y dimension are between 5 and 8, encoding bits size will be 3.
22  */
23 static unsigned int encoding_bits;
24 static unsigned int mask_bits;
25 
26 static const char *const cmn700_rev_to_name[] = {
27     [CMN700_PERIPH_ID_2_REV_R0_P0] = "r0p0",
28     [CMN700_PERIPH_ID_2_REV_R1_P0] = "r1p0",
29     [CMN700_PERIPH_ID_2_REV_R1_P1] = "r1p1",
30     [CMN700_PERIPH_ID_2_REV_R2_P0] = "r2p0",
31     [CMN700_PERIPH_ID_UNKNOWN_REV] = "Unknown!",
32 };
33 
get_node_device_port_count(void * node_base)34 unsigned int get_node_device_port_count(void *node_base)
35 {
36     struct node_header *node = node_base;
37     return (node->NODE_INFO & CMN700_MXP_NODE_INFO_NUM_DEVICE_PORT_MASK) >>
38         CMN700_MXP_NODE_INFO_NUM_DEVICE_PORT_POS;
39 }
40 
get_node_child_count(void * node_base)41 unsigned int get_node_child_count(void *node_base)
42 {
43     struct node_header *node = node_base;
44     return node->CHILD_INFO & CMN700_CHILD_INFO_COUNT;
45 }
46 
get_node_type(void * node_base)47 enum node_type get_node_type(void *node_base)
48 {
49     struct node_header *node = node_base;
50     return (enum node_type)(node->NODE_INFO & CMN700_NODE_INFO_TYPE);
51 }
52 
get_node_id(void * node_base)53 unsigned int get_node_id(void *node_base)
54 {
55     struct node_header *node = node_base;
56     return (node->NODE_INFO & CMN700_NODE_INFO_ID) >> CMN700_NODE_INFO_ID_POS;
57 }
58 
get_node_logical_id(void * node_base)59 unsigned int get_node_logical_id(void *node_base)
60 {
61     struct node_header *node = node_base;
62     return (node->NODE_INFO & CMN700_NODE_INFO_LOGICAL_ID) >>
63         CMN700_NODE_INFO_LOGICAL_ID_POS;
64 }
65 
get_child_node(uintptr_t base,void * node_base,unsigned int child_index)66 void *get_child_node(uintptr_t base, void *node_base, unsigned int child_index)
67 {
68     struct node_header *node = node_base;
69     uint32_t child_pointer;
70     unsigned int offset;
71     void *child_node;
72 
73     child_pointer = node->CHILD_POINTER[child_index];
74     offset = child_pointer & CMN700_CHILD_POINTER_OFFSET;
75 
76     child_node = (void *)(base + offset);
77     return child_node;
78 }
79 
get_child_node_id(void * node_base,unsigned int child_index)80 unsigned int get_child_node_id(void *node_base, unsigned int child_index)
81 {
82     struct node_header *node = node_base;
83     uint32_t node_pointer;
84     unsigned int node_id;
85 
86     node_pointer = (node->CHILD_POINTER[child_index] &
87                     CMN700_CHILD_POINTER_EXT_NODE_POINTER) >>
88         CMN700_CHILD_POINTER_EXT_NODE_POINTER_POS;
89 
90     /*
91      * For mesh widths using 2 bits each for X,Y encoding:
92      * NodeID[1:0] = DeviceID[3:2]
93      * NodeID[2]   = DeviceID[0]
94      * NodeID[4:3] = NODE POINTER[7:6]
95      * NodeID[6:5] = NODE POINTER[9:8]
96      *
97      * For mesh widths using 3 bits each for X,Y encoding:
98      * NodeID[1:0] = DeviceID[3:2]
99      * NodeID[2]   = DeviceID[0]
100      * NodeID[5:3] = NODE POINTER[8:6]
101      * NodeID[8:6] = NODE POINTER[11:9]
102      */
103     node_id = (((node_pointer >> 6) & 0xff) << 3) |
104         ((node_pointer & 0x1) << 2) | ((node_pointer >> 2) & 0x3);
105 
106     return node_id;
107 }
108 
get_cmn700_revision(struct cmn700_cfgm_reg * root)109 unsigned int get_cmn700_revision(struct cmn700_cfgm_reg *root)
110 {
111     return (root->PERIPH_ID[1] & CMN700_PERIPH_ID_2_MASK) >>
112         CMN700_PERIPH_ID_2_REV_POS;
113 }
114 
get_cmn700_revision_name(struct cmn700_cfgm_reg * root)115 const char *get_cmn700_revision_name(struct cmn700_cfgm_reg *root)
116 {
117     unsigned int revision;
118 
119     revision = get_cmn700_revision(root);
120     if (revision > CMN700_PERIPH_ID_UNKNOWN_REV)
121         revision = CMN700_PERIPH_ID_UNKNOWN_REV;
122 
123     return cmn700_rev_to_name[revision];
124 }
125 
is_child_external(void * node_base,unsigned int child_index)126 bool is_child_external(void *node_base, unsigned int child_index)
127 {
128     struct node_header *node = node_base;
129     return !!(node->CHILD_POINTER[child_index] & (1U << 31));
130 }
131 
get_port_number(unsigned int child_node_id,unsigned int xp_port_cnt)132 unsigned int get_port_number(
133     unsigned int child_node_id,
134     unsigned int xp_port_cnt)
135 {
136     if (xp_port_cnt == 2) {
137         return (child_node_id >> CMN700_NODE_ID_PORT_POS) &
138             CMN700_NODE_ID_PORT_MASK;
139     } else {
140         /* For port counts 3 and 4 */
141         return (child_node_id >> CMN700_MULTI_PORTS_NODE_ID_PORT_POS) &
142             CMN700_MULTI_PORTS_NODE_ID_PORT_MASK;
143     }
144 }
145 
get_device_type(void * mxp_base,int port)146 unsigned int get_device_type(void *mxp_base, int port)
147 {
148     struct cmn700_mxp_reg *mxp = mxp_base;
149     return mxp->PORT_CONNECT_INFO[port] &
150         CMN700_MXP_PORT_CONNECT_INFO_DEVICE_TYPE_MASK;
151 }
152 
is_cal_connected(void * mxp_base,bool port)153 bool is_cal_connected(void *mxp_base, bool port)
154 {
155     struct cmn700_mxp_reg *mxp = mxp_base;
156     return (mxp->PORT_CONNECT_INFO[port] &
157             CMN700_MXP_PORT_CONNECT_INFO_CAL_CONNECTED_MASK) >>
158         CMN700_MXP_PORT_CONNECT_INFO_CAL_CONNECTED_POS;
159 }
160 
is_device_type_rnf(void * mxp_base,bool port)161 bool is_device_type_rnf(void *mxp_base, bool port)
162 {
163     return (
164         (get_device_type(mxp_base, port) == DEVICE_TYPE_RN_F_CHIB_ESAM) ||
165         (get_device_type(mxp_base, port) == DEVICE_TYPE_RN_F_CHIC_ESAM) ||
166         (get_device_type(mxp_base, port) == DEVICE_TYPE_RN_F_CHID_ESAM) ||
167         (get_device_type(mxp_base, port) == DEVICE_TYPE_RN_F_CHIE_ESAM));
168 }
169 
get_rnsam_htg_rcomp_lsb_bit_pos(void * rnsam_reg)170 static unsigned int get_rnsam_htg_rcomp_lsb_bit_pos(void *rnsam_reg)
171 {
172     struct cmn700_rnsam_reg *rnsam = rnsam_reg;
173     return (
174         rnsam->UNIT_INFO[1] & CMN700_RNSAM_UNIT_INFO_HTG_RCOMP_LSB_PARAM_MASK);
175 }
176 
get_rnsam_nonhash_rcomp_lsb_bit_pos(void * rnsam_reg)177 static unsigned int get_rnsam_nonhash_rcomp_lsb_bit_pos(void *rnsam_reg)
178 {
179     struct cmn700_rnsam_reg *rnsam = rnsam_reg;
180     return (rnsam->UNIT_INFO[1] &
181             CMN700_RNSAM_UNIT_INFO_NONHASH_RCOMP_LSB_PARAM_MASK) >>
182         CMN700_RNSAM_UNIT_INFO_NONHASH_RCOMP_LSB_PARAM_POS;
183 }
184 
get_rnsam_lsb_addr_mask(void * rnsam_reg,enum sam_type sam_type)185 static uint64_t get_rnsam_lsb_addr_mask(void *rnsam_reg, enum sam_type sam_type)
186 {
187     uint64_t lsb_bit_pos;
188     struct cmn700_rnsam_reg *rnsam = rnsam_reg;
189 
190     lsb_bit_pos = (sam_type == SAM_TYPE_NON_HASH_MEM_REGION) ?
191         get_rnsam_nonhash_rcomp_lsb_bit_pos(rnsam) :
192         get_rnsam_htg_rcomp_lsb_bit_pos(rnsam);
193 
194     return (1 << lsb_bit_pos) - 1;
195 }
196 
get_rnsam_nonhash_range_comp_en_mode(void * rnsam_reg)197 bool get_rnsam_nonhash_range_comp_en_mode(void *rnsam_reg)
198 {
199     struct cmn700_rnsam_reg *rnsam = rnsam_reg;
200     return (rnsam->UNIT_INFO[0] &
201             CMN700_RNSAM_UNIT_INFO_NONHASH_RANGE_COMP_EN_MASK) >>
202         CMN700_RNSAM_UNIT_INFO_NONHASH_RANGE_COMP_EN_POS;
203 }
204 
get_rnsam_htg_range_comp_en_mode(void * rnsam_reg)205 bool get_rnsam_htg_range_comp_en_mode(void *rnsam_reg)
206 {
207     struct cmn700_rnsam_reg *rnsam = rnsam_reg;
208     return (rnsam->UNIT_INFO[0] &
209             CMN700_RNSAM_UNIT_INFO_HTG_RANGE_COMP_EN_MASK) >>
210         CMN700_RNSAM_UNIT_INFO_HTG_RANGE_COMP_EN_POS;
211 }
212 
get_hnsam_range_comp_en_mode(void * hnf_reg)213 bool get_hnsam_range_comp_en_mode(void *hnf_reg)
214 {
215     struct cmn700_hnf_reg *hnf = hnf_reg;
216     return (hnf->UNIT_INFO[1] & CMN700_HNF_UNIT_INFO_HNSAM_RCOMP_EN_MASK) >>
217         CMN700_HNF_UNIT_INFO_HNSAM_RCOMP_EN_POS;
218 }
219 
sam_encode_region_size(uint64_t size)220 uint64_t sam_encode_region_size(uint64_t size)
221 {
222     uint64_t blocks;
223     uint64_t result;
224 
225     /* Size must be a multiple of SAM_GRANULARITY */
226     fwk_assert((size % SAM_GRANULARITY) == 0);
227 
228     /* Size also must be a power of two */
229     fwk_assert((size & (size - 1)) == 0);
230 
231     blocks = size / SAM_GRANULARITY;
232     result = fwk_math_log2(blocks);
233 
234     return result;
235 }
236 
is_region_aligned(struct cmn700_rnsam_reg * rnsam,struct mod_cmn700_mem_region_map * mmap,enum sam_type sam_type)237 bool is_region_aligned(
238     struct cmn700_rnsam_reg *rnsam,
239     struct mod_cmn700_mem_region_map *mmap,
240     enum sam_type sam_type)
241 {
242     uint64_t lsb_addr_mask;
243 
244     lsb_addr_mask = get_rnsam_lsb_addr_mask(rnsam, sam_type);
245     return ((mmap->base & lsb_addr_mask) | (mmap->size & lsb_addr_mask)) == 0;
246 }
247 
is_non_hash_region_mapped(struct cmn700_rnsam_reg * rnsam,uint32_t region_io_count,struct mod_cmn700_mem_region_map * mmap,uint32_t * region_index)248 bool is_non_hash_region_mapped(
249     struct cmn700_rnsam_reg *rnsam,
250     uint32_t region_io_count,
251     struct mod_cmn700_mem_region_map *mmap,
252     uint32_t *region_index)
253 {
254     int idx;
255     unsigned int programmed_node_id;
256     uint32_t group;
257     uint32_t bit_pos;
258     volatile uint64_t *reg;
259     uint64_t lsb_addr_mask;
260 
261     lsb_addr_mask =
262         get_rnsam_lsb_addr_mask(rnsam, SAM_TYPE_NON_HASH_MEM_REGION);
263 
264     for (idx = region_io_count - 1; idx >= 0; idx--) {
265         reg = &rnsam->NON_HASH_MEM_REGION[idx];
266         if (mmap->base == (*reg & ~lsb_addr_mask)) {
267             group = idx / CMN700_RNSAM_NON_HASH_TGT_NODEID_ENTRIES_PER_GROUP;
268             bit_pos = CMN700_RNSAM_NON_HASH_TGT_NODEID_ENTRY_BITS_WIDTH *
269                 (idx % CMN700_RNSAM_NON_HASH_TGT_NODEID_ENTRIES_PER_GROUP);
270             programmed_node_id =
271                 (rnsam->NON_HASH_TGT_NODEID[group] >> bit_pos) &
272                 CMN700_RNSAM_NON_HASH_TGT_NODEID_ENTRY_MASK;
273             mmap->node_id &= CMN700_RNSAM_NON_HASH_TGT_NODEID_ENTRY_MASK;
274 
275             if (programmed_node_id == mmap->node_id) {
276                 FWK_LOG_INFO(
277                     MOD_NAME "Found region: %d mapped for Node: %u ",
278                     idx,
279                     mmap->node_id);
280                 *region_index = idx;
281 
282                 return true;
283             } else {
284                 FWK_LOG_ERR(
285                     MOD_NAME
286                     "Address: 0x%llx mapped to different node id:"
287                     " %u than expected: %u\n",
288                     mmap->base,
289                     programmed_node_id,
290                     mmap->node_id);
291                 fwk_unexpected();
292             }
293         }
294     }
295 
296     return false;
297 }
298 
configure_region(void * rnsam_reg,unsigned int region_idx,uint64_t base,uint64_t size,enum sam_node_type node_type,enum sam_type sam_type)299 void configure_region(
300     void *rnsam_reg,
301     unsigned int region_idx,
302     uint64_t base,
303     uint64_t size,
304     enum sam_node_type node_type,
305     enum sam_type sam_type)
306 {
307     bool prog_start_and_end_addr;
308     uint64_t lsb_addr_mask;
309     uint64_t value;
310     struct cmn700_rnsam_reg *rnsam = rnsam_reg;
311     volatile uint64_t *reg;
312     volatile uint64_t *reg_cfg2;
313 
314     fwk_assert(rnsam_reg);
315 
316     if (sam_type == SAM_TYPE_NON_HASH_MEM_REGION) {
317         if (region_idx >= MAX_NON_HASH_MEM_COUNT) {
318             FWK_LOG_ERR(
319                 MOD_NAME
320                 "Region idx: %d should be less than max non hash region count "
321                 "of %d",
322                 region_idx,
323                 MAX_NON_HASH_MEM_COUNT);
324             fwk_unexpected();
325         }
326 
327         if (region_idx < NON_HASH_MEM_REG_COUNT) {
328             reg = &rnsam->NON_HASH_MEM_REGION[region_idx];
329             reg_cfg2 = &rnsam->NON_HASH_MEM_REGION_CFG2[region_idx];
330         } else {
331             reg = &rnsam->NON_HASH_MEM_REGION_GRP2
332                        [region_idx - NON_HASH_MEM_REG_COUNT];
333             reg_cfg2 = &rnsam->NON_HASH_MEM_REGION_CFG2_GRP2
334                             [region_idx - NON_HASH_MEM_REG_COUNT];
335         }
336     } else if (sam_type == SAM_TYPE_SYS_CACHE_GRP_REGION) {
337         if (region_idx >= MAX_SCG_COUNT) {
338             FWK_LOG_ERR(
339                 MOD_NAME
340                 "Region idx: %d should be less then max SCG region count of %d",
341                 region_idx,
342                 MAX_SCG_COUNT);
343             fwk_unexpected();
344         }
345         reg = &rnsam->SYS_CACHE_GRP_REGION[region_idx];
346         reg_cfg2 = &rnsam->HASHED_TGT_GRP_CFG2_REGION[region_idx];
347     } else {
348         FWK_LOG_ERR(MOD_NAME "Unexpected sam_type!");
349         fwk_unexpected();
350         return;
351     }
352 
353     /* Check if the start and end address has to be programmed */
354     prog_start_and_end_addr = (sam_type == SAM_TYPE_NON_HASH_MEM_REGION) ?
355         get_rnsam_nonhash_range_comp_en_mode(rnsam) :
356         get_rnsam_htg_range_comp_en_mode(rnsam);
357 
358     if ((!prog_start_and_end_addr) && ((base % size) != 0)) {
359         FWK_LOG_ERR(
360             MOD_NAME "Base: 0x%" PRIx64 " should align with Size: 0x%" PRIx64,
361             base,
362             size);
363         fwk_unexpected();
364     }
365 
366     /* Get the LSB mask from LSB bit position defining minimum region size */
367     lsb_addr_mask = get_rnsam_lsb_addr_mask(rnsam, sam_type);
368 
369     value = CMN700_RNSAM_REGION_ENTRY_VALID;
370     value |= node_type << CMN700_RNSAM_REGION_ENTRY_TYPE_POS;
371 
372     if (prog_start_and_end_addr) {
373         value |= (base & ~lsb_addr_mask);
374         *reg = value;
375         *reg_cfg2 = (base + size - 1) & ~lsb_addr_mask;
376     } else {
377         value |= sam_encode_region_size(size)
378             << CMN700_RNSAM_REGION_ENTRY_SIZE_POS;
379         value |= (base / SAM_GRANULARITY) << CMN700_RNSAM_REGION_ENTRY_BASE_POS;
380         *reg = value;
381     }
382 }
383 
384 static const char *const type_to_name[] = {
385     [NODE_TYPE_INVALID]     = "<Invalid>",
386     [NODE_TYPE_DVM]         = "DVM",
387     [NODE_TYPE_CFG]         = "CFG",
388     [NODE_TYPE_DTC]         = "DTC",
389     [NODE_TYPE_HN_I]        = "HN-I",
390     [NODE_TYPE_HN_F]        = "HN-F",
391     [NODE_TYPE_XP]          = "XP",
392     [NODE_TYPE_SBSX]        = "SBSX",
393     [NODE_TYPE_MPAM_S]      = "MPAM-S",
394     [NODE_TYPE_MPAM_NS]     = "MPAM-NS",
395     [NODE_TYPE_RN_I]        = "RN-I",
396     [NODE_TYPE_RN_D]        = "RN-D",
397     [NODE_TYPE_RN_SAM]      = "RN-SAM",
398     [NODE_TYPE_HN_P]        = "HN-P",
399 };
400 
401 static const char *const type_to_name_cml[] = {
402     [NODE_TYPE_CXRA - NODE_TYPE_CML_BASE] = "CXRA",
403     [NODE_TYPE_CXHA - NODE_TYPE_CML_BASE] = "CXHA",
404     [NODE_TYPE_CXLA - NODE_TYPE_CML_BASE] = "CXLA",
405     [NODE_TYPE_CCRA - NODE_TYPE_CML_BASE] = "CCRA",
406     [NODE_TYPE_CCHA - NODE_TYPE_CML_BASE] = "CCHA",
407     [NODE_TYPE_CCLA - NODE_TYPE_CML_BASE] = "CCLA",
408 };
409 
get_node_type_name(enum node_type node_type)410 const char *get_node_type_name(enum node_type node_type)
411 {
412     /* Base node IDs */
413     if (node_type <= NODE_TYPE_HN_P)
414         return type_to_name[node_type];
415 
416     /* CML node IDs */
417     if ((node_type >= NODE_TYPE_CML_BASE) && (node_type <= NODE_TYPE_CCLA))
418         return type_to_name_cml[node_type - NODE_TYPE_CML_BASE];
419 
420     /* Invalid node IDs */
421     return type_to_name[NODE_TYPE_INVALID];
422 }
423 
get_node_pos_x(void * node_base)424 unsigned int get_node_pos_x(void *node_base)
425 {
426     struct node_header *node = node_base;
427     return (get_node_id(node) >> (CMN700_NODE_ID_Y_POS + encoding_bits)) &
428         mask_bits;
429 }
430 
get_node_pos_y(void * node_base)431 unsigned int get_node_pos_y(void *node_base)
432 {
433     struct node_header *node = node_base;
434     return (get_node_id(node) >> CMN700_NODE_ID_Y_POS) & mask_bits;
435 }
436 
set_encoding_and_masking_bits(const struct mod_cmn700_config * config)437 void set_encoding_and_masking_bits(const struct mod_cmn700_config *config)
438 {
439     unsigned int mesh_size_x;
440     unsigned int mesh_size_y;
441 
442     mesh_size_x = config->mesh_size_x;
443     mesh_size_y = config->mesh_size_y;
444 
445     /*
446      * Determine the number of bits used to represent each node coordinate based
447      * on the mesh size as per CMN700 specification.
448      */
449     if ((mesh_size_x > 8) || (mesh_size_y > 8)) {
450         encoding_bits = 4;
451     } else {
452         encoding_bits = ((mesh_size_x > 4) || (mesh_size_y > 4)) ? 3 : 2;
453     }
454 
455     /* Extract node coordinates from the node identifier */
456 
457     mask_bits = (1 << encoding_bits) - 1;
458 }
459