diff options
author | Aidan MacDonald <amachronic@protonmail.com> | 2021-07-06 21:02:37 +0100 |
---|---|---|
committer | Aidan MacDonald <amachronic@protonmail.com> | 2021-07-08 16:01:38 +0000 |
commit | 0e1a90ea1da0c1737363e9412781f734f39048d4 (patch) | |
tree | bef444477385a5eb8e8766ec75391ba48d63c613 /firmware/target/mips/ingenic_x1000/fiiom3k | |
parent | 65aa9ce57067f810d6a8f9417ec7d0a5741b404a (diff) | |
download | rockbox-0e1a90ea1da0c1737363e9412781f734f39048d4.tar.gz rockbox-0e1a90ea1da0c1737363e9412781f734f39048d4.zip |
x1000: SPL refactoring
This streamlines the boot code a bit and reduces target specific
boilerplate. The clock init hack used by the bootloader has been
"standardized" and works for the main Rockbox binary now, so you
can boot rockbox.bin over USB without special hacks.
Change-Id: I7c1fac37df5a45873583ce6818eaedb9f71a782b
Diffstat (limited to 'firmware/target/mips/ingenic_x1000/fiiom3k')
-rw-r--r-- | firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c | 327 |
1 files changed, 59 insertions, 268 deletions
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c index 5680b1e548..cbbe8b1d5d 100644 --- a/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c | |||
@@ -20,306 +20,97 @@ | |||
20 | ****************************************************************************/ | 20 | ****************************************************************************/ |
21 | 21 | ||
22 | #include "system.h" | 22 | #include "system.h" |
23 | #include "ucl_decompress.h" | 23 | #include "clk-x1000.h" |
24 | #include "spl-x1000.h" | 24 | #include "spl-x1000.h" |
25 | #include "gpio-x1000.h" | 25 | #include "gpio-x1000.h" |
26 | #include "clk-x1000.h" | ||
27 | #include "nand-x1000.h" | ||
28 | #include "x1000/cpm.h" | ||
29 | #include <string.h> | ||
30 | #include <stdint.h> | ||
31 | |||
32 | /* Available boot options */ | ||
33 | #define BOOTOPTION_ROCKBOX 0 | ||
34 | #define BOOTOPTION_ORIG_FW 1 | ||
35 | #define BOOTOPTION_RECOVERY 2 | ||
36 | |||
37 | /* Boot select button state must remain stable for this duration | ||
38 | * before the choice will be accepted. Currently 100ms. | ||
39 | */ | ||
40 | #define BTN_STABLE_TIME (100 * (X1000_EXCLK_FREQ / 4000)) | ||
41 | 26 | ||
42 | static const char normal_cmdline[] = "mem=64M@0x0\ | 27 | #define CMDLINE_COMMON \ |
43 | no_console_suspend\ | 28 | "mem=64M@0x0 no_console_suspend console=ttyS2,115200n8 lpj=5009408 ip=off" |
44 | console=ttyS2,115200n8\ | 29 | #define CMDLINE_NORMAL \ |
45 | lpj=5009408\ | 30 | " init=/linuxrc ubi.mtd=3 root=ubi0:rootfs ubi.mtd=4 rootfstype=ubifs rw loglevel=8" |
46 | ip=off\ | ||
47 | init=/linuxrc\ | ||
48 | ubi.mtd=3\ | ||
49 | root=ubi0:rootfs\ | ||
50 | ubi.mtd=4\ | ||
51 | rootfstype=ubifs\ | ||
52 | rw\ | ||
53 | loglevel=8"; | ||
54 | 31 | ||
55 | static const char recovery_cmdline[] = "mem=64M@0x0\ | 32 | static int dualboot_setup(void) |
56 | no_console_suspend\ | 33 | { |
57 | console=ttyS2,115200n8\ | 34 | spl_dualboot_init_clocktree(); |
58 | lpj=5009408\ | 35 | spl_dualboot_init_uart2(); |
59 | ip=off"; | ||
60 | |||
61 | /* Entry point function type, defined to be Linux compatible. */ | ||
62 | typedef void(*entry_fn)(int, char**, int, int); | ||
63 | 36 | ||
64 | struct spl_boot_option { | 37 | /* load PDMA MCU firmware */ |
65 | uint32_t nand_addr; | 38 | jz_writef(CPM_CLKGR, PDMA(0)); |
66 | uint32_t nand_size; | 39 | return spl_storage_read(0x4000, 0x2000, (void*)0xb3422000); |
67 | uint32_t load_addr; | 40 | } |
68 | uint32_t exec_addr; | ||
69 | const char* cmdline; /* for Linux */ | ||
70 | }; | ||
71 | 41 | ||
72 | const struct spl_boot_option spl_boot_options[] = { | 42 | const struct spl_boot_option spl_boot_options[] = { |
73 | { | 43 | [BOOT_OPTION_ROCKBOX] = { |
74 | /* Rockbox: the first unused NAND page is 26 KiB in, and the | 44 | .storage_addr = 0x6800, |
75 | * remainder of the block is unused, giving us 102 KiB to use. | 45 | .storage_size = 102 * 1024, |
76 | */ | 46 | .load_addr = X1000_DRAM_BASE, |
77 | .nand_addr = 0x6800, | ||
78 | .nand_size = 0x19800, | ||
79 | .load_addr = X1000_DRAM_END - 0x19800, | ||
80 | .exec_addr = X1000_DRAM_BASE, | 47 | .exec_addr = X1000_DRAM_BASE, |
81 | .cmdline = NULL, | 48 | .flags = BOOTFLAG_UCLPACK, |
82 | }, | 49 | }, |
83 | { | 50 | [BOOT_OPTION_OFW_PLAYER] = { |
84 | /* Original firmware */ | 51 | .storage_addr = 0x20000, |
85 | .nand_addr = 0x20000, | 52 | .storage_size = 4 * 1024 * 1024, |
86 | .nand_size = 0x400000, | ||
87 | .load_addr = 0x80efffc0, | 53 | .load_addr = 0x80efffc0, |
88 | .exec_addr = 0x80f00000, | 54 | .exec_addr = 0x80f00000, |
89 | .cmdline = normal_cmdline, | 55 | .cmdline = CMDLINE_COMMON CMDLINE_NORMAL, |
56 | .cmdline_addr = 0x80004000, | ||
57 | .setup = dualboot_setup, | ||
90 | }, | 58 | }, |
91 | { | 59 | [BOOT_OPTION_OFW_RECOVERY] = { |
92 | /* Recovery image */ | 60 | .storage_addr = 0x420000, |
93 | .nand_addr = 0x420000, | 61 | .storage_size = 5 * 1024 * 1024, |
94 | .nand_size = 0x500000, | ||
95 | .load_addr = 0x80efffc0, | 62 | .load_addr = 0x80efffc0, |
96 | .exec_addr = 0x80f00000, | 63 | .exec_addr = 0x80f00000, |
97 | .cmdline = recovery_cmdline, | 64 | .cmdline = CMDLINE_COMMON, |
65 | .cmdline_addr = 0x80004000, | ||
66 | .setup = dualboot_setup, | ||
98 | }, | 67 | }, |
99 | }; | 68 | }; |
100 | 69 | ||
101 | void spl_error(void) | ||
102 | { | ||
103 | const uint32_t pin = (1 << 24); | ||
104 | |||
105 | /* Turn on button light */ | ||
106 | jz_clr(GPIO_INT(GPIO_C), pin); | ||
107 | jz_set(GPIO_MSK(GPIO_C), pin); | ||
108 | jz_clr(GPIO_PAT1(GPIO_C), pin); | ||
109 | jz_set(GPIO_PAT0(GPIO_C), pin); | ||
110 | |||
111 | while(1) { | ||
112 | /* Turn it off */ | ||
113 | mdelay(100); | ||
114 | jz_set(GPIO_PAT0(GPIO_C), pin); | ||
115 | |||
116 | /* Turn it on */ | ||
117 | mdelay(100); | ||
118 | jz_clr(GPIO_PAT0(GPIO_C), pin); | ||
119 | } | ||
120 | } | ||
121 | |||
122 | nand_drv* alloc_nand_drv(uint32_t laddr, uint32_t lsize) | ||
123 | { | ||
124 | size_t need_size = sizeof(nand_drv) + | ||
125 | NAND_DRV_SCRATCHSIZE + | ||
126 | NAND_DRV_MAXPAGESIZE; | ||
127 | |||
128 | /* find a hole to keep the buffers */ | ||
129 | uintptr_t addr; | ||
130 | if(X1000_SDRAM_BASE + need_size <= laddr) | ||
131 | addr = X1000_SDRAM_BASE; | ||
132 | else | ||
133 | addr = CACHEALIGN_UP(X1000_SDRAM_BASE + laddr + lsize); | ||
134 | |||
135 | uint8_t* page_buf = (uint8_t*)addr; | ||
136 | uint8_t* scratch_buf = page_buf + NAND_DRV_MAXPAGESIZE; | ||
137 | nand_drv* drv = (nand_drv*)(scratch_buf + NAND_DRV_SCRATCHSIZE); | ||
138 | |||
139 | drv->page_buf = page_buf; | ||
140 | drv->scratch_buf = scratch_buf; | ||
141 | drv->refcount = 0; | ||
142 | return drv; | ||
143 | } | ||
144 | |||
145 | void spl_target_boot(void) | ||
146 | { | ||
147 | int opt_index = spl_get_boot_option(); | ||
148 | const struct spl_boot_option* opt = &spl_boot_options[opt_index]; | ||
149 | uint8_t* load_addr = (uint8_t*)opt->load_addr; | ||
150 | |||
151 | /* Clock setup etc. */ | ||
152 | spl_handle_pre_boot(opt_index); | ||
153 | |||
154 | /* Since the GPIO refactor, the SFC driver no longer assigns its own pins. | ||
155 | * We don't want to call gpio_init(), to keep code size down. Assign them | ||
156 | * here manually, we shouldn't need any other pins. */ | ||
157 | gpioz_configure(GPIO_A, 0x3f << 26, GPIOF_DEVICE(1)); | ||
158 | |||
159 | /* Open NAND chip */ | ||
160 | nand_drv* ndrv = alloc_nand_drv(opt->load_addr, opt->nand_size); | ||
161 | int rc = nand_open(ndrv); | ||
162 | if(rc) | ||
163 | spl_error(); | ||
164 | |||
165 | /* For OF only: load DMA coprocessor's firmware from flash */ | ||
166 | if(opt_index != BOOTOPTION_ROCKBOX) { | ||
167 | rc = nand_read_bytes(ndrv, 0x4000, 0x2000, (uint8_t*)0xb3422000); | ||
168 | if(rc) | ||
169 | goto nand_err; | ||
170 | } | ||
171 | |||
172 | /* Read the firmware */ | ||
173 | rc = nand_read_bytes(ndrv, opt->nand_addr, opt->nand_size, load_addr); | ||
174 | if(rc) | ||
175 | goto nand_err; | ||
176 | |||
177 | /* Rockbox doesn't need the NAND; for the OF, we should leave it open */ | ||
178 | if(opt_index == BOOTOPTION_ROCKBOX) | ||
179 | nand_close(ndrv); | ||
180 | |||
181 | /* Kernel arguments pointer, for Linux only */ | ||
182 | char** kargv = (char**)0x80004000; | ||
183 | |||
184 | if(!opt->cmdline) { | ||
185 | /* Uncompress the rockbox bootloader */ | ||
186 | uint32_t out_size = X1000_DRAM_END - opt->exec_addr; | ||
187 | int rc = ucl_unpack(load_addr, opt->nand_size, | ||
188 | (uint8_t*)opt->exec_addr, &out_size); | ||
189 | if(rc != UCL_E_OK) | ||
190 | spl_error(); | ||
191 | } else { | ||
192 | /* Set kernel args */ | ||
193 | kargv[0] = 0; | ||
194 | kargv[1] = (char*)opt->cmdline; | ||
195 | } | ||
196 | |||
197 | entry_fn entry = (entry_fn)opt->exec_addr; | ||
198 | commit_discard_idcache(); | ||
199 | entry(2, kargv, 0, 0); | ||
200 | __builtin_unreachable(); | ||
201 | |||
202 | nand_err: | ||
203 | nand_close(ndrv); | ||
204 | spl_error(); | ||
205 | } | ||
206 | |||
207 | int spl_get_boot_option(void) | 70 | int spl_get_boot_option(void) |
208 | { | 71 | { |
209 | const uint32_t pinmask = (1 << 17) | (1 << 19); | 72 | /* Button debounce time in OST clock cycles */ |
73 | const uint32_t btn_stable_time = 100 * (X1000_EXCLK_FREQ / 4000); | ||
210 | 74 | ||
211 | uint32_t pin = 1, lastpin = 0; | 75 | /* Buttons to poll */ |
76 | const unsigned port = GPIO_A; | ||
77 | const uint32_t recov_pin = (1 << 19); /* Volume Up */ | ||
78 | const uint32_t orig_fw_pin = (1 << 17); /* Play */ | ||
79 | |||
80 | uint32_t pin = -1, lastpin = 0; | ||
212 | uint32_t deadline = 0; | 81 | uint32_t deadline = 0; |
213 | /* Iteration count guards against unlikely case of broken buttons | 82 | int iter_count = 30; /* to avoid an infinite loop */ |
214 | * which never stabilize; if this occurs, we always boot Rockbox. */ | ||
215 | int iter_count = 0; | ||
216 | const int max_iter_count = 30; | ||
217 | 83 | ||
218 | /* Configure the button GPIOs as inputs */ | 84 | /* set GPIOs to input state */ |
219 | gpioz_configure(GPIO_A, pinmask, GPIOF_INPUT); | 85 | gpioz_configure(port, recov_pin|orig_fw_pin, GPIOF_INPUT); |
220 | 86 | ||
221 | /* Poll the pins for a short duration to detect a keypress */ | 87 | /* Poll until we get a stable reading */ |
222 | do { | 88 | do { |
223 | lastpin = pin; | 89 | lastpin = pin; |
224 | pin = ~REG_GPIO_PIN(GPIO_A) & pinmask; | 90 | pin = ~REG_GPIO_PIN(port) & (recov_pin|orig_fw_pin); |
225 | if(pin != lastpin) { | 91 | if(pin != lastpin) { |
226 | /* This will always be set on the first iteration */ | 92 | deadline = __ost_read32() + btn_stable_time; |
227 | deadline = __ost_read32() + BTN_STABLE_TIME; | 93 | iter_count -= 1; |
228 | iter_count += 1; | ||
229 | } | 94 | } |
230 | } while(iter_count < max_iter_count && __ost_read32() < deadline); | 95 | } while(iter_count > 0 && __ost_read32() < deadline); |
231 | 96 | ||
232 | if(iter_count < max_iter_count && (pin & (1 << 17))) { | 97 | if(iter_count >= 0 && (pin & orig_fw_pin)) { |
233 | if(pin & (1 << 19)) | 98 | if(pin & recov_pin) |
234 | return BOOTOPTION_RECOVERY; /* Play+Volume Up */ | 99 | return BOOT_OPTION_OFW_RECOVERY; |
235 | else | 100 | else |
236 | return BOOTOPTION_ORIG_FW; /* Play */ | 101 | return BOOT_OPTION_OFW_PLAYER; |
237 | } else { | ||
238 | return BOOTOPTION_ROCKBOX; /* Volume Up or no buttons */ | ||
239 | } | 102 | } |
103 | |||
104 | return BOOT_OPTION_ROCKBOX; | ||
240 | } | 105 | } |
241 | 106 | ||
242 | void spl_handle_pre_boot(int bootopt) | 107 | void spl_error(void) |
243 | { | 108 | { |
244 | /* Move system to EXCLK so we can manipulate the PLLs */ | 109 | /* Flash the buttonlight */ |
245 | clk_set_ccr_mux(CLKMUX_SCLK_A(EXCLK) | CLKMUX_CPU(SCLK_A) | | 110 | int level = 0; |
246 | CLKMUX_AHB0(SCLK_A) | CLKMUX_AHB2(SCLK_A)); | 111 | while(1) { |
247 | clk_set_ccr_div(1, 1, 1, 1, 1); | 112 | gpio_set_function(GPIO_PC(24), GPIOF_OUTPUT(level)); |
248 | 113 | mdelay(100); | |
249 | /* Enable APLL @ 1008 MHz (24 MHz EXCLK * 42 = 1008 MHz) */ | 114 | level = 1 - level; |
250 | jz_writef(CPM_APCR, BS(1), PLLM(41), PLLN(0), PLLOD(0), ENABLE(1)); | ||
251 | while(jz_readf(CPM_APCR, ON) == 0); | ||
252 | |||
253 | /* System clock setup -- common to Rockbox and FiiO firmware | ||
254 | * ---- | ||
255 | * CPU at 1 GHz, L2 cache at 500 MHz | ||
256 | * AHB0 and AHB2 at 200 MHz | ||
257 | * PCLK at 100 MHz | ||
258 | * DDR at 200 MHz | ||
259 | */ | ||
260 | |||
261 | if(bootopt == BOOTOPTION_ROCKBOX) { | ||
262 | /* We don't use MPLL in Rockbox, so run everything from APLL. */ | ||
263 | clk_set_ccr_div(1, 2, 5, 5, 10); | ||
264 | clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) | CLKMUX_CPU(SCLK_A) | | ||
265 | CLKMUX_AHB0(SCLK_A) | CLKMUX_AHB2(SCLK_A)); | ||
266 | clk_set_ddr(X1000_CLK_SCLK_A, 5); | ||
267 | |||
268 | /* Turn off MPLL */ | ||
269 | jz_writef(CPM_MPCR, ENABLE(0)); | ||
270 | } else { | ||
271 | /* Typical ingenic setup -- 1008 MHz APLL, 600 MHz MPLL | ||
272 | * with APLL driving the CPU/L2 and MPLL driving busses. */ | ||
273 | clk_set_ccr_div(1, 2, 3, 3, 6); | ||
274 | clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) | CLKMUX_CPU(SCLK_A) | | ||
275 | CLKMUX_AHB0(MPLL) | CLKMUX_AHB2(MPLL)); | ||
276 | |||
277 | /* Mimic OF's clock gating setup */ | ||
278 | jz_writef(CPM_CLKGR, PDMA(0), PCM(1), MAC(1), LCD(1), | ||
279 | MSC0(1), MSC1(1), OTG(1), CIM(1)); | ||
280 | |||
281 | /* We now need to define the clock tree by fiddling with | ||
282 | * various CPM registers. Although the kernel has code to | ||
283 | * set up the clock tree itself, it isn't used and relies | ||
284 | * on the SPL for this. */ | ||
285 | jz_writef(CPM_I2SCDR, CS_V(EXCLK)); | ||
286 | jz_writef(CPM_PCMCDR, CS_V(EXCLK)); | ||
287 | |||
288 | jz_writef(CPM_MACCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe)); | ||
289 | while(jz_readf(CPM_MACCDR, BUSY)); | ||
290 | |||
291 | jz_writef(CPM_LPCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe)); | ||
292 | while(jz_readf(CPM_LPCDR, BUSY)); | ||
293 | |||
294 | jz_writef(CPM_MSC0CDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe)); | ||
295 | while(jz_readf(CPM_MSC0CDR, BUSY)); | ||
296 | |||
297 | jz_writef(CPM_MSC1CDR, CE(1), STOP(1), CLKDIV(0xfe)); | ||
298 | while(jz_readf(CPM_MSC1CDR, BUSY)); | ||
299 | |||
300 | jz_writef(CPM_CIMCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe)); | ||
301 | while(jz_readf(CPM_CIMCDR, BUSY)); | ||
302 | |||
303 | jz_writef(CPM_USBCDR, CLKSRC_V(EXCLK), CE(1), STOP(1)); | ||
304 | while(jz_readf(CPM_USBCDR, BUSY)); | ||
305 | |||
306 | /* Handle UART initialization */ | ||
307 | gpioz_configure(GPIO_C, 3 << 30, GPIOF_DEVICE(1)); | ||
308 | jz_writef(CPM_CLKGR, UART2(0)); | ||
309 | |||
310 | /* TODO: Stop being lazy and make this human readable */ | ||
311 | volatile uint8_t* ub = (volatile uint8_t*)0xb0032000; | ||
312 | ub[0x04] = 0; | ||
313 | ub[0x08] = 0xef; | ||
314 | ub[0x20] = 0xfc; | ||
315 | ub[0x0c] = 3; | ||
316 | uint8_t uv = ub[0x0c]; | ||
317 | ub[0x0c] = uv | 0x80; | ||
318 | ub[0x04] = 0; | ||
319 | ub[0x00] = 0x0d; | ||
320 | ub[0x24] = 0x10; | ||
321 | ub[0x28] = 0; | ||
322 | ub[0x0c] = uv & 0x7f; | ||
323 | ub[0x08] = 0x17; | ||
324 | } | 115 | } |
325 | } | 116 | } |