summaryrefslogtreecommitdiff
path: root/bootloader/fiiom3k-spl.c
diff options
context:
space:
mode:
Diffstat (limited to 'bootloader/fiiom3k-spl.c')
-rw-r--r--bootloader/fiiom3k-spl.c204
1 files changed, 204 insertions, 0 deletions
diff --git a/bootloader/fiiom3k-spl.c b/bootloader/fiiom3k-spl.c
new file mode 100644
index 0000000000..ec532d5789
--- /dev/null
+++ b/bootloader/fiiom3k-spl.c
@@ -0,0 +1,204 @@
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" :: "r"(execaddr));
203 __builtin_unreachable();
204}