aboutsummaryrefslogtreecommitdiff
path: root/drivers/marvell/ccu.c
blob: ecf5091b48018c866570dc6e848e6d9c74ce14fb (plain)
1
2
3
4
5
6
7
8
9
10
11
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
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
/*
 * Copyright (C) 2018 Marvell International Ltd.
 *
 * SPDX-License-Identifier:     BSD-3-Clause
 * https://spdx.org/licenses
 */

/* CCU unit device driver for Marvell AP807, AP807 and AP810 SoCs */

#include <common/debug.h>
#include <drivers/marvell/ccu.h>
#include <lib/mmio.h>

#include <armada_common.h>
#include <mvebu.h>
#include <mvebu_def.h>

#if LOG_LEVEL >= LOG_LEVEL_INFO
#define DEBUG_ADDR_MAP
#endif

/* common defines */
#define WIN_ENABLE_BIT			(0x1)
/* Physical address of the base of the window = {AddrLow[19:0],20'h0} */
#define ADDRESS_SHIFT			(20 - 4)
#define ADDRESS_MASK			(0xFFFFFFF0)
#define CCU_WIN_ALIGNMENT		(0x100000)

#define IS_DRAM_TARGET(tgt)		((((tgt) == DRAM_0_TID) || \
					((tgt) == DRAM_1_TID) || \
					((tgt) == RAR_TID)) ? 1 : 0)

#define CCU_RGF(win)			(MVEBU_CCU_BASE(MVEBU_AP0) +	\
					 0x90 + 4 * (win))

/* For storage of CR, SCR, ALR, AHR abd GCR */
static uint32_t ccu_regs_save[MVEBU_CCU_MAX_WINS * 4 + 1];

#ifdef DEBUG_ADDR_MAP
static void dump_ccu(int ap_index)
{
	uint32_t win_id, win_cr, alr, ahr;
	uint8_t target_id;
	uint64_t start, end;

	/* Dump all AP windows */
	printf("\tbank  target     start              end\n");
	printf("\t----------------------------------------------------\n");
	for (win_id = 0; win_id < MVEBU_CCU_MAX_WINS; win_id++) {
		win_cr = mmio_read_32(CCU_WIN_CR_OFFSET(ap_index, win_id));
		if (win_cr & WIN_ENABLE_BIT) {
			target_id = (win_cr >> CCU_TARGET_ID_OFFSET) &
				     CCU_TARGET_ID_MASK;
			alr = mmio_read_32(CCU_WIN_ALR_OFFSET(ap_index,
							      win_id));
			ahr = mmio_read_32(CCU_WIN_AHR_OFFSET(ap_index,
							      win_id));
			start = ((uint64_t)alr << ADDRESS_SHIFT);
			end = (((uint64_t)ahr + 0x10) << ADDRESS_SHIFT);
			printf("\tccu%d    %02x     0x%016llx 0x%016llx\n",
			       win_id, target_id, start, end);
		}
	}
	win_cr = mmio_read_32(CCU_WIN_GCR_OFFSET(ap_index));
	target_id = (win_cr >> CCU_GCR_TARGET_OFFSET) & CCU_GCR_TARGET_MASK;
	printf("\tccu   GCR %d - all other transactions\n", target_id);
}
#endif

void ccu_win_check(struct addr_map_win *win)
{
	/* check if address is aligned to 1M */
	if (IS_NOT_ALIGN(win->base_addr, CCU_WIN_ALIGNMENT)) {
		win->base_addr = ALIGN_UP(win->base_addr, CCU_WIN_ALIGNMENT);
		NOTICE("%s: Align up the base address to 0x%llx\n",
		       __func__, win->base_addr);
	}

	/* size parameter validity check */
	if (IS_NOT_ALIGN(win->win_size, CCU_WIN_ALIGNMENT)) {
		win->win_size = ALIGN_UP(win->win_size, CCU_WIN_ALIGNMENT);
		NOTICE("%s: Aligning size to 0x%llx\n",
		       __func__, win->win_size);
	}
}

int ccu_is_win_enabled(int ap_index, uint32_t win_id)
{
	return mmio_read_32(CCU_WIN_CR_OFFSET(ap_index, win_id)) &
			    WIN_ENABLE_BIT;
}

void ccu_enable_win(int ap_index, struct addr_map_win *win, uint32_t win_id)
{
	uint32_t ccu_win_reg;
	uint32_t alr, ahr;
	uint64_t end_addr;

	if ((win_id == 0) || (win_id > MVEBU_CCU_MAX_WINS)) {
		ERROR("Enabling wrong CCU window %d!\n", win_id);
		return;
	}

	end_addr = (win->base_addr + win->win_size - 1);
	alr = (uint32_t)((win->base_addr >> ADDRESS_SHIFT) & ADDRESS_MASK);
	ahr = (uint32_t)((end_addr >> ADDRESS_SHIFT) & ADDRESS_MASK);

	mmio_write_32(CCU_WIN_ALR_OFFSET(ap_index, win_id), alr);
	mmio_write_32(CCU_WIN_AHR_OFFSET(ap_index, win_id), ahr);

	ccu_win_reg = WIN_ENABLE_BIT;
	ccu_win_reg |= (win->target_id & CCU_TARGET_ID_MASK)
			<< CCU_TARGET_ID_OFFSET;
	mmio_write_32(CCU_WIN_CR_OFFSET(ap_index, win_id), ccu_win_reg);
}

static void ccu_disable_win(int ap_index, uint32_t win_id)
{
	uint32_t win_reg;

	if ((win_id == 0) || (win_id > MVEBU_CCU_MAX_WINS)) {
		ERROR("Disabling wrong CCU window %d!\n", win_id);
		return;
	}

	win_reg = mmio_read_32(CCU_WIN_CR_OFFSET(ap_index, win_id));
	win_reg &= ~WIN_ENABLE_BIT;
	mmio_write_32(CCU_WIN_CR_OFFSET(ap_index, win_id), win_reg);
}

/* Insert/Remove temporary window for using the out-of reset default
 * CPx base address to access the CP configuration space prior to
 * the further base address update in accordance with address mapping
 * design.
 *
 * NOTE: Use the same window array for insertion and removal of
 *       temporary windows.
 */
void ccu_temp_win_insert(int ap_index, struct addr_map_win *win, int size)
{
	uint32_t win_id;

	for (int i = 0; i < size; i++) {
		win_id = MVEBU_CCU_MAX_WINS - 1 - i;
		ccu_win_check(win);
		ccu_enable_win(ap_index, win, win_id);
		win++;
	}
}

/*
 * NOTE: Use the same window array for insertion and removal of
 *       temporary windows.
 */
void ccu_temp_win_remove(int ap_index, struct addr_map_win *win, int size)
{
	uint32_t win_id;

	for (int i = 0; i < size; i++) {
		uint64_t base;
		uint32_t target;

		win_id = MVEBU_CCU_MAX_WINS - 1 - i;

		target = mmio_read_32(CCU_WIN_CR_OFFSET(ap_index, win_id));
		target >>= CCU_TARGET_ID_OFFSET;
		target &= CCU_TARGET_ID_MASK;

		base = mmio_read_32(CCU_WIN_ALR_OFFSET(ap_index, win_id));
		base <<= ADDRESS_SHIFT;

		if ((win->target_id != target) || (win->base_addr != base)) {
			ERROR("%s: Trying to remove bad window-%d!\n",
			      __func__, win_id);
			continue;
		}
		ccu_disable_win(ap_index, win_id);
		win++;
	}
}

/* Returns current DRAM window target (DRAM_0_TID, DRAM_1_TID, RAR_TID)
 * NOTE: Call only once for each AP.
 * The AP0 DRAM window is located at index 2 only at the BL31 execution start.
 * Then it relocated to index 1 for matching the rest of APs DRAM settings.
 * Calling this function after relocation will produce wrong results on AP0
 */
static uint32_t ccu_dram_target_get(int ap_index)
{
	/* On BLE stage the AP0 DRAM window is opened by the BootROM at index 2.
	 * All the rest of detected APs will use window at index 1.
	 * The AP0 DRAM window is moved from index 2 to 1 during
	 * init_ccu() execution.
	 */
	const uint32_t win_id = (ap_index == 0) ? 2 : 1;
	uint32_t target;

	target = mmio_read_32(CCU_WIN_CR_OFFSET(ap_index, win_id));
	target >>= CCU_TARGET_ID_OFFSET;
	target &= CCU_TARGET_ID_MASK;

	return target;
}

void ccu_dram_target_set(int ap_index, uint32_t target)
{
	/* On BLE stage the AP0 DRAM window is opened by the BootROM at index 2.
	 * All the rest of detected APs will use window at index 1.
	 * The AP0 DRAM window is moved from index 2 to 1
	 * during init_ccu() execution.
	 */
	const uint32_t win_id = (ap_index == 0) ? 2 : 1;
	uint32_t dram_cr;

	dram_cr = mmio_read_32(CCU_WIN_CR_OFFSET(ap_index, win_id));
	dram_cr &= ~(CCU_TARGET_ID_MASK << CCU_TARGET_ID_OFFSET);
	dram_cr |= (target & CCU_TARGET_ID_MASK) << CCU_TARGET_ID_OFFSET;
	mmio_write_32(CCU_WIN_CR_OFFSET(ap_index, win_id), dram_cr);
}

/* Setup CCU DRAM window and enable it */
void ccu_dram_win_config(int ap_index, struct addr_map_win *win)
{
#if IMAGE_BLE /* BLE */
	/* On BLE stage the AP0 DRAM window is opened by the BootROM at index 2.
	 * Since the BootROM is not accessing DRAM at BLE stage,
	 * the DRAM window can be temporarely disabled.
	 */
	const uint32_t win_id = (ap_index == 0) ? 2 : 1;
#else /* end of BLE */
	/* At the ccu_init() execution stage, DRAM windows of all APs
	 * are arranged at index 1.
	 * The AP0 still has the old window BootROM DRAM at index 2, so
	 * the window-1 can be safely disabled without breaking the DRAM access.
	 */
	const uint32_t win_id = 1;
#endif

	ccu_disable_win(ap_index, win_id);
	/* enable write secure (and clear read secure) */
	mmio_write_32(CCU_WIN_SCR_OFFSET(ap_index, win_id),
		      CCU_WIN_ENA_WRITE_SECURE);
	ccu_win_check(win);
	ccu_enable_win(ap_index, win, win_id);
}

/* Save content of CCU window + GCR */
static void ccu_save_win_range(int ap_id, int win_first,
			       int win_last, uint32_t *buffer)
{
	int win_id, idx;
	/* Save CCU */
	for (idx = 0, win_id = win_first; win_id <= win_last; win_id++) {
		buffer[idx++] = mmio_read_32(CCU_WIN_CR_OFFSET(ap_id, win_id));
		buffer[idx++] = mmio_read_32(CCU_WIN_SCR_OFFSET(ap_id, win_id));
		buffer[idx++] = mmio_read_32(CCU_WIN_ALR_OFFSET(ap_id, win_id));
		buffer[idx++] = mmio_read_32(CCU_WIN_AHR_OFFSET(ap_id, win_id));
	}
	buffer[idx] = mmio_read_32(CCU_WIN_GCR_OFFSET(ap_id));
}

/* Restore content of CCU window + GCR */
static void ccu_restore_win_range(int ap_id, int win_first,
				  int win_last, uint32_t *buffer)
{
	int win_id, idx;
	/* Restore CCU */
	for (idx = 0, win_id = win_first; win_id <= win_last; win_id++) {
		mmio_write_32(CCU_WIN_CR_OFFSET(ap_id, win_id),  buffer[idx++]);
		mmio_write_32(CCU_WIN_SCR_OFFSET(ap_id, win_id), buffer[idx++]);
		mmio_write_32(CCU_WIN_ALR_OFFSET(ap_id, win_id), buffer[idx++]);
		mmio_write_32(CCU_WIN_AHR_OFFSET(ap_id, win_id), buffer[idx++]);
	}
	mmio_write_32(CCU_WIN_GCR_OFFSET(ap_id), buffer[idx]);
}

void ccu_save_win_all(int ap_id)
{
	ccu_save_win_range(ap_id, 0, MVEBU_CCU_MAX_WINS - 1, ccu_regs_save);
}

void ccu_restore_win_all(int ap_id)
{
	ccu_restore_win_range(ap_id, 0, MVEBU_CCU_MAX_WINS - 1, ccu_regs_save);
}

int init_ccu(int ap_index)
{
	struct addr_map_win *win, *dram_win;
	uint32_t win_id, win_reg;
	uint32_t win_count, array_id;
	uint32_t dram_target;
#if IMAGE_BLE
	/* In BootROM context CCU Window-1
	 * has SRAM_TID target and should not be disabled
	 */
	const uint32_t win_start = 2;
#else
	const uint32_t win_start = 1;
#endif

	INFO("Initializing CCU Address decoding\n");

	/* Get the array of the windows and fill the map data */
	marvell_get_ccu_memory_map(ap_index, &win, &win_count);
	if (win_count <= 0) {
		INFO("No windows configurations found\n");
	} else if (win_count > (MVEBU_CCU_MAX_WINS - 1)) {
		ERROR("CCU mem map array > than max available windows (%d)\n",
		      MVEBU_CCU_MAX_WINS);
		win_count = MVEBU_CCU_MAX_WINS;
	}

	/* Need to set GCR to DRAM before all CCU windows are disabled for
	 * securing the normal access to DRAM location, which the ATF is running
	 * from. Once all CCU windows are set, which have to include the
	 * dedicated DRAM window as well, the GCR can be switched to the target
	 * defined by the platform configuration.
	 */
	dram_target = ccu_dram_target_get(ap_index);
	win_reg = (dram_target & CCU_GCR_TARGET_MASK) << CCU_GCR_TARGET_OFFSET;
	mmio_write_32(CCU_WIN_GCR_OFFSET(ap_index), win_reg);

	/* If the DRAM window was already configured at the BLE stage,
	 * only the window target considered valid, the address range should be
	 * updated according to the platform configuration.
	 */
	for (dram_win = win, array_id = 0; array_id < win_count;
	     array_id++, dram_win++) {
		if (IS_DRAM_TARGET(dram_win->target_id)) {
			dram_win->target_id = dram_target;
			break;
		}
	}

	/* Disable all AP CCU windows
	 * Window-0 is always bypassed since it already contains
	 * data allowing the internal configuration space access
	 */
	for (win_id = win_start; win_id < MVEBU_CCU_MAX_WINS; win_id++) {
		ccu_disable_win(ap_index, win_id);
		/* enable write secure (and clear read secure) */
		mmio_write_32(CCU_WIN_SCR_OFFSET(ap_index, win_id),
			      CCU_WIN_ENA_WRITE_SECURE);
	}

	/* win_id is the index of the current ccu window
	 * array_id is the index of the current memory map window entry
	 */
	for (win_id = win_start, array_id = 0;
	    ((win_id < MVEBU_CCU_MAX_WINS) && (array_id < win_count));
	    win_id++) {
		ccu_win_check(win);
		ccu_enable_win(ap_index, win, win_id);
		win++;
		array_id++;
	}

	/* Get & set the default target according to board topology */
	win_reg = (marvell_get_ccu_gcr_target(ap_index) & CCU_GCR_TARGET_MASK)
		   << CCU_GCR_TARGET_OFFSET;
	mmio_write_32(CCU_WIN_GCR_OFFSET(ap_index), win_reg);

#ifdef DEBUG_ADDR_MAP
	dump_ccu(ap_index);
#endif

	INFO("Done CCU Address decoding Initializing\n");

	return 0;
}

void errata_wa_init(void)
{
	/*
	 * EERATA ID: RES-3033912 - Internal Address Space Init state causes
	 * a hang upon accesses to [0xf070_0000, 0xf07f_ffff]
	 * Workaround: Boot Firmware (ATF) should configure CCU_RGF_WIN(4) to
	 * split [0x6e_0000, 0xff_ffff] to values [0x6e_0000, 0x6f_ffff] and
	 * [0x80_0000, 0xff_ffff] that cause accesses to the
	 * segment of [0xf070_0000, 0xf07f_ffff] to act as RAZWI.
	 */
	mmio_write_32(CCU_RGF(4), 0x37f9b809);
	mmio_write_32(CCU_RGF(5), 0x7ffa0009);
}