diff --git a/drivers/arm/tzc/tzc_dmc620.c b/drivers/arm/tzc/tzc_dmc620.c new file mode 100644 index 0000000..4abd080 --- /dev/null +++ b/drivers/arm/tzc/tzc_dmc620.c @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include + +/* Mask to extract bit 31 to 16 */ +#define MASK_31_16 UINT64_C(0x0000ffff0000) +/* Mask to extract bit 47 to 32 */ +#define MASK_47_32 UINT64_C(0xffff00000000) + +/* Helper macro for getting dmc_base addr of a dmc_inst */ +#define DMC_BASE(plat_data, dmc_inst) \ + ((uintptr_t)(plat_data->dmc_base[dmc_inst])) + +/* Pointer to the tzc_dmc620_config_data structure populated by the platform */ +static const tzc_dmc620_config_data_t *g_plat_config_data; + +#if ENABLE_ASSERTIONS +/* + * Helper function to check if the DMC-620 instance is present at the + * base address provided by the platform and also check if at least + * one dmc instance is present. + */ +static void tzc_dmc620_validate_plat_driver_data( + const tzc_dmc620_driver_data_t *plat_driver_data) +{ + uint8_t dmc_inst, dmc_count; + unsigned int dmc_id; + uintptr_t base; + + assert(plat_driver_data != NULL); + + dmc_count = plat_driver_data->dmc_count; + assert(dmc_count > 0U); + + for (dmc_inst = 0U; dmc_inst < dmc_count; dmc_inst++) { + base = DMC_BASE(plat_driver_data, dmc_inst); + dmc_id = mmio_read_32(base + DMC620_PERIPHERAL_ID_0); + assert(dmc_id == DMC620_PERIPHERAL_ID_0_VALUE); + } +} +#endif + +/* + * Program a region with region base and region top addresses of all + * DMC-620 instances. + */ +static void tzc_dmc620_configure_region(int region_no, + unsigned long long region_base, + unsigned long long region_top, + unsigned int sec_attr) +{ + uint32_t min_31_00, min_47_32; + uint32_t max_31_00, max_47_32; + uint8_t dmc_inst, dmc_count; + uintptr_t base; + const tzc_dmc620_driver_data_t *plat_driver_data; + + plat_driver_data = g_plat_config_data->plat_drv_data; + assert(plat_driver_data != NULL); + + /* Do range checks on regions. */ + assert((region_no >= 0U) && (region_no <= DMC620_ACC_ADDR_COUNT)); + + /* region_base and (region_top + 1) must be 4KB aligned */ + assert(((region_base | (region_top + 1U)) & (4096U - 1U)) == 0U); + + dmc_count = plat_driver_data->dmc_count; + for (dmc_inst = 0U; dmc_inst < dmc_count; dmc_inst++) { + min_31_00 = (region_base & MASK_31_16) | sec_attr; + min_47_32 = (region_base & MASK_47_32) + >> DMC620_ACC_ADDR_WIDTH; + max_31_00 = (region_top & MASK_31_16); + max_47_32 = (region_top & MASK_47_32) + >> DMC620_ACC_ADDR_WIDTH; + + /* Extract the base address of the DMC-620 instance */ + base = DMC_BASE(plat_driver_data, dmc_inst); + /* Configure access address region registers */ + mmio_write_32(base + DMC620_ACC_ADDR_MIN_31_00_NEXT(region_no), + min_31_00); + mmio_write_32(base + DMC620_ACC_ADDR_MIN_47_32_NEXT(region_no), + min_47_32); + mmio_write_32(base + DMC620_ACC_ADDR_MAX_31_00_NEXT(region_no), + max_31_00); + mmio_write_32(base + DMC620_ACC_ADDR_MAX_47_32_NEXT(region_no), + max_47_32); + } +} + +/* + * Set the action value for all the DMC-620 instances. + */ +static void tzc_dmc620_set_action(void) +{ + uint8_t dmc_inst, dmc_count; + uintptr_t base; + const tzc_dmc620_driver_data_t *plat_driver_data; + + plat_driver_data = g_plat_config_data->plat_drv_data; + dmc_count = plat_driver_data->dmc_count; + for (dmc_inst = 0U; dmc_inst < dmc_count; dmc_inst++) { + /* Extract the base address of the DMC-620 instance */ + base = DMC_BASE(plat_driver_data, dmc_inst); + /* Switch to READY */ + mmio_write_32(base + DMC620_MEMC_CMD, DMC620_MEMC_CMD_GO); + mmio_write_32(base + DMC620_MEMC_CMD, DMC620_MEMC_CMD_EXECUTE); + } +} + +/* + * Verify whether the DMC-620 configuration is complete by reading back + * configuration registers and comparing it with the configured value. If + * configuration is incomplete, loop till the configured value is reflected in + * the register. + */ +static void tzc_dmc620_verify_complete(void) +{ + uint8_t dmc_inst, dmc_count; + uintptr_t base; + const tzc_dmc620_driver_data_t *plat_driver_data; + + plat_driver_data = g_plat_config_data->plat_drv_data; + dmc_count = plat_driver_data->dmc_count; + for (dmc_inst = 0U; dmc_inst < dmc_count; dmc_inst++) { + /* Extract the base address of the DMC-620 instance */ + base = DMC_BASE(plat_driver_data, dmc_inst); + while ((mmio_read_32(base + DMC620_MEMC_STATUS) & + DMC620_MEMC_CMD_MASK) != DMC620_MEMC_CMD_GO) + continue; + } +} + +/* + * Initialize the DMC-620 TrustZone Controller using the region configuration + * supplied by the platform. The DMC620 controller should be enabled elsewhere + * before invoking this function. + */ +void arm_tzc_dmc620_setup(const tzc_dmc620_config_data_t *plat_config_data) +{ + int i; + + /* Check if valid pointer is passed */ + assert(plat_config_data != NULL); + + /* + * Check if access address count passed by the platform is less than or + * equal to DMC620's access address count + */ + assert(plat_config_data->acc_addr_count <= DMC620_ACC_ADDR_COUNT); + +#if ENABLE_ASSERTIONS + /* Validates the information passed by platform */ + tzc_dmc620_validate_plat_driver_data(plat_config_data->plat_drv_data); +#endif + + g_plat_config_data = plat_config_data; + + INFO("Configuring DMC-620 TZC settings\n"); + for (i = 0U; i < g_plat_config_data->acc_addr_count; i++) + tzc_dmc620_configure_region(i, + g_plat_config_data->plat_acc_addr_data[i].region_base, + g_plat_config_data->plat_acc_addr_data[i].region_top, + g_plat_config_data->plat_acc_addr_data[i].sec_attr); + + tzc_dmc620_set_action(); + tzc_dmc620_verify_complete(); + INFO("DMC-620 TZC setup completed\n"); +} diff --git a/include/drivers/arm/tzc_dmc620.h b/include/drivers/arm/tzc_dmc620.h new file mode 100644 index 0000000..074bbc1 --- /dev/null +++ b/include/drivers/arm/tzc_dmc620.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef TZC_DMC620_H +#define TZC_DMC620_H + +#include + +/* DMC-620 memc register offsets */ +#define DMC620_MEMC_STATUS U(0x0000) +#define DMC620_MEMC_CMD U(0x0008) + +/* Mask value to check the status of memc_cmd register */ +#define DMC620_MEMC_CMD_MASK U(0x00000007) + +/* memc_cmd register's action values */ +#define DMC620_MEMC_CMD_GO U(0x00000003) +#define DMC620_MEMC_CMD_EXECUTE U(0x00000004) + +/* Address offsets of access address next region 0 registers */ +#define DMC620_ACC_ADDR_MIN_31_00_NEXT_BASE U(0x0080) +#define DMC620_ACC_ADDR_MIN_47_32_NEXT_BASE U(0x0084) +#define DMC620_ACC_ADDR_MAX_31_00_NEXT_BASE U(0x0088) +#define DMC620_ACC_ADDR_MAX_47_32_NEXT_BASE U(0x008c) + +/* Length of one block of access address next register region */ +#define DMC620_ACC_ADDR_NEXT_SIZE U(0x0010) + +/* Address offsets of access address next registers */ +#define DMC620_ACC_ADDR_MIN_31_00_NEXT(region_no) \ + (DMC620_ACC_ADDR_MIN_31_00_NEXT_BASE + \ + (region_no * DMC620_ACC_ADDR_NEXT_SIZE)) +#define DMC620_ACC_ADDR_MIN_47_32_NEXT(region_no) \ + (DMC620_ACC_ADDR_MIN_47_32_NEXT_BASE + \ + (region_no * DMC620_ACC_ADDR_NEXT_SIZE)) +#define DMC620_ACC_ADDR_MAX_31_00_NEXT(region_no) \ + (DMC620_ACC_ADDR_MAX_31_00_NEXT_BASE + \ + (region_no * DMC620_ACC_ADDR_NEXT_SIZE)) +#define DMC620_ACC_ADDR_MAX_47_32_NEXT(region_no) \ + (DMC620_ACC_ADDR_MAX_47_32_NEXT_BASE + \ + (region_no * DMC620_ACC_ADDR_NEXT_SIZE)) + +/* Number of TZC address regions in DMC-620 */ +#define DMC620_ACC_ADDR_COUNT U(8) +/* Width of access address registers */ +#define DMC620_ACC_ADDR_WIDTH U(32) + +/* Peripheral ID registers offsets */ +#define DMC620_PERIPHERAL_ID_0 U(0x1fe0) + +/* Default values in id registers */ +#define DMC620_PERIPHERAL_ID_0_VALUE U(0x00000054) + +/* Secure access region attributes. */ +#define TZC_DMC620_REGION_NS_RD U(0x00000001) +#define TZC_DMC620_REGION_NS_WR U(0x00000002) +#define TZC_DMC620_REGION_NS_RDWR \ + (TZC_DMC620_REGION_NS_RD | TZC_DMC620_REGION_NS_WR) +#define TZC_DMC620_REGION_S_RD U(0x00000004) +#define TZC_DMC620_REGION_S_WR U(0x00000008) +#define TZC_DMC620_REGION_S_RDWR \ + (TZC_DMC620_REGION_S_RD | TZC_DMC620_REGION_S_WR) +#define TZC_DMC620_REGION_S_NS_RDWR \ + (TZC_DMC620_REGION_NS_RDWR | TZC_DMC620_REGION_S_RDWR) + +/* + * Contains pointer to the base addresses of all the DMC-620 instances. + * 'dmc_count' specifies the number of DMC base addresses contained in the + * array pointed to by dmc_base. + */ +typedef struct tzc_dmc620_driver_data { + const uintptr_t *dmc_base; + const unsigned int dmc_count; +} tzc_dmc620_driver_data_t; + +/* + * Contains region base, region top addresses and corresponding attributes + * for configuring TZC access region registers. + */ +typedef struct tzc_dmc620_acc_addr_data { + const unsigned long long region_base; + const unsigned long long region_top; + const unsigned int sec_attr; +} tzc_dmc620_acc_addr_data_t; + +/* + * Contains platform specific data for configuring TZC region base and + * region top address. 'acc_addr_count' specifies the number of + * valid entries in 'plat_acc_addr_data' array. + */ +typedef struct tzc_dmc620_config_data { + const tzc_dmc620_driver_data_t *plat_drv_data; + const tzc_dmc620_acc_addr_data_t *plat_acc_addr_data; + const uint8_t acc_addr_count; +} tzc_dmc620_config_data_t; + +/* Function prototypes */ +void arm_tzc_dmc620_setup(const tzc_dmc620_config_data_t *plat_config_data); + +#endif /* TZC_DMC620_H */ +