diff options
-rw-r--r-- | firmware/target/mips/ingenic_x1000/fiiom3k/spl-fiiom3k.c | 148 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/spl-x1000.c | 62 | ||||
-rw-r--r-- | firmware/target/mips/ingenic_x1000/spl-x1000.h | 16 |
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); |