]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - cpu/blackfin/initcode.c
Blackfin: recurse with early serial initcode
[karo-tx-uboot.git] / cpu / blackfin / initcode.c
index 3f3b4796c11a3534b6911493fc52fc1c9163fa74..c0fe2c65a6c6232398ea0ce328c85d4ce2fcd7e8 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>
@@ -91,7 +92,7 @@ static inline void serial_putc(char c)
                return;
 
        if (c == '\n')
-               *pUART_THR = '\r';
+               serial_putc('\r');
 
        *pUART_THR = c;
 
@@ -203,9 +204,62 @@ static inline void serial_putc(char c)
 # define CONFIG_VR_CTL_VAL (CONFIG_VR_CTL_CLKBUF | CONFIG_VR_CTL_VLEV | CONFIG_VR_CTL_FREQ)
 #endif
 
+/* some parts do not have an on-chip voltage regulator */
+#if defined(__ADSPBF51x__)
+# define CONFIG_HAS_VR 0
+# undef CONFIG_VR_CTL_VAL
+# define CONFIG_VR_CTL_VAL 0
+#else
+# define CONFIG_HAS_VR 1
+#endif
+
+#ifndef EBIU_RSTCTL
+/* Blackfin with SDRAM */
+#ifndef CONFIG_EBIU_SDBCTL_VAL
+# if CONFIG_MEM_SIZE == 16
+#  define CONFIG_EBSZ_VAL EBSZ_16
+# elif CONFIG_MEM_SIZE == 32
+#  define CONFIG_EBSZ_VAL EBSZ_32
+# elif CONFIG_MEM_SIZE == 64
+#  define CONFIG_EBSZ_VAL EBSZ_64
+# elif CONFIG_MEM_SIZE == 128
+#  define CONFIG_EBSZ_VAL EBSZ_128
+# elif CONFIG_MEM_SIZE == 256
+#  define CONFIG_EBSZ_VAL EBSZ_256
+# elif CONFIG_MEM_SIZE == 512
+#  define CONFIG_EBSZ_VAL EBSZ_512
+# else
+#  error You need to define CONFIG_EBIU_SDBCTL_VAL or CONFIG_MEM_SIZE
+# endif
+# if CONFIG_MEM_ADD_WDTH == 8
+#  define CONFIG_EBCAW_VAL EBCAW_8
+# elif CONFIG_MEM_ADD_WDTH == 9
+#  define CONFIG_EBCAW_VAL EBCAW_9
+# elif CONFIG_MEM_ADD_WDTH == 10
+#  define CONFIG_EBCAW_VAL EBCAW_10
+# elif CONFIG_MEM_ADD_WDTH == 11
+#  define CONFIG_EBCAW_VAL EBCAW_11
+# else
+#  error You need to define CONFIG_EBIU_SDBCTL_VAL or CONFIG_MEM_ADD_WDTH
+# endif
+# define CONFIG_EBIU_SDBCTL_VAL (CONFIG_EBCAW_VAL | CONFIG_EBSZ_VAL | EBE)
+#endif
+#endif
+
+/* Conflicting Column Address Widths Causes SDRAM Errors:
+ * EB2CAW and EB3CAW must be the same
+ */
+#if ANOMALY_05000362
+# if ((CONFIG_EBIU_SDBCTL_VAL & 0x30000000) >> 8) != (CONFIG_EBIU_SDBCTL_VAL & 0x00300000)
+#  error "Anomaly 05000362: EB2CAW and EB3CAW must be the same"
+# endif
+#endif
+
 BOOTROM_CALLED_FUNC_ATTR
 void initcode(ADI_BOOT_DATA *bootstruct)
 {
+       ADI_BOOT_DATA bootstruct_scratch;
+
        /* Save the clock pieces that are used in baud rate calculation */
        unsigned int sdivB, divB, vcoB;
        serial_init();
@@ -215,6 +269,15 @@ void initcode(ADI_BOOT_DATA *bootstruct)
                divB = serial_early_get_div();
        }
 
+       serial_putc('A');
+
+       /* If the bootstruct is NULL, then it's because we're loading
+        * dynamically and not via LDR (bootrom).  So set the struct to
+        * some scratch space.
+        */
+       if (!bootstruct)
+               bootstruct = &bootstruct_scratch;
+
 #ifdef CONFIG_HW_WATCHDOG
 # ifndef CONFIG_HW_WATCHDOG_TIMEOUT_INITCODE
 #  define CONFIG_HW_WATCHDOG_TIMEOUT_INITCODE 20000
@@ -231,7 +294,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
@@ -244,54 +323,87 @@ 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');
 
+               /* Always programming PLL_LOCKCNT avoids Anomaly 05000430 */
                ADI_SYSCTRL_VALUES memory_settings;
-               memory_settings.uwVrCtl = CONFIG_VR_CTL_VAL;
+               uint32_t actions = SYSCTRL_WRITE | SYSCTRL_PLLCTL | SYSCTRL_PLLDIV | SYSCTRL_LOCKCNT;
+               if (CONFIG_HAS_VR) {
+                       actions |= SYSCTRL_VRCTL;
+                       if (CONFIG_VR_CTL_VAL & FREQ_MASK)
+                               actions |= SYSCTRL_INTVOLTAGE;
+                       else
+                               actions |= SYSCTRL_EXTVOLTAGE;
+                       memory_settings.uwVrCtl = CONFIG_VR_CTL_VAL;
+               } else
+                       actions |= SYSCTRL_EXTVOLTAGE;
                memory_settings.uwPllCtl = CONFIG_PLL_CTL_VAL;
                memory_settings.uwPllDiv = CONFIG_PLL_DIV_VAL;
                memory_settings.uwPllLockCnt = CONFIG_PLL_LOCKCNT_VAL;
-               syscontrol(SYSCTRL_WRITE | SYSCTRL_VRCTL | SYSCTRL_PLLCTL | SYSCTRL_PLLDIV | SYSCTRL_LOCKCNT |
-                       (CONFIG_VR_CTL_VAL & FREQ_MASK ? SYSCTRL_INTVOLTAGE : SYSCTRL_EXTVOLTAGE), &memory_settings, NULL);
+#if ANOMALY_05000432
+               bfin_write_SIC_IWR1(0);
+#endif
+               bfrom_SysControl(actions, &memory_settings, NULL);
+#if ANOMALY_05000432
+               bfin_write_SIC_IWR1(-1);
+#endif
+#if ANOMALY_05000171
+               bfin_write_SICA_IWR0(-1);
+               bfin_write_SICA_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');
 
+               /* Always programming PLL_LOCKCNT avoids Anomaly 05000430 */
                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);
 
@@ -300,13 +412,31 @@ void initcode(ADI_BOOT_DATA *bootstruct)
                /* Only reprogram when needed to avoid triggering unnecessary
                 * PLL relock sequences.
                 */
-               if (bfin_read_PLL_CTL() != CONFIG_PLL_CTL_VAL) {
+               if (ANOMALY_05000242 || bfin_read_PLL_CTL() != CONFIG_PLL_CTL_VAL) {
                        serial_putc('!');
                        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
@@ -324,23 +454,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);
@@ -350,28 +489,64 @@ 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);
+
        serial_putc('>');
        serial_putc('\n');