Blackfin: put memory into self-refresh before/after programming clocks
authorMike Frysinger <vapier@gentoo.org>
Sun, 12 Oct 2008 01:58:33 +0000 (21:58 -0400)
committerMike Frysinger <vapier@gentoo.org>
Mon, 23 Mar 2009 19:14:55 +0000 (15:14 -0400)
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>
cpu/blackfin/initcode.c
cpu/blackfin/start.S

index bbc2fa5bed4b8390509020cb00283b570217fbfc..7bd4b227e6d3b71c07a5338e80ba2d77fad4b103 100644 (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,23 +430,32 @@ void initcode(ADI_BOOT_DATA *bootstruct)
                serial_early_put_div(quotient - ANOMALY_05000230);
        }
 
-       serial_putc('F');
+       serial_putc('N');
 
-       /* Program the async banks controller. */
-       bfin_write_EBIU_AMBCTL0(CONFIG_EBIU_AMBCTL0_VAL);
-       bfin_write_EBIU_AMBCTL1(CONFIG_EBIU_AMBCTL1_VAL);
-       bfin_write_EBIU_AMGCTL(CONFIG_EBIU_AMGCTL_VAL);
+       /* 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
 
-#ifdef EBIU_MODE
-       /* Not all parts have these additional MMRs. */
-       bfin_write_EBIU_MBSCTL(CONFIG_EBIU_MBSCTL_VAL);
-       bfin_write_EBIU_MODE(CONFIG_EBIU_MODE_VAL);
-       bfin_write_EBIU_FCTL(CONFIG_EBIU_FCTL_VAL);
+       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('I');
+       serial_putc('P');
 
-       /* Program the external memory controller. */
+       /* 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);
@@ -406,28 +465,60 @@ void initcode(ADI_BOOT_DATA *bootstruct)
        /* 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);
+# ifdef CONFIG_EBIU_DDRQUE_VAL
+       bfin_write_EBIU_DDRQUE(bfin_read_EBIU_DDRQUE() | CONFIG_EBIU_DDRQUE_VAL);
+# endif
 #endif
 
-       serial_putc('N');
+       serial_putc('Q');
 
-       /* 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);
+       /* 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);
+       bfin_write_EBIU_AMBCTL1(CONFIG_EBIU_AMBCTL1_VAL);
+       bfin_write_EBIU_AMGCTL(CONFIG_EBIU_AMGCTL_VAL);
+
+#ifdef EBIU_MODE
+       /* Not all parts have these additional MMRs. */
+       bfin_write_EBIU_MBSCTL(CONFIG_EBIU_MBSCTL_VAL);
+       bfin_write_EBIU_MODE(CONFIG_EBIU_MODE_VAL);
+       bfin_write_EBIU_FCTL(CONFIG_EBIU_FCTL_VAL);
 #endif
 
+       serial_putc('T');
+
        /* tell the bootrom where our entry point is */
        if (CONFIG_BFIN_BOOT_MODE != BFIN_BOOT_BYPASS)
                bfin_write_EVT1(CONFIG_SYS_MONITOR_BASE);
index 6c8def48162fef0cd0ec23cfa9bbbf820b5610ae..506fea58cc6d3c42c67c00b937ac80c3ccefb8d7 100644 (file)
@@ -95,36 +95,64 @@ 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;
+.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".
+        */
+       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
 
-.Lproc_initialized:
-#endif
-
-       /* 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".
-        */
-       r6 = 1 (x);
-
        /* Relocate from wherever we are (FLASH/RAM/etc...) to the hardcoded
         * monitor location in the end of RAM.  We know that memcpy() only
         * uses registers, so it is safe to call here.  Note that this 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;