diff options
author | Aidan MacDonald <amachronic@protonmail.com> | 2021-04-07 22:11:01 +0100 |
---|---|---|
committer | Aidan MacDonald <amachronic@protonmail.com> | 2021-04-17 20:22:49 +0000 |
commit | 1b8542490da3283dfa0ce0f3363f16eab0609815 (patch) | |
tree | d3775ef4ee2739e0f140dc70f70e422bc06059f3 /firmware/target | |
parent | 85fbbd9c7f3e1ac84910a16095a297cbe13a8123 (diff) | |
download | rockbox-1b8542490da3283dfa0ce0f3363f16eab0609815.tar.gz rockbox-1b8542490da3283dfa0ce0f3363f16eab0609815.zip |
x1000: Redesign SPL, and allow it to flash the bootloader
SPL is now designed so core X1000 code is in control of the boot,
under the reasonable assumption that the device boots from flash.
It should not be too hard to adapt to other X1000 ports.
The biggest functional change is that the SPL can now read/write
the flash, under the control of a host computer. The SPL relies
on the boot ROM for USB communication, so the host has to execute
the SPL multiple times following a protocol.
Change-Id: I3ffaa00e4bf191e043c9df0e2e64d15193ff42c9
Diffstat (limited to 'firmware/target')
-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__ */ | ||