• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <arch.h>
8 #include <assert.h>
9 #include <bakery_lock.h>
10 #include <ccn.h>
11 #include <debug.h>
12 #include <errno.h>
13 #include <mmio.h>
14 #include "ccn_private.h"
15 
16 static const ccn_desc_t *ccn_plat_desc;
17 #if defined(IMAGE_BL31) || (defined(AARCH32) && defined(IMAGE_BL32))
18 DEFINE_BAKERY_LOCK(ccn_lock);
19 #endif
20 
21 /*******************************************************************************
22  * This function takes the base address of the CCN's programmer's view (PV), a
23  * region ID of one of the 256 regions (0-255) and a register offset within the
24  * region. It converts the first two parameters into a base address and uses it
25  * to read the register at the offset.
26  ******************************************************************************/
ccn_reg_read(uintptr_t periphbase,unsigned int region_id,unsigned int register_offset)27 static inline unsigned long long ccn_reg_read(uintptr_t periphbase,
28 			     unsigned int region_id,
29 			     unsigned int register_offset)
30 {
31 	uintptr_t region_base;
32 
33 	assert(periphbase);
34 	assert(region_id < REGION_ID_LIMIT);
35 
36 	region_base = periphbase + region_id_to_base(region_id);
37 	return mmio_read_64(region_base + register_offset);
38 }
39 
40 /*******************************************************************************
41  * This function takes the base address of the CCN's programmer's view (PV), a
42  * region ID of one of the 256 regions (0-255), a register offset within the
43  * region and a value. It converts the first two parameters into a base address
44  * and uses it to write the value in the register at the offset.
45  ******************************************************************************/
ccn_reg_write(uintptr_t periphbase,unsigned int region_id,unsigned int register_offset,unsigned long long value)46 static inline void ccn_reg_write(uintptr_t periphbase,
47 			  unsigned int region_id,
48 			  unsigned int register_offset,
49 			  unsigned long long value)
50 {
51 	uintptr_t region_base;
52 
53 	assert(periphbase);
54 	assert(region_id < REGION_ID_LIMIT);
55 
56 	region_base = periphbase + region_id_to_base(region_id);
57 	mmio_write_64(region_base + register_offset, value);
58 }
59 
60 #if ENABLE_ASSERTIONS
61 
62 typedef struct rn_info {
63 		unsigned char node_desc[MAX_RN_NODES];
64 	} rn_info_t;
65 
66 /*******************************************************************************
67  * This function takes the base address of the CCN's programmer's view (PV) and
68  * the node ID of a Request Node (RN-D or RN-I). It returns the maximum number
69  * of master interfaces resident on that node. This number is equal to the least
70  * significant two bits of the node type ID + 1.
71  ******************************************************************************/
ccn_get_rni_mcount(uintptr_t periphbase,unsigned int rn_id)72 static unsigned int ccn_get_rni_mcount(uintptr_t periphbase,
73 				       unsigned int rn_id)
74 {
75 	unsigned int rn_type_id;
76 
77 	/* Use the node id to find the type of RN-I/D node */
78 	rn_type_id = get_node_type(ccn_reg_read(periphbase,
79 						rn_id + RNI_REGION_ID_START,
80 						REGION_ID_OFFSET));
81 
82 	/* Return the number master interfaces based on node type */
83 	return rn_type_id_to_master_cnt(rn_type_id);
84 }
85 
86 /*******************************************************************************
87  * This function reads the CCN registers to find the following information about
88  * the ACE/ACELite/ACELite+DVM/CHI interfaces resident on the various types of
89  * Request Nodes (RN-Fs, RN-Is and RN-Ds) in the system:
90  *
91  * 1. The total number of such interfaces that this CCN IP supports. This is the
92  *    cumulative number of interfaces across all Request node types. It is
93  *    passed back as the return value of this function.
94  *
95  * 2. The maximum number of interfaces of a type resident on a Request node of
96  *    one of the three types. This information is populated in the 'info'
97  *    array provided by the caller as described next.
98  *
99  *    The array has 64 entries. Each entry corresponds to a Request node. The
100  *    Miscellaneous node's programmer's view has RN-F, RN-I and RN-D ID
101  *    registers. For each RN-I and RN-D ID indicated as being present in these
102  *    registers, its identification register (offset 0xFF00) is read. This
103  *    register specifies the maximum number of master interfaces the node
104  *    supports. For RN-Fs it is assumed that there can be only a single fully
105  *    coherent master resident on each node. The counts for each type of node
106  *    are use to populate the array entry at the index corresponding to the node
107  *    ID i.e. rn_info[node ID] = <number of master interfaces>
108  ******************************************************************************/
ccn_get_rn_master_info(uintptr_t periphbase,rn_info_t * info)109 static unsigned int ccn_get_rn_master_info(uintptr_t periphbase,
110 					   rn_info_t *info)
111 {
112 	unsigned int num_masters = 0;
113 	rn_types_t rn_type;
114 
115 	assert (info);
116 
117 	for (rn_type = RN_TYPE_RNF; rn_type < NUM_RN_TYPES; rn_type++) {
118 		unsigned int mn_reg_off, node_id;
119 		unsigned long long rn_bitmap;
120 
121 		/*
122 		 * RN-F, RN-I, RN-D node registers in the MN region occupy
123 		 * contiguous 16 byte apart offsets.
124 		 */
125 		mn_reg_off = MN_RNF_NODEID_OFFSET + (rn_type << 4);
126 		rn_bitmap = ccn_reg_read(periphbase, MN_REGION_ID, mn_reg_off);
127 
128 		FOR_EACH_PRESENT_NODE_ID(node_id, rn_bitmap) {
129 			unsigned int node_mcount;
130 
131 			/*
132 			 * A RN-F does not have a node type since it does not
133 			 * export a programmer's interface. It can only have a
134 			 * single fully coherent master residing on it. If the
135 			 * offset of the MN(Miscellaneous Node) register points
136 			 * to a RN-I/D node then the master count is set to the
137 			 * maximum number of master interfaces that can possibly
138 			 * reside on the node.
139 			 */
140 			node_mcount = (mn_reg_off == MN_RNF_NODEID_OFFSET ? 1 :
141 				       ccn_get_rni_mcount(periphbase, node_id));
142 
143 			/*
144 			 * Use this value to increment the maximum possible
145 			 * master interfaces in the system.
146 			 */
147 			num_masters += node_mcount;
148 
149 			/*
150 			 * Update the entry in 'info' for this node ID with
151 			 * the maximum number of masters than can sit on
152 			 * it. This information will be used to validate the
153 			 * node information passed by the platform later.
154 			 */
155 			info->node_desc[node_id] = node_mcount;
156 		}
157 	}
158 
159 	return num_masters;
160 }
161 
162 /*******************************************************************************
163  * This function validates parameters passed by the platform (in a debug build).
164  * It collects information about the maximum number of master interfaces that:
165  * a) the CCN IP can accommodate and
166  * b) can exist on each Request node.
167  * It compares this with the information provided by the platform to determine
168  * the validity of the latter.
169  ******************************************************************************/
ccn_validate_plat_params(const ccn_desc_t * plat_desc)170 static void ccn_validate_plat_params(const ccn_desc_t *plat_desc)
171 {
172 	unsigned int master_id, num_rn_masters;
173 	rn_info_t info = { {0} };
174 
175 	assert(plat_desc);
176 	assert(plat_desc->periphbase);
177 	assert(plat_desc->master_to_rn_id_map);
178 	assert(plat_desc->num_masters);
179 	assert(plat_desc->num_masters < CCN_MAX_RN_MASTERS);
180 
181 	/*
182 	 * Find the number and properties of fully coherent, IO coherent and IO
183 	 * coherent + DVM master interfaces
184 	 */
185 	num_rn_masters = ccn_get_rn_master_info(plat_desc->periphbase, &info);
186 	assert(plat_desc->num_masters < num_rn_masters);
187 
188 	/*
189 	 * Iterate through the Request nodes specified by the platform.
190 	 * Decrement the count of the masters in the 'info' array for each
191 	 * Request node encountered. If the count would drop below 0 then the
192 	 * platform's view of this aspect of CCN configuration is incorrect.
193 	 */
194 	for (master_id = 0; master_id < plat_desc->num_masters; master_id++) {
195 		unsigned int node_id;
196 
197 		node_id = plat_desc->master_to_rn_id_map[master_id];
198 		assert(node_id < MAX_RN_NODES);
199 		assert(info.node_desc[node_id]);
200 		info.node_desc[node_id]--;
201 	}
202 }
203 #endif /* ENABLE_ASSERTIONS */
204 
205 /*******************************************************************************
206  * This function validates parameters passed by the platform (in a debug build)
207  * and initialises its internal data structures. A lock is required to prevent
208  * simultaneous CCN operations at runtime (only BL31) to add and remove Request
209  * nodes from coherency.
210  ******************************************************************************/
ccn_init(const ccn_desc_t * plat_desc)211 void ccn_init(const ccn_desc_t *plat_desc)
212 {
213 #if ENABLE_ASSERTIONS
214 	ccn_validate_plat_params(plat_desc);
215 #endif
216 
217 	ccn_plat_desc = plat_desc;
218 }
219 
220 /*******************************************************************************
221  * This function converts a bit map of master interface IDs to a bit map of the
222  * Request node IDs that they reside on.
223  ******************************************************************************/
ccn_master_to_rn_id_map(unsigned long long master_map)224 static unsigned long long ccn_master_to_rn_id_map(unsigned long long master_map)
225 {
226 	unsigned long long rn_id_map = 0;
227 	unsigned int node_id, iface_id;
228 
229 	assert(master_map);
230 	assert(ccn_plat_desc);
231 
232 	FOR_EACH_PRESENT_MASTER_INTERFACE(iface_id, master_map) {
233 		assert(iface_id < ccn_plat_desc->num_masters);
234 
235 		/* Convert the master ID into the node ID */
236 		node_id = ccn_plat_desc->master_to_rn_id_map[iface_id];
237 
238 		/* Set the bit corresponding to this node ID */
239 		rn_id_map |= (1ULL << node_id);
240 	}
241 
242 	return rn_id_map;
243 }
244 
245 /*******************************************************************************
246  * This function executes the necessary operations to add or remove Request node
247  * IDs specified in the 'rn_id_map' bitmap from the snoop/DVM domains specified
248  * in the 'hn_id_map'. The 'region_id' specifies the ID of the first HN-F/MN
249  * on which the operation should be performed. 'op_reg_offset' specifies the
250  * type of operation (add/remove). 'stat_reg_offset' specifies the register
251  * which should be polled to determine if the operation has completed or not.
252  ******************************************************************************/
ccn_snoop_dvm_do_op(unsigned long long rn_id_map,unsigned long long hn_id_map,unsigned int region_id,unsigned int op_reg_offset,unsigned int stat_reg_offset)253 static void ccn_snoop_dvm_do_op(unsigned long long rn_id_map,
254 				unsigned long long hn_id_map,
255 				unsigned int region_id,
256 				unsigned int op_reg_offset,
257 				unsigned int stat_reg_offset)
258 {
259 	unsigned int start_region_id;
260 
261 	assert(ccn_plat_desc);
262 	assert(ccn_plat_desc->periphbase);
263 
264 #if defined(IMAGE_BL31) || (defined(AARCH32) && defined(IMAGE_BL32))
265 	bakery_lock_get(&ccn_lock);
266 #endif
267 	start_region_id = region_id;
268 	FOR_EACH_PRESENT_REGION_ID(start_region_id, hn_id_map) {
269 		ccn_reg_write(ccn_plat_desc->periphbase,
270 			      start_region_id,
271 			      op_reg_offset,
272 			      rn_id_map);
273 	}
274 
275 	start_region_id = region_id;
276 
277 	FOR_EACH_PRESENT_REGION_ID(start_region_id, hn_id_map) {
278 		WAIT_FOR_DOMAIN_CTRL_OP_COMPLETION(start_region_id,
279 						   stat_reg_offset,
280 						   op_reg_offset,
281 						   rn_id_map);
282 	}
283 
284 #if defined(IMAGE_BL31) || (defined(AARCH32) && defined(IMAGE_BL32))
285 	bakery_lock_release(&ccn_lock);
286 #endif
287 }
288 
289 /*******************************************************************************
290  * The following functions provide the boot and runtime API to the platform for
291  * adding and removing master interfaces from the snoop/DVM domains. A bitmap of
292  * master interfaces IDs is passed as a parameter. It is converted into a bitmap
293  * of Request node IDs using the mapping provided by the platform while
294  * initialising the driver.
295  * For example, consider a dual cluster system where the clusters have values 0
296  * & 1 in the affinity level 1 field of their respective MPIDRs. While
297  * initialising this driver, the platform provides the mapping between each
298  * cluster and the corresponding Request node. To add or remove a cluster from
299  * the snoop and dvm domain, the bit position corresponding to the cluster ID
300  * should be set in the 'master_iface_map' i.e. to remove both clusters the
301  * bitmap would equal 0x11.
302  ******************************************************************************/
ccn_enter_snoop_dvm_domain(unsigned long long master_iface_map)303 void ccn_enter_snoop_dvm_domain(unsigned long long master_iface_map)
304 {
305 	unsigned long long rn_id_map;
306 
307 	rn_id_map = ccn_master_to_rn_id_map(master_iface_map);
308 	ccn_snoop_dvm_do_op(rn_id_map,
309 			    CCN_GET_HN_NODEID_MAP(ccn_plat_desc->periphbase,
310 						  MN_HNF_NODEID_OFFSET),
311 			    HNF_REGION_ID_START,
312 			    HNF_SDC_SET_OFFSET,
313 			    HNF_SDC_STAT_OFFSET);
314 
315 	ccn_snoop_dvm_do_op(rn_id_map,
316 			    CCN_GET_MN_NODEID_MAP(ccn_plat_desc->periphbase),
317 			    MN_REGION_ID,
318 			    MN_DDC_SET_OFFSET,
319 			    MN_DDC_STAT_OFFSET);
320 }
321 
ccn_exit_snoop_dvm_domain(unsigned long long master_iface_map)322 void ccn_exit_snoop_dvm_domain(unsigned long long master_iface_map)
323 {
324 	unsigned long long rn_id_map;
325 
326 	rn_id_map = ccn_master_to_rn_id_map(master_iface_map);
327 	ccn_snoop_dvm_do_op(rn_id_map,
328 			    CCN_GET_HN_NODEID_MAP(ccn_plat_desc->periphbase,
329 						  MN_HNF_NODEID_OFFSET),
330 			    HNF_REGION_ID_START,
331 			    HNF_SDC_CLR_OFFSET,
332 			    HNF_SDC_STAT_OFFSET);
333 
334 	ccn_snoop_dvm_do_op(rn_id_map,
335 			    CCN_GET_MN_NODEID_MAP(ccn_plat_desc->periphbase),
336 			    MN_REGION_ID,
337 			    MN_DDC_CLR_OFFSET,
338 			    MN_DDC_STAT_OFFSET);
339 }
340 
ccn_enter_dvm_domain(unsigned long long master_iface_map)341 void ccn_enter_dvm_domain(unsigned long long master_iface_map)
342 {
343 	unsigned long long rn_id_map;
344 
345 	rn_id_map = ccn_master_to_rn_id_map(master_iface_map);
346 	ccn_snoop_dvm_do_op(rn_id_map,
347 			    CCN_GET_MN_NODEID_MAP(ccn_plat_desc->periphbase),
348 			    MN_REGION_ID,
349 			    MN_DDC_SET_OFFSET,
350 			    MN_DDC_STAT_OFFSET);
351 }
352 
ccn_exit_dvm_domain(unsigned long long master_iface_map)353 void ccn_exit_dvm_domain(unsigned long long master_iface_map)
354 {
355 	unsigned long long rn_id_map;
356 
357 	rn_id_map = ccn_master_to_rn_id_map(master_iface_map);
358 	ccn_snoop_dvm_do_op(rn_id_map,
359 			    CCN_GET_MN_NODEID_MAP(ccn_plat_desc->periphbase),
360 			    MN_REGION_ID,
361 			    MN_DDC_CLR_OFFSET,
362 			    MN_DDC_STAT_OFFSET);
363 }
364 
365 /*******************************************************************************
366  * This function returns the run mode of all the L3 cache partitions in the
367  * system. The state is expected to be one of NO_L3, SF_ONLY, L3_HAM or
368  * L3_FAM. Instead of comparing the states reported by all HN-Fs, the state of
369  * the first present HN-F node is reported. Since the driver does not export an
370  * interface to program them seperately, there is no reason to perform this
371  * check. An HN-F could report that the L3 cache is transitioning from one mode
372  * to another e.g. HNF_PM_NOL3_2_SFONLY. In this case, the function waits for
373  * the transition to complete and reports the final state.
374  ******************************************************************************/
ccn_get_l3_run_mode(void)375 unsigned int ccn_get_l3_run_mode(void)
376 {
377 	unsigned long long hnf_pstate_stat;
378 
379 	assert(ccn_plat_desc);
380 	assert(ccn_plat_desc->periphbase);
381 
382 	/*
383 	 * Wait for a L3 cache paritition to enter any run mode. The pstate
384 	 * parameter is read from an HN-F P-state status register. A non-zero
385 	 * value in bits[1:0] means that the cache is transitioning to a run
386 	 * mode.
387 	 */
388 	do {
389 		hnf_pstate_stat = ccn_reg_read(ccn_plat_desc->periphbase,
390 					       HNF_REGION_ID_START,
391 					       HNF_PSTATE_STAT_OFFSET);
392 	} while (hnf_pstate_stat & 0x3);
393 
394 	return PSTATE_TO_RUN_MODE(hnf_pstate_stat);
395 }
396 
397 /*******************************************************************************
398  * This function sets the run mode of all the L3 cache partitions in the
399  * system to one of NO_L3, SF_ONLY, L3_HAM or L3_FAM depending upon the state
400  * specified by the 'mode' argument.
401  ******************************************************************************/
ccn_set_l3_run_mode(unsigned int mode)402 void ccn_set_l3_run_mode(unsigned int mode)
403 {
404 	unsigned long long mn_hnf_id_map, hnf_pstate_stat;
405 	unsigned int region_id;
406 
407 	assert(ccn_plat_desc);
408 	assert(ccn_plat_desc->periphbase);
409 	assert(mode <= CCN_L3_RUN_MODE_FAM);
410 
411 	mn_hnf_id_map = ccn_reg_read(ccn_plat_desc->periphbase,
412 				     MN_REGION_ID,
413 				     MN_HNF_NODEID_OFFSET);
414 	region_id = HNF_REGION_ID_START;
415 
416 	/* Program the desired run mode */
417 	FOR_EACH_PRESENT_REGION_ID(region_id, mn_hnf_id_map) {
418 		ccn_reg_write(ccn_plat_desc->periphbase,
419 			      region_id,
420 			      HNF_PSTATE_REQ_OFFSET,
421 			      mode);
422 	}
423 
424 	/* Wait for the caches to transition to the run mode */
425 	region_id = HNF_REGION_ID_START;
426 	FOR_EACH_PRESENT_REGION_ID(region_id, mn_hnf_id_map) {
427 		/*
428 		 * Wait for a L3 cache paritition to enter a target run
429 		 * mode. The pstate parameter is read from an HN-F P-state
430 		 * status register.
431 		 */
432 		do {
433 			hnf_pstate_stat = ccn_reg_read(ccn_plat_desc->periphbase,
434 					       region_id,
435 					       HNF_PSTATE_STAT_OFFSET);
436 		} while (((hnf_pstate_stat & HNF_PSTATE_MASK) >> 2) != mode);
437 	}
438 }
439 
440 /*******************************************************************************
441  * This function configures system address map and provides option to enable the
442  * 3SN striping mode of Slave node operation. The Slave node IDs and the Top
443  * Address bit1 and bit0 are provided as parameters to this function. This
444  * configuration is needed only if network contains a single SN-F or 3 SN-F and
445  * must be completed before the first request by the system to normal memory.
446  ******************************************************************************/
ccn_program_sys_addrmap(unsigned int sn0_id,unsigned int sn1_id,unsigned int sn2_id,unsigned int top_addr_bit0,unsigned int top_addr_bit1,unsigned char three_sn_en)447 void ccn_program_sys_addrmap(unsigned int sn0_id,
448 		 unsigned int sn1_id,
449 		 unsigned int sn2_id,
450 		 unsigned int top_addr_bit0,
451 		 unsigned int top_addr_bit1,
452 		 unsigned char three_sn_en)
453 {
454 	unsigned long long mn_hnf_id_map, hnf_sam_ctrl_value;
455 	unsigned int region_id;
456 
457 	assert(ccn_plat_desc);
458 	assert(ccn_plat_desc->periphbase);
459 
460 	mn_hnf_id_map = ccn_reg_read(ccn_plat_desc->periphbase,
461 				     MN_REGION_ID,
462 				     MN_HNF_NODEID_OFFSET);
463 	region_id = HNF_REGION_ID_START;
464 	hnf_sam_ctrl_value = MAKE_HNF_SAM_CTRL_VALUE(sn0_id,
465 						     sn1_id,
466 						     sn2_id,
467 						     top_addr_bit0,
468 						     top_addr_bit1,
469 						     three_sn_en);
470 
471 	FOR_EACH_PRESENT_REGION_ID(region_id, mn_hnf_id_map) {
472 
473 		/* Program the SAM control register */
474 		ccn_reg_write(ccn_plat_desc->periphbase,
475 			      region_id,
476 			      HNF_SAM_CTRL_OFFSET,
477 			      hnf_sam_ctrl_value);
478 	}
479 
480 }
481 
482 /*******************************************************************************
483  * This function returns the part0 id from the peripheralID 0 register
484  * in CCN. This id can be used to distinguish the CCN variant present in the
485  * system.
486  ******************************************************************************/
ccn_get_part0_id(uintptr_t periphbase)487 int ccn_get_part0_id(uintptr_t periphbase)
488 {
489 	assert(periphbase);
490 	return (int)(mmio_read_64(periphbase
491 			+ MN_PERIPH_ID_0_1_OFFSET) & 0xFF);
492 }
493