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 #elif defined(SANSA_CONNECT) sansaconnect.c show_logo.c -#elif defined(FIIO_M3K) -#ifdef BOOTLOADER_SPL -x1000-spl.c -fiiom3k-spl.c -#else +#elif defined(FIIO_M3K) && !defined(BOOTLOADER_SPL) fiiom3k.c #endif -#endif diff --git a/firmware/SOURCES b/firmware/SOURCES index 3a42381003..ce3d8d52e2 100644 --- a/firmware/SOURCES +++ b/firmware/SOURCES @@ -1747,6 +1747,9 @@ target/mips/ingenic_x1000/msc-x1000.c #if (CONFIG_STORAGE & STORAGE_SD) target/mips/ingenic_x1000/sd-x1000.c #endif +#ifdef BOOTLOADER_SPL +target/mips/ingenic_x1000/spl-x1000.c +#endif #endif /* CONFIG_CPU == X1000 */ #if defined(ONDA_VX747) || defined(ONDA_VX747P) || defined(ONDA_VX777) @@ -1780,6 +1783,9 @@ target/mips/ingenic_x1000/fiiom3k/installer-fiiom3k.c target/mips/ingenic_x1000/fiiom3k/lcd-fiiom3k.c target/mips/ingenic_x1000/fiiom3k/nand-fiiom3k.c target/mips/ingenic_x1000/fiiom3k/power-fiiom3k.c +#ifdef BOOTLOADER_SPL +target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c +#endif #endif /* FIIO_M3K */ #if defined(LYRE_PROTO1) diff --git a/bootloader/fiiom3k-spl.c b/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c similarity index 57% rename from bootloader/fiiom3k-spl.c rename to firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c index 67b4b0a59c..0ebe11e24d 100644 --- a/bootloader/fiiom3k-spl.c +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c @@ -19,28 +19,16 @@ * ****************************************************************************/ -#include "config.h" -#include "nand-x1000.h" +#include "spl-x1000.h" #include "gpio-x1000.h" -#include "mmu-mips.h" +#include "nand-x1000.h" +#include "system.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"; +/* 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)) static const char normal_cmdline[] = "mem=64M@0x0\ no_console_suspend\ @@ -55,18 +43,13 @@ static const char normal_cmdline[] = "mem=64M@0x0\ rw\ loglevel=8"; -#define BOOTOPTION_ROCKBOX 0 -#define BOOTOPTION_FIIOLINUX 1 -#define BOOTOPTION_RECOVERY 2 -#define NUM_BOOTOPTIONS 3 +static const char recovery_cmdline[] = "mem=64M@0x0\ + no_console_suspend\ + console=ttyS2,115200n8\ + lpj=5009408\ + ip=off"; -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] = { +const struct spl_boot_option spl_boot_options[] = { { /* Rockbox: the first unused NAND page is 26 KiB in, and the * remainder of the block is unused, giving us 102 KiB to use. @@ -95,10 +78,7 @@ static const struct bootoption { }, }; -/* Simple diagnostic if something goes wrong -- a little nicer than wondering - * what's going on when the machine hangs - */ -void die(void) +void spl_error(void) { const int pin = (1 << 24); @@ -119,12 +99,7 @@ void die(void) } } -/* 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) +int spl_get_boot_option(void) { const uint32_t pinmask = (1 << 17) | (1 << 19); @@ -146,61 +121,12 @@ int get_boot_option(void) /* Play button boots original firmware */ if(pin == (1 << 17)) - return BOOTOPTION_FIIOLINUX; + return SPL_BOOTOPT_ORIG_FW; /* Volume up boots recovery */ if(pin == (1 << 19)) - return BOOTOPTION_RECOVERY; + return SPL_BOOTOPT_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\n" - "nop\n" - :: "r"(execaddr)); - __builtin_unreachable(); + return SPL_BOOTOPT_ROCKBOX; } diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/spl-target.h b/firmware/target/mips/ingenic_x1000/fiiom3k/spl-target.h new file mode 100644 index 0000000000..ac90508f44 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/fiiom3k/spl-target.h @@ -0,0 +1,29 @@ +/*************************************************************************** + * __________ __ ___. + * 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. + * + ****************************************************************************/ + +#ifndef __SPL_TARGET_H__ +#define __SPL_TARGET_H__ + +#define SPL_DDR_MEMORYSIZE 64 +#define SPL_DDR_AUTOSR_EN 1 +#define SPL_DDR_NEED_BYPASS 1 + +#endif /* __SPL_TARGET_H__ */ diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000-defs.h b/firmware/target/mips/ingenic_x1000/spl-x1000-defs.h new file mode 100644 index 0000000000..1d9f120ee2 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/spl-x1000-defs.h @@ -0,0 +1,66 @@ +/*************************************************************************** + * __________ __ ___. + * 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. + * + ****************************************************************************/ + +#ifndef __SPL_X1000_DEFS_H__ +#define __SPL_X1000_DEFS_H__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define SPL_CMD_BOOT 0 +#define SPL_CMD_FLASH_READ 1 +#define SPL_CMD_FLASH_WRITE 2 + +#define SPL_BOOTOPT_CHOOSE 0 +#define SPL_BOOTOPT_ROCKBOX 1 +#define SPL_BOOTOPT_ORIG_FW 2 +#define SPL_BOOTOPT_RECOVERY 3 +#define SPL_BOOTOPT_NONE 4 + +#define SPL_FLAG_SKIP_INIT (1 << 0) + +#define SPL_MAX_SIZE (12 * 1024) +#define SPL_LOAD_ADDRESS 0xf4001000 +#define SPL_EXEC_ADDRESS 0xf4001800 +#define SPL_ARGUMENTS_ADDRESS 0xf40011f0 +#define SPL_STATUS_ADDRESS 0xf40011e0 +#define SPL_BUFFER_ADDRESS 0xa0004000 + +struct x1000_spl_arguments { + uint32_t command; + uint32_t param1; + uint32_t param2; + uint32_t flags; +}; + +struct x1000_spl_status { + int err_code; + int reserved; +}; + +#ifdef __cplusplus +} +#endif + +#endif /* __SPL_X1000_DEFS_H__ */ diff --git a/bootloader/x1000-spl.c b/firmware/target/mips/ingenic_x1000/spl-x1000.c similarity index 65% rename from bootloader/x1000-spl.c rename to firmware/target/mips/ingenic_x1000/spl-x1000.c index 1c780a9843..59e0fb687d 100644 --- a/bootloader/x1000-spl.c +++ b/firmware/target/mips/ingenic_x1000/spl-x1000.c @@ -19,26 +19,26 @@ * ****************************************************************************/ -#include "system.h" +#include "spl-x1000.h" +#include "spl-target.h" #include "clk-x1000.h" +#include "nand-x1000.h" +#include "system.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 +struct x1000_spl_arguments* const spl_arguments = + (struct x1000_spl_arguments*)SPL_ARGUMENTS_ADDRESS; -#define hang() do { } while(1) +struct x1000_spl_status* const spl_status = + (struct x1000_spl_status*)SPL_STATUS_ADDRESS; -/* Target-specific routine to load & execute the Rockbox bootloader */ -extern void spl_main(void); +/* defined to be Linux compatible; Rockbox needs no arguments so there + * is no harm in passing them and we save a little code size */ +typedef void(*entry_fn)(int, char**, int, int); /* 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 @@ -95,9 +95,9 @@ static void ddr_init(void) while(i > 0 && REG_DDRPHY_PGSR != 7 && REG_DDRPHY_PGSR != 0x1f) i -= 1; if(i == 0) - hang(); + spl_error(); -#if DDR_NEED_BYPASS +#if SPL_DDR_NEED_BYPASS REG_DDRPHY_ACDLLCR = 0x80000000; REG_DDRPHY_DSGCR &= ~0x10; REG_DDRPHY_DLLGCR |= 0x800000; @@ -109,7 +109,7 @@ static void ddr_init(void) while(i > 0 && REG_DDRPHY_PGSR != 0xf && REG_DDRPHY_PGSR != 0x1f) i -= 1; if(i == 0) - hang(); + spl_error(); REG_DDRC_APB_PHYRST_CFG = 0x400000; mdelay(3); @@ -118,7 +118,7 @@ static void ddr_init(void) REG_DDRC_CFG = 0xa468aec; REG_DDRC_CTRL = 2; -#if DDR_NEED_BYPASS +#if SPL_DDR_NEED_BYPASS REG_DDRPHY_PIR = 0x20020081; #else REG_DDRPHY_PIR = 0x85; @@ -132,10 +132,10 @@ static void ddr_init(void) } if(i == 0) - hang(); + spl_error(); if((REG_DDRPHY_PGSR & 0x60) != 0 && REG_DDRPHY_PGSR != 0) - hang(); + spl_error(); REG_DDRC_CTRL = 0; REG_DDRC_CTRL = 10; @@ -147,10 +147,10 @@ static void ddr_init(void) REG_DDRC_TIMING4 = 0xb7a0251; REG_DDRC_TIMING5 = 0xff090200; REG_DDRC_TIMING6 = 0xa0a0202; -#if DDR_MEMORYSIZE == 64 +#if SPL_DDR_MEMORYSIZE == 64 REG_DDRC_MMAP0 = 0x20fc; REG_DDRC_MMAP1 = 0x2400; -#elif DDR_MEMORYSIZE == 32 +#elif SPL_DDR_MEMORYSIZE == 32 REG_DDRC_MMAP0 = 0x20fe; REG_DDRC_MMAP1 = 0x2200; #else @@ -160,13 +160,13 @@ static void ddr_init(void) REG_DDRC_REFCNT = 0x2f0003; REG_DDRC_CTRL = 0xc91e; -#if DDR_MEMORYSIZE == 64 +#if SPL_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 +#elif SPL_DDR_MEMORYSIZE == 32 REG_DDRC_REMAP1 = 0x03020b0a; REG_DDRC_REMAP2 = 0x07060504; REG_DDRC_REMAP3 = 0x01000908; @@ -178,8 +178,8 @@ static void ddr_init(void) REG_DDRC_STATUS &= ~0x40; -#if DDR_USE_AUTOSR -#if DDR_NEED_BYPASS +#if SPL_DDR_AUTOSR_EN +#if SPL_DDR_NEED_BYPASS jz_writef(CPM_DDRCDR, GATE_EN(1)); REG_DDRC_APB_CLKSTP_CFG = 0x9000000f; #else @@ -187,10 +187,10 @@ static void ddr_init(void) #endif #endif - REG_DDRC_AUTOSR_EN = DDR_USE_AUTOSR; + REG_DDRC_AUTOSR_EN = SPL_DDR_AUTOSR_EN; } -void main(void) +static void init(void) { /* from original firmware SPL */ REG_CPM_PSWC0ST = 0x00; @@ -224,7 +224,89 @@ void main(void) /* init DDR memory */ ddr_init(); - - /* jump to the target's main routine */ - spl_main(); +} + +static int nandread(uint32_t addr, uint32_t size, void* buffer) +{ + int rc; + + if((rc = nand_open())) + return rc; + + rc = nand_read_bytes(addr, size, buffer); + nand_close(); + return rc; +} + +static int nandwrite(uint32_t addr, uint32_t size, void* buffer) +{ + int rc; + + if((rc = nand_open())) + return rc; + + if((rc = nand_enable_writes(true))) + goto _end; + + if((rc = nand_erase_bytes(addr, size))) + goto _end1; + + rc = nand_write_bytes(addr, size, buffer); + + _end1: + /* an error here is very unlikely, so ignore it */ + nand_enable_writes(false); + + _end: + nand_close(); + return rc; +} + +void main(void) +{ + if(!(SPL_ARGUMENTS->flags & SPL_FLAG_SKIP_INIT)) + init(); + + switch(SPL_ARGUMENTS->command) { + case SPL_CMD_BOOT: { + int option = SPL_ARGUMENTS->param1; + if(option == SPL_BOOTOPT_CHOOSE) + option = spl_get_boot_option(); + if(option == SPL_BOOTOPT_NONE) + return; + + const struct spl_boot_option* opt = &spl_boot_options[option-1]; + if(nandread(opt->nand_addr, opt->nand_size, (void*)opt->load_addr)) + spl_error(); + + /* TODO: implement dual boot */ + + /* Reading the Linux command line from the bootloader is handled by + * arch/mips/xburst/core/prom.c -- see Ingenic kernel sources. + * + * Rockbox doesn't use arguments, but passing them does not hurt and it + * saves an unnecessary branch. + */ + entry_fn entry = (entry_fn)opt->exec_addr; + char** argv = (char**)0x80004000; + argv[0] = 0; + argv[1] = (char*)opt->cmdline; + + commit_discard_idcache(); + entry(2, argv, 0, 0); + __builtin_unreachable(); + } + + case SPL_CMD_FLASH_READ: + SPL_STATUS->err_code = nandread(SPL_ARGUMENTS->param1, + SPL_ARGUMENTS->param2, + (void*)SPL_BUFFER_ADDRESS); + return; + + case SPL_CMD_FLASH_WRITE: + SPL_STATUS->err_code = nandwrite(SPL_ARGUMENTS->param1, + SPL_ARGUMENTS->param2, + (void*)SPL_BUFFER_ADDRESS); + return; + } } diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000.h b/firmware/target/mips/ingenic_x1000/spl-x1000.h new file mode 100644 index 0000000000..44601438f3 --- /dev/null +++ b/firmware/target/mips/ingenic_x1000/spl-x1000.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * __________ __ ___. + * 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. + * + ****************************************************************************/ + +#ifndef __SPL_X1000_H__ +#define __SPL_X1000_H__ + +#include "spl-x1000-defs.h" + +#define SPL_ARGUMENTS ((struct x1000_spl_arguments*)SPL_ARGUMENTS_ADDRESS) +#define SPL_STATUS ((struct x1000_spl_status*)SPL_STATUS_ADDRESS) + +struct spl_boot_option { + uint32_t nand_addr; + uint32_t nand_size; + uint32_t load_addr; + uint32_t exec_addr; + const char* cmdline; /* for Linux */ +}; + +/* Defined by target, indices are 0 = ROCKBOX, 1 = ORIG_FW, etc... */ +extern const struct spl_boot_option spl_boot_options[]; + +/* Called on a fatal error */ +void spl_error(void) __attribute__((noreturn)); + +/* When SPL boots with SPL_BOOTOPTION_CHOOSE, this function is invoked + * to let the target figure out the boot option based on buttons the + * user is pressing */ +extern int spl_get_boot_option(void); + +#endif /* __SPL_X1000_H__ */