rockbox/firmware/target/mips/ingenic_x1000/spl-x1000.c
Aidan MacDonald 01d1eb4258 FiiO M3K/X1000: Do system clock initialization in the SPL
Initializing the clocks in the SPL brings Rockbox in line with
how the FiiO M3K's original SPL works. It's likely other X1000
devices do this too.

There was a logic error in the previous setup: the code falsely
assumed that DDR memory would always be running from MPLL, but
it would be switched to APLL by the bootloader. Rockbox would
then try to re-init APLL, albeit with the same parameters. Maybe
this was the cause of the boot hang on some units.

Change-Id: I64064585e491bbdf1e95fe9428c91a9314f2a917
2021-04-17 20:24:07 +00:00

313 lines
8.7 KiB
C

/***************************************************************************
* __________ __ ___.
* 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.
*
****************************************************************************/
#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"
struct x1000_spl_arguments* const spl_arguments =
(struct x1000_spl_arguments*)SPL_ARGUMENTS_ADDRESS;
struct x1000_spl_status* const spl_status =
(struct x1000_spl_status*)SPL_STATUS_ADDRESS;
/* 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
* 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)
spl_error();
#if SPL_DDR_NEED_BYPASS
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)
spl_error();
REG_DDRC_APB_PHYRST_CFG = 0x400000;
mdelay(3);
REG_DDRC_APB_PHYRST_CFG = 0;
mdelay(3);
REG_DDRC_CFG = 0xa468aec;
REG_DDRC_CTRL = 2;
#if SPL_DDR_NEED_BYPASS
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)
spl_error();
if((REG_DDRPHY_PGSR & 0x60) != 0 && REG_DDRPHY_PGSR != 0)
spl_error();
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;
#if SPL_DDR_MEMORYSIZE == 64
REG_DDRC_MMAP0 = 0x20fc;
REG_DDRC_MMAP1 = 0x2400;
#elif SPL_DDR_MEMORYSIZE == 32
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;
#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 SPL_DDR_MEMORYSIZE == 32
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;
#if SPL_DDR_AUTOSR_EN
#if SPL_DDR_NEED_BYPASS
jz_writef(CPM_DDRCDR, GATE_EN(1));
REG_DDRC_APB_CLKSTP_CFG = 0x9000000f;
#else
REG_DDRC_DLP = 0;
#endif
#endif
REG_DDRC_AUTOSR_EN = SPL_DDR_AUTOSR_EN;
}
static void init(void)
{
/* 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();
}
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();
/* Let target handle necessary pre-boot setup */
spl_handle_pre_boot(option);
/* 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;
}
}