diff options
Diffstat (limited to 'firmware/target/mips')
-rw-r--r-- | firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c | 132 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/fiiom3k/spl-target.h | 29 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/spl-x1000-defs.h | 66 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/spl-x1000.c | 312 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/spl-x1000.h | 49 |
5 files changed, 588 insertions, 0 deletions
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c new file mode 100644 index 0000000000..0ebe11e24d --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c | |||
@@ -0,0 +1,132 @@ | |||
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 | #include "spl-x1000.h" | ||
23 | #include "gpio-x1000.h" | ||
24 | #include "nand-x1000.h" | ||
25 | #include "system.h" | ||
26 | #include <string.h> | ||
27 | |||
28 | /* Boot select button state must remain stable for this duration | ||
29 | * before the choice will be accepted. Currently 100ms. | ||
30 | */ | ||
31 | #define BTN_STABLE_TIME (100 * (X1000_EXCLK_FREQ / 4000)) | ||
32 | |||
33 | static const char normal_cmdline[] = "mem=64M@0x0\ | ||
34 | no_console_suspend\ | ||
35 | console=ttyS2,115200n8\ | ||
36 | lpj=5009408\ | ||
37 | ip=off\ | ||
38 | init=/linuxrc\ | ||
39 | ubi.mtd=3\ | ||
40 | root=ubi0:rootfs\ | ||
41 | ubi.mtd=4\ | ||
42 | rootfstype=ubifs\ | ||
43 | rw\ | ||
44 | loglevel=8"; | ||
45 | |||
46 | static const char recovery_cmdline[] = "mem=64M@0x0\ | ||
47 | no_console_suspend\ | ||
48 | console=ttyS2,115200n8\ | ||
49 | lpj=5009408\ | ||
50 | ip=off"; | ||
51 | |||
52 | const struct spl_boot_option spl_boot_options[] = { | ||
53 | { | ||
54 | /* Rockbox: the first unused NAND page is 26 KiB in, and the | ||
55 | * remainder of the block is unused, giving us 102 KiB to use. | ||
56 | */ | ||
57 | .nand_addr = 0x6800, | ||
58 | .nand_size = 0x19800, | ||
59 | .load_addr = 0x80003ff8, /* first 8 bytes are bootloader ID */ | ||
60 | .exec_addr = 0x80004000, | ||
61 | .cmdline = NULL, | ||
62 | }, | ||
63 | { | ||
64 | /* Original firmware */ | ||
65 | .nand_addr = 0x20000, | ||
66 | .nand_size = 0x400000, | ||
67 | .load_addr = 0x80efffc0, | ||
68 | .exec_addr = 0x80f00000, | ||
69 | .cmdline = normal_cmdline, | ||
70 | }, | ||
71 | { | ||
72 | /* Recovery image */ | ||
73 | .nand_addr = 0x420000, | ||
74 | .nand_size = 0x500000, | ||
75 | .load_addr = 0x80efffc0, | ||
76 | .exec_addr = 0x80f00000, | ||
77 | .cmdline = recovery_cmdline, | ||
78 | }, | ||
79 | }; | ||
80 | |||
81 | void spl_error(void) | ||
82 | { | ||
83 | const int pin = (1 << 24); | ||
84 | |||
85 | /* Turn on button light */ | ||
86 | jz_clr(GPIO_INT(GPIO_C), pin); | ||
87 | jz_set(GPIO_MSK(GPIO_C), pin); | ||
88 | jz_clr(GPIO_PAT1(GPIO_C), pin); | ||
89 | jz_set(GPIO_PAT0(GPIO_C), pin); | ||
90 | |||
91 | while(1) { | ||
92 | /* Turn it off */ | ||
93 | mdelay(100); | ||
94 | jz_set(GPIO_PAT0(GPIO_C), pin); | ||
95 | |||
96 | /* Turn it on */ | ||
97 | mdelay(100); | ||
98 | jz_clr(GPIO_PAT0(GPIO_C), pin); | ||
99 | } | ||
100 | } | ||
101 | |||
102 | int spl_get_boot_option(void) | ||
103 | { | ||
104 | const uint32_t pinmask = (1 << 17) | (1 << 19); | ||
105 | |||
106 | uint32_t pin = 1, lastpin = 0; | ||
107 | uint32_t deadline = 0; | ||
108 | |||
109 | /* Configure the button GPIOs as inputs */ | ||
110 | gpio_config(GPIO_A, pinmask, GPIO_INPUT); | ||
111 | |||
112 | /* Poll the pins for a short duration to detect a keypress */ | ||
113 | do { | ||
114 | lastpin = pin; | ||
115 | pin = ~REG_GPIO_PIN(GPIO_A) & pinmask; | ||
116 | if(pin != lastpin) { | ||
117 | /* This will always be set on the first iteration */ | ||
118 | deadline = __ost_read32() + BTN_STABLE_TIME; | ||
119 | } | ||
120 | } while(__ost_read32() < deadline); | ||
121 | |||
122 | /* Play button boots original firmware */ | ||
123 | if(pin == (1 << 17)) | ||
124 | return SPL_BOOTOPT_ORIG_FW; | ||
125 | |||
126 | /* Volume up boots recovery */ | ||
127 | if(pin == (1 << 19)) | ||
128 | return SPL_BOOTOPT_RECOVERY; | ||
129 | |||
130 | /* Default is to boot Rockbox */ | ||
131 | return SPL_BOOTOPT_ROCKBOX; | ||
132 | } | ||
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/spl-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/spl-target.h new file mode 100644 index 0000000000..ac90508f44 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/spl-target.h | |||
@@ -0,0 +1,29 @@ | |||
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 __SPL_TARGET_H__ | ||
23 | #define __SPL_TARGET_H__ | ||
24 | |||
25 | #define SPL_DDR_MEMORYSIZE 64 | ||
26 | #define SPL_DDR_AUTOSR_EN 1 | ||
27 | #define SPL_DDR_NEED_BYPASS 1 | ||
28 | |||
29 | #endif /* __SPL_TARGET_H__ */ | ||
diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000-defs.h b/firmware/target/mips/ingenic_x1000/spl-x1000-defs.h new file mode 100644 index 0000000000..1d9f120ee2 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/spl-x1000-defs.h | |||
@@ -0,0 +1,66 @@ | |||
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 __SPL_X1000_DEFS_H__ | ||
23 | #define __SPL_X1000_DEFS_H__ | ||
24 | |||
25 | #include <stdint.h> | ||
26 | |||
27 | #ifdef __cplusplus | ||
28 | extern "C" { | ||
29 | #endif | ||
30 | |||
31 | #define SPL_CMD_BOOT 0 | ||
32 | #define SPL_CMD_FLASH_READ 1 | ||
33 | #define SPL_CMD_FLASH_WRITE 2 | ||
34 | |||
35 | #define SPL_BOOTOPT_CHOOSE 0 | ||
36 | #define SPL_BOOTOPT_ROCKBOX 1 | ||
37 | #define SPL_BOOTOPT_ORIG_FW 2 | ||
38 | #define SPL_BOOTOPT_RECOVERY 3 | ||
39 | #define SPL_BOOTOPT_NONE 4 | ||
40 | |||
41 | #define SPL_FLAG_SKIP_INIT (1 << 0) | ||
42 | |||
43 | #define SPL_MAX_SIZE (12 * 1024) | ||
44 | #define SPL_LOAD_ADDRESS 0xf4001000 | ||
45 | #define SPL_EXEC_ADDRESS 0xf4001800 | ||
46 | #define SPL_ARGUMENTS_ADDRESS 0xf40011f0 | ||
47 | #define SPL_STATUS_ADDRESS 0xf40011e0 | ||
48 | #define SPL_BUFFER_ADDRESS 0xa0004000 | ||
49 | |||
50 | struct x1000_spl_arguments { | ||
51 | uint32_t command; | ||
52 | uint32_t param1; | ||
53 | uint32_t param2; | ||
54 | uint32_t flags; | ||
55 | }; | ||
56 | |||
57 | struct x1000_spl_status { | ||
58 | int err_code; | ||
59 | int reserved; | ||
60 | }; | ||
61 | |||
62 | #ifdef __cplusplus | ||
63 | } | ||
64 | #endif | ||
65 | |||
66 | #endif /* __SPL_X1000_DEFS_H__ */ | ||
diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000.c b/firmware/target/mips/ingenic_x1000/spl-x1000.c new file mode 100644 index 0000000000..59e0fb687d --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/spl-x1000.c | |||
@@ -0,0 +1,312 @@ | |||
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 | #include "spl-x1000.h" | ||
23 | #include "spl-target.h" | ||
24 | #include "clk-x1000.h" | ||
25 | #include "nand-x1000.h" | ||
26 | #include "system.h" | ||
27 | #include "x1000/cpm.h" | ||
28 | #include "x1000/ost.h" | ||
29 | #include "x1000/ddrc.h" | ||
30 | #include "x1000/ddrc_apb.h" | ||
31 | #include "x1000/ddrphy.h" | ||
32 | |||
33 | struct x1000_spl_arguments* const spl_arguments = | ||
34 | (struct x1000_spl_arguments*)SPL_ARGUMENTS_ADDRESS; | ||
35 | |||
36 | struct x1000_spl_status* const spl_status = | ||
37 | (struct x1000_spl_status*)SPL_STATUS_ADDRESS; | ||
38 | |||
39 | /* defined to be Linux compatible; Rockbox needs no arguments so there | ||
40 | * is no harm in passing them and we save a little code size */ | ||
41 | typedef void(*entry_fn)(int, char**, int, int); | ||
42 | |||
43 | /* Note: This is based purely on disassembly of the SPL from the FiiO M3K. | ||
44 | * The code there is somewhat generic and corresponds roughly to Ingenic's | ||
45 | * U-Boot code, but isn't entirely the same. | ||
46 | * | ||
47 | * I converted all the runtime conditionals to compile-time ones in order to | ||
48 | * save code space, since they should be constant for any given target. | ||
49 | * | ||
50 | * I haven't bothered to decode all the register fields. Some of the values | ||
51 | * written are going to bits documented as "Reserved" by Ingenic, but their | ||
52 | * documentation doesn't seem completely reliable, so either these are bits | ||
53 | * which _do_ have a purpose, or they're only defined on other Ingenic CPUs. | ||
54 | * | ||
55 | * The DDR PHY registers appear to be from Synopsys "PHY Utility Block Lite". | ||
56 | * These aren't documented by Ingenic, but the addresses and names can be found | ||
57 | * in their U-Boot code. | ||
58 | */ | ||
59 | static void ddr_init(void) | ||
60 | { | ||
61 | REG_CPM_DRCG = 0x73; | ||
62 | mdelay(3); | ||
63 | REG_CPM_DRCG = 0x71; | ||
64 | mdelay(3); | ||
65 | REG_DDRC_APB_PHYRST_CFG = 0x1a00001; | ||
66 | mdelay(3); | ||
67 | REG_DDRC_APB_PHYRST_CFG = 0; | ||
68 | mdelay(3); | ||
69 | REG_DDRC_CTRL = 0xf00000; | ||
70 | mdelay(3); | ||
71 | REG_DDRC_CTRL = 0; | ||
72 | mdelay(3); | ||
73 | |||
74 | REG_DDRC_CFG = 0xa468a6c; | ||
75 | REG_DDRC_CTRL = 2; | ||
76 | REG_DDRPHY_DTAR = 0x150000; | ||
77 | REG_DDRPHY_DCR = 0; | ||
78 | REG_DDRPHY_MR0 = 0x42; | ||
79 | REG_DDRPHY_MR2 = 0x98; | ||
80 | REG_DDRPHY_PTR0 = 0x21000a; | ||
81 | REG_DDRPHY_PTR1 = 0xa09c40; | ||
82 | REG_DDRPHY_PTR2 = 0x280014; | ||
83 | REG_DDRPHY_DTPR0 = 0x1a69444a; | ||
84 | REG_DDRPHY_DTPR1 = 0x180090; | ||
85 | REG_DDRPHY_DTPR2 = 0x1ff99428; | ||
86 | REG_DDRPHY_DXGCR(0) = 0x90881; | ||
87 | REG_DDRPHY_DXGCR(1) = 0x90881; | ||
88 | REG_DDRPHY_DXGCR(2) = 0x90e80; | ||
89 | REG_DDRPHY_DXGCR(3) = 0x90e80; | ||
90 | REG_DDRPHY_PGCR = 0x1042e03; | ||
91 | REG_DDRPHY_ACIOCR = 0x30c00813; | ||
92 | REG_DDRPHY_DXCCR = 0x4912; | ||
93 | |||
94 | int i = 10000; | ||
95 | while(i > 0 && REG_DDRPHY_PGSR != 7 && REG_DDRPHY_PGSR != 0x1f) | ||
96 | i -= 1; | ||
97 | if(i == 0) | ||
98 | spl_error(); | ||
99 | |||
100 | #if SPL_DDR_NEED_BYPASS | ||
101 | REG_DDRPHY_ACDLLCR = 0x80000000; | ||
102 | REG_DDRPHY_DSGCR &= ~0x10; | ||
103 | REG_DDRPHY_DLLGCR |= 0x800000; | ||
104 | REG_DDRPHY_PIR = 0x20020041; | ||
105 | #else | ||
106 | REG_DDRPHY_PIR = 0x41; | ||
107 | #endif | ||
108 | |||
109 | while(i > 0 && REG_DDRPHY_PGSR != 0xf && REG_DDRPHY_PGSR != 0x1f) | ||
110 | i -= 1; | ||
111 | if(i == 0) | ||
112 | spl_error(); | ||
113 | |||
114 | REG_DDRC_APB_PHYRST_CFG = 0x400000; | ||
115 | mdelay(3); | ||
116 | REG_DDRC_APB_PHYRST_CFG = 0; | ||
117 | mdelay(3); | ||
118 | |||
119 | REG_DDRC_CFG = 0xa468aec; | ||
120 | REG_DDRC_CTRL = 2; | ||
121 | #if SPL_DDR_NEED_BYPASS | ||
122 | REG_DDRPHY_PIR = 0x20020081; | ||
123 | #else | ||
124 | REG_DDRPHY_PIR = 0x85; | ||
125 | #endif | ||
126 | |||
127 | i = 500000; | ||
128 | while(REG_DDRPHY_PGSR != 0x1f) { | ||
129 | if(REG_DDRPHY_PGSR & 0x70) | ||
130 | break; | ||
131 | i -= 1; | ||
132 | } | ||
133 | |||
134 | if(i == 0) | ||
135 | spl_error(); | ||
136 | |||
137 | if((REG_DDRPHY_PGSR & 0x60) != 0 && REG_DDRPHY_PGSR != 0) | ||
138 | spl_error(); | ||
139 | |||
140 | REG_DDRC_CTRL = 0; | ||
141 | REG_DDRC_CTRL = 10; | ||
142 | REG_DDRC_CTRL = 0; | ||
143 | REG_DDRC_CFG = 0xa468a6c; | ||
144 | REG_DDRC_TIMING1 = 0x2050501; | ||
145 | REG_DDRC_TIMING2 = 0x4090404; | ||
146 | REG_DDRC_TIMING3 = 0x2704030d; | ||
147 | REG_DDRC_TIMING4 = 0xb7a0251; | ||
148 | REG_DDRC_TIMING5 = 0xff090200; | ||
149 | REG_DDRC_TIMING6 = 0xa0a0202; | ||
150 | #if SPL_DDR_MEMORYSIZE == 64 | ||
151 | REG_DDRC_MMAP0 = 0x20fc; | ||
152 | REG_DDRC_MMAP1 = 0x2400; | ||
153 | #elif SPL_DDR_MEMORYSIZE == 32 | ||
154 | REG_DDRC_MMAP0 = 0x20fe; | ||
155 | REG_DDRC_MMAP1 = 0x2200; | ||
156 | #else | ||
157 | # error "Unsupported DDR_MEMORYSIZE" | ||
158 | #endif | ||
159 | REG_DDRC_CTRL = 10; | ||
160 | REG_DDRC_REFCNT = 0x2f0003; | ||
161 | REG_DDRC_CTRL = 0xc91e; | ||
162 | |||
163 | #if SPL_DDR_MEMORYSIZE == 64 | ||
164 | REG_DDRC_REMAP1 = 0x03020c0b; | ||
165 | REG_DDRC_REMAP2 = 0x07060504; | ||
166 | REG_DDRC_REMAP3 = 0x000a0908; | ||
167 | REG_DDRC_REMAP4 = 0x0f0e0d01; | ||
168 | REG_DDRC_REMAP5 = 0x13121110; | ||
169 | #elif SPL_DDR_MEMORYSIZE == 32 | ||
170 | REG_DDRC_REMAP1 = 0x03020b0a; | ||
171 | REG_DDRC_REMAP2 = 0x07060504; | ||
172 | REG_DDRC_REMAP3 = 0x01000908; | ||
173 | REG_DDRC_REMAP4 = 0x0f0e0d0c; | ||
174 | REG_DDRC_REMAP5 = 0x13121110; | ||
175 | #else | ||
176 | # error "Unsupported DDR_MEMORYSIZE" | ||
177 | #endif | ||
178 | |||
179 | REG_DDRC_STATUS &= ~0x40; | ||
180 | |||
181 | #if SPL_DDR_AUTOSR_EN | ||
182 | #if SPL_DDR_NEED_BYPASS | ||
183 | jz_writef(CPM_DDRCDR, GATE_EN(1)); | ||
184 | REG_DDRC_APB_CLKSTP_CFG = 0x9000000f; | ||
185 | #else | ||
186 | REG_DDRC_DLP = 0; | ||
187 | #endif | ||
188 | #endif | ||
189 | |||
190 | REG_DDRC_AUTOSR_EN = SPL_DDR_AUTOSR_EN; | ||
191 | } | ||
192 | |||
193 | static void init(void) | ||
194 | { | ||
195 | /* from original firmware SPL */ | ||
196 | REG_CPM_PSWC0ST = 0x00; | ||
197 | REG_CPM_PSWC1ST = 0x10; | ||
198 | REG_CPM_PSWC2ST = 0x18; | ||
199 | REG_CPM_PSWC3ST = 0x08; | ||
200 | |||
201 | /* enable MPLL */ | ||
202 | #if X1000_EXCLK_FREQ == 24000000 | ||
203 | /* 24 * (24+1) = 600 MHz */ | ||
204 | jz_writef(CPM_MPCR, ENABLE(1), BS(1), PLLN(0), PLLM(24), PLLOD(0)); | ||
205 | #elif X1000_EXCLK_FREQ == 26000000 | ||
206 | /* 26 * (22+1) = 598 MHz */ | ||
207 | jz_writef(CPM_MPCR, ENABLE(1), BS(1), PLLN(0), PLLM(22), PLLOD(0)); | ||
208 | #else | ||
209 | # error "unknown EXCLK frequency" | ||
210 | #endif | ||
211 | while(jz_readf(CPM_MPCR, ON) == 0); | ||
212 | |||
213 | /* set DDR clock to MPLL/3 = 200 MHz */ | ||
214 | jz_writef(CPM_CLKGR, DDR(0)); | ||
215 | clk_set_ddr(X1000_CLK_MPLL, 3); | ||
216 | |||
217 | /* start OST so we can use mdelay/udelay */ | ||
218 | jz_writef(CPM_CLKGR, OST(0)); | ||
219 | jz_writef(OST_CTRL, PRESCALE2_V(BY_4)); | ||
220 | jz_writef(OST_CLEAR, OST2(1)); | ||
221 | jz_write(OST_2CNTH, 0); | ||
222 | jz_write(OST_2CNTL, 0); | ||
223 | jz_setf(OST_ENABLE, OST2); | ||
224 | |||
225 | /* init DDR memory */ | ||
226 | ddr_init(); | ||
227 | } | ||
228 | |||
229 | static int nandread(uint32_t addr, uint32_t size, void* buffer) | ||
230 | { | ||
231 | int rc; | ||
232 | |||
233 | if((rc = nand_open())) | ||
234 | return rc; | ||
235 | |||
236 | rc = nand_read_bytes(addr, size, buffer); | ||
237 | nand_close(); | ||
238 | return rc; | ||
239 | } | ||
240 | |||
241 | static int nandwrite(uint32_t addr, uint32_t size, void* buffer) | ||
242 | { | ||
243 | int rc; | ||
244 | |||
245 | if((rc = nand_open())) | ||
246 | return rc; | ||
247 | |||
248 | if((rc = nand_enable_writes(true))) | ||
249 | goto _end; | ||
250 | |||
251 | if((rc = nand_erase_bytes(addr, size))) | ||
252 | goto _end1; | ||
253 | |||
254 | rc = nand_write_bytes(addr, size, buffer); | ||
255 | |||
256 | _end1: | ||
257 | /* an error here is very unlikely, so ignore it */ | ||
258 | nand_enable_writes(false); | ||
259 | |||
260 | _end: | ||
261 | nand_close(); | ||
262 | return rc; | ||
263 | } | ||
264 | |||
265 | void main(void) | ||
266 | { | ||
267 | if(!(SPL_ARGUMENTS->flags & SPL_FLAG_SKIP_INIT)) | ||
268 | init(); | ||
269 | |||
270 | switch(SPL_ARGUMENTS->command) { | ||
271 | case SPL_CMD_BOOT: { | ||
272 | int option = SPL_ARGUMENTS->param1; | ||
273 | if(option == SPL_BOOTOPT_CHOOSE) | ||
274 | option = spl_get_boot_option(); | ||
275 | if(option == SPL_BOOTOPT_NONE) | ||
276 | return; | ||
277 | |||
278 | const struct spl_boot_option* opt = &spl_boot_options[option-1]; | ||
279 | if(nandread(opt->nand_addr, opt->nand_size, (void*)opt->load_addr)) | ||
280 | spl_error(); | ||
281 | |||
282 | /* TODO: implement dual boot */ | ||
283 | |||
284 | /* Reading the Linux command line from the bootloader is handled by | ||
285 | * arch/mips/xburst/core/prom.c -- see Ingenic kernel sources. | ||
286 | * | ||
287 | * Rockbox doesn't use arguments, but passing them does not hurt and it | ||
288 | * saves an unnecessary branch. | ||
289 | */ | ||
290 | entry_fn entry = (entry_fn)opt->exec_addr; | ||
291 | char** argv = (char**)0x80004000; | ||
292 | argv[0] = 0; | ||
293 | argv[1] = (char*)opt->cmdline; | ||
294 | |||
295 | commit_discard_idcache(); | ||
296 | entry(2, argv, 0, 0); | ||
297 | __builtin_unreachable(); | ||
298 | } | ||
299 | |||
300 | case SPL_CMD_FLASH_READ: | ||
301 | SPL_STATUS->err_code = nandread(SPL_ARGUMENTS->param1, | ||
302 | SPL_ARGUMENTS->param2, | ||
303 | (void*)SPL_BUFFER_ADDRESS); | ||
304 | return; | ||
305 | |||
306 | case SPL_CMD_FLASH_WRITE: | ||
307 | SPL_STATUS->err_code = nandwrite(SPL_ARGUMENTS->param1, | ||
308 | SPL_ARGUMENTS->param2, | ||
309 | (void*)SPL_BUFFER_ADDRESS); | ||
310 | return; | ||
311 | } | ||
312 | } | ||
diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000.h b/firmware/target/mips/ingenic_x1000/spl-x1000.h new file mode 100644 index 0000000000..44601438f3 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/spl-x1000.h | |||
@@ -0,0 +1,49 @@ | |||
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 __SPL_X1000_H__ | ||
23 | #define __SPL_X1000_H__ | ||
24 | |||
25 | #include "spl-x1000-defs.h" | ||
26 | |||
27 | #define SPL_ARGUMENTS ((struct x1000_spl_arguments*)SPL_ARGUMENTS_ADDRESS) | ||
28 | #define SPL_STATUS ((struct x1000_spl_status*)SPL_STATUS_ADDRESS) | ||
29 | |||
30 | struct spl_boot_option { | ||
31 | uint32_t nand_addr; | ||
32 | uint32_t nand_size; | ||
33 | uint32_t load_addr; | ||
34 | uint32_t exec_addr; | ||
35 | const char* cmdline; /* for Linux */ | ||
36 | }; | ||
37 | |||
38 | /* Defined by target, indices are 0 = ROCKBOX, 1 = ORIG_FW, etc... */ | ||
39 | extern const struct spl_boot_option spl_boot_options[]; | ||
40 | |||
41 | /* Called on a fatal error */ | ||
42 | void spl_error(void) __attribute__((noreturn)); | ||
43 | |||
44 | /* When SPL boots with SPL_BOOTOPTION_CHOOSE, this function is invoked | ||
45 | * to let the target figure out the boot option based on buttons the | ||
46 | * user is pressing */ | ||
47 | extern int spl_get_boot_option(void); | ||
48 | |||
49 | #endif /* __SPL_X1000_H__ */ | ||