summaryrefslogtreecommitdiff
path: root/firmware/target/mips/ingenic_x1000/spl-x1000.c
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/spl-x1000.c
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/spl-x1000.c')
-rw-r--r--firmware/target/mips/ingenic_x1000/spl-x1000.c360
1 files changed, 286 insertions, 74 deletions
diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000.c b/firmware/target/mips/ingenic_x1000/spl-x1000.c
index fd664e231d..284b963e97 100644
--- a/firmware/target/mips/ingenic_x1000/spl-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/spl-x1000.c
@@ -19,41 +19,188 @@
19 * 19 *
20 ****************************************************************************/ 20 ****************************************************************************/
21 21
22#include "system.h"
22#include "spl-x1000.h" 23#include "spl-x1000.h"
23#include "clk-x1000.h" 24#include "clk-x1000.h"
24#include "system.h" 25#include "nand-x1000.h"
26#include "gpio-x1000.h"
27#include "boot-x1000.h"
25#include "x1000/cpm.h" 28#include "x1000/cpm.h"
26#include "x1000/ost.h" 29#include "x1000/ost.h"
30#include "x1000/uart.h"
27#include "x1000/ddrc.h" 31#include "x1000/ddrc.h"
28#include "x1000/ddrc_apb.h" 32#include "x1000/ddrc_apb.h"
29#include "x1000/ddrphy.h" 33#include "x1000/ddrphy.h"
34#include "ucl_decompress.h"
35#include <string.h>
30 36
31#ifdef FIIO_M3K 37#ifdef FIIO_M3K
32# define SPL_DDR_MEMORYSIZE 64 38# define SPL_DDR_MEMORYSIZE 64
33# define SPL_DDR_AUTOSR_EN 1 39# define SPL_DDR_AUTOSR_EN 1
34# define SPL_DDR_NEED_BYPASS 1 40# define SPL_DDR_NEED_BYPASS 1
35#else 41#else
36# error "please add SPL memory definitions" 42# error "please define DRAM settings"
37#endif 43#endif
38 44
39/* Note: This is based purely on disassembly of the SPL from the FiiO M3K. 45static void* heap = (void*)(X1000_SDRAM_BASE + X1000_SDRAM_SIZE);
40 * The code there is somewhat generic and corresponds roughly to Ingenic's 46static nand_drv* ndrv = NULL;
41 * U-Boot code, but isn't entirely the same. 47
48void* spl_alloc(size_t count)
49{
50 heap -= CACHEALIGN_UP(count);
51 memset(heap, 0, CACHEALIGN_UP(count));
52 return heap;
53}
54
55int spl_storage_open(void)
56{
57 /* We need to assign the GPIOs manually */
58 gpioz_configure(GPIO_A, 0x3f << 26, GPIOF_DEVICE(1));
59
60 /* Allocate NAND driver manually in DRAM */
61 ndrv = spl_alloc(sizeof(nand_drv));
62 ndrv->page_buf = spl_alloc(NAND_DRV_MAXPAGESIZE);
63 ndrv->scratch_buf = spl_alloc(NAND_DRV_SCRATCHSIZE);
64 ndrv->refcount = 0;
65
66 return nand_open(ndrv);
67}
68
69void spl_storage_close(void)
70{
71 nand_close(ndrv);
72}
73
74int spl_storage_read(uint32_t addr, uint32_t length, void* buffer)
75{
76 return nand_read_bytes(ndrv, addr, length, buffer);
77}
78
79/* Used by:
80 * - FiiO M3K
81 * - Shanling Q1
42 * 82 *
43 * I converted all the runtime conditionals to compile-time ones in order to 83 * Amend it and add #ifdefs for other targets if needed.
44 * save code space, since they should be constant for any given target. 84 */
85void spl_dualboot_init_clocktree(void)
86{
87 /* Make sure these are gated to match the OF behavior. */
88 jz_writef(CPM_CLKGR, PCM(1), MAC(1), LCD(1), MSC0(1), MSC1(1), OTG(1), CIM(1));
89
90 /* Set clock sources, and make sure every clock starts out stopped */
91 jz_writef(CPM_I2SCDR, CS_V(EXCLK));
92 jz_writef(CPM_PCMCDR, CS_V(EXCLK));
93
94 jz_writef(CPM_MACCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
95 while(jz_readf(CPM_MACCDR, BUSY));
96
97 jz_writef(CPM_LPCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
98 while(jz_readf(CPM_LPCDR, BUSY));
99
100 jz_writef(CPM_MSC0CDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
101 while(jz_readf(CPM_MSC0CDR, BUSY));
102
103 jz_writef(CPM_MSC1CDR, CE(1), STOP(1), CLKDIV(0xfe));
104 while(jz_readf(CPM_MSC1CDR, BUSY));
105
106 jz_writef(CPM_CIMCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
107 while(jz_readf(CPM_CIMCDR, BUSY));
108
109 jz_writef(CPM_USBCDR, CLKSRC_V(EXCLK), CE(1), STOP(1));
110 while(jz_readf(CPM_USBCDR, BUSY));
111}
112
113void spl_dualboot_init_uart2(void)
114{
115 /* Ungate the clock and select UART2 device function */
116 jz_writef(CPM_CLKGR, UART2(0));
117 gpioz_configure(GPIO_C, 3 << 30, GPIOF_DEVICE(1));
118
119 /* Disable all interrupts */
120 jz_write(UART_UIER(2), 0);
121
122 /* FIFO configuration */
123 jz_overwritef(UART_UFCR(2),
124 RDTR(3), /* FIFO trigger level = 60? */
125 UME(0), /* UART module disable */
126 DME(1), /* DMA mode enable? */
127 TFRT(1), /* transmit FIFO reset */
128 RFRT(1), /* receive FIFO reset */
129 FME(1)); /* FIFO mode enable */
130
131 /* IR mode configuration */
132 jz_overwritef(UART_ISR(2),
133 RDPL(1), /* Zero is negative pulse for receive */
134 TDPL(1), /* ... and for transmit */
135 XMODE(1), /* Pulse width 1.6us */
136 RCVEIR(0), /* Disable IR for recieve */
137 XMITIR(0)); /* ... and for transmit */
138
139 /* Line configuration */
140 jz_overwritef(UART_ULCR(2), DLAB(0),
141 WLS_V(8BITS), /* 8 bit words */
142 SBLS_V(1_STOP_BIT), /* 1 stop bit */
143 PARE(0), /* no parity */
144 SBK(0)); /* don't set break */
145
146 /* Set the baud rate... not too sure how this works. (Docs unclear!) */
147 const unsigned divisor = 0x0004;
148 jz_writef(UART_ULCR(2), DLAB(1));
149 jz_write(UART_UDLHR(2), (divisor >> 8) & 0xff);
150 jz_write(UART_UDLLR(2), divisor & 0xff);
151 jz_write(UART_UMR(2), 16);
152 jz_write(UART_UACR(2), 0);
153 jz_writef(UART_ULCR(2), DLAB(0));
154
155 /* Enable UART */
156 jz_overwritef(UART_UFCR(2),
157 RDTR(0), /* FIFO trigger level = 1 */
158 DME(0), /* DMA mode disable */
159 UME(1), /* UART module enable */
160 TFRT(1), /* transmit FIFO reset */
161 RFRT(1), /* receive FIFO reset */
162 FME(1)); /* FIFO mode enable */
163}
164
165static void init_ost(void)
166{
167 /* NOTE: the prescaler needs to be the same as in system-x1000.c */
168 jz_writef(CPM_CLKGR, OST(0));
169 jz_writef(OST_CTRL, PRESCALE2_V(BY_4));
170 jz_overwritef(OST_CLEAR, OST2(1));
171 jz_write(OST_2CNTH, 0);
172 jz_write(OST_2CNTL, 0);
173 jz_setf(OST_ENABLE, OST2);
174}
175
176/* NOTE: This is originally based on disassembly of the FiiO M3K SPL, which
177 * is in fact the GPL'd Ingenic X-Loader SPL. Similar stuff can be found in
178 * Ingenic's U-boot code.
179 *
180 * The source code for the Ingenic X-Loader SPL can be found in these repos:
181 * - https://github.com/JaminCheung/x-loader
182 * - https://github.com/YuanhuanLiang/X1000
183 *
184 * Runtime conditionals based on the SoC type, looked up by OTP bits in EFUSE,
185 * are converted to compile-time conditionals here, as they are constant for a
186 * given target and there is no point in wasting precious space on dead code.
45 * 187 *
46 * I haven't bothered to decode all the register fields. Some of the values 188 * I didn't decode the register fields; note that some values are documented as
47 * written are going to bits documented as "Reserved" by Ingenic, but their 189 * "reserved" in the X1000 PM. The X-Loader source might shed more light on it;
48 * documentation doesn't seem completely reliable, so either these are bits 190 * it's likely these bits have meaning only on other Ingenic SoCs.
49 * which _do_ have a purpose, or they're only defined on other Ingenic CPUs.
50 * 191 *
51 * The DDR PHY registers appear to be from Synopsys "PHY Utility Block Lite". 192 * The DDR PHY registers match Synopsys's "PHY Utility Block Lite." The names
52 * These aren't documented by Ingenic, but the addresses and names can be found 193 * of those registers & their fields can also be found in the X-Loader code,
53 * in their U-Boot code. 194 * but they're not documented by Ingenic.
54 */ 195 */
55static void ddr_init(void) 196static int init_dram(void)
56{ 197{
198#if SPL_DDR_MEMORYSIZE != 64 && SPL_DDR_MEMORYSIZE != 32
199# error "bad memory size"
200#endif
201
202 jz_writef(CPM_CLKGR, DDR(0));
203
57 REG_CPM_DRCG = 0x73; 204 REG_CPM_DRCG = 0x73;
58 mdelay(3); 205 mdelay(3);
59 REG_CPM_DRCG = 0x71; 206 REG_CPM_DRCG = 0x71;
@@ -67,7 +214,11 @@ static void ddr_init(void)
67 REG_DDRC_CTRL = 0; 214 REG_DDRC_CTRL = 0;
68 mdelay(3); 215 mdelay(3);
69 216
217#if SPL_DDR_MEMORYSIZE == 64
70 REG_DDRC_CFG = 0xa468a6c; 218 REG_DDRC_CFG = 0xa468a6c;
219#elif SPL_DDR_MEMORYSIZE == 32
220 REG_DDRC_CFG = 0xa46896c;
221#endif
71 REG_DDRC_CTRL = 2; 222 REG_DDRC_CTRL = 2;
72 REG_DDRPHY_DTAR = 0x150000; 223 REG_DDRPHY_DTAR = 0x150000;
73 REG_DDRPHY_DCR = 0; 224 REG_DDRPHY_DCR = 0;
@@ -91,7 +242,7 @@ static void ddr_init(void)
91 while(i > 0 && REG_DDRPHY_PGSR != 7 && REG_DDRPHY_PGSR != 0x1f) 242 while(i > 0 && REG_DDRPHY_PGSR != 7 && REG_DDRPHY_PGSR != 0x1f)
92 i -= 1; 243 i -= 1;
93 if(i == 0) 244 if(i == 0)
94 spl_error(); 245 return 1;
95 246
96#if SPL_DDR_NEED_BYPASS 247#if SPL_DDR_NEED_BYPASS
97 REG_DDRPHY_ACDLLCR = 0x80000000; 248 REG_DDRPHY_ACDLLCR = 0x80000000;
@@ -105,14 +256,18 @@ static void ddr_init(void)
105 while(i > 0 && REG_DDRPHY_PGSR != 0xf && REG_DDRPHY_PGSR != 0x1f) 256 while(i > 0 && REG_DDRPHY_PGSR != 0xf && REG_DDRPHY_PGSR != 0x1f)
106 i -= 1; 257 i -= 1;
107 if(i == 0) 258 if(i == 0)
108 spl_error(); 259 return 2;
109 260
110 REG_DDRC_APB_PHYRST_CFG = 0x400000; 261 REG_DDRC_APB_PHYRST_CFG = 0x400000;
111 mdelay(3); 262 mdelay(3);
112 REG_DDRC_APB_PHYRST_CFG = 0; 263 REG_DDRC_APB_PHYRST_CFG = 0;
113 mdelay(3); 264 mdelay(3);
114 265
266#if SPL_DDR_MEMORYSIZE == 64
115 REG_DDRC_CFG = 0xa468aec; 267 REG_DDRC_CFG = 0xa468aec;
268#elif SPL_DDR_MEMORYSIZE == 32
269 REG_DDRC_CFG = 0xa4689ec;
270#endif
116 REG_DDRC_CTRL = 2; 271 REG_DDRC_CTRL = 2;
117#if SPL_DDR_NEED_BYPASS 272#if SPL_DDR_NEED_BYPASS
118 REG_DDRPHY_PIR = 0x20020081; 273 REG_DDRPHY_PIR = 0x20020081;
@@ -128,15 +283,19 @@ static void ddr_init(void)
128 } 283 }
129 284
130 if(i == 0) 285 if(i == 0)
131 spl_error(); 286 return 3;
132 287
133 if((REG_DDRPHY_PGSR & 0x60) != 0 && REG_DDRPHY_PGSR != 0) 288 if((REG_DDRPHY_PGSR & 0x60) != 0 && REG_DDRPHY_PGSR != 0)
134 spl_error(); 289 return 4;
135 290
136 REG_DDRC_CTRL = 0; 291 REG_DDRC_CTRL = 0;
137 REG_DDRC_CTRL = 10; 292 REG_DDRC_CTRL = 10;
138 REG_DDRC_CTRL = 0; 293 REG_DDRC_CTRL = 0;
294#if SPL_DDR_MEMORYSIZE == 64
139 REG_DDRC_CFG = 0xa468a6c; 295 REG_DDRC_CFG = 0xa468a6c;
296#elif SPL_DDR_MEMORYSIZE == 32
297 REG_DDRC_CFG = 0xa46896c;
298#endif
140 REG_DDRC_TIMING1 = 0x2050501; 299 REG_DDRC_TIMING1 = 0x2050501;
141 REG_DDRC_TIMING2 = 0x4090404; 300 REG_DDRC_TIMING2 = 0x4090404;
142 REG_DDRC_TIMING3 = 0x2704030d; 301 REG_DDRC_TIMING3 = 0x2704030d;
@@ -149,11 +308,9 @@ static void ddr_init(void)
149#elif SPL_DDR_MEMORYSIZE == 32 308#elif SPL_DDR_MEMORYSIZE == 32
150 REG_DDRC_MMAP0 = 0x20fe; 309 REG_DDRC_MMAP0 = 0x20fe;
151 REG_DDRC_MMAP1 = 0x2200; 310 REG_DDRC_MMAP1 = 0x2200;
152#else
153# error "Unsupported DDR_MEMORYSIZE"
154#endif 311#endif
155 REG_DDRC_CTRL = 10; 312 REG_DDRC_CTRL = 10;
156 REG_DDRC_REFCNT = 0x2f0003; 313 REG_DDRC_REFCNT = 0x2f0003; /* is this adjustable for 32M? */
157 REG_DDRC_CTRL = 0xc91e; 314 REG_DDRC_CTRL = 0xc91e;
158 315
159#if SPL_DDR_MEMORYSIZE == 64 316#if SPL_DDR_MEMORYSIZE == 64
@@ -168,8 +325,6 @@ static void ddr_init(void)
168 REG_DDRC_REMAP3 = 0x01000908; 325 REG_DDRC_REMAP3 = 0x01000908;
169 REG_DDRC_REMAP4 = 0x0f0e0d0c; 326 REG_DDRC_REMAP4 = 0x0f0e0d0c;
170 REG_DDRC_REMAP5 = 0x13121110; 327 REG_DDRC_REMAP5 = 0x13121110;
171#else
172# error "Unsupported DDR_MEMORYSIZE"
173#endif 328#endif
174 329
175 REG_DDRC_STATUS &= ~0x40; 330 REG_DDRC_STATUS &= ~0x40;
@@ -184,69 +339,126 @@ static void ddr_init(void)
184#endif 339#endif
185 340
186 REG_DDRC_AUTOSR_EN = SPL_DDR_AUTOSR_EN; 341 REG_DDRC_AUTOSR_EN = SPL_DDR_AUTOSR_EN;
342 return 0;
187} 343}
188 344
189static void init(void) 345static void* get_load_buffer(const struct spl_boot_option* opt)
190{ 346{
191 /* from original firmware SPL */ 347 /* read to a temporary location if we need to decompress,
348 * otherwise simply read directly to the load address. */
349 if(opt->flags & BOOTFLAG_UCLPACK)
350 return spl_alloc(opt->storage_size);
351 else
352 return (void*)opt->load_addr;
353}
354
355/* Mapping of boot_sel[1:0] pins.
356 * See X1000 PM pg. 687, "XBurst Boot ROM Specification" */
357enum {
358 BSEL_MSC = 1,
359 BSEL_USB = 2,
360 BSEL_SFC = 3,
361};
362
363static uint32_t get_boot_sel(void)
364{
365 /* This variable holds the level of the boot_sel[2:0] pins at boot time,
366 * and is defined by the maskrom.
367 *
368 * We use it to detect when we're USB booting, but this isn't totally
369 * accurate because it only reflects the selected boot mode at reset and
370 * not the current mode -- if the original selection fails and we fall
371 * back to USB, this variable will only return the original selection.
372 */
373 return (*(uint32_t*)0xf40001ec) & 3;
374}
375
376typedef void(*entry_fn)(int, char**, int, int) __attribute__((noreturn));
377
378void spl_main(void)
379{
380 int rc, boot_option;
381 const struct spl_boot_option* opt;
382 void* load_buffer;
383 char** kargv = NULL;
384 int kargc = 0;
385
386 /* magic */
192 REG_CPM_PSWC0ST = 0x00; 387 REG_CPM_PSWC0ST = 0x00;
193 REG_CPM_PSWC1ST = 0x10; 388 REG_CPM_PSWC1ST = 0x10;
194 REG_CPM_PSWC2ST = 0x18; 389 REG_CPM_PSWC2ST = 0x18;
195 REG_CPM_PSWC3ST = 0x08; 390 REG_CPM_PSWC3ST = 0x08;
196 391
197 /* enable MPLL */ 392 /* set up boot flags */
198#if X1000_EXCLK_FREQ == 24000000 393 init_boot_flags();
199 /* 24 * (24+1) = 600 MHz */ 394 set_boot_option(BOOT_OPTION_ROCKBOX);
200 jz_writef(CPM_MPCR, ENABLE(1), BS(1), PLLN(0), PLLM(24), PLLOD(0));
201#elif X1000_EXCLK_FREQ == 26000000
202 /* 26 * (22+1) = 598 MHz */
203 jz_writef(CPM_MPCR, ENABLE(1), BS(1), PLLN(0), PLLM(22), PLLOD(0));
204#else
205# error "unknown EXCLK frequency"
206#endif
207 while(jz_readf(CPM_MPCR, ON) == 0);
208 395
209 /* set DDR clock to MPLL/3 = 200 MHz */ 396 /* early clock and DRAM init */
210 jz_writef(CPM_CLKGR, DDR(0)); 397 clk_init_early();
211 clk_set_ddr(X1000_CLK_MPLL, 3); 398 init_ost();
399 if(init_dram() != 0)
400 spl_error();
212 401
213 /* start OST so we can use mdelay/udelay */ 402 /* USB boot stops here */
214 jz_writef(CPM_CLKGR, OST(0)); 403 if(get_boot_sel() == BSEL_USB) {
215 jz_writef(OST_CTRL, PRESCALE2_V(BY_4)); 404 set_boot_flag(BOOT_FLAG_USB_BOOT);
216 jz_writef(OST_CLEAR, OST2(1)); 405 return;
217 jz_write(OST_2CNTH, 0); 406 }
218 jz_write(OST_2CNTL, 0);
219 jz_setf(OST_ENABLE, OST2);
220 407
221 /* init DDR memory */ 408 /* find out what we should boot */
222 ddr_init(); 409 boot_option = spl_get_boot_option();
223} 410 opt = &spl_boot_options[boot_option];
411 load_buffer = get_load_buffer(opt);
224 412
225/* This variable is defined by the maskrom. It's simply the level of the 413 /* save the selection for later */
226 * boot_sel[2:0] pins (GPIOs B28-30) at boot time. Meaning of the bits: 414 set_boot_option(boot_option);
227 *
228 * boot_sel[2] boot_sel[1] boot_sel[0] Description
229 * -----------------------------------------------------------------
230 * 1 X X EXCLK is 26 MHz
231 * 0 X X EXCLK is 24 MHz
232 * X 1 1 Boot from SFC0
233 * X 0 1 Boot from MSC0
234 * X 1 0 Boot from USB 2.0 device
235 * -----------------------------------------------------------------
236 * Source: X1000 PM pg. 687, "XBurst Boot ROM Specification"
237 */
238extern const uint32_t boot_sel;
239 415
240void spl_main(void) 416 /* finish up clock init */
241{ 417 clk_init();
242 /* Basic hardware init */
243 init();
244 418
245 /* If doing a USB boot, host PC will upload 2nd stage itself, 419 /* load the image from storage */
246 * we should not load anything from flash or change clocks. */ 420 rc = spl_storage_open();
247 if((boot_sel & 3) == 2) 421 if(rc != 0)
248 return; 422 spl_error();
423
424 rc = spl_storage_read(opt->storage_addr, opt->storage_size, load_buffer);
425 if(rc != 0)
426 spl_error();
427
428 /* handle compression */
429 switch(opt->flags & BOOTFLAG_COMPRESSED) {
430 case BOOTFLAG_UCLPACK: {
431 uint32_t out_size = X1000_DRAM_END - opt->load_addr;
432 rc = ucl_unpack((uint8_t*)load_buffer, opt->storage_size,
433 (uint8_t*)opt->load_addr, &out_size);
434 } break;
435
436 default:
437 break;
438 }
439
440 if(rc != 0)
441 spl_error();
442
443 /* call the setup hook */
444 if(opt->setup) {
445 rc = opt->setup();
446 if(rc != 0)
447 spl_error();
448 }
449
450 /* close off storage access */
451 spl_storage_close();
452
453 /* handle kernel command line, if specified */
454 if(opt->cmdline) {
455 kargv = (char**)opt->cmdline_addr;
456 kargv[kargc++] = 0;
457 kargv[kargc++] = (char*)opt->cmdline;
458 }
249 459
250 /* Just pass control to the target... */ 460 /* jump to the entry point */
251 spl_target_boot(); 461 entry_fn fn = (entry_fn)opt->exec_addr;
462 commit_discard_idcache();
463 fn(kargc, kargv, 0, 0);
252} 464}