2021-02-27 22:08:58 +00:00
|
|
|
/***************************************************************************
|
|
|
|
* __________ __ ___.
|
|
|
|
* 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.
|
|
|
|
*
|
|
|
|
****************************************************************************/
|
|
|
|
|
2021-04-07 21:11:01 +00:00
|
|
|
#include "spl-x1000.h"
|
|
|
|
#include "spl-target.h"
|
2021-02-27 22:08:58 +00:00
|
|
|
#include "clk-x1000.h"
|
2021-04-07 21:11:01 +00:00
|
|
|
#include "nand-x1000.h"
|
|
|
|
#include "system.h"
|
2021-02-27 22:08:58 +00:00
|
|
|
#include "x1000/cpm.h"
|
|
|
|
#include "x1000/ost.h"
|
|
|
|
#include "x1000/ddrc.h"
|
|
|
|
#include "x1000/ddrc_apb.h"
|
|
|
|
#include "x1000/ddrphy.h"
|
|
|
|
|
2021-04-07 21:11:01 +00:00
|
|
|
struct x1000_spl_arguments* const spl_arguments =
|
|
|
|
(struct x1000_spl_arguments*)SPL_ARGUMENTS_ADDRESS;
|
2021-02-27 22:08:58 +00:00
|
|
|
|
2021-04-07 21:11:01 +00:00
|
|
|
struct x1000_spl_status* const spl_status =
|
|
|
|
(struct x1000_spl_status*)SPL_STATUS_ADDRESS;
|
2021-02-27 22:08:58 +00:00
|
|
|
|
2021-04-07 21:11:01 +00:00
|
|
|
/* 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);
|
2021-02-27 22:08:58 +00:00
|
|
|
|
|
|
|
/* 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
|
|
|
|
* U-Boot code, but isn't entirely the same.
|
|
|
|
*
|
|
|
|
* I converted all the runtime conditionals to compile-time ones in order to
|
|
|
|
* save code space, since they should be constant for any given target.
|
|
|
|
*
|
|
|
|
* I haven't bothered to decode all the register fields. Some of the values
|
|
|
|
* written are going to bits documented as "Reserved" by Ingenic, but their
|
|
|
|
* documentation doesn't seem completely reliable, so either these are bits
|
|
|
|
* which _do_ have a purpose, or they're only defined on other Ingenic CPUs.
|
|
|
|
*
|
|
|
|
* The DDR PHY registers appear to be from Synopsys "PHY Utility Block Lite".
|
|
|
|
* These aren't documented by Ingenic, but the addresses and names can be found
|
|
|
|
* in their U-Boot code.
|
|
|
|
*/
|
|
|
|
static void ddr_init(void)
|
|
|
|
{
|
|
|
|
REG_CPM_DRCG = 0x73;
|
|
|
|
mdelay(3);
|
|
|
|
REG_CPM_DRCG = 0x71;
|
|
|
|
mdelay(3);
|
|
|
|
REG_DDRC_APB_PHYRST_CFG = 0x1a00001;
|
|
|
|
mdelay(3);
|
|
|
|
REG_DDRC_APB_PHYRST_CFG = 0;
|
|
|
|
mdelay(3);
|
|
|
|
REG_DDRC_CTRL = 0xf00000;
|
|
|
|
mdelay(3);
|
|
|
|
REG_DDRC_CTRL = 0;
|
|
|
|
mdelay(3);
|
|
|
|
|
|
|
|
REG_DDRC_CFG = 0xa468a6c;
|
|
|
|
REG_DDRC_CTRL = 2;
|
|
|
|
REG_DDRPHY_DTAR = 0x150000;
|
|
|
|
REG_DDRPHY_DCR = 0;
|
|
|
|
REG_DDRPHY_MR0 = 0x42;
|
|
|
|
REG_DDRPHY_MR2 = 0x98;
|
|
|
|
REG_DDRPHY_PTR0 = 0x21000a;
|
|
|
|
REG_DDRPHY_PTR1 = 0xa09c40;
|
|
|
|
REG_DDRPHY_PTR2 = 0x280014;
|
|
|
|
REG_DDRPHY_DTPR0 = 0x1a69444a;
|
|
|
|
REG_DDRPHY_DTPR1 = 0x180090;
|
|
|
|
REG_DDRPHY_DTPR2 = 0x1ff99428;
|
|
|
|
REG_DDRPHY_DXGCR(0) = 0x90881;
|
|
|
|
REG_DDRPHY_DXGCR(1) = 0x90881;
|
|
|
|
REG_DDRPHY_DXGCR(2) = 0x90e80;
|
|
|
|
REG_DDRPHY_DXGCR(3) = 0x90e80;
|
|
|
|
REG_DDRPHY_PGCR = 0x1042e03;
|
|
|
|
REG_DDRPHY_ACIOCR = 0x30c00813;
|
|
|
|
REG_DDRPHY_DXCCR = 0x4912;
|
|
|
|
|
|
|
|
int i = 10000;
|
|
|
|
while(i > 0 && REG_DDRPHY_PGSR != 7 && REG_DDRPHY_PGSR != 0x1f)
|
|
|
|
i -= 1;
|
|
|
|
if(i == 0)
|
2021-04-07 21:11:01 +00:00
|
|
|
spl_error();
|
2021-02-27 22:08:58 +00:00
|
|
|
|
2021-04-07 21:11:01 +00:00
|
|
|
#if SPL_DDR_NEED_BYPASS
|
2021-02-27 22:08:58 +00:00
|
|
|
REG_DDRPHY_ACDLLCR = 0x80000000;
|
|
|
|
REG_DDRPHY_DSGCR &= ~0x10;
|
|
|
|
REG_DDRPHY_DLLGCR |= 0x800000;
|
|
|
|
REG_DDRPHY_PIR = 0x20020041;
|
|
|
|
#else
|
|
|
|
REG_DDRPHY_PIR = 0x41;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
while(i > 0 && REG_DDRPHY_PGSR != 0xf && REG_DDRPHY_PGSR != 0x1f)
|
|
|
|
i -= 1;
|
|
|
|
if(i == 0)
|
2021-04-07 21:11:01 +00:00
|
|
|
spl_error();
|
2021-02-27 22:08:58 +00:00
|
|
|
|
|
|
|
REG_DDRC_APB_PHYRST_CFG = 0x400000;
|
|
|
|
mdelay(3);
|
|
|
|
REG_DDRC_APB_PHYRST_CFG = 0;
|
|
|
|
mdelay(3);
|
|
|
|
|
|
|
|
REG_DDRC_CFG = 0xa468aec;
|
|
|
|
REG_DDRC_CTRL = 2;
|
2021-04-07 21:11:01 +00:00
|
|
|
#if SPL_DDR_NEED_BYPASS
|
2021-02-27 22:08:58 +00:00
|
|
|
REG_DDRPHY_PIR = 0x20020081;
|
|
|
|
#else
|
|
|
|
REG_DDRPHY_PIR = 0x85;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
i = 500000;
|
|
|
|
while(REG_DDRPHY_PGSR != 0x1f) {
|
|
|
|
if(REG_DDRPHY_PGSR & 0x70)
|
|
|
|
break;
|
|
|
|
i -= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(i == 0)
|
2021-04-07 21:11:01 +00:00
|
|
|
spl_error();
|
2021-02-27 22:08:58 +00:00
|
|
|
|
|
|
|
if((REG_DDRPHY_PGSR & 0x60) != 0 && REG_DDRPHY_PGSR != 0)
|
2021-04-07 21:11:01 +00:00
|
|
|
spl_error();
|
2021-02-27 22:08:58 +00:00
|
|
|
|
|
|
|
REG_DDRC_CTRL = 0;
|
|
|
|
REG_DDRC_CTRL = 10;
|
|
|
|
REG_DDRC_CTRL = 0;
|
|
|
|
REG_DDRC_CFG = 0xa468a6c;
|
|
|
|
REG_DDRC_TIMING1 = 0x2050501;
|
|
|
|
REG_DDRC_TIMING2 = 0x4090404;
|
|
|
|
REG_DDRC_TIMING3 = 0x2704030d;
|
|
|
|
REG_DDRC_TIMING4 = 0xb7a0251;
|
|
|
|
REG_DDRC_TIMING5 = 0xff090200;
|
|
|
|
REG_DDRC_TIMING6 = 0xa0a0202;
|
2021-04-07 21:11:01 +00:00
|
|
|
#if SPL_DDR_MEMORYSIZE == 64
|
2021-02-27 22:08:58 +00:00
|
|
|
REG_DDRC_MMAP0 = 0x20fc;
|
|
|
|
REG_DDRC_MMAP1 = 0x2400;
|
2021-04-07 21:11:01 +00:00
|
|
|
#elif SPL_DDR_MEMORYSIZE == 32
|
2021-02-27 22:08:58 +00:00
|
|
|
REG_DDRC_MMAP0 = 0x20fe;
|
|
|
|
REG_DDRC_MMAP1 = 0x2200;
|
|
|
|
#else
|
|
|
|
# error "Unsupported DDR_MEMORYSIZE"
|
|
|
|
#endif
|
|
|
|
REG_DDRC_CTRL = 10;
|
|
|
|
REG_DDRC_REFCNT = 0x2f0003;
|
|
|
|
REG_DDRC_CTRL = 0xc91e;
|
|
|
|
|
2021-04-07 21:11:01 +00:00
|
|
|
#if SPL_DDR_MEMORYSIZE == 64
|
2021-02-27 22:08:58 +00:00
|
|
|
REG_DDRC_REMAP1 = 0x03020c0b;
|
|
|
|
REG_DDRC_REMAP2 = 0x07060504;
|
|
|
|
REG_DDRC_REMAP3 = 0x000a0908;
|
|
|
|
REG_DDRC_REMAP4 = 0x0f0e0d01;
|
|
|
|
REG_DDRC_REMAP5 = 0x13121110;
|
2021-04-07 21:11:01 +00:00
|
|
|
#elif SPL_DDR_MEMORYSIZE == 32
|
2021-02-27 22:08:58 +00:00
|
|
|
REG_DDRC_REMAP1 = 0x03020b0a;
|
|
|
|
REG_DDRC_REMAP2 = 0x07060504;
|
|
|
|
REG_DDRC_REMAP3 = 0x01000908;
|
|
|
|
REG_DDRC_REMAP4 = 0x0f0e0d0c;
|
|
|
|
REG_DDRC_REMAP5 = 0x13121110;
|
|
|
|
#else
|
|
|
|
# error "Unsupported DDR_MEMORYSIZE"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
REG_DDRC_STATUS &= ~0x40;
|
|
|
|
|
2021-04-07 21:11:01 +00:00
|
|
|
#if SPL_DDR_AUTOSR_EN
|
|
|
|
#if SPL_DDR_NEED_BYPASS
|
2021-02-27 22:08:58 +00:00
|
|
|
jz_writef(CPM_DDRCDR, GATE_EN(1));
|
|
|
|
REG_DDRC_APB_CLKSTP_CFG = 0x9000000f;
|
|
|
|
#else
|
|
|
|
REG_DDRC_DLP = 0;
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
2021-04-07 21:11:01 +00:00
|
|
|
REG_DDRC_AUTOSR_EN = SPL_DDR_AUTOSR_EN;
|
2021-02-27 22:08:58 +00:00
|
|
|
}
|
|
|
|
|
2021-04-07 21:11:01 +00:00
|
|
|
static void init(void)
|
2021-02-27 22:08:58 +00:00
|
|
|
{
|
|
|
|
/* from original firmware SPL */
|
|
|
|
REG_CPM_PSWC0ST = 0x00;
|
|
|
|
REG_CPM_PSWC1ST = 0x10;
|
|
|
|
REG_CPM_PSWC2ST = 0x18;
|
|
|
|
REG_CPM_PSWC3ST = 0x08;
|
|
|
|
|
|
|
|
/* enable MPLL */
|
|
|
|
#if X1000_EXCLK_FREQ == 24000000
|
|
|
|
/* 24 * (24+1) = 600 MHz */
|
|
|
|
jz_writef(CPM_MPCR, ENABLE(1), BS(1), PLLN(0), PLLM(24), PLLOD(0));
|
|
|
|
#elif X1000_EXCLK_FREQ == 26000000
|
|
|
|
/* 26 * (22+1) = 598 MHz */
|
|
|
|
jz_writef(CPM_MPCR, ENABLE(1), BS(1), PLLN(0), PLLM(22), PLLOD(0));
|
|
|
|
#else
|
|
|
|
# error "unknown EXCLK frequency"
|
|
|
|
#endif
|
|
|
|
while(jz_readf(CPM_MPCR, ON) == 0);
|
|
|
|
|
|
|
|
/* set DDR clock to MPLL/3 = 200 MHz */
|
|
|
|
jz_writef(CPM_CLKGR, DDR(0));
|
|
|
|
clk_set_ddr(X1000_CLK_MPLL, 3);
|
|
|
|
|
|
|
|
/* start OST so we can use mdelay/udelay */
|
|
|
|
jz_writef(CPM_CLKGR, OST(0));
|
|
|
|
jz_writef(OST_CTRL, PRESCALE2_V(BY_4));
|
|
|
|
jz_writef(OST_CLEAR, OST2(1));
|
|
|
|
jz_write(OST_2CNTH, 0);
|
|
|
|
jz_write(OST_2CNTL, 0);
|
|
|
|
jz_setf(OST_ENABLE, OST2);
|
|
|
|
|
|
|
|
/* init DDR memory */
|
|
|
|
ddr_init();
|
2021-04-07 21:11:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2021-02-27 22:08:58 +00:00
|
|
|
|
2021-04-07 21:11:01 +00:00
|
|
|
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;
|
|
|
|
}
|
2021-02-27 22:08:58 +00:00
|
|
|
}
|