summaryrefslogtreecommitdiff
path: root/firmware/target/mips/ingenic_x1000/fiiom3k
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-07-06 21:02:37 +0100
committerAidan MacDonald <amachronic@protonmail.com>2021-07-08 16:01:38 +0000
commit0e1a90ea1da0c1737363e9412781f734f39048d4 (patch)
treebef444477385a5eb8e8766ec75391ba48d63c613 /firmware/target/mips/ingenic_x1000/fiiom3k
parent65aa9ce57067f810d6a8f9417ec7d0a5741b404a (diff)
downloadrockbox-0e1a90ea1da0c1737363e9412781f734f39048d4.tar.gz
rockbox-0e1a90ea1da0c1737363e9412781f734f39048d4.zip
x1000: SPL refactoring
This streamlines the boot code a bit and reduces target specific boilerplate. The clock init hack used by the bootloader has been "standardized" and works for the main Rockbox binary now, so you can boot rockbox.bin over USB without special hacks. Change-Id: I7c1fac37df5a45873583ce6818eaedb9f71a782b
Diffstat (limited to 'firmware/target/mips/ingenic_x1000/fiiom3k')
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c327
1 files changed, 59 insertions, 268 deletions
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c
index 5680b1e548..cbbe8b1d5d 100644
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c
@@ -20,306 +20,97 @@
20 ****************************************************************************/ 20 ****************************************************************************/
21 21
22#include "system.h" 22#include "system.h"
23#include "ucl_decompress.h" 23#include "clk-x1000.h"
24#include "spl-x1000.h" 24#include "spl-x1000.h"
25#include "gpio-x1000.h" 25#include "gpio-x1000.h"
26#include "clk-x1000.h"
27#include "nand-x1000.h"
28#include "x1000/cpm.h"
29#include <string.h>
30#include <stdint.h>
31
32/* Available boot options */
33#define BOOTOPTION_ROCKBOX 0
34#define BOOTOPTION_ORIG_FW 1
35#define BOOTOPTION_RECOVERY 2
36
37/* Boot select button state must remain stable for this duration
38 * before the choice will be accepted. Currently 100ms.
39 */
40#define BTN_STABLE_TIME (100 * (X1000_EXCLK_FREQ / 4000))
41 26
42static const char normal_cmdline[] = "mem=64M@0x0\ 27#define CMDLINE_COMMON \
43 no_console_suspend\ 28 "mem=64M@0x0 no_console_suspend console=ttyS2,115200n8 lpj=5009408 ip=off"
44 console=ttyS2,115200n8\ 29#define CMDLINE_NORMAL \
45 lpj=5009408\ 30 " init=/linuxrc ubi.mtd=3 root=ubi0:rootfs ubi.mtd=4 rootfstype=ubifs rw loglevel=8"
46 ip=off\
47 init=/linuxrc\
48 ubi.mtd=3\
49 root=ubi0:rootfs\
50 ubi.mtd=4\
51 rootfstype=ubifs\
52 rw\
53 loglevel=8";
54 31
55static const char recovery_cmdline[] = "mem=64M@0x0\ 32static int dualboot_setup(void)
56 no_console_suspend\ 33{
57 console=ttyS2,115200n8\ 34 spl_dualboot_init_clocktree();
58 lpj=5009408\ 35 spl_dualboot_init_uart2();
59 ip=off";
60
61/* Entry point function type, defined to be Linux compatible. */
62typedef void(*entry_fn)(int, char**, int, int);
63 36
64struct spl_boot_option { 37 /* load PDMA MCU firmware */
65 uint32_t nand_addr; 38 jz_writef(CPM_CLKGR, PDMA(0));
66 uint32_t nand_size; 39 return spl_storage_read(0x4000, 0x2000, (void*)0xb3422000);
67 uint32_t load_addr; 40}
68 uint32_t exec_addr;
69 const char* cmdline; /* for Linux */
70};
71 41
72const struct spl_boot_option spl_boot_options[] = { 42const struct spl_boot_option spl_boot_options[] = {
73 { 43 [BOOT_OPTION_ROCKBOX] = {
74 /* Rockbox: the first unused NAND page is 26 KiB in, and the 44 .storage_addr = 0x6800,
75 * remainder of the block is unused, giving us 102 KiB to use. 45 .storage_size = 102 * 1024,
76 */ 46 .load_addr = X1000_DRAM_BASE,
77 .nand_addr = 0x6800,
78 .nand_size = 0x19800,
79 .load_addr = X1000_DRAM_END - 0x19800,
80 .exec_addr = X1000_DRAM_BASE, 47 .exec_addr = X1000_DRAM_BASE,
81 .cmdline = NULL, 48 .flags = BOOTFLAG_UCLPACK,
82 }, 49 },
83 { 50 [BOOT_OPTION_OFW_PLAYER] = {
84 /* Original firmware */ 51 .storage_addr = 0x20000,
85 .nand_addr = 0x20000, 52 .storage_size = 4 * 1024 * 1024,
86 .nand_size = 0x400000,
87 .load_addr = 0x80efffc0, 53 .load_addr = 0x80efffc0,
88 .exec_addr = 0x80f00000, 54 .exec_addr = 0x80f00000,
89 .cmdline = normal_cmdline, 55 .cmdline = CMDLINE_COMMON CMDLINE_NORMAL,
56 .cmdline_addr = 0x80004000,
57 .setup = dualboot_setup,
90 }, 58 },
91 { 59 [BOOT_OPTION_OFW_RECOVERY] = {
92 /* Recovery image */ 60 .storage_addr = 0x420000,
93 .nand_addr = 0x420000, 61 .storage_size = 5 * 1024 * 1024,
94 .nand_size = 0x500000,
95 .load_addr = 0x80efffc0, 62 .load_addr = 0x80efffc0,
96 .exec_addr = 0x80f00000, 63 .exec_addr = 0x80f00000,
97 .cmdline = recovery_cmdline, 64 .cmdline = CMDLINE_COMMON,
65 .cmdline_addr = 0x80004000,
66 .setup = dualboot_setup,
98 }, 67 },
99}; 68};
100 69
101void spl_error(void)
102{
103 const uint32_t pin = (1 << 24);
104
105 /* Turn on button light */
106 jz_clr(GPIO_INT(GPIO_C), pin);
107 jz_set(GPIO_MSK(GPIO_C), pin);
108 jz_clr(GPIO_PAT1(GPIO_C), pin);
109 jz_set(GPIO_PAT0(GPIO_C), pin);
110
111 while(1) {
112 /* Turn it off */
113 mdelay(100);
114 jz_set(GPIO_PAT0(GPIO_C), pin);
115
116 /* Turn it on */
117 mdelay(100);
118 jz_clr(GPIO_PAT0(GPIO_C), pin);
119 }
120}
121
122nand_drv* alloc_nand_drv(uint32_t laddr, uint32_t lsize)
123{
124 size_t need_size = sizeof(nand_drv) +
125 NAND_DRV_SCRATCHSIZE +
126 NAND_DRV_MAXPAGESIZE;
127
128 /* find a hole to keep the buffers */
129 uintptr_t addr;
130 if(X1000_SDRAM_BASE + need_size <= laddr)
131 addr = X1000_SDRAM_BASE;
132 else
133 addr = CACHEALIGN_UP(X1000_SDRAM_BASE + laddr + lsize);
134
135 uint8_t* page_buf = (uint8_t*)addr;
136 uint8_t* scratch_buf = page_buf + NAND_DRV_MAXPAGESIZE;
137 nand_drv* drv = (nand_drv*)(scratch_buf + NAND_DRV_SCRATCHSIZE);
138
139 drv->page_buf = page_buf;
140 drv->scratch_buf = scratch_buf;
141 drv->refcount = 0;
142 return drv;
143}
144
145void spl_target_boot(void)
146{
147 int opt_index = spl_get_boot_option();
148 const struct spl_boot_option* opt = &spl_boot_options[opt_index];
149 uint8_t* load_addr = (uint8_t*)opt->load_addr;
150
151 /* Clock setup etc. */
152 spl_handle_pre_boot(opt_index);
153
154 /* Since the GPIO refactor, the SFC driver no longer assigns its own pins.
155 * We don't want to call gpio_init(), to keep code size down. Assign them
156 * here manually, we shouldn't need any other pins. */
157 gpioz_configure(GPIO_A, 0x3f << 26, GPIOF_DEVICE(1));
158
159 /* Open NAND chip */
160 nand_drv* ndrv = alloc_nand_drv(opt->load_addr, opt->nand_size);
161 int rc = nand_open(ndrv);
162 if(rc)
163 spl_error();
164
165 /* For OF only: load DMA coprocessor's firmware from flash */
166 if(opt_index != BOOTOPTION_ROCKBOX) {
167 rc = nand_read_bytes(ndrv, 0x4000, 0x2000, (uint8_t*)0xb3422000);
168 if(rc)
169 goto nand_err;
170 }
171
172 /* Read the firmware */
173 rc = nand_read_bytes(ndrv, opt->nand_addr, opt->nand_size, load_addr);
174 if(rc)
175 goto nand_err;
176
177 /* Rockbox doesn't need the NAND; for the OF, we should leave it open */
178 if(opt_index == BOOTOPTION_ROCKBOX)
179 nand_close(ndrv);
180
181 /* Kernel arguments pointer, for Linux only */
182 char** kargv = (char**)0x80004000;
183
184 if(!opt->cmdline) {
185 /* Uncompress the rockbox bootloader */
186 uint32_t out_size = X1000_DRAM_END - opt->exec_addr;
187 int rc = ucl_unpack(load_addr, opt->nand_size,
188 (uint8_t*)opt->exec_addr, &out_size);
189 if(rc != UCL_E_OK)
190 spl_error();
191 } else {
192 /* Set kernel args */
193 kargv[0] = 0;
194 kargv[1] = (char*)opt->cmdline;
195 }
196
197 entry_fn entry = (entry_fn)opt->exec_addr;
198 commit_discard_idcache();
199 entry(2, kargv, 0, 0);
200 __builtin_unreachable();
201
202 nand_err:
203 nand_close(ndrv);
204 spl_error();
205}
206
207int spl_get_boot_option(void) 70int spl_get_boot_option(void)
208{ 71{
209 const uint32_t pinmask = (1 << 17) | (1 << 19); 72 /* Button debounce time in OST clock cycles */
73 const uint32_t btn_stable_time = 100 * (X1000_EXCLK_FREQ / 4000);
210 74
211 uint32_t pin = 1, lastpin = 0; 75 /* Buttons to poll */
76 const unsigned port = GPIO_A;
77 const uint32_t recov_pin = (1 << 19); /* Volume Up */
78 const uint32_t orig_fw_pin = (1 << 17); /* Play */
79
80 uint32_t pin = -1, lastpin = 0;
212 uint32_t deadline = 0; 81 uint32_t deadline = 0;
213 /* Iteration count guards against unlikely case of broken buttons 82 int iter_count = 30; /* to avoid an infinite loop */
214 * which never stabilize; if this occurs, we always boot Rockbox. */
215 int iter_count = 0;
216 const int max_iter_count = 30;
217 83
218 /* Configure the button GPIOs as inputs */ 84 /* set GPIOs to input state */
219 gpioz_configure(GPIO_A, pinmask, GPIOF_INPUT); 85 gpioz_configure(port, recov_pin|orig_fw_pin, GPIOF_INPUT);
220 86
221 /* Poll the pins for a short duration to detect a keypress */ 87 /* Poll until we get a stable reading */
222 do { 88 do {
223 lastpin = pin; 89 lastpin = pin;
224 pin = ~REG_GPIO_PIN(GPIO_A) & pinmask; 90 pin = ~REG_GPIO_PIN(port) & (recov_pin|orig_fw_pin);
225 if(pin != lastpin) { 91 if(pin != lastpin) {
226 /* This will always be set on the first iteration */ 92 deadline = __ost_read32() + btn_stable_time;
227 deadline = __ost_read32() + BTN_STABLE_TIME; 93 iter_count -= 1;
228 iter_count += 1;
229 } 94 }
230 } while(iter_count < max_iter_count && __ost_read32() < deadline); 95 } while(iter_count > 0 && __ost_read32() < deadline);
231 96
232 if(iter_count < max_iter_count && (pin & (1 << 17))) { 97 if(iter_count >= 0 && (pin & orig_fw_pin)) {
233 if(pin & (1 << 19)) 98 if(pin & recov_pin)
234 return BOOTOPTION_RECOVERY; /* Play+Volume Up */ 99 return BOOT_OPTION_OFW_RECOVERY;
235 else 100 else
236 return BOOTOPTION_ORIG_FW; /* Play */ 101 return BOOT_OPTION_OFW_PLAYER;
237 } else {
238 return BOOTOPTION_ROCKBOX; /* Volume Up or no buttons */
239 } 102 }
103
104 return BOOT_OPTION_ROCKBOX;
240} 105}
241 106
242void spl_handle_pre_boot(int bootopt) 107void spl_error(void)
243{ 108{
244 /* Move system to EXCLK so we can manipulate the PLLs */ 109 /* Flash the buttonlight */
245 clk_set_ccr_mux(CLKMUX_SCLK_A(EXCLK) | CLKMUX_CPU(SCLK_A) | 110 int level = 0;
246 CLKMUX_AHB0(SCLK_A) | CLKMUX_AHB2(SCLK_A)); 111 while(1) {
247 clk_set_ccr_div(1, 1, 1, 1, 1); 112 gpio_set_function(GPIO_PC(24), GPIOF_OUTPUT(level));
248 113 mdelay(100);
249 /* Enable APLL @ 1008 MHz (24 MHz EXCLK * 42 = 1008 MHz) */ 114 level = 1 - level;
250 jz_writef(CPM_APCR, BS(1), PLLM(41), PLLN(0), PLLOD(0), ENABLE(1));
251 while(jz_readf(CPM_APCR, ON) == 0);
252
253 /* System clock setup -- common to Rockbox and FiiO firmware
254 * ----
255 * CPU at 1 GHz, L2 cache at 500 MHz
256 * AHB0 and AHB2 at 200 MHz
257 * PCLK at 100 MHz
258 * DDR at 200 MHz
259 */
260
261 if(bootopt == BOOTOPTION_ROCKBOX) {
262 /* We don't use MPLL in Rockbox, so run everything from APLL. */
263 clk_set_ccr_div(1, 2, 5, 5, 10);
264 clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) | CLKMUX_CPU(SCLK_A) |
265 CLKMUX_AHB0(SCLK_A) | CLKMUX_AHB2(SCLK_A));
266 clk_set_ddr(X1000_CLK_SCLK_A, 5);
267
268 /* Turn off MPLL */
269 jz_writef(CPM_MPCR, ENABLE(0));
270 } else {
271 /* Typical ingenic setup -- 1008 MHz APLL, 600 MHz MPLL
272 * with APLL driving the CPU/L2 and MPLL driving busses. */
273 clk_set_ccr_div(1, 2, 3, 3, 6);
274 clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) | CLKMUX_CPU(SCLK_A) |
275 CLKMUX_AHB0(MPLL) | CLKMUX_AHB2(MPLL));
276
277 /* Mimic OF's clock gating setup */
278 jz_writef(CPM_CLKGR, PDMA(0), PCM(1), MAC(1), LCD(1),
279 MSC0(1), MSC1(1), OTG(1), CIM(1));
280
281 /* We now need to define the clock tree by fiddling with
282 * various CPM registers. Although the kernel has code to
283 * set up the clock tree itself, it isn't used and relies
284 * on the SPL for this. */
285 jz_writef(CPM_I2SCDR, CS_V(EXCLK));
286 jz_writef(CPM_PCMCDR, CS_V(EXCLK));
287
288 jz_writef(CPM_MACCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
289 while(jz_readf(CPM_MACCDR, BUSY));
290
291 jz_writef(CPM_LPCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
292 while(jz_readf(CPM_LPCDR, BUSY));
293
294 jz_writef(CPM_MSC0CDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
295 while(jz_readf(CPM_MSC0CDR, BUSY));
296
297 jz_writef(CPM_MSC1CDR, CE(1), STOP(1), CLKDIV(0xfe));
298 while(jz_readf(CPM_MSC1CDR, BUSY));
299
300 jz_writef(CPM_CIMCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
301 while(jz_readf(CPM_CIMCDR, BUSY));
302
303 jz_writef(CPM_USBCDR, CLKSRC_V(EXCLK), CE(1), STOP(1));
304 while(jz_readf(CPM_USBCDR, BUSY));
305
306 /* Handle UART initialization */
307 gpioz_configure(GPIO_C, 3 << 30, GPIOF_DEVICE(1));
308 jz_writef(CPM_CLKGR, UART2(0));
309
310 /* TODO: Stop being lazy and make this human readable */
311 volatile uint8_t* ub = (volatile uint8_t*)0xb0032000;
312 ub[0x04] = 0;
313 ub[0x08] = 0xef;
314 ub[0x20] = 0xfc;
315 ub[0x0c] = 3;
316 uint8_t uv = ub[0x0c];
317 ub[0x0c] = uv | 0x80;
318 ub[0x04] = 0;
319 ub[0x00] = 0x0d;
320 ub[0x24] = 0x10;
321 ub[0x28] = 0;
322 ub[0x0c] = uv & 0x7f;
323 ub[0x08] = 0x17;
324 } 115 }
325} 116}