tzc_dmc620.c 5.37 KB
Newer Older
1
2
3
4
5
6
7
/*
 * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */

#include <assert.h>
8
9
10
11

#include <common/debug.h>
#include <drivers/arm/tzc_dmc620.h>
#include <lib/mmio.h>
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176

/* 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");
}