FiiO M3K: Add dual boot support
Change-Id: Ic34d50855b317b5f4073b232dbf458edf82f55e1
This commit is contained in:
parent
89f4064743
commit
95408f2117
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue