summaryrefslogtreecommitdiff
path: root/firmware/target/mips/ingenic_x1000/spl-x1000.c
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-04-07 22:11:01 +0100
committerAidan MacDonald <amachronic@protonmail.com>2021-04-17 20:22:49 +0000
commit1b8542490da3283dfa0ce0f3363f16eab0609815 (patch)
treed3775ef4ee2739e0f140dc70f70e422bc06059f3 /firmware/target/mips/ingenic_x1000/spl-x1000.c
parent85fbbd9c7f3e1ac84910a16095a297cbe13a8123 (diff)
downloadrockbox-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/mips/ingenic_x1000/spl-x1000.c')
-rw-r--r--firmware/target/mips/ingenic_x1000/spl-x1000.c312
1 files changed, 312 insertions, 0 deletions
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
33struct x1000_spl_arguments* const spl_arguments =
34 (struct x1000_spl_arguments*)SPL_ARGUMENTS_ADDRESS;
35
36struct 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 */
41typedef 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 */
59static 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
193static 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
229static 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
241static 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
265void 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}