rk27xx: Add failsafe mode to the bootloader

Change-Id: Iba8fdc50b3c71831a6f55d15779fee8d1f12cb4f
diff --git a/bootloader/rk27xx.c b/bootloader/rk27xx.c
index dfba970..4afefeb 100644
--- a/bootloader/rk27xx.c
+++ b/bootloader/rk27xx.c
@@ -19,11 +19,59 @@
 #include "crc32-rkw.h"
 #include "rkw-loader.h"
 
+/* beginning of DRAM */
 #define DRAM_ORIG 0x60000000
+
+/* bootloader code runs from 0x60700000
+ * so we cannot load more code to not overwrite ourself
+ */
 #define LOAD_SIZE 0x700000
 
 extern void show_logo( void );
 
+/* This function setup bare minimum
+ * and jumps to rom in order to activate
+ * hardcoded rkusb mode
+ */
+static void enter_rkusb(void)
+{
+    asm volatile (
+        /* turn off cache */
+        "ldr     r0, =0xefff0000 \n"
+        "ldrh    r1, [r0] \n"
+        "strh    r1, [r0] \n"
+
+        /* turn off interrupts */
+        "mrs     r0, cpsr \n"
+        "bic     r0, r0, #0x1f \n"
+        "orr     r0, r0, #0xd3 \n"
+        "msr     cpsr, r0 \n"
+
+        /* disable iram remap */
+        "mov     r0, #0x18000000 \n"
+        "add     r0, r0, #0x1c000 \n"
+        "mov     r1, #0 \n"
+        "str     r1, [r0, #4] \n"
+
+        /* setup stacks in unmapped
+         * iram just as rom will do
+         */
+        "msr     cpsr, #0xd2 \n"
+        "ldr     r1, =0x18200274 \n"
+        "add     r1, r1, #0x200 \n"
+        "mov     sp, r1 \n"
+        "msr     cpsr, #0xd3 \n"
+        "add     r1, r1, #0x400 \n"
+        "mov     sp, r1 \n"
+
+        /* jump to main() in rom
+         * just before dfu handler
+         */
+        "ldr     r0, =0xec0 \n"
+        "bx      r0 \n"
+    );
+}
+
 void main(void) NORETURN_ATTR;
 void main(void)
 {
@@ -87,23 +135,53 @@
         printf(rkw_strerror(ret));
         lcd_update();
         sleep(5*HZ);
-        power_off();
+
+        /* if we boot rockbox we shutdown on error
+         * if we boot OF we fall back to rkusb mode on error
+         */        
+        if (boot == rb)
+        {
+            power_off();
+        }
+        else
+        {
+            /* give visual feedback what we are doing */
+            printf("Entering rockchip USB mode...");
+            lcd_update();
+
+            enter_rkusb();
+        }
     }
     else
     {
+        /* print 'Loading OK' */
         printf(rkw_strerror(0));
         sleep(HZ);
     }
 
+    /* jump to entrypoint */
     kernel_entry = (void*) loadbuffer;
     commit_discard_idcache();
 
     printf("Executing");
     kernel_entry();
 
+    /* this should never be reached actually */
     printf("ERR: Failed to boot");
     sleep(5*HZ);
-    power_off();
+
+    if (boot == rb)
+    {
+        power_off();
+    }
+    else
+    {
+        /* give visual feedback what we are doing */
+        printf("Entering rockchip USB mode...");
+        lcd_update();
+
+        enter_rkusb();
+    }   
 
     /* hang */
     while(1);