Blackfin: put memory into self-refresh before/after programming clocks

When initializing the core clocks, stick external memory into self-refresh.
This gains us a few cool things:
 - support suspend-to-RAM with Linux
 - reprogram clocks automatically when doing "go" on u-boot.bin in RAM
 - make sure settings are stable before flashing new version
 - finally fully unify initialize startup code path between LDR/non-LDR

Signed-off-by: Mike Frysinger <vapier@gentoo.org>
This commit is contained in:
Mike Frysinger 2008-10-11 21:58:33 -04:00
parent d347d572ab
commit 74398b23f9
2 changed files with 197 additions and 89 deletions

View file

@ -12,6 +12,7 @@
#include <config.h>
#include <asm/blackfin.h>
#include <asm/mach-common/bits/bootrom.h>
#include <asm/mach-common/bits/core.h>
#include <asm/mach-common/bits/ebiu.h>
#include <asm/mach-common/bits/pll.h>
#include <asm/mach-common/bits/uart.h>
@ -257,6 +258,8 @@ void initcode(ADI_BOOT_DATA *bootstruct)
divB = serial_early_get_div();
}
serial_putc('A');
#ifdef CONFIG_HW_WATCHDOG
# ifndef CONFIG_HW_WATCHDOG_TIMEOUT_INITCODE
# define CONFIG_HW_WATCHDOG_TIMEOUT_INITCODE 20000
@ -273,7 +276,23 @@ void initcode(ADI_BOOT_DATA *bootstruct)
}
#endif
serial_putc('S');
serial_putc('B');
/* If external memory is enabled, put it into self refresh first. */
bool put_into_srfs = false;
#ifdef EBIU_RSTCTL
if (bfin_read_EBIU_RSTCTL() & DDR_SRESET) {
bfin_write_EBIU_RSTCTL(bfin_read_EBIU_RSTCTL() | SRREQ);
put_into_srfs = true;
}
#else
if (bfin_read_EBIU_SDBCTL() & EBE) {
bfin_write_EBIU_SDGCTL(bfin_read_EBIU_SDGCTL() | SRFS);
put_into_srfs = true;
}
#endif
serial_putc('C');
/* Blackfin bootroms use the SPI slow read opcode instead of the SPI
* fast read, so we need to slow down the SPI clock a lot more during
@ -286,29 +305,26 @@ void initcode(ADI_BOOT_DATA *bootstruct)
bfin_write_SPI_BAUD(CONFIG_SPI_BAUD_INITBLOCK);
}
serial_putc('B');
serial_putc('D');
/* Disable all peripheral wakeups except for the PLL event. */
#ifdef SIC_IWR0
bfin_write_SIC_IWR0(1);
bfin_write_SIC_IWR1(0);
# ifdef SIC_IWR2
bfin_write_SIC_IWR2(0);
# endif
#elif defined(SICA_IWR0)
bfin_write_SICA_IWR0(1);
bfin_write_SICA_IWR1(0);
/* If we're entering self refresh, make sure it has happened. */
if (put_into_srfs)
#ifdef EBIU_RSTCTL
while (!(bfin_read_EBIU_RSTCTL() & SRACK))
#else
bfin_write_SIC_IWR(1);
while (!(bfin_read_EBIU_SDSTAT() & SDSRA))
#endif
continue;
serial_putc('E');
/* With newer bootroms, we use the helper function to set up
* the memory controller. Older bootroms lacks such helpers
* so we do it ourselves.
*/
#define BOOTROM_CAPS_SYSCONTROL 0
if (BOOTROM_CAPS_SYSCONTROL) {
serial_putc('S');
uint16_t vr_ctl = bfin_read_VR_CTL();
if (!ANOMALY_05000386) {
serial_putc('F');
ADI_SYSCTRL_VALUES memory_settings;
uint32_t actions = SYSCTRL_WRITE | SYSCTRL_PLLCTL | SYSCTRL_PLLDIV | SYSCTRL_LOCKCNT;
@ -332,22 +348,38 @@ void initcode(ADI_BOOT_DATA *bootstruct)
bfin_write_SIC_IWR1(-1);
#endif
} else {
serial_putc('L');
serial_putc('G');
/* Disable all peripheral wakeups except for the PLL event. */
#ifdef SIC_IWR0
bfin_write_SIC_IWR0(1);
bfin_write_SIC_IWR1(0);
# ifdef SIC_IWR2
bfin_write_SIC_IWR2(0);
# endif
#elif defined(SICA_IWR0)
bfin_write_SICA_IWR0(1);
bfin_write_SICA_IWR1(0);
#else
bfin_write_SIC_IWR(1);
#endif
serial_putc('H');
bfin_write_PLL_LOCKCNT(CONFIG_PLL_LOCKCNT_VAL);
serial_putc('A');
serial_putc('I');
/* Only reprogram when needed to avoid triggering unnecessary
* PLL relock sequences.
*/
if (bfin_read_VR_CTL() != CONFIG_VR_CTL_VAL) {
if (vr_ctl != CONFIG_VR_CTL_VAL) {
serial_putc('!');
bfin_write_VR_CTL(CONFIG_VR_CTL_VAL);
asm("idle;");
}
serial_putc('C');
serial_putc('J');
bfin_write_PLL_DIV(CONFIG_PLL_DIV_VAL);
@ -361,8 +393,26 @@ void initcode(ADI_BOOT_DATA *bootstruct)
bfin_write_PLL_CTL(CONFIG_PLL_CTL_VAL);
asm("idle;");
}
serial_putc('L');
/* Restore all peripheral wakeups. */
#ifdef SIC_IWR0
bfin_write_SIC_IWR0(-1);
bfin_write_SIC_IWR1(-1);
# ifdef SIC_IWR2
bfin_write_SIC_IWR2(-1);
# endif
#elif defined(SICA_IWR0)
bfin_write_SICA_IWR0(-1);
bfin_write_SICA_IWR1(-1);
#else
bfin_write_SIC_IWR(-1);
#endif
}
serial_putc('M');
/* Since we've changed the SCLK above, we may need to update
* the UART divisors (UART baud rates are based on SCLK).
* Do the division by hand as there are no native instructions
@ -380,7 +430,80 @@ void initcode(ADI_BOOT_DATA *bootstruct)
serial_early_put_div(quotient - ANOMALY_05000230);
}
serial_putc('F');
serial_putc('N');
/* Program the external memory controller before we come out of
* self-refresh. This only works with our SDRAM controller.
*/
#ifndef EBIU_RSTCTL
bfin_write_EBIU_SDRRC(CONFIG_EBIU_SDRRC_VAL);
bfin_write_EBIU_SDBCTL(CONFIG_EBIU_SDBCTL_VAL);
bfin_write_EBIU_SDGCTL(CONFIG_EBIU_SDGCTL_VAL);
#endif
serial_putc('O');
/* Now that we've reprogrammed, take things out of self refresh. */
if (put_into_srfs)
#ifdef EBIU_RSTCTL
bfin_write_EBIU_RSTCTL(bfin_read_EBIU_RSTCTL() & ~(SRREQ));
#else
bfin_write_EBIU_SDGCTL(bfin_read_EBIU_SDGCTL() & ~(SRFS));
#endif
serial_putc('P');
/* Our DDR controller sucks and cannot be programmed while in
* self-refresh. So we have to pull it out before programming.
*/
#ifdef EBIU_RSTCTL
bfin_write_EBIU_RSTCTL(bfin_read_EBIU_RSTCTL() | 0x1 /*DDRSRESET*/ | CONFIG_EBIU_RSTCTL_VAL);
bfin_write_EBIU_DDRCTL0(CONFIG_EBIU_DDRCTL0_VAL);
bfin_write_EBIU_DDRCTL1(CONFIG_EBIU_DDRCTL1_VAL);
bfin_write_EBIU_DDRCTL2(CONFIG_EBIU_DDRCTL2_VAL);
# ifdef CONFIG_EBIU_DDRCTL3_VAL
/* default is disable, so don't need to force this */
bfin_write_EBIU_DDRCTL3(CONFIG_EBIU_DDRCTL3_VAL);
# endif
# ifdef CONFIG_EBIU_DDRQUE_VAL
bfin_write_EBIU_DDRQUE(bfin_read_EBIU_DDRQUE() | CONFIG_EBIU_DDRQUE_VAL);
# endif
#endif
serial_putc('Q');
/* Are we coming out of hibernate (suspend to memory) ?
* The memory layout is:
* 0x0: hibernate magic for anomaly 307 (0xDEADBEEF)
* 0x4: return address
* 0x8: stack pointer
*
* SCKELOW is unreliable on older parts (anomaly 307)
*/
if (ANOMALY_05000307 || vr_ctl & 0x8000) {
uint32_t *hibernate_magic = 0;
__builtin_bfin_ssync(); /* make sure memory controller is done */
if (hibernate_magic[0] == 0xDEADBEEF) {
serial_putc('R');
bfin_write_EVT15(hibernate_magic[1]);
bfin_write_IMASK(EVT_IVG15);
__asm__ __volatile__ (
/* load reti early to avoid anomaly 281 */
"reti = %0;"
/* clear hibernate magic */
"[%0] = %1;"
/* load stack pointer */
"SP = [%0 + 8];"
/* lower ourselves from reset ivg to ivg15 */
"raise 15;"
"rti;"
:
: "p"(hibernate_magic), "d"(0x2000 /* jump.s 0 */)
);
}
}
serial_putc('S');
/* Program the async banks controller. */
bfin_write_EBIU_AMBCTL0(CONFIG_EBIU_AMBCTL0_VAL);
@ -394,39 +517,7 @@ void initcode(ADI_BOOT_DATA *bootstruct)
bfin_write_EBIU_FCTL(CONFIG_EBIU_FCTL_VAL);
#endif
serial_putc('I');
/* Program the external memory controller. */
#ifdef EBIU_RSTCTL
bfin_write_EBIU_RSTCTL(bfin_read_EBIU_RSTCTL() | 0x1 /*DDRSRESET*/ | CONFIG_EBIU_RSTCTL_VAL);
bfin_write_EBIU_DDRCTL0(CONFIG_EBIU_DDRCTL0_VAL);
bfin_write_EBIU_DDRCTL1(CONFIG_EBIU_DDRCTL1_VAL);
bfin_write_EBIU_DDRCTL2(CONFIG_EBIU_DDRCTL2_VAL);
# ifdef CONFIG_EBIU_DDRCTL3_VAL
/* default is disable, so don't need to force this */
bfin_write_EBIU_DDRCTL3(CONFIG_EBIU_DDRCTL3_VAL);
# endif
#else
bfin_write_EBIU_SDRRC(CONFIG_EBIU_SDRRC_VAL);
bfin_write_EBIU_SDBCTL(CONFIG_EBIU_SDBCTL_VAL);
bfin_write_EBIU_SDGCTL(CONFIG_EBIU_SDGCTL_VAL);
#endif
serial_putc('N');
/* Restore all peripheral wakeups. */
#ifdef SIC_IWR0
bfin_write_SIC_IWR0(-1);
bfin_write_SIC_IWR1(-1);
# ifdef SIC_IWR2
bfin_write_SIC_IWR2(-1);
# endif
#elif defined(SICA_IWR0)
bfin_write_SICA_IWR0(-1);
bfin_write_SICA_IWR1(-1);
#else
bfin_write_SIC_IWR(-1);
#endif
serial_putc('T');
/* tell the bootrom where our entry point is */
if (CONFIG_BFIN_BOOT_MODE != BFIN_BOOT_BYPASS)

View file

@ -95,35 +95,63 @@ ENTRY(_start)
/* Save RETX so we can pass it while booting Linux */
r7 = RETX;
#if (CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_BYPASS)
/* In bypass mode, we don't have an LDR with an init block
* so we need to explicitly call it ourselves. This will
* reprogram our clocks and setup our async banks.
*/
/* XXX: we should DMA this into L1, put external memory into
* self refresh, and then jump there ...
/* Figure out where we are currently executing so that we can decide
* how to best reprogram and relocate things. We'll pass below:
* R4: load address of _start
* R5: current (not load) address of _start
*/
serial_early_puts("Find ourselves");
call _get_pc;
r3 = 0x0;
r3.h = 0x2000;
cc = r0 < r3 (iu);
if cc jump .Lproc_initialized;
serial_early_puts("Program Clocks");
call _initcode;
/* Since we reprogrammed SCLK, we need to update the serial divisor */
serial_early_set_baud
.Lproc_initialized:
#endif
.Loffset:
r1.l = .Loffset;
r1.h = .Loffset;
r4.l = _start;
r4.h = _start;
r3 = r1 - r4;
r5 = r0 - r3;
/* Inform upper layers if we had to do the relocation ourselves.
* This allows us to detect whether we were loaded by 'go 0x1000'
* or by the bootrom from an LDR. "r6" is "loaded_from_ldr".
* or by the bootrom from an LDR. "R6" is "loaded_from_ldr".
*/
r6 = 1 (x);
cc = r4 == r5;
if cc jump .Lnorelocate;
r6 = 0 (x);
/* In bypass mode, we don't have an LDR with an init block
* so we need to explicitly call it ourselves. This will
* reprogram our clocks, memory, and setup our async banks.
*/
serial_early_puts("Program Clocks");
/* if we're executing >=0x20000000, then we dont need to dma */
r3 = 0x0;
r3.h = 0x2000;
cc = r5 < r3 (iu);
if cc jump .Ldma_and_reprogram;
call _initcode;
jump .Lprogrammed;
/* we're sitting in external memory, so dma into L1 and reprogram */
.Ldma_and_reprogram:
r0.l = LO(L1_INST_SRAM);
r0.h = HI(L1_INST_SRAM);
r1.l = __initcode_start;
r1.h = __initcode_start;
r2.l = __initcode_end;
r2.h = __initcode_end;
r2 = r2 - r1; /* convert r2 into length of initcode */
r1 = r1 - r4; /* convert r1 from load address of initcode ... */
r1 = r1 + r5; /* ... to current (not load) address of initcode */
p3 = r0;
call _dma_memcpy_nocache;
call (p3);
/* Since we reprogrammed SCLK, we need to update the serial divisor */
.Lprogrammed:
serial_early_set_baud
/* Relocate from wherever we are (FLASH/RAM/etc...) to the hardcoded
* monitor location in the end of RAM. We know that memcpy() only
@ -132,19 +160,8 @@ ENTRY(_start)
* it yet (see "lower to 15" below).
*/
serial_early_puts("Relocate");
call _get_pc;
.Loffset:
r2.l = .Loffset;
r2.h = .Loffset;
r3.l = _start;
r3.h = _start;
r2 = r2 - r3;
r1 = r0 - r2;
cc = r1 == r3;
if cc jump .Lnorelocate;
r6 = 0 (x);
r0 = r3;
r0 = r4;
r1 = r5;
r2.l = LO(CONFIG_SYS_MONITOR_LEN);
r2.h = HI(CONFIG_SYS_MONITOR_LEN);
call _memcpy_ASM;