drivers/tzc-dmc620: add driver to setup DMC-620 TZC controller
authorVijayenthiran Subramaniam <vijayenthiran.subramaniam@arm.com>
Mon, 22 Oct 2018 13:06:35 +0000 (18:36 +0530)
committerVijayenthiran Subramaniam <vijayenthiran.subramaniam@arm.com>
Wed, 21 Nov 2018 13:55:35 +0000 (19:25 +0530)
ARM CoreLink DMC-620 Dynamic Memory Controller includes a TZC controller
to setup secure or non-secure regions of DRAM memory. The TZC controller
allows to setup upto eight such regions of memory in DRAM. This driver
provides helper functions to setup the TZC controller within DMC-620.

Change-Id: Iee7692417c2080052bdb7b1c2873a024bc5d1d10
Signed-off-by: Vijayenthiran Subramaniam <vijayenthiran.subramaniam@arm.com>
drivers/arm/tzc/tzc_dmc620.c [new file with mode: 0644]
include/drivers/arm/tzc_dmc620.h [new file with mode: 0644]

diff --git a/drivers/arm/tzc/tzc_dmc620.c b/drivers/arm/tzc/tzc_dmc620.c
new file mode 100644 (file)
index 0000000..4abd080
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <assert.h>
+#include <debug.h>
+#include <mmio.h>
+#include <tzc_dmc620.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)
+{
+       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 (file)
index 0000000..074bbc1
--- /dev/null
@@ -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 <utils_def.h>
+
+/* 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 */
+