diff options
Diffstat (limited to 'firmware')
-rw-r--r-- | firmware/target/mips/ingenic_x1000/boot-x1000.h | 91 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/clk-x1000.c | 93 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/clk-x1000.h | 13 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/crt0.S | 3 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c | 327 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/spl-x1000.c | 360 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/spl-x1000.h | 52 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/spl.lds | 3 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/system-x1000.c | 22 |
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 | |||
28 | enum { | ||
29 | BOOT_OPTION_ROCKBOX = 0, | ||
30 | BOOT_OPTION_OFW_PLAYER, | ||
31 | BOOT_OPTION_OFW_RECOVERY, | ||
32 | }; | ||
33 | |||
34 | enum { | ||
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 | |||
49 | static 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 | |||
57 | static inline void init_boot_flags(void) | ||
58 | { | ||
59 | cpm_scratch_set(0); | ||
60 | } | ||
61 | |||
62 | static inline bool get_boot_flag(uint32_t bit) | ||
63 | { | ||
64 | return (REG_CPM_SCRATCH & bit) != 0; | ||
65 | } | ||
66 | |||
67 | static inline void set_boot_flag(uint32_t bit) | ||
68 | { | ||
69 | cpm_scratch_set(REG_CPM_SCRATCH | bit); | ||
70 | } | ||
71 | |||
72 | static inline void clr_boot_flag(uint32_t bit) | ||
73 | { | ||
74 | cpm_scratch_set(REG_CPM_SCRATCH & ~bit); | ||
75 | } | ||
76 | |||
77 | static 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 | |||
85 | static 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 | ||
232 | void 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 | |||
245 | void 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 | ||
244 | void clk_set_ccr_div(int cpu, int l2, int ahb0, int ahb2, int pclk) | 327 | void 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 | |||
34 | typedef enum x1000_clk_t { | 41 | typedef 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 */ |
60 | extern const char* clk_get_name(x1000_clk_t clk); | 67 | extern const char* clk_get_name(x1000_clk_t clk); |
61 | 68 | ||
69 | /* Clock initialization */ | ||
70 | extern void clk_init_early(void); | ||
71 | extern void clk_init(void); | ||
72 | |||
62 | /* Sets system clock multiplexers */ | 73 | /* Sets system clock multiplexers */ |
63 | extern void clk_set_ccr_mux(uint32_t muxbits); | 74 | extern void clk_set_ccr_mux(uint32_t muxbits); |
64 | 75 | ||
65 | /* Sets system clock dividers */ | 76 | /* Sets system clock dividers */ |
66 | extern void clk_set_ccr_div(int cpu, int l2, int ahb0, int ahb2, int pclk); | 77 | extern void clk_set_ccr_div(uint32_t divbits); |
67 | 78 | ||
68 | /* Sets DDR clock source and divider */ | 79 | /* Sets DDR clock source and divider */ |
69 | extern void clk_set_ddr(x1000_clk_t src, uint32_t div); | 80 | extern 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 | ||
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 | } |
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. | 45 | static void* heap = (void*)(X1000_SDRAM_BASE + X1000_SDRAM_SIZE); |
40 | * The code there is somewhat generic and corresponds roughly to Ingenic's | 46 | static nand_drv* ndrv = NULL; |
41 | * U-Boot code, but isn't entirely the same. | 47 | |
48 | void* spl_alloc(size_t count) | ||
49 | { | ||
50 | heap -= CACHEALIGN_UP(count); | ||
51 | memset(heap, 0, CACHEALIGN_UP(count)); | ||
52 | return heap; | ||
53 | } | ||
54 | |||
55 | int 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 | |||
69 | void spl_storage_close(void) | ||
70 | { | ||
71 | nand_close(ndrv); | ||
72 | } | ||
73 | |||
74 | int 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 | */ |
85 | void 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 | |||
113 | void 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 | |||
165 | static 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 | */ |
55 | static void ddr_init(void) | 196 | static 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 | ||
189 | static void init(void) | 345 | static 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" */ | ||
357 | enum { | ||
358 | BSEL_MSC = 1, | ||
359 | BSEL_USB = 2, | ||
360 | BSEL_SFC = 3, | ||
361 | }; | ||
362 | |||
363 | static 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 | |||
376 | typedef void(*entry_fn)(int, char**, int, int) __attribute__((noreturn)); | ||
377 | |||
378 | void 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 | */ | ||
238 | extern const uint32_t boot_sel; | ||
239 | 415 | ||
240 | void 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 */ |
28 | extern 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 */ | 32 | struct spl_boot_option { |
31 | extern 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 */ |
34 | extern int spl_get_boot_option(void); | 44 | extern 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. */ | ||
49 | extern 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 | */ |
40 | extern void spl_handle_pre_boot(int bootopt); | 57 | extern int spl_storage_open(void); |
58 | extern void spl_storage_close(void); | ||
59 | extern int spl_storage_read(uint32_t addr, uint32_t length, void* buffer); | ||
60 | |||
61 | /* Helpers for dual-booting with the Ingenic Linux OF */ | ||
62 | extern void spl_dualboot_init_clocktree(void); | ||
63 | extern void spl_dualboot_init_uart2(void); | ||
64 | |||
65 | /* Get the boot option selected by the user, eg. by a key press */ | ||
66 | extern 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. */ | ||
70 | extern 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 | ||
16 | SECTIONS | 16 | SECTIONS |
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 */ | ||
64 | void 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() */ |
63 | void system_init(void) | 85 | void system_init(void) |
64 | { | 86 | { |