summaryrefslogtreecommitdiff
path: root/bootloader
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 /bootloader
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 'bootloader')
-rw-r--r--bootloader/SOURCES7
-rw-r--r--bootloader/fiiom3k-spl.c206
-rw-r--r--bootloader/x1000-spl.c230
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)
90sansaconnect.c 90sansaconnect.c
91show_logo.c 91show_logo.c
92#elif defined(FIIO_M3K) 92#elif defined(FIIO_M3K) && !defined(BOOTLOADER_SPL)
93#ifdef BOOTLOADER_SPL
94x1000-spl.c
95fiiom3k-spl.c
96#else
97fiiom3k.c 93fiiom3k.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 */
32struct linux_kargs {
33 void* arg0;
34 void* arg1;
35};
36
37#define LINUX_KARGSADDR 0x80004000
38
39static const char recovery_cmdline[] = "mem=xxM@0x0\
40 no_console_suspend\
41 console=ttyS2,115200n8\
42 lpj=5009408\
43 ip=off";
44
45static 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
63static 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 */
101void 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
127int 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
159void 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 */
41extern 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 */
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 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
193void 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}