summaryrefslogtreecommitdiff
path: root/firmware/target/mips/ingenic_x1000/spl-x1000.c
diff options
context:
space:
mode:
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}