diff options
Diffstat (limited to 'drivers/arm')
30 files changed, 6093 insertions, 808 deletions
diff --git a/drivers/arm/cci/cci.c b/drivers/arm/cci/cci.c new file mode 100644 index 00000000..e1568384 --- /dev/null +++ b/drivers/arm/cci/cci.c @@ -0,0 +1,167 @@ +/* + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch.h> +#include <assert.h> +#include <cci.h> +#include <debug.h> +#include <mmio.h> +#include <stdint.h> + +#define MAKE_CCI_PART_NUMBER(hi, lo) ((hi << 8) | lo) +#define CCI_PART_LO_MASK 0xff +#define CCI_PART_HI_MASK 0xf + +/* CCI part number codes read from Peripheral ID registers 0 and 1 */ +#define CCI400_PART_NUM 0x420 +#define CCI500_PART_NUM 0x422 +#define CCI550_PART_NUM 0x423 + +#define CCI400_SLAVE_PORTS 5 +#define CCI500_SLAVE_PORTS 7 +#define CCI550_SLAVE_PORTS 7 + +static uintptr_t cci_base; +static const int *cci_slave_if_map; + +#if ENABLE_ASSERTIONS +static unsigned int max_master_id; +static int cci_num_slave_ports; + +static int validate_cci_map(const int *map) +{ + unsigned int valid_cci_map = 0; + int slave_if_id; + int i; + + /* Validate the map */ + for (i = 0; i <= max_master_id; i++) { + slave_if_id = map[i]; + + if (slave_if_id < 0) + continue; + + if (slave_if_id >= cci_num_slave_ports) { + ERROR("Slave interface ID is invalid\n"); + return 0; + } + + if (valid_cci_map & (1 << slave_if_id)) { + ERROR("Multiple masters are assigned same slave interface ID\n"); + return 0; + } + valid_cci_map |= 1 << slave_if_id; + } + + if (!valid_cci_map) { + ERROR("No master is assigned a valid slave interface\n"); + return 0; + } + + return 1; +} + +/* + * Read CCI part number from Peripheral ID registers + */ +static unsigned int read_cci_part_number(uintptr_t base) +{ + unsigned int part_lo, part_hi; + + part_lo = mmio_read_32(base + PERIPHERAL_ID0) & CCI_PART_LO_MASK; + part_hi = mmio_read_32(base + PERIPHERAL_ID1) & CCI_PART_HI_MASK; + + return MAKE_CCI_PART_NUMBER(part_hi, part_lo); +} + +/* + * Identify a CCI device, and return the number of slaves. Return -1 for an + * unidentified device. + */ +static int get_slave_ports(unsigned int part_num) +{ + /* Macro to match CCI products */ +#define RET_ON_MATCH(product) \ + case CCI ## product ## _PART_NUM: \ + return CCI ## product ## _SLAVE_PORTS + + switch (part_num) { + + RET_ON_MATCH(400); + RET_ON_MATCH(500); + RET_ON_MATCH(550); + + default: + return -1; + } + +#undef RET_ON_MATCH +} +#endif /* ENABLE_ASSERTIONS */ + +void cci_init(uintptr_t base, const int *map, unsigned int num_cci_masters) +{ + assert(map); + assert(base); + + cci_base = base; + cci_slave_if_map = map; + +#if ENABLE_ASSERTIONS + /* + * Master Id's are assigned from zero, So in an array of size n + * the max master id is (n - 1). + */ + max_master_id = num_cci_masters - 1; + cci_num_slave_ports = get_slave_ports(read_cci_part_number(base)); +#endif + assert(cci_num_slave_ports >= 0); + + assert(validate_cci_map(map)); +} + +void cci_enable_snoop_dvm_reqs(unsigned int master_id) +{ + int slave_if_id = cci_slave_if_map[master_id]; + + assert(master_id <= max_master_id); + assert((slave_if_id < cci_num_slave_ports) && (slave_if_id >= 0)); + assert(cci_base); + + /* + * Enable Snoops and DVM messages, no need for Read/Modify/Write as + * rest of bits are write ignore + */ + mmio_write_32(cci_base + + SLAVE_IFACE_OFFSET(slave_if_id) + SNOOP_CTRL_REG, + DVM_EN_BIT | SNOOP_EN_BIT); + + /* Wait for the dust to settle down */ + while (mmio_read_32(cci_base + STATUS_REG) & CHANGE_PENDING_BIT) + ; +} + +void cci_disable_snoop_dvm_reqs(unsigned int master_id) +{ + int slave_if_id = cci_slave_if_map[master_id]; + + assert(master_id <= max_master_id); + assert((slave_if_id < cci_num_slave_ports) && (slave_if_id >= 0)); + assert(cci_base); + + /* + * Disable Snoops and DVM messages, no need for Read/Modify/Write as + * rest of bits are write ignore. + */ + mmio_write_32(cci_base + + SLAVE_IFACE_OFFSET(slave_if_id) + SNOOP_CTRL_REG, + ~(DVM_EN_BIT | SNOOP_EN_BIT)); + + /* Wait for the dust to settle down */ + while (mmio_read_32(cci_base + STATUS_REG) & CHANGE_PENDING_BIT) + ; +} + diff --git a/drivers/arm/cci400/cci400.c b/drivers/arm/cci400/cci400.c index 6a8737a5..402e5e19 100644 --- a/drivers/arm/cci400/cci400.c +++ b/drivers/arm/cci400/cci400.c @@ -1,59 +1,40 @@ /* * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of ARM nor the names of its contributors may be used - * to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * SPDX-License-Identifier: BSD-3-Clause */ #include <arch.h> #include <assert.h> #include <cci400.h> +#include <debug.h> #include <mmio.h> +#include <stdint.h> #define MAX_CLUSTERS 2 -static unsigned long cci_base_addr; +static uintptr_t cci_base_addr; static unsigned int cci_cluster_ix_to_iface[MAX_CLUSTERS]; -void cci_init(unsigned long cci_base, +void cci_init(uintptr_t cci_base, int slave_iface3_cluster_ix, int slave_iface4_cluster_ix) { /* * Check the passed arguments are valid. The cluster indices must be * less than MAX_CLUSTERS, not the same as each other and at least one - * of them must be refer to a valid cluster index. + * of them must refer to a valid cluster index. */ assert(cci_base); assert(slave_iface3_cluster_ix < MAX_CLUSTERS); assert(slave_iface4_cluster_ix < MAX_CLUSTERS); assert(slave_iface3_cluster_ix != slave_iface4_cluster_ix); assert((slave_iface3_cluster_ix >= 0) || - (slave_iface3_cluster_ix >= 0)); + (slave_iface4_cluster_ix >= 0)); + + WARN("Please migrate to common cci driver, This driver will be" \ + " deprecated in future\n"); cci_base_addr = cci_base; if (slave_iface3_cluster_ix >= 0) diff --git a/drivers/arm/ccn/ccn.c b/drivers/arm/ccn/ccn.c new file mode 100644 index 00000000..afb7d9d3 --- /dev/null +++ b/drivers/arm/ccn/ccn.c @@ -0,0 +1,492 @@ +/* + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch.h> +#include <assert.h> +#include <bakery_lock.h> +#include <ccn.h> +#include <debug.h> +#include <errno.h> +#include <mmio.h> +#include "ccn_private.h" + +static const ccn_desc_t *ccn_plat_desc; +#if defined(IMAGE_BL31) || (defined(AARCH32) && 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 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 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(AARCH32) && 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(AARCH32) && 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 seperately, 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 paritition 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 paritition 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); +} diff --git a/drivers/arm/ccn/ccn_private.h b/drivers/arm/ccn/ccn_private.h new file mode 100644 index 00000000..c17c2742 --- /dev/null +++ b/drivers/arm/ccn/ccn_private.h @@ -0,0 +1,233 @@ +/* + * Copyright (c) 2015-2016, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __CCN_PRIVATE_H__ +#define __CCN_PRIVATE_H__ + +/* + * A CCN implementation can have a maximum of 64 Request nodes with node IDs + * from 0-63. These IDs are split across the three types of Request nodes + * i.e. RN-F, RN-D and RN-I. + */ +#define MAX_RN_NODES 64 + +/* Enum used to loop through the 3 types of Request nodes */ +typedef enum rn_types { + RN_TYPE_RNF = 0, + RN_TYPE_RNI, + RN_TYPE_RND, + NUM_RN_TYPES +} rn_types_t; + +/* Macro to convert a region id to its base address */ +#define region_id_to_base(id) ((id) << 16) + +/* + * Macro to calculate the number of master interfaces resident on a RN-I/RN-D. + * Value of first two bits of the RN-I/D node type + 1 == Maximum number of + * ACE-Lite or ACE-Lite+DVM interfaces supported on this node. E.g. + * + * 0x14 : RN-I with 1 ACE-Lite interface + * 0x15 : RN-I with 2 ACE-Lite interfaces + * 0x16 : RN-I with 3 ACE-Lite interfaces + */ +#define rn_type_id_to_master_cnt(id) (((id) & 0x3) + 1) + +/* + * Constants used to identify a region in the programmer's view. These are + * common for all regions. + */ +#define REGION_ID_LIMIT 256 +#define REGION_ID_OFFSET 0xFF00 + +#define REGION_NODE_ID_SHIFT 8 +#define REGION_NODE_ID_MASK 0x7f +#define get_node_id(id_reg) (((id_reg) >> REGION_NODE_ID_SHIFT) \ + & REGION_NODE_ID_MASK) + +#define REGION_NODE_TYPE_SHIFT 0 +#define REGION_NODE_TYPE_MASK 0x1f +#define get_node_type(id_reg) (((id_reg) >> REGION_NODE_TYPE_SHIFT) \ + & REGION_NODE_TYPE_MASK) + +/* Common offsets of registers to enter or exit a snoop/dvm domain */ +#define DOMAIN_CTRL_STAT_OFFSET 0x0200 +#define DOMAIN_CTRL_SET_OFFSET 0x0210 +#define DOMAIN_CTRL_CLR_OFFSET 0x0220 + +/* + * Thess macros are used to determine if an operation to add or remove a Request + * node from the snoop/dvm domain has completed. 'rn_id_map' is a bit map of + * nodes. It was used to program the SET or CLEAR control register. The type of + * register is specified by 'op_reg_offset'. 'status_reg' is the bit map of + * nodes currently present in the snoop/dvm domain. 'rn_id_map' and 'status_reg' + * are logically ANDed and the result it stored back in the 'status_reg'. There + * are two outcomes of this operation: + * + * 1. If the DOMAIN_CTRL_SET_OFFSET register was programmed, then the set bits in + * 'rn_id_map' should appear in 'status_reg' when the operation completes. So + * after the AND operation, at some point of time 'status_reg' should equal + * 'rn_id_map'. + * + * 2. If the DOMAIN_CTRL_CLR_OFFSET register was programmed, then the set bits in + * 'rn_id_map' should disappear in 'status_reg' when the operation + * completes. So after the AND operation, at some point of time 'status_reg' + * should equal 0. + */ +#define WAIT_FOR_DOMAIN_CTRL_OP_COMPLETION(region_id, stat_reg_offset, \ + op_reg_offset, rn_id_map) \ + { \ + unsigned long long status_reg; \ + do { \ + status_reg = ccn_reg_read((ccn_plat_desc->periphbase), \ + (region_id), \ + (stat_reg_offset)); \ + status_reg &= (rn_id_map); \ + } while ((op_reg_offset) == DOMAIN_CTRL_SET_OFFSET ? \ + (rn_id_map) != status_reg : status_reg); \ + } + +/* + * Region ID of the Miscellaneous Node is always 0 as its located at the base of + * the programmer's view. + */ +#define MN_REGION_ID 0 + +#define MN_REGION_ID_START 0 +#define DEBUG_REGION_ID_START 1 +#define HNI_REGION_ID_START 8 +#define SBSX_REGION_ID_START 16 +#define HNF_REGION_ID_START 32 +#define XP_REGION_ID_START 64 +#define RNI_REGION_ID_START 128 + +/* Selected register offsets from the base of a HNF region */ +#define HNF_CFG_CTRL_OFFSET 0x0000 +#define HNF_SAM_CTRL_OFFSET 0x0008 +#define HNF_PSTATE_REQ_OFFSET 0x0010 +#define HNF_PSTATE_STAT_OFFSET 0x0018 +#define HNF_SDC_STAT_OFFSET DOMAIN_CTRL_STAT_OFFSET +#define HNF_SDC_SET_OFFSET DOMAIN_CTRL_SET_OFFSET +#define HNF_SDC_CLR_OFFSET DOMAIN_CTRL_CLR_OFFSET +#define HNF_AUX_CTRL_OFFSET 0x0500 + +/* Selected register offsets from the base of a MN region */ +#define MN_SAR_OFFSET 0x0000 +#define MN_RNF_NODEID_OFFSET 0x0180 +#define MN_RNI_NODEID_OFFSET 0x0190 +#define MN_RND_NODEID_OFFSET 0x01A0 +#define MN_HNF_NODEID_OFFSET 0x01B0 +#define MN_HNI_NODEID_OFFSET 0x01C0 +#define MN_SN_NODEID_OFFSET 0x01D0 +#define MN_DDC_STAT_OFFSET DOMAIN_CTRL_STAT_OFFSET +#define MN_DDC_SET_OFFSET DOMAIN_CTRL_SET_OFFSET +#define MN_DDC_CLR_OFFSET DOMAIN_CTRL_CLR_OFFSET +#define MN_PERIPH_ID_0_1_OFFSET 0xFE0 +#define MN_ID_OFFSET REGION_ID_OFFSET + +/* HNF System Address Map register bit masks and shifts */ +#define HNF_SAM_CTRL_SN_ID_MASK 0x7f +#define HNF_SAM_CTRL_SN0_ID_SHIFT 0 +#define HNF_SAM_CTRL_SN1_ID_SHIFT 8 +#define HNF_SAM_CTRL_SN2_ID_SHIFT 16 + +#define HNF_SAM_CTRL_TAB0_MASK ULL(0x3f) +#define HNF_SAM_CTRL_TAB0_SHIFT 48 +#define HNF_SAM_CTRL_TAB1_MASK ULL(0x3f) +#define HNF_SAM_CTRL_TAB1_SHIFT 56 + +#define HNF_SAM_CTRL_3SN_ENB_SHIFT 32 +#define HNF_SAM_CTRL_3SN_ENB_MASK ULL(0x01) + +/* + * Macro to create a value suitable for programming into a HNF SAM Control + * register for enabling 3SN striping. + */ +#define MAKE_HNF_SAM_CTRL_VALUE(sn0, sn1, sn2, tab0, tab1, three_sn_en) \ + ((((sn0) & HNF_SAM_CTRL_SN_ID_MASK) << HNF_SAM_CTRL_SN0_ID_SHIFT) | \ + (((sn1) & HNF_SAM_CTRL_SN_ID_MASK) << HNF_SAM_CTRL_SN1_ID_SHIFT) | \ + (((sn2) & HNF_SAM_CTRL_SN_ID_MASK) << HNF_SAM_CTRL_SN2_ID_SHIFT) | \ + (((tab0) & HNF_SAM_CTRL_TAB0_MASK) << HNF_SAM_CTRL_TAB0_SHIFT) | \ + (((tab1) & HNF_SAM_CTRL_TAB1_MASK) << HNF_SAM_CTRL_TAB1_SHIFT) | \ + (((three_sn_en) & HNF_SAM_CTRL_3SN_ENB_MASK) << HNF_SAM_CTRL_3SN_ENB_SHIFT)) + +/* Mask to read the power state value from an HN-F P-state register */ +#define HNF_PSTATE_MASK 0xf + +/* Macro to extract the run mode from a p-state value */ +#define PSTATE_TO_RUN_MODE(pstate) (((pstate) & HNF_PSTATE_MASK) >> 2) + +/* + * Helper macro that iterates through a given bit map. In each iteration, + * it returns the position of the set bit. + * It can be used by other utility macros to iterates through all nodes + * or masters given a bit map of them. + */ +#define FOR_EACH_BIT(bit_pos, bit_map) \ + for (bit_pos = __builtin_ctzll(bit_map); \ + bit_map; \ + bit_map &= ~(1ULL << (bit_pos)), \ + bit_pos = __builtin_ctzll(bit_map)) + +/* + * Utility macro that iterates through a bit map of node IDs. In each + * iteration, it returns the ID of the next present node in the bit map. Node + * ID of a present node == Position of set bit == Number of zeroes trailing the + * bit. + */ +#define FOR_EACH_PRESENT_NODE_ID(node_id, bit_map) \ + FOR_EACH_BIT(node_id, bit_map) + +/* + * Helper function to return number of set bits in bitmap + */ +static inline unsigned int count_set_bits(unsigned long long bitmap) +{ + unsigned int count = 0; + + for (; bitmap; bitmap &= bitmap - 1) + ++count; + + return count; +} + +/* + * Utility macro that iterates through a bit map of node IDs. In each iteration, + * it returns the ID of the next present region corresponding to a node present + * in the bit map. Region ID of a present node is in between passed region id + * and region id + number of set bits in the bitmap i.e. the number of present + * nodes. + */ +#define FOR_EACH_PRESENT_REGION_ID(region_id, bit_map) \ + for (unsigned long long region_id_limit = count_set_bits(bit_map) \ + + region_id; \ + region_id < region_id_limit; \ + region_id++) + +/* + * Same macro as FOR_EACH_PRESENT_NODE, but renamed to indicate it traverses + * through a bit map of master interfaces. + */ +#define FOR_EACH_PRESENT_MASTER_INTERFACE(iface_id, bit_map) \ + FOR_EACH_BIT(iface_id, bit_map) + +/* + * Macro that returns the node id bit map for the Miscellaneous Node + */ +#define CCN_GET_MN_NODEID_MAP(periphbase) \ + (1 << get_node_id(ccn_reg_read(periphbase, MN_REGION_ID, \ + REGION_ID_OFFSET))) + +/* + * This macro returns the bitmap of Home nodes on the basis of the + * 'mn_hn_id_reg_offset' parameter from the Miscellaneous node's (MN) + * programmer's view. The MN has a register which carries the bitmap of present + * Home nodes of each type i.e. HN-Fs, HN-Is & HN-Ds. + */ +#define CCN_GET_HN_NODEID_MAP(periphbase, mn_hn_id_reg_offset) \ + ccn_reg_read(periphbase, MN_REGION_ID, mn_hn_id_reg_offset) + +#endif /* __CCN_PRIVATE_H__ */ diff --git a/drivers/arm/gic/arm_gic.c b/drivers/arm/gic/arm_gic.c index 58fbc89a..e040e0ac 100644 --- a/drivers/arm/gic/arm_gic.c +++ b/drivers/arm/gic/arm_gic.c @@ -1,31 +1,7 @@ /* - * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2016, ARM Limited and Contributors. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of ARM nor the names of its contributors may be used - * to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * SPDX-License-Identifier: BSD-3-Clause */ #include <arch.h> @@ -47,9 +23,9 @@ (GIC_HIGHEST_NS_PRIORITY << 16) | \ (GIC_HIGHEST_NS_PRIORITY << 24)) -static unsigned int g_gicc_base; -static unsigned int g_gicd_base; -static unsigned long g_gicr_base; +static uintptr_t g_gicc_base; +static uintptr_t g_gicd_base; +static uintptr_t g_gicr_base; static const unsigned int *g_irq_sec_ptr; static unsigned int g_num_irqs; @@ -62,7 +38,7 @@ static unsigned int g_num_irqs; ******************************************************************************/ static void gicv3_cpuif_setup(void) { - unsigned int scr_val, val; + unsigned int val; uintptr_t base; /* @@ -93,35 +69,9 @@ static void gicv3_cpuif_setup(void) while (val & WAKER_CA) val = gicr_read_waker(base); - /* - * We need to set SCR_EL3.NS in order to see GICv3 non-secure state. - * Restore SCR_EL3.NS again before exit. - */ - scr_val = read_scr(); - write_scr(scr_val | SCR_NS_BIT); - isb(); /* ensure NS=1 takes effect before accessing ICC_SRE_EL2 */ - - /* - * By default EL2 and NS-EL1 software should be able to enable GICv3 - * System register access without any configuration at EL3. But it turns - * out that GICC PMR as set in GICv2 mode does not affect GICv3 mode. So - * we need to set it here again. In order to do that we need to enable - * register access. We leave it enabled as it should be fine and might - * prevent problems with later software trying to access GIC System - * Registers. - */ val = read_icc_sre_el3(); write_icc_sre_el3(val | ICC_SRE_EN | ICC_SRE_SRE); - - val = read_icc_sre_el2(); - write_icc_sre_el2(val | ICC_SRE_EN | ICC_SRE_SRE); - - write_icc_pmr_el1(GIC_PRI_MASK); - isb(); /* commit ICC_* changes before setting NS=0 */ - - /* Restore SCR_EL3 */ - write_scr(scr_val); - isb(); /* ensure NS=0 takes effect immediately */ + isb(); } /******************************************************************************* @@ -219,13 +169,10 @@ void arm_gic_cpuif_deactivate(void) ******************************************************************************/ void arm_gic_pcpu_distif_setup(void) { - unsigned int index, irq_num; + unsigned int index, irq_num, sec_ppi_sgi_mask; assert(g_gicd_base); - /* Mark all 32 SGI+PPI interrupts as Group 1 (non-secure) */ - gicd_write_igroupr(g_gicd_base, 0, ~0); - /* Setup PPI priorities doing four at a time */ for (index = 0; index < 32; index += 4) { gicd_write_ipriorityr(g_gicd_base, index, @@ -233,16 +180,29 @@ void arm_gic_pcpu_distif_setup(void) } assert(g_irq_sec_ptr); + sec_ppi_sgi_mask = 0; + + /* Ensure all SGIs and PPIs are Group0 to begin with */ + gicd_write_igroupr(g_gicd_base, 0, 0); + for (index = 0; index < g_num_irqs; index++) { irq_num = g_irq_sec_ptr[index]; if (irq_num < MIN_SPI_ID) { /* We have an SGI or a PPI */ - gicd_clr_igroupr(g_gicd_base, irq_num); + sec_ppi_sgi_mask |= 1U << irq_num; gicd_set_ipriorityr(g_gicd_base, irq_num, GIC_HIGHEST_SEC_PRIORITY); gicd_set_isenabler(g_gicd_base, irq_num); } } + + /* + * Invert the bitmask to create a mask for non-secure PPIs and + * SGIs. Program the GICD_IGROUPR0 with this bit mask. This write will + * update the GICR_IGROUPR0 as well in case we are running on a GICv3 + * system. This is critical if GICD_CTLR.ARE_NS=1. + */ + gicd_write_igroupr(g_gicd_base, 0, ~sec_ppi_sgi_mask); } /******************************************************************************* @@ -317,12 +277,11 @@ static void arm_gic_distif_setup(void) /******************************************************************************* * Initialize the ARM GIC driver with the provided platform inputs ******************************************************************************/ -void arm_gic_init(unsigned int gicc_base, - unsigned int gicd_base, - unsigned long gicr_base, - const unsigned int *irq_sec_ptr, - unsigned int num_irqs - ) +void arm_gic_init(uintptr_t gicc_base, + uintptr_t gicd_base, + uintptr_t gicr_base, + const unsigned int *irq_sec_ptr, + unsigned int num_irqs) { unsigned int val; @@ -395,7 +354,7 @@ uint32_t arm_gic_get_pending_interrupt_type(void) uint32_t id; assert(g_gicc_base); - id = gicc_read_hppir(g_gicc_base); + id = gicc_read_hppir(g_gicc_base) & INT_ID_MASK; /* Assume that all secure interrupts are S-EL1 interrupts */ if (id < 1022) @@ -417,7 +376,7 @@ uint32_t arm_gic_get_pending_interrupt_id(void) uint32_t id; assert(g_gicc_base); - id = gicc_read_hppir(g_gicc_base); + id = gicc_read_hppir(g_gicc_base) & INT_ID_MASK; if (id < 1022) return id; @@ -429,7 +388,7 @@ uint32_t arm_gic_get_pending_interrupt_id(void) * Find out which non-secure interrupt it is under the assumption that * the GICC_CTLR.AckCtl bit is 0. */ - return gicc_read_ahppir(g_gicc_base); + return gicc_read_ahppir(g_gicc_base) & INT_ID_MASK; } /******************************************************************************* diff --git a/drivers/arm/gic/common/gic_common.c b/drivers/arm/gic/common/gic_common.c new file mode 100644 index 00000000..d523772b --- /dev/null +++ b/drivers/arm/gic/common/gic_common.c @@ -0,0 +1,313 @@ +/* + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <gic_common.h> +#include <mmio.h> +#include "gic_common_private.h" + +/******************************************************************************* + * GIC Distributor interface accessors for reading entire registers + ******************************************************************************/ +/* + * Accessor to read the GIC Distributor IGROUPR corresponding to the interrupt + * `id`, 32 interrupt ids at a time. + */ +unsigned int gicd_read_igroupr(uintptr_t base, unsigned int id) +{ + unsigned n = id >> IGROUPR_SHIFT; + return mmio_read_32(base + GICD_IGROUPR + (n << 2)); +} + +/* + * Accessor to read the GIC Distributor ISENABLER corresponding to the + * interrupt `id`, 32 interrupt ids at a time. + */ +unsigned int gicd_read_isenabler(uintptr_t base, unsigned int id) +{ + unsigned n = id >> ISENABLER_SHIFT; + return mmio_read_32(base + GICD_ISENABLER + (n << 2)); +} + +/* + * Accessor to read the GIC Distributor ICENABLER corresponding to the + * interrupt `id`, 32 interrupt IDs at a time. + */ +unsigned int gicd_read_icenabler(uintptr_t base, unsigned int id) +{ + unsigned n = id >> ICENABLER_SHIFT; + return mmio_read_32(base + GICD_ICENABLER + (n << 2)); +} + +/* + * Accessor to read the GIC Distributor ISPENDR corresponding to the + * interrupt `id`, 32 interrupt IDs at a time. + */ +unsigned int gicd_read_ispendr(uintptr_t base, unsigned int id) +{ + unsigned n = id >> ISPENDR_SHIFT; + return mmio_read_32(base + GICD_ISPENDR + (n << 2)); +} + +/* + * Accessor to read the GIC Distributor ICPENDR corresponding to the + * interrupt `id`, 32 interrupt IDs at a time. + */ +unsigned int gicd_read_icpendr(uintptr_t base, unsigned int id) +{ + unsigned n = id >> ICPENDR_SHIFT; + return mmio_read_32(base + GICD_ICPENDR + (n << 2)); +} + +/* + * Accessor to read the GIC Distributor ISACTIVER corresponding to the + * interrupt `id`, 32 interrupt IDs at a time. + */ +unsigned int gicd_read_isactiver(uintptr_t base, unsigned int id) +{ + unsigned n = id >> ISACTIVER_SHIFT; + return mmio_read_32(base + GICD_ISACTIVER + (n << 2)); +} + +/* + * Accessor to read the GIC Distributor ICACTIVER corresponding to the + * interrupt `id`, 32 interrupt IDs at a time. + */ +unsigned int gicd_read_icactiver(uintptr_t base, unsigned int id) +{ + unsigned n = id >> ICACTIVER_SHIFT; + return mmio_read_32(base + GICD_ICACTIVER + (n << 2)); +} + +/* + * Accessor to read the GIC Distributor IPRIORITYR corresponding to the + * interrupt `id`, 4 interrupt IDs at a time. + */ +unsigned int gicd_read_ipriorityr(uintptr_t base, unsigned int id) +{ + unsigned n = id >> IPRIORITYR_SHIFT; + return mmio_read_32(base + GICD_IPRIORITYR + (n << 2)); +} + +/* + * Accessor to read the GIC Distributor ICGFR corresponding to the + * interrupt `id`, 16 interrupt IDs at a time. + */ +unsigned int gicd_read_icfgr(uintptr_t base, unsigned int id) +{ + unsigned n = id >> ICFGR_SHIFT; + return mmio_read_32(base + GICD_ICFGR + (n << 2)); +} + +/* + * Accessor to read the GIC Distributor NSACR corresponding to the + * interrupt `id`, 16 interrupt IDs at a time. + */ +unsigned int gicd_read_nsacr(uintptr_t base, unsigned int id) +{ + unsigned n = id >> NSACR_SHIFT; + return mmio_read_32(base + GICD_NSACR + (n << 2)); +} + +/******************************************************************************* + * GIC Distributor interface accessors for writing entire registers + ******************************************************************************/ +/* + * Accessor to write the GIC Distributor IGROUPR corresponding to the + * interrupt `id`, 32 interrupt IDs at a time. + */ +void gicd_write_igroupr(uintptr_t base, unsigned int id, unsigned int val) +{ + unsigned n = id >> IGROUPR_SHIFT; + mmio_write_32(base + GICD_IGROUPR + (n << 2), val); +} + +/* + * Accessor to write the GIC Distributor ISENABLER corresponding to the + * interrupt `id`, 32 interrupt IDs at a time. + */ +void gicd_write_isenabler(uintptr_t base, unsigned int id, unsigned int val) +{ + unsigned n = id >> ISENABLER_SHIFT; + mmio_write_32(base + GICD_ISENABLER + (n << 2), val); +} + +/* + * Accessor to write the GIC Distributor ICENABLER corresponding to the + * interrupt `id`, 32 interrupt IDs at a time. + */ +void gicd_write_icenabler(uintptr_t base, unsigned int id, unsigned int val) +{ + unsigned n = id >> ICENABLER_SHIFT; + mmio_write_32(base + GICD_ICENABLER + (n << 2), val); +} + +/* + * Accessor to write the GIC Distributor ISPENDR corresponding to the + * interrupt `id`, 32 interrupt IDs at a time. + */ +void gicd_write_ispendr(uintptr_t base, unsigned int id, unsigned int val) +{ + unsigned n = id >> ISPENDR_SHIFT; + mmio_write_32(base + GICD_ISPENDR + (n << 2), val); +} + +/* + * Accessor to write the GIC Distributor ICPENDR corresponding to the + * interrupt `id`, 32 interrupt IDs at a time. + */ +void gicd_write_icpendr(uintptr_t base, unsigned int id, unsigned int val) +{ + unsigned n = id >> ICPENDR_SHIFT; + mmio_write_32(base + GICD_ICPENDR + (n << 2), val); +} + +/* + * Accessor to write the GIC Distributor ISACTIVER corresponding to the + * interrupt `id`, 32 interrupt IDs at a time. + */ +void gicd_write_isactiver(uintptr_t base, unsigned int id, unsigned int val) +{ + unsigned n = id >> ISACTIVER_SHIFT; + mmio_write_32(base + GICD_ISACTIVER + (n << 2), val); +} + +/* + * Accessor to write the GIC Distributor ICACTIVER corresponding to the + * interrupt `id`, 32 interrupt IDs at a time. + */ +void gicd_write_icactiver(uintptr_t base, unsigned int id, unsigned int val) +{ + unsigned n = id >> ICACTIVER_SHIFT; + mmio_write_32(base + GICD_ICACTIVER + (n << 2), val); +} + +/* + * Accessor to write the GIC Distributor IPRIORITYR corresponding to the + * interrupt `id`, 4 interrupt IDs at a time. + */ +void gicd_write_ipriorityr(uintptr_t base, unsigned int id, unsigned int val) +{ + unsigned n = id >> IPRIORITYR_SHIFT; + mmio_write_32(base + GICD_IPRIORITYR + (n << 2), val); +} + +/* + * Accessor to write the GIC Distributor ICFGR corresponding to the + * interrupt `id`, 16 interrupt IDs at a time. + */ +void gicd_write_icfgr(uintptr_t base, unsigned int id, unsigned int val) +{ + unsigned n = id >> ICFGR_SHIFT; + mmio_write_32(base + GICD_ICFGR + (n << 2), val); +} + +/* + * Accessor to write the GIC Distributor NSACR corresponding to the + * interrupt `id`, 16 interrupt IDs at a time. + */ +void gicd_write_nsacr(uintptr_t base, unsigned int id, unsigned int val) +{ + unsigned n = id >> NSACR_SHIFT; + mmio_write_32(base + GICD_NSACR + (n << 2), val); +} + +/******************************************************************************* + * GIC Distributor functions for accessing the GIC registers + * corresponding to a single interrupt ID. These functions use bitwise + * operations or appropriate register accesses to modify or return + * the bit-field corresponding the single interrupt ID. + ******************************************************************************/ +unsigned int gicd_get_igroupr(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << IGROUPR_SHIFT) - 1); + unsigned int reg_val = gicd_read_igroupr(base, id); + + return (reg_val >> bit_num) & 0x1; +} + +void gicd_set_igroupr(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << IGROUPR_SHIFT) - 1); + unsigned int reg_val = gicd_read_igroupr(base, id); + + gicd_write_igroupr(base, id, reg_val | (1 << bit_num)); +} + +void gicd_clr_igroupr(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << IGROUPR_SHIFT) - 1); + unsigned int reg_val = gicd_read_igroupr(base, id); + + gicd_write_igroupr(base, id, reg_val & ~(1 << bit_num)); +} + +void gicd_set_isenabler(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << ISENABLER_SHIFT) - 1); + + gicd_write_isenabler(base, id, (1 << bit_num)); +} + +void gicd_set_icenabler(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << ICENABLER_SHIFT) - 1); + + gicd_write_icenabler(base, id, (1 << bit_num)); +} + +void gicd_set_ispendr(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << ISPENDR_SHIFT) - 1); + + gicd_write_ispendr(base, id, (1 << bit_num)); +} + +void gicd_set_icpendr(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << ICPENDR_SHIFT) - 1); + + gicd_write_icpendr(base, id, (1 << bit_num)); +} + +unsigned int gicd_get_isactiver(uintptr_t base, unsigned int id) +{ + unsigned int bit_num = id & ((1 << ISACTIVER_SHIFT) - 1); + unsigned int reg_val = gicd_read_isactiver(base, id); + + return (reg_val >> bit_num) & 0x1; +} + +void gicd_set_isactiver(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << ISACTIVER_SHIFT) - 1); + + gicd_write_isactiver(base, id, (1 << bit_num)); +} + +void gicd_set_icactiver(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << ICACTIVER_SHIFT) - 1); + + gicd_write_icactiver(base, id, (1 << bit_num)); +} + +void gicd_set_ipriorityr(uintptr_t base, unsigned int id, unsigned int pri) +{ + mmio_write_8(base + GICD_IPRIORITYR + id, pri & GIC_PRI_MASK); +} + +void gicd_set_icfgr(uintptr_t base, unsigned int id, unsigned int cfg) +{ + unsigned bit_num = id & ((1 << ICFGR_SHIFT) - 1); + uint32_t reg_val = gicd_read_icfgr(base, id); + + /* Clear the field, and insert required configuration */ + reg_val &= ~(GIC_CFG_MASK << bit_num); + reg_val |= ((cfg & GIC_CFG_MASK) << bit_num); + + gicd_write_icfgr(base, id, reg_val); +} diff --git a/drivers/arm/gic/common/gic_common_private.h b/drivers/arm/gic/common/gic_common_private.h new file mode 100644 index 00000000..2021f9aa --- /dev/null +++ b/drivers/arm/gic/common/gic_common_private.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef GIC_COMMON_PRIVATE_H_ +#define GIC_COMMON_PRIVATE_H_ + +#include <gic_common.h> +#include <mmio.h> +#include <stdint.h> + +/******************************************************************************* + * GIC Distributor interface register accessors that are common to GICv3 & GICv2 + ******************************************************************************/ +static inline unsigned int gicd_read_ctlr(uintptr_t base) +{ + return mmio_read_32(base + GICD_CTLR); +} + +static inline unsigned int gicd_read_typer(uintptr_t base) +{ + return mmio_read_32(base + GICD_TYPER); +} + +static inline unsigned int gicd_read_iidr(uintptr_t base) +{ + return mmio_read_32(base + GICD_IIDR); +} + +static inline void gicd_write_ctlr(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICD_CTLR, val); +} + +/******************************************************************************* + * GIC Distributor function prototypes for accessing entire registers. + * Note: The raw register values correspond to multiple interrupt IDs and + * the number of interrupt IDs involved depends on the register accessed. + ******************************************************************************/ +unsigned int gicd_read_igroupr(uintptr_t base, unsigned int id); +unsigned int gicd_read_isenabler(uintptr_t base, unsigned int id); +unsigned int gicd_read_icenabler(uintptr_t base, unsigned int id); +unsigned int gicd_read_ispendr(uintptr_t base, unsigned int id); +unsigned int gicd_read_icpendr(uintptr_t base, unsigned int id); +unsigned int gicd_read_isactiver(uintptr_t base, unsigned int id); +unsigned int gicd_read_icactiver(uintptr_t base, unsigned int id); +unsigned int gicd_read_ipriorityr(uintptr_t base, unsigned int id); +unsigned int gicd_read_icfgr(uintptr_t base, unsigned int id); +unsigned int gicd_read_nsacr(uintptr_t base, unsigned int id); +void gicd_write_igroupr(uintptr_t base, unsigned int id, unsigned int val); +void gicd_write_isenabler(uintptr_t base, unsigned int id, unsigned int val); +void gicd_write_icenabler(uintptr_t base, unsigned int id, unsigned int val); +void gicd_write_ispendr(uintptr_t base, unsigned int id, unsigned int val); +void gicd_write_icpendr(uintptr_t base, unsigned int id, unsigned int val); +void gicd_write_isactiver(uintptr_t base, unsigned int id, unsigned int val); +void gicd_write_icactiver(uintptr_t base, unsigned int id, unsigned int val); +void gicd_write_ipriorityr(uintptr_t base, unsigned int id, unsigned int val); +void gicd_write_icfgr(uintptr_t base, unsigned int id, unsigned int val); +void gicd_write_nsacr(uintptr_t base, unsigned int id, unsigned int val); + +/******************************************************************************* + * GIC Distributor function prototypes for accessing the GIC registers + * corresponding to a single interrupt ID. These functions use bitwise + * operations or appropriate register accesses to modify or return + * the bit-field corresponding the single interrupt ID. + ******************************************************************************/ +unsigned int gicd_get_igroupr(uintptr_t base, unsigned int id); +void gicd_set_igroupr(uintptr_t base, unsigned int id); +void gicd_clr_igroupr(uintptr_t base, unsigned int id); +void gicd_set_isenabler(uintptr_t base, unsigned int id); +void gicd_set_icenabler(uintptr_t base, unsigned int id); +void gicd_set_ispendr(uintptr_t base, unsigned int id); +void gicd_set_icpendr(uintptr_t base, unsigned int id); +unsigned int gicd_get_isactiver(uintptr_t base, unsigned int id); +void gicd_set_isactiver(uintptr_t base, unsigned int id); +void gicd_set_icactiver(uintptr_t base, unsigned int id); +void gicd_set_ipriorityr(uintptr_t base, unsigned int id, unsigned int pri); +void gicd_set_icfgr(uintptr_t base, unsigned int id, unsigned int cfg); + +#endif /* GIC_COMMON_PRIVATE_H_ */ diff --git a/drivers/arm/gic/gic_v2.c b/drivers/arm/gic/gic_v2.c index 41603a98..29c79e07 100644 --- a/drivers/arm/gic/gic_v2.c +++ b/drivers/arm/gic/gic_v2.c @@ -1,31 +1,7 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of ARM nor the names of its contributors may be used - * to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * SPDX-License-Identifier: BSD-3-Clause */ #include <arch.h> @@ -38,73 +14,73 @@ * GIC Distributor interface accessors for reading entire registers ******************************************************************************/ -unsigned int gicd_read_igroupr(unsigned int base, unsigned int id) +unsigned int gicd_read_igroupr(uintptr_t base, unsigned int id) { unsigned n = id >> IGROUPR_SHIFT; return mmio_read_32(base + GICD_IGROUPR + (n << 2)); } -unsigned int gicd_read_isenabler(unsigned int base, unsigned int id) +unsigned int gicd_read_isenabler(uintptr_t base, unsigned int id) { unsigned n = id >> ISENABLER_SHIFT; return mmio_read_32(base + GICD_ISENABLER + (n << 2)); } -unsigned int gicd_read_icenabler(unsigned int base, unsigned int id) +unsigned int gicd_read_icenabler(uintptr_t base, unsigned int id) { unsigned n = id >> ICENABLER_SHIFT; return mmio_read_32(base + GICD_ICENABLER + (n << 2)); } -unsigned int gicd_read_ispendr(unsigned int base, unsigned int id) +unsigned int gicd_read_ispendr(uintptr_t base, unsigned int id) { unsigned n = id >> ISPENDR_SHIFT; return mmio_read_32(base + GICD_ISPENDR + (n << 2)); } -unsigned int gicd_read_icpendr(unsigned int base, unsigned int id) +unsigned int gicd_read_icpendr(uintptr_t base, unsigned int id) { unsigned n = id >> ICPENDR_SHIFT; return mmio_read_32(base + GICD_ICPENDR + (n << 2)); } -unsigned int gicd_read_isactiver(unsigned int base, unsigned int id) +unsigned int gicd_read_isactiver(uintptr_t base, unsigned int id) { unsigned n = id >> ISACTIVER_SHIFT; return mmio_read_32(base + GICD_ISACTIVER + (n << 2)); } -unsigned int gicd_read_icactiver(unsigned int base, unsigned int id) +unsigned int gicd_read_icactiver(uintptr_t base, unsigned int id) { unsigned n = id >> ICACTIVER_SHIFT; return mmio_read_32(base + GICD_ICACTIVER + (n << 2)); } -unsigned int gicd_read_ipriorityr(unsigned int base, unsigned int id) +unsigned int gicd_read_ipriorityr(uintptr_t base, unsigned int id) { unsigned n = id >> IPRIORITYR_SHIFT; return mmio_read_32(base + GICD_IPRIORITYR + (n << 2)); } -unsigned int gicd_read_itargetsr(unsigned int base, unsigned int id) +unsigned int gicd_read_itargetsr(uintptr_t base, unsigned int id) { unsigned n = id >> ITARGETSR_SHIFT; return mmio_read_32(base + GICD_ITARGETSR + (n << 2)); } -unsigned int gicd_read_icfgr(unsigned int base, unsigned int id) +unsigned int gicd_read_icfgr(uintptr_t base, unsigned int id) { unsigned n = id >> ICFGR_SHIFT; return mmio_read_32(base + GICD_ICFGR + (n << 2)); } -unsigned int gicd_read_cpendsgir(unsigned int base, unsigned int id) +unsigned int gicd_read_cpendsgir(uintptr_t base, unsigned int id) { unsigned n = id >> CPENDSGIR_SHIFT; return mmio_read_32(base + GICD_CPENDSGIR + (n << 2)); } -unsigned int gicd_read_spendsgir(unsigned int base, unsigned int id) +unsigned int gicd_read_spendsgir(uintptr_t base, unsigned int id) { unsigned n = id >> SPENDSGIR_SHIFT; return mmio_read_32(base + GICD_SPENDSGIR + (n << 2)); @@ -114,73 +90,73 @@ unsigned int gicd_read_spendsgir(unsigned int base, unsigned int id) * GIC Distributor interface accessors for writing entire registers ******************************************************************************/ -void gicd_write_igroupr(unsigned int base, unsigned int id, unsigned int val) +void gicd_write_igroupr(uintptr_t base, unsigned int id, unsigned int val) { unsigned n = id >> IGROUPR_SHIFT; mmio_write_32(base + GICD_IGROUPR + (n << 2), val); } -void gicd_write_isenabler(unsigned int base, unsigned int id, unsigned int val) +void gicd_write_isenabler(uintptr_t base, unsigned int id, unsigned int val) { unsigned n = id >> ISENABLER_SHIFT; mmio_write_32(base + GICD_ISENABLER + (n << 2), val); } -void gicd_write_icenabler(unsigned int base, unsigned int id, unsigned int val) +void gicd_write_icenabler(uintptr_t base, unsigned int id, unsigned int val) { unsigned n = id >> ICENABLER_SHIFT; mmio_write_32(base + GICD_ICENABLER + (n << 2), val); } -void gicd_write_ispendr(unsigned int base, unsigned int id, unsigned int val) +void gicd_write_ispendr(uintptr_t base, unsigned int id, unsigned int val) { unsigned n = id >> ISPENDR_SHIFT; mmio_write_32(base + GICD_ISPENDR + (n << 2), val); } -void gicd_write_icpendr(unsigned int base, unsigned int id, unsigned int val) +void gicd_write_icpendr(uintptr_t base, unsigned int id, unsigned int val) { unsigned n = id >> ICPENDR_SHIFT; mmio_write_32(base + GICD_ICPENDR + (n << 2), val); } -void gicd_write_isactiver(unsigned int base, unsigned int id, unsigned int val) +void gicd_write_isactiver(uintptr_t base, unsigned int id, unsigned int val) { unsigned n = id >> ISACTIVER_SHIFT; mmio_write_32(base + GICD_ISACTIVER + (n << 2), val); } -void gicd_write_icactiver(unsigned int base, unsigned int id, unsigned int val) +void gicd_write_icactiver(uintptr_t base, unsigned int id, unsigned int val) { unsigned n = id >> ICACTIVER_SHIFT; mmio_write_32(base + GICD_ICACTIVER + (n << 2), val); } -void gicd_write_ipriorityr(unsigned int base, unsigned int id, unsigned int val) +void gicd_write_ipriorityr(uintptr_t base, unsigned int id, unsigned int val) { unsigned n = id >> IPRIORITYR_SHIFT; mmio_write_32(base + GICD_IPRIORITYR + (n << 2), val); } -void gicd_write_itargetsr(unsigned int base, unsigned int id, unsigned int val) +void gicd_write_itargetsr(uintptr_t base, unsigned int id, unsigned int val) { unsigned n = id >> ITARGETSR_SHIFT; mmio_write_32(base + GICD_ITARGETSR + (n << 2), val); } -void gicd_write_icfgr(unsigned int base, unsigned int id, unsigned int val) +void gicd_write_icfgr(uintptr_t base, unsigned int id, unsigned int val) { unsigned n = id >> ICFGR_SHIFT; mmio_write_32(base + GICD_ICFGR + (n << 2), val); } -void gicd_write_cpendsgir(unsigned int base, unsigned int id, unsigned int val) +void gicd_write_cpendsgir(uintptr_t base, unsigned int id, unsigned int val) { unsigned n = id >> CPENDSGIR_SHIFT; mmio_write_32(base + GICD_CPENDSGIR + (n << 2), val); } -void gicd_write_spendsgir(unsigned int base, unsigned int id, unsigned int val) +void gicd_write_spendsgir(uintptr_t base, unsigned int id, unsigned int val) { unsigned n = id >> SPENDSGIR_SHIFT; mmio_write_32(base + GICD_SPENDSGIR + (n << 2), val); @@ -189,7 +165,7 @@ void gicd_write_spendsgir(unsigned int base, unsigned int id, unsigned int val) /******************************************************************************* * GIC Distributor interface accessors for individual interrupt manipulation ******************************************************************************/ -unsigned int gicd_get_igroupr(unsigned int base, unsigned int id) +unsigned int gicd_get_igroupr(uintptr_t base, unsigned int id) { unsigned bit_num = id & ((1 << IGROUPR_SHIFT) - 1); unsigned int reg_val = gicd_read_igroupr(base, id); @@ -197,7 +173,7 @@ unsigned int gicd_get_igroupr(unsigned int base, unsigned int id) return (reg_val >> bit_num) & 0x1; } -void gicd_set_igroupr(unsigned int base, unsigned int id) +void gicd_set_igroupr(uintptr_t base, unsigned int id) { unsigned bit_num = id & ((1 << IGROUPR_SHIFT) - 1); unsigned int reg_val = gicd_read_igroupr(base, id); @@ -205,7 +181,7 @@ void gicd_set_igroupr(unsigned int base, unsigned int id) gicd_write_igroupr(base, id, reg_val | (1 << bit_num)); } -void gicd_clr_igroupr(unsigned int base, unsigned int id) +void gicd_clr_igroupr(uintptr_t base, unsigned int id) { unsigned bit_num = id & ((1 << IGROUPR_SHIFT) - 1); unsigned int reg_val = gicd_read_igroupr(base, id); @@ -213,42 +189,42 @@ void gicd_clr_igroupr(unsigned int base, unsigned int id) gicd_write_igroupr(base, id, reg_val & ~(1 << bit_num)); } -void gicd_set_isenabler(unsigned int base, unsigned int id) +void gicd_set_isenabler(uintptr_t base, unsigned int id) { unsigned bit_num = id & ((1 << ISENABLER_SHIFT) - 1); gicd_write_isenabler(base, id, (1 << bit_num)); } -void gicd_set_icenabler(unsigned int base, unsigned int id) +void gicd_set_icenabler(uintptr_t base, unsigned int id) { unsigned bit_num = id & ((1 << ICENABLER_SHIFT) - 1); gicd_write_icenabler(base, id, (1 << bit_num)); } -void gicd_set_ispendr(unsigned int base, unsigned int id) +void gicd_set_ispendr(uintptr_t base, unsigned int id) { unsigned bit_num = id & ((1 << ISPENDR_SHIFT) - 1); gicd_write_ispendr(base, id, (1 << bit_num)); } -void gicd_set_icpendr(unsigned int base, unsigned int id) +void gicd_set_icpendr(uintptr_t base, unsigned int id) { unsigned bit_num = id & ((1 << ICPENDR_SHIFT) - 1); gicd_write_icpendr(base, id, (1 << bit_num)); } -void gicd_set_isactiver(unsigned int base, unsigned int id) +void gicd_set_isactiver(uintptr_t base, unsigned int id) { unsigned bit_num = id & ((1 << ISACTIVER_SHIFT) - 1); gicd_write_isactiver(base, id, (1 << bit_num)); } -void gicd_set_icactiver(unsigned int base, unsigned int id) +void gicd_set_icactiver(uintptr_t base, unsigned int id) { unsigned bit_num = id & ((1 << ICACTIVER_SHIFT) - 1); @@ -259,12 +235,8 @@ void gicd_set_icactiver(unsigned int base, unsigned int id) * Make sure that the interrupt's group is set before expecting * this function to do its job correctly. */ -void gicd_set_ipriorityr(unsigned int base, unsigned int id, unsigned int pri) +void gicd_set_ipriorityr(uintptr_t base, unsigned int id, unsigned int pri) { - unsigned int reg = base + GICD_IPRIORITYR + (id & ~3); - unsigned int shift = (id & 3) << 3; - unsigned int reg_val = mmio_read_32(reg); - /* * Enforce ARM recommendation to manage priority values such * that group1 interrupts always have a lower priority than @@ -278,17 +250,12 @@ void gicd_set_ipriorityr(unsigned int base, unsigned int id, unsigned int pri) pri >= GIC_HIGHEST_SEC_PRIORITY && pri <= GIC_LOWEST_SEC_PRIORITY); - reg_val &= ~(GIC_PRI_MASK << shift); - reg_val |= (pri & GIC_PRI_MASK) << shift; - mmio_write_32(reg, reg_val); + mmio_write_8(base + GICD_IPRIORITYR + id, pri & GIC_PRI_MASK); } -void gicd_set_itargetsr(unsigned int base, unsigned int id, unsigned int target) +void gicd_set_itargetsr(uintptr_t base, unsigned int id, unsigned int target) { - unsigned byte_off = id & ((1 << ITARGETSR_SHIFT) - 1); - unsigned int reg_val = gicd_read_itargetsr(base, id); - - gicd_write_itargetsr(base, id, reg_val | (target << (byte_off << 3))); + mmio_write_8(base + GICD_ITARGETSR + id, target & GIC_TARGET_CPU_MASK); } /******************************************************************************* diff --git a/drivers/arm/gic/gic_v3.c b/drivers/arm/gic/gic_v3.c index f4296629..3e802fdd 100644 --- a/drivers/arm/gic/gic_v3.c +++ b/drivers/arm/gic/gic_v3.c @@ -1,31 +1,7 @@ /* * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of ARM nor the names of its contributors may be used - * to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * SPDX-License-Identifier: BSD-3-Clause */ #include <arch.h> @@ -60,8 +36,8 @@ uintptr_t gicv3_get_rdist(uintptr_t gicr_base, uint64_t mpidr) /* Disable this print for now as it appears every time * when using PSCI CPU_SUSPEND. * TODO: Print this only the first time for each CPU. - * INFO("GICv3 - Found RDIST for MPIDR(0x%lx) at 0x%lx\n", - * mpidr, addr); + * INFO("GICv3 - Found RDIST for MPIDR(0x%lx) at %p\n", + * mpidr, (void *) addr); */ return addr; } diff --git a/drivers/arm/gic/v2/gicv2_helpers.c b/drivers/arm/gic/v2/gicv2_helpers.c new file mode 100644 index 00000000..0df50fb0 --- /dev/null +++ b/drivers/arm/gic/v2/gicv2_helpers.c @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch.h> +#include <arch_helpers.h> +#include <assert.h> +#include <debug.h> +#include <gic_common.h> +#include <interrupt_props.h> +#include "../common/gic_common_private.h" +#include "gicv2_private.h" + +/* + * Accessor to read the GIC Distributor ITARGETSR corresponding to the + * interrupt `id`, 4 interrupt IDs at a time. + */ +unsigned int gicd_read_itargetsr(uintptr_t base, unsigned int id) +{ + unsigned n = id >> ITARGETSR_SHIFT; + return mmio_read_32(base + GICD_ITARGETSR + (n << 2)); +} + +/* + * Accessor to read the GIC Distributor CPENDSGIR corresponding to the + * interrupt `id`, 4 interrupt IDs at a time. + */ +unsigned int gicd_read_cpendsgir(uintptr_t base, unsigned int id) +{ + unsigned n = id >> CPENDSGIR_SHIFT; + return mmio_read_32(base + GICD_CPENDSGIR + (n << 2)); +} + +/* + * Accessor to read the GIC Distributor SPENDSGIR corresponding to the + * interrupt `id`, 4 interrupt IDs at a time. + */ +unsigned int gicd_read_spendsgir(uintptr_t base, unsigned int id) +{ + unsigned n = id >> SPENDSGIR_SHIFT; + return mmio_read_32(base + GICD_SPENDSGIR + (n << 2)); +} + +/* + * Accessor to write the GIC Distributor ITARGETSR corresponding to the + * interrupt `id`, 4 interrupt IDs at a time. + */ +void gicd_write_itargetsr(uintptr_t base, unsigned int id, unsigned int val) +{ + unsigned n = id >> ITARGETSR_SHIFT; + mmio_write_32(base + GICD_ITARGETSR + (n << 2), val); +} + +/* + * Accessor to write the GIC Distributor CPENDSGIR corresponding to the + * interrupt `id`, 4 interrupt IDs at a time. + */ +void gicd_write_cpendsgir(uintptr_t base, unsigned int id, unsigned int val) +{ + unsigned n = id >> CPENDSGIR_SHIFT; + mmio_write_32(base + GICD_CPENDSGIR + (n << 2), val); +} + +/* + * Accessor to write the GIC Distributor SPENDSGIR corresponding to the + * interrupt `id`, 4 interrupt IDs at a time. + */ +void gicd_write_spendsgir(uintptr_t base, unsigned int id, unsigned int val) +{ + unsigned n = id >> SPENDSGIR_SHIFT; + mmio_write_32(base + GICD_SPENDSGIR + (n << 2), val); +} + +/******************************************************************************* + * Get the current CPU bit mask from GICD_ITARGETSR0 + ******************************************************************************/ +unsigned int gicv2_get_cpuif_id(uintptr_t base) +{ + unsigned int val; + + val = gicd_read_itargetsr(base, 0); + return val & GIC_TARGET_CPU_MASK; +} + +/******************************************************************************* + * Helper function to configure the default attributes of SPIs. + ******************************************************************************/ +void gicv2_spis_configure_defaults(uintptr_t gicd_base) +{ + unsigned int index, num_ints; + + num_ints = gicd_read_typer(gicd_base); + num_ints &= TYPER_IT_LINES_NO_MASK; + num_ints = (num_ints + 1) << 5; + + /* + * Treat all SPIs as G1NS by default. The number of interrupts is + * calculated as 32 * (IT_LINES + 1). We do 32 at a time. + */ + for (index = MIN_SPI_ID; index < num_ints; index += 32) + gicd_write_igroupr(gicd_base, index, ~0U); + + /* Setup the default SPI priorities doing four at a time */ + for (index = MIN_SPI_ID; index < num_ints; index += 4) + gicd_write_ipriorityr(gicd_base, + index, + GICD_IPRIORITYR_DEF_VAL); + + /* Treat all SPIs as level triggered by default, 16 at a time */ + for (index = MIN_SPI_ID; index < num_ints; index += 16) + gicd_write_icfgr(gicd_base, index, 0); +} + +#if !ERROR_DEPRECATED +/******************************************************************************* + * Helper function to configure secure G0 SPIs. + ******************************************************************************/ +void gicv2_secure_spis_configure(uintptr_t gicd_base, + unsigned int num_ints, + const unsigned int *sec_intr_list) +{ + unsigned int index, irq_num; + + /* If `num_ints` is not 0, ensure that `sec_intr_list` is not NULL */ + assert(num_ints ? (uintptr_t)sec_intr_list : 1); + + for (index = 0; index < num_ints; index++) { + irq_num = sec_intr_list[index]; + if (irq_num >= MIN_SPI_ID) { + /* Configure this interrupt as a secure interrupt */ + gicd_clr_igroupr(gicd_base, irq_num); + + /* Set the priority of this interrupt */ + gicd_set_ipriorityr(gicd_base, + irq_num, + GIC_HIGHEST_SEC_PRIORITY); + + /* Target the secure interrupts to primary CPU */ + gicd_set_itargetsr(gicd_base, irq_num, + gicv2_get_cpuif_id(gicd_base)); + + /* Enable this interrupt */ + gicd_set_isenabler(gicd_base, irq_num); + } + } + +} +#endif + +/******************************************************************************* + * Helper function to configure properties of secure G0 SPIs. + ******************************************************************************/ +void gicv2_secure_spis_configure_props(uintptr_t gicd_base, + const interrupt_prop_t *interrupt_props, + unsigned int interrupt_props_num) +{ + unsigned int i; + const interrupt_prop_t *prop_desc; + + /* Make sure there's a valid property array */ + assert(interrupt_props_num != 0 ? (uintptr_t) interrupt_props : 1); + + for (i = 0; i < interrupt_props_num; i++) { + prop_desc = &interrupt_props[i]; + + if (prop_desc->intr_num < MIN_SPI_ID) + continue; + + /* Configure this interrupt as a secure interrupt */ + assert(prop_desc->intr_grp == GICV2_INTR_GROUP0); + gicd_clr_igroupr(gicd_base, prop_desc->intr_num); + + /* Set the priority of this interrupt */ + gicd_set_ipriorityr(gicd_base, prop_desc->intr_num, + prop_desc->intr_pri); + + /* Target the secure interrupts to primary CPU */ + gicd_set_itargetsr(gicd_base, prop_desc->intr_num, + gicv2_get_cpuif_id(gicd_base)); + + /* Set interrupt configuration */ + gicd_set_icfgr(gicd_base, prop_desc->intr_num, + prop_desc->intr_cfg); + + /* Enable this interrupt */ + gicd_set_isenabler(gicd_base, prop_desc->intr_num); + } +} + +#if !ERROR_DEPRECATED +/******************************************************************************* + * Helper function to configure secure G0 SGIs and PPIs. + ******************************************************************************/ +void gicv2_secure_ppi_sgi_setup(uintptr_t gicd_base, + unsigned int num_ints, + const unsigned int *sec_intr_list) +{ + unsigned int index, irq_num, sec_ppi_sgi_mask = 0; + + /* If `num_ints` is not 0, ensure that `sec_intr_list` is not NULL */ + assert(num_ints ? (uintptr_t)sec_intr_list : 1); + + /* + * Disable all SGIs (imp. def.)/PPIs before configuring them. This is a + * more scalable approach as it avoids clearing the enable bits in the + * GICD_CTLR. + */ + gicd_write_icenabler(gicd_base, 0, ~0); + + /* Setup the default PPI/SGI priorities doing four at a time */ + for (index = 0; index < MIN_SPI_ID; index += 4) + gicd_write_ipriorityr(gicd_base, + index, + GICD_IPRIORITYR_DEF_VAL); + + for (index = 0; index < num_ints; index++) { + irq_num = sec_intr_list[index]; + if (irq_num < MIN_SPI_ID) { + /* We have an SGI or a PPI. They are Group0 at reset */ + sec_ppi_sgi_mask |= 1U << irq_num; + + /* Set the priority of this interrupt */ + gicd_set_ipriorityr(gicd_base, + irq_num, + GIC_HIGHEST_SEC_PRIORITY); + } + } + + /* + * Invert the bitmask to create a mask for non-secure PPIs and + * SGIs. Program the GICD_IGROUPR0 with this bit mask. + */ + gicd_write_igroupr(gicd_base, 0, ~sec_ppi_sgi_mask); + + /* Enable the Group 0 SGIs and PPIs */ + gicd_write_isenabler(gicd_base, 0, sec_ppi_sgi_mask); +} +#endif + +/******************************************************************************* + * Helper function to configure properties of secure G0 SGIs and PPIs. + ******************************************************************************/ +void gicv2_secure_ppi_sgi_setup_props(uintptr_t gicd_base, + const interrupt_prop_t *interrupt_props, + unsigned int interrupt_props_num) +{ + unsigned int i; + uint32_t sec_ppi_sgi_mask = 0; + const interrupt_prop_t *prop_desc; + + /* Make sure there's a valid property array */ + assert(interrupt_props_num != 0 ? (uintptr_t) interrupt_props : 1); + + /* + * Disable all SGIs (imp. def.)/PPIs before configuring them. This is a + * more scalable approach as it avoids clearing the enable bits in the + * GICD_CTLR. + */ + gicd_write_icenabler(gicd_base, 0, ~0); + + /* Setup the default PPI/SGI priorities doing four at a time */ + for (i = 0; i < MIN_SPI_ID; i += 4) + gicd_write_ipriorityr(gicd_base, i, GICD_IPRIORITYR_DEF_VAL); + + for (i = 0; i < interrupt_props_num; i++) { + prop_desc = &interrupt_props[i]; + + if (prop_desc->intr_num >= MIN_SPI_ID) + continue; + + /* Configure this interrupt as a secure interrupt */ + assert(prop_desc->intr_grp == GICV2_INTR_GROUP0); + + /* + * Set interrupt configuration for PPIs. Configuration for SGIs + * are ignored. + */ + if ((prop_desc->intr_num >= MIN_PPI_ID) && + (prop_desc->intr_num < MIN_SPI_ID)) { + gicd_set_icfgr(gicd_base, prop_desc->intr_num, + prop_desc->intr_cfg); + } + + /* We have an SGI or a PPI. They are Group0 at reset */ + sec_ppi_sgi_mask |= (1u << prop_desc->intr_num); + + /* Set the priority of this interrupt */ + gicd_set_ipriorityr(gicd_base, prop_desc->intr_num, + prop_desc->intr_pri); + } + + /* + * Invert the bitmask to create a mask for non-secure PPIs and SGIs. + * Program the GICD_IGROUPR0 with this bit mask. + */ + gicd_write_igroupr(gicd_base, 0, ~sec_ppi_sgi_mask); + + /* Enable the Group 0 SGIs and PPIs */ + gicd_write_isenabler(gicd_base, 0, sec_ppi_sgi_mask); +} diff --git a/drivers/arm/gic/v2/gicv2_main.c b/drivers/arm/gic/v2/gicv2_main.c new file mode 100644 index 00000000..25296a63 --- /dev/null +++ b/drivers/arm/gic/v2/gicv2_main.c @@ -0,0 +1,531 @@ +/* + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch.h> +#include <arch_helpers.h> +#include <assert.h> +#include <debug.h> +#include <gic_common.h> +#include <gicv2.h> +#include <interrupt_props.h> +#include <spinlock.h> +#include "../common/gic_common_private.h" +#include "gicv2_private.h" + +static const gicv2_driver_data_t *driver_data; + +/* + * Spinlock to guard registers needing read-modify-write. APIs protected by this + * spinlock are used either at boot time (when only a single CPU is active), or + * when the system is fully coherent. + */ +spinlock_t gic_lock; + +/******************************************************************************* + * Enable secure interrupts and use FIQs to route them. Disable legacy bypass + * and set the priority mask register to allow all interrupts to trickle in. + ******************************************************************************/ +void gicv2_cpuif_enable(void) +{ + unsigned int val; + + assert(driver_data); + assert(driver_data->gicc_base); + + /* + * Enable the Group 0 interrupts, FIQEn and disable Group 0/1 + * bypass. + */ + val = CTLR_ENABLE_G0_BIT | FIQ_EN_BIT | FIQ_BYP_DIS_GRP0; + val |= IRQ_BYP_DIS_GRP0 | FIQ_BYP_DIS_GRP1 | IRQ_BYP_DIS_GRP1; + + /* Program the idle priority in the PMR */ + gicc_write_pmr(driver_data->gicc_base, GIC_PRI_MASK); + gicc_write_ctlr(driver_data->gicc_base, val); +} + +/******************************************************************************* + * Place the cpu interface in a state where it can never make a cpu exit wfi as + * as result of an asserted interrupt. This is critical for powering down a cpu + ******************************************************************************/ +void gicv2_cpuif_disable(void) +{ + unsigned int val; + + assert(driver_data); + assert(driver_data->gicc_base); + + /* Disable secure, non-secure interrupts and disable their bypass */ + val = gicc_read_ctlr(driver_data->gicc_base); + val &= ~(CTLR_ENABLE_G0_BIT | CTLR_ENABLE_G1_BIT); + val |= FIQ_BYP_DIS_GRP1 | FIQ_BYP_DIS_GRP0; + val |= IRQ_BYP_DIS_GRP0 | IRQ_BYP_DIS_GRP1; + gicc_write_ctlr(driver_data->gicc_base, val); +} + +/******************************************************************************* + * Per cpu gic distributor setup which will be done by all cpus after a cold + * boot/hotplug. This marks out the secure SPIs and PPIs & enables them. + ******************************************************************************/ +void gicv2_pcpu_distif_init(void) +{ + assert(driver_data); + assert(driver_data->gicd_base); + +#if !ERROR_DEPRECATED + if (driver_data->interrupt_props != NULL) { +#endif + gicv2_secure_ppi_sgi_setup_props(driver_data->gicd_base, + driver_data->interrupt_props, + driver_data->interrupt_props_num); +#if !ERROR_DEPRECATED + } else { + assert(driver_data->g0_interrupt_array); + gicv2_secure_ppi_sgi_setup(driver_data->gicd_base, + driver_data->g0_interrupt_num, + driver_data->g0_interrupt_array); + } +#endif +} + +/******************************************************************************* + * Global gic distributor init which will be done by the primary cpu after a + * cold boot. It marks out the secure SPIs, PPIs & SGIs and enables them. It + * then enables the secure GIC distributor interface. + ******************************************************************************/ +void gicv2_distif_init(void) +{ + unsigned int ctlr; + + assert(driver_data); + assert(driver_data->gicd_base); + + /* Disable the distributor before going further */ + ctlr = gicd_read_ctlr(driver_data->gicd_base); + gicd_write_ctlr(driver_data->gicd_base, + ctlr & ~(CTLR_ENABLE_G0_BIT | CTLR_ENABLE_G1_BIT)); + + /* Set the default attribute of all SPIs */ + gicv2_spis_configure_defaults(driver_data->gicd_base); + +#if !ERROR_DEPRECATED + if (driver_data->interrupt_props != NULL) { +#endif + gicv2_secure_spis_configure_props(driver_data->gicd_base, + driver_data->interrupt_props, + driver_data->interrupt_props_num); +#if !ERROR_DEPRECATED + } else { + assert(driver_data->g0_interrupt_array); + + /* Configure the G0 SPIs */ + gicv2_secure_spis_configure(driver_data->gicd_base, + driver_data->g0_interrupt_num, + driver_data->g0_interrupt_array); + } +#endif + + /* Re-enable the secure SPIs now that they have been configured */ + gicd_write_ctlr(driver_data->gicd_base, ctlr | CTLR_ENABLE_G0_BIT); +} + +/******************************************************************************* + * Initialize the ARM GICv2 driver with the provided platform inputs + ******************************************************************************/ +void gicv2_driver_init(const gicv2_driver_data_t *plat_driver_data) +{ + unsigned int gic_version; + assert(plat_driver_data); + assert(plat_driver_data->gicd_base); + assert(plat_driver_data->gicc_base); + +#if !ERROR_DEPRECATED + if (plat_driver_data->interrupt_props == NULL) { + /* Interrupt properties array size must be 0 */ + assert(plat_driver_data->interrupt_props_num == 0); + + /* The platform should provide a list of secure interrupts */ + assert(plat_driver_data->g0_interrupt_array); + + /* + * If there are no interrupts of a particular type, then the + * number of interrupts of that type should be 0 and vice-versa. + */ + assert(plat_driver_data->g0_interrupt_array ? + plat_driver_data->g0_interrupt_num : + plat_driver_data->g0_interrupt_num == 0); + } +#else + assert(plat_driver_data->interrupt_props != NULL); + assert(plat_driver_data->interrupt_props_num > 0); +#endif + + /* Ensure that this is a GICv2 system */ + gic_version = gicd_read_pidr2(plat_driver_data->gicd_base); + gic_version = (gic_version >> PIDR2_ARCH_REV_SHIFT) + & PIDR2_ARCH_REV_MASK; + assert(gic_version == ARCH_REV_GICV2); + + driver_data = plat_driver_data; + + /* + * The GIC driver data is initialized by the primary CPU with caches + * enabled. When the secondary CPU boots up, it initializes the + * GICC/GICR interface with the caches disabled. Hence flush the + * driver_data to ensure coherency. This is not required if the + * platform has HW_ASSISTED_COHERENCY enabled. + */ +#if !HW_ASSISTED_COHERENCY + flush_dcache_range((uintptr_t) &driver_data, sizeof(driver_data)); + flush_dcache_range((uintptr_t) driver_data, sizeof(*driver_data)); +#endif + INFO("ARM GICv2 driver initialized\n"); +} + +/****************************************************************************** + * This function returns whether FIQ is enabled in the GIC CPU interface. + *****************************************************************************/ +unsigned int gicv2_is_fiq_enabled(void) +{ + unsigned int gicc_ctlr; + + assert(driver_data); + assert(driver_data->gicc_base); + + gicc_ctlr = gicc_read_ctlr(driver_data->gicc_base); + return (gicc_ctlr >> FIQ_EN_SHIFT) & 0x1; +} + +/******************************************************************************* + * This function returns the type of the highest priority pending interrupt at + * the GIC cpu interface. The return values can be one of the following : + * PENDING_G1_INTID : The interrupt type is non secure Group 1. + * 0 - 1019 : The interrupt type is secure Group 0. + * GIC_SPURIOUS_INTERRUPT : there is no pending interrupt with + * sufficient priority to be signaled + ******************************************************************************/ +unsigned int gicv2_get_pending_interrupt_type(void) +{ + assert(driver_data); + assert(driver_data->gicc_base); + + return gicc_read_hppir(driver_data->gicc_base) & INT_ID_MASK; +} + +/******************************************************************************* + * This function returns the id of the highest priority pending interrupt at + * the GIC cpu interface. GIC_SPURIOUS_INTERRUPT is returned when there is no + * interrupt pending. + ******************************************************************************/ +unsigned int gicv2_get_pending_interrupt_id(void) +{ + unsigned int id; + + assert(driver_data); + assert(driver_data->gicc_base); + + id = gicc_read_hppir(driver_data->gicc_base) & INT_ID_MASK; + + /* + * Find out which non-secure interrupt it is under the assumption that + * the GICC_CTLR.AckCtl bit is 0. + */ + if (id == PENDING_G1_INTID) + id = gicc_read_ahppir(driver_data->gicc_base) & INT_ID_MASK; + + return id; +} + +/******************************************************************************* + * This functions reads the GIC cpu interface Interrupt Acknowledge register + * to start handling the pending secure 0 interrupt. It returns the + * contents of the IAR. + ******************************************************************************/ +unsigned int gicv2_acknowledge_interrupt(void) +{ + assert(driver_data); + assert(driver_data->gicc_base); + + return gicc_read_IAR(driver_data->gicc_base); +} + +/******************************************************************************* + * This functions writes the GIC cpu interface End Of Interrupt register with + * the passed value to finish handling the active secure group 0 interrupt. + ******************************************************************************/ +void gicv2_end_of_interrupt(unsigned int id) +{ + assert(driver_data); + assert(driver_data->gicc_base); + + gicc_write_EOIR(driver_data->gicc_base, id); +} + +/******************************************************************************* + * This function returns the type of the interrupt id depending upon the group + * this interrupt has been configured under by the interrupt controller i.e. + * group0 secure or group1 non secure. It returns zero for Group 0 secure and + * one for Group 1 non secure interrupt. + ******************************************************************************/ +unsigned int gicv2_get_interrupt_group(unsigned int id) +{ + assert(driver_data); + assert(driver_data->gicd_base); + + return gicd_get_igroupr(driver_data->gicd_base, id); +} + +/******************************************************************************* + * This function returns the priority of the interrupt the processor is + * currently servicing. + ******************************************************************************/ +unsigned int gicv2_get_running_priority(void) +{ + assert(driver_data); + assert(driver_data->gicc_base); + + return gicc_read_rpr(driver_data->gicc_base); +} + +/******************************************************************************* + * This function sets the GICv2 target mask pattern for the current PE. The PE + * target mask is used to translate linear PE index (returned by platform core + * position) to a bit mask used when targeting interrupts to a PE, viz. when + * raising SGIs and routing SPIs. + ******************************************************************************/ +void gicv2_set_pe_target_mask(unsigned int proc_num) +{ + assert(driver_data); + assert(driver_data->gicd_base); + assert(driver_data->target_masks); + assert(proc_num < GICV2_MAX_TARGET_PE); + assert(proc_num < driver_data->target_masks_num); + + /* Return if the target mask is already populated */ + if (driver_data->target_masks[proc_num]) + return; + + /* Read target register corresponding to this CPU */ + driver_data->target_masks[proc_num] = + gicv2_get_cpuif_id(driver_data->gicd_base); +} + +/******************************************************************************* + * This function returns the active status of the interrupt (either because the + * state is active, or active and pending). + ******************************************************************************/ +unsigned int gicv2_get_interrupt_active(unsigned int id) +{ + assert(driver_data); + assert(driver_data->gicd_base); + assert(id <= MAX_SPI_ID); + + return gicd_get_isactiver(driver_data->gicd_base, id); +} + +/******************************************************************************* + * This function enables the interrupt identified by id. + ******************************************************************************/ +void gicv2_enable_interrupt(unsigned int id) +{ + assert(driver_data); + assert(driver_data->gicd_base); + assert(id <= MAX_SPI_ID); + + /* + * Ensure that any shared variable updates depending on out of band + * interrupt trigger are observed before enabling interrupt. + */ + dsbishst(); + gicd_set_isenabler(driver_data->gicd_base, id); +} + +/******************************************************************************* + * This function disables the interrupt identified by id. + ******************************************************************************/ +void gicv2_disable_interrupt(unsigned int id) +{ + assert(driver_data); + assert(driver_data->gicd_base); + assert(id <= MAX_SPI_ID); + + /* + * Disable interrupt, and ensure that any shared variable updates + * depending on out of band interrupt trigger are observed afterwards. + */ + gicd_set_icenabler(driver_data->gicd_base, id); + dsbishst(); +} + +/******************************************************************************* + * This function sets the interrupt priority as supplied for the given interrupt + * id. + ******************************************************************************/ +void gicv2_set_interrupt_priority(unsigned int id, unsigned int priority) +{ + assert(driver_data); + assert(driver_data->gicd_base); + assert(id <= MAX_SPI_ID); + + gicd_set_ipriorityr(driver_data->gicd_base, id, priority); +} + +/******************************************************************************* + * This function assigns group for the interrupt identified by id. The group can + * be any of GICV2_INTR_GROUP* + ******************************************************************************/ +void gicv2_set_interrupt_type(unsigned int id, unsigned int type) +{ + assert(driver_data); + assert(driver_data->gicd_base); + assert(id <= MAX_SPI_ID); + + /* Serialize read-modify-write to Distributor registers */ + spin_lock(&gic_lock); + switch (type) { + case GICV2_INTR_GROUP1: + gicd_set_igroupr(driver_data->gicd_base, id); + break; + case GICV2_INTR_GROUP0: + gicd_clr_igroupr(driver_data->gicd_base, id); + break; + default: + assert(0); + } + spin_unlock(&gic_lock); +} + +/******************************************************************************* + * This function raises the specified SGI to requested targets. + * + * The proc_num parameter must be the linear index of the target PE in the + * system. + ******************************************************************************/ +void gicv2_raise_sgi(int sgi_num, int proc_num) +{ + unsigned int sgir_val, target; + + assert(driver_data); + assert(proc_num < GICV2_MAX_TARGET_PE); + assert(driver_data->gicd_base); + + /* + * Target masks array must have been supplied, and the core position + * should be valid. + */ + assert(driver_data->target_masks); + assert(proc_num < driver_data->target_masks_num); + + /* Don't raise SGI if the mask hasn't been populated */ + target = driver_data->target_masks[proc_num]; + assert(target != 0); + + sgir_val = GICV2_SGIR_VALUE(SGIR_TGT_SPECIFIC, target, sgi_num); + + /* + * Ensure that any shared variable updates depending on out of band + * interrupt trigger are observed before raising SGI. + */ + dsbishst(); + gicd_write_sgir(driver_data->gicd_base, sgir_val); +} + +/******************************************************************************* + * This function sets the interrupt routing for the given SPI interrupt id. + * The interrupt routing is specified in routing mode. The proc_num parameter is + * linear index of the PE to target SPI. When proc_num < 0, the SPI may target + * all PEs. + ******************************************************************************/ +void gicv2_set_spi_routing(unsigned int id, int proc_num) +{ + int target; + + assert(driver_data); + assert(driver_data->gicd_base); + + assert(id >= MIN_SPI_ID && id <= MAX_SPI_ID); + + /* + * Target masks array must have been supplied, and the core position + * should be valid. + */ + assert(driver_data->target_masks); + assert(proc_num < GICV2_MAX_TARGET_PE); + assert(proc_num < driver_data->target_masks_num); + + if (proc_num < 0) { + /* Target all PEs */ + target = GIC_TARGET_CPU_MASK; + } else { + /* Don't route interrupt if the mask hasn't been populated */ + target = driver_data->target_masks[proc_num]; + assert(target != 0); + } + + gicd_set_itargetsr(driver_data->gicd_base, id, target); +} + +/******************************************************************************* + * This function clears the pending status of an interrupt identified by id. + ******************************************************************************/ +void gicv2_clear_interrupt_pending(unsigned int id) +{ + assert(driver_data); + assert(driver_data->gicd_base); + + /* SGIs can't be cleared pending */ + assert(id >= MIN_PPI_ID); + + /* + * Clear pending interrupt, and ensure that any shared variable updates + * depending on out of band interrupt trigger are observed afterwards. + */ + gicd_set_icpendr(driver_data->gicd_base, id); + dsbishst(); +} + +/******************************************************************************* + * This function sets the pending status of an interrupt identified by id. + ******************************************************************************/ +void gicv2_set_interrupt_pending(unsigned int id) +{ + assert(driver_data); + assert(driver_data->gicd_base); + + /* SGIs can't be cleared pending */ + assert(id >= MIN_PPI_ID); + + /* + * Ensure that any shared variable updates depending on out of band + * interrupt trigger are observed before setting interrupt pending. + */ + dsbishst(); + gicd_set_ispendr(driver_data->gicd_base, id); +} + +/******************************************************************************* + * This function sets the PMR register with the supplied value. Returns the + * original PMR. + ******************************************************************************/ +unsigned int gicv2_set_pmr(unsigned int mask) +{ + unsigned int old_mask; + + assert(driver_data); + assert(driver_data->gicc_base); + + old_mask = gicc_read_pmr(driver_data->gicc_base); + + /* + * Order memory updates w.r.t. PMR write, and ensure they're visible + * before potential out of band interrupt trigger because of PMR update. + */ + dmbishst(); + gicc_write_pmr(driver_data->gicc_base, mask); + dsbishst(); + + return old_mask; +} diff --git a/drivers/arm/gic/v2/gicv2_private.h b/drivers/arm/gic/v2/gicv2_private.h new file mode 100644 index 00000000..25600deb --- /dev/null +++ b/drivers/arm/gic/v2/gicv2_private.h @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __GICV2_PRIVATE_H__ +#define __GICV2_PRIVATE_H__ + +#include <gicv2.h> +#include <mmio.h> +#include <stdint.h> + +/******************************************************************************* + * Private function prototypes + ******************************************************************************/ +void gicv2_spis_configure_defaults(uintptr_t gicd_base); +#if !ERROR_DEPRECATED +void gicv2_secure_spis_configure(uintptr_t gicd_base, + unsigned int num_ints, + const unsigned int *sec_intr_list); +void gicv2_secure_ppi_sgi_setup(uintptr_t gicd_base, + unsigned int num_ints, + const unsigned int *sec_intr_list); +#endif +void gicv2_secure_spis_configure_props(uintptr_t gicd_base, + const interrupt_prop_t *interrupt_props, + unsigned int interrupt_props_num); +void gicv2_secure_ppi_sgi_setup_props(uintptr_t gicd_base, + const interrupt_prop_t *interrupt_props, + unsigned int interrupt_props_num); +unsigned int gicv2_get_cpuif_id(uintptr_t base); + +/******************************************************************************* + * GIC Distributor interface accessors for reading entire registers + ******************************************************************************/ +static inline unsigned int gicd_read_pidr2(uintptr_t base) +{ + return mmio_read_32(base + GICD_PIDR2_GICV2); +} + +/******************************************************************************* + * GIC Distributor interface accessors for writing entire registers + ******************************************************************************/ +static inline unsigned int gicd_get_itargetsr(uintptr_t base, unsigned int id) +{ + return mmio_read_8(base + GICD_ITARGETSR + id); +} + +static inline void gicd_set_itargetsr(uintptr_t base, unsigned int id, + unsigned int target) +{ + mmio_write_8(base + GICD_ITARGETSR + id, target & GIC_TARGET_CPU_MASK); +} + +static inline void gicd_write_sgir(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICD_SGIR, val); +} + +/******************************************************************************* + * GIC CPU interface accessors for reading entire registers + ******************************************************************************/ + +static inline unsigned int gicc_read_ctlr(uintptr_t base) +{ + return mmio_read_32(base + GICC_CTLR); +} + +static inline unsigned int gicc_read_pmr(uintptr_t base) +{ + return mmio_read_32(base + GICC_PMR); +} + +static inline unsigned int gicc_read_BPR(uintptr_t base) +{ + return mmio_read_32(base + GICC_BPR); +} + +static inline unsigned int gicc_read_IAR(uintptr_t base) +{ + return mmio_read_32(base + GICC_IAR); +} + +static inline unsigned int gicc_read_EOIR(uintptr_t base) +{ + return mmio_read_32(base + GICC_EOIR); +} + +static inline unsigned int gicc_read_hppir(uintptr_t base) +{ + return mmio_read_32(base + GICC_HPPIR); +} + +static inline unsigned int gicc_read_ahppir(uintptr_t base) +{ + return mmio_read_32(base + GICC_AHPPIR); +} + +static inline unsigned int gicc_read_dir(uintptr_t base) +{ + return mmio_read_32(base + GICC_DIR); +} + +static inline unsigned int gicc_read_iidr(uintptr_t base) +{ + return mmio_read_32(base + GICC_IIDR); +} + +static inline unsigned int gicc_read_rpr(uintptr_t base) +{ + return mmio_read_32(base + GICC_RPR); +} + +/******************************************************************************* + * GIC CPU interface accessors for writing entire registers + ******************************************************************************/ + +static inline void gicc_write_ctlr(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICC_CTLR, val); +} + +static inline void gicc_write_pmr(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICC_PMR, val); +} + +static inline void gicc_write_BPR(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICC_BPR, val); +} + + +static inline void gicc_write_IAR(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICC_IAR, val); +} + +static inline void gicc_write_EOIR(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICC_EOIR, val); +} + +static inline void gicc_write_hppir(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICC_HPPIR, val); +} + +static inline void gicc_write_dir(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICC_DIR, val); +} + +#endif /* __GICV2_PRIVATE_H__ */ diff --git a/drivers/arm/gic/v3/arm_gicv3_common.c b/drivers/arm/gic/v3/arm_gicv3_common.c new file mode 100644 index 00000000..8d552ca5 --- /dev/null +++ b/drivers/arm/gic/v3/arm_gicv3_common.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Driver for implementation defined features that are identical in ARM GICv3 +* implementations (GIC-500 and GIC-600 for now). This driver only overrides +* APIs that are different to those generic ones in GICv3 driver. + */ + +#include <arch_helpers.h> +#include <assert.h> +#include <gicv3.h> + +#include "gicv3_private.h" +#include "arm_gicv3_common.h" + +/* + * Flush the internal GIC cache of the LPIs pending tables to memory before + * saving the state of the Redistributor. This is required before powering off + * the GIC when the pending status must be preserved. + * `rdist_proc_num` is the processor number corresponding to the Redistributor of the + * current CPU. + */ +void arm_gicv3_distif_pre_save(unsigned int rdist_proc_num) +{ + uintptr_t gicr_base = 0; + + assert(gicv3_driver_data); + assert(gicv3_driver_data->rdistif_base_addrs); + + /* + * The GICR_WAKER.Sleep bit should be set only when both + * GICR_WAKER.ChildrenAsleep and GICR_WAKER.ProcessorSleep are set on + * all the Redistributors. + */ + for (unsigned int i = 0; i < gicv3_driver_data->rdistif_num; i++) { + gicr_base = gicv3_driver_data->rdistif_base_addrs[i]; + assert(gicr_base); + assert(gicr_read_waker(gicr_base) & WAKER_CA_BIT); + assert(gicr_read_waker(gicr_base) & WAKER_PS_BIT); + } + + gicr_base = gicv3_driver_data->rdistif_base_addrs[rdist_proc_num]; + /* + * According to the TRM, there is only one instance of the + * GICR_WAKER.Sleep and GICR_WAKER.Quiescent bits that can be accessed + * through any of the Redistributor. + */ + + /* + * Set GICR_WAKER.Sleep + * After this point, the system must be configured so that the + * wake_request signals for the right cores are asserted when a wakeup + * interrupt is detected. The GIC will not be able to do that anymore + * when the GICR_WAKER.Sleep bit is set to 1. + */ + gicr_write_waker(gicr_base, gicr_read_waker(gicr_base) | WAKER_SL_BIT); + + /* Wait until the GICR_WAKER.Quiescent bit is set */ + while (!(gicr_read_waker(gicr_base) & WAKER_QSC_BIT)) + ; +} + +/* + * Allow the LPIs pending state to be read back from the tables in memory after + * having restored the state of the GIC Redistributor. + */ +void arm_gicv3_distif_post_restore(unsigned int rdist_proc_num) +{ + uintptr_t gicr_base; + + assert(gicv3_driver_data); + assert(gicv3_driver_data->rdistif_base_addrs); + + /* + * According to the TRM, there is only one instance of the + * GICR_WAKER.Sleep and GICR_WAKER.Quiescent bits that can be accessed + * through any of the Redistributor. + */ + gicr_base = gicv3_driver_data->rdistif_base_addrs[rdist_proc_num]; + assert(gicr_base); + + /* + * Writes to GICR_WAKER.Sleep bit are ignored if GICR_WAKER.Quiescent + * bit is not set. We should be alright on power on path, therefore + * coming out of sleep and Quiescent should be set, but we assert in + * case. + */ + assert(gicr_read_waker(gicr_base) & WAKER_QSC_BIT); + + /* Clear GICR_WAKER.Sleep */ + gicr_write_waker(gicr_base, gicr_read_waker(gicr_base) & ~WAKER_SL_BIT); + + /* + * We don't know if the effects of setting GICR_WAKER.Sleep bit is + * instantaneous, so we wait until the interface is not Quiescent + * anymore. + */ + while (gicr_read_waker(gicr_base) & WAKER_QSC_BIT) + ; +} + diff --git a/drivers/arm/gic/v3/gic500.c b/drivers/arm/gic/v3/gic500.c new file mode 100644 index 00000000..f03e33f8 --- /dev/null +++ b/drivers/arm/gic/v3/gic500.c @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Driver for GIC500-specific features. This driver only overrides APIs that are + * different to those generic ones in GICv3 driver. + */ +#include "gicv3_private.h" + +void gicv3_distif_pre_save(unsigned int proc_num) +{ + arm_gicv3_distif_pre_save(proc_num); +} + +void gicv3_distif_post_restore(unsigned int proc_num) +{ + arm_gicv3_distif_post_restore(proc_num); +} + diff --git a/drivers/arm/gic/v3/gic600.c b/drivers/arm/gic/v3/gic600.c new file mode 100644 index 00000000..eb4fc548 --- /dev/null +++ b/drivers/arm/gic/v3/gic600.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * Driver for GIC600-specific features. This driver only overrides APIs that are + * different to those generic ones in GICv3 driver. + * + * GIC600 supports independently power-gating redistributor interface. + */ + +#include <arch_helpers.h> +#include <assert.h> +#include <gicv3.h> + +#include "gicv3_private.h" + +/* GIC600-specific register offsets */ +#define GICR_PWRR 0x24 + +/* GICR_PWRR fields */ +#define PWRR_RDPD_SHIFT 0 +#define PWRR_RDGPD_SHIFT 2 +#define PWRR_RDGPO_SHIFT 3 + +#define PWRR_RDGPD (1 << PWRR_RDGPD_SHIFT) +#define PWRR_RDGPO (1 << PWRR_RDGPO_SHIFT) + +/* Values to write to GICR_PWRR register to power redistributor */ +#define PWRR_ON (0 << PWRR_RDPD_SHIFT) +#define PWRR_OFF (1 << PWRR_RDPD_SHIFT) + +/* GIC600-specific accessor functions */ +static void gicr_write_pwrr(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICR_PWRR, val); +} + +static uint32_t gicr_read_pwrr(uintptr_t base) +{ + return mmio_read_32(base + GICR_PWRR); +} + +static int gicr_group_powering_down(uint32_t pwrr) +{ + /* + * Whether the redistributor group power down operation is in transit: + * i.e. it's intending to, but not finished yet. + */ + return ((pwrr & PWRR_RDGPD) && !(pwrr & PWRR_RDGPO)); +} + +static void gic600_pwr_on(uintptr_t base) +{ + /* Power on redistributor */ + gicr_write_pwrr(base, PWRR_ON); + + /* Wait until the power on state is reflected */ + while (gicr_read_pwrr(base) & PWRR_RDGPO) + ; +} + +static void gic600_pwr_off(uintptr_t base) +{ + /* Power off redistributor */ + gicr_write_pwrr(base, PWRR_OFF); + + /* + * If this is the last man, turning this redistributor frame off will + * result in the group itself being powered off. In that case, wait as + * long as it's in transition, or has aborted the transition altogether + * for any reason. + */ + if (gicr_read_pwrr(base) & PWRR_RDGPD) { + while (gicr_group_powering_down(gicr_read_pwrr(base))) + ; + } +} + +void gicv3_distif_pre_save(unsigned int proc_num) +{ + arm_gicv3_distif_pre_save(proc_num); +} + +void gicv3_distif_post_restore(unsigned int proc_num) +{ + arm_gicv3_distif_post_restore(proc_num); +} + +/* + * Power off GIC600 redistributor + */ +void gicv3_rdistif_off(unsigned int proc_num) +{ + uintptr_t gicr_base; + + assert(gicv3_driver_data); + assert(proc_num < gicv3_driver_data->rdistif_num); + assert(gicv3_driver_data->rdistif_base_addrs); + + gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num]; + assert(gicr_base); + + /* Attempt to power redistributor off */ + gic600_pwr_off(gicr_base); +} + +/* + * Power on GIC600 redistributor + */ +void gicv3_rdistif_on(unsigned int proc_num) +{ + uintptr_t gicr_base; + + assert(gicv3_driver_data); + assert(proc_num < gicv3_driver_data->rdistif_num); + assert(gicv3_driver_data->rdistif_base_addrs); + + gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num]; + assert(gicr_base); + + /* Power redistributor on */ + gic600_pwr_on(gicr_base); +} diff --git a/drivers/arm/gic/v3/gicv3_helpers.c b/drivers/arm/gic/v3/gicv3_helpers.c new file mode 100644 index 00000000..25226956 --- /dev/null +++ b/drivers/arm/gic/v3/gicv3_helpers.c @@ -0,0 +1,589 @@ +/* + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch.h> +#include <arch_helpers.h> +#include <assert.h> +#include <debug.h> +#include <gic_common.h> +#include <interrupt_props.h> +#include "../common/gic_common_private.h" +#include "gicv3_private.h" + +/* + * Accessor to read the GIC Distributor IGRPMODR corresponding to the + * interrupt `id`, 32 interrupt IDs at a time. + */ +unsigned int gicd_read_igrpmodr(uintptr_t base, unsigned int id) +{ + unsigned n = id >> IGRPMODR_SHIFT; + return mmio_read_32(base + GICD_IGRPMODR + (n << 2)); +} + +/* + * Accessor to write the GIC Distributor IGRPMODR corresponding to the + * interrupt `id`, 32 interrupt IDs at a time. + */ +void gicd_write_igrpmodr(uintptr_t base, unsigned int id, unsigned int val) +{ + unsigned n = id >> IGRPMODR_SHIFT; + mmio_write_32(base + GICD_IGRPMODR + (n << 2), val); +} + +/* + * Accessor to get the bit corresponding to interrupt ID + * in GIC Distributor IGRPMODR. + */ +unsigned int gicd_get_igrpmodr(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << IGRPMODR_SHIFT) - 1); + unsigned int reg_val = gicd_read_igrpmodr(base, id); + + return (reg_val >> bit_num) & 0x1; +} + +/* + * Accessor to set the bit corresponding to interrupt ID + * in GIC Distributor IGRPMODR. + */ +void gicd_set_igrpmodr(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << IGRPMODR_SHIFT) - 1); + unsigned int reg_val = gicd_read_igrpmodr(base, id); + + gicd_write_igrpmodr(base, id, reg_val | (1 << bit_num)); +} + +/* + * Accessor to clear the bit corresponding to interrupt ID + * in GIC Distributor IGRPMODR. + */ +void gicd_clr_igrpmodr(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << IGRPMODR_SHIFT) - 1); + unsigned int reg_val = gicd_read_igrpmodr(base, id); + + gicd_write_igrpmodr(base, id, reg_val & ~(1 << bit_num)); +} + +/* + * Accessor to read the GIC Re-distributor IPRIORITYR corresponding to the + * interrupt `id`, 4 interrupts IDs at a time. + */ +unsigned int gicr_read_ipriorityr(uintptr_t base, unsigned int id) +{ + unsigned n = id >> IPRIORITYR_SHIFT; + return mmio_read_32(base + GICR_IPRIORITYR + (n << 2)); +} + +/* + * Accessor to write the GIC Re-distributor IPRIORITYR corresponding to the + * interrupt `id`, 4 interrupts IDs at a time. + */ +void gicr_write_ipriorityr(uintptr_t base, unsigned int id, unsigned int val) +{ + unsigned n = id >> IPRIORITYR_SHIFT; + mmio_write_32(base + GICR_IPRIORITYR + (n << 2), val); +} + +/* + * Accessor to get the bit corresponding to interrupt ID + * from GIC Re-distributor IGROUPR0. + */ +unsigned int gicr_get_igroupr0(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << IGROUPR_SHIFT) - 1); + unsigned int reg_val = gicr_read_igroupr0(base); + + return (reg_val >> bit_num) & 0x1; +} + +/* + * Accessor to set the bit corresponding to interrupt ID + * in GIC Re-distributor IGROUPR0. + */ +void gicr_set_igroupr0(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << IGROUPR_SHIFT) - 1); + unsigned int reg_val = gicr_read_igroupr0(base); + + gicr_write_igroupr0(base, reg_val | (1 << bit_num)); +} + +/* + * Accessor to clear the bit corresponding to interrupt ID + * in GIC Re-distributor IGROUPR0. + */ +void gicr_clr_igroupr0(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << IGROUPR_SHIFT) - 1); + unsigned int reg_val = gicr_read_igroupr0(base); + + gicr_write_igroupr0(base, reg_val & ~(1 << bit_num)); +} + +/* + * Accessor to get the bit corresponding to interrupt ID + * from GIC Re-distributor IGRPMODR0. + */ +unsigned int gicr_get_igrpmodr0(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << IGRPMODR_SHIFT) - 1); + unsigned int reg_val = gicr_read_igrpmodr0(base); + + return (reg_val >> bit_num) & 0x1; +} + +/* + * Accessor to set the bit corresponding to interrupt ID + * in GIC Re-distributor IGRPMODR0. + */ +void gicr_set_igrpmodr0(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << IGRPMODR_SHIFT) - 1); + unsigned int reg_val = gicr_read_igrpmodr0(base); + + gicr_write_igrpmodr0(base, reg_val | (1 << bit_num)); +} + +/* + * Accessor to clear the bit corresponding to interrupt ID + * in GIC Re-distributor IGRPMODR0. + */ +void gicr_clr_igrpmodr0(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << IGRPMODR_SHIFT) - 1); + unsigned int reg_val = gicr_read_igrpmodr0(base); + + gicr_write_igrpmodr0(base, reg_val & ~(1 << bit_num)); +} + +/* + * Accessor to set the bit corresponding to interrupt ID + * in GIC Re-distributor ISENABLER0. + */ +void gicr_set_isenabler0(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << ISENABLER_SHIFT) - 1); + + gicr_write_isenabler0(base, (1 << bit_num)); +} + +/* + * Accessor to set the bit corresponding to interrupt ID in GIC Re-distributor + * ICENABLER0. + */ +void gicr_set_icenabler0(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << ICENABLER_SHIFT) - 1); + + gicr_write_icenabler0(base, (1 << bit_num)); +} + +/* + * Accessor to set the bit corresponding to interrupt ID in GIC Re-distributor + * ISACTIVER0. + */ +unsigned int gicr_get_isactiver0(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << ISACTIVER_SHIFT) - 1); + unsigned int reg_val = gicr_read_isactiver0(base); + + return (reg_val >> bit_num) & 0x1; +} + +/* + * Accessor to clear the bit corresponding to interrupt ID in GIC Re-distributor + * ICPENDRR0. + */ +void gicr_set_icpendr0(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << ICPENDR_SHIFT) - 1); + + gicr_write_icpendr0(base, (1 << bit_num)); +} + +/* + * Accessor to set the bit corresponding to interrupt ID in GIC Re-distributor + * ISPENDR0. + */ +void gicr_set_ispendr0(uintptr_t base, unsigned int id) +{ + unsigned bit_num = id & ((1 << ISPENDR_SHIFT) - 1); + + gicr_write_ispendr0(base, (1 << bit_num)); +} + +/* + * Accessor to set the byte corresponding to interrupt ID + * in GIC Re-distributor IPRIORITYR. + */ +void gicr_set_ipriorityr(uintptr_t base, unsigned int id, unsigned int pri) +{ + mmio_write_8(base + GICR_IPRIORITYR + id, pri & GIC_PRI_MASK); +} + +/* + * Accessor to set the bit fields corresponding to interrupt ID + * in GIC Re-distributor ICFGR0. + */ +void gicr_set_icfgr0(uintptr_t base, unsigned int id, unsigned int cfg) +{ + unsigned bit_num = id & ((1 << ICFGR_SHIFT) - 1); + uint32_t reg_val = gicr_read_icfgr0(base); + + /* Clear the field, and insert required configuration */ + reg_val &= ~(GIC_CFG_MASK << bit_num); + reg_val |= ((cfg & GIC_CFG_MASK) << bit_num); + + gicr_write_icfgr0(base, reg_val); +} + +/* + * Accessor to set the bit fields corresponding to interrupt ID + * in GIC Re-distributor ICFGR1. + */ +void gicr_set_icfgr1(uintptr_t base, unsigned int id, unsigned int cfg) +{ + unsigned bit_num = id & ((1 << ICFGR_SHIFT) - 1); + uint32_t reg_val = gicr_read_icfgr1(base); + + /* Clear the field, and insert required configuration */ + reg_val &= ~(GIC_CFG_MASK << bit_num); + reg_val |= ((cfg & GIC_CFG_MASK) << bit_num); + + gicr_write_icfgr1(base, reg_val); +} + +/****************************************************************************** + * This function marks the core as awake in the re-distributor and + * ensures that the interface is active. + *****************************************************************************/ +void gicv3_rdistif_mark_core_awake(uintptr_t gicr_base) +{ + /* + * The WAKER_PS_BIT should be changed to 0 + * only when WAKER_CA_BIT is 1. + */ + assert(gicr_read_waker(gicr_base) & WAKER_CA_BIT); + + /* Mark the connected core as awake */ + gicr_write_waker(gicr_base, gicr_read_waker(gicr_base) & ~WAKER_PS_BIT); + + /* Wait till the WAKER_CA_BIT changes to 0 */ + while (gicr_read_waker(gicr_base) & WAKER_CA_BIT) + ; +} + + +/****************************************************************************** + * This function marks the core as asleep in the re-distributor and ensures + * that the interface is quiescent. + *****************************************************************************/ +void gicv3_rdistif_mark_core_asleep(uintptr_t gicr_base) +{ + /* Mark the connected core as asleep */ + gicr_write_waker(gicr_base, gicr_read_waker(gicr_base) | WAKER_PS_BIT); + + /* Wait till the WAKER_CA_BIT changes to 1 */ + while (!(gicr_read_waker(gicr_base) & WAKER_CA_BIT)) + ; +} + + +/******************************************************************************* + * This function probes the Redistributor frames when the driver is initialised + * and saves their base addresses. These base addresses are used later to + * initialise each Redistributor interface. + ******************************************************************************/ +void gicv3_rdistif_base_addrs_probe(uintptr_t *rdistif_base_addrs, + unsigned int rdistif_num, + uintptr_t gicr_base, + mpidr_hash_fn mpidr_to_core_pos) +{ + u_register_t mpidr; + unsigned int proc_num; + unsigned long long typer_val; + uintptr_t rdistif_base = gicr_base; + + assert(rdistif_base_addrs); + + /* + * Iterate over the Redistributor frames. Store the base address of each + * frame in the platform provided array. Use the "Processor Number" + * field to index into the array if the platform has not provided a hash + * function to convert an MPIDR (obtained from the "Affinity Value" + * field into a linear index. + */ + do { + typer_val = gicr_read_typer(rdistif_base); + if (mpidr_to_core_pos) { + mpidr = mpidr_from_gicr_typer(typer_val); + proc_num = mpidr_to_core_pos(mpidr); + } else { + proc_num = (typer_val >> TYPER_PROC_NUM_SHIFT) & + TYPER_PROC_NUM_MASK; + } + assert(proc_num < rdistif_num); + rdistif_base_addrs[proc_num] = rdistif_base; + rdistif_base += (1 << GICR_PCPUBASE_SHIFT); + } while (!(typer_val & TYPER_LAST_BIT)); +} + +/******************************************************************************* + * Helper function to configure the default attributes of SPIs. + ******************************************************************************/ +void gicv3_spis_configure_defaults(uintptr_t gicd_base) +{ + unsigned int index, num_ints; + + num_ints = gicd_read_typer(gicd_base); + num_ints &= TYPER_IT_LINES_NO_MASK; + num_ints = (num_ints + 1) << 5; + + /* + * Treat all SPIs as G1NS by default. The number of interrupts is + * calculated as 32 * (IT_LINES + 1). We do 32 at a time. + */ + for (index = MIN_SPI_ID; index < num_ints; index += 32) + gicd_write_igroupr(gicd_base, index, ~0U); + + /* Setup the default SPI priorities doing four at a time */ + for (index = MIN_SPI_ID; index < num_ints; index += 4) + gicd_write_ipriorityr(gicd_base, + index, + GICD_IPRIORITYR_DEF_VAL); + + /* + * Treat all SPIs as level triggered by default, write 16 at + * a time + */ + for (index = MIN_SPI_ID; index < num_ints; index += 16) + gicd_write_icfgr(gicd_base, index, 0); +} + +#if !ERROR_DEPRECATED +/******************************************************************************* + * Helper function to configure secure G0 and G1S SPIs. + ******************************************************************************/ +void gicv3_secure_spis_configure(uintptr_t gicd_base, + unsigned int num_ints, + const unsigned int *sec_intr_list, + unsigned int int_grp) +{ + unsigned int index, irq_num; + unsigned long long gic_affinity_val; + + assert((int_grp == INTR_GROUP1S) || (int_grp == INTR_GROUP0)); + /* If `num_ints` is not 0, ensure that `sec_intr_list` is not NULL */ + assert(num_ints ? (uintptr_t)sec_intr_list : 1); + + for (index = 0; index < num_ints; index++) { + irq_num = sec_intr_list[index]; + if (irq_num >= MIN_SPI_ID) { + + /* Configure this interrupt as a secure interrupt */ + gicd_clr_igroupr(gicd_base, irq_num); + + /* Configure this interrupt as G0 or a G1S interrupt */ + if (int_grp == INTR_GROUP1S) + gicd_set_igrpmodr(gicd_base, irq_num); + else + gicd_clr_igrpmodr(gicd_base, irq_num); + + /* Set the priority of this interrupt */ + gicd_set_ipriorityr(gicd_base, + irq_num, + GIC_HIGHEST_SEC_PRIORITY); + + /* Target SPIs to the primary CPU */ + gic_affinity_val = + gicd_irouter_val_from_mpidr(read_mpidr(), 0); + gicd_write_irouter(gicd_base, + irq_num, + gic_affinity_val); + + /* Enable this interrupt */ + gicd_set_isenabler(gicd_base, irq_num); + } + } + +} +#endif + +/******************************************************************************* + * Helper function to configure properties of secure SPIs + ******************************************************************************/ +unsigned int gicv3_secure_spis_configure_props(uintptr_t gicd_base, + const interrupt_prop_t *interrupt_props, + unsigned int interrupt_props_num) +{ + unsigned int i; + const interrupt_prop_t *current_prop; + unsigned long long gic_affinity_val; + unsigned int ctlr_enable = 0; + + /* Make sure there's a valid property array */ + assert(interrupt_props != NULL); + assert(interrupt_props_num > 0); + + for (i = 0; i < interrupt_props_num; i++) { + current_prop = &interrupt_props[i]; + + if (current_prop->intr_num < MIN_SPI_ID) + continue; + + /* Configure this interrupt as a secure interrupt */ + gicd_clr_igroupr(gicd_base, current_prop->intr_num); + + /* Configure this interrupt as G0 or a G1S interrupt */ + assert((current_prop->intr_grp == INTR_GROUP0) || + (current_prop->intr_grp == INTR_GROUP1S)); + if (current_prop->intr_grp == INTR_GROUP1S) { + gicd_set_igrpmodr(gicd_base, current_prop->intr_num); + ctlr_enable |= CTLR_ENABLE_G1S_BIT; + } else { + gicd_clr_igrpmodr(gicd_base, current_prop->intr_num); + ctlr_enable |= CTLR_ENABLE_G0_BIT; + } + + /* Set interrupt configuration */ + gicd_set_icfgr(gicd_base, current_prop->intr_num, + current_prop->intr_cfg); + + /* Set the priority of this interrupt */ + gicd_set_ipriorityr(gicd_base, current_prop->intr_num, + current_prop->intr_pri); + + /* Target SPIs to the primary CPU */ + gic_affinity_val = gicd_irouter_val_from_mpidr(read_mpidr(), 0); + gicd_write_irouter(gicd_base, current_prop->intr_num, + gic_affinity_val); + + /* Enable this interrupt */ + gicd_set_isenabler(gicd_base, current_prop->intr_num); + } + + return ctlr_enable; +} + +/******************************************************************************* + * Helper function to configure the default attributes of SPIs. + ******************************************************************************/ +void gicv3_ppi_sgi_configure_defaults(uintptr_t gicr_base) +{ + unsigned int index; + + /* + * Disable all SGIs (imp. def.)/PPIs before configuring them. This is a + * more scalable approach as it avoids clearing the enable bits in the + * GICD_CTLR + */ + gicr_write_icenabler0(gicr_base, ~0); + gicr_wait_for_pending_write(gicr_base); + + /* Treat all SGIs/PPIs as G1NS by default. */ + gicr_write_igroupr0(gicr_base, ~0U); + + /* Setup the default PPI/SGI priorities doing four at a time */ + for (index = 0; index < MIN_SPI_ID; index += 4) + gicr_write_ipriorityr(gicr_base, + index, + GICD_IPRIORITYR_DEF_VAL); + + /* Configure all PPIs as level triggered by default */ + gicr_write_icfgr1(gicr_base, 0); +} + +#if !ERROR_DEPRECATED +/******************************************************************************* + * Helper function to configure secure G0 and G1S SPIs. + ******************************************************************************/ +void gicv3_secure_ppi_sgi_configure(uintptr_t gicr_base, + unsigned int num_ints, + const unsigned int *sec_intr_list, + unsigned int int_grp) +{ + unsigned int index, irq_num; + + assert((int_grp == INTR_GROUP1S) || (int_grp == INTR_GROUP0)); + /* If `num_ints` is not 0, ensure that `sec_intr_list` is not NULL */ + assert(num_ints ? (uintptr_t)sec_intr_list : 1); + + for (index = 0; index < num_ints; index++) { + irq_num = sec_intr_list[index]; + if (irq_num < MIN_SPI_ID) { + + /* Configure this interrupt as a secure interrupt */ + gicr_clr_igroupr0(gicr_base, irq_num); + + /* Configure this interrupt as G0 or a G1S interrupt */ + if (int_grp == INTR_GROUP1S) + gicr_set_igrpmodr0(gicr_base, irq_num); + else + gicr_clr_igrpmodr0(gicr_base, irq_num); + + /* Set the priority of this interrupt */ + gicr_set_ipriorityr(gicr_base, + irq_num, + GIC_HIGHEST_SEC_PRIORITY); + + /* Enable this interrupt */ + gicr_set_isenabler0(gicr_base, irq_num); + } + } +} +#endif + +/******************************************************************************* + * Helper function to configure properties of secure G0 and G1S PPIs and SGIs. + ******************************************************************************/ +void gicv3_secure_ppi_sgi_configure_props(uintptr_t gicr_base, + const interrupt_prop_t *interrupt_props, + unsigned int interrupt_props_num) +{ + unsigned int i; + const interrupt_prop_t *current_prop; + + /* Make sure there's a valid property array */ + assert(interrupt_props != NULL); + assert(interrupt_props_num > 0); + + for (i = 0; i < interrupt_props_num; i++) { + current_prop = &interrupt_props[i]; + + if (current_prop->intr_num >= MIN_SPI_ID) + continue; + + /* Configure this interrupt as a secure interrupt */ + gicr_clr_igroupr0(gicr_base, current_prop->intr_num); + + /* Configure this interrupt as G0 or a G1S interrupt */ + assert((current_prop->intr_grp == INTR_GROUP0) || + (current_prop->intr_grp == INTR_GROUP1S)); + if (current_prop->intr_grp == INTR_GROUP1S) + gicr_set_igrpmodr0(gicr_base, current_prop->intr_num); + else + gicr_clr_igrpmodr0(gicr_base, current_prop->intr_num); + + /* Set the priority of this interrupt */ + gicr_set_ipriorityr(gicr_base, current_prop->intr_num, + current_prop->intr_pri); + + /* + * Set interrupt configuration for PPIs. Configuration for SGIs + * are ignored. + */ + if ((current_prop->intr_num >= MIN_PPI_ID) && + (current_prop->intr_num < MIN_SPI_ID)) { + gicr_set_icfgr1(gicr_base, current_prop->intr_num, + current_prop->intr_cfg); + } + + /* Enable this interrupt */ + gicr_set_isenabler0(gicr_base, current_prop->intr_num); + } +} diff --git a/drivers/arm/gic/v3/gicv3_main.c b/drivers/arm/gic/v3/gicv3_main.c new file mode 100644 index 00000000..8c4f5084 --- /dev/null +++ b/drivers/arm/gic/v3/gicv3_main.c @@ -0,0 +1,1147 @@ +/* + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <arch.h> +#include <arch_helpers.h> +#include <assert.h> +#include <debug.h> +#include <gicv3.h> +#include <interrupt_props.h> +#include <spinlock.h> +#include "gicv3_private.h" + +const gicv3_driver_data_t *gicv3_driver_data; +static unsigned int gicv2_compat; + +/* + * Spinlock to guard registers needing read-modify-write. APIs protected by this + * spinlock are used either at boot time (when only a single CPU is active), or + * when the system is fully coherent. + */ +spinlock_t gic_lock; + +/* + * Redistributor power operations are weakly bound so that they can be + * overridden + */ +#pragma weak gicv3_rdistif_off +#pragma weak gicv3_rdistif_on + + +/* Helper macros to save and restore GICD registers to and from the context */ +#define RESTORE_GICD_REGS(base, ctx, intr_num, reg, REG) \ + do { \ + for (unsigned int int_id = MIN_SPI_ID; int_id < intr_num; \ + int_id += (1 << REG##_SHIFT)) { \ + gicd_write_##reg(base, int_id, \ + ctx->gicd_##reg[(int_id - MIN_SPI_ID) >> REG##_SHIFT]); \ + } \ + } while (0) + +#define SAVE_GICD_REGS(base, ctx, intr_num, reg, REG) \ + do { \ + for (unsigned int int_id = MIN_SPI_ID; int_id < intr_num; \ + int_id += (1 << REG##_SHIFT)) { \ + ctx->gicd_##reg[(int_id - MIN_SPI_ID) >> REG##_SHIFT] =\ + gicd_read_##reg(base, int_id); \ + } \ + } while (0) + + +/******************************************************************************* + * This function initialises the ARM GICv3 driver in EL3 with provided platform + * inputs. + ******************************************************************************/ +void gicv3_driver_init(const gicv3_driver_data_t *plat_driver_data) +{ + unsigned int gic_version; + + assert(plat_driver_data); + assert(plat_driver_data->gicd_base); + assert(plat_driver_data->gicr_base); + assert(plat_driver_data->rdistif_num); + assert(plat_driver_data->rdistif_base_addrs); + + assert(IS_IN_EL3()); + +#if !ERROR_DEPRECATED + if (plat_driver_data->interrupt_props == NULL) { + /* Interrupt properties array size must be 0 */ + assert(plat_driver_data->interrupt_props_num == 0); + + /* + * The platform should provide a list of at least one type of + * interrupt. + */ + assert(plat_driver_data->g0_interrupt_array || + plat_driver_data->g1s_interrupt_array); + + /* + * If there are no interrupts of a particular type, then the + * number of interrupts of that type should be 0 and vice-versa. + */ + assert(plat_driver_data->g0_interrupt_array ? + plat_driver_data->g0_interrupt_num : + plat_driver_data->g0_interrupt_num == 0); + assert(plat_driver_data->g1s_interrupt_array ? + plat_driver_data->g1s_interrupt_num : + plat_driver_data->g1s_interrupt_num == 0); + } +#else + assert(plat_driver_data->interrupt_props != NULL); + assert(plat_driver_data->interrupt_props_num > 0); +#endif + + /* Check for system register support */ +#ifdef AARCH32 + assert(read_id_pfr1() & (ID_PFR1_GIC_MASK << ID_PFR1_GIC_SHIFT)); +#else + assert(read_id_aa64pfr0_el1() & + (ID_AA64PFR0_GIC_MASK << ID_AA64PFR0_GIC_SHIFT)); +#endif /* AARCH32 */ + + /* The GIC version should be 3.0 */ + gic_version = gicd_read_pidr2(plat_driver_data->gicd_base); + gic_version >>= PIDR2_ARCH_REV_SHIFT; + gic_version &= PIDR2_ARCH_REV_MASK; + assert(gic_version == ARCH_REV_GICV3); + + /* + * Find out whether the GIC supports the GICv2 compatibility mode. The + * ARE_S bit resets to 0 if supported + */ + gicv2_compat = gicd_read_ctlr(plat_driver_data->gicd_base); + gicv2_compat >>= CTLR_ARE_S_SHIFT; + gicv2_compat = !(gicv2_compat & CTLR_ARE_S_MASK); + + /* + * Find the base address of each implemented Redistributor interface. + * The number of interfaces should be equal to the number of CPUs in the + * system. The memory for saving these addresses has to be allocated by + * the platform port + */ + gicv3_rdistif_base_addrs_probe(plat_driver_data->rdistif_base_addrs, + plat_driver_data->rdistif_num, + plat_driver_data->gicr_base, + plat_driver_data->mpidr_to_core_pos); + + gicv3_driver_data = plat_driver_data; + + /* + * The GIC driver data is initialized by the primary CPU with caches + * enabled. When the secondary CPU boots up, it initializes the + * GICC/GICR interface with the caches disabled. Hence flush the + * driver data to ensure coherency. This is not required if the + * platform has HW_ASSISTED_COHERENCY enabled. + */ +#if !HW_ASSISTED_COHERENCY + flush_dcache_range((uintptr_t) &gicv3_driver_data, + sizeof(gicv3_driver_data)); + flush_dcache_range((uintptr_t) gicv3_driver_data, + sizeof(*gicv3_driver_data)); +#endif + + INFO("GICv3 %s legacy support detected." + " ARM GICV3 driver initialized in EL3\n", + gicv2_compat ? "with" : "without"); +} + +/******************************************************************************* + * This function initialises the GIC distributor interface based upon the data + * provided by the platform while initialising the driver. + ******************************************************************************/ +void gicv3_distif_init(void) +{ + unsigned int bitmap = 0; + + assert(gicv3_driver_data); + assert(gicv3_driver_data->gicd_base); + + assert(IS_IN_EL3()); + + /* + * Clear the "enable" bits for G0/G1S/G1NS interrupts before configuring + * the ARE_S bit. The Distributor might generate a system error + * otherwise. + */ + gicd_clr_ctlr(gicv3_driver_data->gicd_base, + CTLR_ENABLE_G0_BIT | + CTLR_ENABLE_G1S_BIT | + CTLR_ENABLE_G1NS_BIT, + RWP_TRUE); + + /* Set the ARE_S and ARE_NS bit now that interrupts have been disabled */ + gicd_set_ctlr(gicv3_driver_data->gicd_base, + CTLR_ARE_S_BIT | CTLR_ARE_NS_BIT, RWP_TRUE); + + /* Set the default attribute of all SPIs */ + gicv3_spis_configure_defaults(gicv3_driver_data->gicd_base); + +#if !ERROR_DEPRECATED + if (gicv3_driver_data->interrupt_props != NULL) { +#endif + bitmap = gicv3_secure_spis_configure_props( + gicv3_driver_data->gicd_base, + gicv3_driver_data->interrupt_props, + gicv3_driver_data->interrupt_props_num); +#if !ERROR_DEPRECATED + } else { + assert(gicv3_driver_data->g1s_interrupt_array || + gicv3_driver_data->g0_interrupt_array); + + /* Configure the G1S SPIs */ + if (gicv3_driver_data->g1s_interrupt_array) { + gicv3_secure_spis_configure(gicv3_driver_data->gicd_base, + gicv3_driver_data->g1s_interrupt_num, + gicv3_driver_data->g1s_interrupt_array, + INTR_GROUP1S); + bitmap |= CTLR_ENABLE_G1S_BIT; + } + + /* Configure the G0 SPIs */ + if (gicv3_driver_data->g0_interrupt_array) { + gicv3_secure_spis_configure(gicv3_driver_data->gicd_base, + gicv3_driver_data->g0_interrupt_num, + gicv3_driver_data->g0_interrupt_array, + INTR_GROUP0); + bitmap |= CTLR_ENABLE_G0_BIT; + } + } +#endif + + /* Enable the secure SPIs now that they have been configured */ + gicd_set_ctlr(gicv3_driver_data->gicd_base, bitmap, RWP_TRUE); +} + +/******************************************************************************* + * This function initialises the GIC Redistributor interface of the calling CPU + * (identified by the 'proc_num' parameter) based upon the data provided by the + * platform while initialising the driver. + ******************************************************************************/ +void gicv3_rdistif_init(unsigned int proc_num) +{ + uintptr_t gicr_base; + + assert(gicv3_driver_data); + assert(proc_num < gicv3_driver_data->rdistif_num); + assert(gicv3_driver_data->rdistif_base_addrs); + assert(gicv3_driver_data->gicd_base); + assert(gicd_read_ctlr(gicv3_driver_data->gicd_base) & CTLR_ARE_S_BIT); + + assert(IS_IN_EL3()); + + /* Power on redistributor */ + gicv3_rdistif_on(proc_num); + + gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num]; + + /* Set the default attribute of all SGIs and PPIs */ + gicv3_ppi_sgi_configure_defaults(gicr_base); + +#if !ERROR_DEPRECATED + if (gicv3_driver_data->interrupt_props != NULL) { +#endif + gicv3_secure_ppi_sgi_configure_props(gicr_base, + gicv3_driver_data->interrupt_props, + gicv3_driver_data->interrupt_props_num); +#if !ERROR_DEPRECATED + } else { + assert(gicv3_driver_data->g1s_interrupt_array || + gicv3_driver_data->g0_interrupt_array); + + /* Configure the G1S SGIs/PPIs */ + if (gicv3_driver_data->g1s_interrupt_array) { + gicv3_secure_ppi_sgi_configure(gicr_base, + gicv3_driver_data->g1s_interrupt_num, + gicv3_driver_data->g1s_interrupt_array, + INTR_GROUP1S); + } + + /* Configure the G0 SGIs/PPIs */ + if (gicv3_driver_data->g0_interrupt_array) { + gicv3_secure_ppi_sgi_configure(gicr_base, + gicv3_driver_data->g0_interrupt_num, + gicv3_driver_data->g0_interrupt_array, + INTR_GROUP0); + } + } +#endif +} + +/******************************************************************************* + * Functions to perform power operations on GIC Redistributor + ******************************************************************************/ +void gicv3_rdistif_off(unsigned int proc_num) +{ + return; +} + +void gicv3_rdistif_on(unsigned int proc_num) +{ + return; +} + +/******************************************************************************* + * This function enables the GIC CPU interface of the calling CPU using only + * system register accesses. + ******************************************************************************/ +void gicv3_cpuif_enable(unsigned int proc_num) +{ + uintptr_t gicr_base; + unsigned int scr_el3; + unsigned int icc_sre_el3; + + assert(gicv3_driver_data); + assert(proc_num < gicv3_driver_data->rdistif_num); + assert(gicv3_driver_data->rdistif_base_addrs); + assert(IS_IN_EL3()); + + /* Mark the connected core as awake */ + gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num]; + gicv3_rdistif_mark_core_awake(gicr_base); + + /* Disable the legacy interrupt bypass */ + icc_sre_el3 = ICC_SRE_DIB_BIT | ICC_SRE_DFB_BIT; + + /* + * Enable system register access for EL3 and allow lower exception + * levels to configure the same for themselves. If the legacy mode is + * not supported, the SRE bit is RAO/WI + */ + icc_sre_el3 |= (ICC_SRE_EN_BIT | ICC_SRE_SRE_BIT); + write_icc_sre_el3(read_icc_sre_el3() | icc_sre_el3); + + scr_el3 = read_scr_el3(); + + /* + * Switch to NS state to write Non secure ICC_SRE_EL1 and + * ICC_SRE_EL2 registers. + */ + write_scr_el3(scr_el3 | SCR_NS_BIT); + isb(); + + write_icc_sre_el2(read_icc_sre_el2() | icc_sre_el3); + write_icc_sre_el1(ICC_SRE_SRE_BIT); + isb(); + + /* Switch to secure state. */ + write_scr_el3(scr_el3 & (~SCR_NS_BIT)); + isb(); + + /* Program the idle priority in the PMR */ + write_icc_pmr_el1(GIC_PRI_MASK); + + /* Enable Group0 interrupts */ + write_icc_igrpen0_el1(IGRPEN1_EL1_ENABLE_G0_BIT); + + /* Enable Group1 Secure interrupts */ + write_icc_igrpen1_el3(read_icc_igrpen1_el3() | + IGRPEN1_EL3_ENABLE_G1S_BIT); + + /* Write the secure ICC_SRE_EL1 register */ + write_icc_sre_el1(ICC_SRE_SRE_BIT); + isb(); +} + +/******************************************************************************* + * This function disables the GIC CPU interface of the calling CPU using + * only system register accesses. + ******************************************************************************/ +void gicv3_cpuif_disable(unsigned int proc_num) +{ + uintptr_t gicr_base; + + assert(gicv3_driver_data); + assert(proc_num < gicv3_driver_data->rdistif_num); + assert(gicv3_driver_data->rdistif_base_addrs); + + assert(IS_IN_EL3()); + + /* Disable legacy interrupt bypass */ + write_icc_sre_el3(read_icc_sre_el3() | + (ICC_SRE_DIB_BIT | ICC_SRE_DFB_BIT)); + + /* Disable Group0 interrupts */ + write_icc_igrpen0_el1(read_icc_igrpen0_el1() & + ~IGRPEN1_EL1_ENABLE_G0_BIT); + + /* Disable Group1 Secure and Non-Secure interrupts */ + write_icc_igrpen1_el3(read_icc_igrpen1_el3() & + ~(IGRPEN1_EL3_ENABLE_G1NS_BIT | + IGRPEN1_EL3_ENABLE_G1S_BIT)); + + /* Synchronise accesses to group enable registers */ + isb(); + + /* Mark the connected core as asleep */ + gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num]; + gicv3_rdistif_mark_core_asleep(gicr_base); +} + +/******************************************************************************* + * This function returns the id of the highest priority pending interrupt at + * the GIC cpu interface. + ******************************************************************************/ +unsigned int gicv3_get_pending_interrupt_id(void) +{ + unsigned int id; + + assert(IS_IN_EL3()); + id = read_icc_hppir0_el1() & HPPIR0_EL1_INTID_MASK; + + /* + * If the ID is special identifier corresponding to G1S or G1NS + * interrupt, then read the highest pending group 1 interrupt. + */ + if ((id == PENDING_G1S_INTID) || (id == PENDING_G1NS_INTID)) + return read_icc_hppir1_el1() & HPPIR1_EL1_INTID_MASK; + + return id; +} + +/******************************************************************************* + * This function returns the type of the highest priority pending interrupt at + * the GIC cpu interface. The return values can be one of the following : + * PENDING_G1S_INTID : The interrupt type is secure Group 1. + * PENDING_G1NS_INTID : The interrupt type is non secure Group 1. + * 0 - 1019 : The interrupt type is secure Group 0. + * GIC_SPURIOUS_INTERRUPT : there is no pending interrupt with + * sufficient priority to be signaled + ******************************************************************************/ +unsigned int gicv3_get_pending_interrupt_type(void) +{ + assert(IS_IN_EL3()); + return read_icc_hppir0_el1() & HPPIR0_EL1_INTID_MASK; +} + +/******************************************************************************* + * This function returns the type of the interrupt id depending upon the group + * this interrupt has been configured under by the interrupt controller i.e. + * group0 or group1 Secure / Non Secure. The return value can be one of the + * following : + * INTR_GROUP0 : The interrupt type is a Secure Group 0 interrupt + * INTR_GROUP1S : The interrupt type is a Secure Group 1 secure interrupt + * INTR_GROUP1NS: The interrupt type is a Secure Group 1 non secure + * interrupt. + ******************************************************************************/ +unsigned int gicv3_get_interrupt_type(unsigned int id, + unsigned int proc_num) +{ + unsigned int igroup, grpmodr; + uintptr_t gicr_base; + + assert(IS_IN_EL3()); + assert(gicv3_driver_data); + + /* Ensure the parameters are valid */ + assert(id < PENDING_G1S_INTID || id >= MIN_LPI_ID); + assert(proc_num < gicv3_driver_data->rdistif_num); + + /* All LPI interrupts are Group 1 non secure */ + if (id >= MIN_LPI_ID) + return INTR_GROUP1NS; + + if (id < MIN_SPI_ID) { + assert(gicv3_driver_data->rdistif_base_addrs); + gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num]; + igroup = gicr_get_igroupr0(gicr_base, id); + grpmodr = gicr_get_igrpmodr0(gicr_base, id); + } else { + assert(gicv3_driver_data->gicd_base); + igroup = gicd_get_igroupr(gicv3_driver_data->gicd_base, id); + grpmodr = gicd_get_igrpmodr(gicv3_driver_data->gicd_base, id); + } + + /* + * If the IGROUP bit is set, then it is a Group 1 Non secure + * interrupt + */ + if (igroup) + return INTR_GROUP1NS; + + /* If the GRPMOD bit is set, then it is a Group 1 Secure interrupt */ + if (grpmodr) + return INTR_GROUP1S; + + /* Else it is a Group 0 Secure interrupt */ + return INTR_GROUP0; +} + +/***************************************************************************** + * Function to save and disable the GIC ITS register context. The power + * management of GIC ITS is implementation-defined and this function doesn't + * save any memory structures required to support ITS. As the sequence to save + * this state is implementation defined, it should be executed in platform + * specific code. Calling this function alone and then powering down the GIC and + * ITS without implementing the aforementioned platform specific code will + * corrupt the ITS state. + * + * This function must be invoked after the GIC CPU interface is disabled. + *****************************************************************************/ +void gicv3_its_save_disable(uintptr_t gits_base, gicv3_its_ctx_t * const its_ctx) +{ + int i; + + assert(gicv3_driver_data); + assert(IS_IN_EL3()); + assert(its_ctx); + assert(gits_base); + + its_ctx->gits_ctlr = gits_read_ctlr(gits_base); + + /* Disable the ITS */ + gits_write_ctlr(gits_base, its_ctx->gits_ctlr & + (~GITS_CTLR_ENABLED_BIT)); + + /* Wait for quiescent state */ + gits_wait_for_quiescent_bit(gits_base); + + its_ctx->gits_cbaser = gits_read_cbaser(gits_base); + its_ctx->gits_cwriter = gits_read_cwriter(gits_base); + + for (i = 0; i < ARRAY_SIZE(its_ctx->gits_baser); i++) + its_ctx->gits_baser[i] = gits_read_baser(gits_base, i); +} + +/***************************************************************************** + * Function to restore the GIC ITS register context. The power + * management of GIC ITS is implementation defined and this function doesn't + * restore any memory structures required to support ITS. The assumption is + * that these structures are in memory and are retained during system suspend. + * + * This must be invoked before the GIC CPU interface is enabled. + *****************************************************************************/ +void gicv3_its_restore(uintptr_t gits_base, const gicv3_its_ctx_t * const its_ctx) +{ + int i; + + assert(gicv3_driver_data); + assert(IS_IN_EL3()); + assert(its_ctx); + assert(gits_base); + + /* Assert that the GITS is disabled and quiescent */ + assert((gits_read_ctlr(gits_base) & GITS_CTLR_ENABLED_BIT) == 0); + assert((gits_read_ctlr(gits_base) & GITS_CTLR_QUIESCENT_BIT) != 0); + + gits_write_cbaser(gits_base, its_ctx->gits_cbaser); + gits_write_cwriter(gits_base, its_ctx->gits_cwriter); + + for (i = 0; i < ARRAY_SIZE(its_ctx->gits_baser); i++) + gits_write_baser(gits_base, i, its_ctx->gits_baser[i]); + + /* Restore the ITS CTLR but leave the ITS disabled */ + gits_write_ctlr(gits_base, its_ctx->gits_ctlr & + (~GITS_CTLR_ENABLED_BIT)); +} + +/***************************************************************************** + * Function to save the GIC Redistributor register context. This function + * must be invoked after CPU interface disable and prior to Distributor save. + *****************************************************************************/ +void gicv3_rdistif_save(unsigned int proc_num, gicv3_redist_ctx_t * const rdist_ctx) +{ + uintptr_t gicr_base; + unsigned int int_id; + + assert(gicv3_driver_data); + assert(proc_num < gicv3_driver_data->rdistif_num); + assert(gicv3_driver_data->rdistif_base_addrs); + assert(IS_IN_EL3()); + assert(rdist_ctx); + + gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num]; + + /* + * Wait for any write to GICR_CTLR to complete before trying to save any + * state. + */ + gicr_wait_for_pending_write(gicr_base); + + rdist_ctx->gicr_ctlr = gicr_read_ctlr(gicr_base); + + rdist_ctx->gicr_propbaser = gicr_read_propbaser(gicr_base); + rdist_ctx->gicr_pendbaser = gicr_read_pendbaser(gicr_base); + + rdist_ctx->gicr_igroupr0 = gicr_read_igroupr0(gicr_base); + rdist_ctx->gicr_isenabler0 = gicr_read_isenabler0(gicr_base); + rdist_ctx->gicr_ispendr0 = gicr_read_ispendr0(gicr_base); + rdist_ctx->gicr_isactiver0 = gicr_read_isactiver0(gicr_base); + rdist_ctx->gicr_icfgr0 = gicr_read_icfgr0(gicr_base); + rdist_ctx->gicr_icfgr1 = gicr_read_icfgr1(gicr_base); + rdist_ctx->gicr_igrpmodr0 = gicr_read_igrpmodr0(gicr_base); + rdist_ctx->gicr_nsacr = gicr_read_nsacr(gicr_base); + for (int_id = MIN_SGI_ID; int_id < TOTAL_PCPU_INTR_NUM; + int_id += (1 << IPRIORITYR_SHIFT)) { + rdist_ctx->gicr_ipriorityr[(int_id - MIN_SGI_ID) >> IPRIORITYR_SHIFT] = + gicr_read_ipriorityr(gicr_base, int_id); + } + + + /* + * Call the pre-save hook that implements the IMP DEF sequence that may + * be required on some GIC implementations. As this may need to access + * the Redistributor registers, we pass it proc_num. + */ + gicv3_distif_pre_save(proc_num); +} + +/***************************************************************************** + * Function to restore the GIC Redistributor register context. We disable + * LPI and per-cpu interrupts before we start restore of the Redistributor. + * This function must be invoked after Distributor restore but prior to + * CPU interface enable. The pending and active interrupts are restored + * after the interrupts are fully configured and enabled. + *****************************************************************************/ +void gicv3_rdistif_init_restore(unsigned int proc_num, + const gicv3_redist_ctx_t * const rdist_ctx) +{ + uintptr_t gicr_base; + unsigned int int_id; + + assert(gicv3_driver_data); + assert(proc_num < gicv3_driver_data->rdistif_num); + assert(gicv3_driver_data->rdistif_base_addrs); + assert(IS_IN_EL3()); + assert(rdist_ctx); + + gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num]; + + /* Power on redistributor */ + gicv3_rdistif_on(proc_num); + + /* + * Call the post-restore hook that implements the IMP DEF sequence that + * may be required on some GIC implementations. As this may need to + * access the Redistributor registers, we pass it proc_num. + */ + gicv3_distif_post_restore(proc_num); + + /* + * Disable all SGIs (imp. def.)/PPIs before configuring them. This is a + * more scalable approach as it avoids clearing the enable bits in the + * GICD_CTLR + */ + gicr_write_icenabler0(gicr_base, ~0); + /* Wait for pending writes to GICR_ICENABLER */ + gicr_wait_for_pending_write(gicr_base); + + /* + * Disable the LPIs to avoid unpredictable behavior when writing to + * GICR_PROPBASER and GICR_PENDBASER. + */ + gicr_write_ctlr(gicr_base, + rdist_ctx->gicr_ctlr & ~(GICR_CTLR_EN_LPIS_BIT)); + + /* Restore registers' content */ + gicr_write_propbaser(gicr_base, rdist_ctx->gicr_propbaser); + gicr_write_pendbaser(gicr_base, rdist_ctx->gicr_pendbaser); + + gicr_write_igroupr0(gicr_base, rdist_ctx->gicr_igroupr0); + + for (int_id = MIN_SGI_ID; int_id < TOTAL_PCPU_INTR_NUM; + int_id += (1 << IPRIORITYR_SHIFT)) { + gicr_write_ipriorityr(gicr_base, int_id, + rdist_ctx->gicr_ipriorityr[ + (int_id - MIN_SGI_ID) >> IPRIORITYR_SHIFT]); + } + + gicr_write_icfgr0(gicr_base, rdist_ctx->gicr_icfgr0); + gicr_write_icfgr1(gicr_base, rdist_ctx->gicr_icfgr1); + gicr_write_igrpmodr0(gicr_base, rdist_ctx->gicr_igrpmodr0); + gicr_write_nsacr(gicr_base, rdist_ctx->gicr_nsacr); + + /* Restore after group and priorities are set */ + gicr_write_ispendr0(gicr_base, rdist_ctx->gicr_ispendr0); + gicr_write_isactiver0(gicr_base, rdist_ctx->gicr_isactiver0); + + /* + * Wait for all writes to the Distributor to complete before enabling + * the SGI and PPIs. + */ + gicr_wait_for_upstream_pending_write(gicr_base); + gicr_write_isenabler0(gicr_base, rdist_ctx->gicr_isenabler0); + + /* + * Restore GICR_CTLR.Enable_LPIs bit and wait for pending writes in case + * the first write to GICR_CTLR was still in flight (this write only + * restores GICR_CTLR.Enable_LPIs and no waiting is required for this + * bit). + */ + gicr_write_ctlr(gicr_base, rdist_ctx->gicr_ctlr); + gicr_wait_for_pending_write(gicr_base); +} + +/***************************************************************************** + * Function to save the GIC Distributor register context. This function + * must be invoked after CPU interface disable and Redistributor save. + *****************************************************************************/ +void gicv3_distif_save(gicv3_dist_ctx_t * const dist_ctx) +{ + unsigned int num_ints; + + assert(gicv3_driver_data); + assert(gicv3_driver_data->gicd_base); + assert(IS_IN_EL3()); + assert(dist_ctx); + + uintptr_t gicd_base = gicv3_driver_data->gicd_base; + + num_ints = gicd_read_typer(gicd_base); + num_ints &= TYPER_IT_LINES_NO_MASK; + num_ints = (num_ints + 1) << 5; + + assert(num_ints <= MAX_SPI_ID + 1); + + /* Wait for pending write to complete */ + gicd_wait_for_pending_write(gicd_base); + + /* Save the GICD_CTLR */ + dist_ctx->gicd_ctlr = gicd_read_ctlr(gicd_base); + + /* Save GICD_IGROUPR for INTIDs 32 - 1020 */ + SAVE_GICD_REGS(gicd_base, dist_ctx, num_ints, igroupr, IGROUPR); + + /* Save GICD_ISENABLER for INT_IDs 32 - 1020 */ + SAVE_GICD_REGS(gicd_base, dist_ctx, num_ints, isenabler, ISENABLER); + + /* Save GICD_ISPENDR for INTIDs 32 - 1020 */ + SAVE_GICD_REGS(gicd_base, dist_ctx, num_ints, ispendr, ISPENDR); + + /* Save GICD_ISACTIVER for INTIDs 32 - 1020 */ + SAVE_GICD_REGS(gicd_base, dist_ctx, num_ints, isactiver, ISACTIVER); + + /* Save GICD_IPRIORITYR for INTIDs 32 - 1020 */ + SAVE_GICD_REGS(gicd_base, dist_ctx, num_ints, ipriorityr, IPRIORITYR); + + /* Save GICD_ICFGR for INTIDs 32 - 1020 */ + SAVE_GICD_REGS(gicd_base, dist_ctx, num_ints, icfgr, ICFGR); + + /* Save GICD_IGRPMODR for INTIDs 32 - 1020 */ + SAVE_GICD_REGS(gicd_base, dist_ctx, num_ints, igrpmodr, IGRPMODR); + + /* Save GICD_NSACR for INTIDs 32 - 1020 */ + SAVE_GICD_REGS(gicd_base, dist_ctx, num_ints, nsacr, NSACR); + + /* Save GICD_IROUTER for INTIDs 32 - 1024 */ + SAVE_GICD_REGS(gicd_base, dist_ctx, num_ints, irouter, IROUTER); + + /* + * GICD_ITARGETSR<n> and GICD_SPENDSGIR<n> are RAZ/WI when + * GICD_CTLR.ARE_(S|NS) bits are set which is the case for our GICv3 + * driver. + */ +} + +/***************************************************************************** + * Function to restore the GIC Distributor register context. We disable G0, G1S + * and G1NS interrupt groups before we start restore of the Distributor. This + * function must be invoked prior to Redistributor restore and CPU interface + * enable. The pending and active interrupts are restored after the interrupts + * are fully configured and enabled. + *****************************************************************************/ +void gicv3_distif_init_restore(const gicv3_dist_ctx_t * const dist_ctx) +{ + unsigned int num_ints = 0; + + assert(gicv3_driver_data); + assert(gicv3_driver_data->gicd_base); + assert(IS_IN_EL3()); + assert(dist_ctx); + + uintptr_t gicd_base = gicv3_driver_data->gicd_base; + + /* + * Clear the "enable" bits for G0/G1S/G1NS interrupts before configuring + * the ARE_S bit. The Distributor might generate a system error + * otherwise. + */ + gicd_clr_ctlr(gicd_base, + CTLR_ENABLE_G0_BIT | + CTLR_ENABLE_G1S_BIT | + CTLR_ENABLE_G1NS_BIT, + RWP_TRUE); + + /* Set the ARE_S and ARE_NS bit now that interrupts have been disabled */ + gicd_set_ctlr(gicd_base, CTLR_ARE_S_BIT | CTLR_ARE_NS_BIT, RWP_TRUE); + + num_ints = gicd_read_typer(gicd_base); + num_ints &= TYPER_IT_LINES_NO_MASK; + num_ints = (num_ints + 1) << 5; + + assert(num_ints <= MAX_SPI_ID + 1); + + /* Restore GICD_IGROUPR for INTIDs 32 - 1020 */ + RESTORE_GICD_REGS(gicd_base, dist_ctx, num_ints, igroupr, IGROUPR); + + /* Restore GICD_IPRIORITYR for INTIDs 32 - 1020 */ + RESTORE_GICD_REGS(gicd_base, dist_ctx, num_ints, ipriorityr, IPRIORITYR); + + /* Restore GICD_ICFGR for INTIDs 32 - 1020 */ + RESTORE_GICD_REGS(gicd_base, dist_ctx, num_ints, icfgr, ICFGR); + + /* Restore GICD_IGRPMODR for INTIDs 32 - 1020 */ + RESTORE_GICD_REGS(gicd_base, dist_ctx, num_ints, igrpmodr, IGRPMODR); + + /* Restore GICD_NSACR for INTIDs 32 - 1020 */ + RESTORE_GICD_REGS(gicd_base, dist_ctx, num_ints, nsacr, NSACR); + + /* Restore GICD_IROUTER for INTIDs 32 - 1020 */ + RESTORE_GICD_REGS(gicd_base, dist_ctx, num_ints, irouter, IROUTER); + + /* + * Restore ISENABLER, ISPENDR and ISACTIVER after the interrupts are + * configured. + */ + + /* Restore GICD_ISENABLER for INT_IDs 32 - 1020 */ + RESTORE_GICD_REGS(gicd_base, dist_ctx, num_ints, isenabler, ISENABLER); + + /* Restore GICD_ISPENDR for INTIDs 32 - 1020 */ + RESTORE_GICD_REGS(gicd_base, dist_ctx, num_ints, ispendr, ISPENDR); + + /* Restore GICD_ISACTIVER for INTIDs 32 - 1020 */ + RESTORE_GICD_REGS(gicd_base, dist_ctx, num_ints, isactiver, ISACTIVER); + + /* Restore the GICD_CTLR */ + gicd_write_ctlr(gicd_base, dist_ctx->gicd_ctlr); + gicd_wait_for_pending_write(gicd_base); + +} + +/******************************************************************************* + * This function gets the priority of the interrupt the processor is currently + * servicing. + ******************************************************************************/ +unsigned int gicv3_get_running_priority(void) +{ + return read_icc_rpr_el1(); +} + +/******************************************************************************* + * This function checks if the interrupt identified by id is active (whether the + * state is either active, or active and pending). The proc_num is used if the + * interrupt is SGI or PPI and programs the corresponding Redistributor + * interface. + ******************************************************************************/ +unsigned int gicv3_get_interrupt_active(unsigned int id, unsigned int proc_num) +{ + unsigned int value; + + assert(gicv3_driver_data); + assert(gicv3_driver_data->gicd_base); + assert(proc_num < gicv3_driver_data->rdistif_num); + assert(gicv3_driver_data->rdistif_base_addrs); + assert(id <= MAX_SPI_ID); + + if (id < MIN_SPI_ID) { + /* For SGIs and PPIs */ + value = gicr_get_isactiver0( + gicv3_driver_data->rdistif_base_addrs[proc_num], id); + } else { + value = gicd_get_isactiver(gicv3_driver_data->gicd_base, id); + } + + return value; +} + +/******************************************************************************* + * This function enables the interrupt identified by id. The proc_num + * is used if the interrupt is SGI or PPI, and programs the corresponding + * Redistributor interface. + ******************************************************************************/ +void gicv3_enable_interrupt(unsigned int id, unsigned int proc_num) +{ + assert(gicv3_driver_data); + assert(gicv3_driver_data->gicd_base); + assert(proc_num < gicv3_driver_data->rdistif_num); + assert(gicv3_driver_data->rdistif_base_addrs); + assert(id <= MAX_SPI_ID); + + /* + * Ensure that any shared variable updates depending on out of band + * interrupt trigger are observed before enabling interrupt. + */ + dsbishst(); + if (id < MIN_SPI_ID) { + /* For SGIs and PPIs */ + gicr_set_isenabler0( + gicv3_driver_data->rdistif_base_addrs[proc_num], + id); + } else { + gicd_set_isenabler(gicv3_driver_data->gicd_base, id); + } +} + +/******************************************************************************* + * This function disables the interrupt identified by id. The proc_num + * is used if the interrupt is SGI or PPI, and programs the corresponding + * Redistributor interface. + ******************************************************************************/ +void gicv3_disable_interrupt(unsigned int id, unsigned int proc_num) +{ + assert(gicv3_driver_data); + assert(gicv3_driver_data->gicd_base); + assert(proc_num < gicv3_driver_data->rdistif_num); + assert(gicv3_driver_data->rdistif_base_addrs); + assert(id <= MAX_SPI_ID); + + /* + * Disable interrupt, and ensure that any shared variable updates + * depending on out of band interrupt trigger are observed afterwards. + */ + if (id < MIN_SPI_ID) { + /* For SGIs and PPIs */ + gicr_set_icenabler0( + gicv3_driver_data->rdistif_base_addrs[proc_num], + id); + + /* Write to clear enable requires waiting for pending writes */ + gicr_wait_for_pending_write( + gicv3_driver_data->rdistif_base_addrs[proc_num]); + } else { + gicd_set_icenabler(gicv3_driver_data->gicd_base, id); + + /* Write to clear enable requires waiting for pending writes */ + gicd_wait_for_pending_write(gicv3_driver_data->gicd_base); + } + + dsbishst(); +} + +/******************************************************************************* + * This function sets the interrupt priority as supplied for the given interrupt + * id. + ******************************************************************************/ +void gicv3_set_interrupt_priority(unsigned int id, unsigned int proc_num, + unsigned int priority) +{ + uintptr_t gicr_base; + + assert(gicv3_driver_data); + assert(gicv3_driver_data->gicd_base); + assert(proc_num < gicv3_driver_data->rdistif_num); + assert(gicv3_driver_data->rdistif_base_addrs); + assert(id <= MAX_SPI_ID); + + if (id < MIN_SPI_ID) { + gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num]; + gicr_set_ipriorityr(gicr_base, id, priority); + } else { + gicd_set_ipriorityr(gicv3_driver_data->gicd_base, id, priority); + } +} + +/******************************************************************************* + * This function assigns group for the interrupt identified by id. The proc_num + * is used if the interrupt is SGI or PPI, and programs the corresponding + * Redistributor interface. The group can be any of GICV3_INTR_GROUP* + ******************************************************************************/ +void gicv3_set_interrupt_type(unsigned int id, unsigned int proc_num, + unsigned int type) +{ + unsigned int igroup = 0, grpmod = 0; + uintptr_t gicr_base; + + assert(gicv3_driver_data); + assert(gicv3_driver_data->gicd_base); + assert(proc_num < gicv3_driver_data->rdistif_num); + assert(gicv3_driver_data->rdistif_base_addrs); + + switch (type) { + case INTR_GROUP1S: + igroup = 0; + grpmod = 1; + break; + case INTR_GROUP0: + igroup = 0; + grpmod = 0; + break; + case INTR_GROUP1NS: + igroup = 1; + grpmod = 0; + break; + default: + assert(0); + } + + if (id < MIN_SPI_ID) { + gicr_base = gicv3_driver_data->rdistif_base_addrs[proc_num]; + if (igroup) + gicr_set_igroupr0(gicr_base, id); + else + gicr_clr_igroupr0(gicr_base, id); + + if (grpmod) + gicr_set_igrpmodr0(gicr_base, id); + else + gicr_clr_igrpmodr0(gicr_base, id); + } else { + /* Serialize read-modify-write to Distributor registers */ + spin_lock(&gic_lock); + if (igroup) + gicd_set_igroupr(gicv3_driver_data->gicd_base, id); + else + gicd_clr_igroupr(gicv3_driver_data->gicd_base, id); + + if (grpmod) + gicd_set_igrpmodr(gicv3_driver_data->gicd_base, id); + else + gicd_clr_igrpmodr(gicv3_driver_data->gicd_base, id); + spin_unlock(&gic_lock); + } +} + +/******************************************************************************* + * This function raises the specified Secure Group 0 SGI. + * + * The target parameter must be a valid MPIDR in the system. + ******************************************************************************/ +void gicv3_raise_secure_g0_sgi(int sgi_num, u_register_t target) +{ + unsigned int tgt, aff3, aff2, aff1, aff0; + uint64_t sgi_val; + + /* Verify interrupt number is in the SGI range */ + assert((sgi_num >= MIN_SGI_ID) && (sgi_num < MIN_PPI_ID)); + + /* Extract affinity fields from target */ + aff0 = MPIDR_AFFLVL0_VAL(target); + aff1 = MPIDR_AFFLVL1_VAL(target); + aff2 = MPIDR_AFFLVL2_VAL(target); + aff3 = MPIDR_AFFLVL3_VAL(target); + + /* + * Make target list from affinity 0, and ensure GICv3 SGI can target + * this PE. + */ + assert(aff0 < GICV3_MAX_SGI_TARGETS); + tgt = BIT(aff0); + + /* Raise SGI to PE specified by its affinity */ + sgi_val = GICV3_SGIR_VALUE(aff3, aff2, aff1, sgi_num, SGIR_IRM_TO_AFF, + tgt); + + /* + * Ensure that any shared variable updates depending on out of band + * interrupt trigger are observed before raising SGI. + */ + dsbishst(); + write_icc_sgi0r_el1(sgi_val); + isb(); +} + +/******************************************************************************* + * This function sets the interrupt routing for the given SPI interrupt id. + * The interrupt routing is specified in routing mode and mpidr. + * + * The routing mode can be either of: + * - GICV3_IRM_ANY + * - GICV3_IRM_PE + * + * The mpidr is the affinity of the PE to which the interrupt will be routed, + * and is ignored for routing mode GICV3_IRM_ANY. + ******************************************************************************/ +void gicv3_set_spi_routing(unsigned int id, unsigned int irm, u_register_t mpidr) +{ + unsigned long long aff; + uint64_t router; + + assert(gicv3_driver_data); + assert(gicv3_driver_data->gicd_base); + + assert((irm == GICV3_IRM_ANY) || (irm == GICV3_IRM_PE)); + assert(id >= MIN_SPI_ID && id <= MAX_SPI_ID); + + aff = gicd_irouter_val_from_mpidr(mpidr, irm); + gicd_write_irouter(gicv3_driver_data->gicd_base, id, aff); + + /* + * In implementations that do not require 1 of N distribution of SPIs, + * IRM might be RAZ/WI. Read back and verify IRM bit. + */ + if (irm == GICV3_IRM_ANY) { + router = gicd_read_irouter(gicv3_driver_data->gicd_base, id); + if (!((router >> IROUTER_IRM_SHIFT) & IROUTER_IRM_MASK)) { + ERROR("GICv3 implementation doesn't support routing ANY\n"); + panic(); + } + } +} + +/******************************************************************************* + * This function clears the pending status of an interrupt identified by id. + * The proc_num is used if the interrupt is SGI or PPI, and programs the + * corresponding Redistributor interface. + ******************************************************************************/ +void gicv3_clear_interrupt_pending(unsigned int id, unsigned int proc_num) +{ + assert(gicv3_driver_data); + assert(gicv3_driver_data->gicd_base); + assert(proc_num < gicv3_driver_data->rdistif_num); + assert(gicv3_driver_data->rdistif_base_addrs); + + /* + * Clear pending interrupt, and ensure that any shared variable updates + * depending on out of band interrupt trigger are observed afterwards. + */ + if (id < MIN_SPI_ID) { + /* For SGIs and PPIs */ + gicr_set_icpendr0(gicv3_driver_data->rdistif_base_addrs[proc_num], + id); + } else { + gicd_set_icpendr(gicv3_driver_data->gicd_base, id); + } + dsbishst(); +} + +/******************************************************************************* + * This function sets the pending status of an interrupt identified by id. + * The proc_num is used if the interrupt is SGI or PPI and programs the + * corresponding Redistributor interface. + ******************************************************************************/ +void gicv3_set_interrupt_pending(unsigned int id, unsigned int proc_num) +{ + assert(gicv3_driver_data); + assert(gicv3_driver_data->gicd_base); + assert(proc_num < gicv3_driver_data->rdistif_num); + assert(gicv3_driver_data->rdistif_base_addrs); + + /* + * Ensure that any shared variable updates depending on out of band + * interrupt trigger are observed before setting interrupt pending. + */ + dsbishst(); + if (id < MIN_SPI_ID) { + /* For SGIs and PPIs */ + gicr_set_ispendr0(gicv3_driver_data->rdistif_base_addrs[proc_num], + id); + } else { + gicd_set_ispendr(gicv3_driver_data->gicd_base, id); + } +} + +/******************************************************************************* + * This function sets the PMR register with the supplied value. Returns the + * original PMR. + ******************************************************************************/ +unsigned int gicv3_set_pmr(unsigned int mask) +{ + unsigned int old_mask; + + old_mask = read_icc_pmr_el1(); + + /* + * Order memory updates w.r.t. PMR write, and ensure they're visible + * before potential out of band interrupt trigger because of PMR update. + * PMR system register writes are self-synchronizing, so no ISB required + * thereafter. + */ + dsbishst(); + write_icc_pmr_el1(mask); + + return old_mask; +} diff --git a/drivers/arm/gic/v3/gicv3_private.h b/drivers/arm/gic/v3/gicv3_private.h new file mode 100644 index 00000000..a5093d0c --- /dev/null +++ b/drivers/arm/gic/v3/gicv3_private.h @@ -0,0 +1,390 @@ +/* + * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __GICV3_PRIVATE_H__ +#define __GICV3_PRIVATE_H__ + +#include <assert.h> +#include <gic_common.h> +#include <gicv3.h> +#include <mmio.h> +#include <stdint.h> +#include "../common/gic_common_private.h" + +/******************************************************************************* + * GICv3 private macro definitions + ******************************************************************************/ + +/* Constants to indicate the status of the RWP bit */ +#define RWP_TRUE 1 +#define RWP_FALSE 0 + +/* + * Macro to convert an mpidr to a value suitable for programming into a + * GICD_IROUTER. Bits[31:24] in the MPIDR are cleared as they are not relevant + * to GICv3. + */ +#define gicd_irouter_val_from_mpidr(mpidr, irm) \ + ((mpidr & ~(0xff << 24)) | \ + (irm & IROUTER_IRM_MASK) << IROUTER_IRM_SHIFT) + +/* + * Macro to convert a GICR_TYPER affinity value into a MPIDR value. Bits[31:24] + * are zeroes. + */ +#ifdef AARCH32 +#define mpidr_from_gicr_typer(typer_val) (((typer_val) >> 32) & 0xffffff) +#else +#define mpidr_from_gicr_typer(typer_val) \ + (((((typer_val) >> 56) & MPIDR_AFFLVL_MASK) << MPIDR_AFF3_SHIFT) | \ + (((typer_val) >> 32) & 0xffffff)) +#endif + +/******************************************************************************* + * GICv3 private global variables declarations + ******************************************************************************/ +extern const gicv3_driver_data_t *gicv3_driver_data; + +/******************************************************************************* + * Private GICv3 function prototypes for accessing entire registers. + * Note: The raw register values correspond to multiple interrupt IDs and + * the number of interrupt IDs involved depends on the register accessed. + ******************************************************************************/ +unsigned int gicd_read_igrpmodr(uintptr_t base, unsigned int id); +unsigned int gicr_read_ipriorityr(uintptr_t base, unsigned int id); +void gicd_write_igrpmodr(uintptr_t base, unsigned int id, unsigned int val); +void gicr_write_ipriorityr(uintptr_t base, unsigned int id, unsigned int val); + +/******************************************************************************* + * Private GICv3 function prototypes for accessing the GIC registers + * corresponding to a single interrupt ID. These functions use bitwise + * operations or appropriate register accesses to modify or return + * the bit-field corresponding the single interrupt ID. + ******************************************************************************/ +unsigned int gicd_get_igrpmodr(uintptr_t base, unsigned int id); +unsigned int gicr_get_igrpmodr0(uintptr_t base, unsigned int id); +unsigned int gicr_get_igroupr0(uintptr_t base, unsigned int id); +unsigned int gicr_get_isactiver0(uintptr_t base, unsigned int id); +void gicd_set_igrpmodr(uintptr_t base, unsigned int id); +void gicr_set_igrpmodr0(uintptr_t base, unsigned int id); +void gicr_set_isenabler0(uintptr_t base, unsigned int id); +void gicr_set_icenabler0(uintptr_t base, unsigned int id); +void gicr_set_ispendr0(uintptr_t base, unsigned int id); +void gicr_set_icpendr0(uintptr_t base, unsigned int id); +void gicr_set_igroupr0(uintptr_t base, unsigned int id); +void gicd_clr_igrpmodr(uintptr_t base, unsigned int id); +void gicr_clr_igrpmodr0(uintptr_t base, unsigned int id); +void gicr_clr_igroupr0(uintptr_t base, unsigned int id); +void gicr_set_ipriorityr(uintptr_t base, unsigned int id, unsigned int pri); + +/******************************************************************************* + * Private GICv3 helper function prototypes + ******************************************************************************/ +void gicv3_spis_configure_defaults(uintptr_t gicd_base); +void gicv3_ppi_sgi_configure_defaults(uintptr_t gicr_base); +#if !ERROR_DEPRECATED +void gicv3_secure_spis_configure(uintptr_t gicd_base, + unsigned int num_ints, + const unsigned int *sec_intr_list, + unsigned int int_grp); +void gicv3_secure_ppi_sgi_configure(uintptr_t gicr_base, + unsigned int num_ints, + const unsigned int *sec_intr_list, + unsigned int int_grp); +#endif +void gicv3_secure_ppi_sgi_configure_props(uintptr_t gicr_base, + const interrupt_prop_t *interrupt_props, + unsigned int interrupt_props_num); +unsigned int gicv3_secure_spis_configure_props(uintptr_t gicd_base, + const interrupt_prop_t *interrupt_props, + unsigned int interrupt_props_num); +void gicv3_rdistif_base_addrs_probe(uintptr_t *rdistif_base_addrs, + unsigned int rdistif_num, + uintptr_t gicr_base, + mpidr_hash_fn mpidr_to_core_pos); +void gicv3_rdistif_mark_core_awake(uintptr_t gicr_base); +void gicv3_rdistif_mark_core_asleep(uintptr_t gicr_base); + +/******************************************************************************* + * GIC Distributor interface accessors + ******************************************************************************/ +/* + * Wait for updates to : + * GICD_CTLR[2:0] - the Group Enables + * GICD_CTLR[5:4] - the ARE bits + * GICD_ICENABLERn - the clearing of enable state for SPIs + */ +static inline void gicd_wait_for_pending_write(uintptr_t gicd_base) +{ + while (gicd_read_ctlr(gicd_base) & GICD_CTLR_RWP_BIT) + ; +} + +static inline unsigned int gicd_read_pidr2(uintptr_t base) +{ + return mmio_read_32(base + GICD_PIDR2_GICV3); +} + +static inline unsigned long long gicd_read_irouter(uintptr_t base, unsigned int id) +{ + assert(id >= MIN_SPI_ID); + return mmio_read_64(base + GICD_IROUTER + (id << 3)); +} + +static inline void gicd_write_irouter(uintptr_t base, + unsigned int id, + unsigned long long affinity) +{ + assert(id >= MIN_SPI_ID); + mmio_write_64(base + GICD_IROUTER + (id << 3), affinity); +} + +static inline void gicd_clr_ctlr(uintptr_t base, + unsigned int bitmap, + unsigned int rwp) +{ + gicd_write_ctlr(base, gicd_read_ctlr(base) & ~bitmap); + if (rwp) + gicd_wait_for_pending_write(base); +} + +static inline void gicd_set_ctlr(uintptr_t base, + unsigned int bitmap, + unsigned int rwp) +{ + gicd_write_ctlr(base, gicd_read_ctlr(base) | bitmap); + if (rwp) + gicd_wait_for_pending_write(base); +} + +/******************************************************************************* + * GIC Redistributor interface accessors + ******************************************************************************/ +static inline unsigned long long gicr_read_ctlr(uintptr_t base) +{ + return mmio_read_64(base + GICR_CTLR); +} + +static inline void gicr_write_ctlr(uintptr_t base, uint64_t val) +{ + mmio_write_64(base + GICR_CTLR, val); +} + +static inline unsigned long long gicr_read_typer(uintptr_t base) +{ + return mmio_read_64(base + GICR_TYPER); +} + +static inline unsigned int gicr_read_waker(uintptr_t base) +{ + return mmio_read_32(base + GICR_WAKER); +} + +static inline void gicr_write_waker(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICR_WAKER, val); +} + +/* + * Wait for updates to : + * GICR_ICENABLER0 + * GICR_CTLR.DPG1S + * GICR_CTLR.DPG1NS + * GICR_CTLR.DPG0 + */ +static inline void gicr_wait_for_pending_write(uintptr_t gicr_base) +{ + while (gicr_read_ctlr(gicr_base) & GICR_CTLR_RWP_BIT) + ; +} + +static inline void gicr_wait_for_upstream_pending_write(uintptr_t gicr_base) +{ + while (gicr_read_ctlr(gicr_base) & GICR_CTLR_UWP_BIT) + ; +} + +/* Private implementation of Distributor power control hooks */ +void arm_gicv3_distif_pre_save(unsigned int rdist_proc_num); +void arm_gicv3_distif_post_restore(unsigned int rdist_proc_num); + +/******************************************************************************* + * GIC Re-distributor functions for accessing entire registers. + * Note: The raw register values correspond to multiple interrupt IDs and + * the number of interrupt IDs involved depends on the register accessed. + ******************************************************************************/ +static inline unsigned int gicr_read_icenabler0(uintptr_t base) +{ + return mmio_read_32(base + GICR_ICENABLER0); +} + +static inline void gicr_write_icenabler0(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICR_ICENABLER0, val); +} + +static inline unsigned int gicr_read_isenabler0(uintptr_t base) +{ + return mmio_read_32(base + GICR_ISENABLER0); +} + +static inline void gicr_write_icpendr0(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICR_ICPENDR0, val); +} + +static inline void gicr_write_isenabler0(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICR_ISENABLER0, val); +} + +static inline unsigned int gicr_read_igroupr0(uintptr_t base) +{ + return mmio_read_32(base + GICR_IGROUPR0); +} + +static inline unsigned int gicr_read_ispendr0(uintptr_t base) +{ + return mmio_read_32(base + GICR_ISPENDR0); +} + +static inline void gicr_write_ispendr0(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICR_ISPENDR0, val); +} + +static inline void gicr_write_igroupr0(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICR_IGROUPR0, val); +} + +static inline unsigned int gicr_read_igrpmodr0(uintptr_t base) +{ + return mmio_read_32(base + GICR_IGRPMODR0); +} + +static inline void gicr_write_igrpmodr0(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICR_IGRPMODR0, val); +} + +static inline unsigned int gicr_read_nsacr(uintptr_t base) +{ + return mmio_read_32(base + GICR_NSACR); +} + +static inline void gicr_write_nsacr(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICR_NSACR, val); +} + +static inline unsigned int gicr_read_isactiver0(uintptr_t base) +{ + return mmio_read_32(base + GICR_ISACTIVER0); +} + +static inline void gicr_write_isactiver0(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICR_ISACTIVER0, val); +} + +static inline unsigned int gicr_read_icfgr0(uintptr_t base) +{ + return mmio_read_32(base + GICR_ICFGR0); +} + +static inline unsigned int gicr_read_icfgr1(uintptr_t base) +{ + return mmio_read_32(base + GICR_ICFGR1); +} + +static inline void gicr_write_icfgr0(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICR_ICFGR0, val); +} + +static inline void gicr_write_icfgr1(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICR_ICFGR1, val); +} + +static inline unsigned int gicr_read_propbaser(uintptr_t base) +{ + return mmio_read_32(base + GICR_PROPBASER); +} + +static inline void gicr_write_propbaser(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICR_PROPBASER, val); +} + +static inline unsigned int gicr_read_pendbaser(uintptr_t base) +{ + return mmio_read_32(base + GICR_PENDBASER); +} + +static inline void gicr_write_pendbaser(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GICR_PENDBASER, val); +} + +/******************************************************************************* + * GIC ITS functions to read and write entire ITS registers. + ******************************************************************************/ +static inline uint32_t gits_read_ctlr(uintptr_t base) +{ + return mmio_read_32(base + GITS_CTLR); +} + +static inline void gits_write_ctlr(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GITS_CTLR, val); +} + +static inline uint64_t gits_read_cbaser(uintptr_t base) +{ + return mmio_read_64(base + GITS_CBASER); +} + +static inline void gits_write_cbaser(uintptr_t base, uint64_t val) +{ + mmio_write_32(base + GITS_CBASER, val); +} + +static inline uint64_t gits_read_cwriter(uintptr_t base) +{ + return mmio_read_64(base + GITS_CWRITER); +} + +static inline void gits_write_cwriter(uintptr_t base, uint64_t val) +{ + mmio_write_32(base + GITS_CWRITER, val); +} + +static inline uint64_t gits_read_baser(uintptr_t base, unsigned int its_table_id) +{ + assert(its_table_id < 8); + return mmio_read_64(base + GITS_BASER + (8 * its_table_id)); +} + +static inline void gits_write_baser(uintptr_t base, unsigned int its_table_id, uint64_t val) +{ + assert(its_table_id < 8); + mmio_write_64(base + GITS_BASER + (8 * its_table_id), val); +} + +/* + * Wait for Quiescent bit when GIC ITS is disabled + */ +static inline void gits_wait_for_quiescent_bit(uintptr_t gits_base) +{ + assert(!(gits_read_ctlr(gits_base) & GITS_CTLR_ENABLED_BIT)); + while ((gits_read_ctlr(gits_base) & GITS_CTLR_QUIESCENT_BIT) == 0) + ; +} + + +#endif /* __GICV3_PRIVATE_H__ */ diff --git a/drivers/arm/gpio/gpio.c b/drivers/arm/gpio/gpio.c deleted file mode 100644 index a38db948..00000000 --- a/drivers/arm/gpio/gpio.c +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (c) 2014-2015, Linaro Ltd and Contributors. All rights reserved. - * Copyright (c) 2014-2015, Hisilicon Ltd and Contributors. All rights reserved. - * - * GPIO driver for PL061 - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of ARM nor the names of its contributors may be used - * to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include <console.h> -#include <debug.h> -#include <errno.h> -#include <gpio.h> -#include <mmio.h> - -#define MAX_GPIO_DEVICES 32 -#define GPIOS_PER_DEV 8 -#define GPIO_DIR 0x400 - -#define BIT(nr) (1UL << (nr)) - -struct gpio_device_t { - unsigned int base[MAX_GPIO_DEVICES]; - unsigned int count; -}; - -static struct gpio_device_t gpio_dev; - -/* return 0 for failure */ -static unsigned int find_gc_base(unsigned int gpio) -{ - int gc; - - gc = gpio / GPIOS_PER_DEV; - if (gc >= gpio_dev.count) - return 0; - return gpio_dev.base[gc]; -} - -int gpio_direction_input(unsigned int gpio) -{ - unsigned int gc_base, offset, data; - - gc_base = find_gc_base(gpio); - if (!gc_base) - return -EINVAL; - offset = gpio % GPIOS_PER_DEV; - - data = mmio_read_8(gc_base + GPIO_DIR); - data &= ~(1 << offset); - mmio_write_8(gc_base + GPIO_DIR, data); - return 0; -} - -int gpio_direction_output(unsigned int gpio) -{ - unsigned int gc_base, offset, data; - - gc_base = find_gc_base(gpio); - if (!gc_base) - return -EINVAL; - offset = gpio % 8; - - data = mmio_read_8(gc_base + GPIO_DIR); - data |= 1 << offset; - mmio_write_8(gc_base + GPIO_DIR, data); - return 0; -} - -int gpio_get_value(unsigned int gpio) -{ - unsigned int gc_base, offset; - - gc_base = find_gc_base(gpio); - if (!gc_base) - return -EINVAL; - offset = gpio % 8; - - return !!mmio_read_8(gc_base + (BIT(offset + 2))); -} - -int gpio_set_value(unsigned int gpio, unsigned int value) -{ - unsigned int gc_base, offset; - - gc_base = find_gc_base(gpio); - if (!gc_base) - return -EINVAL; - offset = gpio % 8; - mmio_write_8(gc_base + (BIT(offset + 2)), !!value << offset); - return 0; -} - -int gpio_register_device(unsigned int base) -{ - int i; - if (gpio_dev.count > MAX_GPIO_DEVICES) - return -EINVAL; - for (i = 0; i < gpio_dev.count; i++) { - if (gpio_dev.base[i] == base) { - WARN("%s: duplicated gpio base\n", __func__); - return -EINVAL; - } - } - gpio_dev.base[gpio_dev.count] = base; - gpio_dev.count++; - return 0; -} diff --git a/drivers/arm/pl011/aarch32/pl011_console.S b/drivers/arm/pl011/aarch32/pl011_console.S new file mode 100644 index 00000000..3718fff4 --- /dev/null +++ b/drivers/arm/pl011/aarch32/pl011_console.S @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include <arch.h> +#include <asm_macros.S> +#include <pl011.h> + +/* + * Pull in generic functions to provide backwards compatibility for + * platform makefiles + */ +#include "../../../console/aarch32/console.S" + + .globl console_core_init + .globl console_core_putc + .globl console_core_getc + .globl console_core_flush + + + /* ----------------------------------------------- + * int console_core_init(uintptr_t base_addr, + * unsigned int uart_clk, unsigned int baud_rate) + * Function to initialize the console without a + * C Runtime to print debug information. This + * function will be accessed by console_init and + * crash reporting. + * In: r0 - console base address + * r1 - Uart clock in Hz + * r2 - Baud rate + * Out: return 1 on success else 0 on error + * Clobber list : r1, r2, r3 + * ----------------------------------------------- + */ +func console_core_init + /* Check the input base address */ + cmp r0, #0 + beq core_init_fail +#if !PL011_GENERIC_UART + /* Check baud rate and uart clock for sanity */ + cmp r1, #0 + beq core_init_fail + cmp r2, #0 + beq core_init_fail + /* Disable the UART before initialization */ + ldr r3, [r0, #UARTCR] + bic r3, r3, #PL011_UARTCR_UARTEN + str r3, [r0, #UARTCR] + /* Program the baudrate */ + /* Divisor = (Uart clock * 4) / baudrate */ + lsl r1, r1, #2 + udiv r2, r1, r2 + /* IBRD = Divisor >> 6 */ + lsr r1, r2, #6 + /* Write the IBRD */ + str r1, [r0, #UARTIBRD] + /* FBRD = Divisor & 0x3F */ + and r1, r2, #0x3f + /* Write the FBRD */ + str r1, [r0, #UARTFBRD] + mov r1, #PL011_LINE_CONTROL + str r1, [r0, #UARTLCR_H] + /* Clear any pending errors */ + mov r1, #0 + str r1, [r0, #UARTECR] + /* Enable tx, rx, and uart overall */ + ldr r1, =(PL011_UARTCR_RXE | PL011_UARTCR_TXE | PL011_UARTCR_UARTEN) + str r1, [r0, #UARTCR] +#endif + mov r0, #1 + bx lr +core_init_fail: + mov r0, #0 + bx lr +endfunc console_core_init + + /* -------------------------------------------------------- + * int console_core_putc(int c, uintptr_t base_addr) + * Function to output a character over the console. It + * returns the character printed on success or -1 on error. + * In : r0 - character to be printed + * r1 - console base address + * Out : return -1 on error else return character. + * Clobber list : r2 + * -------------------------------------------------------- + */ +func console_core_putc + /* Check the input parameter */ + cmp r1, #0 + beq putc_error + /* Prepend '\r' to '\n' */ + cmp r0, #0xA + bne 2f +1: + /* Check if the transmit FIFO is full */ + ldr r2, [r1, #UARTFR] + tst r2, #PL011_UARTFR_TXFF + bne 1b + mov r2, #0xD + str r2, [r1, #UARTDR] +2: + /* Check if the transmit FIFO is full */ + ldr r2, [r1, #UARTFR] + tst r2, #PL011_UARTFR_TXFF + bne 2b + str r0, [r1, #UARTDR] + bx lr +putc_error: + mov r0, #-1 + bx lr +endfunc console_core_putc + + /* --------------------------------------------- + * int console_core_getc(uintptr_t base_addr) + * Function to get a character from the console. + * It returns the character grabbed on success + * or -1 on error. + * In : r0 - console base address + * Clobber list : r0, r1 + * --------------------------------------------- + */ +func console_core_getc + cmp r0, #0 + beq getc_error +1: + /* Check if the receive FIFO is empty */ + ldr r1, [r0, #UARTFR] + tst r1, #PL011_UARTFR_RXFE + bne 1b + ldr r1, [r0, #UARTDR] + mov r0, r1 + bx lr +getc_error: + mov r0, #-1 + bx lr +endfunc console_core_getc + + /* --------------------------------------------- + * int console_core_flush(uintptr_t base_addr) + * Function to force a write of all buffered + * data that hasn't been output. + * In : r0 - console base address + * Out : return -1 on error else return 0. + * Clobber list : r0, r1 + * --------------------------------------------- + */ +func console_core_flush + cmp r0, #0 + beq flush_error + +1: + /* Loop while the transmit FIFO is busy */ + ldr r1, [r0, #UARTFR] + tst r1, #PL011_UARTFR_BUSY + bne 1b + + mov r0, #0 + bx lr +flush_error: + mov r0, #-1 + bx lr +endfunc console_core_flush diff --git a/drivers/arm/pl011/aarch64/pl011_console.S b/drivers/arm/pl011/aarch64/pl011_console.S new file mode 100644 index 00000000..8b15d565 --- /dev/null +++ b/drivers/arm/pl011/aarch64/pl011_console.S @@ -0,0 +1,154 @@ +/* + * Copyright (c) 2013-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ +#include <arch.h> +#include <asm_macros.S> +#include <pl011.h> + +/* + * Pull in generic functions to provide backwards compatibility for + * platform makefiles + */ +#include "../../../console/aarch64/console.S" + + + .globl console_core_init + .globl console_core_putc + .globl console_core_getc + .globl console_core_flush + + + /* ----------------------------------------------- + * int console_core_init(uintptr_t base_addr, + * unsigned int uart_clk, unsigned int baud_rate) + * Function to initialize the console without a + * C Runtime to print debug information. This + * function will be accessed by console_init and + * crash reporting. + * In: x0 - console base address + * w1 - Uart clock in Hz + * w2 - Baud rate + * Out: return 1 on success else 0 on error + * Clobber list : x1, x2, x3, x4 + * ----------------------------------------------- + */ +func console_core_init + /* Check the input base address */ + cbz x0, core_init_fail +#if !PL011_GENERIC_UART + /* Check baud rate and uart clock for sanity */ + cbz w1, core_init_fail + cbz w2, core_init_fail + /* Disable uart before programming */ + ldr w3, [x0, #UARTCR] + mov w4, #PL011_UARTCR_UARTEN + bic w3, w3, w4 + str w3, [x0, #UARTCR] + /* Program the baudrate */ + /* Divisor = (Uart clock * 4) / baudrate */ + lsl w1, w1, #2 + udiv w2, w1, w2 + /* IBRD = Divisor >> 6 */ + lsr w1, w2, #6 + /* Write the IBRD */ + str w1, [x0, #UARTIBRD] + /* FBRD = Divisor & 0x3F */ + and w1, w2, #0x3f + /* Write the FBRD */ + str w1, [x0, #UARTFBRD] + mov w1, #PL011_LINE_CONTROL + str w1, [x0, #UARTLCR_H] + /* Clear any pending errors */ + str wzr, [x0, #UARTECR] + /* Enable tx, rx, and uart overall */ + mov w1, #(PL011_UARTCR_RXE | PL011_UARTCR_TXE | PL011_UARTCR_UARTEN) + str w1, [x0, #UARTCR] +#endif + mov w0, #1 + ret +core_init_fail: + mov w0, wzr + ret +endfunc console_core_init + + /* -------------------------------------------------------- + * int console_core_putc(int c, uintptr_t base_addr) + * Function to output a character over the console. It + * returns the character printed on success or -1 on error. + * In : w0 - character to be printed + * x1 - console base address + * Out : return -1 on error else return character. + * Clobber list : x2 + * -------------------------------------------------------- + */ +func console_core_putc + /* Check the input parameter */ + cbz x1, putc_error + /* Prepend '\r' to '\n' */ + cmp w0, #0xA + b.ne 2f +1: + /* Check if the transmit FIFO is full */ + ldr w2, [x1, #UARTFR] + tbnz w2, #PL011_UARTFR_TXFF_BIT, 1b + mov w2, #0xD + str w2, [x1, #UARTDR] +2: + /* Check if the transmit FIFO is full */ + ldr w2, [x1, #UARTFR] + tbnz w2, #PL011_UARTFR_TXFF_BIT, 2b + str w0, [x1, #UARTDR] + ret +putc_error: + mov w0, #-1 + ret +endfunc console_core_putc + + /* --------------------------------------------- + * int console_core_getc(uintptr_t base_addr) + * Function to get a character from the console. + * It returns the character grabbed on success + * or -1 on error. + * In : x0 - console base address + * Clobber list : x0, x1 + * --------------------------------------------- + */ +func console_core_getc + cbz x0, getc_error +1: + /* Check if the receive FIFO is empty */ + ldr w1, [x0, #UARTFR] + tbnz w1, #PL011_UARTFR_RXFE_BIT, 1b + ldr w1, [x0, #UARTDR] + mov w0, w1 + ret +getc_error: + mov w0, #-1 + ret +endfunc console_core_getc + + /* --------------------------------------------- + * int console_core_flush(uintptr_t base_addr) + * Function to force a write of all buffered + * data that hasn't been output. + * In : x0 - console base address + * Out : return -1 on error else return 0. + * Clobber list : x0, x1 + * --------------------------------------------- + */ +func console_core_flush + cbz x0, flush_error + +1: + /* Loop until the transmit FIFO is empty */ + ldr w1, [x0, #UARTFR] + tbnz w1, #PL011_UARTFR_BUSY_BIT, 1b + + mov w0, #0 + ret +flush_error: + mov w0, #-1 + ret +endfunc console_core_flush diff --git a/drivers/arm/pl011/pl011_console.S b/drivers/arm/pl011/pl011_console.S index 5ff1582c..1789f153 100644 --- a/drivers/arm/pl011/pl011_console.S +++ b/drivers/arm/pl011/pl011_console.S @@ -1,178 +1,9 @@ /* - * Copyright (c) 2013-2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of ARM nor the names of its contributors may be used - * to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. + * SPDX-License-Identifier: BSD-3-Clause */ -#include <arch.h> -#include <asm_macros.S> -#include <pl011.h> - - .globl console_init - .globl console_putc - .globl console_core_init - .globl console_core_putc - .globl console_getc - - /* - * The console base is in the data section and not in .bss - * even though it is zero-init. In particular, this allows - * the console functions to start using this variable before - * the runtime memory is initialized for images which do not - * need to copy the .data section from ROM to RAM. - */ -.section .data.console_base ; .align 3 - console_base: .quad 0x0 - - /* ----------------------------------------------- - * int console_init(unsigned long base_addr, - * unsigned int uart_clk, unsigned int baud_rate) - * Function to initialize the console without a - * C Runtime to print debug information. It saves - * the console base to the data section. - * In: x0 - console base address - * w1 - Uart clock in Hz - * w2 - Baud rate - * out: return 1 on success. - * Clobber list : x1 - x3 - * ----------------------------------------------- - */ -func console_init - adrp x3, console_base - str x0, [x3, :lo12:console_base] - b console_core_init - - /* ----------------------------------------------- - * int console_core_init(unsigned long base_addr, - * unsigned int uart_clk, unsigned int baud_rate) - * Function to initialize the console without a - * C Runtime to print debug information. This - * function will be accessed by console_init and - * crash reporting. - * In: x0 - console base address - * w1 - Uart clock in Hz - * w2 - Baud rate - * Out: return 1 on success - * Clobber list : x1, x2 - * ----------------------------------------------- - */ -func console_core_init - /* Check the input base address */ - cbz x0, init_fail - /* Check baud rate and uart clock for sanity */ - cbz w1, init_fail - cbz w2, init_fail - /* Program the baudrate */ - /* Divisor = (Uart clock * 4) / baudrate */ - lsl w1, w1, #2 - udiv w2, w1, w2 - /* IBRD = Divisor >> 6 */ - lsr w1, w2, #6 - /* Write the IBRD */ - str w1, [x0, #UARTIBRD] - /* FBRD = Divisor & 0x3F */ - and w1, w2, #0x3f - /* Write the FBRD */ - str w1, [x0, #UARTFBRD] - mov w1, #PL011_LINE_CONTROL - str w1, [x0, #UARTLCR_H] - /* Clear any pending errors */ - str wzr, [x0, #UARTECR] - /* Enable tx, rx, and uart overall */ - mov w1, #(PL011_UARTCR_RXE | PL011_UARTCR_TXE | PL011_UARTCR_UARTEN) - str w1, [x0, #UARTCR] - mov w0, #1 -init_fail: - ret - - /* --------------------------------------------- - * int console_putc(int c) - * Function to output a character over the - * console. It returns the character printed on - * success or -1 on error. - * In : x0 - character to be printed - * Out : return -1 on error else return character. - * Clobber list : x1, x2 - * --------------------------------------------- - */ -func console_putc - adrp x2, console_base - ldr x1, [x2, :lo12:console_base] - b console_core_putc - - /* -------------------------------------------------------- - * int console_core_putc(int c, unsigned int base_addr) - * Function to output a character over the console. It - * returns the character printed on success or -1 on error. - * In : w0 - character to be printed - * x1 - console base address - * Out : return -1 on error else return character. - * Clobber list : x2 - * -------------------------------------------------------- - */ -func console_core_putc - /* Check the input parameter */ - cbz x1, putc_error - /* Prepend '\r' to '\n' */ - cmp w0, #0xA - b.ne 2f -1: - /* Check if the transmit FIFO is full */ - ldr w2, [x1, #UARTFR] - tbnz w2, #PL011_UARTFR_TXFF_BIT, 1b - mov w2, #0xD - str w2, [x1, #UARTDR] -2: - /* Check if the transmit FIFO is full */ - ldr w2, [x1, #UARTFR] - tbnz w2, #PL011_UARTFR_TXFF_BIT, 2b - str w0, [x1, #UARTDR] - ret -putc_error: - mov w0, #-1 - ret - /* --------------------------------------------- - * int console_getc(void) - * Function to get a character from the console. - * It returns the character grabbed on success - * or -1 on error. - * Clobber list : x0, x1 - * --------------------------------------------- - */ -func console_getc - adrp x0, console_base - ldr x1, [x0, :lo12:console_base] - cbz x1, getc_error -1: - /* Check if the receive FIFO is empty */ - ldr w0, [x1, #UARTFR] - tbnz w0, #PL011_UARTFR_RXFE_BIT, 1b - ldr w0, [x1, #UARTDR] - ret -getc_error: - mov w0, #-1 - ret +#if !ERROR_DEPRECATED +#include "./aarch64/pl011_console.S" +#endif diff --git a/drivers/arm/pl061/pl061_gpio.c b/drivers/arm/pl061/pl061_gpio.c new file mode 100644 index 00000000..af980e5e --- /dev/null +++ b/drivers/arm/pl061/pl061_gpio.c @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2016, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + * ARM PL061 GPIO Driver. + * Reference to ARM DDI 0190B document. + * + */ + +#include <assert.h> +#include <cassert.h> +#include <debug.h> +#include <errno.h> +#include <gpio.h> +#include <mmio.h> +#include <pl061_gpio.h> +#include <utils.h> + +#if !PLAT_PL061_MAX_GPIOS +# define PLAT_PL061_MAX_GPIOS 32 +#endif /* PLAT_PL061_MAX_GPIOS */ + +CASSERT(PLAT_PL061_MAX_GPIOS > 0, assert_plat_pl061_max_gpios); + +#define MAX_GPIO_DEVICES ((PLAT_PL061_MAX_GPIOS + \ + (GPIOS_PER_PL061 - 1)) / GPIOS_PER_PL061) + +#define PL061_GPIO_DIR 0x400 + +#define GPIOS_PER_PL061 8 + +static int pl061_get_direction(int gpio); +static void pl061_set_direction(int gpio, int direction); +static int pl061_get_value(int gpio); +static void pl061_set_value(int gpio, int value); + +static uintptr_t pl061_reg_base[MAX_GPIO_DEVICES]; + +static const gpio_ops_t pl061_gpio_ops = { + .get_direction = pl061_get_direction, + .set_direction = pl061_set_direction, + .get_value = pl061_get_value, + .set_value = pl061_set_value, +}; + +static int pl061_get_direction(int gpio) +{ + uintptr_t base_addr; + unsigned int data, offset; + + assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS)); + + base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061]; + offset = gpio % GPIOS_PER_PL061; + data = mmio_read_8(base_addr + PL061_GPIO_DIR); + if (data & BIT(offset)) + return GPIO_DIR_OUT; + return GPIO_DIR_IN; +} + +static void pl061_set_direction(int gpio, int direction) +{ + uintptr_t base_addr; + unsigned int data, offset; + + assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS)); + + base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061]; + offset = gpio % GPIOS_PER_PL061; + if (direction == GPIO_DIR_OUT) { + data = mmio_read_8(base_addr + PL061_GPIO_DIR) | BIT(offset); + mmio_write_8(base_addr + PL061_GPIO_DIR, data); + } else { + data = mmio_read_8(base_addr + PL061_GPIO_DIR) & ~BIT(offset); + mmio_write_8(base_addr + PL061_GPIO_DIR, data); + } +} + +/* + * The offset of GPIODATA register is 0. + * The values read from GPIODATA are determined for each bit, by the mask bit + * derived from the address used to access the data register, PADDR[9:2]. + * Bits that are 1 in the address mask cause the corresponding bits in GPIODATA + * to be read, and bits that are 0 in the address mask cause the corresponding + * bits in GPIODATA to be read as 0, regardless of their value. + */ +static int pl061_get_value(int gpio) +{ + uintptr_t base_addr; + unsigned int offset; + + assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS)); + + base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061]; + offset = gpio % GPIOS_PER_PL061; + if (mmio_read_8(base_addr + BIT(offset + 2))) + return GPIO_LEVEL_HIGH; + return GPIO_LEVEL_LOW; +} + +/* + * In order to write GPIODATA, the corresponding bits in the mask, resulting + * from the address bus, PADDR[9:2], must be HIGH. Otherwise the bit values + * remain unchanged by the write. + */ +static void pl061_set_value(int gpio, int value) +{ + uintptr_t base_addr; + int offset; + + assert((gpio >= 0) && (gpio < PLAT_PL061_MAX_GPIOS)); + + base_addr = pl061_reg_base[gpio / GPIOS_PER_PL061]; + offset = gpio % GPIOS_PER_PL061; + if (value == GPIO_LEVEL_HIGH) + mmio_write_8(base_addr + BIT(offset + 2), BIT(offset)); + else + mmio_write_8(base_addr + BIT(offset + 2), 0); +} + + +/* + * Register the PL061 GPIO controller with a base address and the offset + * of start pin in this GPIO controller. + * This function is called after pl061_gpio_ops_init(). + */ +void pl061_gpio_register(uintptr_t base_addr, int gpio_dev) +{ + assert((gpio_dev >= 0) && (gpio_dev < MAX_GPIO_DEVICES)); + + pl061_reg_base[gpio_dev] = base_addr; +} + +/* + * Initialize PL061 GPIO controller with the total GPIO numbers in SoC. + */ +void pl061_gpio_init(void) +{ + gpio_init(&pl061_gpio_ops); +} diff --git a/drivers/arm/smmu/smmu_v3.c b/drivers/arm/smmu/smmu_v3.c new file mode 100644 index 00000000..cfe8c2a4 --- /dev/null +++ b/drivers/arm/smmu/smmu_v3.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <mmio.h> +#include <smmu_v3.h> + +/* Test for pending invalidate */ +#define INVAL_PENDING(base) \ + smmuv3_read_s_init(base) & SMMU_S_INIT_INV_ALL_MASK + +static inline uint32_t smmuv3_read_s_idr1(uintptr_t base) +{ + return mmio_read_32(base + SMMU_S_IDR1); +} + +static inline uint32_t smmuv3_read_s_init(uintptr_t base) +{ + return mmio_read_32(base + SMMU_S_INIT); +} + +static inline void smmuv3_write_s_init(uintptr_t base, uint32_t value) +{ + mmio_write_32(base + SMMU_S_INIT, value); +} + +/* + * Initialize the SMMU by invalidating all secure caches and TLBs. + * + * Returns 0 on success, and -1 on failure. + */ +int smmuv3_init(uintptr_t smmu_base) +{ + uint32_t idr1_reg; + + /* + * Invalidation of secure caches and TLBs is required only if the SMMU + * supports secure state. If not, it's implementation defined as to how + * SMMU_S_INIT register is accessed. + */ + idr1_reg = smmuv3_read_s_idr1(smmu_base); + if (!((idr1_reg >> SMMU_S_IDR1_SECURE_IMPL_SHIFT) & + SMMU_S_IDR1_SECURE_IMPL_MASK)) { + return -1; + } + + /* Initiate invalidation, and wait for it to finish */ + smmuv3_write_s_init(smmu_base, SMMU_S_INIT_INV_ALL_MASK); + while (INVAL_PENDING(smmu_base)) + ; + + return 0; +} diff --git a/drivers/arm/sp804/sp804_delay_timer.c b/drivers/arm/sp804/sp804_delay_timer.c new file mode 100644 index 00000000..8f1c5c51 --- /dev/null +++ b/drivers/arm/sp804/sp804_delay_timer.c @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <delay_timer.h> +#include <mmio.h> + +uintptr_t sp804_base_addr; + +#define SP804_TIMER1_LOAD (sp804_base_addr + 0x000) +#define SP804_TIMER1_VALUE (sp804_base_addr + 0x004) +#define SP804_TIMER1_CONTROL (sp804_base_addr + 0x008) +#define SP804_TIMER1_BGLOAD (sp804_base_addr + 0x018) + +#define TIMER_CTRL_ONESHOT (1 << 0) +#define TIMER_CTRL_32BIT (1 << 1) +#define TIMER_CTRL_DIV1 (0 << 2) +#define TIMER_CTRL_DIV16 (1 << 2) +#define TIMER_CTRL_DIV256 (2 << 2) +#define TIMER_CTRL_IE (1 << 5) +#define TIMER_CTRL_PERIODIC (1 << 6) +#define TIMER_CTRL_ENABLE (1 << 7) + +/******************************************************************** + * The SP804 timer delay function + ********************************************************************/ +uint32_t sp804_get_timer_value(void) +{ + return mmio_read_32(SP804_TIMER1_VALUE); +} + +/******************************************************************** + * Initialize the 1st timer in the SP804 dual timer with a base + * address and a timer ops + ********************************************************************/ +void sp804_timer_ops_init(uintptr_t base_addr, const timer_ops_t *ops) +{ + assert(base_addr != 0); + assert(ops != 0 && ops->get_timer_value == sp804_get_timer_value); + + sp804_base_addr = base_addr; + timer_init(ops); + + /* disable timer1 */ + mmio_write_32(SP804_TIMER1_CONTROL, 0); + mmio_write_32(SP804_TIMER1_LOAD, UINT32_MAX); + mmio_write_32(SP804_TIMER1_VALUE, UINT32_MAX); + + /* enable as a free running 32-bit counter */ + mmio_write_32(SP804_TIMER1_CONTROL, + TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE); +} diff --git a/drivers/arm/sp805/sp805.c b/drivers/arm/sp805/sp805.c new file mode 100644 index 00000000..98df7e21 --- /dev/null +++ b/drivers/arm/sp805/sp805.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <mmio.h> +#include <sp805.h> +#include <stdint.h> + +/* Inline register access functions */ + +static inline void sp805_write_wdog_load(uintptr_t base, unsigned long value) +{ + mmio_write_32(base + SP805_WDOG_LOAD_OFF, value); +} + +static inline void sp805_write_wdog_ctrl(uintptr_t base, unsigned long value) +{ + mmio_write_32(base + SP805_WDOG_CTR_OFF, value); +} + +static inline void sp805_write_wdog_lock(uintptr_t base, unsigned long value) +{ + mmio_write_32(base + SP805_WDOG_LOCK_OFF, value); +} + + +/* Public API implementation */ + +void sp805_start(uintptr_t base, unsigned long ticks) +{ + sp805_write_wdog_load(base, ticks); + sp805_write_wdog_ctrl(base, SP805_CTR_RESEN | SP805_CTR_INTEN); + /* Lock registers access */ + sp805_write_wdog_lock(base, 0); +} + +void sp805_stop(uintptr_t base) +{ + sp805_write_wdog_lock(base, WDOG_UNLOCK_KEY); + sp805_write_wdog_ctrl(base, 0); +} + +void sp805_refresh(uintptr_t base, unsigned long ticks) +{ + sp805_write_wdog_lock(base, WDOG_UNLOCK_KEY); + sp805_write_wdog_load(base, ticks); + sp805_write_wdog_lock(base, 0); +} diff --git a/drivers/arm/tzc/tzc400.c b/drivers/arm/tzc/tzc400.c new file mode 100644 index 00000000..b817487c --- /dev/null +++ b/drivers/arm/tzc/tzc400.c @@ -0,0 +1,235 @@ +/* + * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <debug.h> +#include <mmio.h> +#include <stddef.h> +#include <tzc400.h> +#include "tzc_common_private.h" + +/* + * Macros which will be used by common core functions. + */ +#define TZC_400_REGION_BASE_LOW_0_OFFSET 0x100 +#define TZC_400_REGION_BASE_HIGH_0_OFFSET 0x104 +#define TZC_400_REGION_TOP_LOW_0_OFFSET 0x108 +#define TZC_400_REGION_TOP_HIGH_0_OFFSET 0x10c +#define TZC_400_REGION_ATTR_0_OFFSET 0x110 +#define TZC_400_REGION_ID_ACCESS_0_OFFSET 0x114 + +/* + * Implementation defined values used to validate inputs later. + * Filters : max of 4 ; 0 to 3 + * Regions : max of 9 ; 0 to 8 + * Address width : Values between 32 to 64 + */ +typedef struct tzc400_instance { + uintptr_t base; + uint8_t addr_width; + uint8_t num_filters; + uint8_t num_regions; +} tzc400_instance_t; + +tzc400_instance_t tzc400; + +static inline unsigned int _tzc400_read_build_config(uintptr_t base) +{ + return mmio_read_32(base + BUILD_CONFIG_OFF); +} + +static inline unsigned int _tzc400_read_gate_keeper(uintptr_t base) +{ + return mmio_read_32(base + GATE_KEEPER_OFF); +} + +static inline void _tzc400_write_gate_keeper(uintptr_t base, unsigned int val) +{ + mmio_write_32(base + GATE_KEEPER_OFF, val); +} + +/* + * Get the open status information for all filter units. + */ +#define get_gate_keeper_os(base) ((_tzc400_read_gate_keeper(base) >> \ + GATE_KEEPER_OS_SHIFT) & \ + GATE_KEEPER_OS_MASK) + + +/* Define common core functions used across different TZC peripherals. */ +DEFINE_TZC_COMMON_WRITE_ACTION(400, 400) +DEFINE_TZC_COMMON_WRITE_REGION_BASE(400, 400) +DEFINE_TZC_COMMON_WRITE_REGION_TOP(400, 400) +DEFINE_TZC_COMMON_WRITE_REGION_ATTRIBUTES(400, 400) +DEFINE_TZC_COMMON_WRITE_REGION_ID_ACCESS(400, 400) +DEFINE_TZC_COMMON_CONFIGURE_REGION0(400) +DEFINE_TZC_COMMON_CONFIGURE_REGION(400) + +static unsigned int _tzc400_get_gate_keeper(uintptr_t base, + unsigned int filter) +{ + unsigned int open_status; + + open_status = get_gate_keeper_os(base); + + return (open_status >> filter) & GATE_KEEPER_FILTER_MASK; +} + +/* This function is not MP safe. */ +static void _tzc400_set_gate_keeper(uintptr_t base, + unsigned int filter, + int val) +{ + unsigned int open_status; + + /* Upper half is current state. Lower half is requested state. */ + open_status = get_gate_keeper_os(base); + + if (val) + open_status |= (1 << filter); + else + open_status &= ~(1 << filter); + + _tzc400_write_gate_keeper(base, (open_status & GATE_KEEPER_OR_MASK) << + GATE_KEEPER_OR_SHIFT); + + /* Wait here until we see the change reflected in the TZC status. */ + while ((get_gate_keeper_os(base)) != open_status) + ; +} + +void tzc400_set_action(tzc_action_t action) +{ + assert(tzc400.base); + assert(action <= TZC_ACTION_ERR_INT); + + /* + * - Currently no handler is provided to trap an error via interrupt + * or exception. + * - The interrupt action has not been tested. + */ + _tzc400_write_action(tzc400.base, action); +} + +void tzc400_init(uintptr_t base) +{ +#if DEBUG + unsigned int tzc400_id; +#endif + unsigned int tzc400_build; + + assert(base); + tzc400.base = base; + +#if DEBUG + tzc400_id = _tzc_read_peripheral_id(base); + if (tzc400_id != TZC_400_PERIPHERAL_ID) { + ERROR("TZC-400 : Wrong device ID (0x%x).\n", tzc400_id); + panic(); + } +#endif + + /* Save values we will use later. */ + tzc400_build = _tzc400_read_build_config(tzc400.base); + tzc400.num_filters = ((tzc400_build >> BUILD_CONFIG_NF_SHIFT) & + BUILD_CONFIG_NF_MASK) + 1; + tzc400.addr_width = ((tzc400_build >> BUILD_CONFIG_AW_SHIFT) & + BUILD_CONFIG_AW_MASK) + 1; + tzc400.num_regions = ((tzc400_build >> BUILD_CONFIG_NR_SHIFT) & + BUILD_CONFIG_NR_MASK) + 1; +} + +/* + * `tzc400_configure_region0` is used to program region 0 into the TrustZone + * controller. Region 0 covers the whole address space that is not mapped + * to any other region, and is enabled on all filters; this cannot be + * changed. This function only changes the access permissions. + */ +void tzc400_configure_region0(tzc_region_attributes_t sec_attr, + unsigned int ns_device_access) +{ + assert(tzc400.base); + assert(sec_attr <= TZC_REGION_S_RDWR); + + _tzc400_configure_region0(tzc400.base, sec_attr, ns_device_access); +} + +/* + * `tzc400_configure_region` is used to program regions into the TrustZone + * controller. A region can be associated with more than one filter. The + * associated filters are passed in as a bitmap (bit0 = filter0). + * NOTE: + * Region 0 is special; it is preferable to use tzc400_configure_region0 + * for this region (see comment for that function). + */ +void tzc400_configure_region(unsigned int filters, + int region, + unsigned long long region_base, + unsigned long long region_top, + tzc_region_attributes_t sec_attr, + unsigned int nsaid_permissions) +{ + assert(tzc400.base); + + /* Do range checks on filters and regions. */ + assert(((filters >> tzc400.num_filters) == 0) && + (region >= 0) && (region < tzc400.num_regions)); + + /* + * Do address range check based on TZC configuration. A 64bit address is + * the max and expected case. + */ + assert(((region_top <= _tzc_get_max_top_addr(tzc400.addr_width)) && + (region_base < region_top))); + + /* region_base and (region_top + 1) must be 4KB aligned */ + assert(((region_base | (region_top + 1)) & (4096 - 1)) == 0); + + assert(sec_attr <= TZC_REGION_S_RDWR); + + _tzc400_configure_region(tzc400.base, filters, region, region_base, + region_top, + sec_attr, nsaid_permissions); +} + +void tzc400_enable_filters(void) +{ + unsigned int state; + unsigned int filter; + + assert(tzc400.base); + + for (filter = 0; filter < tzc400.num_filters; filter++) { + state = _tzc400_get_gate_keeper(tzc400.base, filter); + if (state) { + /* The TZC filter is already configured. Changing the + * programmer's view in an active system can cause + * unpredictable behavior therefore panic for now rather + * than try to determine whether this is safe in this + * instance. See: + * http://infocenter.arm.com/help/index.jsp?\ + * topic=/com.arm.doc.ddi0504c/CJHHECBF.html */ + ERROR("TZC-400 : Filter %d Gatekeeper already" + " enabled.\n", filter); + panic(); + } + _tzc400_set_gate_keeper(tzc400.base, filter, 1); + } +} + +void tzc400_disable_filters(void) +{ + unsigned int filter; + + assert(tzc400.base); + + /* + * We don't do the same state check as above as the Gatekeepers are + * disabled after reset. + */ + for (filter = 0; filter < tzc400.num_filters; filter++) + _tzc400_set_gate_keeper(tzc400.base, filter, 0); +} diff --git a/drivers/arm/tzc/tzc_common_private.h b/drivers/arm/tzc/tzc_common_private.h new file mode 100644 index 00000000..f092cf1f --- /dev/null +++ b/drivers/arm/tzc/tzc_common_private.h @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __TZC_COMMON_PRIVATE_H__ +#define __TZC_COMMON_PRIVATE_H__ + +#include <arch.h> +#include <arch_helpers.h> +#include <mmio.h> +#include <tzc_common.h> + +#define DEFINE_TZC_COMMON_WRITE_ACTION(fn_name, macro_name) \ + static inline void _tzc##fn_name##_write_action( \ + uintptr_t base, \ + tzc_action_t action) \ + { \ + mmio_write_32(base + TZC_##macro_name##_ACTION_OFF, \ + action); \ + } + +#define DEFINE_TZC_COMMON_WRITE_REGION_BASE(fn_name, macro_name) \ + static inline void _tzc##fn_name##_write_region_base( \ + uintptr_t base, \ + int region_no, \ + unsigned long long region_base) \ + { \ + mmio_write_32(base + \ + TZC_REGION_OFFSET( \ + TZC_##macro_name##_REGION_SIZE, \ + region_no) + \ + TZC_##macro_name##_REGION_BASE_LOW_0_OFFSET, \ + (uint32_t)region_base); \ + mmio_write_32(base + \ + TZC_REGION_OFFSET( \ + TZC_##macro_name##_REGION_SIZE, \ + region_no) + \ + TZC_##macro_name##_REGION_BASE_HIGH_0_OFFSET, \ + (uint32_t)(region_base >> 32)); \ + } + +#define DEFINE_TZC_COMMON_WRITE_REGION_TOP(fn_name, macro_name) \ + static inline void _tzc##fn_name##_write_region_top( \ + uintptr_t base, \ + int region_no, \ + unsigned long long region_top) \ + { \ + mmio_write_32(base + \ + TZC_REGION_OFFSET \ + (TZC_##macro_name##_REGION_SIZE, \ + region_no) + \ + TZC_##macro_name##_REGION_TOP_LOW_0_OFFSET, \ + (uint32_t)region_top); \ + mmio_write_32(base + \ + TZC_REGION_OFFSET( \ + TZC_##macro_name##_REGION_SIZE, \ + region_no) + \ + TZC_##macro_name##_REGION_TOP_HIGH_0_OFFSET, \ + (uint32_t)(region_top >> 32)); \ + } + +#define DEFINE_TZC_COMMON_WRITE_REGION_ATTRIBUTES(fn_name, macro_name) \ + static inline void _tzc##fn_name##_write_region_attributes( \ + uintptr_t base, \ + int region_no, \ + unsigned int attr) \ + { \ + mmio_write_32(base + \ + TZC_REGION_OFFSET( \ + TZC_##macro_name##_REGION_SIZE, \ + region_no) + \ + TZC_##macro_name##_REGION_ATTR_0_OFFSET, \ + attr); \ + } + +#define DEFINE_TZC_COMMON_WRITE_REGION_ID_ACCESS(fn_name, macro_name) \ + static inline void _tzc##fn_name##_write_region_id_access( \ + uintptr_t base, \ + int region_no, \ + unsigned int val) \ + { \ + mmio_write_32(base + \ + TZC_REGION_OFFSET( \ + TZC_##macro_name##_REGION_SIZE, \ + region_no) + \ + TZC_##macro_name##_REGION_ID_ACCESS_0_OFFSET, \ + val); \ + } + +/* + * It is used to program region 0 ATTRIBUTES and ACCESS register. + */ +#define DEFINE_TZC_COMMON_CONFIGURE_REGION0(fn_name) \ + void _tzc##fn_name##_configure_region0(uintptr_t base, \ + tzc_region_attributes_t sec_attr, \ + unsigned int ns_device_access) \ + { \ + assert(base); \ + VERBOSE("TrustZone : Configuring region 0 " \ + "(TZC Interface Base=%p sec_attr=0x%x," \ + " ns_devs=0x%x)\n", (void *)base, \ + sec_attr, ns_device_access); \ + \ + /* Set secure attributes on region 0 */ \ + _tzc##fn_name##_write_region_attributes(base, 0, \ + sec_attr << TZC_REGION_ATTR_SEC_SHIFT); \ + \ + /***************************************************/ \ + /* Specify which non-secure devices have permission*/ \ + /* to access region 0. */ \ + /***************************************************/ \ + _tzc##fn_name##_write_region_id_access(base, \ + 0, \ + ns_device_access); \ + } + +/* + * It is used to program a region from 1 to 8 in the TrustZone controller. + * NOTE: + * Region 0 is special; it is preferable to use + * ##fn_name##_configure_region0 for this region (see comment for + * that function). + */ +#define DEFINE_TZC_COMMON_CONFIGURE_REGION(fn_name) \ + void _tzc##fn_name##_configure_region(uintptr_t base, \ + unsigned int filters, \ + int region_no, \ + unsigned long long region_base, \ + unsigned long long region_top, \ + tzc_region_attributes_t sec_attr, \ + unsigned int nsaid_permissions) \ + { \ + assert(base); \ + VERBOSE("TrustZone : Configuring region " \ + "(TZC Interface Base: %p, region_no = %d)" \ + "...\n", (void *)base, region_no); \ + VERBOSE("TrustZone : ... base = %llx, top = %llx," \ + "\n", region_base, region_top);\ + VERBOSE("TrustZone : ... sec_attr = 0x%x," \ + " ns_devs = 0x%x)\n", \ + sec_attr, nsaid_permissions); \ + \ + /***************************************************/ \ + /* Inputs look ok, start programming registers. */ \ + /* All the address registers are 32 bits wide and */ \ + /* have a LOW and HIGH */ \ + /* component used to construct an address up to a */ \ + /* 64bit. */ \ + /***************************************************/ \ + _tzc##fn_name##_write_region_base(base, \ + region_no, region_base); \ + _tzc##fn_name##_write_region_top(base, \ + region_no, region_top); \ + \ + /* Enable filter to the region and set secure attributes */\ + _tzc##fn_name##_write_region_attributes(base, \ + region_no, \ + (sec_attr << TZC_REGION_ATTR_SEC_SHIFT) |\ + (filters << TZC_REGION_ATTR_F_EN_SHIFT));\ + \ + /***************************************************/ \ + /* Specify which non-secure devices have permission*/ \ + /* to access this region. */ \ + /***************************************************/ \ + _tzc##fn_name##_write_region_id_access(base, \ + region_no, \ + nsaid_permissions); \ + } + +#if ENABLE_ASSERTIONS + +static inline unsigned int _tzc_read_peripheral_id(uintptr_t base) +{ + unsigned int id; + + id = mmio_read_32(base + PID0_OFF); + /* Masks DESC part in PID1 */ + id |= ((mmio_read_32(base + PID1_OFF) & 0xF) << 8); + + return id; +} + +#ifdef AARCH32 +static inline unsigned long long _tzc_get_max_top_addr(int addr_width) +{ + /* + * Assume at least 32 bit wide address and initialize the max. + * This function doesn't use 64-bit integer arithmetic to avoid + * having to implement additional compiler library functions. + */ + unsigned long long addr_mask = 0xFFFFFFFF; + uint32_t *addr_ptr = (uint32_t *)&addr_mask; + + assert(addr_width >= 32); + + /* This logic works only on little - endian platforms */ + assert((read_sctlr() & SCTLR_EE_BIT) == 0); + + /* + * If required address width is greater than 32, populate the higher + * 32 bits of the 64 bit field with the max address. + */ + if (addr_width > 32) + *(addr_ptr + 1) = ((1 << (addr_width - 32)) - 1); + + return addr_mask; +} +#else +#define _tzc_get_max_top_addr(addr_width)\ + (UINT64_MAX >> (64 - (addr_width))) +#endif /* AARCH32 */ + +#endif /* ENABLE_ASSERTIONS */ + +#endif /* __TZC_COMMON_PRIVATE_H__ */ diff --git a/drivers/arm/tzc/tzc_dmc500.c b/drivers/arm/tzc/tzc_dmc500.c new file mode 100644 index 00000000..7350b2ce --- /dev/null +++ b/drivers/arm/tzc/tzc_dmc500.c @@ -0,0 +1,275 @@ +/* + * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <assert.h> +#include <debug.h> +#include <mmio.h> +#include <tzc_dmc500.h> +#include "tzc_common.h" +#include "tzc_common_private.h" + +/* + * Macros which will be used by common core functions. + */ +#define TZC_DMC500_REGION_BASE_LOW_0_OFFSET 0x054 +#define TZC_DMC500_REGION_BASE_HIGH_0_OFFSET 0x058 +#define TZC_DMC500_REGION_TOP_LOW_0_OFFSET 0x05C +#define TZC_DMC500_REGION_TOP_HIGH_0_OFFSET 0x060 +#define TZC_DMC500_REGION_ATTR_0_OFFSET 0x064 +#define TZC_DMC500_REGION_ID_ACCESS_0_OFFSET 0x068 + +#define TZC_DMC500_ACTION_OFF 0x50 + +/* Pointer to the tzc_dmc500_driver_data structure populated by the platform */ +static const tzc_dmc500_driver_data_t *g_driver_data; + +#define verify_region_attr(region, attr) \ + ((g_conf_regions[(region)].sec_attr == \ + ((attr) >> TZC_REGION_ATTR_SEC_SHIFT)) \ + && ((attr) & (0x1 << TZC_REGION_ATTR_F_EN_SHIFT))) + +/* + * Structure for configured regions attributes in DMC500. + */ +typedef struct tzc_dmc500_regions { + tzc_region_attributes_t sec_attr; + int is_enabled; +} tzc_dmc500_regions_t; + +/* + * Array storing the attributes of the configured regions. This array + * will be used by the `tzc_dmc500_verify_complete` to verify the flush + * completion. + */ +static tzc_dmc500_regions_t g_conf_regions[MAX_REGION_VAL + 1]; + +/* Helper Macros for making the code readable */ +#define DMC_INST_BASE_ADDR(instance) (g_driver_data->dmc_base[instance]) +#define DMC_INST_SI_BASE(instance, interface) \ + (DMC_INST_BASE_ADDR(instance) + IFACE_OFFSET(interface)) + +DEFINE_TZC_COMMON_WRITE_ACTION(_dmc500, DMC500) +DEFINE_TZC_COMMON_WRITE_REGION_BASE(_dmc500, DMC500) +DEFINE_TZC_COMMON_WRITE_REGION_TOP(_dmc500, DMC500) +DEFINE_TZC_COMMON_WRITE_REGION_ATTRIBUTES(_dmc500, DMC500) +DEFINE_TZC_COMMON_WRITE_REGION_ID_ACCESS(_dmc500, DMC500) + +DEFINE_TZC_COMMON_CONFIGURE_REGION0(_dmc500) +DEFINE_TZC_COMMON_CONFIGURE_REGION(_dmc500) + +static inline unsigned int _tzc_dmc500_read_region_attr_0( + uintptr_t dmc_si_base, + int region_no) +{ + return mmio_read_32(dmc_si_base + + TZC_REGION_OFFSET(TZC_DMC500_REGION_SIZE, region_no) + + TZC_DMC500_REGION_ATTR_0_OFFSET); +} + +static inline void _tzc_dmc500_write_flush_control(uintptr_t dmc_si_base) +{ + mmio_write_32(dmc_si_base + SI_FLUSH_CTRL_OFFSET, 1); +} + +/* + * Sets the Flush controls for all the DMC Instances and System Interfaces. + * This initiates the flush of configuration settings from the shadow + * registers to the actual configuration register. The caller should poll + * changed register to confirm update. + */ +void tzc_dmc500_config_complete(void) +{ + int dmc_inst, sys_if; + + assert(g_driver_data); + + for (dmc_inst = 0; dmc_inst < g_driver_data->dmc_count; dmc_inst++) { + assert(DMC_INST_BASE_ADDR(dmc_inst)); + for (sys_if = 0; sys_if < MAX_SYS_IF_COUNT; sys_if++) + _tzc_dmc500_write_flush_control( + DMC_INST_SI_BASE(dmc_inst, sys_if)); + } +} + +/* + * This function reads back the secure attributes from the configuration + * register for each DMC Instance and System Interface and compares it with + * the configured value. The successful verification of the region attributes + * confirms that the flush operation has completed. + * If the verification fails, the caller is expected to invoke this API again + * till it succeeds. + * Returns 0 on success and 1 on failure. + */ +int tzc_dmc500_verify_complete(void) +{ + int dmc_inst, sys_if, region_no; + unsigned int attr; + + assert(g_driver_data); + /* Region 0 must be configured */ + assert(g_conf_regions[0].is_enabled); + + /* Iterate over all configured regions */ + for (region_no = 0; region_no <= MAX_REGION_VAL; region_no++) { + if (!g_conf_regions[region_no].is_enabled) + continue; + for (dmc_inst = 0; dmc_inst < g_driver_data->dmc_count; + dmc_inst++) { + assert(DMC_INST_BASE_ADDR(dmc_inst)); + for (sys_if = 0; sys_if < MAX_SYS_IF_COUNT; + sys_if++) { + attr = _tzc_dmc500_read_region_attr_0( + DMC_INST_SI_BASE(dmc_inst, sys_if), + region_no); + VERBOSE("Verifying DMC500 region:%d" + " dmc_inst:%d sys_if:%d attr:%x\n", + region_no, dmc_inst, sys_if, attr); + if (!verify_region_attr(region_no, attr)) + return 1; + } + } + } + + return 0; +} + +/* + * `tzc_dmc500_configure_region0` is used to program region 0 in both the + * system interfaces of all the DMC-500 instances. Region 0 covers the whole + * address space that is not mapped to any other region for a system interface, + * and is always enabled; this cannot be changed. This function only changes + * the access permissions. + */ +void tzc_dmc500_configure_region0(tzc_region_attributes_t sec_attr, + unsigned int nsaid_permissions) +{ + int dmc_inst, sys_if; + + /* Assert if DMC-500 is not initialized */ + assert(g_driver_data); + + /* Configure region_0 in all DMC instances */ + for (dmc_inst = 0; dmc_inst < g_driver_data->dmc_count; dmc_inst++) { + assert(DMC_INST_BASE_ADDR(dmc_inst)); + for (sys_if = 0; sys_if < MAX_SYS_IF_COUNT; sys_if++) + _tzc_dmc500_configure_region0( + DMC_INST_SI_BASE(dmc_inst, sys_if), + sec_attr, nsaid_permissions); + } + + g_conf_regions[0].sec_attr = sec_attr; + g_conf_regions[0].is_enabled = 1; +} + +/* + * `tzc_dmc500_configure_region` is used to program a region into all system + * interfaces of all the DMC instances. + * NOTE: + * Region 0 is special; it is preferable to use tzc_dmc500_configure_region0 + * for this region (see comment for that function). + */ +void tzc_dmc500_configure_region(int region_no, + unsigned long long region_base, + unsigned long long region_top, + tzc_region_attributes_t sec_attr, + unsigned int nsaid_permissions) +{ + int dmc_inst, sys_if; + + assert(g_driver_data); + /* Do range checks on regions. */ + assert(region_no >= 0 && region_no <= MAX_REGION_VAL); + + /* + * Do address range check based on DMC-TZ configuration. A 43bit address + * is the max and expected case. + */ + assert(((region_top <= _tzc_get_max_top_addr(43)) && + (region_base < region_top))); + + /* region_base and (region_top + 1) must be 4KB aligned */ + assert(((region_base | (region_top + 1)) & (4096 - 1)) == 0); + + for (dmc_inst = 0; dmc_inst < g_driver_data->dmc_count; dmc_inst++) { + assert(DMC_INST_BASE_ADDR(dmc_inst)); + for (sys_if = 0; sys_if < MAX_SYS_IF_COUNT; sys_if++) + _tzc_dmc500_configure_region( + DMC_INST_SI_BASE(dmc_inst, sys_if), + TZC_DMC500_REGION_ATTR_F_EN_MASK, + region_no, region_base, region_top, + sec_attr, nsaid_permissions); + } + + g_conf_regions[region_no].sec_attr = sec_attr; + g_conf_regions[region_no].is_enabled = 1; +} + +/* Sets the action value for all the DMC instances */ +void tzc_dmc500_set_action(tzc_action_t action) +{ + int dmc_inst; + + assert(g_driver_data); + + for (dmc_inst = 0; dmc_inst < g_driver_data->dmc_count; dmc_inst++) { + assert(DMC_INST_BASE_ADDR(dmc_inst)); + /* + * - Currently no handler is provided to trap an error via + * interrupt or exception. + * - The interrupt action has not been tested. + */ + _tzc_dmc500_write_action(DMC_INST_BASE_ADDR(dmc_inst), action); + } +} + +/* + * A DMC-500 instance must be present at each base address provided by the + * platform. It also expects platform to pass at least one instance of + * DMC-500. + */ +static void validate_plat_driver_data( + const tzc_dmc500_driver_data_t *plat_driver_data) +{ +#if ENABLE_ASSERTIONS + int i; + unsigned int dmc_id; + uintptr_t dmc_base; + + assert(plat_driver_data); + assert(plat_driver_data->dmc_count > 0 && + (plat_driver_data->dmc_count <= MAX_DMC_COUNT)); + + for (i = 0; i < plat_driver_data->dmc_count; i++) { + dmc_base = plat_driver_data->dmc_base[i]; + assert(dmc_base); + + dmc_id = _tzc_read_peripheral_id(dmc_base); + assert(dmc_id == DMC500_PERIPHERAL_ID); + } +#endif /* ENABLE_ASSERTIONS */ +} + + +/* + * Initializes the base address and count of DMC instances. + * + * Note : Only pointer to plat_driver_data is saved, so it is caller's + * responsibility to keep it valid until the driver is used. + */ +void tzc_dmc500_driver_init(const tzc_dmc500_driver_data_t *plat_driver_data) +{ + /* Check valid pointer is passed */ + assert(plat_driver_data); + + /* + * NOTE: This driver expects the DMC-500 controller is already in + * READY state. Hence, it uses the reconfiguration method for + * programming TrustZone regions + */ + /* Validates the information passed by platform */ + validate_plat_driver_data(plat_driver_data); + g_driver_data = plat_driver_data; +} diff --git a/drivers/arm/tzc400/tzc400.c b/drivers/arm/tzc400/tzc400.c index df52c9cf..ff2ebc70 100644 --- a/drivers/arm/tzc400/tzc400.c +++ b/drivers/arm/tzc400/tzc400.c @@ -1,307 +1,11 @@ /* - * Copyright (c) 2014, ARM Limited and Contributors. All rights reserved. + * Copyright (c) 2014-2016, ARM Limited and Contributors. All rights reserved. * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * Redistributions of source code must retain the above copyright notice, this - * list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * - * Neither the name of ARM nor the names of its contributors may be used - * to endorse or promote products derived from this software without specific - * prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - */ - -#include <assert.h> -#include <debug.h> -#include <mmio.h> -#include <stddef.h> -#include <tzc400.h> - -/* - * Implementation defined values used to validate inputs later. - * Filters : max of 4 ; 0 to 3 - * Regions : max of 9 ; 0 to 8 - * Address width : Values between 32 to 64 - */ -typedef struct tzc_instance { - uint64_t base; - uint8_t addr_width; - uint8_t num_filters; - uint8_t num_regions; -} tzc_instance_t; - -tzc_instance_t tzc; - - -static inline uint32_t tzc_read_build_config(uint64_t base) -{ - return mmio_read_32(base + BUILD_CONFIG_OFF); -} - -static inline uint32_t tzc_read_gate_keeper(uint64_t base) -{ - return mmio_read_32(base + GATE_KEEPER_OFF); -} - -static inline void tzc_write_gate_keeper(uint64_t base, uint32_t val) -{ - mmio_write_32(base + GATE_KEEPER_OFF, val); -} - -static inline void tzc_write_action(uint64_t base, tzc_action_t action) -{ - mmio_write_32(base + ACTION_OFF, action); -} - -static inline void tzc_write_region_base_low(uint64_t base, - uint32_t region, - uint32_t val) -{ - mmio_write_32(base + REGION_BASE_LOW_OFF + - REGION_NUM_OFF(region), val); -} - -static inline void tzc_write_region_base_high(uint64_t base, - uint32_t region, - uint32_t val) -{ - mmio_write_32(base + REGION_BASE_HIGH_OFF + - REGION_NUM_OFF(region), val); -} - -static inline void tzc_write_region_top_low(uint64_t base, - uint32_t region, - uint32_t val) -{ - mmio_write_32(base + REGION_TOP_LOW_OFF + - REGION_NUM_OFF(region), val); -} - -static inline void tzc_write_region_top_high(uint64_t base, - uint32_t region, - uint32_t val) -{ - mmio_write_32(base + REGION_TOP_HIGH_OFF + - REGION_NUM_OFF(region), val); -} - -static inline void tzc_write_region_attributes(uint64_t base, - uint32_t region, - uint32_t val) -{ - mmio_write_32(base + REGION_ATTRIBUTES_OFF + - REGION_NUM_OFF(region), val); -} - -static inline void tzc_write_region_id_access(uint64_t base, - uint32_t region, - uint32_t val) -{ - mmio_write_32(base + REGION_ID_ACCESS_OFF + - REGION_NUM_OFF(region), val); -} - -static uint32_t tzc_read_component_id(uint64_t base) -{ - uint32_t id; - - id = mmio_read_8(base + CID0_OFF); - id |= (mmio_read_8(base + CID1_OFF) << 8); - id |= (mmio_read_8(base + CID2_OFF) << 16); - id |= (mmio_read_8(base + CID3_OFF) << 24); - - return id; -} - -static uint32_t tzc_get_gate_keeper(uint64_t base, uint8_t filter) -{ - uint32_t tmp; - - tmp = (tzc_read_gate_keeper(base) >> GATE_KEEPER_OS_SHIFT) & - GATE_KEEPER_OS_MASK; - - return (tmp >> filter) & GATE_KEEPER_FILTER_MASK; -} - -/* This function is not MP safe. */ -static void tzc_set_gate_keeper(uint64_t base, uint8_t filter, uint32_t val) -{ - uint32_t tmp; - - /* Upper half is current state. Lower half is requested state. */ - tmp = (tzc_read_gate_keeper(base) >> GATE_KEEPER_OS_SHIFT) & - GATE_KEEPER_OS_MASK; - - if (val) - tmp |= (1 << filter); - else - tmp &= ~(1 << filter); - - tzc_write_gate_keeper(base, (tmp & GATE_KEEPER_OR_MASK) << - GATE_KEEPER_OR_SHIFT); - - /* Wait here until we see the change reflected in the TZC status. */ - while (((tzc_read_gate_keeper(base) >> GATE_KEEPER_OS_SHIFT) & - GATE_KEEPER_OS_MASK) != tmp) - ; -} - - -void tzc_init(uint64_t base) -{ - uint32_t tzc_id, tzc_build; - - assert(base); - tzc.base = base; - - /* - * We expect to see a tzc400. Check component ID. The TZC-400 TRM shows - * component ID is expected to be "0xB105F00D". - */ - tzc_id = tzc_read_component_id(tzc.base); - if (tzc_id != TZC400_COMPONENT_ID) { - ERROR("TZC : Wrong device ID (0x%x).\n", tzc_id); - panic(); - } - - /* Save values we will use later. */ - tzc_build = tzc_read_build_config(tzc.base); - tzc.num_filters = ((tzc_build >> BUILD_CONFIG_NF_SHIFT) & - BUILD_CONFIG_NF_MASK) + 1; - tzc.addr_width = ((tzc_build >> BUILD_CONFIG_AW_SHIFT) & - BUILD_CONFIG_AW_MASK) + 1; - tzc.num_regions = ((tzc_build >> BUILD_CONFIG_NR_SHIFT) & - BUILD_CONFIG_NR_MASK) + 1; -} - - -/* - * `tzc_configure_region` is used to program regions into the TrustZone - * controller. A region can be associated with more than one filter. The - * associated filters are passed in as a bitmap (bit0 = filter0). - * NOTE: - * The region 0 covers the whole address space and is enabled on all filters, - * this cannot be changed. It is, however, possible to change some region 0 - * permissions. + * SPDX-License-Identifier: BSD-3-Clause */ -void tzc_configure_region(uint32_t filters, - uint8_t region, - uint64_t region_base, - uint64_t region_top, - tzc_region_attributes_t sec_attr, - uint32_t ns_device_access) -{ - assert(tzc.base); - - /* Do range checks on filters and regions. */ - assert(((filters >> tzc.num_filters) == 0) && - (region < tzc.num_regions)); - - /* - * Do address range check based on TZC configuration. A 64bit address is - * the max and expected case. - */ - assert(((region_top <= (UINT64_MAX >> (64 - tzc.addr_width))) && - (region_base < region_top))); - - /* region_base and (region_top + 1) must be 4KB aligned */ - assert(((region_base | (region_top + 1)) & (4096 - 1)) == 0); - - assert(sec_attr <= TZC_REGION_S_RDWR); - - /* - * Inputs look ok, start programming registers. - * All the address registers are 32 bits wide and have a LOW and HIGH - * component used to construct a up to a 64bit address. - */ - tzc_write_region_base_low(tzc.base, region, - (uint32_t)(region_base)); - tzc_write_region_base_high(tzc.base, region, - (uint32_t)(region_base >> 32)); - - tzc_write_region_top_low(tzc.base, region, - (uint32_t)(region_top)); - tzc_write_region_top_high(tzc.base, region, - (uint32_t)(region_top >> 32)); - - /* Assign the region to a filter and set secure attributes */ - tzc_write_region_attributes(tzc.base, region, - (sec_attr << REG_ATTR_SEC_SHIFT) | filters); - - /* - * Specify which non-secure devices have permission to access this - * region. - */ - tzc_write_region_id_access(tzc.base, region, ns_device_access); -} - - -void tzc_set_action(tzc_action_t action) -{ - assert(tzc.base); - - /* - * - Currently no handler is provided to trap an error via interrupt - * or exception. - * - The interrupt action has not been tested. - */ - tzc_write_action(tzc.base, action); -} - - -void tzc_enable_filters(void) -{ - uint32_t state; - uint32_t filter; - - assert(tzc.base); - - for (filter = 0; filter < tzc.num_filters; filter++) { - state = tzc_get_gate_keeper(tzc.base, filter); - if (state) { - /* The TZC filter is already configured. Changing the - * programmer's view in an active system can cause - * unpredictable behavior therefore panic for now rather - * than try to determine whether this is safe in this - * instance. See: - * http://infocenter.arm.com/help/index.jsp?\ - * topic=/com.arm.doc.ddi0504c/CJHHECBF.html */ - ERROR("TZC : Filter %d Gatekeeper already enabled.\n", - filter); - panic(); - } - tzc_set_gate_keeper(tzc.base, filter, 1); - } -} - - -void tzc_disable_filters(void) -{ - uint32_t filter; - - assert(tzc.base); - /* - * We don't do the same state check as above as the Gatekeepers are - * disabled after reset. - */ - for (filter = 0; filter < tzc.num_filters; filter++) - tzc_set_gate_keeper(tzc.base, filter, 0); -} +#if ERROR_DEPRECATED +#error "Using deprecated TZC-400 source file" +#else +#include "../tzc/tzc400.c" +#endif /* ERROR_DEPRECATED */ |