| /* |
| * Copyright (c) 2018-2020, ARM Limited and Contributors. All rights reserved. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| */ |
| |
| #include <assert.h> |
| |
| #include <common/debug.h> |
| #include <drivers/arm/tzc_dmc620.h> |
| #include <lib/mmio.h> |
| |
| /* 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) |
| { |
| unsigned int dmc_inst, dmc_count, 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; |
| unsigned int 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 >= 0) && (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 = (uint32_t)((region_base & MASK_31_16) | sec_attr); |
| min_47_32 = (uint32_t)((region_base & MASK_47_32) |
| >> DMC620_ACC_ADDR_WIDTH); |
| max_31_00 = (uint32_t)(region_top & MASK_31_16); |
| max_47_32 = (uint32_t)((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) |
| { |
| unsigned int 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) |
| { |
| unsigned int 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) |
| { |
| uint8_t 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"); |
| } |