summaryrefslogtreecommitdiff
path: root/firmware/target
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target')
-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{