blob: 910cd7cd27b2845bb2c6145bace59b8bbc33af93 [file] [log] [blame]
Achin Guptaa80720f2015-07-20 22:28:23 +01001/*
Daniel Boulby844b4872018-09-18 13:36:39 +01002 * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved.
Achin Guptaa80720f2015-07-20 22:28:23 +01003 *
dp-armfa3cf0b2017-05-03 09:38:09 +01004 * SPDX-License-Identifier: BSD-3-Clause
Achin Guptaa80720f2015-07-20 22:28:23 +01005 */
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
16static const ccn_desc_t *ccn_plat_desc;
Soby Mathew074f6932017-02-28 22:58:29 +000017#if defined(IMAGE_BL31) || (defined(AARCH32) && defined(IMAGE_BL32))
Achin Guptaa80720f2015-07-20 22:28:23 +010018DEFINE_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 ******************************************************************************/
27static 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 ******************************************************************************/
46static 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
Antonio Nino Diaz3759e3f2017-03-22 15:48:51 +000060#if ENABLE_ASSERTIONS
Achin Guptaa80720f2015-07-20 22:28:23 +010061
62typedef 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 ******************************************************************************/
72static 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 ******************************************************************************/
109static 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 ******************************************************************************/
Daniel Boulby844b4872018-09-18 13:36:39 +0100170static void __init ccn_validate_plat_params(const ccn_desc_t *plat_desc)
Achin Guptaa80720f2015-07-20 22:28:23 +0100171{
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}
Antonio Nino Diaz3759e3f2017-03-22 15:48:51 +0000203#endif /* ENABLE_ASSERTIONS */
Achin Guptaa80720f2015-07-20 22:28:23 +0100204
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 ******************************************************************************/
Daniel Boulby844b4872018-09-18 13:36:39 +0100211void __init ccn_init(const ccn_desc_t *plat_desc)
Achin Guptaa80720f2015-07-20 22:28:23 +0100212{
Antonio Nino Diaz3759e3f2017-03-22 15:48:51 +0000213#if ENABLE_ASSERTIONS
Achin Guptaa80720f2015-07-20 22:28:23 +0100214 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 ******************************************************************************/
224static 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) {
Soby Mathew095b9bc2016-03-23 17:14:57 +0000233 assert(iface_id < ccn_plat_desc->num_masters);
Achin Guptaa80720f2015-07-20 22:28:23 +0100234
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 */
Antonio Nino Diazf94e40d2017-09-14 15:57:44 +0100239 rn_id_map |= (1ULL << node_id);
Achin Guptaa80720f2015-07-20 22:28:23 +0100240 }
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
Vikram Kanigiri4aceeb02016-01-04 16:23:22 +0000248 * in the 'hn_id_map'. The 'region_id' specifies the ID of the first HN-F/MN
Achin Guptaa80720f2015-07-20 22:28:23 +0100249 * 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 ******************************************************************************/
253static 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
Soby Mathew074f6932017-02-28 22:58:29 +0000264#if defined(IMAGE_BL31) || (defined(AARCH32) && defined(IMAGE_BL32))
Achin Guptaa80720f2015-07-20 22:28:23 +0100265 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
Soby Mathew074f6932017-02-28 22:58:29 +0000284#if defined(IMAGE_BL31) || (defined(AARCH32) && defined(IMAGE_BL32))
Achin Guptaa80720f2015-07-20 22:28:23 +0100285 bakery_lock_release(&ccn_lock);
286#endif
287}
288
289/*******************************************************************************
Achin Guptaa80720f2015-07-20 22:28:23 +0100290 * 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 ******************************************************************************/
303void 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);
Vikram Kanigiri4aceeb02016-01-04 16:23:22 +0000308 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);
Achin Guptaa80720f2015-07-20 22:28:23 +0100314
Vikram Kanigiri4aceeb02016-01-04 16:23:22 +0000315 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);
Achin Guptaa80720f2015-07-20 22:28:23 +0100320}
321
322void 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);
Vikram Kanigiri4aceeb02016-01-04 16:23:22 +0000327 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);
Achin Guptaa80720f2015-07-20 22:28:23 +0100333
Vikram Kanigiri4aceeb02016-01-04 16:23:22 +0000334 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);
Achin Guptaa80720f2015-07-20 22:28:23 +0100339}
340
341void 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);
Vikram Kanigiri4aceeb02016-01-04 16:23:22 +0000346 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);
Achin Guptaa80720f2015-07-20 22:28:23 +0100351}
352
353void 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);
Vikram Kanigiri4aceeb02016-01-04 16:23:22 +0000358 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);
Achin Guptaa80720f2015-07-20 22:28:23 +0100363}
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 ******************************************************************************/
375unsigned 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 ******************************************************************************/
402void 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 ******************************************************************************/
447void 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}
Soby Mathew095b9bc2016-03-23 17:14:57 +0000481
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 ******************************************************************************/
487int 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}