summaryrefslogtreecommitdiff
path: root/plat/marvell/a8k-p/common/plat_bl1_setup.c
blob: 872f4fcf0dbee13882b7607b942926d65e5d0cf9 (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
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
/*
 * Copyright (C) 2017 Marvell International Ltd.
 *
 * SPDX-License-Identifier:	BSD-3-Clause
 * https://spdx.org/licenses
 */

#include <plat_config.h>
#include <plat_marvell.h>
#include <plat_private.h>

#include <ap810_setup.h>
#include <debug.h>
#include <delay_timer.h>
#include <mci.h>
#include <mmio.h>

/* MCI related defines */
#define MVEBU_AP_SYSTEM_SOFT_RESET_REG(ap)	(MVEBU_AP_MISC_SOC_BASE(ap) + 0x54)
/* For every MCI have 2 reset parameters MAIN/PHY SW reset */
#define SOFT_RESET_IHBx_MAIN_SW_RESET(mci)	((0x1) << ((mci * 2) + 3))
#define SOFT_RESET_IHBx_PHY_SW_RESET(mci)	((0x1) << ((mci * 2) + 4))

/* MCIx_REG_START_ADDRESS */
#define MVEBU_MCI_REG_START_ADDRESS(ap, mci)	(MVEBU_AR_RFU_BASE(ap) + 0x4158 + mci * 0x4)
#define MVEBU_MCI_REG_START_ADDR_SHIFT		12

#define MVEBU_IHB_CNTRL_REG_1(ap, mci_idx)	(MVEBU_MCI_PHY(ap, mci_idx) + 0x4)
#define IHB_CNTRL_REG1_DEVICE_WDAT(val)		(((val) & 0xff) << 0)
#define IHB_CNTRL_REG1_DEVICE_ADDR_BYTE_SEL(val)(((val) & 0x3) << 16)
#define IHB_CNTRL_REG1_DEVICE_ADDR(val)		(((val) & 0xff) << 18)
#define IHB_CNTRL_REG1_DEVICE_REG_WEN(val)	(((val) & 0x1) << 28)
#define IHB_CNTRL_REG1_DEVICE_WEN_DONE(val)	(((val) & 0x1) << 29)

#define MVEBU_IHB_PWM_CTRL_REG3(ap, mci_idx)	(MVEBU_MCI_PHY(ap, mci_idx) + 0x1c)
#define IHB_PWM_CTRL_REG3_AUTO_SPEED_OFFSET	0
#define IHB_PWM_CTRL_REG3_AUTO_SPEED_MASK	(0xf << IHB_PWM_CTRL_REG3_AUTO_SPEED_OFFSET)

#define MCI_RETRY_COUNT				10

/* SYSRST_OUTn Config definitions */
#define MVEBU_SYSRST_OUT_CONFIG_REG(ap)		(MVEBU_AP_MISC_SOC_BASE(ap) + 0x4)
#define WD_MASK_SYS_RST_OUT			(1 << 2)

static uint32_t mci_get_link_speed(int ap_idx, int mci_idx)
{
	return mmio_read_32(MVEBU_IHB_PWM_CTRL_REG3(ap_idx, mci_idx)) & IHB_PWM_CTRL_REG3_AUTO_SPEED_MASK;
}

static void mci_phy_config(int ap_idx, int mci_idx)
{
	mmio_write_32(MVEBU_IHB_CNTRL_REG_1(ap_idx, mci_idx),
				IHB_CNTRL_REG1_DEVICE_WDAT(0x50) |
				IHB_CNTRL_REG1_DEVICE_ADDR_BYTE_SEL(0x1) |
				IHB_CNTRL_REG1_DEVICE_ADDR(0x21) |
				IHB_CNTRL_REG1_DEVICE_REG_WEN(0x1));
	mdelay(5);

	mmio_write_32(MVEBU_IHB_CNTRL_REG_1(ap_idx, mci_idx),
				IHB_CNTRL_REG1_DEVICE_WDAT(0x50) |
				IHB_CNTRL_REG1_DEVICE_ADDR_BYTE_SEL(0x1) |
				IHB_CNTRL_REG1_DEVICE_ADDR(0x21) |
				IHB_CNTRL_REG1_DEVICE_REG_WEN(0x0));
	mdelay(5);
}

static void ap810_win_route_open(int ap_id, uint64_t base_addr, uint64_t addr_sz, uint32_t target_id)
{
	struct addr_map_win gwin_temp_win;
	struct addr_map_win ccu_temp_win;

	ccu_temp_win.base_addr = base_addr;
	ccu_temp_win.win_size = addr_sz;

	/* If this is a remote AP */
	if (ap_id > 0) {
		gwin_temp_win.base_addr = base_addr;
		gwin_temp_win.win_size  = addr_sz;
		gwin_temp_win.target_id = ap_id;
		gwin_temp_win_insert(0, &gwin_temp_win, 1);

		/* Open temp window for access to GWIN */
		ccu_temp_win.target_id = GLOBAL_TID;
		ccu_temp_win_insert(0, &ccu_temp_win, 1);
	}

	ccu_temp_win.target_id = target_id;
	ccu_temp_win_insert(ap_id, &ccu_temp_win, 1);
}

static void ap810_win_route_close(int ap_id, uint64_t base_addr, uint64_t addr_sz, uint32_t target_id)
{
	struct addr_map_win gwin_temp_win;
	struct addr_map_win ccu_temp_win;

	ccu_temp_win.base_addr = base_addr;
	ccu_temp_win.win_size  = addr_sz;
	ccu_temp_win.target_id = target_id;
	ccu_temp_win_remove(ap_id, &ccu_temp_win, 1);

	/* If this is a remote AP */
	if (ap_id > 0) {
		gwin_temp_win.base_addr = base_addr;
		gwin_temp_win.win_size  = addr_sz;
		gwin_temp_win.target_id = ap_id;
		gwin_temp_win_remove(0, &gwin_temp_win, 1);

		/* Open temp window for access to GWIN */
		ccu_temp_win.target_id = GLOBAL_TID;
		ccu_temp_win_remove(0, &ccu_temp_win, 1);
	}
}

static void ap810_mci_phy_soft_reset(int ap_id, int mci_idx)
{
	uint32_t reg;

	/* For every MCI, there is MAIN SW reset & PHY SW reset */
	reg = mmio_read_32(MVEBU_AP_SYSTEM_SOFT_RESET_REG(ap_id));
	reg &= ~(SOFT_RESET_IHBx_MAIN_SW_RESET(mci_idx) | SOFT_RESET_IHBx_PHY_SW_RESET(mci_idx));
	mmio_write_32(MVEBU_AP_SYSTEM_SOFT_RESET_REG(ap_id), reg);

	/* Wait 5ms before get into reset */
	mdelay(5);
	reg |= (SOFT_RESET_IHBx_MAIN_SW_RESET(mci_idx) | SOFT_RESET_IHBx_PHY_SW_RESET(mci_idx));
	mmio_write_32(MVEBU_AP_SYSTEM_SOFT_RESET_REG(ap_id), reg);
}

static void a8kp_mci_turn_off_links(uintptr_t mci_base)
{
	int ap_id, cp_id, mci_id;

	/* Go over the APs and turn off the link of MCIs */
	for (ap_id = 0; ap_id < ap810_get_ap_count(); ap_id++) {
		ap810_win_route_open(ap_id, mci_base, MVEBU_MCI_REG_SIZE_REMAP, IO_0_TID);

		/* Go over the MCIs  */
		for (cp_id = 0; cp_id < ap810_get_cp_per_ap_static_cnt(ap_id); cp_id++) {
			struct addr_map_win iowin_temp_win = {
				.base_addr = mci_base,
				.win_size = MVEBU_MCI_REG_SIZE_REMAP,
			};
			/* Get the MCI index */
			mci_id = marvell_get_mci_map(ap_id, cp_id);
			INFO("Turn link off for AP-%d MCI-%d\n", ap_id, mci_id);

			/* Open temp window IO_WIN unit with relevant target ID */
			iowin_temp_win.target_id = mci_id;
			iow_temp_win_insert(ap_id, &iowin_temp_win, 1);

			/* Open window for MCI indirect access from APx */
			mmio_write_32(MVEBU_MCI_REG_START_ADDRESS(ap_id, mci_id),
					mci_base >> MVEBU_MCI_REG_START_ADDR_SHIFT);
			/* Turn link off */
			mci_turn_link_down();
			/* Remove the temporary IO-WIN window */
			iow_temp_win_remove(ap_id, &iowin_temp_win, 1);
		}

		ap810_win_route_close(ap_id, mci_base, MVEBU_MCI_REG_SIZE_REMAP, IO_0_TID);
	}
}

static void a8kp_mci_mpp_reset(int mpp)
{
	uint32_t val;

	/* Set MPP to low */
	val = mmio_read_32(MVEBU_AP_GPIO_DATA_IN(0));
	val &= ~(0x1 << mpp);
	mmio_write_32(MVEBU_AP_GPIO_DATA_IN(0), val);

	/* Clear data out */
	val = mmio_read_32(MVEBU_AP_GPIO_DATA_OUT_VAL(0));
	val &= ~(0x1 << mpp);
	mmio_write_32(MVEBU_AP_GPIO_DATA_OUT_VAL(0), val);

	/* Enable data out */
	val = mmio_read_32(MVEBU_AP_GPIO_DATA_OUT_EN(0));
	val &= ~(0x1 << mpp);
	mmio_write_32(MVEBU_AP_GPIO_DATA_OUT_EN(0), val);

	INFO("Get out CPs from reset\n");
	val = mmio_read_32(MVEBU_AP_GPIO_DATA_OUT_VAL(0));
	val |= (0x1 << mpp);
	mmio_write_32(MVEBU_AP_GPIO_DATA_OUT_VAL(0), val);

	/* Wait until CP release from reset */
	mdelay(2);
}

/* MCI initialize for all APs, the sequence split to 3 parts:
 * 1. Turn off the link on all MCIs
 * 2. Reset the CPs via MPP
 * 3. Re-init the MCI phy in AP side & in CP side
 */
static int mci_wa_initialize(void)
{
	int ap_id, mci_id, cp_id;
	uintptr_t mci_base = MVEBU_MCI_REG_BASE_REMAP(0);

	debug_enter();

	/* 1st stage - Turn off the link on all MCIs */
	a8kp_mci_turn_off_links(mci_base);

	/* 2nd stage - reset CPs via MPP */
	a8kp_mci_mpp_reset(MPP_MCI_RELEASE_FROM_RESET);

	/* 3rd stage - Re-init the MCI phy in AP side & in CP side */
	for (ap_id = 0; ap_id < ap810_get_ap_count(); ap_id++) {
		ap810_win_route_open(ap_id, mci_base, MVEBU_MCI_REG_SIZE_REMAP, IO_0_TID);

		/* Go over the MCIs in every APx */
		for (cp_id = 0; cp_id < ap810_get_cp_per_ap_static_cnt(ap_id); cp_id++) {
			uint32_t reg;
			struct addr_map_win iowin_temp_win = {
				.base_addr = mci_base,
				.win_size = MVEBU_MCI_REG_SIZE_REMAP,
			};
			/* Get the MCI index */
			mci_id = marvell_get_mci_map(ap_id, cp_id);
			INFO("Turn link on & ID assign AP%d MCI-%d\n", ap_id, mci_id);

			/* Config MCI phy on AP side */
			mci_phy_config(ap_id, mci_id);

			/* Reset MCI phy on AP side */
			ap810_mci_phy_soft_reset(ap_id, mci_id);

			/* Open temp window IO_WIN unit with relevant target ID */
			iowin_temp_win.target_id = mci_id;
			iow_temp_win_insert(ap_id, &iowin_temp_win, 1);

			/* Open window for MCI indirect access from APx */
			mmio_write_32(MVEBU_MCI_REG_START_ADDRESS(ap_id, mci_id),
					mci_base >> MVEBU_MCI_REG_START_ADDR_SHIFT);

			/* Turn on link on CP side */
			mci_turn_link_on();

			/* Wait 20ms, until link is stable*/
			mdelay(20);

			/* Check the link status on CP side */
			reg = mci_get_link_status();
			if (reg == -1) {
				ERROR("bad link on MCI-%d - status register is %x\n", mci_id, reg);
				return -1;
			}

			reg = mci_get_link_speed(ap_id, mci_id);
			if (reg != 0x3) {
				ERROR("link speed is not correct on MCI-%d - link speed is %x\n", mci_id, reg);
				return -1;
			}

			INFO("MCI-%d link is 8G (speed = %x)\n", mci_id, reg);

			/* Remove the temporary IO-WIN window */
			iow_temp_win_remove(ap_id, &iowin_temp_win, 1);
		}

		ap810_win_route_close(ap_id, mci_base, MVEBU_MCI_REG_SIZE_REMAP, IO_0_TID);
	}

	debug_exit();
	return 0;
}

/* Armada-8k-plus have bug on MCI, that link between AP & CP is
 * not stable and should be re-initialize.
 */
void a8kp_mci_wa_initialize(void)
{
	int retry = MCI_RETRY_COUNT;

	/* Retry until success, if the MCI initialization failed, reset is required */
	while (retry > 0) {
		if (mci_wa_initialize() == 0)
			break;
		ERROR("MCIx failed to create link - retry again %d of %d\n", retry, MCI_RETRY_COUNT);
		retry--;
	}

	if (retry == 0) {
		ERROR("MCIx failed to create link after %d times, reset is required\n", MCI_RETRY_COUNT);
		panic();
	}
}

/* Configure the threshold of every MCI */
static int a8kp_mci_configure_threshold(void)
{
	int ap_id, mci_id, cp_id;
	uintptr_t mci_base = MVEBU_MCI_REG_BASE_REMAP(0);

	debug_enter();

	/* Run MCI WA for performance improvements */
	for (ap_id = 0; ap_id < ap810_get_ap_count(); ap_id++) {
		ap810_win_route_open(ap_id, mci_base, MVEBU_MCI_REG_SIZE_REMAP, IO_0_TID);

		/* Go over the MCIs in every APx */
		for (cp_id = 0; cp_id < ap810_get_cp_per_ap_cnt(ap_id); cp_id++) {
			struct addr_map_win iowin_temp_win = {
				.base_addr = mci_base,
				.win_size = MVEBU_MCI_REG_SIZE_REMAP,
			};
			/* Get the MCI index */
			mci_id = marvell_get_mci_map(ap_id, cp_id);
			INFO("Configure threshold & ID assin AP%d MCI-%d\n", ap_id, mci_id);

			/* Open temp window IO_WIN unit with relevant target ID */
			iowin_temp_win.target_id = mci_id;
			iow_temp_win_insert(ap_id, &iowin_temp_win, 1);

			/* Open window for MCI indirect access from APx */
			mmio_write_32(MVEBU_MCI_REG_START_ADDRESS(ap_id, mci_id),
					mci_base >> MVEBU_MCI_REG_START_ADDR_SHIFT);

			/* Run MCI WA for performance improvements */
			mci_initialize(mci_id);

			/* Remove the temporary IO-WIN window */
			iow_temp_win_remove(ap_id, &iowin_temp_win, 1);
		}

		ap810_win_route_close(ap_id, mci_base, MVEBU_MCI_REG_SIZE_REMAP, IO_0_TID);
	}

	debug_exit();
	return 0;
}

/* CP110 has configuration space address set by the default to 0xf200_0000
 * In Armada-8k-plus family there is an option to connect more than
 * a single CP110 to AP810.
 * Need to update the configuration space according to the address map of
 * Armada-8k-plus family.
 * This function opens a temporary windows in GWIN/CCU/IO-WIN to access different
 * CPs, changes the configuration space of every CP & closes the temporary windows
 */
static void update_cp110_default_win(void)
{
	int ap_id, cp_id, mci_id;
	uintptr_t cp110_base, cp110_temp_base;

	debug_enter();

	/* CP110 default configuration address space */
	cp110_temp_base = MVEBU_CP_DEFAULT_BASE_ADDR;

	/* Go over the APs and update every CP with
	 * the new configuration address
	 */
	for (ap_id = 0; ap_id < ap810_get_ap_count(); ap_id++) {
		ap810_win_route_open(ap_id, cp110_temp_base, MVEBU_CP_DEFAULT_BASE_SIZE, IO_0_TID);

		/* Go over the connected CPx in the APx */
		for (cp_id = 0; cp_id < ap810_get_cp_per_ap_cnt(ap_id); cp_id++) {
			struct addr_map_win iowin_temp_win = {
				.base_addr = cp110_temp_base,
				.win_size = MVEBU_CP_DEFAULT_BASE_SIZE,
			};
			/* Get the MCI index */
			mci_id = marvell_get_mci_map(ap_id, cp_id);
			INFO("AP-%d MCI-%d CP-%d\n", ap_id, mci_id, cp_id);

			/* Open temp window in IO_WIN unit with relevant target ID */
			iowin_temp_win.target_id = mci_id;
			iow_temp_win_insert(ap_id, &iowin_temp_win, 1);

			/* Calculate the new CP110 - base address */
			cp110_base = MVEBU_CP_REGS_BASE(ap_id, cp_id);
			/* Go and update the CP110 configuration address space */
			iob_cfg_space_update(ap_id, cp_id, cp110_temp_base, cp110_base);

			/* Remove the temporary IO-WIN window */
			iow_temp_win_remove(ap_id, &iowin_temp_win, 1);
		}

		ap810_win_route_close(ap_id, cp110_temp_base, MVEBU_CP_DEFAULT_BASE_SIZE, IO_0_TID);
	}

	debug_exit();
}

static void ap810_addr_decode_init(void)
{
	int ap_id;

	debug_enter();

	for (ap_id = 0; ap_id < ap810_get_ap_count(); ap_id++) {
		INFO("Initialize address decode for AP-%d\n", ap_id);
		/* configure IO-WIN windows */
		init_io_win(ap_id);
		/* configure GWIN windows */
		init_gwin(ap_id);
		/* configure CCU windows */
		init_ccu(ap_id);
	}

	debug_exit();
}

void marvell_bl1_setup_mpps(void)
{
	/* Enable UART MPPs.
	 ** In a normal system, this is done by Bootrom.
	 */
	mmio_write_32(MVEBU_AP_MPP_REGS(0, 1), 0x3000);
	mmio_write_32(MVEBU_AP_MPP_REGS(0, 2), 0x30000);
}

static void ap810_soc_misc_configurations(void)
{
	uint32_t reg, ap;

	debug_enter();

	for (ap = 0; ap < ap810_get_ap_count(); ap++) {
		/* Un-mask Watchdog reset from influencing the SYSRST_OUTn.
		 * Otherwise, upon WD timeout, the WD reset singal won't trigger reset
		 */
		reg = mmio_read_32(MVEBU_SYSRST_OUT_CONFIG_REG(ap));
		reg &= ~(WD_MASK_SYS_RST_OUT);
		mmio_write_32(MVEBU_SYSRST_OUT_CONFIG_REG(ap), reg);
	}

	debug_exit();
}

void bl1_plat_arch_setup(void)
{
	marvell_bl1_plat_arch_setup();

	plat_delay_timer_init();

#if PALLADIUM
	/* Palladium does not run BLE so ap810_enumeration_algo()
	 * isn't executed - need to make sure that we init the
	 * enumeration algorithm.
	 */
	ap810_enumeration_algo();
#endif

	/* No need to run MCI WA for palladium */
#if !PALLADIUM
	/* Re-init MCI connection due bug in Armada-8k-plus */
	if (ap810_rev_id_get(MVEBU_AP0) == MVEBU_AP810_REV_ID_A0)
		a8kp_mci_wa_initialize();
#endif

	/* Initialize the MCI threshold to improve performance */
	a8kp_mci_configure_threshold();

	/* misc configuration of the SoC */
	ap810_soc_misc_configurations();

	/* Update configuration space of CP110 from 0xf200_0000, to the
	 * new address according to address map of Armada-8k-plus family.
	 */
	update_cp110_default_win();

	/* configure AP810 address decode - call it after
	 * update_cp110_default_win to make sure that temporary windows do
	 * not override any window that will be configured in GWIN/CCU/IOWIN
	 */
	ap810_addr_decode_init();
}