123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621 |
- /*
- * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved.
- *
- * SPDX-License-Identifier: BSD-3-Clause
- */
- #include <assert.h>
- #include <stdbool.h>
- #include <arch.h>
- #include <common/debug.h>
- #include <drivers/arm/ccn.h>
- #include <lib/bakery_lock.h>
- #include <lib/mmio.h>
- #include "ccn_private.h"
- static const ccn_desc_t *ccn_plat_desc;
- #if defined(IMAGE_BL31) || (!defined(__aarch64__) && defined(IMAGE_BL32))
- DEFINE_BAKERY_LOCK(ccn_lock);
- #endif
- /*******************************************************************************
- * This function takes the base address of the CCN's programmer's view (PV), a
- * region ID of one of the 256 regions (0-255) and a register offset within the
- * region. It converts the first two parameters into a base address and uses it
- * to read the register at the offset.
- ******************************************************************************/
- static inline unsigned long long ccn_reg_read(uintptr_t periphbase,
- unsigned int region_id,
- unsigned int register_offset)
- {
- uintptr_t region_base;
- assert(periphbase);
- assert(region_id < REGION_ID_LIMIT);
- region_base = periphbase + region_id_to_base(region_id);
- return mmio_read_64(region_base + register_offset);
- }
- /*******************************************************************************
- * This function takes the base address of the CCN's programmer's view (PV), a
- * region ID of one of the 256 regions (0-255), a register offset within the
- * region and a value. It converts the first two parameters into a base address
- * and uses it to write the value in the register at the offset.
- ******************************************************************************/
- static inline void ccn_reg_write(uintptr_t periphbase,
- unsigned int region_id,
- unsigned int register_offset,
- unsigned long long value)
- {
- uintptr_t region_base;
- assert(periphbase);
- assert(region_id < REGION_ID_LIMIT);
- region_base = periphbase + region_id_to_base(region_id);
- mmio_write_64(region_base + register_offset, value);
- }
- #if ENABLE_ASSERTIONS
- typedef struct rn_info {
- unsigned char node_desc[MAX_RN_NODES];
- } rn_info_t;
- /*******************************************************************************
- * This function takes the base address of the CCN's programmer's view (PV) and
- * the node ID of a Request Node (RN-D or RN-I). It returns the maximum number
- * of master interfaces resident on that node. This number is equal to the least
- * significant two bits of the node type ID + 1.
- ******************************************************************************/
- static unsigned int ccn_get_rni_mcount(uintptr_t periphbase,
- unsigned int rn_id)
- {
- unsigned int rn_type_id;
- /* Use the node id to find the type of RN-I/D node */
- rn_type_id = get_node_type(ccn_reg_read(periphbase,
- rn_id + RNI_REGION_ID_START,
- REGION_ID_OFFSET));
- /* Return the number master interfaces based on node type */
- return rn_type_id_to_master_cnt(rn_type_id);
- }
- /*******************************************************************************
- * This function reads the CCN registers to find the following information about
- * the ACE/ACELite/ACELite+DVM/CHI interfaces resident on the various types of
- * Request Nodes (RN-Fs, RN-Is and RN-Ds) in the system:
- *
- * 1. The total number of such interfaces that this CCN IP supports. This is the
- * cumulative number of interfaces across all Request node types. It is
- * passed back as the return value of this function.
- *
- * 2. The maximum number of interfaces of a type resident on a Request node of
- * one of the three types. This information is populated in the 'info'
- * array provided by the caller as described next.
- *
- * The array has 64 entries. Each entry corresponds to a Request node. The
- * Miscellaneous node's programmer's view has RN-F, RN-I and RN-D ID
- * registers. For each RN-I and RN-D ID indicated as being present in these
- * registers, its identification register (offset 0xFF00) is read. This
- * register specifies the maximum number of master interfaces the node
- * supports. For RN-Fs it is assumed that there can be only a single fully
- * coherent master resident on each node. The counts for each type of node
- * are use to populate the array entry at the index corresponding to the node
- * ID i.e. rn_info[node ID] = <number of master interfaces>
- ******************************************************************************/
- static unsigned int ccn_get_rn_master_info(uintptr_t periphbase,
- rn_info_t *info)
- {
- unsigned int num_masters = 0;
- rn_types_t rn_type;
- assert (info);
- for (rn_type = RN_TYPE_RNF; rn_type < NUM_RN_TYPES; rn_type++) {
- unsigned int mn_reg_off, node_id;
- unsigned long long rn_bitmap;
- /*
- * RN-F, RN-I, RN-D node registers in the MN region occupy
- * contiguous 16 byte apart offsets.
- */
- mn_reg_off = MN_RNF_NODEID_OFFSET + (rn_type << 4);
- rn_bitmap = ccn_reg_read(periphbase, MN_REGION_ID, mn_reg_off);
- FOR_EACH_PRESENT_NODE_ID(node_id, rn_bitmap) {
- unsigned int node_mcount;
- /*
- * A RN-F does not have a node type since it does not
- * export a programmer's interface. It can only have a
- * single fully coherent master residing on it. If the
- * offset of the MN(Miscellaneous Node) register points
- * to a RN-I/D node then the master count is set to the
- * maximum number of master interfaces that can possibly
- * reside on the node.
- */
- node_mcount = (mn_reg_off == MN_RNF_NODEID_OFFSET ? 1 :
- ccn_get_rni_mcount(periphbase, node_id));
- /*
- * Use this value to increment the maximum possible
- * master interfaces in the system.
- */
- num_masters += node_mcount;
- /*
- * Update the entry in 'info' for this node ID with
- * the maximum number of masters than can sit on
- * it. This information will be used to validate the
- * node information passed by the platform later.
- */
- info->node_desc[node_id] = node_mcount;
- }
- }
- return num_masters;
- }
- /*******************************************************************************
- * This function validates parameters passed by the platform (in a debug build).
- * It collects information about the maximum number of master interfaces that:
- * a) the CCN IP can accommodate and
- * b) can exist on each Request node.
- * It compares this with the information provided by the platform to determine
- * the validity of the latter.
- ******************************************************************************/
- static void __init ccn_validate_plat_params(const ccn_desc_t *plat_desc)
- {
- unsigned int master_id, num_rn_masters;
- rn_info_t info = { {0} };
- assert(plat_desc);
- assert(plat_desc->periphbase);
- assert(plat_desc->master_to_rn_id_map);
- assert(plat_desc->num_masters);
- assert(plat_desc->num_masters < CCN_MAX_RN_MASTERS);
- /*
- * Find the number and properties of fully coherent, IO coherent and IO
- * coherent + DVM master interfaces
- */
- num_rn_masters = ccn_get_rn_master_info(plat_desc->periphbase, &info);
- assert(plat_desc->num_masters < num_rn_masters);
- /*
- * Iterate through the Request nodes specified by the platform.
- * Decrement the count of the masters in the 'info' array for each
- * Request node encountered. If the count would drop below 0 then the
- * platform's view of this aspect of CCN configuration is incorrect.
- */
- for (master_id = 0; master_id < plat_desc->num_masters; master_id++) {
- unsigned int node_id;
- node_id = plat_desc->master_to_rn_id_map[master_id];
- assert(node_id < MAX_RN_NODES);
- assert(info.node_desc[node_id]);
- info.node_desc[node_id]--;
- }
- }
- #endif /* ENABLE_ASSERTIONS */
- /*******************************************************************************
- * This function validates parameters passed by the platform (in a debug build)
- * and initialises its internal data structures. A lock is required to prevent
- * simultaneous CCN operations at runtime (only BL31) to add and remove Request
- * nodes from coherency.
- ******************************************************************************/
- void __init ccn_init(const ccn_desc_t *plat_desc)
- {
- #if ENABLE_ASSERTIONS
- ccn_validate_plat_params(plat_desc);
- #endif
- ccn_plat_desc = plat_desc;
- }
- /*******************************************************************************
- * This function converts a bit map of master interface IDs to a bit map of the
- * Request node IDs that they reside on.
- ******************************************************************************/
- static unsigned long long ccn_master_to_rn_id_map(unsigned long long master_map)
- {
- unsigned long long rn_id_map = 0;
- unsigned int node_id, iface_id;
- assert(master_map);
- assert(ccn_plat_desc);
- FOR_EACH_PRESENT_MASTER_INTERFACE(iface_id, master_map) {
- assert(iface_id < ccn_plat_desc->num_masters);
- /* Convert the master ID into the node ID */
- node_id = ccn_plat_desc->master_to_rn_id_map[iface_id];
- /* Set the bit corresponding to this node ID */
- rn_id_map |= (1ULL << node_id);
- }
- return rn_id_map;
- }
- /*******************************************************************************
- * This function executes the necessary operations to add or remove Request node
- * IDs specified in the 'rn_id_map' bitmap from the snoop/DVM domains specified
- * in the 'hn_id_map'. The 'region_id' specifies the ID of the first HN-F/MN
- * on which the operation should be performed. 'op_reg_offset' specifies the
- * type of operation (add/remove). 'stat_reg_offset' specifies the register
- * which should be polled to determine if the operation has completed or not.
- ******************************************************************************/
- static void 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)
- {
- unsigned int start_region_id;
- assert(ccn_plat_desc);
- assert(ccn_plat_desc->periphbase);
- #if defined(IMAGE_BL31) || (!defined(__aarch64__) && defined(IMAGE_BL32))
- bakery_lock_get(&ccn_lock);
- #endif
- start_region_id = region_id;
- FOR_EACH_PRESENT_REGION_ID(start_region_id, hn_id_map) {
- ccn_reg_write(ccn_plat_desc->periphbase,
- start_region_id,
- op_reg_offset,
- rn_id_map);
- }
- start_region_id = region_id;
- FOR_EACH_PRESENT_REGION_ID(start_region_id, hn_id_map) {
- WAIT_FOR_DOMAIN_CTRL_OP_COMPLETION(start_region_id,
- stat_reg_offset,
- op_reg_offset,
- rn_id_map);
- }
- #if defined(IMAGE_BL31) || (!defined(__aarch64__) && defined(IMAGE_BL32))
- bakery_lock_release(&ccn_lock);
- #endif
- }
- /*******************************************************************************
- * The following functions provide the boot and runtime API to the platform for
- * adding and removing master interfaces from the snoop/DVM domains. A bitmap of
- * master interfaces IDs is passed as a parameter. It is converted into a bitmap
- * of Request node IDs using the mapping provided by the platform while
- * initialising the driver.
- * For example, consider a dual cluster system where the clusters have values 0
- * & 1 in the affinity level 1 field of their respective MPIDRs. While
- * initialising this driver, the platform provides the mapping between each
- * cluster and the corresponding Request node. To add or remove a cluster from
- * the snoop and dvm domain, the bit position corresponding to the cluster ID
- * should be set in the 'master_iface_map' i.e. to remove both clusters the
- * bitmap would equal 0x11.
- ******************************************************************************/
- void ccn_enter_snoop_dvm_domain(unsigned long long master_iface_map)
- {
- unsigned long long rn_id_map;
- rn_id_map = ccn_master_to_rn_id_map(master_iface_map);
- ccn_snoop_dvm_do_op(rn_id_map,
- CCN_GET_HN_NODEID_MAP(ccn_plat_desc->periphbase,
- MN_HNF_NODEID_OFFSET),
- HNF_REGION_ID_START,
- HNF_SDC_SET_OFFSET,
- HNF_SDC_STAT_OFFSET);
- ccn_snoop_dvm_do_op(rn_id_map,
- CCN_GET_MN_NODEID_MAP(ccn_plat_desc->periphbase),
- MN_REGION_ID,
- MN_DDC_SET_OFFSET,
- MN_DDC_STAT_OFFSET);
- }
- void ccn_exit_snoop_dvm_domain(unsigned long long master_iface_map)
- {
- unsigned long long rn_id_map;
- rn_id_map = ccn_master_to_rn_id_map(master_iface_map);
- ccn_snoop_dvm_do_op(rn_id_map,
- CCN_GET_HN_NODEID_MAP(ccn_plat_desc->periphbase,
- MN_HNF_NODEID_OFFSET),
- HNF_REGION_ID_START,
- HNF_SDC_CLR_OFFSET,
- HNF_SDC_STAT_OFFSET);
- ccn_snoop_dvm_do_op(rn_id_map,
- CCN_GET_MN_NODEID_MAP(ccn_plat_desc->periphbase),
- MN_REGION_ID,
- MN_DDC_CLR_OFFSET,
- MN_DDC_STAT_OFFSET);
- }
- void ccn_enter_dvm_domain(unsigned long long master_iface_map)
- {
- unsigned long long rn_id_map;
- rn_id_map = ccn_master_to_rn_id_map(master_iface_map);
- ccn_snoop_dvm_do_op(rn_id_map,
- CCN_GET_MN_NODEID_MAP(ccn_plat_desc->periphbase),
- MN_REGION_ID,
- MN_DDC_SET_OFFSET,
- MN_DDC_STAT_OFFSET);
- }
- void ccn_exit_dvm_domain(unsigned long long master_iface_map)
- {
- unsigned long long rn_id_map;
- rn_id_map = ccn_master_to_rn_id_map(master_iface_map);
- ccn_snoop_dvm_do_op(rn_id_map,
- CCN_GET_MN_NODEID_MAP(ccn_plat_desc->periphbase),
- MN_REGION_ID,
- MN_DDC_CLR_OFFSET,
- MN_DDC_STAT_OFFSET);
- }
- /*******************************************************************************
- * This function returns the run mode of all the L3 cache partitions in the
- * system. The state is expected to be one of NO_L3, SF_ONLY, L3_HAM or
- * L3_FAM. Instead of comparing the states reported by all HN-Fs, the state of
- * the first present HN-F node is reported. Since the driver does not export an
- * interface to program them separately, there is no reason to perform this
- * check. An HN-F could report that the L3 cache is transitioning from one mode
- * to another e.g. HNF_PM_NOL3_2_SFONLY. In this case, the function waits for
- * the transition to complete and reports the final state.
- ******************************************************************************/
- unsigned int ccn_get_l3_run_mode(void)
- {
- unsigned long long hnf_pstate_stat;
- assert(ccn_plat_desc);
- assert(ccn_plat_desc->periphbase);
- /*
- * Wait for a L3 cache partition to enter any run mode. The pstate
- * parameter is read from an HN-F P-state status register. A non-zero
- * value in bits[1:0] means that the cache is transitioning to a run
- * mode.
- */
- do {
- hnf_pstate_stat = ccn_reg_read(ccn_plat_desc->periphbase,
- HNF_REGION_ID_START,
- HNF_PSTATE_STAT_OFFSET);
- } while (hnf_pstate_stat & 0x3);
- return PSTATE_TO_RUN_MODE(hnf_pstate_stat);
- }
- /*******************************************************************************
- * This function sets the run mode of all the L3 cache partitions in the
- * system to one of NO_L3, SF_ONLY, L3_HAM or L3_FAM depending upon the state
- * specified by the 'mode' argument.
- ******************************************************************************/
- void ccn_set_l3_run_mode(unsigned int mode)
- {
- unsigned long long mn_hnf_id_map, hnf_pstate_stat;
- unsigned int region_id;
- assert(ccn_plat_desc);
- assert(ccn_plat_desc->periphbase);
- assert(mode <= CCN_L3_RUN_MODE_FAM);
- mn_hnf_id_map = ccn_reg_read(ccn_plat_desc->periphbase,
- MN_REGION_ID,
- MN_HNF_NODEID_OFFSET);
- region_id = HNF_REGION_ID_START;
- /* Program the desired run mode */
- FOR_EACH_PRESENT_REGION_ID(region_id, mn_hnf_id_map) {
- ccn_reg_write(ccn_plat_desc->periphbase,
- region_id,
- HNF_PSTATE_REQ_OFFSET,
- mode);
- }
- /* Wait for the caches to transition to the run mode */
- region_id = HNF_REGION_ID_START;
- FOR_EACH_PRESENT_REGION_ID(region_id, mn_hnf_id_map) {
- /*
- * Wait for a L3 cache partition to enter a target run
- * mode. The pstate parameter is read from an HN-F P-state
- * status register.
- */
- do {
- hnf_pstate_stat = ccn_reg_read(ccn_plat_desc->periphbase,
- region_id,
- HNF_PSTATE_STAT_OFFSET);
- } while (((hnf_pstate_stat & HNF_PSTATE_MASK) >> 2) != mode);
- }
- }
- /*******************************************************************************
- * This function configures system address map and provides option to enable the
- * 3SN striping mode of Slave node operation. The Slave node IDs and the Top
- * Address bit1 and bit0 are provided as parameters to this function. This
- * configuration is needed only if network contains a single SN-F or 3 SN-F and
- * must be completed before the first request by the system to normal memory.
- ******************************************************************************/
- void 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)
- {
- unsigned long long mn_hnf_id_map, hnf_sam_ctrl_value;
- unsigned int region_id;
- assert(ccn_plat_desc);
- assert(ccn_plat_desc->periphbase);
- mn_hnf_id_map = ccn_reg_read(ccn_plat_desc->periphbase,
- MN_REGION_ID,
- MN_HNF_NODEID_OFFSET);
- region_id = HNF_REGION_ID_START;
- hnf_sam_ctrl_value = MAKE_HNF_SAM_CTRL_VALUE(sn0_id,
- sn1_id,
- sn2_id,
- top_addr_bit0,
- top_addr_bit1,
- three_sn_en);
- FOR_EACH_PRESENT_REGION_ID(region_id, mn_hnf_id_map) {
- /* Program the SAM control register */
- ccn_reg_write(ccn_plat_desc->periphbase,
- region_id,
- HNF_SAM_CTRL_OFFSET,
- hnf_sam_ctrl_value);
- }
- }
- /*******************************************************************************
- * This function returns the part0 id from the peripheralID 0 register
- * in CCN. This id can be used to distinguish the CCN variant present in the
- * system.
- ******************************************************************************/
- int ccn_get_part0_id(uintptr_t periphbase)
- {
- assert(periphbase);
- return (int)(mmio_read_64(periphbase
- + MN_PERIPH_ID_0_1_OFFSET) & 0xFF);
- }
- /*******************************************************************************
- * This function returns the region id corresponding to a node_id of node_type.
- ******************************************************************************/
- static unsigned int get_region_id_for_node(node_types_t node_type,
- unsigned int node_id)
- {
- unsigned int mn_reg_off, region_id;
- unsigned long long node_bitmap;
- unsigned int loc_node_id, node_pos_in_map = 0;
- assert(node_type < NUM_NODE_TYPES);
- assert(node_id < MAX_RN_NODES);
- switch (node_type) {
- case NODE_TYPE_RNI:
- region_id = RNI_REGION_ID_START;
- break;
- case NODE_TYPE_HNF:
- region_id = HNF_REGION_ID_START;
- break;
- case NODE_TYPE_HNI:
- region_id = HNI_REGION_ID_START;
- break;
- case NODE_TYPE_SN:
- region_id = SBSX_REGION_ID_START;
- break;
- default:
- ERROR("Un-supported Node Type = %d.\n", node_type);
- assert(false);
- return REGION_ID_LIMIT;
- }
- /*
- * RN-I, HN-F, HN-I, SN node registers in the MN region
- * occupy contiguous 16 byte apart offsets.
- *
- * RN-F and RN-D node are not supported as
- * none of them exposes any memory map to
- * configure any of their offset registers.
- */
- mn_reg_off = MN_RNF_NODEID_OFFSET + (node_type << 4);
- node_bitmap = ccn_reg_read(ccn_plat_desc->periphbase,
- MN_REGION_ID, mn_reg_off);
- assert((node_bitmap & (1ULL << (node_id))) != 0U);
- FOR_EACH_PRESENT_NODE_ID(loc_node_id, node_bitmap) {
- INFO("Index = %u with loc_nod=%u and input nod=%u\n",
- node_pos_in_map, loc_node_id, node_id);
- if (loc_node_id == node_id)
- break;
- node_pos_in_map++;
- }
- if (node_pos_in_map == CCN_MAX_RN_MASTERS) {
- ERROR("Node Id = %d, is not found.\n", node_id);
- assert(false);
- return REGION_ID_LIMIT;
- }
- /*
- * According to section 3.1.1 in CCN specification, region offset for
- * the RN-I components is calculated as (128 + NodeID of RN-I).
- */
- if (node_type == NODE_TYPE_RNI)
- region_id += node_id;
- else
- region_id += node_pos_in_map;
- return region_id;
- }
- /*******************************************************************************
- * This function sets the value 'val' to the register at register_offset from
- * the base address pointed to by the region_id.
- * where, region id is mapped to a node_id of node_type.
- ******************************************************************************/
- void ccn_write_node_reg(node_types_t node_type, unsigned int node_id,
- unsigned int reg_offset, unsigned long long val)
- {
- unsigned int region_id = get_region_id_for_node(node_type, node_id);
- if (reg_offset > REGION_ID_OFFSET) {
- ERROR("Invalid Register offset 0x%x is provided.\n",
- reg_offset);
- assert(false);
- return;
- }
- /* Setting the value of Auxiliary Control Register of the Node */
- ccn_reg_write(ccn_plat_desc->periphbase, region_id, reg_offset, val);
- VERBOSE("Value is successfully written at address 0x%lx.\n",
- (ccn_plat_desc->periphbase
- + region_id_to_base(region_id))
- + reg_offset);
- }
- /*******************************************************************************
- * This function read the value 'val' stored in the register at register_offset
- * from the base address pointed to by the region_id.
- * where, region id is mapped to a node_id of node_type.
- ******************************************************************************/
- unsigned long long ccn_read_node_reg(node_types_t node_type,
- unsigned int node_id,
- unsigned int reg_offset)
- {
- unsigned long long val;
- unsigned int region_id = get_region_id_for_node(node_type, node_id);
- if (reg_offset > REGION_ID_OFFSET) {
- ERROR("Invalid Register offset 0x%x is provided.\n",
- reg_offset);
- assert(false);
- return ULL(0);
- }
- /* Setting the value of Auxiliary Control Register of the Node */
- val = ccn_reg_read(ccn_plat_desc->periphbase, region_id, reg_offset);
- VERBOSE("Value is successfully read from address 0x%lx.\n",
- (ccn_plat_desc->periphbase
- + region_id_to_base(region_id))
- + reg_offset);
- return val;
- }
|