diff options
Diffstat (limited to 'firmware/target/mips/ingenic_x1000/fiiom3k')
-rw-r--r-- | firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c | 327 |
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 | ||
42 | static 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 | ||
55 | static const char recovery_cmdline[] = "mem=64M@0x0\ | 32 | static 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. */ | ||
62 | typedef void(*entry_fn)(int, char**, int, int); | ||
63 | 36 | ||
64 | struct 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 | ||
72 | const struct spl_boot_option spl_boot_options[] = { | 42 | const 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 | ||
101 | void 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 | |||
122 | nand_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 | |||
145 | void 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 | |||
207 | int spl_get_boot_option(void) | 70 | int 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 | ||
242 | void spl_handle_pre_boot(int bootopt) | 107 | void 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 | } |