summaryrefslogtreecommitdiff
path: root/firmware
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-07-06 21:02:37 +0100
committerAidan MacDonald <amachronic@protonmail.com>2021-07-08 16:01:38 +0000
commit0e1a90ea1da0c1737363e9412781f734f39048d4 (patch)
treebef444477385a5eb8e8766ec75391ba48d63c613 /firmware
parent65aa9ce57067f810d6a8f9417ec7d0a5741b404a (diff)
downloadrockbox-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')
-rw-r--r--firmware/target/mips/ingenic_x1000/boot-x1000.h91
-rw-r--r--firmware/target/mips/ingenic_x1000/clk-x1000.c93
-rw-r--r--firmware/target/mips/ingenic_x1000/clk-x1000.h13
-rw-r--r--firmware/target/mips/ingenic_x1000/crt0.S3
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c327
-rw-r--r--firmware/target/mips/ingenic_x1000/spl-x1000.c360
-rw-r--r--firmware/target/mips/ingenic_x1000/spl-x1000.h52
-rw-r--r--firmware/target/mips/ingenic_x1000/spl.lds3
-rw-r--r--firmware/target/mips/ingenic_x1000/system-x1000.c22
9 files changed, 603 insertions, 361 deletions
diff --git a/firmware/target/mips/ingenic_x1000/boot-x1000.h b/firmware/target/mips/ingenic_x1000/boot-x1000.h
new file mode 100644
index 0000000000..400eb93dc1
--- /dev/null
+++ b/firmware/target/mips/ingenic_x1000/boot-x1000.h
@@ -0,0 +1,91 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#ifndef __BOOT_X1000_H__
23#define __BOOT_X1000_H__
24
25#include "x1000/cpm.h"
26#include <stdbool.h>
27
28enum {
29 BOOT_OPTION_ROCKBOX = 0,
30 BOOT_OPTION_OFW_PLAYER,
31 BOOT_OPTION_OFW_RECOVERY,
32};
33
34enum {
35 /* 3 bits to store the boot option selected by the SPL */
36 BOOT_OPTION_MASK = 0x7,
37 BOOT_OPTION_SHIFT = 0,
38
39 /* Set after running clk_init() and setting up system clocks */
40 BOOT_FLAG_CLK_INIT = (1 << 31),
41
42 /* Set by the SPL if it was loaded over USB boot */
43 BOOT_FLAG_USB_BOOT = (1 << 30),
44};
45
46/* Note: these functions are inlined to minimize SPL code size.
47 * They are private to the X1000 early boot code anyway... */
48
49static inline void cpm_scratch_set(uint32_t value)
50{
51 /* TODO: see if this holds its state over a WDT reset */
52 REG_CPM_SCRATCH_PROT = 0x5a5a;
53 REG_CPM_SCRATCH = value;
54 REG_CPM_SCRATCH_PROT = 0xa5a5;
55}
56
57static inline void init_boot_flags(void)
58{
59 cpm_scratch_set(0);
60}
61
62static inline bool get_boot_flag(uint32_t bit)
63{
64 return (REG_CPM_SCRATCH & bit) != 0;
65}
66
67static inline void set_boot_flag(uint32_t bit)
68{
69 cpm_scratch_set(REG_CPM_SCRATCH | bit);
70}
71
72static inline void clr_boot_flag(uint32_t bit)
73{
74 cpm_scratch_set(REG_CPM_SCRATCH & ~bit);
75}
76
77static inline void set_boot_option(int opt)
78{
79 uint32_t r = REG_CPM_SCRATCH;
80 r &= ~(BOOT_OPTION_MASK << BOOT_OPTION_SHIFT);
81 r |= (opt & BOOT_OPTION_MASK) << BOOT_OPTION_SHIFT;
82 cpm_scratch_set(r);
83}
84
85static inline int get_boot_option(void)
86{
87 uint32_t r = REG_CPM_SCRATCH;
88 return (r >> BOOT_OPTION_SHIFT) & BOOT_OPTION_MASK;
89}
90
91#endif /* __BOOT_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/clk-x1000.c b/firmware/target/mips/ingenic_x1000/clk-x1000.c
index 6cc18fb113..7e254401fb 100644
--- a/firmware/target/mips/ingenic_x1000/clk-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/clk-x1000.c
@@ -21,6 +21,7 @@
21 21
22#include "system.h" 22#include "system.h"
23#include "clk-x1000.h" 23#include "clk-x1000.h"
24#include "boot-x1000.h"
24#include "x1000/cpm.h" 25#include "x1000/cpm.h"
25#include "x1000/msc.h" 26#include "x1000/msc.h"
26#include "x1000/aic.h" 27#include "x1000/aic.h"
@@ -225,7 +226,89 @@ const char* clk_get_name(x1000_clk_t clk)
225 } 226 }
226} 227}
227 228
229/* At present we've only got 24 MHz targets, and they are all using
230 * the same "standard" configuration. */
231#if X1000_EXCLK_FREQ == 24000000
232void clk_init_early(void)
233{
234 jz_writef(CPM_MPCR, ENABLE(0));
235 while(jz_readf(CPM_MPCR, ON));
236
237 /* 24 MHz * 25 = 600 MHz */
238 jz_writef(CPM_MPCR, BS(1), PLLM(25 - 1), PLLN(0), PLLOD(0), ENABLE(1));
239 while(jz_readf(CPM_MPCR, ON) == 0);
240
241 /* 600 MHz / 3 = 200 MHz */
242 clk_set_ddr(X1000_CLK_MPLL, 3);
243}
244
245void clk_init(void)
246{
247 /* make sure we only initialize the clocks once */
248 if(get_boot_flag(BOOT_FLAG_CLK_INIT))
249 return;
250
251 clk_set_ccr_mux(CLKMUX_SCLK_A(EXCLK) |
252 CLKMUX_CPU(SCLK_A) |
253 CLKMUX_AHB0(SCLK_A) |
254 CLKMUX_AHB2(SCLK_A));
255 clk_set_ccr_div(CLKDIV_CPU(1) |
256 CLKDIV_L2(1) |
257 CLKDIV_AHB0(1) |
258 CLKDIV_AHB2(1) |
259 CLKDIV_PCLK(1));
260
261 jz_writef(CPM_APCR, ENABLE(0));
262 while(jz_readf(CPM_APCR, ON));
263
264 /* 24 MHz * 42 = 1008 MHz */
265 jz_writef(CPM_APCR, BS(1), PLLM(42 - 1), PLLN(0), PLLOD(0), ENABLE(1));
266 while(jz_readf(CPM_APCR, ON) == 0);
267
268#if defined(FIIO_M3K)
269 /* TODO: Allow targets to define their clock frequencies in their config,
270 * instead of having this be a random special case. */
271 if(get_boot_option() == BOOT_OPTION_ROCKBOX) {
272 clk_set_ccr_div(CLKDIV_CPU(1) | /* 1008 MHz */
273 CLKDIV_L2(2) | /* 504 MHz */
274 CLKDIV_AHB0(5) | /* 201.6 MHz */
275 CLKDIV_AHB2(5) | /* 201.6 MHz */
276 CLKDIV_PCLK(10)); /* 100.8 MHz */
277 clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) |
278 CLKMUX_CPU(SCLK_A) |
279 CLKMUX_AHB0(SCLK_A) |
280 CLKMUX_AHB2(SCLK_A));
281
282 /* DDR to 201.6 MHz */
283 clk_set_ddr(X1000_CLK_SCLK_A, 5);
284
285 /* Disable MPLL */
286 jz_writef(CPM_MPCR, ENABLE(0));
287 while(jz_readf(CPM_MPCR, ON));
288 } else {
289#endif
290 clk_set_ccr_div(CLKDIV_CPU(1) | /* 1008 MHz */
291 CLKDIV_L2(2) | /* 504 MHz */
292 CLKDIV_AHB0(3) | /* 200 MHz */
293 CLKDIV_AHB2(3) | /* 200 MHz */
294 CLKDIV_PCLK(6)); /* 100 MHz */
295 clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) |
296 CLKMUX_CPU(SCLK_A) |
297 CLKMUX_AHB0(MPLL) |
298 CLKMUX_AHB2(MPLL));
299#if defined(FIIO_M3K)
300 }
301#endif
302
303 /* mark that clocks have been initialized */
304 set_boot_flag(BOOT_FLAG_CLK_INIT);
305}
306#else
307# error "please write a new clk_init() for this EXCLK frequency"
308#endif
309
228#define CCR_MUX_BITS jz_orm(CPM_CCR, SEL_SRC, SEL_CPLL, SEL_H0PLL, SEL_H2PLL) 310#define CCR_MUX_BITS jz_orm(CPM_CCR, SEL_SRC, SEL_CPLL, SEL_H0PLL, SEL_H2PLL)
311#define CCR_DIV_BITS jz_orm(CPM_CCR, CDIV, L2DIV, H0DIV, H2DIV, PDIV)
229#define CSR_MUX_BITS jz_orm(CPM_CSR, SRC_MUX, CPU_MUX, AHB0_MUX, AHB2_MUX) 312#define CSR_MUX_BITS jz_orm(CPM_CSR, SRC_MUX, CPU_MUX, AHB0_MUX, AHB2_MUX)
230#define CSR_DIV_BITS jz_orm(CPM_CSR, H2DIV_BUSY, H0DIV_BUSY, CDIV_BUSY) 313#define CSR_DIV_BITS jz_orm(CPM_CSR, H2DIV_BUSY, H0DIV_BUSY, CDIV_BUSY)
231 314
@@ -241,12 +324,14 @@ void clk_set_ccr_mux(uint32_t muxbits)
241 while((REG_CPM_CSR & CSR_MUX_BITS) != CSR_MUX_BITS); 324 while((REG_CPM_CSR & CSR_MUX_BITS) != CSR_MUX_BITS);
242} 325}
243 326
244void clk_set_ccr_div(int cpu, int l2, int ahb0, int ahb2, int pclk) 327void clk_set_ccr_div(uint32_t divbits)
245{ 328{
246 /* Set new divider configuration */ 329 /* Set new divider configuration */
247 jz_writef(CPM_CCR, CDIV(cpu - 1), L2DIV(l2 - 1), 330 uint32_t reg = REG_CPM_CCR;
248 H0DIV(ahb0 - 1), H2DIV(ahb2 - 1), PDIV(pclk - 1), 331 reg &= ~CCR_DIV_BITS;
249 CE_CPU(1), CE_AHB0(1), CE_AHB2(1)); 332 reg |= divbits & CCR_DIV_BITS;
333 reg |= jz_orm(CPM_CCR, CE_CPU, CE_AHB0, CE_AHB2);
334 REG_CPM_CCR = reg;
250 335
251 /* Wait until divider change completes */ 336 /* Wait until divider change completes */
252 while(REG_CPM_CSR & CSR_DIV_BITS); 337 while(REG_CPM_CSR & CSR_DIV_BITS);
diff --git a/firmware/target/mips/ingenic_x1000/clk-x1000.h b/firmware/target/mips/ingenic_x1000/clk-x1000.h
index 2ff602db9f..e19c56d0ba 100644
--- a/firmware/target/mips/ingenic_x1000/clk-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/clk-x1000.h
@@ -31,6 +31,13 @@
31#define CLKMUX_AHB0(x) jz_orf(CPM_CCR, SEL_H0PLL_V(x)) 31#define CLKMUX_AHB0(x) jz_orf(CPM_CCR, SEL_H0PLL_V(x))
32#define CLKMUX_AHB2(x) jz_orf(CPM_CCR, SEL_H2PLL_V(x)) 32#define CLKMUX_AHB2(x) jz_orf(CPM_CCR, SEL_H2PLL_V(x))
33 33
34/* Arguments to clk_set_ccr_div() */
35#define CLKDIV_CPU(x) jz_orf(CPM_CCR, CDIV((x) - 1))
36#define CLKDIV_L2(x) jz_orf(CPM_CCR, L2DIV((x) - 1))
37#define CLKDIV_AHB0(x) jz_orf(CPM_CCR, H0DIV((x) - 1))
38#define CLKDIV_AHB2(x) jz_orf(CPM_CCR, H2DIV((x) - 1))
39#define CLKDIV_PCLK(x) jz_orf(CPM_CCR, PDIV((x) - 1))
40
34typedef enum x1000_clk_t { 41typedef enum x1000_clk_t {
35 X1000_CLK_EXCLK, 42 X1000_CLK_EXCLK,
36 X1000_CLK_APLL, 43 X1000_CLK_APLL,
@@ -59,11 +66,15 @@ extern uint32_t clk_get(x1000_clk_t clk);
59/* Get the name of a clock for debug purposes */ 66/* Get the name of a clock for debug purposes */
60extern const char* clk_get_name(x1000_clk_t clk); 67extern const char* clk_get_name(x1000_clk_t clk);
61 68
69/* Clock initialization */
70extern void clk_init_early(void);
71extern void clk_init(void);
72
62/* Sets system clock multiplexers */ 73/* Sets system clock multiplexers */
63extern void clk_set_ccr_mux(uint32_t muxbits); 74extern void clk_set_ccr_mux(uint32_t muxbits);
64 75
65/* Sets system clock dividers */ 76/* Sets system clock dividers */
66extern void clk_set_ccr_div(int cpu, int l2, int ahb0, int ahb2, int pclk); 77extern void clk_set_ccr_div(uint32_t divbits);
67 78
68/* Sets DDR clock source and divider */ 79/* Sets DDR clock source and divider */
69extern void clk_set_ddr(x1000_clk_t src, uint32_t div); 80extern void clk_set_ddr(x1000_clk_t src, uint32_t div);
diff --git a/firmware/target/mips/ingenic_x1000/crt0.S b/firmware/target/mips/ingenic_x1000/crt0.S
index b36500b462..304f8d682f 100644
--- a/firmware/target/mips/ingenic_x1000/crt0.S
+++ b/firmware/target/mips/ingenic_x1000/crt0.S
@@ -24,6 +24,7 @@
24 24
25 .text 25 .text
26 .extern main 26 .extern main
27 .extern system_early_init
27 .global _start 28 .global _start
28 29
29 .set push 30 .set push
@@ -92,6 +93,8 @@ _irqstack_loop:
92 sw t1, -4(t0) 93 sw t1, -4(t0)
93 94
94 /* Jump to C code */ 95 /* Jump to C code */
96 jal system_early_init
97 nop
95 j main 98 j main
96 nop 99 nop
97 100
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
42static 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
55static const char recovery_cmdline[] = "mem=64M@0x0\ 32static 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. */
62typedef void(*entry_fn)(int, char**, int, int);
63 36
64struct 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
72const struct spl_boot_option spl_boot_options[] = { 42const 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
101void 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
122nand_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
145void 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
207int spl_get_boot_option(void) 70int 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
242void spl_handle_pre_boot(int bootopt) 107void 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}
diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000.c b/firmware/target/mips/ingenic_x1000/spl-x1000.c
index fd664e231d..284b963e97 100644
--- a/firmware/target/mips/ingenic_x1000/spl-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/spl-x1000.c
@@ -19,41 +19,188 @@
19 * 19 *
20 ****************************************************************************/ 20 ****************************************************************************/
21 21
22#include "system.h"
22#include "spl-x1000.h" 23#include "spl-x1000.h"
23#include "clk-x1000.h" 24#include "clk-x1000.h"
24#include "system.h" 25#include "nand-x1000.h"
26#include "gpio-x1000.h"
27#include "boot-x1000.h"
25#include "x1000/cpm.h" 28#include "x1000/cpm.h"
26#include "x1000/ost.h" 29#include "x1000/ost.h"
30#include "x1000/uart.h"
27#include "x1000/ddrc.h" 31#include "x1000/ddrc.h"
28#include "x1000/ddrc_apb.h" 32#include "x1000/ddrc_apb.h"
29#include "x1000/ddrphy.h" 33#include "x1000/ddrphy.h"
34#include "ucl_decompress.h"
35#include <string.h>
30 36
31#ifdef FIIO_M3K 37#ifdef FIIO_M3K
32# define SPL_DDR_MEMORYSIZE 64 38# define SPL_DDR_MEMORYSIZE 64
33# define SPL_DDR_AUTOSR_EN 1 39# define SPL_DDR_AUTOSR_EN 1
34# define SPL_DDR_NEED_BYPASS 1 40# define SPL_DDR_NEED_BYPASS 1
35#else 41#else
36# error "please add SPL memory definitions" 42# error "please define DRAM settings"
37#endif 43#endif
38 44
39/* Note: This is based purely on disassembly of the SPL from the FiiO M3K. 45static void* heap = (void*)(X1000_SDRAM_BASE + X1000_SDRAM_SIZE);
40 * The code there is somewhat generic and corresponds roughly to Ingenic's 46static nand_drv* ndrv = NULL;
41 * U-Boot code, but isn't entirely the same. 47
48void* spl_alloc(size_t count)
49{
50 heap -= CACHEALIGN_UP(count);
51 memset(heap, 0, CACHEALIGN_UP(count));
52 return heap;
53}
54
55int spl_storage_open(void)
56{
57 /* We need to assign the GPIOs manually */
58 gpioz_configure(GPIO_A, 0x3f << 26, GPIOF_DEVICE(1));
59
60 /* Allocate NAND driver manually in DRAM */
61 ndrv = spl_alloc(sizeof(nand_drv));
62 ndrv->page_buf = spl_alloc(NAND_DRV_MAXPAGESIZE);
63 ndrv->scratch_buf = spl_alloc(NAND_DRV_SCRATCHSIZE);
64 ndrv->refcount = 0;
65
66 return nand_open(ndrv);
67}
68
69void spl_storage_close(void)
70{
71 nand_close(ndrv);
72}
73
74int spl_storage_read(uint32_t addr, uint32_t length, void* buffer)
75{
76 return nand_read_bytes(ndrv, addr, length, buffer);
77}
78
79/* Used by:
80 * - FiiO M3K
81 * - Shanling Q1
42 * 82 *
43 * I converted all the runtime conditionals to compile-time ones in order to 83 * Amend it and add #ifdefs for other targets if needed.
44 * save code space, since they should be constant for any given target. 84 */
85void spl_dualboot_init_clocktree(void)
86{
87 /* Make sure these are gated to match the OF behavior. */
88 jz_writef(CPM_CLKGR, PCM(1), MAC(1), LCD(1), MSC0(1), MSC1(1), OTG(1), CIM(1));
89
90 /* Set clock sources, and make sure every clock starts out stopped */
91 jz_writef(CPM_I2SCDR, CS_V(EXCLK));
92 jz_writef(CPM_PCMCDR, CS_V(EXCLK));
93
94 jz_writef(CPM_MACCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
95 while(jz_readf(CPM_MACCDR, BUSY));
96
97 jz_writef(CPM_LPCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
98 while(jz_readf(CPM_LPCDR, BUSY));
99
100 jz_writef(CPM_MSC0CDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
101 while(jz_readf(CPM_MSC0CDR, BUSY));
102
103 jz_writef(CPM_MSC1CDR, CE(1), STOP(1), CLKDIV(0xfe));
104 while(jz_readf(CPM_MSC1CDR, BUSY));
105
106 jz_writef(CPM_CIMCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
107 while(jz_readf(CPM_CIMCDR, BUSY));
108
109 jz_writef(CPM_USBCDR, CLKSRC_V(EXCLK), CE(1), STOP(1));
110 while(jz_readf(CPM_USBCDR, BUSY));
111}
112
113void spl_dualboot_init_uart2(void)
114{
115 /* Ungate the clock and select UART2 device function */
116 jz_writef(CPM_CLKGR, UART2(0));
117 gpioz_configure(GPIO_C, 3 << 30, GPIOF_DEVICE(1));
118
119 /* Disable all interrupts */
120 jz_write(UART_UIER(2), 0);
121
122 /* FIFO configuration */
123 jz_overwritef(UART_UFCR(2),
124 RDTR(3), /* FIFO trigger level = 60? */
125 UME(0), /* UART module disable */
126 DME(1), /* DMA mode enable? */
127 TFRT(1), /* transmit FIFO reset */
128 RFRT(1), /* receive FIFO reset */
129 FME(1)); /* FIFO mode enable */
130
131 /* IR mode configuration */
132 jz_overwritef(UART_ISR(2),
133 RDPL(1), /* Zero is negative pulse for receive */
134 TDPL(1), /* ... and for transmit */
135 XMODE(1), /* Pulse width 1.6us */
136 RCVEIR(0), /* Disable IR for recieve */
137 XMITIR(0)); /* ... and for transmit */
138
139 /* Line configuration */
140 jz_overwritef(UART_ULCR(2), DLAB(0),
141 WLS_V(8BITS), /* 8 bit words */
142 SBLS_V(1_STOP_BIT), /* 1 stop bit */
143 PARE(0), /* no parity */
144 SBK(0)); /* don't set break */
145
146 /* Set the baud rate... not too sure how this works. (Docs unclear!) */
147 const unsigned divisor = 0x0004;
148 jz_writef(UART_ULCR(2), DLAB(1));
149 jz_write(UART_UDLHR(2), (divisor >> 8) & 0xff);
150 jz_write(UART_UDLLR(2), divisor & 0xff);
151 jz_write(UART_UMR(2), 16);
152 jz_write(UART_UACR(2), 0);
153 jz_writef(UART_ULCR(2), DLAB(0));
154
155 /* Enable UART */
156 jz_overwritef(UART_UFCR(2),
157 RDTR(0), /* FIFO trigger level = 1 */
158 DME(0), /* DMA mode disable */
159 UME(1), /* UART module enable */
160 TFRT(1), /* transmit FIFO reset */
161 RFRT(1), /* receive FIFO reset */
162 FME(1)); /* FIFO mode enable */
163}
164
165static void init_ost(void)
166{
167 /* NOTE: the prescaler needs to be the same as in system-x1000.c */
168 jz_writef(CPM_CLKGR, OST(0));
169 jz_writef(OST_CTRL, PRESCALE2_V(BY_4));
170 jz_overwritef(OST_CLEAR, OST2(1));
171 jz_write(OST_2CNTH, 0);
172 jz_write(OST_2CNTL, 0);
173 jz_setf(OST_ENABLE, OST2);
174}
175
176/* NOTE: This is originally based on disassembly of the FiiO M3K SPL, which
177 * is in fact the GPL'd Ingenic X-Loader SPL. Similar stuff can be found in
178 * Ingenic's U-boot code.
179 *
180 * The source code for the Ingenic X-Loader SPL can be found in these repos:
181 * - https://github.com/JaminCheung/x-loader
182 * - https://github.com/YuanhuanLiang/X1000
183 *
184 * Runtime conditionals based on the SoC type, looked up by OTP bits in EFUSE,
185 * are converted to compile-time conditionals here, as they are constant for a
186 * given target and there is no point in wasting precious space on dead code.
45 * 187 *
46 * I haven't bothered to decode all the register fields. Some of the values 188 * I didn't decode the register fields; note that some values are documented as
47 * written are going to bits documented as "Reserved" by Ingenic, but their 189 * "reserved" in the X1000 PM. The X-Loader source might shed more light on it;
48 * documentation doesn't seem completely reliable, so either these are bits 190 * it's likely these bits have meaning only on other Ingenic SoCs.
49 * which _do_ have a purpose, or they're only defined on other Ingenic CPUs.
50 * 191 *
51 * The DDR PHY registers appear to be from Synopsys "PHY Utility Block Lite". 192 * The DDR PHY registers match Synopsys's "PHY Utility Block Lite." The names
52 * These aren't documented by Ingenic, but the addresses and names can be found 193 * of those registers & their fields can also be found in the X-Loader code,
53 * in their U-Boot code. 194 * but they're not documented by Ingenic.
54 */ 195 */
55static void ddr_init(void) 196static int init_dram(void)
56{ 197{
198#if SPL_DDR_MEMORYSIZE != 64 && SPL_DDR_MEMORYSIZE != 32
199# error "bad memory size"
200#endif
201
202 jz_writef(CPM_CLKGR, DDR(0));
203
57 REG_CPM_DRCG = 0x73; 204 REG_CPM_DRCG = 0x73;
58 mdelay(3); 205 mdelay(3);
59 REG_CPM_DRCG = 0x71; 206 REG_CPM_DRCG = 0x71;
@@ -67,7 +214,11 @@ static void ddr_init(void)
67 REG_DDRC_CTRL = 0; 214 REG_DDRC_CTRL = 0;
68 mdelay(3); 215 mdelay(3);
69 216
217#if SPL_DDR_MEMORYSIZE == 64
70 REG_DDRC_CFG = 0xa468a6c; 218 REG_DDRC_CFG = 0xa468a6c;
219#elif SPL_DDR_MEMORYSIZE == 32
220 REG_DDRC_CFG = 0xa46896c;
221#endif
71 REG_DDRC_CTRL = 2; 222 REG_DDRC_CTRL = 2;
72 REG_DDRPHY_DTAR = 0x150000; 223 REG_DDRPHY_DTAR = 0x150000;
73 REG_DDRPHY_DCR = 0; 224 REG_DDRPHY_DCR = 0;
@@ -91,7 +242,7 @@ static void ddr_init(void)
91 while(i > 0 && REG_DDRPHY_PGSR != 7 && REG_DDRPHY_PGSR != 0x1f) 242 while(i > 0 && REG_DDRPHY_PGSR != 7 && REG_DDRPHY_PGSR != 0x1f)
92 i -= 1; 243 i -= 1;
93 if(i == 0) 244 if(i == 0)
94 spl_error(); 245 return 1;
95 246
96#if SPL_DDR_NEED_BYPASS 247#if SPL_DDR_NEED_BYPASS
97 REG_DDRPHY_ACDLLCR = 0x80000000; 248 REG_DDRPHY_ACDLLCR = 0x80000000;
@@ -105,14 +256,18 @@ static void ddr_init(void)
105 while(i > 0 && REG_DDRPHY_PGSR != 0xf && REG_DDRPHY_PGSR != 0x1f) 256 while(i > 0 && REG_DDRPHY_PGSR != 0xf && REG_DDRPHY_PGSR != 0x1f)
106 i -= 1; 257 i -= 1;
107 if(i == 0) 258 if(i == 0)
108 spl_error(); 259 return 2;
109 260
110 REG_DDRC_APB_PHYRST_CFG = 0x400000; 261 REG_DDRC_APB_PHYRST_CFG = 0x400000;
111 mdelay(3); 262 mdelay(3);
112 REG_DDRC_APB_PHYRST_CFG = 0; 263 REG_DDRC_APB_PHYRST_CFG = 0;
113 mdelay(3); 264 mdelay(3);
114 265
266#if SPL_DDR_MEMORYSIZE == 64
115 REG_DDRC_CFG = 0xa468aec; 267 REG_DDRC_CFG = 0xa468aec;
268#elif SPL_DDR_MEMORYSIZE == 32
269 REG_DDRC_CFG = 0xa4689ec;
270#endif
116 REG_DDRC_CTRL = 2; 271 REG_DDRC_CTRL = 2;
117#if SPL_DDR_NEED_BYPASS 272#if SPL_DDR_NEED_BYPASS
118 REG_DDRPHY_PIR = 0x20020081; 273 REG_DDRPHY_PIR = 0x20020081;
@@ -128,15 +283,19 @@ static void ddr_init(void)
128 } 283 }
129 284
130 if(i == 0) 285 if(i == 0)
131 spl_error(); 286 return 3;
132 287
133 if((REG_DDRPHY_PGSR & 0x60) != 0 && REG_DDRPHY_PGSR != 0) 288 if((REG_DDRPHY_PGSR & 0x60) != 0 && REG_DDRPHY_PGSR != 0)
134 spl_error(); 289 return 4;
135 290
136 REG_DDRC_CTRL = 0; 291 REG_DDRC_CTRL = 0;
137 REG_DDRC_CTRL = 10; 292 REG_DDRC_CTRL = 10;
138 REG_DDRC_CTRL = 0; 293 REG_DDRC_CTRL = 0;
294#if SPL_DDR_MEMORYSIZE == 64
139 REG_DDRC_CFG = 0xa468a6c; 295 REG_DDRC_CFG = 0xa468a6c;
296#elif SPL_DDR_MEMORYSIZE == 32
297 REG_DDRC_CFG = 0xa46896c;
298#endif
140 REG_DDRC_TIMING1 = 0x2050501; 299 REG_DDRC_TIMING1 = 0x2050501;
141 REG_DDRC_TIMING2 = 0x4090404; 300 REG_DDRC_TIMING2 = 0x4090404;
142 REG_DDRC_TIMING3 = 0x2704030d; 301 REG_DDRC_TIMING3 = 0x2704030d;
@@ -149,11 +308,9 @@ static void ddr_init(void)
149#elif SPL_DDR_MEMORYSIZE == 32 308#elif SPL_DDR_MEMORYSIZE == 32
150 REG_DDRC_MMAP0 = 0x20fe; 309 REG_DDRC_MMAP0 = 0x20fe;
151 REG_DDRC_MMAP1 = 0x2200; 310 REG_DDRC_MMAP1 = 0x2200;
152#else
153# error "Unsupported DDR_MEMORYSIZE"
154#endif 311#endif
155 REG_DDRC_CTRL = 10; 312 REG_DDRC_CTRL = 10;
156 REG_DDRC_REFCNT = 0x2f0003; 313 REG_DDRC_REFCNT = 0x2f0003; /* is this adjustable for 32M? */
157 REG_DDRC_CTRL = 0xc91e; 314 REG_DDRC_CTRL = 0xc91e;
158 315
159#if SPL_DDR_MEMORYSIZE == 64 316#if SPL_DDR_MEMORYSIZE == 64
@@ -168,8 +325,6 @@ static void ddr_init(void)
168 REG_DDRC_REMAP3 = 0x01000908; 325 REG_DDRC_REMAP3 = 0x01000908;
169 REG_DDRC_REMAP4 = 0x0f0e0d0c; 326 REG_DDRC_REMAP4 = 0x0f0e0d0c;
170 REG_DDRC_REMAP5 = 0x13121110; 327 REG_DDRC_REMAP5 = 0x13121110;
171#else
172# error "Unsupported DDR_MEMORYSIZE"
173#endif 328#endif
174 329
175 REG_DDRC_STATUS &= ~0x40; 330 REG_DDRC_STATUS &= ~0x40;
@@ -184,69 +339,126 @@ static void ddr_init(void)
184#endif 339#endif
185 340
186 REG_DDRC_AUTOSR_EN = SPL_DDR_AUTOSR_EN; 341 REG_DDRC_AUTOSR_EN = SPL_DDR_AUTOSR_EN;
342 return 0;
187} 343}
188 344
189static void init(void) 345static void* get_load_buffer(const struct spl_boot_option* opt)
190{ 346{
191 /* from original firmware SPL */ 347 /* read to a temporary location if we need to decompress,
348 * otherwise simply read directly to the load address. */
349 if(opt->flags & BOOTFLAG_UCLPACK)
350 return spl_alloc(opt->storage_size);
351 else
352 return (void*)opt->load_addr;
353}
354
355/* Mapping of boot_sel[1:0] pins.
356 * See X1000 PM pg. 687, "XBurst Boot ROM Specification" */
357enum {
358 BSEL_MSC = 1,
359 BSEL_USB = 2,
360 BSEL_SFC = 3,
361};
362
363static uint32_t get_boot_sel(void)
364{
365 /* This variable holds the level of the boot_sel[2:0] pins at boot time,
366 * and is defined by the maskrom.
367 *
368 * We use it to detect when we're USB booting, but this isn't totally
369 * accurate because it only reflects the selected boot mode at reset and
370 * not the current mode -- if the original selection fails and we fall
371 * back to USB, this variable will only return the original selection.
372 */
373 return (*(uint32_t*)0xf40001ec) & 3;
374}
375
376typedef void(*entry_fn)(int, char**, int, int) __attribute__((noreturn));
377
378void spl_main(void)
379{
380 int rc, boot_option;
381 const struct spl_boot_option* opt;
382 void* load_buffer;
383 char** kargv = NULL;
384 int kargc = 0;
385
386 /* magic */
192 REG_CPM_PSWC0ST = 0x00; 387 REG_CPM_PSWC0ST = 0x00;
193 REG_CPM_PSWC1ST = 0x10; 388 REG_CPM_PSWC1ST = 0x10;
194 REG_CPM_PSWC2ST = 0x18; 389 REG_CPM_PSWC2ST = 0x18;
195 REG_CPM_PSWC3ST = 0x08; 390 REG_CPM_PSWC3ST = 0x08;
196 391
197 /* enable MPLL */ 392 /* set up boot flags */
198#if X1000_EXCLK_FREQ == 24000000 393 init_boot_flags();
199 /* 24 * (24+1) = 600 MHz */ 394 set_boot_option(BOOT_OPTION_ROCKBOX);
200 jz_writef(CPM_MPCR, ENABLE(1), BS(1), PLLN(0), PLLM(24), PLLOD(0));
201#elif X1000_EXCLK_FREQ == 26000000
202 /* 26 * (22+1) = 598 MHz */
203 jz_writef(CPM_MPCR, ENABLE(1), BS(1), PLLN(0), PLLM(22), PLLOD(0));
204#else
205# error "unknown EXCLK frequency"
206#endif
207 while(jz_readf(CPM_MPCR, ON) == 0);
208 395
209 /* set DDR clock to MPLL/3 = 200 MHz */ 396 /* early clock and DRAM init */
210 jz_writef(CPM_CLKGR, DDR(0)); 397 clk_init_early();
211 clk_set_ddr(X1000_CLK_MPLL, 3); 398 init_ost();
399 if(init_dram() != 0)
400 spl_error();
212 401
213 /* start OST so we can use mdelay/udelay */ 402 /* USB boot stops here */
214 jz_writef(CPM_CLKGR, OST(0)); 403 if(get_boot_sel() == BSEL_USB) {
215 jz_writef(OST_CTRL, PRESCALE2_V(BY_4)); 404 set_boot_flag(BOOT_FLAG_USB_BOOT);
216 jz_writef(OST_CLEAR, OST2(1)); 405 return;
217 jz_write(OST_2CNTH, 0); 406 }
218 jz_write(OST_2CNTL, 0);
219 jz_setf(OST_ENABLE, OST2);
220 407
221 /* init DDR memory */ 408 /* find out what we should boot */
222 ddr_init(); 409 boot_option = spl_get_boot_option();
223} 410 opt = &spl_boot_options[boot_option];
411 load_buffer = get_load_buffer(opt);
224 412
225/* This variable is defined by the maskrom. It's simply the level of the 413 /* save the selection for later */
226 * boot_sel[2:0] pins (GPIOs B28-30) at boot time. Meaning of the bits: 414 set_boot_option(boot_option);
227 *
228 * boot_sel[2] boot_sel[1] boot_sel[0] Description
229 * -----------------------------------------------------------------
230 * 1 X X EXCLK is 26 MHz
231 * 0 X X EXCLK is 24 MHz
232 * X 1 1 Boot from SFC0
233 * X 0 1 Boot from MSC0
234 * X 1 0 Boot from USB 2.0 device
235 * -----------------------------------------------------------------
236 * Source: X1000 PM pg. 687, "XBurst Boot ROM Specification"
237 */
238extern const uint32_t boot_sel;
239 415
240void spl_main(void) 416 /* finish up clock init */
241{ 417 clk_init();
242 /* Basic hardware init */
243 init();
244 418
245 /* If doing a USB boot, host PC will upload 2nd stage itself, 419 /* load the image from storage */
246 * we should not load anything from flash or change clocks. */ 420 rc = spl_storage_open();
247 if((boot_sel & 3) == 2) 421 if(rc != 0)
248 return; 422 spl_error();
423
424 rc = spl_storage_read(opt->storage_addr, opt->storage_size, load_buffer);
425 if(rc != 0)
426 spl_error();
427
428 /* handle compression */
429 switch(opt->flags & BOOTFLAG_COMPRESSED) {
430 case BOOTFLAG_UCLPACK: {
431 uint32_t out_size = X1000_DRAM_END - opt->load_addr;
432 rc = ucl_unpack((uint8_t*)load_buffer, opt->storage_size,
433 (uint8_t*)opt->load_addr, &out_size);
434 } break;
435
436 default:
437 break;
438 }
439
440 if(rc != 0)
441 spl_error();
442
443 /* call the setup hook */
444 if(opt->setup) {
445 rc = opt->setup();
446 if(rc != 0)
447 spl_error();
448 }
449
450 /* close off storage access */
451 spl_storage_close();
452
453 /* handle kernel command line, if specified */
454 if(opt->cmdline) {
455 kargv = (char**)opt->cmdline_addr;
456 kargv[kargc++] = 0;
457 kargv[kargc++] = (char*)opt->cmdline;
458 }
249 459
250 /* Just pass control to the target... */ 460 /* jump to the entry point */
251 spl_target_boot(); 461 entry_fn fn = (entry_fn)opt->exec_addr;
462 commit_discard_idcache();
463 fn(kargc, kargv, 0, 0);
252} 464}
diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000.h b/firmware/target/mips/ingenic_x1000/spl-x1000.h
index 81714a3ed2..6d60dbf880 100644
--- a/firmware/target/mips/ingenic_x1000/spl-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/spl-x1000.h
@@ -22,21 +22,51 @@
22#ifndef __SPL_X1000_H__ 22#ifndef __SPL_X1000_H__
23#define __SPL_X1000_H__ 23#define __SPL_X1000_H__
24 24
25/* TODO: this needs some refactoring... */ 25#include "boot-x1000.h"
26#include <stddef.h>
27#include <stdint.h>
26 28
27/* Called on a fatal error */ 29#define BOOTFLAG_COMPRESSED 0x0f /* mask for compression flags */
28extern void spl_error(void) __attribute__((noreturn)); 30#define BOOTFLAG_UCLPACK 0x01 /* image is compressed with 'uclpack' */
29 31
30/* Called by SPL to handle a main boot */ 32struct spl_boot_option {
31extern void spl_target_boot(void); 33 uint32_t storage_addr; /* image's location in storage */
34 uint32_t storage_size; /* number of bytes to load */
35 uint32_t load_addr; /* address to load image to */
36 uint32_t exec_addr; /* address of the entry point */
37 uint32_t flags; /* any special flags */
38 const char* cmdline; /* command line; use NULL if not needed */
39 uint32_t cmdline_addr; /* address to contain command line 'argv[]' */
40 int(*setup)(void); /* setup hook, called before jumping to image */
41};
32 42
33/* Invoked by SPL main routine to determine the boot option */ 43/* array of boot option descriptions */
34extern int spl_get_boot_option(void); 44extern const struct spl_boot_option spl_boot_options[];
45
46/* Memory allocator. Allocation starts from the top of DRAM and counts down.
47 * Allocation sizes are rounded up to a multiple of the cacheline size, so
48 * the returned address is always suitably aligned for DMA. */
49extern void* spl_alloc(size_t count);
35 50
36/* Do any setup/initialization needed for the given boot option, this 51/* Access to boot storage medium, eg. flash or MMC/SD card.
37 * will be called right before flushing caches + jumping to the image. 52 *
38 * Typical use is to set up system clocks, etc. 53 * Read address and length is given in bytes. To make life easier, no
54 * alignment restrictions are placed on the buffer, length, or address.
55 * The buffer doesn't even need to be in DRAM.
39 */ 56 */
40extern void spl_handle_pre_boot(int bootopt); 57extern int spl_storage_open(void);
58extern void spl_storage_close(void);
59extern int spl_storage_read(uint32_t addr, uint32_t length, void* buffer);
60
61/* Helpers for dual-booting with the Ingenic Linux OF */
62extern void spl_dualboot_init_clocktree(void);
63extern void spl_dualboot_init_uart2(void);
64
65/* Get the boot option selected by the user, eg. by a key press */
66extern int spl_get_boot_option(void);
67
68/* Called on a fatal error -- it should do something visible to the user
69 * like flash the backlight repeatedly. */
70extern void spl_error(void) __attribute__((noreturn));
41 71
42#endif /* __SPL_X1000_H__ */ 72#endif /* __SPL_X1000_H__ */
diff --git a/firmware/target/mips/ingenic_x1000/spl.lds b/firmware/target/mips/ingenic_x1000/spl.lds
index e932bd9c2e..b0169ab1aa 100644
--- a/firmware/target/mips/ingenic_x1000/spl.lds
+++ b/firmware/target/mips/ingenic_x1000/spl.lds
@@ -15,9 +15,6 @@ MEMORY {
15 15
16SECTIONS 16SECTIONS
17{ 17{
18 /* Mask ROM variables, addresses found by disassembly */
19 boot_sel = X1000_TCSM_BASE + 0x1ec;
20
21 .text : 18 .text :
22 { 19 {
23 *(.init.spl); 20 *(.init.spl);
diff --git a/firmware/target/mips/ingenic_x1000/system-x1000.c b/firmware/target/mips/ingenic_x1000/system-x1000.c
index 779bb2055c..d43c8e67e4 100644
--- a/firmware/target/mips/ingenic_x1000/system-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/system-x1000.c
@@ -27,6 +27,7 @@
27#include "dma-x1000.h" 27#include "dma-x1000.h"
28#include "irq-x1000.h" 28#include "irq-x1000.h"
29#include "clk-x1000.h" 29#include "clk-x1000.h"
30#include "boot-x1000.h"
30#include "x1000/cpm.h" 31#include "x1000/cpm.h"
31#include "x1000/ost.h" 32#include "x1000/ost.h"
32#include "x1000/tcu.h" 33#include "x1000/tcu.h"
@@ -59,6 +60,27 @@ static void system_init_irq(void)
59 write_c0_cause(M_CauseIV); 60 write_c0_cause(M_CauseIV);
60} 61}
61 62
63/* First function called by crt0.S */
64void system_early_init(void)
65{
66#if defined(FIIO_M3K) && !defined(BOOTLOADER)
67 /* HACK for compatibility: CPM scratchpad has undefined contents at
68 * time of reset and old bootloader revisions don't initialize it.
69 * Therefore we can't rely on its contents on the FiiO M3K. This does
70 * kind of break the entire point of boot flags, but right now they
71 * are really only used by the bootloader so it's not a huge issue.
72 * This hack should keep everything working as usual. */
73 if(jz_readf(CPM_MPCR, ON) == 0) {
74 init_boot_flags();
75 set_boot_option(BOOT_OPTION_ROCKBOX);
76 set_boot_flag(BOOT_FLAG_CLK_INIT);
77 }
78#endif
79
80 /* Finish up clock init */
81 clk_init();
82}
83
62/* First thing called from Rockbox main() */ 84/* First thing called from Rockbox main() */
63void system_init(void) 85void system_init(void)
64{ 86{