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 <config.h>
#include <asm/blackfin.h> #include <asm/blackfin.h>
#include <asm/mach-common/bits/bootrom.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/ebiu.h>
#include <asm/mach-common/bits/pll.h> #include <asm/mach-common/bits/pll.h>
#include <asm/mach-common/bits/uart.h> #include <asm/mach-common/bits/uart.h>
@ -257,6 +258,8 @@ void initcode(ADI_BOOT_DATA *bootstruct)
divB = serial_early_get_div(); divB = serial_early_get_div();
} }
serial_putc('A');
#ifdef CONFIG_HW_WATCHDOG #ifdef CONFIG_HW_WATCHDOG
# ifndef CONFIG_HW_WATCHDOG_TIMEOUT_INITCODE # ifndef CONFIG_HW_WATCHDOG_TIMEOUT_INITCODE
# define CONFIG_HW_WATCHDOG_TIMEOUT_INITCODE 20000 # define CONFIG_HW_WATCHDOG_TIMEOUT_INITCODE 20000
@ -273,7 +276,23 @@ void initcode(ADI_BOOT_DATA *bootstruct)
} }
#endif #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 /* 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 * 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); bfin_write_SPI_BAUD(CONFIG_SPI_BAUD_INITBLOCK);
} }
serial_putc('B'); serial_putc('D');
/* Disable all peripheral wakeups except for the PLL event. */ /* If we're entering self refresh, make sure it has happened. */
#ifdef SIC_IWR0 if (put_into_srfs)
bfin_write_SIC_IWR0(1); #ifdef EBIU_RSTCTL
bfin_write_SIC_IWR1(0); while (!(bfin_read_EBIU_RSTCTL() & SRACK))
# ifdef SIC_IWR2
bfin_write_SIC_IWR2(0);
# endif
#elif defined(SICA_IWR0)
bfin_write_SICA_IWR0(1);
bfin_write_SICA_IWR1(0);
#else #else
bfin_write_SIC_IWR(1); while (!(bfin_read_EBIU_SDSTAT() & SDSRA))
#endif #endif
continue;
serial_putc('E');
/* With newer bootroms, we use the helper function to set up /* With newer bootroms, we use the helper function to set up
* the memory controller. Older bootroms lacks such helpers * the memory controller. Older bootroms lacks such helpers
* so we do it ourselves. * so we do it ourselves.
*/ */
#define BOOTROM_CAPS_SYSCONTROL 0 uint16_t vr_ctl = bfin_read_VR_CTL();
if (BOOTROM_CAPS_SYSCONTROL) { if (!ANOMALY_05000386) {
serial_putc('S'); serial_putc('F');
ADI_SYSCTRL_VALUES memory_settings; ADI_SYSCTRL_VALUES memory_settings;
uint32_t actions = SYSCTRL_WRITE | SYSCTRL_PLLCTL | SYSCTRL_PLLDIV | SYSCTRL_LOCKCNT; 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); bfin_write_SIC_IWR1(-1);
#endif #endif
} else { } 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); bfin_write_PLL_LOCKCNT(CONFIG_PLL_LOCKCNT_VAL);
serial_putc('A'); serial_putc('I');
/* Only reprogram when needed to avoid triggering unnecessary /* Only reprogram when needed to avoid triggering unnecessary
* PLL relock sequences. * PLL relock sequences.
*/ */
if (bfin_read_VR_CTL() != CONFIG_VR_CTL_VAL) { if (vr_ctl != CONFIG_VR_CTL_VAL) {
serial_putc('!'); serial_putc('!');
bfin_write_VR_CTL(CONFIG_VR_CTL_VAL); bfin_write_VR_CTL(CONFIG_VR_CTL_VAL);
asm("idle;"); asm("idle;");
} }
serial_putc('C'); serial_putc('J');
bfin_write_PLL_DIV(CONFIG_PLL_DIV_VAL); 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); bfin_write_PLL_CTL(CONFIG_PLL_CTL_VAL);
asm("idle;"); 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 /* Since we've changed the SCLK above, we may need to update
* the UART divisors (UART baud rates are based on SCLK). * the UART divisors (UART baud rates are based on SCLK).
* Do the division by hand as there are no native instructions * 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_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. */ /* Program the async banks controller. */
bfin_write_EBIU_AMBCTL0(CONFIG_EBIU_AMBCTL0_VAL); 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); bfin_write_EBIU_FCTL(CONFIG_EBIU_FCTL_VAL);
#endif #endif
serial_putc('I'); serial_putc('T');
/* 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
/* tell the bootrom where our entry point is */ /* tell the bootrom where our entry point is */
if (CONFIG_BFIN_BOOT_MODE != BFIN_BOOT_BYPASS) 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 */ /* Save RETX so we can pass it while booting Linux */
r7 = RETX; r7 = RETX;
#if (CONFIG_BFIN_BOOT_MODE == BFIN_BOOT_BYPASS) /* Figure out where we are currently executing so that we can decide
/* In bypass mode, we don't have an LDR with an init block * how to best reprogram and relocate things. We'll pass below:
* so we need to explicitly call it ourselves. This will * R4: load address of _start
* reprogram our clocks and setup our async banks. * R5: current (not load) address of _start
*/
/* XXX: we should DMA this into L1, put external memory into
* self refresh, and then jump there ...
*/ */
serial_early_puts("Find ourselves");
call _get_pc; call _get_pc;
r3 = 0x0; .Loffset:
r3.h = 0x2000; r1.l = .Loffset;
cc = r0 < r3 (iu); r1.h = .Loffset;
if cc jump .Lproc_initialized; r4.l = _start;
r4.h = _start;
serial_early_puts("Program Clocks"); r3 = r1 - r4;
r5 = r0 - r3;
call _initcode;
/* Since we reprogrammed SCLK, we need to update the serial divisor */
serial_early_set_baud
.Lproc_initialized:
#endif
/* Inform upper layers if we had to do the relocation ourselves. /* Inform upper layers if we had to do the relocation ourselves.
* This allows us to detect whether we were loaded by 'go 0x1000' * 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); 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 /* Relocate from wherever we are (FLASH/RAM/etc...) to the hardcoded
* monitor location in the end of RAM. We know that memcpy() only * 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). * it yet (see "lower to 15" below).
*/ */
serial_early_puts("Relocate"); serial_early_puts("Relocate");
call _get_pc; r0 = r4;
.Loffset: r1 = r5;
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;
r2.l = LO(CONFIG_SYS_MONITOR_LEN); r2.l = LO(CONFIG_SYS_MONITOR_LEN);
r2.h = HI(CONFIG_SYS_MONITOR_LEN); r2.h = HI(CONFIG_SYS_MONITOR_LEN);
call _memcpy_ASM; call _memcpy_ASM;