From 3ec66893e377b088c1284d2d23adb2aeea6d7965 Mon Sep 17 00:00:00 2001 From: Aidan MacDonald Date: Sat, 27 Feb 2021 22:08:58 +0000 Subject: New port: FiiO M3K on bare metal Change-Id: I7517e7d5459e129dcfc9465c6fbd708619888fbe --- bootloader/fiiom3k-spl.c | 204 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 204 insertions(+) create mode 100644 bootloader/fiiom3k-spl.c (limited to 'bootloader/fiiom3k-spl.c') 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 + +/* "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(); +} -- cgit v1.2.3