aboutsummaryrefslogtreecommitdiff
path: root/arch/arm/mach-at91/spl_atmel.c
blob: a30c4f6c075f2ad1e13093b111b2463af047a7c0 (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
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) 2013 Atmel Corporation
 *		      Bo Shen <voice.shen@atmel.com>
 */

#include <common.h>
#include <hang.h>
#include <init.h>
#include <log.h>
#include <asm/io.h>
#include <asm/arch/at91_common.h>
#include <asm/arch/at91_pit.h>
#include <asm/arch/at91_pmc.h>
#include <asm/arch/at91_rstc.h>
#include <asm/arch/at91_wdt.h>
#include <asm/arch/clk.h>
#include <spl.h>

static void switch_to_main_crystal_osc(void)
{
	struct at91_pmc *pmc = (struct at91_pmc *)ATMEL_BASE_PMC;
	u32 tmp;

	tmp = readl(&pmc->mor);
	tmp &= ~AT91_PMC_MOR_OSCOUNT(0xff);
	tmp &= ~AT91_PMC_MOR_KEY(0xff);
	tmp |= AT91_PMC_MOR_MOSCEN;
	tmp |= AT91_PMC_MOR_OSCOUNT(8);
	tmp |= AT91_PMC_MOR_KEY(0x37);
	writel(tmp, &pmc->mor);
	while (!(readl(&pmc->sr) & AT91_PMC_IXR_MOSCS))
		;

#if defined(CONFIG_SAMA5D2)
	/* Enable a measurement of the external oscillator */
	tmp = readl(&pmc->mcfr);
	tmp |= AT91_PMC_MCFR_CCSS_XTAL_OSC;
	tmp |= AT91_PMC_MCFR_RCMEAS;
	writel(tmp, &pmc->mcfr);

	while (!(readl(&pmc->mcfr) & AT91_PMC_MCFR_MAINRDY))
		;

	if (!(readl(&pmc->mcfr) & AT91_PMC_MCFR_MAINF_MASK))
		hang();
#endif

	tmp = readl(&pmc->mor);
/*
 * some boards have an external oscillator with driving.
 * in this case we need to disable the internal SoC driving (bypass mode)
 */
#if defined(CONFIG_SPL_AT91_MCK_BYPASS)
	tmp |= AT91_PMC_MOR_OSCBYPASS;
#else
	tmp &= ~AT91_PMC_MOR_OSCBYPASS;
#endif
	tmp &= ~AT91_PMC_MOR_KEY(0xff);
	tmp |= AT91_PMC_MOR_KEY(0x37);
	writel(tmp, &pmc->mor);

	tmp = readl(&pmc->mor);
	tmp |= AT91_PMC_MOR_MOSCSEL;
	tmp &= ~AT91_PMC_MOR_KEY(0xff);
	tmp |= AT91_PMC_MOR_KEY(0x37);
	writel(tmp, &pmc->mor);

	while (!(readl(&pmc->sr) & AT91_PMC_IXR_MOSCSELS))
		;

#if !defined(CONFIG_SAMA5D2)
	/* Wait until MAINRDY field is set to make sure main clock is stable */
	while (!(readl(&pmc->mcfr) & AT91_PMC_MAINRDY))
		;
#endif

#if !defined(CONFIG_SAMA5D4) && !defined(CONFIG_SAMA5D2)
	tmp = readl(&pmc->mor);
	tmp &= ~AT91_PMC_MOR_MOSCRCEN;
	tmp &= ~AT91_PMC_MOR_KEY(0xff);
	tmp |= AT91_PMC_MOR_KEY(0x37);
	writel(tmp, &pmc->mor);
#endif
}

__weak void matrix_init(void)
{
	/* This only be used for sama5d4 soc now */
}

__weak void redirect_int_from_saic_to_aic(void)
{
	/* This only be used for sama5d4 soc now */
}

/* empty stub to satisfy current lowlevel_init, can be removed any time */
void s_init(void)
{
}

void board_init_f(ulong dummy)
{
	int ret;

	if (IS_ENABLED(CONFIG_OF_CONTROL)) {
		ret = spl_early_init();
		if (ret) {
			debug("spl_early_init() failed: %d\n", ret);
			hang();
		}
	}
	switch_to_main_crystal_osc();

#ifdef CONFIG_SAMA5D2
	configure_2nd_sram_as_l2_cache();
#endif

#if !defined(CONFIG_WDT_AT91)
	/* disable watchdog */
	at91_disable_wdt();
#endif

	/* PMC configuration */
	at91_pmc_init();

	at91_clock_init(CFG_SYS_AT91_MAIN_CLOCK);

	matrix_init();

	redirect_int_from_saic_to_aic();

	timer_init();

	board_early_init_f();

	mem_init();

	ret = spl_init();
	if (ret) {
		debug("spl_init() failed: %d\n", ret);
		hang();
	}

	preloader_console_init();

}