board.c
6.54 KB
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
/*
* Copyright (C) 2014-2015 Eistec AB
*
* This file is subject to the terms and conditions of the GNU Lesser General
* Public License v2.1. See the file LICENSE in the top level directory for more
* details.
*/
/**
* @ingroup board_mulle
* @{
*
* @file
* @brief Board specific implementations for the Mulle board
*
* @author Joakim Nohlgård <joakim.nohlgard@eistec.se>
*
* @}
*/
#include <stddef.h> /* for NULL */
#include <stdio.h>
#include "board.h"
#include "cpu.h"
#include "mcg.h"
#include "periph/gpio.h"
#include "periph/uart.h"
#include "periph/rtt.h"
#include "periph/spi.h"
#include "nvram-spi.h"
#include "xtimer.h"
static nvram_t mulle_nvram_dev;
nvram_t *mulle_nvram = &mulle_nvram_dev;
static nvram_spi_params_t nvram_spi_params = {
.spi = MULLE_NVRAM_SPI_DEV,
.cs = MULLE_NVRAM_SPI_CS,
.address_count = MULLE_NVRAM_SPI_ADDRESS_COUNT,
};
/** @brief Initialize the GPIO pins controlling the power switches. */
static inline void power_pins_init(void);
/**
* @brief Set clock prescalers to safe values
*
* This should be done before switching to FLL/PLL as clock source to ensure
* that all clocks remain within the specified limits.
*/
static inline void set_safe_clock_dividers(void);
/** @brief Set the FLL source clock to RTC32k */
static inline void set_fll_source(void);
static void increase_boot_count(void);
static int mulle_nvram_init(void);
void board_init(void)
{
int status;
/* initialize the boards LEDs */
gpio_init(LED0_PIN, GPIO_OUT);
gpio_init(LED1_PIN, GPIO_OUT);
gpio_init(LED2_PIN, GPIO_OUT);
/* Initialize power control pins */
power_pins_init();
/* Turn on Vperiph for peripherals */
/*
* By turning on Vperiph first, and before waiting for the clocks to
* stabilize, we will have used enough time to have let the FRAM start up
* properly when we want to access it later without having to add any extra
* delays.
*/
gpio_set(MULLE_POWER_VPERIPH);
/* Turn on AVDD for reading voltages */
gpio_set(MULLE_POWER_AVDD);
/* Initialize RTC oscillator as early as possible since we are using it as a
* base clock for the FLL.
* It takes a while to stabilize the oscillator, therefore we do this as
* soon as possible during boot in order to let it stabilize while other
* stuff is initializing. */
/* If the clock is not stable then the UART will have the wrong baud rate
* for debug prints as well */
rtt_init();
/* Set up clocks */
set_safe_clock_dividers();
set_fll_source();
kinetis_mcg_set_mode(KINETIS_MCG_FEE);
/* At this point we need to wait for 1 ms until the clock is stable.
* Since the clock is not yet stable we can only guess how long we must
* wait. I have tried to make this as short as possible but still being able
* to read the initialization messages written on the UART.
* (If the clock is not stable all UART output is garbled until it has
* stabilized) */
for (int i = 0; i < 100000; ++i) {
__asm__ volatile("nop\n");
}
/* Update SystemCoreClock global var */
SystemCoreClockUpdate();
/* initialize the CPU */
cpu_init();
/* NVRAM requires xtimer for timing */
xtimer_init();
/* Initialize NVRAM */
status = mulle_nvram_init();
if (status == 0) {
/* Increment boot counter */
increase_boot_count();
}
}
static inline void power_pins_init(void)
{
gpio_init(MULLE_POWER_AVDD, GPIO_OUT);
gpio_init(MULLE_POWER_VPERIPH, GPIO_OUT);
gpio_init(MULLE_POWER_VSEC, GPIO_OUT);
gpio_clear(MULLE_POWER_AVDD);
gpio_clear(MULLE_POWER_VPERIPH);
gpio_clear(MULLE_POWER_VSEC);
}
static inline void set_safe_clock_dividers(void)
{
/*
* We want to achieve the following clocks:
* Core/system: <100MHz
* Bus: <50MHz
* FlexBus: <50MHz
* Flash: <25MHz
*
* using dividers 1-2-2-4 will obey the above limits when using a 96MHz FLL source.
*/
SIM->CLKDIV1 = (
SIM_CLKDIV1_OUTDIV1(CONFIG_CLOCK_K60_SYS_DIV) | /* Core/System clock divider */
SIM_CLKDIV1_OUTDIV2(CONFIG_CLOCK_K60_BUS_DIV) | /* Bus clock divider */
SIM_CLKDIV1_OUTDIV3(CONFIG_CLOCK_K60_FB_DIV) | /* FlexBus divider, not used in Mulle */
SIM_CLKDIV1_OUTDIV4(CONFIG_CLOCK_K60_FLASH_DIV)); /* Flash clock divider */
}
static inline void set_fll_source(void)
{
/* Select FLL as source (as opposed to PLL) */
SIM->SOPT2 &= ~(SIM_SOPT2_PLLFLLSEL_MASK);
/* Use external 32kHz RTC clock as source for OSC32K */
#if K60_CPU_REV == 1
SIM->SOPT1 |= SIM_SOPT1_OSC32KSEL_MASK;
#elif K60_CPU_REV == 2
SIM->SOPT1 = (SIM->SOPT1 & ~(SIM_SOPT1_OSC32KSEL_MASK)) | SIM_SOPT1_OSC32KSEL(2);
#else
#error Unknown K60 CPU revision
#endif
/* Select RTC 32kHz clock as reference clock for the FLL */
#if K60_CPU_REV == 1
/* Rev 1 parts */
SIM->SOPT2 |= SIM_SOPT2_MCGCLKSEL_MASK;
#elif K60_CPU_REV == 2
/* Rev 2 parts */
MCG->C7 = (MCG_C7_OSCSEL_MASK);
#else
#error Unknown K60 CPU revision
#endif
}
static int mulle_nvram_init(void)
{
union {
uint32_t u32;
uint8_t u8[sizeof(uint32_t)];
} rec;
rec.u32 = 0;
if (spi_init_master(MULLE_NVRAM_SPI_DEV, SPI_CONF_FIRST_RISING, SPI_SPEED_5MHZ) != 0) {
return -1;
}
if (nvram_spi_init(mulle_nvram, &nvram_spi_params, MULLE_NVRAM_CAPACITY) != 0) {
return -2;
}
if (mulle_nvram->read(mulle_nvram, &rec.u8[0], MULLE_NVRAM_MAGIC, sizeof(rec.u32)) != sizeof(rec.u32)) {
return -3;
}
if (rec.u32 != MULLE_NVRAM_MAGIC_EXPECTED) {
int i;
union {
uint64_t u64;
uint8_t u8[sizeof(uint64_t)];
} zero;
zero.u64 = 0;
for (i = 0; i < MULLE_NVRAM_CAPACITY; i += sizeof(zero)) {
if (mulle_nvram->write(mulle_nvram, &zero.u8[0], i, sizeof(zero.u64)) != sizeof(zero.u64)) {
return -4;
}
}
rec.u32 = MULLE_NVRAM_MAGIC_EXPECTED;
if (mulle_nvram->write(mulle_nvram, &rec.u8[0], MULLE_NVRAM_MAGIC, sizeof(rec.u32)) != sizeof(rec.u32)) {
return -5;
}
}
return 0;
}
static void increase_boot_count(void)
{
union {
uint32_t u32;
uint8_t u8[sizeof(uint32_t)];
} rec;
rec.u32 = 0;
if (mulle_nvram->read(mulle_nvram, &rec.u8[0], MULLE_NVRAM_BOOT_COUNT, sizeof(rec.u32)) != sizeof(rec.u32)) {
return;
}
++rec.u32;
mulle_nvram->write(mulle_nvram, &rec.u8[0], MULLE_NVRAM_BOOT_COUNT, sizeof(rec.u32));
}