This repository has been archived by the owner on Dec 11, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 51
/
bootcode.c
232 lines (187 loc) · 8.12 KB
/
bootcode.c
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
/*
* The MIT License (MIT)
*
* Copyright (c) 2021 Peter Lawrence
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "bsp/board.h"
#include "tusb.h"
#include <rp2040.h>
#include "hardware/resets.h"
/* this is code that runs before main() and cannot be used once execution reaches main() (since it exists in RAM we have not reserved) */
__attribute__ (( section(".bootc") )) static void pll_init(pll_hw_t *pll, uint32_t refdiv, uint32_t vco_freq, uint32_t post_div1, uint8_t post_div2)
{
uint32_t ref_mhz = XOSC_MHZ / refdiv;
// What are we multiplying the reference clock by to get the vco freq
// (The regs are called div, because you divide the vco output and compare it to the refclk)
uint32_t fbdiv = vco_freq / (ref_mhz * 1000000UL);
// fbdiv
assert(fbdiv >= 16 && fbdiv <= 320);
// Check divider ranges
assert((post_div1 >= 1 && post_div1 <= 7) && (post_div2 >= 1 && post_div2 <= 7));
// post_div1 should be >= post_div2
// from appnote page 11
// postdiv1 is designed to operate with a higher input frequency
// than postdiv2
assert(post_div2 <= post_div1);
// Check that reference frequency is no greater than vco / 16
assert(ref_mhz <= (vco_freq / 16));
// Set up post dividers - div1 feeds into div2 so if div1 is 5 and div2 is 2 then you get a divide by 10
uint32_t pdiv = (post_div1 << PLL_PRIM_POSTDIV1_LSB) |
(post_div2 << PLL_PRIM_POSTDIV2_LSB);
uint32_t pll_reset = (pll_usb_hw == pll) ? RESETS_RESET_PLL_USB_BITS : RESETS_RESET_PLL_SYS_BITS;
reset_block(pll_reset);
unreset_block_wait(pll_reset);
// Turn off PLL in case it is already running
pll->pwr = 0xffffffff;
pll->fbdiv_int = 0;
pll->cs = refdiv;
// Put calculated value into feedback divider
pll->fbdiv_int = fbdiv;
// Turn on PLL
uint32_t power = PLL_PWR_PD_BITS | // Main power
PLL_PWR_VCOPD_BITS; // VCO Power
hw_clear_bits(&pll->pwr, power);
// Wait for PLL to lock
while (!(pll->cs & PLL_CS_LOCK_BITS));
// Set up post dividers
pll->prim = pdiv;
// Turn on post divider
hw_clear_bits(&pll->pwr, PLL_PWR_POSTDIVPD_BITS);
}
/*
This is a more streamlined alternative to the current pico-sdk based TinyUSB board support package.
Sticking to C and avoiding all that C++ yields a much smaller executable.
*/
/* overhaul of clock_configure() from pico-sdk to use much less memory */
__attribute__ (( section(".bootc") )) bool simple_clock_configure(enum clock_index clk_index, uint32_t src, uint32_t auxsrc, bool glitchless)
{
const uint32_t div = 0x100; /* always 1:1 ratio */
clock_hw_t *clock = &clocks_hw->clk[clk_index];
// If increasing divisor, set divisor before source. Otherwise set source
// before divisor. This avoids a momentary overspeed when e.g. switching
// to a faster source and increasing divisor to compensate.
if (div > clock->div)
clock->div = div;
// If switching a glitchless slice (ref or sys) to an aux source, switch
// away from aux *first* to avoid passing glitches when changing aux mux.
// Assume (!!!) glitchless source 0 is no faster than the aux source.
if (glitchless)
{
hw_clear_bits(&clock->ctrl, CLOCKS_CLK_REF_CTRL_SRC_BITS);
while (!(clock->selected & 1u));
}
// If no glitchless mux, cleanly stop the clock to avoid glitches
// propagating when changing aux mux. Note it would be a really bad idea
// to do this on one of the glitchless clocks (clk_sys, clk_ref).
else
{
hw_clear_bits(&clock->ctrl, CLOCKS_CLK_GPOUT0_CTRL_ENABLE_BITS);
}
// Set aux mux first, and then glitchless mux if this clock has one
hw_write_masked(&clock->ctrl,
(auxsrc << CLOCKS_CLK_SYS_CTRL_AUXSRC_LSB),
CLOCKS_CLK_SYS_CTRL_AUXSRC_BITS
);
if (glitchless)
{
hw_write_masked(&clock->ctrl,
src << CLOCKS_CLK_REF_CTRL_SRC_LSB,
CLOCKS_CLK_REF_CTRL_SRC_BITS
);
while (!(clock->selected & (1u << src)));
}
hw_set_bits(&clock->ctrl, CLOCKS_CLK_GPOUT0_CTRL_ENABLE_BITS);
// Now that the source is configured, we can trust that the user-supplied
// divisor is a safe value.
clock->div = div;
return true;
}
__attribute__ (( section(".bootc") )) static void usb_clock_init(void)
{
hw_set_bits(&resets_hw->reset, RESETS_RESET_PLL_USB_BITS);
hw_clear_bits(&resets_hw->reset, RESETS_RESET_PLL_USB_BITS);
while (~resets_hw->reset_done & RESETS_RESET_PLL_USB_BITS);
pll_init(pll_usb_hw, 1, 480 * 1000000, 5, 2);
// CLK SYS = PLL USB (48MHz) / 1 = 48MHz
simple_clock_configure(clk_sys,
CLOCKS_CLK_SYS_CTRL_SRC_VALUE_CLKSRC_CLK_SYS_AUX,
CLOCKS_CLK_SYS_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
true);
// CLK USB = PLL USB (48MHz) / 1 = 48MHz
simple_clock_configure(clk_usb,
0, // No GLMUX
CLOCKS_CLK_USB_CTRL_AUXSRC_VALUE_CLKSRC_PLL_USB,
false);
}
typedef void *(*rom_table_lookup_fn)(uint16_t *table, uint32_t code);
#define rom_hword_as_ptr(rom_address) (void *)(uint32_t)(*(uint16_t *)rom_address)
__attribute__ (( section(".bootc") )) static void *rom_func_lookup(uint32_t code)
{
rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn)rom_hword_as_ptr(0x18);
uint16_t *func_table = (uint16_t *)rom_hword_as_ptr(0x14);
return rom_table_lookup(func_table, code);
}
__attribute__ (( section(".bootc") )) static void *rom_data_lookup(uint32_t code)
{
rom_table_lookup_fn rom_table_lookup = (rom_table_lookup_fn)rom_hword_as_ptr(0x18);
uint16_t *data_table = (uint16_t *)rom_hword_as_ptr(0x16);
return rom_table_lookup(data_table, code);
}
__attribute__ (( section(".bootc") )) static uint32_t rom_table_code(char c1, char c2)
{
return (c2 << 8) | c1;
}
typedef uint32_t (*pop_fn)(uint32_t);
typedef void (*void_fn)(void);
extern char unique_id[17];
__attribute__ (( section(".bootc") )) void SystemInit(void)
{
void_fn connect_internal_flash = rom_func_lookup(rom_table_code('I', 'F'));
void_fn flash_exit_xip = rom_func_lookup(rom_table_code('E', 'X'));
connect_internal_flash();
flash_exit_xip();
hw_write_masked(&ioqspi_hw->io[1].ctrl,
IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_LOW << IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_LSB,
IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_BITS
);
/* Unique ID command and 32-bits of pipeline delay */
for (int i = 0; i < 5; i++)
{
ssi_hw->dr0 = 0x4B;
while (!(ssi_hw->sr & SSI_SR_RFNE_BITS));
(void)ssi_hw->dr0;
}
/* read the 64-bits and write as hex chars */
char *pnt = unique_id;
for (int i = 0; i < 8; i++)
{
ssi_hw->dr0 = 0;
while (!(ssi_hw->sr & SSI_SR_RFNE_BITS));
uint8_t byte = ssi_hw->dr0;
*pnt++ = "0123456789ABCDEF"[(byte >> 4) & 0xf];
*pnt++ = "0123456789ABCDEF"[(byte >> 0) & 0xf];
}
hw_write_masked(&ioqspi_hw->io[1].ctrl,
IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_VALUE_HIGH << IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_LSB,
IO_QSPI_GPIO_QSPI_SS_CTRL_OUTOVER_BITS
);
usb_clock_init();
}