summaryrefslogtreecommitdiff
path: root/firmware/target
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-06-13 17:39:29 +0100
committerAidan MacDonald <amachronic@protonmail.com>2021-06-14 20:45:14 +0100
commit95408f2117fdcac26662a2f020664d39c774e2c9 (patch)
tree69f3ab271a1bf10165fe8760a82ae595cda8d71a /firmware/target
parent89f40647431713b663e416329341a733c457df32 (diff)
FiiO M3K: Add dual boot support
Change-Id: Ic34d50855b317b5f4073b232dbf458edf82f55e1
Diffstat (limited to 'firmware/target')
-rw-r--r--firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c148
-rw-r--r--firmware/target/mips/ingenic_x1000/spl-x1000.c62
-rw-r--r--firmware/target/mips/ingenic_x1000/spl-x1000.h16
3 files changed, 148 insertions, 78 deletions
diff --git a/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c b/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c
index 85ce4da8f6..7c56e4ac19 100644
--- a/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c
+++ b/firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c
@@ -19,11 +19,15 @@
*
****************************************************************************/
+#include "system.h"
+#include "ucl_decompress.h"
#include "spl-x1000.h"
#include "gpio-x1000.h"
#include "clk-x1000.h"
-#include "system.h"
+#include "nand-x1000.h"
+#include "x1000/cpm.h"
#include <string.h>
+#include <stdint.h>
/* Available boot options */
#define BOOTOPTION_ROCKBOX 0
@@ -54,6 +58,17 @@ static const char recovery_cmdline[] = "mem=64M@0x0\
lpj=5009408\
ip=off";
+/* Entry point function type, defined to be Linux compatible. */
+typedef void(*entry_fn)(int, char**, int, int);
+
+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 */
+};
+
const struct spl_boot_option spl_boot_options[] = {
{
/* Rockbox: the first unused NAND page is 26 KiB in, and the
@@ -104,6 +119,75 @@ void spl_error(void)
}
}
+void spl_target_boot(void)
+{
+ int opt_index = spl_get_boot_option();
+ const struct spl_boot_option* opt = &spl_boot_options[opt_index];
+ uint8_t* load_addr = (uint8_t*)opt->load_addr;
+
+ /* Clock setup etc. */
+ spl_handle_pre_boot(opt_index);
+
+ /* Since the GPIO refactor, the SFC driver no longer assigns its own pins.
+ * We don't want to call gpio_init(), to keep code size down. Assign them
+ * here manually, we shouldn't need any other pins. */
+ gpioz_configure(GPIO_A, 0x3f << 26, GPIOF_DEVICE(1));
+
+ /* Open NAND chip */
+ int rc = nand_open();
+ if(rc)
+ spl_error();
+
+ int mf_id, dev_id;
+ rc = nand_identify(&mf_id, &dev_id);
+ if(rc)
+ goto nand_err;
+
+ /* For OF only: load DMA coprocessor's firmware from flash */
+ if(opt_index != BOOTOPTION_ROCKBOX) {
+ rc = nand_read(0x4000, 0x2000, (uint8_t*)0xb3422000);
+ if(rc)
+ goto nand_err;
+ }
+
+ /* Read the firmware */
+ rc = nand_read(opt->nand_addr, opt->nand_size, load_addr);
+ if(rc)
+ goto nand_err;
+
+ /* Rockbox doesn't need the NAND; for the OF, we should leave it open
+ * and also make sure to turn off the write protect bits. */
+ if(opt_index == BOOTOPTION_ROCKBOX)
+ nand_close();
+ else
+ nand_enable_writes(true);
+
+ /* Kernel arguments pointer, for Linux only */
+ char** kargv = (char**)0x80004000;
+
+ if(!opt->cmdline) {
+ /* Uncompress the rockbox bootloader */
+ uint32_t out_size = X1000_DRAM_END - opt->exec_addr;
+ int rc = ucl_unpack(load_addr, opt->nand_size,
+ (uint8_t*)opt->exec_addr, &out_size);
+ if(rc != UCL_E_OK)
+ spl_error();
+ } else {
+ /* Set kernel args */
+ kargv[0] = 0;
+ kargv[1] = (char*)opt->cmdline;
+ }
+
+ entry_fn entry = (entry_fn)opt->exec_addr;
+ commit_discard_idcache();
+ entry(2, kargv, 0, 0);
+ __builtin_unreachable();
+
+ nand_err:
+ nand_close();
+ spl_error();
+}
+
int spl_get_boot_option(void)
{
const uint32_t pinmask = (1 << 17) | (1 << 19);
@@ -157,17 +241,69 @@ void spl_handle_pre_boot(int bootopt)
* PCLK at 100 MHz
* DDR at 200 MHz
*/
- clk_set_ccr_div(1, 2, 5, 5, 10);
- clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) | CLKMUX_CPU(SCLK_A) |
- CLKMUX_AHB0(SCLK_A) | CLKMUX_AHB2(SCLK_A));
if(bootopt == BOOTOPTION_ROCKBOX) {
- /* We don't use MPLL in Rockbox, so switch DDR memory to APLL */
+ /* We don't use MPLL in Rockbox, so run everything from APLL. */
+ clk_set_ccr_div(1, 2, 5, 5, 10);
+ clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) | CLKMUX_CPU(SCLK_A) |
+ CLKMUX_AHB0(SCLK_A) | CLKMUX_AHB2(SCLK_A));
clk_set_ddr(X1000_CLK_SCLK_A, 5);
/* Turn off MPLL */
jz_writef(CPM_MPCR, ENABLE(0));
} else {
- /* TODO: Original firmware needs a lot of other clocks turned on */
+ /* Typical ingenic setup -- 1008 MHz APLL, 600 MHz MPLL
+ * with APLL driving the CPU/L2 and MPLL driving busses. */
+ clk_set_ccr_div(1, 2, 3, 3, 6);
+ clk_set_ccr_mux(CLKMUX_SCLK_A(APLL) | CLKMUX_CPU(SCLK_A) |
+ CLKMUX_AHB0(MPLL) | CLKMUX_AHB2(MPLL));
+
+ /* Mimic OF's clock gating setup */
+ jz_writef(CPM_CLKGR, PDMA(0), PCM(1), MAC(1), LCD(1),
+ MSC0(1), MSC1(1), OTG(1), CIM(1));
+
+ /* We now need to define the clock tree by fiddling with
+ * various CPM registers. Although the kernel has code to
+ * set up the clock tree itself, it isn't used and relies
+ * on the SPL for this. */
+ jz_writef(CPM_I2SCDR, CS_V(EXCLK));
+ jz_writef(CPM_PCMCDR, CS_V(EXCLK));
+
+ jz_writef(CPM_MACCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
+ while(jz_readf(CPM_MACCDR, BUSY));
+
+ jz_writef(CPM_LPCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
+ while(jz_readf(CPM_LPCDR, BUSY));
+
+ jz_writef(CPM_MSC0CDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
+ while(jz_readf(CPM_MSC0CDR, BUSY));
+
+ jz_writef(CPM_MSC1CDR, CE(1), STOP(1), CLKDIV(0xfe));
+ while(jz_readf(CPM_MSC1CDR, BUSY));
+
+ jz_writef(CPM_CIMCDR, CLKSRC_V(MPLL), CE(1), STOP(1), CLKDIV(0xfe));
+ while(jz_readf(CPM_CIMCDR, BUSY));
+
+ jz_writef(CPM_USBCDR, CLKSRC_V(EXCLK), CE(1), STOP(1));
+ while(jz_readf(CPM_USBCDR, BUSY));
+
+ /* Handle UART initialization */
+ gpioz_configure(GPIO_C, 3 << 30, GPIOF_DEVICE(1));
+ jz_writef(CPM_CLKGR, UART2(0));
+
+ /* TODO: Stop being lazy and make this human readable */
+ volatile uint8_t* ub = (volatile uint8_t*)0xb0032000;
+ ub[0x04] = 0;
+ ub[0x08] = 0xef;
+ ub[0x20] = 0xfc;
+ ub[0x0c] = 3;
+ uint8_t uv = ub[0x0c];
+ ub[0x0c] = uv | 0x80;
+ ub[0x04] = 0;
+ ub[0x00] = 0x0d;
+ ub[0x24] = 0x10;
+ ub[0x28] = 0;
+ ub[0x0c] = uv & 0x7f;
+ ub[0x08] = 0x17;
}
}
diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000.c b/firmware/target/mips/ingenic_x1000/spl-x1000.c
index 07453f6182..fd664e231d 100644
--- a/firmware/target/mips/ingenic_x1000/spl-x1000.c
+++ b/firmware/target/mips/ingenic_x1000/spl-x1000.c
@@ -21,14 +21,12 @@
#include "spl-x1000.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"
-#include "ucl_decompress.h"
#ifdef FIIO_M3K
# define SPL_DDR_MEMORYSIZE 64
@@ -224,29 +222,6 @@ static void init(void)
ddr_init();
}
-static int nandread(uint32_t addr, uint32_t size, void* buffer)
-{
- int rc;
- int mf_id, dev_id;
-
- if((rc = nand_open()))
- return rc;
- if((rc = nand_identify(&mf_id, &dev_id))) {
- nand_close();
- return rc;
- }
-
- rc = nand_read(addr, size, (uint8_t*)buffer);
- nand_close();
- return rc;
-}
-
-/* Entry point function type, defined to be Linux compatible. */
-typedef void(*entry_fn)(int, char**, int, int);
-
-/* Kernel command line arguments */
-static char* argv[2];
-
/* This variable is defined by the maskrom. It's simply the level of the
* boot_sel[2:0] pins (GPIOs B28-30) at boot time. Meaning of the bits:
*
@@ -264,10 +239,6 @@ extern const uint32_t boot_sel;
void spl_main(void)
{
- int opt_index;
- uint8_t* load_addr;
- const struct spl_boot_option* opt;
-
/* Basic hardware init */
init();
@@ -276,35 +247,6 @@ void spl_main(void)
if((boot_sel & 3) == 2)
return;
- /* Get the boot option */
- opt_index = spl_get_boot_option();
- opt = &spl_boot_options[opt_index];
- load_addr = (uint8_t*)opt->load_addr;
-
- /* Set up hardware, load stuff from flash */
- spl_handle_pre_boot(opt_index);
- if(nandread(opt->nand_addr, opt->nand_size, load_addr))
- spl_error();
-
- if(!opt->cmdline) {
- /* No command line => we are booting Rockbox, decompress bootloader.
- * In the case of Rockbox, load binary directly to exec address */
- uint32_t out_size = X1000_DRAM_END - opt->exec_addr;
- int rc = ucl_unpack(load_addr, opt->nand_size,
- (uint8_t*)opt->exec_addr, &out_size);
- if(rc != UCL_E_OK)
- spl_error();
- }
-
- /* Reading the Linux command line from the bootloader is handled by
- * arch/mips/xburst/core/prom.c -- see Ingenic kernel sources. It's
- * simply an (int argc, char* argv[]) thing.
- */
- entry_fn entry = (entry_fn)opt->exec_addr;
- argv[0] = 0;
- argv[1] = (char*)opt->cmdline;
-
- commit_discard_idcache();
- entry(2, argv, 0, 0);
- __builtin_unreachable();
+ /* Just pass control to the target... */
+ spl_target_boot();
}
diff --git a/firmware/target/mips/ingenic_x1000/spl-x1000.h b/firmware/target/mips/ingenic_x1000/spl-x1000.h
index 062cb40f88..81714a3ed2 100644
--- a/firmware/target/mips/ingenic_x1000/spl-x1000.h
+++ b/firmware/target/mips/ingenic_x1000/spl-x1000.h
@@ -22,22 +22,14 @@
#ifndef __SPL_X1000_H__
#define __SPL_X1000_H__
-#include <stdint.h>
-
-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, order is not important */
-extern const struct spl_boot_option spl_boot_options[];
+/* TODO: this needs some refactoring... */
/* Called on a fatal error */
extern void spl_error(void) __attribute__((noreturn));
+/* Called by SPL to handle a main boot */
+extern void spl_target_boot(void);
+
/* Invoked by SPL main routine to determine the boot option */
extern int spl_get_boot_option(void);