summaryrefslogtreecommitdiff
path: root/arch/arm/soc/atmel_sam3/soc.c
blob: 879f376845f6759570fad3d3992d082ac5724668 (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
/*
 * Copyright (c) 2016 Intel Corporation.
 * Copyright (c) 2013-2015 Wind River Systems, Inc.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @file
 * @brief System/hardware module for Atmel SAM3 family processor
 *
 * This module provides routines to initialize and support board-level hardware
 * for the Atmel SAM3 family processor.
 */

#include <kernel.h>
#include <device.h>
#include <init.h>
#include <soc.h>

#include <arch/cpu.h>

/**
 * @brief Setup various clock on SoC.
 *
 * Setup the SoC clocks according to section 28.12 in datasheet.
 *
 * Assumption:
 * SLCK = 32.768kHz
 */
static ALWAYS_INLINE void clock_init(void)
{
	uint32_t tmp;

	/* Note:
	 * Magic numbers below are obtained by reading the registers
	 * when the SoC was running the SAM-BA bootloader
	 * (with reserved bits set to 0).
	 */

#ifdef CONFIG_SOC_ATMEL_SAM3_EXT_SLCK
	/* This part is to switch the slow clock to using
	 * the external 32 kHz crystal oscillator.
	 */

	/* Select external crystal */
	__SUPC->cr = SUPC_CR_KEY | SUPC_CR_XTALSEL;

	/* Wait for oscillator to be stablized */
	while (!(__SUPC->sr & SUPC_SR_OSCSEL))
		;
#endif /* CONFIG_SOC_ATMEL_SAM3_EXT_SLCK */

#ifdef CONFIG_SOC_ATMEL_SAM3_EXT_MAINCK
	/* Start the external main oscillator */
	__PMC->ckgr_mor = PMC_CKGR_MOR_KEY | PMC_CKGR_MOR_MOSCRCF_4MHZ
			  | PMC_CKGR_MOR_MOSCRCEN | PMC_CKGR_MOR_MOSCXTEN
			  | PMC_CKGR_MOR_MOSCXTST;

	/* Wait for main oscillator to be stablized */
	while (!(__PMC->sr & PMC_INT_MOSCXTS))
		;

	/* Select main oscillator as source since it is more accurate
	 * according to datasheet.
	 */
	__PMC->ckgr_mor = PMC_CKGR_MOR_KEY | PMC_CKGR_MOR_MOSCRCF_4MHZ
			  | PMC_CKGR_MOR_MOSCRCEN | PMC_CKGR_MOR_MOSCXTEN
			  | PMC_CKGR_MOR_MOSCXTST | PMC_CKGR_MOR_MOSCSEL;

	/* Wait for main oscillator to be selected */
	while (!(__PMC->sr & PMC_INT_MOSCSELS))
		;
#ifdef CONFIG_SOC_ATMEL_SAM3_WAIT_MODE
	/*
	 * Instruct CPU enter Wait mode instead of Sleep mode to
	 * keep Processor Clock (HCLK) and thus be able to debug
	 * CPU using JTAG
	 */
	__PMC->fsmr |= PMC_FSMR_LPM;
#endif
#else
	/* Set main fast RC oscillator to 12 MHz */
	__PMC->ckgr_mor = PMC_CKGR_MOR_KEY | PMC_CKGR_MOR_MOSCRCF_12MHZ
			  | PMC_CKGR_MOR_MOSCRCEN;

	/* Wait for main fast RC oscillator to be stablized */
	while (!(__PMC->sr & PMC_INT_MOSCRCS))
		;
#endif /* CONFIG_SOC_ATMEL_SAM3_EXT_MAINCK */

	/* Use PLLA as master clock.
	 * According to datasheet, PMC_MCKR must not be programmed in
	 * a single write operation. So it seems the safe way is to
	 * get the system to use main clock (by setting CSS). Then set
	 * the prescaler (PRES). Finally setting it back to using PLL.
	 */

	/* Switch to main clock first so we can setup PLL */
	tmp = __PMC->mckr & ~PMC_MCKR_CSS_MASK;
	__PMC->mckr = tmp | PMC_MCKR_CSS_MAIN;

	/* Wait for clock selection complete */
	while (!(__PMC->sr & PMC_INT_MCKRDY))
		;

	/* Setup PLLA */
	__PMC->ckgr_pllar = PMC_CKGR_PLLAR_DIVA | PMC_CKGR_PLLAR_ONE
			    | PMC_CKGR_PLLAR_MULA
			    | PMC_CKGR_PLLAR_PLLACOUNT;

	/* Wait for PLL lock */
	while (!(__PMC->sr & PMC_INT_LOCKA))
		;

	/* Setup prescaler */
	tmp = __PMC->mckr & ~PMC_MCKR_PRES_MASK;
	__PMC->mckr = tmp | PMC_MCKR_PRES_CLK;

	/* Wait for main clock setup complete */
	while (!(__PMC->sr & PMC_INT_MCKRDY))
		;

	/* Finally select PLL as clock source */
	tmp = __PMC->mckr & ~PMC_MCKR_CSS_MASK;
	__PMC->mckr = tmp | PMC_MCKR_CSS_PLLA;

	/* Wait for main clock setup complete */
	while (!(__PMC->sr & PMC_INT_MCKRDY))
		;
}

/**
 * @brief Perform basic hardware initialization at boot.
 *
 * This needs to be run from the very beginning.
 * So the init priority has to be 0 (zero).
 *
 * @return 0
 */
static int atmel_sam3_init(struct device *arg)
{
	uint32_t key;

	ARG_UNUSED(arg);

	/* Note:
	 * Magic numbers below are obtained by reading the registers
	 * when the SoC was running the SAM-BA bootloader
	 * (with reserved bits set to 0).
	 */

	key = irq_lock();

	/* Setup the vector table offset register (VTOR),
	 * which is located at the beginning of flash area.
	 */
	_scs_relocate_vector_table((void *)CONFIG_FLASH_BASE_ADDRESS);

	/* Setup the flash controller.
	 * The bootloader is running @ 48 MHz with
	 * FWS == 2.
	 * When running at 84 MHz, FWS == 4 seems
	 * to be more stable, and allows the board
	 * to boot.
	 */
	__EEFC0->fmr = 0x00000400;
	__EEFC1->fmr = 0x00000400;

	/* Clear all faults */
	_ScbMemFaultAllFaultsReset();
	_ScbBusFaultAllFaultsReset();
	_ScbUsageFaultAllFaultsReset();

	_ScbHardFaultAllFaultsReset();

	/* Setup master clock */
	clock_init();

	/* Disable watchdog timer, not used by system */
	__WDT->mr |= WDT_DISABLE;

	/* Install default handler that simply resets the CPU
	 * if configured in the kernel, NOP otherwise
	 */
	NMI_INIT();

	irq_unlock(key);

	return 0;
}

SYS_INIT(atmel_sam3_init, PRE_KERNEL_1, 0);