diff options
author | Aidan MacDonald <amachronic@protonmail.com> | 2021-04-07 22:11:01 +0100 |
---|---|---|
committer | Aidan MacDonald <amachronic@protonmail.com> | 2021-04-17 20:22:49 +0000 |
commit | 1b8542490da3283dfa0ce0f3363f16eab0609815 (patch) | |
tree | d3775ef4ee2739e0f140dc70f70e422bc06059f3 /bootloader | |
parent | 85fbbd9c7f3e1ac84910a16095a297cbe13a8123 (diff) | |
download | rockbox-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 'bootloader')
-rw-r--r-- | bootloader/SOURCES | 7 | ||||
-rw-r--r-- | bootloader/fiiom3k-spl.c | 206 | ||||
-rw-r--r-- | bootloader/x1000-spl.c | 230 |
3 files changed, 1 insertions, 442 deletions
diff --git a/bootloader/SOURCES b/bootloader/SOURCES index db9e05644c..446bdac0e1 100644 --- a/bootloader/SOURCES +++ b/bootloader/SOURCES | |||
@@ -89,11 +89,6 @@ show_logo.c | |||
89 | #elif defined(SANSA_CONNECT) | 89 | #elif defined(SANSA_CONNECT) |
90 | sansaconnect.c | 90 | sansaconnect.c |
91 | show_logo.c | 91 | show_logo.c |
92 | #elif defined(FIIO_M3K) | 92 | #elif defined(FIIO_M3K) && !defined(BOOTLOADER_SPL) |
93 | #ifdef BOOTLOADER_SPL | ||
94 | x1000-spl.c | ||
95 | fiiom3k-spl.c | ||
96 | #else | ||
97 | fiiom3k.c | 93 | fiiom3k.c |
98 | #endif | 94 | #endif |
99 | #endif | ||
diff --git a/bootloader/fiiom3k-spl.c b/bootloader/fiiom3k-spl.c deleted file mode 100644 index 67b4b0a59c..0000000000 --- a/bootloader/fiiom3k-spl.c +++ /dev/null | |||
@@ -1,206 +0,0 @@ | |||
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 "config.h" | ||
23 | #include "nand-x1000.h" | ||
24 | #include "gpio-x1000.h" | ||
25 | #include "mmu-mips.h" | ||
26 | #include <string.h> | ||
27 | |||
28 | /* "fiio" in little endian */ | ||
29 | #define BOOTMAGIC 0x6f696966 | ||
30 | |||
31 | /* Argument structure needed by Linux */ | ||
32 | struct linux_kargs { | ||
33 | void* arg0; | ||
34 | void* arg1; | ||
35 | }; | ||
36 | |||
37 | #define LINUX_KARGSADDR 0x80004000 | ||
38 | |||
39 | static const char recovery_cmdline[] = "mem=xxM@0x0\ | ||
40 | no_console_suspend\ | ||
41 | console=ttyS2,115200n8\ | ||
42 | lpj=5009408\ | ||
43 | ip=off"; | ||
44 | |||
45 | static const char normal_cmdline[] = "mem=64M@0x0\ | ||
46 | no_console_suspend\ | ||
47 | console=ttyS2,115200n8\ | ||
48 | lpj=5009408\ | ||
49 | ip=off\ | ||
50 | init=/linuxrc\ | ||
51 | ubi.mtd=3\ | ||
52 | root=ubi0:rootfs\ | ||
53 | ubi.mtd=4\ | ||
54 | rootfstype=ubifs\ | ||
55 | rw\ | ||
56 | loglevel=8"; | ||
57 | |||
58 | #define BOOTOPTION_ROCKBOX 0 | ||
59 | #define BOOTOPTION_FIIOLINUX 1 | ||
60 | #define BOOTOPTION_RECOVERY 2 | ||
61 | #define NUM_BOOTOPTIONS 3 | ||
62 | |||
63 | static const struct bootoption { | ||
64 | uint32_t nand_addr; | ||
65 | uint32_t nand_size; | ||
66 | unsigned long load_addr; | ||
67 | unsigned long exec_addr; | ||
68 | const char* cmdline; | ||
69 | } boot_options[NUM_BOOTOPTIONS] = { | ||
70 | { | ||
71 | /* Rockbox: the first unused NAND page is 26 KiB in, and the | ||
72 | * remainder of the block is unused, giving us 102 KiB to use. | ||
73 | */ | ||
74 | .nand_addr = 0x6800, | ||
75 | .nand_size = 0x19800, | ||
76 | .load_addr = 0x80003ff8, /* first 8 bytes are bootloader ID */ | ||
77 | .exec_addr = 0x80004000, | ||
78 | .cmdline = NULL, | ||
79 | }, | ||
80 | { | ||
81 | /* Original firmware */ | ||
82 | .nand_addr = 0x20000, | ||
83 | .nand_size = 0x400000, | ||
84 | .load_addr = 0x80efffc0, | ||
85 | .exec_addr = 0x80f00000, | ||
86 | .cmdline = normal_cmdline, | ||
87 | }, | ||
88 | { | ||
89 | /* Recovery image */ | ||
90 | .nand_addr = 0x420000, | ||
91 | .nand_size = 0x500000, | ||
92 | .load_addr = 0x80efffc0, | ||
93 | .exec_addr = 0x80f00000, | ||
94 | .cmdline = recovery_cmdline, | ||
95 | }, | ||
96 | }; | ||
97 | |||
98 | /* Simple diagnostic if something goes wrong -- a little nicer than wondering | ||
99 | * what's going on when the machine hangs | ||
100 | */ | ||
101 | void die(void) | ||
102 | { | ||
103 | const int 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 | /* Boot select button state must remain stable for this duration | ||
123 | * before the choice will be accepted. Currently 100ms. | ||
124 | */ | ||
125 | #define BTN_STABLE_TIME (100 * (X1000_EXCLK_FREQ / 4000)) | ||
126 | |||
127 | int get_boot_option(void) | ||
128 | { | ||
129 | const uint32_t pinmask = (1 << 17) | (1 << 19); | ||
130 | |||
131 | uint32_t pin = 1, lastpin = 0; | ||
132 | uint32_t deadline = 0; | ||
133 | |||
134 | /* Configure the button GPIOs as inputs */ | ||
135 | gpio_config(GPIO_A, pinmask, GPIO_INPUT); | ||
136 | |||
137 | /* Poll the pins for a short duration to detect a keypress */ | ||
138 | do { | ||
139 | lastpin = pin; | ||
140 | pin = ~REG_GPIO_PIN(GPIO_A) & pinmask; | ||
141 | if(pin != lastpin) { | ||
142 | /* This will always be set on the first iteration */ | ||
143 | deadline = __ost_read32() + BTN_STABLE_TIME; | ||
144 | } | ||
145 | } while(__ost_read32() < deadline); | ||
146 | |||
147 | /* Play button boots original firmware */ | ||
148 | if(pin == (1 << 17)) | ||
149 | return BOOTOPTION_FIIOLINUX; | ||
150 | |||
151 | /* Volume up boots recovery */ | ||
152 | if(pin == (1 << 19)) | ||
153 | return BOOTOPTION_RECOVERY; | ||
154 | |||
155 | /* Default is to boot Rockbox */ | ||
156 | return BOOTOPTION_ROCKBOX; | ||
157 | } | ||
158 | |||
159 | void spl_main(void) | ||
160 | { | ||
161 | /* Get user boot option */ | ||
162 | int booti = get_boot_option(); | ||
163 | const struct bootoption* opt = &boot_options[booti]; | ||
164 | |||
165 | /* Load selected firmware from flash */ | ||
166 | if(nand_open()) | ||
167 | die(); | ||
168 | if(nand_read_bytes(opt->nand_addr, opt->nand_size, (void*)opt->load_addr)) | ||
169 | die(); | ||
170 | |||
171 | if(booti == BOOTOPTION_ROCKBOX) { | ||
172 | /* If bootloader is not installed, return back to boot ROM. | ||
173 | * Also read in the first eraseblock of NAND flash so it can be | ||
174 | * dumped back over USB. | ||
175 | */ | ||
176 | if(*(unsigned*)(opt->load_addr + 4) != BOOTMAGIC) { | ||
177 | nand_read_bytes(0, 128 * 1024, (void*)0x80000000); | ||
178 | commit_discard_idcache(); | ||
179 | return; | ||
180 | } | ||
181 | } else { | ||
182 | /* TODO: Linux boot not implemented yet | ||
183 | * | ||
184 | * - Have to initialize UART2, as it's used for the serial console | ||
185 | * - Must initialize APLL and change clocks over | ||
186 | * - There are some other clocks which need to be initialized | ||
187 | * - We should turn off OST since the OF SPL does not turn it on | ||
188 | */ | ||
189 | die(); | ||
190 | } | ||
191 | |||
192 | if(boot_options[booti].cmdline) { | ||
193 | /* Handle Linux command line arguments */ | ||
194 | struct linux_kargs* kargs = (struct linux_kargs*)LINUX_KARGSADDR; | ||
195 | kargs->arg0 = 0; | ||
196 | kargs->arg1 = (void*)boot_options[booti].cmdline; | ||
197 | } | ||
198 | |||
199 | /* Flush caches and jump to address */ | ||
200 | void* execaddr = (void*)opt->exec_addr; | ||
201 | commit_discard_idcache(); | ||
202 | __asm__ __volatile__ ("jr %0\n" | ||
203 | "nop\n" | ||
204 | :: "r"(execaddr)); | ||
205 | __builtin_unreachable(); | ||
206 | } | ||
diff --git a/bootloader/x1000-spl.c b/bootloader/x1000-spl.c deleted file mode 100644 index 1c780a9843..0000000000 --- a/bootloader/x1000-spl.c +++ /dev/null | |||
@@ -1,230 +0,0 @@ | |||
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 "system.h" | ||
23 | #include "clk-x1000.h" | ||
24 | #include "x1000/cpm.h" | ||
25 | #include "x1000/ost.h" | ||
26 | #include "x1000/ddrc.h" | ||
27 | #include "x1000/ddrc_apb.h" | ||
28 | #include "x1000/ddrphy.h" | ||
29 | |||
30 | #ifdef FIIO_M3K | ||
31 | # define DDR_USE_AUTOSR 1 | ||
32 | # define DDR_NEED_BYPASS 1 | ||
33 | # define DDR_MEMORYSIZE 64 | ||
34 | #else | ||
35 | # error "Please add DDR definitions for new target!" | ||
36 | #endif | ||
37 | |||
38 | #define hang() do { } while(1) | ||
39 | |||
40 | /* Target-specific routine to load & execute the Rockbox bootloader */ | ||
41 | extern void spl_main(void); | ||
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 | */ | ||
59 | static 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 | hang(); | ||
99 | |||
100 | #if 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 | hang(); | ||
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 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 | hang(); | ||
136 | |||
137 | if((REG_DDRPHY_PGSR & 0x60) != 0 && REG_DDRPHY_PGSR != 0) | ||
138 | hang(); | ||
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 DDR_MEMORYSIZE == 64 | ||
151 | REG_DDRC_MMAP0 = 0x20fc; | ||
152 | REG_DDRC_MMAP1 = 0x2400; | ||
153 | #elif 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 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 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 DDR_USE_AUTOSR | ||
182 | #if 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 = DDR_USE_AUTOSR; | ||
191 | } | ||
192 | |||
193 | void main(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 | /* jump to the target's main routine */ | ||
229 | spl_main(); | ||
230 | } | ||