summaryrefslogtreecommitdiff
path: root/bootloader
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-02-27 22:08:58 +0000
committerAidan MacDonald <amachronic@protonmail.com>2021-03-28 00:01:37 +0000
commit3ec66893e377b088c1284d2d23adb2aeea6d7965 (patch)
treeb647717f83ad56b15dc42cfdef5d04d68cd9bd6b /bootloader
parent83fcbedc65f4b9ae7e491ecf6f07c0af4b245f74 (diff)
New port: FiiO M3K on bare metal
Change-Id: I7517e7d5459e129dcfc9465c6fbd708619888fbe
Diffstat (limited to 'bootloader')
-rw-r--r--bootloader/SOURCES7
-rw-r--r--bootloader/fiiom3k-spl.c204
-rw-r--r--bootloader/fiiom3k.c96
-rw-r--r--bootloader/x1000-spl.c230
4 files changed, 537 insertions, 0 deletions
diff --git a/bootloader/SOURCES b/bootloader/SOURCES
index 3c9a3068ad..db9e05644c 100644
--- a/bootloader/SOURCES
+++ b/bootloader/SOURCES
@@ -89,4 +89,11 @@ show_logo.c
#elif defined(SANSA_CONNECT)
sansaconnect.c
show_logo.c
+#elif defined(FIIO_M3K)
+#ifdef BOOTLOADER_SPL
+x1000-spl.c
+fiiom3k-spl.c
+#else
+fiiom3k.c
+#endif
#endif
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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "config.h"
+#include "nand-x1000.h"
+#include "gpio-x1000.h"
+#include "mmu-mips.h"
+#include <string.h>
+
+/* "fiio" in little endian */
+#define BOOTMAGIC 0x6f696966
+
+/* Argument structure needed by Linux */
+struct linux_kargs {
+ void* arg0;
+ void* arg1;
+};
+
+#define LINUX_KARGSADDR 0x80004000
+
+static const char recovery_cmdline[] = "mem=xxM@0x0\
+ no_console_suspend\
+ console=ttyS2,115200n8\
+ lpj=5009408\
+ ip=off";
+
+static const char normal_cmdline[] = "mem=64M@0x0\
+ no_console_suspend\
+ console=ttyS2,115200n8\
+ lpj=5009408\
+ ip=off\
+ init=/linuxrc\
+ ubi.mtd=3\
+ root=ubi0:rootfs\
+ ubi.mtd=4\
+ rootfstype=ubifs\
+ rw\
+ loglevel=8";
+
+#define BOOTOPTION_ROCKBOX 0
+#define BOOTOPTION_FIIOLINUX 1
+#define BOOTOPTION_RECOVERY 2
+#define NUM_BOOTOPTIONS 3
+
+static const struct bootoption {
+ uint32_t nand_addr;
+ uint32_t nand_size;
+ unsigned long load_addr;
+ unsigned long exec_addr;
+ const char* cmdline;
+} boot_options[NUM_BOOTOPTIONS] = {
+ {
+ /* Rockbox: the first unused NAND page is 26 KiB in, and the
+ * remainder of the block is unused, giving us 102 KiB to use.
+ */
+ .nand_addr = 0x6800,
+ .nand_size = 0x19800,
+ .load_addr = 0x80003ff8, /* first 8 bytes are bootloader ID */
+ .exec_addr = 0x80004000,
+ .cmdline = NULL,
+ },
+ {
+ /* Original firmware */
+ .nand_addr = 0x20000,
+ .nand_size = 0x400000,
+ .load_addr = 0x80efffc0,
+ .exec_addr = 0x80f00000,
+ .cmdline = normal_cmdline,
+ },
+ {
+ /* Recovery image */
+ .nand_addr = 0x420000,
+ .nand_size = 0x500000,
+ .load_addr = 0x80efffc0,
+ .exec_addr = 0x80f00000,
+ .cmdline = recovery_cmdline,
+ },
+};
+
+/* Simple diagnostic if something goes wrong -- a little nicer than wondering
+ * what's going on when the machine hangs
+ */
+void die(void)
+{
+ const int pin = (1 << 24);
+
+ /* Turn on button light */
+ jz_clr(GPIO_INT(GPIO_C), pin);
+ jz_set(GPIO_MSK(GPIO_C), pin);
+ jz_clr(GPIO_PAT1(GPIO_C), pin);
+ jz_set(GPIO_PAT0(GPIO_C), pin);
+
+ while(1) {
+ /* Turn it off */
+ mdelay(100);
+ jz_set(GPIO_PAT0(GPIO_C), pin);
+
+ /* Turn it on */
+ mdelay(100);
+ jz_clr(GPIO_PAT0(GPIO_C), pin);
+ }
+}
+
+/* Boot select button state must remain stable for this duration
+ * before the choice will be accepted. Currently 100ms.
+ */
+#define BTN_STABLE_TIME (100 * (X1000_EXCLK_FREQ / 4000))
+
+int get_boot_option(void)
+{
+ const uint32_t pinmask = (1 << 17) | (1 << 19);
+
+ uint32_t pin = 1, lastpin = 0;
+ uint32_t deadline = 0;
+
+ /* Configure the button GPIOs as inputs */
+ gpio_config(GPIO_A, pinmask, GPIO_INPUT);
+
+ /* Poll the pins for a short duration to detect a keypress */
+ do {
+ lastpin = pin;
+ pin = ~REG_GPIO_PIN(GPIO_A) & pinmask;
+ if(pin != lastpin) {
+ /* This will always be set on the first iteration */
+ deadline = __ost_read32() + BTN_STABLE_TIME;
+ }
+ } while(__ost_read32() < deadline);
+
+ /* Play button boots original firmware */
+ if(pin == (1 << 17))
+ return BOOTOPTION_FIIOLINUX;
+
+ /* Volume up boots recovery */
+ if(pin == (1 << 19))
+ return BOOTOPTION_RECOVERY;
+
+ /* Default is to boot Rockbox */
+ return BOOTOPTION_ROCKBOX;
+}
+
+void spl_main(void)
+{
+ /* Get user boot option */
+ int booti = get_boot_option();
+ const struct bootoption* opt = &boot_options[booti];
+
+ /* Load selected firmware from flash */
+ if(nand_open())
+ die();
+ if(nand_read_bytes(opt->nand_addr, opt->nand_size, (void*)opt->load_addr))
+ die();
+
+ if(booti == BOOTOPTION_ROCKBOX) {
+ /* If bootloader is not installed, return back to boot ROM.
+ * Also read in the first eraseblock of NAND flash so it can be
+ * dumped back over USB.
+ */
+ if(*(unsigned*)(opt->load_addr + 4) != BOOTMAGIC) {
+ nand_read_bytes(0, 128 * 1024, (void*)0x80000000);
+ commit_discard_idcache();
+ return;
+ }
+ } else {
+ /* TODO: Linux boot not implemented yet
+ *
+ * - Have to initialize UART2, as it's used for the serial console
+ * - Must initialize APLL and change clocks over
+ * - There are some other clocks which need to be initialized
+ * - We should turn off OST since the OF SPL does not turn it on
+ */
+ die();
+ }
+
+ if(boot_options[booti].cmdline) {
+ /* Handle Linux command line arguments */
+ struct linux_kargs* kargs = (struct linux_kargs*)LINUX_KARGSADDR;
+ kargs->arg0 = 0;
+ kargs->arg1 = (void*)boot_options[booti].cmdline;
+ }
+
+ /* Flush caches and jump to address */
+ void* execaddr = (void*)opt->exec_addr;
+ commit_discard_idcache();
+ __asm__ __volatile__ ("jr %0" :: "r"(execaddr));
+ __builtin_unreachable();
+}
diff --git a/bootloader/fiiom3k.c b/bootloader/fiiom3k.c
new file mode 100644
index 0000000000..6108a37efc
--- /dev/null
+++ b/bootloader/fiiom3k.c
@@ -0,0 +1,96 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "system.h"
+#include "kernel/kernel-internal.h"
+#include "i2c.h"
+#include "power.h"
+#include "lcd.h"
+#include "backlight.h"
+#include "button.h"
+#include "storage.h"
+#include "file_internal.h"
+#include "disk.h"
+#include "rb-loader.h"
+#include "loader_strerror.h"
+
+/* Load address where the binary needs to be placed */
+extern unsigned char loadaddress[];
+
+/* Fixed buffer to contain the loaded binary in memory */
+extern unsigned char loadbuffer[];
+extern unsigned char loadbufferend[];
+#define MAX_LOAD_SIZE (loadbufferend - loadbuffer)
+
+void exec(void* dst, const void* src, int bytes)
+ __attribute__((noreturn, section(".icode")));
+
+void exec(void* dst, const void* src, int bytes)
+{
+ memcpy(dst, src, bytes);
+ commit_discard_idcache();
+ __asm__ __volatile__ ("jr %0" :: "r"(dst));
+ __builtin_unreachable();
+}
+
+static void error(const char* msg)
+{
+ /* Initialization of the LCD/buttons only if needed */
+ lcd_init();
+ backlight_init();
+ button_init();
+
+ lcd_clear_display();
+ lcd_puts(0, 0, msg);
+ lcd_puts(0, 2, "Press POWER to power off");
+ lcd_update();
+
+ while(button_get(true) != BUTTON_POWER);
+ power_off();
+}
+
+void main(void)
+{
+ system_init();
+ kernel_init();
+ i2c_init();
+ power_init();
+ enable_irq();
+
+ if(storage_init() < 0)
+ error("Storage initialization failed");
+
+ filesystem_init();
+
+ if(!storage_present(0))
+ error("No SD card present");
+
+ if(disk_mount_all() <= 0)
+ error("Unable to mount filesystem");
+
+ int loadsize = load_firmware(loadbuffer, BOOTFILE, MAX_LOAD_SIZE);
+ if(loadsize <= 0)
+ error(loader_strerror(loadsize));
+
+ disable_irq();
+
+ exec(loadaddress, loadbuffer, loadsize);
+}
diff --git a/bootloader/x1000-spl.c b/bootloader/x1000-spl.c
new file mode 100644
index 0000000000..1c780a9843
--- /dev/null
+++ b/bootloader/x1000-spl.c
@@ -0,0 +1,230 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2021 Aidan MacDonald
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "system.h"
+#include "clk-x1000.h"
+#include "x1000/cpm.h"
+#include "x1000/ost.h"
+#include "x1000/ddrc.h"
+#include "x1000/ddrc_apb.h"
+#include "x1000/ddrphy.h"
+
+#ifdef FIIO_M3K
+# define DDR_USE_AUTOSR 1
+# define DDR_NEED_BYPASS 1
+# define DDR_MEMORYSIZE 64
+#else
+# error "Please add DDR definitions for new target!"
+#endif
+
+#define hang() do { } while(1)
+
+/* Target-specific routine to load & execute the Rockbox bootloader */
+extern void spl_main(void);
+
+/* Note: This is based purely on disassembly of the SPL from the FiiO M3K.
+ * The code there is somewhat generic and corresponds roughly to Ingenic's
+ * U-Boot code, but isn't entirely the same.
+ *
+ * I converted all the runtime conditionals to compile-time ones in order to
+ * save code space, since they should be constant for any given target.
+ *
+ * I haven't bothered to decode all the register fields. Some of the values
+ * written are going to bits documented as "Reserved" by Ingenic, but their
+ * documentation doesn't seem completely reliable, so either these are bits
+ * which _do_ have a purpose, or they're only defined on other Ingenic CPUs.
+ *
+ * The DDR PHY registers appear to be from Synopsys "PHY Utility Block Lite".
+ * These aren't documented by Ingenic, but the addresses and names can be found
+ * in their U-Boot code.
+ */
+static void ddr_init(void)
+{
+ REG_CPM_DRCG = 0x73;
+ mdelay(3);
+ REG_CPM_DRCG = 0x71;
+ mdelay(3);
+ REG_DDRC_APB_PHYRST_CFG = 0x1a00001;
+ mdelay(3);
+ REG_DDRC_APB_PHYRST_CFG = 0;
+ mdelay(3);
+ REG_DDRC_CTRL = 0xf00000;
+ mdelay(3);
+ REG_DDRC_CTRL = 0;
+ mdelay(3);
+
+ REG_DDRC_CFG = 0xa468a6c;
+ REG_DDRC_CTRL = 2;
+ REG_DDRPHY_DTAR = 0x150000;
+ REG_DDRPHY_DCR = 0;
+ REG_DDRPHY_MR0 = 0x42;
+ REG_DDRPHY_MR2 = 0x98;
+ REG_DDRPHY_PTR0 = 0x21000a;
+ REG_DDRPHY_PTR1 = 0xa09c40;
+ REG_DDRPHY_PTR2 = 0x280014;
+ REG_DDRPHY_DTPR0 = 0x1a69444a;
+ REG_DDRPHY_DTPR1 = 0x180090;
+ REG_DDRPHY_DTPR2 = 0x1ff99428;
+ REG_DDRPHY_DXGCR(0) = 0x90881;
+ REG_DDRPHY_DXGCR(1) = 0x90881;
+ REG_DDRPHY_DXGCR(2) = 0x90e80;
+ REG_DDRPHY_DXGCR(3) = 0x90e80;
+ REG_DDRPHY_PGCR = 0x1042e03;
+ REG_DDRPHY_ACIOCR = 0x30c00813;
+ REG_DDRPHY_DXCCR = 0x4912;
+
+ int i = 10000;
+ while(i > 0 && REG_DDRPHY_PGSR != 7 && REG_DDRPHY_PGSR != 0x1f)
+ i -= 1;
+ if(i == 0)
+ hang();
+
+#if DDR_NEED_BYPASS
+ REG_DDRPHY_ACDLLCR = 0x80000000;
+ REG_DDRPHY_DSGCR &= ~0x10;
+ REG_DDRPHY_DLLGCR |= 0x800000;
+ REG_DDRPHY_PIR = 0x20020041;
+#else
+ REG_DDRPHY_PIR = 0x41;
+#endif
+
+ while(i > 0 && REG_DDRPHY_PGSR != 0xf && REG_DDRPHY_PGSR != 0x1f)
+ i -= 1;
+ if(i == 0)
+ hang();
+
+ REG_DDRC_APB_PHYRST_CFG = 0x400000;
+ mdelay(3);
+ REG_DDRC_APB_PHYRST_CFG = 0;
+ mdelay(3);
+
+ REG_DDRC_CFG = 0xa468aec;
+ REG_DDRC_CTRL = 2;
+#if DDR_NEED_BYPASS
+ REG_DDRPHY_PIR = 0x20020081;
+#else
+ REG_DDRPHY_PIR = 0x85;
+#endif
+
+ i = 500000;
+ while(REG_DDRPHY_PGSR != 0x1f) {
+ if(REG_DDRPHY_PGSR & 0x70)
+ break;
+ i -= 1;
+ }
+
+ if(i == 0)
+ hang();
+
+ if((REG_DDRPHY_PGSR & 0x60) != 0 && REG_DDRPHY_PGSR != 0)
+ hang();
+
+ REG_DDRC_CTRL = 0;
+ REG_DDRC_CTRL = 10;
+ REG_DDRC_CTRL = 0;
+ REG_DDRC_CFG = 0xa468a6c;
+ REG_DDRC_TIMING1 = 0x2050501;
+ REG_DDRC_TIMING2 = 0x4090404;
+ REG_DDRC_TIMING3 = 0x2704030d;
+ REG_DDRC_TIMING4 = 0xb7a0251;
+ REG_DDRC_TIMING5 = 0xff090200;
+ REG_DDRC_TIMING6 = 0xa0a0202;
+#if DDR_MEMORYSIZE == 64
+ REG_DDRC_MMAP0 = 0x20fc;
+ REG_DDRC_MMAP1 = 0x2400;
+#elif DDR_MEMORYSIZE == 32
+ REG_DDRC_MMAP0 = 0x20fe;
+ REG_DDRC_MMAP1 = 0x2200;
+#else
+# error "Unsupported DDR_MEMORYSIZE"
+#endif
+ REG_DDRC_CTRL = 10;
+ REG_DDRC_REFCNT = 0x2f0003;
+ REG_DDRC_CTRL = 0xc91e;
+
+#if DDR_MEMORYSIZE == 64
+ REG_DDRC_REMAP1 = 0x03020c0b;
+ REG_DDRC_REMAP2 = 0x07060504;
+ REG_DDRC_REMAP3 = 0x000a0908;
+ REG_DDRC_REMAP4 = 0x0f0e0d01;
+ REG_DDRC_REMAP5 = 0x13121110;
+#elif DDR_MEMORYSIZE == 32
+ REG_DDRC_REMAP1 = 0x03020b0a;
+ REG_DDRC_REMAP2 = 0x07060504;
+ REG_DDRC_REMAP3 = 0x01000908;
+ REG_DDRC_REMAP4 = 0x0f0e0d0c;
+ REG_DDRC_REMAP5 = 0x13121110;
+#else
+# error "Unsupported DDR_MEMORYSIZE"
+#endif
+
+ REG_DDRC_STATUS &= ~0x40;
+
+#if DDR_USE_AUTOSR
+#if DDR_NEED_BYPASS
+ jz_writef(CPM_DDRCDR, GATE_EN(1));
+ REG_DDRC_APB_CLKSTP_CFG = 0x9000000f;
+#else
+ REG_DDRC_DLP = 0;
+#endif
+#endif
+
+ REG_DDRC_AUTOSR_EN = DDR_USE_AUTOSR;
+}
+
+void main(void)
+{
+ /* from original firmware SPL */
+ REG_CPM_PSWC0ST = 0x00;
+ REG_CPM_PSWC1ST = 0x10;
+ REG_CPM_PSWC2ST = 0x18;
+ REG_CPM_PSWC3ST = 0x08;
+
+ /* enable MPLL */
+#if X1000_EXCLK_FREQ == 24000000
+ /* 24 * (24+1) = 600 MHz */
+ jz_writef(CPM_MPCR, ENABLE(1), BS(1), PLLN(0), PLLM(24), PLLOD(0));
+#elif X1000_EXCLK_FREQ == 26000000
+ /* 26 * (22+1) = 598 MHz */
+ jz_writef(CPM_MPCR, ENABLE(1), BS(1), PLLN(0), PLLM(22), PLLOD(0));
+#else
+# error "unknown EXCLK frequency"
+#endif
+ while(jz_readf(CPM_MPCR, ON) == 0);
+
+ /* set DDR clock to MPLL/3 = 200 MHz */
+ jz_writef(CPM_CLKGR, DDR(0));
+ clk_set_ddr(X1000_CLK_MPLL, 3);
+
+ /* start OST so we can use mdelay/udelay */
+ jz_writef(CPM_CLKGR, OST(0));
+ jz_writef(OST_CTRL, PRESCALE2_V(BY_4));
+ jz_writef(OST_CLEAR, OST2(1));
+ jz_write(OST_2CNTH, 0);
+ jz_write(OST_2CNTL, 0);
+ jz_setf(OST_ENABLE, OST2);
+
+ /* init DDR memory */
+ ddr_init();
+
+ /* jump to the target's main routine */
+ spl_main();
+}